연습문제: 표현식 평가

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으로 나누기"))
    );
}
}