use either::{Either, Left, Right};
use gc::Gc;
use ast::{ConvertError, convert_fn, Expr};
use collections::GcLinkedList;
use context::InterpreterContext;
use symbol::Symbol;
use util::{as_list, as_shl};
use value::Value;
pub fn expand_module<C: 'static + InterpreterContext>(
body: Vec<Gc<Value<C>>>,
env: &mut GcLinkedList<(Symbol, Gc<Value<C>>)>,
menv: &mut GcLinkedList<(Symbol, Gc<Value<C>>)>,
) -> Result<Vec<Gc<Value<C>>>, Either<C::RuntimeError, ConvertError<C>>> {
let mut out = vec![];
for v in body {
let v = match expand_expr(v, env, menv) {
Ok(Some(v)) => v,
Ok(None) => continue,
Err(err) => return Err(err),
};
out.extend(expand_macro_progns(v));
}
Ok(out)
}
pub fn expand_expr<C: 'static + InterpreterContext>(
value: Gc<Value<C>>,
env: &mut GcLinkedList<(Symbol, Gc<Value<C>>)>,
menv: &mut GcLinkedList<(Symbol, Gc<Value<C>>)>,
) -> Result<Option<Gc<Value<C>>>, Either<C::RuntimeError, ConvertError<C>>> {
if let Some((sym, rest)) = as_shl(value.clone()) {
if sym.as_str() == "defmacro" {
if rest.len() > 2 {
let mut body = expand_list(rest, &mut env.clone(), &mut menv.clone())?;
let name = if let Value::Symbol(name, _) = *body.remove(0) {
name
} else {
unimplemented!("invalid defmacro")
};
let args = body.remove(0);
let lambda = convert_fn(Some(name), args, body).map_err(Right)?;
let lambda = C::from_expr(lambda);
let func = C::eval(lambda, env).map_err(Left)?;
menv.push((name, func));
Ok(None)
} else {
unimplemented!("defmacro")
}
} else if let Some(mac) = menv.lookup(sym) {
let args = rest.into_iter().map(Expr::Literal).map(Gc::new).collect();
let call = Gc::new(Expr::Call(Gc::new(Expr::Literal(mac)), args));
let call = C::from_expr(call);
let val = C::eval(call, env).map_err(Left)?;
expand_expr(val, env, menv)
} else {
expand_list(rest, env, menv)
.map(|l| {
let tl = Value::list(l, Default::default());
let hd = Gc::new(Value::Symbol(sym, Default::default()));
Gc::new(Value::Cons(hd, tl, Default::default()))
})
.map(Some)
}
} else if let Some(l) = as_list(value.clone()) {
expand_list(l, env, menv)
.map(|l| Value::list(l, Default::default()))
.map(Some)
} else if let Value::Vector(ref v, _) = *value.clone() {
expand_list(v.clone(), env, menv).map(Value::vector).map(
Some,
)
} else {
Ok(Some(value))
}
}
fn expand_list<C: 'static + InterpreterContext>(
vals: Vec<Gc<Value<C>>>,
env: &mut GcLinkedList<(Symbol, Gc<Value<C>>)>,
menv: &mut GcLinkedList<(Symbol, Gc<Value<C>>)>,
) -> Result<Vec<Gc<Value<C>>>, Either<C::RuntimeError, ConvertError<C>>> {
let mut menv = menv.clone();
vals.into_iter()
.filter_map(|v| match expand_expr(v, env, &mut menv) {
Ok(Some(v)) => Some(Ok(v)),
Ok(None) => None,
Err(err) => Some(Err(err)),
})
.collect()
}
fn expand_macro_progns<C: 'static + InterpreterContext>(value: Gc<Value<C>>) -> Vec<Gc<Value<C>>> {
fn helper<C: 'static + InterpreterContext>(value: Gc<Value<C>>, v: &mut Vec<Gc<Value<C>>>) {
if let Some((sym, rest)) = as_shl(value.clone()) {
if sym == "macro-progn".into() {
for value in rest {
helper(value, v);
}
} else {
v.push(value);
}
} else {
v.push(value);
}
}
let mut out = vec![];
helper(value, &mut out);
out
}