141 lines
4.9 KiB
Rust
141 lines
4.9 KiB
Rust
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<T> = Value<Expression<T>>;
|
|
type IREvalError<T> = EvalError<Expression<T>>;
|
|
|
|
impl<T: Clone + Into<Type>> Program<T> {
|
|
/// 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<T>, String), IREvalError<T>> {
|
|
let mut env: ScopedMap<Variable, IRValue<T>> = 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<T> Expression<T>
|
|
where
|
|
T: Clone + Into<Type>,
|
|
{
|
|
fn eval(
|
|
&self,
|
|
env: &ScopedMap<Variable, IRValue<T>>,
|
|
stdout: &mut String,
|
|
) -> Result<IRValue<T>, IREvalError<T>> {
|
|
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::<Result<Vec<IRValue<T>>, IREvalError<T>>>()?;
|
|
|
|
// 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<T: Clone> ValueOrRef<T> {
|
|
fn eval(
|
|
&self,
|
|
env: &ScopedMap<Variable, IRValue<T>>,
|
|
) -> Result<IRValue<T>, IREvalError<T>> {
|
|
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);
|
|
}
|