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:
@@ -565,7 +565,7 @@ def _sf_defkeyframes(expr: list, env: dict) -> Any:
|
||||
|
||||
|
||||
def _sf_defcomp(expr: list, env: dict) -> Component:
|
||||
"""``(defcomp ~name (&key param1 param2 &rest children) body)``"""
|
||||
"""``(defcomp ~name (&key ...) [:affinity :client|:server] body)``"""
|
||||
if len(expr) < 4:
|
||||
raise EvalError("defcomp requires name, params, and body")
|
||||
name_sym = expr[1]
|
||||
@@ -593,21 +593,38 @@ def _sf_defcomp(expr: list, env: dict) -> Component:
|
||||
params.append(p.name)
|
||||
else:
|
||||
params.append(p.name)
|
||||
# Skip children param name after &rest
|
||||
elif isinstance(p, str):
|
||||
params.append(p)
|
||||
|
||||
# Body is always last element; keyword annotations between params and body
|
||||
body = expr[-1]
|
||||
affinity = _defcomp_kwarg(expr, "affinity", "auto")
|
||||
|
||||
comp = Component(
|
||||
name=comp_name,
|
||||
params=params,
|
||||
has_children=has_children,
|
||||
body=expr[3],
|
||||
body=body,
|
||||
closure=dict(env),
|
||||
affinity=affinity,
|
||||
)
|
||||
env[name_sym.name] = comp
|
||||
return comp
|
||||
|
||||
|
||||
def _defcomp_kwarg(expr: list, key: str, default: str) -> str:
|
||||
"""Extract a keyword annotation from defcomp, e.g. :affinity :client."""
|
||||
# Scan from index 3 to second-to-last for :key value pairs
|
||||
for i in range(3, len(expr) - 1):
|
||||
item = expr[i]
|
||||
if isinstance(item, Keyword) and item.name == key:
|
||||
val = expr[i + 1]
|
||||
if isinstance(val, Keyword):
|
||||
return val.name
|
||||
return str(val)
|
||||
return default
|
||||
|
||||
|
||||
def _sf_begin(expr: list, env: dict) -> Any:
|
||||
if len(expr) < 2:
|
||||
return NIL
|
||||
|
||||
Reference in New Issue
Block a user