/* 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
{
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(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
%token TRAIT
%token INTEGER
%token WORD
%token FLOAT
%token CHARACTER
%token STRING
%token BOOLEAN
%token LEFT_PAREN "(" RIGHT_PAREN ")" LEFT_SQUARE "[" RIGHT_SQUARE "]"
%token ASSIGNMENT ":="
ARROW "->" EXCLAMATION "!"
AT "@" HAT "^"
COLON ":" SEMICOLON ";" DOT "." COMMA ","
%token NOT "~"
CAST "cast"
NIL "nil"
CONST "const"
VAR "var"
PROCEDURE "proc"
TYPE "type"
RECORD "record"
UNION "union"
EXTERN "extern"
IF "if"
WHILE "while"
DO "do"
THEN "then"
ELSE "else"
ELSIF "elsif"
RETURN "return"
BEGIN_BLOCK "begin"
END_BLOCK "end"
DEFER "defer"
%token 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 "|" "&" "xor"
%left "=" "<>" "<" ">" "<=" ">="
%left "<<" ">>"
%left "+" "-"
%left "*" "/" "%"
%type literal;
%type constant_definition;
%type > constant_part constant_definitions;
%type > variable_declarations variable_part variable_declaration;
%type > type_expression;
%type >> type_expressions;
%type traits_expression;
%type expression operand qualident;
%type unary_expression;
%type binary_expression;
%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;
%type procedure_definition;
%type , std::shared_ptr>> procedure_heading;
%type return_declaration;
%type > procedure_definitions procedure_part;
%type type_definition;
%type > type_definitions type_part;
%type block;
%type field_declaration formal_parameter;
%type >>>
optional_fields required_fields formal_parameters;
%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" statements "end" "."
{
auto tree = new boot::program(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" statements "end"
{
$$ = new boot::block(boot::make_position(@3));
std::swap($$->constants, $1);
std::swap($$->variables, $2);
std::swap($$->body, $4);
}
identifier_definition:
IDENTIFIER "*"
{
$$ = std::make_pair($1, true);
}
| IDENTIFIER
{
$$ = std::make_pair($1, false);
}
identifier_definitions:
identifier_definition "," identifier_definitions
{
std::swap($$, $3);
$$.emplace($$.cbegin(), $1);
}
| identifier_definition { $$.emplace_back(std::move($1)); }
return_declaration:
/* proper procedure */ {}
| "->" "!" { $$ = boot::return_declaration(std::monostate{}); }
| "->" type_expression { $$ = boot::return_declaration($2); }
procedure_heading:
"(" formal_parameters ")" return_declaration
{
$$.second = std::make_shared(boot::make_position(@1),
std::move($4));
for (auto& [name, type] : $2)
{
$$.first.emplace_back(std::move(name));
$$.second->parameters.push_back(type);
}
}
procedure_definition:
"proc" identifier_definition procedure_heading ";" block
{
$$ = new boot::procedure_definition(boot::make_position(@1),
$2.first, $2.second, $3.second, $5);
std::swap($3.first, $$->parameter_names);
}
| "proc" identifier_definition procedure_heading ";" "extern"
{
$$ = new boot::procedure_definition(boot::make_position(@1), $2.first, $2.second, $3.second);
std::swap($3.first, $$->parameter_names);
}
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 ":=" expression
{
$$ = new boot::assign_statement(boot::make_position(@1), $1, $3);
}
call_expression: designator_expression actual_parameter_list
{
$$ = new boot::procedure_call(boot::make_position(@1), $1);
std::swap($$->arguments, $2);
}
cast_expression: "cast" "(" expression ":" type_expression ")"
{
$$ = new boot::cast_expression(boot::make_position(@1), $5, $3);
}
elsif_do_statements:
"elsif" expression "do" statements elsif_do_statements
{
boot::conditional_statements *branch = new boot::conditional_statements($2);
std::swap(branch->statements, $4);
std::swap($5, $$);
$$.emplace($$.begin(), branch);
}
| {}
while_statement: "while" expression "do" statements elsif_do_statements "end"
{
auto body = new boot::conditional_statements($2);
std::swap($4, body->statements);
$$ = new boot::while_statement(boot::make_position(@1), body);
std::swap($5, $$->branches);
}
elsif_then_statements:
"elsif" expression "then" statements elsif_then_statements
{
boot::conditional_statements *branch = new boot::conditional_statements($2);
std::swap(branch->statements, $4);
std::swap($5, $$);
$$.emplace($$.begin(), branch);
}
| {}
if_statement:
"if" expression "then" statements elsif_then_statements "end"
{
auto then = new boot::conditional_statements($2);
std::swap($4, then->statements);
$$ = new boot::if_statement(boot::make_position(@1), then);
std::swap($5, $$->branches);
}
| "if" expression "then" statements elsif_then_statements "else" statements "end"
{
auto then = new boot::conditional_statements($2);
std::swap($4, then->statements);
auto _else = new std::vector(std::move($7));
$$ = new boot::if_statement(boot::make_position(@1), then, _else);
std::swap($5, $$->branches);
}
return_statement:
"return" expression
{
$$ = new boot::return_statement(boot::make_position(@1), $2);
}
| "return"
{
$$ = new boot::return_statement(boot::make_position(@1));
}
defer_statement: DEFER statements "end"
{
$$ = new boot::defer_statement(boot::make_position(@1));
std::swap($2, $$->statements);
}
literal:
INTEGER
{
$$ = new boot::literal(boot::make_position(@1), $1);
}
| WORD
{
$$ = new boot::literal(boot::make_position(@1), $1);
}
| FLOAT
{
$$ = new boot::literal(boot::make_position(@1), $1);
}
| BOOLEAN
{
$$ = new boot::literal(boot::make_position(@1), $1);
}
| CHARACTER
{
$$ = new boot::literal(boot::make_position(@1), $1.at(0));
}
| "nil"
{
$$ = new boot::literal(boot::make_position(@1), nullptr);
}
| STRING
{
$$ = new boot::literal(boot::make_position(@1), $1);
}
traits_expression:
TRAIT "(" type_expressions ")"
{
$$ = new boot::traits_expression(boot::make_position(@1), $1);
std::swap($3, $$->parameters);
}
qualident:
literal { $$ = $1; }
| designator_expression { $$ = $1; }
| traits_expression { $$ = $1; }
| cast_expression { $$ = $1; }
| call_expression { $$ = $1; }
| "(" expression ")" { $$ = $2; }
operand:
unary_expression { $$ = $1; }
| qualident { $$ = $1; }
expression:
binary_expression { $$ = $1; }
| operand { $$ = $1; }
binary_expression:
expression "*" expression
{
$$ = new boot::binary_expression(boot::make_position(@2), $1, $3, boot::binary_operator::multiplication);
}
| expression "/" expression
{
$$ = new boot::binary_expression(boot::make_position(@2), $1, $3, boot::binary_operator::division);
}
| expression "%" expression
{
$$ = new boot::binary_expression(boot::make_position(@2), $1, $3, boot::binary_operator::remainder);
}
| expression "+" expression
{
$$ = new boot::binary_expression(boot::make_position(@2), $1, $3, boot::binary_operator::sum);
}
| expression "-" expression
{
$$ = new boot::binary_expression(boot::make_position(@2), $1, $3, boot::binary_operator::subtraction);
}
| expression "=" expression
{
$$ = new boot::binary_expression(boot::make_position(@2), $1, $3, boot::binary_operator::equals);
}
| expression "<>" expression
{
$$ = new boot::binary_expression(boot::make_position(@2), $1, $3, boot::binary_operator::not_equals);
}
| expression "<" expression
{
$$ = new boot::binary_expression(boot::make_position(@2), $1, $3, boot::binary_operator::less);
}
| expression ">" expression
{
$$ = new boot::binary_expression(boot::make_position(@2), $1, $3, boot::binary_operator::greater);
}
| expression "<=" expression
{
$$ = new boot::binary_expression(boot::make_position(@2), $1, $3,
boot::binary_operator::less_equal);
}
| expression ">=" expression
{
$$ = new boot::binary_expression(boot::make_position(@2), $1, $3, boot::binary_operator::greater_equal);
}
| expression "&" expression
{
$$ = new boot::binary_expression(boot::make_position(@2), $1, $3, boot::binary_operator::conjunction);
}
| expression "|" expression
{
$$ = new boot::binary_expression(boot::make_position(@2), $1, $3, boot::binary_operator::disjunction);
}
| expression "xor" expression
{
$$ = new boot::binary_expression(boot::make_position(@2), $1, $3,
boot::binary_operator::exclusive_disjunction);
}
| expression "<<" expression
{
$$ = new boot::binary_expression(boot::make_position(@2), $1, $3, boot::binary_operator::shift_left);
}
| expression ">>" expression
{
$$ = new boot::binary_expression(boot::make_position(@2), $1, $3, boot::binary_operator::shift_right);
}
unary_expression:
"@" operand
{
$$ = new boot::unary_expression(boot::make_position(@1), $2, boot::unary_operator::reference);
}
| "~" operand
{
$$ = new boot::unary_expression(boot::make_position(@1), $2, boot::unary_operator::negation);
}
| "-" operand
{
$$ = new boot::unary_expression(boot::make_position(@1), $2, boot::unary_operator::minus);
}
expressions:
expression "," expressions
{
std::swap($$, $3);
$$.emplace($$.cbegin(), $1);
}
| expression { $$.push_back($1); }
type_expressions:
type_expression "," type_expressions
{
std::swap($$, $3);
$$.emplace($$.cbegin(), $1);
}
| type_expression { $$.push_back($1); }
designator_expression:
qualident "[" expression "]"
{
$$ = new boot::array_access_expression(boot::make_position(@2), $1, $3);
}
| qualident "." IDENTIFIER
{
$$ = new boot::field_access_expression(boot::make_position(@2), $1, $3);
}
| qualident "^"
{
$$ = new boot::dereference_expression(boot::make_position(@1), $1);
}
| IDENTIFIER
{
$$ = new boot::variable_expression(boot::make_position(@1), $1);
}
statement:
assign_statement { $$ = $1; }
| while_statement { $$ = $1; }
| if_statement { $$ = $1; }
| return_statement { $$ = $1; }
| call_expression { $$ = $1; }
| defer_statement { $$ = $1; }
statements:
statement ";" statements
{
std::swap($$, $3);
$$.insert($$.cbegin(), $1);
}
| statement { $$.push_back($1); }
| /* no statements */ {}
field_declaration:
IDENTIFIER ":" type_expression { $$ = std::make_pair($1, $3); }
required_fields:
field_declaration ";" required_fields
{
std::swap($$, $3);
$$.emplace($$.cbegin(), $1);
}
| field_declaration { $$.emplace_back($1); }
optional_fields:
required_fields { std::swap($$, $1); }
| /* no fields */ {}
type_expression:
"[" INTEGER "]" type_expression
{
$$ = std::make_shared(boot::make_position(@1), $4, $2);
}
| "^" type_expression
{
$$ = std::make_shared(boot::make_position(@1), $2);
}
| "record" optional_fields "end"
{
$$ = std::make_shared(boot::make_position(@1), std::move($2));
}
| "union" required_fields "end"
{
$$ = std::make_shared(boot::make_position(@1), std::move($2));
}
| "proc" "(" type_expressions ")" return_declaration
{
auto result = std::make_shared(boot::make_position(@1),
std::move($5));
std::swap(result->parameters, $3);
$$ = result;
}
| IDENTIFIER
{
$$ = std::make_shared(boot::make_position(@1), $1);
}
variable_declaration: identifier_definitions ":" type_expression
{
for (const std::pair& identifier : $1)
{
boot::variable_declaration *declaration = new boot::variable_declaration(
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 "=" literal
{
$$ = new boot::constant_definition(boot::make_position(@1), $1.first, $1.second, $3);
}
constant_definitions:
constant_definition ";" constant_definitions
{
std::swap($$, $3);
$$.insert($$.cbegin(), $1);
}
| /* no constant definitions */ {}
constant_part:
/* no constant definitions */ {}
| "const" constant_definitions { std::swap($$, $2); }
type_definition: identifier_definition "=" type_expression
{
$$ = new boot::type_definition(boot::make_position(@1), $1.first, $1.second, $3);
}
type_definitions:
type_definition type_definitions
{
std::swap($$, $2);
$$.insert($$.cbegin(), $1);
}
| /* no type definitions */ {}
type_part:
/* no type definitions */ {}
| "type" type_definitions { std::swap($$, $2); }
formal_parameter: IDENTIFIER ":" type_expression
{
$$ = std::make_pair($1, $3);
}
formal_parameters:
/* no formal parameters */ {}
| formal_parameter "," formal_parameters
{
std::swap($$, $3);
$$.emplace($$.cbegin(), $1);
}
| formal_parameter { $$.emplace_back(std::move($1)); }
actual_parameter_list:
"(" ")" {}
| "(" expressions ")" { std::swap($$, $2); }
%%
void yy::parser::error(const location_type& loc, const std::string& message)
{
driver.add_error(message, driver.input_file, loc);
}