Fix CSSX styling: trampoline wiring + scope-emit!/emitted for adapter-html.sx

Root causes of missing CSSX classes in SSR:

1. _sx_trampoline_fn in sx_primitives.ml was never wired — call_any in
   HO forms (map/filter/for-each) returned unresolved Thunks, so callbacks
   like render-lambda-html's param binding never executed. Fixed in
   bootstrap.py FIXUPS: wire Sx_primitives._sx_trampoline_fn after eval_expr.

2. adapter-html.sx used (emit! ...) and (emitted ...) which are CEK special
   forms (walk kont for ScopeAccFrame), but scope-push!/scope-pop! use the
   hashtable. CEK frames and hashtable are two different scope systems.
   Fixed: adapter uses scope-emit!/scope-emitted (hashtable primitives).

3. env-* operations (env-has?, env-get, env-bind!, env-set!, env-extend,
   env-merge) only accepted Env type. adapter-html.sx passes Dict as env.
   Fixed: all env ops go through unwrap_env which handles Dict/Nil.

Also: fix merge conflict in sx/sx/geography/index.sx, remove duplicate
scope primitives from sx_primitives.ml (sx_server.ml registers them).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-24 02:23:00 +00:00
parent e4cabcbb59
commit 7d793ec76c
8 changed files with 151 additions and 87 deletions

View File

@@ -14,7 +14,7 @@
// =========================================================================
var NIL = Object.freeze({ _nil: true, toString: function() { return "nil"; } });
var SX_VERSION = "2026-03-24T01:05:46Z";
var SX_VERSION = "2026-03-24T02:21:48Z";
function isNil(x) { return x === NIL || x === null || x === undefined; }
function isSxTruthy(x) { return x !== false && !isNil(x); }
@@ -520,6 +520,7 @@
}
PRIMITIVES["scope-emit!"] = scopeEmit;
PRIMITIVES["scope-peek"] = scopePeek;
PRIMITIVES["scope-emitted"] = sxEmitted;
// ---- VM stack primitives ----
// The VM spec (vm.sx) requires these array-like operations.
@@ -540,6 +541,11 @@
if (name in PRIMITIVES) return PRIMITIVES[name];
throw new Error("VM undefined: " + name);
};
PRIMITIVES["call-primitive"] = function(name, args) {
if (!(name in PRIMITIVES)) throw new Error("VM undefined: " + name);
var fn = PRIMITIVES[name];
return fn.apply(null, Array.isArray(args) ? args : []);
};
PRIMITIVES["primitive?"] = function(name) {
return name in PRIMITIVES;
};
@@ -2492,11 +2498,11 @@ PRIMITIVES["serialize"] = serialize;
// render-to-html
var renderToHtml = function(expr, env) { setRenderActiveB(true);
return (function() { var _m = typeOf(expr); if (_m == "nil") return ""; if (_m == "string") return escapeHtml(expr); if (_m == "number") return (String(expr)); if (_m == "boolean") return (isSxTruthy(expr) ? "true" : "false"); if (_m == "list") return (isSxTruthy(isEmpty(expr)) ? "" : renderListToHtml(expr, env)); if (_m == "symbol") return renderValueToHtml(trampoline(evalExpr(expr, env)), env); if (_m == "keyword") return escapeHtml(keywordName(expr)); if (_m == "raw-html") return rawHtmlContent(expr); if (_m == "spread") return (sxEmit("element-attrs", spreadAttrs(expr)), ""); if (_m == "thunk") return renderToHtml(thunkExpr(expr), thunkEnv(expr)); return renderValueToHtml(trampoline(evalExpr(expr, env)), env); })(); };
return (function() { var _m = typeOf(expr); if (_m == "nil") return ""; if (_m == "string") return escapeHtml(expr); if (_m == "number") return (String(expr)); if (_m == "boolean") return (isSxTruthy(expr) ? "true" : "false"); if (_m == "list") return (isSxTruthy(isEmpty(expr)) ? "" : renderListToHtml(expr, env)); if (_m == "symbol") return renderValueToHtml(trampoline(evalExpr(expr, env)), env); if (_m == "keyword") return escapeHtml(keywordName(expr)); if (_m == "raw-html") return rawHtmlContent(expr); if (_m == "spread") return (scopeEmit("element-attrs", spreadAttrs(expr)), ""); if (_m == "thunk") return renderToHtml(thunkExpr(expr), thunkEnv(expr)); return renderValueToHtml(trampoline(evalExpr(expr, env)), env); })(); };
PRIMITIVES["render-to-html"] = renderToHtml;
// render-value-to-html
var renderValueToHtml = function(val, env) { return (function() { var _m = typeOf(val); if (_m == "nil") return ""; if (_m == "string") return escapeHtml(val); if (_m == "number") return (String(val)); if (_m == "boolean") return (isSxTruthy(val) ? "true" : "false"); if (_m == "list") return renderListToHtml(val, env); if (_m == "raw-html") return rawHtmlContent(val); if (_m == "spread") return (sxEmit("element-attrs", spreadAttrs(val)), ""); if (_m == "thunk") return renderToHtml(thunkExpr(val), thunkEnv(val)); return escapeHtml((String(val))); })(); };
var renderValueToHtml = function(val, env) { return (function() { var _m = typeOf(val); if (_m == "nil") return ""; if (_m == "string") return escapeHtml(val); if (_m == "number") return (String(val)); if (_m == "boolean") return (isSxTruthy(val) ? "true" : "false"); if (_m == "list") return renderListToHtml(val, env); if (_m == "raw-html") return rawHtmlContent(val); if (_m == "spread") return (scopeEmit("element-attrs", spreadAttrs(val)), ""); if (_m == "thunk") return renderToHtml(thunkExpr(val), thunkEnv(val)); return escapeHtml((String(val))); })(); };
PRIMITIVES["render-value-to-html"] = renderValueToHtml;
// RENDER_HTML_FORMS
@@ -2624,7 +2630,7 @@ PRIMITIVES["render-html-component"] = renderHtmlComponent;
var isVoid = contains(VOID_ELEMENTS, tag);
return (isSxTruthy(isVoid) ? (String("<") + String(tag) + String(renderAttrs(attrs)) + String(" />")) : (scopePush("element-attrs", NIL), (function() {
var content = join("", map(function(c) { return renderToHtml(c, env); }, children));
{ var _c = sxEmitted("element-attrs"); for (var _i = 0; _i < _c.length; _i++) { var spreadDict = _c[_i]; mergeSpreadAttrs(attrs, spreadDict); } }
{ var _c = scopeEmitted("element-attrs"); for (var _i = 0; _i < _c.length; _i++) { var spreadDict = _c[_i]; mergeSpreadAttrs(attrs, spreadDict); } }
scopePop("element-attrs");
return (String("<") + String(tag) + String(renderAttrs(attrs)) + String(">") + String(content) + String("</") + String(tag) + String(">"));
})()));
@@ -2650,7 +2656,7 @@ PRIMITIVES["render-html-element"] = renderHtmlElement;
scopePush("element-attrs", NIL);
return (function() {
var content = join("", map(function(c) { return renderToHtml(c, env); }, children));
{ var _c = sxEmitted("element-attrs"); for (var _i = 0; _i < _c.length; _i++) { var spreadDict = _c[_i]; mergeSpreadAttrs(lakeAttrs, spreadDict); } }
{ var _c = scopeEmitted("element-attrs"); for (var _i = 0; _i < _c.length; _i++) { var spreadDict = _c[_i]; mergeSpreadAttrs(lakeAttrs, spreadDict); } }
scopePop("element-attrs");
return (String("<") + String(lakeTag) + String(renderAttrs(lakeAttrs)) + String(">") + String(content) + String("</") + String(lakeTag) + String(">"));
})();
@@ -2677,7 +2683,7 @@ PRIMITIVES["render-html-lake"] = renderHtmlLake;
scopePush("element-attrs", NIL);
return (function() {
var content = join("", map(function(c) { return renderToHtml(c, env); }, children));
{ var _c = sxEmitted("element-attrs"); for (var _i = 0; _i < _c.length; _i++) { var spreadDict = _c[_i]; mergeSpreadAttrs(marshAttrs, spreadDict); } }
{ var _c = scopeEmitted("element-attrs"); for (var _i = 0; _i < _c.length; _i++) { var spreadDict = _c[_i]; mergeSpreadAttrs(marshAttrs, spreadDict); } }
scopePop("element-attrs");
return (String("<") + String(marshTag) + String(renderAttrs(marshAttrs)) + String(">") + String(content) + String("</") + String(marshTag) + String(">"));
})();