Unicode escapes, variadic infix fix, spreads demos, scoped-effects + foundations plans

- Add \uXXXX unicode escape support to parser.py and parser.sx spec
- Add char-from-code primitive (Python chr(), JS String.fromCharCode())
- Fix variadic infix operators in both bootstrappers (js.sx, py.sx) —
  (+ a b c d) was silently dropping terms, now left-folds correctly
- Rebootstrap sx_ref.py and sx-browser.js with all fixes
- Fix 3 pre-existing map-dict test failures in shared/sx/tests/run.py
- Add live demos alongside examples in spreads essay (side-by-side layout)
- Add scoped-effects plan: algebraic effects as unified foundation for
  spread/collect/island/lake/signal/context
- Add foundations plan: CEK machine, the computational floor, three-axis
  model (depth/topology/linearity), Curry-Howard correspondence
- Route both plans in page-functions.sx and nav-data.sx

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-13 12:03:58 +00:00
parent 28a6560963
commit 214963ea6a
15 changed files with 1323 additions and 38 deletions

View File

@@ -2,6 +2,87 @@
;; Spreads — child-to-parent communication across render boundaries
;; ---------------------------------------------------------------------------
;; ---- Layout helper ----
(defcomp ~geography/demo-example (&key demo code)
(div :class "grid grid-cols-1 lg:grid-cols-2 gap-4 my-6 items-start"
(div :class "border border-dashed border-stone-300 rounded-lg p-4 bg-stone-50 min-h-[80px]"
demo)
(div :class "not-prose bg-stone-100 rounded-lg p-4 overflow-x-auto"
(pre :class "text-sm leading-relaxed whitespace-pre-wrap break-words" (code code)))))
;; ---- Demo components ----
(defcomp ~geography/demo-callout (&key type)
(make-spread
(cond
(= type "info") {"style" "border-left:4px solid #3b82f6;padding:0.5rem 0.75rem;background:#eff6ff;border-radius:0.25rem"
"data-callout" "info"}
(= type "warning") {"style" "border-left:4px solid #f59e0b;padding:0.5rem 0.75rem;background:#fffbeb;border-radius:0.25rem"
"data-callout" "warning"}
(= type "success") {"style" "border-left:4px solid #10b981;padding:0.5rem 0.75rem;background:#ecfdf5;border-radius:0.25rem"
"data-callout" "success"}
:else {"style" "border-left:4px solid #78716c;padding:0.5rem 0.75rem;background:#fafaf9;border-radius:0.25rem"
"data-callout" "default"})))
(defcomp ~geography/demo-spread-basic ()
(div :class "space-y-3"
(div (~geography/demo-callout :type "info")
(p :class "text-sm" "Info — styled by its child."))
(div (~geography/demo-callout :type "warning")
(p :class "text-sm" "Warning — same component, different type."))
(div (~geography/demo-callout :type "success")
(p :class "text-sm" "Success — child tells parent how to look."))))
(defcomp ~geography/demo-cssx-tw ()
(div :class "space-y-3"
(div (~cssx/tw :tokens "bg-violet-100 rounded-lg p-4")
(h4 (~cssx/tw :tokens "text-violet-800 font-semibold text-lg") "Styled via ~cssx/tw")
(p (~cssx/tw :tokens "text-stone-600 text-sm mt-1")
"Classes injected from spread child."))
(div (~cssx/tw :tokens "bg-rose-50 rounded-lg p-4 border border-rose-200")
(h4 (~cssx/tw :tokens "text-rose-700 font-semibold text-lg") "Different tokens")
(p (~cssx/tw :tokens "text-stone-600 text-sm mt-1")
"CSS rules JIT-generated. No build step."))))
(defcomp ~geography/demo-semantic-vars ()
(let ((card (~cssx/tw :tokens "bg-stone-50 rounded-lg p-4 shadow-sm border border-stone-200"))
(heading (~cssx/tw :tokens "text-violet-700 text-lg font-bold"))
(body-text (~cssx/tw :tokens "text-stone-600 text-sm mt-1")))
(div :class "space-y-3"
(div card
(h4 heading "First Card")
(p body-text "Named spreads bound with let."))
(div card
(h4 heading "Second Card")
(p body-text "Same variables, consistent look.")))))
(defisland ~geography/demo-reactive-spread ()
(let ((colour (signal "violet")))
(div (~cssx/tw :tokens (str "rounded-lg p-4 transition-all duration-300 bg-" (deref colour) "-100 border border-" (deref colour) "-300"))
(h4 (~cssx/tw :tokens (str "text-" (deref colour) "-800 font-semibold text-lg"))
(str "Theme: " (deref colour)))
(p (~cssx/tw :tokens "text-stone-600 text-sm mt-2 mb-3")
"Click to change theme. Reactive spreads surgically update classes.")
(div :class "flex gap-2 flex-wrap"
(button :on-click (fn (e) (reset! colour "violet"))
(~cssx/tw :tokens "bg-violet-500 text-white px-3 py-1.5 rounded text-sm font-medium")
"Violet")
(button :on-click (fn (e) (reset! colour "rose"))
(~cssx/tw :tokens "bg-rose-500 text-white px-3 py-1.5 rounded text-sm font-medium")
"Rose")
(button :on-click (fn (e) (reset! colour "amber"))
(~cssx/tw :tokens "bg-amber-500 text-white px-3 py-1.5 rounded text-sm font-medium")
"Amber")
(button :on-click (fn (e) (reset! colour "emerald"))
(~cssx/tw :tokens "bg-emerald-500 text-white px-3 py-1.5 rounded text-sm font-medium")
"Emerald")))))
;; ---- Page content ----
(defcomp ~geography/spreads-content ()
(~docs/page :title "Spreads"
@@ -22,9 +103,11 @@
(p "A spread is a value type. " (code "make-spread") " creates one from a dict of "
"attributes. When the renderer encounters a spread as a child of an element, "
"it merges the attrs onto the parent element instead of appending a DOM node.")
(~docs/code :code (highlight "(defcomp ~highlight (&key colour)\n (make-spread {\"class\" (str \"highlight-\" colour)\n \"data-highlight\" colour}))" "lisp"))
(p "Use it as a child of any element:")
(~docs/code :code (highlight "(div (~highlight :colour \"yellow\")\n \"This div gets class=highlight-yellow\")" "lisp"))
(~geography/demo-example
:demo (~geography/demo-spread-basic)
:code (highlight "(defcomp ~callout (&key type)\n (make-spread\n (cond\n (= type \"info\")\n {\"style\" \"border-left:4px solid\n #3b82f6; background:#eff6ff\"}\n (= type \"warning\")\n {\"style\" \"border-left:4px solid\n #f59e0b; background:#fffbeb\"}\n (= type \"success\")\n {\"style\" \"border-left:4px solid\n #10b981; background:#ecfdf5\"})))\n\n;; Child injects attrs onto parent:\n(div (~callout :type \"info\")\n \"This div gets the callout style.\")" "lisp"))
(p (code "class") " values are appended (space-joined). "
(code "style") " values are appended (semicolon-joined). "
"All other attributes overwrite."))
@@ -32,7 +115,7 @@
(~docs/subsection :title "2. collect! / collected / clear-collected!"
(p "Render-time accumulators. Values are collected into named buckets "
"during rendering and retrieved at flush points. Deduplication is automatic.")
(~docs/code :code (highlight ";; Deep inside a component tree:\n(collect! \"cssx\" \".sx-bg-red-500{background-color:hsl(0,72%,53%)}\")\n\n;; At the flush point (once, in the layout):\n(let ((rules (collected \"cssx\")))\n (clear-collected! \"cssx\")\n (raw! (str \"<style>\" (join \"\" rules) \"</style>\")))" "lisp"))
(~docs/code :code (highlight ";; Deep inside a component tree:\n(collect! \"cssx\" \".sx-bg-red-500{background:red}\")\n\n;; At the flush point (once, in the layout):\n(let ((rules (collected \"cssx\")))\n (clear-collected! \"cssx\")\n (raw! (str \"<style>\" (join \"\" rules) \"</style>\")))" "lisp"))
(p "This is upward communication through the render tree: "
"a deeply nested component contributes a CSS rule, and the layout "
"emits all accumulated rules as a single " (code "<style>") " tag. "
@@ -42,12 +125,15 @@
(p "Inside an island, when a spread's value depends on signals, "
(code "reactive-spread") " tracks signal dependencies and surgically "
"updates the parent element's attributes when signals change.")
(~docs/code :code (highlight "(defisland ~themed-card ()\n (let ((theme (signal \"violet\")))\n (div (~cssx/tw :tokens (str \"bg-\" (deref theme) \"-500 p-4\"))\n (button :on-click (fn (e) (reset! theme \"rose\"))\n \"change theme\"))))" "lisp"))
(p "When " (code "theme") " changes from " (code "\"violet\"") " to "
(code "\"rose\"") ":")
(~geography/demo-example
:demo (~geography/demo-reactive-spread)
:code (highlight "(defisland ~themed-card ()\n (let ((theme (signal \"violet\")))\n (div (~cssx/tw :tokens\n (str \"bg-\" (deref theme)\n \"-100 p-4\"))\n (button\n :on-click\n (fn (e) (reset! theme \"rose\"))\n \"change theme\"))))" "lisp"))
(p "When " (code "theme") " changes:")
(ul :class "list-disc pl-5 space-y-1 text-stone-600"
(li "Old classes (" (code "sx-bg-violet-500") ") are removed from the element")
(li "New classes (" (code "sx-bg-rose-500") ") are added")
(li "Old classes are removed from the element")
(li "New classes are added")
(li "New CSS rules are JIT-generated and flushed to the live stylesheet")
(li "No re-render. No VDOM. No diffing. Just attr surgery."))))
@@ -98,11 +184,15 @@
(~docs/section :title "CSSX: the first application" :id "cssx"
(p (code "~cssx/tw") " is a component that uses all three primitives:")
(~docs/code :code (highlight "(defcomp ~cssx/tw (tokens)\n (let ((token-list (filter (fn (t) (not (= t \"\")))\n (split (or tokens \"\") \" \")))\n (results (map cssx-process-token token-list))\n (valid (filter (fn (r) (not (nil? r))) results))\n (classes (map (fn (r) (get r \"cls\")) valid))\n (rules (map (fn (r) (get r \"rule\")) valid))\n (_ (for-each (fn (rule) (collect! \"cssx\" rule)) rules)))\n (if (empty? classes)\n nil\n (make-spread {\"class\" (join \" \" classes)\n \"data-tw\" (or tokens \"\")}))))" "lisp"))
(p "It's a regular " (code "defcomp") ". It uses " (code "make-spread") " to "
"inject classes onto its parent, " (code "collect!") " to accumulate CSS rules "
"for batch flushing, and when called inside an island with signal-dependent "
"tokens, " (code "reactive-spread") " makes it live.")
(~geography/demo-example
:demo (~geography/demo-cssx-tw)
:code (highlight "(defcomp ~cssx/tw (tokens)\n (let ((token-list\n (filter (fn (t) (not (= t \"\")))\n (split (or tokens \"\") \" \")))\n (results\n (map cssx-process-token\n token-list))\n (classes (map (fn (r)\n (get r \"cls\")) results))\n (rules (map (fn (r)\n (get r \"rule\")) results)))\n (for-each (fn (rule)\n (collect! \"cssx\" rule)) rules)\n (make-spread\n {\"class\" (join \" \" classes)})))" "lisp"))
(p "It uses " (code "make-spread") " to inject classes, "
(code "collect!") " to accumulate CSS rules for batch flushing, and "
"when called inside an island with signal-dependent tokens, "
(code "reactive-spread") " makes it live.")
(p "But " (code "~cssx/tw") " is just one instance. The same primitives enable:")
(ul :class "list-disc pl-5 space-y-1 text-stone-600"
(li (code "~aria") " — reactive accessibility attributes driven by UI state")
@@ -117,9 +207,13 @@
(~docs/section :title "Semantic style variables" :id "variables"
(p "Because " (code "~cssx/tw") " returns a spread, and spreads are values, "
"you can bind them to names:")
(~docs/code :code (highlight ";; Define once\n(define heading-style (~cssx/tw :tokens \"text-violet-700 text-2xl font-bold\"))\n(define nav-link (~cssx/tw :tokens \"text-stone-500 text-sm\"))\n(define card-base (~cssx/tw :tokens \"bg-stone-50 rounded-lg p-4\"))\n\n;; Use everywhere\n(div card-base\n (h1 heading-style \"Title\")\n (a nav-link :href \"/\" \"Home\"))" "lisp"))
(~geography/demo-example
:demo (~geography/demo-semantic-vars)
:code (highlight "(let ((card (~cssx/tw :tokens\n \"bg-stone-50 rounded-lg p-4\n shadow-sm border\"))\n (heading (~cssx/tw :tokens\n \"text-violet-700 text-lg\n font-bold\"))\n (body (~cssx/tw :tokens\n \"text-stone-600 text-sm\")))\n ;; Reuse everywhere:\n (div card\n (h4 heading \"First Card\")\n (p body \"Same variables.\"))\n (div card\n (h4 heading \"Second Card\")\n (p body \"Consistent look.\")))" "lisp"))
(p "These are semantic names wrapping utility tokens. Change the definition, "
"every use updates. No build step, no CSS-in-JS runtime. Just " (code "define") ".")
"every use updates. No build step, no CSS-in-JS runtime. Just " (code "let") ".")
(p "Namespacing prevents clashes — " (code "~app/heading") " vs "
(code "~admin/heading") " are different components in different namespaces."))