The examples are files that run, not prose, so the page cannot drift from them

Copying a snippet into HTML is where a documented language stops being the
real one. Each block on the page is a program here with its recorded output
beside it, and check.sh is what says the page is still true after a change.
This commit is contained in:
Joseph Ferano 2026-09-12 03:40:03 +07:00
parent 2ecfac7561
commit dfd64d89ea
30 changed files with 281 additions and 0 deletions

23
web/examples/arrays.flan Normal file
View File

@ -0,0 +1,23 @@
(defconst rows 3)
(defconst cols 4)
;; A fixed array is a value: inline storage, copies on assignment.
(defconst palette [4 u32] [0xE6B800FF 0x3B6E8CFF 0xA83232FF 0xCC6B1FFF])
;; No initialiser means all-bytes-zero, so this is BSS and costs nothing.
(defvar grid [rows [cols i32]])
(defn main []
(set (at grid 1 2) 7)
(print-i64 (i64 (at grid 1 2))) (newline) ; 7
(print-i64 (i64 (len palette))) (newline) ; 4
;; A slice is ptr+len and non-owning: it views the array, it does not copy it.
(let [row (slice (at grid 1) 0 cols)]
(set (at row 0) 5)
(print-i64 (i64 (at grid 1 0))) (newline) ; 5 — the same storage
(print-i64 (sum-i32 row)) (newline)) ; 12
;; (zeroed) is a memset, not an allocation.
(set grid (zeroed))
(print-i64 (i64 (at grid 1 2))) (newline)) ; 0

6
web/examples/arrays.out Normal file
View File

@ -0,0 +1,6 @@
7
4
5
12
0
exit 0

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

@ -0,0 +1,12 @@
(defstruct Missing [id i32])
(defn load [n i32] i32
(restart-case
(do (error (Missing {:id n})) ; Never — only a transfer gets past
0)
(use-placeholder [] -1)
(retry [] 7)))
(defn main []
(print-i64 (i64 (load 1)))
(newline))

2
web/examples/boom.out Normal file
View File

@ -0,0 +1,2 @@
unhandled Missing
exit 134

37
web/examples/check.sh Normal file
View File

@ -0,0 +1,37 @@
#!/bin/sh
# Every Flan program shown on index.html is in this directory, and this script
# runs all of them and compares what they print against the .out file beside
# them. An example that does not compile is worse than no example, so the page
# quotes only what this script has been green on.
#
# $ dune build && sh web/examples/check.sh
#
# FLAN overrides the compiler; the default is the one dune just built.
here=$(cd "$(dirname "$0")" && pwd)
root=$(cd "$here/../.." && pwd)
FLAN=${FLAN:-$root/_build/default/bin/main.exe}
cd "$here" || exit 1
fail=0
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
got=$( { "$FLAN" run "$f"; echo "exit $?"; } 2>&1 )
if [ "$got" = "$(cat "${f%.flan}.out")" ]; then
echo "ok $f"
else
echo "FAIL $f"
printf '%s\n' "$got" | diff -u "${f%.flan}.out" - || true
fail=1
fi
done
if "$FLAN" shim shimdemo.flan | grep -q 'GetMousePosition(void)'; then
echo "ok shimdemo.flan (flan shim)"
else
echo "FAIL shimdemo.flan (flan shim)"
fail=1
fi
exit $fail

16
web/examples/conds.flan Normal file
View File

@ -0,0 +1,16 @@
(defstruct AssetMissing [id i32])
(defvar seen i64)
(defn load-all []
(signal (AssetMissing {:id 1})) ; Unit — the caller carries on
(signal (AssetMissing {:id 2})))
(defn main []
(load-all) ; no handler: a no-op
(print-i64 seen) (newline) ; 0
;; A handler that returns normally accumulates and lets the signaller run on.
(handler-bind [(AssetMissing [c] (set seen (+ seen (i64 (.id c)))))]
(load-all))
(print-i64 seen) (newline)) ; 3

3
web/examples/conds.out Normal file
View File

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

30
web/examples/control.flan Normal file
View File

@ -0,0 +1,30 @@
(defconst nums [5 i32] [1 3 8 9 10])
(defn classify [n i32] string
(cond
(< n 0) "negative"
(= n 0) "zero"
:else "positive"))
(defn countdown [n i32]
(let [i n]
(while (> i 0)
(print-i64 (i64 i))
(print-str " ")
(set i (- i 1)))
(newline)))
(defn first-even [s [i32]] (Option i32)
(dotimes [i (len s)]
(when (= 0 (% (at s i) 2))
(return (Some (at s i)))))
None)
(defn main []
(print-line (classify -3))
(countdown 4)
(unless false
(print-line "unless runs when the test is false"))
(match (first-even (slice nums 0 (len nums)))
(Some n) (do (print-i64 (i64 n)) (newline))
None (print-line "none")))

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

@ -0,0 +1,5 @@
negative
4 3 2 1
unless runs when the test is false
8
exit 0

11
web/examples/defer.flan Normal file
View File

@ -0,0 +1,11 @@
(defn work [n i32] i32
(defer (print-line "second"))
(defer (print-line "first")) ; innermost-first at exit
(when (< n 0)
(return 0)) ; runs both defers above it
(print-line "body")
n)
(defn main []
(print-i64 (i64 (work 3)))
(newline))

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

@ -0,0 +1,5 @@
body
first
second
3
exit 0

14
web/examples/enums.flan Normal file
View File

@ -0,0 +1,14 @@
(defenum Key
[space 32 escape 256 left 263 right 262])
(defn key-name [k Key] string
(cond
(= k :space) "space"
(= k :escape) "escape"
:else "an arrow"))
(defn main []
;; :space resolves against the parameter's enum at compile time.
;; A typo is an error here, not a wrong number later.
(print-line (key-name :space))
(print-line (key-name :left)))

3
web/examples/enums.out Normal file
View File

@ -0,0 +1,3 @@
space
an arrow
exit 0

6
web/examples/ffi.flan Normal file
View File

@ -0,0 +1,6 @@
;; A plain `declare` names a C symbol in a signature Flan can already spell:
;; no aggregate crosses, so no wrapper is generated.
(declare cos-f64 [x f64] f64 "cos")
(defn main []
(print-f64 (cos-f64 0.0)) (newline))

2
web/examples/ffi.out Normal file
View File

@ -0,0 +1,2 @@
1
exit 0

View File

@ -0,0 +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.
(defn length [v V2] f32
(sqrt-f32 (+ (* (.x v) (.x v)) (* (.y v) (.y v)))))

View File

@ -0,0 +1,5 @@
;; No package declaration: the name comes from the directory.
(defstruct V2 [x f32 y f32])
(defn add [a V2 b V2] V2
(V2 {:x (+ (.x a) (.x b)) :y (+ (.y a) (.y b))}))

2
web/examples/hello.flan Normal file
View File

@ -0,0 +1,2 @@
(defn main []
(print-line "hello from flan"))

2
web/examples/hello.out Normal file
View File

@ -0,0 +1,2 @@
hello from flan
exit 0

View File

@ -0,0 +1,8 @@
(defn main [] i32
(let [n 40 ; i32, inferred
big (i64 n) ; every widening is written
x 1.5] ; f64
(print-i64 (+ big 2)) (newline)
(print-f64 (* x 2.5)) (newline)
(print-i64 (i64 (bit-xor (<< 1 8) 255))) (newline)
0))

4
web/examples/numbers.out Normal file
View File

@ -0,0 +1,4 @@
42
3.75
511
exit 0

13
web/examples/option.flan Normal file
View File

@ -0,0 +1,13 @@
(defconst nums [4 i32] [4 8 15 16])
;; `some` unwraps Some and early-returns None from *this* function.
(defn doubled-first [s [i32]] (Option i32)
(Some (* 2 (some (index-of-i32 s 15)))))
(defn main []
(match (doubled-first (slice nums 0 4))
(Some i) (do (print-i64 (i64 i)) (newline)) ; 4
None (print-line "not found"))
(match (index-of-i32 (slice nums 0 4) 99)
(Some i) (do (print-i64 (i64 i)) (newline))
None (print-line "not found")))

3
web/examples/option.out Normal file
View File

@ -0,0 +1,3 @@
4
not found
exit 0

8
web/examples/pkg.flan Normal file
View File

@ -0,0 +1,8 @@
;; The directory is the package. Everything it declares arrives qualified.
(import g "geom")
(defn main []
(let [v (g/add (g/V2 {:x 3.0 :y 0.0})
(g/V2 {:x 0.0 :y 4.0}))]
(print-f64 (f64 (g/length v)))
(newline)))

2
web/examples/pkg.out Normal file
View File

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

24
web/examples/restart.flan Normal file
View File

@ -0,0 +1,24 @@
(defstruct AssetMissing [id i32])
(defvar cleanups i64)
(defn load [n i32] i32
(signal (AssetMissing {:id n}))
100)
(defn middle [n i32] i32
(defer (set cleanups (+ cleanups 1))) ; runs on the transfer too
(+ (load n) 1))
(defn fetch [n i32] i32
(restart-case (middle n) ; its value if nothing transfers
(use-placeholder [] -1)
(retry [] 7)))
(defn main []
(print-i64 (i64 (fetch 1))) (newline) ; 101 — nothing handled it
(handler-bind [(AssetMissing [c] (invoke-restart 'use-placeholder))]
(print-i64 (i64 (fetch 2))) (newline)) ; -1
(print-i64 cleanups) (newline)) ; 2 — the defer ran both times

4
web/examples/restart.out Normal file
View File

@ -0,0 +1,4 @@
101
-1
2
exit 0

View File

@ -0,0 +1,7 @@
(defstruct Vector2 [x f32 y f32])
(declare-c get-mouse-position [] Vector2 "GetMousePosition")
(defn main []
(print-f64 (f64 (.x (get-mouse-position))))
(newline))

17
web/examples/structs.flan Normal file
View File

@ -0,0 +1,17 @@
(defstruct Cursor
[src [u8] ; a non-owning slice
pos i32]) ; no initialiser means zeroed
(defn peek [c (Ptr Cursor)] u8
(if (< (.pos c) (len (.src c)))
(at (.src c) (.pos c))
0))
(defn advance [c (Ptr Cursor)]
(set (.pos c) (+ (.pos c) 1))) ; field access derefs one level
(defn main []
(let [c (Cursor {:src (bytes "hi")})] ; pos omitted, so pos is 0
(print-i64 (i64 (peek (addr c)))) (newline)
(advance (addr c))
(print-i64 (i64 (peek (addr c)))) (newline)))

3
web/examples/structs.out Normal file
View File

@ -0,0 +1,3 @@
104
105
exit 0