From 72be94c900f343bcd6acd8293e10cf3c993ac9a9 Mon Sep 17 00:00:00 2001 From: giles Date: Sat, 9 May 2026 15:18:42 +0000 Subject: [PATCH] js-on-sx: parser accepts new ; runtime throws TypeError --- lib/js/parser.sx | 12 ++++++++++++ plans/js-on-sx.md | 2 ++ 2 files changed, 14 insertions(+) diff --git a/lib/js/parser.sx b/lib/js/parser.sx index 7de6db00..ce2039c9 100644 --- a/lib/js/parser.sx +++ b/lib/js/parser.sx @@ -153,6 +153,18 @@ (do (jp-advance! st) (list (quote js-ident) "this"))) ((and (= (get t :type) "keyword") (= (get t :value) "new")) (do (jp-advance! st) (jp-parse-new-expr st))) + ((and (= (get t :type) "keyword") (= (get t :value) "true")) + (do (jp-advance! st) (list (quote js-bool) true))) + ((and (= (get t :type) "keyword") (= (get t :value) "false")) + (do (jp-advance! st) (list (quote js-bool) false))) + ((and (= (get t :type) "keyword") (= (get t :value) "null")) + (do (jp-advance! st) (list (quote js-null)))) + ((and (= (get t :type) "keyword") (= (get t :value) "undefined")) + (do (jp-advance! st) (list (quote js-undef)))) + ((= (get t :type) "number") + (do (jp-advance! st) (list (quote js-num) (get t :value)))) + ((= (get t :type) "string") + (do (jp-advance! st) (list (quote js-str) (get t :value)))) ((and (= (get t :type) "punct") (= (get t :value) "(")) (jp-parse-paren-or-arrow st)) (else diff --git a/plans/js-on-sx.md b/plans/js-on-sx.md index 6e6f25b2..eef29783 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-09 — **Parser accepts `new ` (boolean/number/string/null/undefined) and lets it throw TypeError at runtime.** Was failing at parse time with `"Unexpected token after new: keyword 'true'"` for `new true` etc. Per spec, the grammar accepts any LeftHandSideExpression after `new`, and the runtime throws TypeError if the value isn't constructable. Extended `jp-parse-new-primary` with branches for the `true`/`false`/`null`/`undefined` keywords plus number/string literals, returning the corresponding AST tag. `js-new-call`'s existing `(not (js-function? ctor))` guard then raises the right TypeError. Result: language/expressions/new 11/30 → 16/30. Object 30/30 holds. conformance.sh: 148/148. + - 2026-05-09 — **`bind` returns a dict-with-`__callable__` so bound functions are mutable + carry spec metadata.** Was returning a bare `(fn ...)` lambda — `obj.property = 12` on the bound result silently no-op'd because `js-set-prop` on a lambda only handles the `"prototype"` key. Now bind returns `{:__callable__ :length :name "bound" :__js_bound_target__ recv}`. Notably skipped the `"bound " + target.name` style — for dict constructors (`Number`, `String`) `js-extract-fn-name` calls `inspect` which walks the entire prototype chain and is pathologically slow on those huge dicts (timed out 6 tests). Result: built-ins/Function/prototype/bind 22/30 → 24/30, Function/prototype 19/30 maintained. Object 30/30, Array 18/30 unchanged. conformance.sh: 148/148. - 2026-05-09 — **`Function.prototype.call` / `apply` box primitive `thisArg` per non-strict ToObject.** Per spec, in non-strict mode the called function receives `ToObject(thisArg)` as `this` — so `f.call(1)` should see a `Number(1)` wrapper, not the raw primitive. We were passing primitives through unchanged, so `this.touched = true` inside the function silently no-op'd (`js-set-prop` on a number returns val unchanged). Extracted a `js-coerce-this-arg` helper that does the spec coercion: undefined/null → globalThis, number/rational → `new Number(v)`, string → `new String(v)`, boolean → `new Boolean(v)`, else as-is. Result: built-ins/Function/prototype/call 19/30 → 23/30, apply 22/30 → 25/30. bind 22/30, Object 30/30 unchanged. conformance.sh: 148/148.