2015-12-30 15:09:31 -08:00
|
|
|
use parser::AST;
|
|
|
|
|
|
|
|
struct Evaluator {
|
|
|
|
ast: AST
|
|
|
|
}
|
|
|
|
|
2015-12-30 20:48:59 -08:00
|
|
|
pub type EvaluatorResult<T> = Result<T, EvaluatorError>;
|
|
|
|
|
|
|
|
struct EvaluatorError {
|
|
|
|
err: String
|
|
|
|
}
|
|
|
|
|
2015-12-30 15:09:31 -08:00
|
|
|
impl Evaluator {
|
2015-12-30 20:48:59 -08:00
|
|
|
pub fn run(self) -> String {
|
|
|
|
match reduce_full(self.ast) {
|
|
|
|
Err(e) => format!("{}", e.err),
|
|
|
|
Ok(ast) => format!("{}", ast)
|
2015-12-30 15:09:31 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl AST {
|
|
|
|
fn can_reduce(&self) -> bool {
|
2015-12-30 20:48:59 -08:00
|
|
|
use parser::AST::*;
|
|
|
|
match self {
|
|
|
|
&Number(_) => false,
|
|
|
|
&Name(_) => false,
|
|
|
|
_ => true
|
|
|
|
}
|
2015-12-30 15:09:31 -08:00
|
|
|
}
|
2015-12-30 20:48:59 -08:00
|
|
|
}
|
2015-12-30 15:09:31 -08:00
|
|
|
|
2015-12-30 20:48:59 -08:00
|
|
|
fn reduce_full(ast: AST) -> EvaluatorResult<AST> {
|
|
|
|
let mut ast = ast;
|
|
|
|
while ast.can_reduce() {
|
|
|
|
ast = try!(reduce_step(ast));
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(ast)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn reduce_step(ast: AST) -> EvaluatorResult<AST> {
|
|
|
|
use parser::AST::*;
|
|
|
|
match ast {
|
|
|
|
BinOp(left, op, right) => {
|
|
|
|
let left = try!(reduce_full(*left));
|
|
|
|
let op = try!(reduce_full(*op));
|
|
|
|
let right = try!(reduce_full(*right));
|
|
|
|
match (left, op, right) {
|
|
|
|
(Number(l), Name(op), Number(r)) => {
|
|
|
|
match &op[..] {
|
|
|
|
"+" => Ok(Number(l + r)),
|
|
|
|
"-" => Ok(Number(l - r)),
|
|
|
|
"*" => Ok(Number(l * r)),
|
|
|
|
"/" => {
|
|
|
|
if r == 0.0 {
|
|
|
|
Err(EvaluatorError { err: format!("Divide by zero") })
|
|
|
|
} else {
|
|
|
|
Ok(Number(l / r))
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_ => Err(EvaluatorError { err: format!("Bad BinOp operator") })
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_ => Err(EvaluatorError {
|
|
|
|
err: format!("Bad arguments for BinOp")
|
|
|
|
})
|
|
|
|
}
|
|
|
|
},
|
|
|
|
Number(_) => Ok(ast),
|
|
|
|
Name(_) => Ok(ast),
|
2015-12-30 15:09:31 -08:00
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn evaluate(ast: AST) -> String {
|
2015-12-30 20:48:59 -08:00
|
|
|
let ev = Evaluator { ast: ast };
|
2015-12-30 15:09:31 -08:00
|
|
|
ev.run()
|
|
|
|
}
|
2015-12-30 23:30:50 -08:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use tokenizer::tokenize;
|
|
|
|
use parser::{parse, AST};
|
|
|
|
use super::evaluate;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_basic_evaluation() {
|
|
|
|
let input = "1 + 2\n";
|
|
|
|
let output = evaluate(parse(tokenize(input)).unwrap());
|
|
|
|
assert_eq!(output, "3");
|
|
|
|
}
|
|
|
|
}
|