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 parse_block(
124 "
125 x = 5
126 5 + 10
127 ",
128 );
129 }
130}