From 108e25d4186c244875ce17607bf1abe6016c4e2e Mon Sep 17 00:00:00 2001 From: giles Date: Thu, 23 Apr 2026 21:52:55 +0000 Subject: [PATCH] HS: string template \${x} (+2 tests) `\$window.foo` / `\${window.foo}` couldn't resolve. Two fixes: (a) compiler.sx: in a dot-chain base position, known globals (window, document, navigator, location, history, screen, localStorage, sessionStorage, console) emit `(host-global "name")` instead of a bare unbound symbol. (b) generator: `eval-hs-locals` now also sets each binding on `window.` via `host-set!`, so tests that translated `window.X = Y` as a local pair still see `window.X` at eval time. Co-Authored-By: Claude Opus 4.7 (1M context) --- lib/hyperscript/compiler.sx | 3 ++- shared/static/wasm/sx/hs-compiler.sx | 3 ++- spec/tests/test-hyperscript-behavioral.sx | 3 +++ tests/playwright/generate-sx-tests.py | 3 +++ 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/hyperscript/compiler.sx b/lib/hyperscript/compiler.sx index d67e1439..41918e13 100644 --- a/lib/hyperscript/compiler.sx +++ b/lib/hyperscript/compiler.sx @@ -918,7 +918,8 @@ ((= head (quote event)) (quote event)) ((= head dot-sym) (let - ((target (hs-to-sx (nth ast 1))) (prop (nth ast 2))) + ((target (let ((t (hs-to-sx (nth ast 1)))) (if (and (symbol? t) (or (= (str t) "window") (= (str t) "document") (= (str t) "navigator") (= (str t) "location") (= (str t) "history") (= (str t) "screen") (= (str t) "localStorage") (= (str t) "sessionStorage") (= (str t) "console"))) (list (quote host-global) (str t)) t))) + (prop (nth ast 2))) (cond ((= prop "first") (list (quote hs-first) target)) ((= prop "last") (list (quote hs-last) target)) diff --git a/shared/static/wasm/sx/hs-compiler.sx b/shared/static/wasm/sx/hs-compiler.sx index d67e1439..41918e13 100644 --- a/shared/static/wasm/sx/hs-compiler.sx +++ b/shared/static/wasm/sx/hs-compiler.sx @@ -918,7 +918,8 @@ ((= head (quote event)) (quote event)) ((= head dot-sym) (let - ((target (hs-to-sx (nth ast 1))) (prop (nth ast 2))) + ((target (let ((t (hs-to-sx (nth ast 1)))) (if (and (symbol? t) (or (= (str t) "window") (= (str t) "document") (= (str t) "navigator") (= (str t) "location") (= (str t) "history") (= (str t) "screen") (= (str t) "localStorage") (= (str t) "sessionStorage") (= (str t) "console"))) (list (quote host-global) (str t)) t))) + (prop (nth ast 2))) (cond ((= prop "first") (list (quote hs-first) target)) ((= prop "last") (list (quote hs-last) target)) diff --git a/spec/tests/test-hyperscript-behavioral.sx b/spec/tests/test-hyperscript-behavioral.sx index 0008180f..cb309769 100644 --- a/spec/tests/test-hyperscript-behavioral.sx +++ b/spec/tests/test-hyperscript-behavioral.sx @@ -53,6 +53,9 @@ ;; in a fresh CEK env. Avoids `apply` (whose JIT path can loop on some forms). (define eval-hs-locals (fn (src bindings) + ;; Also expose bindings on the `window` global so tests that reference + ;; window.X (common in upstream tests) can resolve them. + (for-each (fn (b) (host-set! (host-global "window") (str (first b)) (nth b 1))) bindings) (let ((sx (hs-to-sx (hs-compile src)))) ;; Build (let ((name1 (quote val1)) ...) ) (let ((let-binds (map (fn (b) (list (first b) (list (quote quote) (nth b 1)))) bindings))) diff --git a/tests/playwright/generate-sx-tests.py b/tests/playwright/generate-sx-tests.py index 33a3ad10..eabc99ca 100644 --- a/tests/playwright/generate-sx-tests.py +++ b/tests/playwright/generate-sx-tests.py @@ -2339,6 +2339,9 @@ output.append(';; Locals are injected as a `let` wrapping the compiled body, the output.append(';; in a fresh CEK env. Avoids `apply` (whose JIT path can loop on some forms).') output.append('(define eval-hs-locals') output.append(' (fn (src bindings)') +output.append(' ;; Also expose bindings on the `window` global so tests that reference') +output.append(' ;; window.X (common in upstream tests) can resolve them.') +output.append(' (for-each (fn (b) (host-set! (host-global "window") (str (first b)) (nth b 1))) bindings)') output.append(' (let ((sx (hs-to-sx (hs-compile src))))') output.append(' ;; Build (let ((name1 (quote val1)) ...) )') output.append(' (let ((let-binds (map (fn (b) (list (first b) (list (quote quote) (nth b 1)))) bindings)))')