From 30144fc85f73a7df205e5d674d307332b247d370 Mon Sep 17 00:00:00 2001
From: belazy <aarthuur01@gmail.com>
Date: Sat, 24 Dec 2022 15:24:21 +0100
Subject: [PATCH] =?UTF-8?q?Resolve=20"Add=20Universe=20Polymorphism"=20?=
 =?UTF-8?q?=E2=9C=A8=EF=B8=8F=20Closes=20#28=20=F0=9F=90=94=EF=B8=8F?=
 =?UTF-8?q?=F0=9F=91=8D=EF=B8=8F=20Approved-by:=20belazy=20<aarthuur01@gma?=
 =?UTF-8?q?il.com>=20Approved-by:=20aalbert=20<augustin.albert@bleu-azure.?=
 =?UTF-8?q?fr>=20Approved-by:=20v-lafeychine=20<vincent.lafeychine@proton.?=
 =?UTF-8?q?me>=20Approved-by:=20loutr=20<loutr@crans.org>?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

🦀️🍰🦀️🍰🦀️🍰

* fix(proost): added Define command back (need to distinguish between the two in order to bind and reuse correctly later

* chore(kernel, calculus/level): adapt documentation to new type

* chore(level): Add specific enum to handle comparison state

* chore(kernel): c-o-v-e-r-a-g-e

* chore(parser): remove TODO

* chore(parser): more coverage

* fix(kernel): trailing comma when printing `InstantiatedDeclaration`s

* fix(kernel): chiale + aboie, coverage

* chore(kernel): mention #14

* chore(kernel): yet another test

* chore(kernel): always more coverage

* chore(parser): add issue nb to todo

* fix(kernel): avoid reentrant entry bug when infering type of decls with 0 universe arguments

* fix(parser): revert comit, wtf clippy ?

* chore(parser): remove unused branch

* chore(kernel): another test

* Apply 1 suggestion(s) to 1 file(s)

* fix(kernel): remove dangerous function

* chore(kernel): always more coverage

* feat(parser): remove `Define` command

* fix(kernel): interface checking for decls

* Apply 1 suggestion(s) to 1 file(s)

* chore(kernel): add coverage

* Apply 1 suggestion(s) to 1 file(s)

* Apply 1 suggestion(s) to 1 file(s)

* Apply 1 suggestion(s) to 1 file(s)

* Apply 1 suggestion(s) to 1 file(s)

* Apply 1 suggestion(s) to 1 file(s)

* chore(kernel): coverage

* Apply 1 suggestion(s) to 1 file(s)

* fix(kernel): over-agressive normalization

* Apply 1 suggestion(s) to 1 file(s)

* Apply 1 suggestion(s) to 1 file(s)

* Apply 1 suggestion(s) to 1 file(s)

* Apply 1 suggestion(s) to 1 file(s)

* Apply 1 suggestion(s) to 1 file(s)

* Apply 1 suggestion(s) to 1 file(s)

* Apply 1 suggestion(s) to 1 file(s)

* Apply 1 suggestion(s) to 1 file(s)

* fix(parser): avoid spacing befor univ decls/vars

* Apply 1 suggestion(s) to 1 file(s)

* Apply 1 suggestion(s) to 1 file(s)

* Apply 1 suggestion(s) to 1 file(s)

* fix(proost): check existing identifiers for al definitions

* Apply 1 suggestion(s) to 1 file(s)

* feat(parser): add `DeclarationCheckType`

* chore(kernel): remove unused test

* fix(proost): type-check terms before `Eval`uating

* docs: update

* fix: further adjustments to parser

* chore: formatting + some adjustments for kernel tests

* fix(parser, proost): modify command generation for declarations

* chore(parser): add tests todo

* chore(parser): tests on sort parsing

* fix(parser): tests

* feat(parser): parse universe bindings

* feat(parser): parse universe declarations in define_type

* fix(level): comments

* chore: remove biging as a dependency

* chore(kernel): remove todo

* chore(kernel): remove unused file

* chore(kernel): more tests

* chore(kernel): documentation of `level_builder.rs`

* feat(kernel): tests coverage

* fix(kernel): tests

* feat(kernel/term/builder): add binding to (instantiated) declaration builders

* feat(kernel): add builders for declaration

* chore(kernel): refactor builder file hierarchy and simplify these files

* fix(proost): some import errors

* feat(parser): add parsing for univ parameters of definitions

* chore(kernel): add todo

* fix: remove lifetime dependency on LevelEnvironment

* feat : add inference for declarations.

* feat(parser): add new parsing rules for levels

* feat(parser): new syntax for universe polymorphism

* feat(kernel): add type-checking for Decls

* feat: incorporate LevelBuilder in TermBuilder

* fix(kernel): refactor impl organisation: unload most of arena's

* WOOOHOOOO

* fix(kernel, level): fix incorrect assignment in hashcons function

* fix(kernel): more errors

* feat(kernel): fixed some errors

* feat(kernel): finish rudimentary bases for modifying parser and type checker

* feat(kernel): further work on Levels

* WIP feat(kernel): modify declaration.rs structure

* fix(kernel): removing Dweller struct, using a macro instead

* fix(kernel): refactor file structure to add new core types. first attempt for levels.

* feat(kernel): add generic Dweller type

* WIP fix(kernel): refactor file structure to add new core types

* um

* feat:something

* AH

* first commit
---
 kernel/src/calculus/level.rs         | 68 ++++++++++++++++++++--------
 kernel/src/calculus/term.rs          |  1 -
 kernel/src/memory/declaration/mod.rs |  7 ++-
 parser/src/parser.rs                 | 35 +++++++++++++-
 parser/src/term.pest                 |  4 +-
 proost/src/evaluator.rs              |  4 +-
 6 files changed, 92 insertions(+), 27 deletions(-)

diff --git a/kernel/src/calculus/level.rs b/kernel/src/calculus/level.rs
index 47e7acce..561664d8 100644
--- a/kernel/src/calculus/level.rs
+++ b/kernel/src/calculus/level.rs
@@ -5,6 +5,25 @@ use Payload::*;
 use crate::memory::arena::Arena;
 use crate::memory::level::{Level, Payload};
 
+/// State during computation for level comparison.
+enum State {
+    /// Comparison failed.
+    False,
+
+    /// Comparison succeed.
+    True,
+
+    /// Induction is needed to complete the comparaison.
+    Stuck(usize),
+}
+
+impl State {
+    /// Checks if the comparison succeeded.
+    fn is_true(&self) -> bool {
+        matches!(self, State::True)
+    }
+}
+
 impl<'arena> Level<'arena> {
     /// Helper function for equality checking, used to substitute `Var(i)` with `Z` and `S(Var(i))`.
     fn substitute_single(self, var: usize, u: Self, arena: &mut Arena<'arena>) -> Self {
@@ -34,25 +53,30 @@ impl<'arena> Level<'arena> {
     /// Checks whether `self <= rhs + n`, without applying substitution.
     ///
     /// Precisely, it returns:
-    /// - `(false, i + 1)` if `Var(i)` needs to be substituted to unstuck the comparison.
-    /// - `(true, 0)` if `self <= rhs + n`,
-    /// - `(false, 0)` else.
-    fn geq_no_subst(self, rhs: Self, n: i64) -> (bool, usize) {
+    /// - `Stuck(i + 1)` if `Var(i)` needs to be substituted to unstuck the comparison.
+    /// - `True` if `self <= rhs + n`,
+    /// - `False` else.
+    fn geq_no_subst(self, rhs: Self, n: i64) -> State {
         match (&*self, &*rhs) {
-            (Zero, _) if n >= 0 => (true, 0),
-            (_, _) if self == rhs && n >= 0 => (true, 0),
-            (Succ(l), _) if l.geq_no_subst(rhs, n - 1).0 => (true, 0),
-            (_, Succ(l)) if self.geq_no_subst(*l, n + 1).0 => (true, 0),
+            (Zero, _) if n >= 0 => State::True,
+
+            (_, _) if self == rhs && n >= 0 => State::True,
+
+            (Succ(l), _) if l.geq_no_subst(rhs, n - 1).is_true() => State::True,
+            (_, Succ(l)) if self.geq_no_subst(*l, n + 1).is_true() => State::True,
+
             (_, Max(l1, l2))
-                if self.geq_no_subst(*l1, n).0 || self.geq_no_subst(*l2, n).0 =>
+                if self.geq_no_subst(*l1, n).is_true() || self.geq_no_subst(*l2, n).is_true() =>
             {
-                (true, 0)
+                State::True
             }
-            (Max(l1, l2), _) if l1.geq_no_subst(rhs, n).0 && l2.geq_no_subst(rhs, n).0 => {
-                (true, 0)
+            (Max(l1, l2), _) if l1.geq_no_subst(rhs, n).is_true() && l2.geq_no_subst(rhs, n).is_true() => {
+                State::True
             }
-            (_, IMax(_, v)) | (IMax(_, v), _) if let Var(i) = **v => (false, i + 1),
-            _ => (false, 0),
+
+            (_, IMax(_, v)) | (IMax(_, v), _) if let Var(i) = **v => State::Stuck(i),
+
+            _ => State::False,
         }
     }
 
@@ -62,13 +86,13 @@ impl<'arena> Level<'arena> {
     /// is substituted for `0` and `S(Var(i))`.
     pub fn geq(self, rhs: Self, n: i64, arena: &mut Arena<'arena>) -> bool {
         match self.geq_no_subst(rhs, n) {
-            (true, _) => true,
-            (false, 0) => false,
-            (false, i) => {
+            State::True => true,
+            State::False => false,
+            State::Stuck(i) => {
                 let zero = Level::zero(arena);
-                let vv = Level::var(i - 1, arena).succ(arena);
-                self.substitute_single(i - 1, zero, arena).geq(rhs.substitute_single(i - 1, zero, arena), n, arena)
-                    && self.substitute_single(i - 1, vv, arena).geq(rhs.substitute_single(i - 1, vv, arena), n, arena)
+                let vv = Level::var(i, arena).succ(arena);
+                self.substitute_single(i, zero, arena).geq(rhs.substitute_single(i, zero, arena), n, arena)
+                    && self.substitute_single(i, vv, arena).geq(rhs.substitute_single(i, vv, arena), n, arena)
             },
         }
     }
@@ -90,6 +114,7 @@ mod tests {
     #[test]
     fn univ_eq() {
         use_arena(|arena| {
+            let two = arena.build_level_raw(succ(succ(zero())));
             let one = arena.build_level_raw(succ(zero()));
             let zero_ = arena.build_level_raw(zero());
             let var0 = Level::var(0, arena);
@@ -97,12 +122,15 @@ mod tests {
             let succ_var0 = Level::succ(var0, arena);
             let max1_var0 = Level::max(one, var0, arena);
             let max0_var0 = Level::max(zero_, var0, arena);
+            let imax_0_var0 = Level::imax(zero_, var0, arena);
             let max_var0_var1 = Level::max(var0, var1, arena);
             let max_var1_var0 = Level::max(var1, var0, arena);
             let max_var1_var1 = Level::max(var1, var1, arena);
             let succ_max_var0_var1 = Level::succ(max_var0_var1, arena);
             let max_succ_var0_succ_var1 = arena.build_level_raw(max(succ(var(0)), succ(var(1))));
 
+            assert!(!two.geq_no_subst(succ_var0, 0).is_true());
+            assert!(imax_0_var0.is_eq(var0, arena));
             assert!(max1_var0.geq(succ_var0, 0, arena));
             assert!(!zero_.is_eq(one, arena));
             assert!(!one.is_eq(zero_, arena));
diff --git a/kernel/src/calculus/term.rs b/kernel/src/calculus/term.rs
index d332dc99..f2148b83 100644
--- a/kernel/src/calculus/term.rs
+++ b/kernel/src/calculus/term.rs
@@ -16,7 +16,6 @@ impl<'arena> Term<'arena> {
         if let Some(red) = self.reduce_recursor(arena) {
             return red;
         };
-        // TODO beta-reduce recursors
         match *self {
             App(t1, t2) => match *t1 {
                 Abs(_, t1) => t1.substitute(t2, 1, arena),
diff --git a/kernel/src/memory/declaration/mod.rs b/kernel/src/memory/declaration/mod.rs
index c54d763e..a2ef8888 100644
--- a/kernel/src/memory/declaration/mod.rs
+++ b/kernel/src/memory/declaration/mod.rs
@@ -46,7 +46,12 @@ impl<'arena> fmt::Display for InstantiatedDeclaration<'arena> {
             Some(term) => write!(f, "{term}"),
             None => {
                 write!(f, "({}).{{", self.0.payload.decl)?;
-                self.0.payload.params.iter().try_for_each(|level| write!(f, "{level}, "))?;
+
+                let mut iter = self.0.payload.params.iter();
+
+                iter.next().map(|level| write!(f, "{level}")).unwrap_or(Ok(()))?;
+                iter.try_for_each(|level| write!(f, ", {level}"))?;
+
                 write!(f, "}}")
             },
         }
diff --git a/parser/src/parser.rs b/parser/src/parser.rs
index 18b63945..f303feb4 100644
--- a/parser/src/parser.rs
+++ b/parser/src/parser.rs
@@ -242,6 +242,7 @@ fn convert_error(err: pest::error::Error<Rule>) -> error::Error {
         Rule::ImportFile => "import path_to_file".to_owned(),
         Rule::Search => "search var".to_owned(),
         Rule::Max => "max".to_owned(),
+        Rule::IMax => "imax".to_owned(),
         Rule::Plus => "plus".to_owned(),
         Rule::arg_univ => "universe argument".to_owned(),
         Rule::univ_decl => "universe declaration".to_owned(),
@@ -319,7 +320,7 @@ mod tests {
     const COMMAND_ERR: &str = "expected def var := term, def var : term := term, def decl.{ vars, ... } := term, def decl.{ vars, ... } : term := term, check term : term, check term, eval term, import path_to_file, or search var";
     const TERM_ERR: &str = "expected variable, abstraction, dependent product, application, product, Prop, Type, or Sort";
     const SIMPLE_TERM_ERR: &str = "expected variable, abstraction, Prop, Type, Sort, or universe argument";
-    const UNIVERSE_ERR: &str = "expected number, variable, abstraction, Prop, Type, Sort, plus, or max";
+    const UNIVERSE_ERR: &str = "expected number, variable, abstraction, Prop, Type, Sort, plus, max, or imax";
 
     #[test]
     fn failure_universe_level() {
@@ -341,6 +342,24 @@ mod tests {
                 Some(declaration::Builder::Decl(box Type(box level::Builder::Const(0)), Vec::new())),
                 declaration::Builder::Decl(box Prop, Vec::new())
             ))
+        )
+    }
+
+    #[test]
+    fn successful_declare_with_type_annotation() {
+        assert_eq!(
+            parse_line("def x.{u} : Type u := foo.{u}"),
+            Ok(Declaration(
+                "x",
+                Some(declaration::Builder::Decl(box Type(box level::Builder::Var("u")), ["u"].to_vec())),
+                declaration::Builder::Decl(
+                    box kernel::memory::term::builder::Builder::Decl(box declaration::InstantiatedBuilder::Var(
+                        "foo",
+                        [level::Builder::Var("u")].to_vec()
+                    )),
+                    ["u"].to_vec()
+                )
+            ))
         );
     }
 
@@ -365,6 +384,11 @@ mod tests {
         assert_eq!(parse_line("def x := Prop"), Ok(Declaration("x", None, declaration::Builder::Decl(box Prop, Vec::new()))));
     }
 
+    #[test]
+    fn successful_declare() {
+        assert_eq!(parse_line("def x.{} := Prop"), Ok(Declaration("x", None, declaration::Builder::Decl(box Prop, Vec::new()))));
+    }
+
     #[test]
     fn successful_checktype() {
         assert_eq!(parse_line("check Prop : Type"), Ok(CheckType(Prop, Type(box level::Builder::Const(0)))));
@@ -400,6 +424,15 @@ mod tests {
         assert_eq!(parse_line("check Sort"), Ok(GetType(Sort(box level::Builder::Const(0)))));
         assert_eq!(parse_line("check Sort 0"), Ok(GetType(Sort(box level::Builder::Const(0)))));
         assert_eq!(parse_line("check Sort 1"), Ok(GetType(Sort(box level::Builder::Const(1)))));
+        assert_eq!(parse_line("check Sort (0 + 1)"), Ok(GetType(Sort(box level::Builder::Plus(box level::Builder::Const(0), 1)))));
+        assert_eq!(
+            parse_line("check Sort max 0 0"),
+            Ok(GetType(Sort(box level::Builder::Max(box level::Builder::Const(0), box level::Builder::Const(0)))))
+        );
+        assert_eq!(
+            parse_line("check Sort imax 0 0"),
+            Ok(GetType(Sort(box level::Builder::IMax(box level::Builder::Const(0), box level::Builder::Const(0)))))
+        );
     }
 
     #[test]
diff --git a/parser/src/term.pest b/parser/src/term.pest
index 72987348..c0250907 100644
--- a/parser/src/term.pest
+++ b/parser/src/term.pest
@@ -28,8 +28,8 @@ Prop = { "Prop" }
 
 Type = { "Type" ~ univ? }
 Sort = { "Sort" ~ univ? }
-univ = _{ Plus | Max | Num | string | "(" ~ univ ~ ")" }
-univ_plus = _{ Max | Num | string | "(" ~ univ ~ ")" }
+univ = _{ Plus | Max | IMax | Num | string | "(" ~ univ ~ ")" }
+univ_plus = _{ Max | IMax | Num | string | "(" ~ univ ~ ")" }
 Num = { number }
 Plus = { univ_plus ~ ( "+" ~ number )+ }
 Max = { ( "max" ~ "(" ~ univ ~ "," ~ univ ~ ")" ) | ( "max" ~ univ ~ univ ) }
diff --git a/proost/src/evaluator.rs b/proost/src/evaluator.rs
index 38947a67..c8a2e95f 100644
--- a/proost/src/evaluator.rs
+++ b/proost/src/evaluator.rs
@@ -132,10 +132,10 @@ impl<'arena> Evaluator {
             .map(|_| None)
     }
 
-    fn process<'build>(
+    fn process(
         &mut self,
         arena: &mut Arena<'arena>,
-        command: &Command<'build>,
+        command: &Command,
         importing: &mut Vec<PathBuf>,
     ) -> Result<'arena, Option<Term<'arena>>> {
         match command {
-- 
GitLab