diff --git a/Cargo.lock b/Cargo.lock index 5f68ce11..eefae263 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1116,6 +1116,10 @@ dependencies = [ [[package]] name = "llamada" version = "0.1.0" +dependencies = [ + "better-std", + "pretty_assertions", +] [[package]] name = "llvm-sys" diff --git a/llamada/Cargo.toml b/llamada/Cargo.toml index 91e5b277..2a3b4a5f 100644 --- a/llamada/Cargo.toml +++ b/llamada/Cargo.toml @@ -13,3 +13,5 @@ include = ["src/**/*", "LICENSE.md", "README.md"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +pretty_assertions = "1.0" +better-std = { path = "../better-std", features = [ ] } diff --git a/llamada/src/macros.rs b/llamada/src/macros.rs index 317da00a..7dc9721b 100644 --- a/llamada/src/macros.rs +++ b/llamada/src/macros.rs @@ -57,3 +57,35 @@ macro_rules! derive_expr_from( } } ); + +#[macro_export] +macro_rules! build_exprs( + { $ast: expr, $($name: ident = $inner: expr),+} => { + { + let expr = $ast; + $( + let $name = expr.add($inner); + )+ + let root = expr.get_last_id(); + *expr.root_mut() = (root.clone()); + root + } + } +); + +#[macro_export] +macro_rules! build_ast( + { $ty: ty, $name1: ident = $inner1: expr $(, $name: ident = $inner: expr)*} => { + { + let mut expr = <$ty>::new($inner1, Empty); + let $name1 = expr.get_last_id(); + let root = $crate::build_exprs!( + &mut expr, + $( + $name = $inner + ),* + ); + (expr, root) + } + } +); diff --git a/llamada/src/reprs/ref_counted.rs b/llamada/src/reprs/ref_counted.rs index 44a06794..bbb847cb 100644 --- a/llamada/src/reprs/ref_counted.rs +++ b/llamada/src/reprs/ref_counted.rs @@ -65,6 +65,7 @@ impl< } } fn get_last_id(&self) -> Self::Index { + // This clone is always cheap thanks to reference counting. self.terms.last().unwrap().clone() } fn get<'a>(&'a self, id: &'a Self::Index) -> &'a Term { @@ -93,7 +94,9 @@ impl< term: Term, meta: Self::Meta, ) -> Self::Index { - Rc::new(Ptr::new(term, meta)) + let node = Rc::new(Ptr::new(term, meta)); + self.terms.push(node.clone()); + node } fn print_meta(&self) -> bool { self.print_meta @@ -107,5 +110,6 @@ pub type LambdaCalc = RcRepr; #[cfg(test)] mod tests { use super::*; + use better_std::assert_eq; tests!(LambdaCalc); } diff --git a/llamada/src/reprs/sparse.rs b/llamada/src/reprs/sparse.rs index 180d69a1..9ed58c40 100644 --- a/llamada/src/reprs/sparse.rs +++ b/llamada/src/reprs/sparse.rs @@ -95,7 +95,10 @@ impl< term: Term, meta: Self::Meta, ) -> Self::Index { - Ptr::new(term, meta) + let node = Ptr::new(term, meta); + // TODO: Consider a way to avoid this copy. + self.terms.push(node.clone()); + node } fn print_meta(&self) -> bool { self.print_meta @@ -109,5 +112,6 @@ pub type LambdaCalc = SparseRepr; #[cfg(test)] mod tests { use super::*; + use better_std::assert_eq; tests!(LambdaCalc); } diff --git a/llamada/src/tests.rs b/llamada/src/tests.rs index e9d1f4ac..740ae8b8 100644 --- a/llamada/src/tests.rs +++ b/llamada/src/tests.rs @@ -171,6 +171,46 @@ macro_rules! tests { assert_eq!(expr.as_church(expr.root()), Some(2)); } + #[test] + fn plus_expr_using_macros() { + let (mut expr, plus) = $crate::build_ast!( + $ty, + x = Term::Var(1), + f = Term::Var(2), + m = Term::Var(3), + n = Term::Var(4), + nf = Term::App(n.clone(), f.clone()), + mf = Term::App(m.clone(), f.clone()), + mfx = Term::App(mf, x.clone()), + nfmfx = Term::App(nf.clone(), mfx.clone()), + abs1_nfmfx = Term::abs(nfmfx), + abs2_nfmfx = Term::abs(abs1_nfmfx), + abs3_nfmfx = Term::abs(abs2_nfmfx), + _plus = Term::abs(abs3_nfmfx) + ); + + assert_eq!( + format!("{}", &expr), + "(a => (b => (c => (d => ((a c) ((b c) d))))))" + ); + for n in 0..10 { + for m in 0..10 { + let church_n = expr.to_church(n); + let church_m = expr.to_church(m); + + $crate::build_exprs!( + &mut expr, + plus_m = Term::App(plus.clone(), church_m), + _plus_n_m = Term::App(plus_m, church_n) + ); + expr.reduce(); + let result = expr.as_church(expr.root()); + eprintln!("{n:?} + {m:?} = {result:?}"); + assert_eq!(result, Some(n + m)); + } + } + } + #[test] fn plus_expr() { let mut expr = <$ty>::new(Term::Var(1), Empty);