// wasm/app/runner.js // Tier runner — wraps the three loaders. Exposes a per-tier API so the // SPA can iterate, time, and render a live elapsed-ms counter between // the eval start and finish. import { createPythonTier } from "./python/lumbda-py.js"; import { createCTier } from "./c/lumbda-c.loader.js"; import { createAsmTier } from "./asm/lumbda-asm.loader.js"; const cache = {}; const config = { bendUrl: null, pauseFlag: null }; export function setBendUrl(url) { config.bendUrl = url || null; // Propagate to already-loaded tiers. for (const t of Object.values(cache)) { if (t && t.setBendUrl) t.setBendUrl(config.bendUrl); } } // SharedArrayBuffer that the tier's eval loop polls for a pause // signal. Main thread writes 1 to atomic index 0; the tier's polling // hook (c/eval.c lumbda_check_pause) raises lisp_error("paused") at // the next K-iteration boundary so module-global env is preserved // and (portal-snapshot!) afterward captures everything that ran // before the pause. export function setPauseFlag(flag) { config.pauseFlag = flag || null; for (const t of Object.values(cache)) { if (t && t.setPauseFlag) t.setPauseFlag(config.pauseFlag); } } export async function getTier(name, onLoad) { if (cache[name]) return cache[name]; if (onLoad) onLoad(name); if (name === "python") cache[name] = await createPythonTier(); else if (name === "c") cache[name] = await createCTier(); else if (name === "asm") cache[name] = await createAsmTier(); else throw new Error(`unknown tier: ${name}`); if (cache[name].setBendUrl) cache[name].setBendUrl(config.bendUrl); if (cache[name].setPauseFlag) cache[name].setPauseFlag(config.pauseFlag); return cache[name]; } export async function evalOnTier(name, src, onLoad, onChunk) { const tier = await getTier(name, onLoad); return tier.evalLisp(src, onChunk); } export function heapStats(name) { const tier = cache[name]; if (!tier || !tier.heapStats) return null; return tier.heapStats(); } // Portal — REPL save/resume bridge. portalSave reads a snapshot blob // from the tier (MEMFS on the C tier, similar bridges on others as // they land). Returns null when the tier hasn't implemented portals // yet (asm-wat today) so the caller can surface a friendly message // instead of crashing. portalLoad is the symmetric write-in path. export function portalSave(name, checkpointName) { const tier = cache[name]; if (!tier || !tier.portalSave) return null; return tier.portalSave(checkpointName); } export function portalLoad(name, checkpointName, blob) { const tier = cache[name]; if (!tier || !tier.portalLoad) return false; tier.portalLoad(checkpointName, blob); return true; }