diff options
| author | Paul Buetow <paul@buetow.org> | 2026-03-20 22:31:16 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-03-20 22:31:16 +0200 |
| commit | 7df37824bb6408fb137eb2eb22bc93563ed37535 (patch) | |
| tree | b9257c5e25bb2a5ea9ddf5428c67d6b61737d71d | |
| parent | 015d238aa65d5b1ac087e13d3a07d262bc9790d1 (diff) | |
internal/rpn: split ParseAndEvaluate into smaller helper functions
- Extracted handleAssignment method to handle assignment formats
- ParseAndEvaluate now delegates to handleAssignment and evaluate
- This reduces function size and improves readability
| -rw-r--r-- | internal/rpn/rpn.go | 119 |
1 files changed, 63 insertions, 56 deletions
diff --git a/internal/rpn/rpn.go b/internal/rpn/rpn.go index 348da96..5fd1d39 100644 --- a/internal/rpn/rpn.go +++ b/internal/rpn/rpn.go @@ -27,81 +27,88 @@ func NewRPN(vars VariableStore) *RPN { // ParseAndEvaluate parses and evaluates an RPN expression. // Returns the result as a formatted string or an error. func (r *RPN) ParseAndEvaluate(input string) (string, error) { + // Validate input and initialize input = strings.TrimSpace(input) if input == "" { return "", fmt.Errorf("empty expression") } - - // Initialize stack if nil (first use) if r.currentStack == nil { r.currentStack = NewStack() } - // Handle single assignment: "name value =" - // This is when the entire input is just an assignment - if strings.Contains(input, " = ") { - parts := strings.SplitN(input, " = ", 2) - if len(parts) == 2 { - name := strings.TrimSpace(parts[0]) - valueStr := strings.TrimSpace(parts[1]) - // Validate name is a single word (variable name) - nameFields := strings.Fields(name) - if len(nameFields) == 1 { - // Validate value is a single number - valueFields := strings.Fields(valueStr) - if len(valueFields) == 1 { - val, err := strconv.ParseFloat(valueFields[0], 64) - if err != nil { - return "", fmt.Errorf("invalid value '%s' for assignment: %w", valueFields[0], err) - } - if err := r.vars.SetVariable(nameFields[0], val); err != nil { - return "", err - } - return fmt.Sprintf("%s = %.10g", nameFields[0], val), nil + // Handle assignment formats + if assignmentResult, isAssignment, err := r.handleAssignment(input); err != nil { + return "", err + } else if isAssignment { + return assignmentResult, nil + } + + // Evaluate standard RPN expression + tokens := Tokenize(input) + if len(tokens) == 0 { + return "", fmt.Errorf("no valid tokens found") + } + + return r.evaluate(tokens) +} + +// handleAssignment checks if the input is an assignment format and handles it. +// Returns (result string, isAssignment bool, error error). +func (r *RPN) handleAssignment(input string) (string, bool, error) { + if !strings.Contains(input, " = ") { + return "", false, nil + } + + // Handle single assignment: "name = value" + if parts := strings.SplitN(input, " = ", 2); len(parts) == 2 { + name := strings.TrimSpace(parts[0]) + valueStr := strings.TrimSpace(parts[1]) + + // Validate name is a single word (variable name) + nameFields := strings.Fields(name) + if len(nameFields) == 1 { + // Validate value is a single number + valueFields := strings.Fields(valueStr) + if len(valueFields) == 1 { + val, err := strconv.ParseFloat(valueFields[0], 64) + if err != nil { + return "", false, fmt.Errorf("invalid value '%s' for assignment: %w", valueFields[0], err) + } + if err := r.vars.SetVariable(nameFields[0], val); err != nil { + return "", false, err } + return fmt.Sprintf("%s = %.10g", nameFields[0], val), true, nil } } } // Handle assignment with expression: "name value = expression..." - // Format: variable_name value = expression (where = comes after value) - if strings.Contains(input, " = ") { - // Check if the input matches pattern: "name value = expr..." - // where name and value are single tokens, and = comes after value - // For example: "x 5 = x x +" or "pi 3.14 = pi 2 *" - - // Find " = " position and split - pos := strings.Index(input, " = ") - if pos >= 0 { - before := input[:pos] // "name value" - after := input[pos+3:] // "expr..." - - beforeFields := strings.Fields(before) - if len(beforeFields) == 2 { - name := beforeFields[0] - valueStr := beforeFields[1] - - // Try to parse value as a number - val, err := strconv.ParseFloat(valueStr, 64) - if err == nil { - // Valid assignment pattern: "name value = expr..." - if err := r.vars.SetVariable(name, val); err != nil { - return "", err - } - - // Evaluate the remaining expression - return r.evaluate(strings.Fields(strings.TrimSpace(after))) + pos := strings.Index(input, " = ") + if pos >= 0 { + before := input[:pos] // "name value" + after := input[pos+3:] // "expr..." + + beforeFields := strings.Fields(before) + if len(beforeFields) == 2 { + name := beforeFields[0] + valueStr := beforeFields[1] + + // Try to parse value as a number + val, err := strconv.ParseFloat(valueStr, 64) + if err == nil { + // Valid assignment pattern: "name value = expr..." + if err := r.vars.SetVariable(name, val); err != nil { + return "", false, err } + + // Evaluate the remaining expression + result, err := r.evaluate(strings.Fields(strings.TrimSpace(after))) + return result, true, err } } } - tokens := Tokenize(input) - if len(tokens) == 0 { - return "", fmt.Errorf("no valid tokens found") - } - - return r.evaluate(tokens) + return "", false, nil } // ResultStack returns the final stack state after evaluation. |
