diff options
| author | Paul Buetow <paul@buetow.org> | 2026-03-25 22:39:32 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-03-25 22:39:32 +0200 |
| commit | 7d0747df9c4298aa4044684f8cdd6b6e09b57442 (patch) | |
| tree | 5cd67221de5bf6bb26d362049ed19bce28764d35 /internal | |
| parent | 18b0017e2603436dd418b8866279445ff45ad6fb (diff) | |
rpn: Fix AssignLeft/AssignRight to handle StringNum correctly
Diffstat (limited to 'internal')
| -rw-r--r-- | internal/rpn/operations.go | 54 | ||||
| -rw-r--r-- | internal/rpn/operations_test.go | 16 | ||||
| -rw-r--r-- | internal/rpn/rpn_test.go | 30 |
3 files changed, 80 insertions, 20 deletions
diff --git a/internal/rpn/operations.go b/internal/rpn/operations.go index ef6bcd6..11aa624 100644 --- a/internal/rpn/operations.go +++ b/internal/rpn/operations.go @@ -954,35 +954,65 @@ func (o *Operations) ClearVariables() { // This function pops name first (top of stack), then value. // Usage: `value name :=` func (o *Operations) AssignLeft(stack *Stack) error { + val, err := stack.Pop() + if err != nil { + return fmt.Errorf("insufficient operands for assignment: need value") + } + name, err := stack.Pop() if err != nil { return fmt.Errorf("insufficient operands for assignment: need variable name") } - val, err := stack.Pop() - if err != nil { - return fmt.Errorf("insufficient operands for assignment: need value") + // Get the variable name - if it's StringNum, get the string; otherwise convert to string + varName := "" + switch v := name.(type) { + case *StringNum: + varName = v.String() + default: + varName = name.String() } - return o.vars.SetVariable(name.String(), val.Float64()) + return o.vars.SetVariable(varName, val.Float64()) } + +// AssignRight assigns a value to a variable (for =: operator). +// For =: operator, the stack order is: name value =: (name on bottom, value on top). +// This function pops value first (top of stack), then name (below it). +// Usage: `name value =:` (e.g., `x 5 =:`) + // AssignRight assigns a value to a variable (for =: operator). -// Pops value from stack first, then pops variable name. // For =: operator, the stack order is: name value =: (name on bottom, value on top). -// This function pops value first (top of stack), then name. -// Usage: `name value =:` +// This function pops name first (top of stack), then value. +// Usage: `name value =:` (e.g., `x 5 =:`) +// Note: When called via the RPN parser, the name is pushed as StringNum. +// We pop name first (StringNum), then value (Number). func (o *Operations) AssignRight(stack *Stack) error { - val, err := stack.Pop() + // Pop name first (top of stack), which should be StringNum + name, err := stack.Pop() if err != nil { - return fmt.Errorf("insufficient operands for assignment: need value") + return fmt.Errorf("insufficient operands for =: : need variable name") } - name, err := stack.Pop() + // Pop value (below name on stack) + val, err := stack.Pop() if err != nil { - return fmt.Errorf("insufficient operands for assignment: need variable name") + return fmt.Errorf("insufficient operands for =: : need value") } - return o.vars.SetVariable(name.String(), val.Float64()) + // Get the variable name - if it's StringNum, get the string; otherwise convert to string + varName := "" + switch v := name.(type) { + case *StringNum: + varName = v.String() + default: + varName = name.String() + } + + // Get the value as float64 + varValue := val.Float64() + + return o.vars.SetVariable(varName, varValue) } diff --git a/internal/rpn/operations_test.go b/internal/rpn/operations_test.go index 25ba352..e1a9b53 100644 --- a/internal/rpn/operations_test.go +++ b/internal/rpn/operations_test.go @@ -1019,10 +1019,10 @@ func TestAssignLeft(t *testing.T) { s := NewStack() // For "5 x :=": - // Stack order is: value name := (value on bottom, name on top) - // Push value first (will be popped second), then name (will be popped first) - s.Push(NewNumber(5, FloatMode)) // value - s.Push(NewStringNum("x")) // name + // Stack should have value (5) on top, name ("x") below + // Push name first (will be popped second), then value (will be popped first) + s.Push(NewStringNum("x")) // name (will be popped second) + s.Push(NewNumber(5, FloatMode)) // value (will be popped first) err := o.AssignLeft(s) if err != nil { @@ -1050,10 +1050,10 @@ func TestAssignRight(t *testing.T) { s := NewStack() // For "x 5 =:": - // Stack order is: name value =: (name on bottom, value on top) - // Push name first (will be popped second), then value (will be popped first) - s.Push(NewStringNum("x")) // name - s.Push(NewNumber(5, FloatMode)) // value + // Stack should have name ("x") on top, value (5) below + // Push value first (will be popped second), then name (will be popped first) + s.Push(NewNumber(5, FloatMode)) // value (will be popped second) + s.Push(NewStringNum("x")) // name (will be popped first) err := o.AssignRight(s) if err != nil { diff --git a/internal/rpn/rpn_test.go b/internal/rpn/rpn_test.go index b5e1739..8357d2f 100644 --- a/internal/rpn/rpn_test.go +++ b/internal/rpn/rpn_test.go @@ -1389,3 +1389,33 @@ func TestParseAndEvaluateAssignmentLeftRight(t *testing.T) { }) } } + +// TestRPNIncrementalAssignment tests assignment with incremental operations +func TestRPNIncrementalAssignment(t *testing.T) { + v := NewVariables() + r := NewRPN(v) + + // First, evaluate "1" to push 1 to stack + result, err := r.ParseAndEvaluate("1") + if err != nil { + t.Fatalf("First evaluation failed: %v", err) + } + if result != "1" { + t.Errorf("First result = %q, want '1'", result) + } + + // Now try x =: - should assign 1 to variable x + result, err = r.ParseAndEvaluate("x =:") + if err != nil { + t.Fatalf("Assignment failed: %v", err) + } + + // Check if x was set to 1 + val, exists := v.GetVariable("x") + if !exists { + t.Errorf("Variable x should exist after assignment") + } + if val != 1 { + t.Errorf("Variable x = %v, want 1", val) + } +} |
