feat: parser slices 8 + 9 — trait + impl declarations (unified <T> form) #2

Manually merged
dferreira merged 7 commits from feat/002-feat-parser-slices-8-9-trait-impl-declarations into master 2026-01-06 16:40:21 +01:00
Owner

feat: parser slices 8 + 9 — trait + impl declarations (unified form)

Along the way:

  • feat: parser slice 10 — Pratt expression core (literals, paths, calls, members, binary ops)
  • feat: parser slice 11 — blocks (trailing expression, Block as primary)
  • feat: parser slice 12 — statements (LetStmt, ExprStmt, ; terminator)
  • docs: add impl/slices.md — per-slice index across the compiler cascade
  • docs: add 00_why.md — the five bets justifying a new language
  • feat: parser slice 13 — patterns (literal / ident / wildcard / list / cons / type-assert)

Touches 66 files: docs/00_why.md, docs/impl/03-parser.md, docs/impl/README.md, docs/impl/slices.md, docs/syntax.md, docs/traits.md and 60 more.

feat: parser slices 8 + 9 — trait + impl declarations (unified <T> form) Along the way: - feat: parser slice 10 — Pratt expression core (literals, paths, calls, members, binary ops) - feat: parser slice 11 — blocks (trailing expression, Block as primary) - feat: parser slice 12 — statements (LetStmt, ExprStmt, ; terminator) - docs: add impl/slices.md — per-slice index across the compiler cascade - docs: add 00_why.md — the five bets justifying a new language - feat: parser slice 13 — patterns (literal / ident / wildcard / list / cons / type-assert) Touches 66 files: `docs/00_why.md`, `docs/impl/03-parser.md`, `docs/impl/README.md`, `docs/impl/slices.md`, `docs/syntax.md`, `docs/traits.md` and 60 more.
Author
Owner

Split. The decision in first_block is a pure function now, with its own tests in src/rust/crates/ck_parser/src/tests/stmt.rs.

Split. The decision in `first_block` is a pure function now, with its own tests in `src/rust/crates/ck_parser/src/tests/stmt.rs`.
dferreira approved these changes 2026-01-06 13:00:00 +01:00
dferreira left a comment

A clear improvement. The change in src/rust/crates/ck_parser/src/tests/stmt.rs is the part I would have argued for.

A clear improvement. The change in `src/rust/crates/ck_parser/src/tests/stmt.rs` is the part I would have argued for.
@ -0,0 +90,4 @@
}
self.expect(SyntaxKind::Eq, "E0054", "expected `=` in let binding");
self.parse_expr(0);
self.expect(
Owner

The expect message on expect is the only thing a user sees if this fires. Does it say what they should do, or only what we assumed?

The expect message on `expect` is the only thing a user sees if this fires. Does it say what they should do, or only what we assumed?
@ -0,0 +10,4 @@
use super::*;
use ck_lex::SyntaxKind;
fn first_block(out: &ParseOutput) -> &SyntaxNode {
Owner

first_block reads, decides and writes in one function. The deciding part is the piece I would want to test on its own. Worth splitting?

`first_block` reads, decides and writes in one function. The deciding part is the piece I would want to test on its own. Worth splitting?
Author
Owner

It said what I assumed. Reworded in src/rust/crates/ck_parser/src/stmt.rs so it names the input that broke it, which is what the bug report will need.

It said what I assumed. Reworded in `src/rust/crates/ck_parser/src/stmt.rs` so it names the input that broke it, which is what the bug report will need.
Slice 8 adds `TraitDecl` / `TraitMethod`. A trait body holds only method
signatures (no default bodies, no associated types or constants); trait
methods reuse `ParamList` / `ReturnType` / `EffectClause` from slice 7,
and the `TraitMethod` node intentionally omits a `Block` child so its
absence is grammar, not a parse error. Diagnostics E0037–E0041.

Slice 9 adds `ImplDecl` after locking todo #4 as the unified `<T>` form
(2026-05-21): every impl is `impl Trait<TypeArgs> { FnDecl* }`. No `on`
keyword, no privileged `self` — single-parameter and multi-parameter
impls share one grammar rule, and the impl body's method signatures
mirror the trait's signatures with type parameters substituted for
concrete types. Impl-body methods reuse `FnDecl` verbatim; the resolver
will distinguish "impl method" from "free function" by walking up to
the enclosing node. Diagnostics E0042–E0046.

Both bodies brace-balance through stray inner blocks (a user writing a
method body inside a trait, or a bare `{...}` inside an impl) so the
surrounding body loop doesn't lose sync with its own closing `}`.

Test layout: ck_parser tests split by production
(`src/tests/{data,fn,trait,impl}_decl.rs` + shared helpers and
cross-cutting tests in `src/tests/mod.rs`) so every file stays under
the R3 line cap as productions land. New worst case: `lib.rs` at 798.

New `.cktest` fixtures under `tests/parser/trait/` (6) and
`tests/parser/impl/` (8) cover happy paths and each new error code.
The CST round-trip set is extended with trait + impl snippets.
Locks the value-expression production family that makes function bodies parseable:
Literal / PathExpr / ParenExpr / CallExpr / ArgList / MemberExpr / UnaryExpr / BinaryExpr.
Boolean keywords (and/or/not/is) emit E0047 on sight — precedence relative to
arithmetic is undecided in syntax.md; wrapping in parens does not suppress.
Turbofish (`::<`) in value position stops the path at the preceding ident.

ck_syntax gains Checkpoint + start_node_at so the Pratt loop can retroactively
wrap an already-parsed primary as the LHS of a BinaryExpr.

The expression parser is reachable only via a test seam (expr::parse_expression);
parse_block stays brace-balanced until slice 11 wires it in — same "production
first, wire-up later" pattern slice 7 used for the placeholder Block body.

Diagnostic codes: E0047..E0052.
Wires the slice-10 Pratt parser into `parse_block`, so fn bodies, impl-method bodies, and the new block-as-primary case all share one `Block ::= { Expr? }` production. Generalizes E0036's message from "function body" to "block".
Fills the Stmt* slot in Block: `let Ident (: TypeExpr)? = Expr ;` bindings and `Expr ;` expression statements. The block loop uses a checkpoint to retroactively wrap an expression in ExprStmt only when a `;` follows, keeping the slice-11 bare-trailing-expression CST shape intact. Adds the `let` keyword to the lexer and codes E0053..E0055 to the parser. Closes the newline-sensitivity open question — `;` is the statement terminator, newlines stay trivia.
Central index of every slice landed and still pending, with links to the impl-doc anchor where each slice is described and the design doc(s) it realizes. Wires it into docs/impl/README.md so the impl-doc table and the slice index are discoverable side-by-side. Replaces the previous "grep 03-parser.md for slice headers" workflow.
Also adds empty CLAUDE.md and README.md placeholders.
Lands the pattern grammar (LiteralPattern, IdentPattern, WildcardPattern,
ListPattern, ConsPattern + PatternArgList / FieldPatternList + FieldPattern,
RestPattern, TypeAssertPattern) in pattern.rs and rewires the LetStmt slot
through parse_pattern. The slice-12 `let Ident (: TypeExpr)?` collapses into
`let Pattern`: the type annotation is now a TypeAssertPattern. One mechanism
for `let x: Int = 5;` and match-arm `s: String` per the four-symbol uniformity.

Diagnostics: E0056..E0060 new; E0053 broadened to "expected pattern after \`let\`"
(slot unchanged). parse_let_stmt guards with is_pattern_start so the slot-specific
E0053 and the generic E0056 never double-fire.

`when` guard deferred to slice 14 alongside MatchArm: no caller in slice 13, so
landing it now would be dead code. Documented in slices.md and 03-parser.md.

23 new unit tests, 4 new .cktest fixtures, 8 existing fixtures updated for the
new CST shape. Workspace: 257 tests pass, clippy -D warnings clean, R1–R4
architecture rules pass.
dferreira manually merged commit 3d7df47641 into master 2026-01-06 16:40:21 +01:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
northgale/cklang-bootstrap!2
No description provided.