controllogic: add action.array.* nodes (push/set/remove/pop/clear)

This commit is contained in:
Martino Ferrari
2026-06-24 19:45:40 +02:00
parent a6fa4e7c7c
commit 088063d9cd
2 changed files with 155 additions and 0 deletions
+100
View File
@@ -276,6 +276,31 @@ func (e *Engine) liveGet(ds, name string) float64 {
return v
}
// setPath assigns v at the nested index path within arr, growing (zero-filling)
// as needed and resolving negative indices against length. Port of logic.ts.
func setPath(arr []Value, path []int, v Value) []Value {
if len(path) == 0 {
return arr
}
k := path[0]
if k < 0 {
k = len(arr) + k
}
if k < 0 {
return arr
}
for len(arr) <= k {
arr = append(arr, 0.0)
}
if len(path) == 1 {
arr[k] = v
return arr
}
sub, _ := arr[k].([]Value)
arr[k] = setPath(sub, path[1:], v)
return arr
}
// write applies an action.write/lua-set to a target: a bare name updates a
// graph-local var; a ds:name target writes to the data source.
func (e *Engine) write(cg *compiledGraph, target string, val Value) {
@@ -686,6 +711,13 @@ func compile(g Graph) *compiledGraph {
}
case "action.write", "action.log":
wantExpr(n.param("expr"))
case "action.array.push":
wantExpr(n.param("expr"))
case "action.array.set":
wantExpr(n.param("expr"))
wantExpr(n.param("index"))
case "action.array.remove":
wantExpr(n.param("index"))
case "action.lua":
cg.luaNodes[n.ID] = newLuaRuntime(n.param("script"))
for _, r := range luaGetRefs(n.param("script")) {
@@ -1095,6 +1127,74 @@ func (cg *compiledGraph) run(nodeID string, ctx *runCtx) {
}
cg.follow(node.ID, "out", ctx)
case "action.array.push":
name := strings.TrimSpace(node.param("array"))
if name != "" {
val := EvalValue(node.param("expr"), ctx.resolve)
cur, _ := cg.getLocal(name).([]Value)
cg.setLocal(name, append(append([]Value{}, cur...), val))
}
cg.follow(node.ID, "out", ctx)
case "action.array.set":
name := strings.TrimSpace(node.param("array"))
if name != "" {
cur, _ := cg.getLocal(name).([]Value)
arr := append([]Value{}, cur...)
var path []int
ok := true
for _, s := range strings.Split(node.param("index"), ",") {
f := EvalExpr(strings.TrimSpace(s), ctx.resolve)
if math.IsNaN(f) {
ok = false
break
}
path = append(path, int(f))
}
val := EvalValue(node.param("expr"), ctx.resolve)
if ok && len(path) > 0 {
arr = setPath(arr, path, val)
}
cg.setLocal(name, arr)
}
cg.follow(node.ID, "out", ctx)
case "action.array.remove":
name := strings.TrimSpace(node.param("array"))
if name != "" {
cur, _ := cg.getLocal(name).([]Value)
arr := append([]Value{}, cur...)
i := int(EvalExpr(node.param("index"), ctx.resolve))
k := i
if k < 0 {
k = len(arr) + k
}
if k >= 0 && k < len(arr) {
arr = append(arr[:k], arr[k+1:]...)
}
cg.setLocal(name, arr)
}
cg.follow(node.ID, "out", ctx)
case "action.array.pop":
name := strings.TrimSpace(node.param("array"))
if name != "" {
cur, _ := cg.getLocal(name).([]Value)
arr := append([]Value{}, cur...)
if len(arr) > 0 {
arr = arr[:len(arr)-1]
}
cg.setLocal(name, arr)
}
cg.follow(node.ID, "out", ctx)
case "action.array.clear":
name := strings.TrimSpace(node.param("array"))
if name != "" {
cg.setLocal(name, []Value{}) // setLocal applies sizing (fixed → zero-pad)
}
cg.follow(node.ID, "out", ctx)
default:
cg.follow(node.ID, "out", ctx)
}