From e224fb2db01ab6cca4dce4ed0dedb0dadb81349c Mon Sep 17 00:00:00 2001 From: giles Date: Fri, 24 Apr 2026 18:05:35 +0000 Subject: [PATCH] lua: pcall/xpcall/error via guard+raise; arity-dispatch lua-apply +9 tests --- lib/lua/runtime.sx | 52 ++++++++++++++++++++++++++++++++++++++++++++-- lib/lua/test.sh | 33 +++++++++++++++++++++++++++++ plans/lua-on-sx.md | 3 ++- 3 files changed, 85 insertions(+), 3 deletions(-) diff --git a/lib/lua/runtime.sx b/lib/lua/runtime.sx index 445852b2..6a6f2fc7 100644 --- a/lib/lua/runtime.sx +++ b/lib/lua/runtime.sx @@ -384,6 +384,24 @@ (define sx-apply-ref apply) +(define + lua-apply + (fn + (f rargs) + (let + ((n (len rargs))) + (cond + ((= n 0) (f)) + ((= n 1) (f (nth rargs 0))) + ((= n 2) (f (nth rargs 0) (nth rargs 1))) + ((= n 3) (f (nth rargs 0) (nth rargs 1) (nth rargs 2))) + ((= n 4) (f (nth rargs 0) (nth rargs 1) (nth rargs 2) (nth rargs 3))) + ((= n 5) (f (nth rargs 0) (nth rargs 1) (nth rargs 2) (nth rargs 3) (nth rargs 4))) + ((= n 6) (f (nth rargs 0) (nth rargs 1) (nth rargs 2) (nth rargs 3) (nth rargs 4) (nth rargs 5))) + ((= n 7) (f (nth rargs 0) (nth rargs 1) (nth rargs 2) (nth rargs 3) (nth rargs 4) (nth rargs 5) (nth rargs 6))) + ((= n 8) (f (nth rargs 0) (nth rargs 1) (nth rargs 2) (nth rargs 3) (nth rargs 4) (nth rargs 5) (nth rargs 6) (nth rargs 7))) + (else (sx-apply-ref f rargs)))))) + (define lua-call (fn @@ -392,11 +410,41 @@ ((f (first args)) (rargs (rest args))) (cond ((or (= (type-of f) "function") (= (type-of f) "lambda")) - (sx-apply-ref f rargs)) + (lua-apply f rargs)) ((= (type-of f) "dict") (let ((m (lua-get-mm f "__call"))) (cond ((= m nil) (error "lua: attempt to call non-function")) - (else (sx-apply-ref m (cons f rargs)))))) + (else (lua-apply m (cons f rargs)))))) (else (error "lua: attempt to call non-function")))))) + +(define lua-error (fn (&rest args) (raise (first args)))) + +(define error lua-error) + +(define + pcall + (fn + (&rest args) + (let + ((f (first args)) (rargs (rest args))) + (guard + (e (true (list (quote lua-multi) false e))) + (let + ((r (lua-apply f rargs))) + (cond + ((lua-multi? r) (cons (quote lua-multi) (cons true (rest r)))) + (else (list (quote lua-multi) true r)))))))) + +(define + xpcall + (fn + (f msgh) + (guard + (e (true (list (quote lua-multi) false (lua-first (lua-apply msgh (list e)))))) + (let + ((r (lua-apply f (list)))) + (cond + ((lua-multi? r) (cons (quote lua-multi) (cons true (rest r)))) + (else (list (quote lua-multi) true r))))))) diff --git a/lib/lua/test.sh b/lib/lua/test.sh index f75e0262..e390bf6b 100755 --- a/lib/lua/test.sh +++ b/lib/lua/test.sh @@ -659,6 +659,28 @@ cat > "$TMPFILE" << 'EPOCHS' (epoch 880) (eval "(lua-eval-ast \"local Animal = {} Animal.__index = Animal function Animal:new(name) local o = setmetatable({name = name}, self) return o end function Animal:getName() return self.name end local a = Animal:new(\\\"Rex\\\") return a:getName()\")") +;; ── Phase 4: pcall / xpcall / error ──────────────────────────── +(epoch 900) +(eval "(lua-eval-ast \"local ok, err = pcall(function() error(\\\"boom\\\") end) if ok then return 0 else return err end\")") +(epoch 901) +(eval "(lua-eval-ast \"local ok, v = pcall(function() return 42 end) if ok then return v else return -1 end\")") +(epoch 902) +(eval "(lua-eval-ast \"local ok, a, b = pcall(function() return 1, 2 end) return (ok and a + b) or 0\")") +(epoch 903) +(eval "(lua-eval-ast \"local function div(a, b) if b == 0 then error(\\\"div by zero\\\") end return a / b end local ok, r = pcall(div, 10, 2) if ok then return r else return -1 end\")") +(epoch 904) +(eval "(lua-eval-ast \"local function div(a, b) if b == 0 then error(\\\"div by zero\\\") end return a / b end local ok, e = pcall(div, 10, 0) if ok then return \\\"no\\\" else return e end\")") +(epoch 905) +(eval "(lua-eval-ast \"local function f() error({code = 42}) end local ok, err = pcall(f) return err.code\")") +(epoch 906) +(eval "(lua-eval-ast \"local ok1, e1 = pcall(function() local ok2, e2 = pcall(function() error(\\\"inner\\\") end) if not ok2 then error(\\\"outer:\\\" .. e2) end end) return e1\")") + +;; xpcall +(epoch 910) +(eval "(lua-eval-ast \"local function f() error(\\\"raw\\\") end local function handler(e) return \\\"H:\\\" .. e end local ok, v = xpcall(f, handler) if ok then return \\\"no\\\" else return v end\")") +(epoch 911) +(eval "(lua-eval-ast \"local function f() return 99 end local function handler(e) return e end local ok, v = xpcall(f, handler) return v\")") + EPOCHS OUTPUT=$(timeout 60 "$SX_SERVER" < "$TMPFILE" 2>/dev/null) @@ -989,6 +1011,17 @@ check 860 "__call" '105' check 870 "__len" '99' check 880 "OO pattern self:m()" '"Rex"' +# ── Phase 4: pcall / xpcall / error ─────────────────────────── +check 900 "pcall catches error(msg)" '"boom"' +check 901 "pcall ok path single val" '42' +check 902 "pcall ok path multi val" '3' +check 903 "pcall with args, ok" '5' +check 904 "pcall with args, err" '"div by zero"' +check 905 "error(table) preserved" '42' +check 906 "nested pcall" '"outer:inner"' +check 910 "xpcall invokes handler" '"H:raw"' +check 911 "xpcall ok path" '99' + TOTAL=$((PASS + FAIL)) if [ $FAIL -eq 0 ]; then echo "ok $PASS/$TOTAL Lua-on-SX tests passed" diff --git a/plans/lua-on-sx.md b/plans/lua-on-sx.md index a99eb41b..fc3766b4 100644 --- a/plans/lua-on-sx.md +++ b/plans/lua-on-sx.md @@ -61,7 +61,7 @@ Each item: implement → tests → tick box → update progress log. ### Phase 4 — metatables + error handling (next run) - [x] Metatable dispatch: `__index`, `__newindex`, `__add`/`__sub`/…, `__eq`, `__lt`, `__call`, `__tostring`, `__len` -- [ ] `pcall`/`xpcall`/`error` via handler-bind +- [x] `pcall`/`xpcall`/`error` via handler-bind - [ ] Generic `for … in …` ### Phase 5 — coroutines (the showcase) @@ -82,6 +82,7 @@ Each item: implement → tests → tick box → update progress log. _Newest first. Agent appends on every commit._ +- 2026-04-24: lua: `pcall`/`xpcall`/`error` via SX `guard` + `raise`. Added `lua-apply` (arity-dispatch 0-8, apply fallback) because SX `apply` re-wraps raises as "Unhandled exception". Table payloads preserved (`error({code = 42})`). 256 total tests. - 2026-04-24: lua: phase 4 — metatable dispatch (`__index`/`__newindex`/arith/compare/`__call`/`__len`), `setmetatable`/`getmetatable`/`type` globals, OO `self:method` pattern. Transpile routes all calls through `lua-call` (stashed `sx-apply-ref` to dodge user-shadowing of SX `apply`). Skipped `__tostring` (needs `tostring()` builtin). 247 total tests. - 2026-04-24: lua: PUC-Rio scoreboard baseline — 0/16 runnable pass (0.0%). Top modes: 14× parse error, 1× `print` undef, 1× vararg transpile. Phase 3 complete. - 2026-04-24: lua: conformance runner — `conformance.sh` shim + `conformance.py` (long-lived sx_server, epoch protocol, classify_error, writes scoreboard.{json,md}). 24 files classified in full run: 8 skip / 16 fail / 0 timeout.