Refactor spread to use provide/emit! internally
Spreads now emit their attrs into the nearest element's provide scope instead of requiring per-child spread? checks at every intermediate layer. emit! is tolerant (no-op when no provider), so spreads in non-element contexts silently vanish. - adapter-html: element/lake/marsh wrap children in provide, collect emitted; removed 14 spread filters from fragment, forms, components - adapter-sx: aser wraps result to catch spread values from fn calls; aser-call uses provide with attr-parts/child-parts ordering - adapter-async: same pattern for both render and aser paths - adapter-dom: added emit! in spread dispatch + provide in element rendering; kept spread? checks for reactive/island and DOM safety - platform: emit! returns NIL when no provider instead of erroring - 3 new aser tests: stored spread, nested element, silent drop Co-Authored-By: Claude Opus 4.6 <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-13T12:16:43Z";
|
var SX_VERSION = "2026-03-13T15:35:20Z";
|
||||||
|
|
||||||
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); }
|
||||||
@@ -180,8 +180,6 @@
|
|||||||
function sxEmit(name, value) {
|
function sxEmit(name, value) {
|
||||||
if (_provideStacks[name] && _provideStacks[name].length) {
|
if (_provideStacks[name] && _provideStacks[name].length) {
|
||||||
_provideStacks[name][_provideStacks[name].length - 1].emitted.push(value);
|
_provideStacks[name][_provideStacks[name].length - 1].emitted.push(value);
|
||||||
} else {
|
|
||||||
throw new Error("No provider for emit!: " + name);
|
|
||||||
}
|
}
|
||||||
return NIL;
|
return NIL;
|
||||||
}
|
}
|
||||||
@@ -1523,10 +1521,10 @@ continue; } else { return NIL; } } };
|
|||||||
|
|
||||||
// render-to-html
|
// render-to-html
|
||||||
var renderToHtml = function(expr, env) { setRenderActiveB(true);
|
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 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 (sxEmit("element-attrs", spreadAttrs(expr)), ""); return renderValueToHtml(trampoline(evalExpr(expr, env)), env); })(); };
|
||||||
|
|
||||||
// render-value-to-html
|
// 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 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 (sxEmit("element-attrs", spreadAttrs(val)), ""); return escapeHtml((String(val))); })(); };
|
||||||
|
|
||||||
// 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", "provide"];
|
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", "provide"];
|
||||||
@@ -1537,10 +1535,10 @@ return (function() { var _m = typeOf(expr); if (_m == "nil") return ""; if (_m =
|
|||||||
// render-list-to-html
|
// render-list-to-html
|
||||||
var renderListToHtml = function(expr, env) { return (isSxTruthy(isEmpty(expr)) ? "" : (function() {
|
var renderListToHtml = function(expr, env) { return (isSxTruthy(isEmpty(expr)) ? "" : (function() {
|
||||||
var head = first(expr);
|
var head = first(expr);
|
||||||
return (isSxTruthy(!isSxTruthy((typeOf(head) == "symbol"))) ? join("", filter(function(x) { return !isSxTruthy(isSpread(x)); }, map(function(x) { return renderValueToHtml(x, env); }, expr))) : (function() {
|
return (isSxTruthy(!isSxTruthy((typeOf(head) == "symbol"))) ? join("", map(function(x) { return renderValueToHtml(x, env); }, expr)) : (function() {
|
||||||
var name = symbolName(head);
|
var name = symbolName(head);
|
||||||
var args = rest(expr);
|
var args = rest(expr);
|
||||||
return (isSxTruthy((name == "<>")) ? join("", filter(function(x) { return !isSxTruthy(isSpread(x)); }, map(function(x) { return renderToHtml(x, env); }, args))) : (isSxTruthy((name == "raw!")) ? join("", map(function(x) { return (String(trampoline(evalExpr(x, env)))); }, args)) : (isSxTruthy((name == "lake")) ? renderHtmlLake(args, env) : (isSxTruthy((name == "marsh")) ? renderHtmlMarsh(args, env) : (isSxTruthy(contains(HTML_TAGS, name)) ? renderHtmlElement(name, args, env) : (isSxTruthy((isSxTruthy(startsWith(name, "~")) && isSxTruthy(envHas(env, name)) && isIsland(envGet(env, name)))) ? renderHtmlIsland(envGet(env, name), args, env) : (isSxTruthy(startsWith(name, "~")) ? (function() {
|
return (isSxTruthy((name == "<>")) ? join("", map(function(x) { return renderToHtml(x, env); }, args)) : (isSxTruthy((name == "raw!")) ? join("", map(function(x) { return (String(trampoline(evalExpr(x, env)))); }, args)) : (isSxTruthy((name == "lake")) ? renderHtmlLake(args, env) : (isSxTruthy((name == "marsh")) ? renderHtmlMarsh(args, env) : (isSxTruthy(contains(HTML_TAGS, name)) ? renderHtmlElement(name, args, env) : (isSxTruthy((isSxTruthy(startsWith(name, "~")) && isSxTruthy(envHas(env, name)) && isIsland(envGet(env, name)))) ? renderHtmlIsland(envGet(env, name), args, env) : (isSxTruthy(startsWith(name, "~")) ? (function() {
|
||||||
var val = envGet(env, name);
|
var val = envGet(env, name);
|
||||||
return (isSxTruthy(isComponent(val)) ? renderHtmlComponent(val, args, env) : (isSxTruthy(isMacro(val)) ? renderToHtml(expandMacro(val, args, env), env) : error((String("Unknown component: ") + String(name)))));
|
return (isSxTruthy(isComponent(val)) ? renderHtmlComponent(val, args, env) : (isSxTruthy(isMacro(val)) ? renderToHtml(expandMacro(val, args, env), env) : error((String("Unknown component: ") + String(name)))));
|
||||||
})() : (isSxTruthy(isRenderHtmlForm(name)) ? dispatchHtmlForm(name, expr, env) : (isSxTruthy((isSxTruthy(envHas(env, name)) && isMacro(envGet(env, name)))) ? renderToHtml(expandMacro(envGet(env, name), args, env), env) : renderValueToHtml(trampoline(evalExpr(expr, env)), env))))))))));
|
})() : (isSxTruthy(isRenderHtmlForm(name)) ? dispatchHtmlForm(name, expr, env) : (isSxTruthy((isSxTruthy(envHas(env, name)) && isMacro(envGet(env, name)))) ? renderToHtml(expandMacro(envGet(env, name), args, env), env) : renderValueToHtml(trampoline(evalExpr(expr, env)), env))))))))));
|
||||||
@@ -1551,33 +1549,24 @@ return (function() { var _m = typeOf(expr); if (_m == "nil") return ""; if (_m =
|
|||||||
var dispatchHtmlForm = function(name, expr, env) { return (isSxTruthy((name == "if")) ? (function() {
|
var dispatchHtmlForm = function(name, expr, env) { return (isSxTruthy((name == "if")) ? (function() {
|
||||||
var condVal = trampoline(evalExpr(nth(expr, 1), env));
|
var condVal = trampoline(evalExpr(nth(expr, 1), env));
|
||||||
return (isSxTruthy(condVal) ? renderToHtml(nth(expr, 2), env) : (isSxTruthy((len(expr) > 3)) ? renderToHtml(nth(expr, 3), env) : ""));
|
return (isSxTruthy(condVal) ? renderToHtml(nth(expr, 2), env) : (isSxTruthy((len(expr) > 3)) ? renderToHtml(nth(expr, 3), env) : ""));
|
||||||
})() : (isSxTruthy((name == "when")) ? (isSxTruthy(!isSxTruthy(trampoline(evalExpr(nth(expr, 1), env)))) ? "" : (isSxTruthy((len(expr) == 3)) ? renderToHtml(nth(expr, 2), env) : (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 results = map(function(i) { return renderToHtml(nth(expr, i), env); }, range(2, len(expr)));
|
|
||||||
return join("", filter(function(r) { return !isSxTruthy(isSpread(r)); }, results));
|
|
||||||
})())) : (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(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) : (function() {
|
return (isSxTruthy((len(expr) == 3)) ? renderToHtml(nth(expr, 2), local) : join("", map(function(i) { return renderToHtml(nth(expr, i), local); }, range(2, len(expr)))));
|
||||||
var results = 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() {
|
||||||
return join("", filter(function(r) { return !isSxTruthy(isSpread(r)); }, results));
|
|
||||||
})());
|
|
||||||
})() : (isSxTruthy(sxOr((name == "begin"), (name == "do"))) ? (isSxTruthy((len(expr) == 2)) ? renderToHtml(nth(expr, 1), env) : (function() {
|
|
||||||
var results = map(function(i) { return renderToHtml(nth(expr, i), env); }, range(1, len(expr)));
|
|
||||||
return join("", filter(function(r) { return !isSxTruthy(isSpread(r)); }, results));
|
|
||||||
})()) : (isSxTruthy(isDefinitionForm(name)) ? (trampoline(evalExpr(expr, env)), "") : (isSxTruthy((name == "map")) ? (function() {
|
|
||||||
var f = trampoline(evalExpr(nth(expr, 1), env));
|
var f = trampoline(evalExpr(nth(expr, 1), env));
|
||||||
var coll = trampoline(evalExpr(nth(expr, 2), env));
|
var coll = trampoline(evalExpr(nth(expr, 2), env));
|
||||||
return join("", filter(function(r) { return !isSxTruthy(isSpread(r)); }, map(function(item) { return (isSxTruthy(isLambda(f)) ? renderLambdaHtml(f, [item], env) : renderToHtml(apply(f, [item]), env)); }, coll)));
|
return join("", map(function(item) { return (isSxTruthy(isLambda(f)) ? renderLambdaHtml(f, [item], env) : renderToHtml(apply(f, [item]), env)); }, coll));
|
||||||
})() : (isSxTruthy((name == "map-indexed")) ? (function() {
|
})() : (isSxTruthy((name == "map-indexed")) ? (function() {
|
||||||
var f = trampoline(evalExpr(nth(expr, 1), env));
|
var f = trampoline(evalExpr(nth(expr, 1), env));
|
||||||
var coll = trampoline(evalExpr(nth(expr, 2), env));
|
var coll = trampoline(evalExpr(nth(expr, 2), env));
|
||||||
return join("", filter(function(r) { return !isSxTruthy(isSpread(r)); }, mapIndexed(function(i, item) { return (isSxTruthy(isLambda(f)) ? renderLambdaHtml(f, [i, item], env) : renderToHtml(apply(f, [i, item]), env)); }, coll)));
|
return join("", mapIndexed(function(i, item) { return (isSxTruthy(isLambda(f)) ? renderLambdaHtml(f, [i, item], env) : renderToHtml(apply(f, [i, item]), env)); }, coll));
|
||||||
})() : (isSxTruthy((name == "filter")) ? renderToHtml(trampoline(evalExpr(expr, env)), env) : (isSxTruthy((name == "for-each")) ? (function() {
|
})() : (isSxTruthy((name == "filter")) ? renderToHtml(trampoline(evalExpr(expr, env)), env) : (isSxTruthy((name == "for-each")) ? (function() {
|
||||||
var f = trampoline(evalExpr(nth(expr, 1), env));
|
var f = trampoline(evalExpr(nth(expr, 1), env));
|
||||||
var coll = trampoline(evalExpr(nth(expr, 2), env));
|
var coll = trampoline(evalExpr(nth(expr, 2), env));
|
||||||
return join("", filter(function(r) { return !isSxTruthy(isSpread(r)); }, map(function(item) { return (isSxTruthy(isLambda(f)) ? renderLambdaHtml(f, [item], env) : renderToHtml(apply(f, [item]), env)); }, coll)));
|
return join("", map(function(item) { return (isSxTruthy(isLambda(f)) ? renderLambdaHtml(f, [item], env) : renderToHtml(apply(f, [item]), env)); }, coll));
|
||||||
})() : (isSxTruthy((name == "provide")) ? (function() {
|
})() : (isSxTruthy((name == "provide")) ? (function() {
|
||||||
var provName = trampoline(evalExpr(nth(expr, 1), env));
|
var provName = trampoline(evalExpr(nth(expr, 1), env));
|
||||||
var provVal = trampoline(evalExpr(nth(expr, 2), env));
|
var provVal = trampoline(evalExpr(nth(expr, 2), env));
|
||||||
@@ -1585,7 +1574,7 @@ return (function() { var _m = typeOf(expr); if (_m == "nil") return ""; if (_m =
|
|||||||
var bodyCount = (len(expr) - 3);
|
var bodyCount = (len(expr) - 3);
|
||||||
providePush(provName, provVal);
|
providePush(provName, provVal);
|
||||||
return (function() {
|
return (function() {
|
||||||
var result = (isSxTruthy((bodyCount == 1)) ? renderToHtml(nth(expr, bodyStart), env) : join("", filter(function(r) { return !isSxTruthy(isSpread(r)); }, map(function(i) { return renderToHtml(nth(expr, i), env); }, range(bodyStart, (bodyStart + bodyCount))))));
|
var result = (isSxTruthy((bodyCount == 1)) ? renderToHtml(nth(expr, bodyStart), env) : join("", map(function(i) { return renderToHtml(nth(expr, i), env); }, range(bodyStart, (bodyStart + bodyCount)))));
|
||||||
providePop(provName);
|
providePop(provName);
|
||||||
return result;
|
return result;
|
||||||
})();
|
})();
|
||||||
@@ -1614,14 +1603,7 @@ return (function() { var _m = typeOf(expr); if (_m == "nil") return ""; if (_m =
|
|||||||
var local = envMerge(componentClosure(comp), env);
|
var local = envMerge(componentClosure(comp), env);
|
||||||
{ var _c = componentParams(comp); for (var _i = 0; _i < _c.length; _i++) { var p = _c[_i]; envSet(local, p, (isSxTruthy(dictHas(kwargs, p)) ? dictGet(kwargs, p) : NIL)); } }
|
{ var _c = componentParams(comp); for (var _i = 0; _i < _c.length; _i++) { var p = _c[_i]; envSet(local, p, (isSxTruthy(dictHas(kwargs, p)) ? dictGet(kwargs, p) : NIL)); } }
|
||||||
if (isSxTruthy(componentHasChildren(comp))) {
|
if (isSxTruthy(componentHasChildren(comp))) {
|
||||||
(function() {
|
envSet(local, "children", makeRawHtml(join("", map(function(c) { return renderToHtml(c, env); }, children))));
|
||||||
var parts = [];
|
|
||||||
{ var _c = children; for (var _i = 0; _i < _c.length; _i++) { var c = _c[_i]; (function() {
|
|
||||||
var r = renderToHtml(c, env);
|
|
||||||
return (isSxTruthy(!isSxTruthy(isSpread(r))) ? append_b(parts, r) : NIL);
|
|
||||||
})(); } }
|
|
||||||
return envSet(local, "children", makeRawHtml(join("", parts)));
|
|
||||||
})();
|
|
||||||
}
|
}
|
||||||
return renderToHtml(componentBody(comp), local);
|
return renderToHtml(componentBody(comp), local);
|
||||||
})();
|
})();
|
||||||
@@ -1633,14 +1615,12 @@ return (function() { var _m = typeOf(expr); if (_m == "nil") return ""; if (_m =
|
|||||||
var attrs = first(parsed);
|
var attrs = first(parsed);
|
||||||
var children = nth(parsed, 1);
|
var children = nth(parsed, 1);
|
||||||
var isVoid = contains(VOID_ELEMENTS, tag);
|
var isVoid = contains(VOID_ELEMENTS, tag);
|
||||||
return (isSxTruthy(isVoid) ? (String("<") + String(tag) + String(renderAttrs(attrs)) + String(" />")) : (function() {
|
return (isSxTruthy(isVoid) ? (String("<") + String(tag) + String(renderAttrs(attrs)) + String(" />")) : (providePush("element-attrs", NIL), (function() {
|
||||||
var contentParts = [];
|
var content = join("", map(function(c) { return renderToHtml(c, env); }, children));
|
||||||
{ var _c = children; for (var _i = 0; _i < _c.length; _i++) { var c = _c[_i]; (function() {
|
{ var _c = sxEmitted("element-attrs"); for (var _i = 0; _i < _c.length; _i++) { var spreadDict = _c[_i]; mergeSpreadAttrs(attrs, spreadDict); } }
|
||||||
var result = renderToHtml(c, env);
|
providePop("element-attrs");
|
||||||
return (isSxTruthy(isSpread(result)) ? mergeSpreadAttrs(attrs, spreadAttrs(result)) : append_b(contentParts, result));
|
return (String("<") + String(tag) + String(renderAttrs(attrs)) + String(">") + String(content) + String("</") + String(tag) + String(">"));
|
||||||
})(); } }
|
})()));
|
||||||
return (String("<") + String(tag) + String(renderAttrs(attrs)) + String(">") + String(join("", contentParts)) + String("</") + String(tag) + String(">"));
|
|
||||||
})());
|
|
||||||
})(); };
|
})(); };
|
||||||
|
|
||||||
// render-html-lake
|
// render-html-lake
|
||||||
@@ -1659,12 +1639,13 @@ return (function() { var _m = typeOf(expr); if (_m == "nil") return ""; if (_m =
|
|||||||
})(); }, {["i"]: 0, ["skip"]: false}, args);
|
})(); }, {["i"]: 0, ["skip"]: false}, args);
|
||||||
return (function() {
|
return (function() {
|
||||||
var lakeAttrs = {["data-sx-lake"]: sxOr(lakeId, "")};
|
var lakeAttrs = {["data-sx-lake"]: sxOr(lakeId, "")};
|
||||||
var contentParts = [];
|
providePush("element-attrs", NIL);
|
||||||
{ var _c = children; for (var _i = 0; _i < _c.length; _i++) { var c = _c[_i]; (function() {
|
return (function() {
|
||||||
var result = renderToHtml(c, env);
|
var content = join("", map(function(c) { return renderToHtml(c, env); }, children));
|
||||||
return (isSxTruthy(isSpread(result)) ? mergeSpreadAttrs(lakeAttrs, spreadAttrs(result)) : append_b(contentParts, result));
|
{ var _c = sxEmitted("element-attrs"); for (var _i = 0; _i < _c.length; _i++) { var spreadDict = _c[_i]; mergeSpreadAttrs(lakeAttrs, spreadDict); } }
|
||||||
})(); } }
|
providePop("element-attrs");
|
||||||
return (String("<") + String(lakeTag) + String(renderAttrs(lakeAttrs)) + String(">") + String(join("", contentParts)) + String("</") + String(lakeTag) + String(">"));
|
return (String("<") + String(lakeTag) + String(renderAttrs(lakeAttrs)) + String(">") + String(content) + String("</") + String(lakeTag) + String(">"));
|
||||||
|
})();
|
||||||
})();
|
})();
|
||||||
})(); };
|
})(); };
|
||||||
|
|
||||||
@@ -1684,12 +1665,13 @@ return (function() { var _m = typeOf(expr); if (_m == "nil") return ""; if (_m =
|
|||||||
})(); }, {["i"]: 0, ["skip"]: false}, args);
|
})(); }, {["i"]: 0, ["skip"]: false}, args);
|
||||||
return (function() {
|
return (function() {
|
||||||
var marshAttrs = {["data-sx-marsh"]: sxOr(marshId, "")};
|
var marshAttrs = {["data-sx-marsh"]: sxOr(marshId, "")};
|
||||||
var contentParts = [];
|
providePush("element-attrs", NIL);
|
||||||
{ var _c = children; for (var _i = 0; _i < _c.length; _i++) { var c = _c[_i]; (function() {
|
return (function() {
|
||||||
var result = renderToHtml(c, env);
|
var content = join("", map(function(c) { return renderToHtml(c, env); }, children));
|
||||||
return (isSxTruthy(isSpread(result)) ? mergeSpreadAttrs(marshAttrs, spreadAttrs(result)) : append_b(contentParts, result));
|
{ var _c = sxEmitted("element-attrs"); for (var _i = 0; _i < _c.length; _i++) { var spreadDict = _c[_i]; mergeSpreadAttrs(marshAttrs, spreadDict); } }
|
||||||
})(); } }
|
providePop("element-attrs");
|
||||||
return (String("<") + String(marshTag) + String(renderAttrs(marshAttrs)) + String(">") + String(join("", contentParts)) + String("</") + String(marshTag) + String(">"));
|
return (String("<") + String(marshTag) + String(renderAttrs(marshAttrs)) + String(">") + String(content) + String("</") + String(marshTag) + String(">"));
|
||||||
|
})();
|
||||||
})();
|
})();
|
||||||
})(); };
|
})(); };
|
||||||
|
|
||||||
@@ -1710,14 +1692,7 @@ return (function() { var _m = typeOf(expr); if (_m == "nil") return ""; if (_m =
|
|||||||
var islandName = componentName(island);
|
var islandName = componentName(island);
|
||||||
{ var _c = componentParams(island); for (var _i = 0; _i < _c.length; _i++) { var p = _c[_i]; envSet(local, p, (isSxTruthy(dictHas(kwargs, p)) ? dictGet(kwargs, p) : NIL)); } }
|
{ var _c = componentParams(island); for (var _i = 0; _i < _c.length; _i++) { var p = _c[_i]; envSet(local, p, (isSxTruthy(dictHas(kwargs, p)) ? dictGet(kwargs, p) : NIL)); } }
|
||||||
if (isSxTruthy(componentHasChildren(island))) {
|
if (isSxTruthy(componentHasChildren(island))) {
|
||||||
(function() {
|
envSet(local, "children", makeRawHtml(join("", map(function(c) { return renderToHtml(c, env); }, children))));
|
||||||
var parts = [];
|
|
||||||
{ var _c = children; for (var _i = 0; _i < _c.length; _i++) { var c = _c[_i]; (function() {
|
|
||||||
var r = renderToHtml(c, env);
|
|
||||||
return (isSxTruthy(!isSxTruthy(isSpread(r))) ? append_b(parts, r) : NIL);
|
|
||||||
})(); } }
|
|
||||||
return envSet(local, "children", makeRawHtml(join("", parts)));
|
|
||||||
})();
|
|
||||||
}
|
}
|
||||||
return (function() {
|
return (function() {
|
||||||
var bodyHtml = renderToHtml(componentBody(island), local);
|
var bodyHtml = renderToHtml(componentBody(island), local);
|
||||||
@@ -1741,10 +1716,13 @@ return (function() { var _m = typeOf(expr); if (_m == "nil") return ""; if (_m =
|
|||||||
|
|
||||||
// aser
|
// aser
|
||||||
var aser = function(expr, env) { setRenderActiveB(true);
|
var aser = function(expr, env) { setRenderActiveB(true);
|
||||||
return (function() { var _m = typeOf(expr); if (_m == "number") return expr; if (_m == "string") return expr; if (_m == "boolean") return expr; if (_m == "nil") return NIL; if (_m == "symbol") return (function() {
|
return (function() {
|
||||||
|
var result = (function() { var _m = typeOf(expr); if (_m == "number") return expr; if (_m == "string") return expr; if (_m == "boolean") return expr; if (_m == "nil") return NIL; if (_m == "symbol") return (function() {
|
||||||
var name = symbolName(expr);
|
var name = symbolName(expr);
|
||||||
return (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 (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))))))));
|
||||||
})(); if (_m == "keyword") return keywordName(expr); if (_m == "list") return (isSxTruthy(isEmpty(expr)) ? [] : aserList(expr, env)); if (_m == "spread") return expr; return expr; })(); };
|
})(); if (_m == "keyword") return keywordName(expr); if (_m == "list") return (isSxTruthy(isEmpty(expr)) ? [] : aserList(expr, env)); if (_m == "spread") return (sxEmit("element-attrs", spreadAttrs(expr)), NIL); return expr; })();
|
||||||
|
return (isSxTruthy(isSpread(result)) ? (sxEmit("element-attrs", spreadAttrs(result)), NIL) : result);
|
||||||
|
})(); };
|
||||||
|
|
||||||
// aser-list
|
// aser-list
|
||||||
var aserList = function(expr, env) { return (function() {
|
var aserList = function(expr, env) { return (function() {
|
||||||
@@ -1772,29 +1750,36 @@ return (function() { var _m = typeOf(expr); if (_m == "number") return expr; if
|
|||||||
|
|
||||||
// aser-call
|
// aser-call
|
||||||
var aserCall = function(name, args, env) { return (function() {
|
var aserCall = function(name, args, env) { return (function() {
|
||||||
var parts = [name];
|
var attrParts = [];
|
||||||
|
var childParts = [];
|
||||||
var skip = false;
|
var skip = false;
|
||||||
var i = 0;
|
var i = 0;
|
||||||
|
providePush("element-attrs", NIL);
|
||||||
{ var _c = args; for (var _i = 0; _i < _c.length; _i++) { var arg = _c[_i]; (isSxTruthy(skip) ? ((skip = false), (i = (i + 1))) : (isSxTruthy((isSxTruthy((typeOf(arg) == "keyword")) && ((i + 1) < len(args)))) ? (function() {
|
{ var _c = args; for (var _i = 0; _i < _c.length; _i++) { var arg = _c[_i]; (isSxTruthy(skip) ? ((skip = false), (i = (i + 1))) : (isSxTruthy((isSxTruthy((typeOf(arg) == "keyword")) && ((i + 1) < len(args)))) ? (function() {
|
||||||
var val = aser(nth(args, (i + 1)), env);
|
var val = aser(nth(args, (i + 1)), env);
|
||||||
if (isSxTruthy(!isSxTruthy(isNil(val)))) {
|
if (isSxTruthy(!isSxTruthy(isNil(val)))) {
|
||||||
parts.push((String(":") + String(keywordName(arg))));
|
attrParts.push((String(":") + String(keywordName(arg))));
|
||||||
parts.push(serialize(val));
|
attrParts.push(serialize(val));
|
||||||
}
|
}
|
||||||
skip = true;
|
skip = true;
|
||||||
return (i = (i + 1));
|
return (i = (i + 1));
|
||||||
})() : (function() {
|
})() : (function() {
|
||||||
var val = aser(arg, env);
|
var val = aser(arg, env);
|
||||||
if (isSxTruthy(!isSxTruthy(isNil(val)))) {
|
if (isSxTruthy(!isSxTruthy(isNil(val)))) {
|
||||||
(isSxTruthy(isSpread(val)) ? forEach(function(k) { return (function() {
|
(isSxTruthy((typeOf(val) == "list")) ? forEach(function(item) { return (isSxTruthy(!isSxTruthy(isNil(item))) ? append_b(childParts, serialize(item)) : NIL); }, val) : append_b(childParts, serialize(val)));
|
||||||
var v = dictGet(spreadAttrs(val), k);
|
|
||||||
parts.push((String(":") + String(k)));
|
|
||||||
return append_b(parts, serialize(v));
|
|
||||||
})(); }, keys(spreadAttrs(val))) : (isSxTruthy((typeOf(val) == "list")) ? forEach(function(item) { return (isSxTruthy(!isSxTruthy(isNil(item))) ? append_b(parts, serialize(item)) : NIL); }, val) : append_b(parts, serialize(val))));
|
|
||||||
}
|
}
|
||||||
return (i = (i + 1));
|
return (i = (i + 1));
|
||||||
})())); } }
|
})())); } }
|
||||||
|
{ var _c = sxEmitted("element-attrs"); for (var _i = 0; _i < _c.length; _i++) { var spreadDict = _c[_i]; { var _c = keys(spreadDict); for (var _i = 0; _i < _c.length; _i++) { var k = _c[_i]; (function() {
|
||||||
|
var v = dictGet(spreadDict, k);
|
||||||
|
attrParts.push((String(":") + String(k)));
|
||||||
|
return append_b(attrParts, serialize(v));
|
||||||
|
})(); } } } }
|
||||||
|
providePop("element-attrs");
|
||||||
|
return (function() {
|
||||||
|
var parts = concat([name], attrParts, childParts);
|
||||||
return (String("(") + String(join(" ", parts)) + String(")"));
|
return (String("(") + String(join(" ", parts)) + String(")"));
|
||||||
|
})();
|
||||||
})(); };
|
})(); };
|
||||||
|
|
||||||
// SPECIAL_FORM_NAMES
|
// SPECIAL_FORM_NAMES
|
||||||
@@ -1898,7 +1883,7 @@ return result; }, args);
|
|||||||
|
|
||||||
// render-to-dom
|
// render-to-dom
|
||||||
var renderToDom = function(expr, env, ns) { setRenderActiveB(true);
|
var renderToDom = function(expr, env, ns) { setRenderActiveB(true);
|
||||||
return (function() { var _m = typeOf(expr); if (_m == "nil") return createFragment(); if (_m == "boolean") return createFragment(); if (_m == "raw-html") return domParseHtml(rawHtmlContent(expr)); if (_m == "string") return createTextNode(expr); if (_m == "number") return createTextNode((String(expr))); if (_m == "symbol") return renderToDom(trampoline(evalExpr(expr, env)), env, ns); if (_m == "keyword") return createTextNode(keywordName(expr)); if (_m == "dom-node") return expr; if (_m == "spread") return expr; if (_m == "dict") return createFragment(); if (_m == "list") return (isSxTruthy(isEmpty(expr)) ? createFragment() : renderDomList(expr, env, ns)); return (isSxTruthy(isSignal(expr)) ? (isSxTruthy(_islandScope) ? reactiveText(expr) : createTextNode((String(deref(expr))))) : createTextNode((String(expr)))); })(); };
|
return (function() { var _m = typeOf(expr); if (_m == "nil") return createFragment(); if (_m == "boolean") return createFragment(); if (_m == "raw-html") return domParseHtml(rawHtmlContent(expr)); if (_m == "string") return createTextNode(expr); if (_m == "number") return createTextNode((String(expr))); if (_m == "symbol") return renderToDom(trampoline(evalExpr(expr, env)), env, ns); if (_m == "keyword") return createTextNode(keywordName(expr)); if (_m == "dom-node") return expr; if (_m == "spread") return (sxEmit("element-attrs", spreadAttrs(expr)), expr); if (_m == "dict") return createFragment(); if (_m == "list") return (isSxTruthy(isEmpty(expr)) ? createFragment() : renderDomList(expr, env, ns)); return (isSxTruthy(isSignal(expr)) ? (isSxTruthy(_islandScope) ? reactiveText(expr) : createTextNode((String(deref(expr))))) : createTextNode((String(expr)))); })(); };
|
||||||
|
|
||||||
// render-dom-list
|
// render-dom-list
|
||||||
var renderDomList = function(expr, env, ns) { return (function() {
|
var renderDomList = function(expr, env, ns) { return (function() {
|
||||||
@@ -1927,6 +1912,7 @@ return (function() { var _m = typeOf(expr); if (_m == "nil") return createFragme
|
|||||||
var renderDomElement = function(tag, args, env, ns) { return (function() {
|
var renderDomElement = function(tag, args, env, ns) { return (function() {
|
||||||
var newNs = (isSxTruthy((tag == "svg")) ? SVG_NS : (isSxTruthy((tag == "math")) ? MATH_NS : ns));
|
var newNs = (isSxTruthy((tag == "svg")) ? SVG_NS : (isSxTruthy((tag == "math")) ? MATH_NS : ns));
|
||||||
var el = domCreateElement(tag, newNs);
|
var el = domCreateElement(tag, newNs);
|
||||||
|
providePush("element-attrs", NIL);
|
||||||
reduce(function(state, arg) { return (function() {
|
reduce(function(state, arg) { return (function() {
|
||||||
var skip = get(state, "skip");
|
var skip = get(state, "skip");
|
||||||
return (isSxTruthy(skip) ? assoc(state, "skip", false, "i", (get(state, "i") + 1)) : (isSxTruthy((isSxTruthy((typeOf(arg) == "keyword")) && ((get(state, "i") + 1) < len(args)))) ? (function() {
|
return (isSxTruthy(skip) ? assoc(state, "skip", false, "i", (get(state, "i") + 1)) : (isSxTruthy((isSxTruthy((typeOf(arg) == "keyword")) && ((get(state, "i") + 1) < len(args)))) ? (function() {
|
||||||
@@ -1951,8 +1937,11 @@ return (function() { var _m = typeOf(expr); if (_m == "nil") return createFragme
|
|||||||
return assoc(state, "skip", true, "i", (get(state, "i") + 1));
|
return assoc(state, "skip", true, "i", (get(state, "i") + 1));
|
||||||
})() : ((isSxTruthy(!isSxTruthy(contains(VOID_ELEMENTS, tag))) ? (function() {
|
})() : ((isSxTruthy(!isSxTruthy(contains(VOID_ELEMENTS, tag))) ? (function() {
|
||||||
var child = renderToDom(arg, env, newNs);
|
var child = renderToDom(arg, env, newNs);
|
||||||
return (isSxTruthy((isSxTruthy(isSpread(child)) && _islandScope)) ? reactiveSpread(el, function() { return renderToDom(arg, env, newNs); }) : (isSxTruthy(isSpread(child)) ? forEach(function(key) { return (function() {
|
return (isSxTruthy((isSxTruthy(isSpread(child)) && _islandScope)) ? reactiveSpread(el, function() { return renderToDom(arg, env, newNs); }) : (isSxTruthy(isSpread(child)) ? NIL : domAppend(el, child)));
|
||||||
var val = dictGet(spreadAttrs(child), key);
|
})() : NIL), assoc(state, "i", (get(state, "i") + 1)))));
|
||||||
|
})(); }, {["i"]: 0, ["skip"]: false}, args);
|
||||||
|
{ var _c = sxEmitted("element-attrs"); for (var _i = 0; _i < _c.length; _i++) { var spreadDict = _c[_i]; { var _c = keys(spreadDict); for (var _i = 0; _i < _c.length; _i++) { var key = _c[_i]; (function() {
|
||||||
|
var val = dictGet(spreadDict, key);
|
||||||
return (isSxTruthy((key == "class")) ? (function() {
|
return (isSxTruthy((key == "class")) ? (function() {
|
||||||
var existing = domGetAttr(el, "class");
|
var existing = domGetAttr(el, "class");
|
||||||
return domSetAttr(el, "class", (isSxTruthy((isSxTruthy(existing) && !isSxTruthy((existing == "")))) ? (String(existing) + String(" ") + String(val)) : val));
|
return domSetAttr(el, "class", (isSxTruthy((isSxTruthy(existing) && !isSxTruthy((existing == "")))) ? (String(existing) + String(" ") + String(val)) : val));
|
||||||
@@ -1960,9 +1949,8 @@ return (function() { var _m = typeOf(expr); if (_m == "nil") return createFragme
|
|||||||
var existing = domGetAttr(el, "style");
|
var existing = domGetAttr(el, "style");
|
||||||
return domSetAttr(el, "style", (isSxTruthy((isSxTruthy(existing) && !isSxTruthy((existing == "")))) ? (String(existing) + String(";") + String(val)) : val));
|
return domSetAttr(el, "style", (isSxTruthy((isSxTruthy(existing) && !isSxTruthy((existing == "")))) ? (String(existing) + String(";") + String(val)) : val));
|
||||||
})() : domSetAttr(el, key, (String(val)))));
|
})() : domSetAttr(el, key, (String(val)))));
|
||||||
})(); }, keys(spreadAttrs(child))) : domAppend(el, child)));
|
})(); } } } }
|
||||||
})() : NIL), assoc(state, "i", (get(state, "i") + 1)))));
|
providePop("element-attrs");
|
||||||
})(); }, {["i"]: 0, ["skip"]: false}, args);
|
|
||||||
return el;
|
return el;
|
||||||
})(); };
|
})(); };
|
||||||
|
|
||||||
|
|||||||
@@ -48,7 +48,7 @@
|
|||||||
"string" (escape-html expr)
|
"string" (escape-html expr)
|
||||||
"number" (escape-html (str expr))
|
"number" (escape-html (str expr))
|
||||||
"raw-html" (raw-html-content expr)
|
"raw-html" (raw-html-content expr)
|
||||||
"spread" expr
|
"spread" (do (emit! "element-attrs" (spread-attrs expr)) "")
|
||||||
"symbol" (let ((val (async-eval expr env ctx)))
|
"symbol" (let ((val (async-eval expr env ctx)))
|
||||||
(async-render val env ctx))
|
(async-render val env ctx))
|
||||||
"keyword" (escape-html (keyword-name expr))
|
"keyword" (escape-html (keyword-name expr))
|
||||||
@@ -80,10 +80,9 @@
|
|||||||
(= name "raw!")
|
(= name "raw!")
|
||||||
(async-render-raw args env ctx)
|
(async-render-raw args env ctx)
|
||||||
|
|
||||||
;; Fragment (spreads filtered — no parent element)
|
;; Fragment
|
||||||
(= name "<>")
|
(= name "<>")
|
||||||
(join "" (filter (fn (r) (not (spread? r)))
|
(join "" (async-map-render args env ctx))
|
||||||
(async-map-render args env ctx)))
|
|
||||||
|
|
||||||
;; html: prefix
|
;; html: prefix
|
||||||
(starts-with? name "html:")
|
(starts-with? name "html:")
|
||||||
@@ -171,18 +170,19 @@
|
|||||||
(css-class-collect! (str class-val))))
|
(css-class-collect! (str class-val))))
|
||||||
(if (contains? VOID_ELEMENTS tag)
|
(if (contains? VOID_ELEMENTS tag)
|
||||||
(str "<" tag (render-attrs attrs) ">")
|
(str "<" tag (render-attrs attrs) ">")
|
||||||
;; Render children, collecting spreads and content separately
|
;; Provide scope for spread emit!
|
||||||
(let ((token (if (or (= tag "svg") (= tag "math"))
|
(let ((token (if (or (= tag "svg") (= tag "math"))
|
||||||
(svg-context-set! true)
|
(svg-context-set! true)
|
||||||
nil))
|
nil))
|
||||||
(content-parts (list)))
|
(content-parts (list)))
|
||||||
|
(provide-push! "element-attrs" nil)
|
||||||
(for-each
|
(for-each
|
||||||
(fn (c)
|
(fn (c) (append! content-parts (async-render c env ctx)))
|
||||||
(let ((result (async-render c env ctx)))
|
|
||||||
(if (spread? result)
|
|
||||||
(merge-spread-attrs attrs (spread-attrs result))
|
|
||||||
(append! content-parts result))))
|
|
||||||
children)
|
children)
|
||||||
|
(for-each
|
||||||
|
(fn (spread-dict) (merge-spread-attrs attrs spread-dict))
|
||||||
|
(emitted "element-attrs"))
|
||||||
|
(provide-pop! "element-attrs")
|
||||||
(when token (svg-context-reset! token))
|
(when token (svg-context-reset! token))
|
||||||
(str "<" tag (render-attrs attrs) ">"
|
(str "<" tag (render-attrs attrs) ">"
|
||||||
(join "" content-parts)
|
(join "" content-parts)
|
||||||
@@ -231,14 +231,11 @@
|
|||||||
(for-each
|
(for-each
|
||||||
(fn (p) (env-set! local p (if (dict-has? kwargs p) (dict-get kwargs p) nil)))
|
(fn (p) (env-set! local p (if (dict-has? kwargs p) (dict-get kwargs p) nil)))
|
||||||
(component-params comp))
|
(component-params comp))
|
||||||
;; Pre-render children to raw HTML (filter spreads — no parent element)
|
;; Pre-render children to raw HTML
|
||||||
(when (component-has-children? comp)
|
(when (component-has-children? comp)
|
||||||
(let ((parts (list)))
|
(let ((parts (list)))
|
||||||
(for-each
|
(for-each
|
||||||
(fn (c)
|
(fn (c) (append! parts (async-render c env ctx)))
|
||||||
(let ((r (async-render c env ctx)))
|
|
||||||
(when (not (spread? r))
|
|
||||||
(append! parts r))))
|
|
||||||
children)
|
children)
|
||||||
(env-set! local "children"
|
(env-set! local "children"
|
||||||
(make-raw-html (join "" parts)))))
|
(make-raw-html (join "" parts)))))
|
||||||
@@ -259,14 +256,11 @@
|
|||||||
(for-each
|
(for-each
|
||||||
(fn (p) (env-set! local p (if (dict-has? kwargs p) (dict-get kwargs p) nil)))
|
(fn (p) (env-set! local p (if (dict-has? kwargs p) (dict-get kwargs p) nil)))
|
||||||
(component-params island))
|
(component-params island))
|
||||||
;; Pre-render children (filter spreads — no parent element)
|
;; Pre-render children
|
||||||
(when (component-has-children? island)
|
(when (component-has-children? island)
|
||||||
(let ((parts (list)))
|
(let ((parts (list)))
|
||||||
(for-each
|
(for-each
|
||||||
(fn (c)
|
(fn (c) (append! parts (async-render c env ctx)))
|
||||||
(let ((r (async-render c env ctx)))
|
|
||||||
(when (not (spread? r))
|
|
||||||
(append! parts r))))
|
|
||||||
children)
|
children)
|
||||||
(env-set! local "children"
|
(env-set! local "children"
|
||||||
(make-raw-html (join "" parts)))))
|
(make-raw-html (join "" parts)))))
|
||||||
@@ -367,14 +361,13 @@
|
|||||||
(async-render (nth expr 3) env ctx)
|
(async-render (nth expr 3) env ctx)
|
||||||
"")))
|
"")))
|
||||||
|
|
||||||
;; when — single body: pass through (spread propagates). Multi: join strings.
|
;; when — single body: pass through. Multi: join strings.
|
||||||
(= name "when")
|
(= name "when")
|
||||||
(if (not (async-eval (nth expr 1) env ctx))
|
(if (not (async-eval (nth expr 1) env ctx))
|
||||||
""
|
""
|
||||||
(if (= (len expr) 3)
|
(if (= (len expr) 3)
|
||||||
(async-render (nth expr 2) env ctx)
|
(async-render (nth expr 2) env ctx)
|
||||||
(let ((results (async-map-render (slice expr 2) env ctx)))
|
(join "" (async-map-render (slice expr 2) env ctx))))
|
||||||
(join "" (filter (fn (r) (not (spread? r))) results)))))
|
|
||||||
|
|
||||||
;; cond — uses cond-scheme? (every? check) from eval.sx
|
;; cond — uses cond-scheme? (every? check) from eval.sx
|
||||||
(= name "cond")
|
(= name "cond")
|
||||||
@@ -392,47 +385,39 @@
|
|||||||
(let ((local (async-process-bindings (nth expr 1) env ctx)))
|
(let ((local (async-process-bindings (nth expr 1) env ctx)))
|
||||||
(if (= (len expr) 3)
|
(if (= (len expr) 3)
|
||||||
(async-render (nth expr 2) local ctx)
|
(async-render (nth expr 2) local ctx)
|
||||||
(let ((results (async-map-render (slice expr 2) local ctx)))
|
(join "" (async-map-render (slice expr 2) local ctx))))
|
||||||
(join "" (filter (fn (r) (not (spread? r))) results)))))
|
|
||||||
|
|
||||||
;; begin / do — single body: pass through. Multi: join strings.
|
;; begin / do — single body: pass through. Multi: join strings.
|
||||||
(or (= name "begin") (= name "do"))
|
(or (= name "begin") (= name "do"))
|
||||||
(if (= (len expr) 2)
|
(if (= (len expr) 2)
|
||||||
(async-render (nth expr 1) env ctx)
|
(async-render (nth expr 1) env ctx)
|
||||||
(let ((results (async-map-render (rest expr) env ctx)))
|
(join "" (async-map-render (rest expr) env ctx)))
|
||||||
(join "" (filter (fn (r) (not (spread? r))) results))))
|
|
||||||
|
|
||||||
;; Definition forms
|
;; Definition forms
|
||||||
(definition-form? name)
|
(definition-form? name)
|
||||||
(do (async-eval expr env ctx) "")
|
(do (async-eval expr env ctx) "")
|
||||||
|
|
||||||
;; map — spreads filtered
|
;; map
|
||||||
(= name "map")
|
(= name "map")
|
||||||
(let ((f (async-eval (nth expr 1) env ctx))
|
(let ((f (async-eval (nth expr 1) env ctx))
|
||||||
(coll (async-eval (nth expr 2) env ctx)))
|
(coll (async-eval (nth expr 2) env ctx)))
|
||||||
(join ""
|
(join "" (async-map-fn-render f coll env ctx)))
|
||||||
(filter (fn (r) (not (spread? r)))
|
|
||||||
(async-map-fn-render f coll env ctx))))
|
|
||||||
|
|
||||||
;; map-indexed — spreads filtered
|
;; map-indexed
|
||||||
(= name "map-indexed")
|
(= name "map-indexed")
|
||||||
(let ((f (async-eval (nth expr 1) env ctx))
|
(let ((f (async-eval (nth expr 1) env ctx))
|
||||||
(coll (async-eval (nth expr 2) env ctx)))
|
(coll (async-eval (nth expr 2) env ctx)))
|
||||||
(join ""
|
(join "" (async-map-indexed-fn-render f coll env ctx)))
|
||||||
(filter (fn (r) (not (spread? r)))
|
|
||||||
(async-map-indexed-fn-render f coll env ctx))))
|
|
||||||
|
|
||||||
;; filter — eval fully then render
|
;; filter — eval fully then render
|
||||||
(= name "filter")
|
(= name "filter")
|
||||||
(async-render (async-eval expr env ctx) env ctx)
|
(async-render (async-eval expr env ctx) env ctx)
|
||||||
|
|
||||||
;; for-each (render variant) — spreads filtered
|
;; for-each (render variant)
|
||||||
(= name "for-each")
|
(= name "for-each")
|
||||||
(let ((f (async-eval (nth expr 1) env ctx))
|
(let ((f (async-eval (nth expr 1) env ctx))
|
||||||
(coll (async-eval (nth expr 2) env ctx)))
|
(coll (async-eval (nth expr 2) env ctx)))
|
||||||
(join ""
|
(join "" (async-map-fn-render f coll env ctx)))
|
||||||
(filter (fn (r) (not (spread? r)))
|
|
||||||
(async-map-fn-render f coll env ctx))))
|
|
||||||
|
|
||||||
;; provide — render-time dynamic scope
|
;; provide — render-time dynamic scope
|
||||||
(= name "provide")
|
(= name "provide")
|
||||||
@@ -443,8 +428,7 @@
|
|||||||
(provide-push! prov-name prov-val)
|
(provide-push! prov-name prov-val)
|
||||||
(let ((result (if (= body-count 1)
|
(let ((result (if (= body-count 1)
|
||||||
(async-render (nth expr body-start) env ctx)
|
(async-render (nth expr body-start) env ctx)
|
||||||
(let ((results (async-map-render (slice expr body-start) env ctx)))
|
(join "" (async-map-render (slice expr body-start) env ctx)))))
|
||||||
(join "" (filter (fn (r) (not (spread? r))) results))))))
|
|
||||||
(provide-pop! prov-name)
|
(provide-pop! prov-name)
|
||||||
result))
|
result))
|
||||||
|
|
||||||
@@ -595,35 +579,34 @@
|
|||||||
|
|
||||||
(define-async async-aser :effects [render io]
|
(define-async async-aser :effects [render io]
|
||||||
(fn (expr (env :as dict) ctx)
|
(fn (expr (env :as dict) ctx)
|
||||||
(case (type-of expr)
|
(let ((t (type-of expr))
|
||||||
"number" expr
|
(result nil))
|
||||||
"string" expr
|
(cond
|
||||||
"boolean" expr
|
(= t "number") (set! result expr)
|
||||||
"nil" nil
|
(= t "string") (set! result expr)
|
||||||
|
(= t "boolean") (set! result expr)
|
||||||
"symbol"
|
(= t "nil") (set! result nil)
|
||||||
(let ((name (symbol-name expr)))
|
(= t "symbol")
|
||||||
(cond
|
(let ((name (symbol-name expr)))
|
||||||
(env-has? env name) (env-get env name)
|
(set! result
|
||||||
(primitive? name) (get-primitive name)
|
(cond
|
||||||
(= name "true") true
|
(env-has? env name) (env-get env name)
|
||||||
(= name "false") false
|
(primitive? name) (get-primitive name)
|
||||||
(= name "nil") nil
|
(= name "true") true
|
||||||
:else (error (str "Undefined symbol: " name))))
|
(= name "false") false
|
||||||
|
(= name "nil") nil
|
||||||
"keyword" (keyword-name expr)
|
:else (error (str "Undefined symbol: " name)))))
|
||||||
|
(= t "keyword") (set! result (keyword-name expr))
|
||||||
"dict" (async-aser-dict expr env ctx)
|
(= t "dict") (set! result (async-aser-dict expr env ctx))
|
||||||
|
;; Spread — emit attrs to nearest element provider
|
||||||
;; Spread — pass through for client rendering
|
(= t "spread") (do (emit! "element-attrs" (spread-attrs expr))
|
||||||
"spread" expr
|
(set! result nil))
|
||||||
|
(= t "list") (set! result (if (empty? expr) (list) (async-aser-list expr env ctx)))
|
||||||
"list"
|
:else (set! result expr))
|
||||||
(if (empty? expr)
|
;; Catch spread values from function calls and symbol lookups
|
||||||
(list)
|
(if (spread? result)
|
||||||
(async-aser-list expr env ctx))
|
(do (emit! "element-attrs" (spread-attrs result)) nil)
|
||||||
|
result))))
|
||||||
:else expr)))
|
|
||||||
|
|
||||||
|
|
||||||
(define-async async-aser-dict :effects [render io]
|
(define-async async-aser-dict :effects [render io]
|
||||||
@@ -775,7 +758,6 @@
|
|||||||
|
|
||||||
(define-async async-aser-fragment :effects [render io]
|
(define-async async-aser-fragment :effects [render io]
|
||||||
(fn ((children :as list) (env :as dict) ctx)
|
(fn ((children :as list) (env :as dict) ctx)
|
||||||
;; Spreads are filtered — fragments have no parent element to merge into
|
|
||||||
(let ((parts (list)))
|
(let ((parts (list)))
|
||||||
(for-each
|
(for-each
|
||||||
(fn (c)
|
(fn (c)
|
||||||
@@ -783,10 +765,10 @@
|
|||||||
(if (= (type-of result) "list")
|
(if (= (type-of result) "list")
|
||||||
(for-each
|
(for-each
|
||||||
(fn (item)
|
(fn (item)
|
||||||
(when (and (not (nil? item)) (not (spread? item)))
|
(when (not (nil? item))
|
||||||
(append! parts (serialize item))))
|
(append! parts (serialize item))))
|
||||||
result)
|
result)
|
||||||
(when (and (not (nil? result)) (not (spread? result)))
|
(when (not (nil? result))
|
||||||
(append! parts (serialize result))))))
|
(append! parts (serialize result))))))
|
||||||
children)
|
children)
|
||||||
(if (empty? parts)
|
(if (empty? parts)
|
||||||
@@ -860,9 +842,12 @@
|
|||||||
(let ((token (if (or (= name "svg") (= name "math"))
|
(let ((token (if (or (= name "svg") (= name "math"))
|
||||||
(svg-context-set! true)
|
(svg-context-set! true)
|
||||||
nil))
|
nil))
|
||||||
(parts (list name))
|
(attr-parts (list))
|
||||||
|
(child-parts (list))
|
||||||
(skip false)
|
(skip false)
|
||||||
(i 0))
|
(i 0))
|
||||||
|
;; Provide scope for spread emit!
|
||||||
|
(provide-push! "element-attrs" nil)
|
||||||
(for-each
|
(for-each
|
||||||
(fn (arg)
|
(fn (arg)
|
||||||
(if skip
|
(if skip
|
||||||
@@ -872,39 +857,43 @@
|
|||||||
(< (inc i) (len args)))
|
(< (inc i) (len args)))
|
||||||
(let ((val (async-aser (nth args (inc i)) env ctx)))
|
(let ((val (async-aser (nth args (inc i)) env ctx)))
|
||||||
(when (not (nil? val))
|
(when (not (nil? val))
|
||||||
(append! parts (str ":" (keyword-name arg)))
|
(append! attr-parts (str ":" (keyword-name arg)))
|
||||||
(if (= (type-of val) "list")
|
(if (= (type-of val) "list")
|
||||||
(let ((live (filter (fn (v) (not (nil? v))) val)))
|
(let ((live (filter (fn (v) (not (nil? v))) val)))
|
||||||
(if (empty? live)
|
(if (empty? live)
|
||||||
(append! parts "nil")
|
(append! attr-parts "nil")
|
||||||
(let ((items (map serialize live)))
|
(let ((items (map serialize live)))
|
||||||
(if (some (fn (v) (sx-expr? v)) live)
|
(if (some (fn (v) (sx-expr? v)) live)
|
||||||
(append! parts (str "(<> " (join " " items) ")"))
|
(append! attr-parts (str "(<> " (join " " items) ")"))
|
||||||
(append! parts (str "(list " (join " " items) ")"))))))
|
(append! attr-parts (str "(list " (join " " items) ")"))))))
|
||||||
(append! parts (serialize val))))
|
(append! attr-parts (serialize val))))
|
||||||
(set! skip true)
|
(set! skip true)
|
||||||
(set! i (inc i)))
|
(set! i (inc i)))
|
||||||
(let ((result (async-aser arg env ctx)))
|
(let ((result (async-aser arg env ctx)))
|
||||||
(when (not (nil? result))
|
(when (not (nil? result))
|
||||||
(if (spread? result)
|
(if (= (type-of result) "list")
|
||||||
;; Spread child — merge attrs as keyword args into parent element
|
|
||||||
(for-each
|
(for-each
|
||||||
(fn (k)
|
(fn (item)
|
||||||
(let ((v (dict-get (spread-attrs result) k)))
|
(when (not (nil? item))
|
||||||
(append! parts (str ":" k))
|
(append! child-parts (serialize item))))
|
||||||
(append! parts (serialize v))))
|
result)
|
||||||
(keys (spread-attrs result)))
|
(append! child-parts (serialize result))))
|
||||||
(if (= (type-of result) "list")
|
|
||||||
(for-each
|
|
||||||
(fn (item)
|
|
||||||
(when (not (nil? item))
|
|
||||||
(append! parts (serialize item))))
|
|
||||||
result)
|
|
||||||
(append! parts (serialize result)))))
|
|
||||||
(set! i (inc i))))))
|
(set! i (inc i))))))
|
||||||
args)
|
args)
|
||||||
|
;; Collect emitted spread attrs — after explicit attrs, before children
|
||||||
|
(for-each
|
||||||
|
(fn (spread-dict)
|
||||||
|
(for-each
|
||||||
|
(fn (k)
|
||||||
|
(let ((v (dict-get spread-dict k)))
|
||||||
|
(append! attr-parts (str ":" k))
|
||||||
|
(append! attr-parts (serialize v))))
|
||||||
|
(keys spread-dict)))
|
||||||
|
(emitted "element-attrs"))
|
||||||
|
(provide-pop! "element-attrs")
|
||||||
(when token (svg-context-reset! token))
|
(when token (svg-context-reset! token))
|
||||||
(make-sx-expr (str "(" (join " " parts) ")")))))
|
(let ((parts (concat (list name) attr-parts child-parts)))
|
||||||
|
(make-sx-expr (str "(" (join " " parts) ")"))))))
|
||||||
|
|
||||||
|
|
||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -44,8 +44,8 @@
|
|||||||
;; Pre-rendered DOM node → pass through
|
;; Pre-rendered DOM node → pass through
|
||||||
"dom-node" expr
|
"dom-node" expr
|
||||||
|
|
||||||
;; Spread → pass through (parent element handles it)
|
;; Spread → emit attrs to nearest element provider, pass through for reactive-spread
|
||||||
"spread" expr
|
"spread" (do (emit! "element-attrs" (spread-attrs expr)) expr)
|
||||||
|
|
||||||
;; Dict → empty
|
;; Dict → empty
|
||||||
"dict" (create-fragment)
|
"dict" (create-fragment)
|
||||||
@@ -180,6 +180,9 @@
|
|||||||
:else ns))
|
:else ns))
|
||||||
(el (dom-create-element tag new-ns)))
|
(el (dom-create-element tag new-ns)))
|
||||||
|
|
||||||
|
;; Provide scope for spread emit! — deeply nested spreads emit here
|
||||||
|
(provide-push! "element-attrs" nil)
|
||||||
|
|
||||||
;; Process args: keywords → attrs, others → children
|
;; Process args: keywords → attrs, others → children
|
||||||
(reduce
|
(reduce
|
||||||
(fn (state arg)
|
(fn (state arg)
|
||||||
@@ -236,28 +239,8 @@
|
|||||||
;; Reactive spread: track signal deps, update attrs on change
|
;; Reactive spread: track signal deps, update attrs on change
|
||||||
(and (spread? child) *island-scope*)
|
(and (spread? child) *island-scope*)
|
||||||
(reactive-spread el (fn () (render-to-dom arg env new-ns)))
|
(reactive-spread el (fn () (render-to-dom arg env new-ns)))
|
||||||
;; Static spread: one-shot merge attrs onto parent element
|
;; Static spread: already emitted via provide, skip
|
||||||
(spread? child)
|
(spread? child) nil
|
||||||
(for-each
|
|
||||||
(fn ((key :as string))
|
|
||||||
(let ((val (dict-get (spread-attrs child) key)))
|
|
||||||
(if (= key "class")
|
|
||||||
;; Class: append to existing
|
|
||||||
(let ((existing (dom-get-attr el "class")))
|
|
||||||
(dom-set-attr el "class"
|
|
||||||
(if (and existing (not (= existing "")))
|
|
||||||
(str existing " " val)
|
|
||||||
val)))
|
|
||||||
(if (= key "style")
|
|
||||||
;; Style: append with semicolon
|
|
||||||
(let ((existing (dom-get-attr el "style")))
|
|
||||||
(dom-set-attr el "style"
|
|
||||||
(if (and existing (not (= existing "")))
|
|
||||||
(str existing ";" val)
|
|
||||||
val)))
|
|
||||||
;; Other attrs: overwrite
|
|
||||||
(dom-set-attr el key (str val))))))
|
|
||||||
(keys (spread-attrs child)))
|
|
||||||
;; Normal child: append to element
|
;; Normal child: append to element
|
||||||
:else
|
:else
|
||||||
(dom-append el child))))
|
(dom-append el child))))
|
||||||
@@ -265,6 +248,29 @@
|
|||||||
(dict "i" 0 "skip" false)
|
(dict "i" 0 "skip" false)
|
||||||
args)
|
args)
|
||||||
|
|
||||||
|
;; Collect emitted spread attrs and merge onto DOM element
|
||||||
|
(for-each
|
||||||
|
(fn (spread-dict)
|
||||||
|
(for-each
|
||||||
|
(fn ((key :as string))
|
||||||
|
(let ((val (dict-get spread-dict key)))
|
||||||
|
(if (= key "class")
|
||||||
|
(let ((existing (dom-get-attr el "class")))
|
||||||
|
(dom-set-attr el "class"
|
||||||
|
(if (and existing (not (= existing "")))
|
||||||
|
(str existing " " val)
|
||||||
|
val)))
|
||||||
|
(if (= key "style")
|
||||||
|
(let ((existing (dom-get-attr el "style")))
|
||||||
|
(dom-set-attr el "style"
|
||||||
|
(if (and existing (not (= existing "")))
|
||||||
|
(str existing ";" val)
|
||||||
|
val)))
|
||||||
|
(dom-set-attr el key (str val))))))
|
||||||
|
(keys spread-dict)))
|
||||||
|
(emitted "element-attrs"))
|
||||||
|
(provide-pop! "element-attrs")
|
||||||
|
|
||||||
el)))
|
el)))
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -30,8 +30,8 @@
|
|||||||
"keyword" (escape-html (keyword-name expr))
|
"keyword" (escape-html (keyword-name expr))
|
||||||
;; Raw HTML passthrough
|
;; Raw HTML passthrough
|
||||||
"raw-html" (raw-html-content expr)
|
"raw-html" (raw-html-content expr)
|
||||||
;; Spread — pass through as-is (parent element will merge attrs)
|
;; Spread — emit attrs to nearest element provider
|
||||||
"spread" expr
|
"spread" (do (emit! "element-attrs" (spread-attrs expr)) "")
|
||||||
;; Everything else — evaluate first
|
;; Everything else — evaluate first
|
||||||
:else (render-value-to-html (trampoline (eval-expr expr env)) env))))
|
:else (render-value-to-html (trampoline (eval-expr expr env)) env))))
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
"boolean" (if val "true" "false")
|
"boolean" (if val "true" "false")
|
||||||
"list" (render-list-to-html val env)
|
"list" (render-list-to-html val env)
|
||||||
"raw-html" (raw-html-content val)
|
"raw-html" (raw-html-content val)
|
||||||
"spread" val
|
"spread" (do (emit! "element-attrs" (spread-attrs val)) "")
|
||||||
:else (escape-html (str val)))))
|
:else (escape-html (str val)))))
|
||||||
|
|
||||||
|
|
||||||
@@ -73,16 +73,14 @@
|
|||||||
""
|
""
|
||||||
(let ((head (first expr)))
|
(let ((head (first expr)))
|
||||||
(if (not (= (type-of head) "symbol"))
|
(if (not (= (type-of head) "symbol"))
|
||||||
;; Data list — render each item (spreads filtered — no parent element)
|
;; Data list — render each item
|
||||||
(join "" (filter (fn (x) (not (spread? x)))
|
(join "" (map (fn (x) (render-value-to-html x env)) expr))
|
||||||
(map (fn (x) (render-value-to-html x env)) expr)))
|
|
||||||
(let ((name (symbol-name head))
|
(let ((name (symbol-name head))
|
||||||
(args (rest expr)))
|
(args (rest expr)))
|
||||||
(cond
|
(cond
|
||||||
;; Fragment (spreads filtered — no parent element)
|
;; Fragment
|
||||||
(= name "<>")
|
(= name "<>")
|
||||||
(join "" (filter (fn (x) (not (spread? x)))
|
(join "" (map (fn (x) (render-to-html x env)) args))
|
||||||
(map (fn (x) (render-to-html x env)) args)))
|
|
||||||
|
|
||||||
;; Raw HTML passthrough
|
;; Raw HTML passthrough
|
||||||
(= name "raw!")
|
(= name "raw!")
|
||||||
@@ -152,15 +150,14 @@
|
|||||||
(render-to-html (nth expr 3) env)
|
(render-to-html (nth expr 3) env)
|
||||||
"")))
|
"")))
|
||||||
|
|
||||||
;; when — single body: pass through (spread propagates). Multi: join strings.
|
;; when — single body: pass through. Multi: join strings.
|
||||||
(= name "when")
|
(= name "when")
|
||||||
(if (not (trampoline (eval-expr (nth expr 1) env)))
|
(if (not (trampoline (eval-expr (nth expr 1) env)))
|
||||||
""
|
""
|
||||||
(if (= (len expr) 3)
|
(if (= (len expr) 3)
|
||||||
(render-to-html (nth expr 2) env)
|
(render-to-html (nth expr 2) env)
|
||||||
(let ((results (map (fn (i) (render-to-html (nth expr i) env))
|
(join "" (map (fn (i) (render-to-html (nth expr i) env))
|
||||||
(range 2 (len expr)))))
|
(range 2 (len expr))))))
|
||||||
(join "" (filter (fn (r) (not (spread? r))) results)))))
|
|
||||||
|
|
||||||
;; cond
|
;; cond
|
||||||
(= name "cond")
|
(= name "cond")
|
||||||
@@ -178,64 +175,59 @@
|
|||||||
(let ((local (process-bindings (nth expr 1) env)))
|
(let ((local (process-bindings (nth expr 1) env)))
|
||||||
(if (= (len expr) 3)
|
(if (= (len expr) 3)
|
||||||
(render-to-html (nth expr 2) local)
|
(render-to-html (nth expr 2) local)
|
||||||
(let ((results (map (fn (i) (render-to-html (nth expr i) local))
|
(join "" (map (fn (i) (render-to-html (nth expr i) local))
|
||||||
(range 2 (len expr)))))
|
(range 2 (len expr))))))
|
||||||
(join "" (filter (fn (r) (not (spread? r))) results)))))
|
|
||||||
|
|
||||||
;; begin / do — single body: pass through. Multi: join strings.
|
;; begin / do — single body: pass through. Multi: join strings.
|
||||||
(or (= name "begin") (= name "do"))
|
(or (= name "begin") (= name "do"))
|
||||||
(if (= (len expr) 2)
|
(if (= (len expr) 2)
|
||||||
(render-to-html (nth expr 1) env)
|
(render-to-html (nth expr 1) env)
|
||||||
(let ((results (map (fn (i) (render-to-html (nth expr i) env))
|
(join "" (map (fn (i) (render-to-html (nth expr i) env))
|
||||||
(range 1 (len expr)))))
|
(range 1 (len expr)))))
|
||||||
(join "" (filter (fn (r) (not (spread? r))) results))))
|
|
||||||
|
|
||||||
;; Definition forms — eval for side effects
|
;; Definition forms — eval for side effects
|
||||||
(definition-form? name)
|
(definition-form? name)
|
||||||
(do (trampoline (eval-expr expr env)) "")
|
(do (trampoline (eval-expr expr env)) "")
|
||||||
|
|
||||||
;; map — spreads filtered (no parent element in list context)
|
;; map
|
||||||
(= name "map")
|
(= name "map")
|
||||||
(let ((f (trampoline (eval-expr (nth expr 1) env)))
|
(let ((f (trampoline (eval-expr (nth expr 1) env)))
|
||||||
(coll (trampoline (eval-expr (nth expr 2) env))))
|
(coll (trampoline (eval-expr (nth expr 2) env))))
|
||||||
(join ""
|
(join ""
|
||||||
(filter (fn (r) (not (spread? r)))
|
(map
|
||||||
(map
|
(fn (item)
|
||||||
(fn (item)
|
(if (lambda? f)
|
||||||
(if (lambda? f)
|
(render-lambda-html f (list item) env)
|
||||||
(render-lambda-html f (list item) env)
|
(render-to-html (apply f (list item)) env)))
|
||||||
(render-to-html (apply f (list item)) env)))
|
coll)))
|
||||||
coll))))
|
|
||||||
|
|
||||||
;; map-indexed — spreads filtered
|
;; map-indexed
|
||||||
(= name "map-indexed")
|
(= name "map-indexed")
|
||||||
(let ((f (trampoline (eval-expr (nth expr 1) env)))
|
(let ((f (trampoline (eval-expr (nth expr 1) env)))
|
||||||
(coll (trampoline (eval-expr (nth expr 2) env))))
|
(coll (trampoline (eval-expr (nth expr 2) env))))
|
||||||
(join ""
|
(join ""
|
||||||
(filter (fn (r) (not (spread? r)))
|
(map-indexed
|
||||||
(map-indexed
|
(fn (i item)
|
||||||
(fn (i item)
|
(if (lambda? f)
|
||||||
(if (lambda? f)
|
(render-lambda-html f (list i item) env)
|
||||||
(render-lambda-html f (list i item) env)
|
(render-to-html (apply f (list i item)) env)))
|
||||||
(render-to-html (apply f (list i item)) env)))
|
coll)))
|
||||||
coll))))
|
|
||||||
|
|
||||||
;; filter — evaluate fully then render
|
;; filter — evaluate fully then render
|
||||||
(= name "filter")
|
(= name "filter")
|
||||||
(render-to-html (trampoline (eval-expr expr env)) env)
|
(render-to-html (trampoline (eval-expr expr env)) env)
|
||||||
|
|
||||||
;; for-each (render variant) — spreads filtered
|
;; for-each (render variant)
|
||||||
(= name "for-each")
|
(= name "for-each")
|
||||||
(let ((f (trampoline (eval-expr (nth expr 1) env)))
|
(let ((f (trampoline (eval-expr (nth expr 1) env)))
|
||||||
(coll (trampoline (eval-expr (nth expr 2) env))))
|
(coll (trampoline (eval-expr (nth expr 2) env))))
|
||||||
(join ""
|
(join ""
|
||||||
(filter (fn (r) (not (spread? r)))
|
(map
|
||||||
(map
|
(fn (item)
|
||||||
(fn (item)
|
(if (lambda? f)
|
||||||
(if (lambda? f)
|
(render-lambda-html f (list item) env)
|
||||||
(render-lambda-html f (list item) env)
|
(render-to-html (apply f (list item)) env)))
|
||||||
(render-to-html (apply f (list item)) env)))
|
coll)))
|
||||||
coll))))
|
|
||||||
|
|
||||||
;; provide — render-time dynamic scope
|
;; provide — render-time dynamic scope
|
||||||
(= name "provide")
|
(= name "provide")
|
||||||
@@ -246,9 +238,8 @@
|
|||||||
(provide-push! prov-name prov-val)
|
(provide-push! prov-name prov-val)
|
||||||
(let ((result (if (= body-count 1)
|
(let ((result (if (= body-count 1)
|
||||||
(render-to-html (nth expr body-start) env)
|
(render-to-html (nth expr body-start) env)
|
||||||
(join "" (filter (fn (r) (not (spread? r)))
|
(join "" (map (fn (i) (render-to-html (nth expr i) env))
|
||||||
(map (fn (i) (render-to-html (nth expr i) env))
|
(range body-start (+ body-start body-count)))))))
|
||||||
(range body-start (+ body-start body-count))))))))
|
|
||||||
(provide-pop! prov-name)
|
(provide-pop! prov-name)
|
||||||
result))
|
result))
|
||||||
|
|
||||||
@@ -307,17 +298,9 @@
|
|||||||
(env-set! local p (if (dict-has? kwargs p) (dict-get kwargs p) nil)))
|
(env-set! local p (if (dict-has? kwargs p) (dict-get kwargs p) nil)))
|
||||||
(component-params comp))
|
(component-params comp))
|
||||||
;; If component accepts children, pre-render them to raw HTML
|
;; If component accepts children, pre-render them to raw HTML
|
||||||
;; Spread values are filtered out (no parent element to merge onto)
|
|
||||||
(when (component-has-children? comp)
|
(when (component-has-children? comp)
|
||||||
(let ((parts (list)))
|
(env-set! local "children"
|
||||||
(for-each
|
(make-raw-html (join "" (map (fn (c) (render-to-html c env)) children)))))
|
||||||
(fn (c)
|
|
||||||
(let ((r (render-to-html c env)))
|
|
||||||
(when (not (spread? r))
|
|
||||||
(append! parts r))))
|
|
||||||
children)
|
|
||||||
(env-set! local "children"
|
|
||||||
(make-raw-html (join "" parts)))))
|
|
||||||
(render-to-html (component-body comp) local)))))
|
(render-to-html (component-body comp) local)))))
|
||||||
|
|
||||||
|
|
||||||
@@ -329,18 +312,17 @@
|
|||||||
(is-void (contains? VOID_ELEMENTS tag)))
|
(is-void (contains? VOID_ELEMENTS tag)))
|
||||||
(if is-void
|
(if is-void
|
||||||
(str "<" tag (render-attrs attrs) " />")
|
(str "<" tag (render-attrs attrs) " />")
|
||||||
;; Render children, collecting spreads and content separately
|
;; Provide scope for spread emit!
|
||||||
(let ((content-parts (list)))
|
(do
|
||||||
(for-each
|
(provide-push! "element-attrs" nil)
|
||||||
(fn (c)
|
(let ((content (join "" (map (fn (c) (render-to-html c env)) children))))
|
||||||
(let ((result (render-to-html c env)))
|
(for-each
|
||||||
(if (spread? result)
|
(fn (spread-dict) (merge-spread-attrs attrs spread-dict))
|
||||||
(merge-spread-attrs attrs (spread-attrs result))
|
(emitted "element-attrs"))
|
||||||
(append! content-parts result))))
|
(provide-pop! "element-attrs")
|
||||||
children)
|
(str "<" tag (render-attrs attrs) ">"
|
||||||
(str "<" tag (render-attrs attrs) ">"
|
content
|
||||||
(join "" content-parts)
|
"</" tag ">")))))))
|
||||||
"</" tag ">"))))))
|
|
||||||
|
|
||||||
|
|
||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
@@ -375,19 +357,17 @@
|
|||||||
(assoc state "i" (inc (get state "i"))))))))
|
(assoc state "i" (inc (get state "i"))))))))
|
||||||
(dict "i" 0 "skip" false)
|
(dict "i" 0 "skip" false)
|
||||||
args)
|
args)
|
||||||
;; Render children, handling spreads
|
;; Provide scope for spread emit!
|
||||||
(let ((lake-attrs (dict "data-sx-lake" (or lake-id "")))
|
(let ((lake-attrs (dict "data-sx-lake" (or lake-id ""))))
|
||||||
(content-parts (list)))
|
(provide-push! "element-attrs" nil)
|
||||||
(for-each
|
(let ((content (join "" (map (fn (c) (render-to-html c env)) children))))
|
||||||
(fn (c)
|
(for-each
|
||||||
(let ((result (render-to-html c env)))
|
(fn (spread-dict) (merge-spread-attrs lake-attrs spread-dict))
|
||||||
(if (spread? result)
|
(emitted "element-attrs"))
|
||||||
(merge-spread-attrs lake-attrs (spread-attrs result))
|
(provide-pop! "element-attrs")
|
||||||
(append! content-parts result))))
|
(str "<" lake-tag (render-attrs lake-attrs) ">"
|
||||||
children)
|
content
|
||||||
(str "<" lake-tag (render-attrs lake-attrs) ">"
|
"</" lake-tag ">"))))))
|
||||||
(join "" content-parts)
|
|
||||||
"</" lake-tag ">")))))
|
|
||||||
|
|
||||||
|
|
||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
@@ -425,19 +405,17 @@
|
|||||||
(assoc state "i" (inc (get state "i"))))))))
|
(assoc state "i" (inc (get state "i"))))))))
|
||||||
(dict "i" 0 "skip" false)
|
(dict "i" 0 "skip" false)
|
||||||
args)
|
args)
|
||||||
;; Render children, handling spreads
|
;; Provide scope for spread emit!
|
||||||
(let ((marsh-attrs (dict "data-sx-marsh" (or marsh-id "")))
|
(let ((marsh-attrs (dict "data-sx-marsh" (or marsh-id ""))))
|
||||||
(content-parts (list)))
|
(provide-push! "element-attrs" nil)
|
||||||
(for-each
|
(let ((content (join "" (map (fn (c) (render-to-html c env)) children))))
|
||||||
(fn (c)
|
(for-each
|
||||||
(let ((result (render-to-html c env)))
|
(fn (spread-dict) (merge-spread-attrs marsh-attrs spread-dict))
|
||||||
(if (spread? result)
|
(emitted "element-attrs"))
|
||||||
(merge-spread-attrs marsh-attrs (spread-attrs result))
|
(provide-pop! "element-attrs")
|
||||||
(append! content-parts result))))
|
(str "<" marsh-tag (render-attrs marsh-attrs) ">"
|
||||||
children)
|
content
|
||||||
(str "<" marsh-tag (render-attrs marsh-attrs) ">"
|
"</" marsh-tag ">"))))))
|
||||||
(join "" content-parts)
|
|
||||||
"</" marsh-tag ">")))))
|
|
||||||
|
|
||||||
|
|
||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
@@ -487,17 +465,9 @@
|
|||||||
(component-params island))
|
(component-params island))
|
||||||
|
|
||||||
;; If island accepts children, pre-render them to raw HTML
|
;; If island accepts children, pre-render them to raw HTML
|
||||||
;; Spread values filtered out (no parent element)
|
|
||||||
(when (component-has-children? island)
|
(when (component-has-children? island)
|
||||||
(let ((parts (list)))
|
(env-set! local "children"
|
||||||
(for-each
|
(make-raw-html (join "" (map (fn (c) (render-to-html c env)) children)))))
|
||||||
(fn (c)
|
|
||||||
(let ((r (render-to-html c env)))
|
|
||||||
(when (not (spread? r))
|
|
||||||
(append! parts r))))
|
|
||||||
children)
|
|
||||||
(env-set! local "children"
|
|
||||||
(make-raw-html (join "" parts)))))
|
|
||||||
|
|
||||||
;; Render the island body as HTML
|
;; Render the island body as HTML
|
||||||
(let ((body-html (render-to-html (component-body island) local))
|
(let ((body-html (render-to-html (component-body island) local))
|
||||||
|
|||||||
@@ -25,33 +25,38 @@
|
|||||||
;; Evaluate for SX wire format — serialize rendering forms,
|
;; Evaluate for SX wire format — serialize rendering forms,
|
||||||
;; evaluate control flow and function calls.
|
;; evaluate control flow and function calls.
|
||||||
(set-render-active! true)
|
(set-render-active! true)
|
||||||
(case (type-of expr)
|
(let ((result
|
||||||
"number" expr
|
(case (type-of expr)
|
||||||
"string" expr
|
"number" expr
|
||||||
"boolean" expr
|
"string" expr
|
||||||
"nil" nil
|
"boolean" expr
|
||||||
|
"nil" nil
|
||||||
|
|
||||||
"symbol"
|
"symbol"
|
||||||
(let ((name (symbol-name expr)))
|
(let ((name (symbol-name expr)))
|
||||||
(cond
|
(cond
|
||||||
(env-has? env name) (env-get env name)
|
(env-has? env name) (env-get env name)
|
||||||
(primitive? name) (get-primitive name)
|
(primitive? name) (get-primitive name)
|
||||||
(= name "true") true
|
(= name "true") true
|
||||||
(= name "false") false
|
(= name "false") false
|
||||||
(= name "nil") nil
|
(= name "nil") nil
|
||||||
:else (error (str "Undefined symbol: " name))))
|
:else (error (str "Undefined symbol: " name))))
|
||||||
|
|
||||||
"keyword" (keyword-name expr)
|
"keyword" (keyword-name expr)
|
||||||
|
|
||||||
"list"
|
"list"
|
||||||
(if (empty? expr)
|
(if (empty? expr)
|
||||||
(list)
|
(list)
|
||||||
(aser-list expr env))
|
(aser-list expr env))
|
||||||
|
|
||||||
;; Spread — pass through for client rendering
|
;; Spread — emit attrs to nearest element provider
|
||||||
"spread" expr
|
"spread" (do (emit! "element-attrs" (spread-attrs expr)) nil)
|
||||||
|
|
||||||
:else expr)))
|
:else expr)))
|
||||||
|
;; Catch spread values from function calls and symbol lookups
|
||||||
|
(if (spread? result)
|
||||||
|
(do (emit! "element-attrs" (spread-attrs result)) nil)
|
||||||
|
result))))
|
||||||
|
|
||||||
|
|
||||||
(define aser-list :effects [render]
|
(define aser-list :effects [render]
|
||||||
@@ -110,7 +115,6 @@
|
|||||||
(fn ((children :as list) (env :as dict))
|
(fn ((children :as list) (env :as dict))
|
||||||
;; Serialize (<> child1 child2 ...) to sx source string
|
;; Serialize (<> child1 child2 ...) to sx source string
|
||||||
;; Must flatten list results (e.g. from map/filter) to avoid nested parens
|
;; Must flatten list results (e.g. from map/filter) to avoid nested parens
|
||||||
;; Spreads are filtered — fragments have no parent element to merge into
|
|
||||||
(let ((parts (list)))
|
(let ((parts (list)))
|
||||||
(for-each
|
(for-each
|
||||||
(fn (c)
|
(fn (c)
|
||||||
@@ -118,10 +122,10 @@
|
|||||||
(if (= (type-of result) "list")
|
(if (= (type-of result) "list")
|
||||||
(for-each
|
(for-each
|
||||||
(fn (item)
|
(fn (item)
|
||||||
(when (and (not (nil? item)) (not (spread? item)))
|
(when (not (nil? item))
|
||||||
(append! parts (serialize item))))
|
(append! parts (serialize item))))
|
||||||
result)
|
result)
|
||||||
(when (and (not (nil? result)) (not (spread? result)))
|
(when (not (nil? result))
|
||||||
(append! parts (serialize result))))))
|
(append! parts (serialize result))))))
|
||||||
children)
|
children)
|
||||||
(if (empty? parts)
|
(if (empty? parts)
|
||||||
@@ -134,9 +138,13 @@
|
|||||||
;; Serialize (name :key val child ...) — evaluate args but keep as sx
|
;; Serialize (name :key val child ...) — evaluate args but keep as sx
|
||||||
;; Uses for-each + mutable state (not reduce) so bootstrapper emits for-loops
|
;; Uses for-each + mutable state (not reduce) so bootstrapper emits for-loops
|
||||||
;; that can contain nested for-each for list flattening.
|
;; that can contain nested for-each for list flattening.
|
||||||
(let ((parts (list name))
|
;; Separate attrs and children so emitted spread attrs go before children.
|
||||||
|
(let ((attr-parts (list))
|
||||||
|
(child-parts (list))
|
||||||
(skip false)
|
(skip false)
|
||||||
(i 0))
|
(i 0))
|
||||||
|
;; Provide scope for spread emit!
|
||||||
|
(provide-push! "element-attrs" nil)
|
||||||
(for-each
|
(for-each
|
||||||
(fn (arg)
|
(fn (arg)
|
||||||
(if skip
|
(if skip
|
||||||
@@ -146,30 +154,34 @@
|
|||||||
(< (inc i) (len args)))
|
(< (inc i) (len args)))
|
||||||
(let ((val (aser (nth args (inc i)) env)))
|
(let ((val (aser (nth args (inc i)) env)))
|
||||||
(when (not (nil? val))
|
(when (not (nil? val))
|
||||||
(append! parts (str ":" (keyword-name arg)))
|
(append! attr-parts (str ":" (keyword-name arg)))
|
||||||
(append! parts (serialize val)))
|
(append! attr-parts (serialize val)))
|
||||||
(set! skip true)
|
(set! skip true)
|
||||||
(set! i (inc i)))
|
(set! i (inc i)))
|
||||||
(let ((val (aser arg env)))
|
(let ((val (aser arg env)))
|
||||||
(when (not (nil? val))
|
(when (not (nil? val))
|
||||||
(if (spread? val)
|
(if (= (type-of val) "list")
|
||||||
;; Spread child — merge attrs as keyword args into parent element
|
|
||||||
(for-each
|
(for-each
|
||||||
(fn (k)
|
(fn (item)
|
||||||
(let ((v (dict-get (spread-attrs val) k)))
|
(when (not (nil? item))
|
||||||
(append! parts (str ":" k))
|
(append! child-parts (serialize item))))
|
||||||
(append! parts (serialize v))))
|
val)
|
||||||
(keys (spread-attrs val)))
|
(append! child-parts (serialize val))))
|
||||||
(if (= (type-of val) "list")
|
|
||||||
(for-each
|
|
||||||
(fn (item)
|
|
||||||
(when (not (nil? item))
|
|
||||||
(append! parts (serialize item))))
|
|
||||||
val)
|
|
||||||
(append! parts (serialize val)))))
|
|
||||||
(set! i (inc i))))))
|
(set! i (inc i))))))
|
||||||
args)
|
args)
|
||||||
(str "(" (join " " parts) ")"))))
|
;; Collect emitted spread attrs — goes after explicit attrs, before children
|
||||||
|
(for-each
|
||||||
|
(fn (spread-dict)
|
||||||
|
(for-each
|
||||||
|
(fn (k)
|
||||||
|
(let ((v (dict-get spread-dict k)))
|
||||||
|
(append! attr-parts (str ":" k))
|
||||||
|
(append! attr-parts (serialize v))))
|
||||||
|
(keys spread-dict)))
|
||||||
|
(emitted "element-attrs"))
|
||||||
|
(provide-pop! "element-attrs")
|
||||||
|
(let ((parts (concat (list name) attr-parts child-parts)))
|
||||||
|
(str "(" (join " " parts) ")")))))
|
||||||
|
|
||||||
|
|
||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -1203,8 +1203,6 @@ PLATFORM_JS_PRE = '''
|
|||||||
function sxEmit(name, value) {
|
function sxEmit(name, value) {
|
||||||
if (_provideStacks[name] && _provideStacks[name].length) {
|
if (_provideStacks[name] && _provideStacks[name].length) {
|
||||||
_provideStacks[name][_provideStacks[name].length - 1].emitted.push(value);
|
_provideStacks[name][_provideStacks[name].length - 1].emitted.push(value);
|
||||||
} else {
|
|
||||||
throw new Error("No provider for emit!: " + name);
|
|
||||||
}
|
}
|
||||||
return NIL;
|
return NIL;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -126,11 +126,9 @@ def sx_context(name, *default):
|
|||||||
|
|
||||||
|
|
||||||
def sx_emit(name, value):
|
def sx_emit(name, value):
|
||||||
"""Append value to nearest enclosing provider's accumulator. Error if no provider."""
|
"""Append value to nearest enclosing provider's accumulator. No-op if no provider."""
|
||||||
if name in _provide_stacks and _provide_stacks[name]:
|
if name in _provide_stacks and _provide_stacks[name]:
|
||||||
_provide_stacks[name][-1]["emitted"].append(value)
|
_provide_stacks[name][-1]["emitted"].append(value)
|
||||||
else:
|
|
||||||
raise RuntimeError(f"No provider for emit!: {name}")
|
|
||||||
return NIL
|
return NIL
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
# WARNING: special-forms.sx declares forms not in eval.sx: reset, shift
|
|
||||||
# WARNING: eval.sx dispatches forms not in special-forms.sx: form?, provide
|
|
||||||
"""
|
"""
|
||||||
sx_ref.py -- Generated from reference SX evaluator specification.
|
sx_ref.py -- Generated from reference SX evaluator specification.
|
||||||
|
|
||||||
@@ -87,11 +85,9 @@ def sx_context(name, *default):
|
|||||||
|
|
||||||
|
|
||||||
def sx_emit(name, value):
|
def sx_emit(name, value):
|
||||||
"""Append value to nearest enclosing provider's accumulator. Error if no provider."""
|
"""Append value to nearest enclosing provider's accumulator. No-op if no provider."""
|
||||||
if name in _provide_stacks and _provide_stacks[name]:
|
if name in _provide_stacks and _provide_stacks[name]:
|
||||||
_provide_stacks[name][-1]["emitted"].append(value)
|
_provide_stacks[name][-1]["emitted"].append(value)
|
||||||
else:
|
|
||||||
raise RuntimeError(f"No provider for emit!: {name}")
|
|
||||||
return NIL
|
return NIL
|
||||||
|
|
||||||
|
|
||||||
@@ -2225,7 +2221,8 @@ def render_to_html(expr, env):
|
|||||||
elif _match == 'raw-html':
|
elif _match == 'raw-html':
|
||||||
return raw_html_content(expr)
|
return raw_html_content(expr)
|
||||||
elif _match == 'spread':
|
elif _match == 'spread':
|
||||||
return expr
|
sx_emit('element-attrs', spread_attrs(expr))
|
||||||
|
return ''
|
||||||
else:
|
else:
|
||||||
return render_value_to_html(trampoline(eval_expr(expr, env)), env)
|
return render_value_to_html(trampoline(eval_expr(expr, env)), env)
|
||||||
|
|
||||||
@@ -2248,7 +2245,8 @@ def render_value_to_html(val, env):
|
|||||||
elif _match == 'raw-html':
|
elif _match == 'raw-html':
|
||||||
return raw_html_content(val)
|
return raw_html_content(val)
|
||||||
elif _match == 'spread':
|
elif _match == 'spread':
|
||||||
return val
|
sx_emit('element-attrs', spread_attrs(val))
|
||||||
|
return ''
|
||||||
else:
|
else:
|
||||||
return escape_html(sx_str(val))
|
return escape_html(sx_str(val))
|
||||||
|
|
||||||
@@ -2266,12 +2264,12 @@ def render_list_to_html(expr, env):
|
|||||||
else:
|
else:
|
||||||
head = first(expr)
|
head = first(expr)
|
||||||
if sx_truthy((not sx_truthy((type_of(head) == 'symbol')))):
|
if sx_truthy((not sx_truthy((type_of(head) == 'symbol')))):
|
||||||
return join('', filter(lambda x: (not sx_truthy(is_spread(x))), map(lambda x: render_value_to_html(x, env), expr)))
|
return join('', map(lambda x: render_value_to_html(x, env), expr))
|
||||||
else:
|
else:
|
||||||
name = symbol_name(head)
|
name = symbol_name(head)
|
||||||
args = rest(expr)
|
args = rest(expr)
|
||||||
if sx_truthy((name == '<>')):
|
if sx_truthy((name == '<>')):
|
||||||
return join('', filter(lambda x: (not sx_truthy(is_spread(x))), map(lambda x: render_to_html(x, env), args)))
|
return join('', map(lambda x: render_to_html(x, env), args))
|
||||||
elif sx_truthy((name == 'raw!')):
|
elif sx_truthy((name == 'raw!')):
|
||||||
return join('', map(lambda x: sx_str(trampoline(eval_expr(x, env))), args))
|
return join('', map(lambda x: sx_str(trampoline(eval_expr(x, env))), args))
|
||||||
elif sx_truthy((name == 'lake')):
|
elif sx_truthy((name == 'lake')):
|
||||||
@@ -2315,8 +2313,7 @@ def dispatch_html_form(name, expr, env):
|
|||||||
if sx_truthy((len(expr) == 3)):
|
if sx_truthy((len(expr) == 3)):
|
||||||
return render_to_html(nth(expr, 2), env)
|
return render_to_html(nth(expr, 2), env)
|
||||||
else:
|
else:
|
||||||
results = map(lambda i: render_to_html(nth(expr, i), env), range(2, len(expr)))
|
return join('', map(lambda i: render_to_html(nth(expr, i), env), range(2, len(expr))))
|
||||||
return join('', filter(lambda r: (not sx_truthy(is_spread(r))), results))
|
|
||||||
elif sx_truthy((name == 'cond')):
|
elif sx_truthy((name == 'cond')):
|
||||||
branch = eval_cond(rest(expr), env)
|
branch = eval_cond(rest(expr), env)
|
||||||
if sx_truthy(branch):
|
if sx_truthy(branch):
|
||||||
@@ -2330,38 +2327,36 @@ def dispatch_html_form(name, expr, env):
|
|||||||
if sx_truthy((len(expr) == 3)):
|
if sx_truthy((len(expr) == 3)):
|
||||||
return render_to_html(nth(expr, 2), local)
|
return render_to_html(nth(expr, 2), local)
|
||||||
else:
|
else:
|
||||||
results = map(lambda i: render_to_html(nth(expr, i), local), range(2, len(expr)))
|
return join('', map(lambda i: render_to_html(nth(expr, i), local), range(2, len(expr))))
|
||||||
return join('', filter(lambda r: (not sx_truthy(is_spread(r))), results))
|
|
||||||
elif sx_truthy(((name == 'begin') if sx_truthy((name == 'begin')) else (name == 'do'))):
|
elif sx_truthy(((name == 'begin') if sx_truthy((name == 'begin')) else (name == 'do'))):
|
||||||
if sx_truthy((len(expr) == 2)):
|
if sx_truthy((len(expr) == 2)):
|
||||||
return render_to_html(nth(expr, 1), env)
|
return render_to_html(nth(expr, 1), env)
|
||||||
else:
|
else:
|
||||||
results = map(lambda i: render_to_html(nth(expr, i), env), range(1, len(expr)))
|
return join('', map(lambda i: render_to_html(nth(expr, i), env), range(1, len(expr))))
|
||||||
return join('', filter(lambda r: (not sx_truthy(is_spread(r))), results))
|
|
||||||
elif sx_truthy(is_definition_form(name)):
|
elif sx_truthy(is_definition_form(name)):
|
||||||
trampoline(eval_expr(expr, env))
|
trampoline(eval_expr(expr, env))
|
||||||
return ''
|
return ''
|
||||||
elif sx_truthy((name == 'map')):
|
elif sx_truthy((name == 'map')):
|
||||||
f = trampoline(eval_expr(nth(expr, 1), env))
|
f = trampoline(eval_expr(nth(expr, 1), env))
|
||||||
coll = trampoline(eval_expr(nth(expr, 2), env))
|
coll = trampoline(eval_expr(nth(expr, 2), env))
|
||||||
return join('', filter(lambda r: (not sx_truthy(is_spread(r))), map(lambda item: (render_lambda_html(f, [item], env) if sx_truthy(is_lambda(f)) else render_to_html(apply(f, [item]), env)), coll)))
|
return join('', map(lambda item: (render_lambda_html(f, [item], env) if sx_truthy(is_lambda(f)) else render_to_html(apply(f, [item]), env)), coll))
|
||||||
elif sx_truthy((name == 'map-indexed')):
|
elif sx_truthy((name == 'map-indexed')):
|
||||||
f = trampoline(eval_expr(nth(expr, 1), env))
|
f = trampoline(eval_expr(nth(expr, 1), env))
|
||||||
coll = trampoline(eval_expr(nth(expr, 2), env))
|
coll = trampoline(eval_expr(nth(expr, 2), env))
|
||||||
return join('', filter(lambda r: (not sx_truthy(is_spread(r))), map_indexed(lambda i, item: (render_lambda_html(f, [i, item], env) if sx_truthy(is_lambda(f)) else render_to_html(apply(f, [i, item]), env)), coll)))
|
return join('', map_indexed(lambda i, item: (render_lambda_html(f, [i, item], env) if sx_truthy(is_lambda(f)) else render_to_html(apply(f, [i, item]), env)), coll))
|
||||||
elif sx_truthy((name == 'filter')):
|
elif sx_truthy((name == 'filter')):
|
||||||
return render_to_html(trampoline(eval_expr(expr, env)), env)
|
return render_to_html(trampoline(eval_expr(expr, env)), env)
|
||||||
elif sx_truthy((name == 'for-each')):
|
elif sx_truthy((name == 'for-each')):
|
||||||
f = trampoline(eval_expr(nth(expr, 1), env))
|
f = trampoline(eval_expr(nth(expr, 1), env))
|
||||||
coll = trampoline(eval_expr(nth(expr, 2), env))
|
coll = trampoline(eval_expr(nth(expr, 2), env))
|
||||||
return join('', filter(lambda r: (not sx_truthy(is_spread(r))), map(lambda item: (render_lambda_html(f, [item], env) if sx_truthy(is_lambda(f)) else render_to_html(apply(f, [item]), env)), coll)))
|
return join('', map(lambda item: (render_lambda_html(f, [item], env) if sx_truthy(is_lambda(f)) else render_to_html(apply(f, [item]), env)), coll))
|
||||||
elif sx_truthy((name == 'provide')):
|
elif sx_truthy((name == 'provide')):
|
||||||
prov_name = trampoline(eval_expr(nth(expr, 1), env))
|
prov_name = trampoline(eval_expr(nth(expr, 1), env))
|
||||||
prov_val = trampoline(eval_expr(nth(expr, 2), env))
|
prov_val = trampoline(eval_expr(nth(expr, 2), env))
|
||||||
body_start = 3
|
body_start = 3
|
||||||
body_count = (len(expr) - 3)
|
body_count = (len(expr) - 3)
|
||||||
provide_push(prov_name, prov_val)
|
provide_push(prov_name, prov_val)
|
||||||
result = (render_to_html(nth(expr, body_start), env) if sx_truthy((body_count == 1)) else join('', filter(lambda r: (not sx_truthy(is_spread(r))), map(lambda i: render_to_html(nth(expr, i), env), range(body_start, (body_start + body_count))))))
|
result = (render_to_html(nth(expr, body_start), env) if sx_truthy((body_count == 1)) else join('', map(lambda i: render_to_html(nth(expr, i), env), range(body_start, (body_start + body_count)))))
|
||||||
provide_pop(prov_name)
|
provide_pop(prov_name)
|
||||||
return result
|
return result
|
||||||
else:
|
else:
|
||||||
@@ -2382,12 +2377,7 @@ def render_html_component(comp, args, env):
|
|||||||
for p in component_params(comp):
|
for p in component_params(comp):
|
||||||
local[p] = (dict_get(kwargs, p) if sx_truthy(dict_has(kwargs, p)) else NIL)
|
local[p] = (dict_get(kwargs, p) if sx_truthy(dict_has(kwargs, p)) else NIL)
|
||||||
if sx_truthy(component_has_children(comp)):
|
if sx_truthy(component_has_children(comp)):
|
||||||
parts = []
|
local['children'] = make_raw_html(join('', map(lambda c: render_to_html(c, env), children)))
|
||||||
for c in children:
|
|
||||||
r = render_to_html(c, env)
|
|
||||||
if sx_truthy((not sx_truthy(is_spread(r)))):
|
|
||||||
parts.append(r)
|
|
||||||
local['children'] = make_raw_html(join('', parts))
|
|
||||||
return render_to_html(component_body(comp), local)
|
return render_to_html(component_body(comp), local)
|
||||||
|
|
||||||
# render-html-element
|
# render-html-element
|
||||||
@@ -2399,14 +2389,12 @@ def render_html_element(tag, args, env):
|
|||||||
if sx_truthy(is_void):
|
if sx_truthy(is_void):
|
||||||
return sx_str('<', tag, render_attrs(attrs), ' />')
|
return sx_str('<', tag, render_attrs(attrs), ' />')
|
||||||
else:
|
else:
|
||||||
content_parts = []
|
provide_push('element-attrs', NIL)
|
||||||
for c in children:
|
content = join('', map(lambda c: render_to_html(c, env), children))
|
||||||
result = render_to_html(c, env)
|
for spread_dict in sx_emitted('element-attrs'):
|
||||||
if sx_truthy(is_spread(result)):
|
merge_spread_attrs(attrs, spread_dict)
|
||||||
merge_spread_attrs(attrs, spread_attrs(result))
|
provide_pop('element-attrs')
|
||||||
else:
|
return sx_str('<', tag, render_attrs(attrs), '>', content, '</', tag, '>')
|
||||||
content_parts.append(result)
|
|
||||||
return sx_str('<', tag, render_attrs(attrs), '>', join('', content_parts), '</', tag, '>')
|
|
||||||
|
|
||||||
# render-html-lake
|
# render-html-lake
|
||||||
def render_html_lake(args, env):
|
def render_html_lake(args, env):
|
||||||
@@ -2416,14 +2404,12 @@ def render_html_lake(args, env):
|
|||||||
children = []
|
children = []
|
||||||
reduce(lambda state, arg: (lambda skip: (assoc(state, 'skip', False, 'i', (get(state, 'i') + 1)) if sx_truthy(skip) else ((lambda kname: (lambda kval: _sx_begin((_sx_cell_set(_cells, 'lake_id', kval) if sx_truthy((kname == 'id')) else (_sx_cell_set(_cells, 'lake_tag', kval) if sx_truthy((kname == 'tag')) else NIL)), assoc(state, 'skip', True, 'i', (get(state, 'i') + 1))))(trampoline(eval_expr(nth(args, (get(state, 'i') + 1)), env))))(keyword_name(arg)) if sx_truthy(((type_of(arg) == 'keyword') if not sx_truthy((type_of(arg) == 'keyword')) else ((get(state, 'i') + 1) < len(args)))) else _sx_begin(_sx_append(children, arg), assoc(state, 'i', (get(state, 'i') + 1))))))(get(state, 'skip')), {'i': 0, 'skip': False}, args)
|
reduce(lambda state, arg: (lambda skip: (assoc(state, 'skip', False, 'i', (get(state, 'i') + 1)) if sx_truthy(skip) else ((lambda kname: (lambda kval: _sx_begin((_sx_cell_set(_cells, 'lake_id', kval) if sx_truthy((kname == 'id')) else (_sx_cell_set(_cells, 'lake_tag', kval) if sx_truthy((kname == 'tag')) else NIL)), assoc(state, 'skip', True, 'i', (get(state, 'i') + 1))))(trampoline(eval_expr(nth(args, (get(state, 'i') + 1)), env))))(keyword_name(arg)) if sx_truthy(((type_of(arg) == 'keyword') if not sx_truthy((type_of(arg) == 'keyword')) else ((get(state, 'i') + 1) < len(args)))) else _sx_begin(_sx_append(children, arg), assoc(state, 'i', (get(state, 'i') + 1))))))(get(state, 'skip')), {'i': 0, 'skip': False}, args)
|
||||||
lake_attrs = {'data-sx-lake': (_cells['lake_id'] if sx_truthy(_cells['lake_id']) else '')}
|
lake_attrs = {'data-sx-lake': (_cells['lake_id'] if sx_truthy(_cells['lake_id']) else '')}
|
||||||
content_parts = []
|
provide_push('element-attrs', NIL)
|
||||||
for c in children:
|
content = join('', map(lambda c: render_to_html(c, env), children))
|
||||||
result = render_to_html(c, env)
|
for spread_dict in sx_emitted('element-attrs'):
|
||||||
if sx_truthy(is_spread(result)):
|
merge_spread_attrs(lake_attrs, spread_dict)
|
||||||
merge_spread_attrs(lake_attrs, spread_attrs(result))
|
provide_pop('element-attrs')
|
||||||
else:
|
return sx_str('<', _cells['lake_tag'], render_attrs(lake_attrs), '>', content, '</', _cells['lake_tag'], '>')
|
||||||
content_parts.append(result)
|
|
||||||
return sx_str('<', _cells['lake_tag'], render_attrs(lake_attrs), '>', join('', content_parts), '</', _cells['lake_tag'], '>')
|
|
||||||
|
|
||||||
# render-html-marsh
|
# render-html-marsh
|
||||||
def render_html_marsh(args, env):
|
def render_html_marsh(args, env):
|
||||||
@@ -2433,14 +2419,12 @@ def render_html_marsh(args, env):
|
|||||||
children = []
|
children = []
|
||||||
reduce(lambda state, arg: (lambda skip: (assoc(state, 'skip', False, 'i', (get(state, 'i') + 1)) if sx_truthy(skip) else ((lambda kname: (lambda kval: _sx_begin((_sx_cell_set(_cells, 'marsh_id', kval) if sx_truthy((kname == 'id')) else (_sx_cell_set(_cells, 'marsh_tag', kval) if sx_truthy((kname == 'tag')) else (NIL if sx_truthy((kname == 'transform')) else NIL))), assoc(state, 'skip', True, 'i', (get(state, 'i') + 1))))(trampoline(eval_expr(nth(args, (get(state, 'i') + 1)), env))))(keyword_name(arg)) if sx_truthy(((type_of(arg) == 'keyword') if not sx_truthy((type_of(arg) == 'keyword')) else ((get(state, 'i') + 1) < len(args)))) else _sx_begin(_sx_append(children, arg), assoc(state, 'i', (get(state, 'i') + 1))))))(get(state, 'skip')), {'i': 0, 'skip': False}, args)
|
reduce(lambda state, arg: (lambda skip: (assoc(state, 'skip', False, 'i', (get(state, 'i') + 1)) if sx_truthy(skip) else ((lambda kname: (lambda kval: _sx_begin((_sx_cell_set(_cells, 'marsh_id', kval) if sx_truthy((kname == 'id')) else (_sx_cell_set(_cells, 'marsh_tag', kval) if sx_truthy((kname == 'tag')) else (NIL if sx_truthy((kname == 'transform')) else NIL))), assoc(state, 'skip', True, 'i', (get(state, 'i') + 1))))(trampoline(eval_expr(nth(args, (get(state, 'i') + 1)), env))))(keyword_name(arg)) if sx_truthy(((type_of(arg) == 'keyword') if not sx_truthy((type_of(arg) == 'keyword')) else ((get(state, 'i') + 1) < len(args)))) else _sx_begin(_sx_append(children, arg), assoc(state, 'i', (get(state, 'i') + 1))))))(get(state, 'skip')), {'i': 0, 'skip': False}, args)
|
||||||
marsh_attrs = {'data-sx-marsh': (_cells['marsh_id'] if sx_truthy(_cells['marsh_id']) else '')}
|
marsh_attrs = {'data-sx-marsh': (_cells['marsh_id'] if sx_truthy(_cells['marsh_id']) else '')}
|
||||||
content_parts = []
|
provide_push('element-attrs', NIL)
|
||||||
for c in children:
|
content = join('', map(lambda c: render_to_html(c, env), children))
|
||||||
result = render_to_html(c, env)
|
for spread_dict in sx_emitted('element-attrs'):
|
||||||
if sx_truthy(is_spread(result)):
|
merge_spread_attrs(marsh_attrs, spread_dict)
|
||||||
merge_spread_attrs(marsh_attrs, spread_attrs(result))
|
provide_pop('element-attrs')
|
||||||
else:
|
return sx_str('<', _cells['marsh_tag'], render_attrs(marsh_attrs), '>', content, '</', _cells['marsh_tag'], '>')
|
||||||
content_parts.append(result)
|
|
||||||
return sx_str('<', _cells['marsh_tag'], render_attrs(marsh_attrs), '>', join('', content_parts), '</', _cells['marsh_tag'], '>')
|
|
||||||
|
|
||||||
# render-html-island
|
# render-html-island
|
||||||
def render_html_island(island, args, env):
|
def render_html_island(island, args, env):
|
||||||
@@ -2452,12 +2436,7 @@ def render_html_island(island, args, env):
|
|||||||
for p in component_params(island):
|
for p in component_params(island):
|
||||||
local[p] = (dict_get(kwargs, p) if sx_truthy(dict_has(kwargs, p)) else NIL)
|
local[p] = (dict_get(kwargs, p) if sx_truthy(dict_has(kwargs, p)) else NIL)
|
||||||
if sx_truthy(component_has_children(island)):
|
if sx_truthy(component_has_children(island)):
|
||||||
parts = []
|
local['children'] = make_raw_html(join('', map(lambda c: render_to_html(c, env), children)))
|
||||||
for c in children:
|
|
||||||
r = render_to_html(c, env)
|
|
||||||
if sx_truthy((not sx_truthy(is_spread(r)))):
|
|
||||||
parts.append(r)
|
|
||||||
local['children'] = make_raw_html(join('', parts))
|
|
||||||
body_html = render_to_html(component_body(island), local)
|
body_html = render_to_html(component_body(island), local)
|
||||||
state_sx = serialize_island_state(kwargs)
|
state_sx = serialize_island_state(kwargs)
|
||||||
return sx_str('<span data-sx-island="', escape_attr(island_name), '"', (sx_str(' data-sx-state="', escape_attr(state_sx), '"') if sx_truthy(state_sx) else ''), '>', body_html, '</span>')
|
return sx_str('<span data-sx-island="', escape_attr(island_name), '"', (sx_str(' data-sx-state="', escape_attr(state_sx), '"') if sx_truthy(state_sx) else ''), '>', body_html, '</span>')
|
||||||
@@ -2483,40 +2462,12 @@ def render_to_sx(expr, env):
|
|||||||
# aser
|
# aser
|
||||||
def aser(expr, env):
|
def aser(expr, env):
|
||||||
set_render_active_b(True)
|
set_render_active_b(True)
|
||||||
_match = type_of(expr)
|
result = _sx_case(type_of(expr), [('number', lambda: expr), ('string', lambda: expr), ('boolean', lambda: expr), ('nil', lambda: NIL), ('symbol', lambda: (lambda name: (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))))))))(symbol_name(expr))), ('keyword', lambda: keyword_name(expr)), ('list', lambda: ([] if sx_truthy(empty_p(expr)) else aser_list(expr, env))), ('spread', lambda: _sx_begin(sx_emit('element-attrs', spread_attrs(expr)), NIL)), (None, lambda: expr)])
|
||||||
if _match == 'number':
|
if sx_truthy(is_spread(result)):
|
||||||
return expr
|
sx_emit('element-attrs', spread_attrs(result))
|
||||||
elif _match == 'string':
|
|
||||||
return expr
|
|
||||||
elif _match == 'boolean':
|
|
||||||
return expr
|
|
||||||
elif _match == 'nil':
|
|
||||||
return NIL
|
return NIL
|
||||||
elif _match == 'symbol':
|
|
||||||
name = symbol_name(expr)
|
|
||||||
if sx_truthy(env_has(env, name)):
|
|
||||||
return env_get(env, name)
|
|
||||||
elif sx_truthy(is_primitive(name)):
|
|
||||||
return get_primitive(name)
|
|
||||||
elif sx_truthy((name == 'true')):
|
|
||||||
return True
|
|
||||||
elif sx_truthy((name == 'false')):
|
|
||||||
return False
|
|
||||||
elif sx_truthy((name == 'nil')):
|
|
||||||
return NIL
|
|
||||||
else:
|
|
||||||
return error(sx_str('Undefined symbol: ', name))
|
|
||||||
elif _match == 'keyword':
|
|
||||||
return keyword_name(expr)
|
|
||||||
elif _match == 'list':
|
|
||||||
if sx_truthy(empty_p(expr)):
|
|
||||||
return []
|
|
||||||
else:
|
|
||||||
return aser_list(expr, env)
|
|
||||||
elif _match == 'spread':
|
|
||||||
return expr
|
|
||||||
else:
|
else:
|
||||||
return expr
|
return result
|
||||||
|
|
||||||
# aser-list
|
# aser-list
|
||||||
def aser_list(expr, env):
|
def aser_list(expr, env):
|
||||||
@@ -2561,10 +2512,10 @@ def aser_fragment(children, env):
|
|||||||
result = aser(c, env)
|
result = aser(c, env)
|
||||||
if sx_truthy((type_of(result) == 'list')):
|
if sx_truthy((type_of(result) == 'list')):
|
||||||
for item in result:
|
for item in result:
|
||||||
if sx_truthy(((not sx_truthy(is_nil(item))) if not sx_truthy((not sx_truthy(is_nil(item)))) else (not sx_truthy(is_spread(item))))):
|
if sx_truthy((not sx_truthy(is_nil(item)))):
|
||||||
parts.append(serialize(item))
|
parts.append(serialize(item))
|
||||||
else:
|
else:
|
||||||
if sx_truthy(((not sx_truthy(is_nil(result))) if not sx_truthy((not sx_truthy(is_nil(result)))) else (not sx_truthy(is_spread(result))))):
|
if sx_truthy((not sx_truthy(is_nil(result)))):
|
||||||
parts.append(serialize(result))
|
parts.append(serialize(result))
|
||||||
if sx_truthy(empty_p(parts)):
|
if sx_truthy(empty_p(parts)):
|
||||||
return ''
|
return ''
|
||||||
@@ -2574,9 +2525,11 @@ def aser_fragment(children, env):
|
|||||||
# aser-call
|
# aser-call
|
||||||
def aser_call(name, args, env):
|
def aser_call(name, args, env):
|
||||||
_cells = {}
|
_cells = {}
|
||||||
parts = [name]
|
attr_parts = []
|
||||||
|
child_parts = []
|
||||||
_cells['skip'] = False
|
_cells['skip'] = False
|
||||||
_cells['i'] = 0
|
_cells['i'] = 0
|
||||||
|
provide_push('element-attrs', NIL)
|
||||||
for arg in args:
|
for arg in args:
|
||||||
if sx_truthy(_cells['skip']):
|
if sx_truthy(_cells['skip']):
|
||||||
_cells['skip'] = False
|
_cells['skip'] = False
|
||||||
@@ -2585,26 +2538,27 @@ def aser_call(name, args, env):
|
|||||||
if sx_truthy(((type_of(arg) == 'keyword') if not sx_truthy((type_of(arg) == 'keyword')) else ((_cells['i'] + 1) < len(args)))):
|
if sx_truthy(((type_of(arg) == 'keyword') if not sx_truthy((type_of(arg) == 'keyword')) else ((_cells['i'] + 1) < len(args)))):
|
||||||
val = aser(nth(args, (_cells['i'] + 1)), env)
|
val = aser(nth(args, (_cells['i'] + 1)), env)
|
||||||
if sx_truthy((not sx_truthy(is_nil(val)))):
|
if sx_truthy((not sx_truthy(is_nil(val)))):
|
||||||
parts.append(sx_str(':', keyword_name(arg)))
|
attr_parts.append(sx_str(':', keyword_name(arg)))
|
||||||
parts.append(serialize(val))
|
attr_parts.append(serialize(val))
|
||||||
_cells['skip'] = True
|
_cells['skip'] = True
|
||||||
_cells['i'] = (_cells['i'] + 1)
|
_cells['i'] = (_cells['i'] + 1)
|
||||||
else:
|
else:
|
||||||
val = aser(arg, env)
|
val = aser(arg, env)
|
||||||
if sx_truthy((not sx_truthy(is_nil(val)))):
|
if sx_truthy((not sx_truthy(is_nil(val)))):
|
||||||
if sx_truthy(is_spread(val)):
|
if sx_truthy((type_of(val) == 'list')):
|
||||||
for k in keys(spread_attrs(val)):
|
for item in val:
|
||||||
v = dict_get(spread_attrs(val), k)
|
if sx_truthy((not sx_truthy(is_nil(item)))):
|
||||||
parts.append(sx_str(':', k))
|
child_parts.append(serialize(item))
|
||||||
parts.append(serialize(v))
|
|
||||||
else:
|
else:
|
||||||
if sx_truthy((type_of(val) == 'list')):
|
child_parts.append(serialize(val))
|
||||||
for item in val:
|
|
||||||
if sx_truthy((not sx_truthy(is_nil(item)))):
|
|
||||||
parts.append(serialize(item))
|
|
||||||
else:
|
|
||||||
parts.append(serialize(val))
|
|
||||||
_cells['i'] = (_cells['i'] + 1)
|
_cells['i'] = (_cells['i'] + 1)
|
||||||
|
for spread_dict in sx_emitted('element-attrs'):
|
||||||
|
for k in keys(spread_dict):
|
||||||
|
v = dict_get(spread_dict, k)
|
||||||
|
attr_parts.append(sx_str(':', k))
|
||||||
|
attr_parts.append(serialize(v))
|
||||||
|
provide_pop('element-attrs')
|
||||||
|
parts = concat([name], attr_parts, child_parts)
|
||||||
return sx_str('(', join(' ', parts), ')')
|
return sx_str('(', join(' ', parts), ')')
|
||||||
|
|
||||||
# SPECIAL_FORM_NAMES
|
# SPECIAL_FORM_NAMES
|
||||||
@@ -3659,7 +3613,8 @@ async def async_render(expr, env, ctx):
|
|||||||
elif _match == 'raw-html':
|
elif _match == 'raw-html':
|
||||||
return raw_html_content(expr)
|
return raw_html_content(expr)
|
||||||
elif _match == 'spread':
|
elif _match == 'spread':
|
||||||
return expr
|
sx_emit('element-attrs', spread_attrs(expr))
|
||||||
|
return ''
|
||||||
elif _match == 'symbol':
|
elif _match == 'symbol':
|
||||||
val = (await async_eval(expr, env, ctx))
|
val = (await async_eval(expr, env, ctx))
|
||||||
return (await async_render(val, env, ctx))
|
return (await async_render(val, env, ctx))
|
||||||
@@ -3691,7 +3646,7 @@ async def async_render_list(expr, env, ctx):
|
|||||||
elif sx_truthy((name == 'raw!')):
|
elif sx_truthy((name == 'raw!')):
|
||||||
return (await async_render_raw(args, env, ctx))
|
return (await async_render_raw(args, env, ctx))
|
||||||
elif sx_truthy((name == '<>')):
|
elif sx_truthy((name == '<>')):
|
||||||
return join('', filter(lambda r: (not sx_truthy(is_spread(r))), (await async_map_render(args, env, ctx))))
|
return join('', (await async_map_render(args, env, ctx)))
|
||||||
elif sx_truthy(starts_with_p(name, 'html:')):
|
elif sx_truthy(starts_with_p(name, 'html:')):
|
||||||
return (await async_render_element(slice(name, 5), args, env, ctx))
|
return (await async_render_element(slice(name, 5), args, env, ctx))
|
||||||
elif sx_truthy(async_render_form_p(name)):
|
elif sx_truthy(async_render_form_p(name)):
|
||||||
@@ -3746,12 +3701,12 @@ async def async_render_element(tag, args, env, ctx):
|
|||||||
else:
|
else:
|
||||||
token = (svg_context_set(True) if sx_truthy(((tag == 'svg') if sx_truthy((tag == 'svg')) else (tag == 'math'))) else NIL)
|
token = (svg_context_set(True) if sx_truthy(((tag == 'svg') if sx_truthy((tag == 'svg')) else (tag == 'math'))) else NIL)
|
||||||
content_parts = []
|
content_parts = []
|
||||||
|
provide_push('element-attrs', NIL)
|
||||||
for c in children:
|
for c in children:
|
||||||
result = (await async_render(c, env, ctx))
|
content_parts.append((await async_render(c, env, ctx)))
|
||||||
if sx_truthy(is_spread(result)):
|
for spread_dict in sx_emitted('element-attrs'):
|
||||||
merge_spread_attrs(attrs, spread_attrs(result))
|
merge_spread_attrs(attrs, spread_dict)
|
||||||
else:
|
provide_pop('element-attrs')
|
||||||
content_parts.append(result)
|
|
||||||
if sx_truthy(token):
|
if sx_truthy(token):
|
||||||
svg_context_reset(token)
|
svg_context_reset(token)
|
||||||
return sx_str('<', tag, render_attrs(attrs), '>', join('', content_parts), '</', tag, '>')
|
return sx_str('<', tag, render_attrs(attrs), '>', join('', content_parts), '</', tag, '>')
|
||||||
@@ -3787,9 +3742,7 @@ async def async_render_component(comp, args, env, ctx):
|
|||||||
if sx_truthy(component_has_children(comp)):
|
if sx_truthy(component_has_children(comp)):
|
||||||
parts = []
|
parts = []
|
||||||
for c in children:
|
for c in children:
|
||||||
r = (await async_render(c, env, ctx))
|
parts.append((await async_render(c, env, ctx)))
|
||||||
if sx_truthy((not sx_truthy(is_spread(r)))):
|
|
||||||
parts.append(r)
|
|
||||||
local['children'] = make_raw_html(join('', parts))
|
local['children'] = make_raw_html(join('', parts))
|
||||||
return (await async_render(component_body(comp), local, ctx))
|
return (await async_render(component_body(comp), local, ctx))
|
||||||
|
|
||||||
@@ -3805,9 +3758,7 @@ async def async_render_island(island, args, env, ctx):
|
|||||||
if sx_truthy(component_has_children(island)):
|
if sx_truthy(component_has_children(island)):
|
||||||
parts = []
|
parts = []
|
||||||
for c in children:
|
for c in children:
|
||||||
r = (await async_render(c, env, ctx))
|
parts.append((await async_render(c, env, ctx)))
|
||||||
if sx_truthy((not sx_truthy(is_spread(r)))):
|
|
||||||
parts.append(r)
|
|
||||||
local['children'] = make_raw_html(join('', parts))
|
local['children'] = make_raw_html(join('', parts))
|
||||||
body_html = (await async_render(component_body(island), local, ctx))
|
body_html = (await async_render(component_body(island), local, ctx))
|
||||||
state_json = serialize_island_state(kwargs)
|
state_json = serialize_island_state(kwargs)
|
||||||
@@ -3871,8 +3822,7 @@ async def dispatch_async_render_form(name, expr, env, ctx):
|
|||||||
if sx_truthy((len(expr) == 3)):
|
if sx_truthy((len(expr) == 3)):
|
||||||
return (await async_render(nth(expr, 2), env, ctx))
|
return (await async_render(nth(expr, 2), env, ctx))
|
||||||
else:
|
else:
|
||||||
results = (await async_map_render(slice(expr, 2), env, ctx))
|
return join('', (await async_map_render(slice(expr, 2), env, ctx)))
|
||||||
return join('', filter(lambda r: (not sx_truthy(is_spread(r))), results))
|
|
||||||
elif sx_truthy((name == 'cond')):
|
elif sx_truthy((name == 'cond')):
|
||||||
clauses = rest(expr)
|
clauses = rest(expr)
|
||||||
if sx_truthy(cond_scheme_p(clauses)):
|
if sx_truthy(cond_scheme_p(clauses)):
|
||||||
@@ -3886,38 +3836,36 @@ async def dispatch_async_render_form(name, expr, env, ctx):
|
|||||||
if sx_truthy((len(expr) == 3)):
|
if sx_truthy((len(expr) == 3)):
|
||||||
return (await async_render(nth(expr, 2), local, ctx))
|
return (await async_render(nth(expr, 2), local, ctx))
|
||||||
else:
|
else:
|
||||||
results = (await async_map_render(slice(expr, 2), local, ctx))
|
return join('', (await async_map_render(slice(expr, 2), local, ctx)))
|
||||||
return join('', filter(lambda r: (not sx_truthy(is_spread(r))), results))
|
|
||||||
elif sx_truthy(((name == 'begin') if sx_truthy((name == 'begin')) else (name == 'do'))):
|
elif sx_truthy(((name == 'begin') if sx_truthy((name == 'begin')) else (name == 'do'))):
|
||||||
if sx_truthy((len(expr) == 2)):
|
if sx_truthy((len(expr) == 2)):
|
||||||
return (await async_render(nth(expr, 1), env, ctx))
|
return (await async_render(nth(expr, 1), env, ctx))
|
||||||
else:
|
else:
|
||||||
results = (await async_map_render(rest(expr), env, ctx))
|
return join('', (await async_map_render(rest(expr), env, ctx)))
|
||||||
return join('', filter(lambda r: (not sx_truthy(is_spread(r))), results))
|
|
||||||
elif sx_truthy(is_definition_form(name)):
|
elif sx_truthy(is_definition_form(name)):
|
||||||
(await async_eval(expr, env, ctx))
|
(await async_eval(expr, env, ctx))
|
||||||
return ''
|
return ''
|
||||||
elif sx_truthy((name == 'map')):
|
elif sx_truthy((name == 'map')):
|
||||||
f = (await async_eval(nth(expr, 1), env, ctx))
|
f = (await async_eval(nth(expr, 1), env, ctx))
|
||||||
coll = (await async_eval(nth(expr, 2), env, ctx))
|
coll = (await async_eval(nth(expr, 2), env, ctx))
|
||||||
return join('', filter(lambda r: (not sx_truthy(is_spread(r))), (await async_map_fn_render(f, coll, env, ctx))))
|
return join('', (await async_map_fn_render(f, coll, env, ctx)))
|
||||||
elif sx_truthy((name == 'map-indexed')):
|
elif sx_truthy((name == 'map-indexed')):
|
||||||
f = (await async_eval(nth(expr, 1), env, ctx))
|
f = (await async_eval(nth(expr, 1), env, ctx))
|
||||||
coll = (await async_eval(nth(expr, 2), env, ctx))
|
coll = (await async_eval(nth(expr, 2), env, ctx))
|
||||||
return join('', filter(lambda r: (not sx_truthy(is_spread(r))), (await async_map_indexed_fn_render(f, coll, env, ctx))))
|
return join('', (await async_map_indexed_fn_render(f, coll, env, ctx)))
|
||||||
elif sx_truthy((name == 'filter')):
|
elif sx_truthy((name == 'filter')):
|
||||||
return (await async_render((await async_eval(expr, env, ctx)), env, ctx))
|
return (await async_render((await async_eval(expr, env, ctx)), env, ctx))
|
||||||
elif sx_truthy((name == 'for-each')):
|
elif sx_truthy((name == 'for-each')):
|
||||||
f = (await async_eval(nth(expr, 1), env, ctx))
|
f = (await async_eval(nth(expr, 1), env, ctx))
|
||||||
coll = (await async_eval(nth(expr, 2), env, ctx))
|
coll = (await async_eval(nth(expr, 2), env, ctx))
|
||||||
return join('', filter(lambda r: (not sx_truthy(is_spread(r))), (await async_map_fn_render(f, coll, env, ctx))))
|
return join('', (await async_map_fn_render(f, coll, env, ctx)))
|
||||||
elif sx_truthy((name == 'provide')):
|
elif sx_truthy((name == 'provide')):
|
||||||
prov_name = (await async_eval(nth(expr, 1), env, ctx))
|
prov_name = (await async_eval(nth(expr, 1), env, ctx))
|
||||||
prov_val = (await async_eval(nth(expr, 2), env, ctx))
|
prov_val = (await async_eval(nth(expr, 2), env, ctx))
|
||||||
body_start = 3
|
body_start = 3
|
||||||
body_count = (len(expr) - 3)
|
body_count = (len(expr) - 3)
|
||||||
provide_push(prov_name, prov_val)
|
provide_push(prov_name, prov_val)
|
||||||
result = ((await async_render(nth(expr, body_start), env, ctx)) if sx_truthy((body_count == 1)) else (lambda results: join('', filter(lambda r: (not sx_truthy(is_spread(r))), results)))((await async_map_render(slice(expr, body_start), env, ctx))))
|
result = ((await async_render(nth(expr, body_start), env, ctx)) if sx_truthy((body_count == 1)) else join('', (await async_map_render(slice(expr, body_start), env, ctx))))
|
||||||
provide_pop(prov_name)
|
provide_pop(prov_name)
|
||||||
return result
|
return result
|
||||||
else:
|
else:
|
||||||
@@ -4019,42 +3967,35 @@ async def async_invoke(f, *args):
|
|||||||
|
|
||||||
# async-aser
|
# async-aser
|
||||||
async def async_aser(expr, env, ctx):
|
async def async_aser(expr, env, ctx):
|
||||||
_match = type_of(expr)
|
t = type_of(expr)
|
||||||
if _match == 'number':
|
result = NIL
|
||||||
return expr
|
if sx_truthy((t == 'number')):
|
||||||
elif _match == 'string':
|
result = expr
|
||||||
return expr
|
elif sx_truthy((t == 'string')):
|
||||||
elif _match == 'boolean':
|
result = expr
|
||||||
return expr
|
elif sx_truthy((t == 'boolean')):
|
||||||
elif _match == 'nil':
|
result = expr
|
||||||
return NIL
|
elif sx_truthy((t == 'nil')):
|
||||||
elif _match == 'symbol':
|
result = NIL
|
||||||
|
elif sx_truthy((t == 'symbol')):
|
||||||
name = symbol_name(expr)
|
name = symbol_name(expr)
|
||||||
if sx_truthy(env_has(env, name)):
|
result = (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 env_get(env, name)
|
elif sx_truthy((t == 'keyword')):
|
||||||
elif sx_truthy(is_primitive(name)):
|
result = keyword_name(expr)
|
||||||
return get_primitive(name)
|
elif sx_truthy((t == 'dict')):
|
||||||
elif sx_truthy((name == 'true')):
|
result = (await async_aser_dict(expr, env, ctx))
|
||||||
return True
|
elif sx_truthy((t == 'spread')):
|
||||||
elif sx_truthy((name == 'false')):
|
sx_emit('element-attrs', spread_attrs(expr))
|
||||||
return False
|
result = NIL
|
||||||
elif sx_truthy((name == 'nil')):
|
elif sx_truthy((t == 'list')):
|
||||||
return NIL
|
result = ([] if sx_truthy(empty_p(expr)) else (await async_aser_list(expr, env, ctx)))
|
||||||
else:
|
|
||||||
return error(sx_str('Undefined symbol: ', name))
|
|
||||||
elif _match == 'keyword':
|
|
||||||
return keyword_name(expr)
|
|
||||||
elif _match == 'dict':
|
|
||||||
return (await async_aser_dict(expr, env, ctx))
|
|
||||||
elif _match == 'spread':
|
|
||||||
return expr
|
|
||||||
elif _match == 'list':
|
|
||||||
if sx_truthy(empty_p(expr)):
|
|
||||||
return []
|
|
||||||
else:
|
|
||||||
return (await async_aser_list(expr, env, ctx))
|
|
||||||
else:
|
else:
|
||||||
return expr
|
result = expr
|
||||||
|
if sx_truthy(is_spread(result)):
|
||||||
|
sx_emit('element-attrs', spread_attrs(result))
|
||||||
|
return NIL
|
||||||
|
else:
|
||||||
|
return result
|
||||||
|
|
||||||
# async-aser-dict
|
# async-aser-dict
|
||||||
async def async_aser_dict(expr, env, ctx):
|
async def async_aser_dict(expr, env, ctx):
|
||||||
@@ -4148,10 +4089,10 @@ async def async_aser_fragment(children, env, ctx):
|
|||||||
result = (await async_aser(c, env, ctx))
|
result = (await async_aser(c, env, ctx))
|
||||||
if sx_truthy((type_of(result) == 'list')):
|
if sx_truthy((type_of(result) == 'list')):
|
||||||
for item in result:
|
for item in result:
|
||||||
if sx_truthy(((not sx_truthy(is_nil(item))) if not sx_truthy((not sx_truthy(is_nil(item)))) else (not sx_truthy(is_spread(item))))):
|
if sx_truthy((not sx_truthy(is_nil(item)))):
|
||||||
parts.append(serialize(item))
|
parts.append(serialize(item))
|
||||||
else:
|
else:
|
||||||
if sx_truthy(((not sx_truthy(is_nil(result))) if not sx_truthy((not sx_truthy(is_nil(result)))) else (not sx_truthy(is_spread(result))))):
|
if sx_truthy((not sx_truthy(is_nil(result)))):
|
||||||
parts.append(serialize(result))
|
parts.append(serialize(result))
|
||||||
if sx_truthy(empty_p(parts)):
|
if sx_truthy(empty_p(parts)):
|
||||||
return make_sx_expr('')
|
return make_sx_expr('')
|
||||||
@@ -4204,9 +4145,11 @@ async def async_parse_aser_kw_args(args, kwargs, children, env, ctx):
|
|||||||
async def async_aser_call(name, args, env, ctx):
|
async def async_aser_call(name, args, env, ctx):
|
||||||
_cells = {}
|
_cells = {}
|
||||||
token = (svg_context_set(True) if sx_truthy(((name == 'svg') if sx_truthy((name == 'svg')) else (name == 'math'))) else NIL)
|
token = (svg_context_set(True) if sx_truthy(((name == 'svg') if sx_truthy((name == 'svg')) else (name == 'math'))) else NIL)
|
||||||
parts = [name]
|
attr_parts = []
|
||||||
|
child_parts = []
|
||||||
_cells['skip'] = False
|
_cells['skip'] = False
|
||||||
_cells['i'] = 0
|
_cells['i'] = 0
|
||||||
|
provide_push('element-attrs', NIL)
|
||||||
for arg in args:
|
for arg in args:
|
||||||
if sx_truthy(_cells['skip']):
|
if sx_truthy(_cells['skip']):
|
||||||
_cells['skip'] = False
|
_cells['skip'] = False
|
||||||
@@ -4215,39 +4158,40 @@ async def async_aser_call(name, args, env, ctx):
|
|||||||
if sx_truthy(((type_of(arg) == 'keyword') if not sx_truthy((type_of(arg) == 'keyword')) else ((_cells['i'] + 1) < len(args)))):
|
if sx_truthy(((type_of(arg) == 'keyword') if not sx_truthy((type_of(arg) == 'keyword')) else ((_cells['i'] + 1) < len(args)))):
|
||||||
val = (await async_aser(nth(args, (_cells['i'] + 1)), env, ctx))
|
val = (await async_aser(nth(args, (_cells['i'] + 1)), env, ctx))
|
||||||
if sx_truthy((not sx_truthy(is_nil(val)))):
|
if sx_truthy((not sx_truthy(is_nil(val)))):
|
||||||
parts.append(sx_str(':', keyword_name(arg)))
|
attr_parts.append(sx_str(':', keyword_name(arg)))
|
||||||
if sx_truthy((type_of(val) == 'list')):
|
if sx_truthy((type_of(val) == 'list')):
|
||||||
live = filter(lambda v: (not sx_truthy(is_nil(v))), val)
|
live = filter(lambda v: (not sx_truthy(is_nil(v))), val)
|
||||||
if sx_truthy(empty_p(live)):
|
if sx_truthy(empty_p(live)):
|
||||||
parts.append('nil')
|
attr_parts.append('nil')
|
||||||
else:
|
else:
|
||||||
items = map(serialize, live)
|
items = map(serialize, live)
|
||||||
if sx_truthy(some(lambda v: is_sx_expr(v), live)):
|
if sx_truthy(some(lambda v: is_sx_expr(v), live)):
|
||||||
parts.append(sx_str('(<> ', join(' ', items), ')'))
|
attr_parts.append(sx_str('(<> ', join(' ', items), ')'))
|
||||||
else:
|
else:
|
||||||
parts.append(sx_str('(list ', join(' ', items), ')'))
|
attr_parts.append(sx_str('(list ', join(' ', items), ')'))
|
||||||
else:
|
else:
|
||||||
parts.append(serialize(val))
|
attr_parts.append(serialize(val))
|
||||||
_cells['skip'] = True
|
_cells['skip'] = True
|
||||||
_cells['i'] = (_cells['i'] + 1)
|
_cells['i'] = (_cells['i'] + 1)
|
||||||
else:
|
else:
|
||||||
result = (await async_aser(arg, env, ctx))
|
result = (await async_aser(arg, env, ctx))
|
||||||
if sx_truthy((not sx_truthy(is_nil(result)))):
|
if sx_truthy((not sx_truthy(is_nil(result)))):
|
||||||
if sx_truthy(is_spread(result)):
|
if sx_truthy((type_of(result) == 'list')):
|
||||||
for k in keys(spread_attrs(result)):
|
for item in result:
|
||||||
v = dict_get(spread_attrs(result), k)
|
if sx_truthy((not sx_truthy(is_nil(item)))):
|
||||||
parts.append(sx_str(':', k))
|
child_parts.append(serialize(item))
|
||||||
parts.append(serialize(v))
|
|
||||||
else:
|
else:
|
||||||
if sx_truthy((type_of(result) == 'list')):
|
child_parts.append(serialize(result))
|
||||||
for item in result:
|
|
||||||
if sx_truthy((not sx_truthy(is_nil(item)))):
|
|
||||||
parts.append(serialize(item))
|
|
||||||
else:
|
|
||||||
parts.append(serialize(result))
|
|
||||||
_cells['i'] = (_cells['i'] + 1)
|
_cells['i'] = (_cells['i'] + 1)
|
||||||
|
for spread_dict in sx_emitted('element-attrs'):
|
||||||
|
for k in keys(spread_dict):
|
||||||
|
v = dict_get(spread_dict, k)
|
||||||
|
attr_parts.append(sx_str(':', k))
|
||||||
|
attr_parts.append(serialize(v))
|
||||||
|
provide_pop('element-attrs')
|
||||||
if sx_truthy(token):
|
if sx_truthy(token):
|
||||||
svg_context_reset(token)
|
svg_context_reset(token)
|
||||||
|
parts = concat([name], attr_parts, child_parts)
|
||||||
return make_sx_expr(sx_str('(', join(' ', parts), ')'))
|
return make_sx_expr(sx_str('(', join(' ', parts), ')'))
|
||||||
|
|
||||||
# ASYNC_ASER_FORM_NAMES
|
# ASYNC_ASER_FORM_NAMES
|
||||||
|
|||||||
@@ -285,6 +285,19 @@
|
|||||||
(assert-equal "(div :class \"card\" :style \"color:red\" \"hello\")"
|
(assert-equal "(div :class \"card\" :style \"color:red\" \"hello\")"
|
||||||
(render-sx "(div (make-spread {:class \"card\"}) (make-spread {:style \"color:red\"}) \"hello\")")))
|
(render-sx "(div (make-spread {:class \"card\"}) (make-spread {:style \"color:red\"}) \"hello\")")))
|
||||||
|
|
||||||
(deftest "spread in fragment is filtered"
|
(deftest "spread in fragment is silently dropped"
|
||||||
(assert-equal "(<> \"hello\")"
|
(assert-equal "(<> \"hello\")"
|
||||||
(render-sx "(<> (make-spread {:class \"card\"}) \"hello\")"))))
|
(render-sx "(<> (make-spread {:class \"card\"}) \"hello\")")))
|
||||||
|
|
||||||
|
(deftest "stored spread in let binding"
|
||||||
|
(assert-equal "(div :class \"card\" \"hello\")"
|
||||||
|
(render-sx "(let ((card (make-spread {:class \"card\"})))
|
||||||
|
(div card \"hello\"))")))
|
||||||
|
|
||||||
|
(deftest "spread in nested element"
|
||||||
|
(assert-equal "(div (span :class \"inner\" \"hi\"))"
|
||||||
|
(render-sx "(div (span (make-spread {:class \"inner\"}) \"hi\"))")))
|
||||||
|
|
||||||
|
(deftest "spread in non-element context silently drops"
|
||||||
|
(assert-equal "hello"
|
||||||
|
(render-sx "(do (make-spread {:class \"card\"}) \"hello\")"))))
|
||||||
|
|||||||
Reference in New Issue
Block a user