The trio the author decided on 2026-09-20 is now all built: def is CL's defparameter — its initialiser runs on every daemon re-run, unguarded, so an edited initialiser repaints the same storage on C-c C-c plus re-run — defonce (Clojure's name for CL's defvar, per the author) initialises once behind the .init~once. flag, and defconst stays the image. One parse arm reads both forms; the difference is Ast.reinit, carried to Tast.global's grerun. Emit.startup_plan gives a def no guard flag, and Check.check_global lifts every def initialiser — zero and literal included — into global/<n>, so the host's startup reaches it through the function cell and a re-evaluated def swaps it (Session's def_inits; Emit.redefinition declares the cell for a non-sibling target). The old defvar spelling is refused with the rename and both compiling spellings, and every program, test, doc and editor list is swept — except sand.flan, the author's live WIP, whose seven defvar lines are flagged in FIX.org and keep its three dependent tests red on this branch.
90 lines
3.3 KiB
Plaintext
90 lines
3.3 KiB
Plaintext
;; The untagged union: one storage, several ways of reading it.
|
|
;;
|
|
;; Every line here is a fact about *bytes*, which is the whole reason the
|
|
;; program exists rather than a checker row. A union's layout is the only
|
|
;; thing about it that can be wrong silently: reading the member that was not
|
|
;; written is defined behaviour in Flan, so nothing at run time would notice a
|
|
;; member placed at the wrong offset or a type sized to the wrong member --
|
|
;; the numbers would simply be different ones. So the numbers are written
|
|
;; down, and both backends have to produce them.
|
|
;;
|
|
;; 0x3F800000 is 1.0f and 0x4000000000000000 is 2.0. They are here as decimal
|
|
;; literals because that is what a reader who doubts the output has to be able
|
|
;; to check by hand against IEEE 754, and a hex literal would only move the
|
|
;; question.
|
|
|
|
(defstruct P [x f32 y f32])
|
|
|
|
(defunion Bits [i i32 f f32 bs [4 u8]])
|
|
(defunion Wide [n i64 d f64 p P bs [8 u8]])
|
|
|
|
;; A union inside a struct, which is the FFI shape: the C library keeps the
|
|
;; tag beside the union and the rule relating them is prose in its manual, so
|
|
;; `kind' here is an ordinary field this program reads itself.
|
|
(defstruct Slot [kind i32 v Bits])
|
|
|
|
;; Zeroed and uninit, side by side. A data type refuses uninit because its tag
|
|
;; steers a match; this one has no tag to steer anything, so both are legal
|
|
;; and the zeroed one is all-bytes-zero.
|
|
(defonce zeroed Bits)
|
|
(defonce scratch Bits uninit)
|
|
|
|
(defn as-float [b Bits] f32 (.f b))
|
|
|
|
(defn of-float [x f32] Bits (Bits {.f x}))
|
|
|
|
(defn main [] i32
|
|
;; Punning, both directions, through the same storage.
|
|
(let [b (Bits {.i 1065353216})]
|
|
(println (.f b))
|
|
(println (.i b))
|
|
;; The bytes little-endian: 0x3F800000 is 00 00 80 3F.
|
|
(println (at (.bs b) 0))
|
|
(println (at (.bs b) 3))
|
|
;; A member written through a place, which is the other half of the same
|
|
;; claim -- the read above could have been folded from the literal, and a
|
|
;; store into the union could not.
|
|
(set (.f b) 2.0)
|
|
(println (.i b)))
|
|
|
|
;; The union crosses a call boundary in both directions by value.
|
|
(println (as-float (of-float 0.5)))
|
|
|
|
;; A wider union: the size is the widest member and not the first one. An
|
|
;; array of two is where that shows up -- writing element 1 would land
|
|
;; inside element 0 if the type were sized to its i64 member alone and the
|
|
;; f64 or the [8 u8] were wider, and every element's value would change
|
|
;; under the other's write.
|
|
(let [w (Wide {.d 2.0})]
|
|
(println (.n w))
|
|
(println (.x (.p w))))
|
|
|
|
(let [ws (array 2 Wide)]
|
|
(set (.n (at ws 0)) 11)
|
|
(set (.n (at ws 1)) 22)
|
|
(println (.n (at ws 0)))
|
|
(println (.n (at ws 1))))
|
|
|
|
;; ZII: a union with no member given is all-bytes-zero, and so is a global
|
|
;; declared with no value.
|
|
(let [empty (Bits {})]
|
|
(println (.i empty)))
|
|
(println (.i zeroed))
|
|
;; And a global is written and read like any other place.
|
|
(set (.i scratch) 7)
|
|
(println (.i scratch))
|
|
|
|
;; A union inside a struct, with the tag the program keeps itself.
|
|
(let [s (Slot {.kind 1 .v (Bits {.f 1.5})})]
|
|
(println (.kind s))
|
|
(println (.f (.v s)))
|
|
(set (.kind s) 2)
|
|
(set (.i (.v s)) 9)
|
|
(println (.kind s))
|
|
(println (.i (.v s))))
|
|
|
|
;; Printed by name and not walked: the printer cannot know which member is
|
|
;; live, and a member may be a pointer.
|
|
(println zeroed)
|
|
0)
|