Merge branch 'main' into worktree-typed-sx
# Conflicts: # shared/sx/ref/platform_py.py # shared/sx/ref/sx_ref.py
This commit is contained in:
@@ -20,7 +20,7 @@ _PROJECT = os.path.abspath(os.path.join(_HERE, "..", "..", ".."))
|
||||
sys.path.insert(0, _PROJECT)
|
||||
|
||||
from shared.sx.parser import parse_all
|
||||
from shared.sx.evaluator import _eval, _trampoline, _call_lambda
|
||||
from shared.sx.ref.sx_ref import eval_expr as _eval, trampoline as _trampoline, call_lambda as _call_lambda
|
||||
from shared.sx.types import Symbol, Keyword, Lambda, NIL, Component, Island
|
||||
|
||||
# --- Test state ---
|
||||
@@ -127,13 +127,38 @@ def render_html(sx_source):
|
||||
except ImportError:
|
||||
raise RuntimeError("render-to-html not available — sx_ref.py not built")
|
||||
exprs = parse_all(sx_source)
|
||||
render_env = dict(env)
|
||||
# Use Env (not flat dict) so tests exercise the real scope chain path.
|
||||
render_env = _Env(dict(env))
|
||||
result = ""
|
||||
for expr in exprs:
|
||||
result += _render_to_html(expr, render_env)
|
||||
return result
|
||||
|
||||
|
||||
# --- Render SX (aser) platform function ---
|
||||
|
||||
def render_sx(sx_source):
|
||||
"""Parse SX source and serialize to SX wire format via the bootstrapped evaluator."""
|
||||
try:
|
||||
from shared.sx.ref.sx_ref import aser as _aser, serialize as _serialize
|
||||
except ImportError:
|
||||
raise RuntimeError("aser not available — sx_ref.py not built")
|
||||
exprs = parse_all(sx_source)
|
||||
# Use Env (not flat dict) so tests exercise the real scope chain path.
|
||||
# Using dict(env) hides bugs where merge() drops Env parent scopes.
|
||||
render_env = _Env(dict(env))
|
||||
result = ""
|
||||
for expr in exprs:
|
||||
val = _aser(expr, render_env)
|
||||
if isinstance(val, str):
|
||||
result += val
|
||||
elif val is None or val is NIL:
|
||||
pass
|
||||
else:
|
||||
result += _serialize(val)
|
||||
return result
|
||||
|
||||
|
||||
# --- Signal platform primitives ---
|
||||
# Implements the signal runtime platform interface for testing signals.sx
|
||||
|
||||
@@ -258,6 +283,7 @@ SPECS = {
|
||||
"parser": {"file": "test-parser.sx", "needs": ["sx-parse"]},
|
||||
"router": {"file": "test-router.sx", "needs": []},
|
||||
"render": {"file": "test-render.sx", "needs": ["render-html"]},
|
||||
"aser": {"file": "test-aser.sx", "needs": ["render-sx"]},
|
||||
"deps": {"file": "test-deps.sx", "needs": []},
|
||||
"engine": {"file": "test-engine.sx", "needs": []},
|
||||
"orchestration": {"file": "test-orchestration.sx", "needs": []},
|
||||
@@ -297,8 +323,9 @@ env = _Env({
|
||||
"make-keyword": make_keyword,
|
||||
"symbol-name": symbol_name,
|
||||
"keyword-name": keyword_name,
|
||||
# Render platform function
|
||||
# Render platform functions
|
||||
"render-html": render_html,
|
||||
"render-sx": render_sx,
|
||||
# Extra primitives needed by spec modules (router.sx, deps.sx)
|
||||
"for-each-indexed": "_deferred", # replaced below
|
||||
"dict-set!": "_deferred",
|
||||
@@ -848,9 +875,9 @@ def main():
|
||||
print(f"# --- {spec_name} ---")
|
||||
eval_file(spec["file"], env)
|
||||
|
||||
# Reset render state after render tests to avoid leaking
|
||||
# Reset render state after render/aser tests to avoid leaking
|
||||
# into subsequent specs (bootstrapped evaluator checks render_active)
|
||||
if spec_name == "render":
|
||||
if spec_name in ("render", "aser"):
|
||||
try:
|
||||
from shared.sx.ref.sx_ref import set_render_active_b
|
||||
set_render_active_b(False)
|
||||
|
||||
@@ -21,7 +21,7 @@ class TestJsSxTranslation:
|
||||
|
||||
def _translate(self, sx_source: str) -> str:
|
||||
"""Translate a single SX expression to JS using js.sx."""
|
||||
from shared.sx.evaluator import evaluate
|
||||
from shared.sx.ref.sx_ref import evaluate
|
||||
env = load_js_sx()
|
||||
expr = parse(sx_source)
|
||||
env["_def_expr"] = expr
|
||||
|
||||
@@ -18,7 +18,7 @@ from shared.sx.deps import (
|
||||
|
||||
def make_env(*sx_sources: str) -> dict:
|
||||
"""Parse and evaluate component definitions into an env dict."""
|
||||
from shared.sx.evaluator import _eval, _trampoline
|
||||
from shared.sx.ref.sx_ref import eval_expr as _eval, trampoline as _trampoline
|
||||
env: dict = {}
|
||||
for source in sx_sources:
|
||||
exprs = parse_all(source)
|
||||
|
||||
@@ -23,7 +23,7 @@ from shared.sx.deps import (
|
||||
|
||||
def make_env(*sx_sources: str) -> dict:
|
||||
"""Parse and evaluate component definitions into an env dict."""
|
||||
from shared.sx.evaluator import _eval, _trampoline
|
||||
from shared.sx.ref.sx_ref import eval_expr as _eval, trampoline as _trampoline
|
||||
env: dict = {}
|
||||
for source in sx_sources:
|
||||
exprs = parse_all(source)
|
||||
|
||||
@@ -20,7 +20,7 @@ from shared.sx.deps import (
|
||||
|
||||
def make_env(*sx_sources: str) -> dict:
|
||||
"""Parse and evaluate component definitions into an env dict."""
|
||||
from shared.sx.evaluator import _eval, _trampoline
|
||||
from shared.sx.ref.sx_ref import eval_expr as _eval, trampoline as _trampoline
|
||||
env: dict = {}
|
||||
for source in sx_sources:
|
||||
exprs = parse_all(source)
|
||||
@@ -282,7 +282,7 @@ class TestIoRoutingLogic:
|
||||
"""
|
||||
|
||||
def _eval(self, src, env):
|
||||
from shared.sx.evaluator import _eval, _trampoline
|
||||
from shared.sx.ref.sx_ref import eval_expr as _eval, trampoline as _trampoline
|
||||
result = None
|
||||
for expr in parse_all(src):
|
||||
result = _trampoline(_eval(expr, env))
|
||||
|
||||
@@ -156,7 +156,7 @@ class TestDataPageDeps:
|
||||
def test_deps_computed_for_data_page(self):
|
||||
from shared.sx.deps import components_needed
|
||||
from shared.sx.parser import parse_all as pa
|
||||
from shared.sx.evaluator import _eval, _trampoline
|
||||
from shared.sx.ref.sx_ref import eval_expr as _eval, trampoline as _trampoline
|
||||
|
||||
# Define a component
|
||||
env = {}
|
||||
@@ -172,7 +172,7 @@ class TestDataPageDeps:
|
||||
def test_deps_transitive_for_data_page(self):
|
||||
from shared.sx.deps import components_needed
|
||||
from shared.sx.parser import parse_all as pa
|
||||
from shared.sx.evaluator import _eval, _trampoline
|
||||
from shared.sx.ref.sx_ref import eval_expr as _eval, trampoline as _trampoline
|
||||
|
||||
env = {}
|
||||
source = """
|
||||
@@ -205,7 +205,7 @@ class TestDataPipelineSimulation:
|
||||
|
||||
def test_full_pipeline(self):
|
||||
from shared.sx.parser import parse_all as pa
|
||||
from shared.sx.evaluator import _eval, _trampoline
|
||||
from shared.sx.ref.sx_ref import eval_expr as _eval, trampoline as _trampoline
|
||||
|
||||
# 1. Define a component that uses only pure primitives
|
||||
env = {}
|
||||
@@ -236,7 +236,7 @@ class TestDataPipelineSimulation:
|
||||
|
||||
def test_pipeline_with_list_data(self):
|
||||
from shared.sx.parser import parse_all as pa
|
||||
from shared.sx.evaluator import _eval, _trampoline
|
||||
from shared.sx.ref.sx_ref import eval_expr as _eval, trampoline as _trampoline
|
||||
|
||||
env = {}
|
||||
for expr in pa('''
|
||||
@@ -262,7 +262,7 @@ class TestDataPipelineSimulation:
|
||||
def test_pipeline_data_isolation(self):
|
||||
"""Different data for the same content produces different results."""
|
||||
from shared.sx.parser import parse_all as pa
|
||||
from shared.sx.evaluator import _eval, _trampoline
|
||||
from shared.sx.ref.sx_ref import eval_expr as _eval, trampoline as _trampoline
|
||||
|
||||
env = {}
|
||||
for expr in pa('(defcomp ~page (&key title count) (str title ": " count))'):
|
||||
@@ -298,7 +298,7 @@ class TestDataCache:
|
||||
def _make_env(self, current_time_ms=1000):
|
||||
"""Create an env with cache functions and a controllable now-ms."""
|
||||
from shared.sx.parser import parse_all as pa
|
||||
from shared.sx.evaluator import _eval, _trampoline
|
||||
from shared.sx.ref.sx_ref import eval_expr as _eval, trampoline as _trampoline
|
||||
|
||||
env = {}
|
||||
# Mock now-ms as a callable that returns current_time_ms
|
||||
@@ -344,7 +344,7 @@ class TestDataCache:
|
||||
|
||||
def _eval(self, src, env):
|
||||
from shared.sx.parser import parse_all as pa
|
||||
from shared.sx.evaluator import _eval, _trampoline
|
||||
from shared.sx.ref.sx_ref import eval_expr as _eval, trampoline as _trampoline
|
||||
result = None
|
||||
for expr in pa(src):
|
||||
result = _trampoline(_eval(expr, env))
|
||||
|
||||
@@ -18,7 +18,7 @@ from shared.sx.types import Symbol, Keyword, Lambda, Component, Macro, NIL
|
||||
|
||||
def hw_eval(text, env=None):
|
||||
"""Evaluate via hand-written evaluator.py."""
|
||||
from shared.sx.evaluator import evaluate as _evaluate, EvalError
|
||||
from shared.sx.ref.sx_ref import evaluate as _evaluate
|
||||
if env is None:
|
||||
env = {}
|
||||
return _evaluate(parse(text), env)
|
||||
@@ -50,7 +50,7 @@ def ref_render(text, env=None):
|
||||
|
||||
def hw_eval_multi(text, env=None):
|
||||
"""Evaluate multiple expressions (e.g. defines then call)."""
|
||||
from shared.sx.evaluator import evaluate as _evaluate
|
||||
from shared.sx.ref.sx_ref import evaluate as _evaluate
|
||||
if env is None:
|
||||
env = {}
|
||||
result = None
|
||||
@@ -736,7 +736,7 @@ class TestParityDeps:
|
||||
|
||||
class TestParityErrors:
|
||||
def test_undefined_symbol(self):
|
||||
from shared.sx.evaluator import EvalError as HwError
|
||||
from shared.sx.types import EvalError as HwError
|
||||
from shared.sx.ref.sx_ref import EvalError as RefError
|
||||
with pytest.raises(HwError):
|
||||
hw_eval("undefined_var")
|
||||
|
||||
@@ -12,7 +12,7 @@ import pytest
|
||||
|
||||
from shared.sx.parser import parse, parse_all
|
||||
from shared.sx.html import render as py_render
|
||||
from shared.sx.evaluator import evaluate
|
||||
from shared.sx.ref.sx_ref import evaluate
|
||||
|
||||
SX_JS = Path(__file__).resolve().parents[2] / "static" / "scripts" / "sx.js"
|
||||
SX_TEST_JS = Path(__file__).resolve().parents[2] / "static" / "scripts" / "sx-test.js"
|
||||
|
||||
@@ -7,7 +7,7 @@ from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
from shared.sx.parser import parse_all
|
||||
from shared.sx.evaluator import _eval, _trampoline
|
||||
from shared.sx.ref.sx_ref import eval_expr as _eval, trampoline as _trampoline
|
||||
|
||||
|
||||
_PREAMBLE = '''(define assert-equal (fn (expected actual) (assert (equal? expected actual) (str "Expected " (str expected) " but got " (str actual)))))
|
||||
|
||||
Reference in New Issue
Block a user