Implement while loops
This commit is contained in:
parent
d46608b358
commit
20949c7829
@ -1,13 +1,17 @@
|
|||||||
#include "elna/gcc/elna-diagnostic.h"
|
#include "elna/gcc/elna-diagnostic.h"
|
||||||
|
|
||||||
location_t elna_gcc_location(const elna::source::position *position)
|
namespace elna
|
||||||
|
{
|
||||||
|
namespace gcc
|
||||||
|
{
|
||||||
|
location_t get_location(const elna::source::position *position)
|
||||||
{
|
{
|
||||||
linemap_line_start(line_table, position->line, 0);
|
linemap_line_start(line_table, position->line, 0);
|
||||||
|
|
||||||
return linemap_position_for_column(line_table, position->column);
|
return linemap_position_for_column(line_table, position->column);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *elna_gcc_print_type(tree type)
|
const char *print_type(tree type)
|
||||||
{
|
{
|
||||||
gcc_assert(TYPE_P(type));
|
gcc_assert(TYPE_P(type));
|
||||||
|
|
||||||
@ -24,3 +28,5 @@ const char *elna_gcc_print_type(tree type)
|
|||||||
return "<<unknown-type>>";
|
return "<<unknown-type>>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -13,12 +13,37 @@ namespace gcc
|
|||||||
{
|
{
|
||||||
void generic_visitor::visit(source::call_statement *statement)
|
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[] = {
|
tree args[] = {
|
||||||
build_string_literal(strlen(format_integer) + 1, format_integer),
|
build_string_literal(strlen(format_integer) + 1, format_integer),
|
||||||
this->current_expression
|
argument_tree
|
||||||
};
|
};
|
||||||
tree fndecl_type_param[] = {
|
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* */
|
||||||
@ -42,9 +67,9 @@ namespace gcc
|
|||||||
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_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);
|
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),
|
tree set_result = build2(INIT_EXPR, void_type_node, DECL_RESULT(main_fndecl),
|
||||||
build_int_cst_type(integer_type_node, 0));
|
build_int_cst_type(integer_type_node, 0));
|
||||||
tree return_stmt = build1(RETURN_EXPR, void_type_node, set_result);
|
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 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);
|
tree bind_expr = build3(BIND_EXPR, void_type_node, NULL_TREE, this->current_statements, new_block);
|
||||||
|
|
||||||
BLOCK_SUPERCONTEXT(new_block) = main_fndecl;
|
BLOCK_SUPERCONTEXT(new_block) = this->main_fndecl;
|
||||||
DECL_INITIAL(main_fndecl) = new_block;
|
DECL_INITIAL(this->main_fndecl) = new_block;
|
||||||
DECL_SAVED_TREE(main_fndecl) = bind_expr;
|
DECL_SAVED_TREE(this->main_fndecl) = bind_expr;
|
||||||
|
|
||||||
DECL_EXTERNAL(main_fndecl) = 0;
|
DECL_EXTERNAL(this->main_fndecl) = 0;
|
||||||
DECL_PRESERVE_P(main_fndecl) = 1;
|
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)
|
void generic_visitor::visit(source::integer_literal *literal)
|
||||||
@ -84,11 +109,14 @@ namespace gcc
|
|||||||
{
|
{
|
||||||
expression->lhs().accept(this);
|
expression->lhs().accept(this);
|
||||||
auto left = this->current_expression;
|
auto left = this->current_expression;
|
||||||
|
auto left_type = TREE_TYPE(left);
|
||||||
|
|
||||||
expression->rhs().accept(this);
|
expression->rhs().accept(this);
|
||||||
auto right = this->current_expression;
|
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())
|
switch (expression->operation())
|
||||||
{
|
{
|
||||||
@ -104,23 +132,63 @@ namespace gcc
|
|||||||
case source::binary_operator::multiplication:
|
case source::binary_operator::multiplication:
|
||||||
operator_code = MULT_EXPR;
|
operator_code = MULT_EXPR;
|
||||||
break;
|
break;
|
||||||
default:
|
|
||||||
gcc_unreachable();
|
|
||||||
}
|
}
|
||||||
|
if (operator_code != ERROR_MARK) // An arithmetic operation.
|
||||||
this->current_expression = build2(operator_code, integer_type_node, left, right);
|
{
|
||||||
|
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)
|
void generic_visitor::visit(source::declaration *declaration)
|
||||||
{
|
{
|
||||||
if (declaration->type().base() != "Int" && declaration->type().base() != "Bool")
|
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",
|
"type '%s' not declared",
|
||||||
declaration->type().base().c_str());
|
declaration->type().base().c_str());
|
||||||
return;
|
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,
|
tree declaration_tree = build_decl(declaration_location, VAR_DECL,
|
||||||
get_identifier(declaration->identifier().c_str()), integer_type_node);
|
get_identifier(declaration->identifier().c_str()), integer_type_node);
|
||||||
auto result = this->symbol_map.insert({ declaration->identifier(), declaration_tree });
|
auto result = this->symbol_map.insert({ declaration->identifier(), declaration_tree });
|
||||||
@ -145,7 +213,7 @@ namespace gcc
|
|||||||
|
|
||||||
if (symbol == this->symbol_map.end())
|
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",
|
"variable '%s' not declared in the current scope",
|
||||||
expression->name().c_str());
|
expression->name().c_str());
|
||||||
this->current_expression = error_mark_node;
|
this->current_expression = error_mark_node;
|
||||||
@ -157,7 +225,7 @@ namespace gcc
|
|||||||
void generic_visitor::visit(source::assign_statement *statement)
|
void generic_visitor::visit(source::assign_statement *statement)
|
||||||
{
|
{
|
||||||
auto lvalue = this->symbol_map.find(statement->lvalue());
|
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())
|
if (lvalue == this->symbol_map.end())
|
||||||
{
|
{
|
||||||
@ -170,11 +238,11 @@ namespace gcc
|
|||||||
|
|
||||||
if (TREE_TYPE(this->current_expression) != TREE_TYPE(lvalue->second))
|
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",
|
"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(),
|
statement->lvalue().c_str(),
|
||||||
elna_gcc_print_type(TREE_TYPE(lvalue->second)));
|
print_type(TREE_TYPE(lvalue->second)));
|
||||||
this->current_expression = error_mark_node;
|
this->current_expression = error_mark_node;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -184,5 +252,102 @@ namespace gcc
|
|||||||
append_to_statement_list(assignment, &this->current_statements);
|
append_to_statement_list(assignment, &this->current_statements);
|
||||||
this->current_expression = NULL_TREE;
|
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())
|
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());
|
error_at(gcc_location, error->what().c_str());
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,12 @@
|
|||||||
|
|
||||||
#include "elna/source/result.h"
|
#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_statements{ NULL_TREE };
|
||||||
tree current_expression{ NULL_TREE };
|
tree current_expression{ NULL_TREE };
|
||||||
std::unordered_map<std::string, tree> symbol_map;
|
std::unordered_map<std::string, tree> symbol_map;
|
||||||
|
tree main_fndecl{ NULL_TREE };
|
||||||
|
|
||||||
|
tree build_label_decl (const char *name, location_t loc);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void visit(source::program *program) override;
|
void visit(source::program *program) override;
|
||||||
@ -30,6 +33,8 @@ namespace gcc
|
|||||||
void visit(source::declaration *declaration) override;
|
void visit(source::declaration *declaration) override;
|
||||||
void visit(source::variable_expression *expression) override;
|
void visit(source::variable_expression *expression) override;
|
||||||
void visit(source::assign_statement *statement) 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();
|
expression& operand();
|
||||||
unary_operator operation() const noexcept;
|
unary_operator operation() const noexcept;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const char *print_binary_operator(const binary_operator operation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -525,5 +525,32 @@ namespace source
|
|||||||
{
|
{
|
||||||
return *m_body;
|
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