3 Commits

Author SHA1 Message Date
96ab4c9cf0 Retire the per-type printers, since print says all of it
print-str, print-i64, print-f64, print-bytes, print-line and newline leave
the prelude. print and println are the whole printing surface now, and print
is the better call at every one of the sites that used them: it is the same
structural walk without the newline, so the no-newline case the family was
kept for is covered, and it takes the value as it is. The old print-i64
forced an explicit (i64 x) at every call site, because this language widens
nothing implicitly; that cast is gone from 127 places.

Dropping it moves one answer. hash-grid returns u64, and the cast through
the signed printer showed sand-headless's hash as -2851001042534928384.
print routes a u64 through flan_u64_to_bytes, so it now prints
15595743031174623232 — the same 64 bits, read as the unsigned number they
are. The pinned expectation follows the correction.

test-flan-dev.el and test_session.ml both reached for print-line as "a name
the prelude has"; they reach for rand-seed instead.
2026-09-12 05:32:25 +07:00
ad092d7d02 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.
2026-09-11 20:04:29 +07:00
7e7f77f2da The struct reader is what proves the cursor is usable
read-enemy in test/programs/edn.flan is the worked example the API is for: the
map opened, the keys looped over, each known one dispatched onto its field and
the rest skipped, written by hand because the compiler cannot emit it yet. It
is there rather than in a doc comment because an API only a compiler could
call would be present without being usable, and writing one out is the only
way to find out which it is. Two things came back from writing it — that
float-of has to accept an integer token, since a config file writing `:speed 2`
for an f32 field is not making a mistake, and that a caller needs `fail` on the
cursor, because a reader's own "expected an integer here" has nowhere else to
get a position from.

The expected output is a raw literal. The dump is brackets and quotes end to
end, and escaping it into an ordinary OCaml string would put a second reader
between the test and what the program printed.

Every case was checked by breaking the tokenizer and watching it go red;
sixteen of them, each restored afterwards. The ones worth naming, because they
are the ones that could have been quietly unobservable: dropping the escape
refusal, accepting `#{`, and collapsing every refusal onto one message — that
last is the shape where a table asserting only "it failed" stays green while
observing nothing. Also: a semicolon no longer ending an atom, a comment scan
that does not test for end of input (which traps rather than differing, on the
comment with no trailing newline), the ratio rule widened to any atom
containing a slash (which takes foo/bar with it), text slices left including
the quote and the colon, a closer counted but not matched, any byte accepted as
a symbol start, a comma not counted as whitespace, and skip-value consuming one
token instead of a whole collection.
2026-09-11 20:01:21 +07:00