From 2ef3f03db38a484e833172cbd56dedf11eba184e Mon Sep 17 00:00:00 2001 From: giles Date: Sun, 15 Mar 2026 15:08:02 +0000 Subject: [PATCH] Fix eval-expr stub: define as CEK wrapper, not error stub The eval-expr forward declaration was an error-throwing stub that the CEK fixup was supposed to override. If anything prevented the fixup from running (or if eval-expr was captured by value before the fixup), the stub would throw "CEK fixup not loaded". Fix: define eval-expr and trampoline as real CEK wrappers at the end of evaluator.sx (after cek-run is defined). The forward declaration is now a harmless nil-returning stub. The fixup still overrides with the iterative version, but even without it, eval works correctly. Co-Authored-By: Claude Opus 4.6 (1M context) --- shared/static/scripts/sx-browser.js | 12 ++++++++++-- spec/evaluator.sx | 26 ++++++++++++++++++++++---- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/shared/static/scripts/sx-browser.js b/shared/static/scripts/sx-browser.js index 67ce7af..3e9632d 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-15T14:39:41Z"; + var SX_VERSION = "2026-03-15T15:05:23Z"; function isNil(x) { return x === NIL || x === null || x === undefined; } function isSxTruthy(x) { return x !== false && !isNil(x); } @@ -1064,7 +1064,7 @@ PRIMITIVES["value-matches-type?"] = valueMatchesType_p; PRIMITIVES["strict-check-args"] = strictCheckArgs; // eval-expr - var evalExpr = function(expr, env) { return error("eval-expr: CEK fixup not loaded"); }; + var evalExpr = function(expr, env) { return NIL; }; PRIMITIVES["eval-expr"] = evalExpr; // call-lambda @@ -2053,6 +2053,14 @@ PRIMITIVES["freeze-to-cid"] = freezeToCid; })(); }; PRIMITIVES["thaw-from-cid"] = thawFromCid; + // eval-expr + var evalExpr = function(expr, env) { return cekRun(makeCekState(expr, env, [])); }; +PRIMITIVES["eval-expr"] = evalExpr; + + // trampoline + var trampoline = function(val) { return (isSxTruthy(isThunk(val)) ? evalExpr(thunkExpr(val), thunkEnv(val)) : val); }; +PRIMITIVES["trampoline"] = trampoline; + // === Transpiled from render (core) === diff --git a/spec/evaluator.sx b/spec/evaluator.sx index 3127c12..72658fd 100644 --- a/spec/evaluator.sx +++ b/spec/evaluator.sx @@ -453,11 +453,10 @@ ;; trampoline = (val) → if thunk? then eval-expr(thunk-expr, thunk-env) else val ;; All evaluation goes through the CEK machine. +;; eval-expr: forward declaration — redefined at end of file after cek-run exists. +;; This stub is needed so functions between here and Part 3 can reference eval-expr. (define eval-expr - (fn (expr (env :as dict)) - ;; Stub — overridden by CEK fixup before any code runs. - ;; If this executes, CEK fixup failed to load. - (error "eval-expr: CEK fixup not loaded"))) + (fn (expr (env :as dict)) nil)) @@ -2470,3 +2469,22 @@ (when sx-text (thaw-from-sx sx-text) true)))) + + +;; ************************************************************************** +;; eval-expr / trampoline — canonical definitions (after cek-run is defined) +;; ************************************************************************** +;; +;; These override the forward declarations from Part 2. All evaluation +;; goes through the CEK machine. The CEK fixups in the host platform +;; may further override these (e.g., to make cek-run iterative). + +(define eval-expr + (fn (expr (env :as dict)) + (cek-run (make-cek-state expr env (list))))) + +(define trampoline + (fn (val) + (if (thunk? val) + (eval-expr (thunk-expr val) (thunk-env val)) + val)))