/* Syntax analyzer. Copyright (C) 2025 Free Software Foundation, Inc. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING3. If not see . */ %require "3.4" %language "c++" %code { using namespace elna; } %code requires { #include #include #include "elna/boot/driver.h" #if !defined(yyFlexLexerOnce) #include #endif namespace elna::boot { class lexer; } } %code provides { namespace elna::boot { class lexer: public yyFlexLexer { public: yy::location location; /// Where the comment being scanned was opened. yy::location comment_start; lexer(std::istream& arg_yyin) : yyFlexLexer(&arg_yyin) { } yy::parser::symbol_type lex(driver& driver); }; } } %define api.token.raw %define api.token.constructor %define api.value.type variant %define api.value.automove %define parse.error detailed %define parse.lac full %expect 0 // Assert zero shift/reduce conflicts. %parse-param {elna::boot::lexer& lexer} %param {elna::boot::driver& driver} %locations %header %code { #define yylex lexer.lex } %start program; %token IDENTIFIER %token TRAIT %token > INTEGER INTEGER64 WORD WORD64 %token > INTEGER8 WORD8 %token > INTEGER16 WORD16 %token > INTEGER32 WORD32 %token SINGLE %token DOUBLE %token CHARACTER %token STRING %token BOOLEAN %token LEFT_PAREN "(" RIGHT_PAREN ")" LEFT_SQUARE "[" RIGHT_SQUARE "]" LEFT_BRACE "{" RIGHT_BRACE "}" GENERIC_OPEN "#[" ATTRIBUTE_OPEN "#{" %token ASSIGNMENT ":=" ARROW "->" AT "@" HAT "^" COLON ":" SEMICOLON ";" DOT "." COMMA "," %token PROGRAM "program" MODULE "module" CAST "cast" NIL "nil" CONST "const" VAR "var" THREADVAR "threadvar" PROCEDURE "proc" TYPE "type" RECORD "record" EXTERN "extern" IF "if" THEN "then" ELSE "else" ELSIF "elsif" WHILE "while" DO "do" FOR "for" REPEAT "repeat" UNTIL "until" WITH "with" RETURN "return" IMPORT "import" BEGIN_BLOCK "begin" END_BLOCK "end" DEFER "defer" BLOCK "block" BREAK "break" CASE "case" OF "of" TO "to" PIPE "|" %token NOT "~" OR "or" AND "&" XOR "xor" EQUALS "=" NOT_EQUAL "<>" LESS_THAN "<" GREATER_THAN ">" LESS_EQUAL "<=" GREATER_EQUAL ">=" SHIFT_LEFT "<<" SHIFT_RIGHT ">>" PLUS "+" MINUS "-" MULTIPLICATION "*" DIVISION "/" REMAINDER "%" %left "or" "&" "xor" %left "=" "<>" "<" ">" "<=" ">=" %left "<<" ">>" %left "+" "-" %left "*" "/" "%" %type literal; %type > case_labels; %type > switch_case; %type > switch_cases; %type variable_declaration; %type > variable_declarations; %type type_expression return_declaration; %type > type_expressions; %type expression operand simple_expression procedure_return unary_expression; %type binary_expression; %type > optional_expressions required_expressions actual_parameter_list; %type designator_expression; %type call_expression; %type statement; %type > statements statement_part optional_begin_statements; %type procedure_declaration; %type procedure_heading; %type type_declaration; %type > type_declarations sections section; %type , std::vector>> required_declarations optional_declarations; %type > procedure_body; %type field_declaration; %type > field_declarations; %type > field_initializer; %type > field_initializers; %type > elsif_then_statements elsif_do_statements;; %type > else_statements block; %type > identifier with_counter; %type > identifier_definition attributed_identifier; %type > identifier_definitions; %type > attribute; %type > attributes; %type > import_declaration; %type > required_identifiers optional_identifiers generic_parameters; %type > enumeration_member; %type > enumeration_members; %type name_reference; %type > import_declarations; %type array_type_expression; %type > unit; %% program: unit { driver.tree = $1; } unit: "module" optional_declarations "end" "." { auto [modules, sections] = $2; $$ = std::make_unique(boot::make_position(@$), std::move(modules), std::move(sections)); } | "program" "(" optional_identifiers ")" required_declarations statement_part "end" "." { auto [modules, sections] = $5; $$ = std::make_unique(boot::make_position(@$), std::move(modules), std::move(sections), $3, boot::make_position(@1), $6); } | "program" "(" optional_identifiers ")" optional_begin_statements "end" "." { $$ = std::make_unique(boot::make_position(@$), std::vector{}, std::vector{}, $3, boot::make_position(@1), $5); } section: "type" type_declarations { $$ = $2; } | "var" variable_declarations { std::vector variables = $2; $$.insert($$.end(), variables.begin(), variables.end()); } | "threadvar" variable_declarations { std::vector variables = $2; for (boot::variable_declaration *const variable : variables) { variable->is_thread_local = true; } $$.insert($$.end(), variables.begin(), variables.end()); } | procedure_declaration { $$.emplace_back($1); } required_declarations: sections { $$ = { {}, $1 }; } | "import" import_declarations { $$ = { $2, {} }; } | "import" import_declarations sections { $$ = { $2, $3 }; } optional_declarations: %empty {} | required_declarations { $$ = $1; } sections: section sections { std::vector appendix = $2; $$ = $1; $$.insert($$.end(), appendix.begin(), appendix.end()); } | section { $$ = $1; } procedure_body: block procedure_return { $$ = std::make_unique(std::move(*$1), $2); } block: "var" variable_declarations statement_part { $$ = std::make_unique($2, $3); } | optional_begin_statements { $$ = std::make_unique(std::vector(), $1); } statement_part: %empty {} | "begin" statements { $$ = $2; } optional_begin_statements: "begin" statements { $$ = $2; } | statements { $$ = $1; } identifier: IDENTIFIER { $$ = std::make_unique($1, boot::make_position(@1)); } identifier_definition: IDENTIFIER "*" { $$ = std::make_unique($1, boot::make_position(@1), true); } | IDENTIFIER { $$ = std::make_unique($1, boot::make_position(@1), false); } attribute: "#{" IDENTIFIER optional_expressions "}" { $$ = std::make_unique(boot::identifier($2, boot::make_position(@2)), $3); } attributes: %empty {} | attribute attributes { $$ = $2; $$.emplace($$.cbegin(), std::move(*$1)); } attributed_identifier: identifier_definition attributes { $$ = $1; $$->attributes = $2; } identifier_definitions: attributed_identifier "," identifier_definitions { $$ = $3; $$.emplace($$.cbegin(), std::move(*$1)); } | attributed_identifier { $$.emplace_back(std::move(*$1)); } return_declaration: %empty {} | "->" type_expression { $$ = $2; } procedure_heading: "(" field_declarations ")" attributes return_declaration { $$ = new boot::procedure_type_expression(boot::make_position(@$), $2, $5); $$->attributes = $4; } procedure_declaration: "proc" identifier_definition generic_parameters procedure_heading procedure_body { $$ = new boot::procedure_declaration(boot::make_position(@$), std::move(*$2), $4, std::move(*$5)); $$->parameters = $3; } | "proc" identifier_definition generic_parameters procedure_heading "extern" { $$ = new boot::procedure_declaration(boot::make_position(@$), std::move(*$2), $4); $$->parameters = $3; } call_expression: designator_expression actual_parameter_list { $$ = new boot::procedure_call(boot::make_position(@$), $1, $2); } with_counter: "with" identifier { $$ = $2; } | %empty { $$ = nullptr; } elsif_do_statements: "elsif" expression "do" block elsif_do_statements { $$ = $5; $$.emplace($$.begin(), $2, std::move(*$4)); } | %empty {} else_statements: "else" block { $$ = $2; } | %empty { $$ = nullptr; } elsif_then_statements: "elsif" expression "then" block elsif_then_statements { $$ = $5; $$.emplace($$.begin(), $2, std::move(*$4)); } | %empty {} procedure_return: "return" expression { $$ = $2; } | "return" { $$ = nullptr; } literal: INTEGER { auto [magnitude, wants_signed] = $1; $$ = new boot::literal(boot::make_position(@$), boot::integer_literal::from(magnitude), wants_signed, false); } | INTEGER8 { auto [magnitude, wants_signed] = $1; $$ = new boot::literal(boot::make_position(@$), boot::integer_literal::from(magnitude), wants_signed); } | INTEGER16 { auto [magnitude, wants_signed] = $1; $$ = new boot::literal(boot::make_position(@$), boot::integer_literal::from(magnitude), wants_signed); } | INTEGER32 { auto [magnitude, wants_signed] = $1; $$ = new boot::literal(boot::make_position(@$), boot::integer_literal::from(magnitude), wants_signed); } | INTEGER64 { auto [magnitude, wants_signed] = $1; $$ = new boot::literal(boot::make_position(@$), boot::integer_literal::from(magnitude), wants_signed); } | WORD { auto [magnitude, wants_signed] = $1; $$ = new boot::literal(boot::make_position(@$), boot::integer_literal::from(magnitude), wants_signed, false); } | WORD8 { auto [magnitude, wants_signed] = $1; $$ = new boot::literal(boot::make_position(@$), boot::integer_literal::from(magnitude), wants_signed); } | WORD16 { auto [magnitude, wants_signed] = $1; $$ = new boot::literal(boot::make_position(@$), boot::integer_literal::from(magnitude), wants_signed); } | WORD32 { auto [magnitude, wants_signed] = $1; $$ = new boot::literal(boot::make_position(@$), boot::integer_literal::from(magnitude), wants_signed); } | WORD64 { auto [magnitude, wants_signed] = $1; $$ = new boot::literal(boot::make_position(@$), boot::integer_literal::from(magnitude), wants_signed); } | SINGLE { $$ = new boot::literal(boot::make_position(@$), boot::float_literal::from($1), boot::integer_sign::unmarked); } | DOUBLE { $$ = new boot::literal(boot::make_position(@$), boot::float_literal::from($1), boot::integer_sign::unmarked); } | BOOLEAN { $$ = new boot::literal(boot::make_position(@$), $1, boot::integer_sign::_unsigned); } | CHARACTER { $$ = new boot::literal(boot::make_position(@$), $1, boot::integer_sign::_unsigned); } | "nil" { $$ = new boot::literal(boot::make_position(@$), nullptr, boot::integer_sign::_unsigned); } | STRING { $$ = new boot::literal(boot::make_position(@$), $1, boot::integer_sign::_unsigned); } simple_expression: literal { $$ = $1; } | designator_expression { $$ = $1; } | TRAIT "(" type_expressions ")" { $$ = new boot::traits_expression(boot::make_position(@$), boot::identifier($1, boot::make_position(@1)), $3); } | "cast" "(" expression ":" type_expression ")" { $$ = new boot::cast_expression(boot::make_position(@$), $5, $3); } | call_expression { $$ = $1; } | "(" expression ")" { $$ = $2; } | "{" type_expression field_initializers "}" { $$ = new boot::record_constructor_expression(boot::make_position(@$), $2, $3); } | "[" required_expressions "]" { $$ = new boot::array_constructor_expression(boot::make_position(@$), $2); } operand: unary_expression { $$ = $1; } | simple_expression { $$ = $1; } expression: binary_expression { $$ = $1; } | operand { $$ = $1; } binary_expression: expression "*" expression { $$ = new boot::binary_expression(boot::make_position(@$), $1, $3, boot::binary_operator::multiplication); } | expression "/" expression { $$ = new boot::binary_expression(boot::make_position(@$), $1, $3, boot::binary_operator::division); } | expression "%" expression { $$ = new boot::binary_expression(boot::make_position(@$), $1, $3, boot::binary_operator::remainder); } | expression "+" expression { $$ = new boot::binary_expression(boot::make_position(@$), $1, $3, boot::binary_operator::sum); } | expression "-" expression { $$ = new boot::binary_expression(boot::make_position(@$), $1, $3, boot::binary_operator::subtraction); } | expression "=" expression { $$ = new boot::binary_expression(boot::make_position(@$), $1, $3, boot::binary_operator::equals); } | expression "<>" expression { $$ = new boot::binary_expression(boot::make_position(@$), $1, $3, boot::binary_operator::not_equals); } | expression "<" expression { $$ = new boot::binary_expression(boot::make_position(@$), $1, $3, boot::binary_operator::less); } | expression ">" expression { $$ = new boot::binary_expression(boot::make_position(@$), $1, $3, boot::binary_operator::greater); } | expression "<=" expression { $$ = new boot::binary_expression(boot::make_position(@$), $1, $3, boot::binary_operator::less_equal); } | expression ">=" expression { $$ = new boot::binary_expression(boot::make_position(@$), $1, $3, boot::binary_operator::greater_equal); } | expression "&" expression { $$ = new boot::binary_expression(boot::make_position(@$), $1, $3, boot::binary_operator::conjunction); } | expression "or" expression { $$ = new boot::binary_expression(boot::make_position(@$), $1, $3, boot::binary_operator::disjunction); } | expression "xor" expression { $$ = new boot::binary_expression(boot::make_position(@$), $1, $3, boot::binary_operator::exclusive_disjunction); } | expression "<<" expression { $$ = new boot::binary_expression(boot::make_position(@$), $1, $3, boot::binary_operator::shift_left); } | expression ">>" expression { $$ = new boot::binary_expression(boot::make_position(@$), $1, $3, boot::binary_operator::shift_right); } unary_expression: "@" operand { $$ = new boot::unary_expression(boot::make_position(@$), $2, boot::unary_operator::reference); } | "~" operand { $$ = new boot::unary_expression(boot::make_position(@$), $2, boot::unary_operator::negation); } | "-" operand { auto operand = $2; auto integer_operand = operand->is_literal() == nullptr ? nullptr : operand->is_literal()->is_a(); if (integer_operand != nullptr && integer_operand->wants_signed == boot::integer_sign::unmarked) { // Rebuild the literal with a position spanning the minus and the // integer token. $$ = new boot::literal(boot::make_position(@$), integer_operand->value, boot::integer_sign::negative, integer_operand->has_explicit_size); delete integer_operand; } else { $$ = new boot::unary_expression(boot::make_position(@$), operand, boot::unary_operator::minus); } } | "+" operand { $$ = new boot::unary_expression(boot::make_position(@$), $2, boot::unary_operator::plus); } optional_expressions: required_expressions { $$ = $1; } | %empty { $$ = std::vector(); } required_expressions: expression "," required_expressions { $$ = $3; $$.emplace($$.cbegin(), $1); } | expression { $$.push_back($1); } type_expressions: type_expression "," type_expressions { $$ = $3; $$.emplace($$.cbegin(), $1); } | type_expression { $$.push_back($1); } designator_expression: simple_expression "[" expression "]" { $$ = new boot::array_access_expression(boot::make_position(@$), $1, $3); } | simple_expression "[" expression "to" expression "]" { $$ = new boot::slicing_expression(boot::make_position(@$), $1, $3, $5); } | simple_expression "." identifier { $$ = new boot::field_access_expression(boot::make_position(@$), $1, std::move(*$3)); } | simple_expression "^" { $$ = new boot::dereference_expression(boot::make_position(@$), $1); } | name_reference { $$ = $1; } name_reference: IDENTIFIER { $$ = new boot::named_expression(boot::make_position(@$), $1); } | IDENTIFIER "#[" type_expressions "]" { $$ = new boot::named_expression(boot::make_position(@$), $1, $3); } statement: designator_expression ":=" expression { $$ = new boot::assign_statement(boot::make_position(@$), $1, $3); } | "while" expression "do" block elsif_do_statements "end" { boot::conditional_statements *body = new boot::conditional_statements($2, std::move(*$4)); $$ = new boot::while_statement(boot::make_position(@$), body, $5); } | "repeat" block "until" expression { $$ = new boot::repeat_statement(boot::make_position(@$), std::move(*$2), $4); } | "for" identifier "of" expression with_counter "do" block "end" { $$ = new boot::for_statement(boot::make_position(@$), std::move(*$2), $4, std::move(*$7), $5.release()); } | "if" expression "then" block 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(@$), then, $5, $6.release()); } | call_expression { $$ = $1; } | "defer" block "end" { $$ = new boot::defer_statement(boot::make_position(@$), std::move(*$2)); } | "block" identifier block "end" { $$ = new boot::block_statement(boot::make_position(@$), std::move(*$2), std::move(*$3)); } | "break" identifier { $$ = new boot::break_statement(boot::make_position(@$), std::move(*$2)); } | "case" expression "of" switch_cases else_statements "end" { $$ = new boot::case_statement(boot::make_position(@$), $2, $4, $5.release()); } | %empty { $$ = new boot::empty_statement(boot::make_position(@$)); } switch_case: case_labels ":" block { $$ = std::make_unique($1, std::move(*$3)); } switch_cases: switch_case "|" switch_cases { $$ = $3; $$.insert($$.cbegin(), std::move(*$1)); } | switch_case { $$.push_back(std::move(*$1)); } case_labels: expression "," case_labels { $$ = $3; $$.emplace($$.cbegin(), $1); } | expression { $$.push_back($1); } statements: statements ";" statement { $$ = $1; $$.insert($$.cend(), $3); } | statement { $$.push_back($1); } field_declaration: identifier_definitions ":" type_expression { $$ = std::make_pair($1, std::shared_ptr($3)); } field_declarations: field_declaration ";" field_declarations { $$ = $3; $$.emplace($$.cbegin(), $1); } | field_declaration { $$.emplace_back($1); } | %empty {} field_initializer: identifier ":" expression { $$ = std::make_unique(std::move(*$1), $3); } field_initializers: ";" field_initializer { $$.push_back(std::move(*$2)); } | field_initializers "," field_initializer { $$ = $1; $$.push_back(std::move(*$3)); } | %empty {} array_type_expression: "[" expression "]" type_expression { $$ = new boot::array_type_expression(boot::make_position(@$), $4, $2); } type_expression: array_type_expression { $$ = $1; } | "[" "]" type_expression { $$ = new boot::slice_type_expression(boot::make_position(@$), $3); } | "const" type_expression { $$ = new boot::constant_type_expression(boot::make_position(@$), $2); } | "^" type_expression { $$ = new boot::pointer_type_expression(boot::make_position(@$), $2); } | "record" attributes field_declarations "end" { auto *record = new boot::record_type_expression(boot::make_position(@$), $3); record->attributes = $2; $$ = record; } | "record" "(" name_reference ")" attributes field_declarations "end" { boot::named_expression *reference = $3; auto *record = new boot::record_type_expression(boot::make_position(@$), $6, boot::identifier(reference->name, reference->position()), std::move(reference->arguments)); record->attributes = $5; delete reference; $$ = record; } | "proc" procedure_heading { $$ = $2; } | "(" enumeration_members ")" { $$ = new boot::enumeration_type_expression(boot::make_position(@$), std::move($2)); } | name_reference { $$ = $1; } enumeration_members: enumeration_member "," enumeration_members { $$ = std::move($3); $$.emplace($$.cbegin(), std::move(*$1)); } | enumeration_member { $$.emplace_back(std::move(*$1)); } enumeration_member: identifier { $$ = std::make_unique(std::move(*$1), nullptr); } | identifier "=" expression { $$ = std::make_unique(std::move(*$1), std::unique_ptr($3)); } generic_parameters: %empty {} | "#[" required_identifiers "]" { $$ = $2; } required_identifiers: identifier "," required_identifiers { $$ = $3; $$.emplace($$.cbegin(), std::move(*$1)); } | identifier { $$.emplace_back(std::move(*$1)); } optional_identifiers: required_identifiers { $$ = $1; } | %empty {} variable_declaration: identifier_definitions ":" type_expression { $$ = new boot::variable_declaration(boot::make_position(@$), $1, $3); } | identifier_definitions ":" type_expression ":=" "extern" { $$ = new boot::variable_declaration(boot::make_position(@$), $1, $3, std::monostate{}); } | identifier_definitions ":" type_expression ":=" expression { $$ = new boot::variable_declaration(boot::make_position(@$), $1, $3, $5); } | identifier_definitions ":=" expression { $$ = new boot::variable_declaration(boot::make_position(@$), $1, nullptr, $3); } | identifier_definitions ":" "const" ":=" expression { $$ = new boot::variable_declaration(boot::make_position(@$), $1, nullptr, $5, true); } variable_declarations: %empty {} | variable_declaration variable_declarations { $$ = $2; $$.insert(std::cbegin($$), $1); } import_declaration: IDENTIFIER "." import_declaration { $$ = $3; $$.emplace($$.cbegin(), $1); } | IDENTIFIER { $$.emplace_back($1); } import_declarations: import_declaration "," import_declarations { $$ = $3; $$.emplace($$.cbegin(), new boot::import_declaration(boot::make_position(@$), $1)); } | import_declaration { $$.emplace_back(new boot::import_declaration(boot::make_position(@$), $1)); } type_declaration: identifier_definition generic_parameters "=" type_expression { $$ = new boot::type_declaration(boot::make_position(@$), std::move(*$1), $4); $$->parameters = $2; } | identifier_definition generic_parameters "=" "extern" { $$ = new boot::type_declaration(boot::make_position(@$), std::move(*$1), new boot::extern_type_expression(boot::make_position(@$))); $$->parameters = $2; } type_declarations: type_declaration type_declarations { $$ = $2; $$.insert($$.cbegin(), $1); } | %empty {} actual_parameter_list: "(" optional_expressions ")" { $$ = $2; } %% void yy::parser::error(const location_type& loc, const std::string& message) { driver.add_error(message, loc); }