use std::collections::HashMap;
use gc::Gc;
use oftlisp::{InterpreterContext, Symbol, Value};
use oftlisp::collections::GcLinkedList;
use errors::RuntimeError;
use primitives::PRIMITIVES;
use types::{Context, Expr, Prim};
impl Context {
pub fn apply(func: Gc<Value<Self>>, args: Vec<Gc<Value<Self>>>) -> Result<Gc<Value<Self>>, RuntimeError> {
match *func {
Value::BuiltinFunction(_, f, _) => {
f(args)
},
Value::Func(n, ref a, (ref b, ref e), _) => {
let mut env = e.clone();
if let Some(n) = n {
env.push((n, func.clone()));
}
env.push_all_gc(a.bind(n, args)?);
Self::eval(b.clone(), &mut env)
},
_ => Err(RuntimeError::NotAFunction(func.clone())),
}
}
fn eval_prim(prim: &Prim, env: &GcLinkedList<(Symbol, Gc<Value<Context>>)>) -> Result<Gc<Value<Self>>, RuntimeError> {
match *prim {
Prim::Fn(name, ref args, ref body) => {
let mut closure_env = GcLinkedList::new();
for var in prim.freevars() {
if let Some(value) = env.lookup(var) {
closure_env.push((var, value));
} else {
return Err(RuntimeError::NonexistentVar(var));
}
}
Ok(Gc::new(Value::Func(name, args.clone(), (body.clone(), closure_env), ())))
},
Prim::Lit(ref val) => Ok(val.clone()),
Prim::Var(var) => if let Some(value) = env.lookup(var) {
Ok(value)
} else {
Err(RuntimeError::NonexistentVar(var))
},
Prim::Vec(ref vec) => {
let vec = vec.iter()
.cloned()
.map(|p| Self::eval_prim(&p, &env))
.collect::<Result<_, _>>()?;
Ok(Gc::new(Value::Vector(vec, ())))
},
}
}
}
impl InterpreterContext for Context {
type RuntimeError = RuntimeError;
fn eval(expr: Gc<Expr>, env: &mut GcLinkedList<(Symbol, Gc<Value<Self>>)>) -> Result<Gc<Value<Self>>, RuntimeError> {
trace!("Evaling {}", expr);
match *expr {
Expr::Call(ref func, ref args) => {
let func = Self::eval_prim(func, env)?;
let args = args.iter()
.map(|v| Self::eval_prim(v, env))
.collect::<Result<Vec<_>, _>>()?;
Context::apply(func, args)
},
Expr::If(ref c, ref t, ref e) => {
if let Value::Nil(_) = *Self::eval_prim(c, env)? {
Self::eval(e.clone(), env)
} else {
Self::eval(t.clone(), env)
}
},
Expr::Let(var, ref bound, ref next) => {
let value = Self::eval(bound.clone(), env)?;
let mut env = env.clone();
if let Some(var) = var {
env.push((var, value));
}
Self::eval(next.clone(), &mut env)
},
Expr::Prim(ref prim) => Self::eval_prim(&prim, env),
}
}
fn primitives() -> HashMap<Symbol, Gc<Value<Self>>> {
PRIMITIVES.clone().into()
}
}