diff --git a/boot/ast.cc b/boot/ast.cc index e43951c..92e5bf5 100644 --- a/boot/ast.cc +++ b/boot/ast.cc @@ -33,36 +33,6 @@ namespace elna::boot return this->source_position; } - assign_statement *statement::is_assign() - { - return nullptr; - } - - if_statement *statement::is_if() - { - return nullptr; - } - - while_statement *statement::is_while() - { - return nullptr; - } - - return_statement *statement::is_return() - { - return nullptr; - } - - defer_statement *statement::is_defer() - { - return nullptr; - } - - procedure_call *statement::is_call_statement() - { - return nullptr; - } - cast_expression *expression::is_cast() { return nullptr; @@ -447,11 +417,6 @@ namespace elna::boot visitor->visit(this); } - defer_statement *defer_statement::is_defer() - { - return this; - } - defer_statement::~defer_statement() { for (statement *body_statement : statements) @@ -676,11 +641,6 @@ namespace elna::boot visitor->visit(this); } - procedure_call *procedure_call::is_call_statement() - { - return this; - } - procedure_call *procedure_call::is_call_expression() { return this; @@ -754,8 +714,8 @@ namespace elna::boot return this; } - conditional_statements::conditional_statements(expression *prerequisite) - : m_prerequisite(prerequisite) + conditional_statements::conditional_statements(expression *prerequisite, std::vector&& statements) + : m_prerequisite(prerequisite), statements(std::move(statements)) { } @@ -783,11 +743,6 @@ namespace elna::boot visitor->visit(this); } - return_statement *return_statement::is_return() - { - return this; - } - return_statement::~return_statement() { delete this->return_expression; @@ -820,11 +775,6 @@ namespace elna::boot visitor->visit(this); } - assign_statement *assign_statement::is_assign() - { - return this; - } - variable_expression *designator_expression::is_variable() { return nullptr; @@ -871,11 +821,6 @@ namespace elna::boot visitor->visit(this); } - if_statement *if_statement::is_if() - { - return this; - } - conditional_statements& if_statement::body() { return *m_body; @@ -891,8 +836,26 @@ namespace elna::boot delete this->alternative; } - while_statement::while_statement(const struct position position, conditional_statements *body) - : node(position), m_body(body) + escape_statement::escape_statement(const struct position position, + escape_direction direction, const std::string& label) + : node(position), direction(direction), label(label) + { + } + + void escape_statement::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + + while_statement::while_statement(const struct position position, conditional_statements *body, + std::vector&& branches) + : node(position), m_body(body), branches(std::move(branches)) + { + } + + while_statement::while_statement(const struct position position, conditional_statements *body, + std::vector&& branches, const std::string& label) + : node(position), m_body(body), branches(std::move(branches)), label(label) { } @@ -901,11 +864,6 @@ namespace elna::boot visitor->visit(this); } - while_statement *while_statement::is_while() - { - return this; - } - conditional_statements& while_statement::body() { return *m_body; diff --git a/boot/lexer.ll b/boot/lexer.ll index 9827ba0..181d900 100644 --- a/boot/lexer.ll +++ b/boot/lexer.ll @@ -125,6 +125,12 @@ or { return { return yy::parser::make_RETURN(this->location); } +break { + return yy::parser::make_BREAK(this->location); + } +repeat { + return yy::parser::make_REPEAT(this->location); + } cast { return yy::parser::make_CAST(this->location); } diff --git a/boot/parser.yy b/boot/parser.yy index 57eabb7..e4bc9c2 100644 --- a/boot/parser.yy +++ b/boot/parser.yy @@ -108,6 +108,8 @@ along with GCC; see the file COPYING3. If not see ELSE "else" ELSIF "elsif" RETURN "return" + REPEAT "repeat" + BREAK "break" BEGIN_BLOCK "begin" END_BLOCK "end" DEFER "defer" @@ -247,8 +249,7 @@ cast_expression: "cast" "(" expression ":" type_expression ")" elsif_do_statements: "elsif" expression "do" statements elsif_do_statements { - boot::conditional_statements *branch = new boot::conditional_statements($2); - std::swap(branch->statements, $4); + boot::conditional_statements *branch = new boot::conditional_statements($2, std::move($4)); std::swap($5, $$); $$.emplace($$.begin(), branch); } @@ -256,18 +257,21 @@ elsif_do_statements: else_statements: "else" statements { $$ = new std::vector(std::move($2)); } | { $$ = nullptr; } -while_statement: "while" expression "do" statements elsif_do_statements "end" +while_statement: + "while" expression "do" statements elsif_do_statements "end" { - auto body = new boot::conditional_statements($2); - std::swap($4, body->statements); - $$ = new boot::while_statement(boot::make_position(@1), body); - std::swap($5, $$->branches); + boot::conditional_statements *body = new boot::conditional_statements($2, std::move($4)); + $$ = new boot::while_statement(boot::make_position(@1), body, std::move($5)); + } + | "while" expression "," IDENTIFIER "do" statements elsif_do_statements "end" + { + boot::conditional_statements *body = new boot::conditional_statements($2, std::move($6)); + $$ = new boot::while_statement(boot::make_position(@1), body, std::move($7), $4); } elsif_then_statements: "elsif" expression "then" statements elsif_then_statements { - boot::conditional_statements *branch = new boot::conditional_statements($2); - std::swap(branch->statements, $4); + boot::conditional_statements *branch = new boot::conditional_statements($2, std::move($4)); std::swap($5, $$); $$.emplace($$.begin(), branch); } @@ -417,13 +421,16 @@ statement: | while_statement { $$ = $1; } | "if" expression "then" statements elsif_then_statements else_statements "end" { - auto then = new boot::conditional_statements($2); - std::swap($4, then->statements); + boot::conditional_statements *then = new boot::conditional_statements($2, std::move($4)); auto result = new boot::if_statement(boot::make_position(@1), then, $6); std::swap($5, result->branches); - $$ = result; + $$ = result; } | return_statement { $$ = $1; } + | "break" IDENTIFIER + { $$ = new boot::escape_statement(boot::make_position(@1), boot::escape_direction::end, $2); } + | "repeat" IDENTIFIER + { $$ = new boot::escape_statement(boot::make_position(@1), boot::escape_direction::begin, $2); } | call_expression { $$ = $1; } | "defer" statements "end" { $$ = new boot::defer_statement(boot::make_position(@1), std::move($2)); } | "case" expression "of" switch_cases else_statements "end" @@ -523,7 +530,7 @@ variable_declarations: variable_part: /* no variable declarations */ {} | "var" variable_declarations { std::swap($$, $2); } -constant_definition: identifier_definition "=" expression +constant_definition: identifier_definition ":=" expression { $$ = new boot::constant_definition(boot::make_position(@1), $1.first, $1.second, $3); } diff --git a/boot/semantic.cc b/boot/semantic.cc index 55c6fea..c7a41ee 100644 --- a/boot/semantic.cc +++ b/boot/semantic.cc @@ -239,6 +239,10 @@ namespace elna::boot } } + void declaration_visitor::visit(escape_statement *) + { + } + void declaration_visitor::visit(while_statement *statement) { statement->body().prerequisite().accept(this); diff --git a/gcc/elna-generic.cc b/gcc/elna-generic.cc index 10d7f85..ba4a96e 100644 --- a/gcc/elna-generic.cc +++ b/gcc/elna-generic.cc @@ -1174,7 +1174,7 @@ namespace elna::gcc void generic_visitor::visit(boot::if_statement *statement) { - tree endif_label_decl = build_label_decl("endif", UNKNOWN_LOCATION); + tree endif_label_decl = create_artificial_label(UNKNOWN_LOCATION); tree goto_endif = build1(GOTO_EXPR, void_type_node, endif_label_decl); make_if_branch(statement->body(), goto_endif); @@ -1202,7 +1202,7 @@ namespace elna::gcc if (TREE_TYPE(this->current_expression) != elna_bool_type_node) { error_at(get_location(&branch.prerequisite().position()), - "expected expression of boolean type but its type is %s", + "Expected expression of boolean type but its type is %s", print_type(TREE_TYPE(this->current_expression)).c_str()); this->current_expression = error_mark_node; return; @@ -1229,13 +1229,46 @@ namespace elna::gcc append_statement(else_label_expr); } + void generic_visitor::visit(boot::escape_statement *statement) + { + for (const auto& [begin, end] : this->loops) + { + if (statement->label == IDENTIFIER_POINTER(DECL_NAME(begin))) + { + tree target_declaration{ NULL_TREE }; + + switch (statement->direction) + { + case boot::escape_direction::begin: + target_declaration = begin; + break; + case boot::escape_direction::end: + target_declaration = end; + break; + default: + gcc_unreachable(); + } + tree goto_expression = build1(GOTO_EXPR, void_type_node, target_declaration); + TREE_USED(target_declaration) = 1; + append_statement(goto_expression); + return; + } + } + error_at(get_location(&statement->position()), "Unknown loop labeled '%s'", statement->label.c_str()); + } + void generic_visitor::visit(boot::while_statement *statement) { - auto prerequisite_location = get_location(&statement->body().prerequisite().position()); - auto prerequisite_label_decl = build_label_decl("while_check", prerequisite_location); + std::string loop_identifier = statement->label.value_or("while"); + location_t prerequisite_location = get_location(&statement->body().prerequisite().position()); + tree prerequisite_label_decl = build_label_decl(loop_identifier.c_str(), prerequisite_location); auto prerequisite_label_expr = build1_loc(prerequisite_location, LABEL_EXPR, void_type_node, prerequisite_label_decl); auto goto_check = build1(GOTO_EXPR, void_type_node, prerequisite_label_decl); + tree branch_end_declaration = build_label_decl(loop_identifier.c_str(), UNKNOWN_LOCATION); + tree branch_end_expression = build1_loc(UNKNOWN_LOCATION, LABEL_EXPR, void_type_node, branch_end_declaration); + + this->loops.push_front({ prerequisite_label_decl, branch_end_declaration }); append_statement(prerequisite_label_expr); make_if_branch(statement->body(), goto_check); @@ -1244,6 +1277,7 @@ namespace elna::gcc { make_if_branch(*branch, goto_check); } + append_statement(branch_end_expression); this->current_expression = NULL_TREE; } diff --git a/include/elna/boot/ast.h b/include/elna/boot/ast.h index 3934447..c2ae0ee 100644 --- a/include/elna/boot/ast.h +++ b/include/elna/boot/ast.h @@ -21,6 +21,7 @@ along with GCC; see the file COPYING3. If not see #include #include #include +#include #include "elna/boot/result.h" namespace elna::boot @@ -52,6 +53,12 @@ namespace elna::boot minus }; + enum class escape_direction + { + begin, + end + }; + class variable_declaration; class constant_definition; class procedure_definition; @@ -60,6 +67,7 @@ namespace elna::boot class cast_expression; class assign_statement; class if_statement; + class escape_statement; class while_statement; class return_statement; class case_statement; @@ -99,6 +107,7 @@ namespace elna::boot virtual void visit(traits_expression *) = 0; virtual void visit(assign_statement *) = 0; virtual void visit(if_statement *) = 0; + virtual void visit(escape_statement *) = 0; virtual void visit(while_statement *) = 0; virtual void visit(return_statement *) = 0; virtual void visit(defer_statement *) = 0; @@ -152,13 +161,6 @@ namespace elna::boot class statement : public virtual node { - public: - virtual assign_statement *is_assign(); - virtual if_statement *is_if(); - virtual while_statement *is_while(); - virtual return_statement *is_return(); - virtual defer_statement *is_defer(); - virtual procedure_call *is_call_statement(); }; class expression : public virtual node @@ -428,9 +430,9 @@ namespace elna::boot expression *m_prerequisite; public: - std::vector statements; + const std::vector statements; - conditional_statements(expression *prerequisite); + conditional_statements(expression *prerequisite, std::vector&& statements); expression& prerequisite(); @@ -444,7 +446,6 @@ namespace elna::boot return_statement(const struct position position, expression *return_expression = nullptr); void accept(parser_visitor *visitor) override; - virtual return_statement *is_return() override; virtual ~return_statement() override; }; @@ -558,7 +559,6 @@ namespace elna::boot procedure_call(const struct position position, designator_expression *callable); void accept(parser_visitor *visitor) override; - virtual procedure_call *is_call_statement() override; virtual procedure_call *is_call_expression() override; designator_expression& callable(); @@ -585,7 +585,6 @@ namespace elna::boot expression& rvalue(); virtual ~assign_statement() override; - assign_statement *is_assign() override; }; /** @@ -602,13 +601,23 @@ namespace elna::boot if_statement(const struct position position, conditional_statements *body, std::vector *alternative = nullptr); void accept(parser_visitor *visitor) override; - virtual if_statement *is_if() override; conditional_statements& body(); virtual ~if_statement() override; }; + class escape_statement : public statement + { + public: + const escape_direction direction; + const std::string label; + + escape_statement(const struct position position, + escape_direction direction, const std::string& label); + void accept(parser_visitor *visitor) override; + }; + /** * While-statement. */ @@ -617,10 +626,14 @@ namespace elna::boot conditional_statements *m_body; public: - std::vector branches; - while_statement(const struct position position, conditional_statements *body); + const std::vector branches; + const std::optional label; + + while_statement(const struct position position, conditional_statements *body, + std::vector&& branches); + while_statement(const struct position position, conditional_statements *body, + std::vector&& branches, const std::string& label); void accept(parser_visitor *visitor) override; - while_statement *is_while() override; conditional_statements& body(); @@ -676,7 +689,6 @@ namespace elna::boot defer_statement(const struct position position, std::vector&& statements); void accept(parser_visitor *visitor) override; - defer_statement *is_defer() override; virtual ~defer_statement() override; }; diff --git a/include/elna/boot/semantic.h b/include/elna/boot/semantic.h index a74b9b6..5eafae9 100644 --- a/include/elna/boot/semantic.h +++ b/include/elna/boot/semantic.h @@ -75,6 +75,7 @@ namespace elna::boot void visit(procedure_definition *definition) override; void visit(assign_statement *statement) override; void visit(if_statement *statement) override; + void visit(escape_statement *) override; void visit(while_statement *statement) override; void visit(return_statement *statement) override; void visit(defer_statement *statement) override; diff --git a/include/elna/gcc/elna-generic.h b/include/elna/gcc/elna-generic.h index 981b9b9..69e4b44 100644 --- a/include/elna/gcc/elna-generic.h +++ b/include/elna/gcc/elna-generic.h @@ -29,6 +29,7 @@ along with GCC; see the file COPYING3. If not see #include "tree-iterator.h" #include +#include namespace elna::gcc { @@ -45,6 +46,7 @@ namespace elna::gcc tree current_expression{ NULL_TREE }; std::shared_ptr symbols; std::unordered_map unresolved; + std::forward_list> loops; void declare_procedure(boot::procedure_definition *const definition); tree build_procedure_type(boot::procedure_type_expression& type); @@ -100,6 +102,7 @@ namespace elna::gcc void visit(boot::block *block) override; void visit(boot::assign_statement *statement) override; void visit(boot::if_statement *statement) override; + void visit(boot::escape_statement *statement) override; void visit(boot::while_statement *statement) override; void visit(boot::named_type_expression *type) override; void visit(boot::array_type_expression *type) override; diff --git a/source.elna b/source.elna index cc903db..ff62def 100644 --- a/source.elna +++ b/source.elna @@ -1,7 +1,7 @@ const - SEEK_SET* = 0; - SEEK_CUR* = 1; - SEEK_END* = 2; + SEEK_SET* := 0; + SEEK_CUR* := 1; + SEEK_END* := 2; type TokenKind* = ( @@ -404,9 +404,15 @@ begin end proc skip_spaces(source_code: ^SourceCode); +var + current: Char; begin - while ~source_code_empty(source_code) & is_space(source_code_head(source_code^)) do - if source_code_head(source_code^) = '\n' then + while ~source_code_empty(source_code), loop do + current := source_code_head(source_code^); + + if ~is_space(current) then + break loop + elsif current = '\n' then source_code_break(source_code) end; source_code_advance(source_code)