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)
|
|
|
|
(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)))
|