Skip to content

Commit

Permalink
feat: add support for negative steps in foreach
Browse files Browse the repository at this point in the history
  • Loading branch information
matteo-cristino authored and jaromil committed Jan 8, 2025
1 parent 5b50b0b commit a190171
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 11 deletions.
44 changes: 33 additions & 11 deletions src/lua/zencode_foreach.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
63 changes: 63 additions & 0 deletions test/zencode/foreach.bats
Original file line number Diff line number Diff line change
Expand Up @@ -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]}'
}

0 comments on commit a190171

Please sign in to comment.