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:
2026-03-07 23:53:33 +00:00
parent 81d8e55fb0
commit a70ff2b153
19 changed files with 540 additions and 224 deletions

View File

@@ -479,6 +479,18 @@
:stream-message stream-message
:stream-time stream-time))
(defpage affinity-demo
:path "/isomorphism/affinity"
:auth :public
:layout (:sx-section
:section "Isomorphism"
:sub-label "Isomorphism"
:sub-href "/isomorphism/"
:sub-nav (~section-nav :items isomorphism-nav-items :current "Affinity")
:selected "Affinity")
:data (affinity-demo-data)
:content (~affinity-demo-content :components components))
;; Wildcard must come AFTER specific routes (first-match routing)
(defpage isomorphism-page
:path "/isomorphism/<slug>"

View File

@@ -27,6 +27,7 @@ def _register_sx_helpers() -> None:
"run-spec-tests": _run_spec_tests,
"run-modular-tests": _run_modular_tests,
"streaming-demo-data": _streaming_demo_data,
"affinity-demo-data": _affinity_demo_data,
})
@@ -318,6 +319,8 @@ def _bundle_analyzer_data() -> dict:
comp_details.append({
"name": comp_name,
"is-pure": is_pure,
"affinity": val.affinity,
"render-target": val.render_target,
"io-refs": sorted(val.io_refs),
"deps": sorted(val.deps),
"source": source,
@@ -875,3 +878,30 @@ async def _streaming_demo_data():
"stream-message": "Model inference completed in ~5 seconds",
"stream-time": datetime.now(timezone.utc).isoformat(timespec="seconds"),
}
def _affinity_demo_data() -> dict:
"""Return affinity analysis for the demo components."""
from shared.sx.jinja_bridge import get_component_env
from shared.sx.types import Component
env = get_component_env()
demo_names = [
"~aff-demo-auto",
"~aff-demo-client",
"~aff-demo-server",
"~aff-demo-io-auto",
"~aff-demo-io-client",
]
components = []
for name in demo_names:
val = env.get(name)
if isinstance(val, Component):
components.append({
"name": name,
"affinity": val.affinity,
"render-target": val.render_target,
"io-refs": sorted(val.io_refs),
"is-pure": val.is_pure,
})
return {"components": components}