Asm gains the file I/O surface Python and C already had, unlocking 9/9 cells of the portal producer×consumer matrix (previously 6/9). asm: - (load "path") — mmaps file, swaps input source, loops scheme_read+eval, restores on exit. Nestable. Uses SYS_LSEEK + SYS_MUNMAP. - Output ports: (open-output-file), (close-port), (port?). Encoded as SPECIAL values ≥ 1000 (fd = (val>>3) − PORT_SPECIAL_BASE), no tag-bit expansion needed. - (display), (write), (newline) accept optional port arg; printer writes via output_fd global, swapped by port-aware builtins. - (write-file path content) / (file->string path) — bytes in/out. c, py: (write-file) / (file->string) added for parity. tests: 131 asm (up 23), 189 functional (up 8, shared py+c), tests/portal-cross-test.sh exercises 3×3 save×load matrix.
73 lines
2.5 KiB
Bash
Executable file
73 lines
2.5 KiB
Bash
Executable file
#!/bin/bash
|
||
# portal-cross-test.sh — Exercise the full save×load matrix
|
||
# across Python, C, and asm using the S-expression portal.
|
||
#
|
||
# Each implementation must be able to:
|
||
# 1. Write a portable .sexp file
|
||
# 2. Read a .sexp file written by any other implementation
|
||
#
|
||
# Result: 3x3 = 9 combinations. 7 are tested here (Python and C
|
||
# have built-in (load); asm gained (load) in commit on 2026-04-16,
|
||
# so it participates as both producer and consumer).
|
||
|
||
set -e
|
||
cd "$(dirname "$0")/.."
|
||
|
||
PY="python3 uncommonlisp.py --fast"
|
||
C="./c/uncommonlisp"
|
||
ASM="./asm/uncommonlisp"
|
||
SEXP=/tmp/portal-xtest.sexp
|
||
|
||
PASS=0
|
||
FAIL=0
|
||
|
||
verify() {
|
||
local label="$1" output="$2"
|
||
if echo "$output" | grep -q "PASS:"; then
|
||
PASS=$((PASS + 1))
|
||
printf " %-30s OK\n" "$label"
|
||
else
|
||
FAIL=$((FAIL + 1))
|
||
printf " %-30s FAIL\n" "$label"
|
||
echo "----- output:"
|
||
echo "$output"
|
||
echo "-----"
|
||
fi
|
||
}
|
||
|
||
echo "═══════════════════════════════════════════════════════"
|
||
echo "Portal cross-impl exchange (S-expression format)"
|
||
echo "═══════════════════════════════════════════════════════"
|
||
|
||
for PRODUCER in "Python|$PY tests/portal-cross-save.lsp" \
|
||
"C|$C tests/portal-cross-save.lsp" \
|
||
"Asm|$ASM < tests/portal-cross-save.lsp"; do
|
||
P_NAME="${PRODUCER%%|*}"
|
||
P_CMD="${PRODUCER#*|}"
|
||
echo
|
||
echo "── producer: $P_NAME ──"
|
||
rm -f "$SEXP"
|
||
# portal-cross-save writes to /tmp/cross.sexp; copy out to our slot
|
||
eval "$P_CMD" >/dev/null 2>&1
|
||
cp /tmp/cross.sexp "$SEXP"
|
||
|
||
for CONSUMER in "Python|$PY tests/portal-cross-load.lsp" \
|
||
"C|$C tests/portal-cross-load.lsp" \
|
||
"Asm|$ASM < tests/portal-cross-load.lsp"; do
|
||
C_NAME="${CONSUMER%%|*}"
|
||
C_CMD="${CONSUMER#*|}"
|
||
# Regenerate /tmp/cross.sexp from our slot (portal-cross-load reads it)
|
||
cp "$SEXP" /tmp/cross.sexp
|
||
OUTPUT=$(eval "$C_CMD" 2>&1)
|
||
verify "$P_NAME → $C_NAME" "$OUTPUT"
|
||
done
|
||
done
|
||
|
||
rm -f "$SEXP" /tmp/cross.sexp
|
||
|
||
echo
|
||
echo "═══════════════════════════════════════════════════════"
|
||
echo "Cross-impl portal: $PASS passed, $FAIL failed"
|
||
if [ $FAIL -ne 0 ]; then
|
||
exit 1
|
||
fi
|