#{{:a 1} {:a 1} {:a 2}} answers 2 whether tables=? compares anything or
compares nothing, so it was proving the count and not the compare. The
pair beside it isolates both halves: one map twice must collapse to 1,
and two maps of one entry each with different keys must stay 2, which is
what a size-only compare would get wrong.
And read's comment stops implying a property it does not have: empty
input answers (Some Value.Nil), indistinguishable from the document that
is nil. Empty is not malformed and the reader is not the thing that gets
to decide it is.
133 lines
5.7 KiB
Plaintext
133 lines
5.7 KiB
Plaintext
;;;; (edn/read bytes) over the file it was built for, plus the two 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 hand-written struct reader in programs/edn.flan is the other route and
|
|
;;;; needs a defstruct per file; this one needs nothing and reads a file whose
|
|
;;;; keys it has never heard of.
|
|
;;;;
|
|
;;;; The two properties:
|
|
;;;;
|
|
;;;; * a set holds each value once, by *structure*. #{[0 0] [0 0]} is one
|
|
;;;; element, and a dedup written with `=` would make it two — two Vec
|
|
;;;; headers over two blocks are never the same header.
|
|
;;;;
|
|
;;;; * a Value owns its strings. The last case reads a document out of a
|
|
;;;; buffer and then overwrites every byte of that buffer in place. A
|
|
;;;; reader holding views prints the overwriting bytes; one holding copies
|
|
;;;; prints what was in the file. The buffer is written rather than freed
|
|
;;;; because a freed buffer is a read of released memory, which can pass by
|
|
;;;; luck; overwriting it cannot.
|
|
|
|
(import edn "vendor:edn")
|
|
|
|
(defvar frame Allocator)
|
|
|
|
;; 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"))
|
|
|
|
;; [a b] as a Value, so a membership test can be written against a pair this
|
|
;; program made rather than one it found. Building a Value from outside the
|
|
;; package is the same two forms as building one inside it.
|
|
(defn pair [a i64 b i64] edn/Value
|
|
(let [items (vec-new edn/Value)]
|
|
(push items (edn/Value.Int {.n a}))
|
|
(push items (edn/Value.Int {.n b}))
|
|
(edn/Value.List {.items items})))
|
|
|
|
(defn field [v edn/Value k string] edn/Value
|
|
(match v
|
|
(Table entries) (match (get entries k) (Some x) x None edn/Value.Nil)
|
|
_ edn/Value.Nil))
|
|
|
|
(defn show-tileset [] ()
|
|
(match (edn/read tileset)
|
|
(Some doc)
|
|
(do
|
|
(match (field doc "texture-path")
|
|
(Text s) (println s)
|
|
_ (println "no texture path"))
|
|
(match (field doc "selected-cells")
|
|
(Set cells)
|
|
(do
|
|
(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 both of these.
|
|
(println (edn/member? cells (pair (i64 4) (i64 3))))
|
|
(println (edn/member? cells (pair (i64 0) (i64 0))))
|
|
(println (edn/member? cells (pair (i64 3) (i64 4))))
|
|
(println (edn/member? cells (pair (i64 9) (i64 9)))))
|
|
_ (println "no cells")))
|
|
None (println "malformed")))
|
|
|
|
;; 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] ()
|
|
(match (edn/read (bytes src))
|
|
(Some v) (match v (Set items) (println (len items)) _ (println "not a set"))
|
|
None (println "malformed")))
|
|
|
|
;; A document in a buffer this program owns and can write to. (bytes "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 "{:name \"level-1\" :xs [1 2]}"))
|
|
(let [src (as-slice buf)
|
|
v (edn/read src)]
|
|
(dotimes [i (len src)]
|
|
(set (at src i) \x))
|
|
(match v
|
|
(Some doc)
|
|
(match (field doc "name")
|
|
(Text s) (println s)
|
|
_ (println "no name"))
|
|
None (println "malformed")))))
|
|
|
|
(defn main [] i32
|
|
(set frame (arena-new 262144))
|
|
(with-allocator frame
|
|
(do
|
|
(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.
|
|
(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 tables=? 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}}")
|
|
(set-size "#{1 1.0}") ; an int and a float are two values
|
|
(set-size "#{true false true}")
|
|
(println "")
|
|
|
|
(survives-its-buffer)
|
|
;; And the refusal, which has to be distinguishable from the document
|
|
;; that is literally nil.
|
|
(match (edn/read (bytes "#{1 2")) (Some _v) (println "read") None (println "malformed"))
|
|
(match (edn/read (bytes "nil")) (Some _v) (println "read") None (println "malformed"))))
|
|
(free-all frame)
|
|
(arena-destroy frame)
|
|
0)
|