From 2f616f349b0a1c34d9e94dbd48bf6129095e2d17 Mon Sep 17 00:00:00 2001 From: Celestino Amoroso Date: Thu, 16 Jul 2026 07:25:15 +0200 Subject: [PATCH] at operator return -1 when fails in lists and arrays; insert operators (<< and >>) changes che container if it is a variable --- builtin-base.go | 4 ++-- kern/term.go | 7 +++++- operator-at.go | 5 ++++- operator-insert.go | 30 +++++++++---------------- scan/term.go | 22 +++++++++++++++++++ t_array_test.go | 46 +++++++++++++++++++-------------------- t_builtin-base_test.go | 20 ++++++++--------- t_list_test.go | 4 ++-- t_operator_test.go | 2 ++ types/array/array-type.go | 12 ++++++++++ types/list/linked-list.go | 13 +++++++++++ 11 files changed, 105 insertions(+), 60 deletions(-) diff --git a/builtin-base.go b/builtin-base.go index e79e7a1..72cae35 100644 --- a/builtin-base.go +++ b/builtin-base.go @@ -123,7 +123,7 @@ func intFunc(ctx kern.ExprContext, name string, args map[string]any) (result any return } -func decFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) { +func floatFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) { switch v := args[kern.ParamValue].(type) { case int64: result = float64(v) @@ -318,7 +318,7 @@ func ImportBuiltinsFuncs(ctx kern.ExprContext) { ctx.RegisterFunc("bool", kern.NewGolangFunctor(boolFunc), kern.TypeBoolean, anyParams) ctx.RegisterFunc("int", kern.NewGolangFunctor(intFunc), kern.TypeInt, anyParams) - ctx.RegisterFunc("dec", kern.NewGolangFunctor(decFunc), kern.TypeFloat, anyParams) + ctx.RegisterFunc("float", kern.NewGolangFunctor(floatFunc), kern.TypeFloat, anyParams) ctx.RegisterFunc("string", kern.NewGolangFunctor(stringFunc), kern.TypeString, anyParams) ctx.RegisterFunc("fract", kern.NewGolangFunctor(fractFunc), kern.TypeFraction, []kern.ExprFuncParam{ kern.NewFuncParam(kern.ParamValue), diff --git a/kern/term.go b/kern/term.go index 6617c48..01f08e3 100644 --- a/kern/term.go +++ b/kern/term.go @@ -15,7 +15,12 @@ type Term interface { GetChildCount() (count int) GetChild(index int) Term GetChildSource(index int) string - Compute(ctx ExprContext) (result any, err error) + GetLeftChild() (c Term) + GetRightChild() (c Term) IsAssign() bool + IsVar() bool + Compute(ctx ExprContext) (result any, err error) + EvalInfix(ctx ExprContext) (leftValue, rightValue any, err error) Errorf(template string, args ...any) (err error) + ErrIncompatibleTypes(leftValue, rightValue any) error } diff --git a/operator-at.go b/operator-at.go index 6025129..7a114a0 100644 --- a/operator-at.go +++ b/operator-at.go @@ -36,6 +36,7 @@ func evalAt(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) { return } + v = int64(-1) // default value if not found if array.IsArray(rightValue) { a, _ := rightValue.(*array.ArrayType) if index := a.IndexDeepSameCmp(leftValue); index >= 0 { @@ -45,14 +46,16 @@ func evalAt(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) { dict, _ := rightValue.(*dict.DictType) if k, exists := dict.FindKey(leftValue); exists { v = k + } else { + v = nil } - } else if list.IsLinkedList(rightValue) { ls, _ := rightValue.(*list.LinkedList) if index := ls.Index(leftValue); index >= 0 { v = index } } else { + v = nil err = opTerm.ErrIncompatibleTypes(leftValue, rightValue) } return diff --git a/operator-insert.go b/operator-insert.go index b173de1..2ec59cd 100644 --- a/operator-insert.go +++ b/operator-insert.go @@ -33,7 +33,7 @@ func newAppendTerm(tk *scan.Token) (inst *scan.Term) { } } -func prependToList(opTerm *scan.Term, leftValue, rightValue any) (result any, err error) { +func prependToList(opTerm kern.Term, leftValue, rightValue any) (result any, err error) { if a, ok := rightValue.(*array.ArrayType); ok { var it kern.Iterator if it, ok = leftValue.(kern.Iterator); !ok { @@ -53,6 +53,10 @@ func prependToList(opTerm *scan.Term, leftValue, rightValue any) (result any, er for _, item := range *a { newArray = append(newArray, item) } + if opTerm.GetRightChild().IsVar() { + // Update the original array if the right operand is a variable + *a = newArray + } result = &newArray } else if ll, ok := rightValue.(*list.LinkedList); ok { var it kern.Iterator @@ -78,7 +82,7 @@ func evalPrepend(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) { return } -func appendToList(opTerm *scan.Term, leftValue, rightValue any) (result any, err error) { +func appendToList(opTerm kern.Term, leftValue, rightValue any) (result any, err error) { if a, ok := leftValue.(*array.ArrayType); ok { var it kern.Iterator if it, ok = rightValue.(kern.Iterator); !ok { @@ -98,6 +102,10 @@ func appendToList(opTerm *scan.Term, leftValue, rightValue any) (result any, err for node := ls.FirstNode(); node != nil; node = node.Next() { newArray = append(newArray, node.Data()) } + if opTerm.GetLeftChild().IsVar() { + // Update the original array if the left operand is a variable + *a = newArray + } result = &newArray } else if ll, ok := leftValue.(*list.LinkedList); ok { var it kern.Iterator @@ -124,24 +132,6 @@ func evalAppend(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) { return } -// // func evalAssignAppend(ctx ExprContext, self *term) (v any, err error) { -// // var leftValue, rightValue any - -// // if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { -// // return -// // } - -// // if IsArray(leftValue) { -// // list, _ := leftValue.(*ListType) -// // newList := append(*list, rightValue) -// // v = &newList -// // if -// // } else { -// // err = self.errIncompatibleTypes(leftValue, rightValue) -// // } -// // return -// // } - // init func init() { scan.RegisterTermConstructor(scan.SymPlusGreater, newPrependTerm) diff --git a/scan/term.go b/scan/term.go index 07424b0..f5a4511 100644 --- a/scan/term.go +++ b/scan/term.go @@ -186,10 +186,32 @@ func (t *Term) GetChildSource(index int) (s string) { return } +func (t *Term) GetLeftChild() (c kern.Term) { + if t.Position == PosInfix && len(t.Children) > 0 { + c = t.Children[0] + } + return +} + +func (t *Term) GetRightChild() (c kern.Term) { + if t.Position == PosInfix && len(t.Children) > 1 { + c = t.Children[1] + } + return +} + func (t *Term) IsAssign() bool { return t.Symbol() == SymEqual } +func (t *Term) IsVar() bool { + return t.Symbol() == SymVariable +} + +// func (t *Term) IsFunc() bool { +// return t.Symbol() == SymFunction +// } + func (t *Term) Compute(ctx kern.ExprContext) (v any, err error) { if t.EvalFunc == nil { err = t.Errorf("undefined eval-func for %q term", t.Source()) diff --git a/t_array_test.go b/t_array_test.go index 2dfcad6..3eafe64 100644 --- a/t_array_test.go +++ b/t_array_test.go @@ -25,8 +25,8 @@ func TestArray(t *testing.T) { /* 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}, + /* 12 */ {`[1,2,3] <+ 2+2`, array.ArrayFromIntsA(1, 2, 3, 4), nil}, + /* 13 */ {`2-1 +> [2,3]`, array.ArrayFromIntsA(1, 2, 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}, @@ -41,31 +41,26 @@ func TestArray(t *testing.T) { /* 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}, + /* 28 */ {`but +> ["a", "b", "c"]`, nil, `[1:6] infix operator "+>" requires two non-nil operands, got 0`}, + /* 29 */ {`a=[1,2]; a<+3`, array.NewArrayA(int64(1), int64(2), int64(3)), nil}, + /* 30 */ {`a=[1,2]; 5+>a`, array.NewArrayA(int64(5), int64(1), int64(2)), nil}, + /* 31 */ {`L=[1,2]; L[0]=9; L`, array.NewArrayA(int64(9), int64(2)), nil}, + /* 32 */ {`L=[1,2]; L[5]=9; L`, nil, `index 5 out of bounds (0, 1)`}, + /* 33 */ {`L=[1,2]; L[]=9; L`, nil, `[1:12] index/key specification expected, got [] [array]`}, + /* 34 */ {`L=[1,2]; L[nil]=9;`, nil, `[1:12] index/key is nil`}, + /* 35 */ {`[0,1,2,3,4][2:3]`, array.NewArrayA(int64(2)), nil}, + /* 36 */ {`[0,1,2,3,4][3:-1]`, array.NewArrayA(int64(3)), nil}, + /* 37 */ {`[0,1,2,3,4][-3:-1]`, array.NewArrayA(int64(2), int64(3)), nil}, + /* 38 */ {`[0,1,2,3,4][0:]`, array.NewArrayA(int64(0), int64(1), int64(2), int64(3), int64(4)), nil}, + /* 39 */ {`2 IN [1,2,3]`, true, nil}, + /* 40 */ {`2 AT [1,2,3]`, int64(1), nil}, + /* 41 */ {`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) + // RunTestSuiteSpec(t, section, inputs, 43) RunTestSuite(t, section, inputs) } @@ -82,11 +77,14 @@ func TestArrayInsert(t *testing.T) { /* 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}, + /* 11 */ {`L=["a", "b"]; L << $([1,2,3]); L`, array.NewArrayA("a", "b", int64(1), int64(2), int64(3)), nil}, /* 12 */ {`L << $([1,2,3])`, nil, `undefined variable or function "L"`}, + /* 13 */ {`[0] << $([1,2,3,4])`, array.NewArrayA(int64(0), int64(1), int64(2), int64(3), int64(4)), nil}, + /* 14 */ {`L=[]; [1] >> L; L`, array.NewArrayA(array.ArrayFromIntsA(1)), nil}, + /* 15 */ {`L=[]; L << [1]; L`, array.NewArrayA(array.ArrayFromIntsA(1)), nil}, } - // runTestSuiteSpec(t, section, inputs, 9) + // RunTestSuiteSpec(t, section, inputs, 14) RunTestSuite(t, section, inputs) } diff --git a/t_builtin-base_test.go b/t_builtin-base_test.go index 624f02f..fdeed57 100644 --- a/t_builtin-base_test.go +++ b/t_builtin-base_test.go @@ -40,13 +40,13 @@ func TestFuncBase(t *testing.T) { /* 23 */ {`fract("1.21(3)")`, fract.NewFraction(91, 75), nil}, /* 24 */ {`fract(1.21(3))`, fract.NewFraction(91, 75), nil}, /* 25 */ {`fract(1.21)`, fract.NewFraction(121, 100), nil}, - /* 26 */ {`dec(2)`, float64(2), nil}, - /* 27 */ {`dec(2.0)`, float64(2), nil}, - /* 28 */ {`dec("2.0")`, float64(2), nil}, - /* 29 */ {`dec(true)`, float64(1), nil}, - /* 30 */ {`dec(true")`, nil, "[1:11] expected one of `,`, `)`, got `\"`"}, - /* 31 */ {`dec()`, nil, `dec(): too few params -- expected 1, got 0`}, - /* 32 */ {`dec(1,2,3)`, nil, `dec(): too many params -- expected 1, got 3`}, + /* 26 */ {`float(2)`, float64(2), nil}, + /* 27 */ {`float(2.0)`, float64(2), nil}, + /* 28 */ {`float("2.0")`, float64(2), nil}, + /* 29 */ {`float(true)`, float64(1), nil}, + /* 30 */ {`float(true")`, nil, "[1:13] expected one of `,`, `)`, got `\"`"}, + /* 31 */ {`float()`, nil, `float(): too few params -- expected 1, got 0`}, + /* 32 */ {`float(1,2,3)`, nil, `float(): too many params -- expected 1, got 3`}, /* 33 */ {`isBool(false)`, true, nil}, /* 34 */ {`fract(1:2)`, fract.NewFraction(1, 2), nil}, /* 35 */ {`fract(12,2)`, fract.NewFraction(6, 1), nil}, @@ -59,9 +59,9 @@ func TestFuncBase(t *testing.T) { /* 42 */ {`bool([])`, false, nil}, /* 43 */ {`bool({})`, false, nil}, /* 44 */ {`bool({1:"one"})`, true, nil}, - /* 45 */ {`dec(false)`, float64(0), nil}, - /* 46 */ {`dec(1:2)`, float64(0.5), nil}, - /* 47 */ {`dec([1])`, nil, `dec(): can't convert array to float`}, + /* 45 */ {`float(false)`, float64(0), nil}, + /* 46 */ {`float(1:2)`, float64(0.5), nil}, + /* 47 */ {`float([1])`, nil, `float(): can't convert array to float`}, /* 48 */ {`eval("a=3"); a`, int64(3), nil}, /* 49 */ {`int(5:2)`, int64(2), nil}, diff --git a/t_list_test.go b/t_list_test.go index 8a74e55..2a6d87c 100644 --- a/t_list_test.go +++ b/t_list_test.go @@ -38,10 +38,10 @@ func TestLinkedInsert(t *testing.T) { section := "LinkedList-Insert" inputs := []inputType{ /* 1 */ {`[<>] << 1`, list.NewLinkedListA(1), nil}, - /* 2 */ {`[<>] << [1,2]`, list.NewLinkedListA(array.NewArrayA(int64(1), int64(2))), nil}, + /* 2 */ {`[<>] << [1,2]`, list.NewLinkedListA(array.ArrayFromIntsA(1, 2)), nil}, /* 3 */ {`[<>] << [<1,2>]`, list.NewLinkedListA(list.NewLinkedListA(1, 2)), nil}, /* 4 */ {`1 >> [<>]`, list.NewLinkedListA(1), nil}, - /* 5 */ {`[1,2] >> [<>]`, list.NewLinkedListA(array.NewArrayA(int64(1), int64(2))), nil}, + /* 5 */ {`[1,2] >> [<>]`, list.NewLinkedListA(array.ArrayFromIntsA(1, 2)), nil}, /* 6 */ {`[<1,2>] >> [<>]`, list.NewLinkedListA(list.NewLinkedListA(1, 2)), nil}, /* 7 */ {`$([1,2]) >> [<>]`, list.NewLinkedListA(1, 2), nil}, /* 8 */ {`$([<1,2>]) >> [<>]`, list.NewLinkedListA(1, 2), nil}, diff --git a/t_operator_test.go b/t_operator_test.go index 45aaf9d..f56c804 100644 --- a/t_operator_test.go +++ b/t_operator_test.go @@ -42,6 +42,8 @@ func TestOperator(t *testing.T) { /* 26 */ {`a="1"; --a`, nil, `[1:9] prefix/postfix operator "--" does not support operand '1' [string]`}, /* 27 */ {`a="1"; a++`, nil, `[1:10] prefix/postfix operator "++" does not support operand '1' [string]`}, /* 28 */ {`a="1"; a--`, nil, `[1:10] prefix/postfix operator "--" does not support operand '1' [string]`}, + /* 29 */ {`2 << 3;`, int64(16), nil}, + /* 30 */ {`2 >> 3;`, int64(0), nil}, } // t.Setenv("EXPR_PATH", ".") diff --git a/types/array/array-type.go b/types/array/array-type.go index 198537b..d381198 100644 --- a/types/array/array-type.go +++ b/types/array/array-type.go @@ -51,6 +51,18 @@ func ArrayFromStrings(stringSlice []string) (aRef *ArrayType) { return } +func ArrayFromIntsA(ints ...int) (aRef *ArrayType) { + return ArrayFromInts(ints) +} + +func ArrayFromInts(intSlice []int) (aRef *ArrayType) { + aRef = MakeArray(len(intSlice), 0) + for i, s := range intSlice { + (*aRef)[i] = int64(s) + } + return +} + func (aRef *ArrayType) ToString(opt kern.FmtOpt) (s string) { indent := kern.GetFormatIndent(opt) flags := kern.GetFormatFlags(opt) diff --git a/types/list/linked-list.go b/types/list/linked-list.go index 6a93b38..de9d4ba 100644 --- a/types/list/linked-list.go +++ b/types/list/linked-list.go @@ -141,6 +141,19 @@ func (ls *LinkedList) Last() (data any, err error) { return } +// func (ls *LinkedList) Reverse() *ListNode { +// var prev *ListNode + +// for current := ls.first; current != nil; { +// succ := current.next +// prev = succ.next +// succ.next = current +// current = prev +// } +// ls.first, ls.last = ls.last, ls.first +// return ls.first +// } + func (ls *LinkedList) NodeAt(index64 int64) (node *ListNode, err error) { index := int(index64) if ls.count == 0 {