haskell: conformance.sh runner + scoreboard.json + scoreboard.md (16/16, 5/5)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
140
lib/haskell/conformance.sh
Executable file
140
lib/haskell/conformance.sh
Executable file
@@ -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" <<EPOCHS
|
||||
(epoch 1)
|
||||
(load "lib/haskell/tokenizer.sx")
|
||||
(load "lib/haskell/layout.sx")
|
||||
(load "lib/haskell/parser.sx")
|
||||
(load "lib/haskell/desugar.sx")
|
||||
(load "lib/haskell/runtime.sx")
|
||||
(load "lib/haskell/match.sx")
|
||||
(load "lib/haskell/eval.sx")
|
||||
(load "lib/haskell/testlib.sx")
|
||||
(epoch 2)
|
||||
(load "$FILE")
|
||||
(epoch 3)
|
||||
(eval "(list hk-test-pass hk-test-fail)")
|
||||
EPOCHS
|
||||
local OUTPUT
|
||||
OUTPUT=$(timeout 120 "$SX_SERVER" < "$TMPFILE" 2>&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 ]
|
||||
12
lib/haskell/scoreboard.json
Normal file
12
lib/haskell/scoreboard.json
Normal file
@@ -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}
|
||||
}
|
||||
}
|
||||
12
lib/haskell/scoreboard.md
Normal file
12
lib/haskell/scoreboard.md
Normal file
@@ -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** |
|
||||
Reference in New Issue
Block a user