/* 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;
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
%token WORD
%token FLOAT
%token CHARACTER
%token STRING
%token BOOLEAN
%token LEFT_PAREN "(" RIGHT_PAREN ")" LEFT_SQUARE "[" RIGHT_SQUARE "]"
%token LEFT_BRACE "{" RIGHT_BRACE "}"
%token ASSIGNMENT ":="
EXCLAMATION "!"
AT "@" HAT "^"
COLON ":" SEMICOLON ";" DOT "." COMMA ","
%token NOT "~"
CAST "cast"
NIL "nil"
CONST "const"
VAR "var"
PROCEDURE "proc"
TYPE "type"
RECORD "record"
EXTERN "extern"
IF "if"
WHILE "while"
DO "do"
THEN "then"
ELSE "else"
ELSIF "elsif"
RETURN "return"
IMPORT "import"
BEGIN_BLOCK "begin"
END_BLOCK "end"
DEFER "defer"
CASE "case"
OF "of"
PIPE "|"
%token 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 constant_declaration;
%type > constant_part constant_declarations;
%type variable_declaration;
%type > variable_declarations variable_part;
%type type_expression;
%type > type_expressions;
%type traits_expression;
%type expression operand simple_expression return_statement;
%type unary_expression;
%type binary_expression;
%type > expressions actual_parameter_list;
%type designator_expression;
%type call_expression;
%type statement;
%type > statements statement_part;
%type procedure_declaration;
%type procedure_heading;
%type return_declaration;
%type > procedure_part;
%type type_declaration;
%type > type_declarations type_part;
%type > procedure_body;
%type field_declaration;
%type > optional_fields required_fields;
%type field_initializer;
%type > field_initializers;
%type > elsif_then_statements elsif_do_statements;
%type *> else_statements;
%type cast_expression;
%type identifier_definition;
%type > identifier_definitions;
%type > identifiers import_declaration;
%type > import_declarations import_part;
%%
program:
import_part constant_part type_part variable_part procedure_part statement_part return_statement "end" "."
{
boot::unit *tree = new boot::unit(boot::make_position(@$), $1, $2, $3, $4, $5, $6, $7);
driver.tree.reset(tree);
}
procedure_body:
constant_part variable_part statement_part return_statement "end"
{ $$ = std::make_unique($1, $2, $3, $4); }
statement_part:
/* no statements */ {}
| "begin" statements { $$ = $2; }
identifier_definition:
IDENTIFIER "*" { $$ = boot::identifier_definition{ $1, true }; }
| IDENTIFIER { $$ = boot::identifier_definition{ $1, false }; }
identifier_definitions:
identifier_definition "," identifier_definitions
{
$$ = $3;
$$.emplace($$.cbegin(), $1);
}
| identifier_definition { $$.emplace_back($1); }
return_declaration:
/* proper procedure */ {}
| ":" "!" { $$ = boot::procedure_type_expression::return_t(std::monostate{}); }
| ":" type_expression { $$ = boot::procedure_type_expression::return_t($2); }
procedure_heading: "(" optional_fields ")" return_declaration
{ $$ = new boot::procedure_type_expression(boot::make_position(@$), $2, $4); }
procedure_declaration:
"proc" identifier_definition procedure_heading procedure_body
{
$$ = new boot::procedure_declaration(boot::make_position(@$), $2, $3, std::move(*$4));
}
| "proc" identifier_definition procedure_heading "extern"
{
$$ = new boot::procedure_declaration(boot::make_position(@$), $2, $3);
}
procedure_part:
/* no procedure declarations */ {}
| procedure_declaration procedure_part
{
$$ = $2;
$$.emplace($$.cbegin(), $1);
}
call_expression: designator_expression actual_parameter_list
{
$$ = new boot::procedure_call(boot::make_position(@$), $1, $2);
}
cast_expression: "cast" "(" expression ":" type_expression ")"
{ $$ = new boot::cast_expression(boot::make_position(@$), $5, $3); }
elsif_do_statements:
"elsif" expression "do" statements elsif_do_statements
{
boot::conditional_statements *branch = new boot::conditional_statements($2, $4);
$$ = $5;
$$.emplace($$.begin(), branch);
}
| /* no branches */ {}
else_statements:
"else" statements { $$ = new std::vector($2); }
| { $$ = nullptr; }
elsif_then_statements:
"elsif" expression "then" statements elsif_then_statements
{
boot::conditional_statements *branch = new boot::conditional_statements($2, $4);
$$ = $5;
$$.emplace($$.begin(), branch);
}
| /* no branches */ {}
return_statement:
"return" expression { $$ = $2; }
| /* no return statement */ { $$ = nullptr; }
literal:
INTEGER { $$ = new boot::literal(boot::make_position(@$), $1); }
| WORD { $$ = new boot::literal(boot::make_position(@$), $1); }
| FLOAT { $$ = new boot::literal(boot::make_position(@$), $1); }
| BOOLEAN { $$ = new boot::literal(boot::make_position(@$), $1); }
| CHARACTER { $$ = new boot::literal(boot::make_position(@$), $1.at(0)); }
| "nil" { $$ = new boot::literal(boot::make_position(@$), nullptr); }
| STRING { $$ = new boot::literal(boot::make_position(@$), $1); }
traits_expression:
TRAIT "(" type_expressions ")"
{
$$ = new boot::traits_expression(boot::make_position(@$), $1, $3);
}
simple_expression:
literal { $$ = $1; }
| designator_expression { $$ = $1; }
| traits_expression { $$ = $1; }
| cast_expression { $$ = $1; }
| call_expression { $$ = $1; }
| "(" expression ")" { $$ = $2; }
| IDENTIFIER "{" field_initializers "}"
{
$$ = new boot::record_constructor_expression(boot::make_position(@$), $1, $3);
}
| "[" INTEGER "]" type_expression "{" expressions "}"
{
$$ = new boot::array_constructor_expression(boot::make_position(@$), $2, $4, $6);
}
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
{
$$ = new boot::unary_expression(boot::make_position(@$), $2, boot::unary_operator::minus);
}
expressions:
expression "," 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 "." IDENTIFIER
{ $$ = new boot::field_access_expression(boot::make_position(@$), $1, $3); }
| simple_expression "^"
{ $$ = new boot::dereference_expression(boot::make_position(@$), $1); }
| IDENTIFIER
{ $$ = new boot::named_expression(boot::make_position(@$), $1); }
statement:
designator_expression ":=" expression
{ $$ = new boot::assign_statement(boot::make_position(@$), $1, $3); }
| "while" expression "do" statements elsif_do_statements "end"
{
boot::conditional_statements *body = new boot::conditional_statements($2, $4);
$$ = new boot::while_statement(boot::make_position(@$), body, $5);
}
| "if" expression "then" statements elsif_then_statements else_statements "end"
{
boot::conditional_statements *then = new boot::conditional_statements($2, $4);
$$ = new boot::if_statement(boot::make_position(@$), then, $5, $6);
}
| call_expression { $$ = $1; }
| "defer" statements "end"
{ $$ = new boot::defer_statement(boot::make_position(@$), $2); }
| "case" expression "of" switch_cases else_statements "end"
{ $$ = new boot::case_statement(boot::make_position(@$), $2, $4, $5); }
| { $$ = new boot::empty_statement(boot::make_position(@$)); }
switch_case: case_labels ":" statements
{ $$ = { .labels = $1, .statements = $3 }; }
switch_cases:
switch_case "|" switch_cases
{
$$ = $3;
$$.emplace($$.cbegin(), $1);
}
| switch_case { $$.push_back($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:
identifiers ":" type_expression { $$ = std::make_pair($1, std::shared_ptr($3)); }
required_fields:
field_declaration ";" required_fields
{
$$ = $3;
$$.emplace($$.cbegin(), $1);
}
| field_declaration { $$.emplace_back($1); }
optional_fields:
required_fields { $$ = $1; }
| /* no fields */ {}
field_initializer:
IDENTIFIER ":" expression { $$.name = $1; $$.value = $3; }
field_initializers:
field_initializer "," field_initializers
{
$$ = $3;
$$.emplace($$.cbegin(), $1);
}
| field_initializer { $$.push_back($1); }
type_expression:
"[" INTEGER "]" type_expression
{
$$ = new boot::array_type_expression(boot::make_position(@$), $4, $2);
}
| "^" type_expression
{
$$ = new boot::pointer_type_expression(boot::make_position(@$), $2);
}
| "record" optional_fields "end"
{
$$ = new boot::record_type_expression(boot::make_position(@$), $2);
}
| "record" "(" IDENTIFIER ")" optional_fields "end"
{
$$ = new boot::record_type_expression(boot::make_position(@$), $5, $3);
}
| "proc" procedure_heading
{
$$ = $2;
}
| "(" identifiers ")"
{
$$ = new boot::enumeration_type_expression(boot::make_position(@$), $2);
}
| IDENTIFIER
{
$$ = new boot::named_expression(boot::make_position(@$), $1);
}
identifiers:
IDENTIFIER "," identifiers
{
$$ = $3;
$$.emplace($$.cbegin(), $1);
}
| IDENTIFIER { $$.emplace_back($1); }
variable_declaration:
identifier_definitions ":" type_expression
{
std::shared_ptr shared_type{ $3 };
$$ = new boot::variable_declaration( boot::make_position(@$), $1, shared_type);
}
| identifier_definitions ":" type_expression ":=" "extern"
{
std::shared_ptr shared_type{ $3 };
$$ = new boot::variable_declaration( boot::make_position(@$), $1, shared_type, std::monostate{});
}
| identifier_definitions ":" type_expression ":=" expression
{
std::shared_ptr shared_type{ $3 };
$$ = new boot::variable_declaration( boot::make_position(@$), $1, shared_type, $5);
}
variable_declarations:
/* no variable declarations */ {}
| variable_declaration variable_declarations
{
$$ = $2;
$$.insert(std::cbegin($$), $1);
}
variable_part:
/* no variable declarations */ {}
| "var" variable_declarations { $$ = $2; }
constant_declaration: identifier_definition ":=" expression
{
$$ = new boot::constant_declaration(boot::make_position(@$), $1, $3);
}
constant_declarations:
constant_declaration constant_declarations
{
$$ = $2;
$$.insert(std::cbegin($$), $1);
}
| /* no constant definitions */ {}
constant_part:
/* no constant definitions */ {}
| "const" constant_declarations { $$ = $2; }
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));
}
import_part:
/* no import declarations */ {}
| "import" import_declarations { $$ = $2; }
type_declaration: identifier_definition "=" type_expression
{
$$ = new boot::type_declaration(boot::make_position(@$), $1, $3);
}
type_declarations:
type_declaration type_declarations
{
$$ = $2;
$$.insert($$.cbegin(), $1);
}
| /* no type definitions */ {}
type_part:
/* no type definitions */ {}
| "type" type_declarations { $$ = $2; }
actual_parameter_list:
"(" ")" {}
| "(" expressions ")" { $$ = $2; }
%%
void yy::parser::error(const location_type& loc, const std::string& message)
{
driver.add_error(message, loc);
}