calculatex/
parser.rs

1use crate::parser::naive_string::parse_naive_string;
2use crate::{error::CalcError, latex::UnitHint};
3use pest::iterators::Pair;
4use pest::Parser;
5use pest_derive::*;
6
7use crate::statement::Statement;
8
9pub mod unit;
10use unit::parse_unit_expr;
11
12pub mod expr;
13use expr::parse_expr;
14
15pub mod fn_call;
16
17pub mod naive_string;
18
19#[derive(Parser)]
20#[grammar = "parser/grammar.pest"]
21pub struct MathParser;
22
23fn parse_var_dec(r: Pair<Rule>) -> Result<Statement, CalcError> {
24    assert_eq!(r.as_rule(), Rule::var_dec);
25    let mut inner = r.into_inner();
26    let lhs = inner.next().unwrap();
27    let rhs = inner.next().unwrap();
28    Ok(Statement::VarDec {
29        lhs: lhs.as_str().to_string(),
30        rhs: parse_expr(rhs)?,
31    })
32}
33
34fn parse_print_stmt(r: Pair<Rule>) -> Result<Statement, CalcError> {
35    assert_eq!(r.as_rule(), Rule::print_expr);
36    let mut inner = r.into_inner();
37    let lhs = inner.next().unwrap();
38
39    let unit_hint = match inner.next() {
40        Some(r) => Some(UnitHint {
41            unit: parse_unit_expr(r.clone())?.eval(),
42            pretty_string: parse_naive_string(r)?,
43        }),
44        None => None,
45    };
46
47    Ok(Statement::PrintExpr {
48        expr: parse_expr(lhs)?,
49        unit_hint,
50    })
51}
52
53fn parse_dec_print_stmt(r: Pair<Rule>) -> Result<Statement, CalcError> {
54    assert_eq!(r.as_rule(), Rule::dec_print_expr);
55    let mut inner = r.into_inner();
56    let lhs = inner.next().unwrap();
57    let rhs = inner.next().unwrap();
58
59    let unit_hint = match inner.next() {
60        Some(r) => Some(UnitHint {
61            unit: parse_unit_expr(r.clone())?.eval(),
62            pretty_string: parse_naive_string(r)?,
63        }),
64        None => None,
65    };
66
67    Ok(Statement::DecPrintExpr {
68        lhs: lhs.as_str().to_string(),
69        rhs: parse_expr(rhs)?,
70        unit_hint,
71    })
72}
73
74fn parse_digit_set(r: Pair<Rule>) -> Statement {
75    assert_eq!(r.as_rule(), Rule::digit_set);
76    let mut inner = r.into_inner();
77    let n_digits = inner.next().unwrap().as_str().parse::<usize>().unwrap();
78    Statement::DigitSet(n_digits)
79}
80
81pub fn parse_block(s: &str) -> Result<Vec<(usize, Statement)>, CalcError> {
82    let inp = MathParser::parse(Rule::program, s)?;
83    inp.map(|s| {
84        let stmt = s.into_inner().next().unwrap();
85        let (line, _) = stmt.as_span().start_pos().line_col();
86        let add_line = |e: CalcError| e.add_line(line);
87        Ok((
88            line,
89            match stmt.as_rule() {
90                Rule::digit_set => parse_digit_set(stmt),
91                Rule::set_scientific => Statement::SetScientific,
92                Rule::var_dec => parse_var_dec(stmt).map_err(add_line)?,
93                Rule::print_expr => parse_print_stmt(stmt).map_err(add_line)?,
94                Rule::dec_print_expr => parse_dec_print_stmt(stmt).map_err(add_line)?,
95                Rule::line_gap_stmt => Statement::LineGap,
96                Rule::latex_block => Statement::RawLaTeX(
97                    stmt.as_str()
98                        .trim_start_matches("'''")
99                        .trim_end_matches("'''")
100                        .to_owned(),
101                ),
102                Rule::error => {
103                    return Err(CalcError::Other(format!(
104                        "Invalid statement {}",
105                        stmt.as_str()
106                    )))
107                    .map_err(add_line)?
108                }
109                _ => unreachable!(),
110            },
111        ))
112    })
113    .collect()
114}
115
116#[cfg(test)]
117mod tests {
118    use super::*;
119
120    #[test]
121    fn test_parser() {
122        // just check if it doesn't crash rn
123        parse_block(
124            "
125                x = 5
126                5 + 10
127            ",
128        );
129    }
130}