SX-to-JavaScript translator written in SX itself. When executed by the
Python evaluator against the spec files, produces output identical to
the hand-written bootstrap_js.py JSEmitter.
- 1,382 lines, 61 defines
- 431/431 defines match across all 22 spec files (G0 == G1)
- 267 defines in the standard compilation, 151,763 bytes identical
- camelCase mangling, var declarations, function(){} syntax
- Self-tail-recursive optimization (zero-arg → while loops)
- JS-native statement patterns: d[k]=v, arr.push(x), f.name=x
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
102 lines
2.9 KiB
Python
102 lines
2.9 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Bootstrap runner: execute js.sx against spec files to produce sx-ref.js.
|
|
|
|
This is the G1 bootstrapper — js.sx (SX-to-JavaScript translator written in SX)
|
|
is loaded into the Python evaluator, which then uses it to translate the
|
|
spec .sx files into JavaScript.
|
|
|
|
The output (transpiled defines only) should be identical to what
|
|
bootstrap_js.py's JSEmitter produces.
|
|
|
|
Usage:
|
|
python run_js_sx.py > /tmp/sx_ref_g1.js
|
|
"""
|
|
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
|
|
|
|
|
|
def load_js_sx() -> dict:
|
|
"""Load js.sx into an evaluator environment and return it."""
|
|
js_sx_path = os.path.join(_HERE, "js.sx")
|
|
with open(js_sx_path) as f:
|
|
source = f.read()
|
|
|
|
exprs = parse_all(source)
|
|
|
|
from shared.sx.evaluator 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.evaluator import evaluate
|
|
|
|
# Load js.sx into evaluator
|
|
env = load_js_sx()
|
|
|
|
# Same file list and order as bootstrap_js.py compile_ref_to_js() with all adapters
|
|
sx_files = [
|
|
("eval.sx", "eval"),
|
|
("render.sx", "render (core)"),
|
|
("parser.sx", "parser"),
|
|
("adapter-html.sx", "adapter-html"),
|
|
("adapter-sx.sx", "adapter-sx"),
|
|
("adapter-dom.sx", "adapter-dom"),
|
|
("engine.sx", "engine"),
|
|
("orchestration.sx", "orchestration"),
|
|
("boot.sx", "boot"),
|
|
("deps.sx", "deps (component dependency analysis)"),
|
|
("router.sx", "router (client-side route matching)"),
|
|
("signals.sx", "signals (reactive signal runtime)"),
|
|
]
|
|
|
|
# Translate each spec file using js.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
|
|
sx_defines = [[name, expr] for name, expr in defines]
|
|
|
|
print(f"\n // === Transpiled from {label} ===\n")
|
|
env["_defines"] = sx_defines
|
|
result = evaluate(
|
|
[Symbol("js-translate-file"), Symbol("_defines")],
|
|
env,
|
|
)
|
|
print(result)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|