use std::collections::BTreeSet;
use symbol::Symbol;
use ast::{Decl, Expr, Literal, Op, Pattern};
use cst::{Decl as CstDecl, Expr as CstExpr};
#[derive(Clone, Debug, Fail, PartialEq)]
pub enum ASTConversionError {
#[fail(display = "Found duplicate variable `{}' in arguments to `{}'.", _1, _0)]
DuplicateArgVar(Symbol, Symbol),
}
pub fn check_args<Aux>(args: &[Pattern<Aux>]) -> Result<(), Symbol> {
let mut vars = BTreeSet::new();
fn check_arg<Aux>(vars: &mut BTreeSet<Symbol>, arg: &Pattern<Aux>) -> Result<(), Symbol> {
match *arg {
Pattern::Binding(n, _) => if vars.insert(n) {
Ok(())
} else {
Err(n)
},
Pattern::Cons(ref l, ref r, _) => {
check_arg(vars, l)?;
check_arg(vars, r)
}
Pattern::Literal(_, _) => Ok(()),
}
}
for arg in args {
check_arg(&mut vars, arg)?;
}
Ok(())
}
impl<Aux> Decl<Aux> {
pub fn to_cst(&self) -> CstDecl {
fn remove_aux_from_pattern<Aux>(pat: &Pattern<Aux>) -> Pattern<()> {
match *pat {
Pattern::Binding(var, _) => Pattern::Binding(var, ()),
Pattern::Cons(ref l, ref r, _) => Pattern::Cons(
Box::new(remove_aux_from_pattern(l)),
Box::new(remove_aux_from_pattern(r)),
(),
),
Pattern::Literal(lit, _) => Pattern::Literal(lit, ()),
}
}
CstDecl {
name: self.name,
args: self.args.iter().map(remove_aux_from_pattern).collect(),
body: self.body.to_cst(),
}
}
}
impl CstDecl {
pub fn into_ast(self) -> Result<Decl<()>, ASTConversionError> {
check_args(&self.args).map_err(|n| ASTConversionError::DuplicateArgVar(self.name, n))?;
Ok(Decl {
name: self.name,
args: self.args,
body: self.body.into_ast()?,
aux: (),
})
}
}
impl<Aux> Expr<Aux> {
pub fn to_cst(&self) -> CstExpr {
match *self {
Expr::If(ref c, ref t, ref e, _) => CstExpr::If(
Box::new(c.to_cst()),
Box::new(t.to_cst()),
Box::new(e.to_cst()),
),
Expr::Literal(l, _) => CstExpr::Literal(l),
Expr::Op(op, ref l, ref r, _) => {
CstExpr::Op(op, Box::new(l.to_cst()), Box::new(r.to_cst()))
}
Expr::Variable(var, _) => CstExpr::Variable(var),
}
}
}
impl CstExpr {
pub fn into_ast(self) -> Result<Expr<()>, ASTConversionError> {
match self {
CstExpr::If(c, t, e) => Ok(Expr::If(
Box::new(c.into_ast()?),
Box::new(t.into_ast()?),
Box::new(e.into_ast()?),
(),
)),
CstExpr::List(mut es) => {
let mut expr = Expr::Literal(Literal::Nil, ());
while let Some(e) = es.pop() {
expr = Expr::Op(Op::Cons, Box::new(e.into_ast()?), Box::new(expr), ());
}
Ok(expr)
}
CstExpr::Literal(lit) => Ok(Expr::Literal(lit, ())),
CstExpr::Op(op, l, r) => Ok(Expr::Op(
op,
Box::new(l.into_ast()?),
Box::new(r.into_ast()?),
(),
)),
CstExpr::Variable(sym) => Ok(Expr::Variable(sym, ())),
}
}
}