Split monolithic render.sx into core (tag registries, shared utils) plus four adapter .sx files: adapter-html (server HTML strings), adapter-sx (SX wire format), adapter-dom (browser DOM nodes), and engine (SxEngine triggers, morphing, swaps). All adapters written in s-expressions with platform interface declarations for JS bridge functions. Bootstrap compiler now accepts --adapters flag to emit targeted builds: -a html → server-only (1108 lines) -a dom,engine → browser-only (1634 lines) -a html,sx → server with SX wire (1169 lines) (default) → all adapters (1800 lines) Fixes: keyword arg i-counter desync in reduce across all adapters, render-aware special forms (let/if/when/cond/map) in HTML adapter, component children double-escaping, ~prefixed macro dispatch. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
148 lines
5.9 KiB
Plaintext
148 lines
5.9 KiB
Plaintext
;; ==========================================================================
|
|
;; render.sx — Core rendering specification
|
|
;;
|
|
;; Shared registries and utilities used by all rendering adapters.
|
|
;; This file defines WHAT is renderable (tag registries, attribute rules)
|
|
;; and HOW arguments are parsed — but not the output format.
|
|
;;
|
|
;; Adapters:
|
|
;; adapter-html.sx — HTML string output (server)
|
|
;; adapter-sx.sx — SX wire format output (server → client)
|
|
;; adapter-dom.sx — Live DOM node output (browser)
|
|
;;
|
|
;; Each adapter imports these shared definitions and provides its own
|
|
;; render entry point (render-to-html, render-to-sx, render-to-dom).
|
|
;; ==========================================================================
|
|
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; HTML tag registry
|
|
;; --------------------------------------------------------------------------
|
|
;; Tags known to the renderer. Unknown names are treated as function calls.
|
|
;; Void elements self-close (no children). Boolean attrs emit name only.
|
|
|
|
(define HTML_TAGS
|
|
(list
|
|
;; Document
|
|
"html" "head" "body" "title" "meta" "link" "script" "style" "noscript"
|
|
;; Sections
|
|
"header" "nav" "main" "section" "article" "aside" "footer"
|
|
"h1" "h2" "h3" "h4" "h5" "h6" "hgroup"
|
|
;; Block
|
|
"div" "p" "blockquote" "pre" "figure" "figcaption" "address" "details" "summary"
|
|
;; Inline
|
|
"a" "span" "em" "strong" "small" "b" "i" "u" "s" "mark" "sub" "sup"
|
|
"abbr" "cite" "code" "time" "br" "wbr" "hr"
|
|
;; Lists
|
|
"ul" "ol" "li" "dl" "dt" "dd"
|
|
;; Tables
|
|
"table" "thead" "tbody" "tfoot" "tr" "th" "td" "caption" "colgroup" "col"
|
|
;; Forms
|
|
"form" "input" "textarea" "select" "option" "optgroup" "button" "label"
|
|
"fieldset" "legend" "output" "datalist"
|
|
;; Media
|
|
"img" "video" "audio" "source" "picture" "canvas" "iframe"
|
|
;; SVG
|
|
"svg" "math" "path" "circle" "ellipse" "rect" "line" "polyline" "polygon"
|
|
"text" "tspan" "g" "defs" "use" "clipPath" "mask" "pattern"
|
|
"linearGradient" "radialGradient" "stop" "filter"
|
|
"feGaussianBlur" "feOffset" "feBlend" "feColorMatrix" "feComposite"
|
|
"feMerge" "feMergeNode" "feTurbulence"
|
|
"feComponentTransfer" "feFuncR" "feFuncG" "feFuncB" "feFuncA"
|
|
"feDisplacementMap" "feFlood" "feImage" "feMorphology"
|
|
"feSpecularLighting" "feDiffuseLighting"
|
|
"fePointLight" "feSpotLight" "feDistantLight"
|
|
"animate" "animateTransform" "foreignObject"
|
|
;; Other
|
|
"template" "slot" "dialog" "menu"))
|
|
|
|
(define VOID_ELEMENTS
|
|
(list "area" "base" "br" "col" "embed" "hr" "img" "input"
|
|
"link" "meta" "param" "source" "track" "wbr"))
|
|
|
|
(define BOOLEAN_ATTRS
|
|
(list "async" "autofocus" "autoplay" "checked" "controls" "default"
|
|
"defer" "disabled" "formnovalidate" "hidden" "inert" "ismap"
|
|
"loop" "multiple" "muted" "nomodule" "novalidate" "open"
|
|
"playsinline" "readonly" "required" "reversed" "selected"))
|
|
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; Shared utilities
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(define definition-form?
|
|
(fn (name)
|
|
(or (= name "define") (= name "defcomp") (= name "defmacro")
|
|
(= name "defstyle") (= name "defkeyframes") (= name "defhandler"))))
|
|
|
|
|
|
(define parse-element-args
|
|
(fn (args env)
|
|
;; Parse (:key val :key2 val2 child1 child2) into (attrs-dict children-list)
|
|
(let ((attrs (dict))
|
|
(children (list)))
|
|
(reduce
|
|
(fn (state arg)
|
|
(let ((skip (get state "skip")))
|
|
(if skip
|
|
(assoc state "skip" false "i" (inc (get state "i")))
|
|
(if (and (= (type-of arg) "keyword")
|
|
(< (inc (get state "i")) (len args)))
|
|
(let ((val (trampoline (eval-expr (nth args (inc (get state "i"))) env))))
|
|
(dict-set! attrs (keyword-name arg) val)
|
|
(assoc state "skip" true "i" (inc (get state "i"))))
|
|
(do
|
|
(append! children arg)
|
|
(assoc state "i" (inc (get state "i"))))))))
|
|
(dict "i" 0 "skip" false)
|
|
args)
|
|
(list attrs children))))
|
|
|
|
|
|
(define render-attrs
|
|
(fn (attrs)
|
|
;; Render an attrs dict to an HTML attribute string.
|
|
;; Used by adapter-html.sx and adapter-sx.sx.
|
|
(join ""
|
|
(map
|
|
(fn (key)
|
|
(let ((val (dict-get attrs key)))
|
|
(cond
|
|
;; Boolean attrs
|
|
(and (contains? BOOLEAN_ATTRS key) val)
|
|
(str " " key)
|
|
(and (contains? BOOLEAN_ATTRS key) (not val))
|
|
""
|
|
;; Nil values — skip
|
|
(nil? val) ""
|
|
;; StyleValue on :style → emit as class
|
|
(and (= key "style") (style-value? val))
|
|
(str " class=\"" (style-value-class val) "\"")
|
|
;; Normal attr
|
|
:else (str " " key "=\"" (escape-attr (str val)) "\""))))
|
|
(keys attrs)))))
|
|
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; Platform interface (shared across adapters)
|
|
;; --------------------------------------------------------------------------
|
|
;;
|
|
;; HTML/attribute escaping (used by HTML and SX wire adapters):
|
|
;; (escape-html s) → HTML-escaped string
|
|
;; (escape-attr s) → attribute-value-escaped string
|
|
;; (raw-html-content r) → unwrap RawHTML marker to string
|
|
;;
|
|
;; StyleValue:
|
|
;; (style-value? x) → boolean (is x a StyleValue?)
|
|
;; (style-value-class sv) → string (CSS class name)
|
|
;;
|
|
;; Serialization:
|
|
;; (serialize val) → SX source string representation of val
|
|
;;
|
|
;; Form classification (used by SX wire adapter):
|
|
;; (special-form? name) → boolean
|
|
;; (ho-form? name) → boolean
|
|
;; (aser-special name expr env) → evaluate special/HO form through aser
|
|
;; --------------------------------------------------------------------------
|