The author's ruling: "I think the prefix reads better, keep it" — so two
prefixed enums out of eleven was the inconsistency, not the prefix.
TraceLogLevel takes log-, CameraProjection projection-, CameraMode camera-,
GamepadButton button-, GamepadAxis axis-, Gesture gesture-, MouseCursor
cursor-, TextureFilter filter-, PixelFormat pixel-.
Two of those are judgement. CameraProjection and CameraMode share raylib's
CAMERA_ and deliberately do not share a Flan prefix: they are two questions
asked of the same struct, and :projection-perspective beside :camera-orbital
says which is being answered. GamepadButton and GamepadAxis take the short
stems rather than a shared gamepad-, which keeps :button-left-face-up and
:axis-left-trigger readable.
It is a reading choice and not a collision fix, and bindings, raylib.flan and
docs/BUILT.md all say so: a keyword resolves against the expected type and
nothing else, so :point at a TextureFilter site was never ambiguous. What the
prefix buys is the call site read on its own.
The three constant exception lines are keyed on the member's full Flan
spelling and moved with it. flan generate-c vendor/raylib is green against
raylib-5.5.h, and the check was confirmed non-vacuous by breaking it:
filter-trilinearr reported TEXTURE_FILTER_TRILINEARR rather than passing.
test_flan pins one member of each of the eleven to the C name the rule
reaches, read out of the real bindings file.
sand.flan line 121 is (rl/set-trace-log-level :warning) and is the author's
to respell. TraceLogLevel carries a warning alias beside log-warning, mapped
by name in bindings, so the suite stays green until he does; FIX.org has the
three-edit removal recipe.
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.
vendor/raylib/modes.flan: five macros over the five pairs the package binds
-- with-drawing, with-mode-2d, with-mode-3d, with-texture-mode,
with-scissor-mode. A second file with no declare-c in it, split out on
vector.flan's reasoning: raylib.flan is the package's statement about C and
nothing here names C, so nothing here can be made wrong by raylib changing.
Each expands to (do (begin-... args) body... (end-...)) -- the calls the
author used to type, in the order they typed them. No let, no gensym: nothing
binds a name, so there is nothing for a caller's name to collide with.
What it removes is the End* that is missing, wrong, or no longer beside its
Begin*. What it cannot remove is a body leaving through the unwind path: a
return or an invoke-restart skips the rest of the do and the End* with it.
defer is the obvious fix and is refused inside a loop body, which is where a
pair always lives -- checked, not assumed. So sand.flan's discipline stays:
keep the restart boundary outside the pair.
35 call sites converted across examples/ and sand.flan. The one left is
core-scissor-test.flan, whose Begin and End sit in two separate `when`s with
the drawing between them -- a conditional pair is a shape a bracketing macro
cannot express.
test/programs/rl-with.flan covers with-scissor-mode, which no example can,
with a frame function unreachable from main so it needs no libraylib;
rl-with-reject.flan is the arity half.
Two gaps the raylib examples hit.
The layout check compared a Flan enum against the header's `int` and
called it a disagreement. It is not one: Shim.cty lowers a defenum to
int32_t in a struct field exactly as it does in a parameter, which is
what the signature check already knew and the layout check did not. One
predicate now serves both, symmetric, and tolerant of a 32-bit integer
and nothing else -- f64 against the library's float still fails, in the
very struct whose other field is an enum. Camera3D.projection is a
CameraProjection again and rl/camera-projection is gone with it, so
`.projection :perspective` resolves at the construction site.
And generate-c's claim said nothing about a defconst or a defenum
member, so a wrong flag bit was completely silent. `bindings` gained
`enum`, `const` and `constant` lines saying what a Flan constant is
called in C -- the prefix is nowhere in the Flan name, so it is declared
rather than guessed. Nothing goes quiet in either direction: a name the
rule builds and the header lacks is reported, a rule that reaches
nothing is reported, and a defenum with no line is itself a finding,
because otherwise the silence just moves up one level.
clang's dump gives anonymous EnumDecls for every raylib enum and no
value at all for an enumerator written without `= n`, so the constants
are one flat table and the values are counted the way C counts them.
cache_format bumped with the dump type.
core-2d-camera, core-scissor-test, core-window-flags, core-world-screen
and core-window-should-close, ported from raylib 5.5's examples/core.
What each one asked of vendor/raylib:
2d-camera nothing. A whole Camera2D by value, per frame, into the
call that actually draws with it — the layout the
acceptance table pins through arithmetic, now going
through the path it was bound for.
scissor-test begin-scissor-mode / end-scissor-mode, hand-written.
window-flags twelve more ConfigFlags constants; raylib.flan carried
the four sand.flan sets and this reads eleven.
world-screen the 3D surface did not exist: no Vector3, no Camera3D,
and the importer refused every 3D function in raylib.h
by name for want of them. Two defstructs, two defenums,
begin/end-mode-3d, update-camera, get-world-to-screen,
draw-cube, draw-cube-wires, draw-grid — and 24 more 3D
lines the importer can generate now that the types are
described.
window-should-close set-exit-key, and Key/null to pass it.
The hand-written/generated line, written down in vendor/raylib/bindings:
a drawing pair inside a frame is hand-written, and so is anything whose
Flan face is not the C signature — set-exit-key takes a Key, update-camera
takes a (Ptr Camera3D) and a CameraMode. The window-state family and
get-mouse-x/y stay generated: plain scalars in and bool out, with nothing
for a hand-written line to add.
Camera3D's projection field stays i32, because the header says int and the
layout check holds this file to that; rl/camera-projection is the
conversion, and still takes a keyword.