Skip to main content

mimium_lang/compiler/parser/
cst_parser.rs

1/// CST Parser - parses token indices into Green Tree
2/// This is a simple recursive descent parser that produces a CST
3use super::green::{GreenNodeArena, GreenNodeId, GreenTreeBuilder, Marker, SyntaxKind};
4use super::preparser::PreParsedTokens;
5use super::token::{Token, TokenKind};
6use thiserror::Error;
7
8/// Error detail - structured error information
9#[derive(Debug, Clone, PartialEq, Eq, Error)]
10pub enum ErrorDetail {
11    /// Expected token not found (e.g., expected `)` but found `}`)
12    #[error("Expected {expected}, found {found}")]
13    UnexpectedToken { expected: String, found: String },
14    /// Unexpected end of input
15    #[error("Unexpected end of input, expected {expected}")]
16    UnexpectedEof { expected: String },
17    /// Invalid syntax with reason
18    #[error("Invalid syntax: {reason}")]
19    InvalidSyntax { reason: String },
20}
21
22/// Parser error with recovery information
23#[derive(Debug, Clone, PartialEq, Eq, Error)]
24#[error("{detail}")]
25pub struct ParserError {
26    /// Token index where the error occurred
27    pub token_index: usize,
28    /// Error detail
29    pub detail: ErrorDetail,
30}
31
32impl ParserError {
33    pub fn unexpected_token(token_index: usize, expected: &str, found: &str) -> Self {
34        Self {
35            token_index,
36            detail: ErrorDetail::UnexpectedToken {
37                expected: expected.to_string(),
38                found: found.to_string(),
39            },
40        }
41    }
42
43    pub fn unexpected_eof(token_index: usize, expected: &str) -> Self {
44        Self {
45            token_index,
46            detail: ErrorDetail::UnexpectedEof {
47                expected: expected.to_string(),
48            },
49        }
50    }
51
52    pub fn invalid_syntax(token_index: usize, reason: &str) -> Self {
53        Self {
54            token_index,
55            detail: ErrorDetail::InvalidSyntax {
56                reason: reason.to_string(),
57            },
58        }
59    }
60}
61
62/// Helper trait to reduce node boilerplate
63trait NodeBuilder {
64    fn emit_node<F>(&mut self, kind: SyntaxKind, f: F)
65    where
66        F: FnOnce(&mut Self);
67}
68
69impl NodeBuilder for Parser<'_> {
70    fn emit_node<F>(&mut self, kind: SyntaxKind, f: F)
71    where
72        F: FnOnce(&mut Self),
73    {
74        self.builder.start_node(kind);
75        f(self);
76        self.builder.finish_node();
77    }
78}
79/// Parser state
80pub struct Parser<'a> {
81    tokens: Vec<Token>,
82    preparsed: &'a PreParsedTokens,
83    current: usize, // Index into preparsed.token_indices
84    builder: GreenTreeBuilder,
85    /// Collected parser errors for error recovery
86    errors: Vec<ParserError>,
87}
88
89/// Maximum lookahead distance for disambiguation
90const MAX_LOOKAHEAD: usize = 20;
91
92impl<'a> Parser<'a> {
93    pub fn new(tokens: Vec<Token>, preparsed: &'a PreParsedTokens) -> Self {
94        Self {
95            tokens,
96            preparsed,
97            current: 0,
98            builder: GreenTreeBuilder::new(),
99            errors: Vec::new(),
100        }
101    }
102
103    /// Record a parser error
104    fn add_error(&mut self, error: ParserError) {
105        self.errors.push(error);
106    }
107
108    /// Get the current token index in the original token array
109    fn current_token_index(&self) -> usize {
110        self.preparsed
111            .token_indices
112            .get(self.current)
113            .copied()
114            .unwrap_or(0)
115    }
116
117    /// Get the current token kind
118    fn peek(&self) -> Option<TokenKind> {
119        self.preparsed
120            .get_token(self.current, &self.tokens)
121            .map(|t| t.kind)
122    }
123
124    /// Peek ahead n tokens
125    fn peek_ahead(&self, n: usize) -> Option<TokenKind> {
126        self.preparsed
127            .get_token(self.current + n, &self.tokens)
128            .map(|t| t.kind)
129    }
130
131    /// Check if current position starts a path (possibly qualified) followed by macro expansion (!)
132    /// Returns the offset where MacroExpand is found, or None if not a macro expansion
133    fn find_macro_expand_after_path(&self) -> Option<usize> {
134        let mut offset = 0;
135
136        // First must be Ident
137        if self.peek_ahead(offset) != Some(TokenKind::Ident) {
138            return None;
139        }
140        offset += 1;
141
142        // Consume :: Ident pairs
143        while self.peek_ahead(offset) == Some(TokenKind::DoubleColon) {
144            offset += 1; // ::
145            if self.peek_ahead(offset) != Some(TokenKind::Ident) {
146                return None; // Malformed path
147            }
148            offset += 1; // Ident
149        }
150
151        // Check if followed by MacroExpand
152        if self.peek_ahead(offset) == Some(TokenKind::MacroExpand) {
153            Some(offset)
154        } else {
155            None
156        }
157    }
158
159    /// Get the current token
160    #[allow(dead_code)]
161    fn current_token(&self) -> Option<&Token> {
162        self.preparsed.get_token(self.current, &self.tokens)
163    }
164
165    /// Check if current token matches the expected kind
166    fn check(&self, kind: TokenKind) -> bool {
167        self.peek() == Some(kind)
168    }
169
170    /// Advance to the next token and add current to the tree
171    fn bump(&mut self) {
172        if let Some(&token_idx) = self.preparsed.token_indices.get(self.current)
173            && let Some(token) = self.tokens.get(token_idx)
174        {
175            self.builder.add_token(token_idx, token.length);
176        }
177        self.current += 1;
178    }
179
180    /// Expect a specific token kind and consume it
181    /// Returns true if the token matched, false otherwise (and records error)
182    fn expect(&mut self, kind: TokenKind) -> bool {
183        if self.check(kind) {
184            self.bump();
185            true
186        } else if self.is_at_end() {
187            self.add_error(ParserError::unexpected_eof(
188                self.current_token_index(),
189                &format!("{kind:?}"),
190            ));
191            false
192        } else {
193            let found = self.peek().map(|k| format!("{k:?}")).unwrap_or_default();
194            self.add_error(ParserError::unexpected_token(
195                self.current_token_index(),
196                &format!("{kind:?}"),
197                &found,
198            ));
199            false
200        }
201    }
202
203    /// Expect any of the given token kinds and consume the first match.
204    /// Returns true if matched; otherwise records an error and returns false.
205    fn expects(&mut self, kinds: &[TokenKind]) -> bool {
206        let expected = kinds
207            .iter()
208            .map(|k| format!("{k:?}"))
209            .collect::<Vec<_>>()
210            .join(" or ");
211
212        match self.peek() {
213            Some(kind) if kinds.contains(&kind) => {
214                self.bump();
215                true
216            }
217            Some(found) => {
218                self.add_error(ParserError::unexpected_token(
219                    self.current_token_index(),
220                    &expected,
221                    &format!("{found:?}"),
222                ));
223                false
224            }
225            None => {
226                self.add_error(ParserError::unexpected_eof(
227                    self.current_token_index(),
228                    &expected,
229                ));
230                false
231            }
232        }
233    }
234
235    /// Expect all token kinds in order, consuming each. Returns true if all matched.
236    fn expect_all(&mut self, kinds: &[TokenKind]) -> bool {
237        kinds
238            .iter()
239            .fold(true, |acc, &kind| acc & self.expect(kind))
240    }
241
242    /// Parse the entire program
243    pub fn parse(mut self) -> (GreenNodeId, GreenNodeArena, Vec<Token>, Vec<ParserError>) {
244        self.builder.start_node(SyntaxKind::Program);
245
246        while !self.is_at_end() {
247            let before = self.current;
248            self.parse_statement();
249            if self.current == before && !self.is_at_end() {
250                self.add_error(ParserError::invalid_syntax(
251                    self.current_token_index(),
252                    "parser made no progress while parsing statement; skipping token for recovery",
253                ));
254                self.bump();
255            }
256        }
257
258        let root_id = self.builder.finish_node().unwrap();
259        let arena = self.builder.into_arena();
260        let errors = self.errors;
261        (root_id, arena, self.tokens, errors)
262    }
263
264    /// Check if we've reached the end
265    fn is_at_end(&self) -> bool {
266        self.peek().is_none_or(|k| k == TokenKind::Eof)
267    }
268
269    /// Check if the previous token has a linebreak in its trailing trivia
270    fn has_trailing_linebreak(&self) -> bool {
271        if self.current == 0 {
272            return false;
273        }
274        let prev_idx = self.current - 1;
275        if let Some(trivia_indices) = self.preparsed.trailing_trivia_map.get(&prev_idx) {
276            for &trivia_idx in trivia_indices {
277                if let Some(token) = self.tokens.get(trivia_idx)
278                    && token.kind == TokenKind::LineBreak
279                {
280                    return true;
281                }
282            }
283        }
284        if let Some(trivia_indices) = self.preparsed.leading_trivia_map.get(&self.current) {
285            for &trivia_idx in trivia_indices {
286                if let Some(token) = self.tokens.get(trivia_idx)
287                    && token.kind == TokenKind::LineBreak
288                {
289                    return true;
290                }
291            }
292        }
293        false
294    }
295
296    /// Get previous token kind if there is no trivia between previous and current tokens.
297    fn prev_kind_if_adjacent(&self) -> Option<TokenKind> {
298        if self.current == 0 {
299            return None;
300        }
301        let curr_raw = *self.preparsed.token_indices.get(self.current)?;
302        let prev_raw = *self.preparsed.token_indices.get(self.current - 1)?;
303        if curr_raw == prev_raw + 1 {
304            self.tokens.get(prev_raw).map(|t| t.kind)
305        } else {
306            None
307        }
308    }
309
310    /// Parse a statement
311    fn parse_statement(&mut self) {
312        self.emit_node(SyntaxKind::Statement, |this| {
313            // Check for visibility modifier first
314            let has_pub = this.check(TokenKind::Pub);
315            if has_pub {
316                this.emit_node(SyntaxKind::VisibilityPub, |this| {
317                    this.bump(); // consume 'pub'
318                });
319            }
320
321            match this.peek() {
322                Some(TokenKind::Function) => this.parse_function_decl(),
323                Some(TokenKind::Macro) => this.parse_macro_decl(),
324                Some(TokenKind::Let) => this.parse_let_decl(),
325                Some(TokenKind::LetRec) => this.parse_letrec_decl(),
326                Some(TokenKind::Include) => this.parse_include_stmt(),
327                Some(TokenKind::Sharp) => this.parse_stage_decl(),
328                Some(TokenKind::Mod) => this.parse_module_decl(),
329                Some(TokenKind::Use) => this.parse_use_stmt(),
330                Some(TokenKind::Type) => {
331                    // Check if this is "type alias", "type rec", or just "type"
332                    if this.peek_ahead(1) == Some(TokenKind::Alias) {
333                        this.parse_type_alias_decl();
334                    } else {
335                        this.parse_type_decl();
336                    }
337                }
338                _ => {
339                    // Try parsing as expression
340                    this.parse_expr();
341                }
342            }
343        });
344    }
345
346    /// Parse module declaration: mod name { ... } or mod name;
347    fn parse_module_decl(&mut self) {
348        self.emit_node(SyntaxKind::ModuleDecl, |this| {
349            this.expect(TokenKind::Mod);
350            this.expect(TokenKind::Ident);
351
352            // Check if this is an external file module (mod foo;) or inline module (mod foo { ... })
353            if this.check(TokenKind::LineBreak) {
354                // External file module: mod foo;
355                // Just consume the line break, no body
356                this.bump();
357            } else if this.check(TokenKind::BlockBegin) {
358                // Inline module: mod foo { ... }
359                this.expect(TokenKind::BlockBegin);
360
361                // Parse module body (statements)
362                while !this.check(TokenKind::BlockEnd) && !this.is_at_end() {
363                    let before = this.current;
364                    this.parse_statement();
365
366                    if this.current == before && !this.is_at_end() {
367                        this.add_error(ParserError::invalid_syntax(
368                            this.current_token_index(),
369                            "parser made no progress in module; skipping token for recovery",
370                        ));
371                        this.bump();
372                    }
373                }
374
375                this.expect(TokenKind::BlockEnd);
376            }
377            // If neither, error recovery will handle it
378        });
379    }
380
381    /// Parse use statement:
382    /// - `use path::to::module` (single import)
383    /// - `use path::{a, b, c}` (multiple imports)
384    /// - `use path::*` (wildcard import)
385    fn parse_use_stmt(&mut self) {
386        self.emit_node(SyntaxKind::UseStmt, |this| {
387            this.expect(TokenKind::Use);
388            this.parse_use_path();
389        });
390    }
391
392    /// Parse use path with support for multiple/wildcard imports
393    fn parse_use_path(&mut self) {
394        self.emit_node(SyntaxKind::QualifiedPath, |this| {
395            this.expect(TokenKind::Ident);
396            while this.check(TokenKind::DoubleColon) {
397                this.bump(); // consume '::'
398
399                // Check for wildcard: use foo::*
400                if this.check(TokenKind::OpProduct) {
401                    this.emit_node(SyntaxKind::UseTargetWildcard, |this2| {
402                        this2.bump(); // consume '*'
403                    });
404                    return;
405                }
406
407                // Check for multiple imports: use foo::{a, b}
408                if this.check(TokenKind::BlockBegin) {
409                    this.emit_node(SyntaxKind::UseTargetMultiple, |this2| {
410                        this2.bump(); // consume '{'
411                        // Parse comma-separated identifiers
412                        if this2.check(TokenKind::Ident) {
413                            this2.bump();
414                            while this2.check(TokenKind::Comma) {
415                                this2.bump(); // consume ','
416                                this2.expect(TokenKind::Ident);
417                            }
418                        }
419                        this2.expect(TokenKind::BlockEnd);
420                    });
421                    return;
422                }
423
424                // Regular identifier
425                this.expect(TokenKind::Ident);
426            }
427        });
428    }
429
430    /// Parse qualified path: ident (:: ident)*
431    fn parse_qualified_path(&mut self) {
432        self.emit_node(SyntaxKind::QualifiedPath, |this| {
433            this.expect(TokenKind::Ident);
434            while this.check(TokenKind::DoubleColon) {
435                this.bump(); // consume '::'
436                this.expect(TokenKind::Ident);
437            }
438        });
439    }
440
441    /// Parse macro declaration: macro name(params) { body }
442    fn parse_macro_decl(&mut self) {
443        self.emit_node(SyntaxKind::FunctionDecl, |this| {
444            this.expect(TokenKind::Macro);
445
446            // Mark macro name with IdentFunction kind
447            if this.check(TokenKind::Ident) {
448                if let Some(&token_idx) = this.preparsed.token_indices.get(this.current) {
449                    this.tokens[token_idx].kind = TokenKind::IdentFunction;
450                }
451                this.bump();
452            }
453
454            // Parameters
455            if this.check(TokenKind::ParenBegin) {
456                this.parse_param_list();
457            }
458
459            // Optional return type annotation after '->'
460            if this.check(TokenKind::Arrow) {
461                this.bump();
462                this.parse_type();
463            }
464
465            // Body
466            if this.check(TokenKind::BlockBegin) {
467                this.parse_block_expr();
468            }
469        });
470    }
471
472    /// Parse include statement: include("filename.mmm")
473    fn parse_include_stmt(&mut self) {
474        self.emit_node(SyntaxKind::IncludeStmt, |this| {
475            this.expect_all(&[
476                TokenKind::Include,
477                TokenKind::ParenBegin,
478                TokenKind::Str,
479                TokenKind::ParenEnd,
480            ]);
481        });
482    }
483
484    /// Parse stage declaration: #stage(main) or #stage(macro)
485    fn parse_stage_decl(&mut self) {
486        self.emit_node(SyntaxKind::StageDecl, |this| {
487            this.expect_all(&[TokenKind::Sharp, TokenKind::StageKwd, TokenKind::ParenBegin]);
488
489            // Expect either 'main' or 'macro'
490            this.expects(&[TokenKind::Main, TokenKind::Macro]);
491
492            this.expect(TokenKind::ParenEnd);
493        });
494    }
495
496    /// Parse macro expansion: MacroName!(args) or Qualified::Path!(args)
497    fn parse_macro_expansion(&mut self) {
498        self.emit_node(SyntaxKind::MacroExpansion, |this| {
499            // Parse the macro name which can be a simple identifier or qualified path
500            // Check if it's a qualified path
501            if this.peek_ahead(1) == Some(TokenKind::DoubleColon) {
502                this.parse_qualified_path();
503            } else {
504                this.expect(TokenKind::Ident);
505            }
506
507            // '!('
508            this.expect_all(&[TokenKind::MacroExpand, TokenKind::ParenBegin]);
509
510            // Parse arguments as expression list
511            if !this.check(TokenKind::ParenEnd) {
512                this.parse_expr();
513                while this.check(TokenKind::Comma) {
514                    this.bump();
515                    if !this.check(TokenKind::ParenEnd) {
516                        this.parse_expr();
517                    }
518                }
519            }
520
521            this.expect(TokenKind::ParenEnd);
522        });
523    }
524
525    /// Parse bracket (quote) expression: `expr or `{block}
526    fn parse_bracket_expr(&mut self) {
527        self.emit_node(SyntaxKind::BracketExpr, |this| {
528            this.expect(TokenKind::BackQuote);
529
530            // Check if it's a block or single expression
531            if this.check(TokenKind::BlockBegin) {
532                this.parse_block_expr();
533            } else {
534                this.parse_expr();
535            }
536        });
537    }
538
539    /// Parse escape (unquote) expression: $expr
540    fn parse_escape_expr(&mut self) {
541        self.emit_node(SyntaxKind::EscapeExpr, |this| {
542            this.expect(TokenKind::Dollar);
543            this.parse_prefix_expr();
544        });
545    }
546
547    /// Parse function declaration: fn name(params) { body }
548    fn parse_function_decl(&mut self) {
549        self.emit_node(SyntaxKind::FunctionDecl, |this| {
550            this.expect(TokenKind::Function);
551
552            // Mark function name with IdentFunction kind
553            if this.check(TokenKind::Ident) {
554                if let Some(&token_idx) = this.preparsed.token_indices.get(this.current) {
555                    this.tokens[token_idx].kind = TokenKind::IdentFunction;
556                }
557                this.bump();
558            }
559
560            // Parameters
561            if this.check(TokenKind::ParenBegin) {
562                this.parse_param_list();
563            }
564
565            // Optional return type annotation after '->'
566            if this.check(TokenKind::Arrow) {
567                this.bump();
568                this.parse_type();
569            }
570
571            // Body
572            if this.check(TokenKind::BlockBegin) {
573                this.parse_block_expr();
574            }
575        });
576    }
577
578    /// Parse let declaration: let pattern = expr
579    fn parse_let_decl(&mut self) {
580        self.emit_node(SyntaxKind::LetDecl, |this| {
581            this.expect(TokenKind::Let);
582            this.parse_pattern(); // Parse pattern instead of just identifier
583
584            // Optional type annotation after ':'
585            if this.check(TokenKind::Colon) {
586                this.parse_type_annotation();
587            }
588
589            if this.expect(TokenKind::Assign) {
590                this.parse_expr();
591            }
592        });
593    }
594
595    /// Parse letrec declaration
596    fn parse_letrec_decl(&mut self) {
597        self.emit_node(SyntaxKind::LetRecDecl, |this| {
598            this.expect_all(&[TokenKind::LetRec, TokenKind::Ident]);
599
600            if this.expect(TokenKind::Assign) {
601                this.parse_expr();
602            }
603        });
604    }
605
606    /// Parse pattern: single identifier, tuple pattern, or record pattern
607    fn parse_pattern(&mut self) {
608        match self.peek() {
609            Some(TokenKind::Ident) | Some(TokenKind::PlaceHolder) => {
610                // Single pattern: x or _
611                self.emit_node(SyntaxKind::SinglePattern, |this| {
612                    this.bump();
613                });
614            }
615            Some(TokenKind::ParenBegin) => {
616                // Tuple pattern: (x, y, z)
617                self.parse_tuple_pattern();
618            }
619            Some(TokenKind::BlockBegin) => {
620                // Record pattern: {a = x, b = y}
621                self.parse_record_pattern();
622            }
623            Some(TokenKind::BackQuote) => {
624                // Code type: `Type
625                self.emit_node(SyntaxKind::CodeType, |inner| {
626                    inner.expect(TokenKind::BackQuote);
627                    inner.parse_type();
628                });
629            }
630            _ => {
631                // Error: unexpected token
632                if let Some(kind) = self.peek() {
633                    self.add_error(ParserError::unexpected_token(
634                        self.current_token_index(),
635                        "pattern (identifier, tuple, or record)",
636                        &format!("{kind:?}"),
637                    ));
638                }
639                self.bump();
640            }
641        }
642    }
643
644    /// Parse tuple pattern: (x, y, z)
645    fn parse_tuple_pattern(&mut self) {
646        self.emit_node(SyntaxKind::TuplePattern, |this| {
647            this.expect(TokenKind::ParenBegin);
648
649            if !this.check(TokenKind::ParenEnd) {
650                this.parse_pattern();
651
652                while this.check(TokenKind::Comma) {
653                    this.bump(); // ,
654                    if !this.check(TokenKind::ParenEnd) {
655                        this.parse_pattern();
656                    }
657                }
658            }
659
660            this.expect(TokenKind::ParenEnd);
661        });
662    }
663
664    /// Parse record pattern: {a = x, b = y}
665    fn parse_record_pattern(&mut self) {
666        self.emit_node(SyntaxKind::RecordPattern, |this| {
667            this.expect(TokenKind::BlockBegin);
668
669            if !this.check(TokenKind::BlockEnd) {
670                // Parse field: name = pattern
671                this.expect_all(&[TokenKind::Ident, TokenKind::Assign]);
672                this.parse_pattern();
673
674                while this.check(TokenKind::Comma) {
675                    this.bump(); // ,
676                    if !this.check(TokenKind::BlockEnd) {
677                        this.expect_all(&[TokenKind::Ident, TokenKind::Assign]);
678                        this.parse_pattern();
679                    }
680                }
681            }
682
683            this.expect(TokenKind::BlockEnd);
684        });
685    }
686
687    /// Parse parameter list: (param1, param2, ...) or (param1: Type, param2: Type)
688    fn parse_param_list(&mut self) {
689        self.emit_node(SyntaxKind::ParamList, |this| {
690            this.expect(TokenKind::ParenBegin);
691
692            while !this.check(TokenKind::ParenEnd) && !this.is_at_end() {
693                // Mark parameter name with IdentParameter kind
694                if this.check(TokenKind::Ident) {
695                    if let Some(&token_idx) = this.preparsed.token_indices.get(this.current) {
696                        // Change token kind to IdentParameter
697                        this.tokens[token_idx].kind = TokenKind::IdentParameter;
698                    }
699                    this.bump();
700
701                    // Optional type annotation
702                    if this.check(TokenKind::Colon) {
703                        this.parse_type_annotation();
704                    }
705
706                    // Optional default value
707                    if this.check(TokenKind::Assign) {
708                        this.emit_node(SyntaxKind::ParamDefault, |inner| {
709                            inner.expect(TokenKind::Assign);
710                            // Use precedence 1 to avoid statement boundary detection within params
711                            inner.parse_expr_with_precedence(1);
712                        });
713                    }
714                }
715
716                if this.check(TokenKind::Comma) {
717                    this.bump();
718                } else {
719                    break;
720                }
721            }
722
723            this.expect(TokenKind::ParenEnd);
724        });
725    }
726
727    /// Parse expression with operator precedence and assignment
728    fn parse_expr(&mut self) {
729        self.parse_assignment_expr();
730    }
731
732    /// Parse assignment expression: expr = expr
733    /// Assignment has lower precedence than all binary operators
734    fn parse_assignment_expr(&mut self) {
735        self.parse_expr_with_precedence(0);
736
737        // Check if this is an assignment
738        if self.check(TokenKind::Assign) {
739            // Need to wrap the already-parsed LHS in AssignExpr
740            // This is a bit tricky since we've already added nodes to the tree
741            // Solution: We'll handle this at the CST level by creating AssignExpr node
742            // and the RHS after the operator
743            self.emit_node(SyntaxKind::AssignExpr, |this| {
744                this.expect(TokenKind::Assign);
745                this.parse_expr_with_precedence(0);
746            });
747        }
748    }
749
750    /// Parse expression with given minimum precedence (Pratt parser)
751    fn parse_expr_with_precedence(&mut self, min_prec: usize) {
752        // Save marker BEFORE parsing prefix (LHS)
753        let mut lhs_marker = self.builder.marker();
754
755        self.parse_prefix_expr();
756
757        // In statement context (min_prec == 0), stop at linebreak after prefix
758        // unless the next token is an infix operator (e.g. leading '|>').
759        if min_prec == 0
760            && self.has_trailing_linebreak()
761            && self
762                .peek()
763                .and_then(|k| self.get_infix_precedence(k))
764                .is_none()
765        {
766            return;
767        }
768
769        while let Some(token_kind) = self.peek() {
770            if let Some(prec) = self.get_infix_precedence(token_kind) {
771                if prec < min_prec {
772                    break;
773                }
774
775                // Wrap LHS and infix into BinaryExpr using the marker
776                self.builder
777                    .start_node_at(lhs_marker, SyntaxKind::BinaryExpr);
778                // Consume the binary operator
779                self.bump();
780                // Parse the right-hand side with appropriate precedence
781                // For left-associative operators, use prec + 1
782                self.parse_expr_with_precedence(prec + 1);
783                self.builder.finish_node();
784
785                // Update marker for next iteration (now points to the BinaryExpr we just created)
786                lhs_marker = Marker {
787                    pos: self.builder.marker().pos.saturating_sub(1),
788                };
789
790                // After parsing infix, check for linebreak in statement context
791                if min_prec == 0
792                    && self.has_trailing_linebreak()
793                    && self
794                        .peek()
795                        .and_then(|k| self.get_infix_precedence(k))
796                        .is_none()
797                {
798                    break;
799                }
800            } else {
801                break;
802            }
803        }
804    }
805
806    /// Parse expression with given minimum precedence without linebreak stopping.
807    fn parse_expr_with_precedence_no_linebreak(&mut self, min_prec: usize) {
808        // Save marker BEFORE parsing prefix (LHS)
809        let mut lhs_marker = self.builder.marker();
810
811        self.parse_prefix_expr();
812
813        while let Some(token_kind) = self.peek() {
814            if let Some(prec) = self.get_infix_precedence(token_kind) {
815                if prec < min_prec {
816                    break;
817                }
818
819                // Wrap LHS and infix into BinaryExpr using the marker
820                self.builder
821                    .start_node_at(lhs_marker, SyntaxKind::BinaryExpr);
822                // Consume the binary operator
823                self.bump();
824                // Parse the right-hand side with appropriate precedence
825                self.parse_expr_with_precedence_no_linebreak(prec + 1);
826                self.builder.finish_node();
827
828                // Update marker for next iteration
829                lhs_marker = Marker {
830                    pos: self.builder.marker().pos.saturating_sub(1),
831                };
832            } else {
833                break;
834            }
835        }
836    }
837
838    /// Get precedence of an infix operator (None means not an infix operator)
839    fn get_infix_precedence(&self, token_kind: TokenKind) -> Option<usize> {
840        match token_kind {
841            // Pipe operators share the same precedence and associate to the left.
842            // but must be > 0 to avoid infinite loop in statement context (min_prec == 0)
843            TokenKind::OpPipeMacro | TokenKind::OpPipe => Some(2),
844            TokenKind::OpOr => Some(3),
845            TokenKind::OpAnd => Some(4),
846            TokenKind::OpEqual | TokenKind::OpNotEqual => Some(5),
847            TokenKind::OpLessThan
848            | TokenKind::OpLessEqual
849            | TokenKind::OpGreaterThan
850            | TokenKind::OpGreaterEqual => Some(6),
851            TokenKind::OpSum | TokenKind::OpMinus => Some(7),
852            TokenKind::OpProduct | TokenKind::OpDivide | TokenKind::OpModulo => Some(8),
853            TokenKind::OpExponent => Some(9),
854            TokenKind::OpAt => Some(10),
855            // Arrow is NOT an infix operator in expressions, only used in type annotations
856            // Return None to prevent it from being treated as an operator
857            TokenKind::Arrow => None,
858            _ => None,
859        }
860    }
861
862    /// Get unary operator precedence
863    fn get_prefix_precedence(&self, token_kind: Option<TokenKind>) -> bool {
864        matches!(
865            token_kind,
866            Some(TokenKind::OpMinus)
867                | Some(TokenKind::OpSum)
868                | Some(TokenKind::Dollar)
869                | Some(TokenKind::BackQuote)
870        )
871    }
872
873    /// Parse prefix expression (unary operators and postfix operations)
874    fn parse_prefix_expr(&mut self) {
875        if self.get_prefix_precedence(self.peek()) {
876            self.parse_unary_expr();
877        } else {
878            self.parse_postfix_expr();
879        }
880    }
881
882    /// Parse unary expression
883    fn parse_unary_expr(&mut self) {
884        match self.peek() {
885            Some(TokenKind::BackQuote) => {
886                self.parse_bracket_expr();
887            }
888            Some(TokenKind::Dollar) => {
889                self.parse_escape_expr();
890            }
891            _ => {
892                // Standard unary operator (-, +)
893                self.emit_node(SyntaxKind::UnaryExpr, |this| {
894                    if matches!(
895                        this.peek(),
896                        Some(TokenKind::OpSum) | Some(TokenKind::OpMinus)
897                    ) && let Some(prev) = this.prev_kind_if_adjacent()
898                        && matches!(
899                            prev,
900                            TokenKind::OpSum
901                                | TokenKind::OpMinus
902                                | TokenKind::OpProduct
903                                | TokenKind::OpDivide
904                                | TokenKind::OpModulo
905                                | TokenKind::OpExponent
906                        )
907                    {
908                        this.add_error(ParserError::invalid_syntax(
909                            this.current_token_index(),
910                            "Consecutive operators without whitespace are not allowed",
911                        ));
912                    }
913                    // Consume the unary operator
914                    this.bump();
915                    // Parse the operand with high precedence
916                    this.parse_prefix_expr();
917                });
918            }
919        }
920    }
921
922    /// Parse postfix expression (handles function calls, field access, etc.)
923    fn parse_postfix_expr(&mut self) {
924        // Save marker before parsing primary (the potential callee/lhs)
925        let mut lhs_marker = self.builder.marker();
926
927        self.parse_primary();
928
929        loop {
930            // Do not allow postfix operators across a line break
931            if self.has_trailing_linebreak() {
932                break;
933            }
934            match self.peek() {
935                Some(TokenKind::ParenBegin) => {
936                    // Function call - wrap callee and arglist into CallExpr
937                    self.builder.start_node_at(lhs_marker, SyntaxKind::CallExpr);
938                    self.parse_arg_list();
939                    self.builder.finish_node();
940
941                    // Update marker for chained calls (e.g., foo()())
942                    lhs_marker = Marker {
943                        pos: self.builder.marker().pos.saturating_sub(1),
944                    };
945                }
946                Some(TokenKind::Dot) => {
947                    // Field access: expr.field or projection: expr.0, expr.1
948                    self.builder
949                        .start_node_at(lhs_marker, SyntaxKind::FieldAccess);
950                    self.bump(); // consume Dot
951                    // Can be either Ident (field name) or Int (tuple projection)
952                    self.expects(&[TokenKind::Ident, TokenKind::Int]);
953                    self.builder.finish_node();
954
955                    // Update marker for chained access
956                    lhs_marker = Marker {
957                        pos: self.builder.marker().pos.saturating_sub(1),
958                    };
959                }
960                Some(TokenKind::ArrayBegin) => {
961                    // Array indexing: expr[index]
962                    self.builder
963                        .start_node_at(lhs_marker, SyntaxKind::IndexExpr);
964                    self.bump(); // consume [
965                    self.parse_expr();
966                    self.expect(TokenKind::ArrayEnd);
967                    self.builder.finish_node();
968
969                    // Update marker for chained indexing
970                    lhs_marker = Marker {
971                        pos: self.builder.marker().pos.saturating_sub(1),
972                    };
973                }
974                _ => break,
975            }
976        }
977    }
978
979    /// Parse argument list: (expr, expr, ...)
980    fn parse_arg_list(&mut self) {
981        self.emit_node(SyntaxKind::ArgList, |this| {
982            this.expect(TokenKind::ParenBegin);
983
984            if !this.check(TokenKind::ParenEnd) {
985                // Do not stop at linebreaks, and allow low-precedence operators in args
986                this.parse_expr_with_precedence_no_linebreak(0);
987
988                while this.check(TokenKind::Comma) {
989                    this.bump(); // ,
990                    if !this.check(TokenKind::ParenEnd) {
991                        this.parse_expr_with_precedence_no_linebreak(0);
992                    }
993                }
994            }
995
996            this.expect(TokenKind::ParenEnd);
997        });
998    }
999
1000    /// Parse type annotation: : Type
1001    fn parse_type_annotation(&mut self) {
1002        self.emit_node(SyntaxKind::TypeAnnotation, |this| {
1003            this.expect(TokenKind::Colon);
1004            this.parse_type();
1005        });
1006    }
1007
1008    /// Parse type expression
1009    fn parse_type(&mut self) {
1010        // Check for function type with parentheses: (T1, T2) -> R or (T) -> R
1011        if self.check(TokenKind::ParenBegin) {
1012            // Look ahead for Arrow to detect function types
1013            // Need to handle both `(T1, T2) -> R` and `(T) -> R`
1014            let mut ahead_arrow = None;
1015            let mut paren_depth = 0;
1016            for i in 1..MAX_LOOKAHEAD {
1017                match self.peek_ahead(i) {
1018                    Some(TokenKind::ParenBegin) => paren_depth += 1,
1019                    Some(TokenKind::ParenEnd) => {
1020                        if paren_depth == 0 {
1021                            // Found the closing paren of our type expression
1022                            // Check if Arrow follows immediately after
1023                            if self.peek_ahead(i + 1) == Some(TokenKind::Arrow) {
1024                                ahead_arrow = Some(i + 1);
1025                            }
1026                            break;
1027                        } else {
1028                            paren_depth -= 1;
1029                        }
1030                    }
1031                    None => break,
1032                    _ => continue,
1033                }
1034            }
1035
1036            if ahead_arrow.is_some() {
1037                // Function type
1038                self.emit_node(SyntaxKind::FunctionType, |inner| {
1039                    inner.parse_type_tuple_or_paren();
1040                    inner.expect(TokenKind::Arrow);
1041                    inner.parse_type();
1042                });
1043                return;
1044            }
1045        }
1046
1047        self.parse_type_union();
1048    }
1049
1050    /// Parse union type: A | B | C
1051    /// Note: We need lookahead to distinguish union type separator `|` from lambda's closing `|`
1052    fn parse_type_union(&mut self) {
1053        let marker = self.builder.marker();
1054        self.parse_type_primary();
1055
1056        // Only continue parsing union if `|` is followed by a type start token
1057        if self.check(TokenKind::LambdaArgBeginEnd) && self.is_type_start_after_pipe() {
1058            // We have a union type, wrap the first type and parse the rest
1059            self.builder.start_node_at(marker, SyntaxKind::UnionType);
1060            while self.check(TokenKind::LambdaArgBeginEnd) && self.is_type_start_after_pipe() {
1061                self.bump(); // consume |
1062                self.parse_type_primary();
1063            }
1064            self.builder.finish_node();
1065        }
1066    }
1067
1068    /// Check if the token after `|` is a type start token (for union type disambiguation)
1069    fn is_type_start_after_pipe(&self) -> bool {
1070        match self.peek_ahead(1) {
1071            Some(TokenKind::FloatType)
1072            | Some(TokenKind::IntegerType)
1073            | Some(TokenKind::StringType)
1074            | Some(TokenKind::ParenBegin)
1075            | Some(TokenKind::ArrayBegin)
1076            | Some(TokenKind::BackQuote) => true,
1077            // For BlockBegin, we need to distinguish record type `{field: Type}` from
1078            // lambda body block `{expression}`. Record type starts with `{ ident :`
1079            Some(TokenKind::BlockBegin) => {
1080                self.peek_ahead(2) == Some(TokenKind::Ident)
1081                    && self.peek_ahead(3) == Some(TokenKind::Colon)
1082            }
1083            _ => self.is_type_ident_after_pipe(),
1084        }
1085    }
1086
1087    /// Check if the token after `|` is a type identifier
1088    /// A type identifier is followed by `|`, `,`, `)`, `}`, `]`, or is a known type name
1089    fn is_type_ident_after_pipe(&self) -> bool {
1090        if self.peek_ahead(1) != Some(TokenKind::Ident) {
1091            return false;
1092        }
1093        // Check what follows the identifier
1094        // If it's followed by another `|` that continues union, or `,`, `)`, etc.
1095        // it's likely a type identifier
1096        matches!(
1097            self.peek_ahead(2),
1098            Some(TokenKind::LambdaArgBeginEnd) // Union continues or lambda closes
1099                | Some(TokenKind::Comma)
1100                | Some(TokenKind::ParenEnd)
1101                | Some(TokenKind::BlockEnd)
1102                | Some(TokenKind::ArrayEnd)
1103                | Some(TokenKind::Arrow)  // Function return type
1104                | None
1105        )
1106    }
1107
1108    /// Parse primary type (primitive, tuple, etc.)
1109    fn parse_type_primary(&mut self) {
1110        match self.peek() {
1111            Some(TokenKind::FloatType)
1112            | Some(TokenKind::IntegerType)
1113            | Some(TokenKind::StringType) => {
1114                self.emit_node(SyntaxKind::PrimitiveType, |inner| {
1115                    inner.bump();
1116                });
1117            }
1118            Some(TokenKind::ParenBegin) => {
1119                // Tuple type: (T1, T2, ...)
1120                self.parse_type_tuple_or_paren();
1121            }
1122            Some(TokenKind::BlockBegin) => {
1123                // Record type: {field: Type, ...}
1124                self.parse_type_record();
1125            }
1126            Some(TokenKind::ArrayBegin) => {
1127                // Array type: [T]
1128                self.emit_node(SyntaxKind::ArrayType, |inner| {
1129                    inner.expect(TokenKind::ArrayBegin);
1130                    inner.parse_type();
1131                    inner.expect(TokenKind::ArrayEnd);
1132                });
1133            }
1134            Some(TokenKind::BackQuote) => {
1135                // Code type: `Type
1136                self.emit_node(SyntaxKind::CodeType, |inner| {
1137                    inner.expect(TokenKind::BackQuote);
1138                    inner.parse_type();
1139                });
1140            }
1141            Some(TokenKind::Ident) => {
1142                // Type variable or named type (can be qualified path like mymath::PrivateNum)
1143                self.emit_node(SyntaxKind::TypeIdent, |inner| {
1144                    inner.bump(); // First identifier
1145                    // Check for qualified path (::)
1146                    while inner.check(TokenKind::DoubleColon) {
1147                        inner.bump(); // Consume ::
1148                        if inner.check(TokenKind::Ident) {
1149                            inner.bump(); // Next identifier
1150                        } else {
1151                            inner.add_error(ParserError::unexpected_token(
1152                                inner.current_token_index(),
1153                                "identifier after ::",
1154                                &format!("{:?}", inner.peek().unwrap_or(TokenKind::Eof)),
1155                            ));
1156                            break;
1157                        }
1158                    }
1159                });
1160            }
1161            _ => {
1162                // Error: unexpected token for type
1163                if !self.is_at_end() {
1164                    self.add_error(ParserError::unexpected_token(
1165                        self.current_token_index(),
1166                        "type",
1167                        &format!("{:?}", self.peek().unwrap_or(TokenKind::Eof)),
1168                    ));
1169                    self.bump();
1170                }
1171            }
1172        }
1173    }
1174
1175    /// Parse tuple type or parenthesized type: (T) or (T1, T2)
1176    fn parse_type_tuple_or_paren(&mut self) {
1177        // Look ahead to determine if it's a tuple
1178        let mut paren_depth = 0;
1179        let is_tuple = (1..MAX_LOOKAHEAD)
1180            .find_map(|i| match self.peek_ahead(i) {
1181                Some(TokenKind::ParenBegin) => {
1182                    paren_depth += 1;
1183                    None
1184                }
1185                Some(TokenKind::ParenEnd) => {
1186                    if paren_depth == 0 {
1187                        Some(false)
1188                    } else {
1189                        paren_depth -= 1;
1190                        None
1191                    }
1192                }
1193                Some(TokenKind::Comma) if paren_depth == 0 => Some(true),
1194                None => Some(false),
1195                _ => None,
1196            })
1197            .unwrap_or(false);
1198
1199        if is_tuple {
1200            self.emit_node(SyntaxKind::TupleType, |inner| {
1201                inner.expect(TokenKind::ParenBegin);
1202                if !inner.check(TokenKind::ParenEnd) {
1203                    inner.parse_type();
1204                    while inner.check(TokenKind::Comma) {
1205                        inner.bump();
1206                        if !inner.check(TokenKind::ParenEnd) {
1207                            inner.parse_type();
1208                        }
1209                    }
1210                }
1211                inner.expect(TokenKind::ParenEnd);
1212            });
1213        } else {
1214            // Parenthesized type or unit type
1215            if self.peek_ahead(1) == Some(TokenKind::ParenEnd) {
1216                self.emit_node(SyntaxKind::UnitType, |inner| {
1217                    inner.expect(TokenKind::ParenBegin);
1218                    inner.expect(TokenKind::ParenEnd);
1219                });
1220            } else {
1221                self.bump(); // (
1222                self.parse_type();
1223                self.expect(TokenKind::ParenEnd);
1224            }
1225        }
1226    }
1227
1228    /// Parse record type: {field1: Type1, field2: Type2}
1229    fn parse_type_record(&mut self) {
1230        self.emit_node(SyntaxKind::RecordType, |inner| {
1231            inner.expect(TokenKind::BlockBegin);
1232
1233            if !inner.check(TokenKind::BlockEnd) {
1234                // Parse field: name : Type
1235                inner.expect_all(&[TokenKind::Ident, TokenKind::Colon]);
1236                inner.parse_type();
1237
1238                while inner.check(TokenKind::Comma) {
1239                    inner.bump();
1240                    if !inner.check(TokenKind::BlockEnd) {
1241                        inner.expect_all(&[TokenKind::Ident, TokenKind::Colon]);
1242                        inner.parse_type();
1243                    }
1244                }
1245            }
1246
1247            inner.expect(TokenKind::BlockEnd);
1248        });
1249    }
1250
1251    /// Parse primary expression
1252    fn parse_primary(&mut self) {
1253        match self.peek() {
1254            Some(TokenKind::Int) => {
1255                self.emit_node(SyntaxKind::IntLiteral, |this| {
1256                    this.bump();
1257                });
1258            }
1259            Some(TokenKind::Float) => {
1260                self.emit_node(SyntaxKind::FloatLiteral, |this| {
1261                    this.bump();
1262                });
1263            }
1264            Some(TokenKind::Str) => {
1265                self.emit_node(SyntaxKind::StringLiteral, |this| {
1266                    this.bump();
1267                });
1268            }
1269            Some(TokenKind::SelfLit) => {
1270                self.emit_node(SyntaxKind::SelfLiteral, |this| {
1271                    this.bump();
1272                });
1273            }
1274            Some(TokenKind::Now) => {
1275                self.emit_node(SyntaxKind::NowLiteral, |this| {
1276                    this.bump();
1277                });
1278            }
1279            Some(TokenKind::SampleRate) => {
1280                self.emit_node(SyntaxKind::SampleRateLiteral, |this| {
1281                    this.bump();
1282                });
1283            }
1284            Some(TokenKind::LambdaArgBeginEnd) => {
1285                self.parse_lambda_expr();
1286            }
1287            Some(TokenKind::Ident) => {
1288                // Check if this is a macro expansion (including qualified paths)
1289                if self.find_macro_expand_after_path().is_some() {
1290                    self.parse_macro_expansion();
1291                } else if self.peek_ahead(1) == Some(TokenKind::DoubleColon) {
1292                    // Qualified path: mod::submod::name
1293                    self.parse_qualified_path();
1294                } else {
1295                    self.emit_node(SyntaxKind::Identifier, |this| {
1296                        this.bump();
1297                    });
1298                }
1299            }
1300            Some(TokenKind::ArrayBegin) => {
1301                // Array literal: [1, 2, 3]
1302                self.parse_array_expr();
1303            }
1304            Some(TokenKind::ParenBegin) => {
1305                // Need lookahead to distinguish tuple from parenthesized expression
1306                // Tuple: (a, b) or (a,)
1307                // Paren: (a) or (expr)
1308                if self.is_tuple_expr() {
1309                    self.parse_tuple_expr();
1310                } else {
1311                    self.emit_node(SyntaxKind::ParenExpr, |this| {
1312                        this.bump(); // (
1313                        this.parse_expr();
1314                        this.expect(TokenKind::ParenEnd);
1315                    });
1316                }
1317            }
1318            Some(TokenKind::BlockBegin) => {
1319                // Need lookahead to distinguish record from block
1320                // Record: {a = 1, b = 2}
1321                // Block: {stmt; stmt}
1322                if self.is_record_expr() {
1323                    self.parse_record_expr();
1324                } else {
1325                    self.parse_block_expr();
1326                }
1327            }
1328            Some(TokenKind::If) => {
1329                self.parse_if_expr();
1330            }
1331            Some(TokenKind::Match) => {
1332                self.parse_match_expr();
1333            }
1334            Some(TokenKind::PlaceHolder) => {
1335                // Placeholder: _
1336                self.emit_node(SyntaxKind::PlaceHolderLiteral, |this| {
1337                    this.bump();
1338                });
1339            }
1340            Some(TokenKind::BlockEnd) | Some(TokenKind::ParenEnd) | Some(TokenKind::ArrayEnd) => {
1341                // Do not consume closing tokens here; let the enclosing parser handle them.
1342                if let Some(kind) = self.peek() {
1343                    self.add_error(ParserError::unexpected_token(
1344                        self.current_token_index(),
1345                        "expression",
1346                        &format!("{kind:?}"),
1347                    ));
1348                }
1349            }
1350            None | Some(TokenKind::Eof) => {
1351                // EOF reached when expecting an expression
1352                self.add_error(ParserError::unexpected_eof(
1353                    self.current_token_index(),
1354                    "expression",
1355                ));
1356            }
1357            _ => {
1358                // Error: unexpected token
1359                // Record the error and continue
1360                if let Some(kind) = self.peek() {
1361                    self.add_error(ParserError::unexpected_token(
1362                        self.current_token_index(),
1363                        "expression",
1364                        &format!("{kind:?}"),
1365                    ));
1366                }
1367                // Skip the unexpected token to recover
1368                self.bump();
1369            }
1370        }
1371    }
1372
1373    /// Parse lambda expression: |x, y| expr
1374    fn parse_lambda_expr(&mut self) {
1375        self.emit_node(SyntaxKind::LambdaExpr, |this| {
1376            // Opening '|'
1377            this.expect(TokenKind::LambdaArgBeginEnd);
1378
1379            // Parameters
1380            while !this.check(TokenKind::LambdaArgBeginEnd) && !this.is_at_end() {
1381                if this.check(TokenKind::Ident) {
1382                    if let Some(&token_idx) = this.preparsed.token_indices.get(this.current) {
1383                        this.tokens[token_idx].kind = TokenKind::IdentParameter;
1384                    }
1385                    this.bump();
1386
1387                    // Optional type annotation
1388                    if this.check(TokenKind::Colon) {
1389                        this.parse_type_annotation();
1390                    }
1391                } else {
1392                    // Skip unexpected tokens in parameter list to recover
1393                    this.bump();
1394                }
1395
1396                if this.check(TokenKind::Comma) {
1397                    this.bump();
1398                } else if !this.check(TokenKind::LambdaArgBeginEnd) {
1399                    // Invalid separator; try to recover by stopping params
1400                    break;
1401                }
1402            }
1403
1404            // Closing '|'
1405            this.expect(TokenKind::LambdaArgBeginEnd);
1406
1407            // Optional return type annotation after '->'
1408            if this.check(TokenKind::Arrow) {
1409                this.bump();
1410                this.parse_type();
1411            }
1412
1413            // Body expression
1414            if !this.is_at_end() {
1415                if this.check(TokenKind::BlockBegin) {
1416                    this.parse_block_expr();
1417                } else {
1418                    this.parse_expr();
1419                }
1420            }
1421        });
1422    }
1423
1424    /// Check if current position is a tuple expression
1425    /// Tuple: (a, b) or (a,) - has comma
1426    /// Paren: (a) - no comma
1427    fn is_tuple_expr(&self) -> bool {
1428        if !self.check(TokenKind::ParenBegin) {
1429            return false;
1430        }
1431
1432        // Look ahead to find comma before ParenEnd
1433        let mut depth = 0;
1434        for i in 1..MAX_LOOKAHEAD {
1435            match self.peek_ahead(i) {
1436                Some(TokenKind::ParenBegin) => depth += 1,
1437                Some(TokenKind::ParenEnd) => {
1438                    if depth == 0 {
1439                        return false; // no comma found
1440                    }
1441                    depth -= 1;
1442                }
1443                Some(TokenKind::Comma) if depth == 0 => return true,
1444                None => return false,
1445                _ => {}
1446            }
1447        }
1448        false
1449    }
1450
1451    /// Check if current position is a record expression
1452    /// Record: {a = 1, b = 2} - has assignment
1453    /// Block: {stmt; stmt} - has other tokens
1454    fn is_record_expr(&self) -> bool {
1455        if !self.check(TokenKind::BlockBegin) {
1456            return false;
1457        }
1458
1459        // Get lookahead tokens once to avoid repeated peek_ahead calls
1460        let token1 = self.peek_ahead(1);
1461        let token2 = self.peek_ahead(2);
1462
1463        let is_record_key = matches!(
1464            token1,
1465            Some(TokenKind::Ident) | Some(TokenKind::IdentParameter)
1466        );
1467
1468        (matches!(token1, Some(TokenKind::DoubleDot))
1469            || (is_record_key
1470                && matches!(token2, Some(TokenKind::Assign) | Some(TokenKind::LeftArrow))))
1471    }
1472
1473    /// Parse tuple expression: (a, b, c)
1474    fn parse_tuple_expr(&mut self) {
1475        self.emit_node(SyntaxKind::TupleExpr, |this| {
1476            this.expect(TokenKind::ParenBegin);
1477
1478            if !this.check(TokenKind::ParenEnd) {
1479                this.parse_expr();
1480
1481                while this.check(TokenKind::Comma) {
1482                    this.bump(); // ,
1483                    if !this.check(TokenKind::ParenEnd) {
1484                        this.parse_expr();
1485                    }
1486                }
1487            }
1488
1489            this.expect(TokenKind::ParenEnd);
1490        });
1491    }
1492
1493    /// Parse record expression: {field1 = expr1, field2 = expr2}
1494    /// Also handles record update: {base <- field = value}
1495    /// And incomplete records: {field1 = expr1, ..}
1496    fn parse_record_expr(&mut self) {
1497        self.emit_node(SyntaxKind::RecordExpr, |this| {
1498            this.expect(TokenKind::BlockBegin);
1499
1500            if !this.check(TokenKind::BlockEnd) {
1501                // Check for record update: {base <- field = value}
1502                if this.check(TokenKind::Ident) && this.peek_ahead(1) == Some(TokenKind::LeftArrow)
1503                {
1504                    this.parse_expr(); // base record
1505                    this.expect(TokenKind::LeftArrow); // <-
1506
1507                    // Parse updated fields
1508                    this.expects(&[TokenKind::Ident, TokenKind::IdentParameter]);
1509                    this.expect(TokenKind::Assign);
1510                    this.parse_expr();
1511
1512                    while this.check(TokenKind::Comma) {
1513                        this.bump();
1514                        if !this.check(TokenKind::BlockEnd) {
1515                            this.expects(&[TokenKind::Ident, TokenKind::IdentParameter]);
1516                            this.expect(TokenKind::Assign);
1517                            this.parse_expr();
1518                        }
1519                    }
1520                } else {
1521                    // Regular record: {field = expr, ...} or {field = expr, ..}
1522                    // Allow incomplete record: {..}
1523                    if this.check(TokenKind::DoubleDot) {
1524                        this.bump(); // ..
1525                    } else {
1526                        // Parse field: name = expr
1527                        this.expects(&[TokenKind::Ident, TokenKind::IdentParameter]);
1528                        this.expect(TokenKind::Assign);
1529                        this.parse_expr();
1530                    }
1531
1532                    while this.check(TokenKind::Comma) {
1533                        this.bump();
1534                        if !this.check(TokenKind::BlockEnd) {
1535                            if this.check(TokenKind::DoubleDot) {
1536                                // Incomplete record: field = expr, ..
1537                                this.bump(); // ..
1538                                break;
1539                            } else {
1540                                this.expects(&[TokenKind::Ident, TokenKind::IdentParameter]);
1541                                this.expect(TokenKind::Assign);
1542                                this.parse_expr();
1543                            }
1544                        }
1545                    }
1546                }
1547            }
1548
1549            this.expect(TokenKind::BlockEnd);
1550        });
1551    }
1552
1553    /// Parse block expression: { statements }
1554    fn parse_block_expr(&mut self) {
1555        self.emit_node(SyntaxKind::BlockExpr, |this| {
1556            this.expect(TokenKind::BlockBegin);
1557
1558            while !this.check(TokenKind::BlockEnd) && !this.is_at_end() {
1559                let before = this.current;
1560                this.parse_statement();
1561
1562                if this.current == before && !this.is_at_end() {
1563                    this.add_error(ParserError::invalid_syntax(
1564                        this.current_token_index(),
1565                        "parser made no progress in block; skipping token for recovery",
1566                    ));
1567                    this.bump();
1568                    continue;
1569                }
1570
1571                // After a statement, if there's trailing linebreak/semicolon,
1572                // prepare for next statement. Otherwise, assume statement continues
1573                // (but note that parse_statement itself stops at natural boundaries)
1574                if this.has_trailing_linebreak() {
1575                    // Linebreak found - ready for next statement
1576                    continue;
1577                }
1578                // If no linebreak and not at block end, something may be off
1579                // but let parse_statement handle it in next iteration
1580            }
1581
1582            this.expect(TokenKind::BlockEnd);
1583        });
1584    }
1585
1586    /// Parse if expression: if cond then-expr [else expr]
1587    fn parse_if_expr(&mut self) {
1588        self.emit_node(SyntaxKind::IfExpr, |this| {
1589            this.expect(TokenKind::If);
1590            this.parse_expr(); // condition
1591
1592            if this.check(TokenKind::BlockBegin) {
1593                this.parse_block_expr();
1594            } else {
1595                this.parse_expr();
1596            }
1597
1598            if this.check(TokenKind::Else) {
1599                this.bump();
1600                if this.check(TokenKind::If) {
1601                    this.parse_if_expr();
1602                } else if this.check(TokenKind::BlockBegin) {
1603                    this.parse_block_expr();
1604                } else {
1605                    this.parse_expr();
1606                }
1607            }
1608        });
1609    }
1610
1611    /// Parse match expression: match scrutinee { pattern => expr, ... }
1612    fn parse_match_expr(&mut self) {
1613        self.emit_node(SyntaxKind::MatchExpr, |this| {
1614            this.expect(TokenKind::Match);
1615            this.parse_expr(); // scrutinee
1616
1617            this.expect(TokenKind::BlockBegin);
1618
1619            // Parse match arms
1620            this.emit_node(SyntaxKind::MatchArmList, |this| {
1621                while !this.check(TokenKind::BlockEnd) && !this.is_at_end() {
1622                    let before = this.current;
1623                    this.parse_match_arm();
1624
1625                    if this.current == before && !this.is_at_end() {
1626                        this.add_error(ParserError::invalid_syntax(
1627                            this.current_token_index(),
1628                            "parser made no progress in match arm; skipping token for recovery",
1629                        ));
1630                        this.bump();
1631                        continue;
1632                    }
1633
1634                    // Arms are separated by linebreaks or commas
1635                    if this.has_trailing_linebreak() {
1636                        continue;
1637                    }
1638                    if this.check(TokenKind::Comma) {
1639                        this.bump();
1640                    }
1641                }
1642            });
1643
1644            this.expect(TokenKind::BlockEnd);
1645        });
1646    }
1647
1648    /// Check if the current position has a tuple pattern inside a constructor
1649    /// i.e., Constructor((x, y)) - the inner (x, y) is a tuple pattern
1650    /// Returns true if we see: ( ( or ( ident ,
1651    fn is_tuple_pattern_in_constructor(&self) -> bool {
1652        // We're at ( - look ahead to see if it's a tuple pattern
1653        // A tuple pattern starts with ( and either:
1654        // 1. Has another ( immediately (nested tuple)
1655        // 2. Has ident, comma pattern
1656        if self.peek() != Some(TokenKind::ParenBegin) {
1657            return false;
1658        }
1659
1660        // Check what follows the opening paren
1661        match self.peek_ahead(1) {
1662            // ((x, y)) - nested tuple
1663            Some(TokenKind::ParenBegin) => true,
1664            // (x, y) - look for comma after first element
1665            Some(TokenKind::Ident) | Some(TokenKind::PlaceHolder) => {
1666                // Check if there's a comma after the identifier
1667                self.peek_ahead(2) == Some(TokenKind::Comma)
1668            }
1669            _ => false,
1670        }
1671    }
1672
1673    /// Parse a single match arm: pattern => expr
1674    fn parse_match_arm(&mut self) {
1675        self.emit_node(SyntaxKind::MatchArm, |this| {
1676            this.parse_match_pattern();
1677            this.expect(TokenKind::FatArrow);
1678            // Arm body can be a block or simple expression
1679            if this.check(TokenKind::BlockBegin) {
1680                this.parse_block_expr();
1681            } else {
1682                this.parse_expr();
1683            }
1684        });
1685    }
1686
1687    /// Parse a match pattern
1688    /// Supports:
1689    /// - Integer literals: 0, 1, 2
1690    /// - Float literals: 1.0, 2.5
1691    /// - Wildcard: _
1692    /// - Constructor patterns: Float(x), String(s), TypeName(binding)
1693    fn parse_match_pattern(&mut self) {
1694        self.emit_node(SyntaxKind::MatchPattern, |this| {
1695            match this.peek() {
1696                Some(TokenKind::Int) => {
1697                    this.emit_node(SyntaxKind::IntLiteral, |this| {
1698                        this.bump();
1699                    });
1700                }
1701                Some(TokenKind::Float) => {
1702                    this.emit_node(SyntaxKind::FloatLiteral, |this| {
1703                        this.bump();
1704                    });
1705                }
1706                Some(TokenKind::PlaceHolder) => {
1707                    this.emit_node(SyntaxKind::PlaceHolderLiteral, |this| {
1708                        this.bump();
1709                    });
1710                }
1711                // Tuple pattern at top level of match arm: (pat1, pat2, ...)
1712                Some(TokenKind::ParenBegin) => {
1713                    this.parse_match_tuple_pattern();
1714                }
1715                // Constructor pattern: Identifier(binding) or Identifier
1716                // Also handle type keywords (float, string, int) as constructor names
1717                Some(TokenKind::Ident)
1718                | Some(TokenKind::FloatType)
1719                | Some(TokenKind::StringType)
1720                | Some(TokenKind::IntegerType) => {
1721                    this.emit_node(SyntaxKind::ConstructorPattern, |this| {
1722                        // Constructor name (e.g., float, string, MyType)
1723                        this.emit_node(SyntaxKind::Identifier, |this| {
1724                            this.bump();
1725                        });
1726                        // Optional binding: (x) or ((x, y)) for tuple patterns
1727                        if this.check(TokenKind::ParenBegin) {
1728                            // Look ahead to determine if this is a tuple pattern or simple binding
1729                            // If there's a comma after the first identifier, it's a tuple pattern
1730                            let is_tuple_pattern = this.is_tuple_pattern_in_constructor();
1731
1732                            if is_tuple_pattern {
1733                                // Parse as tuple pattern
1734                                this.parse_tuple_pattern();
1735                            } else {
1736                                // Simple binding: (x) or (_)
1737                                this.bump(); // (
1738                                if this.check(TokenKind::Ident) {
1739                                    this.emit_node(SyntaxKind::Identifier, |this| {
1740                                        this.bump();
1741                                    });
1742                                } else if this.check(TokenKind::PlaceHolder) {
1743                                    // Allow _ as binding to discard the value
1744                                    this.emit_node(SyntaxKind::PlaceHolderLiteral, |this| {
1745                                        this.bump();
1746                                    });
1747                                } else if this.check(TokenKind::ParenBegin) {
1748                                    // Nested tuple pattern: ((x, y))
1749                                    this.parse_tuple_pattern();
1750                                }
1751                                this.expect(TokenKind::ParenEnd);
1752                            }
1753                        }
1754                    });
1755                }
1756                _ => {
1757                    if let Some(kind) = this.peek() {
1758                        this.add_error(ParserError::unexpected_token(
1759                            this.current_token_index(),
1760                            "match pattern (int, float, _, or constructor)",
1761                            &format!("{kind:?}"),
1762                        ));
1763                    }
1764                    this.bump();
1765                }
1766            }
1767        });
1768    }
1769
1770    /// Parse tuple pattern in match expression: (pat1, pat2, ...)
1771    /// Each element is a match pattern (can be int, float, _, constructor, or nested tuple)
1772    fn parse_match_tuple_pattern(&mut self) {
1773        self.emit_node(SyntaxKind::TuplePattern, |this| {
1774            this.expect(TokenKind::ParenBegin);
1775
1776            if !this.check(TokenKind::ParenEnd) {
1777                this.parse_match_pattern();
1778
1779                while this.check(TokenKind::Comma) {
1780                    this.bump(); // ,
1781                    if !this.check(TokenKind::ParenEnd) {
1782                        this.parse_match_pattern();
1783                    }
1784                }
1785            }
1786
1787            this.expect(TokenKind::ParenEnd);
1788        });
1789    }
1790
1791    /// Parse type declaration (Union/Sum types only)
1792    /// Type declaration: type Name = Variant1 | Variant2(Type) | ...
1793    /// Recursive type:   type rec Name = Variant1 | Variant2(Type, Name) | ...
1794    fn parse_type_decl(&mut self) {
1795        self.emit_node(SyntaxKind::TypeDecl, |this| {
1796            this.expect(TokenKind::Type);
1797            // Optionally consume 'rec' keyword for recursive types
1798            if this.check(TokenKind::Rec) {
1799                this.bump(); // consume 'rec'
1800            }
1801            this.expect(TokenKind::Ident); // type name
1802            this.expect(TokenKind::Assign); // =
1803
1804            // Parse as variant declaration: type Name = Variant1 | Variant2 | ...
1805            this.parse_variant_def();
1806
1807            // Parse remaining variants separated by |
1808            while this.check(TokenKind::LambdaArgBeginEnd) {
1809                this.bump(); // consume |
1810                this.parse_variant_def();
1811            }
1812        });
1813    }
1814
1815    /// Parse type alias declaration
1816    /// Type alias: type alias Name = BaseType
1817    fn parse_type_alias_decl(&mut self) {
1818        self.emit_node(SyntaxKind::TypeDecl, |this| {
1819            this.expect(TokenKind::Type);
1820            this.expect(TokenKind::Alias);
1821            this.expect(TokenKind::Ident); // alias name
1822            this.expect(TokenKind::Assign); // =
1823
1824            // Parse the target type
1825            this.parse_type();
1826        });
1827    }
1828
1829    /// Parse a single variant definition: Name or Name(Type)
1830    fn parse_variant_def(&mut self) {
1831        self.emit_node(SyntaxKind::VariantDef, |this| {
1832            this.expect(TokenKind::Ident); // variant name
1833
1834            // Check for optional payload type: Name(Type) or Name(T1, T2, ...)
1835            // If multiple comma-separated types are found, treat as tuple implicitly
1836            if this.check(TokenKind::ParenBegin) {
1837                this.bump(); // consume (
1838
1839                // Parse first type
1840                this.parse_type();
1841
1842                // If there's a comma, we have multiple types - treat as tuple
1843                if this.check(TokenKind::Comma) {
1844                    // We've already parsed the first type, now wrap it in a TupleType
1845                    // and parse the rest
1846                    while this.check(TokenKind::Comma) {
1847                        this.bump(); // consume ,
1848                        if !this.check(TokenKind::ParenEnd) {
1849                            this.parse_type();
1850                        }
1851                    }
1852                }
1853
1854                this.expect(TokenKind::ParenEnd);
1855            }
1856        });
1857    }
1858
1859    /// Parse array expression: [expr1, expr2, ...]
1860    fn parse_array_expr(&mut self) {
1861        self.emit_node(SyntaxKind::ArrayExpr, |this| {
1862            this.expect(TokenKind::ArrayBegin);
1863
1864            if !this.check(TokenKind::ArrayEnd) {
1865                this.parse_expr();
1866
1867                while this.check(TokenKind::Comma) {
1868                    this.bump(); // ,
1869                    if !this.check(TokenKind::ArrayEnd) {
1870                        this.parse_expr();
1871                    }
1872                }
1873            }
1874
1875            this.expect(TokenKind::ArrayEnd);
1876        });
1877    }
1878}
1879
1880/// Parse tokens into a Green Tree (CST) with error collection
1881pub fn parse_cst(
1882    tokens: Vec<Token>,
1883    preparsed: &PreParsedTokens,
1884) -> (GreenNodeId, GreenNodeArena, Vec<Token>, Vec<ParserError>) {
1885    let parser = Parser::new(tokens, preparsed);
1886    parser.parse()
1887}