From 73a68f53121480a7006f33120b875c48bc26bfaa Mon Sep 17 00:00:00 2001
From: arthur-adjedj <arthur.adjedj@gmail.com>
Date: Wed, 9 Nov 2022 15:07:52 +0100
Subject: [PATCH] feat : finish implementation

---
 kernel/src/command.rs      |  20 ++--
 kernel/src/lib.rs          |   1 +
 kernel/src/term.rs         | 179 +++++++++++++++++++++------------
 kernel/src/type_checker.rs | 193 ++++++++++++++++++++++-------------
 kernel/src/universe.rs     |  57 ++++++++++-
 parser/src/parser.rs       | 201 +++++++++++++++++++++++++++----------
 parser/src/term.pest       |  27 +++--
 7 files changed, 468 insertions(+), 210 deletions(-)

diff --git a/kernel/src/command.rs b/kernel/src/command.rs
index 9bd85a1d..39402a2d 100644
--- a/kernel/src/command.rs
+++ b/kernel/src/command.rs
@@ -38,14 +38,14 @@ mod tests {
 
     fn simple_env() -> Environment {
         Environment::new()
-            .insert("x".to_string(), Term::Type(0.into()), Term::Prop)
+            .insert("x".to_string(), Term::r#type(0.into()), Term::PROP)
             .unwrap()
             .clone()
     }
 
     #[test]
     fn failed_untyped_define() {
-        let cmd = Command::Define("x".to_string(), None, Term::Prop);
+        let cmd = Command::Define("x".to_string(), None, Term::PROP);
         let mut env = simple_env();
 
         assert!(cmd.process(&mut env).is_err());
@@ -54,21 +54,21 @@ mod tests {
 
     #[test]
     fn successful_untyped_define() {
-        let cmd = Command::Define("y".to_string(), None, Term::Prop);
+        let cmd = Command::Define("y".to_string(), None, Term::PROP);
         let mut env = simple_env();
 
         assert!(cmd.process(&mut env).is_ok());
         assert_eq!(
             env,
             *(simple_env()
-                .insert("y".to_string(), Term::Prop, Term::Type(0.into()))
+                .insert("y".to_string(), Term::PROP, Term::r#type(0.into()))
                 .unwrap())
         );
     }
 
     #[test]
     fn failed_typed_define() {
-        let cmd = Command::Define("y".to_string(), Some(Term::Type(1.into())), Term::Prop);
+        let cmd = Command::Define("y".to_string(), Some(Term::r#type(1.into())), Term::PROP);
         let mut env = simple_env();
 
         assert!(cmd.process(&mut env).is_err());
@@ -77,21 +77,21 @@ mod tests {
 
     #[test]
     fn successful_typed_define() {
-        let cmd = Command::Define("y".to_string(), Some(Term::Type(0.into())), Term::Prop);
+        let cmd = Command::Define("y".to_string(), Some(Term::r#type(0.into())), Term::PROP);
         let mut env = simple_env();
 
         assert!(cmd.process(&mut env).is_ok());
         assert_eq!(
             env,
             *(simple_env()
-                .insert("y".to_string(), Term::Prop, Term::Type(0.into()))
+                .insert("y".to_string(), Term::PROP, Term::r#type(0.into()))
                 .unwrap())
         );
     }
 
     #[test]
     fn failed_checktype() {
-        let cmd = Command::CheckType(Term::Prop, Term::Prop);
+        let cmd = Command::CheckType(Term::PROP, Term::PROP);
         let mut env = simple_env();
 
         assert!(cmd.process(&mut env).is_err());
@@ -100,7 +100,7 @@ mod tests {
 
     #[test]
     fn successful_checktype() {
-        let cmd = Command::CheckType(Term::Prop, Term::Type(0.into()));
+        let cmd = Command::CheckType(Term::PROP, Term::r#type(0.into()));
         let mut env = simple_env();
 
         assert!(cmd.process(&mut env).is_ok());
@@ -118,7 +118,7 @@ mod tests {
 
     #[test]
     fn successful_gettype() {
-        let cmd = Command::GetType(Term::Prop);
+        let cmd = Command::GetType(Term::PROP);
         let mut env = simple_env();
 
         assert!(cmd.process(&mut env).is_ok());
diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs
index d8591c4c..70a6a4ca 100644
--- a/kernel/src/lib.rs
+++ b/kernel/src/lib.rs
@@ -15,6 +15,7 @@ pub use derive_more;
 pub use num_bigint;
 
 pub use command::Command;
+pub use declaration::Declaration;
 pub use environment::Environment;
 pub use error::{KernelError, Loc};
 pub use term::Term;
diff --git a/kernel/src/term.rs b/kernel/src/term.rs
index e55b3a0f..03c332b1 100644
--- a/kernel/src/term.rs
+++ b/kernel/src/term.rs
@@ -18,11 +18,8 @@ pub enum Term {
     #[display(fmt = "{}", _0)]
     Const(String, Vec<UniverseLevel>),
 
-    #[display(fmt = "Prop")]
-    Prop,
-
-    #[display(fmt = "Type {}", _0)]
-    Type(UniverseLevel),
+    #[display(fmt = "Sort {}", _0)]
+    Sort(UniverseLevel),
 
     #[display(fmt = "{} {}", _0, _1)]
     App(Box<Term>, Box<Term>),
@@ -37,8 +34,17 @@ pub enum Term {
 use Term::*;
 
 impl Term {
+    pub const PROP: Term = Sort(UniverseLevel::Zero);
+
+    pub fn r#type(u: UniverseLevel) -> Term {
+        Sort(u + 1)
+    }
+
     /// Apply one step of β-reduction, using leftmost outermost evaluation strategy.
     pub fn beta_reduction(&self, env: &Environment) -> Term {
+        //if self.is_eliminator() {
+        //    return self.compute()
+        //}
         match self {
             App(box Abs(_, box t1), box t2) => t1.substitute(t2, 1),
             App(box t1, box t2) => App(box t1.beta_reduction(env), box t2.clone()),
@@ -75,7 +81,7 @@ impl Term {
 
     /// Returns the normal form of a term in a given environment.
     ///
-    /// This function is computationally expensive and should only be used for Reduce/Eval commands, not when type-checking.
+    /// This function is computationally expensive and should only be used for Reduce/Eval commands, not when Sort-checking.
     pub fn normal_form(self, env: &Environment) -> Term {
         let mut res = self.beta_reduction(env);
         let mut temp = self;
@@ -106,8 +112,7 @@ impl Term {
         match self {
             Var(_) => 0,
             Const(..) => 0,
-            Prop => 0,
-            Type(u) => u.clone().univ_vars(),
+            Sort(u) => u.clone().univ_vars(),
             App(t1, t2) | Abs(t1, t2) | Prod(t1, t2) => t1.univ_vars().max(t2.univ_vars()),
         }
     }
@@ -116,18 +121,35 @@ impl Term {
         match self {
             Var(_) => self.clone(),
             Const(..) => self.clone(),
-            Prop => self.clone(),
-            Type(u) => Type(u.clone().substitute(vec)),
+            Sort(u) => Sort(u.clone().substitute(vec)),
             App(t1, t2) => App(box t1.substitute_univs(vec), box t2.substitute_univs(vec)),
             Abs(t1, t2) => Abs(box t1.substitute_univs(vec), box t2.substitute_univs(vec)),
             Prod(t1, t2) => Prod(box t1.substitute_univs(vec), box t2.substitute_univs(vec)),
         }
     }
+
+    /*fn is_eliminator(&self) -> bool {
+        match self {
+                App(
+                    box App(
+                        box App(
+                            box App(
+                                box Const(rec,_var),
+                                _motive
+                            ),
+                            _p_zero
+                        ),
+                        _p_succ
+                    ),
+                _n) if rec == "Nat_rec" => true,
+            _ => false
+        }
+    }*/
 }
 
 #[cfg(test)]
 mod tests {
-    // /!\ most of these tests are on ill-typed terms and should not be used for further testings
+    // /!\ most of these tests are on ill-Sortd terms and should not be used for further testings
     use super::Term::*;
     use crate::term::Environment;
     use crate::universe::UniverseLevel;
@@ -136,15 +158,21 @@ mod tests {
     fn simple_subst() {
         // λx.(λy.x y) x
         let term = Abs(
-            box Prop,
+            box Sort(0.into()),
             box App(
-                box Abs(box Prop, box App(box Var(2.into()), box Var(1.into()))),
+                box Abs(
+                    box Sort(0.into()),
+                    box App(box Var(2.into()), box Var(1.into())),
+                ),
                 box Var(1.into()),
             ),
         );
 
         // λx.x x
-        let reduced = Abs(box Prop, box App(box Var(1.into()), box Var(1.into())));
+        let reduced = Abs(
+            box Sort(0.into()),
+            box App(box Var(1.into()), box Var(1.into())),
+        );
 
         assert_eq!(term.beta_reduction(&Environment::new()), reduced);
     }
@@ -154,19 +182,19 @@ mod tests {
         // (λa.λb.λc.a (λd.λe.e (d b)) (λ_.c) (λd.d)) (λa.λb.a b)
         let term = App(
             box Abs(
-                box Prop,
+                box Sort(0.into()),
                 box Abs(
-                    box Prop,
+                    box Sort(0.into()),
                     box Abs(
-                        box Prop,
+                        box Sort(0.into()),
                         box App(
                             box App(
                                 box App(
                                     box Var(3.into()),
                                     box Abs(
-                                        box Prop,
+                                        box Sort(0.into()),
                                         box Abs(
-                                            box Prop,
+                                            box Sort(0.into()),
                                             box App(
                                                 box Var(1.into()),
                                                 box App(box Var(2.into()), box Var(4.into())),
@@ -174,34 +202,40 @@ mod tests {
                                         ),
                                     ),
                                 ),
-                                box Abs(box Prop, box Var(2.into())),
+                                box Abs(box Sort(0.into()), box Var(2.into())),
                             ),
-                            box Abs(box Prop, box Var(1.into())),
+                            box Abs(box Sort(0.into()), box Var(1.into())),
                         ),
                     ),
                 ),
             ),
             box Abs(
-                box Prop,
-                box Abs(box Prop, box App(box Var(2.into()), box Var(1.into()))),
+                box Sort(0.into()),
+                box Abs(
+                    box Sort(0.into()),
+                    box App(box Var(2.into()), box Var(1.into())),
+                ),
             ),
         );
 
         let term_step_1 = Abs(
-            box Prop,
+            box Sort(0.into()),
             box Abs(
-                box Prop,
+                box Sort(0.into()),
                 box App(
                     box App(
                         box App(
                             box Abs(
-                                box Prop,
-                                box Abs(box Prop, box App(box Var(2.into()), box Var(1.into()))),
+                                box Sort(0.into()),
+                                box Abs(
+                                    box Sort(0.into()),
+                                    box App(box Var(2.into()), box Var(1.into())),
+                                ),
                             ),
                             box Abs(
-                                box Prop,
+                                box Sort(0.into()),
                                 box Abs(
-                                    box Prop,
+                                    box Sort(0.into()),
                                     box App(
                                         box Var(1.into()),
                                         box App(box Var(2.into()), box Var(4.into())),
@@ -209,26 +243,26 @@ mod tests {
                                 ),
                             ),
                         ),
-                        box Abs(box Prop, box Var(2.into())),
+                        box Abs(box Sort(0.into()), box Var(2.into())),
                     ),
-                    box Abs(box Prop, box Var(1.into())),
+                    box Abs(box Sort(0.into()), box Var(1.into())),
                 ),
             ),
         );
 
         let term_step_2 = Abs(
-            box Prop,
+            box Sort(0.into()),
             box Abs(
-                box Prop,
+                box Sort(0.into()),
                 box App(
                     box App(
                         box Abs(
-                            box Prop,
+                            box Sort(0.into()),
                             box App(
                                 box Abs(
-                                    box Prop,
+                                    box Sort(0.into()),
                                     box Abs(
-                                        box Prop,
+                                        box Sort(0.into()),
                                         box App(
                                             box Var(1.into()),
                                             box App(box Var(2.into()), box Var(5.into())),
@@ -238,74 +272,86 @@ mod tests {
                                 box Var(1.into()),
                             ),
                         ),
-                        box Abs(box Prop, box Var(2.into())),
+                        box Abs(box Sort(0.into()), box Var(2.into())),
                     ),
-                    box Abs(box Prop, box Var(1.into())),
+                    box Abs(box Sort(0.into()), box Var(1.into())),
                 ),
             ),
         );
 
         let term_step_3 = Abs(
-            box Prop,
+            box Sort(0.into()),
             box Abs(
-                box Prop,
+                box Sort(0.into()),
                 box App(
                     box App(
                         box Abs(
-                            box Prop,
+                            box Sort(0.into()),
                             box Abs(
-                                box Prop,
+                                box Sort(0.into()),
                                 box App(
                                     box Var(1.into()),
                                     box App(box Var(2.into()), box Var(4.into())),
                                 ),
                             ),
                         ),
-                        box Abs(box Prop, box Var(2.into())),
+                        box Abs(box Sort(0.into()), box Var(2.into())),
                     ),
-                    box Abs(box Prop, box Var(1.into())),
+                    box Abs(box Sort(0.into()), box Var(1.into())),
                 ),
             ),
         );
 
         let term_step_4 = Abs(
-            box Prop,
+            box Sort(0.into()),
             box Abs(
-                box Prop,
+                box Sort(0.into()),
                 box App(
                     box Abs(
-                        box Prop,
+                        box Sort(0.into()),
                         box App(
                             box Var(1.into()),
-                            box App(box Abs(box Prop, box Var(3.into())), box Var(3.into())),
+                            box App(
+                                box Abs(box Sort(0.into()), box Var(3.into())),
+                                box Var(3.into()),
+                            ),
                         ),
                     ),
-                    box Abs(box Prop, box Var(1.into())),
+                    box Abs(box Sort(0.into()), box Var(1.into())),
                 ),
             ),
         );
 
         let term_step_5 = Abs(
-            box Prop,
+            box Sort(0.into()),
             box Abs(
-                box Prop,
+                box Sort(0.into()),
                 box App(
-                    box Abs(box Prop, box Var(1.into())),
-                    box App(box Abs(box Prop, box Var(2.into())), box Var(2.into())),
+                    box Abs(box Sort(0.into()), box Var(1.into())),
+                    box App(
+                        box Abs(box Sort(0.into()), box Var(2.into())),
+                        box Var(2.into()),
+                    ),
                 ),
             ),
         );
 
         let term_step_6 = Abs(
-            box Prop,
+            box Sort(0.into()),
             box Abs(
-                box Prop,
-                box App(box Abs(box Prop, box Var(2.into())), box Var(2.into())),
+                box Sort(0.into()),
+                box App(
+                    box Abs(box Sort(0.into()), box Var(2.into())),
+                    box Var(2.into()),
+                ),
             ),
         );
 
         // λa.λb.b
-        let term_step_7 = Abs(box Prop, box Abs(box Prop, box Var(1.into())));
+        let term_step_7 = Abs(
+            box Sort(0.into()),
+            box Abs(box Sort(0.into()), box Var(1.into())),
+        );
 
         let env = Environment::new();
 
@@ -322,16 +368,23 @@ mod tests {
     #[test]
     fn shift_prod() {
         let t1 = Prod(box Var(1.into()), box Var(1.into()));
-        let t2 = App(box Abs(box Prop, box t1.clone()), box Prop);
+        let t2 = App(
+            box Abs(box Sort(0.into()), box t1.clone()),
+            box Sort(0.into()),
+        );
 
         assert_eq!(t2.beta_reduction(&Environment::new()), t1)
     }
 
     #[test]
     fn beta_red_const() {
-        let id_prop = Prod(box Prop, box Prod(box Var(1.into()), box Var(1.into())));
+        let id_prop = Prod(
+            box Sort(0.into()),
+            box Prod(box Var(1.into()), box Var(1.into())),
+        );
         let mut env = Environment::new();
-        env.insert("foo".into(), id_prop.clone(), Prop).unwrap();
+        env.insert("foo".into(), id_prop.clone(), Sort(0.into()))
+            .unwrap();
 
         assert_eq!(
             Const("foo".into(), Vec::new()).beta_reduction(&env),
@@ -342,15 +395,15 @@ mod tests {
     #[test]
     fn poly_univ_id() {
         let id_ty = Prod(
-            box Type(UniverseLevel::Var(0)),
+            box Sort(UniverseLevel::Var(0)),
             box Prod(box Var(1.into()), box Var(2.into())),
         );
         let id_te = Abs(
-            box Type(UniverseLevel::Var(0)),
+            box Sort(UniverseLevel::Var(0)),
             box Abs(box Var(1.into()), box Var(1.into())),
         );
         let id_zero = Abs(
-            box Type(0.into()),
+            box Sort(0.into()),
             box Abs(box Var(1.into()), box Var(1.into())),
         );
         assert!(id_te.check(&id_ty, &Environment::new()).is_ok());
diff --git a/kernel/src/type_checker.rs b/kernel/src/type_checker.rs
index 043060dc..fbe9b3d5 100644
--- a/kernel/src/type_checker.rs
+++ b/kernel/src/type_checker.rs
@@ -46,9 +46,9 @@ impl Term {
     /// The conversion is untyped, meaning that it should **only** be called during type-checking when the two `Term`s are already known to be of the same type and in the same context.
     fn conversion(&self, rhs: &Term, env: &Environment, lvl: DeBruijnIndex) -> bool {
         match (self.whnf(env), rhs.whnf(env)) {
-            (Type(i), Type(j)) => i.is_eq(&j),
+            (lhs, rhs) if lhs == rhs => true,
 
-            (Prop, Prop) => true,
+            (Sort(i), Sort(j)) => i.is_eq(&j),
 
             (Var(i), Var(j)) => i == j,
 
@@ -91,27 +91,16 @@ impl Term {
 
     /// Computes universe the universe in which `(x : A) -> B` lives when `A : u1` and `B : u2`.
     fn imax(&self, rhs: &Term) -> Result<Term, KernelError> {
-        match rhs {
-            // Because Prop is impredicative, if B : Prop, then (x : A) -> b : Prop
-            Prop => Ok(Prop),
-
-            Type(ref i) => match self {
-                Prop => Ok(Type(i.clone())),
-
-                // else if u1 = Type(i) and u2 = Type(j), then (x : A) -> B : Type(max(i,j))
-                Type(j) => Ok(Type(UniverseLevel::Max(box i.clone(), box j.clone()))),
-
-                _ => Err(KernelError::NotUniverse(self.clone())),
-            },
-
+        match (self, rhs) {
+            (Sort(u), Sort(v)) => Ok(Sort(UniverseLevel::IMax(box u.clone(), box v.clone()))),
+            (Sort(_), _) => Err(KernelError::NotUniverse(rhs.clone())),
             _ => Err(KernelError::NotUniverse(rhs.clone())),
         }
     }
 
     fn _infer(&self, env: &Environment, ctx: &mut Context) -> Result<Term, KernelError> {
         match self {
-            Prop => Ok(Type(UniverseLevel::Zero)),
-            Type(i) => Ok(Type(i.clone() + 1)),
+            Sort(i) => Ok(Sort(i.clone() + 1)),
             Var(i) => Ok(ctx.types[ctx.lvl - *i].clone()),
 
             Const(s, vec) => match env.get_type(s, vec) {
@@ -170,41 +159,50 @@ mod tests {
     use crate::type_checker::*;
 
     fn id(l: usize) -> Box<Term> {
-        box Abs(box Prop, box Var(l.into()))
+        box Abs(box Sort(0.into()), box Var(l.into()))
     }
 
     #[test]
     fn var_subst_1() {
         // (λ P. λP. 1) (λP.1)
         let t = App(
-            box Abs(box Prop, box Abs(box Prop, box Var(1.into()))),
+            box Abs(
+                box Sort(0.into()),
+                box Abs(box Sort(0.into()), box Var(1.into())),
+            ),
             id(1),
         );
 
-        let nf = Abs(box Prop, box Var(1.into()));
+        let nf = Abs(box Sort(0.into()), box Var(1.into()));
         assert_eq!(t.normal_form(&Environment::new()), nf)
     }
 
     #[test]
     fn var_subst_2() {
         let t = App(
-            box Abs(box Prop, box Abs(box Prop, box Var(2.into()))),
+            box Abs(
+                box Sort(0.into()),
+                box Abs(box Sort(0.into()), box Var(2.into())),
+            ),
             id(1),
         );
 
-        let nf = Abs(box Prop, id(1));
+        let nf = Abs(box Sort(0.into()), id(1));
         assert_eq!(t.normal_form(&Environment::new()), nf)
     }
 
     #[test]
     fn simple() {
-        let t1 = App(box Abs(box Type(0.into()), box Var(1.into())), box Prop);
+        let t1 = App(
+            box Abs(box Sort(1.into()), box Var(1.into())),
+            box Sort(0.into()),
+        );
 
-        let t2 = Prop;
+        let t2 = Sort(0.into());
         assert!(t1.conversion(&t2, &Environment::new(), 0.into()));
 
         let ty = t1.infer(&Environment::new());
-        assert_eq!(ty, Ok(Type(0.into())));
+        assert_eq!(ty, Ok(Sort(1.into())));
     }
 
     #[test]
@@ -212,15 +210,21 @@ mod tests {
         // λ (x : P -> P).(λ (y :P).x y) x
         //λ P -> P.(λ P.2 1) 1
         let term = Abs(
-            box Prod(box Prop, box Prop),
+            box Prod(box Sort(0.into()), box Sort(0.into())),
             box App(
-                box Abs(box Prop, box App(box Var(2.into()), box Var(1.into()))),
+                box Abs(
+                    box Sort(0.into()),
+                    box App(box Var(2.into()), box Var(1.into())),
+                ),
                 box Var(1.into()),
             ),
         );
 
         // λx.x x
-        let reduced = Abs(box Prop, box App(box Var(1.into()), box Var(1.into())));
+        let reduced = Abs(
+            box Sort(0.into()),
+            box App(box Var(1.into()), box Var(1.into())),
+        );
         assert!(term.is_def_eq(&reduced, &Environment::new()).is_ok());
         assert!(term.infer(&Environment::new()).is_err());
     }
@@ -235,34 +239,40 @@ mod tests {
                     // (P → P) → ((P → P) → P)
                     box Prod(
                         // P -> P
-                        box Prod(box Prop, box Prop),
+                        box Prod(box Sort(0.into()), box Sort(0.into())),
                         // (P -> P) -> P
-                        box Prod(box Prod(box Prop, box Prop), box Prop),
+                        box Prod(
+                            box Prod(box Sort(0.into()), box Sort(0.into())),
+                            box Sort(0.into()),
+                        ),
                     ),
                     // (P → P) → ((P → P) → P)
                     box Prod(
                         // P -> P
-                        box Prod(box Prop, box Prop),
+                        box Prod(box Sort(0.into()), box Sort(0.into())),
                         // (P -> P) -> P
-                        box Prod(box Prod(box Prop, box Prop), box Prop),
+                        box Prod(
+                            box Prod(box Sort(0.into()), box Sort(0.into())),
+                            box Sort(0.into()),
+                        ),
                     ),
                 ),
                 box Abs(
                     // b : P
-                    box Prop,
+                    box Sort(0.into()),
                     box Abs(
                         // c : P
-                        box Prop,
+                        box Sort(0.into()),
                         box App(
                             box App(
                                 box App(
                                     box Var(3.into()),
                                     box Abs(
                                         // d : P -> P
-                                        box Prod(box Prop, box Prop),
+                                        box Prod(box Sort(0.into()), box Sort(0.into())),
                                         box Abs(
                                             // e : P -> P
-                                            box Prod(box Prop, box Prop),
+                                            box Prod(box Sort(0.into()), box Sort(0.into())),
                                             box App(
                                                 box Var(1.into()),
                                                 box App(box Var(2.into()), box Var(4.into())),
@@ -271,10 +281,10 @@ mod tests {
                                     ),
                                 ),
                                 // _ : P
-                                box Abs(box Prop, box Var(2.into())),
+                                box Abs(box Sort(0.into()), box Var(2.into())),
                             ),
                             //d : P
-                            box Abs(box Prop, box Var(1.into())),
+                            box Abs(box Sort(0.into()), box Var(1.into())),
                         ),
                     ),
                 ),
@@ -282,19 +292,25 @@ mod tests {
             box Abs(
                 //a : (P -> P) -> (P -> P) -> P
                 box Prod(
-                    box Prod(box Prop, box Prop),
-                    box Prod(box Prod(box Prop, box Prop), box Prop),
+                    box Prod(box Sort(0.into()), box Sort(0.into())),
+                    box Prod(
+                        box Prod(box Sort(0.into()), box Sort(0.into())),
+                        box Sort(0.into()),
+                    ),
                 ),
                 box Abs(
                     //b : P -> P
-                    box Prod(box Prop, box Prop),
+                    box Prod(box Sort(0.into()), box Sort(0.into())),
                     box App(box Var(2.into()), box Var(1.into())),
                 ),
             ),
         );
 
         // λa : P.λb : P .b
-        let reduced = Abs(box Prop, box Abs(box Prop, box Var(1.into())));
+        let reduced = Abs(
+            box Sort(0.into()),
+            box Abs(box Sort(0.into()), box Var(1.into())),
+        );
         assert!(term.is_def_eq(&reduced, &Environment::new()).is_ok());
         assert!(term.infer(&Environment::new()).is_ok())
     }
@@ -303,7 +319,10 @@ mod tests {
     #[test]
     fn nf_test() {
         //λa.a (λx.x) (λx.x)
-        let reduced = Abs(box Prop, box App(box App(box Var(2.into()), id(1)), id(1)));
+        let reduced = Abs(
+            box Sort(0.into()),
+            box App(box App(box Var(2.into()), id(1)), id(1)),
+        );
 
         let nff = reduced.clone().normal_form(&Environment::new());
         assert_eq!(reduced, nff);
@@ -313,7 +332,7 @@ mod tests {
     #[test]
     fn polymorphism() {
         let id = Abs(
-            box Type(0.into()),
+            box Sort(0.into()),
             box Abs(box Var(1.into()), box Var(1.into())),
         );
 
@@ -322,45 +341,54 @@ mod tests {
 
     #[test]
     fn type_type() {
-        assert!(Type(0.into())
-            .check(&Type(1.into()), &Environment::new())
+        assert!(Sort(0.into())
+            .check(&Sort(1.into()), &Environment::new())
             .is_ok());
     }
 
     #[test]
     fn not_function() {
-        let t = App(box Prop, box Prop);
+        let t = App(box Sort(0.into()), box Sort(0.into()));
         assert!(t.infer(&Environment::new()).is_err())
     }
 
     #[test]
     fn not_type_prod() {
-        let t = Prod(box Abs(box Prop, box Var(1.into())), box Prop);
+        let t = Prod(
+            box Abs(box Sort(0.into()), box Var(1.into())),
+            box Sort(0.into()),
+        );
         assert!(t.infer(&Environment::new()).is_err());
 
-        let t = Prod(box Prop, box Abs(box Prop, box Prop));
+        let t = Prod(
+            box Sort(0.into()),
+            box Abs(box Sort(0.into()), box Sort(0.into())),
+        );
         assert!(t.infer(&Environment::new()).is_err());
 
-        let wf_prod = Prod(box Prop, box Prop);
+        let wf_prod = Prod(box Sort(0.into()), box Sort(0.into()));
         println!("{:?}", wf_prod.infer(&Environment::new()));
-        assert!(wf_prod.check(&Type(0.into()), &Environment::new()).is_ok());
+        assert!(wf_prod.check(&Sort(1.into()), &Environment::new()).is_ok());
 
-        let wf_prod = Prod(box Prop, box Var(1.into()));
-        assert!(wf_prod.check(&Prop, &Environment::new()).is_ok());
+        let wf_prod = Prod(box Sort(0.into()), box Var(1.into()));
+        assert!(wf_prod.check(&Sort(0.into()), &Environment::new()).is_ok());
 
-        // Type0 -> (A : Prop) ->
-        let wf_prod = Prod(box Prop, box Prop);
-        assert!(wf_prod.check(&Type(0.into()), &Environment::new()).is_ok());
+        // Sort0 -> (A : Sort(0.into())) ->
+        let wf_prod = Prod(box Sort(0.into()), box Sort(0.into()));
+        assert!(wf_prod.check(&Sort(1.into()), &Environment::new()).is_ok());
     }
 
     #[test]
     fn poly_test2() {
         let t = Abs(
-            box Prop,
+            box Sort(0.into()),
             box Abs(
-                box Prop,
+                box Sort(0.into()),
                 box Abs(
-                    box Prod(box Prop, box Prod(box Prop, box Prop)),
+                    box Prod(
+                        box Sort(0.into()),
+                        box Prod(box Sort(0.into()), box Sort(0.into())),
+                    ),
                     box App(
                         box App(box Var(1.into()), box Var(3.into())),
                         box Var(2.into()),
@@ -374,10 +402,18 @@ mod tests {
 
     #[test]
     fn env_test() {
-        let id_prop = Prod(box Prop, box Prod(box Var(1.into()), box Var(2.into())));
-        let t = Abs(box Prop, box Abs(box Var(1.into()), box Var(1.into())));
+        let id_prop = Prod(
+            box Sort(0.into()),
+            box Prod(box Var(1.into()), box Var(2.into())),
+        );
+        let t = Abs(
+            box Sort(0.into()),
+            box Abs(box Var(1.into()), box Var(1.into())),
+        );
         let mut binding = Environment::new();
-        let env = binding.insert("foo".into(), id_prop.clone(), Prop).unwrap();
+        let env = binding
+            .insert("foo".into(), id_prop.clone(), Sort(0.into()))
+            .unwrap();
 
         assert!(t.check(&Const("foo".into(), Vec::new()), env).is_ok());
         assert!(id_prop
@@ -387,21 +423,28 @@ mod tests {
 
     #[test]
     fn check_bad() {
-        assert!(Prop.check(&Prop, &Environment::new()).is_err());
+        assert!(Sort(0.into())
+            .check(&Sort(0.into()), &Environment::new())
+            .is_err());
     }
 
     #[test]
     fn not_def_eq() {
-        assert!(Prop
-            .is_def_eq(&Type(0.into()), &Environment::new())
+        assert!(Sort(0.into())
+            .is_def_eq(&Sort(1.into()), &Environment::new())
             .is_err());
     }
 
     #[test]
     fn infer_const() {
-        let id_prop = Prod(box Prop, box Prod(box Var(1.into()), box Var(1.into())));
+        let id_prop = Prod(
+            box Sort(0.into()),
+            box Prod(box Var(1.into()), box Var(1.into())),
+        );
         let mut binding = Environment::new();
-        let env = binding.insert("foo".into(), id_prop, Prop).unwrap();
+        let env = binding
+            .insert("foo".into(), id_prop, Sort(0.into()))
+            .unwrap();
 
         assert!(Const("foo".into(), Vec::new()).infer(env).is_ok());
         assert!(Const("foo".into(), Vec::new())
@@ -411,17 +454,23 @@ mod tests {
 
     #[test]
     fn prod_var() {
-        let ty = Prod(box Prop, box Prod(box Var(1.into()), box Var(2.into())));
-        let t = Abs(box Prop, box Abs(box Var(1.into()), box Var(1.into())));
+        let ty = Prod(
+            box Sort(0.into()),
+            box Prod(box Var(1.into()), box Var(2.into())),
+        );
+        let t = Abs(
+            box Sort(0.into()),
+            box Abs(box Var(1.into()), box Var(1.into())),
+        );
         assert!(t.check(&ty, &Environment::new()).is_ok())
     }
 
     #[test]
     fn univ_reduc() {
         let ty = App(
-            box Abs(box Prop, box Type(0.into())),
-            box Prod(box Prop, box Var(1.into())),
+            box Abs(box Sort(0.into()), box Sort(0.into())),
+            box Prod(box Sort(0.into()), box Var(1.into())),
         );
-        assert!(ty.conversion(&Type(0.into()), &Environment::new(), 0.into()))
+        assert!(ty.conversion(&Sort(0.into()), &Environment::new(), 0.into()))
     }
 }
diff --git a/kernel/src/universe.rs b/kernel/src/universe.rs
index 1da5d8ee..40d3bf99 100644
--- a/kernel/src/universe.rs
+++ b/kernel/src/universe.rs
@@ -11,6 +11,8 @@ pub enum UniverseLevel {
 
     Max(Box<UniverseLevel>, Box<UniverseLevel>),
 
+    IMax(Box<UniverseLevel>, Box<UniverseLevel>),
+
     Var(usize),
 }
 
@@ -57,6 +59,13 @@ impl UniverseLevel {
             Max(box n, box m) => n
                 .to_numeral()
                 .and_then(|n| m.to_numeral().map(|m| n.max(m))),
+            IMax(box n, box m) => m.to_numeral().and_then(|m| {
+                if m == 0 {
+                    0.into()
+                } else {
+                    n.to_numeral().map(|n| m.max(n))
+                }
+            }),
             _ => None,
         }
     }
@@ -78,10 +87,11 @@ impl UniverseLevel {
             None => match self {
                 Zero => unreachable!("Zero is a numeral"),
                 Succ(_) => match self.plus() {
-                    (u, 0) => format!("{}", u.pretty_print()),
+                    (u, 0) => u.pretty_print(),
                     (u, n) => format!("{} + {}", u.pretty_print(), n),
                 },
                 Max(box n, box m) => format!("max ({}) ({})", n.pretty_print(), m.pretty_print()),
+                IMax(box n, box m) => format!("imax ({}) ({})", n.pretty_print(), m.pretty_print()),
                 Var(n) => format!("u{}", n),
             },
         }
@@ -92,6 +102,7 @@ impl UniverseLevel {
             Zero => 0,
             Succ(n) => n.univ_vars(),
             Max(n, m) => n.univ_vars().max(m.univ_vars()),
+            IMax(n, m) => n.univ_vars().max(m.univ_vars()),
             Var(n) => n + 1,
         }
     }
@@ -101,6 +112,7 @@ impl UniverseLevel {
             Zero => Zero,
             Succ(v) => Succ(box v.substitute(univs)),
             Max(u1, u2) => Max(box u1.substitute(univs), box u2.substitute(univs)),
+            IMax(u1, u2) => IMax(box u1.substitute(univs), box u2.substitute(univs)),
             Var(var) => univs[var].clone(),
         }
     }
@@ -113,6 +125,49 @@ impl UniverseLevel {
             (_, Succ(box l)) if self.geq(l, n + 1) => true,
             (_, Max(box l1, box l2)) if self.geq(l1, n) || self.geq(l2, n) => true,
             (Max(box l1, box l2), _) if l1.geq(u2, n) && l2.geq(u2, n) => true,
+            (IMax(_, box Zero), l) if Zero.geq(l, n) => true,
+            (IMax(l1, box Succ(l2)), l) if Max(l1.clone(), box Succ(l2.clone())).geq(l, n) => true,
+            (l, IMax(l1, box Succ(l2))) if l.geq(&Max(l1.clone(), box Succ(l2.clone())), n) => true,
+            (Max(l1, box IMax(l2, l3)), l)
+                if Max(
+                    box IMax(l1.clone(), l3.clone()),
+                    box IMax(l2.clone(), l3.clone()),
+                )
+                .geq(l, n) =>
+            {
+                true
+            }
+            (l, IMax(l1, box IMax(l2, l3)))
+                if l.geq(
+                    &Max(
+                        box IMax(l1.clone(), l3.clone()),
+                        box IMax(l2.clone(), l3.clone()),
+                    ),
+                    n,
+                ) =>
+            {
+                true
+            }
+            (IMax(l1, box Max(l2, l3)), l)
+                if Max(
+                    box IMax(l1.clone(), l2.clone()),
+                    box IMax(l1.clone(), l3.clone()),
+                )
+                .geq(l, n) =>
+            {
+                true
+            }
+            (l, IMax(l1, box Max(l2, l3)))
+                if l.geq(
+                    &Max(
+                        box IMax(l1.clone(), l2.clone()),
+                        box IMax(l1.clone(), l3.clone()),
+                    ),
+                    n,
+                ) =>
+            {
+                true
+            }
             _ => false,
         }
     }
diff --git a/parser/src/parser.rs b/parser/src/parser.rs
index 4631bcda..39f825e8 100644
--- a/parser/src/parser.rs
+++ b/parser/src/parser.rs
@@ -2,17 +2,18 @@ use kernel::{Command, KernelError, Loc, Term, UniverseLevel};
 use pest::error::{Error, LineColLocation};
 use pest::iterators::Pair;
 use pest::{Parser, Span};
-use std::collections::VecDeque;
+use std::collections::{HashMap, VecDeque};
 
 #[derive(Parser)]
 #[grammar = "term.pest"]
 struct CommandParser;
 
 /// build universe level from errorless pest's output
-fn build_universe_level_from_expr(pair: Pair<Rule>) -> UniverseLevel {
+fn build_universe_level_from_expr(
+    pair: Pair<Rule>,
+    univ_var_map: &HashMap<String, usize>,
+) -> UniverseLevel {
     match pair.as_rule() {
-        //TODO after term refactor (new ways to deal with variables)
-        Rule::Var => UniverseLevel::Var(0),
         Rule::Num => {
             let n = pair.into_inner().as_str().parse().unwrap();
             let mut univ = UniverseLevel::Zero;
@@ -23,19 +24,26 @@ fn build_universe_level_from_expr(pair: Pair<Rule>) -> UniverseLevel {
         }
         Rule::Max => {
             let mut iter = pair.into_inner();
-            let univ1 = build_universe_level_from_expr(iter.next().unwrap());
-            let univ2 = build_universe_level_from_expr(iter.next().unwrap());
+            let univ1 = build_universe_level_from_expr(iter.next().unwrap(), univ_var_map);
+            let univ2 = build_universe_level_from_expr(iter.next().unwrap(), univ_var_map);
             UniverseLevel::Max(box univ1, box univ2)
         }
         Rule::Plus => {
             let mut iter = pair.into_inner();
-            let mut univ = build_universe_level_from_expr(iter.next().unwrap());
+            let mut univ = build_universe_level_from_expr(iter.next().unwrap(), univ_var_map);
             let n = iter.map(|x| x.as_str().parse::<u64>().unwrap()).sum();
             for _ in 0..n {
                 univ = UniverseLevel::Succ(box univ);
             }
             univ
         }
+        Rule::string => {
+            let name = pair.as_str().to_string();
+            match univ_var_map.get(&name) {
+                Some(n) => UniverseLevel::Var(*n),
+                None => panic!("Universe level {:?} is unbound", name),
+            }
+        }
         univ => unreachable!("Unexpected universe level: {:?}", univ),
     }
 }
@@ -47,27 +55,65 @@ fn convert_span(span: Span) -> Loc {
     Loc::new(x1, y1, x2, y2)
 }
 
+fn build_univ_map_from_expr(pair: Pair<Rule>) -> HashMap<String, usize> {
+    let iter = pair.into_inner();
+    let mut map = HashMap::new();
+    for (i,pair) in iter.enumerate() {
+        let str = pair.as_str();
+        if map.insert(str.to_string(), i).is_some() {
+            panic!("Duplicate universe variable {}", str);
+        }
+    }
+    map
+}
+
+fn build_univ_bindings_from_expr(
+    pair: Pair<Rule>,
+    univ_var_map: &HashMap<String, usize>,
+) -> Vec<UniverseLevel> {
+    let iter = pair.into_inner();
+    let mut vec = Vec::new();
+    for pair in iter {
+        vec.push(build_universe_level_from_expr(pair, univ_var_map));
+    }
+    vec
+}
+
 /// build terms from errorless pest's output
-fn build_term_from_expr(pair: Pair<Rule>, known_vars: &mut VecDeque<String>) -> Term {
+fn build_term_from_expr(
+    pair: Pair<Rule>,
+    known_vars: &mut VecDeque<String>,
+    univ_var_map: &HashMap<String, usize>,
+) -> Term {
     // location to be used in a future version
     let _loc = convert_span(pair.as_span());
     match pair.as_rule() {
-        Rule::Prop => Term::Prop,
+        Rule::Prop => Term::Sort(0.into()),
         Rule::Type => match pair.into_inner().next() {
-            Some(pair) => Term::Type(build_universe_level_from_expr(pair)),
-            None => Term::Type(UniverseLevel::Zero),
+            Some(pair) => Term::Sort(build_universe_level_from_expr(pair, univ_var_map) + 1),
+            None => Term::Sort(UniverseLevel::Zero + 1),
+        },
+        Rule::Sort => match pair.into_inner().next() {
+            Some(pair) => Term::Sort(build_universe_level_from_expr(pair, univ_var_map)),
+            None => Term::Sort(UniverseLevel::Zero),
         },
         Rule::Var => {
-            let name = pair.into_inner().as_str().to_string();
+            let mut iter = pair.into_inner();
+            let name = iter.next().unwrap().as_str().to_string();
             match known_vars.iter().position(|x| *x == name) {
                 Some(i) => Term::Var((i + 1).into()),
-                None => Term::Const(name, Vec::new()),
+                None => Term::Const(
+                    name,
+                    iter.next()
+                        .map(|x| build_univ_bindings_from_expr(x, univ_var_map))
+                        .unwrap_or_default(),
+                ),
             }
         }
         Rule::App => {
             let mut iter = pair
                 .into_inner()
-                .map(|x| build_term_from_expr(x, known_vars));
+                .map(|x| build_term_from_expr(x, known_vars, univ_var_map));
             let t = iter.next().unwrap();
             iter.fold(t, |acc, x| Term::App(box acc, box x))
         }
@@ -79,11 +125,15 @@ fn build_term_from_expr(pair: Pair<Rule>, known_vars: &mut VecDeque<String>) ->
                 let mut iter = pair.into_inner();
                 let old_pair = iter.next_back().unwrap();
                 for pair in iter {
-                    terms.push(build_term_from_expr(old_pair.clone(), known_vars));
+                    terms.push(build_term_from_expr(
+                        old_pair.clone(),
+                        known_vars,
+                        univ_var_map,
+                    ));
                     known_vars.push_front(pair.as_str().to_string());
                 }
             }
-            let t = build_term_from_expr(pair, known_vars);
+            let t = build_term_from_expr(pair, known_vars, univ_var_map);
             terms.into_iter().rev().fold(t, |acc, x| {
                 known_vars.pop_front();
                 Term::Abs(box x, box acc)
@@ -97,11 +147,15 @@ fn build_term_from_expr(pair: Pair<Rule>, known_vars: &mut VecDeque<String>) ->
                 let mut iter = pair.into_inner();
                 let old_pair = iter.next_back().unwrap();
                 for pair in iter {
-                    terms.push(build_term_from_expr(old_pair.clone(), known_vars));
+                    terms.push(build_term_from_expr(
+                        old_pair.clone(),
+                        known_vars,
+                        univ_var_map,
+                    ));
                     known_vars.push_front(pair.as_str().to_string());
                 }
             }
-            let t = build_term_from_expr(pair, known_vars);
+            let t = build_term_from_expr(pair, known_vars, univ_var_map);
             terms.into_iter().rev().fold(t, |acc, x| {
                 known_vars.pop_front();
                 Term::Prod(box x, box acc)
@@ -112,11 +166,11 @@ fn build_term_from_expr(pair: Pair<Rule>, known_vars: &mut VecDeque<String>) ->
             let pair = iter.next_back().unwrap();
             let mut terms = Vec::new();
             for pair in iter {
-                let t = build_term_from_expr(pair, known_vars);
+                let t = build_term_from_expr(pair, known_vars, univ_var_map);
                 known_vars.push_front("_".to_string());
                 terms.push(t);
             }
-            let t = build_term_from_expr(pair, known_vars);
+            let t = build_term_from_expr(pair, known_vars, univ_var_map);
             terms.into_iter().rev().fold(t, |acc, x| {
                 known_vars.pop_front();
                 Term::Prod(box x, box acc)
@@ -133,31 +187,51 @@ fn build_command_from_expr(pair: Pair<Rule>) -> Command {
     match pair.as_rule() {
         Rule::GetType => {
             let mut iter = pair.into_inner();
-            let t = build_term_from_expr(iter.next().unwrap(), &mut VecDeque::new());
+            let t =
+                build_term_from_expr(iter.next().unwrap(), &mut VecDeque::new(), &HashMap::new());
             Command::GetType(t)
         }
         Rule::CheckType => {
             let mut iter = pair.into_inner();
-            let t1 = build_term_from_expr(iter.next().unwrap(), &mut VecDeque::new());
-            let t2 = build_term_from_expr(iter.next().unwrap(), &mut VecDeque::new());
+            let t1 =
+                build_term_from_expr(iter.next().unwrap(), &mut VecDeque::new(), &HashMap::new());
+            let t2 =
+                build_term_from_expr(iter.next().unwrap(), &mut VecDeque::new(), &HashMap::new());
             Command::CheckType(t1, t2)
         }
         Rule::Define => {
             let mut iter = pair.into_inner();
             let s = iter.next().unwrap().as_str().to_string();
-            let term = build_term_from_expr(iter.next().unwrap(), &mut VecDeque::new());
+            //let _univ_params = iter.next();
+            let next = iter.next();
+            let term = {
+                if matches!(
+                    next.clone().map(|x| x.as_rule()),
+                    None | Some(Rule::univ_decl)
+                ) {
+                    let univs = next
+                        .map(build_univ_map_from_expr )
+                        .unwrap_or_default();
+                    build_term_from_expr(iter.next().unwrap(), &mut VecDeque::new(), &univs)
+                } else {
+                    build_term_from_expr(next.unwrap(), &mut VecDeque::new(), &HashMap::new())
+                }
+            };
             Command::Define(s, None, term)
         }
         Rule::DefineCheckType => {
             let mut iter = pair.into_inner();
             let s = iter.next().unwrap().as_str().to_string();
-            let t = build_term_from_expr(iter.next().unwrap(), &mut VecDeque::new());
-            let term = build_term_from_expr(iter.next().unwrap(), &mut VecDeque::new());
+            let t =
+                build_term_from_expr(iter.next().unwrap(), &mut VecDeque::new(), &HashMap::new());
+            let term =
+                build_term_from_expr(iter.next().unwrap(), &mut VecDeque::new(), &HashMap::new());
             Command::Define(s, Some(t), term)
         }
         Rule::Eval => {
             let mut iter = pair.into_inner();
-            let t = build_term_from_expr(iter.next().unwrap(), &mut VecDeque::new());
+            let t =
+                build_term_from_expr(iter.next().unwrap(), &mut VecDeque::new(), &HashMap::new());
             Command::Eval(t)
         }
 
@@ -174,14 +248,23 @@ fn convert_error(err: Error<Rule>) -> KernelError {
         Rule::Define => "def var := term".to_owned(),
         Rule::CheckType => "check term : term".to_owned(),
         Rule::GetType => "check term".to_owned(),
+        Rule::Eval => "eval term".to_owned(),
         Rule::DefineCheckType => "def var : term := term".to_owned(),
         Rule::Abs => "abstraction".to_owned(),
         Rule::dProd => "dependent product".to_owned(),
         Rule::Prod => "product".to_owned(),
         Rule::App => "application".to_owned(),
         Rule::Prop => "Prop".to_owned(),
+        Rule::Sort => "Sort".to_owned(),
         Rule::Type => "Type".to_owned(),
-        _ => unreachable!("low level rules cannot appear in error messages"),
+        Rule::Max => "max".to_owned(),
+        Rule::Plus => "plus".to_owned(),
+        Rule::arg_univ => "universe argument".to_owned(),
+        Rule::univ_decl => "universe declaration".to_owned(),
+        rule => {
+            println!("{:?}", rule);
+            unreachable!("low level rules cannot appear in error messages")
+        }
     });
 
     // extracting the location from the pest output
@@ -252,11 +335,13 @@ mod tests {
 
     /// Error messages
     const COMMAND_ERR: &str =
-        "expected def var := term, def var : term := term, check term : term, or check term";
+        "expected eval term, def var := term, def var : term := term, check term : term, or check term";
     const TERM_ERR: &str =
-        "expected variable, abstraction, dependent product, application, product, Prop, or Type";
-    const SIMPLE_TERM_ERR: &str = "expected variable, abstraction, Prop, or Type";
-    const UNIVERSE_ERR: &str = "expected universe level, variable, abstraction, Prop, or Type";
+        "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";
 
     #[test]
     fn failure_universe_level() {
@@ -273,7 +358,11 @@ mod tests {
     fn successful_definechecktype() {
         assert_eq!(
             parse_line("def x : Type := Prop"),
-            Ok(Define("x".to_string(), Some(Type(0.into())), Prop))
+            Ok(Define(
+                "x".to_string(),
+                Some(Sort(1.into())),
+                Sort(0.into())
+            ))
         )
     }
 
@@ -281,7 +370,7 @@ mod tests {
     fn successful_define() {
         assert_eq!(
             parse_line("def x := Prop"),
-            Ok(Define("x".to_string(), None, Prop))
+            Ok(Define("x".to_string(), None, Sort(0.into())))
         )
     }
 
@@ -289,13 +378,13 @@ mod tests {
     fn successful_checktype() {
         assert_eq!(
             parse_line("check Prop : Type"),
-            Ok(CheckType(Prop, Type(0.into())))
+            Ok(CheckType(Sort(0.into()), Sort(1.into())))
         )
     }
 
     #[test]
     fn successful_gettype_prop() {
-        assert_eq!(parse_line("check Prop"), Ok(GetType(Prop)))
+        assert_eq!(parse_line("check Prop"), Ok(GetType(Sort(0.into()))))
     }
 
     #[test]
@@ -306,15 +395,15 @@ mod tests {
         );
         assert_eq!(
             parse_line("check fun A:Prop => A"),
-            Ok(GetType(Abs(box Prop, box Var(1.into()))))
+            Ok(GetType(Abs(box Sort(0.into()), box Var(1.into()))))
         )
     }
 
     #[test]
     fn successful_type() {
-        assert_eq!(parse_line("check Type"), Ok(GetType(Type(0.into()))));
-        assert_eq!(parse_line("check Type 0"), Ok(GetType(Type(0.into()))));
-        assert_eq!(parse_line("check Type 1"), Ok(GetType(Type(1.into()))))
+        assert_eq!(parse_line("check Type"), Ok(GetType(Sort(1.into()))));
+        assert_eq!(parse_line("check Type 0"), Ok(GetType(Sort(1.into()))));
+        assert_eq!(parse_line("check Type 1"), Ok(GetType(Sort(2.into()))))
     }
 
     #[test]
@@ -408,10 +497,13 @@ mod tests {
         assert_eq!(
             parse_line("check fun w x : Prop, y z : Prop => x"),
             Ok(GetType(Abs(
-                box Prop,
+                box Sort(0.into()),
                 box Abs(
-                    box Prop,
-                    box Abs(box Prop, box Abs(box Prop, box Var(3.into())))
+                    box Sort(0.into()),
+                    box Abs(
+                        box Sort(0.into()),
+                        box Abs(box Sort(0.into()), box Var(3.into()))
+                    )
                 )
             )))
         )
@@ -440,7 +532,7 @@ mod tests {
         assert_eq!(
             parse_line("check fun x : Prop, x : x, x : x => x"),
             Ok(GetType(Abs(
-                box Prop,
+                box Sort(0.into()),
                 box Abs(
                     box Var(1.into()),
                     box Abs(box Var(1.into()), box Var(1.into()))
@@ -450,7 +542,7 @@ mod tests {
         assert_eq!(
             parse_line("check fun x : Prop, x x : x => x"),
             Ok(GetType(Abs(
-                box Prop,
+                box Sort(0.into()),
                 box Abs(
                     box Var(1.into()),
                     box Abs(box Var(1.into()), box Var(1.into()))
@@ -460,7 +552,7 @@ mod tests {
         assert_eq!(
             parse_line("check fun x : Prop, y z : x => z"),
             Ok(GetType(Abs(
-                box Prop,
+                box Sort(0.into()),
                 box Abs(
                     box Var(1.into()),
                     box Abs(box Var(2.into()), box Var(1.into()))
@@ -474,7 +566,7 @@ mod tests {
         assert_eq!(
             parse_line("check (x : Prop, x : x, x : x) -> x"),
             Ok(GetType(Prod(
-                box Prop,
+                box Sort(0.into()),
                 box Prod(
                     box Var(1.into()),
                     box Prod(box Var(1.into()), box Var(1.into()))
@@ -484,7 +576,7 @@ mod tests {
         assert_eq!(
             parse_line("check (x : Prop, x x : x) -> x"),
             Ok(GetType(Prod(
-                box Prop,
+                box Sort(0.into()),
                 box Prod(
                     box Var(1.into()),
                     box Prod(box Var(1.into()), box Var(1.into()))
@@ -494,7 +586,7 @@ mod tests {
         assert_eq!(
             parse_line("check (x : Prop, y z : x) -> z"),
             Ok(GetType(Prod(
-                box Prop,
+                box Sort(0.into()),
                 box Prod(
                     box Var(1.into()),
                     box Prod(box Var(2.into()), box Var(1.into()))
@@ -508,10 +600,13 @@ mod tests {
         assert_eq!(
             parse_line("check fun (((w x : Prop))), y z : Prop => x"),
             Ok(GetType(Abs(
-                box Prop,
+                box Sort(0.into()),
                 box Abs(
-                    box Prop,
-                    box Abs(box Prop, box Abs(box Prop, box Var(3.into())))
+                    box Sort(0.into()),
+                    box Abs(
+                        box Sort(0.into()),
+                        box Abs(box Sort(0.into()), box Var(3.into()))
+                    )
                 )
             )))
         )
@@ -595,7 +690,7 @@ mod tests {
     fn failed_parsers() {
         assert_eq!(
             parse_file(
-                "def x : Type := Prop -> Prop
+                "def x : Type := Prop-> Prop
                  // this is a comment
                         check .x"
             ),
diff --git a/parser/src/term.pest b/parser/src/term.pest
index 6d44b2f6..b1efb680 100644
--- a/parser/src/term.pest
+++ b/parser/src/term.pest
@@ -1,15 +1,15 @@
 WHITESPACE = _{ WHITE_SPACE }
 COMMENT = _{ "//" ~ (!"\n" ~ ANY)* }
 eoi = _{ !ANY }
-keywords = @{ ( "fun" | "def" | "check" | "Prop" | "Type" ) ~ !ASCII_ALPHANUMERIC }
+keywords = @{ ( "fun" | "def" | "check" | "Prop" | "Type" | "Sort" ) ~ !ASCII_ALPHANUMERIC }
 number = @{ ASCII_DIGIT+ }
 string = @{ !keywords ~ ASCII_ALPHA ~ ( "_" | ASCII_ALPHANUMERIC )* }
 
-Term = _{ Abs | dProd | Prod | App | Var | Prop | Type | "(" ~ Term ~ ")" }
-term_prod = _{ App | Abs | dProd | Var | Prop | Type | "(" ~ Prod ~ ")" | "(" ~ term_prod ~ ")" }
-term_app = _{ Abs | Var | Prop | Type | "(" ~ App ~ ")" | "(" ~ Prod ~ ")" | "(" ~ dProd ~ ")" | "(" ~ term_app ~ ")" } 
-term_dprod = _{ App | Abs | Prod | dProd | Var | Prop | Type | "(" ~ term_dprod ~ ")" }
-term_abs = _{ App | Abs | Prod | dProd | Var | Prop | Type | "(" ~ term_abs ~ ")" }
+Term = _{ Abs | dProd | Prod | App | Var | Prop | Type | Sort | "(" ~ Term ~ ")" }
+term_prod = _{ App | Abs | dProd | Var | Prop | Type | Sort | "(" ~ Prod ~ ")" | "(" ~ term_prod ~ ")" }
+term_app = _{ Abs | Var | Prop | Type | Sort | "(" ~ App ~ ")" | "(" ~ Prod ~ ")" | "(" ~ dProd ~ ")" | "(" ~ term_app ~ ")" } 
+term_dprod = _{ App | Abs | Prod | dProd | Var | Prop | Type | Sort | "(" ~ term_dprod ~ ")" }
+term_abs = _{ App | Abs | Prod | dProd | Var | Prop | Type | Sort | "(" ~ term_abs ~ ")" }
 
 Abs = { ( "fun" ~ ( arg_abs_par ~ ( "," ~ arg_abs_par )* ) ~ "=>" ~ Term ) }
 arg_abs_par = _{ arg_abs | "(" ~ arg_abs_par ~ ")" }
@@ -24,17 +24,22 @@ Prod = { term_prod ~ ( "->" ~ term_prod )+ }
 Prop = { "Prop" }
 
 Type = { "Type" ~ univ? }
-univ = _{ Plus | Max | Var | Num | "(" ~ univ ~ ")" }
-univ_plus = _{ Max | Var | Num | "(" ~ univ ~ ")" }
+Sort = { "Sort" ~ univ? }
+univ = _{ Plus | Max | Num | string | "(" ~ univ ~ ")" }
+univ_plus = _{ Max | Num | string | "(" ~ univ ~ ")" }
 Num = { number }
 Plus = { univ_plus ~ ( "+" ~ number )+ }
 Max = { ( "max" ~ "(" ~ univ ~ "," ~ univ ~ ")" ) | ( "max" ~ univ ~ univ ) }
-Var = { string }
+
+Var = { string ~ arg_univ? }
+arg_univ = {".{" ~ (univ ~ ("," ~ univ)*)? ~ "}"}
+
+univ_decl = {".{" ~ (string ~ ("," ~ string)* )? ~ "}"}
 
 Command = _{ Eval | Define | CheckType | GetType | DefineCheckType }
 Eval = { "eval" ~ Term }
-Define = { "def" ~ string ~ ":=" ~ Term }
-DefineCheckType = { "def" ~ string ~ ":" ~ Term ~ ":=" ~ Term }
+Define = { "def" ~ string ~ univ_decl? ~ ":=" ~ Term }
+DefineCheckType = { "def" ~ string ~ univ_decl? ~ ":" ~ Term ~ ":=" ~ Term }
 CheckType = { "check" ~ Term ~ ":" ~ Term }
 GetType = { "check" ~ Term }
 
-- 
GitLab