Implement while loops
This commit is contained in:
parent
d46608b358
commit
20949c7829
@ -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 "<<unknown-type>>";
|
||||
gcc_assert(TYPE_P(type));
|
||||
|
||||
if (type == integer_type_node)
|
||||
{
|
||||
return "Int";
|
||||
}
|
||||
else if (type == boolean_type_node)
|
||||
{
|
||||
return "Boolean";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "<<unknown-type>>";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,9 @@ namespace gcc
|
||||
tree current_statements{ NULL_TREE };
|
||||
tree current_expression{ NULL_TREE };
|
||||
std::unordered_map<std::string, tree> 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;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -486,5 +486,7 @@ namespace source
|
||||
expression& operand();
|
||||
unary_operator operation() const noexcept;
|
||||
};
|
||||
|
||||
const char *print_binary_operator(const binary_operator operation);
|
||||
}
|
||||
}
|
||||
|
@ -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 ">=";
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user