Tuples now seem to work.

I am concerned because this is going too well.
M fml/src/ast.rs +3 -0
@@ 88,6 88,9 @@ pub enum Expr {
         func: ExprNode,
         params: Vec<ExprNode>,
     },
+    TupleCtor {
+        body: Vec<ExprNode>,
+    },
 }
 
 impl Expr {

          
M fml/src/parser.rs +26 -12
@@ 291,7 291,7 @@ impl<'input> Parser<'input> {
     ///
     /// I ended up seeing a lot of `if self.peek_is(thing) { self.expect(thing)`
     /// so maybe this helps.
-    fn try_expect(&mut self, expected: Discr<TokenKind>) -> bool {
+    fn peek_expect(&mut self, expected: Discr<TokenKind>) -> bool {
         if self.peek_is(expected) {
             self.drop();
             true

          
@@ 400,7 400,7 @@ impl<'input> Parser<'input> {
             let tname = self.parse_type();
             args.push((name, tname));
 
-            if self.try_expect(T::Comma.discr()) {
+            if self.peek_expect(T::Comma.discr()) {
             } else {
                 break;
             }

          
@@ 427,7 427,7 @@ impl<'input> Parser<'input> {
             let tname = self.parse_type();
             args.push(tname);
 
-            if self.try_expect(T::Comma.discr()) {
+            if self.peek_expect(T::Comma.discr()) {
             } else {
                 break;
             }

          
@@ 436,28 436,26 @@ impl<'input> Parser<'input> {
         args
     }
 
-    /*
-    fn parse_tuple_type(&mut self) -> TypeInfo {
+    fn parse_tuple_type(&mut self) -> Type {
         let mut body = vec![];
         while !self.peek_is(T::RBrace.discr()) {
             let t = self.parse_type();
             body.push(t);
-            if self.try_expect(T::Comma.discr()) {
+            if self.peek_expect(T::Comma.discr()) {
             } else {
                 break;
             }
         }
         self.expect(T::RBrace);
-        TypeInfo::Tuple(body)
+        Type::Named("Tuple".to_string(), body)
     }
-    */
 
     fn parse_exprs(&mut self) -> Vec<ast::ExprNode> {
         let mut exprs = vec![];
         let tok = self.peek();
         while let Some(e) = self.parse_expr(0) {
             // Hey and if we have a semicolon after an expr we can just eat it
-            self.try_expect(T::Semicolon.discr());
+            self.peek_expect(T::Semicolon.discr());
             exprs.push(e);
         }
         if exprs.is_empty() {

          
@@ 505,6 503,8 @@ impl<'input> Parser<'input> {
                 self.expect(T::RParen);
                 lhs
             }
+            // Tuple literal
+            T::LBrace => self.parse_constructor(),
 
             // Something else not a valid expr
             _x => return None,

          
@@ 603,6 603,22 @@ impl<'input> Parser<'input> {
         ast::ExprNode::new(ast::Expr::Lambda { signature, body })
     }
 
+    /// tuple constructor = "{" [expr {"," expr} [","] "}"
+    fn parse_constructor(&mut self) -> ast::ExprNode {
+        self.expect(T::LBrace);
+        let mut body = vec![];
+        while let Some(expr) = self.parse_expr(0) {
+            body.push(expr);
+
+            if self.peek_expect(T::Comma.discr()) {
+            } else {
+                break;
+            }
+        }
+        self.expect(T::RBrace);
+        ast::ExprNode::new(ast::Expr::TupleCtor { body })
+    }
+
     fn parse_type(&mut self) -> Type {
         let t = self.next();
         match t {

          
@@ 621,14 637,12 @@ impl<'input> Parser<'input> {
                 let s = self.expect_ident();
                 Type::Generic(s)
             }
-            /*
             Some(Token {
                 kind: T::LBrace, ..
             }) => {
                 let tuptype = self.parse_tuple_type();
-                crate::INT.intern_type(&tuptype)
+                tuptype
             }
-            */
             Some(Token { kind: T::Fn, .. }) => {
                 let fntype = self.parse_fn_type();
                 fntype

          
M fml/src/typeck.rs +11 -0
@@ 335,6 335,17 @@ fn typecheck_expr(
             signature: _,
             body: _,
         } => todo!("idk mang"),
+        TupleCtor { body } => {
+            let body_types: Result<Vec<_>, _> = body
+                .iter()
+                .map(|expr| typecheck_expr(tck, symtbl, expr))
+                .collect();
+            let body_types = body_types?;
+            let tuple_type = TypeInfo::Named("Tuple".to_string(), body_types);
+            let typeid = tck.insert(tuple_type);
+            tck.set_expr_type(expr, typeid);
+            Ok(typeid)
+        }
         Funcall { func, params } => {
             // Oh, defined generics are "easy".
             // Each time I call a function I create new type

          
M fml/tests/main.rs +19 -0
@@ 25,3 25,22 @@ fn test_failure() {
     let src = include_str!("test_failure.gt");
     let _output = fml::compile("test_failure.gt", src);
 }
+
+#[test]
+fn test_tuple1() {
+    let src = include_str!("test_tuple1.gt");
+    let _output = fml::compile("test_tuple1.gt", src);
+}
+
+#[test]
+fn test_tuple2() {
+    let src = include_str!("test_tuple2.gt");
+    let _output = fml::compile("test_tuple2.gt", src);
+}
+
+#[test]
+#[should_panic]
+fn test_tuple3() {
+    let src = include_str!("test_tuple3.gt");
+    let _output = fml::compile("test_tuple3.gt", src);
+}

          
A => fml/tests/test_tuple1.gt +5 -0
@@ 0,0 1,5 @@ 
+
+fn main(): I32 =
+    let a: {I32, Bool} = {3, false}
+    3
+end

          
A => fml/tests/test_tuple2.gt +10 -0
@@ 0,0 1,10 @@ 
+
+fn identity(i: @T): @T =
+    let y: @T = i
+    y
+end
+
+fn main(): I32 =
+    let a: {I32, Bool, I32} = identity({1, false, 3})
+    3
+end

          
A => fml/tests/test_tuple3.gt +10 -0
@@ 0,0 1,10 @@ 
+
+fn identity(i: @T): @T =
+    let y: @T = i
+    y
+end
+
+fn main(): I32 =
+    let a: {I32, Bool, I32} = identity({1, 2, 3})
+    3
+end