lua: proper early-return via guard+raise sentinel; fixes if-then-return-end-rest +3 tests
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Has been cancelled

This commit is contained in:
2026-04-24 21:46:23 +00:00
parent b1bed8e0e5
commit 68b0a279f8
6 changed files with 104 additions and 60 deletions

View File

@@ -1503,9 +1503,15 @@
(guard
(e (true (list (quote lua-multi) nil (str e))))
(let ((compiled (lua-transpile src)))
(let ((wrapped (list (make-symbol "fn") (list (make-symbol "&rest") (make-symbol "__args"))
(list (make-symbol "let") (list) compiled))))
(eval-expr wrapped))))))
(let ((guarded (list
(make-symbol "guard")
(list (make-symbol "e")
(list
(list (make-symbol "lua-return-sentinel?") (make-symbol "e"))
(list (make-symbol "lua-return-value") (make-symbol "e"))))
(list (make-symbol "let") (list) compiled))))
(let ((wrapped (list (make-symbol "fn") (list (make-symbol "&rest") (make-symbol "__args")) guarded)))
(eval-expr wrapped)))))))
(define loadstring lua-loadstring)
(define load lua-loadstring)
@@ -1630,3 +1636,13 @@
(va-build skip)
(dict-set! t "n" n)
t))))
;; Return-sentinel: wrap function bodies so mid-block `return` can escape.
(define
lua-return-sentinel?
(fn (e)
(and (= (type-of e) "list") (> (len e) 0) (= (first e) (quote lua-ret)))))
(define
lua-return-value
(fn (e) (if (> (len e) 1) (nth e 1) nil)))

View File

@@ -11,7 +11,7 @@
"top_failure_modes": [
[
"other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\",
9
10
],
[
"timeout",
@@ -22,11 +22,7 @@
2
],
[
"other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"lua: attempt to compare incompat",
1
],
[
"other: Unhandled exception: \\\"Not callable: nil (kont=10 frames)\\",
"other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"lua: concat on list and string\\\\",
1
]
],
@@ -47,19 +43,19 @@
"name": "attrib.lua",
"status": "fail",
"reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\",
"ms": 6000
"ms": 6225
},
{
"name": "big.lua",
"status": "timeout",
"reason": "per-test timeout",
"ms": 8006
"ms": 8008
},
{
"name": "calls.lua",
"status": "fail",
"reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\",
"ms": 4946
"ms": 5175
},
{
"name": "checktable.lua",
@@ -71,7 +67,7 @@
"name": "closure.lua",
"status": "timeout",
"reason": "per-test timeout",
"ms": 8003
"ms": 8007
},
{
"name": "code.lua",
@@ -82,8 +78,8 @@
{
"name": "constructs.lua",
"status": "fail",
"reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"lua: attempt to compare incompat",
"ms": 4533
"reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"lua: concat on list and string\\\\",
"ms": 4874
},
{
"name": "db.lua",
@@ -94,14 +90,14 @@
{
"name": "errors.lua",
"status": "fail",
"reason": "other: Unhandled exception: \\\"Not callable: nil (kont=10 frames)\\",
"ms": 3217
"reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\",
"ms": 3289
},
{
"name": "events.lua",
"status": "fail",
"reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\",
"ms": 7215
"ms": 7546
},
{
"name": "files.lua",
@@ -119,13 +115,13 @@
"name": "literals.lua",
"status": "fail",
"reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\",
"ms": 1901
"ms": 1888
},
{
"name": "locals.lua",
"status": "fail",
"reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"lua: attempt to call non-functio",
"ms": 1693
"ms": 1721
},
{
"name": "main.lua",
@@ -137,43 +133,43 @@
"name": "math.lua",
"status": "fail",
"reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\",
"ms": 4095
"ms": 4231
},
{
"name": "nextvar.lua",
"status": "fail",
"reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\",
"ms": 7177
"ms": 7290
},
{
"name": "pm.lua",
"status": "fail",
"reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\",
"ms": 6048
"ms": 6155
},
{
"name": "sort.lua",
"status": "timeout",
"reason": "per-test timeout",
"ms": 8007
"ms": 8006
},
{
"name": "strings.lua",
"status": "fail",
"reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\",
"ms": 3990
"ms": 4215
},
{
"name": "vararg.lua",
"status": "fail",
"reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\",
"ms": 2249
"ms": 2288
},
{
"name": "verybig.lua",
"status": "fail",
"reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"lua: attempt to call non-functio",
"ms": 572
"ms": 622
}
]
}

View File

@@ -5,11 +5,10 @@ fail=13 timeout=3 skip=8 total=24
## Top failure modes
- **9x** other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\
- **10x** other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\
- **3x** timeout
- **2x** other: Unhandled exception: \"Unhandled exception: \\\"lua: attempt to call non-functio
- **1x** other: Unhandled exception: \"Unhandled exception: \\\"lua: attempt to compare incompat
- **1x** other: Unhandled exception: \"Not callable: nil (kont=10 frames)\
- **1x** other: Unhandled exception: \"Unhandled exception: \\\"lua: concat on list and string\\
## Per-test results
@@ -17,25 +16,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!\\\"\ | 6000 |
| big.lua | timeout | per-test timeout | 8006 |
| calls.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 4946 |
| 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 |
| checktable.lua | skip | internal debug helpers | 0 |
| closure.lua | timeout | per-test timeout | 8003 |
| closure.lua | timeout | per-test timeout | 8007 |
| code.lua | skip | bytecode inspection via debug library | 0 |
| constructs.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"lua: attempt to compare incompat | 4533 |
| constructs.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"lua: concat on list and string\\ | 4874 |
| db.lua | skip | debug library | 0 |
| errors.lua | fail | other: Unhandled exception: \"Not callable: nil (kont=10 frames)\ | 3217 |
| events.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 7215 |
| errors.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 3289 |
| events.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 7546 |
| files.lua | skip | io library | 0 |
| gc.lua | skip | collectgarbage / finalisers | 0 |
| literals.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 1901 |
| locals.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"lua: attempt to call non-functio | 1693 |
| 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 |
| main.lua | skip | standalone interpreter driver | 0 |
| math.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 4095 |
| nextvar.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 7177 |
| pm.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 6048 |
| sort.lua | timeout | per-test timeout | 8007 |
| strings.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 3990 |
| vararg.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 2249 |
| verybig.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"lua: attempt to call non-functio | 572 |
| 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 |

View File

@@ -936,6 +936,14 @@ cat > "$TMPFILE" << 'EPOCHS'
(epoch 1842)
(eval "(lua-eval-ast \"if -2^2 == -4 then return 1 else return 0 end\")")
;; ── Early-return inside nested block (guard+raise sentinel) ──
(epoch 1850)
(eval "(lua-eval-ast \"local function f(n) if n < 0 then return -1 end return n * 2 end return f(-5)\")")
(epoch 1851)
(eval "(lua-eval-ast \"local function f(n) if n < 0 then return -1 end return n * 2 end return f(7)\")")
(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\")")
EPOCHS
OUTPUT=$(timeout 60 "$SX_SERVER" < "$TMPFILE" 2>/dev/null)
@@ -1414,6 +1422,11 @@ check 1840 "-2^2 = -4" '-4'
check 1841 "2^3^2 = 512 (right-assoc)" '512'
check 1842 "-2^2 == -4 true" '1'
# ── Early-return inside nested block ─────────────────────────
check 1850 "early return negative path" '-1'
check 1851 "non-early return path" '14'
check 1852 "nested early-return recursion" '3'
TOTAL=$((PASS + FAIL))
if [ $FAIL -eq 0 ]; then
echo "ok $PASS/$TOTAL Lua-on-SX tests passed"

View File

@@ -187,6 +187,17 @@
(make-symbol "arg")
(list (make-symbol "lua-varargs-arg-table") (make-symbol "__args") n))))
(define
lua-tx-function-guard
(fn (body-sx)
(list
(make-symbol "guard")
(list (make-symbol "e")
(list
(list (make-symbol "lua-return-sentinel?") (make-symbol "e"))
(list (make-symbol "lua-return-value") (make-symbol "e"))))
body-sx)))
(define
lua-tx-function
(fn
@@ -200,7 +211,7 @@
(list
(make-symbol "fn")
(list (make-symbol "&rest") (make-symbol "__args"))
(lua-tx body)))
(lua-tx-function-guard (lua-tx body))))
(else
(let
((bindings (lua-tx-function-bindings params 0)))
@@ -215,10 +226,11 @@
(list
(make-symbol "fn")
(list (make-symbol "&rest") (make-symbol "__args"))
(list
(make-symbol "let")
all-bindings
(lua-tx body))))))))))
(lua-tx-function-guard
(list
(make-symbol "let")
all-bindings
(lua-tx body)))))))))))
(define
lua-tx-block
@@ -460,13 +472,18 @@
(node)
(let
((exps (nth node 1)))
(cond
((= (len exps) 0) nil)
((= (len exps) 1) (lua-tx (first exps)))
(else
(list
(make-symbol "lua-pack-return")
(cons (make-symbol "list") (lua-tx-multi-args exps 0))))))))
(let
((val
(cond
((= (len exps) 0) nil)
((= (len exps) 1) (lua-tx (first exps)))
(else
(list
(make-symbol "lua-pack-return")
(cons (make-symbol "list") (lua-tx-multi-args exps 0)))))))
(list
(make-symbol "raise")
(list (make-symbol "list") (list (make-symbol "quote") (make-symbol "lua-ret")) val))))))
(define
lua-tx-local-function
@@ -500,7 +517,9 @@
(define
lua-eval-ast
(fn (src) (let ((sx (lua-transpile src))) (eval-expr sx))))
(fn (src)
(let ((sx (lua-transpile src)))
(eval-expr (lua-tx-function-guard sx)))))
(define
lua-tx-multi-args

View File

@@ -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 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, 1431, 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).
- 2026-04-24: lua: scoreboard iteration — **decimal string escapes** `\ddd` (1-3 digits). Tokenizer `read-string` previously fell through to literal for digits, so `"\65"` came out as `"65"` not `"A"`. Added `read-decimal-escape!` consuming up to 3 digits while keeping value ≤255, plus `\a`/`\b`/`\f`/`\v` control escapes and `lua-byte-to-char` ASCII lookup. 362 tests (+2 escape tests).
@@ -133,4 +134,4 @@ _Shared-file issues that need someone else to fix. Minimal repro only._
- **`string.find`/`match`/`gmatch`/`gsub` patterns are LITERAL only** — no `%d`/`%a`/`.`/`*`/`+`/etc. Implementing Lua patterns is a separate work item; literal search covers the common case.
- **`string.format`** supports only `%s`, `%d`, `%f`, `%%`. No width/precision flags (`%.2f`, `%5d`).
- **`string.char`** supports printable ASCII 32126 plus `\t`/`\n`/`\r`; other codes error.
- **Early `return` inside nested block** — `if cond then return nil end ...rest` doesn't exit the enclosing function; `rest` runs anyway. Use `if cond then return X else return Y end` instead. Likely needs guard+raise sentinel for proper fix.
- ~~Early `return` inside nested block~~ — **FIXED 2026-04-24** via guard+raise sentinel (`lua-ret`). All function bodies and the top-level chunk wrap in a guard that catches the return-sentinel; `return` statements raise it.