Exercício: Avaliação de Expressões
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)] fn main() { /// Uma operação a ser realizada em duas subexpressões. #[derive(Debug)] enum Operation { Add, Sub, Mul, Div, } /// Uma expressão, em forma de árvore. #[derive(Debug)] enum Expression { /// Uma operação em duas subexpressões. Op { op: Operation, left: Box<Expression>, right: Box<Expression> }, /// Um valor literal Value(i64), } fn eval(e: Expression) -> Result<i64, String> { todo!() } #[test] fn test_value() { assert_eq!(eval(Expression::Value(19)), Ok(19)); } #[test] fn test_sum() { assert_eq!( eval(Expression::Op { op: Operation::Add, left: Box::new(Expression::Value(10)), right: Box::new(Expression::Value(20)), }), Ok(30) ); } #[test] fn test_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] fn test_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")) ); } }