Parser: skip unit suffix when next ident is a comparison keyword (starts, ends, contains, matches, is, does, in, precedes, follows). Fixes "123 starts with '12'" returning "123starts" instead of true. eval-hs: use hs-compile directly instead of hs-to-sx-from-source with "return " prefix, which was causing the parser to consume the comparison as a string suffix. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
283 lines
27 KiB
Plaintext
283 lines
27 KiB
Plaintext
;; ---------------------------------------------------------------------------
|
|
;; Runtime Slicing
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ()
|
|
(~docs/page :title "Runtime Slicing"
|
|
|
|
(~docs/section :title "The Problem" :id "problem"
|
|
(p "sx-browser.js is the full SX client runtime — evaluator, parser, renderer, engine, morph, signals, routing, orchestration, boot. Every page loads all of it.")
|
|
|
|
(div (~tw :tokens "overflow-x-auto rounded border border-stone-200 mb-4")
|
|
(table (~tw :tokens "w-full text-left text-sm")
|
|
(thead (tr (~tw :tokens "border-b border-stone-200 bg-stone-100")
|
|
(th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "File")
|
|
(th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "Raw")
|
|
(th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "Gzipped")
|
|
(th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "Min+Gz")))
|
|
(tbody
|
|
(tr (~tw :tokens "border-b border-stone-100")
|
|
(td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "sx-browser.js")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") "354KB")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") "75KB")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") "44KB"))
|
|
(tr (~tw :tokens "border-b border-stone-100")
|
|
(td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "sx-ref.js")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") "244KB")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") "49KB")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") "29KB"))
|
|
(tr (~tw :tokens "border-b border-stone-100")
|
|
(td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "React + ReactDOM")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") "—")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") "—")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") "~5KB + ~40KB")))))
|
|
|
|
(p "Most pages are L0 (pure hypermedia — server renders, client morphs). They don't need the parser, the evaluator, the full primitive set, signals, or client-side routing. They need morph + swap + trigger dispatch. That's a fraction of the runtime.")
|
|
(p "The runtime should be sliceable: each page declares what level it operates at, and the bootstrapper emits only the code that level requires."))
|
|
|
|
;; -----------------------------------------------------------------------
|
|
;; Tiers
|
|
;; -----------------------------------------------------------------------
|
|
|
|
(~docs/section :title "Tiers" :id "tiers"
|
|
(p "Four tiers, matching the " (a :href "/sx/(geography.(reactive.(reactive-design)))" (~tw :tokens "text-violet-700 underline") "reactive islands") " levels:")
|
|
|
|
(div (~tw :tokens "overflow-x-auto rounded border border-stone-200 mb-4")
|
|
(table (~tw :tokens "w-full text-left text-sm")
|
|
(thead (tr (~tw :tokens "border-b border-stone-200 bg-stone-100")
|
|
(th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "Tier")
|
|
(th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "What")
|
|
(th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "Modules")
|
|
(th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "Target (min+gz)")))
|
|
(tbody
|
|
(tr (~tw :tokens "border-b border-stone-100")
|
|
(td (~tw :tokens "px-3 py-2 font-semibold text-stone-800") "L0 Hypermedia")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") "Morph, swap, trigger dispatch, history")
|
|
(td (~tw :tokens "px-3 py-2 font-mono text-sm text-stone-600") "engine, boot (partial)")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") "~5KB"))
|
|
(tr (~tw :tokens "border-b border-stone-100")
|
|
(td (~tw :tokens "px-3 py-2 font-semibold text-stone-800") "L1 DOM Ops")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") "L0 + toggle!, set-attr!, on-event, class-list ops")
|
|
(td (~tw :tokens "px-3 py-2 font-mono text-sm text-stone-600") "+ DOM adapter (partial)")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") "~8KB"))
|
|
(tr (~tw :tokens "border-b border-stone-100")
|
|
(td (~tw :tokens "px-3 py-2 font-semibold text-stone-800") "L2 Islands")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") "L1 + signals, computed, effect, defisland hydration")
|
|
(td (~tw :tokens "px-3 py-2 font-mono text-sm text-stone-600") "+ signals, DOM adapter (full)")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") "~15KB"))
|
|
(tr (~tw :tokens "border-b border-stone-100")
|
|
(td (~tw :tokens "px-3 py-2 font-semibold text-stone-800") "L3 Full Eval")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") "L2 + parser, evaluator, all primitives, client routing, component resolution")
|
|
(td (~tw :tokens "px-3 py-2 font-mono text-sm text-stone-600") "everything")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") "~44KB (current)")))))
|
|
|
|
(p "90% of typical pages are L0. A blog post, a product listing, a checkout form — server renders, client morphs on navigation. The full evaluator only loads for pages that do client-side rendering or have reactive islands.")
|
|
|
|
(div (~tw :tokens "rounded border border-amber-200 bg-amber-50 p-4 mt-4")
|
|
(p (~tw :tokens "text-amber-900 font-medium") "Progressive loading")
|
|
(p (~tw :tokens "text-amber-800") "A user navigating an L0 page downloads ~5KB. If they navigate to an L2 page (reactive island), the delta (~10KB) loads on demand. The runtime grows with need, never speculatively.")))
|
|
|
|
;; -----------------------------------------------------------------------
|
|
;; The slicer is SX
|
|
;; -----------------------------------------------------------------------
|
|
|
|
(~docs/section :title "The Slicer is SX" :id "slicer-is-sx"
|
|
(p "Per the " (a :href "/sx/(etc.(plan.self-hosting-bootstrapper))" (~tw :tokens "text-violet-700 underline") "self-hosting principle") ", the slicer is not a build tool — it's a spec module. " (code "slice.sx") " analyzes the spec's own dependency graph and determines which defines belong to which tier.")
|
|
(p (code "js.sx") " (the self-hosting SX-to-JavaScript translator) already compiles the full spec. Slicing is a filter: " (code "js.sx") " translates only the defines that " (code "slice.sx") " selects for a given tier.")
|
|
|
|
(~docs/code :src (highlight ";; slice.sx — determine which defines each tier needs\n;;\n;; Input: the full list of defines from all spec files\n;; Output: a filtered list for the requested tier\n\n(define tier-deps\n ;; Which spec modules each tier requires\n {:L0 (list \"engine\" \"boot-partial\")\n :L1 (list \"engine\" \"boot-partial\" \"dom-partial\")\n :L2 (list \"engine\" \"boot-partial\" \"dom-partial\"\n \"signals\" \"dom-island\")\n :L3 (list \"eval\" \"render\" \"parser\"\n \"engine\" \"orchestration\" \"boot\"\n \"dom\" \"signals\" \"router\")})\n\n(define slice-defines\n (fn (tier all-defines)\n ;; 1. Get the module list for this tier\n ;; 2. Walk each define's dependency references\n ;; 3. Include a define only if ALL its deps are\n ;; satisfiable within the tier's module set\n ;; 4. Return the filtered define list\n (let ((modules (get tier-deps tier)))\n (filter\n (fn (d) (tier-satisfies? modules (define-deps d)))\n all-defines))))" "lisp"))
|
|
|
|
(p "The pipeline becomes:")
|
|
(ol (~tw :tokens "list-decimal pl-5 text-stone-700 space-y-2")
|
|
(li (code "slice.sx") " analyzes the spec and produces a define list per tier")
|
|
(li (code "js.sx") " translates each define list to JavaScript (same translator, different input)")
|
|
(li "The bootstrapper wraps each tier's output with its platform interface (the hand-written JS glue)")
|
|
(li "Output: one " (code ".js") " file per tier, or a single file with tier markers for runtime splitting"))
|
|
|
|
(p "Because " (code "js.sx") " is self-hosting, slicing is just function composition: " (code "(js-translate-file (slice-defines :L0 all-defines))") ". No new translator. No new build tool. The same 1,382-line " (code "js.sx") " that produces the full runtime produces every tier."))
|
|
|
|
;; -----------------------------------------------------------------------
|
|
;; Dependency analysis
|
|
;; -----------------------------------------------------------------------
|
|
|
|
(~docs/section :title "Define-Level Dependency Analysis" :id "deps"
|
|
(p "The slicer needs to know which defines reference which other defines. This is a simpler version of what " (code "deps.sx") " does for components — but at the define level, not the component level.")
|
|
|
|
(~docs/code :src (highlight ";; Walk a define's body AST, collect all symbol references\n;; that resolve to other top-level defines.\n;;\n;; (define morph-children\n;; (fn (old-node new-node)\n;; ...uses morph-node, morph-attrs, create-element...))\n;;\n;; → deps: #{morph-node morph-attrs create-element}\n\n(define define-refs\n (fn (body all-define-names)\n ;; Collect all symbols in body that appear in all-define-names\n (let ((refs (make-set)))\n (walk-ast body\n (fn (node)\n (when (and (symbol? node)\n (set-has? all-define-names (symbol-name node)))\n (set-add! refs (symbol-name node)))))\n refs)))" "lisp"))
|
|
|
|
(p "From these per-define deps, we build the full dependency graph and compute transitive closures. A tier's define set is the transitive closure of its entry points:")
|
|
|
|
(div (~tw :tokens "overflow-x-auto rounded border border-stone-200 mb-4")
|
|
(table (~tw :tokens "w-full text-left text-sm")
|
|
(thead (tr (~tw :tokens "border-b border-stone-200 bg-stone-100")
|
|
(th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "Tier")
|
|
(th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "Entry points")
|
|
(th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "Pulls in")))
|
|
(tbody
|
|
(tr (~tw :tokens "border-b border-stone-100")
|
|
(td (~tw :tokens "px-3 py-2 font-semibold text-stone-800") "L0")
|
|
(td (~tw :tokens "px-3 py-2 font-mono text-sm text-stone-600") "morph-node, process-swap, dispatch-trigger, push-url")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") "morph-attrs, morph-children, create-element, extract-swap-config — the morph/swap subgraph"))
|
|
(tr (~tw :tokens "border-b border-stone-100")
|
|
(td (~tw :tokens "px-3 py-2 font-semibold text-stone-800") "L1")
|
|
(td (~tw :tokens "px-3 py-2 font-mono text-sm text-stone-600") "L0 + toggle-class!, set-attr!, add-event-listener!")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") "DOM manipulation helpers — small subgraph"))
|
|
(tr (~tw :tokens "border-b border-stone-100")
|
|
(td (~tw :tokens "px-3 py-2 font-semibold text-stone-800") "L2")
|
|
(td (~tw :tokens "px-3 py-2 font-mono text-sm text-stone-600") "L1 + signal, deref, reset!, computed, effect, render-dom-island")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") "Signal runtime + reactive DOM adapter"))
|
|
(tr (~tw :tokens "border-b border-stone-100")
|
|
(td (~tw :tokens "px-3 py-2 font-semibold text-stone-800") "L3")
|
|
(td (~tw :tokens "px-3 py-2 font-mono text-sm text-stone-600") "L2 + eval-expr, sx-parse, render-to-dom, resolve-component-by-cid")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") "Everything — full evaluator, parser, all ~80 primitives"))))))
|
|
|
|
;; -----------------------------------------------------------------------
|
|
;; Platform interface slicing
|
|
;; -----------------------------------------------------------------------
|
|
|
|
(~docs/section :title "Platform Interface Slicing" :id "platform"
|
|
(p "The bootstrapped code (from js.sx) is only half the story. Each define also depends on platform primitives — the hand-written JS glue that js.sx doesn't produce. These live in " (code "bootstrap_js.py") "'s PLATFORM_* blocks.")
|
|
(p "The slicer must track platform deps too:")
|
|
|
|
(~docs/code :src (highlight ";; Platform primitives referenced by tier\n;;\n;; L0 needs: createElement, setAttribute, morphAttrs,\n;; fetch (for sx-get/post), pushState, replaceState\n;;\n;; L1 adds: classList.toggle, addEventListener\n;;\n;; L2 adds: createComment, createTextNode, domRemove,\n;; domChildNodes — the reactive DOM primitives\n;;\n;; L3 adds: everything in PLATFORM_PARSER_JS,\n;; full DOM adapter, async IO bridge\n\n(define platform-deps\n {:L0 (list :dom-morph :fetch :history)\n :L1 (list :dom-morph :fetch :history :dom-events)\n :L2 (list :dom-morph :fetch :history :dom-events\n :dom-reactive :signal-constructors)\n :L3 (list :dom-morph :fetch :history :dom-events\n :dom-reactive :signal-constructors\n :parser :evaluator :async-io)})" "lisp"))
|
|
|
|
(p "The platform JS blocks in " (code "bootstrap_js.py") " are already organized by adapter (" (code "PLATFORM_DOM_JS") ", " (code "PLATFORM_ENGINE_PURE_JS") ", etc). Slicing further subdivides these into the minimal set each tier needs.")
|
|
(p "This subdivision also happens in SX: " (code "slice.sx") " declares which platform blocks each tier requires. " (code "js.sx") " doesn't need to change — it translates defines. The bootstrapper script reads the slice spec and assembles the platform preamble accordingly."))
|
|
|
|
;; -----------------------------------------------------------------------
|
|
;; Progressive loading
|
|
;; -----------------------------------------------------------------------
|
|
|
|
(~docs/section :title "Progressive Loading" :id "progressive"
|
|
(p "The simplest approach: one file per tier. The server knows each page's tier (from " (code "defpage") " metadata or component analysis) and serves the right script tag.")
|
|
(p "Better: a base file (L0) that all pages load, plus tier deltas loaded on demand.")
|
|
|
|
(~docs/code :src (highlight ";; Server emits the appropriate script for the page's tier\n;;\n;; L0 page (blog post, product listing):\n;; <script src=\"/static/scripts/sx-L0.js\"></script>\n;;\n;; L2 page (reactive island):\n;; <script src=\"/static/scripts/sx-L0.js\"></script>\n;; <script src=\"/static/scripts/sx-L2-delta.js\"></script>\n;;\n;; Client-side navigation from L0 → L2:\n;; 1. L0 runtime handles the swap\n;; 2. New page declares tier=L2 in response header\n;; 3. L0 runtime loads sx-L2-delta.js dynamically\n;; 4. Island hydration proceeds\n\n(define page-tier\n (fn (page)\n ;; Analyze the page's component tree\n ;; If any component is defisland → L2\n ;; If any component uses on-event/toggle! → L1\n ;; Otherwise → L0\n (cond\n ((page-has-islands? page) :L2)\n ((page-has-dom-ops? page) :L1)\n (true :L0))))" "lisp"))
|
|
|
|
(~docs/subsection :title "SX-Tier Response Header"
|
|
(p "The server includes the page's tier in the response:")
|
|
(~docs/code :src (highlight "HTTP/1.1 200 OK\nSX-Tier: L0\nSX-Components: ~card:bafy...,~plans/environment-images/nav:bafy...\n\n;; or for an island page:\nSX-Tier: L2\nSX-Components: ~counter-island:bafy..." "http"))
|
|
(p "On client-side navigation, the engine reads " (code "SX-Tier") " from the response. If the new page requires a higher tier than currently loaded, it fetches the delta script before processing the swap. The delta script registers its additional primitives and the swap proceeds."))
|
|
|
|
(~docs/subsection :title "Cache Behavior"
|
|
(p "Each tier file is content-hashed (like the current " (code "sx_js_hash") " mechanism). Cache-forever semantics. A user who visits any L0 page caches the L0 runtime permanently. If they later visit an L2 page, only the ~10KB delta downloads.")
|
|
(p "Combined with " (a :href "/sx/(etc.(plan.environment-images))" (~tw :tokens "text-violet-700 underline") "environment images") ": the image CID includes the tier. An L0 image is smaller than an L3 image — it contains fewer primitives, no parser state, no evaluator. The standalone HTML bundle for an L0 page is tiny.")))
|
|
|
|
;; -----------------------------------------------------------------------
|
|
;; Automatic tier detection
|
|
;; -----------------------------------------------------------------------
|
|
|
|
(~docs/section :title "Automatic Tier Detection" :id "auto-detect"
|
|
(p (code "deps.sx") " already classifies components as pure or IO-dependent. Extend it to classify pages by tier:")
|
|
|
|
(~docs/code :src (highlight ";; Extend deps.sx with tier analysis\n;;\n;; Walk the page's component tree:\n;; - Any defisland → L2 minimum\n;; - Any on-event, toggle!, set-attr! call → L1 minimum \n;; - Any client-eval'd component (SX wire + defcomp) → L3\n;; - Otherwise → L0\n;;\n;; The tier is the MAX of all components' requirements.\n\n(define component-tier\n (fn (comp)\n (cond\n ((island? comp) :L2)\n ((has-dom-ops? (component-body comp)) :L1)\n (true :L0))))\n\n(define page-tier\n (fn (page-def)\n (let ((comp-tiers (map component-tier\n (page-all-components page-def))))\n (max-tier comp-tiers))))" "lisp"))
|
|
|
|
(p "This runs at registration time (same phase as " (code "compute_all_deps") "). Each " (code "PageDef") " gains a " (code "tier") " field. The server uses it to select the script tag. No manual annotation needed — the tier is derived from what the page actually uses."))
|
|
|
|
;; -----------------------------------------------------------------------
|
|
;; What L0 actually needs
|
|
;; -----------------------------------------------------------------------
|
|
|
|
(~docs/section :title "What L0 Actually Needs" :id "l0-detail"
|
|
(p "L0 is the critical tier — it's what most pages load. Every byte matters. Let's be precise about what it contains:")
|
|
|
|
(div (~tw :tokens "overflow-x-auto rounded border border-stone-200 mb-4")
|
|
(table (~tw :tokens "w-full text-left text-sm")
|
|
(thead (tr (~tw :tokens "border-b border-stone-200 bg-stone-100")
|
|
(th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "Function")
|
|
(th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "Purpose")
|
|
(th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "Source")))
|
|
(tbody
|
|
(tr (~tw :tokens "border-b border-stone-100")
|
|
(td (~tw :tokens "px-3 py-2 font-mono text-sm text-stone-700") "morph-node")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") "DOM diffing — update existing DOM from new HTML")
|
|
(td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "engine.sx"))
|
|
(tr (~tw :tokens "border-b border-stone-100")
|
|
(td (~tw :tokens "px-3 py-2 font-mono text-sm text-stone-700") "morph-attrs")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") "Attribute diffing on a single element")
|
|
(td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "engine.sx"))
|
|
(tr (~tw :tokens "border-b border-stone-100")
|
|
(td (~tw :tokens "px-3 py-2 font-mono text-sm text-stone-700") "morph-children")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") "Child node reconciliation")
|
|
(td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "engine.sx"))
|
|
(tr (~tw :tokens "border-b border-stone-100")
|
|
(td (~tw :tokens "px-3 py-2 font-mono text-sm text-stone-700") "process-swap")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") "Apply sx-swap directive (innerHTML, outerHTML, etc)")
|
|
(td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "engine.sx"))
|
|
(tr (~tw :tokens "border-b border-stone-100")
|
|
(td (~tw :tokens "px-3 py-2 font-mono text-sm text-stone-700") "dispatch-trigger")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") "Process sx-trigger attributes (click, submit, load, etc)")
|
|
(td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "engine.sx"))
|
|
(tr (~tw :tokens "border-b border-stone-100")
|
|
(td (~tw :tokens "px-3 py-2 font-mono text-sm text-stone-700") "sx-fetch")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") "Make sx-get/sx-post requests, process response")
|
|
(td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "orchestration.sx"))
|
|
(tr (~tw :tokens "border-b border-stone-100")
|
|
(td (~tw :tokens "px-3 py-2 font-mono text-sm text-stone-700") "push-url / replace-url")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") "History management for sx-push-url")
|
|
(td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "engine.sx"))
|
|
(tr (~tw :tokens "border-b border-stone-100")
|
|
(td (~tw :tokens "px-3 py-2 font-mono text-sm text-stone-700") "boot-triggers")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") "Scan DOM for sx-* attributes, wire up event listeners")
|
|
(td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "boot.sx"))
|
|
(tr (~tw :tokens "border-b border-stone-100")
|
|
(td (~tw :tokens "px-3 py-2 font-mono text-sm text-stone-700") "resolve-suspense")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") "Fill in streamed suspense slots")
|
|
(td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "boot.sx")))))
|
|
|
|
(p "That's roughly 20-30 defines from engine.sx + orchestration.sx + boot.sx, plus their transitive deps. No parser, no evaluator, no primitives beyond what those defines call internally. The platform JS is just: fetch wrapper, DOM helpers (createElement, setAttribute, morphing), history API, and event delegation.")
|
|
|
|
(p "Target: " (strong "~5KB min+gz") " — competitive with htmx (10KB) while being semantically richer (morph-based, not innerHTML-based)."))
|
|
|
|
;; -----------------------------------------------------------------------
|
|
;; Build pipeline
|
|
;; -----------------------------------------------------------------------
|
|
|
|
(~docs/section :title "Build Pipeline" :id "pipeline"
|
|
(p "The pipeline uses the same tools that already exist — " (code "js.sx") " for translation, " (code "bootstrap_js.py") " for platform assembly — but feeds them filtered define lists.")
|
|
|
|
(~docs/code :src (highlight ";; Build all tiers\n;;\n;; 1. Load all spec .sx files\n;; 2. Extract all defines (same as current bootstrap)\n;; 3. Run slice.sx to partition defines by tier\n;; 4. For each tier:\n;; a. js.sx translates the tier's define list\n;; b. Platform assembler wraps with minimal platform JS\n;; c. Output: sx-L{n}.js\n;; 5. Compute deltas: L1-delta = L1 - L0, L2-delta = L2 - L1, etc.\n\n;; The bootstrapper script orchestrates this:\n;;\n;; python bootstrap_js.py --tier L0 -o sx-L0.js\n;; python bootstrap_js.py --tier L1 --delta -o sx-L1-delta.js\n;; python bootstrap_js.py --tier L2 --delta -o sx-L2-delta.js\n;; python bootstrap_js.py -o sx-browser.js # full (L3, backward compat)" "lisp"))
|
|
|
|
(p "The " (code "--delta") " flag emits only the defines not present in the previous tier. The delta file calls " (code "Sx.extend()") " to register its additions into the already-loaded runtime.")
|
|
|
|
(div (~tw :tokens "rounded border border-violet-200 bg-violet-50 p-4 mt-4")
|
|
(p (~tw :tokens "text-violet-900 font-medium") "Self-hosting all the way")
|
|
(p (~tw :tokens "text-violet-800") (code "slice.sx") " is spec. " (code "js.sx") " is spec. The bootstrapper script (" (code "bootstrap_js.py") ") is the thin host-specific glue that reads slice output, calls js.sx via the evaluator, and wraps with platform JS. The slicer could itself be bootstrapped to JavaScript and run in a browser build tool — but that's a future concern.")))
|
|
|
|
;; -----------------------------------------------------------------------
|
|
;; Spec modules
|
|
;; -----------------------------------------------------------------------
|
|
|
|
(~docs/section :title "Spec Modules" :id "spec-modules"
|
|
(div (~tw :tokens "overflow-x-auto rounded border border-stone-200 mb-4")
|
|
(table (~tw :tokens "w-full text-left text-sm")
|
|
(thead (tr (~tw :tokens "border-b border-stone-200 bg-stone-100")
|
|
(th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "Module")
|
|
(th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "Functions")
|
|
(th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "Depends on")))
|
|
(tbody
|
|
(tr (~tw :tokens "border-b border-stone-100")
|
|
(td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "slice.sx")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") (code "define-refs") ", " (code "define-dep-graph") ", " (code "slice-defines") ", " (code "tier-entry-points") ", " (code "page-tier") ", " (code "component-tier"))
|
|
(td (~tw :tokens "px-3 py-2 text-stone-600") "deps.sx (component analysis), eval.sx (AST walking)"))
|
|
(tr (~tw :tokens "border-b border-stone-100")
|
|
(td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "js.sx")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-700") (code "js-translate-file") " — already exists, unchanged")
|
|
(td (~tw :tokens "px-3 py-2 text-stone-600") "eval.sx (runs on evaluator)")))))
|
|
|
|
(p "One new spec file (" (code "slice.sx") "), one existing translator (" (code "js.sx") "), one modified host script (" (code "bootstrap_js.py") " gains " (code "--tier") " and " (code "--delta") " flags)."))
|
|
|
|
;; -----------------------------------------------------------------------
|
|
;; Relationships
|
|
;; -----------------------------------------------------------------------
|
|
|
|
(~docs/section :title "Relationships" :id "relationships"
|
|
(ul (~tw :tokens "list-disc pl-5 text-stone-700 space-y-1")
|
|
(li (a :href "/sx/(etc.(plan.environment-images))" (~tw :tokens "text-violet-700 underline") "Environment Images") " — tiered images are smaller. An L0 image omits the parser, evaluator, and most primitives.")
|
|
(li (a :href "/sx/(etc.(plan.content-addressed-components))" (~tw :tokens "text-violet-700 underline") "Content-Addressed Components") " — component CID resolution is L3-only. L0 pages don't resolve components client-side.")
|
|
(li (a :href "/sx/(geography.(reactive.(reactive-design)))" (~tw :tokens "text-violet-700 underline") "Reactive Islands") " — L2 tier is defined by island presence. The signal runtime is the L1→L2 delta.")
|
|
(li (a :href "/sx/(etc.(plan.isomorphic-architecture))" (~tw :tokens "text-violet-700 underline") "Isomorphic Architecture") " — client-side page rendering is L3. Most pages don't need it."))
|
|
|
|
(div (~tw :tokens "rounded border border-amber-200 bg-amber-50 p-3 mt-2")
|
|
(p (~tw :tokens "text-amber-800 text-sm") (strong "Depends on: ") (code "js.sx") " (complete), " (code "deps.sx") " (complete), " (code "bootstrap_js.py") " adapter selection (exists). " (strong "New: ") (code "slice.sx") " spec module.")))))
|