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}