flan/web/examples/quotes.sh
Joseph Ferano aa82364066 Two aliases for the checks nobody ran, and one word that names them all
@page runs web/examples/check.sh and web/examples/quotes.sh against the compiler
dune just built. @cells runs spike/x86/cells.sh, which was a real pass/fail check
-- four builds, two backends, 22 22 against 42 42 -- that nothing in the tree ran.
@checks is @page, @x86 and @cells together, and its comment argues for where the
boundary sits: everything you can run while making coffee is in, @sanitize and
@valgrind are out because folding tens of minutes in would make the umbrella the
thing nobody has time for, which is the disease rather than the cure.

All three scripts learned to resolve FLAN to an absolute path, which is what
actually stood between them and a dune rule: %{workspace_root} expands relative to
the directory the rule is written in, and every one of these scripts cd's somewhere
before using it. The first run of @page failed with twenty diffs all saying
'../bin/main.exe: No such file or directory', which is at least a failure that says
what is wrong.

docs/BUILT.md carried the same colon-spelled renderer block index.html did, from the
same sweep. Nothing checks BUILT.md, so it is corrected here by hand.
2026-09-14 10:26:48 +07:00

124 lines
6.3 KiB
Bash

#!/bin/sh
# The blocks on index.html that are not programs in this directory are quoted
# from somewhere: a repository file, or the output of a command. Each one is
# re-derived here and looked for in the page, so a quote cannot go stale
# quietly. check.sh covers the runnable blocks; this covers the rest.
#
# $ dune build && sh web/examples/quotes.sh
here=$(cd "$(dirname "$0")" && pwd)
root=$(cd "$here/../.." && pwd)
FLAN=${FLAN:-$root/_build/default/bin/main.exe}
# Absolute, for the reason check.sh gives beside the same line: everything else
# here is resolved against $here or $root, so a FLAN relative to the caller's
# working directory would be the one path that meant something different.
case $FLAN in /*) ;; *) FLAN=$(cd "$(dirname "$FLAN")" && pwd)/$(basename "$FLAN") ;; esac
page=$here/../index.html
fail=0
# The page is compared with its whitespace collapsed, so that a long compiler
# message may be wrapped in the HTML and still be recognised as the same text.
flat=$(sed -e 's/&lt;/</g' -e 's/&gt;/>/g' -e 's/&amp;/\&/g' "$page" | tr '\n' ' ' | tr -s ' ')
want() {
# An empty needle would match anything — `case $x in **)` is always true — so
# every check whose text comes from a grep would go green the moment the line
# it greps for was renamed. That is the one failure this script exists to
# catch, so the empty case is a failure and not a match.
if [ -z "$2" ]; then
echo "FAIL $1"
echo " nothing to compare against — whatever this quotes has moved"
fail=1
return
fi
needle=$(printf '%s' "$2" | tr '\n' ' ' | tr -s ' ')
case $flat in
*"$needle"*) echo "ok $1" ;;
*) echo "FAIL $1"; echo " not on the page: $2"; fail=1 ;;
esac
}
# The usage text, from the binary itself.
want "flan usage" "$("$FLAN" 2>&1 | sed -n 2p)"
# calc-me, the first acceptance program, still answers what the page says.
want "calc-me" "$("$FLAN" run "$root/calc-me.flan" '1 + 2 * (3 - 0.5) / 2')"
# The sand hash. The page shows it twice — native and wasm32 — and the claim is
# that the two agree; only the native half is cheap enough to check here.
want "sand hash" "$("$FLAN" run "$root/test/programs/sand-headless.flan")"
# The two cross-target refusals, in the compiler's own words.
want "run --target" "$("$FLAN" run "$here/hello.flan" --target=wasm32-wasi 2>&1)"
# The refusal and diagnostic messages quoted in the prose and in the
# "Not implemented yet" table. Each is a program the compiler must reject, and
# what is compared is the first line of the rejection -- the message itself.
# Not the whole of what `flan check` prints: it grew a source excerpt with a
# caret under the offending form, and a needle that swallowed the excerpt was
# comparing three lines against a page that quotes one.
#
# Six probes that used to live here are gone, and it is worth saying why rather
# than leaving the next reader to wonder what the list once covered. (Vec T),
# (Map K V), (Handle T), an (Fn ...) parameter, a defer inside a let, and break
# were each refused as "not implemented yet" when this loop was written. All six
# work now, the page's table lost their rows as they landed, and the probes were
# the only thing left asserting a claim nobody makes any more. A probe for a
# refusal that no longer happens cannot go green, and keeping it would have meant
# a permanently red script -- which is the same as no script at all.
for pair in \
'result:(defn f [] (Result i32 i32) None)' \
'quoted:(defn f [] i32 (quote a))' \
'i64index:(defconst xs [3 i32] [1 2 3]) (defn main [] i32 (let [i (i64 1)] (at xs i)))'
do
name=${pair%%:*}; src=${pair#*:}
printf '%s\n' "$src" > "$here/.q.flan"
out=$("$FLAN" check "$here/.q.flan" 2>&1)
# A probe that compiles is the failure this loop is most likely to meet, and
# it is the one that used to be unreadable: `flan check` answers a clean
# program with its whole symbol table, so the needle became eighty lines of
# prelude signatures and the diff said nothing. Say what actually happened.
if "$FLAN" check "$here/.q.flan" >/dev/null 2>&1; then
echo "FAIL message: $name"
echo " this program compiles now — the page still says it is refused"
fail=1
continue
fi
want "message: $name" "$(printf '%s\n' "$out" | sed 's/^[^ ]*: //' | sed -n 1p)"
done
rm -f "$here/.q.flan"
# The cell and the transfer channel, from a real --dev emit. hello.flan cannot
# show a cell any more: println is compiler-provided rather than a Flan
# function, so the smallest program makes no Flan-to-Flan call at all. defer.flan
# is the smallest one on the page that does — its main calls work.
ir=$("$FLAN" emit --dev "$here/defer.flan" 2>/dev/null)
want "cell global" "$(printf '%s\n' "$ir" | grep '^@"flan.cell.work"')"
want "cell load" "$(printf '%s\n' "$ir" | grep 'load ptr, ptr @"flan.cell.work"')"
want "xfer channel" "$(printf '%s\n' "$ir" | grep 'define {} @"flan.main"')"
# The generated C, from flan shim.
want "shim wrapper" "$("$FLAN" shim "$here/shimdemo.flan" | grep 'GetMousePosition();')"
# Lines quoted verbatim from repository files.
want "raylib binding" "$(grep -F 'unload-texture' "$root/vendor/raylib/raylib.flan")"
want "agent declare" "$(grep -F 'flan_agent_poll' "$root/vendor/agent/agent.flan" | head -1)"
want "conditions.org" "$(grep -F 'Innermost frame offering the name wins' "$root/conditions.org")"
# The value renderer, anchored in the test corpus rather than in NEXT.md. It
# used to grep NEXT.md for this line, and NEXT.md is a rolling scratch document:
# the line was rewritten out of it, the grep went empty, and the check spent
# weeks reporting "whatever this quotes has moved" while the page really had
# gone stale -- the colon-to-dot sweep rewrote every field label in the corpus
# and left the renderer's own output on this page spelled the old way. An anchor
# has to be somewhere that cannot quietly stop saying the thing, and the corpus
# is such a place, because `dune test` builds and runs it.
want "renderer" "$(grep -oF '{.r 17 .g 34 .b 51 .a 68}' "$root/test/programs/raylib-imported.flan" | head -1)"
# The Emacs bindings, from the keymap rather than from any prose about it.
for k in "C-c C-c" "C-c C-k" "C-x C-e" "C-c C-b" "C-c C-v" "C-c C-x"; do
grep -qF "(kbd \"$k\")" "$root/emacs/flan-mode.el" || {
echo "FAIL keybinding $k is not in flan-mode.el"; fail=1; continue; }
want "keybinding $k" "<kbd>$k</kbd>"
done
exit $fail