summaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-03-25 22:39:32 +0200
committerPaul Buetow <paul@buetow.org>2026-03-25 22:39:32 +0200
commit7d0747df9c4298aa4044684f8cdd6b6e09b57442 (patch)
tree5cd67221de5bf6bb26d362049ed19bce28764d35 /internal
parent18b0017e2603436dd418b8866279445ff45ad6fb (diff)
rpn: Fix AssignLeft/AssignRight to handle StringNum correctly
Diffstat (limited to 'internal')
-rw-r--r--internal/rpn/operations.go54
-rw-r--r--internal/rpn/operations_test.go16
-rw-r--r--internal/rpn/rpn_test.go30
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)
+ }
+}