use std::collections::{BTreeMap, BTreeSet};
use failure::Error;
use symbol::Symbol;
use ast::{Decl, Expr, Op, Pattern};
pub fn reducible<Aux>(expr: &Expr<Aux>, decls: &[Decl<Aux>]) -> bool {
match *expr {
Expr::If(_, _, _, _) => true,
Expr::Literal(_, _) => false,
Expr::Op(Op::App, ref l, ref r, _) => {
reducible(l, decls) || reducible(r, decls)
|| beta_number(expr, decls).map(|n| n == 0).unwrap_or(false)
}
Expr::Op(Op::Cons, ref l, ref r, _) => reducible(l, decls) || reducible(r, decls),
Expr::Op(_, _, _, _) => true,
Expr::Variable(var, _) => {
let decl = decls
.iter()
.find(|decl| decl.name == var)
.unwrap_or_else(|| panic!("Unknown variable {}", var));
decl.args.is_empty()
}
}
}
pub fn beta_number<Aux>(expr: &Expr<Aux>, decls: &[Decl<Aux>]) -> Option<isize> {
match *expr {
Expr::Op(Op::App, ref l, _, _) => beta_number(l, decls).map(|n| n - 1),
Expr::Variable(var, _) => {
let decl = decls
.iter()
.find(|decl| decl.name == var)
.unwrap_or_else(|| panic!("Unknown variable {}", var));
Some(decl.args.len() as isize)
}
_ => None,
}
}
pub fn apply<Aux: Clone>(
func: Symbol,
args: &[Expr<Aux>],
decls: &[Decl<Aux>],
) -> Result<Expr<Aux>, Error> {
let decl = decls
.iter()
.find(|decl| decl.name == func)
.ok_or_else(|| format_err!("No matching declaration for call to function {}", func))?;
apply_to(decl, args)
}
pub fn apply_to<Aux: Clone>(decl: &Decl<Aux>, args: &[Expr<Aux>]) -> Result<Expr<Aux>, Error> {
let args = matches_all(&decl.args, &args).unwrap();
Ok(apply_replacement(&decl.body, &args))
}
fn apply_replacement<Aux: Clone>(
expr: &Expr<Aux>,
replacement: &BTreeMap<Symbol, Expr<Aux>>,
) -> Expr<Aux> {
match *expr {
Expr::If(ref c, ref t, ref e, ref aux) => Expr::If(
Box::new(apply_replacement(c, replacement)),
Box::new(apply_replacement(t, replacement)),
Box::new(apply_replacement(e, replacement)),
aux.clone(),
),
Expr::Literal(lit, ref aux) => Expr::Literal(lit, aux.clone()),
Expr::Op(op, ref l, ref r, ref aux) => Expr::Op(
op,
Box::new(apply_replacement(l, replacement)),
Box::new(apply_replacement(r, replacement)),
aux.clone(),
),
Expr::Variable(ref var, ref aux) => {
if let Some(expr) = replacement.get(var) {
expr.clone()
} else {
Expr::Variable(*var, aux.clone())
}
}
}
}
fn matches_all<Aux: Clone>(
pats: &[Pattern<Aux>],
args: &[Expr<Aux>],
) -> Option<BTreeMap<Symbol, Expr<Aux>>> {
assert_eq!(pats.len(), args.len());
let mut map = BTreeMap::new();
for (pat, arg) in pats.iter().zip(args) {
map.extend(pat.matches(arg)?);
}
Some(map)
}
pub fn transitive_closure<F, I1, I2, T>(initial: I1, op: F) -> BTreeSet<T>
where
F: Fn(&T) -> I2,
I1: IntoIterator<Item = T>,
I2: IntoIterator<Item = T>,
T: Clone + Ord,
{
let mut set = initial.into_iter().collect::<BTreeSet<T>>();
let mut prev = BTreeSet::new();
while set != prev {
prev = set.clone();
set.extend(prev.iter().flat_map(&op));
}
set
}