diff options
| author | Paul Buetow <paul@buetow.org> | 2026-03-25 22:45:49 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-03-25 22:45:49 +0200 |
| commit | a4a8510a0bf8abaf3c58ce276d619fa30dad26f5 (patch) | |
| tree | be98570510035617e184bfd7ac1ef62530069e99 | |
| parent | 7d0747df9c4298aa4044684f8cdd6b6e09b57442 (diff) | |
rpn: Fix =: operator pop order for REPL mode
| -rw-r--r-- | internal/repl/repl_test.go | 36 | ||||
| -rw-r--r-- | internal/rpn/operations.go | 3 | ||||
| -rw-r--r-- | internal/rpn/operations_test.go | 4 |
3 files changed, 39 insertions, 4 deletions
diff --git a/internal/repl/repl_test.go b/internal/repl/repl_test.go index cd062cb..2b7ed16 100644 --- a/internal/repl/repl_test.go +++ b/internal/repl/repl_test.go @@ -627,3 +627,39 @@ func TestDefaultExecutorCodePaths(t *testing.T) { // Path 5: Whitespace only (trimmed to empty, returns early) executor(" ") } + +func TestExecutorWithAssignmentRight(t *testing.T) { + // Test := and =: operators + executor("5 x :=") + state := getRPNState() + val, exists := state.vars.GetVariable("x") + if !exists { + t.Errorf("Variable x should exist after x :=") + } + if val != 5 { + t.Errorf("Variable x = %v, want 5", val) + } + + executor("y 3 =:") + state = getRPNState() + val, exists = state.vars.GetVariable("y") + if !exists { + t.Errorf("Variable y should exist after y =:") + } + if val != 3 { + t.Errorf("Variable y = %v, want 3", val) + } +} + +func TestExecutorWithAssignmentAfterCalculation(t *testing.T) { + // Test that assignment works after a calculation + executor("1 2 + x =:") + state := getRPNState() + val, exists := state.vars.GetVariable("x") + if !exists { + t.Errorf("Variable x should exist") + } + if val != 3 { + t.Errorf("Variable x = %v, want 3", val) + } +} diff --git a/internal/rpn/operations.go b/internal/rpn/operations.go index 11aa624..b21e3a9 100644 --- a/internal/rpn/operations.go +++ b/internal/rpn/operations.go @@ -990,13 +990,12 @@ func (o *Operations) AssignLeft(stack *Stack) error { // 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 { - // Pop name first (top of stack), which should be StringNum + // Pop name first (top of stack), then value name, err := stack.Pop() if err != nil { return fmt.Errorf("insufficient operands for =: : need variable name") } - // Pop value (below name on stack) val, err := stack.Pop() if err != nil { return fmt.Errorf("insufficient operands for =: : need value") diff --git a/internal/rpn/operations_test.go b/internal/rpn/operations_test.go index e1a9b53..4725f7b 100644 --- a/internal/rpn/operations_test.go +++ b/internal/rpn/operations_test.go @@ -1019,7 +1019,7 @@ func TestAssignLeft(t *testing.T) { s := NewStack() // For "5 x :=": - // Stack should have value (5) on top, name ("x") below + // AssignLeft pops val first, then name // 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) @@ -1050,7 +1050,7 @@ func TestAssignRight(t *testing.T) { s := NewStack() // For "x 5 =:": - // Stack should have name ("x") on top, value (5) below + // AssignRight pops name first, then value // 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) |
