HS: remove bare @attr, set X @attr, JSON clean, FormEncoded, HTML join

- parser remove/set: accept bare @attr (not just [@attr])
- parser set: wrap tgt as (attr name tgt) when @attr follows target
- runtime: hs-json-stringify walks sx-dict/list to emit plain JSON
  (strips _type key which leaked via JSON.stringify)
- hs-coerce JSON / JSONString: use hs-json-stringify
- hs-coerce FormEncoded: dict → k=v&... (list values repeat key)
- hs-coerce HTML: join list elements; element → outerHTML

+4 tests (button query in form, JSONString value, array→HTML,
form | JSONString now fails only on key order).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-23 20:14:03 +00:00
parent 8984520f05
commit 6b0334affe
6 changed files with 466 additions and 82 deletions

View File

@@ -18,7 +18,17 @@
(target value) (target value)
(if (if
(not (list? target)) (not (list? target))
(list (quote set!) target value) (if
(= target (quote the-result))
(list
(quote let)
(list (list (quote __hs-r) value))
(list
(quote begin)
(list (quote set!) (quote the-result) (quote __hs-r))
(list (quote set!) (quote it) (quote __hs-r))
(quote __hs-r)))
(list (quote set!) target value))
(let (let
((th (first target))) ((th (first target)))
(cond (cond
@@ -1169,15 +1179,71 @@
(let (let
((tgt-expr (cond ((and (list? raw-tgt) (= (first raw-tgt) (quote query))) (list (quote hs-query-all) (nth raw-tgt 1))) ((and (list? raw-tgt) (= (first raw-tgt) (quote in?)) (list? (nth raw-tgt 1)) (= (first (nth raw-tgt 1)) (quote query))) (list (quote host-call) (hs-to-sx (nth raw-tgt 2)) "querySelectorAll" (nth (nth raw-tgt 1) 1))) (true (hs-to-sx raw-tgt))))) ((tgt-expr (cond ((and (list? raw-tgt) (= (first raw-tgt) (quote query))) (list (quote hs-query-all) (nth raw-tgt 1))) ((and (list? raw-tgt) (= (first raw-tgt) (quote in?)) (list? (nth raw-tgt 1)) (= (first (nth raw-tgt 1)) (quote query))) (list (quote host-call) (hs-to-sx (nth raw-tgt 2)) "querySelectorAll" (nth (nth raw-tgt 1) 1))) (true (hs-to-sx raw-tgt)))))
(list (list
(quote for-each) (quote let)
(list (list
(quote fn)
(list (quote it))
(list (list
(quote when) (quote __hs-matched)
(hs-to-sx when-cond) (list
(list (quote dom-add-class) (quote it) cls))) (quote filter)
tgt-expr)))) (list
(quote fn)
(list (quote it))
(hs-to-sx when-cond))
tgt-expr)))
(list
(quote begin)
(list
(quote set!)
(quote the-result)
(quote __hs-matched))
(list (quote set!) (quote it) (quote __hs-matched))
(list
(quote for-each)
(list
(quote fn)
(list (quote it))
(list (quote dom-add-class) (quote it) cls))
(quote __hs-matched))
(quote __hs-matched))))))
((= head (quote add-attr-when))
(let
((attr-name (nth ast 1))
(attr-val (hs-to-sx (nth ast 2)))
(raw-tgt (nth ast 3))
(when-cond (nth ast 4)))
(let
((tgt-expr (cond ((and (list? raw-tgt) (= (first raw-tgt) (quote query))) (list (quote hs-query-all) (nth raw-tgt 1))) (true (hs-to-sx raw-tgt)))))
(list
(quote let)
(list
(list
(quote __hs-matched)
(list
(quote filter)
(list
(quote fn)
(list (quote it))
(hs-to-sx when-cond))
tgt-expr)))
(list
(quote begin)
(list
(quote set!)
(quote the-result)
(quote __hs-matched))
(list (quote set!) (quote it) (quote __hs-matched))
(list
(quote for-each)
(list
(quote fn)
(list (quote it))
(list
(quote hs-set-attr!)
(quote it)
attr-name
attr-val))
(quote __hs-matched))
(quote __hs-matched))))))
((= head (quote multi-remove-class)) ((= head (quote multi-remove-class))
(let (let
((target (hs-to-sx (nth ast 1))) ((target (hs-to-sx (nth ast 1)))
@@ -1598,11 +1664,17 @@
((= head (quote append!)) ((= head (quote append!))
(let (let
((tgt (hs-to-sx (nth ast 2))) ((tgt (hs-to-sx (nth ast 2)))
(val (hs-to-sx (nth ast 1)))) (val (hs-to-sx (nth ast 1)))
(if (raw-tgt (nth ast 2)))
(symbol? tgt) (cond
(list (quote set!) tgt (list (quote hs-append) tgt val)) ((symbol? tgt)
(list (quote hs-append!) val tgt)))) (list
(quote set!)
tgt
(list (quote hs-append) tgt val)))
((and (list? raw-tgt) (or (= (first raw-tgt) (quote local)) (= (first raw-tgt) (quote ref))))
(emit-set raw-tgt (list (quote hs-append) tgt val)))
(true (list (quote hs-append!) val tgt)))))
((= head (quote tell)) ((= head (quote tell))
(let (let
((tgt (hs-to-sx (nth ast 1)))) ((tgt (hs-to-sx (nth ast 1))))

View File

@@ -954,7 +954,35 @@
(when (= (tp-type) "bracket-close") (adv!)) (when (= (tp-type) "bracket-close") (adv!))
(let (let
((tgt (parse-tgt-kw "to" (list (quote me))))) ((tgt (parse-tgt-kw "to" (list (quote me)))))
(list (quote add-attr) attr-name attr-val tgt)))))) (let
((when-clause (if (match-kw "when") (parse-expr) nil)))
(if
when-clause
(list
(quote add-attr-when)
attr-name
attr-val
tgt
when-clause)
(list (quote add-attr) attr-name attr-val tgt))))))))
((= (tp-type) "attr")
(let
((attr-name (get (adv!) "value")))
(let
((attr-val (if (and (= (tp-type) "op") (= (tp-val) "=")) (do (adv!) (parse-expr)) "")))
(let
((tgt (if (match-kw "to") (parse-expr) (list (quote me)))))
(let
((when-clause (if (match-kw "when") (parse-expr) nil)))
(if
when-clause
(list
(quote add-attr-when)
attr-name
attr-val
tgt
when-clause)
(list (quote add-attr) attr-name attr-val tgt)))))))
(true (true
(let (let
((value (parse-expr))) ((value (parse-expr)))
@@ -991,6 +1019,12 @@
(cons (cons
(quote multi-remove-class) (quote multi-remove-class)
(cons tgt (cons cls extra-classes))))))) (cons tgt (cons cls extra-classes)))))))
((= (tp-type) "attr")
(let
((attr-name (get (adv!) "value")))
(let
((tgt (if (match-kw "from") (parse-expr) (list (quote me)))))
(list (quote remove-attr) attr-name tgt))))
((and (= (tp-type) "bracket-open") (= (tp-val) "[")) ((and (= (tp-type) "bracket-open") (= (tp-val) "["))
(do (do
(adv!) (adv!)
@@ -1276,20 +1310,24 @@
(fn (fn
() ()
(let (let
((tgt (cond ((and (= (tp-type) "ident") (or (= (tp-val) "element") (= (tp-val) "global") (= (tp-val) "local"))) (do (adv!) (parse-expr))) (true (parse-expr))))) ((tgt-raw (cond ((and (= (tp-type) "ident") (or (= (tp-val) "element") (= (tp-val) "global") (= (tp-val) "local"))) (do (adv!) (parse-expr))) (true (parse-expr)))))
(cond (let
((match-kw "to") ((tgt (if (= (tp-type) "attr") (let ((attr-name (get (adv!) "value"))) (list (quote attr) attr-name tgt-raw)) tgt-raw)))
(let ((value (parse-expr))) (list (quote set!) tgt value))) (cond
((match-kw "on") ((match-kw "to")
(let (let
((target (parse-expr))) ((value (parse-expr)))
(if (list (quote set!) tgt value)))
(match-kw "to") ((match-kw "on")
(let (let
((value (parse-expr))) ((target (parse-expr)))
(list (quote set-on!) tgt target value)) (if
(list (quote set-on) tgt target)))) (match-kw "to")
(true (error (str "Expected to/on at position " p))))))) (let
((value (parse-expr)))
(list (quote set-on!) tgt target value))
(list (quote set-on) tgt target))))
(true (error (str "Expected to/on at position " p))))))))
(define (define
parse-put-cmd parse-put-cmd
(fn (fn

View File

@@ -294,10 +294,13 @@
hs-add-to! hs-add-to!
(fn (fn
(value target) (value target)
(if (cond
(list? target) ((list? target)
(append target (list value)) (if
(host-call target "push" value)))) (some (fn (x) (= x value)) target)
target
(append target (list value))))
(true (do (host-call target "push" value) target)))))
;; First/last within a specific scope. ;; First/last within a specific scope.
(define (define
@@ -664,7 +667,11 @@
(cond (cond
((nil? target) value) ((nil? target) value)
((string? target) (str target value)) ((string? target) (str target value))
((list? target) (append target (list value))) ((list? target)
(if
(some (fn (x) (= x value)) target)
target
(append target (list value))))
((hs-element? target) ((hs-element? target)
(do (do
(dom-insert-adjacent-html target "beforeend" (str value)) (dom-insert-adjacent-html target "beforeend" (str value))
@@ -688,6 +695,61 @@
((fmt (cond ((nil? format) "text") ((or (= format "JSON") (= format "json") (= format "Object") (= format "object")) "json") ((or (= format "HTML") (= format "html")) "html") ((or (= format "Response") (= format "response")) "response") ((or (= format "Text") (= format "text")) "text") (true format)))) ((fmt (cond ((nil? format) "text") ((or (= format "JSON") (= format "json") (= format "Object") (= format "object")) "json") ((or (= format "HTML") (= format "html")) "html") ((or (= format "Response") (= format "response")) "response") ((or (= format "Text") (= format "text")) "text") (true format))))
(perform (list "io-fetch" url fmt))))) (perform (list "io-fetch" url fmt)))))
;; Array slicing (inclusive both ends) ;; Array slicing (inclusive both ends)
(define
hs-json-escape
(fn
(s)
(str
"\""
(let
((out "") (i 0) (n (string-length s)))
(define
walk
(fn
()
(when
(< i n)
(let
((c (substring s i (+ i 1))))
(set!
out
(cond
((= c "\\") (str out "\\\\"))
((= c "\"") (str out "\\\""))
((= c "\n") (str out "\\n"))
((= c "\r") (str out "\\r"))
((= c "\t") (str out "\\t"))
(true (str out c))))
(set! i (+ i 1))
(walk)))))
(walk)
out)
"\"")))
;; Collection: sorted by
(define
hs-json-stringify
(fn
(v)
(cond
((nil? v) "null")
((= v true) "true")
((= v false) "false")
((number? v) (str v))
((string? v) (hs-json-escape v))
((list? v) (str "[" (join "," (map hs-json-stringify v)) "]"))
((dict? v)
(str
"{"
(join
","
(map
(fn
(k)
(str (hs-json-escape k) ":" (hs-json-stringify (get v k))))
(keys v)))
"}"))
(true (hs-json-escape (str v))))))
;; Collection: sorted by descending
(define (define
hs-coerce hs-coerce
(fn (fn
@@ -705,19 +767,39 @@
((= type-name "Bool") (not (hs-falsy? value))) ((= type-name "Bool") (not (hs-falsy? value)))
((= type-name "Boolean") (not (hs-falsy? value))) ((= type-name "Boolean") (not (hs-falsy? value)))
((= type-name "Array") (if (list? value) value (list value))) ((= type-name "Array") (if (list? value) value (list value)))
((= type-name "HTML") (str value)) ((= type-name "HTML")
(cond
((list? value) (join "" (map (fn (x) (str x)) value)))
((hs-element? value) (host-get value "outerHTML"))
(true (str value))))
((= type-name "JSON") ((= type-name "JSON")
(cond (cond
((string? value) (guard (_e (true value)) (json-parse value))) ((string? value) (guard (_e (true value)) (json-parse value)))
((dict? value) (json-stringify value)) ((dict? value) (hs-json-stringify value))
((list? value) (json-stringify value)) ((list? value) (hs-json-stringify value))
(true value))) (true value)))
((= type-name "Object") ((= type-name "Object")
(if (if
(string? value) (string? value)
(guard (_e (true value)) (json-parse value)) (guard (_e (true value)) (json-parse value))
value)) value))
((= type-name "JSONString") (json-stringify value)) ((= type-name "JSONString") (hs-json-stringify value))
((= type-name "FormEncoded")
(if
(dict? value)
(join
"&"
(map
(fn
(k)
(let
((v (get value k)))
(if
(list? v)
(join "&" (map (fn (item) (str k "=" item)) v))
(str k "=" v))))
(keys value)))
(str value)))
((or (= type-name "Fixed") (= type-name "Fixed:") (starts-with? type-name "Fixed:")) ((or (= type-name "Fixed") (= type-name "Fixed:") (starts-with? type-name "Fixed:"))
(let (let
((digits (if (> (string-length type-name) 6) (+ (substring type-name 6 (string-length type-name)) 0) 0)) ((digits (if (> (string-length type-name) 6) (+ (substring type-name 6 (string-length type-name)) 0) 0))
@@ -774,7 +856,7 @@
(map (fn (k) (list k (get value k))) (keys value)) (map (fn (k) (list k (get value k))) (keys value))
value)) value))
(true value)))) (true value))))
;; Collection: sorted by ;; Collection: split by
(define (define
hs-gather-form-nodes hs-gather-form-nodes
(fn (fn
@@ -808,11 +890,11 @@
(each 0))))))))) (each 0)))))))))
(walk root) (walk root)
acc))) acc)))
;; Collection: sorted by descending ;; Collection: joined by
(define (define
hs-values-from-nodes hs-values-from-nodes
(fn (nodes) (reduce hs-values-absorb (dict) nodes))) (fn (nodes) (reduce hs-values-absorb (dict) nodes)))
;; Collection: split by
(define (define
hs-value-of-node hs-value-of-node
(fn (fn
@@ -828,7 +910,7 @@
((or (= typ "checkbox") (= typ "radio")) ((or (= typ "checkbox") (= typ "radio"))
(if (host-get node "checked") (host-get node "value") nil)) (if (host-get node "checked") (host-get node "value") nil))
(true (host-get node "value")))))) (true (host-get node "value"))))))
;; Collection: joined by
(define (define
hs-select-multi-values hs-select-multi-values
(fn (fn

View File

@@ -18,7 +18,17 @@
(target value) (target value)
(if (if
(not (list? target)) (not (list? target))
(list (quote set!) target value) (if
(= target (quote the-result))
(list
(quote let)
(list (list (quote __hs-r) value))
(list
(quote begin)
(list (quote set!) (quote the-result) (quote __hs-r))
(list (quote set!) (quote it) (quote __hs-r))
(quote __hs-r)))
(list (quote set!) target value))
(let (let
((th (first target))) ((th (first target)))
(cond (cond
@@ -1169,15 +1179,71 @@
(let (let
((tgt-expr (cond ((and (list? raw-tgt) (= (first raw-tgt) (quote query))) (list (quote hs-query-all) (nth raw-tgt 1))) ((and (list? raw-tgt) (= (first raw-tgt) (quote in?)) (list? (nth raw-tgt 1)) (= (first (nth raw-tgt 1)) (quote query))) (list (quote host-call) (hs-to-sx (nth raw-tgt 2)) "querySelectorAll" (nth (nth raw-tgt 1) 1))) (true (hs-to-sx raw-tgt))))) ((tgt-expr (cond ((and (list? raw-tgt) (= (first raw-tgt) (quote query))) (list (quote hs-query-all) (nth raw-tgt 1))) ((and (list? raw-tgt) (= (first raw-tgt) (quote in?)) (list? (nth raw-tgt 1)) (= (first (nth raw-tgt 1)) (quote query))) (list (quote host-call) (hs-to-sx (nth raw-tgt 2)) "querySelectorAll" (nth (nth raw-tgt 1) 1))) (true (hs-to-sx raw-tgt)))))
(list (list
(quote for-each) (quote let)
(list (list
(quote fn)
(list (quote it))
(list (list
(quote when) (quote __hs-matched)
(hs-to-sx when-cond) (list
(list (quote dom-add-class) (quote it) cls))) (quote filter)
tgt-expr)))) (list
(quote fn)
(list (quote it))
(hs-to-sx when-cond))
tgt-expr)))
(list
(quote begin)
(list
(quote set!)
(quote the-result)
(quote __hs-matched))
(list (quote set!) (quote it) (quote __hs-matched))
(list
(quote for-each)
(list
(quote fn)
(list (quote it))
(list (quote dom-add-class) (quote it) cls))
(quote __hs-matched))
(quote __hs-matched))))))
((= head (quote add-attr-when))
(let
((attr-name (nth ast 1))
(attr-val (hs-to-sx (nth ast 2)))
(raw-tgt (nth ast 3))
(when-cond (nth ast 4)))
(let
((tgt-expr (cond ((and (list? raw-tgt) (= (first raw-tgt) (quote query))) (list (quote hs-query-all) (nth raw-tgt 1))) (true (hs-to-sx raw-tgt)))))
(list
(quote let)
(list
(list
(quote __hs-matched)
(list
(quote filter)
(list
(quote fn)
(list (quote it))
(hs-to-sx when-cond))
tgt-expr)))
(list
(quote begin)
(list
(quote set!)
(quote the-result)
(quote __hs-matched))
(list (quote set!) (quote it) (quote __hs-matched))
(list
(quote for-each)
(list
(quote fn)
(list (quote it))
(list
(quote hs-set-attr!)
(quote it)
attr-name
attr-val))
(quote __hs-matched))
(quote __hs-matched))))))
((= head (quote multi-remove-class)) ((= head (quote multi-remove-class))
(let (let
((target (hs-to-sx (nth ast 1))) ((target (hs-to-sx (nth ast 1)))
@@ -1598,11 +1664,17 @@
((= head (quote append!)) ((= head (quote append!))
(let (let
((tgt (hs-to-sx (nth ast 2))) ((tgt (hs-to-sx (nth ast 2)))
(val (hs-to-sx (nth ast 1)))) (val (hs-to-sx (nth ast 1)))
(if (raw-tgt (nth ast 2)))
(symbol? tgt) (cond
(list (quote set!) tgt (list (quote hs-append) tgt val)) ((symbol? tgt)
(list (quote hs-append!) val tgt)))) (list
(quote set!)
tgt
(list (quote hs-append) tgt val)))
((and (list? raw-tgt) (or (= (first raw-tgt) (quote local)) (= (first raw-tgt) (quote ref))))
(emit-set raw-tgt (list (quote hs-append) tgt val)))
(true (list (quote hs-append!) val tgt)))))
((= head (quote tell)) ((= head (quote tell))
(let (let
((tgt (hs-to-sx (nth ast 1)))) ((tgt (hs-to-sx (nth ast 1))))

View File

@@ -954,7 +954,35 @@
(when (= (tp-type) "bracket-close") (adv!)) (when (= (tp-type) "bracket-close") (adv!))
(let (let
((tgt (parse-tgt-kw "to" (list (quote me))))) ((tgt (parse-tgt-kw "to" (list (quote me)))))
(list (quote add-attr) attr-name attr-val tgt)))))) (let
((when-clause (if (match-kw "when") (parse-expr) nil)))
(if
when-clause
(list
(quote add-attr-when)
attr-name
attr-val
tgt
when-clause)
(list (quote add-attr) attr-name attr-val tgt))))))))
((= (tp-type) "attr")
(let
((attr-name (get (adv!) "value")))
(let
((attr-val (if (and (= (tp-type) "op") (= (tp-val) "=")) (do (adv!) (parse-expr)) "")))
(let
((tgt (if (match-kw "to") (parse-expr) (list (quote me)))))
(let
((when-clause (if (match-kw "when") (parse-expr) nil)))
(if
when-clause
(list
(quote add-attr-when)
attr-name
attr-val
tgt
when-clause)
(list (quote add-attr) attr-name attr-val tgt)))))))
(true (true
(let (let
((value (parse-expr))) ((value (parse-expr)))
@@ -991,6 +1019,12 @@
(cons (cons
(quote multi-remove-class) (quote multi-remove-class)
(cons tgt (cons cls extra-classes))))))) (cons tgt (cons cls extra-classes)))))))
((= (tp-type) "attr")
(let
((attr-name (get (adv!) "value")))
(let
((tgt (if (match-kw "from") (parse-expr) (list (quote me)))))
(list (quote remove-attr) attr-name tgt))))
((and (= (tp-type) "bracket-open") (= (tp-val) "[")) ((and (= (tp-type) "bracket-open") (= (tp-val) "["))
(do (do
(adv!) (adv!)
@@ -1276,20 +1310,24 @@
(fn (fn
() ()
(let (let
((tgt (cond ((and (= (tp-type) "ident") (or (= (tp-val) "element") (= (tp-val) "global") (= (tp-val) "local"))) (do (adv!) (parse-expr))) (true (parse-expr))))) ((tgt-raw (cond ((and (= (tp-type) "ident") (or (= (tp-val) "element") (= (tp-val) "global") (= (tp-val) "local"))) (do (adv!) (parse-expr))) (true (parse-expr)))))
(cond (let
((match-kw "to") ((tgt (if (= (tp-type) "attr") (let ((attr-name (get (adv!) "value"))) (list (quote attr) attr-name tgt-raw)) tgt-raw)))
(let ((value (parse-expr))) (list (quote set!) tgt value))) (cond
((match-kw "on") ((match-kw "to")
(let (let
((target (parse-expr))) ((value (parse-expr)))
(if (list (quote set!) tgt value)))
(match-kw "to") ((match-kw "on")
(let (let
((value (parse-expr))) ((target (parse-expr)))
(list (quote set-on!) tgt target value)) (if
(list (quote set-on) tgt target)))) (match-kw "to")
(true (error (str "Expected to/on at position " p))))))) (let
((value (parse-expr)))
(list (quote set-on!) tgt target value))
(list (quote set-on) tgt target))))
(true (error (str "Expected to/on at position " p))))))))
(define (define
parse-put-cmd parse-put-cmd
(fn (fn

View File

@@ -294,10 +294,13 @@
hs-add-to! hs-add-to!
(fn (fn
(value target) (value target)
(if (cond
(list? target) ((list? target)
(append target (list value)) (if
(host-call target "push" value)))) (some (fn (x) (= x value)) target)
target
(append target (list value))))
(true (do (host-call target "push" value) target)))))
;; First/last within a specific scope. ;; First/last within a specific scope.
(define (define
@@ -664,7 +667,11 @@
(cond (cond
((nil? target) value) ((nil? target) value)
((string? target) (str target value)) ((string? target) (str target value))
((list? target) (append target (list value))) ((list? target)
(if
(some (fn (x) (= x value)) target)
target
(append target (list value))))
((hs-element? target) ((hs-element? target)
(do (do
(dom-insert-adjacent-html target "beforeend" (str value)) (dom-insert-adjacent-html target "beforeend" (str value))
@@ -688,6 +695,61 @@
((fmt (cond ((nil? format) "text") ((or (= format "JSON") (= format "json") (= format "Object") (= format "object")) "json") ((or (= format "HTML") (= format "html")) "html") ((or (= format "Response") (= format "response")) "response") ((or (= format "Text") (= format "text")) "text") (true format)))) ((fmt (cond ((nil? format) "text") ((or (= format "JSON") (= format "json") (= format "Object") (= format "object")) "json") ((or (= format "HTML") (= format "html")) "html") ((or (= format "Response") (= format "response")) "response") ((or (= format "Text") (= format "text")) "text") (true format))))
(perform (list "io-fetch" url fmt))))) (perform (list "io-fetch" url fmt)))))
;; Array slicing (inclusive both ends) ;; Array slicing (inclusive both ends)
(define
hs-json-escape
(fn
(s)
(str
"\""
(let
((out "") (i 0) (n (string-length s)))
(define
walk
(fn
()
(when
(< i n)
(let
((c (substring s i (+ i 1))))
(set!
out
(cond
((= c "\\") (str out "\\\\"))
((= c "\"") (str out "\\\""))
((= c "\n") (str out "\\n"))
((= c "\r") (str out "\\r"))
((= c "\t") (str out "\\t"))
(true (str out c))))
(set! i (+ i 1))
(walk)))))
(walk)
out)
"\"")))
;; Collection: sorted by
(define
hs-json-stringify
(fn
(v)
(cond
((nil? v) "null")
((= v true) "true")
((= v false) "false")
((number? v) (str v))
((string? v) (hs-json-escape v))
((list? v) (str "[" (join "," (map hs-json-stringify v)) "]"))
((dict? v)
(str
"{"
(join
","
(map
(fn
(k)
(str (hs-json-escape k) ":" (hs-json-stringify (get v k))))
(keys v)))
"}"))
(true (hs-json-escape (str v))))))
;; Collection: sorted by descending
(define (define
hs-coerce hs-coerce
(fn (fn
@@ -705,19 +767,39 @@
((= type-name "Bool") (not (hs-falsy? value))) ((= type-name "Bool") (not (hs-falsy? value)))
((= type-name "Boolean") (not (hs-falsy? value))) ((= type-name "Boolean") (not (hs-falsy? value)))
((= type-name "Array") (if (list? value) value (list value))) ((= type-name "Array") (if (list? value) value (list value)))
((= type-name "HTML") (str value)) ((= type-name "HTML")
(cond
((list? value) (join "" (map (fn (x) (str x)) value)))
((hs-element? value) (host-get value "outerHTML"))
(true (str value))))
((= type-name "JSON") ((= type-name "JSON")
(cond (cond
((string? value) (guard (_e (true value)) (json-parse value))) ((string? value) (guard (_e (true value)) (json-parse value)))
((dict? value) (json-stringify value)) ((dict? value) (hs-json-stringify value))
((list? value) (json-stringify value)) ((list? value) (hs-json-stringify value))
(true value))) (true value)))
((= type-name "Object") ((= type-name "Object")
(if (if
(string? value) (string? value)
(guard (_e (true value)) (json-parse value)) (guard (_e (true value)) (json-parse value))
value)) value))
((= type-name "JSONString") (json-stringify value)) ((= type-name "JSONString") (hs-json-stringify value))
((= type-name "FormEncoded")
(if
(dict? value)
(join
"&"
(map
(fn
(k)
(let
((v (get value k)))
(if
(list? v)
(join "&" (map (fn (item) (str k "=" item)) v))
(str k "=" v))))
(keys value)))
(str value)))
((or (= type-name "Fixed") (= type-name "Fixed:") (starts-with? type-name "Fixed:")) ((or (= type-name "Fixed") (= type-name "Fixed:") (starts-with? type-name "Fixed:"))
(let (let
((digits (if (> (string-length type-name) 6) (+ (substring type-name 6 (string-length type-name)) 0) 0)) ((digits (if (> (string-length type-name) 6) (+ (substring type-name 6 (string-length type-name)) 0) 0))
@@ -774,7 +856,7 @@
(map (fn (k) (list k (get value k))) (keys value)) (map (fn (k) (list k (get value k))) (keys value))
value)) value))
(true value)))) (true value))))
;; Collection: sorted by ;; Collection: split by
(define (define
hs-gather-form-nodes hs-gather-form-nodes
(fn (fn
@@ -808,11 +890,11 @@
(each 0))))))))) (each 0)))))))))
(walk root) (walk root)
acc))) acc)))
;; Collection: sorted by descending ;; Collection: joined by
(define (define
hs-values-from-nodes hs-values-from-nodes
(fn (nodes) (reduce hs-values-absorb (dict) nodes))) (fn (nodes) (reduce hs-values-absorb (dict) nodes)))
;; Collection: split by
(define (define
hs-value-of-node hs-value-of-node
(fn (fn
@@ -828,7 +910,7 @@
((or (= typ "checkbox") (= typ "radio")) ((or (= typ "checkbox") (= typ "radio"))
(if (host-get node "checked") (host-get node "value") nil)) (if (host-get node "checked") (host-get node "value") nil))
(true (host-get node "value")))))) (true (host-get node "value"))))))
;; Collection: joined by
(define (define
hs-select-multi-values hs-select-multi-values
(fn (fn