From 6b561fea45c5d67933456db10da16a691cb12f18 Mon Sep 17 00:00:00 2001 From: Celestino Amoroso Date: Mon, 13 Jul 2026 14:39:26 +0200 Subject: [PATCH] array and linked-list: at operator --- operator-at.go | 56 ++++++++++++++++++ scan/symbol-map.go | 1 + scan/symbol.go | 2 + t_array_test.go | 102 +++++++++++++++++++++++++++++++++ t_list_test.go | 2 + types/list/linked-list-type.go | 13 +++++ 6 files changed, 176 insertions(+) create mode 100644 operator-at.go create mode 100644 t_array_test.go diff --git a/operator-at.go b/operator-at.go new file mode 100644 index 0000000..fc8b635 --- /dev/null +++ b/operator-at.go @@ -0,0 +1,56 @@ +// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com). +// All rights reserved. + +// operator-at.go +package expr + +import ( + "git.portale-stac.it/go-pkg/expr/kern" + "git.portale-stac.it/go-pkg/expr/scan" + "git.portale-stac.it/go-pkg/expr/types/array" + "git.portale-stac.it/go-pkg/expr/types/list" +) + +//-------- at term + +func newAtTerm(tk *scan.Token) (inst *scan.Term) { + return &scan.Term{ + Tk: *tk, + Children: make([]*scan.Term, 0, 2), + Position: scan.PosInfix, + Priority: scan.PriRelational, + EvalFunc: evalAt, + } +} + +// func hasKey(d map[any]any, target any) (ok bool) { +// _, ok = d[target] +// return +// } + +func evalAt(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) { + var leftValue, rightValue any + + if leftValue, rightValue, err = opTerm.EvalInfix(ctx); err != nil { + return + } + + if array.IsArray(rightValue) { + a, _ := rightValue.(*array.ArrayType) + v = a.IndexDeepSameCmp(leftValue) + // } else if dict.IsDict(rightValue) { + // dict, _ := rightValue.(*dict.DictType) + // v = dict.HasKey(leftValue) + } else if list.IsLinkedList(rightValue) { + ls, _ := rightValue.(*list.LinkedList) + v = ls.Index(leftValue) + } else { + err = opTerm.ErrIncompatibleTypes(leftValue, rightValue) + } + return +} + +// init +func init() { + scan.RegisterTermConstructor(scan.SymKwAt, newAtTerm) +} diff --git a/scan/symbol-map.go b/scan/symbol-map.go index e8a22ed..7ab523e 100644 --- a/scan/symbol-map.go +++ b/scan/symbol-map.go @@ -149,6 +149,7 @@ func init() { SymKwBuiltin: {"builtin", SymClassOperator, PosPrefix}, SymKwPlugin: {"plugin", SymClassOperator, PosPrefix}, SymKwIn: {"in", SymClassOperator, PosInfix}, + SymKwAt: {"at", SymClassOperator, PosInfix}, SymKwInclude: {"include", SymClassOperator, PosPrefix}, SymKwNil: {"nil", SymClassValue, PosLeaf}, SymKwUnset: {"unset", SymClassOperator, PosPrefix}, diff --git a/scan/symbol.go b/scan/symbol.go index ce175db..8e6716c 100644 --- a/scan/symbol.go +++ b/scan/symbol.go @@ -122,6 +122,7 @@ const ( SymKwBuiltin SymKwPlugin SymKwIn + SymKwAt SymKwInclude SymKwMap SymKwFilter @@ -144,6 +145,7 @@ func init() { "BUT": SymKwBut, "FUNC": SymKwFunc, "IN": SymKwIn, + "AT": SymKwAt, "INCLUDE": SymKwInclude, "MAP": SymKwMap, "CAT": SymKwCat, diff --git a/t_array_test.go b/t_array_test.go new file mode 100644 index 0000000..2dfcad6 --- /dev/null +++ b/t_array_test.go @@ -0,0 +1,102 @@ +// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com). +// All rights reserved. + +// t_array_test.go +package expr + +import ( + "testing" + + "git.portale-stac.it/go-pkg/expr/types/array" +) + +func TestArray(t *testing.T) { + section := "Array" + + inputs := []inputType{ + /* 1 */ {`[]`, array.NewArrayA(), nil}, + /* 2 */ {`[1,2,3]`, array.NewArrayA(int64(1), int64(2), int64(3)), nil}, + /* 3 */ {`[1,2,"hello"]`, array.NewArrayA(int64(1), int64(2), "hello"), nil}, + /* 4 */ {`[1+2, not true, "hello"]`, array.NewArrayA(int64(3), false, "hello"), nil}, + /* 5 */ {`[1,2]+[3]`, array.NewArrayA(int64(1), int64(2), int64(3)), nil}, + /* 6 */ {`[1,4,3,2]-[3]`, array.NewArrayA(int64(1), int64(4), int64(2)), nil}, + /* 7 */ {`builtin "math.arith"; add([1,4,3,2])`, int64(10), nil}, + /* 8 */ {`builtin "math.arith"; add([1,[2,2],3,2])`, int64(10), nil}, + /* 9 */ {`builtin "math.arith"; mul([1,4,3.0,2])`, float64(24.0), nil}, + /* 10 */ {`builtin "math.arith"; add([1,"hello"])`, nil, `add(): param nr 2 (2 in 1) has wrong type string, number expected`}, + /* 11 */ {`[a=1,b=2,c=3] but a+b+c`, int64(6), nil}, + /* 12 */ {`[1,2,3] <+ 2+2`, array.NewArrayA(int64(1), int64(2), int64(3), int64(4)), nil}, + /* 13 */ {`2-1 +> [2,3]`, array.NewArrayA(int64(1), int64(2), int64(3)), nil}, + /* 14 */ {`[1,2,3][1]`, int64(2), nil}, + /* 15 */ {`ls=[1,2,3] but ls[1]`, int64(2), nil}, + /* 16 */ {`ls=[1,2,3] but ls[-1]`, int64(3), nil}, + /* 17 */ {`list=["one","two","three"]; list[10]`, nil, `[1:34] index 10 out of bounds`}, + /* 18 */ {`["a", "b", "c"]`, array.NewArrayA("a", "b", "c"), nil}, + /* 19 */ {`["a", "b", "c"]`, array.NewArray([]any{"a", "b", "c"}), nil}, + /* 20 */ {`#["a", "b", "c"]`, int64(3), nil}, + /* 21 */ {`"b" in ["a", "b", "c"]`, true, nil}, + /* 22 */ {`a=[1,2]; (a)<+3`, array.NewArrayA(int64(1), int64(2), int64(3)), nil}, + /* 23 */ {`a=[1,2]; (a)<+3; a`, array.NewArrayA(int64(1), int64(2)), nil}, + /* 24 */ {`["a","b","c","d"][1]`, "b", nil}, + /* 25 */ {`["a","b","c","d"][1,1]`, nil, `[1:19] one index only is allowed`}, + /* 26 */ {`[0,1,2,3,4][:]`, array.NewArrayA(int64(0), int64(1), int64(2), int64(3), int64(4)), nil}, + /* 27 */ {`["a", "b", "c"] <+ ;`, nil, `[1:18] infix operator "<+" requires two non-nil operands, got 1`}, + /* 28 */ {`2 << 3;`, int64(16), nil}, + /* 29 */ {`but +> ["a", "b", "c"]`, nil, `[1:6] infix operator "+>" requires two non-nil operands, got 0`}, + /* 30 */ {`2 >> 3;`, int64(0), nil}, + /* 31 */ {`a=[1,2]; a<+3`, array.NewArrayA(int64(1), int64(2), int64(3)), nil}, + /* 32 */ {`a=[1,2]; 5+>a`, array.NewArrayA(int64(5), int64(1), int64(2)), nil}, + /* 33 */ {`L=[1,2]; L[0]=9; L`, array.NewArrayA(int64(9), int64(2)), nil}, + /* 34 */ {`L=[1,2]; L[5]=9; L`, nil, `index 5 out of bounds (0, 1)`}, + /* 35 */ {`L=[1,2]; L[]=9; L`, nil, `[1:12] index/key specification expected, got [] [array]`}, + /* 36 */ {`L=[1,2]; L[nil]=9;`, nil, `[1:12] index/key is nil`}, + /* 37 */ {`[0,1,2,3,4][2:3]`, array.NewArrayA(int64(2)), nil}, + /* 38 */ {`[0,1,2,3,4][3:-1]`, array.NewArrayA(int64(3)), nil}, + /* 30 */ {`[0,1,2,3,4][-3:-1]`, array.NewArrayA(int64(2), int64(3)), nil}, + /* 40 */ {`[0,1,2,3,4][0:]`, array.NewArrayA(int64(0), int64(1), int64(2), int64(3), int64(4)), nil}, + /* 41 */ {`[0] << $([1,2,3,4])`, array.NewArrayA(int64(0), int64(1), int64(2), int64(3), int64(4)), nil}, + /* 42 */ {`L=[]; [1] >> L; L`, array.NewArrayA(), nil}, + /* 43 */ {`L=[]; L << [1]; L`, array.NewArrayA(), nil}, + /* 44 */ {`2 IN [1,2,3]`, true, nil}, + /* 45 */ {`2 AT [1,2,3]`, int64(1), nil}, + /* 46 */ {`4 AT [1,2,3]`, int64(-1), nil}, + // /* 44 */ {`[0,1,2,3,4][2:3]`, kern.NewListA(int64(20)), nil}, + } + + // t.Setenv("EXPR_PATH", ".") + + // runTestSuiteSpec(t, section, inputs, 44) + RunTestSuite(t, section, inputs) +} + +func TestArrayInsert(t *testing.T) { + section := "Array-Insert" + inputs := []inputType{ + /* 1 */ {`["a", "b"] << nil`, array.NewArrayA("a", "b"), nil}, + /* 2 */ {`["a", "b"] << []`, array.NewArrayA("a", "b", array.NewArrayA()), nil}, + /* 3 */ {`["a", "b"] << $([])`, array.NewArrayA("a", "b"), nil}, + /* 4 */ {`["a", "b"] << 3`, array.NewArrayA("a", "b", int64(3)), nil}, + /* 5 */ {`3 << ["a", "b"]`, nil, `[1:5] left operand '3' [integer] and right operand '["a", "b"]' [array] are not compatible with operator "<<"`}, + /* 6 */ {`nil >> ["a", "b"]`, array.NewArrayA("a", "b"), nil}, + /* 7 */ {`[] >> ["a", "b"]`, array.NewArrayA(array.NewArrayA(), "a", "b"), nil}, + /* 8 */ {`$([]) >> ["a", "b"]`, array.NewArrayA("a", "b"), nil}, + /* 9 */ {`["a", "b"] << $([1,2,3])`, array.NewArrayA("a", "b", int64(1), int64(2), int64(3)), nil}, + /* 10 */ {`L=["a", "b"]; L << $([1,2,3])`, array.NewArrayA("a", "b", int64(1), int64(2), int64(3)), nil}, + /* 11 */ {`L=["a", "b"]; L << $([1,2,3]); L`, array.NewArrayA("a", "b"), nil}, + /* 12 */ {`L << $([1,2,3])`, nil, `undefined variable or function "L"`}, + } + + // runTestSuiteSpec(t, section, inputs, 9) + RunTestSuite(t, section, inputs) +} + +func TestArrayDeepAssign(t *testing.T) { + section := "Array-DeepAssign" + inputs := []inputType{ + /* 1 */ {`LL=["a", "b"]; L=LL; L[0]="x"; LL`, array.NewArrayA("x", "b"), nil}, + /* 2 */ {`LL=["a", "b"]; L:=LL; L[0]="x"; LL`, array.NewArrayA("a", "b"), nil}, + } + + // runTestSuiteSpec(t, section, inputs, 4) + RunTestSuite(t, section, inputs) +} diff --git a/t_list_test.go b/t_list_test.go index 24d040a..b9750e5 100644 --- a/t_list_test.go +++ b/t_list_test.go @@ -80,6 +80,8 @@ func TestLinkedListParser(t *testing.T) { /* 7 */ {`L=[<1>] + [<2,3>]; L[0]=100; L`, list.NewLinkedListA(100, 2, 3), nil}, /* 8 */ {`builtin "base"; L=[<1>]; isList(L)`, true, nil}, /* 9 */ {`2 IN [<1,2,3>]`, true, nil}, + /* 10 */ {`2 AT [<1,2,3>]`, int64(1), nil}, + /* 11 */ {`4 AT [<1,2,3>]`, int64(-1), nil}, } // t.Setenv("EXPR_PATH", ".") diff --git a/types/list/linked-list-type.go b/types/list/linked-list-type.go index b0ad30d..345bfc8 100644 --- a/types/list/linked-list-type.go +++ b/types/list/linked-list-type.go @@ -137,3 +137,16 @@ func (ls *LinkedList) HasValue(value any) (found bool) { } return } + +func (ls *LinkedList) Index(value any) (index int64) { + index = -1 + i := int64(0) + for node := ls.FirstNode(); node != nil; node = node.Next() { + if kern.Equal(node.Data(), value) { + index = i + break + } + i++ + } + return +}