AST and parser for structs

The syntax is pretty lame but here we are
5 files changed, 82 insertions(+), 14 deletions(-)

M fml/src/ast.rs
M fml/src/lib.rs
M fml/src/parser.rs
M fml/src/typeck.rs
M fml/tests/main.rs
M fml/src/ast.rs +0 -5
@@ 119,11 119,6 @@ pub enum Decl {
         signature: Signature,
         body: Vec<ExprNode>,
     },
-
-    Struct {
-        name: String,
-        tys: HashMap<String, Type>,
-    },
     TypeDef {
         name: String,
         ty: Type,

          
M fml/src/lib.rs +6 -1
@@ 1,5 1,7 @@ 
 //! Garnet compiler guts.
 
+use std::collections::HashMap;
+
 pub mod ast;
 pub mod parser;
 pub mod typeck;

          
@@ 9,6 11,7 @@ pub mod typeck;
 pub enum Type {
     Named(String, Vec<Type>),
     Func(Vec<Type>, Box<Type>),
+    Struct(HashMap<String, Type>),
     /// A generic type parameter
     Generic(String),
 }

          
@@ 29,7 32,7 @@ impl Type {
 pub struct TypeId(usize);
 
 /// Information about a type term
-#[derive(Clone, Debug, Hash, PartialEq, Eq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
 pub enum TypeInfo {
     /// No information about the type of this type term
     Unknown,

          
@@ 42,6 45,8 @@ pub enum TypeInfo {
     Named(String, Vec<TypeId>),
     /// This type term is definitely a function
     Func(Vec<TypeId>, TypeId),
+    /// This is definitely some kind of struct
+    Struct(HashMap<String, TypeId>),
     /// This is some generic type that has a name like @A
     /// AKA a type parameter.
     TypeParam(String),

          
M fml/src/parser.rs +60 -7
@@ 58,6 58,8 @@ pub enum TokenKind {
     Let,
     #[token("end")]
     End,
+    #[token("struct")]
+    Struct,
 
     // Punctuation
     #[token("(")]

          
@@ 497,14 499,8 @@ impl<'input> Parser<'input> {
             T::Ident(_) => {
                 let ident = self.expect_ident();
                 ast::ExprNode::new(ast::Expr::Var { name: ident })
-                /*
-                if self.peek_is(TokenKind::LBrace.discr()) {
-                    self.parse_struct_literal(ident)
-                } else {
-                    ast::Expr::Var { name: ident }
-                }
-                */
             }
+            T::Struct => self.parse_struct_literal(),
             T::Let => self.parse_let(),
             T::Fn => self.parse_lambda(),
             // Parenthesized expr's

          
@@ 640,6 636,37 @@ impl<'input> Parser<'input> {
         ast::ExprNode::new(ast::Expr::TupleCtor { body })
     }
 
+    /// struct literal = "struct" "{" ... "}"
+    fn parse_struct_literal(&mut self) -> ast::ExprNode {
+        self.expect(T::Struct);
+        self.expect(T::LBrace);
+        let body = self.parse_struct_lit_fields();
+        self.expect(T::RBrace);
+        ast::ExprNode::new(ast::Expr::StructCtor { body })
+    }
+
+    fn parse_struct_lit_fields(&mut self) -> HashMap<String, ast::ExprNode> {
+        let mut fields = HashMap::new();
+
+        loop {
+            match self.lex.peek() {
+                Some((T::Ident(_i), _span)) => {
+                    let name = self.expect_ident();
+                    self.expect(T::Equals);
+                    let vl = self.parse_expr(0).unwrap();
+                    fields.insert(name, vl);
+                }
+                _ => break,
+            }
+
+            if self.peek_expect(T::Comma.discr()) {
+            } else {
+                break;
+            }
+        }
+        fields
+    }
+
     fn parse_type(&mut self) -> Type {
         let t = self.next();
         match t {

          
@@ 667,9 694,35 @@ impl<'input> Parser<'input> {
                 let fntype = self.parse_fn_type();
                 fntype
             }
+            Some(Token {
+                kind: T::Struct, ..
+            }) => self.parse_struct_type(),
             other => self.error("type", other),
         }
     }
+
+    fn parse_struct_type(&mut self) -> Type {
+        let mut fields = HashMap::new();
+
+        loop {
+            match self.lex.peek() {
+                Some((T::Ident(_i), _span)) => {
+                    let name = self.expect_ident();
+                    self.expect(T::Colon);
+                    let vl = self.parse_type();
+                    fields.insert(name, vl);
+                }
+                _ => break,
+            }
+
+            if self.peek_expect(T::Comma.discr()) {
+            } else {
+                break;
+            }
+        }
+        self.expect(T::End);
+        Type::Struct(fields)
+    }
 }
 
 /// Specifies binding power of postfix operators.

          
M fml/src/typeck.rs +10 -1
@@ 45,6 45,7 @@ impl Tck {
                 b == rettype || args.iter().any(|arg| self._contains_type(*arg, b))
             }
             TypeParam(_) => false,
+            Struct(_) => todo!(),
         }
     }
 

          
@@ 72,6 73,13 @@ impl Tck {
                 TypeInfo::Func(new_args, new_rettype)
             }
             Type::Generic(s) => TypeInfo::TypeParam(s.to_string()),
+            Type::Struct(body) => {
+                let new_body = body
+                    .iter()
+                    .map(|(nm, t)| (nm.clone(), self.insert_known(t)))
+                    .collect();
+                TypeInfo::Struct(new_body)
+            }
         };
         self.insert(tinfo)
     }

          
@@ 146,6 154,7 @@ impl Tck {
                 ))
             }
             TypeParam(name) => Ok(Type::Generic(name.to_owned())),
+            Struct(_body) => todo!(),
         }
     }
 

          
@@ 203,6 212,7 @@ impl Tck {
                 let inst_ret = self.instantiate(named_types, rettype);
                 TypeInfo::Func(inst_args, inst_ret)
             }
+            Type::Struct(_body) => todo!(),
         };
         self.insert(typeinfo)
     }

          
@@ 490,7 500,6 @@ pub fn typecheck(ast: &ast::Ast) {
                     panic!("Error while typechecking function {}:\n{:?}", name, e)
                 });
             }
-            Struct { name: _, tys: _ } => todo!("struct decl"),
             TypeDef { name, ty } => symtbl.add_type(name, ty),
         }
     }

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