diff --git a/shared/static/scripts/sx-browser.js b/shared/static/scripts/sx-browser.js index 7fe53e3..d9e0f40 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-23T15:29:13Z"; + var SX_VERSION = "2026-03-23T15:39:32Z"; function isNil(x) { return x === NIL || x === null || x === undefined; } function isSxTruthy(x) { return x !== false && !isNil(x); } @@ -2464,7 +2464,7 @@ PRIMITIVES["render-to-html"] = renderToHtml; PRIMITIVES["render-value-to-html"] = renderValueToHtml; // 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; // 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() { var branch = evalCond(rest(expr), 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); 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() { @@ -2530,7 +2533,7 @@ PRIMITIVES["render-list-to-html"] = renderListToHtml; scopePop(provName); return result; })(); -})() : renderValueToHtml(trampoline(evalExpr(expr, env)), env)))))))))))))); }; +})() : renderValueToHtml(trampoline(evalExpr(expr, env)), env))))))))))))))); }; PRIMITIVES["dispatch-html-form"] = dispatchHtmlForm; // render-lambda-html diff --git a/web/adapter-html.sx b/web/adapter-html.sx index 29125dd..5f2c13f 100644 --- a/web/adapter-html.sx +++ b/web/adapter-html.sx @@ -56,7 +56,7 @@ ;; -------------------------------------------------------------------------- (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" "deftype" "defeffect" "map" "map-indexed" "filter" "for-each" "scope" "provide")) @@ -173,6 +173,13 @@ (= name "case") (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. (or (= name "let") (= name "let*")) (let ((local (process-bindings (nth expr 1) env)))