#!/usr/bin/env python3 """ Bootstrap runner: execute py.sx against spec files to produce sx_ref.py. This is the G1 bootstrapper — py.sx (SX-to-Python translator written in SX) is loaded into the Python evaluator, which then uses it to translate the spec .sx files into Python. The output should be identical to: python bootstrap_py.py Usage: python run_py_sx.py > sx_ref_g1.py """ from __future__ import annotations import os import sys _HERE = os.path.dirname(os.path.abspath(__file__)) _PROJECT = os.path.abspath(os.path.join(_HERE, "..", "..", "..")) sys.path.insert(0, _PROJECT) from shared.sx.parser import parse_all from shared.sx.types import Symbol from shared.sx.ref.platform_py import ( PREAMBLE, PLATFORM_PY, PRIMITIVES_PY_PRE, PRIMITIVES_PY_POST, PLATFORM_DEPS_PY, FIXUPS_PY, CONTINUATIONS_PY, _assemble_primitives_py, public_api_py, ) def load_py_sx(evaluator_env: dict) -> dict: """Load py.sx into an evaluator environment and return it.""" py_sx_path = os.path.join(_HERE, "py.sx") with open(py_sx_path) as f: source = f.read() exprs = parse_all(source) # Import the evaluator from shared.sx.ref.sx_ref import evaluate, make_env env = make_env() for expr in exprs: evaluate(expr, env) return env def extract_defines(source: str) -> list[tuple[str, list]]: """Parse .sx source, return list of (name, define-expr) for top-level defines.""" exprs = parse_all(source) defines = [] for expr in exprs: if isinstance(expr, list) and expr and isinstance(expr[0], Symbol): if expr[0].name == "define": name = expr[1].name if isinstance(expr[1], Symbol) else str(expr[1]) defines.append((name, expr)) return defines def main(): from shared.sx.ref.sx_ref import evaluate # Load py.sx into evaluator env = load_py_sx({}) # Get the py-translate-file function py_translate_file = env.get("py-translate-file") if py_translate_file is None: print("ERROR: py-translate-file not found in py.sx environment", file=sys.stderr) sys.exit(1) # Same file list and order as bootstrap_py.py compile_ref_to_py() sx_files = [ ("eval.sx", "eval"), ("forms.sx", "forms (server definition forms)"), ("render.sx", "render (core)"), ("adapter-html.sx", "adapter-html"), ("adapter-sx.sx", "adapter-sx"), ("deps.sx", "deps (component dependency analysis)"), ("signals.sx", "signals (reactive signal runtime)"), ] # Build output — static sections are identical parts = [] parts.append(PREAMBLE) parts.append(PLATFORM_PY) parts.append(PRIMITIVES_PY_PRE) parts.append(_assemble_primitives_py(None)) parts.append(PRIMITIVES_PY_POST) parts.append(PLATFORM_DEPS_PY) # Translate each spec file using py.sx for filename, label in sx_files: filepath = os.path.join(_HERE, filename) if not os.path.exists(filepath): continue with open(filepath) as f: src = f.read() defines = extract_defines(src) # Convert defines to SX-compatible format: list of [name, expr] pairs sx_defines = [[name, expr] for name, expr in defines] parts.append(f"\n# === Transpiled from {label} ===\n") # Bind defines as data in env to avoid evaluator trying to execute AST env["_defines"] = sx_defines result = evaluate( [Symbol("py-translate-file"), Symbol("_defines")], env, ) parts.append(result) parts.append(FIXUPS_PY) parts.append(public_api_py(True, True, True)) print("\n".join(parts)) if __name__ == "__main__": main()