package ucl_test import ( "bytes" "context" "github.com/lmika/ucl/ucl" "github.com/stretchr/testify/assert" "testing" ) func TestInst_Eval(t *testing.T) { tests := []struct { desc string expr string want any wantErr error }{ {desc: "simple string", expr: `firstarg "hello"`, want: "hello"}, {desc: "simple int 1", expr: `firstarg 123`, want: 123}, {desc: "simple int 2", expr: `firstarg -234`, want: -234}, {desc: "simple ident", expr: `firstarg a-test`, want: "a-test"}, // String interpolation {desc: "interpolate string 1", expr: `set what "world" ; firstarg "hello $what"`, want: "hello world"}, {desc: "interpolate string 2", expr: `set what "world" ; set when "now" ; firstarg "$when, hello $what"`, want: "now, hello world"}, {desc: "interpolate string 3", expr: `set what "world" ; set when "now" ; firstarg "${when}, hello ${what}"`, want: "now, hello world"}, {desc: "interpolate string 4", expr: `set "crazy var" "unknown" ; firstarg "hello ${crazy var}"`, want: "hello unknown"}, {desc: "interpolate string 5", expr: `set what "world" ; firstarg "hello $($what)"`, want: "hello world"}, {desc: "interpolate string 6", expr: `firstarg "hello $([1 2 3] | len)"`, want: "hello 3"}, {desc: "interpolate string 7", expr: `firstarg "hello $(add (add 1 2) 3)"`, want: "hello 6"}, {desc: "interpolate string 8", expr: `firstarg ("$(add 2 (add 1 1)) + $([1 2 3].(1) | cat ("$("")")) = $(("$(add 2 (4))"))")`, want: "4 + 2 = 6"}, // Sub-expressions {desc: "sub expression 1", expr: `firstarg (sjoin "hello")`, want: "hello"}, {desc: "sub expression 2", expr: `firstarg (sjoin "hello " "world")`, want: "hello world"}, {desc: "sub expression 3", expr: `firstarg (sjoin "hello" (sjoin " ") (sjoin "world"))`, want: "hello world"}, // Variables {desc: "var 1", expr: `firstarg $a`, want: "alpha"}, {desc: "var 2", expr: `firstarg $bee`, want: "buzz"}, {desc: "var 3", expr: `firstarg (sjoin $bee " " $bee " " $bee)`, want: "buzz buzz buzz"}, // Pipeline {desc: "pipe 1", expr: `list "aye" "bee" "see" | joinpipe`, want: "aye,bee,see"}, {desc: "pipe 2", expr: `list "aye" "bee" "see" | map { |x| toUpper $x } | joinpipe`, want: "AYE,BEE,SEE"}, {desc: "pipe 3", expr: `firstarg ["normal"] | map { |x| toUpper $x } | joinpipe`, want: "NORMAL"}, {desc: "pipe literal 1", expr: `"hello" | firstarg`, want: "hello"}, {desc: "pipe literal 2", expr: `["hello" "world"] | joinpipe`, want: "hello,world"}, {desc: "ignored pipe", expr: `(list "aye" | firstarg "ignore me") | joinpipe`, want: "aye"}, // Multi-statements {desc: "multi 1", expr: `firstarg "hello" ; firstarg "world"`, want: "world"}, {desc: "multi 2", expr: `list "hello" | toUpper ; firstarg "world"`, want: "world"}, {desc: "multi 3", expr: `set new "this is new" ; firstarg $new`, want: "this is new"}, // Lists {desc: "list 1", expr: `firstarg ["1" "2" "3"]`, want: []any{"1", "2", "3"}}, {desc: "list 2", expr: `set one "one" ; firstarg [$one (list "two" | map { |x| toUpper $x } | head) "three"]`, want: []any{"one", "TWO", "three"}}, {desc: "list 3", expr: `firstarg []`, want: []any{}}, {desc: "list 4", expr: `set x ["a" "b" "c"] ; firstarg [$x.(2) $x.(1) $x.(0)]`, want: []any{"c", "b", "a"}}, // Maps {desc: "map 1", expr: `firstarg [one:"1" two:"2" three:"3"]`, want: map[string]any{"one": "1", "two": "2", "three": "3"}}, {desc: "map 2", expr: `firstarg ["one":"1" "two":"2" "three":"3"]`, want: map[string]any{"one": "1", "two": "2", "three": "3"}}, {desc: "map 3", expr: ` set one "one" ; set n1 "1" firstarg [ $one:$n1 (list "two" | map { |x| toUpper $x } | head):(list "2" | map { |x| toUpper $x } | head) three:"3" ]`, want: map[string]any{"one": "1", "TWO": "2", "three": "3"}}, {desc: "map 4", expr: `firstarg [:]`, want: map[string]any{}}, {desc: "map 5", expr: `set x ["a" "b" "c"] ; firstarg ["one":$x.(2) "two":$x.(1) "three":$x.(0)]`, want: map[string]any{"one": "c", "two": "b", "three": "a"}}, {desc: "map 6", expr: `set x [a:"A" b:"B" c:"C"] ; firstarg ["one":$x.c "two":$x.b "three":$x.a]`, want: map[string]any{"one": "C", "two": "B", "three": "A"}}, // Dots {desc: "dot 1", expr: `set x [1 2 3] ; $x.(0)`, want: 1}, {desc: "dot 2", expr: `set x [1 2 3] ; $x.(1)`, want: 2}, {desc: "dot 3", expr: `set x [1 2 3] ; $x.(2)`, want: 3}, {desc: "dot 4", expr: `set x [1 2 3] ; $x.(3)`, want: nil}, {desc: "dot 5", expr: `set x [1 2 3] ; $x.(add 1 1)`, want: 3}, {desc: "dot 6", expr: `set x [alpha:"hello" bravo:"world"] ; $x.alpha`, want: "hello"}, {desc: "dot 7", expr: `set x [alpha:"hello" bravo:"world"] ; $x.bravo`, want: "world"}, {desc: "dot 8", expr: `set x [alpha:"hello" bravo:"world"] ; $x.charlie`, want: nil}, {desc: "dot 9", expr: `set x [alpha:"hello" bravo:"world"] ; $x.("alpha")`, want: "hello"}, {desc: "dot 10", expr: `set x [alpha:"hello" bravo:"world"] ; $x.("bravo")`, want: "world"}, {desc: "dot 11", expr: `set x [alpha:"hello" bravo:"world"] ; $x.("charlie")`, want: nil}, {desc: "dot 12", expr: `set x [MORE:"stuff"] ; $x.("more" | toUpper)`, want: "stuff"}, {desc: "dot 13", expr: `set x [MORE:"stuff"] ; $x.(toUpper ("more"))`, want: "stuff"}, {desc: "dot 14", expr: `set x [MORE:"stuff"] ; x.y`, want: nil}, {desc: "parse comments 1", expr: parseComments1, wantErr: ucl.ErrNotConvertable}, {desc: "parse comments 2", expr: parseComments2, wantErr: ucl.ErrNotConvertable}, {desc: "parse comments 3", expr: parseComments3, wantErr: ucl.ErrNotConvertable}, {desc: "parse comments 4", expr: parseComments4, wantErr: ucl.ErrNotConvertable}, } for _, tt := range tests { t.Run(tt.desc, func(t *testing.T) { ctx := context.Background() outW := bytes.NewBuffer(nil) inst := ucl.New(ucl.WithOut(outW), ucl.WithTestBuiltin()) res, err := inst.Eval(ctx, tt.expr) if tt.wantErr != nil { assert.ErrorIs(t, err, tt.wantErr) } else { assert.NoError(t, err) assert.Equal(t, tt.want, res) } }) } } var parseComments1 = ` proc lookup { |file| foreach { |toks| } # this use to fail } ` var parseComments2 = ` proc lookup { |file| foreach { |toks| } # this use to fail # # And so did this } ` var parseComments3 = ` proc lookup { |file| foreach { |toks| } # this use to fail # # And so did this } ` var parseComments4 = ` proc lookup { |file| foreach { |toks| } } # this use to fail`