From 09f204bd16142f6cb7e97362f848394b3f0a5914 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Sun, 2 Mar 2025 10:45:54 +0100 Subject: [PATCH] Revert "Allow only one return" This reverts commit 18602d00a118e69d4ccc45287e89a16b86c1d92e. --- boot/ast.cc | 62 +++++++++++++++++++++++++++++-- boot/lexer.ll | 3 ++ boot/parser.yy | 49 +++++++++++++------------ gcc/elna-generic.cc | 13 +++++-- gcc/elna-tree.cc | 35 +++++++++++++++++- include/elna/boot/ast.h | 34 +++++++++++++++-- include/elna/gcc/elna-generic.h | 65 +++++++++++++++++---------------- include/elna/gcc/elna-tree.h | 2 + include/elna/gcc/elna1.h | 9 +++++ source.elna | 38 ++++++++++++------- 10 files changed, 231 insertions(+), 79 deletions(-) diff --git a/boot/ast.cc b/boot/ast.cc index 14e4230..ac0b57e 100644 --- a/boot/ast.cc +++ b/boot/ast.cc @@ -86,6 +86,24 @@ namespace boot } } + void empty_visitor::visit(return_statement *statement) + { + expression *return_expression = statement->return_expression(); + + if (return_expression != nullptr) + { + return_expression->accept(this); + } + } + + void empty_visitor::visit(defer_statement *defer) + { + for (statement *const body_statement : defer->statements) + { + body_statement->accept(this); + } + } + void empty_visitor::visit(block *block) { for (constant_definition *const constant : block->constants) @@ -370,9 +388,8 @@ namespace boot } procedure_definition::procedure_definition(const struct position position, const std::string& identifier, - const bool exported, std::shared_ptr heading, - block *const body, expression *const returning) - : definition(position, identifier, exported), m_heading(heading), body(body), returning(returning) + const bool exported, std::shared_ptr heading, block *body) + : definition(position, identifier, exported), m_heading(heading), body(body) { } @@ -389,7 +406,6 @@ namespace boot procedure_definition::~procedure_definition() { delete body; - delete returning; } type_definition::type_definition(const struct position position, const std::string& identifier, @@ -460,6 +476,24 @@ namespace boot { } + defer_statement::defer_statement(const struct position position) + : node(position) + { + } + + void defer_statement::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + + defer_statement::~defer_statement() + { + for (statement *body_statement : statements) + { + delete body_statement; + } + } + designator_expression::designator_expression() { } @@ -711,6 +745,26 @@ namespace boot } } + return_statement::return_statement(const struct position position, expression *return_expression) + : node(position), m_return_expression(return_expression) + { + } + + void return_statement::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + + expression *return_statement::return_expression() + { + return m_return_expression; + } + + return_statement::~return_statement() + { + delete m_return_expression; + } + void assign_statement::accept(parser_visitor *visitor) { visitor->visit(this); diff --git a/boot/lexer.ll b/boot/lexer.ll index 0019f8d..bb7ca06 100644 --- a/boot/lexer.ll +++ b/boot/lexer.ll @@ -125,6 +125,9 @@ return { cast { return yy::parser::make_CAST(this->location); } +defer { + return yy::parser::make_DEFER(this->location); + } [A-Za-z_][A-Za-z0-9_]* { return yy::parser::make_IDENTIFIER(yytext, this->location); } diff --git a/boot/parser.yy b/boot/parser.yy index 8adc80b..b740b70 100644 --- a/boot/parser.yy +++ b/boot/parser.yy @@ -106,6 +106,7 @@ along with GCC; see the file COPYING3. If not see RETURN "return" BEGIN_BLOCK "begin" END_BLOCK "end" + DEFER "defer" %token OR "or" AND "and" XOR "xor" EQUALS "=" NOT_EQUAL "<>" LESS_THAN "<" GREATER_THAN ">" LESS_EQUAL "<=" GREATER_EQUAL ">=" SHIFT_LEFT "<<" SHIFT_RIGHT ">>" @@ -135,12 +136,12 @@ along with GCC; see the file COPYING3. If not see %type call_expression; %type while_statement; %type if_statement; -%type return_statement; +%type return_statement; %type statement; -%type > statements statement_part; +%type > statements; %type procedure_definition; %type > procedure_heading; -%type > procedure_definitions; +%type > procedure_definitions procedure_part; %type type_definition; %type > type_definitions type_part; %type block; @@ -148,11 +149,12 @@ along with GCC; see the file COPYING3. If not see %type >>> optional_fields required_fields; %type > elsif_then_statements elsif_do_statements; %type cast_expression; +%type defer_statement; %type > identifier_definition; %type >> identifier_definitions; %% program: - constant_part type_part variable_part procedure_definitions "begin" statements "end" "." + constant_part type_part variable_part procedure_part "begin" statements "end" "." { auto tree = new elna::boot::program(elna::boot::make_position(@5)); @@ -164,13 +166,13 @@ program: driver.tree.reset(tree); } -block: constant_part variable_part statement_part +block: constant_part variable_part "begin" statements "end" { - $$ = new elna::boot::block(elna::boot::make_position(@1)); + $$ = new elna::boot::block(elna::boot::make_position(@3)); std::swap($$->constants, $1); std::swap($$->variables, $2); - std::swap($$->body, $3); + std::swap($$->body, $4); } identifier_definition: IDENTIFIER "*" @@ -205,10 +207,9 @@ procedure_heading: std::swap($1, $$->parameters); } procedure_definition: - "proc" identifier_definition procedure_heading ";" block return_statement "end" + "proc" identifier_definition procedure_heading ";" block { - $$ = new elna::boot::procedure_definition(elna::boot::make_position(@1), $2.first, $2.second, - $3, $5, $6); + $$ = new elna::boot::procedure_definition(elna::boot::make_position(@1), $2.first, $2.second, $3, $5); } | "proc" identifier_definition procedure_heading ";" "extern" { @@ -220,7 +221,10 @@ procedure_definitions: std::swap($$, $2); $$.emplace($$.cbegin(), std::move($1)); } - | /* no procedure definitions */ {} + | procedure_definition { $$.emplace_back(std::move($1)); } +procedure_part: + /* no procedure definitions */ {} + | procedure_definitions { std::swap($$, $1); } assign_statement: designator_expression ":=" expression { $$ = new elna::boot::assign_statement(elna::boot::make_position(@1), $1, $3); @@ -275,12 +279,15 @@ if_statement: $$ = new elna::boot::if_statement(elna::boot::make_position(@1), then, _else); std::swap($5, $$->branches); } -return_statement: - "return" expression +return_statement: "return" expression { - $$ = $2; + $$ = new elna::boot::return_statement(elna::boot::make_position(@1), $2); + } +defer_statement: DEFER statements "end" + { + $$ = new elna::boot::defer_statement(elna::boot::make_position(@1)); + std::swap($2, $$->statements); } - | /* no return statement */ { $$ = nullptr; } literal: INTEGER { @@ -451,21 +458,17 @@ statement: assign_statement { $$ = $1; } | while_statement { $$ = $1; } | if_statement { $$ = $1; } + | return_statement { $$ = $1; } | call_expression { $$ = $1; } + | defer_statement { $$ = $1; } statements: statement ";" statements { std::swap($$, $3); - $$.emplace($$.cbegin(), $1); - } - | statement - { - $$.push_back($1); + $$.insert($$.cbegin(), $1); } + | statement { $$.push_back($1); } | /* no statements */ {} -statement_part: - "begin" statements { std::swap($$, $2); } - | {} field_declaration: IDENTIFIER ":" type_expression { $$ = std::make_pair($1, $3); } required_fields: diff --git a/gcc/elna-generic.cc b/gcc/elna-generic.cc index ad2d9d5..aa32df6 100644 --- a/gcc/elna-generic.cc +++ b/gcc/elna-generic.cc @@ -294,7 +294,6 @@ namespace gcc if (definition->body != nullptr) { definition->body->accept(this); - visit_return(definition->returning); tree mapping = leave_scope(); BLOCK_SUPERCONTEXT(BIND_EXPR_BLOCK(mapping)) = fndecl; @@ -335,7 +334,7 @@ namespace gcc { BLOCK_SUPERCONTEXT(it) = new_block; } - tree bind_expr = build3(BIND_EXPR, void_type_node, variables, f_binding_level->statement_list, new_block); + tree bind_expr = build3(BIND_EXPR, void_type_node, variables, chain_defer(), new_block); this->symbol_map = this->symbol_map->scope(); f_binding_level = f_binding_level->level_chain; @@ -1085,8 +1084,10 @@ namespace gcc } } - void generic_visitor::visit_return(boot::expression *const return_expression) + void generic_visitor::visit(boot::return_statement *statement) { + boot::expression *return_expression = statement->return_expression(); + if (return_expression == nullptr) { return; @@ -1206,5 +1207,11 @@ namespace gcc this->current_expression = build_pointer_type_for_mode(procedure_type_node, VOIDmode, true); } + void generic_visitor::visit(boot::defer_statement *statement) + { + enter_scope(); + visit_statements(statement->statements); + defer(leave_scope()); + } } } diff --git a/gcc/elna-tree.cc b/gcc/elna-tree.cc index 569e0b9..c8262df 100644 --- a/gcc/elna-tree.cc +++ b/gcc/elna-tree.cc @@ -91,7 +91,40 @@ namespace gcc void append_statement(tree statement_tree) { - append_to_statement_list(statement_tree, &f_binding_level->statement_list); + if (!vec_safe_is_empty(f_binding_level->defers)) + { + append_to_statement_list(statement_tree, &f_binding_level->defers->begin()->try_statements); + } + else + { + append_to_statement_list(statement_tree, &f_binding_level->statement_list); + } + } + + void defer(tree statement_tree) + { + defer_scope new_defer{ statement_tree, alloc_stmt_list() }; + vec_safe_insert(f_binding_level->defers, 0, new_defer); + } + + tree chain_defer() + { + if (vec_safe_is_empty(f_binding_level->defers)) + { + return f_binding_level->statement_list; + } + defer_scope *defer_iterator = f_binding_level->defers->begin(); + tree defer_tree = build2(TRY_FINALLY_EXPR, void_type_node, + defer_iterator->try_statements, defer_iterator->defer_block); + int i; + + FOR_EACH_VEC_ELT_FROM(*f_binding_level->defers, i, defer_iterator, 1) + { + append_to_statement_list(defer_tree, &defer_iterator->try_statements); + defer_tree = build2(TRY_FINALLY_EXPR, void_type_node, + defer_iterator->try_statements, defer_iterator->defer_block); + } + return build2(COMPOUND_EXPR, TREE_TYPE(defer_tree), f_binding_level->statement_list, defer_tree); } tree build_field(location_t location, tree record_type, const std::string name, tree type) diff --git a/include/elna/boot/ast.h b/include/elna/boot/ast.h index 3002cbe..9bc523f 100644 --- a/include/elna/boot/ast.h +++ b/include/elna/boot/ast.h @@ -63,6 +63,7 @@ namespace boot class assign_statement; class if_statement; class while_statement; + class return_statement; class traits_expression; class block; class program; @@ -80,6 +81,7 @@ namespace boot class dereference_expression; template class number_literal; + class defer_statement; /** * Interface for AST visitors. @@ -96,6 +98,8 @@ namespace boot virtual void visit(assign_statement *) = 0; virtual void visit(if_statement *) = 0; virtual void visit(while_statement *) = 0; + virtual void visit(return_statement *) = 0; + virtual void visit(defer_statement *) = 0; virtual void visit(block *) = 0; virtual void visit(program *) = 0; virtual void visit(binary_expression *) = 0; @@ -134,6 +138,8 @@ namespace boot virtual void visit(assign_statement *statement) override; virtual void visit(if_statement *) override; virtual void visit(while_statement *) override; + virtual void visit(return_statement *) override; + virtual void visit(defer_statement *defer) override; virtual void visit(block *block) override; virtual void visit(program *program) override; virtual void visit(binary_expression *expression) override; @@ -360,11 +366,9 @@ namespace boot public: block *const body; - expression *const returning; procedure_definition(const struct position position, const std::string& identifier, - const bool exported, std::shared_ptr heading, - block *const body = nullptr, expression *const returning = nullptr); + const bool exported, std::shared_ptr heading, block *body = nullptr); virtual void accept(parser_visitor *visitor) override; procedure_type& heading(); @@ -435,6 +439,19 @@ namespace boot virtual ~conditional_statements(); }; + class return_statement : public statement + { + expression *m_return_expression{ nullptr }; + + public: + return_statement(const struct position position, expression *return_expression); + virtual void accept(parser_visitor *visitor) override; + + expression *return_expression(); + + virtual ~return_statement() override; + }; + class designator_expression : public expression { public: @@ -627,6 +644,17 @@ namespace boot } }; + class defer_statement : public statement + { + public: + std::vector statements; + + defer_statement(const struct position position); + virtual void accept(parser_visitor *visitor) override; + + virtual ~defer_statement() override; + }; + class binary_expression : public expression { expression *m_lhs; diff --git a/include/elna/gcc/elna-generic.h b/include/elna/gcc/elna-generic.h index 668b759..d3ddde9 100644 --- a/include/elna/gcc/elna-generic.h +++ b/include/elna/gcc/elna-generic.h @@ -60,42 +60,43 @@ namespace gcc void build_record_call(location_t call_location, tree symbol, const std::vector& arguments); void visit_statements(const std::vector& statements); - void visit_return(boot::expression *const return_expression); public: generic_visitor(std::shared_ptr symbol_table); - virtual void visit(boot::program *program) override; - virtual void visit(boot::procedure_definition *definition) override; - virtual void visit(boot::procedure_call *call) override; - virtual void visit(boot::cast_expression *expression) override; - virtual void visit(boot::traits_expression *trait) override; - virtual void visit(boot::number_literal *literal) override; - virtual void visit(boot::number_literal *literal) override; - virtual void visit(boot::number_literal *literal) override; - virtual void visit(boot::number_literal *boolean) override; - virtual void visit(boot::number_literal *character) override; - virtual void visit(boot::number_literal *) override; - virtual void visit(boot::number_literal *string) override; - virtual void visit(boot::binary_expression *expression) override; - virtual void visit(boot::unary_expression *expression) override; - virtual void visit(boot::constant_definition *definition) override; - virtual void visit(boot::type_definition *definition) override; - virtual void visit(boot::variable_declaration *declaration) override; - virtual void visit(boot::variable_expression *expression) override; - virtual void visit(boot::array_access_expression *expression) override; - virtual void visit(boot::field_access_expression *expression) override; - virtual void visit(boot::dereference_expression *expression) override; - virtual void visit(boot::block *block) override; - virtual void visit(boot::assign_statement *statement) override; - virtual void visit(boot::if_statement *statement) override; - virtual void visit(boot::while_statement *statement) override; - virtual void visit(boot::basic_type *type) override; - virtual void visit(boot::array_type *type) override; - virtual void visit(boot::pointer_type *type) override; - virtual void visit(boot::record_type *type) override; - virtual void visit(boot::union_type *type) override; - virtual void visit(boot::procedure_type *type) override; + void visit(boot::program *program) override; + void visit(boot::procedure_definition *definition) override; + void visit(boot::procedure_call *call) override; + void visit(boot::cast_expression *expression) override; + void visit(boot::traits_expression *trait) override; + void visit(boot::number_literal *literal) override; + void visit(boot::number_literal *literal) override; + void visit(boot::number_literal *literal) override; + void visit(boot::number_literal *boolean) override; + void visit(boot::number_literal *character) override; + void visit(boot::number_literal *) override; + void visit(boot::number_literal *string) override; + void visit(boot::binary_expression *expression) override; + void visit(boot::unary_expression *expression) override; + void visit(boot::constant_definition *definition) override; + void visit(boot::type_definition *definition) override; + void visit(boot::variable_declaration *declaration) override; + void visit(boot::variable_expression *expression) override; + void visit(boot::array_access_expression *expression) override; + void visit(boot::field_access_expression *expression) override; + void visit(boot::dereference_expression *expression) override; + void visit(boot::block *block) override; + void visit(boot::assign_statement *statement) override; + void visit(boot::if_statement *statement) override; + void visit(boot::while_statement *statement) override; + void visit(boot::basic_type *type) override; + void visit(boot::array_type *type) override; + void visit(boot::pointer_type *type) override; + void visit(boot::record_type *type) override; + void visit(boot::union_type *type) override; + void visit(boot::procedure_type *type) override; + void visit(boot::return_statement *statement) override; + void visit(boot::defer_statement *statement) override; }; } } diff --git a/include/elna/gcc/elna-tree.h b/include/elna/gcc/elna-tree.h index d1ffd30..1f83b05 100644 --- a/include/elna/gcc/elna-tree.h +++ b/include/elna/gcc/elna-tree.h @@ -72,6 +72,8 @@ namespace gcc bool is_assignable_from(tree assignee, tree assignment); void append_statement(tree statement_tree); + void defer(tree statement_tree); + tree chain_defer(); tree do_pointer_arithmetic(boot::binary_operator binary_operator, tree left, tree right); tree build_binary_operation(bool condition, boot::binary_expression *expression, diff --git a/include/elna/gcc/elna1.h b/include/elna/gcc/elna1.h index d65440c..418c832 100644 --- a/include/elna/gcc/elna1.h +++ b/include/elna/gcc/elna1.h @@ -58,6 +58,12 @@ struct GTY (()) lang_decl { }; +struct GTY (()) defer_scope +{ + tree defer_block; + tree try_statements; +}; + struct GTY ((chain_next ("%h.level_chain"))) binding_level { // A block chain is needed to call defer statements beloning to each block. @@ -68,6 +74,9 @@ struct GTY ((chain_next ("%h.level_chain"))) binding_level // Statements before the first defer has been seen. tree statement_list; + + // Defer statement coupled with statements following it. + vec *defers; }; struct GTY (()) language_function diff --git a/source.elna b/source.elna index 5ba30ee..aae4bc6 100644 --- a/source.elna +++ b/source.elna @@ -140,6 +140,7 @@ proc exit(code: Int) -> !; extern Standard procedures. *) proc reallocarray(ptr: ^Byte, n: Word, size: Word) -> ^Byte; +begin return realloc(ptr, n * size) end @@ -197,26 +198,32 @@ begin end proc is_digit(c: Char) -> Bool; +begin return cast(c: Int) >= cast('0': Int) and cast(c: Int) <= cast('9': Int) end proc is_alpha(c: Char) -> Bool; +begin return cast(c: Int) >= cast('A': Int) and cast(c: Int) <= cast('z': Int) end proc is_alnum(c: Char) -> Bool; +begin return is_digit(c) or is_alpha(c) end proc is_space(c: Char) -> Bool; +begin return c = ' ' or c = '\n' or c = '\t' end proc substring(string: String, start: Word, count: Word) -> String; +begin return String(string.ptr + start, count) end proc open_substring(string: String, start: Word) -> String; +begin return substring(string, start, string.length - start) end @@ -270,6 +277,7 @@ end *) proc make_position() -> Position; +begin return Position(1u, 1u) end @@ -285,7 +293,7 @@ begin result^.handle := file_handle; result^.size := 0u; result^.index := 1u - end + end; return result end @@ -344,7 +352,7 @@ begin if source_file^.index > source_file^.size then source_file^.size := fread(cast(@source_file^.buffer: ^Byte), 1u, 1024u, source_file^.handle); source_file^.index := 1u - end + end; return source_file^.size = 0u end @@ -368,10 +376,12 @@ begin end proc source_code_empty(source_code: ^SourceCode) -> Bool; +begin return source_code^.empty(source_code^.input) end proc source_code_head(source_code: SourceCode) -> Char; +begin return source_code.head(source_code.input) end @@ -388,6 +398,7 @@ begin end proc source_code_expect(source_code: ^SourceCode, expected: Char) -> Bool; +begin return not source_code_empty(source_code) and source_code_head(source_code^) = expected end @@ -402,6 +413,7 @@ begin end proc is_ident(char: Char) -> Bool; +begin return is_alnum(char) or char = '_' end @@ -433,7 +445,7 @@ begin trailing := 0u end; source_code_advance(source_code) - end + end; return trailing = 2u end @@ -456,7 +468,7 @@ begin end; if successful then source_code_advance(source_code) - end + end; return successful end @@ -481,7 +493,7 @@ begin source_code_advance(source_code) else is_valid := false - end + end; return is_valid end @@ -710,7 +722,7 @@ begin else current_token.kind := TOKEN_IDENTIFIER; current_token.value.string := string_dup(token_content) - end + end; return current_token end @@ -887,7 +899,7 @@ begin write_c(first_char); write_s("\".\n") end - end + end; return tokens end @@ -904,7 +916,7 @@ begin result^.syntax_tree := false; result^.input := nil; - while i < argc and result <> nil do + while i < argc do parameter := argv + i; if strcmp(parameter^, "--tokenize\0".ptr) = 0 then @@ -920,15 +932,15 @@ begin write_z(parameter^); write_s(".\n"); - result := nil + return nil end; i := i + 1 end; - if result <> nil and result^.input = nil then + if result^.input = nil then write_s("Fatal error: no input files.\n"); - result := nil - end + return nil + end; return result end @@ -969,7 +981,7 @@ begin if command_line^.tokenize then print_tokens(tokens, tokens_size) end - end + end; return return_code end