use self::Term::*;
use self::Error::*;
use std::fmt;
use std::borrow::Cow;
#[derive(Debug, PartialEq, Clone)]
pub enum Term {
Var(usize),
Abs(Box<Term>),
App(Box<Term>, Box<Term>)
}
#[derive(Debug, PartialEq, Clone)]
pub enum Error {
NotAVar,
NotAnAbs,
NotAnApp,
NotANum,
NotAPair,
NotAList
}
impl Term {
pub fn app(self, argument: Term) -> Term { App(Box::new(self), Box::new(argument)) }
pub fn unvar(self) -> Result<usize, Error> {
match self {
Var(n) => Ok(n),
_ => Err(NotAVar)
}
}
pub fn unvar_ref(&self) -> Result<&usize, Error> {
match *self {
Var(ref n) => Ok(&n),
_ => Err(NotAVar)
}
}
pub fn unabs(self) -> Result<Term, Error> {
match self {
Abs(x) => Ok(*x),
_ => Err(NotAnAbs)
}
}
pub fn unabs_ref(&self) -> Result<&Term, Error> {
match *self {
Abs(ref x) => Ok(&*x),
_ => Err(NotAnAbs)
}
}
pub fn unabs_ref_mut(&mut self) -> Result<&mut Term, Error> {
match *self {
Abs(ref mut x) => Ok(&mut *x),
_ => Err(NotAnAbs)
}
}
pub fn unapp(self) -> Result<(Term, Term), Error> {
match self {
App(lhs, rhs) => Ok((*lhs, *rhs)),
_ => Err(NotAnApp)
}
}
pub fn unapp_ref(&self) -> Result<(&Term, &Term), Error> {
match *self {
App(ref lhs, ref rhs) => Ok((&*lhs, &*rhs)),
_ => Err(NotAnApp)
}
}
pub fn unapp_ref_mut(&mut self) -> Result<(&mut Term, &mut Term), Error> {
match *self {
App(ref mut lhs, ref mut rhs) => Ok((&mut *lhs, &mut *rhs)),
_ => Err(NotAnApp)
}
}
pub fn lhs(self) -> Result<Term, Error> {
if let Ok((lhs, _)) = self.unapp() { Ok(lhs) } else { Err(NotAnApp) }
}
pub fn lhs_ref(&self) -> Result<&Term, Error> {
if let Ok((lhs, _)) = self.unapp_ref() { Ok(lhs) } else { Err(NotAnApp) }
}
pub fn lhs_ref_mut(&mut self) -> Result<&mut Term, Error> {
if let Ok((lhs, _)) = self.unapp_ref_mut() { Ok(lhs) } else { Err(NotAnApp) }
}
pub fn rhs(self) -> Result<Term, Error> {
if let Ok((_, rhs)) = self.unapp() { Ok(rhs) } else { Err(NotAnApp) }
}
pub fn rhs_ref(&self) -> Result<&Term, Error> {
if let Ok((_, rhs)) = self.unapp_ref() { Ok(rhs) } else { Err(NotAnApp) }
}
pub fn rhs_ref_mut(&mut self) -> Result<&mut Term, Error> {
if let Ok((_, rhs)) = self.unapp_ref_mut() { Ok(rhs) } else { Err(NotAnApp) }
}
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 abs(term: Term) -> Term { Abs(Box::new(term)) }
pub fn app(lhs: Term, rhs: Term) -> Term { App(Box::new(lhs), Box::new(rhs)) }
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; 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.clone(), 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)
}
}
}
pub const DISPLAY_PRETTY: bool = true;
impl fmt::Display for Term {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", show_precedence(0, self))
}
}
fn show_precedence(context_precedence: usize, term: &Term) -> String {
match *term {
Var(i) => format!("{:X}", i), Abs(ref t) => {
let ret = format!("{}{}", if DISPLAY_PRETTY { 'λ' } else { '\\' }, t);
parenthesize_if(context_precedence > 1, &ret).into()
},
App(ref t1, ref t2) => {
let ret = format!("{}{}", show_precedence(2, t1), show_precedence(3, t2));
parenthesize_if(context_precedence == 3, &ret).into()
}
}
}
fn parenthesize_if(condition: bool, input: &str) -> Cow<str> {
if condition {
format!("({})", input).into()
} else {
input.into()
}
}
#[cfg(test)]
mod test {
use arithmetic::{zero, succ, pred};
use combinators::i;
use parser::parse;
use super::{apply, abs};
use super::Term::Var;
#[test]
fn applying() {
let lhs = parse(&"λλ42(λ13)").unwrap();
let rhs = parse(&"λ51").unwrap();
let result = parse(&"λ3(λ61)(λ1(λ71))").unwrap();
assert_eq!(apply(lhs, rhs), Ok(result));
assert_eq!(i().app(zero()).eval().unwrap(), abs(abs(Var(1))));
}
#[test]
fn displaying_terms() {
assert_eq!(&format!("{}", zero()), "λλ1");
assert_eq!(&format!("{}", succ()), "λλλ2(321)");
assert_eq!(&format!("{}", pred()), "λλλ3(λλ1(24))(λ2)(λ1)");
}
}