|
|
|
@ -36,11 +36,20 @@ along with GCC; see the file COPYING3. If not see
|
|
|
|
|
|
|
|
|
|
namespace elna::gcc
|
|
|
|
|
{
|
|
|
|
|
tree get_inner_alias(const boot::type& type, std::shared_ptr<symbol_table> symbols)
|
|
|
|
|
tree get_inner_alias(const boot::type& type, std::shared_ptr<symbol_table> symbols,
|
|
|
|
|
std::unordered_map<std::string, tree>& unresolved)
|
|
|
|
|
{
|
|
|
|
|
if (auto reference = type.get<boot::primitive_type>())
|
|
|
|
|
{
|
|
|
|
|
return symbols->lookup(reference->identifier);
|
|
|
|
|
auto looked_up = unresolved.find(reference->identifier);
|
|
|
|
|
if (looked_up == unresolved.cend())
|
|
|
|
|
{
|
|
|
|
|
return symbols->lookup(reference->identifier);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return looked_up->second;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (auto reference = type.get<boot::record_type>())
|
|
|
|
|
{
|
|
|
|
@ -52,7 +61,7 @@ namespace elna::gcc
|
|
|
|
|
}
|
|
|
|
|
else if (auto reference = type.get<boot::pointer_type>())
|
|
|
|
|
{
|
|
|
|
|
return build_pointer_type_for_mode(get_inner_alias(reference->base, symbols), VOIDmode, true);
|
|
|
|
|
return build_global_pointer_type(get_inner_alias(reference->base, symbols, unresolved));
|
|
|
|
|
}
|
|
|
|
|
else if (auto reference = type.get<boot::array_type>())
|
|
|
|
|
{
|
|
|
|
@ -60,30 +69,31 @@ namespace elna::gcc
|
|
|
|
|
tree upper_bound = build_int_cst_type(integer_type_node, reference->size);
|
|
|
|
|
tree range_type = build_range_type(integer_type_node, lower_bound, upper_bound);
|
|
|
|
|
|
|
|
|
|
return build_array_type(get_inner_alias(reference->base, symbols), range_type);
|
|
|
|
|
return build_array_type(get_inner_alias(reference->base, symbols, unresolved), range_type);
|
|
|
|
|
}
|
|
|
|
|
else if (auto reference = type.get<boot::alias_type>())
|
|
|
|
|
{
|
|
|
|
|
return handle_symbol(reference->name, reference->reference, symbols);
|
|
|
|
|
return handle_symbol(reference->name, reference->reference, symbols, unresolved);
|
|
|
|
|
}
|
|
|
|
|
return error_mark_node;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tree handle_symbol(const std::string& symbol_name, const boot::type& type, std::shared_ptr<symbol_table> symbols)
|
|
|
|
|
tree handle_symbol(const std::string& symbol_name, const boot::type& type,
|
|
|
|
|
std::shared_ptr<symbol_table> symbols, std::unordered_map<std::string, tree>& unresolved)
|
|
|
|
|
{
|
|
|
|
|
auto looked_up = symbols->lookup(symbol_name);
|
|
|
|
|
|
|
|
|
|
if (looked_up == NULL_TREE)
|
|
|
|
|
{
|
|
|
|
|
looked_up = get_inner_alias(type, symbols);
|
|
|
|
|
symbols->enter(symbol_name, looked_up);
|
|
|
|
|
looked_up = get_inner_alias(type, symbols, unresolved);
|
|
|
|
|
unresolved.insert({ symbol_name, looked_up });
|
|
|
|
|
}
|
|
|
|
|
return looked_up;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::deque<std::unique_ptr<boot::error>> do_semantic_analysis(const char *path,
|
|
|
|
|
std::unique_ptr<boot::program>& ast, std::shared_ptr<boot::symbol_table> info_table,
|
|
|
|
|
std::shared_ptr<symbol_table> symbols)
|
|
|
|
|
std::shared_ptr<symbol_table> symbols, std::unordered_map<std::string, tree>& unresolved)
|
|
|
|
|
{
|
|
|
|
|
boot::declaration_visitor declaration_visitor(path, info_table);
|
|
|
|
|
|
|
|
|
@ -93,15 +103,16 @@ namespace elna::gcc
|
|
|
|
|
{
|
|
|
|
|
for (auto& [symbol_name, symbol_info] : declaration_visitor.unresolved)
|
|
|
|
|
{
|
|
|
|
|
handle_symbol(symbol_name, boot::type(symbol_info), symbols);
|
|
|
|
|
handle_symbol(symbol_name, boot::type(symbol_info), symbols, unresolved);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return std::move(declaration_visitor.errors());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
generic_visitor::generic_visitor(std::shared_ptr<symbol_table> symbol_table)
|
|
|
|
|
generic_visitor::generic_visitor(std::shared_ptr<symbol_table> symbol_table,
|
|
|
|
|
std::unordered_map<std::string, tree>&& unresolved)
|
|
|
|
|
: symbols(symbol_table), unresolved(std::move(unresolved))
|
|
|
|
|
{
|
|
|
|
|
this->symbols = symbol_table;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void generic_visitor::build_procedure_call(location_t call_location,
|
|
|
|
@ -201,14 +212,14 @@ namespace elna::gcc
|
|
|
|
|
? this->current_expression
|
|
|
|
|
: TREE_TYPE(this->current_expression);
|
|
|
|
|
|
|
|
|
|
if (TYPE_P(this->current_expression) && TREE_CODE(expression_type) == RECORD_TYPE)
|
|
|
|
|
if (TREE_CODE(expression_type) == RECORD_TYPE)
|
|
|
|
|
{
|
|
|
|
|
build_record_call(call_location, this->current_expression, call->arguments);
|
|
|
|
|
build_record_call(call_location, expression_type, call->arguments);
|
|
|
|
|
}
|
|
|
|
|
else if (TREE_CODE(expression_type) == FUNCTION_TYPE)
|
|
|
|
|
{
|
|
|
|
|
this->current_expression = build1(ADDR_EXPR,
|
|
|
|
|
build_pointer_type_for_mode(expression_type, VOIDmode, true), this->current_expression);
|
|
|
|
|
build_global_pointer_type(expression_type), this->current_expression);
|
|
|
|
|
build_procedure_call(call_location, this->current_expression, call->arguments);
|
|
|
|
|
}
|
|
|
|
|
else if (is_pointer_type(expression_type) && TREE_CODE(TREE_TYPE(expression_type)) == FUNCTION_TYPE)
|
|
|
|
@ -263,8 +274,8 @@ namespace elna::gcc
|
|
|
|
|
{
|
|
|
|
|
procedure->accept(this);
|
|
|
|
|
}
|
|
|
|
|
tree declaration_type = build_function_type_list(integer_type_node, integer_type_node,
|
|
|
|
|
build_pointer_type(build_pointer_type(char_type_node)), NULL_TREE);
|
|
|
|
|
tree declaration_type = build_function_type_list(integer_type_node, elna_int_type_node,
|
|
|
|
|
build_global_pointer_type(build_global_pointer_type(elna_char_type_node)), NULL_TREE);
|
|
|
|
|
tree fndecl = build_fn_decl("main", declaration_type);
|
|
|
|
|
|
|
|
|
|
tree resdecl = build_decl(UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE, integer_type_node);
|
|
|
|
@ -702,7 +713,7 @@ namespace elna::gcc
|
|
|
|
|
TREE_ADDRESSABLE(this->current_expression) = 1;
|
|
|
|
|
this->current_expression = build_fold_addr_expr_with_type_loc(location,
|
|
|
|
|
this->current_expression,
|
|
|
|
|
build_pointer_type_for_mode(TREE_TYPE(this->current_expression), VOIDmode, true));
|
|
|
|
|
build_global_pointer_type(TREE_TYPE(this->current_expression)));
|
|
|
|
|
TREE_NO_TRAMPOLINE(this->current_expression) = 1;
|
|
|
|
|
break;
|
|
|
|
|
case boot::unary_operator::negation:
|
|
|
|
@ -763,8 +774,7 @@ namespace elna::gcc
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
error_at(definition_location,
|
|
|
|
|
"variable '%s' already declared in this scope",
|
|
|
|
|
error_at(definition_location, "Variable '%s' already declared in this scope",
|
|
|
|
|
definition->identifier.c_str());
|
|
|
|
|
}
|
|
|
|
|
this->current_expression = NULL_TREE;
|
|
|
|
@ -773,15 +783,18 @@ namespace elna::gcc
|
|
|
|
|
void generic_visitor::visit(boot::type_definition *definition)
|
|
|
|
|
{
|
|
|
|
|
location_t definition_location = get_location(&definition->position());
|
|
|
|
|
this->current_expression = this->symbols->lookup(definition->identifier);
|
|
|
|
|
this->current_expression = this->unresolved.at(definition->identifier);
|
|
|
|
|
definition->body().accept(this);
|
|
|
|
|
|
|
|
|
|
tree definition_tree = build_decl(definition_location, TYPE_DECL,
|
|
|
|
|
get_identifier(definition->identifier.c_str()), this->current_expression);
|
|
|
|
|
auto result = this->symbols->enter(definition->identifier, this->current_expression);
|
|
|
|
|
|
|
|
|
|
TREE_PUBLIC(definition_tree) = definition->exported;
|
|
|
|
|
TYPE_NAME(this->current_expression) = get_identifier(definition->identifier.c_str());
|
|
|
|
|
|
|
|
|
|
auto result = this->symbols->enter(definition->identifier, definition_tree);
|
|
|
|
|
gcc_assert(result);
|
|
|
|
|
|
|
|
|
|
this->current_expression = NULL_TREE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -878,8 +891,7 @@ namespace elna::gcc
|
|
|
|
|
|
|
|
|
|
if (symbol == NULL_TREE)
|
|
|
|
|
{
|
|
|
|
|
error_at(get_location(&expression->position()),
|
|
|
|
|
"symbol '%s' not declared in the current scope",
|
|
|
|
|
error_at(get_location(&expression->position()), "Symbol '%s' not declared in the current scope",
|
|
|
|
|
expression->name.c_str());
|
|
|
|
|
this->current_expression = error_mark_node;
|
|
|
|
|
}
|
|
|
|
@ -1051,7 +1063,7 @@ namespace elna::gcc
|
|
|
|
|
}
|
|
|
|
|
else if (is_array_type(aggregate_type) && expression->field() == "ptr")
|
|
|
|
|
{
|
|
|
|
|
tree ptr_type = build_pointer_type_for_mode(TREE_TYPE(aggregate_type), VOIDmode, true);
|
|
|
|
|
tree ptr_type = build_global_pointer_type(TREE_TYPE(aggregate_type));
|
|
|
|
|
this->current_expression = build1(ADDR_EXPR,
|
|
|
|
|
build_qualified_type(ptr_type, TYPE_QUAL_CONST), this->current_expression);
|
|
|
|
|
}
|
|
|
|
@ -1225,29 +1237,65 @@ namespace elna::gcc
|
|
|
|
|
void generic_visitor::visit(boot::return_statement *statement)
|
|
|
|
|
{
|
|
|
|
|
boot::expression *return_expression = statement->return_expression();
|
|
|
|
|
location_t statement_position = get_location(&statement->position());
|
|
|
|
|
tree set_result{ NULL_TREE };
|
|
|
|
|
tree return_type = TREE_TYPE(TREE_TYPE(current_function_decl));
|
|
|
|
|
|
|
|
|
|
if (return_expression == nullptr)
|
|
|
|
|
if (TREE_THIS_VOLATILE(current_function_decl) == 1)
|
|
|
|
|
{
|
|
|
|
|
error_at(statement_position, "This procedure is not allowed to return");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
return_expression->accept(this);
|
|
|
|
|
|
|
|
|
|
tree set_result = build2(INIT_EXPR, void_type_node, DECL_RESULT(current_function_decl),
|
|
|
|
|
this->current_expression);
|
|
|
|
|
tree return_stmt = build1(RETURN_EXPR, void_type_node, set_result);
|
|
|
|
|
append_statement(return_stmt);
|
|
|
|
|
if (return_expression != nullptr)
|
|
|
|
|
{
|
|
|
|
|
return_expression->accept(this);
|
|
|
|
|
|
|
|
|
|
set_result = build2(INIT_EXPR, void_type_node, DECL_RESULT(current_function_decl),
|
|
|
|
|
this->current_expression);
|
|
|
|
|
}
|
|
|
|
|
if (return_type == void_type_node && set_result != NULL_TREE)
|
|
|
|
|
{
|
|
|
|
|
error_at(statement_position, "Proper procedure is not allowed to return a value");
|
|
|
|
|
}
|
|
|
|
|
else if (return_type != void_type_node && set_result == NULL_TREE)
|
|
|
|
|
{
|
|
|
|
|
error_at(statement_position, "Procedure is expected to return a value of type '%s'",
|
|
|
|
|
print_type(return_type).c_str());
|
|
|
|
|
}
|
|
|
|
|
else if (return_type != void_type_node && !is_assignable_from(return_type, this->current_expression))
|
|
|
|
|
{
|
|
|
|
|
error_at(statement_position, "Cannot return '%s' from a procedure returning '%s'",
|
|
|
|
|
print_type(return_type).c_str(),
|
|
|
|
|
print_type(TREE_TYPE(this->current_expression)).c_str());
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
tree return_stmt = build1_loc(statement_position, RETURN_EXPR, void_type_node, set_result);
|
|
|
|
|
append_statement(return_stmt);
|
|
|
|
|
}
|
|
|
|
|
this->current_expression = NULL_TREE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void generic_visitor::visit(boot::primitive_type_expression *type)
|
|
|
|
|
{
|
|
|
|
|
tree symbol = this->symbols->lookup(type->name);
|
|
|
|
|
auto looked_up = this->unresolved.find(type->name);
|
|
|
|
|
tree symbol;
|
|
|
|
|
|
|
|
|
|
if (looked_up == this->unresolved.cend())
|
|
|
|
|
{
|
|
|
|
|
symbol = this->symbols->lookup(type->name);
|
|
|
|
|
if (symbol != NULL_TREE)
|
|
|
|
|
{
|
|
|
|
|
symbol = TREE_TYPE(symbol);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
symbol = looked_up->second;
|
|
|
|
|
}
|
|
|
|
|
if (symbol == NULL_TREE || !TYPE_P(symbol))
|
|
|
|
|
{
|
|
|
|
|
error_at(get_location(&type->position()),
|
|
|
|
|
"type '%s' not declared", type->name.c_str());
|
|
|
|
|
error_at(get_location(&type->position()), "Type '%s' not declared", type->name.c_str());
|
|
|
|
|
|
|
|
|
|
this->current_expression = error_mark_node;
|
|
|
|
|
}
|
|
|
|
@ -1277,7 +1325,7 @@ namespace elna::gcc
|
|
|
|
|
|
|
|
|
|
if (this->current_expression != NULL_TREE && this->current_expression != error_mark_node)
|
|
|
|
|
{
|
|
|
|
|
this->current_expression = build_pointer_type_for_mode(this->current_expression, VOIDmode, true);
|
|
|
|
|
this->current_expression = build_global_pointer_type(this->current_expression);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1302,7 +1350,7 @@ namespace elna::gcc
|
|
|
|
|
void generic_visitor::visit(boot::procedure_type_expression *type)
|
|
|
|
|
{
|
|
|
|
|
tree procedure_type_node = build_procedure_type(*type);
|
|
|
|
|
this->current_expression = build_pointer_type_for_mode(procedure_type_node, VOIDmode, true);
|
|
|
|
|
this->current_expression = build_global_pointer_type(procedure_type_node);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void generic_visitor::visit(boot::defer_statement *statement)
|
|
|
|
|