From 2448ee4ea0cfbd585a214ff90cf9a2da10113bb0 Mon Sep 17 00:00:00 2001
From: Vincent Lafeychine <vincent.lafeychine@proton.me>
Date: Sun, 2 Oct 2022 18:59:29 +0200
Subject: [PATCH] [WIP] feat(term): Improve beta-reduction + Add tests

---
 core/src/lib.rs  |   1 +
 core/src/term.rs | 252 +++++++++++++++++++++++++++++++++++++++++------
 2 files changed, 225 insertions(+), 28 deletions(-)

diff --git a/core/src/lib.rs b/core/src/lib.rs
index 30e5e5c8..b91b3b42 100644
--- a/core/src/lib.rs
+++ b/core/src/lib.rs
@@ -1,3 +1,4 @@
+#![feature(box_patterns)]
 #![feature(box_syntax)]
 
 mod command;
diff --git a/core/src/term.rs b/core/src/term.rs
index 6277231a..acda4ef1 100644
--- a/core/src/term.rs
+++ b/core/src/term.rs
@@ -13,20 +13,25 @@ pub enum Term {
 use Term::*;
 
 impl Term {
+    /// Returns the normal form of the term
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// // TODO: Use parser
+    /// ```
     pub fn beta_reduction(self) -> Term {
         match self {
-            App(box Abs(_, box mut t1), box t2) => {
-                t1.substitute(t2, 1);
-                t1
-            }
+            App(box Abs(_, box t1), box t2) => t1.substitute(t2, 1),
+            App(box t1, box t2) => App(box t1.beta_reduction(), box t2.beta_reduction()),
             Abs(x, box t) => Abs(x, box t.beta_reduction()),
             _ => self,
         }
     }
 
-    fn shift(&mut self, offset: usize) -> Term {
+    fn shift(self, offset: usize) -> Term {
         match self {
-            Var(x) => Var(*x + offset),
+            Var(x) => Var(x + offset),
             App(box t1, box t2) => App(box t1.shift(offset), box t2.shift(offset)),
             Abs(x, box t) => Abs(x.clone(), box t.shift(offset)),
             Prod(x, box t) => Prod(x.clone(), box t.shift(offset)),
@@ -34,18 +39,18 @@ impl Term {
         }
     }
 
-    fn substitute(&mut self, rhs: Term, depth: usize) {
+    fn substitute(self, rhs: Term, depth: usize) -> Term {
         match self {
-            Var(i) if *i == depth => *self = rhs.clone().shift(depth - 1),
-            Var(i) if *i != depth => *self = Var(*i - 1),
-            App(l, r) => {
-                l.substitute(rhs.clone(), depth);
-                r.substitute(rhs, depth);
-            }
-            Abs(_, t) | Prod(_, t) => {
-                t.substitute(rhs, depth + 1);
-            }
-            _ => {}
+            Var(i) if i == depth => rhs.shift(depth - 1),
+            Var(i) if i > depth => Var(i - 1),
+
+            App(l, r) => App(
+                box l.substitute(rhs.clone(), depth),
+                box r.substitute(rhs, depth),
+            ),
+            Abs(t, term) => Abs(t, box term.substitute(rhs, depth + 1)),
+            Prod(t, term) => Prod(t, box term.substitute(rhs, depth + 1)),
+            _ => self,
         }
     }
 }
@@ -63,17 +68,208 @@ impl Display for Term {
     }
 }
 
-#[test]
-fn subst() {
-    let term = Abs(
-        box Prop,
-        box App(
-            box Abs(box Prop, box App(box Var(2), box Var(1))),
-            box Var(1),
-        ),
-    );
+#[cfg(test)]
+mod tests {
+    // TODO: Correctly types lambda terms.
+
+    use super::Term::*;
+
+    #[test]
+    fn simple_subst() {
+        // λx.(λy.x y) x
+        let term = Abs(
+            box Prop,
+            box App(
+                box Abs(box Prop, box App(box Var(2), box Var(1))),
+                box Var(1),
+            ),
+        );
+
+        // λx.x x
+        let reduced = Abs(box Prop, box App(box Var(1), box Var(1)));
+
+        assert_eq!(term.beta_reduction(), reduced);
+    }
+
+    #[test]
+    fn complex_subst() {
+        // [λa.λb.λc.a (λd.λe.e (d b)) (λ_.c) (λd.d)] (λa.λb.a b)
+        let term = App(
+            box Abs(
+                box Prop,
+                box Abs(
+                    box Prop,
+                    box Abs(
+                        box Prop,
+                        box App(
+                            box Var(3),
+                            box App(
+                                box App(
+                                    box Abs(
+                                        box Prop,
+                                        box Abs(
+                                            box Prop,
+                                            box App(box Var(1), box App(box Var(2), box Var(4))),
+                                        ),
+                                    ),
+                                    box Abs(box Prop, box Var(2)),
+                                ),
+                                box Abs(box Prop, box Var(1)),
+                            ),
+                        ),
+                    ),
+                ),
+            ),
+            box Abs(box Prop, box Abs(box Prop, box App(box Var(2), box Var(1)))),
+        );
+
+        // λa.λb.((λc.(λd.λe.e (d a)) c) (λ_.b)) (λc.c)
+        // λa.λb.((λc.λd.(d (c a))) (λ_.b)) (λc.c)
+        // λa.λb.(λc.c ((λ_.b) a)) (λc.c)
+        // λa.λb.(λc.c) ((λ_.b) a)
+        // λa.λb.b
+        let reduced = Abs(box Prop, box Abs(box Prop, box Var(1)));
+
+        assert_eq!(term.beta_reduction(), reduced);
+    }
+
+    #[test]
+    fn complex_subst_step_5() {
+        // λa.λb.((λc.(λd.λe.e (d a)) c) (λ_.b)) (λc.c)
+        let term = Abs(
+            box Prop,
+            box Abs(
+                box Prop,
+                box App(
+                    box App(
+                        box Abs(
+                            box Prop,
+                            box App(
+                                box Abs(
+                                    box Prop,
+                                    box Abs(
+                                        box Prop,
+                                        box App(box Var(1), box App(box Var(2), box Var(5))),
+                                    ),
+                                ),
+                                box Var(1),
+                            ),
+                        ),
+                        box Abs(box Prop, box Var(2)),
+                    ),
+                    box Abs(box Prop, box Var(1)),
+                ),
+            ),
+        );
+
+        // λa.λb.b
+        let reduced = Abs(box Prop, box Abs(box Prop, box Var(1)));
+
+        assert_eq!(
+            term.beta_reduction()
+                .beta_reduction()
+                .beta_reduction()
+                .beta_reduction()
+                .beta_reduction(),
+            reduced
+        );
+    }
 
-    let reduced = Abs(box Prop, box App(box Var(1), box Var(1)));
+    #[test]
+    fn complex_subst_step_4() {
+        // λa.λb.((λc.λd.(d (c a))) (λ_.b)) (λc.c)
+        let term = Abs(
+            box Prop,
+            box Abs(
+                box Prop,
+                box App(
+                    box App(
+                        box Abs(
+                            box Prop,
+                            box Abs(
+                                box Prop,
+                                box App(box Var(1), box App(box Var(2), box Var(4))),
+                            ),
+                        ),
+                        box Abs(box Prop, box Var(2)),
+                    ),
+                    box Abs(box Prop, box Var(1)),
+                ),
+            ),
+        );
 
-    assert_eq!(term.beta_reduction(), reduced);
+        // λa.λb.b
+        let reduced = Abs(box Prop, box Abs(box Prop, box Var(1)));
+
+        assert_eq!(
+            term.beta_reduction()
+                .beta_reduction()
+                .beta_reduction()
+                .beta_reduction(),
+            reduced
+        );
+    }
+
+    #[test]
+    fn complex_subst_step_3() {
+        // λa.λb.(λc.c ((λ_.b) a)) (λc.c)
+        let term = Abs(
+            box Prop,
+            box Abs(
+                box Prop,
+                box App(
+                    box Abs(
+                        box Prop,
+                        box App(
+                            box Var(1),
+                            box App(box Abs(box Prop, box Var(3)), box Var(3)),
+                        ),
+                    ),
+                    box Abs(box Prop, box Var(1)),
+                ),
+            ),
+        );
+
+        // λa.λb.b
+        let reduced = Abs(box Prop, box Abs(box Prop, box Var(1)));
+
+        assert_eq!(
+            term.beta_reduction().beta_reduction().beta_reduction(),
+            reduced
+        );
+    }
+
+    #[test]
+    fn complex_subst_step_2() {
+        // λa.λb.(λc.c) ((λ_.b) a)
+        let term = Abs(
+            box Prop,
+            box Abs(
+                box Prop,
+                box App(
+                    box Abs(box Prop, box Var(1)),
+                    box App(box Abs(box Prop, box Var(2)), box Var(2)),
+                ),
+            ),
+        );
+
+        // λa.λb.b
+        let reduced = Abs(box Prop, box Abs(box Prop, box Var(1)));
+
+        assert_eq!(term.beta_reduction().beta_reduction(), reduced);
+    }
+
+    #[test]
+    fn complex_subst_step_1() {
+        // λa.λb.(λc.b) a
+        let term = Abs(
+            box Prop,
+            box Abs(box Prop, box App(box Abs(box Prop, box Var(2)), box Var(2))),
+        );
+
+        // λa.λb.b
+        let reduced = Abs(box Prop, box Abs(box Prop, box Var(1)));
+
+        assert_eq!(term.beta_reduction(), reduced);
+    }
 }
-- 
GitLab