Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(foreach): ✨ add statement to exit from a foreach loop #841

Merged
merged 6 commits into from
Apr 5, 2024
2 changes: 1 addition & 1 deletion src/lua/zencode.lua
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ function ZEN:begin(new_heap)
{name = 'enter_ifforeach', from = {'foreach', 'whenforeach', 'ifforeach', 'endifforeach'}, to = 'ifforeach'},
{name = 'enter_whenifforeach', from = {'ifforeach', 'whenifforeach'}, to = 'whenifforeach'},
{name = 'enter_endifforeach', from = {'ifforeach', 'whenifforeach'}, to = 'endifforeach'},
{name = 'enter_foreach', from = {'given', 'when', 'endif', 'foreach'}, to = 'foreach'},
{name = 'enter_foreach', from = {'given', 'when', 'endif', 'foreach', 'endforeach'}, to = 'foreach'},
{name = 'enter_whenforeach', from = {'foreach', 'whenforeach', 'endifforeach'}, to = 'whenforeach'},
{name = 'enter_endforeach', from = {'whenforeach', 'endifforeach'}, to = 'endforeach'},
{name = 'enter_and', from = 'when', to = 'when'},
Expand Down
34 changes: 32 additions & 2 deletions src/lua/zencode_foreach.lua
Original file line number Diff line number Diff line change
@@ -1,20 +1,38 @@
local function clear_iterators()
if not ZEN.ITER.names then return end
for _,v in pairs(ZEN.ITER.names) do
ACK[v] = nil
CODEC[v] = nil
end
ZEN.ITER.names = nil
end

Foreach("'' in ''", function(name, collection)
local info = ZEN.ITER
local col = have(collection)
zencode_assert(CODEC[collection].zentype == "a", "Can only iterate over arrays")
local collection_codec = CODEC[collection]
zencode_assert(collection_codec.zentype == "a", "Can only iterate over arrays")
-- in the first itaration decale the index variable
if info.pos == 1 or not ACK[name] then
empty(name)
if info.names then table.insert(info.names, name)
else info.names = {name} end
end

-- skip execution in the last iteration
if info.pos == #col+1 then
info.pos = 0
clear_iterators()
else
-- for each iteration read the value in the collection
ACK[name] = col[info.pos]
if not CODEC[name] then
new_codec(name, {encoding = CODEC[collection].encoding})
local n_codec = {encoding = collection_codec.encoding}
if collection_codec.schema then
n_codec.schema = collection_codec.schema
n_codec.zentype = "e"
end
new_codec(name, n_codec)
end
end
end)
Expand All @@ -27,6 +45,8 @@ Foreach("'' in sequence from '' to '' with step ''", function(name, from_name, t

if info.pos == 1 or not ACK[name] then
empty(name)
if info.names then table.insert(info.names, name)
else info.names = {name} end
end

zencode_assert(type(from) == type(to) and type(to) == type(step), "Types must be equal in foreach declaration")
Expand Down Expand Up @@ -56,6 +76,7 @@ Foreach("'' in sequence from '' to '' with step ''", function(name, from_name, t

if finished then
info.pos = 0
clear_iterators()
else
ACK[name] = current_value
if not CODEC[name] then
Expand All @@ -64,3 +85,12 @@ Foreach("'' in sequence from '' to '' with step ''", function(name, from_name, t
end
end
end)

local function break_foreach()
zencode_assert(ZEN.ITER and ZEN.ITER.pos ~=0, "Can only exit from foreach loop")
ZEN.ITER.pos = 0
clear_iterators()
end

When("exit foreach", break_foreach)
When("break foreach", break_foreach)
100 changes: 100 additions & 0 deletions test/zencode/foreach.bats
Original file line number Diff line number Diff line change
Expand Up @@ -271,3 +271,103 @@ EOF
assert_output '{"floats":[5,6,10,9,8,7,6,5,4,3]}'
}

@test "exit from foreach loop" {
cat << EOF | save_asset exit_from_foreach.data
{
"numbers": [1,2,3,4,5,6,7,8],
"limit": 4,
"zero": 0,
"two": 2,
"ten": 10
}
EOF

cat << EOF | zexe exit_from_foreach.zen exit_from_foreach.data
Given I have a 'float array' named 'numbers'
Given I have a 'float' named 'limit'
Given I have a 'float' named 'zero'
Given I have a 'float' named 'two'
Given I have a 'float' named 'ten'

When I create the 'float array' named 'y'
Foreach 'x' in 'numbers'
If I verify 'x' is equal to 'limit'
When I break foreach
EndIf
When I move 'x' in 'y'
EndForeach

If I verify 'x' is found
When I remove 'x'
EndIf

When I create the 'float array' named 'z'
Foreach 'x' in sequence from 'zero' to 'ten' with step 'two'
If I verify 'x' is equal to 'limit'
When I exit foreach
EndIf
When I copy 'x' in 'z'
EndForeach

Then print 'y'
Then print 'z'
EOF
save_output "exit_from_foreach.out"
assert_output '{"y":[1,2,3],"z":[0,2]}'
}

@test "Two foreach with the same iterator variable name" {
cat << EOF | save_asset two_foreach_same_var.data
{
"arr1": [
"str1",
"str2"
],
"arr2": [
"str3"
]
}
EOF
cat << EOF | zexe two_foreach_same_var.zen two_foreach_same_var.data
Given I have a 'string array' named 'arr1'
Given I have a 'string array' named 'arr2'

When I create the 'string array' named 'res'
Foreach 'x' in 'arr1'
When I copy 'x' in 'res'
Endforeach

Foreach 'x' in 'arr2'
When I copy 'x' in 'res'
Endforeach

Then print the 'res'
EOF
save_output two_foreach_same_var.out
assert_output '{"res":["str1","str2","str3"]}'
}

@test "Foreach on array of schema" {
cat << EOF | save_asset foreach_schema.data
{
"arr": [
"eyJhbGciOiAiRVMyNTYiLCAidHlwIjogInZjK3NkLWp3dCJ9.eyJfc2QiOiBbInZucGt1cWZBSWFBTlBmZXl6WXhUVllWUGxJY3JBWlVvU3N5TGFhQ0tlWUkiLCAiM1BEeUhOMXphcklJMG1TdDUwMkV2ZVIwVHhlOHlTQ1hDOFlYd1NvV1lZWSIsICJFMS12Wnl1Wmhlbkhlam1nYi1kTFhtaDBPODFLTUtWU25RYjN2Mjl6SGlRIl0sICJfc2RfYWxnIjogInNoYS0yNTYiLCAiaXNzIjogImh0dHA6Ly9leGFtcGxlLm9yZyIsICJzdWIiOiAidXNlciA0MiJ9.-BY5L0dcz2p-nCIhmL_0RK5QjzmKOI45E7anmZqg0Vct16lyF2_em3R8GzewmcrVT-NnZaT6EKrO79F4VGkFeQ~WyJ1eURGMUgwQVlSYmI0TVFubUx2dmVBIiwgImdpdmVuX25hbWUiLCAiSm9obiJd~WyJWam9zM2ZoOFZVU3Z2NHVYUVhuSWlRIiwgImZhbWlseV9uYW1lIiwgIkRvZSJd~WyIzV2JpbGZtNk1rdDFBUzlERXFtOS1nIiwgImVtYWlsIiwgImpvaG5kb2VAZXhhbXBsZS5jb20iXQ~",
"eyJhbGciOiAiRVMyNTYiLCAidHlwIjogInZjK3NkLWp3dCJ9.eyJfc2QiOiBbIlVaNG1MUV9MWUQ3QWc5RlpBSWMzX0Iyd0ZPR29DdlpBRGg1X2V4U3VHWUUiLCAibGM4bFZmT0ZMaXFSUU80T2lHeTFJWEh5SmpMT0dRSFY3c094eEQ0N1MySSIsICItYkIyZTVVdVZQZEltMzRWWVVpU2FfRHFJV1Ytc2tWSVphUl96MVNwYmFnIiwgInNkT0RjeXdEcVFVaVVnREV5UERyWFZaNTdkeEswNVhoZG5DWFhRRTlOX2ciXSwgIl9zZF9hbGciOiAic2hhLTI1NiIsICJpc3MiOiAiaHR0cDovL2V4YW1wbGUub3JnIiwgInN1YiI6ICJ1c2VyIDQyIn0.tZRB6X7fgASuHmPrhpIK_4veA6c2Ue0g3xGxIrPjHBsWXeROpAEmvMn593x5zkenuvRoO2r2wZNxnHJhqu0l0Q~WyJ0Tm9iM0NBTVhMcUVQWXRBWHBuVmdBIiwgInBob25lX251bWJlciIsICIrMS0yMDItNTU1LTAxMDEiXQ~WyJCbndnQThSODAtYUJtUVpuUzlrSnZnIiwgInBob25lX251bWJlcl92ZXJpZmllZCIsIHRydWVd~WyJhOVA3bENDZHVnNEZXWG95b21FWHZ3IiwgImFkZHJlc3MiLCB7ImNvdW50cnkiOiAiVVMiLCAibG9jYWxpdHkiOiAiQW55dG93biIsICJyZWdpb24iOiAiQW55c3RhdGUiLCAic3RyZWV0X2FkZHJlc3MiOiAiMTIzIE1haW4gU3QifV0~WyJJcURNYkUzeEtVOXAtOXlRTDVKN01BIiwgImJpcnRoZGF0ZSIsICIxOTQwLTAxLTAxIl0~"
]
}
EOF
cat << EOF | zexe foreach_schema.zen foreach_schema.data
Scenario 'sd_jwt' : sd_jwt

Given I have a 'signed_selective_disclosure array' named 'arr'

Foreach 'x' in 'arr'
When I rename 'x' to 'res'
and I break the foreach
Endforeach

Then print the 'res'
EOF
save_output foreach_schema.out
assert_output '{"res":"eyJhbGciOiAiRVMyNTYiLCAidHlwIjogInZjK3NkLWp3dCJ9.eyJfc2QiOiBbInZucGt1cWZBSWFBTlBmZXl6WXhUVllWUGxJY3JBWlVvU3N5TGFhQ0tlWUkiLCAiM1BEeUhOMXphcklJMG1TdDUwMkV2ZVIwVHhlOHlTQ1hDOFlYd1NvV1lZWSIsICJFMS12Wnl1Wmhlbkhlam1nYi1kTFhtaDBPODFLTUtWU25RYjN2Mjl6SGlRIl0sICJfc2RfYWxnIjogInNoYS0yNTYiLCAiaXNzIjogImh0dHA6Ly9leGFtcGxlLm9yZyIsICJzdWIiOiAidXNlciA0MiJ9.-BY5L0dcz2p-nCIhmL_0RK5QjzmKOI45E7anmZqg0Vct16lyF2_em3R8GzewmcrVT-NnZaT6EKrO79F4VGkFeQ~WyJ1eURGMUgwQVlSYmI0TVFubUx2dmVBIiwgImdpdmVuX25hbWUiLCAiSm9obiJd~WyJWam9zM2ZoOFZVU3Z2NHVYUVhuSWlRIiwgImZhbWlseV9uYW1lIiwgIkRvZSJd~WyIzV2JpbGZtNk1rdDFBUzlERXFtOS1nIiwgImVtYWlsIiwgImpvaG5kb2VAZXhhbXBsZS5jb20iXQ~"}'
}
Loading