Skip to main content

tla_syntax/
parser.rs

1use crate::ast::{Bound, Decl, Def, ExceptPath, Expr, LetInstance, Module, Param, QuantKind, Unit};
2use crate::error::{Error, Result};
3use crate::lexer::lex;
4use crate::token::{Kw, Op, Tok, Token};
5
6pub fn parse_module(src: &str) -> Result<Module> {
7    parse_module_bounded(src, DEFAULT_NESTING_LIMIT)
8}
9
10/// Parse with a nesting limit of your own, for a caller whose stack is smaller
11/// than [`DEFAULT_NESTING_LIMIT`] assumes.
12pub fn parse_module_bounded(src: &str, nesting_limit: usize) -> Result<Module> {
13    Parser::new(lex(src)?, nesting_limit).module()
14}
15
16/// Parse a bare expression, with no surrounding module. Task definitions carry
17/// predicates as strings, and they have nowhere else to live.
18pub fn parse_expression(src: &str) -> Result<Expr> {
19    parse_expression_bounded(src, DEFAULT_NESTING_LIMIT)
20}
21
22/// As [`parse_expression`], with a nesting limit of your own.
23pub fn parse_expression_bounded(src: &str, nesting_limit: usize) -> Result<Expr> {
24    let mut p = Parser::new(lex(src)?, nesting_limit);
25    let e = p.expr(0)?;
26    if matches!(p.peek(), Tok::Eof) {
27        Ok(e)
28    } else {
29        Err(p.err(format!("unexpected {:?} after the expression", p.peek())))
30    }
31}
32
33/// How deeply expressions may nest before the parser gives up on them.
34///
35/// The parser descends recursively, so without a bound a file of enough open
36/// brackets exhausts the stack — and a parser that aborts the process on bad
37/// input is worse than one that rejects it. SANY, the reference parser, has no
38/// such bound and dies with a `StackOverflowError`.
39///
40/// The figure is measured, by `examples/depth.rs`, not chosen. Across the 432
41/// modules of the public corpus the deepest expression nests 24, so this is
42/// ten times anything real. Reaching it costs about 512 KiB of stack in an
43/// optimised build and 5 MiB in an unoptimised one, which fits the 8 MiB a
44/// main thread is given but not the 2 MiB a spawned thread gets in a debug
45/// build. A caller in that position should use [`parse_module_bounded`] with
46/// a limit of its own; the cost is close to linear, at roughly 2 KiB per level
47/// optimised and 20 KiB unoptimised.
48pub const DEFAULT_NESTING_LIMIT: usize = 256;
49
50struct Parser {
51    toks: Vec<Token>,
52    pos: usize,
53    depth: usize,
54    nesting_limit: usize,
55    /// Column fences from enclosing junction lists. A token at or left of the
56    /// innermost fence ends the current conjunct; a bracketed context pushes 0
57    /// to suspend the rule while inside it.
58    fences: Vec<u32>,
59}
60
61/// What the tokens after a `[` or `{` reveal about which construct it opens.
62#[derive(Debug, Clone, Copy, PartialEq, Eq)]
63enum Shape {
64    MapsTo { bounded: bool },
65    Colon { bounded: bool },
66    Arrow,
67    Except,
68    Closed,
69}
70
71impl Parser {
72    fn new(toks: Vec<Token>, nesting_limit: usize) -> Self {
73        Self {
74            toks,
75            pos: 0,
76            depth: 0,
77            nesting_limit,
78            fences: Vec::new(),
79        }
80    }
81
82    fn peek(&self) -> &Tok {
83        &self.toks[self.pos].tok
84    }
85
86    fn peek_at(&self, offset: usize) -> &Tok {
87        let i = (self.pos + offset).min(self.toks.len() - 1);
88        &self.toks[i].tok
89    }
90
91    fn col(&self) -> u32 {
92        self.toks[self.pos].col
93    }
94
95    fn advance(&mut self) -> Tok {
96        let t = self.toks[self.pos].tok.clone();
97        if self.pos + 1 < self.toks.len() {
98            self.pos += 1;
99        }
100        t
101    }
102
103    fn eat(&mut self, tok: &Tok) -> bool {
104        if self.peek() == tok {
105            self.advance();
106            true
107        } else {
108            false
109        }
110    }
111
112    fn expect(&mut self, tok: &Tok) -> Result<()> {
113        if self.eat(tok) {
114            Ok(())
115        } else {
116            Err(self.err(format!("expected {tok:?}, found {:?}", self.peek())))
117        }
118    }
119
120    fn expect_ident(&mut self) -> Result<String> {
121        let at = self.pos;
122        match self.advance() {
123            Tok::Ident(name) => Ok(name),
124            other => Err(self.err_at(at, format!("expected an identifier, found {other:?}"))),
125        }
126    }
127
128    fn err(&self, message: impl Into<String>) -> Error {
129        self.err_at(self.pos, message)
130    }
131
132    /// Errors point at the token that caused them, which is not the current
133    /// one once it has been consumed.
134    fn err_at(&self, at: usize, message: impl Into<String>) -> Error {
135        let t = &self.toks[at];
136        Error::parse(message, t.line, t.col)
137    }
138
139    /// True when the upcoming token belongs to an enclosing junction list
140    /// rather than to the expression being parsed.
141    fn fenced(&self) -> bool {
142        self.fences.last().is_some_and(|&f| self.col() <= f)
143    }
144
145    fn bracketed<T>(&mut self, f: impl FnOnce(&mut Self) -> Result<T>) -> Result<T> {
146        self.fences.push(0);
147        let out = f(self);
148        self.fences.pop();
149        out
150    }
151
152    // ---------------------------------------------------------------- module
153
154    fn module(&mut self) -> Result<Module> {
155        while !(matches!(self.peek(), Tok::Separator)
156            && matches!(self.peek_at(1), Tok::Kw(Kw::Module)))
157        {
158            if matches!(self.peek(), Tok::Eof) {
159                return Err(self.err("no `---- MODULE ----` header found"));
160            }
161            self.advance();
162        }
163        self.advance();
164        self.module_body()
165    }
166
167    /// Everything from `MODULE` to the terminator. Modules nest, so this is
168    /// reached both at the top of a file and part-way through one.
169    fn module_body(&mut self) -> Result<Module> {
170        self.expect(&Tok::Kw(Kw::Module))?;
171        let name = self.expect_ident()?;
172        self.expect(&Tok::Separator)?;
173
174        let mut extends = Vec::new();
175        let mut units = Vec::new();
176        loop {
177            while self.eat(&Tok::Separator) {}
178            match self.peek() {
179                Tok::ModuleEnd | Tok::Eof => break,
180                Tok::Kw(Kw::Module) => {
181                    let inner = self.module_body()?;
182                    self.expect(&Tok::ModuleEnd)?;
183                    units.push(Unit::Inner(Box::new(inner)));
184                }
185                Tok::Kw(Kw::Extends) => {
186                    self.advance();
187                    extends.push(self.expect_ident()?);
188                    while self.eat(&Tok::Comma) {
189                        extends.push(self.expect_ident()?);
190                    }
191                }
192                _ => {
193                    // A unit that reads nothing would leave this loop spinning
194                    // on the same token for ever. Nothing should do that, and
195                    // if something does, saying so beats hanging.
196                    let before = self.pos;
197                    let unit = self.unit_body()?;
198                    if self.pos == before {
199                        return Err(self.err(format!(
200                            "cannot make sense of {:?}, and cannot get past it",
201                            self.peek()
202                        )));
203                    }
204                    units.push(unit);
205                }
206            }
207        }
208        Ok(Module {
209            name,
210            extends,
211            units,
212        })
213    }
214
215    fn unit_body(&mut self) -> Result<Unit> {
216        if self.at_proof() {
217            self.skip_proof();
218            return Ok(Unit::Opaque);
219        }
220        let local = self.eat(&Tok::Kw(Kw::Local));
221        match self.peek().clone() {
222            Tok::Kw(Kw::Constant) => {
223                self.advance();
224                Ok(Unit::Constants(self.decl_list()?))
225            }
226            Tok::Kw(Kw::Recursive) => {
227                self.advance();
228                Ok(Unit::Recursive(self.decl_list()?))
229            }
230            Tok::Kw(Kw::Variable) => {
231                self.advance();
232                let mut names = vec![self.expect_ident()?];
233                while self.eat(&Tok::Comma) {
234                    names.push(self.expect_ident()?);
235                }
236                Ok(Unit::Variables(names))
237            }
238            Tok::Kw(Kw::Assume) => {
239                self.advance();
240                self.skip_label();
241                match self.recover(|p| p.expr(0)) {
242                    Some(e) => Ok(Unit::Assume(e)),
243                    None => Ok(Unit::Opaque),
244                }
245            }
246            Tok::Kw(Kw::Theorem) => {
247                self.advance();
248                self.skip_label();
249                let statement = self.recover(|p| p.expr(0));
250                self.skip_proof();
251                match statement {
252                    Some(e) => Ok(Unit::Theorem(e)),
253                    None => Ok(Unit::Opaque),
254                }
255            }
256            Tok::Kw(Kw::Proof) => {
257                self.skip_proof();
258                Ok(Unit::Opaque)
259            }
260            Tok::Kw(Kw::Instance) => {
261                let (module, subs) = self.instance_tail()?;
262                Ok(Unit::Instance {
263                    name: None,
264                    module,
265                    subs,
266                })
267            }
268            // `op a == e`, and the `-.` spelling that distinguishes prefix
269            // minus from the infix one.
270            Tok::Op(op) => {
271                self.advance();
272                self.eat(&Tok::Dot);
273                let operand = self.param()?;
274                self.expect(&Tok::DefEq)?;
275                Ok(Unit::Def(Def {
276                    name: op.symbol().to_string(),
277                    params: vec![operand],
278                    body: self.expr(0)?,
279                    local,
280                }))
281            }
282            Tok::Ident(name) => self.named_definition(name, local),
283            // A specification may define something spelled like a keyword.
284            // SANY reads it and complains afterwards; so do we.
285            Tok::Kw(kw) if matches!(self.peek_at(1), Tok::DefEq) && kw.text().is_some() => {
286                let name = kw.text().expect("checked").to_string();
287                self.named_definition(name, local)
288            }
289            other => Err(self.err(format!("expected a declaration, found {other:?}"))),
290        }
291    }
292
293    /// Everything that can follow a name at the head of a definition: an
294    /// ordinary or operator definition, a function definition, an instance, or
295    /// a definition of an infix or postfix operator whose left operand this is.
296    fn named_definition(&mut self, name: String, local: bool) -> Result<Unit> {
297        self.advance();
298
299        if let Tok::Op(op) = self.peek().clone() {
300            self.advance();
301            let left = Param::value(name);
302            let params = if self.eat(&Tok::DefEq) {
303                vec![left]
304            } else {
305                let right = self.param()?;
306                self.expect(&Tok::DefEq)?;
307                vec![left, right]
308            };
309            return Ok(Unit::Def(Def {
310                name: op.symbol().to_string(),
311                params,
312                body: self.expr(0)?,
313                local,
314            }));
315        }
316
317        // `f[x \in S] == e` defines a function, and may refer to `f` inside.
318        if matches!(self.peek(), Tok::LBrack) {
319            self.advance();
320            let bounds = self.bracketed(|p| p.bounds(&Tok::RBrack))?;
321            self.expect(&Tok::RBrack)?;
322            self.expect(&Tok::DefEq)?;
323            let body = self.expr(0)?;
324            return Ok(Unit::Def(Def {
325                name,
326                params: Vec::new(),
327                body: Expr::FnDef {
328                    bounds,
329                    body: Box::new(body),
330                },
331                local,
332            }));
333        }
334
335        let params = self.opt_params()?;
336        self.expect(&Tok::DefEq)?;
337        if matches!(self.peek(), Tok::Kw(Kw::Instance)) {
338            let (module, subs) = self.instance_tail()?;
339            return Ok(Unit::Instance {
340                name: Some(name),
341                module,
342                subs,
343            });
344        }
345        Ok(Unit::Def(Def {
346            name,
347            params,
348            body: self.expr(0)?,
349            local,
350        }))
351    }
352
353    /// `THEOREM Name == ...` names the theorem; the name carries no meaning
354    /// for evaluation, so it is read and dropped.
355    fn skip_label(&mut self) {
356        if matches!(self.peek(), Tok::Ident(_)) && matches!(self.peek_at(1), Tok::DefEq) {
357            self.advance();
358            self.advance();
359        }
360    }
361
362    /// Try something, and rewind rather than fail if it does not work.
363    fn recover<T>(&mut self, f: impl FnOnce(&mut Self) -> Result<T>) -> Option<T> {
364        let mark = self.pos;
365        let fences = self.fences.len();
366        let depth = self.depth;
367        let Ok(value) = f(self) else {
368            self.pos = mark;
369            self.fences.truncate(fences);
370            self.depth = depth;
371            self.skip_to_unit();
372            return None;
373        };
374        Some(value)
375    }
376
377    /// Skip a TLAPS proof.
378    ///
379    /// Proofs are checked by a prover, not by an evaluator, so this crate
380    /// recognises them in order to get past them. A proof runs until the next
381    /// token that can only begin a new module unit.
382    fn skip_proof(&mut self) {
383        if !self.at_proof() {
384            return;
385        }
386        // Always consume the token that began the proof. A proof step can
387        // itself look like the start of a unit -- `<1> DEFINE Op == ...` --
388        // and returning without moving would leave the caller where it was.
389        self.advance();
390        while !matches!(self.peek(), Tok::Eof | Tok::ModuleEnd | Tok::Separator) {
391            if self.at_unit_start() {
392                return;
393            }
394            self.advance();
395        }
396    }
397
398    /// Advance to whatever comes after something that could not be read.
399    fn skip_to_unit(&mut self) {
400        self.advance();
401        while !matches!(self.peek(), Tok::Eof | Tok::ModuleEnd | Tok::Separator) {
402            if self.at_unit_start() {
403                return;
404            }
405            self.advance();
406        }
407    }
408
409    /// Would a new unit begin `ahead` tokens from here? Used to tell an
410    /// operator substitution from the start of an expression.
411    fn at_unit_start_after(&self, ahead: usize) -> bool {
412        self.toks.get(self.pos + ahead).is_some_and(|t| t.col == 1)
413    }
414
415    fn at_proof(&self) -> bool {
416        if matches!(self.peek(), Tok::Kw(Kw::Proof)) {
417            return true;
418        }
419        // A proof step is written `<1>2.`, which reaches us as `<`, a number
420        // or name, then `>`. It always begins a line; without that, the `<`
421        // and `>` of an ordinary comparison would look the same.
422        self.begins_line()
423            && matches!(self.peek(), Tok::Op(Op::Lt))
424            && matches!(self.peek_at(2), Tok::Op(Op::Gt))
425    }
426
427    fn begins_line(&self) -> bool {
428        self.pos == 0 || self.toks[self.pos - 1].line < self.toks[self.pos].line
429    }
430
431    /// Does a new module unit begin here? Only tokens in the first column can,
432    /// which is what keeps a proof's own keywords from ending it early.
433    fn at_unit_start(&self) -> bool {
434        if self.col() != 1 {
435            return false;
436        }
437        match self.peek() {
438            Tok::Kw(
439                Kw::Variable
440                | Kw::Constant
441                | Kw::Assume
442                | Kw::Theorem
443                | Kw::Local
444                | Kw::Instance
445                | Kw::Recursive
446                | Kw::Extends,
447            ) => true,
448            Tok::Ident(_) | Tok::Op(_) => self.at_definition(),
449            _ => false,
450        }
451    }
452
453    /// Does a definition's left-hand side start here?
454    ///
455    /// Matching the shape matters rather than merely finding a `==` further
456    /// on: a generated specification wraps expressions into the first column,
457    /// and the next definition's `==` is only a few tokens away.
458    /// Does a definition's left-hand side start here?
459    ///
460    /// The two callers want different things, and the looser answer suits
461    /// both. Ending an expression only ever turns on an operator, because a
462    /// name cannot continue one — the expression stops at a name whether or
463    /// not anything calls it a definition. Ending a *proof* turns on a name,
464    /// and there a rough answer is enough: proof steps are indented, so
465    /// anything in the first column that looks like a definition is one.
466    fn at_definition(&self) -> bool {
467        let defeq = |offset: usize| matches!(self.peek_at(offset), Tok::DefEq);
468        if matches!(self.peek(), Tok::Ident(_)) {
469            return match self.peek_at(1) {
470                // `Name ==`, and `Name(..) ==` / `Name[..] ==` without walking
471                // past brackets that may hold anything.
472                Tok::DefEq | Tok::LParen | Tok::LBrack => true,
473                // `b ^+ ==` defines a postfix operator and `a ++ b ==` an
474                // infix one. Both begin with what looks like an ordinary name.
475                Tok::Op(_) => defeq(2) || (matches!(self.peek_at(2), Tok::Ident(_)) && defeq(3)),
476                _ => false,
477            };
478        }
479        // `-. a ==`, `- a ==`, `- _ ==`. An operator *can* continue an
480        // expression, so here the whole shape has to be right.
481        let after = usize::from(matches!(self.peek_at(1), Tok::Dot)) + 1;
482        matches!(self.peek_at(after), Tok::Ident(_) | Tok::Underscore) && defeq(after + 1)
483    }
484
485    fn decl_list(&mut self) -> Result<Vec<Decl>> {
486        let mut out = vec![self.decl()?];
487        while self.eat(&Tok::Comma) {
488            out.push(self.decl()?);
489        }
490        Ok(out)
491    }
492
493    fn decl(&mut self) -> Result<Decl> {
494        if let Some(param) = self.fixity_declaration() {
495            return Ok(Decl {
496                name: param.name,
497                arity: param.arity,
498            });
499        }
500        let name = self.expect_ident()?;
501        let mut arity = 0;
502        if self.eat(&Tok::LParen) {
503            loop {
504                if !self.eat(&Tok::Underscore) {
505                    self.expect_ident()?;
506                }
507                arity += 1;
508                if !self.eat(&Tok::Comma) {
509                    break;
510                }
511            }
512            self.expect(&Tok::RParen)?;
513        }
514        Ok(Decl { name, arity })
515    }
516
517    fn opt_params(&mut self) -> Result<Vec<Param>> {
518        let mut params = Vec::new();
519        if self.eat(&Tok::LParen) {
520            loop {
521                params.push(self.param()?);
522                if !self.eat(&Tok::Comma) {
523                    break;
524                }
525            }
526            self.expect(&Tok::RParen)?;
527        }
528        Ok(params)
529    }
530
531    /// An operator declared by the shape it is written in rather than by a
532    /// name: `_+_` takes two operands around it, `-._` one after it, `_^#` one
533    /// before it. The underscores are where the operands go.
534    fn fixity_declaration(&mut self) -> Option<Param> {
535        let mark = self.pos;
536        // `_ op _` and `_ op`
537        if matches!(self.peek(), Tok::Underscore)
538            && let Tok::Op(op) = *self.peek_at(1)
539        {
540            self.advance();
541            self.advance();
542            let arity = if self.eat(&Tok::Underscore) { 2 } else { 1 };
543            return Some(Param {
544                name: op.symbol().to_string(),
545                arity,
546            });
547        }
548        // `op _`, and the `-._` spelling that marks a prefix minus.
549        if let Tok::Op(op) = *self.peek() {
550            self.advance();
551            self.eat(&Tok::Dot);
552            if self.eat(&Tok::Underscore) {
553                return Some(Param {
554                    name: op.symbol().to_string(),
555                    arity: 1,
556                });
557            }
558            self.pos = mark;
559        }
560        None
561    }
562
563    /// A formal parameter: a name, `f(_, _)` for one that is itself an
564    /// operator, or an operator written by its fixity.
565    fn param(&mut self) -> Result<Param> {
566        if let Some(operator) = self.fixity_declaration() {
567            return Ok(operator);
568        }
569        let name = self.expect_ident()?;
570        let mut arity = 0;
571        if self.eat(&Tok::LParen) {
572            loop {
573                if !self.eat(&Tok::Underscore) {
574                    self.expect_ident()?;
575                }
576                arity += 1;
577                if !self.eat(&Tok::Comma) {
578                    break;
579                }
580            }
581            self.expect(&Tok::RParen)?;
582        }
583        Ok(Param { name, arity })
584    }
585
586    fn instance_tail(&mut self) -> Result<(String, Vec<(String, Expr)>)> {
587        self.expect(&Tok::Kw(Kw::Instance))?;
588        let module = self.expect_ident()?;
589        let mut subs = Vec::new();
590        if self.eat(&Tok::Kw(Kw::With)) {
591            loop {
592                let name = self.expect_ident()?;
593                self.expect(&Tok::Gets)?;
594                let replacement = if matches!(self.peek_at(1), Tok::Comma | Tok::Eof)
595                    || self.at_unit_start_after(1)
596                {
597                    self.operator_by_symbol()
598                } else {
599                    None
600                };
601                match replacement {
602                    Some(operator) => subs.push((name, operator)),
603                    None => subs.push((name, self.expr(0)?)),
604                }
605                if !self.eat(&Tok::Comma) {
606                    break;
607                }
608            }
609        }
610        Ok((module, subs))
611    }
612
613    // ------------------------------------------------------------ expression
614
615    fn expr(&mut self, min_prec: u8) -> Result<Expr> {
616        if self.depth >= self.nesting_limit {
617            let limit = self.nesting_limit;
618            return Err(self.err(format!("expressions nest more than {limit} deep")));
619        }
620        self.depth += 1;
621        let parsed = self.expr_inner(min_prec);
622        self.depth -= 1;
623        parsed
624    }
625
626    fn expr_inner(&mut self, min_prec: u8) -> Result<Expr> {
627        // A bulleted list is an operand, not a whole expression: after
628        //
629        //     /\ TypeOK
630        //     /\ OneVote
631        //     => chosen = {}
632        //
633        // the `=>` ends the list and then applies to it.
634        let mut lhs = match *self.peek() {
635            Tok::Op(op @ (Op::And | Op::Or)) => self.junction(op)?,
636            _ => self.prefix()?,
637        };
638        loop {
639            if self.fenced() || self.at_unit_start() || self.at_proof() {
640                break;
641            }
642            let Tok::Op(op) = *self.peek() else { break };
643            let Some(prec) = op.infix_prec() else { break };
644            if prec < min_prec {
645                break;
646            }
647            self.advance();
648            let next_min = if op.is_right_assoc() { prec } else { prec + 1 };
649            let rhs = self.expr(next_min)?;
650            lhs = Expr::Binary(op, Box::new(lhs), Box::new(rhs));
651        }
652        Ok(lhs)
653    }
654
655    /// A bulleted `/\` or `\/` list, scoped by the column of its bullets.
656    fn junction(&mut self, op: Op) -> Result<Expr> {
657        let col = self.col();
658        self.fences.push(col);
659        let mut items = Vec::new();
660        while *self.peek() == Tok::Op(op) && self.col() == col {
661            self.advance();
662            items.push(self.expr(0)?);
663        }
664        self.fences.pop();
665        Ok(if op == Op::And {
666            Expr::conjunction(items)
667        } else {
668            Expr::disjunction(items)
669        })
670    }
671
672    fn prefix(&mut self) -> Result<Expr> {
673        if let Some(labelled) = self.skip_expression_label() {
674            return labelled;
675        }
676        let op = match *self.peek() {
677            Tok::Op(o @ (Op::Forall | Op::Exists | Op::TemporalForall | Op::TemporalExists)) => {
678                self.advance();
679                let kind = match o {
680                    Op::Forall => QuantKind::Forall,
681                    Op::Exists => QuantKind::Exists,
682                    Op::TemporalForall => QuantKind::TemporalForall,
683                    _ => QuantKind::TemporalExists,
684                };
685                let bounds = self.bounds(&Tok::Colon)?;
686                self.expect(&Tok::Colon)?;
687                let body = self.expr(0)?;
688                return Ok(Expr::Quant {
689                    kind,
690                    bounds,
691                    body: Box::new(body),
692                });
693            }
694            Tok::Op(o @ (Op::Not | Op::Always | Op::Eventually | Op::Minus)) => o,
695            Tok::Kw(Kw::Domain) => Op::Domain,
696            Tok::Kw(Kw::Subset) => Op::Subset,
697            Tok::Kw(Kw::Union) => Op::BigUnion,
698            Tok::Kw(Kw::Enabled) => Op::Enabled,
699            Tok::Kw(Kw::Unchanged) => Op::Unchanged,
700            _ => return self.postfix(),
701        };
702        self.advance();
703        // Every prefix operator here binds tighter than `/\`, so `[][A]_v /\ B`
704        // is a conjunction of two formulas rather than `[]` over both.
705        let operand_prec = match op {
706            Op::Minus => 11,
707            Op::Domain | Op::Subset | Op::BigUnion => 9,
708            _ => 5,
709        };
710        let operand = self.expr(operand_prec)?;
711        Ok(Expr::Unary(op, Box::new(operand)))
712    }
713
714    /// A label names a subexpression so a proof can refer to it. It has no
715    /// bearing on the expression's value, so it is dropped.
716    fn skip_expression_label(&mut self) -> Option<Result<Expr>> {
717        if !matches!(self.peek(), Tok::Ident(_)) {
718            return None;
719        }
720        let mut ahead = 1;
721        if matches!(self.peek_at(1), Tok::LParen) {
722            let mut depth = 0usize;
723            loop {
724                match self.peek_at(ahead) {
725                    Tok::LParen => depth += 1,
726                    Tok::RParen => {
727                        depth -= 1;
728                        if depth == 0 {
729                            ahead += 1;
730                            break;
731                        }
732                    }
733                    Tok::Eof => return None,
734                    _ => {}
735                }
736                ahead += 1;
737            }
738        }
739        if !matches!(self.peek_at(ahead), Tok::ColonColon) {
740            return None;
741        }
742        for _ in 0..=ahead {
743            self.advance();
744        }
745        Some(self.expr(0))
746    }
747
748    fn postfix(&mut self) -> Result<Expr> {
749        let e = self.primary()?;
750        self.continue_postfix(e)
751    }
752
753    fn continue_postfix(&mut self, head: Expr) -> Result<Expr> {
754        let mut e = head;
755        loop {
756            if self.fenced() || self.at_unit_start() {
757                break;
758            }
759            match self.peek() {
760                Tok::Prime => {
761                    self.advance();
762                    e = Expr::Prime(Box::new(e));
763                }
764                Tok::Op(op) if op.is_postfix() => {
765                    let op = *op;
766                    self.advance();
767                    e = Expr::Unary(op, Box::new(e));
768                }
769                Tok::Dot => {
770                    self.advance();
771                    e = Expr::Field(Box::new(e), self.expect_ident()?);
772                }
773                Tok::LBrack => {
774                    self.advance();
775                    let args = self.bracketed(|p| p.expr_list(&Tok::RBrack))?;
776                    self.expect(&Tok::RBrack)?;
777                    e = Expr::FnApply(Box::new(e), args);
778                }
779                Tok::LParen if matches!(e, Expr::Ident(_)) => {
780                    self.advance();
781                    let args = self.bracketed(|p| p.expr_list(&Tok::RParen))?;
782                    self.expect(&Tok::RParen)?;
783                    e = Expr::Apply(Box::new(e), args);
784                }
785                Tok::Bang => {
786                    let instance = match &e {
787                        Expr::Ident(name) => name.clone(),
788                        Expr::Qualified { instance, name, .. } => format!("{instance}!{name}"),
789                        Expr::Apply(head, _) => head.to_string(),
790                        _ => return Err(self.err("`!` must follow an instance name")),
791                    };
792                    self.advance();
793                    // `Inv!2` picks the second conjunct of `Inv`, `Inv!:` its
794                    // whole body, `Inv!<<` and `Inv!>>` the sides of a tuple,
795                    // and `Inv!@` the subject of an EXCEPT. All are proof
796                    // notation for pointing inside a definition, not values.
797                    let name = match self.peek().clone() {
798                        Tok::Num(n) => {
799                            self.advance();
800                            n.to_string()
801                        }
802                        Tok::Op(op) => {
803                            self.advance();
804                            op.symbol().to_string()
805                        }
806                        Tok::At => {
807                            self.advance();
808                            "@".to_string()
809                        }
810                        Tok::LTup => {
811                            self.advance();
812                            "<<".to_string()
813                        }
814                        Tok::RTup => {
815                            self.advance();
816                            ">>".to_string()
817                        }
818                        Tok::Colon => {
819                            self.advance();
820                            String::new()
821                        }
822                        _ => self.expect_ident()?,
823                    };
824                    let mut args = Vec::new();
825                    if self.eat(&Tok::LParen) {
826                        args = self.bracketed(|p| p.expr_list(&Tok::RParen))?;
827                        self.expect(&Tok::RParen)?;
828                    }
829                    e = Expr::Qualified {
830                        instance,
831                        name,
832                        args,
833                    };
834                }
835                _ => break,
836            }
837        }
838        Ok(e)
839    }
840
841    fn expr_list(&mut self, close: &Tok) -> Result<Vec<Expr>> {
842        let mut out = Vec::new();
843        if self.peek() == close {
844            return Ok(out);
845        }
846        loop {
847            // `FoldSet(+, 0, S)` passes the operator itself, not an
848            // application of it. So does `apply(', v')`, where the operator is
849            // the prime.
850            if (matches!(self.peek_at(1), Tok::Comma) || self.peek_at(1) == close)
851                && let Some(operator) = self.operator_by_symbol()
852            {
853                out.push(operator);
854                if !self.eat(&Tok::Comma) {
855                    return Ok(out);
856                }
857                continue;
858            }
859            out.push(self.expr(0)?);
860            if !self.eat(&Tok::Comma) {
861                return Ok(out);
862            }
863        }
864    }
865
866    /// An operator standing where a value would, named by its own symbol.
867    fn operator_by_symbol(&mut self) -> Option<Expr> {
868        let symbol = match self.peek() {
869            Tok::Op(op) => op.symbol(),
870            Tok::Prime => "'",
871            Tok::Kw(Kw::Enabled) => "ENABLED",
872            Tok::Kw(Kw::Unchanged) => "UNCHANGED",
873            Tok::Kw(Kw::Domain) => "DOMAIN",
874            Tok::Kw(Kw::Subset) => "SUBSET",
875            Tok::Kw(Kw::Union) => "UNION",
876            _ => return None,
877        };
878        self.advance();
879        Some(Expr::Ident(symbol.to_string()))
880    }
881
882    fn primary(&mut self) -> Result<Expr> {
883        let at = self.pos;
884        match self.advance() {
885            Tok::Num(n) => Ok(Expr::Num(n)),
886            Tok::Decimal(text) => Ok(Expr::Decimal(text)),
887            Tok::Str(s) => Ok(Expr::Str(s)),
888            Tok::Kw(Kw::True) => Ok(Expr::Bool(true)),
889            Tok::Kw(Kw::False) => Ok(Expr::Bool(false)),
890            Tok::Ident(name) => Ok(Expr::Ident(name)),
891            Tok::At => Ok(Expr::At),
892            Tok::LParen => {
893                let e = self.bracketed(|p| p.expr(0))?;
894                self.expect(&Tok::RParen)?;
895                Ok(e)
896            }
897            Tok::LTup => self.tuple_or_action(),
898            Tok::LBrace => self.brace_form(),
899            Tok::LBrack => self.bracket_form(),
900            Tok::Kw(Kw::If) => {
901                let cond = self.expr(0)?;
902                self.expect(&Tok::Kw(Kw::Then))?;
903                let then = self.expr(0)?;
904                self.expect(&Tok::Kw(Kw::Else))?;
905                let otherwise = self.expr(0)?;
906                Ok(Expr::If {
907                    cond: Box::new(cond),
908                    then: Box::new(then),
909                    otherwise: Box::new(otherwise),
910                })
911            }
912            Tok::Kw(Kw::Let) => self.let_form(),
913            Tok::Kw(Kw::Choose) => {
914                let mut bounds = self.bounds(&Tok::Colon)?;
915                self.expect(&Tok::Colon)?;
916                let body = self.expr(0)?;
917                if bounds.len() != 1 {
918                    return Err(self.err("CHOOSE takes exactly one bound variable"));
919                }
920                Ok(Expr::Choose {
921                    bound: Box::new(bounds.remove(0)),
922                    body: Box::new(body),
923                })
924            }
925            Tok::Kw(Kw::Case) => self.case_form(),
926            Tok::Kw(Kw::Lambda) => {
927                let mut params = vec![self.param()?];
928                while self.eat(&Tok::Comma) {
929                    params.push(self.param()?);
930                }
931                self.expect(&Tok::Colon)?;
932                Ok(Expr::Lambda {
933                    params,
934                    body: Box::new(self.expr(0)?),
935                })
936            }
937            // `WF_vars` arrives as one word, but `WF_<<a, b>>` leaves the
938            // subscript for the parser to read.
939            Tok::Fair { strong, subscript } => {
940                let subscript = if subscript.is_empty() {
941                    self.postfix()?
942                } else {
943                    Expr::Ident(subscript)
944                };
945                self.expect(&Tok::LParen)?;
946                let action = self.bracketed(|p| p.expr(0))?;
947                self.expect(&Tok::RParen)?;
948                Ok(Expr::Fairness {
949                    strong,
950                    subscript: Box::new(subscript),
951                    action: Box::new(action),
952                })
953            }
954            other => Err(self.err_at(at, format!("expected an expression, found {other:?}"))),
955        }
956    }
957
958    fn let_form(&mut self) -> Result<Expr> {
959        let mut defs = Vec::new();
960        let mut instances = Vec::new();
961        while !matches!(self.peek(), Tok::Kw(Kw::In)) {
962            if self.eat(&Tok::Kw(Kw::Recursive)) {
963                self.decl_list()?;
964                continue;
965            }
966            if matches!(self.peek(), Tok::Kw(Kw::Instance)) {
967                let (module, subs) = self.instance_tail()?;
968                instances.push(LetInstance {
969                    name: None,
970                    module,
971                    subs,
972                });
973                continue;
974            }
975            let Tok::Ident(name) = self.peek().clone() else {
976                return Err(self.err("expected a definition inside LET"));
977            };
978            match self.named_definition(name, false)? {
979                Unit::Def(def) => defs.push(def),
980                Unit::Instance { name, module, subs } => {
981                    instances.push(LetInstance { name, module, subs });
982                }
983                _ => return Err(self.err("only definitions may appear inside LET")),
984            }
985        }
986        self.advance();
987        let body = self.expr(0)?;
988        Ok(Expr::Let {
989            defs,
990            instances,
991            body: Box::new(body),
992        })
993    }
994
995    fn case_form(&mut self) -> Result<Expr> {
996        let mut arms = Vec::new();
997        let mut other = None;
998        loop {
999            if self.eat(&Tok::Kw(Kw::Other)) {
1000                self.expect(&Tok::Arrow)?;
1001                other = Some(Box::new(self.expr(0)?));
1002            } else {
1003                let guard = self.expr(0)?;
1004                self.expect(&Tok::Arrow)?;
1005                arms.push((guard, self.expr(0)?));
1006            }
1007            if other.is_some() || !self.eat(&Tok::Op(Op::Always)) {
1008                return Ok(Expr::Case { arms, other });
1009            }
1010        }
1011    }
1012
1013    fn tuple_or_action(&mut self) -> Result<Expr> {
1014        let items = self.bracketed(|p| p.expr_list(&Tok::RTup))?;
1015        self.expect(&Tok::RTup)?;
1016        if self.at_subscript() {
1017            if items.len() != 1 {
1018                return Err(self.err("`<<A>>_v` takes a single action"));
1019            }
1020            let subscript = self.subscript()?;
1021            return Ok(Expr::ActionAngle {
1022                action: Box::new(items.into_iter().next().expect("length checked")),
1023                subscript: Box::new(subscript),
1024            });
1025        }
1026        Ok(Expr::Tuple(items))
1027    }
1028
1029    /// Is a `_v` subscript coming? Identifiers may begin with an underscore,
1030    /// so `[A]_vars` reaches the parser as one token, not two.
1031    fn at_subscript(&self) -> bool {
1032        match self.peek() {
1033            Tok::Underscore => true,
1034            Tok::Ident(name) => name.starts_with('_'),
1035            _ => false,
1036        }
1037    }
1038
1039    /// The `v` of `[A]_v`, parsed tightly so it cannot swallow what follows.
1040    fn subscript(&mut self) -> Result<Expr> {
1041        if let Tok::Ident(name) = self.peek()
1042            && let Some(rest) = name.strip_prefix('_')
1043            && !rest.is_empty()
1044        {
1045            let head = Expr::Ident(rest.to_string());
1046            self.advance();
1047            // The name may carry on -- `[A]_Inst!vars` -- so whatever follows
1048            // it still belongs to the subscript.
1049            return self.continue_postfix(head);
1050        }
1051        self.expect(&Tok::Underscore)?;
1052        self.postfix()
1053    }
1054
1055    /// `{a, b}`, `{x \in S : P}` and `{e : x \in S}` are told apart by what
1056    /// follows their first expression rather than by scanning ahead: a
1057    /// `CHOOSE` inside the braces has a `:` of its own, and a lookahead
1058    /// cannot tell whose it is.
1059    fn brace_form(&mut self) -> Result<Expr> {
1060        if self.eat(&Tok::RBrace) {
1061            return Ok(Expr::SetEnum(Vec::new()));
1062        }
1063        self.bracketed(|p| {
1064            let first = p.expr(0)?;
1065            if p.eat(&Tok::Colon) {
1066                if let Some(bound) = as_bound(&first) {
1067                    let pred = p.expr(0)?;
1068                    p.expect(&Tok::RBrace)?;
1069                    return Ok(Expr::SetFilter {
1070                        bound: Box::new(bound),
1071                        pred: Box::new(pred),
1072                    });
1073                }
1074                let bounds = p.bounds(&Tok::RBrace)?;
1075                p.expect(&Tok::RBrace)?;
1076                return Ok(Expr::SetMap {
1077                    expr: Box::new(first),
1078                    bounds,
1079                });
1080            }
1081            let mut items = vec![first];
1082            while p.eat(&Tok::Comma) {
1083                items.push(p.expr(0)?);
1084            }
1085            p.expect(&Tok::RBrace)?;
1086            Ok(Expr::SetEnum(items))
1087        })
1088    }
1089
1090    fn bracket_form(&mut self) -> Result<Expr> {
1091        let shape = if self.subscript_follows_bracket() {
1092            Shape::Closed
1093        } else {
1094            self.shape()
1095        };
1096        let inner = self.bracketed(|p| match shape {
1097            Shape::MapsTo { bounded: true } => {
1098                let bounds = p.bounds(&Tok::MapsTo)?;
1099                p.expect(&Tok::MapsTo)?;
1100                let body = p.expr(0)?;
1101                p.expect(&Tok::RBrack)?;
1102                Ok(Expr::FnDef {
1103                    bounds,
1104                    body: Box::new(body),
1105                })
1106            }
1107            Shape::MapsTo { bounded: false } => {
1108                let fields = p.field_list(&Tok::MapsTo)?;
1109                p.expect(&Tok::RBrack)?;
1110                Ok(Expr::Record(fields))
1111            }
1112            Shape::Colon { .. } => {
1113                let fields = p.field_list(&Tok::Colon)?;
1114                p.expect(&Tok::RBrack)?;
1115                Ok(Expr::RecordSet(fields))
1116            }
1117            Shape::Arrow => {
1118                let domain = p.expr(0)?;
1119                p.expect(&Tok::Arrow)?;
1120                let range = p.expr(0)?;
1121                p.expect(&Tok::RBrack)?;
1122                Ok(Expr::FnSet {
1123                    domain: Box::new(domain),
1124                    range: Box::new(range),
1125                })
1126            }
1127            Shape::Except => {
1128                let base = p.expr(0)?;
1129                p.expect(&Tok::Kw(Kw::Except))?;
1130                let updates = p.except_updates()?;
1131                p.expect(&Tok::RBrack)?;
1132                Ok(Expr::Except {
1133                    base: Box::new(base),
1134                    updates,
1135                })
1136            }
1137            Shape::Closed => {
1138                let action = p.expr(0)?;
1139                p.expect(&Tok::RBrack)?;
1140                Ok(action)
1141            }
1142        })?;
1143        if shape != Shape::Closed {
1144            return Ok(inner);
1145        }
1146        let subscript = self.subscript()?;
1147        Ok(Expr::ActionBox {
1148            action: Box::new(inner),
1149            subscript: Box::new(subscript),
1150        })
1151    }
1152
1153    fn field_list(&mut self, sep: &Tok) -> Result<Vec<(String, Expr)>> {
1154        let mut out = Vec::new();
1155        loop {
1156            let name = self.expect_ident()?;
1157            self.expect(sep)?;
1158            out.push((name, self.expr(0)?));
1159            if !self.eat(&Tok::Comma) {
1160                return Ok(out);
1161            }
1162        }
1163    }
1164
1165    fn except_updates(&mut self) -> Result<Vec<(Vec<ExceptPath>, Expr)>> {
1166        let mut out = Vec::new();
1167        loop {
1168            self.expect(&Tok::Bang)?;
1169            let mut path = Vec::new();
1170            loop {
1171                if self.eat(&Tok::LBrack) {
1172                    let indices = self.bracketed(|p| p.expr_list(&Tok::RBrack))?;
1173                    self.expect(&Tok::RBrack)?;
1174                    path.push(ExceptPath::Index(if indices.len() == 1 {
1175                        indices.into_iter().next().expect("length checked")
1176                    } else {
1177                        Expr::Tuple(indices)
1178                    }));
1179                } else if self.eat(&Tok::Dot) {
1180                    path.push(ExceptPath::Field(self.expect_ident()?));
1181                } else {
1182                    break;
1183                }
1184            }
1185            if path.is_empty() {
1186                return Err(self.err("EXCEPT update needs a `[...]` or `.field` path"));
1187            }
1188            self.expect(&Tok::Op(Op::Eq))?;
1189            out.push((path, self.expr(0)?));
1190            if !self.eat(&Tok::Comma) {
1191                return Ok(out);
1192            }
1193        }
1194    }
1195
1196    fn bounds(&mut self, terminator: &Tok) -> Result<Vec<Bound>> {
1197        let mut out = Vec::new();
1198        loop {
1199            let mut destructure = false;
1200            let mut names = Vec::new();
1201            if self.eat(&Tok::LTup) {
1202                destructure = true;
1203                loop {
1204                    names.push(self.expect_ident()?);
1205                    if !self.eat(&Tok::Comma) {
1206                        break;
1207                    }
1208                }
1209                self.expect(&Tok::RTup)?;
1210            } else {
1211                names.push(self.expect_ident()?);
1212                while *self.peek() == Tok::Comma && matches!(self.peek_at(1), Tok::Ident(_)) {
1213                    self.advance();
1214                    names.push(self.expect_ident()?);
1215                }
1216            }
1217            let domain = if self.eat(&Tok::Op(Op::In)) {
1218                Some(self.expr(0)?)
1219            } else {
1220                None
1221            };
1222            out.push(Bound {
1223                names,
1224                domain,
1225                destructure,
1226            });
1227            if self.peek() == terminator || !self.eat(&Tok::Comma) {
1228                return Ok(out);
1229            }
1230        }
1231    }
1232
1233    /// Is this `[A]_v`? A quantifier inside the brackets puts a `:` where a
1234    /// record set would have one, so the only reliable sign is the subscript
1235    /// after the closing bracket.
1236    fn subscript_follows_bracket(&self) -> bool {
1237        let mut depth = 0usize;
1238        for (offset, t) in self.toks[self.pos..].iter().enumerate() {
1239            match &t.tok {
1240                Tok::LParen | Tok::LBrack | Tok::LBrace | Tok::LTup => depth += 1,
1241                Tok::RBrack if depth == 0 => {
1242                    return match &self.peek_at(offset + 1) {
1243                        Tok::Underscore => true,
1244                        Tok::Ident(name) => name.starts_with('_'),
1245                        _ => false,
1246                    };
1247                }
1248                Tok::RParen | Tok::RBrack | Tok::RBrace | Tok::RTup => {
1249                    if depth == 0 {
1250                        return false;
1251                    }
1252                    depth -= 1;
1253                }
1254                Tok::Eof => return false,
1255                _ => {}
1256            }
1257        }
1258        false
1259    }
1260
1261    /// Look ahead past a just-consumed `[` or `{` to the first delimiter that
1262    /// identifies the construct, ignoring anything nested inside brackets.
1263    fn shape(&self) -> Shape {
1264        let mut depth = 0usize;
1265        let mut saw_in = false;
1266        for t in &self.toks[self.pos..] {
1267            match &t.tok {
1268                Tok::LParen | Tok::LBrack | Tok::LBrace | Tok::LTup => depth += 1,
1269                Tok::RParen | Tok::RBrack | Tok::RBrace | Tok::RTup => {
1270                    if depth == 0 {
1271                        return Shape::Closed;
1272                    }
1273                    depth -= 1;
1274                }
1275                Tok::Eof => break,
1276                _ if depth > 0 => {}
1277                Tok::Op(Op::In) => saw_in = true,
1278                Tok::MapsTo => return Shape::MapsTo { bounded: saw_in },
1279                Tok::Colon => return Shape::Colon { bounded: saw_in },
1280                Tok::Arrow => return Shape::Arrow,
1281                Tok::Kw(Kw::Except) => return Shape::Except,
1282                _ => {}
1283            }
1284        }
1285        Shape::Closed
1286    }
1287}
1288
1289/// Read `x \in S` back as the bound it is, so `{x \in S : P}` can be told from
1290/// `{e : x \in S}` once the first expression has already been parsed.
1291fn as_bound(e: &Expr) -> Option<Bound> {
1292    let Expr::Binary(Op::In, lhs, domain) = e else {
1293        return None;
1294    };
1295    let (names, destructure) = match &**lhs {
1296        Expr::Ident(name) => (vec![name.clone()], false),
1297        Expr::Tuple(items) => {
1298            let mut names = Vec::with_capacity(items.len());
1299            for item in items {
1300                let Expr::Ident(name) = item else {
1301                    return None;
1302                };
1303                names.push(name.clone());
1304            }
1305            (names, true)
1306        }
1307        _ => return None,
1308    };
1309    Some(Bound {
1310        names,
1311        domain: Some((**domain).clone()),
1312        destructure,
1313    })
1314}