Implement while loops

This commit is contained in:
Eugen Wissner 2024-12-28 14:33:35 +01:00
parent d46608b358
commit 20949c7829
Signed by: belka
GPG Key ID: A27FDC1E8EE902C0
7 changed files with 257 additions and 46 deletions

View File

@ -1,26 +1,32 @@
#include "elna/gcc/elna-diagnostic.h" #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); namespace gcc
return linemap_position_for_column(line_table, position->column);
}
const char *elna_gcc_print_type(tree type)
{ {
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 linemap_position_for_column(line_table, position->column);
{
return "Int";
} }
else if (type == boolean_type_node)
const char *print_type(tree type)
{ {
return "Boolean"; gcc_assert(TYPE_P(type));
}
else if (type == integer_type_node)
{ {
return "<<unknown-type>>"; return "Int";
}
else if (type == boolean_type_node)
{
return "Boolean";
}
else
{
return "<<unknown-type>>";
}
} }
} }
}

View File

@ -13,15 +13,40 @@ 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* */
}; };
tree fndecl_type = build_varargs_function_type_array(integer_type_node, 1, fndecl_type_param); 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[] = { tree main_fndecl_type_param[] = {
integer_type_node, 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_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;
}
} }
} }

View File

@ -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());
} }

View File

@ -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);
}
}

View File

@ -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;
}; };
} }
} }

View File

@ -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);
} }
} }

View File

@ -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 ">=";
}
};
} }
} }