Crafting Interpreters in Rust: Parsing Unary Negation
In the last post, we implemented unary-not. In this one, we’ll implement the other unary operator: negation.
As usual, we’ll start with a test:
#[test]
fn negation() {
let parser = lox::ExpressionParser::new();
let expr = parser.parse("-42").unwrap();
assert_eq!(expr, Expression::Nil);
}
Now – obviously – this isn’t going to pass, but it’ll prompt us through the steps to get it to compile.
First we have to update the grammar to add "-":
Unary = {
"!" <expr: Unary> => Expression::Not(Box::new(expr)),
"-" <expr: Unary> => Expression::Negate(Box::new(expr)),
Primary
};
Then we update the Expression type:
#[derive(Debug, PartialEq)]
pub enum Expression<'input> {
// ...
Negate(Box<Expression<'input>>),
}
Then we can update the test so that it passes:
#[test]
fn negation() {
let parser = lox::ExpressionParser::new();
let expr = parser.parse("-42").unwrap();
assert_eq!(expr, Expression::Negate(Box::new(Expression::Number(42.0))));
}
…and we might as well add another assertion for our evaluator:
#[test]
fn negation() {
let parser = lox::ExpressionParser::new();
let expr = parser.parse("-42").unwrap();
assert_eq!(expr, Expression::Negate(Box::new(Expression::Number(42.0))));
assert_eq!(evaluate(expr), Expression::Number(-42.0));
}
Note that, as it stands, this grammar doesn’t allow for a C-style decrement operator, --x, because we parse it as a
double-negation. This is fine: Lox doesn’t define increment or decrement operators.
And now we also need to update the evaluate function:
use crate::ast::*;
pub fn evaluate(expr: Expression) -> Expression {
match expr {
Expression::Not(v) => { /* ... */ },
Expression::Negate(v) => {
let v = evaluate(*v);
match v {
Expression::Number(n) => Expression::Number(-n),
any => panic!("{:?}", any)
}
},
any => any
}
}
And the new test passes, so we’ve done both unary operators. In the next part, we’ll take a look at the first binary operators: division and multiplication.