nparse is planned before it is written, and the plan is in the repository.
That is unusual and it is deliberate: the specifications catch design mistakes
that would otherwise be found by writing the wrong code twice — which, for
parsing in this ecosystem, has already happened five times.
Read, in this order:
meta/specs/SAFETY.md— the constraintsmeta/specs/README.md— the index and the reading ordermeta/DECISIONS.md— why things are the way they aremeta/roadmap/ROADMAP.md, then the current cycle'sREADME.md
Every change belongs to a subcycle. The current cycle's README.md has the
checklist; a change that is not on it either goes on it or is a finding to be
recorded first.
A specification change is a decision. If your change requires the library to
behave differently from what meta/specs/ says, the specification is amended
and a numbered decision recorded in meta/DECISIONS.md, in the same commit.
A settled decision's text is never rewritten — supersede it with a new one.
Every change is green under the full harness. --only is for iterating and
never for concluding; nothing is committed on the strength of a filtered run.
-
A syntax error is not an error. It is a
Faultvalue in the success channel, andpickexhaustiveness is what forces the caller to handle it. The library declares twoerror:identities and neither can be produced by any input. If you find yourself adding a third, and the trigger is something a byte stream can cause, you have found a design mistake rather than a missing error. -
Arithmetic on input must not trap. Plain integer overflow traps in Nitpick, so
acc = acc * 10 + dis a remote crash on a long enough number. Range-check before the multiply, every time, and return aFault. There is a gate for this and it is not negotiable. -
Nothing in a cell, node or event may own anything. An owning field makes the type move-only, and an arena
getreturns a copy — so a node holding astringcannot be read back at all. Text is an offset and a length into a pool. -
The parser is pull, not push. The caller asks for the next event. This is not a taste question: Nitpick has no closures, so a push parser's visitor would be a trait object plus hand-carried context at every call, and streaming would not compose.
- Expectations live in the test file, as markers, and assert on codes and exit codes — never on message text.
- A negative test with no expectation is a failing test.
- Unexpected diagnostics fail a test as surely as missing ones.
- A conformance corpus is a gate, not a suggestion. JSONTestSuite, toml-test and the yaml-test-suite subset are vendored, committed with their upstream commit recorded, and every case is judged. Hand-written cases test the rules the author understood.
- The round trip is the oracle.
print ∘ parsemust reach a fixed point after one normalisation, for every format, on every corpus case that parses. - Anything with a timing dimension runs forty times, not once.
- A red under stress is a stop sign, never a retry.
You will find them; the library is written against a compiler that is itself under construction. Record the reproduction, stop, and raise it in the compiler repository. Do not work around it in library code: a workaround buried here outlives the bug, is never removed, and is indefensible at verification time.
Match the surrounding code. Public names carry their module's short prefix;
types are PascalCase; constants are SCREAMING_SNAKE. meta/specs/BUILD.md §7
lists the reserved words that read like ordinary names and the substitutes this
library uses instead — use those, so the tree is consistent.