Merge worktree-typed: increment 2 — rings 2-4
Some checks failed
Build and Deploy / build-and-deploy (push) Has been cancelled
Some checks failed
Build and Deploy / build-and-deploy (push) Has been cancelled
This commit is contained in:
@@ -64,6 +64,9 @@
|
||||
(when (> (get stats "render-count") 0)
|
||||
(span :class "bg-sky-100 text-sky-700 px-2 py-0.5 rounded"
|
||||
(str (get stats "render-count") " render")))
|
||||
(when (> (get stats "test-total") 0)
|
||||
(span :class "bg-violet-100 text-violet-700 px-2 py-0.5 rounded"
|
||||
(str (get stats "test-total") " tests")))
|
||||
(span :class "bg-stone-100 text-stone-500 px-2 py-0.5 rounded"
|
||||
(str (get stats "lines") " lines"))))
|
||||
|
||||
@@ -85,7 +88,7 @@
|
||||
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Define card — one function/constant
|
||||
;; Define card — one function/constant with all five rings
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~spec-explorer-define (&key d)
|
||||
@@ -105,10 +108,22 @@
|
||||
(when (not (empty? (get d "params")))
|
||||
(~spec-param-list :params (get d "params")))
|
||||
|
||||
;; Translation panels
|
||||
;; Ring 2: Translation panels (SX + Python + JavaScript + Z3)
|
||||
(~spec-ring-translations
|
||||
:source (get d "source")
|
||||
:python (get d "python"))))
|
||||
:python (get d "python")
|
||||
:javascript (get d "javascript")
|
||||
:z3 (get d "z3"))
|
||||
|
||||
;; Ring 3: Cross-references
|
||||
(when (not (empty? (get d "refs")))
|
||||
(~spec-ring-bridge :refs (get d "refs")))
|
||||
|
||||
;; Ring 4: Tests
|
||||
(when (> (get d "test-count") 0)
|
||||
(~spec-ring-runtime
|
||||
:tests (get d "tests")
|
||||
:test-count (get d "test-count")))))
|
||||
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
@@ -146,29 +161,76 @@
|
||||
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Translation panels (Ring 2)
|
||||
;; Ring 2: Translation panels (nucleus + bootstrapper)
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~spec-ring-translations (&key source python)
|
||||
(defcomp ~spec-ring-translations (&key source python javascript z3)
|
||||
(when (not (= source ""))
|
||||
(div :class "mt-3 border border-stone-200 rounded-lg overflow-hidden"
|
||||
;; SX source — always open
|
||||
;; SX source — Ring 1: the nucleus (always open)
|
||||
(details :open "true"
|
||||
(summary :class "px-3 py-1.5 bg-stone-50 text-xs font-medium text-stone-600 cursor-pointer"
|
||||
"SX")
|
||||
(pre :class "text-xs p-3 overflow-x-auto bg-white"
|
||||
(code (highlight source "sx"))))
|
||||
;; Python
|
||||
;; Python — Ring 2: bootstrapper
|
||||
(when python
|
||||
(details
|
||||
(summary :class "px-3 py-1.5 bg-stone-50 text-xs font-medium text-stone-600 cursor-pointer border-t border-stone-200"
|
||||
"Python")
|
||||
(pre :class "text-xs p-3 overflow-x-auto bg-white"
|
||||
(code (highlight python "python"))))))))
|
||||
(code (highlight python "python")))))
|
||||
;; JavaScript — Ring 2: bootstrapper
|
||||
(when javascript
|
||||
(details
|
||||
(summary :class "px-3 py-1.5 bg-stone-50 text-xs font-medium text-stone-600 cursor-pointer border-t border-stone-200"
|
||||
"JavaScript")
|
||||
(pre :class "text-xs p-3 overflow-x-auto bg-white"
|
||||
(code (highlight javascript "javascript")))))
|
||||
;; Z3 / SMT-LIB — Ring 2: formal translation
|
||||
(when z3
|
||||
(details
|
||||
(summary :class "px-3 py-1.5 bg-stone-50 text-xs font-medium text-stone-600 cursor-pointer border-t border-stone-200"
|
||||
"Z3 / SMT-LIB")
|
||||
(pre :class "text-xs p-3 overflow-x-auto bg-white"
|
||||
(code (highlight z3 "lisp"))))))))
|
||||
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Platform interface table (Ring 3)
|
||||
;; Ring 3: Cross-references (bridge)
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~spec-ring-bridge (&key refs)
|
||||
(div :class "mt-2"
|
||||
(span :class "text-xs font-medium text-stone-500" "References")
|
||||
(div :class "flex flex-wrap gap-1 mt-1"
|
||||
(map (fn (ref)
|
||||
(a :href (str "#fn-" ref)
|
||||
:class "text-xs px-1.5 py-0.5 rounded bg-stone-100 text-stone-600 font-mono hover:bg-stone-200"
|
||||
ref))
|
||||
refs))))
|
||||
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Ring 4: Tests (runtime)
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~spec-ring-runtime (&key tests test-count)
|
||||
(div :class "mt-2"
|
||||
(div :class "flex items-center gap-1"
|
||||
(span :class "text-xs font-medium text-stone-500" "Tests")
|
||||
(span :class "text-xs px-1.5 py-0.5 rounded bg-violet-100 text-violet-700"
|
||||
(str test-count)))
|
||||
(ul :class "mt-1 text-xs text-stone-500 list-none"
|
||||
(map (fn (t)
|
||||
(li :class "flex items-center gap-1"
|
||||
(span :class "text-green-500 text-xs" "●")
|
||||
(get t "name")))
|
||||
tests))))
|
||||
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Platform interface table (Ring 3 overview)
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~spec-platform-interface (&key items)
|
||||
|
||||
@@ -172,6 +172,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.
|
||||
|
||||
@@ -369,6 +526,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,
|
||||
@@ -377,6 +562,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)
|
||||
|
||||
@@ -401,6 +591,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"])
|
||||
@@ -435,6 +639,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,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user