This commit is contained in:
parent
53b05b5ba6
commit
0cf2f816da
|
@ -968,6 +968,72 @@ func ifBuiltin(ctx context.Context, args macroArgs) (Object, error) {
|
||||||
return nil, errors.New("malformed if-elif-else")
|
return nil, errors.New("malformed if-elif-else")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func tryBuiltin(ctx context.Context, args macroArgs) (res Object, err error) {
|
||||||
|
if args.nargs() < 2 {
|
||||||
|
return nil, errors.New("need at least 2 arguments")
|
||||||
|
} else if args.nargs()%2 == 0 {
|
||||||
|
return nil, errors.New("need an odd number of arguments")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select catches and finally
|
||||||
|
catchBlocks := make([]int, 0)
|
||||||
|
finallyBlocks := make([]int, 0)
|
||||||
|
for i := 1; i < args.nargs(); i += 2 {
|
||||||
|
if args.identIs(ctx, i, "catch") {
|
||||||
|
if len(finallyBlocks) > 0 {
|
||||||
|
return nil, errors.New("catch cannot be used after finally")
|
||||||
|
}
|
||||||
|
catchBlocks = append(catchBlocks, i+1)
|
||||||
|
} else if args.identIs(ctx, i, "finally") {
|
||||||
|
finallyBlocks = append(finallyBlocks, i+1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if isBreakErr(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
orgErr = err
|
||||||
|
lastFinallyErr error = nil
|
||||||
|
)
|
||||||
|
|
||||||
|
for _, idx := range finallyBlocks {
|
||||||
|
if _, fErr := args.evalBlock(ctx, idx, nil, false); fErr != nil {
|
||||||
|
if isBreakErr(fErr) {
|
||||||
|
if err == nil {
|
||||||
|
err = fErr
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
lastFinallyErr = fErr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if orgErr == nil {
|
||||||
|
err = lastFinallyErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
res, err = args.evalBlock(ctx, 0, nil, false)
|
||||||
|
if err == nil {
|
||||||
|
return res, nil
|
||||||
|
} else if isBreakErr(err) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, idx := range catchBlocks {
|
||||||
|
res, err = args.evalBlock(ctx, idx, []Object{errObject{err: err}}, false)
|
||||||
|
if err == nil {
|
||||||
|
return res, nil
|
||||||
|
} else if isBreakErr(err) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
func foreachBuiltin(ctx context.Context, args macroArgs) (Object, error) {
|
func foreachBuiltin(ctx context.Context, args macroArgs) (Object, error) {
|
||||||
var (
|
var (
|
||||||
items Object
|
items Object
|
||||||
|
|
|
@ -100,10 +100,10 @@ func New(opts ...InstOption) *Inst {
|
||||||
rootEC.addCmd("assert", invokableFunc(assertBuiltin))
|
rootEC.addCmd("assert", invokableFunc(assertBuiltin))
|
||||||
|
|
||||||
rootEC.addMacro("if", macroFunc(ifBuiltin))
|
rootEC.addMacro("if", macroFunc(ifBuiltin))
|
||||||
rootEC.addMacro("foreach", macroFunc(foreachBuiltin))
|
|
||||||
rootEC.addMacro("for", macroFunc(foreachBuiltin))
|
rootEC.addMacro("for", macroFunc(foreachBuiltin))
|
||||||
rootEC.addMacro("while", macroFunc(whileBuiltin))
|
rootEC.addMacro("while", macroFunc(whileBuiltin))
|
||||||
rootEC.addMacro("proc", macroFunc(procBuiltin))
|
rootEC.addMacro("proc", macroFunc(procBuiltin))
|
||||||
|
rootEC.addMacro("try", macroFunc(tryBuiltin))
|
||||||
|
|
||||||
inst := &Inst{
|
inst := &Inst{
|
||||||
out: os.Stdout,
|
out: os.Stdout,
|
||||||
|
|
|
@ -14,6 +14,7 @@ func TestInst_Eval(t *testing.T) {
|
||||||
desc string
|
desc string
|
||||||
expr string
|
expr string
|
||||||
want any
|
want any
|
||||||
|
wantObj bool
|
||||||
wantErr error
|
wantErr error
|
||||||
}{
|
}{
|
||||||
{desc: "simple string", expr: `firstarg "hello"`, want: "hello"},
|
{desc: "simple string", expr: `firstarg "hello"`, want: "hello"},
|
||||||
|
@ -100,10 +101,10 @@ func TestInst_Eval(t *testing.T) {
|
||||||
{desc: "dot idents 8", expr: `$x = [MORE:"stuff"] ; $x.(toUpper ("more"))`, want: "stuff"},
|
{desc: "dot idents 8", expr: `$x = [MORE:"stuff"] ; $x.(toUpper ("more"))`, want: "stuff"},
|
||||||
{desc: "dot idents 9", expr: `$x = [MORE:"stuff"] ; x.y`, want: nil},
|
{desc: "dot idents 9", expr: `$x = [MORE:"stuff"] ; x.y`, want: nil},
|
||||||
|
|
||||||
{desc: "parse comments 1", expr: parseComments1, wantErr: ucl.ErrNotConvertable},
|
{desc: "parse comments 1", expr: parseComments1, wantObj: true, wantErr: nil},
|
||||||
{desc: "parse comments 2", expr: parseComments2, wantErr: ucl.ErrNotConvertable},
|
{desc: "parse comments 2", expr: parseComments2, wantObj: true, wantErr: nil},
|
||||||
{desc: "parse comments 3", expr: parseComments3, wantErr: ucl.ErrNotConvertable},
|
{desc: "parse comments 3", expr: parseComments3, wantObj: true, wantErr: nil},
|
||||||
{desc: "parse comments 4", expr: parseComments4, wantErr: ucl.ErrNotConvertable},
|
{desc: "parse comments 4", expr: parseComments4, wantObj: true, wantErr: nil},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
@ -116,6 +117,10 @@ func TestInst_Eval(t *testing.T) {
|
||||||
|
|
||||||
if tt.wantErr != nil {
|
if tt.wantErr != nil {
|
||||||
assert.ErrorIs(t, err, tt.wantErr)
|
assert.ErrorIs(t, err, tt.wantErr)
|
||||||
|
} else if tt.wantObj {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
_, isObj := res.(ucl.Object)
|
||||||
|
assert.True(t, isObj)
|
||||||
} else {
|
} else {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, tt.want, res)
|
assert.Equal(t, tt.want, res)
|
||||||
|
|
10
ucl/objs.go
10
ucl/objs.go
|
@ -387,7 +387,7 @@ func (ma macroArgs) evalBlock(ctx context.Context, n int, args []Object, pushSco
|
||||||
type errObject struct{ err error }
|
type errObject struct{ err error }
|
||||||
|
|
||||||
func (eo errObject) String() string {
|
func (eo errObject) String() string {
|
||||||
return "error:" + eo.err.Error()
|
return eo.err.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (eo errObject) Truthy() bool {
|
func (eo errObject) Truthy() bool {
|
||||||
|
@ -672,3 +672,11 @@ func (e errReturn) Error() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
var ErrHalt = errors.New("halt")
|
var ErrHalt = errors.New("halt")
|
||||||
|
|
||||||
|
func isBreakErr(err error) bool {
|
||||||
|
if err == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.As(err, &errBreak{}) || errors.As(err, &errReturn{}) || errors.Is(err, ErrHalt)
|
||||||
|
}
|
||||||
|
|
|
@ -136,7 +136,7 @@ func TestBuiltins_Echo(t *testing.T) {
|
||||||
{desc: "interpolated string 3", expr: `echo "separate\nlines\n\tand tabs"`, want: "separate\nlines\n\tand tabs\n"},
|
{desc: "interpolated string 3", expr: `echo "separate\nlines\n\tand tabs"`, want: "separate\nlines\n\tand tabs\n"},
|
||||||
{desc: "interpolated string 4", expr: `$what = "Hello" ; $where = "world" ; echo "$what, $where"`, want: "Hello, world\n"},
|
{desc: "interpolated string 4", expr: `$what = "Hello" ; $where = "world" ; echo "$what, $where"`, want: "Hello, world\n"},
|
||||||
{desc: "interpolated string 5", expr: `
|
{desc: "interpolated string 5", expr: `
|
||||||
foreach [123 "foo" true ()] { |x|
|
for [123 "foo" true ()] { |x|
|
||||||
echo "[[$x]]"
|
echo "[[$x]]"
|
||||||
}
|
}
|
||||||
`, want: "[[123]]\n[[foo]]\n[[true]]\n[[]]\n"},
|
`, want: "[[123]]\n[[foo]]\n[[true]]\n[[]]\n"},
|
||||||
|
@ -217,8 +217,8 @@ func TestBuiltins_If(t *testing.T) {
|
||||||
{desc: "compressed else", expr: `if $x { echo "true" } else { echo "false" }`, want: "false\n(nil)\n"},
|
{desc: "compressed else", expr: `if $x { echo "true" } else { echo "false" }`, want: "false\n(nil)\n"},
|
||||||
{desc: "compressed if", expr: `if $x { echo "x" } elif $y { echo "y" } else { echo "false" }`, want: "false\n(nil)\n"},
|
{desc: "compressed if", expr: `if $x { echo "x" } elif $y { echo "y" } else { echo "false" }`, want: "false\n(nil)\n"},
|
||||||
{desc: "if of itr 1", expr: `$i = itr ; if $i { echo "more" } else { echo "none" }`, want: "more\n(nil)\n"},
|
{desc: "if of itr 1", expr: `$i = itr ; if $i { echo "more" } else { echo "none" }`, want: "more\n(nil)\n"},
|
||||||
{desc: "if of itr 2", expr: `$i = itr ; foreach (seq 1) { head $i } ; if $i { echo "more" } else { echo "none" }`, want: "more\n(nil)\n"},
|
{desc: "if of itr 2", expr: `$i = itr ; for (seq 1) { head $i } ; if $i { echo "more" } else { echo "none" }`, want: "more\n(nil)\n"},
|
||||||
{desc: "if of itr 3", expr: `$i = itr ; foreach (seq 3) { head $i } ; if $i { echo "more" } else { echo "none" }`, want: "none\n(nil)\n"},
|
{desc: "if of itr 3", expr: `$i = itr ; for (seq 3) { head $i } ; if $i { echo "more" } else { echo "none" }`, want: "none\n(nil)\n"},
|
||||||
{desc: "if of itr 4", expr: `$i = (itr | map { |x| add 2 $x }) ; if $i { echo "more" } else { echo "none" }`, want: "more\n(nil)\n"},
|
{desc: "if of itr 4", expr: `$i = (itr | map { |x| add 2 $x }) ; if $i { echo "more" } else { echo "none" }`, want: "more\n(nil)\n"},
|
||||||
{desc: "if of itr 5", expr: `$i = (itr | filter { |x| () }) ; if $i { echo "more" } else { echo "none" }`, want: "none\n(nil)\n"},
|
{desc: "if of itr 5", expr: `$i = (itr | filter { |x| () }) ; if $i { echo "more" } else { echo "none" }`, want: "none\n(nil)\n"},
|
||||||
{desc: "if of itr 6", expr: `$i = (itr | filter { |x| 1 }) ; if $i { echo "more" } else { echo "none" }`, want: "more\n(nil)\n"},
|
{desc: "if of itr 6", expr: `$i = (itr | filter { |x| 1 }) ; if $i { echo "more" } else { echo "none" }`, want: "more\n(nil)\n"},
|
||||||
|
@ -245,19 +245,19 @@ func TestBuiltins_ForEach(t *testing.T) {
|
||||||
want string
|
want string
|
||||||
}{
|
}{
|
||||||
{desc: "iterate over list 1", expr: `
|
{desc: "iterate over list 1", expr: `
|
||||||
foreach ["1" "2" "3"] { |v|
|
for ["1" "2" "3"] { |v|
|
||||||
echo $v
|
echo $v
|
||||||
}`, want: "1\n2\n3\n(nil)\n"},
|
}`, want: "1\n2\n3\n(nil)\n"},
|
||||||
{desc: "iterate over list 2",
|
{desc: "iterate over list 2",
|
||||||
expr: `foreach ["1" "2" "3"] echo`,
|
expr: `for ["1" "2" "3"] echo`,
|
||||||
want: "1\n2\n3\n(nil)\n"},
|
want: "1\n2\n3\n(nil)\n"},
|
||||||
// TODO: hash is not sorted, so need to find a way to sort it
|
// TODO: hash is not sorted, so need to find a way to sort it
|
||||||
{desc: "iterate over map 1", expr: `
|
{desc: "iterate over map 1", expr: `
|
||||||
foreach [a:"1"] { |k v| echo $k "=" $v }`, want: "a=1\n(nil)\n"},
|
for [a:"1"] { |k v| echo $k "=" $v }`, want: "a=1\n(nil)\n"},
|
||||||
{desc: "iterate over map 2", expr: `
|
{desc: "iterate over map 2", expr: `
|
||||||
foreach [a:"1"] echo`, want: "a1\n(nil)\n"},
|
for [a:"1"] echo`, want: "a1\n(nil)\n"},
|
||||||
{desc: "iterate via pipe", expr: `["2" "4" "6"] | foreach { |x| echo $x }`, want: "2\n4\n6\n(nil)\n"},
|
{desc: "iterate via pipe", expr: `["2" "4" "6"] | for { |x| echo $x }`, want: "2\n4\n6\n(nil)\n"},
|
||||||
{desc: "iterate from iterator 1", expr: `itr | foreach { |x| echo $x }`, want: "1\n2\n3\n(nil)\n"},
|
{desc: "iterate from iterator 1", expr: `itr | for { |x| echo $x }`, want: "1\n2\n3\n(nil)\n"},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
@ -361,29 +361,29 @@ func TestBuiltins_Break(t *testing.T) {
|
||||||
want string
|
want string
|
||||||
}{
|
}{
|
||||||
{desc: "break unconditionally returning nothing", expr: `
|
{desc: "break unconditionally returning nothing", expr: `
|
||||||
foreach ["1" "2" "3"] { |v|
|
for ["1" "2" "3"] { |v|
|
||||||
break
|
break
|
||||||
echo $v
|
echo $v
|
||||||
}`, want: "(nil)\n"},
|
}`, want: "(nil)\n"},
|
||||||
{desc: "break conditionally returning nothing", expr: `
|
{desc: "break conditionally returning nothing", expr: `
|
||||||
foreach ["1" "2" "3"] { |v|
|
for ["1" "2" "3"] { |v|
|
||||||
echo $v
|
echo $v
|
||||||
if (eq $v "2") { break }
|
if (eq $v "2") { break }
|
||||||
}`, want: "1\n2\n(nil)\n"},
|
}`, want: "1\n2\n(nil)\n"},
|
||||||
{desc: "break inner loop only returning nothing", expr: `
|
{desc: "break inner loop only returning nothing", expr: `
|
||||||
foreach ["a" "b"] { |u|
|
for ["a" "b"] { |u|
|
||||||
foreach ["1" "2" "3"] { |v|
|
for ["1" "2" "3"] { |v|
|
||||||
echo $u $v
|
echo $u $v
|
||||||
if (eq $v "2") { break }
|
if (eq $v "2") { break }
|
||||||
}
|
}
|
||||||
}`, want: "a1\na2\nb1\nb2\n(nil)\n"},
|
}`, want: "a1\na2\nb1\nb2\n(nil)\n"},
|
||||||
{desc: "break returning value 1", expr: `
|
{desc: "break returning value 1", expr: `
|
||||||
echo (foreach ["1" "2" "3"] { |v|
|
echo (for ["1" "2" "3"] { |v|
|
||||||
echo $v
|
echo $v
|
||||||
if (eq $v "2") { break "hello" }
|
if (eq $v "2") { break "hello" }
|
||||||
})`, want: "1\n2\nhello\n(nil)\n"},
|
})`, want: "1\n2\nhello\n(nil)\n"},
|
||||||
{desc: "break returning value 2", expr: `
|
{desc: "break returning value 2", expr: `
|
||||||
echo (foreach (itr) { |v|
|
echo (for (itr) { |v|
|
||||||
echo $v
|
echo $v
|
||||||
if (eq $v 2) { break "hello" }
|
if (eq $v 2) { break "hello" }
|
||||||
})`, want: "1\n2\nhello\n(nil)\n"},
|
})`, want: "1\n2\nhello\n(nil)\n"},
|
||||||
|
@ -410,20 +410,20 @@ func TestBuiltins_Continue(t *testing.T) {
|
||||||
want string
|
want string
|
||||||
}{
|
}{
|
||||||
{desc: "continue unconditionally", expr: `
|
{desc: "continue unconditionally", expr: `
|
||||||
foreach ["1" "2" "3"] { |v|
|
for ["1" "2" "3"] { |v|
|
||||||
echo $v "s"
|
echo $v "s"
|
||||||
continue
|
continue
|
||||||
echo $v "e"
|
echo $v "e"
|
||||||
}`, want: "1s\n2s\n3s\n(nil)\n"},
|
}`, want: "1s\n2s\n3s\n(nil)\n"},
|
||||||
{desc: "conditionally conditionally", expr: `
|
{desc: "conditionally conditionally", expr: `
|
||||||
foreach ["1" "2" "3"] { |v|
|
for ["1" "2" "3"] { |v|
|
||||||
echo $v "s"
|
echo $v "s"
|
||||||
if (eq $v "2") { continue }
|
if (eq $v "2") { continue }
|
||||||
echo $v "e"
|
echo $v "e"
|
||||||
}`, want: "1s\n1e\n2s\n3s\n3e\n(nil)\n"},
|
}`, want: "1s\n1e\n2s\n3s\n3e\n(nil)\n"},
|
||||||
{desc: "continue inner loop only", expr: `
|
{desc: "continue inner loop only", expr: `
|
||||||
foreach ["a" "b"] { |u|
|
for ["a" "b"] { |u|
|
||||||
foreach ["1" "2" "3"] { |v|
|
for ["1" "2" "3"] { |v|
|
||||||
if (eq $v "2") { continue }
|
if (eq $v "2") { continue }
|
||||||
echo $u $v
|
echo $u $v
|
||||||
}
|
}
|
||||||
|
@ -614,7 +614,7 @@ func TestBuiltins_Return(t *testing.T) {
|
||||||
`, want: "Greet the\nHello, moon\n(nil)\n"},
|
`, want: "Greet the\nHello, moon\n(nil)\n"},
|
||||||
{desc: "return in loop", expr: `
|
{desc: "return in loop", expr: `
|
||||||
proc countdown { |nums|
|
proc countdown { |nums|
|
||||||
foreach $nums { |n|
|
for $nums { |n|
|
||||||
echo $n
|
echo $n
|
||||||
if (eq $n 3) {
|
if (eq $n 3) {
|
||||||
return "abort"
|
return "abort"
|
||||||
|
@ -639,7 +639,7 @@ func TestBuiltins_Return(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
proc test-thing {
|
proc test-thing {
|
||||||
foreach [1 2 3] { |x|
|
for [1 2 3] { |x|
|
||||||
do-thing {
|
do-thing {
|
||||||
echo $x
|
echo $x
|
||||||
}
|
}
|
||||||
|
@ -654,7 +654,7 @@ func TestBuiltins_Return(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
proc test-thing {
|
proc test-thing {
|
||||||
foreach [1 2 3] { |x|
|
for [1 2 3] { |x|
|
||||||
do-thing (proc {
|
do-thing (proc {
|
||||||
echo $x
|
echo $x
|
||||||
})
|
})
|
||||||
|
@ -669,7 +669,7 @@ func TestBuiltins_Return(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
proc test-thing {
|
proc test-thing {
|
||||||
foreach [1 2 3] { |x|
|
for [1 2 3] { |x|
|
||||||
$myClosure = proc { echo $x }
|
$myClosure = proc { echo $x }
|
||||||
do-thing $myClosure
|
do-thing $myClosure
|
||||||
}
|
}
|
||||||
|
@ -688,7 +688,7 @@ func TestBuiltins_Return(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (test-thing) { |y| call $y }
|
for (test-thing) { |y| call $y }
|
||||||
`, want: "1\n2\n3\n(nil)\n"},
|
`, want: "1\n2\n3\n(nil)\n"},
|
||||||
{desc: "check closure 5", expr: `
|
{desc: "check closure 5", expr: `
|
||||||
proc do-thing { |p|
|
proc do-thing { |p|
|
||||||
|
@ -703,7 +703,7 @@ func TestBuiltins_Return(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
$hello = "xx"
|
$hello = "xx"
|
||||||
foreach (test-thing) { |y| call $y ; echo $hello }
|
for (test-thing) { |y| call $y ; echo $hello }
|
||||||
`, want: "1\nxx\n2\nxx\n3\nxx\n(nil)\n"},
|
`, want: "1\nxx\n2\nxx\n3\nxx\n(nil)\n"},
|
||||||
{desc: "check closure 7", expr: `
|
{desc: "check closure 7", expr: `
|
||||||
proc do-thing { |p|
|
proc do-thing { |p|
|
||||||
|
@ -720,7 +720,7 @@ func TestBuiltins_Return(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
$hello = "xx"
|
$hello = "xx"
|
||||||
foreach (test-thing) { |y| call $y ; echo $hello }
|
for (test-thing) { |y| call $y ; echo $hello }
|
||||||
`, want: "3\nxx\n3\nxx\n3\nxx\n(nil)\n"},
|
`, want: "3\nxx\n3\nxx\n3\nxx\n(nil)\n"},
|
||||||
{desc: "check closure 7", expr: `
|
{desc: "check closure 7", expr: `
|
||||||
proc do-thing { |p|
|
proc do-thing { |p|
|
||||||
|
@ -738,7 +738,7 @@ func TestBuiltins_Return(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
$hello = "xx"
|
$hello = "xx"
|
||||||
foreach (test-thing) { |y| call $y ; echo $hello }
|
for (test-thing) { |y| call $y ; echo $hello }
|
||||||
`, want: "1\nxx\n2\nxx\n3\nxx\n(nil)\n"},
|
`, want: "1\nxx\n2\nxx\n3\nxx\n(nil)\n"},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -756,6 +756,239 @@ func TestBuiltins_Return(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBuiltins_Try(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
desc string
|
||||||
|
expr string
|
||||||
|
want string
|
||||||
|
wantErr string
|
||||||
|
}{
|
||||||
|
{desc: "try 1", expr: `
|
||||||
|
try {
|
||||||
|
echo "Hello"
|
||||||
|
error "bang"
|
||||||
|
echo "World"
|
||||||
|
} catch {
|
||||||
|
echo "Caught"
|
||||||
|
}
|
||||||
|
`, want: "Hello\nCaught\n(nil)\n"},
|
||||||
|
{desc: "try 2", expr: `
|
||||||
|
try {
|
||||||
|
echo "Hello"
|
||||||
|
error "bang"
|
||||||
|
echo "World"
|
||||||
|
} finally {
|
||||||
|
echo "Always"
|
||||||
|
}
|
||||||
|
`, want: "Hello\nAlways\n", wantErr: "bang"},
|
||||||
|
{desc: "try 3", expr: `
|
||||||
|
try {
|
||||||
|
echo "Hello"
|
||||||
|
error "bang"
|
||||||
|
echo "World"
|
||||||
|
} catch { |e|
|
||||||
|
echo "Error was: ${e}"
|
||||||
|
} finally {
|
||||||
|
echo "Always"
|
||||||
|
}
|
||||||
|
`, want: "Hello\nError was: bang\nAlways\n(nil)\n"},
|
||||||
|
{desc: "try 4", expr: `
|
||||||
|
try {
|
||||||
|
echo "Hello"
|
||||||
|
echo "World"
|
||||||
|
} catch { |e|
|
||||||
|
echo "Should not call me"
|
||||||
|
} finally {
|
||||||
|
echo "Always"
|
||||||
|
}
|
||||||
|
`, want: "Hello\nWorld\nAlways\n(nil)\n"},
|
||||||
|
{desc: "try 5", expr: `
|
||||||
|
try {
|
||||||
|
echo "Hello"
|
||||||
|
|
||||||
|
try {
|
||||||
|
echo "Nested"
|
||||||
|
error "bang"
|
||||||
|
echo "World"
|
||||||
|
} catch { |f|
|
||||||
|
echo "Catch me: $f"
|
||||||
|
} finally {
|
||||||
|
echo "Always 2"
|
||||||
|
}
|
||||||
|
} catch { |e|
|
||||||
|
echo "Should not call me"
|
||||||
|
} finally {
|
||||||
|
echo "Always"
|
||||||
|
}
|
||||||
|
`, want: "Hello\nNested\nCatch me: bang\nAlways 2\nAlways\n(nil)\n"},
|
||||||
|
{desc: "try 6", expr: `
|
||||||
|
try {
|
||||||
|
echo "Hello"
|
||||||
|
|
||||||
|
try {
|
||||||
|
echo "Nested"
|
||||||
|
error "bang"
|
||||||
|
echo "World"
|
||||||
|
} finally {
|
||||||
|
echo "Always 2"
|
||||||
|
}
|
||||||
|
} catch { |e|
|
||||||
|
echo "Catch me: $e"
|
||||||
|
} finally {
|
||||||
|
echo "Always"
|
||||||
|
}
|
||||||
|
`, want: "Hello\nNested\nAlways 2\nCatch me: bang\nAlways\n(nil)\n"},
|
||||||
|
{desc: "try 7", expr: `
|
||||||
|
try {
|
||||||
|
echo "Hello"
|
||||||
|
error "bang"
|
||||||
|
} catch { |e|
|
||||||
|
echo "Catch me: $e"
|
||||||
|
error $e
|
||||||
|
} finally {
|
||||||
|
echo "Always"
|
||||||
|
}
|
||||||
|
`, want: "Hello\nCatch me: bang\nAlways\n", wantErr: "bang"},
|
||||||
|
{desc: "try 8", expr: `
|
||||||
|
try {
|
||||||
|
echo "Hello"
|
||||||
|
error "bang"
|
||||||
|
} catch { |e|
|
||||||
|
echo "Catch me: $e"
|
||||||
|
} catch { |e|
|
||||||
|
echo "Catch not me: $e"
|
||||||
|
} finally {
|
||||||
|
echo "Always"
|
||||||
|
}
|
||||||
|
`, want: "Hello\nCatch me: bang\nAlways\n(nil)\n"},
|
||||||
|
{desc: "try 9", expr: `
|
||||||
|
try {
|
||||||
|
echo "Hello"
|
||||||
|
error "bang"
|
||||||
|
} catch { |e|
|
||||||
|
echo "Catch me: $e"
|
||||||
|
error "boom"
|
||||||
|
} catch { |e|
|
||||||
|
echo "Catch me too: $e"
|
||||||
|
} finally {
|
||||||
|
echo "Always"
|
||||||
|
}
|
||||||
|
`, want: "Hello\nCatch me: bang\nCatch me too: boom\nAlways\n(nil)\n"},
|
||||||
|
{desc: "try 10", expr: `
|
||||||
|
try {
|
||||||
|
echo "Hello"
|
||||||
|
error "bang"
|
||||||
|
} catch { |e|
|
||||||
|
echo "Catch me: $e"
|
||||||
|
error "boom"
|
||||||
|
} catch { |e|
|
||||||
|
echo "Catch me too: $e"
|
||||||
|
error "mint"
|
||||||
|
} finally {
|
||||||
|
echo "Always"
|
||||||
|
}
|
||||||
|
`, want: "Hello\nCatch me: bang\nCatch me too: boom\nAlways\n", wantErr: "mint"},
|
||||||
|
{desc: "try 11", expr: `
|
||||||
|
try {
|
||||||
|
echo "Hello"
|
||||||
|
error "bang"
|
||||||
|
} catch { |e|
|
||||||
|
echo "Catch me: $e"
|
||||||
|
} finally {
|
||||||
|
echo "Always"
|
||||||
|
error "boom"
|
||||||
|
}
|
||||||
|
`, want: "Hello\nCatch me: bang\nAlways\n", wantErr: "boom"},
|
||||||
|
{desc: "try 12", expr: `
|
||||||
|
$a = try { "e" } catch { "f" }
|
||||||
|
echo $a
|
||||||
|
`, want: "e\n(nil)\n"},
|
||||||
|
{desc: "try 13", expr: `
|
||||||
|
$a = try { error "bang" } catch { "f" }
|
||||||
|
echo $a
|
||||||
|
`, want: "f\n(nil)\n"},
|
||||||
|
{desc: "try 14", expr: `
|
||||||
|
for [1 2 3] { |x|
|
||||||
|
try {
|
||||||
|
echo $x
|
||||||
|
continue
|
||||||
|
echo "No"
|
||||||
|
} catch { |e|
|
||||||
|
echo "Catch me: $x"
|
||||||
|
} finally {
|
||||||
|
echo "Never"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`, want: "1\n2\n3\n(nil)\n"},
|
||||||
|
{desc: "try 15", expr: `
|
||||||
|
for [1 2 3] { |x|
|
||||||
|
try {
|
||||||
|
echo $x
|
||||||
|
break
|
||||||
|
echo "No"
|
||||||
|
} catch { |e|
|
||||||
|
echo "Catch me: $x"
|
||||||
|
} finally {
|
||||||
|
echo "Never"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`, want: "1\n(nil)\n"},
|
||||||
|
{desc: "try 16", expr: `
|
||||||
|
for [1 2 3] { |x|
|
||||||
|
try {
|
||||||
|
echo $x
|
||||||
|
error "bang"
|
||||||
|
echo "No"
|
||||||
|
} catch { |e|
|
||||||
|
echo "Catch me at $x: $e"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`, want: "1\nCatch me at 1: bang\n2\nCatch me at 2: bang\n3\nCatch me at 3: bang\n(nil)\n"},
|
||||||
|
{desc: "try 17", expr: `
|
||||||
|
for [1 2 3] { |x|
|
||||||
|
try {
|
||||||
|
echo $x
|
||||||
|
error "bang"
|
||||||
|
echo "No"
|
||||||
|
} catch { |e|
|
||||||
|
echo "Catch me: $e"
|
||||||
|
break
|
||||||
|
} finally {
|
||||||
|
echo "Never"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`, want: "1\nCatch me: bang\n(nil)\n"},
|
||||||
|
{desc: "try 18", expr: `
|
||||||
|
for [1 2 3] { |x|
|
||||||
|
try {
|
||||||
|
echo $x
|
||||||
|
} finally {
|
||||||
|
echo "Always $x"
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`, want: "1\nAlways 1\n2\nAlways 2\n3\nAlways 3\n(nil)\n"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.desc, func(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
outW := bytes.NewBuffer(nil)
|
||||||
|
|
||||||
|
inst := New(WithOut(outW), WithTestBuiltin())
|
||||||
|
err := evalAndDisplay(ctx, inst, tt.expr)
|
||||||
|
|
||||||
|
if tt.wantErr != "" {
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Equal(t, tt.wantErr, err.Error())
|
||||||
|
} else {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
assert.Equal(t, tt.want, outW.String())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestBuiltins_Seq(t *testing.T) {
|
func TestBuiltins_Seq(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
desc string
|
desc string
|
||||||
|
@ -853,7 +1086,7 @@ func TestBuiltins_Map(t *testing.T) {
|
||||||
$add2 = proc { |x| add $x 2 }
|
$add2 = proc { |x| add $x 2 }
|
||||||
|
|
||||||
$l = itr | map $add2
|
$l = itr | map $add2
|
||||||
foreach $l { |x| echo $x }
|
for $l { |x| echo $x }
|
||||||
`, want: "3\n4\n5\n(nil)\n"},
|
`, want: "3\n4\n5\n(nil)\n"},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1106,7 +1339,7 @@ func TestBuiltins_Filter(t *testing.T) {
|
||||||
}},
|
}},
|
||||||
{desc: "filter map 3", expr: `filter [alpha:"hello" bravo:"world"] { |k v| eq $v "alpha" }`, want: map[string]any{}},
|
{desc: "filter map 3", expr: `filter [alpha:"hello" bravo:"world"] { |k v| eq $v "alpha" }`, want: map[string]any{}},
|
||||||
|
|
||||||
{desc: "filter itr 1", expr: `$s = "" ; itr | filter { |x| ne $x 2 } | foreach { |x| $s = "$s $x" }; $s`, want: " 1 3"},
|
{desc: "filter itr 1", expr: `$s = "" ; itr | filter { |x| ne $x 2 } | for { |x| $s = "$s $x" }; $s`, want: " 1 3"},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
|
|
@ -164,7 +164,7 @@ func TestInst_SetBuiltin(t *testing.T) {
|
||||||
wantOut string
|
wantOut string
|
||||||
}{
|
}{
|
||||||
{descr: "return as is", expr: `countTo3`, want: []string{"1", "2", "3"}},
|
{descr: "return as is", expr: `countTo3`, want: []string{"1", "2", "3"}},
|
||||||
{descr: "iterate over", expr: `foreach (countTo3) { |x| echo $x }`, wantOut: "1\n2\n3\n"},
|
{descr: "iterate over", expr: `for (countTo3) { |x| echo $x }`, wantOut: "1\n2\n3\n"},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
|
Loading…
Reference in a new issue