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.
133 lines
5.7 KiB
Plaintext
133 lines
5.7 KiB
Plaintext
;;;; examples/text-codepoints-loading.flan's other half: the scan, the
|
|
;;;; deduplication and the codepoint walk, with no window and no font.
|
|
;;;;
|
|
;;;; The same split core-input-virtual-controls.flan already has. What makes it
|
|
;;;; available here is that the *interesting* part of that example is not the
|
|
;;;; drawing: it is which codepoints a piece of UTF-8 contains, which of them
|
|
;;;; are distinct, and where each one starts in the bytes. All three are
|
|
;;;; arithmetic, LoadCodepoints needs no GL context, and the example's `main`
|
|
;;;; is not exported — so this runs the same code the window runs, without one.
|
|
;;;;
|
|
;;;; Three things are pinned here and they fail for three different reasons.
|
|
;;;;
|
|
;;;; **1. The text survived.** 49 distinct codepoints out of 54 is a
|
|
;;;; property of the Iroha and of nothing else, so a literal that lost a byte
|
|
;;;; between the reader and the object file changes both numbers. The first
|
|
;;;; five distinct codepoints are printed as well, because a count alone would
|
|
;;;; survive a re-ordering.
|
|
;;;;
|
|
;;;; **2. The walk agrees with itself.** The text is stepped from the first
|
|
;;;; codepoint to the last, and then back from the last to the first, and the
|
|
;;;; two sequences are compared. That is what pins `step-back`, which is
|
|
;;;; GetCodepointPrevious — a function that reads *backwards* out of the
|
|
;;;; pointer it is handed, and so the one raylib call a Flan `string` must
|
|
;;;; never reach, because a string crosses to C as a NUL-terminated copy and
|
|
;;;; the bytes in front of a copy are the allocator's. Handed a copy it
|
|
;;;; answers 0 with a size of 0, which is also its answer for malformed UTF-8;
|
|
;;;; step-back would then return its argument unchanged, the backward walk
|
|
;;;; would never reach offset 0, and the two sequences would stop being
|
|
;;;; reverses of each other. That is the shape of the wrong answer this row
|
|
;;;; exists to see.
|
|
;;;;
|
|
;;;; **3. The walk agrees with raylib.** The sequence the walk produces is
|
|
;;;; compared against the array LoadCodepoints returned, element for element.
|
|
;;;; Item 2 on its own would pass if both walks were wrong in the same way;
|
|
;;;; this is the outside opinion, and it is raylib's rather than ours.
|
|
;;;;
|
|
;;;; Nothing here draws, so nothing here needs the TTF the example looks for.
|
|
|
|
(import cp "../../examples/text-codepoints-loading.flan")
|
|
(import rl "vendor:raylib")
|
|
|
|
(defconst max-walk 128)
|
|
|
|
(defonce forward [max-walk i32])
|
|
(defonce forward-n i32)
|
|
(defonce backward [max-walk i32])
|
|
(defonce backward-n i32)
|
|
|
|
(defn yes-no [b bool] string (if b "yes" "no"))
|
|
|
|
(defn show [name string n i32] ()
|
|
(print name) (print " ") (println n))
|
|
|
|
;; From the first codepoint to the last. step-forward clamps rather than
|
|
;; running off the end — the C does not, which is a bug it gets away with —
|
|
;; so the walk is over when the offset stops moving.
|
|
(defn walk-forward [] ()
|
|
(set forward-n 0)
|
|
(let [off 0
|
|
size 0
|
|
going true]
|
|
(while going
|
|
(set (at forward forward-n) (cp/codepoint-at off (addr size)))
|
|
(set forward-n (+ forward-n 1))
|
|
(let [next (cp/step-forward off)]
|
|
(if (= next off) (set going false) (set off next))))))
|
|
|
|
;; And back again, from wherever forward stopped. Written as a separate walk
|
|
;; rather than as an index into the first one on purpose: the point is that
|
|
;; step-back finds the lead byte of the previous codepoint out of the bytes
|
|
;; alone, so it has to be asked, not remembered.
|
|
(defn walk-backward [start i32] ()
|
|
(set backward-n 0)
|
|
(let [off start
|
|
size 0
|
|
going true]
|
|
(while going
|
|
(set (at backward backward-n) (cp/codepoint-at off (addr size)))
|
|
(set backward-n (+ backward-n 1))
|
|
(if (= off 0)
|
|
(set going false)
|
|
(set off (cp/step-back off))))))
|
|
|
|
(defn main [] ()
|
|
(let [total 0
|
|
raw (rl/load-codepoints cp/text (addr total))]
|
|
(show "codepoints" total)
|
|
(cp/collect-unique (slice-from-ptr raw total))
|
|
(show "unique" cp/unique-count)
|
|
(dotimes [i 5]
|
|
(print "unique ") (print i) (print " ")
|
|
(println (at cp/unique-codepoints i)))
|
|
|
|
(walk-forward)
|
|
(show "forward" forward-n)
|
|
|
|
;; The last codepoint's offset, recomputed the same way walk-forward found
|
|
;; it, because the walk deliberately keeps no offsets.
|
|
(let [last-off 0
|
|
going true]
|
|
(while going
|
|
(let [next (cp/step-forward last-off)]
|
|
(if (= next last-off) (set going false) (set last-off next))))
|
|
(walk-backward last-off))
|
|
(show "backward" backward-n)
|
|
|
|
;; Item 2: the two walks are reverses of each other.
|
|
(let [mirrored (= forward-n backward-n)]
|
|
(dotimes [i forward-n]
|
|
(when (and mirrored
|
|
(not (= (at forward i) (at backward (- (- backward-n 1) i)))))
|
|
(set mirrored false)))
|
|
(print "walks mirror ") (println (yes-no mirrored)))
|
|
|
|
;; The one call walk-backward never makes: step-back at the very start.
|
|
;; It is where the example goes the moment anybody presses LEFT before
|
|
;; pressing RIGHT, and it is the offset at which there is nothing behind
|
|
;; the cursor to read. GetCodepointPrevious reads *backwards* from the
|
|
;; pointer it is handed, so asking it here would read whatever is in front
|
|
;; of the text; the guard in step-back is what means it is not asked, and
|
|
;; a guard nothing exercises is a guard nobody knows about.
|
|
(show "back at start" (cp/step-back 0))
|
|
|
|
;; Item 3: and the forward walk is what raylib said the text contains.
|
|
(let [agrees (= forward-n total)
|
|
all (slice-from-ptr raw total)]
|
|
(dotimes [i forward-n]
|
|
(when (and agrees (not (= (at forward i) (at all i))))
|
|
(set agrees false)))
|
|
(print "walk matches raylib ") (println (yes-no agrees)))
|
|
|
|
(rl/unload-codepoints raw)))
|