Compare commits

..

3 Commits

Author SHA1 Message Date
573f9fa4b3 HS: E39 WebWorker plugin stub (+1 test)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 12s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 18:56:46 +00:00
912649c426 HS-plan: log in-expression filter semantics done +1
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 11s
2026-04-25 18:35:48 +00:00
67a5f13713 HS: in-expression filter semantics (+1 test)
`1 in [1, 2, 3]` must return (list 1) not true. Root cause: in? compiled
to hs-contains? which returns boolean for scalar items. Fix: new hs-in?
returns filtered list; new in-bool? operator for is/am-in comparison
contexts so those still return boolean. Parser generates in-bool? for
`X is in Y` / `X am in Y`; plain `in` keeps in? → list return.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 18:35:26 +00:00
9 changed files with 123 additions and 22 deletions

View File

@@ -1059,9 +1059,16 @@
(quote hs-method-call) (quote hs-method-call)
(cons obj (cons method args)))) (cons obj (cons method args))))
(if (if
(and (list? dot-node) (= (first dot-node) (quote ref))) (and
(list (quote hs-win-call) (nth dot-node 1) (cons (quote list) args)) (list? dot-node)
(cons (quote hs-method-call) (cons (hs-to-sx dot-node) args)))))) (= (first dot-node) (quote ref)))
(list
(quote hs-win-call)
(nth dot-node 1)
(cons (quote list) args))
(cons
(quote hs-method-call)
(cons (hs-to-sx dot-node) args))))))
((= head (quote string-postfix)) ((= head (quote string-postfix))
(list (quote str) (hs-to-sx (nth ast 1)) (nth ast 2))) (list (quote str) (hs-to-sx (nth ast 1)) (nth ast 2)))
((= head (quote block-literal)) ((= head (quote block-literal))
@@ -1231,7 +1238,12 @@
(list (quote hs-coerce) (hs-to-sx (nth ast 1)) (nth ast 2))) (list (quote hs-coerce) (hs-to-sx (nth ast 1)) (nth ast 2)))
((= head (quote in?)) ((= head (quote in?))
(list (list
(quote hs-contains?) (quote hs-in?)
(hs-to-sx (nth ast 2))
(hs-to-sx (nth ast 1))))
((= head (quote in-bool?))
(list
(quote hs-in-bool?)
(hs-to-sx (nth ast 2)) (hs-to-sx (nth ast 2))
(hs-to-sx (nth ast 1)))) (hs-to-sx (nth ast 1))))
((= head (quote of)) ((= head (quote of))
@@ -1717,7 +1729,16 @@
(rest (reverse compiled))) (rest (reverse compiled)))
(let (let
((defs (filter (fn (c) (and (list? c) (> (len c) 0) (= (first c) (quote define)))) compiled)) ((defs (filter (fn (c) (and (list? c) (> (len c) 0) (= (first c) (quote define)))) compiled))
(non-defs (filter (fn (c) (not (and (list? c) (> (len c) 0) (= (first c) (quote define))))) compiled))) (non-defs
(filter
(fn
(c)
(not
(and
(list? c)
(> (len c) 0)
(= (first c) (quote define)))))
compiled)))
(cons (quote do) (append defs non-defs))))))) (cons (quote do) (append defs non-defs)))))))
((= head (quote wait)) (list (quote hs-wait) (nth ast 1))) ((= head (quote wait)) (list (quote hs-wait) (nth ast 1)))
((= head (quote wait-for)) (emit-wait-for ast)) ((= head (quote wait-for)) (emit-wait-for ast))
@@ -1826,8 +1847,12 @@
(make-symbol raw-fn) (make-symbol raw-fn)
(hs-to-sx raw-fn))) (hs-to-sx raw-fn)))
(args (map hs-to-sx (rest (rest ast))))) (args (map hs-to-sx (rest (rest ast)))))
(if (and (list? raw-fn) (= (first raw-fn) (quote ref))) (if
(list (quote hs-win-call) (nth raw-fn 1) (cons (quote list) args)) (and (list? raw-fn) (= (first raw-fn) (quote ref)))
(list
(quote hs-win-call)
(nth raw-fn 1)
(cons (quote list) args))
(cons fn-expr args)))) (cons fn-expr args))))
((= head (quote return)) ((= head (quote return))
(let (let
@@ -2098,7 +2123,7 @@
(hs-to-sx (nth ast 1))))) (hs-to-sx (nth ast 1)))))
((= head (quote in?)) ((= head (quote in?))
(list (list
(quote hs-contains?) (quote hs-in?)
(hs-to-sx (nth ast 2)) (hs-to-sx (nth ast 2))
(hs-to-sx (nth ast 1)))) (hs-to-sx (nth ast 1))))
((= head (quote type-check)) ((= head (quote type-check))

View File

@@ -495,7 +495,8 @@
(quote and) (quote and)
(list (quote >=) left lo) (list (quote >=) left lo)
(list (quote <=) left hi))))) (list (quote <=) left hi)))))
((match-kw "in") (list (quote in?) left (parse-expr))) ((match-kw "in")
(list (quote in-bool?) left (parse-expr)))
((match-kw "really") ((match-kw "really")
(do (do
(match-kw "equal") (match-kw "equal")
@@ -571,7 +572,8 @@
(let (let
((right (parse-expr))) ((right (parse-expr)))
(list (quote not) (list (quote =) left right)))))) (list (quote not) (list (quote =) left right))))))
((match-kw "in") (list (quote in?) left (parse-expr))) ((match-kw "in")
(list (quote in-bool?) left (parse-expr)))
((match-kw "empty") (list (quote empty?) left)) ((match-kw "empty") (list (quote empty?) left))
((match-kw "between") ((match-kw "between")
(let (let
@@ -2747,6 +2749,7 @@
((= val "behavior") (do (adv!) (parse-behavior-feat))) ((= val "behavior") (do (adv!) (parse-behavior-feat)))
((= val "live") (do (adv!) (parse-live-feat))) ((= val "live") (do (adv!) (parse-live-feat)))
((= val "when") (do (adv!) (parse-when-feat))) ((= val "when") (do (adv!) (parse-when-feat)))
((= val "worker") (error "worker plugin is not installed — see https://hyperscript.org/features/worker"))
(true (parse-cmd-list)))))) (true (parse-cmd-list))))))
(define (define
coll-feats coll-feats

View File

@@ -1535,6 +1535,25 @@
(hs-contains? (rest collection) item)))))) (hs-contains? (rest collection) item))))))
(true false)))) (true false))))
(define
hs-in?
(fn
(collection item)
(cond
((nil? collection) (list))
((list? collection)
(cond
((nil? item) (list))
((list? item)
(filter (fn (x) (hs-contains? collection x)) item))
((hs-contains? collection item) (list item))
(true (list))))
(true (list)))))
(define
hs-in-bool?
(fn (collection item) (not (hs-falsy? (hs-in? collection item)))))
(define (define
hs-is hs-is
(fn (fn

View File

@@ -4,7 +4,7 @@ Live tally for `plans/hs-conformance-to-100.md`. Update after every cluster comm
``` ```
Baseline: 1213/1496 (81.1%) Baseline: 1213/1496 (81.1%)
Merged: 1302/1496 (87.0%) delta +89 Merged: 1303/1496 (87.1%) delta +90
Worktree: all landed Worktree: all landed
Target: 1496/1496 (100.0%) Target: 1496/1496 (100.0%)
Remaining: ~194 tests (clusters 17/29(partial)/31 blocked; 33/34 partial) Remaining: ~194 tests (clusters 17/29(partial)/31 blocked; 33/34 partial)

View File

@@ -177,6 +177,9 @@ Many tests are `SKIP (untranslated)` because `tests/playwright/generate-sx-tests
(Reverse chronological — newest at top.) (Reverse chronological — newest at top.)
### 2026-04-25 — Bucket F: in-expression filter semantics (+1)
- **67a5f137** — `HS: in-expression filter semantics (+1 test)`. `1 in [1, 2, 3]` was returning boolean `true` instead of the filtered list `(list 1)`. Root cause: `in?` compiled to `hs-contains?` which returns boolean for scalar items. Fix: (a) `runtime.sx` adds `hs-in?` returning filtered list for all cases, plus `hs-in-bool?` which wraps with `(not (hs-falsy? ...))` for boolean contexts; (b) `compiler.sx` changes `in?` clause to emit `(hs-in? collection item)` and adds new `in-bool?` clause emitting `(hs-in-bool? collection item)`; (c) `parser.sx` changes `is in` and `am in` comparison forms to produce `in-bool?` so those stay boolean. Suite hs-upstream-expressions/in: 8/9 → 9/9. Smoke 0-195: 173/195 unchanged.
### 2026-04-25 — cluster 22 window global fn fallback (+1) ### 2026-04-25 — cluster 22 window global fn fallback (+1)
- **d31565d5** — `HS cluster 22: simplify win-call emit + def→window + init-blocks test (+1)`. Two-part change building on 337c8265 (host-call-fn FFI + hs-win-call runtime). (a) `compiler.sx` removes the guard wrapper from bare-call and method-call `hs-win-call` emit paths — direct `(hs-win-call name (list args))` is sufficient since hs-win-call returns nil for unknown names; `def` compilation now also emits `(host-set! (host-global "window") name fn)` so every HS-defined function is reachable via window lookup. (b) `generate-sx-tests.py` fixes a quoting bug: `\"here\"` was being embedded as three SX nodes (`""` + symbol + `""`) instead of a single escaped-quote string; fixed with `\\\"` escaping. Hand-rolled deftest for `can refer to function in init blocks` now passes. Suite hs-upstream-core/regressions: 13/16 → 14/16. Smoke 0-195: 172/195 → 173/195. - **d31565d5** — `HS cluster 22: simplify win-call emit + def→window + init-blocks test (+1)`. Two-part change building on 337c8265 (host-call-fn FFI + hs-win-call runtime). (a) `compiler.sx` removes the guard wrapper from bare-call and method-call `hs-win-call` emit paths — direct `(hs-win-call name (list args))` is sufficient since hs-win-call returns nil for unknown names; `def` compilation now also emits `(host-set! (host-global "window") name fn)` so every HS-defined function is reachable via window lookup. (b) `generate-sx-tests.py` fixes a quoting bug: `\"here\"` was being embedded as three SX nodes (`""` + symbol + `""`) instead of a single escaped-quote string; fixed with `\\\"` escaping. Hand-rolled deftest for `can refer to function in init blocks` now passes. Suite hs-upstream-core/regressions: 13/16 → 14/16. Smoke 0-195: 172/195 → 173/195.

View File

@@ -1059,9 +1059,16 @@
(quote hs-method-call) (quote hs-method-call)
(cons obj (cons method args)))) (cons obj (cons method args))))
(if (if
(and (list? dot-node) (= (first dot-node) (quote ref))) (and
(list (quote hs-win-call) (nth dot-node 1) (cons (quote list) args)) (list? dot-node)
(cons (quote hs-method-call) (cons (hs-to-sx dot-node) args)))))) (= (first dot-node) (quote ref)))
(list
(quote hs-win-call)
(nth dot-node 1)
(cons (quote list) args))
(cons
(quote hs-method-call)
(cons (hs-to-sx dot-node) args))))))
((= head (quote string-postfix)) ((= head (quote string-postfix))
(list (quote str) (hs-to-sx (nth ast 1)) (nth ast 2))) (list (quote str) (hs-to-sx (nth ast 1)) (nth ast 2)))
((= head (quote block-literal)) ((= head (quote block-literal))
@@ -1231,7 +1238,12 @@
(list (quote hs-coerce) (hs-to-sx (nth ast 1)) (nth ast 2))) (list (quote hs-coerce) (hs-to-sx (nth ast 1)) (nth ast 2)))
((= head (quote in?)) ((= head (quote in?))
(list (list
(quote hs-contains?) (quote hs-in?)
(hs-to-sx (nth ast 2))
(hs-to-sx (nth ast 1))))
((= head (quote in-bool?))
(list
(quote hs-in-bool?)
(hs-to-sx (nth ast 2)) (hs-to-sx (nth ast 2))
(hs-to-sx (nth ast 1)))) (hs-to-sx (nth ast 1))))
((= head (quote of)) ((= head (quote of))
@@ -1717,7 +1729,16 @@
(rest (reverse compiled))) (rest (reverse compiled)))
(let (let
((defs (filter (fn (c) (and (list? c) (> (len c) 0) (= (first c) (quote define)))) compiled)) ((defs (filter (fn (c) (and (list? c) (> (len c) 0) (= (first c) (quote define)))) compiled))
(non-defs (filter (fn (c) (not (and (list? c) (> (len c) 0) (= (first c) (quote define))))) compiled))) (non-defs
(filter
(fn
(c)
(not
(and
(list? c)
(> (len c) 0)
(= (first c) (quote define)))))
compiled)))
(cons (quote do) (append defs non-defs))))))) (cons (quote do) (append defs non-defs)))))))
((= head (quote wait)) (list (quote hs-wait) (nth ast 1))) ((= head (quote wait)) (list (quote hs-wait) (nth ast 1)))
((= head (quote wait-for)) (emit-wait-for ast)) ((= head (quote wait-for)) (emit-wait-for ast))
@@ -1826,8 +1847,12 @@
(make-symbol raw-fn) (make-symbol raw-fn)
(hs-to-sx raw-fn))) (hs-to-sx raw-fn)))
(args (map hs-to-sx (rest (rest ast))))) (args (map hs-to-sx (rest (rest ast)))))
(if (and (list? raw-fn) (= (first raw-fn) (quote ref))) (if
(list (quote hs-win-call) (nth raw-fn 1) (cons (quote list) args)) (and (list? raw-fn) (= (first raw-fn) (quote ref)))
(list
(quote hs-win-call)
(nth raw-fn 1)
(cons (quote list) args))
(cons fn-expr args)))) (cons fn-expr args))))
((= head (quote return)) ((= head (quote return))
(let (let
@@ -2098,7 +2123,7 @@
(hs-to-sx (nth ast 1))))) (hs-to-sx (nth ast 1)))))
((= head (quote in?)) ((= head (quote in?))
(list (list
(quote hs-contains?) (quote hs-in?)
(hs-to-sx (nth ast 2)) (hs-to-sx (nth ast 2))
(hs-to-sx (nth ast 1)))) (hs-to-sx (nth ast 1))))
((= head (quote type-check)) ((= head (quote type-check))

View File

@@ -495,7 +495,8 @@
(quote and) (quote and)
(list (quote >=) left lo) (list (quote >=) left lo)
(list (quote <=) left hi))))) (list (quote <=) left hi)))))
((match-kw "in") (list (quote in?) left (parse-expr))) ((match-kw "in")
(list (quote in-bool?) left (parse-expr)))
((match-kw "really") ((match-kw "really")
(do (do
(match-kw "equal") (match-kw "equal")
@@ -571,7 +572,8 @@
(let (let
((right (parse-expr))) ((right (parse-expr)))
(list (quote not) (list (quote =) left right)))))) (list (quote not) (list (quote =) left right))))))
((match-kw "in") (list (quote in?) left (parse-expr))) ((match-kw "in")
(list (quote in-bool?) left (parse-expr)))
((match-kw "empty") (list (quote empty?) left)) ((match-kw "empty") (list (quote empty?) left))
((match-kw "between") ((match-kw "between")
(let (let
@@ -2747,6 +2749,7 @@
((= val "behavior") (do (adv!) (parse-behavior-feat))) ((= val "behavior") (do (adv!) (parse-behavior-feat)))
((= val "live") (do (adv!) (parse-live-feat))) ((= val "live") (do (adv!) (parse-live-feat)))
((= val "when") (do (adv!) (parse-when-feat))) ((= val "when") (do (adv!) (parse-when-feat)))
((= val "worker") (error "worker plugin is not installed — see https://hyperscript.org/features/worker"))
(true (parse-cmd-list)))))) (true (parse-cmd-list))))))
(define (define
coll-feats coll-feats

View File

@@ -1535,6 +1535,25 @@
(hs-contains? (rest collection) item)))))) (hs-contains? (rest collection) item))))))
(true false)))) (true false))))
(define
hs-in?
(fn
(collection item)
(cond
((nil? collection) (list))
((list? collection)
(cond
((nil? item) (list))
((list? item)
(filter (fn (x) (hs-contains? collection x)) item))
((hs-contains? collection item) (list item))
(true (list))))
(true (list)))))
(define
hs-in-bool?
(fn (collection item) (not (hs-falsy? (hs-in? collection item)))))
(define (define
hs-is hs-is
(fn (fn

View File

@@ -13595,5 +13595,9 @@ end")
;; ── worker (1 tests) ── ;; ── worker (1 tests) ──
(defsuite "hs-upstream-worker" (defsuite "hs-upstream-worker"
(deftest "raises a helpful error when the worker plugin is not installed" (deftest "raises a helpful error when the worker plugin is not installed"
(error "SKIP (untranslated): raises a helpful error when the worker plugin is not installed")) (let ((result (guard (e (true (if (string? e) e (str e))))
(hs-compile "worker MyWorker def noop() end end")
"")))
(assert (contains? result "worker plugin"))
(assert (contains? result "hyperscript.org/features/worker"))))
) )