1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
//! This module is for taking instructions generated by the parser (an AST)
//! and producing real numbers.

use crate::lexer::*;
use crate::parser::*;
use crate::EvalError;

use std::collections::HashMap;

// If you come bearing big changes, you may have to rewrite
// this to suit your needs.

#[derive(Debug, Clone, PartialEq)]
pub enum ComputeError {
    UnrecognizedIdentifier(String),
}

/// A Computer object calculates expressions and has variables.
/// ```
/// let mut computer = Computer::new();
/// assert_eq!(computer.eval("a = 2"), Ok(2.0));
/// assert_eq!(computer.eval("a * 3"), Ok(6.0));
/// 
/// assert_eq!(
///     Computer::new().eval("a"),
///     Err(EvalError::ComputeError(ComputeError::UnrecognizedIdentifier("a")))
/// );
/// ```
#[derive(Debug, Clone, PartialEq)]
pub struct Computer {
    pub variables: HashMap<String, f64>,
}

impl Computer {
    pub fn new() -> Computer {
        Computer { variables: HashMap::new() }
    }

    /// Lexically analyze, parse, and compute the given equation in string form.
    pub fn eval(&mut self, expr: &str) -> Result<f64, EvalError> {
        match tokenize(expr) {
            Ok(tokens) => match parse(&tokens) {
                Ok(ast) => match self.compute(&ast) {
                    Ok(num) => Ok(num),
                    Err(compute_err) => Err(EvalError::ComputeError(compute_err)),
                }
                Err(parser_err) => Err(EvalError::ParserError(parser_err)),
            }
            Err(lexer_err) => Err(EvalError::LexerError(lexer_err)),
        }
    }

    /// Solve an already parsed `Expr` (AST).
    pub fn compute(&mut self, expr: &Expr) -> Result<f64, ComputeError> {
        match expr {
            Expr::Constant(num) => Ok(*num),
            Expr::Identifier(id) => {
                match self.variables.get(id) {
                    Some(&value) => Ok(value),
                    None => Err(ComputeError::UnrecognizedIdentifier(id.clone())),
                }
            }
            Expr::Neg(expr) => Ok(-self.compute(expr)?),
            Expr::BinOp(op, lexpr, rexpr) => {
                let lnum = self.compute(&lexpr)?;
                let rnum = self.compute(&rexpr)?;

                match op {
                    Operator::Plus => Ok(lnum + rnum),
                    Operator::Minus => Ok(lnum - rnum),
                    Operator::Star => Ok(lnum * rnum),
                    Operator::Slash => Ok(lnum / rnum),
                    Operator::Percent => Ok(lnum % rnum),
                    _ => unimplemented!(),
                }
            }
            Expr::Function(function, expr) => {
                let num = self.compute(&expr)?;
                Ok(match function {
                    Function::Sqrt => num.sqrt(),
                    Function::Sin => num.sin(),
                    Function::Cos => num.cos(),
                    Function::Tan => num.tan(),
                    Function::Log => num.log10(),
                    Function::Abs => num.abs(),
                })
            }
            Expr::Assignment(id, expr) => {
                let value = self.compute(&expr)?;
                self.variables.insert(id.clone(), value);
                Ok(value)
            }
            Expr::Pow(lexpr, rexpr) => {
                Ok(self.compute(&lexpr)?.powf(self.compute(&rexpr)?))
            }
        }
    }
}