Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 26s
Phase 5 (goroutines + channels) opens.
lib/go/sched.sx is the **independent implementation** referenced by
plans/lib-guest-scheduler.md — the first-consumer cut whose realised
shape will inform the eventual sister kit.
Channel representation:
(list :go-chan SEND-FN RECV-FN CLOSED?-FN CLOSE!-FN)
Each closure shares a mutable `buf` (a list mutated via append! and
set!) and a `closed` flag. Channel identity is closure-instance —
two `make()` calls produce distinct values per Go spec § Channel types.
Primitive API in sched.sx:
go-make-chan / go-chan? / go-chan-send! / go-chan-recv! /
go-chan-closed? / go-chan-close!
Eval integration in eval.sx:
* `make` and `close` added as builtins. v0 `make()` takes no args
and returns an unbounded-buffer channel.
* `:send` stmt → go-chan-send! on the channel.
* Unary `<-` recv on channel values → go-chan-recv!. `:empty`
sentinel converted to nil (stand-in for blocking semantics).
* `:go expr` → synchronous eval (v0 limitation, see sched.sx
header).
**v0 concurrency model — synchronous goroutines.** SX doesn't expose
first-class continuations to guest code, so v0 runs `go f()`
immediately and depends on the spawned goroutine running to
completion before the main goroutine receives. This is the right
semantics for the simple producer/consumer patterns covered here.
True preemption with blocking send/recv is Phase 5b — requires either
a CEK-style trampolining eval rewrite or kit-level continuation
support. Logged in sched.sx header and in the sister-plan diary.
Runtime suite (12 tests):
* 6 direct API tests: identity, FIFO order, closed-flag
* 6 source-level: make + send + recv, go ping-pong, close,
multi-goroutine fan-in, worker-with-result
Sister-plan scheduler diary updated with the channel-as-closure-
bundle insight and the v0 synchronous-spawn caveat.
runtime 12/12, total 469/469.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
140 lines
3.4 KiB
Bash
Executable File
140 lines
3.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Go-on-SX conformance runner.
|
|
#
|
|
# Loads every Go-on-SX test suite via the epoch protocol, collects
|
|
# pass/fail counts, and writes lib/go/scoreboard.json + .md.
|
|
#
|
|
# Usage:
|
|
# bash lib/go/conformance.sh # run all suites
|
|
# bash lib/go/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 | pass-counter | total-counter
|
|
SUITES=(
|
|
"lex|go-test-pass|go-test-count"
|
|
"parse|go-parse-test-pass|go-parse-test-count"
|
|
"types|go-types-test-pass|go-types-test-count"
|
|
"eval|go-eval-test-pass|go-eval-test-count"
|
|
"runtime|go-rt-test-pass|go-rt-test-count"
|
|
)
|
|
|
|
cat > "$TMPFILE" <<'EPOCHS'
|
|
(epoch 1)
|
|
(load "lib/guest/lex.sx")
|
|
(load "lib/guest/ast.sx")
|
|
(load "lib/guest/pratt.sx")
|
|
(load "lib/go/lex.sx")
|
|
(load "lib/go/parse.sx")
|
|
(load "lib/go/types.sx")
|
|
(load "lib/go/sched.sx")
|
|
(load "lib/go/eval.sx")
|
|
(load "lib/go/tests/lex.sx")
|
|
(load "lib/go/tests/parse.sx")
|
|
(load "lib/go/tests/types.sx")
|
|
(load "lib/go/tests/eval.sx")
|
|
(load "lib/go/tests/runtime.sx")
|
|
EPOCHS
|
|
|
|
idx=0
|
|
for entry in "${SUITES[@]}"; do
|
|
name="${entry%%|*}"
|
|
pass_var=$(echo "$entry" | awk -F'|' '{print $2}')
|
|
total_var=$(echo "$entry" | awk -F'|' '{print $3}')
|
|
epoch=$((100 + idx))
|
|
echo "(epoch $epoch)" >> "$TMPFILE"
|
|
echo "(eval \"(list $pass_var $total_var)\")" >> "$TMPFILE"
|
|
idx=$((idx + 1))
|
|
done
|
|
|
|
"$SX_SERVER" < "$TMPFILE" > "$OUTFILE" 2>&1
|
|
|
|
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 '\nGo-on-SX conformance: %d / %d\n' "$TOTAL_PASS" "$TOTAL_COUNT"
|
|
|
|
cat > lib/go/scoreboard.json <<JSON
|
|
{
|
|
"language": "go",
|
|
"total_pass": $TOTAL_PASS,
|
|
"total": $TOTAL_COUNT,
|
|
"suites": [$JSON_SUITES,
|
|
{"name":"stdlib","pass":0,"total":0,"status":"pending"},
|
|
{"name":"e2e","pass":0,"total":0,"status":"pending"}
|
|
]
|
|
}
|
|
JSON
|
|
|
|
cat > lib/go/scoreboard.md <<MD
|
|
# Go-on-SX Scoreboard
|
|
|
|
**Total: ${TOTAL_PASS} / ${TOTAL_COUNT} tests passing**
|
|
|
|
| | Suite | Pass | Total |
|
|
|---|---|---|---|
|
|
$MD_ROWS| ⬜ | stdlib | 0 | 0 |
|
|
| ⬜ | e2e | 0 | 0 |
|
|
|
|
Generated by \`lib/go/conformance.sh\`.
|
|
MD
|
|
|
|
if [ "$TOTAL_PASS" -eq "$TOTAL_COUNT" ]; then
|
|
exit 0
|
|
else
|
|
exit 1
|
|
fi
|