From bae0d82f26cbec177ede40911c3fd0dfc1305371 Mon Sep 17 00:00:00 2001 From: apio Date: Thu, 25 Aug 2022 18:49:43 +0200 Subject: [PATCH] WE CAN MAKE FUNCTIONS NOW!!!!!!!!!!!!!!!!!!!!!!!!! --- CMakeLists.txt | 10 +++++ src/AST/ASTNode.h | 1 - src/AST/FunctionNode.cpp | 54 ++++++++++++++++++++++++++ src/AST/FunctionNode.h | 17 +++++++++ src/AST/FunctionPrototype.cpp | 18 +++++++++ src/AST/FunctionPrototype.h | 12 ++++++ src/AST/MulNode.cpp | 1 + src/AST/NumberNode.h | 3 ++ src/AST/ProgramNode.cpp | 18 +++++++++ src/AST/ProgramNode.h | 16 ++++++++ src/AST/SumNode.cpp | 1 + src/AST/SyscallNode.cpp | 2 + src/AST/TopLevelNode.cpp | 11 ++++++ src/AST/TopLevelNode.h | 15 ++++++++ src/GlobalContext.cpp | 8 ++++ src/GlobalContext.h | 5 +++ src/IRBuilder.cpp | 17 ++------- src/IRBuilder.h | 6 +-- src/Lexer.cpp | 12 +++++- src/Parser.cpp | 55 +++++++++++++++++++++++--- src/Parser.h | 6 ++- src/Result.h | 4 ++ src/Token.cpp | 4 ++ src/Token.h | 4 +- src/sapphire.cpp | 9 +++-- src/sapphirepch.h | 1 + src/utils.cpp | 72 +++++++++++++++++++++++++++++++++++ src/utils.h | 3 ++ test.py | 6 +-- tests/calc.json | 15 +++++++- tests/calc.sp | 4 +- tests/import-inexistent.json | 10 ++++- tests/import-inexistent.sp | 6 ++- tests/simple.json | 10 ++++- tests/wimport.json | 17 ++++++++- tests/wimport.sp | 6 ++- 36 files changed, 419 insertions(+), 40 deletions(-) create mode 100644 src/AST/FunctionNode.cpp create mode 100644 src/AST/FunctionNode.h create mode 100644 src/AST/FunctionPrototype.cpp create mode 100644 src/AST/FunctionPrototype.h create mode 100644 src/AST/ProgramNode.cpp create mode 100644 src/AST/ProgramNode.h create mode 100644 src/AST/TopLevelNode.cpp create mode 100644 src/AST/TopLevelNode.h create mode 100644 src/GlobalContext.cpp create mode 100644 src/GlobalContext.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 9956a02..7f34eb8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,6 +51,16 @@ add_executable( src/AST/SyscallNode.h src/AST/UnaryOpNode.cpp src/AST/UnaryOpNode.h + src/AST/ProgramNode.cpp + src/AST/ProgramNode.h + src/AST/TopLevelNode.cpp + src/AST/TopLevelNode.h + src/AST/FunctionPrototype.cpp + src/AST/FunctionPrototype.h + src/AST/FunctionNode.cpp + src/AST/FunctionNode.h + src/GlobalContext.cpp + src/GlobalContext.h src/utils.cpp src/utils.h src/Parser.cpp diff --git a/src/AST/ASTNode.h b/src/AST/ASTNode.h index 5c2d001..beac40c 100644 --- a/src/AST/ASTNode.h +++ b/src/AST/ASTNode.h @@ -1,5 +1,4 @@ #pragma once -#include "../IRBuilder.h" #include "llvm/IR/Value.h" class IRBuilder; diff --git a/src/AST/FunctionNode.cpp b/src/AST/FunctionNode.cpp new file mode 100644 index 0000000..de89f27 --- /dev/null +++ b/src/AST/FunctionNode.cpp @@ -0,0 +1,54 @@ +#include "FunctionNode.h" +#include "../Error.h" +#include "../IRBuilder.h" +#include "../utils.h" +#include "llvm/IR/Verifier.h" + +FunctionNode::FunctionNode(FunctionPrototype prototype, std::shared_ptr body) + : prototype(prototype), body(body) +{ +} + +void FunctionNode::codegen(IRBuilder* builder, 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())); + } + } + + if (!Function) return; + + if (!Function->empty()) + { + Error::throw_error_without_location(format_string("Function %s already has a body", prototype.name.c_str())); + } + + llvm::BasicBlock* BB = llvm::BasicBlock::Create(builder->getBuilder()->getContext(), "entry", Function); + builder->getBuilder()->SetInsertPoint(BB); + + if (llvm::Value* retVal = body->codegen(builder)) + { + builder->getBuilder()->CreateRet(retVal); + + if (llvm::verifyFunction(*Function)) + { + Error::throw_error_without_location(format_string("Invalid function %s", prototype.name.c_str())); + } + return; + } + + Function->eraseFromParent(); + Error::throw_error_without_location(format_string("Error generating code of function %s", prototype.name.c_str())); +} \ No newline at end of file diff --git a/src/AST/FunctionNode.h b/src/AST/FunctionNode.h new file mode 100644 index 0000000..c41bf6a --- /dev/null +++ b/src/AST/FunctionNode.h @@ -0,0 +1,17 @@ +#pragma once +#include "ExprNode.h" +#include "FunctionPrototype.h" +#include "TopLevelNode.h" + +class FunctionNode final : public TopLevelNode +{ + private: + FunctionPrototype prototype; + std::shared_ptr body; + + public: + FunctionNode(FunctionPrototype prototype, std::shared_ptr body); + ~FunctionNode() = default; + + void codegen(IRBuilder* builder, llvm::Module* module) override; +}; \ No newline at end of file diff --git a/src/AST/FunctionPrototype.cpp b/src/AST/FunctionPrototype.cpp new file mode 100644 index 0000000..4d01f61 --- /dev/null +++ b/src/AST/FunctionPrototype.cpp @@ -0,0 +1,18 @@ +#include "FunctionPrototype.h" + +llvm::FunctionType* FunctionPrototype::toFunctionType() +{ + if (arguments.size() == 0) + { + return llvm::FunctionType::get(returnType, false); + } + else + { + std::vector args; + for (auto& item : arguments) + { + args.push_back(item.first); + } + return llvm::FunctionType::get(returnType, args, false); + } +} \ No newline at end of file diff --git a/src/AST/FunctionPrototype.h b/src/AST/FunctionPrototype.h new file mode 100644 index 0000000..cda594e --- /dev/null +++ b/src/AST/FunctionPrototype.h @@ -0,0 +1,12 @@ +#pragma once +#include "llvm/IR/Function.h" +#include "llvm/IR/Type.h" + +struct FunctionPrototype +{ + llvm::Type* returnType; + std::string name; + std::vector> arguments; + + llvm::FunctionType* toFunctionType(); +}; \ No newline at end of file diff --git a/src/AST/MulNode.cpp b/src/AST/MulNode.cpp index 9b6281f..40dcb83 100644 --- a/src/AST/MulNode.cpp +++ b/src/AST/MulNode.cpp @@ -1,4 +1,5 @@ #include "MulNode.h" +#include "../IRBuilder.h" MulNode::MulNode(std::shared_ptr left, std::shared_ptr right, char op) : BinaryOpNode(left, right), op(op) diff --git a/src/AST/NumberNode.h b/src/AST/NumberNode.h index 3367dad..a2b4d54 100644 --- a/src/AST/NumberNode.h +++ b/src/AST/NumberNode.h @@ -1,5 +1,8 @@ #pragma once +#include "../IRBuilder.h" #include "ExprNode.h" +#include "llvm/IR/Constant.h" +#include "llvm/IR/Type.h" #include class NumberNode : public ExprNode diff --git a/src/AST/ProgramNode.cpp b/src/AST/ProgramNode.cpp new file mode 100644 index 0000000..8258aba --- /dev/null +++ b/src/AST/ProgramNode.cpp @@ -0,0 +1,18 @@ +#include "ProgramNode.h" + +ProgramNode::ProgramNode() : TopLevelNode() +{ +} + +void ProgramNode::append(std::shared_ptr item) +{ + program.push_back(item); +} + +void ProgramNode::walk(std::function)> callback) +{ + for (auto& item : program) + { + callback(item); + } +} \ No newline at end of file diff --git a/src/AST/ProgramNode.h b/src/AST/ProgramNode.h new file mode 100644 index 0000000..015d515 --- /dev/null +++ b/src/AST/ProgramNode.h @@ -0,0 +1,16 @@ +#pragma once +#include "TopLevelNode.h" + +class ProgramNode final : public TopLevelNode +{ + private: + std::vector> program; + + public: + ProgramNode(); + ~ProgramNode() = default; + + void append(std::shared_ptr); + + void walk(std::function)> callback); +}; \ No newline at end of file diff --git a/src/AST/SumNode.cpp b/src/AST/SumNode.cpp index 39cc1e4..19143c5 100644 --- a/src/AST/SumNode.cpp +++ b/src/AST/SumNode.cpp @@ -1,4 +1,5 @@ #include "SumNode.h" +#include "../IRBuilder.h" SumNode::SumNode(std::shared_ptr left, std::shared_ptr right, char op) : BinaryOpNode(left, right), op(op) diff --git a/src/AST/SyscallNode.cpp b/src/AST/SyscallNode.cpp index ea26afe..985d4e1 100644 --- a/src/AST/SyscallNode.cpp +++ b/src/AST/SyscallNode.cpp @@ -1,6 +1,8 @@ #include "SyscallNode.h" #include "../Arguments.h" #include "../Error.h" +#include "../IRBuilder.h" +#include "llvm/IR/Function.h" #include "llvm/IR/InlineAsm.h" Syscall0Node::Syscall0Node(int syscall_number) : sys_num(syscall_number), ExprNode() diff --git a/src/AST/TopLevelNode.cpp b/src/AST/TopLevelNode.cpp new file mode 100644 index 0000000..10ecb0c --- /dev/null +++ b/src/AST/TopLevelNode.cpp @@ -0,0 +1,11 @@ +#include "TopLevelNode.h" + +void TopLevelNode::codegen(IRBuilder*, llvm::Module*) +{ + return; +} + +llvm::Value* TopLevelNode::codegen(IRBuilder*) +{ + return nullptr; +} \ No newline at end of file diff --git a/src/AST/TopLevelNode.h b/src/AST/TopLevelNode.h new file mode 100644 index 0000000..d1166b9 --- /dev/null +++ b/src/AST/TopLevelNode.h @@ -0,0 +1,15 @@ +#pragma once +#include "ASTNode.h" +#include "llvm/IR/Module.h" + +class IRBuilder; + +class TopLevelNode : public ASTNode +{ + public: + TopLevelNode() = default; + ~TopLevelNode() = default; + + virtual void codegen(IRBuilder* builder, llvm::Module* module); + llvm::Value* codegen(IRBuilder* builder) override; +}; \ No newline at end of file diff --git a/src/GlobalContext.cpp b/src/GlobalContext.cpp new file mode 100644 index 0000000..7ac8d21 --- /dev/null +++ b/src/GlobalContext.cpp @@ -0,0 +1,8 @@ +#include "GlobalContext.h" + +std::shared_ptr globalContext; + +void initGlobalContext() +{ + globalContext = std::make_shared(); +} \ No newline at end of file diff --git a/src/GlobalContext.h b/src/GlobalContext.h new file mode 100644 index 0000000..721883d --- /dev/null +++ b/src/GlobalContext.h @@ -0,0 +1,5 @@ +#include "llvm/IR/LLVMContext.h" + +extern std::shared_ptr globalContext; + +void initGlobalContext(); \ No newline at end of file diff --git a/src/IRBuilder.cpp b/src/IRBuilder.cpp index ed9ebd2..11a7394 100644 --- a/src/IRBuilder.cpp +++ b/src/IRBuilder.cpp @@ -1,6 +1,7 @@ #include "IRBuilder.h" #include "Arguments.h" #include "Error.h" +#include "GlobalContext.h" #include "llvm/IR/Function.h" #include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/Value.h" @@ -15,7 +16,7 @@ IRBuilder::IRBuilder() { - context = std::make_unique(); + context = globalContext; builder = std::unique_ptr>(new llvm::IRBuilder<>(*context)); module = std::make_unique(Arguments::input_fname, *context); } @@ -25,19 +26,9 @@ llvm::IRBuilder<>* IRBuilder::getBuilder() return builder.get(); } -void IRBuilder::create_main_function(std::shared_ptr expression) +void IRBuilder::create_program(std::shared_ptr program) { - llvm::FunctionType* mainType = - llvm::FunctionType::get(llvm::IntegerType::getInt32Ty(*context), std::vector(), false); - llvm::Function* main = llvm::Function::Create(mainType, llvm::GlobalValue::ExternalLinkage, "main", module.get()); - - llvm::BasicBlock* entryBlock = llvm::BasicBlock::Create(*context, "entry", main); - builder->SetInsertPoint(entryBlock); - - llvm::Value* returnValue = expression->codegen(this); - - builder->CreateRet(returnValue); - llvm::verifyFunction(*main); + program->walk([&](std::shared_ptr node) { node->codegen(this, module.get()); }); } void IRBuilder::resolveToLLVMIR(std::string path) diff --git a/src/IRBuilder.h b/src/IRBuilder.h index f28baee..254b58a 100644 --- a/src/IRBuilder.h +++ b/src/IRBuilder.h @@ -1,5 +1,5 @@ #pragma once -#include "AST/ASTNode.h" +#include "AST/ProgramNode.h" #include "llvm/ADT/APFloat.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/LLVMContext.h" @@ -9,13 +9,13 @@ class ASTNode; class IRBuilder { - std::unique_ptr context; + std::shared_ptr context; std::unique_ptr> builder; std::unique_ptr module; public: IRBuilder(); - void create_main_function(std::shared_ptr expression); + void create_program(std::shared_ptr program); llvm::IRBuilder<>* getBuilder(); diff --git a/src/Lexer.cpp b/src/Lexer.cpp index 1bb23b3..f7e7aad 100644 --- a/src/Lexer.cpp +++ b/src/Lexer.cpp @@ -147,10 +147,10 @@ TokenStream Lexer::lex(const std::string& text) result.push_back(Token::make_with_line({TT_RParen, loc}, current_line_text)); break; case '{': - result.push_back(Token::make_with_line({TT_RBracket, loc}, current_line_text)); + result.push_back(Token::make_with_line({TT_LBracket, loc}, current_line_text)); break; case '}': - result.push_back(Token::make_with_line({TT_LBracket, loc}, current_line_text)); + result.push_back(Token::make_with_line({TT_RBracket, loc}, current_line_text)); break; case ';': result.push_back(Token::make_with_line({TT_Semicolon, loc}, current_line_text)); @@ -252,6 +252,10 @@ Token Lexer::create_identifier() if (identifier == "compmacro") return Token::make_with_line({TT_CompilerMacro, {prev_line, prev_column, loc.fname}}, current_line_text); + if (identifier == "let") + return Token::make_with_line({TT_Let, {prev_line, prev_column, loc.fname}}, current_line_text); + if (identifier == "in") + return Token::make_with_line({TT_In, {prev_line, prev_column, loc.fname}}, current_line_text); return Token::make_with_line({TT_Identifier, identifier, {prev_line, prev_column, loc.fname}}, current_line_text); } @@ -281,6 +285,10 @@ Token Lexer::create_identifier() return Token::make_with_line({TT_Syscall5, {prev_line, prev_column, loc.fname}}, current_line_text); if (identifier == "compmacro") return Token::make_with_line({TT_CompilerMacro, {prev_line, prev_column, loc.fname}}, current_line_text); + if (identifier == "let") + return Token::make_with_line({TT_Let, {prev_line, prev_column, loc.fname}}, current_line_text); + if (identifier == "in") + return Token::make_with_line({TT_In, {prev_line, prev_column, loc.fname}}, current_line_text); return Token::make_with_line({TT_Identifier, identifier, {prev_line, prev_column, loc.fname}}, current_line_text); } diff --git a/src/Parser.cpp b/src/Parser.cpp index 05bba80..7ac6789 100644 --- a/src/Parser.cpp +++ b/src/Parser.cpp @@ -1,6 +1,9 @@ #include "Parser.h" +#include "AST/FunctionNode.h" #include "AST/MulNode.h" #include "AST/SumNode.h" +#include "FormatString/FormatString.hpp" +#include "GlobalContext.h" Parser::Parser(const TokenStream& tokens) : tokens(tokens) { @@ -12,16 +15,18 @@ std::shared_ptr Parser::new_parser(const TokenStream& tokens) new Parser(tokens)); // As always, not using std::make_shared 'cause constructor is private } -std::shared_ptr Parser::parse() +std::shared_ptr Parser::parse() { advance(); - auto result = expr(); - if (result.is_error()) result.ethrow(); - if (current_token->tk_type != TT_EOF) + std::shared_ptr final_result = std::make_shared(); + while (true) { - Err("expected *, /, + or -", current_token).ethrow(); + auto result = toplevel(); + if (result.is_error()) result.ethrow(); + final_result->append(result.get()); + if (current_token->tk_type == TT_EOF) break; } - return result.get(); + return final_result; } int Parser::advance() @@ -82,3 +87,41 @@ Result Parser::expr() } return left; } + +Result Parser::toplevel() +{ + // FIXME: Add more top-level stuff later, for now it's only functions. + return function(); +} + +Result Parser::function() +{ + FunctionPrototype proto; + proto.returnType = llvm::IntegerType::getInt32Ty(*globalContext); // FIXME: allow specifying return type + proto.arguments = {}; // FIXME: allow specifying arguments + if (current_token->tk_type != TT_Let) + return Err("Expected let at the beginning of a function", current_token); + advance(); + if (current_token->tk_type != TT_At) + return Err("Expected @ at the beginning of a function", current_token); + advance(); + if (current_token->tk_type != TT_Identifier) return Err("Expected an identifier", current_token); + else + 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); + if (current_token->tk_type == TT_Semicolon) + return Err("Functions without a body are unsupported (for now)", current_token); + advance(); + if (current_token->tk_type != TT_LBracket) + return Err("Invalid syntax", + current_token); // FIXME: Do not be lazy and return a meaningful error message. + advance(); + Result body = expr(); + if (body.is_error()) return Err(body.error(), body.token()); + 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); +} diff --git a/src/Parser.h b/src/Parser.h index f4f79e8..12ca99b 100644 --- a/src/Parser.h +++ b/src/Parser.h @@ -1,5 +1,6 @@ #pragma once #include "AST/NumberNode.h" +#include "AST/ProgramNode.h" #include "AST/SumNode.h" #include "Error.h" #include "Lexer.h" @@ -20,9 +21,12 @@ class Parser Result expr(); Result term(); + Result toplevel(); + Result function(); + public: /* Construct a new Parser with the given TokenStream. */ static std::shared_ptr new_parser(const TokenStream& tokens); /* Parse the stored TokenStream and return the top-level node of the result Abstract Syntax Tree. */ - std::shared_ptr parse(); + std::shared_ptr parse(); }; diff --git a/src/Result.h b/src/Result.h index eae3f82..28fc184 100644 --- a/src/Result.h +++ b/src/Result.h @@ -24,6 +24,10 @@ template class Result { return m_result; } + std::string error() + { + return m_error; + } protected: Token* m_token; diff --git a/src/Token.cpp b/src/Token.cpp index 149d680..780f2b2 100644 --- a/src/Token.cpp +++ b/src/Token.cpp @@ -150,6 +150,10 @@ std::string Token::to_string() const return "SYSCALL5 " + details; case TT_CompilerMacro: return "COMPMACRO " + details; + case TT_Let: + return "LET " + details; + case TT_In: + return "IN " + details; } return ""; } diff --git a/src/Token.h b/src/Token.h index a9bae23..253379f 100644 --- a/src/Token.h +++ b/src/Token.h @@ -44,7 +44,9 @@ enum TokenType TT_Syscall3, TT_Syscall4, TT_Syscall5, - TT_CompilerMacro + TT_CompilerMacro, + TT_Let, + TT_In }; extern const std::string token_strings[]; diff --git a/src/sapphire.cpp b/src/sapphire.cpp index 732adc6..3e47faa 100644 --- a/src/sapphire.cpp +++ b/src/sapphire.cpp @@ -1,5 +1,6 @@ #include "Arguments.h" #include "FileIO.h" +#include "GlobalContext.h" #include "IRBuilder.h" #include "Importer.h" #include "Lexer.h" @@ -33,9 +34,11 @@ int main(int argc, char** argv) result = Normalizer::normalize(result); } + initGlobalContext(); + auto parser = Parser::new_parser(result); - std::shared_ptr ast; + std::shared_ptr ast; { benchmark("Parsing"); ast = parser->parse(); @@ -44,8 +47,8 @@ int main(int argc, char** argv) IRBuilder builder; { - benchmark("IR generation"); - builder.create_main_function(ast); + benchmark("Code generation"); + builder.create_program(ast); } if (Arguments::emit_llvm) builder.resolveToLLVMIR(Arguments::output_fname); diff --git a/src/sapphirepch.h b/src/sapphirepch.h index 0b0ebac..a6304e5 100644 --- a/src/sapphirepch.h +++ b/src/sapphirepch.h @@ -1,5 +1,6 @@ #include #include +#include #include #include #include diff --git a/src/utils.cpp b/src/utils.cpp index c7f3731..8306a77 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -1,6 +1,78 @@ #include "utils.h" +#include +#include #include +bool equals(const llvm::Type* left, const llvm::Type* right) +{ + auto left_ptr = llvm::dyn_cast(left); + auto right_ptr = llvm::dyn_cast(right); + + if (left == right) return true; + + if (left->getTypeID() == right->getTypeID()) return true; + + switch (left->getTypeID()) + { + case llvm::Type::IntegerTyID: + return llvm::cast(left)->getBitWidth() == + llvm::cast(right)->getBitWidth(); + + // left == right would have returned true earlier, because types are uniqued. + case llvm::Type::VoidTyID: + case llvm::Type::FloatTyID: + case llvm::Type::DoubleTyID: + case llvm::Type::X86_FP80TyID: + case llvm::Type::FP128TyID: + case llvm::Type::PPC_FP128TyID: + case llvm::Type::LabelTyID: + case llvm::Type::MetadataTyID: + case llvm::Type::TokenTyID: + return true; + + case llvm::Type::PointerTyID: + assert(left_ptr && right_ptr && "Both types must be pointers here."); + return left_ptr->getAddressSpace() == right_ptr->getAddressSpace(); + + case llvm::Type::StructTyID: { + auto left_struct = llvm::cast(left); + auto right_struct = llvm::cast(right); + + if (left_struct->getNumElements() != right_struct->getNumElements()) return false; + + if (left_struct->isPacked() != right_struct->isPacked()) return false; + + for (unsigned i = 0, e = left_struct->getNumElements(); i != e; ++i) + { + if (!equals(left_struct->getElementType(i), right_struct->getElementType(i))) return false; + } + + return true; + } + + case llvm::Type::FunctionTyID: { + auto left_function = llvm::cast(left); + auto right_function = llvm::cast(right); + + if (left_function->getNumParams() != right_function->getNumParams()) return false; + + if (left_function->isVarArg() != right_function->isVarArg()) return false; + + if (!equals(left_function->getReturnType(), right_function->getReturnType())) return false; + + for (unsigned i = 0, e = left_function->getNumParams(); i != e; ++i) + { + if (!equals(left_function->getParamType(i), right_function->getParamType(i))) return false; + } + + return true; + } + + default: + return false; + } +} + bool replace(std::string& str, const std::string& from, const std::string& to) { size_t start_pos = str.find(from); diff --git a/src/utils.h b/src/utils.h index c64c267..de19133 100644 --- a/src/utils.h +++ b/src/utils.h @@ -7,8 +7,11 @@ #pragma once #include "FormatString/FormatString.hpp" #include "sapphirepch.h" +#include "llvm/IR/Type.h" #include +bool equals(const llvm::Type* left, const llvm::Type* right); + /* * Replaces all ocurrences of a substring with another one in a string. * @param[in] str The input string. diff --git a/test.py b/test.py index 016e904..8ad89d4 100755 --- a/test.py +++ b/test.py @@ -45,7 +45,7 @@ def test_test_case(test_case: dict) -> bool: runtime = test_case.get("run", False) if runtime is not False: print("-> Running command: gcc .tests-bin/output.o -o .tests-bin/output") - link_task = subprocess.Popen(["gcc", ".tests-bin/output.o", "-o", ".tests-bin/output"] + extra_flags, + link_task = subprocess.Popen(["gcc", ".tests-bin/output.o", "-o", ".tests-bin/output"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) if link_task.wait() != 0: print(f"-> Failed to link program") @@ -111,7 +111,7 @@ def create_test(filename: str, extra_flags: list): compiler["stderr"] = retstderr test_case["compile"] = compiler if retcode == 0: - link_task = subprocess.Popen(["gcc", ".tests-bin/output.o", "-o", ".tests-bin/output"] + extra_flags, + link_task = subprocess.Popen(["gcc", ".tests-bin/output.o", "-o", ".tests-bin/output"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) link_status = link_task.wait() if link_status == 0: @@ -127,7 +127,7 @@ def create_test(filename: str, extra_flags: list): test_case["run"] = program ofilepath = ".".join(filepath.split(".")[:-1]) + ".json" ofile = open(ofilepath, "w+") - json.dump(test_case, ofile) + json.dump(test_case, ofile, indent=4) ofile.close() shutil.rmtree(".tests-bin") diff --git a/tests/calc.json b/tests/calc.json index 7473b2c..bebf357 100644 --- a/tests/calc.json +++ b/tests/calc.json @@ -1 +1,14 @@ -{"file": "calc.sp", "compile": {"flags": [], "exit-code": 0, "stdout": "", "stderr": ""}, "run": {"exit-code": 16, "stdout": "", "stderr": ""}} \ No newline at end of file +{ + "file": "calc.sp", + "compile": { + "flags": [], + "exit-code": 0, + "stdout": "", + "stderr": "" + }, + "run": { + "exit-code": 16, + "stdout": "", + "stderr": "" + } +} \ No newline at end of file diff --git a/tests/calc.sp b/tests/calc.sp index 46ec7e5..5656a54 100644 --- a/tests/calc.sp +++ b/tests/calc.sp @@ -1 +1,3 @@ -1 + 3 * 5 \ No newline at end of file +let @main in { + 1 + 3 * 5 +} \ No newline at end of file diff --git a/tests/import-inexistent.json b/tests/import-inexistent.json index 1a946af..aed893e 100644 --- a/tests/import-inexistent.json +++ b/tests/import-inexistent.json @@ -1 +1,9 @@ -{"file": "import-inexistent.sp", "compile": {"flags": [], "exit-code": 1, "stdout": "", "stderr": "\u001b[1;1mtests/import-inexistent.sp:1:8: \u001b[31;49merror: \u001b[0;0mfile not found\n1 import penguin_boi;\n \u001b[31;49m^\u001b[0;0m\n"}} \ No newline at end of file +{ + "file": "import-inexistent.sp", + "compile": { + "flags": [], + "exit-code": 1, + "stdout": "", + "stderr": "\u001b[1;1mtests/import-inexistent.sp:1:8: \u001b[31;49merror: \u001b[0;0mfile not found\n1 import penguin_boi;\n \u001b[31;49m^\u001b[0;0m\n" + } +} \ No newline at end of file diff --git a/tests/import-inexistent.sp b/tests/import-inexistent.sp index f248604..a984010 100644 --- a/tests/import-inexistent.sp +++ b/tests/import-inexistent.sp @@ -1 +1,5 @@ -import penguin_boi; \ No newline at end of file +import penguin_boi; + +let @main in { + 6 + 3 +} \ No newline at end of file diff --git a/tests/simple.json b/tests/simple.json index 317a33b..4c73651 100644 --- a/tests/simple.json +++ b/tests/simple.json @@ -1 +1,9 @@ -{"file": "simple.sp", "compile": {"flags": [], "exit-code": 1, "stdout": "", "stderr": "\u001b[1;1mtests/simple.sp:1:1: \u001b[31;49merror: \u001b[0;0mexpected a number\n1 const { outln } from @'core/io';\n \u001b[31;49m^\u001b[0;0m\n"}} \ No newline at end of file +{ + "file": "simple.sp", + "compile": { + "flags": [], + "exit-code": 1, + "stdout": "", + "stderr": "\u001b[1;1mtests/simple.sp:1:1: \u001b[31;49merror: \u001b[0;0mExpected let at the beginning of a function\n1 const { outln } from @'core/io';\n \u001b[31;49m^\u001b[0;0m\n" + } +} \ No newline at end of file diff --git a/tests/wimport.json b/tests/wimport.json index b2f32a7..355d9c2 100644 --- a/tests/wimport.json +++ b/tests/wimport.json @@ -1 +1,16 @@ -{"file": "wimport.sp", "compile": {"flags": ["--wimport"], "exit-code": 1, "stdout": "\u001b[1;1mtests/wimport.sp:1:8: \u001b[33;49mwarning: \u001b[0;0mfile already imported, skipping\n1 import tests/wimport;\n \u001b[33;49m^\u001b[0;0m\n", "stderr": "\u001b[1;1mtests/wimport.sp:1:22: \u001b[31;49merror: \u001b[0;0mexpected a number\n1 \n \u001b[31;49m^\u001b[0;0m\n"}} \ No newline at end of file +{ + "file": "wimport.sp", + "compile": { + "flags": [ + "--wimport" + ], + "exit-code": 0, + "stdout": "\u001b[1;1mtests/wimport.sp:1:8: \u001b[33;49mwarning: \u001b[0;0mfile already imported, skipping\n1 import tests/wimport;\n \u001b[33;49m^\u001b[0;0m\n", + "stderr": "" + }, + "run": { + "exit-code": 0, + "stdout": "", + "stderr": "" + } +} \ No newline at end of file diff --git a/tests/wimport.sp b/tests/wimport.sp index 62b74a3..0208763 100644 --- a/tests/wimport.sp +++ b/tests/wimport.sp @@ -1 +1,5 @@ -import tests/wimport; \ No newline at end of file +import tests/wimport; + +let @main in { + 0 +} \ No newline at end of file