pub use self::Term::*;
pub use self::Notation::*;
use self::Error::*;
use std::fmt;
use std::borrow::Cow;
use std::char::from_u32;
pub const PRETTY_LAMBDA: bool = true;
#[derive(Debug, PartialEq)]
pub enum Notation {
Classic,
DeBruijn
}
#[derive(PartialEq, Clone)]
pub enum Term {
Var(usize),
Abs(Box<Term>),
App(Box<Term>, Box<Term>)
}
#[derive(Debug, PartialEq)]
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 unvar_ref_mut(&mut self) -> Result<&mut usize, Error> {
match *self {
Var(ref mut 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(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((lhs, 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_cla(self, 0, 0))
}
}
#[doc(hidden)]
pub fn show_precedence_cla(term: &Term, context_precedence: usize, depth: u32) -> String {
match *term {
Var(i) => {
if depth >= i as u32 {
format!("{}", from_u32(depth + 97 - i as u32).expect("error while printing term"))
} else {
format!("{}", from_u32(96 + i as u32).expect("error while printing term"))
}
},
Abs(ref t) => {
let ret = {
format!("{}{}.{}", if PRETTY_LAMBDA { 'λ' } else { '\\' },
from_u32(depth + 97).expect("error while printing term"),
show_precedence_cla(t, 0, depth + 1)
)
};
parenthesize_if(&ret, context_precedence > 1).into()
},
App(ref t1, ref t2) => {
let ret = format!("{} {}",
show_precedence_cla(t1, 2, depth), show_precedence_cla(t2, 3, depth));
parenthesize_if(&ret, context_precedence == 3).into()
}
}
}
impl fmt::Debug for Term {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", show_precedence_dbr(self, 0, 0))
}
}
#[doc(hidden)]
pub fn show_precedence_dbr(term: &Term, context_precedence: usize, depth: u32) -> String {
match *term {
Var(i) => {
format!("{:X}", i)
},
Abs(ref t) => {
let ret = format!("{}{:?}", if PRETTY_LAMBDA { 'λ' } else { '\\' }, t);
parenthesize_if(&ret, context_precedence > 1).into()
},
App(ref t1, ref t2) => {
let ret = format!("{}{}",
show_precedence_dbr(t1, 2, depth), show_precedence_dbr(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()
}
}
#[macro_export]
macro_rules! app {
($term1:expr, $($term2:expr),+) => {
{
let mut term = $term1;
$(term = term.app($term2);)*
term
}
};
}
#[cfg(test)]
mod tests {
use super::{Var, PRETTY_LAMBDA};
use super::Notation::DeBruijn;
use parser::parse;
use arithmetic::{zero, succ, pred};
#[test]
fn app_macro() {
assert_eq!(app!(succ(), app!(Var(1), Var(2), Var(3))),
succ().app(Var(1).app(Var(2)).app(Var(3)))
);
}
#[test]
fn open_term_display() {
assert_eq!(&format!("{}", parse("λ2", DeBruijn).unwrap()), "λa.b");
assert_eq!(&format!("{}", parse("λ3", DeBruijn).unwrap()), "λa.c");
assert_eq!(&format!("{}", parse("λλ3", DeBruijn).unwrap()), "λa.λb.c");
assert_eq!(&format!("{}", parse("λλ4", DeBruijn).unwrap()), "λa.λb.d");
}
#[test]
fn display_modes() {
if PRETTY_LAMBDA {
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)");
assert_eq!(&format!("{:?}", zero()), "λλ1");
assert_eq!(&format!("{:?}", succ()), "λλλ2(321)");
assert_eq!(&format!("{:?}", pred()), "λλλ3(λλ1(24))(λ2)(λ1)");
} else {
assert_eq!(&format!("{}", zero()), r#"\a.\b.b"#);
assert_eq!(&format!("{}", succ()), r#"\a.\b.\c.b (a b c)"#);
assert_eq!(&format!("{}", pred()), r#"\a.\b.\c.a (\d.\e.e (d b)) (\d.c) (\d.d)"#);
assert_eq!(&format!("{:?}", zero()), r#"\\1"#);
assert_eq!(&format!("{:?}", succ()), r#"\\\2(321)"#);
assert_eq!(&format!("{:?}", pred()), r#"\\\3(\\1(24))(\2)(\1)"#);
}
}
}