gruel_parser/
chumsky_parser.rs

1//! Chumsky-based parser for the Gruel programming language.
2//!
3//! This module provides a parser implementation using chumsky combinators
4//! with Pratt parsing for expression precedence.
5
6use crate::ast::{
7    AnonStructField, ArgMode, ArrayLitExpr, AssignStatement, AssignTarget, AssocFnCallExpr, Ast,
8    BinaryExpr, BinaryOp, BlockExpr, BoolLit, BreakExpr, CallArg, CallExpr, CheckedBlockExpr,
9    ComptimeBlockExpr, ComptimeUnrollForExpr, ConstDecl, ContinueExpr, DestructureBinding,
10    DestructureField, Directive, DirectiveArg, Directives, DropFn, EnumDecl, EnumStructLitExpr,
11    EnumVariant, Expr, FieldDecl, FieldExpr, FieldInit, FloatLit, ForExpr, Function, Ident, IfExpr,
12    IndexExpr, IntLit, IntrinsicArg, IntrinsicCallExpr, Item, LetPattern, LetStatement, LoopExpr,
13    MatchArm, MatchExpr, Method, MethodCallExpr, NegIntLit, Param, ParamMode, ParenExpr, PathExpr,
14    PathPattern, Pattern, PatternBinding, PatternFieldBinding, ReturnExpr, SelfExpr, SelfParam,
15    Statement, StringLit, StructDecl, StructLitExpr, TypeExpr, TypeLitExpr, UnaryExpr, UnaryOp,
16    UnitLit, Visibility, WhileExpr,
17};
18use chumsky::input::{Input as ChumskyInput, MapExtra, Stream, ValueInput};
19use chumsky::prelude::*;
20use chumsky::recovery::via_parser;
21use chumsky::recursive::Direct;
22use gruel_error::{CompileError, CompileErrors, ErrorKind, MultiErrorResult};
23use gruel_lexer::TokenKind;
24use gruel_span::{FileId, Span};
25use lasso::{Spur, ThreadedRodeo};
26use std::borrow::Cow;
27
28use chumsky::extra::SimpleState;
29
30/// Pre-interned symbols for primitive type names and special keywords.
31/// These are interned once when the parser is created and reused for all parsing.
32#[derive(Clone, Copy)]
33pub struct PrimitiveTypeSpurs {
34    pub i8: Spur,
35    pub i16: Spur,
36    pub i32: Spur,
37    pub i64: Spur,
38    pub isize: Spur,
39    pub u8: Spur,
40    pub u16: Spur,
41    pub u32: Spur,
42    pub u64: Spur,
43    pub usize: Spur,
44    pub f16: Spur,
45    pub f32: Spur,
46    pub f64: Spur,
47    pub bool: Spur,
48    /// Self type keyword - used in methods to refer to the containing struct type
49    pub self_type: Spur,
50}
51
52impl PrimitiveTypeSpurs {
53    /// Create a new set of primitive type symbols by interning them.
54    pub fn new(interner: &mut ThreadedRodeo) -> Self {
55        Self {
56            i8: interner.get_or_intern("i8"),
57            i16: interner.get_or_intern("i16"),
58            i32: interner.get_or_intern("i32"),
59            i64: interner.get_or_intern("i64"),
60            isize: interner.get_or_intern("isize"),
61            u8: interner.get_or_intern("u8"),
62            u16: interner.get_or_intern("u16"),
63            u32: interner.get_or_intern("u32"),
64            u64: interner.get_or_intern("u64"),
65            usize: interner.get_or_intern("usize"),
66            f16: interner.get_or_intern("f16"),
67            f32: interner.get_or_intern("f32"),
68            f64: interner.get_or_intern("f64"),
69            bool: interner.get_or_intern("bool"),
70            self_type: interner.get_or_intern("Self"),
71        }
72    }
73}
74
75/// Parser state containing primitive type symbols and file ID.
76///
77/// This struct holds mutable state needed during parsing:
78/// - Pre-interned primitive type symbols for efficient lookup
79/// - The FileId for the current file being parsed (for multi-file compilation)
80#[derive(Clone, Copy)]
81pub struct ParserState {
82    /// Pre-interned primitive type symbols.
83    pub syms: PrimitiveTypeSpurs,
84    /// The file ID for spans in this file.
85    pub file_id: FileId,
86}
87
88impl ParserState {
89    /// Create a new parser state with the given symbols and file ID.
90    pub fn new(syms: PrimitiveTypeSpurs, file_id: FileId) -> Self {
91        Self { syms, file_id }
92    }
93}
94
95/// Type alias for parser extras that carries parser state.
96/// This replaces the previous thread-local approach with compile-time safe state passing.
97type ParserExtras<'src> = extra::Full<Rich<'src, TokenKind>, SimpleState<ParserState>, ()>;
98
99/// Type-erased parser to keep symbol names short. Boxing at each function boundary
100/// prevents monomorphized type chains from growing exponentially in length.
101type GruelParser<'src, I, O> = Boxed<'src, 'src, I, O, ParserExtras<'src>>;
102
103/// Convert a `usize` offset to `u32`, asserting it fits in debug builds.
104///
105/// # Panics
106///
107/// In debug builds, panics if `offset` exceeds `u32::MAX`.
108/// This would only happen for source files larger than 4GB.
109#[inline]
110fn offset_to_u32(offset: usize) -> u32 {
111    debug_assert!(
112        offset <= u32::MAX as usize,
113        "offset {} exceeds u32::MAX (source file too large)",
114        offset
115    );
116    offset as u32
117}
118
119/// Convert chumsky SimpleSpan to gruel_span::Span with a specific file ID.
120///
121/// # Panics
122///
123/// In debug builds, panics if `span.start` or `span.end` exceeds `u32::MAX`.
124/// This would only happen for source files larger than 4GB.
125fn to_gruel_span_with_file(span: SimpleSpan, file_id: FileId) -> Span {
126    Span::with_file(file_id, offset_to_u32(span.start), offset_to_u32(span.end))
127}
128
129/// Convert chumsky SimpleSpan to gruel_span::Span using the default file ID.
130/// Only used for error conversion where we don't have access to the parser state.
131fn to_gruel_span(span: SimpleSpan) -> Span {
132    Span::new(offset_to_u32(span.start), offset_to_u32(span.end))
133}
134
135/// Extract a Span with file ID from the parser extra.
136/// This is the primary way to create spans during parsing.
137#[inline]
138fn span_from_extra<'src, I>(e: &mut MapExtra<'src, '_, I, ParserExtras<'src>>) -> Span
139where
140    I: ValueInput<'src, Token = TokenKind, Span = SimpleSpan>,
141{
142    let file_id = e.state().0.file_id;
143    to_gruel_span_with_file(e.span(), file_id)
144}
145
146/// Parser that produces Ident from identifier tokens
147fn ident_parser<'src, I>() -> GruelParser<'src, I, Ident>
148where
149    I: ValueInput<'src, Token = TokenKind, Span = SimpleSpan>,
150{
151    select! {
152        TokenKind::Ident(name) = e => Ident {
153            name,
154            span: span_from_extra(e),
155        },
156    }
157    .boxed()
158}
159
160/// Parser for primitive type keywords: i8, i16, i32, i64, u8, u16, u32, u64, bool
161/// These are reserved keywords that cannot be used as identifiers.
162fn primitive_type_parser<'src, I>() -> GruelParser<'src, I, TypeExpr>
163where
164    I: ValueInput<'src, Token = TokenKind, Span = SimpleSpan>,
165{
166    // Access primitive type symbols from parser state via e.state().0
167    // SimpleState<T> wraps T in a .0 field
168    // Type annotation on closure parameter is needed to help Rust infer the Extra type
169    let i8_parser =
170        just(TokenKind::I8).map_with(|_, e: &mut MapExtra<'src, '_, I, ParserExtras<'src>>| {
171            let syms = e.state().0.syms;
172            TypeExpr::Named(Ident {
173                name: syms.i8,
174                span: span_from_extra(e),
175            })
176        });
177    let i16_parser =
178        just(TokenKind::I16).map_with(|_, e: &mut MapExtra<'src, '_, I, ParserExtras<'src>>| {
179            let syms = e.state().0.syms;
180            TypeExpr::Named(Ident {
181                name: syms.i16,
182                span: span_from_extra(e),
183            })
184        });
185    let i32_parser =
186        just(TokenKind::I32).map_with(|_, e: &mut MapExtra<'src, '_, I, ParserExtras<'src>>| {
187            let syms = e.state().0.syms;
188            TypeExpr::Named(Ident {
189                name: syms.i32,
190                span: span_from_extra(e),
191            })
192        });
193    let i64_parser =
194        just(TokenKind::I64).map_with(|_, e: &mut MapExtra<'src, '_, I, ParserExtras<'src>>| {
195            let syms = e.state().0.syms;
196            TypeExpr::Named(Ident {
197                name: syms.i64,
198                span: span_from_extra(e),
199            })
200        });
201    let u8_parser =
202        just(TokenKind::U8).map_with(|_, e: &mut MapExtra<'src, '_, I, ParserExtras<'src>>| {
203            let syms = e.state().0.syms;
204            TypeExpr::Named(Ident {
205                name: syms.u8,
206                span: span_from_extra(e),
207            })
208        });
209    let u16_parser =
210        just(TokenKind::U16).map_with(|_, e: &mut MapExtra<'src, '_, I, ParserExtras<'src>>| {
211            let syms = e.state().0.syms;
212            TypeExpr::Named(Ident {
213                name: syms.u16,
214                span: span_from_extra(e),
215            })
216        });
217    let u32_parser =
218        just(TokenKind::U32).map_with(|_, e: &mut MapExtra<'src, '_, I, ParserExtras<'src>>| {
219            let syms = e.state().0.syms;
220            TypeExpr::Named(Ident {
221                name: syms.u32,
222                span: span_from_extra(e),
223            })
224        });
225    let u64_parser =
226        just(TokenKind::U64).map_with(|_, e: &mut MapExtra<'src, '_, I, ParserExtras<'src>>| {
227            let syms = e.state().0.syms;
228            TypeExpr::Named(Ident {
229                name: syms.u64,
230                span: span_from_extra(e),
231            })
232        });
233    let isize_parser =
234        just(TokenKind::Isize).map_with(|_, e: &mut MapExtra<'src, '_, I, ParserExtras<'src>>| {
235            let syms = e.state().0.syms;
236            TypeExpr::Named(Ident {
237                name: syms.isize,
238                span: span_from_extra(e),
239            })
240        });
241    let usize_parser =
242        just(TokenKind::Usize).map_with(|_, e: &mut MapExtra<'src, '_, I, ParserExtras<'src>>| {
243            let syms = e.state().0.syms;
244            TypeExpr::Named(Ident {
245                name: syms.usize,
246                span: span_from_extra(e),
247            })
248        });
249    let f16_parser =
250        just(TokenKind::F16).map_with(|_, e: &mut MapExtra<'src, '_, I, ParserExtras<'src>>| {
251            let syms = e.state().0.syms;
252            TypeExpr::Named(Ident {
253                name: syms.f16,
254                span: span_from_extra(e),
255            })
256        });
257    let f32_parser =
258        just(TokenKind::F32).map_with(|_, e: &mut MapExtra<'src, '_, I, ParserExtras<'src>>| {
259            let syms = e.state().0.syms;
260            TypeExpr::Named(Ident {
261                name: syms.f32,
262                span: span_from_extra(e),
263            })
264        });
265    let f64_parser =
266        just(TokenKind::F64).map_with(|_, e: &mut MapExtra<'src, '_, I, ParserExtras<'src>>| {
267            let syms = e.state().0.syms;
268            TypeExpr::Named(Ident {
269                name: syms.f64,
270                span: span_from_extra(e),
271            })
272        });
273    let bool_parser =
274        just(TokenKind::Bool).map_with(|_, e: &mut MapExtra<'src, '_, I, ParserExtras<'src>>| {
275            let syms = e.state().0.syms;
276            TypeExpr::Named(Ident {
277                name: syms.bool,
278                span: span_from_extra(e),
279            })
280        });
281
282    choice((
283        i8_parser.boxed(),
284        i16_parser.boxed(),
285        i32_parser.boxed(),
286        i64_parser.boxed(),
287        isize_parser.boxed(),
288        u8_parser.boxed(),
289        u16_parser.boxed(),
290        u32_parser.boxed(),
291        u64_parser.boxed(),
292        usize_parser.boxed(),
293        f16_parser.boxed(),
294        f32_parser.boxed(),
295        f64_parser.boxed(),
296        bool_parser.boxed(),
297    ))
298    .boxed()
299}
300
301/// Parser for type expressions: primitive types (i32, bool, etc.), () for unit, ! for never, or [T; N] for arrays
302fn type_parser<'src, I>() -> GruelParser<'src, I, TypeExpr>
303where
304    I: ValueInput<'src, Token = TokenKind, Span = SimpleSpan>,
305{
306    recursive(
307        move |ty: Recursive<Direct<'src, 'src, I, TypeExpr, ParserExtras<'src>>>| {
308            // Unit type: ()
309            let unit_type = just(TokenKind::LParen)
310                .then(just(TokenKind::RParen))
311                .map_with(|_, e| TypeExpr::Unit(span_from_extra(e)));
312
313            // Never type: !
314            let never_type =
315                just(TokenKind::Bang).map_with(|_, e| TypeExpr::Never(span_from_extra(e)));
316
317            // Array type: [T; N]
318            let array_type = just(TokenKind::LBracket)
319                .ignore_then(ty.clone())
320                .then_ignore(just(TokenKind::Semi))
321                .then(select! {
322                    TokenKind::Int(n) => n,
323                })
324                .then_ignore(just(TokenKind::RBracket))
325                .map_with(|(element, length), e| TypeExpr::Array {
326                    element: Box::new(element),
327                    length,
328                    span: span_from_extra(e),
329                });
330
331            // Anonymous struct type: struct { field: Type, ... }
332            // Used in comptime type construction
333            let anon_struct_field: GruelParser<'src, I, AnonStructField> = ident_parser()
334                .then_ignore(just(TokenKind::Colon))
335                .then(ty.clone())
336                .map_with(|(name, field_ty), e| AnonStructField {
337                    name,
338                    ty: field_ty,
339                    span: span_from_extra(e),
340                })
341                .boxed();
342
343            let anon_struct_fields: GruelParser<'src, I, Vec<AnonStructField>> = anon_struct_field
344                .separated_by(just(TokenKind::Comma))
345                .allow_trailing()
346                .collect::<Vec<_>>()
347                .boxed();
348
349            let anon_struct_type = just(TokenKind::Struct)
350                .ignore_then(just(TokenKind::LBrace))
351                .ignore_then(anon_struct_fields)
352                .then_ignore(just(TokenKind::RBrace))
353                .map_with(|fields, e| TypeExpr::AnonymousStruct {
354                    fields,
355                    methods: vec![],
356                    span: span_from_extra(e),
357                });
358
359            // Pointer type: ptr const T or ptr mut T
360            let ptr_const_type = just(TokenKind::Ptr)
361                .ignore_then(just(TokenKind::Const))
362                .ignore_then(ty.clone())
363                .map_with(|pointee, e| TypeExpr::PointerConst {
364                    pointee: Box::new(pointee),
365                    span: span_from_extra(e),
366                });
367
368            let ptr_mut_type = just(TokenKind::Ptr)
369                .ignore_then(just(TokenKind::Mut))
370                .ignore_then(ty.clone())
371                .map_with(|pointee, e| TypeExpr::PointerMut {
372                    pointee: Box::new(pointee),
373                    span: span_from_extra(e),
374                });
375
376            // Named type: user-defined types like MyStruct
377            let named_type = ident_parser().map(TypeExpr::Named);
378
379            // Self type: Self keyword used in methods to refer to the containing struct
380            let self_type = just(TokenKind::SelfType).map_with(|_, e| {
381                let span = span_from_extra(e);
382                TypeExpr::Named(Ident {
383                    name: e.state().syms.self_type,
384                    span,
385                })
386            });
387
388            // NOTE: Split into sub-groups to keep Choice<tuple> symbol length < 4K.
389            // 9 elements of Boxed<I,TypeExpr,E> in one tuple would produce ~5K symbols on macOS.
390            let types_a: GruelParser<'src, I, TypeExpr> = choice((
391                unit_type.boxed(),
392                never_type.boxed(),
393                array_type.boxed(),
394                anon_struct_type.boxed(),
395                ptr_const_type.boxed(),
396            ))
397            .boxed();
398            let types_b: GruelParser<'src, I, TypeExpr> = choice((
399                ptr_mut_type.boxed(),
400                primitive_type_parser().boxed(),
401                self_type.boxed(),
402                named_type.boxed(),
403            ))
404            .boxed();
405            choice((types_a, types_b)).boxed()
406        },
407    )
408    .boxed()
409}
410
411/// Parser for parameter mode: inout, borrow, or comptime
412fn param_mode_parser<'src, I>() -> GruelParser<'src, I, ParamMode>
413where
414    I: ValueInput<'src, Token = TokenKind, Span = SimpleSpan>,
415{
416    choice((
417        just(TokenKind::Inout).to(ParamMode::Inout).boxed(),
418        just(TokenKind::Borrow).to(ParamMode::Borrow).boxed(),
419        just(TokenKind::Comptime).to(ParamMode::Comptime).boxed(),
420    ))
421    .boxed()
422}
423
424/// Parser for function parameters: [comptime] [inout|borrow] name: type
425fn param_parser<'src, I>() -> GruelParser<'src, I, Param>
426where
427    I: ValueInput<'src, Token = TokenKind, Span = SimpleSpan>,
428{
429    just(TokenKind::Comptime)
430        .or_not()
431        .then(param_mode_parser().or_not())
432        .then(ident_parser())
433        .then_ignore(just(TokenKind::Colon))
434        .then(type_parser())
435        .map_with(|(((is_comptime, mode), name), ty), e| Param {
436            is_comptime: is_comptime.is_some(),
437            mode: mode.unwrap_or(ParamMode::Normal),
438            name,
439            ty,
440            span: span_from_extra(e),
441        })
442        .boxed()
443}
444
445/// Parser for struct field declarations: name: type
446fn field_decl_parser<'src, I>() -> GruelParser<'src, I, FieldDecl>
447where
448    I: ValueInput<'src, Token = TokenKind, Span = SimpleSpan>,
449{
450    ident_parser()
451        .then_ignore(just(TokenKind::Colon))
452        .then(type_parser())
453        .map_with(|(name, ty), e| FieldDecl {
454            name,
455            ty,
456            span: span_from_extra(e),
457        })
458        .boxed()
459}
460
461/// Parser for comma-separated struct field declarations
462fn field_decls_parser<'src, I>() -> GruelParser<'src, I, Vec<FieldDecl>>
463where
464    I: ValueInput<'src, Token = TokenKind, Span = SimpleSpan>,
465{
466    field_decl_parser()
467        .separated_by(just(TokenKind::Comma))
468        .allow_trailing()
469        .collect::<Vec<_>>()
470        .boxed()
471}
472
473/// Parser for comma-separated parameters
474fn params_parser<'src, I>() -> GruelParser<'src, I, Vec<Param>>
475where
476    I: ValueInput<'src, Token = TokenKind, Span = SimpleSpan>,
477{
478    param_parser()
479        .separated_by(just(TokenKind::Comma))
480        .collect::<Vec<_>>()
481        .boxed()
482}
483
484/// Parser for a single directive: @name or @name(arg1, arg2, ...)
485fn directive_parser<'src, I>() -> GruelParser<'src, I, Directive>
486where
487    I: ValueInput<'src, Token = TokenKind, Span = SimpleSpan>,
488{
489    just(TokenKind::At)
490        .ignore_then(ident_parser())
491        .then(
492            ident_parser()
493                .map(DirectiveArg::Ident)
494                .separated_by(just(TokenKind::Comma))
495                .allow_trailing()
496                .collect::<Vec<_>>()
497                .delimited_by(just(TokenKind::LParen), just(TokenKind::RParen))
498                .or_not(),
499        )
500        .map_with(|(name, args), e| Directive {
501            name,
502            args: args.unwrap_or_default(),
503            span: span_from_extra(e),
504        })
505        .boxed()
506}
507
508/// Parser for zero or more directives
509fn directives_parser<'src, I>() -> GruelParser<'src, I, Directives>
510where
511    I: ValueInput<'src, Token = TokenKind, Span = SimpleSpan>,
512{
513    // Chumsky's collect() requires its Container trait, which SmallVec doesn't implement.
514    // So we collect to Vec first, then convert. The overhead is minimal since most
515    // items have 0-1 directives (Vec is cheap for empty/small collections).
516    directive_parser()
517        .repeated()
518        .collect::<Vec<_>>()
519        .map(|v| v.into_iter().collect())
520        .boxed()
521}
522
523/// Parser for argument mode: inout or borrow
524fn arg_mode_parser<'src, I>() -> GruelParser<'src, I, ArgMode>
525where
526    I: ValueInput<'src, Token = TokenKind, Span = SimpleSpan>,
527{
528    choice((
529        just(TokenKind::Inout).to(ArgMode::Inout).boxed(),
530        just(TokenKind::Borrow).to(ArgMode::Borrow).boxed(),
531    ))
532    .boxed()
533}
534
535/// Parser for a single call argument: [inout|borrow] expr
536fn call_arg_parser<'src, I>(expr: GruelParser<'src, I, Expr>) -> GruelParser<'src, I, CallArg>
537where
538    I: ValueInput<'src, Token = TokenKind, Span = SimpleSpan>,
539{
540    arg_mode_parser()
541        .or_not()
542        .then(expr)
543        .map_with(|(mode, expr), e| CallArg {
544            mode: mode.unwrap_or(ArgMode::Normal),
545            expr,
546            span: span_from_extra(e),
547        })
548        .boxed()
549}
550
551/// Parser for comma-separated call arguments with optional inout
552fn call_args_parser<'src, I>(expr: GruelParser<'src, I, Expr>) -> GruelParser<'src, I, Vec<CallArg>>
553where
554    I: ValueInput<'src, Token = TokenKind, Span = SimpleSpan>,
555{
556    call_arg_parser(expr)
557        .separated_by(just(TokenKind::Comma))
558        .collect::<Vec<_>>()
559        .boxed()
560}
561
562/// Parser for comma-separated expression arguments (for contexts that don't support inout)
563fn args_parser<'src, I>(expr: GruelParser<'src, I, Expr>) -> GruelParser<'src, I, Vec<Expr>>
564where
565    I: ValueInput<'src, Token = TokenKind, Span = SimpleSpan>,
566{
567    expr.separated_by(just(TokenKind::Comma))
568        .collect::<Vec<_>>()
569        .boxed()
570}
571
572/// Parser for struct field initializers: name: expr
573fn field_init_parser<'src, I>(expr: GruelParser<'src, I, Expr>) -> GruelParser<'src, I, FieldInit>
574where
575    I: ValueInput<'src, Token = TokenKind, Span = SimpleSpan>,
576{
577    // Field init with explicit value: `name: expr`
578    let explicit = ident_parser()
579        .then_ignore(just(TokenKind::Colon))
580        .then(expr)
581        .map_with(|(name, value), e| FieldInit {
582            name,
583            value: Box::new(value),
584            span: span_from_extra(e),
585        });
586
587    // Field init shorthand: `name` means `name: name`
588    let shorthand = ident_parser().map_with(|name, e| FieldInit {
589        value: Box::new(Expr::Ident(name)),
590        name,
591        span: span_from_extra(e),
592    });
593
594    choice((explicit, shorthand)).boxed()
595}
596
597/// Parser for comma-separated field initializers
598fn field_inits_parser<'src, I>(
599    expr: GruelParser<'src, I, Expr>,
600) -> GruelParser<'src, I, Vec<FieldInit>>
601where
602    I: ValueInput<'src, Token = TokenKind, Span = SimpleSpan>,
603{
604    field_init_parser(expr)
605        .separated_by(just(TokenKind::Comma))
606        .allow_trailing()
607        .collect::<Vec<_>>()
608        .boxed()
609}
610
611/// Helper to create binary expression
612fn make_binary(left: Expr, op: BinaryOp, right: Expr) -> Expr {
613    let span = Span::new(left.span().start, right.span().end);
614    Expr::Binary(BinaryExpr {
615        left: Box::new(left),
616        op,
617        right: Box::new(right),
618        span,
619    })
620}
621
622/// Helper to create unary expression
623fn make_unary(op: UnaryOp, operand: Expr, op_span: SimpleSpan) -> Expr {
624    let span = Span::new(offset_to_u32(op_span.start), operand.span().end);
625    Expr::Unary(UnaryExpr {
626        op,
627        operand: Box::new(operand),
628        span,
629    })
630}
631
632/// Expression parser using manual precedence climbing.
633///
634/// Operator precedence (tightest binding first):
635///   unary prefix: -, !, ~
636///   multiplicative: *, /, %
637///   additive: +, -
638///   shift: <<, >>
639///   comparison: ==, !=, <, >, <=, >=
640///   bitwise AND: &
641///   bitwise XOR: ^
642///   bitwise OR: |
643///   logical AND: &&
644///   logical OR: ||  (loosest binding)
645///
646/// Each level is immediately `.boxed()` to keep the Rc'd type short and avoid
647/// the huge drop-glue symbols that Chumsky's Pratt parser would generate (the
648/// Pratt operator-tuple type embeds `I` dozens of times).
649fn expr_parser<'src, I>() -> GruelParser<'src, I, Expr>
650where
651    I: ValueInput<'src, Token = TokenKind, Span = SimpleSpan>,
652{
653    recursive(
654        |expr: Recursive<Direct<'src, 'src, I, Expr, ParserExtras<'src>>>| {
655            // Atom parser – primary expressions (highest precedence).
656            let atom: GruelParser<I, Expr> = atom_parser(expr.clone().boxed());
657
658            // Unary prefix operators: -, !, ~  (right-associative; stack and apply inside-out)
659            let unary: GruelParser<I, Expr> = {
660                let prefix_op: GruelParser<I, (UnaryOp, SimpleSpan)> = choice((
661                    just(TokenKind::Minus)
662                        .map_with(|_, e| (UnaryOp::Neg, e.span()))
663                        .boxed(),
664                    just(TokenKind::Bang)
665                        .map_with(|_, e| (UnaryOp::Not, e.span()))
666                        .boxed(),
667                    just(TokenKind::Tilde)
668                        .map_with(|_, e| (UnaryOp::BitNot, e.span()))
669                        .boxed(),
670                ))
671                .boxed();
672                prefix_op
673                    .repeated()
674                    .collect::<Vec<_>>()
675                    .then(atom.clone())
676                    .map(|(mut ops, mut rhs)| {
677                        // Apply operators from innermost (rightmost) outward.
678                        ops.reverse();
679                        for (op, span) in ops {
680                            rhs = make_unary(op, rhs, span);
681                        }
682                        rhs
683                    })
684                    .boxed()
685            };
686
687            // Helper macro: build one left-associative binary level.
688            // Each call boxes the result, keeping the drop-glue type short.
689            macro_rules! left_binary {
690                ($prev:expr, $op_parser:expr) => {{
691                    let prev: GruelParser<I, Expr> = $prev;
692                    let op: GruelParser<I, BinaryOp> = $op_parser;
693                    prev.clone()
694                        .foldl(op.then(prev).repeated(), |l, (op, r)| make_binary(l, op, r))
695                        .boxed()
696                }};
697            }
698
699            // Multiplicative: *, /, %
700            let multiplicative: GruelParser<I, Expr> = left_binary!(
701                unary,
702                choice((
703                    just(TokenKind::Star).to(BinaryOp::Mul).boxed(),
704                    just(TokenKind::Slash).to(BinaryOp::Div).boxed(),
705                    just(TokenKind::Percent).to(BinaryOp::Mod).boxed(),
706                ))
707                .boxed()
708            );
709
710            // Additive: +, -
711            let additive: GruelParser<I, Expr> = left_binary!(
712                multiplicative,
713                choice((
714                    just(TokenKind::Plus).to(BinaryOp::Add).boxed(),
715                    just(TokenKind::Minus).to(BinaryOp::Sub).boxed(),
716                ))
717                .boxed()
718            );
719
720            // Shift: <<, >>
721            let shift: GruelParser<I, Expr> = left_binary!(
722                additive,
723                choice((
724                    just(TokenKind::LtLt).to(BinaryOp::Shl).boxed(),
725                    just(TokenKind::GtGt).to(BinaryOp::Shr).boxed(),
726                ))
727                .boxed()
728            );
729
730            // Comparison: ==, !=, <, >, <=, >=
731            let comparison: GruelParser<I, Expr> = left_binary!(
732                shift,
733                choice((
734                    just(TokenKind::EqEq).to(BinaryOp::Eq).boxed(),
735                    just(TokenKind::BangEq).to(BinaryOp::Ne).boxed(),
736                    just(TokenKind::Lt).to(BinaryOp::Lt).boxed(),
737                    just(TokenKind::Gt).to(BinaryOp::Gt).boxed(),
738                    just(TokenKind::LtEq).to(BinaryOp::Le).boxed(),
739                    just(TokenKind::GtEq).to(BinaryOp::Ge).boxed(),
740                ))
741                .boxed()
742            );
743
744            // Bitwise AND: &
745            let bitwise_and: GruelParser<I, Expr> = left_binary!(
746                comparison,
747                just(TokenKind::Amp).to(BinaryOp::BitAnd).boxed()
748            );
749
750            // Bitwise XOR: ^
751            let bitwise_xor: GruelParser<I, Expr> = left_binary!(
752                bitwise_and,
753                just(TokenKind::Caret).to(BinaryOp::BitXor).boxed()
754            );
755
756            // Bitwise OR: |
757            let bitwise_or: GruelParser<I, Expr> = left_binary!(
758                bitwise_xor,
759                just(TokenKind::Pipe).to(BinaryOp::BitOr).boxed()
760            );
761
762            // Logical AND: &&
763            let logical_and: GruelParser<I, Expr> = left_binary!(
764                bitwise_or,
765                just(TokenKind::AmpAmp).to(BinaryOp::And).boxed()
766            );
767
768            // Logical OR: || (lowest binary precedence)
769            let logical_or: GruelParser<I, Expr> = left_binary!(
770                logical_and,
771                just(TokenKind::PipePipe).to(BinaryOp::Or).boxed()
772            );
773
774            logical_or
775        },
776    )
777    .boxed()
778}
779
780/// Parser for patterns in match arms
781fn pattern_parser<'src, I>() -> GruelParser<'src, I, Pattern>
782where
783    I: ValueInput<'src, Token = TokenKind, Span = SimpleSpan>,
784{
785    // Wildcard pattern: _
786    let wildcard =
787        just(TokenKind::Underscore).map_with(|_, e| Pattern::Wildcard(span_from_extra(e)));
788
789    // Integer literal pattern (positive or zero)
790    let int_pat = select! {
791        TokenKind::Int(n) = e => Pattern::Int(IntLit {
792            value: n,
793            span: span_from_extra(e),
794        }),
795    };
796
797    // Negative integer literal pattern: - followed by integer
798    let neg_int_pat = just(TokenKind::Minus)
799        .then(select! { TokenKind::Int(n) => n })
800        .map_with(|(_, n), e| {
801            Pattern::NegInt(NegIntLit {
802                value: n,
803                span: span_from_extra(e),
804            })
805        });
806
807    // Boolean literal patterns
808    let bool_true = select! {
809        TokenKind::True = e => Pattern::Bool(BoolLit {
810            value: true,
811            span: span_from_extra(e),
812        }),
813    };
814
815    let bool_false = select! {
816        TokenKind::False = e => Pattern::Bool(BoolLit {
817            value: false,
818            span: span_from_extra(e),
819        }),
820    };
821
822    // Parser for a single binding in a data variant pattern: `_`, `x`, or `mut x`
823    let pattern_binding = choice((
824        just(TokenKind::Underscore).map_with(|_, e| PatternBinding::Wildcard(span_from_extra(e))),
825        just(TokenKind::Mut)
826            .ignore_then(ident_parser())
827            .map(|name| PatternBinding::Ident { is_mut: true, name }),
828        ident_parser().map(|name| PatternBinding::Ident {
829            is_mut: false,
830            name,
831        }),
832    ));
833
834    // Parser for optional `(binding, binding, ...)` suffix on a path pattern
835    let bindings_suffix = pattern_binding
836        .clone()
837        .separated_by(just(TokenKind::Comma))
838        .allow_trailing()
839        .collect::<Vec<_>>()
840        .delimited_by(just(TokenKind::LParen), just(TokenKind::RParen));
841
842    // Parser for a single field binding in a struct variant pattern:
843    // `field: binding` or `field` (shorthand for `field: field`)
844    let pattern_field_binding = {
845        let explicit = ident_parser()
846            .then_ignore(just(TokenKind::Colon))
847            .then(pattern_binding.clone())
848            .map(|(field_name, binding)| PatternFieldBinding {
849                field_name,
850                binding,
851            });
852        let shorthand = ident_parser().map(|name| PatternFieldBinding {
853            field_name: name,
854            binding: PatternBinding::Ident {
855                is_mut: false,
856                name,
857            },
858        });
859        choice((explicit, shorthand))
860    };
861
862    // Parser for optional `{ field: binding, ... }` suffix on a path pattern
863    let struct_bindings_suffix = pattern_field_binding
864        .separated_by(just(TokenKind::Comma))
865        .allow_trailing()
866        .collect::<Vec<_>>()
867        .delimited_by(just(TokenKind::LBrace), just(TokenKind::RBrace));
868
869    // Enum for the suffix on an enum variant pattern: tuple, struct, or none.
870    #[derive(Debug, Clone)]
871    enum PatternSuffix {
872        Tuple(Vec<PatternBinding>),
873        Struct(Vec<PatternFieldBinding>),
874    }
875
876    let pattern_suffix = choice((
877        bindings_suffix.clone().map(PatternSuffix::Tuple),
878        struct_bindings_suffix.clone().map(PatternSuffix::Struct),
879    ));
880
881    // Self path pattern: Self::Variant, Self::Variant(binding, ...), or Self::Variant { field: binding, ... }
882    // Used inside anonymous enum methods to match on the enum type via Self
883    let self_path_pat = just(TokenKind::SelfType)
884        .ignore_then(just(TokenKind::ColonColon))
885        .ignore_then(ident_parser())
886        .then(pattern_suffix.clone().or_not())
887        .map_with(|(variant, suffix_opt), e| {
888            let span = span_from_extra(e);
889            let type_name = Ident {
890                name: e.state().syms.self_type,
891                span,
892            };
893            match suffix_opt {
894                Some(PatternSuffix::Tuple(bindings)) => Pattern::DataVariant {
895                    base: None,
896                    type_name,
897                    variant,
898                    bindings,
899                    span,
900                },
901                Some(PatternSuffix::Struct(fields)) => Pattern::StructVariant {
902                    base: None,
903                    type_name,
904                    variant,
905                    fields,
906                    span,
907                },
908                None => Pattern::Path(PathPattern {
909                    base: None,
910                    type_name,
911                    variant,
912                    span,
913                }),
914            }
915        });
916
917    // Simple path pattern: Enum::Variant, Enum::Variant(binding, ...), or Enum::Variant { field: binding, ... }
918    let simple_path_pat = ident_parser()
919        .then_ignore(just(TokenKind::ColonColon))
920        .then(ident_parser())
921        .then(pattern_suffix.clone().or_not())
922        .map_with(|((type_name, variant), suffix_opt), e| match suffix_opt {
923            Some(PatternSuffix::Tuple(bindings)) => Pattern::DataVariant {
924                base: None,
925                type_name,
926                variant,
927                bindings,
928                span: span_from_extra(e),
929            },
930            Some(PatternSuffix::Struct(fields)) => Pattern::StructVariant {
931                base: None,
932                type_name,
933                variant,
934                fields,
935                span: span_from_extra(e),
936            },
937            None => Pattern::Path(PathPattern {
938                base: None,
939                type_name,
940                variant,
941                span: span_from_extra(e),
942            }),
943        });
944
945    // Qualified path pattern: module.Enum::Variant or module.sub.Enum::Variant
946    // Parses: ident ("." ident)+ "::" ident
947    // where the part before "::" is module.Type and after is Variant
948    let qualified_path_pat = ident_parser()
949        .then(
950            just(TokenKind::Dot)
951                .ignore_then(ident_parser())
952                .repeated()
953                .at_least(1)
954                .collect::<Vec<_>>(),
955        )
956        .then_ignore(just(TokenKind::ColonColon))
957        .then(ident_parser())
958        .then(pattern_suffix.or_not())
959        .map_with(|(((first, mut rest), variant), suffix_opt), e| {
960            // first is the first module identifier
961            // rest contains all subsequent identifiers up to and including type_name
962            // The last element of rest is the type_name
963            let type_name = rest.pop().expect("at_least(1) guarantees non-empty");
964
965            // Build the base expression: first.rest[0].rest[1]...
966            let base_expr = if rest.is_empty() {
967                // Just `module.Type::Variant` - base is single ident
968                Expr::Ident(first)
969            } else {
970                // `a.b.c.Type::Variant` - build field access chain
971                let mut base = Expr::Ident(first);
972                for field in rest {
973                    let span = base.span().extend_to(field.span.end);
974                    base = Expr::Field(FieldExpr {
975                        base: Box::new(base),
976                        field,
977                        span,
978                    });
979                }
980                base
981            };
982
983            match suffix_opt {
984                Some(PatternSuffix::Tuple(bindings)) => Pattern::DataVariant {
985                    base: Some(Box::new(base_expr)),
986                    type_name,
987                    variant,
988                    bindings,
989                    span: span_from_extra(e),
990                },
991                Some(PatternSuffix::Struct(fields)) => Pattern::StructVariant {
992                    base: Some(Box::new(base_expr)),
993                    type_name,
994                    variant,
995                    fields,
996                    span: span_from_extra(e),
997                },
998                None => Pattern::Path(PathPattern {
999                    base: Some(Box::new(base_expr)),
1000                    type_name,
1001                    variant,
1002                    span: span_from_extra(e),
1003                }),
1004            }
1005        });
1006
1007    choice((
1008        wildcard.boxed(),
1009        neg_int_pat.boxed(),
1010        int_pat.boxed(),
1011        bool_true.boxed(),
1012        bool_false.boxed(),
1013        // Try qualified path first (has more structure), then Self path, then simple path
1014        qualified_path_pat.boxed(),
1015        self_path_pat.boxed(),
1016        simple_path_pat.boxed(),
1017    ))
1018    .boxed()
1019}
1020
1021/// Parser for a single match arm: pattern => expr
1022fn match_arm_parser<'src, I>(expr: GruelParser<'src, I, Expr>) -> GruelParser<'src, I, MatchArm>
1023where
1024    I: ValueInput<'src, Token = TokenKind, Span = SimpleSpan>,
1025{
1026    pattern_parser()
1027        .then_ignore(just(TokenKind::FatArrow))
1028        .then(expr)
1029        .map_with(|(pattern, body), e| MatchArm {
1030            pattern,
1031            body: Box::new(body),
1032            span: span_from_extra(e),
1033        })
1034        .boxed()
1035}
1036
1037/// Parser for literal expressions: integers, strings, booleans, and unit
1038fn literal_parser<'src, I>() -> GruelParser<'src, I, Expr>
1039where
1040    I: ValueInput<'src, Token = TokenKind, Span = SimpleSpan>,
1041{
1042    // Integer literal
1043    let int_lit = select! {
1044        TokenKind::Int(n) = e => Expr::Int(IntLit {
1045            value: n,
1046            span: span_from_extra(e),
1047        }),
1048    };
1049
1050    // Floating-point literal
1051    let float_lit = select! {
1052        TokenKind::Float(bits) = e => Expr::Float(FloatLit {
1053            bits,
1054            span: span_from_extra(e),
1055        }),
1056    };
1057
1058    // String literal
1059    let string_lit = select! {
1060        TokenKind::String(s) = e => Expr::String(StringLit {
1061            value: s,
1062            span: span_from_extra(e),
1063        }),
1064    };
1065
1066    // Boolean literals
1067    let bool_true = select! {
1068        TokenKind::True = e => Expr::Bool(BoolLit {
1069            value: true,
1070            span: span_from_extra(e),
1071        }),
1072    };
1073
1074    let bool_false = select! {
1075        TokenKind::False = e => Expr::Bool(BoolLit {
1076            value: false,
1077            span: span_from_extra(e),
1078        }),
1079    };
1080
1081    // Unit literal: ()
1082    let unit_lit = just(TokenKind::LParen)
1083        .then(just(TokenKind::RParen))
1084        .map_with(|_, e| {
1085            Expr::Unit(UnitLit {
1086                span: span_from_extra(e),
1087            })
1088        });
1089
1090    choice((
1091        int_lit.boxed(),
1092        float_lit.boxed(),
1093        string_lit.boxed(),
1094        bool_true.boxed(),
1095        bool_false.boxed(),
1096        unit_lit.boxed(),
1097    ))
1098    .boxed()
1099}
1100
1101/// Parser for control flow expressions: break, continue, return, if, while, loop, match
1102fn control_flow_parser<'src, I>(expr: GruelParser<'src, I, Expr>) -> GruelParser<'src, I, Expr>
1103where
1104    I: ValueInput<'src, Token = TokenKind, Span = SimpleSpan>,
1105{
1106    // Break
1107    let break_expr = select! {
1108        TokenKind::Break = e => Expr::Break(BreakExpr { span: span_from_extra(e) }),
1109    };
1110
1111    // Continue
1112    let continue_expr = select! {
1113        TokenKind::Continue = e => Expr::Continue(ContinueExpr { span: span_from_extra(e) }),
1114    };
1115
1116    // Return expression: return <expr>? (expression is optional for unit-returning functions)
1117    let return_expr = just(TokenKind::Return)
1118        .ignore_then(expr.clone().or_not())
1119        .map_with(|value, e| {
1120            Expr::Return(ReturnExpr {
1121                value: value.map(Box::new),
1122                span: span_from_extra(e),
1123            })
1124        });
1125
1126    // If expression - defined with recursive reference to allow `else if` chains
1127    let if_expr: GruelParser<'src, I, Expr> = recursive(
1128        |if_expr_rec: Recursive<Direct<'src, 'src, I, Expr, ParserExtras<'src>>>| {
1129            just(TokenKind::If)
1130                .ignore_then(expr.clone())
1131                .then(maybe_unit_block_parser(expr.clone()))
1132                .then(
1133                    just(TokenKind::Else)
1134                        .ignore_then(choice((
1135                            // else if: wrap the nested if in a synthetic block
1136                            if_expr_rec
1137                                .map_with(|nested_if, e| {
1138                                    let span = span_from_extra(e);
1139                                    BlockExpr {
1140                                        statements: Vec::new(),
1141                                        expr: Box::new(nested_if),
1142                                        span,
1143                                    }
1144                                })
1145                                .boxed(),
1146                            // else { ... }: parse a regular block
1147                            maybe_unit_block_parser(expr.clone()),
1148                        )))
1149                        .or_not(),
1150                )
1151                .map_with(|((cond, then_block), else_block), e| {
1152                    Expr::If(IfExpr {
1153                        cond: Box::new(cond),
1154                        then_block,
1155                        else_block,
1156                        span: span_from_extra(e),
1157                    })
1158                })
1159                .boxed()
1160        },
1161    )
1162    .boxed();
1163
1164    // While expression
1165    let while_expr: GruelParser<'src, I, Expr> = just(TokenKind::While)
1166        .ignore_then(expr.clone())
1167        .then(maybe_unit_block_parser(expr.clone()))
1168        .map_with(|(cond, body), e| {
1169            Expr::While(WhileExpr {
1170                cond: Box::new(cond),
1171                body,
1172                span: span_from_extra(e),
1173            })
1174        })
1175        .boxed();
1176
1177    // For-in expression: for [mut] ident in expr { body }
1178    let for_expr: GruelParser<'src, I, Expr> = just(TokenKind::For)
1179        .ignore_then(just(TokenKind::Mut).or_not())
1180        .then(ident_parser())
1181        .then_ignore(just(TokenKind::In))
1182        .then(expr.clone())
1183        .then(maybe_unit_block_parser(expr.clone()))
1184        .map_with(|(((is_mut, binding), iterable), body), e| {
1185            Expr::For(ForExpr {
1186                binding,
1187                is_mut: is_mut.is_some(),
1188                iterable: Box::new(iterable),
1189                body,
1190                span: span_from_extra(e),
1191            })
1192        })
1193        .boxed();
1194
1195    // Loop expression (infinite loop)
1196    let loop_expr: GruelParser<'src, I, Expr> = just(TokenKind::Loop)
1197        .ignore_then(maybe_unit_block_parser(expr.clone()))
1198        .map_with(|body, e| {
1199            Expr::Loop(LoopExpr {
1200                body,
1201                span: span_from_extra(e),
1202            })
1203        })
1204        .boxed();
1205
1206    // Match expression: match scrutinee { pattern => expr, ... }
1207    let match_expr: GruelParser<'src, I, Expr> = just(TokenKind::Match)
1208        .ignore_then(expr.clone())
1209        .then(
1210            match_arm_parser(expr.clone())
1211                .separated_by(just(TokenKind::Comma))
1212                .allow_trailing()
1213                .collect::<Vec<_>>()
1214                .delimited_by(just(TokenKind::LBrace), just(TokenKind::RBrace)),
1215        )
1216        .map_with(|(scrutinee, arms), e| {
1217            Expr::Match(MatchExpr {
1218                scrutinee: Box::new(scrutinee),
1219                arms,
1220                span: span_from_extra(e),
1221            })
1222        })
1223        .boxed();
1224
1225    // Comptime unroll for expression: comptime_unroll for ident in expr { body }
1226    let comptime_unroll_for_expr: GruelParser<'src, I, Expr> = just(TokenKind::ComptimeUnroll)
1227        .ignore_then(just(TokenKind::For))
1228        .ignore_then(ident_parser())
1229        .then_ignore(just(TokenKind::In))
1230        .then(expr.clone())
1231        .then(maybe_unit_block_parser(expr.clone()))
1232        .map_with(|((binding, iterable), body), e| {
1233            Expr::ComptimeUnrollFor(ComptimeUnrollForExpr {
1234                binding,
1235                iterable: Box::new(iterable),
1236                body,
1237                span: span_from_extra(e),
1238            })
1239        })
1240        .boxed();
1241
1242    choice((
1243        break_expr.boxed(),
1244        continue_expr.boxed(),
1245        return_expr.boxed(),
1246        if_expr,
1247        while_expr,
1248        for_expr,
1249        comptime_unroll_for_expr,
1250        loop_expr,
1251        match_expr,
1252    ))
1253    .boxed()
1254}
1255
1256/// What can follow an identifier: call args, struct fields, path (::variant), path call (::fn()), or nothing
1257#[derive(Clone)]
1258enum IdentSuffix {
1259    Call(Vec<CallArg>),
1260    StructLit(Vec<FieldInit>),
1261    Path(Ident),                          // ::Variant (for enum variants)
1262    PathCall(Ident, Vec<CallArg>),        // ::function() (for associated functions)
1263    PathStructLit(Ident, Vec<FieldInit>), // ::Variant { field: value } (for enum struct variants)
1264    None,
1265}
1266
1267/// Parser for identifier-based expressions: identifiers, function calls, struct literals, and paths
1268fn call_and_access_parser<'src, I>(expr: GruelParser<'src, I, Expr>) -> GruelParser<'src, I, Expr>
1269where
1270    I: ValueInput<'src, Token = TokenKind, Span = SimpleSpan>,
1271{
1272    ident_parser()
1273        .then(
1274            choice((
1275                // Function call: (args)
1276                call_args_parser(expr.clone())
1277                    .delimited_by(just(TokenKind::LParen), just(TokenKind::RParen))
1278                    .map(IdentSuffix::Call)
1279                    .boxed(),
1280                // Struct literal: { field: value, ... } or { field, ... } (shorthand)
1281                // Lookahead: require `{ }` or `{ ident :` or `{ ident ,`
1282                // to disambiguate from blocks like `if cond { expr }`
1283                just(TokenKind::LBrace)
1284                    .then(
1285                        choice((
1286                            just(TokenKind::RBrace).ignored(),
1287                            select! { TokenKind::Ident(_) => () }
1288                                .then_ignore(just(TokenKind::Colon))
1289                                .ignored(),
1290                            select! { TokenKind::Ident(_) => () }
1291                                .then_ignore(just(TokenKind::Comma))
1292                                .ignored(),
1293                        ))
1294                        .rewind(),
1295                    )
1296                    .rewind()
1297                    .ignore_then(
1298                        field_inits_parser(expr.clone())
1299                            .delimited_by(just(TokenKind::LBrace), just(TokenKind::RBrace)),
1300                    )
1301                    .map(IdentSuffix::StructLit)
1302                    .boxed(),
1303                // Associated function call: ::function(args)
1304                just(TokenKind::ColonColon)
1305                    .ignore_then(ident_parser())
1306                    .then(
1307                        call_args_parser(expr.clone())
1308                            .delimited_by(just(TokenKind::LParen), just(TokenKind::RParen)),
1309                    )
1310                    .map(|(func, args)| IdentSuffix::PathCall(func, args))
1311                    .boxed(),
1312                // Enum struct variant literal: ::Variant { field: value, ... }
1313                just(TokenKind::ColonColon)
1314                    .ignore_then(ident_parser())
1315                    .then(
1316                        field_inits_parser(expr.clone())
1317                            .delimited_by(just(TokenKind::LBrace), just(TokenKind::RBrace)),
1318                    )
1319                    .map(|(variant, fields)| IdentSuffix::PathStructLit(variant, fields))
1320                    .boxed(),
1321                // Path: ::Variant (for enum variants)
1322                just(TokenKind::ColonColon)
1323                    .ignore_then(ident_parser())
1324                    .map(IdentSuffix::Path)
1325                    .boxed(),
1326            ))
1327            .or_not()
1328            .map(|opt| opt.unwrap_or(IdentSuffix::None)),
1329        )
1330        .map_with(|(name, suffix), e| match suffix {
1331            IdentSuffix::Call(args) => Expr::Call(CallExpr {
1332                name,
1333                args,
1334                span: span_from_extra(e),
1335            }),
1336            IdentSuffix::StructLit(fields) => Expr::StructLit(StructLitExpr {
1337                base: None, // No module prefix for simple `StructName { ... }`
1338                name,
1339                fields,
1340                span: span_from_extra(e),
1341            }),
1342            IdentSuffix::PathCall(function, args) => Expr::AssocFnCall(AssocFnCallExpr {
1343                base: None, // No module prefix for simple `Type::function()`
1344                type_name: name,
1345                function,
1346                args,
1347                span: span_from_extra(e),
1348            }),
1349            IdentSuffix::PathStructLit(variant, fields) => Expr::EnumStructLit(EnumStructLitExpr {
1350                base: None,
1351                type_name: name,
1352                variant,
1353                fields,
1354                span: span_from_extra(e),
1355            }),
1356            IdentSuffix::Path(variant) => Expr::Path(PathExpr {
1357                base: None, // No module prefix for simple `Enum::Variant`
1358                type_name: name,
1359                variant,
1360                span: span_from_extra(e),
1361            }),
1362            IdentSuffix::None => Expr::Ident(name),
1363        })
1364        .boxed()
1365}
1366
1367/// Suffix for field access (.field), method call (.method(args)), indexing ([expr]),
1368/// qualified struct literals (.Type { ... }), and qualified paths (.Enum::Variant)
1369#[derive(Clone)]
1370enum Suffix {
1371    /// Simple field access: .field
1372    Field(Ident),
1373    /// Method call with method name, arguments, and closing paren position
1374    MethodCall(Ident, Vec<CallArg>, u32),
1375    /// Index expression with the inner expression and closing bracket position
1376    Index(Expr, u32),
1377    /// Qualified struct literal: .StructName { fields }
1378    /// NOTE: Not yet wired up due to grammar ambiguity with field access + block
1379    QualifiedStructLit(Ident, Vec<FieldInit>, u32),
1380    /// Qualified path (enum variant): .EnumName::Variant
1381    QualifiedPath(Ident, Ident, u32),
1382    /// Qualified associated function call: .TypeName::function(args)
1383    QualifiedAssocFnCall(Ident, Ident, Vec<CallArg>, u32),
1384    /// Qualified enum struct variant literal: .EnumName::Variant { field: value }
1385    QualifiedEnumStructLit(Ident, Ident, Vec<FieldInit>, u32),
1386}
1387
1388/// Wraps a primary expression parser with field access, method call, and indexing suffixes
1389fn with_suffix_parser<'src, I>(
1390    primary: impl Parser<'src, I, Expr, ParserExtras<'src>> + Clone + 'src,
1391    expr: GruelParser<'src, I, Expr>,
1392) -> GruelParser<'src, I, Expr>
1393where
1394    I: ValueInput<'src, Token = TokenKind, Span = SimpleSpan>,
1395{
1396    // Method call: .ident(args)
1397    let method_call_suffix = just(TokenKind::Dot)
1398        .ignore_then(ident_parser())
1399        .then(
1400            call_args_parser(expr.clone())
1401                .delimited_by(just(TokenKind::LParen), just(TokenKind::RParen)),
1402        )
1403        .map_with(|(method, args), e| {
1404            Suffix::MethodCall(method, args, offset_to_u32(e.span().end))
1405        });
1406
1407    // Qualified struct literal: .StructName { field: value, ... }
1408    // We need to distinguish between:
1409    //   - `module.Point { x: 1 }` - qualified struct literal
1410    //   - `obj.field { 1 }` - field access followed by a block expression
1411    //
1412    // The key difference is that struct literals have `{ ident: value, ... }` while
1413    // block expressions have `{ expr }`. We use a lookahead to require that `{` is
1414    // followed by `}` (empty struct) or `ident :` (non-empty struct) to confirm it's
1415    // a struct literal, not field access followed by a block.
1416    //
1417    // Lookahead check: succeeds (without consuming) if `{ }` or `{ ident : `
1418    let struct_lit_lookahead = just(TokenKind::LBrace)
1419        .then(
1420            choice((
1421                // Empty struct: { }
1422                just(TokenKind::RBrace).ignored(),
1423                // Non-empty struct: { ident : ...
1424                select! { TokenKind::Ident(_) => () }
1425                    .then_ignore(just(TokenKind::Colon))
1426                    .ignored(),
1427            ))
1428            // We're just checking the pattern exists, not consuming
1429            .rewind(),
1430        )
1431        .rewind();
1432
1433    // NOTE: Box the lookahead itself to prevent its complex nested type from
1434    // accumulating in qualified_struct_lit_suffix before that parser is boxed.
1435    let struct_lit_lookahead: GruelParser<'src, I, _> = struct_lit_lookahead.boxed();
1436
1437    // NOTE: .boxed() is required here to shorten the monomorphized type name.
1438    // The lookahead creates deeply nested generics that exceed macOS linker limits.
1439    // We split into a boxed head (name + lookahead) and then chain the body.
1440    let struct_lit_name: GruelParser<'src, I, Ident> = just(TokenKind::Dot)
1441        .ignore_then(ident_parser())
1442        .then_ignore(struct_lit_lookahead)
1443        .boxed();
1444    let qualified_struct_lit_suffix = struct_lit_name
1445        .then(
1446            field_inits_parser(expr.clone())
1447                .delimited_by(just(TokenKind::LBrace), just(TokenKind::RBrace)),
1448        )
1449        .map_with(|(name, fields), e| {
1450            Suffix::QualifiedStructLit(name, fields, offset_to_u32(e.span().end))
1451        })
1452        .boxed();
1453
1454    // NOTE: .boxed() on the shared head prevents type accumulation in both
1455    // qualified_assoc_fn_suffix and qualified_path_suffix.
1456    let type_and_member: GruelParser<'src, I, (Ident, Ident)> = just(TokenKind::Dot)
1457        .ignore_then(ident_parser())
1458        .then_ignore(just(TokenKind::ColonColon))
1459        .then(ident_parser())
1460        .boxed();
1461
1462    // Qualified associated function call: .TypeName::function(args)
1463    let qualified_assoc_fn_suffix = type_and_member
1464        .clone()
1465        .then(
1466            call_args_parser(expr.clone())
1467                .delimited_by(just(TokenKind::LParen), just(TokenKind::RParen)),
1468        )
1469        .map_with(|((type_name, function), args), e| {
1470            Suffix::QualifiedAssocFnCall(type_name, function, args, offset_to_u32(e.span().end))
1471        });
1472
1473    // Qualified enum struct variant literal: .EnumName::Variant { field: value, ... }
1474    let qualified_enum_struct_lit_suffix = type_and_member
1475        .clone()
1476        .then(
1477            field_inits_parser(expr.clone())
1478                .delimited_by(just(TokenKind::LBrace), just(TokenKind::RBrace)),
1479        )
1480        .map_with(|((type_name, variant), fields), e| {
1481            Suffix::QualifiedEnumStructLit(type_name, variant, fields, offset_to_u32(e.span().end))
1482        });
1483
1484    // Qualified path (enum variant): .EnumName::Variant
1485    // We capture the end position from the variant ident before the negative lookahead
1486    let qualified_path_suffix = type_and_member
1487        .map(|(type_name, variant): (Ident, Ident)| {
1488            let end = variant.span.end;
1489            (type_name, variant, end)
1490        })
1491        .then_ignore(none_of([TokenKind::LParen, TokenKind::LBrace]).rewind())
1492        .map(|(type_name, variant, end)| Suffix::QualifiedPath(type_name, variant, end));
1493
1494    // Field access: .ident (but NOT followed by '(', '::', or struct literal pattern)
1495    // The qualified_struct_lit_suffix is tried first and uses lookahead, so field_suffix
1496    // only matches when we're certain it's field access, not a qualified struct literal.
1497    let field_suffix = just(TokenKind::Dot)
1498        .ignore_then(ident_parser())
1499        .then_ignore(none_of([TokenKind::LParen, TokenKind::ColonColon]).rewind())
1500        .map(Suffix::Field);
1501
1502    let index_suffix = expr
1503        .delimited_by(just(TokenKind::LBracket), just(TokenKind::RBracket))
1504        .map_with(|index, e| Suffix::Index(index, offset_to_u32(e.span().end)));
1505
1506    // Order matters: more specific patterns must come before less specific ones
1507    // - qualified_assoc_fn_suffix before qualified_path_suffix (both have ::)
1508    // - method_call_suffix before field_suffix (both start with .ident)
1509    // - qualified_struct_lit_suffix before field_suffix (uses lookahead for { ident: } pattern)
1510    // - qualified_path_suffix before field_suffix (both start with .ident)
1511    //
1512    // NOTE: .boxed() is required here to shorten the monomorphized type name.
1513    // Without it, macOS's ld64 linker fails with "symbol name too long" because
1514    // the 6-way choice creates extremely long generic type names.
1515    primary
1516        .foldl(
1517            choice((
1518                method_call_suffix.boxed(),
1519                qualified_assoc_fn_suffix.boxed(),
1520                qualified_enum_struct_lit_suffix.boxed(),
1521                qualified_struct_lit_suffix,
1522                qualified_path_suffix.boxed(),
1523                field_suffix.boxed(),
1524                index_suffix.boxed(),
1525            ))
1526            .boxed()
1527            .repeated(),
1528            |base, suffix| match suffix {
1529                Suffix::Field(field) => {
1530                    // Extend the base span to include the field, preserving file_id
1531                    let span = base.span().extend_to(field.span.end);
1532                    Expr::Field(FieldExpr {
1533                        base: Box::new(base),
1534                        field,
1535                        span,
1536                    })
1537                }
1538                Suffix::MethodCall(method, args, end) => {
1539                    // Extend the base span to the end of the call, preserving file_id
1540                    let span = base.span().extend_to(end);
1541                    Expr::MethodCall(MethodCallExpr {
1542                        receiver: Box::new(base),
1543                        method,
1544                        args,
1545                        span,
1546                    })
1547                }
1548                Suffix::Index(index, end) => {
1549                    // Extend the base span to the end of the index, preserving file_id
1550                    let span = base.span().extend_to(end);
1551                    Expr::Index(IndexExpr {
1552                        base: Box::new(base),
1553                        index: Box::new(index),
1554                        span,
1555                    })
1556                }
1557                Suffix::QualifiedStructLit(name, fields, end) => {
1558                    // module.StructName { ... } → StructLitExpr with base
1559                    let span = base.span().extend_to(end);
1560                    Expr::StructLit(StructLitExpr {
1561                        base: Some(Box::new(base)),
1562                        name,
1563                        fields,
1564                        span,
1565                    })
1566                }
1567                Suffix::QualifiedPath(type_name, variant, end) => {
1568                    // module.EnumName::Variant → PathExpr with base
1569                    let span = base.span().extend_to(end);
1570                    Expr::Path(PathExpr {
1571                        base: Some(Box::new(base)),
1572                        type_name,
1573                        variant,
1574                        span,
1575                    })
1576                }
1577                Suffix::QualifiedEnumStructLit(type_name, variant, fields, end) => {
1578                    // module.EnumName::Variant { ... } → EnumStructLitExpr with base
1579                    let span = base.span().extend_to(end);
1580                    Expr::EnumStructLit(EnumStructLitExpr {
1581                        base: Some(Box::new(base)),
1582                        type_name,
1583                        variant,
1584                        fields,
1585                        span,
1586                    })
1587                }
1588                Suffix::QualifiedAssocFnCall(type_name, function, args, end) => {
1589                    // module.TypeName::function(args) → AssocFnCallExpr with base
1590                    let span = base.span().extend_to(end);
1591                    Expr::AssocFnCall(AssocFnCallExpr {
1592                        base: Some(Box::new(base)),
1593                        type_name,
1594                        function,
1595                        args,
1596                        span,
1597                    })
1598                }
1599            },
1600        )
1601        .boxed()
1602}
1603
1604/// Atom parser - primary expressions (literals, identifiers, parens, blocks, control flow)
1605fn atom_parser<'src, I>(expr: GruelParser<'src, I, Expr>) -> GruelParser<'src, I, Expr>
1606where
1607    I: ValueInput<'src, Token = TokenKind, Span = SimpleSpan>,
1608{
1609    // Self expression (in method bodies)
1610    let self_expr = select! {
1611        TokenKind::SelfValue = e => Expr::SelfExpr(SelfExpr { span: span_from_extra(e) }),
1612    };
1613
1614    // Parenthesized expression
1615    let paren_expr = expr
1616        .clone()
1617        .delimited_by(just(TokenKind::LParen), just(TokenKind::RParen))
1618        .map_with(|inner, e| {
1619            Expr::Paren(ParenExpr {
1620                inner: Box::new(inner),
1621                span: span_from_extra(e),
1622            })
1623        });
1624
1625    // Block expression
1626    let block_expr = block_parser(expr.clone());
1627
1628    // Comptime block expression: comptime { expr }
1629    let comptime_expr = just(TokenKind::Comptime)
1630        .ignore_then(block_parser(expr.clone()))
1631        .map_with(|inner_expr, e| {
1632            Expr::Comptime(ComptimeBlockExpr {
1633                expr: Box::new(inner_expr),
1634                span: span_from_extra(e),
1635            })
1636        });
1637
1638    // Checked block expression: checked { expr }
1639    // Unchecked operations are only allowed inside checked blocks
1640    let checked_expr = just(TokenKind::Checked)
1641        .ignore_then(block_parser(expr.clone()))
1642        .map_with(|inner_expr, e| {
1643            Expr::Checked(CheckedBlockExpr {
1644                expr: Box::new(inner_expr),
1645                span: span_from_extra(e),
1646            })
1647        });
1648
1649    // Intrinsic argument: can be either a type or an expression
1650    // We parse as type only for unambiguous type syntax (primitives, (), !, [T;N])
1651    // Bare identifiers are parsed as expressions since they could be variables
1652    let unambiguous_type = {
1653        // Unit type: ()
1654        let unit_type = just(TokenKind::LParen)
1655            .then(just(TokenKind::RParen))
1656            .map_with(|_, e| IntrinsicArg::Type(TypeExpr::Unit(span_from_extra(e))));
1657
1658        // Never type: !
1659        let never_type = just(TokenKind::Bang)
1660            .map_with(|_, e| IntrinsicArg::Type(TypeExpr::Never(span_from_extra(e))));
1661
1662        // Array type: [T; N]
1663        let array_type = just(TokenKind::LBracket)
1664            .ignore_then(type_parser())
1665            .then_ignore(just(TokenKind::Semi))
1666            .then(select! {
1667                TokenKind::Int(n) => n,
1668            })
1669            .then_ignore(just(TokenKind::RBracket))
1670            .map_with(|(element, length), e| {
1671                IntrinsicArg::Type(TypeExpr::Array {
1672                    element: Box::new(element),
1673                    length,
1674                    span: span_from_extra(e),
1675                })
1676            });
1677
1678        // Primitive type keywords (these can't be variable names)
1679        let primitive_type = primitive_type_parser().map(IntrinsicArg::Type);
1680
1681        choice((
1682            unit_type.boxed(),
1683            never_type.boxed(),
1684            array_type.boxed(),
1685            primitive_type.boxed(),
1686        ))
1687        .boxed()
1688    };
1689
1690    // Try unambiguous type syntax first, then fall back to expression.
1691    // Boxed so the SeparatedBy type is short when used in intrinsic_call args.
1692    let intrinsic_arg: GruelParser<I, IntrinsicArg> = choice((
1693        unambiguous_type,
1694        expr.clone().map(IntrinsicArg::Expr).boxed(),
1695    ))
1696    .boxed();
1697
1698    // Shared intrinsic args parser (boxed to keep downstream types short).
1699    let intrinsic_args: GruelParser<I, Vec<IntrinsicArg>> = intrinsic_arg
1700        .separated_by(just(TokenKind::Comma))
1701        .allow_trailing()
1702        .collect::<Vec<_>>()
1703        .delimited_by(just(TokenKind::LParen), just(TokenKind::RParen))
1704        .boxed();
1705
1706    // Intrinsic call: @name(args)
1707    let intrinsic_call: GruelParser<I, Expr> = just(TokenKind::At)
1708        .ignore_then(ident_parser())
1709        .then(intrinsic_args.clone())
1710        .map_with(|(name, args), e| {
1711            Expr::IntrinsicCall(IntrinsicCallExpr {
1712                name,
1713                args,
1714                span: span_from_extra(e),
1715            })
1716        })
1717        .boxed();
1718
1719    // @import(args) - lexer tokenizes @import as a single token with interned "import" Spur
1720    let import_call: GruelParser<I, Expr> = select! {
1721        TokenKind::AtImport(import_spur) = e => (import_spur, span_from_extra(e)),
1722    }
1723    .then(intrinsic_args)
1724    .map_with(|((import_spur, import_span), args), e| {
1725        Expr::IntrinsicCall(IntrinsicCallExpr {
1726            name: Ident {
1727                name: import_spur,
1728                span: import_span,
1729            },
1730            args,
1731            span: span_from_extra(e),
1732        })
1733    })
1734    .boxed();
1735
1736    // Combined intrinsic parser: try @import first, then general @name pattern
1737    let any_intrinsic_call: GruelParser<I, Expr> = choice((import_call, intrinsic_call)).boxed();
1738
1739    // Array literal: [expr, expr, ...]
1740    let array_lit = args_parser(expr.clone())
1741        .delimited_by(just(TokenKind::LBracket), just(TokenKind::RBracket))
1742        .map_with(|elements, e| {
1743            Expr::ArrayLit(ArrayLitExpr {
1744                elements,
1745                span: span_from_extra(e),
1746            })
1747        });
1748
1749    // Type literal expression: i32, bool, etc. used as values
1750    // This enables generic function calls like identity(i32, 42)
1751    let type_lit_expr = primitive_type_parser().map_with(|type_expr, e| {
1752        Expr::TypeLit(TypeLitExpr {
1753            type_expr,
1754            span: span_from_extra(e),
1755        })
1756    });
1757
1758    // Anonymous struct type as expression: struct { field: Type, ... fn method(...) { ... } ... }
1759    // This enables comptime type construction like:
1760    //   fn Pair(comptime T: type) -> type { struct { first: T, second: T } }
1761    // With methods (Zig-style):
1762    //   fn Vec(comptime T: type) -> type { struct { ptr: u64, fn push(self, item: T) { ... } } }
1763    let anon_struct_field: GruelParser<'src, I, AnonStructField> = ident_parser()
1764        .then_ignore(just(TokenKind::Colon))
1765        .then(type_parser())
1766        .map_with(|(name, field_ty), e| AnonStructField {
1767            name,
1768            ty: field_ty,
1769            span: span_from_extra(e),
1770        })
1771        .boxed();
1772
1773    let anon_struct_fields: GruelParser<'src, I, Vec<AnonStructField>> = anon_struct_field
1774        .separated_by(just(TokenKind::Comma))
1775        .allow_trailing()
1776        .collect::<Vec<_>>()
1777        .boxed();
1778
1779    // Parse method for anonymous struct using inline method parsing
1780    // Methods inside anonymous structs follow the same syntax as impl block methods
1781    let anon_struct_method = anon_struct_method_parser(expr.clone());
1782
1783    let anon_struct_header: GruelParser<'src, I, (Vec<AnonStructField>, Vec<Method>)> =
1784        just(TokenKind::Struct)
1785            .ignore_then(just(TokenKind::LBrace))
1786            .ignore_then(anon_struct_fields)
1787            .then(
1788                // Then parse methods (not comma-separated, each ends with })
1789                anon_struct_method.repeated().collect::<Vec<_>>(),
1790            )
1791            .boxed();
1792
1793    let anon_struct_type_expr = anon_struct_header
1794        .then_ignore(just(TokenKind::RBrace))
1795        .map_with(|(fields, methods), e| {
1796            let span = span_from_extra(e);
1797            Expr::TypeLit(TypeLitExpr {
1798                type_expr: TypeExpr::AnonymousStruct {
1799                    fields,
1800                    methods,
1801                    span,
1802                },
1803                span,
1804            })
1805        });
1806
1807    // Anonymous enum type as expression: enum { Variant, Variant(T), ... fn method(...) { ... } ... }
1808    // This enables comptime type construction like:
1809    //   fn Option(comptime T: type) -> type { enum { Some(T), None } }
1810    let anon_enum_method = anon_struct_method_parser(expr.clone());
1811
1812    let anon_enum_type_expr = just(TokenKind::Enum)
1813        .ignore_then(just(TokenKind::LBrace))
1814        .ignore_then(enum_variants_parser())
1815        .then(
1816            // Then parse methods (not comma-separated, each ends with })
1817            anon_enum_method.repeated().collect::<Vec<_>>(),
1818        )
1819        .then_ignore(just(TokenKind::RBrace))
1820        .map_with(|(variants, methods), e| {
1821            let span = span_from_extra(e);
1822            Expr::TypeLit(TypeLitExpr {
1823                type_expr: TypeExpr::AnonymousEnum {
1824                    variants,
1825                    methods,
1826                    span,
1827                },
1828                span,
1829            })
1830        });
1831
1832    // Self type expression: Self { field: value } (struct literal with Self as type)
1833    // This enables constructing instances of anonymous struct types from methods
1834    let self_type_expr = just(TokenKind::SelfType)
1835        .ignore_then(
1836            field_inits_parser(expr.clone())
1837                .delimited_by(just(TokenKind::LBrace), just(TokenKind::RBrace)),
1838        )
1839        .map_with(|fields, e| {
1840            let span = span_from_extra(e);
1841            Expr::StructLit(StructLitExpr {
1842                base: None,
1843                name: Ident {
1844                    name: e.state().syms.self_type,
1845                    span,
1846                },
1847                fields,
1848                span,
1849            })
1850        });
1851
1852    // Self::Variant(args) — associated function call on Self (for anonymous enum variant construction)
1853    let self_assoc_fn_call = just(TokenKind::SelfType)
1854        .ignore_then(just(TokenKind::ColonColon))
1855        .ignore_then(ident_parser())
1856        .then(
1857            call_args_parser(expr.clone())
1858                .delimited_by(just(TokenKind::LParen), just(TokenKind::RParen)),
1859        )
1860        .map_with(|(function, args), e| {
1861            let span = span_from_extra(e);
1862            Expr::AssocFnCall(AssocFnCallExpr {
1863                base: None,
1864                type_name: Ident {
1865                    name: e.state().syms.self_type,
1866                    span,
1867                },
1868                function,
1869                args,
1870                span,
1871            })
1872        });
1873
1874    // Self::Variant { field: value, ... } — struct variant construction on Self
1875    let self_enum_struct_lit = just(TokenKind::SelfType)
1876        .ignore_then(just(TokenKind::ColonColon))
1877        .ignore_then(ident_parser())
1878        .then(
1879            field_inits_parser(expr.clone())
1880                .delimited_by(just(TokenKind::LBrace), just(TokenKind::RBrace)),
1881        )
1882        .map_with(|(variant, fields), e| {
1883            let span = span_from_extra(e);
1884            Expr::EnumStructLit(EnumStructLitExpr {
1885                base: None,
1886                type_name: Ident {
1887                    name: e.state().syms.self_type,
1888                    span,
1889                },
1890                variant,
1891                fields,
1892                span,
1893            })
1894        });
1895
1896    // Self::Variant — unit variant on Self (for anonymous enum variant construction)
1897    let self_enum_variant = just(TokenKind::SelfType)
1898        .ignore_then(just(TokenKind::ColonColon))
1899        .ignore_then(ident_parser())
1900        .then_ignore(none_of([TokenKind::LParen, TokenKind::LBrace]).rewind())
1901        .map_with(|variant, e| {
1902            let span = span_from_extra(e);
1903            Expr::Path(PathExpr {
1904                base: None,
1905                type_name: Ident {
1906                    name: e.state().syms.self_type,
1907                    span,
1908                },
1909                variant,
1910                span,
1911            })
1912        });
1913
1914    // Primary expression (before field access and indexing)
1915    // Note: literal_parser() includes unit_lit which must come before paren_expr
1916    // so () is parsed as unit, not empty parens
1917    // Note: self_expr must come before call_and_access_parser since self is a keyword
1918    // Note: self_type_expr must come before call_and_access_parser since Self is a keyword
1919    // Note: comptime_expr and checked_expr must come before block_expr since they start with keywords
1920    // Note: type_lit_expr must come before call_and_access_parser since type names are keywords
1921    // Note: anon_struct_type_expr must come before call_and_access_parser since struct is a keyword
1922    //
1923    // NOTE: Split into sub-groups to keep Choice<tuple> symbol length < 4K.
1924    // 13 elements of Boxed<I,Expr,E> in one tuple would produce ~7K symbols on macOS.
1925    let primary_a: GruelParser<'src, I, Expr> = choice((
1926        literal_parser(),
1927        control_flow_parser(expr.clone()),
1928        self_expr.boxed(),
1929        self_assoc_fn_call.boxed(),
1930        self_enum_struct_lit.boxed(),
1931        self_enum_variant.boxed(),
1932        self_type_expr.boxed(),
1933        any_intrinsic_call.boxed(),
1934    ))
1935    .boxed();
1936    let primary_b: GruelParser<'src, I, Expr> = choice((
1937        array_lit.boxed(),
1938        anon_struct_type_expr.boxed(),
1939        anon_enum_type_expr.boxed(),
1940        type_lit_expr.boxed(),
1941        call_and_access_parser(expr.clone()),
1942        paren_expr.boxed(),
1943    ))
1944    .boxed();
1945    let primary_c: GruelParser<'src, I, Expr> = choice((
1946        comptime_expr.boxed(),
1947        checked_expr.boxed(),
1948        block_expr.boxed(),
1949    ))
1950    .boxed();
1951    let primary: GruelParser<'src, I, Expr> = choice((primary_a, primary_b, primary_c)).boxed();
1952
1953    // Wrap primary expressions with field access, method call, and indexing suffixes
1954    with_suffix_parser(primary, expr)
1955}
1956
1957/// A block item is either a statement or an expression (potentially the final one)
1958#[derive(Debug, Clone)]
1959enum BlockItem {
1960    Statement(Statement),
1961    Expr(Expr),
1962}
1963
1964/// What token follows an expression in a block (used to determine its role)
1965#[derive(Debug, Clone, Copy)]
1966enum ExprFollower {
1967    /// Semicolon follows - expression is a statement
1968    Semi,
1969    /// Right brace follows (not consumed) - expression is the final/return value
1970    RBrace,
1971    /// Some other token follows - only valid for control flow expressions
1972    Other,
1973    /// End of input - only valid for control flow expressions
1974    End,
1975}
1976
1977/// Parser for a let binding pattern: identifier, wildcard, or struct destructure.
1978fn let_pattern_parser<'src, I>() -> GruelParser<'src, I, LetPattern>
1979where
1980    I: ValueInput<'src, Token = TokenKind, Span = SimpleSpan>,
1981{
1982    let wildcard =
1983        just(TokenKind::Underscore).map_with(|_, e| LetPattern::Wildcard(span_from_extra(e)));
1984
1985    // A single destructure field: [mut] field_name [: binding]
1986    let destructure_field = just(TokenKind::Mut)
1987        .or_not()
1988        .then(ident_parser())
1989        .then(
1990            just(TokenKind::Colon)
1991                .ignore_then(choice((
1992                    just(TokenKind::Underscore)
1993                        .map_with(|_, e| DestructureBinding::Wildcard(span_from_extra(e))),
1994                    ident_parser().map(DestructureBinding::Renamed),
1995                )))
1996                .or_not(),
1997        )
1998        .map(|((is_mut, field_name), binding)| DestructureField {
1999            field_name,
2000            binding: binding.unwrap_or(DestructureBinding::Shorthand),
2001            is_mut: is_mut.is_some(),
2002        });
2003
2004    // Struct destructure: TypeName { field1, field2, ... }
2005    let struct_destructure = ident_parser()
2006        .then(
2007            destructure_field
2008                .separated_by(just(TokenKind::Comma))
2009                .allow_trailing()
2010                .collect::<Vec<_>>()
2011                .delimited_by(just(TokenKind::LBrace), just(TokenKind::RBrace)),
2012        )
2013        .map_with(|(type_name, fields), e| LetPattern::Struct {
2014            type_name,
2015            fields,
2016            span: span_from_extra(e),
2017        });
2018
2019    let ident = ident_parser().map(LetPattern::Ident);
2020
2021    // Try struct destructure first (ident followed by {), then plain ident, then wildcard
2022    struct_destructure.or(ident).or(wildcard).boxed()
2023}
2024
2025/// Parser for let statements: [@directive]* let [mut] pattern [: type] = expr;
2026fn let_statement_parser<'src, I>(
2027    expr: GruelParser<'src, I, Expr>,
2028) -> GruelParser<'src, I, Statement>
2029where
2030    I: ValueInput<'src, Token = TokenKind, Span = SimpleSpan>,
2031{
2032    // Box after 2 thens to keep accumulated type short.
2033    let let_head: GruelParser<I, (Directives, bool, LetPattern)> = directives_parser()
2034        .then(just(TokenKind::Let).ignore_then(just(TokenKind::Mut).or_not().map(|m| m.is_some())))
2035        .then(let_pattern_parser())
2036        .map(|((d, m), p)| (d, m, p))
2037        .boxed();
2038
2039    let let_tail: GruelParser<I, (Option<TypeExpr>, Expr)> = just(TokenKind::Colon)
2040        .ignore_then(type_parser())
2041        .or_not()
2042        .then(just(TokenKind::Eq).ignore_then(expr))
2043        .then_ignore(just(TokenKind::Semi))
2044        .boxed();
2045
2046    let_head
2047        .then(let_tail)
2048        .map_with(|((directives, is_mut, pattern), (ty, init)), e| {
2049            Statement::Let(LetStatement {
2050                directives,
2051                is_mut,
2052                pattern,
2053                ty,
2054                init: Box::new(init),
2055                span: span_from_extra(e),
2056            })
2057        })
2058        .boxed()
2059}
2060
2061/// Suffix for assignment targets: either .field or [index]
2062#[derive(Clone)]
2063enum AssignSuffix {
2064    Field(Ident),
2065    Index(Expr),
2066}
2067
2068/// Parser for assignment target: variable, field access, or index access
2069/// Parses: name or name.field or name[idx] or name.field[idx].field...
2070fn assign_target_parser<'src, I>(
2071    expr: GruelParser<'src, I, Expr>,
2072) -> GruelParser<'src, I, AssignTarget>
2073where
2074    I: ValueInput<'src, Token = TokenKind, Span = SimpleSpan>,
2075{
2076    let field_suffix = just(TokenKind::Dot)
2077        .ignore_then(ident_parser())
2078        .map(AssignSuffix::Field);
2079
2080    let index_suffix = expr
2081        .delimited_by(just(TokenKind::LBracket), just(TokenKind::RBracket))
2082        .map(AssignSuffix::Index);
2083
2084    ident_parser()
2085        .then(
2086            choice((field_suffix.boxed(), index_suffix.boxed()))
2087                .repeated()
2088                .collect::<Vec<_>>(),
2089        )
2090        .map(|(base_ident, suffixes)| {
2091            if suffixes.is_empty() {
2092                // Simple variable: x
2093                AssignTarget::Var(base_ident)
2094            } else {
2095                // Chain of field/index accesses: x.a[0].b...
2096                // Build up the expression from left to right, consuming suffixes by value
2097                let mut base_expr = Expr::Ident(base_ident);
2098                let mut suffixes = suffixes.into_iter().peekable();
2099                while let Some(suffix) = suffixes.next() {
2100                    let is_last = suffixes.peek().is_none();
2101                    if is_last {
2102                        // The last suffix determines the target type
2103                        return match suffix {
2104                            AssignSuffix::Field(field) => {
2105                                let span = Span::new(base_expr.span().start, field.span.end);
2106                                AssignTarget::Field(FieldExpr {
2107                                    base: Box::new(base_expr),
2108                                    field,
2109                                    span,
2110                                })
2111                            }
2112                            AssignSuffix::Index(index) => {
2113                                let span = Span::new(base_expr.span().start, index.span().end);
2114                                AssignTarget::Index(IndexExpr {
2115                                    base: Box::new(base_expr),
2116                                    index: Box::new(index),
2117                                    span,
2118                                })
2119                            }
2120                        };
2121                    }
2122                    // Build intermediate expressions
2123                    match suffix {
2124                        AssignSuffix::Field(field) => {
2125                            let span = Span::new(base_expr.span().start, field.span.end);
2126                            base_expr = Expr::Field(FieldExpr {
2127                                base: Box::new(base_expr),
2128                                field,
2129                                span,
2130                            });
2131                        }
2132                        AssignSuffix::Index(index) => {
2133                            let span = Span::new(base_expr.span().start, index.span().end);
2134                            base_expr = Expr::Index(IndexExpr {
2135                                base: Box::new(base_expr),
2136                                index: Box::new(index),
2137                                span,
2138                            });
2139                        }
2140                    }
2141                }
2142                // This is unreachable since we already checked suffixes.is_empty()
2143                unreachable!()
2144            }
2145        })
2146        .boxed()
2147}
2148
2149/// Parser for assignment statements: target = expr;
2150/// Supports variable (x = 5), field (point.x = 5), and index (arr[0] = 5) assignment
2151fn assign_statement_parser<'src, I>(
2152    expr: GruelParser<'src, I, Expr>,
2153) -> GruelParser<'src, I, Statement>
2154where
2155    I: ValueInput<'src, Token = TokenKind, Span = SimpleSpan>,
2156{
2157    assign_target_parser(expr.clone())
2158        .then_ignore(just(TokenKind::Eq))
2159        .then(expr)
2160        .then_ignore(just(TokenKind::Semi))
2161        .map_with(|(target, value), e| {
2162            Statement::Assign(AssignStatement {
2163                target,
2164                value: Box::new(value),
2165                span: span_from_extra(e),
2166            })
2167        })
2168        .boxed()
2169}
2170
2171/// Returns true if the expression is a control flow construct or block that can appear
2172/// as a statement without a trailing semicolon.
2173///
2174/// This includes:
2175/// - Control flow: if, while, match, loop, break, continue, return
2176/// - Block expressions: { ... }
2177///
2178/// Block expressions are included because they are syntactically similar to control flow:
2179/// they are compound statements that naturally terminate without a semicolon.
2180fn is_control_flow_expr(e: &Expr) -> bool {
2181    matches!(
2182        e,
2183        Expr::If(_)
2184            | Expr::Match(_)
2185            | Expr::While(_)
2186            | Expr::For(_)
2187            | Expr::ComptimeUnrollFor(_)
2188            | Expr::Loop(_)
2189            | Expr::Break(_)
2190            | Expr::Continue(_)
2191            | Expr::Return(_)
2192            | Expr::Block(_)
2193    )
2194}
2195
2196/// Returns true if the expression diverges (has the Never type).
2197/// These expressions can be promoted to the final expression of a block
2198/// since Never coerces to any type.
2199fn is_diverging_expr(e: &Expr) -> bool {
2200    matches!(
2201        e,
2202        Expr::Break(_) | Expr::Continue(_) | Expr::Return(_) | Expr::Loop(_)
2203    )
2204}
2205
2206/// Parses a single item within a block.
2207///
2208/// # Block Item Grammar
2209///
2210/// A block contains zero or more items. Each item is one of:
2211/// - **Let statement**: `let x = expr;` (always requires semicolon)
2212/// - **Assignment statement**: `target = expr;` (always requires semicolon)
2213/// - **Expression statement**: `expr;` (requires semicolon for most expressions)
2214/// - **Control flow statement**: `if/while/match/loop/break/continue/return ...`
2215///   (no semicolon needed when mid-block)
2216/// - **Final expression**: `expr` at the very end of a block (no semicolon, becomes
2217///   the block's return value)
2218///
2219/// # Parsing Strategy: Lookahead with `rewind()`
2220///
2221/// The challenge is distinguishing between:
2222/// 1. `{ foo; bar }` - `foo;` is a statement, `bar` is the final expression
2223/// 2. `{ if c { 1 } else { 2 } x }` - the `if` is a statement, `x` is final
2224/// 3. `{ if c { 1 } else { 2 } }` - the `if` IS the final expression
2225///
2226/// We use `rewind()` as a non-consuming lookahead to peek at what follows:
2227///
2228/// - `none_of([RBrace, Semi]).rewind()`: Succeeds if the NEXT token is neither
2229///   `}` nor `;`. The `.rewind()` means we check without consuming the token.
2230///   This identifies control flow in the middle of a block.
2231///
2232/// - `just(RBrace).rewind()`: Succeeds if the NEXT token is `}`. This identifies
2233///   the final expression of a block.
2234///
2235/// # Why `try_map()` for Control Flow?
2236///
2237/// After parsing an expression followed by a non-`}` non-`;` token, we need to
2238/// validate it's actually a control flow expression. If it's something like `x`
2239/// followed by `y`, that's a syntax error (missing semicolon). We use `try_map()`
2240/// to:
2241/// 1. Accept the parse if it's a control flow expression (valid without semicolon)
2242/// 2. Reject it otherwise, allowing chumsky to backtrack and try other branches
2243///
2244/// # Parse Order Matters
2245///
2246/// The `choice()` tries parsers in order. We must try:
2247/// 1. `let_stmt` first (starts with `let` keyword)
2248/// 2. `assign_stmt` second (identifier followed by `.`/`[` chain then `=`)
2249/// 3. `expr_with_semi` (any expression followed by `;`)
2250/// 4. `control_flow_stmt` (control flow NOT followed by `}` - mid-block)
2251/// 5. `final_expr` (any expression followed by `}` - end of block)
2252///
2253/// The assignment parser is tried before general expressions because `x = 5;`
2254/// could otherwise be misparsed as expression `x` followed by unexpected `=`.
2255fn block_item_parser<'src, I>(expr: GruelParser<'src, I, Expr>) -> GruelParser<'src, I, BlockItem>
2256where
2257    I: ValueInput<'src, Token = TokenKind, Span = SimpleSpan>,
2258{
2259    // Let statement: `let x: T = expr;`
2260    // Always requires a trailing semicolon.
2261    let let_stmt = let_statement_parser(expr.clone()).map(BlockItem::Statement);
2262
2263    // Assignment statement: `target = expr;`
2264    // Target can be: variable (`x`), field (`a.b.c`), or index (`a[i]`).
2265    // Always requires a trailing semicolon.
2266    let assign_stmt = assign_statement_parser(expr.clone()).map(BlockItem::Statement);
2267
2268    // Expression-based item: parse expression ONCE, then decide based on what follows.
2269    // This avoids O(2^n) complexity from repeatedly re-parsing expressions with backtracking.
2270    //
2271    // After parsing an expression, we check the next token:
2272    // - `;` → expression statement (value discarded)
2273    // - `}` → final expression (block's return value)
2274    // - other → mid-block control flow (only valid for if/while/match/loop/break/continue/return)
2275    let expr_item = expr
2276        .then(
2277            choice((
2278                just(TokenKind::Semi).to(ExprFollower::Semi).boxed(),
2279                just(TokenKind::RBrace)
2280                    .rewind()
2281                    .to(ExprFollower::RBrace)
2282                    .boxed(),
2283                any().rewind().to(ExprFollower::Other).boxed(),
2284            ))
2285            .or(end().to(ExprFollower::End)),
2286        )
2287        .try_map(|(e, follower), span| match follower {
2288            ExprFollower::Semi => {
2289                // Expression followed by semicolon: `expr;`
2290                Ok(BlockItem::Statement(Statement::Expr(e)))
2291            }
2292            ExprFollower::RBrace => {
2293                // Final expression: `{ ... expr }`
2294                Ok(BlockItem::Expr(e))
2295            }
2296            ExprFollower::Other | ExprFollower::End => {
2297                // Mid-block control flow (no semicolon, not at end)
2298                // Only control flow expressions are valid here
2299                if is_control_flow_expr(&e) {
2300                    Ok(BlockItem::Statement(Statement::Expr(e)))
2301                } else {
2302                    Err(Rich::custom(span, "expected semicolon after expression"))
2303                }
2304            }
2305        });
2306
2307    // Try parsers in order. Earlier parsers take precedence.
2308    // This order ensures:
2309    // - Keywords (`let`) are matched before being parsed as identifiers
2310    // - Assignments (`x = 5;`) are matched before `x` is parsed as an expression
2311    choice((let_stmt.boxed(), assign_stmt.boxed(), expr_item.boxed())).boxed()
2312}
2313
2314/// Process block items into statements and final expression
2315fn process_block_items(items: Vec<BlockItem>, block_span: Span) -> (Vec<Statement>, Expr) {
2316    let mut statements = Vec::new();
2317    let mut final_expr = None;
2318
2319    for item in items {
2320        match item {
2321            BlockItem::Statement(stmt) => {
2322                // Had a non-semicolon expr before, but now we have more items
2323                // This shouldn't happen with correct grammar, but handle gracefully
2324                if let Some(e) = final_expr.take() {
2325                    statements.push(Statement::Expr(e));
2326                }
2327                statements.push(stmt);
2328            }
2329            BlockItem::Expr(e) => {
2330                if let Some(prev) = final_expr.take() {
2331                    // Had a non-semicolon expr before this one - that's invalid
2332                    // but we'll treat the previous as a statement for error recovery
2333                    statements.push(Statement::Expr(prev));
2334                }
2335                final_expr = Some(e);
2336            }
2337        }
2338    }
2339
2340    let expr = final_expr.unwrap_or_else(|| {
2341        // No explicit final expression. Check if the last statement is a diverging
2342        // expression (break, continue, return) - if so, promote it to the final
2343        // expression since it has type Never which coerces to any type.
2344        if let Some(Statement::Expr(e)) = statements.last()
2345            && is_diverging_expr(e)
2346        {
2347            // Safe to unwrap: we just checked last() is Some(Statement::Expr(_))
2348            let Statement::Expr(e) = statements.pop().unwrap() else {
2349                unreachable!()
2350            };
2351            return e;
2352        }
2353        // Fallback: use a unit expression (block produces unit type)
2354        Expr::Unit(UnitLit {
2355            span: Span::new(block_span.end, block_span.end),
2356        })
2357    });
2358
2359    (statements, expr)
2360}
2361
2362/// Parser for blocks that may end without a final expression (for if/while bodies)
2363fn maybe_unit_block_parser<'src, I>(
2364    expr: GruelParser<'src, I, Expr>,
2365) -> GruelParser<'src, I, BlockExpr>
2366where
2367    I: ValueInput<'src, Token = TokenKind, Span = SimpleSpan>,
2368{
2369    block_item_parser(expr)
2370        .repeated()
2371        .collect::<Vec<_>>()
2372        .delimited_by(just(TokenKind::LBrace), just(TokenKind::RBrace))
2373        .map_with(|items, e| {
2374            let span = span_from_extra(e);
2375            let (statements, final_expr) = process_block_items(items, span);
2376            BlockExpr {
2377                statements,
2378                expr: Box::new(final_expr),
2379                span,
2380            }
2381        })
2382        .boxed()
2383}
2384
2385/// Parser for blocks that require a final expression: { statements... expr }
2386fn block_parser<'src, I>(expr: GruelParser<'src, I, Expr>) -> GruelParser<'src, I, Expr>
2387where
2388    I: ValueInput<'src, Token = TokenKind, Span = SimpleSpan>,
2389{
2390    block_item_parser(expr)
2391        .repeated()
2392        .collect::<Vec<_>>()
2393        .delimited_by(just(TokenKind::LBrace), just(TokenKind::RBrace))
2394        .map_with(|items, e| {
2395            let span = span_from_extra(e);
2396            let (statements, final_expr) = process_block_items(items, span);
2397            Expr::Block(BlockExpr {
2398                statements,
2399                expr: Box::new(final_expr),
2400                span,
2401            })
2402        })
2403        .boxed()
2404}
2405
2406/// Parser for function definitions: [@directive]* [pub] [unchecked] fn name(params) -> Type { body }
2407fn function_parser<'src, I>() -> GruelParser<'src, I, Function>
2408where
2409    I: ValueInput<'src, Token = TokenKind, Span = SimpleSpan>,
2410{
2411    let expr = expr_parser();
2412
2413    // Parse optional visibility (pub keyword)
2414    let visibility = just(TokenKind::Pub).or_not().map(|opt| {
2415        if opt.is_some() {
2416            Visibility::Public
2417        } else {
2418            Visibility::Private
2419        }
2420    });
2421
2422    // Box after 2 thens to keep the accumulated type short.
2423    let fn_head: GruelParser<I, (Directives, Visibility, bool)> = directives_parser()
2424        .then(visibility)
2425        .then(just(TokenKind::Unchecked).or_not())
2426        .map(|((d, v), u)| (d, v, u.is_some()))
2427        .boxed();
2428
2429    let fn_sig: GruelParser<I, (Ident, Vec<Param>)> = just(TokenKind::Fn)
2430        .ignore_then(ident_parser())
2431        .then(params_parser().delimited_by(just(TokenKind::LParen), just(TokenKind::RParen)))
2432        .boxed();
2433
2434    fn_head
2435        .then(fn_sig)
2436        .then(just(TokenKind::Arrow).ignore_then(type_parser()).or_not())
2437        .then(block_parser(expr))
2438        .map_with(
2439            |((((directives, visibility, is_unchecked), (name, params)), return_type), body), e| {
2440                Function {
2441                    directives,
2442                    visibility,
2443                    is_unchecked,
2444                    name,
2445                    params,
2446                    return_type,
2447                    body,
2448                    span: span_from_extra(e),
2449                }
2450            },
2451        )
2452        .boxed()
2453}
2454
2455/// Parser for struct definitions with inline methods:
2456/// [@directive]* [pub] [linear] struct Name { field: Type, ... fn method(self) { ... } }
2457///
2458/// Fields come first (comma-separated), then methods (no separators needed).
2459fn struct_parser<'src, I>() -> GruelParser<'src, I, StructDecl>
2460where
2461    I: ValueInput<'src, Token = TokenKind, Span = SimpleSpan>,
2462{
2463    // Parse optional visibility (pub keyword)
2464    let visibility = just(TokenKind::Pub).or_not().map(|opt| {
2465        if opt.is_some() {
2466            Visibility::Public
2467        } else {
2468            Visibility::Private
2469        }
2470    });
2471
2472    // Box the struct header after 3 thens to keep the accumulated type short.
2473    let struct_head: GruelParser<I, (Directives, Visibility, bool, Ident)> = directives_parser()
2474        .then(visibility)
2475        .then(just(TokenKind::Linear).or_not())
2476        .then(just(TokenKind::Struct).ignore_then(ident_parser()))
2477        .map(|(((d, v), l), name)| (d, v, l.is_some(), name))
2478        .boxed();
2479
2480    // Box the struct body so DelimitedBy wraps a short Boxed type.
2481    let struct_body: GruelParser<I, (Vec<FieldDecl>, Vec<Method>)> = field_decls_parser()
2482        .then(method_parser().repeated().collect::<Vec<_>>())
2483        .boxed();
2484
2485    struct_head
2486        .then(struct_body.delimited_by(just(TokenKind::LBrace), just(TokenKind::RBrace)))
2487        .map_with(
2488            |((directives, visibility, is_linear, name), (fields, methods)), e| StructDecl {
2489                directives,
2490                visibility,
2491                is_linear,
2492                name,
2493                fields,
2494                methods,
2495                span: span_from_extra(e),
2496            },
2497        )
2498        .boxed()
2499}
2500
2501/// Parser for enum variant: unit, tuple `(Type, ...)`, or struct `{ field: Type, ... }`
2502fn enum_variant_parser<'src, I>() -> GruelParser<'src, I, EnumVariant>
2503where
2504    I: ValueInput<'src, Token = TokenKind, Span = SimpleSpan>,
2505{
2506    use crate::ast::{EnumVariantField, EnumVariantKind};
2507
2508    // Tuple-style fields: (Type, Type, ...)
2509    let tuple_fields = type_parser()
2510        .separated_by(just(TokenKind::Comma))
2511        .allow_trailing()
2512        .collect::<Vec<_>>()
2513        .delimited_by(just(TokenKind::LParen), just(TokenKind::RParen));
2514
2515    // Struct-style fields: { name: Type, name: Type, ... }
2516    let struct_field = ident_parser()
2517        .then_ignore(just(TokenKind::Colon))
2518        .then(type_parser())
2519        .map_with(|(name, ty), e| EnumVariantField {
2520            name,
2521            ty,
2522            span: span_from_extra(e),
2523        });
2524    let struct_fields = struct_field
2525        .separated_by(just(TokenKind::Comma))
2526        .allow_trailing()
2527        .collect::<Vec<_>>()
2528        .delimited_by(just(TokenKind::LBrace), just(TokenKind::RBrace));
2529
2530    // Combine: name + optional (tuple | struct) fields
2531    let variant_kind = choice((
2532        tuple_fields.map(EnumVariantKind::Tuple),
2533        struct_fields.map(EnumVariantKind::Struct),
2534    ))
2535    .or_not()
2536    .map(|opt| opt.unwrap_or(EnumVariantKind::Unit));
2537
2538    ident_parser()
2539        .then(variant_kind)
2540        .map_with(|(name, kind), e| EnumVariant {
2541            name,
2542            kind,
2543            span: span_from_extra(e),
2544        })
2545        .boxed()
2546}
2547
2548/// Parser for comma-separated enum variants
2549fn enum_variants_parser<'src, I>() -> GruelParser<'src, I, Vec<EnumVariant>>
2550where
2551    I: ValueInput<'src, Token = TokenKind, Span = SimpleSpan>,
2552{
2553    enum_variant_parser()
2554        .separated_by(just(TokenKind::Comma))
2555        .allow_trailing()
2556        .collect::<Vec<_>>()
2557        .boxed()
2558}
2559
2560/// Parser for enum definitions: [pub] enum Name { Variant1, Variant2, ... }
2561fn enum_parser<'src, I>() -> GruelParser<'src, I, EnumDecl>
2562where
2563    I: ValueInput<'src, Token = TokenKind, Span = SimpleSpan>,
2564{
2565    // Parse optional visibility (pub keyword)
2566    let visibility = just(TokenKind::Pub).or_not().map(|opt| {
2567        if opt.is_some() {
2568            Visibility::Public
2569        } else {
2570            Visibility::Private
2571        }
2572    });
2573
2574    visibility
2575        .then(just(TokenKind::Enum).ignore_then(ident_parser()))
2576        .then(enum_variants_parser().delimited_by(just(TokenKind::LBrace), just(TokenKind::RBrace)))
2577        .map_with(|((visibility, name), variants), e| EnumDecl {
2578            visibility,
2579            name,
2580            variants,
2581            span: span_from_extra(e),
2582        })
2583        .boxed()
2584}
2585
2586/// Parser for method definitions: [@directive]* fn name(self, params) -> Type { body }
2587/// Methods differ from functions in that they can have `self` as the first parameter.
2588fn method_parser<'src, I>() -> GruelParser<'src, I, Method>
2589where
2590    I: ValueInput<'src, Token = TokenKind, Span = SimpleSpan>,
2591{
2592    let expr = expr_parser();
2593    method_parser_with_expr(expr)
2594}
2595
2596/// Parser for method definitions inside anonymous structs.
2597/// Takes an expression parser as a parameter to avoid creating a new one.
2598fn anon_struct_method_parser<'src, I>(
2599    expr: GruelParser<'src, I, Expr>,
2600) -> GruelParser<'src, I, Method>
2601where
2602    I: ValueInput<'src, Token = TokenKind, Span = SimpleSpan>,
2603{
2604    method_parser_with_expr(expr)
2605}
2606
2607/// Shared implementation for method parsing.
2608/// Takes an expression parser to allow reuse from different contexts.
2609fn method_parser_with_expr<'src, I>(
2610    expr: GruelParser<'src, I, Expr>,
2611) -> GruelParser<'src, I, Method>
2612where
2613    I: ValueInput<'src, Token = TokenKind, Span = SimpleSpan>,
2614{
2615    // Parse optional self parameter
2616    let self_param = just(TokenKind::SelfValue).map_with(|_, e| SelfParam {
2617        span: span_from_extra(e),
2618    });
2619
2620    // Parse self followed by optional regular params
2621    let self_then_params = self_param
2622        .then(
2623            just(TokenKind::Comma)
2624                .ignore_then(params_parser())
2625                .or_not()
2626                .map(|opt| opt.unwrap_or_default()),
2627        )
2628        .map(|(self_param, params)| (Some(self_param), params));
2629
2630    // Parse just regular params (no self) - this is an associated function
2631    let just_params = params_parser().map(|params| (None, params));
2632
2633    // Box params_with_optional_self so DelimitedBy wraps a short type.
2634    let params_with_optional_self: GruelParser<I, (Option<SelfParam>, Vec<Param>)> =
2635        choice((self_then_params.boxed(), just_params.boxed())).boxed();
2636
2637    // Box the method head early to keep subsequent type accumulation short.
2638    let method_head: GruelParser<I, (Directives, Ident)> = directives_parser()
2639        .then(just(TokenKind::Fn).ignore_then(ident_parser()))
2640        .boxed();
2641
2642    let method_params: GruelParser<I, (Option<SelfParam>, Vec<Param>)> = params_with_optional_self
2643        .delimited_by(just(TokenKind::LParen), just(TokenKind::RParen))
2644        .boxed();
2645
2646    let method_return: GruelParser<I, Option<TypeExpr>> = just(TokenKind::Arrow)
2647        .ignore_then(type_parser())
2648        .or_not()
2649        .boxed();
2650
2651    method_head
2652        .then(method_params)
2653        .then(method_return)
2654        .then(block_parser(expr))
2655        .map_with(
2656            |((((directives, name), (receiver, params)), return_type), body), e| Method {
2657                directives,
2658                name,
2659                receiver,
2660                params,
2661                return_type,
2662                body,
2663                span: span_from_extra(e),
2664            },
2665        )
2666        .boxed()
2667}
2668
2669/// Parser for drop fn declarations: drop fn TypeName(self) { body }
2670fn drop_fn_parser<'src, I>() -> GruelParser<'src, I, DropFn>
2671where
2672    I: ValueInput<'src, Token = TokenKind, Span = SimpleSpan>,
2673{
2674    let expr = expr_parser();
2675
2676    // Parse self parameter
2677    let self_param = just(TokenKind::SelfValue).map_with(|_, e| SelfParam {
2678        span: span_from_extra(e),
2679    });
2680
2681    // NOTE: Box after accumulating the head to keep the final MapWith type short.
2682    let drop_fn_sig: GruelParser<'src, I, (Ident, SelfParam)> = just(TokenKind::Drop)
2683        .ignore_then(just(TokenKind::Fn))
2684        .ignore_then(ident_parser())
2685        .then(self_param.delimited_by(just(TokenKind::LParen), just(TokenKind::RParen)))
2686        .boxed();
2687
2688    drop_fn_sig
2689        .then(block_parser(expr))
2690        .map_with(|((type_name, self_param), body), e| DropFn {
2691            type_name,
2692            self_param,
2693            body,
2694            span: span_from_extra(e),
2695        })
2696        .boxed()
2697}
2698
2699/// Parser for const declarations: [pub] const name [: Type] = expr;
2700///
2701/// Used for module re-exports:
2702/// ```gruel
2703/// pub const strings = @import("utils/strings.gruel");
2704/// pub const helper = @import("utils/internal.gruel").helper;
2705/// ```
2706fn const_parser<'src, I>() -> GruelParser<'src, I, ConstDecl>
2707where
2708    I: ValueInput<'src, Token = TokenKind, Span = SimpleSpan>,
2709{
2710    let expr = expr_parser();
2711
2712    // Parse optional visibility (pub keyword)
2713    let visibility = just(TokenKind::Pub).or_not().map(|opt| {
2714        if opt.is_some() {
2715            Visibility::Public
2716        } else {
2717            Visibility::Private
2718        }
2719    });
2720
2721    // Box after 2 thens to keep accumulated type short.
2722    let const_head: GruelParser<I, (Directives, Visibility, Ident)> = directives_parser()
2723        .then(visibility)
2724        .then(just(TokenKind::Const).ignore_then(ident_parser()))
2725        .map(|((d, v), n)| (d, v, n))
2726        .boxed();
2727
2728    let const_tail: GruelParser<I, (Option<TypeExpr>, Expr)> = just(TokenKind::Colon)
2729        .ignore_then(type_parser())
2730        .or_not()
2731        .then(just(TokenKind::Eq).ignore_then(expr))
2732        .then_ignore(just(TokenKind::Semi))
2733        .boxed();
2734
2735    const_head
2736        .then(const_tail)
2737        .map_with(
2738            |((directives, visibility, name), (ty, init)), e| ConstDecl {
2739                directives,
2740                visibility,
2741                name,
2742                ty,
2743                init: Box::new(init),
2744                span: span_from_extra(e),
2745            },
2746        )
2747        .boxed()
2748}
2749
2750/// Parser for top-level items (functions, structs, enums, drop fns, and consts)
2751fn item_parser<'src, I>() -> GruelParser<'src, I, Item>
2752where
2753    I: ValueInput<'src, Token = TokenKind, Span = SimpleSpan>,
2754{
2755    choice((
2756        function_parser().map(Item::Function).boxed(),
2757        struct_parser().map(Item::Struct).boxed(),
2758        enum_parser().map(Item::Enum).boxed(),
2759        drop_fn_parser().map(Item::DropFn).boxed(),
2760        const_parser().map(Item::Const).boxed(),
2761    ))
2762    .boxed()
2763}
2764
2765/// Parser that matches tokens that can start an item (for recovery).
2766/// This is a "lookahead" - it peeks but doesn't consume.
2767fn item_start<'src, I>() -> GruelParser<'src, I, ()>
2768where
2769    I: ValueInput<'src, Token = TokenKind, Span = SimpleSpan>,
2770{
2771    // NOTE: Split into sub-groups to keep Choice<tuple> symbol length < 4K.
2772    // 9 elements of Boxed<I,(),E> in one tuple wrapped in Rewind would produce ~5K symbols on macOS.
2773    let item_start_a: GruelParser<'src, I, ()> = choice((
2774        just(TokenKind::Fn).ignored().boxed(),
2775        just(TokenKind::Struct).ignored().boxed(),
2776        just(TokenKind::Enum).ignored().boxed(),
2777        just(TokenKind::Drop).ignored().boxed(),
2778        just(TokenKind::Const).ignored().boxed(),
2779    ))
2780    .boxed();
2781    let item_start_b: GruelParser<'src, I, ()> = choice((
2782        just(TokenKind::Pub).ignored().boxed(),
2783        just(TokenKind::Linear).ignored().boxed(),
2784        just(TokenKind::Unchecked).ignored().boxed(),
2785        just(TokenKind::At).ignored().boxed(), // For @directives
2786    ))
2787    .boxed();
2788    choice((item_start_a, item_start_b))
2789        .rewind() // Peek without consuming
2790        .boxed()
2791}
2792
2793/// Recovery parser that skips tokens until finding an item start.
2794/// Consumes at least one token to guarantee progress, then skips until
2795/// we find a token that could start an item.
2796fn error_recovery<'src, I>() -> GruelParser<'src, I, Item>
2797where
2798    I: ValueInput<'src, Token = TokenKind, Span = SimpleSpan>,
2799{
2800    // Skip at least one token to make progress, capturing the span
2801    any()
2802        .map_with(|_, extra| extra.span())
2803        // Then skip any more tokens that don't start an item
2804        .then(
2805            any()
2806                .and_is(item_start().not())
2807                .repeated()
2808                .collect::<Vec<_>>(),
2809        )
2810        .map(|(start_span, _): (SimpleSpan, Vec<TokenKind>)| {
2811            // Convert SimpleSpan to Span
2812            Item::Error(to_gruel_span(start_span))
2813        })
2814        .boxed()
2815}
2816
2817/// Parser for top-level items with error recovery.
2818/// When an item fails to parse, we skip tokens until we find the start of
2819/// another item, emit an Error node, and continue parsing.
2820fn item_with_recovery<'src, I>() -> GruelParser<'src, I, Item>
2821where
2822    I: ValueInput<'src, Token = TokenKind, Span = SimpleSpan>,
2823{
2824    item_parser()
2825        .recover_with(via_parser(error_recovery()))
2826        .boxed()
2827}
2828
2829/// Main parser that produces an AST
2830fn ast_parser<'src, I>() -> GruelParser<'src, I, Ast>
2831where
2832    I: ValueInput<'src, Token = TokenKind, Span = SimpleSpan>,
2833{
2834    item_with_recovery()
2835        .repeated()
2836        .collect::<Vec<_>>()
2837        .then_ignore(end())
2838        .map(|items| Ast { items })
2839        .boxed()
2840}
2841
2842/// Format a RichPattern for display in error messages.
2843fn format_pattern(pattern: &chumsky::error::RichPattern<'_, TokenKind>) -> String {
2844    use chumsky::error::RichPattern;
2845    match pattern {
2846        RichPattern::Token(tok) => tok.name().to_string(),
2847        RichPattern::Label(label) => label.to_string(),
2848        RichPattern::Identifier(ident) => format!("'{}'", ident),
2849        RichPattern::Any => "any token".to_string(),
2850        RichPattern::SomethingElse => "something else".to_string(),
2851        RichPattern::EndOfInput => "end of input".to_string(),
2852    }
2853}
2854
2855/// Convert chumsky Rich error to CompileError, preserving rich context.
2856fn convert_error(err: Rich<'_, TokenKind>) -> CompileError {
2857    let span = to_gruel_span(*err.span());
2858
2859    // Build the base error from the reason
2860    let mut error = match err.reason() {
2861        chumsky::error::RichReason::ExpectedFound { expected, found } => {
2862            let expected_str: Cow<'static, str> = if expected.is_empty() {
2863                Cow::Borrowed("something")
2864            } else {
2865                Cow::Owned(
2866                    expected
2867                        .iter()
2868                        .take(3) // Limit to first 3 for readability
2869                        .map(format_pattern)
2870                        .collect::<Vec<_>>()
2871                        .join(" or "),
2872                )
2873            };
2874
2875            let found_str: Cow<'static, str> = match found.as_ref() {
2876                Some(t) => Cow::Owned(t.name().to_string()),
2877                None => Cow::Borrowed("end of file"),
2878            };
2879
2880            CompileError::new(
2881                ErrorKind::UnexpectedToken {
2882                    expected: expected_str,
2883                    found: found_str,
2884                },
2885                span,
2886            )
2887        }
2888        chumsky::error::RichReason::Custom(msg) => {
2889            // Preserve the custom error message directly
2890            CompileError::new(ErrorKind::ParseError(msg.clone()), span)
2891        }
2892    };
2893
2894    // Add labelled contexts as secondary labels
2895    for (pattern, ctx_span) in err.contexts() {
2896        let label_msg = format!("while parsing {}", format_pattern(pattern));
2897        let label_span = to_gruel_span(*ctx_span);
2898        error = error.with_label(label_msg, label_span);
2899    }
2900
2901    error
2902}
2903
2904/// Chumsky-based parser that converts tokens into an AST.
2905pub struct ChumskyParser {
2906    tokens: Vec<(TokenKind, SimpleSpan)>,
2907    source_len: usize,
2908    interner: ThreadedRodeo,
2909    /// File ID for spans in this file.
2910    file_id: FileId,
2911}
2912
2913impl ChumskyParser {
2914    /// Create a new parser from tokens and an interner produced by the lexer.
2915    pub fn new(tokens: Vec<gruel_lexer::Token>, interner: ThreadedRodeo) -> Self {
2916        let source_len = tokens.last().map(|t| t.span.end as usize).unwrap_or(0);
2917        // Extract file_id from the first token (all tokens in a file have the same file_id)
2918        let file_id = tokens
2919            .first()
2920            .map(|t| t.span.file_id)
2921            .unwrap_or(FileId::DEFAULT);
2922
2923        let spanned_tokens: Vec<(TokenKind, SimpleSpan)> = tokens
2924            .into_iter()
2925            .filter(|t| t.kind != TokenKind::Eof) // Remove EOF, chumsky handles end differently
2926            .map(|t| {
2927                (
2928                    t.kind,
2929                    SimpleSpan::new(t.span.start as usize, t.span.end as usize),
2930                )
2931            })
2932            .collect();
2933        Self {
2934            tokens: spanned_tokens,
2935            source_len,
2936            interner,
2937            file_id,
2938        }
2939    }
2940
2941    /// Parse the tokens into an AST, returning the AST and the interner.
2942    ///
2943    /// Returns all parse errors if parsing fails, not just the first one.
2944    pub fn parse(mut self) -> MultiErrorResult<(Ast, ThreadedRodeo)> {
2945        // Pre-intern primitive type symbols and create parser state with file ID
2946        let syms = PrimitiveTypeSpurs::new(&mut self.interner);
2947        let parser_state = ParserState::new(syms, self.file_id);
2948        let mut state = SimpleState(parser_state);
2949
2950        // Create a stream from the token iterator
2951        let token_iter = self.tokens.iter().cloned();
2952        let stream = Stream::from_iter(token_iter);
2953
2954        // Map the stream to split (Token, Span) tuples
2955        let eoi: SimpleSpan = (self.source_len..self.source_len).into();
2956        let mapped = stream.map(eoi, |(tok, span)| (tok, span));
2957
2958        let result = ast_parser()
2959            .parse_with_state(mapped, &mut state)
2960            .into_result()
2961            .map_err(|errs| {
2962                let errors: Vec<CompileError> = errs.into_iter().map(convert_error).collect();
2963                CompileErrors::from(errors)
2964            });
2965
2966        result.map(|ast| (ast, self.interner))
2967    }
2968}
2969
2970#[cfg(test)]
2971mod tests {
2972    use super::*;
2973    use gruel_lexer::Lexer;
2974
2975    /// Result type for parsing that includes both the AST and interner.
2976    /// Provides convenient access to both the parsed AST and symbol resolution.
2977    #[derive(Debug)]
2978    struct ParseResult {
2979        ast: Ast,
2980        interner: ThreadedRodeo,
2981    }
2982
2983    impl ParseResult {
2984        /// Get the string for a symbol.
2985        fn get(&self, sym: Spur) -> &str {
2986            self.interner.resolve(&sym)
2987        }
2988    }
2989
2990    /// Result type for expression parsing that includes both the expr and interner.
2991    #[derive(Debug)]
2992    struct ExprResult {
2993        expr: Expr,
2994        interner: ThreadedRodeo,
2995    }
2996
2997    impl ExprResult {
2998        /// Get the string for a symbol.
2999        fn get(&self, sym: Spur) -> &str {
3000            self.interner.resolve(&sym)
3001        }
3002    }
3003
3004    fn parse(source: &str) -> MultiErrorResult<ParseResult> {
3005        let lexer = Lexer::new(source);
3006        let (tokens, interner) = lexer.tokenize().map_err(CompileErrors::from)?;
3007        let parser = ChumskyParser::new(tokens, interner);
3008        let (ast, interner) = parser.parse()?;
3009        Ok(ParseResult { ast, interner })
3010    }
3011
3012    fn parse_expr(source: &str) -> MultiErrorResult<ExprResult> {
3013        let result = parse(&format!("fn main() -> i32 {{ {} }}", source))?;
3014        let interner = result.interner;
3015        let expr = match result.ast.items.into_iter().next().unwrap() {
3016            Item::Function(f) => match f.body {
3017                Expr::Block(block) => *block.expr,
3018                other => other,
3019            },
3020            Item::Struct(_) => panic!("parse_expr helper should only be used with functions"),
3021            Item::Enum(_) => panic!("parse_expr helper should only be used with functions"),
3022            Item::DropFn(_) => panic!("parse_expr helper should only be used with functions"),
3023            Item::Const(_) => panic!("parse_expr helper should only be used with functions"),
3024            Item::Error(_) => panic!("parse_expr helper should only be used with functions"),
3025        };
3026        Ok(ExprResult { expr, interner })
3027    }
3028
3029    #[test]
3030    fn test_chumsky_parse_main() {
3031        let result = parse("fn main() -> i32 { 42 }").unwrap();
3032
3033        assert_eq!(result.ast.items.len(), 1);
3034        match &result.ast.items[0] {
3035            Item::Function(f) => {
3036                assert_eq!(result.get(f.name.name), "main");
3037                match f.return_type.as_ref().unwrap() {
3038                    TypeExpr::Named(ident) => assert_eq!(result.get(ident.name), "i32"),
3039                    _ => panic!("expected Named type"),
3040                }
3041                match &f.body {
3042                    Expr::Block(block) => match block.expr.as_ref() {
3043                        Expr::Int(lit) => assert_eq!(lit.value, 42),
3044                        _ => panic!("expected Int"),
3045                    },
3046                    _ => panic!("expected Block"),
3047                }
3048            }
3049            Item::Struct(_) => panic!("expected Function"),
3050            Item::Enum(_) => panic!("expected Function"),
3051            Item::DropFn(_) => panic!("expected Function"),
3052            Item::Const(_) => panic!("expected Function"),
3053            Item::Error(_) => panic!("expected Function"),
3054        }
3055    }
3056
3057    #[test]
3058    fn test_chumsky_parse_addition() {
3059        let result = parse_expr("1 + 2").unwrap();
3060        match result.expr {
3061            Expr::Binary(bin) => {
3062                assert!(matches!(bin.op, BinaryOp::Add));
3063                match (*bin.left, *bin.right) {
3064                    (Expr::Int(l), Expr::Int(r)) => {
3065                        assert_eq!(l.value, 1);
3066                        assert_eq!(r.value, 2);
3067                    }
3068                    _ => panic!("expected Int operands"),
3069                }
3070            }
3071            _ => panic!("expected Binary"),
3072        }
3073    }
3074
3075    #[test]
3076    fn test_chumsky_parse_precedence() {
3077        // 1 + 2 * 3 should parse as 1 + (2 * 3)
3078        let result = parse_expr("1 + 2 * 3").unwrap();
3079        match result.expr {
3080            Expr::Binary(bin) => {
3081                assert!(matches!(bin.op, BinaryOp::Add));
3082                match *bin.left {
3083                    Expr::Int(l) => assert_eq!(l.value, 1),
3084                    _ => panic!("expected Int"),
3085                }
3086                match *bin.right {
3087                    Expr::Binary(inner) => {
3088                        assert!(matches!(inner.op, BinaryOp::Mul));
3089                    }
3090                    _ => panic!("expected Binary"),
3091                }
3092            }
3093            _ => panic!("expected Binary"),
3094        }
3095    }
3096
3097    #[test]
3098    fn test_chumsky_parse_let_binding() {
3099        let result = parse("fn main() -> i32 { let x = 42; x }").unwrap();
3100        match &result.ast.items[0] {
3101            Item::Function(f) => match &f.body {
3102                Expr::Block(block) => {
3103                    assert_eq!(block.statements.len(), 1);
3104                    match &block.statements[0] {
3105                        Statement::Let(let_stmt) => {
3106                            assert!(!let_stmt.is_mut);
3107                            match &let_stmt.pattern {
3108                                LetPattern::Ident(ident) => {
3109                                    assert_eq!(result.get(ident.name), "x")
3110                                }
3111                                LetPattern::Wildcard(_) => panic!("expected Ident, got Wildcard"),
3112                                LetPattern::Struct { .. } => panic!("expected Ident, got Struct"),
3113                            }
3114                        }
3115                        _ => panic!("expected Let"),
3116                    }
3117                }
3118                _ => panic!("expected Block"),
3119            },
3120            Item::Struct(_) => panic!("expected Function"),
3121            Item::Enum(_) => panic!("expected Function"),
3122            Item::DropFn(_) => panic!("expected Function"),
3123            Item::Const(_) => panic!("expected Function"),
3124            Item::Error(_) => panic!("expected Function"),
3125        }
3126    }
3127
3128    #[test]
3129    fn test_while_simple() {
3130        // Simplest while case
3131        let result = parse("fn main() -> i32 { while true { } 0 }").unwrap();
3132        assert_eq!(result.ast.items.len(), 1);
3133    }
3134
3135    #[test]
3136    fn test_while_with_statement() {
3137        // While with assignment
3138        let result = parse("fn main() -> i32 { while true { x = 1; } 0 }").unwrap();
3139        assert_eq!(result.ast.items.len(), 1);
3140    }
3141
3142    #[test]
3143    fn test_function_calls() {
3144        let result =
3145            parse("fn add(a: i32, b: i32) -> i32 { a + b } fn main() -> i32 { add(1, 2) }")
3146                .unwrap();
3147        assert_eq!(result.ast.items.len(), 2);
3148    }
3149
3150    #[test]
3151    fn test_if_else() {
3152        let result = parse("fn main() -> i32 { if true { 1 } else { 0 } }").unwrap();
3153        assert_eq!(result.ast.items.len(), 1);
3154    }
3155
3156    #[test]
3157    fn test_nested_control_flow() {
3158        let result =
3159            parse("fn main() -> i32 { let mut x = 0; while x < 10 { x = x + 1; } x }").unwrap();
3160        assert_eq!(result.ast.items.len(), 1);
3161    }
3162
3163    // ==================== Struct Method Parsing Tests ====================
3164
3165    #[test]
3166    fn test_struct_with_single_method() {
3167        let result = parse("struct Point { x: i32, fn get_x(self) -> i32 { self.x } }").unwrap();
3168        assert_eq!(result.ast.items.len(), 1);
3169        match &result.ast.items[0] {
3170            Item::Struct(struct_decl) => {
3171                assert_eq!(result.get(struct_decl.name.name), "Point");
3172                assert_eq!(struct_decl.methods.len(), 1);
3173                let method = &struct_decl.methods[0];
3174                assert_eq!(result.get(method.name.name), "get_x");
3175                assert!(method.receiver.is_some()); // has self
3176                assert!(method.params.is_empty()); // no additional params
3177            }
3178            _ => panic!("expected Struct"),
3179        }
3180    }
3181
3182    #[test]
3183    fn test_struct_method_with_params() {
3184        let result =
3185            parse("struct Point { x: i32, fn add(self, n: i32) -> i32 { self.x + n } }").unwrap();
3186        assert_eq!(result.ast.items.len(), 1);
3187        match &result.ast.items[0] {
3188            Item::Struct(struct_decl) => {
3189                let method = &struct_decl.methods[0];
3190                assert_eq!(result.get(method.name.name), "add");
3191                assert!(method.receiver.is_some());
3192                assert_eq!(method.params.len(), 1);
3193                assert_eq!(result.get(method.params[0].name.name), "n");
3194            }
3195            _ => panic!("expected Struct"),
3196        }
3197    }
3198
3199    #[test]
3200    fn test_struct_associated_function() {
3201        // Associated function (no self)
3202        let result = parse(
3203            "struct Point { x: i32, y: i32, fn new(x: i32, y: i32) -> Point { Point { x: x, y: y } } }",
3204        )
3205        .unwrap();
3206        assert_eq!(result.ast.items.len(), 1);
3207        match &result.ast.items[0] {
3208            Item::Struct(struct_decl) => {
3209                let method = &struct_decl.methods[0];
3210                assert_eq!(result.get(method.name.name), "new");
3211                assert!(method.receiver.is_none()); // no self
3212                assert_eq!(method.params.len(), 2);
3213            }
3214            _ => panic!("expected Struct"),
3215        }
3216    }
3217
3218    #[test]
3219    fn test_struct_multiple_methods() {
3220        let result = parse(
3221            "struct Counter {
3222                 value: i32,
3223                 fn new() -> Counter { Counter { value: 0 } }
3224                 fn get(self) -> i32 { self.value }
3225                 fn increment(self) -> i32 { self.value + 1 }
3226             }",
3227        )
3228        .unwrap();
3229        assert_eq!(result.ast.items.len(), 1);
3230        match &result.ast.items[0] {
3231            Item::Struct(struct_decl) => {
3232                assert_eq!(struct_decl.methods.len(), 3);
3233                // First is associated function (no self)
3234                assert!(struct_decl.methods[0].receiver.is_none());
3235                assert_eq!(result.get(struct_decl.methods[0].name.name), "new");
3236                // Second is method (has self)
3237                assert!(struct_decl.methods[1].receiver.is_some());
3238                assert_eq!(result.get(struct_decl.methods[1].name.name), "get");
3239                // Third is method (has self)
3240                assert!(struct_decl.methods[2].receiver.is_some());
3241                assert_eq!(result.get(struct_decl.methods[2].name.name), "increment");
3242            }
3243            _ => panic!("expected Struct"),
3244        }
3245    }
3246
3247    #[test]
3248    fn test_struct_method_with_directive() {
3249        let result = parse("struct Foo { @inline fn bar(self) -> i32 { 42 } }").unwrap();
3250        match &result.ast.items[0] {
3251            Item::Struct(struct_decl) => {
3252                let method = &struct_decl.methods[0];
3253                assert_eq!(method.directives.len(), 1);
3254                assert_eq!(result.get(method.directives[0].name.name), "inline");
3255            }
3256            _ => panic!("expected Struct"),
3257        }
3258    }
3259
3260    // ==================== Method Call Parsing Tests ====================
3261
3262    #[test]
3263    fn test_method_call_simple() {
3264        let result = parse_expr("x.foo()").unwrap();
3265        match &result.expr {
3266            Expr::MethodCall(call) => {
3267                assert_eq!(result.get(call.method.name), "foo");
3268                assert!(call.args.is_empty());
3269                match call.receiver.as_ref() {
3270                    Expr::Ident(ident) => assert_eq!(result.get(ident.name), "x"),
3271                    _ => panic!("expected Ident receiver"),
3272                }
3273            }
3274            _ => panic!("expected MethodCall, got {:?}", result.expr),
3275        }
3276    }
3277
3278    #[test]
3279    fn test_method_call_with_args() {
3280        let result = parse_expr("point.add(5, 10)").unwrap();
3281        match &result.expr {
3282            Expr::MethodCall(call) => {
3283                assert_eq!(result.get(call.method.name), "add");
3284                assert_eq!(call.args.len(), 2);
3285            }
3286            _ => panic!("expected MethodCall"),
3287        }
3288    }
3289
3290    #[test]
3291    fn test_method_call_chained() {
3292        let result = parse_expr("x.foo().bar()").unwrap();
3293        match &result.expr {
3294            Expr::MethodCall(outer) => {
3295                assert_eq!(result.get(outer.method.name), "bar");
3296                match outer.receiver.as_ref() {
3297                    Expr::MethodCall(inner) => {
3298                        assert_eq!(result.get(inner.method.name), "foo");
3299                    }
3300                    _ => panic!("expected inner MethodCall"),
3301                }
3302            }
3303            _ => panic!("expected outer MethodCall"),
3304        }
3305    }
3306
3307    #[test]
3308    fn test_method_call_on_field_access() {
3309        let result = parse_expr("obj.field.method()").unwrap();
3310        match &result.expr {
3311            Expr::MethodCall(call) => {
3312                assert_eq!(result.get(call.method.name), "method");
3313                match call.receiver.as_ref() {
3314                    Expr::Field(field) => {
3315                        assert_eq!(result.get(field.field.name), "field");
3316                    }
3317                    _ => panic!("expected Field receiver"),
3318                }
3319            }
3320            _ => panic!("expected MethodCall"),
3321        }
3322    }
3323
3324    #[test]
3325    fn test_field_access_not_method_call() {
3326        // .field (not followed by parens) should parse as FieldExpr, not MethodCall
3327        let result = parse_expr("x.field").unwrap();
3328        match &result.expr {
3329            Expr::Field(field) => {
3330                assert_eq!(result.get(field.field.name), "field");
3331            }
3332            _ => panic!("expected Field, got {:?}", result.expr),
3333        }
3334    }
3335
3336    #[test]
3337    fn test_method_call_on_struct_literal() {
3338        let result =
3339            parse("struct Point { x: i32 } fn main() -> i32 { Point { x: 1 }.get() }").unwrap();
3340        match &result.ast.items[1] {
3341            Item::Function(f) => match &f.body {
3342                Expr::Block(block) => match block.expr.as_ref() {
3343                    Expr::MethodCall(call) => {
3344                        assert_eq!(result.get(call.method.name), "get");
3345                        match call.receiver.as_ref() {
3346                            Expr::StructLit(lit) => {
3347                                assert_eq!(result.get(lit.name.name), "Point")
3348                            }
3349                            _ => panic!("expected StructLit receiver"),
3350                        }
3351                    }
3352                    _ => panic!("expected MethodCall"),
3353                },
3354                _ => panic!("expected Block"),
3355            },
3356            _ => panic!("expected Function"),
3357        }
3358    }
3359
3360    #[test]
3361    fn test_method_call_on_paren_expr() {
3362        let result = parse_expr("(x).method()").unwrap();
3363        match &result.expr {
3364            Expr::MethodCall(call) => {
3365                assert_eq!(result.get(call.method.name), "method");
3366                match call.receiver.as_ref() {
3367                    Expr::Paren(_) => {}
3368                    _ => panic!("expected Paren receiver"),
3369                }
3370            }
3371            _ => panic!("expected MethodCall"),
3372        }
3373    }
3374
3375    #[test]
3376    fn test_method_call_mixed_with_index() {
3377        // Complex chain: array[0].method().field
3378        let result = parse_expr("arr[0].get().value").unwrap();
3379        match &result.expr {
3380            Expr::Field(field) => {
3381                assert_eq!(result.get(field.field.name), "value");
3382                match field.base.as_ref() {
3383                    Expr::MethodCall(call) => {
3384                        assert_eq!(result.get(call.method.name), "get");
3385                        match call.receiver.as_ref() {
3386                            Expr::Index(_) => {}
3387                            _ => panic!("expected Index receiver"),
3388                        }
3389                    }
3390                    _ => panic!("expected MethodCall"),
3391                }
3392            }
3393            _ => panic!("expected Field"),
3394        }
3395    }
3396
3397    #[test]
3398    fn test_associated_function_call() {
3399        // Type::function(args) syntax
3400        let result = parse_expr("Point::new(1, 2)").unwrap();
3401        match &result.expr {
3402            Expr::AssocFnCall(call) => {
3403                assert_eq!(result.get(call.type_name.name), "Point");
3404                assert_eq!(result.get(call.function.name), "new");
3405                assert_eq!(call.args.len(), 2);
3406            }
3407            _ => panic!("expected AssocFnCall, got {:?}", result.expr),
3408        }
3409    }
3410
3411    // ==================== Borrow Parameter Parsing Tests ====================
3412
3413    #[test]
3414    fn test_borrow_param_simple() {
3415        // Function with a borrow parameter
3416        let result = parse("fn read(borrow x: i32) -> i32 { x }").unwrap();
3417        match &result.ast.items[0] {
3418            Item::Function(f) => {
3419                assert_eq!(f.params.len(), 1);
3420                assert_eq!(f.params[0].mode, ParamMode::Borrow);
3421                assert_eq!(result.get(f.params[0].name.name), "x");
3422            }
3423            _ => panic!("expected Function"),
3424        }
3425    }
3426
3427    #[test]
3428    fn test_borrow_param_with_struct_type() {
3429        // Borrow parameter with a user-defined type
3430        let result =
3431            parse("struct Point { x: i32 } fn read(borrow p: Point) -> i32 { p.x }").unwrap();
3432        match &result.ast.items[1] {
3433            Item::Function(f) => {
3434                assert_eq!(f.params.len(), 1);
3435                assert_eq!(f.params[0].mode, ParamMode::Borrow);
3436                assert_eq!(result.get(f.params[0].name.name), "p");
3437                match &f.params[0].ty {
3438                    TypeExpr::Named(ident) => assert_eq!(result.get(ident.name), "Point"),
3439                    _ => panic!("expected Named type"),
3440                }
3441            }
3442            _ => panic!("expected Function"),
3443        }
3444    }
3445
3446    #[test]
3447    fn test_borrow_param_mixed_with_normal() {
3448        // Mixed borrow and normal parameters
3449        let result = parse("fn add(borrow a: i32, b: i32) -> i32 { a + b }").unwrap();
3450        match &result.ast.items[0] {
3451            Item::Function(f) => {
3452                assert_eq!(f.params.len(), 2);
3453                assert_eq!(f.params[0].mode, ParamMode::Borrow);
3454                assert_eq!(result.get(f.params[0].name.name), "a");
3455                assert_eq!(f.params[1].mode, ParamMode::Normal);
3456                assert_eq!(result.get(f.params[1].name.name), "b");
3457            }
3458            _ => panic!("expected Function"),
3459        }
3460    }
3461
3462    #[test]
3463    fn test_borrow_param_mixed_with_inout() {
3464        // Borrow and inout parameters in the same function
3465        let result = parse("fn modify(borrow a: i32, inout b: i32) { b = a; }").unwrap();
3466        match &result.ast.items[0] {
3467            Item::Function(f) => {
3468                assert_eq!(f.params.len(), 2);
3469                assert_eq!(f.params[0].mode, ParamMode::Borrow);
3470                assert_eq!(result.get(f.params[0].name.name), "a");
3471                assert_eq!(f.params[1].mode, ParamMode::Inout);
3472                assert_eq!(result.get(f.params[1].name.name), "b");
3473            }
3474            _ => panic!("expected Function"),
3475        }
3476    }
3477
3478    #[test]
3479    fn test_borrow_param_with_array_type() {
3480        // Borrow parameter with array type
3481        let result = parse("fn first(borrow arr: [i32; 3]) -> i32 { arr[0] }").unwrap();
3482        match &result.ast.items[0] {
3483            Item::Function(f) => {
3484                assert_eq!(f.params.len(), 1);
3485                assert_eq!(f.params[0].mode, ParamMode::Borrow);
3486                match &f.params[0].ty {
3487                    TypeExpr::Array { length, .. } => assert_eq!(*length, 3),
3488                    _ => panic!("expected Array type"),
3489                }
3490            }
3491            _ => panic!("expected Function"),
3492        }
3493    }
3494
3495    #[test]
3496    fn test_borrow_param_multiple() {
3497        // Multiple borrow parameters
3498        let result = parse("fn sum(borrow a: i32, borrow b: i32) -> i32 { a + b }").unwrap();
3499        match &result.ast.items[0] {
3500            Item::Function(f) => {
3501                assert_eq!(f.params.len(), 2);
3502                assert_eq!(f.params[0].mode, ParamMode::Borrow);
3503                assert_eq!(f.params[1].mode, ParamMode::Borrow);
3504            }
3505            _ => panic!("expected Function"),
3506        }
3507    }
3508
3509    // ==================== Borrow Argument Parsing Tests ====================
3510
3511    #[test]
3512    fn test_borrow_arg_simple() {
3513        // Function call with a borrow argument
3514        let result = parse_expr("read(borrow x)").unwrap();
3515        match &result.expr {
3516            Expr::Call(call) => {
3517                assert_eq!(result.get(call.name.name), "read");
3518                assert_eq!(call.args.len(), 1);
3519                assert_eq!(call.args[0].mode, ArgMode::Borrow);
3520                match &call.args[0].expr {
3521                    Expr::Ident(ident) => assert_eq!(result.get(ident.name), "x"),
3522                    _ => panic!("expected Ident argument"),
3523                }
3524            }
3525            _ => panic!("expected Call, got {:?}", result.expr),
3526        }
3527    }
3528
3529    #[test]
3530    fn test_borrow_arg_mixed_with_normal() {
3531        // Mixed borrow and normal arguments
3532        let result = parse_expr("foo(borrow a, b)").unwrap();
3533        match &result.expr {
3534            Expr::Call(call) => {
3535                assert_eq!(call.args.len(), 2);
3536                assert_eq!(call.args[0].mode, ArgMode::Borrow);
3537                assert_eq!(call.args[1].mode, ArgMode::Normal);
3538            }
3539            _ => panic!("expected Call"),
3540        }
3541    }
3542
3543    #[test]
3544    fn test_borrow_arg_mixed_with_inout() {
3545        // Borrow and inout arguments in the same call
3546        let result = parse_expr("modify(borrow a, inout b)").unwrap();
3547        match &result.expr {
3548            Expr::Call(call) => {
3549                assert_eq!(call.args.len(), 2);
3550                assert_eq!(call.args[0].mode, ArgMode::Borrow);
3551                assert_eq!(call.args[1].mode, ArgMode::Inout);
3552            }
3553            _ => panic!("expected Call"),
3554        }
3555    }
3556
3557    #[test]
3558    fn test_borrow_arg_multiple() {
3559        // Multiple borrow arguments
3560        let result = parse_expr("sum(borrow x, borrow y)").unwrap();
3561        match &result.expr {
3562            Expr::Call(call) => {
3563                assert_eq!(call.args.len(), 2);
3564                assert_eq!(call.args[0].mode, ArgMode::Borrow);
3565                assert_eq!(call.args[1].mode, ArgMode::Borrow);
3566            }
3567            _ => panic!("expected Call"),
3568        }
3569    }
3570
3571    #[test]
3572    fn test_borrow_arg_with_field_access() {
3573        // Borrow argument with field access expression
3574        let result = parse_expr("read(borrow point.x)").unwrap();
3575        match &result.expr {
3576            Expr::Call(call) => {
3577                assert_eq!(call.args.len(), 1);
3578                assert_eq!(call.args[0].mode, ArgMode::Borrow);
3579                match &call.args[0].expr {
3580                    Expr::Field(field) => assert_eq!(result.get(field.field.name), "x"),
3581                    _ => panic!("expected Field expression"),
3582                }
3583            }
3584            _ => panic!("expected Call"),
3585        }
3586    }
3587
3588    #[test]
3589    fn test_borrow_arg_in_method_call() {
3590        // Borrow argument in method call
3591        let result = parse_expr("obj.method(borrow x)").unwrap();
3592        match &result.expr {
3593            Expr::MethodCall(call) => {
3594                assert_eq!(call.args.len(), 1);
3595                assert_eq!(call.args[0].mode, ArgMode::Borrow);
3596            }
3597            _ => panic!("expected MethodCall"),
3598        }
3599    }
3600
3601    #[test]
3602    fn test_borrow_arg_in_associated_function() {
3603        // Borrow argument in associated function call
3604        let result = parse_expr("Foo::bar(borrow x)").unwrap();
3605        match &result.expr {
3606            Expr::AssocFnCall(call) => {
3607                assert_eq!(call.args.len(), 1);
3608                assert_eq!(call.args[0].mode, ArgMode::Borrow);
3609            }
3610            _ => panic!("expected AssocFnCall"),
3611        }
3612    }
3613
3614    #[test]
3615    fn test_borrow_helper_methods() {
3616        // Test CallArg helper methods
3617        let result = parse_expr("foo(borrow x, inout y, z)").unwrap();
3618        match &result.expr {
3619            Expr::Call(call) => {
3620                assert!(call.args[0].is_borrow());
3621                assert!(!call.args[0].is_inout());
3622                assert!(call.args[1].is_inout());
3623                assert!(!call.args[1].is_borrow());
3624                assert!(!call.args[2].is_borrow());
3625                assert!(!call.args[2].is_inout());
3626            }
3627            _ => panic!("expected Call"),
3628        }
3629    }
3630
3631    // ==================== Block Expression Statement Tests ====================
3632
3633    #[test]
3634    fn test_block_statement_followed_by_identifier() {
3635        // Block expression as a statement followed by an identifier expression
3636        // This is the regression test for gruel-wo1g
3637        let result = parse(
3638            "fn main() -> i32 {
3639                let a = 1;
3640                {
3641                    let b = 2;
3642                }
3643                a
3644            }",
3645        )
3646        .unwrap();
3647        match &result.ast.items[0] {
3648            Item::Function(f) => match &f.body {
3649                Expr::Block(block) => {
3650                    // Should have 2 statements: let a = 1; and { let b = 2; }
3651                    assert_eq!(block.statements.len(), 2);
3652                    // First statement is a let
3653                    assert!(matches!(&block.statements[0], Statement::Let(_)));
3654                    // Second statement is an expression statement containing a block
3655                    match &block.statements[1] {
3656                        Statement::Expr(Expr::Block(_)) => {}
3657                        _ => panic!("expected Expr(Block), got {:?}", block.statements[1]),
3658                    }
3659                    // Final expression should be 'a' (simple identifier)
3660                    match block.expr.as_ref() {
3661                        Expr::Ident(ident) => assert_eq!(result.get(ident.name), "a"),
3662                        _ => panic!("expected Ident expression, got {:?}", block.expr),
3663                    }
3664                }
3665                _ => panic!("expected Block"),
3666            },
3667            _ => panic!("expected Function"),
3668        }
3669    }
3670
3671    // ==================== Error Conversion Tests ====================
3672
3673    #[test]
3674    fn test_error_preserves_span() {
3675        // Parse invalid syntax and verify error has span information
3676        let result = parse("fn main() -> i32 { let = 42; }");
3677        assert!(result.is_err());
3678        let errors = result.unwrap_err();
3679        // Get the first error and verify it has span information
3680        let error = errors.first().expect("should have at least one error");
3681        assert!(error.has_span());
3682        assert!(error.span().is_some());
3683    }
3684
3685    #[test]
3686    fn test_error_expected_found() {
3687        // Missing expression after let
3688        let result = parse("fn main() -> i32 { let x = ; }");
3689        assert!(result.is_err());
3690        let errors = result.unwrap_err();
3691        let error = errors.first().expect("should have at least one error");
3692        // Error message should describe what was expected vs found
3693        let msg = error.to_string();
3694        assert!(
3695            msg.contains("expected") || msg.contains("found"),
3696            "error message: {}",
3697            msg
3698        );
3699    }
3700
3701    #[test]
3702    fn test_error_unexpected_eof() {
3703        // Unterminated block
3704        let result = parse("fn main() -> i32 {");
3705        assert!(result.is_err());
3706        let errors = result.unwrap_err();
3707        let error = errors.first().expect("should have at least one error");
3708        let msg = error.to_string();
3709        // Should indicate end of file was reached unexpectedly
3710        assert!(
3711            msg.contains("end of file") || msg.contains("expected"),
3712            "error message: {}",
3713            msg
3714        );
3715    }
3716
3717    #[test]
3718    fn test_format_pattern_token() {
3719        use chumsky::error::RichPattern;
3720        use chumsky::util::MaybeRef;
3721
3722        let pattern = RichPattern::Token(MaybeRef::Val(TokenKind::Plus));
3723        let formatted = format_pattern(&pattern);
3724        // TokenKind::name() returns quoted form like "'+'"
3725        assert_eq!(formatted, "'+'");
3726    }
3727
3728    #[test]
3729    fn test_format_pattern_label() {
3730        use chumsky::error::RichPattern;
3731
3732        let pattern: RichPattern<'_, TokenKind> = RichPattern::Label(Cow::Borrowed("expression"));
3733        let formatted = format_pattern(&pattern);
3734        assert_eq!(formatted, "expression");
3735    }
3736
3737    #[test]
3738    fn test_format_pattern_identifier() {
3739        use chumsky::error::RichPattern;
3740
3741        let pattern: RichPattern<'_, TokenKind> = RichPattern::Identifier("while".to_string());
3742        let formatted = format_pattern(&pattern);
3743        assert_eq!(formatted, "'while'");
3744    }
3745
3746    #[test]
3747    fn test_format_pattern_any() {
3748        use chumsky::error::RichPattern;
3749
3750        let pattern: RichPattern<'_, TokenKind> = RichPattern::Any;
3751        let formatted = format_pattern(&pattern);
3752        assert_eq!(formatted, "any token");
3753    }
3754
3755    #[test]
3756    fn test_format_pattern_end_of_input() {
3757        use chumsky::error::RichPattern;
3758
3759        let pattern: RichPattern<'_, TokenKind> = RichPattern::EndOfInput;
3760        let formatted = format_pattern(&pattern);
3761        assert_eq!(formatted, "end of input");
3762    }
3763
3764    #[test]
3765    fn test_parse_error_no_empty_found_clause() {
3766        // Test that error messages don't have empty "found" clauses
3767        // This was a bug when Custom errors were mapped to UnexpectedToken with empty found
3768        let result = parse("fn main() -> i32 { let x = ; }");
3769        assert!(result.is_err());
3770        let errors = result.unwrap_err();
3771        let error = errors.first().expect("should have at least one error");
3772        let msg = error.to_string();
3773        // Should not end with "found " (empty found)
3774        assert!(
3775            !msg.ends_with("found "),
3776            "error message should not have trailing empty 'found': {}",
3777            msg
3778        );
3779    }
3780
3781    #[test]
3782    fn test_parse_error_variant_display() {
3783        // Directly test that ParseError displays correctly
3784        let error = CompileError::new(
3785            ErrorKind::ParseError("expected semicolon after expression".to_string()),
3786            gruel_span::Span::new(0, 10),
3787        );
3788        assert_eq!(error.to_string(), "expected semicolon after expression");
3789    }
3790
3791    #[test]
3792    fn test_offset_to_u32_normal() {
3793        // Normal values should convert without issue
3794        assert_eq!(offset_to_u32(0), 0);
3795        assert_eq!(offset_to_u32(42), 42);
3796        assert_eq!(offset_to_u32(1000000), 1000000);
3797        assert_eq!(offset_to_u32(u32::MAX as usize), u32::MAX);
3798    }
3799
3800    #[test]
3801    #[should_panic(expected = "offset 4294967296 exceeds u32::MAX")]
3802    #[cfg(debug_assertions)]
3803    fn test_offset_to_u32_overflow_panics_in_debug() {
3804        // Value just over u32::MAX should panic in debug builds
3805        let _ = offset_to_u32((u32::MAX as usize) + 1);
3806    }
3807
3808    #[test]
3809    fn test_to_gruel_span_normal() {
3810        // Normal spans should convert without issue
3811        let simple = SimpleSpan::new(10, 20);
3812        let gruel = to_gruel_span(simple);
3813        assert_eq!(gruel.start, 10);
3814        assert_eq!(gruel.end, 20);
3815    }
3816
3817    #[test]
3818    fn test_parse_returns_multiple_errors() {
3819        // Test that we return ALL parse errors, not just the first one.
3820        // This uses source with multiple syntax errors that can be detected at once.
3821        // Note: The actual number of errors depends on Chumsky's error recovery,
3822        // but this test ensures the infrastructure returns all errors it finds.
3823        let source = "fn main() { let }"; // Missing variable name and expression
3824
3825        let result = parse(source);
3826        assert!(result.is_err(), "Expected parsing to fail");
3827
3828        // We should get at least one error
3829        let errors = result.unwrap_err();
3830        assert!(
3831            !errors.is_empty(),
3832            "Expected at least one error but got none"
3833        );
3834
3835        // Verify we can iterate over all errors
3836        let error_count = errors.len();
3837        assert!(
3838            error_count >= 1,
3839            "Expected at least 1 error, got {}",
3840            error_count
3841        );
3842    }
3843
3844    #[test]
3845    fn test_parse_error_collection_preserves_all() {
3846        // Test that the error collection mechanism preserves all errors.
3847        // This directly tests the CompileErrors::from(Vec<CompileError>) path.
3848        let errors = vec![
3849            CompileError::without_span(ErrorKind::UnexpectedToken {
3850                expected: std::borrow::Cow::Borrowed("ident"),
3851                found: std::borrow::Cow::Borrowed("let"),
3852            }),
3853            CompileError::without_span(ErrorKind::UnexpectedToken {
3854                expected: std::borrow::Cow::Borrowed("expr"),
3855                found: std::borrow::Cow::Borrowed("rbrace"),
3856            }),
3857        ];
3858
3859        let compile_errors = CompileErrors::from(errors);
3860        assert_eq!(compile_errors.len(), 2, "Expected 2 errors to be preserved");
3861    }
3862
3863    // ==================== Qualified Struct Literal Tests ====================
3864
3865    #[test]
3866    fn test_qualified_struct_literal() {
3867        // module.Point { x: 1, y: 2 } should parse as a qualified struct literal
3868        let result = parse_expr("mod.Point { x: 1, y: 2 }").unwrap();
3869        match &result.expr {
3870            Expr::StructLit(lit) => {
3871                // Verify it has a base (the module)
3872                assert!(
3873                    lit.base.is_some(),
3874                    "qualified struct literal should have a base"
3875                );
3876                match lit.base.as_ref().unwrap().as_ref() {
3877                    Expr::Ident(ident) => assert_eq!(result.get(ident.name), "mod"),
3878                    _ => panic!("base should be Ident, got {:?}", lit.base),
3879                }
3880                // Verify struct name
3881                assert_eq!(result.get(lit.name.name), "Point");
3882                // Verify fields
3883                assert_eq!(lit.fields.len(), 2);
3884                assert_eq!(result.get(lit.fields[0].name.name), "x");
3885                assert_eq!(result.get(lit.fields[1].name.name), "y");
3886            }
3887            _ => panic!("expected StructLit, got {:?}", result.expr),
3888        }
3889    }
3890
3891    #[test]
3892    fn test_qualified_struct_literal_empty() {
3893        // module.Empty {} should parse as a qualified struct literal with no fields
3894        let result = parse_expr("mod.Empty {}").unwrap();
3895        match &result.expr {
3896            Expr::StructLit(lit) => {
3897                assert!(
3898                    lit.base.is_some(),
3899                    "qualified struct literal should have a base"
3900                );
3901                assert_eq!(result.get(lit.name.name), "Empty");
3902                assert_eq!(lit.fields.len(), 0);
3903            }
3904            _ => panic!("expected StructLit, got {:?}", result.expr),
3905        }
3906    }
3907
3908    #[test]
3909    fn test_field_access_then_block() {
3910        // obj.field; { 1 } should parse as field access (discarded) followed by block expression
3911        // Note: obj.field { 1 } (without semicolon) is a syntax error because there's no
3912        // operator between the field access and the block.
3913        let result = parse("fn main() -> i32 { x.field; { 1 } }").unwrap();
3914        match &result.ast.items[0] {
3915            Item::Function(f) => match &f.body {
3916                Expr::Block(block) => {
3917                    // The block should have a statement (field access discarded) and
3918                    // a final expression (the inner block returning 1)
3919                    assert_eq!(block.statements.len(), 1);
3920                    match &block.statements[0] {
3921                        Statement::Expr(Expr::Field(field)) => {
3922                            assert_eq!(result.get(field.field.name), "field");
3923                        }
3924                        _ => panic!("expected Field statement, got {:?}", block.statements[0]),
3925                    }
3926                    match block.expr.as_ref() {
3927                        Expr::Block(inner) => match inner.expr.as_ref() {
3928                            Expr::Int(lit) => assert_eq!(lit.value, 1),
3929                            _ => panic!("expected Int, got {:?}", inner.expr),
3930                        },
3931                        _ => panic!("expected Block, got {:?}", block.expr),
3932                    }
3933                }
3934                _ => panic!("expected Block"),
3935            },
3936            _ => panic!("expected Function"),
3937        }
3938    }
3939
3940    #[test]
3941    fn test_field_access_block_without_semicolon_is_error() {
3942        // obj.field { 1 } without semicolon is a syntax error - it's not a struct literal
3943        // (because { 1 } doesn't match the { ident: } pattern) and it's not valid syntax.
3944        let result = parse("fn main() -> i32 { x.field { 1 } }");
3945        assert!(
3946            result.is_err(),
3947            "field + block without semicolon should be syntax error"
3948        );
3949    }
3950
3951    #[test]
3952    fn test_chained_qualified_struct_literal() {
3953        // a.b.Point { x: 1 } - nested field access then struct literal
3954        let result = parse_expr("a.b.Point { x: 1 }").unwrap();
3955        match &result.expr {
3956            Expr::StructLit(lit) => {
3957                // Should have a base that's a.b
3958                assert!(lit.base.is_some());
3959                match lit.base.as_ref().unwrap().as_ref() {
3960                    Expr::Field(field) => {
3961                        assert_eq!(result.get(field.field.name), "b");
3962                        match field.base.as_ref() {
3963                            Expr::Ident(ident) => assert_eq!(result.get(ident.name), "a"),
3964                            _ => panic!("inner base should be Ident"),
3965                        }
3966                    }
3967                    _ => panic!("base should be Field, got {:?}", lit.base),
3968                }
3969                assert_eq!(result.get(lit.name.name), "Point");
3970            }
3971            _ => panic!("expected StructLit, got {:?}", result.expr),
3972        }
3973    }
3974
3975    // ==================== Anonymous Struct Method Parsing Tests ====================
3976
3977    #[test]
3978    fn test_anon_struct_with_fields_only() {
3979        // Anonymous struct with only fields (no methods)
3980        let result = parse("fn make_type() -> type { struct { x: i32, y: i32 } }").unwrap();
3981        match &result.ast.items[0] {
3982            Item::Function(f) => {
3983                assert_eq!(result.get(f.name.name), "make_type");
3984                match &f.body {
3985                    Expr::Block(block) => match block.expr.as_ref() {
3986                        Expr::TypeLit(type_lit) => match &type_lit.type_expr {
3987                            TypeExpr::AnonymousStruct {
3988                                fields, methods, ..
3989                            } => {
3990                                assert_eq!(fields.len(), 2);
3991                                assert_eq!(result.get(fields[0].name.name), "x");
3992                                assert_eq!(result.get(fields[1].name.name), "y");
3993                                assert!(methods.is_empty());
3994                            }
3995                            _ => panic!("expected AnonymousStruct"),
3996                        },
3997                        _ => panic!("expected TypeLit"),
3998                    },
3999                    _ => panic!("expected Block"),
4000                }
4001            }
4002            _ => panic!("expected Function"),
4003        }
4004    }
4005
4006    #[test]
4007    fn test_anon_struct_with_method() {
4008        // Anonymous struct with a single method
4009        let result =
4010            parse("fn make_type() -> type { struct { x: i32, fn get_x(self) -> i32 { self.x } } }")
4011                .unwrap();
4012        match &result.ast.items[0] {
4013            Item::Function(f) => match &f.body {
4014                Expr::Block(block) => match block.expr.as_ref() {
4015                    Expr::TypeLit(type_lit) => match &type_lit.type_expr {
4016                        TypeExpr::AnonymousStruct {
4017                            fields, methods, ..
4018                        } => {
4019                            assert_eq!(fields.len(), 1);
4020                            assert_eq!(result.get(fields[0].name.name), "x");
4021                            assert_eq!(methods.len(), 1);
4022                            assert_eq!(result.get(methods[0].name.name), "get_x");
4023                            assert!(
4024                                methods[0].receiver.is_some(),
4025                                "method should have self receiver"
4026                            );
4027                        }
4028                        _ => panic!("expected AnonymousStruct"),
4029                    },
4030                    _ => panic!("expected TypeLit"),
4031                },
4032                _ => panic!("expected Block"),
4033            },
4034            _ => panic!("expected Function"),
4035        }
4036    }
4037
4038    #[test]
4039    fn test_anon_struct_with_associated_function() {
4040        // Anonymous struct with an associated function (no self)
4041        let result = parse(
4042            "fn make_type() -> type { struct { x: i32, fn new() -> Self { Self { x: 0 } } } }",
4043        )
4044        .unwrap();
4045        match &result.ast.items[0] {
4046            Item::Function(f) => match &f.body {
4047                Expr::Block(block) => match block.expr.as_ref() {
4048                    Expr::TypeLit(type_lit) => match &type_lit.type_expr {
4049                        TypeExpr::AnonymousStruct {
4050                            fields, methods, ..
4051                        } => {
4052                            assert_eq!(fields.len(), 1);
4053                            assert_eq!(methods.len(), 1);
4054                            assert_eq!(result.get(methods[0].name.name), "new");
4055                            assert!(
4056                                methods[0].receiver.is_none(),
4057                                "associated function should not have self"
4058                            );
4059                            // Check return type is Self
4060                            match &methods[0].return_type {
4061                                Some(TypeExpr::Named(ident)) => {
4062                                    assert_eq!(result.get(ident.name), "Self");
4063                                }
4064                                _ => panic!("expected Self return type"),
4065                            }
4066                        }
4067                        _ => panic!("expected AnonymousStruct"),
4068                    },
4069                    _ => panic!("expected TypeLit"),
4070                },
4071                _ => panic!("expected Block"),
4072            },
4073            _ => panic!("expected Function"),
4074        }
4075    }
4076
4077    #[test]
4078    fn test_anon_struct_with_multiple_methods() {
4079        // Anonymous struct with multiple methods
4080        let result = parse(
4081            r#"
4082            fn make_type() -> type {
4083                struct {
4084                    value: i32,
4085                    fn get(self) -> i32 { self.value }
4086                    fn set(self, v: i32) -> Self { Self { value: v } }
4087                }
4088            }
4089        "#,
4090        )
4091        .unwrap();
4092        match &result.ast.items[0] {
4093            Item::Function(f) => match &f.body {
4094                Expr::Block(block) => match block.expr.as_ref() {
4095                    Expr::TypeLit(type_lit) => match &type_lit.type_expr {
4096                        TypeExpr::AnonymousStruct {
4097                            fields, methods, ..
4098                        } => {
4099                            assert_eq!(fields.len(), 1);
4100                            assert_eq!(result.get(fields[0].name.name), "value");
4101                            assert_eq!(methods.len(), 2);
4102                            assert_eq!(result.get(methods[0].name.name), "get");
4103                            assert_eq!(result.get(methods[1].name.name), "set");
4104                            // Check set has a parameter
4105                            assert_eq!(methods[1].params.len(), 1);
4106                            assert_eq!(result.get(methods[1].params[0].name.name), "v");
4107                        }
4108                        _ => panic!("expected AnonymousStruct"),
4109                    },
4110                    _ => panic!("expected TypeLit"),
4111                },
4112                _ => panic!("expected Block"),
4113            },
4114            _ => panic!("expected Function"),
4115        }
4116    }
4117
4118    #[test]
4119    fn test_anon_struct_methods_only() {
4120        // Anonymous struct with only methods (no fields)
4121        let result =
4122            parse("fn make_type() -> type { struct { fn new() -> Self { Self { } } } }").unwrap();
4123        match &result.ast.items[0] {
4124            Item::Function(f) => match &f.body {
4125                Expr::Block(block) => match block.expr.as_ref() {
4126                    Expr::TypeLit(type_lit) => match &type_lit.type_expr {
4127                        TypeExpr::AnonymousStruct {
4128                            fields, methods, ..
4129                        } => {
4130                            assert!(fields.is_empty());
4131                            assert_eq!(methods.len(), 1);
4132                            assert_eq!(result.get(methods[0].name.name), "new");
4133                        }
4134                        _ => panic!("expected AnonymousStruct"),
4135                    },
4136                    _ => panic!("expected TypeLit"),
4137                },
4138                _ => panic!("expected Block"),
4139            },
4140            _ => panic!("expected Function"),
4141        }
4142    }
4143
4144    #[test]
4145    fn test_self_type_in_return() {
4146        // Test Self as return type
4147        let result =
4148            parse("fn make_type() -> type { struct { x: i32, fn clone(self) -> Self { self } } }")
4149                .unwrap();
4150        match &result.ast.items[0] {
4151            Item::Function(f) => match &f.body {
4152                Expr::Block(block) => match block.expr.as_ref() {
4153                    Expr::TypeLit(type_lit) => match &type_lit.type_expr {
4154                        TypeExpr::AnonymousStruct { methods, .. } => {
4155                            match &methods[0].return_type {
4156                                Some(TypeExpr::Named(ident)) => {
4157                                    assert_eq!(result.get(ident.name), "Self");
4158                                }
4159                                _ => panic!("expected Self return type"),
4160                            }
4161                        }
4162                        _ => panic!("expected AnonymousStruct"),
4163                    },
4164                    _ => panic!("expected TypeLit"),
4165                },
4166                _ => panic!("expected Block"),
4167            },
4168            _ => panic!("expected Function"),
4169        }
4170    }
4171
4172    #[test]
4173    fn test_self_type_in_param() {
4174        // Test Self as parameter type
4175        let result = parse(
4176            "fn make_type() -> type { struct { fn combine(self, other: Self) -> Self { self } } }",
4177        )
4178        .unwrap();
4179        match &result.ast.items[0] {
4180            Item::Function(f) => match &f.body {
4181                Expr::Block(block) => match block.expr.as_ref() {
4182                    Expr::TypeLit(type_lit) => match &type_lit.type_expr {
4183                        TypeExpr::AnonymousStruct { methods, .. } => {
4184                            // Check parameter type is Self
4185                            let param = &methods[0].params[0];
4186                            match &param.ty {
4187                                TypeExpr::Named(ident) => {
4188                                    assert_eq!(result.get(ident.name), "Self");
4189                                }
4190                                _ => panic!("expected Self param type"),
4191                            }
4192                        }
4193                        _ => panic!("expected AnonymousStruct"),
4194                    },
4195                    _ => panic!("expected TypeLit"),
4196                },
4197                _ => panic!("expected Block"),
4198            },
4199            _ => panic!("expected Function"),
4200        }
4201    }
4202
4203    #[test]
4204    fn test_self_type_standalone() {
4205        // Test Self as a standalone type (e.g., in struct method context)
4206        // This just tests lexing/parsing of Self as a type keyword
4207        let result = parse("struct Foo { x: i32, fn clone(self) -> Self { self } }").unwrap();
4208        match &result.ast.items[0] {
4209            Item::Struct(struct_decl) => {
4210                let method = &struct_decl.methods[0];
4211                match &method.return_type {
4212                    Some(TypeExpr::Named(ident)) => {
4213                        assert_eq!(result.get(ident.name), "Self");
4214                    }
4215                    _ => panic!("expected Self return type"),
4216                }
4217            }
4218            _ => panic!("expected Struct"),
4219        }
4220    }
4221}