about summary refs log tree commit diff
diff options
context:
space:
mode:
authorGriffin Smith <root@gws.fyi>2021-03-14T21·01-0400
committerGriffin Smith <root@gws.fyi>2021-03-14T21·01-0400
commitb93268085aab14c80a400c299da5d04d2781098e (patch)
tree99e9eefc175a36f3949404daf6c2fe4a49d1e1c1
parentecb4c0f803e9b408e4fd21c475769eb4dc649d14 (diff)
Implement top-level ascription of declarations
-rw-r--r--ach/Makefile2
-rw-r--r--src/ast/mod.rs1
-rw-r--r--src/parser/mod.rs30
-rw-r--r--src/tc/mod.rs42
-rw-r--r--tests/compile.rs13
5 files changed, 73 insertions, 15 deletions
diff --git a/ach/Makefile b/ach/Makefile
index 869a0d0f8a..3a8cd2865e 100644
--- a/ach/Makefile
+++ b/ach/Makefile
@@ -12,4 +12,4 @@ default: simple
 .PHONY: clean
 
 clean:
-	@rm -f *.ll *.o simple
+	@rm -f *.ll *.o simple functions
diff --git a/src/ast/mod.rs b/src/ast/mod.rs
index 1884ba69f4..3a2261aeda 100644
--- a/src/ast/mod.rs
+++ b/src/ast/mod.rs
@@ -266,6 +266,7 @@ impl<'a> Fun<'a> {
 #[derive(Debug, PartialEq, Eq, Clone)]
 pub enum Decl<'a> {
     Fun { name: Ident<'a>, body: Fun<'a> },
+    Ascription { name: Ident<'a>, type_: Type<'a> },
 }
 
 ////
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index 8599ccabfc..dd7874aff8 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -98,7 +98,20 @@ named!(fun_decl(&str) -> Decl, do_parse!(
         })
 ));
 
+named!(ascription_decl(&str) -> Decl, do_parse!(
+    name: ident
+        >> multispace0
+        >> complete!(char!(':'))
+        >> multispace0
+        >> type_: type_
+        >> (Decl::Ascription {
+            name,
+            type_
+        })
+));
+
 named!(pub decl(&str) -> Decl, alt!(
+    ascription_decl |
     fun_decl
 ));
 
@@ -108,7 +121,7 @@ named!(pub toplevel(&str) -> Vec<Decl>, terminated!(many0!(decl), multispace0));
 mod tests {
     use std::convert::TryInto;
 
-    use crate::ast::{BinaryOperator, Expr, Literal, Type};
+    use crate::ast::{BinaryOperator, Expr, FunctionType, Literal, Type};
 
     use super::*;
     use expr::tests::ident_expr;
@@ -166,4 +179,19 @@ mod tests {
         );
         assert_eq!(res.len(), 3);
     }
+
+    #[test]
+    fn top_level_ascription() {
+        let res = test_parse!(toplevel, "id : fn a -> a");
+        assert_eq!(
+            res,
+            vec![Decl::Ascription {
+                name: "id".try_into().unwrap(),
+                type_: Type::Function(FunctionType {
+                    args: vec![Type::Var("a".try_into().unwrap())],
+                    ret: Box::new(Type::Var("a".try_into().unwrap()))
+                })
+            }]
+        )
+    }
 }
diff --git a/src/tc/mod.rs b/src/tc/mod.rs
index 4c088c8857..559ac993cc 100644
--- a/src/tc/mod.rs
+++ b/src/tc/mod.rs
@@ -305,22 +305,38 @@ impl<'ast> Typechecker<'ast> {
         }
     }
 
-    pub(crate) fn tc_decl(&mut self, decl: ast::Decl<'ast>) -> Result<hir::Decl<'ast, Type>> {
+    pub(crate) fn tc_decl(
+        &mut self,
+        decl: ast::Decl<'ast>,
+    ) -> Result<Option<hir::Decl<'ast, Type>>> {
         match decl {
             ast::Decl::Fun { name, body } => {
-                let body = self.tc_expr(ast::Expr::Fun(Box::new(body)))?;
+                let mut expr = ast::Expr::Fun(Box::new(body));
+                if let Some(type_) = self.env.resolve(&name) {
+                    expr = ast::Expr::Ascription {
+                        expr: Box::new(expr),
+                        type_: self.finalize_type(type_.clone())?,
+                    };
+                }
+
+                let body = self.tc_expr(expr)?;
                 let type_ = body.type_().clone();
                 self.env.set(name.clone(), type_);
                 match body {
-                    hir::Expr::Fun { args, body, type_ } => Ok(hir::Decl::Fun {
+                    hir::Expr::Fun { args, body, type_ } => Ok(Some(hir::Decl::Fun {
                         name,
                         args,
                         body,
                         type_,
-                    }),
+                    })),
                     _ => unreachable!(),
                 }
             }
+            ast::Decl::Ascription { name, type_ } => {
+                let type_ = self.type_from_ast_type(type_);
+                self.env.set(name.clone(), type_);
+                Ok(None)
+            }
         }
     }
 
@@ -561,15 +577,15 @@ pub fn typecheck_expr(expr: ast::Expr) -> Result<hir::Expr<ast::Type>> {
 
 pub fn typecheck_toplevel(decls: Vec<ast::Decl>) -> Result<Vec<hir::Decl<ast::Type>>> {
     let mut typechecker = Typechecker::new();
-    decls
-        .into_iter()
-        .map(|decl| {
-            let hir_decl = typechecker.tc_decl(decl)?;
-            let res = typechecker.finalize_decl(hir_decl)?;
-            typechecker.ctx.clear();
-            Ok(res)
-        })
-        .try_collect()
+    let mut res = Vec::with_capacity(decls.len());
+    for decl in decls {
+        if let Some(hir_decl) = typechecker.tc_decl(decl)? {
+            let hir_decl = typechecker.finalize_decl(hir_decl)?;
+            res.push(hir_decl);
+        }
+        typechecker.ctx.clear();
+    }
+    Ok(res)
 }
 
 #[cfg(test)]
diff --git a/tests/compile.rs b/tests/compile.rs
index 177391423c..7fa15ad965 100644
--- a/tests/compile.rs
+++ b/tests/compile.rs
@@ -8,6 +8,19 @@ const FIXTURES: &[(&str, i32)] = &[("simple", 5), ("functions", 9)];
 fn compile_and_run_files() {
     let ach = root().unwrap().join("ach");
 
+    println!("Running: `make clean`");
+    assert!(
+        Command::new("make")
+            .arg("clean")
+            .current_dir(&ach)
+            .spawn()
+            .unwrap()
+            .wait()
+            .unwrap()
+            .success(),
+        "make clean failed"
+    );
+
     for (fixture, exit_code) in FIXTURES {
         println!(">>> Testing: {}", fixture);