conformance: migrate erlang onto shared driver (dict, 761/761 parity)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 1m12s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 1m12s
Erlang's suites load into one session and each exposes a pass counter plus a
*count* (total) counter rather than a fail counter, so MODE=dict fits directly:
each suite's runner is a dict literal {:passed P :failed (- count P) :total count}.
No driver change needed (dict mode already supports arbitrary runner expressions).
conformance.conf + 3-line shim; historical scoreboard schema preserved.
Parity verified 761/761 (0 fail), every suite matching baseline.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
68
lib/erlang/conformance.conf
Normal file
68
lib/erlang/conformance.conf
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
# Erlang-on-SX conformance config — sourced by lib/guest/conformance.sh.
|
||||||
|
#
|
||||||
|
# Erlang's suites load into one session and each exposes a pass counter and a
|
||||||
|
# *count* (total) counter — not a fail counter. dict mode fits cleanly: each
|
||||||
|
# runner is a dict literal computing :failed as count - pass. (counters mode
|
||||||
|
# would misread the count counter as a fail counter.)
|
||||||
|
|
||||||
|
LANG_NAME=erlang
|
||||||
|
MODE=dict
|
||||||
|
|
||||||
|
PRELOADS=(
|
||||||
|
lib/erlang/tokenizer.sx
|
||||||
|
lib/erlang/parser.sx
|
||||||
|
lib/erlang/parser-core.sx
|
||||||
|
lib/erlang/parser-expr.sx
|
||||||
|
lib/erlang/parser-module.sx
|
||||||
|
lib/erlang/transpile.sx
|
||||||
|
lib/erlang/runtime.sx
|
||||||
|
lib/erlang/vm/dispatcher.sx
|
||||||
|
)
|
||||||
|
|
||||||
|
# name:file:(runner) — runner is a dict literal {:passed :failed :total}.
|
||||||
|
SUITES=(
|
||||||
|
"tokenize:lib/erlang/tests/tokenize.sx:{:passed er-test-pass :failed (- er-test-count er-test-pass) :total er-test-count}"
|
||||||
|
"parse:lib/erlang/tests/parse.sx:{:passed er-parse-test-pass :failed (- er-parse-test-count er-parse-test-pass) :total er-parse-test-count}"
|
||||||
|
"eval:lib/erlang/tests/eval.sx:{:passed er-eval-test-pass :failed (- er-eval-test-count er-eval-test-pass) :total er-eval-test-count}"
|
||||||
|
"runtime:lib/erlang/tests/runtime.sx:{:passed er-rt-test-pass :failed (- er-rt-test-count er-rt-test-pass) :total er-rt-test-count}"
|
||||||
|
"ring:lib/erlang/tests/programs/ring.sx:{:passed er-ring-test-pass :failed (- er-ring-test-count er-ring-test-pass) :total er-ring-test-count}"
|
||||||
|
"ping-pong:lib/erlang/tests/programs/ping_pong.sx:{:passed er-pp-test-pass :failed (- er-pp-test-count er-pp-test-pass) :total er-pp-test-count}"
|
||||||
|
"bank:lib/erlang/tests/programs/bank.sx:{:passed er-bank-test-pass :failed (- er-bank-test-count er-bank-test-pass) :total er-bank-test-count}"
|
||||||
|
"echo:lib/erlang/tests/programs/echo.sx:{:passed er-echo-test-pass :failed (- er-echo-test-count er-echo-test-pass) :total er-echo-test-count}"
|
||||||
|
"fib:lib/erlang/tests/programs/fib_server.sx:{:passed er-fib-test-pass :failed (- er-fib-test-count er-fib-test-pass) :total er-fib-test-count}"
|
||||||
|
"ffi:lib/erlang/tests/ffi.sx:{:passed er-ffi-test-pass :failed (- er-ffi-test-count er-ffi-test-pass) :total er-ffi-test-count}"
|
||||||
|
"vm:lib/erlang/tests/vm.sx:{:passed er-vm-test-pass :failed (- er-vm-test-count er-vm-test-pass) :total er-vm-test-count}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Preserve the historical scoreboard schema so consumers of
|
||||||
|
# lib/erlang/scoreboard.json keep working.
|
||||||
|
emit_scoreboard_json() {
|
||||||
|
local n=${#GC_NAMES[@]} i status
|
||||||
|
printf '{\n'
|
||||||
|
printf ' "language": "erlang",\n'
|
||||||
|
printf ' "total_pass": %d,\n' "$GC_TOTAL_PASS"
|
||||||
|
printf ' "total": %d,\n' "$GC_TOTAL"
|
||||||
|
printf ' "suites": ['
|
||||||
|
for ((i=0; i<n; i++)); do
|
||||||
|
[ "$i" -gt 0 ] && printf ','
|
||||||
|
status="ok"; [ "${GC_FAIL[$i]}" -gt 0 ] && status="fail"
|
||||||
|
printf '\n {"name":"%s","pass":%d,"total":%d,"status":"%s"}' \
|
||||||
|
"${GC_NAMES[$i]}" "${GC_PASS[$i]}" "${GC_TOTAL_S[$i]}" "$status"
|
||||||
|
done
|
||||||
|
printf '\n ]\n'
|
||||||
|
printf '}\n'
|
||||||
|
}
|
||||||
|
|
||||||
|
emit_scoreboard_md() {
|
||||||
|
local n=${#GC_NAMES[@]} i marker
|
||||||
|
printf '# Erlang-on-SX Scoreboard\n\n'
|
||||||
|
printf '**Total: %d / %d tests passing**\n\n' "$GC_TOTAL_PASS" "$GC_TOTAL"
|
||||||
|
printf '| | Suite | Pass | Total |\n'
|
||||||
|
printf '|---|---|---|---|\n'
|
||||||
|
for ((i=0; i<n; i++)); do
|
||||||
|
marker="✅"; [ "${GC_FAIL[$i]}" -gt 0 ] && marker="❌"
|
||||||
|
printf '| %s | %s | %d | %d |\n' \
|
||||||
|
"$marker" "${GC_NAMES[$i]}" "${GC_PASS[$i]}" "${GC_TOTAL_S[$i]}"
|
||||||
|
done
|
||||||
|
printf '\nGenerated by `lib/erlang/conformance.sh`.\n'
|
||||||
|
}
|
||||||
@@ -1,162 +1,3 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Erlang-on-SX conformance runner.
|
# Thin wrapper — see lib/guest/conformance.sh and lib/erlang/conformance.conf.
|
||||||
#
|
exec bash "$(dirname "$0")/../guest/conformance.sh" "$(dirname "$0")/conformance.conf" "$@"
|
||||||
# Loads every erlang test suite via the epoch protocol, collects
|
|
||||||
# pass/fail counts, and writes lib/erlang/scoreboard.json + .md.
|
|
||||||
#
|
|
||||||
# Usage:
|
|
||||||
# bash lib/erlang/conformance.sh # run all suites
|
|
||||||
# bash lib/erlang/conformance.sh -v # verbose per-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:-}"
|
|
||||||
TMPFILE=$(mktemp)
|
|
||||||
OUTFILE=$(mktemp)
|
|
||||||
trap "rm -f $TMPFILE $OUTFILE" EXIT
|
|
||||||
|
|
||||||
# Each suite: name | counter pass | counter total
|
|
||||||
SUITES=(
|
|
||||||
"tokenize|er-test-pass|er-test-count"
|
|
||||||
"parse|er-parse-test-pass|er-parse-test-count"
|
|
||||||
"eval|er-eval-test-pass|er-eval-test-count"
|
|
||||||
"runtime|er-rt-test-pass|er-rt-test-count"
|
|
||||||
"ring|er-ring-test-pass|er-ring-test-count"
|
|
||||||
"ping-pong|er-pp-test-pass|er-pp-test-count"
|
|
||||||
"bank|er-bank-test-pass|er-bank-test-count"
|
|
||||||
"echo|er-echo-test-pass|er-echo-test-count"
|
|
||||||
"fib|er-fib-test-pass|er-fib-test-count"
|
|
||||||
"ffi|er-ffi-test-pass|er-ffi-test-count"
|
|
||||||
"vm|er-vm-test-pass|er-vm-test-count"
|
|
||||||
)
|
|
||||||
|
|
||||||
cat > "$TMPFILE" << 'EPOCHS'
|
|
||||||
(epoch 1)
|
|
||||||
(load "lib/erlang/tokenizer.sx")
|
|
||||||
(load "lib/erlang/parser.sx")
|
|
||||||
(load "lib/erlang/parser-core.sx")
|
|
||||||
(load "lib/erlang/parser-expr.sx")
|
|
||||||
(load "lib/erlang/parser-module.sx")
|
|
||||||
(load "lib/erlang/transpile.sx")
|
|
||||||
(load "lib/erlang/runtime.sx")
|
|
||||||
(load "lib/erlang/tests/tokenize.sx")
|
|
||||||
(load "lib/erlang/tests/parse.sx")
|
|
||||||
(load "lib/erlang/tests/eval.sx")
|
|
||||||
(load "lib/erlang/tests/runtime.sx")
|
|
||||||
(load "lib/erlang/tests/programs/ring.sx")
|
|
||||||
(load "lib/erlang/tests/programs/ping_pong.sx")
|
|
||||||
(load "lib/erlang/tests/programs/bank.sx")
|
|
||||||
(load "lib/erlang/tests/programs/echo.sx")
|
|
||||||
(load "lib/erlang/tests/programs/fib_server.sx")
|
|
||||||
(load "lib/erlang/vm/dispatcher.sx")
|
|
||||||
(load "lib/erlang/tests/ffi.sx")
|
|
||||||
(load "lib/erlang/tests/vm.sx")
|
|
||||||
(epoch 100)
|
|
||||||
(eval "(list er-test-pass er-test-count)")
|
|
||||||
(epoch 101)
|
|
||||||
(eval "(list er-parse-test-pass er-parse-test-count)")
|
|
||||||
(epoch 102)
|
|
||||||
(eval "(list er-eval-test-pass er-eval-test-count)")
|
|
||||||
(epoch 103)
|
|
||||||
(eval "(list er-rt-test-pass er-rt-test-count)")
|
|
||||||
(epoch 104)
|
|
||||||
(eval "(list er-ring-test-pass er-ring-test-count)")
|
|
||||||
(epoch 105)
|
|
||||||
(eval "(list er-pp-test-pass er-pp-test-count)")
|
|
||||||
(epoch 106)
|
|
||||||
(eval "(list er-bank-test-pass er-bank-test-count)")
|
|
||||||
(epoch 107)
|
|
||||||
(eval "(list er-echo-test-pass er-echo-test-count)")
|
|
||||||
(epoch 108)
|
|
||||||
(eval "(list er-fib-test-pass er-fib-test-count)")
|
|
||||||
(epoch 109)
|
|
||||||
(eval "(list er-ffi-test-pass er-ffi-test-count)")
|
|
||||||
(epoch 110)
|
|
||||||
(eval "(list er-vm-test-pass er-vm-test-count)")
|
|
||||||
EPOCHS
|
|
||||||
|
|
||||||
timeout 600 "$SX_SERVER" < "$TMPFILE" > "$OUTFILE" 2>&1
|
|
||||||
|
|
||||||
# Parse "(N M)" from the line after each "(ok-len <epoch> ...)" marker.
|
|
||||||
parse_pair() {
|
|
||||||
local epoch="$1"
|
|
||||||
local line
|
|
||||||
line=$(grep -A1 "^(ok-len $epoch " "$OUTFILE" | tail -1)
|
|
||||||
echo "$line" | sed -E 's/[()]//g'
|
|
||||||
}
|
|
||||||
|
|
||||||
TOTAL_PASS=0
|
|
||||||
TOTAL_COUNT=0
|
|
||||||
JSON_SUITES=""
|
|
||||||
MD_ROWS=""
|
|
||||||
|
|
||||||
idx=0
|
|
||||||
for entry in "${SUITES[@]}"; do
|
|
||||||
name="${entry%%|*}"
|
|
||||||
epoch=$((100 + idx))
|
|
||||||
pair=$(parse_pair "$epoch")
|
|
||||||
pass=$(echo "$pair" | awk '{print $1}')
|
|
||||||
count=$(echo "$pair" | awk '{print $2}')
|
|
||||||
if [ -z "$pass" ] || [ -z "$count" ]; then
|
|
||||||
pass=0
|
|
||||||
count=0
|
|
||||||
fi
|
|
||||||
TOTAL_PASS=$((TOTAL_PASS + pass))
|
|
||||||
TOTAL_COUNT=$((TOTAL_COUNT + count))
|
|
||||||
status="ok"
|
|
||||||
marker="✅"
|
|
||||||
if [ "$pass" != "$count" ]; then
|
|
||||||
status="fail"
|
|
||||||
marker="❌"
|
|
||||||
fi
|
|
||||||
if [ "$VERBOSE" = "-v" ]; then
|
|
||||||
printf " %-12s %s/%s\n" "$name" "$pass" "$count"
|
|
||||||
fi
|
|
||||||
if [ -n "$JSON_SUITES" ]; then JSON_SUITES+=","; fi
|
|
||||||
JSON_SUITES+=$'\n '
|
|
||||||
JSON_SUITES+="{\"name\":\"$name\",\"pass\":$pass,\"total\":$count,\"status\":\"$status\"}"
|
|
||||||
MD_ROWS+="| $marker | $name | $pass | $count |"$'\n'
|
|
||||||
idx=$((idx + 1))
|
|
||||||
done
|
|
||||||
|
|
||||||
printf '\nErlang-on-SX conformance: %d / %d\n' "$TOTAL_PASS" "$TOTAL_COUNT"
|
|
||||||
|
|
||||||
# scoreboard.json
|
|
||||||
cat > lib/erlang/scoreboard.json <<JSON
|
|
||||||
{
|
|
||||||
"language": "erlang",
|
|
||||||
"total_pass": $TOTAL_PASS,
|
|
||||||
"total": $TOTAL_COUNT,
|
|
||||||
"suites": [$JSON_SUITES
|
|
||||||
]
|
|
||||||
}
|
|
||||||
JSON
|
|
||||||
|
|
||||||
# scoreboard.md
|
|
||||||
cat > lib/erlang/scoreboard.md <<MD
|
|
||||||
# Erlang-on-SX Scoreboard
|
|
||||||
|
|
||||||
**Total: ${TOTAL_PASS} / ${TOTAL_COUNT} tests passing**
|
|
||||||
|
|
||||||
| | Suite | Pass | Total |
|
|
||||||
|---|---|---|---|
|
|
||||||
$MD_ROWS
|
|
||||||
|
|
||||||
Generated by \`lib/erlang/conformance.sh\`.
|
|
||||||
MD
|
|
||||||
|
|
||||||
if [ "$TOTAL_PASS" -eq "$TOTAL_COUNT" ]; then
|
|
||||||
exit 0
|
|
||||||
else
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|||||||
@@ -16,5 +16,4 @@
|
|||||||
| ✅ | ffi | 37 | 37 |
|
| ✅ | ffi | 37 | 37 |
|
||||||
| ✅ | vm | 78 | 78 |
|
| ✅ | vm | 78 | 78 |
|
||||||
|
|
||||||
|
|
||||||
Generated by `lib/erlang/conformance.sh`.
|
Generated by `lib/erlang/conformance.sh`.
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ Blocked with specifics and move to the next candidate next iteration.
|
|||||||
## Checklist
|
## Checklist
|
||||||
|
|
||||||
- [x] common-lisp — migrated 487/487 (counters; driver extended for per-suite counters+preloads)
|
- [x] common-lisp — migrated 487/487 (counters; driver extended for per-suite counters+preloads)
|
||||||
- [ ] erlang
|
- [x] erlang — migrated 761/761 (dict; pass/count → :failed = count-pass)
|
||||||
- [ ] feed
|
- [ ] feed
|
||||||
- [ ] forth
|
- [ ] forth
|
||||||
- [ ] go
|
- [ ] go
|
||||||
@@ -100,6 +100,12 @@ Blocked with specifics and move to the next candidate next iteration.
|
|||||||
|
|
||||||
## Progress log (newest first)
|
## Progress log (newest first)
|
||||||
|
|
||||||
|
- 2026-06-07 — erlang: migrated to `MODE=dict`, 761/761 exact parity (tokenize 62,
|
||||||
|
parse 52, eval 408, runtime 93, ring 4, ping-pong 4, bank 8, echo 7, fib 8, ffi 37,
|
||||||
|
vm 78). Erlang exposes pass + *count* (total) counters, not pass/fail, so each suite's
|
||||||
|
dict-literal runner computes `:failed (- count pass)`. Loads in one session (matches
|
||||||
|
dict mode), so no driver change needed — only conformance.conf + shim. Kept historical
|
||||||
|
scoreboard schema (language/total_pass/total/suites[name,pass,total,status]).
|
||||||
- 2026-06-07 — common-lisp: UNBLOCKED + migrated. Extended the shared driver's
|
- 2026-06-07 — common-lisp: UNBLOCKED + migrated. Extended the shared driver's
|
||||||
`MODE=counters` (lib/guest/conformance.sh) with a backward-compatible SUITES format
|
`MODE=counters` (lib/guest/conformance.sh) with a backward-compatible SUITES format
|
||||||
`name:file[:pass-var:fail-var[:extra-preload ...]]` — optional per-suite counter
|
`name:file[:pass-var:fail-var[:extra-preload ...]]` — optional per-suite counter
|
||||||
|
|||||||
Reference in New Issue
Block a user