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