diff --git a/lib/lua/runtime.sx b/lib/lua/runtime.sx index 68c70de6..28f0b23f 100644 --- a/lib/lua/runtime.sx +++ b/lib/lua/runtime.sx @@ -730,14 +730,76 @@ ;; Match a single "pattern atom" (char or class) at src position — returns true or false. ;; pat-pos is position of the atom start. Returns the atom's length in pat (1 or 2). +(define + lua-pat-set-end + (fn (pat pat-pos) + (let ((i (+ pat-pos 1))) + (begin + (when (and (< i (len pat)) (= (char-at pat i) "^")) + (set! i (+ i 1))) + (when (and (< i (len pat)) (= (char-at pat i) "]")) + (set! i (+ i 1))) + (define + se-loop + (fn () + (when (< i (len pat)) + (let ((c (char-at pat i))) + (cond + ((= c "]") nil) + ((= c "%") + (begin (set! i (+ i 2)) (se-loop))) + (else + (begin (set! i (+ i 1)) (se-loop)))))))) + (se-loop) + i)))) + (define lua-pat-atom-len (fn (pat pat-pos) (cond ((>= pat-pos (len pat)) 0) ((= (char-at pat pat-pos) "%") 2) + ((= (char-at pat pat-pos) "[") + (let ((end (lua-pat-set-end pat pat-pos))) + (- (+ end 1) pat-pos))) (else 1)))) +(define + lua-pat-set-match + (fn (pat set-start sc) + (let ((i (+ set-start 1)) (negated false) (matched false)) + (begin + (when (and (< i (len pat)) (= (char-at pat i) "^")) + (begin (set! negated true) (set! i (+ i 1)))) + (define + sm-loop + (fn () + (when (and (< i (len pat)) (not (= (char-at pat i) "]"))) + (let ((c (char-at pat i))) + (cond + ((= c "%") + (cond + ((< (+ i 1) (len pat)) + (begin + (when (lua-pat-class-match (char-at pat (+ i 1)) sc) + (set! matched true)) + (set! i (+ i 2)) + (sm-loop))) + (else (set! i (+ i 1))))) + ((and (< (+ i 2) (len pat)) (= (char-at pat (+ i 1)) "-") (not (= (char-at pat (+ i 2)) "]"))) + (begin + (when (and (>= sc c) (<= sc (char-at pat (+ i 2)))) + (set! matched true)) + (set! i (+ i 3)) + (sm-loop))) + (else + (begin + (when (= sc c) (set! matched true)) + (set! i (+ i 1)) + (sm-loop)))))))) + (sm-loop) + (if negated (not matched) matched))))) + (define lua-pat-atom-match (fn (pat pat-pos s s-pos) @@ -752,6 +814,7 @@ (cond ((>= (+ pat-pos 1) (len pat)) false) (else (lua-pat-class-match (char-at pat (+ pat-pos 1)) sc)))) + ((= pc "[") (lua-pat-set-match pat pat-pos sc)) (else (= pc sc)))))))) ;; Match pattern against string starting at s-pos and pat-pos. Returns end-pos (inclusive+1) or -1 on no-match. diff --git a/lib/lua/scoreboard.json b/lib/lua/scoreboard.json index 8a6acc38..8a4dae70 100644 --- a/lib/lua/scoreboard.json +++ b/lib/lua/scoreboard.json @@ -1,8 +1,8 @@ { "totals": { "pass": 1, - "fail": 11, - "timeout": 4, + "fail": 8, + "timeout": 7, "skip": 8, "total": 24, "runnable": 16, @@ -10,11 +10,11 @@ }, "top_failure_modes": [ [ - "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\", - 6 + "timeout", + 7 ], [ - "timeout", + "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\", 4 ], [ @@ -28,10 +28,6 @@ [ "undefined symbol: fat\\", 1 - ], - [ - "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"lua: attempt to compare incompat", - 1 ] ], "results": [ @@ -51,7 +47,7 @@ "name": "attrib.lua", "status": "fail", "reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"lua: module 'C' not found\\\\\\\"\\", - "ms": 6133 + "ms": 6895 }, { "name": "big.lua", @@ -63,7 +59,7 @@ "name": "calls.lua", "status": "fail", "reason": "undefined symbol: fat\\", - "ms": 5786 + "ms": 5736 }, { "name": "checktable.lua", @@ -75,7 +71,7 @@ "name": "closure.lua", "status": "timeout", "reason": "per-test timeout", - "ms": 8008 + "ms": 8007 }, { "name": "code.lua", @@ -85,9 +81,9 @@ }, { "name": "constructs.lua", - "status": "fail", - "reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"lua: attempt to compare incompat", - "ms": 7418 + "status": "timeout", + "reason": "per-test timeout", + "ms": 8008 }, { "name": "db.lua", @@ -99,13 +95,13 @@ "name": "errors.lua", "status": "fail", "reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\", - "ms": 4168 + "ms": 3826 }, { "name": "events.lua", - "status": "fail", - "reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\", - "ms": 7884 + "status": "timeout", + "reason": "per-test timeout", + "ms": 8007 }, { "name": "files.lua", @@ -123,13 +119,13 @@ "name": "literals.lua", "status": "fail", "reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\", - "ms": 1796 + "ms": 2748 }, { "name": "locals.lua", "status": "fail", "reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"lua: attempt to call non-functio", - "ms": 1655 + "ms": 2339 }, { "name": "main.lua", @@ -141,7 +137,7 @@ "name": "math.lua", "status": "fail", "reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"lua: attempt to call non-functio", - "ms": 5457 + "ms": 6042 }, { "name": "nextvar.lua", @@ -151,33 +147,33 @@ }, { "name": "pm.lua", - "status": "fail", - "reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\", - "ms": 6576 + "status": "timeout", + "reason": "per-test timeout", + "ms": 8010 }, { "name": "sort.lua", "status": "timeout", "reason": "per-test timeout", - "ms": 8008 + "ms": 8007 }, { "name": "strings.lua", "status": "fail", "reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\", - "ms": 4708 + "ms": 6167 }, { "name": "vararg.lua", "status": "fail", "reason": "other: Unhandled exception: \\\"Unhandled exception: \\\\\\\"assertion failed!\\\\\\\"\\", - "ms": 2227 + "ms": 3432 }, { "name": "verybig.lua", "status": "pass", "reason": "", - "ms": 959 + "ms": 1659 } ] } \ No newline at end of file diff --git a/lib/lua/scoreboard.md b/lib/lua/scoreboard.md index 9612c3aa..5dab9f84 100644 --- a/lib/lua/scoreboard.md +++ b/lib/lua/scoreboard.md @@ -1,16 +1,15 @@ # Lua-on-SX conformance scoreboard **Pass rate:** 1/16 runnable (6.2%) -fail=11 timeout=4 skip=8 total=24 +fail=8 timeout=7 skip=8 total=24 ## Top failure modes -- **6x** other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ -- **4x** timeout +- **7x** timeout +- **4x** other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ - **2x** other: Unhandled exception: \"Unhandled exception: \\\"lua: attempt to call non-functio - **1x** other: Unhandled exception: \"Unhandled exception: \\\"lua: module 'C' not found\\\"\ - **1x** undefined symbol: fat\ -- **1x** other: Unhandled exception: \"Unhandled exception: \\\"lua: attempt to compare incompat ## Per-test results @@ -18,25 +17,25 @@ fail=11 timeout=4 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\\\"\ | 6133 | +| attrib.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"lua: module 'C' not found\\\"\ | 6895 | | big.lua | timeout | per-test timeout | 8007 | -| calls.lua | fail | undefined symbol: fat\ | 5786 | +| calls.lua | fail | undefined symbol: fat\ | 5736 | | checktable.lua | skip | internal debug helpers | 0 | -| closure.lua | timeout | per-test timeout | 8008 | +| 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 | 7418 | +| constructs.lua | timeout | per-test timeout | 8008 | | db.lua | skip | debug library | 0 | -| errors.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 4168 | -| events.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 7884 | +| errors.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 3826 | +| events.lua | timeout | per-test timeout | 8007 | | files.lua | skip | io library | 0 | | gc.lua | skip | collectgarbage / finalisers | 0 | -| literals.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 1796 | -| locals.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"lua: attempt to call non-functio | 1655 | +| literals.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 2748 | +| locals.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"lua: attempt to call non-functio | 2339 | | main.lua | skip | standalone interpreter driver | 0 | -| math.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"lua: attempt to call non-functio | 5457 | +| math.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"lua: attempt to call non-functio | 6042 | | nextvar.lua | timeout | per-test timeout | 8007 | -| pm.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 6576 | -| sort.lua | timeout | per-test timeout | 8008 | -| strings.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 4708 | -| vararg.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 2227 | -| verybig.lua | pass | - | 959 | +| pm.lua | timeout | per-test timeout | 8010 | +| sort.lua | timeout | per-test timeout | 8007 | +| strings.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 6167 | +| vararg.lua | fail | other: Unhandled exception: \"Unhandled exception: \\\"assertion failed!\\\"\ | 3432 | +| verybig.lua | pass | - | 1659 | diff --git a/lib/lua/test.sh b/lib/lua/test.sh index deebeb57..bc846a73 100755 --- a/lib/lua/test.sh +++ b/lib/lua/test.sh @@ -986,6 +986,14 @@ cat > "$TMPFILE" << 'EPOCHS' (epoch 1902) (eval "(lua-eval-ast \"if tonumber('99', 8) == nil then return 1 else return 0 end\")") +;; ── Pattern character sets [...] ────────────────────────────── +(epoch 1910) +(eval "(lua-eval-ast \"return string.match(\\\"hello123\\\", \\\"[a-z]+\\\")\")") +(epoch 1911) +(eval "(lua-eval-ast \"return string.match(\\\"hello123\\\", \\\"[0-9]+\\\")\")") +(epoch 1912) +(eval "(lua-eval-ast \"return string.match(\\\"abc\\\", \\\"[^a]+\\\")\")") + EPOCHS OUTPUT=$(timeout 60 "$SX_SERVER" < "$TMPFILE" 2>/dev/null) @@ -1495,6 +1503,11 @@ check 1900 "tonumber('1010', 2)" '10' check 1901 "tonumber('FF', 16)" '255' check 1902 "tonumber('99', 8) → nil" '1' +# ── Pattern character sets [...] ───────────────────────────── +check 1910 "[a-z]+ on hello123" '"hello"' +check 1911 "[0-9]+ on hello123" '"123"' +check 1912 "[^a]+ on abc" '"bc"' + 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 879b0e15..6f76ba17 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 — **pattern character sets** `[...]` and `[^...]`. New `lua-pat-set-end`/`lua-pat-set-match` helpers handle ranges (`[a-z]`), classes inside sets (`[%d%a]`), negation (`[^abc]`), and `[]...]`/`[^]...]` (literal `]` as first char). Asserts 6→4, but timeouts 3→7 — many tests now reach loop-heavy code. 387/387 green (+3 charset tests). - 2026-04-24: lua: scoreboard iteration — `tonumber(s, base)` for bases 2-36. Validates digit ranges per base, supports leading `+`/`-`, trims whitespace. `math.lua` past assert #21. Asserts 8→6, timeouts 3→4. 384/384 green. - 2026-04-24: lua: scoreboard iteration — added `lua-unwrap-final-return` (post-processor that rewrites top-level `(raise (list 'lua-ret V))` → `V` so top-level defines leak to SX top and loadstring closures can see them). Tried dropping the function-guard at top level, but too many tests use `if x then return 0 else return err end` at chunk tail, whose returns aren't at the *statement-list* tail — guard still needed. Kept guard + unwrap-as-no-op. Scoreboard unchanged. - 2026-04-24: lua: scoreboard iteration — `lua-pat-strip-captures` helper lets patterns with `(...)` capture parens at least match (captures themselves aren't returned yet — match returns whole match). Unblocks common Lua pattern idioms like `(%a+)=(%d+)`. Scoreboard unchanged.