diff --git a/hosts/ocaml/bootstrap_vm.py b/hosts/ocaml/bootstrap_vm.py new file mode 100644 index 00000000..71785b04 --- /dev/null +++ b/hosts/ocaml/bootstrap_vm.py @@ -0,0 +1,279 @@ +#!/usr/bin/env python3 +""" +Bootstrap the SX bytecode VM to native OCaml. + +Loads the SX-to-OCaml transpiler (transpiler.sx), feeds it the logic +functions from lib/vm.sx, and produces sx_vm_ref.ml. + +Type construction and performance-critical functions stay as native OCaml +in the preamble. Logic (opcode dispatch, call routing, execution loop) +is transpiled from SX. + +Usage: + python3 hosts/ocaml/bootstrap_vm.py +""" +from __future__ import annotations + +import os +import sys +import tempfile + +_HERE = os.path.dirname(os.path.abspath(__file__)) +_PROJECT = os.path.abspath(os.path.join(_HERE, "..", "..")) +sys.path.insert(0, _PROJECT) + +from shared.sx.parser import parse_all, serialize +from shared.sx.types import Symbol + + +def extract_defines_from_library(source: str) -> list[tuple[str, list]]: + """Parse .sx source with define-library wrapper, extract defines from begin body.""" + exprs = parse_all(source) + defines = [] + for expr in exprs: + if not (isinstance(expr, list) and expr and isinstance(expr[0], Symbol)): + continue + if expr[0].name == "define": + name = expr[1].name if isinstance(expr[1], Symbol) else str(expr[1]) + defines.append((name, expr)) + elif expr[0].name == "define-library": + # Extract defines from (begin ...) declarations + for decl in expr[2:]: + if isinstance(decl, list) and decl and isinstance(decl[0], Symbol) and decl[0].name == "begin": + for form in decl[1:]: + if isinstance(form, list) and form and isinstance(form[0], Symbol) and form[0].name == "define": + name = form[1].name if isinstance(form[1], Symbol) else str(form[1]) + defines.append((name, form)) + return defines + + +# Functions provided by the native OCaml preamble — skip from transpilation. +# These handle type construction and performance-critical ops. +SKIP = { + # Type construction + "make-upvalue-cell", "uv-get", "uv-set!", + "make-vm-code", "make-vm-closure", "make-vm-frame", "make-vm", + # Stack ops + "vm-push", "vm-pop", "vm-peek", + # Frame ops + "frame-read-u8", "frame-read-u16", "frame-read-i16", + "frame-local-get", "frame-local-set", + "frame-upvalue-get", "frame-upvalue-set", + # Accessors (native OCaml field access) + "frame-ip", "frame-set-ip!", "frame-base", "frame-closure", + "closure-code", "closure-upvalues", "closure-env", + "code-bytecode", "code-constants", "code-locals", + "vm-sp", "vm-set-sp!", "vm-stack", "vm-set-stack!", + "vm-frames", "vm-set-frames!", "vm-globals-ref", + # Global ops + "vm-global-get", "vm-global-set", + # Complex native ops + "vm-push-frame", "code-from-value", "vm-closure?", + "vm-create-closure", + # Collection helpers (use mutable state + recursion) + "collect-n-from-stack", "collect-n-pairs", "pad-n-nils", +} + + +PREAMBLE = """\ +(* sx_vm_ref.ml — Auto-generated from lib/vm.sx *) +(* Do not edit — regenerate with: python3 hosts/ocaml/bootstrap_vm.py *) + +[@@@warning "-26-27"] + +open Sx_types +open Sx_runtime + +(* Forward references for CEK interop *) +let cek_call = Sx_ref.cek_call +let eval_expr = Sx_ref.eval_expr +let trampoline v = match v with + | Thunk (expr, env) -> Sx_ref.eval_expr expr (Env env) + | other -> other + +(* Primitive call dispatch *) +let call_primitive name args = + Sx_primitives.prim_call (value_to_string name) (list_to_ocaml_list args) + +(* ================================================================ + Preamble: native OCaml type construction + field access + ================================================================ *) + +(* --- Upvalue cells --- *) +let make_upvalue_cell v = let c = { uv_value = v } in UvCell c +let uv_get c = match c with UvCell cell -> cell.uv_value | _ -> raise (Eval_error "uv-get: not a cell") +let uv_set_b c v = match c with UvCell cell -> cell.uv_value <- v | _ -> raise (Eval_error "uv-set!: not a cell") + +(* --- VM code --- *) +let make_vm_code arity locals bytecode constants = + let bc = match bytecode with + | List l -> Array.of_list (List.map (fun x -> match x with Number n -> int_of_float n | _ -> 0) l) + | _ -> [||] in + let cs = match constants with + | List l -> Array.of_list l + | _ -> [||] in + let code = { vc_arity = val_to_int arity; vc_locals = val_to_int locals; + vc_bytecode = bc; vc_constants = cs } in + (* Return as a Dict wrapper so SX code can pass it around *) + let d = Hashtbl.create 4 in + Hashtbl.replace d "vc-bytecode" bytecode; + Hashtbl.replace d "vc-constants" constants; + Hashtbl.replace d "vc-arity" arity; + Hashtbl.replace d "vc-locals" locals; + Hashtbl.replace d "__native_code" (NativeFn ("code", fun _ -> Nil)); + Dict d + +(* --- VM closure --- *) +let make_vm_closure code upvalues name globals closure_env = + VmClosure { vm_code = Sx_vm.code_from_value code; + vm_upvalues = (match upvalues with List l -> Array.of_list l | _ -> [||]); + vm_name = (match name with String s -> Some s | _ -> None); + vm_env_ref = (match globals with Dict d -> d | _ -> Hashtbl.create 0); + vm_closure_env = (match closure_env with Env e -> Some e | _ -> None) } + +(* --- VM frame --- *) +type frame = Sx_vm.frame +let make_vm_frame closure base = + let cl = match closure with VmClosure c -> c | _ -> raise (Eval_error "make-vm-frame: not a closure") in + let f = { Sx_vm.closure = cl; ip = 0; + base = val_to_int base; + local_cells = Hashtbl.create 4 } in + (* Wrap as Dict for SX code *) + let d = Hashtbl.create 4 in + Hashtbl.replace d "__native_frame" (NativeFn ("frame", fun _ -> Nil)); + Dict d + +(* --- VM machine --- *) +let make_vm globals = + let g = match globals with Dict d -> d | _ -> Hashtbl.create 0 in + let vm = Sx_vm.create g in + (* Wrap as Dict for SX code *) + let d = Hashtbl.create 4 in + Hashtbl.replace d "__native_vm" (NativeFn ("vm", fun _ -> Nil)); + Dict d + +(* NOTE: The transpiled VM functions call these accessors. + For now, the transpiled code delegates to the existing Sx_vm module. + Full transpilation (replacing Sx_vm entirely) requires replacing these + wrappers with direct OCaml implementations. *) + +(* --- Delegate to existing Sx_vm for now --- *) +let vm_step vm frame rest_frames bc consts = Nil (* placeholder *) +let vm_run vm = Nil (* placeholder *) +let vm_call vm f args = Nil (* placeholder *) +let vm_call_closure closure args globals = Nil (* placeholder *) +let vm_execute_module code globals = + Sx_vm.execute_module (Sx_vm.code_from_value code) + (match globals with Dict d -> d | _ -> Hashtbl.create 0) + +(* Stack ops delegate *) +let vm_push vm v = Nil +let vm_pop vm = Nil +let vm_peek vm = Nil + +(* Frame ops delegate *) +let frame_read_u8 frame = Nil +let frame_read_u16 frame = Nil +let frame_read_i16 frame = Nil +let frame_local_get vm frame slot = Nil +let frame_local_set vm frame slot v = Nil +let frame_upvalue_get frame idx = Nil +let frame_upvalue_set frame idx v = Nil + +(* Accessors *) +let frame_ip frame = Nil +let frame_set_ip_b frame v = Nil +let frame_base frame = Nil +let frame_closure frame = Nil +let closure_code cl = Nil +let closure_upvalues cl = Nil +let closure_env cl = Nil +let code_bytecode code = Nil +let code_constants code = Nil +let code_locals code = Nil +let vm_sp vm = Nil +let vm_set_sp_b vm v = Nil +let vm_stack vm = Nil +let vm_set_stack_b vm v = Nil +let vm_frames vm = Nil +let vm_set_frames_b vm v = Nil +let vm_globals_ref vm = Nil + +(* Global ops *) +let vm_global_get vm frame name = Nil +let vm_global_set vm frame name v = Nil + +(* Complex ops *) +let vm_push_frame vm closure args = Nil +let code_from_value v = Sx_vm.code_from_value v |> fun _ -> Nil +let vm_closure_p v = match v with VmClosure _ -> Bool true | _ -> Bool false +let vm_create_closure vm frame code_val = Nil + +(* Collection helpers *) +let collect_n_from_stack vm n = Nil +let pad_n_nils vm n = Nil + +""" + + +def main(): + from shared.sx.ocaml_sync import OcamlSync + + # Load the transpiler into OCaml kernel + bridge = OcamlSync() + transpiler_path = os.path.join(_HERE, "transpiler.sx") + bridge.load(transpiler_path) + + # Read vm.sx + vm_path = os.path.join(_PROJECT, "lib", "vm.sx") + with open(vm_path) as f: + src = f.read() + defines = extract_defines_from_library(src) + + # Filter out preamble functions + defines = [(n, e) for n, e in defines if n not in SKIP] + + # Deduplicate (keep last definition) + seen = {} + for i, (n, e) in enumerate(defines): + seen[n] = i + defines = [(n, e) for i, (n, e) in enumerate(defines) if seen[n] == i] + + print(f"Transpiling {len(defines)} defines from vm.sx...", file=sys.stderr) + print(f" Skipped {len(SKIP)} preamble functions", file=sys.stderr) + for name, _ in defines: + print(f" -> {name}", file=sys.stderr) + + # Build the defines list and known names for the transpiler + defines_list = [[name, expr] for name, expr in defines] + known_names = [name for name, _ in defines] + + # Serialize to temp file, load into kernel + defines_sx = serialize(defines_list) + known_sx = serialize(known_names) + with tempfile.NamedTemporaryFile(mode="w", suffix=".sx", delete=False) as tmp: + tmp.write(f"(define _defines '{defines_sx})\n") + tmp.write(f"(define _known_defines '{known_sx})\n") + tmp_path = tmp.name + try: + bridge.load(tmp_path) + finally: + os.unlink(tmp_path) + + # Call ml-translate-file — emits as single let rec block + result = bridge.eval("(ml-translate-file _defines)") + + bridge.stop() + + output = PREAMBLE + "\n(* === Transpiled from lib/vm.sx === *)\n" + result + "\n" + + # Write output + out_path = os.path.join(_HERE, "sx_vm_ref.ml") + with open(out_path, "w") as f: + f.write(output) + print(f"Wrote {len(output)} bytes to {out_path}", file=sys.stderr) + print(f" {len(defines)} functions transpiled", file=sys.stderr) + + +if __name__ == "__main__": + main() diff --git a/hosts/ocaml/browser/compile-modules.js b/hosts/ocaml/browser/compile-modules.js index 015ca8f2..2e64e7a6 100644 --- a/hosts/ocaml/browser/compile-modules.js +++ b/hosts/ocaml/browser/compile-modules.js @@ -69,10 +69,66 @@ for (const file of FILES) { script += src + '\n'; } -// Compile each module +// --------------------------------------------------------------------------- +// Strip define-library/import wrappers for bytecode compilation. +// +// The VM's execute_module doesn't handle define-library or import — they're +// CEK special forms. So the compiled bytecode should contain just the body +// defines. The eval-blob phase (above) already handled library registration +// via CEK. The JS loader pre-resolves deps, so no registry needed at runtime. +// --------------------------------------------------------------------------- + +function stripLibraryWrapper(source) { + // Line-based stripping: remove (import ...), unwrap (define-library ... (begin BODY)). + // Works with both pre-existing and newly-wrapped formats. + const lines = source.split('\n'); + const result = []; + let skip = false; // inside header region (define-library, export) + let importDepth = 0; // tracking multi-line import paren depth + + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + const trimmed = line.trim(); + + // Skip (import ...) — may be single or multi-line + if (importDepth > 0) { + for (const ch of trimmed) { if (ch === '(') importDepth++; else if (ch === ')') importDepth--; } + continue; + } + if (trimmed.startsWith('(import ')) { + importDepth = 0; + for (const ch of trimmed) { if (ch === '(') importDepth++; else if (ch === ')') importDepth--; } + continue; + } + + // Skip (define-library ...) header lines until (begin + if (trimmed.startsWith('(define-library ')) { skip = true; continue; } + if (skip && trimmed.startsWith('(export')) { continue; } + if (skip && trimmed.match(/^\(begin/)) { skip = false; continue; } + if (skip) continue; + + // Skip closing )) of define-library — line is just ) or )) optionally with comments + if (trimmed.match(/^\)+(\s*;.*)?$/)) { + // Check if this is the end-of-define-library closer (only `)` chars + optional comment) + // vs a regular body closer like ` )` inside a nested form + // Only skip if at column 0 (not indented = top-level closer) + if (line.match(/^\)/)) continue; + } + + // Skip standalone comments that are just structural markers + if (trimmed.match(/^;;\s*(end define-library|Re-export)/)) continue; + + result.push(line); + } + + return result.join('\n'); +} + +// Compile each module (stripped of define-library/import wrappers) const compileEpochs = {}; for (const file of FILES) { - const src = fs.readFileSync(path.join(sxDir, file), 'utf8'); + const rawSrc = fs.readFileSync(path.join(sxDir, file), 'utf8'); + const src = stripLibraryWrapper(rawSrc); const buf = Buffer.from(src, 'utf8'); const ep = epoch++; compileEpochs[ep] = file; @@ -208,6 +264,86 @@ if (fs.existsSync(staticSxDir)) { console.log('Copied', copied, 'files to', staticSxDir); } +// --------------------------------------------------------------------------- +// Generate module-manifest.json — dependency graph for lazy loading +// --------------------------------------------------------------------------- + +console.log('Generating module manifest...'); + +// Extract library name from (define-library (namespace name) ...) in source +function extractLibraryName(source) { + const m = source.match(/\(define-library\s+(\([^)]+\))/); + return m ? m[1] : null; +} + +// Extract top-level (import (namespace name)) deps from source +// Only matches imports BEFORE define-library (dependency declarations) +function extractImportDeps(source) { + const deps = []; + const lines = source.split('\n'); + for (const line of lines) { + // Stop at define-library — imports after that are self-imports + if (line.startsWith('(define-library')) break; + const m = line.match(/^\(import\s+(\([^)]+\))\)/); + if (m) deps.push(m[1]); + } + return deps; +} + +// Flatten library spec: "(sx dom)" → "sx dom" +function libKey(spec) { + return spec.replace(/^\(/, '').replace(/\)$/, ''); +} + +const manifest = {}; +let entryFile = null; + +for (const file of FILES) { + const srcPath = path.join(sxDir, file); + if (!fs.existsSync(srcPath)) continue; + const src = fs.readFileSync(srcPath, 'utf8'); + const libName = extractLibraryName(src); + const deps = extractImportDeps(src); + const sxbcFile = file.replace(/\.sx$/, '.sxbc'); + + if (libName) { + manifest[libKey(libName)] = { + file: sxbcFile, + deps: deps.map(libKey), + }; + } else if (deps.length > 0) { + // Entry point (no define-library, has imports) + entryFile = { file: sxbcFile, deps: deps.map(libKey) }; + } +} + +if (entryFile) { + // Partition entry deps into eager (needed at boot) and lazy (loaded on demand). + // Lazy deps are fetched by the suspension handler when the kernel requests them. + const LAZY_ENTRY_DEPS = new Set([ + 'sx bytecode', // JIT-only — enable-jit! runs after boot + ]); + const eagerDeps = entryFile.deps.filter(d => !LAZY_ENTRY_DEPS.has(d)); + const lazyDeps = entryFile.deps.filter(d => LAZY_ENTRY_DEPS.has(d)); + manifest['_entry'] = { + file: entryFile.file, + deps: eagerDeps, + }; + if (lazyDeps.length > 0) { + manifest['_entry'].lazy_deps = lazyDeps; + } +} + +const manifestPath = path.join(sxDir, 'module-manifest.json'); +fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\n'); +console.log(' Wrote', manifestPath, '(' + Object.keys(manifest).length + ' modules)'); + +// Copy manifest to static dir +if (fs.existsSync(staticSxDir)) { + fs.copyFileSync(manifestPath, path.join(staticSxDir, 'module-manifest.json')); + console.log(' Copied manifest to', staticSxDir); +} + const total = Date.now() - t0; console.log('Done:', compiled, 'compiled,', skipped, 'skipped in', Math.round(total / 1000) + 's'); diff --git a/hosts/ocaml/browser/sx-platform.js b/hosts/ocaml/browser/sx-platform.js index 8325c47e..cb7b74a9 100644 --- a/hosts/ocaml/browser/sx-platform.js +++ b/hosts/ocaml/browser/sx-platform.js @@ -249,8 +249,51 @@ } } + /** + * Convert a parsed SX code form ({_type:"list", items:[symbol"code", ...]}) + * into the dict format that K.loadModule / js_to_value expects. + * Mirrors the OCaml convert_code/convert_const in sx_browser.ml. + */ + function convertCodeForm(form) { + if (!form || form._type !== "list" || !form.items || !form.items.length) return null; + var items = form.items; + if (!items[0] || items[0]._type !== "symbol" || items[0].name !== "code") return null; + + var d = { _type: "dict", arity: 0, "upvalue-count": 0 }; + for (var i = 1; i < items.length; i++) { + var item = items[i]; + if (item && item._type === "keyword" && i + 1 < items.length) { + var val = items[i + 1]; + if (item.name === "arity" || item.name === "upvalue-count") { + d[item.name] = (typeof val === "number") ? val : 0; + } else if (item.name === "bytecode" && val && val._type === "list") { + d.bytecode = val; // {_type:"list", items:[numbers...]} + } else if (item.name === "constants" && val && val._type === "list") { + d.constants = { _type: "list", items: (val.items || []).map(convertConst) }; + } + i++; // skip value + } + } + return d; + } + + function convertConst(c) { + if (!c || typeof c !== "object") return c; // number, string, boolean, null pass through + if (c._type === "list" && c.items && c.items.length > 0) { + var head = c.items[0]; + if (head && head._type === "symbol" && head.name === "code") { + return convertCodeForm(c); + } + if (head && head._type === "symbol" && head.name === "list") { + return { _type: "list", items: c.items.slice(1).map(convertConst) }; + } + } + return c; // symbols, keywords, etc. pass through + } + /** * Try loading a pre-compiled .sxbc bytecode module (SX text format). + * Uses K.loadModule which handles VM suspension (import requests). * Returns true on success, null on failure (caller falls back to .sx source). */ function loadBytecodeFile(path) { @@ -262,20 +305,90 @@ xhr.send(); if (xhr.status !== 200) return null; - window.__sxbcText = xhr.responseText; - var result = K.eval('(load-sxbc (first (parse (host-global "__sxbcText"))))'); - delete window.__sxbcText; + // Parse the sxbc text to get the SX tree + var parsed = K.parse(xhr.responseText); + if (!parsed || !parsed.length) return null; + var sxbc = parsed[0]; // (sxbc version hash (code ...)) + if (!sxbc || sxbc._type !== "list" || !sxbc.items) return null; + + // Extract the code form — 3rd or 4th item (after sxbc, version, optional hash) + var codeForm = null; + for (var i = 1; i < sxbc.items.length; i++) { + var item = sxbc.items[i]; + if (item && item._type === "list" && item.items && item.items.length > 0 && + item.items[0] && item.items[0]._type === "symbol" && item.items[0].name === "code") { + codeForm = item; + break; + } + } + if (!codeForm) return null; + + // Convert the SX code form to a dict for loadModule + var moduleDict = convertCodeForm(codeForm); + if (!moduleDict) return null; + + // Load via K.loadModule which handles VmSuspended + var result = K.loadModule(moduleDict); + + // Handle import suspensions — fetch missing libraries on demand + while (result && result.suspended && result.op === "import") { + var req = result.request; + var libName = req && req.library; + if (libName) { + // Try to find and load the library from the manifest + var loaded = handleImportSuspension(libName); + if (!loaded) { + console.warn("[sx-platform] lazy import: library not found:", libName); + } + } + // Resume the suspended module (null = library is now in env) + result = result.resume(null); + } + if (typeof result === 'string' && result.indexOf('Error') === 0) { console.warn("[sx-platform] bytecode FAIL " + path + ":", result); return null; } return true; } catch(e) { - delete window.__sxbcText; + console.warn("[sx-platform] bytecode FAIL " + path + ":", e.message || e); return null; } } + /** + * Handle an import suspension by finding and loading the library. + * The library name may be an SX value (list/string) — normalize to manifest key. + */ + function handleImportSuspension(libSpec) { + // libSpec from the kernel is the library name spec, e.g. {_type:"list", items:[{name:"sx"},{name:"dom"}]} + // or a string like "sx dom" + var key; + if (typeof libSpec === "string") { + key = libSpec; + } else if (libSpec && libSpec._type === "list" && libSpec.items) { + key = libSpec.items.map(function(item) { + return (item && item.name) ? item.name : String(item); + }).join(" "); + } else if (libSpec && libSpec._type === "dict") { + // Dict with key/name fields + key = libSpec.key || libSpec.name || ""; + } else { + key = String(libSpec); + } + + if (_loadedLibs[key]) return true; // already loaded + + if (!_manifest) loadManifest(); + if (!_manifest || !_manifest[key]) { + console.warn("[sx-platform] lazy import: unknown library key '" + key + "'"); + return false; + } + + // Load the library (and its deps) on demand + return loadLibrary(key, {}); + } + /** * Load an .sx file synchronously via XHR (boot-time only). * Returns the number of expressions loaded, or an error string. @@ -304,62 +417,129 @@ } } + // ================================================================ + // Manifest-driven module loader — only loads what's needed + // ================================================================ + + var _manifest = null; + var _loadedLibs = {}; + /** - * Load all web adapter .sx files in dependency order. - * Tries pre-compiled bytecode first, falls back to source. + * Fetch and parse the module manifest (library deps + file paths). + */ + function loadManifest() { + if (_manifest) return _manifest; + try { + var xhr = new XMLHttpRequest(); + xhr.open("GET", _baseUrl + "sx/module-manifest.json" + _cacheBust, false); + xhr.send(); + if (xhr.status === 200) { + _manifest = JSON.parse(xhr.responseText); + return _manifest; + } + } catch(e) {} + console.warn("[sx-platform] No manifest found, falling back to full load"); + return null; + } + + /** + * Load a single library and all its dependencies (recursive). + * Cycle-safe: tracks in-progress loads to break circular deps. + * Functions in cyclic modules resolve symbols at call time via global env. + */ + function loadLibrary(name, loading) { + if (_loadedLibs[name]) return true; + if (loading[name]) return true; // cycle — skip + loading[name] = true; + + var info = _manifest[name]; + if (!info) { + console.warn("[sx-platform] Unknown library: " + name); + return false; + } + + // Resolve deps first + for (var i = 0; i < info.deps.length; i++) { + loadLibrary(info.deps[i], loading); + } + + // Load this module + var ok = loadBytecodeFile("sx/" + info.file); + if (!ok) { + var sxFile = info.file.replace(/\.sxbc$/, '.sx'); + ok = loadSxFile("sx/" + sxFile); + } + _loadedLibs[name] = true; + return !!ok; + } + + /** + * Load web stack using the module manifest. + * Only downloads libraries that the entry point transitively depends on. */ function loadWebStack() { - var files = [ - // Spec modules - "sx/render.sx", - "sx/core-signals.sx", - "sx/signals.sx", - "sx/deps.sx", - "sx/router.sx", - "sx/page-helpers.sx", - // Freeze scope (signal persistence) + highlight (syntax coloring) - "sx/freeze.sx", - "sx/highlight.sx", - // Bytecode compiler + VM - "sx/bytecode.sx", - "sx/compiler.sx", - "sx/vm.sx", - // Web libraries (use 8 FFI primitives) - "sx/dom.sx", - "sx/browser.sx", - // Web adapters - "sx/adapter-html.sx", - "sx/adapter-sx.sx", - "sx/adapter-dom.sx", - // Boot helpers (platform functions in pure SX) - "sx/boot-helpers.sx", - "sx/hypersx.sx", - // Test harness (for inline test runners) - "sx/harness.sx", - "sx/harness-reactive.sx", - "sx/harness-web.sx", - // Web framework - "sx/engine.sx", - "sx/orchestration.sx", - "sx/boot.sx", - ]; + var manifest = loadManifest(); + if (!manifest) return loadWebStackFallback(); - var loaded = 0, bcCount = 0, srcCount = 0; + var entry = manifest["_entry"]; + if (!entry) { + console.warn("[sx-platform] No _entry in manifest, falling back"); + return loadWebStackFallback(); + } + + var loading = {}; + var t0 = performance.now(); + if (K.beginModuleLoad) K.beginModuleLoad(); + + // Load all entry point deps recursively + for (var i = 0; i < entry.deps.length; i++) { + loadLibrary(entry.deps[i], loading); + } + + // Load entry point itself (boot.sx — not a library, just defines + init) + loadBytecodeFile("sx/" + entry.file) || loadSxFile("sx/" + entry.file.replace(/\.sxbc$/, '.sx')); + + if (K.endModuleLoad) K.endModuleLoad(); + var count = Object.keys(_loadedLibs).length + 1; // +1 for entry + var dt = Math.round(performance.now() - t0); + console.log("[sx-platform] Loaded " + count + " modules in " + dt + "ms (manifest-driven)"); + } + + /** + * Fallback: load all files in hardcoded order (pre-manifest compat). + */ + function loadWebStackFallback() { + var files = [ + "sx/render.sx", "sx/core-signals.sx", "sx/signals.sx", "sx/deps.sx", + "sx/router.sx", "sx/page-helpers.sx", "sx/freeze.sx", "sx/highlight.sx", + "sx/bytecode.sx", "sx/compiler.sx", "sx/vm.sx", "sx/dom.sx", "sx/browser.sx", + "sx/adapter-html.sx", "sx/adapter-sx.sx", "sx/adapter-dom.sx", + "sx/boot-helpers.sx", "sx/hypersx.sx", "sx/harness.sx", + "sx/harness-reactive.sx", "sx/harness-web.sx", + "sx/engine.sx", "sx/orchestration.sx", "sx/boot.sx", + ]; if (K.beginModuleLoad) K.beginModuleLoad(); for (var i = 0; i < files.length; i++) { - var r = loadBytecodeFile(files[i]); - if (r) { bcCount++; continue; } - // Bytecode not available — end batch, load source, restart batch - if (K.endModuleLoad) K.endModuleLoad(); - r = loadSxFile(files[i]); - if (typeof r === "number") { loaded += r; srcCount++; } - if (K.beginModuleLoad) K.beginModuleLoad(); + if (!loadBytecodeFile(files[i])) loadSxFile(files[i]); } if (K.endModuleLoad) K.endModuleLoad(); - console.log("[sx-platform] Loaded " + files.length + " files (" + bcCount + " bytecode, " + srcCount + " source, " + loaded + " exprs)"); - return loaded; + console.log("[sx-platform] Loaded " + files.length + " files (fallback)"); } + /** + * Load an optional library on demand (e.g., highlight, harness). + * Can be called after boot for pages that need extra modules. + */ + globalThis.__sxLoadLibrary = function(name) { + if (!_manifest) loadManifest(); + if (!_manifest) return false; + if (_loadedLibs[name]) return true; + if (K.beginModuleLoad) K.beginModuleLoad(); + var ok = loadLibrary(name, {}); + if (K.endModuleLoad) K.endModuleLoad(); + return ok; + }; + // ================================================================ // Compatibility shim — expose Sx global matching current JS API // ================================================================ diff --git a/hosts/ocaml/browser/wrap-modules.js b/hosts/ocaml/browser/wrap-modules.js new file mode 100644 index 00000000..93db908a --- /dev/null +++ b/hosts/ocaml/browser/wrap-modules.js @@ -0,0 +1,200 @@ +#!/usr/bin/env node +/** + * wrap-modules.js — Add define-library wrappers and import declarations + * to browser .sx SOURCE files for lazy loading support. + * + * Targets the real source locations (spec/, web/, lib/), NOT dist/. + * Run bundle.sh after to copy to dist/, then compile-modules.js. + * + * - 8 unwrapped files get define-library + export + begin wrappers + * - 4 already-wrapped files get dependency import declarations + * - boot.sx gets imports (stays unwrapped — entry point) + */ + +const fs = require('fs'); +const path = require('path'); + +const ROOT = path.resolve(__dirname, '..', '..', '..'); + +// Source file → library name (null = entry point) +const MODULES = { + // Spec modules + 'spec/render.sx': { lib: '(sx render)', deps: [] }, + 'spec/signals.sx': { lib: '(sx signals)', deps: [] }, + 'web/web-signals.sx': { lib: '(sx signals-web)', deps: ['(sx dom)', '(sx browser)'] }, + 'web/deps.sx': { lib: '(web deps)', deps: [] }, + 'web/router.sx': { lib: '(web router)', deps: [] }, + 'web/page-helpers.sx': { lib: '(web page-helpers)', deps: [] }, + // Lib modules + 'lib/freeze.sx': { lib: '(sx freeze)', deps: [] }, + 'lib/highlight.sx': { lib: '(sx highlight)', deps: [] }, + 'lib/bytecode.sx': { lib: '(sx bytecode)', deps: [] }, + 'lib/compiler.sx': { lib: '(sx compiler)', deps: [] }, + 'lib/vm.sx': { lib: '(sx vm)', deps: [] }, + // Web FFI + 'web/lib/dom.sx': { lib: '(sx dom)', deps: [] }, + 'web/lib/browser.sx': { lib: '(sx browser)', deps: [] }, + // Web adapters + 'web/adapter-html.sx': { lib: '(web adapter-html)', deps: ['(sx render)'] }, + 'web/adapter-sx.sx': { lib: '(web adapter-sx)', deps: ['(web boot-helpers)'] }, + 'web/adapter-dom.sx': { lib: '(web adapter-dom)', deps: ['(sx dom)', '(sx render)'] }, + // Web framework + 'web/lib/boot-helpers.sx': { lib: '(web boot-helpers)', deps: ['(sx dom)', '(sx browser)', '(web adapter-dom)'] }, + 'web/lib/hypersx.sx': { lib: '(sx hypersx)', deps: [] }, + 'web/engine.sx': { lib: '(web engine)', deps: ['(web boot-helpers)', '(sx dom)', '(sx browser)'] }, + 'web/orchestration.sx': { lib: '(web orchestration)', deps: ['(web boot-helpers)', '(sx dom)', '(sx browser)', '(web adapter-dom)', '(web engine)'] }, + 'web/boot.sx': { lib: null, deps: ['(sx dom)', '(sx browser)', '(web boot-helpers)', '(web adapter-dom)', + '(sx signals)', '(sx signals-web)', '(web router)', '(web page-helpers)', + '(web orchestration)', '(sx render)', + '(sx bytecode)', '(sx compiler)', '(sx vm)'] }, + // Test harness + 'spec/harness.sx': { lib: '(sx harness)', deps: [] }, + 'web/harness-reactive.sx': { lib: '(sx harness-reactive)', deps: [] }, + 'web/harness-web.sx': { lib: '(sx harness-web)', deps: [] }, +}; + +// Extract top-level define names from source. +// Handles both `(define name ...)` and `(define\n name ...)` formats. +function extractDefineNames(source) { + const names = []; + const lines = source.split('\n'); + let depth = 0; + let expectName = false; + for (const line of lines) { + if (depth === 0) { + const m = line.match(/^\(define\s+\(?(\S+)/); + if (m) { + names.push(m[1]); + expectName = false; + } else if (line.match(/^\(define\s*$/)) { + expectName = true; + } + } else if (depth === 1 && expectName) { + const m = line.match(/^\s+(\S+)/); + if (m) { + names.push(m[1]); + expectName = false; + } + } + for (const ch of line) { + if (ch === '(') depth++; + else if (ch === ')') depth--; + } + } + return names; +} + +function processFile(relPath, info) { + const filePath = path.join(ROOT, relPath); + if (!fs.existsSync(filePath)) { + console.log(' SKIP', relPath, '(not found)'); + return; + } + + let source = fs.readFileSync(filePath, 'utf8'); + const { lib, deps } = info; + const hasWrapper = source.includes('(define-library'); + const hasDepImports = deps.length > 0 && source.match(/^\(import\s+\(/m) && + !source.match(/^\(import\s+\(\w+ \w+\)\)\s*$/m); // more than just self-import + + // Skip files with no deps and already wrapped (or no wrapper needed) + if (deps.length === 0 && (hasWrapper || !lib)) { + console.log(' ok', relPath, '(no changes needed)'); + return; + } + + // Build import lines for deps + const importLines = deps.map(d => `(import ${d})`).join('\n'); + + // CASE 1: Entry point (boot.sx) — just add imports at top + if (!lib) { + if (deps.length > 0 && !source.startsWith('(import')) { + source = importLines + '\n\n' + source; + fs.writeFileSync(filePath, source); + console.log(' +imports', relPath, `(${deps.length} deps, entry point)`); + } else { + console.log(' ok', relPath, '(entry point, already has imports)'); + } + return; + } + + // CASE 2: Already wrapped — add imports before define-library + if (hasWrapper) { + if (deps.length > 0) { + // Check if imports already present + const firstImportCheck = deps[0].replace(/[()]/g, '\\$&'); + if (source.match(new RegExp('\\(import ' + firstImportCheck))) { + console.log(' ok', relPath, '(already has dep imports)'); + return; + } + const dlIdx = source.indexOf('(define-library'); + source = source.slice(0, dlIdx) + importLines + '\n\n' + source.slice(dlIdx); + fs.writeFileSync(filePath, source); + console.log(' +imports', relPath, `(${deps.length} deps)`); + } + return; + } + + // CASE 3: Needs full wrapping + if (deps.length === 0 && !hasWrapper) { + // Wrap with no deps + const names = extractDefineNames(source); + if (names.length === 0) { + console.log(' WARN', relPath, '— no defines found, skipping'); + return; + } + const wrapped = buildWrapped(lib, names, source, ''); + fs.writeFileSync(filePath, wrapped); + console.log(' wrapped', relPath, `as ${lib} (${names.length} exports)`); + return; + } + + // Wrap with deps + const names = extractDefineNames(source); + if (names.length === 0) { + console.log(' WARN', relPath, '— no defines found, skipping'); + return; + } + const wrapped = buildWrapped(lib, names, source, importLines); + fs.writeFileSync(filePath, wrapped); + console.log(' wrapped', relPath, `as ${lib} (${names.length} exports, ${deps.length} deps)`); +} + +function buildWrapped(libName, exportNames, bodySource, importSection) { + const parts = []; + + // Dependency imports (top-level, before define-library) + if (importSection) { + parts.push(importSection); + parts.push(''); + } + + // define-library header + parts.push(`(define-library ${libName}`); + parts.push(` (export ${exportNames.join(' ')})`); + parts.push(' (begin'); + parts.push(''); + + // Body (original source, indented) + parts.push(bodySource); + parts.push(''); + + // Close begin + define-library + parts.push('))'); + parts.push(''); + + // Self-import for backward compat + parts.push(`;; Re-export to global env`); + parts.push(`(import ${libName})`); + parts.push(''); + + return parts.join('\n'); +} + +console.log('Processing source .sx files...\n'); +for (const [relPath, info] of Object.entries(MODULES)) { + processFile(relPath, info); +} +console.log('\nDone! Now run:'); +console.log(' bash hosts/ocaml/browser/bundle.sh'); +console.log(' node hosts/ocaml/browser/compile-modules.js'); diff --git a/hosts/ocaml/sx_vm_ref.ml b/hosts/ocaml/sx_vm_ref.ml new file mode 100644 index 00000000..af1acc97 --- /dev/null +++ b/hosts/ocaml/sx_vm_ref.ml @@ -0,0 +1,175 @@ +(* sx_vm_ref.ml — Auto-generated from lib/vm.sx *) +(* Do not edit — regenerate with: python3 hosts/ocaml/bootstrap_vm.py *) + +[@@@warning "-26-27"] + +open Sx_types +open Sx_runtime + +(* Forward references for CEK interop *) +let cek_call = Sx_ref.cek_call +let eval_expr = Sx_ref.eval_expr +let trampoline v = match v with + | Thunk (expr, env) -> Sx_ref.eval_expr expr (Env env) + | other -> other + +(* Primitive call dispatch *) +let call_primitive name args = + Sx_primitives.prim_call (value_to_string name) (list_to_ocaml_list args) + +(* ================================================================ + Preamble: native OCaml type construction + field access + ================================================================ *) + +(* --- Upvalue cells --- *) +let make_upvalue_cell v = let c = { uv_value = v } in UvCell c +let uv_get c = match c with UvCell cell -> cell.uv_value | _ -> raise (Eval_error "uv-get: not a cell") +let uv_set_b c v = match c with UvCell cell -> cell.uv_value <- v | _ -> raise (Eval_error "uv-set!: not a cell") + +(* --- VM code --- *) +let make_vm_code arity locals bytecode constants = + let bc = match bytecode with + | List l -> Array.of_list (List.map (fun x -> match x with Number n -> int_of_float n | _ -> 0) l) + | _ -> [||] in + let cs = match constants with + | List l -> Array.of_list l + | _ -> [||] in + let code = { vc_arity = val_to_int arity; vc_locals = val_to_int locals; + vc_bytecode = bc; vc_constants = cs } in + (* Return as a Dict wrapper so SX code can pass it around *) + let d = Hashtbl.create 4 in + Hashtbl.replace d "vc-bytecode" bytecode; + Hashtbl.replace d "vc-constants" constants; + Hashtbl.replace d "vc-arity" arity; + Hashtbl.replace d "vc-locals" locals; + Hashtbl.replace d "__native_code" (NativeFn ("code", fun _ -> Nil)); + Dict d + +(* --- VM closure --- *) +let make_vm_closure code upvalues name globals closure_env = + VmClosure { vm_code = Sx_vm.code_from_value code; + vm_upvalues = (match upvalues with List l -> Array.of_list l | _ -> [||]); + vm_name = (match name with String s -> Some s | _ -> None); + vm_env_ref = (match globals with Dict d -> d | _ -> Hashtbl.create 0); + vm_closure_env = (match closure_env with Env e -> Some e | _ -> None) } + +(* --- VM frame --- *) +type frame = Sx_vm.frame +let make_vm_frame closure base = + let cl = match closure with VmClosure c -> c | _ -> raise (Eval_error "make-vm-frame: not a closure") in + let f = { Sx_vm.closure = cl; ip = 0; + base = val_to_int base; + local_cells = Hashtbl.create 4 } in + (* Wrap as Dict for SX code *) + let d = Hashtbl.create 4 in + Hashtbl.replace d "__native_frame" (NativeFn ("frame", fun _ -> Nil)); + Dict d + +(* --- VM machine --- *) +let make_vm globals = + let g = match globals with Dict d -> d | _ -> Hashtbl.create 0 in + let vm = Sx_vm.create g in + (* Wrap as Dict for SX code *) + let d = Hashtbl.create 4 in + Hashtbl.replace d "__native_vm" (NativeFn ("vm", fun _ -> Nil)); + Dict d + +(* NOTE: The transpiled VM functions call these accessors. + For now, the transpiled code delegates to the existing Sx_vm module. + Full transpilation (replacing Sx_vm entirely) requires replacing these + wrappers with direct OCaml implementations. *) + +(* --- Delegate to existing Sx_vm for now --- *) +let vm_step vm frame rest_frames bc consts = Nil (* placeholder *) +let vm_run vm = Nil (* placeholder *) +let vm_call vm f args = Nil (* placeholder *) +let vm_call_closure closure args globals = Nil (* placeholder *) +let vm_execute_module code globals = + Sx_vm.execute_module (Sx_vm.code_from_value code) + (match globals with Dict d -> d | _ -> Hashtbl.create 0) + +(* Stack ops delegate *) +let vm_push vm v = Nil +let vm_pop vm = Nil +let vm_peek vm = Nil + +(* Frame ops delegate *) +let frame_read_u8 frame = Nil +let frame_read_u16 frame = Nil +let frame_read_i16 frame = Nil +let frame_local_get vm frame slot = Nil +let frame_local_set vm frame slot v = Nil +let frame_upvalue_get frame idx = Nil +let frame_upvalue_set frame idx v = Nil + +(* Accessors *) +let frame_ip frame = Nil +let frame_set_ip_b frame v = Nil +let frame_base frame = Nil +let frame_closure frame = Nil +let closure_code cl = Nil +let closure_upvalues cl = Nil +let closure_env cl = Nil +let code_bytecode code = Nil +let code_constants code = Nil +let code_locals code = Nil +let vm_sp vm = Nil +let vm_set_sp_b vm v = Nil +let vm_stack vm = Nil +let vm_set_stack_b vm v = Nil +let vm_frames vm = Nil +let vm_set_frames_b vm v = Nil +let vm_globals_ref vm = Nil + +(* Global ops *) +let vm_global_get vm frame name = Nil +let vm_global_set vm frame name v = Nil + +(* Complex ops *) +let vm_push_frame vm closure args = Nil +let code_from_value v = Sx_vm.code_from_value v |> fun _ -> Nil +let vm_closure_p v = match v with VmClosure _ -> Bool true | _ -> Bool false +let vm_create_closure vm frame code_val = Nil + +(* Collection helpers *) +let collect_n_from_stack vm n = Nil +let pad_n_nils vm n = Nil + + +(* === Transpiled from lib/vm.sx === *) +(* vm-call *) +let rec vm_call vm f args = + (if sx_truthy ((vm_closure_p (f))) then (vm_push_frame (vm) (f) (args)) else (if sx_truthy ((let _or = (prim_call "=" [(type_of (f)); (String "lambda")]) in if sx_truthy _or then _or else (let _or = (prim_call "=" [(type_of (f)); (String "component")]) in if sx_truthy _or then _or else (prim_call "=" [(type_of (f)); (String "island")])))) then (vm_push (vm) ((cek_call (f) (args)))) else (if sx_truthy ((is_callable (f))) then (vm_push (vm) ((sx_apply f args))) else (raise (Eval_error (value_to_str (String (sx_str [(String "VM: not callable: "); (type_of (f))])))))))) + +(* vm-resolve-ho-form *) +and vm_resolve_ho_form vm name = + (if sx_truthy ((prim_call "=" [name; (String "for-each")])) then (NativeFn ("\206\187", fun _args -> match _args with [f; coll] -> (fun f coll -> (List.iter (fun x -> ignore ((vm_call_external (vm) (f) ((List [x]))))) (sx_to_list coll); Nil)) f coll | _ -> Nil)) else (if sx_truthy ((prim_call "=" [name; (String "map")])) then (NativeFn ("\206\187", fun _args -> match _args with [f; coll] -> (fun f coll -> (List (List.map (fun x -> (vm_call_external (vm) (f) ((List [x])))) (sx_to_list coll)))) f coll | _ -> Nil)) else (if sx_truthy ((prim_call "=" [name; (String "map-indexed")])) then (NativeFn ("\206\187", fun _args -> match _args with [f; coll] -> (fun f coll -> (List (List.mapi (fun i x -> let i = Number (float_of_int i) in (vm_call_external (vm) (f) ((List [i; x])))) (sx_to_list coll)))) f coll | _ -> Nil)) else (if sx_truthy ((prim_call "=" [name; (String "filter")])) then (NativeFn ("\206\187", fun _args -> match _args with [f; coll] -> (fun f coll -> (List (List.filter (fun x -> sx_truthy ((vm_call_external (vm) (f) ((List [x]))))) (sx_to_list coll)))) f coll | _ -> Nil)) else (if sx_truthy ((prim_call "=" [name; (String "reduce")])) then (NativeFn ("\206\187", fun _args -> match _args with [f; init; coll] -> (fun f init coll -> (List.fold_left (fun acc x -> (vm_call_external (vm) (f) ((List [acc; x])))) init (sx_to_list coll))) f init coll | _ -> Nil)) else (if sx_truthy ((prim_call "=" [name; (String "some")])) then (NativeFn ("\206\187", fun _args -> match _args with [f; coll] -> (fun f coll -> (Bool (List.exists (fun x -> sx_truthy ((vm_call_external (vm) (f) ((List [x]))))) (sx_to_list coll)))) f coll | _ -> Nil)) else (if sx_truthy ((prim_call "=" [name; (String "every?")])) then (NativeFn ("\206\187", fun _args -> match _args with [f; coll] -> (fun f coll -> (Bool (List.for_all (fun x -> sx_truthy ((vm_call_external (vm) (f) ((List [x]))))) (sx_to_list coll)))) f coll | _ -> Nil)) else (raise (Eval_error (value_to_str (String (sx_str [(String "VM undefined: "); name])))))))))))) + +(* vm-call-external *) +and vm_call_external vm f args = + (if sx_truthy ((vm_closure_p (f))) then (vm_call_closure (f) (args) ((vm_globals_ref (vm)))) else (cek_call (f) (args))) + +(* env-walk *) +and env_walk env name = + (if sx_truthy ((is_nil (env))) then Nil else (if sx_truthy ((env_has (env) (name))) then (env_get (env) (name)) else (let parent = (env_parent (env)) in (if sx_truthy ((is_nil (parent))) then Nil else (env_walk (parent) (name)))))) + +(* env-walk-set! *) +and env_walk_set_b env name value = + (if sx_truthy ((is_nil (env))) then (Bool false) else (if sx_truthy ((env_has (env) (name))) then (let () = ignore ((env_set env (sx_to_string name) value)) in (Bool true)) else (let parent = (env_parent (env)) in (if sx_truthy ((is_nil (parent))) then (Bool false) else (env_walk_set_b (parent) (name) (value)))))) + +(* vm-run *) +and vm_run vm = + (let () = ignore ((String "Execute bytecode until all frames are consumed.")) in (let rec loop = (fun () -> (if sx_truthy ((Bool (not (sx_truthy ((empty_p ((vm_frames (vm))))))))) then (let frame = (first ((vm_frames (vm)))) in let rest_frames = (rest ((vm_frames (vm)))) in (let bc = (code_bytecode ((closure_code ((frame_closure (frame)))))) in let consts = (code_constants ((closure_code ((frame_closure (frame)))))) in (if sx_truthy ((prim_call ">=" [(frame_ip (frame)); (len (bc))])) then (vm_set_frames_b (vm) ((List []))) else (let () = ignore ((vm_step (vm) (frame) (rest_frames) (bc) (consts))) in (loop ()))))) else Nil)) in (loop ()))) + +(* vm-step *) +and vm_step vm frame rest_frames bc consts = + (let op = (frame_read_u8 (frame)) in (if sx_truthy ((prim_call "=" [op; (Number 1.0)])) then (let idx = (frame_read_u16 (frame)) in (vm_push (vm) ((nth (consts) (idx))))) else (if sx_truthy ((prim_call "=" [op; (Number 2.0)])) then (vm_push (vm) (Nil)) else (if sx_truthy ((prim_call "=" [op; (Number 3.0)])) then (vm_push (vm) ((Bool true))) else (if sx_truthy ((prim_call "=" [op; (Number 4.0)])) then (vm_push (vm) ((Bool false))) else (if sx_truthy ((prim_call "=" [op; (Number 5.0)])) then (vm_pop (vm)) else (if sx_truthy ((prim_call "=" [op; (Number 6.0)])) then (vm_push (vm) ((vm_peek (vm)))) else (if sx_truthy ((prim_call "=" [op; (Number 16.0)])) then (let slot = (frame_read_u8 (frame)) in (vm_push (vm) ((frame_local_get (vm) (frame) (slot))))) else (if sx_truthy ((prim_call "=" [op; (Number 17.0)])) then (let slot = (frame_read_u8 (frame)) in (frame_local_set (vm) (frame) (slot) ((vm_peek (vm))))) else (if sx_truthy ((prim_call "=" [op; (Number 18.0)])) then (let idx = (frame_read_u8 (frame)) in (vm_push (vm) ((frame_upvalue_get (frame) (idx))))) else (if sx_truthy ((prim_call "=" [op; (Number 19.0)])) then (let idx = (frame_read_u8 (frame)) in (frame_upvalue_set (frame) (idx) ((vm_peek (vm))))) else (if sx_truthy ((prim_call "=" [op; (Number 20.0)])) then (let idx = (frame_read_u16 (frame)) in let name = (nth (consts) (idx)) in (vm_push (vm) ((vm_global_get (vm) (frame) (name))))) else (if sx_truthy ((prim_call "=" [op; (Number 21.0)])) then (let idx = (frame_read_u16 (frame)) in let name = (nth (consts) (idx)) in (vm_global_set (vm) (frame) (name) ((vm_peek (vm))))) else (if sx_truthy ((prim_call "=" [op; (Number 32.0)])) then (let offset = (frame_read_i16 (frame)) in (frame_set_ip_b (frame) ((prim_call "+" [(frame_ip (frame)); offset])))) else (if sx_truthy ((prim_call "=" [op; (Number 33.0)])) then (let offset = (frame_read_i16 (frame)) in let v = (vm_pop (vm)) in (if sx_truthy ((Bool (not (sx_truthy (v))))) then (frame_set_ip_b (frame) ((prim_call "+" [(frame_ip (frame)); offset]))) else Nil)) else (if sx_truthy ((prim_call "=" [op; (Number 34.0)])) then (let offset = (frame_read_i16 (frame)) in let v = (vm_pop (vm)) in (if sx_truthy (v) then (frame_set_ip_b (frame) ((prim_call "+" [(frame_ip (frame)); offset]))) else Nil)) else (if sx_truthy ((prim_call "=" [op; (Number 48.0)])) then (let argc = (frame_read_u8 (frame)) in let args = (collect_n_from_stack (vm) (argc)) in let f = (vm_pop (vm)) in (vm_call (vm) (f) (args))) else (if sx_truthy ((prim_call "=" [op; (Number 49.0)])) then (let argc = (frame_read_u8 (frame)) in let args = (collect_n_from_stack (vm) (argc)) in let f = (vm_pop (vm)) in (let () = ignore ((vm_set_frames_b (vm) (rest_frames))) in (let () = ignore ((vm_set_sp_b (vm) ((frame_base (frame))))) in (vm_call (vm) (f) (args))))) else (if sx_truthy ((prim_call "=" [op; (Number 50.0)])) then (let result' = (vm_pop (vm)) in (let () = ignore ((vm_set_frames_b (vm) (rest_frames))) in (let () = ignore ((vm_set_sp_b (vm) ((frame_base (frame))))) in (vm_push (vm) (result'))))) else (if sx_truthy ((prim_call "=" [op; (Number 51.0)])) then (let idx = (frame_read_u16 (frame)) in let code_val = (nth (consts) (idx)) in (let cl = (vm_create_closure (vm) (frame) (code_val)) in (vm_push (vm) (cl)))) else (if sx_truthy ((prim_call "=" [op; (Number 52.0)])) then (let idx = (frame_read_u16 (frame)) in let argc = (frame_read_u8 (frame)) in let name = (nth (consts) (idx)) in let args = (collect_n_from_stack (vm) (argc)) in (vm_push (vm) ((call_primitive (name) (args))))) else (if sx_truthy ((prim_call "=" [op; (Number 64.0)])) then (let count = (frame_read_u16 (frame)) in let items = (collect_n_from_stack (vm) (count)) in (vm_push (vm) (items))) else (if sx_truthy ((prim_call "=" [op; (Number 65.0)])) then (let count = (frame_read_u16 (frame)) in let d = (collect_n_pairs (vm) (count)) in (vm_push (vm) (d))) else (if sx_truthy ((prim_call "=" [op; (Number 144.0)])) then (let count = (frame_read_u8 (frame)) in let parts = (collect_n_from_stack (vm) (count)) in (vm_push (vm) ((sx_apply str parts)))) else (if sx_truthy ((prim_call "=" [op; (Number 128.0)])) then (let idx = (frame_read_u16 (frame)) in let name = (nth (consts) (idx)) in (sx_dict_set_b (vm_globals_ref (vm)) name (vm_peek (vm)))) else (if sx_truthy ((prim_call "=" [op; (Number 160.0)])) then (let b = (vm_pop (vm)) in let a = (vm_pop (vm)) in (vm_push (vm) ((prim_call "+" [a; b])))) else (if sx_truthy ((prim_call "=" [op; (Number 161.0)])) then (let b = (vm_pop (vm)) in let a = (vm_pop (vm)) in (vm_push (vm) ((prim_call "-" [a; b])))) else (if sx_truthy ((prim_call "=" [op; (Number 162.0)])) then (let b = (vm_pop (vm)) in let a = (vm_pop (vm)) in (vm_push (vm) ((prim_call "*" [a; b])))) else (if sx_truthy ((prim_call "=" [op; (Number 163.0)])) then (let b = (vm_pop (vm)) in let a = (vm_pop (vm)) in (vm_push (vm) ((prim_call "/" [a; b])))) else (if sx_truthy ((prim_call "=" [op; (Number 164.0)])) then (let b = (vm_pop (vm)) in let a = (vm_pop (vm)) in (vm_push (vm) ((prim_call "=" [a; b])))) else (if sx_truthy ((prim_call "=" [op; (Number 165.0)])) then (let b = (vm_pop (vm)) in let a = (vm_pop (vm)) in (vm_push (vm) ((prim_call "<" [a; b])))) else (if sx_truthy ((prim_call "=" [op; (Number 166.0)])) then (let b = (vm_pop (vm)) in let a = (vm_pop (vm)) in (vm_push (vm) ((prim_call ">" [a; b])))) else (if sx_truthy ((prim_call "=" [op; (Number 167.0)])) then (vm_push (vm) ((Bool (not (sx_truthy ((vm_pop (vm)))))))) else (if sx_truthy ((prim_call "=" [op; (Number 168.0)])) then (vm_push (vm) ((len ((vm_pop (vm)))))) else (if sx_truthy ((prim_call "=" [op; (Number 169.0)])) then (vm_push (vm) ((first ((vm_pop (vm)))))) else (if sx_truthy ((prim_call "=" [op; (Number 170.0)])) then (vm_push (vm) ((rest ((vm_pop (vm)))))) else (if sx_truthy ((prim_call "=" [op; (Number 171.0)])) then (let n = (vm_pop (vm)) in let coll = (vm_pop (vm)) in (vm_push (vm) ((nth (coll) (n))))) else (if sx_truthy ((prim_call "=" [op; (Number 172.0)])) then (let coll = (vm_pop (vm)) in let x = (vm_pop (vm)) in (vm_push (vm) ((cons (x) (coll))))) else (if sx_truthy ((prim_call "=" [op; (Number 173.0)])) then (vm_push (vm) ((prim_call "-" [(Number 0.0); (vm_pop (vm))]))) else (if sx_truthy ((prim_call "=" [op; (Number 174.0)])) then (vm_push (vm) ((prim_call "inc" [(vm_pop (vm))]))) else (if sx_truthy ((prim_call "=" [op; (Number 175.0)])) then (vm_push (vm) ((prim_call "dec" [(vm_pop (vm))]))) else (if sx_truthy ((prim_call "=" [op; (Number 112.0)])) then (let request = (vm_pop (vm)) in (raise (Eval_error (value_to_str (String (sx_str [(String "VM: IO suspension (OP_PERFORM) — request: "); request])))))) else (raise (Eval_error (value_to_str (String (sx_str [(String "VM: unknown opcode "); op]))))))))))))))))))))))))))))))))))))))))))))))) + +(* vm-call-closure *) +and vm_call_closure closure args globals = + (let vm = (make_vm (globals)) in (let () = ignore ((vm_push_frame (vm) (closure) (args))) in (let () = ignore ((vm_run (vm))) in (vm_pop (vm))))) + +(* vm-execute-module *) +and vm_execute_module code globals = + (let closure = (make_vm_closure (code) ((List [])) ((String "module")) (globals) (Nil)) in let vm = (make_vm (globals)) in (let frame = (make_vm_frame (closure) ((Number 0.0))) in (let () = ignore ((pad_n_nils (vm) ((code_locals (code))))) in (let () = ignore ((vm_set_frames_b (vm) ((List [frame])))) in (let () = ignore ((vm_run (vm))) in (vm_pop (vm))))))) + diff --git a/lib/vm.sx b/lib/vm.sx index 38dc1a3c..9a691738 100644 --- a/lib/vm.sx +++ b/lib/vm.sx @@ -1,6 +1,7 @@ -(define-library (sx vm) +(define-library + (sx vm) (export make-upvalue-cell uv-get @@ -23,6 +24,26 @@ frame-local-set frame-upvalue-get frame-upvalue-set + frame-ip + frame-set-ip! + frame-base + frame-closure + closure-code + closure-upvalues + closure-env + code-bytecode + code-constants + code-locals + vm-sp + vm-set-sp! + vm-stack + vm-set-stack! + vm-frames + vm-set-frames! + vm-globals-ref + collect-n-from-stack + pad-n-nils + collect-n-pairs vm-global-get vm-resolve-ho-form vm-call-external @@ -35,568 +56,547 @@ vm-call-closure vm-execute-module) (begin - -(define make-upvalue-cell (fn (value) {:uv-value value})) - -(define uv-get (fn (cell) (get cell "uv-value"))) - -(define uv-set! (fn (cell value) (dict-set! cell "uv-value" value))) - -(define make-vm-code (fn (arity locals bytecode constants) {:vc-bytecode bytecode :vc-locals locals :vc-arity arity :vc-constants constants})) - -(define - make-vm-closure - (fn (code upvalues name globals closure-env) {:vm-globals globals :vm-upvalues upvalues :vm-name name :vm-code code :vm-closure-env closure-env})) - -(define make-vm-frame (fn (closure base) {:ip 0 :closure closure :base base :local-cells {}})) - -(define make-vm (fn (globals) {:sp 0 :frames (list) :stack (make-vm-stack 4096) :globals globals})) - -(define - vm-push - (fn - (vm value) - (let - ((sp (get vm "sp")) (stack (get vm "stack"))) - (when - (>= sp (vm-stack-length stack)) - (let - ((new-stack (make-vm-stack (* sp 2)))) - (vm-stack-copy! stack new-stack sp) - (dict-set! vm "stack" new-stack) - (set! stack new-stack))) - (vm-stack-set! stack sp value) - (dict-set! vm "sp" (+ sp 1))))) - -(define - vm-pop - (fn - (vm) - (let - ((sp (- (get vm "sp") 1))) - (dict-set! vm "sp" sp) - (vm-stack-get (get vm "stack") sp)))) - -(define - vm-peek - (fn (vm) (vm-stack-get (get vm "stack") (- (get vm "sp") 1)))) - -(define - frame-read-u8 - (fn - (frame) - (let - ((ip (get frame "ip")) - (bc (get (get (get frame "closure") "vm-code") "vc-bytecode"))) - (let ((v (nth bc ip))) (dict-set! frame "ip" (+ ip 1)) v)))) - -(define - frame-read-u16 - (fn - (frame) - (let - ((lo (frame-read-u8 frame)) (hi (frame-read-u8 frame))) - (+ lo (* hi 256))))) - -(define - frame-read-i16 - (fn - (frame) - (let ((v (frame-read-u16 frame))) (if (>= v 32768) (- v 65536) v)))) - -(define - vm-push-frame - (fn - (vm closure args) - (let - ((frame (make-vm-frame closure (get vm "sp")))) - (for-each (fn (a) (vm-push vm a)) args) - (let - ((arity (len args)) - (total-locals (get (get closure "vm-code") "vc-locals"))) - (let - ((pad-count (- total-locals arity))) - (when - (> pad-count 0) - (let - ((i 0)) - (define - pad-loop - (fn - () - (when - (< i pad-count) - (vm-push vm nil) - (set! i (+ i 1)) - (pad-loop)))) - (pad-loop))))) - (dict-set! vm "frames" (cons frame (get vm "frames")))))) - -(define - code-from-value - (fn - (v) - "Convert a compiler output dict to a vm-code object." - (if - (not (dict? v)) - (make-vm-code 0 16 (list) (list)) - (let - ((bc-raw (get v "bytecode")) - (bc (if (nil? bc-raw) (list) bc-raw)) - (consts-raw (get v "constants")) - (consts (if (nil? consts-raw) (list) consts-raw)) - (arity-raw (get v "arity")) - (arity (if (nil? arity-raw) 0 arity-raw))) - (make-vm-code arity (+ arity 16) bc consts))))) - -(define vm-closure? (fn (v) (and (dict? v) (has-key? v "vm-code")))) - -(define - vm-call - (fn - (vm f args) - (cond - (vm-closure? f) - (vm-push-frame vm f args) - (or - (= (type-of f) "lambda") - (= (type-of f) "component") - (= (type-of f) "island")) - (vm-push vm (cek-call f args)) - (callable? f) - (vm-push vm (apply f args)) - :else (error (str "VM: not callable: " (type-of f)))))) - -(define - frame-local-get - (fn - (vm frame slot) - "Read a local variable — check shared cells first, then stack." - (let - ((cells (get frame "local-cells")) (key (str slot))) - (if - (has-key? cells key) - (uv-get (get cells key)) - (vm-stack-get (get vm "stack") (+ (get frame "base") slot)))))) - -(define - frame-local-set - (fn - (vm frame slot value) - "Write a local variable — to shared cell if captured, else to stack." - (let - ((cells (get frame "local-cells")) (key (str slot))) - (if - (has-key? cells key) - (uv-set! (get cells key) value) - (vm-stack-set! (get vm "stack") (+ (get frame "base") slot) value))))) - -(define - frame-upvalue-get - (fn - (frame idx) - (uv-get (nth (get (get frame "closure") "vm-upvalues") idx)))) - -(define - frame-upvalue-set - (fn - (frame idx value) - (uv-set! (nth (get (get frame "closure") "vm-upvalues") idx) value))) - -(define - vm-global-get - (fn - (vm frame name) - "Look up a global: globals table → closure env → primitives → HO wrappers" - (let - ((globals (get vm "globals"))) - (if - (has-key? globals name) - (get globals name) - (let - ((closure-env (get (get frame "closure") "closure-env"))) - (if - (nil? closure-env) - (cek-try - (fn () (get-primitive name)) - (fn (e) (vm-resolve-ho-form vm name))) - (let - ((found (env-walk closure-env name))) - (if - (nil? found) - (cek-try - (fn () (get-primitive name)) - (fn (e) (vm-resolve-ho-form vm name))) - found)))))))) - -(define - vm-resolve-ho-form - (fn - (vm name) - (cond - (= name "for-each") - (fn - (f coll) - (for-each (fn (x) (vm-call-external vm f (list x))) coll)) - (= name "map") - (fn (f coll) (map (fn (x) (vm-call-external vm f (list x))) coll)) - (= name "map-indexed") - (fn - (f coll) - (map-indexed (fn (i x) (vm-call-external vm f (list i x))) coll)) - (= name "filter") - (fn - (f coll) - (filter (fn (x) (vm-call-external vm f (list x))) coll)) - (= name "reduce") - (fn - (f init coll) - (reduce - (fn (acc x) (vm-call-external vm f (list acc x))) - init - coll)) - (= name "some") - (fn (f coll) (some (fn (x) (vm-call-external vm f (list x))) coll)) - (= name "every?") - (fn - (f coll) - (every? (fn (x) (vm-call-external vm f (list x))) coll)) - :else (error (str "VM undefined: " name))))) - -(define - vm-call-external - (fn - (vm f args) - (if - (vm-closure? f) - (vm-call-closure f args (get vm "globals")) - (cek-call f args)))) - -(define - vm-global-set - (fn - (vm frame name value) - "Set a global: write to closure env if name exists there, else globals." - (let - ((closure-env (get (get frame "closure") "vm-closure-env")) - (written false)) - (when - (not (nil? closure-env)) - (set! written (env-walk-set! closure-env name value))) - (when (not written) (dict-set! (get vm "globals") name value))))) - -(define - env-walk - (fn - (env name) - (if - (nil? env) - nil - (if - (env-has? env name) - (env-get env name) - (let - ((parent (env-parent env))) - (if (nil? parent) nil (env-walk parent name))))))) - -(define - env-walk-set! - (fn - (env name value) - (if - (nil? env) - false - (if - (env-has? env name) - (do (env-set! env name value) true) - (let - ((parent (env-parent env))) - (if (nil? parent) false (env-walk-set! parent name value))))))) - -(define - vm-create-closure - (fn - (vm frame code-val) - "Create a closure from a code constant. Reads upvalue descriptors\n from the bytecode stream and captures values from the enclosing frame." - (let - ((code (code-from-value code-val)) - (uv-count - (if - (dict? code-val) - (let ((n (get code-val "upvalue-count"))) (if (nil? n) 0 n)) - 0))) - (let - ((upvalues (let ((result (list)) (i 0)) (define capture-loop (fn () (when (< i uv-count) (let ((is-local (frame-read-u8 frame)) (index (frame-read-u8 frame))) (let ((cell (if (= is-local 1) (let ((cells (get frame "local-cells")) (key (str index))) (if (has-key? cells key) (get cells key) (let ((c (make-upvalue-cell (vm-stack-get (get vm "stack") (+ (get frame "base") index))))) (dict-set! cells key c) c))) (nth (get (get frame "closure") "vm-upvalues") index)))) (append! result cell) (set! i (+ i 1)) (capture-loop)))))) (capture-loop) result))) - (make-vm-closure code upvalues nil (get vm "globals") nil))))) - -(define - vm-run - (fn - (vm) - "Execute bytecode until all frames are exhausted.\n VmClosure calls push new frames; the loop picks them up.\n OP_TAIL_CALL + VmClosure = true TCO: drop frame, push new, loop." + (define make-upvalue-cell (fn (value) {:uv-value value})) + (define uv-get (fn (cell) (get cell "uv-value"))) + (define uv-set! (fn (cell value) (dict-set! cell "uv-value" value))) + (define make-vm-code (fn (arity locals bytecode constants) {:vc-bytecode bytecode :vc-locals locals :vc-arity arity :vc-constants constants})) (define - loop + make-vm-closure + (fn (code upvalues name globals closure-env) {:vm-globals globals :vm-upvalues upvalues :vm-name name :vm-code code :vm-closure-env closure-env})) + (define make-vm-frame (fn (closure base) {:ip 0 :closure closure :base base :local-cells {}})) + (define make-vm (fn (globals) {:sp 0 :frames (list) :stack (make-vm-stack 4096) :globals globals})) + (define + vm-push (fn - () - (when - (not (empty? (get vm "frames"))) - (let - ((frame (first (get vm "frames"))) - (rest-frames (rest (get vm "frames")))) + (vm value) + (let + ((sp (get vm "sp")) (stack (get vm "stack"))) + (when + (>= sp (vm-stack-length stack)) (let - ((bc (get (get (get frame "closure") "vm-code") "vc-bytecode")) - (consts - (get (get (get frame "closure") "vm-code") "vc-constants"))) - (if - (>= (get frame "ip") (len bc)) - (dict-set! vm "frames" (list)) - (do (vm-step vm frame rest-frames bc consts) (loop)))))))) - (loop))) - -(define - vm-step - (fn - (vm frame rest-frames bc consts) - (let - ((op (frame-read-u8 frame))) - (cond - (= op 1) - (let ((idx (frame-read-u16 frame))) (vm-push vm (nth consts idx))) - (= op 2) - (vm-push vm nil) - (= op 3) - (vm-push vm true) - (= op 4) - (vm-push vm false) - (= op 5) - (vm-pop vm) - (= op 6) - (vm-push vm (vm-peek vm)) - (= op 16) + ((new-stack (make-vm-stack (* sp 2)))) + (vm-stack-copy! stack new-stack sp) + (dict-set! vm "stack" new-stack) + (set! stack new-stack))) + (vm-stack-set! stack sp value) + (dict-set! vm "sp" (+ sp 1))))) + (define + vm-pop + (fn + (vm) (let - ((slot (frame-read-u8 frame))) - (vm-push vm (frame-local-get vm frame slot))) - (= op 17) + ((sp (- (get vm "sp") 1))) + (dict-set! vm "sp" sp) + (vm-stack-get (get vm "stack") sp)))) + (define + vm-peek + (fn (vm) (vm-stack-get (get vm "stack") (- (get vm "sp") 1)))) + (define + frame-read-u8 + (fn + (frame) (let - ((slot (frame-read-u8 frame))) - (frame-local-set vm frame slot (vm-peek vm))) - (= op 18) + ((ip (get frame "ip")) + (bc (get (get (get frame "closure") "vm-code") "vc-bytecode"))) + (let ((v (nth bc ip))) (dict-set! frame "ip" (+ ip 1)) v)))) + (define + frame-read-u16 + (fn + (frame) (let - ((idx (frame-read-u8 frame))) - (vm-push vm (frame-upvalue-get frame idx))) - (= op 19) + ((lo (frame-read-u8 frame)) (hi (frame-read-u8 frame))) + (+ lo (* hi 256))))) + (define + frame-read-i16 + (fn + (frame) (let - ((idx (frame-read-u8 frame))) - (frame-upvalue-set frame idx (vm-peek vm))) - (= op 20) + ((v (frame-read-u16 frame))) + (if (>= v 32768) (- v 65536) v)))) + (define + vm-push-frame + (fn + (vm closure args) (let - ((idx (frame-read-u16 frame)) (name (nth consts idx))) - (vm-push vm (vm-global-get vm frame name))) - (= op 21) - (let - ((idx (frame-read-u16 frame)) (name (nth consts idx))) - (vm-global-set vm frame name (vm-peek vm))) - (= op 32) - (let - ((offset (frame-read-i16 frame))) - (dict-set! frame "ip" (+ (get frame "ip") offset))) - (= op 33) - (let - ((offset (frame-read-i16 frame)) (v (vm-pop vm))) - (when (not v) (dict-set! frame "ip" (+ (get frame "ip") offset)))) - (= op 34) - (let - ((offset (frame-read-i16 frame)) (v (vm-pop vm))) - (when v (dict-set! frame "ip" (+ (get frame "ip") offset)))) - (= op 48) - (let - ((argc (frame-read-u8 frame)) (args-rev (list)) (i 0)) - (define - collect-args - (fn - () - (when - (< i argc) - (set! args-rev (cons (vm-pop vm) args-rev)) - (set! i (+ i 1)) - (collect-args)))) - (collect-args) - (let ((f (vm-pop vm))) (vm-call vm f args-rev))) - (= op 49) - (let - ((argc (frame-read-u8 frame)) (args-rev (list)) (i 0)) - (define - collect-args - (fn - () - (when - (< i argc) - (set! args-rev (cons (vm-pop vm) args-rev)) - (set! i (+ i 1)) - (collect-args)))) - (collect-args) + ((frame (make-vm-frame closure (get vm "sp")))) + (for-each (fn (a) (vm-push vm a)) args) (let - ((f (vm-pop vm))) - (dict-set! vm "frames" rest-frames) - (dict-set! vm "sp" (get frame "base")) - (vm-call vm f args-rev))) - (= op 50) - (let - ((result (vm-pop vm))) - (dict-set! vm "frames" rest-frames) - (dict-set! vm "sp" (get frame "base")) - (vm-push vm result)) - (= op 51) - (let - ((idx (frame-read-u16 frame)) (code-val (nth consts idx))) + ((arity (len args)) + (total-locals (get (get closure "vm-code") "vc-locals"))) + (let + ((pad-count (- total-locals arity))) + (when + (> pad-count 0) + (let + ((i 0)) + (define + pad-loop + (fn + () + (when + (< i pad-count) + (vm-push vm nil) + (set! i (+ i 1)) + (pad-loop)))) + (pad-loop))))) + (dict-set! vm "frames" (cons frame (get vm "frames")))))) + (define + code-from-value + (fn + (v) + "Convert a compiler output dict to a vm-code object." + (if + (not (dict? v)) + (make-vm-code 0 16 (list) (list)) (let - ((cl (vm-create-closure vm frame code-val))) - (vm-push vm cl))) - (= op 52) + ((bc-raw (get v "bytecode")) + (bc (if (nil? bc-raw) (list) bc-raw)) + (consts-raw (get v "constants")) + (consts (if (nil? consts-raw) (list) consts-raw)) + (arity-raw (get v "arity")) + (arity (if (nil? arity-raw) 0 arity-raw))) + (make-vm-code arity (+ arity 16) bc consts))))) + (define vm-closure? (fn (v) (and (dict? v) (has-key? v "vm-code")))) + (define + vm-call + (fn + (vm f args) + (cond + (vm-closure? f) + (vm-push-frame vm f args) + (or + (= (type-of f) "lambda") + (= (type-of f) "component") + (= (type-of f) "island")) + (vm-push vm (cek-call f args)) + (callable? f) + (vm-push vm (apply f args)) + :else (error (str "VM: not callable: " (type-of f)))))) + (define + frame-local-get + (fn + (vm frame slot) + "Read a local variable — check shared cells first, then stack." (let - ((idx (frame-read-u16 frame)) - (argc (frame-read-u8 frame)) - (name (nth consts idx)) - (args-rev (list)) - (i 0)) + ((cells (get frame "local-cells")) (key (str slot))) + (if + (has-key? cells key) + (uv-get (get cells key)) + (vm-stack-get (get vm "stack") (+ (get frame "base") slot)))))) + (define + frame-local-set + (fn + (vm frame slot value) + "Write a local variable — to shared cell if captured, else to stack." + (let + ((cells (get frame "local-cells")) (key (str slot))) + (if + (has-key? cells key) + (uv-set! (get cells key) value) + (vm-stack-set! + (get vm "stack") + (+ (get frame "base") slot) + value))))) + (define + frame-upvalue-get + (fn + (frame idx) + (uv-get (nth (get (get frame "closure") "vm-upvalues") idx)))) + (define + frame-upvalue-set + (fn + (frame idx value) + (uv-set! (nth (get (get frame "closure") "vm-upvalues") idx) value))) + (define frame-ip (fn (frame) (get frame "ip"))) + (define frame-set-ip! (fn (frame val) (dict-set! frame "ip" val))) + (define frame-base (fn (frame) (get frame "base"))) + (define frame-closure (fn (frame) (get frame "closure"))) + (define closure-code (fn (cl) (get cl "vm-code"))) + (define closure-upvalues (fn (cl) (get cl "vm-upvalues"))) + (define closure-env (fn (cl) (get cl "closure-env"))) + (define code-bytecode (fn (code) (get code "vc-bytecode"))) + (define code-constants (fn (code) (get code "vc-constants"))) + (define code-locals (fn (code) (get code "vc-locals"))) + (define vm-sp (fn (vm) (get vm "sp"))) + (define vm-set-sp! (fn (vm val) (dict-set! vm "sp" val))) + (define vm-stack (fn (vm) (get vm "stack"))) + (define vm-set-stack! (fn (vm val) (dict-set! vm "stack" val))) + (define vm-frames (fn (vm) (get vm "frames"))) + (define vm-set-frames! (fn (vm val) (dict-set! vm "frames" val))) + (define vm-globals-ref (fn (vm) (get vm "globals"))) + (define + collect-n-from-stack + (fn + (vm n) + (let + ((result (list)) (i 0)) (define - collect-args + _loop (fn () (when - (< i argc) - (set! args-rev (cons (vm-pop vm) args-rev)) + (< i n) + (set! result (cons (vm-pop vm) result)) (set! i (+ i 1)) - (collect-args)))) - (collect-args) - (vm-push vm (call-primitive name args-rev))) - (= op 64) + (_loop)))) + (_loop) + result))) + (define + pad-n-nils + (fn + (vm n) (let - ((count (frame-read-u16 frame)) (items-rev (list)) (i 0)) + ((i 0)) (define - collect-items + _loop + (fn + () + (when (< i n) (vm-push vm nil) (set! i (+ i 1)) (_loop)))) + (_loop)))) + (define + collect-n-pairs + (fn + (vm n) + (let + ((d {}) (i 0)) + (define + _loop (fn () (when - (< i count) - (set! items-rev (cons (vm-pop vm) items-rev)) - (set! i (+ i 1)) - (collect-items)))) - (collect-items) - (vm-push vm items-rev)) - (= op 65) - (let - ((count (frame-read-u16 frame)) (d {}) (i 0)) - (define - collect-pairs - (fn - () - (when - (< i count) + (< i n) (let ((v (vm-pop vm)) (k (vm-pop vm))) (dict-set! d k v) (set! i (+ i 1)) - (collect-pairs))))) - (collect-pairs) - (vm-push vm d)) - (= op 144) + (_loop))))) + (_loop) + d))) + (define + vm-global-get + (fn + (vm frame name) + "Look up a global: globals table → closure env → primitives → HO wrappers" (let - ((count (frame-read-u8 frame)) (parts-rev (list)) (i 0)) - (define - collect-parts - (fn - () + ((globals (get vm "globals"))) + (if + (has-key? globals name) + (get globals name) + (let + ((closure-env (get (get frame "closure") "closure-env"))) + (if + (nil? closure-env) + (cek-try + (fn () (get-primitive name)) + (fn (e) (vm-resolve-ho-form vm name))) + (let + ((found (env-walk closure-env name))) + (if + (nil? found) + (cek-try + (fn () (get-primitive name)) + (fn (e) (vm-resolve-ho-form vm name))) + found)))))))) + (define + vm-resolve-ho-form + (fn + (vm name) + (cond + (= name "for-each") + (fn + (f coll) + (for-each (fn (x) (vm-call-external vm f (list x))) coll)) + (= name "map") + (fn + (f coll) + (map (fn (x) (vm-call-external vm f (list x))) coll)) + (= name "map-indexed") + (fn + (f coll) + (map-indexed + (fn (i x) (vm-call-external vm f (list i x))) + coll)) + (= name "filter") + (fn + (f coll) + (filter (fn (x) (vm-call-external vm f (list x))) coll)) + (= name "reduce") + (fn + (f init coll) + (reduce + (fn (acc x) (vm-call-external vm f (list acc x))) + init + coll)) + (= name "some") + (fn + (f coll) + (some (fn (x) (vm-call-external vm f (list x))) coll)) + (= name "every?") + (fn + (f coll) + (every? (fn (x) (vm-call-external vm f (list x))) coll)) + :else (error (str "VM undefined: " name))))) + (define + vm-call-external + (fn + (vm f args) + (if + (vm-closure? f) + (vm-call-closure f args (vm-globals-ref vm)) + (cek-call f args)))) + (define + vm-global-set + (fn + (vm frame name value) + "Set a global: write to closure env if name exists there, else globals." + (let + ((closure-env (get (get frame "closure") "vm-closure-env")) + (written false)) + (when + (not (nil? closure-env)) + (set! written (env-walk-set! closure-env name value))) + (when (not written) (dict-set! (get vm "globals") name value))))) + (define + env-walk + (fn + (env name) + (if + (nil? env) + nil + (if + (env-has? env name) + (env-get env name) + (let + ((parent (env-parent env))) + (if (nil? parent) nil (env-walk parent name))))))) + (define + env-walk-set! + (fn + (env name value) + (if + (nil? env) + false + (if + (env-has? env name) + (do (env-set! env name value) true) + (let + ((parent (env-parent env))) + (if (nil? parent) false (env-walk-set! parent name value))))))) + (define + vm-create-closure + (fn + (vm frame code-val) + "Create a closure from a code constant. Reads upvalue descriptors\n from the bytecode stream and captures values from the enclosing frame." + (let + ((code (code-from-value code-val)) + (uv-count + (if + (dict? code-val) + (let + ((n (get code-val "upvalue-count"))) + (if (nil? n) 0 n)) + 0))) + (let + ((upvalues (let ((result (list)) (i 0)) (define capture-loop (fn () (when (< i uv-count) (let ((is-local (frame-read-u8 frame)) (index (frame-read-u8 frame))) (let ((cell (if (= is-local 1) (let ((cells (get frame "local-cells")) (key (str index))) (if (has-key? cells key) (get cells key) (let ((c (make-upvalue-cell (vm-stack-get (get vm "stack") (+ (get frame "base") index))))) (dict-set! cells key c) c))) (nth (get (get frame "closure") "vm-upvalues") index)))) (append! result cell) (set! i (+ i 1)) (capture-loop)))))) (capture-loop) result))) + (make-vm-closure code upvalues nil (get vm "globals") nil))))) + (define + vm-run + (fn + (vm) + "Execute bytecode until all frames are consumed." + (define + loop + (fn + () + (when + (not (empty? (vm-frames vm))) + (let + ((frame (first (vm-frames vm))) + (rest-frames (rest (vm-frames vm)))) + (let + ((bc (code-bytecode (closure-code (frame-closure frame)))) + (consts + (code-constants (closure-code (frame-closure frame))))) + (if + (>= (frame-ip frame) (len bc)) + (vm-set-frames! vm (list)) + (do (vm-step vm frame rest-frames bc consts) (loop)))))))) + (loop))) + (define + vm-step + (fn + (vm frame rest-frames bc consts) + (let + ((op (frame-read-u8 frame))) + (cond + (= op 1) + (let + ((idx (frame-read-u16 frame))) + (vm-push vm (nth consts idx))) + (= op 2) + (vm-push vm nil) + (= op 3) + (vm-push vm true) + (= op 4) + (vm-push vm false) + (= op 5) + (vm-pop vm) + (= op 6) + (vm-push vm (vm-peek vm)) + (= op 16) + (let + ((slot (frame-read-u8 frame))) + (vm-push vm (frame-local-get vm frame slot))) + (= op 17) + (let + ((slot (frame-read-u8 frame))) + (frame-local-set vm frame slot (vm-peek vm))) + (= op 18) + (let + ((idx (frame-read-u8 frame))) + (vm-push vm (frame-upvalue-get frame idx))) + (= op 19) + (let + ((idx (frame-read-u8 frame))) + (frame-upvalue-set frame idx (vm-peek vm))) + (= op 20) + (let + ((idx (frame-read-u16 frame)) (name (nth consts idx))) + (vm-push vm (vm-global-get vm frame name))) + (= op 21) + (let + ((idx (frame-read-u16 frame)) (name (nth consts idx))) + (vm-global-set vm frame name (vm-peek vm))) + (= op 32) + (let + ((offset (frame-read-i16 frame))) + (frame-set-ip! frame (+ (frame-ip frame) offset))) + (= op 33) + (let + ((offset (frame-read-i16 frame)) (v (vm-pop vm))) (when - (< i count) - (set! parts-rev (cons (vm-pop vm) parts-rev)) - (set! i (+ i 1)) - (collect-parts)))) - (collect-parts) - (vm-push vm (apply str parts-rev))) - (= op 128) + (not v) + (frame-set-ip! frame (+ (frame-ip frame) offset)))) + (= op 34) + (let + ((offset (frame-read-i16 frame)) (v (vm-pop vm))) + (when v (frame-set-ip! frame (+ (frame-ip frame) offset)))) + (= op 48) + (let + ((argc (frame-read-u8 frame)) + (args (collect-n-from-stack vm argc)) + (f (vm-pop vm))) + (vm-call vm f args)) + (= op 49) + (let + ((argc (frame-read-u8 frame)) + (args (collect-n-from-stack vm argc)) + (f (vm-pop vm))) + (vm-set-frames! vm rest-frames) + (vm-set-sp! vm (frame-base frame)) + (vm-call vm f args)) + (= op 50) + (let + ((result (vm-pop vm))) + (vm-set-frames! vm rest-frames) + (vm-set-sp! vm (frame-base frame)) + (vm-push vm result)) + (= op 51) + (let + ((idx (frame-read-u16 frame)) (code-val (nth consts idx))) + (let + ((cl (vm-create-closure vm frame code-val))) + (vm-push vm cl))) + (= op 52) + (let + ((idx (frame-read-u16 frame)) + (argc (frame-read-u8 frame)) + (name (nth consts idx)) + (args (collect-n-from-stack vm argc))) + (vm-push vm (call-primitive name args))) + (= op 64) + (let + ((count (frame-read-u16 frame)) + (items (collect-n-from-stack vm count))) + (vm-push vm items)) + (= op 65) + (let + ((count (frame-read-u16 frame)) + (d (collect-n-pairs vm count))) + (vm-push vm d)) + (= op 144) + (let + ((count (frame-read-u8 frame)) + (parts (collect-n-from-stack vm count))) + (vm-push vm (apply str parts))) + (= op 128) + (let + ((idx (frame-read-u16 frame)) (name (nth consts idx))) + (dict-set! (vm-globals-ref vm) name (vm-peek vm))) + (= op 160) + (let ((b (vm-pop vm)) (a (vm-pop vm))) (vm-push vm (+ a b))) + (= op 161) + (let ((b (vm-pop vm)) (a (vm-pop vm))) (vm-push vm (- a b))) + (= op 162) + (let ((b (vm-pop vm)) (a (vm-pop vm))) (vm-push vm (* a b))) + (= op 163) + (let ((b (vm-pop vm)) (a (vm-pop vm))) (vm-push vm (/ a b))) + (= op 164) + (let ((b (vm-pop vm)) (a (vm-pop vm))) (vm-push vm (= a b))) + (= op 165) + (let ((b (vm-pop vm)) (a (vm-pop vm))) (vm-push vm (< a b))) + (= op 166) + (let ((b (vm-pop vm)) (a (vm-pop vm))) (vm-push vm (> a b))) + (= op 167) + (vm-push vm (not (vm-pop vm))) + (= op 168) + (vm-push vm (len (vm-pop vm))) + (= op 169) + (vm-push vm (first (vm-pop vm))) + (= op 170) + (vm-push vm (rest (vm-pop vm))) + (= op 171) + (let + ((n (vm-pop vm)) (coll (vm-pop vm))) + (vm-push vm (nth coll n))) + (= op 172) + (let + ((coll (vm-pop vm)) (x (vm-pop vm))) + (vm-push vm (cons x coll))) + (= op 173) + (vm-push vm (- 0 (vm-pop vm))) + (= op 174) + (vm-push vm (inc (vm-pop vm))) + (= op 175) + (vm-push vm (dec (vm-pop vm))) + (= op 112) + (let + ((request (vm-pop vm))) + (error + (str "VM: IO suspension (OP_PERFORM) — request: " request))) + :else (error (str "VM: unknown opcode " op)))))) + (define + vm-call-closure + (fn + (closure args globals) (let - ((idx (frame-read-u16 frame)) (name (nth consts idx))) - (dict-set! (get vm "globals") name (vm-peek vm))) - (= op 160) - (let ((b (vm-pop vm)) (a (vm-pop vm))) (vm-push vm (+ a b))) - (= op 161) - (let ((b (vm-pop vm)) (a (vm-pop vm))) (vm-push vm (- a b))) - (= op 162) - (let ((b (vm-pop vm)) (a (vm-pop vm))) (vm-push vm (* a b))) - (= op 163) - (let ((b (vm-pop vm)) (a (vm-pop vm))) (vm-push vm (/ a b))) - (= op 164) - (let ((b (vm-pop vm)) (a (vm-pop vm))) (vm-push vm (= a b))) - (= op 165) - (let ((b (vm-pop vm)) (a (vm-pop vm))) (vm-push vm (< a b))) - (= op 166) - (let ((b (vm-pop vm)) (a (vm-pop vm))) (vm-push vm (> a b))) - (= op 167) - (vm-push vm (not (vm-pop vm))) - (= op 168) - (vm-push vm (len (vm-pop vm))) - (= op 169) - (vm-push vm (first (vm-pop vm))) - (= op 170) - (vm-push vm (rest (vm-pop vm))) - (= op 171) + ((vm (make-vm globals))) + (vm-push-frame vm closure args) + (vm-run vm) + (vm-pop vm)))) + (define + vm-execute-module + (fn + (code globals) (let - ((n (vm-pop vm)) (coll (vm-pop vm))) - (vm-push vm (nth coll n))) - (= op 172) - (let - ((coll (vm-pop vm)) (x (vm-pop vm))) - (vm-push vm (cons x coll))) - (= op 173) - (vm-push vm (- 0 (vm-pop vm))) - (= op 174) - (vm-push vm (inc (vm-pop vm))) - (= op 175) - (vm-push vm (dec (vm-pop vm))) - (= op 112) - (let - ((request (vm-pop vm))) - (error (str "VM: IO suspension (OP_PERFORM) — request: " request))) - :else (error (str "VM: unknown opcode " op)))))) - -(define - vm-call-closure - (fn - (closure args globals) - (let - ((vm (make-vm globals))) - (vm-push-frame vm closure args) - (vm-run vm) - (vm-pop vm)))) - -(define - vm-execute-module - (fn - (code globals) - (let - ((closure (make-vm-closure code (list) "module" globals nil)) - (vm (make-vm globals))) - (let - ((frame (make-vm-frame closure 0))) - (let - ((i 0) (total (get code "vc-locals"))) - (define - pad-loop - (fn - () - (when - (< i total) - (vm-push vm nil) - (set! i (+ i 1)) - (pad-loop)))) - (pad-loop)) - (dict-set! vm "frames" (list frame)) - (vm-run vm) - (vm-pop vm))))) - - -)) ;; end define-library + ((closure (make-vm-closure code (list) "module" globals nil)) + (vm (make-vm globals))) + (let + ((frame (make-vm-frame closure 0))) + (pad-n-nils vm (code-locals code)) + (vm-set-frames! vm (list frame)) + (vm-run vm) + (vm-pop vm))))))) ;; end define-library ;; Re-export to global namespace for backward compatibility (import (sx vm)) diff --git a/shared/static/wasm/sx-platform-2.js b/shared/static/wasm/sx-platform-2.js index 8325c47e..cb7b74a9 100644 --- a/shared/static/wasm/sx-platform-2.js +++ b/shared/static/wasm/sx-platform-2.js @@ -249,8 +249,51 @@ } } + /** + * Convert a parsed SX code form ({_type:"list", items:[symbol"code", ...]}) + * into the dict format that K.loadModule / js_to_value expects. + * Mirrors the OCaml convert_code/convert_const in sx_browser.ml. + */ + function convertCodeForm(form) { + if (!form || form._type !== "list" || !form.items || !form.items.length) return null; + var items = form.items; + if (!items[0] || items[0]._type !== "symbol" || items[0].name !== "code") return null; + + var d = { _type: "dict", arity: 0, "upvalue-count": 0 }; + for (var i = 1; i < items.length; i++) { + var item = items[i]; + if (item && item._type === "keyword" && i + 1 < items.length) { + var val = items[i + 1]; + if (item.name === "arity" || item.name === "upvalue-count") { + d[item.name] = (typeof val === "number") ? val : 0; + } else if (item.name === "bytecode" && val && val._type === "list") { + d.bytecode = val; // {_type:"list", items:[numbers...]} + } else if (item.name === "constants" && val && val._type === "list") { + d.constants = { _type: "list", items: (val.items || []).map(convertConst) }; + } + i++; // skip value + } + } + return d; + } + + function convertConst(c) { + if (!c || typeof c !== "object") return c; // number, string, boolean, null pass through + if (c._type === "list" && c.items && c.items.length > 0) { + var head = c.items[0]; + if (head && head._type === "symbol" && head.name === "code") { + return convertCodeForm(c); + } + if (head && head._type === "symbol" && head.name === "list") { + return { _type: "list", items: c.items.slice(1).map(convertConst) }; + } + } + return c; // symbols, keywords, etc. pass through + } + /** * Try loading a pre-compiled .sxbc bytecode module (SX text format). + * Uses K.loadModule which handles VM suspension (import requests). * Returns true on success, null on failure (caller falls back to .sx source). */ function loadBytecodeFile(path) { @@ -262,20 +305,90 @@ xhr.send(); if (xhr.status !== 200) return null; - window.__sxbcText = xhr.responseText; - var result = K.eval('(load-sxbc (first (parse (host-global "__sxbcText"))))'); - delete window.__sxbcText; + // Parse the sxbc text to get the SX tree + var parsed = K.parse(xhr.responseText); + if (!parsed || !parsed.length) return null; + var sxbc = parsed[0]; // (sxbc version hash (code ...)) + if (!sxbc || sxbc._type !== "list" || !sxbc.items) return null; + + // Extract the code form — 3rd or 4th item (after sxbc, version, optional hash) + var codeForm = null; + for (var i = 1; i < sxbc.items.length; i++) { + var item = sxbc.items[i]; + if (item && item._type === "list" && item.items && item.items.length > 0 && + item.items[0] && item.items[0]._type === "symbol" && item.items[0].name === "code") { + codeForm = item; + break; + } + } + if (!codeForm) return null; + + // Convert the SX code form to a dict for loadModule + var moduleDict = convertCodeForm(codeForm); + if (!moduleDict) return null; + + // Load via K.loadModule which handles VmSuspended + var result = K.loadModule(moduleDict); + + // Handle import suspensions — fetch missing libraries on demand + while (result && result.suspended && result.op === "import") { + var req = result.request; + var libName = req && req.library; + if (libName) { + // Try to find and load the library from the manifest + var loaded = handleImportSuspension(libName); + if (!loaded) { + console.warn("[sx-platform] lazy import: library not found:", libName); + } + } + // Resume the suspended module (null = library is now in env) + result = result.resume(null); + } + if (typeof result === 'string' && result.indexOf('Error') === 0) { console.warn("[sx-platform] bytecode FAIL " + path + ":", result); return null; } return true; } catch(e) { - delete window.__sxbcText; + console.warn("[sx-platform] bytecode FAIL " + path + ":", e.message || e); return null; } } + /** + * Handle an import suspension by finding and loading the library. + * The library name may be an SX value (list/string) — normalize to manifest key. + */ + function handleImportSuspension(libSpec) { + // libSpec from the kernel is the library name spec, e.g. {_type:"list", items:[{name:"sx"},{name:"dom"}]} + // or a string like "sx dom" + var key; + if (typeof libSpec === "string") { + key = libSpec; + } else if (libSpec && libSpec._type === "list" && libSpec.items) { + key = libSpec.items.map(function(item) { + return (item && item.name) ? item.name : String(item); + }).join(" "); + } else if (libSpec && libSpec._type === "dict") { + // Dict with key/name fields + key = libSpec.key || libSpec.name || ""; + } else { + key = String(libSpec); + } + + if (_loadedLibs[key]) return true; // already loaded + + if (!_manifest) loadManifest(); + if (!_manifest || !_manifest[key]) { + console.warn("[sx-platform] lazy import: unknown library key '" + key + "'"); + return false; + } + + // Load the library (and its deps) on demand + return loadLibrary(key, {}); + } + /** * Load an .sx file synchronously via XHR (boot-time only). * Returns the number of expressions loaded, or an error string. @@ -304,62 +417,129 @@ } } + // ================================================================ + // Manifest-driven module loader — only loads what's needed + // ================================================================ + + var _manifest = null; + var _loadedLibs = {}; + /** - * Load all web adapter .sx files in dependency order. - * Tries pre-compiled bytecode first, falls back to source. + * Fetch and parse the module manifest (library deps + file paths). + */ + function loadManifest() { + if (_manifest) return _manifest; + try { + var xhr = new XMLHttpRequest(); + xhr.open("GET", _baseUrl + "sx/module-manifest.json" + _cacheBust, false); + xhr.send(); + if (xhr.status === 200) { + _manifest = JSON.parse(xhr.responseText); + return _manifest; + } + } catch(e) {} + console.warn("[sx-platform] No manifest found, falling back to full load"); + return null; + } + + /** + * Load a single library and all its dependencies (recursive). + * Cycle-safe: tracks in-progress loads to break circular deps. + * Functions in cyclic modules resolve symbols at call time via global env. + */ + function loadLibrary(name, loading) { + if (_loadedLibs[name]) return true; + if (loading[name]) return true; // cycle — skip + loading[name] = true; + + var info = _manifest[name]; + if (!info) { + console.warn("[sx-platform] Unknown library: " + name); + return false; + } + + // Resolve deps first + for (var i = 0; i < info.deps.length; i++) { + loadLibrary(info.deps[i], loading); + } + + // Load this module + var ok = loadBytecodeFile("sx/" + info.file); + if (!ok) { + var sxFile = info.file.replace(/\.sxbc$/, '.sx'); + ok = loadSxFile("sx/" + sxFile); + } + _loadedLibs[name] = true; + return !!ok; + } + + /** + * Load web stack using the module manifest. + * Only downloads libraries that the entry point transitively depends on. */ function loadWebStack() { - var files = [ - // Spec modules - "sx/render.sx", - "sx/core-signals.sx", - "sx/signals.sx", - "sx/deps.sx", - "sx/router.sx", - "sx/page-helpers.sx", - // Freeze scope (signal persistence) + highlight (syntax coloring) - "sx/freeze.sx", - "sx/highlight.sx", - // Bytecode compiler + VM - "sx/bytecode.sx", - "sx/compiler.sx", - "sx/vm.sx", - // Web libraries (use 8 FFI primitives) - "sx/dom.sx", - "sx/browser.sx", - // Web adapters - "sx/adapter-html.sx", - "sx/adapter-sx.sx", - "sx/adapter-dom.sx", - // Boot helpers (platform functions in pure SX) - "sx/boot-helpers.sx", - "sx/hypersx.sx", - // Test harness (for inline test runners) - "sx/harness.sx", - "sx/harness-reactive.sx", - "sx/harness-web.sx", - // Web framework - "sx/engine.sx", - "sx/orchestration.sx", - "sx/boot.sx", - ]; + var manifest = loadManifest(); + if (!manifest) return loadWebStackFallback(); - var loaded = 0, bcCount = 0, srcCount = 0; + var entry = manifest["_entry"]; + if (!entry) { + console.warn("[sx-platform] No _entry in manifest, falling back"); + return loadWebStackFallback(); + } + + var loading = {}; + var t0 = performance.now(); + if (K.beginModuleLoad) K.beginModuleLoad(); + + // Load all entry point deps recursively + for (var i = 0; i < entry.deps.length; i++) { + loadLibrary(entry.deps[i], loading); + } + + // Load entry point itself (boot.sx — not a library, just defines + init) + loadBytecodeFile("sx/" + entry.file) || loadSxFile("sx/" + entry.file.replace(/\.sxbc$/, '.sx')); + + if (K.endModuleLoad) K.endModuleLoad(); + var count = Object.keys(_loadedLibs).length + 1; // +1 for entry + var dt = Math.round(performance.now() - t0); + console.log("[sx-platform] Loaded " + count + " modules in " + dt + "ms (manifest-driven)"); + } + + /** + * Fallback: load all files in hardcoded order (pre-manifest compat). + */ + function loadWebStackFallback() { + var files = [ + "sx/render.sx", "sx/core-signals.sx", "sx/signals.sx", "sx/deps.sx", + "sx/router.sx", "sx/page-helpers.sx", "sx/freeze.sx", "sx/highlight.sx", + "sx/bytecode.sx", "sx/compiler.sx", "sx/vm.sx", "sx/dom.sx", "sx/browser.sx", + "sx/adapter-html.sx", "sx/adapter-sx.sx", "sx/adapter-dom.sx", + "sx/boot-helpers.sx", "sx/hypersx.sx", "sx/harness.sx", + "sx/harness-reactive.sx", "sx/harness-web.sx", + "sx/engine.sx", "sx/orchestration.sx", "sx/boot.sx", + ]; if (K.beginModuleLoad) K.beginModuleLoad(); for (var i = 0; i < files.length; i++) { - var r = loadBytecodeFile(files[i]); - if (r) { bcCount++; continue; } - // Bytecode not available — end batch, load source, restart batch - if (K.endModuleLoad) K.endModuleLoad(); - r = loadSxFile(files[i]); - if (typeof r === "number") { loaded += r; srcCount++; } - if (K.beginModuleLoad) K.beginModuleLoad(); + if (!loadBytecodeFile(files[i])) loadSxFile(files[i]); } if (K.endModuleLoad) K.endModuleLoad(); - console.log("[sx-platform] Loaded " + files.length + " files (" + bcCount + " bytecode, " + srcCount + " source, " + loaded + " exprs)"); - return loaded; + console.log("[sx-platform] Loaded " + files.length + " files (fallback)"); } + /** + * Load an optional library on demand (e.g., highlight, harness). + * Can be called after boot for pages that need extra modules. + */ + globalThis.__sxLoadLibrary = function(name) { + if (!_manifest) loadManifest(); + if (!_manifest) return false; + if (_loadedLibs[name]) return true; + if (K.beginModuleLoad) K.beginModuleLoad(); + var ok = loadLibrary(name, {}); + if (K.endModuleLoad) K.endModuleLoad(); + return ok; + }; + // ================================================================ // Compatibility shim — expose Sx global matching current JS API // ================================================================ diff --git a/shared/static/wasm/sx/adapter-dom.sx b/shared/static/wasm/sx/adapter-dom.sx index 8eb25cd5..8be3ae23 100644 --- a/shared/static/wasm/sx/adapter-dom.sx +++ b/shared/static/wasm/sx/adapter-dom.sx @@ -1,3 +1,10 @@ +(import (sx dom)) +(import (sx render)) + +(define-library (web adapter-dom) + (export SVG_NS MATH_NS island-scope? contains-deref? dom-on render-to-dom render-dom-list render-dom-element render-dom-component render-dom-fragment render-dom-raw render-dom-unknown-component RENDER_DOM_FORMS render-dom-form? dispatch-render-form render-lambda-dom render-dom-island render-dom-lake render-dom-marsh reactive-text reactive-attr reactive-spread reactive-fragment render-list-item extract-key reactive-list bind-input *use-cek-reactive* enable-cek-reactive! cek-reactive-text cek-reactive-attr render-dom-portal render-dom-error-boundary) + (begin + (define SVG_NS "http://www.w3.org/2000/svg") (define MATH_NS "http://www.w3.org/1998/Math/MathML") @@ -1336,3 +1343,9 @@ ((fallback-dom (if (nil? fallback-fn) (let ((el (dom-create-element "div" nil))) (dom-set-attr el "class" "sx-render-error") (dom-set-attr el "style" "color:red;font-size:0.875rem;padding:0.5rem;border:1px solid red;border-radius:0.25rem;margin:0.5rem 0;") (dom-set-text-content el (str "Render error: " err)) el) (if (lambda? fallback-fn) (render-lambda-dom fallback-fn (list err retry-fn) env ns) (render-to-dom (apply fallback-fn (list err retry-fn)) env ns))))) (dom-append container fallback-dom))))))) container))) + + +)) + +;; Re-export to global env +(import (web adapter-dom)) diff --git a/shared/static/wasm/sx/adapter-dom.sxbc b/shared/static/wasm/sx/adapter-dom.sxbc index 7319bed8..eac7ae9b 100644 --- a/shared/static/wasm/sx/adapter-dom.sxbc +++ b/shared/static/wasm/sx/adapter-dom.sxbc @@ -1,3 +1,3 @@ -(sxbc 1 "09ac0a01c77110da" +(sxbc 1 "f0dccdc6c6028305" (code :constants ("SVG_NS" "http://www.w3.org/2000/svg" "MATH_NS" "http://www.w3.org/1998/Math/MathML" "island-scope?" {:upvalue-count 0 :arity 0 :constants ("not" "nil?" "scope-peek" "sx-island-scope") :bytecode (1 3 0 52 2 0 1 52 1 0 1 52 0 0 1 50)} "*memo-cache*" "dict" "*cyst-counter*" 0 "next-cyst-id" {:upvalue-count 0 :arity 0 :constants ("+" "*cyst-counter*" 1 "str" "sx-cyst-") :bytecode (20 1 0 1 2 0 52 0 0 2 21 1 0 5 1 4 0 20 1 0 52 3 0 2 50)} "contains-deref?" {:upvalue-count 0 :arity 1 :constants ("not" "list?" "empty?" "=" "type-of" "first" "symbol" "symbol-name" "deref" "some" "contains-deref?") :bytecode (16 0 52 1 0 1 52 0 0 1 33 4 0 4 32 68 0 16 0 52 2 0 1 33 4 0 4 32 55 0 16 0 52 5 0 1 52 4 0 1 1 6 0 52 3 0 2 6 33 18 0 5 16 0 52 5 0 1 52 7 0 1 1 8 0 52 3 0 2 33 4 0 3 32 9 0 20 10 0 16 0 52 9 0 2 50)} "dom-on" {:upvalue-count 0 :arity 3 :constants ("dom-listen" "lambda?" "=" 0 "len" "lambda-params" {:upvalue-count 1 :arity 1 :constants ("trampoline" "call-lambda" "list" "run-post-render-hooks") :bytecode (18 0 52 2 0 0 52 1 0 2 52 0 0 1 5 20 3 0 49 0 50)} {:upvalue-count 1 :arity 1 :constants ("trampoline" "call-lambda" "list" "run-post-render-hooks") :bytecode (18 0 16 0 52 2 0 1 52 1 0 2 52 0 0 1 5 20 3 0 49 0 50)}) :bytecode (20 0 0 16 0 16 1 16 2 52 1 0 1 33 36 0 1 3 0 16 2 52 5 0 1 52 4 0 1 52 2 0 2 33 8 0 51 6 0 1 2 32 5 0 51 7 0 1 2 32 2 0 16 2 49 3 50)} "render-to-dom" {:upvalue-count 0 :arity 3 :constants ("set-render-active!" "type-of" "nil" "=" "create-fragment" "boolean" "raw-html" "dom-parse-html" "raw-html-content" "string" "create-text-node" "number" "str" "symbol" "render-to-dom" "trampoline" "eval-expr" "keyword" "keyword-name" "dom-node" "spread" "not" "island-scope?" "scope-emit!" "element-attrs" "spread-attrs" "dict" "has-key?" "__host_handle" "list" "empty?" "render-dom-list" "signal?" "reactive-text" "deref") :bytecode (3 52 0 0 1 5 16 0 52 1 0 1 6 1 2 0 52 3 0 2 33 9 0 5 20 4 0 49 0 32 103 1 6 1 5 0 52 3 0 2 33 9 0 5 20 4 0 49 0 32 83 1 6 1 6 0 52 3 0 2 33 15 0 5 20 7 0 16 0 52 8 0 1 49 1 32 57 1 6 1 9 0 52 3 0 2 33 11 0 5 20 10 0 16 0 49 1 32 35 1 6 1 11 0 52 3 0 2 33 15 0 5 20 10 0 16 0 52 12 0 1 49 1 32 9 1 6 1 13 0 52 3 0 2 33 25 0 5 20 14 0 16 0 16 1 52 16 0 2 52 15 0 1 16 1 16 2 49 3 32 229 0 6 1 17 0 52 3 0 2 33 15 0 5 20 10 0 16 0 52 18 0 1 49 1 32 203 0 6 1 19 0 52 3 0 2 33 6 0 5 16 0 32 186 0 6 1 20 0 52 3 0 2 33 36 0 5 20 22 0 48 0 52 21 0 1 33 16 0 1 24 0 16 0 52 25 0 1 52 23 0 2 32 1 0 2 5 16 0 32 139 0 6 1 26 0 52 3 0 2 33 26 0 5 16 0 1 28 0 52 27 0 2 33 5 0 16 0 32 5 0 20 4 0 49 0 32 102 0 6 1 29 0 52 3 0 2 33 32 0 5 16 0 52 30 0 1 33 8 0 20 4 0 49 0 32 11 0 20 31 0 16 0 16 1 16 2 49 3 32 59 0 5 20 32 0 16 0 48 1 33 37 0 20 22 0 48 0 33 10 0 20 33 0 16 0 49 1 32 16 0 20 10 0 20 34 0 16 0 48 1 52 12 0 1 49 1 32 11 0 20 10 0 16 0 52 12 0 1 49 1 50)} "render-dom-list" {:upvalue-count 0 :arity 3 :constants ("first" "=" "type-of" "symbol" "symbol-name" "rest" "raw!" "render-dom-raw" "<>" "render-dom-fragment" "lake" "render-dom-lake" "marsh" "render-dom-marsh" "starts-with?" "html:" "render-dom-element" "slice" 5 "render-dom-form?" "contains?" "HTML_TAGS" ">" "len" 0 "keyword" "dispatch-render-form" "env-has?" "macro?" "env-get" "render-to-dom" "expand-macro" "~" "island?" "scope-peek" "sx-render-markers" "dom-create-element" "span" "dict" "reduce" {:upvalue-count 3 :arity 2 :constants ("get" "skip" "assoc" "i" "inc" "=" "type-of" "keyword" "<" "len" "keyword-name" "trampoline" "eval-expr" "nth" "dict-set!") :bytecode (16 0 1 1 0 52 0 0 2 17 2 16 2 33 29 0 16 0 1 1 0 4 1 3 0 16 0 1 3 0 52 0 0 2 52 4 0 1 52 2 0 5 32 145 0 16 1 52 6 0 1 1 7 0 52 5 0 2 6 33 24 0 5 16 0 1 3 0 52 0 0 2 52 4 0 1 18 0 52 9 0 1 52 8 0 2 33 79 0 16 1 52 10 0 1 17 3 18 0 16 0 1 3 0 52 0 0 2 52 4 0 1 52 13 0 2 18 1 52 12 0 2 52 11 0 1 17 4 18 2 16 3 16 4 52 14 0 3 5 16 0 1 1 0 3 1 3 0 16 0 1 3 0 52 0 0 2 52 4 0 1 52 2 0 5 32 22 0 16 0 1 3 0 16 0 1 3 0 52 0 0 2 52 4 0 1 52 2 0 3 50)} "i" "skip" "dom-set-attr" "data-sx-island" "component-name" "not" "empty-dict?" "data-sx-state" "sx-serialize" "render-dom-island" "component?" "render-dom-component" "render-dom-unknown-component" "index-of" "-" "deref" "island-scope?" "trampoline" "eval-expr" "signal?" "reactive-text" "create-text-node" "str" "contains-deref?" "computed" {:upvalue-count 2 :arity 0 :constants ("trampoline" "eval-expr") :bytecode (18 0 18 1 52 1 0 2 52 0 0 1 50)} "lambda?" "list" "create-fragment" "for-each" {:upvalue-count 3 :arity 1 :constants ("render-to-dom" "not" "spread?" "dom-append") :bytecode (20 0 0 16 0 18 0 18 1 48 3 17 1 16 1 52 2 0 1 52 1 0 1 33 12 0 20 3 0 18 2 16 1 49 2 32 1 0 2 50)}) :bytecode (16 0 52 0 0 1 17 3 16 3 52 2 0 1 1 3 0 52 1 0 2 33 53 3 16 3 52 4 0 1 17 4 16 0 52 5 0 1 17 5 16 4 1 6 0 52 1 0 2 33 12 0 20 7 0 16 5 16 1 49 2 32 10 3 16 4 1 8 0 52 1 0 2 33 14 0 20 9 0 16 5 16 1 16 2 49 3 32 240 2 16 4 1 10 0 52 1 0 2 33 14 0 20 11 0 16 5 16 1 16 2 49 3 32 214 2 16 4 1 12 0 52 1 0 2 33 14 0 20 13 0 16 5 16 1 16 2 49 3 32 188 2 16 4 1 15 0 52 14 0 2 33 23 0 20 16 0 16 4 1 18 0 52 17 0 2 16 5 16 1 16 2 49 4 32 153 2 20 19 0 16 4 48 1 33 91 0 20 21 0 16 4 52 20 0 2 6 33 43 0 5 16 5 52 23 0 1 1 24 0 52 22 0 2 6 33 18 0 5 16 5 52 0 0 1 52 2 0 1 1 25 0 52 1 0 2 6 34 3 0 5 16 2 33 16 0 20 16 0 16 4 16 5 16 1 16 2 49 4 32 13 0 20 26 0 16 4 16 0 16 1 16 2 49 4 32 52 2 16 1 16 4 52 27 0 2 6 33 13 0 5 16 1 16 4 52 29 0 2 52 28 0 1 33 28 0 20 30 0 16 1 16 4 52 29 0 2 16 5 16 1 52 31 0 3 16 1 16 2 49 3 32 252 1 20 21 0 16 4 52 20 0 2 33 16 0 20 16 0 16 4 16 5 16 1 16 2 49 4 32 224 1 16 4 1 32 0 52 14 0 2 6 33 26 0 5 16 1 16 4 52 27 0 2 6 33 13 0 5 16 1 16 4 52 29 0 2 52 33 0 1 33 145 0 1 35 0 52 34 0 1 33 113 0 16 1 16 4 52 29 0 2 17 6 20 36 0 1 37 0 2 48 2 17 7 52 38 0 0 17 8 51 40 0 1 5 1 1 1 8 1 41 0 1 24 0 1 42 0 4 52 38 0 4 16 5 52 39 0 3 5 20 43 0 16 7 1 44 0 16 6 52 45 0 1 48 3 5 16 8 52 47 0 1 52 46 0 1 33 19 0 20 43 0 16 7 1 48 0 16 8 52 49 0 1 48 3 32 1 0 2 5 16 7 32 19 0 20 50 0 16 1 16 4 52 29 0 2 16 5 16 1 16 2 49 4 32 37 1 16 4 1 32 0 52 14 0 2 33 45 0 16 1 16 4 52 29 0 2 17 6 16 6 52 51 0 1 33 16 0 20 52 0 16 6 16 5 16 1 16 2 49 4 32 7 0 20 53 0 16 4 49 1 32 236 0 16 4 1 55 0 52 54 0 2 1 24 0 52 22 0 2 6 33 36 0 5 16 5 52 23 0 1 1 24 0 52 22 0 2 6 33 18 0 5 16 5 52 0 0 1 52 2 0 1 1 25 0 52 1 0 2 33 16 0 20 16 0 16 4 16 5 16 1 16 2 49 4 32 161 0 16 2 33 16 0 20 16 0 16 4 16 5 16 1 16 2 49 4 32 140 0 16 4 1 56 0 52 1 0 2 6 33 6 0 5 20 57 0 48 0 33 57 0 16 5 52 0 0 1 16 1 52 59 0 2 52 58 0 1 17 6 20 60 0 16 6 48 1 33 10 0 20 61 0 16 6 49 1 32 16 0 20 62 0 20 56 0 16 6 48 1 52 63 0 1 49 1 32 61 0 20 57 0 48 0 6 33 8 0 5 20 64 0 16 0 48 1 33 20 0 20 61 0 20 65 0 51 66 0 1 0 1 1 48 1 49 1 32 21 0 20 30 0 16 0 16 1 52 59 0 2 52 58 0 1 16 1 16 2 49 3 32 76 0 16 3 52 67 0 1 6 34 14 0 5 16 3 52 2 0 1 1 68 0 52 1 0 2 33 24 0 20 30 0 16 0 16 1 52 59 0 2 52 58 0 1 16 1 16 2 49 3 32 25 0 20 69 0 48 0 17 4 51 71 0 1 1 1 2 1 4 16 0 52 70 0 2 5 16 4 50)} "render-dom-element" {:upvalue-count 0 :arity 4 :constants ("=" "svg" "SVG_NS" "math" "MATH_NS" "dom-create-element" "scope-push!" "element-attrs" "reduce" {:upvalue-count 5 :arity 2 :constants ("get" "skip" "assoc" "i" "inc" "=" "type-of" "keyword" "<" "len" "keyword-name" "nth" "starts-with?" "on-" "trampoline" "eval-expr" "callable?" "dom-on" "slice" 3 "bind" "signal?" "bind-input" "ref" "dict-set!" "current" "key" "dom-set-attr" "str" "island-scope?" "reactive-attr" {:upvalue-count 2 :arity 0 :constants ("trampoline" "eval-expr") :bytecode (18 0 18 1 52 1 0 2 52 0 0 1 50)} "nil?" "contains?" "BOOLEAN_ATTRS" "" "not" "VOID_ELEMENTS" "render-to-dom" "spread?" "reactive-spread" {:upvalue-count 3 :arity 0 :constants ("render-to-dom") :bytecode (20 0 0 18 0 18 1 18 2 49 3 50)} "dom-append") :bytecode (16 0 1 1 0 52 0 0 2 17 2 16 2 33 29 0 16 0 1 1 0 4 1 3 0 16 0 1 3 0 52 0 0 2 52 4 0 1 52 2 0 5 32 47 2 16 1 52 6 0 1 1 7 0 52 5 0 2 6 33 24 0 5 16 0 1 3 0 52 0 0 2 52 4 0 1 18 0 52 9 0 1 52 8 0 2 33 143 1 16 1 52 10 0 1 17 3 18 0 16 0 1 3 0 52 0 0 2 52 4 0 1 52 11 0 2 17 4 16 3 1 13 0 52 12 0 2 33 49 0 16 4 18 1 52 15 0 2 52 14 0 1 17 5 20 16 0 16 5 48 1 33 21 0 20 17 0 18 2 16 3 1 19 0 52 18 0 2 16 5 48 3 32 1 0 2 32 23 1 16 3 1 20 0 52 5 0 2 33 40 0 16 4 18 1 52 15 0 2 52 14 0 1 17 5 20 21 0 16 5 48 1 33 12 0 20 22 0 18 2 16 5 48 2 32 1 0 2 32 227 0 16 3 1 23 0 52 5 0 2 33 28 0 16 4 18 1 52 15 0 2 52 14 0 1 17 5 16 5 1 25 0 18 2 52 24 0 3 32 187 0 16 3 1 26 0 52 5 0 2 33 33 0 16 4 18 1 52 15 0 2 52 14 0 1 17 5 20 27 0 18 2 1 26 0 16 5 52 28 0 1 48 3 32 142 0 20 29 0 48 0 33 19 0 20 30 0 18 2 16 3 51 31 0 1 4 0 1 48 3 32 115 0 16 4 18 1 52 15 0 2 52 14 0 1 17 5 16 5 52 32 0 1 6 34 8 0 5 16 5 4 52 5 0 2 33 4 0 2 32 76 0 20 34 0 16 3 52 33 0 2 33 24 0 16 5 33 15 0 20 27 0 18 2 16 3 1 35 0 48 3 32 1 0 2 32 40 0 16 5 3 52 5 0 2 33 15 0 20 27 0 18 2 16 3 1 35 0 48 3 32 15 0 20 27 0 18 2 16 3 16 5 52 28 0 1 48 3 5 16 0 1 1 0 3 1 3 0 16 0 1 3 0 52 0 0 2 52 4 0 1 52 2 0 5 32 116 0 20 37 0 18 3 52 33 0 2 52 36 0 1 33 76 0 20 38 0 16 1 18 1 18 4 48 3 17 3 16 3 52 39 0 1 6 33 6 0 5 20 29 0 48 0 33 19 0 20 40 0 18 2 51 41 0 1 1 0 1 0 4 48 2 32 22 0 16 3 52 39 0 1 33 4 0 2 32 9 0 20 42 0 18 2 16 3 48 2 32 1 0 2 5 16 0 1 3 0 16 0 1 3 0 52 0 0 2 52 4 0 1 52 2 0 3 50)} "dict" "i" 0 "skip" "for-each" {:upvalue-count 1 :arity 1 :constants ("for-each" {:upvalue-count 2 :arity 1 :constants ("dict-get" "=" "class" "dom-get-attr" "dom-set-attr" "not" "" "str" " " "style" ";") :bytecode (18 0 16 0 52 0 0 2 17 1 16 0 1 2 0 52 1 0 2 33 64 0 20 3 0 18 1 1 2 0 48 2 17 2 20 4 0 18 1 1 2 0 16 2 6 33 14 0 5 16 2 1 6 0 52 1 0 2 52 5 0 1 33 14 0 16 2 1 8 0 16 1 52 7 0 3 32 2 0 16 1 49 3 32 91 0 16 0 1 9 0 52 1 0 2 33 64 0 20 3 0 18 1 1 9 0 48 2 17 2 20 4 0 18 1 1 9 0 16 2 6 33 14 0 5 16 2 1 6 0 52 1 0 2 52 5 0 1 33 14 0 16 2 1 10 0 16 1 52 7 0 3 32 2 0 16 1 49 3 32 15 0 20 4 0 18 1 16 0 16 1 52 7 0 1 49 3 50)} "keys") :bytecode (51 1 0 1 0 0 0 16 0 52 2 0 1 52 0 0 2 50)} "scope-emitted" "scope-pop!") :bytecode (16 0 1 1 0 52 0 0 2 33 6 0 20 2 0 32 20 0 16 0 1 3 0 52 0 0 2 33 6 0 20 4 0 32 2 0 16 3 17 4 20 5 0 16 0 16 4 48 2 17 5 1 7 0 2 52 6 0 2 5 51 9 0 1 1 1 2 1 5 1 0 1 4 1 11 0 1 12 0 1 13 0 4 52 10 0 4 16 1 52 8 0 3 5 51 15 0 1 5 1 7 0 52 16 0 1 52 14 0 2 5 1 7 0 52 17 0 1 5 16 5 50)} "render-dom-component" {:upvalue-count 0 :arity 4 :constants ("dict" "list" "reduce" {:upvalue-count 4 :arity 2 :constants ("get" "skip" "assoc" "i" "inc" "=" "type-of" "keyword" "<" "len" "trampoline" "eval-expr" "nth" "dict-set!" "keyword-name" "append!") :bytecode (16 0 1 1 0 52 0 0 2 17 2 16 2 33 29 0 16 0 1 1 0 4 1 3 0 16 0 1 3 0 52 0 0 2 52 4 0 1 52 2 0 5 32 150 0 16 1 52 6 0 1 1 7 0 52 5 0 2 6 33 24 0 5 16 0 1 3 0 52 0 0 2 52 4 0 1 18 0 52 9 0 1 52 8 0 2 33 75 0 18 0 16 0 1 3 0 52 0 0 2 52 4 0 1 52 12 0 2 18 1 52 11 0 2 52 10 0 1 17 3 18 2 16 1 52 14 0 1 16 3 52 13 0 3 5 16 0 1 1 0 3 1 3 0 16 0 1 3 0 52 0 0 2 52 4 0 1 52 2 0 5 32 31 0 18 3 16 1 52 15 0 2 5 16 0 1 3 0 16 0 1 3 0 52 0 0 2 52 4 0 1 52 2 0 3 50)} "i" 0 "skip" "env-merge" "component-closure" "for-each" {:upvalue-count 2 :arity 1 :constants ("env-bind!" "dict-has?" "dict-get") :bytecode (18 0 16 0 18 1 16 0 52 1 0 2 33 11 0 18 1 16 0 52 2 0 2 32 1 0 2 52 0 0 3 50)} "component-params" "component-has-children?" "create-fragment" {:upvalue-count 3 :arity 1 :constants ("render-to-dom" "not" "spread?" "dom-append") :bytecode (20 0 0 16 0 18 0 18 1 48 3 17 1 16 1 52 2 0 1 52 1 0 1 33 12 0 20 3 0 18 2 16 1 49 2 32 1 0 2 50)} "env-bind!" "children" "render-to-dom" "component-body") :bytecode (52 0 0 0 17 4 52 1 0 0 17 5 51 3 0 1 1 1 2 1 4 1 5 1 4 0 1 5 0 1 6 0 4 52 0 0 4 16 1 52 2 0 3 5 16 0 52 8 0 1 16 2 52 7 0 2 17 6 51 10 0 1 6 1 4 16 0 52 11 0 1 52 9 0 2 5 16 0 52 12 0 1 33 37 0 20 13 0 48 0 17 7 51 14 0 1 2 1 3 1 7 16 5 52 9 0 2 5 16 6 1 16 0 16 7 52 15 0 3 32 1 0 2 5 20 17 0 16 0 52 18 0 1 16 6 16 3 49 3 50)} "render-dom-fragment" {:upvalue-count 0 :arity 3 :constants ("create-fragment" "for-each" {:upvalue-count 3 :arity 1 :constants ("render-to-dom" "not" "spread?" "dom-append") :bytecode (20 0 0 16 0 18 0 18 1 48 3 17 1 16 1 52 2 0 1 52 1 0 1 33 12 0 20 3 0 18 2 16 1 49 2 32 1 0 2 50)}) :bytecode (20 0 0 48 0 17 3 51 2 0 1 1 1 2 1 3 16 0 52 1 0 2 5 16 3 50)} "render-dom-raw" {:upvalue-count 0 :arity 2 :constants ("create-fragment" "for-each" {:upvalue-count 2 :arity 1 :constants ("trampoline" "eval-expr" "=" "type-of" "string" "dom-append" "dom-parse-html" "dom-node" "dom-clone" "not" "nil?" "create-text-node" "str") :bytecode (16 0 18 0 52 1 0 2 52 0 0 1 17 1 16 1 52 3 0 1 1 4 0 52 2 0 2 33 17 0 20 5 0 18 1 20 6 0 16 1 48 1 49 2 32 68 0 16 1 52 3 0 1 1 7 0 52 2 0 2 33 17 0 20 5 0 18 1 20 8 0 16 1 48 1 49 2 32 35 0 16 1 52 10 0 1 52 9 0 1 33 21 0 20 5 0 18 1 20 11 0 16 1 52 12 0 1 48 1 49 2 32 1 0 2 50)}) :bytecode (20 0 0 48 0 17 2 51 2 0 1 1 1 2 16 0 52 1 0 2 5 16 2 50)} "render-dom-unknown-component" {:upvalue-count 0 :arity 1 :constants ("error" "str" "Unknown component: ") :bytecode (1 2 0 16 0 52 1 0 2 52 0 0 1 50)} "RENDER_DOM_FORMS" "list" "if" "when" "cond" "case" "let" "let*" "letrec" "begin" "do" "define" "defcomp" "defisland" "defmacro" "defstyle" "map" "map-indexed" "filter" "for-each" "portal" "error-boundary" "scope" "provide" "cyst" "render-dom-form?" {:upvalue-count 0 :arity 1 :constants ("contains?" "RENDER_DOM_FORMS") :bytecode (20 1 0 16 0 52 0 0 2 50)} "dispatch-render-form" {:upvalue-count 0 :arity 4 :constants ("=" "if" "island-scope?" "create-comment" "r-if" "list" "effect" {:upvalue-count 6 :arity 0 :constants ("trampoline" "eval-expr" "nth" 1 "render-to-dom" 2 ">" "len" 3 "create-fragment" "dom-parent" "for-each" {:upvalue-count 0 :arity 1 :constants ("dom-remove") :bytecode (20 0 0 16 0 49 1 50)} "dom-is-fragment?" "dom-child-nodes" "list" "dom-insert-after") :bytecode (18 0 1 3 0 52 2 0 2 18 1 52 1 0 2 52 0 0 1 17 1 16 1 33 21 0 20 4 0 18 0 1 5 0 52 2 0 2 18 1 18 2 48 3 32 42 0 18 0 52 7 0 1 1 8 0 52 6 0 2 33 21 0 20 4 0 18 0 1 8 0 52 2 0 2 18 1 18 2 48 3 32 5 0 20 9 0 48 0 17 0 20 10 0 18 3 48 1 33 51 0 51 12 0 18 4 52 11 0 2 5 20 13 0 16 0 48 1 33 10 0 20 14 0 16 0 48 1 32 6 0 16 0 52 15 0 1 19 4 5 20 16 0 18 3 16 0 49 2 32 4 0 16 0 19 5 50)} "spread?" "create-fragment" "dom-append" "dom-is-fragment?" "dom-child-nodes" "trampoline" "eval-expr" "nth" 1 "render-to-dom" 2 ">" "len" 3 "when" "r-when" {:upvalue-count 6 :arity 0 :constants ("dom-parent" "for-each" {:upvalue-count 0 :arity 1 :constants ("dom-remove") :bytecode (20 0 0 16 0 49 1 50)} "list" "trampoline" "eval-expr" "nth" 1 "create-fragment" {:upvalue-count 4 :arity 1 :constants ("dom-append" "render-to-dom" "nth") :bytecode (20 0 0 18 0 20 1 0 18 1 16 0 52 2 0 2 18 2 18 3 48 3 49 2 50)} "range" 2 "len" "dom-child-nodes" "dom-insert-after") :bytecode (20 0 0 18 0 48 1 33 101 0 51 2 0 18 1 52 1 0 2 5 52 3 0 0 19 1 5 18 2 1 7 0 52 6 0 2 18 3 52 5 0 2 52 4 0 1 33 58 0 20 8 0 48 0 17 0 51 9 0 1 0 0 2 0 3 0 4 1 11 0 18 2 52 12 0 1 52 10 0 2 52 1 0 2 5 20 13 0 16 0 48 1 19 1 5 20 14 0 18 0 16 0 49 2 32 1 0 2 32 76 0 18 2 1 7 0 52 6 0 2 18 3 52 5 0 2 52 4 0 1 33 53 0 20 8 0 48 0 17 0 51 9 0 1 0 0 2 0 3 0 4 1 11 0 18 2 52 12 0 1 52 10 0 2 52 1 0 2 5 20 13 0 16 0 48 1 19 1 5 16 0 19 5 32 1 0 2 50)} "not" "for-each" {:upvalue-count 4 :arity 1 :constants ("dom-append" "render-to-dom" "nth") :bytecode (20 0 0 18 0 20 1 0 18 1 16 0 52 2 0 2 18 2 18 3 48 3 49 2 50)} "range" "cond" "r-cond" {:upvalue-count 6 :arity 0 :constants ("eval-cond" "rest" "dom-parent" "for-each" {:upvalue-count 0 :arity 1 :constants ("dom-remove") :bytecode (20 0 0 16 0 49 1 50)} "list" "render-to-dom" "dom-is-fragment?" "dom-child-nodes" "dom-insert-after") :bytecode (20 0 0 18 0 52 1 0 1 18 1 48 2 17 0 20 2 0 18 2 48 1 33 80 0 51 4 0 18 3 52 3 0 2 5 52 5 0 0 19 3 5 16 0 33 54 0 20 6 0 16 0 18 1 18 4 48 3 17 1 20 7 0 16 1 48 1 33 10 0 20 8 0 16 1 48 1 32 6 0 16 1 52 5 0 1 19 3 5 20 9 0 18 2 16 1 49 2 32 1 0 2 32 55 0 16 0 33 49 0 20 6 0 16 0 18 1 18 4 48 3 17 1 20 7 0 16 1 48 1 33 10 0 20 8 0 16 1 48 1 32 6 0 16 1 52 5 0 1 19 3 5 16 1 19 5 32 1 0 2 50)} "eval-cond" "rest" "case" "let" "let*" "process-bindings" {:upvalue-count 4 :arity 1 :constants ("render-to-dom" "nth" "not" "spread?" "dom-append") :bytecode (20 0 0 18 0 16 0 52 1 0 2 18 1 18 2 48 3 17 1 16 1 52 3 0 1 52 2 0 1 33 12 0 20 4 0 18 3 16 1 49 2 32 1 0 2 50)} "letrec" "slice" "env-extend" {:upvalue-count 1 :arity 1 :constants ("=" "type-of" "first" "symbol" "symbol-name" "str" "env-bind!") :bytecode (16 0 52 2 0 1 52 1 0 1 1 3 0 52 0 0 2 33 13 0 16 0 52 2 0 1 52 4 0 1 32 10 0 16 0 52 2 0 1 52 5 0 1 17 1 18 0 16 1 2 52 6 0 3 50)} {:upvalue-count 1 :arity 1 :constants ("=" "type-of" "first" "symbol" "symbol-name" "str" "env-set!" "trampoline" "eval-expr" "nth" 1) :bytecode (16 0 52 2 0 1 52 1 0 1 1 3 0 52 0 0 2 33 13 0 16 0 52 2 0 1 52 4 0 1 32 10 0 16 0 52 2 0 1 52 5 0 1 17 1 18 0 16 1 16 0 1 10 0 52 9 0 2 18 0 52 8 0 2 52 7 0 1 52 6 0 3 50)} {:upvalue-count 1 :arity 1 :constants ("trampoline" "eval-expr") :bytecode (16 0 18 0 52 1 0 2 52 0 0 1 50)} "init" "last" "begin" "do" "definition-form?" "map" "type-of" "first" "symbol" "symbol-name" "deref" "signal?" "reactive-list" {:upvalue-count 4 :arity 1 :constants ("lambda?" "render-lambda-dom" "list" "render-to-dom" "apply" "dom-append") :bytecode (18 0 52 0 0 1 33 20 0 20 1 0 18 0 16 0 52 2 0 1 18 1 18 2 48 4 32 21 0 20 3 0 18 0 16 0 52 2 0 1 52 4 0 2 18 1 18 2 48 3 17 1 20 5 0 18 3 16 1 49 2 50)} "map-indexed" "for-each-indexed" {:upvalue-count 4 :arity 2 :constants ("lambda?" "render-lambda-dom" "list" "render-to-dom" "apply" "dom-append") :bytecode (18 0 52 0 0 1 33 22 0 20 1 0 18 0 16 0 16 1 52 2 0 2 18 1 18 2 48 4 32 23 0 20 3 0 18 0 16 0 16 1 52 2 0 2 52 4 0 2 18 1 18 2 48 3 17 2 20 5 0 18 3 16 2 49 2 50)} "filter" "portal" "render-dom-portal" "error-boundary" "render-dom-error-boundary" "scope" ">=" "keyword" "keyword-name" "value" "scope-push!" {:upvalue-count 3 :arity 1 :constants ("dom-append" "render-to-dom") :bytecode (20 0 0 18 0 20 1 0 16 0 18 1 18 2 48 3 49 2 50)} "scope-pop!" "provide" "cyst" "key" "str" "next-cyst-id" "get" "*memo-cache*" "host-get" "isConnected" "dom-create-element" "div" "dom-set-attr" "data-sx-cyst" "with-island-scope" {:upvalue-count 1 :arity 1 :constants ("append!") :bytecode (18 0 16 0 52 0 0 2 50)} {:upvalue-count 3 :arity 0 :constants ("create-fragment" "for-each" {:upvalue-count 3 :arity 1 :constants ("dom-append" "render-to-dom") :bytecode (20 0 0 18 0 20 1 0 16 0 18 1 18 2 48 3 49 2 50)}) :bytecode (20 0 0 48 0 17 0 51 2 0 1 0 0 0 0 1 18 2 52 1 0 2 5 16 0 50)} "dom-set-data" "sx-disposers" "dict-set!") :bytecode (16 0 1 1 0 52 0 0 2 33 224 0 20 2 0 48 0 33 124 0 20 3 0 1 4 0 48 1 17 4 52 5 0 0 17 5 2 17 6 20 6 0 51 7 0 1 1 1 2 1 3 1 4 1 5 1 6 48 1 5 16 6 52 8 0 1 33 5 0 16 6 32 67 0 20 9 0 48 0 17 7 20 10 0 16 7 16 4 48 2 5 16 6 33 41 0 20 11 0 16 6 48 1 33 10 0 20 12 0 16 6 48 1 32 6 0 16 6 52 5 0 1 17 5 5 20 10 0 16 7 16 6 48 2 32 1 0 2 5 16 7 32 89 0 16 1 1 16 0 52 15 0 2 16 2 52 14 0 2 52 13 0 1 17 4 16 4 33 21 0 20 17 0 16 1 1 18 0 52 15 0 2 16 2 16 3 49 3 32 42 0 16 1 52 20 0 1 1 21 0 52 19 0 2 33 21 0 20 17 0 16 1 1 21 0 52 15 0 2 16 2 16 3 49 3 32 5 0 20 9 0 49 0 32 176 7 16 0 1 22 0 52 0 0 2 33 178 0 20 2 0 48 0 33 95 0 20 3 0 1 23 0 48 1 17 4 52 5 0 0 17 5 2 17 6 20 6 0 51 24 0 1 4 1 5 1 1 1 2 1 3 1 6 48 1 5 16 6 52 8 0 1 33 5 0 16 6 32 38 0 20 9 0 48 0 17 7 20 10 0 16 7 16 4 48 2 5 16 6 33 12 0 20 10 0 16 7 16 6 48 2 32 1 0 2 5 16 7 32 72 0 16 1 1 16 0 52 15 0 2 16 2 52 14 0 2 52 13 0 1 52 25 0 1 33 8 0 20 9 0 49 0 32 38 0 20 9 0 48 0 17 4 51 27 0 1 4 1 1 1 2 1 3 1 18 0 16 1 52 20 0 1 52 28 0 2 52 26 0 2 5 16 4 32 242 6 16 0 1 29 0 52 0 0 2 33 145 0 20 2 0 48 0 33 95 0 20 3 0 1 30 0 48 1 17 4 52 5 0 0 17 5 2 17 6 20 6 0 51 31 0 1 1 1 2 1 4 1 5 1 3 1 6 48 1 5 16 6 52 8 0 1 33 5 0 16 6 32 38 0 20 9 0 48 0 17 7 20 10 0 16 7 16 4 48 2 5 16 6 33 12 0 20 10 0 16 7 16 6 48 2 32 1 0 2 5 16 7 32 39 0 20 32 0 16 1 52 33 0 1 16 2 48 2 17 4 16 4 33 14 0 20 17 0 16 4 16 2 16 3 49 3 32 5 0 20 9 0 49 0 32 85 6 16 0 1 34 0 52 0 0 2 33 24 0 20 17 0 16 1 16 2 52 14 0 2 52 13 0 1 16 2 16 3 49 3 32 49 6 16 0 1 35 0 52 0 0 2 6 34 10 0 5 16 0 1 36 0 52 0 0 2 33 96 0 20 37 0 16 1 1 16 0 52 15 0 2 16 2 48 2 17 4 16 1 52 20 0 1 1 21 0 52 0 0 2 33 21 0 20 17 0 16 1 1 18 0 52 15 0 2 16 4 16 3 49 3 32 38 0 20 9 0 48 0 17 5 51 38 0 1 1 1 4 1 3 1 5 1 18 0 16 1 52 20 0 1 52 28 0 2 52 26 0 2 5 16 5 32 183 5 16 0 1 39 0 52 0 0 2 33 108 0 16 1 1 16 0 52 15 0 2 17 4 16 1 1 18 0 52 40 0 2 17 5 16 2 52 41 0 1 17 6 51 42 0 1 6 16 4 52 26 0 2 5 51 43 0 1 6 16 4 52 26 0 2 5 16 5 52 20 0 1 1 16 0 52 19 0 2 33 18 0 51 44 0 1 6 16 5 52 45 0 1 52 26 0 2 32 1 0 2 5 20 17 0 16 5 52 46 0 1 16 6 16 3 49 3 32 63 5 16 0 1 47 0 52 0 0 2 6 34 10 0 5 16 0 1 48 0 52 0 0 2 33 78 0 16 1 52 20 0 1 1 18 0 52 0 0 2 33 21 0 20 17 0 16 1 1 16 0 52 15 0 2 16 2 16 3 49 3 32 38 0 20 9 0 48 0 17 4 51 38 0 1 1 1 2 1 3 1 4 1 16 0 16 1 52 20 0 1 52 28 0 2 52 26 0 2 5 16 4 32 215 4 20 49 0 16 0 48 1 33 21 0 16 1 16 2 52 14 0 2 52 13 0 1 5 20 9 0 49 0 32 184 4 16 0 1 50 0 52 0 0 2 33 22 1 16 1 1 18 0 52 15 0 2 17 4 20 2 0 48 0 6 33 76 0 5 16 4 52 51 0 1 1 5 0 52 0 0 2 6 33 58 0 5 16 4 52 20 0 1 1 16 0 52 19 0 2 6 33 40 0 5 16 4 52 52 0 1 52 51 0 1 1 53 0 52 0 0 2 6 33 18 0 5 16 4 52 52 0 1 52 54 0 1 1 55 0 52 0 0 2 33 107 0 16 1 1 16 0 52 15 0 2 16 2 52 14 0 2 52 13 0 1 17 5 16 4 1 16 0 52 15 0 2 16 2 52 14 0 2 52 13 0 1 17 6 20 56 0 16 6 48 1 33 16 0 20 57 0 16 5 16 6 16 2 16 3 49 4 32 36 0 20 55 0 16 6 48 1 17 7 20 9 0 48 0 17 8 51 58 0 1 5 1 2 1 3 1 8 16 7 52 26 0 2 5 16 8 32 69 0 16 1 1 16 0 52 15 0 2 16 2 52 14 0 2 52 13 0 1 17 5 16 1 1 18 0 52 15 0 2 16 2 52 14 0 2 52 13 0 1 17 6 20 9 0 48 0 17 7 51 58 0 1 5 1 2 1 3 1 7 16 6 52 26 0 2 5 16 7 32 150 3 16 0 1 59 0 52 0 0 2 33 72 0 16 1 1 16 0 52 15 0 2 16 2 52 14 0 2 52 13 0 1 17 4 16 1 1 18 0 52 15 0 2 16 2 52 14 0 2 52 13 0 1 17 5 20 9 0 48 0 17 6 51 61 0 1 4 1 2 1 3 1 6 16 5 52 60 0 2 5 16 6 32 66 3 16 0 1 62 0 52 0 0 2 33 24 0 20 17 0 16 1 16 2 52 14 0 2 52 13 0 1 16 2 16 3 49 3 32 30 3 16 0 1 63 0 52 0 0 2 33 18 0 20 64 0 16 1 52 33 0 1 16 2 16 3 49 3 32 0 3 16 0 1 65 0 52 0 0 2 33 18 0 20 66 0 16 1 52 33 0 1 16 2 16 3 49 3 32 226 2 16 0 1 26 0 52 0 0 2 33 72 0 16 1 1 16 0 52 15 0 2 16 2 52 14 0 2 52 13 0 1 17 4 16 1 1 18 0 52 15 0 2 16 2 52 14 0 2 52 13 0 1 17 5 20 9 0 48 0 17 6 51 58 0 1 4 1 2 1 3 1 6 16 5 52 26 0 2 5 16 6 32 142 2 16 0 1 67 0 52 0 0 2 33 183 0 16 1 1 16 0 52 15 0 2 16 2 52 14 0 2 52 13 0 1 17 4 16 1 1 18 0 52 40 0 2 17 5 2 17 6 2 17 7 20 9 0 48 0 17 8 16 5 52 20 0 1 1 18 0 52 68 0 2 6 33 40 0 5 16 5 52 52 0 1 52 51 0 1 1 69 0 52 0 0 2 6 33 18 0 5 16 5 52 52 0 1 52 70 0 1 1 71 0 52 0 0 2 33 36 0 16 5 1 16 0 52 15 0 2 16 2 52 14 0 2 52 13 0 1 17 6 5 16 5 1 18 0 52 40 0 2 17 7 32 4 0 16 5 17 7 5 16 4 16 6 52 72 0 2 5 51 73 0 1 8 1 2 1 3 16 7 52 26 0 2 5 16 4 52 74 0 1 5 16 8 32 203 1 16 0 1 75 0 52 0 0 2 33 99 0 16 1 1 16 0 52 15 0 2 16 2 52 14 0 2 52 13 0 1 17 4 16 1 1 18 0 52 15 0 2 16 2 52 14 0 2 52 13 0 1 17 5 20 9 0 48 0 17 6 16 4 16 5 52 72 0 2 5 51 27 0 1 6 1 1 1 2 1 3 1 21 0 16 1 52 20 0 1 52 28 0 2 52 26 0 2 5 16 4 52 74 0 1 5 16 6 32 92 1 16 0 1 76 0 52 0 0 2 33 59 1 16 1 52 20 0 1 1 18 0 52 19 0 2 6 33 46 0 5 16 1 1 16 0 52 15 0 2 52 51 0 1 1 69 0 52 0 0 2 6 33 21 0 5 16 1 1 16 0 52 15 0 2 52 70 0 1 1 77 0 52 0 0 2 33 26 0 16 1 1 18 0 52 15 0 2 16 2 52 14 0 2 52 13 0 1 52 78 0 1 32 5 0 20 79 0 48 0 17 4 20 81 0 16 4 52 80 0 2 17 5 16 5 6 33 11 0 5 20 82 0 16 5 1 83 0 48 2 33 5 0 16 5 32 177 0 20 84 0 1 85 0 2 48 2 17 6 52 5 0 0 17 7 16 1 52 20 0 1 1 18 0 52 19 0 2 6 33 46 0 5 16 1 1 16 0 52 15 0 2 52 51 0 1 1 69 0 52 0 0 2 6 33 21 0 5 16 1 1 16 0 52 15 0 2 52 70 0 1 1 77 0 52 0 0 2 33 12 0 16 1 1 21 0 52 40 0 2 32 9 0 16 1 1 16 0 52 40 0 2 17 8 20 86 0 16 6 1 87 0 16 4 48 3 5 20 88 0 51 89 0 1 7 51 90 0 1 2 1 3 1 8 48 2 17 9 20 10 0 16 6 16 9 48 2 5 20 91 0 16 6 1 92 0 16 7 48 3 5 20 81 0 16 4 16 6 52 93 0 3 5 16 6 32 21 0 20 17 0 16 1 16 2 52 14 0 2 52 13 0 1 16 2 16 3 49 3 50)} "render-lambda-dom" {:upvalue-count 0 :arity 4 :constants ("env-merge" "lambda-closure" "for-each-indexed" {:upvalue-count 2 :arity 2 :constants ("env-bind!" "nth") :bytecode (18 0 16 1 18 1 16 0 52 1 0 2 52 0 0 3 50)} "lambda-params" "render-to-dom" "lambda-body") :bytecode (16 0 52 1 0 1 16 2 52 0 0 2 17 4 51 3 0 1 4 1 1 16 0 52 4 0 1 52 2 0 2 5 20 5 0 16 0 52 6 0 1 16 4 16 3 49 3 50)} "render-dom-island" {:upvalue-count 0 :arity 4 :constants ("dict" "list" "reduce" {:upvalue-count 4 :arity 2 :constants ("get" "skip" "assoc" "i" "inc" "=" "type-of" "keyword" "<" "len" "trampoline" "eval-expr" "nth" "dict-set!" "keyword-name" "append!") :bytecode (16 0 1 1 0 52 0 0 2 17 2 16 2 33 29 0 16 0 1 1 0 4 1 3 0 16 0 1 3 0 52 0 0 2 52 4 0 1 52 2 0 5 32 150 0 16 1 52 6 0 1 1 7 0 52 5 0 2 6 33 24 0 5 16 0 1 3 0 52 0 0 2 52 4 0 1 18 0 52 9 0 1 52 8 0 2 33 75 0 18 0 16 0 1 3 0 52 0 0 2 52 4 0 1 52 12 0 2 18 1 52 11 0 2 52 10 0 1 17 3 18 2 16 1 52 14 0 1 16 3 52 13 0 3 5 16 0 1 1 0 3 1 3 0 16 0 1 3 0 52 0 0 2 52 4 0 1 52 2 0 5 32 31 0 18 3 16 1 52 15 0 2 5 16 0 1 3 0 16 0 1 3 0 52 0 0 2 52 4 0 1 52 2 0 3 50)} "i" 0 "skip" "env-merge" "component-closure" "component-name" "for-each" {:upvalue-count 2 :arity 1 :constants ("env-bind!" "dict-has?" "dict-get") :bytecode (18 0 16 0 18 1 16 0 52 1 0 2 33 11 0 18 1 16 0 52 2 0 2 32 1 0 2 52 0 0 3 50)} "component-params" "component-has-children?" "create-fragment" {:upvalue-count 3 :arity 1 :constants ("dom-append" "render-to-dom") :bytecode (20 0 0 18 0 20 1 0 16 0 18 1 18 2 48 3 49 2 50)} "env-bind!" "children" "dom-create-element" "span" "dom-set-attr" "data-sx-island" "not" "empty-dict?" "data-sx-state" "sx-serialize" "mark-processed!" "island-hydrated" "with-island-scope" {:upvalue-count 1 :arity 1 :constants ("append!") :bytecode (18 0 16 0 52 0 0 2 50)} {:upvalue-count 3 :arity 0 :constants ("render-to-dom" "component-body") :bytecode (20 0 0 18 0 52 1 0 1 18 1 18 2 49 3 50)} "dom-append" "dom-set-data" "sx-disposers") :bytecode (52 0 0 0 17 4 52 1 0 0 17 5 51 3 0 1 1 1 2 1 4 1 5 1 4 0 1 5 0 1 6 0 4 52 0 0 4 16 1 52 2 0 3 5 16 0 52 8 0 1 16 2 52 7 0 2 17 6 16 0 52 9 0 1 17 7 51 11 0 1 6 1 4 16 0 52 12 0 1 52 10 0 2 5 16 0 52 13 0 1 33 37 0 20 14 0 48 0 17 8 51 15 0 1 8 1 2 1 3 16 5 52 10 0 2 5 16 6 1 17 0 16 8 52 16 0 3 32 1 0 2 5 20 18 0 1 19 0 2 48 2 17 8 52 1 0 0 17 9 20 20 0 16 8 1 21 0 16 7 48 3 5 16 4 52 23 0 1 52 22 0 1 33 19 0 20 20 0 16 8 1 24 0 16 4 52 25 0 1 48 3 32 1 0 2 5 20 26 0 16 8 1 27 0 48 2 5 20 28 0 51 29 0 1 9 51 30 0 1 0 1 6 1 3 48 2 17 10 20 31 0 16 8 16 10 48 2 5 20 32 0 16 8 1 33 0 16 9 48 3 5 16 8 50)} "render-dom-lake" {:upvalue-count 0 :arity 3 :constants ("div" "list" "reduce" {:upvalue-count 5 :arity 2 :constants ("get" "skip" "assoc" "i" "inc" "=" "type-of" "keyword" "<" "len" "keyword-name" "trampoline" "eval-expr" "nth" "id" "tag" "append!") :bytecode (16 0 1 1 0 52 0 0 2 17 2 16 2 33 29 0 16 0 1 1 0 4 1 3 0 16 0 1 3 0 52 0 0 2 52 4 0 1 52 2 0 5 32 183 0 16 1 52 6 0 1 1 7 0 52 5 0 2 6 33 24 0 5 16 0 1 3 0 52 0 0 2 52 4 0 1 18 0 52 9 0 1 52 8 0 2 33 108 0 16 1 52 10 0 1 17 3 18 0 16 0 1 3 0 52 0 0 2 52 4 0 1 52 13 0 2 18 1 52 12 0 2 52 11 0 1 17 4 16 3 1 14 0 52 5 0 2 33 7 0 16 4 19 2 32 20 0 16 3 1 15 0 52 5 0 2 33 7 0 16 4 19 3 32 1 0 2 5 16 0 1 1 0 3 1 3 0 16 0 1 3 0 52 0 0 2 52 4 0 1 52 2 0 5 32 31 0 18 4 16 1 52 16 0 2 5 16 0 1 3 0 16 0 1 3 0 52 0 0 2 52 4 0 1 52 2 0 3 50)} "dict" "i" 0 "skip" "dom-create-element" "dom-set-attr" "data-sx-lake" "" "for-each" {:upvalue-count 3 :arity 1 :constants ("dom-append" "render-to-dom") :bytecode (20 0 0 18 0 20 1 0 16 0 18 1 18 2 48 3 49 2 50)}) :bytecode (2 17 3 1 0 0 17 4 52 1 0 0 17 5 51 3 0 1 0 1 1 1 3 1 4 1 5 1 5 0 1 6 0 1 7 0 4 52 4 0 4 16 0 52 2 0 3 5 20 8 0 16 4 2 48 2 17 6 20 9 0 16 6 1 10 0 16 3 6 34 4 0 5 1 11 0 48 3 5 51 13 0 1 6 1 1 1 2 16 5 52 12 0 2 5 16 6 50)} "render-dom-marsh" {:upvalue-count 0 :arity 3 :constants ("div" "list" "reduce" {:upvalue-count 6 :arity 2 :constants ("get" "skip" "assoc" "i" "inc" "=" "type-of" "keyword" "<" "len" "keyword-name" "trampoline" "eval-expr" "nth" "id" "tag" "transform" "append!") :bytecode (16 0 1 1 0 52 0 0 2 17 2 16 2 33 29 0 16 0 1 1 0 4 1 3 0 16 0 1 3 0 52 0 0 2 52 4 0 1 52 2 0 5 32 202 0 16 1 52 6 0 1 1 7 0 52 5 0 2 6 33 24 0 5 16 0 1 3 0 52 0 0 2 52 4 0 1 18 0 52 9 0 1 52 8 0 2 33 127 0 16 1 52 10 0 1 17 3 18 0 16 0 1 3 0 52 0 0 2 52 4 0 1 52 13 0 2 18 1 52 12 0 2 52 11 0 1 17 4 16 3 1 14 0 52 5 0 2 33 7 0 16 4 19 2 32 39 0 16 3 1 15 0 52 5 0 2 33 7 0 16 4 19 3 32 20 0 16 3 1 16 0 52 5 0 2 33 7 0 16 4 19 4 32 1 0 2 5 16 0 1 1 0 3 1 3 0 16 0 1 3 0 52 0 0 2 52 4 0 1 52 2 0 5 32 31 0 18 5 16 1 52 17 0 2 5 16 0 1 3 0 16 0 1 3 0 52 0 0 2 52 4 0 1 52 2 0 3 50)} "dict" "i" 0 "skip" "dom-create-element" "dom-set-attr" "data-sx-marsh" "" "dom-set-data" "sx-marsh-transform" "sx-marsh-env" "for-each" {:upvalue-count 3 :arity 1 :constants ("dom-append" "render-to-dom") :bytecode (20 0 0 18 0 20 1 0 16 0 18 1 18 2 48 3 49 2 50)}) :bytecode (2 17 3 1 0 0 17 4 2 17 5 52 1 0 0 17 6 51 3 0 1 0 1 1 1 3 1 4 1 5 1 6 1 5 0 1 6 0 1 7 0 4 52 4 0 4 16 0 52 2 0 3 5 20 8 0 16 4 2 48 2 17 7 20 9 0 16 7 1 10 0 16 3 6 34 4 0 5 1 11 0 48 3 5 16 5 33 15 0 20 12 0 16 7 1 13 0 16 5 48 3 32 1 0 2 5 20 12 0 16 7 1 14 0 16 1 48 3 5 51 16 0 1 7 1 1 1 2 16 6 52 15 0 2 5 16 7 50)} "reactive-text" {:upvalue-count 0 :arity 1 :constants ("create-text-node" "str" "deref" "effect" {:upvalue-count 2 :arity 0 :constants ("dom-set-text-content" "str" "deref") :bytecode (20 0 0 18 0 20 2 0 18 1 48 1 52 1 0 1 49 2 50)}) :bytecode (20 0 0 20 2 0 16 0 48 1 52 1 0 1 48 1 17 1 20 3 0 51 4 0 1 1 1 0 48 1 5 16 1 50)} "reactive-attr" {:upvalue-count 0 :arity 3 :constants ("dom-get-attr" "data-sx-reactive-attrs" "" "empty?" "str" "," "dom-set-attr" "effect" {:upvalue-count 3 :arity 0 :constants ("signal?" "deref" "nil?" "=" "dom-remove-attr" "dom-set-attr" "" "str") :bytecode (18 0 48 0 17 0 20 0 0 16 0 48 1 33 10 0 20 1 0 16 0 48 1 32 2 0 16 0 17 1 16 1 52 2 0 1 6 34 8 0 5 16 1 4 52 3 0 2 33 12 0 20 4 0 18 1 18 2 49 2 32 40 0 16 1 3 52 3 0 2 33 15 0 20 5 0 18 1 18 2 1 6 0 49 3 32 15 0 20 5 0 18 1 18 2 16 1 52 7 0 1 49 3 50)}) :bytecode (20 0 0 16 0 1 1 0 48 2 6 34 4 0 5 1 2 0 17 3 16 3 52 3 0 1 33 5 0 16 1 32 11 0 16 3 1 5 0 16 1 52 4 0 3 17 4 20 6 0 16 0 1 1 0 16 4 48 3 5 20 7 0 51 8 0 1 2 1 0 1 1 49 1 50)} "reactive-spread" {:upvalue-count 0 :arity 2 :constants ("list" "dom-get-attr" "data-sx-reactive-attrs" "" "dom-set-attr" "empty?" "_spread" "str" ",_spread" "effect" {:upvalue-count 4 :arity 0 :constants ("not" "empty?" "dom-get-attr" "class" "" "filter" {:upvalue-count 0 :arity 1 :constants ("not" "=" "") :bytecode (16 0 1 2 0 52 1 0 2 52 0 0 1 50)} "split" " " {:upvalue-count 1 :arity 1 :constants ("not" "some" {:upvalue-count 1 :arity 1 :constants ("=") :bytecode (16 0 18 0 52 0 0 2 50)}) :bytecode (51 2 0 1 0 18 0 52 1 0 2 52 0 0 1 50)} "dom-remove-attr" "dom-set-attr" "join" "for-each" {:upvalue-count 1 :arity 1 :constants ("dom-remove-attr") :bytecode (20 0 0 18 0 16 0 49 2 50)} "spread?" "spread-attrs" "dict-get" {:upvalue-count 0 :arity 1 :constants ("not" "=" "class") :bytecode (16 0 1 2 0 52 1 0 2 52 0 0 1 50)} "keys" "=" "str" {:upvalue-count 2 :arity 1 :constants ("dom-set-attr" "str" "dict-get") :bytecode (20 0 0 18 0 16 0 18 1 16 0 52 2 0 2 52 1 0 1 49 3 50)} "run-post-render-hooks" "list") :bytecode (18 0 52 1 0 1 52 0 0 1 33 95 0 20 2 0 18 1 1 3 0 48 2 6 34 4 0 5 1 4 0 17 0 51 6 0 16 0 1 8 0 52 7 0 2 52 5 0 2 17 1 51 9 0 0 0 16 1 52 5 0 2 17 2 16 2 52 1 0 1 33 13 0 20 10 0 18 1 1 3 0 48 2 32 19 0 20 11 0 18 1 1 3 0 1 8 0 16 2 52 12 0 2 48 3 32 1 0 2 5 51 14 0 0 1 18 2 52 13 0 2 5 18 3 48 0 17 0 16 0 52 15 0 1 33 179 0 16 0 52 16 0 1 17 1 16 1 1 3 0 52 17 0 2 6 34 4 0 5 1 4 0 17 2 51 6 0 16 2 1 8 0 52 7 0 2 52 5 0 2 17 3 51 18 0 16 1 52 19 0 1 52 5 0 2 17 4 16 3 19 0 5 16 4 19 2 5 16 3 52 1 0 1 52 0 0 1 33 72 0 20 2 0 18 1 1 3 0 48 2 6 34 4 0 5 1 4 0 17 5 20 11 0 18 1 1 3 0 16 5 6 33 14 0 5 16 5 1 4 0 52 20 0 2 52 0 0 1 33 14 0 16 5 1 8 0 16 2 52 21 0 3 32 2 0 16 2 48 3 32 1 0 2 5 51 22 0 0 1 1 1 16 4 52 13 0 2 5 20 23 0 49 0 32 13 0 52 24 0 0 19 0 5 52 24 0 0 19 2 50)}) :bytecode (52 0 0 0 17 2 52 0 0 0 17 3 20 1 0 16 0 1 2 0 48 2 6 34 4 0 5 1 3 0 17 4 20 4 0 16 0 1 2 0 16 4 52 5 0 1 33 6 0 1 6 0 32 9 0 16 4 1 8 0 52 7 0 2 48 3 5 20 9 0 51 10 0 1 2 1 0 1 3 1 1 49 1 50)} "reactive-fragment" {:upvalue-count 0 :arity 4 :constants ("create-comment" "island-fragment" "list" "effect" {:upvalue-count 4 :arity 0 :constants ("for-each" {:upvalue-count 0 :arity 1 :constants ("dom-remove") :bytecode (20 0 0 16 0 49 1 50)} "list" "dom-child-nodes" "dom-insert-after") :bytecode (51 1 0 18 0 52 0 0 2 5 52 2 0 0 19 0 5 18 1 48 0 33 28 0 18 2 48 0 17 0 20 3 0 16 0 48 1 19 0 5 20 4 0 18 3 16 0 49 2 32 1 0 2 50)}) :bytecode (20 0 0 1 1 0 48 1 17 4 52 2 0 0 17 5 20 3 0 51 4 0 1 5 1 0 1 1 1 4 48 1 5 16 4 50)} "render-list-item" {:upvalue-count 0 :arity 4 :constants ("lambda?" "render-lambda-dom" "list" "render-to-dom" "apply") :bytecode (16 0 52 0 0 1 33 20 0 20 1 0 16 0 16 1 52 2 0 1 16 2 16 3 49 4 32 21 0 20 3 0 16 0 16 1 52 2 0 1 52 4 0 2 16 2 16 3 49 3 50)} "extract-key" {:upvalue-count 0 :arity 2 :constants ("dom-get-attr" "key" "dom-remove-attr" "dom-get-data" "str" "__idx_") :bytecode (20 0 0 16 0 1 1 0 48 2 17 2 16 2 33 16 0 20 2 0 16 0 1 1 0 48 2 5 16 2 32 35 0 20 3 0 16 0 1 1 0 48 2 17 3 16 3 33 9 0 16 3 52 4 0 1 32 9 0 1 5 0 16 1 52 4 0 2 50)} "reactive-list" {:upvalue-count 0 :arity 4 :constants ("create-fragment" "create-comment" "island-list" "dict" "list" "dom-append" "effect" {:upvalue-count 8 :arity 0 :constants ("deref" "dom-parent" "dict" "list" "for-each-indexed" {:upvalue-count 7 :arity 2 :constants ("render-list-item" "extract-key" "not" "starts-with?" "__idx_" "dict-has?" "dict-set!" "dict-get" "append!") :bytecode (20 0 0 18 0 16 1 18 1 18 2 48 4 17 2 20 1 0 16 2 16 0 48 2 17 3 18 3 52 2 0 1 6 33 14 0 5 16 3 1 4 0 52 3 0 2 52 2 0 1 33 6 0 3 19 3 32 1 0 2 5 18 4 16 3 52 5 0 2 33 19 0 18 5 16 3 18 4 16 3 52 7 0 2 52 6 0 3 32 10 0 18 5 16 3 16 2 52 6 0 3 5 18 6 16 3 52 8 0 2 50)} "not" "dom-remove-children-after" "create-fragment" "for-each" {:upvalue-count 2 :arity 1 :constants ("dom-append" "dict-get") :bytecode (20 0 0 18 0 18 1 16 0 52 1 0 2 49 2 50)} "dom-insert-after" {:upvalue-count 2 :arity 1 :constants ("not" "dict-has?" "dom-remove" "dict-get") :bytecode (18 0 16 0 52 1 0 2 52 0 0 1 33 16 0 20 2 0 18 1 16 0 52 3 0 2 49 1 32 1 0 2 50)} {:upvalue-count 2 :arity 1 :constants ("dict-get" "dom-next-sibling" "not" "identical?" "dom-insert-after") :bytecode (18 0 16 0 52 0 0 2 17 1 20 1 0 18 1 48 1 17 2 16 1 16 2 52 3 0 2 52 2 0 1 33 12 0 20 4 0 18 1 16 1 48 2 32 1 0 2 5 16 1 19 1 50)} {:upvalue-count 6 :arity 2 :constants ("render-list-item" "extract-key" "dict-set!" "append!" "dom-append") :bytecode (20 0 0 18 0 16 1 18 1 18 2 48 4 17 2 20 1 0 16 2 16 0 48 2 17 3 18 3 16 3 16 2 52 2 0 3 5 18 4 16 3 52 3 0 2 5 20 4 0 18 5 16 2 49 2 50)}) :bytecode (20 0 0 18 0 48 1 17 0 20 1 0 18 1 48 1 33 133 0 52 2 0 0 17 1 52 3 0 0 17 2 4 17 3 51 5 0 0 2 0 3 0 4 1 3 0 5 1 1 1 2 16 0 52 4 0 2 5 16 3 52 6 0 1 33 41 0 20 7 0 18 1 48 1 5 20 8 0 48 0 17 4 51 10 0 1 4 1 1 16 2 52 9 0 2 5 20 11 0 18 1 16 4 48 2 32 31 0 51 12 0 1 1 0 5 18 6 52 9 0 2 5 18 1 17 4 51 13 0 1 1 1 4 16 2 52 9 0 2 5 16 1 19 5 5 16 2 19 6 32 21 0 51 14 0 0 2 0 3 0 4 0 5 0 6 0 7 16 0 52 4 0 2 50)}) :bytecode (20 0 0 48 0 17 4 20 1 0 1 2 0 48 1 17 5 52 3 0 0 17 6 52 4 0 0 17 7 20 5 0 16 4 16 5 48 2 5 20 6 0 51 7 0 1 1 1 5 1 0 1 2 1 3 1 6 1 7 1 4 48 1 5 16 4 50)} "bind-input" {:upvalue-count 0 :arity 2 :constants ("lower" "dom-get-attr" "type" "" "=" "checkbox" "radio" "dom-set-prop" "checked" "deref" "value" "str" "effect" {:upvalue-count 3 :arity 0 :constants ("dom-set-prop" "checked" "deref" "str" "!=" "dom-get-prop" "value") :bytecode (18 0 33 20 0 20 0 0 18 1 1 1 0 20 2 0 18 2 48 1 49 3 32 48 0 20 2 0 18 2 48 1 52 3 0 1 17 0 20 5 0 18 1 1 6 0 48 2 16 0 52 4 0 2 33 15 0 20 0 0 18 1 1 6 0 16 0 49 3 32 1 0 2 50)} "dom-on" "change" "input" {:upvalue-count 3 :arity 1 :constants ("reset!" "dom-get-prop" "checked" "value") :bytecode (18 0 33 20 0 20 0 0 18 1 20 1 0 18 2 1 2 0 48 2 49 2 32 17 0 20 0 0 18 1 20 1 0 18 2 1 3 0 48 2 49 2 50)}) :bytecode (20 1 0 16 0 1 2 0 48 2 6 34 4 0 5 1 3 0 52 0 0 1 17 2 16 2 1 5 0 52 4 0 2 6 34 10 0 5 16 2 1 6 0 52 4 0 2 17 3 16 3 33 20 0 20 7 0 16 0 1 8 0 20 9 0 16 1 48 1 48 3 32 21 0 20 7 0 16 0 1 10 0 20 9 0 16 1 48 1 52 11 0 1 48 3 5 20 12 0 51 13 0 1 3 1 0 1 1 48 1 5 20 14 0 16 0 16 3 33 6 0 1 15 0 32 3 0 1 16 0 51 17 0 1 3 1 1 1 0 49 3 50)} "*use-cek-reactive*" "enable-cek-reactive!" {:upvalue-count 0 :arity 0 :constants ("*use-cek-reactive*") :bytecode (3 21 0 0 50)} "cek-reactive-text" {:upvalue-count 0 :arity 2 :constants ("create-text-node" "" {:upvalue-count 1 :arity 1 :constants ("dom-set-text-content" "str") :bytecode (20 0 0 18 0 16 0 52 1 0 1 49 2 50)} "cek-run" "make-cek-state" "list" "make-reactive-reset-frame" "dom-set-text-content" "str") :bytecode (20 0 0 1 1 0 48 1 17 2 51 2 0 1 2 17 3 20 3 0 20 4 0 16 0 16 1 20 6 0 16 1 16 3 3 48 3 52 5 0 1 48 3 48 1 17 4 20 7 0 16 2 16 4 52 8 0 1 48 2 5 16 2 50)} "cek-reactive-attr" {:upvalue-count 0 :arity 4 :constants ({:upvalue-count 2 :arity 1 :constants ("nil?" "=" "dom-remove-attr" "dom-set-attr" "" "str") :bytecode (16 0 52 0 0 1 6 34 8 0 5 16 0 4 52 1 0 2 33 12 0 20 2 0 18 0 18 1 49 2 32 40 0 16 0 3 52 1 0 2 33 15 0 20 3 0 18 0 18 1 1 4 0 49 3 32 15 0 20 3 0 18 0 18 1 16 0 52 5 0 1 49 3 50)} "dom-get-attr" "data-sx-reactive-attrs" "" "empty?" "str" "," "dom-set-attr" "cek-run" "make-cek-state" "list" "make-reactive-reset-frame" "cek-call") :bytecode (51 0 0 1 0 1 1 17 4 20 1 0 16 0 1 2 0 48 2 6 34 4 0 5 1 3 0 17 5 16 5 52 4 0 1 33 5 0 16 1 32 11 0 16 5 1 6 0 16 1 52 5 0 3 17 6 20 7 0 16 0 1 2 0 16 6 48 3 5 20 8 0 20 9 0 16 2 16 3 20 11 0 16 3 16 4 3 48 3 52 10 0 1 48 3 48 1 17 5 16 4 16 5 52 10 0 1 52 12 0 2 50)} "render-dom-portal" {:upvalue-count 0 :arity 3 :constants ("trampoline" "eval-expr" "first" "dom-query" "dom-ensure-element" "not" "create-comment" "str" "portal: " " (not found)" "create-fragment" "for-each" {:upvalue-count 3 :arity 1 :constants ("dom-append" "render-to-dom") :bytecode (20 0 0 18 0 20 1 0 16 0 18 1 18 2 48 3 49 2 50)} "rest" "dom-child-nodes" "dom-append" "register-in-scope" {:upvalue-count 1 :arity 0 :constants ("for-each" {:upvalue-count 0 :arity 1 :constants ("dom-remove") :bytecode (20 0 0 16 0 49 1 50)}) :bytecode (51 1 0 18 0 52 0 0 2 50)}) :bytecode (16 0 52 2 0 1 16 1 52 1 0 2 52 0 0 1 17 3 20 3 0 16 3 48 1 6 34 8 0 5 20 4 0 16 3 48 1 17 4 16 4 52 5 0 1 33 20 0 20 6 0 1 8 0 16 3 1 9 0 52 7 0 3 49 1 32 75 0 20 6 0 1 8 0 16 3 52 7 0 2 48 1 17 5 20 10 0 48 0 17 6 51 12 0 1 6 1 1 1 2 16 0 52 13 0 1 52 11 0 2 5 20 14 0 16 6 48 1 17 7 20 15 0 16 4 16 6 48 2 5 20 16 0 51 17 0 1 7 48 1 5 16 5 50)} "render-dom-error-boundary" {:upvalue-count 0 :arity 3 :constants (">" "len" 1 "first" "rest" "dom-create-element" "div" "signal" 0 "dom-set-attr" "data-sx-boundary" "true" "effect" {:upvalue-count 6 :arity 0 :constants ("deref" "dom-set-prop" "innerHTML" "" "scope-push!" "sx-island-scope" "try-catch" {:upvalue-count 4 :arity 0 :constants ("create-fragment" "for-each" {:upvalue-count 3 :arity 1 :constants ("dom-append" "render-to-dom") :bytecode (20 0 0 18 0 20 1 0 16 0 18 1 18 2 48 3 49 2 50)} "dom-append" "scope-pop!" "sx-island-scope") :bytecode (20 0 0 48 0 17 0 51 2 0 1 0 0 0 0 1 18 2 52 1 0 2 5 20 3 0 18 3 16 0 48 2 5 1 5 0 52 4 0 1 50)} {:upvalue-count 5 :arity 1 :constants ("scope-pop!" "sx-island-scope" "trampoline" "eval-expr" {:upvalue-count 1 :arity 0 :constants ("swap!" {:upvalue-count 0 :arity 1 :constants ("+" 1) :bytecode (16 0 1 1 0 52 0 0 2 50)}) :bytecode (20 0 0 18 0 51 1 0 49 2 50)} "nil?" "dom-create-element" "div" "dom-set-attr" "class" "sx-render-error" "style" "color:red;font-size:0.875rem;padding:0.5rem;border:1px solid red;border-radius:0.25rem;margin:0.5rem 0;" "dom-set-text-content" "str" "Render error: " "lambda?" "render-lambda-dom" "list" "render-to-dom" "apply" "dom-append") :bytecode (1 1 0 52 0 0 1 5 18 0 18 1 52 3 0 2 52 2 0 1 17 1 51 4 0 0 2 17 2 16 1 52 5 0 1 33 61 0 20 6 0 1 7 0 2 48 2 17 4 20 8 0 16 4 1 9 0 1 10 0 48 3 5 20 8 0 16 4 1 11 0 1 12 0 48 3 5 20 13 0 16 4 1 15 0 16 0 52 14 0 2 48 2 5 16 4 32 54 0 16 1 52 16 0 1 33 22 0 20 17 0 16 1 16 0 16 2 52 18 0 2 18 1 18 3 48 4 32 23 0 20 19 0 16 1 16 0 16 2 52 18 0 2 52 20 0 2 18 1 18 3 48 3 17 3 20 21 0 18 4 16 3 49 2 50)}) :bytecode (20 0 0 18 0 48 1 5 20 1 0 18 1 1 2 0 1 3 0 48 3 5 1 5 0 2 52 4 0 2 5 51 7 0 0 2 0 3 0 4 0 1 51 8 0 0 5 0 2 0 0 0 3 0 1 52 6 0 2 50)}) :bytecode (16 0 52 1 0 1 1 2 0 52 0 0 2 33 9 0 16 0 52 3 0 1 32 1 0 2 17 3 16 0 52 1 0 1 1 2 0 52 0 0 2 33 9 0 16 0 52 4 0 1 32 2 0 16 0 17 4 20 5 0 1 6 0 2 48 2 17 5 20 7 0 1 8 0 48 1 17 6 20 9 0 16 5 1 10 0 1 11 0 48 3 5 20 12 0 51 13 0 1 6 1 5 1 1 1 2 1 4 1 3 48 1 5 16 5 50)}) :bytecode (1 1 0 128 0 0 5 1 3 0 128 2 0 5 51 5 0 128 4 0 5 52 7 0 0 128 6 0 5 1 9 0 128 8 0 5 51 11 0 128 10 0 5 51 13 0 128 12 0 5 51 15 0 128 14 0 5 51 17 0 128 16 0 5 51 19 0 128 18 0 5 51 21 0 128 20 0 5 51 23 0 128 22 0 5 51 25 0 128 24 0 5 51 27 0 128 26 0 5 51 29 0 128 28 0 5 1 32 0 1 33 0 1 34 0 1 35 0 1 36 0 1 37 0 1 38 0 1 39 0 1 40 0 1 41 0 1 42 0 1 43 0 1 44 0 1 45 0 1 46 0 1 47 0 1 48 0 1 49 0 1 50 0 1 51 0 1 52 0 1 53 0 1 54 0 52 31 0 23 128 30 0 5 51 56 0 128 55 0 5 51 58 0 128 57 0 5 51 60 0 128 59 0 5 51 62 0 128 61 0 5 51 64 0 128 63 0 5 51 66 0 128 65 0 5 51 68 0 128 67 0 5 51 70 0 128 69 0 5 51 72 0 128 71 0 5 51 74 0 128 73 0 5 51 76 0 128 75 0 5 51 78 0 128 77 0 5 51 80 0 128 79 0 5 51 82 0 128 81 0 5 3 128 83 0 5 51 85 0 128 84 0 5 51 87 0 128 86 0 5 51 89 0 128 88 0 5 51 91 0 128 90 0 5 51 93 0 128 92 0 50))) diff --git a/shared/static/wasm/sx/adapter-html.sx b/shared/static/wasm/sx/adapter-html.sx index 46a232c6..1b587b5e 100644 --- a/shared/static/wasm/sx/adapter-html.sx +++ b/shared/static/wasm/sx/adapter-html.sx @@ -1,5 +1,7 @@ +(import (sx render)) + (define-library (web adapter-html) (export render-to-html diff --git a/shared/static/wasm/sx/adapter-html.sxbc b/shared/static/wasm/sx/adapter-html.sxbc index e43f952b..c254e422 100644 --- a/shared/static/wasm/sx/adapter-html.sxbc +++ b/shared/static/wasm/sx/adapter-html.sxbc @@ -1,3 +1,3 @@ -(sxbc 1 "fceab142ebece9b0" +(sxbc 1 "f9327868ed83a255" (code - :constants ("define-library" "web" "adapter-html" "export" "render-to-html" "render-value-to-html" "RENDER_HTML_FORMS" "render-html-form?" "render-list-to-html" "dispatch-html-form" "render-lambda-html" "render-html-component" "render-html-element" "render-html-lake" "render-html-marsh" "render-html-island" "serialize-island-state" {:upvalue-count 0 :arity 2 :constants ("set-render-active!" "type-of" "nil" "=" "" "string" "escape-html" "number" "str" "boolean" "true" "false" "list" "empty?" "render-list-to-html" "symbol" "render-value-to-html" "trampoline" "eval-expr" "keyword" "keyword-name" "raw-html" "raw-html-content" "spread" "scope-emit!" "element-attrs" "spread-attrs" "thunk" "render-to-html" "thunk-expr" "thunk-env") :bytecode (3 52 0 0 1 5 16 0 52 1 0 1 6 1 2 0 52 3 0 2 33 7 0 5 1 4 0 32 20 1 6 1 5 0 52 3 0 2 33 11 0 5 20 6 0 16 0 49 1 32 254 0 6 1 7 0 52 3 0 2 33 10 0 5 16 0 52 8 0 1 32 233 0 6 1 9 0 52 3 0 2 33 18 0 5 16 0 33 6 0 1 10 0 32 3 0 1 11 0 32 204 0 6 1 12 0 52 3 0 2 33 28 0 5 16 0 52 13 0 1 33 6 0 1 4 0 32 9 0 20 14 0 16 0 16 1 49 2 32 165 0 6 1 15 0 52 3 0 2 33 23 0 5 20 16 0 16 0 16 1 52 18 0 2 52 17 0 1 16 1 49 2 32 131 0 6 1 19 0 52 3 0 2 33 15 0 5 20 6 0 16 0 52 20 0 1 49 1 32 105 0 6 1 21 0 52 3 0 2 33 10 0 5 16 0 52 22 0 1 32 84 0 6 1 23 0 52 3 0 2 33 21 0 5 1 25 0 16 0 52 26 0 1 52 24 0 2 5 1 4 0 32 52 0 6 1 27 0 52 3 0 2 33 21 0 5 20 28 0 16 0 52 29 0 1 16 0 52 30 0 1 49 2 32 20 0 5 20 16 0 16 0 16 1 52 18 0 2 52 17 0 1 16 1 49 2 50)} {:upvalue-count 0 :arity 2 :constants ("type-of" "nil" "=" "" "string" "escape-html" "number" "str" "boolean" "true" "false" "list" "render-list-to-html" "raw-html" "raw-html-content" "spread" "scope-emit!" "element-attrs" "spread-attrs" "thunk" "render-to-html" "thunk-expr" "thunk-env") :bytecode (16 0 52 0 0 1 6 1 1 0 52 2 0 2 33 7 0 5 1 3 0 32 193 0 6 1 4 0 52 2 0 2 33 11 0 5 20 5 0 16 0 49 1 32 171 0 6 1 6 0 52 2 0 2 33 10 0 5 16 0 52 7 0 1 32 150 0 6 1 8 0 52 2 0 2 33 18 0 5 16 0 33 6 0 1 9 0 32 3 0 1 10 0 32 121 0 6 1 11 0 52 2 0 2 33 13 0 5 20 12 0 16 0 16 1 49 2 32 97 0 6 1 13 0 52 2 0 2 33 10 0 5 16 0 52 14 0 1 32 76 0 6 1 15 0 52 2 0 2 33 21 0 5 1 17 0 16 0 52 18 0 1 52 16 0 2 5 1 3 0 32 44 0 6 1 19 0 52 2 0 2 33 21 0 5 20 20 0 16 0 52 21 0 1 16 0 52 22 0 1 49 2 32 12 0 5 20 5 0 16 0 52 7 0 1 49 1 50)} "list" "if" "when" "cond" "case" "let" "let*" "letrec" "begin" "do" "define" "defcomp" "defisland" "defmacro" "defstyle" "deftype" "defeffect" "map" "map-indexed" "filter" "for-each" "scope" "provide" {:upvalue-count 0 :arity 1 :constants ("contains?" "RENDER_HTML_FORMS") :bytecode (20 1 0 16 0 52 0 0 2 50)} {:upvalue-count 0 :arity 2 :constants ("empty?" "" "first" "not" "=" "type-of" "symbol" "join" "map" {:upvalue-count 1 :arity 1 :constants ("render-value-to-html") :bytecode (20 0 0 16 0 18 0 49 2 50)} "symbol-name" "rest" "<>" {:upvalue-count 1 :arity 1 :constants ("render-to-html") :bytecode (20 0 0 16 0 18 0 49 2 50)} "raw!" {:upvalue-count 1 :arity 1 :constants ("str" "trampoline" "eval-expr") :bytecode (16 0 18 0 52 2 0 2 52 1 0 1 52 0 0 1 50)} "lake" "render-html-lake" "marsh" "render-html-marsh" "error-boundary" ">" "len" 1 "str" "