use term::{Term, Error, abs, app};
use term::Term::*;
use term::Error::*;
use church::booleans::{tru, fls};
use church::pairs::{pair, fst, snd};
use church::numerals::zero;
use combinators::z;
use std::ops::Index;
use std::mem;
pub fn nil() -> Term { fls() }
pub fn null() -> Term {
abs(app!(Var(1), abs!(3, fls()), tru()))
}
pub fn cons() -> Term { pair() }
pub fn head() -> Term { fst() }
pub fn tail() -> Term { snd() }
pub fn length() -> Term {
app!(
z(),
abs!(3, app!(
Var(1),
abs!(5, Var(1)),
abs!(2, Var(2)),
abs(Var(3)),
abs(app!(
Var(4),
abs!(2, app(Var(2), app!(Var(5), Var(2), Var(1)))),
app(Var(2), abs!(2, Var(1)))
)),
abs(Var(1))
)),
zero()
)
}
pub fn reverse() -> Term {
app!(
z(),
abs!(3, app!(
Var(1),
abs!(5, Var(1)),
abs!(2, Var(2)),
abs(Var(3)),
abs(app!(
Var(4),
abs(app!(
Var(1),
app(Var(3), abs!(2, Var(2))),
Var(4)
)),
app(Var(2), abs!(2, Var(1)))
)),
abs(Var(1))
)),
nil()
)
}
pub fn list() -> Term {
abs(app!(
Var(1),
abs!(3, app(Var(3), app!(pair(), Var(1), Var(2)))),
reverse(),
nil()
))
}
pub fn append() -> Term {
z().app(
abs!(3, app!(
Var(2),
abs!(5, Var(1)),
abs!(2, Var(2)),
abs(Var(2)),
abs!(2, app!(
Var(1),
app(Var(4), abs!(2, Var(2))),
app!(
Var(5),
app(Var(4), abs!(2, Var(1))),
Var(3)
)
)),
Var(1)
))
)
}
pub fn index() -> Term {
abs!(2, app!(
Var(2),
abs(app(Var(1), abs!(2, Var(1)))),
Var(1),
abs!(2, Var(2))
))
}
pub fn map() -> Term {
z().app(
abs!(3, app!(
Var(1),
abs!(5, Var(1)),
abs!(2, Var(2)),
abs!(3, Var(1)),
abs!(2, app!(
Var(1),
app(
Var(4),
app(Var(3), abs!(2, Var(2)))
),
app!(
Var(5),
Var(4),
app(Var(3), abs!(2, Var(1)))
)
)),
abs(Var(1))
))
)
}
pub fn foldl() -> Term {
z().app(
abs!(4, app!(
Var(1),
abs!(5, Var(1)),
abs!(2, Var(2)),
abs(Var(3)),
abs(app!(
Var(5),
Var(4),
app!(
Var(4),
Var(3),
app(Var(2), abs!(2, Var(2)))
),
app(Var(2), abs!(2, Var(1)))
)),
abs(Var(1))
))
)
}
pub fn foldr() -> Term {
abs!(3, app!(
z(),
abs!(2, app!(
Var(1),
abs!(5, Var(1)),
abs!(2, Var(2)),
abs(Var(5)),
abs(app!(
Var(6),
app(Var(2), abs!(2, Var(2))),
app(Var(3), app(Var(2), abs!(2, Var(1))))
)),
abs(Var(1))
)),
Var(1)
))
}
pub fn filter() -> Term {
z().app(
abs!(3, app!(
Var(1),
abs!(5, Var(1)),
abs!(2, Var(2)),
abs!(3, Var(1)),
abs(app!(
Var(3),
app(Var(2), abs!(2, Var(2))),
abs!(2, app!(
Var(1),
app(Var(4), abs!(2, Var(2))),
Var(2)
)),
abs(Var(1)),
app!(
Var(4),
Var(3),
app(Var(2), abs!(2, Var(1)))
)
)),
abs(Var(1))
))
)
}
pub fn last() -> Term {
app(
z(),
abs!(2, app!(
null(),
Var(1),
abs(fls()),
abs(app!(
Var(2),
abs!(2, Var(1)),
abs!(5, Var(1)),
abs!(2, Var(2)),
app(Var(2), abs!(2, Var(2))),
app(Var(3), app(Var(2), abs!(2, Var(1))))
)),
abs(Var(1))
))
)
}
impl Term {
pub fn is_empty(&self) -> bool {
*self == nil()
}
fn last_ref(&self) -> Result<&Term, Error> {
if !self.is_pair() { return Err(NotAList) }
let mut last_candidate = self.snd_ref()?;
while let Ok(second) = last_candidate.snd_ref() {
last_candidate = second;
}
Ok(last_candidate)
}
pub fn is_list(&self) -> bool {
self.last_ref() == Ok(&nil())
}
pub fn uncons(self) -> Result<(Term, Term), Error> {
if !self.is_list() {
Err(NotAList)
} else {
self.unpair()
}
}
pub fn uncons_ref(&self) -> Result<(&Term, &Term), Error> {
if !self.is_list() {
Err(NotAList)
} else {
self.unpair_ref()
}
}
pub fn uncons_mut(&mut self) -> Result<(&mut Term, &mut Term), Error> {
if !self.is_list() {
Err(NotAList)
} else {
self.unpair_mut()
}
}
pub fn head(self) -> Result<Term, Error> {
Ok(self.uncons()?.0)
}
pub fn head_ref(&self) -> Result<&Term, Error> {
Ok(self.uncons_ref()?.0)
}
pub fn head_mut(&mut self) -> Result<&mut Term, Error> {
Ok(self.uncons_mut()?.0)
}
pub fn tail(self) -> Result<Term, Error> {
Ok(self.uncons()?.1)
}
pub fn tail_ref(&self) -> Result<&Term, Error> {
Ok(self.uncons_ref()?.1)
}
pub fn tail_mut(&mut self) -> Result<&mut Term, Error> {
Ok(self.uncons_mut()?.1)
}
pub fn len(&self) -> Result<usize, Error> {
let mut inner = self;
let mut n = 0;
while *inner != nil() {
n += 1;
inner = inner.tail_ref()?;
}
Ok(n)
}
pub fn push(self, term: Term) -> Result<Term, Error> {
if !self.is_list() && self != nil() { return Err(NotAList) }
Ok(abs(app!(Var(1), term, self)))
}
pub fn pop(&mut self) -> Result<Term, Error> {
let to_uncons = mem::replace(self, Var(0)); let (head, tail) = to_uncons.uncons()?;
mem::replace(self, tail);
Ok(head)
}
}
impl From<Vec<Term>> for Term {
fn from(terms: Vec<Term>) -> Self {
let mut output = nil();
for term in terms.into_iter().rev() {
output = output.push(term).unwrap(); }
output
}
}
impl Iterator for Term {
type Item = Term;
fn next(&mut self) -> Option<Term> {
if self.is_empty() {
None
} else {
Some(self.pop().unwrap()) }
}
}
impl Index<usize> for Term {
type Output = Term;
fn index(&self, i: usize) -> &Self::Output {
if !self.is_list() { panic!("attempting to index something that is not a list!") }
if i == 0 { return self.head_ref().unwrap() }
let mut candidate = self.snd_ref().expect("index out of bounds!");
for _ in 1..i {
candidate = candidate.snd_ref().expect("index out of bounds!")
}
candidate.head_ref().unwrap() }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn list_length() {
assert_eq!(nil().len(), Ok(0));
assert_eq!(Term::from(vec![]).len(), Ok(0));
assert_eq!(Term::from(vec![1.into()]).len(), Ok(1));
assert_eq!(Term::from(vec![1.into(), 2.into()]).len(), Ok(2));
assert_eq!(Term::from(vec![1.into(), 2.into(), 3.into()]).len(), Ok(3));
}
#[test]
fn list_head_tail() {
let list = Term::from(vec![1.into(), 2.into(), 3.into()]);
assert_eq!(list.head_ref(), Ok(&1.into()));
assert_eq!(list.tail_ref(), Ok(&vec![2.into(), 3.into()].into()));
assert_eq!(
list.tail_ref().and_then(|t| t.head_ref()),
Ok(&2.into())
);
assert_eq!(
list.tail_ref().and_then(|t| t.tail_ref()).and_then(|t| t.head_ref()),
Ok(&3.into())
);
assert_eq!(list.uncons(), Ok((1.into(), vec![2.into(), 3.into()].into())));
}
#[test]
fn iterating_list() {
let list = Term::from(vec![1.into(), 2.into(), 3.into()]);
let mut iter = list.into_iter();
assert_eq!(iter.next(), Some(1.into()));
assert_eq!(iter.next(), Some(2.into()));
assert_eq!(iter.next(), Some(3.into()));
assert_eq!(iter.next(), None);
}
#[test]
fn indexing_list() {
let list = Term::from(vec![0.into(), 1.into(), 2.into(), 3.into(), 4.into()]);
assert_eq!(list[0], 0.into());
assert_eq!(list[1], 1.into());
assert_eq!(list[2], 2.into());
assert_eq!(list[3], 3.into());
assert_eq!(list[4], 4.into());
}
}