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.
151 lines
6.8 KiB
Plaintext
151 lines
6.8 KiB
Plaintext
;;;; (edn/read bytes) over the file it was built for, plus the properties that
|
|
;;;; are only claims until something runs them.
|
|
;;;;
|
|
;;;; assets/edn/tileset.edn is the real thing, copied out of the editor that
|
|
;;;; writes it: a map of :texture-path to a string and :selected-cells to a set
|
|
;;;; of 54 integer pairs. Nothing here declares a type for it — the point.
|
|
;;;;
|
|
;;;; It is in a subdirectory of assets/ rather than in it, because programs/
|
|
;;;; embed.flan holds (embed-dir "assets") in a [3 EmbedFile] and a fourth file
|
|
;;;; beside the other three is a type error in a program that has nothing to do
|
|
;;;; with this one. embed-dir does not descend, which is what makes a
|
|
;;;; subdirectory the answer rather than a second assets directory.
|
|
;;;;
|
|
;;;; The document is plain dyn now — maps, vecs, keywords, texts — where it
|
|
;;;; used to be the edn/Value tagged union. What the union's hand-written
|
|
;;;; equality bought, the runtime's structural equality answers:
|
|
;;;;
|
|
;;;; * a set holds each value once, by *structure*. #{[0 0] [0 0]} is one
|
|
;;;; element. A set reads as a dyn map from element to true, and the dedup
|
|
;;;; is put's own key-replace, so a set with a duplicate never exists.
|
|
;;;;
|
|
;;;; * the document owns its strings. The buffer a document was read from is
|
|
;;;; overwritten byte by byte afterwards, and the string read out of the
|
|
;;;; document still prints what was in the file — the box copied.
|
|
;;;;
|
|
;;;; * (edn/read-file path) frees its buffer inside the call, safe because
|
|
;;;; of the property above, and passes a FileError through untouched with
|
|
;;;; both restarts still armed.
|
|
;;;;
|
|
;;;; What dyn cannot say and the old (Option Value) could: `read` answers nil
|
|
;;;; both for malformed input and for the document `nil`. The distinction did
|
|
;;;; not vanish — it moved to the cursor, where the error position has always
|
|
;;;; lived, and `malformed?` below is the three lines it costs.
|
|
|
|
(import edn "vendor:edn")
|
|
|
|
;; The document, read at compile time. An embed is bytes in the binary, so
|
|
;; there is no file open here and no path to get wrong at run time.
|
|
(defconst tileset (embed "assets/edn/tileset.edn"))
|
|
|
|
(defn show-tileset [] ()
|
|
(let [doc (edn/read tileset)
|
|
cells (get doc :selected-cells)]
|
|
(println (get doc :texture-path))
|
|
(println (len cells))
|
|
;; Two cells that are in the file and one that is not. A reader that
|
|
;; flattened the pairs into 108 integers would still have the right count
|
|
;; of *something*, and would miss all of these; [3 4] answering yes where
|
|
;; [9 9] answers no is what says the pair compare is positional.
|
|
(println (has-key? cells [4 3]))
|
|
(println (has-key? cells [0 0]))
|
|
(println (has-key? cells [3 4]))
|
|
(println (has-key? cells [9 9]))))
|
|
|
|
;; The size of a set after the dedup, which is the whole of what the dedup can
|
|
;; be asked for.
|
|
(defn set-size [src string] ()
|
|
(println (len (edn/read (bytes-view src)))))
|
|
|
|
;; Malformed input, told apart from the document `nil` by the cursor — the
|
|
;; return value alone cannot say it, and this is the spelling that can.
|
|
(defn malformed? [src string] bool
|
|
(let [c (edn/cursor (bytes-view src))
|
|
t (edn/next (addr c))
|
|
v (edn/read-value (addr c) t)] ; the value is not the question here
|
|
(not (edn/ok? (addr c)))))
|
|
|
|
;; A document in a buffer this program owns and can write to. (bytes-view "literal")
|
|
;; is not that — a literal is constant data behind a writable-looking slice —
|
|
;; so the source is built with append and the write goes through as-slice.
|
|
(defn survives-its-buffer [] ()
|
|
(let [buf (vec-new u8)]
|
|
(append (addr buf) (bytes-view "{:name \"level-1\" :xs [1 2]}"))
|
|
(let [src (as-slice buf)
|
|
v (edn/read src)]
|
|
(dotimes [i (len src)]
|
|
(set (at src i) \x))
|
|
(println (get v :name)))))
|
|
|
|
;; The path-taking entry point over the same file the embed above holds. The
|
|
;; texture path printed here has to be the one show-tileset printed, which is
|
|
;; what says read-file read the file and not merely something — and that
|
|
;; freeing the buffer inside the call took none of the document with it. The
|
|
;; defonce is a dyn global, rooted once at startup, so the document lives past
|
|
;; the frame that read it.
|
|
(defonce game-data dyn)
|
|
|
|
(defn by-path [] ()
|
|
(set game-data (edn/read-file "programs/assets/edn/tileset.edn"))
|
|
(println (get game-data :texture-path)))
|
|
|
|
;; A missing file, answered from outside read-file. Nothing in the package
|
|
;; handles FileError, so the condition walks past it to here with both restarts
|
|
;; armed, and `use-value` names a path read-file then slurps instead — the read
|
|
;; resumes as if that file had been asked for all along, which is exactly what
|
|
;; slurp.flan asserts for slurp alone.
|
|
;;
|
|
;; The map check is the assertion: the answer is a real document rather than
|
|
;; the nil a swallowed error would have to become, so the restart was taken.
|
|
(defonce saw-file-error i64)
|
|
|
|
(defn by-missing-path [] ()
|
|
(handler-bind
|
|
[(FileError [c]
|
|
(set saw-file-error (+ saw-file-error 1))
|
|
(invoke-restart 'use-value "programs/assets/edn/tileset.edn"))]
|
|
(println (has-key? (edn/read-file "programs/assets/edn/not-here.edn")
|
|
:texture-path)))
|
|
(println saw-file-error))
|
|
|
|
(defn main [] i32
|
|
(show-tileset)
|
|
(println "")
|
|
|
|
;; Dedup, on each kind of element a set can hold. The nested pair is the
|
|
;; one a structural compare is needed for; the nested *set* is the one
|
|
;; that also needs the compare to ignore order, or #{1 2} and #{2 1}
|
|
;; would be two elements — as maps-to-true they are one map, compared by
|
|
;; lookup and not by position.
|
|
(set-size "#{}")
|
|
(set-size "#{1 1 2}")
|
|
(set-size "#{[0 0] [0 0] [0 1]}")
|
|
(set-size "#{\"a\" \"a\" :a :a}")
|
|
(set-size "#{#{1 2} #{2 1}}")
|
|
;; Three map cases and not one, because #{{:a 1} {:a 1} {:a 2}} answers 2
|
|
;; whether the map compare works or does nothing at all — two merge and one
|
|
;; does not, or none merge and there were only ever three. The pair below
|
|
;; isolates it: the first must be 1, and the second must be 2 on the
|
|
;; *keys*, which a size compare alone would get wrong.
|
|
(set-size "#{{:a 1} {:a 1}}")
|
|
(set-size "#{{:a 1} {:b 1}}")
|
|
(set-size "#{{:a 1} {:a 1} {:a 2}}")
|
|
;; One, and the old Value answered two. Dyn equality is the language's own,
|
|
;; and it says (= 1 1.0) the way the operators promote — so under a
|
|
;; maps-to-true set, 1 and 1.0 are one key. A narrowing against EDN's
|
|
;; letter, taken with open eyes: the alternative was a second equality kept
|
|
;; beside the runtime's, which is the duplication this rewrite retired.
|
|
(set-size "#{1 1.0}")
|
|
(set-size "#{true false true}")
|
|
(println "")
|
|
|
|
(survives-its-buffer)
|
|
;; And the refusal, told apart from the document that is literally nil.
|
|
(println (malformed? "#{1 2"))
|
|
(println (malformed? "nil"))
|
|
(println "")
|
|
|
|
(by-path)
|
|
(by-missing-path)
|
|
0)
|