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:
@@ -31,6 +31,19 @@ from shared.sx.types import Symbol, Keyword, NIL as SX_NIL
|
||||
# SX -> Python transpiler
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# Python reserved words — SX names that collide get _ suffix
|
||||
# Excludes names we intentionally shadow (list, dict, range, filter, map)
|
||||
_PY_RESERVED = frozenset({
|
||||
"False", "None", "True", "and", "as", "assert", "async", "await",
|
||||
"break", "class", "continue", "def", "del", "elif", "else", "except",
|
||||
"finally", "for", "from", "global", "if", "import", "in", "is",
|
||||
"lambda", "nonlocal", "not", "or", "pass", "raise", "return", "try",
|
||||
"while", "with", "yield",
|
||||
# builtins we don't want to shadow
|
||||
"default", "type", "id", "input", "open", "print", "set", "super",
|
||||
})
|
||||
|
||||
|
||||
class PyEmitter:
|
||||
"""Transpile an SX AST node to Python source code."""
|
||||
|
||||
@@ -124,6 +137,7 @@ class PyEmitter:
|
||||
"component-closure": "component_closure",
|
||||
"component-has-children?": "component_has_children",
|
||||
"component-name": "component_name",
|
||||
"component-affinity": "component_affinity",
|
||||
"macro-params": "macro_params",
|
||||
"macro-rest-param": "macro_rest_param",
|
||||
"macro-body": "macro_body",
|
||||
@@ -182,6 +196,7 @@ class PyEmitter:
|
||||
"sf-lambda": "sf_lambda",
|
||||
"sf-define": "sf_define",
|
||||
"sf-defcomp": "sf_defcomp",
|
||||
"defcomp-kwarg": "defcomp_kwarg",
|
||||
"sf-defmacro": "sf_defmacro",
|
||||
"sf-begin": "sf_begin",
|
||||
"sf-quote": "sf_quote",
|
||||
@@ -262,6 +277,7 @@ class PyEmitter:
|
||||
"transitive-io-refs": "transitive_io_refs",
|
||||
"compute-all-io-refs": "compute_all_io_refs",
|
||||
"component-pure?": "component_pure_p",
|
||||
"render-target": "render_target",
|
||||
# router.sx
|
||||
"split-path-segments": "split_path_segments",
|
||||
"make-route-segment": "make_route_segment",
|
||||
@@ -281,9 +297,9 @@ class PyEmitter:
|
||||
result = result[:-1] + "_b"
|
||||
# Kebab to snake_case
|
||||
result = result.replace("-", "_")
|
||||
# Avoid Python keyword conflicts
|
||||
if result in ("list", "dict", "range", "filter"):
|
||||
result = result # keep as-is, these are our SX aliases
|
||||
# Escape Python reserved words
|
||||
if result in _PY_RESERVED:
|
||||
result = result + "_"
|
||||
return result
|
||||
|
||||
# --- List emission ---
|
||||
@@ -1220,9 +1236,9 @@ def make_lambda(params, body, env):
|
||||
return Lambda(params=list(params), body=body, closure=dict(env))
|
||||
|
||||
|
||||
def make_component(name, params, has_children, body, env):
|
||||
def make_component(name, params, has_children, body, env, affinity="auto"):
|
||||
return Component(name=name, params=list(params), has_children=has_children,
|
||||
body=body, closure=dict(env))
|
||||
body=body, closure=dict(env), affinity=str(affinity) if affinity else "auto")
|
||||
|
||||
|
||||
def make_macro(params, rest_param, body, env, name=None):
|
||||
@@ -1311,6 +1327,10 @@ def component_name(c):
|
||||
return c.name
|
||||
|
||||
|
||||
def component_affinity(c):
|
||||
return getattr(c, 'affinity', 'auto')
|
||||
|
||||
|
||||
def macro_params(m):
|
||||
return m.params
|
||||
|
||||
|
||||
Reference in New Issue
Block a user