Phase 7a: affinity annotations + fix parser escape sequences
Add :affinity :client/:server/:auto annotations to defcomp, with render-target function combining affinity + IO analysis. Includes spec (eval.sx, deps.sx), tests, Python evaluator, and demo page. Fix critical bug: Python SX parser _ESCAPE_MAP was missing \r and \0, causing bootstrapped JS parser to treat 'r' as whitespace — breaking all client-side SX parsing. Also add \0 to JS string emitter and fix serializer round-tripping for \r and \0. Reserved word escaping: bootstrappers now auto-append _ to identifiers colliding with JS/Python reserved words (e.g. default → default_, final → final_), so the spec never needs to avoid host language keywords. 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-07T21:45:27Z";
|
||||
var SX_VERSION = "2026-03-07T23:47:29Z";
|
||||
|
||||
function isNil(x) { return x === NIL || x === null || x === undefined; }
|
||||
function isSxTruthy(x) { return x !== false && !isNil(x); }
|
||||
@@ -35,12 +35,13 @@
|
||||
}
|
||||
Lambda.prototype._lambda = true;
|
||||
|
||||
function Component(name, params, hasChildren, body, closure) {
|
||||
function Component(name, params, hasChildren, body, closure, affinity) {
|
||||
this.name = name;
|
||||
this.params = params;
|
||||
this.hasChildren = hasChildren;
|
||||
this.body = body;
|
||||
this.closure = closure || {};
|
||||
this.affinity = affinity || "auto";
|
||||
}
|
||||
Component.prototype._component = true;
|
||||
|
||||
@@ -116,8 +117,8 @@
|
||||
function makeKeyword(n) { return new Keyword(n); }
|
||||
|
||||
function makeLambda(params, body, env) { return new Lambda(params, body, merge(env)); }
|
||||
function makeComponent(name, params, hasChildren, body, env) {
|
||||
return new Component(name, params, hasChildren, body, merge(env));
|
||||
function makeComponent(name, params, hasChildren, body, env, affinity) {
|
||||
return new Component(name, params, hasChildren, body, merge(env), affinity);
|
||||
}
|
||||
function makeMacro(params, restParam, body, env, name) {
|
||||
return new Macro(params, restParam, body, merge(env), name);
|
||||
@@ -135,6 +136,7 @@
|
||||
function componentClosure(c) { return c.closure; }
|
||||
function componentHasChildren(c) { return c.hasChildren; }
|
||||
function componentName(c) { return c.name; }
|
||||
function componentAffinity(c) { return c.affinity || "auto"; }
|
||||
|
||||
function macroParams(m) { return m.params; }
|
||||
function macroRestParam(m) { return m.restParam; }
|
||||
@@ -771,18 +773,32 @@ return append_b(inits, nth(binding, 1)); }, bindings) : reduce(function(acc, pai
|
||||
var sfDefcomp = function(args, env) { return (function() {
|
||||
var nameSym = first(args);
|
||||
var paramsRaw = nth(args, 1);
|
||||
var body = nth(args, 2);
|
||||
var body = last(args);
|
||||
var compName = stripPrefix(symbolName(nameSym), "~");
|
||||
var parsed = parseCompParams(paramsRaw);
|
||||
var params = first(parsed);
|
||||
var hasChildren = nth(parsed, 1);
|
||||
var affinity = defcompKwarg(args, "affinity", "auto");
|
||||
return (function() {
|
||||
var comp = makeComponent(compName, params, hasChildren, body, env);
|
||||
var comp = makeComponent(compName, params, hasChildren, body, env, affinity);
|
||||
env[symbolName(nameSym)] = comp;
|
||||
return comp;
|
||||
})();
|
||||
})(); };
|
||||
|
||||
// defcomp-kwarg
|
||||
var defcompKwarg = function(args, key, default_) { return (function() {
|
||||
var end = (len(args) - 1);
|
||||
var result = default_;
|
||||
{ var _c = range(2, end, 1); for (var _i = 0; _i < _c.length; _i++) { var i = _c[_i]; if (isSxTruthy((isSxTruthy((typeOf(nth(args, i)) == "keyword")) && isSxTruthy((keywordName(nth(args, i)) == key)) && ((i + 1) < end)))) {
|
||||
(function() {
|
||||
var val = nth(args, (i + 1));
|
||||
return (result = (isSxTruthy((typeOf(val) == "keyword")) ? keywordName(val) : val));
|
||||
})();
|
||||
} } }
|
||||
return result;
|
||||
})(); };
|
||||
|
||||
// parse-comp-params
|
||||
var parseCompParams = function(paramsExpr) { return (function() {
|
||||
var params = [];
|
||||
@@ -1057,7 +1073,7 @@ return append_b(inits, nth(binding, 1)); }, bindings) : reduce(function(acc, pai
|
||||
var skipComment = function() { while(true) { if (isSxTruthy((isSxTruthy((pos < lenSrc)) && !isSxTruthy((nth(source, pos) == "\n"))))) { pos = (pos + 1);
|
||||
continue; } else { return NIL; } } };
|
||||
var skipWs = function() { while(true) { if (isSxTruthy((pos < lenSrc))) { { var ch = nth(source, pos);
|
||||
if (isSxTruthy(sxOr((ch == " "), (ch == "\t"), (ch == "\n"), (ch == "\\r")))) { pos = (pos + 1);
|
||||
if (isSxTruthy(sxOr((ch == " "), (ch == "\t"), (ch == "\n"), (ch == "\r")))) { pos = (pos + 1);
|
||||
continue; } else if (isSxTruthy((ch == ";"))) { pos = (pos + 1);
|
||||
skipComment();
|
||||
continue; } else { return NIL; } } } else { return NIL; } } };
|
||||
@@ -1068,7 +1084,7 @@ return (function() {
|
||||
if (isSxTruthy((ch == "\""))) { pos = (pos + 1);
|
||||
return NIL; } else if (isSxTruthy((ch == "\\"))) { pos = (pos + 1);
|
||||
{ var esc = nth(source, pos);
|
||||
buf = (String(buf) + String((isSxTruthy((esc == "n")) ? "\n" : (isSxTruthy((esc == "t")) ? "\t" : (isSxTruthy((esc == "r")) ? "\\r" : esc)))));
|
||||
buf = (String(buf) + String((isSxTruthy((esc == "n")) ? "\n" : (isSxTruthy((esc == "t")) ? "\t" : (isSxTruthy((esc == "r")) ? "\r" : esc)))));
|
||||
pos = (pos + 1);
|
||||
continue; } } else { buf = (String(buf) + String(ch));
|
||||
pos = (pos + 1);
|
||||
@@ -1818,9 +1834,9 @@ return forEach(function(attr) { return (isSxTruthy(!isSxTruthy(domHasAttr(newEl,
|
||||
var handleSxResponse = function(el, target, text, swapStyle, useTransition) { return (function() {
|
||||
var cleaned = stripComponentScripts(text);
|
||||
return (function() {
|
||||
var final = extractResponseCss(cleaned);
|
||||
var final_ = extractResponseCss(cleaned);
|
||||
return (function() {
|
||||
var trimmed = trim(final);
|
||||
var trimmed = trim(final_);
|
||||
return (isSxTruthy(!isSxTruthy(isEmpty(trimmed))) ? (function() {
|
||||
var rendered = sxRender(trimmed);
|
||||
var container = domCreateElement("div", NIL);
|
||||
@@ -2265,7 +2281,7 @@ return (_styleCache = {}); };
|
||||
|
||||
// resolve-style
|
||||
var resolveStyle = function(atoms) { return (function() {
|
||||
var key = join("\\0", atoms);
|
||||
var key = join("\0", atoms);
|
||||
return (function() {
|
||||
var cached = dictGet(_styleCache, key);
|
||||
return (isSxTruthy(!isSxTruthy(isNil(cached))) ? cached : (function() {
|
||||
|
||||
Reference in New Issue
Block a user