use term::*;
use term::Term::*;
use self::Order::*;
pub const SHOW_REDUCTIONS: bool = false;
pub const EVALUATION_ORDER: Order = Normal;
#[derive(Debug, PartialEq)]
pub enum Order {
Normal,
Applicative
}
pub fn apply(mut lhs: Term, rhs: &Term) -> Result<Term, Error> {
_apply(&mut lhs, rhs, 0);
lhs.unabs()
}
fn _apply(lhs: &mut Term, rhs: &Term, depth: usize) {
match *lhs {
Var(i) => if i == depth {
*lhs = rhs.clone(); update_free_variables(lhs, depth - 1, 0); } else if i > depth {
*lhs = Var(i - 1) },
Abs(_) => {
_apply(lhs.unabs_ref_mut().unwrap(), rhs, depth + 1)
},
App(_, _) => {
_apply(lhs.lhs_ref_mut().unwrap(), rhs, depth);
_apply(lhs.rhs_ref_mut().unwrap(), rhs, depth)
}
}
}
fn update_free_variables(term: &mut Term, added_depth: usize, own_depth: usize) {
match *term {
Var(i) => if i > own_depth {
*term = Var(i + added_depth)
},
Abs(_) => {
update_free_variables(term.unabs_ref_mut().unwrap(), added_depth, own_depth + 1)
},
App(_, _) => {
update_free_variables(term.lhs_ref_mut().unwrap(), added_depth, own_depth);
update_free_variables(term.rhs_ref_mut().unwrap(), added_depth, own_depth)
}
}
}
impl Term {
pub fn apply(self, rhs: &Term) -> Result<Term, Error> {
apply(self, rhs)
}
pub fn eval(self) -> Result<Term, Error> {
let (lhs, rhs) = try!(self.unapp());
apply(lhs, &rhs)
}
pub fn beta_once(&mut self) {
match EVALUATION_ORDER {
Normal => self._beta_once_normal(0),
Applicative => self._beta_once_applicative(0)
};
}
fn _beta_once_normal(&mut self, depth: u32) -> bool {
match *self {
Var(_) => false,
Abs(_) => self.unabs_ref_mut().unwrap()._beta_once_normal(depth + 1),
App(_, _) => {
if self.lhs_ref().unwrap().unabs_ref().is_ok() {
let copy = self.clone();
if SHOW_REDUCTIONS { print!(" {} reduces to ", show_precedence(self, 0, depth)) };
*self = copy.eval().unwrap();
if SHOW_REDUCTIONS { println!("{}", show_precedence(self, 0, depth)) }
true
} else {
if !self.lhs_ref_mut().unwrap()._beta_once_normal(depth) {
self.rhs_ref_mut().unwrap()._beta_once_normal(depth)
} else {
true
}
}
}
}
}
fn _beta_once_applicative(&mut self, depth: u32) -> bool {
match *self {
Var(_) => false,
Abs(_) => self.unabs_ref_mut().unwrap()._beta_once_applicative(depth + 1),
App(_, _) => {
if !self.lhs_ref().unwrap().is_beta_reducible() &&
!self.rhs_ref().unwrap().is_beta_reducible() &&
self.lhs_ref().unwrap().unabs_ref().is_ok()
{
let copy = self.clone();
if SHOW_REDUCTIONS { print!(" {} reduces to ", show_precedence(self, 0, depth)) };
*self = copy.eval().unwrap();
if SHOW_REDUCTIONS { println!("{}", show_precedence(self, 0, depth)) }
true
} else {
if !self.lhs_ref_mut().unwrap()._beta_once_applicative(depth) {
self.rhs_ref_mut().unwrap()._beta_once_applicative(depth)
} else {
true
}
}
}
}
}
pub fn is_beta_reducible(&self) -> bool {
match *self {
Var(_) => false,
Abs(_) => self.unabs_ref().unwrap().is_beta_reducible(),
App(_, _) => {
if self.lhs_ref().unwrap().unabs_ref().is_ok() {
true
} else {
self.lhs_ref().unwrap().is_beta_reducible() ||
self.rhs_ref().unwrap().is_beta_reducible()
}
}
}
}
pub fn beta_full(&mut self) {
let mut tmp;
loop {
if SHOW_REDUCTIONS { println!("reducing {}", self) }
tmp = self.clone();
self.beta_once();
if *self == tmp { break }
}
if SHOW_REDUCTIONS { println!(" doesn't reduce") }
}
}
pub fn beta_full(mut term: Term) -> Term {
term.beta_full();
term
}
pub fn beta_once(mut term: Term) -> Term {
term.beta_once();
term
}
#[cfg(test)]
mod test {
use super::*;
use parser::parse;
#[test]
fn normal_order() {
if EVALUATION_ORDER == Normal {
let should_reduce = parse(&"(λ2)((λ111)(λ111))").unwrap();
assert_eq!(beta_full(should_reduce), Var(1))
}
}
#[test]
fn applicative_order() {
if EVALUATION_ORDER == Applicative {
let shouldnt_reduce = parse(&"(λ2)((λ111)(λ111))").unwrap();
assert_eq!(beta_once(shouldnt_reduce), parse(&"(λ2)((λ111)(λ111)(λ111))").unwrap())
}
}
}