use super::{Primitive, Type, ValueOrRef}; use crate::eval::{EvalError, Value}; use crate::ir::{Expression, Program, TopLevel, Variable}; use crate::util::scoped_map::ScopedMap; type IRValue = Value>; type IREvalError = EvalError>; impl> Program { /// Evaluate the program, returning either an error or the result of the final /// statement and the complete contents of the console output. /// /// The print outs will be newline separated, with one print out per line. pub fn eval(&self) -> Result<(IRValue, String), IREvalError> { let mut env: ScopedMap> = ScopedMap::new(); let mut stdout = String::new(); let mut last_value = Value::Void; for stmt in self.items.iter() { match stmt { TopLevel::Function(name, args, _, body) => { let closure = Value::Closure( Some(name.clone()), env.clone(), args.iter().map(|(x, _)| x.clone()).collect(), body.clone(), ); env.insert(name.clone(), closure.clone()); last_value = closure; } TopLevel::Statement(expr) => { last_value = expr.eval(&env, &mut stdout)?; } } } Ok((last_value, stdout)) } } impl Expression where T: Clone + Into, { fn eval( &self, env: &ScopedMap>, stdout: &mut String, ) -> Result, IREvalError> { match self { Expression::Atomic(x) => x.eval(env), Expression::Cast(_, t, valref) => { let value = valref.eval(env)?; let ty = t.clone().into(); match ty { Type::Primitive(pt) => Ok(pt.safe_cast(&value)?), Type::Function(_, _) => Err(EvalError::CastToFunction(ty.to_string())), } } Expression::Primitive(_, _, op, args) => { let arg_values = args .iter() .map(|x| x.eval(env)) .collect::>, IREvalError>>()?; // 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)?), } } Expression::Block(_, _, _) => { unimplemented!() } Expression::Print(loc, n) => { let value = env .get(n) .cloned() .ok_or_else(|| EvalError::LookupFailed(loc.clone(), n.to_string()))?; stdout.push_str(&format!("{} = {}\n", n, value)); Ok(Value::Void) } Expression::Bind(_, _, _, _) => unimplemented!(), } } } impl ValueOrRef { fn eval( &self, env: &ScopedMap>, ) -> Result, IREvalError> { match self { ValueOrRef::Value(_, _, v) => match v { super::Value::I8(_, v) => Ok(Value::I8(*v)), super::Value::I16(_, v) => Ok(Value::I16(*v)), super::Value::I32(_, v) => Ok(Value::I32(*v)), super::Value::I64(_, v) => Ok(Value::I64(*v)), super::Value::U8(_, v) => Ok(Value::U8(*v)), super::Value::U16(_, v) => Ok(Value::U16(*v)), super::Value::U32(_, v) => Ok(Value::U32(*v)), super::Value::U64(_, v) => Ok(Value::U64(*v)), }, ValueOrRef::Ref(loc, _, n) => env .get(n) .cloned() .ok_or_else(|| EvalError::LookupFailed(loc.clone(), n.to_string())), } } } #[test] fn two_plus_three() { let input = crate::syntax::Program::parse(0, "x = 2 + 3; print x;").expect("parse works"); let ir = input.type_infer().expect("test should be type-valid"); let (_, output) = ir.eval().expect("runs successfully"); assert_eq!("x = 5u64\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 = input.type_infer().expect("test should be type-valid"); let (_, output) = ir.eval().expect("runs successfully"); assert_eq!("x = 7u64\n", &output); }