use either::{Either, Left, Right};
use gc::Gc;
use oftlisp::Value;
use oftlisp::ast::{Expr as AstExpr};
use oftlisp::gensym::gensym_prefix;
use types::{Context, Expr, Prim};
impl Context {
fn convert(expr: Gc<AstExpr<Self>>) -> Either<Gc<Prim>, Gc<Expr>> {
match *expr {
AstExpr::Call(ref f, ref a) => Right({
Self::convert_prim(f.clone(), |f| {
let mut a = a.clone();
a.reverse();
Self::convert_prims(a, |mut a| {
a.reverse();
Gc::new(Expr::Call(f, a))
})
})
}),
AstExpr::Def(_, ref e) => {
let nil = Gc::new(Prim::Lit(Gc::new(Value::Nil(()))));
match Self::convert(e.clone()) {
Left(_) => Left(nil),
Right(expr) => Right({
let nil = Gc::new(Expr::Prim(nil));
Gc::new(Expr::Let(None, expr, nil))
}),
}
},
AstExpr::If(ref c, ref t, ref e) => {
Right(Self::convert_prim(c.clone(), |c| {
let t = Self::convert_expr(t.clone());
let e = Self::convert_expr(e.clone());
Gc::new(Expr::If(c, t, e))
}))
},
AstExpr::Lambda(n, ref a, ref b, ref t) => {
let t = Self::convert_expr(t.clone());
let body = Self::convert_block(b.clone(), t);
Left(Gc::new(Prim::Fn(n, a.clone(), body)))
},
AstExpr::Literal(ref v) => Left(Gc::new(Prim::Lit(v.clone()))),
AstExpr::Progn(ref exprs) => if exprs.len() == 0 {
Left(Gc::new(Prim::Lit(Gc::new(Value::Nil(())))))
} else {
let mut body = exprs.clone();
let tail = Self::convert_expr(body.pop().unwrap());
Right(Self::convert_block(body, tail))
},
AstExpr::Variable(s) => Left(Gc::new(Prim::Var(s))),
AstExpr::Vector(ref v) => {
let mut v = v.clone();
v.reverse();
Right(Self::convert_prims(v, |mut v| {
v.reverse();
Gc::new(Expr::Prim(Gc::new(Prim::Vec(v))))
}))
},
}
}
fn convert_block(mut body: Vec<Gc<AstExpr<Self>>>, tail: Gc<Expr>) -> Gc<Expr> {
match body.pop() {
Some(expr) => {
let tail = if let AstExpr::Def(n, ref e) = *expr.clone() {
Expr::Let(Some(n), Self::convert_expr(e.clone()), tail)
} else {
Expr::Let(None, Self::convert_expr(expr), tail)
};
Self::convert_block(body, Gc::new(tail))
},
None => tail,
}
}
pub(crate) fn convert_expr(expr: Gc<AstExpr<Context>>) -> Gc<Expr> {
match Self::convert(expr) {
Left(prim) => Gc::new(Expr::Prim(prim)),
Right(expr) => expr,
}
}
fn convert_prim<F: FnOnce(Gc<Prim>) -> Gc<Expr>>(expr: Gc<AstExpr<Context>>, f: F) -> Gc<Expr> {
match Self::convert(expr) {
Left(prim) => f(prim),
Right(expr) => {
let name = gensym_prefix("anf");
let next = f(Gc::new(Prim::Var(name)));
Gc::new(Expr::Let(Some(name), expr, next))
},
}
}
fn convert_prims<F: FnOnce(Vec<Gc<Prim>>) -> Gc<Expr>>(exprs: Vec<Gc<AstExpr<Context>>>, f: F) -> Gc<Expr> {
let mut prims = Vec::new();
let mut conts = Vec::new();
for expr in exprs {
match Self::convert(expr) {
Left(prim) => prims.push(prim),
Right(expr) => {
let name = gensym_prefix("anf");
prims.push(Gc::new(Prim::Var(name)));
conts.push(move |next| Gc::new(Expr::Let(Some(name), expr, next)))
},
}
}
let mut expr = f(prims);
for cont in conts {
expr = cont(expr);
}
expr
}
}