Skip to main content

bhc_parser/
expr.rs

1//! Expression parsing.
2
3use bhc_ast::{Alt, ArithSeq, Expr, FieldBind, Guard, GuardedRhs, Lit, ModuleName, Pat, Rhs, Stmt};
4use bhc_intern::Ident;
5use bhc_lexer::TokenKind;
6use bhc_span::Span;
7
8use crate::{ParseError, ParseResult, Parser};
9
10impl<'src> Parser<'src> {
11    /// Parse an expression, including optional type annotation.
12    pub fn parse_expr(&mut self) -> ParseResult<Expr> {
13        self.enter_recursion()?;
14        let result = self.parse_expr_guarded();
15        self.exit_recursion();
16        result
17    }
18
19    fn parse_expr_guarded(&mut self) -> ParseResult<Expr> {
20        let expr = self.parse_infix_expr(0)?;
21
22        // Check for type annotation: expr :: Type
23        if self.check(&TokenKind::DoubleColon) {
24            self.parse_type_annotation(expr)
25        } else {
26            Ok(expr)
27        }
28    }
29
30    /// Parse an infix expression with precedence climbing.
31    fn parse_infix_expr(&mut self, min_prec: u8) -> ParseResult<Expr> {
32        let lhs = self.parse_prefix_expr()?;
33        self.continue_infix_expr(lhs, min_prec)
34    }
35
36    /// Continue parsing an infix expression from a pre-parsed LHS.
37    fn continue_infix_expr(&mut self, mut lhs: Expr, min_prec: u8) -> ParseResult<Expr> {
38        while let Some(tok) = self.current() {
39            // Track whether the operator was already consumed (for backtick case)
40            let mut op_consumed = false;
41
42            let (op, prec, assoc) = match &tok.node.kind {
43                TokenKind::Operator(sym) => {
44                    let (prec, assoc) = self.get_operator_info(sym.as_str());
45                    if prec < min_prec {
46                        break;
47                    }
48                    (Ident::new(*sym), prec, assoc)
49                }
50                // Handle * (Star token is lexed separately for kind syntax)
51                TokenKind::Star => {
52                    let (prec, assoc) = self.get_operator_info("*");
53                    if prec < min_prec {
54                        break;
55                    }
56                    (Ident::from_str("*"), prec, assoc)
57                }
58                // Handle - (Minus token is lexed separately for prefix negation)
59                TokenKind::Minus => {
60                    let (prec, assoc) = self.get_operator_info("-");
61                    if prec < min_prec {
62                        break;
63                    }
64                    (Ident::from_str("-"), prec, assoc)
65                }
66                // Handle % (Percent token is lexed separately)
67                TokenKind::Percent => {
68                    let (prec, assoc) = self.get_operator_info("%");
69                    if prec < min_prec {
70                        break;
71                    }
72                    (Ident::from_str("%"), prec, assoc)
73                }
74                TokenKind::Backtick => {
75                    // Infix function application: `x `mod` y` or `x `E.catch` y`.
76                    // Backtick-quoted identifiers have default precedence 9, left-assoc.
77                    // Bail BEFORE consuming any tokens if the precedence is too low —
78                    // otherwise we would strand the operator and its trailing operand.
79                    if 9 < min_prec {
80                        break;
81                    }
82                    self.advance(); // `
83                    let Some(func_tok) = self.current() else {
84                        return Err(ParseError::UnexpectedEof {
85                            expected: "identifier".to_string(),
86                        });
87                    };
88                    let func = match &func_tok.node.kind {
89                        TokenKind::Ident(sym) => Ident::new(*sym),
90                        TokenKind::ConId(sym) => Ident::new(*sym),
91                        TokenKind::QualIdent(qual, name) => {
92                            // Create qualified name like "E.catch"
93                            let full_name = format!("{}.{}", qual.as_str(), name.as_str());
94                            Ident::from_str(&full_name)
95                        }
96                        _ => {
97                            return Err(ParseError::Unexpected {
98                                found: func_tok.node.kind.description().to_string(),
99                                expected: "identifier".to_string(),
100                                span: func_tok.span,
101                            });
102                        }
103                    };
104                    self.advance();
105                    self.expect(&TokenKind::Backtick)?;
106                    op_consumed = true; // Already consumed the entire `func` operator
107                    (func, 9, Assoc::Left) // Default infix precedence
108                }
109                // Handle . (Dot token for function composition)
110                TokenKind::Dot => {
111                    let (prec, assoc) = self.get_operator_info(".");
112                    if prec < min_prec {
113                        break;
114                    }
115                    (Ident::from_str("."), prec, assoc)
116                }
117                // Handle constructor operators like : and :|
118                TokenKind::ConOperator(sym) => {
119                    let (prec, assoc) = self.get_operator_info(sym.as_str());
120                    if prec < min_prec {
121                        break;
122                    }
123                    (Ident::new(*sym), prec, assoc)
124                }
125                // Handle qualified operators like Seq.|> or M.\\
126                TokenKind::QualOperator(qual, sym) => {
127                    let (prec, assoc) = self.get_operator_info(sym.as_str());
128                    if prec < min_prec {
129                        break;
130                    }
131                    let full_name = format!("{}.{}", qual.as_str(), sym.as_str());
132                    (Ident::from_str(&full_name), prec, assoc)
133                }
134                // Handle qualified constructor operators like Seq.:> in expressions
135                TokenKind::QualConOperator(qual, sym) => {
136                    let (prec, assoc) = self.get_operator_info(sym.as_str());
137                    if prec < min_prec {
138                        break;
139                    }
140                    let full_name = format!("{}.{}", qual.as_str(), sym.as_str());
141                    (Ident::from_str(&full_name), prec, assoc)
142                }
143                // Single-char tokens that can also appear as binary operators.
144                // The lexer emits them as standalone tokens because they have
145                // other meanings (bang patterns, implicit params, magic hash).
146                // In infix position they are operators.
147                TokenKind::Bang => {
148                    let (prec, assoc) = self.get_operator_info("!");
149                    if prec < min_prec {
150                        break;
151                    }
152                    (Ident::from_str("!"), prec, assoc)
153                }
154                TokenKind::Question => {
155                    let (prec, assoc) = self.get_operator_info("?");
156                    if prec < min_prec {
157                        break;
158                    }
159                    (Ident::from_str("?"), prec, assoc)
160                }
161                TokenKind::Hash => {
162                    let (prec, assoc) = self.get_operator_info("#");
163                    if prec < min_prec {
164                        break;
165                    }
166                    (Ident::from_str("#"), prec, assoc)
167                }
168                _ => break,
169            };
170
171            if prec < min_prec {
172                break;
173            }
174
175            let _op_span = self.current_span();
176            if !op_consumed {
177                self.advance();
178            }
179
180            let next_min_prec = match assoc {
181                Assoc::Left => prec + 1,
182                Assoc::Right => prec,
183                Assoc::None => prec + 1,
184            };
185
186            let rhs = self.parse_infix_expr(next_min_prec)?;
187            let span = lhs.span().merge(rhs.span());
188            lhs = Expr::Infix(Box::new(lhs), op, Box::new(rhs), span);
189        }
190
191        Ok(lhs)
192    }
193
194    /// Parse a prefix expression (negation, etc.).
195    fn parse_prefix_expr(&mut self) -> ParseResult<Expr> {
196        // This is the knot every nested expression form recurses through
197        // (paren exprs re-enter here without passing parse_expr), so the
198        // depth guard lives here as well
199        self.enter_recursion()?;
200        let result = self.parse_prefix_expr_guarded();
201        self.exit_recursion();
202        result
203    }
204
205    fn parse_prefix_expr_guarded(&mut self) -> ParseResult<Expr> {
206        if let Some(tok) = self.current() {
207            // Handle prefix negation - lexer produces TokenKind::Minus for standalone `-`
208            let is_minus = matches!(&tok.node.kind, TokenKind::Minus)
209                || matches!(&tok.node.kind, TokenKind::Operator(s) if s.as_str() == "-");
210            if is_minus {
211                let start = tok.span;
212                self.advance();
213                let expr = self.parse_prefix_expr()?;
214                let span = start.to(expr.span());
215                return Ok(Expr::Neg(Box::new(expr), span));
216            }
217        }
218
219        self.parse_app_expr()
220    }
221
222    /// Parse an application expression.
223    fn parse_app_expr(&mut self) -> ParseResult<Expr> {
224        let mut expr = self.parse_atom_expr()?;
225
226        loop {
227            // Check for record update: `expr { field = value }`
228            if self.check(&TokenKind::LBrace) && !matches!(expr, Expr::Con(_, _)) {
229                let start = expr.span();
230                expr = self.parse_record_update(expr, start)?;
231                continue;
232            }
233
234            // Check for type application: `expr @Type`
235            if self.check(&TokenKind::At) {
236                self.advance(); // consume @
237                let ty = self.parse_atype()?;
238                let span = expr.span().to(ty.span());
239                expr = Expr::TypeApp(Box::new(expr), ty, span);
240                continue;
241            }
242
243            // Check if this looks like an argument
244            if let Some(tok) = self.current() {
245                if self.is_atom_start(&tok.node.kind) {
246                    let arg = self.parse_atom_expr()?;
247                    let span = expr.span().to(arg.span());
248                    expr = Expr::App(Box::new(expr), Box::new(arg), span);
249                    continue;
250                }
251            }
252
253            break;
254        }
255
256        Ok(expr)
257    }
258
259    /// Check if a token can start an atom expression.
260    fn is_atom_start(&self, kind: &TokenKind) -> bool {
261        matches!(
262            kind,
263            TokenKind::Ident(_)
264                | TokenKind::QualIdent(_, _)
265                | TokenKind::ConId(_)
266                | TokenKind::QualConId(_, _)
267                | TokenKind::IntLit(_)
268                | TokenKind::FloatLit(_)
269                | TokenKind::CharLit(_)
270                | TokenKind::StringLit(_)
271                | TokenKind::LParen
272                | TokenKind::LBracket
273                | TokenKind::Backslash
274                | TokenKind::Let
275                | TokenKind::If
276                | TokenKind::Case
277                | TokenKind::Do
278                | TokenKind::Underscore // For holes/wildcards in patterns that are parsed as expressions first
279        )
280    }
281
282    /// Parse an atomic expression.
283    fn parse_atom_expr(&mut self) -> ParseResult<Expr> {
284        let tok = self.current().ok_or(ParseError::UnexpectedEof {
285            expected: "expression".to_string(),
286        })?;
287
288        match &tok.node.kind.clone() {
289            TokenKind::Ident(sym)
290                if sym.as_str() == "lazy"
291                    && self.pos + 1 < self.tokens.len()
292                    && self.tokens[self.pos + 1].node.kind == TokenKind::LBrace =>
293            {
294                self.parse_lazy_expr()
295            }
296            TokenKind::Ident(sym) => {
297                let ident = Ident::new(*sym);
298                let span = tok.span;
299                self.advance();
300                Ok(Expr::Var(ident, span))
301            }
302
303            TokenKind::QualIdent(qualifier, name) => {
304                let module_name = ModuleName {
305                    parts: vec![*qualifier],
306                    span: tok.span,
307                };
308                let ident = Ident::new(*name);
309                let span = tok.span;
310                self.advance();
311                Ok(Expr::QualVar(module_name, ident, span))
312            }
313
314            TokenKind::ConId(sym) => {
315                let ident = Ident::new(*sym);
316                let span = tok.span;
317                self.advance();
318
319                // Check for record construction: Con { field = value }
320                if self.check(&TokenKind::LBrace) {
321                    return self.parse_record_con(ident, span);
322                }
323
324                Ok(Expr::Con(ident, span))
325            }
326
327            TokenKind::QualConId(qualifier, name) => {
328                let module_name = ModuleName {
329                    parts: vec![*qualifier],
330                    span: tok.span,
331                };
332                let ident = Ident::new(*name);
333                let span = tok.span;
334                self.advance();
335
336                // Check for record construction: M.Con { field = value }
337                if self.check(&TokenKind::LBrace) {
338                    return self.parse_qual_record_con(module_name, ident, span);
339                }
340
341                Ok(Expr::QualCon(module_name, ident, span))
342            }
343
344            TokenKind::IntLit(ref lit) => {
345                let span = tok.span;
346                let value = self.parse_int_literal(&lit.text, span)?;
347                self.advance();
348                Ok(Expr::Lit(Lit::Int(value), span))
349            }
350
351            TokenKind::FloatLit(ref lit) => {
352                let span = tok.span;
353                let value = self.parse_float_literal(&lit.text, span)?;
354                self.advance();
355                Ok(Expr::Lit(Lit::Float(value), span))
356            }
357
358            TokenKind::CharLit(c) => {
359                let span = tok.span;
360                let c = *c;
361                self.advance();
362                Ok(Expr::Lit(Lit::Char(c), span))
363            }
364
365            TokenKind::StringLit(s) => {
366                let span = tok.span;
367                let s = s.clone();
368                self.advance();
369                Ok(Expr::Lit(Lit::String(s), span))
370            }
371
372            TokenKind::LParen => self.parse_paren_expr(),
373
374            TokenKind::LBracket => self.parse_list_expr(),
375
376            TokenKind::Backslash => self.parse_lambda(),
377
378            TokenKind::Let => self.parse_let_expr(),
379
380            TokenKind::If => self.parse_if_expr(),
381
382            TokenKind::Case => self.parse_case_expr(),
383
384            TokenKind::Do => self.parse_do_expr(),
385
386            TokenKind::Underscore => {
387                // Wildcard/hole - used in patterns that are parsed as expressions first
388                let span = tok.span;
389                self.advance();
390                Ok(Expr::Wildcard(span))
391            }
392
393            _ => Err(ParseError::Unexpected {
394                found: tok.node.kind.description().to_string(),
395                expected: "expression".to_string(),
396                span: tok.span,
397            }),
398        }
399    }
400
401    /// Parse an integer literal.
402    pub(crate) fn parse_int_literal(&self, s: &str, span: Span) -> ParseResult<i64> {
403        let s = s.replace('_', "");
404        let value = if s.starts_with("0x") || s.starts_with("0X") {
405            i64::from_str_radix(&s[2..], 16)
406        } else if s.starts_with("0o") || s.starts_with("0O") {
407            i64::from_str_radix(&s[2..], 8)
408        } else if s.starts_with("0b") || s.starts_with("0B") {
409            i64::from_str_radix(&s[2..], 2)
410        } else {
411            s.parse()
412        };
413
414        value.map_err(|e| ParseError::InvalidLiteral {
415            message: e.to_string(),
416            span,
417        })
418    }
419
420    /// Parse a float literal.
421    pub(crate) fn parse_float_literal(&self, s: &str, span: Span) -> ParseResult<f64> {
422        let s = s.replace('_', "");
423        s.parse()
424            .map_err(|e: std::num::ParseFloatError| ParseError::InvalidLiteral {
425                message: e.to_string(),
426                span,
427            })
428    }
429
430    /// Get operator precedence and associativity.
431    fn get_operator_info(&self, op: &str) -> (u8, Assoc) {
432        // Default precedences (can be overridden by fixity declarations)
433        match op {
434            "." => (9, Assoc::Right),
435            "^" | "^^" | "**" => (8, Assoc::Right),
436            "*" | "/" | "`div`" | "`mod`" => (7, Assoc::Left),
437            "+" | "-" => (6, Assoc::Left),
438            "<>" => (6, Assoc::Right),
439            ":" | "++" => (5, Assoc::Right),
440            "==" | "/=" | "<" | "<=" | ">" | ">=" | "`elem`" | "`notElem`" => (4, Assoc::None),
441            "<$>" | "<*>" | "<*" | "*>" => (4, Assoc::Left),
442            "<$" => (4, Assoc::Left),
443            "<|>" => (3, Assoc::Left),
444            "&&" => (3, Assoc::Right),
445            "||" => (2, Assoc::Right),
446            ">>=" | ">>" => (1, Assoc::Left),
447            "=<<" => (1, Assoc::Right),
448            "$" | "$!" => (0, Assoc::Right),
449            _ => (9, Assoc::Left), // Default
450        }
451    }
452
453    /// Parse a parenthesized expression, tuple, or operator section.
454    fn parse_paren_expr(&mut self) -> ParseResult<Expr> {
455        let start = self.current_span();
456        self.expect(&TokenKind::LParen)?;
457
458        if self.eat(&TokenKind::RParen) {
459            // Unit: ()
460            let span = start.to(self.tokens[self.pos - 1].span);
461            return Ok(Expr::Con(Ident::from_str("()"), span));
462        }
463
464        // TupleSections: (,x) or (,x,y) or (,,) etc.
465        // Detect leading hole: `(` followed by `,`
466        if self.check(&TokenKind::Comma) {
467            return self.parse_tuple_section(start, vec![None]);
468        }
469
470        // Check for operator section starting with operator: `(+)` or `(+ x)` or `(.)` or `(:)`
471        // Note: `-` is NOT included here because `(-5)` should be negative 5, not a section.
472        // Only `(-)` (minus followed immediately by rparen) should be a section.
473        // We handle that case below.
474        if matches!(
475            self.current_kind(),
476            Some(TokenKind::Operator(_))
477                | Some(TokenKind::Star)
478                | Some(TokenKind::Percent)
479                | Some(TokenKind::Dot)
480                | Some(TokenKind::ConOperator(_))
481                | Some(TokenKind::QualOperator(_, _))
482                | Some(TokenKind::QualConOperator(_, _))
483        ) {
484            return self.parse_operator_section(start);
485        }
486
487        // Special case for `-`: only `(-)` is a section, otherwise it's negation
488        if matches!(self.current_kind(), Some(TokenKind::Minus)) {
489            // Peek at the next token to see if it's immediately followed by `)`
490            if self.pos + 1 < self.tokens.len()
491                && matches!(self.tokens[self.pos + 1].node.kind, TokenKind::RParen)
492            {
493                // `(-)` is the subtraction operator as a function
494                return self.parse_operator_section(start);
495            }
496            // Otherwise, fall through to parse as prefix negation
497        }
498
499        // Check for backtick right section: (`op` x) means \y -> y `op` x
500        if self.check(&TokenKind::Backtick) {
501            self.advance(); // consume opening backtick
502            let Some(func_tok) = self.current() else {
503                return Err(ParseError::UnexpectedEof {
504                    expected: "identifier".to_string(),
505                });
506            };
507            let func = match &func_tok.node.kind {
508                TokenKind::Ident(sym) => Ident::new(*sym),
509                TokenKind::ConId(sym) => Ident::new(*sym),
510                TokenKind::QualIdent(qual, name) => {
511                    let full_name = format!("{}.{}", qual.as_str(), name.as_str());
512                    Ident::from_str(&full_name)
513                }
514                _ => {
515                    return Err(ParseError::Unexpected {
516                        found: func_tok.node.kind.description().to_string(),
517                        expected: "identifier".to_string(),
518                        span: func_tok.span,
519                    });
520                }
521            };
522            self.advance();
523            self.expect(&TokenKind::Backtick)?; // closing backtick
524
525            // Parse the RHS of the section
526            let rhs = self.parse_app_expr()?;
527
528            let end = self.expect(&TokenKind::RParen)?;
529            let span = start.to(end.span);
530
531            // (`op` x) desugars to (\y -> y `op` x)
532            let y = Ident::from_str("$section_arg");
533            let y_pat = Pat::Var(y, Span::DUMMY);
534            let y_expr = Expr::Var(y, Span::DUMMY);
535            let body = Expr::Infix(Box::new(y_expr), func, Box::new(rhs), span);
536            return Ok(Expr::Lam(vec![y_pat], Box::new(body), span));
537        }
538
539        // For left sections like `(1 +)`, we need to parse without consuming operators
540        // Parse prefix expression first (handles negation like `(-5)`)
541        let first = self.parse_prefix_expr()?;
542
543        // Check for left operator section: `(x +)` or infix expression in parens
544        let op_ident = match self.current_kind().cloned() {
545            Some(TokenKind::Operator(sym)) => Some(Ident::new(sym)),
546            Some(TokenKind::Star) => Some(Ident::from_str("*")),
547            Some(TokenKind::Minus) => Some(Ident::from_str("-")),
548            Some(TokenKind::Percent) => Some(Ident::from_str("%")),
549            Some(TokenKind::Dot) => Some(Ident::from_str(".")),
550            Some(TokenKind::ConOperator(sym)) => Some(Ident::new(sym)),
551            Some(TokenKind::QualOperator(qual, sym)) => {
552                let full_name = format!("{}.{}", qual.as_str(), sym.as_str());
553                Some(Ident::from_str(&full_name))
554            }
555            Some(TokenKind::QualConOperator(qual, sym)) => {
556                let full_name = format!("{}.{}", qual.as_str(), sym.as_str());
557                Some(Ident::from_str(&full_name))
558            }
559            _ => None,
560        };
561
562        if let Some(op) = op_ident {
563            self.advance();
564
565            if self.eat(&TokenKind::RParen) {
566                // Left section: `(x +)` desugars to `(\y -> x + y)`
567                let span = start.to(self.tokens[self.pos - 1].span);
568                let y = Ident::from_str("$section_arg");
569                let y_pat = Pat::Var(y, Span::DUMMY);
570                let y_expr = Expr::Var(y, Span::DUMMY);
571                let body = Expr::Infix(Box::new(first), op, Box::new(y_expr), span);
572                return Ok(Expr::Lam(vec![y_pat], Box::new(body), span));
573            }
574
575            // Otherwise, this is an infix expression - could be in parens or part of a tuple
576            // Use proper precedence for the consumed operator so that lower-precedence
577            // operators don't get absorbed into the RHS.
578            let (op_prec, op_assoc) = self.get_operator_info(op.name.as_str());
579            let next_min_prec = match op_assoc {
580                Assoc::Left => op_prec + 1,
581                Assoc::Right => op_prec,
582                Assoc::None => op_prec + 1,
583            };
584            let rhs = self.parse_infix_expr(next_min_prec)?;
585            let infix_span = first.span().merge(rhs.span());
586            let first_infix = Expr::Infix(Box::new(first), op, Box::new(rhs), infix_span);
587            // Continue parsing any remaining infix operators (e.g., `1 % 3 + 1 % 6`)
588            let infix_expr = self.continue_infix_expr(first_infix, 0)?;
589
590            // Check for type annotation: (expr $ expr :: Type)
591            let infix_expr = if self.check(&TokenKind::DoubleColon) {
592                self.parse_type_annotation(infix_expr)?
593            } else {
594                infix_expr
595            };
596
597            // Check if this is a tuple: (a .|. b, c, ...)
598            if self.eat(&TokenKind::Comma) {
599                let mut exprs = vec![infix_expr];
600                loop {
601                    exprs.push(self.parse_expr()?);
602                    if !self.eat(&TokenKind::Comma) {
603                        break;
604                    }
605                }
606                let end = self.expect(&TokenKind::RParen)?;
607                let span = start.to(end.span);
608                return Ok(Expr::Tuple(exprs, span));
609            }
610
611            let end = self.expect(&TokenKind::RParen)?;
612            let span = start.to(end.span);
613            return Ok(Expr::Paren(Box::new(infix_expr), span));
614        }
615
616        // Check for backtick infix: (x `elem` xs) or left section: (x `op`)
617        if self.check(&TokenKind::Backtick) {
618            // Parse the backtick infix expression
619            self.advance(); // consume first backtick
620            let Some(func_tok) = self.current() else {
621                return Err(ParseError::UnexpectedEof {
622                    expected: "identifier".to_string(),
623                });
624            };
625            let func = match &func_tok.node.kind {
626                TokenKind::Ident(sym) => Ident::new(*sym),
627                TokenKind::ConId(sym) => Ident::new(*sym),
628                TokenKind::QualIdent(qual, name) => {
629                    let full_name = format!("{}.{}", qual.as_str(), name.as_str());
630                    Ident::from_str(&full_name)
631                }
632                _ => {
633                    return Err(ParseError::Unexpected {
634                        found: func_tok.node.kind.description().to_string(),
635                        expected: "identifier".to_string(),
636                        span: func_tok.span,
637                    });
638                }
639            };
640            self.advance();
641            self.expect(&TokenKind::Backtick)?; // closing backtick
642
643            // Check for left section: (x `op`) means \y -> x `op` y
644            if self.eat(&TokenKind::RParen) {
645                let span = start.to(self.tokens[self.pos - 1].span);
646                let y = Ident::from_str("$section_arg");
647                let y_pat = Pat::Var(y, Span::DUMMY);
648                let y_expr = Expr::Var(y, Span::DUMMY);
649                let body = Expr::Infix(Box::new(first), func, Box::new(y_expr), span);
650                return Ok(Expr::Lam(vec![y_pat], Box::new(body), span));
651            }
652
653            // Parse RHS for full infix expression with proper precedence
654            let (bt_prec, bt_assoc) = self.get_operator_info(func.name.as_str());
655            let bt_next_min = match bt_assoc {
656                Assoc::Left => bt_prec + 1,
657                Assoc::Right => bt_prec,
658                Assoc::None => bt_prec + 1,
659            };
660            let rhs = self.parse_infix_expr(bt_next_min)?;
661            let infix_span = first.span().merge(rhs.span());
662            let first_infix = Expr::Infix(Box::new(first), func, Box::new(rhs), infix_span);
663            let infix_expr = self.continue_infix_expr(first_infix, 0)?;
664
665            // Check for type annotation: (expr `op` expr :: Type)
666            let infix_expr = if self.check(&TokenKind::DoubleColon) {
667                self.parse_type_annotation(infix_expr)?
668            } else {
669                infix_expr
670            };
671
672            // Check for tuple
673            if self.eat(&TokenKind::Comma) {
674                let mut exprs = vec![infix_expr];
675                loop {
676                    exprs.push(self.parse_expr()?);
677                    if !self.eat(&TokenKind::Comma) {
678                        break;
679                    }
680                }
681                let end = self.expect(&TokenKind::RParen)?;
682                let span = start.to(end.span);
683                return Ok(Expr::Tuple(exprs, span));
684            }
685
686            let end = self.expect(&TokenKind::RParen)?;
687            let span = start.to(end.span);
688            return Ok(Expr::Paren(Box::new(infix_expr), span));
689        }
690
691        // Check for type annotation: (expr :: Type)
692        if self.check(&TokenKind::DoubleColon) {
693            let annotated = self.parse_type_annotation(first)?;
694            // Check for tuple with type annotation: (expr :: Type, ...)
695            if self.eat(&TokenKind::Comma) {
696                let mut exprs = vec![annotated];
697                loop {
698                    exprs.push(self.parse_expr()?);
699                    if !self.eat(&TokenKind::Comma) {
700                        break;
701                    }
702                }
703                let end = self.expect(&TokenKind::RParen)?;
704                let span = start.to(end.span);
705                return Ok(Expr::Tuple(exprs, span));
706            }
707            let end = self.expect(&TokenKind::RParen)?;
708            let span = start.to(end.span);
709            return Ok(Expr::Paren(Box::new(annotated), span));
710        }
711
712        if self.eat(&TokenKind::Comma) {
713            // Check for TupleSections: (x,,z) or (x,) etc.
714            // If the next token is `,` or `)`, there's a hole after the comma.
715            if self.check(&TokenKind::Comma) || self.check(&TokenKind::RParen) {
716                // The comma we just ate separates first from a hole
717                return self.parse_tuple_section(start, vec![Some(first), None]);
718            }
719
720            // Regular tuple
721            let mut exprs = vec![first];
722            loop {
723                exprs.push(self.parse_expr()?);
724                if !self.eat(&TokenKind::Comma) {
725                    break;
726                }
727                // Check for hole after comma in middle of tuple
728                if self.check(&TokenKind::Comma) || self.check(&TokenKind::RParen) {
729                    // Switch to tuple section parser, adding None for the hole
730                    let mut elements: Vec<Option<Expr>> = exprs.into_iter().map(Some).collect();
731                    elements.push(None);
732                    return self.parse_tuple_section(start, elements);
733                }
734            }
735            let end = self.expect(&TokenKind::RParen)?;
736            let span = start.to(end.span);
737            Ok(Expr::Tuple(exprs, span))
738        } else {
739            // Parenthesized expression
740            let end = self.expect(&TokenKind::RParen)?;
741            let span = start.to(end.span);
742            Ok(Expr::Paren(Box::new(first), span))
743        }
744    }
745
746    /// Parse a tuple section (TupleSections extension).
747    ///
748    /// Called when a hole (missing expression) has been detected in a tuple.
749    /// `elements` contains the already-parsed elements (Some = present, None = hole).
750    /// Continues parsing remaining elements from the current position.
751    ///
752    /// Desugars `(,x)` to `\a -> (a, x)`, `(x,,z)` to `\b -> (x, b, z)`, etc.
753    fn parse_tuple_section(
754        &mut self,
755        start: Span,
756        mut elements: Vec<Option<Expr>>,
757    ) -> ParseResult<Expr> {
758        // Continue parsing remaining elements
759        loop {
760            if self.eat(&TokenKind::RParen) {
761                break;
762            }
763            if self.eat(&TokenKind::Comma) {
764                // Check if next is comma or rparen → hole
765                if self.check(&TokenKind::Comma) || self.check(&TokenKind::RParen) {
766                    elements.push(None);
767                } else {
768                    elements.push(Some(self.parse_expr()?));
769                }
770            } else {
771                // Should not happen — we expect commas between elements
772                elements.push(Some(self.parse_expr()?));
773                if !self.eat(&TokenKind::Comma) {
774                    self.expect(&TokenKind::RParen)?;
775                    break;
776                }
777                // After comma, check for hole
778                if self.check(&TokenKind::Comma) || self.check(&TokenKind::RParen) {
779                    elements.push(None);
780                }
781            }
782        }
783
784        let span = start.to(self.tokens[self.pos.saturating_sub(1)].span);
785
786        // Generate fresh parameter names for holes
787        let mut params = Vec::new();
788        let mut tuple_exprs = Vec::new();
789        let mut hole_idx = 0;
790
791        for elem in elements {
792            match elem {
793                Some(expr) => tuple_exprs.push(expr),
794                None => {
795                    let name = Ident::from_str(&format!("$tsec_{}", hole_idx));
796                    hole_idx += 1;
797                    params.push(Pat::Var(name, Span::DUMMY));
798                    tuple_exprs.push(Expr::Var(name, Span::DUMMY));
799                }
800            }
801        }
802
803        let tuple = Expr::Tuple(tuple_exprs, span);
804
805        if params.is_empty() {
806            // No holes — shouldn't happen but return the tuple
807            Ok(tuple)
808        } else {
809            Ok(Expr::Lam(params, Box::new(tuple), span))
810        }
811    }
812
813    /// Parse a list expression.
814    fn parse_list_expr(&mut self) -> ParseResult<Expr> {
815        let start = self.current_span();
816        self.expect(&TokenKind::LBracket)?;
817
818        if self.eat(&TokenKind::RBracket) {
819            // Empty list
820            let span = start.to(self.tokens[self.pos - 1].span);
821            return Ok(Expr::List(vec![], span));
822        }
823
824        let first = self.parse_expr()?;
825
826        // Check for list comprehension: [x | ...]
827        if self.eat(&TokenKind::Pipe) {
828            let stmts = self.parse_list_comp_stmts()?;
829            let end = self.expect(&TokenKind::RBracket)?;
830            let span = start.to(end.span);
831            return Ok(Expr::ListComp(Box::new(first), stmts, span));
832        }
833
834        // Check for arithmetic sequence: [1..] or [1..10] or [1,2..]
835        if self.eat(&TokenKind::DotDot) {
836            if self.eat(&TokenKind::RBracket) {
837                // [from..]
838                let span = start.to(self.tokens[self.pos - 1].span);
839                return Ok(Expr::ArithSeq(ArithSeq::From(Box::new(first)), span));
840            }
841            let to = self.parse_expr()?;
842            let end = self.expect(&TokenKind::RBracket)?;
843            let span = start.to(end.span);
844            return Ok(Expr::ArithSeq(
845                ArithSeq::FromTo(Box::new(first), Box::new(to)),
846                span,
847            ));
848        }
849
850        if self.eat(&TokenKind::Comma) {
851            // Could be tuple-like list or [from, then..]
852            let second = self.parse_expr()?;
853
854            if self.eat(&TokenKind::DotDot) {
855                if self.eat(&TokenKind::RBracket) {
856                    // [from, then..]
857                    let span = start.to(self.tokens[self.pos - 1].span);
858                    return Ok(Expr::ArithSeq(
859                        ArithSeq::FromThen(Box::new(first), Box::new(second)),
860                        span,
861                    ));
862                }
863                let to = self.parse_expr()?;
864                let end = self.expect(&TokenKind::RBracket)?;
865                let span = start.to(end.span);
866                return Ok(Expr::ArithSeq(
867                    ArithSeq::FromThenTo(Box::new(first), Box::new(second), Box::new(to)),
868                    span,
869                ));
870            }
871
872            // Regular list
873            let mut exprs = vec![first, second];
874            while self.eat(&TokenKind::Comma) {
875                exprs.push(self.parse_expr()?);
876            }
877            let end = self.expect(&TokenKind::RBracket)?;
878            let span = start.to(end.span);
879            return Ok(Expr::List(exprs, span));
880        }
881
882        // Single-element list
883        let end = self.expect(&TokenKind::RBracket)?;
884        let span = start.to(end.span);
885        Ok(Expr::List(vec![first], span))
886    }
887
888    /// Parse list comprehension statements.
889    fn parse_list_comp_stmts(&mut self) -> ParseResult<Vec<Stmt>> {
890        let mut stmts = vec![self.parse_stmt()?];
891        while self.eat(&TokenKind::Comma) {
892            stmts.push(self.parse_stmt()?);
893        }
894        Ok(stmts)
895    }
896
897    /// Parse a statement (for do blocks and list comprehensions).
898    fn parse_stmt(&mut self) -> ParseResult<Stmt> {
899        let start = self.current_span();
900
901        // Try to parse as generator: pat <- expr
902        // For simplicity, we first try parsing an expression
903        // and check if it's followed by <-
904        if self.check(&TokenKind::Let) {
905            self.advance();
906            let decls = self.parse_local_decls()?;
907            let span = start.to(self.tokens[self.pos - 1].span);
908            return Ok(Stmt::LetStmt(decls, span));
909        }
910
911        // First try to parse as a pattern directly if we might have one
912        // This is needed because patterns can contain `@` which isn't valid in expressions
913        // We try parsing as a pattern and check for `<-`
914        let saved_pos = self.pos;
915
916        // Try pattern first - if it fails or isn't followed by `<-`, backtrack
917        if self.is_pattern_start() {
918            if let Ok(pat) = self.parse_pattern() {
919                if self.eat(&TokenKind::LeftArrow) {
920                    let expr = self.parse_expr()?;
921                    let span = start.to(expr.span());
922                    return Ok(Stmt::Generator(pat, expr, span));
923                }
924            }
925            // Backtrack - not a generator
926            self.pos = saved_pos;
927        }
928
929        // Parse as expression
930        let expr = self.parse_expr()?;
931        let span = expr.span();
932        Ok(Stmt::Qualifier(expr, span))
933    }
934
935    /// Convert an expression to a pattern (for generators).
936    #[allow(dead_code)]
937    fn expr_to_pat(&self, expr: Expr) -> ParseResult<Pat> {
938        match expr {
939            Expr::Var(id, span) => Ok(Pat::Var(id, span)),
940            Expr::Con(id, span) => Ok(Pat::Con(id, vec![], span)),
941            Expr::Lit(lit, span) => Ok(Pat::Lit(lit, span)),
942            Expr::Tuple(exprs, span) => {
943                let pats: ParseResult<Vec<_>> =
944                    exprs.into_iter().map(|e| self.expr_to_pat(e)).collect();
945                Ok(Pat::Tuple(pats?, span))
946            }
947            Expr::List(exprs, span) => {
948                let pats: ParseResult<Vec<_>> =
949                    exprs.into_iter().map(|e| self.expr_to_pat(e)).collect();
950                Ok(Pat::List(pats?, span))
951            }
952            Expr::Paren(e, span) => {
953                let p = self.expr_to_pat(*e)?;
954                Ok(Pat::Paren(Box::new(p), span))
955            }
956            Expr::QualCon(module, id, span) => {
957                // Qualified constructor - create qualified name for pattern
958                let qual_name = format!(
959                    "{}.{}",
960                    module
961                        .parts
962                        .iter()
963                        .map(|s| s.as_str())
964                        .collect::<Vec<_>>()
965                        .join("."),
966                    id.as_str()
967                );
968                Ok(Pat::Con(Ident::from_str(&qual_name), vec![], span))
969            }
970            Expr::App(f, x, span) => {
971                // Could be constructor application (Con arg1 arg2 ...)
972                // Flatten nested applications
973                let mut args = vec![*x];
974                let mut cur = *f;
975                while let Expr::App(inner_f, inner_x, _) = cur {
976                    args.push(*inner_x);
977                    cur = *inner_f;
978                }
979                args.reverse();
980
981                let con_id = match cur {
982                    Expr::Con(id, _) => id,
983                    Expr::QualCon(module, id, _) => {
984                        let qual_name = format!(
985                            "{}.{}",
986                            module
987                                .parts
988                                .iter()
989                                .map(|s| s.as_str())
990                                .collect::<Vec<_>>()
991                                .join("."),
992                            id.as_str()
993                        );
994                        Ident::from_str(&qual_name)
995                    }
996                    _ => {
997                        return Err(ParseError::Unexpected {
998                            found: "expression".to_string(),
999                            expected: "pattern".to_string(),
1000                            span,
1001                        });
1002                    }
1003                };
1004
1005                let pats: ParseResult<Vec<_>> =
1006                    args.into_iter().map(|e| self.expr_to_pat(e)).collect();
1007                Ok(Pat::Con(con_id, pats?, span))
1008            }
1009            Expr::Wildcard(span) => Ok(Pat::Wildcard(span)),
1010            Expr::RecordCon(con, field_binds, has_wildcard, span) => {
1011                // Record pattern: Con { field = pat, ... }
1012                use bhc_ast::FieldPat;
1013                let mut field_pats = Vec::new();
1014                for fb in field_binds {
1015                    let pat = match fb.value {
1016                        Some(expr) => Some(self.expr_to_pat(expr)?),
1017                        None => None, // Punning: { x } means { x = x }
1018                    };
1019                    field_pats.push(FieldPat {
1020                        qualifier: fb.qualifier,
1021                        name: fb.name,
1022                        pat,
1023                        span: fb.span,
1024                    });
1025                }
1026                Ok(Pat::Record(con, field_pats, has_wildcard, span))
1027            }
1028            Expr::QualRecordCon(module_name, con, field_binds, has_wildcard, span) => {
1029                // Qualified record pattern: M.Con { field = pat, ... }
1030                use bhc_ast::FieldPat;
1031                let mut field_pats = Vec::new();
1032                for fb in field_binds {
1033                    let pat = match fb.value {
1034                        Some(expr) => Some(self.expr_to_pat(expr)?),
1035                        None => None,
1036                    };
1037                    field_pats.push(FieldPat {
1038                        qualifier: fb.qualifier,
1039                        name: fb.name,
1040                        pat,
1041                        span: fb.span,
1042                    });
1043                }
1044                Ok(Pat::QualRecord(
1045                    module_name,
1046                    con,
1047                    field_pats,
1048                    has_wildcard,
1049                    span,
1050                ))
1051            }
1052            _ => Err(ParseError::Unexpected {
1053                found: "expression".to_string(),
1054                expected: "pattern".to_string(),
1055                span: expr.span(),
1056            }),
1057        }
1058    }
1059
1060    /// Parse a lambda expression or lambda-case.
1061    fn parse_lambda(&mut self) -> ParseResult<Expr> {
1062        let start = self.current_span();
1063        self.expect(&TokenKind::Backslash)?;
1064
1065        // Check for lambda-case: \case { ... }
1066        if self.eat(&TokenKind::Case) {
1067            let alts = self.parse_case_alts()?;
1068            let span = start.to(alts.last().map(|a| a.span).unwrap_or(start));
1069
1070            // Lambda-case desugars to \x -> case x of { ... }
1071            let x = Ident::from_str("$lambdacase_arg");
1072            let x_pat = Pat::Var(x, Span::DUMMY);
1073            let x_expr = Expr::Var(x, Span::DUMMY);
1074            let case_expr = Expr::Case(Box::new(x_expr), alts, span);
1075            return Ok(Expr::Lam(vec![x_pat], Box::new(case_expr), span));
1076        }
1077
1078        let mut pats = vec![self.parse_pattern()?];
1079        while !self.check(&TokenKind::Arrow) && !self.at_eof() {
1080            if self.is_pattern_start() {
1081                pats.push(self.parse_pattern()?);
1082            } else {
1083                break;
1084            }
1085        }
1086
1087        self.expect(&TokenKind::Arrow)?;
1088        let body = self.parse_expr()?;
1089        let span = start.to(body.span());
1090
1091        Ok(Expr::Lam(pats, Box::new(body), span))
1092    }
1093
1094    /// Parse a let expression.
1095    fn parse_let_expr(&mut self) -> ParseResult<Expr> {
1096        let start = self.current_span();
1097        self.expect(&TokenKind::Let)?;
1098
1099        let decls = self.parse_local_decls()?;
1100
1101        self.expect(&TokenKind::In)?;
1102
1103        // When `in` appears on the same line as a case alternative (e.g.,
1104        //   let x = case n of _ -> 0 in body
1105        // ), the lexer still emits VirtualRBrace tokens for the case-of and
1106        // let layout contexts when the next line is at a lower column. These
1107        // are "phantom" closers — the blocks were already semantically
1108        // terminated by `in`. Skip them so `parse_expr` sees the body.
1109        while self.check(&TokenKind::VirtualRBrace) {
1110            self.advance();
1111        }
1112
1113        let body = self.parse_expr()?;
1114        let span = start.to(body.span());
1115
1116        Ok(Expr::Let(decls, Box::new(body), span))
1117    }
1118
1119    /// Parse an if expression.
1120    fn parse_if_expr(&mut self) -> ParseResult<Expr> {
1121        let start = self.current_span();
1122        self.expect(&TokenKind::If)?;
1123
1124        // MultiWayIf: if | cond1 -> e1 | cond2 -> e2 | otherwise -> e3
1125        if self.check(&TokenKind::Pipe) {
1126            return self.parse_multi_way_if(start);
1127        }
1128
1129        let cond = self.parse_expr()?;
1130        self.expect(&TokenKind::Then)?;
1131        let then_branch = self.parse_expr()?;
1132        self.expect(&TokenKind::Else)?;
1133        let else_branch = self.parse_expr()?;
1134
1135        let span = start.to(else_branch.span());
1136        Ok(Expr::If(
1137            Box::new(cond),
1138            Box::new(then_branch),
1139            Box::new(else_branch),
1140            span,
1141        ))
1142    }
1143
1144    /// Parse a multi-way if expression (MultiWayIf extension).
1145    ///
1146    /// Desugars `if | c1 -> e1 | c2 -> e2 | otherwise -> e3` into
1147    /// nested `if c1 then e1 else if c2 then e2 else e3`.
1148    fn parse_multi_way_if(&mut self, start: Span) -> ParseResult<Expr> {
1149        let guarded_rhss = self.parse_guarded_rhss()?;
1150
1151        if guarded_rhss.is_empty() {
1152            return Err(ParseError::Unexpected {
1153                found: "end of multi-way if".to_string(),
1154                expected: "guarded alternative".to_string(),
1155                span: start,
1156            });
1157        }
1158
1159        // Desugar to nested if-then-else.
1160        // Build from the last guard backwards.
1161        let mut result = Expr::App(
1162            Box::new(Expr::Var(Ident::from_str("error"), start)),
1163            Box::new(Expr::Lit(
1164                Lit::String("Non-exhaustive guards in multi-way if".to_string()),
1165                start,
1166            )),
1167            start,
1168        );
1169
1170        for grhs in guarded_rhss.into_iter().rev() {
1171            // For now, handle only single boolean guards (the common case).
1172            // Multi-guard and pattern guards fall through to the first guard expression.
1173            let cond = match grhs.guards.into_iter().next() {
1174                Some(Guard::Expr(cond_expr, _)) => cond_expr,
1175                Some(Guard::Pattern(_, expr, _)) => expr,
1176                None => continue,
1177            };
1178            let span = start.to(grhs.body.span());
1179            result = Expr::If(Box::new(cond), Box::new(grhs.body), Box::new(result), span);
1180        }
1181
1182        Ok(result)
1183    }
1184
1185    /// Parse a case expression.
1186    fn parse_case_expr(&mut self) -> ParseResult<Expr> {
1187        let start = self.current_span();
1188        self.expect(&TokenKind::Case)?;
1189
1190        let scrutinee = self.parse_expr()?;
1191        self.expect(&TokenKind::Of)?;
1192
1193        let alts = self.parse_case_alts()?;
1194        let span = start.to(self.tokens[self.pos.saturating_sub(1)].span);
1195
1196        Ok(Expr::Case(Box::new(scrutinee), alts, span))
1197    }
1198
1199    /// Parse case alternatives.
1200    fn parse_case_alts(&mut self) -> ParseResult<Vec<Alt>> {
1201        let mut alts = Vec::new();
1202
1203        // Check for explicit or virtual opening brace
1204        let explicit_braces = self.eat(&TokenKind::LBrace);
1205        let virtual_braces = !explicit_braces && self.eat(&TokenKind::VirtualLBrace);
1206        let has_braces = explicit_braces || virtual_braces;
1207
1208        if has_braces {
1209            // Parse alternatives separated by semicolons (real or virtual)
1210            let rbrace = if explicit_braces {
1211                TokenKind::RBrace
1212            } else {
1213                TokenKind::VirtualRBrace
1214            };
1215
1216            // `in` terminates case blocks inside `let` (when on the same line
1217            // as the last alternative, the lexer doesn't insert VirtualRBrace)
1218            let is_terminated = |s: &Self| {
1219                s.check(&rbrace)
1220                    || s.check(&TokenKind::VirtualRBrace)
1221                    || (!explicit_braces && s.check(&TokenKind::In))
1222            };
1223
1224            if !is_terminated(self) {
1225                alts.push(self.parse_alt()?);
1226                // Accept either real or virtual semicolons
1227                while self.eat(&TokenKind::Semi) || self.eat(&TokenKind::VirtualSemi) {
1228                    if is_terminated(self) {
1229                        break;
1230                    }
1231                    // Skip any extra semicolons
1232                    while self.eat(&TokenKind::Semi) || self.eat(&TokenKind::VirtualSemi) {}
1233                    if is_terminated(self) {
1234                        break;
1235                    }
1236                    alts.push(self.parse_alt()?);
1237                }
1238            }
1239
1240            if explicit_braces {
1241                self.expect(&TokenKind::RBrace)?;
1242            } else {
1243                // Virtual braces: consume if present, but don't require.
1244                // Don't consume `in` — it's consumed by parse_let_expr.
1245                self.eat(&TokenKind::VirtualRBrace);
1246            }
1247        } else {
1248            // No braces at all - single alternative
1249            alts.push(self.parse_alt()?);
1250        }
1251
1252        Ok(alts)
1253    }
1254
1255    /// Parse a case alternative.
1256    fn parse_alt(&mut self) -> ParseResult<Alt> {
1257        let start = self.current_span();
1258        let pat = self.parse_pattern()?;
1259
1260        // Check for guards: `| guard -> expr`
1261        let rhs = if self.check(&TokenKind::Pipe) {
1262            let guards = self.parse_guarded_rhss()?;
1263            let end_span = guards.last().map(|g| g.span).unwrap_or(start);
1264            Rhs::Guarded(guards, start.to(end_span))
1265        } else {
1266            self.expect(&TokenKind::Arrow)?;
1267            let expr = self.parse_expr()?;
1268            let span = start.to(expr.span());
1269            Rhs::Simple(expr, span)
1270        };
1271
1272        // Parse optional where clause
1273        let wheres = if self.eat(&TokenKind::Where) {
1274            self.parse_local_decls()?
1275        } else {
1276            vec![]
1277        };
1278
1279        let span = start.to(self.tokens[self.pos.saturating_sub(1)].span);
1280        Ok(Alt {
1281            pat,
1282            rhs,
1283            wheres,
1284            span,
1285        })
1286    }
1287
1288    /// Parse guarded right-hand sides: `| guard -> expr | guard -> expr ...`
1289    fn parse_guarded_rhss(&mut self) -> ParseResult<Vec<GuardedRhs>> {
1290        let mut guarded_rhss = Vec::new();
1291        while self.eat(&TokenKind::Pipe) {
1292            let start = self.current_span();
1293
1294            // Parse guards (can be multiple, separated by commas)
1295            // Each guard is either:
1296            //   - A pattern guard: `pat <- expr`
1297            //   - A boolean guard: `expr`
1298            let mut guards = Vec::new();
1299            loop {
1300                let guard = self.parse_guard_item()?;
1301                guards.push(guard);
1302
1303                // Multiple guards can be separated by commas
1304                if !self.eat(&TokenKind::Comma) {
1305                    break;
1306                }
1307            }
1308
1309            self.expect(&TokenKind::Arrow)?;
1310            let body = self.parse_expr()?;
1311            let span = start.to(body.span());
1312            guarded_rhss.push(GuardedRhs { guards, body, span });
1313        }
1314        Ok(guarded_rhss)
1315    }
1316
1317    /// Parse a single guard item (pattern guard `pat <- expr` or boolean guard `expr`).
1318    fn parse_guard_item(&mut self) -> ParseResult<Guard> {
1319        let start = self.current_span();
1320
1321        // Save position for backtracking
1322        let saved_pos = self.pos;
1323
1324        // Try parsing as pattern guard: pat <- expr
1325        if let Ok(pat) = self.parse_pattern() {
1326            if self.eat(&TokenKind::LeftArrow) {
1327                // This is a pattern guard
1328                let expr = self.parse_expr()?;
1329                let span = start.to(expr.span());
1330                return Ok(Guard::Pattern(pat, expr, span));
1331            }
1332            // Not a pattern guard, backtrack
1333            self.pos = saved_pos;
1334        } else {
1335            // Couldn't parse as pattern, reset position
1336            self.pos = saved_pos;
1337        }
1338
1339        // Parse as boolean guard
1340        let expr = self.parse_expr()?;
1341        let span = start.to(expr.span());
1342        Ok(Guard::Expr(expr, span))
1343    }
1344
1345    /// Parse a do expression.
1346    fn parse_do_expr(&mut self) -> ParseResult<Expr> {
1347        let start = self.current_span();
1348        self.expect(&TokenKind::Do)?;
1349
1350        let stmts = self.parse_do_stmts()?;
1351        let span = start.to(self.tokens[self.pos.saturating_sub(1)].span);
1352
1353        Ok(Expr::Do(stmts, span))
1354    }
1355
1356    /// Parse do statements.
1357    fn parse_do_stmts(&mut self) -> ParseResult<Vec<Stmt>> {
1358        let mut stmts = Vec::new();
1359
1360        // Check for explicit or virtual opening brace
1361        let explicit_braces = self.eat(&TokenKind::LBrace);
1362        let virtual_braces = !explicit_braces && self.eat(&TokenKind::VirtualLBrace);
1363        let has_braces = explicit_braces || virtual_braces;
1364
1365        if has_braces {
1366            // Parse statements separated by semicolons (real or virtual)
1367            let rbrace = if explicit_braces {
1368                TokenKind::RBrace
1369            } else {
1370                TokenKind::VirtualRBrace
1371            };
1372
1373            if !self.check(&rbrace) {
1374                stmts.push(self.parse_stmt()?);
1375                // Accept either real or virtual semicolons
1376                while self.eat(&TokenKind::Semi) || self.eat(&TokenKind::VirtualSemi) {
1377                    if self.check(&rbrace) || self.check(&TokenKind::VirtualRBrace) {
1378                        break;
1379                    }
1380                    // Stop at 'where' - it belongs to the enclosing function binding
1381                    if self.check(&TokenKind::Where) {
1382                        break;
1383                    }
1384                    // Skip any extra semicolons
1385                    while self.eat(&TokenKind::Semi) || self.eat(&TokenKind::VirtualSemi) {}
1386                    if self.check(&rbrace) || self.check(&TokenKind::VirtualRBrace) {
1387                        break;
1388                    }
1389                    // Stop at 'where' - it belongs to the enclosing function binding
1390                    if self.check(&TokenKind::Where) {
1391                        break;
1392                    }
1393                    stmts.push(self.parse_stmt()?);
1394                }
1395            }
1396
1397            if explicit_braces {
1398                self.expect(&TokenKind::RBrace)?;
1399            } else {
1400                // Virtual braces: consume if present, but don't require
1401                self.eat(&TokenKind::VirtualRBrace);
1402            }
1403        } else {
1404            // No braces at all - single statement
1405            stmts.push(self.parse_stmt()?);
1406        }
1407
1408        Ok(stmts)
1409    }
1410
1411    /// Parse a lazy expression (H26 extension).
1412    fn parse_lazy_expr(&mut self) -> ParseResult<Expr> {
1413        let start = self.current_span();
1414        self.expect_ident_str("lazy")?;
1415        self.expect(&TokenKind::LBrace)?;
1416        let expr = self.parse_expr()?;
1417        let end = self.expect(&TokenKind::RBrace)?;
1418        let span = start.to(end.span);
1419
1420        Ok(Expr::Lazy(Box::new(expr), span))
1421    }
1422
1423    /// Parse record construction: `Con { field = value, ... }` or `Con { field = value, .. }`
1424    fn parse_record_con(&mut self, con: Ident, start: Span) -> ParseResult<Expr> {
1425        self.expect(&TokenKind::LBrace)?;
1426
1427        let mut fields = Vec::new();
1428        let mut has_wildcard = false;
1429        if !self.check(&TokenKind::RBrace) {
1430            // Check for leading `..`
1431            if self.eat(&TokenKind::DotDot) {
1432                has_wildcard = true;
1433            } else {
1434                fields.push(self.parse_field_bind()?);
1435                while self.eat(&TokenKind::Comma) {
1436                    if self.check(&TokenKind::RBrace) {
1437                        break;
1438                    }
1439                    // Check for trailing `..`
1440                    if self.eat(&TokenKind::DotDot) {
1441                        has_wildcard = true;
1442                        break;
1443                    }
1444                    fields.push(self.parse_field_bind()?);
1445                }
1446            }
1447        }
1448
1449        let end = self.expect(&TokenKind::RBrace)?;
1450        let span = start.to(end.span);
1451        Ok(Expr::RecordCon(con, fields, has_wildcard, span))
1452    }
1453
1454    /// Parse qualified record construction: `M.Con { field = value, ... }`
1455    fn parse_qual_record_con(
1456        &mut self,
1457        module_name: ModuleName,
1458        con: Ident,
1459        start: Span,
1460    ) -> ParseResult<Expr> {
1461        self.expect(&TokenKind::LBrace)?;
1462
1463        let mut fields = Vec::new();
1464        let mut has_wildcard = false;
1465        if !self.check(&TokenKind::RBrace) {
1466            if self.eat(&TokenKind::DotDot) {
1467                has_wildcard = true;
1468            } else {
1469                fields.push(self.parse_field_bind()?);
1470                while self.eat(&TokenKind::Comma) {
1471                    if self.check(&TokenKind::RBrace) {
1472                        break;
1473                    }
1474                    if self.eat(&TokenKind::DotDot) {
1475                        has_wildcard = true;
1476                        break;
1477                    }
1478                    fields.push(self.parse_field_bind()?);
1479                }
1480            }
1481        }
1482
1483        let end = self.expect(&TokenKind::RBrace)?;
1484        let span = start.to(end.span);
1485        Ok(Expr::QualRecordCon(
1486            module_name,
1487            con,
1488            fields,
1489            has_wildcard,
1490            span,
1491        ))
1492    }
1493
1494    /// Parse record update: `expr { field = value, ... }`
1495    fn parse_record_update(&mut self, expr: Expr, start: Span) -> ParseResult<Expr> {
1496        self.expect(&TokenKind::LBrace)?;
1497
1498        let mut fields = Vec::new();
1499        if !self.check(&TokenKind::RBrace) {
1500            fields.push(self.parse_field_bind()?);
1501            while self.eat(&TokenKind::Comma) {
1502                if self.check(&TokenKind::RBrace) {
1503                    break;
1504                }
1505                fields.push(self.parse_field_bind()?);
1506            }
1507        }
1508
1509        let end = self.expect(&TokenKind::RBrace)?;
1510        let span = start.to(end.span);
1511        Ok(Expr::RecordUpd(Box::new(expr), fields, span))
1512    }
1513
1514    /// Parse a field binding: `field = expr`, `Mod.field = expr`, or `field` (punning)
1515    fn parse_field_bind(&mut self) -> ParseResult<FieldBind> {
1516        let tok = self.current().ok_or(ParseError::UnexpectedEof {
1517            expected: "field name".to_string(),
1518        })?;
1519
1520        let (qualifier, name, span) = match &tok.node.kind {
1521            TokenKind::Ident(sym) => (None, Ident::new(*sym), tok.span),
1522            TokenKind::QualIdent(qual, sym) => {
1523                let module_name = ModuleName {
1524                    parts: vec![*qual],
1525                    span: tok.span,
1526                };
1527                (Some(module_name), Ident::new(*sym), tok.span)
1528            }
1529            _ => {
1530                return Err(ParseError::Unexpected {
1531                    found: tok.node.kind.description().to_string(),
1532                    expected: "field name".to_string(),
1533                    span: tok.span,
1534                });
1535            }
1536        };
1537        self.advance();
1538
1539        let value = if self.eat(&TokenKind::Eq) {
1540            Some(self.parse_expr()?)
1541        } else {
1542            None // Punning: `Foo { bar }` means `Foo { bar = bar }`
1543        };
1544
1545        let end_span = value.as_ref().map(|e| e.span()).unwrap_or(span);
1546        let full_span = span.to(end_span);
1547        Ok(FieldBind {
1548            qualifier,
1549            name,
1550            value,
1551            span: full_span,
1552        })
1553    }
1554
1555    /// Parse an operator section: `(+ 1)` or `(1 +)`
1556    fn parse_operator_section(&mut self, start: Span) -> ParseResult<Expr> {
1557        // Already consumed LParen, now at operator
1558        let tok = self.current().ok_or(ParseError::UnexpectedEof {
1559            expected: "operator".to_string(),
1560        })?;
1561
1562        let op = match &tok.node.kind {
1563            TokenKind::Operator(sym) => Ident::new(*sym),
1564            TokenKind::Star => Ident::from_str("*"),
1565            TokenKind::Minus => Ident::from_str("-"),
1566            TokenKind::Percent => Ident::from_str("%"),
1567            TokenKind::Dot => Ident::from_str("."),
1568            TokenKind::ConOperator(sym) => Ident::new(*sym),
1569            TokenKind::QualOperator(qual, sym) => {
1570                let full_name = format!("{}.{}", qual.as_str(), sym.as_str());
1571                Ident::from_str(&full_name)
1572            }
1573            TokenKind::QualConOperator(qual, sym) => {
1574                let full_name = format!("{}.{}", qual.as_str(), sym.as_str());
1575                Ident::from_str(&full_name)
1576            }
1577            _ => {
1578                return Err(ParseError::Unexpected {
1579                    found: tok.node.kind.description().to_string(),
1580                    expected: "operator".to_string(),
1581                    span: tok.span,
1582                });
1583            }
1584        };
1585        self.advance();
1586
1587        if self.eat(&TokenKind::RParen) {
1588            // Just `(+)` - operator as a function
1589            let span = start.to(self.tokens[self.pos - 1].span);
1590            return Ok(Expr::Var(op, span));
1591        }
1592
1593        // Right section: `(+ 1)`
1594        let arg = self.parse_expr()?;
1595        let end = self.expect(&TokenKind::RParen)?;
1596        let span = start.to(end.span);
1597
1598        // Desugar (+ x) to (\y -> y + x)
1599        let y = Ident::from_str("$section_arg");
1600        let y_pat = Pat::Var(y, Span::DUMMY);
1601        let y_expr = Expr::Var(y, Span::DUMMY);
1602        let body = Expr::Infix(Box::new(y_expr), op, Box::new(arg), span);
1603        Ok(Expr::Lam(vec![y_pat], Box::new(body), span))
1604    }
1605
1606    /// Parse type annotation: `expr :: Type`
1607    fn parse_type_annotation(&mut self, expr: Expr) -> ParseResult<Expr> {
1608        let start = expr.span();
1609        self.expect(&TokenKind::DoubleColon)?;
1610        let ty = self.parse_type()?;
1611        let span = start.to(ty.span());
1612        Ok(Expr::Ann(Box::new(expr), ty, span))
1613    }
1614}
1615
1616/// Operator associativity.
1617#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1618enum Assoc {
1619    Left,
1620    Right,
1621    None,
1622}