|
| 1 | +#!/usr/bin/env bash |
| 2 | +# Every reason token the engine can emit is in docs/50's table, and every row |
| 3 | +# of that table is a token the engine can emit. |
| 4 | +# |
| 5 | +# WHY THIS EXISTS. The table is a MACHINE INTERFACE: mcpp-index's compatibility |
| 6 | +# measurement reads `[interface-not-provided]` out of a refusal to tell "this |
| 7 | +# graph does not supply what the member asked for" from "the member did not |
| 8 | +# build", and that distinction decides a published figure. A token the engine |
| 9 | +# emits and the table omits is a promise nobody can rely on; a row naming a |
| 10 | +# token no branch emits is one a consumer will wait for forever. |
| 11 | +# |
| 12 | +# IT EXISTS BECAUSE ADDING THE MISSING ONES BY HAND MISSED SOME. Four tokens |
| 13 | +# were added to this table on 2026-09-18 as "the ones it was missing"; a |
| 14 | +# later enumeration found four more that had been absent the whole time. A set |
| 15 | +# compared by reading is a set compared by sampling. |
| 16 | +set -u |
| 17 | +cd "$(dirname "$0")/../.." |
| 18 | + |
| 19 | +src=src/build/refusal.cppm |
| 20 | +doc=docs/50-machine-output.md |
| 21 | +[ -f "$src" ] && [ -f "$doc" ] || { echo "::error::missing $src or $doc"; exit 1; } |
| 22 | + |
| 23 | +# `none` is the sentinel for "no refusal was recorded" and names no branch. |
| 24 | +# `other` stays in BOTH sets: it is emitted and it is documented. |
| 25 | +emitted=$(grep -oE 'return "[a-z][a-z0-9-]*";' "$src" \ |
| 26 | + | sed 's/return "//; s/";//' | grep -vx none | sort -u) |
| 27 | + |
| 28 | +# The table is the contiguous run of `| \`token\` |` rows containing `other`, |
| 29 | +# which is its documented catch-all. Anchoring on a row rather than on a |
| 30 | +# heading keeps this working when the prose around it is rewritten. |
| 31 | +n=$(grep -n '^| `other` |' "$doc" | head -1 | cut -d: -f1) |
| 32 | +[ -n "$n" ] || { echo "::error::$doc has no \`other\` row; the table moved"; exit 1; } |
| 33 | +start=$n |
| 34 | +while [ "$start" -gt 1 ] && sed -n "$((start-1))p" "$doc" | grep -q '^|'; do |
| 35 | + start=$((start-1)) |
| 36 | +done |
| 37 | +# A ROW IS SELECTED BY HAVING A DESCRIPTION, NOT BY LOOKING LIKE A ROW. This |
| 38 | +# table's COLUMN HEADER is `| \`reason\` | |` --- a backticked name in the first |
| 39 | +# cell, exactly the shape of the rows beneath it --- so a pattern that matches |
| 40 | +# "backticked name at the start of a line" reports `reason` as a documented |
| 41 | +# token. The second cell is what tells a header from a row, so the pattern |
| 42 | +# requires a non-empty one. |
| 43 | +documented=$(sed -n "${start},${n}p" "$doc" \ |
| 44 | + | grep -oE '^\| `[a-z][a-z0-9-]*` \| [^|]+ \|' \ |
| 45 | + | sed 's/^| `//; s/`.*//' | sort -u) |
| 46 | + |
| 47 | +missing=$(comm -23 <(printf '%s\n' "$emitted") <(printf '%s\n' "$documented")) |
| 48 | +extra=$(comm -13 <(printf '%s\n' "$emitted") <(printf '%s\n' "$documented")) |
| 49 | + |
| 50 | +rc=0 |
| 51 | +if [ -n "$missing" ]; then |
| 52 | + echo "::error::these reason tokens are emitted by $src and absent from $doc:" |
| 53 | + printf ' %s\n' $missing |
| 54 | + rc=1 |
| 55 | +fi |
| 56 | +if [ -n "$extra" ]; then |
| 57 | + echo "::error::$doc names these tokens and no branch in $src emits them:" |
| 58 | + printf ' %s\n' $extra |
| 59 | + rc=1 |
| 60 | +fi |
| 61 | +# AND THE 简体中文 MIRROR CARRIES THE SAME SET. A translated page falls behind |
| 62 | +# by losing rows, and a structural check that only counts headings cannot see |
| 63 | +# it: the token names are identical in both languages, so they compare |
| 64 | +# directly even though nothing else on the page does. |
| 65 | +zh=docs/zh/50-machine-output.md |
| 66 | +if [ -f "$zh" ]; then |
| 67 | + zn=$(grep -n '^| `other` |' "$zh" | head -1 | cut -d: -f1) |
| 68 | + if [ -z "$zn" ]; then |
| 69 | + echo "::error::$zh has no \`other\` row; the mirror's table moved" |
| 70 | + rc=1 |
| 71 | + else |
| 72 | + zstart=$zn |
| 73 | + while [ "$zstart" -gt 1 ] && sed -n "$((zstart-1))p" "$zh" | grep -q '^|'; do |
| 74 | + zstart=$((zstart-1)) |
| 75 | + done |
| 76 | + zdoc=$(sed -n "${zstart},${zn}p" "$zh" \ |
| 77 | + | grep -oE '^\| `[a-z][a-z0-9-]*` \| [^|]+ \|' \ |
| 78 | + | sed 's/^| `//; s/`.*//' | sort -u) |
| 79 | + zmiss=$(comm -23 <(printf '%s\n' "$documented") <(printf '%s\n' "$zdoc")) |
| 80 | + zextra=$(comm -13 <(printf '%s\n' "$documented") <(printf '%s\n' "$zdoc")) |
| 81 | + if [ -n "$zmiss" ]; then |
| 82 | + echo "::error::$zh is missing reason tokens that $doc documents:" |
| 83 | + printf ' %s\n' $zmiss |
| 84 | + rc=1 |
| 85 | + fi |
| 86 | + if [ -n "$zextra" ]; then |
| 87 | + echo "::error::$zh documents reason tokens $doc does not:" |
| 88 | + printf ' %s\n' $zextra |
| 89 | + rc=1 |
| 90 | + fi |
| 91 | + fi |
| 92 | +fi |
| 93 | + |
| 94 | +[ "$rc" -eq 0 ] && echo "OK: $(printf '%s\n' "$emitted" | grep -c .) reason tokens; engine, table and 简体中文 mirror agree" |
| 95 | +exit $rc |
0 commit comments