use self::Term::*;
use self::Error::*;
use std::fmt;
use std::borrow::Cow;
use std::char::from_u32;
pub const DISPLAY_PRETTY: bool = true;
pub const DISPLAY_CLASSIC: bool = false;
#[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 abs(term: Term) -> Term { Abs(Box::new(term)) }
pub fn app(lhs: Term, rhs: Term) -> Term { App(Box::new(lhs), Box::new(rhs)) }
impl fmt::Display for Term {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", show_precedence(self, 0, 0))
}
}
#[doc(hidden)]
pub fn show_precedence(term: &Term, context_precedence: usize, depth: u32) -> String {
match *term {
Var(i) => if DISPLAY_CLASSIC {
format!("{}", from_u32(depth + 97 - i as u32).unwrap())
} else {
format!("{:X}", i)
},
Abs(ref t) => {
let ret = if DISPLAY_CLASSIC {
format!("{}{}. {}", if DISPLAY_PRETTY { 'λ' } else { '\\' }, from_u32(depth + 97).unwrap(), show_precedence(t, 0, depth + 1))
} else {
format!("{}{}", if DISPLAY_PRETTY { 'λ' } else { '\\' }, t)
};
parenthesize_if(&ret, context_precedence > 1).into()
},
App(ref t1, ref t2) => {
let ret = if DISPLAY_CLASSIC {
format!("{} {}", show_precedence(t1, 2, depth), show_precedence(t2, 3, depth))
} else {
format!("{}{}", show_precedence(t1, 2, depth), show_precedence(t2, 3, depth))
};
parenthesize_if(&ret, context_precedence == 3).into()
}
}
}
fn parenthesize_if(input: &str, condition: bool) -> Cow<str> {
if condition {
format!("({})", input).into()
} else {
input.into()
}
}
#[cfg(test)]
mod test {
use super::DISPLAY_CLASSIC;
use arithmetic::{zero, succ, pred};
#[test]
fn display_modes() {
if DISPLAY_CLASSIC {
assert_eq!(&format!("{}", zero()), "λa. λb. b");
assert_eq!(&format!("{}", succ()), "λa. λb. λc. b (a b c)");
assert_eq!(&format!("{}", pred()), "λa. λb. λc. a (λd. λe. e (d b)) (λd. c) (λd. d)");
} else {
assert_eq!(&format!("{}", zero()), "λλ1");
assert_eq!(&format!("{}", succ()), "λλλ2(321)");
assert_eq!(&format!("{}", pred()), "λλλ3(λλ1(24))(λ2)(λ1)");
}
}
}