Vamos escrever um interpretador recursivo simples para expressões aritméticas.
O tipo Box aqui é um ponteiro inteligente (smart pointer) e será abordado em detalhes mais adiante no curso. Uma expressão pode ser “encaixotada” com Box::new como visto nos testes. Para avaliar uma expressão encaixotada, use o operador de desreferência (*) para o “desencaixotar”: eval(*boxed_expr).
Algumas expressões não podem ser avaliadas e retornarão um erro. O tipo padrão Result<Value, String> é um enum que representa um valor de sucesso (Ok(Value)) ou um erro (Err(String)). Abordaremos esse tipo em detalhes mais adiante.
Copie e cole o código no playground do Rust e comece a implementar eval. O produto final deve passar nos testes. Pode ser útil usar todo!() e fazer os testes passarem um por um. Você também pode ignorar um teste temporariamente com #[ignore]:
#[test]
#[ignore]
fn test_value() { .. }
Se você terminar cedo, tente escrever um teste que resulte em divisão por zero ou integer overflow. Como você poderia lidar com isso com Result em vez de um pânico?
#![allow(unused)]fnmain() {
/// Uma operação a ser realizada em duas subexpressões.#[derive(Debug)]enumOperation {
Add,
Sub,
Mul,
Div,
}
/// Uma expressão, em forma de árvore.#[derive(Debug)]enumExpression {
/// Uma operação em duas subexpressões.
Op { op: Operation, left: Box<Expression>, right: Box<Expression> },
/// Um valor literal
Value(i64),
}
fneval(e: Expression) -> Result<i64, String> {
todo!()
}
#[test]fntest_value() {
assert_eq!(eval(Expression::Value(19)), Ok(19));
}
#[test]fntest_sum() {
assert_eq!(
eval(Expression::Op {
op: Operation::Add,
left: Box::new(Expression::Value(10)),
right: Box::new(Expression::Value(20)),
}),
Ok(30)
);
}
#[test]fntest_recursion() {
let term1 = Expression::Op {
op: Operation::Mul,
left: Box::new(Expression::Value(10)),
right: Box::new(Expression::Value(9)),
};
let term2 = Expression::Op {
op: Operation::Mul,
left: Box::new(Expression::Op {
op: Operation::Sub,
left: Box::new(Expression::Value(3)),
right: Box::new(Expression::Value(4)),
}),
right: Box::new(Expression::Value(5)),
};
assert_eq!(
eval(Expression::Op {
op: Operation::Add,
left: Box::new(term1),
right: Box::new(term2),
}),
Ok(85)
);
}
#[test]fntest_error() {
assert_eq!(
eval(Expression::Op {
op: Operation::Div,
left: Box::new(Expression::Value(99)),
right: Box::new(Expression::Value(0)),
}),
Err(String::from("divisão por zero"))
);
}
}