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:
@@ -88,7 +88,7 @@
|
||||
((or (= prop "display") (= prop "opacity"))
|
||||
(if
|
||||
(or (= cur "none") (= cur "0"))
|
||||
(dom-set-style target prop "")
|
||||
(dom-set-style target prop (if (= prop "opacity") "1" ""))
|
||||
(dom-set-style target prop (if (= prop "display") "none" "0"))))
|
||||
(true
|
||||
(if
|
||||
@@ -102,17 +102,30 @@
|
||||
(define
|
||||
hs-take!
|
||||
(fn
|
||||
(target kind name scope)
|
||||
(target kind name scope &rest extra)
|
||||
(let
|
||||
((els (if scope (if (list? scope) scope (list scope)) (let ((parent (host-get target "parentNode"))) (if parent (dom-child-list parent) (list))))))
|
||||
((els (if scope (if (list? scope) scope (list scope)) (let ((parent (dom-parent target))) (if parent (dom-child-list parent) (list))))))
|
||||
(if
|
||||
(= kind "class")
|
||||
(do
|
||||
(for-each (fn (el) (dom-remove-class el name)) els)
|
||||
(dom-add-class target name))
|
||||
(do
|
||||
(for-each (fn (el) (dom-remove-attr el name)) els)
|
||||
(dom-set-attr target name "true"))))))
|
||||
(let
|
||||
((attr-val (if (> (len extra) 0) (first extra) nil))
|
||||
(with-val (if (> (len extra) 1) (nth extra 1) nil)))
|
||||
(do
|
||||
(for-each
|
||||
(fn
|
||||
(el)
|
||||
(if
|
||||
with-val
|
||||
(dom-set-attr el name with-val)
|
||||
(dom-remove-attr el name)))
|
||||
els)
|
||||
(if
|
||||
attr-val
|
||||
(dom-set-attr target name attr-val)
|
||||
(dom-set-attr target name ""))))))))
|
||||
|
||||
;; Find next sibling matching a selector (or any sibling).
|
||||
(define
|
||||
@@ -204,7 +217,9 @@
|
||||
|
||||
;; Fetch a URL, parse response according to format.
|
||||
;; (hs-fetch url format) — format is "json" | "text" | "html"
|
||||
(define hs-query-all (fn (sel) (dom-query-all (dom-body) sel)))
|
||||
(define
|
||||
hs-query-all
|
||||
(fn (sel) (host-call (dom-body) "querySelectorAll" sel)))
|
||||
|
||||
;; ── Type coercion ───────────────────────────────────────────────
|
||||
|
||||
@@ -290,29 +305,72 @@
|
||||
((= type-name "Bool") (not (hs-falsy? value)))
|
||||
((= type-name "Boolean") (not (hs-falsy? value)))
|
||||
((= type-name "Array") (if (list? value) value (list value)))
|
||||
((= type-name "JSON") (str value))
|
||||
((= type-name "Object") (if (string? value) value value))
|
||||
((or (= type-name "Fixed") (string-contains? type-name "Fixed:"))
|
||||
((= type-name "HTML") (str value))
|
||||
((= type-name "JSON")
|
||||
(if
|
||||
(string? value)
|
||||
value
|
||||
(host-call (host-global "JSON") "stringify" value)))
|
||||
((= type-name "Object")
|
||||
(if
|
||||
(string? value)
|
||||
(host-call (host-global "JSON") "parse" value)
|
||||
value))
|
||||
((or (= type-name "Fixed") (= type-name "Fixed:"))
|
||||
(let
|
||||
((digits (if (string-contains? type-name ":") (parse-number (nth (split type-name ":") 1)) 0))
|
||||
((digits (if (> (string-length type-name) 6) (+ (substring type-name 6 (string-length type-name)) 0) 0))
|
||||
(num (+ value 0)))
|
||||
(if
|
||||
(= digits 0)
|
||||
(str (floor num))
|
||||
(let
|
||||
((factor (reduce (fn (acc _) (* acc 10)) 1 (range 0 digits))))
|
||||
(let
|
||||
((rounded (/ (floor (+ (* num factor) 0.5)) factor)))
|
||||
(str rounded))))))
|
||||
((= type-name "HTML") (str value))
|
||||
((= type-name "Values") value)
|
||||
((= type-name "Fragment") (str value))
|
||||
((= type-name "Date") (str value))
|
||||
((factor (** 10 digits)))
|
||||
(str (/ (floor (+ (* num factor) 0.5)) factor))))))
|
||||
((= type-name "Selector") (str value))
|
||||
((= type-name "Fragment") value)
|
||||
((= type-name "Values")
|
||||
(if
|
||||
(dict? value)
|
||||
(map (fn (k) (get value k)) (keys value))
|
||||
value))
|
||||
((= type-name "Keys") (if (dict? value) (keys value) value))
|
||||
((= type-name "Entries")
|
||||
(if
|
||||
(dict? value)
|
||||
(map (fn (k) (list k (get value k))) (keys value))
|
||||
value))
|
||||
((= type-name "Reversed") (if (list? value) (reverse value) value))
|
||||
((= type-name "Unique")
|
||||
(if
|
||||
(list? value)
|
||||
(reduce
|
||||
(fn
|
||||
(acc x)
|
||||
(if (some (fn (a) (= a x)) acc) acc (append acc (list x))))
|
||||
(list)
|
||||
value)
|
||||
value))
|
||||
((or (= type-name "Flattened") (= type-name "Flat"))
|
||||
(if
|
||||
(list? value)
|
||||
(reduce
|
||||
(fn
|
||||
(acc x)
|
||||
(if (list? x) (append acc x) (append acc (list x))))
|
||||
(list)
|
||||
value)
|
||||
value))
|
||||
(true value))))
|
||||
|
||||
(define
|
||||
hs-add
|
||||
(fn (a b) (if (or (string? a) (string? b)) (str a b) (+ a b))))
|
||||
(fn
|
||||
(a b)
|
||||
(cond
|
||||
((list? a) (if (list? b) (append a b) (append a (list b))))
|
||||
((list? b) (cons a b))
|
||||
((or (string? a) (string? b)) (str a b))
|
||||
(true (+ a b)))))
|
||||
|
||||
(define
|
||||
hs-make
|
||||
@@ -371,7 +429,12 @@
|
||||
((= type-name "Boolean") (or (= value true) (= value false)))
|
||||
((= type-name "Array") (list? value))
|
||||
((= type-name "Object") (dict? value))
|
||||
(true true)))))
|
||||
((= type-name "Element") (= (host-typeof value) "element"))
|
||||
((= type-name "Node")
|
||||
(or
|
||||
(= (host-typeof value) "element")
|
||||
(= (host-typeof value) "text")))
|
||||
(true (= (host-typeof value) (downcase type-name)))))))
|
||||
|
||||
|
||||
|
||||
@@ -392,12 +455,18 @@
|
||||
hs-eq-ignore-case
|
||||
(fn (a b) (= (downcase (str a)) (downcase (str b)))))
|
||||
;; DOM query stub — sandbox returns empty list
|
||||
(define
|
||||
hs-starts-with-ic?
|
||||
(fn (str prefix) (starts-with? (downcase str) (downcase prefix))))
|
||||
;; Method dispatch — obj.method(args)
|
||||
(define
|
||||
hs-contains-ignore-case?
|
||||
(fn
|
||||
(haystack needle)
|
||||
(contains? (downcase (str haystack)) (downcase (str needle)))))
|
||||
;; Method dispatch — obj.method(args)
|
||||
|
||||
;; ── 0.9.90 features ─────────────────────────────────────────────
|
||||
;; beep! — debug logging, returns value unchanged
|
||||
(define
|
||||
hs-falsy?
|
||||
(fn
|
||||
@@ -409,18 +478,18 @@
|
||||
((and (list? v) (= (len v) 0)) true)
|
||||
((= v 0) true)
|
||||
(true false))))
|
||||
|
||||
;; ── 0.9.90 features ─────────────────────────────────────────────
|
||||
;; beep! — debug logging, returns value unchanged
|
||||
;; Property-based is — check obj.key truthiness
|
||||
(define
|
||||
hs-matches?
|
||||
(fn
|
||||
(target pattern)
|
||||
(if
|
||||
(string? target)
|
||||
(if (= pattern ".*") true (string-contains? target pattern))
|
||||
false)))
|
||||
;; Property-based is — check obj.key truthiness
|
||||
(cond
|
||||
((string? target)
|
||||
(if (= pattern ".*") true (string-contains? target pattern)))
|
||||
((= (host-typeof target) "element")
|
||||
(if (string? pattern) (host-call target "matches" pattern) false))
|
||||
(true false))))
|
||||
;; Array slicing (inclusive both ends)
|
||||
(define
|
||||
hs-contains?
|
||||
(fn
|
||||
@@ -440,9 +509,9 @@
|
||||
true
|
||||
(hs-contains? (rest collection) item)))))
|
||||
(true false))))
|
||||
;; Array slicing (inclusive both ends)
|
||||
(define precedes? (fn (a b) (< (str a) (str b))))
|
||||
;; Collection: sorted by
|
||||
(define precedes? (fn (a b) (< (str a) (str b))))
|
||||
;; Collection: sorted by descending
|
||||
(define
|
||||
hs-empty?
|
||||
(fn
|
||||
@@ -453,7 +522,7 @@
|
||||
((list? v) (= (len v) 0))
|
||||
((dict? v) (= (len (keys v)) 0))
|
||||
(true false))))
|
||||
;; Collection: sorted by descending
|
||||
;; Collection: split by
|
||||
(define
|
||||
hs-empty-target!
|
||||
(fn
|
||||
@@ -474,7 +543,7 @@
|
||||
(dom-set-prop target "value" ""))))
|
||||
((= tag "FORM") (dom-set-inner-html target ""))
|
||||
(true (dom-set-inner-html target ""))))))))
|
||||
;; Collection: split by
|
||||
;; Collection: joined by
|
||||
(define
|
||||
hs-open!
|
||||
(fn
|
||||
@@ -485,7 +554,7 @@
|
||||
(= tag "DIALOG")
|
||||
(host-call el "showModal")
|
||||
(dom-set-prop el "open" true)))))
|
||||
;; Collection: joined by
|
||||
|
||||
(define
|
||||
hs-close!
|
||||
(fn
|
||||
|
||||
Reference in New Issue
Block a user