about summary refs log tree commit diff
path: root/users/glittershark/achilles
diff options
context:
space:
mode:
authorGriffin Smith <grfn@gws.fyi>2021-03-20T00·46-0400
committerglittershark <grfn@gws.fyi>2021-03-20T20·20+0000
commit2c838ab845bc54c5ef6cb0561332c84f34249368 (patch)
treec54c95ebc7f503a1db255566b3e25494bbaa28b3 /users/glittershark/achilles
parentfec6595d211e7e3ea616d8335fe5d143a4a7507d (diff)
feat(gs/achilles): Implement extern decls, for glibc functions r/2293
Implement extern decls, which codegen to LLVM as forward-declared
functions, and use these as a hook into calling glibc functions.

We can print to the terminal now! The integration tests can test this
now.

Change-Id: I70af4546b417b888ad9fbb18798db240f77f4e71
Reviewed-on: https://cl.tvl.fyi/c/depot/+/2614
Tested-by: BuildkiteCI
Reviewed-by: glittershark <grfn@gws.fyi>
Diffstat (limited to 'users/glittershark/achilles')
-rw-r--r--users/glittershark/achilles/ach/.gitignore1
-rw-r--r--users/glittershark/achilles/ach/externs.ach5
-rw-r--r--users/glittershark/achilles/src/ast/hir.rs20
-rw-r--r--users/glittershark/achilles/src/ast/mod.rs14
-rw-r--r--users/glittershark/achilles/src/codegen/llvm.rs40
-rw-r--r--users/glittershark/achilles/src/parser/mod.rs21
-rw-r--r--users/glittershark/achilles/src/parser/type_.rs8
-rw-r--r--users/glittershark/achilles/src/tc/mod.rs13
-rw-r--r--users/glittershark/achilles/tests/compile.rs53
9 files changed, 147 insertions, 28 deletions
diff --git a/users/glittershark/achilles/ach/.gitignore b/users/glittershark/achilles/ach/.gitignore
index 683a53a01f6c..07b37e168979 100644
--- a/users/glittershark/achilles/ach/.gitignore
+++ b/users/glittershark/achilles/ach/.gitignore
@@ -3,3 +3,4 @@
 
 functions
 simple
+externs
diff --git a/users/glittershark/achilles/ach/externs.ach b/users/glittershark/achilles/ach/externs.ach
new file mode 100644
index 000000000000..faf8ce90e353
--- /dev/null
+++ b/users/glittershark/achilles/ach/externs.ach
@@ -0,0 +1,5 @@
+extern puts : fn cstring -> int
+
+fn main =
+    let _ = puts "foobar"
+    in 0
diff --git a/users/glittershark/achilles/src/ast/hir.rs b/users/glittershark/achilles/src/ast/hir.rs
index 6859174a2dd0..0212b3dbcdbb 100644
--- a/users/glittershark/achilles/src/ast/hir.rs
+++ b/users/glittershark/achilles/src/ast/hir.rs
@@ -219,12 +219,19 @@ pub enum Decl<'a, T> {
         body: Box<Expr<'a, T>>,
         type_: T,
     },
+
+    Extern {
+        name: Ident<'a>,
+        arg_types: Vec<T>,
+        ret_type: T,
+    },
 }
 
 impl<'a, T> Decl<'a, T> {
-    pub fn type_(&self) -> &T {
+    pub fn type_(&self) -> Option<&T> {
         match self {
-            Decl::Fun { type_, .. } => type_,
+            Decl::Fun { type_, .. } => Some(type_),
+            Decl::Extern { .. } => None,
         }
     }
 
@@ -247,6 +254,15 @@ impl<'a, T> Decl<'a, T> {
                 body: Box::new(body.traverse_type(f.clone())?),
                 type_: f(type_)?,
             }),
+            Decl::Extern {
+                name,
+                arg_types,
+                ret_type,
+            } => Ok(Decl::Extern {
+                name,
+                arg_types: arg_types.into_iter().map(f.clone()).try_collect()?,
+                ret_type: f(ret_type)?,
+            }),
         }
     }
 }
diff --git a/users/glittershark/achilles/src/ast/mod.rs b/users/glittershark/achilles/src/ast/mod.rs
index 3a2261aeda23..22d16c93645c 100644
--- a/users/glittershark/achilles/src/ast/mod.rs
+++ b/users/glittershark/achilles/src/ast/mod.rs
@@ -265,8 +265,18 @@ 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> },
+    Fun {
+        name: Ident<'a>,
+        body: Fun<'a>,
+    },
+    Ascription {
+        name: Ident<'a>,
+        type_: Type<'a>,
+    },
+    Extern {
+        name: Ident<'a>,
+        type_: FunctionType<'a>,
+    },
 }
 
 ////
diff --git a/users/glittershark/achilles/src/codegen/llvm.rs b/users/glittershark/achilles/src/codegen/llvm.rs
index 9b580d3c4536..f49e084a8174 100644
--- a/users/glittershark/achilles/src/codegen/llvm.rs
+++ b/users/glittershark/achilles/src/codegen/llvm.rs
@@ -9,7 +9,7 @@ use inkwell::module::Module;
 use inkwell::support::LLVMString;
 use inkwell::types::{BasicType, BasicTypeEnum, FunctionType, IntType};
 use inkwell::values::{AnyValueEnum, BasicValueEnum, FunctionValue};
-use inkwell::IntPredicate;
+use inkwell::{AddressSpace, IntPredicate};
 use thiserror::Error;
 
 use crate::ast::hir::{Binding, Decl, Expr};
@@ -249,6 +249,26 @@ impl<'ctx, 'ast> Codegen<'ctx, 'ast> {
         Ok(self.finish_function(&res))
     }
 
+    pub fn codegen_extern(
+        &mut self,
+        name: &str,
+        args: &'ast [Type],
+        ret: &'ast Type,
+    ) -> Result<()> {
+        self.module.add_function(
+            name,
+            self.codegen_type(ret).fn_type(
+                &args
+                    .iter()
+                    .map(|t| self.codegen_type(t))
+                    .collect::<Vec<_>>(),
+                false,
+            ),
+            None,
+        );
+        Ok(())
+    }
+
     pub fn codegen_decl(&mut self, decl: &'ast Decl<'ast, Type>) -> Result<()> {
         match decl {
             Decl::Fun {
@@ -257,6 +277,11 @@ impl<'ctx, 'ast> Codegen<'ctx, 'ast> {
                 self.codegen_function(name.into(), args, body)?;
                 Ok(())
             }
+            Decl::Extern {
+                name,
+                arg_types,
+                ret_type,
+            } => self.codegen_extern(name.into(), arg_types, ret_type),
         }
     }
 
@@ -274,7 +299,18 @@ impl<'ctx, 'ast> Codegen<'ctx, 'ast> {
 
     fn codegen_type(&self, type_: &'ast Type) -> BasicTypeEnum<'ctx> {
         // TODO
-        self.context.i64_type().into()
+        match type_ {
+            Type::Int => self.context.i64_type().into(),
+            Type::Float => self.context.f64_type().into(),
+            Type::Bool => self.context.bool_type().into(),
+            Type::CString => self
+                .context
+                .i8_type()
+                .ptr_type(AddressSpace::Generic)
+                .into(),
+            Type::Function(_) => todo!(),
+            Type::Var(_) => unreachable!(),
+        }
     }
 
     fn codegen_int_type(&self, type_: &'ast Type) -> IntType<'ctx> {
diff --git a/users/glittershark/achilles/src/parser/mod.rs b/users/glittershark/achilles/src/parser/mod.rs
index dd7874aff853..c7e541ce48a0 100644
--- a/users/glittershark/achilles/src/parser/mod.rs
+++ b/users/glittershark/achilles/src/parser/mod.rs
@@ -9,6 +9,7 @@ mod type_;
 
 use crate::ast::{Arg, Decl, Fun, Ident};
 pub use expr::expr;
+use type_::function_type;
 pub use type_::type_;
 
 pub type Error = nom::Err<nom::error::Error<String>>;
@@ -79,9 +80,24 @@ named!(arg(&str) -> Arg, alt!(
     ascripted_arg
 ));
 
+named!(extern_decl(&str) -> Decl, do_parse!(
+    complete!(tag!("extern"))
+        >> multispace1
+        >> name: ident
+        >> multispace0
+        >> char!(':')
+        >> multispace0
+        >> type_: function_type
+        >> multispace0
+        >> (Decl::Extern {
+            name,
+            type_
+        })
+));
+
 named!(fun_decl(&str) -> Decl, do_parse!(
     complete!(tag!("fn"))
-        >> multispace0
+        >> multispace1
         >> name: ident
         >> multispace1
         >> args: separated_list0!(multispace1, arg)
@@ -112,7 +128,8 @@ named!(ascription_decl(&str) -> Decl, do_parse!(
 
 named!(pub decl(&str) -> Decl, alt!(
     ascription_decl |
-    fun_decl
+    fun_decl |
+    extern_decl
 ));
 
 named!(pub toplevel(&str) -> Vec<Decl>, terminated!(many0!(decl), multispace0));
diff --git a/users/glittershark/achilles/src/parser/type_.rs b/users/glittershark/achilles/src/parser/type_.rs
index c90ceda4d72e..1e6e380bb823 100644
--- a/users/glittershark/achilles/src/parser/type_.rs
+++ b/users/glittershark/achilles/src/parser/type_.rs
@@ -4,7 +4,7 @@ use nom::{alt, delimited, do_parse, map, named, opt, separated_list0, tag, termi
 use super::ident;
 use crate::ast::{FunctionType, Type};
 
-named!(function_type(&str) -> Type, do_parse!(
+named!(pub function_type(&str) -> FunctionType, do_parse!(
     tag!("fn")
         >> multispace1
         >> args: map!(opt!(terminated!(separated_list0!(
@@ -18,10 +18,10 @@ named!(function_type(&str) -> Type, do_parse!(
         >> tag!("->")
         >> multispace1
         >> ret: type_
-        >> (Type::Function(FunctionType {
+        >> (FunctionType {
             args,
             ret: Box::new(ret)
-        }))
+        })
 ));
 
 named!(pub type_(&str) -> Type, alt!(
@@ -29,7 +29,7 @@ named!(pub type_(&str) -> Type, alt!(
     tag!("float") => { |_| Type::Float } |
     tag!("bool") => { |_| Type::Bool } |
     tag!("cstring") => { |_| Type::CString } |
-    function_type |
+    function_type => { |ft| Type::Function(ft) }|
     ident => { |id| Type::Var(id) } |
     delimited!(
         tuple!(tag!("("), multispace0),
diff --git a/users/glittershark/achilles/src/tc/mod.rs b/users/glittershark/achilles/src/tc/mod.rs
index 52c18e6d5329..4bca52733bc8 100644
--- a/users/glittershark/achilles/src/tc/mod.rs
+++ b/users/glittershark/achilles/src/tc/mod.rs
@@ -337,6 +337,19 @@ impl<'ast> Typechecker<'ast> {
                 self.env.set(name.clone(), type_);
                 Ok(None)
             }
+            ast::Decl::Extern { name, type_ } => {
+                let type_ = self.type_from_ast_type(ast::Type::Function(type_));
+                self.env.set(name.clone(), type_.clone());
+                let (arg_types, ret_type) = match type_ {
+                    Type::Fun { args, ret } => (args, *ret),
+                    _ => unreachable!(),
+                };
+                Ok(Some(hir::Decl::Extern {
+                    name,
+                    arg_types,
+                    ret_type,
+                }))
+            }
         }
     }
 
diff --git a/users/glittershark/achilles/tests/compile.rs b/users/glittershark/achilles/tests/compile.rs
index 7fa15ad9653e..1b4da463a980 100644
--- a/users/glittershark/achilles/tests/compile.rs
+++ b/users/glittershark/achilles/tests/compile.rs
@@ -2,7 +2,30 @@ use std::process::Command;
 
 use crate_root::root;
 
-const FIXTURES: &[(&str, i32)] = &[("simple", 5), ("functions", 9)];
+struct Fixture {
+    name: &'static str,
+    exit_code: i32,
+    expected_output: &'static str,
+}
+
+const FIXTURES: &[Fixture] = &[
+    Fixture {
+        name: "simple",
+        exit_code: 5,
+        expected_output: "",
+    },
+    // TODO(grfn): needs monomorphization
+    // Fixture {
+    //     name: "functions",
+    //     exit_code: 9,
+    //     expected_output: "",
+    // },
+    Fixture {
+        name: "externs",
+        exit_code: 0,
+        expected_output: "foobar\n",
+    },
+];
 
 #[test]
 fn compile_and_run_files() {
@@ -21,13 +44,18 @@ fn compile_and_run_files() {
         "make clean failed"
     );
 
-    for (fixture, exit_code) in FIXTURES {
-        println!(">>> Testing: {}", fixture);
+    for Fixture {
+        name,
+        exit_code,
+        expected_output,
+    } in FIXTURES
+    {
+        println!(">>> Testing: {}", name);
 
-        println!("    Running: `make {}`", fixture);
+        println!("    Running: `make {}`", name);
         assert!(
             Command::new("make")
-                .arg(fixture)
+                .arg(name)
                 .current_dir(&ach)
                 .spawn()
                 .unwrap()
@@ -37,18 +65,11 @@ fn compile_and_run_files() {
             "make failed"
         );
 
-        let out_path = ach.join(fixture);
+        let out_path = ach.join(name);
         println!("    Running: `{}`", out_path.to_str().unwrap());
-        assert_eq!(
-            Command::new(out_path)
-                .spawn()
-                .unwrap()
-                .wait()
-                .unwrap()
-                .code()
-                .unwrap(),
-            *exit_code,
-        );
+        let output = Command::new(out_path).output().unwrap();
+        assert_eq!(output.status.code().unwrap(), *exit_code,);
+        assert_eq!(output.stdout, expected_output.as_bytes());
         println!("    OK");
     }
 }