Skip to main content

ion_core/
parser.rs

1use crate::ast::*;
2use crate::error::IonError;
3use crate::token::{SpannedToken, Token};
4
5/// Result of parsing: partial AST + accumulated errors.
6pub struct ParseOutput {
7    pub program: Program,
8    pub errors: Vec<IonError>,
9}
10
11pub struct Parser {
12    tokens: Vec<SpannedToken>,
13    pos: usize,
14    errors: Vec<IonError>,
15}
16
17impl Parser {
18    pub fn new(tokens: Vec<SpannedToken>) -> Self {
19        Self {
20            tokens,
21            pos: 0,
22            errors: Vec::new(),
23        }
24    }
25
26    /// Parse the full program, recovering from errors at statement boundaries.
27    /// Returns a `ParseOutput` containing both the partial AST and any errors.
28    pub fn parse_program_recovering(&mut self) -> ParseOutput {
29        let mut stmts = Vec::new();
30        while !self.is_at_end() {
31            let before = self.pos;
32            match self.parse_stmt() {
33                Ok(stmt) => stmts.push(stmt),
34                Err(e) => {
35                    self.errors.push(e);
36                    self.synchronize();
37                    // If no progress was made, force advance to prevent infinite loop
38                    if self.pos == before {
39                        self.advance();
40                    }
41                }
42            }
43        }
44        ParseOutput {
45            program: Program { stmts },
46            errors: std::mem::take(&mut self.errors),
47        }
48    }
49
50    /// Parse the full program, returning an error if any parse errors occurred.
51    /// For multi-error reporting, use `parse_program_recovering` instead.
52    pub fn parse_program(&mut self) -> Result<Program, IonError> {
53        let output = self.parse_program_recovering();
54        if output.errors.is_empty() {
55            Ok(output.program)
56        } else {
57            let mut errors = output.errors;
58            let mut first = errors.remove(0);
59            first.additional = errors;
60            Err(first)
61        }
62    }
63
64    /// Advance past the current error to the next statement boundary.
65    /// Respects brace nesting — stops at `}` that closes the current block.
66    fn synchronize(&mut self) {
67        let mut brace_depth = 0i32;
68        while !self.is_at_end() {
69            // If we just passed a semicolon at the same brace depth, we're at a new statement
70            if self.pos > 0 && brace_depth == 0 {
71                if let Token::Semicolon = &self.tokens[self.pos - 1].token {
72                    return;
73                }
74            }
75            match self.peek() {
76                // Track brace depth to avoid skipping past a closing }
77                Token::LBrace => {
78                    brace_depth += 1;
79                    self.advance();
80                }
81                Token::RBrace => {
82                    if brace_depth > 0 {
83                        brace_depth -= 1;
84                        self.advance();
85                    } else {
86                        // This } closes the enclosing block — stop before it
87                        return;
88                    }
89                }
90                // Stop at tokens that typically begin a new statement (if at top level)
91                Token::Let
92                | Token::Fn
93                | Token::For
94                | Token::While
95                | Token::If
96                | Token::Return
97                | Token::Match
98                | Token::Loop
99                | Token::Try
100                | Token::Break
101                | Token::Continue
102                | Token::Use
103                    if brace_depth == 0 =>
104                {
105                    return;
106                }
107                _ => {
108                    self.advance();
109                }
110            }
111        }
112    }
113
114    // --- Helpers ---
115
116    fn peek(&self) -> &Token {
117        &self.tokens[self.pos].token
118    }
119
120    fn span(&self) -> Span {
121        let t = &self.tokens[self.pos];
122        Span {
123            line: t.line,
124            col: t.col,
125        }
126    }
127
128    fn prev_span(&self) -> Span {
129        if self.pos > 0 {
130            let t = &self.tokens[self.pos - 1];
131            Span {
132                line: t.line,
133                col: t.col,
134            }
135        } else {
136            self.span()
137        }
138    }
139
140    fn advance(&mut self) -> &SpannedToken {
141        let tok = &self.tokens[self.pos];
142        if !self.is_at_end() {
143            self.pos += 1;
144        }
145        tok
146    }
147
148    fn is_at_end(&self) -> bool {
149        matches!(self.peek(), Token::Eof)
150    }
151
152    fn check(&self, token: &Token) -> bool {
153        std::mem::discriminant(self.peek()) == std::mem::discriminant(token)
154    }
155
156    fn eat(&mut self, expected: &Token) -> Result<(), IonError> {
157        if self.check(expected) {
158            self.advance();
159            Ok(())
160        } else {
161            let s = self.span();
162            Err(IonError::parse(
163                format!(
164                    "{}{:?}{}{:?}",
165                    ion_str!("expected "),
166                    expected,
167                    ion_str!(", found "),
168                    self.peek()
169                ),
170                s.line,
171                s.col,
172            ))
173        }
174    }
175
176    fn eat_ident(&mut self) -> Result<String, IonError> {
177        if let Token::Ident(name) = self.peek().clone() {
178            self.advance();
179            Ok(name)
180        } else {
181            let s = self.span();
182            Err(IonError::parse(
183                format!(
184                    "{}{:?}",
185                    ion_str!("expected identifier, found "),
186                    self.peek()
187                ),
188                s.line,
189                s.col,
190            ))
191        }
192    }
193
194    // --- Statement Parsing ---
195
196    fn parse_stmt(&mut self) -> Result<Stmt, IonError> {
197        let span = self.span();
198        match self.peek().clone() {
199            Token::Let => self.parse_let_stmt(),
200            Token::Fn => self.parse_fn_decl(),
201            Token::For => self.parse_for_stmt(None),
202            Token::While => self.parse_while_stmt(None),
203            Token::Loop => self.parse_loop_stmt(None),
204            Token::Label(name) => {
205                self.advance();
206                self.eat(&Token::Colon)?;
207                match self.peek() {
208                    Token::For => self.parse_for_stmt(Some(name)),
209                    Token::While => self.parse_while_stmt(Some(name)),
210                    Token::Loop => self.parse_loop_stmt(Some(name)),
211                    other => {
212                        let s = self.span();
213                        Err(IonError::parse(
214                            format!(
215                                "{}{:?}",
216                                ion_str!("expected loop after label, found "),
217                                other,
218                            ),
219                            s.line,
220                            s.col,
221                        ))
222                    }
223                }
224            }
225            Token::Break => self.parse_break_stmt(),
226            Token::Continue => self.parse_continue_stmt(span),
227            Token::Return => self.parse_return_stmt(),
228            Token::Use => self.parse_use_stmt(),
229            _ => self.parse_expr_or_assign_stmt(),
230        }
231    }
232
233    fn parse_optional_label(&mut self) -> Option<String> {
234        if let Token::Label(name) = self.peek().clone() {
235            self.advance();
236            Some(name)
237        } else {
238            None
239        }
240    }
241
242    fn parse_continue_stmt(&mut self, span: Span) -> Result<Stmt, IonError> {
243        self.eat(&Token::Continue)?;
244        let label = self.parse_optional_label();
245        self.eat(&Token::Semicolon)?;
246        Ok(Stmt {
247            kind: StmtKind::Continue { label },
248            span,
249        })
250    }
251
252    fn parse_let_stmt(&mut self) -> Result<Stmt, IonError> {
253        let span = self.span();
254        self.eat(&Token::Let)?;
255        let mutable = if self.check(&Token::Mut) {
256            self.advance();
257            true
258        } else {
259            false
260        };
261        let pattern = self.parse_pattern()?;
262        let type_ann = if self.check(&Token::Colon) {
263            self.advance();
264            Some(self.parse_type_ann()?)
265        } else {
266            None
267        };
268        self.eat(&Token::Eq)?;
269        let value = self.parse_expr()?;
270        self.eat(&Token::Semicolon)?;
271        Ok(Stmt {
272            kind: StmtKind::Let {
273                mutable,
274                pattern,
275                type_ann,
276                value,
277            },
278            span,
279        })
280    }
281
282    fn parse_type_ann(&mut self) -> Result<TypeAnn, IonError> {
283        // `fn` is a keyword token, so eat it specially
284        if self.check(&Token::Fn) {
285            self.advance();
286            return Ok(TypeAnn::Simple("fn".to_string()));
287        }
288        let name = self.eat_ident()?;
289        match name.as_str() {
290            "Option" => {
291                self.eat(&Token::Lt)?;
292                let inner = self.parse_type_ann()?;
293                self.eat(&Token::Gt)?;
294                Ok(TypeAnn::Option(Box::new(inner)))
295            }
296            "Result" => {
297                self.eat(&Token::Lt)?;
298                let ok = self.parse_type_ann()?;
299                self.eat(&Token::Comma)?;
300                let err = self.parse_type_ann()?;
301                self.eat(&Token::Gt)?;
302                Ok(TypeAnn::Result(Box::new(ok), Box::new(err)))
303            }
304            "list" => {
305                if self.check(&Token::Lt) {
306                    self.advance();
307                    let inner = self.parse_type_ann()?;
308                    self.eat(&Token::Gt)?;
309                    Ok(TypeAnn::List(Box::new(inner)))
310                } else {
311                    Ok(TypeAnn::Simple(name))
312                }
313            }
314            "dict" => {
315                if self.check(&Token::Lt) {
316                    self.advance();
317                    let key = self.parse_type_ann()?;
318                    self.eat(&Token::Comma)?;
319                    let val = self.parse_type_ann()?;
320                    self.eat(&Token::Gt)?;
321                    Ok(TypeAnn::Dict(Box::new(key), Box::new(val)))
322                } else {
323                    Ok(TypeAnn::Simple(name))
324                }
325            }
326            _ => Ok(TypeAnn::Simple(name)),
327        }
328    }
329
330    fn parse_fn_decl(&mut self) -> Result<Stmt, IonError> {
331        let span = self.span();
332        self.eat(&Token::Fn)?;
333        let name = self.eat_ident()?;
334        self.eat(&Token::LParen)?;
335        let params = self.parse_params()?;
336        self.eat(&Token::RParen)?;
337        self.eat(&Token::LBrace)?;
338        let body = self.parse_block_stmts()?;
339        self.eat(&Token::RBrace)?;
340        Ok(Stmt {
341            kind: StmtKind::FnDecl { name, params, body },
342            span,
343        })
344    }
345
346    fn parse_params(&mut self) -> Result<Vec<Param>, IonError> {
347        let mut params = Vec::new();
348        while !self.check(&Token::RParen) {
349            let name = self.eat_ident()?;
350            let default = if self.check(&Token::Eq) {
351                self.advance();
352                Some(self.parse_expr()?)
353            } else {
354                None
355            };
356            params.push(Param { name, default });
357            if !self.check(&Token::RParen) {
358                self.eat(&Token::Comma)?;
359            }
360        }
361        Ok(params)
362    }
363
364    fn parse_for_stmt(&mut self, label: Option<String>) -> Result<Stmt, IonError> {
365        let span = self.span();
366        self.eat(&Token::For)?;
367        let pattern = self.parse_pattern()?;
368        self.eat(&Token::In)?;
369        let iter = self.parse_expr()?;
370        self.eat(&Token::LBrace)?;
371        let body = self.parse_block_stmts()?;
372        self.eat(&Token::RBrace)?;
373        Ok(Stmt {
374            kind: StmtKind::For {
375                label,
376                pattern,
377                iter,
378                body,
379            },
380            span,
381        })
382    }
383
384    fn parse_while_stmt(&mut self, label: Option<String>) -> Result<Stmt, IonError> {
385        let span = self.span();
386        self.eat(&Token::While)?;
387        // while let ...
388        if self.check(&Token::Let) {
389            self.advance();
390            let pattern = self.parse_pattern()?;
391            self.eat(&Token::Eq)?;
392            let expr = self.parse_expr()?;
393            self.eat(&Token::LBrace)?;
394            let body = self.parse_block_stmts()?;
395            self.eat(&Token::RBrace)?;
396            return Ok(Stmt {
397                kind: StmtKind::WhileLet {
398                    label,
399                    pattern,
400                    expr,
401                    body,
402                },
403                span,
404            });
405        }
406        let cond = self.parse_expr()?;
407        self.eat(&Token::LBrace)?;
408        let body = self.parse_block_stmts()?;
409        self.eat(&Token::RBrace)?;
410        Ok(Stmt {
411            kind: StmtKind::While { label, cond, body },
412            span,
413        })
414    }
415
416    fn parse_loop_stmt(&mut self, label: Option<String>) -> Result<Stmt, IonError> {
417        let span = self.span();
418        self.eat(&Token::Loop)?;
419        self.eat(&Token::LBrace)?;
420        let body = self.parse_block_stmts()?;
421        self.eat(&Token::RBrace)?;
422        Ok(Stmt {
423            kind: StmtKind::Loop { label, body },
424            span,
425        })
426    }
427
428    fn parse_break_stmt(&mut self) -> Result<Stmt, IonError> {
429        let span = self.span();
430        self.eat(&Token::Break)?;
431        let label = self.parse_optional_label();
432        let value = if self.check(&Token::Semicolon) {
433            None
434        } else {
435            Some(self.parse_expr()?)
436        };
437        self.eat(&Token::Semicolon)?;
438        Ok(Stmt {
439            kind: StmtKind::Break { label, value },
440            span,
441        })
442    }
443
444    fn parse_return_stmt(&mut self) -> Result<Stmt, IonError> {
445        let span = self.span();
446        self.eat(&Token::Return)?;
447        let value = if self.check(&Token::Semicolon) {
448            None
449        } else {
450            Some(self.parse_expr()?)
451        };
452        self.eat(&Token::Semicolon)?;
453        Ok(Stmt {
454            kind: StmtKind::Return { value },
455            span,
456        })
457    }
458
459    /// Parse `use path::name;` or `use path::{a, b};` or `use path::*;`
460    fn parse_use_stmt(&mut self) -> Result<Stmt, IonError> {
461        let span = self.span();
462        self.eat(&Token::Use)?;
463
464        // Parse the module path: `a::b::...`
465        let mut path = Vec::new();
466        path.push(self.eat_ident()?);
467        while self.check(&Token::ColonColon) {
468            self.advance();
469            // Check what follows ::
470            if self.check(&Token::Star) {
471                // use path::*
472                self.advance();
473                self.eat(&Token::Semicolon)?;
474                return Ok(Stmt {
475                    kind: StmtKind::Use {
476                        path,
477                        imports: UseImports::Glob,
478                    },
479                    span,
480                });
481            } else if self.check(&Token::LBrace) {
482                // use path::{a, b, c}
483                self.advance();
484                let mut names = Vec::new();
485                while !self.check(&Token::RBrace) && !self.is_at_end() {
486                    names.push(self.eat_ident()?);
487                    if !self.check(&Token::RBrace) {
488                        self.eat(&Token::Comma)?;
489                    }
490                }
491                self.eat(&Token::RBrace)?;
492                self.eat(&Token::Semicolon)?;
493                return Ok(Stmt {
494                    kind: StmtKind::Use {
495                        path,
496                        imports: UseImports::Names(names),
497                    },
498                    span,
499                });
500            } else {
501                // More path segments or final single name
502                path.push(self.eat_ident()?);
503            }
504        }
505
506        // `use path::name;` — last segment is the imported name
507        self.eat(&Token::Semicolon)?;
508        if path.len() < 2 {
509            return Err(IonError::parse(
510                ion_str!("use statement requires at least module::name"),
511                span.line,
512                span.col,
513            ));
514        }
515        let name = path.pop().unwrap();
516        Ok(Stmt {
517            kind: StmtKind::Use {
518                path,
519                imports: UseImports::Single(name),
520            },
521            span,
522        })
523    }
524
525    fn parse_expr_or_assign_stmt(&mut self) -> Result<Stmt, IonError> {
526        let span = self.span();
527        let expr = self.parse_expr()?;
528
529        // Check for assignment
530        let assign_op = match self.peek() {
531            Token::Eq => Some(AssignOp::Eq),
532            Token::PlusEq => Some(AssignOp::PlusEq),
533            Token::MinusEq => Some(AssignOp::MinusEq),
534            Token::StarEq => Some(AssignOp::StarEq),
535            Token::SlashEq => Some(AssignOp::SlashEq),
536            _ => None,
537        };
538
539        if let Some(op) = assign_op {
540            self.advance();
541            let target = self.expr_to_assign_target(&expr)?;
542            let value = self.parse_expr()?;
543            self.eat(&Token::Semicolon)?;
544            Ok(Stmt {
545                kind: StmtKind::Assign { target, op, value },
546                span,
547            })
548        } else {
549            let has_semi = self.check(&Token::Semicolon);
550            if has_semi {
551                self.advance();
552            }
553            Ok(Stmt {
554                kind: StmtKind::ExprStmt { expr, has_semi },
555                span,
556            })
557        }
558    }
559
560    fn expr_to_assign_target(&self, expr: &Expr) -> Result<AssignTarget, IonError> {
561        match &expr.kind {
562            ExprKind::Ident(name) => Ok(AssignTarget::Ident(name.clone())),
563            ExprKind::Index { expr, index } => Ok(AssignTarget::Index(expr.clone(), index.clone())),
564            ExprKind::FieldAccess { expr, field } => {
565                Ok(AssignTarget::Field(expr.clone(), field.clone()))
566            }
567            _ => Err(IonError::parse(
568                ion_str!("invalid assignment target").to_string(),
569                expr.span.line,
570                expr.span.col,
571            )),
572        }
573    }
574
575    fn parse_block_stmts(&mut self) -> Result<Vec<Stmt>, IonError> {
576        let mut stmts = Vec::new();
577        while !self.check(&Token::RBrace) && !self.is_at_end() {
578            let before = self.pos;
579            match self.parse_stmt() {
580                Ok(stmt) => stmts.push(stmt),
581                Err(e) => {
582                    self.errors.push(e);
583                    self.synchronize();
584                    // If no progress was made, force advance to prevent infinite loop
585                    if self.pos == before {
586                        self.advance();
587                    }
588                }
589            }
590        }
591        Ok(stmts)
592    }
593
594    // --- Expression Parsing (Pratt) ---
595
596    fn parse_expr(&mut self) -> Result<Expr, IonError> {
597        self.parse_pipe()
598    }
599
600    fn parse_pipe(&mut self) -> Result<Expr, IonError> {
601        let mut left = self.parse_or()?;
602        while self.check(&Token::Pipe) {
603            self.advance();
604            let right = self.parse_or()?;
605            let span = left.span;
606            left = Expr {
607                kind: ExprKind::PipeOp {
608                    left: Box::new(left),
609                    right: Box::new(right),
610                },
611                span,
612            };
613        }
614        Ok(left)
615    }
616
617    fn parse_or(&mut self) -> Result<Expr, IonError> {
618        let mut left = self.parse_and()?;
619        while self.check(&Token::Or) {
620            self.advance();
621            let right = self.parse_and()?;
622            let span = left.span;
623            left = Expr {
624                kind: ExprKind::BinOp {
625                    left: Box::new(left),
626                    op: BinOp::Or,
627                    right: Box::new(right),
628                },
629                span,
630            };
631        }
632        Ok(left)
633    }
634
635    fn parse_and(&mut self) -> Result<Expr, IonError> {
636        let mut left = self.parse_bitwise_or()?;
637        while self.check(&Token::And) {
638            self.advance();
639            let right = self.parse_bitwise_or()?;
640            let span = left.span;
641            left = Expr {
642                kind: ExprKind::BinOp {
643                    left: Box::new(left),
644                    op: BinOp::And,
645                    right: Box::new(right),
646                },
647                span,
648            };
649        }
650        Ok(left)
651    }
652
653    fn parse_bitwise_or(&mut self) -> Result<Expr, IonError> {
654        let mut left = self.parse_bitwise_xor()?;
655        while self.check(&Token::PipeSym) {
656            self.advance();
657            let right = self.parse_bitwise_xor()?;
658            let span = left.span;
659            left = Expr {
660                kind: ExprKind::BinOp {
661                    left: Box::new(left),
662                    op: BinOp::BitOr,
663                    right: Box::new(right),
664                },
665                span,
666            };
667        }
668        Ok(left)
669    }
670
671    fn parse_bitwise_xor(&mut self) -> Result<Expr, IonError> {
672        let mut left = self.parse_bitwise_and()?;
673        while self.check(&Token::Caret) {
674            self.advance();
675            let right = self.parse_bitwise_and()?;
676            let span = left.span;
677            left = Expr {
678                kind: ExprKind::BinOp {
679                    left: Box::new(left),
680                    op: BinOp::BitXor,
681                    right: Box::new(right),
682                },
683                span,
684            };
685        }
686        Ok(left)
687    }
688
689    fn parse_bitwise_and(&mut self) -> Result<Expr, IonError> {
690        let mut left = self.parse_equality()?;
691        while self.check(&Token::Ampersand) {
692            self.advance();
693            let right = self.parse_equality()?;
694            let span = left.span;
695            left = Expr {
696                kind: ExprKind::BinOp {
697                    left: Box::new(left),
698                    op: BinOp::BitAnd,
699                    right: Box::new(right),
700                },
701                span,
702            };
703        }
704        Ok(left)
705    }
706
707    fn parse_equality(&mut self) -> Result<Expr, IonError> {
708        let mut left = self.parse_comparison()?;
709        loop {
710            let op = match self.peek() {
711                Token::EqEq => BinOp::Eq,
712                Token::BangEq => BinOp::Ne,
713                _ => break,
714            };
715            self.advance();
716            let right = self.parse_comparison()?;
717            let span = left.span;
718            left = Expr {
719                kind: ExprKind::BinOp {
720                    left: Box::new(left),
721                    op,
722                    right: Box::new(right),
723                },
724                span,
725            };
726        }
727        Ok(left)
728    }
729
730    fn parse_comparison(&mut self) -> Result<Expr, IonError> {
731        let mut left = self.parse_shift()?;
732        loop {
733            let op = match self.peek() {
734                Token::Lt => BinOp::Lt,
735                Token::Gt => BinOp::Gt,
736                Token::LtEq => BinOp::Le,
737                Token::GtEq => BinOp::Ge,
738                _ => break,
739            };
740            self.advance();
741            let right = self.parse_shift()?;
742            let span = left.span;
743            left = Expr {
744                kind: ExprKind::BinOp {
745                    left: Box::new(left),
746                    op,
747                    right: Box::new(right),
748                },
749                span,
750            };
751        }
752        Ok(left)
753    }
754
755    fn parse_shift(&mut self) -> Result<Expr, IonError> {
756        let mut left = self.parse_range()?;
757        loop {
758            let op = match self.peek() {
759                Token::Shl => BinOp::Shl,
760                Token::Shr => BinOp::Shr,
761                _ => break,
762            };
763            self.advance();
764            let right = self.parse_range()?;
765            let span = left.span;
766            left = Expr {
767                kind: ExprKind::BinOp {
768                    left: Box::new(left),
769                    op,
770                    right: Box::new(right),
771                },
772                span,
773            };
774        }
775        Ok(left)
776    }
777
778    fn parse_range(&mut self) -> Result<Expr, IonError> {
779        let left = self.parse_addition()?;
780        match self.peek() {
781            Token::DotDot => {
782                self.advance();
783                let right = self.parse_addition()?;
784                let span = left.span;
785                Ok(Expr {
786                    kind: ExprKind::Range {
787                        start: Box::new(left),
788                        end: Box::new(right),
789                        inclusive: false,
790                    },
791                    span,
792                })
793            }
794            Token::DotDotEq => {
795                self.advance();
796                let right = self.parse_addition()?;
797                let span = left.span;
798                Ok(Expr {
799                    kind: ExprKind::Range {
800                        start: Box::new(left),
801                        end: Box::new(right),
802                        inclusive: true,
803                    },
804                    span,
805                })
806            }
807            _ => Ok(left),
808        }
809    }
810
811    fn parse_addition(&mut self) -> Result<Expr, IonError> {
812        let mut left = self.parse_multiplication()?;
813        loop {
814            let op = match self.peek() {
815                Token::Plus => BinOp::Add,
816                Token::Minus => BinOp::Sub,
817                _ => break,
818            };
819            self.advance();
820            let right = self.parse_multiplication()?;
821            let span = left.span;
822            left = Expr {
823                kind: ExprKind::BinOp {
824                    left: Box::new(left),
825                    op,
826                    right: Box::new(right),
827                },
828                span,
829            };
830        }
831        Ok(left)
832    }
833
834    fn parse_multiplication(&mut self) -> Result<Expr, IonError> {
835        let mut left = self.parse_unary()?;
836        loop {
837            let op = match self.peek() {
838                Token::Star => BinOp::Mul,
839                Token::Slash => BinOp::Div,
840                Token::Percent => BinOp::Mod,
841                _ => break,
842            };
843            self.advance();
844            let right = self.parse_unary()?;
845            let span = left.span;
846            left = Expr {
847                kind: ExprKind::BinOp {
848                    left: Box::new(left),
849                    op,
850                    right: Box::new(right),
851                },
852                span,
853            };
854        }
855        Ok(left)
856    }
857
858    fn parse_unary(&mut self) -> Result<Expr, IonError> {
859        let span = self.span();
860        match self.peek() {
861            Token::Minus => {
862                self.advance();
863                let expr = self.parse_unary()?;
864                Ok(Expr {
865                    kind: ExprKind::UnaryOp {
866                        op: UnaryOp::Neg,
867                        expr: Box::new(expr),
868                    },
869                    span,
870                })
871            }
872            Token::Bang => {
873                self.advance();
874                let expr = self.parse_unary()?;
875                Ok(Expr {
876                    kind: ExprKind::UnaryOp {
877                        op: UnaryOp::Not,
878                        expr: Box::new(expr),
879                    },
880                    span,
881                })
882            }
883            _ => self.parse_postfix(),
884        }
885    }
886
887    fn parse_postfix(&mut self) -> Result<Expr, IonError> {
888        let mut expr = self.parse_primary()?;
889
890        loop {
891            match self.peek() {
892                Token::Question => {
893                    self.advance();
894                    let span = expr.span;
895                    expr = Expr {
896                        kind: ExprKind::Try(Box::new(expr)),
897                        span,
898                    };
899                }
900                Token::Dot => {
901                    self.advance();
902                    // .await is special syntax
903                    if self.check(&Token::Await) {
904                        self.advance();
905                        let span = expr.span;
906                        expr = Expr {
907                            kind: ExprKind::AwaitExpr(Box::new(expr)),
908                            span,
909                        };
910                        continue;
911                    }
912                    let field = self.eat_ident()?;
913                    if self.check(&Token::LParen) {
914                        // Method call
915                        self.advance();
916                        let args = self.parse_call_args()?;
917                        self.eat(&Token::RParen)?;
918                        let span = expr.span;
919                        expr = Expr {
920                            kind: ExprKind::MethodCall {
921                                expr: Box::new(expr),
922                                method: field,
923                                args,
924                            },
925                            span,
926                        };
927                    } else {
928                        let span = expr.span;
929                        expr = Expr {
930                            kind: ExprKind::FieldAccess {
931                                expr: Box::new(expr),
932                                field,
933                            },
934                            span,
935                        };
936                    }
937                }
938                Token::LBracket => {
939                    self.advance();
940                    let span = expr.span;
941                    // Check for [..end] or [..=end] or [..]
942                    if self.check(&Token::DotDot) || self.check(&Token::DotDotEq) {
943                        let inclusive = self.check(&Token::DotDotEq);
944                        self.advance();
945                        let end = if self.check(&Token::RBracket) {
946                            None
947                        } else {
948                            Some(Box::new(self.parse_expr()?))
949                        };
950                        self.eat(&Token::RBracket)?;
951                        expr = Expr {
952                            kind: ExprKind::Slice {
953                                expr: Box::new(expr),
954                                start: None,
955                                end,
956                                inclusive,
957                            },
958                            span,
959                        };
960                        continue;
961                    }
962                    // Parse index/start using parse_addition (stops before ..)
963                    let first = self.parse_addition()?;
964                    if self.check(&Token::DotDot) || self.check(&Token::DotDotEq) {
965                        let inclusive = self.check(&Token::DotDotEq);
966                        self.advance();
967                        let end = if self.check(&Token::RBracket) {
968                            None
969                        } else {
970                            Some(Box::new(self.parse_expr()?))
971                        };
972                        self.eat(&Token::RBracket)?;
973                        expr = Expr {
974                            kind: ExprKind::Slice {
975                                expr: Box::new(expr),
976                                start: Some(Box::new(first)),
977                                end,
978                                inclusive,
979                            },
980                            span,
981                        };
982                    } else {
983                        self.eat(&Token::RBracket)?;
984                        expr = Expr {
985                            kind: ExprKind::Index {
986                                expr: Box::new(expr),
987                                index: Box::new(first),
988                            },
989                            span,
990                        };
991                    }
992                }
993                Token::LParen => {
994                    // Check if this is truly a call (the expr must be callable)
995                    self.advance();
996                    let args = self.parse_call_args()?;
997                    self.eat(&Token::RParen)?;
998                    let span = expr.span;
999                    expr = Expr {
1000                        kind: ExprKind::Call {
1001                            func: Box::new(expr),
1002                            args,
1003                        },
1004                        span,
1005                    };
1006                }
1007                _ => break,
1008            }
1009        }
1010        Ok(expr)
1011    }
1012
1013    fn parse_call_args(&mut self) -> Result<Vec<CallArg>, IonError> {
1014        let mut args = Vec::new();
1015        while !self.check(&Token::RParen) && !self.is_at_end() {
1016            // Check for named argument: `name: value`
1017            let arg = if let Token::Ident(name) = self.peek().clone() {
1018                if self.tokens.get(self.pos + 1).map(|t| &t.token) == Some(&Token::Colon) {
1019                    let name = name.clone();
1020                    self.advance(); // ident
1021                    self.advance(); // colon
1022                    let value = self.parse_expr()?;
1023                    CallArg {
1024                        name: Some(name),
1025                        value,
1026                    }
1027                } else {
1028                    let value = self.parse_expr()?;
1029                    CallArg { name: None, value }
1030                }
1031            } else {
1032                let value = self.parse_expr()?;
1033                CallArg { name: None, value }
1034            };
1035            args.push(arg);
1036            if !self.check(&Token::RParen) {
1037                self.eat(&Token::Comma)?;
1038            }
1039        }
1040        Ok(args)
1041    }
1042
1043    fn parse_primary(&mut self) -> Result<Expr, IonError> {
1044        let span = self.span();
1045        match self.peek().clone() {
1046            Token::Int(n) => {
1047                self.advance();
1048                Ok(Expr {
1049                    kind: ExprKind::Int(n),
1050                    span,
1051                })
1052            }
1053            Token::Float(n) => {
1054                self.advance();
1055                Ok(Expr {
1056                    kind: ExprKind::Float(n),
1057                    span,
1058                })
1059            }
1060            Token::True => {
1061                self.advance();
1062                Ok(Expr {
1063                    kind: ExprKind::Bool(true),
1064                    span,
1065                })
1066            }
1067            Token::False => {
1068                self.advance();
1069                Ok(Expr {
1070                    kind: ExprKind::Bool(false),
1071                    span,
1072                })
1073            }
1074            Token::Str(s) => {
1075                self.advance();
1076                Ok(Expr {
1077                    kind: ExprKind::Str(s),
1078                    span,
1079                })
1080            }
1081            Token::Bytes(b) => {
1082                self.advance();
1083                Ok(Expr {
1084                    kind: ExprKind::Bytes(b),
1085                    span,
1086                })
1087            }
1088            Token::FStr(template) => {
1089                self.advance();
1090                let parts = self.parse_fstr_parts(&template, span)?;
1091                Ok(Expr {
1092                    kind: ExprKind::FStr(parts),
1093                    span,
1094                })
1095            }
1096            Token::None => {
1097                self.advance();
1098                Ok(Expr {
1099                    kind: ExprKind::None,
1100                    span,
1101                })
1102            }
1103            Token::Some => {
1104                self.advance();
1105                self.eat(&Token::LParen)?;
1106                let expr = self.parse_expr()?;
1107                self.eat(&Token::RParen)?;
1108                Ok(Expr {
1109                    kind: ExprKind::SomeExpr(Box::new(expr)),
1110                    span,
1111                })
1112            }
1113            Token::Ok => {
1114                self.advance();
1115                self.eat(&Token::LParen)?;
1116                let expr = self.parse_expr()?;
1117                self.eat(&Token::RParen)?;
1118                Ok(Expr {
1119                    kind: ExprKind::OkExpr(Box::new(expr)),
1120                    span,
1121                })
1122            }
1123            Token::Err => {
1124                self.advance();
1125                self.eat(&Token::LParen)?;
1126                let expr = self.parse_expr()?;
1127                self.eat(&Token::RParen)?;
1128                Ok(Expr {
1129                    kind: ExprKind::ErrExpr(Box::new(expr)),
1130                    span,
1131                })
1132            }
1133            Token::LParen => {
1134                self.advance();
1135                // Check for closure: |
1136                // or tuple: (a, b, c)
1137                // or grouping: (expr)
1138                if self.check(&Token::RParen) {
1139                    self.advance();
1140                    return Ok(Expr {
1141                        kind: ExprKind::Unit,
1142                        span,
1143                    });
1144                }
1145                let first = self.parse_expr()?;
1146                if self.check(&Token::Comma) {
1147                    // Tuple
1148                    let mut items = vec![first];
1149                    while self.check(&Token::Comma) {
1150                        self.advance();
1151                        if self.check(&Token::RParen) {
1152                            break;
1153                        }
1154                        items.push(self.parse_expr()?);
1155                    }
1156                    self.eat(&Token::RParen)?;
1157                    Ok(Expr {
1158                        kind: ExprKind::Tuple(items),
1159                        span,
1160                    })
1161                } else {
1162                    self.eat(&Token::RParen)?;
1163                    Ok(first) // grouping
1164                }
1165            }
1166            Token::LBracket => {
1167                self.advance();
1168                if self.check(&Token::RBracket) {
1169                    self.advance();
1170                    return Ok(Expr {
1171                        kind: ExprKind::List(vec![]),
1172                        span,
1173                    });
1174                }
1175                // Check for spread as first entry
1176                let first_entry = if self.check(&Token::DotDotDot) {
1177                    self.advance();
1178                    ListEntry::Spread(self.parse_expr()?)
1179                } else {
1180                    ListEntry::Elem(self.parse_expr()?)
1181                };
1182                // Check for list comprehension: [expr for pattern in iter]
1183                if let ListEntry::Elem(ref first) = first_entry {
1184                    if self.check(&Token::For) {
1185                        self.advance();
1186                        let pattern = self.parse_pattern()?;
1187                        self.eat(&Token::In)?;
1188                        let iter = self.parse_expr()?;
1189                        let cond = if self.check(&Token::If) {
1190                            self.advance();
1191                            Some(Box::new(self.parse_expr()?))
1192                        } else {
1193                            None
1194                        };
1195                        self.eat(&Token::RBracket)?;
1196                        return Ok(Expr {
1197                            kind: ExprKind::ListComp {
1198                                expr: Box::new(first.clone()),
1199                                pattern,
1200                                iter: Box::new(iter),
1201                                cond,
1202                            },
1203                            span,
1204                        });
1205                    }
1206                }
1207                let mut items = vec![first_entry];
1208                while self.check(&Token::Comma) {
1209                    self.advance();
1210                    if self.check(&Token::RBracket) {
1211                        break;
1212                    }
1213                    if self.check(&Token::DotDotDot) {
1214                        self.advance();
1215                        items.push(ListEntry::Spread(self.parse_expr()?));
1216                    } else {
1217                        items.push(ListEntry::Elem(self.parse_expr()?));
1218                    }
1219                }
1220                self.eat(&Token::RBracket)?;
1221                Ok(Expr {
1222                    kind: ExprKind::List(items),
1223                    span,
1224                })
1225            }
1226            Token::HashBrace => {
1227                self.advance();
1228                if self.check(&Token::RBrace) {
1229                    self.advance();
1230                    return Ok(Expr {
1231                        kind: ExprKind::Dict(vec![]),
1232                        span,
1233                    });
1234                }
1235                // Check for spread first entry
1236                if self.check(&Token::DotDotDot) {
1237                    return self.parse_dict_entries(span);
1238                }
1239                let first_key = self.parse_dict_key()?;
1240                self.eat(&Token::Colon)?;
1241                let first_val = self.parse_expr()?;
1242                // Check for dict comprehension: #{ key: val for pat in iter }
1243                if self.check(&Token::For) {
1244                    self.advance();
1245                    let pattern = self.parse_pattern()?;
1246                    self.eat(&Token::In)?;
1247                    let iter = self.parse_expr()?;
1248                    let cond = if self.check(&Token::If) {
1249                        self.advance();
1250                        Some(Box::new(self.parse_expr()?))
1251                    } else {
1252                        None
1253                    };
1254                    self.eat(&Token::RBrace)?;
1255                    return Ok(Expr {
1256                        kind: ExprKind::DictComp {
1257                            key: Box::new(first_key),
1258                            value: Box::new(first_val),
1259                            pattern,
1260                            iter: Box::new(iter),
1261                            cond,
1262                        },
1263                        span,
1264                    });
1265                }
1266                let mut entries = vec![DictEntry::KeyValue(first_key, first_val)];
1267                while self.check(&Token::Comma) {
1268                    self.advance();
1269                    if self.check(&Token::RBrace) {
1270                        break;
1271                    }
1272                    if self.check(&Token::DotDotDot) {
1273                        self.advance();
1274                        entries.push(DictEntry::Spread(self.parse_expr()?));
1275                    } else {
1276                        let key = self.parse_dict_key()?;
1277                        self.eat(&Token::Colon)?;
1278                        let value = self.parse_expr()?;
1279                        entries.push(DictEntry::KeyValue(key, value));
1280                    }
1281                }
1282                self.eat(&Token::RBrace)?;
1283                Ok(Expr {
1284                    kind: ExprKind::Dict(entries),
1285                    span,
1286                })
1287            }
1288            Token::PipeSym => {
1289                // Lambda: |params| body
1290                self.advance();
1291                let mut params = Vec::new();
1292                if !self.check(&Token::PipeSym) {
1293                    params.push(self.eat_ident()?);
1294                    while self.check(&Token::Comma) {
1295                        self.advance();
1296                        params.push(self.eat_ident()?);
1297                    }
1298                }
1299                self.eat(&Token::PipeSym)?;
1300                let body = if self.check(&Token::LBrace) {
1301                    self.advance();
1302                    let stmts = self.parse_block_stmts()?;
1303                    self.eat(&Token::RBrace)?;
1304                    Expr {
1305                        kind: ExprKind::Block(stmts),
1306                        span,
1307                    }
1308                } else {
1309                    self.parse_expr()?
1310                };
1311                Ok(Expr {
1312                    kind: ExprKind::Lambda {
1313                        params,
1314                        body: Box::new(body),
1315                    },
1316                    span,
1317                })
1318            }
1319            Token::Or => {
1320                // Zero-arg lambda: || body
1321                self.advance();
1322                let body = if self.check(&Token::LBrace) {
1323                    self.advance();
1324                    let stmts = self.parse_block_stmts()?;
1325                    self.eat(&Token::RBrace)?;
1326                    Expr {
1327                        kind: ExprKind::Block(stmts),
1328                        span,
1329                    }
1330                } else {
1331                    self.parse_expr()?
1332                };
1333                Ok(Expr {
1334                    kind: ExprKind::Lambda {
1335                        params: Vec::new(),
1336                        body: Box::new(body),
1337                    },
1338                    span,
1339                })
1340            }
1341            Token::If => self.parse_if_expr(),
1342            Token::Match => self.parse_match_expr(),
1343            Token::Loop => {
1344                self.advance();
1345                self.eat(&Token::LBrace)?;
1346                let body = self.parse_block_stmts()?;
1347                self.eat(&Token::RBrace)?;
1348                Ok(Expr {
1349                    kind: ExprKind::LoopExpr(body),
1350                    span,
1351                })
1352            }
1353            Token::Try => {
1354                self.advance();
1355                self.eat(&Token::LBrace)?;
1356                let body = self.parse_block_stmts()?;
1357                self.eat(&Token::RBrace)?;
1358                self.eat(&Token::Catch)?;
1359                let var = match self.peek() {
1360                    Token::Ident(name) => {
1361                        let name = name.clone();
1362                        self.advance();
1363                        name
1364                    }
1365                    _ => {
1366                        return Err(IonError::parse(
1367                            ion_str!("expected identifier after 'catch'").to_string(),
1368                            self.span().line,
1369                            self.span().col,
1370                        ));
1371                    }
1372                };
1373                self.eat(&Token::LBrace)?;
1374                let handler = self.parse_block_stmts()?;
1375                self.eat(&Token::RBrace)?;
1376                Ok(Expr {
1377                    kind: ExprKind::TryCatch { body, var, handler },
1378                    span,
1379                })
1380            }
1381            Token::Async => {
1382                self.advance();
1383                self.eat(&Token::LBrace)?;
1384                let body = self.parse_block_stmts()?;
1385                self.eat(&Token::RBrace)?;
1386                Ok(Expr {
1387                    kind: ExprKind::AsyncBlock(body),
1388                    span,
1389                })
1390            }
1391            Token::Spawn => {
1392                self.advance();
1393                let expr = self.parse_expr()?;
1394                Ok(Expr {
1395                    kind: ExprKind::SpawnExpr(Box::new(expr)),
1396                    span,
1397                })
1398            }
1399            Token::Select => {
1400                self.advance();
1401                self.eat(&Token::LBrace)?;
1402                let mut branches = Vec::new();
1403                while !self.check(&Token::RBrace) {
1404                    let pattern = self.parse_pattern()?;
1405                    self.eat(&Token::Eq)?;
1406                    let future_expr = self.parse_expr()?;
1407                    self.eat(&Token::Arrow)?;
1408                    let body = self.parse_expr()?;
1409                    branches.push(SelectBranch {
1410                        pattern,
1411                        future_expr,
1412                        body,
1413                    });
1414                    if self.check(&Token::Comma) {
1415                        self.advance();
1416                    }
1417                }
1418                self.eat(&Token::RBrace)?;
1419                Ok(Expr {
1420                    kind: ExprKind::SelectExpr(branches),
1421                    span,
1422                })
1423            }
1424            Token::LBrace => {
1425                self.advance();
1426                let stmts = self.parse_block_stmts()?;
1427                self.eat(&Token::RBrace)?;
1428                Ok(Expr {
1429                    kind: ExprKind::Block(stmts),
1430                    span,
1431                })
1432            }
1433            Token::Ident(name) => {
1434                self.advance();
1435                // Check for :: (enum variant or module path)
1436                if self.check(&Token::ColonColon) {
1437                    self.advance();
1438                    let second = self.eat_ident()?;
1439                    // If more :: segments follow, it's a module path: a::b::c::...
1440                    if self.check(&Token::ColonColon) {
1441                        let mut segments = vec![name, second];
1442                        while self.check(&Token::ColonColon) {
1443                            self.advance();
1444                            segments.push(self.eat_ident()?);
1445                        }
1446                        Ok(Expr {
1447                            kind: ExprKind::ModulePath(segments),
1448                            span,
1449                        })
1450                    }
1451                    // Two segments: could be enum variant or module path
1452                    // Enum variants have uppercase first char (e.g. Color::Red)
1453                    else if name.chars().next().is_some_and(|c| c.is_uppercase()) {
1454                        // Enum variant — check for call args
1455                        if self.check(&Token::LParen) {
1456                            self.advance();
1457                            let mut args = Vec::new();
1458                            while !self.check(&Token::RParen) && !self.is_at_end() {
1459                                args.push(self.parse_expr()?);
1460                                if !self.check(&Token::RParen) {
1461                                    self.eat(&Token::Comma)?;
1462                                }
1463                            }
1464                            self.eat(&Token::RParen)?;
1465                            Ok(Expr {
1466                                kind: ExprKind::EnumVariantCall {
1467                                    enum_name: name,
1468                                    variant: second,
1469                                    args,
1470                                },
1471                                span,
1472                            })
1473                        } else {
1474                            Ok(Expr {
1475                                kind: ExprKind::EnumVariant {
1476                                    enum_name: name,
1477                                    variant: second,
1478                                },
1479                                span,
1480                            })
1481                        }
1482                    }
1483                    // Two segments, lowercase first: module path (e.g. fs::read)
1484                    else {
1485                        Ok(Expr {
1486                            kind: ExprKind::ModulePath(vec![name, second]),
1487                            span,
1488                        })
1489                    }
1490                }
1491                // Check for struct construction: Name { ... }
1492                else if self.check(&Token::LBrace)
1493                    && name.chars().next().is_some_and(|c| c.is_uppercase())
1494                {
1495                    self.advance();
1496                    let mut fields = Vec::new();
1497                    let mut spread = None;
1498                    while !self.check(&Token::RBrace) && !self.is_at_end() {
1499                        if self.check(&Token::DotDotDot) {
1500                            self.advance();
1501                            spread = Some(Box::new(self.parse_expr()?));
1502                            if !self.check(&Token::RBrace) {
1503                                self.eat(&Token::Comma)?;
1504                            }
1505                            continue;
1506                        }
1507                        let field_name = self.eat_ident()?;
1508                        self.eat(&Token::Colon)?;
1509                        let field_value = self.parse_expr()?;
1510                        fields.push((field_name, field_value));
1511                        if !self.check(&Token::RBrace) {
1512                            self.eat(&Token::Comma)?;
1513                        }
1514                    }
1515                    self.eat(&Token::RBrace)?;
1516                    Ok(Expr {
1517                        kind: ExprKind::StructConstruct {
1518                            name,
1519                            fields,
1520                            spread,
1521                        },
1522                        span,
1523                    })
1524                } else {
1525                    Ok(Expr {
1526                        kind: ExprKind::Ident(name),
1527                        span,
1528                    })
1529                }
1530            }
1531            _ => {
1532                let s = self.span();
1533                Err(IonError::parse(
1534                    format!("{}{:?}", ion_str!("unexpected token: "), self.peek()),
1535                    s.line,
1536                    s.col,
1537                ))
1538            }
1539        }
1540    }
1541
1542    /// Parse dict entries when the first token is `...` (spread).
1543    /// Parse a dict key: if it's an identifier followed by `:`, treat as string literal.
1544    fn parse_dict_key(&mut self) -> Result<Expr, IonError> {
1545        let span = self.span();
1546        if let Token::Ident(name) = self.peek().clone() {
1547            // Lookahead: if next token is `:`, this is a shorthand key
1548            if self.tokens.get(self.pos + 1).map(|t| &t.token) == Some(&Token::Colon) {
1549                self.advance(); // consume the identifier
1550                return Ok(Expr {
1551                    kind: ExprKind::Str(name),
1552                    span,
1553                });
1554            }
1555        }
1556        self.parse_expr()
1557    }
1558
1559    fn parse_dict_entries(&mut self, span: Span) -> Result<Expr, IonError> {
1560        let mut entries = Vec::new();
1561        // First entry is a spread
1562        self.advance(); // consume `...`
1563        entries.push(DictEntry::Spread(self.parse_expr()?));
1564        while self.check(&Token::Comma) {
1565            self.advance();
1566            if self.check(&Token::RBrace) {
1567                break;
1568            }
1569            if self.check(&Token::DotDotDot) {
1570                self.advance();
1571                entries.push(DictEntry::Spread(self.parse_expr()?));
1572            } else {
1573                let key = self.parse_dict_key()?;
1574                self.eat(&Token::Colon)?;
1575                let value = self.parse_expr()?;
1576                entries.push(DictEntry::KeyValue(key, value));
1577            }
1578        }
1579        self.eat(&Token::RBrace)?;
1580        Ok(Expr {
1581            kind: ExprKind::Dict(entries),
1582            span,
1583        })
1584    }
1585
1586    fn parse_if_expr(&mut self) -> Result<Expr, IonError> {
1587        let span = self.span();
1588        self.eat(&Token::If)?;
1589
1590        // if let pattern = expr { ... }
1591        if self.check(&Token::Let) {
1592            self.advance();
1593            let pattern = self.parse_pattern()?;
1594            self.eat(&Token::Eq)?;
1595            let expr = self.parse_expr()?;
1596            self.eat(&Token::LBrace)?;
1597            let then_body = self.parse_block_stmts()?;
1598            self.eat(&Token::RBrace)?;
1599            let else_body = if self.check(&Token::Else) {
1600                self.advance();
1601                self.eat(&Token::LBrace)?;
1602                let stmts = self.parse_block_stmts()?;
1603                self.eat(&Token::RBrace)?;
1604                Some(stmts)
1605            } else {
1606                None
1607            };
1608            return Ok(Expr {
1609                kind: ExprKind::IfLet {
1610                    pattern,
1611                    expr: Box::new(expr),
1612                    then_body,
1613                    else_body,
1614                },
1615                span,
1616            });
1617        }
1618
1619        let cond = self.parse_expr()?;
1620        self.eat(&Token::LBrace)?;
1621        let then_body = self.parse_block_stmts()?;
1622        self.eat(&Token::RBrace)?;
1623        let else_body = if self.check(&Token::Else) {
1624            self.advance();
1625            if self.check(&Token::If) {
1626                // else if
1627                let else_if = self.parse_if_expr()?;
1628                Some(vec![Stmt {
1629                    kind: StmtKind::ExprStmt {
1630                        expr: else_if,
1631                        has_semi: false,
1632                    },
1633                    span: self.prev_span(),
1634                }])
1635            } else {
1636                self.eat(&Token::LBrace)?;
1637                let stmts = self.parse_block_stmts()?;
1638                self.eat(&Token::RBrace)?;
1639                Some(stmts)
1640            }
1641        } else {
1642            None
1643        };
1644        Ok(Expr {
1645            kind: ExprKind::If {
1646                cond: Box::new(cond),
1647                then_body,
1648                else_body,
1649            },
1650            span,
1651        })
1652    }
1653
1654    fn parse_match_expr(&mut self) -> Result<Expr, IonError> {
1655        let span = self.span();
1656        self.eat(&Token::Match)?;
1657        let expr = self.parse_expr()?;
1658        self.eat(&Token::LBrace)?;
1659        let mut arms = Vec::new();
1660        while !self.check(&Token::RBrace) && !self.is_at_end() {
1661            let pattern = self.parse_pattern()?;
1662            let guard = if self.check(&Token::If) {
1663                self.advance();
1664                Some(self.parse_expr()?)
1665            } else {
1666                None
1667            };
1668            self.eat(&Token::Arrow)?;
1669            let body = self.parse_expr()?;
1670            arms.push(MatchArm {
1671                pattern,
1672                guard,
1673                body,
1674            });
1675            if !self.check(&Token::RBrace) {
1676                self.eat(&Token::Comma)?;
1677            }
1678        }
1679        self.eat(&Token::RBrace)?;
1680        Ok(Expr {
1681            kind: ExprKind::Match {
1682                expr: Box::new(expr),
1683                arms,
1684            },
1685            span,
1686        })
1687    }
1688
1689    // --- Pattern Parsing ---
1690
1691    fn parse_pattern(&mut self) -> Result<Pattern, IonError> {
1692        match self.peek().clone() {
1693            Token::Ident(name) if name == "_" => {
1694                self.advance();
1695                Ok(Pattern::Wildcard)
1696            }
1697            Token::Ident(name) => {
1698                self.advance();
1699                // Check for :: (enum variant)
1700                if self.check(&Token::ColonColon) {
1701                    self.advance();
1702                    let variant = self.eat_ident()?;
1703                    let fields = if self.check(&Token::LParen) {
1704                        self.advance();
1705                        let mut pats = Vec::new();
1706                        while !self.check(&Token::RParen) && !self.is_at_end() {
1707                            pats.push(self.parse_pattern()?);
1708                            if !self.check(&Token::RParen) {
1709                                self.eat(&Token::Comma)?;
1710                            }
1711                        }
1712                        self.eat(&Token::RParen)?;
1713                        EnumPatternFields::Positional(pats)
1714                    } else if self.check(&Token::LBrace) {
1715                        self.advance();
1716                        let mut fields = Vec::new();
1717                        while !self.check(&Token::RBrace) && !self.is_at_end() {
1718                            let field_name = self.eat_ident()?;
1719                            let pat = if self.check(&Token::Colon) {
1720                                self.advance();
1721                                Some(self.parse_pattern()?)
1722                            } else {
1723                                None
1724                            };
1725                            fields.push((field_name, pat));
1726                            if !self.check(&Token::RBrace) {
1727                                self.eat(&Token::Comma)?;
1728                            }
1729                        }
1730                        self.eat(&Token::RBrace)?;
1731                        EnumPatternFields::Named(fields)
1732                    } else {
1733                        EnumPatternFields::None
1734                    };
1735                    Ok(Pattern::EnumVariant {
1736                        enum_name: name,
1737                        variant,
1738                        fields,
1739                    })
1740                }
1741                // Check for struct pattern: Name { ... }
1742                else if self.check(&Token::LBrace)
1743                    && name.chars().next().is_some_and(|c| c.is_uppercase())
1744                {
1745                    self.advance();
1746                    let mut fields = Vec::new();
1747                    while !self.check(&Token::RBrace) && !self.is_at_end() {
1748                        let field_name = self.eat_ident()?;
1749                        let pat = if self.check(&Token::Colon) {
1750                            self.advance();
1751                            Some(self.parse_pattern()?)
1752                        } else {
1753                            None
1754                        };
1755                        fields.push((field_name, pat));
1756                        if !self.check(&Token::RBrace) {
1757                            self.eat(&Token::Comma)?;
1758                        }
1759                    }
1760                    self.eat(&Token::RBrace)?;
1761                    Ok(Pattern::Struct { name, fields })
1762                } else {
1763                    Ok(Pattern::Ident(name))
1764                }
1765            }
1766            Token::Int(n) => {
1767                self.advance();
1768                Ok(Pattern::Int(n))
1769            }
1770            Token::Float(n) => {
1771                self.advance();
1772                Ok(Pattern::Float(n))
1773            }
1774            Token::True => {
1775                self.advance();
1776                Ok(Pattern::Bool(true))
1777            }
1778            Token::False => {
1779                self.advance();
1780                Ok(Pattern::Bool(false))
1781            }
1782            Token::Str(s) => {
1783                self.advance();
1784                Ok(Pattern::Str(s))
1785            }
1786            Token::Bytes(b) => {
1787                self.advance();
1788                Ok(Pattern::Bytes(b))
1789            }
1790            Token::None => {
1791                self.advance();
1792                Ok(Pattern::None)
1793            }
1794            Token::Some => {
1795                self.advance();
1796                self.eat(&Token::LParen)?;
1797                let inner = self.parse_pattern()?;
1798                self.eat(&Token::RParen)?;
1799                Ok(Pattern::Some(Box::new(inner)))
1800            }
1801            Token::Ok => {
1802                self.advance();
1803                self.eat(&Token::LParen)?;
1804                let inner = self.parse_pattern()?;
1805                self.eat(&Token::RParen)?;
1806                Ok(Pattern::Ok(Box::new(inner)))
1807            }
1808            Token::Err => {
1809                self.advance();
1810                self.eat(&Token::LParen)?;
1811                let inner = self.parse_pattern()?;
1812                self.eat(&Token::RParen)?;
1813                Ok(Pattern::Err(Box::new(inner)))
1814            }
1815            Token::LParen => {
1816                self.advance();
1817                let mut pats = Vec::new();
1818                while !self.check(&Token::RParen) && !self.is_at_end() {
1819                    pats.push(self.parse_pattern()?);
1820                    if !self.check(&Token::RParen) {
1821                        self.eat(&Token::Comma)?;
1822                    }
1823                }
1824                self.eat(&Token::RParen)?;
1825                Ok(Pattern::Tuple(pats))
1826            }
1827            Token::LBracket => {
1828                self.advance();
1829                let mut pats = Vec::new();
1830                let mut rest = None;
1831                while !self.check(&Token::RBracket) && !self.is_at_end() {
1832                    if self.check(&Token::DotDotDot) {
1833                        self.advance();
1834                        let rest_name = self.eat_ident()?;
1835                        rest = Some(Box::new(Pattern::Ident(rest_name)));
1836                        if !self.check(&Token::RBracket) {
1837                            self.eat(&Token::Comma)?;
1838                        }
1839                        continue;
1840                    }
1841                    pats.push(self.parse_pattern()?);
1842                    if !self.check(&Token::RBracket) {
1843                        self.eat(&Token::Comma)?;
1844                    }
1845                }
1846                self.eat(&Token::RBracket)?;
1847                Ok(Pattern::List(pats, rest))
1848            }
1849            _ => {
1850                let s = self.span();
1851                Err(IonError::parse(
1852                    format!(
1853                        "{}{:?}",
1854                        ion_str!("unexpected token in pattern: "),
1855                        self.peek()
1856                    ),
1857                    s.line,
1858                    s.col,
1859                ))
1860            }
1861        }
1862    }
1863
1864    // --- F-string parsing ---
1865
1866    fn parse_fstr_parts(&self, template: &str, span: Span) -> Result<Vec<FStrPart>, IonError> {
1867        let mut parts = Vec::new();
1868        let mut chars = template.chars().peekable();
1869        let mut current = String::new();
1870
1871        while let Some(ch) = chars.next() {
1872            if ch == '{' {
1873                if !current.is_empty() {
1874                    parts.push(FStrPart::Literal(std::mem::take(&mut current)));
1875                }
1876                let mut expr_str = String::new();
1877                let mut depth = 1;
1878                for inner in chars.by_ref() {
1879                    if inner == '{' {
1880                        depth += 1;
1881                    } else if inner == '}' {
1882                        depth -= 1;
1883                        if depth == 0 {
1884                            break;
1885                        }
1886                    }
1887                    expr_str.push(inner);
1888                }
1889                if depth != 0 {
1890                    return Err(IonError::parse(
1891                        ion_str!("unterminated expression in f-string").to_string(),
1892                        span.line,
1893                        span.col,
1894                    ));
1895                }
1896                let mut lexer = crate::lexer::Lexer::new(&expr_str);
1897                let tokens = lexer.tokenize()?;
1898                let mut parser = Parser::new(tokens);
1899                let expr = parser.parse_expr()?;
1900                if !parser.is_at_end() {
1901                    let s = parser.span();
1902                    return Err(IonError::parse(
1903                        format!(
1904                            "{}{:?}",
1905                            ion_str!("unexpected token in f-string expression: "),
1906                            parser.peek()
1907                        ),
1908                        span.line,
1909                        span.col + s.col.saturating_sub(1),
1910                    ));
1911                }
1912                parts.push(FStrPart::Expr(expr));
1913            } else {
1914                current.push(ch);
1915            }
1916        }
1917        if !current.is_empty() {
1918            parts.push(FStrPart::Literal(current));
1919        }
1920        Ok(parts)
1921    }
1922}
1923
1924#[cfg(test)]
1925mod tests {
1926    use super::*;
1927    use crate::lexer::Lexer;
1928
1929    fn parse(src: &str) -> Program {
1930        let tokens = Lexer::new(src).tokenize().unwrap();
1931        Parser::new(tokens).parse_program().unwrap()
1932    }
1933
1934    #[test]
1935    fn test_let_stmt() {
1936        let prog = parse("let x = 42;");
1937        assert_eq!(prog.stmts.len(), 1);
1938        assert!(matches!(
1939            &prog.stmts[0].kind,
1940            StmtKind::Let { mutable: false, .. }
1941        ));
1942    }
1943
1944    #[test]
1945    fn test_let_mut() {
1946        let prog = parse("let mut x = 42;");
1947        assert!(matches!(
1948            &prog.stmts[0].kind,
1949            StmtKind::Let { mutable: true, .. }
1950        ));
1951    }
1952
1953    #[test]
1954    fn test_fn_decl() {
1955        let prog = parse("fn add(a, b) { a + b }");
1956        assert!(matches!(&prog.stmts[0].kind, StmtKind::FnDecl { .. }));
1957    }
1958
1959    #[test]
1960    fn test_if_expr() {
1961        let prog = parse("let x = if true { 1 } else { 2 };");
1962        assert!(matches!(&prog.stmts[0].kind, StmtKind::Let { .. }));
1963    }
1964
1965    #[test]
1966    fn test_match_expr() {
1967        let prog = parse(r#"let x = match y { 1 => "one", _ => "other" };"#);
1968        assert!(matches!(&prog.stmts[0].kind, StmtKind::Let { .. }));
1969    }
1970
1971    #[test]
1972    fn test_lambda() {
1973        let prog = parse("let f = |x| x + 1;");
1974        assert!(matches!(&prog.stmts[0].kind, StmtKind::Let { .. }));
1975    }
1976
1977    #[test]
1978    fn test_dict() {
1979        let prog = parse(r#"let d = #{ "a": 1, "b": 2 };"#);
1980        assert!(matches!(&prog.stmts[0].kind, StmtKind::Let { .. }));
1981    }
1982
1983    #[test]
1984    fn test_for_loop() {
1985        let prog = parse("for x in items { x; }");
1986        assert!(matches!(&prog.stmts[0].kind, StmtKind::For { .. }));
1987    }
1988}