From 96a9100d70b571c5946e3cc1bab7cbb3019ba6dd Mon Sep 17 00:00:00 2001 From: Sherif Nafee Date: Sat, 9 Apr 2022 19:01:19 +0300 Subject: [PATCH 1/3] Add string to documentation --- docs/documentation.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/documentation.md b/docs/documentation.md index dec9368..f01ebdd 100644 --- a/docs/documentation.md +++ b/docs/documentation.md @@ -38,6 +38,7 @@ The following rules are used in sections presenting language syntax: - **integer**: supports integer numbers (8 bytes signed integers) - **real**: supports real values (4 bytes) - **boolean**: can only be true or false (1 byte) +- **string**: supports raw text ### User types @@ -83,10 +84,12 @@ The following rules are used in sections presenting language syntax: var a : integer is 20; var b : boolean is false; var c : real is 1.5; -var d : integer; # will not be initialized unless global +var d : string is "Hello AC+"; +var e : integer; # will not be initialized unless global var x is 5; # x becomes integer automatically var y is true; # y becomes boolean automatically var z is 0.5; # z becomes real automatically +var w is "HI!"; # w becomes string automatically ``` ### Type Declaration: @@ -139,7 +142,7 @@ var x : integer; # This is also a comment ### Arithmetic: **Operators:** -- **+**: Addition +- **+**: Addition (With strings the strings get concatinated together) - **-**: Subtraction - *****: Multiplication - **/**: Division @@ -149,6 +152,7 @@ var x : integer; # This is also a comment ```python var x is 5 + 5; # 10 +var y is "a" + "b"; #"ab" ``` ### Relational: **Operators:** From f30538edc0740500bab2829efe15f3c990fa8c66 Mon Sep 17 00:00:00 2001 From: Sherif Nafee Date: Tue, 26 Apr 2022 16:41:14 +0300 Subject: [PATCH 2/3] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0ef3c5b..1600830 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ Compiler for **C+** toy imperative language, based on [LLVM](https://llvm.org/do 2. Clone repo ```bash - git clone https://github.com/Sh3B0/cplus/ + git clone https://github.com/Sh1co/cplus/ cd cplus ``` From b1dfc98c50d082349011de34d7c5aa476efc3046 Mon Sep 17 00:00:00 2001 From: Igor Parfenov Date: Thu, 28 Apr 2022 01:33:19 +0300 Subject: [PATCH 3/3] type_checker 1 --- CMakeLists.txt | 4 +- ast.hpp | 28 ++- lexer.l | 5 + llvm.cpp | 12 ++ llvm.hpp | 4 +- main.cpp | 6 +- parser.y | 14 +- type_checker.cpp | 531 +++++++++++++++++++++++++++++++++++++++++++++++ type_checker.hpp | 53 +++++ 9 files changed, 649 insertions(+), 8 deletions(-) create mode 100755 type_checker.cpp create mode 100755 type_checker.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ac3ee6f..8bfc9bd 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,9 +16,9 @@ FLEX_TARGET(MyScanner lexer.l ${CMAKE_BINARY_DIR}/lexer.cpp) BISON_TARGET(MyParser parser.y ${CMAKE_BINARY_DIR}/parser.cpp) ADD_FLEX_BISON_DEPENDENCY(MyScanner MyParser) -set(HEADERS "shell.hpp" "lexer.h" "ast.hpp" "llvm.hpp") +set(HEADERS "shell.hpp" "lexer.h" "ast.hpp" "type_checker.hpp" "llvm.hpp") -set(SOURCES "main.cpp" "shell.cpp" "llvm.cpp") +set(SOURCES "main.cpp" "shell.cpp" "type_checker.cpp" "llvm.cpp") add_executable(cplus ${HEADERS} ${SOURCES} ${BISON_MyParser_OUTPUTS} ${FLEX_MyScanner_OUTPUTS}) diff --git a/ast.hpp b/ast.hpp index 46e769b..c03f12d 100755 --- a/ast.hpp +++ b/ast.hpp @@ -15,11 +15,13 @@ struct Expression; struct UnaryExpression; struct BinaryExpression; struct Identifier; +struct EmptyType; struct IntType; struct RealType; struct BoolType; struct ArrayType; struct RecordType; +struct FunctionType; struct IntLiteral; struct RealLiteral; struct BoolLiteral; @@ -40,11 +42,13 @@ struct RoutineCall; class Visitor { public: virtual void visit(ast::Program *program) = 0; + virtual void visit(ast::EmptyType *it) = 0; virtual void visit(ast::IntType *it) = 0; virtual void visit(ast::RealType *it) = 0; virtual void visit(ast::BoolType *it) = 0; virtual void visit(ast::ArrayType *at) = 0; virtual void visit(ast::RecordType *rt) = 0; + virtual void visit(ast::FunctionType *ft) = 0; virtual void visit(ast::IntLiteral *il) = 0; virtual void visit(ast::RealLiteral *rl) = 0; virtual void visit(ast::BoolLiteral *bl) = 0; @@ -69,7 +73,7 @@ namespace ast { template using node_ptr = std::shared_ptr; // Enumerations -enum class TypeEnum { INT, REAL, BOOL, ARRAY, RECORD }; +enum class TypeEnum { EMPTY, INT, REAL, BOOL, ARRAY, RECORD, FUNCTION }; enum class OperatorEnum { PLUS, MINUS, MUL, DIV, MOD, AND, OR, NOT, XOR, EQ, NEQ, LT, GT, LEQ, GEQ }; // Base class for AST nodes @@ -103,6 +107,13 @@ struct Statement : virtual Node { }; // +struct EmptyType : Type { + EmptyType() {} + TypeEnum getType() { return TypeEnum::EMPTY; } + + void accept(Visitor *v) override { v->visit(this); } +}; + struct IntType : Type { IntType() {} TypeEnum getType() { return TypeEnum::INT; } @@ -151,6 +162,19 @@ struct RecordType : Type { void accept(Visitor *v) override { v->visit(this); } }; +struct FunctionType : Type { + node_ptr from, to; + + FunctionType(node_ptr from, node_ptr to) { + this->from = from; + this->to = to; + } + + TypeEnum getType() { return TypeEnum::FUNCTION; } + + void accept(Visitor *v) override { v->visit(this); } +}; + // // struct UnaryExpression : Expression { @@ -397,4 +421,4 @@ struct RoutineCall : Statement, Expression { // } // namespace ast -#endif // AST_H \ No newline at end of file +#endif // AST_H diff --git a/lexer.l b/lexer.l index 26d549b..8c43814 100755 --- a/lexer.l +++ b/lexer.l @@ -265,6 +265,11 @@ alphanum [a-zA-Z_0-9\.] return cplus::Parser::make_NEQ(); } +"->" { + LDEBUG("ARROW") + return cplus::Parser::make_ARROW(); +} + true { LDEBUG("BOOL_VAL") return cplus::Parser::make_BOOL_VAL(true); diff --git a/llvm.cpp b/llvm.cpp index 0b5f37a..ca52029 100755 --- a/llvm.cpp +++ b/llvm.cpp @@ -443,6 +443,10 @@ void IRGenerator::visit(ast::BinaryExpression *exp) { BLOCK_E("BinaryExpression") } +void IRGenerator::visit(ast::EmptyType *it) { + +} + void IRGenerator::visit(ast::IntType *it) { tmp_t = int_t; } @@ -499,6 +503,14 @@ void IRGenerator::visit(ast::RecordType *rt) { BLOCK_E("RecordType") } +void IRGenerator::visit(ast::FunctionType *ft) { + BLOCK_B("FucntionType") + + GERROR("Function Types are not supported"); + + BLOCK_E("FunctionType") +} + void IRGenerator::visit(ast::IntLiteral *il) { tmp_v = llvm::ConstantInt::get(context, llvm::APInt(64, il->value, true)); tmp_t = int_t; diff --git a/llvm.hpp b/llvm.hpp index d0fb378..f2e47d9 100755 --- a/llvm.hpp +++ b/llvm.hpp @@ -20,11 +20,13 @@ class IRGenerator : public Visitor { IRGenerator(); void generate(); void visit(ast::Program *program) override; + void visit(ast::EmptyType *it) override; void visit(ast::IntType *it) override; void visit(ast::RealType *rt) override; void visit(ast::BoolType *bt) override; void visit(ast::ArrayType *at) override; void visit(ast::RecordType *rt) override; + void visit(ast::FunctionType *ft) override; void visit(ast::IntLiteral *il) override; void visit(ast::RealLiteral *rl) override; void visit(ast::BoolLiteral *bl) override; @@ -68,4 +70,4 @@ class IRGenerator : public Visitor { llvm::Type *pop_t(); }; -#endif // LLVM_H \ No newline at end of file +#endif // LLVM_H diff --git a/main.cpp b/main.cpp index 45f51d7..3813436 100755 --- a/main.cpp +++ b/main.cpp @@ -3,6 +3,7 @@ #include "lexer.h" #include "parser.hpp" #include "shell.hpp" +#include "type_checker.hpp" #include "llvm.hpp" #define RED "\033[31m" @@ -33,6 +34,9 @@ int main(int argc, char **argv) { if(shell.debug) { std::cout << CYAN << "[AST]:" << RESET << std::endl; } + + ast::TypeChecker type_checker; + program->accept(&type_checker); IRGenerator gen; program->accept(&gen); @@ -49,4 +53,4 @@ int main(int argc, char **argv) { } return 0; -} \ No newline at end of file +} diff --git a/parser.y b/parser.y index 07bdcea..bd134f5 100755 --- a/parser.y +++ b/parser.y @@ -19,7 +19,7 @@ %token COLON SEMICOLON COMMA DDOT BECOMES // : ; , . .. := %token PLUS MINUS MUL DIV MOD // + - * / % %token AND OR XOR NOT // and or xor not -%token LT GT EQ LEQ GEQ NEQ // < > = <= >= /= +%token LT GT EQ LEQ GEQ NEQ ARROW // < > = <= >= /= -> %token ARRAY RECORD ROUTINE RETURN END // array record routine return end %token PRINT PRINTLN STRING // print println %token IF THEN ELSE WHILE FOR IN LOOP REVERSE // if then else while for in loop reverse @@ -35,7 +35,7 @@ %type > ROUTINE_DECLARATION %type > EXPRESSION %type >> EXPRESSIONS NON_EMPTY_EXPRESSIONS -%type > TYPE PRIMITIVE_TYPE ARRAY_TYPE RECORD_TYPE +%type > TYPE PRIMITIVE_TYPE ARRAY_TYPE RECORD_TYPE FUNCTION_TYPE %type > BODY %type > MODIFIABLE_PRIMARY %type > STATEMENT @@ -53,6 +53,7 @@ %left AND %left EQ NEQ XOR %left LT LEQ GT GEQ +%right ARROW %left PLUS MINUS %left MUL DIV MOD %right NOT @@ -158,6 +159,8 @@ TYPE : PRIMITIVE_TYPE | ARRAY_TYPE | RECORD_TYPE + | FUNCTION_TYPE + | CB_L TYPE CB_R { $$ = $2; } | ID { PDEBUG("ALIASED_TYPE_ACCESS") $$ = program->types[$1]; @@ -194,6 +197,13 @@ VARIABLE_DECLARATIONS : } ; +FUNCTION_TYPE : + TYPE ARROW TYPE { + PDEBUG("FUNCTION_TYPE") + $$ = std::make_shared($1, $3); + } +; + ROUTINE_DECLARATION : ROUTINE ID B_L PARAMETERS B_R IS BODY END { PDEBUG("PROCEDURE_DECLARATION") diff --git a/type_checker.cpp b/type_checker.cpp new file mode 100755 index 0000000..1014fd9 --- /dev/null +++ b/type_checker.cpp @@ -0,0 +1,531 @@ +#include "type_checker.hpp" + +#define RED "\033[31m" +#define CYAN "\033[36m" +#define YELLOW "\033[33m" +#define RESET "\033[0m" + +#define GDEBUG(X) if (shell.debug) std::cout << CYAN << X << RESET << std::endl; +#define GWARNING(X) std::cerr << YELLOW << "[TYPE]: [WARNINGS]:\n" << X << RESET << std::endl; +#define GERROR(X) std::cerr << RED << "[TYPE]: [ERROR]: " << X << RESET << std::endl; std::exit(1); + +#define BLOCK_B(X) \ + if (shell.debug) { \ + std::cout << CYAN; \ + for (int i=0; i" << RESET << std::endl; \ + spaces += 4; \ + } + +#define BLOCK_E(X) \ + if (shell.debug) { \ + spaces -= 4; \ + std::cout << CYAN; \ + for (int i=0; i" << RESET << std::endl; \ + } + +extern cplus::Shell shell; + +namespace ast { + +TypeChecker::TypeChecker() { + +} + +bool TypeChecker::type_equal(node_ptr type1, node_ptr type2) { + if (std::dynamic_pointer_cast (type1) && std::dynamic_pointer_cast (type2)) + return true; + if (std::dynamic_pointer_cast (type1) && std::dynamic_pointer_cast (type2)) + return true; + if (std::dynamic_pointer_cast (type1) && std::dynamic_pointer_cast (type2)) + return true; + + if ((std::dynamic_pointer_cast (type1) || + std::dynamic_pointer_cast (type1) || + std::dynamic_pointer_cast (type1)) + && + (std::dynamic_pointer_cast (type2) || + std::dynamic_pointer_cast (type2) || + std::dynamic_pointer_cast (type2))){ + GWARNING("Simple types casting") + return true; + } + + node_ptr array1 = std::dynamic_pointer_cast (type1); + node_ptr array2 = std::dynamic_pointer_cast (type2); + if (array1 && array2) { + return type_equal(array1->dtype, array2->dtype); + } + + node_ptr record1 = std::dynamic_pointer_cast (type1); + node_ptr record2 = std::dynamic_pointer_cast (type2); + if (record1 && record2) { + if (record1->fields.size() != record2->fields.size()) + return false; + for (int i = 0; i < (int)record1->fields.size(); i++) { + record1->fields[i]->accept(this); + node_ptr type__ = type_; + record2->fields[i]->accept(this); + if (!type_equal(type__, type_)) + return false; + } + return true; + } + + node_ptr function1 = std::dynamic_pointer_cast (type1); + node_ptr function2 = std::dynamic_pointer_cast (type2); + if (function1 && function2) { + return (type_equal(function1->from, function2->from) && + type_equal(function1->to, function2->to)); + } + return false; +} + +int TypeChecker::find_in_context_expr(std::string name) { + for (int i = (int)context_expr.size() - 1; i >= 0; i--) { + if (context_expr[i].first == name) + return i; + } + return -1; +} + +std::string TypeChecker::build_variable_name() { + std::string res; + for (auto& s : record_stack) { + res += s + "."; + } + return res; +} + +void TypeChecker::visit(ast::Program *program) { + BLOCK_B("Program") + + for (auto u = program->variables.rbegin(); u != program->variables.rend(); u++) { + (*u)->accept(this); + } + + for (auto& u : program->routines) { + u->accept(this); + } + + context_expr.clear(); + + BLOCK_E("Program") +} + +void TypeChecker::visit(ast::VariableDeclaration *var) { + BLOCK_B("VariableDeclaration") + if (var->dtype) { + if (var->initial_value) { + var->initial_value->accept(this); + if (!type_equal(var->dtype, type_)) { + GERROR("Variable declaration type and value have different types") + } + } + type_ = var->dtype; + context_expr.push_back({build_variable_name() + var->name, var->dtype}); + } + else { + if (var->initial_value) { + var->initial_value->accept(this); + context_expr.push_back({var->name, type_}); + } + else { + GERROR("Variable declaration has neither type nor value") + } + } + + if (node_ptr array = std::dynamic_pointer_cast (type_)) { + array->accept(this); + } + if (node_ptr record = std::dynamic_pointer_cast (type_)) { + record_stack.push_back(var->name); + record->accept(this); + record_stack.pop_back(); + } + + BLOCK_E("VariableDeclaration") +} + +void TypeChecker::visit(ast::Identifier *id) { + BLOCK_B("Identifier") + + int pos = find_in_context_expr(id->name); + if (pos == -1) { + GERROR(id->name << " is not declared") + } + if (id->idx) { + id->idx->accept(this); + if (!std::dynamic_pointer_cast (type_)) { + GERROR("Array index expression has not int type") + } + if (!std::dynamic_pointer_cast (context_expr[pos].second)) { + GERROR("Indexation applied to not array type") + } + } + else { + type_ = context_expr[pos].second; + } + + BLOCK_E("Identifier") +} + +void TypeChecker::visit(ast::UnaryExpression *exp) { + BLOCK_B("UnaryExpression") + + exp->operand->accept(this); + + if (exp->op == OperatorEnum::MINUS) { + if (!std::dynamic_pointer_cast (type_) && + !std::dynamic_pointer_cast (type_)) { + GERROR("Unary minus applied to neither int nor real") + } + if (std::dynamic_pointer_cast (type_)) + type_ = std::make_shared (); + else + type_ = std::make_shared (); + } + + if (exp->op == OperatorEnum::NOT) { + if (!std::dynamic_pointer_cast (type_)) { + GERROR("Unary not applied to not bool") + } + type_ = std::make_shared (); + } + + BLOCK_E("UnaryExpression") +} + +void TypeChecker::visit(ast::BinaryExpression *exp) { + BLOCK_B("BinaryExpression") + + if (exp->op == OperatorEnum::PLUS || + exp->op == OperatorEnum::MINUS || + exp->op == OperatorEnum::MUL || + exp->op == OperatorEnum::DIV) { + + std::string op; + if (exp->op == OperatorEnum::PLUS) op = "Plus"; + if (exp->op == OperatorEnum::MINUS) op = "Minus"; + if (exp->op == OperatorEnum::MOD) op = "Mul"; + if (exp->op == OperatorEnum::DIV) op = "Div"; + + exp->lhs->accept(this); + if (!std::dynamic_pointer_cast (type_) && + !std::dynamic_pointer_cast (type_) && + !std::dynamic_pointer_cast (type_)) { + GERROR(op << " left operand is neither int nor real nor bool type") + } + bool left_is_int = false; + if (std::dynamic_pointer_cast (type_)) + left_is_int = true; + + exp->rhs->accept(this); + if (!std::dynamic_pointer_cast (type_) && + !std::dynamic_pointer_cast (type_) && + !std::dynamic_pointer_cast (type_)) { + GERROR(op << " right operand is neither int nor real nor bool type") + } + if (left_is_int && std::dynamic_pointer_cast (type_)) + type_ = std::make_shared (); + else + type_ = std::make_shared (); + } + + if (exp->op == OperatorEnum::MOD) { + exp->lhs->accept(this); + if (!std::dynamic_pointer_cast (type_)) { + GERROR("Mod left operand is not int type") + } + exp->rhs->accept(this); + if (!std::dynamic_pointer_cast (type_)) { + GERROR("Mod right operand is not int type") + } + type_ = std::make_shared (); + } + + if (exp->op == OperatorEnum::AND || + exp->op == OperatorEnum::OR || + exp->op == OperatorEnum::XOR) { + + std::string op; + if (exp->op == OperatorEnum::AND) op = "And"; + if (exp->op == OperatorEnum::OR) op = "Or"; + if (exp->op == OperatorEnum::XOR) op = "Xor"; + + exp->lhs->accept(this); + if (!std::dynamic_pointer_cast (type_)) { + GERROR(op << " left operand is not bool type") + } + exp->rhs->accept(this); + if (!std::dynamic_pointer_cast (type_)) { + GERROR(op << " right operand is not bool type") + } + type_ = std::make_shared (); + } + + if (exp->op == OperatorEnum::EQ || + exp->op == OperatorEnum::NEQ || + exp->op == OperatorEnum::GT || + exp->op == OperatorEnum::LT || + exp->op == OperatorEnum::GEQ || + exp->op == OperatorEnum::LEQ) { + + std::string op; + if (exp->op == OperatorEnum::EQ) op = "Eq"; + if (exp->op == OperatorEnum::NEQ) op = "Neq"; + if (exp->op == OperatorEnum::GT) op = "Gt"; + if (exp->op == OperatorEnum::LT) op = "Lt"; + if (exp->op == OperatorEnum::GEQ) op = "Geq"; + if (exp->op == OperatorEnum::LEQ) op = "Leq"; + + exp->lhs->accept(this); + if (!std::dynamic_pointer_cast (type_) && + !std::dynamic_pointer_cast (type_)) { + GERROR(op << " left operand is neither int nor real type") + } + exp->rhs->accept(this); + if (!std::dynamic_pointer_cast (type_) && + !std::dynamic_pointer_cast (type_)) { + GERROR(op << " right operand is neither int nor real type") + } + type_ = std::make_shared (); + } + + BLOCK_E("BinaryExpression") +} + +void TypeChecker::visit(ast::EmptyType *it) { + GERROR("EmptyType is visited") +} + +void TypeChecker::visit(ast::IntType *it) { + GERROR("IntType is visited") +} + +void TypeChecker::visit(ast::RealType *rt) { + GERROR("RealType is visited") +} + +void TypeChecker::visit(ast::BoolType *bt) { + GERROR("BoolType is visited") +} + +void TypeChecker::visit(ast::ArrayType *at) { + BLOCK_B("ArrayType") + + at->size->accept(this); + if (!std::dynamic_pointer_cast (type_)) { + GERROR("Array size expression has not int type") + } + + BLOCK_E("ArrayType") +} + +void TypeChecker::visit(ast::RecordType *rt) { + BLOCK_B("RecordType") + + for (auto& f : rt->fields) { + f->accept(this); + } + + BLOCK_E("RecordType") +} + +void TypeChecker::visit(ast::FunctionType *ft) { + GERROR("FunctionType is visited") +} + +void TypeChecker::visit(ast::IntLiteral *il) { + type_ = std::make_shared (); +} + +void TypeChecker::visit(ast::RealLiteral *rl) { + type_ = std::make_shared (); +} + +void TypeChecker::visit(ast::BoolLiteral *bl) { + type_ = std::make_shared (); +} + +void TypeChecker::visit(ast::RoutineDeclaration *routine) { + BLOCK_B("RoutineDeclaration") + + for (auto p = routine->params.rbegin(); p != routine->params.rend(); p++) { + (*p)->accept(this); + } + + routine->body->accept(this); + + node_ptr return_type; + if (context_return.empty()) { + return_type = std::make_shared (); + } + else { + return_type = context_return[0]; + } + + type_ = return_type; + + if (routine->rtype && !type_equal(routine->rtype, type_)) { + GERROR("Routine specified and actual return types are different") + } + + for (auto& p : routine->params) { + type_ = std::make_shared (context_expr.back().second, type_); + context_expr.pop_back(); + } + context_expr.push_back({routine->name, type_}); + + if (context_return.size() >= 2) { + for (int i = 1; i < (int)context_return.size(); i++) { + if (!type_equal(context_return[0], context_return[i])) { + GERROR("Return statements have different types") + } + } + } + context_return.clear(); + + BLOCK_E("RoutineDeclaration") +} + +void TypeChecker::visit(ast::Body *body) { + BLOCK_B("Body") + + size_t body_context_size = context_expr.size(); + + for (auto u = body->variables.rbegin(); u != body->variables.rend(); u++) { + (*u)->accept(this); + } + + for (auto u = body->statements.rbegin(); u != body->statements.rend(); u++) { + (*u)->accept(this); + } + + while(context_expr.size() != body_context_size) { + context_expr.pop_back(); + } + + BLOCK_E("Body") +} + +void TypeChecker::visit(ast::ReturnStatement *stmt) { + BLOCK_B("ReturnStatement") + + if (stmt->exp) { + stmt->exp->accept(this); + context_return.push_back(type_); + } + + BLOCK_E("ReturnStatement") +} + +void TypeChecker::visit(ast::PrintStatement *stmt) { + BLOCK_B("PrintStatement") + + if (stmt->exp) { + stmt->exp->accept(this); + if (!std::dynamic_pointer_cast (type_) && + !std::dynamic_pointer_cast (type_) && + !std::dynamic_pointer_cast (type_)) { + GERROR("Print operand is neither int nor real nor bool type") + } + } + + BLOCK_E("PrintStatement") +} + +void TypeChecker::visit(ast::AssignmentStatement *stmt) { + BLOCK_B("AssignmentStatement") + + stmt->id->accept(this); + node_ptr type__ = type_; + + stmt->exp->accept(this); + if (!type_equal(type_, type__)) { + GERROR("Variable and expression in assignment have different types") + } + + BLOCK_E("AssignmentStatement") +} + +void TypeChecker::visit(ast::IfStatement *stmt) { + BLOCK_B("IfStatement") + + stmt->cond->accept(this); + if (!std::dynamic_pointer_cast (type_) && + !std::dynamic_pointer_cast (type_) && + !std::dynamic_pointer_cast (type_)) { + GERROR("If condition has neither int nor real nor bool type") + } + stmt->then_body->accept(this); + if (stmt->else_body) + stmt->else_body->accept(this); + + BLOCK_E("IfStatement") +} + +void TypeChecker::visit(ast::WhileLoop *stmt) { + BLOCK_B("WhileLoop") + + stmt->cond->accept(this); + if (!std::dynamic_pointer_cast (type_) && + !std::dynamic_pointer_cast (type_) && + !std::dynamic_pointer_cast (type_)) { + GERROR("While condition has neither int nor real nor bool type") + } + stmt->body->accept(this); + + BLOCK_E("WhileLoop") +} + +// For loop is just a fancy while loop :) +void TypeChecker::visit(ast::ForLoop *stmt) { + BLOCK_B("ForLoop") + + stmt->loop_var->accept(this); + + stmt->cond->accept(this); + if (!std::dynamic_pointer_cast (type_) && + !std::dynamic_pointer_cast (type_) && + !std::dynamic_pointer_cast (type_)) { + GERROR("For condition has neither int nor real nor bool type") + } + stmt->action->accept(this); + stmt->body->accept(this); + + context_expr.pop_back(); + + BLOCK_E("ForLoop") +} + +void TypeChecker::visit(ast::RoutineCall *stmt) { + BLOCK_B("RoutineCall") + + int pos = find_in_context_expr(stmt->routine->name); + if (pos == -1) { + GERROR("Routine " << stmt->routine->name << " is not declared") + } + + node_ptr type__ = context_expr[pos].second; + for (auto a = stmt->args.rbegin(); a != stmt->args.rend(); a++) { + node_ptr function_type = std::dynamic_pointer_cast (type__); + if (!function_type) { + GERROR("Application applied to not function") + } + (*a)->accept(this); + if (!type_equal(function_type->from, type_)) { + GERROR("Application types of argument mismatch") + } + type__ = function_type->to; + } + type_ = type__; + + BLOCK_E("RoutineCall") +} +} // namespace ast diff --git a/type_checker.hpp b/type_checker.hpp new file mode 100755 index 0000000..6a45ba5 --- /dev/null +++ b/type_checker.hpp @@ -0,0 +1,53 @@ +#ifndef TYPE_CHECKER_H +#define TYPE_CHECKER_H + +#include "ast.hpp" +#include "shell.hpp" + +namespace ast { + +// Visits AST nodes and checks types. +class TypeChecker : public Visitor { +public: + TypeChecker(); + void generate(); + void visit(ast::Program *program) override; + void visit(ast::EmptyType *it) override; + void visit(ast::IntType *it) override; + void visit(ast::RealType *rt) override; + void visit(ast::BoolType *bt) override; + void visit(ast::ArrayType *at) override; + void visit(ast::RecordType *rt) override; + void visit(ast::FunctionType *ft) override; + void visit(ast::IntLiteral *il) override; + void visit(ast::RealLiteral *rl) override; + void visit(ast::BoolLiteral *bl) override; + void visit(ast::VariableDeclaration *vardecl) override; + void visit(ast::Identifier *id) override; + void visit(ast::UnaryExpression *exp) override; + void visit(ast::BinaryExpression *exp) override; + void visit(ast::RoutineDeclaration *routine) override; + void visit(ast::Body *body) override; + void visit(ast::ReturnStatement *stmt) override; + void visit(ast::PrintStatement *stmt) override; + void visit(ast::AssignmentStatement *stmt) override; + void visit(ast::IfStatement *stmt) override; + void visit(ast::WhileLoop *stmt) override; + void visit(ast::ForLoop *stmt) override; + void visit(ast::RoutineCall *stmt) override; + +private: + node_ptr type_; + std::vector>> context_expr; + std::vector> context_return; + std::vector record_stack; + + bool type_equal(node_ptr type1, node_ptr type2); + int find_in_context_expr(std::string name); + std::string build_variable_name(); + + int spaces = 0; +}; +} // namespace ast + +#endif // TYPE_CHECKER_H