diff --git a/.gitignore b/.gitignore index 403fcd7..7230abe 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ cmake-build-debug .idea .DS_store +/third-party/ +/cmake-build-debug-vm/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 209b77d..4e7ec61 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,3 +1,10 @@ + +#Required Env's, example: +#set(ENV{CMAKE_INCLUDE_PATH} "/usr/local/opt/flex/include:ENV{CMAKE_INCLUDE_PATH}") +#set(ENV{CMAKE_LIBRARY_PATH} "/usr/local/opt/flex/lib;/usr/local/opt/bison/lib:ENV{CMAKE_LIBRARY_PATH}") +#set(ENV{PATH} "/usr/local/opt/flex/bin:/usr/local/opt/bison/bin:ENV{PATH}") +#set(ENV{LLVM_DIR} "/usr/local/Cellar/llvm@11/lib") + cmake_minimum_required(VERSION 3.10) add_subdirectory(third-party/yaml-cpp EXCLUDE_FROM_ALL) @@ -6,7 +13,8 @@ add_subdirectory(third-party/googletest EXCLUDE_FROM_ALL) project(compiler) -set(CMAKE_CXX_CLANG_TIDY clang-tidy -checks=-*,readability-*,-warnings-as-errors=*) +# Sad :( +#set(CMAKE_CXX_CLANG_TIDY clang-tidy -checks=-*,readability-*,warnings-as-errors=*,) set(CMAKE_CXX_STANDARD 20) set(COMPILE_FLAGS -Wall -Wextra -pedantic -Werror) @@ -17,6 +25,8 @@ add_subdirectory(visitors) add_subdirectory(symbol_table) add_subdirectory(scope_table) add_subdirectory(types) +add_subdirectory(ir_builder) +add_subdirectory(jit) add_library( @@ -24,7 +34,7 @@ add_library( INTERFACE ) -target_link_libraries(Compiler INTERFACE Driver Visitors SymbolTable ScopeTable Types) +target_link_libraries(Compiler INTERFACE Driver Visitors SymbolTable ScopeTable Types IRBuilder JIT) add_executable( CompilerExe diff --git a/ast_components/ast_forward_declaration.hh b/ast_components/ast_forward_declaration.hh index 5d5a9bf..8276461 100644 --- a/ast_components/ast_forward_declaration.hh +++ b/ast_components/ast_forward_declaration.hh @@ -10,6 +10,7 @@ class TClassDeclaration; class TClassMemberDeclarationList; class TDivExpression; class TEqExpression; +class TNEqExpression; class TExpressionList; class TFieldInvocation; class TFieldInvocationExpression; @@ -31,7 +32,6 @@ class TMethodInvocationExpression; class TMethodInvocationStatement; class TModExpression; class TMulExpression; -class TNewArrayExpression; class TNewExpression; class TNotExpression; class TOrExpression; @@ -51,5 +51,6 @@ class TIntTypeNode; class TBooleanExpression; class TVoidTypeNode; class TIdentifierTypeNode; +class TArrayTypeNode; #endif//COMPILER_AST_FORWARD_DECLARATION_HH diff --git a/ast_components/declarations/variable.hh b/ast_components/declarations/variable.hh index 551799b..e363d5e 100644 --- a/ast_components/declarations/variable.hh +++ b/ast_components/declarations/variable.hh @@ -1,9 +1,9 @@ #ifndef COMPILER_VARIABLE_HH #define COMPILER_VARIABLE_HH +#include #include #include -#include #include "types/type.hh" diff --git a/ast_components/expressions/binary_expressions.hh b/ast_components/expressions/binary_expressions.hh index 67db605..1d1c99f 100644 --- a/ast_components/expressions/binary_expressions.hh +++ b/ast_components/expressions/binary_expressions.hh @@ -120,6 +120,7 @@ class TEqExpression : public TBinaryExpression { public: explicit TEqExpression(TExpressionPtr&& lhs, TExpressionPtr&& rhs) : TBinaryExpression(std::move(lhs), std::move(rhs)) {} + void Accept(IVisitor* visitor) override { visitor->Visit(this); } @@ -129,6 +130,16 @@ public: using TEqExpressionPtr = std::unique_ptr; +class TNEqExpression : public TBinaryExpression { +public: + explicit TNEqExpression(TExpressionPtr&& lhs, TExpressionPtr&& rhs) + : TBinaryExpression(std::move(lhs), std::move(rhs)) {} + + void Accept(IVisitor* visitor) override { + visitor->Visit(this); + } +}; + class TLeExpression : public TBinaryExpression { public: explicit TLeExpression(TExpressionPtr&& lhs, TExpressionPtr&& rhs) diff --git a/ast_components/expressions/expressions.hh b/ast_components/expressions/expressions.hh index aca1429..81ab1dc 100644 --- a/ast_components/expressions/expressions.hh +++ b/ast_components/expressions/expressions.hh @@ -34,7 +34,7 @@ public: visitor->Visit(this); } - TExpression* Expression() const { + [[nodiscard]] TExpression* Expression() const { return Expression_.get(); } @@ -54,11 +54,11 @@ public: visitor->Visit(this); } - TExpression* Expression() const { + [[nodiscard]] TExpression* Expression() const { return Expression_.get(); } - TExpression* Index() const { + [[nodiscard]] TExpression* Index() const { return Index_.get(); } @@ -93,7 +93,7 @@ public: visitor->Visit(this); } - TTypeNode* Type() const { + [[nodiscard]] TTypeNode* Type() const { return Type_.get(); } @@ -109,15 +109,16 @@ class TNewArrayExpression : public TExpression { public: explicit TNewArrayExpression(TTypeNodePtr&& type, TExpressionPtr&& size) : Type_(std::move(type)), Size_(std::move(size)) {} + void Accept(IVisitor* visitor) override { visitor->Visit(this); } - TTypeNode* Type() const { + [[nodiscard]] TTypeNode* Type() const { return Type_.get(); } - TExpression* Size() const { + [[nodiscard]] TExpression* Size() const { return Size_.get(); } @@ -137,7 +138,7 @@ public: visitor->Visit(this); } - const std::string& Identifier() const { + [[nodiscard]] const std::string& Identifier() const { return Identifier_; } @@ -153,7 +154,7 @@ class TIntExpression : public TExpression { public: explicit TIntExpression(int value) : Value_(value) {} - int GetValue() const { + [[nodiscard]] int GetValue() const { return Value_; } void Accept(IVisitor* visitor) override { @@ -184,9 +185,10 @@ class TBooleanExpression : public TExpression { public: explicit TBooleanExpression(bool value) : Value_(value) {} - bool GetValue() const { + [[nodiscard]] bool GetValue() const { return Value_; } + void Accept(IVisitor* visitor) override { visitor->Visit(this); } @@ -219,17 +221,17 @@ using TMethodInvocationExpressionPtr = std::unique_ptrVisit(this); } - TFieldInvocation* Method() { - return Method_.get(); + TFieldInvocation* Invocation() { + return Invocation_.get(); } private: - TFieldInvocationPtr Method_; + TFieldInvocationPtr Invocation_; }; using TMethodInvocationExpressionPtr = std::unique_ptr; diff --git a/ast_components/invocation.hh b/ast_components/invocation.hh index 1a3a126..c3a1c62 100644 --- a/ast_components/invocation.hh +++ b/ast_components/invocation.hh @@ -3,6 +3,7 @@ #include #include +#include #include "expressions/expression_base.hh" #include "i_node.hh" @@ -39,16 +40,23 @@ using TMethodInvocationPtr = std::unique_ptr; class TFieldInvocation : public INode { public: - explicit TFieldInvocation(std::string&& identifier) : Identifier_(identifier) {} + explicit TFieldInvocation(TExpressionPtr&& expression, std::string identifier) + : Expression_(std::move(expression)), Identifier_(std::move(identifier)) {} + void Accept(IVisitor* visitor) override { visitor->Visit(this); } + [[nodiscard]] TExpression* Expression() const { + return Expression_.get(); + } + [[nodiscard]] const std::string& Identifier() const { return Identifier_; } private: + TExpressionPtr Expression_; std::string Identifier_; }; @@ -56,6 +64,7 @@ private: using TFieldInvocationPtr = std::unique_ptr; +/* class TFieldInvocationIndexed : public TFieldInvocation { public: explicit TFieldInvocationIndexed(std::string&& identifier, TExpressionPtr&& index) @@ -74,5 +83,6 @@ private: using TFieldInvocationIndexedPtr = std::unique_ptr; + */ #endif//COMPILER_INVOCATION_HH diff --git a/ast_components/types/type.hh b/ast_components/types/type.hh index 2e7a738..8a80701 100644 --- a/ast_components/types/type.hh +++ b/ast_components/types/type.hh @@ -6,19 +6,7 @@ #include "i_node.hh" -class TTypeNode : public INode { -public: - void MakeArray() { - IsArray_ = true; - } - - [[nodiscard]] bool IsArray() const { - return IsArray_; - } - -private: - bool IsArray_ = false; -}; +class TTypeNode : public INode {}; using TTypeNodePtr = std::unique_ptr; @@ -75,4 +63,21 @@ private: using TIdentifierTypeNodePtr = std::unique_ptr; + +class TArrayTypeNode : public TTypeNode { +public: + explicit TArrayTypeNode(TTypeNodePtr&& type) : Type_(std::move(type)) {} + + void Accept(IVisitor* visitor) override { + visitor->Visit(this); + } + + [[nodiscard]] TTypeNode* Type() const { + return Type_.get(); + } + +private: + TTypeNodePtr Type_; +}; + #endif//COMPILER_TYPE_HH diff --git a/driver/CMakeLists.txt b/driver/CMakeLists.txt index c149e66..a37b031 100644 --- a/driver/CMakeLists.txt +++ b/driver/CMakeLists.txt @@ -1,12 +1,6 @@ cmake_minimum_required(VERSION 3.10) -if (${APPLE}) - set(ENV{CMAKE_INCLUDE_PATH} "/usr/local/opt/flex/include:ENV{CMAKE_INCLUDE_PATH}") - set(ENV{CMAKE_LIBRARY_PATH} "/usr/local/opt/flex/lib;/usr/local/opt/bison/lib:ENV{CMAKE_LIBRARY_PATH}") - set(ENV{PATH} "/usr/local/opt/flex/bin:/usr/local/opt/bison/bin:ENV{PATH}") -endif () - find_package(BISON 3.5 REQUIRED) BISON_TARGET( Parser diff --git a/driver/parser.y b/driver/parser.y index 2555b4f..01d9ebc 100644 --- a/driver/parser.y +++ b/driver/parser.y @@ -48,9 +48,10 @@ PERCENT "%" GT ">" LT "<" - GE "=>" + GE ">=" LE "<=" EQ "==" + NEQ "!=" NOT "!" AND "&&" @@ -243,8 +244,7 @@ simple_type: array_type: simple_type "[]" { - $$ = std::move($1); - $$->MakeArray(); + $$ = std::make_unique(std::move($1)); } @@ -260,7 +260,7 @@ type_identifier: %right "||"; %right "&&"; %precedence "!"; -%left ">" "<" "<=" ">=" "=="; +%left ">" "<" "<=" ">=" "==" "!="; %left "+" "-"; %left "*" "/" "%"; %left UMINUS; @@ -293,6 +293,9 @@ method_invocation: expr "." IDENTIFIER "(" expr_comma_separated_list ")" { $$ = std::make_unique(std::move($1), std::move($3), std::move($5)); } + | IDENTIFIER "(" expr_comma_separated_list ")" { + $$ = std::make_unique(std::make_unique(), std::move($1), std::move($3)); + } expr_comma_separated_list: @@ -317,10 +320,7 @@ expr_comma_separated_list_prefix: field_invocation: expr "." IDENTIFIER %prec NO_BRACKET { - $$ = std::make_unique(std::move($3)); - } - | expr "." IDENTIFIER "[" expr "]" { - $$ = std::make_unique(std::move($3), std::move($5)); + $$ = std::make_unique(std::move($1), std::move($3)); } @@ -333,6 +333,7 @@ expr: | expr ">=" expr {$$ = std::make_unique(std::move($1), std::move($3));} | expr "<=" expr {$$ = std::make_unique(std::move($1), std::move($3));} | expr "==" expr {$$ = std::make_unique(std::move($1), std::move($3));} + | expr "!=" expr {$$ = std::make_unique(std::move($1), std::move($3));} | expr "+" expr {$$ = std::make_unique(std::move($1), std::move($3));} | expr "-" expr {$$ = std::make_unique(std::move($1), std::move($3));} @@ -343,8 +344,8 @@ expr: | expr "[" expr "]" {$$ = std::make_unique(std::move($1), std::move($3));} | expr "." "length" {$$ = std::make_unique(std::move($1));} + | "new" simple_type "(" ")" {$$ = std::make_unique(std::move($2));} | "new" simple_type "[" expr "]" {$$ = std::make_unique(std::move($2), std::move($4));} - | "new" type_identifier "(" ")" {$$ = std::make_unique(std::move($2));} | "!" expr {$$ = std::make_unique(std::move($2));} | "(" expr ")" {$$ = std::move($2);} | IDENTIFIER {$$ = std::make_unique(std::move($1));} diff --git a/driver/scanner.l b/driver/scanner.l index a524f93..7a31a0e 100644 --- a/driver/scanner.l +++ b/driver/scanner.l @@ -29,6 +29,8 @@ id [a-zA-Z][a-zA-Z_0-9]* int [-+0-9][0-9]* blank [ \t\r] +%x LARGE_COMMENT +%x LINE_COMMENT %{ // Code run each time a pattern is matched. @@ -45,6 +47,21 @@ blank [ \t\r] {blank}+ loc.step (); \n+ loc.lines (yyleng); loc.step (); + +"/*" { BEGIN(LARGE_COMMENT); } +"*/" { BEGIN(INITIAL); } +\n+ { loc.lines(yyleng); loc.step(); } +. { loc.step(); } +"//" { BEGIN(LINE_COMMENT); } +\n { loc.lines(yyleng); loc.step(); BEGIN(INITIAL); } +. { loc.step(); } + + + +"<=" return yy::parser::make_LE(loc); +">=" return yy::parser::make_GE(loc); +"==" return yy::parser::make_EQ(loc); +"!=" return yy::parser::make_NEQ(loc); "-" return yy::parser::make_MINUS (loc); "+" return yy::parser::make_PLUS (loc); "*" return yy::parser::make_STAR (loc); @@ -52,6 +69,7 @@ blank [ \t\r] "%" return yy::parser::make_PERCENT (loc); "&&" return yy::parser::make_AND (loc); "||" return yy::parser::make_OR (loc); +"!" return yy::parser::make_NOT (loc); "(" return yy::parser::make_LPAREN (loc); ")" return yy::parser::make_RPAREN (loc); "{" return yy::parser::make_LEFTSCOPE(loc); @@ -59,16 +77,13 @@ blank [ \t\r] "[]" return yy::parser::make_DOUBLEBRACKET(loc); "[" return yy::parser::make_LBRACKET(loc); "]" return yy::parser::make_RBRACKET(loc); -"=" return yy::parser::make_ASSIGN (loc); "::" return yy::parser::make_DOUBLE_COLON(loc); "," return yy::parser::make_COMMA(loc); "'" return yy::parser::make_QUOTE(loc); "\"" return yy::parser::make_QUOTE(loc); -">" return yy::parser::make_GT(loc); "<" return yy::parser::make_LT(loc); -">=" return yy::parser::make_GE(loc); -"<=" return yy::parser::make_LE(loc); -"==" return yy::parser::make_EQ(loc); +">" return yy::parser::make_GT(loc); +"=" return yy::parser::make_ASSIGN (loc); "." return yy::parser::make_DOT(loc); ";" return yy::parser::make_SEMICOLON(loc); diff --git a/ir_builder/CMakeLists.txt b/ir_builder/CMakeLists.txt new file mode 100644 index 0000000..7a280d8 --- /dev/null +++ b/ir_builder/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.10) + + +find_package(LLVM 11.0.0 REQUIRED CONFIG) + +add_definitions(${LLVM_DEFINITIONS}) + +add_library(IRBuilder INTERFACE) + +target_include_directories(IRBuilder SYSTEM INTERFACE ${LLVM_INCLUDE_DIRS}) +llvm_map_components_to_libnames(llvm_libs + Core + Support + core + support) + +target_include_directories(IRBuilder SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) +target_link_libraries(IRBuilder INTERFACE Visitors) +target_link_libraries(IRBuilder INTERFACE SymbolTable) +target_link_libraries(IRBuilder INTERFACE ${llvm_libs}) diff --git a/ir_builder/base_builder.hh b/ir_builder/base_builder.hh new file mode 100644 index 0000000..20b9865 --- /dev/null +++ b/ir_builder/base_builder.hh @@ -0,0 +1,37 @@ +#pragma once + +#include + +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" + +#include "partial_visitor.hh" +#include "scope_table.hh" +#include "symbol_table.hh" +#include "value.hh" + +class TBaseBuilder : public TPartialVisitor { +private: + using ClassTableType = TSymbolTable; + using MemberTableType = TSymbolTable; + using FunctionTableType = TSymbolTable; + +public: + TBaseBuilder(llvm::LLVMContext& context, llvm::Module& Module) + : ClassTablePtr_(std::make_shared()), FunctionTablePtr_(std::make_shared()), + MemberTablePtr_(std::make_shared()), Context(context), Module(Module), + ClassTable(*ClassTablePtr_), FunctionTable(*FunctionTablePtr_), MemberTable(*MemberTablePtr_) {} + + +private: + std::shared_ptr ClassTablePtr_; + std::shared_ptr FunctionTablePtr_; + std::shared_ptr MemberTablePtr_; + +public: + llvm::LLVMContext& Context; + llvm::Module& Module; + ClassTableType& ClassTable; + FunctionTableType& FunctionTable; + MemberTableType& MemberTable; +}; diff --git a/ir_builder/class_declaration_builder.hh b/ir_builder/class_declaration_builder.hh new file mode 100644 index 0000000..489041a --- /dev/null +++ b/ir_builder/class_declaration_builder.hh @@ -0,0 +1,25 @@ +#pragma once + +#include "base_builder.hh" + + +class TClassesDeclarationBuilder : public TBaseBuilder { +public: + explicit TClassesDeclarationBuilder(TBaseBuilder& base) : TBaseBuilder(base) {} + + void Visit(struct TProgram* program) override { + program->ClassDeclarations()->Accept(this); + } + + void Visit(struct TClassDeclarationList* list) override { + for (auto& decl : *list) { + decl->Accept(this); + } + } + void Visit(struct TClassDeclaration* declaration) override { + assert(declaration->Extends() == "object");// TODO + auto StructPtr = llvm::StructType::create(Context, declaration->ClassName()); + StructPtr->setName(declaration->ClassName()); + ClassTable.Add(declaration->ClassName(), StructPtr); + } +}; diff --git a/ir_builder/class_realization_builder.hh b/ir_builder/class_realization_builder.hh new file mode 100644 index 0000000..8f0e8cc --- /dev/null +++ b/ir_builder/class_realization_builder.hh @@ -0,0 +1,53 @@ +#pragma once + +#include "base_builder.hh" +#include "type_resolver.hh" + + +class TClassRealizationBuilder : public TBaseBuilder { +public: + explicit TClassRealizationBuilder(TBaseBuilder& base, const std::string& name) + : TBaseBuilder(base), className_(name) {} + + void Visit(struct TClassMemberDeclarationList* list) override { + for (auto& decl : *list) { + decl->Accept(this); + } + + ClassTable.Get(className_)->setBody(Members_); + } + void Visit(struct TMemberMethodDeclaration* declaration) override { + // Do nothing + } + void Visit(struct TMemberVariableDeclaration* declaration) override { + auto type = TTypeResolver{*this}.Accept(declaration->Variable().Type()); + MemberTable.Add(className_ + "#" + declaration->Variable().Name(), Members_.size()); + Members_.push_back(type); + } + +private: + const std::string& className_; + std::vector Members_; +}; + +class TClassesRealizationBuilder : public TBaseBuilder { +public: + explicit TClassesRealizationBuilder(TBaseBuilder& base) : TBaseBuilder(base) {} + + void Visit(struct TProgram* program) override { + program->ClassDeclarations()->Accept(this); + } + + void Visit(struct TClassDeclarationList* list) override { + for (auto& decl : *list) { + decl->Accept(this); + } + } + + void Visit(struct TClassDeclaration* declaration) override { + assert(declaration->Extends() == "object");// TODO + TClassRealizationBuilder builder{*this, declaration->ClassName()}; + + declaration->Members()->Accept(&builder); + } +}; diff --git a/ir_builder/function_declaration_builder.hh b/ir_builder/function_declaration_builder.hh new file mode 100644 index 0000000..1f52ad4 --- /dev/null +++ b/ir_builder/function_declaration_builder.hh @@ -0,0 +1,73 @@ +#pragma once + + +#include "base_builder.hh" + +class TClassFunctionsDeclarationBuilder : public TBaseBuilder { +public: + TClassFunctionsDeclarationBuilder(TBaseBuilder& base, const std::string& name) + : TBaseBuilder(base), ClassName_(name) {} + + void Visit(struct TClassMemberDeclarationList* list) override { + for (auto& decl : *list) { + decl->Accept(this); + } + } + + void Visit(struct TMemberMethodDeclaration* declaration) override { + auto arguments = ConstructArguments_(declaration); + + llvm::FunctionType* funcType = + llvm::FunctionType::get(TypeResolver.Accept(declaration->ReturnType()), arguments, false); + + auto funcName = std::string(ClassName_) + "#" + declaration->Signature().Name(); + llvm::Function* func = llvm::Function::Create(funcType, llvm::Function::ExternalLinkage, funcName, Module); + + FunctionTable.Add(funcName, func); + } + + void Visit(struct TMemberVariableDeclaration* declaration) override { + // Do nothing + } + +private: + std::vector ConstructArguments_(TMemberMethodDeclaration* declaration) { + std::vector arguments; + + if (!declaration->IsStatic()) { + arguments.push_back(llvm::PointerType::get(ClassTable.Get(ClassName_), 0));// "this" argument + } + + for (auto& arg : declaration->Signature().Arguments()) { + arguments.push_back(TypeResolver.Accept(arg.Type())); + } + + return arguments; + } + +private: + const std::string& ClassName_; + TTypeResolver TypeResolver{*this}; +}; + + +class TFunctionsDeclarationBuilder : public TBaseBuilder { +public: + explicit TFunctionsDeclarationBuilder(TBaseBuilder& base) : TBaseBuilder(base) {} + + void Visit(struct TProgram* program) override { + program->ClassDeclarations()->Accept(this); + } + + void Visit(struct TClassDeclarationList* list) override { + for (auto& decl : *list) { + decl->Accept(this); + } + } + + void Visit(struct TClassDeclaration* declaration) override { + assert(declaration->Extends() == "object");// TODO + TClassFunctionsDeclarationBuilder builder{*this, declaration->ClassName()}; + declaration->Members()->Accept(&builder); + } +}; \ No newline at end of file diff --git a/ir_builder/function_realization_builder.hh b/ir_builder/function_realization_builder.hh new file mode 100644 index 0000000..b9a3be4 --- /dev/null +++ b/ir_builder/function_realization_builder.hh @@ -0,0 +1,234 @@ +#pragma once + +#include "llvm/IR/Attributes.h" +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Type.h" +#include "llvm/IR/Value.h" +#include "llvm/IR/Verifier.h" +#include + +#include + +#include "base_builder.hh" +#include "scope_table.hh" +#include "value_builder.hh" + +class TFunctionRealizationBuilder : public TBaseBuilder { +public: + explicit TFunctionRealizationBuilder(TBaseBuilder& base, const std::string& className, + const std::string& functionName) + : TBaseBuilder(base), ClassName_(className), Function_(FunctionTable.Get(functionName)) {} + + void Visit(struct TMemberMethodDeclaration* declaration) override { + ScopeTable_.BeginScope("Arguments"); + + BasicBlock_ = llvm::BasicBlock::Create(Context, "Entry", Function_); + ArgsToScope_(declaration); + + + declaration->Statements()->Accept(this); + + if (BasicBlock_->getTerminator() == nullptr) { + if (TTypeResolver{*this}.Accept(declaration->ReturnType())->isVoidTy()) { + llvm::ReturnInst::Create(Context, BasicBlock_); + } else { + throw std::logic_error{"No return statement"}; + } + } + + ScopeTable_.EndScope(); + + llvm::verifyFunction(*Function_); + } + + void Visit(struct TStatementList* list) override { + ScopeTable_.BeginScope(); + for (auto& stat : *list) { + stat->Accept(this); + } + ScopeTable_.EndScope(); + } + + + void Visit(struct TAssertionStatement* statement) override { + TValueBuilder ValueBuilder{*this, ClassName_, ScopeTable_, BasicBlock_}; + auto* result = ValueBuilder.Accept(statement->Expression())->Load(BasicBlock_); + if (result->getType() != llvm::Type::getInt1Ty(Context)) { + throw std::logic_error{"unexpected type"}; + } + + llvm::BasicBlock* FailBB = llvm::BasicBlock::Create(Context, "Fail", Function_); + llvm::BasicBlock* OkBB = llvm::BasicBlock::Create(Context, "Ok", Function_); + + + llvm::BranchInst::Create(OkBB, FailBB, result, BasicBlock_); + + llvm::FunctionType* exitType = + llvm::FunctionType::get(llvm::Type::getVoidTy(Context), {llvm::Type::getInt32Ty(Context)}, false); + + auto exitFunction = Module.getOrInsertFunction("exit", exitType); + + llvm::CallInst::Create(exitFunction, {llvm::ConstantInt::get(llvm::Type::getInt32Ty(Context), 1)}, "", FailBB); + + new llvm::UnreachableInst(Context, FailBB); + + BasicBlock_ = OkBB; + } + void Visit(struct TWhileStatement* statement) override { + TValueBuilder ValueBuilder{*this, ClassName_, ScopeTable_, BasicBlock_}; + auto* cond = ValueBuilder.Accept(statement->Condition())->Load(BasicBlock_); + + auto* bodyBB = llvm::BasicBlock::Create(Context, "body", Function_); + auto* continueBB = llvm::BasicBlock::Create(Context, "continue", Function_); + + llvm::BranchInst::Create(bodyBB, continueBB, cond, BasicBlock_); + + CheckoutBB(bodyBB); + statement->Statement()->Accept(this); + auto* cond2 = ValueBuilder.Accept(statement->Condition())->Load(BasicBlock_); + llvm::BranchInst::Create(bodyBB, continueBB, cond2, BasicBlock_); + + CheckoutBB(continueBB); + } + + void Visit(struct TReturnStatement* statement) override { + TValueBuilder ValueBuilder{*this, ClassName_, ScopeTable_, BasicBlock_}; + auto value = ValueBuilder.Accept(statement->Expression()); + llvm::ReturnInst::Create(Context, value->Load(BasicBlock_), BasicBlock_); + } + + void Visit(struct TAssignmentStatement* statement) override { + TValueBuilder ValueBuilder{*this, ClassName_, ScopeTable_, BasicBlock_}; + auto lvalue = ValueBuilder.Accept(statement->Lvalue()); + auto value = ValueBuilder.Accept(statement->Expression()); + + lvalue->Store(value.get(), BasicBlock_); + } + + void Visit(struct TVariableDeclarationStatement* statement) override { + TTypeResolver typeResolver{*this}; + + auto* type = typeResolver.Accept(statement->Variable().Type()); + auto* ptr = new llvm::AllocaInst(type, 0, statement->Variable().Name(), BasicBlock_); + ScopeTable_.AddVariable(statement->Variable().Name(), TLocalVariable::Create(ptr, type)); + } + + void Visit(struct TMethodInvocationStatement* statement) override { + TValueBuilder ValueBuilder{*this, ClassName_, ScopeTable_, BasicBlock_}; + ValueBuilder.Accept(statement->Method());// Ignore result :) + } + + void Visit(struct TIfElseStatement* statement) override { + TValueBuilder ValueBuilder{*this, ClassName_, ScopeTable_, BasicBlock_}; + auto* expr = ValueBuilder.Accept(statement->Condition())->Load(BasicBlock_); + auto* ifCase = llvm::BasicBlock::Create(Context, "if", Function_); + auto* elseCase = llvm::BasicBlock::Create(Context, "else", Function_); + auto* cont = llvm::BasicBlock::Create(Context, "continue", Function_); + + llvm::BranchInst::Create(ifCase, elseCase, expr, BasicBlock_); + + CheckoutBB(ifCase); + statement->Statement()->Accept(this); + CheckoutBB(elseCase); + statement->ElseStatement()->Accept(this); + CheckoutBB(cont); + } + + void Visit(struct TIfStatement* statement) override { + TValueBuilder ValueBuilder{*this, ClassName_, ScopeTable_, BasicBlock_}; + auto* expr = ValueBuilder.Accept(statement->Condition())->Load(BasicBlock_); + auto* ifCase = llvm::BasicBlock::Create(Context, "if", Function_); + auto* cont = llvm::BasicBlock::Create(Context, "continue", Function_); + + llvm::BranchInst::Create(ifCase, cont, expr, BasicBlock_); + + CheckoutBB(ifCase); + statement->Statement()->Accept(this); + CheckoutBB(cont); + } + +private: + void CheckoutBB(llvm::BasicBlock* newBB) { + if (!BasicBlock_->getTerminator()) { + llvm::BranchInst::Create(newBB, BasicBlock_); + } + BasicBlock_ = newBB; + } + + void ArgsToScope_(TMemberMethodDeclaration* declaration) { + size_t offset = 0; + auto& args = declaration->Signature().Arguments(); + if (args.size() < Function_->arg_size()) { + offset = 1; + auto* arg = Function_->getArg(0); + auto* type = arg->getType(); + + auto* allocaInst = new llvm::AllocaInst(type, 0, "this", BasicBlock_); + new llvm::StoreInst(arg, allocaInst, BasicBlock_); + ScopeTable_.AddVariable("this", TLocalVariable::Create(allocaInst, type)); + } + + for (size_t i = offset; i < Function_->arg_size(); ++i) { + auto* arg = Function_->getArg(i); + auto* type = arg->getType(); + auto& name = args[i - offset].Name(); + + auto* allocaInst = new llvm::AllocaInst(type, 0, name, BasicBlock_); + new llvm::StoreInst(arg, allocaInst, BasicBlock_); + ScopeTable_.AddVariable(name, TLocalVariable::Create(allocaInst, type)); + } + } + +private: + TScopeTable ScopeTable_; + const std::string& ClassName_; + llvm::Function* Function_; + llvm::BasicBlock* BasicBlock_ = nullptr; +}; + +class TClassFunctionsRealizationBuilder : public TBaseBuilder { +public: + explicit TClassFunctionsRealizationBuilder(TBaseBuilder& base, const std::string& name) + : TBaseBuilder(base), ClassName_(name) {} + + void Visit(struct TClassMemberDeclarationList* list) override { + for (auto& decl : *list) { + decl->Accept(this); + } + } + + void Visit(struct TMemberMethodDeclaration* declaration) override { + auto funcName = std::string(ClassName_) + "#" + declaration->Signature().Name(); + + TFunctionRealizationBuilder builder{*this, ClassName_, funcName}; + declaration->Accept(&builder); + } + + void Visit(struct TMemberVariableDeclaration* declaration) override { + // Do nothing + } + +private: + const std::string& ClassName_; +}; + +class TFunctionsRealizationBuilder : public TBaseBuilder { +public: + explicit TFunctionsRealizationBuilder(TBaseBuilder& base) : TBaseBuilder(base) {} + + void Visit(struct TProgram* program) override { + program->ClassDeclarations()->Accept(this); + } + + void Visit(struct TClassDeclarationList* list) override { + for (auto& decl : *list) { + decl->Accept(this); + } + } + void Visit(struct TClassDeclaration* declaration) override { + assert(declaration->Extends() == "object");// TODO + TClassFunctionsRealizationBuilder builder{*this, declaration->ClassName()}; + declaration->Members()->Accept(&builder); + } +}; diff --git a/ir_builder/program_builder.hh b/ir_builder/program_builder.hh new file mode 100644 index 0000000..ae089d2 --- /dev/null +++ b/ir_builder/program_builder.hh @@ -0,0 +1,25 @@ +#pragma once + +#include "partial_visitor.hh" + +#include "base_builder.hh" +#include "class_declaration_builder.hh" +#include "class_realization_builder.hh" +#include "function_declaration_builder.hh" +#include "function_realization_builder.hh" + +class TProgramBuilder : public TBaseBuilder { +public: + explicit TProgramBuilder(TBaseBuilder& base) : TBaseBuilder(base) {} + + template + explicit TProgramBuilder(Args&&... args) : TBaseBuilder(std::forward(args)...) {} + +private: + void Visit(struct TProgram* program) override { + TClassesDeclarationBuilder{*this}.Visit(program); + TClassesRealizationBuilder{*this}.Visit(program); + TFunctionsDeclarationBuilder{*this}.Visit(program); + TFunctionsRealizationBuilder{*this}.Visit(program); + } +}; \ No newline at end of file diff --git a/ir_builder/type_resolver.hh b/ir_builder/type_resolver.hh new file mode 100644 index 0000000..49203c1 --- /dev/null +++ b/ir_builder/type_resolver.hh @@ -0,0 +1,34 @@ +#pragma once + +#include "llvm/IR/Type.h" + +#include "base_builder.hh" +#include "template_visitor.hh" + +class TTypeResolver : public TTemplateVisitor { +public: + explicit TTypeResolver(TBaseBuilder& base) : Base(base) {} + + void Visit(struct TIntTypeNode* type) override { + Return(llvm::Type::getInt32Ty(Base.Context)); + } + + void Visit(struct TBooleanTypeNode* type) override { + Return(llvm::Type::getInt1Ty(Base.Context)); + } + + void Visit(struct TVoidTypeNode* type) override { + Return(llvm::Type::getVoidTy(Base.Context)); + } + + void Visit(struct TIdentifierTypeNode* type) override { + Return(llvm::PointerType::get(Base.ClassTable.Get(type->Identifier()), 0)); + } + + void Visit(struct TArrayTypeNode* node) override { + Return(llvm::PointerType::get(Accept(node->Type()), 0)); + } + +private: + TBaseBuilder& Base; +}; diff --git a/ir_builder/value.hh b/ir_builder/value.hh new file mode 100644 index 0000000..7cda148 --- /dev/null +++ b/ir_builder/value.hh @@ -0,0 +1,118 @@ +#pragma once + +#include +#include + +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Value.h" + + +class IValue { +public: + virtual llvm::Value* Load(llvm::BasicBlock* bb) = 0; + virtual void Store(IValue* value, llvm::BasicBlock* bb) = 0; + + virtual ~IValue() = default; +}; + + +using IValuePtr = std::shared_ptr; + + +class TLLVMValue : public IValue { +public: + static IValuePtr Create(llvm::Value* value) { + return std::make_shared(value); + } + + explicit TLLVMValue(llvm::Value* value) : Value_(value) {} + + llvm::Value* Load(llvm::BasicBlock* bb) override { + return Value_; + } + + void Store(IValue* value, llvm::BasicBlock* bb) override { + throw std::logic_error{"unable to store rvalue"}; + } + +private: + llvm::Value* Value_; +}; + + +using TLLVMValuePtr = std::shared_ptr; + + +class TLocalVariable : public IValue { +public: + static IValuePtr Create(llvm::Value* ptr, llvm::Type* type) { + return std::make_shared(ptr, type); + } + + explicit TLocalVariable(llvm::Value* ptr, llvm::Type* type) : Ptr_(ptr), Type_(type) {} + + llvm::Value* Load(llvm::BasicBlock* bb) override { + return new llvm::LoadInst(Type_, Ptr_, "", bb); + } + + void Store(IValue* value, llvm::BasicBlock* bb) override { + auto* loaded = value->Load(bb); + new llvm::StoreInst(loaded, Ptr_, bb); + } + +private: + llvm::Value* Ptr_; + llvm::Type* Type_; +}; + + +class TPointer : public IValue { +public: + static IValuePtr Create(llvm::Instruction* ptr, llvm::Type* type) { + return std::make_shared(ptr, type); + } + + explicit TPointer(llvm::Instruction* ptr, llvm::Type* type) : Ptr_(ptr), Type_(type) {} + + llvm::Value* Load(llvm::BasicBlock* bb) override { + return Ptr_; + } + + void Store(IValue* value, llvm::BasicBlock* bb) override { + throw std::logic_error{"unable to store class"}; + } + +private: + llvm::Instruction* Ptr_; + llvm::Type* Type_; +}; + + +class TPointerIndexValue : public IValue { +public: + static IValuePtr Create(llvm::Value* ptr, llvm::Type* type, const std::vector& index) { + return std::make_shared(ptr, type, index); + } + + TPointerIndexValue(llvm::Value* ptr, llvm::Type* type, std::vector index) + : Ptr_(ptr), Type_(type), Index_(std::move(index)) {} + + llvm::Value* Load(llvm::BasicBlock* bb) override { + auto* elementPtr = llvm::GetElementPtrInst::Create(Type_, Ptr_, Index_, "", bb); + + return new llvm::LoadInst(elementPtr->getResultElementType(), elementPtr, "", bb); + } + + void Store(IValue* value, llvm::BasicBlock* bb) override { + auto* loaded = value->Load(bb); + auto* elementPtr = llvm::GetElementPtrInst::Create(Type_, Ptr_, Index_, "", bb); + + new llvm::StoreInst(loaded, elementPtr, bb); + } + +private: + llvm::Value* Ptr_; + llvm::Type* Type_; + std::vector Index_; +}; \ No newline at end of file diff --git a/ir_builder/value_builder.hh b/ir_builder/value_builder.hh new file mode 100644 index 0000000..1c07da3 --- /dev/null +++ b/ir_builder/value_builder.hh @@ -0,0 +1,212 @@ +#pragma once + +#include "llvm/IR/Value.h" + +#include "base_builder.hh" +#include "scope_table.hh" +#include "template_visitor.hh" + +#include "value.hh" + +class TValueBuilder : public TTemplateVisitor { +public: + TValueBuilder(TBaseBuilder& base, const std::string& className, TScopeTable& scopeTable, + llvm::BasicBlock*& basicBlock) + : Base_(base), ClassName_(className), ScopeTable_(scopeTable), BasicBlock_(basicBlock){}; + + void Visit(struct TBooleanExpression* expression) override { + Return(TLLVMValue::Create( + llvm::ConstantInt::get(llvm::Type::getInt1Ty(Base_.Context), expression->GetValue()))); + } + + void Visit(struct TAndExpression* expression) override { + BinaryExpr_(expression, llvm::BinaryOperator::And); + } + + void Visit(struct TIntExpression* expression) override { + Return(TLLVMValue::Create( + llvm::ConstantInt::get(llvm::Type::getInt32Ty(Base_.Context), expression->GetValue()))); + } + + void Visit(struct TEqExpression* expression) override { + ICmpExpr_(expression, llvm::ICmpInst::Predicate::ICMP_EQ); + } + + void Visit(struct TNEqExpression* expression) override { + ICmpExpr_(expression, llvm::ICmpInst::Predicate::ICMP_NE); + } + + void Visit(struct TLeqExpression* expression) override { + ICmpExpr_(expression, llvm::ICmpInst::Predicate::ICMP_SLE); + } + + void Visit(struct TLeExpression* expression) override { + ICmpExpr_(expression, llvm::ICmpInst::Predicate::ICMP_SLT); + } + + void Visit(struct TGeExpression* expression) override { + ICmpExpr_(expression, llvm::ICmpInst::Predicate::ICMP_SGT); + } + + void Visit(struct TGeqExpression* expression) override { + ICmpExpr_(expression, llvm::ICmpInst::Predicate::ICMP_SGE); + } + + void Visit(struct TIdentifierExpression* expression) override { + if (ScopeTable_.HasVariable(expression->Identifier())) { + Return(ScopeTable_.Variable(expression->Identifier())); + } else if (Base_.MemberTable.Has(ClassName_ + "#" + expression->Identifier())) { + auto field = + std::make_unique(std::make_unique(), expression->Identifier()); + Return(Accept(field.get())); + } else { + throw std::logic_error{"Undeclared variable"}; + } + } + + void Visit(struct TMethodInvocationExpression* expression) override { + Return(Accept(expression->Invocation())); + } + + void Visit(struct TMethodInvocation* invocation) override { + auto className = ClassNameFromInvocation_(invocation); + std::vector args; + + llvm::Function* func; + if (!className.has_value()) { + auto self = Accept(invocation->Expression()); + auto* selfPtr = self->Load(BasicBlock_); + className = std::string( + llvm::dyn_cast(selfPtr->getType())->getElementType()->getStructName()); + + func = Base_.FunctionTable.Get(className.value() + "#" + invocation->Method()); + + if (func->arg_size() > invocation->Arguments()->size()) { + args.push_back(selfPtr); + } + + } else { + func = Base_.FunctionTable.Get(className.value() + "#" + invocation->Method()); + } + + for (auto& expr : *invocation->Arguments()) { + args.push_back(Accept(expr.get())->Load(BasicBlock_)); + } + + auto* callInst = llvm::CallInst::Create(func, args, "", BasicBlock_); + callInst->setTailCall(); + + Return(TLLVMValue::Create(callInst)); + } + + + void Visit(struct TThisExpression* expression) override { + Return(ScopeTable_.Variable("this")); + } + + void Visit(struct TSumExpression* expression) override { + BinaryExpr_(expression, llvm::Instruction::BinaryOps::Add); + } + + void Visit(struct TSubExpression* expression) override { + BinaryExpr_(expression, llvm::Instruction::BinaryOps::Sub); + } + + void Visit(struct TModExpression* expression) override { + BinaryExpr_(expression, llvm::Instruction::BinaryOps::SRem); + } + + void Visit(struct TMulExpression* expression) override { + BinaryExpr_(expression, llvm::Instruction::BinaryOps::Mul); + } + + void Visit(struct TDivExpression* expression) override { + BinaryExpr_(expression, llvm::Instruction::BinaryOps::SDiv); + } + + void Visit(struct TNotExpression* expression) override { + auto* res = Accept(expression->Expression())->Load(BasicBlock_); + Return(TLLVMValue::Create(llvm::BinaryOperator::CreateNot(res, "", BasicBlock_))); + } + + void Visit(struct TNewExpression* expression) override { + auto* type = TTypeResolver{Base_}.Accept(expression->Type()); + auto* structType = llvm::dyn_cast(type)->getElementType(); + auto* mallocInst = + llvm::CallInst::CreateMalloc(BasicBlock_, llvm::Type::getInt64Ty(Base_.Context), structType, + llvm::ConstantExpr::getSizeOf(structType), nullptr, nullptr, ""); + BasicBlock_->getInstList().push_back(mallocInst);// :/ + Return(TPointer::Create(mallocInst, structType)); + } + + void Visit(struct TNewArrayExpression* expression) override { + TTypeResolver typeResolver{Base_}; + auto* type = typeResolver.Accept(expression->Type()); + auto* sizeValue = Accept(expression->Size())->Load(BasicBlock_); + + auto* mallocInst = llvm::CallInst::CreateMalloc(BasicBlock_, llvm::Type::getInt64Ty(Base_.Context), type, + llvm::ConstantExpr::getSizeOf(type), sizeValue, nullptr, ""); + BasicBlock_->getInstList().push_back(mallocInst);// :/ + Return(TPointer::Create(mallocInst, type)); + } + + void Visit(struct TIndexExpression* expression) override { + auto* ptr = Accept(expression->Expression())->Load(BasicBlock_); + auto* type = llvm::dyn_cast(ptr->getType())->getElementType(); + auto* index = Accept(expression->Index())->Load(BasicBlock_); + + auto* indexToI64 = + llvm::CastInst::CreateIntegerCast(index, llvm::Type::getInt64Ty(Base_.Context), true, "", BasicBlock_); + Return(TPointerIndexValue::Create(ptr, type, {indexToI64})); + } + + void Visit(struct TFieldInvocation* invocation) override { + auto* ptr = Accept(invocation->Expression())->Load(BasicBlock_); + + auto* type = llvm::dyn_cast(ptr->getType())->getElementType(); + std::string className = std::string(type->getStructName()); + + int index = Base_.MemberTable.Get(className + "#" + invocation->Identifier()); + Return(TPointerIndexValue::Create( + ptr, type, + {llvm::ConstantInt::get(llvm::Type::getInt64Ty(Base_.Context), 0, false), + llvm::ConstantInt::get(llvm::Type::getInt32Ty(Base_.Context), index, false)})); + } + + void Visit(struct TFieldInvocationExpression* expression) override { + Return(Accept(expression->Invocation())); + } + +private: + std::optional ClassNameFromInvocation_(TMethodInvocation* invocation) { + auto* identifier = dynamic_cast(invocation->Expression()); + if (identifier and Base_.ClassTable.Has(identifier->Identifier())) { + return identifier->Identifier(); + } + auto* thisPtr = dynamic_cast(invocation->Expression()); + if (thisPtr) { + return ClassName_; + } + return std::nullopt; + } + + void BinaryExpr_(TBinaryExpression* expression, llvm::Instruction::BinaryOps op) { + auto* lhs = Accept(expression->Lhs())->Load(BasicBlock_); + auto* rhs = Accept(expression->Rhs())->Load(BasicBlock_); + + Return(TLLVMValue::Create(llvm::BinaryOperator::Create(op, lhs, rhs, "", BasicBlock_))); + } + + void ICmpExpr_(TBinaryExpression* expression, llvm::ICmpInst::Predicate predicate) { + auto* lsh = Accept(expression->Lhs())->Load(BasicBlock_); + auto* rhs = Accept(expression->Rhs())->Load(BasicBlock_); + + Return(TLLVMValue::Create( + llvm::CmpInst::Create(llvm::Instruction::OtherOps::ICmp, predicate, lsh, rhs, "", BasicBlock_))); + } + + TBaseBuilder& Base_; + const std::string& ClassName_; + TScopeTable& ScopeTable_; + llvm::BasicBlock*& BasicBlock_; +}; \ No newline at end of file diff --git a/jit/CMakeLists.txt b/jit/CMakeLists.txt new file mode 100644 index 0000000..dc19ba4 --- /dev/null +++ b/jit/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 3.10) + +find_package(LLVM 11.0.0 REQUIRED CONFIG) + +add_definitions(${LLVM_DEFINITIONS}) + +add_library(JIT INTERFACE) + +target_include_directories(JIT SYSTEM INTERFACE ${LLVM_INCLUDE_DIRS}) +llvm_map_components_to_libnames(llvm_libs + Core + ExecutionEngine + Interpreter + MC + MCJIT + Support + nativecodegen + native + ) + +target_include_directories(JIT SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) +target_link_libraries(JIT INTERFACE ${llvm_libs}) +target_link_libraries(JIT INTERFACE IRBuilder) +target_link_libraries(JIT INTERFACE Driver) \ No newline at end of file diff --git a/jit/jit_compile.hh b/jit/jit_compile.hh new file mode 100644 index 0000000..712d925 --- /dev/null +++ b/jit/jit_compile.hh @@ -0,0 +1,57 @@ +#pragma once + +#include + +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" + +#include "driver.hh" +#include "jit_run.hh" +#include "program_builder.hh" + + +inline std::pair, std::unique_ptr> +JitCompile(const std::filesystem::path& path) { + TDriver driver; + + if (driver.parse(path) != 0) { + throw std::logic_error{"parse fail"}; + } + + auto context = std::make_unique(); + auto module = std::make_unique("main", *context); + + TProgramBuilder builder{*context, *module}; + driver.Program_->Accept(&builder); + + return {std::move(module), std::move(context)}; +} + +inline llvm::Function* GetEntry(llvm::Module* module) { + std::optional entry; + for (auto& func : module->getFunctionList()) { + if (func.hasName() && func.getName().endswith("#main")) { + if (entry.has_value()) { + throw std::logic_error{"Multiple main functions"}; + } + entry.emplace(&func); + } + } + + if (!entry.has_value()) { + throw std::logic_error{"No main function to entry"}; + } + return entry.value(); +} + +inline void JitCompileAndRun(const std::filesystem::path& path) { + auto [module, context] = JitCompile(path); + auto entry = GetEntry(module.get()); + JitRun(std::move(module), entry); +} + +inline Status JitCompileAndRunInSubprocess(const std::filesystem::path& path, int timeout = 1) { + auto [module, context] = JitCompile(path); + auto entry = GetEntry(module.get()); + return JitRunSubprocess(std::move(module), entry, timeout); +} \ No newline at end of file diff --git a/jit/jit_run.hh b/jit/jit_run.hh new file mode 100644 index 0000000..eddd8f0 --- /dev/null +++ b/jit/jit_run.hh @@ -0,0 +1,64 @@ +#pragma once + +#include "llvm/ExecutionEngine/ExecutionEngine.h" +#include "llvm/ExecutionEngine/MCJIT.h" +#include +#include +#include +#include + +#include "llvm/IR/Module.h" + +class Status { +public: + static Status Ok() { + return Status(true); + } + + static Status Fail() { + return Status(false); + } + + [[nodiscard]] bool IsOk() const { + return status_; + } + + bool operator==(const Status& other) const { + return status_ == other.status_; + } + +private: + explicit Status(bool res) : status_(res) {} + + bool status_; +}; + +inline void JitRun(std::unique_ptr module, llvm::Function* entry) { + llvm::InitializeNativeTarget(); + llvm::InitializeNativeTargetAsmPrinter(); + llvm::InitializeNativeTargetAsmParser(); + + std::string errStr; + auto* EE = + llvm::EngineBuilder(std::move(module)).setErrorStr(&errStr).setEngineKind(llvm::EngineKind::JIT).create(); + + if (!EE) { + throw std::logic_error{"Engine build failed: " + errStr}; + } + + llvm::GenericValue GV = EE->runFunction(entry, {}); +} + +inline Status JitRunSubprocess(std::unique_ptr module, llvm::Function* entry, int timeout = 1) { + pid_t childPid = fork(); + if (!childPid) { + alarm(timeout); + JitRun(std::move(module), entry); + exit(0); + } + + int status; + waitpid(childPid, &status, 0); + + return (WIFEXITED(status) != 0 && (WEXITSTATUS(status) == 0)) ? Status::Ok() : Status::Fail(); +} diff --git a/main.cpp b/main.cpp index c153650..65fbb69 100644 --- a/main.cpp +++ b/main.cpp @@ -1,14 +1,5 @@ -#include - -#include "driver.hh" -#include "yaml_visitor.hh" +#include "jit_compile.hh" int main(int argc, char* argv[]) { - TDriver driver; - driver.parse(argv[1]); - - TYamlVisitor visitor; - driver.Program_->Accept(&visitor); - visitor.Print(std::cout); - return 0; + JitCompileAndRun(argv[1]); } diff --git a/scope_table/scope.hh b/scope_table/scope.hh index 5e63672..12d2a74 100644 --- a/scope_table/scope.hh +++ b/scope_table/scope.hh @@ -8,43 +8,39 @@ #include "types.hh" +template class TScope { public: explicit TScope(std::string debugName = "") : debugName(std::move(debugName)) {} - void AddVariable(const std::string& name, std::unique_ptr&& type) { + template + void AddVariable(const std::string& name, Args&&... args) { if (Variable_.contains(name)) { throw std::logic_error{"Compilation Error: Variable redefinition"}; } - Variable_[name] = std::make_unique(std::move(type), false); + Variable_.emplace(std::pair(name, T{std::forward(args)...})); } - [[nodiscard]] TVariableSpecification* TryVariable(const std::string& name) const { - if (Variable_.contains(name)) { - return Variable_.at(name).get(); - } - return nullptr; + [[nodiscard]] bool HasVariable(const std::string& name) const { + return Variable_.contains(name); } - [[nodiscard]] TVariableSpecification* Variable(const std::string& name) const { - auto* res = TryVariable(name); - if (res) { - return res; - } - throw std::logic_error{"Compilation Error: Variable used without declaration"}; + [[nodiscard]] const T& Variable(const std::string& name) const { + return Variable_.at(name); } public: - const std::string debugName; + std::string debugName; private: - std::unordered_map> Variable_; + std::unordered_map Variable_; }; -class TScopeTreeNode : public TScope { +template +class TScopeTreeNode : public TScope { public: - TScopeTreeNode(std::string debugName, TScopeTreeNode* parent) : TScope(std::move(debugName)), Parent_(parent) {} + TScopeTreeNode(const std::string& debugName, TScopeTreeNode* parent) : TScope(debugName), Parent_(parent) {} TScopeTreeNode* Parent() { return Parent_; diff --git a/scope_table/scope_table.hh b/scope_table/scope_table.hh index bc52c72..312cc28 100644 --- a/scope_table/scope_table.hh +++ b/scope_table/scope_table.hh @@ -2,13 +2,25 @@ #include #include +#include #include "ast_components.hh" #include "i_type.hh" #include "scope.hh" +template> class TScopeTable { public: + TScopeTable() = default; + + TScopeTable(const TScopeTable& other) = delete; + TScopeTable(TScopeTable&& other) = delete; + + + TScopeTable operator=(const TScopeTable& other) = delete; + TScopeTable operator=(TScopeTable&& other) = delete; + + void BeginScope(const std::string& name = "") { AllScopes_.emplace_back(name, Current_); Current_ = &AllScopes_.back(); @@ -18,16 +30,27 @@ public: Current_ = Current_->Parent(); } - void AddVariable(const std::string& name, std::unique_ptr&& type) { - Current_->AddVariable(name, std::move(type)); + template + void AddVariable(const std::string& name, Args&&... args) { + Current_->AddVariable(name, std::forward(args)...); + } + + [[nodiscard]] bool HasVariable(const std::string& name) const { + auto* findIn = Current_; + while (findIn) { + if (findIn->HasVariable(name)) { + return true; + } + findIn = findIn->Parent(); + } + return false; } - [[nodiscard]] TVariableSpecification* Variable(const std::string& name) const { - TScopeTreeNode* findIn = Current_; + [[nodiscard]] const T& Variable(const std::string& name) const { + auto* findIn = Current_; while (findIn) { - auto* variable = findIn->TryVariable(name); - if (variable) { - return variable; + if (findIn->HasVariable(name)) { + return findIn->Variable(name); } findIn = findIn->Parent(); } @@ -35,6 +58,6 @@ public: } private: - TScopeTreeNode* Current_{nullptr}; - std::deque AllScopes_; + TScopeTreeNode* Current_{nullptr}; + std::deque> AllScopes_; }; diff --git a/symbol_table/symbol_table.hh b/symbol_table/symbol_table.hh index 50cb7e9..7fc087c 100644 --- a/symbol_table/symbol_table.hh +++ b/symbol_table/symbol_table.hh @@ -6,30 +6,36 @@ #include "class_specification.hh" +template> class TSymbolTable { public: TSymbolTable() = default; TSymbolTable(const TSymbolTable&) = delete; TSymbolTable& operator=(const TSymbolTable&) = delete; - TSymbolTable(TSymbolTable&&) = default; - TSymbolTable& operator=(TSymbolTable&&) = default; + TSymbolTable(TSymbolTable&&) noexcept = default; + TSymbolTable& operator=(TSymbolTable&&) noexcept = default; - [[nodiscard]] TClassSpecification* Class(const std::string& name) const { - if (ClassSpecifications_.contains(name)) { - return ClassSpecifications_.at(name).get(); + [[nodiscard]] T& Get(const std::string& name) { + if (Content_.contains(name)) { + return Content_.at(name); } throw std::logic_error{"Compilation error: Undefined symbol"};// TODO Compilation Error } - void AddClass(const std::string& name) { - if (ClassSpecifications_.contains(name)) { + [[nodiscard]] bool Has(const std::string& name) { + return Content_.contains(name); + } + + template + void Add(const std::string& name, Args&&... args) { + if (Content_.contains(name)) { throw std::logic_error{"Compilation error: Symbol redefinition"};// TODO Compilation Error } - ClassSpecifications_[name] = std::make_unique(); + Content_.emplace(std::pair(name, T{std::forward(args)...})); } private: - std::unordered_map> ClassSpecifications_; + std::unordered_map Content_; }; \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 3970cfa..13e5961 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,6 +1,15 @@ cmake_minimum_required(VERSION 3.10) -set(TEST_SOURCES test_main.cpp test_interpreter.cpp test_scopes.cpp test_symbol_table.cpp test_types.cpp test_symbol_table_builder.cpp) +set(TEST_SOURCES + test_print_visitor.cpp + test_interpreter.cpp + test_scopes.cpp + test_symbol_table.cpp + test_types.cpp + test_symbol_table_builder.cpp + test_parser.cpp + test_ir_builder.cpp + test_jit_compiler.cpp) add_executable(Tests ${TEST_SOURCES}) target_link_libraries(Tests Compiler) diff --git a/tests/compiler_tests/arrays.java b/tests/compiler_tests/arrays.java new file mode 100644 index 0000000..7c47391 --- /dev/null +++ b/tests/compiler_tests/arrays.java @@ -0,0 +1,27 @@ +class Main { + public static void main() { + int[] array; + array = new int[3]; + array[0] = 0; + array[1] = 1; + array[2] = 2; + + int[] array_same; + array_same = array; + + assert (array[0] == 0); + assert (array[1] == 1); + assert (array[2]== 2); + + assert (array_same[0] == 0); + assert (array_same[1] == 1); + assert (array_same[2] == 2); + + array_same[0] = 5; + assert (array_same[0] == 5); + assert (array[0] == 5); + + array[1] = 10; + assert (array[1] == 10 && array_same[1] == array[1]); + } +} \ No newline at end of file diff --git a/tests/compiler_tests/assertion_arithmetic.java b/tests/compiler_tests/assertion_arithmetic.java new file mode 100644 index 0000000..02963b6 --- /dev/null +++ b/tests/compiler_tests/assertion_arithmetic.java @@ -0,0 +1,27 @@ +class Main { + public static void main() { + assert (2 + 2 == 4); + assert (2 - 2 == 0); + assert (2 + 0 == 2); + assert (2 * 2 == 4); + assert (2 * -2 == -4); + assert (-2 * -2 == 4); + assert (2 % 2 == 0); + assert (6 % 3 == 0); + assert (3 % 2 == 1); + assert (5 % 2 == 1); + + assert (2 * 2 <= 5); + assert (3 * 3 > 5); + assert (5 % 2 == 1); + assert (!(5 % 2 == 0)); + + assert (0 == 0); + assert (1 > 0); + assert (-1 < 0); + assert (0 > -1); + assert (0 >= 0); + assert (0 <= 0); + + } +} \ No newline at end of file diff --git a/tests/compiler_tests/assertion_false.java b/tests/compiler_tests/assertion_false.java new file mode 100644 index 0000000..10b10a7 --- /dev/null +++ b/tests/compiler_tests/assertion_false.java @@ -0,0 +1,5 @@ +class NotMatter { + public static void main() { + assert (false); + } +} \ No newline at end of file diff --git a/tests/compiler_tests/assertion_true.java b/tests/compiler_tests/assertion_true.java new file mode 100644 index 0000000..5f6607f --- /dev/null +++ b/tests/compiler_tests/assertion_true.java @@ -0,0 +1,5 @@ +class NotMatter { + public static void main() { + assert (true); + } +} \ No newline at end of file diff --git a/tests/compiler_tests/classes.java b/tests/compiler_tests/classes.java new file mode 100644 index 0000000..9dda101 --- /dev/null +++ b/tests/compiler_tests/classes.java @@ -0,0 +1,56 @@ +class Data { + int member_x; + int member_y; + Data member_data; + + public void Swap() { + int tmp; + tmp = member_x; + member_x = member_y; + member_y = tmp; + } + + public Data Copy() { + Data res; + res = new Data(); + res.member_x = member_x; + res.member_y = member_y; + res.member_data = member_data; + + this.Swap(); + this.Swap(); + + return res; + } +} + +class Main { + public static void main() { + Data struct1; + Data struct1_dub; + + struct1 = new Main(); + struct1_dub = struct1; + + struct1.member_x = 0; + struct1_dub.member_y = 1; + + struct1.Swap(); + assert (struct1.member_x == 1); + assert (struct1.member_y == 0); + + struct1.member_data = struct1_dub; + assert (struct1_dub == struct1); + assert (struct1_dub.member_x == struct1.member_data.member_x); + + + Data copy; + copy = struct1.Copy(); + + assert (copy.member_x == struct1.member_x); + copy.member_x = struct1.member_x + 1; + assert (copy.member_x != struct1.member_x); + + assert (copy.member_data == struct1.member_data); + } +} \ No newline at end of file diff --git a/tests/compiler_tests/empty.java b/tests/compiler_tests/empty.java new file mode 100644 index 0000000..a5ffdb6 --- /dev/null +++ b/tests/compiler_tests/empty.java @@ -0,0 +1,4 @@ +class Empty { + public static void main() { + } +} \ No newline at end of file diff --git a/tests/compiler_tests/fibonacci.java b/tests/compiler_tests/fibonacci.java new file mode 100644 index 0000000..51bb933 --- /dev/null +++ b/tests/compiler_tests/fibonacci.java @@ -0,0 +1,24 @@ +class Fib { + public static int Fib(int x) { + if (x == 1) { + return 1; + } + if (x == 0) { + return 0; + } + return Fib(x - 1) + Fib(x - 2); + } +} + +class Main { + public static void main() { + assert (Fib.Fib(5) == 5); + assert (Fib.Fib(6) == 8); + + int x; + x = Fib.Fib(0); + assert (x == 0); + + assert (Fib.Fib(20) == 6765); + } +} \ No newline at end of file diff --git a/tests/compiler_tests/primary_numbers.java b/tests/compiler_tests/primary_numbers.java new file mode 100644 index 0000000..0e55dc6 --- /dev/null +++ b/tests/compiler_tests/primary_numbers.java @@ -0,0 +1,84 @@ +class PrimeCheck { + public static boolean IsPrime(int x) { + if (x <= 1) { + return false; + } else { + int i; + i = 2; + while (i * i <= x) { + if (x % i == 0) { + return false; + } + i = i + 1; + } + return true; + } + assert (false); + return false; + } +} + +class SieveOfEratosthenes { + int size; + boolean[] is_prime; + + public void Prepare(int size) { + this.size = size; + is_prime = new boolean[size]; + + is_prime[0] = false; + is_prime[1] = false; + int i; + i = 2; + while (i <= size) { + is_prime[i] = true; + i = i + 1; + } + + i = 2; + while (i <= size) { + if (is_prime[i]) { + int j; + j = 2; + while (j * i <= size) { + is_prime[i * j] = false; + j = j + 1; + } + } + i = i + 1; + } + } + + public boolean IsPrime(int x) { + assert (x <= size); + return is_prime[x]; + } +} + +class Main { + public static void main() { + assert (PrimeCheck.IsPrime(3)); + assert (PrimeCheck.IsPrime(5)); + assert (PrimeCheck.IsPrime(7)); + assert (!PrimeCheck.IsPrime(12)); + assert (PrimeCheck.IsPrime(1000 * 1000 * 1000 + 7)); // Yea, prime + assert (PrimeCheck.IsPrime(1000 * 1000 * 1000 + 9)); // prime too + assert (!PrimeCheck.IsPrime(1000 * 1000 * 1000 + 11)); // not prime :( + + SieveOfEratosthenes sieve; + sieve = new SieveOfEratosthenes(); + + int N; + N = 1 * 1000 * 1000; + sieve.Prepare(N); + + + int i; + i = 0; + + while (i <= N) { + assert (PrimeCheck.IsPrime(i) == sieve.IsPrime(i)); + i = i + 1; + } + } +} \ No newline at end of file diff --git a/tests/compiler_tests/shadowing.java b/tests/compiler_tests/shadowing.java new file mode 100644 index 0000000..9b83241 --- /dev/null +++ b/tests/compiler_tests/shadowing.java @@ -0,0 +1,16 @@ +class Main { + public static void main() { + int x; + x = 2; + assert (x == 2); + + { + assert (x == 2); + int x; + x = 15; + assert (x == 15); + } + assert (x == 2); + + } +} \ No newline at end of file diff --git a/tests/compiler_tests/time_limit.java b/tests/compiler_tests/time_limit.java new file mode 100644 index 0000000..79c5a50 --- /dev/null +++ b/tests/compiler_tests/time_limit.java @@ -0,0 +1,9 @@ +class Main { + public static void main() { + int x; + x = 0; + while (x >= 0) { + x = x + 1; + } + } +} \ No newline at end of file diff --git a/tests/ir_builder_tests/assertion.java b/tests/ir_builder_tests/assertion.java new file mode 100644 index 0000000..e8eede1 --- /dev/null +++ b/tests/ir_builder_tests/assertion.java @@ -0,0 +1,10 @@ +// Test translation to IR +class Assertion { + public static void simple() { + assert (true && true); + assert (1 == 1); + assert (2 == 2); + assert (256 == 256); + assert (-1 == -1); + } +} \ No newline at end of file diff --git a/tests/ir_builder_tests/assertion.ll b/tests/ir_builder_tests/assertion.ll new file mode 100644 index 0000000..4e88dd3 --- /dev/null +++ b/tests/ir_builder_tests/assertion.ll @@ -0,0 +1,49 @@ +; ModuleID = 'main' +source_filename = "main" + +define void @"Assertion#simple"() { +Entry: + %0 = and i1 true, true + br i1 %0, label %Ok, label %Fail + +Fail: ; preds = %Entry + call void @exit(i32 1) + unreachable + +Ok: ; preds = %Entry + %1 = icmp eq i32 1, 1 + br i1 %1, label %Ok2, label %Fail1 + +Fail1: ; preds = %Ok + call void @exit(i32 1) + unreachable + +Ok2: ; preds = %Ok + %2 = icmp eq i32 2, 2 + br i1 %2, label %Ok4, label %Fail3 + +Fail3: ; preds = %Ok2 + call void @exit(i32 1) + unreachable + +Ok4: ; preds = %Ok2 + %3 = icmp eq i32 256, 256 + br i1 %3, label %Ok6, label %Fail5 + +Fail5: ; preds = %Ok4 + call void @exit(i32 1) + unreachable + +Ok6: ; preds = %Ok4 + %4 = icmp eq i32 -1, -1 + br i1 %4, label %Ok8, label %Fail7 + +Fail7: ; preds = %Ok6 + call void @exit(i32 1) + unreachable + +Ok8: ; preds = %Ok6 + ret void +} + +declare void @exit(i32) diff --git a/tests/parse_tests/comments.java b/tests/parse_tests/comments.java new file mode 100644 index 0000000..a067c67 --- /dev/null +++ b/tests/parse_tests/comments.java @@ -0,0 +1,39 @@ +//this is single-line comment + +/* + and it's comment too + // it may contain all kind of symbols + /* + * + * + +// + ** // + * / + * + / + except comment close combination: +*/ +class Factorial { + // // // also comments can be everywhere + public static void main (/* this */) { + // comment + System.out.println (new Fac ().ComputeFac(/* num */10)); + } + // also comment +} + +class Fac { + public int ComputeFac (int /* */ num) { + assert (num > -1); // and this is a comment + int num_aux; + if (num == 0) + num_aux = 1; + /* */ // and comment that says that previous comment is empty + else + num_aux = num * this.ComputeFac (num - 1); + return num_aux; + } +} + +/* the end! */ \ No newline at end of file diff --git a/tests/parse_tests/arithmetic.java b/tests/print_visitor_tests/arithmetic.java similarity index 100% rename from tests/parse_tests/arithmetic.java rename to tests/print_visitor_tests/arithmetic.java diff --git a/tests/parse_tests/arithmetic.yaml b/tests/print_visitor_tests/arithmetic.yaml similarity index 99% rename from tests/parse_tests/arithmetic.yaml rename to tests/print_visitor_tests/arithmetic.yaml index c926b5c..c8eab4e 100644 --- a/tests/parse_tests/arithmetic.yaml +++ b/tests/print_visitor_tests/arithmetic.yaml @@ -7,7 +7,6 @@ Classes: name: main return-type: kind: void type - is-array: false arguments: ~ statements: - kind: assert statement diff --git a/tests/parse_tests/if-else.java b/tests/print_visitor_tests/if-else.java similarity index 100% rename from tests/parse_tests/if-else.java rename to tests/print_visitor_tests/if-else.java diff --git a/tests/parse_tests/if-else.yaml b/tests/print_visitor_tests/if-else.yaml similarity index 96% rename from tests/parse_tests/if-else.yaml rename to tests/print_visitor_tests/if-else.yaml index 8052b12..c505ec7 100644 --- a/tests/parse_tests/if-else.yaml +++ b/tests/print_visitor_tests/if-else.yaml @@ -7,13 +7,11 @@ Classes: name: main return-type: kind: void type - is-array: false arguments: ~ statements: - kind: variable declaration statement variable-type: kind: int type - is-array: false variable-name: x - kind: assignment statement expression: diff --git a/tests/parse_tests/return_lvalue.java b/tests/print_visitor_tests/return_lvalue.java similarity index 79% rename from tests/parse_tests/return_lvalue.java rename to tests/print_visitor_tests/return_lvalue.java index 40524b4..6deba61 100644 --- a/tests/parse_tests/return_lvalue.java +++ b/tests/print_visitor_tests/return_lvalue.java @@ -9,6 +9,9 @@ public int[] GetMember() { public int[] GetMemberToo() { return this.Member; } + public int[] GetMemberTooToo() { + return GetMember(); + } } class Main { @@ -17,5 +20,6 @@ public static void main () { proxy.Init(100 + 1); proxy.GetMember()[5] = -5; proxy.GetMemberToo()[4] = -4; + proxy.GetMemberTooToo(); } } \ No newline at end of file diff --git a/tests/parse_tests/return_lvalue.yaml b/tests/print_visitor_tests/return_lvalue.yaml similarity index 76% rename from tests/parse_tests/return_lvalue.yaml rename to tests/print_visitor_tests/return_lvalue.yaml index 5b11302..2566605 100644 --- a/tests/parse_tests/return_lvalue.yaml +++ b/tests/print_visitor_tests/return_lvalue.yaml @@ -5,26 +5,24 @@ Classes: - kind: member variable declaration name: Member variable-type: - kind: int type - is-array: true + kind: array type + inner type: + kind: int type - kind: method is-static: false name: Init return-type: kind: void type - is-array: false arguments: - name: cnt type: kind: int type - is-array: false statements: - kind: assignment statement expression: kind: new array expression value-type: kind: int type - is-array: false size: kind: identifier expression identifier: cnt @@ -35,8 +33,9 @@ Classes: is-static: false name: GetMember return-type: - kind: int type - is-array: true + kind: array type + inner type: + kind: int type arguments: ~ statements: - kind: return statement @@ -47,8 +46,9 @@ Classes: is-static: false name: GetMemberToo return-type: - kind: int type - is-array: true + kind: array type + inner type: + kind: int type arguments: ~ statements: - kind: return statement @@ -57,6 +57,24 @@ Classes: invocation: kind: field invocation identifier: Member + - kind: method + is-static: false + name: GetMemberTooToo + return-type: + kind: array type + inner type: + kind: int type + arguments: ~ + statements: + - kind: return statement + expression: + kind: method invocation expression + invocation: + kind: method invocation + method: GetMember + expression: + kind: this expression + arguments: ~ - kind: class name: Main members: @@ -65,14 +83,12 @@ Classes: name: main return-type: kind: void type - is-array: false arguments: ~ statements: - kind: variable declaration statement variable-type: kind: identifier type identifier: Proxy - is-array: false variable-name: proxy - statement: method invocation statement invocation: @@ -124,4 +140,12 @@ Classes: arguments: ~ index: kind: int expression - value: 4 \ No newline at end of file + value: 4 + - statement: method invocation statement + invocation: + kind: method invocation + method: GetMemberTooToo + expression: + kind: identifier expression + identifier: proxy + arguments: ~ \ No newline at end of file diff --git a/tests/parse_tests/sample.java b/tests/print_visitor_tests/sample.java similarity index 100% rename from tests/parse_tests/sample.java rename to tests/print_visitor_tests/sample.java diff --git a/tests/parse_tests/sample.yaml b/tests/print_visitor_tests/sample.yaml similarity index 95% rename from tests/parse_tests/sample.yaml rename to tests/print_visitor_tests/sample.yaml index 917bc06..54902e1 100644 --- a/tests/parse_tests/sample.yaml +++ b/tests/print_visitor_tests/sample.yaml @@ -7,7 +7,6 @@ Classes: name: main return-type: kind: void type - is-array: false arguments: ~ statements: - kind: print statement @@ -21,7 +20,6 @@ Classes: value-type: kind: identifier type identifier: Fac - is-array: false arguments: - kind: int expression value: 10 @@ -33,12 +31,10 @@ Classes: name: ComputeFac return-type: kind: int type - is-array: false arguments: - name: num type: kind: int type - is-array: false statements: - kind: assert statement expresion: @@ -52,7 +48,6 @@ Classes: - kind: variable declaration statement variable-type: kind: int type - is-array: false variable-name: num_aux - kind: if-else statement condition: diff --git a/tests/test_ir_builder.cpp b/tests/test_ir_builder.cpp new file mode 100644 index 0000000..fec2693 --- /dev/null +++ b/tests/test_ir_builder.cpp @@ -0,0 +1,52 @@ +#include + +#include + +#include "llvm/Bitcode/BitcodeWriter.h" +#include "llvm/ExecutionEngine/ExecutionEngine.h" +#include "llvm/Support/raw_ostream.h" + + +#include "driver.hh" +#include "program_builder.hh" +#include "test_utils.hh" + +class IrBuilderTest : public testing::TestWithParam { +public: + static void TestBuild(const std::filesystem::path& src) { + TDriver driver; + ASSERT_EQ(driver.parse(src), 0); + + std::filesystem::path ans = src; + ans.replace_extension(".ll"); + + std::ostringstream correct_answer; + { + std::ifstream tmp(ans); + correct_answer << tmp.rdbuf(); + } + + llvm::LLVMContext context; + auto module = std::make_unique("main", context); + + TProgramBuilder moduleVisitor{context, *module}; + driver.Program_->Accept(&moduleVisitor); + + ASSERT_EQ(ToLL(module.get()), correct_answer.str()); + } + + static std::string ToLL(llvm::Module* module) { + std::string str; + llvm::raw_string_ostream stream(str); + module->print(stream, nullptr); + return str; + } +}; + + +TEST_P(IrBuilderTest, BuildTest) { + TestBuild(GetParam()); +} + + +INSTANTIATE_TEST_SUITE_P(BuildTest, IrBuilderTest, ::testing::ValuesIn(FilesToTest({"ir_builder_tests"}))); diff --git a/tests/test_jit_compiler.cpp b/tests/test_jit_compiler.cpp new file mode 100644 index 0000000..c82cd9c --- /dev/null +++ b/tests/test_jit_compiler.cpp @@ -0,0 +1,53 @@ +#include + +#include + +#include "jit_compile.hh" + + +class JitCompile : public testing::TestWithParam { +public: + static void RunAndExpect(const std::filesystem::path& path, Status expectedResult) { + ASSERT_EQ(JitCompileAndRunInSubprocess(path), expectedResult); + } +}; + +TEST_F(JitCompile, AssertionFalse) { + RunAndExpect("compiler_tests/assertion_false.java", Status::Fail()); +} + +TEST_F(JitCompile, AssertionTrue) { + RunAndExpect("compiler_tests/assertion_true.java", Status::Ok()); +} + +TEST_F(JitCompile, Empty) { + RunAndExpect("compiler_tests/empty.java", Status::Ok()); +} + +TEST_F(JitCompile, Arithmetic) { + RunAndExpect("compiler_tests/assertion_arithmetic.java", Status::Ok()); +} + +TEST_F(JitCompile, Fibonacci) { + RunAndExpect("compiler_tests/fibonacci.java", Status::Ok()); +} + +TEST_F(JitCompile, Arrays) { + RunAndExpect("compiler_tests/arrays.java", Status::Ok()); +} + +TEST_F(JitCompile, TimeLimit) { + RunAndExpect("compiler_tests/time_limit.java", Status::Fail()); +} + +TEST_F(JitCompile, Classes) { + RunAndExpect("compiler_tests/classes.java", Status::Ok()); +} + +TEST_F(JitCompile, Primary) { + RunAndExpect("compiler_tests/primary_numbers.java", Status::Ok()); +} + +TEST_F(JitCompile, Shadowing) { + RunAndExpect("compiler_tests/shadowing.java", Status::Ok()); +} \ No newline at end of file diff --git a/tests/test_parser.cpp b/tests/test_parser.cpp new file mode 100644 index 0000000..cb38f5a --- /dev/null +++ b/tests/test_parser.cpp @@ -0,0 +1,11 @@ +#include + +#include + +#include "driver.hh" + + +TEST(CommentsTest, CommentsOk) { + TDriver driver; + ASSERT_EQ(driver.parse(std::filesystem::path("./parse_tests/comments.java")), 0); +} \ No newline at end of file diff --git a/tests/test_main.cpp b/tests/test_print_visitor.cpp similarity index 52% rename from tests/test_main.cpp rename to tests/test_print_visitor.cpp index 9b1800f..e4782b7 100644 --- a/tests/test_main.cpp +++ b/tests/test_print_visitor.cpp @@ -5,8 +5,10 @@ #include "driver.hh" #include "yaml_visitor.hh" +#include "test_utils.hh" -class TCompilerTest : public testing::TestWithParam { + +class PrintVisitorTest : public testing::TestWithParam { protected: static void TestParse(const std::filesystem::path& src) { TDriver driver; @@ -32,21 +34,9 @@ class TCompilerTest : public testing::TestWithParam { }; -TEST_P(TCompilerTest, ParseTest) { +TEST_P(PrintVisitorTest, ParseTest) { TestParse(GetParam()); } -std::vector FilesToTest(const std::vector& testDirs) { - std::vector res; - for (const auto& testDir : testDirs) { - for (const auto& file : std::filesystem::directory_iterator(testDir)) { - if (file.is_regular_file() && file.path().extension() == ".java") { - res.push_back(file.path()); - } - } - } - return res; -} - -INSTANTIATE_TEST_SUITE_P(ParseTest, TCompilerTest, ::testing::ValuesIn(FilesToTest({"parse_tests"}))); \ No newline at end of file +INSTANTIATE_TEST_SUITE_P(ParseTest, PrintVisitorTest, ::testing::ValuesIn(FilesToTest({"print_visitor_tests"}))); \ No newline at end of file diff --git a/tests/test_scopes.cpp b/tests/test_scopes.cpp index 564f875..58f9c35 100644 --- a/tests/test_scopes.cpp +++ b/tests/test_scopes.cpp @@ -9,17 +9,21 @@ TEST(ScopeTable, BeginEnd) { table.BeginScope(); ASSERT_THROW((void) table.Variable("x"), std::logic_error); - table.AddVariable("x", std::make_unique()); - ASSERT_THROW(table.AddVariable("x", std::make_unique()), std::logic_error); + table.AddVariable("x", std::make_unique(std::make_unique(), false)); + + ASSERT_THROW( + table.AddVariable("x", std::make_unique(std::make_unique(), false)), + std::logic_error); ASSERT_NO_THROW((void) table.Variable("x")); - table.AddVariable("y", std::make_unique()); + table.AddVariable("y", std::make_unique(std::make_unique(), false)); ASSERT_NO_THROW((void) table.Variable("y")); table.BeginScope(); - table.AddVariable("x", std::make_unique());// shadow x variable - table.AddVariable("z", std::make_unique()); + table.AddVariable( + "x", std::make_unique(std::make_unique(), false));// shadow x variable + table.AddVariable("z", std::make_unique(std::make_unique(), false)); ASSERT_NO_THROW((void) table.Variable("x")); table.EndScope(); diff --git a/tests/test_symbol_table.cpp b/tests/test_symbol_table.cpp index 1cc2b7a..c48a386 100644 --- a/tests/test_symbol_table.cpp +++ b/tests/test_symbol_table.cpp @@ -6,14 +6,14 @@ TEST(SymbolTable, Add) { TSymbolTable table; - table.AddClass("MainClass"); - ASSERT_THROW(table.AddClass("MainClass"), std::logic_error); - ASSERT_THROW(table.AddClass("MainClass"), std::logic_error); - ASSERT_THROW((void)table.Class("NoSuchClass"), std::logic_error); + table.Add("MainClass", std::make_unique()); + ASSERT_THROW(table.Add("MainClass"), std::logic_error); + ASSERT_THROW(table.Add("MainClass"), std::logic_error); + ASSERT_THROW((void) table.Get("NoSuchClass"), std::logic_error); - table.Class("MainClass")->AddVariable("x", std::make_unique()); - ASSERT_THROW(table.Class("MainClass")->AddVariable("x", std::make_unique()), std::logic_error); - table.Class("MainClass")->AddVariable("self", std::make_unique(table.Class("MainClass"))); + table.Get("MainClass")->AddVariable("x", std::make_unique()); + ASSERT_THROW(table.Get("MainClass")->AddVariable("x", std::make_unique()), std::logic_error); + table.Get("MainClass")->AddVariable("self", std::make_unique(table.Get("MainClass").get())); - ASSERT_EQ(table.Class("MainClass")->Variable("self")->IsStatic(), false); + ASSERT_EQ(table.Get("MainClass")->Variable("self")->IsStatic(), false); } \ No newline at end of file diff --git a/tests/test_symbol_table_builder.cpp b/tests/test_symbol_table_builder.cpp index 16e5f11..acc042c 100644 --- a/tests/test_symbol_table_builder.cpp +++ b/tests/test_symbol_table_builder.cpp @@ -17,33 +17,33 @@ class SymbolTableBuilder : public ::testing::Test { Table = std::move(builder.SymbolTable()); } - TSymbolTable Table; + TSymbolTable<> Table; }; TEST_F(SymbolTableBuilder, Sample) { BuildSymbolTree("./symbol_table_tests/sample.java"); - ASSERT_THROW((void) Table.Class("NoClass"), std::logic_error); - ASSERT_NO_THROW((void) Table.Class("Factorial")); + ASSERT_THROW((void) Table.Get("NoClass"), std::logic_error); + ASSERT_NO_THROW((void) Table.Get("Factorial")); - ASSERT_TRUE(Table.Class("Factorial")->Method("main")->IsStatic()); - ASSERT_FALSE(Table.Class("Fac")->Method("ComputeFac")->IsStatic()); + ASSERT_TRUE(Table.Get("Factorial")->Method("main")->IsStatic()); + ASSERT_FALSE(Table.Get("Fac")->Method("ComputeFac")->IsStatic()); } TEST_F(SymbolTableBuilder, CyclicReference) { BuildSymbolTree("./symbol_table_tests/cyclic.java"); - ASSERT_EQ(ClassTypeSpec(Table.Class("A")->Variable("VarB")->Type()), Table.Class("B")); - ASSERT_EQ(ClassTypeSpec(Table.Class("A")->Variable("VarA")->Type()), Table.Class("A")); + ASSERT_EQ(ClassTypeSpec(Table.Get("A")->Variable("VarB")->Type()), Table.Get("B").get()); + ASSERT_EQ(ClassTypeSpec(Table.Get("A")->Variable("VarA")->Type()), Table.Get("A").get()); - ASSERT_EQ(ClassTypeSpec(Table.Class("B")->Variable("VarA")->Type()), Table.Class("A")); + ASSERT_EQ(ClassTypeSpec(Table.Get("B")->Variable("VarA")->Type()), Table.Get("A").get()); - ASSERT_TRUE(IsEqual(Table.Class("A")->Variable("VarInt")->Type(), std::make_unique().get())); + ASSERT_TRUE(IsEqual(Table.Get("A")->Variable("VarInt")->Type(), std::make_unique().get())); - ASSERT_EQ(ClassTypeSpec(Table.Class("A")->Method("methodA")->ReturnType()), Table.Class("A")); - ASSERT_EQ(ClassTypeSpec(Table.Class("A")->Method("methodB")->ReturnType()), Table.Class("B")); + ASSERT_EQ(ClassTypeSpec(Table.Get("A")->Method("methodA")->ReturnType()), Table.Get("A").get()); + ASSERT_EQ(ClassTypeSpec(Table.Get("A")->Method("methodB")->ReturnType()), Table.Get("B").get()); - ASSERT_EQ(ClassTypeSpec(Table.Class("A")->Method("methodA")->Args()[0].Type()), Table.Class("A")); - ASSERT_TRUE(Table.Class("A")->Method("methodB")->Args()[1].Name() == "argB"); + ASSERT_EQ(ClassTypeSpec(Table.Get("A")->Method("methodA")->Args()[0].Type()), Table.Get("A").get()); + ASSERT_TRUE(Table.Get("A")->Method("methodB")->Args()[1].Name() == "argB"); } \ No newline at end of file diff --git a/tests/test_utils.hh b/tests/test_utils.hh new file mode 100644 index 0000000..a58e024 --- /dev/null +++ b/tests/test_utils.hh @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + + +inline std::vector FilesToTest(const std::vector& testDirs, + const std::string& filterExt = ".java") { + std::vector res; + for (const auto& testDir : testDirs) { + for (const auto& file : std::filesystem::directory_iterator(testDir)) { + if (file.is_regular_file() && file.path().extension() == filterExt) { + res.push_back(file.path()); + } + } + } + return res; +} \ No newline at end of file diff --git a/visitors/i_visitor.hh b/visitors/i_visitor.hh index 8e14fdb..72b3ba7 100644 --- a/visitors/i_visitor.hh +++ b/visitors/i_visitor.hh @@ -13,10 +13,10 @@ public: virtual void Visit(class TClassMemberDeclarationList*) = 0; virtual void Visit(class TDivExpression*) = 0; virtual void Visit(class TEqExpression*) = 0; + virtual void Visit(class TNEqExpression*) = 0; virtual void Visit(class TExpressionList*) = 0; virtual void Visit(class TFieldInvocation*) = 0; virtual void Visit(class TFieldInvocationExpression*) = 0; - virtual void Visit(class TFieldInvocationIndexed*) = 0; virtual void Visit(class TGeExpression*) = 0; virtual void Visit(class TGeqExpression*) = 0; virtual void Visit(class TIdentifierExpression*) = 0; @@ -52,7 +52,10 @@ public: virtual void Visit(class TBooleanTypeNode*) = 0; virtual void Visit(class TVoidTypeNode*) = 0; virtual void Visit(class TIdentifierTypeNode*) = 0; + virtual void Visit(class TArrayTypeNode*) = 0; virtual void Visit(class TUnaryMinusExpression*) = 0; + + virtual ~IVisitor() = default; }; #endif//COMPILER_I_VISITOR_HH diff --git a/visitors/naive_interpreter.hh b/visitors/naive_interpreter.hh index bf7430b..6a75301 100644 --- a/visitors/naive_interpreter.hh +++ b/visitors/naive_interpreter.hh @@ -14,7 +14,7 @@ class TNaiveInterpreter : public IVisitor { public: explicit TNaiveInterpreter(std::ostream& out) : Out_(out), ReturnValue_(-1) {} void Visit(struct TMemberVariableDeclaration* declaration) override { - throw std::logic_error{"Unsupported"}; + throw std::logic_error{"unsupported"}; } void Visit(struct TIdentifierTypeNode* type) override { throw std::logic_error{"unsupported"}; @@ -57,6 +57,9 @@ public: void Visit(struct TEqExpression* expression) override { BinaryExpression(expression, std::equal_to<>()); } + void Visit(struct TNEqExpression* expression) override { + BinaryExpression(expression, std::not_equal_to<>()); + } void Visit(struct TExpressionList* list) override { for (const auto& expression : *list) { expression->Accept(this); @@ -68,9 +71,6 @@ public: void Visit(struct TFieldInvocationExpression* expression) override { throw std::logic_error{"unsupported"}; } - void Visit(struct TFieldInvocationIndexed* indexed) override { - throw std::logic_error{"unsupported"}; - } void Visit(struct TGeExpression* expression) override { BinaryExpression(expression, std::greater<>()); } @@ -97,6 +97,7 @@ public: throw std::logic_error{"unsupported"}; } void Visit(struct TIntTypeNode* type) override {} + void Visit(struct TArrayTypeNode* node) override {} void Visit(struct TBooleanTypeNode* type) override {} void Visit(struct TThisExpression* expression) override { throw std::logic_error{"unsupported"}; @@ -150,11 +151,7 @@ public: } void Visit(struct TVariableDeclarationStatement* statement) override { const std::string& name = statement->Variable().Name(); - if (statement->Variable().Type()->IsArray()) { - throw std::logic_error{"unsupported"}; - } else { - Variables_[name] = 0; - } + Variables_[name] = 0; } void Visit(struct TMethodInvocationStatement* statement) override { throw std::logic_error{"unsupported"}; diff --git a/visitors/partial_visitor.hh b/visitors/partial_visitor.hh new file mode 100644 index 0000000..dcdebe5 --- /dev/null +++ b/visitors/partial_visitor.hh @@ -0,0 +1,155 @@ +#pragma once + +#include "i_visitor.hh" + + +class TPartialVisitor : public IVisitor { +public: + void Visit(struct TAndExpression* expression) override { + throw std::runtime_error{"unimplemented"}; + } + void Visit(struct TAssertionStatement* statement) override { + throw std::runtime_error{"unimplemented"}; + } + void Visit(struct TAssignmentStatement* statement) override { + throw std::runtime_error{"unimplemented"}; + } + void Visit(struct TBooleanExpression* expression) override { + throw std::runtime_error{"unimplemented"}; + } + void Visit(struct TClassDeclaration* declaration) override { + throw std::runtime_error{"unimplemented"}; + } + void Visit(struct TClassMemberDeclarationList* list) override { + throw std::runtime_error{"unimplemented"}; + } + void Visit(struct TDivExpression* expression) override { + throw std::runtime_error{"unimplemented"}; + } + void Visit(struct TEqExpression* expression) override { + throw std::runtime_error{"unimplemented"}; + } + void Visit(struct TNEqExpression* expression) override { + throw std::runtime_error{"unimplemented"}; + } + void Visit(struct TExpressionList* list) override { + throw std::runtime_error{"unimplemented"}; + } + void Visit(struct TFieldInvocation* invocation) override { + throw std::runtime_error{"unimplemented"}; + } + void Visit(struct TFieldInvocationExpression* expression) override { + throw std::runtime_error{"unimplemented"}; + } + void Visit(struct TGeExpression* expression) override { + throw std::runtime_error{"unimplemented"}; + } + void Visit(struct TGeqExpression* expression) override { + throw std::runtime_error{"unimplemented"}; + } + void Visit(struct TIdentifierExpression* expression) override { + throw std::runtime_error{"unimplemented"}; + } + void Visit(struct TIfElseStatement* statement) override { + throw std::runtime_error{"unimplemented"}; + } + void Visit(struct TIfStatement* statement) override { + throw std::runtime_error{"unimplemented"}; + } + void Visit(struct TIndexExpression* expression) override { + throw std::runtime_error{"unimplemented"}; + } + void Visit(struct TIntExpression* expression) override { + throw std::runtime_error{"unimplemented"}; + } + void Visit(struct TLeExpression* expression) override { + throw std::runtime_error{"unimplemented"}; + } + void Visit(struct TLengthExpression* expression) override { + throw std::runtime_error{"unimplemented"}; + } + void Visit(struct TLeqExpression* expression) override { + throw std::runtime_error{"unimplemented"}; + } + void Visit(struct TMemberMethodDeclaration* declaration) override { + throw std::runtime_error{"unimplemented"}; + } + void Visit(struct TMemberVariableDeclaration* declaration) override { + throw std::runtime_error{"unimplemented"}; + } + void Visit(struct TMethodInvocation* invocation) override { + throw std::runtime_error{"unimplemented"}; + } + void Visit(struct TMethodInvocationExpression* expression) override { + throw std::runtime_error{"unimplemented"}; + } + void Visit(struct TMethodInvocationStatement* statement) override { + throw std::runtime_error{"unimplemented"}; + } + void Visit(struct TModExpression* expression) override { + throw std::runtime_error{"unimplemented"}; + } + void Visit(struct TMulExpression* expression) override { + throw std::runtime_error{"unimplemented"}; + } + void Visit(struct TNewArrayExpression* expression) override { + throw std::runtime_error{"unimplemented"}; + } + void Visit(struct TNewExpression* expression) override { + throw std::runtime_error{"unimplemented"}; + } + void Visit(struct TNotExpression* expression) override { + throw std::runtime_error{"unimplemented"}; + } + void Visit(struct TOrExpression* expression) override { + throw std::runtime_error{"unimplemented"}; + } + void Visit(struct TPrintlnStatement* statement) override { + throw std::runtime_error{"unimplemented"}; + } + void Visit(struct TProgram* program) override { + throw std::runtime_error{"unimplemented"}; + } + void Visit(struct TReturnStatement* statement) override { + throw std::runtime_error{"unimplemented"}; + } + void Visit(struct TStatementList* list) override { + throw std::runtime_error{"unimplemented"}; + } + void Visit(struct TSubExpression* expression) override { + throw std::runtime_error{"unimplemented"}; + } + void Visit(struct TSumExpression* expression) override { + throw std::runtime_error{"unimplemented"}; + } + void Visit(struct TThisExpression* expression) override { + throw std::runtime_error{"unimplemented"}; + } + void Visit(struct TVariableDeclarationStatement* statement) override { + throw std::runtime_error{"unimplemented"}; + } + void Visit(struct TWhileStatement* statement) override { + throw std::runtime_error{"unimplemented"}; + } + void Visit(struct TClassDeclarationList* list) override { + throw std::runtime_error{"unimplemented"}; + } + void Visit(struct TIntTypeNode* type) override { + throw std::runtime_error{"unimplemented"}; + } + void Visit(struct TArrayTypeNode* node) override { + throw std::runtime_error{"unimplemented"}; + } + void Visit(struct TBooleanTypeNode* type) override { + throw std::runtime_error{"unimplemented"}; + } + void Visit(struct TVoidTypeNode* type) override { + throw std::runtime_error{"unimplemented"}; + } + void Visit(struct TIdentifierTypeNode* type) override { + throw std::runtime_error{"unimplemented"}; + } + void Visit(struct TUnaryMinusExpression* expression) override { + throw std::runtime_error{"unimplemented"}; + } +}; \ No newline at end of file diff --git a/visitors/symbol_table_builder.hh b/visitors/symbol_table_builder.hh index 7c97e95..978af85 100644 --- a/visitors/symbol_table_builder.hh +++ b/visitors/symbol_table_builder.hh @@ -11,7 +11,7 @@ class TSymbolTableBuilder : public TTemplateVisitor> { public: void Visit(TMemberVariableDeclaration* declaration) override { auto type = Accept(declaration->Variable().Type()); - Table_.Class(CurrentClass_)->AddVariable(declaration->Variable().Name(), std::move(type)); + Table_.Get(CurrentClass_)->AddVariable(declaration->Variable().Name(), std::move(type)); } void Visit(TMemberMethodDeclaration* declaration) override { std::vector args; @@ -20,26 +20,25 @@ public: args.emplace_back(arg.Name(), Accept(arg.Type())); }); - Table_.Class(CurrentClass_) + Table_.Get(CurrentClass_) ->AddMethod(declaration->Signature().Name(), std::make_unique(Accept(declaration->ReturnType()), std::move(args), declaration->IsStatic())); } void Visit(TIntTypeNode* type) override { - assert(!type->IsArray()); Return(std::make_unique()); } void Visit(TBooleanTypeNode* type) override { - assert(!type->IsArray()); Return(std::make_unique()); } void Visit(TVoidTypeNode* type) override { - assert(!type->IsArray()); Return(std::make_unique()); } void Visit(TIdentifierTypeNode* type) override { - assert(!type->IsArray()); - Return(std::make_unique(Table_.Class(type->Identifier()))); + Return(std::make_unique(Table_.Get(type->Identifier()).get())); + } + void Visit(TArrayTypeNode* node) override { + Return(std::make_unique()); } void Visit(TClassMemberDeclarationList* list) override { std::for_each(list->begin(), list->end(), [this](const auto& it) { @@ -52,7 +51,7 @@ public: } void Visit(TClassDeclarationList* list) override { std::for_each(list->begin(), list->end(), [this](const auto& it) { - Table_.AddClass(it->ClassName()); + Table_.Add(it->ClassName(), std::make_unique()); }); std::for_each(list->begin(), list->end(), [this](const auto& it) { it->Accept(this); @@ -62,11 +61,11 @@ public: Accept(program->ClassDeclarations()); } - TSymbolTable& SymbolTable() { + TSymbolTable<>& SymbolTable() { return Table_; } private: - TSymbolTable Table_; + TSymbolTable<> Table_; std::string CurrentClass_; }; \ No newline at end of file diff --git a/visitors/template_visitor.hh b/visitors/template_visitor.hh index b1d04e2..9bb21b2 100644 --- a/visitors/template_visitor.hh +++ b/visitors/template_visitor.hh @@ -1,164 +1,21 @@ #pragma once #include "ast_components.hh" -#include "i_visitor.hh" +#include "partial_visitor.hh" template -class TTemplateVisitor : public IVisitor { +class TTemplateVisitor : public TPartialVisitor { public: ReturnType Accept(INode* node) { node->Accept(this); - return std::move(ReturnValue_); - } - void Visit(struct TAndExpression* expression) override { - throw std::runtime_error{"unimplemented"}; - } - void Visit(struct TAssertionStatement* statement) override { - throw std::runtime_error{"unimplemented"}; - } - void Visit(struct TAssignmentStatement* statement) override { - throw std::runtime_error{"unimplemented"}; - } - void Visit(struct TBooleanExpression* expression) override { - throw std::runtime_error{"unimplemented"}; - } - void Visit(struct TClassDeclaration* declaration) override { - throw std::runtime_error{"unimplemented"}; - } - void Visit(struct TClassMemberDeclarationList* list) override { - throw std::runtime_error{"unimplemented"}; - } - void Visit(struct TDivExpression* expression) override { - throw std::runtime_error{"unimplemented"}; - } - void Visit(struct TEqExpression* expression) override { - throw std::runtime_error{"unimplemented"}; - } - void Visit(struct TExpressionList* list) override { - throw std::runtime_error{"unimplemented"}; - } - void Visit(struct TFieldInvocation* invocation) override { - throw std::runtime_error{"unimplemented"}; - } - void Visit(struct TFieldInvocationExpression* expression) override { - throw std::runtime_error{"unimplemented"}; - } - void Visit(struct TFieldInvocationIndexed* indexed) override { - throw std::runtime_error{"unimplemented"}; - } - void Visit(struct TGeExpression* expression) override { - throw std::runtime_error{"unimplemented"}; - } - void Visit(struct TGeqExpression* expression) override { - throw std::runtime_error{"unimplemented"}; - } - void Visit(struct TIdentifierExpression* expression) override { - throw std::runtime_error{"unimplemented"}; - } - void Visit(struct TIfElseStatement* statement) override { - throw std::runtime_error{"unimplemented"}; - } - void Visit(struct TIfStatement* statement) override { - throw std::runtime_error{"unimplemented"}; - } - void Visit(struct TIndexExpression* expression) override { - throw std::runtime_error{"unimplemented"}; - } - void Visit(struct TIntExpression* expression) override { - throw std::runtime_error{"unimplemented"}; - } - void Visit(struct TLeExpression* expression) override { - throw std::runtime_error{"unimplemented"}; - } - void Visit(struct TLengthExpression* expression) override { - throw std::runtime_error{"unimplemented"}; - } - void Visit(struct TLeqExpression* expression) override { - throw std::runtime_error{"unimplemented"}; - } - void Visit(struct TMemberMethodDeclaration* declaration) override { - throw std::runtime_error{"unimplemented"}; - } - void Visit(struct TMemberVariableDeclaration* declaration) override { - throw std::runtime_error{"unimplemented"}; - } - void Visit(struct TMethodInvocation* invocation) override { - throw std::runtime_error{"unimplemented"}; - } - void Visit(struct TMethodInvocationExpression* expression) override { - throw std::runtime_error{"unimplemented"}; - } - void Visit(struct TMethodInvocationStatement* statement) override { - throw std::runtime_error{"unimplemented"}; - } - void Visit(struct TModExpression* expression) override { - throw std::runtime_error{"unimplemented"}; - } - void Visit(struct TMulExpression* expression) override { - throw std::runtime_error{"unimplemented"}; - } - void Visit(struct TNewArrayExpression* expression) override { - throw std::runtime_error{"unimplemented"}; - } - void Visit(struct TNewExpression* expression) override { - throw std::runtime_error{"unimplemented"}; - } - void Visit(struct TNotExpression* expression) override { - throw std::runtime_error{"unimplemented"}; - } - void Visit(struct TOrExpression* expression) override { - throw std::runtime_error{"unimplemented"}; - } - void Visit(struct TPrintlnStatement* statement) override { - throw std::runtime_error{"unimplemented"}; - } - void Visit(struct TProgram* program) override { - throw std::runtime_error{"unimplemented"}; - } - void Visit(struct TReturnStatement* statement) override { - throw std::runtime_error{"unimplemented"}; - } - void Visit(struct TStatementList* list) override { - throw std::runtime_error{"unimplemented"}; - } - void Visit(struct TSubExpression* expression) override { - throw std::runtime_error{"unimplemented"}; - } - void Visit(struct TSumExpression* expression) override { - throw std::runtime_error{"unimplemented"}; - } - void Visit(struct TThisExpression* expression) override { - throw std::runtime_error{"unimplemented"}; - } - void Visit(struct TVariableDeclarationStatement* statement) override { - throw std::runtime_error{"unimplemented"}; - } - void Visit(struct TWhileStatement* statement) override { - throw std::runtime_error{"unimplemented"}; - } - void Visit(struct TClassDeclarationList* list) override { - throw std::runtime_error{"unimplemented"}; - } - void Visit(struct TIntTypeNode* type) override { - throw std::runtime_error{"unimplemented"}; - } - void Visit(struct TBooleanTypeNode* type) override { - throw std::runtime_error{"unimplemented"}; - } - void Visit(struct TVoidTypeNode* type) override { - throw std::runtime_error{"unimplemented"}; - } - void Visit(struct TIdentifierTypeNode* type) override { - throw std::runtime_error{"unimplemented"}; - } - void Visit(struct TUnaryMinusExpression* expression) override { - throw std::runtime_error{"unimplemented"}; + return std::move(ReturnValue_.value()); } - void Return(ReturnType value) { - ReturnValue_ = std::move(value); + template + void Return(T&& value) { + ReturnValue_.emplace(std::forward(value)); } private: - ReturnType ReturnValue_; + std::optional ReturnValue_; }; \ No newline at end of file diff --git a/visitors/yaml_visitor.hh b/visitors/yaml_visitor.hh index 20ef9c6..57f0fd1 100644 --- a/visitors/yaml_visitor.hh +++ b/visitors/yaml_visitor.hh @@ -60,6 +60,10 @@ public: ProcessBinaryExpression_("eq", expression); } + void Visit(struct TNEqExpression* expression) override { + ProcessBinaryExpression_("neq", expression); + } + void Visit(struct TExpressionList* list) override { ProcessList_(list); } @@ -75,21 +79,12 @@ public: void Visit(struct TFieldInvocationExpression* expression) override { YAML::Node result; result["kind"] = "field invocation expression"; - expression->Method()->Accept(this); + expression->Invocation()->Accept(this); result["invocation"] = Return_; SetReturn_(result); } - void Visit(struct TFieldInvocationIndexed* invocationIndexed) override { - YAML::Node result; - result["kind"] = "field invocation indexed"; - invocationIndexed->Index()->Accept(this); - result["index"] = Return_; - - SetReturn_(result); - } - void Visit(struct TGeExpression* expression) override { ProcessBinaryExpression_("ge", expression); } @@ -361,6 +356,15 @@ public: ProcessType_("int", type); } + void Visit(struct TArrayTypeNode* node) override { + YAML::Node result; + result["kind"] = "array type"; + node->Type()->Accept(this); + result["inner type"] = Return_; + + SetReturn_(result); + } + void Visit(struct TBooleanTypeNode* type) override { ProcessType_("boolean", type); } @@ -373,7 +377,6 @@ public: YAML::Node result; result["kind"] = "identifier type"; result["identifier"] = type->Identifier(); - result["is-array"] = type->IsArray(); SetReturn_(result); } @@ -435,8 +438,7 @@ private: void ProcessType_(const std::string& prefix, TTypeNode* type) { YAML::Node result; - result["kind"] = prefix + " type"; - result["is-array"] = type->IsArray(); + result["kind"] = prefix + " type"; SetReturn_(result); }