preprocessor/src/macro/Parser.yy

500 lines
18 KiB
C++

// -*- C++ -*-
/*
* Copyright © 2019-2020 Dynare Team
*
* This file is part of Dynare.
*
* Dynare 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 of the License, or
* (at your option) any later version.
*
* Dynare 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 Dynare. If not, see <https://www.gnu.org/licenses/>.
*/
%language "c++"
%require "3.2"
%defines
%define api.value.type variant
%define api.namespace {Tokenizer}
%define parse.assert
%define parse.error verbose
%define parse.trace
%code requires {
namespace macro { class Driver; }
}
%param { macro::Driver &driver }
%locations
%initial-action
{
// Initialize the location filenames
@$.begin.filename = @$.end.filename = &driver.file;
};
%code requires {
#include "Directives.hh"
using namespace macro;
}
%code {
#include "Driver.hh"
/* this "connects" the bison parser in the driver to the flex scanner class
* object. it defines the yylex() function call to pull the next token from the
* current lexer object of the driver context. */
#undef yylex
#define yylex driver.lexer->lex
}
%token FOR ENDFOR IF IFDEF IFNDEF ELSEIF ELSE ENDIF TRUE FALSE
%token INCLUDE INCLUDEPATH DEFINE EQUAL D_ECHO ERROR
%token COMMA LPAREN RPAREN LBRACKET RBRACKET WHEN
%token BEGIN_EVAL END_EVAL ECHOMACROVARS SAVE LINE
%token EXP LOG LN LOG10 SIN COS TAN ASIN ACOS ATAN
%token SQRT CBRT SIGN MAX MIN FLOOR CEIL TRUNC SUM MOD
%token ERF ERFC GAMMA LGAMMA ROUND NORMPDF NORMCDF LENGTH
%token ISEMPTY
%token ISBOOLEAN ISREAL ISSTRING ISTUPLE ISARRAY
%token BOOL REAL STRING TUPLE ARRAY
%token DEFINED
%left OR
%left AND
%left EQUAL_EQUAL NOT_EQUAL
%left LESS GREATER LESS_EQUAL GREATER_EQUAL
%nonassoc IN
/* The COLON operator cannot be given a precedence, because it has both a
binary and a ternary forms. But technically it belongs here given how the
grammar rules are organized */
%token COLON
%left UNION
%left INTERSECTION
%left PLUS MINUS
%left TIMES DIVIDE
%precedence UNARY NOT
%precedence CAST
%nonassoc POWER
%token <string> NAME TEXT QUOTED_STRING NUMBER EOL
%type <DirectivePtr> statement
%type <DirectivePtr> directive directive_one_line directive_multiline for if ifdef ifndef text eval
%type <vector<pair<ExpressionPtr, vector<DirectivePtr>>>> if_list if_list1
%type <pair<ExpressionPtr, vector<DirectivePtr>>> elseif else
%type <ExpressionPtr> primary_expr oper_expr colon_expr expr for_when
%type <FunctionPtr> function
%type <VariablePtr> symbol
%type <vector<ExpressionPtr>> comma_expr function_args tuple_comma_expr
%type <vector<string>> name_list
%%
%start statements;
statements : statement
{
driver.inContext() ? driver.pushContextTop($1) : driver.pushStatements($1);
}
| statements statement
{
driver.inContext() ? driver.pushContextTop($2) : driver.pushStatements($2);
}
;
statement : directive
| text
| eval
;
directive : directive_one_line EOL
| directive_multiline EOL
;
directive_one_line : INCLUDE expr
{ $$ = make_shared<Include>($2, @$); }
| INCLUDEPATH expr
{ $$ = make_shared<IncludePath>($2, @$); }
| DEFINE symbol
{
auto tmp = make_shared<Real>("1", @$);
$$ = make_shared<Define>($2, tmp, @$);
}
| DEFINE symbol EQUAL expr
{ $$ = make_shared<Define>($2, $4, @$); }
| DEFINE function EQUAL expr
{ $$ = make_shared<Define>($2, $4, @$); }
| D_ECHO expr
{ $$ = make_shared<Echo>($2, @$); }
| ERROR expr
{ $$ = make_shared<Error>($2, @$); }
| ECHOMACROVARS
{ $$ = make_shared<EchoMacroVars>(false, @$); }
| ECHOMACROVARS name_list
{ $$ = make_shared<EchoMacroVars>(false, $2, @$); }
| ECHOMACROVARS LPAREN SAVE RPAREN
{ $$ = make_shared<EchoMacroVars>(true, @$); }
| ECHOMACROVARS LPAREN SAVE RPAREN name_list
{ $$ = make_shared<EchoMacroVars>(true, $5, @$); }
| LINE QUOTED_STRING NUMBER
{
// `@#line` is ignored; adjust newlines in output to accord
auto l = static_cast<Tokenizer::parser::location_type>(@$);
$$ = make_shared<TextNode>(string(l.end.line - l.begin.line + 1, '\n'), @$);
}
;
name_list : NAME
{ $$ = {$1}; }
| name_list NAME
{
$1.emplace_back($2);
$$ = $1;
}
;
directive_multiline : for
| if
| ifdef
| ifndef
;
for_when : %empty
{ $$ = shared_ptr<Expression>(); }
| WHEN expr
{ $$ = $2; }
;
for : FOR { driver.pushContext(); } expr IN expr for_when EOL statements ENDFOR
{
vector<VariablePtr> vvnp;
auto tmpt = dynamic_pointer_cast<Tuple>($3);
auto tmpv = dynamic_pointer_cast<Variable>($3);
if (tmpv)
vvnp.emplace_back(tmpv);
else if (tmpt)
for (const auto & it : tmpt->getValue())
{
auto vnp = dynamic_pointer_cast<Variable>(it);
if (!vnp)
error(@$, "For loop indices must be variables");
vvnp.emplace_back(vnp);
}
else
error(@1, "For loop indices must be a variable or a tuple");
auto vdp = driver.popContext();
vdp.emplace_back(make_shared<TextNode>("\n", @9));
if (!$6)
$$ = make_shared<For>(vvnp, $5, vdp, @$);
else
{
auto tmpc = make_shared<Comprehension>(true, $3, $5, $6, @6);
$$ = make_shared<For>(vvnp, tmpc, vdp, @$);
}
}
;
if : IF { driver.pushContext(); } if_list ENDIF
{ $$ = make_shared<If>($3, @$); }
;
ifdef : IFDEF { driver.pushContext(); } if_list ENDIF
{ $$ = make_shared<Ifdef>($3, @$); }
;
ifndef : IFNDEF { driver.pushContext(); } if_list ENDIF
{ $$ = make_shared<Ifndef>($3, @$); }
;
if_list : if_list1
| if_list1 else
{
$1.emplace_back($2);
$$ = $1;
}
;
if_list1 : expr EOL
{
auto context = driver.popContext();
context.emplace_back(make_shared<TextNode>("\n", @2));
$$ = {{$1, context}};
}
| expr EOL statements
{
auto context = driver.popContext();
context.emplace_back(make_shared<TextNode>("\n", @3));
$$ = {{$1, context}};
}
| if_list1 elseif
{
$1.emplace_back($2);
$$ = $1;
}
;
elseif_begin : ELSEIF { driver.pushContext(); } ;
elseif : elseif_begin expr EOL
{
auto context = driver.popContext();
context.emplace_back(make_shared<TextNode>("\n", @3));
$$ = {$2, context};
}
| elseif_begin expr EOL statements
{
auto context = driver.popContext();
context.emplace_back(make_shared<TextNode>("\n", @4));
$$ = {$2, context};
}
;
else_begin : ELSE { driver.pushContext(); } ;
else : else_begin EOL
{
auto context = driver.popContext();
context.emplace_back(make_shared<TextNode>("\n", @2));
$$ = {make_shared<Bool>(true, @1), context};
}
| else_begin EOL statements
{
auto context = driver.popContext();
context.emplace_back(make_shared<TextNode>("\n", @3));
$$ = {make_shared<Bool>(true, @1), context};
}
;
text : TEXT
{ $$ = make_shared<TextNode>($1, @$); }
| EOL
{ $$ = make_shared<TextNode>($1, @$); }
;
eval : BEGIN_EVAL expr END_EVAL
{ $$ = make_shared<Eval>($2, @$); }
;
symbol : NAME
{ $$ = make_shared<Variable>($1, @$); }
;
function : NAME LPAREN RPAREN
{ $$ = make_shared<Function>($1, vector<ExpressionPtr>(), @$); }
| NAME LPAREN function_args RPAREN
{ $$ = make_shared<Function>($1, $3, @$); }
;
function_args : symbol
{ $$ = {$1}; }
| function_args COMMA symbol
{ $1.emplace_back($3); $$ = $1; }
;
comma_expr : %empty
{ $$ = {}; }
| expr
{ $$ = {$1}; }
| comma_expr COMMA expr
{ $1.emplace_back($3); $$ = $1; }
;
tuple_comma_expr : %empty
{ $$ = {}; }
| expr COMMA
{ $$ = {$1}; }
| expr COMMA expr
{ $$ = {$1, $3}; }
| tuple_comma_expr COMMA expr
{ $1.emplace_back($3); $$ = $1; }
;
primary_expr : LPAREN expr RPAREN
{ $$ = $2; }
| symbol
{ $$ = $1; } // Explicit rule needed for type conversion
| NAME LBRACKET comma_expr RBRACKET
{
$$ = make_shared<Variable>($1, make_shared<Array>($3, @3), @$);
}
| NAME LPAREN comma_expr RPAREN
{ $$ = make_shared<Function>($1, $3, @$); }
| TRUE
{ $$ = make_shared<Bool>(true, @$); }
| FALSE
{ $$ = make_shared<Bool>(false, @$); }
| NUMBER
{ $$ = make_shared<Real>($1, @$); }
| QUOTED_STRING
{ $$ = make_shared<String>($1, @$); }
| LBRACKET comma_expr RBRACKET
{ $$ = make_shared<Array>($2, @$); }
| LPAREN tuple_comma_expr RPAREN
{ $$ = make_shared<Tuple>($2, @$); }
| LBRACKET expr IN expr WHEN expr RBRACKET
{ $$ = make_shared<Comprehension>(true, $2, $4, $6, @$); }
| LBRACKET expr FOR expr IN expr RBRACKET
{ $$ = make_shared<Comprehension>($2, $4, $6, @$); }
| LBRACKET expr FOR expr IN expr WHEN expr RBRACKET
{ $$ = make_shared<Comprehension>($2, $4, $6, $8, @$); }
| LENGTH LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::length, $3, @$); }
| ISEMPTY LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::isempty, $3, @$); }
| ISBOOLEAN LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::isboolean, $3, @$); }
| ISREAL LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::isreal, $3, @$); }
| ISSTRING LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::isstring, $3, @$); }
| ISTUPLE LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::istuple, $3, @$); }
| ISARRAY LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::isarray, $3, @$); }
| EXP LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::exp, $3, @$); }
| LOG LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::ln, $3, @$); }
| LN LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::ln, $3, @$); }
| LOG10 LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::log10, $3, @$); }
| SIN LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::sin, $3, @$); }
| COS LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::cos, $3, @$); }
| TAN LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::tan, $3, @$); }
| ASIN LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::asin, $3, @$); }
| ACOS LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::acos, $3, @$); }
| ATAN LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::atan, $3, @$); }
| SQRT LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::sqrt, $3, @$); }
| CBRT LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::cbrt, $3, @$); }
| SIGN LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::sign, $3, @$); }
| FLOOR LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::floor, $3, @$); }
| CEIL LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::ceil, $3, @$); }
| TRUNC LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::trunc, $3, @$); }
| SUM LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::sum, $3, @$); }
| ERF LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::erf, $3, @$); }
| ERFC LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::erfc, $3, @$); }
| GAMMA LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::gamma, $3, @$); }
| LGAMMA LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::lgamma, $3, @$); }
| ROUND LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::round, $3, @$); }
| NORMPDF LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::normpdf, $3, @$); }
| NORMCDF LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::normcdf, $3, @$); }
| MAX LPAREN expr COMMA expr RPAREN
{ $$ = make_shared<BinaryOp>(codes::BinaryOp::max, $3, $5, @$); }
| MIN LPAREN expr COMMA expr RPAREN
{ $$ = make_shared<BinaryOp>(codes::BinaryOp::min, $3, $5, @$); }
| MOD LPAREN expr COMMA expr RPAREN
{ $$ = make_shared<BinaryOp>(codes::BinaryOp::mod, $3, $5, @$); }
| NORMPDF LPAREN expr COMMA expr COMMA expr RPAREN
{ $$ = make_shared<TrinaryOp>(codes::TrinaryOp::normpdf, $3, $5, $7, @$); }
| NORMCDF LPAREN expr COMMA expr COMMA expr RPAREN
{ $$ = make_shared<TrinaryOp>(codes::TrinaryOp::normcdf, $3, $5, $7, @$); }
| DEFINED LPAREN NAME RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::defined, make_shared<String>($3, @3), @$); }
;
oper_expr : primary_expr
| LPAREN BOOL RPAREN oper_expr %prec CAST
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::cast_bool, $4, @$); }
| LPAREN REAL RPAREN oper_expr %prec CAST
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::cast_real, $4, @$); }
| LPAREN STRING RPAREN oper_expr %prec CAST
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::cast_string, $4, @$); }
| LPAREN TUPLE RPAREN oper_expr %prec CAST
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::cast_tuple, $4, @$); }
| LPAREN ARRAY RPAREN oper_expr %prec CAST
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::cast_array, $4, @$); }
| NOT oper_expr
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::logical_not, $2, @$); }
| MINUS oper_expr %prec UNARY
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::unary_minus, $2, @$); }
| PLUS oper_expr %prec UNARY
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::unary_plus, $2, @$); }
| oper_expr PLUS oper_expr
{ $$ = make_shared<BinaryOp>(codes::BinaryOp::plus, $1, $3, @$); }
| oper_expr MINUS oper_expr
{ $$ = make_shared<BinaryOp>(codes::BinaryOp::minus, $1, $3, @$); }
| oper_expr TIMES oper_expr
{ $$ = make_shared<BinaryOp>(codes::BinaryOp::times, $1, $3, @$); }
| oper_expr DIVIDE oper_expr
{ $$ = make_shared<BinaryOp>(codes::BinaryOp::divide, $1, $3, @$); }
| oper_expr POWER oper_expr
{ $$ = make_shared<BinaryOp>(codes::BinaryOp::power, $1, $3, @$); }
| oper_expr UNION oper_expr
{ $$ = make_shared<BinaryOp>(codes::BinaryOp::set_union, $1, $3, @$); }
| oper_expr INTERSECTION oper_expr
{ $$ = make_shared<BinaryOp>(codes::BinaryOp::set_intersection, $1, $3, @$); }
;
colon_expr : oper_expr COLON oper_expr
{ $$ = make_shared<Range>($1, $3, @$); }
| oper_expr COLON oper_expr COLON oper_expr
{ $$ = make_shared<Range>($1, $3, $5, @$); }
;
expr : oper_expr
| colon_expr
| expr EQUAL_EQUAL expr
{ $$ = make_shared<BinaryOp>(codes::BinaryOp::equal_equal, $1, $3, @$); }
| expr NOT_EQUAL expr
{ $$ = make_shared<BinaryOp>(codes::BinaryOp::not_equal, $1, $3, @$); }
| expr LESS expr
{ $$ = make_shared<BinaryOp>(codes::BinaryOp::less, $1, $3, @$); }
| expr GREATER expr
{ $$ = make_shared<BinaryOp>(codes::BinaryOp::greater, $1, $3, @$); }
| expr LESS_EQUAL expr
{ $$ = make_shared<BinaryOp>(codes::BinaryOp::less_equal, $1, $3, @$); }
| expr GREATER_EQUAL expr
{ $$ = make_shared<BinaryOp>(codes::BinaryOp::greater_equal, $1, $3, @$); }
| expr AND expr
{ $$ = make_shared<BinaryOp>(codes::BinaryOp::logical_and, $1, $3, @$); }
| expr OR expr
{ $$ = make_shared<BinaryOp>(codes::BinaryOp::logical_or, $1, $3, @$); }
| expr IN expr
{ $$ = make_shared<BinaryOp>(codes::BinaryOp::in, $1, $3, @$); }
;
%%
void
Tokenizer::parser::error(const Tokenizer::parser::location_type &l, const string &m)
{
driver.error(l, m);
}