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

@@ -169,12 +169,22 @@ class Component:
css_classes: set[str] = field(default_factory=set) # pre-scanned :class values
deps: set[str] = field(default_factory=set) # transitive component deps (~names)
io_refs: set[str] = field(default_factory=set) # transitive IO primitive refs
affinity: str = "auto" # "auto" | "client" | "server"
@property
def is_pure(self) -> bool:
"""True if this component has no transitive IO dependencies."""
return not self.io_refs
@property
def render_target(self) -> str:
"""Where this component should render: 'server' or 'client'."""
if self.affinity == "server":
return "server"
if self.affinity == "client":
return "client"
return "server" if self.io_refs else "client"
def __repr__(self):
return f"<Component ~{self.name}({', '.join(self.params)})>"