From 8ca5c8052dede266b756904c61afdc30588a7335 Mon Sep 17 00:00:00 2001 From: giles Date: Sat, 25 Apr 2026 11:15:12 +0000 Subject: [PATCH] lua: string metatable, high-byte chars, multi-return truthy, perf --- .claude/scheduled_tasks.lock | 1 + lib/lua/runtime.sx | 169 ++++++++++++++++++++++++----------- lib/lua/scoreboard.json | 74 +++++++-------- lib/lua/scoreboard.md | 40 ++++----- lib/lua/test.sh | 10 +++ lib/lua/tokenizer.sx | Bin 13356 -> 13824 bytes lib/lua/transpile.sx | 23 +++-- 7 files changed, 196 insertions(+), 121 deletions(-) create mode 100644 .claude/scheduled_tasks.lock diff --git a/.claude/scheduled_tasks.lock b/.claude/scheduled_tasks.lock new file mode 100644 index 00000000..ea7330c7 --- /dev/null +++ b/.claude/scheduled_tasks.lock @@ -0,0 +1 @@ +{"sessionId":"31c80255-eb92-43e4-8997-84ad84e27326","pid":90960,"procStart":"564684","acquiredAt":1777049890282} \ No newline at end of file diff --git a/lib/lua/runtime.sx b/lib/lua/runtime.sx index b87ce355..1eb64014 100644 --- a/lib/lua/runtime.sx +++ b/lib/lua/runtime.sx @@ -1,4 +1,4 @@ -(define lua-truthy? (fn (v) (and (not (= v nil)) (not (= v false))))) +(define lua-truthy? (fn (v) (let ((v1 (if (and (= (type-of v) "list") (> (len v) 0) (= (first v) (quote lua-multi))) (if (> (len v) 1) (nth v 1) nil) v))) (and (not (= v1 nil)) (not (= v1 false)))))) (define lua-to-number @@ -151,18 +151,19 @@ lua-eq (fn (a b) - (cond - ((and (= a nil) (= b nil)) true) - ((or (= a nil) (= b nil)) false) - ((and (= (type-of a) (type-of b)) (= a b)) true) - ((and (= (type-of a) "dict") (= (type-of b) "dict")) - (let - ((m (lua-get-mm a "__eq"))) - (cond - ((not (= m nil)) - (let ((r (lua-first (m a b)))) (and (not (= r nil)) (not (= r false))))) - (else false)))) - (else false)))) + (let ((a (lua-first a)) (b (lua-first b))) + (cond + ((and (= a nil) (= b nil)) true) + ((or (= a nil) (= b nil)) false) + ((and (= (type-of a) (type-of b)) (= a b)) true) + ((and (= (type-of a) "dict") (= (type-of b) "dict")) + (let + ((m (lua-get-mm a "__eq"))) + (cond + ((not (= m nil)) + (let ((r (lua-first (m a b)))) (and (not (= r nil)) (not (= r false))))) + (else false)))) + (else false))))) (define lua-neq (fn (a b) (not (lua-eq a b)))) @@ -170,44 +171,46 @@ lua-lt (fn (a b) - (cond - ((and (= (type-of a) "number") (= (type-of b) "number")) (< a b)) - ((and (= (type-of a) "string") (= (type-of b) "string")) (< a b)) - (else - (let - ((m (lua-get-mm a "__lt"))) - (cond - ((not (= m nil)) - (let ((r (lua-first (m a b)))) (and (not (= r nil)) (not (= r false))))) - (else - (let - ((m2 (lua-get-mm b "__lt"))) - (cond - ((not (= m2 nil)) - (let ((r (lua-first (m2 a b)))) (and (not (= r nil)) (not (= r false))))) - (else (error "lua: attempt to compare incompatible types"))))))))))) + (let ((a (lua-first a)) (b (lua-first b))) + (cond + ((and (= (type-of a) "number") (= (type-of b) "number")) (< a b)) + ((and (= (type-of a) "string") (= (type-of b) "string")) (< a b)) + (else + (let + ((m (lua-get-mm a "__lt"))) + (cond + ((not (= m nil)) + (let ((r (lua-first (m a b)))) (and (not (= r nil)) (not (= r false))))) + (else + (let + ((m2 (lua-get-mm b "__lt"))) + (cond + ((not (= m2 nil)) + (let ((r (lua-first (m2 a b)))) (and (not (= r nil)) (not (= r false))))) + (else (error "lua: attempt to compare incompatible types")))))))))))) (define lua-le (fn (a b) - (cond - ((and (= (type-of a) "number") (= (type-of b) "number")) (<= a b)) - ((and (= (type-of a) "string") (= (type-of b) "string")) - (or (< a b) (= a b))) - (else - (let - ((m (lua-get-mm a "__le"))) - (cond - ((not (= m nil)) - (let ((r (lua-first (m a b)))) (and (not (= r nil)) (not (= r false))))) - (else - (let - ((m2 (lua-get-mm b "__le"))) - (cond - ((not (= m2 nil)) - (let ((r (lua-first (m2 a b)))) (and (not (= r nil)) (not (= r false))))) - (else (not (lua-lt b a)))))))))))) + (let ((a (lua-first a)) (b (lua-first b))) + (cond + ((and (= (type-of a) "number") (= (type-of b) "number")) (<= a b)) + ((and (= (type-of a) "string") (= (type-of b) "string")) + (or (< a b) (= a b))) + (else + (let + ((m (lua-get-mm a "__le"))) + (cond + ((not (= m nil)) + (let ((r (lua-first (m a b)))) (and (not (= r nil)) (not (= r false))))) + (else + (let + ((m2 (lua-get-mm b "__le"))) + (cond + ((not (= m2 nil)) + (let ((r (lua-first (m2 a b)))) (and (not (= r nil)) (not (= r false))))) + (else (not (lua-lt b a))))))))))))) (define lua-gt (fn (a b) (lua-lt b a))) @@ -271,14 +274,16 @@ (fn (i) (when (< i (len v)) (begin - (set! t (assoc t (str array-idx) (nth v i))) + (when (not (= (nth v i) nil)) + (set! t (assoc t (str array-idx) (nth v i)))) (set! array-idx (+ array-idx 1)) (spread-loop (+ i 1)))))) (spread-loop 1))) (else (let ((val (if (lua-multi? v) (lua-first v) v))) (begin - (set! t (assoc t (str array-idx) val)) + (when (not (= val nil)) + (set! t (assoc t (str array-idx) val))) (set! array-idx (+ array-idx 1)))))))) ((= (first f) "kv") (let @@ -294,6 +299,7 @@ (t k) (cond ((= t nil) nil) + ((= (type-of t) "string") (lua-get string k)) ((not (= (type-of t) "dict")) nil) (else (let @@ -315,6 +321,8 @@ (let ((key (str k))) (cond + ((= v nil) + (when (has-key? t key) (dict-delete! t key))) ((has-key? t key) (dict-set! t key v)) (else (let @@ -1015,14 +1023,21 @@ (b-loop ni) out))))))))))) +(define __lua-ctrl-32 " + ") + +(define __lua-127-255 "€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ") + (define lua-char-one (fn (n) (cond - ((= n 9) "\t") + ((= n 9) "\t") ((= n 10) "\n") ((= n 13) "\r") + ((and (>= n 0) (< n 32)) (char-at __lua-ctrl-32 n)) ((and (>= n 32) (<= n 126)) (char-at __ascii-32-126 (- n 32))) + ((and (>= n 127) (<= n 255)) (char-at __lua-127-255 (- n 127))) (else (error (str "lua: string.char out of range: " n)))))) (define @@ -1999,7 +2014,14 @@ (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))) + (let ((wrapped (list + (make-symbol "fn") + (list (make-symbol "&rest") (make-symbol "__args")) + (list (make-symbol "let") + (list (list (make-symbol "__varargs") + (list (make-symbol "lua-varargs") + (make-symbol "__args") 0))) + guarded)))) (eval-expr wrapped))))))) (define loadstring lua-loadstring) @@ -2015,7 +2037,9 @@ (cond ((= n "#") (len rest-args)) ((= (type-of n) "number") - (let ((i (- n 1))) + (let ((i (if (< n 0) + (+ (len rest-args) 1 n -1) + (- n 1)))) (cond ((< i 0) (error "lua: bad argument to select")) ((>= i (len rest-args)) nil) @@ -2075,7 +2099,7 @@ (dict-set! __package-loaded "package" package) (dict-set! __package-loaded "_G" _G) -(define arg {}) +(define arg nil) ;; preload debug stub (define debug {}) @@ -2141,3 +2165,44 @@ (define dofile (fn (&rest args) nil)) (define loadfile (fn (&rest args) nil)) + +;; Populate _G with the standard Lua 5.1 global environment. +;; _G must be non-empty so that next(_G) returns a key-value pair. +(dict-set! _G "assert" assert) +(dict-set! _G "collectgarbage" collectgarbage) +(dict-set! _G "dofile" dofile) +(dict-set! _G "error" error) +(dict-set! _G "getfenv" getfenv) +(dict-set! _G "getmetatable" getmetatable) +(dict-set! _G "ipairs" ipairs) +(dict-set! _G "load" load) +(dict-set! _G "loadfile" loadfile) +(dict-set! _G "loadstring" loadstring) +(dict-set! _G "next" next) +(dict-set! _G "pairs" pairs) +(dict-set! _G "pcall" pcall) +(dict-set! _G "print" print) +(dict-set! _G "rawequal" rawequal) +(dict-set! _G "rawget" rawget) +(dict-set! _G "rawset" rawset) +(dict-set! _G "require" require) +(dict-set! _G "select" select) +(dict-set! _G "setfenv" setfenv) +(dict-set! _G "setmetatable" setmetatable) +(dict-set! _G "tonumber" tonumber) +(dict-set! _G "tostring" tostring) +(dict-set! _G "type" type) +(dict-set! _G "unpack" unpack) +(dict-set! _G "xpcall" xpcall) +(dict-set! _G "string" string) +(dict-set! _G "table" table) +(dict-set! _G "math" math) +(dict-set! _G "io" io) +(dict-set! _G "os" os) +(dict-set! _G "coroutine" coroutine) +(dict-set! _G "package" package) +(dict-set! _G "debug" debug) +(dict-set! _G "_VERSION" _VERSION) +(dict-set! _G "_G" _G) +;; Soft mode: skip tests that require io/os/C facilities +(dict-set! _G "_soft" true) diff --git a/lib/lua/scoreboard.json b/lib/lua/scoreboard.json index c5d68ddb..5eb1acdb 100644 --- a/lib/lua/scoreboard.json +++ b/lib/lua/scoreboard.json @@ -1,33 +1,25 @@ { "totals": { - "pass": 1, - "fail": 9, - "timeout": 6, + "pass": 3, + "fail": 8, + "timeout": 5, "skip": 8, "total": 24, "runnable": 16, - "pass_rate": 6.2 + "pass_rate": 18.8 }, "top_failure_modes": [ [ - "timeout", - 6 + "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\", + 7 ], [ - "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\", + "timeout", 5 ], - [ - "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"lua: attempt to call non-functio", - 2 - ], [ "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"lua: module 'C' not found\\\\\\\"\\", 1 - ], - [ - "undefined symbol: fat\\", - 1 ] ], "results": [ @@ -47,19 +39,19 @@ "name": "attrib.lua", "status": "fail", "reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"lua: module 'C' not found\\\\\\\"\\", - "ms": 6174 + "ms": 6616 }, { "name": "big.lua", "status": "timeout", "reason": "per-test timeout", - "ms": 8007 + "ms": 8008 }, { "name": "calls.lua", - "status": "fail", - "reason": "undefined symbol: fat\\", - "ms": 5270 + "status": "timeout", + "reason": "per-test timeout", + "ms": 8006 }, { "name": "checktable.lua", @@ -71,7 +63,7 @@ "name": "closure.lua", "status": "timeout", "reason": "per-test timeout", - "ms": 8006 + "ms": 8000 }, { "name": "code.lua", @@ -81,9 +73,9 @@ }, { "name": "constructs.lua", - "status": "timeout", - "reason": "per-test timeout", - "ms": 8007 + "status": "fail", + "reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\", + "ms": 7429 }, { "name": "db.lua", @@ -95,13 +87,13 @@ "name": "errors.lua", "status": "fail", "reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\", - "ms": 4731 + "ms": 3517 }, { "name": "events.lua", - "status": "timeout", - "reason": "per-test timeout", - "ms": 8007 + "status": "fail", + "reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\", + "ms": 7893 }, { "name": "files.lua", @@ -119,13 +111,13 @@ "name": "literals.lua", "status": "fail", "reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\", - "ms": 1996 + "ms": 5676 }, { "name": "locals.lua", "status": "fail", - "reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"lua: attempt to call non-functio", - "ms": 1785 + "reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\", + "ms": 1829 }, { "name": "main.lua", @@ -135,9 +127,9 @@ }, { "name": "math.lua", - "status": "fail", - "reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"lua: attempt to call non-functio", - "ms": 4610 + "status": "pass", + "reason": "", + "ms": 4512 }, { "name": "nextvar.lua", @@ -149,31 +141,31 @@ "name": "pm.lua", "status": "fail", "reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\", - "ms": 7860 + "ms": 6665 }, { "name": "sort.lua", "status": "timeout", "reason": "per-test timeout", - "ms": 8003 + "ms": 8007 }, { "name": "strings.lua", "status": "fail", "reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\", - "ms": 4636 + "ms": 4488 }, { "name": "vararg.lua", - "status": "fail", - "reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\", - "ms": 2770 + "status": "pass", + "reason": "", + "ms": 2734 }, { "name": "verybig.lua", "status": "pass", "reason": "", - "ms": 1319 + "ms": 612 } ] } \ No newline at end of file diff --git a/lib/lua/scoreboard.md b/lib/lua/scoreboard.md index b16f8400..10f28cc5 100644 --- a/lib/lua/scoreboard.md +++ b/lib/lua/scoreboard.md @@ -1,15 +1,13 @@ # Lua-on-SX conformance scoreboard -**Pass rate:** 1/16 runnable (6.2%) -fail=9 timeout=6 skip=8 total=24 +**Pass rate:** 3/16 runnable (18.8%) +fail=8 timeout=5 skip=8 total=24 ## Top failure modes -- **6x** timeout -- **5x** other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ -- **2x** other: Unhandled exception: \"Unhandled exception: \\\"lua: attempt to call non-functio +- **7x** other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ +- **5x** timeout - **1x** other: Unhandled exception: \"Unhandled exception: \\\"lua: module 'C' not found\\\"\ -- **1x** undefined symbol: fat\ ## Per-test results @@ -17,25 +15,25 @@ fail=9 timeout=6 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: \\\"lua: module 'C' not found\\\"\ | 6174 | -| big.lua | timeout | per-test timeout | 8007 | -| calls.lua | fail | undefined symbol: fat\ | 5270 | +| attrib.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"lua: module 'C' not found\\\"\ | 6616 | +| big.lua | timeout | per-test timeout | 8008 | +| calls.lua | timeout | per-test timeout | 8006 | | checktable.lua | skip | internal debug helpers | 0 | -| closure.lua | timeout | per-test timeout | 8006 | +| closure.lua | timeout | per-test timeout | 8000 | | code.lua | skip | bytecode inspection via debug library | 0 | -| constructs.lua | timeout | per-test timeout | 8007 | +| constructs.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 7429 | | db.lua | skip | debug library | 0 | -| errors.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 4731 | -| events.lua | timeout | per-test timeout | 8007 | +| errors.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 3517 | +| events.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 7893 | | files.lua | skip | io library | 0 | | gc.lua | skip | collectgarbage / finalisers | 0 | -| literals.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 1996 | -| locals.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"lua: attempt to call non-functio | 1785 | +| literals.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 5676 | +| locals.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 1829 | | main.lua | skip | standalone interpreter driver | 0 | -| math.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"lua: attempt to call non-functio | 4610 | +| math.lua | pass | - | 4512 | | nextvar.lua | timeout | per-test timeout | 8007 | -| pm.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 7860 | -| sort.lua | timeout | per-test timeout | 8003 | -| strings.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 4636 | -| vararg.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 2770 | -| verybig.lua | pass | - | 1319 | +| pm.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 6665 | +| sort.lua | timeout | per-test timeout | 8007 | +| strings.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 4488 | +| vararg.lua | pass | - | 2734 | +| verybig.lua | pass | - | 612 | diff --git a/lib/lua/test.sh b/lib/lua/test.sh index ef794195..771d8022 100755 --- a/lib/lua/test.sh +++ b/lib/lua/test.sh @@ -1008,6 +1008,12 @@ cat > "$TMPFILE" << 'EPOCHS' (epoch 1925) (eval "(lua-eval-ast \"return string.format('%.3s', 'hello')\")") +;; ── New-style vararg: arg is nil when ... used in body ────── +(epoch 1930) +(eval "(lua-eval-ast \"function f(...) local x = {...} return arg == nil and 1 or 0 end return f(1,2,3)\")") +(epoch 1931) +(eval "(lua-eval-ast \"function f(...) return select('#', ...) end return f(10,20,30)\")") + EPOCHS OUTPUT=$(timeout 60 "$SX_SERVER" < "$TMPFILE" 2>/dev/null) @@ -1530,6 +1536,10 @@ check 1923 "%X HEX" '"FF"' check 1924 "%c char" '"A"' check 1925 "%.3s precision" '"hel"' +# ── New-style vararg: arg is nil when ... used in body ─────── +check 1930 "new-style vararg arg==nil" '1' +check 1931 "select # vararg count" '3' + TOTAL=$((PASS + FAIL)) if [ $FAIL -eq 0 ]; then echo "ok $PASS/$TOTAL Lua-on-SX tests passed" diff --git a/lib/lua/tokenizer.sx b/lib/lua/tokenizer.sx index b5a73f4d9efd9bc1e9e5fdfd639df41d86b64ca2..82c72af569a6ad47303035c32503bf5a21904ffc 100644 GIT binary patch delta 557 zcmZ3J(U3EtsXjhFr!-MFxuhsZ*Vst6BtKh0iGh)cnT3^&or9B$n}?T=UqDbuSVUAz zTtZSxT1Hk*UP+USOCu#UEi*4w0ixZ|$XwUR)D)tLMO`ErD-L`$l&Rx6r?A^Ej zz`;X@j~qRA{KUyqr_Y={cmBe~OP8-)y>|V^&0Dwc+`V`I!NW(7pFDl`{Kd;xuiw0V z_x{7jPoKYh{r3IG&tJd){QdWTVp)rpfu@3njY6J+v5_W_m7I}Sq?=d*buO}R^E4+u z^S0MW%u7+wu(Jhf0Qv=_6~qL32~#t&Uo~_g8aAsj9*~qp3d)kiBrXLgP-5rc=24nF zkxgRqGbuM8td0SyS1{1jgzEum)zARB!9o+r!s2|84#UZD(ozyQB#nTQV)91VJrB}t s476`@qL8ouZC#bj|N^UX7uwn|QJl5wloC@v{d(8$RwE>XzS)YRlsKmr;XwhDO) m7Mco#6&V^5rOF6h)nrC#VO|vTCmYDfPM#piy7{22y#xSGfG*Jh diff --git a/lib/lua/transpile.sx b/lib/lua/transpile.sx index 400d514d..d809952c 100644 --- a/lib/lua/transpile.sx +++ b/lib/lua/transpile.sx @@ -204,6 +204,16 @@ (make-symbol "arg") (list (make-symbol "lua-varargs-arg-table") (make-symbol "__args") n)))) +(define + lua-body-uses-vararg? + (fn + (node) + (cond + ((not (= (type-of node) "list")) false) + ((= (first node) (quote lua-vararg)) true) + ((= (first node) (quote lua-function)) false) + (else (some lua-body-uses-vararg? node))))) + (define lua-tx-function-guard (fn (body-sx) @@ -236,9 +246,10 @@ ((all-bindings (if is-vararg (append bindings - (list - (lua-tx-function-varargs-binding (len params)) - (lua-tx-function-arg-binding (len params)))) + (list (lua-tx-function-varargs-binding (len params))) + (if (lua-body-uses-vararg? body) + (list) + (list (lua-tx-function-arg-binding (len params))))) bindings))) (list (make-symbol "fn") @@ -518,10 +529,8 @@ ((target (nth node 1)) (func (nth node 2))) (cond ((= (first target) (quote lua-name)) - (list - (make-symbol "define") - (make-symbol (nth target 1)) - (lua-tx func))) + (let ((nm (nth target 1))) + (list (make-symbol "set!") (make-symbol nm) (lua-tx func)))) ((= (first target) (quote lua-field)) (list (make-symbol "lua-set!")