extern crate lambda_calculus as lambda;
use lambda::*;
fn assert_roundtrips(term: &Term) {
let rendered = format!("{term:?}");
match parse(&rendered, DeBruijn) {
Ok(back) => assert_eq!(
back, *term,
"{rendered} reparsed as {back:?} instead of {term:?}"
),
Err(e) => panic!("{rendered} failed to reparse: {e:?}"),
}
}
#[test]
fn every_index_roundtrips() {
for i in 0..5_000 {
assert_roundtrips(&Var(i));
assert_roundtrips(&abs(Var(i)));
assert_roundtrips(&app(Var(i), Var(1)));
assert_roundtrips(&app(Var(1), Var(i)));
}
for i in [usize::MAX, usize::MAX - 1, 1 << 32, 1 << 63, 1_000_000] {
assert_roundtrips(&Var(i));
assert_roundtrips(&abs(app(Var(i), Var(i))));
}
}
#[test]
fn the_historical_collisions_are_gone() {
assert_ne!(
format!("{:?}", Var(16)),
format!("{:?}", app(Var(1), Var(0)))
);
assert_ne!(
format!("{:?}", Var(17)),
format!("{:?}", app(Var(1), Var(1)))
);
assert_ne!(
format!("{:?}", Var(32)),
format!("{:?}", app(Var(2), Var(0)))
);
assert_ne!(
format!("{:?}", Var(31)),
format!("{:?}", app(Var(1), Var(15)))
);
assert_eq!(format!("{:?}", Var(15)), "F");
assert_eq!(format!("{:?}", Var(16)), "[10]");
assert_eq!(format!("{:?}", Var(300)), "[12C]");
assert_eq!(format!("{:?}", UD), "[0]");
assert_eq!(UD.to_string(), "undefined");
}
#[test]
fn existing_notation_is_unchanged() {
for (rendered, expected) in [
("λλ1", abs(abs(Var(1)))),
(
"λλλ2(321)",
abs(abs(abs(app(Var(2), app!(Var(3), Var(2), Var(1)))))),
),
(
"λλλ31(21)",
abs(abs(abs(app!(Var(3), Var(1), app(Var(2), Var(1)))))),
),
] {
assert_eq!(parse(rendered, DeBruijn).unwrap(), expected);
assert_eq!(
format!("{expected:?}").replace(lambda::term::LAMBDA, "λ"),
rendered
);
}
assert_eq!(
parse("λλ2a1", DeBruijn).unwrap(),
parse("λλ2A1", DeBruijn).unwrap()
);
assert_eq!(parse("A", DeBruijn).unwrap(), Var(10));
assert_eq!(parse("F", DeBruijn).unwrap(), Var(15));
}
#[test]
fn brackets_delimit_without_switching_base() {
assert_eq!(parse("[A]", DeBruijn).unwrap(), Var(10));
assert_eq!(
parse("[A]", DeBruijn).unwrap(),
parse("A", DeBruijn).unwrap()
);
assert_eq!(parse("[a]", DeBruijn).unwrap(), Var(10));
assert_eq!(parse("[F]", DeBruijn).unwrap(), Var(15));
assert_eq!(parse("[10]", DeBruijn).unwrap(), Var(16));
assert_eq!(parse("[12C]", DeBruijn).unwrap(), Var(300));
assert_eq!(parse("[1]", DeBruijn).unwrap(), Var(1));
assert_eq!(parse("[0]", DeBruijn).unwrap(), UD);
assert_eq!(
parse("λλ[2][1]", DeBruijn).unwrap(),
abs(abs(app(Var(2), Var(1))))
);
assert_eq!(
parse("λλ21", DeBruijn).unwrap(),
parse("λλ[2][1]", DeBruijn).unwrap()
);
assert_ne!(
parse("[10]", DeBruijn).unwrap(),
parse("10", DeBruijn).unwrap()
);
assert_eq!(parse("10", DeBruijn).unwrap(), app(Var(1), UD));
assert_eq!(parse("16", DeBruijn).unwrap(), app(Var(1), Var(6)));
}
#[test]
fn malformed_brackets_are_rejected_not_panics() {
for bad in [
"[",
"]",
"[]",
"[G]",
"[1G]",
"[1",
"λ[",
"[[1]]",
"[1]]",
"[-1]",
"[ ]",
"[1 ]",
"[FFFFFFFFFFFFFFFFF]",
] {
assert!(
parse(bad, DeBruijn).is_err(),
"{bad:?} should not have parsed"
);
}
}
#[test]
fn random_terms_roundtrip() {
struct Rng(u64);
impl Rng {
fn next(&mut self) -> u64 {
self.0 = self
.0
.wrapping_mul(6364136223846793005)
.wrapping_add(1442695040888963407);
self.0 >> 33
}
}
fn build(rng: &mut Rng, budget: u32) -> Term {
if budget == 0 {
return Var((rng.next() % 64) as usize);
}
match rng.next() % 3 {
0 => Var((rng.next() % 64) as usize),
1 => abs(build(rng, budget - 1)),
_ => app(build(rng, budget - 1), build(rng, budget - 1)),
}
}
let mut rng = Rng(0x5eed);
for i in 0..20_000 {
assert_roundtrips(&build(&mut rng, 1 + (i % 8) as u32));
}
}