diff --git a/src/lua/zencode.lua b/src/lua/zencode.lua index 3cd201391..93d276a53 100644 --- a/src/lua/zencode.lua +++ b/src/lua/zencode.lua @@ -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'}, diff --git a/src/lua/zencode_foreach.lua b/src/lua/zencode_foreach.lua index 1979adf3a..719b8efab 100644 --- a/src/lua/zencode_foreach.lua +++ b/src/lua/zencode_foreach.lua @@ -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) @@ -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") @@ -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 @@ -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) diff --git a/test/zencode/foreach.bats b/test/zencode/foreach.bats index 87ed7fa0b..37ec1ae09 100644 --- a/test/zencode/foreach.bats +++ b/test/zencode/foreach.bats @@ -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~"}' +}