feat: parser slice 27 — test declarations + bundled test { } mock blocks #5
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/005-feat-parser-slice-27-test-declarations-bundled"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
feat: parser slice 27 — test declarations + bundled test { } mock blocks
Along the way:
mod X provides Effectas a module-header clause<-— effects resolve callee-drivenTouches 105 files:
docs/README.md,docs/ai-training.md,docs/effects.md,docs/impl/02-lexer.md,docs/impl/03-parser.md,docs/impl/README.mdand 99 more.Two things inline. The one in
src/rust/crates/ck_parser/src/tests/actor_decl.rsis the one I care about.@ -0,0 +155,4 @@}#[test]fn receive_missing_open_paren_emits_e0107() {receive_missing_open_paren_emits_e0107reads, decides and writes in one function. The deciding part is the piece I would want to test on its own. Worth splitting?Split. The decision in
receive_missing_open_paren_emits_e0107is a pure function now, with its own tests insrc/rust/crates/ck_parser/src/tests/actor_decl.rs.Good. The second pass on
src/rust/crates/ck_parser/src/tests/actor_decl.rsis what I hoped for.Pushed a fix for both. The second pass on
src/rust/crates/ck_parser/src/tests/actor_decl.rsis much smaller than the first.Lands the `test` keyword's two grammatical roles: - top-level `test fn name(...) { ... }` → TestDecl wrapping a reused FnDecl - `test { ... }` mock block in an effect body → TestBlock with `state { }` (StateBlock/StateField), operation bodies (reused FnDecl), and `seed fn` helpers (SeedFn). Replaces slice 26's brace-balanced E0090 recovery. Decisions (documented in docs/impl/03-parser.md): - `seed` stays a contextual identifier, not a keyword — it also names a record field and a member-access path, so a hard keyword would break those. A seed fn is recognized by the `Ident fn` shape; the resolver verifies the modifier is literally `seed`. - A state field is a binding with a mandatory initial value (`Ident : T = E`, E0100), by analogy with `const`. Empty `state {}` is well-formed. 5 new SyntaxKind nodes, 9 new diagnostics (E0092..E0100), E0090 message broadened to "expected `fn`, `test`, or `}` in effect body". No lexer change. Gates green: fmt, clippy -D warnings, 499 tests.mod X provides Effectas a module-header clause c59bac54efLands the provider-module form. The grammar was specified two incompatible ways across the locked docs; resolved (2026-05-24) in favor of modules.md: ModuleHeader ::= mod Ident ProvidesClause? ProvidesClause ::= provides TypePath GenericArgs? `mod X provides Effect` is a HEADER CLAUSE, not a braced top-level declaration: the file is the provider and its top-level fns are the operation implementations. modules.md forbids `mod { ... }` blocks inside files, so a braced `mod X provides Effect { ... }` declaration cannot exist (and would force a redundant second `mod`). The effects.md / syntax.md braced examples were illustrative grouping, not grammar, and are corrected to the header-line form here. Decisions (documented in docs/impl/03-parser.md): - The effect reference reuses impl's shape (`parse_type_path` head + optional `parse_generic_args`), so `provides File` and the marker `provides Alloc<Ctx>` both parse. Generic args are optional here, unlike impl's mandatory `<TypeArgs>`. - `mod` leaves the top-level dispatch and the `is_top_level_decl_start` sync set: a `mod` token only ever opens the file header, so a stray mid-file `mod` now emits the generic E0013 instead of placeholder E0012. - The clause production lives in its own file (`provides_clause.rs`) per the R3 per-file convention; `parse_module_header` calls into it. One new diagnostic (E0101); a malformed generic list reuses E0020. No lexer change — `provides` was already a keyword. Re-blessed tests/lex/program_effects.cktest: `mod RuntimePanic provides Panic` now parses as a clean ProvidesClause. Also fixed that fixture's stale `fn boom() -> Nothing with Panic` to the canonical effect-clause keyword (`effect Panic`), turning a 3-diagnostic parse into a fully clean one (now `diagnostics: (none)`). Gates green: fmt, clippy -D warnings, 387 parser tests, architecture R1–R4, conformance.Parse `actor Name { ... }` with all eight slots — const / uses / spawn / receive / state / despawn / test / http — plus internal `fn` helpers. - Lexer: add `receive` / `despawn` / `http` keywords and the `<-` (LArrow) token for `uses` bindings; extract the keyword table into keywords.rs to stay under the R3 file cap. - Parser: new actor_decl.rs — ActorDecl + ActorConst / UsesBlock / UsesBinding / SpawnSlot / ReceiveSlot / DespawnSlot / HttpSlot. spawn/ despawn/http share one signature-shaped helper; receive is separate (untyped `(msg)` / `(_)` binder, type comes from `<T>`). Actor-root `const x = e` has no type annotation or `;` (inferred, block-style), distinct from the module-level ConstDecl. - Reuse + extend the slice-27 test block to also accept actor slots (spawn / receive / despawn) alongside state / fn / seed fn. - Retire the last top-level placeholder (`actor`); drop E0012. - New diagnostics E0102–E0115; ratchet MAX_FILE_LOC 828 → 783 after the keyword extraction. Doc updates (03-parser.md slice-29 section, slices.md Done row, 02-lexer.md keyword table) and the `<-`-vs-reuse-existing-token decision are deliberately deferred, not forgotten.A corpus of real, complete example programs (one .ck per app) for reading and discussion, separate from .cktest parser fixtures: no golden sections, ignored by the conformance harness (it ingests only `.cktest`), and free to use the full intended language even where the bootstrap parser can't parse it yet. The bar is the inverse of a fixture's — every construct must be real and meaningful (no invented primitives, no placeholder mocks). First app: greeter.ck — effect `Log` + its mock, an effectful `greet`, production injection via `uses { Log <- StdoutLog }`, and a `test fn` that exercises the mock. Each construct cites its source in the docs.<-— effects resolve callee-driven 545b5a574fAn effect has one canonical provider, so there is no per-actor provider choice to declare. The dependency graph is built from callees (each slot's effect rows), and the production provider is selected once per environment by infra-as-code config — never bound per actor. So the binding slot has nothing to bind. - Lexer: remove the `<-` (LArrow) token and the now-dead `uses` keyword (`uses` lexes as an ordinary identifier again). - Parser: remove UsesBlock / UsesBinding node kinds and the parse_uses_* productions; the actor body loses its `uses` member. - Renumber slice-29 diagnostics to stay contiguous now that the uses codes (old E0106–E0109) are gone: receive / const / slot-body are E0102–E0111. - Fixtures + corpus: strip `uses`/`<-`; greeter.ck now explains callee-driven resolution and the `effect X with Log { test {} }` custom-mock mechanism. Reverses the `uses`/`<-` mechanism introduced in the slice-29 commit, per the design conversation that followed it.