pub use self::Term::*;
pub use self::Notation::*;
use self::TermError::*;
use std::fmt;
use std::borrow::Cow;
use std::char::from_u32;
#[cfg(feature = "backslash_lambda")]
pub const LAMBDA: char = '\\';
#[cfg(not(feature = "backslash_lambda"))]
pub const LAMBDA: char = 'λ';
pub const UD: Term = Var(0);
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum Notation {
Classic,
DeBruijn
}
#[derive(PartialEq, Clone, Hash, Eq)]
pub enum Term {
Var(usize),
Abs(Box<Term>),
App(Box<(Term, Term)>)
}
#[derive(Debug, PartialEq)]
pub enum TermError {
NotVar,
NotAbs,
NotApp,
}
impl Term {
pub fn unvar(self) -> Result<usize, TermError> {
if let Var(n) = self { Ok(n) } else { Err(NotVar) }
}
pub fn unvar_ref(&self) -> Result<&usize, TermError> {
if let Var(ref n) = *self { Ok(n) } else { Err(NotVar) }
}
pub fn unvar_mut(&mut self) -> Result<&mut usize, TermError> {
if let Var(ref mut n) = *self { Ok(n) } else { Err(NotVar) }
}
pub fn unabs(self) -> Result<Term, TermError> {
if let Abs(x) = self { Ok(*x) } else { Err(NotAbs) }
}
pub fn unabs_ref(&self) -> Result<&Term, TermError> {
if let Abs(ref x) = *self { Ok(x) } else { Err(NotAbs) }
}
pub fn unabs_mut(&mut self) -> Result<&mut Term, TermError> {
if let Abs(ref mut x) = *self { Ok(x) } else { Err(NotAbs) }
}
pub fn unapp(self) -> Result<(Term, Term), TermError> {
if let App(boxed) = self {
let (lhs, rhs) = *boxed;
Ok((lhs, rhs))
} else {
Err(NotApp)
}
}
pub fn unapp_ref(&self) -> Result<(&Term, &Term), TermError> {
if let App(boxed) = self {
let (ref lhs, ref rhs) = **boxed;
Ok((lhs, rhs))
} else {
Err(NotApp)
}
}
pub fn unapp_mut(&mut self) -> Result<(&mut Term, &mut Term), TermError> {
if let App(boxed) = self {
let (ref mut lhs, ref mut rhs) = **boxed;
Ok((lhs, rhs))
} else {
Err(NotApp)
}
}
pub fn lhs(self) -> Result<Term, TermError> {
if let Ok((lhs, _)) = self.unapp() { Ok(lhs) } else { Err(NotApp) }
}
pub fn lhs_ref(&self) -> Result<&Term, TermError> {
if let Ok((lhs, _)) = self.unapp_ref() { Ok(lhs) } else { Err(NotApp) }
}
pub fn lhs_mut(&mut self) -> Result<&mut Term, TermError> {
if let Ok((lhs, _)) = self.unapp_mut() { Ok(lhs) } else { Err(NotApp) }
}
pub fn rhs(self) -> Result<Term, TermError> {
if let Ok((_, rhs)) = self.unapp() { Ok(rhs) } else { Err(NotApp) }
}
pub fn rhs_ref(&self) -> Result<&Term, TermError> {
if let Ok((_, rhs)) = self.unapp_ref() { Ok(rhs) } else { Err(NotApp) }
}
pub fn rhs_mut(&mut self) -> Result<&mut Term, TermError> {
if let Ok((_, rhs)) = self.unapp_mut() { Ok(rhs) } else { Err(NotApp) }
}
pub fn is_supercombinator(&self) -> bool {
let mut stack = vec![(0usize, self)];
while let Some((depth, term)) = stack.pop() {
match term {
Var(i) => if *i > depth { return false },
Abs(ref t) => stack.push((depth + 1, t)),
App(boxed) => {
let (ref f, ref a) = **boxed;
stack.push((depth, f));
stack.push((depth, a))
}
}
}
true
}
}
pub fn abs(term: Term) -> Term { Abs(Box::new(term)) }
pub fn app(lhs: Term, rhs: Term) -> Term { App(Box::new((lhs, rhs))) }
impl fmt::Display for Term {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", show_precedence_cla(self, 0, 0))
}
}
fn show_precedence_cla(term: &Term, context_precedence: usize, depth: u32) -> String {
match term {
Var(0) => {
"undefined".to_owned()
},
Var(i) => {
if depth >= *i as u32 {
from_u32(depth + 97 - *i as u32).expect("error while printing term").to_string()
} else {
from_u32(96 + *i as u32).expect("error while printing term").to_string()
}
},
Abs(ref t) => {
let ret = {
format!("{}{}.{}",
LAMBDA,
from_u32(depth + 97).expect("error while printing term"),
show_precedence_cla(t, 0, depth + 1)
)
};
parenthesize_if(&ret, context_precedence > 1).into()
},
App(boxed) => {
let (ref t1, ref t2) = **boxed;
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))
}
}
fn show_precedence_dbr(term: &Term, context_precedence: usize, depth: u32) -> String {
match term {
Var(0) => {
"undefined".to_owned()
},
Var(i) => {
format!("{:X}", i)
},
Abs(ref t) => {
let ret = format!("{}{:?}", LAMBDA, t);
parenthesize_if(&ret, context_precedence > 1).into()
},
App(boxed) => {
let (ref t1, ref t2) = **boxed;
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 = app(term, $term2);)*
term
}
};
}
#[macro_export]
macro_rules! abs {
($n:expr, $term:expr) => {
{
let mut term = $term;
for _ in 0..$n {
term = abs(term);
}
term
}
};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn app_macro() {
assert_eq!(app!(Var(4), app!(Var(1), Var(2), Var(3))),
app(Var(4), app(app(Var(1), Var(2)), Var(3)))
);
}
#[test]
fn abs_macro() {
assert_eq!(abs!(4, Var(1)),
abs(abs(abs(abs(Var(1)))))
);
assert_eq!(abs!(2, app(Var(1), Var(2))),
abs(abs(app(Var(1), Var(2))))
);
}
#[test]
fn open_term_display() {
assert_eq!(&abs(Var(2)).to_string(), "λa.b");
assert_eq!(&abs(Var(3)).to_string(), "λa.c");
assert_eq!(&abs!(2, Var(3)).to_string(), "λa.λb.c");
assert_eq!(&abs!(2, Var(4)).to_string(), "λa.λb.d");
}
#[test]
fn display_modes() {
let zero = abs!(2, Var(1));
let succ = abs!(3, app(Var(2), app!(Var(3), Var(2), Var(1))));
let pred = abs!(3, app!(
Var(3),
abs!(2, app(Var(1), app(Var(2), Var(4)))),
abs(Var(2)),
abs(Var(1))
));
assert_eq!(&zero.to_string(), "λa.λb.b");
assert_eq!(&succ.to_string(), "λa.λb.λc.b (a b c)");
assert_eq!(&pred.to_string(), "λ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)");
}
#[test]
fn is_supercombinator() {
assert_eq!(abs(Var(1)).is_supercombinator(), true);
assert_eq!(app(abs(Var(1)), abs(Var(1))).is_supercombinator(), true);
assert_eq!(abs!(10, Var(10)).is_supercombinator(), true);
assert_eq!(abs!(10, app(Var(10), Var(10))).is_supercombinator(), true);
assert_eq!(Var(1).is_supercombinator(), false);
assert_eq!(abs(Var(2)).is_supercombinator(), false);
assert_eq!(app(abs(Var(1)), Var(1)).is_supercombinator(), false);
assert_eq!(abs!(10, Var(11)).is_supercombinator(), false);
assert_eq!(abs!(10, app(Var(10), Var(11))).is_supercombinator(), false);
}
}