summaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-03-25 17:25:00 +0200
committerPaul Buetow <paul@buetow.org>2026-03-25 17:25:00 +0200
commit6b5bee78c239b221188eea2bebe0e87714970866 (patch)
treec35622e61aa629c0912621b21655fdb8c04cfa1c /internal
parentf692d72dc840be8eb17857b2ef85ee42dde5fd3f (diff)
refactor: Refactor RPN to use Number interface uniformly for stack values
This commit refactors the internal/rpn package to use the Number interface instead of the old Value struct for stack values. Key changes: 1. Updated Number interface to include IsBool() and Bool() methods for boolean value support 2. Modified Float and Rat types to support boolean mode with: - isBool and boolVal fields - Float64() returns 1 for true, 0 for false - String() returns 'true' or 'false' for boolean values 3. Updated Stack to use []Number instead of []Value 4. Updated all operations to use the Number interface methods directly - Add, Sub, Mul, Div, Pow, Mod now use Float64() for values 5. Updated tests to use NewNumber() with mode parameter instead of NewNumberValue(), and use Float64() instead of Number() Benefits: - Simplified code - no need for toNumber() and NewNumberValue() wrappers - Better type safety - stack values are Number interface instances - Boolean-to-number coercion works correctly in all operations
Diffstat (limited to 'internal')
-rw-r--r--internal/rpn/number.go102
-rw-r--r--internal/rpn/operations.go166
-rw-r--r--internal/rpn/operations_test.go166
-rw-r--r--internal/rpn/rpn_ops.go14
-rw-r--r--internal/rpn/rpn_parse.go8
-rw-r--r--internal/rpn/rpn_state.go8
-rw-r--r--internal/rpn/rpn_test.go4
-rw-r--r--internal/rpn/variables.go22
8 files changed, 302 insertions, 188 deletions
diff --git a/internal/rpn/number.go b/internal/rpn/number.go
index 277b054..9cf9216 100644
--- a/internal/rpn/number.go
+++ b/internal/rpn/number.go
@@ -28,6 +28,7 @@ func toNumber(v Value) float64 {
// Number represents a number that can be used in RPN calculations.
// It can be either a float64 or a *big.Rat for precise rational calculations.
+// Booleans are also supported through IsBool() and Bool() methods.
type Number interface {
// String returns the string representation of the number.
String() string
@@ -53,6 +54,10 @@ type Number interface {
IsNegative() bool
// Compare returns -1, 0, or 1 if this number is less than, equal to, or greater than another.
Compare(other Number) int
+ // IsBool returns true if this number represents a boolean value.
+ IsBool() bool
+ // Bool returns the boolean value, or false if not a boolean.
+ Bool() bool
}
// NewNumber creates a Number from a float64 value.
@@ -61,42 +66,75 @@ func NewNumber(value float64, mode CalculationMode) Number {
if mode == RationalMode {
return NewRat(value)
}
- return &Float{n: value}
+ return NewFloat(value)
}
// Float is a Number implementation using float64.
+// It can also represent boolean values (true=1, false=0).
type Float struct {
- n float64
+ n float64
+ isBool bool
+ boolVal bool
}
// NewFloat creates a new Float number.
func NewFloat(n float64) *Float {
- return &Float{n: n}
+ return &Float{n: n, isBool: false, boolVal: false}
+}
+
+// NewFloatFromBool creates a new Float representing a boolean.
+func NewFloatFromBool(b bool) *Float {
+ return &Float{n: 0, isBool: true, boolVal: b}
}
// String returns the string representation of the float.
func (f *Float) String() string {
+ if f.isBool {
+ if f.boolVal {
+ return "true"
+ }
+ return "false"
+ }
return fmt.Sprintf("%.10g", f.n)
}
// Float64 returns the float64 value.
func (f *Float) Float64() float64 {
+ if f.isBool {
+ if f.boolVal {
+ return 1
+ }
+ return 0
+ }
return f.n
}
+// IsBool returns true if this number represents a boolean value.
+func (f *Float) IsBool() bool {
+ return f.isBool
+}
+
+// Bool returns the boolean value, or false if not a boolean.
+func (f *Float) Bool() bool {
+ return f.boolVal
+}
+
// Add returns the sum of two float numbers.
func (f *Float) Add(other Number) Number {
- return NewFloat(f.n + other.Float64())
+ // Use Float64() to handle both regular numbers and boolean values
+ return NewFloat(f.Float64() + other.Float64())
}
// Sub returns the difference of two float numbers.
func (f *Float) Sub(other Number) Number {
- return NewFloat(f.n - other.Float64())
+ // Use Float64() to handle both regular numbers and boolean values
+ return NewFloat(f.Float64() - other.Float64())
}
// Mul returns the product of two float numbers.
func (f *Float) Mul(other Number) Number {
- return NewFloat(f.n * other.Float64())
+ // Use Float64() to handle both regular numbers and boolean values
+ return NewFloat(f.Float64() * other.Float64())
}
// Div returns the quotient of two float numbers.
@@ -104,12 +142,14 @@ func (f *Float) Div(other Number) (Number, error) {
if other.IsZero() {
return nil, fmt.Errorf("division by zero")
}
- return NewFloat(f.n / other.Float64()), nil
+ // Use Float64() to handle both regular numbers and boolean values
+ return NewFloat(f.Float64() / other.Float64()), nil
}
// Pow returns this float raised to the power of another.
func (f *Float) Pow(other Number) Number {
- return NewFloat(math.Pow(f.n, other.Float64()))
+ // Use Float64() to handle both regular numbers and boolean values
+ return NewFloat(math.Pow(f.Float64(), other.Float64()))
}
// Mod returns the remainder of this float divided by another.
@@ -117,12 +157,14 @@ func (f *Float) Mod(other Number) (Number, error) {
if other.IsZero() {
return nil, fmt.Errorf("modulo by zero")
}
- return NewFloat(math.Mod(f.n, other.Float64())), nil
+ // Use Float64() to handle both regular numbers and boolean values
+ return NewFloat(math.Mod(f.Float64(), other.Float64())), nil
}
// IsZero returns true if the float is zero.
+// For boolean values, false (0) is zero, true (1) is not zero.
func (f *Float) IsZero() bool {
- return f.n == 0
+ return f.Float64() == 0
}
// IsNegative returns true if the float is negative.
@@ -143,15 +185,29 @@ func (f *Float) Compare(other Number) int {
}
// Rat is a Number implementation using *big.Rat.
+// It can also represent boolean values (true=1, false=0).
type Rat struct {
- n *big.Rat
+ n *big.Rat
+ isBool bool
+ boolVal bool
}
// NewRat creates a new Rat number from a float64.
func NewRat(n float64) *Rat {
r := &big.Rat{}
r.SetFloat64(n)
- return &Rat{n: r}
+ return &Rat{n: r, isBool: false, boolVal: false}
+}
+
+// NewRatFromBool creates a new Rat representing a boolean.
+func NewRatFromBool(b bool) *Rat {
+ r := &big.Rat{}
+ if b {
+ r.SetInt64(1)
+ } else {
+ r.SetInt64(0)
+ }
+ return &Rat{n: r, isBool: true, boolVal: b}
}
// NewRatFromString creates a new Rat number from a string representation.
@@ -166,6 +222,12 @@ func NewRatFromString(s string) (*Rat, error) {
// String returns the string representation of the rational number.
func (r *Rat) String() string {
+ if r.isBool {
+ if r.boolVal {
+ return "true"
+ }
+ return "false"
+ }
// Format as decimal for consistency with Float
// Use a reasonable precision
return r.n.FloatString(10)
@@ -173,10 +235,26 @@ func (r *Rat) String() string {
// Float64 returns the float64 representation.
func (r *Rat) Float64() float64 {
+ if r.isBool {
+ if r.boolVal {
+ return 1
+ }
+ return 0
+ }
f, _ := r.n.Float64()
return f
}
+// IsBool returns true if this number represents a boolean value.
+func (r *Rat) IsBool() bool {
+ return r.isBool
+}
+
+// Bool returns the boolean value, or false if not a boolean.
+func (r *Rat) Bool() bool {
+ return r.boolVal
+}
+
// Add returns the sum of two rational numbers.
func (r *Rat) Add(other Number) Number {
result := &big.Rat{}
diff --git a/internal/rpn/operations.go b/internal/rpn/operations.go
index 2b37c39..1c9182a 100644
--- a/internal/rpn/operations.go
+++ b/internal/rpn/operations.go
@@ -220,7 +220,7 @@ func (r *OperatorRegistry) IsHyperOperator(token string) bool {
// arithmetic operators
-// Add pops two values from stack, adds them (with boolean-to-number coercion), and pushes result.
+// Add pops two values from stack, adds them, and pushes result.
func (o *Operations) Add(stack *Stack) error {
bVal, err := stack.Pop()
if err != nil {
@@ -232,8 +232,8 @@ func (o *Operations) Add(stack *Stack) error {
return fmt.Errorf("insufficient operands for +: %w", err)
}
- // Use toNumber for automatic boolean-to-number coercion
- stack.Push(NewNumberValue(toNumber(aVal) + toNumber(bVal)))
+ // Use the Number interface for arithmetic
+ stack.Push(aVal.Add(bVal))
return nil
}
@@ -249,7 +249,7 @@ func (o *Operations) Subtract(stack *Stack) error {
return fmt.Errorf("insufficient operands for -: %w", err)
}
- stack.Push(NewNumberValue(toNumber(a) - toNumber(b)))
+ stack.Push(a.Sub(b))
return nil
}
@@ -265,7 +265,7 @@ func (o *Operations) Multiply(stack *Stack) error {
return fmt.Errorf("insufficient operands for *: %w", err)
}
- stack.Push(NewNumberValue(toNumber(a) * toNumber(b)))
+ stack.Push(a.Mul(b))
return nil
}
@@ -276,16 +276,20 @@ func (o *Operations) Divide(stack *Stack) error {
return fmt.Errorf("insufficient operands for /: %w", err)
}
+ if b.IsZero() {
+ return fmt.Errorf("division by zero")
+ }
+
a, err := stack.Pop()
if err != nil {
return fmt.Errorf("insufficient operands for /: %w", err)
}
- if toNumber(b) == 0 {
- return fmt.Errorf("division by zero")
+ result, err := a.Div(b)
+ if err != nil {
+ return fmt.Errorf("division error: %w", err)
}
-
- stack.Push(NewNumberValue(toNumber(a) / toNumber(b)))
+ stack.Push(result)
return nil
}
@@ -301,7 +305,7 @@ func (o *Operations) Power(stack *Stack) error {
return fmt.Errorf("insufficient operands for ^: %w", err)
}
- stack.Push(NewNumberValue(math.Pow(toNumber(a), toNumber(b))))
+ stack.Push(a.Pow(b))
return nil
}
@@ -317,11 +321,15 @@ func (o *Operations) Modulo(stack *Stack) error {
return fmt.Errorf("insufficient operands for %%: %w", err)
}
- if toNumber(b) == 0 {
+ if b.IsZero() {
return fmt.Errorf("modulo by zero")
}
- stack.Push(NewNumberValue(math.Mod(toNumber(a), toNumber(b))))
+ result, err := a.Mod(b)
+ if err != nil {
+ return fmt.Errorf("modulo error: %w", err)
+ }
+ stack.Push(result)
return nil
}
@@ -332,11 +340,14 @@ func (o *Operations) Log2(stack *Stack) error {
return fmt.Errorf("insufficient operands for lg: %w", err)
}
- if toNumber(a) <= 0 {
+ // Check if value is zero or negative
+ val := a.Float64()
+ if val <= 0 {
return fmt.Errorf("log2 undefined for non-positive numbers")
}
- stack.Push(NewNumberValue(math.Log2(toNumber(a))))
+ // Compute log2 using the number interface
+ stack.Push(NewNumber(math.Log2(val), o.mode))
return nil
}
@@ -347,11 +358,14 @@ func (o *Operations) Log10(stack *Stack) error {
return fmt.Errorf("insufficient operands for log: %w", err)
}
- if toNumber(a) <= 0 {
+ // Check if value is zero or negative
+ val := a.Float64()
+ if val <= 0 {
return fmt.Errorf("log10 undefined for non-positive numbers")
}
- stack.Push(NewNumberValue(math.Log10(toNumber(a))))
+ // Compute log10 using the number interface
+ stack.Push(NewNumber(math.Log10(val), o.mode))
return nil
}
@@ -362,11 +376,14 @@ func (o *Operations) Ln(stack *Stack) error {
return fmt.Errorf("insufficient operands for ln: %w", err)
}
- if toNumber(a) <= 0 {
+ // Check if value is zero or negative
+ val := a.Float64()
+ if val <= 0 {
return fmt.Errorf("ln undefined for non-positive numbers")
}
- stack.Push(NewNumberValue(math.Log(toNumber(a))))
+ // Compute ln using the number interface
+ stack.Push(NewNumber(math.Log(val), o.mode))
return nil
}
@@ -379,7 +396,7 @@ func (o *Operations) HyperAdd(stack *Stack) error {
}
// Pop all values into a slice (in reverse order - top first)
- var values []Value
+ var values []Number
for stack.Len() > 0 {
val, err := stack.Pop()
if err != nil {
@@ -393,12 +410,12 @@ func (o *Operations) HyperAdd(stack *Stack) error {
values[i], values[j] = values[j], values[i]
}
- // Process left-associative with toNumber coercion
+ // Process left-associative with Number interface
sum := 0.0
for i := 0; i < len(values); i++ {
- sum += toNumber(values[i])
+ sum += values[i].Float64()
}
- stack.Push(NewNumberValue(sum))
+ stack.Push(NewNumber(sum, o.mode))
return nil
}
@@ -414,9 +431,9 @@ func (o *Operations) HyperMultiply(stack *Stack) error {
if err != nil {
return fmt.Errorf("hypermultiply: %w", err)
}
- product *= toNumber(val)
+ product *= val.Float64()
}
- stack.Push(NewNumberValue(product))
+ stack.Push(NewNumber(product, o.mode))
return nil
}
@@ -427,7 +444,7 @@ func (o *Operations) HyperSubtract(stack *Stack) error {
}
// Pop all values into a slice (in reverse order - top first)
- var values []Value
+ var values []Number
for stack.Len() > 0 {
val, err := stack.Pop()
if err != nil {
@@ -441,12 +458,12 @@ func (o *Operations) HyperSubtract(stack *Stack) error {
values[i], values[j] = values[j], values[i]
}
- // Process left-associative with toNumber coercion
- result := toNumber(values[0])
+ // Process left-associative with Number interface
+ result := values[0].Float64()
for i := 1; i < len(values); i++ {
- result -= toNumber(values[i])
+ result -= values[i].Float64()
}
- stack.Push(NewNumberValue(result))
+ stack.Push(NewNumber(result, o.mode))
return nil
}
@@ -457,7 +474,7 @@ func (o *Operations) HyperDivide(stack *Stack) error {
}
// Pop all values into a slice (in reverse order - top first)
- var values []Value
+ var values []Number
for stack.Len() > 0 {
val, err := stack.Pop()
if err != nil {
@@ -471,15 +488,16 @@ func (o *Operations) HyperDivide(stack *Stack) error {
values[i], values[j] = values[j], values[i]
}
- // Process left-associative with toNumber coercion
- result := toNumber(values[0])
+ // Process left-associative with Number interface
+ result := values[0].Float64()
for i := 1; i < len(values); i++ {
- if toNumber(values[i]) == 0 {
+ val := values[i].Float64()
+ if val == 0 {
return fmt.Errorf("division by zero")
}
- result /= toNumber(values[i])
+ result /= val
}
- stack.Push(NewNumberValue(result))
+ stack.Push(NewNumber(result, o.mode))
return nil
}
@@ -490,7 +508,7 @@ func (o *Operations) HyperPower(stack *Stack) error {
}
// Pop all values into a slice (in reverse order - top first)
- var values []Value
+ var values []Number
for stack.Len() > 0 {
val, err := stack.Pop()
if err != nil {
@@ -504,12 +522,12 @@ func (o *Operations) HyperPower(stack *Stack) error {
values[i], values[j] = values[j], values[i]
}
- // Process left-associative with toNumber coercion
- result := toNumber(values[0])
+ // Process left-associative with Number interface
+ result := values[0].Float64()
for i := 1; i < len(values); i++ {
- result = math.Pow(result, toNumber(values[i]))
+ result = math.Pow(result, values[i].Float64())
}
- stack.Push(NewNumberValue(result))
+ stack.Push(NewNumber(result, o.mode))
return nil
}
@@ -520,7 +538,7 @@ func (o *Operations) HyperModulo(stack *Stack) error {
}
// Pop all values into a slice (in reverse order - top first)
- var values []Value
+ var values []Number
for stack.Len() > 0 {
val, err := stack.Pop()
if err != nil {
@@ -534,15 +552,16 @@ func (o *Operations) HyperModulo(stack *Stack) error {
values[i], values[j] = values[j], values[i]
}
- // Process left-associative with toNumber coercion
- result := toNumber(values[0])
+ // Process left-associative with Number interface
+ result := values[0].Float64()
for i := 1; i < len(values); i++ {
- if toNumber(values[i]) == 0 {
+ val := values[i].Float64()
+ if val == 0 {
return fmt.Errorf("modulo by zero")
}
- result = math.Mod(result, toNumber(values[i]))
+ result = math.Mod(result, val)
}
- stack.Push(NewNumberValue(result))
+ stack.Push(NewNumber(result, o.mode))
return nil
}
@@ -554,7 +573,7 @@ func (o *Operations) HyperLog2(stack *Stack) error {
}
// Pop all values into a slice (in reverse order - top first)
- var values []Value
+ var values []Number
for stack.Len() > 0 {
val, err := stack.Pop()
if err != nil {
@@ -568,15 +587,18 @@ func (o *Operations) HyperLog2(stack *Stack) error {
values[i], values[j] = values[j], values[i]
}
- // Sum the log2 of all values with toNumber coercion
+ // Sum the log2 of all values with Number interface
var result float64 = 0
for i := 0; i < len(values); i++ {
- if toNumber(values[i]) <= 0 {
+ val := values[i].Float64()
+ if val <= 0 {
return fmt.Errorf("hyperlog2 undefined for non-positive numbers")
}
- result += math.Log2(toNumber(values[i]))
+ result += math.Log2(val)
}
- stack.Push(NewNumberValue(result))
+
+ // Push the result as a Number
+ stack.Push(NewNumber(result, o.mode))
return nil
}
@@ -588,7 +610,7 @@ func (o *Operations) HyperLog10(stack *Stack) error {
}
// Pop all values into a slice (in reverse order - top first)
- var values []Value
+ var values []Number
for stack.Len() > 0 {
val, err := stack.Pop()
if err != nil {
@@ -602,15 +624,18 @@ func (o *Operations) HyperLog10(stack *Stack) error {
values[i], values[j] = values[j], values[i]
}
- // Sum the log10 of all values with toNumber coercion
+ // Sum the log10 of all values
var result float64 = 0
for i := 0; i < len(values); i++ {
- if toNumber(values[i]) <= 0 {
+ val := values[i].Float64()
+ if val <= 0 {
return fmt.Errorf("hyperlog10 undefined for non-positive numbers")
}
- result += math.Log10(toNumber(values[i]))
+ result += math.Log10(val)
}
- stack.Push(NewNumberValue(result))
+
+ // Push the result as a Number
+ stack.Push(NewNumber(result, o.mode))
return nil
}
@@ -622,7 +647,7 @@ func (o *Operations) HyperLn(stack *Stack) error {
}
// Pop all values into a slice (in reverse order - top first)
- var values []Value
+ var values []Number
for stack.Len() > 0 {
val, err := stack.Pop()
if err != nil {
@@ -636,15 +661,16 @@ func (o *Operations) HyperLn(stack *Stack) error {
values[i], values[j] = values[j], values[i]
}
- // Sum the natural log of all values with toNumber coercion
+ // Sum the natural log of all values with Number interface
var result float64 = 0
for i := 0; i < len(values); i++ {
- if toNumber(values[i]) <= 0 {
+ val := values[i].Float64()
+ if val <= 0 {
return fmt.Errorf("hyperln undefined for non-positive numbers")
}
- result += math.Log(toNumber(values[i]))
+ result += math.Log(val)
}
- stack.Push(NewNumberValue(result))
+ stack.Push(NewNumber(result, o.mode))
return nil
}
@@ -662,7 +688,7 @@ func (o *Operations) GT(stack *Stack) error {
return fmt.Errorf("insufficient operands for gt: %w", err)
}
- stack.Push(NewBoolValue(toNumber(a) > toNumber(b)))
+ stack.Push(NewFloatFromBool(a.Float64() > b.Float64()))
return nil
}
@@ -678,7 +704,7 @@ func (o *Operations) LT(stack *Stack) error {
return fmt.Errorf("insufficient operands for lt: %w", err)
}
- stack.Push(NewBoolValue(toNumber(a) < toNumber(b)))
+ stack.Push(NewFloatFromBool(a.Float64() < b.Float64()))
return nil
}
@@ -694,7 +720,7 @@ func (o *Operations) GTE(stack *Stack) error {
return fmt.Errorf("insufficient operands for gte: %w", err)
}
- stack.Push(NewBoolValue(toNumber(a) >= toNumber(b)))
+ stack.Push(NewFloatFromBool(a.Float64() >= b.Float64()))
return nil
}
@@ -710,7 +736,7 @@ func (o *Operations) LTE(stack *Stack) error {
return fmt.Errorf("insufficient operands for lte: %w", err)
}
- stack.Push(NewBoolValue(toNumber(a) <= toNumber(b)))
+ stack.Push(NewFloatFromBool(a.Float64() <= b.Float64()))
return nil
}
@@ -726,7 +752,7 @@ func (o *Operations) EQ(stack *Stack) error {
return fmt.Errorf("insufficient operands for eq: %w", err)
}
- stack.Push(NewBoolValue(toNumber(a) == toNumber(b)))
+ stack.Push(NewFloatFromBool(a.Float64() == b.Float64()))
return nil
}
@@ -742,7 +768,7 @@ func (o *Operations) NEQ(stack *Stack) error {
return fmt.Errorf("insufficient operands for neq: %w", err)
}
- stack.Push(NewBoolValue(toNumber(a) != toNumber(b)))
+ stack.Push(NewFloatFromBool(a.Float64() != b.Float64()))
return nil
}
@@ -830,8 +856,8 @@ func (o *Operations) AssignVariable(stack *Stack, name string) error {
return err
}
- // Convert Value to float64 for variable storage
- return o.vars.SetVariable(name, toNumber(val))
+ // Convert Number to float64 for variable storage
+ return o.vars.SetVariable(name, val.Float64())
}
// UseVariable pushes a variable's value onto the stack.
@@ -846,7 +872,7 @@ func (o *Operations) UseVariable(stack *Stack, name string) error {
return fmt.Errorf("%w: %s", ErrVariableNotFound, name)
}
- stack.Push(NewNumberValue(val))
+ stack.Push(NewNumber(val, o.mode))
return nil
}
diff --git a/internal/rpn/operations_test.go b/internal/rpn/operations_test.go
index 02e07c3..dc70ebe 100644
--- a/internal/rpn/operations_test.go
+++ b/internal/rpn/operations_test.go
@@ -23,9 +23,9 @@ func TestStackNewStack(t *testing.T) {
func TestStackPushPop(t *testing.T) {
s := NewStack()
- s.Push(NewNumberValue(1.0))
- s.Push(NewNumberValue(2.0))
- s.Push(NewNumberValue(3.0))
+ s.Push(NewNumber(1.0, FloatMode))
+ s.Push(NewNumber(2.0, FloatMode))
+ s.Push(NewNumber(3.0, FloatMode))
if s.Len() != 3 {
t.Errorf("Length after 3 pushes = %d, want 3", s.Len())
@@ -35,7 +35,7 @@ func TestStackPushPop(t *testing.T) {
if err != nil {
t.Fatalf("Pop() returned error: %v", err)
}
- if val.Number() != 3.0 {
+ if val.Float64() != 3.0 {
t.Errorf("Pop() = %v, want 3.0", val)
}
@@ -46,13 +46,13 @@ func TestStackPushPop(t *testing.T) {
func TestStackPeek(t *testing.T) {
s := NewStack()
- s.Push(NewNumberValue(5.0))
+ s.Push(NewNumber(5.0, FloatMode))
val, err := s.Peek()
if err != nil {
t.Fatalf("Peek() returned error: %v", err)
}
- if val.Number() != 5.0 {
+ if val.Float64() != 5.0 {
t.Errorf("Peek() = %v, want 5.0", val)
}
@@ -83,9 +83,9 @@ func TestStackPopEmpty(t *testing.T) {
func TestStackValues(t *testing.T) {
s := NewStack()
- s.Push(NewNumberValue(1.0))
- s.Push(NewNumberValue(2.0))
- s.Push(NewNumberValue(3.0))
+ s.Push(NewNumber(1.0, FloatMode))
+ s.Push(NewNumber(2.0, FloatMode))
+ s.Push(NewNumber(3.0, FloatMode))
vals := s.Values()
if len(vals) != 3 {
@@ -94,16 +94,16 @@ func TestStackValues(t *testing.T) {
// Values() returns values in storage order (bottom-to-top)
// Push order: 1, 2, 3 so storage is [1, 2, 3] with 3 on top
- if vals[0].Number() != 1.0 || vals[1].Number() != 2.0 || vals[2].Number() != 3.0 {
+ if vals[0].Float64() != 1.0 || vals[1].Float64() != 2.0 || vals[2].Float64() != 3.0 {
t.Errorf("Values() = %v, want [1 2 3] (bottom-to-top)", vals)
}
}
func TestStackClear(t *testing.T) {
s := NewStack()
- s.Push(NewNumberValue(1.0))
- s.Push(NewNumberValue(2.0))
- s.Push(NewNumberValue(3.0))
+ s.Push(NewNumber(1.0, FloatMode))
+ s.Push(NewNumber(2.0, FloatMode))
+ s.Push(NewNumber(3.0, FloatMode))
s.Clear()
@@ -116,8 +116,8 @@ func TestOperationsAdd(t *testing.T) {
v := NewVariables()
o := NewOperations(v)
s := NewStack()
- s.Push(NewNumberValue(3.0))
- s.Push(NewNumberValue(4.0))
+ s.Push(NewNumber(3.0, FloatMode))
+ s.Push(NewNumber(4.0, FloatMode))
err := o.Add(s)
if err != nil {
@@ -128,7 +128,7 @@ func TestOperationsAdd(t *testing.T) {
if err != nil {
t.Fatalf("Pop() after Add() returned error: %v", err)
}
- if val.Number() != 7.0 {
+ if val.Float64() != 7.0 {
t.Errorf("Add result = %v, want 7.0", val)
}
}
@@ -137,8 +137,8 @@ func TestOperationsSubtract(t *testing.T) {
v := NewVariables()
o := NewOperations(v)
s := NewStack()
- s.Push(NewNumberValue(10.0))
- s.Push(NewNumberValue(4.0))
+ s.Push(NewNumber(10.0, FloatMode))
+ s.Push(NewNumber(4.0, FloatMode))
err := o.Subtract(s)
if err != nil {
@@ -149,7 +149,7 @@ func TestOperationsSubtract(t *testing.T) {
if err != nil {
t.Fatalf("Pop() after Subtract() returned error: %v", err)
}
- if val.Number() != 6.0 {
+ if val.Float64() != 6.0 {
t.Errorf("Subtract result = %v, want 6.0 (10 - 4)", val)
}
}
@@ -158,8 +158,8 @@ func TestOperationsMultiply(t *testing.T) {
v := NewVariables()
o := NewOperations(v)
s := NewStack()
- s.Push(NewNumberValue(5.0))
- s.Push(NewNumberValue(3.0))
+ s.Push(NewNumber(5.0, FloatMode))
+ s.Push(NewNumber(3.0, FloatMode))
err := o.Multiply(s)
if err != nil {
@@ -170,7 +170,7 @@ func TestOperationsMultiply(t *testing.T) {
if err != nil {
t.Fatalf("Pop() after Multiply() returned error: %v", err)
}
- if val.Number() != 15.0 {
+ if val.Float64() != 15.0 {
t.Errorf("Multiply result = %v, want 15.0", val)
}
}
@@ -179,8 +179,8 @@ func TestOperationsDivide(t *testing.T) {
v := NewVariables()
o := NewOperations(v)
s := NewStack()
- s.Push(NewNumberValue(20.0))
- s.Push(NewNumberValue(4.0))
+ s.Push(NewNumber(20.0, FloatMode))
+ s.Push(NewNumber(4.0, FloatMode))
err := o.Divide(s)
if err != nil {
@@ -191,7 +191,7 @@ func TestOperationsDivide(t *testing.T) {
if err != nil {
t.Fatalf("Pop() after Divide() returned error: %v", err)
}
- if val.Number() != 5.0 {
+ if val.Float64() != 5.0 {
t.Errorf("Divide result = %v, want 5.0", val)
}
}
@@ -200,8 +200,8 @@ func TestOperationsDivideByZero(t *testing.T) {
v := NewVariables()
o := NewOperations(v)
s := NewStack()
- s.Push(NewNumberValue(10.0))
- s.Push(NewNumberValue(0.0))
+ s.Push(NewNumber(10.0, FloatMode))
+ s.Push(NewNumber(0.0, FloatMode))
err := o.Divide(s)
if err == nil {
@@ -216,8 +216,8 @@ func TestOperationsPower(t *testing.T) {
v := NewVariables()
o := NewOperations(v)
s := NewStack()
- s.Push(NewNumberValue(2.0))
- s.Push(NewNumberValue(3.0))
+ s.Push(NewNumber(2.0, FloatMode))
+ s.Push(NewNumber(3.0, FloatMode))
err := o.Power(s)
if err != nil {
@@ -228,7 +228,7 @@ func TestOperationsPower(t *testing.T) {
if err != nil {
t.Fatalf("Pop() after Power() returned error: %v", err)
}
- if val.Number() != 8.0 {
+ if val.Float64() != 8.0 {
t.Errorf("Power result = %v, want 8.0 (2^3)", val)
}
}
@@ -237,8 +237,8 @@ func TestOperationsModulo(t *testing.T) {
v := NewVariables()
o := NewOperations(v)
s := NewStack()
- s.Push(NewNumberValue(10.0))
- s.Push(NewNumberValue(3.0))
+ s.Push(NewNumber(10.0, FloatMode))
+ s.Push(NewNumber(3.0, FloatMode))
err := o.Modulo(s)
if err != nil {
@@ -249,7 +249,7 @@ func TestOperationsModulo(t *testing.T) {
if err != nil {
t.Fatalf("Pop() after Modulo() returned error: %v", err)
}
- if val.Number() != 1.0 {
+ if val.Float64() != 1.0 {
t.Errorf("Modulo result = %v, want 1.0 (10 %% 3)", val)
}
}
@@ -258,8 +258,8 @@ func TestOperationsModuloByZero(t *testing.T) {
v := NewVariables()
o := NewOperations(v)
s := NewStack()
- s.Push(NewNumberValue(10.0))
- s.Push(NewNumberValue(0.0))
+ s.Push(NewNumber(10.0, FloatMode))
+ s.Push(NewNumber(0.0, FloatMode))
err := o.Modulo(s)
if err == nil {
@@ -271,7 +271,7 @@ func TestOperationsInsufficientOperands(t *testing.T) {
v := NewVariables()
o := NewOperations(v)
s := NewStack()
- s.Push(NewNumberValue(5.0))
+ s.Push(NewNumber(5.0, FloatMode))
// Try to add with only one operand
err := o.Add(s)
@@ -284,7 +284,7 @@ func TestOperationsDup(t *testing.T) {
v := NewVariables()
o := NewOperations(v)
s := NewStack()
- s.Push(NewNumberValue(7.0))
+ s.Push(NewNumber(7.0, FloatMode))
err := o.Dup(s)
if err != nil {
@@ -297,7 +297,7 @@ func TestOperationsDup(t *testing.T) {
val1, _ := s.Pop()
val2, _ := s.Pop()
- if val1.Number() != 7.0 || val2.Number() != 7.0 {
+ if val1.Float64() != 7.0 || val2.Float64() != 7.0 {
t.Errorf("Dup values = %v, %v, want both 7.0", val1, val2)
}
}
@@ -306,8 +306,8 @@ func TestOperationsSwap(t *testing.T) {
v := NewVariables()
o := NewOperations(v)
s := NewStack()
- s.Push(NewNumberValue(1.0))
- s.Push(NewNumberValue(2.0))
+ s.Push(NewNumber(1.0, FloatMode))
+ s.Push(NewNumber(2.0, FloatMode))
err := o.Swap(s)
if err != nil {
@@ -316,7 +316,7 @@ func TestOperationsSwap(t *testing.T) {
val1, _ := s.Pop()
val2, _ := s.Pop()
- if val1.Number() != 1.0 || val2.Number() != 2.0 {
+ if val1.Float64() != 1.0 || val2.Float64() != 2.0 {
t.Errorf("After Swap, values = %v, %v, want 1.0, 2.0 (swapped)", val1, val2)
}
}
@@ -325,7 +325,7 @@ func TestOperationsSwapInsufficient(t *testing.T) {
v := NewVariables()
o := NewOperations(v)
s := NewStack()
- s.Push(NewNumberValue(5.0))
+ s.Push(NewNumber(5.0, FloatMode))
err := o.Swap(s)
if err == nil {
@@ -337,9 +337,9 @@ func TestOperationsPop(t *testing.T) {
v := NewVariables()
o := NewOperations(v)
s := NewStack()
- s.Push(NewNumberValue(1.0))
- s.Push(NewNumberValue(2.0))
- s.Push(NewNumberValue(3.0))
+ s.Push(NewNumber(1.0, FloatMode))
+ s.Push(NewNumber(2.0, FloatMode))
+ s.Push(NewNumber(3.0, FloatMode))
err := o.Pop(s)
if err != nil {
@@ -366,9 +366,9 @@ func TestOperationsShow(t *testing.T) {
v := NewVariables()
o := NewOperations(v)
s := NewStack()
- s.Push(NewNumberValue(1.0))
- s.Push(NewNumberValue(2.0))
- s.Push(NewNumberValue(3.0))
+ s.Push(NewNumber(1.0, FloatMode))
+ s.Push(NewNumber(2.0, FloatMode))
+ s.Push(NewNumber(3.0, FloatMode))
result, err := o.Show(s)
if err != nil {
@@ -399,7 +399,7 @@ func TestOperationsAssignVariable(t *testing.T) {
v := NewVariables()
o := NewOperations(v)
s := NewStack()
- s.Push(NewNumberValue(5.0))
+ s.Push(NewNumber(5.0, FloatMode))
err := o.AssignVariable(s, "x")
if err != nil {
@@ -449,7 +449,7 @@ func TestOperationsUseVariable(t *testing.T) {
if err != nil {
t.Fatalf("Pop() after UseVariable() returned error: %v", err)
}
- if val.Number() != 3.14159 {
+ if val.Float64() != 3.14159 {
t.Errorf("Variable value pushed to stack = %v, want 3.14159", val)
}
}
@@ -545,7 +545,7 @@ func TestOperationsConcurrent(t *testing.T) {
go func(id int) {
name := fmt.Sprintf("concurrent%d", id)
s := NewStack()
- s.Push(NewNumberValue(float64(id)))
+ s.Push(NewNumber(float64(id), FloatMode))
if err := o.AssignVariable(s, name); err != nil {
t.Errorf("AssignVariable() returned error: %v", err)
}
@@ -567,7 +567,7 @@ func TestLog2(t *testing.T) {
stack := NewStack()
// Test log₂(8) = 3
- stack.Push(NewNumberValue(8))
+ stack.Push(NewNumber(8, FloatMode))
err := o.Log2(stack)
if err != nil {
t.Errorf("Log2() returned error: %v", err)
@@ -576,12 +576,12 @@ func TestLog2(t *testing.T) {
if err != nil {
t.Errorf("Pop() returned error: %v", err)
}
- if val.Number() != 3.0 {
- t.Errorf("Log2(8) = %f, want 3.0)", val.Number())
+ if val.Float64() != 3.0 {
+ t.Errorf("Log2(8) = %f, want 3.0)", val.Float64())
}
// Test log₂(1) = 0
- stack.Push(NewNumberValue(1))
+ stack.Push(NewNumber(1.0, FloatMode))
err = o.Log2(stack)
if err != nil {
t.Errorf("Log2(1) returned error: %v", err)
@@ -590,12 +590,12 @@ func TestLog2(t *testing.T) {
if err != nil {
t.Errorf("Pop() returned error: %v", err)
}
- if val.Number() != 0.0 {
- t.Errorf("Log2(1) = %f, want 0.0)", val.Number())
+ if val.Float64() != 0.0 {
+ t.Errorf("Log2(1) = %f, want 0.0)", val.Float64())
}
// Test log₂(0) should error
- stack.Push(NewNumberValue(0))
+ stack.Push(NewNumber(0.0, FloatMode))
err = o.Log2(stack)
if err == nil {
t.Errorf("Log2(0) should return error, got nil")
@@ -607,7 +607,7 @@ func TestLog10(t *testing.T) {
stack := NewStack()
// Test log₁₀(100) = 2
- stack.Push(NewNumberValue(100))
+ stack.Push(NewNumber(100.0, FloatMode))
err := o.Log10(stack)
if err != nil {
t.Errorf("Log10() returned error: %v", err)
@@ -616,12 +616,12 @@ func TestLog10(t *testing.T) {
if err != nil {
t.Errorf("Pop() returned error: %v", err)
}
- if val.Number() != 2.0 {
- t.Errorf("Log10(100) = %f, want 2.0)", val.Number())
+ if val.Float64() != 2.0 {
+ t.Errorf("Log10(100) = %f, want 2.0)", val.Float64())
}
// Test log₁₀(1) = 0
- stack.Push(NewNumberValue(1))
+ stack.Push(NewNumber(1.0, FloatMode))
err = o.Log10(stack)
if err != nil {
t.Errorf("Log10(1) returned error: %v", err)
@@ -630,8 +630,8 @@ func TestLog10(t *testing.T) {
if err != nil {
t.Errorf("Pop() returned error: %v", err)
}
- if val.Number() != 0.0 {
- t.Errorf("Log10(1) = %f, want 0.0)", val.Number())
+ if val.Float64() != 0.0 {
+ t.Errorf("Log10(1) = %f, want 0.0)", val.Float64())
}
}
@@ -640,7 +640,7 @@ func TestLn(t *testing.T) {
stack := NewStack()
// Test ln(e) ≈ 1
- stack.Push(NewNumberValue(math.E))
+ stack.Push(NewNumber(math.E, FloatMode))
er