cas_parser/parser/ast/
product.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, Product as ProductToken},
10    Parse,
11    Parser,
12};
13use std::{fmt, ops::Range};
14
15#[cfg(feature = "serde")]
16use serde::{Deserialize, Serialize};
17
18/// A product expression, such as `product n in 1..10 of n`.
19///
20/// A product expression is a shortcut for a loop that represents an accumulative product. The
21/// final expression is multiplied over the specified range, with a specific variable name taking
22/// on each value in the range. The above example is equivalent to the following code:
23///
24/// ```calcscript
25/// out = 1
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 Product {
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 product.
41    pub body: Box<Expr>,
42
43    /// The region of the source code that this `product` expression was parsed from.
44    pub span: Range<usize>,
45}
46
47impl Product {
48    /// Returns the span of the `product` expression.
49    pub fn span(&self) -> Range<usize> {
50        self.span.clone()
51    }
52}
53
54impl<'source> Parse<'source> for Product {
55    fn std_parse(
56        input: &mut Parser<'source>,
57        recoverable_errors: &mut Vec<Error>
58    ) -> Result<Self, Vec<Error>> {
59        let product_token = input.try_parse::<ProductToken>().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 = product_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 Product {
78    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
79        write!(f, "product {} in {} {}", self.variable, self.range, self.body)
80    }
81}
82
83impl Latex for Product {
84    fn fmt_latex(&self, f: &mut fmt::Formatter) -> fmt::Result {
85        write!(
86            f,
87            "\\prod_{{{}={}}}^{{{}}} {}",
88            self.variable,
89            self.range.start,
90            self.range.end,
91            self.body,
92        )
93    }
94}