746 lines
28 KiB
C++
746 lines
28 KiB
C++
#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"
|
|
#include "stor-layout.h"
|
|
#include "varasm.h"
|
|
#include <set>
|
|
|
|
namespace elna
|
|
{
|
|
namespace gcc
|
|
{
|
|
generic_visitor::generic_visitor()
|
|
{
|
|
this->symbol_map = std::make_shared<source::symbol_table<tree>>();
|
|
}
|
|
|
|
void generic_visitor::visit(source::call_statement *statement)
|
|
{
|
|
auto symbol = this->symbol_map->lookup(statement->name());
|
|
|
|
if (statement->name() == "writei")
|
|
{
|
|
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 if (is_pointer_type(argument_type))
|
|
{
|
|
format_number = "%p\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;
|
|
}
|
|
else if (symbol)
|
|
{
|
|
tree fndecl_type = build_function_type_list(integer_type_node, NULL_TREE);
|
|
tree printf_fn = build1(ADDR_EXPR, build_pointer_type(fndecl_type), symbol->payload);
|
|
|
|
tree stmt = build_call_nary(integer_type_node, printf_fn, 0);
|
|
|
|
append_to_statement_list(stmt, &this->current_statements);
|
|
this->current_expression = NULL_TREE;
|
|
}
|
|
else
|
|
{
|
|
error_at(get_location(&statement->position()),
|
|
"procedure '%s' not declared",
|
|
statement->name().c_str());
|
|
}
|
|
}
|
|
|
|
void generic_visitor::visit(source::program *program)
|
|
{
|
|
for (const auto& constant : program->type_definitions)
|
|
{
|
|
constant->accept(this);
|
|
}
|
|
|
|
tree parameter_types[] = {
|
|
integer_type_node,
|
|
build_pointer_type(build_pointer_type(char_type_node))
|
|
};
|
|
tree declaration_type = build_function_type_array(integer_type_node, 2, parameter_types);
|
|
this->main_fndecl = build_fn_decl("main", declaration_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();
|
|
|
|
for (const auto& definition : program->value_definitions)
|
|
{
|
|
definition->accept(this);
|
|
}
|
|
program->body().accept(this);
|
|
|
|
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::visit(source::procedure_definition *definition)
|
|
{
|
|
tree *parameter_types = reinterpret_cast<tree *>(xmalloc(definition->parameters().size() * sizeof(tree)));
|
|
|
|
for (std::size_t i = 0; i < definition->parameters().size(); ++i)
|
|
{
|
|
parameter_types[i] = build_type(definition->parameters().at(i)->type());
|
|
}
|
|
tree declaration_type = build_function_type_array(void_type_node,
|
|
definition->parameters().size(), parameter_types);
|
|
this->main_fndecl = build_fn_decl(definition->identifier().c_str(), declaration_type);
|
|
|
|
this->symbol_map->enter(definition->identifier(), source::make_info(this->main_fndecl));
|
|
|
|
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();
|
|
definition->body().accept(this);
|
|
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);
|
|
|
|
free(parameter_types);
|
|
}
|
|
|
|
void generic_visitor::enter_scope()
|
|
{
|
|
this->current_statements = alloc_stmt_list();
|
|
this->variable_chain = tree_chain();
|
|
this->symbol_map = std::make_shared<source::symbol_table<tree>>(this->symbol_map);
|
|
}
|
|
|
|
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);
|
|
this->symbol_map = this->symbol_map->scope();
|
|
|
|
return tree_symbol_mapping{ bind_expr, new_block };
|
|
}
|
|
|
|
void generic_visitor::visit(source::number_literal<std::int32_t> *literal)
|
|
{
|
|
this->current_expression = build_int_cst_type(integer_type_node, literal->number());
|
|
}
|
|
|
|
void generic_visitor::visit(source::number_literal<double> *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<bool> *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::unary_expression *expression)
|
|
{
|
|
switch (expression->operation())
|
|
{
|
|
case source::unary_operator::reference:
|
|
expression->operand().accept(this);
|
|
|
|
this->current_expression = build1_loc(get_location(&expression->position()), ADDR_EXPR,
|
|
build_pointer_type_for_mode(TREE_TYPE(this->current_expression), VOIDmode, true),
|
|
this->current_expression);
|
|
break;
|
|
}
|
|
}
|
|
|
|
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->enter(definition->identifier(), source::make_info(definition_tree));
|
|
|
|
if (result)
|
|
{
|
|
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::type_definition *definition)
|
|
{
|
|
tree tree_type = build_type(definition->body());
|
|
|
|
if (tree_type == NULL_TREE)
|
|
{
|
|
return;
|
|
}
|
|
location_t definition_location = get_location(&definition->position());
|
|
tree definition_tree = build_decl(definition_location, TYPE_DECL,
|
|
get_identifier(definition->identifier().c_str()), tree_type);
|
|
auto result = this->symbol_map->enter(definition->identifier(), source::make_info(definition_tree));
|
|
|
|
if (result)
|
|
{
|
|
DECL_CONTEXT(definition_tree) = this->main_fndecl;
|
|
variable_chain.append(definition_tree);
|
|
|
|
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,
|
|
"type '%s' already declared in this scope",
|
|
definition->identifier().c_str());
|
|
}
|
|
}
|
|
|
|
tree generic_visitor::build_type(source::type_expression& type)
|
|
{
|
|
if (source::basic_type_expression *basic_type = type.is_basic())
|
|
{
|
|
if (basic_type->base_name() == "Int")
|
|
{
|
|
return integer_type_node;
|
|
}
|
|
else if (basic_type->base_name() == "Bool")
|
|
{
|
|
return boolean_type_node;
|
|
}
|
|
else if (basic_type->base_name() == "Float")
|
|
{
|
|
return double_type_node;
|
|
}
|
|
else if (basic_type->base_name() == "Char")
|
|
{
|
|
return elna_char_type_node;
|
|
}
|
|
else if (basic_type->base_name() == "String")
|
|
{
|
|
return elna_string_type_node;
|
|
}
|
|
auto symbol = this->symbol_map->lookup(basic_type->base_name());
|
|
|
|
if (symbol)
|
|
{
|
|
return TREE_TYPE(symbol->payload);
|
|
}
|
|
error_at(get_location(&basic_type->position()),
|
|
"type '%s' not declared", basic_type->base_name().c_str());
|
|
|
|
return error_mark_node;
|
|
}
|
|
else if (source::array_type_expression *array_type = type.is_array())
|
|
{
|
|
tree lower_bound = build_int_cst_type(integer_type_node, 0);
|
|
tree upper_bound = build_int_cst_type(integer_type_node, array_type->size);
|
|
tree base_type = build_type(array_type->base());
|
|
|
|
if (base_type == NULL_TREE || base_type == error_mark_node)
|
|
{
|
|
return base_type;
|
|
}
|
|
tree range_type = build_range_type(integer_type_node, lower_bound, upper_bound);
|
|
|
|
return build_array_type(base_type, range_type);
|
|
}
|
|
else if (source::pointer_type_expression *pointer_type = type.is_pointer())
|
|
{
|
|
tree base_type = build_type(pointer_type->base());
|
|
|
|
if (base_type == NULL_TREE || base_type == error_mark_node)
|
|
{
|
|
return base_type;
|
|
}
|
|
return build_pointer_type_for_mode(base_type, VOIDmode, true);
|
|
}
|
|
else if (source::record_type_expression *record_type = type.is_record())
|
|
{
|
|
std::set<std::string> field_names;
|
|
tree record_type_node = make_node(RECORD_TYPE);
|
|
tree_chain record_chain;
|
|
|
|
for (auto& field : record_type->fields())
|
|
{
|
|
if (field_names.find(field.first) != field_names.cend())
|
|
{
|
|
error_at(get_location(&field.second->position()), "repeated field name");
|
|
return error_mark_node;
|
|
}
|
|
field_names.insert(field.first);
|
|
|
|
tree field_type = build_type(*field.second);
|
|
if (field_type == NULL_TREE || field_type == error_mark_node)
|
|
{
|
|
return field_type;
|
|
}
|
|
tree field_declaration = build_decl(get_location(&field.second->position()),
|
|
FIELD_DECL, get_identifier(field.first.c_str()), field_type);
|
|
TREE_ADDRESSABLE(field_declaration) = 1;
|
|
DECL_CONTEXT(field_declaration) = record_type_node;
|
|
|
|
record_chain.append(field_declaration);
|
|
}
|
|
TYPE_FIELDS(record_type_node) = record_chain.head();
|
|
layout_type(record_type_node);
|
|
|
|
return record_type_node;
|
|
}
|
|
return NULL_TREE;
|
|
}
|
|
|
|
void generic_visitor::visit(source::variable_declaration *declaration)
|
|
{
|
|
tree declaration_type = build_type(declaration->type());
|
|
gcc_assert(declaration_type != NULL_TREE);
|
|
|
|
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->enter(declaration->identifier(), source::make_info(declaration_tree));
|
|
|
|
if (result)
|
|
{
|
|
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->lookup(expression->name());
|
|
|
|
if (!symbol)
|
|
{
|
|
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->payload;
|
|
}
|
|
|
|
void generic_visitor::visit(source::array_access_expression *expression)
|
|
{
|
|
expression->base().accept(this);
|
|
tree designator = this->current_expression;
|
|
|
|
expression->index().accept(this);
|
|
tree index = this->current_expression;
|
|
|
|
tree element_type = TREE_TYPE(TREE_TYPE(designator));
|
|
|
|
this->current_expression = build4_loc(get_location(&expression->position()),
|
|
ARRAY_REF, element_type, designator, index, NULL_TREE, NULL_TREE);
|
|
}
|
|
|
|
void generic_visitor::visit(source::field_access_expression *expression)
|
|
{
|
|
expression->base().accept(this);
|
|
tree field_declaration = TYPE_FIELDS(TREE_TYPE(this->current_expression));
|
|
|
|
while (field_declaration != NULL_TREE)
|
|
{
|
|
tree declaration_name = DECL_NAME(field_declaration);
|
|
const char *identifier_pointer = IDENTIFIER_POINTER(declaration_name);
|
|
|
|
if (expression->field() == identifier_pointer)
|
|
{
|
|
break;
|
|
}
|
|
field_declaration = TREE_CHAIN(field_declaration);
|
|
}
|
|
location_t expression_location = get_location(&expression->position());
|
|
if (field_declaration == NULL_TREE)
|
|
{
|
|
error_at(expression_location,
|
|
"record type does not have a field named '%s'",
|
|
expression->field().c_str());
|
|
this->current_expression = error_mark_node;
|
|
}
|
|
else
|
|
{
|
|
this->current_expression = build3_loc(expression_location, COMPONENT_REF,
|
|
TREE_TYPE(field_declaration), this->current_expression,
|
|
field_declaration, NULL_TREE);
|
|
}
|
|
}
|
|
|
|
void generic_visitor::visit(source::dereference_expression *expression)
|
|
{
|
|
expression->base().accept(this);
|
|
|
|
this->current_expression = build1_loc(get_location(&expression->position()), INDIRECT_REF,
|
|
TREE_TYPE(TREE_TYPE(this->current_expression)), this->current_expression);
|
|
}
|
|
|
|
void generic_visitor::visit(source::assign_statement *statement)
|
|
{
|
|
statement->lvalue().accept(this);
|
|
|
|
auto lvalue = this->current_expression;
|
|
auto statement_location = get_location(&statement->position());
|
|
|
|
statement->rvalue().accept(this);
|
|
|
|
if (TREE_CODE(lvalue) == CONST_DECL)
|
|
{
|
|
error_at(statement_location, "cannot modify constant '%s'",
|
|
statement->lvalue().is_variable()->name().c_str());
|
|
this->current_expression = error_mark_node;
|
|
return;
|
|
}
|
|
if (TREE_TYPE(this->current_expression) != TREE_TYPE(lvalue))
|
|
{
|
|
error_at(statement_location,
|
|
"cannot assign value of type %s to variable of type %s",
|
|
print_type(TREE_TYPE(this->current_expression)),
|
|
print_type(TREE_TYPE(lvalue)));
|
|
this->current_expression = error_mark_node;
|
|
return;
|
|
}
|
|
auto assignment = build2_loc(statement_location, MODIFY_EXPR,
|
|
void_type_node, lvalue, 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;
|
|
}
|
|
}
|
|
}
|