cas_parser/parser/ast/
loop_expr.rs

1use crate::parser::{
2    ast::expr::Expr,
3    error::{kind::{BreakOutsideLoop, ContinueOutsideLoop}, Error},
4    fmt::Latex,
5    keyword::{Break as BreakToken, Continue as ContinueToken, Loop as LoopToken},
6    Parse,
7    Parser,
8};
9use std::{fmt, ops::Range};
10
11#[cfg(feature = "serde")]
12use serde::{Deserialize, Serialize};
13
14/// A `loop` expression, as in `loop { ... }`. The code inside the braces is
15/// evaluated repeatedly until a `break` expression is encountered.
16#[derive(Debug, Clone, PartialEq)]
17#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
18pub struct Loop {
19    /// The body of the loop.
20    pub body: Box<Expr>,
21
22    /// The region of the source code that this expression was parsed from.
23    pub span: Range<usize>,
24
25    /// The span of the `loop` keyword.
26    pub loop_span: Range<usize>,
27}
28
29impl Loop {
30    /// Returns the span of the `loop` expression.
31    pub fn span(&self) -> Range<usize> {
32        self.span.clone()
33    }
34}
35
36impl<'source> Parse<'source> for Loop {
37    fn std_parse(
38        input: &mut Parser<'source>,
39        recoverable_errors: &mut Vec<Error>
40    ) -> Result<Self, Vec<Error>> {
41        let loop_token = input.try_parse::<LoopToken>().forward_errors(recoverable_errors)?;
42
43        let body = input.try_parse_with_state::<_, Expr>(|state| {
44            state.allow_loop_control = true;
45        }).forward_errors(recoverable_errors)?;
46        let span = loop_token.span.start..body.span().end;
47
48        Ok(Self {
49            body: Box::new(body),
50            span,
51            loop_span: loop_token.span,
52        })
53    }
54}
55
56impl std::fmt::Display for Loop {
57    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
58        write!(f, "loop {}", self.body)
59    }
60}
61
62impl Latex for Loop {
63    fn fmt_latex(&self, f: &mut fmt::Formatter) -> fmt::Result {
64        write!(f, "\\text{{loop }}")?;
65        self.body.fmt_latex(f)?;
66        Ok(())
67    }
68}
69
70/// A `break` expression, used to exit a loop, optionally with a value.
71#[derive(Debug, Clone, PartialEq)]
72#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
73pub struct Break {
74    /// The value to return from the loop.
75    pub value: Option<Box<Expr>>,
76
77    /// The region of the source code that this expression was parsed from.
78    pub span: Range<usize>,
79
80    /// The span of the `break` keyword.
81    pub break_span: Range<usize>,
82}
83
84impl Break {
85    /// Returns the span of the `break` expression.
86    pub fn span(&self) -> Range<usize> {
87        self.span.clone()
88    }
89}
90
91impl<'source> Parse<'source> for Break {
92    fn std_parse(
93        input: &mut Parser<'source>,
94        recoverable_errors: &mut Vec<Error>
95    ) -> Result<Self, Vec<Error>> {
96        let break_token = input.try_parse::<BreakToken>().forward_errors(recoverable_errors)?;
97        let value = if let Ok(value) = input.try_parse::<Expr>().forward_errors(recoverable_errors) {
98            Some(value)
99        } else {
100            None
101        };
102        let span = if let Some(value) = &value {
103            break_token.span.start..value.span().end
104        } else {
105            break_token.span.clone()
106        };
107
108        // `break` expressions can only be used inside loops
109        if !input.state.allow_loop_control {
110            recoverable_errors.push(Error::new(
111                vec![break_token.span.clone()],
112                BreakOutsideLoop,
113            ));
114        }
115
116        Ok(Self {
117            value: value.map(Box::new),
118            span,
119            break_span: break_token.span,
120        })
121    }
122}
123
124impl std::fmt::Display for Break {
125    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
126        write!(f, "break")?;
127        if let Some(value) = &self.value {
128            write!(f, " {}", value)?;
129        }
130        Ok(())
131    }
132}
133
134impl Latex for Break {
135    fn fmt_latex(&self, f: &mut fmt::Formatter) -> fmt::Result {
136        write!(f, "\\text{{break }}")?;
137        if let Some(value) = &self.value {
138            value.fmt_latex(f)?;
139        }
140        Ok(())
141    }
142}
143
144/// A `continue` expression, used to skip the rest of a loop iteration.
145#[derive(Debug, Clone, PartialEq)]
146#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
147pub struct Continue {
148    /// The region of the source code that this expression was parsed from.
149    pub span: Range<usize>,
150}
151
152impl Continue {
153    /// Returns the span of the `continue` expression.
154    pub fn span(&self) -> Range<usize> {
155        self.span.clone()
156    }
157}
158
159impl<'source> Parse<'source> for Continue {
160    fn std_parse(
161        input: &mut Parser<'source>,
162        recoverable_errors: &mut Vec<Error>
163    ) -> Result<Self, Vec<Error>> {
164        let continue_token = input.try_parse::<ContinueToken>()
165            .forward_errors(recoverable_errors)?;
166
167        // `continue` expressions can only be used inside loops
168        if !input.state.allow_loop_control {
169            recoverable_errors.push(Error::new(
170                vec![continue_token.span.clone()],
171                ContinueOutsideLoop,
172            ));
173        }
174
175        Ok(Self {
176            span: continue_token.span,
177        })
178    }
179}
180
181impl std::fmt::Display for Continue {
182    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
183        write!(f, "continue")
184    }
185}
186
187impl Latex for Continue {
188    fn fmt_latex(&self, f: &mut fmt::Formatter) -> fmt::Result {
189        write!(f, "\\text{{continue}}")
190    }
191}