math_engine/
parser.rs

1use crate::error::ParserError;
2use crate::expression::Expression;
3use lexgen::lexer;
4use pomelo::pomelo;
5
6pomelo! {
7    %include {
8        use crate::expression::*;
9    }
10
11    %type input Option<Expression>;
12    %type expr Expression;
13    %type Constant f32;
14    %type Literal String;
15
16    %left Plus;
17    %left Minus;
18    %left Prod;
19    %left Div;
20
21    input ::= expr?(E) { E };
22    expr ::= Constant(N) { Expression::constant(N) }
23    expr ::= Literal(V) { Expression::variable(V.as_str()) }
24    expr ::= LPar expr(E) RPar { E }
25    expr ::= expr(E1) Plus expr(E2) { Expression::addition(E1, E2) }
26    expr ::= expr(E1) Minus expr(E2) { Expression::subtraction(E1, E2) }
27    expr ::= expr(E1) Prod expr(E2) { Expression::product(E1, E2) }
28    expr ::= expr(E1) Div expr(E2) { Expression::division(E1, E2) }
29}
30
31use parser::Token;
32
33lexer! {
34    ExprLexer -> Token;
35
36    let number = ['0'-'9']+ '.' ['0'-'9']*;
37    let var = ['a'-'z''A'-'Z''_']*;
38
39    rule Init {
40        [' ''\t' '\n' '\r']+,
41        '+' => |lex| lex.return_(Token::Plus),
42        '-' => |lex| lex.return_(Token::Minus),
43        '*' => |lex| lex.return_(Token::Prod),
44        '/' => |lex| lex.return_(Token::Div),
45        '(' => |lex| lex.return_(Token::LPar),
46        ')' => |lex| lex.return_(Token::RPar),
47        $number => |lex| lex.return_(Token::Constant(
48                lex.match_()[..].to_owned().parse::<f32>().unwrap()
49        )),
50        $var => |lex| lex.return_(Token::Literal(
51                lex.match_()[..].to_string()
52        )),
53    }
54}
55
56/// Parses a mathematical expression from a string.
57/// As of now, the user must use full notation for floating point values.
58///
59/// # Examples
60/// Basid usage:
61///
62/// ```
63/// use math_engine::context::Context;
64/// use math_engine::expression::Expression;
65/// use math_engine::parser::parse_expression;
66///
67/// let expr = parse_expression("1.0 + x").unwrap();
68/// let ctx = Context::new().with_variable("x", 4.0);
69/// let eval = expr.eval_with_context(&ctx).unwrap();
70///
71/// assert_eq!(eval, 5.0);
72/// ```
73///
74/// # Errors
75/// An error is returned if the string could not be parsed.
76pub fn parse_expression(input: &str) -> Result<Expression, ParserError> {
77    use parser::Parser;
78
79    let mut p = Parser::new();
80    let lexer = ExprLexer::new(input);
81
82    for tok in lexer {
83        match tok {
84            Ok((_, tok, _)) => {
85                if p.parse(tok).is_err() {
86                    return Err(ParserError::ParserError);
87                }
88            }
89            Err(_) => return Err(ParserError::LexerError),
90        }
91    }
92
93    match p.end_of_input() {
94        Ok(Some(e)) => Ok(e),
95        Ok(None) => Err(ParserError::NoExpressionFound),
96        Err(_) => Err(ParserError::ParserError),
97    }
98}