Give the page a section on the two printers it never mentioned
print and println had no line anywhere on index.html — a builtin the reader meets in the first example and is never told about. There is a section for them now, between arrays and the prelude: what the walk covers, that an enum comes back as its name and a Ptr does not get followed, that a string is raw at the top and quoted inside a structure, and that the depth and span caps are what keep a grid from printing a screenful. printing.flan is beside the other examples so check.sh has been green on every line of it. The prelude's table loses its output row, because the prelude has no output functions left, and the fifty-odd example blocks follow the files they quote. Two pinned things moved. sand-headless prints 15595743031174623232 where it printed -2851001042534928384: same bits, read unsigned, because the cast that made it signed is gone. bounds.flan's trap moved from column 28 to 19, which is where (at xs i) now starts on that line. And the indirection-cell illustration is defer.flan rather than hello.flan. hello.flan cannot show a cell any more: its one call was to a prelude function, and println is compiler-provided, so the smallest program makes no Flan-to-Flan call at all. defer.flan's main calls work, and is already on the page a few sections up.
This commit is contained in:
parent
96ab4c9cf0
commit
b8019a96d5
@ -9,15 +9,15 @@
|
|||||||
|
|
||||||
(defn main []
|
(defn main []
|
||||||
(set (at grid 1 2) 7)
|
(set (at grid 1 2) 7)
|
||||||
(print-i64 (i64 (at grid 1 2))) (newline) ; 7
|
(print (at grid 1 2)) (println "") ; 7
|
||||||
(print-i64 (i64 (len palette))) (newline) ; 4
|
(print (len palette)) (println "") ; 4
|
||||||
|
|
||||||
;; A slice is ptr+len and non-owning: it views the array, it does not copy it.
|
;; 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)]
|
(let [row (slice (at grid 1) 0 cols)]
|
||||||
(set (at row 0) 5)
|
(set (at row 0) 5)
|
||||||
(print-i64 (i64 (at grid 1 0))) (newline) ; 5 — the same storage
|
(print (at grid 1 0)) (println "") ; 5 — the same storage
|
||||||
(print-i64 (sum-i32 row)) (newline)) ; 12
|
(print (sum-i32 row)) (println "")) ; 12
|
||||||
|
|
||||||
;; (zeroed) is a memset, not an allocation.
|
;; (zeroed) is a memset, not an allocation.
|
||||||
(set grid (zeroed))
|
(set grid (zeroed))
|
||||||
(print-i64 (i64 (at grid 1 2))) (newline)) ; 0
|
(print (at grid 1 2)) (println "")) ; 0
|
||||||
|
|||||||
@ -8,5 +8,5 @@
|
|||||||
(retry [] 7)))
|
(retry [] 7)))
|
||||||
|
|
||||||
(defn main []
|
(defn main []
|
||||||
(print-i64 (i64 (load 1)))
|
(print (load 1))
|
||||||
(newline))
|
(println ""))
|
||||||
|
|||||||
@ -5,6 +5,6 @@
|
|||||||
;; catches it — the same message, and the program stops where it happened.
|
;; catches it — the same message, and the program stops where it happened.
|
||||||
(defn main []
|
(defn main []
|
||||||
(let [i 7]
|
(let [i 7]
|
||||||
(print-line "before")
|
(println "before")
|
||||||
(print-i64 (i64 (at xs i)))
|
(print (at xs i))
|
||||||
(print-line "unreachable")))
|
(println "unreachable")))
|
||||||
|
|||||||
@ -1,3 +1,3 @@
|
|||||||
before
|
before
|
||||||
bounds.flan:9:28: index 7 is out of bounds for length 3
|
bounds.flan:9:19: index 7 is out of bounds for length 3
|
||||||
exit 134
|
exit 134
|
||||||
|
|||||||
@ -11,5 +11,5 @@
|
|||||||
|
|
||||||
(defn main []
|
(defn main []
|
||||||
(agent/start "/tmp/flan-breakdemo.sock")
|
(agent/start "/tmp/flan-breakdemo.sock")
|
||||||
(print-i64 (i64 (load 1)))
|
(print (load 1))
|
||||||
(newline))
|
(println ""))
|
||||||
|
|||||||
@ -8,9 +8,9 @@
|
|||||||
|
|
||||||
(defn main []
|
(defn main []
|
||||||
(load-all) ; no handler: a no-op
|
(load-all) ; no handler: a no-op
|
||||||
(print-i64 seen) (newline) ; 0
|
(print seen) (println "") ; 0
|
||||||
|
|
||||||
;; A handler that returns normally accumulates and lets the signaller run on.
|
;; A handler that returns normally accumulates and lets the signaller run on.
|
||||||
(handler-bind [(AssetMissing [c] (set seen (+ seen (i64 (.id c)))))]
|
(handler-bind [(AssetMissing [c] (set seen (+ seen (i64 (.id c)))))]
|
||||||
(load-all))
|
(load-all))
|
||||||
(print-i64 seen) (newline)) ; 3
|
(print seen) (println "")) ; 3
|
||||||
|
|||||||
@ -9,10 +9,10 @@
|
|||||||
(defn countdown [n i32]
|
(defn countdown [n i32]
|
||||||
(let [i n]
|
(let [i n]
|
||||||
(while (> i 0)
|
(while (> i 0)
|
||||||
(print-i64 (i64 i))
|
(print i)
|
||||||
(print-str " ")
|
(print " ")
|
||||||
(set i (- i 1)))
|
(set i (- i 1)))
|
||||||
(newline)))
|
(println "")))
|
||||||
|
|
||||||
(defn first-even [s [i32]] (Option i32)
|
(defn first-even [s [i32]] (Option i32)
|
||||||
(dotimes [i (len s)]
|
(dotimes [i (len s)]
|
||||||
@ -21,10 +21,10 @@
|
|||||||
None)
|
None)
|
||||||
|
|
||||||
(defn main []
|
(defn main []
|
||||||
(print-line (classify -3))
|
(println (classify -3))
|
||||||
(countdown 4)
|
(countdown 4)
|
||||||
(unless false
|
(unless false
|
||||||
(print-line "unless runs when the test is false"))
|
(println "unless runs when the test is false"))
|
||||||
(match (first-even (slice nums 0 (len nums)))
|
(match (first-even (slice nums 0 (len nums)))
|
||||||
(Some n) (do (print-i64 (i64 n)) (newline))
|
(Some n) (do (print n) (println ""))
|
||||||
None (print-line "none")))
|
None (println "none")))
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
(defn work [n i32] i32
|
(defn work [n i32] i32
|
||||||
(defer (print-line "second"))
|
(defer (println "second"))
|
||||||
(defer (print-line "first")) ; innermost-first at exit
|
(defer (println "first")) ; innermost-first at exit
|
||||||
(when (< n 0)
|
(when (< n 0)
|
||||||
(return 0)) ; runs both defers above it
|
(return 0)) ; runs both defers above it
|
||||||
(print-line "body")
|
(println "body")
|
||||||
n)
|
n)
|
||||||
|
|
||||||
(defn main []
|
(defn main []
|
||||||
(print-i64 (i64 (work 3)))
|
(print (work 3))
|
||||||
(newline))
|
(println ""))
|
||||||
|
|||||||
@ -10,5 +10,5 @@
|
|||||||
(defn main []
|
(defn main []
|
||||||
;; :space resolves against the parameter's enum at compile time.
|
;; :space resolves against the parameter's enum at compile time.
|
||||||
;; A typo is an error here, not a wrong number later.
|
;; A typo is an error here, not a wrong number later.
|
||||||
(print-line (key-name :space))
|
(println (key-name :space))
|
||||||
(print-line (key-name :left)))
|
(println (key-name :left)))
|
||||||
|
|||||||
@ -3,4 +3,4 @@
|
|||||||
(declare cos-f64 [x f64] f64 "cos")
|
(declare cos-f64 [x f64] f64 "cos")
|
||||||
|
|
||||||
(defn main []
|
(defn main []
|
||||||
(print-f64 (cos-f64 0.0)) (newline))
|
(print (cos-f64 0.0)) (println ""))
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
(defvar grid [rows [cols u32]]) ; BSS, rows*cols*4 bytes
|
(defvar grid [rows [cols u32]]) ; BSS, rows*cols*4 bytes
|
||||||
|
|
||||||
(defn main []
|
(defn main []
|
||||||
(print-i64 (i64 cell-size)) (newline)
|
(print cell-size) (println "")
|
||||||
(print-f64 (f64 gravity)) (newline)
|
(print gravity) (println "")
|
||||||
(print-i64 (i64 current-color)) (newline)
|
(print current-color) (println "")
|
||||||
(print-i64 (i64 (at grid 2 3))) (newline))
|
(print (at grid 2 3)) (println ""))
|
||||||
|
|||||||
@ -1,2 +1,2 @@
|
|||||||
(defn main []
|
(defn main []
|
||||||
(print-line "hello from flan"))
|
(println "hello from flan"))
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
(let [n 40 ; i32, inferred
|
(let [n 40 ; i32, inferred
|
||||||
big (i64 n) ; every widening is written
|
big (i64 n) ; every widening is written
|
||||||
x 1.5] ; f64
|
x 1.5] ; f64
|
||||||
(print-i64 (+ big 2)) (newline)
|
(print (+ big 2)) (println "")
|
||||||
(print-f64 (* x 2.5)) (newline)
|
(print (* x 2.5)) (println "")
|
||||||
(print-i64 (i64 (bit-xor (<< 1 8) 255))) (newline)
|
(print (bit-xor (<< 1 8) 255)) (println "")
|
||||||
0))
|
0))
|
||||||
|
|||||||
@ -6,8 +6,8 @@
|
|||||||
|
|
||||||
(defn main []
|
(defn main []
|
||||||
(match (doubled-first (slice nums 0 4))
|
(match (doubled-first (slice nums 0 4))
|
||||||
(Some i) (do (print-i64 (i64 i)) (newline)) ; 4
|
(Some i) (do (print i) (println "")) ; 4
|
||||||
None (print-line "not found"))
|
None (println "not found"))
|
||||||
(match (index-of-i32 (slice nums 0 4) 99)
|
(match (index-of-i32 (slice nums 0 4) 99)
|
||||||
(Some i) (do (print-i64 (i64 i)) (newline))
|
(Some i) (do (print i) (println ""))
|
||||||
None (print-line "not found")))
|
None (println "not found")))
|
||||||
|
|||||||
@ -5,5 +5,5 @@
|
|||||||
(defn main []
|
(defn main []
|
||||||
(let [v (g/add (g/V2 {:x 3.0 :y 0.0})
|
(let [v (g/add (g/V2 {:x 3.0 :y 0.0})
|
||||||
(g/V2 {:x 0.0 :y 4.0}))]
|
(g/V2 {:x 0.0 :y 4.0}))]
|
||||||
(print-f64 (f64 (g/length v)))
|
(print (g/length v))
|
||||||
(newline)))
|
(println "")))
|
||||||
|
|||||||
@ -14,7 +14,7 @@
|
|||||||
(set (at room 2) 5) ; a fixed array or slice element
|
(set (at room 2) 5) ; a fixed array or slice element
|
||||||
(set (deref p) (Enemy {:hp 3 :name "wisp"})) ; a whole-object store
|
(set (deref p) (Enemy {:hp 3 :name "wisp"})) ; a whole-object store
|
||||||
|
|
||||||
(print-i64 (i64 (.hp e))) (newline)
|
(print (.hp e)) (println "")
|
||||||
(print-line (.name e))
|
(println (.name e))
|
||||||
(print-i64 (i64 (at room 2))) (newline)
|
(print (at room 2)) (println "")
|
||||||
(print-i64 (i64 spawned)) (newline)))
|
(print spawned) (println "")))
|
||||||
|
|||||||
13
web/examples/printing.flan
Normal file
13
web/examples/printing.flan
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
(defenum Key [space 32 left 263])
|
||||||
|
(defstruct Enemy [hp i32 name string key Key])
|
||||||
|
|
||||||
|
(defn look-up [k Key] (Option i32)
|
||||||
|
(if (= k :space) (Some 32) None))
|
||||||
|
|
||||||
|
(defn main []
|
||||||
|
(println 42) ; an i32, uncast
|
||||||
|
(println 1.5)
|
||||||
|
(println (Enemy {:hp 3 :name "wisp" :key :left}))
|
||||||
|
(println (look-up :space))
|
||||||
|
(println (look-up :left))
|
||||||
|
(print "no newline: ") (println true))
|
||||||
7
web/examples/printing.out
Normal file
7
web/examples/printing.out
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
42
|
||||||
|
1.5
|
||||||
|
(Enemy {:hp 3 :name "wisp" :key :left})
|
||||||
|
(some 32)
|
||||||
|
none
|
||||||
|
no newline: true
|
||||||
|
exit 0
|
||||||
@ -55,7 +55,7 @@ for pair in \
|
|||||||
'handle:(defvar h (Handle i32))' \
|
'handle:(defvar h (Handle i32))' \
|
||||||
'fnty:(defn f [g (Fn [i32] i32)] i32 (g 1))' \
|
'fnty:(defn f [g (Fn [i32] i32)] i32 (g 1))' \
|
||||||
'quoted:(defn f [] i32 (quote a))' \
|
'quoted:(defn f [] i32 (quote a))' \
|
||||||
'deferblock:(defn f [] i32 (let [x 1] (defer (print-line "a")) x))' \
|
'deferblock:(defn f [] i32 (let [x 1] (defer (println "a")) x))' \
|
||||||
'i64index:(defconst xs [3 i32] [1 2 3]) (defn main [] i32 (let [i (i64 1)] (at xs i)))' \
|
'i64index:(defconst xs [3 i32] [1 2 3]) (defn main [] i32 (let [i (i64 1)] (at xs i)))' \
|
||||||
'break:(defn main [] (let [i 0] (while (< i 3) (break))))'
|
'break:(defn main [] (let [i 0] (while (< i 3) (break))))'
|
||||||
do
|
do
|
||||||
@ -66,10 +66,13 @@ do
|
|||||||
done
|
done
|
||||||
rm -f "$here/.q.flan"
|
rm -f "$here/.q.flan"
|
||||||
|
|
||||||
# The cell and the transfer channel, from a real --dev emit of hello.flan.
|
# The cell and the transfer channel, from a real --dev emit. hello.flan cannot
|
||||||
ir=$("$FLAN" emit --dev "$here/hello.flan" 2>/dev/null)
|
# show a cell any more: println is compiler-provided rather than a Flan
|
||||||
want "cell global" "$(printf '%s\n' "$ir" | grep '^@"flan.cell.print-line"')"
|
# function, so the smallest program makes no Flan-to-Flan call at all. defer.flan
|
||||||
want "cell load" "$(printf '%s\n' "$ir" | grep 'load ptr, ptr @"flan.cell.print-line"')"
|
# 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"')"
|
want "xfer channel" "$(printf '%s\n' "$ir" | grep 'define {} @"flan.main"')"
|
||||||
|
|
||||||
# The generated C, from flan shim.
|
# The generated C, from flan shim.
|
||||||
|
|||||||
@ -16,9 +16,9 @@
|
|||||||
(retry [] 7)))
|
(retry [] 7)))
|
||||||
|
|
||||||
(defn main []
|
(defn main []
|
||||||
(print-i64 (i64 (fetch 1))) (newline) ; 101 — nothing handled it
|
(print (fetch 1)) (println "") ; 101 — nothing handled it
|
||||||
|
|
||||||
(handler-bind [(AssetMissing [c] (invoke-restart 'use-placeholder))]
|
(handler-bind [(AssetMissing [c] (invoke-restart 'use-placeholder))]
|
||||||
(print-i64 (i64 (fetch 2))) (newline)) ; -1
|
(print (fetch 2)) (println "")) ; -1
|
||||||
|
|
||||||
(print-i64 cleanups) (newline)) ; 2 — the defer ran both times
|
(print cleanups) (println "")) ; 2 — the defer ran both times
|
||||||
|
|||||||
@ -3,5 +3,5 @@
|
|||||||
(declare-c get-mouse-position [] Vector2 "GetMousePosition")
|
(declare-c get-mouse-position [] Vector2 "GetMousePosition")
|
||||||
|
|
||||||
(defn main []
|
(defn main []
|
||||||
(print-f64 (f64 (.x (get-mouse-position))))
|
(print (.x (get-mouse-position)))
|
||||||
(newline))
|
(println ""))
|
||||||
|
|||||||
@ -12,6 +12,6 @@
|
|||||||
|
|
||||||
(defn main []
|
(defn main []
|
||||||
(let [c (Cursor {:src (bytes "hi")})] ; pos omitted, so pos is 0
|
(let [c (Cursor {:src (bytes "hi")})] ; pos omitted, so pos is 0
|
||||||
(print-i64 (i64 (peek (addr c)))) (newline)
|
(print (peek (addr c))) (println "")
|
||||||
(advance (addr c))
|
(advance (addr c))
|
||||||
(print-i64 (i64 (peek (addr c)))) (newline)))
|
(print (peek (addr c))) (println "")))
|
||||||
|
|||||||
189
web/index.html
189
web/index.html
@ -200,6 +200,7 @@ footer { margin-top: 3.5rem; padding-top: 1.5rem; border-top: 1px solid var(--ru
|
|||||||
<li><a href="#control">Control flow</a></li>
|
<li><a href="#control">Control flow</a></li>
|
||||||
<li><a href="#defer">defer</a></li>
|
<li><a href="#defer">defer</a></li>
|
||||||
<li><a href="#arrays">Arrays and slices</a></li>
|
<li><a href="#arrays">Arrays and slices</a></li>
|
||||||
|
<li><a href="#printing">Printing</a></li>
|
||||||
<li><a href="#prelude">The prelude</a></li>
|
<li><a href="#prelude">The prelude</a></li>
|
||||||
<li><a href="#packages">Packages</a></li>
|
<li><a href="#packages">Packages</a></li>
|
||||||
<li><a href="#conditions">Conditions and restarts</a></li>
|
<li><a href="#conditions">Conditions and restarts</a></li>
|
||||||
@ -286,7 +287,7 @@ produced. <code>run</code> builds to a temporary file and execs it.</p>
|
|||||||
<p>The smallest program:</p>
|
<p>The smallest program:</p>
|
||||||
|
|
||||||
<pre><code>(defn main []
|
<pre><code>(defn main []
|
||||||
(print-line "hello from flan"))</code></pre>
|
(println "hello from flan"))</code></pre>
|
||||||
|
|
||||||
<p>The entry point is <code>(defn main [args [string]] i32)</code>. Both the parameter
|
<p>The entry point is <code>(defn main [args [string]] i32)</code>. Both the parameter
|
||||||
and the return type are optional: omitting <code>args</code> means the program ignores
|
and the return type are optional: omitting <code>args</code> means the program ignores
|
||||||
@ -337,10 +338,10 @@ heap is involved.</p>
|
|||||||
(set (at room 2) 5) ; a fixed array or slice element
|
(set (at room 2) 5) ; a fixed array or slice element
|
||||||
(set (deref p) (Enemy {:hp 3 :name "wisp"})) ; a whole-object store
|
(set (deref p) (Enemy {:hp 3 :name "wisp"})) ; a whole-object store
|
||||||
|
|
||||||
(print-i64 (i64 (.hp e))) (newline)
|
(print (.hp e)) (println "")
|
||||||
(print-line (.name e))
|
(println (.name e))
|
||||||
(print-i64 (i64 (at room 2))) (newline)
|
(print (at room 2)) (println "")
|
||||||
(print-i64 (i64 spawned)) (newline)))</code></pre>
|
(print spawned) (println "")))</code></pre>
|
||||||
|
|
||||||
<pre><code class="sh">3
|
<pre><code class="sh">3
|
||||||
wisp
|
wisp
|
||||||
@ -361,13 +362,13 @@ wisp
|
|||||||
;; catches it — the same message, and the program stops where it happened.
|
;; catches it — the same message, and the program stops where it happened.
|
||||||
(defn main []
|
(defn main []
|
||||||
(let [i 7]
|
(let [i 7]
|
||||||
(print-line "before")
|
(println "before")
|
||||||
(print-i64 (i64 (at xs i)))
|
(print (at xs i))
|
||||||
(print-line "unreachable")))</code></pre>
|
(println "unreachable")))</code></pre>
|
||||||
|
|
||||||
<pre><code class="sh">$ flan run bounds.flan
|
<pre><code class="sh">$ flan run bounds.flan
|
||||||
before
|
before
|
||||||
bounds.flan:9:28: index 7 is out of bounds for length 3
|
bounds.flan:9:19: index 7 is out of bounds for length 3
|
||||||
$ echo $?
|
$ echo $?
|
||||||
134</code></pre>
|
134</code></pre>
|
||||||
|
|
||||||
@ -408,9 +409,9 @@ have one type, and every conversion is written as a cast:</p>
|
|||||||
(let [n 40 ; i32, inferred
|
(let [n 40 ; i32, inferred
|
||||||
big (i64 n) ; every widening is written
|
big (i64 n) ; every widening is written
|
||||||
x 1.5] ; f64
|
x 1.5] ; f64
|
||||||
(print-i64 (+ big 2)) (newline)
|
(print (+ big 2)) (println "")
|
||||||
(print-f64 (* x 2.5)) (newline)
|
(print (* x 2.5)) (println "")
|
||||||
(print-i64 (i64 (bit-xor (<< 1 8) 255))) (newline)
|
(print (bit-xor (<< 1 8) 255)) (println "")
|
||||||
0))</code></pre>
|
0))</code></pre>
|
||||||
|
|
||||||
<pre><code class="sh">42
|
<pre><code class="sh">42
|
||||||
@ -455,9 +456,9 @@ its fields, and omitted fields are zeroed.</p>
|
|||||||
|
|
||||||
(defn main []
|
(defn main []
|
||||||
(let [c (Cursor {:src (bytes "hi")})] ; pos omitted, so pos is 0
|
(let [c (Cursor {:src (bytes "hi")})] ; pos omitted, so pos is 0
|
||||||
(print-i64 (i64 (peek (addr c)))) (newline)
|
(print (peek (addr c))) (println "")
|
||||||
(advance (addr c))
|
(advance (addr c))
|
||||||
(print-i64 (i64 (peek (addr c)))) (newline)))</code></pre>
|
(print (peek (addr c))) (println "")))</code></pre>
|
||||||
|
|
||||||
<pre><code class="sh">104
|
<pre><code class="sh">104
|
||||||
105</code></pre>
|
105</code></pre>
|
||||||
@ -466,7 +467,8 @@ its fields, and omitted fields are zeroed.</p>
|
|||||||
byte is a <code>u8</code> — but there is a byte literal, so <code>\h</code> is 104 and
|
byte is a <code>u8</code> — but there is a byte literal, so <code>\h</code> is 104 and
|
||||||
<code>\space</code> is 32, and the prelude's <code>digit?</code> reads as
|
<code>\space</code> is 32, and the prelude's <code>digit?</code> reads as
|
||||||
<code>(and (>= b \0) (<= b \9))</code>. To see a byte as a letter rather than as a
|
<code>(and (>= b \0) (<= b \9))</code>. To see a byte as a letter rather than as a
|
||||||
number, print a slice of them with <code>print-bytes</code>.</p>
|
number, print a slice of them: <code>print</code> writes a <code>[u8]</code> as
|
||||||
|
its bytes.</p>
|
||||||
|
|
||||||
<p>An enum is an <code>i32</code> at run time and its own type in the checker. A
|
<p>An enum is an <code>i32</code> at run time and its own type in the checker. A
|
||||||
keyword at a call site resolves against the parameter's enum type at compile time, so a
|
keyword at a call site resolves against the parameter's enum type at compile time, so a
|
||||||
@ -484,8 +486,8 @@ typo is an error there rather than a wrong number later.</p>
|
|||||||
(defn main []
|
(defn main []
|
||||||
;; :space resolves against the parameter's enum at compile time.
|
;; :space resolves against the parameter's enum at compile time.
|
||||||
;; A typo is an error here, not a wrong number later.
|
;; A typo is an error here, not a wrong number later.
|
||||||
(print-line (key-name :space))
|
(println (key-name :space))
|
||||||
(print-line (key-name :left)))</code></pre>
|
(println (key-name :left)))</code></pre>
|
||||||
|
|
||||||
<pre><code class="sh">space
|
<pre><code class="sh">space
|
||||||
an arrow</code></pre>
|
an arrow</code></pre>
|
||||||
@ -515,10 +517,10 @@ functions need no forward declaration. Globals come in two kinds:</p>
|
|||||||
(defvar grid [rows [cols u32]]) ; BSS, rows*cols*4 bytes
|
(defvar grid [rows [cols u32]]) ; BSS, rows*cols*4 bytes
|
||||||
|
|
||||||
(defn main []
|
(defn main []
|
||||||
(print-i64 (i64 cell-size)) (newline)
|
(print cell-size) (println "")
|
||||||
(print-f64 (f64 gravity)) (newline)
|
(print gravity) (println "")
|
||||||
(print-i64 (i64 current-color)) (newline)
|
(print current-color) (println "")
|
||||||
(print-i64 (i64 (at grid 2 3))) (newline))</code></pre>
|
(print (at grid 2 3)) (println ""))</code></pre>
|
||||||
|
|
||||||
<pre><code class="sh">5
|
<pre><code class="sh">5
|
||||||
0.05
|
0.05
|
||||||
@ -551,10 +553,10 @@ whose type matters is named at the top level rather than written inline.</p>
|
|||||||
(defn countdown [n i32]
|
(defn countdown [n i32]
|
||||||
(let [i n]
|
(let [i n]
|
||||||
(while (> i 0)
|
(while (> i 0)
|
||||||
(print-i64 (i64 i))
|
(print i)
|
||||||
(print-str " ")
|
(print " ")
|
||||||
(set i (- i 1)))
|
(set i (- i 1)))
|
||||||
(newline)))
|
(println "")))
|
||||||
|
|
||||||
(defn first-even [s [i32]] (Option i32)
|
(defn first-even [s [i32]] (Option i32)
|
||||||
(dotimes [i (len s)]
|
(dotimes [i (len s)]
|
||||||
@ -563,13 +565,13 @@ whose type matters is named at the top level rather than written inline.</p>
|
|||||||
None)
|
None)
|
||||||
|
|
||||||
(defn main []
|
(defn main []
|
||||||
(print-line (classify -3))
|
(println (classify -3))
|
||||||
(countdown 4)
|
(countdown 4)
|
||||||
(unless false
|
(unless false
|
||||||
(print-line "unless runs when the test is false"))
|
(println "unless runs when the test is false"))
|
||||||
(match (first-even (slice nums 0 (len nums)))
|
(match (first-even (slice nums 0 (len nums)))
|
||||||
(Some n) (do (print-i64 (i64 n)) (newline))
|
(Some n) (do (print n) (println ""))
|
||||||
None (print-line "none")))</code></pre>
|
None (println "none")))</code></pre>
|
||||||
|
|
||||||
<pre><code class="sh">negative
|
<pre><code class="sh">negative
|
||||||
4 3 2 1
|
4 3 2 1
|
||||||
@ -604,11 +606,11 @@ on nothing else today. <code>some</code> unwraps <code>Some</code> and early-ret
|
|||||||
|
|
||||||
(defn main []
|
(defn main []
|
||||||
(match (doubled-first (slice nums 0 4))
|
(match (doubled-first (slice nums 0 4))
|
||||||
(Some i) (do (print-i64 (i64 i)) (newline)) ; 4
|
(Some i) (do (print i) (println "")) ; 4
|
||||||
None (print-line "not found"))
|
None (println "not found"))
|
||||||
(match (index-of-i32 (slice nums 0 4) 99)
|
(match (index-of-i32 (slice nums 0 4) 99)
|
||||||
(Some i) (do (print-i64 (i64 i)) (newline))
|
(Some i) (do (print i) (println ""))
|
||||||
None (print-line "not found")))</code></pre>
|
None (println "not found")))</code></pre>
|
||||||
|
|
||||||
<pre><code class="sh">4
|
<pre><code class="sh">4
|
||||||
not found</code></pre>
|
not found</code></pre>
|
||||||
@ -620,16 +622,16 @@ not found</code></pre>
|
|||||||
has not executed yet and must not fire.</p>
|
has not executed yet and must not fire.</p>
|
||||||
|
|
||||||
<pre><code>(defn work [n i32] i32
|
<pre><code>(defn work [n i32] i32
|
||||||
(defer (print-line "second"))
|
(defer (println "second"))
|
||||||
(defer (print-line "first")) ; innermost-first at exit
|
(defer (println "first")) ; innermost-first at exit
|
||||||
(when (< n 0)
|
(when (< n 0)
|
||||||
(return 0)) ; runs both defers above it
|
(return 0)) ; runs both defers above it
|
||||||
(print-line "body")
|
(println "body")
|
||||||
n)
|
n)
|
||||||
|
|
||||||
(defn main []
|
(defn main []
|
||||||
(print-i64 (i64 (work 3)))
|
(print (work 3))
|
||||||
(newline))</code></pre>
|
(println ""))</code></pre>
|
||||||
|
|
||||||
<pre><code class="sh">body
|
<pre><code class="sh">body
|
||||||
first
|
first
|
||||||
@ -660,18 +662,18 @@ It is a place: <code>(set (at grid r c) v)</code> and <code>(addr (at grid r c))
|
|||||||
|
|
||||||
(defn main []
|
(defn main []
|
||||||
(set (at grid 1 2) 7)
|
(set (at grid 1 2) 7)
|
||||||
(print-i64 (i64 (at grid 1 2))) (newline) ; 7
|
(print (at grid 1 2)) (println "") ; 7
|
||||||
(print-i64 (i64 (len palette))) (newline) ; 4
|
(print (len palette)) (println "") ; 4
|
||||||
|
|
||||||
;; A slice is ptr+len and non-owning: it views the array, it does not copy it.
|
;; 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)]
|
(let [row (slice (at grid 1) 0 cols)]
|
||||||
(set (at row 0) 5)
|
(set (at row 0) 5)
|
||||||
(print-i64 (i64 (at grid 1 0))) (newline) ; 5 — the same storage
|
(print (at grid 1 0)) (println "") ; 5 — the same storage
|
||||||
(print-i64 (sum-i32 row)) (newline)) ; 12
|
(print (sum-i32 row)) (println "")) ; 12
|
||||||
|
|
||||||
;; (zeroed) is a memset, not an allocation.
|
;; (zeroed) is a memset, not an allocation.
|
||||||
(set grid (zeroed))
|
(set grid (zeroed))
|
||||||
(print-i64 (i64 (at grid 1 2))) (newline)) ; 0</code></pre>
|
(print (at grid 1 2)) (println "")) ; 0</code></pre>
|
||||||
|
|
||||||
<pre><code class="sh">7
|
<pre><code class="sh">7
|
||||||
4
|
4
|
||||||
@ -682,17 +684,72 @@ It is a place: <code>(set (at grid r c) v)</code> and <code>(addr (at grid r c))
|
|||||||
<p>A reversed range — <code>lo</code> greater than <code>hi</code> — traps, rather than
|
<p>A reversed range — <code>lo</code> greater than <code>hi</code> — traps, rather than
|
||||||
yielding a huge unsigned length.</p>
|
yielding a huge unsigned length.</p>
|
||||||
|
|
||||||
|
<h2 id="printing">Printing</h2>
|
||||||
|
|
||||||
|
<p><code>println</code> prints a value and a newline; <code>print</code> is the same
|
||||||
|
walk without the newline. There is one of each and they take any type, but neither is a
|
||||||
|
function and neither is overloading: the compiler walks the argument's type where the
|
||||||
|
call is written and emits the printing for it. Nothing is decided at run time — a Flan
|
||||||
|
value carries no header, so nothing at run time could say what it is — and there is no
|
||||||
|
user-supplied printer to choose between.</p>
|
||||||
|
|
||||||
|
<pre><code>(defenum Key [space 32 left 263])
|
||||||
|
(defstruct Enemy [hp i32 name string key Key])
|
||||||
|
|
||||||
|
(defn look-up [k Key] (Option i32)
|
||||||
|
(if (= k :space) (Some 32) None))
|
||||||
|
|
||||||
|
(defn main []
|
||||||
|
(println 42) ; an i32, uncast
|
||||||
|
(println 1.5)
|
||||||
|
(println (Enemy {:hp 3 :name "wisp" :key :left}))
|
||||||
|
(println (look-up :space))
|
||||||
|
(println (look-up :left))
|
||||||
|
(print "no newline: ") (println true))</code></pre>
|
||||||
|
|
||||||
|
<pre><code class="sh">42
|
||||||
|
1.5
|
||||||
|
(Enemy {:hp 3 :name "wisp" :key :left})
|
||||||
|
(some 32)
|
||||||
|
none
|
||||||
|
no newline: true</code></pre>
|
||||||
|
|
||||||
|
<p>The walk covers every integer and float type, <code>bool</code>, <code>Unit</code>,
|
||||||
|
<code>string</code>, <code>[u8]</code>, enums, <code>Ptr</code>, <code>Option</code>,
|
||||||
|
structs, fixed arrays and slices. An enum member comes back as its name: the value is
|
||||||
|
an <code>i32</code> by the time the backend sees it, so the name is recovered here from
|
||||||
|
the checker's table, and a value outside the declared members falls through to the
|
||||||
|
number, which is what you would want to see. A <code>Ptr</code> prints as
|
||||||
|
<code><ptr></code> and is never followed — it is the one thing that could make
|
||||||
|
the walk cycle, and dereferencing a pointer on someone else's behalf is not safe.</p>
|
||||||
|
|
||||||
|
<p>A string prints raw at the top level and quoted-and-escaped inside a structure.
|
||||||
|
Those are not in conflict: <code>(println "hello")</code> has to print
|
||||||
|
<code>hello</code> or it is useless, and the <code>name</code> field above has to be
|
||||||
|
quoted or it could not be told from the punctuation around it.</p>
|
||||||
|
|
||||||
|
<p>The walk is bounded in depth and in span, so a deeply nested value or a
|
||||||
|
<code>[100 [100 u32]]</code> grid prints <code>...</code> rather than a screenful — and,
|
||||||
|
since the walk is unrolled at compile time, rather than putting ten thousand printing
|
||||||
|
sites in the module.</p>
|
||||||
|
|
||||||
|
<p>That the walk takes the argument's own type matters more here than it would in a
|
||||||
|
language that widens implicitly. Nothing widens implicitly in this one, so a printer
|
||||||
|
that named a type would need a cast written at every call — and a <code>u64</code>
|
||||||
|
above 2<sup>63</sup> put through a signed one comes out negative. <code>print</code>
|
||||||
|
takes the value as it is and prints the number it holds.</p>
|
||||||
|
|
||||||
<h2 id="prelude">The prelude</h2>
|
<h2 id="prelude">The prelude</h2>
|
||||||
|
|
||||||
<p>The prelude is written in Flan, all but one line of it, and prepended to every
|
<p>The prelude is written in Flan, all but one line of it, and prepended to every
|
||||||
program, so nothing in it needs importing. Printing is deliberately not a primitive:
|
program, so nothing in it needs importing. It holds no printing of its own:
|
||||||
<code>write-stdout</code> is the one output primitive and everything above it is
|
<code>print</code> and <code>println</code> are the compiler's, and
|
||||||
ordinary Flan.</p>
|
<code>write-stdout</code> — the one output primitive — is what they are written
|
||||||
|
over.</p>
|
||||||
|
|
||||||
<div class="scroll">
|
<div class="scroll">
|
||||||
<table>
|
<table>
|
||||||
<tr><th>Group</th><th>Names</th></tr>
|
<tr><th>Group</th><th>Names</th></tr>
|
||||||
<tr><td>output</td><td><code>print-str</code>, <code>print-bytes</code>, <code>print-i64</code>, <code>print-f64</code>, <code>print-line</code>, <code>newline</code></td></tr>
|
|
||||||
<tr><td>slices of <code>i32</code></td><td><code>swap-i32!</code>, <code>reverse-i32!</code>, <code>sort-i32!</code>, <code>index-of-i32</code>, <code>min-i32</code>, <code>max-i32</code>, <code>sum-i32</code></td></tr>
|
<tr><td>slices of <code>i32</code></td><td><code>swap-i32!</code>, <code>reverse-i32!</code>, <code>sort-i32!</code>, <code>index-of-i32</code>, <code>min-i32</code>, <code>max-i32</code>, <code>sum-i32</code></td></tr>
|
||||||
<tr><td>bytes</td><td><code>bytes=?</code>, <code>starts-with?</code>, <code>ends-with?</code>, <code>index-of-byte</code>, <code>index-of-bytes</code>, <code>trim</code>, <code>digit?</code>, <code>space?</code></td></tr>
|
<tr><td>bytes</td><td><code>bytes=?</code>, <code>starts-with?</code>, <code>ends-with?</code>, <code>index-of-byte</code>, <code>index-of-bytes</code>, <code>trim</code>, <code>digit?</code>, <code>space?</code></td></tr>
|
||||||
<tr><td>parsing</td><td><code>parse-i64</code>, <code>parse-f64</code></td></tr>
|
<tr><td>parsing</td><td><code>parse-i64</code>, <code>parse-f64</code></td></tr>
|
||||||
@ -754,8 +811,8 @@ and no ceremony.</p>
|
|||||||
(defn main []
|
(defn main []
|
||||||
(let [v (g/add (g/V2 {:x 3.0 :y 0.0})
|
(let [v (g/add (g/V2 {:x 3.0 :y 0.0})
|
||||||
(g/V2 {:x 0.0 :y 4.0}))]
|
(g/V2 {:x 0.0 :y 4.0}))]
|
||||||
(print-f64 (f64 (g/length v)))
|
(print (g/length v))
|
||||||
(newline)))</code></pre>
|
(println "")))</code></pre>
|
||||||
|
|
||||||
<pre><code class="sh">5</code></pre>
|
<pre><code class="sh">5</code></pre>
|
||||||
|
|
||||||
@ -827,12 +884,12 @@ normally leaves the signaller to carry on — the accumulation case:</p>
|
|||||||
|
|
||||||
(defn main []
|
(defn main []
|
||||||
(load-all) ; no handler: a no-op
|
(load-all) ; no handler: a no-op
|
||||||
(print-i64 seen) (newline) ; 0
|
(print seen) (println "") ; 0
|
||||||
|
|
||||||
;; A handler that returns normally accumulates and lets the signaller run on.
|
;; A handler that returns normally accumulates and lets the signaller run on.
|
||||||
(handler-bind [(AssetMissing [c] (set seen (+ seen (i64 (.id c)))))]
|
(handler-bind [(AssetMissing [c] (set seen (+ seen (i64 (.id c)))))]
|
||||||
(load-all))
|
(load-all))
|
||||||
(print-i64 seen) (newline)) ; 3</code></pre>
|
(print seen) (println "")) ; 3</code></pre>
|
||||||
|
|
||||||
<pre><code class="sh">0
|
<pre><code class="sh">0
|
||||||
3</code></pre>
|
3</code></pre>
|
||||||
@ -860,12 +917,12 @@ first, before the clause body starts.</p>
|
|||||||
(retry [] 7)))
|
(retry [] 7)))
|
||||||
|
|
||||||
(defn main []
|
(defn main []
|
||||||
(print-i64 (i64 (fetch 1))) (newline) ; 101 — nothing handled it
|
(print (fetch 1)) (println "") ; 101 — nothing handled it
|
||||||
|
|
||||||
(handler-bind [(AssetMissing [c] (invoke-restart 'use-placeholder))]
|
(handler-bind [(AssetMissing [c] (invoke-restart 'use-placeholder))]
|
||||||
(print-i64 (i64 (fetch 2))) (newline)) ; -1
|
(print (fetch 2)) (println "")) ; -1
|
||||||
|
|
||||||
(print-i64 cleanups) (newline)) ; 2 — the defer ran both times</code></pre>
|
(print cleanups) (println "")) ; 2 — the defer ran both times</code></pre>
|
||||||
|
|
||||||
<pre><code class="sh">101
|
<pre><code class="sh">101
|
||||||
-1
|
-1
|
||||||
@ -947,8 +1004,8 @@ and an exit status of 134:</p>
|
|||||||
(retry [] 7)))
|
(retry [] 7)))
|
||||||
|
|
||||||
(defn main []
|
(defn main []
|
||||||
(print-i64 (i64 (load 1)))
|
(print (load 1))
|
||||||
(newline))</code></pre>
|
(println ""))</code></pre>
|
||||||
|
|
||||||
<pre><code class="sh">$ flan run boom.flan
|
<pre><code class="sh">$ flan run boom.flan
|
||||||
unhandled Missing
|
unhandled Missing
|
||||||
@ -966,7 +1023,7 @@ is generated; a Flan string crosses as ptr+len, exactly as it is stored.</p>
|
|||||||
<pre><code>(declare cos-f64 [x f64] f64 "cos")
|
<pre><code>(declare cos-f64 [x f64] f64 "cos")
|
||||||
|
|
||||||
(defn main []
|
(defn main []
|
||||||
(print-f64 (cos-f64 0.0)) (newline))</code></pre>
|
(print (cos-f64 0.0)) (println ""))</code></pre>
|
||||||
|
|
||||||
<pre><code class="sh">1</code></pre>
|
<pre><code class="sh">1</code></pre>
|
||||||
|
|
||||||
@ -1072,20 +1129,22 @@ bound at link time cannot be made to notice one, so a <code>--dev</code> build r
|
|||||||
every Flan-to-Flan call through a cell — a mutable global holding the address of the
|
every Flan-to-Flan call through a cell — a mutable global holding the address of the
|
||||||
function that is current.</p>
|
function that is current.</p>
|
||||||
|
|
||||||
<p>Here is the whole of <code>hello.flan</code> through
|
<p>Here is the <code>defer</code> example from <a href="#defer">above</a> — its
|
||||||
<code>flan emit --dev</code>:</p>
|
<code>main</code> calls <code>work</code> — through <code>flan emit --dev</code>:</p>
|
||||||
|
|
||||||
<pre><code class="llvm">@"flan.cell.print-line" = global ptr @"flan.print-line"
|
<pre><code class="llvm">@"flan.cell.work" = global ptr @"flan.work"
|
||||||
|
|
||||||
define {} @"flan.main"(ptr %xfer) {
|
define {} @"flan.main"(ptr %xfer) {
|
||||||
entry:
|
entry:
|
||||||
%t1 = load ptr, ptr @"flan.cell.print-line"
|
%t7 = alloca %slice
|
||||||
%t2 = call {} %t1(%slice { ptr @".str.36", i64 15 }, ptr %xfer)</code></pre>
|
%t1 = load ptr, ptr @"flan.cell.work"
|
||||||
|
%t2 = call i32 %t1(i32 3, ptr %xfer)</code></pre>
|
||||||
|
|
||||||
<p>The call site loads the cell rather than naming <code>@"flan.print-line"</code>
|
<p>The call site loads the cell rather than naming <code>@"flan.work"</code>
|
||||||
directly. The signature carries <code>ptr %xfer</code> — the transfer channel from
|
directly. The signature carries <code>ptr %xfer</code> — the transfer channel from
|
||||||
<a href="#conditions">conditions</a>, on every Flan function, release builds
|
<a href="#conditions">conditions</a>, on every Flan function, release builds
|
||||||
included.</p>
|
included. <code>println</code> is not in there: it is not a Flan function, so there is
|
||||||
|
no call to route and no cell for it.</p>
|
||||||
|
|
||||||
<p>Redefinition is then one store, below a microsecond. Three rules follow. A
|
<p>Redefinition is then one store, below a microsecond. Three rules follow. A
|
||||||
redefinition module declares every <em>global</em> external, so globals live in the host
|
redefinition module declares every <em>global</em> external, so globals live in the host
|
||||||
@ -1242,10 +1301,10 @@ module, and the headless sand acceptance program prints the same 64-bit hash und
|
|||||||
it does natively:</p>
|
it does natively:</p>
|
||||||
|
|
||||||
<pre><code class="sh">$ flan run test/programs/sand-headless.flan
|
<pre><code class="sh">$ flan run test/programs/sand-headless.flan
|
||||||
-2851001042534928384
|
15595743031174623232
|
||||||
$ flan build test/programs/sand-headless.flan --target=wasm32-wasi -o sand.wasm
|
$ flan build test/programs/sand-headless.flan --target=wasm32-wasi -o sand.wasm
|
||||||
$ node --no-warnings test/wasm-run.mjs sand.wasm
|
$ node --no-warnings test/wasm-run.mjs sand.wasm
|
||||||
-2851001042534928384</code></pre>
|
15595743031174623232</code></pre>
|
||||||
|
|
||||||
<p>The RNG is written in Flan rather than called from libc for that number: a grid hash
|
<p>The RNG is written in Flan rather than called from libc for that number: a grid hash
|
||||||
is only a regression test if the sequence is byte-identical on both targets. It holds at
|
is only a regression test if the sequence is byte-identical on both targets. It holds at
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user