# sexpr.js — Development Plan **An isomorphic S-expression runtime for the web.** **Code is data is DOM.** --- ## Vision Replace HTML as the document/wire format with S-expressions. A single JavaScript library runs on both server (Node/Deno/Bun) and client (browser). The server composes and sends S-expressions over HTTP or WebSocket. The client parses them and renders/mutates the DOM. Because S-expressions are homoiconic, hypermedia controls (fetching, swapping, transitions) are native to the format — not bolted on as special attributes. The framework is not a Lisp. It is a document runtime that happens to use S-expression syntax because that syntax makes documents and commands interchangeable. --- ## Architecture Overview ``` ┌─────────────────────────────────────────────────────┐ │ sexpr.js (shared) │ │ │ │ ┌───────────┐ ┌───────────┐ ┌────────────────┐ │ │ │ Parser / │ │ Component │ │ Mutation │ │ │ │ Serializer │ │ Registry │ │ Engine │ │ │ └───────────┘ └───────────┘ └────────────────┘ │ │ ┌───────────┐ ┌───────────┐ ┌────────────────┐ │ │ │ Style │ │ Event │ │ VTree Diff │ │ │ │ Compiler │ │ System │ │ & Patch │ │ │ └───────────┘ └───────────┘ └────────────────┘ │ └─────────────┬───────────────────────────┬───────────┘ │ │ ┌────────▼────────┐ ┌─────────▼─────────┐ │ Server Adapter │ │ Client Adapter │ │ │ │ │ │ • renderToStr() │ │ • renderToDOM() │ │ • diff on AST │ │ • mount/hydrate │ │ • HTTP handler │ │ • WebSocket recv │ │ • WS push │ │ • event dispatch │ │ • SSR bootstrap │ │ • service worker │ └──────────────────┘ └────────────────────┘ ``` The core is environment-agnostic. Thin adapters provide DOM APIs (client) or string serialization (server). Both sides share the parser, component system, style compiler, and mutation engine. --- ## Phase 1: Core Runtime (Weeks 1–4) The foundation. A single ES module that works in any JS environment. ### 1.1 Parser & Serializer **Parser** — tokenizer + recursive descent, producing an AST of plain JS objects. - Atoms: strings (`"hello"`), numbers (`42`, `3.14`), booleans (`#t`, `#f`), symbols (`div`, `my-component`), keywords (`:class`, `:on-click`) - Lists: `(tag :attr "val" children...)` - Comments: `; line comment` and `#| block comment |#` - Quasiquote / unquote: `` ` `` and `,` for template interpolation on the server - Streaming parser variant for large documents (SAX-style) **Serializer** — AST back to S-expression string. Round-trip fidelity. Pretty-printer with configurable indentation. **Deliverables:** - `parse(string) → AST` - `serialize(AST) → string` - `prettyPrint(AST, opts) → string` - Streaming: `createParser()` returning a push-based parser - Comprehensive test suite (edge cases: nested strings, escapes, unicode, deeply nested structures) - Benchmark: parse speed vs JSON.parse for equivalent data ### 1.2 AST Representation The AST should be cheap to construct, diff, and serialize. Plain objects, not classes: ```javascript // Atoms { type: 'symbol', value: 'div' } { type: 'keyword', value: 'class' } { type: 'string', value: 'hello' } { type: 'number', value: 42 } { type: 'boolean', value: true } // List (the fundamental structure) [head, ...rest] // plain arrays — cheap, diffable, JSON-compatible // Element sugar (derived during render, not stored) // (div :class "box" (p "hi")) → // [sym('div'), kw('class'), str('box'), [sym('p'), str('hi')]] ``` **Design decision:** ASTs are plain arrays and objects. No custom classes. This means they serialize to JSON trivially — enabling WebSocket transmission, IndexedDB caching, and worker postMessage without structured clone overhead. ### 1.3 Element Rendering The core render function: AST → target output. **Shared logic** (environment-agnostic): - Parse keyword attributes from element expressions - Resolve component references - Evaluate special forms (`if`, `each`, `list`, `let`, `slot`) - Compile inline styles **Client adapter** — `renderToDOM(ast, env) → Node`: - Creates real DOM nodes via `document.createElement` - Handles SVG namespace detection - Registers event handlers - Returns live DOM node **Server adapter** — `renderToString(ast, env) → string`: - Produces HTML string for initial page load (SEO, fast first paint) - Inserts hydration markers so the client can attach without full re-render - Escapes text content for safety ### 1.4 Style System Styles as S-expressions, compiled to CSS strings. Isomorphic: the same style expressions produce CSS on server (injected into `