sparkles:parsing — Delivery Plan
Audience: contributors implementing the toolkit. Execution-only — milestones, dependencies, verification, deferrals. For the design rationale and prior-art justification read the proposal; for the evidence base read the parsing survey.
NOTE
Status: proposal. This is a milestoned plan for a library that does not exist yet. It is deliberately incremental: each milestone is independently useful, builds bottom-up on the existing sparkles.base.text substrate, and can stop early — Sparkles gets value at M2 (combinators + Pratt) without ever needing M4. Nothing here should be built ahead of a real in-repo client that needs it.
1. Milestone overview
| # | Deliverable | Prior art | Depends on |
|---|---|---|---|
| M0 | ParseResult!T + the leaf-parser convention over base.text.readers (formalise the existing idiom) | nom · in-tree | — |
| M1 | Ordered-choice combinators (seq/alt/many/opt/sepBy/map + predicates), attribute-inferring | PEG · nom/winnow | M0 |
| M2 | Pratt expression engine (binding-power loop); first client: version-constraint grammar | pratt | M0, M1 |
| M3 | Error posture: fail-fast default + opt-in cut + opt-in chumsky-style recovery (partial + error list) | flatparse · chumsky | M0–M2 |
| M4 | (deferred) Optional lossless/CST view — only if a re-serializable-tree client appears | red-green | M1–M3 |
M0 formalises what parseSemVerShaped already does implicitly. M1 is the bulk. M2 is where Sparkles first gains something it lacks today (a reusable constraint parser). M3 is a per-client policy, not new core. M4 is deferred and may never be built.
2. Per-milestone detail
Each milestone's outcome is: the relevant modules compiling @safe pure nothrow @nogc where the leaf parsers allow, unit-tested (silly, feature modules — not package.d), a runnable [Output] README example, and DDoc per AGENTS.md. All new code lives in a new libs/parsing/ sub-package (mirroring the eight existing ones) or, if it stays small, under libs/base/src/sparkles/base/text/ beside the readers it extends — decide at M0.
M0 — core result + leaf convention
- Define
ParseResult!TasExpected!(T, ParseError, NoGcHook)(alias the existingParseExpected!Tfromerrors.d) and the parser shapeParseResult!T delegate(ref scope const(char)[])/ aParserconcept (isParser!P). - Wrap the
readers.dprimitives (readInteger,skipSpaces,tryConsume,readUntil) as canonical leaf parsers; confirm advance-on-success semantics are uniform. - No new parsing power — this is the vocabulary M1 composes.
M1 — ordered-choice combinators
seq(p...),alt(p...)/choice(first-match PEG ordered choice),many/many1,opt,sepBy/sepBy1,map/mapErr,peek,notFollowedBy. Let attributes infer (do not force@safe/@nogcon the templates — see AGENTS § safety attributes).- Accumulate into
SmallBuffer, neverappender; no memoization. - Verify a
()-returning validator allocates nothing (a@nogcunittest is the proof).
M2 — Pratt engine + first real client
- A table-free binding-power loop (
prattParse!(prefix, infix)) per pratt-precedence. - First client: re-express a version-constraint grammar (node-semver-style
>=1.2 <2.0 || ^3) with M1 combinators + M2 Pratt, and validate it against the existingsparkles.versionsrange parsers (same accept/reject set). This is the milestone that proves the toolkit earns its place.
M3 — error posture
- Fail-fast is the default (an
errshort-circuits). Addcut(p)(commit — a failure aftercutis unrecoverable, the flatparse/nom error vs failure split). Add an opt-in recovering combinator that returns(partial value, ParseError[])for user-facing grammars, modelled on chumsky'sParseResult. Recovery is opt-in per parser, never global.
M4 — deferred: lossless view
- Only if a client needs a re-serializable CST (e.g. a config formatter): a lossless node type that records byte spans + trivia, borrowing the red-green position-free-node idea. Do not build speculatively.
3. Verification
- Per milestone:
dub test :parsing(or:base) green; a@nogcunittest proving the zero-allocation property (M0/M1); the M2 client matchingsparkles.versions' accept/reject set on a shared corpus; a runnable README[Output]example verified bynix run .#ci -- --verify. - Idiom conformance:
@safe pure nothrow @nogcon leaf/non-template code; attributes inferred on combinator templates;Expected(no exceptions in the@nogcpath); tests in feature modules, notpackage.d(the silly gotcha). - Docs: the toolkit gets a
docs/libs/parsing/Diátaxis tree when it lands; thisdocs/specs/parsing/pair (proposal + plan) becomes its design-history reference.
4. Deferrals / non-goals
Per the proposal §4: no SIMD by default (use mir-ion/asdf if a megabyte-JSON need appears), no incremental/query machinery (Sparkles parses once), no CTFE grammar DSL à la Pegged (hand-written RD is the survey's evidence-backed default). This plan builds the small @nogc combinator + Pratt layer that D lacks (the gap) — nothing more.