Add signal test suite (17/17) and Island type to evaluator
test-signals.sx: 17 tests covering signal basics (create, deref, reset!, swap!), computed (derive, update, chain), effects (run, re-run, dispose, cleanup), batch (deferred deduped notifications), and defisland (create, call, children). types.py: Island dataclass mirroring Component but for reactive boundaries. evaluator.py: sf_defisland special form, Island in call dispatch. run.py: Signal platform primitives (make-signal, tracking context, etc) and native effect/computed/batch implementations that bridge Lambda calls across the Python↔SX boundary. signals.sx: Updated batch to deduplicate subscribers across signals. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -33,7 +33,7 @@ from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from .types import Component, Continuation, HandlerDef, Keyword, Lambda, Macro, NIL, PageDef, RelationDef, Symbol, _ShiftSignal
|
||||
from .types import Component, Continuation, HandlerDef, Island, Keyword, Lambda, Macro, NIL, PageDef, RelationDef, Symbol, _ShiftSignal
|
||||
from .primitives import _PRIMITIVES
|
||||
|
||||
|
||||
@@ -147,13 +147,13 @@ def _eval(expr: Any, env: dict[str, Any]) -> Any:
|
||||
fn = _trampoline(_eval(head, env))
|
||||
args = [_trampoline(_eval(a, env)) for a in expr[1:]]
|
||||
|
||||
if callable(fn) and not isinstance(fn, (Lambda, Component)):
|
||||
if callable(fn) and not isinstance(fn, (Lambda, Component, Island)):
|
||||
return fn(*args)
|
||||
|
||||
if isinstance(fn, Lambda):
|
||||
return _call_lambda(fn, args, env)
|
||||
|
||||
if isinstance(fn, Component):
|
||||
if isinstance(fn, (Component, Island)):
|
||||
return _call_component(fn, expr[1:], env)
|
||||
|
||||
raise EvalError(f"Not callable: {fn!r}")
|
||||
@@ -555,6 +555,51 @@ def _sf_defcomp(expr: list, env: dict) -> Component:
|
||||
return comp
|
||||
|
||||
|
||||
def _sf_defisland(expr: list, env: dict) -> Island:
|
||||
"""``(defisland ~name (&key ...) body)``"""
|
||||
if len(expr) < 4:
|
||||
raise EvalError("defisland requires name, params, and body")
|
||||
name_sym = expr[1]
|
||||
if not isinstance(name_sym, Symbol):
|
||||
raise EvalError(f"defisland name must be symbol, got {type(name_sym).__name__}")
|
||||
comp_name = name_sym.name.lstrip("~")
|
||||
|
||||
params_expr = expr[2]
|
||||
if not isinstance(params_expr, list):
|
||||
raise EvalError("defisland params must be a list")
|
||||
|
||||
params: list[str] = []
|
||||
has_children = False
|
||||
in_key = False
|
||||
for p in params_expr:
|
||||
if isinstance(p, Symbol):
|
||||
if p.name == "&key":
|
||||
in_key = True
|
||||
continue
|
||||
if p.name == "&rest":
|
||||
has_children = True
|
||||
continue
|
||||
if in_key or has_children:
|
||||
if not has_children:
|
||||
params.append(p.name)
|
||||
else:
|
||||
params.append(p.name)
|
||||
elif isinstance(p, str):
|
||||
params.append(p)
|
||||
|
||||
body = expr[-1]
|
||||
|
||||
island = Island(
|
||||
name=comp_name,
|
||||
params=params,
|
||||
has_children=has_children,
|
||||
body=body,
|
||||
closure=dict(env),
|
||||
)
|
||||
env[name_sym.name] = island
|
||||
return island
|
||||
|
||||
|
||||
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
|
||||
@@ -592,7 +637,7 @@ def _sf_thread_first(expr: list, env: dict) -> Any:
|
||||
else:
|
||||
fn = _trampoline(_eval(form, env))
|
||||
args = [result]
|
||||
if callable(fn) and not isinstance(fn, (Lambda, Component)):
|
||||
if callable(fn) and not isinstance(fn, (Lambda, Component, Island)):
|
||||
result = fn(*args)
|
||||
elif isinstance(fn, Lambda):
|
||||
result = _trampoline(_call_lambda(fn, args, env))
|
||||
@@ -1021,6 +1066,7 @@ _SPECIAL_FORMS: dict[str, Any] = {
|
||||
"define": _sf_define,
|
||||
"defstyle": _sf_defstyle,
|
||||
"defcomp": _sf_defcomp,
|
||||
"defisland": _sf_defisland,
|
||||
"defrelation": _sf_defrelation,
|
||||
"begin": _sf_begin,
|
||||
"do": _sf_begin,
|
||||
|
||||
Reference in New Issue
Block a user