%require "3.2" %language "c++" %code requires { #include #include "elna/source/parser.hpp" #if ! defined(yyFlexLexerOnce) #include #endif namespace elna::syntax { class FooLexer; } } %code provides { namespace elna::syntax { class FooLexer : public yyFlexLexer { public: yy::location location; FooLexer(std::istream& arg_yyin) : yyFlexLexer(&arg_yyin) { } yy::parser::symbol_type lex(); }; } } %define api.token.raw %define api.token.constructor %define api.value.type variant %define parse.assert %parse-param {elna::syntax::FooLexer& lexer} %parse-param {std::unique_ptr& program} %locations %header %code { #define yylex lexer.lex } %start program; %token IDENTIFIER "identifier" %token NUMBER "number" %token BOOLEAN %token IF THEN WHILE DO %token CONST VAR PROCEDURE %token BEGIN_BLOCK END_BLOCK %token TRUE FALSE %token LEFT_PAREN RIGHT_PAREN SEMICOLON DOT COMMA %token GREATER_EQUAL LESS_EQUAL LESS_THAN GREATER_THAN NOT_EQUAL EQUALS %token PLUS MINUS MULTIPLICATION DIVISION %token ASSIGNMENT COLON HAT AT %type > integer_literal; %type > boolean_literal; %type > variable_expression; %type > constant_definition; %type > reference_expression dereference_expression; %type >> constant_definition_part constant_definitions; %type > type_expression; %type > variable_declaration; %type >> variable_declaration_part variable_declarations; %type > assign_statement; %type > expression factor term comparand; %type > statement; %type >> arguments; %type > call_statement; %type > compound_statement; %type >> statements; %type > if_statement; %type > while_statement; %type > procedure_definition; %type >> procedure_definition_part procedure_definitions; %% program: constant_definition_part variable_declaration_part procedure_definition_part statement DOT { elna::source::position position; std::vector> definitions($1.size()); std::vector> declarations($2.size()); std::vector>::iterator definition = definitions.begin(); std::vector>::iterator declaration = declarations.begin(); for (auto& constant : $1) { *definition++ = std::move(constant); } for (auto& variable : $2) { *declaration++ = std::move(variable); } program = std::make_unique(position, std::move(definitions), std::move(declarations), std::move($4)); } procedure_definition: PROCEDURE IDENTIFIER SEMICOLON constant_definition_part variable_declaration_part statement SEMICOLON { elna::source::position position; std::vector> definitions($4.size()); std::vector> declarations($5.size()); std::vector>::iterator definition = definitions.begin(); std::vector>::iterator declaration = declarations.begin(); for (auto& constant : $4) { *definition++ = std::move(constant); } for (auto& variable : $5) { *declaration++ = std::move(variable); } auto block = std::make_unique(position, std::move(definitions), std::move(declarations), std::move($6)); $$ = std::make_unique(position, $2, std::move(block)); } procedure_definition_part: /* no procedure definitions. */ {} | procedure_definitions { std::swap($1, $$); } procedure_definitions: procedure_definition procedure_definitions { std::swap($$, $2); $$.emplace($$.cbegin(), std::move($1)); } | procedure_definition { $$.emplace_back(std::move($1)); } integer_literal: NUMBER { elna::source::position position{ static_cast(@1.begin.line), static_cast(@1.begin.column) }; $$ = std::make_unique(position, $1); } boolean_literal: BOOLEAN { elna::source::position position{ static_cast(@1.begin.line), static_cast(@1.begin.column) }; $$ = std::make_unique(position, $1); } variable_expression: IDENTIFIER { elna::source::position position{ static_cast(@1.begin.line), static_cast(@1.begin.column) }; $$ = std::make_unique(position, $1); } reference_expression: AT variable_expression { elna::source::position position{ static_cast(@1.begin.line), static_cast(@1.begin.column) }; $$ = std::make_unique(position, std::move($2), '@'); } dereference_expression: factor HAT { elna::source::position position{ static_cast(@1.begin.line), static_cast(@1.begin.column) }; $$ = std::make_unique(position, std::move($1), '^'); } expression: comparand EQUALS comparand { elna::source::position position{ static_cast(@1.begin.line), static_cast(@1.begin.column) }; $$ = std::make_unique(position, std::move($1), std::move($3), '='); } | comparand NOT_EQUAL comparand { elna::source::position position{ static_cast(@1.begin.line), static_cast(@1.begin.column) }; $$ = std::make_unique(position, std::move($1), std::move($3), 'n'); } | comparand GREATER_THAN comparand { elna::source::position position{ static_cast(@1.begin.line), static_cast(@1.begin.column) }; $$ = std::make_unique(position, std::move($1), std::move($3), '>'); } | comparand LESS_THAN comparand { elna::source::position position{ static_cast(@1.begin.line), static_cast(@1.begin.column) }; $$ = std::make_unique(position, std::move($1), std::move($3), '<'); } | comparand GREATER_EQUAL comparand { elna::source::position position{ static_cast(@1.begin.line), static_cast(@1.begin.column) }; $$ = std::make_unique(position, std::move($1), std::move($3), 'g'); } | comparand LESS_EQUAL comparand { elna::source::position position{ static_cast(@1.begin.line), static_cast(@1.begin.column) }; $$ = std::make_unique(position, std::move($1), std::move($3), 'l'); } comparand: term PLUS term { elna::source::position position{ static_cast(@1.begin.line), static_cast(@1.begin.column) }; $$ = std::make_unique(position, std::move($1), std::move($3), '+'); } | term MINUS term { elna::source::position position{ static_cast(@1.begin.line), static_cast(@1.begin.column) }; $$ = std::make_unique(position, std::move($1), std::move($3), '-'); } | term { $$ = std::move($1); } factor: integer_literal { $$ = std::move($1); } | boolean_literal { $$ = std::move($1); } | variable_expression { $$ = std::move($1); } | reference_expression { $$ = std::move($1); } | dereference_expression { $$ = std::move($1); } | LEFT_PAREN expression RIGHT_PAREN { $$ = std::move($2); } term: factor MULTIPLICATION factor { elna::source::position position{ static_cast(@1.begin.line), static_cast(@1.begin.column) }; $$ = std::make_unique(position, std::move($1), std::move($3), '*'); } | factor DIVISION factor { elna::source::position position{ static_cast(@1.begin.line), static_cast(@1.begin.column) }; $$ = std::make_unique(position, std::move($1), std::move($3), '/'); } | factor { $$ = std::move($1); } type_expression: HAT IDENTIFIER { elna::source::position position{ static_cast(@1.begin.line), static_cast(@1.begin.column) }; $$ = std::make_unique(position, $2, true); } | IDENTIFIER { elna::source::position position{ static_cast(@1.begin.line), static_cast(@1.begin.column) }; $$ = std::make_unique(position, $1, false); } assign_statement: IDENTIFIER ASSIGNMENT expression { elna::source::position position{ static_cast(@1.begin.line), static_cast(@2.begin.column) }; $$ = std::make_unique(position, $1, std::move($3)); } call_statement: IDENTIFIER LEFT_PAREN arguments RIGHT_PAREN { elna::source::position position{ static_cast(@1.begin.line), static_cast(@1.begin.column) }; $$ = std::make_unique(position, $1); std::swap($3, $$->arguments()); } compound_statement: BEGIN_BLOCK statements END_BLOCK { elna::source::position position{ static_cast(@1.begin.line), static_cast(@1.begin.column) }; $$ = std::make_unique(position); std::swap($2, $$->statements()); } if_statement: IF expression THEN statement { elna::source::position position{ static_cast(@1.begin.line), static_cast(@1.begin.column) }; $$ = std::make_unique(position, std::move($2), std::move($4)); } while_statement: WHILE expression DO statement { elna::source::position position{ static_cast(@1.begin.line), static_cast(@1.begin.column) }; $$ = std::make_unique(position, std::move($2), std::move($4)); } statement: assign_statement { $$ = std::move($1); } | call_statement { $$ = std::move($1); } | compound_statement { $$ = std::move($1); } | if_statement { $$ = std::move($1); } | while_statement { $$ = std::move($1); } variable_declaration: IDENTIFIER COLON type_expression { elna::source::position position{ static_cast(@1.begin.line), static_cast(@1.begin.column) }; $$ = std::make_unique(position, $1, std::move($3)); } variable_declarations: variable_declaration COMMA variable_declarations { std::swap($$, $3); $$.emplace($$.cbegin(), std::move($1)); } | variable_declaration { $$.emplace_back(std::move($1)); } constant_definition: IDENTIFIER EQUALS integer_literal { elna::source::position position{ static_cast(@1.begin.line), static_cast(@1.begin.column) }; $$ = std::make_unique(position, $1, std::move($3)); }; constant_definitions: constant_definition COMMA constant_definitions { std::swap($$, $3); $$.emplace($$.cbegin(), std::move($1)); } | constant_definition { $$.emplace_back(std::move($1)); } constant_definition_part: /* no constant definitions */ {} | CONST constant_definitions SEMICOLON { std::swap($$, $2); }; variable_declaration_part: /* no constant declarations */ {} | VAR variable_declarations SEMICOLON { std::swap($$, $2); }; arguments: /* no arguments */ {} | expression COMMA arguments { std::swap($$, $3); $$.emplace($$.cbegin(), std::move($1)); } | expression { $$.emplace_back(std::move($1)); } statements: /* no statements */ {} | statement SEMICOLON statements { std::swap($$, $3); $$.emplace($$.cbegin(), std::move($1)); } | statement { $$.emplace_back(std::move($1)); } %% void yy::parser::error(const location_type& loc, const std::string &message) { throw yy::parser::syntax_error(loc, message); }