From bea0783a17952521fbd7b27cbf2253d0ed5b780f Mon Sep 17 00:00:00 2001
From: Avery <avery@gitea.itycodes.org>
Date: Sat, 22 Mar 2025 14:12:06 +0100
Subject: [PATCH] Grammar updates

---
 .gitignore            |  1 +
 grammar.ebnf          | 32 +++++++++++++++++++++++
 src/inference/test.rs |  4 +--
 src/parse/test.rs     | 60 +++++++++++++++++++++++++++++++++++++++++++
 src/parse/tokenize.rs |  3 +++
 src/parser.lalrpop    | 14 ++++++++--
 6 files changed, 110 insertions(+), 4 deletions(-)
 create mode 100644 grammar.ebnf

diff --git a/.gitignore b/.gitignore
index ea8c4bf..97311d8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
 /target
+grammar.html
diff --git a/grammar.ebnf b/grammar.ebnf
new file mode 100644
index 0000000..3e16d79
--- /dev/null
+++ b/grammar.ebnf
@@ -0,0 +1,32 @@
+(*
+
+
+
+
+
+
+
+
+ *)
+ast				=	  term 
+					| "\", {ident, [":", tagged type], ","}, ident, [":", tagged type], ".", ast ;
+
+term			=	  ident 
+					| constant 
+					| "(", ast, ")" 
+					| term, term ;
+
+constant		=	nat | float | bool ;
+
+tagged type		=	  type tag, ident 
+					| type tag, ident, "=>", tagged type 
+					| type ;
+
+type tag		=	"Num" | "Any" ;
+
+type			=	basic type | arrow type ;
+
+basic type		=	"Nat" | "Float" | "Bool" | ident ;
+
+arrow type		=	  "(", type, ")", "->", type
+					| basic type, "->", type ;
diff --git a/src/inference/test.rs b/src/inference/test.rs
index a96670a..5ab9260 100644
--- a/src/inference/test.rs
+++ b/src/inference/test.rs
@@ -172,8 +172,8 @@ fn infer_id_uni() {
         typ,
         TaggedType::Tagged(
             TypeTag::Any,
-            "?typ_1".to_string(),
-            Box::new(TaggedType::Concrete(Type::arrow("?typ_1", "?typ_1")))
+            "?typ_0".to_string(),
+            Box::new(TaggedType::Concrete(Type::arrow("?typ_0", "?typ_0")))
         )
     );
 }
diff --git a/src/parse/test.rs b/src/parse/test.rs
index 3b0bf9e..ae647b7 100644
--- a/src/parse/test.rs
+++ b/src/parse/test.rs
@@ -132,6 +132,66 @@ fn parse_abstraction() {
             ))
         )
     );
+
+    let ast = parser.parse(Lexer::new(r"\x:Any a.\y:Any b.x")).unwrap();
+    assert_eq!(
+        ast,
+        Ast::Abstraction(
+            "x".to_string(),
+            Some(TaggedType::Tagged(
+                TypeTag::Any,
+                "a".to_string(),
+                Box::new(TaggedType::Concrete("a".into()))
+            )),
+            Box::new(Ast::Abstraction(
+                "y".to_string(),
+                Some(TaggedType::Tagged(
+                    TypeTag::Any,
+                    "b".to_string(),
+                    Box::new(TaggedType::Concrete("b".into()))
+                )),
+                Box::new(Ast::Variable("x".to_string()))
+            ))
+        )
+    );
+
+    let ast = parser
+        .parse(Lexer::new(r"\x:Any a => a,y:Any b => b.x"))
+        .unwrap();
+    assert_eq!(
+        ast,
+        Ast::Abstraction(
+            "x".to_string(),
+            Some(TaggedType::Tagged(
+                TypeTag::Any,
+                "a".to_string(),
+                Box::new(TaggedType::Concrete("a".into()))
+            )),
+            Box::new(Ast::Abstraction(
+                "y".to_string(),
+                Some(TaggedType::Tagged(
+                    TypeTag::Any,
+                    "b".to_string(),
+                    Box::new(TaggedType::Concrete("b".into()))
+                )),
+                Box::new(Ast::Variable("x".to_string()))
+            ))
+        )
+    );
+
+    let ast = parser.parse(Lexer::new(r"\x,y.x")).unwrap();
+    assert_eq!(
+        ast,
+        Ast::Abstraction(
+            "x".to_string(),
+            None,
+            Box::new(Ast::Abstraction(
+                "y".to_string(),
+                None,
+                Box::new(Ast::Variable("x".to_string()))
+            ))
+        )
+    );
 }
 
 #[test]
diff --git a/src/parse/tokenize.rs b/src/parse/tokenize.rs
index 1c34b28..9e4dd41 100644
--- a/src/parse/tokenize.rs
+++ b/src/parse/tokenize.rs
@@ -51,6 +51,8 @@ pub enum Token {
     Colon,
     #[token(".")]
     Period,
+    #[token(",")]
+    Comma,
 
     #[token("=")]
     Equals,
@@ -117,6 +119,7 @@ impl Display for Token {
             Token::ThinArrow => write!(f, "->"),
             Token::Colon => write!(f, ":"),
             Token::Period => write!(f, "."),
+            Token::Comma => write!(f, ","),
             Token::Equals => write!(f, "="),
             Token::NotEquals => write!(f, "<>"),
             Token::Plus => write!(f, "+"),
diff --git a/src/parser.lalrpop b/src/parser.lalrpop
index d3f370b..b443dee 100644
--- a/src/parser.lalrpop
+++ b/src/parser.lalrpop
@@ -22,6 +22,7 @@ extern {
 		"->" => lexer::Token::ThinArrow,
 		":" => lexer::Token::Colon,
 		"." => lexer::Token::Period,
+		"," => lexer::Token::Comma,
 
 		"=" => lexer::Token::Equals,
 		"<>" => lexer::Token::NotEquals,
@@ -43,12 +44,20 @@ extern {
 
 }
 
+Multi<T>: Vec<T> = { // (1)
+    <mut v:(<T> ",")*> <e:T> => {
+		v.push(e);
+		v
+    }
+};
+
 pub Ast: Ast = {
 	Term => <>,
-	r"\" <x:Ident> ":" <t:TaggedType> "." <ast:Ast> => Ast::Abstraction(x, Some(t), Box::new(ast)),
-	r"\" <x:Ident> "." <ast:Ast> => Ast::Abstraction(x, None, Box::new(ast)),
+	r"\" <args:Multi<IdentAndType>> "." <ast:Ast> => args.into_iter().rev().fold(ast, |ast, (i, t)| Ast::Abstraction(i, t, Box::new(ast))),
 };
 
+pub IdentAndType: (String, Option<TaggedType>) = <x:Ident> <t:(":" <TaggedType>)?> => (<>);
+
 
 Term: Ast = {
 	#[precedence(level="0")]
@@ -68,6 +77,7 @@ Constant: Constant = {
 };
 
 pub TaggedType: TaggedType = {
+	<t:TypeTag> <i:Ident> => TaggedType::Tagged(t, i.clone(), Box::new(TaggedType::Concrete(Type::Variable(i)))),
 	<t:TypeTag> <i:Ident> "=>" <tt:TaggedType> => TaggedType::Tagged(t, i, Box::new(tt)),
 	<t:Type> => TaggedType::Concrete(t),
 };