summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--internal/rpn/operations.go112
-rw-r--r--internal/rpn/rpn.go285
2 files changed, 193 insertions, 204 deletions
diff --git a/internal/rpn/operations.go b/internal/rpn/operations.go
index 5d90212..88736e6 100644
--- a/internal/rpn/operations.go
+++ b/internal/rpn/operations.go
@@ -60,6 +60,118 @@ func NewOperations(vars VariableStore) *Operations {
}
}
+// OperatorHandler represents a function that handles an operator.
+// Returns (result string, handled bool, error error).
+// result is non-empty only for commands that return immediately (like show, vars).
+// handled indicates if the token was recognized.
+type OperatorHandler func(stack *Stack) (result string, handled bool, err error)
+
+// OperatorRegistry maintains a registry of operators.
+type OperatorRegistry struct {
+ standardOperators map[string]OperatorHandler
+ hyperOperators map[string]OperatorHandler
+}
+
+// NewOperatorRegistry creates a new operator registry and registers all operators.
+func NewOperatorRegistry(op Operator) *OperatorRegistry {
+ registry := &OperatorRegistry{
+ standardOperators: make(map[string]OperatorHandler),
+ hyperOperators: make(map[string]OperatorHandler),
+ }
+
+ // Register standard operators
+ registry.registerStandardOperator("+", func(stack *Stack) error { return op.Add(stack) })
+ registry.registerStandardOperator("-", func(stack *Stack) error { return op.Subtract(stack) })
+ registry.registerStandardOperator("*", func(stack *Stack) error { return op.Multiply(stack) })
+ registry.registerStandardOperator("/", func(stack *Stack) error { return op.Divide(stack) })
+ registry.registerStandardOperator("^", func(stack *Stack) error { return op.Power(stack) })
+ registry.registerStandardOperator("%", func(stack *Stack) error { return op.Modulo(stack) })
+ registry.registerStandardOperator("dup", func(stack *Stack) error { return op.Dup(stack) })
+ registry.registerStandardOperator("swap", func(stack *Stack) error { return op.Swap(stack) })
+ registry.registerStandardOperator("pop", func(stack *Stack) error { return op.Pop(stack) })
+ registry.registerStandardOperator("d", func(stack *Stack) error {
+ return fmt.Errorf("'d' command not supported as standalone token")
+ })
+
+ // Commands that return immediately
+ registry.registerCommandOperator("show", func(stack *Stack) (string, error) { return op.Show(stack) })
+ registry.registerCommandOperator("showstack", func(stack *Stack) (string, error) { return op.Show(stack) })
+ registry.registerCommandOperator("print", func(stack *Stack) (string, error) { return op.Show(stack) })
+ registry.registerCommandOperator("vars", func(stack *Stack) (string, error) { return op.ListVariables() })
+ registry.registerCommandOperator("clear", func(stack *Stack) (string, error) { op.ClearVariables(); return "All variables cleared", nil })
+
+ // Register hyper operators
+ registry.registerHyperOperator("[+]", func(stack *Stack) error { return op.HyperAdd(stack) })
+ registry.registerHyperOperator("[-]", func(stack *Stack) error { return op.HyperSubtract(stack) })
+ registry.registerHyperOperator("[*]", func(stack *Stack) error { return op.HyperMultiply(stack) })
+ registry.registerHyperOperator("[/]", func(stack *Stack) error { return op.HyperDivide(stack) })
+ registry.registerHyperOperator("[^]", func(stack *Stack) error { return op.HyperPower(stack) })
+ registry.registerHyperOperator("[%]", func(stack *Stack) error { return op.HyperModulo(stack) })
+
+ return registry
+}
+
+// registerStandardOperator registers a standard operator that returns empty result.
+func (r *OperatorRegistry) registerStandardOperator(name string, handler func(*Stack) error) {
+ r.standardOperators[name] = func(stack *Stack) (string, bool, error) {
+ if err := handler(stack); err != nil {
+ return "", false, err
+ }
+ return "", true, nil
+ }
+}
+
+// registerCommandOperator registers a command operator that returns a result immediately.
+func (r *OperatorRegistry) registerCommandOperator(name string, handler func(*Stack) (string, error)) {
+ r.standardOperators[name] = func(stack *Stack) (string, bool, error) {
+ result, err := handler(stack)
+ if err != nil {
+ return "", false, err
+ }
+ return result, true, nil
+ }
+}
+
+// registerHyperOperator registers a hyper operator.
+func (r *OperatorRegistry) registerHyperOperator(name string, handler func(*Stack) error) {
+ r.hyperOperators[name] = func(stack *Stack) (string, bool, error) {
+ if err := handler(stack); err != nil {
+ return "", false, err
+ }
+ return "", true, nil
+ }
+}
+
+// HandleStandardOperator handles a standard operator.
+// Returns (result string, handled bool, error error).
+func (r *OperatorRegistry) HandleStandardOperator(stack *Stack, token string) (string, bool, error) {
+ if handler, exists := r.standardOperators[token]; exists {
+ return handler(stack)
+ }
+ return "", false, fmt.Errorf("unknown token '%s'", token)
+}
+
+// HandleHyperOperator handles a hyper operator.
+// Returns (result string, handled bool, error error).
+func (r *OperatorRegistry) HandleHyperOperator(stack *Stack, token string) (string, bool, error) {
+ if handler, exists := r.hyperOperators[token]; exists {
+ return handler(stack)
+ }
+ return "", false, fmt.Errorf("unknown token '%s'", token)
+}
+
+// IsStandardOperator checks if a token is a standard operator.
+func (r *OperatorRegistry) IsStandardOperator(token string) bool {
+ _, exists := r.standardOperators[token]
+ return exists
+}
+
+// IsHyperOperator checks if a token is a hyper operator.
+func (r *OperatorRegistry) IsHyperOperator(token string) bool {
+ _, exists := r.hyperOperators[token]
+ return exists
+}
+
// arithmetic operators
// Add pops two values from stack, adds them, and pushes result.
diff --git a/internal/rpn/rpn.go b/internal/rpn/rpn.go
index 0de21f8..d4744c9 100644
--- a/internal/rpn/rpn.go
+++ b/internal/rpn/rpn.go
@@ -8,17 +8,20 @@ import (
// RPN represents the RPN parser and evaluator.
type RPN struct {
- vars VariableStore
- ops Operator
- maxStack int
- currentStack *Stack
+ vars VariableStore
+ ops Operator
+ opRegistry *OperatorRegistry
+ maxStack int
+ currentStack *Stack
}
// NewRPN creates a new RPN parser and evaluator with the given variable store.
func NewRPN(vars VariableStore) *RPN {
+ ops := NewOperations(vars)
return &RPN{
vars: vars,
- ops: NewOperations(vars),
+ ops: ops,
+ opRegistry: NewOperatorRegistry(ops),
maxStack: 1000, // Reasonable limit for RPN expressions
currentStack: NewStack(),
}
@@ -67,59 +70,33 @@ func (r *RPN) ResultStack(tokens []string) (string, error) {
continue
}
- // Check for operators and special commands
- switch token {
- case "+":
- if err := r.ops.Add(stack); err != nil {
- return "", err
- }
- case "-":
- if err := r.ops.Subtract(stack); err != nil {
- return "", err
- }
- case "*":
- if err := r.ops.Multiply(stack); err != nil {
- return "", err
- }
- case "/":
- if err := r.ops.Divide(stack); err != nil {
- return "", err
- }
- case "^":
- if err := r.ops.Power(stack); err != nil {
- return "", err
- }
- case "%":
- if err := r.ops.Modulo(stack); err != nil {
- return "", err
- }
- case "dup":
- if err := r.ops.Dup(stack); err != nil {
- return "", err
- }
- case "swap":
- if err := r.ops.Swap(stack); err != nil {
- return "", err
- }
- case "pop":
- if err := r.ops.Pop(stack); err != nil {
+ // Check for hyperoperators
+ if handled, result, err := r.handleHyperOperatorWithRegistry(stack, token); err != nil {
+ return "", err
+ } else if handled {
+ return result, nil
+ }
+
+ // Check for standard operators
+ if result, handled, err := r.opRegistry.HandleStandardOperator(stack, token); err != nil {
+ // If the error is not "unknown token", return it
+ // Otherwise, fall through to check for variable
+ if !strings.Contains(err.Error(), "unknown token") {
return "", err
}
- case "show", "showstack", "print":
- return r.ops.Show(stack)
- case "vars":
- return r.ops.ListVariables()
- case "clear":
- r.ops.ClearVariables()
- return "All variables cleared", nil
- default:
- // Check if it's a variable reference (push its value)
- val, exists := r.vars.GetVariable(token)
- if exists {
- stack.Push(val)
- } else {
- return "", fmt.Errorf("unknown token '%s'", token)
+ } else if handled {
+ if result != "" {
+ return result, nil
}
+ continue
+ }
+
+ // Check if it's a variable reference (push its value)
+ val, exists := r.vars.GetVariable(token)
+ if exists {
+ stack.Push(val)
+ } else {
+ return "", fmt.Errorf("unknown token '%s'", token)
}
}
@@ -133,56 +110,35 @@ func (r *RPN) EvalOperator(op string) (string, error) {
r.currentStack = NewStack()
}
- switch op {
- case "+":
- if err := r.ops.Add(r.currentStack); err != nil {
- return "", fmt.Errorf("operator +: %w", err)
- }
- case "-":
- if err := r.ops.Subtract(r.currentStack); err != nil {
- return "", fmt.Errorf("operator -: %w", err)
- }
- case "*":
- if err := r.ops.Multiply(r.currentStack); err != nil {
- return "", fmt.Errorf("operator *: %w", err)
- }
- case "/":
- if err := r.ops.Divide(r.currentStack); err != nil {
- return "", fmt.Errorf("operator /: %w", err)
- }
- case "^":
- if err := r.ops.Power(r.currentStack); err != nil {
- return "", fmt.Errorf("operator ^: %w", err)
- }
- case "%":
- if err := r.ops.Modulo(r.currentStack); err != nil {
- return "", fmt.Errorf("operator %%: %w", err)
+ // Check for hyperoperators
+ if handled, result, err := r.handleHyperOperatorWithRegistry(r.currentStack, op); err != nil {
+ return "", err
+ } else if handled {
+ if result != "" {
+ return result, nil
}
- case "dup":
- if err := r.ops.Dup(r.currentStack); err != nil {
- return "", fmt.Errorf("dup: %w", err)
+ stackShow, err := r.ops.Show(r.currentStack)
+ if err != nil {
+ return "", fmt.Errorf("show stack: %w", err)
}
- case "swap":
- if err := r.ops.Swap(r.currentStack); err != nil {
- return "", fmt.Errorf("swap: %w", err)
+ return stackShow, nil
+ }
+
+ // Check for standard operators
+ if result, handled, err := r.opRegistry.HandleStandardOperator(r.currentStack, op); err != nil {
+ return "", err
+ } else if handled {
+ if result != "" {
+ return result, nil
}
- case "pop":
- if err := r.ops.Pop(r.currentStack); err != nil {
- return "", fmt.Errorf("pop: %w", err)
+ stackShow, err := r.ops.Show(r.currentStack)
+ if err != nil {
+ return "", fmt.Errorf("show stack: %w", err)
}
- case "show", "showstack", "print":
- return r.ops.Show(r.currentStack)
- case "clear":
- r.ops.ClearVariables()
- return "All variables cleared", nil
- case "vars":
- return r.ops.ListVariables()
- default:
- return "", fmt.Errorf("unknown operator '%s'", op)
+ return stackShow, nil
}
- // Return the current stack state
- return r.ops.Show(r.currentStack)
+ return "", fmt.Errorf("unknown operator '%s'", op)
}
// GetCurrentStack returns a copy of the current stack for inspection.
@@ -259,122 +215,43 @@ func (r *RPN) evaluate(tokens []string) (string, error) {
return fmt.Sprintf("%.10g", val), nil
}
-// handleOperator handles operators and special commands
+// handleOperator handles operators and special commands using the operator registry.
func (r *RPN) handleOperator(stack *Stack, token string, tokenIndex int) (string, error) {
- // Handle hyperoperators
- if isHyperOperator(token) {
- if err := r.handleHyperOperator(stack, token); err != nil {
- return "", err
- }
+ // Check if it's a number first
+ if _, err := strconv.ParseFloat(token, 64); err == nil {
return "", nil
}
- // Handle standard operators
- switch token {
- case "+":
- if err := r.ops.Add(stack); err != nil {
- return "", fmt.Errorf("operator +: %w", err)
- }
- case "-":
- if err := r.ops.Subtract(stack); err != nil {
- return "", fmt.Errorf("operator -: %w", err)
- }
- case "*":
- if err := r.ops.Multiply(stack); err != nil {
- return "", fmt.Errorf("operator *: %w", err)
- }
- case "/":
- if err := r.ops.Divide(stack); err != nil {
- return "", fmt.Errorf("operator /: %w", err)
- }
- case "^":
- if err := r.ops.Power(stack); err != nil {
- return "", fmt.Errorf("operator ^: %w", err)
- }
- case "%":
- if err := r.ops.Modulo(stack); err != nil {
- return "", fmt.Errorf("operator %%: %w", err)
- }
- case "dup":
- if err := r.ops.Dup(stack); err != nil {
- return "", fmt.Errorf("dup: %w", err)
- }
- case "swap":
- if err := r.ops.Swap(stack); err != nil {
- return "", fmt.Errorf("swap: %w", err)
- }
- case "pop":
- if err := r.ops.Pop(stack); err != nil {
- return "", fmt.Errorf("pop: %w", err)
- }
- case "show", "showstack", "print":
- result, err := r.ops.Show(stack)
- if err != nil {
- return "", fmt.Errorf("show: %w", err)
- }
- return result, nil
- case "vars":
- result, err := r.ops.ListVariables()
- if err != nil {
- return "", fmt.Errorf("vars: %w", err)
- }
+ // Check if it's a variable reference first (before operators)
+ if val, exists := r.vars.GetVariable(token); exists {
+ stack.Push(val)
+ return "", nil
+ }
+
+ // Check for hyperoperators
+ if handled, result, err := r.handleHyperOperatorWithRegistry(stack, token); err != nil {
+ return "", err
+ } else if handled {
return result, nil
- case "clear":
- r.ops.ClearVariables()
- return "All variables cleared", nil
- case "d":
- return "", fmt.Errorf("'d' command not supported as standalone token")
- default:
- // Check if it's a variable reference (push its value)
- val, exists := r.vars.GetVariable(token)
- if exists {
- stack.Push(val)
- } else {
- return "", fmt.Errorf("rpn: unknown token '%s' at position %d", token, tokenIndex)
- }
}
- return "", nil
-}
-// isHyperOperator checks if the token is a hyperoperator
-func isHyperOperator(token string) bool {
- switch token {
- case "[+]", "[-]", "[*]", "[/]", "[^]", "[%]":
- return true
- default:
- return false
+ // Handle standard operators
+ if result, handled, err := r.opRegistry.HandleStandardOperator(stack, token); err != nil {
+ return "", err
+ } else if handled {
+ return result, nil
}
+
+ return "", fmt.Errorf("unknown token '%s'", token)
}
-// handleHyperOperator handles hyperoperators
-func (r *RPN) handleHyperOperator(stack *Stack, token string) error {
- switch token {
- case "[+]":
- if err := r.ops.HyperAdd(stack); err != nil {
- return fmt.Errorf("hyperoperator [+]: %w", err)
- }
- case "[-]":
- if err := r.ops.HyperSubtract(stack); err != nil {
- return fmt.Errorf("hyperoperator [-]: %w", err)
- }
- case "[*]":
- if err := r.ops.HyperMultiply(stack); err != nil {
- return fmt.Errorf("hyperoperator [*]: %w", err)
- }
- case "[/]":
- if err := r.ops.HyperDivide(stack); err != nil {
- return fmt.Errorf("hyperoperator [/]: %w", err)
- }
- case "[^]":
- if err := r.ops.HyperPower(stack); err != nil {
- return fmt.Errorf("hyperoperator [^]: %w", err)
- }
- case "[%]":
- if err := r.ops.HyperModulo(stack); err != nil {
- return fmt.Errorf("hyperoperator [%%]: %w", err)
- }
+// handleHyperOperatorWithRegistry handles hyperoperators and returns (handled, result, error).
+func (r *RPN) handleHyperOperatorWithRegistry(stack *Stack, token string) (bool, string, error) {
+ if !r.opRegistry.IsHyperOperator(token) {
+ return false, "", nil
}
- return nil
+ result, handled, err := r.opRegistry.HandleHyperOperator(stack, token)
+ return handled, result, err
}
// handleAssignment checks if the input is an assignment format and handles it.