flan/test/programs/edn-provide.flan
Joseph Ferano 2e203f64b8 bytes copies, bytes-view aliases, and a dev-session segfault parks
The INSERTIONSORT crash, all three rulings (FIX.org 2026-09-20):

- (bytes s) allocates a writable copy through the allocator surface —
  context or (bytes s a), StorageExhausted with retry, a registry note in
  dev builds (flan_bytes_dup, lowered like vec-new). (bytes-view s) is the
  old zero-cost reinterpret, renamed, read-only by convention; every
  in-repo reader swept over to it. (string b) unchanged.
- String constants were already read-only on both backends at -O0; now
  pinned — bytes-copy.flan rows on LLVM/-O0/--x86, and dies_segv rows
  asserting the write-through-view trap on both backends.
- A dev build installs a SIGSEGV/SIGBUS handler by the same dev-only
  constructor slot that arms the registry: one line naming the address and
  the innermost frame, then the trap-hook park — stopped, not dead, the
  daemon serving. No agent: message and re-raise. Release builds untouched.
  Pinned by trap_park over dev-segv.flan.
2026-09-20 23:12:42 +07:00

121 lines
5.4 KiB
Plaintext

;;;; defedn over two real files, and the condition a drifted one signals.
;;;;
;;;; The first half is the claim the feature is for: `(.texture-path t)` here
;;;; is a field load off a struct nobody declared, and the same numbers
;;;; edn-read.flan prints through the dynamic reader come out of it. Two
;;;; readers over one file agreeing is what says the derived one is right —
;;;; either alone could be self-consistently wrong.
;;;;
;;;; The second half is the shape matrix: a string, an integer, a float, a
;;;; boolean, a vector, and a map inside a map, each read back.
;;;;
;;;; The third is drift. The struct was derived from the file as it was when
;;;; this was compiled; the bytes read at run time are a later version of it,
;;;; with one key gone and one arrived. Both are named by SchemaDrift, which is
;;;; the whole reason the reader carries one: a missing key otherwise leaves a
;;;; field at zero and the program draws nothing for a reason nothing reports.
(import edn "vendor:edn")
;; Both derived at compile time, from the files as they sit beside this one.
;; The path is the (embed "...") path — relative to this file — and not the
;; path the run-time reads below use, which is relative to the process.
(edn/defedn Tileset "assets/edn/tileset.edn")
(edn/defedn Tuning "assets/edn/tuning.edn")
;; The same file the dynamic reader in edn-read.flan walks. Embedded rather
;; than read, so this half asserts the reader and not the filesystem.
(defconst tileset (embed "assets/edn/tileset.edn"))
(defconst tuning (embed "assets/edn/tuning.edn"))
(defn show-tileset [a Allocator] ()
(let [t (Tileset-of-bytes tileset a)]
;; The line edn-read.flan prints first, off a struct field this time.
(println (.texture-path t))
;; 54 pairs, and the same three memberships and one miss. A derivation
;; that flattened the pairs into 108 integers would have a different count
;; and would answer no to every one of these.
(println (len (.selected-cells t)))
(println (has-key? (.selected-cells t) [3 4]))
(println (has-key? (.selected-cells t) [0 0]))
(println (has-key? (.selected-cells t) [4 11]))
(println (has-key? (.selected-cells t) [9 9]))))
(defn show-tuning [a Allocator] ()
(let [t (Tuning-of-bytes tuning a)]
(println (.name t))
(println (.hp t))
(println (.speed t))
(println (if (.boss? t) "yes" "no"))
(println (len (.drops t)))
;; 3 + 1 + 4 + 1 + 5. A vector read that stopped at the first element would
;; still have a plausible length from a zeroed Vec, so the sum is the claim.
(let [total (i64 0)]
(dotimes [i (len (.drops t))]
(set total (+ total (at (.drops t) i))))
(println total))
;; The nested structs, by the names the paths give them: Tuning-hitbox and
;; Tuning-hitbox-offset. Both are ordinary field loads, two deep.
(println (.w (.hitbox t)))
(println (.h (.hitbox t)))
(println (.x (.offset (.hitbox t))))
(println (.y (.offset (.hitbox t))))))
;; ── Drift ───────────────────────────────────────────────────────────
;;
;; The struct says :name :hp :speed :boss? :drops :hitbox. These bytes have no
;; :speed and have a :level the struct has never heard of, which is what a
;; tuning file looks like a month after the program was built.
(defconst drifted string
"{:name \"imp\" :hp 3 :level 7 :boss? true :drops [1] :hitbox {:w 1 :h 1 :offset {:x 0 :y 0}}}")
(defn show-drift [a Allocator] ()
(handler-bind
[(edn/SchemaDrift [d]
(do (print (if (.extra? d) "extra " "missing "))
(print (.field d))
(print " in ")
(println (.struct d))))]
(let [t (Tuning-of-bytes (bytes-view drifted) a)]
;; The fields that were there are read, which is the other half of the
;; contract: a drifted file is reported, not refused. :speed is the one
;; that was missing and is zero.
(println (.name t))
(println (.hp t))
(println (.speed t)))))
;; ── A file that does not parse ──────────────────────────────────────
;;
;; The louder failure, and the one that had the quieter answer until ReadFailed
;; existed: a generated reader accumulates errors on the cursor, and the cursor
;; is made and dropped inside the entry point, so a stray brace gave back a
;; zeroed struct with nothing said. The rest of this package refuses to do
;; that — read-file answers an Option so that a malformed document is
;; distinguishable from one that is literally nil — and a derived reader has to
;; be at least as honest.
(defconst broken string "{:name \"orc\" :hp }")
(defn show-broken [a Allocator] ()
(handler-bind
[(edn/ReadFailed [e]
(do (print "unreadable ")
(print (.struct e))
(print ": ")
(println (edn/error-message (.code e)))))]
(let [t (Tuning-of-bytes (bytes-view broken) a)]
;; Read anyway, and zeroed, which is the half a handler that carries on
;; is choosing. Printed so that "it signalled" and "it gave back nothing
;; usable" are two claims rather than one.
(println (.hp t)))))
(defn main [] i32
(let [a (heap-allocator)]
(show-tileset a)
(println "")
(show-tuning a)
(println "")
(show-drift a)
(println "")
(show-broken a))
0)