From 265e6d6a27eebbcafbd73f0da76e516a139f21d1 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Sat, 4 Jul 2026 22:29:49 +0200 Subject: Allow empty statements --- boot/ast.cc | 15 +++++++ boot/parser.yy | 28 ++++++------- boot/semantic.cc | 8 ++++ doc/appendix.tex | 46 ++++++++++------------ doc/language.tex | 4 +- gcc/gcc/elna-generic.cc | 4 ++ include/elna/boot/ast.h | 10 +++++ include/elna/boot/semantic.h | 4 +- include/elna/gcc/elna-generic.h | 1 + .../compilable/empty_statements_in_a_row.elna | 6 +++ 10 files changed, 83 insertions(+), 43 deletions(-) create mode 100644 testsuite/compilable/empty_statements_in_a_row.elna diff --git a/boot/ast.cc b/boot/ast.cc index e4c46a4..91b46a4 100644 --- a/boot/ast.cc +++ b/boot/ast.cc @@ -114,6 +114,11 @@ namespace elna::boot not_implemented(); } + void empty_visitor::visit(empty_statement *) + { + not_implemented(); + } + void empty_visitor::visit(case_statement *) { not_implemented(); @@ -702,6 +707,16 @@ namespace elna::boot } } + void empty_statement::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + + empty_statement::empty_statement(const struct position position) + : node(position) + { + } + designator_expression::~designator_expression() { } diff --git a/boot/parser.yy b/boot/parser.yy index 1a5364b..4d4f038 100644 --- a/boot/parser.yy +++ b/boot/parser.yy @@ -140,7 +140,7 @@ along with GCC; see the file COPYING3. If not see %type call_expression; %type return_statement; %type statement; -%type > required_statements optional_statements; +%type > statements; %type >> statement_part; %type procedure_declaration; %type , elna::boot::procedure_type_expression *>> procedure_heading; @@ -193,12 +193,12 @@ block: constant_part variable_part statement_part "end" } statement_part: /* no statements */ {} - | "begin" required_statements { $$ = std::make_unique>(std::move($2));; } + | "begin" statements { $$ = std::make_unique>(std::move($2));; } | return_statement { $$ = std::make_unique>(std::vector{ $1 }); } - | "begin" required_statements ";" return_statement + | "begin" statements ";" return_statement { $$ = std::make_unique>(std::move($2)); $$->push_back($4); @@ -253,7 +253,7 @@ call_expression: designator_expression actual_parameter_list cast_expression: "cast" "(" expression ":" type_expression ")" { $$ = new boot::cast_expression(boot::make_position(@1), $5, $3); } elsif_do_statements: - "elsif" expression "do" optional_statements elsif_do_statements + "elsif" expression "do" statements elsif_do_statements { boot::conditional_statements *branch = new boot::conditional_statements($2, std::move($4)); std::swap($5, $$); @@ -261,10 +261,10 @@ elsif_do_statements: } | {} else_statements: - "else" optional_statements { $$ = new std::vector(std::move($2)); } + "else" statements { $$ = new std::vector(std::move($2)); } | { $$ = nullptr; } elsif_then_statements: - "elsif" expression "then" optional_statements elsif_then_statements + "elsif" expression "then" statements elsif_then_statements { boot::conditional_statements *branch = new boot::conditional_statements($2, std::move($4)); std::swap($5, $$); @@ -406,22 +406,23 @@ designator_expression: statement: designator_expression ":=" expression { $$ = new boot::assign_statement(boot::make_position(@1), $1, $3); } - | "while" expression "do" optional_statements elsif_do_statements "end" + | "while" expression "do" statements elsif_do_statements "end" { boot::conditional_statements *body = new boot::conditional_statements($2, std::move($4)); $$ = new boot::while_statement(boot::make_position(@1), body, std::move($5)); } - | "if" expression "then" optional_statements elsif_then_statements else_statements "end" + | "if" expression "then" statements elsif_then_statements else_statements "end" { boot::conditional_statements *then = new boot::conditional_statements($2, std::move($4)); $$ = new boot::if_statement(boot::make_position(@1), then, std::move($5), $6); } | call_expression { $$ = $1; } - | "defer" optional_statements "end" + | "defer" statements "end" { $$ = new boot::defer_statement(boot::make_position(@1), std::move($2)); } | "case" expression "of" switch_cases else_statements "end" { $$ = new boot::case_statement(boot::make_position(@1), $2, std::move($4), $5); } -switch_case: case_labels ":" optional_statements + | { $$ = new boot::empty_statement(boot::make_position(@$)); } +switch_case: case_labels ":" statements { $$ = { .labels = std::move($1), .statements = std::move($3) }; } switch_cases: switch_case "|" switch_cases @@ -437,16 +438,13 @@ case_labels: $$.emplace($$.cbegin(), $1); } | expression { $$.push_back($1); } -required_statements: - required_statements ";" statement +statements: + statements ";" statement { std::swap($$, $1); $$.insert($$.cend(), $3); } | statement { $$.push_back($1); } -optional_statements: - required_statements { std::swap($$, $1); } - | /* no statements */ {} field_declaration: IDENTIFIER ":" type_expression { $$ = std::make_pair($1, $3); } required_fields: diff --git a/boot/semantic.cc b/boot/semantic.cc index 3ccc81b..300c91d 100644 --- a/boot/semantic.cc +++ b/boot/semantic.cc @@ -139,6 +139,10 @@ namespace elna::boot { } + void type_analysis_visitor::visit(empty_statement *) + { + } + void type_analysis_visitor::visit(case_statement *) { } @@ -463,6 +467,10 @@ namespace elna::boot } } + void name_analysis_visitor::visit(empty_statement *) + { + } + void name_analysis_visitor::visit(case_statement *statement) { statement->condition().accept(this); diff --git a/doc/appendix.tex b/doc/appendix.tex index 09bdbde..d06a49b 100644 --- a/doc/appendix.tex +++ b/doc/appendix.tex @@ -64,7 +64,7 @@ = `[' `]' | `.\@' | `^'. - = `:\@' . + = `:\@' . = | . @@ -95,9 +95,7 @@ = \{`,' \}. - = \{`;' \}. - - = []. + = \{`;' \}. = [`->' `!\@' | `->' type]. @@ -122,46 +120,46 @@ = `:=' . - = `if' `then' \\ - \{`elsif' `then' \} \\ - {[`else' ]} `end'. + = `if' `then' \\ + \{`elsif' `then' \} \\ + {[`else' ]} `end'. - = `while' `do' \\ - \{`elsif' `do' \} `end'. + = `while' `do' \\ + \{`elsif' `do' \} `end'. - = `defer' `end'. + = `defer' `end'. = `case' `of' \{`|' case\} \\ - {[`else' ]} `end'. + {[`else' ]} `end'. = `.\@' . = `goto' . = | | - | | | - | | | . + | | | + | | | $\varepsilon{}$. - = [`begin' + = [`begin' \alt{} `return' - \alt{} `begin' `;' `return' ]. + \alt{} `begin' `;' `return' ]. = `:=' . - = [`const' \{ `;'\}]. + = [`const' \{\}]. = `:\@' \\ {[`:=' ( | `extern')]}. - = [`var' \{ `;'\}]. + = [`var' \{\}]. = `=' . - = [`type' \{ `;'\}]. + = [`type' \{\}]. = \{`.\@' \}. - = [`import' \{import-declaration `;'\}]. + = [`import' \{import-declaration\}]. = `proc' \\ `(' [ \{`,' \}] `)' . @@ -170,10 +168,8 @@ = `;' (block | `extern'). - = \\ - \\ - \{ `;'\}. - - = `program' `;' `end' `.\@' - \alt{} `module' `;' `end' `.\@'. + = + + \{\} + `end' `.\@'. \end{grammar} diff --git a/doc/language.tex b/doc/language.tex index 36ee877..26d8b48 100644 --- a/doc/language.tex +++ b/doc/language.tex @@ -506,8 +506,8 @@ relations $=$ and $<>$ apply to all types. \begin{grammar} = | | - | | | - | | | . + | | | + | | | $\varepsilon{}$. \end{grammar} Statements denote actions. There are elementary and structured statements. diff --git a/gcc/gcc/elna-generic.cc b/gcc/gcc/elna-generic.cc index 66bd9a2..2c16a37 100644 --- a/gcc/gcc/elna-generic.cc +++ b/gcc/gcc/elna-generic.cc @@ -1190,6 +1190,10 @@ namespace elna::gcc defer(leave_scope()); } + void generic_visitor::visit(boot::empty_statement *) + { + } + void generic_visitor::visit(boot::case_statement *statement) { statement->condition().accept(this); diff --git a/include/elna/boot/ast.h b/include/elna/boot/ast.h index 7d94e84..03d3e27 100644 --- a/include/elna/boot/ast.h +++ b/include/elna/boot/ast.h @@ -87,6 +87,7 @@ namespace elna::boot template class literal; class defer_statement; + class empty_statement; /** * Interface for AST visitors. @@ -107,6 +108,7 @@ namespace elna::boot virtual void visit(return_statement *) = 0; virtual void visit(defer_statement *) = 0; virtual void visit(case_statement *) = 0; + virtual void visit(empty_statement *) = 0; virtual void visit(unit *) = 0; virtual void visit(program *) = 0; virtual void visit(binary_expression *) = 0; @@ -158,6 +160,7 @@ namespace elna::boot [[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; [[noreturn]] virtual void visit(procedure_call *) override; [[noreturn]] virtual void visit(unit *) override; @@ -758,6 +761,13 @@ namespace elna::boot virtual ~defer_statement() override; }; + class empty_statement : public statement + { + public: + empty_statement(const struct position); + void accept(parser_visitor *visitor) override; + }; + class binary_expression : public expression { expression *m_lhs; diff --git a/include/elna/boot/semantic.h b/include/elna/boot/semantic.h index 66eb0a7..7094ee9 100644 --- a/include/elna/boot/semantic.h +++ b/include/elna/boot/semantic.h @@ -109,6 +109,7 @@ namespace elna::boot void visit(while_statement *) override; void visit(return_statement *) override; void visit(defer_statement *) override; + void visit(empty_statement *) override; void visit(case_statement *) override; void visit(procedure_call *) override; void visit(unit *unit) override; @@ -151,7 +152,8 @@ namespace elna::boot void visit(import_declaration *) override; void visit(while_statement *statement) override; void visit(return_statement *statement) override; - void visit(defer_statement *statement) override; + void visit(defer_statement *) override; + void visit(empty_statement *statement) override; void visit(case_statement *statement) override; void visit(procedure_call *call) override; void visit(unit *unit) override; diff --git a/include/elna/gcc/elna-generic.h b/include/elna/gcc/elna-generic.h index 7490e92..9e148e6 100644 --- a/include/elna/gcc/elna-generic.h +++ b/include/elna/gcc/elna-generic.h @@ -92,6 +92,7 @@ namespace elna::gcc 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/testsuite/compilable/empty_statements_in_a_row.elna b/testsuite/compilable/empty_statements_in_a_row.elna new file mode 100644 index 0000000..3cc283c --- /dev/null +++ b/testsuite/compilable/empty_statements_in_a_row.elna @@ -0,0 +1,6 @@ +proc f() +begin + ; +end + +end. -- cgit v1.2.3