/* 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 requires { #include #include #include "elna/boot/driver.h" #if !defined(yyFlexLexerOnce) #include #endif namespace elna { namespace boot { class lexer; } } } %code provides { namespace elna { namespace boot { class lexer: public yyFlexLexer { public: yy::location location; lexer(std::istream& arg_yyin) : yyFlexLexer(&arg_yyin) { } yy::parser::symbol_type lex(elna::boot::driver& driver); }; } } } %define api.token.raw %define api.token.constructor %define api.value.type variant %parse-param {elna::boot::lexer& lexer} %param {elna::boot::driver& driver} %locations %header %code { #define yylex lexer.lex } %start program; %token IDENTIFIER "identifier" %token INTEGER "integer" %token WORD "word" %token FLOAT "float" %token CHARACTER "character" %token STRING "string" %token BOOLEAN %token IF WHILE DO THEN ELSE ELSIF RETURN %token CONST VAR PROCEDURE TYPE RECORD UNION %token BEGIN_BLOCK END_BLOCK EXTERN DEFER %token LEFT_PAREN RIGHT_PAREN LEFT_SQUARE RIGHT_SQUARE SEMICOLON DOT COMMA %token NOT CAST EXCLAMATION %token ASSIGNMENT COLON HAT AT NIL ARROW %left OR AND XOR %left EQUALS NOT_EQUAL LESS_THAN GREATER_THAN LESS_EQUAL GREATER_EQUAL %left SHIFT_LEFT SHIFT_RIGHT %left PLUS MINUS %left MULTIPLICATION DIVISION REMAINDER %type literal; %type constant_definition; %type > constant_part constant_definitions; %type > variable_declarations variable_part variable_declaration formal_parameters formal_parameter_list; %type formal_parameter %type > type_expression; %type expression operand unary; %type > expressions actual_parameter_list; %type designator_expression; %type assign_statement; %type call_expression; %type while_statement; %type if_statement; %type return_statement; %type statement; %type > statements optional_statements; %type procedure_definition; %type > procedure_heading; %type > procedure_definitions procedure_part; %type type_definition; %type > type_definitions type_part; %type block; %type field_declaration; %type >>> optional_fields fields; %type > elsif_then_statements elsif_do_statements; %type cast_expression; %type defer_statement; %type > identifier_definition; %type >> identifier_definitions; %% program: constant_part type_part variable_part procedure_part BEGIN_BLOCK optional_statements END_BLOCK DOT { auto tree = new elna::boot::program(elna::boot::make_position(@5)); std::swap(tree->constants, $1); std::swap(tree->types , $2); std::swap(tree->variables, $3); std::swap(tree->procedures, $4); std::swap(tree->body, $6); driver.tree.reset(tree); } block: constant_part variable_part BEGIN_BLOCK optional_statements END_BLOCK { $$ = new elna::boot::block(elna::boot::make_position(@3)); std::swap($$->constants, $1); std::swap($$->variables, $2); std::swap($$->body, $4); } identifier_definition: IDENTIFIER MULTIPLICATION { $$ = std::make_pair($1, true); } | IDENTIFIER { $$ = std::make_pair($1, false); } identifier_definitions: identifier_definition COMMA identifier_definitions { std::swap($$, $3); $$.emplace($$.cbegin(), $1); } | identifier_definition { $$.emplace_back(std::move($1)); } procedure_heading: formal_parameter_list { $$ = std::make_shared(elna::boot::make_position(@1)); std::swap($1, $$->parameters); } | formal_parameter_list ARROW EXCLAMATION { $$ = std::make_shared(elna::boot::make_position(@1), elna::boot::no_return); std::swap($1, $$->parameters); } | formal_parameter_list ARROW type_expression { $$ = std::make_shared(elna::boot::make_position(@1), $3); std::swap($1, $$->parameters); } procedure_definition: PROCEDURE identifier_definition procedure_heading SEMICOLON block { $$ = new elna::boot::procedure_definition(elna::boot::make_position(@1), $2.first, $2.second, $3, $5); } | PROCEDURE identifier_definition procedure_heading SEMICOLON EXTERN { $$ = new elna::boot::procedure_definition(elna::boot::make_position(@1), $2.first, $2.second, $3); } procedure_definitions: procedure_definition procedure_definitions { std::swap($$, $2); $$.emplace($$.cbegin(), std::move($1)); } | procedure_definition { $$.emplace_back(std::move($1)); } procedure_part: /* no procedure definitions */ {} | procedure_definitions { std::swap($$, $1); } assign_statement: designator_expression ASSIGNMENT expression { $$ = new elna::boot::assign_statement(elna::boot::make_position(@1), $1, $3); } call_expression: IDENTIFIER actual_parameter_list { $$ = new elna::boot::call_expression(elna::boot::make_position(@1), $1); std::swap($$->arguments(), $2); } cast_expression: CAST LEFT_PAREN expression COLON type_expression RIGHT_PAREN { $$ = new elna::boot::cast_expression(elna::boot::make_position(@1), $5, $3); } elsif_do_statements: ELSIF expression DO optional_statements elsif_do_statements { elna::boot::conditional_statements *branch = new elna::boot::conditional_statements($2); std::swap(branch->statements, $4); std::swap($5, $$); $$.emplace($$.begin(), branch); } | {} while_statement: WHILE expression DO optional_statements elsif_do_statements END_BLOCK { auto body = new elna::boot::conditional_statements($2); std::swap($4, body->statements); $$ = new elna::boot::while_statement(elna::boot::make_position(@1), body); std::swap($5, $$->branches); } elsif_then_statements: ELSIF expression THEN optional_statements elsif_then_statements { elna::boot::conditional_statements *branch = new elna::boot::conditional_statements($2); std::swap(branch->statements, $4); std::swap($5, $$); $$.emplace($$.begin(), branch); } | {} if_statement: IF expression THEN optional_statements elsif_then_statements END_BLOCK { auto then = new elna::boot::conditional_statements($2); std::swap($4, then->statements); $$ = new elna::boot::if_statement(elna::boot::make_position(@1), then); std::swap($5, $$->branches); } | IF expression THEN optional_statements elsif_then_statements ELSE optional_statements END_BLOCK { auto then = new elna::boot::conditional_statements($2); std::swap($4, then->statements); auto _else = new std::vector(std::move($7)); $$ = new elna::boot::if_statement(elna::boot::make_position(@1), then, _else); std::swap($5, $$->branches); } return_statement: RETURN expression { $$ = new elna::boot::return_statement(elna::boot::make_position(@1), $2); } defer_statement: DEFER optional_statements END_BLOCK { $$ = new elna::boot::defer_statement(elna::boot::make_position(@1)); std::swap($2, $$->statements); } literal: INTEGER { $$ = new elna::boot::number_literal(elna::boot::make_position(@1), $1); } | WORD { $$ = new elna::boot::number_literal(elna::boot::make_position(@1), $1); } | FLOAT { $$ = new elna::boot::number_literal(elna::boot::make_position(@1), $1); } | BOOLEAN { $$ = new elna::boot::number_literal(elna::boot::make_position(@1), $1); } | CHARACTER { $$ = new elna::boot::number_literal(elna::boot::make_position(@1), $1.at(0)); } | NIL { $$ = new elna::boot::number_literal(elna::boot::make_position(@1), nullptr); } | STRING { $$ = new elna::boot::number_literal(elna::boot::make_position(@1), $1); } operand: literal { $$ = $1; } | designator_expression { $$ = $1; } | LEFT_PAREN type_expression RIGHT_PAREN { $$ = new elna::boot::type_expression(elna::boot::make_position(@1), $2); } | cast_expression { $$ = $1; } | call_expression { $$ = $1; } | LEFT_PAREN expression RIGHT_PAREN { $$ = $2; } expression: unary { $$ = $1; } | expression MULTIPLICATION expression { $$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3, elna::boot::binary_operator::multiplication); } | expression DIVISION expression { $$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3, elna::boot::binary_operator::division); } | expression REMAINDER expression { $$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3, elna::boot::binary_operator::remainder); } | expression PLUS expression { $$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3, elna::boot::binary_operator::sum); } | expression MINUS expression { $$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3, elna::boot::binary_operator::subtraction); } | expression EQUALS expression { $$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3, elna::boot::binary_operator::equals); } | expression NOT_EQUAL expression { $$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3, elna::boot::binary_operator::not_equals); } | expression LESS_THAN expression { $$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3, elna::boot::binary_operator::less); } | expression GREATER_THAN expression { $$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3, elna::boot::binary_operator::greater); } | expression LESS_EQUAL expression { $$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3, elna::boot::binary_operator::less_equal); } | expression GREATER_EQUAL expression { $$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3, elna::boot::binary_operator::greater_equal); } | expression AND expression { $$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3, elna::boot::binary_operator::conjunction); } | expression OR expression { $$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3, elna::boot::binary_operator::disjunction); } | expression XOR expression { $$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3, elna::boot::binary_operator::exclusive_disjunction); } | expression SHIFT_LEFT expression { $$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3, elna::boot::binary_operator::shift_left); } | expression SHIFT_RIGHT expression { $$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3, elna::boot::binary_operator::shift_right); } unary: AT operand { $$ = new elna::boot::unary_expression(elna::boot::make_position(@1), $2, elna::boot::unary_operator::reference); } | NOT operand { $$ = new elna::boot::unary_expression(elna::boot::make_position(@1), $2, elna::boot::unary_operator::negation); } | MINUS operand { $$ = new elna::boot::unary_expression(elna::boot::make_position(@1), $2, elna::boot::unary_operator::minus); } | operand { $$ = $1; } expressions: expression COMMA expressions { std::swap($$, $3); $$.emplace($$.cbegin(), $1); } | expression { $$.emplace_back(std::move($1)); } designator_expression: operand LEFT_SQUARE expression RIGHT_SQUARE { $$ = new elna::boot::array_access_expression(elna::boot::make_position(@2), $1, $3); } | operand DOT IDENTIFIER { $$ = new elna::boot::field_access_expression(elna::boot::make_position(@2), $1, $3); } | operand HAT { $$ = new elna::boot::dereference_expression(elna::boot::make_position(@1), $1); } | IDENTIFIER { $$ = new elna::boot::variable_expression(elna::boot::make_position(@1), $1); } statement: assign_statement { $$ = $1; } | while_statement { $$ = $1; } | if_statement { $$ = $1; } | return_statement { $$ = $1; } | call_expression { $$ = new elna::boot::call_statement(elna::boot::make_position(@1), $1); } | defer_statement { $$ = $1; } statements: statement statements { std::swap($$, $2); $$.emplace($$.cbegin(), $1); } | statement { $$.push_back($1); } optional_statements: statements { std::swap($$, $1); } | /* no statements */ {} field_declaration: IDENTIFIER COLON type_expression { $$ = std::make_pair($1, $3); } fields: field_declaration fields { std::swap($$, $2); $$.emplace($$.cbegin(), $1); } | field_declaration { $$.emplace_back($1); } optional_fields: fields { std::swap($$, $1); } | /* no fields */ {} type_expression: LEFT_SQUARE INTEGER RIGHT_SQUARE type_expression { $$ = std::make_shared(elna::boot::make_position(@1), $4, $2); } | HAT type_expression { $$ = std::make_shared(elna::boot::make_position(@1), $2); } | RECORD optional_fields END_BLOCK { $$ = std::make_shared(elna::boot::make_position(@1), std::move($2)); } | UNION fields END_BLOCK { $$ = std::make_shared(elna::boot::make_position(@1), std::move($2)); } | PROCEDURE procedure_heading { $$ = $2; } | IDENTIFIER { $$ = std::make_shared(elna::boot::make_position(@1), $1); } variable_declaration: identifier_definitions COLON type_expression { for (const std::pair& identifier : $1) { elna::boot::variable_declaration *declaration = new elna::boot::variable_declaration( elna::boot::make_position(@2), identifier.first, $3, identifier.second); $$.push_back(declaration); } } variable_declarations: variable_declaration variable_declarations { std::swap($$, $1); $$.reserve($$.size() + $2.size()); $$.insert(std::end($$), std::begin($2), std::end($2)); } | variable_declaration { std::swap($$, $1); } variable_part: /* no variable declarations */ {} | VAR variable_declarations { std::swap($$, $2); } constant_definition: identifier_definition EQUALS literal { $$ = new elna::boot::constant_definition(elna::boot::make_position(@1), $1.first, $1.second, $3); } constant_definitions: constant_definition constant_definitions { std::swap($$, $2); $$.emplace($$.cbegin(), std::move($1)); } | constant_definition { $$.emplace_back(std::move($1)); } constant_part: /* no constant definitions */ {} | CONST {} | CONST constant_definitions { std::swap($$, $2); } type_definition: identifier_definition EQUALS type_expression { $$ = new elna::boot::type_definition(elna::boot::make_position(@1), $1.first, $1.second, $3); } type_definitions: type_definition type_definitions { std::swap($$, $2); $$.emplace($$.cbegin(), std::move($1)); } | type_definition { $$.emplace_back(std::move($1)); } type_part: /* no type definitions */ {} | TYPE {} | TYPE type_definitions { std::swap($$, $2); } formal_parameter: IDENTIFIER COLON type_expression { $$ = new elna::boot::variable_declaration(elna::boot::make_position(@2), $1, $3); } formal_parameters: formal_parameter COMMA formal_parameters { std::swap($$, $3); $$.emplace($$.cbegin(), $1); } | formal_parameter { $$.emplace_back(std::move($1)); } formal_parameter_list: LEFT_PAREN RIGHT_PAREN {} | LEFT_PAREN formal_parameters RIGHT_PAREN { std::swap($$, $2); } actual_parameter_list: LEFT_PAREN RIGHT_PAREN {} | LEFT_PAREN expressions RIGHT_PAREN { std::swap($$, $2); } %% void yy::parser::error(const location_type& loc, const std::string& message) { driver.error(loc, message); }