Phase 2: Remove dead tree-walk code from eval.sx
eval.sx: 1272 → 846 lines (-33%). sx-browser.js: 392KB → 377KB. Deleted (superseded by CEK step handlers in cek.sx): - eval-list: tree-walk dispatch table - eval-call: tree-walk function dispatch - sf-if, sf-when, sf-cond (3 variants), sf-case (2 variants) - sf-and, sf-or, sf-let, sf-begin, sf-quote, sf-quasiquote - sf-thread-first, sf-set!, sf-define - ho-map, ho-filter, ho-reduce, ho-some, ho-every, ho-for-each, ho-map-indexed, call-fn Kept (still called by CEK as delegates): - sf-lambda, sf-defcomp, sf-defisland, sf-defmacro, sf-defstyle, sf-deftype, sf-defeffect, sf-letrec, sf-named-let - sf-scope, sf-provide, sf-dynamic-wind - expand-macro, qq-expand, cond-scheme? - call-lambda, call-component, parse-keyword-args - Strict mode, type helpers eval-expr is now a stub overridden by CEK fixup. All tests unchanged: JS 747/747, Full 864/870, Python 679/679. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -14,7 +14,7 @@
|
||||
// =========================================================================
|
||||
|
||||
var NIL = Object.freeze({ _nil: true, toString: function() { return "nil"; } });
|
||||
var SX_VERSION = "2026-03-15T13:02:48Z";
|
||||
var SX_VERSION = "2026-03-15T13:27:20Z";
|
||||
|
||||
function isNil(x) { return x === NIL || x === null || x === undefined; }
|
||||
function isSxTruthy(x) { return x !== false && !isNil(x); }
|
||||
@@ -860,34 +860,9 @@ PRIMITIVES["value-matches-type?"] = valueMatchesType_p;
|
||||
PRIMITIVES["strict-check-args"] = strictCheckArgs;
|
||||
|
||||
// eval-expr
|
||||
var evalExpr = function(expr, env) { 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() {
|
||||
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 : (debugLog("Undefined symbol:", name, "primitive?:", isPrimitive(name)), error((String("Undefined symbol: ") + String(name)))))))));
|
||||
})(); if (_m == "keyword") return keywordName(expr); if (_m == "dict") return mapDict(function(k, v) { return trampoline(evalExpr(v, env)); }, expr); if (_m == "list") return (isSxTruthy(isEmpty(expr)) ? [] : evalList(expr, env)); return expr; })(); };
|
||||
var evalExpr = function(expr, env) { return error("eval-expr: CEK fixup not loaded"); };
|
||||
PRIMITIVES["eval-expr"] = evalExpr;
|
||||
|
||||
// eval-list
|
||||
var evalList = function(expr, env) { return (function() {
|
||||
var head = first(expr);
|
||||
var args = rest(expr);
|
||||
return (isSxTruthy(!isSxTruthy(sxOr((typeOf(head) == "symbol"), (typeOf(head) == "lambda"), (typeOf(head) == "list")))) ? map(function(x) { return trampoline(evalExpr(x, env)); }, expr) : (isSxTruthy((typeOf(head) == "symbol")) ? (function() {
|
||||
var name = symbolName(head);
|
||||
return (isSxTruthy((name == "if")) ? sfIf(args, env) : (isSxTruthy((name == "when")) ? sfWhen(args, env) : (isSxTruthy((name == "cond")) ? sfCond(args, env) : (isSxTruthy((name == "case")) ? sfCase(args, env) : (isSxTruthy((name == "and")) ? sfAnd(args, env) : (isSxTruthy((name == "or")) ? sfOr(args, env) : (isSxTruthy((name == "let")) ? sfLet(args, env) : (isSxTruthy((name == "let*")) ? sfLet(args, env) : (isSxTruthy((name == "letrec")) ? sfLetrec(args, env) : (isSxTruthy((name == "lambda")) ? sfLambda(args, env) : (isSxTruthy((name == "fn")) ? sfLambda(args, env) : (isSxTruthy((name == "define")) ? sfDefine(args, env) : (isSxTruthy((name == "defcomp")) ? sfDefcomp(args, env) : (isSxTruthy((name == "defisland")) ? sfDefisland(args, env) : (isSxTruthy((name == "defmacro")) ? sfDefmacro(args, env) : (isSxTruthy((name == "defstyle")) ? sfDefstyle(args, env) : (isSxTruthy((name == "defhandler")) ? sfDefhandler(args, env) : (isSxTruthy((name == "defpage")) ? sfDefpage(args, env) : (isSxTruthy((name == "defquery")) ? sfDefquery(args, env) : (isSxTruthy((name == "defaction")) ? sfDefaction(args, env) : (isSxTruthy((name == "deftype")) ? sfDeftype(args, env) : (isSxTruthy((name == "defeffect")) ? sfDefeffect(args, env) : (isSxTruthy((name == "begin")) ? sfBegin(args, env) : (isSxTruthy((name == "do")) ? sfBegin(args, env) : (isSxTruthy((name == "quote")) ? sfQuote(args, env) : (isSxTruthy((name == "quasiquote")) ? sfQuasiquote(args, env) : (isSxTruthy((name == "->")) ? sfThreadFirst(args, env) : (isSxTruthy((name == "set!")) ? sfSetBang(args, env) : (isSxTruthy((name == "reset")) ? sfReset(args, env) : (isSxTruthy((name == "shift")) ? sfShift(args, env) : (isSxTruthy((name == "dynamic-wind")) ? sfDynamicWind(args, env) : (isSxTruthy((name == "scope")) ? sfScope(args, env) : (isSxTruthy((name == "provide")) ? sfProvide(args, env) : (isSxTruthy((name == "map")) ? hoMap(args, env) : (isSxTruthy((name == "map-indexed")) ? hoMapIndexed(args, env) : (isSxTruthy((name == "filter")) ? hoFilter(args, env) : (isSxTruthy((name == "reduce")) ? hoReduce(args, env) : (isSxTruthy((name == "some")) ? hoSome(args, env) : (isSxTruthy((name == "every?")) ? hoEvery(args, env) : (isSxTruthy((name == "for-each")) ? hoForEach(args, env) : (isSxTruthy((isSxTruthy(envHas(env, name)) && isMacro(envGet(env, name)))) ? (function() {
|
||||
var mac = envGet(env, name);
|
||||
return makeThunk(expandMacro(mac, args, env), env);
|
||||
})() : (isSxTruthy((isSxTruthy(renderActiveP()) && isRenderExpr(expr))) ? renderExpr(expr, env) : evalCall(head, args, env)))))))))))))))))))))))))))))))))))))))))));
|
||||
})() : evalCall(head, args, env)));
|
||||
})(); };
|
||||
PRIMITIVES["eval-list"] = evalList;
|
||||
|
||||
// eval-call
|
||||
var evalCall = function(head, args, env) { return (function() {
|
||||
var f = trampoline(evalExpr(head, env));
|
||||
var evaluatedArgs = map(function(a) { return trampoline(evalExpr(a, env)); }, args);
|
||||
return (isSxTruthy((isSxTruthy(isCallable(f)) && isSxTruthy(!isSxTruthy(isLambda(f))) && isSxTruthy(!isSxTruthy(isComponent(f))) && !isSxTruthy(isIsland(f)))) ? ((isSxTruthy((isSxTruthy(_strict_) && (typeOf(head) == "symbol"))) ? strictCheckArgs(symbolName(head), evaluatedArgs) : NIL), apply(f, evaluatedArgs)) : (isSxTruthy(isLambda(f)) ? callLambda(f, evaluatedArgs, env) : (isSxTruthy(isComponent(f)) ? callComponent(f, args, env) : (isSxTruthy(isIsland(f)) ? callComponent(f, args, env) : error((String("Not callable: ") + String(inspect(f))))))));
|
||||
})(); };
|
||||
PRIMITIVES["eval-call"] = evalCall;
|
||||
|
||||
// call-lambda
|
||||
var callLambda = function(f, args, callerEnv) { return (function() {
|
||||
var params = lambdaParams(f);
|
||||
@@ -924,96 +899,10 @@ PRIMITIVES["call-component"] = callComponent;
|
||||
})(); };
|
||||
PRIMITIVES["parse-keyword-args"] = parseKeywordArgs;
|
||||
|
||||
// sf-if
|
||||
var sfIf = function(args, env) { return (function() {
|
||||
var condition = trampoline(evalExpr(first(args), env));
|
||||
return (isSxTruthy((isSxTruthy(condition) && !isSxTruthy(isNil(condition)))) ? makeThunk(nth(args, 1), env) : (isSxTruthy((len(args) > 2)) ? makeThunk(nth(args, 2), env) : NIL));
|
||||
})(); };
|
||||
PRIMITIVES["sf-if"] = sfIf;
|
||||
|
||||
// sf-when
|
||||
var sfWhen = function(args, env) { return (function() {
|
||||
var condition = trampoline(evalExpr(first(args), env));
|
||||
return (isSxTruthy((isSxTruthy(condition) && !isSxTruthy(isNil(condition)))) ? (forEach(function(e) { return trampoline(evalExpr(e, env)); }, slice(args, 1, (len(args) - 1))), makeThunk(last(args), env)) : NIL);
|
||||
})(); };
|
||||
PRIMITIVES["sf-when"] = sfWhen;
|
||||
|
||||
// cond-scheme?
|
||||
var condScheme_p = function(clauses) { return isEvery(function(c) { return (isSxTruthy((typeOf(c) == "list")) && (len(c) == 2)); }, clauses); };
|
||||
PRIMITIVES["cond-scheme?"] = condScheme_p;
|
||||
|
||||
// sf-cond
|
||||
var sfCond = function(args, env) { return (isSxTruthy(condScheme_p(args)) ? sfCondScheme(args, env) : sfCondClojure(args, env)); };
|
||||
PRIMITIVES["sf-cond"] = sfCond;
|
||||
|
||||
// sf-cond-scheme
|
||||
var sfCondScheme = function(clauses, env) { return (isSxTruthy(isEmpty(clauses)) ? NIL : (function() {
|
||||
var clause = first(clauses);
|
||||
var test = first(clause);
|
||||
var body = nth(clause, 1);
|
||||
return (isSxTruthy(sxOr((isSxTruthy((typeOf(test) == "symbol")) && sxOr((symbolName(test) == "else"), (symbolName(test) == ":else"))), (isSxTruthy((typeOf(test) == "keyword")) && (keywordName(test) == "else")))) ? makeThunk(body, env) : (isSxTruthy(trampoline(evalExpr(test, env))) ? makeThunk(body, env) : sfCondScheme(rest(clauses), env)));
|
||||
})()); };
|
||||
PRIMITIVES["sf-cond-scheme"] = sfCondScheme;
|
||||
|
||||
// sf-cond-clojure
|
||||
var sfCondClojure = function(clauses, env) { return (isSxTruthy((len(clauses) < 2)) ? NIL : (function() {
|
||||
var test = first(clauses);
|
||||
var body = nth(clauses, 1);
|
||||
return (isSxTruthy(sxOr((isSxTruthy((typeOf(test) == "keyword")) && (keywordName(test) == "else")), (isSxTruthy((typeOf(test) == "symbol")) && sxOr((symbolName(test) == "else"), (symbolName(test) == ":else"))))) ? makeThunk(body, env) : (isSxTruthy(trampoline(evalExpr(test, env))) ? makeThunk(body, env) : sfCondClojure(slice(clauses, 2), env)));
|
||||
})()); };
|
||||
PRIMITIVES["sf-cond-clojure"] = sfCondClojure;
|
||||
|
||||
// sf-case
|
||||
var sfCase = function(args, env) { return (function() {
|
||||
var matchVal = trampoline(evalExpr(first(args), env));
|
||||
var clauses = rest(args);
|
||||
return sfCaseLoop(matchVal, clauses, env);
|
||||
})(); };
|
||||
PRIMITIVES["sf-case"] = sfCase;
|
||||
|
||||
// sf-case-loop
|
||||
var sfCaseLoop = function(matchVal, clauses, env) { return (isSxTruthy((len(clauses) < 2)) ? NIL : (function() {
|
||||
var test = first(clauses);
|
||||
var body = nth(clauses, 1);
|
||||
return (isSxTruthy(sxOr((isSxTruthy((typeOf(test) == "keyword")) && (keywordName(test) == "else")), (isSxTruthy((typeOf(test) == "symbol")) && sxOr((symbolName(test) == "else"), (symbolName(test) == ":else"))))) ? makeThunk(body, env) : (isSxTruthy((matchVal == trampoline(evalExpr(test, env)))) ? makeThunk(body, env) : sfCaseLoop(matchVal, slice(clauses, 2), env)));
|
||||
})()); };
|
||||
PRIMITIVES["sf-case-loop"] = sfCaseLoop;
|
||||
|
||||
// sf-and
|
||||
var sfAnd = function(args, env) { return (isSxTruthy(isEmpty(args)) ? true : (function() {
|
||||
var val = trampoline(evalExpr(first(args), env));
|
||||
return (isSxTruthy(!isSxTruthy(val)) ? val : (isSxTruthy((len(args) == 1)) ? val : sfAnd(rest(args), env)));
|
||||
})()); };
|
||||
PRIMITIVES["sf-and"] = sfAnd;
|
||||
|
||||
// sf-or
|
||||
var sfOr = function(args, env) { return (isSxTruthy(isEmpty(args)) ? false : (function() {
|
||||
var val = trampoline(evalExpr(first(args), env));
|
||||
return (isSxTruthy(val) ? val : sfOr(rest(args), env));
|
||||
})()); };
|
||||
PRIMITIVES["sf-or"] = sfOr;
|
||||
|
||||
// sf-let
|
||||
var sfLet = function(args, env) { return (isSxTruthy((typeOf(first(args)) == "symbol")) ? sfNamedLet(args, env) : (function() {
|
||||
var bindings = first(args);
|
||||
var body = rest(args);
|
||||
var local = envExtend(env);
|
||||
(isSxTruthy((isSxTruthy((typeOf(first(bindings)) == "list")) && (len(first(bindings)) == 2))) ? forEach(function(binding) { return (function() {
|
||||
var vname = (isSxTruthy((typeOf(first(binding)) == "symbol")) ? symbolName(first(binding)) : first(binding));
|
||||
return envBind(local, vname, trampoline(evalExpr(nth(binding, 1), local)));
|
||||
})(); }, bindings) : (function() {
|
||||
var i = 0;
|
||||
return reduce(function(acc, pairIdx) { return (function() {
|
||||
var vname = (isSxTruthy((typeOf(nth(bindings, (pairIdx * 2))) == "symbol")) ? symbolName(nth(bindings, (pairIdx * 2))) : nth(bindings, (pairIdx * 2)));
|
||||
var valExpr = nth(bindings, ((pairIdx * 2) + 1));
|
||||
return envBind(local, vname, trampoline(evalExpr(valExpr, local)));
|
||||
})(); }, NIL, range(0, (len(bindings) / 2)));
|
||||
})());
|
||||
{ var _c = slice(body, 0, (len(body) - 1)); for (var _i = 0; _i < _c.length; _i++) { var e = _c[_i]; trampoline(evalExpr(e, local)); } }
|
||||
return makeThunk(last(body), local);
|
||||
})()); };
|
||||
PRIMITIVES["sf-let"] = sfLet;
|
||||
|
||||
// sf-named-let
|
||||
var sfNamedLet = function(args, env) { return (function() {
|
||||
var loopName = symbolName(first(args));
|
||||
@@ -1046,29 +935,6 @@ PRIMITIVES["sf-named-let"] = sfNamedLet;
|
||||
})(); };
|
||||
PRIMITIVES["sf-lambda"] = sfLambda;
|
||||
|
||||
// sf-define
|
||||
var sfDefine = function(args, env) { return (function() {
|
||||
var nameSym = first(args);
|
||||
var hasEffects = (isSxTruthy((len(args) >= 4)) && isSxTruthy((typeOf(nth(args, 1)) == "keyword")) && (keywordName(nth(args, 1)) == "effects"));
|
||||
var valIdx = (isSxTruthy((isSxTruthy((len(args) >= 4)) && isSxTruthy((typeOf(nth(args, 1)) == "keyword")) && (keywordName(nth(args, 1)) == "effects"))) ? 3 : 1);
|
||||
var value = trampoline(evalExpr(nth(args, valIdx), env));
|
||||
if (isSxTruthy((isSxTruthy(isLambda(value)) && isNil(lambdaName(value))))) {
|
||||
value.name = symbolName(nameSym);
|
||||
}
|
||||
envBind(env, symbolName(nameSym), value);
|
||||
if (isSxTruthy(hasEffects)) {
|
||||
(function() {
|
||||
var effectsRaw = nth(args, 2);
|
||||
var effectList = (isSxTruthy((typeOf(effectsRaw) == "list")) ? map(function(e) { return (isSxTruthy((typeOf(e) == "symbol")) ? symbolName(e) : (String(e))); }, effectsRaw) : [(String(effectsRaw))]);
|
||||
var effectAnns = (isSxTruthy(envHas(env, "*effect-annotations*")) ? envGet(env, "*effect-annotations*") : {});
|
||||
effectAnns[symbolName(nameSym)] = effectList;
|
||||
return envBind(env, "*effect-annotations*", effectAnns);
|
||||
})();
|
||||
}
|
||||
return value;
|
||||
})(); };
|
||||
PRIMITIVES["sf-define"] = sfDefine;
|
||||
|
||||
// sf-defcomp
|
||||
var sfDefcomp = function(args, env) { return (function() {
|
||||
var nameSym = first(args);
|
||||
@@ -1229,18 +1095,6 @@ PRIMITIVES["sf-deftype"] = sfDeftype;
|
||||
})(); };
|
||||
PRIMITIVES["sf-defeffect"] = sfDefeffect;
|
||||
|
||||
// sf-begin
|
||||
var sfBegin = function(args, env) { return (isSxTruthy(isEmpty(args)) ? NIL : (forEach(function(e) { return trampoline(evalExpr(e, env)); }, slice(args, 0, (len(args) - 1))), makeThunk(last(args), env))); };
|
||||
PRIMITIVES["sf-begin"] = sfBegin;
|
||||
|
||||
// sf-quote
|
||||
var sfQuote = function(args, env) { return (isSxTruthy(isEmpty(args)) ? NIL : first(args)); };
|
||||
PRIMITIVES["sf-quote"] = sfQuote;
|
||||
|
||||
// sf-quasiquote
|
||||
var sfQuasiquote = function(args, env) { return qqExpand(first(args), env); };
|
||||
PRIMITIVES["sf-quasiquote"] = sfQuasiquote;
|
||||
|
||||
// qq-expand
|
||||
var qqExpand = function(template, env) { return (isSxTruthy(!isSxTruthy((typeOf(template) == "list"))) ? template : (isSxTruthy(isEmpty(template)) ? [] : (function() {
|
||||
var head = first(template);
|
||||
@@ -1251,30 +1105,6 @@ PRIMITIVES["sf-quasiquote"] = sfQuasiquote;
|
||||
})())); };
|
||||
PRIMITIVES["qq-expand"] = qqExpand;
|
||||
|
||||
// sf-thread-first
|
||||
var sfThreadFirst = function(args, env) { return (function() {
|
||||
var val = trampoline(evalExpr(first(args), env));
|
||||
return reduce(function(result, form) { return (isSxTruthy((typeOf(form) == "list")) ? (function() {
|
||||
var f = trampoline(evalExpr(first(form), env));
|
||||
var restArgs = map(function(a) { return trampoline(evalExpr(a, env)); }, rest(form));
|
||||
var allArgs = cons(result, restArgs);
|
||||
return (isSxTruthy((isSxTruthy(isCallable(f)) && !isSxTruthy(isLambda(f)))) ? apply(f, allArgs) : (isSxTruthy(isLambda(f)) ? trampoline(callLambda(f, allArgs, env)) : error((String("-> form not callable: ") + String(inspect(f))))));
|
||||
})() : (function() {
|
||||
var f = trampoline(evalExpr(form, env));
|
||||
return (isSxTruthy((isSxTruthy(isCallable(f)) && !isSxTruthy(isLambda(f)))) ? f(result) : (isSxTruthy(isLambda(f)) ? trampoline(callLambda(f, [result], env)) : error((String("-> form not callable: ") + String(inspect(f))))));
|
||||
})()); }, val, rest(args));
|
||||
})(); };
|
||||
PRIMITIVES["sf-thread-first"] = sfThreadFirst;
|
||||
|
||||
// sf-set!
|
||||
var sfSetBang = function(args, env) { return (function() {
|
||||
var name = symbolName(first(args));
|
||||
var value = trampoline(evalExpr(nth(args, 1), env));
|
||||
envSet(env, name, value);
|
||||
return value;
|
||||
})(); };
|
||||
PRIMITIVES["sf-set!"] = sfSetBang;
|
||||
|
||||
// sf-letrec
|
||||
var sfLetrec = function(args, env) { return (function() {
|
||||
var bindings = first(args);
|
||||
@@ -1354,67 +1184,6 @@ PRIMITIVES["sf-provide"] = sfProvide;
|
||||
})(); };
|
||||
PRIMITIVES["expand-macro"] = expandMacro;
|
||||
|
||||
// call-fn
|
||||
var callFn = function(f, args, env) { return (isSxTruthy(isLambda(f)) ? trampoline(callLambda(f, args, env)) : (isSxTruthy(isCallable(f)) ? apply(f, args) : error((String("Not callable in HO form: ") + String(inspect(f)))))); };
|
||||
PRIMITIVES["call-fn"] = callFn;
|
||||
|
||||
// ho-map
|
||||
var hoMap = function(args, env) { return (function() {
|
||||
var f = trampoline(evalExpr(first(args), env));
|
||||
var coll = trampoline(evalExpr(nth(args, 1), env));
|
||||
return map(function(item) { return callFn(f, [item], env); }, coll);
|
||||
})(); };
|
||||
PRIMITIVES["ho-map"] = hoMap;
|
||||
|
||||
// ho-map-indexed
|
||||
var hoMapIndexed = function(args, env) { return (function() {
|
||||
var f = trampoline(evalExpr(first(args), env));
|
||||
var coll = trampoline(evalExpr(nth(args, 1), env));
|
||||
return mapIndexed(function(i, item) { return callFn(f, [i, item], env); }, coll);
|
||||
})(); };
|
||||
PRIMITIVES["ho-map-indexed"] = hoMapIndexed;
|
||||
|
||||
// ho-filter
|
||||
var hoFilter = function(args, env) { return (function() {
|
||||
var f = trampoline(evalExpr(first(args), env));
|
||||
var coll = trampoline(evalExpr(nth(args, 1), env));
|
||||
return filter(function(item) { return callFn(f, [item], env); }, coll);
|
||||
})(); };
|
||||
PRIMITIVES["ho-filter"] = hoFilter;
|
||||
|
||||
// ho-reduce
|
||||
var hoReduce = function(args, env) { return (function() {
|
||||
var f = trampoline(evalExpr(first(args), env));
|
||||
var init = trampoline(evalExpr(nth(args, 1), env));
|
||||
var coll = trampoline(evalExpr(nth(args, 2), env));
|
||||
return reduce(function(acc, item) { return callFn(f, [acc, item], env); }, init, coll);
|
||||
})(); };
|
||||
PRIMITIVES["ho-reduce"] = hoReduce;
|
||||
|
||||
// ho-some
|
||||
var hoSome = function(args, env) { return (function() {
|
||||
var f = trampoline(evalExpr(first(args), env));
|
||||
var coll = trampoline(evalExpr(nth(args, 1), env));
|
||||
return some(function(item) { return callFn(f, [item], env); }, coll);
|
||||
})(); };
|
||||
PRIMITIVES["ho-some"] = hoSome;
|
||||
|
||||
// ho-every
|
||||
var hoEvery = function(args, env) { return (function() {
|
||||
var f = trampoline(evalExpr(first(args), env));
|
||||
var coll = trampoline(evalExpr(nth(args, 1), env));
|
||||
return isEvery(function(item) { return callFn(f, [item], env); }, coll);
|
||||
})(); };
|
||||
PRIMITIVES["ho-every"] = hoEvery;
|
||||
|
||||
// ho-for-each
|
||||
var hoForEach = function(args, env) { return (function() {
|
||||
var f = trampoline(evalExpr(first(args), env));
|
||||
var coll = trampoline(evalExpr(nth(args, 1), env));
|
||||
return forEach(function(item) { return callFn(f, [item], env); }, coll);
|
||||
})(); };
|
||||
PRIMITIVES["ho-for-each"] = hoForEach;
|
||||
|
||||
|
||||
// === Transpiled from render (core) ===
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -20,7 +20,7 @@
|
||||
:class "hidden md:flex md:flex-col max-w-xs md:h-full md:min-h-0 mr-3"
|
||||
(when aside aside))
|
||||
(section :id "main-panel"
|
||||
:class "flex-1 md:h-full md:min-h-0 md:overflow-y-auto md:overscroll-contain js-grid-viewport"
|
||||
:class "flex-1 md:h-full md:min-h-0 md:overflow-y-auto md:overscroll-contain overflow-x-hidden js-grid-viewport"
|
||||
(when content content)
|
||||
(div :class "pb-8")))))))
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
(div :id "root-menu" :sx-swap-oob "outerHTML" :class "md:hidden"
|
||||
(when menu menu))
|
||||
(section :id "main-panel"
|
||||
:class "flex-1 md:h-full md:min-h-0 md:overflow-y-auto md:overscroll-contain js-grid-viewport"
|
||||
:class "flex-1 md:h-full md:min-h-0 md:overflow-y-auto md:overscroll-contain overflow-x-hidden js-grid-viewport"
|
||||
(when content content))))
|
||||
|
||||
(defcomp ~shared:layout/hamburger ()
|
||||
|
||||
458
spec/eval.sx
458
spec/eval.sx
@@ -152,158 +152,29 @@
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; 3. Core evaluator
|
||||
;; 3. Core evaluator — stub (overridden by CEK in fixups)
|
||||
;; --------------------------------------------------------------------------
|
||||
;;
|
||||
;; eval-expr and trampoline are defined as stubs here so the transpiler
|
||||
;; creates the variable declarations. The CEK fixups override them with:
|
||||
;; eval-expr = (expr, env) → cek-run(make-cek-state(expr, env, []))
|
||||
;; trampoline = (val) → if thunk? then eval-expr(thunk-expr, thunk-env) else val
|
||||
;; All evaluation goes through the CEK machine.
|
||||
|
||||
(define eval-expr
|
||||
(fn (expr (env :as dict))
|
||||
(case (type-of expr)
|
||||
|
||||
;; --- literals pass through ---
|
||||
"number" expr
|
||||
"string" expr
|
||||
"boolean" expr
|
||||
"nil" nil
|
||||
|
||||
;; --- symbol lookup ---
|
||||
"symbol"
|
||||
(let ((name (symbol-name expr)))
|
||||
(cond
|
||||
(env-has? env name) (env-get env name)
|
||||
(primitive? name) (get-primitive name)
|
||||
(= name "true") true
|
||||
(= name "false") false
|
||||
(= name "nil") nil
|
||||
:else (do (debug-log "Undefined symbol:" name "primitive?:" (primitive? name))
|
||||
(error (str "Undefined symbol: " name)))))
|
||||
|
||||
;; --- keyword → its string name ---
|
||||
"keyword" (keyword-name expr)
|
||||
|
||||
;; --- dict literal ---
|
||||
"dict"
|
||||
(map-dict (fn (k v) (trampoline (eval-expr v env))) expr)
|
||||
|
||||
;; --- list = call or special form ---
|
||||
"list"
|
||||
(if (empty? expr)
|
||||
(list)
|
||||
(eval-list expr env))
|
||||
|
||||
;; --- anything else passes through ---
|
||||
:else expr)))
|
||||
;; Stub — overridden by CEK fixup before any code runs.
|
||||
;; If this executes, CEK fixup failed to load.
|
||||
(error "eval-expr: CEK fixup not loaded")))
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; 4. List evaluation — dispatch on head
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
(define eval-list
|
||||
(fn (expr (env :as dict))
|
||||
(let ((head (first expr))
|
||||
(args (rest expr)))
|
||||
|
||||
;; If head isn't a symbol, lambda, or list → treat as data list
|
||||
(if (not (or (= (type-of head) "symbol")
|
||||
(= (type-of head) "lambda")
|
||||
(= (type-of head) "list")))
|
||||
(map (fn (x) (trampoline (eval-expr x env))) expr)
|
||||
|
||||
;; Head is a symbol — check special forms, then function call
|
||||
(if (= (type-of head) "symbol")
|
||||
(let ((name (symbol-name head)))
|
||||
(cond
|
||||
;; Special forms
|
||||
(= name "if") (sf-if args env)
|
||||
(= name "when") (sf-when args env)
|
||||
(= name "cond") (sf-cond args env)
|
||||
(= name "case") (sf-case args env)
|
||||
(= name "and") (sf-and args env)
|
||||
(= name "or") (sf-or args env)
|
||||
(= name "let") (sf-let args env)
|
||||
(= name "let*") (sf-let args env)
|
||||
(= name "letrec") (sf-letrec args env)
|
||||
(= name "lambda") (sf-lambda args env)
|
||||
(= name "fn") (sf-lambda args env)
|
||||
(= name "define") (sf-define args env)
|
||||
(= name "defcomp") (sf-defcomp args env)
|
||||
(= name "defisland") (sf-defisland args env)
|
||||
(= name "defmacro") (sf-defmacro args env)
|
||||
(= name "defstyle") (sf-defstyle args env)
|
||||
(= name "defhandler") (sf-defhandler args env)
|
||||
(= name "defpage") (sf-defpage args env)
|
||||
(= name "defquery") (sf-defquery args env)
|
||||
(= name "defaction") (sf-defaction args env)
|
||||
(= name "deftype") (sf-deftype args env)
|
||||
(= name "defeffect") (sf-defeffect args env)
|
||||
(= name "begin") (sf-begin args env)
|
||||
(= name "do") (sf-begin args env)
|
||||
(= name "quote") (sf-quote args env)
|
||||
(= name "quasiquote") (sf-quasiquote args env)
|
||||
(= name "->") (sf-thread-first args env)
|
||||
(= name "set!") (sf-set! args env)
|
||||
(= name "reset") (sf-reset args env)
|
||||
(= name "shift") (sf-shift args env)
|
||||
(= name "dynamic-wind") (sf-dynamic-wind args env)
|
||||
(= name "scope") (sf-scope args env)
|
||||
(= name "provide") (sf-provide args env)
|
||||
|
||||
;; Higher-order forms
|
||||
(= name "map") (ho-map args env)
|
||||
(= name "map-indexed") (ho-map-indexed args env)
|
||||
(= name "filter") (ho-filter args env)
|
||||
(= name "reduce") (ho-reduce args env)
|
||||
(= name "some") (ho-some args env)
|
||||
(= name "every?") (ho-every args env)
|
||||
(= name "for-each") (ho-for-each args env)
|
||||
|
||||
;; Macro expansion
|
||||
(and (env-has? env name) (macro? (env-get env name)))
|
||||
(let ((mac (env-get env name)))
|
||||
(make-thunk (expand-macro mac args env) env))
|
||||
|
||||
;; Render expression — delegate to active adapter (only when rendering).
|
||||
(and (render-active?) (is-render-expr? expr))
|
||||
(render-expr expr env)
|
||||
|
||||
;; Fall through to function call
|
||||
:else (eval-call head args env)))
|
||||
|
||||
;; Head is lambda or list — evaluate as function call
|
||||
(eval-call head args env))))))
|
||||
;; [REMOVED] Section 4: Tree-walk eval-list dispatch table — superseded by CEK step-eval-list
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; 5. Function / lambda / component call
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
(define eval-call
|
||||
(fn (head (args :as list) (env :as dict))
|
||||
(let ((f (trampoline (eval-expr head env)))
|
||||
(evaluated-args (map (fn (a) (trampoline (eval-expr a env))) args)))
|
||||
(cond
|
||||
;; Native callable (primitive function)
|
||||
(and (callable? f) (not (lambda? f)) (not (component? f)) (not (island? f)))
|
||||
(do
|
||||
;; Strict mode: check arg types before dispatch
|
||||
(when (and *strict* (= (type-of head) "symbol"))
|
||||
(strict-check-args (symbol-name head) evaluated-args))
|
||||
(apply f evaluated-args))
|
||||
|
||||
;; Lambda
|
||||
(lambda? f)
|
||||
(call-lambda f evaluated-args env)
|
||||
|
||||
;; Component
|
||||
(component? f)
|
||||
(call-component f args env)
|
||||
|
||||
;; Island (reactive component) — same calling convention
|
||||
(island? f)
|
||||
(call-component f args env)
|
||||
|
||||
:else (error (str "Not callable: " (inspect f)))))))
|
||||
|
||||
;; [REMOVED] eval-call — superseded by CEK continue-with-call
|
||||
|
||||
(define call-lambda
|
||||
(fn ((f :as lambda) (args :as list) (caller-env :as dict))
|
||||
@@ -376,159 +247,16 @@
|
||||
;; --------------------------------------------------------------------------
|
||||
;; 6. Special forms
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
(define sf-if
|
||||
(fn ((args :as list) (env :as dict))
|
||||
(let ((condition (trampoline (eval-expr (first args) env))))
|
||||
(if (and condition (not (nil? condition)))
|
||||
(make-thunk (nth args 1) env)
|
||||
(if (> (len args) 2)
|
||||
(make-thunk (nth args 2) env)
|
||||
nil)))))
|
||||
;; [REMOVED] sf-if, sf-when, sf-cond, sf-case, sf-and, sf-or, sf-let
|
||||
;; — all superseded by CEK step handlers in cek.sx
|
||||
|
||||
|
||||
(define sf-when
|
||||
(fn ((args :as list) (env :as dict))
|
||||
(let ((condition (trampoline (eval-expr (first args) env))))
|
||||
(if (and condition (not (nil? condition)))
|
||||
(do
|
||||
;; Evaluate all but last for side effects
|
||||
(for-each
|
||||
(fn (e) (trampoline (eval-expr e env)))
|
||||
(slice args 1 (dec (len args))))
|
||||
;; Last is tail position
|
||||
(make-thunk (last args) env))
|
||||
nil))))
|
||||
|
||||
|
||||
;; cond-scheme? — check if ALL clauses are 2-element lists (scheme-style).
|
||||
;; Checking only the first arg is ambiguous — (nil? x) is a 2-element
|
||||
;; function call, not a scheme clause ((test body)).
|
||||
;; cond-scheme? — still needed by CEK's step-sf-cond
|
||||
(define cond-scheme?
|
||||
(fn ((clauses :as list))
|
||||
(every? (fn (c) (and (= (type-of c) "list") (= (len c) 2)))
|
||||
clauses)))
|
||||
|
||||
(define sf-cond
|
||||
(fn ((args :as list) (env :as dict))
|
||||
(if (cond-scheme? args)
|
||||
(sf-cond-scheme args env)
|
||||
(sf-cond-clojure args env))))
|
||||
|
||||
(define sf-cond-scheme
|
||||
(fn ((clauses :as list) (env :as dict))
|
||||
(if (empty? clauses)
|
||||
nil
|
||||
(let ((clause (first clauses))
|
||||
(test (first clause))
|
||||
(body (nth clause 1)))
|
||||
(if (or (and (= (type-of test) "symbol")
|
||||
(or (= (symbol-name test) "else")
|
||||
(= (symbol-name test) ":else")))
|
||||
(and (= (type-of test) "keyword")
|
||||
(= (keyword-name test) "else")))
|
||||
(make-thunk body env)
|
||||
(if (trampoline (eval-expr test env))
|
||||
(make-thunk body env)
|
||||
(sf-cond-scheme (rest clauses) env)))))))
|
||||
|
||||
(define sf-cond-clojure
|
||||
(fn ((clauses :as list) (env :as dict))
|
||||
(if (< (len clauses) 2)
|
||||
nil
|
||||
(let ((test (first clauses))
|
||||
(body (nth clauses 1)))
|
||||
(if (or (and (= (type-of test) "keyword") (= (keyword-name test) "else"))
|
||||
(and (= (type-of test) "symbol")
|
||||
(or (= (symbol-name test) "else")
|
||||
(= (symbol-name test) ":else"))))
|
||||
(make-thunk body env)
|
||||
(if (trampoline (eval-expr test env))
|
||||
(make-thunk body env)
|
||||
(sf-cond-clojure (slice clauses 2) env)))))))
|
||||
|
||||
|
||||
(define sf-case
|
||||
(fn ((args :as list) (env :as dict))
|
||||
(let ((match-val (trampoline (eval-expr (first args) env)))
|
||||
(clauses (rest args)))
|
||||
(sf-case-loop match-val clauses env))))
|
||||
|
||||
(define sf-case-loop
|
||||
(fn (match-val (clauses :as list) (env :as dict))
|
||||
(if (< (len clauses) 2)
|
||||
nil
|
||||
(let ((test (first clauses))
|
||||
(body (nth clauses 1)))
|
||||
(if (or (and (= (type-of test) "keyword") (= (keyword-name test) "else"))
|
||||
(and (= (type-of test) "symbol")
|
||||
(or (= (symbol-name test) "else")
|
||||
(= (symbol-name test) ":else"))))
|
||||
(make-thunk body env)
|
||||
(if (= match-val (trampoline (eval-expr test env)))
|
||||
(make-thunk body env)
|
||||
(sf-case-loop match-val (slice clauses 2) env)))))))
|
||||
|
||||
|
||||
(define sf-and
|
||||
(fn ((args :as list) (env :as dict))
|
||||
(if (empty? args)
|
||||
true
|
||||
(let ((val (trampoline (eval-expr (first args) env))))
|
||||
(if (not val)
|
||||
val
|
||||
(if (= (len args) 1)
|
||||
val
|
||||
(sf-and (rest args) env)))))))
|
||||
|
||||
|
||||
(define sf-or
|
||||
(fn ((args :as list) (env :as dict))
|
||||
(if (empty? args)
|
||||
false
|
||||
(let ((val (trampoline (eval-expr (first args) env))))
|
||||
(if val
|
||||
val
|
||||
(sf-or (rest args) env))))))
|
||||
|
||||
|
||||
(define sf-let
|
||||
(fn ((args :as list) (env :as dict))
|
||||
;; Detect named let: (let name ((x 0) ...) body)
|
||||
;; If first arg is a symbol, delegate to sf-named-let.
|
||||
(if (= (type-of (first args)) "symbol")
|
||||
(sf-named-let args env)
|
||||
(let ((bindings (first args))
|
||||
(body (rest args))
|
||||
(local (env-extend env)))
|
||||
;; Parse bindings — support both ((name val) ...) and (name val name val ...)
|
||||
(if (and (= (type-of (first bindings)) "list")
|
||||
(= (len (first bindings)) 2))
|
||||
;; Scheme-style
|
||||
(for-each
|
||||
(fn (binding)
|
||||
(let ((vname (if (= (type-of (first binding)) "symbol")
|
||||
(symbol-name (first binding))
|
||||
(first binding))))
|
||||
(env-bind! local vname (trampoline (eval-expr (nth binding 1) local)))))
|
||||
bindings)
|
||||
;; Clojure-style
|
||||
(let ((i 0))
|
||||
(reduce
|
||||
(fn (acc pair-idx)
|
||||
(let ((vname (if (= (type-of (nth bindings (* pair-idx 2))) "symbol")
|
||||
(symbol-name (nth bindings (* pair-idx 2)))
|
||||
(nth bindings (* pair-idx 2))))
|
||||
(val-expr (nth bindings (inc (* pair-idx 2)))))
|
||||
(env-bind! local vname (trampoline (eval-expr val-expr local)))))
|
||||
nil
|
||||
(range 0 (/ (len bindings) 2)))))
|
||||
;; Evaluate body — last expression in tail position
|
||||
(for-each
|
||||
(fn (e) (trampoline (eval-expr e local)))
|
||||
(slice body 0 (dec (len body))))
|
||||
(make-thunk (last body) local)))))
|
||||
|
||||
|
||||
;; Named let: (let name ((x 0) (y 1)) body...)
|
||||
;; Desugars to a self-recursive lambda called with initial values.
|
||||
@@ -595,37 +323,6 @@
|
||||
(make-lambda param-names body env))))
|
||||
|
||||
|
||||
(define sf-define
|
||||
(fn ((args :as list) (env :as dict))
|
||||
;; Detect :effects keyword: (define name :effects [...] value)
|
||||
(let ((name-sym (first args))
|
||||
(has-effects (and (>= (len args) 4)
|
||||
(= (type-of (nth args 1)) "keyword")
|
||||
(= (keyword-name (nth args 1)) "effects")))
|
||||
(val-idx (if (and (>= (len args) 4)
|
||||
(= (type-of (nth args 1)) "keyword")
|
||||
(= (keyword-name (nth args 1)) "effects"))
|
||||
3 1))
|
||||
(value (trampoline (eval-expr (nth args val-idx) env))))
|
||||
(when (and (lambda? value) (nil? (lambda-name value)))
|
||||
(set-lambda-name! value (symbol-name name-sym)))
|
||||
(env-bind! env (symbol-name name-sym) value)
|
||||
;; Store effect annotation if declared
|
||||
(when has-effects
|
||||
(let ((effects-raw (nth args 2))
|
||||
(effect-list (if (= (type-of effects-raw) "list")
|
||||
(map (fn (e) (if (= (type-of e) "symbol")
|
||||
(symbol-name e) (str e)))
|
||||
effects-raw)
|
||||
(list (str effects-raw))))
|
||||
(effect-anns (if (env-has? env "*effect-annotations*")
|
||||
(env-get env "*effect-annotations*")
|
||||
(dict))))
|
||||
(dict-set! effect-anns (symbol-name name-sym) effect-list)
|
||||
(env-bind! env "*effect-annotations*" effect-anns)))
|
||||
value)))
|
||||
|
||||
|
||||
(define sf-defcomp
|
||||
(fn ((args :as list) (env :as dict))
|
||||
;; (defcomp ~name (params) [:affinity :client|:server] body)
|
||||
@@ -855,26 +552,6 @@
|
||||
nil)))
|
||||
|
||||
|
||||
(define sf-begin
|
||||
(fn ((args :as list) (env :as dict))
|
||||
(if (empty? args)
|
||||
nil
|
||||
(do
|
||||
(for-each
|
||||
(fn (e) (trampoline (eval-expr e env)))
|
||||
(slice args 0 (dec (len args))))
|
||||
(make-thunk (last args) env)))))
|
||||
|
||||
|
||||
(define sf-quote
|
||||
(fn ((args :as list) (env :as dict))
|
||||
(if (empty? args) nil (first args))))
|
||||
|
||||
|
||||
(define sf-quasiquote
|
||||
(fn ((args :as list) (env :as dict))
|
||||
(qq-expand (first args) env)))
|
||||
|
||||
(define qq-expand
|
||||
(fn (template (env :as dict))
|
||||
(if (not (= (type-of template) "list"))
|
||||
@@ -900,41 +577,6 @@
|
||||
template)))))))
|
||||
|
||||
|
||||
(define sf-thread-first
|
||||
(fn ((args :as list) (env :as dict))
|
||||
(let ((val (trampoline (eval-expr (first args) env))))
|
||||
(reduce
|
||||
(fn (result form)
|
||||
(if (= (type-of form) "list")
|
||||
(let ((f (trampoline (eval-expr (first form) env)))
|
||||
(rest-args (map (fn (a) (trampoline (eval-expr a env)))
|
||||
(rest form)))
|
||||
(all-args (cons result rest-args)))
|
||||
(cond
|
||||
(and (callable? f) (not (lambda? f)))
|
||||
(apply f all-args)
|
||||
(lambda? f)
|
||||
(trampoline (call-lambda f all-args env))
|
||||
:else (error (str "-> form not callable: " (inspect f)))))
|
||||
(let ((f (trampoline (eval-expr form env))))
|
||||
(cond
|
||||
(and (callable? f) (not (lambda? f)))
|
||||
(f result)
|
||||
(lambda? f)
|
||||
(trampoline (call-lambda f (list result) env))
|
||||
:else (error (str "-> form not callable: " (inspect f)))))))
|
||||
val
|
||||
(rest args)))))
|
||||
|
||||
|
||||
(define sf-set!
|
||||
(fn ((args :as list) (env :as dict))
|
||||
(let ((name (symbol-name (first args)))
|
||||
(value (trampoline (eval-expr (nth args 1) env))))
|
||||
(env-set! env name value)
|
||||
value)))
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; 6c. letrec — mutually recursive local bindings
|
||||
;; --------------------------------------------------------------------------
|
||||
@@ -1098,75 +740,7 @@
|
||||
(trampoline (eval-expr (macro-body mac) local)))))
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; 7. Higher-order forms
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
;; call-fn: unified caller for HO forms — handles both Lambda and native callable
|
||||
(define call-fn
|
||||
(fn (f (args :as list) (env :as dict))
|
||||
(cond
|
||||
(lambda? f) (trampoline (call-lambda f args env))
|
||||
(callable? f) (apply f args)
|
||||
:else (error (str "Not callable in HO form: " (inspect f))))))
|
||||
|
||||
(define ho-map
|
||||
(fn ((args :as list) (env :as dict))
|
||||
(let ((f (trampoline (eval-expr (first args) env)))
|
||||
(coll (trampoline (eval-expr (nth args 1) env))))
|
||||
(map (fn (item) (call-fn f (list item) env)) coll))))
|
||||
|
||||
(define ho-map-indexed
|
||||
(fn ((args :as list) (env :as dict))
|
||||
(let ((f (trampoline (eval-expr (first args) env)))
|
||||
(coll (trampoline (eval-expr (nth args 1) env))))
|
||||
(map-indexed
|
||||
(fn (i item) (call-fn f (list i item) env))
|
||||
coll))))
|
||||
|
||||
(define ho-filter
|
||||
(fn ((args :as list) (env :as dict))
|
||||
(let ((f (trampoline (eval-expr (first args) env)))
|
||||
(coll (trampoline (eval-expr (nth args 1) env))))
|
||||
(filter
|
||||
(fn (item) (call-fn f (list item) env))
|
||||
coll))))
|
||||
|
||||
(define ho-reduce
|
||||
(fn ((args :as list) (env :as dict))
|
||||
(let ((f (trampoline (eval-expr (first args) env)))
|
||||
(init (trampoline (eval-expr (nth args 1) env)))
|
||||
(coll (trampoline (eval-expr (nth args 2) env))))
|
||||
(reduce
|
||||
(fn (acc item) (call-fn f (list acc item) env))
|
||||
init
|
||||
coll))))
|
||||
|
||||
(define ho-some
|
||||
(fn ((args :as list) (env :as dict))
|
||||
(let ((f (trampoline (eval-expr (first args) env)))
|
||||
(coll (trampoline (eval-expr (nth args 1) env))))
|
||||
(some
|
||||
(fn (item) (call-fn f (list item) env))
|
||||
coll))))
|
||||
|
||||
(define ho-every
|
||||
(fn ((args :as list) (env :as dict))
|
||||
(let ((f (trampoline (eval-expr (first args) env)))
|
||||
(coll (trampoline (eval-expr (nth args 1) env))))
|
||||
(every?
|
||||
(fn (item) (call-fn f (list item) env))
|
||||
coll))))
|
||||
|
||||
|
||||
(define ho-for-each
|
||||
(fn ((args :as list) (env :as dict))
|
||||
(let ((f (trampoline (eval-expr (first args) env)))
|
||||
(coll (trampoline (eval-expr (nth args 1) env))))
|
||||
(for-each
|
||||
(fn (item) (call-fn f (list item) env))
|
||||
coll))))
|
||||
|
||||
;; [REMOVED] Section 7: Tree-walk HO forms — superseded by CEK step-ho-* in cek.sx
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; 8. Primitives — pure functions available in all targets
|
||||
|
||||
@@ -70,20 +70,20 @@
|
||||
(next-idx (mod (+ idx 1) count))
|
||||
(prev-node (nth sibs prev-idx))
|
||||
(next-node (nth sibs next-idx)))
|
||||
(div :class "max-w-3xl mx-auto px-4 py-2 grid grid-cols-3 items-center"
|
||||
(div :class "w-full max-w-3xl mx-auto px-4 py-2 grid grid-cols-3 items-center"
|
||||
:style (str "opacity:" row-opacity ";transition:opacity 0.3s;")
|
||||
(a :href (get prev-node "href")
|
||||
:sx-get (get prev-node "href") :sx-target "#main-panel"
|
||||
:sx-select "#main-panel" :sx-swap "outerHTML"
|
||||
:sx-push-url "true"
|
||||
:class "text-right"
|
||||
:class "text-right min-w-0 truncate"
|
||||
:style (tw "text-stone-500 text-sm")
|
||||
(str "← " (get prev-node "label")))
|
||||
(str "\u2190 " (get prev-node "label")))
|
||||
(a :href (get node "href")
|
||||
:sx-get (get node "href") :sx-target "#main-panel"
|
||||
:sx-select "#main-panel" :sx-swap "outerHTML"
|
||||
:sx-push-url "true"
|
||||
:class "text-center px-4"
|
||||
:class "text-center min-w-0 truncate px-1"
|
||||
:style (if is-leaf
|
||||
(tw "text-violet-700 text-2xl font-bold")
|
||||
(tw "text-violet-700 text-lg font-semibold"))
|
||||
@@ -92,9 +92,9 @@
|
||||
:sx-get (get next-node "href") :sx-target "#main-panel"
|
||||
:sx-select "#main-panel" :sx-swap "outerHTML"
|
||||
:sx-push-url "true"
|
||||
:class "text-left"
|
||||
:class "text-left min-w-0 truncate"
|
||||
:style (tw "text-stone-500 text-sm")
|
||||
(str (get next-node "label") " →")))))))
|
||||
(str (get next-node "label") " \u2192")))))))
|
||||
|
||||
;; Children links — shown as clearly clickable buttons.
|
||||
(defcomp ~layouts/nav-children (&key items)
|
||||
|
||||
Reference in New Issue
Block a user