use crate::eval::{EvalEnvironment, EvalError, Value}; use crate::ir::{Expression, Program, Statement}; use super::{Primitive, ValueOrRef}; impl Program { /// Evaluate the program, returning either an error or a string containing everything /// the program printed out. /// /// The print outs will be newline separated, with one print out per line. pub fn eval(&self) -> Result { let mut env = EvalEnvironment::empty(); let mut stdout = String::new(); for stmt in self.statements.iter() { match stmt { Statement::Binding(_, name, value) => { let actual_value = value.eval(&env)?; env = env.extend(name.clone(), actual_value); } Statement::Print(_, name) => { let value = env.lookup(name.clone())?; let line = format!("{} = {}\n", name, value); stdout.push_str(&line); } } } Ok(stdout) } } impl Expression { fn eval(&self, env: &EvalEnvironment) -> Result { match self { Expression::Value(_, v) => match v { super::Value::Number(_, v) => Ok(Value::I64(*v)), }, Expression::Reference(_, n) => Ok(env.lookup(n.clone())?), Expression::Primitive(_, op, args) => { let mut arg_values = Vec::with_capacity(args.len()); // we implement primitive operations by first evaluating each of the // arguments to the function, and then gathering up all the values // produced. for arg in args.iter() { match arg { ValueOrRef::Ref(_, n) => arg_values.push(env.lookup(n.clone())?), ValueOrRef::Value(_, super::Value::Number(_, v)) => { arg_values.push(Value::I64(*v)) } } } // and then finally we call `calculate` to run them. trust me, it's nice // to not have to deal with all the nonsense hidden under `calculate`. match op { Primitive::Plus => Ok(Value::calculate("+", arg_values)?), Primitive::Minus => Ok(Value::calculate("-", arg_values)?), Primitive::Times => Ok(Value::calculate("*", arg_values)?), Primitive::Divide => Ok(Value::calculate("/", arg_values)?), } } } } } #[test] fn two_plus_three() { let input = crate::syntax::Program::parse(0, "x = 2 + 3; print x;").expect("parse works"); let ir = Program::from(input.simplify()); let output = ir.eval().expect("runs successfully"); assert_eq!("x = 5i64\n", &output); } #[test] fn lotsa_math() { let input = crate::syntax::Program::parse(0, "x = 2 + 3 * 10 / 5 - 1; print x;").expect("parse works"); let ir = Program::from(input.simplify()); let output = ir.eval().expect("runs successfully"); assert_eq!("x = 7i64\n", &output); }