diff --git a/lib/js/parser.sx b/lib/js/parser.sx index ce2039c9..7b81ed81 100644 --- a/lib/js/parser.sx +++ b/lib/js/parser.sx @@ -153,6 +153,20 @@ (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) "function")) + (do + (jp-advance! st) + (let + ((nm + (if + (= (get (jp-peek st) :type) "ident") + (let ((n (get (jp-peek st) :value))) (do (jp-advance! st) n)) + nil))) + (let + ((params (jp-parse-param-list st))) + (let + ((body (jp-parse-block st))) + (list (quote js-funcexpr) nm params body)))))) ((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")) diff --git a/lib/js/transpile.sx b/lib/js/transpile.sx index d6cd58be..1a9b1a73 100644 --- a/lib/js/transpile.sx +++ b/lib/js/transpile.sx @@ -405,7 +405,19 @@ (list (js-sym "js-new-call") (js-transpile callee) - (cons (js-sym "js-args") (map js-transpile args))))) + (cond + ((js-has-spread? args) + (cons + (js-sym "js-array-spread-build") + (map + (fn + (e) + (if + (js-tag? e "js-spread") + (list (js-sym "list") "js-spread" (js-transpile (nth e 1))) + (list (js-sym "list") "js-value" (js-transpile e)))) + args))) + (else (cons (js-sym "js-args") (map js-transpile args))))))) (define js-transpile-array diff --git a/plans/js-on-sx.md b/plans/js-on-sx.md index eef29783..17273b0e 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 — **`new function(){...}(args)` and `new f(...rest)` now parse and execute.** Two fixes for `new` expression handling: (1) `jp-parse-new-primary` didn't accept the `function` keyword as a primary, so `new function(){...}` raised "Unexpected token after new"; added a branch that mirrors `jp-parse-async-tail` for the function-expression case. (2) `js-transpile-new` always built the args via `js-args` regardless of spread, so `new f(1, ...[])` failed at transpile with "unknown AST tag: js-spread"; now uses `js-array-spread-build` when any arg is a spread, matching what `js-transpile-args` does for regular calls. Result: language/expressions/new 16/30 → 19/30. Object 30/30, Array 18/30, language/expressions/call 21/30 unchanged. conformance.sh: 148/148. + - 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.