Run the break loop rather than quote it, since the restart order is a claim

NEXT.md prints the banner with the restarts in source order; the walk is
innermost-first, so it is the other way round. A --dev build under timeout is
enough to settle that, and settles the two place and global snippets with it.
This commit is contained in:
Joseph Ferano 2026-09-12 03:43:24 +07:00
parent dfd64d89ea
commit 86ef557433
10 changed files with 98 additions and 15 deletions

View File

@ -0,0 +1,15 @@
(import agent "vendor:agent")
(defstruct Missing [id i32])
(defn load [n i32] i32
(restart-case
(do (error (Missing {:id n}))
0)
(use-placeholder [] -1)
(retry [] 7)))
(defn main []
(agent/start "/tmp/flan-breakdemo.sock")
(print-i64 (i64 (load 1)))
(newline))

View File

@ -0,0 +1,4 @@
flan: unhandled Missing — stopped, not dead.
restart: retry
restart: use-placeholder

View File

@ -10,28 +10,49 @@
here=$(cd "$(dirname "$0")" && pwd)
root=$(cd "$here/../.." && pwd)
FLAN=${FLAN:-$root/_build/default/bin/main.exe}
tmp=${TMPDIR:-/tmp}/flan-web-check.$$
cd "$here" || exit 1
fail=0
ok() { echo "ok $1"; }
bad() { echo "FAIL $1"; fail=1; }
for f in *.flan; do
# shimdemo.flan calls raylib, so it is not run. What it demonstrates is the
# C the compiler writes, which is checked below by generating that instead.
[ "$f" = shimdemo.flan ] && continue
# Two programs are not run by `flan run`; each is checked its own way below.
[ "$f" = shimdemo.flan ] && continue # calls raylib
[ "$f" = breakdemo.flan ] && continue # stops and waits, on purpose
got=$( { "$FLAN" run "$f"; echo "exit $?"; } 2>&1 )
if [ "$got" = "$(cat "${f%.flan}.out")" ]; then
echo "ok $f"
else
echo "FAIL $f"
if [ "$got" = "$(cat "${f%.flan}.out")" ]; then ok "$f"; else
bad "$f"
printf '%s\n' "$got" | diff -u "${f%.flan}.out" - || true
fail=1
fi
done
# shimdemo.flan is the declare-c example: what it demonstrates is the C the
# compiler writes, so it is checked by generating that rather than by running.
if "$FLAN" shim shimdemo.flan | grep -q 'GetMousePosition(void)'; then
echo "ok shimdemo.flan (flan shim)"
ok "shimdemo.flan (flan shim)"
else
echo "FAIL shimdemo.flan (flan shim)"
fail=1
bad "shimdemo.flan (flan shim)"
fi
# breakdemo.flan is the break loop: an unhandled error stops the program and
# waits for someone to pick a restart, so it never exits on its own. It needs
# a --dev build (the hook lives in vendor/agent) and it is killed after a few
# seconds; what is checked is the banner it printed before it stopped.
if command -v timeout >/dev/null 2>&1; then
if "$FLAN" build breakdemo.flan --dev -o "$tmp" >/dev/null 2>&1; then
got=$(timeout 5 "$tmp" 2>&1)
rm -f "$tmp"
if [ "$got" = "$(cat breakdemo.out)" ]; then ok "breakdemo.flan (break loop)"; else
bad "breakdemo.flan (break loop)"
printf '%s\n' "$got" | diff -u breakdemo.out - || true
fi
else
bad "breakdemo.flan (build --dev)"
fi
else
echo "skip breakdemo.flan (no timeout(1))"
fi
exit $fail

View File

@ -1,4 +1,4 @@
;; A second file in the same directory shares one top-level scope: it does not
;; import vec.flan, and the order of the two files does not matter.
;; geom/len.flan — a second file in the same directory shares one top-level
;; scope: it does not import vec.flan, and the order of the two does not matter.
(defn length [v V2] f32
(sqrt-f32 (+ (* (.x v) (.x v)) (* (.y v) (.y v)))))

View File

@ -1,4 +1,4 @@
;; No package declaration: the name comes from the directory.
;; geom/vec.flan — no package declaration: the name comes from the directory.
(defstruct V2 [x f32 y f32])
(defn add [a V2 b V2] V2

12
web/examples/globals.flan Normal file
View File

@ -0,0 +1,12 @@
(defconst cell-size 5) ; a compile-time constant
(defconst gravity f32 0.05) ; with its type named
(defvar current-color i32) ; zeroed storage
(defconst rows 3)
(defconst cols 4)
(defvar grid [rows [cols u32]]) ; BSS, rows*cols*4 bytes
(defn main []
(print-i64 (i64 cell-size)) (newline)
(print-f64 (f64 gravity)) (newline)
(print-i64 (i64 current-color)) (newline)
(print-i64 (i64 (at grid 2 3))) (newline))

5
web/examples/globals.out Normal file
View File

@ -0,0 +1,5 @@
5
0.05
0
0
exit 0

View File

@ -1,4 +1,5 @@
;; The directory is the package. Everything it declares arrives qualified.
;; pkg.flan — the directory is the package, and everything it declares
;; arrives qualified by the alias this import chose.
(import g "geom")
(defn main []

20
web/examples/places.flan Normal file
View File

@ -0,0 +1,20 @@
(defstruct Enemy [hp i32 name string])
(defvar spawned i32)
(defconst room-size 4)
(defvar room [room-size i32])
;; `set` takes a fixed list of forms, not an extensible setf.
(defn main []
(let [e (Enemy {:hp 10 :name "slime"})
p (addr e)]
(set spawned (+ spawned 1)) ; a local or a defvar
(set (.hp e) 7) ; a struct field
(set (.hp p) 8) ; through a (Ptr Enemy) — derefs one level
(set (at room 2) 5) ; a fixed array or slice element
(set (deref p) (Enemy {:hp 3 :name "wisp"})) ; a whole-object store
(print-i64 (i64 (.hp e))) (newline)
(print-line (.name e))
(print-i64 (i64 (at room 2))) (newline)
(print-i64 (i64 spawned)) (newline)))

5
web/examples/places.out Normal file
View File

@ -0,0 +1,5 @@
3
wisp
5
1
exit 0