cas_parser/parser/ast/
while_expr.rs

1use crate::parser::{
2    ast::expr::Expr,
3    error::{kind, Error},
4    fmt::Latex,
5    garbage::Garbage,
6    keyword::{Then, While as WhileToken},
7    Parse,
8    Parser,
9};
10use std::{fmt, ops::Range};
11
12#[cfg(feature = "serde")]
13use serde::{Deserialize, Serialize};
14
15/// A `while` loop expression, such as `while x < 10 then x += 1`. The loop body is executed
16/// repeatedly as long as the outer condition is true.
17#[derive(Debug, Clone, PartialEq)]
18#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
19pub struct While {
20    /// The condition that must be true for the loop body to be executed.
21    pub condition: Box<Expr>,
22
23    /// The body of the loop.
24    pub body: Box<Expr>,
25
26    /// The region of the source code that this expression was parsed from.
27    pub span: Range<usize>,
28
29    /// The span of the `while` keyword.
30    pub while_span: Range<usize>,
31
32    /// The span of the `then` keyword.
33    pub then_span: Range<usize>,
34}
35
36impl While {
37    /// Returns the span of the `while` loop expression.
38    pub fn span(&self) -> Range<usize> {
39        self.span.clone()
40    }
41}
42
43impl<'source> Parse<'source> for While {
44    fn std_parse(
45        input: &mut Parser<'source>,
46        recoverable_errors: &mut Vec<Error>
47    ) -> Result<Self, Vec<Error>> {
48        let while_token = input.try_parse::<WhileToken>().forward_errors(recoverable_errors)?;
49        let condition = input.try_parse().forward_errors(recoverable_errors)?;
50        let (then_token, body) = 'then: {
51            let then_token = match input.try_parse::<Then>().forward_errors(recoverable_errors) {
52                Ok(token) => token,
53                Err(_) => {
54                    // TODO: add error for missing `then` keyword
55                    recoverable_errors.push(Error::new(
56                        vec![while_token.span.clone(), input.span()],
57                        kind::MissingIfKeyword {
58                            keyword: "then",
59                        },
60                    ));
61                    break 'then Garbage::garbage();
62                },
63            };
64            input.try_parse_with_state::<_, Expr>(|state| {
65                state.allow_loop_control = true;
66            })
67                .forward_errors(recoverable_errors)
68                .map(|expr| (then_token, expr))
69                .unwrap_or_else(|_| {
70                    recoverable_errors.push(Error::new(
71                        vec![while_token.span.clone(), input.span()],
72                        kind::MissingIfBranch {
73                            keyword: "then",
74                        },
75                    ));
76                    Garbage::garbage()
77                })
78        };
79        let span = while_token.span.start..body.span().end;
80
81        Ok(Self {
82            condition: Box::new(condition),
83            body: Box::new(body),
84            span,
85            while_span: while_token.span,
86            then_span: then_token.span,
87        })
88    }
89}
90
91impl std::fmt::Display for While {
92    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
93        write!(f, "while {} then {}", self.condition, self.body)
94    }
95}
96
97impl Latex for While {
98    fn fmt_latex(&self, f: &mut fmt::Formatter) -> fmt::Result {
99        write!(f, "\\text{{while }}")?;
100        self.condition.fmt_latex(f)?;
101        write!(f, "\\text{{ then }}")?;
102        self.body.fmt_latex(f)?;
103        Ok(())
104    }
105}