/* 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 ARRAY OF TYPE RECORD POINTER TO UNION %token BEGIN_BLOCK END_BLOCK EXTERN DEFER %token LEFT_PAREN RIGHT_PAREN LEFT_SQUARE RIGHT_SQUARE SEMICOLON DOT COMMA %token AND OR NOT CAST SHIFT_LEFT SHIFT_RIGHT %token GREATER_EQUAL LESS_EQUAL LESS_THAN GREATER_THAN NOT_EQUAL EQUALS %token PLUS MINUS MULTIPLICATION DIVISION REMAINDER %token ASSIGNMENT COLON HAT AT NIL ARROW %left OR AND XOR %left EQUALS NOT_EQUAL LESS_THAN GREATER_THAN LESS_EQUAL GREATER_EQUAL %left PLUS MINUS %left MULTIPLICATION DIVISION REMAINDER %type literal; %type constant_definition; %type > constant_part constant_definitions; %type variable_declaration; %type > variable_declarations variable_part formal_parameter_list; %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 procedure_heading; %type > procedure_definitions procedure_part; %type type_definition; %type > type_definitions type_part; %type block; %type > field_declaration; %type >> field_list; %type > elsif_statement_list; %type cast_expression; %type defer_statement; %type > identifier_definition; %% 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); } procedure_heading: PROCEDURE identifier_definition formal_parameter_list SEMICOLON { $$ = new elna::boot::procedure_definition(elna::boot::make_position(@1), $2.first, $2.second); std::swap($3, $$->parameters); } | PROCEDURE identifier_definition formal_parameter_list ARROW type_expression SEMICOLON { $$ = new elna::boot::procedure_definition(elna::boot::make_position(@1), $2.first, $2.second, $5); std::swap($3, $$->parameters); } procedure_definition: procedure_heading block { $$ = $1->add_body($2); } | procedure_heading EXTERN { $$ = $1; } 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); } while_statement: WHILE expression DO optional_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); } elsif_statement_list: ELSIF expression THEN optional_statements elsif_statement_list { 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_statement_list 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_statement_list 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 SEMICOLON statements { std::swap($$, $3); $$.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); } field_list: field_declaration field_list { std::swap($$, $2); $$.emplace($$.cbegin(), $1); } | field_declaration { $$.emplace_back($1); } type_expression: ARRAY INTEGER OF type_expression { $$ = new elna::boot::array_type(elna::boot::make_position(@1), $4, $2); } | POINTER TO type_expression { $$ = new elna::boot::pointer_type(elna::boot::make_position(@1), $3); } | RECORD field_list END_BLOCK { $$ = new elna::boot::record_type(elna::boot::make_position(@1), std::move($2)); } | UNION field_list END_BLOCK { $$ = new elna::boot::union_type(elna::boot::make_position(@1), std::move($2)); } | IDENTIFIER { $$ = new elna::boot::basic_type(elna::boot::make_position(@1), $1); } variable_declaration: identifier_definition COLON type_expression { $$ = new elna::boot::variable_declaration(elna::boot::make_position(@2), $1.first, $1.second, $3); } variable_declarations: variable_declaration COMMA variable_declarations { std::swap($$, $3); $$.emplace($$.cbegin(), $1); } | variable_declaration { $$.emplace_back(std::move($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_list: LEFT_PAREN RIGHT_PAREN {} | LEFT_PAREN variable_declarations 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); }