Skip to main content

praxis_parser/
parse.rs

1//! The Praxis parser.
2//!
3//! Recursive descent over statements and structure, with a Pratt (precedence
4//! climbing) loop for arithmetic and the other binary operators (ADR-004). It
5//! consumes the lexer's [`Token`] stream and emits a rowan green tree
6//! (ADR-003) via [`GreenNodeBuilder`], retaining trivia so the tree is lossless
7//! (§13.1). On an unexpected token it emits a `P0xx` diagnostic, wraps the stray
8//! token in a [`SyntaxKind::PARSE_ERROR`] node, advances to a synchronization
9//! point, and keeps going — that is the LSP-grade recovery required by §15.2.
10//!
11//! The grammar is §19: `fn`/`struct`/`enum` items, `var` bindings and
12//! assignment, blocks, `if`/`while`/`for`/`loop`/`match`, and expressions —
13//! literals, calls, closures, ranges, record and collection literals, patterns,
14//! and `read`'s parser expressions (§7.1).
15
16use praxis_source::diagnostic::sort_by_position;
17use praxis_source::{BytePos, Span};
18use praxis_source::{DiagCode, Diagnostic, FileId, FileSpan, Severity};
19use praxis_syntax::{PraxisLanguage, SyntaxKind, SyntaxNode, Token};
20use rowan::{GreenNodeBuilder, Language};
21
22use crate::lex::{LexOutput, lex};
23
24/// The result of parsing one source file: the lossless tree, the lexed tokens,
25/// and any diagnostics (lex + parse merged).
26#[derive(Debug)]
27pub struct ParseOutput {
28    /// The lossless syntax tree (trivia retained). Always produced, even when
29    /// the input is malformed — error nodes ([`SyntaxKind::PARSE_ERROR`]) carry
30    /// the tokens the parser could not place.
31    pub tree: SyntaxNode,
32    /// The token stream from the lex pass, kept for later LSP use (semantic
33    /// tokens) and diagnostics.
34    pub tokens: Vec<Token>,
35    /// Lex (`T0xx`) and parse (`P0xx`) diagnostics, in source order.
36    pub diagnostics: Vec<Diagnostic>,
37}
38
39/// Lex then parse `text` belonging to `file`.
40///
41/// This is the front-end entry point the CLI threads in after reading a file.
42/// It never panics on malformed input; it always returns a tree and as many
43/// diagnostics as it could gather.
44pub fn parse(file: FileId, text: &str) -> ParseOutput {
45    let LexOutput {
46        tokens,
47        mut diagnostics,
48    } = lex(file, text);
49    let mut parser = Parser::new(file, text, &tokens);
50    parser.parse_source_file();
51    let (green, parse_diags) = parser.finish();
52    diagnostics.extend(parse_diags);
53    sort_by_position(&mut diagnostics);
54    let tree = SyntaxNode::new_root(green);
55    ParseOutput {
56        tree,
57        tokens,
58        diagnostics,
59    }
60}
61
62// ---------------------------------------------------------------------------
63// Precedence table (Pratt climbing).
64// ---------------------------------------------------------------------------
65
66/// Binary operator binding power. Higher binds tighter. Left-associative
67/// operators parse by calling `expr(min_bp + 1)` on the right; right-associative
68/// ones would call `expr(min_bp)` (none exist, but the table is shaped to allow
69/// it).
70#[derive(Clone, Copy)]
71struct BindingPower {
72    left: u8,
73    right: u8,
74}
75
76/// The binding power of `op`, or `None` if it is not a binary operator.
77fn infix_binding_power(op: SyntaxKind) -> Option<BindingPower> {
78    Some(match op {
79        // Logical or (lowest of all).
80        SyntaxKind::PIPE2 => bp(1, 2),
81        // Range (§4.11, ADR-059): below comparison, above nothing but `||`. So
82        // `0..n - 1` is `0..(n - 1)` — the bound is an arithmetic expression,
83        // which is how every range in the corpus is written.
84        SyntaxKind::DOT2 | SyntaxKind::DOT2EQ => bp(3, 4),
85        // Logical and: **below comparison and above `..`**. The two rules that
86        // matter are that `&&` binds tighter than `||` (`a || b && c` is
87        // `a || (b && c)`) and looser than comparison (`a == b && c == d` is
88        // `(a == b) && (c == d)`), which is §3.3's own shape. Where `&&` sits
89        // relative to `..` is arbitrary — a range of `Bool`s and a range bound
90        // that is a `&&` are both nonsense.
91        SyntaxKind::AMP2 => bp(5, 6),
92        // Comparison (non-associative in spirit; we parse left-assoc).
93        SyntaxKind::EQ2
94        | SyntaxKind::NEQ
95        | SyntaxKind::LT
96        | SyntaxKind::GT
97        | SyntaxKind::LTEQ
98        | SyntaxKind::GTEQ => bp(7, 8),
99        // Additive.
100        SyntaxKind::PLUS | SyntaxKind::MINUS => bp(9, 10),
101        // Multiplicative.
102        SyntaxKind::STAR | SyntaxKind::SLASH | SyntaxKind::PERCENT => bp(11, 12),
103        _ => return None,
104    })
105}
106
107/// The node kind an infix operator builds. Every operator but `..`/`..=` builds
108/// a [`SyntaxKind::BIN_EXPR`]; a range is its own node because it is not an
109/// operator applied to two numbers but a collection built from two bounds.
110fn infix_node_kind(op: SyntaxKind) -> SyntaxKind {
111    match op {
112        SyntaxKind::DOT2 | SyntaxKind::DOT2EQ => SyntaxKind::RANGE_EXPR,
113        _ => SyntaxKind::BIN_EXPR,
114    }
115}
116
117/// Prefix (unary) operator binding power.
118fn prefix_binding_power(op: SyntaxKind) -> Option<u8> {
119    match op {
120        // Above every infix operator, so `!a && b` is `(!a) && b` — §3.3's own
121        // `!diagonals && dx != 0` — and `-a * b` is `(-a) * b`.
122        SyntaxKind::MINUS | SyntaxKind::BANG => Some(13),
123        // `read` is a prefix expression (§7.1): `read parser_expression`. Its
124        // body is a parser-expression, not an ordinary expression, so it gets
125        // the highest binding power (binds tighter than arithmetic).
126        SyntaxKind::KW_READ => Some(15),
127        _ => None,
128    }
129}
130
131/// The compiler-owned type constructors, the only names that take an explicit
132/// type-argument list in expression position (§3.3's `Counter[(Int, Int)]()`).
133///
134/// The parser has to know these **by name**, because nothing else can tell
135/// `Counter[(Int, Int)]()` from `m[key]`: the brackets are the same and the
136/// contents are ambiguous too — `Int` is a legal expression, and `(Int, Int)` a
137/// legal tuple of two. `parse`'s own special case (§7.1) is the precedent for a
138/// name-driven decision here.
139///
140/// The list is the §6.1 collection set plus `Option`, and `praxis-hir`'s
141/// `is_type_ctor_name` is the other copy of it —
142/// `the_parsers_type_constructors_are_the_compilers` asserts the two agree, so a
143/// name in only one cannot go unnoticed.
144pub const TYPE_CONSTRUCTOR_NAMES: &[&str] = &[
145    "Vec", "Deque", "Map", "Set", "Counter", "MinHeap", "MaxHeap", "BitSet", "Grid", "Range",
146    "Option",
147];
148
149/// Whether `op` is an assignment operator (statement-level reassignment, §4.2).
150/// These are not infix expression operators; they are parsed as statements.
151fn is_assignment_op(op: SyntaxKind) -> bool {
152    matches!(
153        op,
154        SyntaxKind::EQ
155            | SyntaxKind::PLUS_EQ
156            | SyntaxKind::MINUS_EQ
157            | SyntaxKind::STAR_EQ
158            | SyntaxKind::SLASH_EQ
159            | SyntaxKind::PERCENT_EQ
160    )
161}
162
163/// Whether `kind` can start a match pattern (§4.6). Used to decide whether
164/// to continue parsing arms after a newline (arms are comma-OR-newline separated).
165///
166/// This set must stay the set [`Parser::parse_pattern`] consumes: a pattern this
167/// admits is one that parses, and one it rejects silently ends the arm list.
168fn is_pattern_start(kind: SyntaxKind) -> bool {
169    // The literal half is `SyntaxKind::is_pattern_literal` — the same set
170    // `parse_pattern` consumes and `praxis_ast::Pattern` reads back.
171    kind.is_pattern_literal()
172        || matches!(
173            kind,
174            SyntaxKind::UNDERSCORE
175                | SyntaxKind::Ident
176                // A tuple pattern.
177                | SyntaxKind::L_PAREN
178                // A headless record pattern (ADR-091).
179                | SyntaxKind::L_BRACE
180        )
181}
182
183const fn bp(left: u8, right: u8) -> BindingPower {
184    BindingPower { left, right }
185}
186
187// ---------------------------------------------------------------------------
188// Statement separation (D8, ADR-049).
189// ---------------------------------------------------------------------------
190
191/// Whether a bare `Name { … }` in expression position is a record literal.
192///
193/// `if p { … }` is genuinely ambiguous: `p { … }` could be a record literal, or
194/// `p` could be the condition and `{ … }` the then-block. The four keyword heads
195/// — `if`, `while`, `for`'s iterator, `match`'s scrutinee — resolve it by
196/// suppressing the literal, and the suppression follows the operands of the
197/// expression they own.
198///
199/// It stops at the first bracket. Inside `(…)`, `[…]`, an argument list or a
200/// block, the `{` cannot be the body the keyword is looking for, so there is
201/// nothing left to disambiguate — which is why suppression is a parameter
202/// threaded through the expression grammar rather than parser-wide state. State
203/// would leak into every parenthesized subexpression and every match-arm body,
204/// making valid record literals unwritable there.
205#[derive(Clone, Copy, PartialEq, Eq, Debug)]
206enum StructLit {
207    Allowed,
208    Suppressed,
209}
210
211/// What ended a statement.
212///
213/// Returning this rather than a `bool` is the point: a statement loop cannot
214/// advance without either producing a separator value or emitting a diagnostic,
215/// so "two statements adjacent with no separator" has no accepted
216/// representation (D8, ADR-049).
217#[derive(Clone, Copy, PartialEq, Eq, Debug)]
218enum StmtSeparator {
219    /// An explicit `;`, consumed.
220    Semicolon,
221    /// A line break before the next token. The newline itself is trivia and
222    /// stays trivia — the fact that it existed rides on the token after it.
223    Newline,
224    /// `}` or end of file: there is no next statement to separate from.
225    EndOfBlock,
226}
227
228/// What `(1,)` and `(Int,)` are told, in one place because they are one rule
229/// (§4.4). The expression and the type position both reach it through
230/// [`Parser::parse_parenthesized`]; a second copy of the sentence is a second
231/// thing to forget to change.
232const ONE_ELEMENT_TUPLE_MSG: &str = "a tuple has two elements or more, so this comma names nothing";
233
234// ---------------------------------------------------------------------------
235// Parser state.
236// ---------------------------------------------------------------------------
237
238struct Parser<'t> {
239    file: FileId,
240    /// Full source text; needed to extract each token's spelling for the green
241    /// tree (the lexer only stored spans, not text).
242    text: &'t str,
243    tokens: &'t [Token],
244    /// Index into `tokens`; advances monotonically.
245    cursor: usize,
246    builder: GreenNodeBuilder<'static>,
247    diagnostics: Vec<Diagnostic>,
248}
249
250impl<'t> Parser<'t> {
251    fn new(file: FileId, text: &'t str, tokens: &'t [Token]) -> Parser<'t> {
252        Parser {
253            file,
254            text,
255            tokens,
256            cursor: 0,
257            builder: GreenNodeBuilder::new(),
258            diagnostics: Vec::new(),
259        }
260    }
261
262    // --- entry point ---
263
264    fn parse_source_file(&mut self) {
265        self.start_root_node(SyntaxKind::SOURCE_FILE);
266        while !self.at_end() {
267            // Emit any trivia leading each statement into the root node.
268            self.eat_trivia();
269            if self.at_end() {
270                break;
271            }
272            let before = self.meaningful_index();
273            if self.parse_stmt() {
274                // A statement is separated from the next by `;`, a newline, or
275                // the end of the file — and by nothing else.
276                self.expect_stmt_separator();
277            } else {
278                // Recovery: skip to the next statement boundary.
279                self.recover_to_stmt_boundary();
280            }
281            // Guarantee termination on any input (defense against infinite loops).
282            self.ensure_progress(before);
283        }
284        // Trailing trivia belongs to the root.
285        self.eat_trivia();
286        self.finish_node();
287    }
288
289    // --- token cursor (over the meaningful stream; trivia emitted on sight) ---
290
291    /// The kind of the current meaningful token (trivia skipped for the
292    /// decision, but not consumed — it is emitted when bumped).
293    fn peek(&mut self) -> SyntaxKind {
294        self.nth_kind(0)
295    }
296
297    /// Kind of the meaningful token `n` positions ahead (0 = current).
298    fn nth_kind(&mut self, n: usize) -> SyntaxKind {
299        let mut idx = self.cursor;
300        let mut want = n;
301        while idx < self.tokens.len() {
302            let kind = self.tokens[idx].kind;
303            if kind.is_trivia() {
304                idx += 1;
305                continue;
306            }
307            if want == 0 {
308                return kind;
309            }
310            want -= 1;
311            idx += 1;
312        }
313        SyntaxKind::EOF
314    }
315
316    /// Whether the cursor is past the last meaningful token.
317    fn at_end(&mut self) -> bool {
318        self.peek() == SyntaxKind::EOF
319    }
320
321    /// True iff a line break sits between the previous meaningful token and the
322    /// current one (D8, ADR-049).
323    ///
324    /// The flag lives on the token *after* the break, so this answer does not
325    /// depend on whether the intervening trivia has already been emitted into
326    /// the tree.
327    fn newline_before(&self) -> bool {
328        self.newline_before_nth(0)
329    }
330
331    /// [`Parser::newline_before`] about the meaningful token `n` positions ahead
332    /// (0 = current). The field-vs-method decision looks one token past the name,
333    /// and `p.x\n(a, b)` must not be a method call for the same reason
334    /// `10\n(a, b)` must not be one.
335    fn newline_before_nth(&self, n: usize) -> bool {
336        self.tokens[self.cursor..]
337            .iter()
338            .filter(|token| !token.kind.is_trivia())
339            .nth(n)
340            .is_some_and(|token| token.preceded_by_newline)
341    }
342
343    /// True iff the `(` here opens an **argument list** for the expression that
344    /// precedes it, rather than beginning something new.
345    ///
346    /// A `(` begins three things: a parenthesized expression, a tuple, and a
347    /// **tuple pattern**, in a match arm and in a `for` binding. So a line-leading
348    /// `(` is ambiguous in exactly the way ADR-049's rule is written to settle:
349    ///
350    /// ```text
351    /// match p {
352    ///     (0, 0) => 10
353    ///     (a, b) => a + b     // a second arm, not `10(a, b)`
354    /// }
355    /// ```
356    ///
357    /// A match arm has no workaround for the ambiguity — a tuple pattern *is* how
358    /// the arm is written — so the tie is broken by D8's own rule: a newline ends
359    /// a statement. It is not consulted anywhere in the Pratt operator loop, so
360    /// `1 +\n2` and a `.method()` chain across lines are unaffected, and a `(`
361    /// that opens an expression is unaffected — only a `(` asked to *continue* one
362    /// is.
363    ///
364    /// The cost is stated rather than hidden: a call whose callee ends a line and
365    /// whose argument list begins the next (`f\n(1)`) is two expressions, and the
366    /// fix is to move the `(` up.
367    fn at_argument_list(&mut self) -> bool {
368        self.at(SyntaxKind::L_PAREN) && !self.newline_before()
369    }
370
371    /// True iff the `[` here opens a **subscript** on the expression that
372    /// precedes it, rather than beginning a list literal.
373    ///
374    /// [`at_argument_list`](Self::at_argument_list)'s rule at the second bracket.
375    /// A `[` both begins a list literal and continues the expression before it,
376    /// and the two spellings are the same two characters:
377    ///
378    /// ```text
379    /// var n = total
380    /// [1, 2, 3]           // a list literal, not `total[1, 2, 3]`
381    /// ```
382    ///
383    /// So the tie is broken by position, exactly as it is for `(`: a `[` on the
384    /// same line as what precedes it subscripts that expression, and a
385    /// line-leading `[` starts a new one. `m[k]`, `grid[x, y]` and `m[k][j]` are
386    /// unaffected — every one of them is written on one line — and the cost is
387    /// the mirror of the argument-list rule's: a subscript whose receiver ends a
388    /// line and whose bracket begins the next is two expressions, and the fix is
389    /// to move the `[` up.
390    fn at_subscript(&mut self) -> bool {
391        self.at(SyntaxKind::L_BRACK) && !self.newline_before()
392    }
393
394    /// True iff a value follows `break`/`return` on the same line.
395    ///
396    /// Two questions, both of which must say yes: the next token has to be able
397    /// to begin an expression at all (a `;`, `}`, `)`, `,`, `else` or `in`
398    /// cannot), and it has to be on *this* line — `return\n1` is a value-less
399    /// return followed by a separate statement, which is the second half of
400    /// D8's rule.
401    fn starts_expr(&mut self) -> bool {
402        if self.newline_before() {
403            return false;
404        }
405        // Consume trivia so the cursor lands on the meaningful token, then check
406        // whether that token can begin an expression.
407        self.eat_trivia();
408        let k = self
409            .tokens
410            .get(self.cursor)
411            .map(|t| t.kind)
412            .unwrap_or(SyntaxKind::EOF);
413        use SyntaxKind::*;
414        !matches!(
415            k,
416            EOF | SEMICOLON | R_BRACE | R_PAREN | COMMA | KW_ELSE | KW_IN
417        )
418    }
419
420    /// Demand the separator that must follow a statement, and report which one
421    /// it was — `;`, a line break, or the end of the enclosing block/file.
422    ///
423    /// `None` means there was none: two statements ran together on one line, and
424    /// a `P002` has been emitted at the second one. The caller keeps parsing
425    /// (one diagnostic per run-on, not a cascade), because the statement itself
426    /// was well-formed and the next one usually is too.
427    fn expect_stmt_separator(&mut self) -> Option<StmtSeparator> {
428        if self.eat(SyntaxKind::SEMICOLON) {
429            return Some(StmtSeparator::Semicolon);
430        }
431        if self.at_end() || self.at(SyntaxKind::R_BRACE) {
432            return Some(StmtSeparator::EndOfBlock);
433        }
434        if self.newline_before() {
435            return Some(StmtSeparator::Newline);
436        }
437        let span = self.current_span();
438        self.error_with(
439            DiagCode::ExpectedStatementSeparator,
440            span,
441            "expected `;` or a line break between statements",
442        );
443        None
444    }
445
446    /// The source text of the current meaningful token (trivia skipped). Used to
447    /// special-case keywords-that-look-like-idents such as `parse` and parser
448    /// constructor names (`lines`, `csv`, …).
449    fn peek_text(&mut self) -> Option<&'t str> {
450        let mut idx = self.cursor;
451        while idx < self.tokens.len() {
452            let kind = self.tokens[idx].kind;
453            if kind.is_trivia() {
454                idx += 1;
455                continue;
456            }
457            let span = self.tokens[idx].span;
458            return Some(&self.text[span.start().to_usize()..span.end().to_usize()]);
459        }
460        None
461    }
462
463    /// `true` if an updating store's operator starts here: an `Ident` spelling
464    /// `min` or `max`, **immediately** followed by `=` (§6.2).
465    ///
466    /// Adjacency is the rule, exactly as it is for `+=`: `min=` is one operator
467    /// spelled in two tokens because `min` is an identifier, and `min = x` with a
468    /// space is two tokens that mean what they say. Checking the raw token stream
469    /// rather than [`Parser::nth_kind`] is what makes that askable — `nth_kind`
470    /// skips trivia, which is precisely the difference.
471    ///
472    /// `==` cannot be mistaken for it: the lexer's max-munch makes that one
473    /// `EQ2` token.
474    fn at_update_op(&mut self) -> bool {
475        if !self.at(SyntaxKind::Ident) {
476            return false;
477        }
478        if !matches!(self.peek_text(), Some("min" | "max")) {
479            return false;
480        }
481        let ident = self.meaningful_index();
482        self.tokens
483            .get(ident + 1)
484            .is_some_and(|t| t.kind == SyntaxKind::EQ)
485    }
486
487    /// `true` if a statement's `:bp` breakpoint marker starts here: a `:`
488    /// **immediately** followed by an `Ident` spelling `bp` (§9.8).
489    ///
490    /// [`Parser::at_update_op`]'s shape, and adjacency is the rule for its
491    /// reason: `bp` is an identifier, so `:bp` is one marker spelled in two
492    /// tokens, and `: bp` with a space is a `:` followed by a name. The raw
493    /// token stream is what makes that askable — [`Parser::nth_kind`] skips
494    /// trivia, which is exactly the difference between the two spellings.
495    ///
496    /// A type annotation cannot be mistaken for it, because this is only ever
497    /// asked at the *end* of a statement: `var x: Int = 1` has consumed its `:`
498    /// inside [`Parser::parse_var`] long before this runs, and a `:` that
499    /// survives to a statement's end begins nothing else in the grammar.
500    fn at_breakpoint_marker(&mut self) -> bool {
501        self.is_breakpoint_marker_at(self.meaningful_index())
502    }
503
504    /// Whether the raw token at `colon` is a `:` that begins a `:bp` marker.
505    ///
506    /// The marker's rule lives here and nowhere else. A `{`'s lookahead has to
507    /// ask the same question one token further along — `{ x:bp }` is a block
508    /// holding a marked statement, `{ x: bp }` is a record literal whose field
509    /// is the binding `bp` — and asking it by re-spelling the adjacency test
510    /// would put the rule in two places for the two positions to drift apart.
511    fn is_breakpoint_marker_at(&self, colon: usize) -> bool {
512        if self.tokens.get(colon).map(|t| t.kind) != Some(SyntaxKind::COLON) {
513            return false;
514        }
515        self.tokens.get(colon + 1).is_some_and(|t| {
516            t.kind == SyntaxKind::Ident
517                && &self.text[t.span.start().to_usize()..t.span.end().to_usize()] == "bp"
518        })
519    }
520
521    /// Consume a trailing `:bp` marker into a [`SyntaxKind::BREAKPOINT`] node,
522    /// if one is here. Answers whether it consumed anything.
523    ///
524    /// Called from each statement parser just before it closes its own node, so
525    /// the marker is a *child of the statement it marks* rather than a sibling
526    /// of it. That is what lets HIR lowering ask a statement node whether it
527    /// carries one without re-deriving the association from source positions.
528    fn eat_breakpoint_marker(&mut self) -> bool {
529        if !self.at_breakpoint_marker() {
530            return false;
531        }
532        // `start_node` sweeps the leading trivia into the *enclosing* node, so
533        // the whitespace before `:bp` stays outside the marker. The two tokens
534        // are adjacent by `at_breakpoint_marker`'s check, so neither bump needs
535        // a sweep of its own.
536        self.start_node(SyntaxKind::BREAKPOINT);
537        self.bump_meaningful(); // `:`
538        self.bump_meaningful(); // `bp`
539        self.finish_node();
540        true
541    }
542
543    /// `true` if the current meaningful token is `kind`.
544    fn at(&mut self, kind: SyntaxKind) -> bool {
545        self.peek() == kind
546    }
547
548    /// Consume the current meaningful token if it is `kind`, returning `true`.
549    /// Emits any trivia encountered first.
550    fn eat(&mut self, kind: SyntaxKind) -> bool {
551        if self.at(kind) {
552            self.bump();
553            true
554        } else {
555            false
556        }
557    }
558
559    /// Consume the current meaningful token and append it (plus any leading
560    /// trivia) to the tree. Panics only if called at EOF — the grammar always
561    /// checks `at`/`at_end` first, so this is unreachable in well-formed calls.
562    fn bump(&mut self) {
563        // Emit any trivia sitting before the token we are about to take.
564        self.eat_trivia();
565        self.bump_meaningful();
566    }
567
568    /// Consume the current token assuming trivia has already been emitted (no
569    /// trivia sweep). Used after an explicit `eat_trivia` to keep trivia out of
570    /// a node that is about to be opened.
571    fn bump_meaningful(&mut self) {
572        debug_assert!(self.cursor < self.tokens.len(), "bump past EOF");
573        let token = self.tokens[self.cursor];
574        if token.kind == SyntaxKind::EOF {
575            // Do not advance past EOF; callers should not bump it.
576            return;
577        }
578        self.emit_token(token);
579        self.cursor += 1;
580    }
581
582    /// The index of the current *meaningful* token (skipping trivia). Used to
583    /// detect whether a sub-parse made progress; an infinite loop anywhere in
584    /// the grammar would show up as `meaningful_index()` not advancing.
585    fn meaningful_index(&self) -> usize {
586        self.nth_index(0).unwrap_or(self.tokens.len())
587    }
588
589    /// The **raw** token index of the meaningful token `n` positions ahead
590    /// (0 = current), or `None` past the last one.
591    ///
592    /// [`Parser::nth_kind`]'s answer with the position kept, which is what an
593    /// adjacency question needs: `nth_kind` skips trivia, so it cannot tell
594    /// `:bp` from `: bp`, and every two-token operator in this grammar
595    /// (`min=`, `:bp`) is decided by whether the second token *touches* the
596    /// first. `meaningful_index` answers that for the token under the cursor;
597    /// this answers it for one further ahead, which is where a `{`'s contents
598    /// are read from.
599    fn nth_index(&self, n: usize) -> Option<usize> {
600        let mut idx = self.cursor;
601        let mut want = n;
602        while idx < self.tokens.len() {
603            if self.tokens[idx].kind.is_trivia() {
604                idx += 1;
605                continue;
606            }
607            if want == 0 {
608                return Some(idx);
609            }
610            want -= 1;
611            idx += 1;
612        }
613        None
614    }
615
616    /// Defense against catastrophic infinite loops: if the cursor did not
617    /// advance past `before`, consume one token (in a PARSE_ERROR node) so the
618    /// parser always makes progress. Every loop over statements/expressions
619    /// calls this after each body iteration.
620    ///
621    /// This is a safety net, not the primary recovery mechanism — the grammar
622    /// is written to always consume — but it guarantees termination on any
623    /// input, which is a hard requirement (§19: "no panic on fuzzed input",
624    /// and an OOM-kill from an unbounded loop is a panic in disguise).
625    fn ensure_progress(&mut self, before: usize) {
626        let now = self.meaningful_index();
627        if now <= before && !self.at_end() {
628            self.start_node(SyntaxKind::PARSE_ERROR);
629            let span = self.current_span();
630            self.error(span, "stuck: skipping token to make progress");
631            self.bump();
632            self.finish_node();
633        }
634    }
635
636    /// Emit trivia (whitespace/comments) into the tree up to, but not
637    /// including, the next meaningful token. Trivia is part of the lossless
638    /// tree, so it must be appended even though it is ignored for decisions.
639    fn eat_trivia(&mut self) {
640        while self.cursor < self.tokens.len() {
641            let kind = self.tokens[self.cursor].kind;
642            if !kind.is_trivia() {
643                break;
644            }
645            self.emit_token(self.tokens[self.cursor]);
646            self.cursor += 1;
647        }
648    }
649
650    /// Append a single token's text to the green tree.
651    fn emit_token(&mut self, token: Token) {
652        // Compute the byte range first so the immutable borrow of `self.text`
653        // ends before the mutable borrow of `self.builder` begins.
654        let start = token.span.start().to_u32() as usize;
655        let end = token.span.end().to_u32() as usize;
656        let text = &self.text[start..end];
657        self.builder
658            .token(PraxisLanguage::kind_to_raw(token.kind), text);
659    }
660
661    // --- green-tree helpers ---
662
663    /// Open a node **on its first meaningful token** — the trivia in front of it
664    /// is emitted first, so it lands in the enclosing node instead.
665    ///
666    /// This is the whole of the rule "a node never begins with trivia", and it
667    /// belongs here rather than at every call site. It is what makes a node's
668    /// span the node's own text: the `PATH_EXPR` for `a` in `var c = a + b`
669    /// spans `"a"` and not `" a"`, so the caret in a diagnostic that underlines
670    /// an expression starts at the expression.
671    ///
672    /// The root is the one node this cannot open ([`start_root_node`](Self::start_root_node)):
673    /// there is nothing to emit trivia into before it exists.
674    fn start_node(&mut self, kind: SyntaxKind) {
675        self.eat_trivia();
676        self.builder.start_node(PraxisLanguage::kind_to_raw(kind));
677    }
678
679    /// Open the root node. The only node opened *before* its leading trivia,
680    /// because a token cannot be emitted with no node open — the root is where
681    /// a file's leading trivia goes.
682    fn start_root_node(&mut self, kind: SyntaxKind) {
683        self.builder.start_node(PraxisLanguage::kind_to_raw(kind));
684    }
685
686    fn finish_node(&mut self) {
687        self.builder.finish_node();
688    }
689
690    fn start_node_at(&mut self, cp: rowan::Checkpoint, kind: SyntaxKind) {
691        self.builder
692            .start_node_at(cp, PraxisLanguage::kind_to_raw(kind));
693    }
694
695    // --- diagnostics ---
696
697    fn error(&mut self, span: Span, message: impl Into<String>) {
698        self.error_with(DiagCode::UnexpectedToken, span, message);
699    }
700
701    fn error_with(&mut self, code: DiagCode, span: Span, message: impl Into<String>) {
702        self.diagnostics.push(Diagnostic::new(
703            Severity::Error,
704            code,
705            message,
706            FileSpan::new(self.file, span),
707        ));
708    }
709
710    /// Consume `self` to produce the green node and the parse diagnostics.
711    fn finish(self) -> (rowan::GreenNode, Vec<Diagnostic>) {
712        let green = self.builder.finish();
713        (green, self.diagnostics)
714    }
715
716    // -----------------------------------------------------------------------
717    // Grammar.
718    // -----------------------------------------------------------------------
719
720    /// Parse one statement. Returns `false` if recovery was needed.
721    fn parse_stmt(&mut self) -> bool {
722        match self.peek() {
723            SyntaxKind::KW_VAR => self.parse_var(),
724            SyntaxKind::KW_FN => self.parse_fn_item(),
725            SyntaxKind::KW_STRUCT => self.parse_struct_item(),
726            SyntaxKind::KW_ENUM => self.parse_enum_item(),
727            // `name = expr` / `name += expr` reassignment (§4.2).
728            SyntaxKind::Ident if is_assignment_op(self.nth_kind(1)) => self.parse_assign_stmt(),
729            _ => self.parse_expr_stmt(),
730        }
731    }
732
733    /// `var name [: Type] = expr` — the language's one binding form (§4.2).
734    fn parse_var(&mut self) -> bool {
735        self.start_node(SyntaxKind::VAR_STMT);
736        self.bump(); // `var`
737        self.expect_binder("binding name");
738        // Optional type annotation `: Type`.
739        if self.eat(SyntaxKind::COLON) {
740            self.parse_type();
741        }
742        self.expect(SyntaxKind::EQ, "`=`");
743        self.parse_expr();
744        self.eat_breakpoint_marker();
745        self.finish_node();
746        true
747    }
748
749    /// `name = expr` or `name += expr` (etc.) — reassignment to an existing
750    /// binding (§4.2). The lhs is a bare name; a field or subscript target
751    /// builds a `PLACE_ASSIGN_STMT` in
752    /// [`parse_expr_stmt`](Self::parse_expr_stmt) instead.
753    fn parse_assign_stmt(&mut self) -> bool {
754        self.start_node(SyntaxKind::ASSIGN_STMT);
755        self.bump(); // name
756        self.bump(); // assignment operator (=, +=, ...)
757        self.parse_expr();
758        self.eat_breakpoint_marker();
759        self.finish_node();
760        true
761    }
762
763    /// `fn name(params) -> Ret { body }` (params and return type optional).
764    fn parse_fn_item(&mut self) -> bool {
765        self.start_node(SyntaxKind::FN_ITEM);
766        self.bump(); // `fn`
767        self.expect(SyntaxKind::Ident, "function name");
768        if self.eat(SyntaxKind::L_PAREN) {
769            self.start_node(SyntaxKind::PARAM_LIST);
770            // Zero or more `name: Type` params separated by commas.
771            if !self.at(SyntaxKind::R_PAREN) {
772                loop {
773                    let before = self.meaningful_index();
774                    self.start_node(SyntaxKind::PARAM);
775                    self.expect_binder("parameter name");
776                    // The `: Type` annotation is OPTIONAL (§4.9, criterion 1):
777                    // `fn manhattan(a, b) { … }` infers param types from use.
778                    if self.eat(SyntaxKind::COLON) {
779                        self.parse_type();
780                    }
781                    self.finish_node();
782                    if !self.eat(SyntaxKind::COMMA) {
783                        break;
784                    }
785                    // A trailing comma closes the list.
786                    if self.at(SyntaxKind::R_PAREN) {
787                        break;
788                    }
789                    // Guarantee termination on any input.
790                    self.ensure_progress(before);
791                }
792            }
793            self.expect(SyntaxKind::R_PAREN, "`)`");
794            self.finish_node();
795        }
796        // Optional `-> Type` return annotation.
797        if self.eat(SyntaxKind::THIN_ARROW) {
798            self.parse_type();
799        }
800        // Body.
801        if self.at(SyntaxKind::L_BRACE) {
802            self.parse_block();
803        } else {
804            let span = self.current_span();
805            self.error(span, "expected `{` to begin function body");
806        }
807        self.finish_node();
808        true
809    }
810
811    /// `struct Name { field: Type, … }` (§4.5). The field list is a
812    /// `FIELD_LIST` of `FIELD` children, each `name: Type`.
813    fn parse_struct_item(&mut self) -> bool {
814        self.start_node(SyntaxKind::STRUCT_ITEM);
815        self.bump(); // `struct`
816        self.expect(SyntaxKind::Ident, "struct name");
817        self.expect(SyntaxKind::L_BRACE, "`{` to begin struct fields");
818        self.start_node(SyntaxKind::FIELD_LIST);
819        if !self.at(SyntaxKind::R_BRACE) {
820            loop {
821                let before = self.meaningful_index();
822                self.start_node(SyntaxKind::FIELD);
823                self.expect(SyntaxKind::Ident, "field name");
824                self.expect(SyntaxKind::COLON, "`:` before field type");
825                self.parse_type();
826                self.finish_node();
827                // A comma **or** a line break separates fields — §4.5's own
828                // `struct Point { x: Int\n y: Int }` writes the second, and a
829                // trailing comma closes the list either way.
830                if !self.member_separator("struct fields") {
831                    break;
832                }
833                self.ensure_progress(before);
834            }
835        }
836        self.expect(SyntaxKind::R_BRACE, "`}` to end struct fields");
837        self.finish_node(); // FIELD_LIST
838        self.finish_node(); // STRUCT_ITEM
839        true
840    }
841
842    /// Whether another member of a brace-delimited declaration follows, having
843    /// consumed the separator between them.
844    ///
845    /// A member is followed by a comma **or** a line break, which is the rule
846    /// match arms use (D8, ADR-049) and the one §4.5's and §4.6's own
847    /// declarations are written with:
848    ///
849    /// ```praxis
850    /// struct Point {
851    ///     x: Int
852    ///     y: Int
853    /// }
854    /// ```
855    ///
856    /// The two separators are interchangeable and a trailing comma still closes
857    /// the list, so the answer is `false` at the closing brace whichever one
858    /// preceded it. A member that follows with *neither* is reported at the
859    /// same code a run-together statement is — and then parsed anyway, because
860    /// the mistake is the separator and not the member.
861    fn member_separator(&mut self, what: &str) -> bool {
862        let comma = self.eat(SyntaxKind::COMMA);
863        // A closing brace ends the list, and an `Ident` is the only token that
864        // can begin either kind of member — anything else is a mistake the
865        // member's own parser will report.
866        if self.at(SyntaxKind::R_BRACE) || !self.at(SyntaxKind::Ident) {
867            return false;
868        }
869        if !comma && !self.newline_before() {
870            let span = self.current_span();
871            self.error_with(
872                DiagCode::ExpectedStatementSeparator,
873                span,
874                format!("expected `,` or a line break between {what}"),
875            );
876        }
877        true
878    }
879
880    /// `enum Name { Variant, Variant(Type, …), … }` (§4.6). Each variant is
881    /// an `ENUM_VARIANT` node: a name optionally followed by `( type_list )`.
882    fn parse_enum_item(&mut self) -> bool {
883        self.start_node(SyntaxKind::ENUM_ITEM);
884        self.bump(); // `enum`
885        self.expect(SyntaxKind::Ident, "enum name");
886        self.expect(SyntaxKind::L_BRACE, "`{` to begin enum variants");
887        if !self.at(SyntaxKind::R_BRACE) {
888            loop {
889                let before = self.meaningful_index();
890                self.start_node(SyntaxKind::ENUM_VARIANT);
891                self.expect(SyntaxKind::Ident, "variant name");
892                // Optional payload: `( Type, Type, … )`.
893                if self.eat(SyntaxKind::L_PAREN) {
894                    if !self.at(SyntaxKind::R_PAREN) {
895                        loop {
896                            let pbefore = self.meaningful_index();
897                            self.parse_type();
898                            if !self.eat(SyntaxKind::COMMA) {
899                                break;
900                            }
901                            // A trailing comma closes the list.
902                            if self.at(SyntaxKind::R_PAREN) {
903                                break;
904                            }
905                            self.ensure_progress(pbefore);
906                        }
907                    }
908                    self.expect(SyntaxKind::R_PAREN, "`)` to close variant payload");
909                }
910                self.finish_node(); // ENUM_VARIANT
911                // A comma **or** a line break, as §4.6's
912                // own `enum Tile { Empty\n Wall\n … }`
913                // writes it.
914                if !self.member_separator("enum variants") {
915                    break;
916                }
917                self.ensure_progress(before);
918            }
919        }
920        self.expect(SyntaxKind::R_BRACE, "`}` to end enum variants");
921        self.finish_node(); // ENUM_ITEM
922        true
923    }
924
925    /// A bare expression used as a statement.
926    fn parse_expr_stmt(&mut self) -> bool {
927        if self.at(SyntaxKind::L_BRACE) {
928            // A block as a statement is parsed as an expression.
929            self.start_node(SyntaxKind::EXPR_STMT);
930            self.parse_expr();
931            self.eat_breakpoint_marker();
932            self.finish_node();
933            return true;
934        }
935        // The expression is parsed before the statement node is opened, because
936        // what it turns out to be decides the kind: an assignment operator after
937        // it makes the whole thing a `PLACE_ASSIGN_STMT` whose first child is the
938        // target (`counts[point] += 1`), and anything else an `EXPR_STMT`.
939        //
940        // A bare `name` target never reaches here — `parse_stmt` sends
941        // `name = …` to `parse_assign_stmt` on the token after the name — so the
942        // targets this sees are the compound ones. Which of them is a place is
943        // inference's answer rather than the parser's: `p.x = 1` is a field store
944        // (§4.5) and `f() = 1` is a well-formed *shape* whose mistake is that it
945        // names no storage (`Y021`), and a parse error there says only "expected
946        // a statement separator" about either.
947        self.eat_trivia();
948        let cp = self.checkpoint_lhs();
949        self.parse_expr();
950        if is_assignment_op(self.peek()) {
951            self.start_node_at(cp, SyntaxKind::PLACE_ASSIGN_STMT);
952            self.bump(); // assignment operator
953            self.parse_expr();
954            self.eat_breakpoint_marker();
955            self.finish_node(); // PLACE_ASSIGN_STMT
956            return true;
957        }
958        // `distance[key] min= candidate` (§6.2). The operator is two tokens, so
959        // the parser decides it here rather than the lexer: `min` is an
960        // identifier everywhere else, and a lexer rule would take it away from
961        // every program that names the prelude helper.
962        if self.at_update_op() {
963            self.start_node_at(cp, SyntaxKind::PLACE_ASSIGN_STMT);
964            self.start_node(SyntaxKind::UPDATE_OP);
965            self.bump(); // `min` / `max`
966            self.bump(); // `=`
967            self.finish_node(); // UPDATE_OP
968            self.parse_expr();
969            self.eat_breakpoint_marker();
970            self.finish_node(); // PLACE_ASSIGN_STMT
971            return true;
972        }
973        self.start_node_at(cp, SyntaxKind::EXPR_STMT);
974        self.eat_breakpoint_marker();
975        self.finish_node();
976        true
977    }
978
979    /// `{ stmt; stmt; expr }` — a block. The last expression is the block's
980    /// value (§4.11). Every item is either a statement or a trailing expression.
981    fn parse_block(&mut self) {
982        self.start_node(SyntaxKind::BLOCK_EXPR);
983        self.bump(); // `{`
984        while !self.at(SyntaxKind::R_BRACE) && !self.at_end() {
985            self.eat_trivia();
986            if self.at(SyntaxKind::R_BRACE) || self.at_end() {
987                break;
988            }
989            let before = self.meaningful_index();
990            // Every item is a statement (var/fn/assignment) or a bare
991            // expression statement (which may be the trailing expression). The
992            // dispatch in parse_stmt covers all of these.
993            self.parse_stmt();
994            // A `;` is optional only because a newline separates just as well;
995            // one of the two (or the closing `}`) has to be there.
996            self.expect_stmt_separator();
997            // Guarantee termination on any input.
998            self.ensure_progress(before);
999        }
1000        self.expect(SyntaxKind::R_BRACE, "`}` to close block");
1001        self.finish_node();
1002    }
1003
1004    // --- expressions (Pratt climbing) ---
1005
1006    /// Entry into expression parsing at the lowest binding power.
1007    ///
1008    /// This is the *bracketed* entry: a record literal is legal here. Every
1009    /// caller that is inside `(…)`, an argument list, a block or a record body
1010    /// uses it. The four keyword heads use [`Parser::parse_expr_no_struct_lit`].
1011    fn parse_expr(&mut self) {
1012        self.parse_expr_bp(0, StructLit::Allowed);
1013    }
1014
1015    /// Entry into an expression that a `{` will terminate: an `if`/`while`
1016    /// condition, a `for` iterator, a `match` scrutinee.
1017    fn parse_expr_no_struct_lit(&mut self) {
1018        self.parse_expr_bp(0, StructLit::Suppressed);
1019    }
1020
1021    fn parse_expr_bp(&mut self, min_bp: u8, lit: StructLit) {
1022        // Capture the builder position *before* the left operand is emitted, so
1023        // a later infix operator can wrap (lhs op rhs) into a BIN_EXPR via
1024        // start_node_at. This is the standard rowan Pratt idiom.
1025        let cp = self.checkpoint_lhs();
1026        // Parse the left-hand side (prefix / atom).
1027        self.parse_prefix(lit);
1028
1029        // Fold infix operators while their binding power is high enough.
1030        loop {
1031            let op = self.peek();
1032            if op == SyntaxKind::EOF {
1033                break;
1034            }
1035            let Some(bp) = infix_binding_power(op) else {
1036                break;
1037            };
1038            if bp.left < min_bp {
1039                break;
1040            }
1041            // Wrap the already-emitted lhs + operator + rhs in a BIN_EXPR (or a
1042            // RANGE_EXPR), retroactively opening the node at the checkpoint taken
1043            // before lhs.
1044            self.start_node_at(cp, infix_node_kind(op));
1045            self.bump(); // operator
1046            // The operands of a suppressed expression are suppressed too:
1047            // `if a == p { … }` has the same ambiguity `if p { … }` has.
1048            self.parse_expr_bp(bp.right, lit);
1049            self.finish_node();
1050        }
1051    }
1052
1053    /// Prefix expression: unary operators, `read`, then an atom or a
1054    /// parenthesized expression. Followed by any postfix `expr(args)` calls
1055    /// (§4.10) — calling a closure retrieved from a collection
1056    /// (`fs.get(0)(100)`), the result of another call (`f(1)(2)`), a paren
1057    /// (`(|x| x*3)(14)`), etc.
1058    fn parse_prefix(&mut self, lit: StructLit) {
1059        // Capture the builder position before the primary is emitted, so a
1060        // postfix `expr(args)` can wrap the whole primary as the call's callee
1061        // via start_node_at (same idiom as the method-call loop).
1062        let cp = self.checkpoint_lhs();
1063        let op = self.peek();
1064        // `read parser_expression` (§7.1): a prefix expression whose body is a
1065        // parser-expression grammar, not an ordinary expression.
1066        if op == SyntaxKind::KW_READ {
1067            self.start_node(SyntaxKind::READ_EXPR);
1068            self.bump(); // `read`
1069            self.parse_parser_expr();
1070            self.finish_node();
1071        } else if op == SyntaxKind::PIPE || op == SyntaxKind::PIPE2 {
1072            // `|params| expr` closure (§4.10) — and `|| expr`, the
1073            // zero-parameter one (§4.2).
1074            //
1075            // The lexer's max-munch makes `||` one token (`PIPE2`), so the empty
1076            // parameter list and logical-or are spelled identically. The tie is
1077            // broken by **position**, the same rule `min=` and `[` use: this
1078            // function is only ever called where an expression must *begin*, and
1079            // a binary operator has no left operand there. So a `||` here is the
1080            // empty parameter list and nothing else, and a `||` between two
1081            // operands is still logical-or — the infix loop reads it, and it
1082            // never comes through here.
1083            self.parse_closure(lit);
1084        } else if let Some(bp) = prefix_binding_power(op) {
1085            self.start_node(SyntaxKind::UNARY_EXPR);
1086            self.bump(); // unary operator
1087            self.parse_expr_bp(bp, lit);
1088            self.finish_node();
1089        } else {
1090            self.parse_atom(lit);
1091        }
1092        self.parse_postfix(cp);
1093    }
1094
1095    /// The postfix chain on the expression preceding the current position:
1096    /// `expr(args)`, `expr.method(args)` and `expr.field`, in **any order and
1097    /// any number of times**, left-associatively.
1098    ///
1099    /// One loop over all three forms rather than a call loop followed by a
1100    /// field/method loop: two sequential loops cannot express `(fs).get(0)(100)`
1101    /// (a call on the result of a method call), because control never returns
1102    /// from the second loop to the first.
1103    ///
1104    /// `cp` is the checkpoint taken *before* the primary was emitted, so
1105    /// `start_node_at(cp, …)` retroactively wraps it as the new node's first
1106    /// child. It is NOT updated between iterations: each link in `a.b().c()`
1107    /// must wrap the entire preceding expression, which starts at the original
1108    /// `cp`.
1109    fn parse_postfix(&mut self, cp: rowan::Checkpoint) {
1110        loop {
1111            match self.peek() {
1112                // An argument list, and **only on the same line**. See
1113                // [`Parser::at_argument_list`] for why.
1114                SyntaxKind::L_PAREN if self.at_argument_list() => {
1115                    self.start_node_at(cp, SyntaxKind::CALL_EXPR);
1116                    self.bump(); // `(`
1117                    self.parse_arg_list();
1118                    self.finish_node(); // CALL_EXPR
1119                }
1120                // `m[key]`, `grid[x, y]` — a subscript. A postfix form like the
1121                // other two, so `grid[x, y].len()` and `m[k][j]` chain without a
1122                // second loop.
1123                //
1124                // And **only on the same line**, for the reason the `(` above is:
1125                // a `[` also begins a list literal. See [`Parser::at_subscript`].
1126                SyntaxKind::L_BRACK if self.at_subscript() => {
1127                    self.start_node_at(cp, SyntaxKind::INDEX_EXPR);
1128                    self.bump(); // `[`
1129                    // A subscript selects *something*, so unlike a call's
1130                    // argument list an empty one is a syntax error rather than an
1131                    // arity the catalog happens not to have a row for.
1132                    if self.at(SyntaxKind::R_BRACK) {
1133                        let span = self.current_span();
1134                        self.error(span, "expected an index expression");
1135                    }
1136                    self.parse_arg_list_until(SyntaxKind::R_BRACK, "`]`");
1137                    self.finish_node(); // INDEX_EXPR
1138                }
1139                SyntaxKind::DOT => {
1140                    self.bump(); // `.`
1141                    // `p.0` — a tuple element, selected by position.
1142                    // The lexer guarantees the literal is an integer here: a
1143                    // digit run immediately after a `.` takes no fraction, so
1144                    // `t.0.1` is two indices and not an index and a float.
1145                    if self.at(SyntaxKind::IntLit) {
1146                        self.start_node_at(cp, SyntaxKind::TUPLE_INDEX_EXPR);
1147                        self.bump(); // the index
1148                        self.finish_node(); // TUPLE_INDEX_EXPR
1149                        continue;
1150                    }
1151                    if !self.at(SyntaxKind::Ident) {
1152                        let span = self.current_span();
1153                        self.error(span, "expected a name or a tuple index after `.`");
1154                        break;
1155                    }
1156                    // Disambiguate field access (`p.x`) from method call
1157                    // (`p.x()`): an IDENT followed by `(` **on the same line** is
1158                    // a method call. The line break matters here for the same
1159                    // reason it does at the top of this loop — `p.x\n(a, b)` is a
1160                    // match arm body followed by a tuple pattern, not
1161                    // `p.x(a, b)`.
1162                    if self.nth_kind(1) == SyntaxKind::L_PAREN && !self.newline_before_nth(1) {
1163                        self.bump(); // method name
1164                        self.start_node_at(cp, SyntaxKind::METHOD_CALL_EXPR);
1165                        self.bump(); // `(`
1166                        self.parse_arg_list();
1167                        self.finish_node(); // METHOD_CALL_EXPR
1168                    } else {
1169                        self.start_node_at(cp, SyntaxKind::FIELD_EXPR);
1170                        self.bump(); // field name
1171                        self.finish_node(); // FIELD_EXPR
1172                    }
1173                }
1174                _ => break,
1175            }
1176        }
1177    }
1178
1179    /// The `arg, arg, …)` of a call, with the opening `(` already consumed.
1180    /// Emits the `ARG_LIST` node and consumes the closing `)`.
1181    fn parse_arg_list(&mut self) {
1182        self.parse_arg_list_until(SyntaxKind::R_PAREN, "`)`");
1183    }
1184
1185    /// The `arg, arg, …<closer>` of a call or a subscript, with the opener
1186    /// already consumed. Emits the `ARG_LIST` node and consumes `closer`.
1187    ///
1188    /// One function for both brackets: `grid[x, y]` (§6.4) is a comma-separated
1189    /// expression list with the same trailing-comma rule a call's has, and a
1190    /// second copy of a list loop is a second place for that rule to be missing.
1191    fn parse_arg_list_until(&mut self, closer: SyntaxKind, closer_msg: &str) {
1192        self.start_node(SyntaxKind::ARG_LIST);
1193        if !self.at(closer) {
1194            loop {
1195                let before = self.meaningful_index();
1196                self.parse_expr();
1197                if !self.eat(SyntaxKind::COMMA) {
1198                    break;
1199                }
1200                // A trailing comma closes the list rather than opening another
1201                // argument — §3.3's own `max(\n abs(dx),\n abs(dy),\n)` writes
1202                // one, and counting it would make the call's arity one too high.
1203                if self.at(closer) {
1204                    break;
1205                }
1206                // Guarantee termination on any input.
1207                self.ensure_progress(before);
1208            }
1209        }
1210        self.expect(closer, closer_msg);
1211        self.finish_node(); // ARG_LIST
1212    }
1213
1214    /// The `[Type, …]` type-argument list of a constructor call, with the name
1215    /// already emitted and the `[` still current.
1216    ///
1217    /// A type-argument list exists only on a constructor *call*, so the `(` after
1218    /// it is required: `Counter[Int]` alone names a type in value position, which
1219    /// no expression grammar accepts.
1220    fn parse_type_arg_list(&mut self) {
1221        self.start_node(SyntaxKind::TYPE_ARG_LIST);
1222        self.bump(); // `[`
1223        if self.at(SyntaxKind::R_BRACK) {
1224            let span = self.current_span();
1225            self.error(span, "expected a type argument");
1226        } else {
1227            loop {
1228                let before = self.meaningful_index();
1229                self.parse_type();
1230                if !self.eat(SyntaxKind::COMMA) {
1231                    break;
1232                }
1233                // A trailing comma closes the list.
1234                if self.at(SyntaxKind::R_BRACK) {
1235                    break;
1236                }
1237                // Guarantee termination on any input.
1238                self.ensure_progress(before);
1239            }
1240        }
1241        self.expect(SyntaxKind::R_BRACK, "`]`");
1242        self.finish_node(); // TYPE_ARG_LIST
1243        // The `(` is required and it is on **this** line,
1244        // so the report and what `parse_name_or_call` goes
1245        // on to build cannot disagree.
1246        if !self.at_argument_list() {
1247            let span = self.current_span();
1248            self.error(
1249                span,
1250                "`(` — a type argument list belongs to a constructor call",
1251            );
1252        }
1253    }
1254
1255    /// `|params| expr` — a closure expression (§4.10). Each parameter is a
1256    /// **pattern**, optionally annotated `: Type`, separated by commas, between two
1257    /// `|`. The body is a single expression (which may be a `{ block }`). Closures
1258    /// capture outer variables automatically (§4.10); the capture analysis is in
1259    /// HIR.
1260    ///
1261    /// A parameter is a pattern for the reason a `for` binding is one:
1262    /// destructuring in binding position **is** a pattern, so `|(a, b)| a + b`
1263    /// needs no grammar of its own.
1264    ///
1265    /// Nothing here can be confused with the body: a parameter is followed by `,`,
1266    /// `:` or `|`, never by an expression, so a record pattern's brace has nothing
1267    /// else waiting for it.
1268    ///
1269    /// The body inherits the ambient suppression rather than resetting it: `|`
1270    /// is not a bracket the grammar can close over, so a closure written
1271    /// directly as an `if` condition has the same ambiguity a name does.
1272    fn parse_closure(&mut self, lit: StructLit) {
1273        self.start_node(SyntaxKind::CLOSURE_EXPR);
1274        // `|| expr` — the zero-parameter closure. One `PIPE2` token is *both*
1275        // pipes: there is no parameter list between them to parse and no closing
1276        // `|` to demand. The token stays whole rather than being split into two,
1277        // because the tree's job is to round-trip the source and `||` is what
1278        // the source says; the node kind is what carries the meaning.
1279        if self.eat(SyntaxKind::PIPE2) {
1280            self.parse_expr_bp(0, lit);
1281            self.finish_node();
1282            return;
1283        }
1284        self.bump(); // `|`
1285        // Zero or more `pattern` or `pattern: Type` params separated by commas.
1286        if !self.at(SyntaxKind::PIPE) {
1287            loop {
1288                let before = self.meaningful_index();
1289                self.start_node(SyntaxKind::PARAM);
1290                self.parse_pattern();
1291                if self.eat(SyntaxKind::COLON) {
1292                    self.parse_type();
1293                }
1294                self.finish_node();
1295                if !self.eat(SyntaxKind::COMMA) {
1296                    break;
1297                }
1298                // A trailing comma closes the list.
1299                if self.at(SyntaxKind::PIPE) {
1300                    break;
1301                }
1302                self.ensure_progress(before);
1303            }
1304        }
1305        self.expect(SyntaxKind::PIPE, "`|` to close closure parameters");
1306        // Body: a single expression (which may be a `{ block }`).
1307        self.parse_expr_bp(0, lit);
1308        self.finish_node();
1309    }
1310
1311    /// Smallest expression: literals (including interpolated ones), names,
1312    /// calls, parenthesized expressions, list literals, blocks, and the
1313    /// keyword-headed expressions (`if`, `while`, `for`, `loop`, `match`,
1314    /// `break`, `continue`, `return`).
1315    fn parse_atom(&mut self, lit: StructLit) {
1316        let kind = self.peek();
1317        match kind {
1318            // The `LITERAL` set is `SyntaxKind::is_literal_token`: this is the
1319            // site that *builds* the node and `praxis_ast::Literal::token` is the
1320            // one that reads it back, so both must ask the same predicate.
1321            // `true`/`false` are literals too — one arm covers every literal.
1322            k if k.is_literal_token() => {
1323                self.start_node(SyntaxKind::LITERAL);
1324                self.bump();
1325                self.finish_node();
1326            }
1327            SyntaxKind::InterpOpen => self.parse_interp(),
1328            SyntaxKind::L_PAREN => self.parse_paren(),
1329            SyntaxKind::L_BRACK => self.parse_list(),
1330            // An anonymous record literal `{ x: 1, y }` (§5.6) and a block are
1331            // both a `{` here; [`Parser::at_anonymous_record_lit`] is the whole
1332            // of the tie-break.
1333            SyntaxKind::L_BRACE if self.at_anonymous_record_lit() => {
1334                let cp = self.checkpoint_lhs();
1335                self.parse_record_lit_body(cp);
1336            }
1337            SyntaxKind::L_BRACE => self.parse_block(),
1338            SyntaxKind::KW_IF => self.parse_if(),
1339            SyntaxKind::KW_WHILE => self.parse_while(),
1340            SyntaxKind::KW_FOR => self.parse_for(),
1341            SyntaxKind::KW_LOOP => self.parse_loop(),
1342            SyntaxKind::KW_BREAK => self.parse_break(),
1343            SyntaxKind::KW_CONTINUE => self.parse_continue(),
1344            SyntaxKind::KW_RETURN => self.parse_return(),
1345            SyntaxKind::KW_MATCH => self.parse_match(),
1346            SyntaxKind::Ident => self.parse_name_or_call(lit),
1347            _ => {
1348                // Nothing recognizable. CRITICAL: we must make forward progress
1349                // here — emitting a diagnostic without advancing the cursor
1350                // would let every caller loop spin forever (and balloon memory).
1351                // So consume the offending token (wrapped in a PARSE_ERROR) even
1352                // at EOF, where bump() is a no-op, we bail without consuming.
1353                if self.at_end() {
1354                    let span = self.current_span();
1355                    self.error(span, "expected an expression");
1356                } else {
1357                    self.start_node(SyntaxKind::PARSE_ERROR);
1358                    let span = self.current_span();
1359                    self.error(span, "expected an expression");
1360                    self.bump(); // guaranteed progress
1361                    self.finish_node();
1362                }
1363            }
1364        }
1365    }
1366
1367    /// An interpolated text literal: `"a{x}b{y}"` (§8.1, ADR-147).
1368    ///
1369    /// The lexer has already decided the shape — it emits an `InterpOpen`, then
1370    /// the hole's ordinary tokens, then an `InterpMiddle` for each further hole,
1371    /// then exactly one `InterpClose` — so this is a straight walk of that run
1372    /// rather than a scan of any kind. **Nothing here re-reads the source text**,
1373    /// which is the point: the hole's expression is parsed by
1374    /// [`parse_expr`](Self::parse_expr), so it is an ordinary subtree whose names
1375    /// are tokens at their own ranges, and closure capture analysis sees them
1376    /// (ADR-147 decision 1).
1377    ///
1378    /// The loop is bounded by the token stream, not by a count, and the
1379    /// `InterpClose` is `expect`ed rather than assumed: a token stream missing
1380    /// one cannot be produced by the lexer, but a parser that assumed it would
1381    /// spin at EOF instead of reporting.
1382    fn parse_interp(&mut self) {
1383        self.start_node(SyntaxKind::INTERP_EXPR);
1384        self.bump(); // `"…{`
1385        loop {
1386            self.refuse_doubled_brace();
1387            self.parse_expr(); // the hole
1388            if self.at(SyntaxKind::InterpMiddle) {
1389                self.bump(); // `}…{`
1390                continue;
1391            }
1392            break;
1393        }
1394        self.expect(SyntaxKind::InterpClose, "`}` to close the interpolation");
1395        self.finish_node();
1396    }
1397
1398    /// A hole whose expression *opens with a brace* is `{{`, and `{{` is not an
1399    /// escape in this language — ADR-147 decision 4 chose `\{` instead, so that
1400    /// `"…"` and `'…'` keep one shared escape table.
1401    ///
1402    /// Without this the doubling rule other languages use does not fail, it
1403    /// **means something else**: `{` opens the hole, `{}` is an empty block, `}`
1404    /// closes the hole, so `"a{{}}b"` is a well-typed program printing `aUnitb`,
1405    /// and `"a{{x}}b"` reports `N001` about a name the author thought they had
1406    /// escaped. A reader arriving from Rust, C# or Python writes `{{` first and
1407    /// gets no sign they were wrong, so the mistaken escape is refused where it
1408    /// is written and the message names the spelling that works.
1409    ///
1410    /// It costs a block as a hole's whole expression, which nothing wants: a
1411    /// hole is a rendering site, and `"{ var t = f(); t }"` is a sentence no one
1412    /// writes inside a string. A record literal is unaffected — that opens with
1413    /// its type's name, not with a brace.
1414    fn refuse_doubled_brace(&mut self) {
1415        if !self.at(SyntaxKind::L_BRACE) {
1416            return;
1417        }
1418        let span = self.current_span();
1419        self.error(
1420            span,
1421            "`{{` is not an escape for a literal brace: write `\\{` for a `{`, \
1422             or a value to render between single braces",
1423        );
1424    }
1425
1426    fn parse_paren(&mut self) {
1427        // Either `( expr )` (PAREN_EXPR) or `( e1, e2, … )` (TUPLE_EXPR) — the
1428        // decision, and everything that follows from it, is
1429        // `parse_parenthesized`'s.
1430        let cp = self.checkpoint_lhs();
1431        self.bump(); // `(`
1432        self.parse_parenthesized(
1433            cp,
1434            SyntaxKind::TUPLE_EXPR,
1435            SyntaxKind::PAREN_EXPR,
1436            Self::parse_expr,
1437        );
1438    }
1439
1440    /// A parenthesized list, in either position: `( x )` / `( T )` groups,
1441    /// `( a, b, … )` / `( T, U, … )` is a tuple. The caller has taken `cp`
1442    /// *before* the `(` and consumed the `(`; `element` parses one element —
1443    /// `parse_expr` in expression position, `parse_type` in type position.
1444    ///
1445    /// Which node this is cannot be known until a comma turns up after the first
1446    /// element, so the shared prefix is emitted first and the right kind is then
1447    /// opened *retroactively* at `cp`. Both `(` and `)` end up inside the single
1448    /// resulting node (no double nesting).
1449    ///
1450    /// One function for both positions so the §4.4 arity rule below is written
1451    /// once.
1452    fn parse_parenthesized(
1453        &mut self,
1454        cp: rowan::Checkpoint,
1455        tuple_kind: SyntaxKind,
1456        single_kind: SyntaxKind,
1457        mut element: impl FnMut(&mut Self),
1458    ) {
1459        if self.at(SyntaxKind::R_PAREN) {
1460            // Empty `()`, `single_kind` in both positions but for two reasons.
1461            // As an expression it is a degenerate paren expr, and it is `Unit` —
1462            // the same type `()` names in an annotation, and the value `out(())`
1463            // prints. As a type it is recorded as a plain `TYPE_REF` and left
1464            // for type resolution to reject.
1465            self.expect(SyntaxKind::R_PAREN, "`)`");
1466            self.start_node_at(cp, single_kind);
1467            self.finish_node();
1468            return;
1469        }
1470        element(self); // first element
1471        let is_tuple = self.at(SyntaxKind::COMMA);
1472        let mut elements = 1usize;
1473        let mut trailing_comma = None;
1474        if is_tuple {
1475            // Collect the remaining elements.
1476            loop {
1477                let before = self.meaningful_index();
1478                let comma = self.current_span();
1479                if !self.eat(SyntaxKind::COMMA) {
1480                    break;
1481                }
1482                if self.at(SyntaxKind::R_PAREN) {
1483                    // Trailing comma: `(a, b, )` — stop without another element.
1484                    trailing_comma = Some(comma);
1485                    break;
1486                }
1487                element(self);
1488                elements += 1;
1489                // Guarantee termination on any input.
1490                self.ensure_progress(before);
1491            }
1492        }
1493        // **A tuple has two elements or more** (§4.4). `(1,)` is the one spelling
1494        // that reaches here with fewer, and the language has nothing for it to
1495        // mean: `TupleElems` refuses to represent a one-element tuple, so typing
1496        // and lowering would have to disagree about what the node is. `(Int,)` is
1497        // the type-position spelling of the same mistake — an annotation naming a
1498        // type the language does not have.
1499        //
1500        // So the comma is refused here, and the node recovers as the grouping
1501        // the author most likely meant. `(1, 2,)` is untouched — a trailing
1502        // comma is punctuation at every arity the type exists at.
1503        let one_element_tuple = is_tuple && elements < 2;
1504        if one_element_tuple {
1505            let at = trailing_comma.unwrap_or_else(|| self.current_span());
1506            self.error(at, ONE_ELEMENT_TUPLE_MSG);
1507        }
1508        let kind = if is_tuple && !one_element_tuple {
1509            tuple_kind
1510        } else {
1511            single_kind
1512        };
1513        self.expect(SyntaxKind::R_PAREN, "`)`");
1514        self.start_node_at(cp, kind);
1515        self.finish_node();
1516    }
1517
1518    /// `[ e1, e2, … ]` — a `Vec` literal (§6.1). The opening `[` is current.
1519    ///
1520    /// One node kind for every arity, including the empty `[]`: a list is a
1521    /// collection built from its elements, so nothing about it changes at two
1522    /// the way a paren becomes a tuple at two. The element list is an `ARG_LIST`
1523    /// for the reason a subscript's is — the comma rules, the trailing comma and
1524    /// the recovery are one loop, and a second copy is a second place for a rule
1525    /// to be missing.
1526    fn parse_list(&mut self) {
1527        self.start_node(SyntaxKind::LIST_EXPR);
1528        self.bump(); // `[`
1529        // Unlike a subscript, an empty one is legal: `[]` is the empty
1530        // `Vec`, whose element type inference takes from its use.
1531        self.parse_arg_list_until(SyntaxKind::R_BRACK, "`]` to close list literal");
1532        self.finish_node(); // LIST_EXPR
1533    }
1534
1535    fn parse_if(&mut self) {
1536        self.start_node(SyntaxKind::IF_EXPR);
1537        self.bump(); // `if`
1538        // The condition is a parenthesized or bare expression; accept either.
1539        // Suppress record-literal parsing so `if x { … }` doesn't read
1540        // the then-block as `x { … }`.
1541        self.parse_expr_no_struct_lit();
1542        self.parse_block(); // then-branch
1543        if self.eat(SyntaxKind::KW_ELSE) {
1544            self.start_node(SyntaxKind::ELSE_BRANCH);
1545            if self.at(SyntaxKind::KW_IF) {
1546                self.parse_if();
1547            } else {
1548                self.parse_block();
1549            }
1550            self.finish_node();
1551        }
1552        self.finish_node();
1553    }
1554
1555    fn parse_while(&mut self) {
1556        self.start_node(SyntaxKind::WHILE_EXPR);
1557        self.bump(); // `while`
1558        self.parse_expr_no_struct_lit();
1559        self.parse_block();
1560        self.finish_node();
1561    }
1562
1563    /// `for pattern in iter { body }` (§4.11). The binding and the `in` keyword
1564    /// separate the iterator expression from the loop body.
1565    fn parse_for(&mut self) {
1566        self.start_node(SyntaxKind::FOR_EXPR);
1567        self.bump(); // `for`
1568        // The binding is a **pattern**: `for (k, v) in m` takes the
1569        // pair apart, and a bare name is the pattern that binds the
1570        // whole item. Nothing here can be confused with the loop
1571        // body — the pattern is followed by `in`, never by `{`.
1572        self.parse_pattern();
1573        self.expect(SyntaxKind::KW_IN, "`in` after the for-loop binding");
1574        self.parse_expr_no_struct_lit(); // iterator
1575        self.parse_block();
1576        self.finish_node();
1577    }
1578
1579    /// `loop { body }` (§4.11) — an explicit infinite loop, terminated by
1580    /// `break` (optionally with a value).
1581    fn parse_loop(&mut self) {
1582        self.start_node(SyntaxKind::LOOP_EXPR);
1583        self.bump(); // `loop`
1584        self.parse_block();
1585        self.finish_node();
1586    }
1587
1588    /// `break [expr]` (§4.11). The optional value is an expression; absent
1589    /// means the loop yields Unit.
1590    fn parse_break(&mut self) {
1591        self.start_node(SyntaxKind::BREAK_EXPR);
1592        self.bump(); // `break`
1593        // A value follows iff the next token starts an expression (not `;`/`}`/EOF).
1594        if self.starts_expr() {
1595            self.parse_expr();
1596        }
1597        self.finish_node();
1598    }
1599
1600    /// `continue` (§4.11).
1601    fn parse_continue(&mut self) {
1602        self.start_node(SyntaxKind::CONTINUE_EXPR);
1603        self.bump(); // `continue`
1604        self.finish_node();
1605    }
1606
1607    /// `return [expr]` (§4.11).
1608    fn parse_return(&mut self) {
1609        self.start_node(SyntaxKind::RETURN_EXPR);
1610        self.bump(); // `return`
1611        if self.starts_expr() {
1612            self.parse_expr();
1613        }
1614        self.finish_node();
1615    }
1616
1617    /// `match scrutinee { pattern => expr, … }` (§4.6, §4.11).
1618    fn parse_match(&mut self) {
1619        self.start_node(SyntaxKind::MATCH_EXPR);
1620        self.bump(); // `match`
1621        // The scrutinee is an expression; suppress record literals so the `{`
1622        // opening the arm list isn't consumed as a record body.
1623        self.parse_expr_no_struct_lit();
1624        self.expect(SyntaxKind::L_BRACE, "`{` to begin match arms");
1625        if !self.at(SyntaxKind::R_BRACE) {
1626            loop {
1627                let before = self.meaningful_index();
1628                self.start_node(SyntaxKind::MATCH_ARM);
1629                self.parse_pattern();
1630                self.expect(SyntaxKind::FAT_ARROW, "`=>` in match arm");
1631                // Arm body: a record literal is legal here. The `{` that could be
1632                // confused with a block belongs to the *match*, and it was
1633                // consumed above — inside the arm list there is nothing left for
1634                // a `Name { … }` to be mistaken for.
1635                self.parse_expr();
1636                self.finish_node(); // MATCH_ARM
1637                // Arms are comma-OR-newline separated (§4.6).
1638                let comma = self.eat(SyntaxKind::COMMA);
1639                // Stop if we hit `}` or something that can't start a pattern.
1640                if self.at(SyntaxKind::R_BRACE) || !is_pattern_start(self.peek()) {
1641                    break;
1642                }
1643                if !comma && !self.newline_before() {
1644                    let span = self.current_span();
1645                    self.error_with(
1646                        DiagCode::ExpectedStatementSeparator,
1647                        span,
1648                        "expected `,` or a line break between match arms",
1649                    );
1650                }
1651                self.ensure_progress(before);
1652            }
1653        }
1654        self.expect(SyntaxKind::R_BRACE, "`}` to end match arms");
1655        self.finish_node(); // MATCH_EXPR
1656    }
1657
1658    /// Parse a pattern (§4.6). Grammar:
1659    ///
1660    /// ```text
1661    /// pattern := "_"                                  // wildcard
1662    ///          | literal                              // `SyntaxKind::is_pattern_literal`
1663    ///          | Ident                                 // variable bind or payload-less variant
1664    ///          | Ident "(" [pattern ("," pattern)*] ")" // enum variant
1665    ///          | Ident "{" [pattern_field ("," pattern_field)*] "}" // record (§4.5)
1666    ///          | "{" pattern_field ("," pattern_field)* "}"        // headless record (ADR-091)
1667    ///          | "(" pattern ("," pattern)* ")"        // tuple (§4.4)
1668    /// pattern_field := Ident [":" pattern]
1669    /// ```
1670    ///
1671    /// A record pattern's `{` is unambiguous where a record *literal*'s is not:
1672    /// a pattern is followed by `=>` or `in`, never by a block, so nothing else
1673    /// can be waiting for that brace. That is also what makes the **head
1674    /// optional** (ADR-091 Decision 2): a leading `{` in pattern position can
1675    /// only ever open fields, so a headless record pattern needs no new token to
1676    /// tell it apart, and it pins its record from the scrutinee exactly as a
1677    /// tuple pattern does. It is the form a `choice(...)` payload record wants,
1678    /// because an anonymous record has no name a head could write.
1679    ///
1680    /// Parentheses in pattern position are **always** a tuple — there is no
1681    /// grouping form, because a pattern has no precedence to override. `(p)` is
1682    /// therefore a one-element tuple pattern, which `Y123` reports against every
1683    /// type: `TypeData::Tuple` carries two elements or more.
1684    ///
1685    /// A headless `{}` is rejected for `()`'s reason (ADR-091 Decision 3): it
1686    /// binds nothing and tests nothing against a record it cannot even name, so
1687    /// it is an irrefutable arm written by accident. The pattern that matches
1688    /// anything is spelled `_`. A *headed* `P {}` is kept — it names the record
1689    /// it tests for, so it is refutable, and it is `Some` beside `Some(_)`.
1690    fn parse_pattern(&mut self) {
1691        self.start_node(SyntaxKind::PATTERN);
1692        match self.peek() {
1693            SyntaxKind::UNDERSCORE => {
1694                self.bump(); // `_`
1695            }
1696            k if k.is_pattern_literal() => {
1697                self.bump(); // literal
1698            }
1699            // An interpolated literal is not a constant, so it is not a pattern
1700            // (§8.1, ADR-147). It is refused *here*, with the whole run consumed
1701            // into a `PARSE_ERROR`, rather than falling through to the arm below:
1702            // `match s { "{x}" => … }` would otherwise leave a `PATTERN` whose
1703            // only direct `Ident` is the hole's `x`, and `Pattern::kind` would
1704            // read that as a **variable bind** — an irrefutable arm that swallows
1705            // every value.
1706            SyntaxKind::InterpOpen => {
1707                let span = self.current_span();
1708                self.error(
1709                    span,
1710                    "an interpolated text literal is not a pattern; a pattern tests a constant",
1711                );
1712                self.start_node(SyntaxKind::PARSE_ERROR);
1713                self.parse_interp();
1714                self.finish_node();
1715            }
1716            SyntaxKind::Ident => {
1717                self.bump(); // variant name, record name, or variable bind
1718                if self.at(SyntaxKind::L_BRACE) {
1719                    // Record pattern: `Name { field, field: pat, … }`.
1720                    self.parse_record_pattern_fields();
1721                } else if self.eat(SyntaxKind::L_PAREN) {
1722                    // Enum variant with payload: `Name(pat, pat, …)`.
1723                    self.parse_pattern_list(SyntaxKind::R_PAREN);
1724                    self.expect(SyntaxKind::R_PAREN, "`)` to close variant pattern");
1725                }
1726            }
1727            SyntaxKind::L_BRACE => {
1728                // Headless record pattern `{ a, b: p }` (ADR-091). The fields
1729                // are the headed form's, unchanged — one production, so `for
1730                // {x, y} in points` and `|{x, y}| x + y` arrive with it.
1731                if self.nth_kind(1) == SyntaxKind::R_BRACE {
1732                    // `{}` binds nothing and names no record: an arm nobody can
1733                    // read as refutable. Reported where `()` is, and for the
1734                    // same reason.
1735                    let span = self.current_span();
1736                    self.error(span, "expected a pattern");
1737                }
1738                self.parse_record_pattern_fields();
1739            }
1740            SyntaxKind::L_PAREN => {
1741                // Tuple pattern `(a, b)` — or a grouping `(p)`, which the list
1742                // leaves as the one child it parsed.
1743                self.bump(); // `(`
1744                if self.at(SyntaxKind::R_PAREN) {
1745                    // `()` has no type to match: `Unit` is not a tuple.
1746                    let span = self.current_span();
1747                    self.error(span, "expected a pattern");
1748                } else {
1749                    self.parse_pattern_list(SyntaxKind::R_PAREN);
1750                }
1751                self.expect(SyntaxKind::R_PAREN, "`)` to close tuple pattern");
1752            }
1753            _ => {
1754                let span = self.current_span();
1755                self.error(span, "expected a pattern");
1756                if !self.at_end() {
1757                    self.bump();
1758                }
1759            }
1760        }
1761        self.finish_node(); // PATTERN
1762    }
1763
1764    /// A comma-separated list of patterns, up to but not including `closer`.
1765    /// A trailing comma closes the list rather than opening an element.
1766    fn parse_pattern_list(&mut self, closer: SyntaxKind) {
1767        loop {
1768            let before = self.meaningful_index();
1769            self.parse_pattern();
1770            if !self.eat(SyntaxKind::COMMA) {
1771                break;
1772            }
1773            if self.at(closer) {
1774                break;
1775            }
1776            self.ensure_progress(before);
1777        }
1778    }
1779
1780    /// The `{ field, field: pat, … }` body of a record pattern (§4.5).
1781    /// A punned field binds the field's own name; an explicit one matches the
1782    /// sub-pattern against that field.
1783    fn parse_record_pattern_fields(&mut self) {
1784        self.bump(); // `{`
1785        if !self.at(SyntaxKind::R_BRACE) {
1786            loop {
1787                let before = self.meaningful_index();
1788                self.start_node(SyntaxKind::PATTERN_FIELD);
1789                self.expect(SyntaxKind::Ident, "field name");
1790                if self.eat(SyntaxKind::COLON) {
1791                    self.parse_pattern();
1792                }
1793                self.finish_node(); // PATTERN_FIELD
1794                if !self.eat(SyntaxKind::COMMA) {
1795                    break;
1796                }
1797                // A trailing comma closes the list.
1798                if self.at(SyntaxKind::R_BRACE) {
1799                    break;
1800                }
1801                self.ensure_progress(before);
1802            }
1803        }
1804        self.expect(SyntaxKind::R_BRACE, "`}` to close record pattern");
1805    }
1806
1807    // --- types --------------------------------------------------------------
1808
1809    /// Parse a type annotation. Grammar:
1810    ///
1811    /// ```text
1812    /// type := atom_type ("->" type)?       // function types, right-assoc
1813    /// atom_type := Ident                   // scalar: Int, Text, Bool, ...
1814    ///            | "(" [type ("," type)*] ")"  // tuple (≥2) or grouped type
1815    /// ```
1816    ///
1817    /// A scalar or grouped type becomes a [`TYPE_REF`](SyntaxKind::TYPE_REF); a
1818    /// parenthesized two-or-more-element list becomes a
1819    /// [`TUPLE_TYPE`](SyntaxKind::TUPLE_TYPE); anything followed by `->` wraps in
1820    /// an [`FN_TYPE`](SyntaxKind::FN_TYPE). Unknown identifiers (e.g. a typo or a
1821    /// reserved-but-unused scalar like `Float`) parse as `TYPE_REF` and are
1822    /// rejected by name resolution (`N002`), not by the parser.
1823    fn parse_type(&mut self) {
1824        let cp = self.checkpoint_lhs();
1825        self.parse_atom_type();
1826        // Function types bind right-associatively: `A -> B -> C` = `A -> (B -> C)`.
1827        if self.eat(SyntaxKind::THIN_ARROW) {
1828            self.parse_type(); // rhs (recurses, so right-assoc)
1829            // Wrap lhs + arrow + rhs retroactively. `parse_atom_type` already
1830            // emitted exactly one node; reopening at `cp` captures it.
1831            self.start_node_at(cp, SyntaxKind::FN_TYPE);
1832            self.finish_node();
1833        }
1834        // A scalar atom is already a TYPE_REF; a tuple/group is TUPLE_TYPE; an
1835        // arrow-wrapped one is FN_TYPE. The node is on the builder.
1836    }
1837
1838    /// Parse one atomic type (no `->`). Emits exactly one node onto the builder:
1839    /// [`TYPE_REF`] for a scalar or grouped type, [`TUPLE_TYPE`] for two or more
1840    /// comma-separated elements. A scalar name followed by `[T]` or `[K, V]` is
1841    /// a collection type (§4.4) — also emitted as `TYPE_REF` with the bracketed
1842    /// args as children.
1843    fn parse_atom_type(&mut self) {
1844        if self.at(SyntaxKind::Ident) {
1845            let cp = self.checkpoint_lhs();
1846            self.eat_trivia();
1847            self.start_node(SyntaxKind::TYPE_REF);
1848            self.bump_meaningful(); // the scalar name
1849            self.finish_node();
1850            // Collection type args: `Vec[Int]`, `Map[Text, Int]`, …
1851            if self.at(SyntaxKind::L_BRACK) {
1852                self.bump(); // `[`
1853                self.start_node_at(cp, SyntaxKind::TYPE_REF);
1854                // The first type arg.
1855                self.parse_type();
1856                while self.eat(SyntaxKind::COMMA) {
1857                    // A trailing comma closes the list.
1858                    if self.at(SyntaxKind::R_BRACK) {
1859                        break;
1860                    }
1861                    self.parse_type();
1862                }
1863                self.expect(SyntaxKind::R_BRACK, "`]`");
1864                self.finish_node(); // wraps name + args into one TYPE_REF
1865            }
1866            return;
1867        }
1868        if self.at(SyntaxKind::L_PAREN) {
1869            // `( T )` (grouped) or `( T, U, … )` (tuple) — the same list, the
1870            // same arity rule and the same retroactive node as `parse_paren`'s,
1871            // over `parse_type` instead of `parse_expr`.
1872            let cp = self.checkpoint_lhs();
1873            self.bump(); // `(`
1874            self.parse_parenthesized(
1875                cp,
1876                SyntaxKind::TUPLE_TYPE,
1877                SyntaxKind::TYPE_REF,
1878                Self::parse_type,
1879            );
1880            return;
1881        }
1882        // Nothing recognizable: emit a diagnostic + a PARSE_ERROR node, but make
1883        // progress (OOM rule) by consuming the stray token if any.
1884        if self.at_end() {
1885            let span = self.current_span();
1886            self.error(span, "expected a type");
1887        } else {
1888            self.start_node(SyntaxKind::PARSE_ERROR);
1889            let span = self.current_span();
1890            self.error(span, "expected a type");
1891            self.bump();
1892            self.finish_node();
1893        }
1894    }
1895
1896    /// An identifier, possibly followed by a call `(args)` and/or `.method(args)`
1897    /// postfixes. Method calls chain left-associatively: `v.push(1).len()`.
1898    fn parse_name_or_call(&mut self, lit: StructLit) {
1899        let cp = self.checkpoint_lhs();
1900        // Peek the identifier text to special-case `parse(text, parser_expr)`
1901        // (§7.1) before committing to an ordinary call.
1902        let name_text = self.peek_text();
1903        // `parse(text, parser_expression)` (§7.1) is *syntax*, not a call of a
1904        // binding named `parse` — so the keyword must not become a `PATH_EXPR`.
1905        // Name resolution would report `` `parse` is not defined `` on every
1906        // use, and `ParseExpr::text_expr` — "the first `Expr` child" — would
1907        // answer with the keyword's own path instead of the text argument.
1908        // Decided before the node is opened, which needs one token of lookahead.
1909        if name_text == Some("parse") && self.nth_kind(1) == SyntaxKind::L_PAREN {
1910            self.start_node_at(cp, SyntaxKind::PARSE_EXPR);
1911            self.bump(); // `parse`
1912            self.bump(); // `(`
1913            self.parse_expr(); // first arg: the Text
1914            if self.eat(SyntaxKind::COMMA) {
1915                self.parse_parser_expr();
1916            }
1917            self.expect(SyntaxKind::R_PAREN, "`)` to close parse()");
1918            self.finish_node(); // PARSE_EXPR
1919            return;
1920        }
1921        // `Counter[(Int, Int)]()` — explicit type arguments on a constructor call
1922        // (§3.3). Decided from the *name* before the brackets are reached,
1923        // because the brackets themselves are a subscript's and their contents
1924        // cannot break the tie: `Int` parses as an expression too.
1925        //
1926        // Consequence, stated rather than hidden: a binding that shadows a type
1927        // constructor's name cannot be subscripted — `Counter[0]` reads as a type
1928        // argument list and then wants a `(`. That is the whole cost of the rule,
1929        // and it buys `m[k](7)` staying a call on an indexed closure.
1930        let takes_type_args = name_text.is_some_and(|t| TYPE_CONSTRUCTOR_NAMES.contains(&t))
1931            && self.nth_kind(1) == SyntaxKind::L_BRACK;
1932        self.start_node(SyntaxKind::PATH_EXPR);
1933        self.bump(); // name
1934        self.finish_node();
1935        if takes_type_args {
1936            self.parse_type_arg_list();
1937        }
1938        if self.at_argument_list() {
1939            self.bump(); // `(`
1940            self.start_node_at(cp, SyntaxKind::CALL_EXPR);
1941            // Re-open the path as the callee: rowan's checkpoint wraps the
1942            // already-emitted PATH_EXPR, so the call's first child is the path.
1943            self.parse_arg_list();
1944            self.finish_node(); // CALL_EXPR
1945        } else if self.at(SyntaxKind::L_BRACE) && lit == StructLit::Allowed {
1946            // Record literal: `Name { field: expr, … }` or `Name { x, y }` (§4.5
1947            // punning). In expression position, a bare name followed by `{` is a
1948            // record construction; the keyword heads suppress that reading (see
1949            // [`StructLit`]) so their block is not taken as a record body.
1950            self.parse_record_lit_body(cp);
1951        }
1952        // The rest of the postfix chain — `.method(args)`, `.field` and further
1953        // `(args)` calls in any order.
1954        self.parse_postfix(cp);
1955    }
1956
1957    /// Whether the `{` under the cursor opens an **anonymous record literal**
1958    /// (§5.6) rather than a block.
1959    ///
1960    /// Both are a `{` where an expression must begin, so one of the two readings
1961    /// has to be chosen from what follows. The rule is that a record literal is
1962    /// what a **block cannot be**, decided from two tokens:
1963    ///
1964    /// - `{ x: …` — a block's first statement cannot be a name followed by a
1965    ///   `:`. The one thing that looks like it is a marked statement, `{ x:bp }`,
1966    ///   and the `:bp` adjacency rule ([`Parser::is_breakpoint_marker_at`]) is
1967    ///   what separates the two. `{ x: bp }` with a space is the record literal
1968    ///   whose field is the binding `bp`, which is the same answer `min=` gives.
1969    /// - `{ x, …` — a block's first statement cannot be a name followed by a
1970    ///   `,`. This is what admits an all-punned literal, `{ x, y }`.
1971    ///
1972    /// **`{ x }` stays a block**, and that is the one case the rule cannot have
1973    /// both ways: it is a well-formed block whose value is `x` *and* a
1974    /// well-formed one-field punned literal, and blocks-as-values had the
1975    /// spelling first. Write `{ x: x }` for the record. A `{}` is likewise the
1976    /// empty block it already was — a record with no fields has no field set to
1977    /// be identified by, so nothing is lost.
1978    ///
1979    /// The [`StructLit`] suppression the four keyword heads set is deliberately
1980    /// **not** consulted. What that flag protects is `p { … }` — a *name*
1981    /// followed by the brace that could be the keyword's block (ADR-050). A `{`
1982    /// where an operand is still required is not that: `if { x: 1 } == p { … }`
1983    /// has the literal at the head of the condition, so the block the `if` is
1984    /// waiting for cannot be it.
1985    fn at_anonymous_record_lit(&mut self) -> bool {
1986        if !self.at(SyntaxKind::L_BRACE) || self.nth_kind(1) != SyntaxKind::Ident {
1987            return false;
1988        }
1989        match self.nth_kind(2) {
1990            SyntaxKind::COLON => !self
1991                .nth_index(2)
1992                .is_some_and(|colon| self.is_breakpoint_marker_at(colon)),
1993            SyntaxKind::COMMA => true,
1994            _ => false,
1995        }
1996    }
1997
1998    /// The `{ field: expr, … }` body of a record literal, opened as a
1999    /// [`RECORD_LIT_EXPR`](SyntaxKind::RECORD_LIT_EXPR) at `cp`.
2000    ///
2001    /// One production for both spellings. `cp` is what decides which: taken
2002    /// before a head name was emitted it wraps that name as the literal's
2003    /// `PATH_EXPR` child, and taken at the `{` there is no child before the
2004    /// field list — which is exactly how [`praxis_ast::RecordLitExpr::name`]
2005    /// reports an anonymous literal, and the shape a headless record *pattern*
2006    /// already has (ADR-091).
2007    fn parse_record_lit_body(&mut self, cp: rowan::Checkpoint) {
2008        self.start_node_at(cp, SyntaxKind::RECORD_LIT_EXPR);
2009        self.bump(); // `{`
2010        self.start_node(SyntaxKind::FIELD_LIST);
2011        if !self.at(SyntaxKind::R_BRACE) {
2012            loop {
2013                let before = self.meaningful_index();
2014                self.start_node(SyntaxKind::FIELD);
2015                self.expect(SyntaxKind::Ident, "field name");
2016                // Field punning (`{ x, y }`) or explicit (`{ x: expr }`).
2017                if self.eat(SyntaxKind::COLON) {
2018                    self.parse_expr();
2019                }
2020                self.finish_node();
2021                if !self.eat(SyntaxKind::COMMA) {
2022                    break;
2023                }
2024                // A trailing comma closes the list.
2025                if self.at(SyntaxKind::R_BRACE) {
2026                    break;
2027                }
2028                self.ensure_progress(before);
2029            }
2030        }
2031        self.expect(SyntaxKind::R_BRACE, "`}` to close record literal");
2032        self.finish_node(); // FIELD_LIST
2033        self.finish_node(); // RECORD_LIT_EXPR
2034    }
2035
2036    // -----------------------------------------------------------------------
2037    // Input-parser expression grammar (§7).
2038    //
2039    // `parser_expr := atom | template | call`
2040    // Whitespace and indentation outside backticks are insignificant (§7.1).
2041    // -----------------------------------------------------------------------
2042
2043    /// Parse a parser expression (§7 EBNF). Emits a `PARSER_EXPR` wrapper node
2044    /// around exactly one of: an atomic name, a backtick template, or a
2045    /// constructor call `name(args)`.
2046    fn parse_parser_expr(&mut self) {
2047        self.eat_trivia(); // whitespace outside backticks is insignificant (§7.1)
2048        let kind = self.peek();
2049        match kind {
2050            // An unterminated run is still *shaped* like a template, and the
2051            // lexer has already reported it (T002, ADR-094). Taking it here
2052            // rather than falling through to "expected a parser expression"
2053            // is what keeps one typo to one error: the alternative is a P001
2054            // and then an I000 about an interior nobody wrote.
2055            SyntaxKind::BacktickTemplate | SyntaxKind::UnterminatedBacktickTemplate => {
2056                self.parse_parser_template()
2057            }
2058            SyntaxKind::Ident => {
2059                // An identifier is either an atomic parser (`int`, `char`, …)
2060                // or a constructor call (`lines(P)`, `sep(s, P)`). Decide by the
2061                // presence of `(`.
2062                if self.nth_kind(1) == SyntaxKind::L_PAREN {
2063                    self.parse_parser_call();
2064                } else {
2065                    self.parse_parser_atom();
2066                }
2067            }
2068            _ => {
2069                // Nothing recognizable as a parser expression.
2070                let span = self.current_span();
2071                self.error(span, "expected a parser expression");
2072                self.start_node(SyntaxKind::PARSER_EXPR);
2073                self.start_node(SyntaxKind::PARSE_ERROR);
2074                if !self.at_end() {
2075                    self.bump(); // guaranteed progress
2076                }
2077                self.finish_node(); // PARSE_ERROR
2078                self.finish_node(); // PARSER_EXPR
2079            }
2080        }
2081    }
2082
2083    /// Parse an atomic parser name: `int`, `char`, `word`, `text`, `rest`,
2084    /// `digit` (§7.4). The identifier is wrapped in `PARSER_EXPR > PARSER_ATOM`.
2085    fn parse_parser_atom(&mut self) {
2086        self.start_node(SyntaxKind::PARSER_EXPR);
2087        self.start_node(SyntaxKind::PARSER_ATOM);
2088        self.bump(); // the atomic name
2089        self.finish_node(); // PARSER_ATOM
2090        self.finish_node(); // PARSER_EXPR
2091    }
2092
2093    /// Parse a backtick template as a parser expression (§7.2). The whole
2094    /// `BacktickTemplate` token is emitted as a `PARSER_TEMPLATE` child; its
2095    /// interior is re-scanned by `praxis-input-parser` later (in HIR). The
2096    /// template node is wrapped in `PARSER_EXPR`.
2097    fn parse_parser_template(&mut self) {
2098        self.start_node(SyntaxKind::PARSER_EXPR);
2099        self.start_node(SyntaxKind::PARSER_TEMPLATE);
2100        self.bump(); // the BacktickTemplate token (interior re-scanned in HIR)
2101        self.finish_node(); // PARSER_TEMPLATE
2102        self.finish_node(); // PARSER_EXPR
2103    }
2104
2105    /// Parse a constructor call `name(args)` (§7.5). Emits
2106    /// `PARSER_EXPR > PARSER_CALL > PATH_EXPR + PARSER_ARG_LIST`. Each argument
2107    /// is one of:
2108    /// - a positional parser expression (`lines(int)` → child `int`);
2109    /// - a positional literal, emitted as a `LITERAL` node: a string (the
2110    ///   separator for `sep`, the set for `one_of`) or a whole number (the
2111    ///   count of `repeated(P, N)`, §7.5). **Which constructors accept which
2112    ///   literal is not the grammar's question** — it is
2113    ///   `Constructor::arg_shape`'s, exactly as `Constructor::keyword_arg`
2114    ///   owns the keyword question. A count written where no count belongs
2115    ///   earns an argument diagnostic naming the constructor, which says more
2116    ///   than "expected a parser expression" does;
2117    /// - a named argument `name: parser_expr` (§7.5), emitted as a
2118    ///   `PARSER_NAMED_ARG` node — used by heterogeneous `sections`
2119    ///   (`rules: lines(...)`), `chars`/`grid` keyword args (`skip: whitespace`,
2120    ///   `fill: value`), and the `repeated(...)` tail marker of `sections`.
2121    fn parse_parser_call(&mut self) {
2122        self.start_node(SyntaxKind::PARSER_EXPR);
2123        self.start_node(SyntaxKind::PARSER_CALL);
2124        // The constructor name as a path.
2125        self.start_node(SyntaxKind::PATH_EXPR);
2126        self.bump(); // constructor name
2127        self.finish_node();
2128        // Argument list.
2129        self.expect(SyntaxKind::L_PAREN, "`(` to open parser call arguments");
2130        self.start_node(SyntaxKind::PARSER_ARG_LIST);
2131        if !self.at(SyntaxKind::R_PAREN) {
2132            loop {
2133                self.eat_trivia();
2134                if self.at(SyntaxKind::TextLit) || self.at(SyntaxKind::IntLit) {
2135                    // A positional literal: `sep`'s separator, or the count of
2136                    // `repeated(P, N)`.
2137                    self.start_node(SyntaxKind::LITERAL);
2138                    self.bump();
2139                    self.finish_node();
2140                } else if self.at(SyntaxKind::MINUS) && self.nth_kind(1) == SyntaxKind::IntLit {
2141                    // A negative count is still a *count*, and it belongs in one
2142                    // `LITERAL` node so the shape check can say what is wrong
2143                    // with it. Left to `parse_parser_expr`, `repeated(P, -1)`
2144                    // would report "expected a parser expression" at the `-` —
2145                    // a complaint about the wrong thing entirely.
2146                    self.start_node(SyntaxKind::LITERAL);
2147                    self.bump(); // `-`
2148                    self.bump(); // the digits
2149                    self.finish_node();
2150                } else if self.at(SyntaxKind::Ident) && self.nth_kind(1) == SyntaxKind::COLON {
2151                    // A named argument `name: parser_expr`. The name is a bare
2152                    // ident followed by `:`. This does not conflict with a
2153                    // constructor call (`lines(...)`) because that has `(` at
2154                    // position 1, not `:`.
2155                    self.start_node(SyntaxKind::PARSER_NAMED_ARG);
2156                    self.bump(); // the name ident
2157                    self.eat_trivia();
2158                    self.expect(SyntaxKind::COLON, "`:` after a named argument");
2159                    self.eat_trivia();
2160                    self.parse_parser_named_arg_value();
2161                    self.finish_node(); // PARSER_NAMED_ARG
2162                } else {
2163                    self.parse_parser_expr();
2164                }
2165                self.eat_trivia();
2166                if !self.eat(SyntaxKind::COMMA) {
2167                    break;
2168                }
2169                // Allow a trailing comma: if the next token is `)`, stop.
2170                self.eat_trivia();
2171                if self.at(SyntaxKind::R_PAREN) {
2172                    break;
2173                }
2174            }
2175        }
2176        self.expect(SyntaxKind::R_PAREN, "`)` to close parser call arguments");
2177        self.finish_node(); // PARSER_ARG_LIST
2178        self.finish_node(); // PARSER_CALL
2179        self.finish_node(); // PARSER_EXPR
2180    }
2181
2182    /// Parse the value after `name:` in a parser call's argument list.
2183    ///
2184    /// Two shapes live here, and only the grammar can tell them apart by
2185    /// looking:
2186    /// - a **literal** (`fill: 0`, `fill: "-"`) — the value of a keyword
2187    ///   argument, which is not a parser expression at all. It becomes a
2188    ///   `PARSER_KEYWORD_VALUE` node holding the raw token, and whether the
2189    ///   constructor actually has a keyword argument of that name is decided
2190    ///   later, by `Constructor::keyword_arg`, where that rule already lives.
2191    /// - anything else (`rules: lines(int)`, `skip: whitespace`) — a parser
2192    ///   expression.
2193    fn parse_parser_named_arg_value(&mut self) {
2194        if matches!(
2195            self.peek(),
2196            SyntaxKind::IntLit | SyntaxKind::FloatLit | SyntaxKind::TextLit
2197        ) {
2198            self.start_node(SyntaxKind::PARSER_KEYWORD_VALUE);
2199            self.bump(); // the literal token
2200            self.finish_node();
2201        } else {
2202            self.parse_parser_expr();
2203        }
2204    }
2205
2206    // -----------------------------------------------------------------------
2207    // Error recovery.
2208    // -----------------------------------------------------------------------
2209
2210    /// Expect a **binding position**: a name, or `_` for one the program is
2211    /// deliberately not naming (D7, ADR-049). Reports at the current token if
2212    /// neither is there.
2213    ///
2214    /// `var _ = f()`, `fn g(_)` and `|_| 0` are legal and introduce nothing —
2215    /// the AST's name accessors look for an `Ident`, so a wildcard binder is an
2216    /// absent name all the way down rather than a symbol called `_`.
2217    fn expect_binder(&mut self, what: &str) -> bool {
2218        if self.at(SyntaxKind::UNDERSCORE) {
2219            self.bump();
2220            return true;
2221        }
2222        self.expect(SyntaxKind::Ident, what)
2223    }
2224
2225    fn expect(&mut self, kind: SyntaxKind, what: &str) -> bool {
2226        if self.at(kind) {
2227            self.bump();
2228            true
2229        } else {
2230            let span = self.current_span();
2231            let spelling = self.current_spelling();
2232            self.error(span, format!("expected {what}, found {spelling}"));
2233            false
2234        }
2235    }
2236
2237    /// The span of the current meaningful token (or a zero-width span at EOF).
2238    fn current_span(&mut self) -> Span {
2239        self.eat_trivia();
2240        if self.cursor < self.tokens.len() {
2241            self.tokens[self.cursor].span
2242        } else {
2243            Span::at(BytePos::ZERO)
2244        }
2245    }
2246
2247    /// A human-readable spelling of the current token, for diagnostics.
2248    fn current_spelling(&mut self) -> &'static str {
2249        match self.peek() {
2250            SyntaxKind::EOF => "end of file",
2251            _ => "unexpected token",
2252        }
2253    }
2254
2255    /// Skip tokens until a plausible statement boundary (a brace, a statement
2256    /// keyword, or EOF), wrapping skipped tokens in a `PARSE_ERROR` node.
2257    fn recover_to_stmt_boundary(&mut self) {
2258        self.start_node(SyntaxKind::PARSE_ERROR);
2259        let span = self.current_span();
2260        self.error(span, "unexpected token, skipping to recover");
2261        while !self.at_end()
2262            && !self.at(SyntaxKind::R_BRACE)
2263            && !matches!(
2264                self.peek(),
2265                SyntaxKind::KW_VAR
2266                    | SyntaxKind::KW_FN
2267                    | SyntaxKind::KW_IF
2268                    | SyntaxKind::KW_WHILE
2269                    | SyntaxKind::KW_RETURN
2270            )
2271        {
2272            self.bump();
2273        }
2274        self.finish_node();
2275    }
2276
2277    /// The builder checkpoint *before* the current operand was emitted.
2278    ///
2279    /// In a single-pass builder the checkpoint must be captured before the
2280    /// children are emitted, so we stash one at the start of each prefix/atom.
2281    ///
2282    /// The trivia in front of the operand is emitted **first**, for
2283    /// [`start_node`](Self::start_node)'s reason and by the same rule: a node
2284    /// retroactively opened here — a `BIN_EXPR`, a `RANGE_EXPR`, a `CALL_EXPR`,
2285    /// a parenthesized expression — would otherwise begin at a checkpoint taken
2286    /// before the whitespace and swallow it.
2287    fn checkpoint_lhs(&mut self) -> rowan::Checkpoint {
2288        self.eat_trivia();
2289        self.builder.checkpoint()
2290    }
2291}
2292
2293#[cfg(test)]
2294mod tests {
2295    use super::*;
2296    use praxis_source::{DiagnosticCategory, SourceMap};
2297
2298    fn parse_text(text: &str) -> ParseOutput {
2299        let map = SourceMap::new();
2300        let id = map.intern("test.px", text);
2301        parse(id, text)
2302    }
2303
2304    fn dump(text: &str) -> String {
2305        praxis_test_support::format_syntax_tree(&parse_text(text).tree)
2306    }
2307
2308    #[test]
2309    fn parses_var_binding() {
2310        let out = parse_text("var x = 1");
2311        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
2312        insta::assert_snapshot!(dump("var x = 1"), @r#"
2313        SOURCE_FILE@0..9
2314          VAR_STMT@0..9
2315            KW_VAR "var"@0..3
2316            Whitespace " "@3..4
2317            Ident "x"@4..5
2318            Whitespace " "@5..6
2319            EQ "="@6..7
2320            Whitespace " "@7..8
2321            LITERAL@8..9
2322              IntLit "1"@8..9
2323        "#);
2324    }
2325
2326    #[test]
2327    fn parses_var_binding_and_reassignment() {
2328        let out = parse_text("var score = 0");
2329        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
2330        let kinds = construct_names(&out.tree);
2331        assert!(kinds.contains(&SyntaxKind::VAR_STMT));
2332    }
2333
2334    #[test]
2335    fn parses_function_definition() {
2336        let src = "fn add(a: Int, b: Int) -> Int { a + b }";
2337        let out = parse_text(src);
2338        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
2339        let kinds = construct_names(&out.tree);
2340        assert!(kinds.contains(&SyntaxKind::FN_ITEM));
2341        assert!(kinds.contains(&SyntaxKind::PARAM_LIST));
2342        assert!(kinds.contains(&SyntaxKind::PARAM));
2343        assert!(kinds.contains(&SyntaxKind::BLOCK_EXPR));
2344        assert!(kinds.contains(&SyntaxKind::BIN_EXPR));
2345    }
2346
2347    #[test]
2348    fn parses_out_call() {
2349        let out = parse_text("out(42)");
2350        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
2351        let kinds = construct_names(&out.tree);
2352        assert!(kinds.contains(&SyntaxKind::CALL_EXPR));
2353        assert!(kinds.contains(&SyntaxKind::ARG_LIST));
2354    }
2355
2356    #[test]
2357    fn parses_if_else() {
2358        let src = "if x { out(1) } else { out(2) }";
2359        let out = parse_text(src);
2360        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
2361        let kinds = construct_names(&out.tree);
2362        assert!(kinds.contains(&SyntaxKind::IF_EXPR));
2363        assert!(kinds.contains(&SyntaxKind::ELSE_BRANCH));
2364    }
2365
2366    #[test]
2367    fn parses_while_loop() {
2368        let out = parse_text("while x < 10 { x = x + 1 }");
2369        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
2370        let kinds = construct_names(&out.tree);
2371        assert!(kinds.contains(&SyntaxKind::WHILE_EXPR));
2372    }
2373
2374    #[test]
2375    fn parses_block_expression() {
2376        let out = parse_text("{ var a = 1\n a }");
2377        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
2378        let kinds = construct_names(&out.tree);
2379        assert!(kinds.contains(&SyntaxKind::BLOCK_EXPR));
2380        assert!(kinds.contains(&SyntaxKind::VAR_STMT));
2381        assert!(kinds.contains(&SyntaxKind::EXPR_STMT));
2382    }
2383
2384    #[test]
2385    fn parses_text_literal() {
2386        let out = parse_text(r#"out("hello")"#);
2387        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
2388        let kinds = construct_names(&out.tree);
2389        assert!(kinds.contains(&SyntaxKind::TextLit));
2390    }
2391
2392    /// A `'#'` in expression position is a `LITERAL` holding one `CharLit`, and
2393    /// the node spans exactly the literal — no leading trivia.
2394    #[test]
2395    fn parses_char_literal() {
2396        let src = "var c = '#'";
2397        let out = parse_text(src);
2398        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
2399        let lit = out
2400            .tree
2401            .descendants()
2402            .find(|n| n.kind() == SyntaxKind::LITERAL)
2403            .expect("a LITERAL node");
2404        assert_eq!(lit.text().to_string(), "'#'");
2405        assert!(construct_names(&out.tree).contains(&SyntaxKind::CharLit));
2406    }
2407
2408    // --- string interpolation (§8.1, ADR-147) -------------------------------
2409
2410    /// An interpolated literal is an `INTERP_EXPR`, and the hole's expression is
2411    /// an ordinary subtree under it — a `PATH_EXPR` here, a `BIN_EXPR` below.
2412    /// That is what everything else in the compiler reads it through.
2413    #[test]
2414    fn parses_an_interpolated_literal() {
2415        let out = parse_text(r#"out("Part 2: {part2}")"#);
2416        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
2417        let kinds = construct_names(&out.tree);
2418        assert!(kinds.contains(&SyntaxKind::INTERP_EXPR));
2419        assert!(kinds.contains(&SyntaxKind::PATH_EXPR));
2420        assert!(kinds.contains(&SyntaxKind::InterpOpen));
2421        assert!(kinds.contains(&SyntaxKind::InterpClose));
2422        // And it is *not* a LITERAL: a LITERAL is a leaf whose value comes off a
2423        // token, which an interpolated literal's does not.
2424        assert!(!kinds.contains(&SyntaxKind::TextLit));
2425    }
2426
2427    /// A hole holds a full expression (ADR-147 decision 1), so the shapes below
2428    /// each land as the node they would be anywhere else.
2429    #[test]
2430    fn a_hole_holds_a_full_expression() {
2431        for (src, expected) in [
2432            (r#"out("{a + b}")"#, SyntaxKind::BIN_EXPR),
2433            (r#"out("{p.0}")"#, SyntaxKind::TUPLE_INDEX_EXPR),
2434            (r#"out("{xs.len()}")"#, SyntaxKind::METHOD_CALL_EXPR),
2435            (r#"out("{m["k"]}")"#, SyntaxKind::INDEX_EXPR),
2436            (r#"out("{if c { 1 } else { 2 }}")"#, SyntaxKind::IF_EXPR),
2437        ] {
2438            let out = parse_text(src);
2439            assert!(out.diagnostics.is_empty(), "{src}: {:?}", out.diagnostics);
2440            assert!(construct_names(&out.tree).contains(&expected), "{src}");
2441        }
2442    }
2443
2444    /// Several holes are one node with one `InterpClose`, and the fragments
2445    /// alternate with the holes.
2446    #[test]
2447    fn several_holes_are_one_node() {
2448        let out = parse_text(r#"out("{a} and {b} and {c}")"#);
2449        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
2450        let kinds = construct_names(&out.tree);
2451        assert_eq!(
2452            kinds
2453                .iter()
2454                .filter(|k| **k == SyntaxKind::INTERP_EXPR)
2455                .count(),
2456            1
2457        );
2458        assert_eq!(
2459            kinds
2460                .iter()
2461                .filter(|k| **k == SyntaxKind::InterpMiddle)
2462                .count(),
2463            2
2464        );
2465    }
2466
2467    /// Postfix operators apply to the whole literal, not to the last hole: an
2468    /// `INTERP_EXPR` is an atom like any other.
2469    #[test]
2470    fn an_interpolated_literal_takes_postfix_operators() {
2471        let out = parse_text(r#"var n = "{a}b".len()"#);
2472        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
2473        assert!(construct_names(&out.tree).contains(&SyntaxKind::METHOD_CALL_EXPR));
2474    }
2475
2476    /// **An interpolated literal is not a pattern**, and refusing it is not
2477    /// pedantry: without the arm that reports it, `match s { "{x}" => … }`
2478    /// leaves a `PATTERN` whose only direct `Ident` is the hole's `x`, which
2479    /// `Pattern::kind` reads as a variable bind — an irrefutable arm that
2480    /// swallows every value with no diagnostic at all. That is why this asserts
2481    /// the diagnostic rather than the tree shape.
2482    #[test]
2483    fn an_interpolated_literal_is_not_a_pattern() {
2484        let out = parse_text("var r = match s {\n    \"{x}\" => 1\n    _ => 2\n}");
2485        assert!(
2486            !out.diagnostics.is_empty(),
2487            "an interpolated literal in pattern position must be reported"
2488        );
2489        assert!(
2490            out.diagnostics
2491                .iter()
2492                .any(|d| d.message().contains("not a pattern")),
2493            "{:?}",
2494            out.diagnostics
2495        );
2496    }
2497
2498    /// `{{` is the escape in Rust, C# and Python, so it is the first thing a
2499    /// reader tries — and here it would otherwise *parse*: `{` opens the hole,
2500    /// `{}` is an empty block, `}` closes it, and `"a{{}}b"` prints `aUnitb`
2501    /// with no diagnostic. The refusal is what keeps that from being silent.
2502    #[test]
2503    fn a_doubled_brace_is_refused_rather_than_meaning_a_block() {
2504        for src in [
2505            "out(\"a{{}}b\")",
2506            "out(\"a{{x}}b\")",
2507            "out(\"{{}}\")",
2508            "out(\"ok {n} then a{{}}b\")",
2509        ] {
2510            let out = parse_text(src);
2511            assert!(
2512                out.diagnostics
2513                    .iter()
2514                    .any(|d| d.message().contains("is not an escape for a literal brace")),
2515                "`{src}` must be refused, got {:?}",
2516                out.diagnostics
2517            );
2518        }
2519        // The spelling that works, and an ordinary hole, are both untouched.
2520        for src in ["out(\"a\\{\\}b\")", "out(\"a{n}b\")", "out(\"{a + b}\")"] {
2521            let out = parse_text(src);
2522            assert!(
2523                out.diagnostics.is_empty(),
2524                "`{src}` must parse cleanly, got {:?}",
2525                out.diagnostics
2526            );
2527        }
2528    }
2529
2530    /// **`is_pattern_start`'s half of ADR-141.** Leave `CharLit` out of it and
2531    /// the arm list stops after `'#' => …`: the second and third arms leave the
2532    /// tree with no diagnostic at all, and the literal still looks like it
2533    /// works.
2534    #[test]
2535    fn a_char_arm_does_not_truncate_the_arm_list() {
2536        let src = "var r = match c {\n    '#' => 1\n    '.' => 2\n    _ => 3\n}";
2537        let out = parse_text(src);
2538        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
2539        let arms = construct_names(&out.tree)
2540            .into_iter()
2541            .filter(|k| *k == SyntaxKind::MATCH_ARM)
2542            .count();
2543        assert_eq!(arms, 3);
2544    }
2545
2546    // --- statement separation and postfix chains ---------------------------
2547
2548    #[test]
2549    fn regression_same_line_statements_require_a_semicolon() {
2550        let out = parse_text("var a = 1 var b = 2");
2551        assert!(
2552            !out.diagnostics.is_empty(),
2553            "two statements on one line must not parse as if a separator existed"
2554        );
2555    }
2556
2557    #[test]
2558    fn regression_semicolons_separate_top_level_statements() {
2559        let out = parse_text("var a = 1; var b = 2");
2560        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
2561        assert_eq!(
2562            construct_names(&out.tree)
2563                .iter()
2564                .filter(|kind| **kind == SyntaxKind::VAR_STMT)
2565                .count(),
2566            2
2567        );
2568    }
2569
2570    #[test]
2571    fn regression_newline_terminates_a_bare_return() {
2572        let out = parse_text("fn f() { return\n1 }");
2573        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
2574        let return_expr = out
2575            .tree
2576            .descendants()
2577            .find(|node| node.kind() == SyntaxKind::RETURN_EXPR)
2578            .expect("return expression");
2579        assert_eq!(
2580            return_expr.children().count(),
2581            0,
2582            "the next line must be a separate expression, not return's value"
2583        );
2584    }
2585
2586    #[test]
2587    fn regression_postfix_forms_may_be_interleaved() {
2588        let out = parse_text("(fs).get(0)(100)");
2589        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
2590        let kinds = construct_names(&out.tree);
2591        assert_eq!(
2592            kinds
2593                .iter()
2594                .filter(|kind| **kind == SyntaxKind::CALL_EXPR)
2595                .count(),
2596            1,
2597            "the final `(100)` must call the result of `.get(0)`"
2598        );
2599        assert_eq!(
2600            out.tree.children().count(),
2601            1,
2602            "the postfix call must remain part of the same expression statement"
2603        );
2604    }
2605
2606    // --- where a newline ends a statement, and where it does not -----------
2607
2608    /// D8's second half, stated for the operator it is most likely to break.
2609    /// A newline is consulted between statements and at `break`/`return`'s
2610    /// optional-value decision — never inside the Pratt loop.
2611    #[test]
2612    fn an_operator_continues_across_a_line_break() {
2613        let out = parse_text("var a = 1 +\n2");
2614        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
2615        let kinds = construct_names(&out.tree);
2616        assert_eq!(
2617            kinds
2618                .iter()
2619                .filter(|kind| **kind == SyntaxKind::VAR_STMT)
2620                .count(),
2621            1,
2622            "`1 +` and `2` are one addition, not two statements"
2623        );
2624        assert!(kinds.contains(&SyntaxKind::BIN_EXPR));
2625    }
2626
2627    /// **A node never begins with trivia**, so an expression's `text_range` is
2628    /// the expression and a caret under it underlines only that.
2629    ///
2630    /// Asserted as an invariant over whole trees rather than as another
2631    /// snapshot: `start_node` has dozens of call sites and a snapshot pins the
2632    /// handful a fixture happens to reach. The exception is the **root**, which
2633    /// is where a file's leading trivia has to go — there is no node before it.
2634    #[test]
2635    fn a_node_never_begins_with_trivia() {
2636        for src in [
2637            "var c = a + b",
2638            "  var c = ( a ) + 1  ",
2639            "// a leading comment\nvar t = true && false",
2640            "fn f(x) -> Int {\n    var y = -x\n    y * 2\n}",
2641            "var v = read lines( `{a:int},{b:int}` )",
2642            "var m = match t {\n    A => 1\n    _ => 2\n}",
2643            "for i in 0..n {\n    out( i )\n}",
2644            "var p = Point { x: 1, y: 2 }",
2645            "var f = |a, b| a + b\nvar g = f ( 1 , 2 )",
2646            "var s = 0\ns += grid [ 1 , 2 ]",
2647            // Recovery paths open nodes too, and `PARSE_ERROR` is a node.
2648            "var @ = 1\nvar ok = 2",
2649            "var x = (",
2650        ] {
2651            let tree = parse_text(src).tree;
2652            for node in tree.descendants() {
2653                if node.kind() == SyntaxKind::SOURCE_FILE {
2654                    continue;
2655                }
2656                let Some(first) = node.first_token() else {
2657                    continue;
2658                };
2659                assert!(
2660                    !first.kind().is_trivia(),
2661                    "{:?}@{:?} begins with {:?} in {src:?}\n{}",
2662                    node.kind(),
2663                    node.text_range(),
2664                    first.kind(),
2665                    praxis_test_support::format_syntax_tree(&tree),
2666                );
2667            }
2668        }
2669    }
2670
2671    /// The same rule stated directly: an operand's range is the operand, so
2672    /// `lhs.syntax().text_range()` is what a diagnostic can point at.
2673    #[test]
2674    fn an_operands_range_is_the_operand_and_not_the_space_before_it() {
2675        // `var c = a + b` — `a` at 8..9, `b` at 12..13.
2676        let tree = parse_text("var c = a + b").tree;
2677        let bin = tree
2678            .descendants()
2679            .find(|n| n.kind() == SyntaxKind::BIN_EXPR)
2680            .expect("a BIN_EXPR");
2681        assert_eq!(
2682            bin.text_range(),
2683            rowan::TextRange::new(8.into(), 13.into()),
2684            "the binary expression is `a + b`, not `= a + b`"
2685        );
2686        let operands: Vec<_> = bin
2687            .children()
2688            .filter(|n| n.kind() == SyntaxKind::PATH_EXPR)
2689            .map(|n| n.text_range())
2690            .collect();
2691        assert_eq!(
2692            operands,
2693            vec![
2694                rowan::TextRange::new(8.into(), 9.into()),
2695                rowan::TextRange::new(12.into(), 13.into()),
2696            ],
2697            "each operand's range is one character wide"
2698        );
2699    }
2700
2701    /// `..`/`..=` is an infix operator that builds a `RANGE_EXPR` (ADR-059), and
2702    /// it binds **looser than arithmetic and comparison**: every range in the
2703    /// corpus writes an arithmetic bound, so `0..n - 1` has to be `0..(n - 1)`.
2704    #[test]
2705    fn a_range_binds_looser_than_the_arithmetic_in_its_bounds() {
2706        // The bound is the whole subtraction, so the RANGE_EXPR contains a
2707        // BIN_EXPR rather than the other way round.
2708        insta::assert_snapshot!(dump("var r = 0..n - 1"), @r#"
2709        SOURCE_FILE@0..16
2710          VAR_STMT@0..16
2711            KW_VAR "var"@0..3
2712            Whitespace " "@3..4
2713            Ident "r"@4..5
2714            Whitespace " "@5..6
2715            EQ "="@6..7
2716            Whitespace " "@7..8
2717            RANGE_EXPR@8..16
2718              LITERAL@8..9
2719                IntLit "0"@8..9
2720              DOT2 ".."@9..11
2721              BIN_EXPR@11..16
2722                PATH_EXPR@11..12
2723                  Ident "n"@11..12
2724                Whitespace " "@12..13
2725                MINUS "-"@13..14
2726                Whitespace " "@14..15
2727                LITERAL@15..16
2728                  IntLit "1"@15..16
2729        "#);
2730        // …and looser than comparison too, so `a..b == c..d` compares two ranges.
2731        let out = parse_text("var b = 1..2 == 3..4");
2732        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
2733        let kinds = construct_names(&out.tree);
2734        assert_eq!(
2735            kinds
2736                .iter()
2737                .filter(|k| **k == SyntaxKind::RANGE_EXPR)
2738                .count(),
2739            2,
2740            "two ranges, one comparison — not a range over a Bool"
2741        );
2742        assert_eq!(
2743            kinds.iter().filter(|k| **k == SyntaxKind::BIN_EXPR).count(),
2744            1
2745        );
2746    }
2747
2748    /// `..=` is its own token in the same position, and the *node* is the same
2749    /// kind — the difference is the operator token, which is where
2750    /// `RangeExpr::is_inclusive` reads it from.
2751    #[test]
2752    fn an_inclusive_range_is_the_same_node_with_a_different_operator() {
2753        let out = parse_text("var r = 0..=9");
2754        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
2755        let kinds = construct_names(&out.tree);
2756        assert!(kinds.contains(&SyntaxKind::RANGE_EXPR));
2757        assert!(kinds.contains(&SyntaxKind::DOT2EQ));
2758        assert!(!kinds.contains(&SyntaxKind::DOT2));
2759    }
2760
2761    /// A range is an ordinary expression, so it appears wherever one may: a
2762    /// `for` header (where the `{` must not be read as a record literal), a
2763    /// call argument, and a parenthesized bound.
2764    #[test]
2765    fn a_range_is_legal_wherever_an_expression_is() {
2766        for src in [
2767            "for i in 0..n { out(i) }",
2768            "for i in 0..=n { out(i) }",
2769            "var r = (0 - 1)..(n + 1)",
2770            "out(0..3)",
2771            // The bound may itself be a call or a method call.
2772            "var r = 0..v.len()",
2773            "var r = abs(0 - 3)..max(1, 2)",
2774        ] {
2775            let out = parse_text(src);
2776            assert!(out.diagnostics.is_empty(), "{src:?}: {:?}", out.diagnostics);
2777            assert!(
2778                construct_names(&out.tree).contains(&SyntaxKind::RANGE_EXPR),
2779                "{src:?} produced no RANGE_EXPR"
2780            );
2781        }
2782        // `Range` in *type* position is a type name, not a range expression —
2783        // the annotation form D6 makes a first-class value worth having.
2784        let out = parse_text("fn f(r: Range) -> Range { r }");
2785        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
2786        assert!(!construct_names(&out.tree).contains(&SyntaxKind::RANGE_EXPR));
2787    }
2788
2789    /// D8's rule, applied to `..`: a newline after it continues the expression,
2790    /// exactly as one after `+` does. The Pratt loop never consults
2791    /// `newline_before`, and this is the test that says a range is not the
2792    /// exception.
2793    #[test]
2794    fn a_range_continues_across_a_line_break() {
2795        let out = parse_text("var r = 1 ..\n5");
2796        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
2797        let kinds = construct_names(&out.tree);
2798        assert_eq!(
2799            kinds
2800                .iter()
2801                .filter(|kind| **kind == SyntaxKind::VAR_STMT)
2802                .count(),
2803            1,
2804            "`1 ..` and `5` are one range, not two statements"
2805        );
2806        assert!(kinds.contains(&SyntaxKind::RANGE_EXPR));
2807    }
2808
2809    /// …and so does a postfix chain: the line break before `.len()` is inside an
2810    /// expression, so it terminates nothing.
2811    #[test]
2812    fn a_method_chain_continues_across_a_line_break() {
2813        let out = parse_text("var n = v\n  .len()");
2814        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
2815        assert!(construct_names(&out.tree).contains(&SyntaxKind::METHOD_CALL_EXPR));
2816    }
2817
2818    /// `break`'s half of the optional-value rule;
2819    /// `regression_newline_terminates_a_bare_return` covers `return`.
2820    #[test]
2821    fn a_newline_terminates_a_bare_break() {
2822        let out = parse_text("loop { break\n1 }");
2823        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
2824        let break_expr = out
2825            .tree
2826            .descendants()
2827            .find(|node| node.kind() == SyntaxKind::BREAK_EXPR)
2828            .expect("break expression");
2829        assert_eq!(break_expr.children().count(), 0);
2830    }
2831
2832    /// A `;` separates statements *inside* a block — it is one of the two things
2833    /// that can, the other being a line break.
2834    #[test]
2835    fn a_semicolon_separates_two_statements_on_one_line_in_a_block() {
2836        let out = parse_text("fn f() { var a = 1; var b = 2 }");
2837        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
2838        assert_eq!(
2839            construct_names(&out.tree)
2840                .iter()
2841                .filter(|kind| **kind == SyntaxKind::VAR_STMT)
2842                .count(),
2843            2
2844        );
2845    }
2846
2847    /// A run-on is reported where it happens and parsing continues: three
2848    /// statements, two missing separators, three `VAR_STMT`s and no cascade.
2849    #[test]
2850    fn each_missing_separator_is_reported_once_and_parsing_continues() {
2851        let out = parse_text("var a = 1 var b = 2 var c = 3");
2852        let separator_errors = out
2853            .diagnostics
2854            .iter()
2855            .filter(|d| d.kind() == DiagCode::ExpectedStatementSeparator)
2856            .count();
2857        assert_eq!(separator_errors, 2, "{:?}", out.diagnostics);
2858        assert_eq!(
2859            construct_names(&out.tree)
2860                .iter()
2861                .filter(|kind| **kind == SyntaxKind::VAR_STMT)
2862                .count(),
2863            3
2864        );
2865    }
2866
2867    /// The block loop demands one too, and the closing `}` is a separator in its
2868    /// own right — a trailing expression needs nothing after it.
2869    #[test]
2870    fn a_block_demands_a_separator_but_its_closing_brace_is_one() {
2871        let clean = parse_text("fn f() { var a = 1\n a }");
2872        assert!(clean.diagnostics.is_empty(), "{:?}", clean.diagnostics);
2873
2874        let run_on = parse_text("fn f() { var a = 1 a }");
2875        assert!(
2876            run_on
2877                .diagnostics
2878                .iter()
2879                .any(|d| d.kind() == DiagCode::ExpectedStatementSeparator),
2880            "{:?}",
2881            run_on.diagnostics
2882        );
2883    }
2884
2885    /// Match arms are comma-OR-newline separated, and a run-on is reported.
2886    #[test]
2887    fn match_arms_on_one_line_need_a_comma() {
2888        let commas = parse_text("fn f() { match x { A => 1, B => 2 } }");
2889        assert!(commas.diagnostics.is_empty(), "{:?}", commas.diagnostics);
2890
2891        let newlines = parse_text("fn f() { match x {\n A => 1\n B => 2\n } }");
2892        assert!(
2893            newlines.diagnostics.is_empty(),
2894            "{:?}",
2895            newlines.diagnostics
2896        );
2897
2898        let run_on = parse_text("fn f() { match x { A => 1 B => 2 } }");
2899        assert!(
2900            run_on
2901                .diagnostics
2902                .iter()
2903                .any(|d| d.kind() == DiagCode::ExpectedStatementSeparator),
2904            "{:?}",
2905            run_on.diagnostics
2906        );
2907    }
2908
2909    #[test]
2910    fn regression_parenthesized_record_literal_is_valid_in_a_condition() {
2911        let out = parse_text("if (Point { x: 1 } == p) { 0 }");
2912        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
2913        assert!(
2914            construct_names(&out.tree).contains(&SyntaxKind::RECORD_LIT_EXPR),
2915            "the parenthesized condition must retain its record literal"
2916        );
2917    }
2918
2919    #[test]
2920    fn regression_match_arm_may_return_a_record_literal() {
2921        let out = parse_text("match x { A => Point { x: 1 } }");
2922        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
2923        assert!(
2924            construct_names(&out.tree).contains(&SyntaxKind::RECORD_LIT_EXPR),
2925            "the arm body must contain a record literal"
2926        );
2927    }
2928
2929    // --- which brackets reset record-literal suppression --------------------
2930
2931    /// Every bracketed context resets it, not only parentheses: an argument
2932    /// list, a tuple, and a block are all places where the `{` cannot be the
2933    /// body a keyword is waiting for.
2934    #[test]
2935    fn every_bracket_restores_record_literals_inside_a_condition() {
2936        for src in [
2937            "if f(Point { x: 1 }) { 0 }",
2938            "if (Point { x: 1 }, 2) == t { 0 }",
2939            "if { var p = Point { x: 1 }\n p.ok } { 0 }",
2940            "while v.has(Point { x: 1 }) { 0 }",
2941            "for q in near(Origin { x: 0 }) { 0 }",
2942            "match f(Point { x: 1 }) { A => 1 }",
2943        ] {
2944            let out = parse_text(src);
2945            assert!(out.diagnostics.is_empty(), "{src}: {:?}", out.diagnostics);
2946            assert!(
2947                construct_names(&out.tree).contains(&SyntaxKind::RECORD_LIT_EXPR),
2948                "{src}: the bracketed record literal was suppressed"
2949            );
2950        }
2951    }
2952
2953    /// A match arm body resets it at every depth, not just at the top: the arm's
2954    /// own blocks and closures allow record literals too.
2955    #[test]
2956    fn a_match_arm_allows_a_record_literal_at_any_depth() {
2957        for src in [
2958            "match x { A => { Point { x: 1 } } }",
2959            "match x { A => |q| Point { x: 1 } }",
2960            "match x { A => if c { Point { x: 1 } } else { q } }",
2961        ] {
2962            let out = parse_text(src);
2963            assert!(out.diagnostics.is_empty(), "{src}: {:?}", out.diagnostics);
2964            assert!(
2965                construct_names(&out.tree).contains(&SyntaxKind::RECORD_LIT_EXPR),
2966                "{src}: the arm body suppressed its record literal"
2967            );
2968        }
2969    }
2970
2971    /// …and the suppression still does the job it exists for, in all four heads
2972    /// and through the operands of the expression they own.
2973    #[test]
2974    fn a_keyword_head_still_claims_its_brace_as_a_block() {
2975        for (src, body) in [
2976            ("if p { 0 }", SyntaxKind::IF_EXPR),
2977            ("if a == p { 0 }", SyntaxKind::IF_EXPR),
2978            ("if !p { 0 }", SyntaxKind::IF_EXPR),
2979            ("while p { 0 }", SyntaxKind::WHILE_EXPR),
2980            ("for q in ps { 0 }", SyntaxKind::FOR_EXPR),
2981            ("match p { A => 1 }", SyntaxKind::MATCH_EXPR),
2982        ] {
2983            let out = parse_text(src);
2984            assert!(out.diagnostics.is_empty(), "{src}: {:?}", out.diagnostics);
2985            let kinds = construct_names(&out.tree);
2986            assert!(kinds.contains(&body), "{src}: expected a {body:?}");
2987            assert!(
2988                !kinds.contains(&SyntaxKind::RECORD_LIT_EXPR),
2989                "{src}: the head's brace was eaten as a record body"
2990            );
2991        }
2992    }
2993
2994    // --- the headless record literal `{ x: 1 }` (§5.6, ADR-152) -------------
2995
2996    /// A `{` where an expression must begin is a record literal when a block
2997    /// could not be what follows: a name and a `:`, or a name and a `,`.
2998    #[test]
2999    fn a_brace_a_block_cannot_explain_is_a_record_literal() {
3000        for src in [
3001            "var p = { x: 1 }",
3002            "var p = { x: 1, y: 2 }",
3003            "var p = { x: 1, }",
3004            "var p = { x, y }",
3005            "var p = { x, y: 2 }",
3006            "var p = { x: 1, y }",
3007            "var p = { pos: { x: 1 }, n: 2 }",
3008            "f({ x: 1 })",
3009            "var vs = [{ x: 1 }, { x: 2 }]",
3010            "var f = |q| { x: q }",
3011            "out({ x: 1 }.x)",
3012        ] {
3013            let out = parse_text(src);
3014            assert!(out.diagnostics.is_empty(), "{src}: {:?}", out.diagnostics);
3015            assert!(
3016                construct_names(&out.tree).contains(&SyntaxKind::RECORD_LIT_EXPR),
3017                "{src}: read as a block"
3018            );
3019        }
3020    }
3021
3022    /// …and everything else at that position is still the block it was.
3023    ///
3024    /// `{ x }` is the case the rule cannot have both ways — a block whose value
3025    /// is `x`, and a one-field punned literal, are the same six characters — and
3026    /// the block had the spelling first. `{ x:bp }` is the other: `:bp` is a
3027    /// marker on the statement `x`, told from the field `x: bp` by the same
3028    /// adjacency that tells `min=` from `min =`.
3029    #[test]
3030    fn a_brace_a_block_can_explain_is_still_a_block() {
3031        for src in [
3032            "var p = { x }",
3033            "var p = { }",
3034            "var p = { x\n y }",
3035            "var p = { f(1) }",
3036            "var p = { x:bp }",
3037            "var p = { var t = 1\n t }",
3038        ] {
3039            let out = parse_text(src);
3040            assert!(out.diagnostics.is_empty(), "{src}: {:?}", out.diagnostics);
3041            let kinds = construct_names(&out.tree);
3042            assert!(kinds.contains(&SyntaxKind::BLOCK_EXPR), "{src}: no block");
3043            assert!(
3044                !kinds.contains(&SyntaxKind::RECORD_LIT_EXPR),
3045                "{src}: the block was eaten as a record literal"
3046            );
3047        }
3048    }
3049
3050    /// The marker rule is adjacency and only adjacency, so the spacing decides
3051    /// — and both spellings are things somebody means.
3052    #[test]
3053    fn a_space_after_the_colon_is_a_field_and_not_a_marker() {
3054        let marker = parse_text("var p = { x:bp }");
3055        assert!(marker.diagnostics.is_empty(), "{:?}", marker.diagnostics);
3056        assert!(construct_names(&marker.tree).contains(&SyntaxKind::BREAKPOINT));
3057
3058        let field = parse_text("var p = { x: bp }");
3059        assert!(field.diagnostics.is_empty(), "{:?}", field.diagnostics);
3060        let kinds = construct_names(&field.tree);
3061        assert!(kinds.contains(&SyntaxKind::RECORD_LIT_EXPR));
3062        assert!(
3063            !kinds.contains(&SyntaxKind::BREAKPOINT),
3064            "`: bp` with a space is a field whose value is the binding `bp`"
3065        );
3066    }
3067
3068    /// **A keyword head does not suppress it**, and does not need to.
3069    ///
3070    /// What [`StructLit`] suppression protects is `p { … }` — a *name* followed
3071    /// by the brace that could be the keyword's block (ADR-050). A `{` where an
3072    /// operand is still required cannot be that block: the block comes after a
3073    /// complete head, so a brace at the head's start has nothing to be confused
3074    /// with.
3075    #[test]
3076    fn a_keyword_head_may_open_with_a_record_literal() {
3077        for src in [
3078            "if { hit: true }.hit { 0 }",
3079            "while { n: 1 }.n == 1 { 0 }",
3080            "for q in { xs: ps }.xs { 0 }",
3081            "match { tag: t }.tag { A => 1 }",
3082        ] {
3083            let out = parse_text(src);
3084            assert!(out.diagnostics.is_empty(), "{src}: {:?}", out.diagnostics);
3085            assert!(
3086                construct_names(&out.tree).contains(&SyntaxKind::RECORD_LIT_EXPR),
3087                "{src}: the head's own literal was read as a block"
3088            );
3089        }
3090    }
3091
3092    /// An anonymous literal is the same node as a headed one, with the head
3093    /// absent — which is what `RecordLitExpr::name()` answers `None` from.
3094    #[test]
3095    fn an_anonymous_literal_has_no_path_child() {
3096        let anon = dump("var p = { x: 1 }");
3097        let headed = dump("var p = Point { x: 1 }");
3098        assert!(anon.contains("RECORD_LIT_EXPR"), "{anon}");
3099        assert!(anon.contains("FIELD_LIST"), "{anon}");
3100        assert!(
3101            !anon.contains("PATH_EXPR"),
3102            "an anonymous literal names nothing: {anon}"
3103        );
3104        assert!(headed.contains("PATH_EXPR"), "{headed}");
3105    }
3106
3107    // --- Pratt precedence ---
3108
3109    #[test]
3110    fn arithmetic_is_left_associative() {
3111        // 1 + 2 + 3 should nest left: ((1 + 2) + 3).
3112        let out = parse_text("1 + 2 + 3");
3113        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
3114        let tree = dump("1 + 2 + 3");
3115        insta::assert_snapshot!(tree, @r#"
3116        SOURCE_FILE@0..9
3117          EXPR_STMT@0..9
3118            BIN_EXPR@0..9
3119              BIN_EXPR@0..5
3120                LITERAL@0..1
3121                  IntLit "1"@0..1
3122                Whitespace " "@1..2
3123                PLUS "+"@2..3
3124                Whitespace " "@3..4
3125                LITERAL@4..5
3126                  IntLit "2"@4..5
3127              Whitespace " "@5..6
3128              PLUS "+"@6..7
3129              Whitespace " "@7..8
3130              LITERAL@8..9
3131                IntLit "3"@8..9
3132        "#);
3133    }
3134
3135    #[test]
3136    fn multiplication_binds_tighter_than_addition() {
3137        // 1 + 2 * 3 -> 1 + (2 * 3)
3138        let out = parse_text("1 + 2 * 3");
3139        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
3140        let kinds = construct_names(&out.tree);
3141        assert!(kinds.contains(&SyntaxKind::BIN_EXPR));
3142    }
3143
3144    #[test]
3145    fn parentheses_override_precedence() {
3146        // (1 + 2) * 3 -> the addition is the lhs of the multiply.
3147        let out = parse_text("(1 + 2) * 3");
3148        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
3149        let kinds = construct_names(&out.tree);
3150        assert!(kinds.contains(&SyntaxKind::PAREN_EXPR));
3151    }
3152
3153    #[test]
3154    fn parses_unary_minus() {
3155        let out = parse_text("-x");
3156        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
3157        let kinds = construct_names(&out.tree);
3158        assert!(kinds.contains(&SyntaxKind::UNARY_EXPR));
3159    }
3160
3161    // --- Error recovery (§15.2, acceptance: multiple diagnostics, never panic) ---
3162
3163    #[test]
3164    fn malformed_input_produces_multiple_diagnostics_and_never_panics() {
3165        // A file with several distinct problems: missing `=` in a let, a stray
3166        // `)`, and a second broken statement. Recovery must keep going and emit
3167        // at least two P0xx diagnostics.
3168        let src = "var x 1\n )\nvar = \n";
3169        let out = parse_text(src);
3170        let parse_diags: Vec<_> = out
3171            .diagnostics
3172            .iter()
3173            .filter(|d| d.code().category() == DiagnosticCategory::Parse)
3174            .collect();
3175        assert!(
3176            parse_diags.len() >= 2,
3177            "expected >=2 parse diagnostics, got {}: {:?}",
3178            parse_diags.len(),
3179            parse_diags
3180        );
3181        // The tree is always produced.
3182        assert_eq!(out.tree.kind(), SyntaxKind::SOURCE_FILE);
3183    }
3184
3185    #[test]
3186    fn empty_input_parses_to_empty_source_file() {
3187        let out = parse_text("");
3188        assert!(out.diagnostics.is_empty());
3189        assert_eq!(out.tree.kind(), SyntaxKind::SOURCE_FILE);
3190        // No children except possibly trailing trivia (none here).
3191        assert_eq!(out.tree.children_with_tokens().count(), 0);
3192    }
3193
3194    #[test]
3195    fn whitespace_only_input_is_clean() {
3196        let out = parse_text("   \n  // just a comment\n  ");
3197        assert!(out.diagnostics.is_empty());
3198    }
3199
3200    // --- type annotations + tuples ------------------------------------------
3201
3202    #[test]
3203    fn parses_let_with_tuple_type_annotation() {
3204        let src = "var p: (Int, Int) = (1, 2)";
3205        let out = parse_text(src);
3206        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
3207        let kinds = construct_names(&out.tree);
3208        assert!(kinds.contains(&SyntaxKind::TUPLE_TYPE));
3209        assert!(kinds.contains(&SyntaxKind::TUPLE_EXPR));
3210        assert!(kinds.contains(&SyntaxKind::TYPE_REF));
3211    }
3212
3213    #[test]
3214    fn parses_fn_with_full_annotations() {
3215        // Full parameter + return annotations, including a tuple return type.
3216        let src = "fn f(a: Int, b: Int) -> (Int, Int) { (a, b) }";
3217        let out = parse_text(src);
3218        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
3219        let kinds = construct_names(&out.tree);
3220        assert!(kinds.contains(&SyntaxKind::TYPE_REF));
3221        assert!(kinds.contains(&SyntaxKind::TUPLE_TYPE));
3222    }
3223
3224    #[test]
3225    fn parses_higher_order_function_type() {
3226        // `(Int) -> Int` as a parameter type.
3227        let src = "fn apply(f: (Int) -> Int, x: Int) -> Int { f(x) }";
3228        let out = parse_text(src);
3229        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
3230        let kinds = construct_names(&out.tree);
3231        assert!(kinds.contains(&SyntaxKind::FN_TYPE));
3232    }
3233
3234    #[test]
3235    fn parses_scalar_type_annotation() {
3236        let src = "var x: Int = 1";
3237        let out = parse_text(src);
3238        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
3239        let kinds = construct_names(&out.tree);
3240        assert!(kinds.contains(&SyntaxKind::TYPE_REF));
3241    }
3242
3243    #[test]
3244    fn tuple_expression_distinguishes_from_paren() {
3245        // Single-element paren stays PAREN_EXPR; two-element is TUPLE_EXPR.
3246        let single = construct_names(&parse_text("(1)").tree);
3247        assert!(single.contains(&SyntaxKind::PAREN_EXPR));
3248        assert!(!single.contains(&SyntaxKind::TUPLE_EXPR));
3249
3250        let pair = construct_names(&parse_text("(1, 2)").tree);
3251        assert!(pair.contains(&SyntaxKind::TUPLE_EXPR));
3252        assert!(!pair.contains(&SyntaxKind::PAREN_EXPR));
3253    }
3254
3255    /// **A tuple has two elements or more** (§4.4), so `(1,)` is refused *here*,
3256    /// at the comma — a one-element `TUPLE_EXPR` is a shape `TupleElems` cannot
3257    /// represent, so typing and lowering would disagree about the node.
3258    ///
3259    /// The node recovers as `PAREN_EXPR`, the grouping the author most likely
3260    /// meant, so nothing downstream sees the shape that does not exist.
3261    #[test]
3262    fn a_one_element_tuple_is_refused_at_the_comma() {
3263        let parsed = parse_text("var t = (1,)\n");
3264        assert_eq!(
3265            parsed.diagnostics.len(),
3266            1,
3267            "one report, at the comma: {:?}",
3268            parsed.diagnostics
3269        );
3270        assert!(
3271            parsed.diagnostics[0].message().contains("two elements"),
3272            "{}",
3273            parsed.diagnostics[0].message()
3274        );
3275        let kinds = construct_names(&parsed.tree);
3276        assert!(kinds.contains(&SyntaxKind::PAREN_EXPR));
3277        assert!(!kinds.contains(&SyntaxKind::TUPLE_EXPR));
3278
3279        // A trailing comma at an arity the type *has* is punctuation, untouched.
3280        let trailing = parse_text("var t = (1, 2,)\n");
3281        assert!(
3282            trailing.diagnostics.is_empty(),
3283            "{:?}",
3284            trailing.diagnostics
3285        );
3286        assert!(construct_names(&trailing.tree).contains(&SyntaxKind::TUPLE_EXPR));
3287
3288        // The same rule in type position.
3289        let annotated = parse_text("var t: (Int,) = 1\n");
3290        assert_eq!(
3291            annotated.diagnostics.len(),
3292            1,
3293            "{:?}",
3294            annotated.diagnostics
3295        );
3296        assert!(!construct_names(&annotated.tree).contains(&SyntaxKind::TUPLE_TYPE));
3297        assert!(
3298            parse_text("var t: (Int, Text,) = (1, \"a\")\n")
3299                .diagnostics
3300                .is_empty()
3301        );
3302    }
3303
3304    #[test]
3305    fn tuple_expression_snapshot() {
3306        insta::assert_snapshot!(dump("(1, 2)"), @r#"
3307        SOURCE_FILE@0..6
3308          EXPR_STMT@0..6
3309            TUPLE_EXPR@0..6
3310              L_PAREN "("@0..1
3311              LITERAL@1..2
3312                IntLit "1"@1..2
3313              COMMA ","@2..3
3314              Whitespace " "@3..4
3315              LITERAL@4..5
3316                IntLit "2"@4..5
3317              R_PAREN ")"@5..6
3318        "#);
3319    }
3320
3321    #[test]
3322    fn function_type_right_associative() {
3323        // `A -> B -> C` parses as `A -> (B -> C)`: the outer FN_TYPE's result is
3324        // itself an FN_TYPE.
3325        let src = "var f: Int -> Text -> Bool = panic";
3326        let out = parse_text(src);
3327        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
3328        let kinds = construct_names(&out.tree);
3329        // Two FN_TYPE nodes for the nested arrow.
3330        let fn_type_count = kinds.iter().filter(|k| **k == SyntaxKind::FN_TYPE).count();
3331        assert_eq!(fn_type_count, 2);
3332    }
3333
3334    // --- input-parser expression syntax (§7) ---
3335
3336    #[test]
3337    fn parses_read_atomic() {
3338        let out = parse_text("var v = read int");
3339        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
3340        let kinds = construct_names(&out.tree);
3341        assert!(kinds.contains(&SyntaxKind::READ_EXPR));
3342        assert!(kinds.contains(&SyntaxKind::PARSER_EXPR));
3343        assert!(kinds.contains(&SyntaxKind::PARSER_ATOM));
3344    }
3345
3346    #[test]
3347    fn parses_read_lines_of_int() {
3348        let out = parse_text("var v = read lines(int)");
3349        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
3350        let kinds = construct_names(&out.tree);
3351        assert!(kinds.contains(&SyntaxKind::READ_EXPR));
3352        assert!(kinds.contains(&SyntaxKind::PARSER_CALL));
3353        // Nested atom inside the call's arg list.
3354        assert!(kinds.contains(&SyntaxKind::PARSER_ATOM));
3355        assert!(kinds.contains(&SyntaxKind::PARSER_ARG_LIST));
3356    }
3357
3358    #[test]
3359    fn parses_read_nested_constructors() {
3360        // sections(lines(csv(int))) — whitespace outside backticks is
3361        // insignificant (§7.1 acceptance criterion 5).
3362        let out = parse_text("var v = read sections( lines( csv( int ) ) )");
3363        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
3364        let kinds = construct_names(&out.tree);
3365        // Three nested PARSER_CALL nodes (sections, lines, csv).
3366        let call_count = kinds
3367            .iter()
3368            .filter(|k| **k == SyntaxKind::PARSER_CALL)
3369            .count();
3370        assert_eq!(call_count, 3);
3371    }
3372
3373    #[test]
3374    fn parses_read_template() {
3375        let out = parse_text("var v = read lines(`{x:int},{y:int}`)");
3376        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
3377        let kinds = construct_names(&out.tree);
3378        assert!(kinds.contains(&SyntaxKind::READ_EXPR));
3379        assert!(kinds.contains(&SyntaxKind::PARSER_CALL));
3380        assert!(kinds.contains(&SyntaxKind::PARSER_TEMPLATE));
3381        assert!(kinds.contains(&SyntaxKind::BacktickTemplate));
3382    }
3383
3384    #[test]
3385    fn parses_read_grid() {
3386        let out = parse_text("solve(read grid(char))");
3387        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
3388        let kinds = construct_names(&out.tree);
3389        assert!(kinds.contains(&SyntaxKind::READ_EXPR));
3390        assert!(kinds.contains(&SyntaxKind::PARSER_CALL));
3391    }
3392
3393    #[test]
3394    fn parses_read_sep_with_string_literal() {
3395        let out = parse_text(r#"var v = read sep(" -> ", word)"#);
3396        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
3397        let kinds = construct_names(&out.tree);
3398        assert!(kinds.contains(&SyntaxKind::READ_EXPR));
3399        assert!(kinds.contains(&SyntaxKind::PARSER_CALL));
3400        // The string-literal separator is inside the arg list.
3401        assert!(kinds.contains(&SyntaxKind::TextLit));
3402    }
3403
3404    // --- named arguments in parser constructor calls (§7.5) ------------------
3405
3406    #[test]
3407    fn parses_named_args_in_sections() {
3408        // heterogeneous `sections(rules: ..., updates: ...)` — two named args.
3409        let out = parse_text("var v = read sections(rules: lines(int), updates: lines(int))");
3410        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
3411        let kinds = construct_names(&out.tree);
3412        assert!(kinds.contains(&SyntaxKind::PARSER_NAMED_ARG));
3413        // Two named args.
3414        let named_count = kinds
3415            .iter()
3416            .filter(|k| **k == SyntaxKind::PARSER_NAMED_ARG)
3417            .count();
3418        assert_eq!(named_count, 2);
3419    }
3420
3421    #[test]
3422    fn parses_repeated_tail_in_sections() {
3423        // The `repeated(...)` tail marker of named sections.
3424        let out =
3425            parse_text("var v = read sections(draws: csv(int), boards: repeated(matrix(int)))");
3426        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
3427        let kinds = construct_names(&out.tree);
3428        assert!(kinds.contains(&SyntaxKind::PARSER_NAMED_ARG));
3429    }
3430
3431    /// **A count is a positional literal, and the grammar has a shape for it.**
3432    /// The `N` of `repeated(P, N)` is an integer in positional position; the
3433    /// keyword-value path that also accepts one (`fill: 0`) is reachable only
3434    /// after a `name:`, so the positional case needs its own arm.
3435    #[test]
3436    fn a_parser_call_takes_a_positional_count_literal() {
3437        let out = parse_text("var v = read sections(a: repeated(lines(int), 2), b: lines(int))");
3438        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
3439        let kinds = construct_names(&out.tree);
3440        assert!(
3441            !kinds.contains(&SyntaxKind::PARSE_ERROR),
3442            "a count argument must not be an error node"
3443        );
3444        assert!(
3445            kinds.contains(&SyntaxKind::LITERAL),
3446            "the count is a LITERAL child of the argument list"
3447        );
3448    }
3449
3450    /// A negative count is one `LITERAL`, not a `-` the parser complains about
3451    /// separately: the diagnostic a reader needs is about the *count*, and only
3452    /// a node that holds the whole thing can carry it there.
3453    #[test]
3454    fn a_negative_count_is_one_literal_not_a_parse_error() {
3455        let out = parse_text("var v = read sections(a: repeated(int, -1))");
3456        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
3457        let kinds = construct_names(&out.tree);
3458        assert!(!kinds.contains(&SyntaxKind::PARSE_ERROR));
3459        assert_eq!(
3460            kinds.iter().filter(|k| **k == SyntaxKind::LITERAL).count(),
3461            1,
3462            "`-1` is one literal node, not two"
3463        );
3464    }
3465
3466    #[test]
3467    fn parses_keyword_arg_in_chars() {
3468        // `chars(one_of(...), skip: whitespace)` — a positional arg followed by
3469        // a named keyword arg.
3470        let out = parse_text("var v = read chars(one_of(\"LR\"), skip: whitespace)");
3471        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
3472        let kinds = construct_names(&out.tree);
3473        assert!(kinds.contains(&SyntaxKind::PARSER_NAMED_ARG));
3474    }
3475
3476    #[test]
3477    fn named_arg_does_not_shadow_constructor_call() {
3478        // A constructor call argument (`lines(int)`) has `(` at position 1, so
3479        // it is NOT mistaken for a named arg. Only `ident:` is.
3480        let out = parse_text("var v = read sections(lines(int))");
3481        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
3482        let kinds = construct_names(&out.tree);
3483        assert!(
3484            !kinds.contains(&SyntaxKind::PARSER_NAMED_ARG),
3485            "positional constructor-call arg must not parse as a named arg"
3486        );
3487    }
3488
3489    #[test]
3490    fn parses_parse_call() {
3491        let out = parse_text("var v = parse(sample, lines(int))");
3492        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
3493        let kinds = construct_names(&out.tree);
3494        assert!(kinds.contains(&SyntaxKind::PARSE_EXPR));
3495        assert!(kinds.contains(&SyntaxKind::PARSER_CALL));
3496    }
3497
3498    #[test]
3499    fn parser_expression_whitespace_is_insignificant() {
3500        // The same parser expression laid out differently must produce the same
3501        // tree shape (modulo trivia). §7.1 acceptance criterion 5.
3502        let a = construct_names(&parse_text("read lines(int)").tree);
3503        let b = construct_names(&parse_text("read\n  lines(\n    int\n  )").tree);
3504        // Filter out trivia (whitespace) for the comparison.
3505        let filt = |ks: &[SyntaxKind]| -> Vec<SyntaxKind> {
3506            ks.iter().filter(|k| !k.is_trivia()).copied().collect()
3507        };
3508        assert_eq!(filt(&a), filt(&b));
3509    }
3510    /// `&&` is one token and one infix operator, and its precedence is the two
3511    /// facts that matter: tighter than `||`, looser than comparison.
3512    ///
3513    /// The whole precedence table is asserted below rather than just `&&`'s row,
3514    /// because a binding power is only meaningful relative to the others.
3515    #[test]
3516    fn logical_and_binds_tighter_than_or_and_looser_than_comparison() {
3517        // One token, not two `AMP`s — max-munch, as for `||`.
3518        let out = parse_text("var b = x && y");
3519        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
3520        let kinds = construct_names(&out.tree);
3521        assert!(kinds.contains(&SyntaxKind::AMP2));
3522        assert!(!kinds.contains(&SyntaxKind::AMP), "`&&` is never two `&`s");
3523
3524        // `a || b && c` is `a || (b && c)`: the outer BIN_EXPR is the `||`.
3525        assert_eq!(
3526            shape("var r = a || b && c"),
3527            shape("var r = a || (b && c)"),
3528            "&& binds tighter than ||"
3529        );
3530        assert_ne!(shape("var r = a || b && c"), shape("var r = (a || b) && c"));
3531
3532        // `a == b && c == d` is `(a == b) && (c == d)` — §3.3's own shape, and
3533        // the reason `&&` must be looser than comparison.
3534        assert_eq!(
3535            shape("var r = a == b && c == d"),
3536            shape("var r = (a == b) && (c == d)")
3537        );
3538
3539        // `!x && y` is `(!x) && y`: prefix stays above every infix operator.
3540        // §3.3 writes `!diagonals && …`.
3541        assert_eq!(shape("var r = !x && y"), shape("var r = (!x) && y"));
3542        assert_ne!(shape("var r = !x && y"), shape("var r = !(x && y)"));
3543
3544        // The rest of the table: arithmetic binds tighter than comparison, `*`
3545        // than `+`, and unary minus than `*`.
3546        assert_eq!(shape("var r = a + b < c"), shape("var r = (a + b) < c"));
3547        assert_eq!(shape("var r = a + b * c"), shape("var r = a + (b * c)"));
3548        assert_eq!(shape("var r = -a * b"), shape("var r = (-a) * b"));
3549        // …and `..` binds looser than the arithmetic in its bounds (ADR-059).
3550        assert_eq!(shape("var r = 0..n - 1"), shape("var r = 0..(n - 1)"));
3551        assert_ne!(shape("var r = 0..n - 1"), shape("var r = (0..n) - 1"));
3552    }
3553
3554    /// **A trailing comma closes a list; it does not open another element.**
3555    ///
3556    /// Asserted over every comma-separated list in the grammar rather than over
3557    /// the argument list alone: accepting a trailing comma is a property of the
3558    /// grammar, and each list loop is a separate place for it to be missing.
3559    #[test]
3560    fn a_trailing_comma_closes_a_list_rather_than_opening_an_element() {
3561        // The list, and the same list without the trailing comma: identical trees
3562        // once the comma token is out of the way, which is what "closes it" means.
3563        for (with, without) in [
3564            // Call arguments, in §3.3's own layout.
3565            (
3566                "var d = max(\n  abs(a),\n  abs(b),\n)",
3567                "var d = max(abs(a), abs(b))",
3568            ),
3569            ("var x = f(1,)", "var x = f(1)"),
3570            // Tuple literal, collection type arguments.
3571            ("var t = (1, 2,)", "var t = (1, 2)"),
3572            ("var v: Vec[Int,] = Vec()", "var v: Vec[Int] = Vec()"),
3573            (
3574                "var m: Map[Text, Int,] = Map()",
3575                "var m: Map[Text, Int] = Map()",
3576            ),
3577            // Declarations: struct fields, enum variants, an enum payload.
3578            (
3579                "struct P { x: Int, y: Int, }",
3580                "struct P { x: Int, y: Int }",
3581            ),
3582            ("enum E { A, B, }", "enum E { A, B }"),
3583            ("enum E { B(Int, Int,), }", "enum E { B(Int, Int) }"),
3584            // Function and closure parameters.
3585            (
3586                "fn add(a: Int, b: Int,) -> Int { a + b }",
3587                "fn add(a: Int, b: Int) -> Int { a + b }",
3588            ),
3589            ("var f = |a, b,| a + b", "var f = |a, b| a + b"),
3590            // Record literal fields, and match arms.
3591            (
3592                "struct P { x: Int }\nvar p = P { x: 1, }",
3593                "struct P { x: Int }\nvar p = P { x: 1 }",
3594            ),
3595            (
3596                "var r = match n { 1 => 1, _ => 0, }",
3597                "var r = match n { 1 => 1, _ => 0 }",
3598            ),
3599            // Subscript indices.
3600            ("var c = grid[x, y,]", "var c = grid[x, y]"),
3601        ] {
3602            let out = parse_text(with);
3603            assert!(out.diagnostics.is_empty(), "{with}: {:?}", out.diagnostics);
3604            let filt = |t: &str| -> Vec<SyntaxKind> {
3605                construct_names(&parse_text(t).tree)
3606                    .into_iter()
3607                    .filter(|k| !k.is_trivia() && *k != SyntaxKind::COMMA)
3608                    .collect()
3609            };
3610            assert_eq!(filt(with), filt(without), "{with}");
3611        }
3612
3613        // …and a *leading* or doubled comma is still a mistake: the rule is that
3614        // a comma may precede the closer, not that commas are optional.
3615        for bad in ["var x = f(1,,2)", "var x = f(,1)", "var t = (1,,2)"] {
3616            let out = parse_text(bad);
3617            assert!(!out.diagnostics.is_empty(), "{bad} must still report");
3618        }
3619    }
3620
3621    /// **A subscript is a postfix form like a call**, so it chains with the
3622    /// other two in any order — and a statement whose target is one is an
3623    /// assignment.
3624    #[test]
3625    fn a_subscript_is_a_postfix_form_and_can_be_an_assignment_target() {
3626        // Reads, at both arities, and chained with the other postfix forms in
3627        // every order — which is what one loop over all three buys.
3628        for src in [
3629            "var v = m[key]",
3630            "var c = grid[x, y]",
3631            "var n = m[a][b]",
3632            "var n = f(x)[0]",
3633            "var n = grid[x, y].len()",
3634            "var n = v[0].0",
3635            "var n = rows[i].len() + 1",
3636            "var n = m[k](7)",
3637        ] {
3638            let out = parse_text(src);
3639            assert!(out.diagnostics.is_empty(), "{src}: {:?}", out.diagnostics);
3640        }
3641
3642        // A subscript wraps the *whole* preceding expression, so `f(x)[0]` is one
3643        // statement and not two.
3644        let out = parse_text("var n = f(x)[0]");
3645        assert_eq!(
3646            construct_names(&out.tree)
3647                .iter()
3648                .filter(|k| **k == SyntaxKind::INDEX_EXPR)
3649                .count(),
3650            1
3651        );
3652
3653        // Assignment through a subscript, in every operator the grammar has.
3654        for src in [
3655            "m[key] = 1",
3656            "counts[key] += 1",
3657            "m[key] -= 1",
3658            "m[key] *= 2",
3659            "m[key] /= 2",
3660            "m[key] %= 2",
3661            "grid[x, y] = 7",
3662            "m[a][b] += 1",
3663        ] {
3664            let out = parse_text(src);
3665            assert!(out.diagnostics.is_empty(), "{src}: {:?}", out.diagnostics);
3666            assert_eq!(
3667                construct_names(&out.tree)
3668                    .iter()
3669                    .filter(|k| **k == SyntaxKind::PLACE_ASSIGN_STMT)
3670                    .count(),
3671                1,
3672                "{src} is one assignment statement"
3673            );
3674        }
3675
3676        // A bare name target is an `ASSIGN_STMT` — a different node, with a
3677        // *token* target — so the two paths stay distinct.
3678        let out = parse_text("x += 1");
3679        let kinds = construct_names(&out.tree);
3680        assert!(kinds.contains(&SyntaxKind::ASSIGN_STMT), "{kinds:?}");
3681        assert!(!kinds.contains(&SyntaxKind::PLACE_ASSIGN_STMT), "{kinds:?}");
3682
3683        // Whether a target is a place is inference's answer and not the
3684        // parser's, so both kinds parse the same way: `p.x = 3` is a field store
3685        // (§4.5) and `f() = 3` names no storage at all (`Y021`), and the parser
3686        // wraps each of them without asking. This is the assertion that would
3687        // fail if the wrap were made conditional on the target's shape.
3688        for src in ["f() = 3", "p.x = 3", "v.len() += 1"] {
3689            let out = parse_text(src);
3690            assert!(out.diagnostics.is_empty(), "{src}: {:?}", out.diagnostics);
3691            assert_eq!(
3692                construct_names(&out.tree)
3693                    .iter()
3694                    .filter(|k| **k == SyntaxKind::PLACE_ASSIGN_STMT)
3695                    .count(),
3696                1,
3697                "{src}"
3698            );
3699        }
3700
3701        // A `[` after a line break does not continue the expression before it:
3702        // a list literal begins with one, so the tie is broken by position the
3703        // way it is for `(`.
3704        let out = parse_text(
3705            "var n = m
3706[key]",
3707        );
3708        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
3709        assert_eq!(
3710            out.tree.children().count(),
3711            2,
3712            "a line-leading `[` starts a list rather than subscripting"
3713        );
3714        // …and a `[` on the same line still subscripts, which is every subscript
3715        // any program writes.
3716        let out = parse_text("var n = m[key]");
3717        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
3718        assert_eq!(out.tree.children().count(), 1);
3719
3720        // An unclosed subscript is reported rather than swallowing the rest.
3721        for bad in ["var v = m[key", "var v = m[]", "m[key] ="] {
3722            let out = parse_text(bad);
3723            assert!(!out.diagnostics.is_empty(), "{bad} must report");
3724        }
3725    }
3726
3727    /// **A type constructor's name followed by `[` opens a type-argument list;
3728    /// every other name followed by `[` is a subscript.**
3729    ///
3730    /// The two forms are the same two characters, and their contents cannot
3731    /// break the tie either (`Int` is a legal expression, `(Int, Int)` a legal
3732    /// tuple), so the name in front is the whole rule and this is where it is
3733    /// pinned.
3734    #[test]
3735    fn a_type_constructors_brackets_are_type_arguments_and_every_other_names_are_a_subscript() {
3736        let count = |text: &str, kind: SyntaxKind| -> usize {
3737            let out = parse_text(text);
3738            assert!(out.diagnostics.is_empty(), "{text}: {:?}", out.diagnostics);
3739            construct_names(&out.tree)
3740                .into_iter()
3741                .filter(|k| *k == kind)
3742                .count()
3743        };
3744
3745        // §3.3's own spelling, and the shapes around it.
3746        for src in [
3747            "var c = Counter[(Int, Int)]()",
3748            "var v = Vec[Int]()",
3749            "var m = Map[Text, Int]()",
3750            "var g = Grid[Vec[Int]]()",
3751            // A trailing comma closes this list too.
3752            "var m = Map[Text, Int,]()",
3753        ] {
3754            assert_eq!(count(src, SyntaxKind::TYPE_ARG_LIST), 1, "{src}");
3755            assert_eq!(count(src, SyntaxKind::INDEX_EXPR), 0, "{src}");
3756            assert_eq!(count(src, SyntaxKind::CALL_EXPR), 1, "{src}");
3757        }
3758
3759        // …and every other name's brackets are still a subscript, including a
3760        // subscript **followed by a call**, which is what a "brackets before `(`
3761        // are type arguments" rule would have broken.
3762        for src in [
3763            "var v = m[key]",
3764            "var v = m[key](7)",
3765            "var v = counter[key]",
3766            "var v = grid[x, y]",
3767        ] {
3768            assert_eq!(count(src, SyntaxKind::INDEX_EXPR), 1, "{src}");
3769            assert_eq!(count(src, SyntaxKind::TYPE_ARG_LIST), 0, "{src}");
3770        }
3771
3772        // A type-argument list belongs to a *call*, so a bare one reports rather
3773        // than parsing as a type in value position. An empty one reports too: a
3774        // constructor with no arguments is spelled `Counter()`.
3775        for bad in [
3776            "var c = Counter[Int]",
3777            "var c = Counter[]()",
3778            "var c = Counter[Int",
3779            "var c = Vec[Int] + 1",
3780        ] {
3781            let out = parse_text(bad);
3782            assert!(!out.diagnostics.is_empty(), "{bad} must report");
3783        }
3784    }
3785
3786    /// A `[` that **begins** an expression opens a list literal; a `[` that
3787    /// continues one is still a subscript.
3788    ///
3789    /// The two spellings are the same two characters, and — as with a
3790    /// type-argument list and a `(` — their contents cannot break the tie: `[k]`
3791    /// is a legal list and a legal subscript. Position is the whole rule, and
3792    /// this is where it is pinned.
3793    #[test]
3794    fn a_bracket_that_begins_an_expression_is_a_list_and_one_that_continues_it_is_a_subscript() {
3795        let count = |src: &str, kind: SyntaxKind| -> usize {
3796            let out = parse_text(src);
3797            assert!(out.diagnostics.is_empty(), "{src}: {:?}", out.diagnostics);
3798            construct_names(&out.tree)
3799                .into_iter()
3800                .filter(|k| *k == kind)
3801                .count()
3802        };
3803
3804        // Every position an expression can begin in: a binding, an argument, a
3805        // `for` iterable, a return value, an element of another list.
3806        for src in [
3807            "var v = [1, 2, 3]",
3808            "var v = []",
3809            "var v = [1]",
3810            // A trailing comma closes this list too.
3811            "var v = [1, 2,]",
3812            "out([1, 2])",
3813            "for x in [1, 2] { out(x) }",
3814            "fn f() { return [1] }",
3815        ] {
3816            assert!(count(src, SyntaxKind::LIST_EXPR) >= 1, "{src}");
3817            assert_eq!(count(src, SyntaxKind::INDEX_EXPR), 0, "{src}");
3818        }
3819
3820        // One node kind at every arity, including zero: nothing about a list
3821        // changes at two the way a paren becomes a tuple there.
3822        for (src, want) in [
3823            ("var v = []", 1),
3824            ("var v = [1]", 1),
3825            ("var v = [1, 2]", 1),
3826            ("var v = [[1], [2, 3]]", 3),
3827        ] {
3828            assert_eq!(count(src, SyntaxKind::LIST_EXPR), want, "{src}");
3829        }
3830
3831        // …and a `[` that continues an expression is a subscript, including one
3832        // that indexes a list literal.
3833        for (src, want) in [
3834            ("var v = m[key]", 1),
3835            ("var v = grid[x, y]", 1),
3836            // Chained: each link continues the whole expression before it.
3837            ("var v = m[k][j]", 2),
3838            // A list literal is itself something a subscript can continue.
3839            ("var v = [1, 2][0]", 1),
3840            ("var v = f()[0]", 1),
3841        ] {
3842            assert_eq!(count(src, SyntaxKind::INDEX_EXPR), want, "{src}");
3843        }
3844        assert_eq!(count("var v = [1, 2][0]", SyntaxKind::LIST_EXPR), 1);
3845
3846        // An empty subscript is an error: a subscript selects *something*, where
3847        // a list may hold nothing.
3848        for bad in ["var v = m[]", "var v = [1, 2", "var v = [1 2]"] {
3849            let out = parse_text(bad);
3850            assert!(!out.diagnostics.is_empty(), "{bad} must report");
3851        }
3852    }
3853
3854    /// **A `for` binding is a pattern**, so `for (k, v) in m` takes the pair
3855    /// apart where `for kv in m` could only name it. Destructuring in binding
3856    /// position *is* a pattern, so the header reuses the pattern grammar rather
3857    /// than having one of its own (ADR-066 decision 3).
3858    #[test]
3859    fn a_for_binding_is_a_pattern() {
3860        let count = |src: &str, kind: SyntaxKind| -> usize {
3861            let out = parse_text(src);
3862            assert!(out.diagnostics.is_empty(), "{src}: {:?}", out.diagnostics);
3863            construct_names(&out.tree)
3864                .into_iter()
3865                .filter(|k| *k == kind)
3866                .count()
3867        };
3868
3869        // The shapes, and the count of patterns each holds: the header's own,
3870        // plus one per element or field.
3871        for (src, patterns) in [
3872            ("for x in v { }", 1),
3873            ("for (k, v) in m { }", 3),
3874            ("for (a, (b, c)) in v { }", 5),
3875            ("for P { x, y } in ps { }", 1),
3876            ("for P { at: (x, y) } in ps { }", 4),
3877            ("for _ in v { }", 1),
3878        ] {
3879            assert_eq!(count(src, SyntaxKind::PATTERN), patterns, "{src}");
3880            assert_eq!(count(src, SyntaxKind::FOR_EXPR), 1, "{src}");
3881        }
3882
3883        // The pattern is followed by `in`, never by `{`, so a record pattern's
3884        // brace cannot be read as the loop body — and the iterator keeps its own
3885        // record-literal suppression.
3886        let out = parse_text("for P { x } in near(Origin { x: 0 }) { 0 }");
3887        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
3888
3889        // A missing `in`, and a missing pattern, both still report.
3890        for bad in ["for x v { }", "for in v { }", "for (x, in v { }"] {
3891            let out = parse_text(bad);
3892            assert!(!out.diagnostics.is_empty(), "{bad} must report");
3893        }
3894    }
3895
3896    /// **`min=` and `max=` are operators exactly where an identifier cannot be
3897    /// one**, and `min` is a name everywhere else.
3898    ///
3899    /// §6.2 writes `distance[key] min= candidate` and `best[key] max= score`.
3900    /// `min` is an `Ident`, so `min=` is two tokens the parser joins by context:
3901    /// a lexer rule that claimed `min=` would take `min` away from every program
3902    /// that calls the prelude helper (ADR-058), which §3.3's own program does.
3903    #[test]
3904    fn an_updating_store_is_an_operator_only_where_a_name_cannot_be() {
3905        let count = |src: &str, kind: SyntaxKind| -> usize {
3906            let out = parse_text(src);
3907            assert!(out.diagnostics.is_empty(), "{src}: {:?}", out.diagnostics);
3908            construct_names(&out.tree)
3909                .into_iter()
3910                .filter(|k| *k == kind)
3911                .count()
3912        };
3913
3914        // §6.2's own two lines, and the shapes around them: a computed index, a
3915        // computed value, and the operator inside a block.
3916        for src in [
3917            "distance[key] min= candidate",
3918            "best[key] max= score",
3919            "d[a + b] min= f(x)",
3920            "fn go() {\n  d[k] max= n\n}",
3921            "grid[x, y] min= 3",
3922        ] {
3923            assert_eq!(count(src, SyntaxKind::UPDATE_OP), 1, "{src}");
3924            assert_eq!(count(src, SyntaxKind::PLACE_ASSIGN_STMT), 1, "{src}");
3925        }
3926
3927        // The operator is **adjacent**, as `+=` is: with a space it is an
3928        // identifier followed by `=`, which is two statements run together and
3929        // reports as one.
3930        for spaced in ["d[k] min = 3", "d[k] max = 3"] {
3931            let out = parse_text(spaced);
3932            assert!(!out.diagnostics.is_empty(), "{spaced} must report");
3933        }
3934
3935        // `min` and `max` are ordinary names everywhere else — the whole reason
3936        // the rule is contextual.
3937        for src in [
3938            "var d = min(3, 4)",
3939            "var d = max(abs(a), abs(b))",
3940            "var m = min",
3941            "out(min(1, 2) + max(3, 4))",
3942            // …including as the receiver of a subscript, where the identifier is
3943            // followed by `[` and not by `=`.
3944            "var v = min[0]",
3945        ] {
3946            let out = parse_text(src);
3947            assert!(out.diagnostics.is_empty(), "{src}: {:?}", out.diagnostics);
3948            assert_eq!(count(src, SyntaxKind::UPDATE_OP), 0, "{src}");
3949        }
3950
3951        // `==` is one token by max-munch, so a comparison can never be read as an
3952        // update, and no other identifier gets the rule.
3953        for src in ["var r = d[k] == v", "var r = m[k] == 3"] {
3954            assert_eq!(count(src, SyntaxKind::UPDATE_OP), 0, "{src}");
3955        }
3956        let out = parse_text("d[k] mid= 3");
3957        assert!(
3958            !out.diagnostics.is_empty(),
3959            "only `min` and `max` are operators"
3960        );
3961
3962        // A target that is not a place still *parses*, exactly as `f() = 1`
3963        // does: naming no storage is inference's report and not the parser's.
3964        for src in ["x min= 1", "f() max= 1"] {
3965            let out = parse_text(src);
3966            assert!(out.diagnostics.is_empty(), "{src}: {:?}", out.diagnostics);
3967            assert_eq!(count(src, SyntaxKind::UPDATE_OP), 1, "{src}");
3968        }
3969
3970        // …and a missing value is a mistake, not an empty store.
3971        let out = parse_text("d[k] min=");
3972        assert!(!out.diagnostics.is_empty(), "a value is required");
3973    }
3974
3975    /// §9.8's `:bp` marker rides the same rule an updating store does, at the
3976    /// one other position where an identifier can decide an operator: the end of
3977    /// a statement, where a `:` begins nothing else.
3978    #[test]
3979    fn a_breakpoint_marker_is_a_marker_only_where_a_type_cannot_be() {
3980        let count = |src: &str, kind: SyntaxKind| -> usize {
3981            let out = parse_text(src);
3982            assert!(out.diagnostics.is_empty(), "{src}: {:?}", out.diagnostics);
3983            construct_names(&out.tree)
3984                .into_iter()
3985                .filter(|k| *k == kind)
3986                .count()
3987        };
3988
3989        // Every statement form takes one, and it lands inside that statement's
3990        // own node rather than beside it.
3991        for (src, stmt) in [
3992            ("var a = 1 :bp", SyntaxKind::VAR_STMT),
3993            ("var a: Int = 1 :bp", SyntaxKind::VAR_STMT),
3994            (
3995                "fn f() {\n  var a = 1\n  a = 2 :bp\n}",
3996                SyntaxKind::ASSIGN_STMT,
3997            ),
3998            ("var m = [1]\nm[0] = 2 :bp", SyntaxKind::PLACE_ASSIGN_STMT),
3999            (
4000                "var m = [1]\nm[0] min= 2 :bp",
4001                SyntaxKind::PLACE_ASSIGN_STMT,
4002            ),
4003            ("out(1) :bp", SyntaxKind::EXPR_STMT),
4004            ("{ }\n:bp", SyntaxKind::EXPR_STMT),
4005        ] {
4006            assert_eq!(count(src, SyntaxKind::BREAKPOINT), 1, "{src}");
4007            assert_eq!(count(src, stmt), 1, "{src}");
4008        }
4009
4010        // The marker is a *child of the statement*, which is what lets lowering
4011        // ask a statement node whether it carries one.
4012        let out = parse_text("var a = 1 :bp");
4013        let var_stmt = out
4014            .tree
4015            .descendants()
4016            .find(|n| n.kind() == SyntaxKind::VAR_STMT)
4017            .expect("a var statement");
4018        assert!(
4019            var_stmt
4020                .children()
4021                .any(|c| c.kind() == SyntaxKind::BREAKPOINT),
4022            "the marker is the statement's child"
4023        );
4024
4025        // Adjacent, exactly as `min=` is: with a space it is a `:` that begins
4026        // nothing, and the statement runs on.
4027        for spaced in ["var a = 1 : bp", "out(1) : bp"] {
4028            let out = parse_text(spaced);
4029            assert!(!out.diagnostics.is_empty(), "{spaced} must report");
4030        }
4031
4032        // `bp` is an ordinary name everywhere else — the whole reason the rule
4033        // is contextual — including as a binding, a call and a type annotation's
4034        // *value* position.
4035        for src in [
4036            "var bp = 1",
4037            "var a = bp",
4038            "fn bp() {\n  out(1)\n}",
4039            "var a: Int = 1\nvar bp = a",
4040            // The other `:`s in the grammar are inside declarations, where a
4041            // statement has not ended and this rule is never asked.
4042            "struct P { bp: Int }",
4043            "fn f(bp: Int) -> Int {\n  bp\n}",
4044        ] {
4045            let out = parse_text(src);
4046            assert!(out.diagnostics.is_empty(), "{src}: {:?}", out.diagnostics);
4047            assert_eq!(count(src, SyntaxKind::BREAKPOINT), 0, "{src}");
4048        }
4049
4050        // A marker on a nested statement belongs to *that* statement, not to the
4051        // block that contains it: the outer `EXPR_STMT` has no marker child.
4052        let out = parse_text("{\n  out(1) :bp\n}");
4053        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
4054        let outer = out
4055            .tree
4056            .children()
4057            .find(|n| n.kind() == SyntaxKind::EXPR_STMT)
4058            .expect("the block statement");
4059        assert!(
4060            !outer.children().any(|c| c.kind() == SyntaxKind::BREAKPOINT),
4061            "the inner statement's marker is not the outer statement's"
4062        );
4063    }
4064
4065    /// **A declaration's members are separated by a comma or a line break** —
4066    /// which is how §4.5 and §4.6 write their own. Match arms take either
4067    /// separator (D8, ADR-049); struct fields and enum variants are the same
4068    /// rule at the same kind of brace.
4069    #[test]
4070    fn a_declarations_members_take_a_comma_or_a_line_break() {
4071        // The design doc's own text, verbatim, and the comma form beside it: the
4072        // same tree once the comma token is out of the way, which is what "either
4073        // separator" means.
4074        for (breaks, commas) in [
4075            (
4076                "struct Point {\n    x: Int\n    y: Int\n}",
4077                "struct Point { x: Int, y: Int }",
4078            ),
4079            (
4080                "enum Tile {\n    Empty\n    Wall\n    Number(Int)\n    Portal(Text)\n}",
4081                "enum Tile { Empty, Wall, Number(Int), Portal(Text) }",
4082            ),
4083            // Mixed, in both orders — the two separators are interchangeable and
4084            // not two dialects.
4085            (
4086                "struct P {\n    x: Int, y: Int\n    z: Int\n}",
4087                "struct P { x: Int, y: Int, z: Int }",
4088            ),
4089            ("enum E {\n    A, B\n    C\n}", "enum E { A, B, C }"),
4090            // …and a trailing comma still closes the list, whichever preceded it.
4091            (
4092                "struct P {\n    x: Int\n    y: Int,\n}",
4093                "struct P { x: Int, y: Int }",
4094            ),
4095            ("enum E {\n    A\n    B,\n}", "enum E { A, B }"),
4096        ] {
4097            let out = parse_text(breaks);
4098            assert!(
4099                out.diagnostics.is_empty(),
4100                "{breaks}: {:?}",
4101                out.diagnostics
4102            );
4103            let filt = |t: &str| -> Vec<SyntaxKind> {
4104                construct_names(&parse_text(t).tree)
4105                    .into_iter()
4106                    .filter(|k| !k.is_trivia() && *k != SyntaxKind::COMMA)
4107                    .collect()
4108            };
4109            assert_eq!(filt(breaks), filt(commas), "{breaks}");
4110        }
4111
4112        // The rule is a separator, not "separators are optional": two members on
4113        // one line with neither is still a mistake.
4114        for bad in [
4115            "struct P { x: Int y: Int }",
4116            "enum E { A B }",
4117            "enum E { A(Int) B }",
4118        ] {
4119            let out = parse_text(bad);
4120            assert!(!out.diagnostics.is_empty(), "{bad} must report");
4121        }
4122
4123        // The shapes with no separator to give: an empty declaration and a
4124        // one-member one, on one line and across lines.
4125        for src in [
4126            "struct P { }",
4127            "enum E { }",
4128            "struct P { x: Int }",
4129            "struct P {\n    x: Int\n}",
4130            "enum E {\n    A\n}",
4131        ] {
4132            let out = parse_text(src);
4133            assert!(out.diagnostics.is_empty(), "{src}: {:?}", out.diagnostics);
4134        }
4135    }
4136
4137    /// **A record pattern and a tuple pattern are patterns wherever a pattern is
4138    /// legal**, and each carries its sub-patterns in a shape the rest of the
4139    /// compiler can read.
4140    ///
4141    /// The list of shapes matters more than any one of them. A record pattern's
4142    /// fields are `PATTERN_FIELD`s and a tuple's elements are bare `PATTERN`s, so
4143    /// `P { x }` never looks like `P(x)`, and `P { x: p }` and `P { x }` are one
4144    /// node shape with an optional child rather than two identifier-counting
4145    /// rules.
4146    #[test]
4147    fn a_record_pattern_names_fields_and_a_tuple_pattern_names_positions() {
4148        let count = |src: &str, kind: SyntaxKind| -> usize {
4149            let out = parse_text(src);
4150            assert!(out.diagnostics.is_empty(), "{src}: {:?}", out.diagnostics);
4151            construct_names(&out.tree)
4152                .into_iter()
4153                .filter(|k| *k == kind)
4154                .count()
4155        };
4156
4157        // A record pattern's fields, punned and explicit and mixed. The `{` is
4158        // unambiguous here where a record *literal*'s is not: a pattern is
4159        // followed by `=>`, never by a block.
4160        for (src, fields) in [
4161            ("var a = match p { P { x } => x }", 1),
4162            ("var a = match p { P { x, y } => x }", 2),
4163            ("var a = match p { P { x: 1, y } => y }", 2),
4164            ("var a = match p { P { x: q, y: r } => q }", 2),
4165            // A trailing comma closes this list too.
4166            ("var a = match p { P { x, y, } => x }", 2),
4167        ] {
4168            assert_eq!(count(src, SyntaxKind::PATTERN_FIELD), fields, "{src}");
4169        }
4170
4171        // A tuple pattern's elements are sub-patterns, at every arity and nested.
4172        // The counts include the arm's own outer pattern.
4173        for (src, patterns) in [
4174            ("var a = match t { (x, y) => x }", 3),
4175            ("var a = match t { (x, y, z) => x }", 4),
4176            ("var a = match t { (x, (y, z)) => x }", 5),
4177            ("var a = match t { (1, _) => 0, _ => 1 }", 4),
4178            // …and a trailing comma.
4179            ("var a = match t { (x, y,) => x }", 3),
4180        ] {
4181            assert_eq!(count(src, SyntaxKind::PATTERN), patterns, "{src}");
4182        }
4183
4184        // The two compose: a record field holding a tuple, a tuple element
4185        // holding a record, and a variant payload holding either.
4186        for src in [
4187            "var a = match p { P { at: (x, y) } => x }",
4188            "var a = match t { (P { x }, n) => x }",
4189            "var a = match o { Some(P { x, y }) => x, None => 0 }",
4190            "var a = match o { Some((x, y)) => x, None => 0 }",
4191        ] {
4192            let out = parse_text(src);
4193            assert!(out.diagnostics.is_empty(), "{src}: {:?}", out.diagnostics);
4194        }
4195
4196        // A tuple pattern must be able to *start* an arm, or the arm list stops
4197        // at it and every arm after it silently leaves the tree — which is what
4198        // `is_pattern_start` decides.
4199        let out = parse_text("var a = match t { (x, y) => x\n _ => 0 }");
4200        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
4201        assert_eq!(
4202            count(
4203                "var a = match t { (x, y) => x\n _ => 0 }",
4204                SyntaxKind::MATCH_ARM
4205            ),
4206            2,
4207            "both arms are in the tree"
4208        );
4209
4210        // The shapes that are not patterns. `()` has no type to match — `Unit` is
4211        // not a tuple — and a field with a `:` and nothing after it is a pattern
4212        // the program did not finish writing.
4213        for bad in [
4214            "var a = match u { () => 0 }",
4215            "var a = match p { P { x: } => 0 }",
4216            "var a = match p { P { , x } => 0 }",
4217            "var a = match p { P { x => 0 }",
4218            "var a = match t { (x, => 0 }",
4219        ] {
4220            let out = parse_text(bad);
4221            assert!(!out.diagnostics.is_empty(), "{bad} must report");
4222        }
4223    }
4224
4225    /// **A record pattern's head is optional** (ADR-091). It is the headed
4226    /// production with the head made optional, so it costs one arm in
4227    /// `parse_pattern`, one entry in `is_pattern_start`, and no new token.
4228    #[test]
4229    fn a_record_pattern_needs_no_head() {
4230        let count = |src: &str, kind: SyntaxKind| -> usize {
4231            let out = parse_text(src);
4232            assert!(out.diagnostics.is_empty(), "{src}: {:?}", out.diagnostics);
4233            construct_names(&out.tree)
4234                .into_iter()
4235                .filter(|k| *k == kind)
4236                .count()
4237        };
4238
4239        // The fields are the headed form's, unchanged — punned, explicit, mixed,
4240        // and with a trailing comma.
4241        for (src, fields) in [
4242            ("var a = match p { {x} => x }", 1),
4243            ("var a = match p { {x, y} => x }", 2),
4244            ("var a = match p { {x: 1, y} => y }", 2),
4245            ("var a = match p { {x: q, y: r} => q }", 2),
4246            ("var a = match p { {x, y,} => x }", 2),
4247        ] {
4248            assert_eq!(count(src, SyntaxKind::PATTERN_FIELD), fields, "{src}");
4249        }
4250
4251        // One production, so it composes in every position a pattern appears:
4252        // nested in a variant's payload (the shape a `choice(...)` payload record
4253        // needs), in a tuple, in a `for` header, and as a closure parameter.
4254        for src in [
4255            "var a = match m { Mul({x, y}) => x, Do(_) => 0 }",
4256            "var a = match t { ({x}, n) => x }",
4257            "var a = match p { {at: (x, y)} => x }",
4258            "for {x, y} in ps { out(x) }",
4259            "var f = |{x, y}| x + y",
4260        ] {
4261            let out = parse_text(src);
4262            assert!(out.diagnostics.is_empty(), "{src}: {:?}", out.diagnostics);
4263        }
4264
4265        // A headless pattern must be able to *start* an arm, or the arm list
4266        // stops before it and it — with every arm after it — silently leaves the
4267        // tree. The headless arm is written **second** on purpose: the first
4268        // arm's pattern is parsed unconditionally, so only a later one exercises
4269        // `is_pattern_start`.
4270        assert_eq!(
4271            count(
4272                "var a = match p { _ => 0\n {x, y} => x }",
4273                SyntaxKind::MATCH_ARM
4274            ),
4275            2,
4276            "both arms are in the tree"
4277        );
4278
4279        // `{}` is rejected where `()` is, and for the same reason (ADR-091
4280        // Decision 3): it binds nothing and names no record, so it is an
4281        // irrefutable arm written by accident. The pattern that matches
4282        // everything is spelled `_`.
4283        let out = parse_text("var a = match p { {} => 0 }");
4284        assert!(
4285            out.diagnostics
4286                .iter()
4287                .any(|d| d.message().contains("expected a pattern")),
4288            "an empty headless record pattern must report: {:?}",
4289            out.diagnostics
4290        );
4291
4292        // …but a *headed* `P {}` is kept: it names the record it tests for, so it
4293        // is refutable — `Some` beside `Some(_)`.
4294        let out = parse_text("var a = match p { P {} => 0 }");
4295        assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics);
4296    }
4297
4298    /// **A `(` that begins a line begins something new**; a `(` on the same line
4299    /// as the expression before it is that expression's argument list.
4300    ///
4301    /// `peek()` skips trivia and a newline **is** trivia, so without the rule the
4302    /// postfix loop and `parse_name_or_call` would open a `CALL_EXPR` on a
4303    /// line-leading `(`. In a `match` that is silent data loss: the arm body
4304    /// swallows the next arm's tuple pattern as an argument list, the arm loop
4305    /// finds no pattern start, and every arm after the first leaves the tree.
4306    #[test]
4307    fn a_line_leading_paren_begins_a_new_thing_and_a_same_line_one_is_a_call() {
4308        let count = |src: &str, kind: SyntaxKind| -> usize {
4309            let out = parse_text(src);
4310            assert!(out.diagnostics.is_empty(), "{src}: {:?}", out.diagnostics);
4311            construct_names(&out.tree)
4312                .into_iter()
4313                .filter(|k| *k == kind)
4314                .count()
4315        };
4316
4317        // Three arms, and all three are in the tree. A `10(a, b)` call would
4318        // leave one arm and one `CALL_EXPR`.
4319        let arms = "var r = match p {\n    (0, 0) => 10\n    (a, b) => a + b\n    _ => 0\n}";
4320        assert_eq!(count(arms, SyntaxKind::MATCH_ARM), 3);
4321        assert_eq!(count(arms, SyntaxKind::CALL_EXPR), 0);
4322
4323        // The same shape after every kind of arm body a `(` could attach itself
4324        // to — a name, a call, a subscript, a field read, a block.
4325        for body in ["n", "f(1)", "m[k]", "p.x", "{ 0 }"] {
4326            let src = format!("var r = match p {{\n    _ => {body}\n    (a, b) => 1\n}}");
4327            assert_eq!(count(&src, SyntaxKind::MATCH_ARM), 2, "{src}");
4328        }
4329
4330        // The other direction, which is the rule's whole content: on one line a
4331        // `(` still opens an argument list, through every callee shape — a name,
4332        // a call's result, a subscript's, a paren, a closure.
4333        for src in [
4334            "var a = f(1)",
4335            "var a = f(1)(2)",
4336            "var a = m[k](7)",
4337            "var a = (g)(3)",
4338            "var a = (|x| x * 3)(14)",
4339            "var a = fs.get(0)(100)",
4340        ] {
4341            assert!(count(src, SyntaxKind::CALL_EXPR) >= 1, "{src}");
4342        }
4343
4344        // A `[` is subject to the same rule, because a list literal begins with
4345        // one: `m\n[k]` is a binding and a list, not a subscript.
4346        let sub = "var a = m\n[k]";
4347        assert_eq!(count(sub, SyntaxKind::INDEX_EXPR), 0);
4348        assert_eq!(count(sub, SyntaxKind::LIST_EXPR), 1);
4349        assert_eq!(count(sub, SyntaxKind::VAR_STMT), 1);
4350        // On one line it is a subscript.
4351        assert_eq!(count("var a = m[k]", SyntaxKind::INDEX_EXPR), 1);
4352        assert_eq!(count("var a = m[k]", SyntaxKind::LIST_EXPR), 0);
4353
4354        // Nor is the Pratt loop (ADR-049 D8): an operator that ends a line still
4355        // continues across it, and so does a `.method()` chain.
4356        assert_eq!(count("var a = 1 +\n2", SyntaxKind::BIN_EXPR), 1);
4357        assert_eq!(
4358            count("var a = v\n  .len()", SyntaxKind::METHOD_CALL_EXPR),
4359            1
4360        );
4361        assert_eq!(
4362            count(
4363                "var a = v\n  .map(f)\n  .sum()",
4364                SyntaxKind::METHOD_CALL_EXPR
4365            ),
4366            2
4367        );
4368
4369        // A `(` that *opens* an expression is untouched wherever it appears —
4370        // only a `(` asked to continue one is.
4371        assert_eq!(count("var a = 1\n(b, c)", SyntaxKind::TUPLE_EXPR), 1);
4372        assert_eq!(count("var a = 1\n(b, c)", SyntaxKind::VAR_STMT), 1);
4373        assert_eq!(count("var a = 1\n(b + c) * 2", SyntaxKind::PAREN_EXPR), 1);
4374
4375        // …and a `for` binding is the second place a tuple pattern makes a
4376        // line-leading `(` reachable.
4377        assert_eq!(
4378            count("var a = 1\nfor (k, v) in m { }", SyntaxKind::FOR_EXPR),
4379            1
4380        );
4381
4382        // The cost, stated as a test rather than left to be discovered: a callee
4383        // that ends a line and an argument list that begins the next are two
4384        // expressions, so this is two statements and not one call.
4385        let split = "var a = f\n(1)";
4386        assert_eq!(count(split, SyntaxKind::CALL_EXPR), 0);
4387        assert_eq!(count(split, SyntaxKind::PAREN_EXPR), 1);
4388    }
4389
4390    /// **`||` is an empty parameter list where an expression must begin, and
4391    /// logical-or everywhere else.** The lexer's max-munch makes it one token,
4392    /// so position is the only thing that can tell the two apart — §4.2's
4393    /// shadowing example is `var show_old = || out(a)`.
4394    #[test]
4395    fn a_double_pipe_is_a_closure_where_an_expression_begins_and_an_operator_between_two() {
4396        let count = |src: &str, kind: SyntaxKind| -> usize {
4397            let out = parse_text(src);
4398            assert!(out.diagnostics.is_empty(), "{src}: {:?}", out.diagnostics);
4399            construct_names(&out.tree)
4400                .into_iter()
4401                .filter(|k| *k == kind)
4402                .count()
4403        };
4404
4405        // §4.2's own line, and the zero-parameter closure in every position an
4406        // expression begins: a binding, an argument, a block tail, a `return`, an
4407        // operand of the very operator it is spelled like, and its own body.
4408        for src in [
4409            "var show_old = || out(a)",
4410            "var f = || 5",
4411            "out(|| 5)",
4412            "fn g() { || 5 }",
4413            "fn g() { return || 5 }",
4414            "var f = a || || 5",
4415            "var f = || || 7",
4416            "var f = if p { || 1 } else { || 2 }",
4417        ] {
4418            assert!(count(src, SyntaxKind::CLOSURE_EXPR) >= 1, "{src}");
4419            assert_eq!(count(src, SyntaxKind::PARAM), 0, "{src}");
4420        }
4421
4422        // `| |` with a space is the same closure — the two spellings differ only
4423        // in how the lexer munched them.
4424        assert_eq!(count("var f = | | 5", SyntaxKind::CLOSURE_EXPR), 1);
4425
4426        // The other direction: between two operands `||` is logical-or, whose
4427        // precedence is the lowest of all — below `..` and `&&`.
4428        assert_eq!(count("var a = p || q", SyntaxKind::BIN_EXPR), 1);
4429        assert_eq!(count("var a = p || q", SyntaxKind::CLOSURE_EXPR), 0);
4430        assert_eq!(
4431            shape("var a = p || q && r"),
4432            shape("var a = p || (q && r)"),
4433            "`&&` still binds tighter than `||`"
4434        );
4435        assert_eq!(
4436            shape("var a = p == q || r == s"),
4437            shape("var a = (p == q) || (r == s)"),
4438            "comparison still binds tighter than `||`"
4439        );
4440        assert_eq!(
4441            shape("var a = p || q || r"),
4442            shape("var a = (p || q) || r"),
4443            "`||` is still left-associative"
4444        );
4445
4446        // A one-parameter closure is untouched, which is what says the
4447        // zero-parameter arm only fires on the two-pipe token.
4448        assert_eq!(count("var f = |x| x", SyntaxKind::PARAM), 1);
4449    }
4450
4451    /// **A closure parameter is a pattern, not a bare name** — Appendix D writes
4452    /// `|(a, b)| abs(a - b)`. It is the same grammar the `for` binding uses, at
4453    /// the third and last binding position.
4454    #[test]
4455    fn a_closure_parameter_is_a_pattern() {
4456        let count = |src: &str, kind: SyntaxKind| -> usize {
4457            let out = parse_text(src);
4458            assert!(out.diagnostics.is_empty(), "{src}: {:?}", out.diagnostics);
4459            construct_names(&out.tree)
4460                .into_iter()
4461                .filter(|k| *k == kind)
4462                .count()
4463        };
4464
4465        // Appendix D's own line.
4466        assert_eq!(
4467            count(
4468                "var d = left.zip(right).map(|(a, b)| abs(a - b)).sum()",
4469                SyntaxKind::CLOSURE_EXPR
4470            ),
4471            1
4472        );
4473
4474        // The shapes, and the pattern count each holds: the parameter's own, plus
4475        // one per element or nested field.
4476        for (src, params, patterns) in [
4477            ("var f = |x| x", 1, 1),
4478            ("var f = |_| 0", 1, 1),
4479            ("var f = |(a, b)| a", 1, 3),
4480            ("var f = |(a, (b, c))| a", 1, 5),
4481            ("var f = |P { x, y }| x", 1, 1),
4482            ("var f = |P { at: (x, y) }| x", 1, 4),
4483            ("var f = |(a, b), c| a", 2, 4),
4484            ("var f = |a, (b, c)| a", 2, 4),
4485            ("var f = | | 0", 0, 0),
4486        ] {
4487            assert_eq!(count(src, SyntaxKind::PARAM), params, "{src}");
4488            assert_eq!(count(src, SyntaxKind::PATTERN), patterns, "{src}");
4489            assert_eq!(count(src, SyntaxKind::CLOSURE_EXPR), 1, "{src}");
4490        }
4491
4492        // A pattern parameter still takes an annotation, and the annotation is the
4493        // whole argument's — the `:` is what ends the pattern.
4494        assert_eq!(
4495            count("var f = |(a, b): (Int, Int)| a", SyntaxKind::TUPLE_TYPE),
4496            1
4497        );
4498        assert_eq!(count("var f = |x: Int| x", SyntaxKind::TYPE_REF), 1);
4499
4500        // A trailing comma still closes the list, and a record pattern's brace
4501        // is not read as anything else: a parameter is followed by `,`, `:` or
4502        // `|`, never by an expression.
4503        for src in ["var f = |(a, b),| a", "var f = |P { x }| P { x: x }"] {
4504            assert_eq!(count(src, SyntaxKind::CLOSURE_EXPR), 1, "{src}");
4505        }
4506
4507        // The malformed shapes still report.
4508        for bad in [
4509            "var f = |(a, | a",
4510            "var f = |(| a",
4511            "var f = |+| a",
4512            "var f = |a, | ",
4513        ] {
4514            let out = parse_text(bad);
4515            assert!(!out.diagnostics.is_empty(), "{bad} must report");
4516        }
4517    }
4518
4519    /// The construct shape of `text` with parentheses erased, so two spellings
4520    /// that differ only by explicit grouping compare equal exactly when they
4521    /// parse to the same tree. Comparing the raw kind lists could not: the
4522    /// parenthesized form has `PAREN_EXPR`, `L_PAREN` and `R_PAREN` in it.
4523    fn shape(text: &str) -> Vec<SyntaxKind> {
4524        let out = parse_text(text);
4525        assert!(out.diagnostics.is_empty(), "{text}: {:?}", out.diagnostics);
4526        construct_names(&out.tree)
4527            .into_iter()
4528            .filter(|k| {
4529                !k.is_trivia()
4530                    && !matches!(
4531                        k,
4532                        SyntaxKind::PAREN_EXPR | SyntaxKind::L_PAREN | SyntaxKind::R_PAREN
4533                    )
4534            })
4535            .collect()
4536    }
4537
4538    fn construct_names(node: &SyntaxNode) -> Vec<SyntaxKind> {
4539        let mut out = Vec::new();
4540        collect(node, &mut out);
4541        return out;
4542        fn collect(node: &SyntaxNode, out: &mut Vec<SyntaxKind>) {
4543            out.push(node.kind());
4544            for child in node.children_with_tokens() {
4545                match child {
4546                    rowan::NodeOrToken::Node(n) => collect(&n, out),
4547                    rowan::NodeOrToken::Token(t) => out.push(t.kind()),
4548                }
4549            }
4550        }
4551    }
4552}