elna/boot/parser.yy
Eugen Wissner 09f204bd16
Revert "Allow only one return"
This reverts commit 18602d00a118e69d4ccc45287e89a16b86c1d92e.
2025-03-02 10:45:54 +01:00

581 lines
20 KiB
Plaintext

/* 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
<http://www.gnu.org/licenses/>. */
%require "3.4"
%language "c++"
%code requires {
#include <cstdint>
#include <iostream>
#include "elna/boot/driver.h"
#if !defined(yyFlexLexerOnce)
#include <FlexLexer.h>
#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 <std::string> IDENTIFIER
%token <std::string> TRAIT
%token <std::int32_t> INTEGER
%token <std::uint32_t> WORD
%token <float> FLOAT
%token <std::string> CHARACTER
%token <std::string> STRING
%token <bool> BOOLEAN
%token LEFT_PAREN "(" RIGHT_PAREN ")" LEFT_SQUARE "[" RIGHT_SQUARE "]"
%token ASSIGNMENT ":="
ARROW "->" EXCLAMATION "!"
AT "@" HAT "^"
COLON ":" SEMICOLON ";" DOT "." COMMA ","
%token NOT "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 "or" AND "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" "and" "xor"
%left "=" "<>" "<" ">" "<=" ">="
%left "<<" ">>"
%left "+" "-"
%left "*" "/" "%"
%type <elna::boot::literal *> literal;
%type <elna::boot::constant_definition *> constant_definition;
%type <std::vector<elna::boot::constant_definition *>> constant_part constant_definitions;
%type <std::vector<elna::boot::variable_declaration *>> variable_declarations variable_part variable_declaration
formal_parameters formal_parameter_list;
%type <elna::boot::variable_declaration *> formal_parameter
%type <std::shared_ptr<elna::boot::top_type>> type_expression;
%type <elna::boot::traits_expression *> traits_expression;
%type <elna::boot::expression *> expression operand;
%type <elna::boot::unary_expression *> unary_expression;
%type <elna::boot::binary_expression *> binary_expression;
%type <std::vector<elna::boot::expression *>> expressions actual_parameter_list;
%type <elna::boot::designator_expression *> designator_expression;
%type <elna::boot::assign_statement *> assign_statement;
%type <elna::boot::procedure_call*> call_expression;
%type <elna::boot::while_statement *> while_statement;
%type <elna::boot::if_statement *> if_statement;
%type <elna::boot::return_statement *> return_statement;
%type <elna::boot::statement *> statement;
%type <std::vector<elna::boot::statement *>> statements;
%type <elna::boot::procedure_definition *> procedure_definition;
%type <std::shared_ptr<elna::boot::procedure_type>> procedure_heading;
%type <std::vector<elna::boot::procedure_definition *>> procedure_definitions procedure_part;
%type <elna::boot::type_definition *> type_definition;
%type <std::vector<elna::boot::type_definition *>> type_definitions type_part;
%type <elna::boot::block *> block;
%type <elna::boot::field_t> field_declaration;
%type <std::vector<std::pair<std::string, std::shared_ptr<elna::boot::top_type>>>> optional_fields required_fields;
%type <std::vector<elna::boot::conditional_statements *>> elsif_then_statements elsif_do_statements;
%type <elna::boot::cast_expression *> cast_expression;
%type <elna::boot::defer_statement *> defer_statement;
%type <std::pair<std::string, bool>> identifier_definition;
%type <std::vector<std::pair<std::string, bool>>> identifier_definitions;
%%
program:
constant_part type_part variable_part procedure_part "begin" statements "end" "."
{
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" statements "end"
{
$$ = new elna::boot::block(elna::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)); }
procedure_heading:
formal_parameter_list
{
$$ = std::make_shared<elna::boot::procedure_type>(elna::boot::make_position(@1));
std::swap($1, $$->parameters);
}
| formal_parameter_list "->" "!"
{
$$ = std::make_shared<elna::boot::procedure_type>(elna::boot::make_position(@1), elna::boot::no_return);
std::swap($1, $$->parameters);
}
| formal_parameter_list "->" type_expression
{
$$ = std::make_shared<elna::boot::procedure_type>(elna::boot::make_position(@1), $3);
std::swap($1, $$->parameters);
}
procedure_definition:
"proc" identifier_definition procedure_heading ";" block
{
$$ = new elna::boot::procedure_definition(elna::boot::make_position(@1), $2.first, $2.second, $3, $5);
}
| "proc" identifier_definition procedure_heading ";" "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 ":=" expression
{
$$ = new elna::boot::assign_statement(elna::boot::make_position(@1), $1, $3);
}
call_expression: designator_expression actual_parameter_list
{
$$ = new elna::boot::procedure_call(elna::boot::make_position(@1), $1);
std::swap($$->arguments, $2);
}
cast_expression: "cast" "(" expression ":" type_expression ")"
{
$$ = new elna::boot::cast_expression(elna::boot::make_position(@1), $5, $3);
}
elsif_do_statements:
"elsif" expression "do" 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" statements elsif_do_statements "end"
{
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" 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" statements elsif_then_statements "end"
{
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" statements elsif_then_statements "else" statements "end"
{
auto then = new elna::boot::conditional_statements($2);
std::swap($4, then->statements);
auto _else = new std::vector<elna::boot::statement *>(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 statements "end"
{
$$ = new elna::boot::defer_statement(elna::boot::make_position(@1));
std::swap($2, $$->statements);
}
literal:
INTEGER
{
$$ = new elna::boot::number_literal<std::int32_t>(elna::boot::make_position(@1), $1);
}
| WORD
{
$$ = new elna::boot::number_literal<std::uint32_t>(elna::boot::make_position(@1), $1);
}
| FLOAT
{
$$ = new elna::boot::number_literal<double>(elna::boot::make_position(@1), $1);
}
| BOOLEAN
{
$$ = new elna::boot::number_literal<bool>(elna::boot::make_position(@1), $1);
}
| CHARACTER
{
$$ = new elna::boot::number_literal<unsigned char>(elna::boot::make_position(@1), $1.at(0));
}
| "nil"
{
$$ = new elna::boot::number_literal<std::nullptr_t>(elna::boot::make_position(@1), nullptr);
}
| STRING
{
$$ = new elna::boot::number_literal<std::string>(elna::boot::make_position(@1), $1);
}
traits_expression:
TRAIT "(" type_expression ")"
{
$$ = new elna::boot::traits_expression(elna::boot::make_position(@1), $1, $3);
}
operand:
literal { $$ = $1; }
| designator_expression { $$ = $1; }
| traits_expression { $$ = $1; }
| cast_expression { $$ = $1; }
| call_expression { $$ = $1; }
| "(" expression ")" { $$ = $2; }
expression:
unary_expression { $$ = $1; }
| binary_expression { $$ = $1; }
| operand { $$ = $1; }
binary_expression:
expression "*" expression
{
$$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3,
elna::boot::binary_operator::multiplication);
}
| expression "/" expression
{
$$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3,
elna::boot::binary_operator::division);
}
| expression "%" expression
{
$$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3,
elna::boot::binary_operator::remainder);
}
| expression "+" expression
{
$$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3,
elna::boot::binary_operator::sum);
}
| expression "-" expression
{
$$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3,
elna::boot::binary_operator::subtraction);
}
| expression "=" expression
{
$$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3,
elna::boot::binary_operator::equals);
}
| expression "<>" expression
{
$$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3,
elna::boot::binary_operator::not_equals);
}
| expression "<" expression
{
$$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3,
elna::boot::binary_operator::less);
}
| expression ">" expression
{
$$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3,
elna::boot::binary_operator::greater);
}
| expression "<=" expression
{
$$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3,
elna::boot::binary_operator::less_equal);
}
| expression ">=" 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 "<<" expression
{
$$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3,
elna::boot::binary_operator::shift_left);
}
| expression ">>" expression
{
$$ = new elna::boot::binary_expression(elna::boot::make_position(@2), $1, $3,
elna::boot::binary_operator::shift_right);
}
unary_expression:
"@" 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);
}
| "-" operand
{
$$ = new elna::boot::unary_expression(elna::boot::make_position(@1), $2,
elna::boot::unary_operator::minus);
}
expressions:
expression "," expressions
{
std::swap($$, $3);
$$.emplace($$.cbegin(), $1);
}
| expression { $$.emplace_back(std::move($1)); }
designator_expression:
operand "[" expression "]"
{
$$ = new elna::boot::array_access_expression(elna::boot::make_position(@2), $1, $3);
}
| operand "." IDENTIFIER
{
$$ = new elna::boot::field_access_expression(elna::boot::make_position(@2), $1, $3);
}
| operand "^"
{
$$ = 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 { $$ = $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($$, $2);
$$.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<elna::boot::array_type>(elna::boot::make_position(@1), $4, $2);
}
| "^" type_expression
{
$$ = std::make_shared<elna::boot::pointer_type>(elna::boot::make_position(@1), $2);
}
| "record" optional_fields "end"
{
$$ = std::make_shared<elna::boot::record_type>(elna::boot::make_position(@1), std::move($2));
}
| "union" required_fields "end"
{
$$ = std::make_shared<elna::boot::union_type>(elna::boot::make_position(@1), std::move($2));
}
| "proc" procedure_heading
{
$$ = $2;
}
| IDENTIFIER
{
$$ = std::make_shared<elna::boot::basic_type>(elna::boot::make_position(@1), $1);
}
variable_declaration: identifier_definitions ":" type_expression
{
for (const std::pair<std::string, bool>& 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 "=" 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);
$$.insert($$.cbegin(), $1);
}
| /* no constant definitions */ {}
constant_part:
{}
| "const" constant_definitions { std::swap($$, $2); }
type_definition: identifier_definition "=" 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);
$$.insert($$.cbegin(), $1);
}
| type_definition { $$.push_back($1); }
type_part:
/* no type definitions */ {}
| "type" {}
| "type" type_definitions { std::swap($$, $2); }
formal_parameter: IDENTIFIER ":" type_expression
{
$$ = new elna::boot::variable_declaration(elna::boot::make_position(@2), $1, $3);
}
formal_parameters:
formal_parameter "," formal_parameters
{
std::swap($$, $3);
$$.emplace($$.cbegin(), $1);
}
| formal_parameter { $$.emplace_back(std::move($1)); }
formal_parameter_list:
"(" ")" {}
| "(" formal_parameters ")" { std::swap($$, $2); }
actual_parameter_list:
"(" ")" {}
| "(" expressions ")" { std::swap($$, $2); }
%%
void yy::parser::error(const location_type& loc, const std::string& message)
{
driver.error(loc, message);
}