diff options
| author | Eugen Wissner <belka@caraus.de> | 2026-07-08 11:04:34 +0200 |
|---|---|---|
| committer | Eugen Wissner <belka@caraus.de> | 2026-07-08 11:04:34 +0200 |
| commit | ffe6097c534c82b9402bb6f0a57abeebd075e61d (patch) | |
| tree | 9079255db89007046fe1446d27009df8585a154e | |
| parent | 79521d91b2bb85652fd29f5c44279338d55c757f (diff) | |
| download | elna-ffe6097c534c82b9402bb6f0a57abeebd075e61d.tar.gz | |
Allow multiple fields with the same type
| -rw-r--r-- | boot/ast.cc | 19 | ||||
| -rw-r--r-- | boot/parser.yy | 31 | ||||
| -rw-r--r-- | boot/semantic.cc | 19 | ||||
| -rw-r--r-- | doc/appendix.tex | 20 | ||||
| -rw-r--r-- | doc/language.tex | 52 | ||||
| -rw-r--r-- | doc/type-system.tex | 31 | ||||
| -rw-r--r-- | include/elna/boot/ast.h | 13 | ||||
| -rw-r--r-- | testsuite/runnable/two_fields_same_type.elna | 16 |
8 files changed, 86 insertions, 115 deletions
diff --git a/boot/ast.cc b/boot/ast.cc index b7604e5..5885cff 100644 --- a/boot/ast.cc +++ b/boot/ast.cc @@ -388,14 +388,6 @@ namespace elna::boot { } - record_type_expression::~record_type_expression() - { - for (const field_declaration& field : this->fields) - { - delete field.second; - } - } - void record_type_expression::accept(parser_visitor *visitor) { visitor->visit(this); @@ -412,14 +404,6 @@ namespace elna::boot { } - union_type_expression::~union_type_expression() - { - for (const field_declaration& field : this->fields) - { - delete field.second; - } - } - void union_type_expression::accept(parser_visitor *visitor) { visitor->visit(this); @@ -486,8 +470,7 @@ namespace elna::boot } procedure_type_expression::procedure_type_expression(const struct position position, - std::vector<std::pair<std::vector<std::string>, std::shared_ptr<type_expression>>>&& parameters, - return_t return_type) + std::vector<field_declaration>&& parameters, return_t return_type) : node(position), return_type(return_type), parameters(std::move(parameters)) { } diff --git a/boot/parser.yy b/boot/parser.yy index 3b8e69e..b05b544 100644 --- a/boot/parser.yy +++ b/boot/parser.yy @@ -150,11 +150,7 @@ along with GCC; see the file COPYING3. If not see %type <std::vector<elna::boot::type_declaration *>> type_declarations type_part; %type <std::unique_ptr<elna::boot::block>> block; %type <elna::boot::field_declaration> field_declaration; -%type <std::pair<std::vector<std::string>, std::shared_ptr<elna::boot::type_expression>>> formal_parameter; -%type <std::vector<std::pair<std::string, elna::boot::type_expression *>>> - optional_fields required_fields; -%type <std::vector<std::pair<std::vector<std::string>, std::shared_ptr<elna::boot::type_expression>>>> - formal_parameters formal_parameter_list; +%type <std::vector<elna::boot::field_declaration>> optional_fields required_fields; %type <std::vector<elna::boot::conditional_statements *>> elsif_then_statements elsif_do_statements; %type <std::vector<elna::boot::statement *> *> else_statements; %type <elna::boot::cast_expression *> cast_expression; @@ -220,9 +216,9 @@ return_declaration: /* proper procedure */ {} | ":" "!" { $$ = boot::procedure_type_expression::return_t(std::monostate{}); } | ":" type_expression { $$ = boot::procedure_type_expression::return_t($2); } -procedure_heading: formal_parameter_list return_declaration +procedure_heading: "(" optional_fields ")" return_declaration { - $$ = new boot::procedure_type_expression(boot::make_position(@1), std::move($1), std::move($2)); + $$ = new boot::procedure_type_expression(boot::make_position(@1), std::move($2), std::move($4)); } procedure_declaration: "proc" identifier_definition procedure_heading block @@ -442,7 +438,7 @@ statements: } | statement { $$.push_back($1); } field_declaration: - IDENTIFIER ":" type_expression { $$ = std::make_pair($1, $3); } + identifiers ":" type_expression { $$ = std::make_pair(std::move($1), std::shared_ptr<boot::type_expression>($3)); } required_fields: field_declaration ";" required_fields { @@ -470,9 +466,9 @@ type_expression: { $$ = new boot::union_type_expression(boot::make_position(@1), std::move($2)); } - | "proc" formal_parameter_list return_declaration + | "proc" procedure_heading { - $$ = new boot::procedure_type_expression(boot::make_position(@1), std::move($2), std::move($3)); + $$ = $2; } | "(" identifiers ")" { @@ -564,21 +560,6 @@ type_declarations: type_part: /* no type definitions */ {} | "type" type_declarations { std::swap($$, $2); } -formal_parameter: - identifiers ":" type_expression { $$ = std::make_pair(std::move($1), std::shared_ptr<boot::type_expression>($3)); } -formal_parameter_list: - "(" ")" {} - | "(" formal_parameters ")" { std::swap($$, $2); } -formal_parameters: - formal_parameter ";" formal_parameters - { - std::swap($$, $3); - $$.emplace($$.cbegin(), std::move($1.first), $1.second); - } - | formal_parameter - { - $$.emplace_back(std::move($1.first), $1.second); - } actual_parameter_list: "(" ")" {} | "(" expressions ")" { std::swap($$, $2); } diff --git a/boot/semantic.cc b/boot/semantic.cc index cd50742..bc7c2d2 100644 --- a/boot/semantic.cc +++ b/boot/semantic.cc @@ -272,15 +272,18 @@ namespace elna::boot for (auto& field : fields) { - if (field_names.find(field.first) != field_names.cend()) + field.second->accept(this); + for (auto& field_name : field.first) { - add_error<field_duplication_error>(field.first, field.second->position()); - } - else - { - field_names.insert(field.first); - field.second->accept(this); - result.push_back(std::make_pair(field.first, this->current_type)); + if (field_names.find(field_name) != field_names.cend()) + { + add_error<field_duplication_error>(field_name, field.second->position()); + } + else + { + field_names.insert(field_name); + result.push_back(std::make_pair(field_name, this->current_type)); + } } } return result; diff --git a/doc/appendix.tex b/doc/appendix.tex index d06a49b..e98da0c 100644 --- a/doc/appendix.tex +++ b/doc/appendix.tex @@ -91,15 +91,17 @@ <expressions> = <expression> \{`,' <expression>\}. +<identifiers> = <identifier> \{`,' <identifier>\}. + <identifier-definitions> = <identifier-definition> \{`,' <identifier-definition>\}. <types> = <type> \{`,' <type>\}. <statements> = <statement> \{`;' <statement>\}. -<return-declaration> = [`->' `!\@' | `->' type]. +<return-declaration> = [`:\@' `!\@' | `:\@' type]. -<field> = <identifier> `:\@' <type>. +<field> = <identifiers> `:\@' <type>. <array-type> = `[' <expression> `]' <type>. @@ -107,9 +109,9 @@ <record-type> = `record' [`(' <identifier> `)'] [<field> \{`;' <field>\}] `end'. -<enumeration-type> = `(' <identifier> \{`,' <identifier>\} `)'. +<enumeration-type> = `(' <identifiers> `)'. -<procedure-type> = `proc' `(' [<types>] `)' <return-declaration>. +<procedure-type> = `proc' <procedure-heading>. <type> = <array-type> \alt{} <pointer-type> @@ -161,12 +163,14 @@ <import-part> = [`import' \{import-declaration\}]. -<procedure-heading> = `proc' <identifier-definition> \\ - `(' [<field> \{`,' <field>\}] `)' <return-declaration>. +<parameter> = <identifiers> `:\@' <type>. + +<procedure-heading> = `(' [<parameter> \{`;' <parameter>\}] `)' <return-declaration>. -<block> = <constant-part> <variable-part> <statement-part> `end'. +<procedure-body> = <constant-part> <variable-part> <statement-part> `end'. -<procedure-declaration> = <procedure-heading> `;' (block | `extern'). +<procedure-declaration> = `proc' <identifier-definition> <procedure-heading> \\ + (<procedure-body> | `extern'). <program> = <import-part> <constant-part> <type-part> <variable-part> diff --git a/doc/language.tex b/doc/language.tex index 26d8b48..04f8613 100644 --- a/doc/language.tex +++ b/doc/language.tex @@ -1,17 +1,5 @@ \part{Language} -An Elna program consists of one or more source files, called \textbf{modules}. - -Each module can declare \textbf{types}, \textbf{global variables} and -\textbf{procedures}, used by this module or exported to be used by other -modules. - -Each procedure can get some input and produce an output as a result of -executing a \textbf{statement block}, a list, where each \textbf{statement} -is executed in the order it appears in the block. - -\chapter{Vocabulary} - A language is an infinite set of sentences, namely the sentences well formed according to its syntax. In Elna, these sentences are called compilation units. Each unit is a finite sequence of \textit{tokens} from a finite vocabulary. @@ -19,6 +7,20 @@ The vocabulary of Elna consists of identifiers, reserved words, numbers, charact strings, operators, delimiters, and comments. They are called \textit{tokens} and are composed of sequences of characters. +To describe the syntax, an extended Backus-Naur Formalism called EBNF is used. + +\begin{itemize} + \item Alternatives are separated by |. + \item Brackets \lbrack{} and \rbrack{} denote optionality of the enclosed sentential form. + \item Braces \{ and \} denote its repetition (possibly 0 times). + \item Syntactic entities (non-terminal symbols) are denoted by English words expressing their + intuitive meaning. + \item Symbols of the language vocabulary (terminal symbols) are denoted by strings + enclosed in quote marks or by words in capital letters. +\end{itemize} + +\chapter{Vocabulary} + The following lexical rules must be observed when composing tokens. Blanks and line breaks must not occur within tokens (except in comments and strings). They are ignored unless they are essential to separate two consecutive tokens. @@ -153,29 +155,9 @@ of letters and cannot be used in the role of identifiers. \item (\ and\ ) \item \lbrack{} and \rbrack{} \item \{ and \} - \item Pointer - \item module - \item import - \item type - \item const - \item var - \item begin - \item end - \item proc - \item record - \item while - \item do - \item case - \item of - \item if - \item then - \item elsif - \item else - \item cast - \item return - \item true - \item false - \item nil + \item import, type, const, var, begin, end, proc, record + \item while, do, case, of, if, then, elsif, else, cast, return + \item true, false, nil \end{itemize} \section{Comments} diff --git a/doc/type-system.tex b/doc/type-system.tex index 3ae8a30..e1497be 100644 --- a/doc/type-system.tex +++ b/doc/type-system.tex @@ -1,5 +1,11 @@ \part{Type system} +An Elna program consists of one or more source files, called \textbf{modules}. + +Each module can declare \textbf{types}, \textbf{global variables} and +\textbf{procedures}, used by this module or exported to be used by other +modules. + \begin{grammar} <type> = <array-type> \alt{} <pointer-type> @@ -27,7 +33,6 @@ \end{grammar} \begin{lstlisting}[caption=Example] -program; var x: Int; y: ^Int; @@ -44,7 +49,6 @@ end. \end{grammar} \begin{lstlisting}[caption=Example] -program; var array: [3]Int := [1, 2, 3]; begin @@ -55,20 +59,23 @@ end. \chapter{Procedure types} \begin{grammar} -<procedure-heading> = `proc' <identifier-definition> \\ - `(' [<field> \{`,' <field>\}] `)' <return-declaration>. +<identifiers> = <identifier> \{`,' <identifier>\}. + +<parameter> = <identifiers> `:\@' <type>. + +<procedure-heading> = `(' [<parameter> \{`;' <parameter>\}] `)' <return-declaration>. -<block> = <constant-part> <variable-part> <statement-part> `end'. +<procedure-body> = <constant-part> <variable-part> <statement-part> `end'. -<procedure-declaration> = <procedure-heading> `;' (block | `extern'). +<procedure-declaration> = `proc' <identifier-definition> <procedure-heading> \\ + (<procedure-body> | `extern'). -<return-declaration> = [`->' `!\@' | `->' type]. +<return-declaration> = [`:\@' `!\@' | `:\@' type]. -<procedure-type> = `proc' `(' [<types>] `)' <return-declaration>. +<procedure-type> = `proc' <procedure-heading>. \end{grammar} \begin{lstlisting}[caption=Example] -program; var a: proc(Int) -> Int; @@ -84,13 +91,12 @@ end. \chapter{Records} \begin{grammar} -<field> = <identifier> `:\@' <type>. +<field> = <identifiers> `:\@' <type>. <record-type> = `record' [`(' <identifier> `)'] [<field> \{`;' <field>\}] `end'. \end{grammar} \begin{lstlisting}[caption=Example] -program; type T = record x: Int @@ -111,11 +117,10 @@ end. \chapter{Enumerations} \begin{grammar} -<enumeration-type> = `(' <identifier> \{`,' <identifier>\} `)'. +<enumeration-type> = `(' <identifiers> `)'. \end{grammar} \begin{lstlisting}[caption=Example] -program; type E = (one, two, three); var diff --git a/include/elna/boot/ast.h b/include/elna/boot/ast.h index b9414f0..0642d44 100644 --- a/include/elna/boot/ast.h +++ b/include/elna/boot/ast.h @@ -259,7 +259,7 @@ namespace elna::boot array_type_expression(const struct position position, type_expression *base, const std::uint32_t size); - ~array_type_expression(); + ~array_type_expression() override; void accept(parser_visitor *visitor) override; array_type_expression *is_array() override; @@ -273,7 +273,7 @@ namespace elna::boot public: pointer_type_expression(const struct position position, type_expression *base); - ~pointer_type_expression(); + ~pointer_type_expression() override; void accept(parser_visitor *visitor) override; pointer_type_expression *is_pointer() override; @@ -281,7 +281,7 @@ namespace elna::boot type_expression& base(); }; - using field_declaration = std::pair<std::string, type_expression *>; + using field_declaration = std::pair<std::vector<std::string>, std::shared_ptr<type_expression>>; class record_type_expression : public type_expression { @@ -289,7 +289,6 @@ namespace elna::boot const std::vector<field_declaration> fields; record_type_expression(const struct position position, std::vector<field_declaration>&& fields); - ~record_type_expression(); void accept(parser_visitor *visitor) override; record_type_expression *is_record() override; @@ -301,7 +300,6 @@ namespace elna::boot std::vector<field_declaration> fields; union_type_expression(const struct position position, std::vector<field_declaration>&& fields); - ~union_type_expression(); void accept(parser_visitor *visitor) override; union_type_expression *is_union() override; @@ -381,11 +379,10 @@ namespace elna::boot using return_t = return_declaration<type_expression *>; const return_t return_type; - const std::vector<std::pair<std::vector<std::string>, std::shared_ptr<type_expression>>> parameters; + const std::vector<field_declaration> parameters; procedure_type_expression(const struct position position, - std::vector<std::pair<std::vector<std::string>, std::shared_ptr<type_expression>>>&& parameters, - return_t return_type = return_t()); + std::vector<field_declaration>&& parameters, return_t return_type = return_t()); ~procedure_type_expression(); void accept(parser_visitor *visitor) override; diff --git a/testsuite/runnable/two_fields_same_type.elna b/testsuite/runnable/two_fields_same_type.elna new file mode 100644 index 0000000..b881dda --- /dev/null +++ b/testsuite/runnable/two_fields_same_type.elna @@ -0,0 +1,16 @@ +type + R = record + x, y: Int + end + +proc f(): Int +var + r: R := R(3, 2) + + return r.x + r.y +end + +begin + assert(f() = 5); + return 0 +end. |
