Files
rose-ash/shared/sx/tests/run.py
giles e9d86d628b Make test.sx self-executing: evaluators run it directly, no codegen
test.sx now defines deftest/defsuite as macros. Any host that provides
5 platform functions (try-call, report-pass, report-fail, push-suite,
pop-suite) can evaluate the file directly — no bootstrap compilation
step needed for JS.

- Added defmacro for deftest (wraps body in thunk, catches via try-call)
- Added defmacro for defsuite (push/pop suite context stack)
- Created run.js: sx-browser.js evaluates test.sx directly (81/81 pass)
- Created run.py: Python evaluator evaluates test.sx directly (81/81 pass)
- Deleted bootstrap_test_js.py and generated test_sx_spec.js
- Updated testing docs page to reflect self-executing architecture

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 10:50:28 +00:00

93 lines
2.0 KiB
Python

#!/usr/bin/env python3
"""Run test.sx directly against the Python SX evaluator.
The Python evaluator parses and evaluates test.sx — SX tests itself.
This script provides only platform functions (error catching, reporting).
Usage: python shared/sx/tests/run.py
"""
from __future__ import annotations
import os
import sys
import traceback
_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.evaluator import _eval, _trampoline
# --- Test state ---
suite_stack: list[str] = []
passed = 0
failed = 0
test_num = 0
def try_call(thunk):
"""Call an SX thunk, catching errors."""
try:
_trampoline(_eval([thunk], {}))
return {"ok": True}
except Exception as e:
return {"ok": False, "error": str(e)}
def report_pass(name):
global passed, test_num
test_num += 1
passed += 1
full_name = " > ".join(suite_stack + [name])
print(f"ok {test_num} - {full_name}")
def report_fail(name, error):
global failed, test_num
test_num += 1
failed += 1
full_name = " > ".join(suite_stack + [name])
print(f"not ok {test_num} - {full_name}")
print(f" # {error}")
def push_suite(name):
suite_stack.append(name)
def pop_suite():
suite_stack.pop()
def main():
env = {
"try-call": try_call,
"report-pass": report_pass,
"report-fail": report_fail,
"push-suite": push_suite,
"pop-suite": pop_suite,
}
test_sx = os.path.join(_HERE, "..", "ref", "test.sx")
with open(test_sx) as f:
src = f.read()
exprs = parse_all(src)
print("TAP version 13")
for expr in exprs:
_trampoline(_eval(expr, env))
print()
print(f"1..{test_num}")
print(f"# tests {passed + failed}")
print(f"# pass {passed}")
if failed > 0:
print(f"# fail {failed}")
sys.exit(1)
if __name__ == "__main__":
main()