From 29c90a625b40ef9558310e3316867ad7d0eaab86 Mon Sep 17 00:00:00 2001 From: giles Date: Wed, 11 Mar 2026 11:15:48 +0000 Subject: [PATCH] Delete evaluator.py shim: all imports go directly to bootstrapped sx_ref.py EvalError moved to types.py. All 27 files updated to import eval_expr, trampoline, call_lambda, etc. directly from shared.sx.ref.sx_ref instead of through the evaluator.py indirection layer. 320/320 spec tests pass. Co-Authored-By: Claude Opus 4.6 --- shared/sx/__init__.py | 7 +-- shared/sx/async_eval.py | 21 ++++--- shared/sx/evaluator.py | 94 ---------------------------- shared/sx/handlers.py | 5 +- shared/sx/html.py | 2 +- shared/sx/jinja_bridge.py | 5 +- shared/sx/pages.py | 2 +- shared/sx/parser.py | 2 +- shared/sx/query_registry.py | 4 +- shared/sx/ref/bootstrap_test.py | 2 +- shared/sx/ref/platform_py.py | 2 +- shared/sx/ref/reader_z3.py | 6 +- shared/sx/ref/run_js_sx.py | 4 +- shared/sx/ref/run_py_sx.py | 4 +- shared/sx/ref/sx_ref.py | 2 +- shared/sx/resolver.py | 2 +- shared/sx/tests/run.py | 2 +- shared/sx/tests/test_bootstrapper.py | 2 +- shared/sx/tests/test_deps.py | 2 +- shared/sx/tests/test_io_detection.py | 2 +- shared/sx/tests/test_io_proxy.py | 4 +- shared/sx/tests/test_page_data.py | 14 ++--- shared/sx/tests/test_parity.py | 6 +- shared/sx/tests/test_sx_js.py | 2 +- shared/sx/tests/test_sx_spec.py | 2 +- shared/sx/types.py | 9 +++ sx/sxc/pages/helpers.py | 14 ++--- 27 files changed, 65 insertions(+), 158 deletions(-) delete mode 100644 shared/sx/evaluator.py diff --git a/shared/sx/__init__.py b/shared/sx/__init__.py index 20d357c8..9322cfb2 100644 --- a/shared/sx/__init__.py +++ b/shared/sx/__init__.py @@ -31,11 +31,8 @@ from .parser import ( parse_all, serialize, ) -from .evaluator import ( - EvalError, - evaluate, - make_env, -) +from .types import EvalError +from .ref.sx_ref import evaluate, make_env from .primitives import ( all_primitives, diff --git a/shared/sx/async_eval.py b/shared/sx/async_eval.py index beccab34..78c8e526 100644 --- a/shared/sx/async_eval.py +++ b/shared/sx/async_eval.py @@ -53,7 +53,8 @@ from .types import Component, Island, Keyword, Lambda, Macro, NIL, Symbol _expand_components: contextvars.ContextVar[bool] = contextvars.ContextVar( "_expand_components", default=False ) -from .evaluator import _expand_macro, EvalError +from .ref.sx_ref import expand_macro as _expand_macro +from .types import EvalError from .primitives import _PRIMITIVES from .primitives_io import IO_PRIMITIVES, RequestContext, execute_io from .parser import SxExpr, serialize @@ -420,23 +421,23 @@ async def _asf_define(expr, env, ctx): async def _asf_defcomp(expr, env, ctx): - from .evaluator import _sf_defcomp - return _sf_defcomp(expr, env) + from .ref.sx_ref import sf_defcomp + return sf_defcomp(expr[1:], env) async def _asf_defstyle(expr, env, ctx): - from .evaluator import _sf_defstyle - return _sf_defstyle(expr, env) + from .ref.sx_ref import sf_defstyle + return sf_defstyle(expr[1:], env) async def _asf_defmacro(expr, env, ctx): - from .evaluator import _sf_defmacro - return _sf_defmacro(expr, env) + from .ref.sx_ref import sf_defmacro + return sf_defmacro(expr[1:], env) async def _asf_defhandler(expr, env, ctx): - from .evaluator import _sf_defhandler - return _sf_defhandler(expr, env) + from .ref.sx_ref import sf_defhandler + return sf_defhandler(expr[1:], env) async def _asf_begin(expr, env, ctx): @@ -599,7 +600,7 @@ async def _asf_reset(expr, env, ctx): _ASYNC_RESET_RESUME.append(value if value is not None else NIL) try: # Sync re-evaluation; the async caller will trampoline - from .evaluator import _eval as sync_eval, _trampoline + from .ref.sx_ref import eval_expr as sync_eval, trampoline as _trampoline return _trampoline(sync_eval(body, env)) finally: _ASYNC_RESET_RESUME.pop() diff --git a/shared/sx/evaluator.py b/shared/sx/evaluator.py deleted file mode 100644 index 4e703528..00000000 --- a/shared/sx/evaluator.py +++ /dev/null @@ -1,94 +0,0 @@ -""" -S-expression evaluator — thin shim over bootstrapped sx_ref.py. - -All evaluation logic lives in the spec (shared/sx/ref/eval.sx) and is -bootstrapped to Python (shared/sx/ref/sx_ref.py). This module re-exports -the public API and internal helpers under their historical names so that -existing callers don't need updating. - -Imports are lazy (inside functions/properties) to avoid circular imports -during bootstrapping: bootstrap_py.py → parser → __init__ → evaluator → sx_ref. -""" - -from __future__ import annotations - - -def _ref(): - """Lazy import of the bootstrapped evaluator.""" - from .ref import sx_ref - return sx_ref - - -# --------------------------------------------------------------------------- -# Public API — these are the most used, so we make them importable directly -# --------------------------------------------------------------------------- - -class EvalError(Exception): - """Error during expression evaluation. - - Delegates to the bootstrapped EvalError at runtime but is defined here - so imports don't fail during bootstrapping. - """ - pass - - -def evaluate(expr, env=None): - return _ref().evaluate(expr, env) - - -def make_env(**kwargs): - return _ref().make_env(**kwargs) - - -# --------------------------------------------------------------------------- -# Internal helpers — used by html.py, async_eval.py, handlers.py, etc. -# --------------------------------------------------------------------------- - -def _eval(expr, env): - return _ref().eval_expr(expr, env) - - -def _trampoline(val): - return _ref().trampoline(val) - - -def _call_lambda(fn, args, caller_env): - return _ref().call_lambda(fn, args, caller_env) - - -def _call_component(comp, raw_args, env): - return _ref().call_component(comp, raw_args, env) - - -def _expand_macro(macro, raw_args, env): - return _ref().expand_macro(macro, raw_args, env) - - -# --------------------------------------------------------------------------- -# Special-form wrappers: callers pass (expr, env) with expr[0] = head symbol. -# sx_ref.py special forms take (args, env) where args = expr[1:]. -# --------------------------------------------------------------------------- - -def _sf_defcomp(expr, env): - return _ref().sf_defcomp(expr[1:], env) - -def _sf_defisland(expr, env): - return _ref().sf_defisland(expr[1:], env) - -def _sf_defstyle(expr, env): - return _ref().sf_defstyle(expr[1:], env) - -def _sf_defmacro(expr, env): - return _ref().sf_defmacro(expr[1:], env) - -def _sf_defhandler(expr, env): - return _ref().sf_defhandler(expr[1:], env) - -def _sf_defpage(expr, env): - return _ref().sf_defpage(expr[1:], env) - -def _sf_defquery(expr, env): - return _ref().sf_defquery(expr[1:], env) - -def _sf_defaction(expr, env): - return _ref().sf_defaction(expr[1:], env) diff --git a/shared/sx/handlers.py b/shared/sx/handlers.py index 0aabd8a8..1a775d43 100644 --- a/shared/sx/handlers.py +++ b/shared/sx/handlers.py @@ -70,10 +70,7 @@ def load_handler_file(filepath: str, service_name: str) -> list[HandlerDef]: """Parse an .sx file, evaluate it, and register any HandlerDef values.""" from .parser import parse_all import os - if os.environ.get("SX_USE_REF") == "1": - from .ref.sx_ref import eval_expr as _raw_eval, trampoline as _trampoline - else: - from .evaluator import _eval as _raw_eval, _trampoline + from .ref.sx_ref import eval_expr as _raw_eval, trampoline as _trampoline _eval = lambda expr, env: _trampoline(_raw_eval(expr, env)) from .jinja_bridge import get_component_env diff --git a/shared/sx/html.py b/shared/sx/html.py index 2341bd98..f7fc9d50 100644 --- a/shared/sx/html.py +++ b/shared/sx/html.py @@ -28,7 +28,7 @@ import contextvars from typing import Any from .types import Component, Island, Keyword, Lambda, Macro, NIL, Symbol -from .evaluator import _eval as _raw_eval, _call_component as _raw_call_component, _expand_macro, _trampoline +from .ref.sx_ref import eval_expr as _raw_eval, call_component as _raw_call_component, expand_macro as _expand_macro, trampoline as _trampoline def _eval(expr, env): """Evaluate and unwrap thunks — all html.py _eval calls are non-tail.""" diff --git a/shared/sx/jinja_bridge.py b/shared/sx/jinja_bridge.py index 9da4a6c4..167eac57 100644 --- a/shared/sx/jinja_bridge.py +++ b/shared/sx/jinja_bridge.py @@ -229,10 +229,7 @@ def register_components(sx_source: str) -> None: (div :class "..." (div :class "..." title))))) ''') """ - if _os.environ.get("SX_USE_REF") == "1": - from .ref.sx_ref import eval_expr as _raw_eval, trampoline as _trampoline - else: - from .evaluator import _eval as _raw_eval, _trampoline + from .ref.sx_ref import eval_expr as _raw_eval, trampoline as _trampoline _eval = lambda expr, env: _trampoline(_raw_eval(expr, env)) from .parser import parse_all from .css_registry import scan_classes_from_sx diff --git a/shared/sx/pages.py b/shared/sx/pages.py index f0df5ce0..51dc76de 100644 --- a/shared/sx/pages.py +++ b/shared/sx/pages.py @@ -127,7 +127,7 @@ def get_page_helpers(service: str) -> dict[str, Any]: def load_page_file(filepath: str, service_name: str) -> list[PageDef]: """Parse an .sx file, evaluate it, and register any PageDef values.""" from .parser import parse_all - from .evaluator import _eval as _raw_eval, _trampoline + from .ref.sx_ref import eval_expr as _raw_eval, trampoline as _trampoline _eval = lambda expr, env: _trampoline(_raw_eval(expr, env)) from .jinja_bridge import get_component_env diff --git a/shared/sx/parser.py b/shared/sx/parser.py index c4ac622c..f4092936 100644 --- a/shared/sx/parser.py +++ b/shared/sx/parser.py @@ -41,7 +41,7 @@ def _resolve_sx_reader_macro(name: str): """ try: from .jinja_bridge import get_component_env - from .evaluator import _trampoline, _call_lambda + from .ref.sx_ref import trampoline as _trampoline, call_lambda as _call_lambda from .types import Lambda except ImportError: return None diff --git a/shared/sx/query_registry.py b/shared/sx/query_registry.py index e6d63e35..3edda12e 100644 --- a/shared/sx/query_registry.py +++ b/shared/sx/query_registry.py @@ -78,7 +78,7 @@ def clear(service: str | None = None) -> None: def load_query_file(filepath: str, service_name: str) -> list[QueryDef]: """Parse an .sx file and register any defquery definitions.""" from .parser import parse_all - from .evaluator import _eval as _raw_eval, _trampoline + from .ref.sx_ref import eval_expr as _raw_eval, trampoline as _trampoline _eval = lambda expr, env: _trampoline(_raw_eval(expr, env)) from .jinja_bridge import get_component_env @@ -103,7 +103,7 @@ def load_query_file(filepath: str, service_name: str) -> list[QueryDef]: def load_action_file(filepath: str, service_name: str) -> list[ActionDef]: """Parse an .sx file and register any defaction definitions.""" from .parser import parse_all - from .evaluator import _eval as _raw_eval, _trampoline + from .ref.sx_ref import eval_expr as _raw_eval, trampoline as _trampoline _eval = lambda expr, env: _trampoline(_raw_eval(expr, env)) from .jinja_bridge import get_component_env diff --git a/shared/sx/ref/bootstrap_test.py b/shared/sx/ref/bootstrap_test.py index 08eb64bc..0f1ef43c 100644 --- a/shared/sx/ref/bootstrap_test.py +++ b/shared/sx/ref/bootstrap_test.py @@ -143,7 +143,7 @@ def _emit_py(suites: list[dict], preamble: list) -> str: lines.append('') lines.append('import pytest') lines.append('from shared.sx.parser import parse_all') - lines.append('from shared.sx.evaluator import _eval, _trampoline') + lines.append('from shared.sx.ref.sx_ref import eval_expr as _eval, trampoline as _trampoline') lines.append('') lines.append('') lines.append(f"_PREAMBLE = '''{preamble_escaped}'''") diff --git a/shared/sx/ref/platform_py.py b/shared/sx/ref/platform_py.py index c21be752..74fafaef 100644 --- a/shared/sx/ref/platform_py.py +++ b/shared/sx/ref/platform_py.py @@ -600,7 +600,7 @@ def sx_expr_source(x): try: - from shared.sx.evaluator import EvalError + from shared.sx.types import EvalError except ImportError: class EvalError(Exception): pass diff --git a/shared/sx/ref/reader_z3.py b/shared/sx/ref/reader_z3.py index f5c76239..8ab2297a 100644 --- a/shared/sx/ref/reader_z3.py +++ b/shared/sx/ref/reader_z3.py @@ -39,7 +39,7 @@ def _get_z3_env() -> dict[str, Any]: return _z3_env from shared.sx.parser import parse_all - from shared.sx.evaluator import make_env, _eval, _trampoline + from shared.sx.ref.sx_ref import make_env, eval_expr as _eval, trampoline as _trampoline env = make_env() z3_path = os.path.join(os.path.dirname(__file__), "z3.sx") @@ -60,7 +60,7 @@ def z3_translate(expr: Any) -> str: Delegates to z3-translate defined in z3.sx. """ - from shared.sx.evaluator import _trampoline, _call_lambda + from shared.sx.ref.sx_ref import trampoline as _trampoline, call_lambda as _call_lambda env = _get_z3_env() return _trampoline(_call_lambda(env["z3-translate"], [expr], env)) @@ -72,7 +72,7 @@ def z3_translate_file(source: str) -> str: Delegates to z3-translate-file defined in z3.sx. """ from shared.sx.parser import parse_all - from shared.sx.evaluator import _trampoline, _call_lambda + from shared.sx.ref.sx_ref import trampoline as _trampoline, call_lambda as _call_lambda env = _get_z3_env() exprs = parse_all(source) diff --git a/shared/sx/ref/run_js_sx.py b/shared/sx/ref/run_js_sx.py index ebe478b6..9fd1e141 100644 --- a/shared/sx/ref/run_js_sx.py +++ b/shared/sx/ref/run_js_sx.py @@ -49,7 +49,7 @@ def load_js_sx() -> dict: exprs = parse_all(source) - from shared.sx.evaluator import evaluate, make_env + from shared.sx.ref.sx_ref import evaluate, make_env env = make_env() for expr in exprs: @@ -74,7 +74,7 @@ def compile_ref_to_js( spec_modules: List of spec modules (deps, router, signals). None = auto. """ from datetime import datetime, timezone - from shared.sx.evaluator import evaluate + from shared.sx.ref.sx_ref import evaluate ref_dir = _HERE env = load_js_sx() diff --git a/shared/sx/ref/run_py_sx.py b/shared/sx/ref/run_py_sx.py index ab170f5f..54927cf6 100644 --- a/shared/sx/ref/run_py_sx.py +++ b/shared/sx/ref/run_py_sx.py @@ -38,7 +38,7 @@ def load_py_sx(evaluator_env: dict) -> dict: exprs = parse_all(source) # Import the evaluator - from shared.sx.evaluator import evaluate, make_env + from shared.sx.ref.sx_ref import evaluate, make_env env = make_env() for expr in exprs: @@ -60,7 +60,7 @@ def extract_defines(source: str) -> list[tuple[str, list]]: def main(): - from shared.sx.evaluator import evaluate + from shared.sx.ref.sx_ref import evaluate # Load py.sx into evaluator env = load_py_sx({}) diff --git a/shared/sx/ref/sx_ref.py b/shared/sx/ref/sx_ref.py index 427dced1..020daa3e 100644 --- a/shared/sx/ref/sx_ref.py +++ b/shared/sx/ref/sx_ref.py @@ -559,7 +559,7 @@ def sx_expr_source(x): try: - from shared.sx.evaluator import EvalError + from shared.sx.types import EvalError except ImportError: class EvalError(Exception): pass diff --git a/shared/sx/resolver.py b/shared/sx/resolver.py index 93622499..2b90c6fa 100644 --- a/shared/sx/resolver.py +++ b/shared/sx/resolver.py @@ -31,7 +31,7 @@ import asyncio from typing import Any from .types import Component, Keyword, Lambda, NIL, Symbol -from .evaluator import _eval as _raw_eval, _trampoline +from .ref.sx_ref import eval_expr as _raw_eval, trampoline as _trampoline def _eval(expr, env): """Evaluate and unwrap thunks — all resolver.py _eval calls are non-tail.""" diff --git a/shared/sx/tests/run.py b/shared/sx/tests/run.py index c679d59a..abdc791a 100644 --- a/shared/sx/tests/run.py +++ b/shared/sx/tests/run.py @@ -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 --- diff --git a/shared/sx/tests/test_bootstrapper.py b/shared/sx/tests/test_bootstrapper.py index 4fb6416b..635943ea 100644 --- a/shared/sx/tests/test_bootstrapper.py +++ b/shared/sx/tests/test_bootstrapper.py @@ -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 diff --git a/shared/sx/tests/test_deps.py b/shared/sx/tests/test_deps.py index c5ca5262..a648a1ea 100644 --- a/shared/sx/tests/test_deps.py +++ b/shared/sx/tests/test_deps.py @@ -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) diff --git a/shared/sx/tests/test_io_detection.py b/shared/sx/tests/test_io_detection.py index bac513ae..412c8996 100644 --- a/shared/sx/tests/test_io_detection.py +++ b/shared/sx/tests/test_io_detection.py @@ -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) diff --git a/shared/sx/tests/test_io_proxy.py b/shared/sx/tests/test_io_proxy.py index 1516797f..8ccfd366 100644 --- a/shared/sx/tests/test_io_proxy.py +++ b/shared/sx/tests/test_io_proxy.py @@ -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)) diff --git a/shared/sx/tests/test_page_data.py b/shared/sx/tests/test_page_data.py index 7cd22800..5789814d 100644 --- a/shared/sx/tests/test_page_data.py +++ b/shared/sx/tests/test_page_data.py @@ -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)) diff --git a/shared/sx/tests/test_parity.py b/shared/sx/tests/test_parity.py index db809b02..0f079f51 100644 --- a/shared/sx/tests/test_parity.py +++ b/shared/sx/tests/test_parity.py @@ -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") diff --git a/shared/sx/tests/test_sx_js.py b/shared/sx/tests/test_sx_js.py index 135adaec..43b59aae 100644 --- a/shared/sx/tests/test_sx_js.py +++ b/shared/sx/tests/test_sx_js.py @@ -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" diff --git a/shared/sx/tests/test_sx_spec.py b/shared/sx/tests/test_sx_spec.py index db885cce..51592761 100644 --- a/shared/sx/tests/test_sx_spec.py +++ b/shared/sx/tests/test_sx_spec.py @@ -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))))) diff --git a/shared/sx/types.py b/shared/sx/types.py index 97f0e2fc..4ade10f4 100644 --- a/shared/sx/types.py +++ b/shared/sx/types.py @@ -375,6 +375,15 @@ class _ShiftSignal(BaseException): self.env = env +# --------------------------------------------------------------------------- +# EvalError +# --------------------------------------------------------------------------- + +class EvalError(Exception): + """Error during expression evaluation.""" + pass + + # --------------------------------------------------------------------------- # Type alias # --------------------------------------------------------------------------- diff --git a/sx/sxc/pages/helpers.py b/sx/sxc/pages/helpers.py index 102147d5..f8c6deea 100644 --- a/sx/sxc/pages/helpers.py +++ b/sx/sxc/pages/helpers.py @@ -314,7 +314,7 @@ def _self_hosting_data(ref_dir: str) -> dict: import os from shared.sx.parser import parse_all from shared.sx.types import Symbol - from shared.sx.evaluator import evaluate, make_env + from shared.sx.ref.sx_ref import evaluate, make_env from shared.sx.ref.bootstrap_py import extract_defines, compile_ref_to_py, PyEmitter try: @@ -387,7 +387,7 @@ def _js_self_hosting_data(ref_dir: str) -> dict: """Run js.sx live: load into evaluator, translate all spec defines.""" import os from shared.sx.types import Symbol - from shared.sx.evaluator import evaluate + from shared.sx.ref.sx_ref import evaluate from shared.sx.ref.run_js_sx import load_js_sx from shared.sx.ref.platform_js import extract_defines @@ -661,7 +661,7 @@ def _run_spec_tests() -> dict: import os import time 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 ref_dir = os.path.join(os.path.dirname(__file__), "..", "..", "shared", "sx", "ref") if not os.path.isdir(ref_dir): @@ -735,7 +735,7 @@ def _run_modular_tests(spec_name: str) -> dict: import os import time 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 from shared.sx.types import Symbol, Keyword, Lambda, NIL ref_dir = os.path.join(os.path.dirname(__file__), "..", "..", "shared", "sx", "ref") @@ -817,7 +817,7 @@ def _run_modular_tests(spec_name: str) -> dict: def _call_sx(fn, args, caller_env): if isinstance(fn, Lambda): - from shared.sx.evaluator import _call_lambda + from shared.sx.ref.sx_ref import call_lambda as _call_lambda return _trampoline(_call_lambda(fn, list(args), caller_env)) return fn(*args) @@ -1165,9 +1165,9 @@ def _prove_data() -> dict: """ import time from shared.sx.parser import parse_all - from shared.sx.evaluator import evaluate + from shared.sx.ref.sx_ref import evaluate from shared.sx.primitives import all_primitives - from shared.sx.evaluator import _trampoline, _call_lambda + from shared.sx.ref.sx_ref import trampoline as _trampoline, call_lambda as _call_lambda env = all_primitives()