diff --git a/CMakeLists.txt b/CMakeLists.txt index b5f0ab3a20a80f05f1b8c3064a18390e20756b45..934802709e84bfda69154d4ca65385429f3d0dda 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.4 FATAL_ERROR) set(LIB_TARGET dusklib) set(PROJECT_VERSION 1.0.0) project( - ${LIB_TARGET} + "dusk-lang" VERSION ${PROJECT_VERSION} DESCRIPTION "Dusk programming language" ) @@ -21,7 +21,7 @@ message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") # link LLVM add_definitions(${LLVM_DEFINITIONS}) include_directories(${LLVM_INCLUDE_DIRS}) -llvm_map_components_to_libnames(llvm_libs support core irreader tablegen) +llvm_map_components_to_libnames(llvm_libs all) # add dusklib target include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) diff --git a/examples/funcDecl.dusk b/examples/funcDecl.dusk index bc8e437aed0beb445da2a77076a8aac20f7edd4c..de1eeace293c4614b880aec750ca0d2489d601df 100644 --- a/examples/funcDecl.dusk +++ b/examples/funcDecl.dusk @@ -1,3 +1,5 @@ +extern func test(); + func inc(i) -> Int { return i + 1; } diff --git a/grammar.txt b/grammar.txt index 4fdd1aa538ac45a6d1d5dd85d0e36aacae1816cf..7d1e51a403233d69954602db1813c58e61e98626 100644 --- a/grammar.txt +++ b/grammar.txt @@ -6,14 +6,13 @@ Program -> Stmt Program // 'for', 'while', 'if' Decl -> 'var' identifier DeclVal ';' // 'var' Decl -> 'let' identifier DeclVal ';' // 'let' +Decl -> 'extern' FuncDecl // 'extern' DeclVal -> '=' Expr // '=' -// Decl_ -> '[' DeclArray // '[' -// DeclArray -> Expr // '(', identifier, number_literal, '-' -// DeclArray -> ']' '=' ArrayLiteral // ']' +Func -> FuncDelc Block // 'func' +FuncDecl -> 'func' identifier '(' Args ')' RetType // 'func' -Func -> 'func' identifier '(' Args ')' RetType Block // 'func' -RetType -> epsilon // _ | '{' +RetType -> epsilon // _ | '{', ';' RetType -> '->' RetType_ // '->' RetType_ -> 'void' // 'Void' RetType_ -> 'Int' // 'Int' diff --git a/include/dusk/AST/ASTVisitor.h b/include/dusk/AST/ASTVisitor.h index 5e84b6ac90cb4e586955201f18cc3ac5b0dbe6e2..af8db936d18d61ead248cfdcfb647039635e82ee 100644 --- a/include/dusk/AST/ASTVisitor.h +++ b/include/dusk/AST/ASTVisitor.h @@ -102,6 +102,8 @@ public: return getDerived().visit(static_cast<SubscriptStmt *>(S)); case StmtKind::Block: return getDerived().visit(static_cast<BlockStmt *>(S)); + case StmtKind::Extern: + return getDerived().visit(static_cast<ExternStmt *>(S)); case StmtKind::For: return getDerived().visit(static_cast<ForStmt *>(S)); case StmtKind::Func: diff --git a/include/dusk/AST/DiagnosticsParse.h b/include/dusk/AST/DiagnosticsParse.h index ff44661840af99d7fb254f7e61390fd873d68c10..6260c397ee8cd6ee9757e1e541b444cce677e978 100644 --- a/include/dusk/AST/DiagnosticsParse.h +++ b/include/dusk/AST/DiagnosticsParse.h @@ -38,7 +38,8 @@ enum struct DiagID : unsigned { expected_r_bracket, expected_r_brace, expected_return_type, - expected_type_specifier + expected_type_specifier, + expected_func_kw }; static StringRef getTextForID(DiagID ID) { @@ -80,6 +81,8 @@ static StringRef getTextForID(DiagID ID) { return "Expected '->' return type decalration"; case DiagID::expected_type_specifier: return "Expected type specifier"; + case DiagID::expected_func_kw: + return "Expected 'func' keyword to at start of function delaration"; } } diff --git a/include/dusk/AST/Stmt.h b/include/dusk/AST/Stmt.h index 6cdc2fc8aa5dca3f62fd6a852c4a510c009fe38f..ac10dca241be6565efd8190d6dee28ce00b7c31b 100644 --- a/include/dusk/AST/Stmt.h +++ b/include/dusk/AST/Stmt.h @@ -30,6 +30,7 @@ enum struct StmtKind { Break, Return, Range, + Extern, Block, Func, For, @@ -139,6 +140,22 @@ public: virtual SMRange getSourceRange() const override; }; +class ExternStmt : public Stmt { + /// Location of extern keyword + SMLoc ExternLoc; + + /// Function prototype + Decl *Prototype; + +public: + ExternStmt(SMLoc EL, Decl *P); + + SMLoc getExternLoc() const { return ExternLoc; } + Decl *getPrototype() const { return Prototype; } + + virtual SMRange getSourceRange() const override; +}; + /// Represents a Function statement a.k.a declaration and definition. class FuncStmt : public Stmt { Decl *Prototype; diff --git a/include/dusk/Basic/TokenDefinition.h b/include/dusk/Basic/TokenDefinition.h index 7750360be620e7841fc90357ca341396b423a3bb..52639936844c6fa28198ee8309ae022f0003ccc0 100644 --- a/include/dusk/Basic/TokenDefinition.h +++ b/include/dusk/Basic/TokenDefinition.h @@ -27,8 +27,9 @@ enum struct tok { kwFor, kwIn, kwFunc, - kwWriteln, + kwPrintln, kwReadln, + kwExtern, // Types kwVoid, @@ -112,10 +113,12 @@ inline raw_ostream &operator<<(raw_ostream &OS, const dusk::tok &T) { return OS << "in"; case dusk::tok::kwFunc: return OS << "func"; - case dusk::tok::kwWriteln: + case dusk::tok::kwPrintln: return OS << "writeln"; case dusk::tok::kwReadln: return OS << "readln"; + case dusk::tok::kwExtern: + return OS << "extern"; // Types case dusk::tok::kwVoid: diff --git a/include/dusk/Frontend/Compiler.h b/include/dusk/Frontend/Compiler.h index ed230f491431aeb86cf4d96fd64d5714144d4a07..57d4cadbd4932c68940034e7c695f04b5a29e9d1 100644 --- a/include/dusk/Frontend/Compiler.h +++ b/include/dusk/Frontend/Compiler.h @@ -20,6 +20,10 @@ #include <vector> #include <memory> +namespace llvm { +class Module; +} + namespace dusk { class ParserResult; diff --git a/include/dusk/IRGen/IRGenerator.h b/include/dusk/IRGen/IRGenerator.h index cd561a86dc7abf518cd921e2138517775139c152..50e15ec981926110561d6e48fd18f652b39b865a 100644 --- a/include/dusk/IRGen/IRGenerator.h +++ b/include/dusk/IRGen/IRGenerator.h @@ -51,7 +51,7 @@ public: IRGenerator(DiagnosticEngine &Diag); ~IRGenerator(); - bool gen(ModuleDecl *M); + llvm::Module *gen(ModuleDecl *M); private: bool prepareGlobals(Context &Ctx, ModuleDecl *M); diff --git a/include/dusk/Parse/Parser.h b/include/dusk/Parse/Parser.h index 3c3873d254fc1a471f310d22c5131cd1f9d2a667..8428099c35dcd9be945d82d5240e3a7a97f53e92 100644 --- a/include/dusk/Parse/Parser.h +++ b/include/dusk/Parse/Parser.h @@ -147,6 +147,7 @@ private: Stmt *parseSubscriptStmt(); + Stmt *parseExterStmt(); Stmt *parseFuncStmt(); Stmt *parseForStmt(); diff --git a/include/dusk/Parse/Token.h b/include/dusk/Parse/Token.h index 7689faa2a2afcb06a3c21a8df210f82b1dc2057c..1dbb0fcf44817dd6c39ab6afc2a2d0fda8b2771e 100644 --- a/include/dusk/Parse/Token.h +++ b/include/dusk/Parse/Token.h @@ -114,6 +114,7 @@ public: switch (Kind) { case tok::kwVar: case tok::kwLet: + case tok::kwBreak: case tok::kwReturn: case tok::kwIf: @@ -122,10 +123,13 @@ public: case tok::kwFor: case tok::kwIn: case tok::kwFunc: - case tok::kwWriteln: + case tok::kwPrintln: case tok::kwReadln: + case tok::kwExtern: + case tok::kwVoid: case tok::kwInt: + return true; default: return false; diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index ee7a3abff041dc9e6e34cadfee18ed85a0864cc1..2bf63462e0f127f56ac17e13524c1a31a6c1ae63 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -199,6 +199,23 @@ public: return true; } + bool visit(ExternStmt *S) { + Printer.printStmtPre(S); + Printer << tok::kwExtern << " "; + super::visit(S->getPrototype()); + Printer.printStmtPost(S); + return true; + } + + bool visit(FuncStmt *S) { + Printer.printStmtPre(S); + super::visit(S->getPrototype()); + Printer << " "; + super::visit(S->getBody()); + Printer.printStmtPost(S); + return true; + } + bool visit(ForStmt *S) { Printer.printStmtPre(S); @@ -213,15 +230,6 @@ public: return true; } - bool visit(FuncStmt *S) { - Printer.printStmtPre(S); - super::visit(S->getPrototype()); - Printer << " "; - super::visit(S->getBody()); - Printer.printStmtPost(S); - return true; - } - bool visit(IfStmt *S) { Printer.printStmtPre(S); Printer << tok::kwIf << " "; @@ -286,21 +294,21 @@ public: tok KW; switch (D->getKind()) { case DeclKind::Let: + if (!isAtStartOfLine()) + printNewline(); KW = tok::kwLet; break; case DeclKind::Var: + if (!isAtStartOfLine()) + printNewline(); KW = tok::kwVar; break; case DeclKind::Func: KW = tok::kwFunc; - printNewline(); break; default: return; } - - if (!isAtStartOfLine()) - printNewline(); *this << KW << " "; } @@ -320,8 +328,13 @@ public: *this << tok::l_brace; ++(*this); break; - case StmtKind::For: + case StmtKind::Extern: case StmtKind::Func: + if (!isAtStartOfLine()) + printNewline(); + printNewline(); + break; + case StmtKind::For: case StmtKind::If: case StmtKind::While: if (!isAtStartOfLine()) @@ -343,6 +356,7 @@ public: break; case StmtKind::Break: case StmtKind::Return: + case StmtKind::Extern: *this << ";"; break; case StmtKind::Subscript: diff --git a/lib/AST/ASTWalker.cpp b/lib/AST/ASTWalker.cpp index 4d4cd412992ebfb052c1a392900d4a140e0692d5..90a1b812f273dcab3c316117cbde5e2b8db6a6ea 100644 --- a/lib/AST/ASTWalker.cpp +++ b/lib/AST/ASTWalker.cpp @@ -230,24 +230,34 @@ public: return Walker.postWalk(S); } - bool visit(ForStmt *S) { + bool visit(ExternStmt *S) { // Skip subtree if (!Walker.preWalk(S)) + return true; + + if (!super::visit(S->getPrototype())) return false; - - if (!super::visit(S->getRange())) + return Walker.postWalk(S); + } + + bool visit(FuncStmt *S) { + // Skip subtree + if (!Walker.preWalk(S)) + return true; + + if (!super::visit(S->getPrototype())) return false; if (!super::visit(S->getBody())) return false; return !Walker.postWalk(S); } - - bool visit(FuncStmt *S) { + + bool visit(ForStmt *S) { // Skip subtree if (!Walker.preWalk(S)) - return true; + return false; - if (!super::visit(S->getPrototype())) + if (!super::visit(S->getRange())) return false; if (!super::visit(S->getBody())) return false; diff --git a/lib/AST/Stmt.cpp b/lib/AST/Stmt.cpp index 522922d2ce924014bf4ffe42e70b3c6bac4c9878..d8c988379fe01a0cdedea57c26b313d858418839 100644 --- a/lib/AST/Stmt.cpp +++ b/lib/AST/Stmt.cpp @@ -53,6 +53,15 @@ BlockStmt::BlockStmt(SMLoc S, SMLoc E, std::vector<ASTNode *> &&N) SMRange BlockStmt::getSourceRange() const { return {BlockStart, BlockEnd}; } +// MARK: - Extern statement + +ExternStmt::ExternStmt(SMLoc EL, Decl *P) + : Stmt(StmtKind::Extern), ExternLoc(EL), Prototype(P) {} + +SMRange ExternStmt::getSourceRange() const { + return { ExternLoc, Prototype->getSourceRange().End }; +} + // MARK: - Funcion statement FuncStmt::FuncStmt(Decl *FP, Stmt *B) diff --git a/lib/Frontend/Compiler.cpp b/lib/Frontend/Compiler.cpp index cf51e042303d668b0ec53bdc84d21d2487181bf4..18b7851df1bb2cc18b1deea108a4be74df29e37f 100644 --- a/lib/Frontend/Compiler.cpp +++ b/lib/Frontend/Compiler.cpp @@ -6,6 +6,21 @@ #include "dusk/IRGen/IRGenerator.h" #include "llvm/Support/raw_os_ostream.h" +#include "llvm/Analysis/Passes.h" +#include "llvm/ExecutionEngine/GenericValue.h" +#include "llvm/ExecutionEngine/Interpreter.h" +#include "llvm/ExecutionEngine/ExecutionEngine.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Verifier.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Support/raw_ostream.h" + #include <iostream> #include <vector> @@ -33,11 +48,16 @@ void Compiler::Compile() { return; Results.push_back(std::move(R)); } -// + // for (auto &&R : Results) // F.format(R.getRoot(), OS); irgen::IRGenerator IRGen(Engine); - IRGen.gen(Results.front().getRoot()); + auto Module = IRGen.gen(Results.front().getRoot()); + if (!Module) + return; + + Module->print(llvm::errs(), nullptr); + llvm::verifyModule(*Module); std::cout << std::endl; } diff --git a/lib/IRGen/GenStmt.cpp b/lib/IRGen/GenStmt.cpp index 864cfac9921499bedc9e4217a92d934d7d7d1ee6..7f229ea0297a405b319ad2a342a7928c296c442e 100644 --- a/lib/IRGen/GenStmt.cpp +++ b/lib/IRGen/GenStmt.cpp @@ -148,6 +148,8 @@ bool irgen::codegenStmt(Context &Ctx, Scope &Scp, Stmt *S) { return codegenStmt(Ctx, Scp, static_cast<WhileStmt *>(S)); case StmtKind::If: return codegenStmt(Ctx, Scp, static_cast<IfStmt *>(S)); + case StmtKind::Extern: + return true; case StmtKind::Subscript: llvm_unreachable("Not implemented yet"); } diff --git a/lib/IRGen/IRGenerator.cpp b/lib/IRGen/IRGenerator.cpp index 3f5971714f49373b21482b2e5a4de207dedf5833..55bb3ddcedc1e3236226cb26a5bf1b1e2ca40701 100644 --- a/lib/IRGen/IRGenerator.cpp +++ b/lib/IRGen/IRGenerator.cpp @@ -27,7 +27,7 @@ IRGenerator::~IRGenerator() {} -bool IRGenerator::gen(ModuleDecl *M) { +llvm::Module *IRGenerator::gen(ModuleDecl *M) { Module = std::make_unique<llvm::Module>(M->getName(), Ctx); Context Ctx(this->Ctx, Module.get(), Builder); Scope Scp; @@ -44,10 +44,9 @@ bool IRGenerator::gen(ModuleDecl *M) { llvm_unreachable("Unexpected node"); if (!R) - return false; + return nullptr; } - Module->print(llvm::errs(), nullptr); - return true; + return Module.release(); } bool IRGenerator::prepareGlobals(Context &Ctx, ModuleDecl *M) { @@ -58,6 +57,9 @@ bool IRGenerator::prepareGlobals(Context &Ctx, ModuleDecl *M) { if (auto Fn = dynamic_cast<FuncStmt *>(N)) if (!codegenDecl(Ctx, Fn->getPrototype())) return false; + if (auto Fn = dynamic_cast<ExternStmt *>(N)) + if (!codegenDecl(Ctx, Fn->getPrototype())) + return false; } return true; } diff --git a/lib/Parser/Lexer.cpp b/lib/Parser/Lexer.cpp index 219f9cb83cb22c64d9a43c78dfe953b178444f09..3041ff5aad6ab827f9fb491df14b05d0ad1274bb 100644 --- a/lib/Parser/Lexer.cpp +++ b/lib/Parser/Lexer.cpp @@ -286,8 +286,9 @@ tok Lexer::kindOfIdentifier(StringRef Str) { .Case("for", tok::kwFor) .Case("in", tok::kwIn) .Case("func", tok::kwFunc) - .Case("writeln", tok::kwWriteln) + .Case("println", tok::kwPrintln) .Case("readln", tok::kwReadln) + .Case("extern", tok::kwExtern) .Case("Void", tok::kwVoid) .Case("Int", tok::kwInt) .Default(tok::identifier); diff --git a/lib/Parser/ParseDecl.cpp b/lib/Parser/ParseDecl.cpp index 3fc8f854795899791b2a2f7c7c94167a44cce9c3..807d8144671545eb4c0ee4077fc2d2c37f7ad726 100644 --- a/lib/Parser/ParseDecl.cpp +++ b/lib/Parser/ParseDecl.cpp @@ -92,7 +92,7 @@ Decl *Parser::parseFuncDecl() { /// '->' 'Int' | 'Void' FuncRetType *Parser::parseFuncDeclType() { // Implicit return type is `Void` - if (Tok.is(tok::l_brace)) + if (Tok.isAny(tok::l_brace, tok::semicolon)) return nullptr; if (!consumeIf(tok::arrow)) { diff --git a/lib/Parser/ParseStmt.cpp b/lib/Parser/ParseStmt.cpp index c89eac58fa119d61b092a048a4702ba7464424aa..b1349512ab05df261ca5d8a223864dfcf3747c76 100644 --- a/lib/Parser/ParseStmt.cpp +++ b/lib/Parser/ParseStmt.cpp @@ -159,15 +159,38 @@ ASTNode *Parser::parseBlockBody() { } } +/// ExternStmt ::= +/// 'extern' 'func' indentifier +Stmt *Parser::parseExterStmt() { + // Validate `exter` keyword + assert(Tok.is(tok::kwExtern) && "Invalid parse method"); + auto EL = consumeToken(); + if (Tok.isNot(tok::kwFunc)) { + diagnose(Tok.getLoc(), diag::DiagID::expected_func_kw) + .fixItBefore("func", Tok.getLoc()); + return nullptr; + } + + auto D = make<ExternStmt>(EL, parseFuncDecl()); + if (consumeIf(tok::semicolon)) + return D; + + diagnose(Tok.getLoc(), diag::DiagID::expected_semicolon) + .fixItBefore(";", Tok.getLoc()); + return nullptr; +} + +/// FuncStmt ::= +/// 'func' identifier '(' Args ')' RetType Block Stmt *Parser::parseFuncStmt() { // Validate `func` keyword assert(Tok.is(tok::kwFunc) && "Invalid parse method"); auto D = parseFuncDecl(); if (Tok.is(tok::l_brace)) return make<FuncStmt>(D, parseBlock()); + diagnose(Tok.getLoc(), diag::DiagID::expected_l_brace) .fixIt("{", Tok.getLoc()); - return nullptr; } diff --git a/lib/Parser/Parser.cpp b/lib/Parser/Parser.cpp index 05a273fa1cc0c289b0c04074bee0ff2835402d72..42e6bd07db7c7d0a90ca27a237699f8f485482ea 100644 --- a/lib/Parser/Parser.cpp +++ b/lib/Parser/Parser.cpp @@ -69,6 +69,8 @@ ASTNode *Parser::parseGlobal() { return parseVarDecl(); case tok::kwLet: return parseConstDecl(); + case tok::kwExtern: + return parseExterStmt(); case tok::kwFunc: return parseFuncStmt(); case tok::kwFor: