From 680cdf62aa6eb58185de340305d3185dcc19094b Mon Sep 17 00:00:00 2001 From: giles Date: Thu, 14 May 2026 06:52:58 +0000 Subject: [PATCH] =?UTF-8?q?scheme:=20Phase=2011=20=E2=80=94=20test.sh=20+?= =?UTF-8?q?=20scoreboard?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit lib/scheme/test.sh — single-process test runner. Loads parser/eval/ runtime + lib/guest/reflective/env.sx once, then for each test suite loads its file and calls its (*-tests-run!) function. Parses the {:passed N :failed N ...} dict output and aggregates. Usage: bash lib/scheme/test.sh # summary bash lib/scheme/test.sh -v # per-suite breakdown Output: "ok 296/296 scheme-on-sx tests passed (9 suites)" lib/scheme/scoreboard.md — per-suite passing counts, phase status, deferred items, reflective-kit consumption ledger. The scoreboard documents the chisel value of the Scheme port: three reflective kits unlocked (env.sx — already extracted with Scheme as third consumer; evaluator.sx + quoting.sx — second- consumer-ready for extraction whenever a follow-up commit is run). Loop status: 11 phases done (1, 2, 3, 3.5, 4, 5abc, 6ab, 7, 8, 9, 10, 11). Two deferred (6c hygiene, full call/cc-wind interaction). 296 tests, 1830 LoC of Scheme implementation. Zero substrate fixes required across the loop. --- lib/scheme/scoreboard.md | 83 ++++++++++++++++++++++++++++++++++++ lib/scheme/test.sh | 92 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 175 insertions(+) create mode 100644 lib/scheme/scoreboard.md create mode 100755 lib/scheme/test.sh diff --git a/lib/scheme/scoreboard.md b/lib/scheme/scoreboard.md new file mode 100644 index 00000000..477319e0 --- /dev/null +++ b/lib/scheme/scoreboard.md @@ -0,0 +1,83 @@ +# Scheme-on-SX Scoreboard + +**All tests pass: 296 / 296 across 9 suites.** + +## Per-suite breakdown + +| Suite | Passing | Covers | +|-------------|--------:|--------| +| parse | 62 | R7RS lexer: numbers, strings, chars, vectors, lists, quote/quasi/unquote, line/block/datum comments | +| eval | 23 | Self-evaluating literals, symbol lookup, quote, primitive application | +| syntax | 49 | if/define/set!/begin/lambda/closures + let/let*/cond/when/unless/and/or | +| runtime | 78 | Standard env: variadic arithmetic, type predicates, list/string/char/vector ops, higher-order combinators | +| control | 25 | call/cc (escape), raise/guard/with-exception-handler, dynamic-wind | +| macros | 20 | define-syntax / syntax-rules incl. tail-rest ellipsis | +| reflection | 23 | eval / interaction-environment / null-environment / scheme-report-environment + quasiquote runtime | +| records | 9 | define-record-type with constructor / predicate / accessor / mutator | +| modules | 7 | define-library + import (minimal — no cond-expand / include / rename) | + +## Phases implemented + +- [x] Phase 1 — Parser +- [x] Phase 2 — Evaluator + env.sx **third consumer** +- [x] Phase 3 — Syntactic operators (if/lambda/define/set!/begin) +- [x] Phase 3.5 — let/let*/cond/when/unless/and/or +- [x] Phase 4 — Standard environment + set! cond-bugfix +- [x] Phase 5a — call/cc +- [x] Phase 5b — exceptions (raise/guard/with-exception-handler/error) +- [x] Phase 5c — dynamic-wind (basic, no call/cc-escape tracking) +- [x] Phase 6a — define-syntax + syntax-rules (no ellipsis) +- [x] Phase 6b — syntax-rules ellipsis (tail-rest, single variable) +- [x] Phase 7 — eval / interaction-environment **second consumer for evaluator.sx** +- [x] Phase 8 — define-library + import (minimal) +- [x] Phase 9 — define-record-type +- [x] Phase 10 — quasiquote runtime **second consumer for quoting.sx** +- [x] Phase 11 — test.sh + scoreboard + +## Deferred + +- **Phase 6c — hygiene** (scope-set / lifted-symbol Dybvig-style algorithm). + Would be the second consumer for the deferred `lib/guest/reflective/hygiene.sx` + research-grade kit. Current macros work for common patterns but can capture + caller bindings if a macro introduces same-named identifiers. + +- **Nested quasiquote depth tracking** — `` `\`x\` `` is not properly depth-aware; + matches Kernel's deferred state. + +- **R7RS module rich features**: cond-expand, include, include-library-declarations, + `(only ...)` / `(except ...)` / `(prefix ...)` / `(rename ...)` import sets. + +- **Dotted-pair `(a b . rest)` syntax** at the parser level. Lambda rest-args + currently use the `(lambda args ...)` form (bare symbol) instead. + +- **Full call/cc + dynamic-wind interaction**: re-entry/re-exit of dynamic + extents via continuations is not tracked. Pure-eval programs work; call/cc- + heavy code with dynamic-wind interleaving doesn't. + +## Reflective-kit consumption (chisel ledger) + +This Scheme port unlocks three reflective-kit extractions from the kernel-on-sx +loop's original six-candidate list: + +| Kit | Status | +|----------------------|---------------------------------------------| +| env.sx | **Extracted** (third consumer; no adapter) | +| class-chain.sx | n/a (no OO in Scheme) | +| evaluator.sx | **Unblocked** (second consumer ready) | +| quoting.sx | **Unblocked** (second consumer ready) | +| hygiene.sx | Awaiting Phase 6c (research-grade) | +| combiner.sx | n/a (no fexprs in Scheme) | +| short-circuit.sx | n/a (Scheme `and`/`or` are syntactic, not operative) | + +The kit-extraction commits themselves are follow-on work — kit code is staged +in the proposed sections of `plans/kernel-on-sx.md`; Scheme's consumer code +satisfies the two-consumer rule for `evaluator.sx` and `quoting.sx`. + +## Substrate stats + +- parser.sx — 281 LoC +- eval.sx — ~970 LoC +- runtime.sx — ~580 LoC +- Tests — ~1500 LoC across 9 files + +Total Scheme implementation ≈ 1830 LoC. diff --git a/lib/scheme/test.sh b/lib/scheme/test.sh new file mode 100755 index 00000000..130116d5 --- /dev/null +++ b/lib/scheme/test.sh @@ -0,0 +1,92 @@ +#!/usr/bin/env bash +# Scheme-on-SX test runner — runs all tests in one sx_server process. +# +# Usage: +# bash lib/scheme/test.sh # run all suites +# bash lib/scheme/test.sh -v # verbose (list each suite) + +set -uo pipefail +cd "$(git rev-parse --show-toplevel)" + +SX_SERVER="${SX_SERVER:-hosts/ocaml/_build/default/bin/sx_server.exe}" +if [ ! -x "$SX_SERVER" ]; then + SX_SERVER="/root/rose-ash/hosts/ocaml/_build/default/bin/sx_server.exe" +fi +if [ ! -x "$SX_SERVER" ]; then + echo "ERROR: sx_server.exe not found." >&2 + exit 1 +fi + +VERBOSE="${1:-}" + +# Suites: NAME RUNNER-FN PATH +SUITES=( + "parse scm-tests-run! lib/scheme/tests/parse.sx" + "eval scm-eval-tests-run! lib/scheme/tests/eval.sx" + "syntax scm-syn-tests-run! lib/scheme/tests/syntax.sx" + "runtime scm-rt-tests-run! lib/scheme/tests/runtime.sx" + "control scm-ctl-tests-run! lib/scheme/tests/control.sx" + "macros scm-mac-tests-run! lib/scheme/tests/macros.sx" + "reflection scm-ref-tests-run! lib/scheme/tests/reflection.sx" + "records scm-rec-tests-run! lib/scheme/tests/records.sx" + "modules scm-mod-tests-run! lib/scheme/tests/modules.sx" +) + +TMPFILE=$(mktemp); trap "rm -f $TMPFILE" EXIT +EPOCH=1 + +emit_load () { echo "(epoch $EPOCH)"; echo "(load \"$1\")"; EPOCH=$((EPOCH+1)); } +emit_eval () { echo "(epoch $EPOCH)"; echo "(eval \"$1\")"; EPOCH=$((EPOCH+1)); } + +{ + emit_load "lib/guest/lex.sx" + emit_load "lib/guest/reflective/env.sx" + emit_load "lib/scheme/parser.sx" + emit_load "lib/scheme/eval.sx" + emit_load "lib/scheme/runtime.sx" + for SUITE in "${SUITES[@]}"; do + read -r _NAME _RUNNER FILE <<< "$SUITE" + emit_load "$FILE" + emit_eval "($_RUNNER)" + done +} > "$TMPFILE" + +OUTPUT=$(timeout 180 "$SX_SERVER" < "$TMPFILE" 2>&1 || true) + +# Final 9 outputs are the suite results. Parse each "{:passed N :failed N ..}". +TOTAL_PASS=0 +TOTAL_FAIL=0 +FAILED_SUITES=() + +# Walk the output; for each suite, extract the {:passed ...} line. +# The dict format from sx_server is {:passed N :failed N :total N :fails (...)}. +LAST_DICT_LINES=$(echo "$OUTPUT" | grep -E '^\{:' || true) + +I=0 +while read -r LINE; do + [ -z "$LINE" ] && continue + P=$(echo "$LINE" | grep -oE ':passed [0-9]+' | awk '{print $2}') + F=$(echo "$LINE" | grep -oE ':failed [0-9]+' | awk '{print $2}') + [ -z "$P" ] && P=0 + [ -z "$F" ] && F=0 + SUITE_INFO="${SUITES[$I]}" + SUITE_NAME=$(echo "$SUITE_INFO" | awk '{print $1}') + TOTAL_PASS=$((TOTAL_PASS + P)) + TOTAL_FAIL=$((TOTAL_FAIL + F)) + if [ "$F" -gt 0 ]; then + FAILED_SUITES+=("$SUITE_NAME: $P/$((P+F))") + printf 'X %-12s %d/%d\n' "$SUITE_NAME" "$P" "$((P+F))" + elif [ "$VERBOSE" = "-v" ]; then + printf 'ok %-12s %d passed\n' "$SUITE_NAME" "$P" + fi + I=$((I+1)) +done <<< "$LAST_DICT_LINES" + +TOTAL=$((TOTAL_PASS + TOTAL_FAIL)) +if [ $TOTAL_FAIL -eq 0 ]; then + echo "ok $TOTAL_PASS/$TOTAL scheme-on-sx tests passed (${#SUITES[@]} suites)" +else + echo "FAIL $TOTAL_PASS/$TOTAL passed, $TOTAL_FAIL failed:" + for S in "${FAILED_SUITES[@]}"; do echo " $S"; done + exit 1 +fi