use term::*;
use term::Term::*;
use term::Error::*;
use booleans::*;
use pair::*;
use arithmetic::{zero, succ};
use combinators::y;
use std::ops::Index;
pub fn nil() -> Term { fls() }
pub fn null() -> Term {
abs(
Var(1)
.app(abs(abs(abs(fls()))))
.app(tru())
)
}
pub fn cons() -> Term { pair() }
pub fn head() -> Term { first() }
pub fn tail() -> Term { second() }
pub fn length() -> Term {
y()
.app(
abs(abs(abs(
null()
.app(Var(1))
.app(Var(2))
.app(
Var(3)
.app(succ().app(Var(2)))
.app(second().app(Var(1)))
)
)))
)
.app(zero())
}
pub fn reverse() -> Term {
y()
.app(
abs(abs(abs(
null()
.app(Var(1))
.app(Var(2))
.app(
Var(3)
.app(
pair()
.app(first().app(Var(1)))
.app(Var(2))
)
.app(second().app(Var(1)))
)
)))
)
.app(nil())
}
pub fn list() -> Term {
abs(
Var(1)
.app(
abs(abs(abs(
Var(3).app(
pair()
.app(Var(1))
.app(Var(2))
)
)))
)
.app(reverse())
.app(nil())
)
}
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 = try!(self.snd_ref());
while let Ok(ref 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_ref_mut(&mut self) -> Result<(&mut Term, &mut Term), Error> {
if !self.is_list() {
Err(NotAList)
} else {
self.unpair_ref_mut()
}
}
pub fn head(self) -> Result<Term, Error> {
Ok(try!(self.uncons()).0)
}
pub fn head_ref(&self) -> Result<&Term, Error> {
Ok(try!(self.uncons_ref()).0)
}
pub fn head_ref_mut(&mut self) -> Result<&mut Term, Error> {
Ok(try!(self.uncons_ref_mut()).0)
}
pub fn tail(self) -> Result<Term, Error> {
Ok(try!(self.uncons()).1)
}
pub fn tail_ref(&self) -> Result<&Term, Error> {
Ok(try!(self.uncons_ref()).1)
}
pub fn tail_ref_mut(&mut self) -> Result<&mut Term, Error> {
Ok(try!(self.uncons_ref_mut()).1)
}
pub fn len(&self) -> Result<usize, Error> {
let mut inner = self;
let mut n = 0;
while *inner != nil() {
n += 1;
inner = try!(inner.tail_ref());
}
Ok(n)
}
pub fn push(self, term: Term) -> Term {
abs(Var(1).app(term).app(self))
}
pub fn pop(&mut self) -> Result<Term, Error> {
let (head, tail) = try!(self.clone().uncons()); *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);
}
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 test {
use super::*;
use reduction::beta_full;
#[test]
fn empty_list() {
let nil = nil();
assert!(!nil.is_list());
assert!(nil.is_empty());
}
#[test]
fn list_push() {
let list_pushed = nil().push(0.into()).push(1.into()).push(1.into());
let list_consed = beta_full(
cons().app(1.into()).app(cons().app(1.into()).app(cons().app(0.into()).app(nil())))
);
assert_eq!(list_pushed, list_consed);
}
#[test]
fn list_from_vector() {
let list_from_vec = Term::from(vec![1.into(), 1.into(), 0.into()]);
let list_pushed = nil().push(0.into()).push(1.into()).push(1.into());
assert_eq!(list_from_vec, list_pushed);
}
#[test]
fn list_length() {
let list0 = nil();
assert_eq!(list0.len(), Ok(0));
let list1 = list0.push(1.into());
assert_eq!(list1.len(), Ok(1));
let list2 = list1.push(1.into());
assert_eq!(list2.len(), Ok(2));
let list3 = list2.push(1.into());
assert_eq!(list3.len(), Ok(3));
}
#[test]
fn list_operations() {
let list_354 = Term::from(vec![3.into(), 5.into(), 4.into()]);
assert!(list_354.is_list());
assert_eq!(list_354.head_ref(), Ok(&3.into()));
assert_eq!(list_354.tail_ref(), Ok(&Term::from(vec![5.into(), 4.into()])));
assert_eq!(list_354.tail_ref().and_then(|t| t.head_ref()), Ok(&5.into()));
assert_eq!(list_354.tail_ref()
.and_then(|t| t.tail_ref())
.and_then(|t| t.head_ref()), Ok(&4.into()));
let unconsed = list_354.uncons();
assert_eq!(unconsed, Ok((3.into(), Term::from(vec![5.into(), 4.into()]))));
}
#[test]
fn list_pop() {
let mut list_poppable = Term::from(vec![1.into(), 0.into(), 0.into()]);
assert_eq!(list_poppable.pop(), Ok(1.into()));
assert_eq!(list_poppable.pop(), Ok(0.into()));
assert_eq!(list_poppable.pop(), Ok(0.into()));
assert_eq!(list_poppable.pop(), Err(NotAList));
}
#[test]
fn iterating_list() {
let list010 = Term::from(vec![0.into(), 1.into(), 0.into()]);
let mut iter = list010.into_iter();
assert_eq!(iter.next(), Some(0.into()));
assert_eq!(iter.next(), Some(1.into()));
assert_eq!(iter.next(), Some(0.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());
}
}