연습문제: 표현식 평가

Let's write a simple recursive evaluator for arithmetic expressions.

여기서 Box 타입은 스마트 포인터이며 이 과정의 후반부에서 자세히 다룹니다. 테스트에서 볼 수 있듯이 표현식은 Box::new를 사용하여 "박스로 표시"할 수 있습니다. 박스로 표시된 표현식을 평가하려면 deref 연산자 (*)를 사용하여 "박스 표시를 해제"합니다: eval(*boxed_expr).

일부 표현식은 평가할 수 없으며 오류를 반환합니다. 표준 Result<Value, String> 타입은 성공한 값을 나타내거나 (Ok(Value)) 메시지와 함께 오류를 나타냅니다 (Err(String)). 나중에 이 Result 타입에 대해서 자세히 살펴보겠습니다.

코드를 복사하여 Rust 플레이그라운드에 붙여넣고 eval 구현을 시작합니다. 최종 생성물은 테스트를 통과해야 합니다. todo!()를 사용하고 테스트를 하나씩 통과하도록 하면 도움이 될 수 있습니다. #[ignore]를 사용하여 테스트를 일시적으로 건너뛸 수도 있습니다.

#[test] #[ignore] fn test_value() { .. }

일찍 완료한 경우 0으로 나누기나 정수 오버플로가 발생하는 테스트를 작성해 보세요. 패닉 대신 Result로 이 문제를 어떻게 처리할 수 있을까요?

#![allow(unused)] fn main() { /// 두 개의 하위 표현식에서 실행할 연산입니다. #[derive(Debug)] enum Operation { Add, Sub, Mul, Div, } /// 트리 형식의 표현식입니다. #[derive(Debug)] enum Expression { /// 두 개의 하위 표현식에 관한 연산입니다. Op { op: Operation, left: Box<Expression>, right: Box<Expression> }, /// 리터럴 값 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("0으로 나누기")) ); } }