diff --git a/funcs_test.go b/funcs_test.go index eede78d..ac14581 100644 --- a/funcs_test.go +++ b/funcs_test.go @@ -6,17 +6,10 @@ package expr import ( "errors" - "strings" "testing" ) func TestFuncs(t *testing.T) { - type inputType struct { - source string - wantResult any - wantErr error - } - inputs := []inputType{ /* 1 */ {`isNil(nil)`, true, nil}, /* 2 */ {`v=nil; isNil(v)`, true, nil}, @@ -29,53 +22,24 @@ func TestFuncs(t *testing.T) { /* 9 */ {`int("1.5")`, nil, errors.New(`strconv.Atoi: parsing "1.5": invalid syntax`)}, /* 10 */ {`int("432", 4)`, nil, errors.New(`int() requires exactly one param`)}, /* 11 */ {`int(nil)`, nil, errors.New(`int() can't convert to int`)}, + /* 12 */ {`two=func(){2}; two()`, int64(2), nil}, + /* 13 */ {`double=func(x) {2*x}; (double(3))`, int64(6), nil}, + /* 14 */ {`double=func(x){2*x}; double(3)`, int64(6), nil}, + /* 15 */ {`double=func(x){2*x}; a=5; double(3+a) + 1`, int64(17), nil}, + /* 16 */ {`double=func(x){2*x}; a=5; two=func() {2}; (double(3+a) + 1) * two()`, int64(34), nil}, + /* 17 */ {`builtin "import"; import("./test-funcs.expr"); (double(3+a) + 1) * two()`, int64(34), nil}, + /* 18 */ {`builtin "import"; import("test-funcs.expr"); (double(3+a) + 1) * two()`, int64(34), nil}, + /* 19 */ {`@x="hello"; @x`, nil, errors.New(`[1:3] variable references are not allowed in top level expressions: "@x"`)}, + /* 20 */ {`f=func(){@x="hello"}; f(); x`, "hello", nil}, + /* 21 */ {`f=func(@y){@y=@y+1}; f(2); y`, int64(3), nil}, + /* 22 */ {`f=func(@y){g=func(){@x=5}; @y=@y+g()}; f(2); y+x`, nil, errors.New(`undefined variable or function "x"`)}, + /* 23 */ {`f=func(@y){g=func(){@x=5}; @z=g(); @y=@y+@z}; f(2); y+z`, int64(12), nil}, + /* 24 */ {`f=func(@y){g=func(){@x=5}; g(); @z=x; @y=@y+@z}; f(2); y+z`, int64(12), nil}, + /* 25 */ {`f=func(@y){g=func(){@x=5}; g(); @z=x; @x=@y+@z}; f(2); y+x`, int64(9), nil}, + /* 26 */ {`builtin "import"; importAll("./test-funcs.expr"); six()`, int64(6), nil}, + /* 27 */ {`builtin "import"; import("./sample-export-all.expr"); six()`, int64(6), nil}, } - succeeded := 0 - failed := 0 - - // inputs1 := []inputType{ - // /* 1 */ {`0?{}`, nil, nil}, - // } - - for i, input := range inputs { - var expr Expr - var gotResult any - var gotErr error - - ctx := NewSimpleFuncStore() - // ImportMathFuncs(ctx) - // ImportImportFunc(ctx) - // ImportOsFuncs(ctx) - parser := NewParser(ctx) - - logTest(t, i+1, "Funcs", input.source, input.wantResult, input.wantErr) - - r := strings.NewReader(input.source) - scanner := NewScanner(r, DefaultTranslations()) - - good := true - if expr, gotErr = parser.Parse(scanner); gotErr == nil { - gotResult, gotErr = expr.Eval(ctx) - } - - if gotResult != input.wantResult { - t.Errorf("%d: %q -> result = %v [%T], want %v [%T]", i+1, input.source, gotResult, gotResult, input.wantResult, input.wantResult) - good = false - } - - if gotErr != input.wantErr { - if input.wantErr == nil || gotErr == nil || (gotErr.Error() != input.wantErr.Error()) { - t.Errorf("%d: %q -> err = <%v>, want <%v>", i+1, input.source, gotErr, input.wantErr) - good = false - } - } - - if good { - succeeded++ - } else { - failed++ - } - } - t.Logf("test count: %d, succeeded count: %d, failed count: %d", len(inputs), succeeded, failed) + // parserTest(t, "Func", inputs[25:26]) + parserTest(t, "Func", inputs) } diff --git a/iterator_test.go b/iterator_test.go new file mode 100644 index 0000000..7a5c880 --- /dev/null +++ b/iterator_test.go @@ -0,0 +1,26 @@ +// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). +// All rights reserved. + +// iterator_test.go +package expr + +import "testing" + +func TestIteratorParser(t *testing.T) { + inputs := []inputType{ + /* 1 */ {`include "iterator.expr"; it=$(ds,3); ()it`, int64(0), nil}, + /* 2 */ {`include "iterator.expr"; it=$(ds,3); it++; it++`, int64(1), nil}, + /* 3 */ {`include "iterator.expr"; it=$(ds,3); it++; it++; #it`, int64(2), nil}, + /* 4 */ {`include "iterator.expr"; it=$(ds,3); it++; it++; it.reset; ()it`, int64(0), nil}, + /* 5 */ {`builtin "math.arith"; include "iterator.expr"; it=$(ds,3); add(it)`, int64(6), nil}, + /* 6 */ {`builtin "math.arith"; include "iterator.expr"; it=$(ds,3); mul(it)`, int64(0), nil}, + /* 7 */ {`builtin "math.arith"; include "file-reader.expr"; it=$(ds,"int.list"); mul(it)`, int64(12000), nil}, + /* 8 */ {`include "file-reader.expr"; it=$(ds,"int.list"); it++; it.index`, int64(0), nil}, + /* 10 */ {`include "file-reader.expr"; it=$(ds,"int.list"); it.clean`, true, nil}, + /* 11 */ {`it=$(1,2,3); it++`, int64(1), nil}, + } + // inputs1 := []inputType{ + // /* 1 */ {`0?{}`, nil, nil}, + // } + parserTest(t, "Iterator", inputs) +} diff --git a/list_test.go b/list_test.go new file mode 100644 index 0000000..935956a --- /dev/null +++ b/list_test.go @@ -0,0 +1,112 @@ +// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). +// All rights reserved. + +// list_test.go +package expr + +import ( + "errors" + "strings" + "testing" +) + +func TestListParser(t *testing.T) { + section := "List" + + type inputType struct { + source string + wantResult any + wantErr error + } + + inputs := []inputType{ + /* 1 */ {`[]`, []any{}, nil}, + /* 2 */ {`[1,2,3]`, []any{int64(1), int64(2), int64(3)}, nil}, + /* 3 */ {`[1,2,"hello"]`, []any{int64(1), int64(2), "hello"}, nil}, + /* 4 */ {`[1+2, not true, "hello"]`, []any{int64(3), false, "hello"}, nil}, + /* 5 */ {`[1,2]+[3]`, []any{int64(1), int64(2), int64(3)}, nil}, + /* 6 */ {`[1,4,3,2]-[3]`, []any{int64(1), int64(4), int64(2)}, nil}, + /* 7 */ {`add([1,4,3,2])`, int64(10), nil}, + /* 8 */ {`add([1,[2,2],3,2])`, int64(10), nil}, + /* 9 */ {`mul([1,4,3.0,2])`, float64(24.0), nil}, + /* 10 */ {`add([1,"hello"])`, nil, errors.New(`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`, []any{int64(1), int64(2), int64(3), int64(4)}, nil}, + /* 13 */ {`2-1 >> [2,3]`, []any{int64(1), int64(2), int64(3)}, nil}, + + // /* 8 */ {`[int(x)|x=csv("test.csv",1,all(),1)]`, []any{int64(10), int64(40), int64(20)}, nil}, + // /* 9 */ {`sum(@[int(x)|x=csv("test.csv",1,all(),1)])`, []any{int64(10), int64(40), int64(20)}, nil}, + } + + succeeded := 0 + failed := 0 + + // inputs1 := []inputType{ + // /* 7 */ {`add([1,4,3,2])`, int64(10), nil}, + // } + + for i, input := range inputs { + var expr *ast + var gotResult any + var gotErr error + + ctx := NewSimpleFuncStore() + ctx.SetVar("var1", int64(123)) + ctx.SetVar("var2", "abc") + ImportMathFuncs(ctx) + parser := NewParser(ctx) + + logTest(t, i+1, "List", input.source, input.wantResult, input.wantErr) + + r := strings.NewReader(input.source) + scanner := NewScanner(r, DefaultTranslations()) + + good := true + if expr, gotErr = parser.Parse(scanner); gotErr == nil { + gotResult, gotErr = expr.Eval(ctx) + } + + if (gotResult == nil && input.wantResult != nil) || (gotResult != nil && input.wantResult == nil) { + t.Errorf("%d: %q -> result = %v [%T], want %v [%T]", i+1, input.source, gotResult, gotResult, input.wantResult, input.wantResult) + good = false + } + + if gotList, okGot := gotResult.([]any); okGot { + if wantList, okWant := input.wantResult.([]any); okWant { + if (gotList == nil && wantList != nil) || (gotList != nil && wantList == nil) { + t.Errorf("%d: %q -> result = %v [%T], want %v [%T]", i+1, input.source, gotResult, gotResult, input.wantResult, input.wantResult) + good = false + } else { + equal := len(gotList) == len(wantList) + if equal { + for i, gotItem := range gotList { + wantItem := wantList[i] + equal = gotItem == wantItem + if !equal { + break + } + } + } + if !equal { + t.Errorf("%d: %q -> result = %v [%T], want %v [%T]", i+1, input.source, gotResult, gotResult, input.wantResult, input.wantResult) + good = false + } + } + } + } + + if gotErr != input.wantErr { + if input.wantErr == nil || gotErr == nil || (gotErr.Error() != input.wantErr.Error()) { + t.Errorf("%d: %q -> err = <%v>, want <%v>", i+1, input.source, gotErr, input.wantErr) + good = false + } + } + + if good { + succeeded++ + } else { + failed++ + } + } + t.Logf("%s -- test count: %d, succeeded: %d, failed: %d", section, len(inputs), succeeded, failed) +} diff --git a/parser_test.go b/parser_test.go index e587338..2b8b396 100644 --- a/parser_test.go +++ b/parser_test.go @@ -11,13 +11,7 @@ import ( "testing" ) -func TestParser(t *testing.T) { - type inputType struct { - source string - wantResult any - wantErr error - } - +func TestGeneralParser(t *testing.T) { inputs := []inputType{ /* 1 */ {`1+/*5*/2`, int64(3), nil}, /* 2 */ {`3 == 4`, false, nil}, @@ -51,137 +45,111 @@ func TestParser(t *testing.T) { /* 30 */ {"-(-2+1)", int64(1), nil}, /* 31 */ {"(1+1)*5", int64(10), nil}, /* 32 */ {"200 / (1+1) - 1", int64(99), nil}, - /* 33 */ {`add(1,2,3)`, int64(6), nil}, - /* 34 */ {`mulX(1,2,3)`, nil, errors.New(`unknown function mulX()`)}, - /* 35 */ {`add(1+4,3+2,5*(3-2))`, int64(15), nil}, - /* 36 */ {`add(add(1+4),3+2,5*(3-2))`, int64(15), nil}, - /* 37 */ {`add(add(1,4),/*3+2,*/5*(3-2))`, int64(10), nil}, - /* 38 */ {`1+(1+(1+(1)+1)+1)+1`, int64(7), nil}, - /* 39 */ {`(((1)))`, int64(1), nil}, - /* 40 */ {`"uno_" + var2`, `uno_abc`, nil}, - /* 41 */ {`0 || 0.0 && "hello"`, false, nil}, - /* 42 */ {`"s" + true`, nil, errors.New(`[1:6] left operand 's' [string] and right operand 'true' [bool] are not compatible with operator "+"`)}, - /* 43 */ {`+false`, nil, errors.New(`[1:2] prefix/postfix operator "+" do not support operand 'false' [bool]`)}, - /* 44 */ {`false // very simple expression`, false, nil}, - /* 45 */ {`1 + // Missing right operator`, nil, errors.New(`[1:4] infix operator "+" requires two non-nil operands, got 1`)}, - /* 46 */ {"", nil, nil}, - /* 47 */ {"4!", int64(24), nil}, - /* 48 */ {"(-4)!", nil, errors.New(`factorial of a negative integer (-4) is not allowed`)}, - /* 49 */ {"-4!", int64(-24), nil}, - /* 50 */ {"1.5 < 7", true, nil}, - /* 51 */ {"1.5 > 7", false, nil}, - /* 52 */ {"1.5 <= 7", true, nil}, - /* 53 */ {"1.5 >= 7", false, nil}, - /* 54 */ {"1.5 != 7", true, nil}, - /* 55 */ {"1.5 == 7", false, nil}, - /* 56 */ {`"1.5" < "7"`, true, nil}, - /* 57 */ {`"1.5" > "7"`, false, nil}, - /* 58 */ {`"1.5" == "7"`, false, nil}, - /* 59 */ {`"1.5" != "7"`, true, nil}, - /* 60 */ {"1.5 < ", nil, errors.New(`[1:6] infix operator "<" requires two non-nil operands, got 1`)}, - /* 61 */ {"1.5 > ", nil, errors.New(`[1:6] infix operator ">" requires two non-nil operands, got 1`)}, - /* 62 */ {"1.5 <= ", nil, errors.New(`[1:6] infix operator "<=" requires two non-nil operands, got 1`)}, - /* 63 */ {"1.5 >= ", nil, errors.New(`[1:6] infix operator ">=" requires two non-nil operands, got 1`)}, - /* 64 */ {"1.5 != ", nil, errors.New(`[1:6] infix operator "!=" requires two non-nil operands, got 1`)}, - /* 65 */ {"1.5 == ", nil, errors.New(`[1:6] infix operator "==" requires two non-nil operands, got 1`)}, - /* 66 */ {`"1.5" < `, nil, errors.New(`[1:8] infix operator "<" requires two non-nil operands, got 1`)}, - /* 67 */ {`"1.5" > `, nil, errors.New(`[1:8] infix operator ">" requires two non-nil operands, got 1`)}, - /* 68 */ {`"1.5" == `, nil, errors.New(`[1:8] infix operator "==" requires two non-nil operands, got 1`)}, - /* 69 */ {`"1.5" != `, nil, errors.New(`[1:8] infix operator "!=" requires two non-nil operands, got 1`)}, - /* 70 */ {"+1.5", float64(1.5), nil}, - /* 71 */ {"+", nil, errors.New(`[1:2] prefix operator "+" requires one not nil operand`)}, - /* 72 */ {"4 / 0", nil, errors.New(`division by zero`)}, - /* 73 */ {"4.0 / 0", nil, errors.New(`division by zero`)}, - /* 74 */ {"4.0 / \n2", float64(2.0), nil}, - /* 75 */ {`123`, int64(123), nil}, - /* 76 */ {`1.`, float64(1.0), nil}, - /* 77 */ {`1.E-2`, float64(0.01), nil}, - /* 78 */ {`1E2`, float64(100), nil}, - /* 79 */ {`1 / 2`, int64(0), nil}, - /* 80 */ {`1.0 / 2`, float64(0.5), nil}, - /* 81 */ {`1 ./ 2`, float64(0.5), nil}, - /* 82 */ {`5 % 2`, int64(1), nil}, - /* 83 */ {`5 % (-2)`, int64(1), nil}, - /* 84 */ {`-5 % 2`, int64(-1), nil}, - /* 85 */ {`5 % 2.0`, nil, errors.New(`[1:4] left operand '5' [int64] and right operand '2' [float64] are not compatible with operator "%"`)}, - /* 86 */ {`"a" < "b" AND NOT (2 < 1)`, true, nil}, - /* 87 */ {`"a" < "b" AND NOT (2 == 1)`, true, nil}, - /* 88 */ {`"a" < "b" AND ~ 2 == 1`, true, nil}, - /* 89 */ {`~ 2 > 1`, false, nil}, - /* 90 */ {`~ true && true`, false, nil}, - /* 91 */ {`~ false || true`, true, nil}, - /* 92 */ {`false but true`, true, nil}, - /* 93 */ {`2+3 but 5*2`, int64(10), nil}, - /* 94 */ {`add(1,2) but var2`, "abc", nil}, - /* 95 */ {`x=2`, int64(2), nil}, - /* 96 */ {`x=2 but x*10`, int64(20), nil}, - /* 97 */ {`false and true`, false, nil}, - /* 98 */ {`false and (x==2)`, false, nil}, - /* 99 */ {`false and (x=2 but x==2) or x==2`, nil, errors.New(`undefined variable or function "x"`)}, - /* 100 */ {`false or true`, true, nil}, - /* 101 */ {`false or (x==2)`, nil, errors.New(`undefined variable or function "x"`)}, - /* 102 */ {`a=5; a`, int64(5), nil}, - /* 103 */ {`a=5; b=2; add(a, b*3)`, int64(11), nil}, - /* 104 */ {`2=5`, nil, errors.New(`assign operator ("=") must be preceded by a variable`)}, - /* 105 */ {`2+a=5`, nil, errors.New(`[1:3] left operand of "=" must be a variable`)}, - /* 106 */ {`2+(a=5)`, int64(7), nil}, - /* 107 */ {`two=func(){2}; two()`, int64(2), nil}, - /* 108 */ {`double=func(x) {2*x}; (double(3))`, int64(6), nil}, - /* 109 */ {`double=func(x){2*x}; double(3)`, int64(6), nil}, - /* 110 */ {`double=func(x){2*x}; a=5; double(3+a) + 1`, int64(17), nil}, - /* 111 */ {`double=func(x){2*x}; a=5; two=func() {2}; (double(3+a) + 1) * two()`, int64(34), nil}, - /* 112 */ {`import("./test-funcs.expr"); (double(3+a) + 1) * two()`, int64(34), nil}, - // /* 113 */ {`import("test-funcs.expr"); (double(3+a) + 1) * two()`, int64(34), nil}, - /* 114 */ {`x ?? "default"`, "default", nil}, - /* 115 */ {`x="hello"; x ?? "default"`, "hello", nil}, - /* 116 */ {`y=x ?? func(){4}; y()`, int64(4), nil}, - /* 117 */ {`x ?= "default"; x`, "default", nil}, - /* 118 */ {`x="hello"; x ?= "default"; x`, "hello", nil}, - /* 119 */ {`@x="hello"; @x`, nil, errors.New(`[1:3] variable references are not allowed in top level expressions: "@x"`)}, - /* 120 */ {`f=func(){@x="hello"}; f(); x`, "hello", nil}, - /* 121 */ {`f=func(@y){@y=@y+1}; f(2); y`, int64(3), nil}, - /* 122 */ {`f=func(@y){g=func(){@x=5}; @y=@y+g()}; f(2); y+x`, nil, errors.New(`undefined variable or function "x"`)}, - /* 123 */ {`f=func(@y){g=func(){@x=5}; @z=g(); @y=@y+@z}; f(2); y+z`, int64(12), nil}, - /* 124 */ {`f=func(@y){g=func(){@x=5}; g(); @z=x; @y=@y+@z}; f(2); y+z`, int64(12), nil}, - /* 125 */ {`f=func(@y){g=func(){@x=5}; g(); @z=x; @x=@y+@z}; f(2); y+x`, int64(9), nil}, - // /* 126 */ {`include("./test-funcs.expr"); six()`, int64(6), nil}, - /* 127 */ {`import("./sample-export-all.expr"); six()`, int64(6), nil}, - /* 128 */ {`1 ? {"a"} : {"b"}`, "b", nil}, - /* 129 */ {`10 ? {"a"} : {"b"} :: {"c"}`, "c", nil}, - /* 130 */ {`10 ? {"a"} :[true, 2+8] {"b"} :: {"c"}`, "b", nil}, - /* 131 */ {`10 ? {"a"} :[true, 2+8] {"b"} ::[10] {"c"}`, nil, errors.New(`[1:34] case list in default clause`)}, - /* 132 */ {`10 ? {"a"} :[10] {x="b" but x} :: {"c"}`, "b", nil}, - /* 133 */ {`10 ? {"a"} :[10] {x="b"; x} :: {"c"}`, "b", nil}, - /* 134 */ {`10 ? {"a"} : {"b"}`, nil, errors.New(`[1:3] no case catches the value (10) of the selection expression`)}, - /* 135 */ {`10 ? {"a"} :: {"b"} : {"c"}`, nil, errors.New(`[1:22] selector-case outside of a selector context`)}, - /* 136 */ {`1 ? {"a"} : {"b"} ? ["a"] {"A"} :["b"] {"B"}`, "B", nil}, - /* 137 */ {`2 + 1 ? {"a"} : {"b"} * 3`, "2bbb", nil}, - /* 138 */ {`nil`, nil, nil}, - /* 139 */ {`null`, nil, errors.New(`undefined variable or function "null"`)}, - /* 140 */ {`{"key"}`, nil, errors.New(`[1:8] expected ":", got "}"`)}, - /* 141 */ {`{"key":}`, nil, errors.New(`[1:9] expected "dictionary-value", got "}"`)}, - /* 142 */ {`{}`, map[any]any{}, nil}, - /* 143 */ {`1|2`, newFraction(1, 2), nil}, - /* 144 */ {`1|2 + 1`, newFraction(3, 2), nil}, - /* 145 */ {`1|2 - 1`, newFraction(-1, 2), nil}, - /* 146 */ {`1|2 * 1`, newFraction(1, 2), nil}, - /* 147 */ {`1|2 * 2|3`, newFraction(2, 6), nil}, - /* 148 */ {`1|2 / 2|3`, newFraction(3, 4), nil}, - /* 149 */ {`builtin "math.arith"; add(1|2, 2|3)`, newFraction(7, 6), nil}, - /* 150 */ {`builtin "math.arith"; add(1|2, 1.0, 2)`, float64(3.5), nil}, - /* 151 */ {`builtin "math.arith"; mul(1|2, 2|3)`, newFraction(2, 6), nil}, - /* 152 */ {`builtin "math.arith"; mul(1|2, 1.0, 2)`, float64(1.0), nil}, - /* 153 */ {`include "iterator.expr"; it=$(ds,3); ()it`, int64(0), nil}, - /* 154 */ {`include "iterator.expr"; it=$(ds,3); it++; it++`, int64(1), nil}, - /* 155 */ {`include "iterator.expr"; it=$(ds,3); it++; it++; #it`, int64(2), nil}, - /* 156 */ {`include "iterator.expr"; it=$(ds,3); it++; it++; it.reset; ()it`, int64(0), nil}, - /* 157 */ {`builtin "math.arith"; include "iterator.expr"; it=$(ds,3); add(it)`, int64(6), nil}, - /* 158 */ {`builtin "math.arith"; include "iterator.expr"; it=$(ds,3); mul(it)`, int64(0), nil}, - /* 159 */ {`include "file-reader.expr"; it=$(ds,"int.list"); mul(it)`, int64(12000), nil}, - /* 160 */ {`include "file-reader.expr"; it=$(ds,"int.list"); it++; it.index`, int64(0), nil}, - /* 161 */ {`include "file-reader.expr"; it=$(ds,"int.list"); it.clean`, true, nil}, - /* 162 */ {`builtin "os.file"`, int64(1), nil}, - /* 163 */ {`it=$(1,2,3); it++`, int64(1), nil}, + /* 33 */ {`1+(1+(1+(1)+1)+1)+1`, int64(7), nil}, + /* 34 */ {`(((1)))`, int64(1), nil}, + /* 35 */ {`var2="abc"; "uno_" + var2`, `uno_abc`, nil}, + /* 36 */ {`0 || 0.0 && "hello"`, false, nil}, + /* 37 */ {`"s" + true`, nil, errors.New(`[1:6] left operand 's' [string] and right operand 'true' [bool] are not compatible with operator "+"`)}, + /* 38 */ {`+false`, nil, errors.New(`[1:2] prefix/postfix operator "+" do not support operand 'false' [bool]`)}, + /* 39 */ {`false // very simple expression`, false, nil}, + /* 40 */ {`1 + // Missing right operator`, nil, errors.New(`[1:4] infix operator "+" requires two non-nil operands, got 1`)}, + /* 41 */ {"", nil, nil}, + /* 42 */ {"4!", int64(24), nil}, + /* 43 */ {"(-4)!", nil, errors.New(`factorial of a negative integer (-4) is not allowed`)}, + /* 44 */ {"-4!", int64(-24), nil}, + /* 45 */ {"1.5 < 7", true, nil}, + /* 46 */ {"1.5 > 7", false, nil}, + /* 47 */ {"1.5 <= 7", true, nil}, + /* 48 */ {"1.5 >= 7", false, nil}, + /* 49 */ {"1.5 != 7", true, nil}, + /* 50 */ {"1.5 == 7", false, nil}, + /* 51 */ {`"1.5" < "7"`, true, nil}, + /* 52 */ {`"1.5" > "7"`, false, nil}, + /* 53 */ {`"1.5" == "7"`, false, nil}, + /* 54 */ {`"1.5" != "7"`, true, nil}, + /* 55 */ {"1.5 < ", nil, errors.New(`[1:6] infix operator "<" requires two non-nil operands, got 1`)}, + /* 56 */ {"1.5 > ", nil, errors.New(`[1:6] infix operator ">" requires two non-nil operands, got 1`)}, + /* 57 */ {"1.5 <= ", nil, errors.New(`[1:6] infix operator "<=" requires two non-nil operands, got 1`)}, + /* 58 */ {"1.5 >= ", nil, errors.New(`[1:6] infix operator ">=" requires two non-nil operands, got 1`)}, + /* 59 */ {"1.5 != ", nil, errors.New(`[1:6] infix operator "!=" requires two non-nil operands, got 1`)}, + /* 60 */ {"1.5 == ", nil, errors.New(`[1:6] infix operator "==" requires two non-nil operands, got 1`)}, + /* 61 */ {`"1.5" < `, nil, errors.New(`[1:8] infix operator "<" requires two non-nil operands, got 1`)}, + /* 62 */ {`"1.5" > `, nil, errors.New(`[1:8] infix operator ">" requires two non-nil operands, got 1`)}, + /* 63 */ {`"1.5" == `, nil, errors.New(`[1:8] infix operator "==" requires two non-nil operands, got 1`)}, + /* 64 */ {`"1.5" != `, nil, errors.New(`[1:8] infix operator "!=" requires two non-nil operands, got 1`)}, + /* 65 */ {"+1.5", float64(1.5), nil}, + /* 66 */ {"+", nil, errors.New(`[1:2] prefix operator "+" requires one not nil operand`)}, + /* 67 */ {"4 / 0", nil, errors.New(`division by zero`)}, + /* 68 */ {"4.0 / 0", nil, errors.New(`division by zero`)}, + /* 69 */ {"4.0 / \n2", float64(2.0), nil}, + /* 70 */ {`123`, int64(123), nil}, + /* 71 */ {`1.`, float64(1.0), nil}, + /* 72 */ {`1.E-2`, float64(0.01), nil}, + /* 73 */ {`1E2`, float64(100), nil}, + /* 74 */ {`1 / 2`, int64(0), nil}, + /* 75 */ {`1.0 / 2`, float64(0.5), nil}, + /* 76 */ {`1 ./ 2`, float64(0.5), nil}, + /* 77 */ {`5 % 2`, int64(1), nil}, + /* 78 */ {`5 % (-2)`, int64(1), nil}, + /* 79 */ {`-5 % 2`, int64(-1), nil}, + /* 80 */ {`5 % 2.0`, nil, errors.New(`[1:4] left operand '5' [int64] and right operand '2' [float64] are not compatible with operator "%"`)}, + /* 81 */ {`"a" < "b" AND NOT (2 < 1)`, true, nil}, + /* 82 */ {`"a" < "b" AND NOT (2 == 1)`, true, nil}, + /* 83 */ {`"a" < "b" AND ~ 2 == 1`, true, nil}, + /* 84 */ {`~ 2 > 1`, false, nil}, + /* 85 */ {`~ true && true`, false, nil}, + /* 86 */ {`~ false || true`, true, nil}, + /* 87 */ {`false but true`, true, nil}, + /* 88 */ {`2+3 but 5*2`, int64(10), nil}, + /* 89 */ {`x=2`, int64(2), nil}, + /* 90 */ {`x=2 but x*10`, int64(20), nil}, + /* 91 */ {`false and true`, false, nil}, + /* 92 */ {`false and (x==2)`, false, nil}, + /* 93 */ {`false and (x=2 but x==2) or x==2`, nil, errors.New(`undefined variable or function "x"`)}, + /* 94 */ {`false or true`, true, nil}, + /* 95 */ {`false or (x==2)`, nil, errors.New(`undefined variable or function "x"`)}, + /* 96 */ {`a=5; a`, int64(5), nil}, + /* 97 */ {`2=5`, nil, errors.New(`assign operator ("=") must be preceded by a variable`)}, + /* 98 */ {`2+a=5`, nil, errors.New(`[1:3] left operand of "=" must be a variable`)}, + /* 99 */ {`2+(a=5)`, int64(7), nil}, + /* 100 */ {`x ?? "default"`, "default", nil}, + /* 101 */ {`x="hello"; x ?? "default"`, "hello", nil}, + /* 102 */ {`y=x ?? func(){4}; y()`, int64(4), nil}, + /* 103 */ {`x ?= "default"; x`, "default", nil}, + /* 104 */ {`x="hello"; x ?= "default"; x`, "hello", nil}, + /* 105 */ {`1 ? {"a"} : {"b"}`, "b", nil}, + /* 106 */ {`10 ? {"a"} : {"b"} :: {"c"}`, "c", nil}, + /* 107 */ {`10 ? {"a"} :[true, 2+8] {"b"} :: {"c"}`, "b", nil}, + /* 108 */ {`10 ? {"a"} :[true, 2+8] {"b"} ::[10] {"c"}`, nil, errors.New(`[1:34] case list in default clause`)}, + /* 109 */ {`10 ? {"a"} :[10] {x="b" but x} :: {"c"}`, "b", nil}, + /* 110 */ {`10 ? {"a"} :[10] {x="b"; x} :: {"c"}`, "b", nil}, + /* 111 */ {`10 ? {"a"} : {"b"}`, nil, errors.New(`[1:3] no case catches the value (10) of the selection expression`)}, + /* 112 */ {`10 ? {"a"} :: {"b"} : {"c"}`, nil, errors.New(`[1:22] selector-case outside of a selector context`)}, + /* 113 */ {`1 ? {"a"} : {"b"} ? ["a"] {"A"} :["b"] {"B"}`, "B", nil}, + /* 114 */ {`2 + 1 ? {"a"} : {"b"} * 3`, "2bbb", nil}, + /* 115 */ {`nil`, nil, nil}, + /* 116 */ {`null`, nil, errors.New(`undefined variable or function "null"`)}, + /* 117 */ {`{"key"}`, nil, errors.New(`[1:8] expected ":", got "}"`)}, + /* 118 */ {`{"key":}`, nil, errors.New(`[1:9] expected "dictionary-value", got "}"`)}, + /* 119 */ {`{}`, map[any]any{}, nil}, + /* 120 */ {`1|2`, newFraction(1, 2), nil}, + /* 121 */ {`1|2 + 1`, newFraction(3, 2), nil}, + /* 122 */ {`1|2 - 1`, newFraction(-1, 2), nil}, + /* 123 */ {`1|2 * 1`, newFraction(1, 2), nil}, + /* 124 */ {`1|2 * 2|3`, newFraction(2, 6), nil}, + /* 125 */ {`1|2 / 2|3`, newFraction(3, 4), nil}, + /* 126 */ {`builtin "math.arith"; add(1,2,3)`, int64(6), nil}, + /* 127 */ {`builtin "math.arith"; mulX(1,2,3)`, nil, errors.New(`unknown function mulX()`)}, + /* 128 */ {`builtin "math.arith"; add(1+4,3+2,5*(3-2))`, int64(15), nil}, + /* 129 */ {`builtin "math.arith"; add(add(1+4),3+2,5*(3-2))`, int64(15), nil}, + /* 130 */ {`builtin "math.arith"; add(add(1,4),/*3+2,*/5*(3-2))`, int64(10), nil}, + /* 131 */ {`builtin "math.arith"; a=5; b=2; add(a, b*3)`, int64(11), nil}, + /* 132 */ {`builtin "math.arith"; var2="abc"; add(1,2) but var2`, "abc", nil}, + /* 133 */ {`builtin "math.arith"; add(1|2, 2|3)`, newFraction(7, 6), nil}, + /* 134 */ {`builtin "math.arith"; add(1|2, 1.0, 2)`, float64(3.5), nil}, + /* 135 */ {`builtin "math.arith"; mul(1|2, 2|3)`, newFraction(2, 6), nil}, + /* 136 */ {`builtin "math.arith"; mul(1|2, 1.0, 2)`, float64(1.0), nil}, + /* 137 */ {`builtin "os.file"`, int64(1), nil}, } check_env_expr_path := 113 @@ -198,10 +166,10 @@ func TestParser(t *testing.T) { var gotErr error ctx := NewSimpleFuncStore() - ctx.SetVar("var1", int64(123)) - ctx.SetVar("var2", "abc") - ImportMathFuncs(ctx) - ImportImportFuncs(ctx) + // ctx.SetVar("var1", int64(123)) + // ctx.SetVar("var2", "abc") + // ImportMathFuncs(ctx) + // ImportImportFuncs(ctx) parser := NewParser(ctx) logTest(t, i+1, "General", input.source, input.wantResult, input.wantErr) @@ -240,49 +208,34 @@ func TestParser(t *testing.T) { t.Logf("test count: %d, succeeded count: %d, failed count: %d", len(inputs), succeeded, failed) } -func TestListParser(t *testing.T) { - type inputType struct { - source string - wantResult any - wantErr error - } +type inputType struct { + source string + wantResult any + wantErr error +} - inputs := []inputType{ - /* 1 */ {`[]`, []any{}, nil}, - /* 2 */ {`[1,2,3]`, []any{int64(1), int64(2), int64(3)}, nil}, - /* 3 */ {`[1,2,"hello"]`, []any{int64(1), int64(2), "hello"}, nil}, - /* 4 */ {`[1+2, not true, "hello"]`, []any{int64(3), false, "hello"}, nil}, - /* 5 */ {`[1,2]+[3]`, []any{int64(1), int64(2), int64(3)}, nil}, - /* 6 */ {`[1,4,3,2]-[3]`, []any{int64(1), int64(4), int64(2)}, nil}, - /* 7 */ {`add([1,4,3,2])`, int64(10), nil}, - /* 8 */ {`add([1,[2,2],3,2])`, int64(10), nil}, - /* 9 */ {`mul([1,4,3.0,2])`, float64(24.0), nil}, - /* 10 */ {`add([1,"hello"])`, nil, errors.New(`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}, - - // /* 8 */ {`[int(x)|x=csv("test.csv",1,all(),1)]`, []any{int64(10), int64(40), int64(20)}, nil}, - // /* 9 */ {`sum(@[int(x)|x=csv("test.csv",1,all(),1)])`, []any{int64(10), int64(40), int64(20)}, nil}, - } +func parserTest(t *testing.T, section string, inputs []inputType) { succeeded := 0 failed := 0 // inputs1 := []inputType{ - // /* 7 */ {`add([1,4,3,2])`, int64(10), nil}, + // /* 159 */ {`include "file-reader.expr"; it=$(ds,"int.list"); mul(it)`, int64(12000), nil}, // } for i, input := range inputs { - var expr *ast + var expr Expr var gotResult any var gotErr error ctx := NewSimpleFuncStore() - ctx.SetVar("var1", int64(123)) - ctx.SetVar("var2", "abc") - ImportMathFuncs(ctx) + // ctx.SetVar("var1", int64(123)) + // ctx.SetVar("var2", "abc") + // ImportMathFuncs(ctx) + // ImportImportFuncs(ctx) parser := NewParser(ctx) - logTest(t, i+1, "List", input.source, input.wantResult, input.wantErr) + logTest(t, i+1, "Iterator", input.source, input.wantResult, input.wantErr) r := strings.NewReader(input.source) scanner := NewScanner(r, DefaultTranslations()) @@ -292,38 +245,16 @@ func TestListParser(t *testing.T) { gotResult, gotErr = expr.Eval(ctx) } - if (gotResult == nil && input.wantResult != nil) || (gotResult != nil && input.wantResult == nil) { + eq := reflect.DeepEqual(gotResult, input.wantResult) + + if !eq /*gotResult != input.wantResult*/ { t.Errorf("%d: %q -> result = %v [%T], want %v [%T]", i+1, input.source, gotResult, gotResult, input.wantResult, input.wantResult) good = false } - if gotList, okGot := gotResult.([]any); okGot { - if wantList, okWant := input.wantResult.([]any); okWant { - if (gotList == nil && wantList != nil) || (gotList != nil && wantList == nil) { - t.Errorf("%d: %q -> result = %v [%T], want %v [%T]", i+1, input.source, gotResult, gotResult, input.wantResult, input.wantResult) - good = false - } else { - equal := len(gotList) == len(wantList) - if equal { - for i, gotItem := range gotList { - wantItem := wantList[i] - equal = gotItem == wantItem - if !equal { - break - } - } - } - if !equal { - t.Errorf("%d: %q -> result = %v [%T], want %v [%T]", i+1, input.source, gotResult, gotResult, input.wantResult, input.wantResult) - good = false - } - } - } - } - if gotErr != input.wantErr { if input.wantErr == nil || gotErr == nil || (gotErr.Error() != input.wantErr.Error()) { - t.Errorf("%d: %q -> err = <%v>, want <%v>", i+1, input.source, gotErr, input.wantErr) + t.Errorf("%d: %q -> err = <%v>, want <%v>", i+1, input.source, gotErr, input.wantErr) good = false } } @@ -334,7 +265,7 @@ func TestListParser(t *testing.T) { failed++ } } - t.Logf("test count: %d, succeeded count: %d, failed count: %d", len(inputs), succeeded, failed) + t.Logf("%s -- test count: %d, succeeded: %d, failed: %d", section, len(inputs), succeeded, failed) } func logTest(t *testing.T, n int, section, source string, wantResult any, wantErr error) {