Add live CEK stepper island — interactive stepping debugger
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 33s

A defisland that lets users type an SX expression, step through CEK
evaluation one transition at a time, and see C/E/K registers update
live. Demonstrates that cek-step is pure data->data.

- cek.sx geography: add ~geography/cek/demo-stepper island with
  source input, step/run/reset buttons, state display, step history
- platform_js.py: register CEK stepping primitives (make-cek-state,
  cek-step, cek-terminal?, cek-value, make-env, sx-serialize) so
  island code can access them

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-14 13:31:52 +00:00
parent 0047757af8
commit cf017a2c89
4 changed files with 530 additions and 150 deletions

View File

@@ -1508,6 +1508,35 @@ CEK_FIXUPS_JS = '''
while (!cekTerminal_p(state)) { state = cekStep(state); }
return cekValue(state);
};
// Expose spec functions so evaluated SX code can use them.
// Type inspection (platform interface from boundary.sx)
PRIMITIVES["type-of"] = typeOf;
PRIMITIVES["symbol-name"] = symbolName;
PRIMITIVES["keyword-name"] = keywordName;
PRIMITIVES["callable?"] = isCallable;
PRIMITIVES["lambda?"] = isLambda;
PRIMITIVES["lambda-name"] = lambdaName;
PRIMITIVES["component?"] = isComponent;
PRIMITIVES["island?"] = isIsland;
PRIMITIVES["make-symbol"] = function(n) { return new Symbol(n); };
// Parser (from parser.sx)
PRIMITIVES["sx-serialize"] = sxSerialize;
// CEK machine (from cek.sx/frames.sx)
PRIMITIVES["make-cek-state"] = makeCekState;
PRIMITIVES["cek-step"] = cekStep;
PRIMITIVES["cek-run"] = cekRun;
PRIMITIVES["cek-terminal?"] = cekTerminal_p;
PRIMITIVES["cek-value"] = cekValue;
PRIMITIVES["eval-expr-cek"] = evalExprCek;
// Render (from adapter-html.sx / render.sx)
PRIMITIVES["is-html-tag?"] = function(n) { return HTML_TAGS.indexOf(n) >= 0; };
// Environment (for creating eval contexts)
PRIMITIVES["make-env"] = function() { return merge(componentEnv, PRIMITIVES); };
'''
@@ -3033,6 +3062,8 @@ def fixups_js(has_html, has_sx, has_dom, has_signals=False, has_deps=False, has_
if (typeof domOuterHtml === "function") PRIMITIVES["dom-outer-html"] = domOuterHtml;
if (typeof domInnerHtml === "function") PRIMITIVES["dom-inner-html"] = domInnerHtml;
if (typeof domTextContent === "function") PRIMITIVES["dom-text-content"] = domTextContent;
if (typeof domCreateElement === "function") PRIMITIVES["dom-create-element"] = domCreateElement;
if (typeof domAppend === "function") PRIMITIVES["dom-append"] = domAppend;
if (typeof jsonParse === "function") PRIMITIVES["json-parse"] = jsonParse;
if (typeof nowMs === "function") PRIMITIVES["now-ms"] = nowMs;
PRIMITIVES["sx-parse"] = sxParse;

View File

@@ -216,13 +216,15 @@ def compile_ref_to_js(
# Platform JS for selected adapters
if not has_dom:
parts.append("\n var _hasDom = false;\n")
for name in ("dom", "engine", "orchestration", "boot"):
if name in adapter_set and name in adapter_platform:
parts.append(adapter_platform[name])
# CEK fixups + general fixups BEFORE boot (boot hydrates islands that need these)
parts.append(fixups_js(has_html, has_sx, has_dom, has_signals, has_deps, has_page_helpers))
if has_cek:
parts.append(CEK_FIXUPS_JS)
for name in ("dom", "engine", "orchestration", "boot"):
if name in adapter_set and name in adapter_platform:
parts.append(adapter_platform[name])
if has_continuations:
parts.append(CONTINUATIONS_JS)
if has_dom: