cas_parser/parser/ast/
loop_expr.rs

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