Sometime last year, I had tried writing a parser for basic arithmetic to drive a simple calculator in Scala.

To follow-up, I decided to try writing this in Rust.

I looked at some parser crates, settled on peg (it seemed simpler than … pest).

The code is here.

The core of it is this parser section:

peg::parser!(
grammar calc_parser() for str {
    rule number() -> i64
    = n:$(['0'..='9']+) {
        ? n.parse().or(Err("bad num"))
    }

    rule whitespace() = quiet!{[' ']*}

    rule operator() -> Operator
    = op:$(['*' | '/' | '+' | '-']) { Operator(op.chars().next().unwrap()) }

    rule paren() -> i64
    = "(" whitespace() n:calculate() whitespace() ")" { n }

    rule factor() -> i64
    =
    n:number() { n }
    /
    n:paren() { n }

    rule term() -> i64
    =
    n1:factor() whitespace() op:operator() whitespace() n2:factor() {
        eval(&op, n1, n2)
    }
    /
    n:factor() { n }

    pub rule calculate() -> i64
    =
    n1:term() whitespace() op:operator() whitespace() n2:term() {
        eval(&op, n1, n2)
    }
    /
    n:term() { n }
}
);

Here’s what a sample session looks like:

Enter an expression, or 'END' to quit:
45
Answer: 45
Enter an expression, or 'END' to quit:
8 * (7 + (9 / 3))
Answer: 80
Enter an expression, or 'END' to quit:
4 * ( 5
Calculation error: ParseError { location: LineCol { line: 1, column: 8, offset: 7 }, expected: ExpectedSet { expected: {"['0' ..= '9']", "['*' | '/' | '+' | '-']", "\")\""} } }
Enter an expression, or 'END' to quit:
END