#![allow(missing_docs)]
use self::Encoding::*;
use crate::term::Term::*;
use crate::term::{abs, app, Term};
#[derive(Debug, Clone, Copy)]
pub enum Encoding {
Church,
Scott,
Parigot,
StumpFu,
Binary,
}
macro_rules! make_trait {
($trait_name:ident, $function_name:ident) => {
pub trait $trait_name {
#[doc = "Performs the conversion."]
fn $function_name(self) -> Term;
}
};
}
make_trait!(IntoChurchNum, into_church);
make_trait!(IntoScottNum, into_scott);
make_trait!(IntoParigotNum, into_parigot);
make_trait!(IntoStumpFuNum, into_stumpfu);
make_trait!(IntoBinaryNum, into_binary);
pub trait IntoSignedNum {
#[doc = "Performs the conversion. The supported `Encoding`s are `Church`, `Scott`, `Parigot` and
`StumpFu`."]
fn into_signed(self, encoding: Encoding) -> Term;
}
impl IntoChurchNum for usize {
fn into_church(self) -> Term {
let mut ret = Var(1);
for _ in 0..self {
ret = app(Var(2), ret);
}
abs!(2, ret)
}
}
impl IntoScottNum for usize {
fn into_scott(self) -> Term {
let mut ret = abs!(2, Var(2));
for _ in 0..self {
ret = abs!(2, app(Var(1), ret));
}
ret
}
}
impl IntoParigotNum for usize {
fn into_parigot(self) -> Term {
let mut ret = abs!(2, Var(1));
for _ in 0..self {
ret = abs!(
2,
app!(
Var(2),
ret.clone(),
ret.unabs().and_then(|r| r.unabs()).unwrap()
)
);
}
ret
}
}
impl IntoStumpFuNum for usize {
fn into_stumpfu(self) -> Term {
let mut ret = abs!(2, Var(1));
for n in 1..self + 1 {
ret = abs!(2, app!(Var(2), n.into_church(), ret));
}
ret
}
}
impl IntoBinaryNum for usize {
fn into_binary(self) -> Term {
let mut ret = Var(3);
if self != 0 {
let binstr = format!("{:b}", self).into_bytes();
for bit in binstr {
if bit == b'0' {
ret = app(Var(2), ret);
} else {
ret = app(Var(1), ret);
}
}
}
abs!(3, ret)
}
}
impl IntoSignedNum for i32 {
fn into_signed(self, encoding: Encoding) -> Term {
let modulus = self.unsigned_abs() as usize;
let numeral = match encoding {
Church => modulus.into_church(),
Scott => modulus.into_scott(),
Parigot => modulus.into_parigot(),
StumpFu => modulus.into_stumpfu(),
Binary => panic!("signed binary numbers are not supported"),
};
if self > 0 {
tuple!(numeral, abs!(2, Var(1)))
} else {
tuple!(abs!(2, Var(1)), numeral)
}
}
}
macro_rules! impl_pair {
($trait_name:ident, $function_name:ident) => {
impl<T, U> $trait_name for (T, U)
where
T: $trait_name,
U: $trait_name,
{
fn $function_name(self) -> Term {
abs(app!(
Var(1),
(self.0).$function_name(),
(self.1).$function_name()
))
}
}
};
}
impl_pair!(IntoChurchNum, into_church);
impl_pair!(IntoScottNum, into_scott);
impl_pair!(IntoParigotNum, into_parigot);
impl_pair!(IntoStumpFuNum, into_stumpfu);
impl_pair!(IntoBinaryNum, into_binary);
macro_rules! impl_option {
($trait_name:ident, $function_name:ident) => {
impl<T> $trait_name for Option<T>
where
T: $trait_name,
{
fn $function_name(self) -> Term {
match self {
None => abs!(2, Var(2)),
Some(value) => abs!(2, app(Var(1), value.$function_name())),
}
}
}
};
}
impl_option!(IntoChurchNum, into_church);
impl_option!(IntoScottNum, into_scott);
impl_option!(IntoParigotNum, into_parigot);
impl_option!(IntoStumpFuNum, into_stumpfu);
impl_option!(IntoBinaryNum, into_binary);
macro_rules! impl_result {
($trait_name:ident, $function_name:ident) => {
impl<T, U> $trait_name for Result<T, U>
where
T: $trait_name,
U: $trait_name,
{
fn $function_name(self) -> Term {
match self {
Ok(ok) => abs!(2, app(Var(2), ok.$function_name())),
Err(err) => abs!(2, app(Var(1), err.$function_name())),
}
}
}
};
}
impl_result!(IntoChurchNum, into_church);
impl_result!(IntoScottNum, into_scott);
impl_result!(IntoParigotNum, into_parigot);
impl_result!(IntoStumpFuNum, into_stumpfu);
impl_result!(IntoBinaryNum, into_binary);