#!/usr/bin/env bash # run.sh — build and execute the time/ acceptance corpus. # # Each examples/.el is a self-contained El program with a fn main() # that prints a single deterministic result line. The runner compiles each # via the canonical native elc, links it against the shared C runtime, runs # it, and asserts the output matches the expected value. # # The typeerror case is special: the C source is generated successfully (a # `#error` directive sits at the bottom) but cc must FAIL. Pass = cc fails # with the expected diagnostic; Fail = cc succeeds (the type rule didn't # fire) or fails with an unexpected diagnostic. set -uo pipefail cd "$(dirname "$0")" EL_HOME="${EL_HOME:-$(cd ../.. && pwd)}" ELC="${EL_HOME}/dist/platform/elc" RUNTIME_DIR="${EL_HOME}/el-compiler/runtime" if [ ! -x "${ELC}" ]; then echo "elc not found at ${ELC}" >&2 exit 1 fi PASS=0 FAIL=0 FAILED_NAMES=() # run_runtime_case [] # Compiles the source via elc, links against the runtime, runs the binary, # and compares stdout (whitespace-trimmed) against . If is # the literal string "either", any one of the comma-separated values in # counts as a pass — used by time-arithmetic where the second # now() reading is microseconds after the first, which makes the >= # comparison platform-dependent. run_runtime_case() { local name="$1" local src="$2" local expected="$3" local mode="${4:-exact}" local out_c local out_bin out_c="$(mktemp -t time_test.XXXXXX).c" out_bin="$(mktemp -t time_test.XXXXXX)" if ! "${ELC}" "${src}" > "${out_c}" 2>/tmp/time_test.elc.err; then echo "FAIL ${name} — elc emit failed:" cat /tmp/time_test.elc.err | sed 's/^/ /' FAIL=$((FAIL+1)) FAILED_NAMES+=("${name}") rm -f "${out_c}" "${out_bin}" return fi if ! cc -O2 -I "${RUNTIME_DIR}" "${out_c}" "${RUNTIME_DIR}/el_runtime.c" \ -lcurl -lssl -lcrypto -lpthread -lm -o "${out_bin}" 2>/tmp/time_test.cc.err; then echo "FAIL ${name} — cc failed:" cat /tmp/time_test.cc.err | sed 's/^/ /' FAIL=$((FAIL+1)) FAILED_NAMES+=("${name}") rm -f "${out_c}" "${out_bin}" return fi local got got="$("${out_bin}" 2>&1 | tr -d '[:space:]')" if [ "${mode}" = "either" ]; then local ok=0 local IFS=',' for choice in ${expected}; do if [ "${got}" = "${choice}" ]; then ok=1; break; fi done if [ "${ok}" = "1" ]; then echo "PASS ${name} (got: ${got})" PASS=$((PASS+1)) else echo "FAIL ${name} expected one of {${expected}}, got: ${got}" FAIL=$((FAIL+1)) FAILED_NAMES+=("${name}") fi else if [ "${got}" = "${expected}" ]; then echo "PASS ${name}" PASS=$((PASS+1)) else echo "FAIL ${name} expected: ${expected}, got: ${got}" FAIL=$((FAIL+1)) FAILED_NAMES+=("${name}") fi fi rm -f "${out_c}" "${out_bin}" } # run_typeerror_case # Compiles to C (which must succeed; the violation is recorded but codegen # still completes), then asserts cc FAILS with the temporal-type-error # directive in its output. run_typeerror_case() { local name="$1" local src="$2" local out_c out_c="$(mktemp -t time_test.XXXXXX).c" if ! "${ELC}" "${src}" > "${out_c}" 2>/tmp/time_test.elc.err; then echo "FAIL ${name} — elc emit failed (expected emit-then-cc-fail):" cat /tmp/time_test.elc.err | sed 's/^/ /' FAIL=$((FAIL+1)) FAILED_NAMES+=("${name}") rm -f "${out_c}" return fi if cc -O2 -I "${RUNTIME_DIR}" "${out_c}" "${RUNTIME_DIR}/el_runtime.c" \ -lcurl -lssl -lcrypto -lpthread -lm -o /tmp/time_test_should_not_link 2>/tmp/time_test.cc.err; then echo "FAIL ${name} — cc unexpectedly succeeded; type rule did not fire" FAIL=$((FAIL+1)) FAILED_NAMES+=("${name}") rm -f "${out_c}" /tmp/time_test_should_not_link return fi if grep -q "temporal type error" /tmp/time_test.cc.err; then echo "PASS ${name} (cc rejected with: $(grep "temporal type error" /tmp/time_test.cc.err | head -1 | tr -d '\n'))" PASS=$((PASS+1)) else echo "FAIL ${name} — cc failed but without the expected temporal-type-error message:" cat /tmp/time_test.cc.err | sed 's/^/ /' FAIL=$((FAIL+1)) FAILED_NAMES+=("${name}") fi rm -f "${out_c}" } echo "==> Running time-types acceptance corpus" echo run_runtime_case "time-literals" examples/time-literals.el "30" run_runtime_case "time-arithmetic" examples/time-arithmetic.el "0,1" either run_runtime_case "time-comparison" examples/time-comparison.el "1" run_typeerror_case "time-typeerror" examples/time-typeerror.el run_runtime_case "ttl-cache" examples/ttl-cache.el "hello" run_runtime_case "sleep-typed" examples/sleep-typed.el "1" echo echo "${PASS} passed, ${FAIL} failed" if [ "${FAIL}" -gt 0 ]; then echo "failed: ${FAILED_NAMES[*]}" exit 1 fi exit 0