From 9f71706bc8227c7673af969ba79b2bd0a559e50b Mon Sep 17 00:00:00 2001 From: giles Date: Sat, 25 Apr 2026 19:26:26 +0000 Subject: [PATCH] haskell: conformance.sh runner + scoreboard.json + scoreboard.md (16/16, 5/5) Co-Authored-By: Claude Sonnet 4.6 --- lib/haskell/conformance.sh | 140 ++++++++++++++++++++++++++++++++++++ lib/haskell/scoreboard.json | 12 ++++ lib/haskell/scoreboard.md | 12 ++++ 3 files changed, 164 insertions(+) create mode 100755 lib/haskell/conformance.sh create mode 100644 lib/haskell/scoreboard.json create mode 100644 lib/haskell/scoreboard.md diff --git a/lib/haskell/conformance.sh b/lib/haskell/conformance.sh new file mode 100755 index 00000000..0c1bb36d --- /dev/null +++ b/lib/haskell/conformance.sh @@ -0,0 +1,140 @@ +#!/usr/bin/env bash +# lib/haskell/conformance.sh — run the 5 classic-program test suites. +# Writes lib/haskell/scoreboard.json and lib/haskell/scoreboard.md. +# +# Usage: +# bash lib/haskell/conformance.sh # run + write scoreboards +# bash lib/haskell/conformance.sh --check # run only, exit 1 on failure + +set -euo pipefail +cd "$(git rev-parse --show-toplevel)" + +SX_SERVER="hosts/ocaml/_build/default/bin/sx_server.exe" +if [ ! -x "$SX_SERVER" ]; then + MAIN_ROOT=$(git worktree list | head -1 | awk '{print $1}') + if [ -x "$MAIN_ROOT/$SX_SERVER" ]; then + SX_SERVER="$MAIN_ROOT/$SX_SERVER" + else + echo "ERROR: sx_server.exe not found. Run: cd hosts/ocaml && dune build" + exit 1 + fi +fi + +PROGRAMS=(fib sieve quicksort nqueens calculator) +PASS_COUNTS=() +FAIL_COUNTS=() + +run_suite() { + local prog="$1" + local FILE="lib/haskell/tests/program-${prog}.sx" + local TMPFILE + TMPFILE=$(mktemp) + cat > "$TMPFILE" <&1 || true) + rm -f "$TMPFILE" + + local LINE + LINE=$(echo "$OUTPUT" | awk '/^\(ok-len 3 / {getline; print; exit}') + if [ -z "$LINE" ]; then + LINE=$(echo "$OUTPUT" | grep -E '^\(ok 3 \([0-9]+ [0-9]+\)\)' | tail -1 \ + | sed -E 's/^\(ok 3 //; s/\)$//' || true) + fi + if [ -z "$LINE" ]; then + echo "0 1" + else + local P F + P=$(echo "$LINE" | sed -E 's/^\(([0-9]+) ([0-9]+)\).*/\1/' || echo "0") + F=$(echo "$LINE" | sed -E 's/^\(([0-9]+) ([0-9]+)\).*/\2/' || echo "1") + echo "$P $F" + fi +} + +for prog in "${PROGRAMS[@]}"; do + RESULT=$(run_suite "$prog") + P=$(echo "$RESULT" | cut -d' ' -f1) + F=$(echo "$RESULT" | cut -d' ' -f2) + PASS_COUNTS+=("$P") + FAIL_COUNTS+=("$F") + T=$((P + F)) + if [ "$F" -eq 0 ]; then + printf '✓ %-14s %d/%d\n' "${prog}.hs" "$P" "$T" + else + printf '✗ %-14s %d/%d\n' "${prog}.hs" "$P" "$T" + fi +done + +TOTAL_PASS=0 +TOTAL_FAIL=0 +PROG_PASS=0 +for i in "${!PROGRAMS[@]}"; do + TOTAL_PASS=$((TOTAL_PASS + PASS_COUNTS[i])) + TOTAL_FAIL=$((TOTAL_FAIL + FAIL_COUNTS[i])) + [ "${FAIL_COUNTS[$i]}" -eq 0 ] && PROG_PASS=$((PROG_PASS + 1)) +done +PROG_TOTAL=${#PROGRAMS[@]} + +echo "" +echo "Classic programs: ${TOTAL_PASS}/$((TOTAL_PASS + TOTAL_FAIL)) tests | ${PROG_PASS}/${PROG_TOTAL} programs passing" + +if [[ "${1:-}" == "--check" ]]; then + [ $TOTAL_FAIL -eq 0 ] + exit $? +fi + +DATE=$(date '+%Y-%m-%d') + +# scoreboard.json +{ + printf '{\n' + printf ' "date": "%s",\n' "$DATE" + printf ' "total_pass": %d,\n' "$TOTAL_PASS" + printf ' "total_fail": %d,\n' "$TOTAL_FAIL" + printf ' "programs": {\n' + last=$((${#PROGRAMS[@]} - 1)) + for i in "${!PROGRAMS[@]}"; do + prog="${PROGRAMS[$i]}" + if [ $i -lt $last ]; then + printf ' "%s": {"pass": %d, "fail": %d},\n' "$prog" "${PASS_COUNTS[$i]}" "${FAIL_COUNTS[$i]}" + else + printf ' "%s": {"pass": %d, "fail": %d}\n' "$prog" "${PASS_COUNTS[$i]}" "${FAIL_COUNTS[$i]}" + fi + done + printf ' }\n' + printf '}\n' +} > lib/haskell/scoreboard.json + +# scoreboard.md +{ + printf '# Haskell-on-SX Scoreboard\n\n' + printf 'Updated %s · Phase 3 (laziness + classic programs)\n\n' "$DATE" + printf '| Program | Tests | Status |\n' + printf '|---------|-------|--------|\n' + for i in "${!PROGRAMS[@]}"; do + prog="${PROGRAMS[$i]}" + P=${PASS_COUNTS[$i]} + F=${FAIL_COUNTS[$i]} + T=$((P + F)) + [ "$F" -eq 0 ] && STATUS="✓" || STATUS="✗" + printf '| %s | %d/%d | %s |\n' "${prog}.hs" "$P" "$T" "$STATUS" + done + printf '| **Total** | **%d/%d** | **%d/%d programs** |\n' \ + "$TOTAL_PASS" "$((TOTAL_PASS + TOTAL_FAIL))" "$PROG_PASS" "$PROG_TOTAL" +} > lib/haskell/scoreboard.md + +echo "Wrote lib/haskell/scoreboard.json and lib/haskell/scoreboard.md" +[ $TOTAL_FAIL -eq 0 ] diff --git a/lib/haskell/scoreboard.json b/lib/haskell/scoreboard.json new file mode 100644 index 00000000..be956d92 --- /dev/null +++ b/lib/haskell/scoreboard.json @@ -0,0 +1,12 @@ +{ + "date": "2026-04-25", + "total_pass": 16, + "total_fail": 0, + "programs": { + "fib": {"pass": 2, "fail": 0}, + "sieve": {"pass": 2, "fail": 0}, + "quicksort": {"pass": 5, "fail": 0}, + "nqueens": {"pass": 2, "fail": 0}, + "calculator": {"pass": 5, "fail": 0} + } +} diff --git a/lib/haskell/scoreboard.md b/lib/haskell/scoreboard.md new file mode 100644 index 00000000..e514d919 --- /dev/null +++ b/lib/haskell/scoreboard.md @@ -0,0 +1,12 @@ +# Haskell-on-SX Scoreboard + +Updated 2026-04-25 · Phase 3 (laziness + classic programs) + +| Program | Tests | Status | +|---------|-------|--------| +| fib.hs | 2/2 | ✓ | +| sieve.hs | 2/2 | ✓ | +| quicksort.hs | 5/5 | ✓ | +| nqueens.hs | 2/2 | ✓ | +| calculator.hs | 5/5 | ✓ | +| **Total** | **16/16** | **5/5 programs** |