SX URL algebra: relative resolution, keyword ops, ! special forms
Extends router.sx with the full SX URL algebra — structural navigation (.slug, .., ...), keyword set/delta (.:page.4, .:page.+1), bare-dot shorthand, and ! special form parsing (!source, !inspect, !diff, !search, !raw, !json). All pure SX spec, bootstrapped to both Python and JS. Fixes: index-of -1/nil portability (_index-of-safe wrapper), variadic (+ a b c) transpilation bug (use nested binary +). Includes 115 passing tests covering all operations. Also: "The" strapline and essay title. 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 SX_VERSION = "2026-03-12T10:26:23Z";
|
||||
var SX_VERSION = "2026-03-12T18:28:35Z";
|
||||
|
||||
function isNil(x) { return x === NIL || x === null || x === undefined; }
|
||||
function isSxTruthy(x) { return x !== false && !isNil(x); }
|
||||
@@ -3739,6 +3739,189 @@ callExpr.push(dictGet(kwargs, k)); } }
|
||||
})();
|
||||
})()); };
|
||||
|
||||
// _count-leading-dots
|
||||
var _countLeadingDots = function(s) { return (isSxTruthy(isEmpty(s)) ? 0 : (isSxTruthy(startsWith(s, ".")) ? (1 + _countLeadingDots(slice(s, 1))) : 0)); };
|
||||
|
||||
// _strip-trailing-close
|
||||
var _stripTrailingClose = function(s) { return (isSxTruthy(endsWith(s, ")")) ? _stripTrailingClose(slice(s, 0, (len(s) - 1))) : s); };
|
||||
|
||||
// _index-of-safe
|
||||
var _indexOfSafe = function(s, needle) { return (function() {
|
||||
var idx = indexOf_(s, needle);
|
||||
return (isSxTruthy(sxOr(isNil(idx), (idx < 0))) ? NIL : idx);
|
||||
})(); };
|
||||
|
||||
// _last-index-of
|
||||
var _lastIndexOf = function(s, needle) { return (function() {
|
||||
var idx = _indexOfSafe(s, needle);
|
||||
return (isSxTruthy(isNil(idx)) ? NIL : (function() {
|
||||
var restIdx = _lastIndexOf(slice(s, (idx + 1)), needle);
|
||||
return (isSxTruthy(isNil(restIdx)) ? idx : ((idx + 1) + restIdx));
|
||||
})());
|
||||
})(); };
|
||||
|
||||
// _pop-sx-url-level
|
||||
var _popSxUrlLevel = function(url) { return (function() {
|
||||
var stripped = _stripTrailingClose(url);
|
||||
var closeCount = (len(url) - len(_stripTrailingClose(url)));
|
||||
return (isSxTruthy((closeCount <= 1)) ? "/" : (function() {
|
||||
var lastDp = _lastIndexOf(stripped, ".(");
|
||||
return (isSxTruthy(isNil(lastDp)) ? "/" : (String(slice(stripped, 0, lastDp)) + String(slice(url, (len(url) - (closeCount - 1))))));
|
||||
})());
|
||||
})(); };
|
||||
|
||||
// _pop-sx-url-levels
|
||||
var _popSxUrlLevels = function(url, n) { return (isSxTruthy((n <= 0)) ? url : _popSxUrlLevels(_popSxUrlLevel(url), (n - 1))); };
|
||||
|
||||
// _split-pos-kw
|
||||
var _splitPosKw = function(tokens, i, pos, kw) { return (isSxTruthy((i >= len(tokens))) ? {"positional": join(".", pos), "keywords": kw} : (function() {
|
||||
var tok = nth(tokens, i);
|
||||
return (isSxTruthy(startsWith(tok, ":")) ? (function() {
|
||||
var val = (isSxTruthy(((i + 1) < len(tokens))) ? nth(tokens, (i + 1)) : "");
|
||||
return _splitPosKw(tokens, (i + 2), pos, append(kw, [[tok, val]]));
|
||||
})() : _splitPosKw(tokens, (i + 1), append(pos, [tok]), kw));
|
||||
})()); };
|
||||
|
||||
// _parse-relative-body
|
||||
var _parseRelativeBody = function(body) { return (isSxTruthy(isEmpty(body)) ? {"positional": "", "keywords": []} : _splitPosKw(split(body, "."), 0, [], [])); };
|
||||
|
||||
// _extract-innermost
|
||||
var _extractInnermost = function(url) { return (function() {
|
||||
var stripped = _stripTrailingClose(url);
|
||||
var suffix = slice(url, len(_stripTrailingClose(url)));
|
||||
return (function() {
|
||||
var lastDp = _lastIndexOf(stripped, ".(");
|
||||
return (isSxTruthy(isNil(lastDp)) ? {"before": "/(", "content": slice(stripped, 2), "suffix": suffix} : {"before": slice(stripped, 0, (lastDp + 2)), "content": slice(stripped, (lastDp + 2)), "suffix": suffix});
|
||||
})();
|
||||
})(); };
|
||||
|
||||
// _find-kw-in-tokens
|
||||
var _findKwInTokens = function(tokens, i, kw) { return (isSxTruthy((i >= len(tokens))) ? NIL : (isSxTruthy((isSxTruthy((nth(tokens, i) == kw)) && ((i + 1) < len(tokens)))) ? nth(tokens, (i + 1)) : _findKwInTokens(tokens, (i + 1), kw))); };
|
||||
|
||||
// _find-keyword-value
|
||||
var _findKeywordValue = function(content, kw) { return _findKwInTokens(split(content, "."), 0, kw); };
|
||||
|
||||
// _replace-kw-in-tokens
|
||||
var _replaceKwInTokens = function(tokens, i, kw, value) { return (isSxTruthy((i >= len(tokens))) ? [] : (isSxTruthy((isSxTruthy((nth(tokens, i) == kw)) && ((i + 1) < len(tokens)))) ? append([kw, value], _replaceKwInTokens(tokens, (i + 2), kw, value)) : cons(nth(tokens, i), _replaceKwInTokens(tokens, (i + 1), kw, value)))); };
|
||||
|
||||
// _set-keyword-in-content
|
||||
var _setKeywordInContent = function(content, kw, value) { return (function() {
|
||||
var current = _findKeywordValue(content, kw);
|
||||
return (isSxTruthy(isNil(current)) ? (String(content) + String(".") + String(kw) + String(".") + String(value)) : join(".", _replaceKwInTokens(split(content, "."), 0, kw, value)));
|
||||
})(); };
|
||||
|
||||
// _is-delta-value?
|
||||
var _isDeltaValue_p = function(s) { return (isSxTruthy(!isSxTruthy(isEmpty(s))) && isSxTruthy((len(s) > 1)) && sxOr(startsWith(s, "+"), startsWith(s, "-"))); };
|
||||
|
||||
// _apply-delta
|
||||
var _applyDelta = function(currentStr, deltaStr) { return (function() {
|
||||
var cur = parseInt_(currentStr, NIL);
|
||||
var delta = parseInt_(deltaStr, NIL);
|
||||
return (isSxTruthy(sxOr(isNil(cur), isNil(delta))) ? deltaStr : (String((cur + delta))));
|
||||
})(); };
|
||||
|
||||
// _apply-kw-pairs
|
||||
var _applyKwPairs = function(content, kwPairs) { return (isSxTruthy(isEmpty(kwPairs)) ? content : (function() {
|
||||
var pair = first(kwPairs);
|
||||
var kw = first(pair);
|
||||
var rawVal = nth(pair, 1);
|
||||
return (function() {
|
||||
var actualVal = (isSxTruthy(_isDeltaValue_p(rawVal)) ? (function() {
|
||||
var current = _findKeywordValue(content, kw);
|
||||
return (isSxTruthy(isNil(current)) ? rawVal : _applyDelta(current, rawVal));
|
||||
})() : rawVal);
|
||||
return _applyKwPairs(_setKeywordInContent(content, kw, actualVal), rest(kwPairs));
|
||||
})();
|
||||
})()); };
|
||||
|
||||
// _apply-keywords-to-url
|
||||
var _applyKeywordsToUrl = function(url, kwPairs) { return (isSxTruthy(isEmpty(kwPairs)) ? url : (function() {
|
||||
var parts = _extractInnermost(url);
|
||||
return (function() {
|
||||
var newContent = _applyKwPairs(get(parts, "content"), kwPairs);
|
||||
return (String(get(parts, "before")) + String(newContent) + String(get(parts, "suffix")));
|
||||
})();
|
||||
})()); };
|
||||
|
||||
// _normalize-relative
|
||||
var _normalizeRelative = function(url) { return (isSxTruthy(startsWith(url, "(")) ? url : (String("(") + String(url) + String(")"))); };
|
||||
|
||||
// resolve-relative-url
|
||||
var resolveRelativeUrl = function(current, relative) { return (function() {
|
||||
var canonical = _normalizeRelative(relative);
|
||||
return (function() {
|
||||
var relInner = slice(canonical, 1, (len(canonical) - 1));
|
||||
return (function() {
|
||||
var dots = _countLeadingDots(relInner);
|
||||
var body = slice(relInner, _countLeadingDots(relInner));
|
||||
return (isSxTruthy((dots == 0)) ? current : (function() {
|
||||
var parsed = _parseRelativeBody(body);
|
||||
var posBody = get(parsed, "positional");
|
||||
var kwPairs = get(parsed, "keywords");
|
||||
return (function() {
|
||||
var afterNav = (isSxTruthy((dots == 1)) ? (isSxTruthy(isEmpty(posBody)) ? current : (function() {
|
||||
var stripped = _stripTrailingClose(current);
|
||||
var suffix = slice(current, len(_stripTrailingClose(current)));
|
||||
return (String(stripped) + String(".") + String(posBody) + String(suffix));
|
||||
})()) : (function() {
|
||||
var base = _popSxUrlLevels(current, (dots - 1));
|
||||
return (isSxTruthy(isEmpty(posBody)) ? base : (isSxTruthy((base == "/")) ? (String("/(") + String(posBody) + String(")")) : (function() {
|
||||
var stripped = _stripTrailingClose(base);
|
||||
var suffix = slice(base, len(_stripTrailingClose(base)));
|
||||
return (String(stripped) + String(".(") + String(posBody) + String(")") + String(suffix));
|
||||
})()));
|
||||
})());
|
||||
return _applyKeywordsToUrl(afterNav, kwPairs);
|
||||
})();
|
||||
})());
|
||||
})();
|
||||
})();
|
||||
})(); };
|
||||
|
||||
// relative-sx-url?
|
||||
var relativeSxUrl_p = function(url) { return sxOr((isSxTruthy(startsWith(url, "(")) && !isSxTruthy(startsWith(url, "/("))), startsWith(url, ".")); };
|
||||
|
||||
// _url-special-forms
|
||||
var _urlSpecialForms = function() { return ["!source", "!inspect", "!diff", "!search", "!raw", "!json"]; };
|
||||
|
||||
// url-special-form?
|
||||
var urlSpecialForm_p = function(name) { return (isSxTruthy(startsWith(name, "!")) && contains(_urlSpecialForms(), name)); };
|
||||
|
||||
// parse-sx-url
|
||||
var parseSxUrl = function(url) { return (isSxTruthy((url == "/")) ? {"type": "home", "raw": url} : (isSxTruthy(relativeSxUrl_p(url)) ? {"type": "relative", "raw": url} : (isSxTruthy((isSxTruthy(startsWith(url, "/(!")) && endsWith(url, ")"))) ? (function() {
|
||||
var inner = slice(url, 2, (len(url) - 1));
|
||||
return (function() {
|
||||
var dotPos = _indexOfSafe(inner, ".");
|
||||
var parenPos = _indexOfSafe(inner, "(");
|
||||
return (function() {
|
||||
var endPos = (isSxTruthy((isSxTruthy(isNil(dotPos)) && isNil(parenPos))) ? len(inner) : (isSxTruthy(isNil(dotPos)) ? parenPos : (isSxTruthy(isNil(parenPos)) ? dotPos : min(dotPos, parenPos))));
|
||||
return (function() {
|
||||
var formName = slice(inner, 0, endPos);
|
||||
var restPart = slice(inner, endPos);
|
||||
return (function() {
|
||||
var innerExpr = (isSxTruthy(startsWith(restPart, ".")) ? slice(restPart, 1) : restPart);
|
||||
return {"type": "special-form", "form": formName, "inner": innerExpr, "raw": url};
|
||||
})();
|
||||
})();
|
||||
})();
|
||||
})();
|
||||
})() : (isSxTruthy((isSxTruthy(startsWith(url, "/(~")) && endsWith(url, ")"))) ? (function() {
|
||||
var name = slice(url, 2, (len(url) - 1));
|
||||
return {"type": "direct-component", "name": name, "raw": url};
|
||||
})() : (isSxTruthy((isSxTruthy(startsWith(url, "/(")) && endsWith(url, ")"))) ? {"type": "absolute", "raw": url} : {"type": "path", "raw": url}))))); };
|
||||
|
||||
// url-special-form-name
|
||||
var urlSpecialFormName = function(url) { return (function() {
|
||||
var parsed = parseSxUrl(url);
|
||||
return (isSxTruthy((get(parsed, "type") == "special-form")) ? get(parsed, "form") : NIL);
|
||||
})(); };
|
||||
|
||||
// url-special-form-inner
|
||||
var urlSpecialFormInner = function(url) { return (function() {
|
||||
var parsed = parseSxUrl(url);
|
||||
return (isSxTruthy((get(parsed, "type") == "special-form")) ? get(parsed, "inner") : NIL);
|
||||
})(); };
|
||||
|
||||
|
||||
// === Transpiled from signals (reactive signal runtime) ===
|
||||
|
||||
|
||||
Reference in New Issue
Block a user