Add letrec to render-aware HTML forms — stepper island now SSRs
letrec in adapter-html.sx: evaluate via CEK (which handles mutual recursion and returns a thunk), then render-value-to-html unwraps the thunk and renders the expression with the letrec's local env. Both islands (~layouts/header and ~home/stepper) now render server-side with hydration markers and CSS classes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -14,7 +14,7 @@
|
|||||||
// =========================================================================
|
// =========================================================================
|
||||||
|
|
||||||
var NIL = Object.freeze({ _nil: true, toString: function() { return "nil"; } });
|
var NIL = Object.freeze({ _nil: true, toString: function() { return "nil"; } });
|
||||||
var SX_VERSION = "2026-03-23T15:29:13Z";
|
var SX_VERSION = "2026-03-23T15:39:32Z";
|
||||||
|
|
||||||
function isNil(x) { return x === NIL || x === null || x === undefined; }
|
function isNil(x) { return x === NIL || x === null || x === undefined; }
|
||||||
function isSxTruthy(x) { return x !== false && !isNil(x); }
|
function isSxTruthy(x) { return x !== false && !isNil(x); }
|
||||||
@@ -2464,7 +2464,7 @@ PRIMITIVES["render-to-html"] = renderToHtml;
|
|||||||
PRIMITIVES["render-value-to-html"] = renderValueToHtml;
|
PRIMITIVES["render-value-to-html"] = renderValueToHtml;
|
||||||
|
|
||||||
// RENDER_HTML_FORMS
|
// RENDER_HTML_FORMS
|
||||||
var RENDER_HTML_FORMS = ["if", "when", "cond", "case", "let", "let*", "begin", "do", "define", "defcomp", "defisland", "defmacro", "defstyle", "defhandler", "deftype", "defeffect", "map", "map-indexed", "filter", "for-each", "scope", "provide"];
|
var RENDER_HTML_FORMS = ["if", "when", "cond", "case", "let", "let*", "letrec", "begin", "do", "define", "defcomp", "defisland", "defmacro", "defstyle", "defhandler", "deftype", "defeffect", "map", "map-indexed", "filter", "for-each", "scope", "provide"];
|
||||||
PRIMITIVES["RENDER_HTML_FORMS"] = RENDER_HTML_FORMS;
|
PRIMITIVES["RENDER_HTML_FORMS"] = RENDER_HTML_FORMS;
|
||||||
|
|
||||||
// render-html-form?
|
// render-html-form?
|
||||||
@@ -2492,7 +2492,10 @@ PRIMITIVES["render-list-to-html"] = renderListToHtml;
|
|||||||
})() : (isSxTruthy((name == "when")) ? (isSxTruthy(!isSxTruthy(trampoline(evalExpr(nth(expr, 1), env)))) ? "" : (isSxTruthy((len(expr) == 3)) ? renderToHtml(nth(expr, 2), env) : join("", map(function(i) { return renderToHtml(nth(expr, i), env); }, range(2, len(expr)))))) : (isSxTruthy((name == "cond")) ? (function() {
|
})() : (isSxTruthy((name == "when")) ? (isSxTruthy(!isSxTruthy(trampoline(evalExpr(nth(expr, 1), env)))) ? "" : (isSxTruthy((len(expr) == 3)) ? renderToHtml(nth(expr, 2), env) : join("", map(function(i) { return renderToHtml(nth(expr, i), env); }, range(2, len(expr)))))) : (isSxTruthy((name == "cond")) ? (function() {
|
||||||
var branch = evalCond(rest(expr), env);
|
var branch = evalCond(rest(expr), env);
|
||||||
return (isSxTruthy(branch) ? renderToHtml(branch, env) : "");
|
return (isSxTruthy(branch) ? renderToHtml(branch, env) : "");
|
||||||
})() : (isSxTruthy((name == "case")) ? renderToHtml(trampoline(evalExpr(expr, env)), env) : (isSxTruthy(sxOr((name == "let"), (name == "let*"))) ? (function() {
|
})() : (isSxTruthy((name == "case")) ? renderToHtml(trampoline(evalExpr(expr, env)), env) : (isSxTruthy((name == "letrec")) ? (function() {
|
||||||
|
var result = evalExpr(expr, env);
|
||||||
|
return renderValueToHtml(result, env);
|
||||||
|
})() : (isSxTruthy(sxOr((name == "let"), (name == "let*"))) ? (function() {
|
||||||
var local = processBindings(nth(expr, 1), env);
|
var local = processBindings(nth(expr, 1), env);
|
||||||
return (isSxTruthy((len(expr) == 3)) ? renderToHtml(nth(expr, 2), local) : join("", map(function(i) { return renderToHtml(nth(expr, i), local); }, range(2, len(expr)))));
|
return (isSxTruthy((len(expr) == 3)) ? renderToHtml(nth(expr, 2), local) : join("", map(function(i) { return renderToHtml(nth(expr, i), local); }, range(2, len(expr)))));
|
||||||
})() : (isSxTruthy(sxOr((name == "begin"), (name == "do"))) ? (isSxTruthy((len(expr) == 2)) ? renderToHtml(nth(expr, 1), env) : join("", map(function(i) { return renderToHtml(nth(expr, i), env); }, range(1, len(expr))))) : (isSxTruthy(isDefinitionForm(name)) ? (trampoline(evalExpr(expr, env)), "") : (isSxTruthy((name == "map")) ? (function() {
|
})() : (isSxTruthy(sxOr((name == "begin"), (name == "do"))) ? (isSxTruthy((len(expr) == 2)) ? renderToHtml(nth(expr, 1), env) : join("", map(function(i) { return renderToHtml(nth(expr, i), env); }, range(1, len(expr))))) : (isSxTruthy(isDefinitionForm(name)) ? (trampoline(evalExpr(expr, env)), "") : (isSxTruthy((name == "map")) ? (function() {
|
||||||
@@ -2530,7 +2533,7 @@ PRIMITIVES["render-list-to-html"] = renderListToHtml;
|
|||||||
scopePop(provName);
|
scopePop(provName);
|
||||||
return result;
|
return result;
|
||||||
})();
|
})();
|
||||||
})() : renderValueToHtml(trampoline(evalExpr(expr, env)), env)))))))))))))); };
|
})() : renderValueToHtml(trampoline(evalExpr(expr, env)), env))))))))))))))); };
|
||||||
PRIMITIVES["dispatch-html-form"] = dispatchHtmlForm;
|
PRIMITIVES["dispatch-html-form"] = dispatchHtmlForm;
|
||||||
|
|
||||||
// render-lambda-html
|
// render-lambda-html
|
||||||
|
|||||||
@@ -56,7 +56,7 @@
|
|||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
|
|
||||||
(define RENDER_HTML_FORMS
|
(define RENDER_HTML_FORMS
|
||||||
(list "if" "when" "cond" "case" "let" "let*" "begin" "do"
|
(list "if" "when" "cond" "case" "let" "let*" "letrec" "begin" "do"
|
||||||
"define" "defcomp" "defisland" "defmacro" "defstyle" "defhandler"
|
"define" "defcomp" "defisland" "defmacro" "defstyle" "defhandler"
|
||||||
"deftype" "defeffect"
|
"deftype" "defeffect"
|
||||||
"map" "map-indexed" "filter" "for-each" "scope" "provide"))
|
"map" "map-indexed" "filter" "for-each" "scope" "provide"))
|
||||||
@@ -173,6 +173,13 @@
|
|||||||
(= name "case")
|
(= name "case")
|
||||||
(render-to-html (trampoline (eval-expr expr env)) env)
|
(render-to-html (trampoline (eval-expr expr env)) env)
|
||||||
|
|
||||||
|
;; letrec — evaluate via CEK, render the result.
|
||||||
|
;; sf-letrec returns a thunk; the thunk handler in render-value-to-html
|
||||||
|
;; unwraps it and renders the expression with the letrec's local env.
|
||||||
|
(= name "letrec")
|
||||||
|
(let ((result (eval-expr expr env)))
|
||||||
|
(render-value-to-html result env))
|
||||||
|
|
||||||
;; let / let* — single body: pass through. Multi: join strings.
|
;; let / let* — single body: pass through. Multi: join strings.
|
||||||
(or (= name "let") (= name "let*"))
|
(or (= name "let") (= name "let*"))
|
||||||
(let ((local (process-bindings (nth expr 1) env)))
|
(let ((local (process-bindings (nth expr 1) env)))
|
||||||
|
|||||||
Reference in New Issue
Block a user