From fb18629916834024167b5853ed023314f77c8b0f Mon Sep 17 00:00:00 2001 From: giles Date: Fri, 24 Apr 2026 22:48:33 +0000 Subject: [PATCH] lua: parenthesized expressions truncate multi-return via new lua-paren AST node +2 tests --- lib/lua/parser.sx | 2 +- lib/lua/scoreboard.json | 50 ++++++++++++++++++++++------------------- lib/lua/scoreboard.md | 39 ++++++++++++++++---------------- lib/lua/test.sh | 12 +++++++++- lib/lua/transpile.sx | 1 + plans/lua-on-sx.md | 1 + 6 files changed, 61 insertions(+), 44 deletions(-) diff --git a/lib/lua/parser.sx b/lib/lua/parser.sx index 7195ee65..bd84216e 100644 --- a/lib/lua/parser.sx +++ b/lib/lua/parser.sx @@ -281,7 +281,7 @@ ((at-op? "(") (begin (advance-tok!) - (set! base (parse-expr)) + (set! base (list (quote lua-paren) (parse-expr))) (consume! "op" ")"))) (else (error "lua-parse: expected prefixexp"))) (define diff --git a/lib/lua/scoreboard.json b/lib/lua/scoreboard.json index 6562b3bf..a3804036 100644 --- a/lib/lua/scoreboard.json +++ b/lib/lua/scoreboard.json @@ -1,8 +1,8 @@ { "totals": { "pass": 1, - "fail": 12, - "timeout": 3, + "fail": 11, + "timeout": 4, "skip": 8, "total": 24, "runnable": 16, @@ -11,16 +11,20 @@ "top_failure_modes": [ [ "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\", - 10 + 8 ], [ "timeout", - 3 + 4 ], [ "undefined symbol: fat\\", 1 ], + [ + "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"lua: attempt to compare incompat", + 1 + ], [ "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"lua: attempt to call non-functio", 1 @@ -43,19 +47,19 @@ "name": "attrib.lua", "status": "fail", "reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\", - "ms": 5845 + "ms": 6277 }, { "name": "big.lua", "status": "timeout", "reason": "per-test timeout", - "ms": 8006 + "ms": 8005 }, { "name": "calls.lua", "status": "fail", "reason": "undefined symbol: fat\\", - "ms": 5086 + "ms": 5170 }, { "name": "checktable.lua", @@ -67,7 +71,7 @@ "name": "closure.lua", "status": "timeout", "reason": "per-test timeout", - "ms": 8008 + "ms": 8004 }, { "name": "code.lua", @@ -78,8 +82,8 @@ { "name": "constructs.lua", "status": "fail", - "reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\", - "ms": 6732 + "reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"lua: attempt to compare incompat", + "ms": 7085 }, { "name": "db.lua", @@ -91,13 +95,13 @@ "name": "errors.lua", "status": "fail", "reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\", - "ms": 3147 + "ms": 3327 }, { "name": "events.lua", "status": "fail", "reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\", - "ms": 7692 + "ms": 7865 }, { "name": "files.lua", @@ -115,13 +119,13 @@ "name": "literals.lua", "status": "fail", "reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\", - "ms": 2222 + "ms": 2110 }, { "name": "locals.lua", "status": "fail", "reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"lua: attempt to call non-functio", - "ms": 1778 + "ms": 1869 }, { "name": "main.lua", @@ -133,43 +137,43 @@ "name": "math.lua", "status": "fail", "reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\", - "ms": 4400 + "ms": 4658 }, { "name": "nextvar.lua", - "status": "fail", - "reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\", - "ms": 7462 + "status": "timeout", + "reason": "per-test timeout", + "ms": 8007 }, { "name": "pm.lua", "status": "fail", "reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\", - "ms": 6198 + "ms": 7287 }, { "name": "sort.lua", "status": "timeout", "reason": "per-test timeout", - "ms": 8006 + "ms": 8007 }, { "name": "strings.lua", "status": "fail", "reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\", - "ms": 4095 + "ms": 4566 }, { "name": "vararg.lua", "status": "fail", "reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\", - "ms": 2177 + "ms": 2698 }, { "name": "verybig.lua", "status": "pass", "reason": "", - "ms": 574 + "ms": 666 } ] } \ No newline at end of file diff --git a/lib/lua/scoreboard.md b/lib/lua/scoreboard.md index 90d2247b..4f197a5d 100644 --- a/lib/lua/scoreboard.md +++ b/lib/lua/scoreboard.md @@ -1,13 +1,14 @@ # Lua-on-SX conformance scoreboard **Pass rate:** 1/16 runnable (6.2%) -fail=12 timeout=3 skip=8 total=24 +fail=11 timeout=4 skip=8 total=24 ## Top failure modes -- **10x** other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ -- **3x** timeout +- **8x** other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ +- **4x** timeout - **1x** undefined symbol: fat\ +- **1x** other: Unhandled exception: \"Unhandled exception: \\\"lua: attempt to compare incompat - **1x** other: Unhandled exception: \"Unhandled exception: \\\"lua: attempt to call non-functio ## Per-test results @@ -16,25 +17,25 @@ fail=12 timeout=3 skip=8 total=24 |---|---|---|---:| | all.lua | skip | driver uses dofile to chain other tests | 0 | | api.lua | skip | requires testC (C debug library) | 0 | -| attrib.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 5845 | -| big.lua | timeout | per-test timeout | 8006 | -| calls.lua | fail | undefined symbol: fat\ | 5086 | +| attrib.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 6277 | +| big.lua | timeout | per-test timeout | 8005 | +| calls.lua | fail | undefined symbol: fat\ | 5170 | | checktable.lua | skip | internal debug helpers | 0 | -| closure.lua | timeout | per-test timeout | 8008 | +| closure.lua | timeout | per-test timeout | 8004 | | code.lua | skip | bytecode inspection via debug library | 0 | -| constructs.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 6732 | +| constructs.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"lua: attempt to compare incompat | 7085 | | db.lua | skip | debug library | 0 | -| errors.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 3147 | -| events.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 7692 | +| errors.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 3327 | +| events.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 7865 | | files.lua | skip | io library | 0 | | gc.lua | skip | collectgarbage / finalisers | 0 | -| literals.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 2222 | -| locals.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"lua: attempt to call non-functio | 1778 | +| literals.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 2110 | +| locals.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"lua: attempt to call non-functio | 1869 | | main.lua | skip | standalone interpreter driver | 0 | -| math.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 4400 | -| nextvar.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 7462 | -| pm.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 6198 | -| sort.lua | timeout | per-test timeout | 8006 | -| strings.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 4095 | -| vararg.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 2177 | -| verybig.lua | pass | - | 574 | +| math.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 4658 | +| nextvar.lua | timeout | per-test timeout | 8007 | +| pm.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 7287 | +| sort.lua | timeout | per-test timeout | 8007 | +| strings.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 4566 | +| vararg.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 2698 | +| verybig.lua | pass | - | 666 | diff --git a/lib/lua/test.sh b/lib/lua/test.sh index db744837..8ef4ec8f 100755 --- a/lib/lua/test.sh +++ b/lib/lua/test.sh @@ -958,6 +958,12 @@ cat > "$TMPFILE" << 'EPOCHS' (epoch 1870) (eval "(lua-eval-ast \"local a = {x=0} function a:add(x) self.x = self.x+x return self end return a:add(10):add(20):add(30).x\")") +;; ── Parenthesized expression truncates multi-return ────────── +(epoch 1880) +(eval "(lua-eval-ast \"local function f() return 1, 2, 3 end local a, b, c = (f()) return (a == 1 and b == nil and c == nil) and 1 or 0\")") +(epoch 1881) +(eval "(lua-eval-ast \"local function f() return 10, 20 end return (f()) + 1\")") + EPOCHS OUTPUT=$(timeout 60 "$SX_SERVER" < "$TMPFILE" 2>/dev/null) @@ -1077,7 +1083,7 @@ check 213 "parse and/or prec" '(lua-binop "or" (lua-binop "and"' check 214 "parse ==" '(lua-binop "==" (lua-name "a") (lua-name "b"))' check 215 "parse .. right-assoc" '(lua-binop ".." (lua-name "a") (lua-binop ".."' check 216 "parse ^ right-assoc" '(lua-binop "^" (lua-name "a") (lua-binop "^"' -check 217 "parse paren override" '(lua-binop "*" (lua-binop "+"' +check 217 "parse paren override" '(lua-binop "*" (lua-paren (lua-binop "+"' check 220 "parse -x" '(lua-unop "-" (lua-name "x"))' check 221 "parse not x" '(lua-unop "not" (lua-name "x"))' @@ -1450,6 +1456,10 @@ check 1863 "break in repeat" '3' # ── Method-call chaining ───────────────────────────────────── check 1870 "a:add():add():add().x chain" '60' +# ── Parenthesized truncates multi-return ───────────────────── +check 1880 "(f()) scalar coerce" '1' +check 1881 "(f()) + 1 scalar" '11' + TOTAL=$((PASS + FAIL)) if [ $FAIL -eq 0 ]; then echo "ok $PASS/$TOTAL Lua-on-SX tests passed" diff --git a/lib/lua/transpile.sx b/lib/lua/transpile.sx index a3e6e00f..424cb2e5 100644 --- a/lib/lua/transpile.sx +++ b/lib/lua/transpile.sx @@ -30,6 +30,7 @@ ((= tag (quote lua-false)) false) ((= tag (quote lua-name)) (make-symbol (nth node 1))) ((= tag (quote lua-vararg)) (make-symbol "__varargs")) + ((= tag (quote lua-paren)) (list (make-symbol "lua-first") (lua-tx (nth node 1)))) ((= tag (quote lua-binop)) (lua-tx-binop node)) ((= tag (quote lua-unop)) (lua-tx-unop node)) ((= tag (quote lua-call)) (lua-tx-call node)) diff --git a/plans/lua-on-sx.md b/plans/lua-on-sx.md index 26f23697..5f40fe78 100644 --- a/plans/lua-on-sx.md +++ b/plans/lua-on-sx.md @@ -82,6 +82,7 @@ Each item: implement → tests → tick box → update progress log. _Newest first. Agent appends on every commit._ +- 2026-04-24: lua: scoreboard iteration — **parenthesized expressions truncate multi-return** (Lua spec: `(f())` forces single value even if `f` returns multi). Parser wraps `(expr)` in a new `lua-paren` AST node; transpile emits `(lua-first inner)`. Fixes `constructs.lua`@30 (`a,b,c = (f())` expects `a=1, b=nil, c=nil`) and `math.lua`@13. 375/375 green (+2 paren tests). Scoreboard: 8× asserts (was 10). - 2026-04-24: lua: scoreboard iteration — stripped `(else (raise e))` from `lua-tx-loop-guard`. SX `guard` with `(else (raise e))` hangs in a loop (re-enters the same guard). Since unmatched sentinels fall through to the enclosing guard naturally, the else is unnecessary. Diagnosed `calls.lua` undefined-`fat`: `function fat(x)` defined at Lua top-level is scoped inside the SX top-level guard's scope; loadstring-captured closures don't see it via lexical env. Fix would require either dropping the top-level guard (breaking top-level `return`) or dynamic env access — deferred. - 2026-04-24: lua: scoreboard iteration — **method-call double-evaluation bug**. `lua-tx-method-call` emitted `(lua-call (lua-get OBJ name) OBJ args…)` which evaluated OBJ TWICE, so `a:add(10):add(20):add(30).x` computed `110` instead of `60` (side effects applied twice). Fixed by `(let ((__obj OBJ)) (lua-call (lua-get __obj name) __obj args…))`. 373/373 green (+1 chaining test). - 2026-04-24: lua: **🎉 FIRST PASSING PUC-Rio TEST — 1/16 runnable (6.2%)**. `verybig.lua` now passes: needed `io.output`/`io.input`/`io.stdout`/`io.stderr` stubs, made `os.remove` return `true` (test asserts on it), and added `dofile`/`loadfile` stubs. All cumulative fixes (returns/break/scoping/escapes/precedence/vararg/tonumber-trim) combined make this test's full happy path work end-to-end. 372 unit tests. Failure mix: 10× assertion / 4× timeout / 1× call-non-fn.