diff --git a/shared/static/scripts/sx-browser.js b/shared/static/scripts/sx-browser.js index d063988..23a9e74 100644 --- a/shared/static/scripts/sx-browser.js +++ b/shared/static/scripts/sx-browser.js @@ -14,7 +14,7 @@ // ========================================================================= var NIL = Object.freeze({ _nil: true, toString: function() { return "nil"; } }); - var SX_VERSION = "2026-03-13T23:56:11Z"; + var SX_VERSION = "2026-03-14T01:00:32Z"; function isNil(x) { return x === NIL || x === null || x === undefined; } function isSxTruthy(x) { return x !== false && !isNil(x); } @@ -2428,6 +2428,37 @@ return (isSxTruthy(testFn()) ? (function() { return domListen(el, (isSxTruthy(isCheckbox) ? "change" : "input"), function(e) { return (isSxTruthy(isCheckbox) ? reset_b(sig, domGetProp(el, "checked")) : reset_b(sig, domGetProp(el, "value"))); }); })(); }; + // *use-cek-reactive* + var _useCekReactive = true; + + // enable-cek-reactive! + var enableCekReactive = function() { return (_useCekReactive = true); }; + + // cek-reactive-text + var cekReactiveText = function(expr, env) { return (function() { + var node = createTextNode(""); + var updateFn = function(val) { return domSetTextContent(node, (String(val))); }; + return (function() { + var initial = cekRun(makeCekState(expr, env, [makeReactiveResetFrame(env, updateFn, true)])); + domSetTextContent(node, (String(initial))); + return node; +})(); +})(); }; + + // cek-reactive-attr + var cekReactiveAttr = function(el, attrName, expr, env) { return (function() { + var updateFn = function(val) { return (isSxTruthy(sxOr(isNil(val), (val == false))) ? domRemoveAttr(el, attrName) : (isSxTruthy((val == true)) ? domSetAttr(el, attrName, "") : domSetAttr(el, attrName, (String(val))))); }; + (function() { + var existing = sxOr(domGetAttr(el, "data-sx-reactive-attrs"), ""); + var updated = (isSxTruthy(isEmpty(existing)) ? attrName : (String(existing) + String(",") + String(attrName))); + return domSetAttr(el, "data-sx-reactive-attrs", updated); +})(); + return (function() { + var initial = cekRun(makeCekState(expr, env, [makeReactiveResetFrame(env, updateFn, true)])); + return invoke(updateFn, initial); +})(); +})(); }; + // render-dom-portal var renderDomPortal = function(args, env, ns) { return (function() { var selector = trampoline(evalExpr(first(args), env)); @@ -3812,6 +3843,134 @@ callExpr.push(dictGet(kwargs, k)); } } })(); }, keys(env)); }; + // === Transpiled from frames (CEK continuation frames) === + + // make-cek-state + var makeCekState = function(control, env, kont) { return {"control": control, "env": env, "kont": kont, "phase": "eval", "value": NIL}; }; + + // make-cek-value + var makeCekValue = function(value, env, kont) { return {"control": NIL, "env": env, "kont": kont, "phase": "continue", "value": value}; }; + + // cek-terminal? + var cekTerminal_p = function(state) { return (isSxTruthy((get(state, "phase") == "continue")) && isEmpty(get(state, "kont"))); }; + + // cek-control + var cekControl = function(s) { return get(s, "control"); }; + + // cek-env + var cekEnv = function(s) { return get(s, "env"); }; + + // cek-kont + var cekKont = function(s) { return get(s, "kont"); }; + + // cek-phase + var cekPhase = function(s) { return get(s, "phase"); }; + + // cek-value + var cekValue = function(s) { return get(s, "value"); }; + + // make-if-frame + var makeIfFrame = function(thenExpr, elseExpr, env) { return {"type": "if", "then": thenExpr, "else": elseExpr, "env": env}; }; + + // make-when-frame + var makeWhenFrame = function(bodyExprs, env) { return {"type": "when", "body": bodyExprs, "env": env}; }; + + // make-begin-frame + var makeBeginFrame = function(remaining, env) { return {"type": "begin", "remaining": remaining, "env": env}; }; + + // make-let-frame + var makeLetFrame = function(name, remaining, body, local) { return {"type": "let", "name": name, "remaining": remaining, "body": body, "env": local}; }; + + // make-define-frame + var makeDefineFrame = function(name, env, hasEffects, effectList) { return {"type": "define", "name": name, "env": env, "has-effects": hasEffects, "effect-list": effectList}; }; + + // make-set-frame + var makeSetFrame = function(name, env) { return {"type": "set", "name": name, "env": env}; }; + + // make-arg-frame + var makeArgFrame = function(f, evaled, remaining, env, rawArgs) { return {"type": "arg", "f": f, "evaled": evaled, "remaining": remaining, "env": env, "raw-args": rawArgs}; }; + + // make-call-frame + var makeCallFrame = function(f, args, env) { return {"type": "call", "f": f, "args": args, "env": env}; }; + + // make-cond-frame + var makeCondFrame = function(remaining, env, scheme_p) { return {"type": "cond", "remaining": remaining, "env": env, "scheme": scheme_p}; }; + + // make-case-frame + var makeCaseFrame = function(matchVal, remaining, env) { return {"type": "case", "match-val": matchVal, "remaining": remaining, "env": env}; }; + + // make-thread-frame + var makeThreadFrame = function(remaining, env) { return {"type": "thread", "remaining": remaining, "env": env}; }; + + // make-map-frame + var makeMapFrame = function(f, remaining, results, env) { return {"type": "map", "f": f, "remaining": remaining, "results": results, "env": env}; }; + + // make-filter-frame + var makeFilterFrame = function(f, remaining, results, currentItem, env) { return {"type": "filter", "f": f, "remaining": remaining, "results": results, "current-item": currentItem, "env": env}; }; + + // make-reduce-frame + var makeReduceFrame = function(f, remaining, env) { return {"type": "reduce", "f": f, "remaining": remaining, "env": env}; }; + + // make-for-each-frame + var makeForEachFrame = function(f, remaining, env) { return {"type": "for-each", "f": f, "remaining": remaining, "env": env}; }; + + // make-scope-frame + var makeScopeFrame = function(name, remaining, env) { return {"type": "scope", "name": name, "remaining": remaining, "env": env}; }; + + // make-reset-frame + var makeResetFrame = function(env) { return {"type": "reset", "env": env}; }; + + // make-dict-frame + var makeDictFrame = function(remaining, results, env) { return {"type": "dict", "remaining": remaining, "results": results, "env": env}; }; + + // make-and-frame + var makeAndFrame = function(remaining, env) { return {"type": "and", "remaining": remaining, "env": env}; }; + + // make-or-frame + var makeOrFrame = function(remaining, env) { return {"type": "or", "remaining": remaining, "env": env}; }; + + // make-dynamic-wind-frame + var makeDynamicWindFrame = function(phase, bodyThunk, afterThunk, env) { return {"type": "dynamic-wind", "phase": phase, "body-thunk": bodyThunk, "after-thunk": afterThunk, "env": env}; }; + + // make-reactive-reset-frame + var makeReactiveResetFrame = function(env, updateFn, firstRender_p) { return {"type": "reactive-reset", "env": env, "update-fn": updateFn, "first-render": firstRender_p}; }; + + // make-deref-frame + var makeDerefFrame = function(env) { return {"type": "deref", "env": env}; }; + + // frame-type + var frameType = function(f) { return get(f, "type"); }; + + // kont-push + var kontPush = function(frame, kont) { return cons(frame, kont); }; + + // kont-top + var kontTop = function(kont) { return first(kont); }; + + // kont-pop + var kontPop = function(kont) { return rest(kont); }; + + // kont-empty? + var kontEmpty_p = function(kont) { return isEmpty(kont); }; + + // kont-capture-to-reset + var kontCaptureToReset = function(kont) { var scan = function(k, captured) { return (isSxTruthy(isEmpty(k)) ? error("shift without enclosing reset") : (function() { + var frame = first(k); + return (isSxTruthy(sxOr((frameType(frame) == "reset"), (frameType(frame) == "reactive-reset"))) ? [captured, rest(k)] : scan(rest(k), append(captured, [frame]))); +})()); }; +return scan(kont, []); }; + + // has-reactive-reset-frame? + var hasReactiveResetFrame_p = function(kont) { return (isSxTruthy(isEmpty(kont)) ? false : (isSxTruthy((frameType(first(kont)) == "reactive-reset")) ? true : hasReactiveResetFrame_p(rest(kont)))); }; + + // kont-capture-to-reactive-reset + var kontCaptureToReactiveReset = function(kont) { var scan = function(k, captured) { return (isSxTruthy(isEmpty(k)) ? error("reactive deref without enclosing reactive-reset") : (function() { + var frame = first(k); + return (isSxTruthy((frameType(frame) == "reactive-reset")) ? [captured, frame, rest(k)] : scan(rest(k), append(captured, [frame]))); +})()); }; +return scan(kont, []); }; + + // === Transpiled from page-helpers (pure data transformation helpers) === // special-form-category-map @@ -4444,6 +4603,383 @@ return (function() { })(); }; + // === Transpiled from cek (explicit CEK machine evaluator) === + + // cek-run + var cekRun = function(state) { return (isSxTruthy(cekTerminal_p(state)) ? cekValue(state) : cekRun(cekStep(state))); }; + + // cek-step + var cekStep = function(state) { return (isSxTruthy((cekPhase(state) == "eval")) ? stepEval(state) : stepContinue(state)); }; + + // step-eval + var stepEval = function(state) { return (function() { + var expr = cekControl(state); + var env = cekEnv(state); + var kont = cekKont(state); + return (function() { var _m = typeOf(expr); if (_m == "number") return makeCekValue(expr, env, kont); if (_m == "string") return makeCekValue(expr, env, kont); if (_m == "boolean") return makeCekValue(expr, env, kont); if (_m == "nil") return makeCekValue(NIL, env, kont); if (_m == "symbol") return (function() { + var name = symbolName(expr); + return (function() { + var val = (isSxTruthy(envHas(env, name)) ? envGet(env, name) : (isSxTruthy(isPrimitive(name)) ? getPrimitive(name) : (isSxTruthy((name == "true")) ? true : (isSxTruthy((name == "false")) ? false : (isSxTruthy((name == "nil")) ? NIL : error((String("Undefined symbol: ") + String(name)))))))); + return makeCekValue(val, env, kont); +})(); +})(); if (_m == "keyword") return makeCekValue(keywordName(expr), env, kont); if (_m == "dict") return (function() { + var ks = keys(expr); + return (isSxTruthy(isEmpty(ks)) ? makeCekValue({}, env, kont) : (function() { + var firstKey = first(ks); + var remainingEntries = []; + { var _c = rest(ks); for (var _i = 0; _i < _c.length; _i++) { var k = _c[_i]; remainingEntries.push([k, get(expr, k)]); } } + return makeCekState(get(expr, firstKey), env, kontPush(makeDictFrame(remainingEntries, [[firstKey]], env), kont)); +})()); +})(); if (_m == "list") return (isSxTruthy(isEmpty(expr)) ? makeCekValue([], env, kont) : stepEvalList(expr, env, kont)); return makeCekValue(expr, env, kont); })(); +})(); }; + + // step-eval-list + var stepEvalList = function(expr, env, kont) { return (function() { + var head = first(expr); + var args = rest(expr); + return (isSxTruthy(!isSxTruthy(sxOr((typeOf(head) == "symbol"), (typeOf(head) == "lambda"), (typeOf(head) == "list")))) ? (isSxTruthy(isEmpty(expr)) ? makeCekValue([], env, kont) : makeCekState(first(expr), env, kontPush(makeMapFrame(NIL, rest(expr), [], env), kont))) : (isSxTruthy((typeOf(head) == "symbol")) ? (function() { + var name = symbolName(head); + return (isSxTruthy((name == "if")) ? stepSfIf(args, env, kont) : (isSxTruthy((name == "when")) ? stepSfWhen(args, env, kont) : (isSxTruthy((name == "cond")) ? stepSfCond(args, env, kont) : (isSxTruthy((name == "case")) ? stepSfCase(args, env, kont) : (isSxTruthy((name == "and")) ? stepSfAnd(args, env, kont) : (isSxTruthy((name == "or")) ? stepSfOr(args, env, kont) : (isSxTruthy((name == "let")) ? stepSfLet(args, env, kont) : (isSxTruthy((name == "let*")) ? stepSfLet(args, env, kont) : (isSxTruthy((name == "lambda")) ? stepSfLambda(args, env, kont) : (isSxTruthy((name == "fn")) ? stepSfLambda(args, env, kont) : (isSxTruthy((name == "define")) ? stepSfDefine(args, env, kont) : (isSxTruthy((name == "defcomp")) ? makeCekValue(sfDefcomp(args, env), env, kont) : (isSxTruthy((name == "defisland")) ? makeCekValue(sfDefisland(args, env), env, kont) : (isSxTruthy((name == "defmacro")) ? makeCekValue(sfDefmacro(args, env), env, kont) : (isSxTruthy((name == "defstyle")) ? makeCekValue(sfDefstyle(args, env), env, kont) : (isSxTruthy((name == "defhandler")) ? makeCekValue(sfDefhandler(args, env), env, kont) : (isSxTruthy((name == "defpage")) ? makeCekValue(sfDefpage(args, env), env, kont) : (isSxTruthy((name == "defquery")) ? makeCekValue(sfDefquery(args, env), env, kont) : (isSxTruthy((name == "defaction")) ? makeCekValue(sfDefaction(args, env), env, kont) : (isSxTruthy((name == "deftype")) ? makeCekValue(sfDeftype(args, env), env, kont) : (isSxTruthy((name == "defeffect")) ? makeCekValue(sfDefeffect(args, env), env, kont) : (isSxTruthy((name == "begin")) ? stepSfBegin(args, env, kont) : (isSxTruthy((name == "do")) ? stepSfBegin(args, env, kont) : (isSxTruthy((name == "quote")) ? makeCekValue((isSxTruthy(isEmpty(args)) ? NIL : first(args)), env, kont) : (isSxTruthy((name == "quasiquote")) ? makeCekValue(qqExpand(first(args), env), env, kont) : (isSxTruthy((name == "->")) ? stepSfThreadFirst(args, env, kont) : (isSxTruthy((name == "set!")) ? stepSfSet(args, env, kont) : (isSxTruthy((name == "letrec")) ? makeCekValue(sfLetrec(args, env), env, kont) : (isSxTruthy((name == "reset")) ? stepSfReset(args, env, kont) : (isSxTruthy((name == "shift")) ? stepSfShift(args, env, kont) : (isSxTruthy((name == "deref")) ? stepSfDeref(args, env, kont) : (isSxTruthy((name == "scope")) ? stepSfScope(args, env, kont) : (isSxTruthy((name == "provide")) ? stepSfProvide(args, env, kont) : (isSxTruthy((name == "dynamic-wind")) ? makeCekValue(sfDynamicWind(args, env), env, kont) : (isSxTruthy((name == "map")) ? stepHoMap(args, env, kont) : (isSxTruthy((name == "map-indexed")) ? makeCekValue(hoMapIndexed(args, env), env, kont) : (isSxTruthy((name == "filter")) ? stepHoFilter(args, env, kont) : (isSxTruthy((name == "reduce")) ? stepHoReduce(args, env, kont) : (isSxTruthy((name == "some")) ? makeCekValue(hoSome(args, env), env, kont) : (isSxTruthy((name == "every?")) ? makeCekValue(hoEvery(args, env), env, kont) : (isSxTruthy((name == "for-each")) ? stepHoForEach(args, env, kont) : (isSxTruthy((isSxTruthy(envHas(env, name)) && isMacro(envGet(env, name)))) ? (function() { + var mac = envGet(env, name); + return makeCekState(expandMacro(mac, args, env), env, kont); +})() : (isSxTruthy((isSxTruthy(renderActiveP()) && isRenderExpr(expr))) ? makeCekValue(renderExpr(expr, env), env, kont) : stepEvalCall(head, args, env, kont)))))))))))))))))))))))))))))))))))))))))))); +})() : stepEvalCall(head, args, env, kont))); +})(); }; + + // step-sf-if + var stepSfIf = function(args, env, kont) { return makeCekState(first(args), env, kontPush(makeIfFrame(nth(args, 1), (isSxTruthy((len(args) > 2)) ? nth(args, 2) : NIL), env), kont)); }; + + // step-sf-when + var stepSfWhen = function(args, env, kont) { return makeCekState(first(args), env, kontPush(makeWhenFrame(rest(args), env), kont)); }; + + // step-sf-begin + var stepSfBegin = function(args, env, kont) { return (isSxTruthy(isEmpty(args)) ? makeCekValue(NIL, env, kont) : (isSxTruthy((len(args) == 1)) ? makeCekState(first(args), env, kont) : makeCekState(first(args), env, kontPush(makeBeginFrame(rest(args), env), kont)))); }; + + // step-sf-let + var stepSfLet = function(args, env, kont) { return (isSxTruthy((typeOf(first(args)) == "symbol")) ? makeCekValue(sfNamedLet(args, env), env, kont) : (function() { + var bindings = first(args); + var body = rest(args); + var local = envExtend(env); + return (isSxTruthy(isEmpty(bindings)) ? stepSfBegin(body, local, kont) : (function() { + var firstBinding = (isSxTruthy((isSxTruthy((typeOf(first(bindings)) == "list")) && (len(first(bindings)) == 2))) ? first(bindings) : [first(bindings), nth(bindings, 1)]); + var restBindings = (isSxTruthy((isSxTruthy((typeOf(first(bindings)) == "list")) && (len(first(bindings)) == 2))) ? rest(bindings) : (function() { + var pairs = []; + reduce(function(acc, i) { return append_b(pairs, [nth(bindings, (i * 2)), nth(bindings, ((i * 2) + 1))]); }, NIL, range(1, (len(bindings) / 2))); + return pairs; +})()); + return (function() { + var vname = (isSxTruthy((typeOf(first(firstBinding)) == "symbol")) ? symbolName(first(firstBinding)) : first(firstBinding)); + return makeCekState(nth(firstBinding, 1), local, kontPush(makeLetFrame(vname, restBindings, body, local), kont)); +})(); +})()); +})()); }; + + // step-sf-define + var stepSfDefine = function(args, env, kont) { return (function() { + var nameSym = first(args); + var hasEffects = (isSxTruthy((len(args) >= 4)) && isSxTruthy((typeOf(nth(args, 1)) == "keyword")) && (keywordName(nth(args, 1)) == "effects")); + var valIdx = (isSxTruthy((isSxTruthy((len(args) >= 4)) && isSxTruthy((typeOf(nth(args, 1)) == "keyword")) && (keywordName(nth(args, 1)) == "effects"))) ? 3 : 1); + var effectList = (isSxTruthy((isSxTruthy((len(args) >= 4)) && isSxTruthy((typeOf(nth(args, 1)) == "keyword")) && (keywordName(nth(args, 1)) == "effects"))) ? nth(args, 2) : NIL); + return makeCekState(nth(args, valIdx), env, kontPush(makeDefineFrame(symbolName(nameSym), env, hasEffects, effectList), kont)); +})(); }; + + // step-sf-set! + var stepSfSet = function(args, env, kont) { return makeCekState(nth(args, 1), env, kontPush(makeSetFrame(symbolName(first(args)), env), kont)); }; + + // step-sf-and + var stepSfAnd = function(args, env, kont) { return (isSxTruthy(isEmpty(args)) ? makeCekValue(true, env, kont) : makeCekState(first(args), env, kontPush(makeAndFrame(rest(args), env), kont))); }; + + // step-sf-or + var stepSfOr = function(args, env, kont) { return (isSxTruthy(isEmpty(args)) ? makeCekValue(false, env, kont) : makeCekState(first(args), env, kontPush(makeOrFrame(rest(args), env), kont))); }; + + // step-sf-cond + var stepSfCond = function(args, env, kont) { return (function() { + var scheme_p = condScheme_p(args); + return (isSxTruthy(scheme_p) ? (isSxTruthy(isEmpty(args)) ? makeCekValue(NIL, env, kont) : (function() { + var clause = first(args); + var test = first(clause); + return (isSxTruthy(sxOr((isSxTruthy((typeOf(test) == "symbol")) && sxOr((symbolName(test) == "else"), (symbolName(test) == ":else"))), (isSxTruthy((typeOf(test) == "keyword")) && (keywordName(test) == "else")))) ? makeCekState(nth(clause, 1), env, kont) : makeCekState(test, env, kontPush(makeCondFrame(args, env, true), kont))); +})()) : (isSxTruthy((len(args) < 2)) ? makeCekValue(NIL, env, kont) : (function() { + var test = first(args); + return (isSxTruthy(sxOr((isSxTruthy((typeOf(test) == "keyword")) && (keywordName(test) == "else")), (isSxTruthy((typeOf(test) == "symbol")) && sxOr((symbolName(test) == "else"), (symbolName(test) == ":else"))))) ? makeCekState(nth(args, 1), env, kont) : makeCekState(test, env, kontPush(makeCondFrame(args, env, false), kont))); +})())); +})(); }; + + // step-sf-case + var stepSfCase = function(args, env, kont) { return makeCekState(first(args), env, kontPush(makeCaseFrame(NIL, rest(args), env), kont)); }; + + // step-sf-thread-first + var stepSfThreadFirst = function(args, env, kont) { return makeCekState(first(args), env, kontPush(makeThreadFrame(rest(args), env), kont)); }; + + // step-sf-lambda + var stepSfLambda = function(args, env, kont) { return makeCekValue(sfLambda(args, env), env, kont); }; + + // step-sf-scope + var stepSfScope = function(args, env, kont) { return makeCekValue(sfScope(args, env), env, kont); }; + + // step-sf-provide + var stepSfProvide = function(args, env, kont) { return makeCekValue(sfProvide(args, env), env, kont); }; + + // step-sf-reset + var stepSfReset = function(args, env, kont) { return makeCekState(first(args), env, kontPush(makeResetFrame(env), kont)); }; + + // step-sf-shift + var stepSfShift = function(args, env, kont) { return (function() { + var kName = symbolName(first(args)); + var body = nth(args, 1); + var capturedResult = kontCaptureToReset(kont); + var captured = first(capturedResult); + var restKont = nth(capturedResult, 1); + return (function() { + var k = makeCekContinuation(captured, restKont); + return (function() { + var shiftEnv = envExtend(env); + envSet(shiftEnv, kName, k); + return makeCekState(body, shiftEnv, restKont); +})(); +})(); +})(); }; + + // step-sf-deref + var stepSfDeref = function(args, env, kont) { return makeCekState(first(args), env, kontPush(makeDerefFrame(env), kont)); }; + + // reactive-shift-deref + var reactiveShiftDeref = function(sig, env, kont) { return (function() { + var scanResult = kontCaptureToReactiveReset(kont); + var capturedFrames = first(scanResult); + var resetFrame = nth(scanResult, 1); + var remainingKont = nth(scanResult, 2); + var updateFn = get(resetFrame, "update-fn"); + return (function() { + var subDisposers = []; + return (function() { + var subscriber = function() { { var _c = subDisposers; for (var _i = 0; _i < _c.length; _i++) { var d = _c[_i]; invoke(d); } } +subDisposers = []; +return (function() { + var newReset = makeReactiveResetFrame(env, updateFn, false); + var newKont = concat(capturedFrames, [newReset], remainingKont); + return withIslandScope(function(d) { return append_b(subDisposers, d); }, function() { return cekRun(makeCekValue(signalValue(sig), env, newKont)); }); +})(); }; + signalAddSub(sig, subscriber); + registerInScope(function() { signalRemoveSub(sig, subscriber); +return forEach(function(d) { return invoke(d); }, subDisposers); }); + return (function() { + var initialKont = concat(capturedFrames, [resetFrame], remainingKont); + return makeCekValue(signalValue(sig), env, initialKont); +})(); +})(); +})(); +})(); }; + + // step-eval-call + var stepEvalCall = function(head, args, env, kont) { return makeCekState(head, env, kontPush(makeArgFrame(NIL, [], args, env, args), kont)); }; + + // step-ho-map + var stepHoMap = function(args, env, kont) { return makeCekValue(hoMap(args, env), env, kont); }; + + // step-ho-filter + var stepHoFilter = function(args, env, kont) { return makeCekValue(hoFilter(args, env), env, kont); }; + + // step-ho-reduce + var stepHoReduce = function(args, env, kont) { return makeCekValue(hoReduce(args, env), env, kont); }; + + // step-ho-for-each + var stepHoForEach = function(args, env, kont) { return makeCekValue(hoForEach(args, env), env, kont); }; + + // step-continue + var stepContinue = function(state) { return (function() { + var value = cekValue(state); + var env = cekEnv(state); + var kont = cekKont(state); + return (isSxTruthy(kontEmpty_p(kont)) ? state : (function() { + var frame = kontTop(kont); + var restK = kontPop(kont); + var ft = frameType(frame); + return (isSxTruthy((ft == "if")) ? (isSxTruthy((isSxTruthy(value) && !isSxTruthy(isNil(value)))) ? makeCekState(get(frame, "then"), get(frame, "env"), restK) : (isSxTruthy(isNil(get(frame, "else"))) ? makeCekValue(NIL, env, restK) : makeCekState(get(frame, "else"), get(frame, "env"), restK))) : (isSxTruthy((ft == "when")) ? (isSxTruthy((isSxTruthy(value) && !isSxTruthy(isNil(value)))) ? (function() { + var body = get(frame, "body"); + var fenv = get(frame, "env"); + return (isSxTruthy(isEmpty(body)) ? makeCekValue(NIL, fenv, restK) : (isSxTruthy((len(body) == 1)) ? makeCekState(first(body), fenv, restK) : makeCekState(first(body), fenv, kontPush(makeBeginFrame(rest(body), fenv), restK)))); +})() : makeCekValue(NIL, env, restK)) : (isSxTruthy((ft == "begin")) ? (function() { + var remaining = get(frame, "remaining"); + var fenv = get(frame, "env"); + return (isSxTruthy(isEmpty(remaining)) ? makeCekValue(value, fenv, restK) : (isSxTruthy((len(remaining) == 1)) ? makeCekState(first(remaining), fenv, restK) : makeCekState(first(remaining), fenv, kontPush(makeBeginFrame(rest(remaining), fenv), restK)))); +})() : (isSxTruthy((ft == "let")) ? (function() { + var name = get(frame, "name"); + var remaining = get(frame, "remaining"); + var body = get(frame, "body"); + var local = get(frame, "env"); + envSet(local, name, value); + return (isSxTruthy(isEmpty(remaining)) ? stepSfBegin(body, local, restK) : (function() { + var nextBinding = first(remaining); + var vname = (isSxTruthy((typeOf(first(nextBinding)) == "symbol")) ? symbolName(first(nextBinding)) : first(nextBinding)); + return makeCekState(nth(nextBinding, 1), local, kontPush(makeLetFrame(vname, rest(remaining), body, local), restK)); +})()); +})() : (isSxTruthy((ft == "define")) ? (function() { + var name = get(frame, "name"); + var fenv = get(frame, "env"); + var hasEffects = get(frame, "has-effects"); + var effectList = get(frame, "effect-list"); + if (isSxTruthy((isSxTruthy(isLambda(value)) && isNil(lambdaName(value))))) { + value.name = name; +} + envSet(fenv, name, value); + if (isSxTruthy(hasEffects)) { + (function() { + var effectNames = (isSxTruthy((typeOf(effectList) == "list")) ? map(function(e) { return (isSxTruthy((typeOf(e) == "symbol")) ? symbolName(e) : (String(e))); }, effectList) : [(String(effectList))]); + var effectAnns = (isSxTruthy(envHas(fenv, "*effect-annotations*")) ? envGet(fenv, "*effect-annotations*") : {}); + effectAnns[name] = effectNames; + return envSet(fenv, "*effect-annotations*", effectAnns); +})(); +} + return makeCekValue(value, fenv, restK); +})() : (isSxTruthy((ft == "set")) ? (function() { + var name = get(frame, "name"); + var fenv = get(frame, "env"); + envSet(fenv, name, value); + return makeCekValue(value, env, restK); +})() : (isSxTruthy((ft == "and")) ? (isSxTruthy(!isSxTruthy(value)) ? makeCekValue(value, env, restK) : (function() { + var remaining = get(frame, "remaining"); + return (isSxTruthy(isEmpty(remaining)) ? makeCekValue(value, env, restK) : makeCekState(first(remaining), get(frame, "env"), (isSxTruthy((len(remaining) == 1)) ? restK : kontPush(makeAndFrame(rest(remaining), get(frame, "env")), restK)))); +})()) : (isSxTruthy((ft == "or")) ? (isSxTruthy(value) ? makeCekValue(value, env, restK) : (function() { + var remaining = get(frame, "remaining"); + return (isSxTruthy(isEmpty(remaining)) ? makeCekValue(false, env, restK) : makeCekState(first(remaining), get(frame, "env"), (isSxTruthy((len(remaining) == 1)) ? restK : kontPush(makeOrFrame(rest(remaining), get(frame, "env")), restK)))); +})()) : (isSxTruthy((ft == "cond")) ? (function() { + var remaining = get(frame, "remaining"); + var fenv = get(frame, "env"); + var scheme_p = get(frame, "scheme"); + return (isSxTruthy(scheme_p) ? (isSxTruthy(value) ? makeCekState(nth(first(remaining), 1), fenv, restK) : (function() { + var nextClauses = rest(remaining); + return (isSxTruthy(isEmpty(nextClauses)) ? makeCekValue(NIL, fenv, restK) : (function() { + var nextClause = first(nextClauses); + var nextTest = first(nextClause); + return (isSxTruthy(sxOr((isSxTruthy((typeOf(nextTest) == "symbol")) && sxOr((symbolName(nextTest) == "else"), (symbolName(nextTest) == ":else"))), (isSxTruthy((typeOf(nextTest) == "keyword")) && (keywordName(nextTest) == "else")))) ? makeCekState(nth(nextClause, 1), fenv, restK) : makeCekState(nextTest, fenv, kontPush(makeCondFrame(nextClauses, fenv, true), restK))); +})()); +})()) : (isSxTruthy(value) ? makeCekState(nth(remaining, 1), fenv, restK) : (function() { + var next = slice(remaining, 2); + return (isSxTruthy((len(next) < 2)) ? makeCekValue(NIL, fenv, restK) : (function() { + var nextTest = first(next); + return (isSxTruthy(sxOr((isSxTruthy((typeOf(nextTest) == "keyword")) && (keywordName(nextTest) == "else")), (isSxTruthy((typeOf(nextTest) == "symbol")) && sxOr((symbolName(nextTest) == "else"), (symbolName(nextTest) == ":else"))))) ? makeCekState(nth(next, 1), fenv, restK) : makeCekState(nextTest, fenv, kontPush(makeCondFrame(next, fenv, false), restK))); +})()); +})())); +})() : (isSxTruthy((ft == "case")) ? (function() { + var matchVal = get(frame, "match-val"); + var remaining = get(frame, "remaining"); + var fenv = get(frame, "env"); + return (isSxTruthy(isNil(matchVal)) ? sfCaseStepLoop(value, remaining, fenv, restK) : sfCaseStepLoop(matchVal, remaining, fenv, restK)); +})() : (isSxTruthy((ft == "thread")) ? (function() { + var remaining = get(frame, "remaining"); + var fenv = get(frame, "env"); + return (isSxTruthy(isEmpty(remaining)) ? makeCekValue(value, fenv, restK) : (function() { + var form = first(remaining); + var restForms = rest(remaining); + return (function() { + var result = (isSxTruthy((typeOf(form) == "list")) ? (function() { + var f = trampoline(evalExpr(first(form), fenv)); + var rargs = map(function(a) { return trampoline(evalExpr(a, fenv)); }, rest(form)); + var allArgs = cons(value, rargs); + return (isSxTruthy((isSxTruthy(isCallable(f)) && !isSxTruthy(isLambda(f)))) ? apply(f, allArgs) : (isSxTruthy(isLambda(f)) ? trampoline(callLambda(f, allArgs, fenv)) : error((String("-> form not callable: ") + String(inspect(f)))))); +})() : (function() { + var f = trampoline(evalExpr(form, fenv)); + return (isSxTruthy((isSxTruthy(isCallable(f)) && !isSxTruthy(isLambda(f)))) ? f(value) : (isSxTruthy(isLambda(f)) ? trampoline(callLambda(f, [value], fenv)) : error((String("-> form not callable: ") + String(inspect(f)))))); +})()); + return (isSxTruthy(isEmpty(restForms)) ? makeCekValue(result, fenv, restK) : makeCekValue(result, fenv, kontPush(makeThreadFrame(restForms, fenv), restK))); +})(); +})()); +})() : (isSxTruthy((ft == "arg")) ? (function() { + var f = get(frame, "f"); + var evaled = get(frame, "evaled"); + var remaining = get(frame, "remaining"); + var fenv = get(frame, "env"); + var rawArgs = get(frame, "raw-args"); + return (isSxTruthy(isNil(f)) ? (isSxTruthy(isEmpty(remaining)) ? continueWithCall(value, [], fenv, rawArgs, restK) : makeCekState(first(remaining), fenv, kontPush(makeArgFrame(value, [], rest(remaining), fenv, rawArgs), restK))) : (function() { + var newEvaled = append(evaled, [value]); + return (isSxTruthy(isEmpty(remaining)) ? continueWithCall(f, newEvaled, fenv, rawArgs, restK) : makeCekState(first(remaining), fenv, kontPush(makeArgFrame(f, newEvaled, rest(remaining), fenv, rawArgs), restK))); +})()); +})() : (isSxTruthy((ft == "dict")) ? (function() { + var remaining = get(frame, "remaining"); + var results = get(frame, "results"); + var fenv = get(frame, "env"); + return (function() { + var lastResult = last(results); + var completed = append(slice(results, 0, (len(results) - 1)), [[first(lastResult), value]]); + return (isSxTruthy(isEmpty(remaining)) ? (function() { + var d = {}; + { var _c = completed; for (var _i = 0; _i < _c.length; _i++) { var pair = _c[_i]; d[first(pair)] = nth(pair, 1); } } + return makeCekValue(d, fenv, restK); +})() : (function() { + var nextEntry = first(remaining); + return makeCekState(nth(nextEntry, 1), fenv, kontPush(makeDictFrame(rest(remaining), append(completed, [[first(nextEntry)]]), fenv), restK)); +})()); +})(); +})() : (isSxTruthy((ft == "reset")) ? makeCekValue(value, env, restK) : (isSxTruthy((ft == "deref")) ? (function() { + var val = value; + var fenv = get(frame, "env"); + return (isSxTruthy(!isSxTruthy(isSignal(val))) ? makeCekValue(val, fenv, restK) : (isSxTruthy(hasReactiveResetFrame_p(restK)) ? reactiveShiftDeref(val, fenv, restK) : ((function() { + var ctx = sxContext("sx-reactive", NIL); + return (isSxTruthy(ctx) ? (function() { + var depList = get(ctx, "deps"); + var notifyFn = get(ctx, "notify"); + return (isSxTruthy(!isSxTruthy(contains(depList, val))) ? (append_b(depList, val), signalAddSub(val, notifyFn)) : NIL); +})() : NIL); +})(), makeCekValue(signalValue(val), fenv, restK)))); +})() : (isSxTruthy((ft == "reactive-reset")) ? (function() { + var updateFn = get(frame, "update-fn"); + var first_p = get(frame, "first-render"); + if (isSxTruthy((isSxTruthy(updateFn) && !isSxTruthy(first_p)))) { + invoke(updateFn, value); +} + return makeCekValue(value, env, restK); +})() : (isSxTruthy((ft == "scope")) ? (function() { + var name = get(frame, "name"); + var remaining = get(frame, "remaining"); + var fenv = get(frame, "env"); + return (isSxTruthy(isEmpty(remaining)) ? (scopePop(name), makeCekValue(value, fenv, restK)) : makeCekState(first(remaining), fenv, kontPush(makeScopeFrame(name, rest(remaining), fenv), restK))); +})() : error((String("Unknown frame type: ") + String(ft)))))))))))))))))))); +})()); +})(); }; + + // continue-with-call + var continueWithCall = function(f, args, env, rawArgs, kont) { return (isSxTruthy(continuation_p(f)) ? (function() { + var arg = (isSxTruthy(isEmpty(args)) ? NIL : first(args)); + var contData = continuationData(f); + return (function() { + var captured = get(contData, "captured"); + var restK = get(contData, "rest-kont"); + return makeCekValue(arg, env, concat(captured, restK)); +})(); +})() : (isSxTruthy((isSxTruthy(isCallable(f)) && isSxTruthy(!isSxTruthy(isLambda(f))) && isSxTruthy(!isSxTruthy(isComponent(f))) && !isSxTruthy(isIsland(f)))) ? makeCekValue(apply(f, args), env, kont) : (isSxTruthy(isLambda(f)) ? (function() { + var params = lambdaParams(f); + var local = envMerge(lambdaClosure(f), env); + return (isSxTruthy((len(args) > len(params))) ? error((String(sxOr(lambdaName(f), "lambda")) + String(" expects ") + String(len(params)) + String(" args, got ") + String(len(args)))) : (forEach(function(pair) { return envSet(local, first(pair), nth(pair, 1)); }, zip(params, args)), forEach(function(p) { return envSet(local, p, NIL); }, slice(params, len(args))), makeCekState(lambdaBody(f), local, kont))); +})() : (isSxTruthy(sxOr(isComponent(f), isIsland(f))) ? (function() { + var parsed = parseKeywordArgs(rawArgs, env); + var kwargs = first(parsed); + var children = nth(parsed, 1); + var local = envMerge(componentClosure(f), env); + { var _c = componentParams(f); for (var _i = 0; _i < _c.length; _i++) { var p = _c[_i]; envSet(local, p, sxOr(dictGet(kwargs, p), NIL)); } } + if (isSxTruthy(componentHasChildren(f))) { + envSet(local, "children", children); +} + return makeCekState(componentBody(f), local, kont); +})() : error((String("Not callable: ") + String(inspect(f)))))))); }; + + // sf-case-step-loop + var sfCaseStepLoop = function(matchVal, clauses, env, kont) { return (isSxTruthy((len(clauses) < 2)) ? makeCekValue(NIL, env, kont) : (function() { + var test = first(clauses); + var body = nth(clauses, 1); + return (isSxTruthy(sxOr((isSxTruthy((typeOf(test) == "keyword")) && (keywordName(test) == "else")), (isSxTruthy((typeOf(test) == "symbol")) && sxOr((symbolName(test) == "else"), (symbolName(test) == ":else"))))) ? makeCekState(body, env, kont) : (function() { + var testVal = trampoline(evalExpr(test, env)); + return (isSxTruthy((matchVal == testVal)) ? makeCekState(body, env, kont) : sfCaseStepLoop(matchVal, slice(clauses, 2), env, kont)); +})()); +})()); }; + + // eval-expr-cek + var evalExprCek = function(expr, env) { return cekRun(makeCekState(expr, env, [])); }; + + // trampoline-cek + var trampolineCek = function(val) { return (isSxTruthy(isThunk(val)) ? evalExprCek(thunkExpr(val), thunkEnv(val)) : val); }; + + // ========================================================================= // Platform interface — DOM adapter (browser-only) // ========================================================================= @@ -5754,6 +6290,27 @@ return (function() { + // ========================================================================= + // Platform: CEK module — explicit CEK machine + // ========================================================================= + + // Standalone aliases for primitives used by cek.sx / frames.sx + var inc = PRIMITIVES["inc"]; + var dec = PRIMITIVES["dec"]; + var zip_pairs = PRIMITIVES["zip-pairs"]; + + var continuation_p = PRIMITIVES["continuation?"]; + + function makeCekContinuation(captured, restKont) { + var c = new Continuation(function(v) { return v !== undefined ? v : NIL; }); + c._cek_data = {"captured": captured, "rest-kont": restKont}; + return c; + } + function continuationData(c) { + return (c && c._cek_data) ? c._cek_data : {}; + } + + // ========================================================================= // Post-transpilation fixups // ========================================================================= @@ -5878,6 +6435,94 @@ return (function() { PRIMITIVES["build-routing-analysis"] = buildRoutingAnalysis; PRIMITIVES["build-affinity-analysis"] = buildAffinityAnalysis; + // Override recursive cekRun with iterative loop (avoids stack overflow) + cekRun = function(state) { + while (!cekTerminal_p(state)) { state = cekStep(state); } + return cekValue(state); + }; + + + // ========================================================================= + // Extension: Delimited continuations (shift/reset) + // ========================================================================= + + function Continuation(fn) { this.fn = fn; } + Continuation.prototype._continuation = true; + Continuation.prototype.call = function(value) { return this.fn(value !== undefined ? value : NIL); }; + + function ShiftSignal(kName, body, env) { + this.kName = kName; + this.body = body; + this.env = env; + } + + PRIMITIVES["continuation?"] = function(x) { return x != null && x._continuation === true; }; + + var _resetResume = []; + + function sfReset(args, env) { + var body = args[0]; + try { + return trampoline(evalExpr(body, env)); + } catch (e) { + if (e instanceof ShiftSignal) { + var sig = e; + var cont = new Continuation(function(value) { + if (value === undefined) value = NIL; + _resetResume.push(value); + try { + return trampoline(evalExpr(body, env)); + } finally { + _resetResume.pop(); + } + }); + var sigEnv = merge(sig.env); + sigEnv[sig.kName] = cont; + return trampoline(evalExpr(sig.body, sigEnv)); + } + throw e; + } + } + + function sfShift(args, env) { + if (_resetResume.length > 0) { + return _resetResume[_resetResume.length - 1]; + } + var kName = symbolName(args[0]); + var body = args[1]; + throw new ShiftSignal(kName, body, env); + } + + // Wrap evalList to intercept reset/shift + var _baseEvalList = evalList; + evalList = function(expr, env) { + var head = expr[0]; + if (isSym(head)) { + var name = head.name; + if (name === "reset") return sfReset(expr.slice(1), env); + if (name === "shift") return sfShift(expr.slice(1), env); + } + return _baseEvalList(expr, env); + }; + + // Wrap aserSpecial to handle reset/shift in SX wire mode + if (typeof aserSpecial === "function") { + var _baseAserSpecial = aserSpecial; + aserSpecial = function(name, expr, env) { + if (name === "reset") return sfReset(expr.slice(1), env); + if (name === "shift") return sfShift(expr.slice(1), env); + return _baseAserSpecial(name, expr, env); + }; + } + + // Wrap typeOf to recognize continuations + var _baseTypeOf = typeOf; + typeOf = function(x) { + if (x != null && x._continuation) return "continuation"; + return _baseTypeOf(x); + }; + + // ========================================================================= // Async IO: Promise-aware rendering for client-side IO primitives // ========================================================================= diff --git a/shared/sx/ref/bootstrap_py.py b/shared/sx/ref/bootstrap_py.py index 56fc1ca..9f8b5ce 100644 --- a/shared/sx/ref/bootstrap_py.py +++ b/shared/sx/ref/bootstrap_py.py @@ -1122,6 +1122,7 @@ try: from .platform_py import ( PREAMBLE, PLATFORM_PY, PRIMITIVES_PY_PRE, PRIMITIVES_PY_POST, PRIMITIVES_PY_MODULES, _ALL_PY_MODULES, + PLATFORM_PARSER_PY, PLATFORM_DEPS_PY, PLATFORM_CEK_PY, CEK_FIXUPS_PY, PLATFORM_ASYNC_PY, FIXUPS_PY, CONTINUATIONS_PY, _assemble_primitives_py, public_api_py, @@ -1132,6 +1133,7 @@ except ImportError: from shared.sx.ref.platform_py import ( PREAMBLE, PLATFORM_PY, PRIMITIVES_PY_PRE, PRIMITIVES_PY_POST, PRIMITIVES_PY_MODULES, _ALL_PY_MODULES, + PLATFORM_PARSER_PY, PLATFORM_DEPS_PY, PLATFORM_CEK_PY, CEK_FIXUPS_PY, PLATFORM_ASYNC_PY, FIXUPS_PY, CONTINUATIONS_PY, _assemble_primitives_py, public_api_py, @@ -1224,7 +1226,7 @@ def compile_ref_to_py( Args: adapters: List of adapter names to include. - Valid names: html, sx. + Valid names: parser, html, sx. None = include all server-side adapters. modules: List of primitive module names to include. core.* are always included. stdlib.* are opt-in. @@ -1277,9 +1279,9 @@ def compile_ref_to_py( spec_mod_set.add("page-helpers") if "router" in SPEC_MODULES: spec_mod_set.add("router") - # cek module requires frames - if "cek" in spec_mod_set: - spec_mod_set.add("frames") + # CEK is the canonical evaluator — always include + spec_mod_set.add("cek") + spec_mod_set.add("frames") has_deps = "deps" in spec_mod_set has_cek = "cek" in spec_mod_set @@ -1289,6 +1291,9 @@ def compile_ref_to_py( ("forms.sx", "forms (server definition forms)"), ("render.sx", "render (core)"), ] + # Parser before html/sx — provides serialize used by adapters + if "parser" in adapter_set: + sx_files.append(ADAPTER_FILES["parser"]) for name in ("html", "sx"): if name in adapter_set: sx_files.append(ADAPTER_FILES[name]) @@ -1348,6 +1353,7 @@ def compile_ref_to_py( # Build output has_html = "html" in adapter_set has_sx = "sx" in adapter_set + has_parser = "parser" in adapter_set parts = [] parts.append(PREAMBLE) @@ -1356,6 +1362,9 @@ def compile_ref_to_py( parts.append(_assemble_primitives_py(prim_modules)) parts.append(PRIMITIVES_PY_POST) + if has_parser: + parts.append(PLATFORM_PARSER_PY) + if has_deps: parts.append(PLATFORM_DEPS_PY) diff --git a/shared/sx/ref/js.sx b/shared/sx/ref/js.sx index 743817a..62e5a91 100644 --- a/shared/sx/ref/js.sx +++ b/shared/sx/ref/js.sx @@ -214,6 +214,10 @@ "render-dom-island" "renderDomIsland" "reactive-text" "reactiveText" "reactive-attr" "reactiveAttr" + "cek-reactive-text" "cekReactiveText" + "cek-reactive-attr" "cekReactiveAttr" + "*use-cek-reactive*" "_useCekReactive" + "enable-cek-reactive!" "enableCekReactive" "reactive-fragment" "reactiveFragment" "reactive-list" "reactiveList" "dom-create-element" "domCreateElement" @@ -520,6 +524,80 @@ "collect!" "sxCollect" "collected" "sxCollected" "clear-collected!" "sxClearCollected" + "make-cek-continuation" "makeCekContinuation" + "continuation-data" "continuationData" + "make-cek-state" "makeCekState" + "make-cek-value" "makeCekValue" + "cek-terminal?" "cekTerminal_p" + "cek-run" "cekRun" + "cek-step" "cekStep" + "cek-control" "cekControl" + "cek-env" "cekEnv" + "cek-kont" "cekKont" + "cek-phase" "cekPhase" + "cek-value" "cekValue" + "kont-push" "kontPush" + "kont-top" "kontTop" + "kont-pop" "kontPop" + "kont-empty?" "kontEmpty_p" + "kont-capture-to-reset" "kontCaptureToReset" + "kont-capture-to-reactive-reset" "kontCaptureToReactiveReset" + "has-reactive-reset-frame?" "hasReactiveResetFrame_p" + "frame-type" "frameType" + "make-if-frame" "makeIfFrame" + "make-when-frame" "makeWhenFrame" + "make-begin-frame" "makeBeginFrame" + "make-let-frame" "makeLetFrame" + "make-define-frame" "makeDefineFrame" + "make-set-frame" "makeSetFrame" + "make-arg-frame" "makeArgFrame" + "make-call-frame" "makeCallFrame" + "make-cond-frame" "makeCondFrame" + "make-case-frame" "makeCaseFrame" + "make-thread-frame" "makeThreadFrame" + "make-map-frame" "makeMapFrame" + "make-filter-frame" "makeFilterFrame" + "make-reduce-frame" "makeReduceFrame" + "make-for-each-frame" "makeForEachFrame" + "make-scope-frame" "makeScopeFrame" + "make-reset-frame" "makeResetFrame" + "make-dict-frame" "makeDictFrame" + "make-and-frame" "makeAndFrame" + "make-or-frame" "makeOrFrame" + "make-dynamic-wind-frame" "makeDynamicWindFrame" + "make-reactive-reset-frame" "makeReactiveResetFrame" + "make-deref-frame" "makeDerefFrame" + "step-eval" "stepEval" + "step-continue" "stepContinue" + "step-eval-list" "stepEvalList" + "step-eval-call" "stepEvalCall" + "step-sf-if" "stepSfIf" + "step-sf-when" "stepSfWhen" + "step-sf-begin" "stepSfBegin" + "step-sf-let" "stepSfLet" + "step-sf-define" "stepSfDefine" + "step-sf-set!" "stepSfSet" + "step-sf-and" "stepSfAnd" + "step-sf-or" "stepSfOr" + "step-sf-cond" "stepSfCond" + "step-sf-case" "stepSfCase" + "step-sf-thread-first" "stepSfThreadFirst" + "step-sf-lambda" "stepSfLambda" + "step-sf-scope" "stepSfScope" + "step-sf-provide" "stepSfProvide" + "step-sf-reset" "stepSfReset" + "step-sf-shift" "stepSfShift" + "step-sf-deref" "stepSfDeref" + "step-ho-map" "stepHoMap" + "step-ho-filter" "stepHoFilter" + "step-ho-reduce" "stepHoReduce" + "step-ho-for-each" "stepHoForEach" + "continue-with-call" "continueWithCall" + "sf-case-step-loop" "sfCaseStepLoop" + "eval-expr-cek" "evalExprCek" + "trampoline-cek" "trampolineCek" + "reactive-shift-deref" "reactiveShiftDeref" + "cond-scheme?" "condScheme_p" "scope-push!" "scopePush" "scope-pop!" "scopePop" "provide-push!" "providePush" diff --git a/shared/sx/ref/platform_js.py b/shared/sx/ref/platform_js.py index 957b7ad..013cf71 100644 --- a/shared/sx/ref/platform_js.py +++ b/shared/sx/ref/platform_js.py @@ -46,8 +46,14 @@ SPEC_MODULES = { "router": ("router.sx", "router (client-side route matching)"), "signals": ("signals.sx", "signals (reactive signal runtime)"), "page-helpers": ("page-helpers.sx", "page-helpers (pure data transformation helpers)"), + "frames": ("frames.sx", "frames (CEK continuation frames)"), + "cek": ("cek.sx", "cek (explicit CEK machine evaluator)"), } +# Explicit ordering for spec modules with dependencies. +# Modules listed here are emitted in this order; any not listed use alphabetical. +SPEC_MODULE_ORDER = ["deps", "frames", "page-helpers", "router", "signals", "cek"] + EXTENSION_NAMES = {"continuations"} CONTINUATIONS_JS = ''' @@ -1476,6 +1482,38 @@ PLATFORM_JS_POST = ''' };''' +PLATFORM_CEK_JS = ''' + // ========================================================================= + // Platform: CEK module — explicit CEK machine + // ========================================================================= + + // Standalone aliases for primitives used by cek.sx / frames.sx + var inc = PRIMITIVES["inc"]; + var dec = PRIMITIVES["dec"]; + var zip_pairs = PRIMITIVES["zip-pairs"]; + + var continuation_p = PRIMITIVES["continuation?"]; + + function makeCekContinuation(captured, restKont) { + var c = new Continuation(function(v) { return v !== undefined ? v : NIL; }); + c._cek_data = {"captured": captured, "rest-kont": restKont}; + return c; + } + function continuationData(c) { + return (c && c._cek_data) ? c._cek_data : {}; + } +''' + +# Iterative override for cek_run — replaces transpiled recursive version +CEK_FIXUPS_JS = ''' + // Override recursive cekRun with iterative loop (avoids stack overflow) + cekRun = function(state) { + while (!cekTerminal_p(state)) { state = cekStep(state); } + return cekValue(state); + }; +''' + + PLATFORM_DEPS_JS = ''' // ========================================================================= // Platform: deps module — component dependency analysis diff --git a/shared/sx/ref/platform_py.py b/shared/sx/ref/platform_py.py index 3078b2f..47e1030 100644 --- a/shared/sx/ref/platform_py.py +++ b/shared/sx/ref/platform_py.py @@ -659,51 +659,6 @@ def escape_string(s): .replace("<=/!?&]") +_IDENT_CHAR_RE = _re_parser.compile(r"[a-zA-Z0-9_~*+\\-><=/!?.:&/\\[\\]#,]") + + +def ident_start_p(ch): + return bool(_IDENT_START_RE.match(ch)) + + +def ident_char_p(ch): + return bool(_IDENT_CHAR_RE.match(ch)) + + +def parse_number(s): + """Parse a numeric string to int or float.""" + try: + if "." in s or "e" in s or "E" in s: + return float(s) + return int(s) + except (ValueError, TypeError): + return float(s) + + +# Reader macro registry +_reader_macros = {} + + +def reader_macro_get(name): + return _reader_macros.get(name, NIL) + + +def reader_macro_set_b(name, handler): + _reader_macros[name] = handler + return NIL ''' # --------------------------------------------------------------------------- @@ -1159,6 +1163,23 @@ def cek_run(state): while not cek_terminal_p(state): state = cek_step(state) return cek_value(state) + +# CEK is the canonical evaluator — override eval_expr to use it. +# The tree-walk evaluator (eval_expr from eval.sx) is superseded. +_tree_walk_eval_expr = eval_expr + +def eval_expr(expr, env): + """Evaluate expr using the CEK machine.""" + return cek_run(make_cek_state(expr, env, [])) + +# CEK never produces thunks — trampoline becomes identity +_tree_walk_trampoline = trampoline + +def trampoline(val): + """In CEK mode, values are immediate — resolve any legacy thunks.""" + if is_thunk(val): + return eval_expr(thunk_expr(val), thunk_env(val)) + return val ''' # --------------------------------------------------------------------------- @@ -1258,11 +1279,6 @@ def number_p(x): return isinstance(x, (int, float)) and not isinstance(x, bool) -def sx_parse(src): - from shared.sx.parser import parse_all - return parse_all(src) - - def is_async_coroutine(x): return _inspect.iscoroutine(x) @@ -1590,9 +1606,10 @@ def public_api_py(has_html: bool, has_sx: bool, has_deps: bool = False, # --------------------------------------------------------------------------- ADAPTER_FILES = { - "html": ("adapter-html.sx", "adapter-html"), - "sx": ("adapter-sx.sx", "adapter-sx"), - "async": ("adapter-async.sx", "adapter-async"), + "parser": ("parser.sx", "parser"), + "html": ("adapter-html.sx", "adapter-html"), + "sx": ("adapter-sx.sx", "adapter-sx"), + "async": ("adapter-async.sx", "adapter-async"), } SPEC_MODULES = { diff --git a/shared/sx/ref/run_js_sx.py b/shared/sx/ref/run_js_sx.py index a5a92ed..9edfa24 100644 --- a/shared/sx/ref/run_js_sx.py +++ b/shared/sx/ref/run_js_sx.py @@ -24,11 +24,12 @@ from shared.sx.parser import parse_all from shared.sx.types import Symbol from shared.sx.ref.platform_js import ( extract_defines, - ADAPTER_FILES, ADAPTER_DEPS, SPEC_MODULES, EXTENSION_NAMES, + ADAPTER_FILES, ADAPTER_DEPS, SPEC_MODULES, SPEC_MODULE_ORDER, EXTENSION_NAMES, PREAMBLE, PLATFORM_JS_PRE, PLATFORM_JS_POST, PRIMITIVES_JS_MODULES, _ALL_JS_MODULES, _assemble_primitives_js, PLATFORM_DEPS_JS, PLATFORM_PARSER_JS, PLATFORM_DOM_JS, PLATFORM_ENGINE_PURE_JS, PLATFORM_ORCHESTRATION_JS, PLATFORM_BOOT_JS, + PLATFORM_CEK_JS, CEK_FIXUPS_JS, CONTINUATIONS_JS, ASYNC_IO_JS, fixups_js, public_api_js, EPILOGUE, ) @@ -105,9 +106,17 @@ def compile_ref_to_js( spec_mod_set.add("deps") if "page-helpers" in SPEC_MODULES: spec_mod_set.add("page-helpers") + # CEK needed for reactive rendering (deref-as-shift) + if "dom" in adapter_set: + spec_mod_set.add("cek") + spec_mod_set.add("frames") + # cek module requires frames + if "cek" in spec_mod_set: + spec_mod_set.add("frames") has_deps = "deps" in spec_mod_set has_router = "router" in spec_mod_set has_page_helpers = "page-helpers" in spec_mod_set + has_cek = "cek" in spec_mod_set # Resolve extensions ext_set = set() @@ -126,8 +135,14 @@ def compile_ref_to_js( for name in ("parser", "html", "sx", "dom", "engine", "orchestration", "boot"): if name in adapter_set: sx_files.append(ADAPTER_FILES[name]) + # Use explicit ordering for spec modules (respects dependencies) + for name in SPEC_MODULE_ORDER: + if name in spec_mod_set: + sx_files.append(SPEC_MODULES[name]) + # Any spec modules not in the order list (future-proofing) for name in sorted(spec_mod_set): - sx_files.append(SPEC_MODULES[name]) + if name not in SPEC_MODULE_ORDER: + sx_files.append(SPEC_MODULES[name]) has_html = "html" in adapter_set has_sx = "sx" in adapter_set @@ -201,7 +216,12 @@ def compile_ref_to_js( if name in adapter_set and name in adapter_platform: parts.append(adapter_platform[name]) + if has_cek: + parts.append(PLATFORM_CEK_JS) + 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) if has_continuations: parts.append(CONTINUATIONS_JS) if has_dom: diff --git a/shared/sx/ref/sx_ref.py b/shared/sx/ref/sx_ref.py index bbbf179..5c1d11b 100644 --- a/shared/sx/ref/sx_ref.py +++ b/shared/sx/ref/sx_ref.py @@ -618,51 +618,6 @@ def escape_string(s): .replace("<=/!?&]") +_IDENT_CHAR_RE = _re_parser.compile(r"[a-zA-Z0-9_~*+\-><=/!?.:&/\[\]#,]") + + +def ident_start_p(ch): + return bool(_IDENT_START_RE.match(ch)) + + +def ident_char_p(ch): + return bool(_IDENT_CHAR_RE.match(ch)) + + +def parse_number(s): + """Parse a numeric string to int or float.""" + try: + if "." in s or "e" in s or "E" in s: + return float(s) + return int(s) + except (ValueError, TypeError): + return float(s) + + +# Reader macro registry +_reader_macros = {} + + +def reader_macro_get(name): + return _reader_macros.get(name, NIL) + + +def reader_macro_set_b(name, handler): + _reader_macros[name] = handler + return NIL # ========================================================================= @@ -1038,6 +1036,28 @@ def component_set_io_refs(c, refs): c.io_refs = set(refs) if not isinstance(refs, set) else refs +# ========================================================================= +# Platform: CEK module — explicit CEK machine +# ========================================================================= + +# Standalone aliases for primitives used by cek.sx / frames.sx +inc = PRIMITIVES["inc"] +dec = PRIMITIVES["dec"] +zip_pairs = PRIMITIVES["zip-pairs"] + +continuation_p = PRIMITIVES["continuation?"] + +def make_cek_continuation(captured, rest_kont): + """Create a Continuation storing captured CEK frames as data.""" + c = Continuation(lambda v=NIL: v) + c._cek_data = {"captured": captured, "rest-kont": rest_kont} + return c + +def continuation_data(c): + """Return the _cek_data dict from a CEK continuation.""" + return getattr(c, '_cek_data', {}) or {} + + # ========================================================================= # Platform interface -- Async adapter # ========================================================================= @@ -1130,11 +1150,6 @@ def number_p(x): return isinstance(x, (int, float)) and not isinstance(x, bool) -def sx_parse(src): - from shared.sx.parser import parse_all - return parse_all(src) - - def is_async_coroutine(x): return _inspect.iscoroutine(x) @@ -2156,6 +2171,275 @@ def merge_spread_attrs(target, spread_dict): return NIL +# === Transpiled from parser === + +# sx-parse +def sx_parse(source): + _cells = {} + _cells['pos'] = 0 + len_src = len(source) + def skip_comment(): + if sx_truthy(((pos < len_src) if not sx_truthy((pos < len_src)) else (not sx_truthy((nth(source, pos) == '\n'))))): + pos = (pos + 1) + return skip_comment() + return NIL + def skip_ws(): + if sx_truthy((pos < len_src)): + ch = nth(source, pos) + if sx_truthy(((ch == ' ') if sx_truthy((ch == ' ')) else ((ch == '\t') if sx_truthy((ch == '\t')) else ((ch == '\n') if sx_truthy((ch == '\n')) else (ch == '\r'))))): + pos = (pos + 1) + return skip_ws() + elif sx_truthy((ch == ';')): + pos = (pos + 1) + skip_comment() + return skip_ws() + else: + return NIL + return NIL + def hex_digit_value(ch): + return index_of('0123456789abcdef', lower(ch)) + def read_string(): + _cells = {} + _cells['pos'] = (_cells['pos'] + 1) + _cells['buf'] = '' + def read_str_loop(): + if sx_truthy((pos >= len_src)): + return error('Unterminated string') + else: + ch = nth(source, pos) + if sx_truthy((ch == '"')): + pos = (pos + 1) + return NIL + elif sx_truthy((ch == '\\')): + pos = (pos + 1) + esc = nth(source, pos) + if sx_truthy((esc == 'u')): + pos = (pos + 1) + d0 = hex_digit_value(nth(source, pos)) + _ = _sx_cell_set(_cells, 'pos', (pos + 1)) + d1 = hex_digit_value(nth(source, pos)) + _ = _sx_cell_set(_cells, 'pos', (pos + 1)) + d2 = hex_digit_value(nth(source, pos)) + _ = _sx_cell_set(_cells, 'pos', (pos + 1)) + d3 = hex_digit_value(nth(source, pos)) + _ = _sx_cell_set(_cells, 'pos', (pos + 1)) + buf = sx_str(buf, char_from_code(((d0 * 4096) + (d1 * 256)))) + return read_str_loop() + else: + buf = sx_str(buf, ('\n' if sx_truthy((esc == 'n')) else ('\t' if sx_truthy((esc == 't')) else ('\r' if sx_truthy((esc == 'r')) else esc)))) + pos = (pos + 1) + return read_str_loop() + else: + buf = sx_str(buf, ch) + pos = (pos + 1) + return read_str_loop() + read_str_loop() + return _cells['buf'] + def read_ident(): + _cells = {} + start = _cells['pos'] + def read_ident_loop(): + if sx_truthy(((pos < len_src) if not sx_truthy((pos < len_src)) else ident_char_p(nth(source, pos)))): + pos = (pos + 1) + return read_ident_loop() + return NIL + read_ident_loop() + return slice(source, start, _cells['pos']) + def read_keyword(): + pos = (pos + 1) + return make_keyword(read_ident()) + def read_number(): + _cells = {} + start = _cells['pos'] + if sx_truthy(((_cells['pos'] < len_src) if not sx_truthy((_cells['pos'] < len_src)) else (nth(source, _cells['pos']) == '-'))): + _cells['pos'] = (_cells['pos'] + 1) + def read_digits(): + if sx_truthy(((pos < len_src) if not sx_truthy((pos < len_src)) else (lambda c: ((c >= '0') if not sx_truthy((c >= '0')) else (c <= '9')))(nth(source, pos)))): + pos = (pos + 1) + return read_digits() + return NIL + read_digits() + if sx_truthy(((_cells['pos'] < len_src) if not sx_truthy((_cells['pos'] < len_src)) else (nth(source, _cells['pos']) == '.'))): + _cells['pos'] = (_cells['pos'] + 1) + read_digits() + if sx_truthy(((_cells['pos'] < len_src) if not sx_truthy((_cells['pos'] < len_src)) else ((nth(source, _cells['pos']) == 'e') if sx_truthy((nth(source, _cells['pos']) == 'e')) else (nth(source, _cells['pos']) == 'E')))): + _cells['pos'] = (_cells['pos'] + 1) + if sx_truthy(((_cells['pos'] < len_src) if not sx_truthy((_cells['pos'] < len_src)) else ((nth(source, _cells['pos']) == '+') if sx_truthy((nth(source, _cells['pos']) == '+')) else (nth(source, _cells['pos']) == '-')))): + _cells['pos'] = (_cells['pos'] + 1) + read_digits() + return parse_number(slice(source, start, _cells['pos'])) + def read_symbol(): + name = read_ident() + if sx_truthy((name == 'true')): + return True + elif sx_truthy((name == 'false')): + return False + elif sx_truthy((name == 'nil')): + return NIL + else: + return make_symbol(name) + def read_list(close_ch): + _cells = {} + items = [] + def read_list_loop(): + skip_ws() + if sx_truthy((pos >= len_src)): + return error('Unterminated list') + else: + if sx_truthy((nth(source, pos) == close_ch)): + pos = (pos + 1) + return NIL + else: + items.append(read_expr()) + return read_list_loop() + read_list_loop() + return items + def read_map(): + _cells = {} + result = {} + def read_map_loop(): + skip_ws() + if sx_truthy((pos >= len_src)): + return error('Unterminated map') + else: + if sx_truthy((nth(source, pos) == '}')): + pos = (pos + 1) + return NIL + else: + key_expr = read_expr() + key_str = (keyword_name(key_expr) if sx_truthy((type_of(key_expr) == 'keyword')) else sx_str(key_expr)) + val_expr = read_expr() + result[key_str] = val_expr + return read_map_loop() + read_map_loop() + return result + def read_raw_string(): + _cells = {} + _cells['buf'] = '' + def raw_loop(): + if sx_truthy((pos >= len_src)): + return error('Unterminated raw string') + else: + ch = nth(source, pos) + if sx_truthy((ch == '|')): + pos = (pos + 1) + return NIL + else: + buf = sx_str(buf, ch) + pos = (pos + 1) + return raw_loop() + raw_loop() + return _cells['buf'] + def read_expr(): + skip_ws() + if sx_truthy((pos >= len_src)): + return error('Unexpected end of input') + else: + ch = nth(source, pos) + if sx_truthy((ch == '(')): + pos = (pos + 1) + return read_list(')') + elif sx_truthy((ch == '[')): + pos = (pos + 1) + return read_list(']') + elif sx_truthy((ch == '{')): + pos = (pos + 1) + return read_map() + elif sx_truthy((ch == '"')): + return read_string() + elif sx_truthy((ch == ':')): + return read_keyword() + elif sx_truthy((ch == '`')): + pos = (pos + 1) + return [make_symbol('quasiquote'), read_expr()] + elif sx_truthy((ch == ',')): + pos = (pos + 1) + if sx_truthy(((pos < len_src) if not sx_truthy((pos < len_src)) else (nth(source, pos) == '@'))): + pos = (pos + 1) + return [make_symbol('splice-unquote'), read_expr()] + else: + return [make_symbol('unquote'), read_expr()] + elif sx_truthy((ch == '#')): + pos = (pos + 1) + if sx_truthy((pos >= len_src)): + return error('Unexpected end of input after #') + else: + dispatch_ch = nth(source, pos) + if sx_truthy((dispatch_ch == ';')): + pos = (pos + 1) + read_expr() + return read_expr() + elif sx_truthy((dispatch_ch == '|')): + pos = (pos + 1) + return read_raw_string() + elif sx_truthy((dispatch_ch == "'")): + pos = (pos + 1) + return [make_symbol('quote'), read_expr()] + elif sx_truthy(ident_start_p(dispatch_ch)): + macro_name = read_ident() + handler = reader_macro_get(macro_name) + if sx_truthy(handler): + return handler(read_expr()) + else: + return error(sx_str('Unknown reader macro: #', macro_name)) + else: + return error(sx_str('Unknown reader macro: #', dispatch_ch)) + elif sx_truthy((((ch >= '0') if not sx_truthy((ch >= '0')) else (ch <= '9')) if sx_truthy(((ch >= '0') if not sx_truthy((ch >= '0')) else (ch <= '9'))) else ((ch == '-') if not sx_truthy((ch == '-')) else (((pos + 1) < len_src) if not sx_truthy(((pos + 1) < len_src)) else (lambda next_ch: ((next_ch >= '0') if not sx_truthy((next_ch >= '0')) else (next_ch <= '9')))(nth(source, (pos + 1))))))): + return read_number() + elif sx_truthy(((ch == '.') if not sx_truthy((ch == '.')) else (((pos + 2) < len_src) if not sx_truthy(((pos + 2) < len_src)) else ((nth(source, (pos + 1)) == '.') if not sx_truthy((nth(source, (pos + 1)) == '.')) else (nth(source, (pos + 2)) == '.'))))): + pos = (pos + 3) + return make_symbol('...') + elif sx_truthy(ident_start_p(ch)): + return read_symbol() + else: + return error(sx_str('Unexpected character: ', ch)) + exprs = [] + def parse_loop(): + skip_ws() + if sx_truthy((pos < len_src)): + exprs.append(read_expr()) + return parse_loop() + return NIL + parse_loop() + return exprs + +# sx-serialize +def sx_serialize(val): + _match = type_of(val) + if _match == 'nil': + return 'nil' + elif _match == 'boolean': + if sx_truthy(val): + return 'true' + else: + return 'false' + elif _match == 'number': + return sx_str(val) + elif _match == 'string': + return sx_str('"', escape_string(val), '"') + elif _match == 'symbol': + return symbol_name(val) + elif _match == 'keyword': + return sx_str(':', keyword_name(val)) + elif _match == 'list': + return sx_str('(', join(' ', map(sx_serialize, val)), ')') + elif _match == 'dict': + return sx_serialize_dict(val) + elif _match == 'sx-expr': + return sx_expr_source(val) + elif _match == 'spread': + return sx_str('(make-spread ', sx_serialize_dict(spread_attrs(val)), ')') + else: + return sx_str(val) + +# sx-serialize-dict +def sx_serialize_dict(d): + return sx_str('{', join(' ', reduce(lambda acc, key: concat(acc, [sx_str(':', key), sx_serialize(dict_get(d, key))]), [], keys(d))), '}') + +# serialize +serialize = sx_serialize + + # === Transpiled from adapter-html === # render-to-html @@ -2903,6 +3187,189 @@ def env_components(env): return filter(lambda k: (lambda v: (is_component(v) if sx_truthy(is_component(v)) else is_macro(v)))(env_get(env, k)), keys(env)) +# === Transpiled from frames (CEK continuation frames) === + +# make-cek-state +def make_cek_state(control, env, kont): + return {'control': control, 'env': env, 'kont': kont, 'phase': 'eval', 'value': NIL} + +# make-cek-value +def make_cek_value(value, env, kont): + return {'control': NIL, 'env': env, 'kont': kont, 'phase': 'continue', 'value': value} + +# cek-terminal? +def cek_terminal_p(state): + return ((get(state, 'phase') == 'continue') if not sx_truthy((get(state, 'phase') == 'continue')) else empty_p(get(state, 'kont'))) + +# cek-control +def cek_control(s): + return get(s, 'control') + +# cek-env +def cek_env(s): + return get(s, 'env') + +# cek-kont +def cek_kont(s): + return get(s, 'kont') + +# cek-phase +def cek_phase(s): + return get(s, 'phase') + +# cek-value +def cek_value(s): + return get(s, 'value') + +# make-if-frame +def make_if_frame(then_expr, else_expr, env): + return {'type': 'if', 'then': then_expr, 'else': else_expr, 'env': env} + +# make-when-frame +def make_when_frame(body_exprs, env): + return {'type': 'when', 'body': body_exprs, 'env': env} + +# make-begin-frame +def make_begin_frame(remaining, env): + return {'type': 'begin', 'remaining': remaining, 'env': env} + +# make-let-frame +def make_let_frame(name, remaining, body, local): + return {'type': 'let', 'name': name, 'remaining': remaining, 'body': body, 'env': local} + +# make-define-frame +def make_define_frame(name, env, has_effects, effect_list): + return {'type': 'define', 'name': name, 'env': env, 'has-effects': has_effects, 'effect-list': effect_list} + +# make-set-frame +def make_set_frame(name, env): + return {'type': 'set', 'name': name, 'env': env} + +# make-arg-frame +def make_arg_frame(f, evaled, remaining, env, raw_args): + return {'type': 'arg', 'f': f, 'evaled': evaled, 'remaining': remaining, 'env': env, 'raw-args': raw_args} + +# make-call-frame +def make_call_frame(f, args, env): + return {'type': 'call', 'f': f, 'args': args, 'env': env} + +# make-cond-frame +def make_cond_frame(remaining, env, scheme_p): + return {'type': 'cond', 'remaining': remaining, 'env': env, 'scheme': scheme_p} + +# make-case-frame +def make_case_frame(match_val, remaining, env): + return {'type': 'case', 'match-val': match_val, 'remaining': remaining, 'env': env} + +# make-thread-frame +def make_thread_frame(remaining, env): + return {'type': 'thread', 'remaining': remaining, 'env': env} + +# make-map-frame +def make_map_frame(f, remaining, results, env): + return {'type': 'map', 'f': f, 'remaining': remaining, 'results': results, 'env': env} + +# make-filter-frame +def make_filter_frame(f, remaining, results, current_item, env): + return {'type': 'filter', 'f': f, 'remaining': remaining, 'results': results, 'current-item': current_item, 'env': env} + +# make-reduce-frame +def make_reduce_frame(f, remaining, env): + return {'type': 'reduce', 'f': f, 'remaining': remaining, 'env': env} + +# make-for-each-frame +def make_for_each_frame(f, remaining, env): + return {'type': 'for-each', 'f': f, 'remaining': remaining, 'env': env} + +# make-scope-frame +def make_scope_frame(name, remaining, env): + return {'type': 'scope', 'name': name, 'remaining': remaining, 'env': env} + +# make-reset-frame +def make_reset_frame(env): + return {'type': 'reset', 'env': env} + +# make-dict-frame +def make_dict_frame(remaining, results, env): + return {'type': 'dict', 'remaining': remaining, 'results': results, 'env': env} + +# make-and-frame +def make_and_frame(remaining, env): + return {'type': 'and', 'remaining': remaining, 'env': env} + +# make-or-frame +def make_or_frame(remaining, env): + return {'type': 'or', 'remaining': remaining, 'env': env} + +# make-dynamic-wind-frame +def make_dynamic_wind_frame(phase, body_thunk, after_thunk, env): + return {'type': 'dynamic-wind', 'phase': phase, 'body-thunk': body_thunk, 'after-thunk': after_thunk, 'env': env} + +# make-reactive-reset-frame +def make_reactive_reset_frame(env, update_fn, first_render_p): + return {'type': 'reactive-reset', 'env': env, 'update-fn': update_fn, 'first-render': first_render_p} + +# make-deref-frame +def make_deref_frame(env): + return {'type': 'deref', 'env': env} + +# frame-type +def frame_type(f): + return get(f, 'type') + +# kont-push +def kont_push(frame, kont): + return cons(frame, kont) + +# kont-top +def kont_top(kont): + return first(kont) + +# kont-pop +def kont_pop(kont): + return rest(kont) + +# kont-empty? +def kont_empty_p(kont): + return empty_p(kont) + +# kont-capture-to-reset +def kont_capture_to_reset(kont): + def scan(k, captured): + if sx_truthy(empty_p(k)): + return error('shift without enclosing reset') + else: + frame = first(k) + if sx_truthy(((frame_type(frame) == 'reset') if sx_truthy((frame_type(frame) == 'reset')) else (frame_type(frame) == 'reactive-reset'))): + return [captured, rest(k)] + else: + return scan(rest(k), append(captured, [frame])) + return scan(kont, []) + +# has-reactive-reset-frame? +def has_reactive_reset_frame_p(kont): + if sx_truthy(empty_p(kont)): + return False + else: + if sx_truthy((frame_type(first(kont)) == 'reactive-reset')): + return True + else: + return has_reactive_reset_frame_p(rest(kont)) + +# kont-capture-to-reactive-reset +def kont_capture_to_reactive_reset(kont): + def scan(k, captured): + if sx_truthy(empty_p(k)): + return error('reactive deref without enclosing reactive-reset') + else: + frame = first(k) + if sx_truthy((frame_type(frame) == 'reactive-reset')): + return [captured, frame, rest(k)] + else: + return scan(rest(k), append(captured, [frame])) + return scan(kont, []) + + # === Transpiled from page-helpers (pure data transformation helpers) === # special-form-category-map @@ -3629,6 +4096,602 @@ def resource(fetch_fn): return state +# === Transpiled from cek (explicit CEK machine evaluator) === + +# cek-run +def cek_run(state): + if sx_truthy(cek_terminal_p(state)): + return cek_value(state) + else: + return cek_run(cek_step(state)) + +# cek-step +def cek_step(state): + if sx_truthy((cek_phase(state) == 'eval')): + return step_eval(state) + else: + return step_continue(state) + +# step-eval +def step_eval(state): + expr = cek_control(state) + env = cek_env(state) + kont = cek_kont(state) + _match = type_of(expr) + if _match == 'number': + return make_cek_value(expr, env, kont) + elif _match == 'string': + return make_cek_value(expr, env, kont) + elif _match == 'boolean': + return make_cek_value(expr, env, kont) + elif _match == 'nil': + return make_cek_value(NIL, env, kont) + elif _match == 'symbol': + name = symbol_name(expr) + val = (env_get(env, name) if sx_truthy(env_has(env, name)) else (get_primitive(name) if sx_truthy(is_primitive(name)) else (True if sx_truthy((name == 'true')) else (False if sx_truthy((name == 'false')) else (NIL if sx_truthy((name == 'nil')) else error(sx_str('Undefined symbol: ', name))))))) + return make_cek_value(val, env, kont) + elif _match == 'keyword': + return make_cek_value(keyword_name(expr), env, kont) + elif _match == 'dict': + ks = keys(expr) + if sx_truthy(empty_p(ks)): + return make_cek_value({}, env, kont) + else: + first_key = first(ks) + remaining_entries = [] + for k in rest(ks): + remaining_entries.append([k, get(expr, k)]) + return make_cek_state(get(expr, first_key), env, kont_push(make_dict_frame(remaining_entries, [[first_key]], env), kont)) + elif _match == 'list': + if sx_truthy(empty_p(expr)): + return make_cek_value([], env, kont) + else: + return step_eval_list(expr, env, kont) + else: + return make_cek_value(expr, env, kont) + +# step-eval-list +def step_eval_list(expr, env, kont): + head = first(expr) + args = rest(expr) + if sx_truthy((not sx_truthy(((type_of(head) == 'symbol') if sx_truthy((type_of(head) == 'symbol')) else ((type_of(head) == 'lambda') if sx_truthy((type_of(head) == 'lambda')) else (type_of(head) == 'list')))))): + if sx_truthy(empty_p(expr)): + return make_cek_value([], env, kont) + else: + return make_cek_state(first(expr), env, kont_push(make_map_frame(NIL, rest(expr), [], env), kont)) + else: + if sx_truthy((type_of(head) == 'symbol')): + name = symbol_name(head) + if sx_truthy((name == 'if')): + return step_sf_if(args, env, kont) + elif sx_truthy((name == 'when')): + return step_sf_when(args, env, kont) + elif sx_truthy((name == 'cond')): + return step_sf_cond(args, env, kont) + elif sx_truthy((name == 'case')): + return step_sf_case(args, env, kont) + elif sx_truthy((name == 'and')): + return step_sf_and(args, env, kont) + elif sx_truthy((name == 'or')): + return step_sf_or(args, env, kont) + elif sx_truthy((name == 'let')): + return step_sf_let(args, env, kont) + elif sx_truthy((name == 'let*')): + return step_sf_let(args, env, kont) + elif sx_truthy((name == 'lambda')): + return step_sf_lambda(args, env, kont) + elif sx_truthy((name == 'fn')): + return step_sf_lambda(args, env, kont) + elif sx_truthy((name == 'define')): + return step_sf_define(args, env, kont) + elif sx_truthy((name == 'defcomp')): + return make_cek_value(sf_defcomp(args, env), env, kont) + elif sx_truthy((name == 'defisland')): + return make_cek_value(sf_defisland(args, env), env, kont) + elif sx_truthy((name == 'defmacro')): + return make_cek_value(sf_defmacro(args, env), env, kont) + elif sx_truthy((name == 'defstyle')): + return make_cek_value(sf_defstyle(args, env), env, kont) + elif sx_truthy((name == 'defhandler')): + return make_cek_value(sf_defhandler(args, env), env, kont) + elif sx_truthy((name == 'defpage')): + return make_cek_value(sf_defpage(args, env), env, kont) + elif sx_truthy((name == 'defquery')): + return make_cek_value(sf_defquery(args, env), env, kont) + elif sx_truthy((name == 'defaction')): + return make_cek_value(sf_defaction(args, env), env, kont) + elif sx_truthy((name == 'deftype')): + return make_cek_value(sf_deftype(args, env), env, kont) + elif sx_truthy((name == 'defeffect')): + return make_cek_value(sf_defeffect(args, env), env, kont) + elif sx_truthy((name == 'begin')): + return step_sf_begin(args, env, kont) + elif sx_truthy((name == 'do')): + return step_sf_begin(args, env, kont) + elif sx_truthy((name == 'quote')): + return make_cek_value((NIL if sx_truthy(empty_p(args)) else first(args)), env, kont) + elif sx_truthy((name == 'quasiquote')): + return make_cek_value(qq_expand(first(args), env), env, kont) + elif sx_truthy((name == '->')): + return step_sf_thread_first(args, env, kont) + elif sx_truthy((name == 'set!')): + return step_sf_set_b(args, env, kont) + elif sx_truthy((name == 'letrec')): + return make_cek_value(sf_letrec(args, env), env, kont) + elif sx_truthy((name == 'reset')): + return step_sf_reset(args, env, kont) + elif sx_truthy((name == 'shift')): + return step_sf_shift(args, env, kont) + elif sx_truthy((name == 'deref')): + return step_sf_deref(args, env, kont) + elif sx_truthy((name == 'scope')): + return step_sf_scope(args, env, kont) + elif sx_truthy((name == 'provide')): + return step_sf_provide(args, env, kont) + elif sx_truthy((name == 'dynamic-wind')): + return make_cek_value(sf_dynamic_wind(args, env), env, kont) + elif sx_truthy((name == 'map')): + return step_ho_map(args, env, kont) + elif sx_truthy((name == 'map-indexed')): + return make_cek_value(ho_map_indexed(args, env), env, kont) + elif sx_truthy((name == 'filter')): + return step_ho_filter(args, env, kont) + elif sx_truthy((name == 'reduce')): + return step_ho_reduce(args, env, kont) + elif sx_truthy((name == 'some')): + return make_cek_value(ho_some(args, env), env, kont) + elif sx_truthy((name == 'every?')): + return make_cek_value(ho_every(args, env), env, kont) + elif sx_truthy((name == 'for-each')): + return step_ho_for_each(args, env, kont) + elif sx_truthy((env_has(env, name) if not sx_truthy(env_has(env, name)) else is_macro(env_get(env, name)))): + mac = env_get(env, name) + return make_cek_state(expand_macro(mac, args, env), env, kont) + elif sx_truthy((render_active_p() if not sx_truthy(render_active_p()) else is_render_expr(expr))): + return make_cek_value(render_expr(expr, env), env, kont) + else: + return step_eval_call(head, args, env, kont) + else: + return step_eval_call(head, args, env, kont) + +# step-sf-if +def step_sf_if(args, env, kont): + return make_cek_state(first(args), env, kont_push(make_if_frame(nth(args, 1), (nth(args, 2) if sx_truthy((len(args) > 2)) else NIL), env), kont)) + +# step-sf-when +def step_sf_when(args, env, kont): + return make_cek_state(first(args), env, kont_push(make_when_frame(rest(args), env), kont)) + +# step-sf-begin +def step_sf_begin(args, env, kont): + if sx_truthy(empty_p(args)): + return make_cek_value(NIL, env, kont) + else: + if sx_truthy((len(args) == 1)): + return make_cek_state(first(args), env, kont) + else: + return make_cek_state(first(args), env, kont_push(make_begin_frame(rest(args), env), kont)) + +# step-sf-let +def step_sf_let(args, env, kont): + if sx_truthy((type_of(first(args)) == 'symbol')): + return make_cek_value(sf_named_let(args, env), env, kont) + else: + bindings = first(args) + body = rest(args) + local = env_extend(env) + if sx_truthy(empty_p(bindings)): + return step_sf_begin(body, local, kont) + else: + first_binding = (first(bindings) if sx_truthy(((type_of(first(bindings)) == 'list') if not sx_truthy((type_of(first(bindings)) == 'list')) else (len(first(bindings)) == 2))) else [first(bindings), nth(bindings, 1)]) + rest_bindings = (rest(bindings) if sx_truthy(((type_of(first(bindings)) == 'list') if not sx_truthy((type_of(first(bindings)) == 'list')) else (len(first(bindings)) == 2))) else (lambda pairs: _sx_begin(reduce(lambda acc, i: _sx_append(pairs, [nth(bindings, (i * 2)), nth(bindings, ((i * 2) + 1))]), NIL, range(1, (len(bindings) / 2))), pairs))([])) + vname = (symbol_name(first(first_binding)) if sx_truthy((type_of(first(first_binding)) == 'symbol')) else first(first_binding)) + return make_cek_state(nth(first_binding, 1), local, kont_push(make_let_frame(vname, rest_bindings, body, local), kont)) + +# step-sf-define +def step_sf_define(args, env, kont): + name_sym = first(args) + has_effects = ((len(args) >= 4) if not sx_truthy((len(args) >= 4)) else ((type_of(nth(args, 1)) == 'keyword') if not sx_truthy((type_of(nth(args, 1)) == 'keyword')) else (keyword_name(nth(args, 1)) == 'effects'))) + val_idx = (3 if sx_truthy(((len(args) >= 4) if not sx_truthy((len(args) >= 4)) else ((type_of(nth(args, 1)) == 'keyword') if not sx_truthy((type_of(nth(args, 1)) == 'keyword')) else (keyword_name(nth(args, 1)) == 'effects')))) else 1) + effect_list = (nth(args, 2) if sx_truthy(((len(args) >= 4) if not sx_truthy((len(args) >= 4)) else ((type_of(nth(args, 1)) == 'keyword') if not sx_truthy((type_of(nth(args, 1)) == 'keyword')) else (keyword_name(nth(args, 1)) == 'effects')))) else NIL) + return make_cek_state(nth(args, val_idx), env, kont_push(make_define_frame(symbol_name(name_sym), env, has_effects, effect_list), kont)) + +# step-sf-set! +def step_sf_set_b(args, env, kont): + return make_cek_state(nth(args, 1), env, kont_push(make_set_frame(symbol_name(first(args)), env), kont)) + +# step-sf-and +def step_sf_and(args, env, kont): + if sx_truthy(empty_p(args)): + return make_cek_value(True, env, kont) + else: + return make_cek_state(first(args), env, kont_push(make_and_frame(rest(args), env), kont)) + +# step-sf-or +def step_sf_or(args, env, kont): + if sx_truthy(empty_p(args)): + return make_cek_value(False, env, kont) + else: + return make_cek_state(first(args), env, kont_push(make_or_frame(rest(args), env), kont)) + +# step-sf-cond +def step_sf_cond(args, env, kont): + scheme_p = cond_scheme_p(args) + if sx_truthy(scheme_p): + if sx_truthy(empty_p(args)): + return make_cek_value(NIL, env, kont) + else: + clause = first(args) + test = first(clause) + if sx_truthy((((type_of(test) == 'symbol') if not sx_truthy((type_of(test) == 'symbol')) else ((symbol_name(test) == 'else') if sx_truthy((symbol_name(test) == 'else')) else (symbol_name(test) == ':else'))) if sx_truthy(((type_of(test) == 'symbol') if not sx_truthy((type_of(test) == 'symbol')) else ((symbol_name(test) == 'else') if sx_truthy((symbol_name(test) == 'else')) else (symbol_name(test) == ':else')))) else ((type_of(test) == 'keyword') if not sx_truthy((type_of(test) == 'keyword')) else (keyword_name(test) == 'else')))): + return make_cek_state(nth(clause, 1), env, kont) + else: + return make_cek_state(test, env, kont_push(make_cond_frame(args, env, True), kont)) + else: + if sx_truthy((len(args) < 2)): + return make_cek_value(NIL, env, kont) + else: + test = first(args) + if sx_truthy((((type_of(test) == 'keyword') if not sx_truthy((type_of(test) == 'keyword')) else (keyword_name(test) == 'else')) if sx_truthy(((type_of(test) == 'keyword') if not sx_truthy((type_of(test) == 'keyword')) else (keyword_name(test) == 'else'))) else ((type_of(test) == 'symbol') if not sx_truthy((type_of(test) == 'symbol')) else ((symbol_name(test) == 'else') if sx_truthy((symbol_name(test) == 'else')) else (symbol_name(test) == ':else'))))): + return make_cek_state(nth(args, 1), env, kont) + else: + return make_cek_state(test, env, kont_push(make_cond_frame(args, env, False), kont)) + +# step-sf-case +def step_sf_case(args, env, kont): + return make_cek_state(first(args), env, kont_push(make_case_frame(NIL, rest(args), env), kont)) + +# step-sf-thread-first +def step_sf_thread_first(args, env, kont): + return make_cek_state(first(args), env, kont_push(make_thread_frame(rest(args), env), kont)) + +# step-sf-lambda +def step_sf_lambda(args, env, kont): + return make_cek_value(sf_lambda(args, env), env, kont) + +# step-sf-scope +def step_sf_scope(args, env, kont): + return make_cek_value(sf_scope(args, env), env, kont) + +# step-sf-provide +def step_sf_provide(args, env, kont): + return make_cek_value(sf_provide(args, env), env, kont) + +# step-sf-reset +def step_sf_reset(args, env, kont): + return make_cek_state(first(args), env, kont_push(make_reset_frame(env), kont)) + +# step-sf-shift +def step_sf_shift(args, env, kont): + k_name = symbol_name(first(args)) + body = nth(args, 1) + captured_result = kont_capture_to_reset(kont) + captured = first(captured_result) + rest_kont = nth(captured_result, 1) + k = make_cek_continuation(captured, rest_kont) + shift_env = env_extend(env) + shift_env[k_name] = k + return make_cek_state(body, shift_env, rest_kont) + +# step-sf-deref +def step_sf_deref(args, env, kont): + return make_cek_state(first(args), env, kont_push(make_deref_frame(env), kont)) + +# reactive-shift-deref +def reactive_shift_deref(sig, env, kont): + _cells = {} + scan_result = kont_capture_to_reactive_reset(kont) + captured_frames = first(scan_result) + reset_frame = nth(scan_result, 1) + remaining_kont = nth(scan_result, 2) + update_fn = get(reset_frame, 'update-fn') + _cells['sub_disposers'] = [] + subscriber = _sx_fn(lambda : ( + for_each(lambda d: invoke(d), _cells['sub_disposers']), + _sx_cell_set(_cells, 'sub_disposers', []), + (lambda new_reset: (lambda new_kont: with_island_scope(lambda d: _sx_append(_cells['sub_disposers'], d), lambda : cek_run(make_cek_value(signal_value(sig), env, new_kont))))(concat(captured_frames, [new_reset], remaining_kont)))(make_reactive_reset_frame(env, update_fn, False)) +)[-1]) + signal_add_sub(sig, subscriber) + register_in_scope(_sx_fn(lambda : ( + signal_remove_sub(sig, subscriber), + for_each(lambda d: invoke(d), _cells['sub_disposers']) +)[-1])) + initial_kont = concat(captured_frames, [reset_frame], remaining_kont) + return make_cek_value(signal_value(sig), env, initial_kont) + +# step-eval-call +def step_eval_call(head, args, env, kont): + return make_cek_state(head, env, kont_push(make_arg_frame(NIL, [], args, env, args), kont)) + +# step-ho-map +def step_ho_map(args, env, kont): + return make_cek_value(ho_map(args, env), env, kont) + +# step-ho-filter +def step_ho_filter(args, env, kont): + return make_cek_value(ho_filter(args, env), env, kont) + +# step-ho-reduce +def step_ho_reduce(args, env, kont): + return make_cek_value(ho_reduce(args, env), env, kont) + +# step-ho-for-each +def step_ho_for_each(args, env, kont): + return make_cek_value(ho_for_each(args, env), env, kont) + +# step-continue +def step_continue(state): + value = cek_value(state) + env = cek_env(state) + kont = cek_kont(state) + if sx_truthy(kont_empty_p(kont)): + return state + else: + frame = kont_top(kont) + rest_k = kont_pop(kont) + ft = frame_type(frame) + if sx_truthy((ft == 'if')): + if sx_truthy((value if not sx_truthy(value) else (not sx_truthy(is_nil(value))))): + return make_cek_state(get(frame, 'then'), get(frame, 'env'), rest_k) + else: + if sx_truthy(is_nil(get(frame, 'else'))): + return make_cek_value(NIL, env, rest_k) + else: + return make_cek_state(get(frame, 'else'), get(frame, 'env'), rest_k) + elif sx_truthy((ft == 'when')): + if sx_truthy((value if not sx_truthy(value) else (not sx_truthy(is_nil(value))))): + body = get(frame, 'body') + fenv = get(frame, 'env') + if sx_truthy(empty_p(body)): + return make_cek_value(NIL, fenv, rest_k) + else: + if sx_truthy((len(body) == 1)): + return make_cek_state(first(body), fenv, rest_k) + else: + return make_cek_state(first(body), fenv, kont_push(make_begin_frame(rest(body), fenv), rest_k)) + else: + return make_cek_value(NIL, env, rest_k) + elif sx_truthy((ft == 'begin')): + remaining = get(frame, 'remaining') + fenv = get(frame, 'env') + if sx_truthy(empty_p(remaining)): + return make_cek_value(value, fenv, rest_k) + else: + if sx_truthy((len(remaining) == 1)): + return make_cek_state(first(remaining), fenv, rest_k) + else: + return make_cek_state(first(remaining), fenv, kont_push(make_begin_frame(rest(remaining), fenv), rest_k)) + elif sx_truthy((ft == 'let')): + name = get(frame, 'name') + remaining = get(frame, 'remaining') + body = get(frame, 'body') + local = get(frame, 'env') + local[name] = value + if sx_truthy(empty_p(remaining)): + return step_sf_begin(body, local, rest_k) + else: + next_binding = first(remaining) + vname = (symbol_name(first(next_binding)) if sx_truthy((type_of(first(next_binding)) == 'symbol')) else first(next_binding)) + return make_cek_state(nth(next_binding, 1), local, kont_push(make_let_frame(vname, rest(remaining), body, local), rest_k)) + elif sx_truthy((ft == 'define')): + name = get(frame, 'name') + fenv = get(frame, 'env') + has_effects = get(frame, 'has-effects') + effect_list = get(frame, 'effect-list') + if sx_truthy((is_lambda(value) if not sx_truthy(is_lambda(value)) else is_nil(lambda_name(value)))): + value.name = name + fenv[name] = value + if sx_truthy(has_effects): + effect_names = (map(lambda e: (symbol_name(e) if sx_truthy((type_of(e) == 'symbol')) else sx_str(e)), effect_list) if sx_truthy((type_of(effect_list) == 'list')) else [sx_str(effect_list)]) + effect_anns = (env_get(fenv, '*effect-annotations*') if sx_truthy(env_has(fenv, '*effect-annotations*')) else {}) + effect_anns[name] = effect_names + fenv['*effect-annotations*'] = effect_anns + return make_cek_value(value, fenv, rest_k) + elif sx_truthy((ft == 'set')): + name = get(frame, 'name') + fenv = get(frame, 'env') + fenv[name] = value + return make_cek_value(value, env, rest_k) + elif sx_truthy((ft == 'and')): + if sx_truthy((not sx_truthy(value))): + return make_cek_value(value, env, rest_k) + else: + remaining = get(frame, 'remaining') + if sx_truthy(empty_p(remaining)): + return make_cek_value(value, env, rest_k) + else: + return make_cek_state(first(remaining), get(frame, 'env'), (rest_k if sx_truthy((len(remaining) == 1)) else kont_push(make_and_frame(rest(remaining), get(frame, 'env')), rest_k))) + elif sx_truthy((ft == 'or')): + if sx_truthy(value): + return make_cek_value(value, env, rest_k) + else: + remaining = get(frame, 'remaining') + if sx_truthy(empty_p(remaining)): + return make_cek_value(False, env, rest_k) + else: + return make_cek_state(first(remaining), get(frame, 'env'), (rest_k if sx_truthy((len(remaining) == 1)) else kont_push(make_or_frame(rest(remaining), get(frame, 'env')), rest_k))) + elif sx_truthy((ft == 'cond')): + remaining = get(frame, 'remaining') + fenv = get(frame, 'env') + scheme_p = get(frame, 'scheme') + if sx_truthy(scheme_p): + if sx_truthy(value): + return make_cek_state(nth(first(remaining), 1), fenv, rest_k) + else: + next_clauses = rest(remaining) + if sx_truthy(empty_p(next_clauses)): + return make_cek_value(NIL, fenv, rest_k) + else: + next_clause = first(next_clauses) + next_test = first(next_clause) + if sx_truthy((((type_of(next_test) == 'symbol') if not sx_truthy((type_of(next_test) == 'symbol')) else ((symbol_name(next_test) == 'else') if sx_truthy((symbol_name(next_test) == 'else')) else (symbol_name(next_test) == ':else'))) if sx_truthy(((type_of(next_test) == 'symbol') if not sx_truthy((type_of(next_test) == 'symbol')) else ((symbol_name(next_test) == 'else') if sx_truthy((symbol_name(next_test) == 'else')) else (symbol_name(next_test) == ':else')))) else ((type_of(next_test) == 'keyword') if not sx_truthy((type_of(next_test) == 'keyword')) else (keyword_name(next_test) == 'else')))): + return make_cek_state(nth(next_clause, 1), fenv, rest_k) + else: + return make_cek_state(next_test, fenv, kont_push(make_cond_frame(next_clauses, fenv, True), rest_k)) + else: + if sx_truthy(value): + return make_cek_state(nth(remaining, 1), fenv, rest_k) + else: + next = slice(remaining, 2) + if sx_truthy((len(next) < 2)): + return make_cek_value(NIL, fenv, rest_k) + else: + next_test = first(next) + if sx_truthy((((type_of(next_test) == 'keyword') if not sx_truthy((type_of(next_test) == 'keyword')) else (keyword_name(next_test) == 'else')) if sx_truthy(((type_of(next_test) == 'keyword') if not sx_truthy((type_of(next_test) == 'keyword')) else (keyword_name(next_test) == 'else'))) else ((type_of(next_test) == 'symbol') if not sx_truthy((type_of(next_test) == 'symbol')) else ((symbol_name(next_test) == 'else') if sx_truthy((symbol_name(next_test) == 'else')) else (symbol_name(next_test) == ':else'))))): + return make_cek_state(nth(next, 1), fenv, rest_k) + else: + return make_cek_state(next_test, fenv, kont_push(make_cond_frame(next, fenv, False), rest_k)) + elif sx_truthy((ft == 'case')): + match_val = get(frame, 'match-val') + remaining = get(frame, 'remaining') + fenv = get(frame, 'env') + if sx_truthy(is_nil(match_val)): + return sf_case_step_loop(value, remaining, fenv, rest_k) + else: + return sf_case_step_loop(match_val, remaining, fenv, rest_k) + elif sx_truthy((ft == 'thread')): + remaining = get(frame, 'remaining') + fenv = get(frame, 'env') + if sx_truthy(empty_p(remaining)): + return make_cek_value(value, fenv, rest_k) + else: + form = first(remaining) + rest_forms = rest(remaining) + result = ((lambda f: (lambda rargs: (lambda all_args: (apply(f, all_args) if sx_truthy((is_callable(f) if not sx_truthy(is_callable(f)) else (not sx_truthy(is_lambda(f))))) else (trampoline(call_lambda(f, all_args, fenv)) if sx_truthy(is_lambda(f)) else error(sx_str('-> form not callable: ', inspect(f))))))(cons(value, rargs)))(map(lambda a: trampoline(eval_expr(a, fenv)), rest(form))))(trampoline(eval_expr(first(form), fenv))) if sx_truthy((type_of(form) == 'list')) else (lambda f: (f(value) if sx_truthy((is_callable(f) if not sx_truthy(is_callable(f)) else (not sx_truthy(is_lambda(f))))) else (trampoline(call_lambda(f, [value], fenv)) if sx_truthy(is_lambda(f)) else error(sx_str('-> form not callable: ', inspect(f))))))(trampoline(eval_expr(form, fenv)))) + if sx_truthy(empty_p(rest_forms)): + return make_cek_value(result, fenv, rest_k) + else: + return make_cek_value(result, fenv, kont_push(make_thread_frame(rest_forms, fenv), rest_k)) + elif sx_truthy((ft == 'arg')): + f = get(frame, 'f') + evaled = get(frame, 'evaled') + remaining = get(frame, 'remaining') + fenv = get(frame, 'env') + raw_args = get(frame, 'raw-args') + if sx_truthy(is_nil(f)): + if sx_truthy(empty_p(remaining)): + return continue_with_call(value, [], fenv, raw_args, rest_k) + else: + return make_cek_state(first(remaining), fenv, kont_push(make_arg_frame(value, [], rest(remaining), fenv, raw_args), rest_k)) + else: + new_evaled = append(evaled, [value]) + if sx_truthy(empty_p(remaining)): + return continue_with_call(f, new_evaled, fenv, raw_args, rest_k) + else: + return make_cek_state(first(remaining), fenv, kont_push(make_arg_frame(f, new_evaled, rest(remaining), fenv, raw_args), rest_k)) + elif sx_truthy((ft == 'dict')): + remaining = get(frame, 'remaining') + results = get(frame, 'results') + fenv = get(frame, 'env') + last_result = last(results) + completed = append(slice(results, 0, (len(results) - 1)), [[first(last_result), value]]) + if sx_truthy(empty_p(remaining)): + d = {} + for pair in completed: + d[first(pair)] = nth(pair, 1) + return make_cek_value(d, fenv, rest_k) + else: + next_entry = first(remaining) + return make_cek_state(nth(next_entry, 1), fenv, kont_push(make_dict_frame(rest(remaining), append(completed, [[first(next_entry)]]), fenv), rest_k)) + elif sx_truthy((ft == 'reset')): + return make_cek_value(value, env, rest_k) + elif sx_truthy((ft == 'deref')): + val = value + fenv = get(frame, 'env') + if sx_truthy((not sx_truthy(is_signal(val)))): + return make_cek_value(val, fenv, rest_k) + else: + if sx_truthy(has_reactive_reset_frame_p(rest_k)): + return reactive_shift_deref(val, fenv, rest_k) + else: + ctx = sx_context('sx-reactive', NIL) + if sx_truthy(ctx): + dep_list = get(ctx, 'deps') + notify_fn = get(ctx, 'notify') + if sx_truthy((not sx_truthy(contains_p(dep_list, val)))): + dep_list.append(val) + signal_add_sub(val, notify_fn) + return make_cek_value(signal_value(val), fenv, rest_k) + elif sx_truthy((ft == 'reactive-reset')): + update_fn = get(frame, 'update-fn') + first_p = get(frame, 'first-render') + if sx_truthy((update_fn if not sx_truthy(update_fn) else (not sx_truthy(first_p)))): + invoke(update_fn, value) + return make_cek_value(value, env, rest_k) + elif sx_truthy((ft == 'scope')): + name = get(frame, 'name') + remaining = get(frame, 'remaining') + fenv = get(frame, 'env') + if sx_truthy(empty_p(remaining)): + scope_pop(name) + return make_cek_value(value, fenv, rest_k) + else: + return make_cek_state(first(remaining), fenv, kont_push(make_scope_frame(name, rest(remaining), fenv), rest_k)) + else: + return error(sx_str('Unknown frame type: ', ft)) + +# continue-with-call +def continue_with_call(f, args, env, raw_args, kont): + if sx_truthy(continuation_p(f)): + arg = (NIL if sx_truthy(empty_p(args)) else first(args)) + cont_data = continuation_data(f) + captured = get(cont_data, 'captured') + rest_k = get(cont_data, 'rest-kont') + return make_cek_value(arg, env, concat(captured, rest_k)) + elif sx_truthy((is_callable(f) if not sx_truthy(is_callable(f)) else ((not sx_truthy(is_lambda(f))) if not sx_truthy((not sx_truthy(is_lambda(f)))) else ((not sx_truthy(is_component(f))) if not sx_truthy((not sx_truthy(is_component(f)))) else (not sx_truthy(is_island(f))))))): + return make_cek_value(apply(f, args), env, kont) + elif sx_truthy(is_lambda(f)): + params = lambda_params(f) + local = env_merge(lambda_closure(f), env) + if sx_truthy((len(args) > len(params))): + return error(sx_str((lambda_name(f) if sx_truthy(lambda_name(f)) else 'lambda'), ' expects ', len(params), ' args, got ', len(args))) + else: + for pair in zip(params, args): + local[first(pair)] = nth(pair, 1) + for p in slice(params, len(args)): + local[p] = NIL + return make_cek_state(lambda_body(f), local, kont) + elif sx_truthy((is_component(f) if sx_truthy(is_component(f)) else is_island(f))): + parsed = parse_keyword_args(raw_args, env) + kwargs = first(parsed) + children = nth(parsed, 1) + local = env_merge(component_closure(f), env) + for p in component_params(f): + local[p] = (dict_get(kwargs, p) if sx_truthy(dict_get(kwargs, p)) else NIL) + if sx_truthy(component_has_children(f)): + local['children'] = children + return make_cek_state(component_body(f), local, kont) + else: + return error(sx_str('Not callable: ', inspect(f))) + +# sf-case-step-loop +def sf_case_step_loop(match_val, clauses, env, kont): + if sx_truthy((len(clauses) < 2)): + return make_cek_value(NIL, env, kont) + else: + test = first(clauses) + body = nth(clauses, 1) + if sx_truthy((((type_of(test) == 'keyword') if not sx_truthy((type_of(test) == 'keyword')) else (keyword_name(test) == 'else')) if sx_truthy(((type_of(test) == 'keyword') if not sx_truthy((type_of(test) == 'keyword')) else (keyword_name(test) == 'else'))) else ((type_of(test) == 'symbol') if not sx_truthy((type_of(test) == 'symbol')) else ((symbol_name(test) == 'else') if sx_truthy((symbol_name(test) == 'else')) else (symbol_name(test) == ':else'))))): + return make_cek_state(body, env, kont) + else: + test_val = trampoline(eval_expr(test, env)) + if sx_truthy((match_val == test_val)): + return make_cek_state(body, env, kont) + else: + return sf_case_step_loop(match_val, slice(clauses, 2), env, kont) + +# eval-expr-cek +def eval_expr_cek(expr, env): + return cek_run(make_cek_state(expr, env, [])) + +# trampoline-cek +def trampoline_cek(val): + if sx_truthy(is_thunk(val)): + return eval_expr_cek(thunk_expr(val), thunk_env(val)) + else: + return val + + # === Transpiled from adapter-async === # async-render @@ -4570,6 +5633,31 @@ def sf_set_bang(args, env): return value +# Override recursive cek_run with iterative loop (avoids Python stack overflow) +def cek_run(state): + """Drive CEK machine to completion (iterative).""" + while not cek_terminal_p(state): + state = cek_step(state) + return cek_value(state) + +# CEK is the canonical evaluator — override eval_expr to use it. +# The tree-walk evaluator (eval_expr from eval.sx) is superseded. +_tree_walk_eval_expr = eval_expr + +def eval_expr(expr, env): + """Evaluate expr using the CEK machine.""" + return cek_run(make_cek_state(expr, env, [])) + +# CEK never produces thunks — trampoline becomes identity +_tree_walk_trampoline = trampoline + +def trampoline(val): + """In CEK mode, values are immediate — resolve any legacy thunks.""" + if is_thunk(val): + return eval_expr(thunk_expr(val), thunk_env(val)) + return val + + # ========================================================================= # Public API # =========================================================================