Add Rings 2-4: JS/Z3 translations, cross-refs, test matching

Ring 2 (bootstrapper): JavaScript translation via js.sx,
Z3/SMT-LIB translation via reader_z3.py. Each define card
now shows SX, Python, JavaScript, and Z3 collapsible panels.

Ring 3 (bridge): Cross-reference index maps function names
to spec files. Each define shows which other spec functions
it references.

Ring 4 (runtime): Test file parsing extracts defsuite/deftest
structure. Fuzzy name matching links tests to functions.
Stats bar shows test count.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-12 01:45:28 +00:00
parent 95e42f9a87
commit 1fce4970fb
2 changed files with 276 additions and 9 deletions

View File

@@ -142,6 +142,163 @@ def _read_spec_file(filename: str) -> str:
return ";; spec file not found"
# ---------------------------------------------------------------------------
# Spec explorer — translation + cross-reference helpers
# ---------------------------------------------------------------------------
_JS_SX_ENV = None # cached js.sx evaluator env
def _js_translate_define(expr: list, name: str) -> str | None:
"""Translate a single define expression to JavaScript via js.sx."""
global _JS_SX_ENV
if _JS_SX_ENV is None:
from shared.sx.ref.run_js_sx import load_js_sx
_JS_SX_ENV = load_js_sx()
from shared.sx.ref.sx_ref import evaluate
from shared.sx.types import Symbol
env = dict(_JS_SX_ENV)
env["_defines"] = [[name, expr]]
result = evaluate([Symbol("js-translate-file"), Symbol("_defines")], env)
if result and isinstance(result, str) and result.strip():
return result.strip()
return None
def _z3_translate_define(expr: list) -> str | None:
"""Translate a single define expression to SMT-LIB via z3.sx."""
from shared.sx.ref.reader_z3 import z3_translate
result = z3_translate(expr)
if result and isinstance(result, str) and result.strip():
return result.strip()
return None
_SPEC_INDEX: dict[str, str] | None = None # function name → spec slug
def _build_spec_index() -> dict[str, str]:
"""Build a global index mapping function names to spec file slugs."""
global _SPEC_INDEX
if _SPEC_INDEX is not None:
return _SPEC_INDEX
import os
import glob as globmod
from shared.sx.parser import parse_all
from shared.sx.types import Symbol, Keyword
ref_dir = os.path.join(os.path.dirname(__file__), "..", "..", "shared", "sx", "ref")
if not os.path.isdir(ref_dir):
ref_dir = "/app/shared/sx/ref"
index: dict[str, str] = {}
for fp in globmod.glob(os.path.join(ref_dir, "*.sx")):
basename = os.path.basename(fp)
if basename.startswith("test-"):
continue
slug = basename.replace(".sx", "")
try:
with open(fp, encoding="utf-8") as f:
content = f.read()
for expr in parse_all(content):
if not isinstance(expr, list) or len(expr) < 2:
continue
if not isinstance(expr[0], Symbol):
continue
head = expr[0].name
if head in ("define", "define-async"):
name = expr[1].name if isinstance(expr[1], Symbol) else str(expr[1])
index[name] = slug
except Exception:
continue
_SPEC_INDEX = index
return _SPEC_INDEX
# Test file → spec file mapping
_SPEC_TO_TEST = {
"signals.sx": "test-signals.sx",
"eval.sx": "test-eval.sx",
"parser.sx": "test-parser.sx",
"render.sx": "test-render.sx",
"engine.sx": "test-engine.sx",
"orchestration.sx": "test-orchestration.sx",
"router.sx": "test-router.sx",
"deps.sx": "test-deps.sx",
"adapter-sx.sx": "test-aser.sx",
"types.sx": "test-types.sx",
}
def _extract_tests_for_spec(filename: str) -> list[dict]:
"""Extract test suites/cases from the corresponding test file."""
import os
from shared.sx.parser import parse_all
from shared.sx.types import Symbol
test_file = _SPEC_TO_TEST.get(filename)
if not test_file:
return []
ref_dir = os.path.join(os.path.dirname(__file__), "..", "..", "shared", "sx", "ref")
if not os.path.isdir(ref_dir):
ref_dir = "/app/shared/sx/ref"
test_path = os.path.join(ref_dir, test_file)
try:
with open(test_path, encoding="utf-8") as f:
content = f.read()
exprs = parse_all(content)
except Exception:
return []
tests: list[dict] = []
for expr in exprs:
if not isinstance(expr, list) or len(expr) < 3:
continue
if not isinstance(expr[0], Symbol):
continue
if expr[0].name != "defsuite":
continue
suite_name = expr[1] if isinstance(expr[1], str) else str(expr[1])
test_names = []
for child in expr[2:]:
if isinstance(child, list) and len(child) >= 2:
if isinstance(child[0], Symbol) and child[0].name == "deftest":
tname = child[1] if isinstance(child[1], str) else str(child[1])
test_names.append(tname)
tests.append({"suite": suite_name, "tests": test_names})
return tests
def _match_tests_to_function(fn_name: str, all_tests: list[dict]) -> list[dict]:
"""Match test suites to a function by fuzzy name matching."""
matched = []
fn_lower = fn_name.lower().replace("-", " ").replace("!", "").replace("?", "")
fn_words = set(fn_lower.split())
for suite in all_tests:
suite_lower = suite["suite"].lower()
# Match if function name appears in suite name or suite name contains function
if fn_lower in suite_lower or any(w in suite_lower for w in fn_words if len(w) > 2):
matched.append(suite)
return matched
def _collect_symbols(expr) -> set[str]:
"""Recursively collect all Symbol names referenced in an expression."""
from shared.sx.types import Symbol
result: set[str] = set()
if isinstance(expr, Symbol):
result.add(expr.name)
elif isinstance(expr, list):
for item in expr:
result |= _collect_symbols(item)
elif isinstance(expr, dict):
for v in expr.values():
result |= _collect_symbols(v)
return result
def _spec_explorer_data(filename: str, title: str = "", desc: str = "") -> dict | None:
"""Parse a spec file into structured metadata for the spec explorer.
@@ -339,6 +496,34 @@ def _spec_explorer_data(filename: str, title: str = "", desc: str = "") -> dict
except Exception:
pass
# --- JavaScript translation ---
js_code = None
try:
js_code = _js_translate_define(expr, name)
except Exception:
pass
# --- Z3/SMT-LIB translation ---
z3_code = None
try:
z3_code = _z3_translate_define(expr)
except Exception:
pass
# --- Cross-references ---
refs = []
platform_deps = []
try:
spec_index = _build_spec_index()
body_symbols = _collect_symbols(expr)
own_names = {name}
for sym in body_symbols - own_names:
if sym in spec_index:
refs.append(sym)
# Symbols not in any spec file might be platform primitives
except Exception:
pass
define_entry = {
"name": name,
"kind": kind,
@@ -347,6 +532,11 @@ def _spec_explorer_data(filename: str, title: str = "", desc: str = "") -> dict
"source": src,
"line": line_num,
"python": py_code,
"javascript": js_code,
"z3": z3_code,
"refs": refs,
"tests": [],
"test-count": 0,
}
all_defines.append(define_entry)
@@ -371,6 +561,20 @@ def _spec_explorer_data(filename: str, title: str = "", desc: str = "") -> dict
target_section = s
target_section["defines"].append(d)
# --- Test matching ---
all_tests = _extract_tests_for_spec(filename)
test_total = 0
for d in all_defines:
matched = _match_tests_to_function(d["name"], all_tests)
if matched:
test_names = []
for suite in matched:
for t in suite["tests"]:
test_names.append({"name": t, "suite": suite["suite"]})
d["tests"] = test_names
d["test-count"] = len(test_names)
test_total += len(test_names)
# --- Stats ---
pure_count = sum(1 for d in all_defines if not d["effects"])
mutation_count = sum(1 for d in all_defines if "mutation" in d["effects"])
@@ -405,6 +609,7 @@ def _spec_explorer_data(filename: str, title: str = "", desc: str = "") -> dict
"io-count": io_count,
"render-count": render_count,
"lines": len(lines),
"test-total": test_total,
},
}