From 86ef55743311021f3814125689a3269894cb8877 Mon Sep 17 00:00:00 2001 From: Joseph Ferano Date: Sat, 12 Sep 2026 03:43:24 +0700 Subject: [PATCH] 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. --- web/examples/breakdemo.flan | 15 +++++++++++++ web/examples/breakdemo.out | 4 ++++ web/examples/check.sh | 43 +++++++++++++++++++++++++++---------- web/examples/geom/len.flan | 4 ++-- web/examples/geom/vec.flan | 2 +- web/examples/globals.flan | 12 +++++++++++ web/examples/globals.out | 5 +++++ web/examples/pkg.flan | 3 ++- web/examples/places.flan | 20 +++++++++++++++++ web/examples/places.out | 5 +++++ 10 files changed, 98 insertions(+), 15 deletions(-) create mode 100644 web/examples/breakdemo.flan create mode 100644 web/examples/breakdemo.out create mode 100644 web/examples/globals.flan create mode 100644 web/examples/globals.out create mode 100644 web/examples/places.flan create mode 100644 web/examples/places.out diff --git a/web/examples/breakdemo.flan b/web/examples/breakdemo.flan new file mode 100644 index 0000000..6afd53c --- /dev/null +++ b/web/examples/breakdemo.flan @@ -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)) diff --git a/web/examples/breakdemo.out b/web/examples/breakdemo.out new file mode 100644 index 0000000..4383858 --- /dev/null +++ b/web/examples/breakdemo.out @@ -0,0 +1,4 @@ + +flan: unhandled Missing — stopped, not dead. + restart: retry + restart: use-placeholder diff --git a/web/examples/check.sh b/web/examples/check.sh index 8e78563..7811669 100644 --- a/web/examples/check.sh +++ b/web/examples/check.sh @@ -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 diff --git a/web/examples/geom/len.flan b/web/examples/geom/len.flan index 50d577e..8ca23cc 100644 --- a/web/examples/geom/len.flan +++ b/web/examples/geom/len.flan @@ -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))))) diff --git a/web/examples/geom/vec.flan b/web/examples/geom/vec.flan index 5f5c019..956560c 100644 --- a/web/examples/geom/vec.flan +++ b/web/examples/geom/vec.flan @@ -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 diff --git a/web/examples/globals.flan b/web/examples/globals.flan new file mode 100644 index 0000000..495be74 --- /dev/null +++ b/web/examples/globals.flan @@ -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)) diff --git a/web/examples/globals.out b/web/examples/globals.out new file mode 100644 index 0000000..7c27aec --- /dev/null +++ b/web/examples/globals.out @@ -0,0 +1,5 @@ +5 +0.05 +0 +0 +exit 0 diff --git a/web/examples/pkg.flan b/web/examples/pkg.flan index d2a5986..3a62201 100644 --- a/web/examples/pkg.flan +++ b/web/examples/pkg.flan @@ -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 [] diff --git a/web/examples/places.flan b/web/examples/places.flan new file mode 100644 index 0000000..03f3878 --- /dev/null +++ b/web/examples/places.flan @@ -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))) diff --git a/web/examples/places.out b/web/examples/places.out new file mode 100644 index 0000000..4444933 --- /dev/null +++ b/web/examples/places.out @@ -0,0 +1,5 @@ +3 +wisp +5 +1 +exit 0