From 500c0676b3f6cd5a2297987d5b0dc7ccf34a28d9 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Mon, 13 Jul 2026 18:14:45 +0200 Subject: Make return statement part of the body and not a statement --- boot/ast.cc | 128 ++++--------- boot/parser.yy | 62 ++---- boot/semantic.cc | 79 +++++--- cl.elna | 213 --------------------- gcc/gcc/elna-generic.cc | 56 +++--- include/elna/boot/ast.h | 62 +++--- include/elna/boot/semantic.h | 5 +- include/elna/gcc/elna-generic.h | 3 - source/lexer.elna | 205 ++++++++++++++++++++ source/main.elna | 1 + testsuite/compilable/pointer_cast.elna | 4 +- .../fail_compilation/module_without_return.elna | 6 + 12 files changed, 372 insertions(+), 452 deletions(-) create mode 100644 testsuite/fail_compilation/module_without_return.elna diff --git a/boot/ast.cc b/boot/ast.cc index fbecf11..118d92c 100644 --- a/boot/ast.cc +++ b/boot/ast.cc @@ -84,11 +84,6 @@ namespace elna::boot __builtin_unreachable(); } - void empty_visitor::visit(return_statement *) - { - __builtin_unreachable(); - } - void empty_visitor::visit(defer_statement *) { __builtin_unreachable(); @@ -205,18 +200,22 @@ namespace elna::boot } if (declaration->body.has_value()) { - for (constant_declaration *const constant : declaration->body.value().constants()) + for (constant_declaration *const constant : declaration->body.value().constants) { constant->accept(this); } - for (variable_declaration *const variable : declaration->body.value().variables()) + for (variable_declaration *const variable : declaration->body.value().variables) { variable->accept(this); } - for (statement *const statement : declaration->body.value().statements()) + for (statement *const statement : declaration->body.value().entry_point) { statement->accept(this); } + if (declaration->body.value().return_expression != nullptr) + { + declaration->body.value().return_expression->accept(this); + } } } @@ -269,11 +268,6 @@ namespace elna::boot } } - void walking_visitor::visit(return_statement *statement) - { - statement->return_expression().accept(this); - } - void walking_visitor::visit(defer_statement *statement) { for (struct statement *const block_statement : statement->statements) @@ -340,12 +334,13 @@ namespace elna::boot { procedure->accept(this); } - if (unit->entry_point.has_value()) + for (statement *const entry_statement : unit->entry_point) { - for (statement *const entry_statement : unit->entry_point.value()) - { - entry_statement->accept(this); - } + entry_statement->accept(this); + } + if (unit->return_expression != nullptr) + { + unit->return_expression->accept(this); } } @@ -773,70 +768,54 @@ namespace elna::boot } procedure_body::procedure_body(std::vector&& constants, - std::vector&& variables, std::vector&& statements) - : m_variables(std::move(variables)), m_constants(std::move(constants)), m_statements(std::move(statements)) - { - } - - procedure_body::procedure_body(std::vector&& constants, - std::vector&& variables) - : procedure_body(std::move(constants), std::move(variables), std::vector{}) + std::vector&& variables, std::vector&& entry_point, + expression *return_expr) + : constants(std::move(constants)), variables(std::move(variables)), + entry_point(std::move(entry_point)), return_expression(return_expr) { } procedure_body::procedure_body(procedure_body&& that) - : m_variables(std::move(that.m_variables)), m_constants(std::move(that.m_constants)), - m_statements(std::move(that.m_statements)) - { - } - - procedure_body& procedure_body::operator=(procedure_body&& that) - { - std::swap(m_variables, that.m_variables); - std::swap(m_constants, that.m_constants); - std::swap(m_statements, that.m_statements); - - return *this; - } - - const std::vector& procedure_body::variables() - { - return m_variables; - } - - const std::vector& procedure_body::constants() - { - return m_constants; - } - - const std::vector& procedure_body::statements() + : constants(std::move(const_cast&>(that.constants))), + variables(std::move(const_cast&>(that.variables))), + entry_point(std::move(const_cast&>(that.entry_point))), + return_expression(that.return_expression) { - return m_statements; } procedure_body::~procedure_body() { - for (statement *body_statement : this->statements()) + for (statement *body_statement : this->entry_point) { delete body_statement; } - for (variable_declaration *variable : this->variables()) + for (variable_declaration *variable : this->variables) { delete variable; } - for (constant_declaration *constant : this->constants()) + for (constant_declaration *constant : this->constants) { delete constant; } } unit::unit(const struct position position) - : node(position) + : node(position), procedure_body(std::vector{}, + std::vector{}, std::vector{}, nullptr) { } - unit::unit(const struct position position, std::vector&& entry_point) - : node(position), entry_point(std::make_optional>(std::move(entry_point))) + unit::unit(const struct position position, + std::vector&& imports, + std::vector&& constants, + std::vector&& types, + std::vector&& variables, + std::vector&& procedures, + std::vector&& entry_point, + expression *const return_expression) + : node(position), + procedure_body(std::move(constants), std::move(variables), std::move(entry_point), return_expression), + imports(std::move(imports)), types(std::move(types)), procedures(std::move(procedures)) { } @@ -845,24 +824,21 @@ namespace elna::boot visitor->visit(this); } + bool unit::has_body() const + { + return !this->entry_point.empty() || this->return_expression != nullptr; + } + unit::~unit() { for (procedure_declaration *procedure : this->procedures) { delete procedure; } - for (variable_declaration *variable : this->variables) - { - delete variable; - } for (type_declaration *type : this->types) { delete type; } - for (constant_declaration *constant : this->constants) - { - delete constant; - } for (import_declaration *declaration : this->imports) { delete declaration; @@ -1206,26 +1182,6 @@ namespace elna::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; - } - case_statement::case_statement(const struct position position, expression *condition, std::vector&& cases, std::vector *alternative) : node(position), m_condition(condition), cases(std::move(cases)), alternative(alternative) diff --git a/boot/parser.yy b/boot/parser.yy index aace9a1..f1bf915 100644 --- a/boot/parser.yy +++ b/boot/parser.yy @@ -131,16 +131,14 @@ along with GCC; see the file COPYING3. If not see %type type_expression; %type > type_expressions; %type traits_expression; -%type expression operand simple_expression; +%type expression operand simple_expression return_statement; %type unary_expression; %type binary_expression; %type > expressions actual_parameter_list; %type designator_expression; %type call_expression; -%type return_statement; %type statement; -%type > statements; -%type >> statement_part; +%type > statements statement_part; %type procedure_declaration; %type procedure_heading; %type return_declaration; @@ -159,49 +157,22 @@ along with GCC; see the file COPYING3. If not see %type > import_declarations import_part; %% program: - import_part constant_part type_part variable_part procedure_part statement_part "end" "." - { - boot::unit *tree; - if ($6) - { - tree = new boot::unit(boot::make_position(@1), - std::move(*$6.release())); - } - else - { - tree = new boot::unit(boot::make_position(@1)); - } - driver.tree.reset(tree); - - std::swap(driver.tree->imports, $1); - std::swap(driver.tree->constants, $2); - std::swap(driver.tree->types , $3); - std::swap(driver.tree->variables, $4); - std::swap(driver.tree->procedures, $5); - } -procedure_body: constant_part variable_part statement_part "end" - { - if ($3) - { - $$ = std::make_unique(std::move($1), std::move($2), std::move(*$3.release())); - } - else - { - $$ = std::make_unique(std::move($1), std::move($2)); - } - } -statement_part: - /* no statements */ {} - | "begin" statements { $$ = std::make_unique>(std::move($2));; } - | return_statement + import_part constant_part type_part variable_part procedure_part statement_part return_statement "end" "." { - $$ = std::make_unique>(std::vector{ $1 }); + boot::unit *tree = new boot::unit(boot::make_position(@1), + std::move($1), std::move($2), std::move($3), std::move($4), std::move($5), + std::move($6), $7); + driver.tree.reset(tree); } - | "begin" statements ";" return_statement +procedure_body: + constant_part variable_part statement_part return_statement "end" { - $$ = std::make_unique>(std::move($2)); - $$->push_back($4); + $$ = std::make_unique(std::move($1), std::move($2), std::move($3), $4); } + +statement_part: + /* no statements */ {} + | "begin" statements { std::swap($$, $2); } identifier_definition: IDENTIFIER "*" { $$ = boot::identifier_definition{ $1, true }; } | IDENTIFIER { $$ = boot::identifier_definition{ $1, false }; } @@ -263,8 +234,9 @@ elsif_then_statements: $$.emplace($$.begin(), branch); } | /* no branches */ {} -return_statement: "return" expression - { $$ = new boot::return_statement(boot::make_position(@1), $2); } +return_statement: + "return" expression { $$ = $2; } + | /* no return statement */ { $$ = nullptr; } literal: INTEGER { $$ = new boot::literal(boot::make_position(@1), $1); } | WORD { $$ = new boot::literal(boot::make_position(@1), $1); } diff --git a/boot/semantic.cc b/boot/semantic.cc index 3486830..97c316a 100644 --- a/boot/semantic.cc +++ b/boot/semantic.cc @@ -139,7 +139,6 @@ namespace elna::boot void type_analysis_visitor::visit(procedure_declaration *declaration) { this->current_procedure = this->bag.lookup(declaration->identifier.name)->is_procedure(); - this->returns = false; if (declaration->body.has_value()) { @@ -149,41 +148,53 @@ namespace elna::boot if (declaration->body.has_value()) { - this->bag.leave(); - if (!this->returns && declaration->heading().return_type.proper_type != nullptr) + if (declaration->body.value().return_expression != nullptr) + { + expression *return_expr = declaration->body.value().return_expression; + type return_type = this->current_procedure->symbol.return_type.proper_type; + + if (!return_type.empty()) + { + if (!is_assignable_from(return_type, return_expr->type_decoration)) + { + add_error(type_expectation_error::kind::result, return_expr->position()); + } + } + else + { + add_error(type_expectation_error::kind::result, return_expr->position()); + } + } + else if (declaration->heading().return_type.proper_type != nullptr) { add_error(declaration->identifier.name, declaration->position()); } + this->bag.leave(); } this->current_procedure.reset(); } - void type_analysis_visitor::visit(return_statement *statement) + void type_analysis_visitor::visit(unit *unit) { - walking_visitor::visit(statement); - type return_type; + walking_visitor::visit(unit); - if (this->current_procedure == nullptr) + if (unit->has_body()) { - // In the main function. - return_type = this->bag.lookup("Int")->is_type()->symbol; - } - else - { - return_type = this->current_procedure->symbol.return_type.proper_type; - } - if (!return_type.empty()) - { - if (!is_assignable_from(return_type, statement->return_expression().type_decoration)) + if (unit->return_expression != nullptr) { - add_error(type_expectation_error::kind::result, statement->position()); + type return_type = this->bag.lookup("Int")->is_type()->symbol; + + if (!is_assignable_from(return_type, unit->return_expression->type_decoration)) + { + add_error(type_expectation_error::kind::result, + unit->return_expression->position()); + } + } + else + { + add_error("module", unit->position()); } } - else - { - add_error(type_expectation_error::kind::result, statement->position()); - } - this->returns = true; } void type_analysis_visitor::visit(assign_statement *statement) @@ -641,18 +652,22 @@ namespace elna::boot ++name_iterator; ++type_iterator; } - for (constant_declaration *const constant : declaration->body.value().constants()) + for (constant_declaration *const constant : declaration->body.value().constants) { constant->accept(this); } - for (variable_declaration *const variable : declaration->body.value().variables()) + for (variable_declaration *const variable : declaration->body.value().variables) { variable->accept(this); } - for (statement *const statement : declaration->body.value().statements()) + for (statement *const statement : declaration->body.value().entry_point) { statement->accept(this); } + if (declaration->body.value().return_expression != nullptr) + { + declaration->body.value().return_expression->accept(this); + } this->bag.leave(); } else @@ -698,7 +713,7 @@ namespace elna::boot { procedure->accept(this); } - if (unit->entry_point.has_value()) + if (unit->has_body()) { this->bag.enter(); auto variable_type = lookup_primitive_type("Int"); @@ -709,10 +724,14 @@ namespace elna::boot variable_type = type(std::make_shared(variable_type)); this->bag.enter("parameters", std::make_shared(variable_type, false)); - for (statement *const statement : unit->entry_point.value()) + for (statement *const statement : unit->entry_point) { statement->accept(this); } + if (unit->return_expression != nullptr) + { + unit->return_expression->accept(this); + } this->bag.leave(); } } @@ -942,11 +961,11 @@ namespace elna::boot { return; } - for (constant_declaration *const constant : declaration->body.value().constants()) + for (constant_declaration *const constant : declaration->body.value().constants) { constant->accept(this); } - for (variable_declaration *const variable : declaration->body.value().variables()) + for (variable_declaration *const variable : declaration->body.value().variables) { variable->accept(this); } diff --git a/cl.elna b/cl.elna index f716676..fb076ab 100644 --- a/cl.elna +++ b/cl.elna @@ -4728,23 +4728,6 @@ begin return first_procedure end -(** - * Skips comments. - *) -proc elna_lexer_skip_empty_lines(cursor: ^ElnaLexerCursor) -var - token: ^ElnaLexerToken -begin - .skip_empty_lines_rerun; - - token := elna_lexer_peek(cursor); - - if token^.kind = ElnaLexerKind.comment then - elna_lexer_read(cursor); - goto skip_empty_lines_rerun - end -end - proc elna_parser_type_declaration(cursor: ^ElnaLexerCursor; error_list: ^ElnaList): ^ElnaTreeTypeDeclaration var result: ^ElnaTreeTypeDeclaration @@ -6163,202 +6146,6 @@ begin elna_symbol_table_enter(symbol_table_global, "String".ptr, 6, current_info) end -(** - * One time lexer initialization. - *) -proc elna_lexer_initialize(cursor: ^ElnaLexerCursor; code_pointer: Word) -begin - elna_lexer_classifications(); - elna_lexer_transitions(); - - cursor^.start := code_pointer; - cursor^.finish := code_pointer; - cursor^.token := nil; - cursor^.position.start_location.line := 1; - cursor^.position.start_location.column := 1; - cursor^.position.end_location.line := 1; - cursor^.position.end_location.column := 1 -end - -proc elna_lexer_classify_finalize(start_position: ^Char; position: ^ElnaPosition): ^ElnaLexerToken -var - character: Char - result: ^ElnaLexerToken -begin - character := start_position^; - - if character = ':' then - result := elna_lexer_token_create(ElnaLexerKind.colon, position) - elsif character = '.' then - result := elna_lexer_token_create(ElnaLexerKind.dot, position) - elsif character = '(' then - result := elna_lexer_token_create(ElnaLexerKind.left_paren, position) - elsif character = '-' then - result := elna_lexer_token_create(ElnaLexerKind.minus, position) - elsif character = '<' then - result := elna_lexer_token_create(ElnaLexerKind.less_than, position) - elsif character = '>' then - result := elna_lexer_token_create(ElnaLexerKind.greater_than, position) - end; - return result -end - -proc elna_lexer_classify_single(start_position: ^Char; position: ^ElnaPosition): ^ElnaLexerToken -var - character: Char - result: ^ElnaLexerToken -begin - result := malloc(#size(ElnaLexerToken)); - character := start_position^; - - if character = ';' then - result := elna_lexer_token_create(ElnaLexerKind.semicolon, position) - elsif character = ',' then - result := elna_lexer_token_create(ElnaLexerKind.comma, position) - elsif character = ')' then - result := elna_lexer_token_create(ElnaLexerKind.right_paren, position) - elsif character = '@' then - result := elna_lexer_token_create(ElnaLexerKind.at, position) - elsif character = '~' then - result := elna_lexer_token_create(ElnaLexerKind.not, position) - elsif character = '&' then - result := elna_lexer_token_create(ElnaLexerKind.and, position) - elsif character = '+' then - result := elna_lexer_token_create(ElnaLexerKind.plus, position) - elsif character = '*' then - result := elna_lexer_token_create(ElnaLexerKind.multiplication, position) - elsif character = '=' then - result := elna_lexer_token_create(ElnaLexerKind.equals, position) - elsif character = '%' then - result := elna_lexer_token_create(ElnaLexerKind.remainder, position) - elsif character = '/' then - result := elna_lexer_token_create(ElnaLexerKind.division, position) - elsif character = '.' then - result := elna_lexer_token_create(ElnaLexerKind.dot, position) - elsif character = '^' then - result := elna_lexer_token_create(ElnaLexerKind.hat, position) - elsif character = '[' then - result := elna_lexer_token_create(ElnaLexerKind.left_square, position) - elsif character = ']' then - result := elna_lexer_token_create(ElnaLexerKind.right_square, position) - end; - return result -end - -proc elna_lexer_classify_composite(start_position, one_before_last: ^Char; position: ^ElnaPosition): ^ElnaLexerToken -var - first_character: Char - last_character: Char - result: ^ElnaLexerToken -begin - first_character := start_position^; - last_character := one_before_last^; - - if first_character = ':' then - result := elna_lexer_token_create(ElnaLexerKind.assignment, position) - elsif first_character = '<' then - if last_character = '=' then - result := elna_lexer_token_create(ElnaLexerKind.less_equal, position) - elsif last_character = '>' then - result := elna_lexer_token_create(ElnaLexerKind.not_equal, position) - end - elsif first_character = '>' then - if last_character = '=' then - result := elna_lexer_token_create(ElnaLexerKind.greater_equal, position) - end - elsif first_character = '-' then - result := elna_lexer_token_create(ElnaLexerKind.arrow, position) - end; - return result -end - -proc elna_lexer_execute_action(cursor: ^ElnaLexerCursor; action_to_perform: Word): ^ElnaLexerToken -var - token: ^ElnaLexerToken -begin - token := nil; - - if action_to_perform = ElnaLexerAction.none then - elsif action_to_perform = ElnaLexerAction.accumulate then - elna_lexer_advance(cursor) - elsif action_to_perform = ElnaLexerAction.skip then - elna_lexer_classify_space(cursor^.start, @cursor^.position.end_location); - cursor^.start := cursor^.start + 1; - cursor^.finish := cursor^.finish + 1 - elsif action_to_perform = ElnaLexerAction.single then - elna_lexer_advance(cursor); - - token := elna_lexer_classify_single(cursor^.start, @cursor^.position) - elsif action_to_perform = ElnaLexerAction.eof then - token := malloc(#size(ElnaLexerToken)); - token^.kind := ElnaLexerKind.eof - elsif action_to_perform = ElnaLexerAction.finalize then - token := elna_lexer_classify_finalize(cursor^.start, @cursor^.position) - elsif action_to_perform = ElnaLexerAction.composite then - token := elna_lexer_classify_composite(cursor^.start, cursor^.finish, @cursor^.position); - - elna_lexer_advance(cursor) - elsif action_to_perform = ElnaLexerAction.key_id then - token := elna_lexer_classify_keyword(cursor^.start, cursor^.finish, @cursor^.position) - elsif action_to_perform = ElnaLexerAction.integer then - token := elna_lexer_classify_integer(cursor^.start, cursor^.finish, @cursor^.position) - elsif action_to_perform = ElnaLexerAction.delimited then - elna_lexer_advance(cursor); - - token := elna_lexer_classify_delimited(cursor^.start, cursor^.finish, @cursor^.position) - end; - return token -end - -proc elna_lexer_execute_transition(cursor: ^ElnaLexerCursor; kind: ^ElnaLexerKind): ^ElnaLexerToken -var - next_transition: ^ElnaLexerTransition - current_character: Char -begin - current_character := cursor^.finish^; - next_transition := elna_lexer_get_transition(cursor^.state, classification[current_character + 1]); - cursor^.state := next_transition^.next_state; - - return elna_lexer_execute_action(cursor, next_transition^.action, kind) -end - -(** - * Reads the next token and writes its type into the address in the kind parameter. - * Resets the lexer state for reading the next token. - *) -proc elna_lexer_peek(cursor: ^ElnaLexerCursor): ^ElnaLexerToken -var - token: ^ElnaLexerToken -begin - if cursor^.token = nil then - cursor^.state := ElnaLexerState.start; - - .elna_lexer_peek_loop; - token := elna_lexer_execute_transition(cursor); - - if cursor^.state <> ElnaLexerState.finish then - goto elna_lexer_peek_loop - end; - cursor^.token := token - end; - return cursor^.token -end - -(** - * Reads the token and advance the lexer. - *) -proc elna_lexer_read(cursor: ^ElnaLexerCursor): ^ElnaLexerToken -var - token: ^ElnaLexerToken -begin - token := elna_lexer_peek(cursor); - cursor^.token := nil; - cursor^.start := cursor^.finish; - cursor^.position.start_location := cursor^.position.end_location; - - return token -end - proc initialize_global_state() begin stdin := fdopen(0, "r\0".ptr); diff --git a/gcc/gcc/elna-generic.cc b/gcc/gcc/elna-generic.cc index 497bcc6..30dd68b 100644 --- a/gcc/gcc/elna-generic.cc +++ b/gcc/gcc/elna-generic.cc @@ -196,7 +196,7 @@ namespace elna::gcc { procedure->accept(this); } - if (unit->entry_point.has_value()) + if (unit->has_body()) { tree declaration_type = build_function_type_list(elna_int_type_node, elna_int_type_node, @@ -225,11 +225,13 @@ namespace elna::gcc DECL_ARGUMENTS(fndecl) = chainon(DECL_ARGUMENTS(fndecl), declaration_tree); parameter_type = TREE_CHAIN(parameter_type); } - visit_statements(unit->entry_point.value()); + visit_statements(unit->entry_point); + unit->return_expression->accept(this); tree set_result = build2(INIT_EXPR, void_type_node, DECL_RESULT(fndecl), - build_int_cst_type(integer_type_node, 0)); + this->current_expression); tree return_stmt = build1(RETURN_EXPR, void_type_node, set_result); append_statement(return_stmt); + this->current_expression = NULL_TREE; tree mapping = leave_scope(); BLOCK_SUPERCONTEXT(BIND_EXPR_BLOCK(mapping)) = fndecl; @@ -264,15 +266,33 @@ namespace elna::gcc { this->symbols->enter(IDENTIFIER_POINTER(DECL_NAME(argument_chain)), argument_chain); } - for (boot::constant_declaration *const constant : declaration->body.value().constants()) + for (boot::constant_declaration *const constant : declaration->body.value().constants) { constant->accept(this); } - for (boot::variable_declaration *const variable : declaration->body.value().variables()) + for (boot::variable_declaration *const variable : declaration->body.value().variables) { variable->accept(this); } - visit_statements(declaration->body.value().statements()); + visit_statements(declaration->body.value().entry_point); + + if (declaration->body.value().return_expression != nullptr) + { + location_t position = get_location(&declaration->body.value().return_expression->position()); + tree set_result{ NULL_TREE }; + + if (TREE_THIS_VOLATILE(current_function_decl) != 1) + { + declaration->body.value().return_expression->accept(this); + + set_result = build2(INIT_EXPR, void_type_node, DECL_RESULT(current_function_decl), + this->current_expression); + } + tree return_stmt = build1_loc(position, RETURN_EXPR, void_type_node, set_result); + append_statement(return_stmt); + + this->current_expression = NULL_TREE; + } tree mapping = leave_scope(); this->bag.leave(); @@ -1066,30 +1086,6 @@ namespace elna::gcc } } - void generic_visitor::visit(boot::return_statement *statement) - { - boot::expression *return_expression = &statement->return_expression(); - location_t statement_position = get_location(&statement->position()); - tree set_result{ NULL_TREE }; - - if (TREE_THIS_VOLATILE(current_function_decl) == 1) - { - error_at(statement_position, "This procedure is not allowed to return"); - return; - } - if (return_expression != nullptr) - { - return_expression->accept(this); - - set_result = build2(INIT_EXPR, void_type_node, DECL_RESULT(current_function_decl), - this->current_expression); - } - tree return_stmt = build1_loc(statement_position, RETURN_EXPR, void_type_node, set_result); - append_statement(return_stmt); - - this->current_expression = NULL_TREE; - } - void generic_visitor::visit(boot::defer_statement *statement) { enter_scope(); diff --git a/include/elna/boot/ast.h b/include/elna/boot/ast.h index d9f3af8..ab79c30 100644 --- a/include/elna/boot/ast.h +++ b/include/elna/boot/ast.h @@ -64,7 +64,6 @@ namespace elna::boot class if_statement; class import_declaration; class while_statement; - class return_statement; class case_statement; class traits_expression; class unit; @@ -103,7 +102,6 @@ namespace elna::boot virtual void visit(if_statement *) = 0; virtual void visit(import_declaration *) = 0; virtual void visit(while_statement *) = 0; - virtual void visit(return_statement *) = 0; virtual void visit(defer_statement *) = 0; virtual void visit(case_statement *) = 0; virtual void visit(empty_statement *) = 0; @@ -148,7 +146,6 @@ namespace elna::boot [[noreturn]] virtual void visit(if_statement *) override; [[noreturn]] virtual void visit(import_declaration *) override; [[noreturn]] virtual void visit(while_statement *) override; - [[noreturn]] virtual void visit(return_statement *) override; [[noreturn]] virtual void visit(defer_statement *) override; [[noreturn]] virtual void visit(empty_statement *) override; [[noreturn]] virtual void visit(case_statement *) override; @@ -191,7 +188,6 @@ namespace elna::boot virtual void visit(if_statement *) override; virtual void visit(import_declaration *) override; virtual void visit(while_statement *statement) override; - virtual void visit(return_statement *statement) override; virtual void visit(defer_statement *statement) override; virtual void visit(empty_statement *) override; virtual void visit(case_statement *statement) override; @@ -416,26 +412,21 @@ namespace elna::boot struct procedure_body { - procedure_body(std::vector&& constants, - std::vector&& variables, std::vector&& statements); - procedure_body(std::vector&& constants, std::vector&& variables); + const std::vector constants; + const std::vector variables; + const std::vector entry_point; + expression *const return_expression{ nullptr }; + + procedure_body(std::vector&& constants, + std::vector&& variables, + std::vector&& entry_point, + expression *return_expr = nullptr); procedure_body(const procedure_body&) = delete; procedure_body(procedure_body&& that); procedure_body& operator=(const procedure_body&) = delete; - procedure_body& operator=(procedure_body&& that); - - const std::vector& variables(); - const std::vector& constants(); - const std::vector& statements(); virtual ~procedure_body(); - - private: - std::vector m_variables; - std::vector m_constants; - std::vector m_statements; - }; /** @@ -526,19 +517,6 @@ namespace elna::boot virtual ~conditional_statements(); }; - class return_statement : public statement - { - public: - expression *m_return_expression; - - return_statement(const struct position position, expression *return_expression); - void accept(parser_visitor *visitor) override; - - expression& return_expression(); - - virtual ~return_statement() override; - }; - struct switch_case { std::vector labels; @@ -728,19 +706,23 @@ namespace elna::boot virtual ~while_statement() override; }; - class unit : public node + class unit : public node, public procedure_body { public: - const std::optional> entry_point; - - std::vector imports; - std::vector constants; - std::vector types; - std::vector variables; - std::vector procedures; + const std::vector imports; + const std::vector types; + const std::vector procedures; unit(const struct position position); - unit(const struct position position, std::vector&& entry_point); + unit(const struct position position, + std::vector&& imports, + std::vector&& constants, + std::vector&& types, + std::vector&& variables, + std::vector&& procedures, + std::vector&& entry_point, + expression *const return_expression = nullptr); + bool has_body() const; virtual void accept(parser_visitor *visitor) override; virtual ~unit() override; diff --git a/include/elna/boot/semantic.h b/include/elna/boot/semantic.h index b94d269..1e81ebb 100644 --- a/include/elna/boot/semantic.h +++ b/include/elna/boot/semantic.h @@ -18,10 +18,8 @@ along with GCC; see the file COPYING3. If not see #pragma once #include -#include #include #include -#include #include "elna/boot/ast.h" #include "elna/boot/result.h" @@ -147,7 +145,6 @@ namespace elna::boot */ class type_analysis_visitor final : public walking_visitor, public error_container { - bool returns; symbol_bag bag; std::shared_ptr current_procedure; @@ -161,7 +158,7 @@ namespace elna::boot explicit type_analysis_visitor(symbol_bag bag); void visit(procedure_declaration *declaration) override; - void visit(return_statement *statement) override; + void visit(unit *unit) override; void visit(assign_statement *statement) override; void visit(variable_declaration *declaration) override; void visit(type_declaration *declaration) override; diff --git a/include/elna/gcc/elna-generic.h b/include/elna/gcc/elna-generic.h index 28bfe2d..2fe4d48 100644 --- a/include/elna/gcc/elna-generic.h +++ b/include/elna/gcc/elna-generic.h @@ -18,8 +18,6 @@ along with GCC; see the file COPYING3. If not see #pragma once #include -#include - #include "elna/boot/ast.h" #include "elna/boot/symbol.h" #include "elna/boot/semantic.h" @@ -89,7 +87,6 @@ namespace elna::gcc void visit(boot::if_statement *statement) override; void visit(boot::import_declaration *) override; void visit(boot::while_statement *statement) override; - void visit(boot::return_statement *statement) override; void visit(boot::defer_statement *statement) override; void visit(boot::empty_statement *) override; void visit(boot::case_statement *statement) override; diff --git a/source/lexer.elna b/source/lexer.elna index 5029ec5..62ad3b1 100644 --- a/source/lexer.elna +++ b/source/lexer.elna @@ -622,4 +622,209 @@ begin return result end +proc elna_lexer_classify_finalize(start_position: ^Char; position: ^ElnaPosition): ^ElnaLexerToken +var + character: Char + result: ^ElnaLexerToken +begin + character := start_position^; + + if character = ':' then + result := elna_lexer_token_create(ElnaLexerKind.colon, position) + elsif character = '.' then + result := elna_lexer_token_create(ElnaLexerKind.dot, position) + elsif character = '(' then + result := elna_lexer_token_create(ElnaLexerKind.left_paren, position) + elsif character = '-' then + result := elna_lexer_token_create(ElnaLexerKind.minus, position) + elsif character = '<' then + result := elna_lexer_token_create(ElnaLexerKind.less_than, position) + elsif character = '>' then + result := elna_lexer_token_create(ElnaLexerKind.greater_than, position) + end; + return result +end + +proc elna_lexer_classify_single(start_position: ^Char; position: ^ElnaPosition): ^ElnaLexerToken +var + character: Char + result: ^ElnaLexerToken +begin + result := malloc(#size(ElnaLexerToken)); + character := start_position^; + + if character = ';' then + result := elna_lexer_token_create(ElnaLexerKind.semicolon, position) + elsif character = ',' then + result := elna_lexer_token_create(ElnaLexerKind.comma, position) + elsif character = ')' then + result := elna_lexer_token_create(ElnaLexerKind.right_paren, position) + elsif character = '@' then + result := elna_lexer_token_create(ElnaLexerKind.at, position) + elsif character = '~' then + result := elna_lexer_token_create(ElnaLexerKind.not, position) + elsif character = '&' then + result := elna_lexer_token_create(ElnaLexerKind.and, position) + elsif character = '+' then + result := elna_lexer_token_create(ElnaLexerKind.plus, position) + elsif character = '*' then + result := elna_lexer_token_create(ElnaLexerKind.multiplication, position) + elsif character = '=' then + result := elna_lexer_token_create(ElnaLexerKind.equals, position) + elsif character = '%' then + result := elna_lexer_token_create(ElnaLexerKind.remainder, position) + elsif character = '/' then + result := elna_lexer_token_create(ElnaLexerKind.division, position) + elsif character = '.' then + result := elna_lexer_token_create(ElnaLexerKind.dot, position) + elsif character = '^' then + result := elna_lexer_token_create(ElnaLexerKind.hat, position) + elsif character = '[' then + result := elna_lexer_token_create(ElnaLexerKind.left_square, position) + elsif character = ']' then + result := elna_lexer_token_create(ElnaLexerKind.right_square, position) + end; + return result +end + +proc elna_lexer_classify_composite(start_position, one_before_last: ^Char; position: ^ElnaPosition): ^ElnaLexerToken +var + first_character: Char + last_character: Char + result: ^ElnaLexerToken +begin + first_character := start_position^; + last_character := one_before_last^; + + if first_character = ':' then + result := elna_lexer_token_create(ElnaLexerKind.assignment, position) + elsif first_character = '<' then + if last_character = '=' then + result := elna_lexer_token_create(ElnaLexerKind.less_equal, position) + elsif last_character = '>' then + result := elna_lexer_token_create(ElnaLexerKind.not_equal, position) + end + elsif first_character = '>' then + if last_character = '=' then + result := elna_lexer_token_create(ElnaLexerKind.greater_equal, position) + end + end; + return result +end + +proc elna_lexer_execute_action(cursor: ^ElnaLexerCursor; action_to_perform: ElnaLexerAction): ^ElnaLexerToken +var + token: ^ElnaLexerToken +begin + token := nil; + + if action_to_perform = ElnaLexerAction.none then + elsif action_to_perform = ElnaLexerAction.accumulate then + elna_lexer_advance(cursor) + elsif action_to_perform = ElnaLexerAction.skip then + elna_lexer_classify_space(cursor^.start, @cursor^.position.end_location); + cursor^.start := cursor^.start + 1; + cursor^.finish := cursor^.finish + 1 + elsif action_to_perform = ElnaLexerAction.single then + elna_lexer_advance(cursor); + + token := elna_lexer_classify_single(cursor^.start, @cursor^.position) + elsif action_to_perform = ElnaLexerAction.eof then + token := malloc(#size(ElnaLexerToken)); + token^.kind := ElnaLexerKind.eof + elsif action_to_perform = ElnaLexerAction.finalize then + token := elna_lexer_classify_finalize(cursor^.start, @cursor^.position) + elsif action_to_perform = ElnaLexerAction.composite then + token := elna_lexer_classify_composite(cursor^.start, cursor^.finish, @cursor^.position); + + elna_lexer_advance(cursor) + elsif action_to_perform = ElnaLexerAction.key_id then + token := elna_lexer_classify_keyword(cursor^.start, cursor^.finish, @cursor^.position) + elsif action_to_perform = ElnaLexerAction.integer then + token := elna_lexer_classify_integer(cursor^.start, cursor^.finish, @cursor^.position) + elsif action_to_perform = ElnaLexerAction.delimited then + elna_lexer_advance(cursor); + + token := elna_lexer_classify_delimited(cursor^.start, cursor^.finish, @cursor^.position) + end; + return token +end + +proc elna_lexer_execute_transition(cursor: ^ElnaLexerCursor): ^ElnaLexerToken +var + next_transition: ^ElnaLexerTransition + current_character: Char +begin + current_character := cursor^.finish^; + next_transition := elna_lexer_get_transition(cursor^.state, + classification[cast(current_character: Word) + 1u]); + cursor^.state := next_transition^.next_state; + + return elna_lexer_execute_action(cursor, next_transition^.action) +end + +(** + * One time lexer initialization. + *) +proc elna_lexer_initialize(cursor: ^ElnaLexerCursor; code_pointer: ^Char) +begin + elna_lexer_classifications(); + elna_lexer_transitions(); + + cursor^.start := code_pointer; + cursor^.finish := code_pointer; + cursor^.token := nil; + cursor^.position.start_location := ElnaLocation(1u, 1u); + cursor^.position.end_location := ElnaLocation(1u, 1u); +end + +(** + * Reads the next token and writes its type into the address in the kind parameter. + * Resets the lexer state for reading the next token. + *) +proc elna_lexer_peek(cursor: ^ElnaLexerCursor): ^ElnaLexerToken +var + token: ^ElnaLexerToken +begin + if cursor^.token = nil then + cursor^.state := ElnaLexerState.start; + + while cursor^.state <> ElnaLexerState.finish do + token := elna_lexer_execute_transition(cursor) + end; + cursor^.token := token + end; + return cursor^.token +end + +(** + * Reads the token and advance the lexer. + *) +proc elna_lexer_read(cursor: ^ElnaLexerCursor): ^ElnaLexerToken +var + token: ^ElnaLexerToken +begin + token := elna_lexer_peek(cursor); + cursor^.token := nil; + cursor^.start := cursor^.finish; + cursor^.position.start_location := cursor^.position.end_location; + + return token +end + +(** + * Skips comments. + *) +proc elna_lexer_skip_empty_lines(cursor: ^ElnaLexerCursor) +var + token: ^ElnaLexerToken +begin + token := elna_lexer_peek(cursor); + + while token^.kind = ElnaLexerKind.comment do + elna_lexer_read(cursor); + token := elna_lexer_peek(cursor) + end +end + end. diff --git a/source/main.elna b/source/main.elna index 37d0f07..b7ce29c 100644 --- a/source/main.elna +++ b/source/main.elna @@ -851,5 +851,6 @@ end begin initialize_global_state(); + return process(count, parameters) end. diff --git a/testsuite/compilable/pointer_cast.elna b/testsuite/compilable/pointer_cast.elna index d10fb23..64b99ca 100644 --- a/testsuite/compilable/pointer_cast.elna +++ b/testsuite/compilable/pointer_cast.elna @@ -4,5 +4,7 @@ var begin p := c; - c := p + c := p; + + return 0 end. diff --git a/testsuite/fail_compilation/module_without_return.elna b/testsuite/fail_compilation/module_without_return.elna new file mode 100644 index 0000000..f2ece07 --- /dev/null +++ b/testsuite/fail_compilation/module_without_return.elna @@ -0,0 +1,6 @@ +var (* @Error Procedure 'module' is expected to return, but does not have a return statement *) + x: Int + +begin + x := 42 +end. -- cgit v1.2.3