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::Arrow) {
361            Some(self.type_expr()?)
362        } else {
363            None
364        };
365        let body = self.block()?;
366        let span = lo.to(body.span);
367        Ok(FnDecl {
368            name,
369            params,
370            ret,
371            body,
372            span,
373        })
374    }
375
376    fn type_decl(&mut self) -> PResult<TypeDecl> {
377        let lo = self.bump().span; // type
378        let name = self.ident("a type name")?;
379        self.expect(&Tok::Assign, "after the type name")?;
380        let ty = self.type_expr()?;
381        let span = lo.to(self.prev_span());
382        Ok(TypeDecl { name, ty, span })
383    }
384
385    fn enum_decl(&mut self) -> PResult<EnumDecl> {
386        let lo = self.bump().span; // enum
387        let name = self.ident("an enum name")?;
388        self.expect(&Tok::LBrace, "after the enum name")?;
389        let mut variants = Vec::new();
390        self.skip_newlines();
391        while !self.at(&Tok::RBrace) {
392            variants.push(self.ident("a variant name")?);
393            self.skip_newlines();
394            if !self.eat(&Tok::Comma) {
395                break;
396            }
397            self.skip_newlines();
398        }
399        self.skip_newlines();
400        let hi = self.expect(&Tok::RBrace, "to close the enum")?;
401        Ok(EnumDecl {
402            name,
403            variants,
404            span: lo.to(hi),
405        })
406    }
407
408    fn type_expr(&mut self) -> PResult<TypeExpr> {
409        self.nested(|p| p.type_expr_inner())
410    }
411
412    fn type_expr_inner(&mut self) -> PResult<TypeExpr> {
413        if self.at(&Tok::LBrace) {
414            let lo = self.bump().span;
415            let mut fields = Vec::new();
416            self.skip_newlines();
417            while !self.at(&Tok::RBrace) {
418                let name = self.ident("a field name")?;
419                self.expect(&Tok::Colon, "after the field name")?;
420                let ty = self.type_expr()?;
421                fields.push((name, ty));
422                self.skip_newlines();
423                if !self.eat(&Tok::Comma) {
424                    break;
425                }
426                self.skip_newlines();
427            }
428            self.skip_newlines();
429            let hi = self.expect(&Tok::RBrace, "to close the record type")?;
430            return Ok(TypeExpr::Record {
431                fields,
432                span: lo.to(hi),
433            });
434        }
435        let name = self.ident("a type")?;
436        let mut args = Vec::new();
437        if self.eat(&Tok::LBracket) {
438            loop {
439                args.push(self.type_expr()?);
440                if !self.eat(&Tok::Comma) {
441                    break;
442                }
443            }
444            self.expect(&Tok::RBracket, "to close the type arguments")?;
445        }
446        Ok(TypeExpr::Named { name, args })
447    }
448
449    // ── Statements ───────────────────────────────────────────────────────
450
451    fn block(&mut self) -> PResult<Block> {
452        self.nested(|p| p.block_inner())
453    }
454
455    fn block_inner(&mut self) -> PResult<Block> {
456        let lo = self.expect(&Tok::LBrace, "to start a block")?;
457        self.with_restriction(false, |p| {
458            let mut stmts = Vec::new();
459            loop {
460                p.skip_separators();
461                match p.peek() {
462                    Tok::RBrace => break,
463                    Tok::Eof => {
464                        p.error(lo, "this `{` is never closed");
465                        return Err(Failed);
466                    }
467                    _ => {}
468                }
469                match p.stmt() {
470                    Ok(stmt) => {
471                        stmts.push(stmt);
472                        if p.end_of_statement().is_err() {
473                            p.recover();
474                        }
475                    }
476                    Err(Failed) => p.recover(),
477                }
478            }
479            let hi = p.bump().span; // }
480            Ok(Block { stmts, span: lo.to(hi) })
481        })
482    }
483
484    fn stmt(&mut self) -> PResult<Stmt> {
485        let lo = self.span();
486        let kind = match self.peek() {
487            Tok::Let | Tok::Var => {
488                let mutable = self.bump().tok == Tok::Var;
489                let pattern = self.pattern()?;
490                let ty = if self.eat(&Tok::Colon) {
491                    Some(self.type_expr()?)
492                } else {
493                    None
494                };
495                let op = match self.peek() {
496                    Tok::Assign => BindOp::Assign,
497                    Tok::Tilde => BindOp::Draw,
498                    other => {
499                        let found = other.describe();
500                        let span = self.span();
501                        self.error(span, format!("expected `=` or `~` in the binding, found {found}"))
502                            .help("`=` keeps a value; `~` draws one from a distribution");
503                        return Err(Failed);
504                    }
505                };
506                self.bump();
507                let value = self.expr()?;
508                StmtKind::Let {
509                    mutable,
510                    pattern,
511                    ty,
512                    op,
513                    value,
514                }
515            }
516            Tok::For => {
517                self.bump();
518                let pattern = self.pattern()?;
519                self.expect(&Tok::In, "after the loop variable")?;
520                let iter = self.with_restriction(true, |p| p.expr())?;
521                let body = self.block()?;
522                StmtKind::For { pattern, iter, body }
523            }
524            Tok::While => {
525                self.bump();
526                let cond = self.with_restriction(true, |p| p.expr())?;
527                let body = self.block()?;
528                StmtKind::While { cond, body }
529            }
530            Tok::Repeat => {
531                self.bump();
532                let count = self.with_restriction(true, |p| p.expr())?;
533                let body = self.block()?;
534                StmtKind::Repeat { count, body }
535            }
536            Tok::Loop => {
537                self.bump();
538                let body = self.block()?;
539                StmtKind::Loop { body }
540            }
541            Tok::Break => {
542                self.bump();
543                StmtKind::Break
544            }
545            Tok::Continue => {
546                self.bump();
547                StmtKind::Continue
548            }
549            Tok::Return => {
550                self.bump();
551                if matches!(
552                    self.peek(),
553                    Tok::Newline | Tok::Semi | Tok::RBrace | Tok::Eof | Tok::Comma
554                ) {
555                    StmtKind::Return(None)
556                } else {
557                    StmtKind::Return(Some(self.expr()?))
558                }
559            }
560            Tok::Observe => {
561                self.bump();
562                let value = self.expr()?;
563                let from = if self.peek().is_ident("from") {
564                    self.bump();
565                    Some(self.expr()?)
566                } else {
567                    None
568                };
569                StmtKind::Observe { value, from }
570            }
571            Tok::Score => {
572                self.bump();
573                StmtKind::Score(self.expr()?)
574            }
575            Tok::Report => {
576                self.bump();
577                let value = self.expr()?;
578                let by = if self.peek().is_ident("by") {
579                    self.bump();
580                    Some(self.expr()?)
581                } else {
582                    None
583                };
584                let label = if self.peek().is_ident("as") {
585                    self.bump();
586                    match self.peek().clone() {
587                        Tok::Str(parts) => {
588                            let span = self.bump().span;
589                            match plain_string(&parts) {
590                                Some(text) => Some((text, span)),
591                                None => {
592                                    self.error(span, "a report label can't contain `{…}`");
593                                    return Err(Failed);
594                                }
595                            }
596                        }
597                        other => {
598                            let span = self.span();
599                            self.error(
600                                span,
601                                format!("expected a label string after `as`, found {}", other.describe()),
602                            );
603                            return Err(Failed);
604                        }
605                    }
606                } else {
607                    None
608                };
609                StmtKind::Report { value, by, label }
610            }
611            Tok::Fn | Tok::Type | Tok::Enum | Tok::Import => {
612                let span = self.span();
613                let what = self.peek().text();
614                self.error(span, format!("`{what}` declarations are only allowed at the top level"));
615                return Err(Failed);
616            }
617            _ => {
618                let target = self.expr()?;
619                let op = match self.peek() {
620                    Tok::Assign => Some(AssignOp::Set),
621                    Tok::Tilde => Some(AssignOp::Draw),
622                    Tok::PlusAssign => Some(AssignOp::Add),
623                    Tok::MinusAssign => Some(AssignOp::Sub),
624                    Tok::StarAssign => Some(AssignOp::Mul),
625                    Tok::SlashAssign => Some(AssignOp::Div),
626                    _ => None,
627                };
628                match op {
629                    None => StmtKind::Expr(target),
630                    Some(op) => {
631                        self.bump();
632                        if !is_place(&target) {
633                            self.error(target.span, "can't assign to this")
634                                .help("only variables, fields (`a.b`) and elements (`a[i]`) can be assigned");
635                            return Err(Failed);
636                        }
637                        let value = self.expr()?;
638                        StmtKind::Assign { target, op, value }
639                    }
640                }
641            }
642        };
643        Ok(Stmt {
644            kind,
645            span: lo.to(self.prev_span()),
646        })
647    }
648
649    // ── Patterns ─────────────────────────────────────────────────────────
650
651    fn pattern(&mut self) -> PResult<Pattern> {
652        self.nested(|p| p.pattern_inner())
653    }
654
655    fn pattern_inner(&mut self) -> PResult<Pattern> {
656        let first = self.pattern_alt()?;
657        if !self.at(&Tok::Pipe) {
658            return Ok(first);
659        }
660        // Lowering joins the alternatives' tests in a chain of `or`.
661        let mut alts = vec![first];
662        while self.eat(&Tok::Pipe) {
663            self.deeper()?;
664            alts.push(self.pattern_alt()?);
665        }
666        let span = alts[0].span.to(alts.last().unwrap().span);
667        Ok(Pattern {
668            kind: PatternKind::Or(alts),
669            span,
670        })
671    }
672
673    fn pattern_alt(&mut self) -> PResult<Pattern> {
674        let span = self.span();
675        let kind = match self.peek().clone() {
676            Tok::Underscore => {
677                self.bump();
678                PatternKind::Wildcard
679            }
680            Tok::Ident(name) => {
681                self.bump();
682                PatternKind::Name(name)
683            }
684            Tok::Int(_) | Tok::Float(_) | Tok::Percent(_) | Tok::Str(_) | Tok::True | Tok::False => {
685                PatternKind::Literal(self.primary()?)
686            }
687            Tok::Minus if matches!(self.peek_at(1), Tok::Int(_) | Tok::Float(_) | Tok::Percent(_)) => {
688                self.bump();
689                let inner = self.primary()?;
690                PatternKind::Literal(Expr {
691                    span: span.to(inner.span),
692                    kind: ExprKind::Unary {
693                        op: UnOp::Neg,
694                        expr: Box::new(inner),
695                    },
696                })
697            }
698            Tok::LBracket => {
699                self.bump();
700                // Lowering joins the items' tests in a chain of `and`.
701                let mut items = Vec::new();
702                while !self.at(&Tok::RBracket) {
703                    self.deeper()?;
704                    items.push(self.pattern()?);
705                    if !self.eat(&Tok::Comma) {
706                        break;
707                    }
708                }
709                self.expect(&Tok::RBracket, "to close the list pattern")?;
710                PatternKind::List(items)
711            }
712            other => {
713                self.error(span, format!("expected a pattern, found {}", other.describe()));
714                return Err(Failed);
715            }
716        };
717        Ok(Pattern {
718            kind,
719            span: span.to(self.prev_span()),
720        })
721    }
722
723    // ── Expressions ──────────────────────────────────────────────────────
724
725    fn expr(&mut self) -> PResult<Expr> {
726        if let Some(lambda) = self.lambda()? {
727            return Ok(lambda);
728        }
729        self.expr_bp(0)
730    }
731
732    /// `x -> body`, `(a, b) -> body` or `() -> body`, if one starts here.
733    fn lambda(&mut self) -> PResult<Option<Expr>> {
734        let lo = self.span();
735        let params = match (self.peek(), self.peek_at(1)) {
736            (Tok::Ident(_), Tok::Arrow) => {
737                let param = self.ident("a parameter")?;
738                vec![param]
739            }
740            (Tok::LParen, _) => {
741                // Look for `( ident, … ) ->` without consuming anything.
742                let mut i = 1;
743                let mut expect_ident = true;
744                loop {
745                    match (self.peek_at(i), expect_ident) {
746                        (Tok::RParen, _) => break,
747                        (Tok::Ident(_), true) => expect_ident = false,
748                        (Tok::Comma, false) => expect_ident = true,
749                        _ => return Ok(None),
750                    }
751                    i += 1;
752                }
753                if *self.peek_at(i + 1) != Tok::Arrow {
754                    return Ok(None);
755                }
756                self.bump(); // (
757                let mut params = Vec::new();
758                while !self.at(&Tok::RParen) {
759                    params.push(self.ident("a parameter")?);
760                    self.eat(&Tok::Comma);
761                }
762                self.bump(); // )
763                params
764            }
765            _ => return Ok(None),
766        };
767        self.expect(&Tok::Arrow, "")?;
768        let body = self.nested(|p| p.expr())?;
769        let span = lo.to(body.span);
770        Ok(Some(Expr {
771            kind: ExprKind::Lambda {
772                params,
773                body: Box::new(body),
774            },
775            span,
776        }))
777    }
778
779    fn expr_bp(&mut self, min: u8) -> PResult<Expr> {
780        self.nested(|p| p.expr_bp_inner(min))
781    }
782
783    fn expr_bp_inner(&mut self, min: u8) -> PResult<Expr> {
784        let lo = self.span();
785        let mut lhs = match self.peek() {
786            Tok::Typeof | Tok::Not | Tok::Minus | Tok::Tilde => {
787                let (op, precedence) = match self.peek() {
788                    Tok::Typeof => (Some(UnOp::Typeof), PREC_TYPEOF),
789                    Tok::Not => (Some(UnOp::Not), PREC_NOT),
790                    Tok::Minus => (Some(UnOp::Neg), PREC_NEG),
791                    _ => (None, PREC_TYPEOF),
792                };
793                self.bump();
794                let operand = self.expr_bp(precedence)?;
795                Expr {
796                    span: lo.to(operand.span),
797                    kind: match op {
798                        Some(op) => ExprKind::Unary {
799                            op,
800                            expr: Box::new(operand),
801                        },
802                        None => ExprKind::Draw(Box::new(operand)),
803                    },
804                }
805            }
806            _ => self.postfix()?,
807        };
808        while let Some((op, prec, len)) = self.binary_op() {
809            if prec < min {
810                break;
811            }
812            self.deeper()?;
813            for _ in 0..len {
814                self.bump();
815            }
816            let rhs = match prec {
817                PREC_POW => self.expr_bp(PREC_POW)?, // right-associative
818                _ => self.expr_bp(prec + 1)?,
819            };
820            if prec == PREC_CMP || prec == PREC_RANGE {
821                if let Some((next, next_prec, _)) = self.binary_op() {
822                    if next_prec == prec {
823                        let span = self.span();
824                        let msg = if prec == PREC_CMP {
825                            format!(
826                                "comparisons can't be chained: `{}` after `{}`",
827                                next.symbol(),
828                                op.symbol()
829                            )
830                        } else {
831                            format!("`{}` can't follow `{}` directly", next.symbol(), op.symbol())
832                        };
833                        self.error(span, msg)
834                            .help("add parentheses, or combine the tests with `and`");
835                        return Err(Failed);
836                    }
837                }
838            }
839            let span = lhs.span.to(rhs.span);
840            lhs = Expr {
841                kind: ExprKind::Binary {
842                    op,
843                    lhs: Box::new(lhs),
844                    rhs: Box::new(rhs),
845                },
846                span,
847            };
848        }
849        Ok(lhs)
850    }
851
852    /// The binary operator at the current position: (operator, precedence, token count).
853    fn binary_op(&self) -> Option<(BinOp, u8, usize)> {
854        Some(match self.peek() {
855            Tok::Or => (BinOp::Or, PREC_OR, 1),
856            Tok::And => (BinOp::And, PREC_AND, 1),
857            Tok::EqEq => (BinOp::Eq, PREC_CMP, 1),
858            Tok::NotEq => (BinOp::Ne, PREC_CMP, 1),
859            Tok::Lt => (BinOp::Lt, PREC_CMP, 1),
860            Tok::Le => (BinOp::Le, PREC_CMP, 1),
861            Tok::Gt => (BinOp::Gt, PREC_CMP, 1),
862            Tok::Ge => (BinOp::Ge, PREC_CMP, 1),
863            Tok::In => (BinOp::In, PREC_CMP, 1),
864            Tok::Not if *self.peek_at(1) == Tok::In => (BinOp::NotIn, PREC_CMP, 2),
865            Tok::DotDot => (BinOp::Range, PREC_RANGE, 1),
866            Tok::DotDotLt => (BinOp::RangeExcl, PREC_RANGE, 1),
867            Tok::Ident(word) if word == "to" => (BinOp::To, PREC_RANGE, 1),
868            Tok::Plus => (BinOp::Add, PREC_ADD, 1),
869            Tok::Minus => (BinOp::Sub, PREC_ADD, 1),
870            Tok::Star => (BinOp::Mul, PREC_MUL, 1),
871            Tok::Slash => (BinOp::Div, PREC_MUL, 1),
872            Tok::Div => (BinOp::IntDiv, PREC_MUL, 1),
873            Tok::Mod => (BinOp::Mod, PREC_MUL, 1),
874            Tok::Caret => (BinOp::Pow, PREC_POW, 1),
875            _ => return None,
876        })
877    }
878
879    fn postfix(&mut self) -> PResult<Expr> {
880        let mut expr = self.primary()?;
881        loop {
882            if matches!(self.peek(), Tok::Dot | Tok::LParen | Tok::LBracket | Tok::With) {
883                self.deeper()?;
884            }
885            match self.peek() {
886                Tok::Dot => {
887                    self.bump();
888                    let name = self.ident("a field or method name after `.`")?;
889                    if self.at(&Tok::LParen) {
890                        let args = self.call_args()?;
891                        let span = expr.span.to(self.prev_span());
892                        expr = Expr {
893                            kind: ExprKind::Method {
894                                receiver: Box::new(expr),
895                                name,
896                                args,
897                            },
898                            span,
899                        };
900                    } else {
901                        let span = expr.span.to(name.span);
902                        expr = Expr {
903                            kind: ExprKind::Field {
904                                expr: Box::new(expr),
905                                name,
906                            },
907                            span,
908                        };
909                    }
910                }
911                Tok::LParen => {
912                    let args = self.call_args()?;
913                    let span = expr.span.to(self.prev_span());
914                    expr = Expr {
915                        kind: ExprKind::Call {
916                            callee: Box::new(expr),
917                            args,
918                        },
919                        span,
920                    };
921                }
922                Tok::LBracket => {
923                    self.bump();
924                    let index = self.with_restriction(false, |p| p.expr())?;
925                    let hi = self.expect(&Tok::RBracket, "to close the index")?;
926                    let span = expr.span.to(hi);
927                    expr = Expr {
928                        kind: ExprKind::Index {
929                            expr: Box::new(expr),
930                            index: Box::new(index),
931                        },
932                        span,
933                    };
934                }
935                Tok::With => {
936                    self.bump();
937                    self.expect(&Tok::LBrace, "after `with`")?;
938                    let fields = self.record_fields()?;
939                    let span = expr.span.to(self.prev_span());
940                    expr = Expr {
941                        kind: ExprKind::With {
942                            expr: Box::new(expr),
943                            fields,
944                        },
945                        span,
946                    };
947                }
948                _ => return Ok(expr),
949            }
950        }
951    }
952
953    fn call_args(&mut self) -> PResult<Vec<Arg>> {
954        self.expect(&Tok::LParen, "")?;
955        self.with_restriction(false, |p| {
956            let mut args = Vec::new();
957            while !p.at(&Tok::RParen) {
958                let name = if matches!(p.peek(), Tok::Ident(_)) && *p.peek_at(1) == Tok::Colon {
959                    let name = p.ident("an argument name")?;
960                    p.bump(); // :
961                    Some(name)
962                } else {
963                    None
964                };
965                let value = p.expr()?;
966                args.push(Arg { name, value });
967                if !p.eat(&Tok::Comma) {
968                    break;
969                }
970            }
971            p.expect(&Tok::RParen, "to close the arguments")?;
972            Ok(args)
973        })
974    }
975
976    /// Fields of a record literal, after its `{`; consumes the closing `}`.
977    fn record_fields(&mut self) -> PResult<Vec<Field>> {
978        self.with_restriction(false, |p| {
979            let mut fields = Vec::new();
980            p.skip_newlines();
981            while !p.at(&Tok::RBrace) {
982                let name = p.ident("a field name")?;
983                let value = if p.eat(&Tok::Colon) {
984                    p.expr()?
985                } else {
986                    Expr {
987                        kind: ExprKind::Name(name.name.clone()),
988                        span: name.span,
989                    }
990                };
991                fields.push(Field { name, value });
992                p.skip_newlines();
993                if !p.eat(&Tok::Comma) {
994                    break;
995                }
996                p.skip_newlines();
997            }
998            p.skip_newlines();
999            p.expect(&Tok::RBrace, "to close the record")?;
1000            Ok(fields)
1001        })
1002    }
1003
1004    /// Does the `{` at `self.pos + offset` start a record rather than a block?
1005    fn record_ahead(&self, offset: usize, allow_empty: bool) -> bool {
1006        let mut i = offset + 1;
1007        while *self.peek_at(i) == Tok::Newline {
1008            i += 1;
1009        }
1010        match (self.peek_at(i), self.peek_at(i + 1)) {
1011            (Tok::RBrace, _) => allow_empty,
1012            (Tok::Ident(_), Tok::Colon | Tok::Comma) => true,
1013            (Tok::Ident(_), Tok::RBrace) => allow_empty,
1014            _ => false,
1015        }
1016    }
1017
1018    fn primary(&mut self) -> PResult<Expr> {
1019        let lo = self.span();
1020        let kind = match self.peek().clone() {
1021            Tok::Int(v) => {
1022                self.bump();
1023                ExprKind::Int(v)
1024            }
1025            Tok::Float(v) => {
1026                self.bump();
1027                ExprKind::Float(v)
1028            }
1029            Tok::Percent(v) => {
1030                self.bump();
1031                ExprKind::Percent(v)
1032            }
1033            Tok::Dice { count, sides } => {
1034                self.bump();
1035                ExprKind::Dice { count, sides }
1036            }
1037            Tok::True => {
1038                self.bump();
1039                ExprKind::Bool(true)
1040            }
1041            Tok::False => {
1042                self.bump();
1043                ExprKind::Bool(false)
1044            }
1045            Tok::Str(parts) => {
1046                self.bump();
1047                ExprKind::Str(self.string_segments(parts))
1048            }
1049            Tok::Ident(name) => {
1050                // A typed record literal: `Fighter { hp: 12 }`.
1051                if !self.restricted && *self.peek_at(1) == Tok::LBrace && self.record_ahead(1, true) {
1052                    let ident = self.ident("")?;
1053                    self.bump(); // {
1054                    let fields = self.record_fields()?;
1055                    ExprKind::Record {
1056                        name: Some(ident),
1057                        fields,
1058                    }
1059                } else {
1060                    self.bump();
1061                    ExprKind::Name(name)
1062                }
1063            }
1064            Tok::LParen => {
1065                self.bump();
1066                let inner = self.with_restriction(false, |p| p.expr())?;
1067                self.expect(&Tok::RParen, "to close the parenthesis")?;
1068                let span = lo.to(self.prev_span());
1069                return Ok(Expr { span, ..inner });
1070            }
1071            Tok::LBracket => return self.list_or_map(),
1072            Tok::LBrace => {
1073                if self.restricted {
1074                    self.error(lo, "expected an expression before `{`");
1075                    return Err(Failed);
1076                }
1077                if self.record_ahead(0, false) {
1078                    self.bump();
1079                    let fields = self.record_fields()?;
1080                    ExprKind::Record { name: None, fields }
1081                } else {
1082                    ExprKind::Block(self.block()?)
1083                }
1084            }
1085            Tok::If => return self.if_expr(),
1086            Tok::Chance => return self.chance_expr(),
1087            Tok::Match => return self.match_expr(),
1088            Tok::Simulate => {
1089                self.bump();
1090                ExprKind::Simulate(self.block()?)
1091            }
1092            Tok::Underscore => {
1093                self.error(lo, "`_` can only be used in patterns");
1094                return Err(Failed);
1095            }
1096            other => {
1097                self.error(lo, format!("expected an expression, found {}", other.describe()));
1098                return Err(Failed);
1099            }
1100        };
1101        Ok(Expr {
1102            kind,
1103            span: lo.to(self.prev_span()),
1104        })
1105    }
1106
1107    fn string_segments(&mut self, parts: Vec<StrPart>) -> Vec<StrSegment> {
1108        let mut segments = Vec::new();
1109        for part in parts {
1110            match part {
1111                StrPart::Lit(text) => segments.push(StrSegment::Lit(text)),
1112                StrPart::Expr { src, offset } => {
1113                    let (expr, mut diags) = parse_nested_expr(&src, offset, self.depth + 1);
1114                    self.diags.append(&mut diags);
1115                    if let Some(expr) = expr {
1116                        segments.push(StrSegment::Expr(expr));
1117                    }
1118                }
1119            }
1120        }
1121        segments
1122    }
1123
1124    fn list_or_map(&mut self) -> PResult<Expr> {
1125        let lo = self.bump().span; // [
1126        self.with_restriction(false, |p| {
1127            if p.at(&Tok::Colon) && *p.peek_at(1) == Tok::RBracket {
1128                p.bump();
1129                let hi = p.bump().span;
1130                return Ok(Expr {
1131                    kind: ExprKind::Map(Vec::new()),
1132                    span: lo.to(hi),
1133                });
1134            }
1135            if p.at(&Tok::RBracket) {
1136                let hi = p.bump().span;
1137                return Ok(Expr {
1138                    kind: ExprKind::List(Vec::new()),
1139                    span: lo.to(hi),
1140                });
1141            }
1142            let first = p.expr()?;
1143            if p.eat(&Tok::Colon) {
1144                let value = p.expr()?;
1145                let mut entries = vec![(first, value)];
1146                while p.eat(&Tok::Comma) {
1147                    if p.at(&Tok::RBracket) {
1148                        break;
1149                    }
1150                    let key = p.expr()?;
1151                    p.expect(&Tok::Colon, "between a key and its value")?;
1152                    let value = p.expr()?;
1153                    entries.push((key, value));
1154                }
1155                let hi = p.expect(&Tok::RBracket, "to close the map")?;
1156                Ok(Expr {
1157                    kind: ExprKind::Map(entries),
1158                    span: lo.to(hi),
1159                })
1160            } else {
1161                let mut items = vec![first];
1162                while p.eat(&Tok::Comma) {
1163                    if p.at(&Tok::RBracket) {
1164                        break;
1165                    }
1166                    items.push(p.expr()?);
1167                }
1168                let hi = p.expect(&Tok::RBracket, "to close the list")?;
1169                Ok(Expr {
1170                    kind: ExprKind::List(items),
1171                    span: lo.to(hi),
1172                })
1173            }
1174        })
1175    }
1176
1177    fn if_expr(&mut self) -> PResult<Expr> {
1178        let lo = self.bump().span; // if
1179        let cond = self.with_restriction(true, |p| p.expr())?;
1180        let then = self.block()?;
1181        // Allow `else` on the line after the closing `}` (but not `else =>`,
1182        // which starts the next arm of a `chance` block).
1183        let mut ahead = 0;
1184        while *self.peek_at(ahead) == Tok::Newline {
1185            ahead += 1;
1186        }
1187        if ahead > 0 && *self.peek_at(ahead) == Tok::Else && matches!(self.peek_at(ahead + 1), Tok::LBrace | Tok::If) {
1188            self.skip_newlines();
1189        }
1190        let otherwise = if self.eat(&Tok::Else) {
1191            if self.at(&Tok::If) {
1192                Some(Box::new(self.nested(|p| p.if_expr())?))
1193            } else {
1194                let block = self.block()?;
1195                Some(Box::new(Expr {
1196                    span: block.span,
1197                    kind: ExprKind::Block(block),
1198                }))
1199            }
1200        } else {
1201            None
1202        };
1203        Ok(Expr {
1204            kind: ExprKind::If {
1205                cond: Box::new(cond),
1206                then,
1207                otherwise,
1208            },
1209            span: lo.to(self.prev_span()),
1210        })
1211    }
1212
1213    /// Arms of a `chance` or `match` body: `{ arm, arm \n arm }`.
1214    fn arms<T>(&mut self, mut arm: impl FnMut(&mut Parser) -> PResult<T>) -> PResult<Vec<T>> {
1215        self.expect(&Tok::LBrace, "to start the arms")?;
1216        self.with_restriction(false, |p| {
1217            let mut arms = Vec::new();
1218            loop {
1219                while matches!(p.peek(), Tok::Newline | Tok::Comma) {
1220                    p.bump();
1221                }
1222                if p.at(&Tok::RBrace) {
1223                    break;
1224                }
1225                let start = p.pos;
1226                match arm(p) {
1227                    Ok(a) => arms.push(a),
1228                    Err(Failed) => {
1229                        // Skip to the next arm, past the brackets the failed
1230                        // one left open: the commas inside them don't
1231                        // separate arms, and stopping at each would report an
1232                        // error for every one.
1233                        let mut open = p.tokens[start..p.pos].iter().fold(0usize, |open, t| match t.tok {
1234                            Tok::LParen | Tok::LBracket | Tok::LBrace => open + 1,
1235                            Tok::RParen | Tok::RBracket | Tok::RBrace => open.saturating_sub(1),
1236                            _ => open,
1237                        });
1238                        loop {
1239                            match p.peek() {
1240                                Tok::Eof => return Err(Failed),
1241                                Tok::Newline | Tok::Comma | Tok::RBrace if open == 0 => break,
1242                                Tok::LParen | Tok::LBracket | Tok::LBrace => open += 1,
1243                                Tok::RParen | Tok::RBracket | Tok::RBrace => open = open.saturating_sub(1),
1244                                _ => {}
1245                            }
1246                            p.bump();
1247                        }
1248                        continue;
1249                    }
1250                }
1251                match p.peek() {
1252                    Tok::Newline | Tok::Comma | Tok::RBrace => {}
1253                    other => {
1254                        let found = other.describe();
1255                        let span = p.span();
1256                        p.error(span, format!("expected `,` or a new line between arms, found {found}"));
1257                        return Err(Failed);
1258                    }
1259                }
1260            }
1261            p.bump(); // }
1262            Ok(arms)
1263        })
1264    }
1265
1266    fn chance_expr(&mut self) -> PResult<Expr> {
1267        let lo = self.bump().span; // chance
1268        let arms = self.arms(|p| {
1269            let arm_lo = p.span();
1270            let weight = if p.eat(&Tok::Else) { None } else { Some(p.expr()?) };
1271            p.expect(&Tok::FatArrow, "after the arm's probability")?;
1272            let body = p.stmt()?;
1273            Ok(ChanceArm {
1274                weight,
1275                span: arm_lo.to(body.span),
1276                body,
1277            })
1278        })?;
1279        Ok(Expr {
1280            kind: ExprKind::Chance { arms },
1281            span: lo.to(self.prev_span()),
1282        })
1283    }
1284
1285    fn match_expr(&mut self) -> PResult<Expr> {
1286        let lo = self.bump().span; // match
1287        let scrutinee = self.with_restriction(true, |p| p.expr())?;
1288        let arms = self.arms(|p| {
1289            let pattern = p.pattern()?;
1290            let guard = if p.eat(&Tok::If) { Some(p.expr()?) } else { None };
1291            p.expect(&Tok::FatArrow, "after the pattern")?;
1292            let body = p.stmt()?;
1293            Ok(MatchArm {
1294                span: pattern.span.to(body.span),
1295                pattern,
1296                guard,
1297                body,
1298            })
1299        })?;
1300        Ok(Expr {
1301            kind: ExprKind::Match {
1302                scrutinee: Box::new(scrutinee),
1303                arms,
1304            },
1305            span: lo.to(self.prev_span()),
1306        })
1307    }
1308}
1309
1310/// The text of a string literal without interpolation.
1311fn plain_string(parts: &[StrPart]) -> Option<String> {
1312    let mut text = String::new();
1313    for part in parts {
1314        match part {
1315            StrPart::Lit(s) => text.push_str(s),
1316            StrPart::Expr { .. } => return None,
1317        }
1318    }
1319    Some(text)
1320}
1321
1322fn is_place(expr: &Expr) -> bool {
1323    match &expr.kind {
1324        ExprKind::Name(_) => true,
1325        ExprKind::Field { expr, .. } | ExprKind::Index { expr, .. } => is_place(expr),
1326        _ => false,
1327    }
1328}