HS behavioral tests: 478→509/831 (57%→61%), parser/compiler/runtime fixes

Parser: am-a/am-not-a type checks, transition element/selector targeting,
take @attr=value with replacement, toggle my/the possessive, <selector/>
syntax in parse-atom, the-of for style/attr/class/selector, when-clause
filtering for add, starts/ends-with ignoring case.

Compiler: take attr passthrough, toggle-style nil→me default, scoped
querySelectorAll for add/remove/toggle-class, has-class? entry, matches?
extracts selector from (query sel), add-class-when with for-each filter,
starts/ends-with-ic entries, hs-add replaces + for polymorphic add.

Runtime: hs-take! proper attr values, hs-type-check Element/Node via
host-typeof, hs-toggle-style! opacity 0↔1, hs-coerce +8 coercions
(Keys/Values/Entries/Reversed/Unique/Flat/JSON/Object), hs-query-all
bypasses broken dom-query-all (WASM auto-converts arrays), hs-matches?
handles DOM el.matches(selector), hs-add list+string+number polymorphic,
hs-starts/ends-with-ic for case-insensitive comparison.

DOM mock: mkStyle() with setProperty/getPropertyValue, fndAll.item().

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-16 12:53:43 +00:00
parent 1e42451252
commit 684a46297d
7 changed files with 793 additions and 220 deletions

View File

@@ -513,12 +513,26 @@
(do
(adv!)
(match-kw "with")
(list (quote starts-with?) left (parse-expr))))
(let
((rhs (parse-atom)))
(if
(match-kw "ignoring")
(do
(match-kw "case")
(list (quote starts-with-ic?) left rhs))
(list (quote starts-with?) left rhs)))))
((and (or (= typ "keyword") (= typ "ident")) (= val "ends"))
(do
(adv!)
(match-kw "with")
(list (quote ends-with?) left (parse-expr))))
(let
((rhs (parse-atom)))
(if
(match-kw "ignoring")
(do
(match-kw "case")
(list (quote ends-with-ic?) left rhs))
(list (quote ends-with?) left rhs)))))
((and (= typ "keyword") (= val "matches"))
(do
(adv!)
@@ -607,11 +621,17 @@
(quote not)
(list (quote contains?) left (parse-expr))))
((match-kw "start")
(do (match-kw "with")
(list (quote not) (list (quote starts-with?) left (parse-expr)))))
(do
(match-kw "with")
(list
(quote not)
(list (quote starts-with?) left (parse-expr)))))
((match-kw "end")
(do (match-kw "with")
(list (quote not) (list (quote ends-with?) left (parse-expr)))))
(do
(match-kw "with")
(list
(quote not)
(list (quote ends-with?) left (parse-expr)))))
(true left))))
((and (= typ "keyword") (= val "equals"))
(do (adv!) (list (quote =) left (parse-expr))))
@@ -693,12 +713,20 @@
nil
(do
(when
(and (number? left) (= (tp-type) "ident")
(not (or (= (tp-val) "starts") (= (tp-val) "ends")
(= (tp-val) "contains") (= (tp-val) "matches")
(= (tp-val) "is") (= (tp-val) "does")
(= (tp-val) "in") (= (tp-val) "precedes")
(= (tp-val) "follows"))))
(and
(number? left)
(= (tp-type) "ident")
(not
(or
(= (tp-val) "starts")
(= (tp-val) "ends")
(= (tp-val) "contains")
(= (tp-val) "matches")
(= (tp-val) "is")
(= (tp-val) "does")
(= (tp-val) "in")
(= (tp-val) "precedes")
(= (tp-val) "follows"))))
(let
((unit (tp-val)))
(do
@@ -757,12 +785,25 @@
(collect-classes!)
(let
((tgt (parse-tgt-kw "to" (list (quote me)))))
(if
(empty? extra-classes)
(list (quote add-class) cls tgt)
(cons
(quote multi-add-class)
(cons tgt (cons cls extra-classes))))))
(let
((when-clause (if (match-kw "when") (parse-expr) nil)))
(if
(empty? extra-classes)
(if
when-clause
(list (quote add-class-when) cls tgt when-clause)
(list (quote add-class) cls tgt))
(if
when-clause
(list
(quote multi-add-class-when)
tgt
when-clause
cls
extra-classes)
(cons
(quote multi-add-class)
(cons tgt (cons cls extra-classes))))))))
nil)))
(define
parse-remove-cmd
@@ -893,6 +934,70 @@
(let
((tgt (parse-tgt-kw "on" (list (quote me)))))
(list (quote toggle-attr) attr-name tgt)))))
((and (= (tp-type) "keyword") (= (tp-val) "my"))
(do
(adv!)
(cond
((= (tp-type) "style")
(let
((prop (get (adv!) "value")))
(if
(match-kw "between")
(let
((val1 (parse-expr)))
(expect-kw! "and")
(let
((val2 (parse-expr)))
(let
((tgt (if (match-kw "on") (parse-expr) nil)))
(list
(quote toggle-style-between)
prop
val1
val2
tgt))))
(let
((tgt (if (match-kw "on") (parse-expr) nil)))
(list (quote toggle-style) prop tgt)))))
((= (tp-type) "attr")
(let
((attr-name (get (adv!) "value")))
(let
((tgt (if (match-kw "on") (parse-expr) nil)))
(list (quote toggle-attr) attr-name tgt))))
(true nil))))
((and (= (tp-type) "keyword") (= (tp-val) "the"))
(do
(adv!)
(let
((expr (parse-the-expr)))
(cond
((and (list? expr) (= (first expr) (quote style)))
(let
((prop (nth expr 1)) (tgt (nth expr 2)))
(if
(match-kw "between")
(let
((val1 (parse-expr)))
(expect-kw! "and")
(let
((val2 (parse-expr)))
(list
(quote toggle-style-between)
prop
val1
val2
tgt)))
(list (quote toggle-style) prop tgt))))
((and (list? expr) (= (first expr) (quote attr)))
(let
((attr-name (nth expr 1)) (tgt (nth expr 2)))
(list (quote toggle-attr) attr-name tgt)))
((and (list? expr) (= (first expr) (quote has-class?)))
(let
((tgt (nth expr 1)) (cls (nth expr 2)))
(list (quote toggle-class) cls tgt)))
(true nil)))))
(true nil))))
(define
parse-set-cmd
@@ -1080,21 +1185,26 @@
(fn
()
(let
((prop (cond ((= (tp-type) "style") (get (adv!) "value")) ((= (tp-val) "my") (do (adv!) (if (= (tp-type) "style") (get (adv!) "value") (get (adv!) "value")))) (true (get (adv!) "value")))))
((tgt (cond ((and (= (tp-type) "ident") (= (tp-val) "element")) (do (adv!) (parse-atom))) ((= (tp-type) "id") (parse-atom)) ((= (tp-type) "class") (parse-atom)) ((= (tp-type) "selector") (parse-atom)) (true nil))))
(let
((from-val (if (match-kw "from") (parse-expr) nil)))
(expect-kw! "to")
((prop (cond ((= (tp-type) "style") (get (adv!) "value")) ((= (tp-val) "my") (do (adv!) (if (= (tp-type) "style") (get (adv!) "value") (get (adv!) "value")))) (true (get (adv!) "value")))))
(let
((value (parse-expr)))
((from-val (if (match-kw "from") (parse-expr) nil)))
(expect-kw! "to")
(let
((dur (if (match-kw "over") (parse-expr) nil)))
(if
from-val
(list (quote transition-from) prop from-val value dur)
((value (parse-expr)))
(let
((dur (if (match-kw "over") (parse-expr) nil)))
(if
dur
(list (quote transition) prop value dur nil)
(list (quote transition) prop value nil)))))))))
from-val
(list
(quote transition-from)
prop
from-val
value
dur
tgt)
(list (quote transition) prop value dur tgt)))))))))
(define
parse-repeat-cmd
(fn
@@ -1223,22 +1333,53 @@
()
(let
((typ (tp-type)) (val (tp-val)))
(if
(or (= typ "ident") (= typ "keyword"))
(do
(adv!)
(if
(match-kw "of")
(list (make-symbol ".") (parse-expr) val)
(cond
((= val "result") (list (quote it)))
((= val "first") (parse-pos-kw (quote first)))
((= val "last") (parse-pos-kw (quote last)))
((= val "closest") (parse-trav (quote closest)))
((= val "next") (parse-trav (quote next)))
((= val "previous") (parse-trav (quote previous)))
(true (list (quote ref) val)))))
(parse-atom)))))
(cond
((= typ "style")
(do
(adv!)
(if
(match-kw "of")
(list (quote style) val (parse-expr))
(list (quote style) val (list (quote me))))))
((= typ "attr")
(do
(adv!)
(if
(match-kw "of")
(list (quote attr) val (parse-expr))
(list (quote attr) val (list (quote me))))))
((= typ "class")
(do
(adv!)
(if
(match-kw "of")
(list (quote has-class?) (parse-expr) val)
(list (quote has-class?) (list (quote me)) val))))
((= typ "selector")
(do
(adv!)
(if
(match-kw "in")
(list
(quote in?)
(list (quote query) val)
(parse-expr))
(list (quote query) val))))
((or (= typ "ident") (= typ "keyword"))
(do
(adv!)
(if
(match-kw "of")
(list (make-symbol ".") (parse-expr) val)
(cond
((= val "result") (list (quote it)))
((= val "first") (parse-pos-kw (quote first)))
((= val "last") (parse-pos-kw (quote last)))
((= val "closest") (parse-trav (quote closest)))
((= val "next") (parse-trav (quote next)))
((= val "previous") (parse-trav (quote previous)))
(true (list (quote ref) val))))))
(true (parse-atom))))))
(define
parse-array-lit
(fn