I guess we can make nominal types now
4 files changed, 89 insertions(+), 7 deletions(-)

M fml/src/ast.rs
M fml/src/parser.rs
M fml/src/typeck.rs
M fml/tests/main.rs
M fml/src/ast.rs +8 -0
@@ 95,6 95,10 @@ pub enum Expr {
     StructCtor {
         body: HashMap<String, ExprNode>,
     },
+    TypeCtor {
+        name: String,
+        body: ExprNode,
+    },
 }
 
 impl Expr {

          
@@ 120,6 124,10 @@ pub enum Decl {
         name: String,
         tys: HashMap<String, Type>,
     },
+    TypeDef {
+        name: String,
+        ty: Type,
+    },
 }
 
 /// A compilable chunk of AST.

          
M fml/src/parser.rs +24 -2
@@ 50,6 50,8 @@ pub enum TokenKind {
     // Decl stuff
     #[token("fn")]
     Fn,
+    #[token("type")]
+    Type,
 
     // Keywords
     #[token("let")]

          
@@ 82,6 84,8 @@ pub enum TokenKind {
     Equals,
     #[token("@")]
     At,
+    #[token("$")]
+    Dollar,
 
     // We save comment strings so we can use this same
     // parser as a reformatter or such.

          
@@ 360,11 364,20 @@ impl<'input> Parser<'input> {
     fn parse_decl(&mut self) -> Option<ast::Decl> {
         match self.next() {
             Some(Token { kind: T::Fn, .. }) => Some(self.parse_fn()),
+            Some(Token { kind: T::Type, .. }) => Some(self.parse_typedef()),
             Some(other) => self.error("start of decl", Some(other)),
             None => None,
         }
     }
 
+    /// typedef = "type" ident "=" type
+    fn parse_typedef(&mut self) -> ast::Decl {
+        let name = self.expect_ident();
+        self.expect(T::Equals);
+        let ty = self.parse_type();
+        ast::Decl::TypeDef { name, ty }
+    }
+
     fn parse_fn(&mut self) -> ast::Decl {
         let name = self.expect_ident();
         let signature = ast::Signature {

          
@@ 503,6 516,16 @@ impl<'input> Parser<'input> {
             }
             // Tuple literal
             T::LBrace => self.parse_constructor(),
+            // Nominal type literal
+            // $Foo(3)
+            T::Dollar => {
+                self.drop();
+                let name = self.expect_ident();
+                self.expect(T::LParen);
+                let body = self.parse_expr(0)?;
+                self.expect(T::RParen);
+                ast::ExprNode::new(ast::Expr::TypeCtor { name, body })
+            }
 
             // Something else not a valid expr
             _x => return None,

          
@@ 627,8 650,7 @@ impl<'input> Parser<'input> {
                 if let Some(t) = Type::get_primitive_type(s) {
                     t.clone()
                 } else {
-                    //crate::INT.named_type(s)
-                    self.error("Unknown type", t.clone());
+                    Type::Named(s.clone(), vec![])
                 }
             }
             Some(Token { kind: T::At, .. }) => {

          
M fml/src/typeck.rs +51 -5
@@ 149,6 149,14 @@ impl Tck {
         }
     }
 
+    fn print_types(&self) {
+        let mut vars_report: Vec<_> = self.vars.iter().collect();
+        vars_report.sort_by(|(k1, _), (k2, _)| k1.cmp(k2));
+        for (k, v) in vars_report.iter() {
+            print!("  ${} => {:?}\n", k.0, v);
+        }
+    }
+
     /// Kinda the opposite of reconstruction; takes a concrete type
     /// and generates a new type with unknown's (type variables) for the generic types (type
     /// parameters)

          
@@ 206,6 214,7 @@ impl Tck {
 #[derive(Clone)]
 struct Symtbl {
     symbols: Rc<RefCell<Vec<HashMap<String, TypeId>>>>,
+    types: Rc<RefCell<Vec<HashMap<String, Type>>>>,
 }
 
 impl Default for Symtbl {

          
@@ 213,6 222,7 @@ impl Default for Symtbl {
     fn default() -> Self {
         Self {
             symbols: Rc::new(RefCell::new(vec![HashMap::new()])),
+            types: Rc::new(RefCell::new(vec![HashMap::new()])),
         }
     }
 }

          
@@ 228,12 238,18 @@ impl Drop for ScopeGuard {
             .borrow_mut()
             .pop()
             .expect("Scope stack underflow");
+        self.scope
+            .types
+            .borrow_mut()
+            .pop()
+            .expect("Scope stack underflow");
     }
 }
 
 impl Symtbl {
     fn push_scope(&self) -> ScopeGuard {
         self.symbols.borrow_mut().push(HashMap::new());
+        self.types.borrow_mut().push(HashMap::new());
         ScopeGuard {
             scope: self.clone(),
         }

          
@@ 257,6 273,24 @@ impl Symtbl {
         }
         return None;
     }
+
+    fn add_type(&self, name: impl AsRef<str>, ty: &Type) {
+        self.types
+            .borrow_mut()
+            .last_mut()
+            .expect("Scope stack underflow")
+            .insert(name.as_ref().to_owned(), ty.to_owned());
+    }
+
+    fn get_type(&self, ty: impl AsRef<str>) -> Option<Type> {
+        for scope in self.types.borrow().iter().rev() {
+            let v = scope.get(ty.as_ref());
+            if v.is_some() {
+                return v.cloned();
+            }
+        }
+        return None;
+    }
 }
 
 fn infer_lit(lit: &ast::Literal) -> TypeInfo {

          
@@ 309,11 343,7 @@ fn typecheck_func_body(
         "Typechecked function {}, types are",
         name.unwrap_or("(lambda)")
     );
-    let mut vars_report: Vec<_> = tck.vars.iter().collect();
-    vars_report.sort_by(|(k1, _), (k2, _)| k1.cmp(k2));
-    for (k, v) in vars_report.iter() {
-        print!("  ${} => {:?}\n", k.0, v);
-    }
+    tck.print_types();
     Ok(f)
 }
 

          
@@ 420,6 450,20 @@ fn typecheck_expr(
         }
 
         StructCtor { body: _ } => todo!("Struct ctor"),
+        TypeCtor { name, body } => {
+            let named_type = symtbl.get_type(name).expect("Unknown type constructor");
+            println!("Got type named {}: is {:?}", name, named_type);
+            let tid = tck.insert_known(&named_type);
+            let body_type = typecheck_expr(tck, symtbl, body)?;
+            println!("Expected type is {:?}, body type is {:?}", tid, body_type);
+            tck.unify(symtbl, tid, body_type)?;
+            println!("Done unifying type ctor");
+            // The type the expression returns
+            // TODO: Generic params for type constructors
+            let constructed_type = tck.insert_known(&Type::Named(name.clone(), vec![]));
+            tck.set_expr_type(expr, constructed_type);
+            Ok(tid)
+        }
     }
 }
 

          
@@ 442,10 486,12 @@ pub fn typecheck(ast: &ast::Ast) {
             } => {
                 let t = typecheck_func_body(Some(name), tck, symtbl, signature, body);
                 t.unwrap_or_else(|e| {
+                    tck.print_types();
                     panic!("Error while typechecking function {}:\n{:?}", name, e)
                 });
             }
             Struct { name: _, tys: _ } => todo!("struct decl"),
+            TypeDef { name, ty } => symtbl.add_type(name, ty),
         }
     }
     // Print out toplevel symbols

          
M fml/tests/main.rs +6 -0
@@ 75,3 75,9 @@ fn test_lambda5() {
     let src = include_str!("test_lambda5.gt");
     let _output = fml::compile("test_lambda5.gt", src);
 }
+
+#[test]
+fn test_typedef1() {
+    let src = include_str!("test_typedef1.gt");
+    let _output = fml::compile("test_typedef1.gt", src);
+}