;;;; 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) (defvar forward [max-walk i32]) (defvar forward-n i32) (defvar backward [max-walk i32]) (defvar 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)))