The fixed stack needs a case, and .5 needs a decision

err-too-deep was the one error code nothing observed. The message is the least
of it: the plausible wrong version is `>` where the guard wants `>=`, which
writes one element past a [32 i32] and traps at exit 134 rather than answering
anything. 33 opening brackets is the input that separates them, and it is the
whole justification for a fixed array instead of a growable stack — the place
this lane pushes hardest against having no allocator.

`.5` reads as a float here and does not in EDN, where a number must start with
a digit and `.` is a legal symbol-start byte. That makes it a reinterpretation
of a token that is already legal as something else, which is exactly what the
house rule says to name rather than leave to be discovered, so it is written
beside the refusals.

Also: every symbol in the table was lowercase, so the A-Z half of alpha? was
unexercised and a version missing it passed. Enemy/Goblin in an existing dump
rather than a new case. And a line under "Internal helpers" saying the heading
is intent and not enforcement — a package has no visibility, so edn/scan-atom
is as callable as edn/next, the same way rl/get-color-raw is.

Both new cases verified by mutation: the depth guard traps, and alpha? without
its uppercase range fails Enemy/Goblin.
This commit is contained in:
Joseph Ferano 2026-09-11 20:04:29 +07:00
parent 7e7f77f2da
commit ad092d7d02
3 changed files with 25 additions and 2 deletions

View File

@ -145,7 +145,9 @@
(dump "-1 +2 0") ; the signs are part of the number
(dump "1.5 -2.5e3 .5") ; f, and a leading dot is a float
(dump "true false nil") ; b b n — and not three symbols
(dump "foo foo/bar -") ; `-` alone is a symbol; `foo/bar` is NOT a ratio
;; `-` alone is a symbol, `foo/bar` is NOT a ratio, and the uppercase half
;; of the alphabet test is only exercised by a name that has one in it.
(dump "foo Enemy/Goblin -")
(dump ":a :foo/bar") ; k, text without the colon
(newline)
@ -215,6 +217,11 @@
(refusal "[1 2}") ; the wrong closer
(refusal "]") ; a closer with nothing open
(refusal "[1 2") ; end of input with something still open
;; 33 opening brackets against a 32-deep stack. The error message is the
;; least of what this checks: a `>` where the guard needs `>=` writes one
;; past the end of a fixed array, and the answer is a bounds trap rather
;; than a wrong message. The offset is the 33rd bracket.
(refusal "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[")
(newline)
;; ── The struct reader ─────────────────────────────────────────────

View File

@ -404,7 +404,7 @@ let () =
i<-1>i<+2>i<0>
f<1.5>f<-2.5e3>f<.5>
b<true>b<false>n<nil>
y<foo>y<foo/bar>y<->
y<foo>y<Enemy/Goblin>y<->
k<a>k<foo/bar>
[<>i<1>]<>
@ -452,6 +452,7 @@ s<a b>
4 unbalanced: this closing delimiter does not match the one that is open
0 unbalanced: this closing delimiter does not match the one that is open
4 unbalanced: this closing delimiter does not match the one that is open
32 nesting is too deep: the balance stack is a fixed array and it is full
[goblin] hp=12 speed=1.5 boss=no
[dragon] hp=40 speed=0 boss=yes

15
vendor/edn/edn.flan vendored
View File

@ -53,6 +53,17 @@
;;;; a byte once anything is non-ASCII, and there is no
;;;; code point type.
;;;;
;;;; ── One place this is not EDN, on the record ────────────────────────
;;;;
;;;; `.5` is a float here. In EDN a number must begin with a digit and `.` is
;;;; a legal symbol-start byte, so strictly `.5` is the *symbol* `.5` — which
;;;; makes this a reinterpretation of a legal token and not an extension, and
;;;; therefore the kind of thing that gets written down rather than discovered.
;;;; It is this way because number-start? runs before the symbol case and
;;;; parse-f64 accepts a leading dot; a caller who needs the symbol reading
;;;; should not be writing `.5` at all. `-`, by contrast, is a symbol, because
;;;; number-start? requires a digit after the sign.
;;;;
;;;; ── Errors ──────────────────────────────────────────────────────────
;;;;
;;;; On the cursor, not in the return type. `next` answers a Token whose kind
@ -221,6 +232,10 @@
(= b \/)))
;; ── Internal helpers ────────────────────────────────────────────────
;;
;; "Internal" by intent and not by enforcement: a package has no visibility
;; yet, so edn/scan-atom and edn/push-open are as callable as edn/next is.
;; Nothing below is part of the API and none of it will keep its shape.
(defn at-end? [c (Ptr Cursor)] bool
(>= (.pos c) (len (.src c))))