trivet 3.1.0

The trivet Parser Library
Documentation
# Math Example

As another example, let's create a parser for arithmetic expressions. We can build a little grammar for these, and we will need to make sure the usual precedence rules are followed.

Here's a little BNF for our expression language.

```text
    top_expr ::= product_expr (( '+' | '-' ) product_expr)*

    product_expr ::= exp_expr (( '*' | '/' ) exp_expr)*

    exp_expr ::= primitive (( '**' ) primitive)*

    primitive ::=  '-'? ( number | '(' top_expr ')' )

    number ::= FLOAT
```

This actually encodes the priorities of the operators. Consider this little example.

```text
6 + 2 * 4 / 2 ** 2 + 1
```

Based on our priority rules, we expect to evaluate this as follows.

```text
  (6 + ((2 * 4) / (2 ** 2))) + 1
= (6 + (8 / 4)) + 1
= (6 + 2) + 1
= 8 + 1
= 9
```

That is, the power should be computed first, then the multiplication and division from left to right, then finally the addition. Note how this is done. Each level of the grammar only references higher-priority items, so those get parsed, and computed, _first_. Also, we will need to be explicit about looping left to right. If we simply build the above in a naive recursive way, we can end up with _right_ associativity, which isn't what we want.

## Stubs

Let's write some code. We will create a method for each non-terminal, and will stub them out initially. Then we can fill in the details.

```rust,ignore
use trivet::errors::syntax_error;
use trivet::errors::ParseResult;
use trivet::parse_from_stdin;
use trivet::Parser;

fn top_expr_ws(&mut Parser) -> ParseResult<f64> {
    Ok(0)
}

fn product_expr_ws(&mut Parser) -> ParseResult<f64> {
    Ok(0)
}

fn exp_expr_ws(&mut Parser) -> ParseResult<f64> {
    Ok(0)
}

fn primitive_ws(&mut Parser) -> ParseResult<f64> {
    Ok(0)
}

fn number_ws(&mut Parser) -> ParseResult<f64> {
    Ok(0)
}

{{#include ../../examples/book_math_parser.rs:104:113}}
```

The main method lets us run our code, and the compiler will inform us that we have unused functions as a hint to fill them in.

## Top Level Expression

```text
    top_expr ::= product_expr (( '+' | '-' ) product_expr)*
```

For a top-level expression we need to parse a product expression and then look for a plus or minus. If we find either, then we parse the next product expression and repeat. In pseudocode we have the following.

```text
let left = parse product_expr
loop
    if peek is '+' then
        let right = parse product_expr
        left = left + right
    else if peek is '-' then
        let right = parse product_expr
        left = left - right
    else
        break loop
    end
end
return left
```

That's pretty easy to turn into Rust code.

```rust,ignore
{{#include ../../examples/book_math_parser.rs:85:102}}
```

## Product Expression

```text
    product_expr ::= exp_expr (( '*' | '/' ) exp_expr)*
```

We will use the same approach as we did for the top level. This yields the following Rust code.

```rust,ignore
{{#include ../../examples/book_math_parser.rs:66:83}}
```

## Exponent Expression

```text
    exp_expr ::= primitive (( '**' ) primitive)*
```

Even though there is just one operator here, we can still use the same approach. We need to match on both asterisks, so we can use `peek_and_consume_str_ws(&str) -> bool` for this.

```rust,ignore
{{#include ../../examples/book_math_parser.rs:46:64}}
```

## Primitive

```text
    primitive ::=  '-'? ( number | '(' top_expr ')' )
```

Here we check for a leading minus sign, then we look for either a number or an open parenthesis. The open parenthesis is easy to match, so it is easy to tell the two cases apart. The minus sign _might_ be the minus sign for the number, but that is okay. If we consume the minus sign, then the number parser will give us a nonnegative value that we can negate later . This works well for floating point values, but for an integer value we might want to do something different.

```rust,ignore
{{#include ../../examples/book_math_parser.rs:20:44}}
```

## Number

```text
    number ::= FLOAT
```

The last thing we have to deal with is the floating point number. We can use the built-in number parser to deal with numbers.

```rust,ignore
{{#include ../../examples/book_math_parser.rs:10:18}}
```

## Complete

The entire expression parser can be found in `examples/book_math_parser.rs` in the distribution. It accepts input from standard input, so you can enter a sequence of values separated by whitespace and play around with the parser.

The above might seem complicated, but keep in mind that we process four different number bases and five operators with three different priorities plus a unary minus. This is all done in just 66 lines of code (per [Tokei](https://crates.io/crates/tokei)) written in just a short amout of time. And, because we didn't tell it _not_ to, it actually processes comments, even inside expressions. Try it!

Oh, the unary minus. Does it work correctly? We do handle `-(-5)` and even `3 - -3`, which we can write as `3--3`, and which correctly gives the value `6`. A more advanced version might build an abstract syntax tree and label the nodes with the parser location (from `parser.loc() -> Loc`), but this is fast, works well, and is pretty easily extended.

Suppose we decided we wanted new operator "foo" that has a lower priority than exponentiation, but higher than multiplication. We could write a new method, `parse_foo_expr_ws(Parser) -> ParseResult<i64>`. Then `parse_product_expr_ws(Parser) -> ParseResult<i64>` would call our "foo" method, and it would, in turn, call the `parser_exp_expr_ws(Parser) -> ParseResult<i64>` method.