Step 18 (part 7): Extensions — render components + SX escape
Two hyperscript extensions beyond stock:
render ~component :key val [into|before|after target]
Tokenizer: ~ + ident → component token type
Parser: render command with kwargs and optional position
Compiler: emits (render-to-html ~comp :key val) or
(hs-put! (render-to-html ...) pos target)
Bridges hyperscript flow to SX component rendering
eval (sx-expression) — SX escape hatch
Inside eval (...), content is SX syntax (not hyperscript)
Parser: collect-sx-source extracts balanced parens from raw source
Compiler: sx-parse at compile time, inlines AST directly
Result: SX runs in handler scope — hyperscript variables visible!
Also supports string form: eval '(+ 1 2)' for backward compat
set name to "Giles"
set greeting to eval (str "Hello " name) -- name is visible!
16 new tests (parser + compiler + integration).
3127/3127 full build, zero regressions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -203,6 +203,55 @@
|
||||
(assert= (quote fn) (first (nth sx 1))))))
|
||||
|
||||
;; ── Return and throw ─────────────────────────────────────────
|
||||
(defsuite
|
||||
"hs-emit-render"
|
||||
(deftest
|
||||
"render emits render-to-html"
|
||||
(let
|
||||
((sx (hs-to-sx-from-source "render ~card")))
|
||||
(assert= (quote render-to-html) (first sx))
|
||||
(assert= (quote ~card) (nth sx 1))))
|
||||
(deftest
|
||||
"render with kwargs emits keywords"
|
||||
(let
|
||||
((sx (hs-to-sx-from-source "render ~card :title 'Hi'")))
|
||||
(assert= (quote render-to-html) (first sx))
|
||||
(assert= (quote ~card) (nth sx 1))
|
||||
(assert= (make-keyword "title") (nth sx 2))
|
||||
(assert= "Hi" (nth sx 3))))
|
||||
(deftest
|
||||
"render into emits hs-put!"
|
||||
(let
|
||||
((sx (hs-to-sx-from-source "render ~card into #box")))
|
||||
(assert= (quote hs-put!) (first sx))
|
||||
(assert= (quote render-to-html) (first (nth sx 1)))
|
||||
(assert= "into" (nth sx 2))))
|
||||
(deftest
|
||||
"component ref emits symbol"
|
||||
(let
|
||||
((sx (hs-to-sx (list (quote component) "~badge"))))
|
||||
(assert= (quote ~badge) sx))))
|
||||
|
||||
;; ── Increment / decrement ────────────────────────────────────
|
||||
(defsuite
|
||||
"hs-emit-sx-eval"
|
||||
(deftest
|
||||
"eval inlines SX at compile time"
|
||||
(let
|
||||
((sx (hs-to-sx-from-source "set x to eval (+ 1 2)")))
|
||||
(assert= (quote set!) (first sx))
|
||||
(let
|
||||
((val (nth sx 2)))
|
||||
(assert= (quote +) (first val))
|
||||
(assert= 1 (nth val 1))
|
||||
(assert= 2 (nth val 2)))))
|
||||
(deftest
|
||||
"eval preserves variable refs"
|
||||
(let
|
||||
((sx (hs-to-sx-from-source "eval (log x)")))
|
||||
(assert= (quote log) (first sx))
|
||||
(assert= (quote x) (nth sx 1)))))
|
||||
|
||||
(defsuite
|
||||
"hs-emit-return-throw"
|
||||
(deftest
|
||||
@@ -227,7 +276,6 @@
|
||||
(assert= (quote hs-wait-for) (first sx))
|
||||
(assert= "transitionend" (nth sx 2)))))
|
||||
|
||||
;; ── Increment / decrement ────────────────────────────────────
|
||||
(defsuite
|
||||
"hs-emit-inc-dec"
|
||||
(deftest
|
||||
|
||||
@@ -649,6 +649,64 @@
|
||||
(assert= "reset" (nth ast 1))
|
||||
(assert= 0 (len (nth ast 2))))))
|
||||
|
||||
(defsuite
|
||||
"hs-parse-render"
|
||||
(deftest
|
||||
"render component"
|
||||
(let
|
||||
((ast (hs-compile "render ~card")))
|
||||
(assert= (quote render) (first ast))
|
||||
(assert= "~card" (nth ast 1))
|
||||
(assert= 0 (len (nth ast 2)))))
|
||||
(deftest
|
||||
"render with kwargs"
|
||||
(let
|
||||
((ast (hs-compile "render ~card :title 'Hello'")))
|
||||
(assert= "~card" (nth ast 1))
|
||||
(let
|
||||
((kw (nth ast 2)))
|
||||
(assert= "title" (first kw))
|
||||
(assert= "Hello" (nth kw 1)))))
|
||||
(deftest
|
||||
"render into target"
|
||||
(let
|
||||
((ast (hs-compile "render ~card :title 'Hi' into #box")))
|
||||
(assert= "into" (nth ast 3))
|
||||
(assert= (quote query) (first (nth ast 4)))))
|
||||
(deftest
|
||||
"component token in expression"
|
||||
(let
|
||||
((ast (hs-compile "set x to ~myComp")))
|
||||
(let
|
||||
((val (nth ast 2)))
|
||||
(assert= (quote component) (first val))
|
||||
(assert= "~myComp" (nth val 1))))))
|
||||
|
||||
(defsuite
|
||||
"hs-parse-sx-eval"
|
||||
(deftest
|
||||
"eval with parens extracts raw SX"
|
||||
(let
|
||||
((ast (hs-compile "set x to eval (+ 1 2)")))
|
||||
(let
|
||||
((val (nth ast 2)))
|
||||
(assert= (quote sx-eval) (first val))
|
||||
(assert= "(+ 1 2)" (nth val 1)))))
|
||||
(deftest
|
||||
"eval with string fallback"
|
||||
(let
|
||||
((ast (hs-compile "eval '(log 42)'")))
|
||||
(assert= (quote sx-eval) (first ast))
|
||||
(assert= "(log 42)" (nth ast 1))))
|
||||
(deftest
|
||||
"eval nested parens"
|
||||
(let
|
||||
((ast (hs-compile "set x to eval (map (fn (x) (+ x 1)) items)")))
|
||||
(let
|
||||
((val (nth ast 2)))
|
||||
(assert= (quote sx-eval) (first val))
|
||||
(assert= "(map (fn (x) (+ x 1)) items)" (nth val 1))))))
|
||||
|
||||
(defsuite
|
||||
"hs-parse-every-modifier"
|
||||
(deftest
|
||||
|
||||
@@ -125,4 +125,22 @@
|
||||
((sx (hs-to-sx-from-source "def add(a, b) return a end")))
|
||||
(assert= (quote define) (first sx))
|
||||
(assert= (quote add) (nth sx 1))
|
||||
(assert= (quote fn) (first (nth sx 2))))))
|
||||
(assert= (quote fn) (first (nth sx 2))))))
|
||||
|
||||
(defsuite
|
||||
"hs-handler-extensions"
|
||||
(deftest
|
||||
"render compiles to render-to-html"
|
||||
(let
|
||||
((sx (hs-to-sx-from-source "render ~badge :label 'New'")))
|
||||
(assert= (quote render-to-html) (first sx))
|
||||
(assert= (quote ~badge) (nth sx 1))))
|
||||
(deftest
|
||||
"eval inlines SX with variable access"
|
||||
(let
|
||||
((h (hs-handler "set x to 10 then set y to eval (+ x 5)")))
|
||||
(h "el")
|
||||
(assert= 15 y)))
|
||||
(deftest
|
||||
"eval as command in handler"
|
||||
(let ((h (hs-handler "eval (set! z 99)"))) (h "el") (assert= 99 z))))
|
||||
Reference in New Issue
Block a user