Complete Python eval removal: epoch protocol, scope consolidation, JIT fixes
Route all rendering through OCaml bridge — render_to_html no longer uses Python async_eval. Fix register_components to parse &key params and &rest children from defcomp forms. Remove all dead sx_ref.py imports. Epoch protocol (prevents pipe desync): - Every command prefixed with (epoch N), all responses tagged with epoch - Both sides discard stale-epoch messages — desync structurally impossible - OCaml main loop discards stale io-responses between commands Consolidate scope primitives into sx_scope.ml: - Single source of truth for scope-push!/pop!/peek, collect!/collected, emit!/emitted, context, and 12 other scope operations - Removes duplicate registrations from sx_server.ml (including bugs where scope-emit! and clear-collected! were registered twice with different impls) - Bind scope prims into env so JIT VM finds them via OP_GLOBAL_GET JIT VM fixes: - Trampoline thunks before passing args to CALL_PRIM - as_list resolves thunks via _sx_trampoline_fn - len handles all value types (Bool, Number, RawHTML, SxExpr, Spread, etc.) Other fixes: - ~cssx/tw signature: (tokens) → (&key tokens) to match callers - Minimal Python evaluator in html.py for sync sx() Jinja function - Python scope primitive stubs (thread-local) for non-OCaml paths - Reader macro resolution via OcamlSync instead of sx_ref.py Tests: 1114 OCaml, 1078 JS, 35 Python regression, 6/6 Playwright SSR Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -579,26 +579,54 @@ def prim_json_encode(value) -> str:
|
||||
# (shared global state between transpiled and hand-written evaluators)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _lazy_scope_primitives():
|
||||
"""Register scope/provide/collect primitives from sx_ref.py.
|
||||
def _register_scope_primitives():
|
||||
"""Register scope/provide/collect primitive stubs.
|
||||
|
||||
Called at import time — if sx_ref.py isn't built yet, silently skip.
|
||||
These are needed by the hand-written _aser in async_eval.py when
|
||||
expanding components that use scoped effects (e.g. ~cssx/flush).
|
||||
The OCaml kernel provides the real implementations. These stubs exist
|
||||
so _PRIMITIVES contains the names for dependency analysis, and so
|
||||
any Python-side code that checks for their existence finds them.
|
||||
"""
|
||||
try:
|
||||
from .ref.sx_ref import (
|
||||
sx_collect, sx_collected, sx_clear_collected,
|
||||
sx_emitted, sx_emit, sx_context,
|
||||
)
|
||||
_PRIMITIVES["collect!"] = sx_collect
|
||||
_PRIMITIVES["collected"] = sx_collected
|
||||
_PRIMITIVES["clear-collected!"] = sx_clear_collected
|
||||
_PRIMITIVES["emitted"] = sx_emitted
|
||||
_PRIMITIVES["emit!"] = sx_emit
|
||||
_PRIMITIVES["context"] = sx_context
|
||||
except ImportError:
|
||||
pass
|
||||
import threading
|
||||
_scope_data = threading.local()
|
||||
|
||||
_lazy_scope_primitives()
|
||||
def _collect(channel, value):
|
||||
if not hasattr(_scope_data, 'collected'):
|
||||
_scope_data.collected = {}
|
||||
_scope_data.collected.setdefault(channel, []).append(value)
|
||||
return NIL
|
||||
|
||||
def _collected(channel):
|
||||
if not hasattr(_scope_data, 'collected'):
|
||||
return []
|
||||
return list(_scope_data.collected.get(channel, []))
|
||||
|
||||
def _clear_collected(channel):
|
||||
if hasattr(_scope_data, 'collected'):
|
||||
_scope_data.collected.pop(channel, None)
|
||||
return NIL
|
||||
|
||||
def _emit(channel, value):
|
||||
if not hasattr(_scope_data, 'emitted'):
|
||||
_scope_data.emitted = {}
|
||||
_scope_data.emitted.setdefault(channel, []).append(value)
|
||||
return NIL
|
||||
|
||||
def _emitted(channel):
|
||||
if not hasattr(_scope_data, 'emitted'):
|
||||
return []
|
||||
return list(_scope_data.emitted.get(channel, []))
|
||||
|
||||
def _context(key):
|
||||
if not hasattr(_scope_data, 'context'):
|
||||
return NIL
|
||||
return _scope_data.context.get(key, NIL) if isinstance(_scope_data.context, dict) else NIL
|
||||
|
||||
_PRIMITIVES["collect!"] = _collect
|
||||
_PRIMITIVES["collected"] = _collected
|
||||
_PRIMITIVES["clear-collected!"] = _clear_collected
|
||||
_PRIMITIVES["emitted"] = _emitted
|
||||
_PRIMITIVES["emit!"] = _emit
|
||||
_PRIMITIVES["context"] = _context
|
||||
|
||||
_register_scope_primitives()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user