controllogic: make expr evaluator value-polymorphic (arrays, indexing, array funcs)
Rewrites internal/controllogic/expr.go to evaluate to Value (scalar float64 or []Value array): adds array literals [a,b], postfix indexing arr[i], and array functions (len, sum, mean, push, pop, slice, concat, sort, …). Resolver type changes from func(...) float64 to func(...) Value; EvalValue added; EvalExpr/EvalBool retain scalar float64/bool returns. Three temporary shims added in engine.go, lua.go, and expr_test.go pending Tasks 4 and 6. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,11 +2,12 @@ package controllogic
|
||||
|
||||
import (
|
||||
"math"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEvalExpr(t *testing.T) {
|
||||
resolve := func(ds, name string) float64 {
|
||||
resolve := func(ds, name string) Value { // shim: was float64 (Task 4 removes)
|
||||
switch {
|
||||
case ds == "stub" && name == "x":
|
||||
return 10
|
||||
@@ -45,7 +46,7 @@ func TestEvalExpr(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestEvalExprErrors(t *testing.T) {
|
||||
r := func(ds, name string) float64 { return 0 }
|
||||
r := func(ds, name string) Value { return 0 } // shim: was float64 (Task 4 removes)
|
||||
for _, bad := range []string{"1 +", "(1", "1 2", "{unterminated"} {
|
||||
if v := EvalExpr(bad, r); !math.IsNaN(v) {
|
||||
t.Errorf("EvalExpr(%q) = %v, want NaN", bad, v)
|
||||
@@ -84,3 +85,72 @@ func TestEpicsRefSplitFirstColon(t *testing.T) {
|
||||
t.Errorf("got %+v, want epics / SR:BPM:01:X", refs)
|
||||
}
|
||||
}
|
||||
|
||||
// ── New tests for value-polymorphic evaluator ─────────────────────────────────
|
||||
|
||||
func numResolver(vals map[string]Value) Resolver {
|
||||
return func(ds, name string) Value {
|
||||
if v, ok := vals[ds+":"+name]; ok {
|
||||
return v
|
||||
}
|
||||
return math.NaN()
|
||||
}
|
||||
}
|
||||
|
||||
func TestEvalValueScalar(t *testing.T) {
|
||||
R := numResolver(nil)
|
||||
if got := EvalExpr("2 + 3 * 4", R); got != 14 {
|
||||
t.Fatalf("scalar = %v", got)
|
||||
}
|
||||
if !EvalBool("1 < 2 && 3 >= 3", R) {
|
||||
t.Fatal("bool expr should be true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEvalValueArrayLiteralAndIndex(t *testing.T) {
|
||||
R := numResolver(nil)
|
||||
got := EvalValue("[1, 2, 3]", R)
|
||||
if !reflect.DeepEqual(got, []Value{1.0, 2.0, 3.0}) {
|
||||
t.Fatalf("array literal = %#v", got)
|
||||
}
|
||||
if v := EvalExpr("[10,20,30][-1]", R); v != 30 {
|
||||
t.Fatalf("index -1 = %v", v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEvalArrayFuncs(t *testing.T) {
|
||||
R := numResolver(map[string]Value{"local:buf": []Value{3.0, 1.0, 2.0}})
|
||||
if v := EvalExpr("len(buf)", R); v != 3 {
|
||||
t.Fatalf("len = %v", v)
|
||||
}
|
||||
if v := EvalExpr("sum(buf)", R); v != 6 {
|
||||
t.Fatalf("sum = %v", v)
|
||||
}
|
||||
if v := EvalExpr("max(buf)", R); v != 3 {
|
||||
t.Fatalf("max(array) = %v", v)
|
||||
}
|
||||
if v := EvalExpr("max(1, 9, 4)", R); v != 9 {
|
||||
t.Fatalf("max(scalars) = %v", v)
|
||||
}
|
||||
got := EvalValue("push(buf, 7)", R)
|
||||
if !reflect.DeepEqual(got, []Value{3.0, 1.0, 2.0, 7.0}) {
|
||||
t.Fatalf("push = %#v", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEvalExprArrayYieldsNaN(t *testing.T) {
|
||||
if v := EvalExpr("[1,2]", numResolver(nil)); !math.IsNaN(v) {
|
||||
t.Fatalf("array via EvalExpr should be NaN, got %v", v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCollectRefsArray(t *testing.T) {
|
||||
refs := CollectRefs("[{ds:a}, b[0]] ")
|
||||
keys := map[string]bool{}
|
||||
for _, r := range refs {
|
||||
keys[r.DS+":"+r.Name] = true
|
||||
}
|
||||
if !keys["ds:a"] || !keys["local:b"] {
|
||||
t.Fatalf("refs = %#v", refs)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user