aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEugen Wissner <belka@caraus.de>2026-07-04 22:29:49 +0200
committerEugen Wissner <belka@caraus.de>2026-07-04 22:29:49 +0200
commit265e6d6a27eebbcafbd73f0da76e516a139f21d1 (patch)
treefa52c0c4fcf281011987d850102cf6bbfc7f98a2
parente068da3028c1ae30741d2d1ceaca691b71830bfe (diff)
downloadelna-265e6d6a27eebbcafbd73f0da76e516a139f21d1.tar.gz
Allow empty statements
-rw-r--r--boot/ast.cc15
-rw-r--r--boot/parser.yy28
-rw-r--r--boot/semantic.cc8
-rw-r--r--doc/appendix.tex46
-rw-r--r--doc/language.tex4
-rw-r--r--gcc/gcc/elna-generic.cc4
-rw-r--r--include/elna/boot/ast.h10
-rw-r--r--include/elna/boot/semantic.h4
-rw-r--r--include/elna/gcc/elna-generic.h1
-rw-r--r--testsuite/compilable/empty_statements_in_a_row.elna6
10 files changed, 83 insertions, 43 deletions
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 <elna::boot::procedure_call*> call_expression;
%type <elna::boot::return_statement *> return_statement;
%type <elna::boot::statement *> statement;
-%type <std::vector<elna::boot::statement *>> required_statements optional_statements;
+%type <std::vector<elna::boot::statement *>> statements;
%type <std::unique_ptr<std::vector<elna::boot::statement *>>> statement_part;
%type <elna::boot::procedure_declaration *> procedure_declaration;
%type <std::pair<std::vector<std::string>, 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::vector<boot::statement *>>(std::move($2));; }
+ | "begin" statements { $$ = std::make_unique<std::vector<boot::statement *>>(std::move($2));; }
| return_statement
{
$$ = std::make_unique<std::vector<boot::statement *>>(std::vector<boot::statement *>{ $1 });
}
- | "begin" required_statements ";" return_statement
+ | "begin" statements ";" return_statement
{
$$ = std::make_unique<std::vector<boot::statement *>>(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<boot::statement *>(std::move($2)); }
+ "else" statements { $$ = new std::vector<boot::statement *>(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 @@
<selector> = `[' <expression> `]' | `.\@' <identifier> | `^'.
-<case> = <expressions> `:\@' <optional-statements>.
+<case> = <expressions> `:\@' <statements>.
<designator> = <reference> <selector> | <identifier>.
@@ -95,9 +95,7 @@
<types> = <type> \{`,' <type>\}.
-<required-statements> = <statement> \{`;' <statement>\}.
-
-<optional-statements> = [<required-statements>].
+<statements> = <statement> \{`;' <statement>\}.
<return-declaration> = [`->' `!\@' | `->' type].
@@ -122,46 +120,46 @@
<assignment> = <designator> `:=' <expression>.
-<if-statement> = `if' <expression> `then' <optional-statements> \\
- \{`elsif' <expression> `then' <optional-statements>\} \\
- {[`else' <optional-statements>]} `end'.
+<if-statement> = `if' <expression> `then' <statements> \\
+ \{`elsif' <expression> `then' <statements>\} \\
+ {[`else' <statements>]} `end'.
-<while-statement> = `while' <expression> `do' <optional-statements> \\
- \{`elsif' <expression> `do' <optional-statements>\} `end'.
+<while-statement> = `while' <expression> `do' <statements> \\
+ \{`elsif' <expression> `do' <statements>\} `end'.
-<defer-statement> = `defer' <optional-statements> `end'.
+<defer-statement> = `defer' <statements> `end'.
<case-statement> = `case' <expression> `of' <case> \{`|' case\} \\
- {[`else' <optional-statements>]} `end'.
+ {[`else' <statements>]} `end'.
<label-declaration> = `.\@' <identifier>.
<goto-statement> = `goto' <identifier>.
<statement> = <assignment> | <procedure-call> | <defer-statement>
- | <label-declaration> | <goto-statement> |
- | <while-statement> | <if-statement> | <case-statement>.
+ | <label-declaration> | <goto-statement> | <while-statement>
+ | <if-statement> | <case-statement> | $\varepsilon{}$.
-<statement-part> = [`begin' <required-statements>
+<statement-part> = [`begin' <statements>
\alt{} `return' <expression>
- \alt{} `begin' <required-statements> `;' `return' <expression>].
+ \alt{} `begin' <statements> `;' `return' <expression>].
<constant-declaration> = <identifier-definition> `:=' <expression>.
-<constant-part> = [`const' \{<constant-declaration> `;'\}].
+<constant-part> = [`const' \{<constant-declaration>\}].
<variable-declaration> = <identifier-definitions> `:\@' <type> \\
{[`:=' (<expression> | `extern')]}.
-<variable-part> = [`var' \{<variable-declaration> `;'\}].
+<variable-part> = [`var' \{<variable-declaration>\}].
<type-declaration> = <identifier-definition> `=' <type>.
-<type-part> = [`type' \{<type-declaration> `;'\}].
+<type-part> = [`type' \{<type-declaration>\}].
<import-declaration> = <identifier> \{`.\@' <identifier>\}.
-<import-part> = [`import' \{import-declaration `;'\}].
+<import-part> = [`import' \{import-declaration\}].
<procedure-heading> = `proc' <identifier-definition> \\
`(' [<field> \{`,' <field>\}] `)' <return-declaration>.
@@ -170,10 +168,8 @@
<procedure-declaration> = <procedure-heading> `;' (block | `extern').
-<declaration-sequence> = <import-part> \\
- <constant-part> <type-part> <variable-part> \\
- \{<procedure-declaration> `;'\}.
-
-<program> = `program' `;' <declaration-sequence> <statement-part> `end' `.\@'
- \alt{} `module' `;' <declaration-sequence> `end' `.\@'.
+<program> = <import-part>
+ <constant-part> <type-part> <variable-part>
+ \{<procedure-declaration>\}
+ <statement-part> `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}
<statement> = <assignment> | <procedure-call> | <defer-statement>
- | <label-declaration> | <goto-statement> |
- | <while-statement> | <if-statement> | <case-statement>.
+ | <label-declaration> | <goto-statement> | <while-statement>
+ | <if-statement> | <case-statement> | $\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<typename T>
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.