diff --git a/core/src/command.rs b/core/src/command.rs index 0054e094c53cece6d8cb150876c6ffc7dfb9c068..e387449e0b417fd97f68d61cf83cb242baa6a6e5 100644 --- a/core/src/command.rs +++ b/core/src/command.rs @@ -1,7 +1,7 @@ use crate::Term; use std::fmt::{Display, Formatter}; -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq)] pub enum Command { Define(String, Term), CheckType(Term, Term), diff --git a/core/src/lib.rs b/core/src/lib.rs index 30e5e5c82bcf4b8428c3b7a0eebe83f7534556a6..b91b3b42d12a072dfc863de6890d4422b6146bdc 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 351d33b3857ef1b8b7fcc6bd7551a993f14215ef..016a918dd260cb93edab9222b3c7f20c4db11240 100644 --- a/core/src/term.rs +++ b/core/src/term.rs @@ -1,6 +1,10 @@ use std::fmt::{Display, Formatter}; -#[derive(Clone, Debug, PartialEq)] +// TODO: Use derive_more to constraint types (#11) +// struct Index(usize); +// struct Level(usize); + +#[derive(Clone, Debug, Eq, PartialEq)] pub enum Term { Prop, Var(usize), @@ -10,15 +14,231 @@ pub enum Term { Prod(Box<Term>, Box<Term>), } +use Term::*; + +impl Term { + /// Apply one step of β-reduction, using leftmost outermost evaluation strategy. + pub fn beta_reduction(self) -> Term { + match self { + App(box Abs(_, box t1), box t2) => t1.substitute(t2, 1), + App(box t1, box t2) => App(box t1.beta_reduction(), box t2), + Abs(x, box t) => Abs(x, box t.beta_reduction()), + _ => self, + } + } + + fn shift(self, offset: usize, depth: usize) -> Term { + match self { + Var(i) if i > depth => Var(i + offset), + App(box t1, box t2) => App(box t1.shift(offset, depth), box t2.shift(offset, depth)), + Abs(t1, box t2) => Abs(t1, box t2.shift(offset, depth + 1)), + Prod(t1, box t2) => Prod(t1, box t2.shift(offset, depth + 1)), + _ => self, + } + } + + fn substitute(self, rhs: Term, depth: usize) -> Term { + match self { + Var(i) if i == depth => rhs.shift(depth - 1, 0), + 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, + } + } +} + impl Display for Term { fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { match self { - Term::Prop => write!(f, "\u{02119}"), - Term::Var(i) => write!(f, "{}", i), - Term::Type(i) => write!(f, "\u{1D54B}({})", i), - Term::App(t1, t2) => write!(f, "({}) {}", t1, t2), - Term::Abs(t1, t2) => write!(f, "\u{003BB}{} \u{02192} {}", t1, t2), - Term::Prod(t1, t2) => write!(f, "\u{02200}{} \u{02192} {}", t1, t2), + Prop => write!(f, "\u{02119}"), + Var(i) => write!(f, "{}", i), + Type(i) => write!(f, "\u{1D54B}({})", i), + App(t1, t2) => write!(f, "({} {})", t1, t2), + Abs(t1, t2) => write!(f, "\u{003BB}{} \u{02192} {}", t1, t2), + Prod(t1, t2) => write!(f, "\u{02200}{} \u{02192} {}", t1, t2), } } } + +#[cfg(test)] +mod tests { + // TODO: Correctly types lambda terms (#9) + + 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 App( + box App( + box Var(3), + 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.c d) (λc.λd.d (c a))) (λ_.b)) (λc.c) + let term_step_1 = Abs( + box Prop, + box Abs( + box Prop, + box App( + box App( + box App( + box Abs(box Prop, box Abs(box Prop, box App(box Var(2), box Var(1)))), + 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)), + ), + ), + ); + + // λa.λb.((λc.(λd.λe.e (d a)) c) (λ_.b)) (λc.c) + let term_step_2 = 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.[(λc.λd.(d (c a))) (λ_.b)] (λc.c) + let term_step_3 = 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)), + ), + ), + ); + + // λa.λb.(λc.c ((λ_.b) a)) (λc.c) + let term_step_4 = 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.(λc.c) ((λ_.b) a) + let term_step_5 = 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.(λc.b) a + let term_step_6 = Abs( + box Prop, + box Abs(box Prop, box App(box Abs(box Prop, box Var(2)), box Var(2))), + ); + + // λa.λb.b + let term_step_7 = Abs(box Prop, box Abs(box Prop, box Var(1))); + + assert_eq!(term.beta_reduction(), term_step_1); + assert_eq!(term_step_1.beta_reduction(), term_step_2); + assert_eq!(term_step_2.beta_reduction(), term_step_3); + assert_eq!(term_step_3.beta_reduction(), term_step_4); + assert_eq!(term_step_4.beta_reduction(), term_step_5); + assert_eq!(term_step_5.beta_reduction(), term_step_6); + assert_eq!(term_step_6.beta_reduction(), term_step_7); + assert_eq!(term_step_7.clone().beta_reduction(), term_step_7); + } +}