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