diff --git a/lib/js/parser.sx b/lib/js/parser.sx index 5b1d1bb7..4e88f2d4 100644 --- a/lib/js/parser.sx +++ b/lib/js/parser.sx @@ -418,17 +418,43 @@ (dict-set! st :idx saved) (jp-advance! st) (let - ((e (jp-parse-assignment st))) + ((e (jp-parse-comma-seq st))) (jp-expect! st "punct" ")") e))) (do (dict-set! st :idx saved) (jp-advance! st) (let - ((e (jp-parse-assignment st))) + ((e (jp-parse-comma-seq st))) (jp-expect! st "punct" ")") e))))))) +(define + jp-parse-comma-seq + (fn + (st) + (let + ((first-expr (jp-parse-assignment st))) + (if + (jp-at? st "punct" ",") + (jp-parse-comma-seq-rest st (list first-expr)) + first-expr)))) + +(define + jp-parse-comma-seq-rest + (fn + (st acc) + (do + (jp-advance! st) + (let + ((next-expr (jp-parse-assignment st))) + (let + ((acc2 (append acc (list next-expr)))) + (if + (jp-at? st "punct" ",") + (jp-parse-comma-seq-rest st acc2) + (cons (quote js-comma) (list acc2)))))))) + (define jp-collect-params (fn diff --git a/lib/js/test262-scoreboard.json b/lib/js/test262-scoreboard.json index 9cadeee2..546be845 100644 --- a/lib/js/test262-scoreboard.json +++ b/lib/js/test262-scoreboard.json @@ -1,45 +1,37 @@ { "totals": { "pass": 46, - "fail": 2, + "fail": 4, "skip": 0, - "timeout": 2, + "timeout": 0, "total": 50, "runnable": 50, "pass_rate": 92.0 }, "categories": [ { - "category": "built-ins/Number", + "category": "built-ins/Object", "total": 50, "pass": 46, - "fail": 2, + "fail": 4, "skip": 0, - "timeout": 2, + "timeout": 0, "pass_rate": 92.0, "top_failures": [ [ - "Timeout", - 2 - ], - [ - "Test262Error (assertion failed)", - 2 + "ReferenceError (undefined symbol)", + 4 ] ] } ], "top_failure_modes": [ [ - "Timeout", - 2 - ], - [ - "Test262Error (assertion failed)", - 2 + "ReferenceError (undefined symbol)", + 4 ] ], "pinned_commit": "d5e73fc8d2c663554fb72e2380a8c2bc1a318a33", - "elapsed_seconds": 71.0, + "elapsed_seconds": 64.1, "workers": 1 } \ No newline at end of file diff --git a/lib/js/test262-scoreboard.md b/lib/js/test262-scoreboard.md index 752301bb..a14c2efd 100644 --- a/lib/js/test262-scoreboard.md +++ b/lib/js/test262-scoreboard.md @@ -1,24 +1,22 @@ # test262 scoreboard Pinned commit: `d5e73fc8d2c663554fb72e2380a8c2bc1a318a33` -Wall time: 71.0s +Wall time: 64.1s -**Total:** 46/50 runnable passed (92.0%). Raw: pass=46 fail=2 skip=0 timeout=2 total=50. +**Total:** 46/50 runnable passed (92.0%). Raw: pass=46 fail=4 skip=0 timeout=0 total=50. ## Top failure modes -- **2x** Timeout -- **2x** Test262Error (assertion failed) +- **4x** ReferenceError (undefined symbol) ## Categories (worst pass-rate first, min 10 runnable) | Category | Pass | Fail | Skip | Timeout | Total | Pass % | |---|---:|---:|---:|---:|---:|---:| -| built-ins/Number | 46 | 2 | 0 | 2 | 50 | 92.0% | +| built-ins/Object | 46 | 4 | 0 | 0 | 50 | 92.0% | ## Per-category top failures (min 10 runnable, worst first) -### built-ins/Number (46/50 — 92.0%) +### built-ins/Object (46/50 — 92.0%) -- **2x** Timeout -- **2x** Test262Error (assertion failed) +- **4x** ReferenceError (undefined symbol) diff --git a/lib/js/transpile.sx b/lib/js/transpile.sx index c044f558..f717625a 100644 --- a/lib/js/transpile.sx +++ b/lib/js/transpile.sx @@ -165,6 +165,8 @@ (js-transpile-new (nth ast 1) (nth ast 2))) ((js-tag? ast "js-class") (js-transpile-class (nth ast 1) (nth ast 2) (nth ast 3))) + ((js-tag? ast "js-comma") + (cons (js-sym "begin") (map js-transpile (nth ast 1)))) ((js-tag? ast "js-throw") (js-transpile-throw (nth ast 1))) ((js-tag? ast "js-try") (js-transpile-try (nth ast 1) (nth ast 2) (nth ast 3))) diff --git a/plans/js-on-sx.md b/plans/js-on-sx.md index 378f52d5..b98c9a51 100644 --- a/plans/js-on-sx.md +++ b/plans/js-on-sx.md @@ -158,6 +158,8 @@ Each item: implement → tests → update progress. Mark `[x]` when tests green. Append-only record of completed iterations. Loop writes one line per iteration: date, what was done, test count delta. +- 2026-05-08 — **Comma operator `(a, b, c)` parses and evaluates left-to-right, returning last.** Was failing with `Expected punct ')' got punct ','` because `jp-try-arrow-or-paren` only consumed a single assignment expression. Added `jp-parse-comma-seq` / `jp-parse-comma-seq-rest` helpers that build a `js-comma` AST node with the list of expressions; the transpiler emits `(begin ...)` which evaluates each in order and returns the last. Fixes `Object((null,2,3),1,2)`-style tests. built-ins/Object: 44/50 → 46/50. conformance.sh: 148/148. + - 2026-05-08 — **ToPrimitive treats functions as non-primitive in `js-to-string` / `js-to-number`.** Per ES, ToPrimitive only accepts strings/numbers/booleans/null/undefined as primitives — objects AND functions must trigger the next conversion step. Was treating function returns from toString/valueOf as primitives (recursing to extract a string), so a `toString` returning a function wouldn't fall through to `valueOf`. Widened the dict-only check to `(or (= type "dict") (js-function? result))` in both ToPrimitive paths. Now `var o = {toString: () => function(){}, valueOf: () => { throw 'x' }}; new String(o)` propagates `'x'` from valueOf. built-ins/String: 85/99 → 86/99. conformance.sh: 148/148. - 2026-05-08 — **`fn.toString()` and `String(fn)` honour `Function.prototype.toString` overrides.** Two hardcoded paths returned `"function () { [native code] }"` regardless of any user override: the function-method dispatch in `js-invoke-function-method`, and the lambda branch of `js-to-string`. Both now look up `Function.prototype.toString` via `js-dict-get-walk` and invoke it on the function (`recv`/`v`) when available, falling back to the native marker only if no override exists. Now `Function.prototype.toString = ...; (function(){}).toString()` returns the override, and `new String(fn)` stores the override result. built-ins/String: 84/99 → 85/99. conformance.sh: 148/148.