#include "elna/gcc/elna-generic.h" #include "elna/gcc/elna-diagnostic.h" #include "input.h" #include "cgraph.h" #include "gimplify.h" #include "stringpool.h" #include "diagnostic.h" #include "realmpfr.h" namespace elna { namespace gcc { void generic_visitor::visit(source::call_statement *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, %lu 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); const char *format_number{ nullptr }; if (argument_type == integer_type_node) { format_number = "%d\n"; } else if (argument_type == double_type_node) { format_number = "%f\n"; } else if (argument_type == elna_char_type_node) { format_number = "%c\n"; } else if (is_string_type(argument_type)) { format_number = "%s\n"; } else { error_at(get_location(&argument->position()), "invalid argument of type %s for procedure %s", print_type(argument_type), statement->name().c_str()); this->current_expression = error_mark_node; return; } tree args[] = { build_string_literal(strlen(format_number) + 1, format_number), this->current_expression }; tree fndecl_type_param[] = { 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); tree printf_fn_decl = build_fn_decl("printf", fndecl_type); DECL_EXTERNAL(printf_fn_decl) = 1; tree printf_fn = build1(ADDR_EXPR, build_pointer_type(fndecl_type), printf_fn_decl); tree stmt = build_call_array(integer_type_node, printf_fn, 2, args); append_to_statement_list(stmt, &this->current_statements); this->current_expression = NULL_TREE; } void generic_visitor::visit(source::program *program) { tree main_fndecl_type_param[] = { integer_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); this->main_fndecl = build_fn_decl("main", main_fndecl_type); tree resdecl = build_decl(UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE, integer_type_node); DECL_CONTEXT(resdecl) = this->main_fndecl; 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); enter_scope(); empty_visitor::visit(program); append_to_statement_list(return_stmt, &this->current_statements); tree_symbol_mapping mapping = leave_scope(); BLOCK_SUPERCONTEXT(mapping.block()) = this->main_fndecl; DECL_INITIAL(this->main_fndecl) = mapping.block(); DECL_SAVED_TREE(this->main_fndecl) = mapping.bind_expression(); DECL_EXTERNAL(this->main_fndecl) = 0; DECL_PRESERVE_P(this->main_fndecl) = 1; gimplify_function_tree(this->main_fndecl); cgraph_node::finalize_function(this->main_fndecl, true); } void generic_visitor::enter_scope() { this->current_statements = alloc_stmt_list(); this->variable_chain = tree_chain(); } tree_symbol_mapping generic_visitor::leave_scope() { tree new_block = build_block(variable_chain.head(), NULL_TREE, NULL_TREE, NULL_TREE); tree bind_expr = build3(BIND_EXPR, void_type_node, variable_chain.head(), this->current_statements, new_block); return tree_symbol_mapping{ bind_expr, new_block }; } void generic_visitor::visit(source::number_literal *literal) { this->current_expression = build_int_cst_type(integer_type_node, literal->number()); } void generic_visitor::visit(source::number_literal *literal) { REAL_VALUE_TYPE real_value1; mpfr_t number; mpfr_init2(number, SIGNIFICAND_BITS); mpfr_set_d(number, literal->number(), MPFR_RNDN); real_from_mpfr(&real_value1, number, double_type_node, MPFR_RNDN); this->current_expression = build_real(double_type_node, real_value1); mpfr_clear(number); } void generic_visitor::visit(source::number_literal *boolean) { this->current_expression = build_int_cst_type(boolean_type_node, boolean->number()); } void generic_visitor::visit(source::char_literal *character) { this->current_expression = build_int_cstu(elna_char_type_node, character->character()); } void generic_visitor::visit(source::string_literal *string) { this->current_expression = build_string_literal(string->string().size() + 1, string->string().c_str()); } void generic_visitor::visit(source::binary_expression *expression) { 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); auto expression_location = get_location(&expression->position()); tree_code operator_code = ERROR_MARK; tree target_type = error_mark_node; 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; } switch (expression->operation()) { case source::binary_operator::sum: operator_code = PLUS_EXPR; target_type = left_type; break; case source::binary_operator::subtraction: operator_code = MINUS_EXPR; target_type = left_type; break; case source::binary_operator::division: operator_code = TRUNC_DIV_EXPR; target_type = left_type; break; case source::binary_operator::multiplication: operator_code = MULT_EXPR; target_type = left_type; break; default: break; } if (operator_code != ERROR_MARK) // An arithmetic operation. { if (target_type != integer_type_node && target_type != double_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; target_type = boolean_type_node; break; case source::binary_operator::not_equals: operator_code = NE_EXPR; target_type = boolean_type_node; break; case source::binary_operator::less: operator_code = LT_EXPR; target_type = boolean_type_node; break; case source::binary_operator::greater: operator_code = GT_EXPR; target_type = boolean_type_node; break; case source::binary_operator::less_equal: operator_code = LE_EXPR; target_type = boolean_type_node; break; case source::binary_operator::greater_equal: operator_code = GE_EXPR; target_type = boolean_type_node; break; default: break; } gcc_assert(operator_code != ERROR_MARK); gcc_assert(target_type != error_mark_node); this->current_expression = build2_loc(expression_location, operator_code, target_type, left, right); } void generic_visitor::visit(source::constant_definition *definition) { location_t definition_location = get_location(&definition->position()); tree definition_tree = build_decl(definition_location, CONST_DECL, get_identifier(definition->identifier().c_str()), integer_type_node); auto result = this->symbol_map.insert({ definition->identifier(), definition_tree }); if (result.second) { definition->body().accept(this); DECL_INITIAL(definition_tree) = build_int_cst_type(integer_type_node, definition->body().number()); TREE_CONSTANT(definition_tree) = 1; TREE_READONLY(definition_tree) = 1; auto declaration_statement = build1_loc(definition_location, DECL_EXPR, void_type_node, definition_tree); append_to_statement_list(declaration_statement, &this->current_statements); } else { error_at(definition_location, "variable '%s' already declared in this scope", definition->identifier().c_str()); } this->current_expression = NULL_TREE; } void generic_visitor::visit(source::declaration *declaration) { tree declaration_type = error_mark_node; source::basic_type_expression *basic_type = declaration->type().is_basic(); gcc_assert(basic_type != nullptr); if (basic_type->base() == "Int") { declaration_type = integer_type_node; } else if (basic_type->base() == "Bool") { declaration_type = boolean_type_node; } else if (basic_type->base() == "Float") { declaration_type = double_type_node; } else if (basic_type->base() == "Char") { declaration_type = elna_char_type_node; } else if (basic_type->base() == "String") { declaration_type = elna_string_type_node; } else { error_at(get_location(&declaration->type().position()), "type '%s' not declared", basic_type->base().c_str()); return; } auto declaration_location = get_location(&declaration->position()); tree declaration_tree = build_decl(declaration_location, VAR_DECL, get_identifier(declaration->identifier().c_str()), declaration_type); auto result = this->symbol_map.insert({ declaration->identifier(), declaration_tree }); if (result.second) { DECL_CONTEXT(declaration_tree) = this->main_fndecl; variable_chain.append(declaration_tree); auto declaration_statement = build1_loc(declaration_location, DECL_EXPR, void_type_node, declaration_tree); append_to_statement_list(declaration_statement, &this->current_statements); } else { error_at(declaration_location, "variable '%s' already declared in this scope", declaration->identifier().c_str()); } } void generic_visitor::visit(source::variable_expression *expression) { auto symbol = this->symbol_map.find(expression->name()); if (symbol == this->symbol_map.end()) { error_at(get_location(&expression->position()), "variable '%s' not declared in the current scope", expression->name().c_str()); this->current_expression = error_mark_node; return; } this->current_expression = symbol->second; } void generic_visitor::visit(source::assign_statement *statement) { auto lvalue = this->symbol_map.find(statement->lvalue()); auto statement_location = get_location(&statement->position()); if (lvalue == this->symbol_map.end()) { error_at(statement_location, "variable '%s' not declared in the current scope", statement->lvalue().c_str()); return; } statement->rvalue().accept(this); if (TREE_CODE(lvalue->second) == CONST_DECL) { error_at(statement_location, "cannot modify constant '%s'", statement->lvalue().c_str()); this->current_expression = error_mark_node; return; } if (TREE_TYPE(this->current_expression) != TREE_TYPE(lvalue->second)) { error_at(statement_location, "cannot assign value of type %s to variable '%s' of type %s", print_type(TREE_TYPE(this->current_expression)), statement->lvalue().c_str(), print_type(TREE_TYPE(lvalue->second))); this->current_expression = error_mark_node; return; } auto assignment = build2_loc(statement_location, MODIFY_EXPR, void_type_node, lvalue->second, this->current_expression); 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); tree else_label_decl = NULL_TREE; tree goto_else_or_endif = NULL_TREE; if (statement->alternative() != nullptr) { auto else_location = get_location(&statement->alternative()->position()); else_label_decl = build_label_decl("else", else_location); goto_else_or_endif = build1_loc(else_location, GOTO_EXPR, void_type_node, else_label_decl); } else { goto_else_or_endif = goto_endif; } auto cond_expr = build3_loc(prerequisite_location, COND_EXPR, void_type_node, this->current_expression, goto_then, goto_else_or_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); if (statement->alternative() != nullptr) { auto else_label_expr = build1(LABEL_EXPR, void_type_node, else_label_decl); append_to_statement_list(else_label_expr, &this->current_statements); statement->alternative()->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) { 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; } } }