diff --git a/lib/lua/runtime.sx b/lib/lua/runtime.sx index c88dbcce..5efbf147 100644 --- a/lib/lua/runtime.sx +++ b/lib/lua/runtime.sx @@ -59,28 +59,29 @@ (define lua-arith (fn - (mm op a b) - (cond - ((and (= (type-of a) "number") (= (type-of b) "number")) - (lua-num-op op a b)) - ((and - (or (= (type-of a) "number") (= (type-of a) "string")) - (or (= (type-of b) "number") (= (type-of b) "string")) - (not (= (lua-to-number a) nil)) - (not (= (lua-to-number b) nil))) - (lua-num-op op a b)) - (else - (let - ((m (lua-get-mm a mm))) - (cond - ((not (= m nil)) (lua-first (m a b))) - (else - (let - ((m2 (lua-get-mm b mm))) - (if - (not (= m2 nil)) - (lua-first (m2 a b)) - (error (str "lua: arith on " (type-of a) " " op " " (type-of b)))))))))))) + (mm op a-in b-in) + (let ((a (lua-first a-in)) (b (lua-first b-in))) + (cond + ((and (= (type-of a) "number") (= (type-of b) "number")) + (lua-num-op op a b)) + ((and + (or (= (type-of a) "number") (= (type-of a) "string")) + (or (= (type-of b) "number") (= (type-of b) "string")) + (not (= (lua-to-number a) nil)) + (not (= (lua-to-number b) nil))) + (lua-num-op op a b)) + (else + (let + ((m (lua-get-mm a mm))) + (cond + ((not (= m nil)) (lua-first (m a b))) + (else + (let + ((m2 (lua-get-mm b mm))) + (if + (not (= m2 nil)) + (lua-first (m2 a b)) + (error (str "lua: arith on " (type-of a) " " op " " (type-of b))))))))))))) (define lua-add (fn (a b) (lua-arith "__add" "+" a b))) @@ -116,33 +117,35 @@ (define lua-concat-coerce (fn - (v) - (cond - ((= (type-of v) "string") v) - ((= (type-of v) "number") (str v)) - (else (error (str "lua: cannot concat " v)))))) + (v-in) + (let ((v (lua-first v-in))) + (cond + ((= (type-of v) "string") v) + ((= (type-of v) "number") (str v)) + (else (error (str "lua: cannot concat " v))))))) (define lua-concat (fn - (a b) - (cond - ((and - (or (= (type-of a) "string") (= (type-of a) "number")) - (or (= (type-of b) "string") (= (type-of b) "number"))) - (str (lua-concat-coerce a) (lua-concat-coerce b))) - (else - (let - ((m (lua-get-mm a "__concat"))) - (cond - ((not (= m nil)) (lua-first (m a b))) - (else - (let - ((m2 (lua-get-mm b "__concat"))) - (if - (not (= m2 nil)) - (lua-first (m2 a b)) - (error (str "lua: concat on " (type-of a) " and " (type-of b)))))))))))) + (a-in b-in) + (let ((a (lua-first a-in)) (b (lua-first b-in))) + (cond + ((and + (or (= (type-of a) "string") (= (type-of a) "number")) + (or (= (type-of b) "string") (= (type-of b) "number"))) + (str (lua-concat-coerce a) (lua-concat-coerce b))) + (else + (let + ((m (lua-get-mm a "__concat"))) + (cond + ((not (= m nil)) (lua-first (m a b))) + (else + (let + ((m2 (lua-get-mm b "__concat"))) + (if + (not (= m2 nil)) + (lua-first (m2 a b)) + (error (str "lua: concat on " (type-of a) " and " (type-of b))))))))))))) (define lua-eq @@ -1646,3 +1649,6 @@ (define lua-return-value (fn (e) (if (> (len e) 1) (nth e 1) nil))) + +(define lua-break-sentinel? + (fn (e) (and (= (type-of e) "list") (> (len e) 0) (= (first e) (quote lua-brk))))) diff --git a/lib/lua/scoreboard.json b/lib/lua/scoreboard.json index 4bbeb235..caa7b7d5 100644 --- a/lib/lua/scoreboard.json +++ b/lib/lua/scoreboard.json @@ -1,8 +1,8 @@ { "totals": { "pass": 0, - "fail": 13, - "timeout": 3, + "fail": 12, + "timeout": 4, "skip": 8, "total": 24, "runnable": 16, @@ -15,15 +15,11 @@ ], [ "timeout", - 3 + 4 ], [ "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"lua: attempt to call non-functio", 2 - ], - [ - "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"lua: concat on list and string\\\\", - 1 ] ], "results": [ @@ -43,19 +39,19 @@ "name": "attrib.lua", "status": "fail", "reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\", - "ms": 6225 + "ms": 6183 }, { "name": "big.lua", "status": "timeout", "reason": "per-test timeout", - "ms": 8008 + "ms": 8007 }, { "name": "calls.lua", "status": "fail", "reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\", - "ms": 5175 + "ms": 4986 }, { "name": "checktable.lua", @@ -67,7 +63,7 @@ "name": "closure.lua", "status": "timeout", "reason": "per-test timeout", - "ms": 8007 + "ms": 8008 }, { "name": "code.lua", @@ -77,9 +73,9 @@ }, { "name": "constructs.lua", - "status": "fail", - "reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"lua: concat on list and string\\\\", - "ms": 4874 + "status": "timeout", + "reason": "per-test timeout", + "ms": 8005 }, { "name": "db.lua", @@ -91,13 +87,13 @@ "name": "errors.lua", "status": "fail", "reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\", - "ms": 3289 + "ms": 3253 }, { "name": "events.lua", "status": "fail", "reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\", - "ms": 7546 + "ms": 7283 }, { "name": "files.lua", @@ -115,13 +111,13 @@ "name": "literals.lua", "status": "fail", "reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\", - "ms": 1888 + "ms": 1857 }, { "name": "locals.lua", "status": "fail", "reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"lua: attempt to call non-functio", - "ms": 1721 + "ms": 1726 }, { "name": "main.lua", @@ -133,43 +129,43 @@ "name": "math.lua", "status": "fail", "reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\", - "ms": 4231 + "ms": 4173 }, { "name": "nextvar.lua", "status": "fail", "reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\", - "ms": 7290 + "ms": 7128 }, { "name": "pm.lua", "status": "fail", "reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\", - "ms": 6155 + "ms": 6098 }, { "name": "sort.lua", "status": "timeout", "reason": "per-test timeout", - "ms": 8006 + "ms": 8008 }, { "name": "strings.lua", "status": "fail", "reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\", - "ms": 4215 + "ms": 4137 }, { "name": "vararg.lua", "status": "fail", "reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\", - "ms": 2288 + "ms": 2264 }, { "name": "verybig.lua", "status": "fail", "reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"lua: attempt to call non-functio", - "ms": 622 + "ms": 575 } ] } \ No newline at end of file diff --git a/lib/lua/scoreboard.md b/lib/lua/scoreboard.md index f04e00bb..44bbfda4 100644 --- a/lib/lua/scoreboard.md +++ b/lib/lua/scoreboard.md @@ -1,14 +1,13 @@ # Lua-on-SX conformance scoreboard **Pass rate:** 0/16 runnable (0.0%) -fail=13 timeout=3 skip=8 total=24 +fail=12 timeout=4 skip=8 total=24 ## Top failure modes - **10x** other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ -- **3x** timeout +- **4x** timeout - **2x** other: Unhandled exception: \"Unhandled exception: \\\"lua: attempt to call non-functio -- **1x** other: Unhandled exception: \"Unhandled exception: \\\"lua: concat on list and string\\ ## Per-test results @@ -16,25 +15,25 @@ fail=13 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!\\\"\ | 6225 | -| big.lua | timeout | per-test timeout | 8008 | -| calls.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 5175 | +| attrib.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 6183 | +| big.lua | timeout | per-test timeout | 8007 | +| calls.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 4986 | | checktable.lua | skip | internal debug helpers | 0 | -| closure.lua | timeout | per-test timeout | 8007 | +| closure.lua | timeout | per-test timeout | 8008 | | code.lua | skip | bytecode inspection via debug library | 0 | -| constructs.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"lua: concat on list and string\\ | 4874 | +| constructs.lua | timeout | per-test timeout | 8005 | | db.lua | skip | debug library | 0 | -| errors.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 3289 | -| events.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 7546 | +| errors.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 3253 | +| events.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 7283 | | files.lua | skip | io library | 0 | | gc.lua | skip | collectgarbage / finalisers | 0 | -| literals.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 1888 | -| locals.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"lua: attempt to call non-functio | 1721 | +| literals.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 1857 | +| locals.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"lua: attempt to call non-functio | 1726 | | main.lua | skip | standalone interpreter driver | 0 | -| math.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 4231 | -| nextvar.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 7290 | -| pm.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 6155 | -| sort.lua | timeout | per-test timeout | 8006 | -| strings.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 4215 | -| vararg.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 2288 | -| verybig.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"lua: attempt to call non-functio | 622 | +| math.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 4173 | +| nextvar.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 7128 | +| pm.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 6098 | +| sort.lua | timeout | per-test timeout | 8008 | +| strings.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 4137 | +| vararg.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 2264 | +| verybig.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"lua: attempt to call non-functio | 575 | diff --git a/lib/lua/test.sh b/lib/lua/test.sh index d4f58133..8b427201 100755 --- a/lib/lua/test.sh +++ b/lib/lua/test.sh @@ -944,6 +944,16 @@ cat > "$TMPFILE" << 'EPOCHS' (epoch 1852) (eval "(lua-eval-ast \"function f(i) if type(i) ~= \\\"number\\\" then return i, \\\"jojo\\\" end if i > 0 then return i, f(i-1) end end local a, b = f(3) return a\")") +;; ── break via sentinel (escapes while/for-num/for-in/repeat) ── +(epoch 1860) +(eval "(lua-eval-ast \"local i = 0 while true do i = i + 1 if i >= 5 then break end end return i\")") +(epoch 1861) +(eval "(lua-eval-ast \"local s = 0 for i = 1, 100 do if i > 10 then break end s = s + i end return s\")") +(epoch 1862) +(eval "(lua-eval-ast \"local t = {10, 20, 99, 40} local s = 0 for i, v in ipairs(t) do if v == 99 then break end s = s + v end return s\")") +(epoch 1863) +(eval "(lua-eval-ast \"local i = 0 repeat i = i + 1 if i >= 3 then break end until false return i\")") + EPOCHS OUTPUT=$(timeout 60 "$SX_SERVER" < "$TMPFILE" 2>/dev/null) @@ -1427,6 +1437,12 @@ check 1850 "early return negative path" '-1' check 1851 "non-early return path" '14' check 1852 "nested early-return recursion" '3' +# ── break via sentinel ─────────────────────────────────────── +check 1860 "break in while" '5' +check 1861 "break in for-num" '55' +check 1862 "break in for-in" '30' +check 1863 "break in repeat" '3' + 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 c06824e1..ab7c60fa 100644 --- a/lib/lua/transpile.sx +++ b/lib/lua/transpile.sx @@ -1,3 +1,17 @@ +(define + lua-tx-loop-guard + (fn (body-sx) + (list + (make-symbol "guard") + (list (make-symbol "e") + (list + (list (make-symbol "lua-break-sentinel?") (make-symbol "e")) + nil) + (list + (make-symbol "else") + (list (make-symbol "raise") (make-symbol "e")))) + body-sx))) + (define lua-tx (fn @@ -36,7 +50,7 @@ ((= tag (quote lua-for-num)) (lua-tx-for-num node)) ((= tag (quote lua-for-in)) (lua-tx-for-in node)) ((= tag (quote lua-do)) (lua-tx-do node)) - ((= tag (quote lua-break)) (quote lua-break-marker)) + ((= tag (quote lua-break)) (list (make-symbol "raise") (list (make-symbol "list") (list (make-symbol "quote") (make-symbol "lua-brk"))))) ((= tag (quote lua-return)) (lua-tx-return node)) ((= tag (quote lua-call-stmt)) (lua-tx (nth node 1))) ((= tag (quote lua-local-function)) (lua-tx-local-function node)) @@ -392,7 +406,7 @@ (make-symbol "begin") body (list (make-symbol "_while_loop")))))) - (list (make-symbol "_while_loop")))))) + (lua-tx-loop-guard (list (make-symbol "_while_loop"))))))) (define lua-tx-repeat @@ -418,7 +432,7 @@ (make-symbol "not") (list (make-symbol "lua-truthy?") cnd)) (list (make-symbol "_repeat_loop")))))) - (list (make-symbol "_repeat_loop")))))) + (lua-tx-loop-guard (list (make-symbol "_repeat_loop"))))))) (define lua-tx-for-num @@ -462,7 +476,7 @@ (make-symbol name) (make-symbol "_for_step"))) (list (make-symbol "_for_loop")))))) - (list (make-symbol "_for_loop"))))))) + (lua-tx-loop-guard (list (make-symbol "_for_loop")))))))) (define lua-tx-do (fn (node) (list (make-symbol "let") (list) (lua-tx (nth node 1))))) @@ -704,4 +718,4 @@ (make-symbol "fn") (list) (lua-tx-for-in-loop-body names body f-sym s-sym v-sym rets-sym loop-sym first-name))) - (list loop-sym)))))))))) + (lua-tx-loop-guard (list loop-sym))))))))))) diff --git a/plans/lua-on-sx.md b/plans/lua-on-sx.md index 4a6da9fd..72c5946f 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 — **proper `break` via guard+raise sentinel** (`lua-brk`) + auto-first multi-values in arith/concat. Loop break dispatch was previously a no-op (emitted bare `'lua-break-marker` symbol that nothing caught); converted to raise+catch pattern, wrapping the OUTER invocation of `_while_loop`/`_for_loop`/`_repeat_loop`/`__for_loop` in a break-guard (wrapping body doesn't work — break would just be caught and loop keeps recursing). Also `lua-arith`/`lua-concat`/`lua-concat-coerce` now `lua-first` their operands so multi-returns auto-truncate at scalar boundaries. 372/372 green (+4 break tests). Scoreboard: 10×assert / 4×timeout / 2×call-non-fn (no more undef-symbol or compare-incompat). - 2026-04-24: lua: scoreboard iteration — **proper early-return via guard+raise sentinel**. Fixes long-logged limitation: `if cond then return X end ...rest` now exits the enclosing function; `rest` is skipped. `lua-tx-return` raises `(list 'lua-ret value)`; every function body and the top-level chunk + loadstring'd chunks wrap in a guard that catches the sentinel and returns its value. Eliminates "compare incompatible types" from constructs.lua (past line 40). 368/368 green (+3 early-return tests). - 2026-04-24: lua: scoreboard iteration — **unary-minus / `^` precedence fix**. Per Lua spec, `^` binds tighter than unary `-`, so `-2^2` should parse as `-(2^2) = -4`, not `(-2)^2 = 4`. My parser recursed into `parse-unary` and then let `^` bind to the already-negated operand. Added `parse-pow-chain` helper and changed the `else` branch of `parse-unary` to parse a primary + `^`-chain before returning; unary operators now wrap the full `^`-chain. Fixed `constructs.lua` past assert #3 (moved to compare-incompatible). 365/365 green (+3 precedence tests). - 2026-04-24: lua: scoreboard iteration — `lua-byte-to-char` regression fix. My previous change returned 2-char strings (`"\a"` etc.) for bytes that SX string literals can't express (0, 7, 8, 11, 12, 14–31, 127+), breaking `'a\0a'` length from 3 → 4. Now only 9/10/13 and printable 32-126 produce real bytes; others use a single `"?"` placeholder so `string.len` stays correct. literals.lua back to failing at assert #4 (was regressed to #2).