Initial commit of the lib/js/ tree and plans/ directory. A previous session left template-string work in progress — 278/280 unit tests pass (2 failing: tpl part-count off-by-one, escaped-backtick ident lookup). test262-runner.py and scoreboard are placeholders (0/8 with 7 timeouts); fixing the runner is the next queue item.
131 lines
3.8 KiB
Bash
Executable File
131 lines
3.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Cherry-picked test262 conformance runner for JS-on-SX.
|
|
# Walks lib/js/test262-slice/**/*.js, evaluates each via js-eval,
|
|
# and compares against the sibling .expected file (substring match).
|
|
#
|
|
# Usage:
|
|
# bash lib/js/conformance.sh # summary only
|
|
# bash lib/js/conformance.sh -v # per-test pass/fail
|
|
|
|
set -uo pipefail
|
|
cd "$(git rev-parse --show-toplevel)"
|
|
|
|
SX_SERVER="hosts/ocaml/_build/default/bin/sx_server.exe"
|
|
if [ ! -x "$SX_SERVER" ]; then
|
|
echo "ERROR: $SX_SERVER not found. Run: cd hosts/ocaml && dune build"
|
|
exit 1
|
|
fi
|
|
|
|
VERBOSE="${1:-}"
|
|
SLICE_DIR="lib/js/test262-slice"
|
|
PASS=0
|
|
FAIL=0
|
|
ERRORS=""
|
|
|
|
# Find all .js fixtures (sorted for stable output).
|
|
# Skip README.md and similar.
|
|
mapfile -t FIXTURES < <(find "$SLICE_DIR" -type f -name '*.js' | sort)
|
|
|
|
if [ ${#FIXTURES[@]} -eq 0 ]; then
|
|
echo "No fixtures found in $SLICE_DIR"
|
|
exit 1
|
|
fi
|
|
|
|
# Build one big batch script: load everything once, then one epoch per
|
|
# fixture. Avoids the ~200ms boot cost of starting the server for each
|
|
# test.
|
|
|
|
TMPFILE=$(mktemp)
|
|
trap "rm -f $TMPFILE" EXIT
|
|
|
|
{
|
|
echo '(epoch 1)'
|
|
echo '(load "lib/r7rs.sx")'
|
|
echo '(epoch 2)'
|
|
echo '(load "lib/js/lexer.sx")'
|
|
echo '(epoch 3)'
|
|
echo '(load "lib/js/parser.sx")'
|
|
echo '(epoch 4)'
|
|
echo '(load "lib/js/transpile.sx")'
|
|
echo '(epoch 5)'
|
|
echo '(load "lib/js/runtime.sx")'
|
|
|
|
epoch=100
|
|
for f in "${FIXTURES[@]}"; do
|
|
# Read source, strip trailing newline, then escape for *two*
|
|
# nested SX string literals: the outer epoch `(eval "…")` and
|
|
# the inner `(js-eval "…")` that it wraps.
|
|
#
|
|
# Source char → final stream char
|
|
# \ → \\\\ (outer: becomes \\ ; inner: becomes \)
|
|
# " → \\\" (outer: becomes \" ; inner: becomes ")
|
|
# nl → \\n (SX newline escape, survives both levels)
|
|
src=$(python3 -c '
|
|
import sys
|
|
s = open(sys.argv[1], "r", encoding="utf-8").read().rstrip("\n")
|
|
# Two nested SX string literals: outer eval wraps inner js-eval.
|
|
# Escape once for inner (JS source → SX inner string literal):
|
|
inner = s.replace("\\", "\\\\").replace("\"", "\\\"").replace("\n", "\\n")
|
|
# Escape the result again for the outer SX string literal:
|
|
outer = inner.replace("\\", "\\\\").replace("\"", "\\\"")
|
|
sys.stdout.write(outer)
|
|
' "$f")
|
|
echo "(epoch $epoch)"
|
|
echo "(eval \"(js-eval \\\"$src\\\")\")"
|
|
epoch=$((epoch + 1))
|
|
done
|
|
} > "$TMPFILE"
|
|
|
|
OUTPUT=$(timeout 180 "$SX_SERVER" < "$TMPFILE" 2>/dev/null)
|
|
|
|
# Iterate fixtures with the same epoch sequence and check each.
|
|
epoch=100
|
|
for f in "${FIXTURES[@]}"; do
|
|
expected=$(cat "${f%.js}.expected" | sed -e 's/[[:space:]]*$//' | head -n 1)
|
|
name="${f#${SLICE_DIR}/}"
|
|
name="${name%.js}"
|
|
|
|
# Actual output lives on the line after "(ok-len $epoch N)" or on
|
|
# "(ok $epoch VAL)" for short values. Errors surface as "(error …)".
|
|
actual=$(echo "$OUTPUT" | awk -v e="$epoch" '
|
|
$0 ~ ("^\\(ok-len "e" ") { getline; print; exit }
|
|
$0 ~ ("^\\(ok "e" ") { sub("^\\(ok "e" ", ""); sub(")$", ""); print; exit }
|
|
$0 ~ ("^\\(error "e" ") { print; exit }
|
|
')
|
|
[ -z "$actual" ] && actual="<no output>"
|
|
|
|
if echo "$actual" | grep -qF -- "$expected"; then
|
|
PASS=$((PASS + 1))
|
|
[ "$VERBOSE" = "-v" ] && echo " ✓ $name"
|
|
else
|
|
FAIL=$((FAIL + 1))
|
|
ERRORS+=" ✗ $name
|
|
expected: $expected
|
|
actual: $actual
|
|
"
|
|
[ "$VERBOSE" = "-v" ] && echo " ✗ $name (expected: $expected, actual: $actual)"
|
|
fi
|
|
|
|
epoch=$((epoch + 1))
|
|
done
|
|
|
|
TOTAL=$((PASS + FAIL))
|
|
PCT=$(awk "BEGIN{printf \"%.1f\", ($PASS/$TOTAL)*100}")
|
|
|
|
echo
|
|
if [ $FAIL -eq 0 ]; then
|
|
echo "✓ $PASS/$TOTAL test262-slice tests passed ($PCT%)"
|
|
else
|
|
echo "✗ $PASS/$TOTAL passed, $FAIL failed ($PCT%):"
|
|
[ "$VERBOSE" != "-v" ] && echo "$ERRORS"
|
|
fi
|
|
|
|
# Phase 5 target: ≥50% pass.
|
|
TARGET=50
|
|
if (( $(echo "$PCT >= $TARGET" | bc -l 2>/dev/null || python3 -c "print($PCT >= $TARGET)") )); then
|
|
exit 0
|
|
else
|
|
echo "(below target of ${TARGET}%)"
|
|
exit 1
|
|
fi
|