js-on-sx: baseline commit (278/280 unit, 148/148 slice, runner stub)
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.
This commit is contained in:
130
lib/js/conformance.sh
Executable file
130
lib/js/conformance.sh
Executable file
@@ -0,0 +1,130 @@
|
||||
#!/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
|
||||
Reference in New Issue
Block a user