book_math_parser/
book_math_parser.rs

1//! Example of parsing an expression.
2//!
3//! This example is also worked out in The Trivet Parser book.
4
5use trivet::errors::syntax_error;
6use trivet::errors::ParseResult;
7use trivet::parse_from_stdin;
8use trivet::Parser;
9
10/// Parse a number.  The number can be decimal, octal, hexadecimal, or binary.
11/// Underscores can be present to break up long groups of digits, and are ignored.
12///
13/// On entry the parser is assumed to be pointing at the first character.  On
14/// exit any trailing whitespace is consumed.
15///
16pub fn parse_number_ws(parser: &mut Parser) -> ParseResult<f64> {
17    parser.parse_f64_ws()
18}
19
20/// Parse a primitive expression.  This could be a number or a parenthesized expression.
21///
22/// On entry the parser is assumed to be pointing at the first character.  On
23/// exit any trailing whitespace is consumed.
24///
25pub fn parse_primitive_ws(parser: &mut Parser) -> ParseResult<f64> {
26    // Look for leading minus sign.
27    let is_neg = parser.peek_and_consume('-');
28
29    // Look for a parenthesized expression.  Here is where our bottom-up approach
30    // otherwise breaks.
31    let value = if parser.peek_and_consume('(') {
32        let value = parse_top_expr_ws(parser)?;
33        if !parser.peek_and_consume_ws(')') {
34            return Err(syntax_error(parser.loc(), "Missing closing parenthesis."));
35        }
36        value
37    } else {
38        // Note that we have already parsed the negative sign from the number above, so
39        // we wil get back a nonnegative value here.  That's fine, since we will fix it
40        // at the end.
41        parse_number_ws(parser)?
42    };
43    Ok(if is_neg { -value } else { value })
44}
45
46/// Parse an expression containing exponentiation or bitwise operators.
47///
48/// On entry the parser is assumed to be pointing at the first character.  On
49/// exit any trailing whitespace is consumed.
50///
51pub fn parse_exp_expr_ws(parser: &mut Parser) -> ParseResult<f64> {
52    // Parse an initial primitive.
53    let mut left = parse_primitive_ws(parser)?;
54    loop {
55        // Look for an operator.
56        left = if parser.peek_and_consume_str_ws("**") {
57            let right = parse_primitive_ws(parser)?;
58            left.powf(right)
59        } else {
60            break;
61        }
62    }
63    Ok(left)
64}
65
66/// Parse multiplication, division, and remainder operations.
67///
68/// On entry the parser is assumed to be pointing at the first character.  On
69/// exit any trailing whitespace is consumed.
70///
71pub fn parse_product_expr_ws(parser: &mut Parser) -> ParseResult<f64> {
72    let mut left = parse_exp_expr_ws(parser)?;
73    loop {
74        left = if parser.peek_and_consume_ws('*') {
75            left * parse_exp_expr_ws(parser)?
76        } else if parser.peek_and_consume_ws('/') {
77            left / parse_exp_expr_ws(parser)?
78        } else {
79            break;
80        }
81    }
82    Ok(left)
83}
84
85/// Parse an expression containing arithmetic and bitwise operators.
86///
87/// On entry the parser is assumed to be pointing at the first character.  On
88/// exit any trailing whitespace is consumed.
89///
90pub fn parse_top_expr_ws(parser: &mut Parser) -> ParseResult<f64> {
91    let mut left = parse_product_expr_ws(parser)?;
92    loop {
93        left = if parser.peek_and_consume_ws('+') {
94            left + parse_product_expr_ws(parser)?
95        } else if parser.peek_and_consume_ws('-') {
96            left - parse_product_expr_ws(parser)?
97        } else {
98            break;
99        };
100    }
101    Ok(left)
102}
103
104// Main program.
105pub fn main() -> ParseResult<()> {
106    let mut parser = parse_from_stdin();
107    parser.consume_ws();
108    while !parser.is_at_eof() {
109        let number = parse_top_expr_ws(&mut parser)?;
110        println!("  {}", number);
111    }
112    Ok(())
113}