From a1901711d83049c81a683ebad564f37d170a1aa5 Mon Sep 17 00:00:00 2001 From: matteo-cristino Date: Wed, 8 Jan 2025 18:22:29 +0100 Subject: [PATCH] feat: add support for negative steps in foreach --- src/lua/zencode_foreach.lua | 44 +++++++++++++++++++------- test/zencode/foreach.bats | 63 +++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+), 11 deletions(-) diff --git a/src/lua/zencode_foreach.lua b/src/lua/zencode_foreach.lua index a2aaa6083..578c527fe 100644 --- a/src/lua/zencode_foreach.lua +++ b/src/lua/zencode_foreach.lua @@ -56,28 +56,50 @@ Foreach("'' in sequence from '' to '' with step ''", function(name, from_name, t if type(from) == 'zenroom.big' then -- only on first iteration: do checks and save usefull values if info.pos == 1 then - zencode_assert(BIG.zenpositive(step) and step ~= BIG.new(0), "only positive step is supported") - zencode_assert(BIG.zenpositive(BIG.zensub(to, from)), "end of foreach must be bigger than the start") - info.to_plus_1 = BIG.zenadd(to, BIG.new(1)) -- store to avoid repeating at each step + zencode_assert(step ~= BIG.new(0), "zero step is not supported") + local range_size = BIG.zensub(to, from) + if BIG.zenpositive(step) then + zencode_assert(BIG.zenpositive(range_size) or range_size == BIG.new(0), "end of foreach must be bigger than the start for postive step") + info.to_plus_1 = BIG.zenadd(to, BIG.new(1)) + -- current_value > to + -- current_value >= to + 1 + -- (current_value - (to+1)) >= 0 + info.check_fn = function () + local diff = BIG.zensub(info.cv, info.to_plus_1) + return BIG.zenpositive(diff) or diff == BIG.new(0) + end + else + zencode_assert(not BIG.zenpositive(range_size) or range_size == BIG.new(0), "end of foreach must be smaller than the start for negative step") + info.to_minus_1 = BIG.zensub(to, BIG.new(1)) + -- current_value < to + -- current_value <= to - 1 + -- (current_value - (to-1)) <= 0 + info.check_fn = function() + local diff = BIG.zensub(info.cv, info.to_minus_1) + return not BIG.zenpositive(diff) or diff == BIG.new(0) + end + end info.cv = from else info.cv = BIG.zenadd(info.cv, step) end - -- current_value > to - -- current_value >= to + 1 - -- (current_value - (to+1)) >= 0 - local diff = BIG.zensub(info.cv, info.to_plus_1) - finished = BIG.zenpositive(diff) or diff == BIG.new(0) + finished = info.check_fn() else -- only on first iteration: do checks and save usefull values if info.pos == 1 then - zencode_assert(step > F.new(0) and from < to, - "only positive step is supported") + zencode_assert(step ~= F.new(0), "zero step is not supported") + if step > F.new(0) then + zencode_assert(from <= to, "end of foreach must be bigger than the start for postive step") + info.check_fn = function() return info.cv > to end + else + zencode_assert(from >= to, "end of foreach must be smaller than the start for negative step") + info.check_fn = function() return info.cv < to end + end info.cv = from else info.cv = info.cv + step end - finished = info.cv > to + finished = info.check_fn() end if finished then diff --git a/test/zencode/foreach.bats b/test/zencode/foreach.bats index 4c33db94e..683ad878f 100644 --- a/test/zencode/foreach.bats +++ b/test/zencode/foreach.bats @@ -569,3 +569,66 @@ EOF run $ZENROOM_EXECUTABLE -c "maxiter=dec:10" -a maxiter.data.json -z maxiter_error_3.zen assert_line --partial 'Limit of iterations exceeded: 10' } + +@test "negative steps" { + cat << EOF | save_asset negative_steps.data.json +{ + "int_start": "1", + "int_end": "-10", + "int_step": "-1", + "num_start": 1, + "num_end": -10, + "num_step": -1 +} +EOF + cat << EOF | zexe negative_steps.zen negative_steps.data.json +Given I have a 'integer' named 'int_start' +Given I have a 'integer' named 'int_end' +Given I have a 'integer' named 'int_step' +Given I have a 'number' named 'num_start' +Given I have a 'number' named 'num_end' +Given I have a 'number' named 'num_step' + +# integer loop with negative step +When I create the 'integer array' named 'int_res' +Foreach 'i' in sequence from 'int_start' to 'int_end' with step 'int_step' + When I move 'i' in 'int_res' +EndForeach + +# integer loop with negative step that: +# * start and end in the same negative number +# * start and end in the same positive number +When I create the 'integer array' named 'int_res_corner_case' +Foreach 'i' in sequence from 'int_end' to 'int_end' with step 'int_step' + When I move 'i' in 'int_res_corner_case' +EndForeach +Foreach 'i' in sequence from 'int_start' to 'int_start' with step 'int_step' + When I move 'i' in 'int_res_corner_case' +EndForeach + +# number loop with negative step +When I create the 'integer array' named 'num_res' +Foreach 'i' in sequence from 'num_start' to 'num_end' with step 'num_step' + When I move 'i' in 'num_res' +EndForeach + +# number loop with negative step that: +# * start and end in the same negative number +# * start and end in the same positive number +When I create the 'integer array' named 'num_res_corner_case' +Foreach 'i' in sequence from 'num_end' to 'num_end' with step 'num_step' + When I move 'i' in 'num_res_corner_case' +EndForeach +Foreach 'i' in sequence from 'num_start' to 'num_start' with step 'num_step' + When I move 'i' in 'num_res_corner_case' +EndForeach + + +Then print the 'int_res' +Then print the 'num_res' +Then print the 'int_res_corner_case' +Then print the 'num_res_corner_case' +EOF + save_output negative_steps.out + assert_output '{"int_res":["1","0","-1","-2","-3","-4","-5","-6","-7","-8","-9","-10"],"int_res_corner_case":["-10","1"],"num_res":[1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10],"num_res_corner_case":[-10,1]}' +}