Skip to content

Commit 82bac3d

Browse files
committed
fix: forE iterates the half-open range [lo, hi)
PureScript/JS `forE lo hi f` runs over `[lo, hi)` — lo inclusive, hi exclusive (Effect.purs doc + JS `for (i = lo; i < hi; i++)`). Lua's numeric `for i = lo, hi` is inclusive of the upper bound, so the port ran one extra iteration at i = hi and never produced an empty range (`forE n n` ran once instead of zero times). Use `for i = lo, hi - 1`. Adds a Lua 5.1 regression guard (test/regression/effect.lua). Fixes purescript-lua/purescript-lua#78
1 parent 5c3fb38 commit 82bac3d

3 files changed

Lines changed: 83 additions & 1 deletion

File tree

scripts/test

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ set -euo pipefail
44
echo "Running regression tests..."
55

66
lua test/regression/uncurried.lua
7+
lua test/regression/effect.lua

src/Effect.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@ return {
33
bindE = (function(a) return function(f) return function() return f(a())() end end end),
44
untilE = (function(f) return function() while not f() do end end end),
55
whileE = (function(f) return function(a) return function() while f() do a() end end end end),
6-
forE = (function(lo) return function(hi) return function(f) return function() for i = lo, hi do f(i)() end end end end end),
6+
forE = (function(lo) return function(hi) return function(f) return function() for i = lo, hi - 1 do f(i)() end end end end end),
77
foreachE = (function(as) return function(f) return function() for i = 1, #as do f(as[i])() end end end end)
88
}

test/regression/effect.lua

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
-- Regression guard for the Lua 5.1 FFI of Effect (control flow).
2+
--
3+
-- #78 forE lo hi f iterates over the half-open range [lo, hi) — lo inclusive,
4+
-- hi EXCLUSIVE (per Effect.purs and the JS `for (i = lo; i < hi; i++)`). Lua's
5+
-- numeric `for i = lo, hi` is inclusive of the upper bound, so the naive port
6+
-- ran one extra iteration and never produced an empty range. The fix uses
7+
-- `for i = lo, hi - 1`.
8+
--
9+
-- `Effect a` here is a zero-arg thunk; `f :: Int -> Effect Unit` is a curried
10+
-- `function(i) return function() ... end end`.
11+
-- Run from the repo root: `lua test/regression/effect.lua`.
12+
local E = dofile("src/Effect.lua")
13+
14+
local failures = 0
15+
16+
local function check(name, cond, detail)
17+
if cond then
18+
print("ok - " .. name)
19+
else
20+
failures = failures + 1
21+
print("FAIL - " .. name .. ": " .. tostring(detail))
22+
end
23+
end
24+
25+
-- Run forE over [lo, hi) and return the list of indices it visited.
26+
local function visited(lo, hi)
27+
local seen = {}
28+
local function f(i) return function() seen[#seen + 1] = i end end
29+
E.forE(lo)(hi)(f)()
30+
return seen
31+
end
32+
33+
local function showList(t)
34+
local parts = {}
35+
for i = 1, #t do parts[i] = tostring(t[i]) end
36+
return "{" .. table.concat(parts, ", ") .. "}"
37+
end
38+
39+
local function eqList(a, b)
40+
if #a ~= #b then return false end
41+
for i = 1, #a do if a[i] ~= b[i] then return false end end
42+
return true
43+
end
44+
45+
--------------------------------------------------------------------------------
46+
-- #78 forE iterates over [lo, hi) (hi exclusive) -----------------------------
47+
48+
do
49+
local got = visited(0, 3)
50+
check("forE 0 3 visits 0,1,2 (hi exclusive)", eqList(got, {0, 1, 2}), "got " .. showList(got))
51+
end
52+
53+
do
54+
local got = visited(1, 5)
55+
check("forE 1 5 visits 1,2,3,4", eqList(got, {1, 2, 3, 4}), "got " .. showList(got))
56+
end
57+
58+
do
59+
local got = visited(2, 2)
60+
check("forE n n is an empty range (zero iterations)", #got == 0, "got " .. showList(got))
61+
end
62+
63+
do
64+
local got = visited(5, 3)
65+
check("forE lo>hi is empty", #got == 0, "got " .. showList(got))
66+
end
67+
68+
--------------------------------------------------------------------------------
69+
-- foreachE iterates the whole array 1..#as (sanity: NOT off-by-one) ----------
70+
71+
do
72+
local seen = {}
73+
local function f(x) return function() seen[#seen + 1] = x end end
74+
E.foreachE({10, 20, 30})(f)()
75+
check("foreachE visits every element", eqList(seen, {10, 20, 30}), "got " .. showList(seen))
76+
end
77+
78+
--------------------------------------------------------------------------------
79+
80+
if failures > 0 then error(failures .. " regression check(s) failed") end
81+
print("purescript-lua-effect: all FFI regression checks passed")

0 commit comments

Comments
 (0)