cas_parser/parser/ast/
sum.rs

1use cas_error::Error;
2use crate::parser::{
3    ast::{
4        expr::Expr,
5        literal::LitSym,
6        range::Range as RangeExpr,
7    },
8    fmt::Latex,
9    keyword::{In as InToken, Sum as SumToken},
10    Parse,
11    Parser,
12};
13use std::{fmt, ops::Range};
14
15#[cfg(feature = "serde")]
16use serde::{Deserialize, Serialize};
17
18/// A sum expression, such as `sum n in 1..10 of n`.
19///
20/// A sum expression is a shortcut for a loop that represents a summation. The final expression is
21/// summed over the specified range, with a specific variable name taking on each value in the
22/// range. The above example is equivalent to the following code:
23///
24/// ```calcscript
25/// out = 0
26/// for n in 1..10 {
27///     out += n
28/// }
29/// out
30/// ```
31#[derive(Debug, Clone, PartialEq, Eq)]
32#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
33pub struct Sum {
34    /// The variable name representing each value in the range.
35    pub variable: LitSym,
36
37    /// The range of values that the variable will take on.
38    pub range: RangeExpr,
39
40    /// The body of the summation.
41    pub body: Box<Expr>,
42
43    /// The region of the source code that this `sum` expression was parsed from.
44    pub span: Range<usize>,
45}
46
47impl Sum {
48    /// Returns the span of the `sum` expression.
49    pub fn span(&self) -> Range<usize> {
50        self.span.clone()
51    }
52}
53
54impl<'source> Parse<'source> for Sum {
55    fn std_parse(
56        input: &mut Parser<'source>,
57        recoverable_errors: &mut Vec<Error>
58    ) -> Result<Self, Vec<Error>> {
59        let sum_token = input.try_parse::<SumToken>().forward_errors(recoverable_errors)?;
60        let variable = input.try_parse::<LitSym>().forward_errors(recoverable_errors)?;
61        input.try_parse::<InToken>().forward_errors(recoverable_errors)?;
62        let range = input.try_parse::<RangeExpr>().forward_errors(recoverable_errors)?;
63        let body = input.try_parse_with_state::<_, Expr>(|state| {
64            state.allow_of = true;
65        }).forward_errors(recoverable_errors)?;
66        let span = sum_token.span.start..body.span().end;
67
68        Ok(Self {
69            variable,
70            range,
71            body: Box::new(body),
72            span,
73        })
74    }
75}
76
77impl std::fmt::Display for Sum {
78    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
79        write!(f, "sum {} in {} {}", self.variable, self.range, self.body)
80    }
81}
82
83impl Latex for Sum {
84    fn fmt_latex(&self, f: &mut fmt::Formatter) -> fmt::Result {
85        write!(
86            f,
87            "\\sum_{{{}={}}}^{{{}}} {}",
88            self.variable,
89            self.range.start,
90            self.range.end,
91            self.body,
92        )
93    }
94}