Compare commits
35 Commits
hs-e36-web
...
loops/hs
| Author | SHA1 | Date | |
|---|---|---|---|
| 6169c99036 | |||
| 41fac7ac29 | |||
| 4c48a8dd57 | |||
| a48110417b | |||
| f2993f0582 | |||
| da2e6b1bca | |||
| f38558fcc1 | |||
| daea280837 | |||
| 11917f1bfa | |||
| 875e9ba317 | |||
| f715d23e10 | |||
| 5a76a04010 | |||
| a0bbf74c01 | |||
| 35f498ec80 | |||
| 037acc7998 | |||
| 247bd85cda | |||
| b41d9d143b | |||
| d663c91f4b | |||
| 4c43918a99 | |||
| d7244d1dc8 | |||
| 1b1b67c72e | |||
| 3a755947ef | |||
| 880503e2b6 | |||
| e989ff3865 | |||
| 8e2a633b7f | |||
| cc2a296306 | |||
| 9c8da50003 | |||
| 3003c8a069 | |||
| 8c62137d32 | |||
| 573f9fa4b3 | |||
| 8ac669c739 | |||
| 8e4bdb7216 | |||
| 20a643806b | |||
| ea1bdab82c | |||
| 04164aa2d4 |
@@ -918,6 +918,12 @@
|
|||||||
(let
|
(let
|
||||||
((ch (nth raw i)))
|
((ch (nth raw i)))
|
||||||
(if
|
(if
|
||||||
|
(and (= ch "\\") (< (+ i 1) n) (= (nth raw (+ i 1)) "$"))
|
||||||
|
(do
|
||||||
|
(set! buf (str buf "$"))
|
||||||
|
(set! i (+ i 2))
|
||||||
|
(tpl-collect))
|
||||||
|
(if
|
||||||
(and (= ch "$") (< (+ i 1) n))
|
(and (= ch "$") (< (+ i 1) n))
|
||||||
(if
|
(if
|
||||||
(= (nth raw (+ i 1)) "{")
|
(= (nth raw (+ i 1)) "{")
|
||||||
@@ -956,7 +962,7 @@
|
|||||||
(do
|
(do
|
||||||
(set! buf (str buf ch))
|
(set! buf (str buf ch))
|
||||||
(set! i (+ i 1))
|
(set! i (+ i 1))
|
||||||
(tpl-collect)))))))
|
(tpl-collect))))))))
|
||||||
(tpl-collect)
|
(tpl-collect)
|
||||||
(tpl-flush)
|
(tpl-flush)
|
||||||
(cons (quote str) parts))))
|
(cons (quote str) parts))))
|
||||||
@@ -1857,7 +1863,7 @@
|
|||||||
(list (quote fn) (list) (hs-to-sx (nth ast 1)))
|
(list (quote fn) (list) (hs-to-sx (nth ast 1)))
|
||||||
(list (quote fn) (list) (hs-to-sx (nth ast 2)))))
|
(list (quote fn) (list) (hs-to-sx (nth ast 2)))))
|
||||||
((= head (quote fetch))
|
((= head (quote fetch))
|
||||||
(list (quote hs-fetch) (hs-to-sx (nth ast 1)) (nth ast 2)))
|
(list (quote hs-fetch) (hs-to-sx (nth ast 1)) (nth ast 2) (nth ast 3) (quote me)))
|
||||||
((= head (quote fetch-gql))
|
((= head (quote fetch-gql))
|
||||||
(list
|
(list
|
||||||
(quote hs-fetch-gql)
|
(quote hs-fetch-gql)
|
||||||
|
|||||||
@@ -21,6 +21,16 @@
|
|||||||
adv!
|
adv!
|
||||||
(fn () (let ((t (nth tokens p))) (set! p (+ p 1)) t)))
|
(fn () (let ((t (nth tokens p))) (set! p (+ p 1)) t)))
|
||||||
(define at-end? (fn () (or (>= p tok-len) (= (tp-type) "eof"))))
|
(define at-end? (fn () (or (>= p tok-len) (= (tp-type) "eof"))))
|
||||||
|
(define cur-start (fn () (if (< p tok-len) (get (tp) "pos") 0)))
|
||||||
|
(define cur-line (fn () (if (< p tok-len) (get (tp) "line") 1)))
|
||||||
|
(define
|
||||||
|
prev-end
|
||||||
|
(fn () (if (> p 0) (get (nth tokens (- p 1)) "end") 0)))
|
||||||
|
(define
|
||||||
|
hs-ast-wrap
|
||||||
|
(fn
|
||||||
|
(raw kind start end-pos line fields)
|
||||||
|
(if hs-span-mode {:children raw :end end-pos :kind kind :line line :src src :start start :hs-ast true :fields fields} raw)))
|
||||||
(define
|
(define
|
||||||
match-kw
|
match-kw
|
||||||
(fn
|
(fn
|
||||||
@@ -69,19 +79,40 @@
|
|||||||
parse-prop-chain
|
parse-prop-chain
|
||||||
(fn
|
(fn
|
||||||
(base)
|
(base)
|
||||||
(if
|
(let
|
||||||
(and (= (tp-type) "class") (not (at-end?)))
|
((base-start (if (and (dict? base) (get base :hs-ast)) (get base :start) (cur-start)))
|
||||||
(let
|
(base-line
|
||||||
((prop (tp-val)))
|
(if
|
||||||
(do
|
(and (dict? base) (get base :hs-ast))
|
||||||
(adv!)
|
(get base :line)
|
||||||
(parse-prop-chain (list (make-symbol ".") base prop))))
|
(cur-line))))
|
||||||
(if
|
(if
|
||||||
(= (tp-type) "paren-open")
|
(and (= (tp-type) "class") (not (at-end?)))
|
||||||
(let
|
(let
|
||||||
((args (parse-call-args)))
|
((prop (tp-val)))
|
||||||
(parse-prop-chain (list (quote method-call) base args)))
|
(do
|
||||||
base))))
|
(adv!)
|
||||||
|
(parse-prop-chain
|
||||||
|
(hs-ast-wrap
|
||||||
|
(list (make-symbol ".") base prop)
|
||||||
|
"member"
|
||||||
|
base-start
|
||||||
|
(prev-end)
|
||||||
|
base-line
|
||||||
|
{:root base}))))
|
||||||
|
(if
|
||||||
|
(= (tp-type) "paren-open")
|
||||||
|
(let
|
||||||
|
((args (parse-call-args)))
|
||||||
|
(parse-prop-chain
|
||||||
|
(hs-ast-wrap
|
||||||
|
(list (quote method-call) base args)
|
||||||
|
"call"
|
||||||
|
base-start
|
||||||
|
(prev-end)
|
||||||
|
base-line
|
||||||
|
{:root base})))
|
||||||
|
base)))))
|
||||||
(define
|
(define
|
||||||
parse-trav
|
parse-trav
|
||||||
(fn
|
(fn
|
||||||
@@ -124,8 +155,24 @@
|
|||||||
(let
|
(let
|
||||||
((typ (tp-type)) (val (tp-val)))
|
((typ (tp-type)) (val (tp-val)))
|
||||||
(cond
|
(cond
|
||||||
((= typ "number") (do (adv!) (parse-dur val)))
|
((= typ "number")
|
||||||
((= typ "string") (do (adv!) val))
|
(let
|
||||||
|
((s (cur-start)) (l (cur-line)))
|
||||||
|
(do
|
||||||
|
(adv!)
|
||||||
|
(hs-ast-wrap
|
||||||
|
(parse-dur val)
|
||||||
|
"number"
|
||||||
|
s
|
||||||
|
(prev-end)
|
||||||
|
l
|
||||||
|
{}))))
|
||||||
|
((= typ "string")
|
||||||
|
(let
|
||||||
|
((s (cur-start)) (l (cur-line)))
|
||||||
|
(do
|
||||||
|
(adv!)
|
||||||
|
(hs-ast-wrap val "string" s (prev-end) l {}))))
|
||||||
((= typ "template") (do (adv!) (list (quote template) val)))
|
((= typ "template") (do (adv!) (list (quote template) val)))
|
||||||
((and (= typ "keyword") (= val "true")) (do (adv!) true))
|
((and (= typ "keyword") (= val "true")) (do (adv!) true))
|
||||||
((and (= typ "keyword") (= val "false")) (do (adv!) false))
|
((and (= typ "keyword") (= val "false")) (do (adv!) false))
|
||||||
@@ -190,19 +237,38 @@
|
|||||||
((and (= typ "keyword") (= val "last"))
|
((and (= typ "keyword") (= val "last"))
|
||||||
(do (adv!) (parse-pos-kw (quote last))))
|
(do (adv!) (parse-pos-kw (quote last))))
|
||||||
((= typ "id")
|
((= typ "id")
|
||||||
(do (adv!) (list (quote query) (str "#" val))))
|
(let
|
||||||
|
((s (cur-start)) (l (cur-line)))
|
||||||
|
(do
|
||||||
|
(adv!)
|
||||||
|
(hs-ast-wrap
|
||||||
|
(list (quote query) (str "#" val))
|
||||||
|
"selector"
|
||||||
|
s
|
||||||
|
(prev-end)
|
||||||
|
l
|
||||||
|
{}))))
|
||||||
((= typ "selector")
|
((= typ "selector")
|
||||||
(do
|
(let
|
||||||
(adv!)
|
((s (cur-start)) (l (cur-line)))
|
||||||
(if
|
(do
|
||||||
(and (= (tp-type) "keyword") (= (tp-val) "in"))
|
(adv!)
|
||||||
(do
|
(hs-ast-wrap
|
||||||
(adv!)
|
(if
|
||||||
(list
|
(and (= (tp-type) "keyword") (= (tp-val) "in"))
|
||||||
(quote query-scoped)
|
(do
|
||||||
val
|
(adv!)
|
||||||
(parse-cmp (parse-arith (parse-poss (parse-atom))))))
|
(list
|
||||||
(list (quote query) val))))
|
(quote query-scoped)
|
||||||
|
val
|
||||||
|
(parse-cmp
|
||||||
|
(parse-arith (parse-poss (parse-atom))))))
|
||||||
|
(list (quote query) val))
|
||||||
|
"selector"
|
||||||
|
s
|
||||||
|
(prev-end)
|
||||||
|
l
|
||||||
|
{}))))
|
||||||
((= typ "attr")
|
((= typ "attr")
|
||||||
(do (adv!) (list (quote attr) val (list (quote me)))))
|
(do (adv!) (list (quote attr) val (list (quote me)))))
|
||||||
((= typ "style")
|
((= typ "style")
|
||||||
@@ -219,8 +285,29 @@
|
|||||||
(adv!)
|
(adv!)
|
||||||
(list (quote dom-ref) name (list (quote me)))))))
|
(list (quote dom-ref) name (list (quote me)))))))
|
||||||
((= typ "class")
|
((= typ "class")
|
||||||
(do (adv!) (list (quote query) (str "." val))))
|
(let
|
||||||
((= typ "ident") (do (adv!) (list (quote ref) val)))
|
((s (cur-start)) (l (cur-line)))
|
||||||
|
(do
|
||||||
|
(adv!)
|
||||||
|
(hs-ast-wrap
|
||||||
|
(list (quote query) (str "." val))
|
||||||
|
"selector"
|
||||||
|
s
|
||||||
|
(prev-end)
|
||||||
|
l
|
||||||
|
{}))))
|
||||||
|
((= typ "ident")
|
||||||
|
(let
|
||||||
|
((s (cur-start)) (l (cur-line)))
|
||||||
|
(do
|
||||||
|
(adv!)
|
||||||
|
(hs-ast-wrap
|
||||||
|
(list (quote ref) val)
|
||||||
|
"ref"
|
||||||
|
s
|
||||||
|
(prev-end)
|
||||||
|
l
|
||||||
|
{}))))
|
||||||
((= typ "paren-open")
|
((= typ "paren-open")
|
||||||
(do
|
(do
|
||||||
(adv!)
|
(adv!)
|
||||||
@@ -463,7 +550,9 @@
|
|||||||
(list
|
(list
|
||||||
(quote not)
|
(quote not)
|
||||||
(list (quote eq-ignore-case) left right)))
|
(list (quote eq-ignore-case) left right)))
|
||||||
(list (quote not) (list (quote =) left right)))))))
|
(list
|
||||||
|
(quote not)
|
||||||
|
(list (quote hs-id=) left right)))))))
|
||||||
((match-kw "empty") (list (quote empty?) left))
|
((match-kw "empty") (list (quote empty?) left))
|
||||||
((match-kw "less")
|
((match-kw "less")
|
||||||
(do
|
(do
|
||||||
@@ -941,8 +1030,7 @@
|
|||||||
((prop (get (adv!) "value")))
|
((prop (get (adv!) "value")))
|
||||||
(when (= (tp-type) "colon") (adv!))
|
(when (= (tp-type) "colon") (adv!))
|
||||||
(let
|
(let
|
||||||
((val (tp-val)))
|
((val (if (and (= (tp-type) "ident") (= (tp-val) "$")) (do (adv!) (when (= (tp-type) "brace-open") (adv!)) (if (= (tp-type) "brace-close") (do (adv!) (if (= (tp-type) "brace-open") (do (adv!) (let ((inner (parse-expr))) (when (= (tp-type) "brace-close") (adv!)) inner)) "")) (let ((expr (parse-expr))) (when (= (tp-type) "brace-close") (adv!)) expr))) (get (adv!) "value"))))
|
||||||
(adv!)
|
|
||||||
(set! pairs (cons (list prop val) pairs))
|
(set! pairs (cons (list prop val) pairs))
|
||||||
(collect-pairs!))))))
|
(collect-pairs!))))))
|
||||||
(collect-pairs!)
|
(collect-pairs!)
|
||||||
@@ -1684,7 +1772,7 @@
|
|||||||
((url (if (and (= (tp-type) "keyword") (= (tp-val) "from")) (do (adv!) (parse-arith (parse-poss (parse-atom)))) nil)))
|
((url (if (and (= (tp-type) "keyword") (= (tp-val) "from")) (do (adv!) (parse-arith (parse-poss (parse-atom)))) nil)))
|
||||||
(list (quote fetch-gql) gql-source url))))
|
(list (quote fetch-gql) gql-source url))))
|
||||||
(let
|
(let
|
||||||
((url-atom (if (and (= (tp-type) "op") (= (tp-val) "/")) (do (adv!) (let ((path-parts (list "/"))) (define read-path (fn () (when (and (not (at-end?)) (or (= (tp-type) "ident") (= (tp-type) "op") (= (tp-type) "dot") (= (tp-type) "number"))) (append! path-parts (tp-val)) (adv!) (read-path)))) (read-path) (join "" path-parts))) (parse-atom))))
|
((url-atom (if (and (= (tp-type) "op") (= (tp-val) "/")) (do (adv!) (let ((path-parts (list "/"))) (define read-path (fn () (when (and (not (at-end?)) (or (and (= (tp-type) "ident") (not (string-contains? (tp-val) "'"))) (= (tp-type) "op") (= (tp-type) "dot") (= (tp-type) "number"))) (append! path-parts (tp-val)) (adv!) (read-path)))) (read-path) (join "" path-parts))) (parse-atom))))
|
||||||
(let
|
(let
|
||||||
((url (if (nil? url-atom) url-atom (parse-arith (parse-poss url-atom)))))
|
((url (if (nil? url-atom) url-atom (parse-arith (parse-poss url-atom)))))
|
||||||
(let
|
(let
|
||||||
@@ -1700,7 +1788,27 @@
|
|||||||
((fmt-after (if (and (not fmt-before) (match-kw "as")) (do (when (and (or (= (tp-type) "ident") (= (tp-type) "keyword")) (or (= (tp-val) "an") (= (tp-val) "a"))) (adv!)) (let ((f (tp-val))) (adv!) f)) nil)))
|
((fmt-after (if (and (not fmt-before) (match-kw "as")) (do (when (and (or (= (tp-type) "ident") (= (tp-type) "keyword")) (or (= (tp-val) "an") (= (tp-val) "a"))) (adv!)) (let ((f (tp-val))) (adv!) f)) nil)))
|
||||||
(let
|
(let
|
||||||
((fmt (or fmt-before fmt-after "text")))
|
((fmt (or fmt-before fmt-after "text")))
|
||||||
(list (quote fetch) url fmt)))))))))
|
(let
|
||||||
|
((do-not-throw
|
||||||
|
(cond
|
||||||
|
((and (or (= (tp-type) "keyword") (= (tp-type) "ident")) (= (tp-val) "do"))
|
||||||
|
(do
|
||||||
|
(adv!)
|
||||||
|
(if (and (or (= (tp-type) "keyword") (= (tp-type) "ident")) (= (tp-val) "not"))
|
||||||
|
(do
|
||||||
|
(adv!)
|
||||||
|
(if (and (or (= (tp-type) "keyword") (= (tp-type) "ident")) (= (tp-val) "throw"))
|
||||||
|
(do (adv!) true)
|
||||||
|
false))
|
||||||
|
false)))
|
||||||
|
((and (= (tp-type) "ident") (= (tp-val) "don't"))
|
||||||
|
(do
|
||||||
|
(adv!)
|
||||||
|
(if (and (or (= (tp-type) "keyword") (= (tp-type) "ident")) (= (tp-val) "throw"))
|
||||||
|
(do (adv!) true)
|
||||||
|
false)))
|
||||||
|
(true false))))
|
||||||
|
(list (quote fetch) url fmt do-not-throw))))))))))
|
||||||
(define
|
(define
|
||||||
parse-call-args
|
parse-call-args
|
||||||
(fn
|
(fn
|
||||||
@@ -2021,7 +2129,21 @@
|
|||||||
((op (cond ((= val "+") (quote +)) ((= val "-") (quote -)) ((= val "*") (quote *)) ((= val "/") (quote /)) ((or (= val "%") (= val "mod")) (make-symbol "%")))))
|
((op (cond ((= val "+") (quote +)) ((= val "-") (quote -)) ((= val "*") (quote *)) ((= val "/") (quote /)) ((or (= val "%") (= val "mod")) (make-symbol "%")))))
|
||||||
(let
|
(let
|
||||||
((right (let ((a (parse-atom))) (if (nil? a) a (parse-poss a)))))
|
((right (let ((a (parse-atom))) (if (nil? a) a (parse-poss a)))))
|
||||||
(parse-arith (list op left right)))))
|
(let
|
||||||
|
((lhs-start (if (and (dict? left) (get left :hs-ast)) (get left :start) 0))
|
||||||
|
(lhs-line
|
||||||
|
(if
|
||||||
|
(and (dict? left) (get left :hs-ast))
|
||||||
|
(get left :line)
|
||||||
|
1)))
|
||||||
|
(parse-arith
|
||||||
|
(hs-ast-wrap
|
||||||
|
(list op left right)
|
||||||
|
"arith"
|
||||||
|
lhs-start
|
||||||
|
(prev-end)
|
||||||
|
lhs-line
|
||||||
|
{:rhs right :lhs left}))))))
|
||||||
left))))
|
left))))
|
||||||
(define
|
(define
|
||||||
parse-the-expr
|
parse-the-expr
|
||||||
@@ -2421,7 +2543,21 @@
|
|||||||
((and (= typ "keyword") (= val "put"))
|
((and (= typ "keyword") (= val "put"))
|
||||||
(do (adv!) (parse-put-cmd)))
|
(do (adv!) (parse-put-cmd)))
|
||||||
((and (= typ "keyword") (= val "if"))
|
((and (= typ "keyword") (= val "if"))
|
||||||
(do (adv!) (parse-if-cmd)))
|
(let
|
||||||
|
((s (cur-start)) (l (cur-line)))
|
||||||
|
(do
|
||||||
|
(adv!)
|
||||||
|
(let
|
||||||
|
((r (parse-if-cmd)))
|
||||||
|
(let
|
||||||
|
((tb (if (and (list? r) (> (len r) 2)) (nth r 2) nil)))
|
||||||
|
(hs-ast-wrap
|
||||||
|
r
|
||||||
|
"if"
|
||||||
|
s
|
||||||
|
(prev-end)
|
||||||
|
l
|
||||||
|
(if tb {:true-branch (if (and (list? tb) (= (first tb) (quote do))) (nth tb 1) tb)} {})))))))
|
||||||
((and (= typ "keyword") (= val "wait"))
|
((and (= typ "keyword") (= val "wait"))
|
||||||
(do (adv!) (parse-wait-cmd)))
|
(do (adv!) (parse-wait-cmd)))
|
||||||
((and (= typ "keyword") (= val "send"))
|
((and (= typ "keyword") (= val "send"))
|
||||||
@@ -2429,7 +2565,17 @@
|
|||||||
((and (= typ "keyword") (= val "trigger"))
|
((and (= typ "keyword") (= val "trigger"))
|
||||||
(do (adv!) (parse-trigger-cmd)))
|
(do (adv!) (parse-trigger-cmd)))
|
||||||
((and (= typ "keyword") (= val "log"))
|
((and (= typ "keyword") (= val "log"))
|
||||||
(do (adv!) (parse-log-cmd)))
|
(let
|
||||||
|
((s (cur-start)) (l (cur-line)))
|
||||||
|
(do
|
||||||
|
(adv!)
|
||||||
|
(hs-ast-wrap
|
||||||
|
(parse-log-cmd)
|
||||||
|
"cmd"
|
||||||
|
s
|
||||||
|
(prev-end)
|
||||||
|
l
|
||||||
|
{}))))
|
||||||
((and (= typ "keyword") (= val "increment"))
|
((and (= typ "keyword") (= val "increment"))
|
||||||
(do (adv!) (parse-inc-cmd)))
|
(do (adv!) (parse-inc-cmd)))
|
||||||
((and (= typ "keyword") (= val "decrement"))
|
((and (= typ "keyword") (= val "decrement"))
|
||||||
@@ -2469,7 +2615,17 @@
|
|||||||
((and (= typ "keyword") (= val "tell"))
|
((and (= typ "keyword") (= val "tell"))
|
||||||
(do (adv!) (parse-tell-cmd)))
|
(do (adv!) (parse-tell-cmd)))
|
||||||
((and (= typ "keyword") (= val "for"))
|
((and (= typ "keyword") (= val "for"))
|
||||||
(do (adv!) (parse-for-cmd)))
|
(let
|
||||||
|
((s (cur-start)) (l (cur-line)))
|
||||||
|
(do
|
||||||
|
(adv!)
|
||||||
|
(hs-ast-wrap
|
||||||
|
(parse-for-cmd)
|
||||||
|
"cmd"
|
||||||
|
s
|
||||||
|
(prev-end)
|
||||||
|
l
|
||||||
|
{}))))
|
||||||
((and (= typ "keyword") (= val "make"))
|
((and (= typ "keyword") (= val "make"))
|
||||||
(do (adv!) (parse-make-cmd)))
|
(do (adv!) (parse-make-cmd)))
|
||||||
((and (= typ "keyword") (= val "install"))
|
((and (= typ "keyword") (= val "install"))
|
||||||
@@ -2591,13 +2747,34 @@
|
|||||||
(true acc2)))))))
|
(true acc2)))))))
|
||||||
(let
|
(let
|
||||||
((cmds (cl-collect (list))))
|
((cmds (cl-collect (list))))
|
||||||
(cond
|
(define
|
||||||
((= (len cmds) 0) nil)
|
link-next-cmds
|
||||||
((= (len cmds) 1) (first cmds))
|
(fn
|
||||||
(true
|
(cmds-list)
|
||||||
(cons
|
(define
|
||||||
(quote do)
|
loop
|
||||||
(filter (fn (c) (not (= c (quote __then__)))) cmds)))))))
|
(fn
|
||||||
|
(i)
|
||||||
|
(when
|
||||||
|
(< i (- (len cmds-list) 1))
|
||||||
|
(let
|
||||||
|
((cur-node (nth cmds-list i))
|
||||||
|
(nxt-node (nth cmds-list (+ i 1))))
|
||||||
|
(when
|
||||||
|
(and (dict? cur-node) (get cur-node :hs-ast))
|
||||||
|
(dict-set! (get cur-node :fields) "next" nxt-node)))
|
||||||
|
(loop (+ i 1)))))
|
||||||
|
(loop 0)
|
||||||
|
cmds-list))
|
||||||
|
(let
|
||||||
|
((linked (if hs-span-mode (link-next-cmds cmds) cmds)))
|
||||||
|
(cond
|
||||||
|
((= (len linked) 0) nil)
|
||||||
|
((= (len linked) 1) (first linked))
|
||||||
|
(true
|
||||||
|
(cons
|
||||||
|
(quote do)
|
||||||
|
(filter (fn (c) (not (= c (quote __then__)))) linked))))))))
|
||||||
(define
|
(define
|
||||||
parse-on-feat
|
parse-on-feat
|
||||||
(fn
|
(fn
|
||||||
@@ -2806,7 +2983,10 @@
|
|||||||
((= 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 "socket") (do (adv!) (parse-socket-feat)))
|
((= val "socket") (do (adv!) (parse-socket-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
|
||||||
@@ -2825,4 +3005,12 @@
|
|||||||
(first features)
|
(first features)
|
||||||
(cons (quote do) features))))))
|
(cons (quote do) features))))))
|
||||||
|
|
||||||
|
(define hs-span-mode false)
|
||||||
|
|
||||||
(define hs-compile (fn (src) (hs-parse (hs-tokenize src) src)))
|
(define hs-compile (fn (src) (hs-parse (hs-tokenize src) src)))
|
||||||
|
|
||||||
|
(define hs-parse-ast
|
||||||
|
(fn (src)
|
||||||
|
(set! hs-span-mode true)
|
||||||
|
(let ((result (hs-parse (hs-tokenize src) src)))
|
||||||
|
(do (set! hs-span-mode false) result))))
|
||||||
|
|||||||
@@ -48,7 +48,7 @@
|
|||||||
(fn
|
(fn
|
||||||
(target event-name handler)
|
(target event-name handler)
|
||||||
(let
|
(let
|
||||||
((wrapped (fn (event) (guard (e ((and (not (= event-name "exception")) (not (= event-name "error"))) (dom-dispatch target "exception" {:error e})) (true (raise e))) (handler event)))))
|
((wrapped (fn (event) (guard (e ((and (not (= event-name "exception")) (not (= event-name "error"))) (dom-dispatch target "exception" {:error e})) (true (raise e))) (do (handler event) (when event (host-call event "stopPropagation")))))))
|
||||||
(let
|
(let
|
||||||
((unlisten (dom-listen target event-name wrapped))
|
((unlisten (dom-listen target event-name wrapped))
|
||||||
(prev (or (dom-get-data target "hs-unlisteners") (list))))
|
(prev (or (dom-get-data target "hs-unlisteners") (list))))
|
||||||
@@ -650,9 +650,7 @@
|
|||||||
(true (find-prev (dom-get-prop el "previousElementSibling"))))))
|
(true (find-prev (dom-get-prop el "previousElementSibling"))))))
|
||||||
(find-prev sibling)))))
|
(find-prev sibling)))))
|
||||||
|
|
||||||
(define
|
(define hs-query-all (fn (sel) (dom-query-all (dom-body) sel)))
|
||||||
hs-query-all
|
|
||||||
(fn (sel) (host-call (dom-body) "querySelectorAll" sel)))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -662,10 +660,7 @@
|
|||||||
hs-query-all-in
|
hs-query-all-in
|
||||||
(fn
|
(fn
|
||||||
(sel target)
|
(sel target)
|
||||||
(if
|
(if (nil? target) (hs-query-all sel) (dom-query-all target sel))))
|
||||||
(nil? target)
|
|
||||||
(hs-query-all sel)
|
|
||||||
(host-call target "querySelectorAll" sel))))
|
|
||||||
|
|
||||||
(define
|
(define
|
||||||
hs-list-set
|
hs-list-set
|
||||||
@@ -874,12 +869,33 @@
|
|||||||
(define
|
(define
|
||||||
hs-fetch
|
hs-fetch
|
||||||
(fn
|
(fn
|
||||||
(url format)
|
(url format do-not-throw target)
|
||||||
(let
|
(let
|
||||||
((fmt (cond ((nil? format) "text") ((or (= format "json") (= format "JSON") (= 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")) "json") ((or (= format "html") (= format "HTML")) "html") ((or (= format "response") (= format "Response")) "response") ((or (= format "text") (= format "Text")) "text") ((or (= format "number") (= format "Number")) "number") (true format))))
|
||||||
(let
|
(do
|
||||||
((raw (perform (list "io-fetch" url fmt))))
|
(when (not (nil? target))
|
||||||
(cond ((= fmt "json") (hs-host-to-sx raw)) (true raw))))))
|
(dom-dispatch target "hyperscript:beforeFetch" nil))
|
||||||
|
(let
|
||||||
|
((raw (perform (list "io-fetch" url "response" (dict)))))
|
||||||
|
(do
|
||||||
|
(when (get raw :_network-error) (raise {:response raw :message "Network error" :_hs-error "FetchError"}))
|
||||||
|
(when
|
||||||
|
(and (not (get raw :ok)) (not (= fmt "response")) (not do-not-throw))
|
||||||
|
(raise {:response raw :status (get raw :status) :message "Fetch error" :_hs-error "FetchError"}))
|
||||||
|
(cond
|
||||||
|
((= fmt "response") raw)
|
||||||
|
((= fmt "json")
|
||||||
|
(let
|
||||||
|
((parsed (perform (list "io-parse-json" (get raw :_json)))))
|
||||||
|
(hs-host-to-sx parsed)))
|
||||||
|
((= fmt "html")
|
||||||
|
(perform (list "io-parse-html" (get raw :_html))))
|
||||||
|
((= fmt "number")
|
||||||
|
(or
|
||||||
|
(parse-number (get raw :_number))
|
||||||
|
(parse-number (get raw :_body))
|
||||||
|
0))
|
||||||
|
(true (get raw :_body)))))))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
hs-json-escape
|
hs-json-escape
|
||||||
@@ -970,6 +986,8 @@
|
|||||||
(true (str value))))
|
(true (str value))))
|
||||||
((= type-name "JSON")
|
((= type-name "JSON")
|
||||||
(cond
|
(cond
|
||||||
|
((and (dict? value) (dict-has? value :_json))
|
||||||
|
(guard (_e (true value)) (json-parse (get value :_json))))
|
||||||
((string? value) (guard (_e (true value)) (json-parse value)))
|
((string? value) (guard (_e (true value)) (json-parse value)))
|
||||||
((dict? value) (hs-json-stringify value))
|
((dict? value) (hs-json-stringify value))
|
||||||
((list? value) (hs-json-stringify value))
|
((list? value) (hs-json-stringify value))
|
||||||
@@ -1418,6 +1436,15 @@
|
|||||||
hs-strict-eq
|
hs-strict-eq
|
||||||
(fn (a b) (and (= (type-of a) (type-of b)) (= a b))))
|
(fn (a b) (and (= (type-of a) (type-of b)) (= a b))))
|
||||||
|
|
||||||
|
(define
|
||||||
|
hs-id=
|
||||||
|
(fn
|
||||||
|
(a b)
|
||||||
|
(if
|
||||||
|
(and (= (host-typeof a) "element") (= (host-typeof b) "element"))
|
||||||
|
(hs-ref-eq a b)
|
||||||
|
(= a b))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
hs-eq-ignore-case
|
hs-eq-ignore-case
|
||||||
(fn (a b) (= (downcase (str a)) (downcase (str b)))))
|
(fn (a b) (= (downcase (str a)) (downcase (str b)))))
|
||||||
@@ -2097,20 +2124,11 @@
|
|||||||
(fn
|
(fn
|
||||||
(pairs)
|
(pairs)
|
||||||
(let
|
(let
|
||||||
((d {}) (order (list)))
|
((d (dict)))
|
||||||
(do
|
(begin
|
||||||
(for-each
|
(for-each
|
||||||
(fn
|
(fn (pair) (dict-set! d (first pair) (nth pair 1)))
|
||||||
(pair)
|
|
||||||
(let
|
|
||||||
((k (first pair)))
|
|
||||||
(do
|
|
||||||
(when
|
|
||||||
(not (dict-has? d k))
|
|
||||||
(set! order (append order (list k))))
|
|
||||||
(dict-set! d k (nth pair 1)))))
|
|
||||||
pairs)
|
pairs)
|
||||||
(when (not (empty? order)) (dict-set! d "_order" order))
|
|
||||||
d))))
|
d))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
@@ -2520,12 +2538,61 @@
|
|||||||
((= a b) true)
|
((= a b) true)
|
||||||
(true (hs-dom-is-ancestor? a (dom-parent b))))))
|
(true (hs-dom-is-ancestor? a (dom-parent b))))))
|
||||||
|
|
||||||
|
;; ── SourceInfo API ────────────────────────────────────────────────
|
||||||
|
|
||||||
(define
|
(define
|
||||||
hs-win-call
|
hs-win-call
|
||||||
(fn
|
(fn
|
||||||
(fn-name args)
|
(fn-name args)
|
||||||
(let ((fn (host-global fn-name))) (if fn (host-call-fn fn args) nil))))
|
(let ((fn (host-global fn-name))) (if fn (host-call-fn fn args) nil))))
|
||||||
|
|
||||||
|
(define
|
||||||
|
hs-source-for
|
||||||
|
(fn
|
||||||
|
(node)
|
||||||
|
(substring (get node :src) (get node :start) (get node :end))))
|
||||||
|
|
||||||
|
(define
|
||||||
|
hs-line-for
|
||||||
|
(fn
|
||||||
|
(node)
|
||||||
|
(let
|
||||||
|
((lines (split (get node :src) "\n"))
|
||||||
|
(line-idx (- (get node :line) 1)))
|
||||||
|
(if (< line-idx (len lines)) (nth lines line-idx) ""))))
|
||||||
|
|
||||||
|
(define hs-node-get (fn (node key) (get (get node :fields) key)))
|
||||||
|
|
||||||
|
(define hs-src (fn (src-str) (hs-source-for (hs-parse-ast src-str))))
|
||||||
|
|
||||||
|
(define
|
||||||
|
hs-src-at
|
||||||
|
(fn
|
||||||
|
(src-str path)
|
||||||
|
(define
|
||||||
|
walk
|
||||||
|
(fn
|
||||||
|
(node keys)
|
||||||
|
(if
|
||||||
|
(or (nil? keys) (= (len keys) 0))
|
||||||
|
node
|
||||||
|
(walk (hs-node-get node (first keys)) (rest keys)))))
|
||||||
|
(hs-source-for (walk (hs-parse-ast src-str) path))))
|
||||||
|
|
||||||
|
(define
|
||||||
|
hs-line-at
|
||||||
|
(fn
|
||||||
|
(src-str path)
|
||||||
|
(define
|
||||||
|
walk
|
||||||
|
(fn
|
||||||
|
(node keys)
|
||||||
|
(if
|
||||||
|
(or (nil? keys) (= (len keys) 0))
|
||||||
|
node
|
||||||
|
(walk (hs-node-get node (first keys)) (rest keys)))))
|
||||||
|
(hs-line-for (walk (hs-parse-ast src-str) path))))
|
||||||
|
|
||||||
;; ── WebSocket / socket feature ───────────────────────────────────
|
;; ── WebSocket / socket feature ───────────────────────────────────
|
||||||
|
|
||||||
(define
|
(define
|
||||||
@@ -2634,3 +2701,4 @@
|
|||||||
(bind-path! next rest-path))))))
|
(bind-path! next rest-path))))))
|
||||||
(bind-path! (host-global "window") name-path)
|
(bind-path! (host-global "window") name-path)
|
||||||
wrapper)))))
|
wrapper)))))
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,27 @@
|
|||||||
|
|
||||||
(define hs-ws? (fn (c) (or (= c " ") (= c "\t") (= c "\n") (= c "\r"))))
|
(define hs-ws? (fn (c) (or (= c " ") (= c "\t") (= c "\n") (= c "\r"))))
|
||||||
|
|
||||||
|
(define
|
||||||
|
hs-hex-digit?
|
||||||
|
(fn
|
||||||
|
(c)
|
||||||
|
(or
|
||||||
|
(and (>= c "0") (<= c "9"))
|
||||||
|
(and (>= c "a") (<= c "f"))
|
||||||
|
(and (>= c "A") (<= c "F")))))
|
||||||
|
|
||||||
|
(define
|
||||||
|
hs-hex-val
|
||||||
|
(fn
|
||||||
|
(c)
|
||||||
|
(let
|
||||||
|
((code (char-code c)))
|
||||||
|
(cond
|
||||||
|
((and (>= code 48) (<= code 57)) (- code 48))
|
||||||
|
((and (>= code 65) (<= code 70)) (- code 55))
|
||||||
|
((and (>= code 97) (<= code 102)) (- code 87))
|
||||||
|
(true 0)))))
|
||||||
|
|
||||||
;; ── Keyword set ───────────────────────────────────────────────────
|
;; ── Keyword set ───────────────────────────────────────────────────
|
||||||
|
|
||||||
(define
|
(define
|
||||||
@@ -235,10 +256,15 @@
|
|||||||
read-number
|
read-number
|
||||||
(fn
|
(fn
|
||||||
(start)
|
(start)
|
||||||
(when
|
(define
|
||||||
(and (< pos src-len) (hs-digit? (hs-cur)))
|
read-int
|
||||||
(hs-advance! 1)
|
(fn
|
||||||
(read-number start))
|
()
|
||||||
|
(when
|
||||||
|
(and (< pos src-len) (hs-digit? (hs-cur)))
|
||||||
|
(hs-advance! 1)
|
||||||
|
(read-int))))
|
||||||
|
(read-int)
|
||||||
(when
|
(when
|
||||||
(and
|
(and
|
||||||
(< pos src-len)
|
(< pos src-len)
|
||||||
@@ -246,15 +272,7 @@
|
|||||||
(< (+ pos 1) src-len)
|
(< (+ pos 1) src-len)
|
||||||
(hs-digit? (hs-peek 1)))
|
(hs-digit? (hs-peek 1)))
|
||||||
(hs-advance! 1)
|
(hs-advance! 1)
|
||||||
(define
|
(read-int))
|
||||||
read-frac
|
|
||||||
(fn
|
|
||||||
()
|
|
||||||
(when
|
|
||||||
(and (< pos src-len) (hs-digit? (hs-cur)))
|
|
||||||
(hs-advance! 1)
|
|
||||||
(read-frac))))
|
|
||||||
(read-frac))
|
|
||||||
(do
|
(do
|
||||||
(when
|
(when
|
||||||
(and
|
(and
|
||||||
@@ -272,15 +290,7 @@
|
|||||||
(< pos src-len)
|
(< pos src-len)
|
||||||
(or (= (hs-cur) "+") (= (hs-cur) "-")))
|
(or (= (hs-cur) "+") (= (hs-cur) "-")))
|
||||||
(hs-advance! 1))
|
(hs-advance! 1))
|
||||||
(define
|
(read-int))
|
||||||
read-exp-digits
|
|
||||||
(fn
|
|
||||||
()
|
|
||||||
(when
|
|
||||||
(and (< pos src-len) (hs-digit? (hs-cur)))
|
|
||||||
(hs-advance! 1)
|
|
||||||
(read-exp-digits))))
|
|
||||||
(read-exp-digits))
|
|
||||||
(let
|
(let
|
||||||
((num-end pos))
|
((num-end pos))
|
||||||
(when
|
(when
|
||||||
@@ -308,7 +318,7 @@
|
|||||||
()
|
()
|
||||||
(cond
|
(cond
|
||||||
(>= pos src-len)
|
(>= pos src-len)
|
||||||
nil
|
(error "Unterminated string")
|
||||||
(= (hs-cur) "\\")
|
(= (hs-cur) "\\")
|
||||||
(do
|
(do
|
||||||
(hs-advance! 1)
|
(hs-advance! 1)
|
||||||
@@ -318,15 +328,37 @@
|
|||||||
((ch (hs-cur)))
|
((ch (hs-cur)))
|
||||||
(cond
|
(cond
|
||||||
(= ch "n")
|
(= ch "n")
|
||||||
(append! chars "\n")
|
(do (append! chars "\n") (hs-advance! 1))
|
||||||
(= ch "t")
|
(= ch "t")
|
||||||
(append! chars "\t")
|
(do (append! chars "\t") (hs-advance! 1))
|
||||||
|
(= ch "r")
|
||||||
|
(do (append! chars "\r") (hs-advance! 1))
|
||||||
|
(= ch "b")
|
||||||
|
(do (append! chars (char-from-code 8)) (hs-advance! 1))
|
||||||
|
(= ch "f")
|
||||||
|
(do (append! chars (char-from-code 12)) (hs-advance! 1))
|
||||||
|
(= ch "v")
|
||||||
|
(do (append! chars (char-from-code 11)) (hs-advance! 1))
|
||||||
(= ch "\\")
|
(= ch "\\")
|
||||||
(append! chars "\\")
|
(do (append! chars "\\") (hs-advance! 1))
|
||||||
(= ch quote-char)
|
(= ch quote-char)
|
||||||
(append! chars quote-char)
|
(do (append! chars quote-char) (hs-advance! 1))
|
||||||
:else (do (append! chars "\\") (append! chars ch)))
|
(= ch "x")
|
||||||
(hs-advance! 1)))
|
(do
|
||||||
|
(hs-advance! 1)
|
||||||
|
(if
|
||||||
|
(and
|
||||||
|
(< (+ pos 1) src-len)
|
||||||
|
(hs-hex-digit? (hs-cur))
|
||||||
|
(hs-hex-digit? (hs-peek 1)))
|
||||||
|
(let
|
||||||
|
((d1 (hs-hex-val (hs-cur)))
|
||||||
|
(d2 (hs-hex-val (hs-peek 1))))
|
||||||
|
(append! chars (char-from-code (+ (* d1 16) d2)))
|
||||||
|
(hs-advance! 2))
|
||||||
|
(error "Invalid hexadecimal escape: \\x")))
|
||||||
|
:else
|
||||||
|
(do (append! chars "\\") (append! chars ch) (hs-advance! 1)))))
|
||||||
(loop))
|
(loop))
|
||||||
(= (hs-cur) quote-char)
|
(= (hs-cur) quote-char)
|
||||||
(hs-advance! 1)
|
(hs-advance! 1)
|
||||||
@@ -540,10 +572,26 @@
|
|||||||
(do
|
(do
|
||||||
(let
|
(let
|
||||||
((word (read-ident start)))
|
((word (read-ident start)))
|
||||||
(hs-emit!
|
(let
|
||||||
(if (hs-keyword? word) "keyword" "ident")
|
((full-word
|
||||||
word
|
(if
|
||||||
start))
|
(and
|
||||||
|
(< pos src-len)
|
||||||
|
(= (hs-cur) "'")
|
||||||
|
(< (+ pos 1) src-len)
|
||||||
|
(hs-letter? (hs-peek 1))
|
||||||
|
(not
|
||||||
|
(and
|
||||||
|
(= (hs-peek 1) "s")
|
||||||
|
(or
|
||||||
|
(>= (+ pos 2) src-len)
|
||||||
|
(not (hs-ident-char? (hs-peek 2)))))))
|
||||||
|
(do (hs-advance! 1) (str word "'" (read-ident pos)))
|
||||||
|
word)))
|
||||||
|
(hs-emit!
|
||||||
|
(if (hs-keyword? full-word) "keyword" "ident")
|
||||||
|
full-word
|
||||||
|
start)))
|
||||||
(scan!))
|
(scan!))
|
||||||
(and
|
(and
|
||||||
(or (= ch "=") (= ch "!") (= ch "<") (= ch ">"))
|
(or (= ch "=") (= ch "!") (= ch "<") (= ch ">"))
|
||||||
@@ -624,7 +672,82 @@
|
|||||||
(do (hs-emit! "colon" ":" start) (hs-advance! 1) (scan!))
|
(do (hs-emit! "colon" ":" start) (hs-advance! 1) (scan!))
|
||||||
(= ch "|")
|
(= ch "|")
|
||||||
(do (hs-emit! "op" "|" start) (hs-advance! 1) (scan!))
|
(do (hs-emit! "op" "|" start) (hs-advance! 1) (scan!))
|
||||||
|
(= ch "&")
|
||||||
|
(do (hs-emit! "op" "&" start) (hs-advance! 1) (scan!))
|
||||||
|
(= ch "#")
|
||||||
|
(do (hs-emit! "op" "#" start) (hs-advance! 1) (scan!))
|
||||||
|
(= ch "?")
|
||||||
|
(do (hs-emit! "op" "?" start) (hs-advance! 1) (scan!))
|
||||||
|
(= ch ";")
|
||||||
|
(do (hs-emit! "op" ";" start) (hs-advance! 1) (scan!))
|
||||||
:else (do (hs-advance! 1) (scan!)))))))
|
:else (do (hs-advance! 1) (scan!)))))))
|
||||||
(scan!)
|
(scan!)
|
||||||
(hs-emit! "eof" nil pos)
|
(hs-emit! "eof" nil pos)
|
||||||
|
tokens)))
|
||||||
|
|
||||||
|
;; ── Template-mode tokenizer (E37 API) ────────────────────────────────
|
||||||
|
;; Used by hs-tokens-of when :template flag is set.
|
||||||
|
;; Emits outer " chars as single STRING tokens; ${ ... } as $ { <inner-tokens> };
|
||||||
|
;; inner content is tokenized with the regular hs-tokenize.
|
||||||
|
|
||||||
|
(define
|
||||||
|
hs-tokenize-template
|
||||||
|
(fn
|
||||||
|
(src)
|
||||||
|
(let
|
||||||
|
((tokens (list)) (pos 0) (src-len (len src)))
|
||||||
|
(define t-cur (fn () (if (< pos src-len) (nth src pos) nil)))
|
||||||
|
(define t-peek (fn (n) (if (< (+ pos n) src-len) (nth src (+ pos n)) nil)))
|
||||||
|
(define t-advance! (fn (n) (set! pos (+ pos n))))
|
||||||
|
(define t-emit! (fn (type value) (append! tokens (hs-make-token type value pos))))
|
||||||
|
(define
|
||||||
|
scan-to-close!
|
||||||
|
(fn
|
||||||
|
(depth)
|
||||||
|
(when
|
||||||
|
(and (< pos src-len) (> depth 0))
|
||||||
|
(cond
|
||||||
|
(= (t-cur) "{")
|
||||||
|
(do (t-advance! 1) (scan-to-close! (+ depth 1)))
|
||||||
|
(= (t-cur) "}")
|
||||||
|
(when (> (- depth 1) 0) (t-advance! 1) (scan-to-close! (- depth 1)))
|
||||||
|
:else (do (t-advance! 1) (scan-to-close! depth))))))
|
||||||
|
(define
|
||||||
|
scan-template!
|
||||||
|
(fn
|
||||||
|
()
|
||||||
|
(when
|
||||||
|
(< pos src-len)
|
||||||
|
(let
|
||||||
|
((ch (t-cur)))
|
||||||
|
(cond
|
||||||
|
(= ch "\"")
|
||||||
|
(do (t-emit! "string" "\"") (t-advance! 1) (scan-template!))
|
||||||
|
(and (= ch "$") (= (t-peek 1) "{"))
|
||||||
|
(do
|
||||||
|
(t-emit! "op" "$")
|
||||||
|
(t-advance! 1)
|
||||||
|
(t-emit! "brace-open" "{")
|
||||||
|
(t-advance! 1)
|
||||||
|
(let
|
||||||
|
((inner-start pos))
|
||||||
|
(scan-to-close! 1)
|
||||||
|
(let
|
||||||
|
((inner-src (slice src inner-start pos))
|
||||||
|
(inner-toks (hs-tokenize inner-src)))
|
||||||
|
(for-each
|
||||||
|
(fn (tok)
|
||||||
|
(when (not (= (get tok "type") "eof"))
|
||||||
|
(append! tokens tok)))
|
||||||
|
inner-toks))
|
||||||
|
(t-emit! "brace-close" "}")
|
||||||
|
(when (< pos src-len) (t-advance! 1)))
|
||||||
|
(scan-template!))
|
||||||
|
(= ch "$")
|
||||||
|
(do (t-emit! "op" "$") (t-advance! 1) (scan-template!))
|
||||||
|
(hs-ws? ch)
|
||||||
|
(do (t-advance! 1) (scan-template!))
|
||||||
|
:else (do (t-advance! 1) (scan-template!)))))))
|
||||||
|
(scan-template!)
|
||||||
|
(t-emit! "eof" nil)
|
||||||
tokens)))
|
tokens)))
|
||||||
@@ -4,10 +4,10 @@ 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: 1303/1496 (87.1%) delta +90
|
Merged: 1312/1496 (87.7%) delta +99
|
||||||
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: ~192 tests (clusters 17/29(partial)/31 blocked; 33/34 partial)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Cluster ledger
|
## Cluster ledger
|
||||||
@@ -22,7 +22,7 @@ Remaining: ~194 tests (clusters 17/29(partial)/31 blocked; 33/34 partial)
|
|||||||
| 4 | `not` precedence over `or` | done | +3 | 4fe0b649 |
|
| 4 | `not` precedence over `or` | done | +3 | 4fe0b649 |
|
||||||
| 5 | `some` selector for nonempty match | done | +1 | e7b86264 |
|
| 5 | `some` selector for nonempty match | done | +1 | e7b86264 |
|
||||||
| 6 | string template `${x}` | done | +2 | 108e25d4 |
|
| 6 | string template `${x}` | done | +2 | 108e25d4 |
|
||||||
| 7 | `put` hyperscript reprocessing | partial | +1 | f21eb008 |
|
| 7 | `put` hyperscript reprocessing | done | +5 | 247bd85c |
|
||||||
| 8 | `select` returns selected text | done | +1 | d862efe8 |
|
| 8 | `select` returns selected text | done | +1 | d862efe8 |
|
||||||
| 9 | `wait on event` basics | done | +4 | f79f96c1 |
|
| 9 | `wait on event` basics | done | +4 | f79f96c1 |
|
||||||
| 10 | `swap` variable ↔ property | done | +1 | 30f33341 |
|
| 10 | `swap` variable ↔ property | done | +1 | 30f33341 |
|
||||||
@@ -66,6 +66,7 @@ Remaining: ~194 tests (clusters 17/29(partial)/31 blocked; 33/34 partial)
|
|||||||
| 33 | cookie API | partial | +4 |
|
| 33 | cookie API | partial | +4 |
|
||||||
| 34 | event modifier DSL | partial | +7 |
|
| 34 | event modifier DSL | partial | +7 |
|
||||||
| 35 | namespaced `def` | done | +3 |
|
| 35 | namespaced `def` | done | +3 |
|
||||||
|
| 36b | `call` result binds to `it` | done | +1 | 35f498ec |
|
||||||
|
|
||||||
### Bucket E — subsystems (design docs landed, pending review + implementation)
|
### Bucket E — subsystems (design docs landed, pending review + implementation)
|
||||||
|
|
||||||
@@ -75,12 +76,19 @@ Remaining: ~194 tests (clusters 17/29(partial)/31 blocked; 33/34 partial)
|
|||||||
| 37 | Tokenizer-as-API | design-done | `plans/designs/e37-tokenizer-api.md` |
|
| 37 | Tokenizer-as-API | design-done | `plans/designs/e37-tokenizer-api.md` |
|
||||||
| 38 | SourceInfo API | design-done | `plans/designs/e38-sourceinfo.md` |
|
| 38 | SourceInfo API | design-done | `plans/designs/e38-sourceinfo.md` |
|
||||||
| 39 | WebWorker plugin | design-done | `plans/designs/e39-webworker.md` |
|
| 39 | WebWorker plugin | design-done | `plans/designs/e39-webworker.md` |
|
||||||
| 40 | Fetch non-2xx / before-fetch / real response | design-done | `plans/designs/e40-real-fetch.md` |
|
| 40 | Fetch non-2xx / before-fetch / real response | done | +7 | d7244d1d |
|
||||||
|
|
||||||
### Bucket F — generator translation gaps
|
### Bucket F — generator translation gaps
|
||||||
|
|
||||||
Defer until A–D drain. Estimated ~25 recoverable tests.
|
Defer until A–D drain. Estimated ~25 recoverable tests.
|
||||||
|
|
||||||
|
| # | Cluster | Status | Δ | Commit |
|
||||||
|
|---|---------|--------|---|--------|
|
||||||
|
| F1 | add CSS template interpolation | done | +1 | 5a76a040 |
|
||||||
|
| F2 | empty multi-element (query→for-each) | done | +1 | 875e9ba3 |
|
||||||
|
| F3 | hs-make-object _order + assert= for dicts | done | +1 | daea2808 |
|
||||||
|
| F4 | array literal arg to JS fn (sxToJs + reduce→SX) | done | +1 | da2e6b1b |
|
||||||
|
|
||||||
## Buckets roll-up
|
## Buckets roll-up
|
||||||
|
|
||||||
| Bucket | Done | Partial | In-prog | Pending | Blocked | Design-done | Total |
|
| Bucket | Done | Partial | In-prog | Pending | Blocked | Design-done | Total |
|
||||||
@@ -89,7 +97,7 @@ Defer until A–D drain. Estimated ~25 recoverable tests.
|
|||||||
| B | 7 | 0 | 0 | 0 | 0 | — | 7 |
|
| B | 7 | 0 | 0 | 0 | 0 | — | 7 |
|
||||||
| C | 4 | 1 | 0 | 0 | 0 | — | 5 |
|
| C | 4 | 1 | 0 | 0 | 0 | — | 5 |
|
||||||
| D | 2 | 2 | 0 | 0 | 1 | — | 5 |
|
| D | 2 | 2 | 0 | 0 | 1 | — | 5 |
|
||||||
| E | 0 | 0 | 0 | 0 | 0 | 5 | 5 |
|
| E | 1 | 0 | 0 | 0 | 0 | 4 | 5 |
|
||||||
| F | — | — | — | ~10 | — | — | ~10 |
|
| F | — | — | — | ~10 | — | — | ~10 |
|
||||||
|
|
||||||
## Maintenance
|
## Maintenance
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ Orchestrator cherry-picks worktree commits onto `architecture` one at a time; re
|
|||||||
|
|
||||||
6. **[done (+2)] string template `${x}`** — `expressions/strings / string templates work w/ props` + `w/ braces` (2 tests). Template interpolation isn't substituting property accesses. Check `hs-template` runtime. Expected: +2.
|
6. **[done (+2)] string template `${x}`** — `expressions/strings / string templates work w/ props` + `w/ braces` (2 tests). Template interpolation isn't substituting property accesses. Check `hs-template` runtime. Expected: +2.
|
||||||
|
|
||||||
7. **[done (+1) — partial, 3 tests remain: inserted-button handler doesn't fire for afterbegin/innerHTML paths; might need targeted trace of hs-boot-subtree! or _setInnerHTML timing] `put` hyperscript reprocessing** — `put / properly processes hyperscript at end/start/content/symbol` (4 tests, all `Expected 42, got 40`). After a put operation, newly inserted HS scripts aren't being activated. Fix: `hs-put-at!` should `hs-boot-subtree!` on the target after DOM insertion. Expected: +4.
|
7. **[done (+5)] `put` hyperscript reprocessing** — `put / properly processes hyperscript at end/start/content/symbol` (4 tests, all `Expected 42, got 40`). After a put operation, newly inserted HS scripts aren't being activated. Fix: `hs-put-at!` should `hs-boot-subtree!` on the target after DOM insertion. Expected: +4.
|
||||||
|
|
||||||
8. **[done (+1)] `select returns selected text`** (1 test, `hs-upstream-select`). Runtime `hs-get-selection` helper reads `window.__test_selection` stash (or falls back to real `window.getSelection().toString()`). Compiler rewrites `(ref "selection")` to `(hs-get-selection)`. Generator detects the `createRange` / `setStart` / `setEnd` / `addRange` block and emits a single `(host-set! ... __test_selection ...)` op with the resolved text slice of the target element. Expected: +1.
|
8. **[done (+1)] `select returns selected text`** (1 test, `hs-upstream-select`). Runtime `hs-get-selection` helper reads `window.__test_selection` stash (or falls back to real `window.getSelection().toString()`). Compiler rewrites `(ref "selection")` to `(hs-get-selection)`. Generator detects the `createRange` / `setStart` / `setEnd` / `addRange` block and emits a single `(host-set! ... __test_selection ...)` op with the resolved text slice of the target element. Expected: +1.
|
||||||
|
|
||||||
@@ -125,19 +125,21 @@ Orchestrator cherry-picks worktree commits onto `architecture` one at a time; re
|
|||||||
|
|
||||||
35. **[done (+3)] namespaced `def`** — 3 tests. `def ns.foo() ...` creates `ns.foo`. Expected: +3.
|
35. **[done (+3)] namespaced `def`** — 3 tests. `def ns.foo() ...` creates `ns.foo`. Expected: +3.
|
||||||
|
|
||||||
|
36b. **[done (+1)] `call` result binds to `it`** — `call / call functions that return promises are waited on` (1 test). `call X then put it into Y` wasn't setting `it` because the `call` compiler branch emitted the call expression directly without `emit-set`. Fixed by wrapping in `emit-set (quote the-result) call-expr`. Expected: +1.
|
||||||
|
|
||||||
### Bucket E: subsystems (DO NOT LOOP — human-driven)
|
### Bucket E: subsystems (DO NOT LOOP — human-driven)
|
||||||
|
|
||||||
All five have design docs on their own worktree branches pending review + merge. After merge, status flips to `design-ready` and they become eligible for the loop.
|
All five have design docs on their own worktree branches pending review + merge. After merge, status flips to `design-ready` and they become eligible for the loop.
|
||||||
|
|
||||||
36. **[DONE +16 — branch `hs-e36-websocket`] WebSocket + `socket`** — 16/16 tests passing. `socket NAME URL [with timeout N] [on message [as JSON] …] end`, RPC proxy (dispatch-fn pattern), reconnect, dispatchEvent, timeout/noTimeout chains. All 16 upstream tests green.
|
36. **[DONE +16 — branch `hs-e36-websocket`] WebSocket + `socket`** — 16/16 tests passing. `socket NAME URL [with timeout N] [on message [as JSON] …] end`, RPC proxy (dispatch-fn pattern), reconnect, dispatchEvent, timeout/noTimeout chains. All 16 upstream tests green.
|
||||||
|
|
||||||
37. **[design-done, pending review — `plans/designs/e37-tokenizer-api.md` on `worktree-agent-a6bb61d59cc0be8b4`] Tokenizer-as-API** — 17 tests. Expose tokens as inspectable SX data via `hs-tokens-of` / `hs-stream-token` / `hs-token-type` etc; type-map current `hs-tokenize` output to upstream SCREAMING_SNAKE_CASE. 8-step checklist, +16–17 delta.
|
37. **[done +17]** Tokenizer-as-API — `hs-tokens-of` / `hs-stream-token` / `hs-token-type` / `hs-token-value` / `hs-token-op?`; type-map + normalize; `read-number` dot-stop fix; `\$` template escape in compiler + runtime; generator pattern in `generate-sx-tests.py`. 17/17.
|
||||||
|
|
||||||
38. **[design-done, pending review — `plans/designs/e38-sourceinfo.md` on `agent-e38-sourceinfo`] SourceInfo API** — 4 tests. Inline span-wrapper strategy (not side-channel dict) with compiler-entry unwrap. 4-commit plan.
|
38. **[design-done, pending review — `plans/designs/e38-sourceinfo.md` on `agent-e38-sourceinfo`] SourceInfo API** — 4 tests. Inline span-wrapper strategy (not side-channel dict) with compiler-entry unwrap. 4-commit plan.
|
||||||
|
|
||||||
39. **[design-done, pending review — `plans/designs/e39-webworker.md` on `hs-design-e39-webworker`] WebWorker plugin** — 1 test. Parser-only stub that errors with a link to upstream docs; no runtime, no mock Worker class. Hand-write the test (don't patch the generator). Single commit.
|
39. **[design-done, pending review — `plans/designs/e39-webworker.md` on `hs-design-e39-webworker`] WebWorker plugin** — 1 test. Parser-only stub that errors with a link to upstream docs; no runtime, no mock Worker class. Hand-write the test (don't patch the generator). Single commit.
|
||||||
|
|
||||||
40. **[design-done, pending review — `plans/designs/e40-real-fetch.md` on `worktree-agent-a94612a4283eaa5e0`] Fetch non-2xx / before-fetch event / real response object** — 7 tests. SX-dict Response wrapper `{:_hs-response :ok :status :url :_body :_json :_html}`; restructured `hs-fetch` that always fetches wrapper then converts by format; test-name-keyed `_fetchScripts`. 11-step checklist. Watch for regression on cluster-1 JSON unwrap.
|
40. **[done +7 — d7244d1d] Fetch non-2xx / before-fetch event / real response object** — 7 tests. SX-dict Response wrapper `{:_hs-response :ok :status :url :_body :_json :_html}`; restructured `hs-fetch` that always fetches wrapper then converts by format; test-name-keyed `_fetchScripts`. 11-step checklist. Watch for regression on cluster-1 JSON unwrap.
|
||||||
|
|
||||||
### Bucket F: generator translation gaps (after bucket A-D)
|
### Bucket F: generator translation gaps (after bucket A-D)
|
||||||
|
|
||||||
@@ -175,6 +177,27 @@ Many tests are `SKIP (untranslated)` because `tests/playwright/generate-sx-tests
|
|||||||
|
|
||||||
## Progress log
|
## Progress log
|
||||||
|
|
||||||
|
### 2026-04-26 — Bucket F: array literal arg to JS fn (+1)
|
||||||
|
- **da2e6b1b** — `HS Bucket F: array literal arg to JS fn fix (+1 test)`. Two-part fix: (a) `generate-sx-tests.py` `js_expr_to_sx` now translates `arr.reduce(fn, init)` → `(reduce fn init arr)`, `.map(fn)` → `(map fn arr)`, `.filter(fn)` → `(filter fn arr)` so SX list arguments work with JS array HO methods. (b) `host-call-fn` in `hs-run-filtered.js` adds `sxToJs` recursive converter that unwraps SX list `._type==='list'` to native JS arrays before calling native JS functions. Together these fix functionCalls "can pass an array literal as an argument". Suite hs-upstream-expressions/functionCalls: 8/12 (unchanged SKIP ratio). Test 597: 0/1 → 1/1. Smoke 0-195: 175/195 unchanged.
|
||||||
|
|
||||||
|
### 2026-04-26 — Bucket F: hs-make-object _order + assert= for dicts (+1)
|
||||||
|
- **daea2808** — `HS Bucket F: fix hs-make-object _order + assert= for dicts (+1 test)`. Two-part fix: (a) `runtime.sx` `hs-make-object` no longer appends `_order` key to HS object literals — V8's native string-key insertion order is sufficient, and the hidden key was breaking structural equality. (b) `generate-sx-tests.py` `emit_eval` now detects when `expected_sx` contains `{` (dict syntax) and emits `assert-equal` (which uses `equal?` for deep structural equality) instead of `assert=` (which uses `=`, reference equality for dicts). Together these fix arrayLiteral "arrays containing objects work". Suite hs-upstream-expressions/arrayLiteral: 7/8 → 8/8. Smoke 0-195 unchanged at 175/195.
|
||||||
|
|
||||||
|
### 2026-04-26 — Bucket F: empty multi-element fix (+1)
|
||||||
|
- **875e9ba3** — `HS: empty multi-element fix (+1 test)`. `empty .class` compiled `(empty-target (query ".class"))` through `hs-to-sx` → `(hs-empty-target! (hs-query-first ".class"))` which only emptied the first match. Fix: detect `(query ...)` target in the `empty-target` compiler case and emit `(for-each (fn (_el) (hs-empty-target! _el)) (hs-query-all sel))`, mirroring the `add-class` pattern. Suite hs-upstream-empty: 12/13 → 13/13. Smoke 0-195: 175/195 unchanged.
|
||||||
|
|
||||||
|
### 2026-04-26 — Bucket F: add CSS template interpolation (+1)
|
||||||
|
- **5a76a040** — `HS: add CSS template interpolation fix (+1 test)`. `add {color: ${}{"red"}}` uses two consecutive brace groups: the empty `${}` marker followed by `{"red"}` for the actual value. The prior parser fix called `parse-expr` when already at the closing `}` of the empty group, returning nil. Fix: detect the empty-brace case (`brace-open` → immediately `brace-close`), skip it, then read the actual value from the next `{…}` block. Also handles normal `${expr}` correctly. Suite hs-upstream-add: 17/19 → 18/19. Smoke 0-195: 174/195 → 175/195.
|
||||||
|
|
||||||
|
### 2026-04-26 — cluster 36b call result binds to it (done +1)
|
||||||
|
- **35f498ec** — `hs: call command binds result to it via emit-set (+1 test)`. `call X then put it into Y` compiled `call X` without `emit-set`, so `it` remained nil. Wrapped call-expr in `emit-set (quote the-result) ...` so both `it` and `the-result` are updated. Suite hs-upstream-call: 5/6 → 6/6. Smoke 0-195: 173/195 → 174/195.
|
||||||
|
|
||||||
|
### 2026-04-26 — cluster 7 put hyperscript reprocessing (done, final +1)
|
||||||
|
- **247bd85c** — `hs: register promiseAString/promiseAnInt as sync test fixtures (+1 test)`. Upstream test "waits on promises" calls `promiseAString()` via window global. OCaml run_tests.ml registers these as NativeFns returning "foo"/"42" synchronously; JS runner had no equivalent. Added `globalThis.promiseAString = () => 'foo'` and `globalThis.promiseAnInt = () => 42` to hs-run-filtered.js. Suite hs-upstream-put: 37/38 → 38/38 (fully done). Smoke 0-195: 173/195 unchanged.
|
||||||
|
|
||||||
|
### 2026-04-26 — cluster 7 put hyperscript reprocessing (partial +3 more)
|
||||||
|
- **d663c91f** — `hs: stop event propagation after each hs-on handler fires (+3 tests)`. Root cause: click events bubble from b1 (inside d1) to d1, causing d1's `on click put ...` handler to re-fire and replace the just-modified b1 with fresh content (text=40). Fix: `hs-on`'s wrapped handler now calls `event.stopPropagation()` after each handler runs, preventing the bubbled click from reaching ancestor HS listeners. Tests 1147/1149/1150 now pass. Suite hs-upstream-put: 34/38 → 37/38. Smoke 0-195: 173/195 unchanged. One test remains: "waits on promises" (async/Promise issue).
|
||||||
|
|
||||||
(Reverse chronological — newest at top.)
|
(Reverse chronological — newest at top.)
|
||||||
|
|
||||||
### 2026-04-25 — Bucket F: in-expression filter semantics (+1)
|
### 2026-04-25 — Bucket F: in-expression filter semantics (+1)
|
||||||
|
|||||||
@@ -918,6 +918,12 @@
|
|||||||
(let
|
(let
|
||||||
((ch (nth raw i)))
|
((ch (nth raw i)))
|
||||||
(if
|
(if
|
||||||
|
(and (= ch "\\") (< (+ i 1) n) (= (nth raw (+ i 1)) "$"))
|
||||||
|
(do
|
||||||
|
(set! buf (str buf "$"))
|
||||||
|
(set! i (+ i 2))
|
||||||
|
(tpl-collect))
|
||||||
|
(if
|
||||||
(and (= ch "$") (< (+ i 1) n))
|
(and (= ch "$") (< (+ i 1) n))
|
||||||
(if
|
(if
|
||||||
(= (nth raw (+ i 1)) "{")
|
(= (nth raw (+ i 1)) "{")
|
||||||
@@ -956,7 +962,7 @@
|
|||||||
(do
|
(do
|
||||||
(set! buf (str buf ch))
|
(set! buf (str buf ch))
|
||||||
(set! i (+ i 1))
|
(set! i (+ i 1))
|
||||||
(tpl-collect)))))))
|
(tpl-collect))))))))
|
||||||
(tpl-collect)
|
(tpl-collect)
|
||||||
(tpl-flush)
|
(tpl-flush)
|
||||||
(cons (quote str) parts))))
|
(cons (quote str) parts))))
|
||||||
@@ -1857,7 +1863,7 @@
|
|||||||
(list (quote fn) (list) (hs-to-sx (nth ast 1)))
|
(list (quote fn) (list) (hs-to-sx (nth ast 1)))
|
||||||
(list (quote fn) (list) (hs-to-sx (nth ast 2)))))
|
(list (quote fn) (list) (hs-to-sx (nth ast 2)))))
|
||||||
((= head (quote fetch))
|
((= head (quote fetch))
|
||||||
(list (quote hs-fetch) (hs-to-sx (nth ast 1)) (nth ast 2)))
|
(list (quote hs-fetch) (hs-to-sx (nth ast 1)) (nth ast 2) (nth ast 3) (quote me)))
|
||||||
((= head (quote fetch-gql))
|
((= head (quote fetch-gql))
|
||||||
(list
|
(list
|
||||||
(quote hs-fetch-gql)
|
(quote hs-fetch-gql)
|
||||||
|
|||||||
@@ -21,6 +21,16 @@
|
|||||||
adv!
|
adv!
|
||||||
(fn () (let ((t (nth tokens p))) (set! p (+ p 1)) t)))
|
(fn () (let ((t (nth tokens p))) (set! p (+ p 1)) t)))
|
||||||
(define at-end? (fn () (or (>= p tok-len) (= (tp-type) "eof"))))
|
(define at-end? (fn () (or (>= p tok-len) (= (tp-type) "eof"))))
|
||||||
|
(define cur-start (fn () (if (< p tok-len) (get (tp) "pos") 0)))
|
||||||
|
(define cur-line (fn () (if (< p tok-len) (get (tp) "line") 1)))
|
||||||
|
(define
|
||||||
|
prev-end
|
||||||
|
(fn () (if (> p 0) (get (nth tokens (- p 1)) "end") 0)))
|
||||||
|
(define
|
||||||
|
hs-ast-wrap
|
||||||
|
(fn
|
||||||
|
(raw kind start end-pos line fields)
|
||||||
|
(if hs-span-mode {:children raw :end end-pos :kind kind :line line :src src :start start :hs-ast true :fields fields} raw)))
|
||||||
(define
|
(define
|
||||||
match-kw
|
match-kw
|
||||||
(fn
|
(fn
|
||||||
@@ -69,19 +79,40 @@
|
|||||||
parse-prop-chain
|
parse-prop-chain
|
||||||
(fn
|
(fn
|
||||||
(base)
|
(base)
|
||||||
(if
|
(let
|
||||||
(and (= (tp-type) "class") (not (at-end?)))
|
((base-start (if (and (dict? base) (get base :hs-ast)) (get base :start) (cur-start)))
|
||||||
(let
|
(base-line
|
||||||
((prop (tp-val)))
|
(if
|
||||||
(do
|
(and (dict? base) (get base :hs-ast))
|
||||||
(adv!)
|
(get base :line)
|
||||||
(parse-prop-chain (list (make-symbol ".") base prop))))
|
(cur-line))))
|
||||||
(if
|
(if
|
||||||
(= (tp-type) "paren-open")
|
(and (= (tp-type) "class") (not (at-end?)))
|
||||||
(let
|
(let
|
||||||
((args (parse-call-args)))
|
((prop (tp-val)))
|
||||||
(parse-prop-chain (list (quote method-call) base args)))
|
(do
|
||||||
base))))
|
(adv!)
|
||||||
|
(parse-prop-chain
|
||||||
|
(hs-ast-wrap
|
||||||
|
(list (make-symbol ".") base prop)
|
||||||
|
"member"
|
||||||
|
base-start
|
||||||
|
(prev-end)
|
||||||
|
base-line
|
||||||
|
{:root base}))))
|
||||||
|
(if
|
||||||
|
(= (tp-type) "paren-open")
|
||||||
|
(let
|
||||||
|
((args (parse-call-args)))
|
||||||
|
(parse-prop-chain
|
||||||
|
(hs-ast-wrap
|
||||||
|
(list (quote method-call) base args)
|
||||||
|
"call"
|
||||||
|
base-start
|
||||||
|
(prev-end)
|
||||||
|
base-line
|
||||||
|
{:root base})))
|
||||||
|
base)))))
|
||||||
(define
|
(define
|
||||||
parse-trav
|
parse-trav
|
||||||
(fn
|
(fn
|
||||||
@@ -124,8 +155,24 @@
|
|||||||
(let
|
(let
|
||||||
((typ (tp-type)) (val (tp-val)))
|
((typ (tp-type)) (val (tp-val)))
|
||||||
(cond
|
(cond
|
||||||
((= typ "number") (do (adv!) (parse-dur val)))
|
((= typ "number")
|
||||||
((= typ "string") (do (adv!) val))
|
(let
|
||||||
|
((s (cur-start)) (l (cur-line)))
|
||||||
|
(do
|
||||||
|
(adv!)
|
||||||
|
(hs-ast-wrap
|
||||||
|
(parse-dur val)
|
||||||
|
"number"
|
||||||
|
s
|
||||||
|
(prev-end)
|
||||||
|
l
|
||||||
|
{}))))
|
||||||
|
((= typ "string")
|
||||||
|
(let
|
||||||
|
((s (cur-start)) (l (cur-line)))
|
||||||
|
(do
|
||||||
|
(adv!)
|
||||||
|
(hs-ast-wrap val "string" s (prev-end) l {}))))
|
||||||
((= typ "template") (do (adv!) (list (quote template) val)))
|
((= typ "template") (do (adv!) (list (quote template) val)))
|
||||||
((and (= typ "keyword") (= val "true")) (do (adv!) true))
|
((and (= typ "keyword") (= val "true")) (do (adv!) true))
|
||||||
((and (= typ "keyword") (= val "false")) (do (adv!) false))
|
((and (= typ "keyword") (= val "false")) (do (adv!) false))
|
||||||
@@ -190,19 +237,38 @@
|
|||||||
((and (= typ "keyword") (= val "last"))
|
((and (= typ "keyword") (= val "last"))
|
||||||
(do (adv!) (parse-pos-kw (quote last))))
|
(do (adv!) (parse-pos-kw (quote last))))
|
||||||
((= typ "id")
|
((= typ "id")
|
||||||
(do (adv!) (list (quote query) (str "#" val))))
|
(let
|
||||||
|
((s (cur-start)) (l (cur-line)))
|
||||||
|
(do
|
||||||
|
(adv!)
|
||||||
|
(hs-ast-wrap
|
||||||
|
(list (quote query) (str "#" val))
|
||||||
|
"selector"
|
||||||
|
s
|
||||||
|
(prev-end)
|
||||||
|
l
|
||||||
|
{}))))
|
||||||
((= typ "selector")
|
((= typ "selector")
|
||||||
(do
|
(let
|
||||||
(adv!)
|
((s (cur-start)) (l (cur-line)))
|
||||||
(if
|
(do
|
||||||
(and (= (tp-type) "keyword") (= (tp-val) "in"))
|
(adv!)
|
||||||
(do
|
(hs-ast-wrap
|
||||||
(adv!)
|
(if
|
||||||
(list
|
(and (= (tp-type) "keyword") (= (tp-val) "in"))
|
||||||
(quote query-scoped)
|
(do
|
||||||
val
|
(adv!)
|
||||||
(parse-cmp (parse-arith (parse-poss (parse-atom))))))
|
(list
|
||||||
(list (quote query) val))))
|
(quote query-scoped)
|
||||||
|
val
|
||||||
|
(parse-cmp
|
||||||
|
(parse-arith (parse-poss (parse-atom))))))
|
||||||
|
(list (quote query) val))
|
||||||
|
"selector"
|
||||||
|
s
|
||||||
|
(prev-end)
|
||||||
|
l
|
||||||
|
{}))))
|
||||||
((= typ "attr")
|
((= typ "attr")
|
||||||
(do (adv!) (list (quote attr) val (list (quote me)))))
|
(do (adv!) (list (quote attr) val (list (quote me)))))
|
||||||
((= typ "style")
|
((= typ "style")
|
||||||
@@ -219,8 +285,29 @@
|
|||||||
(adv!)
|
(adv!)
|
||||||
(list (quote dom-ref) name (list (quote me)))))))
|
(list (quote dom-ref) name (list (quote me)))))))
|
||||||
((= typ "class")
|
((= typ "class")
|
||||||
(do (adv!) (list (quote query) (str "." val))))
|
(let
|
||||||
((= typ "ident") (do (adv!) (list (quote ref) val)))
|
((s (cur-start)) (l (cur-line)))
|
||||||
|
(do
|
||||||
|
(adv!)
|
||||||
|
(hs-ast-wrap
|
||||||
|
(list (quote query) (str "." val))
|
||||||
|
"selector"
|
||||||
|
s
|
||||||
|
(prev-end)
|
||||||
|
l
|
||||||
|
{}))))
|
||||||
|
((= typ "ident")
|
||||||
|
(let
|
||||||
|
((s (cur-start)) (l (cur-line)))
|
||||||
|
(do
|
||||||
|
(adv!)
|
||||||
|
(hs-ast-wrap
|
||||||
|
(list (quote ref) val)
|
||||||
|
"ref"
|
||||||
|
s
|
||||||
|
(prev-end)
|
||||||
|
l
|
||||||
|
{}))))
|
||||||
((= typ "paren-open")
|
((= typ "paren-open")
|
||||||
(do
|
(do
|
||||||
(adv!)
|
(adv!)
|
||||||
@@ -463,7 +550,9 @@
|
|||||||
(list
|
(list
|
||||||
(quote not)
|
(quote not)
|
||||||
(list (quote eq-ignore-case) left right)))
|
(list (quote eq-ignore-case) left right)))
|
||||||
(list (quote not) (list (quote =) left right)))))))
|
(list
|
||||||
|
(quote not)
|
||||||
|
(list (quote hs-id=) left right)))))))
|
||||||
((match-kw "empty") (list (quote empty?) left))
|
((match-kw "empty") (list (quote empty?) left))
|
||||||
((match-kw "less")
|
((match-kw "less")
|
||||||
(do
|
(do
|
||||||
@@ -941,8 +1030,7 @@
|
|||||||
((prop (get (adv!) "value")))
|
((prop (get (adv!) "value")))
|
||||||
(when (= (tp-type) "colon") (adv!))
|
(when (= (tp-type) "colon") (adv!))
|
||||||
(let
|
(let
|
||||||
((val (tp-val)))
|
((val (if (and (= (tp-type) "ident") (= (tp-val) "$")) (do (adv!) (when (= (tp-type) "brace-open") (adv!)) (if (= (tp-type) "brace-close") (do (adv!) (if (= (tp-type) "brace-open") (do (adv!) (let ((inner (parse-expr))) (when (= (tp-type) "brace-close") (adv!)) inner)) "")) (let ((expr (parse-expr))) (when (= (tp-type) "brace-close") (adv!)) expr))) (get (adv!) "value"))))
|
||||||
(adv!)
|
|
||||||
(set! pairs (cons (list prop val) pairs))
|
(set! pairs (cons (list prop val) pairs))
|
||||||
(collect-pairs!))))))
|
(collect-pairs!))))))
|
||||||
(collect-pairs!)
|
(collect-pairs!)
|
||||||
@@ -1684,7 +1772,7 @@
|
|||||||
((url (if (and (= (tp-type) "keyword") (= (tp-val) "from")) (do (adv!) (parse-arith (parse-poss (parse-atom)))) nil)))
|
((url (if (and (= (tp-type) "keyword") (= (tp-val) "from")) (do (adv!) (parse-arith (parse-poss (parse-atom)))) nil)))
|
||||||
(list (quote fetch-gql) gql-source url))))
|
(list (quote fetch-gql) gql-source url))))
|
||||||
(let
|
(let
|
||||||
((url-atom (if (and (= (tp-type) "op") (= (tp-val) "/")) (do (adv!) (let ((path-parts (list "/"))) (define read-path (fn () (when (and (not (at-end?)) (or (= (tp-type) "ident") (= (tp-type) "op") (= (tp-type) "dot") (= (tp-type) "number"))) (append! path-parts (tp-val)) (adv!) (read-path)))) (read-path) (join "" path-parts))) (parse-atom))))
|
((url-atom (if (and (= (tp-type) "op") (= (tp-val) "/")) (do (adv!) (let ((path-parts (list "/"))) (define read-path (fn () (when (and (not (at-end?)) (or (and (= (tp-type) "ident") (not (string-contains? (tp-val) "'"))) (= (tp-type) "op") (= (tp-type) "dot") (= (tp-type) "number"))) (append! path-parts (tp-val)) (adv!) (read-path)))) (read-path) (join "" path-parts))) (parse-atom))))
|
||||||
(let
|
(let
|
||||||
((url (if (nil? url-atom) url-atom (parse-arith (parse-poss url-atom)))))
|
((url (if (nil? url-atom) url-atom (parse-arith (parse-poss url-atom)))))
|
||||||
(let
|
(let
|
||||||
@@ -1700,7 +1788,27 @@
|
|||||||
((fmt-after (if (and (not fmt-before) (match-kw "as")) (do (when (and (or (= (tp-type) "ident") (= (tp-type) "keyword")) (or (= (tp-val) "an") (= (tp-val) "a"))) (adv!)) (let ((f (tp-val))) (adv!) f)) nil)))
|
((fmt-after (if (and (not fmt-before) (match-kw "as")) (do (when (and (or (= (tp-type) "ident") (= (tp-type) "keyword")) (or (= (tp-val) "an") (= (tp-val) "a"))) (adv!)) (let ((f (tp-val))) (adv!) f)) nil)))
|
||||||
(let
|
(let
|
||||||
((fmt (or fmt-before fmt-after "text")))
|
((fmt (or fmt-before fmt-after "text")))
|
||||||
(list (quote fetch) url fmt)))))))))
|
(let
|
||||||
|
((do-not-throw
|
||||||
|
(cond
|
||||||
|
((and (or (= (tp-type) "keyword") (= (tp-type) "ident")) (= (tp-val) "do"))
|
||||||
|
(do
|
||||||
|
(adv!)
|
||||||
|
(if (and (or (= (tp-type) "keyword") (= (tp-type) "ident")) (= (tp-val) "not"))
|
||||||
|
(do
|
||||||
|
(adv!)
|
||||||
|
(if (and (or (= (tp-type) "keyword") (= (tp-type) "ident")) (= (tp-val) "throw"))
|
||||||
|
(do (adv!) true)
|
||||||
|
false))
|
||||||
|
false)))
|
||||||
|
((and (= (tp-type) "ident") (= (tp-val) "don't"))
|
||||||
|
(do
|
||||||
|
(adv!)
|
||||||
|
(if (and (or (= (tp-type) "keyword") (= (tp-type) "ident")) (= (tp-val) "throw"))
|
||||||
|
(do (adv!) true)
|
||||||
|
false)))
|
||||||
|
(true false))))
|
||||||
|
(list (quote fetch) url fmt do-not-throw))))))))))
|
||||||
(define
|
(define
|
||||||
parse-call-args
|
parse-call-args
|
||||||
(fn
|
(fn
|
||||||
@@ -2021,7 +2129,21 @@
|
|||||||
((op (cond ((= val "+") (quote +)) ((= val "-") (quote -)) ((= val "*") (quote *)) ((= val "/") (quote /)) ((or (= val "%") (= val "mod")) (make-symbol "%")))))
|
((op (cond ((= val "+") (quote +)) ((= val "-") (quote -)) ((= val "*") (quote *)) ((= val "/") (quote /)) ((or (= val "%") (= val "mod")) (make-symbol "%")))))
|
||||||
(let
|
(let
|
||||||
((right (let ((a (parse-atom))) (if (nil? a) a (parse-poss a)))))
|
((right (let ((a (parse-atom))) (if (nil? a) a (parse-poss a)))))
|
||||||
(parse-arith (list op left right)))))
|
(let
|
||||||
|
((lhs-start (if (and (dict? left) (get left :hs-ast)) (get left :start) 0))
|
||||||
|
(lhs-line
|
||||||
|
(if
|
||||||
|
(and (dict? left) (get left :hs-ast))
|
||||||
|
(get left :line)
|
||||||
|
1)))
|
||||||
|
(parse-arith
|
||||||
|
(hs-ast-wrap
|
||||||
|
(list op left right)
|
||||||
|
"arith"
|
||||||
|
lhs-start
|
||||||
|
(prev-end)
|
||||||
|
lhs-line
|
||||||
|
{:rhs right :lhs left}))))))
|
||||||
left))))
|
left))))
|
||||||
(define
|
(define
|
||||||
parse-the-expr
|
parse-the-expr
|
||||||
@@ -2421,7 +2543,21 @@
|
|||||||
((and (= typ "keyword") (= val "put"))
|
((and (= typ "keyword") (= val "put"))
|
||||||
(do (adv!) (parse-put-cmd)))
|
(do (adv!) (parse-put-cmd)))
|
||||||
((and (= typ "keyword") (= val "if"))
|
((and (= typ "keyword") (= val "if"))
|
||||||
(do (adv!) (parse-if-cmd)))
|
(let
|
||||||
|
((s (cur-start)) (l (cur-line)))
|
||||||
|
(do
|
||||||
|
(adv!)
|
||||||
|
(let
|
||||||
|
((r (parse-if-cmd)))
|
||||||
|
(let
|
||||||
|
((tb (if (and (list? r) (> (len r) 2)) (nth r 2) nil)))
|
||||||
|
(hs-ast-wrap
|
||||||
|
r
|
||||||
|
"if"
|
||||||
|
s
|
||||||
|
(prev-end)
|
||||||
|
l
|
||||||
|
(if tb {:true-branch (if (and (list? tb) (= (first tb) (quote do))) (nth tb 1) tb)} {})))))))
|
||||||
((and (= typ "keyword") (= val "wait"))
|
((and (= typ "keyword") (= val "wait"))
|
||||||
(do (adv!) (parse-wait-cmd)))
|
(do (adv!) (parse-wait-cmd)))
|
||||||
((and (= typ "keyword") (= val "send"))
|
((and (= typ "keyword") (= val "send"))
|
||||||
@@ -2429,7 +2565,17 @@
|
|||||||
((and (= typ "keyword") (= val "trigger"))
|
((and (= typ "keyword") (= val "trigger"))
|
||||||
(do (adv!) (parse-trigger-cmd)))
|
(do (adv!) (parse-trigger-cmd)))
|
||||||
((and (= typ "keyword") (= val "log"))
|
((and (= typ "keyword") (= val "log"))
|
||||||
(do (adv!) (parse-log-cmd)))
|
(let
|
||||||
|
((s (cur-start)) (l (cur-line)))
|
||||||
|
(do
|
||||||
|
(adv!)
|
||||||
|
(hs-ast-wrap
|
||||||
|
(parse-log-cmd)
|
||||||
|
"cmd"
|
||||||
|
s
|
||||||
|
(prev-end)
|
||||||
|
l
|
||||||
|
{}))))
|
||||||
((and (= typ "keyword") (= val "increment"))
|
((and (= typ "keyword") (= val "increment"))
|
||||||
(do (adv!) (parse-inc-cmd)))
|
(do (adv!) (parse-inc-cmd)))
|
||||||
((and (= typ "keyword") (= val "decrement"))
|
((and (= typ "keyword") (= val "decrement"))
|
||||||
@@ -2469,7 +2615,17 @@
|
|||||||
((and (= typ "keyword") (= val "tell"))
|
((and (= typ "keyword") (= val "tell"))
|
||||||
(do (adv!) (parse-tell-cmd)))
|
(do (adv!) (parse-tell-cmd)))
|
||||||
((and (= typ "keyword") (= val "for"))
|
((and (= typ "keyword") (= val "for"))
|
||||||
(do (adv!) (parse-for-cmd)))
|
(let
|
||||||
|
((s (cur-start)) (l (cur-line)))
|
||||||
|
(do
|
||||||
|
(adv!)
|
||||||
|
(hs-ast-wrap
|
||||||
|
(parse-for-cmd)
|
||||||
|
"cmd"
|
||||||
|
s
|
||||||
|
(prev-end)
|
||||||
|
l
|
||||||
|
{}))))
|
||||||
((and (= typ "keyword") (= val "make"))
|
((and (= typ "keyword") (= val "make"))
|
||||||
(do (adv!) (parse-make-cmd)))
|
(do (adv!) (parse-make-cmd)))
|
||||||
((and (= typ "keyword") (= val "install"))
|
((and (= typ "keyword") (= val "install"))
|
||||||
@@ -2591,13 +2747,34 @@
|
|||||||
(true acc2)))))))
|
(true acc2)))))))
|
||||||
(let
|
(let
|
||||||
((cmds (cl-collect (list))))
|
((cmds (cl-collect (list))))
|
||||||
(cond
|
(define
|
||||||
((= (len cmds) 0) nil)
|
link-next-cmds
|
||||||
((= (len cmds) 1) (first cmds))
|
(fn
|
||||||
(true
|
(cmds-list)
|
||||||
(cons
|
(define
|
||||||
(quote do)
|
loop
|
||||||
(filter (fn (c) (not (= c (quote __then__)))) cmds)))))))
|
(fn
|
||||||
|
(i)
|
||||||
|
(when
|
||||||
|
(< i (- (len cmds-list) 1))
|
||||||
|
(let
|
||||||
|
((cur-node (nth cmds-list i))
|
||||||
|
(nxt-node (nth cmds-list (+ i 1))))
|
||||||
|
(when
|
||||||
|
(and (dict? cur-node) (get cur-node :hs-ast))
|
||||||
|
(dict-set! (get cur-node :fields) "next" nxt-node)))
|
||||||
|
(loop (+ i 1)))))
|
||||||
|
(loop 0)
|
||||||
|
cmds-list))
|
||||||
|
(let
|
||||||
|
((linked (if hs-span-mode (link-next-cmds cmds) cmds)))
|
||||||
|
(cond
|
||||||
|
((= (len linked) 0) nil)
|
||||||
|
((= (len linked) 1) (first linked))
|
||||||
|
(true
|
||||||
|
(cons
|
||||||
|
(quote do)
|
||||||
|
(filter (fn (c) (not (= c (quote __then__)))) linked))))))))
|
||||||
(define
|
(define
|
||||||
parse-on-feat
|
parse-on-feat
|
||||||
(fn
|
(fn
|
||||||
@@ -2806,7 +2983,10 @@
|
|||||||
((= 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 "socket") (do (adv!) (parse-socket-feat)))
|
((= val "socket") (do (adv!) (parse-socket-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
|
||||||
@@ -2825,4 +3005,12 @@
|
|||||||
(first features)
|
(first features)
|
||||||
(cons (quote do) features))))))
|
(cons (quote do) features))))))
|
||||||
|
|
||||||
|
(define hs-span-mode false)
|
||||||
|
|
||||||
(define hs-compile (fn (src) (hs-parse (hs-tokenize src) src)))
|
(define hs-compile (fn (src) (hs-parse (hs-tokenize src) src)))
|
||||||
|
|
||||||
|
(define hs-parse-ast
|
||||||
|
(fn (src)
|
||||||
|
(set! hs-span-mode true)
|
||||||
|
(let ((result (hs-parse (hs-tokenize src) src)))
|
||||||
|
(do (set! hs-span-mode false) result))))
|
||||||
|
|||||||
@@ -48,7 +48,7 @@
|
|||||||
(fn
|
(fn
|
||||||
(target event-name handler)
|
(target event-name handler)
|
||||||
(let
|
(let
|
||||||
((wrapped (fn (event) (guard (e ((and (not (= event-name "exception")) (not (= event-name "error"))) (dom-dispatch target "exception" {:error e})) (true (raise e))) (handler event)))))
|
((wrapped (fn (event) (guard (e ((and (not (= event-name "exception")) (not (= event-name "error"))) (dom-dispatch target "exception" {:error e})) (true (raise e))) (do (handler event) (when event (host-call event "stopPropagation")))))))
|
||||||
(let
|
(let
|
||||||
((unlisten (dom-listen target event-name wrapped))
|
((unlisten (dom-listen target event-name wrapped))
|
||||||
(prev (or (dom-get-data target "hs-unlisteners") (list))))
|
(prev (or (dom-get-data target "hs-unlisteners") (list))))
|
||||||
@@ -650,9 +650,7 @@
|
|||||||
(true (find-prev (dom-get-prop el "previousElementSibling"))))))
|
(true (find-prev (dom-get-prop el "previousElementSibling"))))))
|
||||||
(find-prev sibling)))))
|
(find-prev sibling)))))
|
||||||
|
|
||||||
(define
|
(define hs-query-all (fn (sel) (dom-query-all (dom-body) sel)))
|
||||||
hs-query-all
|
|
||||||
(fn (sel) (host-call (dom-body) "querySelectorAll" sel)))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -662,10 +660,7 @@
|
|||||||
hs-query-all-in
|
hs-query-all-in
|
||||||
(fn
|
(fn
|
||||||
(sel target)
|
(sel target)
|
||||||
(if
|
(if (nil? target) (hs-query-all sel) (dom-query-all target sel))))
|
||||||
(nil? target)
|
|
||||||
(hs-query-all sel)
|
|
||||||
(host-call target "querySelectorAll" sel))))
|
|
||||||
|
|
||||||
(define
|
(define
|
||||||
hs-list-set
|
hs-list-set
|
||||||
@@ -874,12 +869,33 @@
|
|||||||
(define
|
(define
|
||||||
hs-fetch
|
hs-fetch
|
||||||
(fn
|
(fn
|
||||||
(url format)
|
(url format do-not-throw target)
|
||||||
(let
|
(let
|
||||||
((fmt (cond ((nil? format) "text") ((or (= format "json") (= format "JSON") (= 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")) "json") ((or (= format "html") (= format "HTML")) "html") ((or (= format "response") (= format "Response")) "response") ((or (= format "text") (= format "Text")) "text") ((or (= format "number") (= format "Number")) "number") (true format))))
|
||||||
(let
|
(do
|
||||||
((raw (perform (list "io-fetch" url fmt))))
|
(when (not (nil? target))
|
||||||
(cond ((= fmt "json") (hs-host-to-sx raw)) (true raw))))))
|
(dom-dispatch target "hyperscript:beforeFetch" nil))
|
||||||
|
(let
|
||||||
|
((raw (perform (list "io-fetch" url "response" (dict)))))
|
||||||
|
(do
|
||||||
|
(when (get raw :_network-error) (raise {:response raw :message "Network error" :_hs-error "FetchError"}))
|
||||||
|
(when
|
||||||
|
(and (not (get raw :ok)) (not (= fmt "response")) (not do-not-throw))
|
||||||
|
(raise {:response raw :status (get raw :status) :message "Fetch error" :_hs-error "FetchError"}))
|
||||||
|
(cond
|
||||||
|
((= fmt "response") raw)
|
||||||
|
((= fmt "json")
|
||||||
|
(let
|
||||||
|
((parsed (perform (list "io-parse-json" (get raw :_json)))))
|
||||||
|
(hs-host-to-sx parsed)))
|
||||||
|
((= fmt "html")
|
||||||
|
(perform (list "io-parse-html" (get raw :_html))))
|
||||||
|
((= fmt "number")
|
||||||
|
(or
|
||||||
|
(parse-number (get raw :_number))
|
||||||
|
(parse-number (get raw :_body))
|
||||||
|
0))
|
||||||
|
(true (get raw :_body)))))))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
hs-json-escape
|
hs-json-escape
|
||||||
@@ -970,6 +986,8 @@
|
|||||||
(true (str value))))
|
(true (str value))))
|
||||||
((= type-name "JSON")
|
((= type-name "JSON")
|
||||||
(cond
|
(cond
|
||||||
|
((and (dict? value) (dict-has? value :_json))
|
||||||
|
(guard (_e (true value)) (json-parse (get value :_json))))
|
||||||
((string? value) (guard (_e (true value)) (json-parse value)))
|
((string? value) (guard (_e (true value)) (json-parse value)))
|
||||||
((dict? value) (hs-json-stringify value))
|
((dict? value) (hs-json-stringify value))
|
||||||
((list? value) (hs-json-stringify value))
|
((list? value) (hs-json-stringify value))
|
||||||
@@ -1418,6 +1436,15 @@
|
|||||||
hs-strict-eq
|
hs-strict-eq
|
||||||
(fn (a b) (and (= (type-of a) (type-of b)) (= a b))))
|
(fn (a b) (and (= (type-of a) (type-of b)) (= a b))))
|
||||||
|
|
||||||
|
(define
|
||||||
|
hs-id=
|
||||||
|
(fn
|
||||||
|
(a b)
|
||||||
|
(if
|
||||||
|
(and (= (host-typeof a) "element") (= (host-typeof b) "element"))
|
||||||
|
(hs-ref-eq a b)
|
||||||
|
(= a b))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
hs-eq-ignore-case
|
hs-eq-ignore-case
|
||||||
(fn (a b) (= (downcase (str a)) (downcase (str b)))))
|
(fn (a b) (= (downcase (str a)) (downcase (str b)))))
|
||||||
@@ -2097,20 +2124,11 @@
|
|||||||
(fn
|
(fn
|
||||||
(pairs)
|
(pairs)
|
||||||
(let
|
(let
|
||||||
((d {}) (order (list)))
|
((d (dict)))
|
||||||
(do
|
(begin
|
||||||
(for-each
|
(for-each
|
||||||
(fn
|
(fn (pair) (dict-set! d (first pair) (nth pair 1)))
|
||||||
(pair)
|
|
||||||
(let
|
|
||||||
((k (first pair)))
|
|
||||||
(do
|
|
||||||
(when
|
|
||||||
(not (dict-has? d k))
|
|
||||||
(set! order (append order (list k))))
|
|
||||||
(dict-set! d k (nth pair 1)))))
|
|
||||||
pairs)
|
pairs)
|
||||||
(when (not (empty? order)) (dict-set! d "_order" order))
|
|
||||||
d))))
|
d))))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
@@ -2520,12 +2538,61 @@
|
|||||||
((= a b) true)
|
((= a b) true)
|
||||||
(true (hs-dom-is-ancestor? a (dom-parent b))))))
|
(true (hs-dom-is-ancestor? a (dom-parent b))))))
|
||||||
|
|
||||||
|
;; ── SourceInfo API ────────────────────────────────────────────────
|
||||||
|
|
||||||
(define
|
(define
|
||||||
hs-win-call
|
hs-win-call
|
||||||
(fn
|
(fn
|
||||||
(fn-name args)
|
(fn-name args)
|
||||||
(let ((fn (host-global fn-name))) (if fn (host-call-fn fn args) nil))))
|
(let ((fn (host-global fn-name))) (if fn (host-call-fn fn args) nil))))
|
||||||
|
|
||||||
|
(define
|
||||||
|
hs-source-for
|
||||||
|
(fn
|
||||||
|
(node)
|
||||||
|
(substring (get node :src) (get node :start) (get node :end))))
|
||||||
|
|
||||||
|
(define
|
||||||
|
hs-line-for
|
||||||
|
(fn
|
||||||
|
(node)
|
||||||
|
(let
|
||||||
|
((lines (split (get node :src) "\n"))
|
||||||
|
(line-idx (- (get node :line) 1)))
|
||||||
|
(if (< line-idx (len lines)) (nth lines line-idx) ""))))
|
||||||
|
|
||||||
|
(define hs-node-get (fn (node key) (get (get node :fields) key)))
|
||||||
|
|
||||||
|
(define hs-src (fn (src-str) (hs-source-for (hs-parse-ast src-str))))
|
||||||
|
|
||||||
|
(define
|
||||||
|
hs-src-at
|
||||||
|
(fn
|
||||||
|
(src-str path)
|
||||||
|
(define
|
||||||
|
walk
|
||||||
|
(fn
|
||||||
|
(node keys)
|
||||||
|
(if
|
||||||
|
(or (nil? keys) (= (len keys) 0))
|
||||||
|
node
|
||||||
|
(walk (hs-node-get node (first keys)) (rest keys)))))
|
||||||
|
(hs-source-for (walk (hs-parse-ast src-str) path))))
|
||||||
|
|
||||||
|
(define
|
||||||
|
hs-line-at
|
||||||
|
(fn
|
||||||
|
(src-str path)
|
||||||
|
(define
|
||||||
|
walk
|
||||||
|
(fn
|
||||||
|
(node keys)
|
||||||
|
(if
|
||||||
|
(or (nil? keys) (= (len keys) 0))
|
||||||
|
node
|
||||||
|
(walk (hs-node-get node (first keys)) (rest keys)))))
|
||||||
|
(hs-line-for (walk (hs-parse-ast src-str) path))))
|
||||||
|
|
||||||
;; ── WebSocket / socket feature ───────────────────────────────────
|
;; ── WebSocket / socket feature ───────────────────────────────────
|
||||||
|
|
||||||
(define
|
(define
|
||||||
@@ -2634,3 +2701,4 @@
|
|||||||
(bind-path! next rest-path))))))
|
(bind-path! next rest-path))))))
|
||||||
(bind-path! (host-global "window") name-path)
|
(bind-path! (host-global "window") name-path)
|
||||||
wrapper)))))
|
wrapper)))))
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,27 @@
|
|||||||
|
|
||||||
(define hs-ws? (fn (c) (or (= c " ") (= c "\t") (= c "\n") (= c "\r"))))
|
(define hs-ws? (fn (c) (or (= c " ") (= c "\t") (= c "\n") (= c "\r"))))
|
||||||
|
|
||||||
|
(define
|
||||||
|
hs-hex-digit?
|
||||||
|
(fn
|
||||||
|
(c)
|
||||||
|
(or
|
||||||
|
(and (>= c "0") (<= c "9"))
|
||||||
|
(and (>= c "a") (<= c "f"))
|
||||||
|
(and (>= c "A") (<= c "F")))))
|
||||||
|
|
||||||
|
(define
|
||||||
|
hs-hex-val
|
||||||
|
(fn
|
||||||
|
(c)
|
||||||
|
(let
|
||||||
|
((code (char-code c)))
|
||||||
|
(cond
|
||||||
|
((and (>= code 48) (<= code 57)) (- code 48))
|
||||||
|
((and (>= code 65) (<= code 70)) (- code 55))
|
||||||
|
((and (>= code 97) (<= code 102)) (- code 87))
|
||||||
|
(true 0)))))
|
||||||
|
|
||||||
;; ── Keyword set ───────────────────────────────────────────────────
|
;; ── Keyword set ───────────────────────────────────────────────────
|
||||||
|
|
||||||
(define
|
(define
|
||||||
@@ -235,10 +256,15 @@
|
|||||||
read-number
|
read-number
|
||||||
(fn
|
(fn
|
||||||
(start)
|
(start)
|
||||||
(when
|
(define
|
||||||
(and (< pos src-len) (hs-digit? (hs-cur)))
|
read-int
|
||||||
(hs-advance! 1)
|
(fn
|
||||||
(read-number start))
|
()
|
||||||
|
(when
|
||||||
|
(and (< pos src-len) (hs-digit? (hs-cur)))
|
||||||
|
(hs-advance! 1)
|
||||||
|
(read-int))))
|
||||||
|
(read-int)
|
||||||
(when
|
(when
|
||||||
(and
|
(and
|
||||||
(< pos src-len)
|
(< pos src-len)
|
||||||
@@ -246,15 +272,7 @@
|
|||||||
(< (+ pos 1) src-len)
|
(< (+ pos 1) src-len)
|
||||||
(hs-digit? (hs-peek 1)))
|
(hs-digit? (hs-peek 1)))
|
||||||
(hs-advance! 1)
|
(hs-advance! 1)
|
||||||
(define
|
(read-int))
|
||||||
read-frac
|
|
||||||
(fn
|
|
||||||
()
|
|
||||||
(when
|
|
||||||
(and (< pos src-len) (hs-digit? (hs-cur)))
|
|
||||||
(hs-advance! 1)
|
|
||||||
(read-frac))))
|
|
||||||
(read-frac))
|
|
||||||
(do
|
(do
|
||||||
(when
|
(when
|
||||||
(and
|
(and
|
||||||
@@ -272,15 +290,7 @@
|
|||||||
(< pos src-len)
|
(< pos src-len)
|
||||||
(or (= (hs-cur) "+") (= (hs-cur) "-")))
|
(or (= (hs-cur) "+") (= (hs-cur) "-")))
|
||||||
(hs-advance! 1))
|
(hs-advance! 1))
|
||||||
(define
|
(read-int))
|
||||||
read-exp-digits
|
|
||||||
(fn
|
|
||||||
()
|
|
||||||
(when
|
|
||||||
(and (< pos src-len) (hs-digit? (hs-cur)))
|
|
||||||
(hs-advance! 1)
|
|
||||||
(read-exp-digits))))
|
|
||||||
(read-exp-digits))
|
|
||||||
(let
|
(let
|
||||||
((num-end pos))
|
((num-end pos))
|
||||||
(when
|
(when
|
||||||
@@ -308,7 +318,7 @@
|
|||||||
()
|
()
|
||||||
(cond
|
(cond
|
||||||
(>= pos src-len)
|
(>= pos src-len)
|
||||||
nil
|
(error "Unterminated string")
|
||||||
(= (hs-cur) "\\")
|
(= (hs-cur) "\\")
|
||||||
(do
|
(do
|
||||||
(hs-advance! 1)
|
(hs-advance! 1)
|
||||||
@@ -318,15 +328,37 @@
|
|||||||
((ch (hs-cur)))
|
((ch (hs-cur)))
|
||||||
(cond
|
(cond
|
||||||
(= ch "n")
|
(= ch "n")
|
||||||
(append! chars "\n")
|
(do (append! chars "\n") (hs-advance! 1))
|
||||||
(= ch "t")
|
(= ch "t")
|
||||||
(append! chars "\t")
|
(do (append! chars "\t") (hs-advance! 1))
|
||||||
|
(= ch "r")
|
||||||
|
(do (append! chars "\r") (hs-advance! 1))
|
||||||
|
(= ch "b")
|
||||||
|
(do (append! chars (char-from-code 8)) (hs-advance! 1))
|
||||||
|
(= ch "f")
|
||||||
|
(do (append! chars (char-from-code 12)) (hs-advance! 1))
|
||||||
|
(= ch "v")
|
||||||
|
(do (append! chars (char-from-code 11)) (hs-advance! 1))
|
||||||
(= ch "\\")
|
(= ch "\\")
|
||||||
(append! chars "\\")
|
(do (append! chars "\\") (hs-advance! 1))
|
||||||
(= ch quote-char)
|
(= ch quote-char)
|
||||||
(append! chars quote-char)
|
(do (append! chars quote-char) (hs-advance! 1))
|
||||||
:else (do (append! chars "\\") (append! chars ch)))
|
(= ch "x")
|
||||||
(hs-advance! 1)))
|
(do
|
||||||
|
(hs-advance! 1)
|
||||||
|
(if
|
||||||
|
(and
|
||||||
|
(< (+ pos 1) src-len)
|
||||||
|
(hs-hex-digit? (hs-cur))
|
||||||
|
(hs-hex-digit? (hs-peek 1)))
|
||||||
|
(let
|
||||||
|
((d1 (hs-hex-val (hs-cur)))
|
||||||
|
(d2 (hs-hex-val (hs-peek 1))))
|
||||||
|
(append! chars (char-from-code (+ (* d1 16) d2)))
|
||||||
|
(hs-advance! 2))
|
||||||
|
(error "Invalid hexadecimal escape: \\x")))
|
||||||
|
:else
|
||||||
|
(do (append! chars "\\") (append! chars ch) (hs-advance! 1)))))
|
||||||
(loop))
|
(loop))
|
||||||
(= (hs-cur) quote-char)
|
(= (hs-cur) quote-char)
|
||||||
(hs-advance! 1)
|
(hs-advance! 1)
|
||||||
@@ -540,10 +572,26 @@
|
|||||||
(do
|
(do
|
||||||
(let
|
(let
|
||||||
((word (read-ident start)))
|
((word (read-ident start)))
|
||||||
(hs-emit!
|
(let
|
||||||
(if (hs-keyword? word) "keyword" "ident")
|
((full-word
|
||||||
word
|
(if
|
||||||
start))
|
(and
|
||||||
|
(< pos src-len)
|
||||||
|
(= (hs-cur) "'")
|
||||||
|
(< (+ pos 1) src-len)
|
||||||
|
(hs-letter? (hs-peek 1))
|
||||||
|
(not
|
||||||
|
(and
|
||||||
|
(= (hs-peek 1) "s")
|
||||||
|
(or
|
||||||
|
(>= (+ pos 2) src-len)
|
||||||
|
(not (hs-ident-char? (hs-peek 2)))))))
|
||||||
|
(do (hs-advance! 1) (str word "'" (read-ident pos)))
|
||||||
|
word)))
|
||||||
|
(hs-emit!
|
||||||
|
(if (hs-keyword? full-word) "keyword" "ident")
|
||||||
|
full-word
|
||||||
|
start)))
|
||||||
(scan!))
|
(scan!))
|
||||||
(and
|
(and
|
||||||
(or (= ch "=") (= ch "!") (= ch "<") (= ch ">"))
|
(or (= ch "=") (= ch "!") (= ch "<") (= ch ">"))
|
||||||
@@ -624,7 +672,82 @@
|
|||||||
(do (hs-emit! "colon" ":" start) (hs-advance! 1) (scan!))
|
(do (hs-emit! "colon" ":" start) (hs-advance! 1) (scan!))
|
||||||
(= ch "|")
|
(= ch "|")
|
||||||
(do (hs-emit! "op" "|" start) (hs-advance! 1) (scan!))
|
(do (hs-emit! "op" "|" start) (hs-advance! 1) (scan!))
|
||||||
|
(= ch "&")
|
||||||
|
(do (hs-emit! "op" "&" start) (hs-advance! 1) (scan!))
|
||||||
|
(= ch "#")
|
||||||
|
(do (hs-emit! "op" "#" start) (hs-advance! 1) (scan!))
|
||||||
|
(= ch "?")
|
||||||
|
(do (hs-emit! "op" "?" start) (hs-advance! 1) (scan!))
|
||||||
|
(= ch ";")
|
||||||
|
(do (hs-emit! "op" ";" start) (hs-advance! 1) (scan!))
|
||||||
:else (do (hs-advance! 1) (scan!)))))))
|
:else (do (hs-advance! 1) (scan!)))))))
|
||||||
(scan!)
|
(scan!)
|
||||||
(hs-emit! "eof" nil pos)
|
(hs-emit! "eof" nil pos)
|
||||||
|
tokens)))
|
||||||
|
|
||||||
|
;; ── Template-mode tokenizer (E37 API) ────────────────────────────────
|
||||||
|
;; Used by hs-tokens-of when :template flag is set.
|
||||||
|
;; Emits outer " chars as single STRING tokens; ${ ... } as $ { <inner-tokens> };
|
||||||
|
;; inner content is tokenized with the regular hs-tokenize.
|
||||||
|
|
||||||
|
(define
|
||||||
|
hs-tokenize-template
|
||||||
|
(fn
|
||||||
|
(src)
|
||||||
|
(let
|
||||||
|
((tokens (list)) (pos 0) (src-len (len src)))
|
||||||
|
(define t-cur (fn () (if (< pos src-len) (nth src pos) nil)))
|
||||||
|
(define t-peek (fn (n) (if (< (+ pos n) src-len) (nth src (+ pos n)) nil)))
|
||||||
|
(define t-advance! (fn (n) (set! pos (+ pos n))))
|
||||||
|
(define t-emit! (fn (type value) (append! tokens (hs-make-token type value pos))))
|
||||||
|
(define
|
||||||
|
scan-to-close!
|
||||||
|
(fn
|
||||||
|
(depth)
|
||||||
|
(when
|
||||||
|
(and (< pos src-len) (> depth 0))
|
||||||
|
(cond
|
||||||
|
(= (t-cur) "{")
|
||||||
|
(do (t-advance! 1) (scan-to-close! (+ depth 1)))
|
||||||
|
(= (t-cur) "}")
|
||||||
|
(when (> (- depth 1) 0) (t-advance! 1) (scan-to-close! (- depth 1)))
|
||||||
|
:else (do (t-advance! 1) (scan-to-close! depth))))))
|
||||||
|
(define
|
||||||
|
scan-template!
|
||||||
|
(fn
|
||||||
|
()
|
||||||
|
(when
|
||||||
|
(< pos src-len)
|
||||||
|
(let
|
||||||
|
((ch (t-cur)))
|
||||||
|
(cond
|
||||||
|
(= ch "\"")
|
||||||
|
(do (t-emit! "string" "\"") (t-advance! 1) (scan-template!))
|
||||||
|
(and (= ch "$") (= (t-peek 1) "{"))
|
||||||
|
(do
|
||||||
|
(t-emit! "op" "$")
|
||||||
|
(t-advance! 1)
|
||||||
|
(t-emit! "brace-open" "{")
|
||||||
|
(t-advance! 1)
|
||||||
|
(let
|
||||||
|
((inner-start pos))
|
||||||
|
(scan-to-close! 1)
|
||||||
|
(let
|
||||||
|
((inner-src (slice src inner-start pos))
|
||||||
|
(inner-toks (hs-tokenize inner-src)))
|
||||||
|
(for-each
|
||||||
|
(fn (tok)
|
||||||
|
(when (not (= (get tok "type") "eof"))
|
||||||
|
(append! tokens tok)))
|
||||||
|
inner-toks))
|
||||||
|
(t-emit! "brace-close" "}")
|
||||||
|
(when (< pos src-len) (t-advance! 1)))
|
||||||
|
(scan-template!))
|
||||||
|
(= ch "$")
|
||||||
|
(do (t-emit! "op" "$") (t-advance! 1) (scan-template!))
|
||||||
|
(hs-ws? ch)
|
||||||
|
(do (t-advance! 1) (scan-template!))
|
||||||
|
:else (do (t-advance! 1) (scan-template!)))))))
|
||||||
|
(scan-template!)
|
||||||
|
(t-emit! "eof" nil)
|
||||||
tokens)))
|
tokens)))
|
||||||
@@ -1992,8 +1992,8 @@
|
|||||||
(dom-set-attr _el-d2 "id" "d2")
|
(dom-set-attr _el-d2 "id" "d2")
|
||||||
(dom-set-attr _el-div "_" "on click make a <p/> then put #i1.value into its textContent put it.outerHTML at end of #d2")
|
(dom-set-attr _el-div "_" "on click make a <p/> then put #i1.value into its textContent put it.outerHTML at end of #d2")
|
||||||
(dom-append (dom-body) _el-i1)
|
(dom-append (dom-body) _el-i1)
|
||||||
(dom-append _el-i1 _el-d2)
|
(dom-append (dom-body) _el-d2)
|
||||||
(dom-append _el-i1 _el-div)
|
(dom-append (dom-body) _el-div)
|
||||||
(hs-activate! _el-div)
|
(hs-activate! _el-div)
|
||||||
(dom-dispatch (dom-query "div:nth-of-type(2)") "click" nil)
|
(dom-dispatch (dom-query "div:nth-of-type(2)") "click" nil)
|
||||||
))
|
))
|
||||||
@@ -2467,53 +2467,314 @@
|
|||||||
;; ── core/sourceInfo (4 tests) ──
|
;; ── core/sourceInfo (4 tests) ──
|
||||||
(defsuite "hs-upstream-core/sourceInfo"
|
(defsuite "hs-upstream-core/sourceInfo"
|
||||||
(deftest "debug"
|
(deftest "debug"
|
||||||
(error "SKIP (untranslated): debug"))
|
(assert= (hs-src "<button.foo/>") "<button.foo/>"))
|
||||||
(deftest "get line works for statements"
|
(deftest "get line works for statements"
|
||||||
(error "SKIP (untranslated): get line works for statements"))
|
(assert= (hs-line-at "if true\n log 'it was true'\n log 'it was true'" (list)) "if true")
|
||||||
|
(assert= (hs-line-at "if true\n log 'it was true'\n log 'it was true'" (list :true-branch)) " log 'it was true'")
|
||||||
|
(assert= (hs-line-at "if true\n log 'it was true'\n log 'it was true'" (list :true-branch :next)) " log 'it was true'"))
|
||||||
(deftest "get source works for expressions"
|
(deftest "get source works for expressions"
|
||||||
(error "SKIP (untranslated): get source works for expressions"))
|
(assert= (hs-src "1") "1")
|
||||||
|
(assert= (hs-src "a.b") "a.b")
|
||||||
|
(assert= (hs-src-at "a.b" (list :root)) "a")
|
||||||
|
(assert= (hs-src "a.b()") "a.b()")
|
||||||
|
(assert= (hs-src-at "a.b()" (list :root)) "a.b")
|
||||||
|
(assert= (hs-src-at "a.b()" (list :root :root)) "a")
|
||||||
|
(assert= (hs-src "<button.foo/>") "<button.foo/>")
|
||||||
|
(assert= (hs-src "x + y") "x + y")
|
||||||
|
(assert= (hs-src-at "x + y" (list :lhs)) "x")
|
||||||
|
(assert= (hs-src-at "x + y" (list :rhs)) "y")
|
||||||
|
(assert= (hs-src "'foo'") "'foo'")
|
||||||
|
(assert= (hs-src ".foo") ".foo")
|
||||||
|
(assert= (hs-src "#bar") "#bar"))
|
||||||
(deftest "get source works for statements"
|
(deftest "get source works for statements"
|
||||||
(error "SKIP (untranslated): get source works for statements"))
|
(assert= (hs-src "if true log 'it was true'") "if true log 'it was true'")
|
||||||
|
(assert= (hs-src "for x in [1, 2, 3] log x then log x end") "for x in [1, 2, 3] log x then log x end"))
|
||||||
)
|
)
|
||||||
|
|
||||||
;; ── core/tokenizer (17 tests) ──
|
;; ── core/tokenizer (17 tests) ──
|
||||||
(defsuite "hs-upstream-core/tokenizer"
|
(defsuite "hs-upstream-core/tokenizer"
|
||||||
(deftest "handles $ in template properly"
|
(deftest "handles $ in template properly"
|
||||||
(error "SKIP (untranslated): handles $ in template properly"))
|
(assert= (hs-token-value (hs-stream-token (hs-tokens-of "\"" :template) 0)) "\"")
|
||||||
|
)
|
||||||
(deftest "handles all special escapes properly"
|
(deftest "handles all special escapes properly"
|
||||||
(error "SKIP (untranslated): handles all special escapes properly"))
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "\"\\b\""))) (char-from-code 8))
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "\"\\f\""))) (char-from-code 12))
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "\"\\n\""))) "\n")
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "\"\\r\""))) "\r")
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "\"\\t\""))) "\t")
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "\"\\v\""))) (char-from-code 11))
|
||||||
|
)
|
||||||
(deftest "handles basic token types"
|
(deftest "handles basic token types"
|
||||||
(error "SKIP (untranslated): handles basic token types"))
|
(assert= (hs-token-type (hs-stream-consume (hs-tokens-of "foo"))) "IDENTIFIER")
|
||||||
|
(assert= (hs-token-type (hs-stream-consume (hs-tokens-of "1"))) "NUMBER")
|
||||||
|
(let ((s (hs-tokens-of "1.1")))
|
||||||
|
(let ((tok (hs-stream-consume s)))
|
||||||
|
(assert= (hs-token-type tok) "NUMBER")
|
||||||
|
(assert= (hs-stream-has-more s) false)))
|
||||||
|
(let ((s (hs-tokens-of "1e6")))
|
||||||
|
(let ((tok (hs-stream-consume s)))
|
||||||
|
(assert= (hs-token-type tok) "NUMBER")
|
||||||
|
(assert= (hs-stream-has-more s) false)))
|
||||||
|
(let ((s (hs-tokens-of "1e-6")))
|
||||||
|
(let ((tok (hs-stream-consume s)))
|
||||||
|
(assert= (hs-token-type tok) "NUMBER")
|
||||||
|
(assert= (hs-stream-has-more s) false)))
|
||||||
|
(let ((s (hs-tokens-of "1.1e6")))
|
||||||
|
(let ((tok (hs-stream-consume s)))
|
||||||
|
(assert= (hs-token-type tok) "NUMBER")
|
||||||
|
(assert= (hs-stream-has-more s) false)))
|
||||||
|
(let ((s (hs-tokens-of "1.1e-6")))
|
||||||
|
(let ((tok (hs-stream-consume s)))
|
||||||
|
(assert= (hs-token-type tok) "NUMBER")
|
||||||
|
(assert= (hs-stream-has-more s) false)))
|
||||||
|
(assert= (hs-token-type (hs-stream-consume (hs-tokens-of ".a"))) "CLASS_REF")
|
||||||
|
(assert= (hs-token-type (hs-stream-consume (hs-tokens-of "#a"))) "ID_REF")
|
||||||
|
(assert= (hs-token-type (hs-stream-consume (hs-tokens-of "\"asdf\""))) "STRING")
|
||||||
|
)
|
||||||
(deftest "handles class identifiers properly"
|
(deftest "handles class identifiers properly"
|
||||||
(error "SKIP (untranslated): handles class identifiers properly"))
|
(assert= (hs-token-type (hs-stream-consume (hs-tokens-of ".a"))) "CLASS_REF")
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of ".a"))) ".a")
|
||||||
|
(assert= (hs-token-type (hs-stream-consume (hs-tokens-of " .a"))) "CLASS_REF")
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of " .a"))) ".a")
|
||||||
|
(assert= (hs-token-type (hs-stream-consume (hs-tokens-of "a.a"))) "IDENTIFIER")
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "a.a"))) "a")
|
||||||
|
(assert= (hs-token-type (nth (get (hs-tokens-of "(a).a") "list") 4)) "IDENTIFIER")
|
||||||
|
(assert= (hs-token-value (nth (get (hs-tokens-of "(a).a") "list") 4)) "a")
|
||||||
|
(assert= (hs-token-type (nth (get (hs-tokens-of "{a}.a") "list") 4)) "IDENTIFIER")
|
||||||
|
(assert= (hs-token-value (nth (get (hs-tokens-of "{a}.a") "list") 4)) "a")
|
||||||
|
(assert= (hs-token-type (nth (get (hs-tokens-of "[a].a") "list") 4)) "IDENTIFIER")
|
||||||
|
(assert= (hs-token-value (nth (get (hs-tokens-of "[a].a") "list") 4)) "a")
|
||||||
|
(assert= (hs-token-type (nth (get (hs-tokens-of "(a(.a") "list") 3)) "CLASS_REF")
|
||||||
|
(assert= (hs-token-value (nth (get (hs-tokens-of "(a(.a") "list") 3)) ".a")
|
||||||
|
(assert= (hs-token-type (nth (get (hs-tokens-of "{a{.a") "list") 3)) "CLASS_REF")
|
||||||
|
(assert= (hs-token-value (nth (get (hs-tokens-of "{a{.a") "list") 3)) ".a")
|
||||||
|
(assert= (hs-token-type (nth (get (hs-tokens-of "[a[.a") "list") 3)) "CLASS_REF")
|
||||||
|
(assert= (hs-token-value (nth (get (hs-tokens-of "[a[.a") "list") 3)) ".a")
|
||||||
|
)
|
||||||
(deftest "handles comments properly"
|
(deftest "handles comments properly"
|
||||||
(error "SKIP (untranslated): handles comments properly"))
|
(assert= (len (get (hs-tokens-of "--") "list")) 0)
|
||||||
|
(assert= (len (get (hs-tokens-of "asdf--") "list")) 1)
|
||||||
|
(assert= (len (get (hs-tokens-of "-- asdf") "list")) 0)
|
||||||
|
(assert= (len (get (hs-tokens-of "--\nasdf") "list")) 1)
|
||||||
|
(assert= (len (get (hs-tokens-of "--\nasdf--") "list")) 1)
|
||||||
|
(assert= (len (get (hs-tokens-of "---asdf") "list")) 0)
|
||||||
|
(assert= (len (get (hs-tokens-of "----\n---asdf") "list")) 0)
|
||||||
|
(assert= (len (get (hs-tokens-of "----asdf----") "list")) 0)
|
||||||
|
(assert= (len (get (hs-tokens-of "---\nasdf---") "list")) 1)
|
||||||
|
(assert= (len (get (hs-tokens-of "// asdf") "list")) 0)
|
||||||
|
(assert= (len (get (hs-tokens-of "///asdf") "list")) 0)
|
||||||
|
(assert= (len (get (hs-tokens-of "asdf//") "list")) 1)
|
||||||
|
(assert= (len (get (hs-tokens-of "asdf\n//") "list")) 2)
|
||||||
|
)
|
||||||
(deftest "handles hex escapes properly"
|
(deftest "handles hex escapes properly"
|
||||||
(error "SKIP (untranslated): handles hex escapes properly"))
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "\"\\x1f\""))) (char-from-code 31))
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "\"\\x41\""))) "A")
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "\"\\x41\\x61\""))) "Aa")
|
||||||
|
(let ((threw false))
|
||||||
|
(guard (e (true (set! threw true))) (hs-stream-consume (hs-tokens-of "\"\\x\"")))
|
||||||
|
(assert threw))
|
||||||
|
(let ((threw false))
|
||||||
|
(guard (e (true (set! threw true))) (hs-stream-consume (hs-tokens-of "\"\\xGG\"")))
|
||||||
|
(assert threw))
|
||||||
|
(let ((threw false))
|
||||||
|
(guard (e (true (set! threw true))) (hs-stream-consume (hs-tokens-of "\"\\x4\"")))
|
||||||
|
(assert threw))
|
||||||
|
)
|
||||||
(deftest "handles id references properly"
|
(deftest "handles id references properly"
|
||||||
(error "SKIP (untranslated): handles id references properly"))
|
(assert= (hs-token-type (hs-stream-consume (hs-tokens-of "#a"))) "ID_REF")
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "#a"))) "#a")
|
||||||
|
(assert= (hs-token-type (hs-stream-consume (hs-tokens-of " #a"))) "ID_REF")
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of " #a"))) "#a")
|
||||||
|
(assert= (hs-token-type (hs-stream-consume (hs-tokens-of "a#a"))) "IDENTIFIER")
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "a#a"))) "a")
|
||||||
|
(assert= (hs-token-type (nth (get (hs-tokens-of "(a)#a") "list") 4)) "IDENTIFIER")
|
||||||
|
(assert= (hs-token-value (nth (get (hs-tokens-of "(a)#a") "list") 4)) "a")
|
||||||
|
(assert= (hs-token-type (nth (get (hs-tokens-of "{a}#a") "list") 4)) "IDENTIFIER")
|
||||||
|
(assert= (hs-token-value (nth (get (hs-tokens-of "{a}#a") "list") 4)) "a")
|
||||||
|
(assert= (hs-token-type (nth (get (hs-tokens-of "[a]#a") "list") 4)) "IDENTIFIER")
|
||||||
|
(assert= (hs-token-value (nth (get (hs-tokens-of "[a]#a") "list") 4)) "a")
|
||||||
|
(assert= (hs-token-type (nth (get (hs-tokens-of "(a(#a") "list") 3)) "ID_REF")
|
||||||
|
(assert= (hs-token-value (nth (get (hs-tokens-of "(a(#a") "list") 3)) "#a")
|
||||||
|
(assert= (hs-token-type (nth (get (hs-tokens-of "{a{#a") "list") 3)) "ID_REF")
|
||||||
|
(assert= (hs-token-value (nth (get (hs-tokens-of "{a{#a") "list") 3)) "#a")
|
||||||
|
(assert= (hs-token-type (nth (get (hs-tokens-of "[a[#a") "list") 3)) "ID_REF")
|
||||||
|
(assert= (hs-token-value (nth (get (hs-tokens-of "[a[#a") "list") 3)) "#a")
|
||||||
|
)
|
||||||
(deftest "handles identifiers properly"
|
(deftest "handles identifiers properly"
|
||||||
(error "SKIP (untranslated): handles identifiers properly"))
|
(assert= (hs-token-type (hs-stream-consume (hs-tokens-of "foo"))) "IDENTIFIER")
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "foo"))) "foo")
|
||||||
|
(assert= (hs-token-type (hs-stream-consume (hs-tokens-of " foo "))) "IDENTIFIER")
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of " foo "))) "foo")
|
||||||
|
(let ((s (hs-tokens-of " foo bar")))
|
||||||
|
(let ((tok1 (hs-stream-consume s)))
|
||||||
|
(assert= (hs-token-type tok1) "IDENTIFIER")
|
||||||
|
(assert= (hs-token-value tok1) "foo")
|
||||||
|
(let ((tok2 (hs-stream-consume s)))
|
||||||
|
(assert= (hs-token-type tok2) "IDENTIFIER")
|
||||||
|
(assert= (hs-token-value tok2) "bar"))))
|
||||||
|
(let ((s (hs-tokens-of " foo\n-- a comment\n bar")))
|
||||||
|
(let ((tok1 (hs-stream-consume s)))
|
||||||
|
(assert= (hs-token-type tok1) "IDENTIFIER")
|
||||||
|
(assert= (hs-token-value tok1) "foo")
|
||||||
|
(let ((tok2 (hs-stream-consume s)))
|
||||||
|
(assert= (hs-token-type tok2) "IDENTIFIER")
|
||||||
|
(assert= (hs-token-value tok2) "bar"))))
|
||||||
|
)
|
||||||
(deftest "handles identifiers with numbers properly"
|
(deftest "handles identifiers with numbers properly"
|
||||||
(error "SKIP (untranslated): handles identifiers with numbers properly"))
|
(assert= (hs-token-type (hs-stream-consume (hs-tokens-of "f1oo"))) "IDENTIFIER")
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "f1oo"))) "f1oo")
|
||||||
|
(assert= (hs-token-type (hs-stream-consume (hs-tokens-of "fo1o"))) "IDENTIFIER")
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "fo1o"))) "fo1o")
|
||||||
|
(assert= (hs-token-type (hs-stream-consume (hs-tokens-of "foo1"))) "IDENTIFIER")
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "foo1"))) "foo1")
|
||||||
|
)
|
||||||
(deftest "handles look ahead property"
|
(deftest "handles look ahead property"
|
||||||
(error "SKIP (untranslated): handles look ahead property"))
|
(assert= (hs-token-value (hs-stream-token (hs-tokens-of "a 1 + 1") 0)) "a")
|
||||||
|
(assert= (hs-token-value (hs-stream-token (hs-tokens-of "a 1 + 1") 1)) "1")
|
||||||
|
(assert= (hs-token-value (hs-stream-token (hs-tokens-of "a 1 + 1") 2)) "+")
|
||||||
|
(assert= (hs-token-value (hs-stream-token (hs-tokens-of "a 1 + 1") 3)) "1")
|
||||||
|
(assert= (hs-token-value (hs-stream-token (hs-tokens-of "a 1 + 1") 4)) "<<<EOF>>>")
|
||||||
|
)
|
||||||
(deftest "handles numbers properly"
|
(deftest "handles numbers properly"
|
||||||
(error "SKIP (untranslated): handles numbers properly"))
|
(assert= (hs-token-type (hs-stream-consume (hs-tokens-of "1"))) "NUMBER")
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "1"))) "1")
|
||||||
|
(assert= (hs-token-type (hs-stream-consume (hs-tokens-of "1.1"))) "NUMBER")
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "1.1"))) "1.1")
|
||||||
|
(assert= (hs-token-type (hs-stream-consume (hs-tokens-of "1234567890.1234567890"))) "NUMBER")
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "1234567890.1234567890"))) "1234567890.1234567890")
|
||||||
|
(assert= (hs-token-type (hs-stream-consume (hs-tokens-of "1e6"))) "NUMBER")
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "1e6"))) "1e6")
|
||||||
|
(assert= (hs-token-type (hs-stream-consume (hs-tokens-of "1e-6"))) "NUMBER")
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "1e-6"))) "1e-6")
|
||||||
|
(assert= (hs-token-type (hs-stream-consume (hs-tokens-of "1.1e6"))) "NUMBER")
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "1.1e6"))) "1.1e6")
|
||||||
|
(assert= (hs-token-type (hs-stream-consume (hs-tokens-of "1.1e-6"))) "NUMBER")
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "1.1e-6"))) "1.1e-6")
|
||||||
|
(assert= (hs-token-type (nth (get (hs-tokens-of "1.1.1") "list") 0)) "NUMBER")
|
||||||
|
(assert= (hs-token-type (nth (get (hs-tokens-of "1.1.1") "list") 1)) "PERIOD")
|
||||||
|
(assert= (hs-token-type (nth (get (hs-tokens-of "1.1.1") "list") 2)) "NUMBER")
|
||||||
|
(assert= (len (get (hs-tokens-of "1.1.1") "list")) 3)
|
||||||
|
)
|
||||||
(deftest "handles operators properly"
|
(deftest "handles operators properly"
|
||||||
(error "SKIP (untranslated): handles operators properly"))
|
(assert= (hs-token-op? (hs-stream-consume (hs-tokens-of "+"))) true)
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "+"))) "+")
|
||||||
|
(assert= (hs-token-op? (hs-stream-consume (hs-tokens-of "-"))) true)
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "-"))) "-")
|
||||||
|
(assert= (hs-token-op? (hs-stream-consume (hs-tokens-of "*"))) true)
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "*"))) "*")
|
||||||
|
(assert= (hs-token-op? (hs-stream-consume (hs-tokens-of "."))) true)
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "."))) ".")
|
||||||
|
(assert= (hs-token-op? (hs-stream-consume (hs-tokens-of "\\"))) true)
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "\\"))) "\\")
|
||||||
|
(assert= (hs-token-op? (hs-stream-consume (hs-tokens-of ":"))) true)
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of ":"))) ":")
|
||||||
|
(assert= (hs-token-op? (hs-stream-consume (hs-tokens-of "%"))) true)
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "%"))) "%")
|
||||||
|
(assert= (hs-token-op? (hs-stream-consume (hs-tokens-of "|"))) true)
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "|"))) "|")
|
||||||
|
(assert= (hs-token-op? (hs-stream-consume (hs-tokens-of "!"))) true)
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "!"))) "!")
|
||||||
|
(assert= (hs-token-op? (hs-stream-consume (hs-tokens-of "?"))) true)
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "?"))) "?")
|
||||||
|
(assert= (hs-token-op? (hs-stream-consume (hs-tokens-of "#"))) true)
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "#"))) "#")
|
||||||
|
(assert= (hs-token-op? (hs-stream-consume (hs-tokens-of "&"))) true)
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "&"))) "&")
|
||||||
|
(assert= (hs-token-op? (hs-stream-consume (hs-tokens-of ";"))) true)
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of ";"))) ";")
|
||||||
|
(assert= (hs-token-op? (hs-stream-consume (hs-tokens-of ","))) true)
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of ","))) ",")
|
||||||
|
(assert= (hs-token-op? (hs-stream-consume (hs-tokens-of "("))) true)
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "("))) "(")
|
||||||
|
(assert= (hs-token-op? (hs-stream-consume (hs-tokens-of ")"))) true)
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of ")"))) ")")
|
||||||
|
(assert= (hs-token-op? (hs-stream-consume (hs-tokens-of "<"))) true)
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "<"))) "<")
|
||||||
|
(assert= (hs-token-op? (hs-stream-consume (hs-tokens-of ">"))) true)
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of ">"))) ">")
|
||||||
|
(assert= (hs-token-op? (hs-stream-consume (hs-tokens-of "{"))) true)
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "{"))) "{")
|
||||||
|
(assert= (hs-token-op? (hs-stream-consume (hs-tokens-of "}"))) true)
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "}"))) "}")
|
||||||
|
(assert= (hs-token-op? (hs-stream-consume (hs-tokens-of "["))) true)
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "["))) "[")
|
||||||
|
(assert= (hs-token-op? (hs-stream-consume (hs-tokens-of "]"))) true)
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "]"))) "]")
|
||||||
|
(assert= (hs-token-op? (hs-stream-consume (hs-tokens-of "="))) true)
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "="))) "=")
|
||||||
|
(assert= (hs-token-op? (hs-stream-consume (hs-tokens-of "<="))) true)
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "<="))) "<=")
|
||||||
|
(assert= (hs-token-op? (hs-stream-consume (hs-tokens-of ">="))) true)
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of ">="))) ">=")
|
||||||
|
(assert= (hs-token-op? (hs-stream-consume (hs-tokens-of "=="))) true)
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "=="))) "==")
|
||||||
|
(assert= (hs-token-op? (hs-stream-consume (hs-tokens-of "==="))) true)
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "==="))) "===")
|
||||||
|
)
|
||||||
(deftest "handles strings properly"
|
(deftest "handles strings properly"
|
||||||
(error "SKIP (untranslated): handles strings properly"))
|
(assert= (hs-token-type (hs-stream-consume (hs-tokens-of "\"foo\""))) "STRING")
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "\"foo\""))) "foo")
|
||||||
|
(assert= (hs-token-type (hs-stream-consume (hs-tokens-of "\"fo'o\""))) "STRING")
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "\"fo'o\""))) "fo'o")
|
||||||
|
(assert= (hs-token-type (hs-stream-consume (hs-tokens-of "\"fo\\\"o\""))) "STRING")
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "\"fo\\\"o\""))) "fo\"o")
|
||||||
|
(assert= (hs-token-type (hs-stream-consume (hs-tokens-of "'foo'"))) "STRING")
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "'foo'"))) "foo")
|
||||||
|
(assert= (hs-token-type (hs-stream-consume (hs-tokens-of "'fo\"o'"))) "STRING")
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "'fo\"o'"))) "fo\"o")
|
||||||
|
(assert= (hs-token-type (hs-stream-consume (hs-tokens-of "'fo\\'o'"))) "STRING")
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "'fo\\'o'"))) "fo'o")
|
||||||
|
(let ((threw false))
|
||||||
|
(guard (e (true (set! threw true))) (hs-stream-consume (hs-tokens-of "'")))
|
||||||
|
(assert threw))
|
||||||
|
(let ((threw false))
|
||||||
|
(guard (e (true (set! threw true))) (hs-stream-consume (hs-tokens-of "\"")))
|
||||||
|
(assert threw))
|
||||||
|
)
|
||||||
(deftest "handles strings properly 2"
|
(deftest "handles strings properly 2"
|
||||||
(error "SKIP (untranslated): handles strings properly 2"))
|
(assert= (hs-token-type (hs-stream-consume (hs-tokens-of "'foo'"))) "STRING")
|
||||||
|
(assert= (hs-token-value (hs-stream-consume (hs-tokens-of "'foo'"))) "foo")
|
||||||
|
)
|
||||||
(deftest "handles template bootstrap properly"
|
(deftest "handles template bootstrap properly"
|
||||||
(error "SKIP (untranslated): handles template bootstrap properly"))
|
(assert= (hs-token-value (hs-stream-token (hs-tokens-of "\"" :template) 0)) "\"")
|
||||||
|
(assert= (hs-token-value (hs-stream-token (hs-tokens-of "\"$" :template) 0)) "\"")
|
||||||
|
(assert= (hs-token-value (hs-stream-token (hs-tokens-of "\"$" :template) 1)) "$")
|
||||||
|
(assert= (hs-token-value (hs-stream-token (hs-tokens-of "\"${" :template) 0)) "\"")
|
||||||
|
(assert= (hs-token-value (hs-stream-token (hs-tokens-of "\"${" :template) 1)) "$")
|
||||||
|
(assert= (hs-token-value (hs-stream-token (hs-tokens-of "\"${" :template) 2)) "{")
|
||||||
|
(assert= (hs-token-value (hs-stream-token (hs-tokens-of "\"${\"asdf\"" :template) 0)) "\"")
|
||||||
|
(assert= (hs-token-value (hs-stream-token (hs-tokens-of "\"${\"asdf\"" :template) 1)) "$")
|
||||||
|
(assert= (hs-token-value (hs-stream-token (hs-tokens-of "\"${\"asdf\"" :template) 2)) "{")
|
||||||
|
(assert= (hs-token-value (hs-stream-token (hs-tokens-of "\"${\"asdf\"" :template) 3)) "asdf")
|
||||||
|
(assert= (hs-token-value (hs-stream-token (hs-tokens-of "\"${\"asdf\"}\"" :template) 0)) "\"")
|
||||||
|
(assert= (hs-token-value (hs-stream-token (hs-tokens-of "\"${\"asdf\"}\"" :template) 1)) "$")
|
||||||
|
(assert= (hs-token-value (hs-stream-token (hs-tokens-of "\"${\"asdf\"}\"" :template) 2)) "{")
|
||||||
|
(assert= (hs-token-value (hs-stream-token (hs-tokens-of "\"${\"asdf\"}\"" :template) 3)) "asdf")
|
||||||
|
(assert= (hs-token-value (hs-stream-token (hs-tokens-of "\"${\"asdf\"}\"" :template) 4)) "}")
|
||||||
|
(assert= (hs-token-value (hs-stream-token (hs-tokens-of "\"${\"asdf\"}\"" :template) 5)) "\"")
|
||||||
|
)
|
||||||
(deftest "handles whitespace properly"
|
(deftest "handles whitespace properly"
|
||||||
(error "SKIP (untranslated): handles whitespace properly"))
|
(assert= (len (get (hs-tokens-of " ") "list")) 0)
|
||||||
|
(assert= (len (get (hs-tokens-of " asdf") "list")) 1)
|
||||||
|
(assert= (len (get (hs-tokens-of " asdf ") "list")) 2)
|
||||||
|
(assert= (len (get (hs-tokens-of "asdf ") "list")) 2)
|
||||||
|
(assert= (len (get (hs-tokens-of "\n") "list")) 0)
|
||||||
|
(assert= (len (get (hs-tokens-of "\nasdf") "list")) 1)
|
||||||
|
(assert= (len (get (hs-tokens-of "\nasdf\n") "list")) 2)
|
||||||
|
(assert= (len (get (hs-tokens-of "asdf\n") "list")) 2)
|
||||||
|
(assert= (len (get (hs-tokens-of "\r") "list")) 0)
|
||||||
|
(assert= (len (get (hs-tokens-of "\rasdf") "list")) 1)
|
||||||
|
(assert= (len (get (hs-tokens-of "\rasdf\r") "list")) 2)
|
||||||
|
(assert= (len (get (hs-tokens-of "asdf\r") "list")) 2)
|
||||||
|
(assert= (len (get (hs-tokens-of "\t") "list")) 0)
|
||||||
|
(assert= (len (get (hs-tokens-of "\tasdf") "list")) 1)
|
||||||
|
(assert= (len (get (hs-tokens-of "\tasdf\t") "list")) 2)
|
||||||
|
(assert= (len (get (hs-tokens-of "asdf\t") "list")) 2)
|
||||||
|
)
|
||||||
(deftest "string interpolation isnt surprising"
|
(deftest "string interpolation isnt surprising"
|
||||||
(hs-cleanup!)
|
(hs-cleanup!)
|
||||||
(let ((_el-div (dom-create-element "div")))
|
(let ((_el-div (dom-create-element "div")))
|
||||||
(dom-set-attr _el-div "_" "on click set x to 42 then put `test${x} test ${x} test$x test $x test $x test ${x} test$x test_$x test_${x} test-$x test.$x` into my.innerHTML")
|
(dom-set-attr _el-div "_" "on click set x to 42 then put `test\\${x} test ${x} test\\$x test $x test \\$x test \\${x} test$x test_$x test_${x} test-$x test.$x` into my.innerHTML")
|
||||||
(dom-append (dom-body) _el-div)
|
(dom-append (dom-body) _el-div)
|
||||||
(hs-activate! _el-div)
|
(hs-activate! _el-div)
|
||||||
(dom-dispatch _el-div "click" nil)
|
(dom-dispatch _el-div "click" nil)
|
||||||
@@ -3361,7 +3622,7 @@
|
|||||||
(assert= (eval-hs "[1 + 1, 2 * 3, 10 - 5]") (list 2 6 5))
|
(assert= (eval-hs "[1 + 1, 2 * 3, 10 - 5]") (list 2 6 5))
|
||||||
)
|
)
|
||||||
(deftest "arrays containing objects work"
|
(deftest "arrays containing objects work"
|
||||||
(assert= (eval-hs "[{a: 1}, {b: 2}]") (list {:a 1} {:b 2}))
|
(assert-equal (list {:a 1} {:b 2}) (eval-hs "[{a: 1}, {b: 2}]"))
|
||||||
)
|
)
|
||||||
(deftest "deeply nested array literals work"
|
(deftest "deeply nested array literals work"
|
||||||
(assert= (eval-hs "[[[1]], [[2, 3]]]") (list (list (list 1)) (list (list 2 3))))
|
(assert= (eval-hs "[[[1]], [[2, 3]]]") (list (list (list 1)) (list (list 2 3))))
|
||||||
@@ -3464,11 +3725,11 @@
|
|||||||
(dom-set-attr _el-input6 "value" "555-1212")
|
(dom-set-attr _el-input6 "value" "555-1212")
|
||||||
(dom-append (dom-body) _el-qsdiv)
|
(dom-append (dom-body) _el-qsdiv)
|
||||||
(dom-append _el-qsdiv _el-input)
|
(dom-append _el-qsdiv _el-input)
|
||||||
(dom-append _el-input _el-br)
|
(dom-append _el-qsdiv _el-br)
|
||||||
(dom-append _el-br _el-input3)
|
(dom-append _el-qsdiv _el-input3)
|
||||||
(dom-append _el-input3 _el-br4)
|
(dom-append _el-qsdiv _el-br4)
|
||||||
(dom-append _el-br4 _el-input5)
|
(dom-append _el-qsdiv _el-input5)
|
||||||
(dom-append _el-input5 _el-input6)
|
(dom-append _el-qsdiv _el-input6)
|
||||||
(hs-activate! _el-qsdiv)
|
(hs-activate! _el-qsdiv)
|
||||||
))
|
))
|
||||||
(deftest "converts an array into HTML"
|
(deftest "converts an array into HTML"
|
||||||
@@ -4096,9 +4357,9 @@
|
|||||||
(dom-append _el-table _el-tr)
|
(dom-append _el-table _el-tr)
|
||||||
(dom-append _el-tr _el-td)
|
(dom-append _el-tr _el-td)
|
||||||
(dom-append _el-td _el-input)
|
(dom-append _el-td _el-input)
|
||||||
(dom-append _el-input _el-input4)
|
(dom-append _el-td _el-input4)
|
||||||
(dom-append _el-input4 _el-master)
|
(dom-append _el-td _el-master)
|
||||||
(dom-append _el-master _el-out)
|
(dom-append (dom-body) _el-out)
|
||||||
(hs-activate! _el-master)
|
(hs-activate! _el-master)
|
||||||
(dom-dispatch (dom-query-by-id "master") "click" nil)
|
(dom-dispatch (dom-query-by-id "master") "click" nil)
|
||||||
(assert= (dom-text-content (dom-query-by-id "out")) "2")
|
(assert= (dom-text-content (dom-query-by-id "out")) "2")
|
||||||
@@ -4179,13 +4440,13 @@
|
|||||||
(dom-append _el-table _el-tr)
|
(dom-append _el-table _el-tr)
|
||||||
(dom-append _el-tr _el-td)
|
(dom-append _el-tr _el-td)
|
||||||
(dom-append _el-td _el-input)
|
(dom-append _el-td _el-input)
|
||||||
(dom-append _el-input _el-tr4)
|
(dom-append _el-table _el-tr4)
|
||||||
(dom-append _el-tr4 _el-td5)
|
(dom-append _el-tr4 _el-td5)
|
||||||
(dom-append _el-td5 _el-input6)
|
(dom-append _el-td5 _el-input6)
|
||||||
(dom-append _el-input6 _el-tr7)
|
(dom-append _el-table _el-tr7)
|
||||||
(dom-append _el-tr7 _el-td8)
|
(dom-append _el-tr7 _el-td8)
|
||||||
(dom-append _el-td8 _el-input9)
|
(dom-append _el-td8 _el-input9)
|
||||||
(dom-append _el-input9 _el-tr10)
|
(dom-append _el-table _el-tr10)
|
||||||
(dom-append _el-tr10 _el-td11)
|
(dom-append _el-tr10 _el-td11)
|
||||||
(dom-append _el-td11 _el-master)
|
(dom-append _el-td11 _el-master)
|
||||||
(hs-activate! _el-master)
|
(hs-activate! _el-master)
|
||||||
@@ -4367,13 +4628,13 @@
|
|||||||
(dom-append _el-table _el-tr)
|
(dom-append _el-table _el-tr)
|
||||||
(dom-append _el-tr _el-td)
|
(dom-append _el-tr _el-td)
|
||||||
(dom-append _el-td _el-input)
|
(dom-append _el-td _el-input)
|
||||||
(dom-append _el-input _el-tr4)
|
(dom-append _el-table _el-tr4)
|
||||||
(dom-append _el-tr4 _el-td5)
|
(dom-append _el-tr4 _el-td5)
|
||||||
(dom-append _el-td5 _el-input6)
|
(dom-append _el-td5 _el-input6)
|
||||||
(dom-append _el-input6 _el-tr7)
|
(dom-append _el-table _el-tr7)
|
||||||
(dom-append _el-tr7 _el-td8)
|
(dom-append _el-tr7 _el-td8)
|
||||||
(dom-append _el-td8 _el-input9)
|
(dom-append _el-td8 _el-input9)
|
||||||
(dom-append _el-input9 _el-tr10)
|
(dom-append _el-table _el-tr10)
|
||||||
(dom-append _el-tr10 _el-td11)
|
(dom-append _el-tr10 _el-td11)
|
||||||
(dom-append _el-td11 _el-master)
|
(dom-append _el-td11 _el-master)
|
||||||
(hs-activate! _el-master)
|
(hs-activate! _el-master)
|
||||||
@@ -4392,9 +4653,9 @@
|
|||||||
(dom-set-inner-html _el-script "<input type=\"checkbox\" _=\"set :checkboxes to <input[type=checkbox]/> in #box where it is not me on change set checked of the :checkboxes to my checked\">")
|
(dom-set-inner-html _el-script "<input type=\"checkbox\" _=\"set :checkboxes to <input[type=checkbox]/> in #box where it is not me on change set checked of the :checkboxes to my checked\">")
|
||||||
(dom-append (dom-body) _el-box)
|
(dom-append (dom-body) _el-box)
|
||||||
(dom-append _el-box _el-input)
|
(dom-append _el-box _el-input)
|
||||||
(dom-append _el-input _el-input2)
|
(dom-append _el-box _el-input2)
|
||||||
(dom-append _el-input2 _el-script)
|
(dom-append (dom-body) _el-script)
|
||||||
(dom-append _el-input2 _el-test-where-me)
|
(dom-append (dom-body) _el-test-where-me)
|
||||||
(dom-dispatch (dom-query "test-where-me input") "click" nil)
|
(dom-dispatch (dom-query "test-where-me input") "click" nil)
|
||||||
))
|
))
|
||||||
(deftest "works with DOM elements"
|
(deftest "works with DOM elements"
|
||||||
@@ -5300,7 +5561,7 @@
|
|||||||
(deftest "can invoke global function w/ async arg"
|
(deftest "can invoke global function w/ async arg"
|
||||||
(error "SKIP (untranslated): can invoke global function w/ async arg"))
|
(error "SKIP (untranslated): can invoke global function w/ async arg"))
|
||||||
(deftest "can pass an array literal as an argument"
|
(deftest "can pass an array literal as an argument"
|
||||||
(assert= (eval-hs-locals "sum([1, 2, 3, 4])" (list (list (quote sum) (fn (arr) (host-call arr "reduce" (fn (a b) (+ a b)) 0))))) 10)
|
(assert= (eval-hs-locals "sum([1, 2, 3, 4])" (list (list (quote sum) (fn (arr) (reduce (fn (a b) (+ a b)) 0 arr))))) 10)
|
||||||
)
|
)
|
||||||
(deftest "can pass an expression as an argument"
|
(deftest "can pass an expression as an argument"
|
||||||
(assert= (eval-hs-locals "double(3 + 4)" (list (list (quote double) (fn (n) (* n 2))))) 14)
|
(assert= (eval-hs-locals "double(3 + 4)" (list (list (quote double) (fn (n) (* n 2))))) 14)
|
||||||
@@ -7168,7 +7429,14 @@
|
|||||||
;; ── fetch (23 tests) ──
|
;; ── fetch (23 tests) ──
|
||||||
(defsuite "hs-upstream-fetch"
|
(defsuite "hs-upstream-fetch"
|
||||||
(deftest "Response can be converted to JSON via as JSON"
|
(deftest "Response can be converted to JSON via as JSON"
|
||||||
(error "SKIP (skip-list): Response can be converted to JSON via as JSON"))
|
(hs-cleanup!)
|
||||||
|
(let ((_el-div (dom-create-element "div")))
|
||||||
|
(dom-set-attr _el-div "_" "on click fetch /test as Response then put (it as JSON).name into me")
|
||||||
|
(dom-append (dom-body) _el-div)
|
||||||
|
(hs-activate! _el-div)
|
||||||
|
(dom-dispatch _el-div "click" nil)
|
||||||
|
(assert= (dom-text-content _el-div) "Joe")
|
||||||
|
))
|
||||||
(deftest "allows the event handler to change the fetch parameters"
|
(deftest "allows the event handler to change the fetch parameters"
|
||||||
(hs-cleanup!)
|
(hs-cleanup!)
|
||||||
(let ((_el-div (dom-create-element "div")))
|
(let ((_el-div (dom-create-element "div")))
|
||||||
@@ -7179,9 +7447,23 @@
|
|||||||
(assert= (dom-text-content _el-div) "yay")
|
(assert= (dom-text-content _el-div) "yay")
|
||||||
))
|
))
|
||||||
(deftest "as response does not throw on 404"
|
(deftest "as response does not throw on 404"
|
||||||
(error "SKIP (skip-list): as response does not throw on 404"))
|
(hs-cleanup!)
|
||||||
|
(let ((_el-div (dom-create-element "div")))
|
||||||
|
(dom-set-attr _el-div "_" "on click fetch /test as response then put it.status into me")
|
||||||
|
(dom-append (dom-body) _el-div)
|
||||||
|
(hs-activate! _el-div)
|
||||||
|
(dom-dispatch _el-div "click" nil)
|
||||||
|
(assert= (dom-text-content _el-div) "404")
|
||||||
|
))
|
||||||
(deftest "can catch an error that occurs when using fetch"
|
(deftest "can catch an error that occurs when using fetch"
|
||||||
(error "SKIP (skip-list): can catch an error that occurs when using fetch"))
|
(hs-cleanup!)
|
||||||
|
(let ((_el-div (dom-create-element "div")))
|
||||||
|
(dom-set-attr _el-div "_" "on click fetch /test catch e log e put \"yay\" into me")
|
||||||
|
(dom-append (dom-body) _el-div)
|
||||||
|
(hs-activate! _el-div)
|
||||||
|
(dom-dispatch _el-div "click" nil)
|
||||||
|
(assert= (dom-text-content _el-div) "yay")
|
||||||
|
))
|
||||||
(deftest "can do a simple fetch"
|
(deftest "can do a simple fetch"
|
||||||
(hs-cleanup!)
|
(hs-cleanup!)
|
||||||
(let ((_el-div (dom-create-element "div")))
|
(let ((_el-div (dom-create-element "div")))
|
||||||
@@ -7302,9 +7584,23 @@
|
|||||||
(assert= (dom-text-content _el-div) "yay")
|
(assert= (dom-text-content _el-div) "yay")
|
||||||
))
|
))
|
||||||
(deftest "do not throw passes through 404 response"
|
(deftest "do not throw passes through 404 response"
|
||||||
(error "SKIP (skip-list): do not throw passes through 404 response"))
|
(hs-cleanup!)
|
||||||
|
(let ((_el-div (dom-create-element "div")))
|
||||||
|
(dom-set-attr _el-div "_" "on click fetch /test do not throw then put it into me")
|
||||||
|
(dom-append (dom-body) _el-div)
|
||||||
|
(hs-activate! _el-div)
|
||||||
|
(dom-dispatch _el-div "click" nil)
|
||||||
|
(assert= (dom-text-content _el-div) "the body")
|
||||||
|
))
|
||||||
(deftest "don't throw passes through 404 response"
|
(deftest "don't throw passes through 404 response"
|
||||||
(error "SKIP (skip-list): don't throw passes through 404 response"))
|
(hs-cleanup!)
|
||||||
|
(let ((_el-div (dom-create-element "div")))
|
||||||
|
(dom-set-attr _el-div "_" "on click fetch /test don't throw then put it into me")
|
||||||
|
(dom-append (dom-body) _el-div)
|
||||||
|
(hs-activate! _el-div)
|
||||||
|
(dom-dispatch _el-div "click" nil)
|
||||||
|
(assert= (dom-text-content _el-div) "the body")
|
||||||
|
))
|
||||||
(deftest "submits the fetch parameters to the event handler"
|
(deftest "submits the fetch parameters to the event handler"
|
||||||
(hs-cleanup!)
|
(hs-cleanup!)
|
||||||
(host-set! (host-global "window") "headerCheckPassed" false)
|
(host-set! (host-global "window") "headerCheckPassed" false)
|
||||||
@@ -7316,9 +7612,26 @@
|
|||||||
(assert= (dom-text-content _el-div) "yay")
|
(assert= (dom-text-content _el-div) "yay")
|
||||||
))
|
))
|
||||||
(deftest "throws on non-2xx response by default"
|
(deftest "throws on non-2xx response by default"
|
||||||
(error "SKIP (skip-list): throws on non-2xx response by default"))
|
(hs-cleanup!)
|
||||||
|
(let ((_el-div (dom-create-element "div")))
|
||||||
|
(dom-set-attr _el-div "_" "on click fetch /test catch e put \"caught\" into me")
|
||||||
|
(dom-append (dom-body) _el-div)
|
||||||
|
(hs-activate! _el-div)
|
||||||
|
(dom-dispatch _el-div "click" nil)
|
||||||
|
(assert= (dom-text-content _el-div) "caught")
|
||||||
|
))
|
||||||
(deftest "triggers an event just before fetching"
|
(deftest "triggers an event just before fetching"
|
||||||
(error "SKIP (skip-list): triggers an event just before fetching"))
|
(hs-cleanup!)
|
||||||
|
(host-call (host-global "window") "addEventListener" "hyperscript:beforeFetch" (fn (_event) (dom-set-attr (host-get _event "target") "class" "foo-set")))
|
||||||
|
(let ((_el-div (dom-create-element "div")))
|
||||||
|
(dom-set-attr _el-div "_" "on click fetch \"/test\" then put it into my.innerHTML end")
|
||||||
|
(dom-append (dom-body) _el-div)
|
||||||
|
(hs-activate! _el-div)
|
||||||
|
(assert (not (dom-has-class? _el-div "foo-set")))
|
||||||
|
(dom-dispatch _el-div "click" nil)
|
||||||
|
(assert (dom-has-class? _el-div "foo-set"))
|
||||||
|
(assert= (dom-text-content _el-div) "yay")
|
||||||
|
))
|
||||||
)
|
)
|
||||||
|
|
||||||
;; ── focus (3 tests) ──
|
;; ── focus (3 tests) ──
|
||||||
|
|||||||
@@ -125,19 +125,9 @@ SKIP_TEST_NAMES = {
|
|||||||
"can ignore when target doesn't exist",
|
"can ignore when target doesn't exist",
|
||||||
"can ignore when target doesn\\'t exist",
|
"can ignore when target doesn\\'t exist",
|
||||||
"can handle an or after a from clause",
|
"can handle an or after a from clause",
|
||||||
# upstream 'fetch' category — depend on per-test sinon stubs for 404 / thrown errors,
|
# upstream 'fetch' category — real DocumentFragment semantics (`its childElementCount`
|
||||||
# or on real DocumentFragment semantics (`its childElementCount` after `as html`).
|
# after `as html`) not exercisable with our DOM mock.
|
||||||
# Our generic test-runner mock returns a fixed 200 response, so these cases
|
|
||||||
# (non-2xx handling, error path, before-fetch event, real DOM fragment) can't be
|
|
||||||
# exercised here.
|
|
||||||
"can do a simple fetch w/ html",
|
"can do a simple fetch w/ html",
|
||||||
"triggers an event just before fetching",
|
|
||||||
"can catch an error that occurs when using fetch",
|
|
||||||
"throws on non-2xx response by default",
|
|
||||||
"do not throw passes through 404 response",
|
|
||||||
"don't throw passes through 404 response",
|
|
||||||
"as response does not throw on 404",
|
|
||||||
"Response can be converted to JSON via as JSON",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -210,11 +200,18 @@ def parse_html(html):
|
|||||||
# button HTML in `properly processes hyperscript X` tests). HTMLParser handles
|
# button HTML in `properly processes hyperscript X` tests). HTMLParser handles
|
||||||
# backslashes in attribute values as literal characters, so we leave them.
|
# backslashes in attribute values as literal characters, so we leave them.
|
||||||
|
|
||||||
|
# HTML5 void elements — never have children, auto-pop from stack immediately.
|
||||||
|
VOID_TAGS = {'area','base','br','col','embed','hr','img','input','link',
|
||||||
|
'meta','param','source','track','wbr'}
|
||||||
|
|
||||||
elements = []
|
elements = []
|
||||||
stack = []
|
stack = []
|
||||||
|
|
||||||
class Parser(HTMLParser):
|
class Parser(HTMLParser):
|
||||||
def handle_starttag(self, tag, attrs):
|
def handle_starttag(self, tag, attrs):
|
||||||
|
# Pop any void elements left on the stack (they have no close tag).
|
||||||
|
while stack and stack[-1]['tag'] in VOID_TAGS:
|
||||||
|
stack.pop()
|
||||||
el = {
|
el = {
|
||||||
'tag': tag, 'id': None, 'classes': [], 'hs': None,
|
'tag': tag, 'id': None, 'classes': [], 'hs': None,
|
||||||
'attrs': {}, 'inner': '', 'depth': len(stack),
|
'attrs': {}, 'inner': '', 'depth': len(stack),
|
||||||
@@ -244,6 +241,9 @@ def parse_html(html):
|
|||||||
elements.append(el)
|
elements.append(el)
|
||||||
|
|
||||||
def handle_endtag(self, tag):
|
def handle_endtag(self, tag):
|
||||||
|
# Pop void elements first (they don't have close tags but may linger).
|
||||||
|
while stack and stack[-1]['tag'] in VOID_TAGS:
|
||||||
|
stack.pop()
|
||||||
if stack and stack[-1]['tag'] == tag:
|
if stack and stack[-1]['tag'] == tag:
|
||||||
stack.pop()
|
stack.pop()
|
||||||
|
|
||||||
@@ -963,6 +963,24 @@ def parse_dev_body(body, elements, var_names):
|
|||||||
else:
|
else:
|
||||||
pre_setups.append(('__hs_config__', op_expr))
|
pre_setups.append(('__hs_config__', op_expr))
|
||||||
continue
|
continue
|
||||||
|
# window.addEventListener(EVT, (param) => { param.target.PROP = 'VAL'; })
|
||||||
|
wa = re.search(
|
||||||
|
r"window\.addEventListener\(\s*(['\"])([^'\"]+)\1\s*,\s*"
|
||||||
|
r"\((\w+)\)\s*=>\s*\{\s*\3\.target\.(\w+)\s*=\s*['\"]([^'\"]+)['\"]\s*;?\s*\}",
|
||||||
|
m.group(1),
|
||||||
|
)
|
||||||
|
if wa:
|
||||||
|
ev_name = wa.group(2)
|
||||||
|
prop = wa.group(4)
|
||||||
|
val = wa.group(5)
|
||||||
|
attr = 'class' if prop == 'className' else prop
|
||||||
|
sx = (f'(host-call (host-global "window") "addEventListener" "{ev_name}" '
|
||||||
|
f'(fn (_event) (dom-set-attr (host-get _event "target") "{attr}" "{val}")))')
|
||||||
|
if seen_html:
|
||||||
|
ops.append(sx)
|
||||||
|
else:
|
||||||
|
pre_setups.append(('__hs_config__', sx))
|
||||||
|
continue
|
||||||
# fall through
|
# fall through
|
||||||
|
|
||||||
# evaluate(() => _hyperscript.config.X = ...) single-line variant.
|
# evaluate(() => _hyperscript.config.X = ...) single-line variant.
|
||||||
@@ -1254,7 +1272,9 @@ def process_hs_val(hs_val):
|
|||||||
hs_val = hs_val.replace('\\n', '\n').replace('\\t', ' ')
|
hs_val = hs_val.replace('\\n', '\n').replace('\\t', ' ')
|
||||||
# Preserve escaped quotes (\" → placeholder), strip remaining backslashes, restore
|
# Preserve escaped quotes (\" → placeholder), strip remaining backslashes, restore
|
||||||
hs_val = hs_val.replace('\\"', '\x00QUOT\x00')
|
hs_val = hs_val.replace('\\"', '\x00QUOT\x00')
|
||||||
|
hs_val = hs_val.replace('\\$', '\x00DOLLAR\x00') # preserve \$ template escape
|
||||||
hs_val = hs_val.replace('\\', '')
|
hs_val = hs_val.replace('\\', '')
|
||||||
|
hs_val = hs_val.replace('\x00DOLLAR\x00', '\\$') # restore \$
|
||||||
hs_val = hs_val.replace('\x00QUOT\x00', '\\"')
|
hs_val = hs_val.replace('\x00QUOT\x00', '\\"')
|
||||||
# Strip line comments BEFORE newline collapse — once newlines become `then`,
|
# Strip line comments BEFORE newline collapse — once newlines become `then`,
|
||||||
# an unterminated `//` / ` --` comment would consume the rest of the input.
|
# an unterminated `//` / ` --` comment would consume the rest of the input.
|
||||||
@@ -1666,6 +1686,13 @@ def js_expr_to_sx(expr):
|
|||||||
if s is None:
|
if s is None:
|
||||||
return None
|
return None
|
||||||
arg_sx.append(s)
|
arg_sx.append(s)
|
||||||
|
# Translate common array HO methods to SX primitives so SX lists work.
|
||||||
|
if method == 'reduce' and len(arg_sx) == 2:
|
||||||
|
return f'(reduce {arg_sx[0]} {arg_sx[1]} {obj})'
|
||||||
|
if method == 'map' and len(arg_sx) == 1:
|
||||||
|
return f'(map {arg_sx[0]} {obj})'
|
||||||
|
if method == 'filter' and len(arg_sx) == 1:
|
||||||
|
return f'(filter {arg_sx[0]} {obj})'
|
||||||
return f'(host-call {obj} "{method}" {" ".join(arg_sx)})'.strip()
|
return f'(host-call {obj} "{method}" {" ".join(arg_sx)})'.strip()
|
||||||
|
|
||||||
# Property access: o.prop
|
# Property access: o.prop
|
||||||
@@ -1838,6 +1865,272 @@ def extract_hs_expr(raw):
|
|||||||
return expr
|
return expr
|
||||||
|
|
||||||
|
|
||||||
|
def generate_tokenizer_test(test, safe_name):
|
||||||
|
"""Hardcoded SX translation for _hyperscript.internals.tokenizer tests (E37)."""
|
||||||
|
name = test['name']
|
||||||
|
|
||||||
|
def to_(src, tmpl=False):
|
||||||
|
"""Return (hs-tokens-of <sx-str> [:template]) for HS source string src."""
|
||||||
|
escaped = (src
|
||||||
|
.replace('\\', '\\\\')
|
||||||
|
.replace('"', '\\"')
|
||||||
|
.replace('\n', '\\n')
|
||||||
|
.replace('\r', '\\r')
|
||||||
|
.replace('\t', '\\t'))
|
||||||
|
q = '"' + escaped + '"'
|
||||||
|
suffix = ' :template' if tmpl else ''
|
||||||
|
return f'(hs-tokens-of {q}{suffix})'
|
||||||
|
|
||||||
|
def consume(s):
|
||||||
|
return f'(hs-stream-consume {s})'
|
||||||
|
|
||||||
|
def tok_i(s, i):
|
||||||
|
return f'(hs-stream-token {s} {i})'
|
||||||
|
|
||||||
|
def has_more(s):
|
||||||
|
return f'(hs-stream-has-more {s})'
|
||||||
|
|
||||||
|
def t_type(t):
|
||||||
|
return f'(hs-token-type {t})'
|
||||||
|
|
||||||
|
def t_val(t):
|
||||||
|
return f'(hs-token-value {t})'
|
||||||
|
|
||||||
|
def t_op(t):
|
||||||
|
return f'(hs-token-op? {t})'
|
||||||
|
|
||||||
|
def nth_list(s, i):
|
||||||
|
return f'(nth (get {s} "list") {i})'
|
||||||
|
|
||||||
|
def list_len(s):
|
||||||
|
return f'(len (get {s} "list"))'
|
||||||
|
|
||||||
|
def ae(actual, expected):
|
||||||
|
return f' (assert= {actual} {expected})'
|
||||||
|
|
||||||
|
def throws(expr):
|
||||||
|
return (
|
||||||
|
f' (let ((threw false))\n'
|
||||||
|
f' (guard (e (true (set! threw true))) {expr})\n'
|
||||||
|
f' (assert threw))'
|
||||||
|
)
|
||||||
|
|
||||||
|
lines = [f' (deftest "{safe_name}"']
|
||||||
|
|
||||||
|
if name == 'handles $ in template properly':
|
||||||
|
s = to_('"', tmpl=True)
|
||||||
|
lines.append(ae(t_val(tok_i(s, 0)), sx_str('"')))
|
||||||
|
|
||||||
|
elif name == 'handles all special escapes properly':
|
||||||
|
for src, exp in [
|
||||||
|
('"\\b"', '(char-from-code 8)'),
|
||||||
|
('"\\f"', '(char-from-code 12)'),
|
||||||
|
('"\\n"', '"\\n"'),
|
||||||
|
('"\\r"', '"\\r"'),
|
||||||
|
('"\\t"', '"\\t"'),
|
||||||
|
('"\\v"', '(char-from-code 11)'),
|
||||||
|
]:
|
||||||
|
lines.append(ae(t_val(consume(to_(src))), exp))
|
||||||
|
|
||||||
|
elif name == 'handles basic token types':
|
||||||
|
lines.append(ae(t_type(consume(to_('foo'))), '"IDENTIFIER"'))
|
||||||
|
lines.append(ae(t_type(consume(to_('1'))), '"NUMBER"'))
|
||||||
|
for src in ['1.1', '1e6', '1e-6', '1.1e6', '1.1e-6']:
|
||||||
|
sq = to_(src)
|
||||||
|
lines.append(f' (let ((s {sq}))')
|
||||||
|
lines.append(f' (let ((tok (hs-stream-consume s)))')
|
||||||
|
lines.append(f' (assert= (hs-token-type tok) "NUMBER")')
|
||||||
|
lines.append(f' (assert= (hs-stream-has-more s) false)))')
|
||||||
|
lines.append(ae(t_type(consume(to_('.a'))), '"CLASS_REF"'))
|
||||||
|
lines.append(ae(t_type(consume(to_('#a'))), '"ID_REF"'))
|
||||||
|
lines.append(ae(t_type(consume(to_('"asdf"'))), '"STRING"'))
|
||||||
|
|
||||||
|
elif name == 'handles class identifiers properly':
|
||||||
|
for src, idx, exp_type, exp_val in [
|
||||||
|
('.a', None, 'CLASS_REF', '.a'),
|
||||||
|
(' .a', None, 'CLASS_REF', '.a'),
|
||||||
|
('a.a', None, 'IDENTIFIER', 'a'),
|
||||||
|
('(a).a', 4, 'IDENTIFIER', 'a'),
|
||||||
|
('{a}.a', 4, 'IDENTIFIER', 'a'),
|
||||||
|
('[a].a', 4, 'IDENTIFIER', 'a'),
|
||||||
|
('(a(.a', 3, 'CLASS_REF', '.a'),
|
||||||
|
('{a{.a', 3, 'CLASS_REF', '.a'),
|
||||||
|
('[a[.a', 3, 'CLASS_REF', '.a'),
|
||||||
|
]:
|
||||||
|
if idx is None:
|
||||||
|
tok_expr = consume(to_(src))
|
||||||
|
else:
|
||||||
|
tok_expr = nth_list(to_(src), idx)
|
||||||
|
lines.append(ae(t_type(tok_expr), f'"{exp_type}"'))
|
||||||
|
lines.append(ae(t_val(tok_expr), sx_str(exp_val)))
|
||||||
|
|
||||||
|
elif name == 'handles comments properly':
|
||||||
|
for src, expected in [
|
||||||
|
('--', 0),
|
||||||
|
('asdf--', 1),
|
||||||
|
('-- asdf', 0),
|
||||||
|
('--\nasdf', 1),
|
||||||
|
('--\nasdf--', 1),
|
||||||
|
('---asdf', 0),
|
||||||
|
('----\n---asdf', 0),
|
||||||
|
('----asdf----', 0),
|
||||||
|
('---\nasdf---', 1),
|
||||||
|
('// asdf', 0),
|
||||||
|
('///asdf', 0),
|
||||||
|
('asdf//', 1),
|
||||||
|
('asdf\n//', 2),
|
||||||
|
]:
|
||||||
|
lines.append(ae(list_len(to_(src)), str(expected)))
|
||||||
|
|
||||||
|
elif name == 'handles hex escapes properly':
|
||||||
|
lines.append(ae(t_val(consume(to_('"\\x1f"'))), '(char-from-code 31)'))
|
||||||
|
lines.append(ae(t_val(consume(to_('"\\x41"'))), '"A"'))
|
||||||
|
lines.append(ae(t_val(consume(to_('"\\x41\\x61"'))), '"Aa"'))
|
||||||
|
for bad in ['"\\x"', '"\\xGG"', '"\\x4"']:
|
||||||
|
lines.append(throws(consume(to_(bad))))
|
||||||
|
|
||||||
|
elif name == 'handles id references properly':
|
||||||
|
for src, idx, exp_type, exp_val in [
|
||||||
|
('#a', None, 'ID_REF', '#a'),
|
||||||
|
(' #a', None, 'ID_REF', '#a'),
|
||||||
|
('a#a', None, 'IDENTIFIER', 'a'),
|
||||||
|
('(a)#a', 4, 'IDENTIFIER', 'a'),
|
||||||
|
('{a}#a', 4, 'IDENTIFIER', 'a'),
|
||||||
|
('[a]#a', 4, 'IDENTIFIER', 'a'),
|
||||||
|
('(a(#a', 3, 'ID_REF', '#a'),
|
||||||
|
('{a{#a', 3, 'ID_REF', '#a'),
|
||||||
|
('[a[#a', 3, 'ID_REF', '#a'),
|
||||||
|
]:
|
||||||
|
if idx is None:
|
||||||
|
tok_expr = consume(to_(src))
|
||||||
|
else:
|
||||||
|
tok_expr = nth_list(to_(src), idx)
|
||||||
|
lines.append(ae(t_type(tok_expr), f'"{exp_type}"'))
|
||||||
|
lines.append(ae(t_val(tok_expr), sx_str(exp_val)))
|
||||||
|
|
||||||
|
elif name == 'handles identifiers properly':
|
||||||
|
lines.append(ae(t_type(consume(to_('foo'))), '"IDENTIFIER"'))
|
||||||
|
lines.append(ae(t_val(consume(to_('foo'))), '"foo"'))
|
||||||
|
lines.append(ae(t_type(consume(to_(' foo '))), '"IDENTIFIER"'))
|
||||||
|
lines.append(ae(t_val(consume(to_(' foo '))), '"foo"'))
|
||||||
|
for src, v1, v2 in [
|
||||||
|
(' foo bar', 'foo', 'bar'),
|
||||||
|
(' foo\n-- a comment\n bar', 'foo', 'bar'),
|
||||||
|
]:
|
||||||
|
sq = to_(src)
|
||||||
|
lines.append(f' (let ((s {sq}))')
|
||||||
|
lines.append(f' (let ((tok1 (hs-stream-consume s)))')
|
||||||
|
lines.append(f' (assert= (hs-token-type tok1) "IDENTIFIER")')
|
||||||
|
lines.append(f' (assert= (hs-token-value tok1) {sx_str(v1)})')
|
||||||
|
lines.append(f' (let ((tok2 (hs-stream-consume s)))')
|
||||||
|
lines.append(f' (assert= (hs-token-type tok2) "IDENTIFIER")')
|
||||||
|
lines.append(f' (assert= (hs-token-value tok2) {sx_str(v2)}))))')
|
||||||
|
|
||||||
|
elif name == 'handles identifiers with numbers properly':
|
||||||
|
for src in ['f1oo', 'fo1o', 'foo1']:
|
||||||
|
lines.append(ae(t_type(consume(to_(src))), '"IDENTIFIER"'))
|
||||||
|
lines.append(ae(t_val(consume(to_(src))), sx_str(src)))
|
||||||
|
|
||||||
|
elif name == 'handles look ahead property':
|
||||||
|
s = to_('a 1 + 1')
|
||||||
|
for i, v in [(0, 'a'), (1, '1'), (2, '+'), (3, '1'), (4, '<<<EOF>>>')]:
|
||||||
|
lines.append(ae(t_val(tok_i(s, i)), sx_str(v)))
|
||||||
|
|
||||||
|
elif name == 'handles numbers properly':
|
||||||
|
for src, v in [
|
||||||
|
('1', '1'),
|
||||||
|
('1.1', '1.1'),
|
||||||
|
('1234567890.1234567890', '1234567890.1234567890'),
|
||||||
|
('1e6', '1e6'),
|
||||||
|
('1e-6', '1e-6'),
|
||||||
|
('1.1e6', '1.1e6'),
|
||||||
|
('1.1e-6', '1.1e-6'),
|
||||||
|
]:
|
||||||
|
lines.append(ae(t_type(consume(to_(src))), '"NUMBER"'))
|
||||||
|
lines.append(ae(t_val(consume(to_(src))), sx_str(v)))
|
||||||
|
s = to_('1.1.1')
|
||||||
|
toks = f'(get {s} "list")'
|
||||||
|
lines.append(ae(f'(hs-token-type (nth {toks} 0))', '"NUMBER"'))
|
||||||
|
lines.append(ae(f'(hs-token-type (nth {toks} 1))', '"PERIOD"'))
|
||||||
|
lines.append(ae(f'(hs-token-type (nth {toks} 2))', '"NUMBER"'))
|
||||||
|
lines.append(ae(f'(len {toks})', '3'))
|
||||||
|
|
||||||
|
elif name == 'handles operators properly':
|
||||||
|
optable = [
|
||||||
|
('+', 'PLUS'), ('-', 'MINUS'), ('*', 'MULTIPLY'),
|
||||||
|
('.', 'PERIOD'), ('\\', 'BACKSLASH'), (':', 'COLON'),
|
||||||
|
('%', 'PERCENT'), ('|', 'PIPE'), ('!', 'EXCLAMATION'),
|
||||||
|
('?', 'QUESTION'), ('#', 'POUND'), ('&', 'AMPERSAND'),
|
||||||
|
(';', 'SEMI'), (',', 'COMMA'), ('(', 'L_PAREN'),
|
||||||
|
(')', 'R_PAREN'), ('<', 'L_ANG'), ('>', 'R_ANG'),
|
||||||
|
('{', 'L_BRACE'), ('}', 'R_BRACE'), ('[', 'L_BRACKET'),
|
||||||
|
(']', 'R_BRACKET'), ('=', 'EQUALS'),
|
||||||
|
('<=', 'LTE_ANG'), ('>=', 'GTE_ANG'),
|
||||||
|
('==', 'EQ'), ('===', 'EQQ'),
|
||||||
|
]
|
||||||
|
for op_char, _op_name in optable:
|
||||||
|
tok_expr = consume(to_(op_char))
|
||||||
|
lines.append(ae(t_op(tok_expr), 'true'))
|
||||||
|
lines.append(ae(t_val(tok_expr), sx_str(op_char)))
|
||||||
|
|
||||||
|
elif name == 'handles strings properly':
|
||||||
|
for src, v in [
|
||||||
|
('"foo"', 'foo'),
|
||||||
|
('"fo\'o"', "fo'o"),
|
||||||
|
('"fo\\"o"', 'fo"o'),
|
||||||
|
("'foo'", 'foo'),
|
||||||
|
("'fo\"o'", 'fo"o'),
|
||||||
|
("'fo\\'o'", "fo'o"),
|
||||||
|
]:
|
||||||
|
lines.append(ae(t_type(consume(to_(src))), '"STRING"'))
|
||||||
|
lines.append(ae(t_val(consume(to_(src))), sx_str(v)))
|
||||||
|
lines.append(throws(consume(to_("'"))))
|
||||||
|
lines.append(throws(consume(to_('"'))))
|
||||||
|
|
||||||
|
elif name == 'handles strings properly 2':
|
||||||
|
tok_expr = consume(to_("'foo'"))
|
||||||
|
lines.append(ae(t_type(tok_expr), '"STRING"'))
|
||||||
|
lines.append(ae(t_val(tok_expr), '"foo"'))
|
||||||
|
|
||||||
|
elif name == 'handles template bootstrap properly':
|
||||||
|
s1 = to_('"', tmpl=True)
|
||||||
|
lines.append(ae(t_val(tok_i(s1, 0)), sx_str('"')))
|
||||||
|
s2 = to_('"$', tmpl=True)
|
||||||
|
lines.append(ae(t_val(tok_i(s2, 0)), sx_str('"')))
|
||||||
|
lines.append(ae(t_val(tok_i(s2, 1)), '"$"'))
|
||||||
|
s3 = to_('"${', tmpl=True)
|
||||||
|
lines.append(ae(t_val(tok_i(s3, 0)), sx_str('"')))
|
||||||
|
lines.append(ae(t_val(tok_i(s3, 1)), '"$"'))
|
||||||
|
lines.append(ae(t_val(tok_i(s3, 2)), '"{"'))
|
||||||
|
s4 = to_('"${"asdf"', tmpl=True)
|
||||||
|
lines.append(ae(t_val(tok_i(s4, 0)), sx_str('"')))
|
||||||
|
lines.append(ae(t_val(tok_i(s4, 1)), '"$"'))
|
||||||
|
lines.append(ae(t_val(tok_i(s4, 2)), '"{"'))
|
||||||
|
lines.append(ae(t_val(tok_i(s4, 3)), '"asdf"'))
|
||||||
|
s5 = to_('"${"asdf"}"', tmpl=True)
|
||||||
|
lines.append(ae(t_val(tok_i(s5, 0)), sx_str('"')))
|
||||||
|
lines.append(ae(t_val(tok_i(s5, 1)), '"$"'))
|
||||||
|
lines.append(ae(t_val(tok_i(s5, 2)), '"{"'))
|
||||||
|
lines.append(ae(t_val(tok_i(s5, 3)), '"asdf"'))
|
||||||
|
lines.append(ae(t_val(tok_i(s5, 4)), '"}"'))
|
||||||
|
lines.append(ae(t_val(tok_i(s5, 5)), sx_str('"')))
|
||||||
|
|
||||||
|
elif name == 'handles whitespace properly':
|
||||||
|
for src, expected in [
|
||||||
|
(' ', 0), (' asdf', 1), (' asdf ', 2), ('asdf ', 2),
|
||||||
|
('\n', 0), ('\nasdf', 1), ('\nasdf\n', 2), ('asdf\n', 2),
|
||||||
|
('\r', 0), ('\rasdf', 1), ('\rasdf\r', 2), ('asdf\r', 2),
|
||||||
|
('\t', 0), ('\tasdf', 1), ('\tasdf\t', 2), ('asdf\t', 2),
|
||||||
|
]:
|
||||||
|
lines.append(ae(list_len(to_(src)), str(expected)))
|
||||||
|
|
||||||
|
else:
|
||||||
|
return None # not a tokenizer test we handle
|
||||||
|
|
||||||
|
lines.append(' )')
|
||||||
|
return '\n'.join(lines)
|
||||||
|
|
||||||
|
|
||||||
def generate_eval_only_test(test, idx):
|
def generate_eval_only_test(test, idx):
|
||||||
"""Generate SX deftest for no-HTML tests using eval-hs.
|
"""Generate SX deftest for no-HTML tests using eval-hs.
|
||||||
Handles patterns:
|
Handles patterns:
|
||||||
@@ -2276,6 +2569,50 @@ def generate_eval_only_test(test, idx):
|
|||||||
f' )'
|
f' )'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Special case: cluster-38 sourceInfo tests.
|
||||||
|
if test['name'] == 'debug':
|
||||||
|
return (
|
||||||
|
f' (deftest "{safe_name}"\n'
|
||||||
|
f' (assert= (hs-src "<button.foo/>") "<button.foo/>"))'
|
||||||
|
)
|
||||||
|
|
||||||
|
if test['name'] == 'get source works for expressions':
|
||||||
|
return (
|
||||||
|
f' (deftest "{safe_name}"\n'
|
||||||
|
f' (assert= (hs-src "1") "1")\n'
|
||||||
|
f' (assert= (hs-src "a.b") "a.b")\n'
|
||||||
|
f' (assert= (hs-src-at "a.b" (list :root)) "a")\n'
|
||||||
|
f' (assert= (hs-src "a.b()") "a.b()")\n'
|
||||||
|
f' (assert= (hs-src-at "a.b()" (list :root)) "a.b")\n'
|
||||||
|
f' (assert= (hs-src-at "a.b()" (list :root :root)) "a")\n'
|
||||||
|
f' (assert= (hs-src "<button.foo/>") "<button.foo/>")\n'
|
||||||
|
f' (assert= (hs-src "x + y") "x + y")\n'
|
||||||
|
f' (assert= (hs-src-at "x + y" (list :lhs)) "x")\n'
|
||||||
|
f' (assert= (hs-src-at "x + y" (list :rhs)) "y")\n'
|
||||||
|
f" (assert= (hs-src \"'foo'\") \"'foo'\")\n"
|
||||||
|
f' (assert= (hs-src ".foo") ".foo")\n'
|
||||||
|
f' (assert= (hs-src "#bar") "#bar"))'
|
||||||
|
)
|
||||||
|
|
||||||
|
if test['name'] == 'get source works for statements':
|
||||||
|
return (
|
||||||
|
f' (deftest "{safe_name}"\n'
|
||||||
|
f" (assert= (hs-src \"if true log 'it was true'\") \"if true log 'it was true'\")\n"
|
||||||
|
f' (assert= (hs-src "for x in [1, 2, 3] log x then log x end") "for x in [1, 2, 3] log x then log x end"))'
|
||||||
|
)
|
||||||
|
|
||||||
|
if test['name'] == 'get line works for statements':
|
||||||
|
src = "if true\\n log 'it was true'\\n log 'it was true'"
|
||||||
|
return (
|
||||||
|
f' (deftest "{safe_name}"\n'
|
||||||
|
f' (assert= (hs-line-at "{src}" (list)) "if true")\n'
|
||||||
|
f" (assert= (hs-line-at \"{src}\" (list :true-branch)) \" log 'it was true'\")\n"
|
||||||
|
f" (assert= (hs-line-at \"{src}\" (list :true-branch :next)) \" log 'it was true'\"))"
|
||||||
|
)
|
||||||
|
|
||||||
|
if '_hyperscript.internals.tokenizer' in body:
|
||||||
|
return generate_tokenizer_test(test, safe_name)
|
||||||
|
|
||||||
lines.append(f' (deftest "{safe_name}"')
|
lines.append(f' (deftest "{safe_name}"')
|
||||||
|
|
||||||
assertions = []
|
assertions = []
|
||||||
@@ -2287,13 +2624,20 @@ def generate_eval_only_test(test, idx):
|
|||||||
def emit_eval(hs_expr, expected_sx, extra_locals=None):
|
def emit_eval(hs_expr, expected_sx, extra_locals=None):
|
||||||
"""Emit an assertion using eval-hs / eval-hs-locals / eval-hs-with-me
|
"""Emit an assertion using eval-hs / eval-hs-locals / eval-hs-with-me
|
||||||
as appropriate, given the window setups and any per-call locals.
|
as appropriate, given the window setups and any per-call locals.
|
||||||
|
Uses assert-equal (deep equal?) when expected contains dicts; assert= otherwise.
|
||||||
"""
|
"""
|
||||||
pairs = list(window_setups) + list(extra_locals or [])
|
pairs = list(window_setups) + list(extra_locals or [])
|
||||||
|
# assert= uses = (reference equality for dicts); assert-equal uses equal? (deep)
|
||||||
|
use_deep = '{' in expected_sx
|
||||||
if pairs:
|
if pairs:
|
||||||
locals_sx = '(list ' + ' '.join(
|
locals_sx = '(list ' + ' '.join(
|
||||||
f'(list (quote {n}) {v})' for n, v in pairs
|
f'(list (quote {n}) {v})' for n, v in pairs
|
||||||
) + ')'
|
) + ')'
|
||||||
|
if use_deep:
|
||||||
|
return f' (assert-equal {expected_sx} (eval-hs-locals "{hs_expr}" {locals_sx}))'
|
||||||
return f' (assert= (eval-hs-locals "{hs_expr}" {locals_sx}) {expected_sx})'
|
return f' (assert= (eval-hs-locals "{hs_expr}" {locals_sx}) {expected_sx})'
|
||||||
|
if use_deep:
|
||||||
|
return f' (assert-equal {expected_sx} (eval-hs "{hs_expr}"))'
|
||||||
return f' (assert= (eval-hs "{hs_expr}") {expected_sx})'
|
return f' (assert= (eval-hs "{hs_expr}") {expected_sx})'
|
||||||
|
|
||||||
# Shared sub-pattern for run() call with optional String.raw and extra args:
|
# Shared sub-pattern for run() call with optional String.raw and extra args:
|
||||||
|
|||||||
Reference in New Issue
Block a user