Renamed foreach to for
All checks were successful
Build / build (push) Successful in 1m51s

This commit is contained in:
Leon Mika 2025-02-04 09:04:11 +11:00
parent d1a9bb86c7
commit 67671ce475
5 changed files with 51 additions and 43 deletions

View file

@ -50,10 +50,10 @@ If COL is a list, `first` will consume from index 0 and will continue until eith
satisfies the predicate, or until the end of the list is reached. If COL is an iterator, `first` will continue satisfies the predicate, or until the end of the list is reached. If COL is an iterator, `first` will continue
consuming values until a value satisfying the predicate is found, or until the iterator is exhausted. consuming values until a value satisfying the predicate is found, or until the iterator is exhausted.
### foreach ### for
``` ```
foreach COL BLOCK for COL BLOCK
``` ```
Iterates BLOCK over every element of the sequence. Iterates BLOCK over every element of the sequence.
@ -63,16 +63,16 @@ If COL is a hash, BLOCK receives both the key and value of each element.
BLOCK can call `break` and `continue` which will exit out of the loop, or jump to the start of the next iteration BLOCK can call `break` and `continue` which will exit out of the loop, or jump to the start of the next iteration
respectively. respectively.
The return value of `foreach` will be the result of the last iteration, unless `break` is called with a value. The return value of `for` will be the result of the last iteration, unless `break` is called with a value.
This is implemented as a macro but can be used in a pipeline. This is implemented as a macro but can be used in a pipeline.
``` ```
foreach [1 2 3] { |e| for [1 2 3] { |e|
echo "Element = $e" echo "Element = $e"
} }
[a:"one" b:"two"] | foreach { |k v| [a:"one" b:"two"] | for { |k v|
echo "Key $k = $v" echo "Key $k = $v"
} }
``` ```
@ -167,7 +167,7 @@ If FROM and TO are equal, or if FROM is unspecified and TO is 0, then the sequen
If -inc is specified, then the sequence will step towards TO inclusively. If -inc is specified, then the sequence will step towards TO inclusively.
``` ```
foreach (seq 5) { |i| for (seq 5) { |i|
echo $i echo $i
} }
``` ```
@ -182,3 +182,11 @@ Sets the value of variable NAME to VALUE. Any variable with NAME will be checked
within the scope first, including any parent scopes, before a new variable is defined. within the scope first, including any parent scopes, before a new variable is defined.
Any new variables will only be defined with the current scope. Any new variables will only be defined with the current scope.
### set!
```
set! NAME VALUE
```
Sets the value of variable NAME to VALUE. VALUE must be non-null otherwise `set!` will raise an error.

View file

@ -104,7 +104,7 @@ func New(opts ...InstOption) *Inst {
rootEC.addCmd("error", invokableFunc(errorBuiltin)) rootEC.addCmd("error", invokableFunc(errorBuiltin))
rootEC.addMacro("if", macroFunc(ifBuiltin)) rootEC.addMacro("if", macroFunc(ifBuiltin))
rootEC.addMacro("foreach", macroFunc(foreachBuiltin)) rootEC.addMacro("for", macroFunc(foreachBuiltin))
rootEC.addMacro("proc", macroFunc(procBuiltin)) rootEC.addMacro("proc", macroFunc(procBuiltin))
rootEC.addMacro("try", macroFunc(tryBuiltin)) rootEC.addMacro("try", macroFunc(tryBuiltin))

View file

@ -179,7 +179,7 @@ func TestInst_MissingVarHandler(t *testing.T) {
var parseComments1 = ` var parseComments1 = `
proc lookup { |file| proc lookup { |file|
foreach { |toks| for { |toks|
} }
# this use to fail # this use to fail
} }
@ -187,7 +187,7 @@ var parseComments1 = `
var parseComments2 = ` var parseComments2 = `
proc lookup { |file| proc lookup { |file|
foreach { |toks| for { |toks|
} }
# this use to fail # this use to fail
@ -199,7 +199,7 @@ var parseComments2 = `
var parseComments3 = ` var parseComments3 = `
proc lookup { |file| proc lookup { |file|
foreach { |toks| for { |toks|
} }
# this use to fail # this use to fail
@ -211,7 +211,7 @@ var parseComments3 = `
var parseComments4 = ` var parseComments4 = `
proc lookup { |file| proc lookup { |file|
foreach { |toks| for { |toks|
} }
} }
# this use to fail` # this use to fail`

View file

@ -129,7 +129,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: `set what "Hello" ; set where "world" ; echo "$what, $where"`, want: "Hello, world\n"}, {desc: "interpolated string 4", expr: `set what "Hello" ; set 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"},
@ -210,8 +210,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: `set i (itr) ; if $i { echo "more" } else { echo "none" }`, want: "more\n(nil)\n"}, {desc: "if of itr 1", expr: `set i (itr) ; if $i { echo "more" } else { echo "none" }`, want: "more\n(nil)\n"},
{desc: "if of itr 2", expr: `set 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: `set 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: `set 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: `set 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: `set 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: `set 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: `set i (itr | filter { |x| () }) ; if $i { echo "more" } else { echo "none" }`, want: "none\n(nil)\n"}, {desc: "if of itr 5", expr: `set i (itr | filter { |x| () }) ; if $i { echo "more" } else { echo "none" }`, want: "none\n(nil)\n"},
{desc: "if of itr 6", expr: `set i (itr | filter { |x| 1 }) ; if $i { echo "more" } else { echo "none" }`, want: "more\n(nil)\n"}, {desc: "if of itr 6", expr: `set i (itr | filter { |x| 1 }) ; if $i { echo "more" } else { echo "none" }`, want: "more\n(nil)\n"},
@ -430,26 +430,26 @@ func TestBuiltins_Try(t *testing.T) {
} }
} }
func TestBuiltins_ForEach(t *testing.T) { func TestBuiltins_For(t *testing.T) {
tests := []struct { tests := []struct {
desc string desc string
expr string expr string
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 {
@ -473,29 +473,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"},
@ -522,20 +522,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
} }
@ -726,7 +726,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"
@ -751,7 +751,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
} }
@ -766,7 +766,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
}) })
@ -781,7 +781,7 @@ func TestBuiltins_Return(t *testing.T) {
} }
proc test-thing { proc test-thing {
foreach [1 2 3] { |x| for [1 2 3] { |x|
set myClosure (proc { echo $x }) set myClosure (proc { echo $x })
do-thing $myClosure do-thing $myClosure
} }
@ -800,7 +800,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|
@ -815,7 +815,7 @@ func TestBuiltins_Return(t *testing.T) {
} }
set hello "xx" set 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|
@ -832,7 +832,7 @@ func TestBuiltins_Return(t *testing.T) {
} }
set hello "xx" set 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|
@ -850,7 +850,7 @@ func TestBuiltins_Return(t *testing.T) {
} }
set hello "xx" set 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"},
} }
@ -965,7 +965,7 @@ func TestBuiltins_Map(t *testing.T) {
set add2 (proc { |x| add $x 2 }) set add2 (proc { |x| add $x 2 })
set l (itr | map $add2) set 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"},
} }
@ -1218,7 +1218,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: `set s "" ; itr | filter { |x| ne $x 2 } | foreach { |x| set s "$s $x" }; $s`, want: " 1 3"}, {desc: "filter itr 1", expr: `set s "" ; itr | filter { |x| ne $x 2 } | for { |x| set s "$s $x" }; $s`, want: " 1 3"},
} }
for _, tt := range tests { for _, tt := range tests {

View file

@ -187,7 +187,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 {
@ -222,7 +222,7 @@ func TestInst_SetBuiltin(t *testing.T) {
}{ }{
{descr: "return as is", expr: `getOpaque`, wantErr: false}, {descr: "return as is", expr: `getOpaque`, wantErr: false},
{descr: "carry around ok", expr: `set x (getOpaque) ; $x`, wantErr: false}, {descr: "carry around ok", expr: `set x (getOpaque) ; $x`, wantErr: false},
{descr: "iterate over", expr: `foreach (countTo3) { |x| echo $x }`, wantErr: true}, {descr: "iterate over", expr: `for (countTo3) { |x| echo $x }`, wantErr: true},
} }
for _, tt := range tests { for _, tt := range tests {