From b1c7a26cfbd4c782b7340e400def1402a7209461 Mon Sep 17 00:00:00 2001 From: apio Date: Thu, 25 Aug 2022 19:15:43 +0200 Subject: [PATCH] You can now forward declare functions! --- CMakeLists.txt | 2 ++ src/AST/EmptyFunctionNode.cpp | 27 +++++++++++++++++++++++++++ src/AST/EmptyFunctionNode.h | 16 ++++++++++++++++ src/Parser.cpp | 11 ++++++++--- 4 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 src/AST/EmptyFunctionNode.cpp create mode 100644 src/AST/EmptyFunctionNode.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 7f34eb8..cefacd0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -59,6 +59,8 @@ add_executable( src/AST/FunctionPrototype.h src/AST/FunctionNode.cpp src/AST/FunctionNode.h + src/AST/EmptyFunctionNode.cpp + src/AST/EmptyFunctionNode.h src/GlobalContext.cpp src/GlobalContext.h src/utils.cpp diff --git a/src/AST/EmptyFunctionNode.cpp b/src/AST/EmptyFunctionNode.cpp new file mode 100644 index 0000000..c64d672 --- /dev/null +++ b/src/AST/EmptyFunctionNode.cpp @@ -0,0 +1,27 @@ +#include "EmptyFunctionNode.h" +#include "../Error.h" +#include "../utils.h" + +EmptyFunctionNode::EmptyFunctionNode(FunctionPrototype prototype) : TopLevelNode(), prototype(prototype) +{ +} + +void EmptyFunctionNode::codegen(IRBuilder* generator, llvm::Module* module) +{ + llvm::Function* Function = module->getFunction(prototype.name); + if (!Function) + { + llvm::FunctionType* Type = prototype.toFunctionType(); + + Function = llvm::Function::Create(Type, llvm::Function::ExternalLinkage, prototype.name, *module); + } + else + { + if (!equals(Function->getFunctionType(), prototype.toFunctionType())) + { + // FIXME: add location information to AST nodes, to add information to these errors + Error::throw_error_without_location( + format_string("Function %s redefined with different prototype", prototype.name.c_str())); + } + } +} diff --git a/src/AST/EmptyFunctionNode.h b/src/AST/EmptyFunctionNode.h new file mode 100644 index 0000000..05a68fb --- /dev/null +++ b/src/AST/EmptyFunctionNode.h @@ -0,0 +1,16 @@ +#pragma once +#include "../IRBuilder.h" +#include "FunctionPrototype.h" +#include "TopLevelNode.h" + +class EmptyFunctionNode final : public TopLevelNode +{ + private: + FunctionPrototype prototype; + + public: + EmptyFunctionNode(FunctionPrototype prototype); + ~EmptyFunctionNode() = default; + + void codegen(IRBuilder* generator, llvm::Module* module) override; +}; diff --git a/src/Parser.cpp b/src/Parser.cpp index 7ac6789..e8c6acf 100644 --- a/src/Parser.cpp +++ b/src/Parser.cpp @@ -1,4 +1,5 @@ #include "Parser.h" +#include "AST/EmptyFunctionNode.h" #include "AST/FunctionNode.h" #include "AST/MulNode.h" #include "AST/SumNode.h" @@ -97,6 +98,7 @@ Result Parser::toplevel() Result Parser::function() { FunctionPrototype proto; + Token* ftoken = current_token; proto.returnType = llvm::IntegerType::getInt32Ty(*globalContext); // FIXME: allow specifying return type proto.arguments = {}; // FIXME: allow specifying arguments if (current_token->tk_type != TT_Let) @@ -110,9 +112,12 @@ Result Parser::function() proto.name = current_token->string_value; advance(); if (current_token->tk_type != TT_In && current_token->tk_type != TT_Semicolon) - return Err("Expected 'in'", current_token); + return Err("Expected 'in' or semicolon", current_token); if (current_token->tk_type == TT_Semicolon) - return Err("Functions without a body are unsupported (for now)", current_token); + { + advance(); + return Ok(new EmptyFunctionNode(proto), ftoken); + } advance(); if (current_token->tk_type != TT_LBracket) return Err("Invalid syntax", @@ -123,5 +128,5 @@ Result Parser::function() if (current_token->tk_type != TT_RBracket) return Err(format_string("Invalid syntax %d", current_token->tk_type), current_token); advance(); - return Ok(new FunctionNode(proto, body.get()), current_token); + return Ok(new FunctionNode(proto, body.get()), ftoken); }