cas_parser/parser/ast/
branch.rs

1use cas_error::Error;
2use crate::parser::{
3    ast::expr::Expr,
4    error::{OfOutsideSumProduct, ThenOutsideIfWhileFor},
5    fmt::Latex,
6    keyword::{Of as OfToken, Then as ThenToken},
7    Parse,
8    Parser,
9};
10use std::{fmt, ops::Range};
11
12#[cfg(feature = "serde")]
13use serde::{Deserialize, Serialize};
14
15/// A `then` expression, such as `then x += 1`.
16///
17/// A `then` expression simply evaluates the expression that follows the `then` keyword, and
18/// returns its result. It is only valid in the context of `if` and `while` expressions, and is
19/// used to concisely write one-line `if` and `while` expressions, such as `if x < 10 then x += 1`.
20#[derive(Debug, Clone, PartialEq, Eq)]
21#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
22pub struct Then {
23    /// The expression to evaluate.
24    pub expr: 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 `then` keyword.
30    pub then_span: Range<usize>,
31}
32
33impl Then {
34    /// Returns the span of the `then` expression.
35    pub fn span(&self) -> Range<usize> {
36        self.span.clone()
37    }
38}
39
40impl<'source> Parse<'source> for Then {
41    fn std_parse(
42        input: &mut Parser<'source>,
43        recoverable_errors: &mut Vec<Error>
44    ) -> Result<Self, Vec<Error>> {
45        let then_token = input.try_parse::<ThenToken>().forward_errors(recoverable_errors)?;
46
47        if !input.state.allow_then {
48            recoverable_errors.push(Error::new(
49                vec![then_token.span.clone()],
50                ThenOutsideIfWhileFor,
51            ));
52        }
53
54        // this is to catch stuff like `if true then then then then then 1`
55        let body = input.try_parse_with_state::<_, Expr>(|input| {
56            input.allow_then = false;
57        }).forward_errors(recoverable_errors)?;
58        let span = then_token.span.start..body.span().end;
59
60        Ok(Self {
61            expr: Box::new(body),
62            span,
63            then_span: then_token.span,
64        })
65    }
66}
67
68impl std::fmt::Display for Then {
69    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
70        write!(f, "then {}", self.expr)
71    }
72}
73
74impl Latex for Then {
75    fn fmt_latex(&self, f: &mut fmt::Formatter) -> fmt::Result {
76        write!(f, "\\text{{ then }}")?;
77        self.expr.fmt_latex(f)?;
78        Ok(())
79    }
80}
81
82/// An `of` expression, such as `of x`.
83///
84/// An `of` expression's body is repeatedly evaluated during summation or product expressions,
85/// with a specific variable in the `of` expression taking on each value in the range of the
86/// summation or product. It is only valid in the context of `sum` and `product` expressions, and
87/// is used to concisely write one-line `sum` and `product` expressions, such as `sum x in 1..10 of
88/// x`.
89#[derive(Debug, Clone, PartialEq, Eq)]
90#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
91pub struct Of {
92    /// The expression to evaluate.
93    pub expr: Box<Expr>,
94
95    /// The region of the source code that this expression was parsed from.
96    pub span: Range<usize>,
97
98    /// The span of the `of` keyword.
99    pub of_span: Range<usize>,
100}
101
102impl Of {
103    /// Returns the span of the `of` expression.
104    pub fn span(&self) -> Range<usize> {
105        self.span.clone()
106    }
107}
108
109impl<'source> Parse<'source> for Of {
110    fn std_parse(
111        input: &mut Parser<'source>,
112        recoverable_errors: &mut Vec<Error>
113    ) -> Result<Self, Vec<Error>> {
114        let of_token = input.try_parse::<OfToken>().forward_errors(recoverable_errors)?;
115
116        if !input.state.allow_of {
117            recoverable_errors.push(Error::new(
118                vec![of_token.span.clone()],
119                OfOutsideSumProduct,
120            ));
121        }
122
123        // this is to catch stuff like `sum x in 1..10 of of of of of 1`
124        let body = input.try_parse_with_state::<_, Expr>(|input| {
125            input.allow_of = false;
126        }).forward_errors(recoverable_errors)?;
127        let span = of_token.span.start..body.span().end;
128
129        Ok(Self {
130            expr: Box::new(body),
131            span,
132            of_span: of_token.span,
133        })
134    }
135}
136
137impl std::fmt::Display for Of {
138    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
139        write!(f, "of {}", self.expr)
140    }
141}
142
143impl Latex for Of {
144    fn fmt_latex(&self, f: &mut fmt::Formatter) -> fmt::Result {
145        write!(f, "\\text{{ of }}")?;
146        self.expr.fmt_latex(f)?;
147        Ok(())
148    }
149}