diff --git a/gcc/elna-diagnostic.cc b/gcc/elna-diagnostic.cc index 3bfaaab..c41592a 100644 --- a/gcc/elna-diagnostic.cc +++ b/gcc/elna-diagnostic.cc @@ -1,26 +1,32 @@ #include "elna/gcc/elna-diagnostic.h" -location_t elna_gcc_location(const elna::source::position *position) +namespace elna { - linemap_line_start(line_table, position->line, 0); - - return linemap_position_for_column(line_table, position->column); -} - -const char *elna_gcc_print_type(tree type) +namespace gcc { - gcc_assert(TYPE_P(type)); + location_t get_location(const elna::source::position *position) + { + linemap_line_start(line_table, position->line, 0); - if (type == integer_type_node) - { - return "Int"; + return linemap_position_for_column(line_table, position->column); } - else if (type == boolean_type_node) + + const char *print_type(tree type) { - return "Boolean"; - } - else - { - return "<>"; + gcc_assert(TYPE_P(type)); + + if (type == integer_type_node) + { + return "Int"; + } + else if (type == boolean_type_node) + { + return "Boolean"; + } + else + { + return "<>"; + } } } +} diff --git a/gcc/elna-generic.cc b/gcc/elna-generic.cc index 0dccd6b..ca8bc87 100644 --- a/gcc/elna-generic.cc +++ b/gcc/elna-generic.cc @@ -13,15 +13,40 @@ namespace gcc { void generic_visitor::visit(source::call_statement *statement) { - empty_visitor::visit(statement); + if (statement->name() != "writei") + { + error_at(get_location(&statement->position()), + "procedure '%s' not declared", + statement->name().c_str()); + return; + } + if (statement->arguments().size() != 1) + { + error_at(get_location(&statement->position()), + "procedure '%s' expects 1 argument, %i given", + statement->name().c_str(), statement->arguments().size()); + return; + } + auto& argument = statement->arguments().at(0); + argument->accept(this); + auto argument_type = TREE_TYPE(this->current_expression); + auto argument_tree = this->current_expression; + this->current_expression = NULL_TREE; - const char *format_integer = "%d\n"; + if (argument_type != integer_type_node) + { + error_at(get_location(&argument->position()), + "invalid argument of type %s for procedure %s", + print_type(argument_type), statement->name().c_str()); + return; + } + constexpr const char *format_integer = "%d\n"; tree args[] = { build_string_literal(strlen(format_integer) + 1, format_integer), - this->current_expression + argument_tree }; tree fndecl_type_param[] = { - build_pointer_type (build_qualified_type(char_type_node, TYPE_QUAL_CONST)) /* const char* */ + build_pointer_type(build_qualified_type(char_type_node, TYPE_QUAL_CONST)) /* const char* */ }; tree fndecl_type = build_varargs_function_type_array(integer_type_node, 1, fndecl_type_param); @@ -39,12 +64,12 @@ namespace gcc { tree main_fndecl_type_param[] = { integer_type_node, - build_pointer_type(build_pointer_type (char_type_node)) + build_pointer_type(build_pointer_type(char_type_node)) }; tree main_fndecl_type = build_function_type_array(integer_type_node, 2, main_fndecl_type_param); - tree main_fndecl = build_fn_decl("main", main_fndecl_type); + this->main_fndecl = build_fn_decl("main", main_fndecl_type); tree resdecl = build_decl(UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE, integer_type_node); - DECL_RESULT(main_fndecl) = resdecl; + DECL_RESULT(this->main_fndecl) = resdecl; tree set_result = build2(INIT_EXPR, void_type_node, DECL_RESULT(main_fndecl), build_int_cst_type(integer_type_node, 0)); tree return_stmt = build1(RETURN_EXPR, void_type_node, set_result); @@ -58,16 +83,16 @@ namespace gcc tree new_block = build_block(NULL_TREE, NULL_TREE, NULL_TREE, NULL_TREE); tree bind_expr = build3(BIND_EXPR, void_type_node, NULL_TREE, this->current_statements, new_block); - BLOCK_SUPERCONTEXT(new_block) = main_fndecl; - DECL_INITIAL(main_fndecl) = new_block; - DECL_SAVED_TREE(main_fndecl) = bind_expr; + BLOCK_SUPERCONTEXT(new_block) = this->main_fndecl; + DECL_INITIAL(this->main_fndecl) = new_block; + DECL_SAVED_TREE(this->main_fndecl) = bind_expr; - DECL_EXTERNAL(main_fndecl) = 0; - DECL_PRESERVE_P(main_fndecl) = 1; + DECL_EXTERNAL(this->main_fndecl) = 0; + DECL_PRESERVE_P(this->main_fndecl) = 1; - gimplify_function_tree(main_fndecl); + gimplify_function_tree(this->main_fndecl); - cgraph_node::finalize_function(main_fndecl, true); + cgraph_node::finalize_function(this->main_fndecl, true); } void generic_visitor::visit(source::integer_literal *literal) @@ -84,11 +109,14 @@ namespace gcc { expression->lhs().accept(this); auto left = this->current_expression; + auto left_type = TREE_TYPE(left); expression->rhs().accept(this); auto right = this->current_expression; + auto right_type = TREE_TYPE(right); - tree_code operator_code{}; + auto expression_location = get_location(&expression->position()); + tree_code operator_code = ERROR_MARK; switch (expression->operation()) { @@ -104,23 +132,63 @@ namespace gcc case source::binary_operator::multiplication: operator_code = MULT_EXPR; break; - default: - gcc_unreachable(); } - - this->current_expression = build2(operator_code, integer_type_node, left, right); + if (operator_code != ERROR_MARK) // An arithmetic operation. + { + if (left_type != integer_type_node || right_type != integer_type_node) + { + error_at(expression_location, + "invalid operands of type %s and %s for operator %s", + print_type(left_type), print_type(right_type), + elna::source::print_binary_operator(expression->operation())); + this->current_expression = error_mark_node; + return; + } + } + switch (expression->operation()) + { + case source::binary_operator::equals: + operator_code = EQ_EXPR; + break; + case source::binary_operator::not_equals: + operator_code = NE_EXPR; + break; + case source::binary_operator::less: + operator_code = LT_EXPR; + break; + case source::binary_operator::greater: + operator_code = GT_EXPR; + break; + case source::binary_operator::less_equal: + operator_code = LE_EXPR; + break; + case source::binary_operator::greater_equal: + operator_code = GE_EXPR; + break; + } + if (left_type != right_type) + { + error_at(expression_location, + "invalid operands of type %s and %s for operator %s", + print_type(left_type), print_type(right_type), + elna::source::print_binary_operator(expression->operation())); + this->current_expression = error_mark_node; + return; + } + this->current_expression = build2_loc(expression_location, + operator_code, integer_type_node, left, right); } void generic_visitor::visit(source::declaration *declaration) { if (declaration->type().base() != "Int" && declaration->type().base() != "Bool") { - error_at(elna_gcc_location(&declaration->type().position()), + error_at(get_location(&declaration->type().position()), "type '%s' not declared", declaration->type().base().c_str()); return; } - auto declaration_location = elna_gcc_location(&declaration->position()); + auto declaration_location = get_location(&declaration->position()); tree declaration_tree = build_decl(declaration_location, VAR_DECL, get_identifier(declaration->identifier().c_str()), integer_type_node); auto result = this->symbol_map.insert({ declaration->identifier(), declaration_tree }); @@ -145,7 +213,7 @@ namespace gcc if (symbol == this->symbol_map.end()) { - error_at(elna_gcc_location(&expression->position()), + error_at(get_location(&expression->position()), "variable '%s' not declared in the current scope", expression->name().c_str()); this->current_expression = error_mark_node; @@ -157,7 +225,7 @@ namespace gcc void generic_visitor::visit(source::assign_statement *statement) { auto lvalue = this->symbol_map.find(statement->lvalue()); - auto statement_location = elna_gcc_location(&statement->position()); + auto statement_location = get_location(&statement->position()); if (lvalue == this->symbol_map.end()) { @@ -170,11 +238,11 @@ namespace gcc if (TREE_TYPE(this->current_expression) != TREE_TYPE(lvalue->second)) { - error_at(elna_gcc_location(&statement->position()), + error_at(get_location(&statement->position()), "cannot assign value of type %s to variable '%s' of type %s", - elna_gcc_print_type(TREE_TYPE(this->current_expression)), + print_type(TREE_TYPE(this->current_expression)), statement->lvalue().c_str(), - elna_gcc_print_type(TREE_TYPE(lvalue->second))); + print_type(TREE_TYPE(lvalue->second))); this->current_expression = error_mark_node; return; } @@ -184,5 +252,102 @@ namespace gcc append_to_statement_list(assignment, &this->current_statements); this->current_expression = NULL_TREE; } + + void generic_visitor::visit(source::if_statement *statement) + { + statement->prerequisite().accept(this); + + if (TREE_TYPE(this->current_expression) != boolean_type_node) + { + error_at(get_location(&statement->prerequisite().position()), + "expected expression of boolean type but its type is %s", + print_type(TREE_TYPE(this->current_expression))); + this->current_expression = error_mark_node; + return; + } + auto then_location = get_location(&statement->body().position()); + auto prerequisite_location = get_location(&statement->prerequisite().position()); + + auto then_label_decl = build_label_decl("then", then_location); + auto endif_label_decl = build_label_decl("end_if", then_location); + + auto goto_then = build1_loc(prerequisite_location, GOTO_EXPR, + void_type_node, then_label_decl); + auto goto_endif = build1_loc(prerequisite_location, GOTO_EXPR, + void_type_node, endif_label_decl); + + auto cond_expr = build3_loc(prerequisite_location, COND_EXPR, + void_type_node, this->current_expression, goto_then, goto_endif); + append_to_statement_list(cond_expr, &this->current_statements); + + auto then_label_expr = build1_loc(then_location, LABEL_EXPR, + void_type_node, then_label_decl); + append_to_statement_list(then_label_expr, &this->current_statements); + + statement->body().accept(this); + + auto endif_label_expr = build1(LABEL_EXPR, void_type_node, endif_label_decl); + append_to_statement_list(endif_label_expr, &this->current_statements); + + this->current_expression = NULL_TREE; + } + + tree generic_visitor::build_label_decl(const char *name, location_t loc) + { + auto label_decl = build_decl(loc, + LABEL_DECL, get_identifier(name), void_type_node); + + DECL_CONTEXT(label_decl) = this->main_fndecl; + + return label_decl; + } + + void generic_visitor::visit(source::while_statement *statement) + { + statement->prerequisite().accept(this); + + if (TREE_TYPE(this->current_expression) != boolean_type_node + && TREE_TYPE(this->current_expression) != integer_type_node) + { + error_at(get_location(&statement->prerequisite().position()), + "expected expression of boolean type but its type is %s", + print_type(TREE_TYPE(this->current_expression))); + this->current_expression = error_mark_node; + return; + } + auto prerequisite_location = get_location(&statement->prerequisite().position()); + auto body_location = get_location(&statement->body().position()); + + auto prerequisite_label_decl = build_label_decl("while_check", prerequisite_location); + auto prerequisite_label_expr = build1_loc(prerequisite_location, LABEL_EXPR, + void_type_node, prerequisite_label_decl); + append_to_statement_list(prerequisite_label_expr, &this->current_statements); + + auto body_label_decl = build_label_decl("while_body", body_location); + auto end_label_decl = build_label_decl("end_while", UNKNOWN_LOCATION); + + auto goto_body = build1_loc(prerequisite_location, GOTO_EXPR, + void_type_node, body_label_decl); + auto goto_end = build1_loc(prerequisite_location, GOTO_EXPR, + void_type_node, end_label_decl); + + auto cond_expr = build3_loc(prerequisite_location, COND_EXPR, + void_type_node, this->current_expression, goto_body, goto_end); + append_to_statement_list(cond_expr, &this->current_statements); + + auto body_label_expr = build1_loc(body_location, LABEL_EXPR, + void_type_node, body_label_decl); + append_to_statement_list(body_label_expr, &this->current_statements); + + statement->body().accept(this); + + auto goto_check = build1(GOTO_EXPR, void_type_node, prerequisite_label_decl); + append_to_statement_list(goto_check, &this->current_statements); + + auto endif_label_expr = build1(LABEL_EXPR, void_type_node, end_label_decl); + append_to_statement_list(endif_label_expr, &this->current_statements); + + this->current_expression = NULL_TREE; + } } } diff --git a/gcc/elna1.cc b/gcc/elna1.cc index fe994b1..574b196 100644 --- a/gcc/elna1.cc +++ b/gcc/elna1.cc @@ -92,7 +92,7 @@ static void elna_parse_file (const char *filename) { for (const auto& error : driver.errors()) { - auto gcc_location = elna_gcc_location(&error->position); + auto gcc_location = elna::gcc::get_location(&error->position); error_at(gcc_location, error->what().c_str()); } diff --git a/include/elna/gcc/elna-diagnostic.h b/include/elna/gcc/elna-diagnostic.h index e8f3de0..65fda53 100644 --- a/include/elna/gcc/elna-diagnostic.h +++ b/include/elna/gcc/elna-diagnostic.h @@ -8,6 +8,12 @@ #include "elna/source/result.h" -location_t elna_gcc_location(const elna::source::position *position); +namespace elna +{ +namespace gcc +{ + location_t get_location(const elna::source::position *position); -const char *elna_gcc_print_type(tree type); + const char *print_type(tree type); +} +} diff --git a/include/elna/gcc/elna-generic.h b/include/elna/gcc/elna-generic.h index 388c40a..4254f02 100644 --- a/include/elna/gcc/elna-generic.h +++ b/include/elna/gcc/elna-generic.h @@ -20,6 +20,9 @@ namespace gcc tree current_statements{ NULL_TREE }; tree current_expression{ NULL_TREE }; std::unordered_map symbol_map; + tree main_fndecl{ NULL_TREE }; + + tree build_label_decl (const char *name, location_t loc); public: void visit(source::program *program) override; @@ -30,6 +33,8 @@ namespace gcc void visit(source::declaration *declaration) override; void visit(source::variable_expression *expression) override; void visit(source::assign_statement *statement) override; + void visit(source::if_statement *statement) override; + void visit(source::while_statement *statement) override; }; } } diff --git a/include/elna/source/ast.h b/include/elna/source/ast.h index 1e6e797..cc3188a 100644 --- a/include/elna/source/ast.h +++ b/include/elna/source/ast.h @@ -486,5 +486,7 @@ namespace source expression& operand(); unary_operator operation() const noexcept; }; + + const char *print_binary_operator(const binary_operator operation); } } diff --git a/source/ast.cc b/source/ast.cc index 696569d..cbca9fe 100644 --- a/source/ast.cc +++ b/source/ast.cc @@ -525,5 +525,32 @@ namespace source { return *m_body; } + + const char *print_binary_operator(const binary_operator operation) + { + switch (operation) + { + case binary_operator::sum: + return "+"; + case binary_operator::subtraction: + return "-"; + case binary_operator::multiplication: + return "*"; + case binary_operator::division: + return "/"; + case binary_operator::equals: + return "="; + case binary_operator::not_equals: + return "/="; + case binary_operator::less: + return "<"; + case binary_operator::less_equal: + return "<="; + case binary_operator::greater: + return ">"; + case binary_operator::greater_equal: + return ">="; + } + }; } }