Files
mono/shared/sx/ref/render.sx
giles daeecab310 Restructure SX ref spec into core + selectable adapters
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>
2026-03-05 11:49:44 +00:00

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
;; --------------------------------------------------------------------------