diff options
| -rw-r--r-- | internal/rpn/operations.go | 112 | ||||
| -rw-r--r-- | internal/rpn/rpn.go | 285 |
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. |
