use term::*;
use term::Error::*;
use booleans::*;
use pair::*;
use arithmetic::{zero, succ};
use combinators::{i, z};
use std::ops::Index;
pub fn nil() -> Term { fls() }
pub fn null() -> Term {
abs(app!(Var(1), abs(abs(abs(fls()))), tru()))
}
pub fn cons() -> Term { pair() }
pub fn head() -> Term { fst() }
pub fn tail() -> Term { snd() }
pub fn length() -> Term {
app!(
z(),
abs(abs(abs(
app!(
null(),
Var(1),
abs(Var(3)),
abs(app!(
Var(4),
app(succ(), Var(3)),
app(snd(), Var(2))
)),
i()
)
))),
zero()
)
}
pub fn reverse() -> Term {
app!(
z(),
abs(abs(abs(
app!(
null(),
Var(1),
abs(Var(3)),
abs(app!(
Var(4),
app!(pair(), app(fst(), Var(2)), Var(3)),
app(snd(), Var(2))
)),
i()
)
))),
nil()
)
}
pub fn list() -> Term {
abs(
app!(
Var(1),
abs(abs(abs(
app(Var(3), app!(pair(), Var(1), Var(2)))
))),
reverse(),
nil()
)
)
}
pub fn append() -> Term {
z().app(
abs(abs(abs(
app!(
null(),
Var(2),
abs(Var(2)),
abs(app!(
pair(),
app(fst(), Var(3)),
app!(Var(4), app(snd(), Var(3)), Var(2))
)),
i()
)
)))
)
}
pub fn index() -> Term {
abs(abs(
app(fst(), app!(Var(2), snd(), Var(1)))
))
}
pub fn map() -> Term {
z().app(
abs(abs(abs(
app!(
null(),
Var(1),
abs(nil()),
abs(app!(
pair(),
app(Var(3), app(fst(), Var(2))),
app!(Var(4), Var(3), app(snd(), Var(2)))
)),
i()
)
)))
)
}
pub fn foldl() -> Term {
z().app(
abs(abs(abs(abs(
app!(
null(),
Var(1),
abs(Var(3)),
abs(app!(
Var(5),
Var(4),
app!(Var(4), Var(3), app(fst(), Var(2))),
app(snd(), Var(2))
)),
i()
)
))))
)
}
pub fn foldr() -> Term {
abs(abs(abs(
app!(
z(),
abs(abs(
app!(
null(),
Var(1),
abs(Var(5)),
abs(app!(
Var(6),
app(fst(), Var(2)),
app(Var(3), app(snd(), Var(2)))
)),
i()
)
)),
Var(1)
)
)))
}
pub fn filter() -> Term {
z().app(
abs(abs(abs(
app!(
null(),
Var(1),
abs(nil()),
abs(app!(
Var(3),
app(fst(), Var(2)),
app(pair(), app(fst(), Var(2))),
i(),
app!(Var(4), Var(3), app(snd(), Var(2)))
)),
i()
)
)))
)
}
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(app!(Var(1), term, 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 tests {
use super::*;
use reduction::beta;
use reduction::Order::*;
use arithmetic::{is_zero, plus};
#[test]
fn empty_list() {
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(
app!(
cons(),
1.into(),
app!(
cons(),
1.into(),
app!(
cons(),
0.into(),
nil()
)
)
), NOR, 0, false
);
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());
}
#[test]
fn cbv_list_functions() {
let l = Term::from(vec![1.into(), 2.into(), 3.into(), 4.into()]);
assert_eq!(beta(app!( length(), l.clone()), HAP, 0, false), 4.into());
assert_eq!(beta(app!(reverse(), l.clone()), HAP, 0, false),
Term::from(vec![4.into(), 3.into(), 2.into(), 1.into()]));
assert_eq!(beta(app!(list(), 4.into(), 1.into(), 2.into(), 3.into(), 4.into()), HAP, 0, false), l);
assert_eq!(beta(app!(append(), Term::from(vec![1.into(), 2.into()]),
Term::from(vec![3.into(), 4.into()])), HAP, 0, false), l);
assert_eq!(beta(app!(map(), succ(), l.clone()), HAP, 0, false),
Term::from(vec![2.into(), 3.into(), 4.into(), 5.into()]));
assert_eq!(beta(app!(foldl(), plus(), 0.into(), l.clone()), HAP, 0, false), 10.into());
assert_eq!(beta(app!(foldr(), plus(), 0.into(), l.clone()), HAP, 0, false), 10.into());
assert_eq!(beta(app!(filter(), is_zero(), l.clone()), HAP, 0, false), Term::from(vec![]));
}
}