-
-
Notifications
You must be signed in to change notification settings - Fork 0
feat(ci): gate the runnable corpus — examples/ and conformance/ #89
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+211
−1
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,169 @@ | ||
| #!/usr/bin/env bash | ||
| # SPDX-License-Identifier: MPL-2.0 | ||
| # check-corpus.sh — gate the RUNNABLE corpus: examples/ and conformance/. | ||
| # | ||
| # ── Why this exists ──────────────────────────────────────────────────────── | ||
| # Neither examples/ nor conformance/ was executed by anything — not CI, not the | ||
| # Justfile. The cost of that was concrete: `examples/isotopy.tangle`, the file | ||
| # whose entire purpose is to demonstrate the language's central claim ("two | ||
| # braids can be structurally different but topologically equivalent"), FAILED | ||
| # with "Assertion failed" and had done for as long as `~` used free reduction. | ||
| # It only began passing when TG-7 (#50/#87) made `~` decide real braid-group | ||
| # equivalence. A showcase example can sit broken indefinitely if nothing runs it. | ||
| # | ||
| # ── Why a RATCHET rather than a pass/fail ────────────────────────────────── | ||
| # Five example programs do not typecheck and five conformance `valid/` programs | ||
| # do not parse. Those are real gaps (see below), not things to paper over — but | ||
| # they also cannot be fixed in the change that introduces the gate. So each | ||
| # known failure is recorded EXPLICITLY here with its cause. The gate then | ||
| # enforces, in both directions: | ||
| # | ||
| # * nothing currently working may break (regression) | ||
| # * nothing may join the known-bad lists (new debt) | ||
| # * anything that starts working MUST be removed from the list (the ratchet | ||
| # tightens — a stale entry is itself a failure) | ||
| # | ||
| # That last rule is what stops an "expected failures" list decaying into a | ||
| # permanent excuse. | ||
| # | ||
| # USAGE: scripts/check-corpus.sh [path-to-tangle-binary] | ||
| # EXIT: 0 = corpus matches the manifest exactly; 1 = drift in either direction | ||
|
|
||
| set -uo pipefail | ||
|
hyperpolymath marked this conversation as resolved.
|
||
|
|
||
| ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" | ||
| BIN="${1:-${ROOT}/compiler/_build/default/bin/main.exe}" | ||
|
|
||
| if [[ ! -x "${BIN}" ]]; then | ||
| echo "::error::tangle binary not found or not executable: ${BIN}" | ||
| echo "Build it first: (cd compiler && dune build)" | ||
| exit 1 | ||
| fi | ||
|
|
||
| # ── MANIFEST ─────────────────────────────────────────────────────────────── | ||
| # Examples that must fully TYPECHECK and EVALUATE (all their asserts hold). | ||
| EXAMPLES_MUST_RUN=( | ||
| echo_pd.tangle | ||
| isotopy.tangle # the TG-7 witness — see header | ||
| ) | ||
|
|
||
| # Examples that PARSE but do not yet TYPECHECK. Cause is recorded per entry so | ||
| # the list cannot quietly become a dumping ground. Tracked in #88. | ||
| # braids_as_data / trefoil : recursive fn returning Num — `1 + length(rest)` | ||
| # infers the recursive call as Word[0], not Num | ||
| # compositional_pd / tensor_product : `identity == braid[...]` is a width | ||
| # mismatch (Word[0] vs Word[n]) under T-Eq-Word | ||
| # turing_complete : match arms disagree in width (Word[0] vs Word[2]) | ||
| EXAMPLES_KNOWN_UNTYPED=( | ||
| braids_as_data.tangle | ||
| compositional_pd.tangle | ||
| tensor_product.tangle | ||
| trefoil.tangle | ||
| turing_complete.tangle | ||
| ) | ||
|
|
||
| # conformance/valid programs the parser does not yet accept. These specify | ||
| # language surface that exists in the spec but not in parser.mly. Tracked in #88. | ||
| # v02/v08/v09/v12 : `weave strands a:Q, b:Q into (...) yield strands ...` | ||
| # v11 : `add{ ... }` blocks (JTV data injection) | ||
| CONFORMANCE_KNOWN_UNPARSED=( | ||
| v02_weave.tangle | ||
| v08_crossing.tangle | ||
| v09_twist.tangle | ||
| v11_add_block.tangle | ||
| v12_weave_expr.tangle | ||
| ) | ||
|
|
||
| fail=0 | ||
| note() { printf ' %-34s %s\n' "$1" "$2"; } | ||
|
|
||
| in_list() { local n="$1"; shift; local x; for x in "$@"; do [[ "$x" == "$n" ]] && return 0; done; return 1; } | ||
|
|
||
| # ── 1. Every example must PARSE. No exceptions: a file that cannot even parse | ||
| # is not documentation, it is a syntax error with a licence header. | ||
| echo "== examples: parse ==" | ||
| for f in "${ROOT}"/examples/*.tangle; do | ||
| n="$(basename "$f")" | ||
| if "${BIN}" "$f" >/dev/null 2>&1; then note "$n" "parse ok"; else | ||
| note "$n" "PARSE FAILED"; echo "::error::${n} does not parse"; fail=1; fi | ||
| done | ||
|
|
||
| # ── 2. The working examples must keep typechecking AND evaluating. | ||
| echo "== examples: typecheck + evaluate (must-run set) ==" | ||
| for n in "${EXAMPLES_MUST_RUN[@]}"; do | ||
| f="${ROOT}/examples/${n}" | ||
| if [[ ! -f "$f" ]]; then | ||
| echo "::error::manifest lists ${n} but the file is gone"; fail=1; continue; fi | ||
| if "${BIN}" --eval "$f" >/dev/null 2>&1; then note "$n" "eval ok"; else | ||
| note "$n" "EVAL FAILED" | ||
| echo "::error::${n} must evaluate cleanly (all asserts holding) and does not:" | ||
| "${BIN}" --eval "$f" 2>&1 | head -5 | ||
| fail=1 | ||
| fi | ||
| done | ||
|
|
||
| # ── 3. The known-untyped examples: still parse, still fail to typecheck. | ||
| # If one starts typechecking, the manifest is STALE and must be updated. | ||
| echo "== examples: known-untyped (ratchet) ==" | ||
| for n in "${EXAMPLES_KNOWN_UNTYPED[@]}"; do | ||
| f="${ROOT}/examples/${n}" | ||
| if [[ ! -f "$f" ]]; then | ||
| echo "::error::manifest lists ${n} but the file is gone"; fail=1; continue; fi | ||
| if "${BIN}" --eval "$f" >/dev/null 2>&1; then | ||
| note "$n" "NOW WORKS" | ||
| echo "::error::${n} now evaluates — good! Move it to EXAMPLES_MUST_RUN and drop it from EXAMPLES_KNOWN_UNTYPED." | ||
| fail=1 | ||
| else | ||
| note "$n" "still untyped (expected)" | ||
| fi | ||
| done | ||
|
|
||
| # ── 4. Any example NOT in either list is unaccounted for. | ||
| echo "== examples: manifest covers every file ==" | ||
| for f in "${ROOT}"/examples/*.tangle; do | ||
| n="$(basename "$f")" | ||
| if ! in_list "$n" "${EXAMPLES_MUST_RUN[@]}" && ! in_list "$n" "${EXAMPLES_KNOWN_UNTYPED[@]}"; then | ||
| note "$n" "UNLISTED" | ||
| echo "::error::${n} is in examples/ but not in the manifest. Add it to EXAMPLES_MUST_RUN (preferred) or, with a recorded cause, EXAMPLES_KNOWN_UNTYPED." | ||
| fail=1 | ||
| fi | ||
| done | ||
|
|
||
| # ── 5. Conformance: valid must parse (except the recorded set); invalid must NOT. | ||
| echo "== conformance ==" | ||
| for f in "${ROOT}"/conformance/valid/*.tangle; do | ||
| n="$(basename "$f")" | ||
| if "${BIN}" "$f" >/dev/null 2>&1; then | ||
| if in_list "$n" "${CONFORMANCE_KNOWN_UNPARSED[@]}"; then | ||
| note "valid/$n" "NOW PARSES" | ||
| echo "::error::conformance/valid/${n} now parses — drop it from CONFORMANCE_KNOWN_UNPARSED." | ||
| fail=1 | ||
| else | ||
| note "valid/$n" "parse ok" | ||
| fi | ||
| else | ||
| if in_list "$n" "${CONFORMANCE_KNOWN_UNPARSED[@]}"; then | ||
| note "valid/$n" "still unparsed (expected)" | ||
| else | ||
| note "valid/$n" "PARSE FAILED" | ||
| echo "::error::conformance/valid/${n} must parse and does not"; fail=1 | ||
| fi | ||
| fi | ||
| done | ||
| for f in "${ROOT}"/conformance/invalid/*.tangle; do | ||
| n="$(basename "$f")" | ||
| if "${BIN}" "$f" >/dev/null 2>&1; then | ||
| note "invalid/$n" "PARSED (should not)" | ||
| echo "::error::conformance/invalid/${n} parsed successfully but is meant to be rejected"; fail=1 | ||
| else | ||
| note "invalid/$n" "rejected ok" | ||
| fi | ||
| done | ||
|
|
||
| echo | ||
| if [[ "$fail" -ne 0 ]]; then | ||
| echo "::error::corpus drifted from the manifest — see the errors above." | ||
| exit 1 | ||
| fi | ||
| echo "Corpus matches the manifest: examples parse, the must-run set evaluates," | ||
| echo "known gaps are unchanged, and invalid programs are still rejected." | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.