diff options
| -rw-r--r-- | .gitignore | 3 | ||||
| -rw-r--r-- | CMakeLists.txt | 22 | ||||
| -rw-r--r-- | include/cps/AST.h | 63 | ||||
| -rw-r--r-- | include/cps/CodeGen.h | 32 | ||||
| -rw-r--r-- | include/cps/Lexer.h | 27 | ||||
| -rw-r--r-- | include/cps/Parser.h | 25 | ||||
| -rw-r--r-- | lib/CodeGen/CodeGen.cc | 102 | ||||
| -rw-r--r-- | lib/Lexer/Lexer.cc | 57 | ||||
| -rw-r--r-- | lib/Parser/Parser.cc | 77 | ||||
| -rw-r--r-- | tools/driver/main.cc | 17 |
10 files changed, 425 insertions, 0 deletions
@@ -39,3 +39,6 @@ # debug information files *.dwo + +build/ +.vscode/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..a2342e1 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,22 @@ +cmake_minimum_required(VERSION 3.13) +project(cps_compiler) + +find_package(LLVM REQUIRED CONFIG) +message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") +message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") + +include_directories(${LLVM_INCLUDE_DIRS}) +add_definitions(${LLVM_DEFINITIONS}) + +set(CMAKE_CXX_STANDARD 17) + +include_directories(include) + +add_library(CPSLexer lib/Lexer/Lexer.cc) +add_library(CPSParser lib/Parser/Parser.cc) +add_library(CPSCodeGen lib/CodeGen/CodeGen.cc) + +llvm_map_components_to_libnames(llvm_libs core support native irreader) + +add_executable(cpsc tools/driver/main.cc) +target_link_libraries(cpsc CPSCodeGen CPSParser CPSLexer ${llvm_libs}) diff --git a/include/cps/AST.h b/include/cps/AST.h new file mode 100644 index 0000000..5b28e8a --- /dev/null +++ b/include/cps/AST.h @@ -0,0 +1,63 @@ +#pragma once +#include <string> +#include <memory> +#include <vector> + +namespace cps { + +class ExprAST { +public: + virtual ~ExprAST() = default; +}; + +class NumberExprAST : public ExprAST { + int Val; +public: + NumberExprAST(int Val) : Val(Val) {} + int getVal() const { return Val; } +}; + +class VariableExprAST : public ExprAST { + std::string Name; +public: + VariableExprAST(const std::string &Name) : Name(Name) {} + const std::string &getName() const { return Name; } +}; + +class StmtAST { +public: + virtual ~StmtAST() = default; +}; + +class DeclareStmtAST : public StmtAST { + std::string Name; +public: + DeclareStmtAST(const std::string &Name) : Name(Name) {} + const std::string &getName() const { return Name; } +}; + +class AssignStmtAST : public StmtAST { + std::string Name; + std::unique_ptr<ExprAST> Expr; +public: + AssignStmtAST(const std::string &Name, std::unique_ptr<ExprAST> Expr) + : Name(Name), Expr(std::move(Expr)) {} + const std::string &getName() const { return Name; } + ExprAST *getExpr() const { return Expr.get(); } +}; + +class InputStmtAST : public StmtAST { + std::string Name; +public: + InputStmtAST(const std::string &Name) : Name(Name) {} + const std::string &getName() const { return Name; } +}; + +class OutputStmtAST : public StmtAST { + std::unique_ptr<ExprAST> Expr; +public: + OutputStmtAST(std::unique_ptr<ExprAST> Expr) : Expr(std::move(Expr)) {} + ExprAST *getExpr() const { return Expr.get(); } +}; + +} // namespace cps diff --git a/include/cps/CodeGen.h b/include/cps/CodeGen.h new file mode 100644 index 0000000..2621795 --- /dev/null +++ b/include/cps/CodeGen.h @@ -0,0 +1,32 @@ +#pragma once +#include "cps/AST.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include <map> + +namespace cps { + +class CodeGen { + std::unique_ptr<llvm::LLVMContext> TheContext; + std::unique_ptr<llvm::Module> TheModule; + std::unique_ptr<llvm::IRBuilder<>> Builder; + std::map<std::string, llvm::AllocaInst*> NamedValues; + + llvm::AllocaInst *CreateEntryBlockAlloca(llvm::Function *TheFunction, + const std::string &VarName); + + llvm::FunctionCallee PrintfFunc; + llvm::FunctionCallee ScanfFunc; + llvm::Value *PrintfFormatStr; + llvm::Value *ScanfFormatStr; + + void SetupExternalFunctions(); + +public: + CodeGen(); + void compile(const std::vector<std::unique_ptr<StmtAST>> &Statements); + void print(); +}; + +} // namespace cps diff --git a/include/cps/Lexer.h b/include/cps/Lexer.h new file mode 100644 index 0000000..0ad5609 --- /dev/null +++ b/include/cps/Lexer.h @@ -0,0 +1,27 @@ +#pragma once +#include <string> +#include <iostream> + +namespace cps { + +enum Token { + tok_eof = -1, + tok_declare = -2, + tok_integer_kw = -3, // INTEGER + tok_input = -4, + tok_output = -5, + tok_identifier = -6, + tok_number = -7, + tok_assign = -8, // <- + tok_colon = -9 // : +}; + +class Lexer { +public: + std::string IdentifierStr; + int NumVal; + + int gettok(); +}; + +} // namespace cps diff --git a/include/cps/Parser.h b/include/cps/Parser.h new file mode 100644 index 0000000..c24184f --- /dev/null +++ b/include/cps/Parser.h @@ -0,0 +1,25 @@ +#pragma once +#include "cps/Lexer.h" +#include "cps/AST.h" +#include <vector> +#include <memory> + +namespace cps { + +class Parser { + Lexer &Lex; + int CurTok; + + int getNextToken(); + std::unique_ptr<ExprAST> ParseExpression(); + std::unique_ptr<ExprAST> ParseNumberExpr(); + std::unique_ptr<ExprAST> ParseParenExpr(); // 预留 + std::unique_ptr<ExprAST> ParseIdentifierExpr(); + +public: + Parser(Lexer &L) : Lex(L) { getNextToken(); } + + std::vector<std::unique_ptr<StmtAST>> Parse(); +}; + +} // namespace cps diff --git a/lib/CodeGen/CodeGen.cc b/lib/CodeGen/CodeGen.cc new file mode 100644 index 0000000..a2de939 --- /dev/null +++ b/lib/CodeGen/CodeGen.cc @@ -0,0 +1,102 @@ +#include "cps/CodeGen.h" +#include "llvm/IR/Verifier.h" + +using namespace llvm; +using namespace cps; + +CodeGen::CodeGen() { + TheContext = std::make_unique<LLVMContext>(); + TheModule = std::make_unique<Module>("cps_module", *TheContext); + Builder = std::make_unique<IRBuilder<>>(*TheContext); + + SetupExternalFunctions(); +} + +void CodeGen::SetupExternalFunctions() { + // printf(i8*, ...) + std::vector<Type*> PrintfArgs; + PrintfArgs.push_back(PointerType::getUnqual(*TheContext)); + FunctionType *PrintfType = FunctionType::get(Type::getInt32Ty(*TheContext), PrintfArgs, true); + PrintfFunc = TheModule->getOrInsertFunction("printf", PrintfType); + + // scanf(i8*, ...) + FunctionType *ScanfType = FunctionType::get(Type::getInt32Ty(*TheContext), PrintfArgs, true); + ScanfFunc = TheModule->getOrInsertFunction("scanf", ScanfType); + + // format %d\n and %d + PrintfFormatStr = Builder->CreateGlobalStringPtr("%d\n", "fmt_nl", 0, TheModule.get()); + ScanfFormatStr = Builder->CreateGlobalStringPtr("%d", "fmt_in", 0, TheModule.get()); +} + +AllocaInst *CodeGen::CreateEntryBlockAlloca(Function *TheFunction, const std::string &VarName) { + IRBuilder<> TmpB(&TheFunction->getEntryBlock(), TheFunction->getEntryBlock().begin()); + return TmpB.CreateAlloca(Type::getInt32Ty(*TheContext), nullptr, VarName); +} + +void CodeGen::compile(const std::vector<std::unique_ptr<StmtAST>> &Statements) { + FunctionType *FT = FunctionType::get(Type::getInt32Ty(*TheContext), false); + Function *F = Function::Create(FT, Function::ExternalLinkage, "main", TheModule.get()); + BasicBlock *BB = BasicBlock::Create(*TheContext, "entry", F); + Builder->SetInsertPoint(BB); + + for (const auto &Stmt : Statements) { + // DECLARE + if (auto *Decl = dynamic_cast<DeclareStmtAST*>(Stmt.get())) { + AllocaInst *Alloca = CreateEntryBlockAlloca(F, Decl->getName()); + NamedValues[Decl->getName()] = Alloca; + // 0 + Builder->CreateStore(ConstantInt::get(*TheContext, APInt(32, 0)), Alloca); + } + // ASSIGNMENT (x <- expr) + else if (auto *Assign = dynamic_cast<AssignStmtAST*>(Stmt.get())) { + Value *Val = nullptr; + if (auto *Num = dynamic_cast<NumberExprAST*>(Assign->getExpr())) { + Val = ConstantInt::get(*TheContext, APInt(32, Num->getVal())); + } else if (auto *Var = dynamic_cast<VariableExprAST*>(Assign->getExpr())) { + AllocaInst *A = NamedValues[Var->getName()]; + Val = Builder->CreateLoad(Type::getInt32Ty(*TheContext), A, Var->getName()); + } + + AllocaInst *Alloca = NamedValues[Assign->getName()]; + if (Alloca && Val) { + Builder->CreateStore(Val, Alloca); + } + } + // INPUT + else if (auto *In = dynamic_cast<InputStmtAST*>(Stmt.get())) { + AllocaInst *Alloca = NamedValues[In->getName()]; + if (Alloca) { + std::vector<Value*> Args; + Args.push_back(ScanfFormatStr); + Args.push_back(Alloca); + Builder->CreateCall(ScanfFunc, Args); + } + } + // OUTPUT + else if (auto *Out = dynamic_cast<OutputStmtAST*>(Stmt.get())) { + Value *Val = nullptr; + if (auto *Num = dynamic_cast<NumberExprAST*>(Out->getExpr())) { + Val = ConstantInt::get(*TheContext, APInt(32, Num->getVal())); + } else if (auto *Var = dynamic_cast<VariableExprAST*>(Out->getExpr())) { + AllocaInst *A = NamedValues[Var->getName()]; + Val = Builder->CreateLoad(Type::getInt32Ty(*TheContext), A, Var->getName()); + } + + if (Val) { + // printf("%d\n", val) + std::vector<Value*> Args; + Args.push_back(PrintfFormatStr); + Args.push_back(Val); + Builder->CreateCall(PrintfFunc, Args); + } + } + } + + // main return 0 + Builder->CreateRet(ConstantInt::get(*TheContext, APInt(32, 0))); + verifyFunction(*F); +} + +void CodeGen::print() { + TheModule->print(errs(), nullptr); +} diff --git a/lib/Lexer/Lexer.cc b/lib/Lexer/Lexer.cc new file mode 100644 index 0000000..b04438b --- /dev/null +++ b/lib/Lexer/Lexer.cc @@ -0,0 +1,57 @@ +#include "cps/Lexer.h" +#include <cctype> +#include <cstdio> + +using namespace cps; + +int Lexer::gettok() { + static int LastChar = ' '; + + while (isspace(LastChar)) + LastChar = getchar(); + + if (isalpha(LastChar)) { + IdentifierStr = LastChar; + while (isalnum((LastChar = getchar())) || LastChar == '_') + IdentifierStr += LastChar; + + if (IdentifierStr == "DECLARE") return tok_declare; + if (IdentifierStr == "INTEGER") return tok_integer_kw; + if (IdentifierStr == "INPUT") return tok_input; + if (IdentifierStr == "OUTPUT") return tok_output; + return tok_identifier; + } + + if (isdigit(LastChar)) { + std::string NumStr; + do { + NumStr += LastChar; + LastChar = getchar(); + } while (isdigit(LastChar)); + NumVal = strtol(NumStr.c_str(), nullptr, 10); + return tok_number; + } + + if (LastChar == '<') { + LastChar = getchar(); + if (LastChar == '-') { + LastChar = getchar(); + return tok_assign; + } + return '<'; + } + + // for comments + + if (LastChar == ':') { + LastChar = getchar(); + return tok_colon; + } + + if (LastChar == EOF) + return tok_eof; + + int ThisChar = LastChar; + LastChar = getchar(); + return ThisChar; +} diff --git a/lib/Parser/Parser.cc b/lib/Parser/Parser.cc new file mode 100644 index 0000000..90dc3a0 --- /dev/null +++ b/lib/Parser/Parser.cc @@ -0,0 +1,77 @@ +#include "cps/Parser.h" + +using namespace cps; + +int Parser::getNextToken() { + return CurTok = Lex.gettok(); +} + +std::unique_ptr<ExprAST> Parser::ParseNumberExpr() { + auto Result = std::make_unique<NumberExprAST>(Lex.NumVal); + getNextToken(); + return Result; +} + +std::unique_ptr<ExprAST> Parser::ParseIdentifierExpr() { + std::string IdName = Lex.IdentifierStr; + getNextToken(); + return std::make_unique<VariableExprAST>(IdName); +} + +std::unique_ptr<ExprAST> Parser::ParseExpression() { + if (CurTok == tok_number) + return ParseNumberExpr(); + if (CurTok == tok_identifier) + return ParseIdentifierExpr(); + return nullptr; +} + +std::vector<std::unique_ptr<StmtAST>> Parser::Parse() { + std::vector<std::unique_ptr<StmtAST>> Statements; + + while (CurTok != tok_eof) { + if (CurTok == tok_declare) { + // DECLARE <id> : INTEGER + getNextToken(); // eat DECLARE + if (CurTok != tok_identifier) break; // Error + std::string Name = Lex.IdentifierStr; + getNextToken(); // eat Name + + if (CurTok != tok_colon) break; // Error: expected ':' + getNextToken(); // eat ':' + + if (CurTok != tok_integer_kw) break; // Error: expected INTEGER + getNextToken(); // eat INTEGER + + Statements.push_back(std::make_unique<DeclareStmtAST>(Name)); + } + else if (CurTok == tok_identifier) { + // <id> <- <expr> + std::string Name = Lex.IdentifierStr; + getNextToken(); // eat Name + + if (CurTok != tok_assign) break; // Error: expected '<-' + getNextToken(); // eat '<-' + + auto Expr = ParseExpression(); + Statements.push_back(std::make_unique<AssignStmtAST>(Name, std::move(Expr))); + } + else if (CurTok == tok_input) { + // INPUT <id> + getNextToken(); // eat INPUT + if (CurTok != tok_identifier) break; + Statements.push_back(std::make_unique<InputStmtAST>(Lex.IdentifierStr)); + getNextToken(); + } + else if (CurTok == tok_output) { + // OUTPUT <expr> + getNextToken(); // eat OUTPUT + auto Expr = ParseExpression(); + Statements.push_back(std::make_unique<OutputStmtAST>(std::move(Expr))); + } + else { + getNextToken(); // Skip unknown + } + } + return Statements; +} diff --git a/tools/driver/main.cc b/tools/driver/main.cc new file mode 100644 index 0000000..e83a408 --- /dev/null +++ b/tools/driver/main.cc @@ -0,0 +1,17 @@ +#include "cps/Lexer.h" +#include "cps/Parser.h" +#include "cps/CodeGen.h" + +int main() { + cps::Lexer Lex; + + cps::Parser Parser(Lex); + auto Statements = Parser.Parse(); + + cps::CodeGen CG; + CG.compile(Statements); + + CG.print(); + + return 0; +} |
