Spec URL evaluation in router.sx, bootstrap to Python/JS

Add url-to-expr, auto-quote-unknowns, prepare-url-expr to router.sx —
the canonical URL-to-expression pipeline. Dots→spaces, parse, then
auto-quote unknown symbols as strings (slugs). The same spec serves
both server (Python) and client (JS) route handling.

- router.sx: three new pure functions for URL evaluation
- bootstrap_py.py: auto-include router module with html adapter
- platform_js.py: export urlToExpr/autoQuoteUnknowns/prepareUrlExpr
- sx_router.py: replace hand-written auto_quote_slugs with bootstrapped
  prepare_url_expr — delete ~50 lines of hardcoded function name sets
- Rebootstrap sx_ref.py (4331 lines) and sx-browser.js

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-12 23:05:01 +00:00
parent 355f57a60b
commit 5f06e2e2cc
6 changed files with 546 additions and 150 deletions

View File

@@ -14,7 +14,7 @@
// =========================================================================
var NIL = Object.freeze({ _nil: true, toString: function() { return "nil"; } });
var SX_VERSION = "2026-03-12T22:27:08Z";
var SX_VERSION = "2026-03-12T22:55:39Z";
function isNil(x) { return x === NIL || x === null || x === undefined; }
function isSxTruthy(x) { return x !== false && !isNil(x); }
@@ -3922,6 +3922,30 @@ callExpr.push(dictGet(kwargs, k)); } }
return (isSxTruthy((get(parsed, "type") == "special-form")) ? get(parsed, "inner") : NIL);
})(); };
// url-to-expr
var urlToExpr = function(urlPath) { return (isSxTruthy(sxOr((urlPath == "/"), isEmpty(urlPath))) ? [] : (function() {
var trimmed = (isSxTruthy(startsWith(urlPath, "/")) ? slice(urlPath, 1) : urlPath);
return (function() {
var sxSource = replace_(trimmed, ".", " ");
return (function() {
var exprs = sxParse(sxSource);
return (isSxTruthy(isEmpty(exprs)) ? [] : first(exprs));
})();
})();
})()); };
// auto-quote-unknowns
var autoQuoteUnknowns = function(expr, env) { return (isSxTruthy(!isSxTruthy(isList(expr))) ? expr : (isSxTruthy(isEmpty(expr)) ? expr : cons(first(expr), map(function(child) { return (isSxTruthy(isList(child)) ? autoQuoteUnknowns(child, env) : (isSxTruthy((typeOf(child) == "symbol")) ? (function() {
var name = symbolName(child);
return (isSxTruthy(sxOr(envHas(env, name), startsWith(name, ":"), startsWith(name, "~"), startsWith(name, "!"))) ? child : name);
})() : child)); }, rest(expr))))); };
// prepare-url-expr
var prepareUrlExpr = function(urlPath, env) { return (function() {
var expr = urlToExpr(urlPath);
return (isSxTruthy(isEmpty(expr)) ? expr : autoQuoteUnknowns(expr, env));
})(); };
// === Transpiled from signals (reactive signal runtime) ===
@@ -6300,6 +6324,9 @@ return (isSxTruthy((_batchDepth == 0)) ? (function() {
parseRoutePattern: parseRoutePattern,
matchRoute: matchRoute,
findMatchingRoute: findMatchingRoute,
urlToExpr: urlToExpr,
autoQuoteUnknowns: autoQuoteUnknowns,
prepareUrlExpr: prepareUrlExpr,
registerIo: typeof registerIoPrimitive === "function" ? registerIoPrimitive : null,
registerIoDeps: typeof registerIoDeps === "function" ? registerIoDeps : null,
asyncRender: typeof asyncSxRenderWithEnv === "function" ? asyncSxRenderWithEnv : null,