Skip to main content

probl_syntax/
parser.rs

1//! Recursive-descent parser, with Pratt parsing for binary operators.
2
3use crate::ast::*;
4use crate::diagnostic::Diagnostic;
5use crate::lexer::lex;
6use crate::span::Span;
7use crate::token::{StrPart, Tok, Token};
8
9/// Parse a whole program. Parsing continues after errors, so the program is
10/// returned even when there are diagnostics.
11pub fn parse_program(src: &str) -> (Program, Vec<Diagnostic>) {
12    let (tokens, mut diags) = lex(src, 0);
13    let mut parser = Parser::new(tokens);
14    let program = parser.program();
15    diags.append(&mut parser.diags);
16    diags.sort_by_key(|d| d.span.lo);
17    (program, diags)
18}
19
20/// Parse a single expression (used for string interpolation and the REPL).
21pub fn parse_expr(src: &str, base: u32) -> (Option<Expr>, Vec<Diagnostic>) {
22    parse_nested_expr(src, base, 0)
23}
24
25/// How deeply expressions, blocks and patterns may nest. Deeper programs
26/// would risk overflowing the stack of the recursive parser and of the passes
27/// after it.
28pub const MAX_NESTING: u32 = 200;
29
30fn parse_nested_expr(src: &str, base: u32, depth: u32) -> (Option<Expr>, Vec<Diagnostic>) {
31    let (tokens, mut diags) = lex(src, base);
32    let mut parser = Parser::new(tokens);
33    parser.depth = depth;
34    let expr = parser.expr().ok();
35    if expr.is_some() && !parser.at(&Tok::Eof) {
36        let tok = parser.peek().clone();
37        parser.error(
38            parser.span(),
39            format!("unexpected {} after the expression", tok.describe()),
40        );
41    }
42    diags.append(&mut parser.diags);
43    (expr, diags)
44}
45
46/// Signals that a diagnostic was recorded and the caller should recover.
47#[derive(Debug)]
48pub struct Failed;
49
50type PResult<T> = Result<T, Failed>;
51
52// Binding powers, from loosest to tightest.
53const PREC_OR: u8 = 1;
54const PREC_AND: u8 = 2;
55const PREC_NOT: u8 = 3;
56const PREC_CMP: u8 = 4;
57const PREC_RANGE: u8 = 5;
58const PREC_ADD: u8 = 6;
59const PREC_MUL: u8 = 7;
60const PREC_NEG: u8 = 8;
61const PREC_POW: u8 = 9;
62const PREC_TYPEOF: u8 = 10;
63
64struct Parser {
65    tokens: Vec<Token>,
66    pos: usize,
67    diags: Vec<Diagnostic>,
68    /// Current nesting of expressions, blocks and patterns.
69    depth: u32,
70    /// Set while parsing the header of `if`, `while`, `for`, `repeat` and
71    /// `match`, where `{` starts the body rather than a record.
72    restricted: bool,
73}
74
75impl Parser {
76    fn new(tokens: Vec<Token>) -> Parser {
77        Parser {
78            tokens,
79            pos: 0,
80            diags: Vec::new(),
81            depth: 0,
82            restricted: false,
83        }
84    }
85
86    // ── Token helpers ────────────────────────────────────────────────────
87
88    fn peek(&self) -> &Tok {
89        &self.tokens[self.pos].tok
90    }
91
92    fn peek_at(&self, ahead: usize) -> &Tok {
93        let i = (self.pos + ahead).min(self.tokens.len() - 1);
94        &self.tokens[i].tok
95    }
96
97    fn span(&self) -> Span {
98        self.tokens[self.pos].span
99    }
100
101    fn prev_span(&self) -> Span {
102        if self.pos == 0 {
103            self.span()
104        } else {
105            self.tokens[self.pos - 1].span
106        }
107    }
108
109    fn at(&self, tok: &Tok) -> bool {
110        self.peek() == tok
111    }
112
113    fn bump(&mut self) -> Token {
114        let token = self.tokens[self.pos].clone();
115        if self.pos < self.tokens.len() - 1 {
116            self.pos += 1;
117        }
118        token
119    }
120
121    fn eat(&mut self, tok: &Tok) -> bool {
122        if self.at(tok) {
123            self.bump();
124            true
125        } else {
126            false
127        }
128    }
129
130    fn error(&mut self, span: Span, message: impl Into<String>) -> &mut Diagnostic {
131        self.diags.push(Diagnostic::error(span, message));
132        self.diags.last_mut().unwrap()
133    }
134
135    fn expect(&mut self, tok: &Tok, context: &str) -> PResult<Span> {
136        if self.at(tok) {
137            Ok(self.bump().span)
138        } else {
139            let found = self.peek().describe();
140            let span = self.span();
141            self.error(span, format!("expected `{}` {context}, found {found}", tok.text()));
142            Err(Failed)
143        }
144    }
145
146    fn ident(&mut self, what: &str) -> PResult<Ident> {
147        match self.peek().clone() {
148            Tok::Ident(name) => {
149                let span = self.bump().span;
150                Ok(Ident { name, span })
151            }
152            other => {
153                let span = self.span();
154                self.error(span, format!("expected {what}, found {}", other.describe()));
155                Err(Failed)
156            }
157        }
158    }
159
160    fn skip_newlines(&mut self) {
161        while self.at(&Tok::Newline) {
162            self.bump();
163        }
164    }
165
166    fn skip_separators(&mut self) {
167        while matches!(self.peek(), Tok::Newline | Tok::Semi) {
168            self.bump();
169        }
170    }
171
172    /// After a statement: a line break or `;`, or the end of the enclosing block.
173    fn end_of_statement(&mut self) -> PResult<()> {
174        match self.peek() {
175            Tok::Newline | Tok::Semi => {
176                self.bump();
177                Ok(())
178            }
179            Tok::RBrace | Tok::Eof => Ok(()),
180            other => {
181                let found = other.describe();
182                let span = self.span();
183                self.error(span, format!("expected the end of the statement, found {found}"))
184                    .help("put each statement on its own line, or separate them with `;`");
185                Err(Failed)
186            }
187        }
188    }
189
190    /// Skip to the end of the current statement after an error.
191    fn recover(&mut self) {
192        let mut depth = 0usize;
193        loop {
194            match self.peek() {
195                Tok::Eof => return,
196                Tok::LParen | Tok::LBracket | Tok::LBrace => depth += 1,
197                Tok::RParen | Tok::RBracket => depth = depth.saturating_sub(1),
198                Tok::RBrace => {
199                    if depth == 0 {
200                        return;
201                    }
202                    depth -= 1;
203                }
204                Tok::Newline | Tok::Semi if depth == 0 => {
205                    self.bump();
206                    return;
207                }
208                _ => {}
209            }
210            self.bump();
211        }
212    }
213
214    /// Run `f` one level deeper, or fail if the program nests too deeply.
215    /// The levels `f` adds with [`Parser::deeper`] end with it.
216    fn nested<T>(&mut self, f: impl FnOnce(&mut Parser) -> PResult<T>) -> PResult<T> {
217        let saved = self.depth;
218        self.deeper()?;
219        let result = f(self);
220        self.depth = saved;
221        result
222    }
223
224    /// Go one level deeper, or fail if the program nests too deeply. Loops
225    /// that wrap what they've parsed in a new node, like `a + b + c` or
226    /// `x.f().g`, call it on each turn: their trees grow as deep as the loop
227    /// runs, without the parser recursing, and the passes after it recurse
228    /// through them all the same.
229    fn deeper(&mut self) -> PResult<()> {
230        if self.depth >= MAX_NESTING {
231            let span = self.span();
232            self.error(span, "this is nested too deeply").help(format!(
233                "expressions, blocks and patterns can nest at most {MAX_NESTING} levels, \
234                 and each operator or `.` in a chain like `a + b + c` counts as one"
235            ));
236            return Err(Failed);
237        }
238        self.depth += 1;
239        Ok(())
240    }
241
242    fn with_restriction<T>(&mut self, restricted: bool, f: impl FnOnce(&mut Parser) -> T) -> T {
243        let saved = std::mem::replace(&mut self.restricted, restricted);
244        let result = f(self);
245        self.restricted = saved;
246        result
247    }
248
249    // ── Program and items ────────────────────────────────────────────────
250
251    fn program(&mut self) -> Program {
252        let mut pragmas = Vec::new();
253        let mut items = Vec::new();
254        self.skip_separators();
255        while self.at(&Tok::At) {
256            match self.pragma() {
257                Ok(p) => {
258                    pragmas.push(p);
259                    if self.end_of_statement().is_err() {
260                        self.recover();
261                    }
262                }
263                Err(Failed) => self.recover(),
264            }
265            self.skip_separators();
266        }
267        while !self.at(&Tok::Eof) {
268            if self.at(&Tok::RBrace) {
269                let span = self.span();
270                self.error(span, "unexpected `}`")
271                    .help("there is no open `{` for it to close");
272                self.bump();
273                self.skip_separators();
274                continue;
275            }
276            match self.item() {
277                Ok(item) => {
278                    items.push(item);
279                    if self.end_of_statement().is_err() {
280                        self.recover();
281                    }
282                }
283                Err(Failed) => self.recover(),
284            }
285            self.skip_separators();
286        }
287        Program { pragmas, items }
288    }
289
290    fn pragma(&mut self) -> PResult<Pragma> {
291        let lo = self.expect(&Tok::At, "")?;
292        let name = self.ident("a pragma name after `@`")?;
293        let arg = if matches!(self.peek(), Tok::Newline | Tok::Semi | Tok::Eof) {
294            None
295        } else {
296            Some(self.expr()?)
297        };
298        let span = lo.to(self.prev_span());
299        Ok(Pragma { name, arg, span })
300    }
301
302    fn item(&mut self) -> PResult<Item> {
303        match self.peek() {
304            Tok::Fn => self.fn_decl().map(Item::Fn),
305            Tok::Type => self.type_decl().map(Item::Type),
306            Tok::Enum => self.enum_decl().map(Item::Enum),
307            Tok::Import => {
308                let lo = self.bump().span;
309                match self.peek().clone() {
310                    Tok::Str(parts) => {
311                        let span = self.bump().span;
312                        match plain_string(&parts) {
313                            Some(path) => Ok(Item::Import(Import {
314                                path,
315                                span: lo.to(span),
316                            })),
317                            None => {
318                                self.error(span, "an import path can't contain `{…}`");
319                                Err(Failed)
320                            }
321                        }
322                    }
323                    other => {
324                        let span = self.span();
325                        self.error(
326                            span,
327                            format!("expected a file path after `import`, found {}", other.describe()),
328                        );
329                        Err(Failed)
330                    }
331                }
332            }
333            Tok::At => {
334                let span = self.span();
335                self.error(span, "pragmas must come before everything else in the file");
336                Err(Failed)
337            }
338            _ => self.stmt().map(Item::Stmt),
339        }
340    }
341
342    fn fn_decl(&mut self) -> PResult<FnDecl> {
343        let lo = self.bump().span; // fn
344        let name = self.ident("a function name")?;
345        self.expect(&Tok::LParen, "after the function name")?;
346        let mut params = Vec::new();
347        while !self.at(&Tok::RParen) {
348            let pname = self.ident("a parameter name")?;
349            let ty = if self.eat(&Tok::Colon) {
350                Some(self.type_expr()?)
351            } else {
352                None
353            };
354            params.push(Param { name: pname, ty });
355            if !self.eat(&Tok::Comma) {
356                break;
357            }
358        }
359        self.expect(&Tok::RParen, "after the parameters")?;
360        let ret = if self.eat(&Tok::Colon) {
361            Some(self.type_expr()?)
362        } else if matches!(self.peek(), Tok::Arrow | Tok::Ident(_)) {
363            // `-> int`, the old syntax, or `int`: say so, then read on as if
364            // the `:` were there.
365            let found = self.peek().describe();
366            let span = self.span();
367            self.error(span, format!("expected `:` before the return type, found {found}"))
368                .help("a function's return type follows a `:`, like a variable's: `fn name(…): int { … }`");
369            self.eat(&Tok::Arrow);
370            Some(self.type_expr()?)
371        } else {
372            None
373        };
374        let body = self.block()?;
375        let span = lo.to(body.span);
376        Ok(FnDecl {
377            name,
378            params,
379            ret,
380            body,
381            span,
382        })
383    }
384
385    fn type_decl(&mut self) -> PResult<TypeDecl> {
386        let lo = self.bump().span; // type
387        let name = self.ident("a type name")?;
388        self.expect(&Tok::Assign, "after the type name")?;
389        let ty = self.type_expr()?;
390        let span = lo.to(self.prev_span());
391        Ok(TypeDecl { name, ty, span })
392    }
393
394    fn enum_decl(&mut self) -> PResult<EnumDecl> {
395        let lo = self.bump().span; // enum
396        let name = self.ident("an enum name")?;
397        self.expect(&Tok::LBrace, "after the enum name")?;
398        let mut variants = Vec::new();
399        self.skip_newlines();
400        while !self.at(&Tok::RBrace) {
401            variants.push(self.ident("a variant name")?);
402            self.skip_newlines();
403            if !self.eat(&Tok::Comma) {
404                break;
405            }
406            self.skip_newlines();
407        }
408        self.skip_newlines();
409        let hi = self.expect(&Tok::RBrace, "to close the enum")?;
410        Ok(EnumDecl {
411            name,
412            variants,
413            span: lo.to(hi),
414        })
415    }
416
417    fn type_expr(&mut self) -> PResult<TypeExpr> {
418        self.nested(|p| p.type_expr_inner())
419    }
420
421    fn type_expr_inner(&mut self) -> PResult<TypeExpr> {
422        if self.at(&Tok::LBrace) {
423            let lo = self.bump().span;
424            let mut fields = Vec::new();
425            self.skip_newlines();
426            while !self.at(&Tok::RBrace) {
427                let name = self.ident("a field name")?;
428                self.expect(&Tok::Colon, "after the field name")?;
429                let ty = self.type_expr()?;
430                fields.push((name, ty));
431                self.skip_newlines();
432                if !self.eat(&Tok::Comma) {
433                    break;
434                }
435                self.skip_newlines();
436            }
437            self.skip_newlines();
438            let hi = self.expect(&Tok::RBrace, "to close the record type")?;
439            return Ok(TypeExpr::Record {
440                fields,
441                span: lo.to(hi),
442            });
443        }
444        let name = self.ident("a type")?;
445        let mut args = Vec::new();
446        if self.eat(&Tok::LBracket) {
447            loop {
448                args.push(self.type_expr()?);
449                if !self.eat(&Tok::Comma) {
450                    break;
451                }
452            }
453            self.expect(&Tok::RBracket, "to close the type arguments")?;
454        }
455        Ok(TypeExpr::Named { name, args })
456    }
457
458    // ── Statements ───────────────────────────────────────────────────────
459
460    fn block(&mut self) -> PResult<Block> {
461        self.nested(|p| p.block_inner())
462    }
463
464    fn block_inner(&mut self) -> PResult<Block> {
465        let lo = self.expect(&Tok::LBrace, "to start a block")?;
466        self.with_restriction(false, |p| {
467            let mut stmts = Vec::new();
468            loop {
469                p.skip_separators();
470                match p.peek() {
471                    Tok::RBrace => break,
472                    Tok::Eof => {
473                        p.error(lo, "this `{` is never closed");
474                        return Err(Failed);
475                    }
476                    _ => {}
477                }
478                match p.stmt() {
479                    Ok(stmt) => {
480                        stmts.push(stmt);
481                        if p.end_of_statement().is_err() {
482                            p.recover();
483                        }
484                    }
485                    Err(Failed) => p.recover(),
486                }
487            }
488            let hi = p.bump().span; // }
489            Ok(Block { stmts, span: lo.to(hi) })
490        })
491    }
492
493    fn stmt(&mut self) -> PResult<Stmt> {
494        let lo = self.span();
495        let kind = match self.peek() {
496            Tok::Let | Tok::Var => {
497                let mutable = self.bump().tok == Tok::Var;
498                let pattern = self.pattern()?;
499                let ty = if self.eat(&Tok::Colon) {
500                    Some(self.type_expr()?)
501                } else {
502                    None
503                };
504                let op = match self.peek() {
505                    Tok::Assign => BindOp::Assign,
506                    Tok::Tilde => BindOp::Draw,
507                    other => {
508                        let found = other.describe();
509                        let span = self.span();
510                        self.error(span, format!("expected `=` or `~` in the binding, found {found}"))
511                            .help("`=` keeps a value; `~` draws one from a distribution");
512                        return Err(Failed);
513                    }
514                };
515                self.bump();
516                let value = self.expr()?;
517                StmtKind::Let {
518                    mutable,
519                    pattern,
520                    ty,
521                    op,
522                    value,
523                }
524            }
525            Tok::For => {
526                self.bump();
527                let pattern = self.pattern()?;
528                self.expect(&Tok::In, "after the loop variable")?;
529                let iter = self.with_restriction(true, |p| p.expr())?;
530                let body = self.block()?;
531                StmtKind::For { pattern, iter, body }
532            }
533            Tok::While => {
534                self.bump();
535                let cond = self.with_restriction(true, |p| p.expr())?;
536                let body = self.block()?;
537                StmtKind::While { cond, body }
538            }
539            Tok::Repeat => {
540                self.bump();
541                let count = self.with_restriction(true, |p| p.expr())?;
542                let body = self.block()?;
543                StmtKind::Repeat { count, body }
544            }
545            Tok::Loop => {
546                self.bump();
547                let body = self.block()?;
548                StmtKind::Loop { body }
549            }
550            Tok::Break => {
551                self.bump();
552                StmtKind::Break
553            }
554            Tok::Continue => {
555                self.bump();
556                StmtKind::Continue
557            }
558            Tok::Return => {
559                self.bump();
560                if matches!(
561                    self.peek(),
562                    Tok::Newline | Tok::Semi | Tok::RBrace | Tok::Eof | Tok::Comma
563                ) {
564                    StmtKind::Return(None)
565                } else {
566                    StmtKind::Return(Some(self.expr()?))
567                }
568            }
569            Tok::Observe => {
570                self.bump();
571                let value = self.expr()?;
572                let from = if self.peek().is_ident("from") {
573                    self.bump();
574                    Some(self.expr()?)
575                } else {
576                    None
577                };
578                StmtKind::Observe { value, from }
579            }
580            Tok::Score => {
581                self.bump();
582                StmtKind::Score(self.expr()?)
583            }
584            Tok::Report => {
585                self.bump();
586                let value = self.expr()?;
587                let by = if self.peek().is_ident("by") {
588                    self.bump();
589                    Some(self.expr()?)
590                } else {
591                    None
592                };
593                let label = if self.peek().is_ident("as") {
594                    self.bump();
595                    match self.peek().clone() {
596                        Tok::Str(parts) => {
597                            let span = self.bump().span;
598                            match plain_string(&parts) {
599                                Some(text) => Some((text, span)),
600                                None => {
601                                    self.error(span, "a report label can't contain `{…}`");
602                                    return Err(Failed);
603                                }
604                            }
605                        }
606                        other => {
607                            let span = self.span();
608                            self.error(
609                                span,
610                                format!("expected a label string after `as`, found {}", other.describe()),
611                            );
612                            return Err(Failed);
613                        }
614                    }
615                } else {
616                    None
617                };
618                StmtKind::Report { value, by, label }
619            }
620            Tok::Fn | Tok::Type | Tok::Enum | Tok::Import => {
621                let span = self.span();
622                let what = self.peek().text();
623                self.error(span, format!("`{what}` declarations are only allowed at the top level"));
624                return Err(Failed);
625            }
626            _ => {
627                let target = self.expr()?;
628                let op = match self.peek() {
629                    Tok::Assign => Some(AssignOp::Set),
630                    Tok::Tilde => Some(AssignOp::Draw),
631                    Tok::PlusAssign => Some(AssignOp::Add),
632                    Tok::MinusAssign => Some(AssignOp::Sub),
633                    Tok::StarAssign => Some(AssignOp::Mul),
634                    Tok::SlashAssign => Some(AssignOp::Div),
635                    _ => None,
636                };
637                match op {
638                    None => StmtKind::Expr(target),
639                    Some(op) => {
640                        self.bump();
641                        if !is_place(&target) {
642                            self.error(target.span, "can't assign to this")
643                                .help("only variables, fields (`a.b`) and elements (`a[i]`) can be assigned");
644                            return Err(Failed);
645                        }
646                        let value = self.expr()?;
647                        StmtKind::Assign { target, op, value }
648                    }
649                }
650            }
651        };
652        Ok(Stmt {
653            kind,
654            span: lo.to(self.prev_span()),
655        })
656    }
657
658    // ── Patterns ─────────────────────────────────────────────────────────
659
660    fn pattern(&mut self) -> PResult<Pattern> {
661        self.nested(|p| p.pattern_inner())
662    }
663
664    fn pattern_inner(&mut self) -> PResult<Pattern> {
665        let first = self.pattern_alt()?;
666        if !self.at(&Tok::Pipe) {
667            return Ok(first);
668        }
669        // Lowering joins the alternatives' tests in a chain of `or`.
670        let mut alts = vec![first];
671        while self.eat(&Tok::Pipe) {
672            self.deeper()?;
673            alts.push(self.pattern_alt()?);
674        }
675        let span = alts[0].span.to(alts.last().unwrap().span);
676        Ok(Pattern {
677            kind: PatternKind::Or(alts),
678            span,
679        })
680    }
681
682    fn pattern_alt(&mut self) -> PResult<Pattern> {
683        let span = self.span();
684        let kind = match self.peek().clone() {
685            Tok::Underscore => {
686                self.bump();
687                PatternKind::Wildcard
688            }
689            Tok::Ident(name) => {
690                self.bump();
691                PatternKind::Name(name)
692            }
693            Tok::Int(_) | Tok::Float(_) | Tok::Percent(_) | Tok::Str(_) | Tok::True | Tok::False => {
694                PatternKind::Literal(self.primary()?)
695            }
696            Tok::Minus if matches!(self.peek_at(1), Tok::Int(_) | Tok::Float(_) | Tok::Percent(_)) => {
697                self.bump();
698                let inner = self.primary()?;
699                PatternKind::Literal(Expr {
700                    span: span.to(inner.span),
701                    kind: ExprKind::Unary {
702                        op: UnOp::Neg,
703                        expr: Box::new(inner),
704                    },
705                })
706            }
707            Tok::LBracket => {
708                self.bump();
709                // Lowering joins the items' tests in a chain of `and`.
710                let mut items = Vec::new();
711                while !self.at(&Tok::RBracket) {
712                    self.deeper()?;
713                    items.push(self.pattern()?);
714                    if !self.eat(&Tok::Comma) {
715                        break;
716                    }
717                }
718                self.expect(&Tok::RBracket, "to close the list pattern")?;
719                PatternKind::List(items)
720            }
721            other => {
722                self.error(span, format!("expected a pattern, found {}", other.describe()));
723                return Err(Failed);
724            }
725        };
726        Ok(Pattern {
727            kind,
728            span: span.to(self.prev_span()),
729        })
730    }
731
732    // ── Expressions ──────────────────────────────────────────────────────
733
734    fn expr(&mut self) -> PResult<Expr> {
735        if let Some(lambda) = self.lambda()? {
736            return Ok(lambda);
737        }
738        self.expr_bp(0)
739    }
740
741    /// `x -> body`, `(a, b) -> body` or `() -> body`, if one starts here.
742    fn lambda(&mut self) -> PResult<Option<Expr>> {
743        let lo = self.span();
744        let params = match (self.peek(), self.peek_at(1)) {
745            (Tok::Ident(_), Tok::Arrow) => {
746                let param = self.ident("a parameter")?;
747                vec![param]
748            }
749            (Tok::LParen, _) => {
750                // Look for `( ident, … ) ->` without consuming anything.
751                let mut i = 1;
752                let mut expect_ident = true;
753                loop {
754                    match (self.peek_at(i), expect_ident) {
755                        (Tok::RParen, _) => break,
756                        (Tok::Ident(_), true) => expect_ident = false,
757                        (Tok::Comma, false) => expect_ident = true,
758                        _ => return Ok(None),
759                    }
760                    i += 1;
761                }
762                if *self.peek_at(i + 1) != Tok::Arrow {
763                    return Ok(None);
764                }
765                self.bump(); // (
766                let mut params = Vec::new();
767                while !self.at(&Tok::RParen) {
768                    params.push(self.ident("a parameter")?);
769                    self.eat(&Tok::Comma);
770                }
771                self.bump(); // )
772                params
773            }
774            _ => return Ok(None),
775        };
776        self.expect(&Tok::Arrow, "")?;
777        let body = self.nested(|p| p.expr())?;
778        let span = lo.to(body.span);
779        Ok(Some(Expr {
780            kind: ExprKind::Lambda {
781                params,
782                body: Box::new(body),
783            },
784            span,
785        }))
786    }
787
788    fn expr_bp(&mut self, min: u8) -> PResult<Expr> {
789        self.nested(|p| p.expr_bp_inner(min))
790    }
791
792    fn expr_bp_inner(&mut self, min: u8) -> PResult<Expr> {
793        let lo = self.span();
794        let mut lhs = match self.peek() {
795            Tok::Typeof | Tok::Not | Tok::Minus | Tok::Tilde => {
796                let (op, precedence) = match self.peek() {
797                    Tok::Typeof => (Some(UnOp::Typeof), PREC_TYPEOF),
798                    Tok::Not => (Some(UnOp::Not), PREC_NOT),
799                    Tok::Minus => (Some(UnOp::Neg), PREC_NEG),
800                    _ => (None, PREC_TYPEOF),
801                };
802                self.bump();
803                let operand = self.expr_bp(precedence)?;
804                Expr {
805                    span: lo.to(operand.span),
806                    kind: match op {
807                        Some(op) => ExprKind::Unary {
808                            op,
809                            expr: Box::new(operand),
810                        },
811                        None => ExprKind::Draw(Box::new(operand)),
812                    },
813                }
814            }
815            _ => self.postfix()?,
816        };
817        while let Some((op, prec, len)) = self.binary_op() {
818            if prec < min {
819                break;
820            }
821            self.deeper()?;
822            for _ in 0..len {
823                self.bump();
824            }
825            let rhs = match prec {
826                PREC_POW => self.expr_bp(PREC_POW)?, // right-associative
827                _ => self.expr_bp(prec + 1)?,
828            };
829            if prec == PREC_CMP || prec == PREC_RANGE {
830                if let Some((next, next_prec, _)) = self.binary_op() {
831                    if next_prec == prec {
832                        let span = self.span();
833                        let msg = if prec == PREC_CMP {
834                            format!(
835                                "comparisons can't be chained: `{}` after `{}`",
836                                next.symbol(),
837                                op.symbol()
838                            )
839                        } else {
840                            format!("`{}` can't follow `{}` directly", next.symbol(), op.symbol())
841                        };
842                        self.error(span, msg)
843                            .help("add parentheses, or combine the tests with `and`");
844                        return Err(Failed);
845                    }
846                }
847            }
848            let span = lhs.span.to(rhs.span);
849            lhs = Expr {
850                kind: ExprKind::Binary {
851                    op,
852                    lhs: Box::new(lhs),
853                    rhs: Box::new(rhs),
854                },
855                span,
856            };
857        }
858        Ok(lhs)
859    }
860
861    /// The binary operator at the current position: (operator, precedence, token count).
862    fn binary_op(&self) -> Option<(BinOp, u8, usize)> {
863        Some(match self.peek() {
864            Tok::Or => (BinOp::Or, PREC_OR, 1),
865            Tok::And => (BinOp::And, PREC_AND, 1),
866            Tok::EqEq => (BinOp::Eq, PREC_CMP, 1),
867            Tok::NotEq => (BinOp::Ne, PREC_CMP, 1),
868            Tok::Lt => (BinOp::Lt, PREC_CMP, 1),
869            Tok::Le => (BinOp::Le, PREC_CMP, 1),
870            Tok::Gt => (BinOp::Gt, PREC_CMP, 1),
871            Tok::Ge => (BinOp::Ge, PREC_CMP, 1),
872            Tok::In => (BinOp::In, PREC_CMP, 1),
873            Tok::Not if *self.peek_at(1) == Tok::In => (BinOp::NotIn, PREC_CMP, 2),
874            Tok::DotDot => (BinOp::Range, PREC_RANGE, 1),
875            Tok::DotDotLt => (BinOp::RangeExcl, PREC_RANGE, 1),
876            Tok::Ident(word) if word == "to" => (BinOp::To, PREC_RANGE, 1),
877            Tok::Plus => (BinOp::Add, PREC_ADD, 1),
878            Tok::Minus => (BinOp::Sub, PREC_ADD, 1),
879            Tok::Star => (BinOp::Mul, PREC_MUL, 1),
880            Tok::Slash => (BinOp::Div, PREC_MUL, 1),
881            Tok::Div => (BinOp::IntDiv, PREC_MUL, 1),
882            Tok::Mod => (BinOp::Mod, PREC_MUL, 1),
883            Tok::Caret => (BinOp::Pow, PREC_POW, 1),
884            _ => return None,
885        })
886    }
887
888    fn postfix(&mut self) -> PResult<Expr> {
889        let mut expr = self.primary()?;
890        loop {
891            if matches!(self.peek(), Tok::Dot | Tok::LParen | Tok::LBracket | Tok::With) {
892                self.deeper()?;
893            }
894            match self.peek() {
895                Tok::Dot => {
896                    self.bump();
897                    let name = self.ident("a field or method name after `.`")?;
898                    if self.at(&Tok::LParen) {
899                        let args = self.call_args()?;
900                        let span = expr.span.to(self.prev_span());
901                        expr = Expr {
902                            kind: ExprKind::Method {
903                                receiver: Box::new(expr),
904                                name,
905                                args,
906                            },
907                            span,
908                        };
909                    } else {
910                        let span = expr.span.to(name.span);
911                        expr = Expr {
912                            kind: ExprKind::Field {
913                                expr: Box::new(expr),
914                                name,
915                            },
916                            span,
917                        };
918                    }
919                }
920                Tok::LParen => {
921                    let args = self.call_args()?;
922                    let span = expr.span.to(self.prev_span());
923                    expr = Expr {
924                        kind: ExprKind::Call {
925                            callee: Box::new(expr),
926                            args,
927                        },
928                        span,
929                    };
930                }
931                Tok::LBracket => {
932                    self.bump();
933                    let index = self.with_restriction(false, |p| p.expr())?;
934                    let hi = self.expect(&Tok::RBracket, "to close the index")?;
935                    let span = expr.span.to(hi);
936                    expr = Expr {
937                        kind: ExprKind::Index {
938                            expr: Box::new(expr),
939                            index: Box::new(index),
940                        },
941                        span,
942                    };
943                }
944                Tok::With => {
945                    self.bump();
946                    self.expect(&Tok::LBrace, "after `with`")?;
947                    let fields = self.record_fields()?;
948                    let span = expr.span.to(self.prev_span());
949                    expr = Expr {
950                        kind: ExprKind::With {
951                            expr: Box::new(expr),
952                            fields,
953                        },
954                        span,
955                    };
956                }
957                _ => return Ok(expr),
958            }
959        }
960    }
961
962    fn call_args(&mut self) -> PResult<Vec<Arg>> {
963        self.expect(&Tok::LParen, "")?;
964        self.with_restriction(false, |p| {
965            let mut args = Vec::new();
966            while !p.at(&Tok::RParen) {
967                let name = if matches!(p.peek(), Tok::Ident(_)) && *p.peek_at(1) == Tok::Colon {
968                    let name = p.ident("an argument name")?;
969                    p.bump(); // :
970                    Some(name)
971                } else {
972                    None
973                };
974                let value = p.expr()?;
975                args.push(Arg { name, value });
976                if !p.eat(&Tok::Comma) {
977                    break;
978                }
979            }
980            p.expect(&Tok::RParen, "to close the arguments")?;
981            Ok(args)
982        })
983    }
984
985    /// Fields of a record literal, after its `{`; consumes the closing `}`.
986    fn record_fields(&mut self) -> PResult<Vec<Field>> {
987        self.with_restriction(false, |p| {
988            let mut fields = Vec::new();
989            p.skip_newlines();
990            while !p.at(&Tok::RBrace) {
991                let name = p.ident("a field name")?;
992                let value = if p.eat(&Tok::Colon) {
993                    p.expr()?
994                } else {
995                    Expr {
996                        kind: ExprKind::Name(name.name.clone()),
997                        span: name.span,
998                    }
999                };
1000                fields.push(Field { name, value });
1001                p.skip_newlines();
1002                if !p.eat(&Tok::Comma) {
1003                    break;
1004                }
1005                p.skip_newlines();
1006            }
1007            p.skip_newlines();
1008            p.expect(&Tok::RBrace, "to close the record")?;
1009            Ok(fields)
1010        })
1011    }
1012
1013    /// Does the `{` at `self.pos + offset` start a record rather than a block?
1014    fn record_ahead(&self, offset: usize, allow_empty: bool) -> bool {
1015        let mut i = offset + 1;
1016        while *self.peek_at(i) == Tok::Newline {
1017            i += 1;
1018        }
1019        match (self.peek_at(i), self.peek_at(i + 1)) {
1020            (Tok::RBrace, _) => allow_empty,
1021            (Tok::Ident(_), Tok::Colon | Tok::Comma) => true,
1022            (Tok::Ident(_), Tok::RBrace) => allow_empty,
1023            _ => false,
1024        }
1025    }
1026
1027    fn primary(&mut self) -> PResult<Expr> {
1028        let lo = self.span();
1029        let kind = match self.peek().clone() {
1030            Tok::Int(v) => {
1031                self.bump();
1032                ExprKind::Int(v)
1033            }
1034            Tok::Float(v) => {
1035                self.bump();
1036                ExprKind::Float(v)
1037            }
1038            Tok::Percent(v) => {
1039                self.bump();
1040                ExprKind::Percent(v)
1041            }
1042            Tok::Dice { count, sides } => {
1043                self.bump();
1044                ExprKind::Dice { count, sides }
1045            }
1046            Tok::True => {
1047                self.bump();
1048                ExprKind::Bool(true)
1049            }
1050            Tok::False => {
1051                self.bump();
1052                ExprKind::Bool(false)
1053            }
1054            Tok::Str(parts) => {
1055                self.bump();
1056                ExprKind::Str(self.string_segments(parts))
1057            }
1058            Tok::Ident(name) => {
1059                // A typed record literal: `Fighter { hp: 12 }`.
1060                if !self.restricted && *self.peek_at(1) == Tok::LBrace && self.record_ahead(1, true) {
1061                    let ident = self.ident("")?;
1062                    self.bump(); // {
1063                    let fields = self.record_fields()?;
1064                    ExprKind::Record {
1065                        name: Some(ident),
1066                        fields,
1067                    }
1068                } else {
1069                    self.bump();
1070                    ExprKind::Name(name)
1071                }
1072            }
1073            Tok::LParen => {
1074                self.bump();
1075                let inner = self.with_restriction(false, |p| p.expr())?;
1076                self.expect(&Tok::RParen, "to close the parenthesis")?;
1077                let span = lo.to(self.prev_span());
1078                return Ok(Expr { span, ..inner });
1079            }
1080            Tok::LBracket => return self.list_or_map(),
1081            Tok::LBrace => {
1082                if self.restricted {
1083                    self.error(lo, "expected an expression before `{`");
1084                    return Err(Failed);
1085                }
1086                if self.record_ahead(0, false) {
1087                    self.bump();
1088                    let fields = self.record_fields()?;
1089                    ExprKind::Record { name: None, fields }
1090                } else {
1091                    ExprKind::Block(self.block()?)
1092                }
1093            }
1094            Tok::If => return self.if_expr(),
1095            Tok::Try => return self.try_expr(),
1096            Tok::Chance => return self.chance_expr(),
1097            Tok::Match => return self.match_expr(),
1098            Tok::Simulate => {
1099                self.bump();
1100                ExprKind::Simulate(self.block()?)
1101            }
1102            Tok::Underscore => {
1103                self.error(lo, "`_` can only be used in patterns");
1104                return Err(Failed);
1105            }
1106            other => {
1107                self.error(lo, format!("expected an expression, found {}", other.describe()));
1108                return Err(Failed);
1109            }
1110        };
1111        Ok(Expr {
1112            kind,
1113            span: lo.to(self.prev_span()),
1114        })
1115    }
1116
1117    fn string_segments(&mut self, parts: Vec<StrPart>) -> Vec<StrSegment> {
1118        let mut segments = Vec::new();
1119        for part in parts {
1120            match part {
1121                StrPart::Lit(text) => segments.push(StrSegment::Lit(text)),
1122                StrPart::Expr { src, offset } => {
1123                    let (expr, mut diags) = parse_nested_expr(&src, offset, self.depth + 1);
1124                    self.diags.append(&mut diags);
1125                    if let Some(expr) = expr {
1126                        segments.push(StrSegment::Expr(expr));
1127                    }
1128                }
1129            }
1130        }
1131        segments
1132    }
1133
1134    fn list_or_map(&mut self) -> PResult<Expr> {
1135        let lo = self.bump().span; // [
1136        self.with_restriction(false, |p| {
1137            if p.at(&Tok::Colon) && *p.peek_at(1) == Tok::RBracket {
1138                p.bump();
1139                let hi = p.bump().span;
1140                return Ok(Expr {
1141                    kind: ExprKind::Map(Vec::new()),
1142                    span: lo.to(hi),
1143                });
1144            }
1145            if p.at(&Tok::RBracket) {
1146                let hi = p.bump().span;
1147                return Ok(Expr {
1148                    kind: ExprKind::List(Vec::new()),
1149                    span: lo.to(hi),
1150                });
1151            }
1152            let first = p.expr()?;
1153            if p.eat(&Tok::Colon) {
1154                let value = p.expr()?;
1155                let mut entries = vec![(first, value)];
1156                while p.eat(&Tok::Comma) {
1157                    if p.at(&Tok::RBracket) {
1158                        break;
1159                    }
1160                    let key = p.expr()?;
1161                    p.expect(&Tok::Colon, "between a key and its value")?;
1162                    let value = p.expr()?;
1163                    entries.push((key, value));
1164                }
1165                let hi = p.expect(&Tok::RBracket, "to close the map")?;
1166                Ok(Expr {
1167                    kind: ExprKind::Map(entries),
1168                    span: lo.to(hi),
1169                })
1170            } else {
1171                let mut items = vec![first];
1172                while p.eat(&Tok::Comma) {
1173                    if p.at(&Tok::RBracket) {
1174                        break;
1175                    }
1176                    items.push(p.expr()?);
1177                }
1178                let hi = p.expect(&Tok::RBracket, "to close the list")?;
1179                Ok(Expr {
1180                    kind: ExprKind::List(items),
1181                    span: lo.to(hi),
1182                })
1183            }
1184        })
1185    }
1186
1187    fn if_expr(&mut self) -> PResult<Expr> {
1188        let lo = self.bump().span; // if
1189        let cond = self.with_restriction(true, |p| p.expr())?;
1190        let then = self.block()?;
1191        // Allow `else` on the line after the closing `}` (but not `else =>`,
1192        // which starts the next arm of a `chance` block).
1193        let mut ahead = 0;
1194        while *self.peek_at(ahead) == Tok::Newline {
1195            ahead += 1;
1196        }
1197        if ahead > 0 && *self.peek_at(ahead) == Tok::Else && matches!(self.peek_at(ahead + 1), Tok::LBrace | Tok::If) {
1198            self.skip_newlines();
1199        }
1200        let otherwise = if self.eat(&Tok::Else) {
1201            if self.at(&Tok::If) {
1202                Some(Box::new(self.nested(|p| p.if_expr())?))
1203            } else {
1204                let block = self.block()?;
1205                Some(Box::new(Expr {
1206                    span: block.span,
1207                    kind: ExprKind::Block(block),
1208                }))
1209            }
1210        } else {
1211            None
1212        };
1213        Ok(Expr {
1214            kind: ExprKind::If {
1215                cond: Box::new(cond),
1216                then,
1217                otherwise,
1218            },
1219            span: lo.to(self.prev_span()),
1220        })
1221    }
1222
1223    fn try_expr(&mut self) -> PResult<Expr> {
1224        let lo = self.bump().span; // try
1225        let body = self.block()?;
1226        let mut catches: Vec<Catch> = Vec::new();
1227        loop {
1228            // Like `else`, a `catch` can start the line after the `}`.
1229            let mut ahead = 0;
1230            while *self.peek_at(ahead) == Tok::Newline {
1231                ahead += 1;
1232            }
1233            if *self.peek_at(ahead) != Tok::Catch {
1234                break;
1235            }
1236            self.skip_newlines();
1237            let start = self.bump().span; // catch
1238            let fault = match self.peek() {
1239                Tok::Ident(_) => Some(self.ident("a fault")?),
1240                _ => None,
1241            };
1242            let body = self.block()?;
1243            let span = start.to(self.prev_span());
1244            if catches.last().is_some_and(|c| c.fault.is_none()) {
1245                self.error(span, "a `catch` after one that catches every fault never runs");
1246            }
1247            catches.push(Catch { fault, body, span });
1248        }
1249        if catches.is_empty() {
1250            self.error(
1251                lo,
1252                "`try` needs a `catch`, like `try { 1 / x } catch DivisionByZero { 0 }`",
1253            );
1254            return Err(Failed);
1255        }
1256        Ok(Expr {
1257            kind: ExprKind::Try { body, catches },
1258            span: lo.to(self.prev_span()),
1259        })
1260    }
1261
1262    /// Arms of a `chance` or `match` body: `{ arm, arm \n arm }`.
1263    fn arms<T>(&mut self, mut arm: impl FnMut(&mut Parser) -> PResult<T>) -> PResult<Vec<T>> {
1264        self.expect(&Tok::LBrace, "to start the arms")?;
1265        self.with_restriction(false, |p| {
1266            let mut arms = Vec::new();
1267            loop {
1268                while matches!(p.peek(), Tok::Newline | Tok::Comma) {
1269                    p.bump();
1270                }
1271                if p.at(&Tok::RBrace) {
1272                    break;
1273                }
1274                let start = p.pos;
1275                match arm(p) {
1276                    Ok(a) => arms.push(a),
1277                    Err(Failed) => {
1278                        // Skip to the next arm, past the brackets the failed
1279                        // one left open: the commas inside them don't
1280                        // separate arms, and stopping at each would report an
1281                        // error for every one.
1282                        let mut open = p.tokens[start..p.pos].iter().fold(0usize, |open, t| match t.tok {
1283                            Tok::LParen | Tok::LBracket | Tok::LBrace => open + 1,
1284                            Tok::RParen | Tok::RBracket | Tok::RBrace => open.saturating_sub(1),
1285                            _ => open,
1286                        });
1287                        loop {
1288                            match p.peek() {
1289                                Tok::Eof => return Err(Failed),
1290                                Tok::Newline | Tok::Comma | Tok::RBrace if open == 0 => break,
1291                                Tok::LParen | Tok::LBracket | Tok::LBrace => open += 1,
1292                                Tok::RParen | Tok::RBracket | Tok::RBrace => open = open.saturating_sub(1),
1293                                _ => {}
1294                            }
1295                            p.bump();
1296                        }
1297                        continue;
1298                    }
1299                }
1300                match p.peek() {
1301                    Tok::Newline | Tok::Comma | Tok::RBrace => {}
1302                    other => {
1303                        let found = other.describe();
1304                        let span = p.span();
1305                        p.error(span, format!("expected `,` or a new line between arms, found {found}"));
1306                        return Err(Failed);
1307                    }
1308                }
1309            }
1310            p.bump(); // }
1311            Ok(arms)
1312        })
1313    }
1314
1315    fn chance_expr(&mut self) -> PResult<Expr> {
1316        let lo = self.bump().span; // chance
1317        let arms = self.arms(|p| {
1318            let arm_lo = p.span();
1319            let weight = if p.eat(&Tok::Else) { None } else { Some(p.expr()?) };
1320            p.expect(&Tok::FatArrow, "after the arm's probability")?;
1321            let body = p.stmt()?;
1322            Ok(ChanceArm {
1323                weight,
1324                span: arm_lo.to(body.span),
1325                body,
1326            })
1327        })?;
1328        Ok(Expr {
1329            kind: ExprKind::Chance { arms },
1330            span: lo.to(self.prev_span()),
1331        })
1332    }
1333
1334    fn match_expr(&mut self) -> PResult<Expr> {
1335        let lo = self.bump().span; // match
1336        let scrutinee = self.with_restriction(true, |p| p.expr())?;
1337        let arms = self.arms(|p| {
1338            let pattern = p.pattern()?;
1339            let guard = if p.eat(&Tok::If) { Some(p.expr()?) } else { None };
1340            p.expect(&Tok::FatArrow, "after the pattern")?;
1341            let body = p.stmt()?;
1342            Ok(MatchArm {
1343                span: pattern.span.to(body.span),
1344                pattern,
1345                guard,
1346                body,
1347            })
1348        })?;
1349        Ok(Expr {
1350            kind: ExprKind::Match {
1351                scrutinee: Box::new(scrutinee),
1352                arms,
1353            },
1354            span: lo.to(self.prev_span()),
1355        })
1356    }
1357}
1358
1359/// The text of a string literal without interpolation.
1360fn plain_string(parts: &[StrPart]) -> Option<String> {
1361    let mut text = String::new();
1362    for part in parts {
1363        match part {
1364            StrPart::Lit(s) => text.push_str(s),
1365            StrPart::Expr { .. } => return None,
1366        }
1367    }
1368    Some(text)
1369}
1370
1371fn is_place(expr: &Expr) -> bool {
1372    match &expr.kind {
1373        ExprKind::Name(_) => true,
1374        ExprKind::Field { expr, .. } | ExprKind::Index { expr, .. } => is_place(expr),
1375        _ => false,
1376    }
1377}