preprocessor/src/macro/Parser.yy

446 lines
17 KiB
C++

// -*- C++ -*-
/*
* Copyright © 2019 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 <http://www.gnu.org/licenses/>.
*/
%language "c++"
%require "3.0"
%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 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
%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 BOOL REAL STRING TUPLE ARRAY
%left OR
%left AND
%left EQUAL_EQUAL NOT_EQUAL
%left LESS GREATER LESS_EQUAL GREATER_EQUAL
%nonassoc IN
%left COLON
%left UNION
%left INTERSECTION
%left PLUS MINUS
%left TIMES DIVIDE
%precedence UMINUS UPLUS NOT
%precedence CAST_BOOL CAST_REAL CAST_STRING CAST_TUPLE CAST_ARRAY
%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
%type <EvalPtr> eval
%type <ExpressionPtr> expr
%type <FunctionPtr> function
%type <VariablePtr> symbol
%type <vector<VariablePtr>> comma_name
%type <vector<ExpressionPtr>> comma_expr function_args tuple_comma_expr colon_expr
%%
%start statements;
statements : statement
{
driver.inContext() ? driver.pushContextTop($1) : driver.pushStatements($1);
}
| statements statement
{
driver.inContext() ? driver.pushContextTop($2) : driver.pushStatements($2);
}
;
statement : directive
{ $$ = $1; }
| text
{ $$ = $1; }
| eval
{ $$ = $1; }
;
directive : directive_one_line EOL
{ $$ = $1; }
| directive_multiline EOL
{ $$ = $1; }
;
directive_one_line : INCLUDE expr
{ $$ = make_shared<Include>($2, driver.env, @$); }
| INCLUDEPATH expr
{ $$ = make_shared<IncludePath>($2, driver.env, @$); }
| DEFINE symbol EQUAL expr
{ $$ = make_shared<Define>($2, $4, driver.env, @$); }
| DEFINE function EQUAL expr
{ $$ = make_shared<Define>($2, $4, driver.env, @$); }
| D_ECHO expr
{ $$ = make_shared<Echo>($2, driver.env, @$); }
| ERROR expr
{ $$ = make_shared<Error>($2, driver.env, @$); }
| ECHOMACROVARS
{ $$ = make_shared<EchoMacroVars>(false, driver.env, @$); }
| ECHOMACROVARS LPAREN SAVE RPAREN
{ $$ = make_shared<EchoMacroVars>(true, driver.env, @$); }
;
directive_multiline : for
{ $$ = $1; }
| if
{ $$ = $1; }
| ifdef
{ $$ = $1; }
| ifndef
{ $$ = $1; }
;
for : FOR { driver.pushContext(); } NAME IN expr EOL statements ENDFOR
{
vector<VariablePtr> vvnp = {make_shared<Variable>($3, driver.env, @3)};
auto vdp = driver.popContext();
vdp.emplace_back(make_shared<TextNode>("\n", driver.env, @8));
$$ = make_shared<For>(vvnp, $5, vdp, driver.env, @$);
}
;
for : FOR { driver.pushContext(); } LPAREN comma_name RPAREN IN expr EOL statements ENDFOR
{
vector<VariablePtr> vvnp;
for (auto & it : $4)
{
auto vnp = dynamic_pointer_cast<Variable>(it);
if (!vnp)
error(@$, "For loop indices must be variables");
vvnp.push_back(vnp);
}
auto vdp = driver.popContext();
vdp.emplace_back(make_shared<TextNode>("\n", driver.env, @10));
$$ = make_shared<For>(vvnp, $7, vdp, driver.env, @$);
}
;
comma_name : NAME
{ $$ = vector<VariablePtr>{make_shared<Variable>($1, driver.env, @$)}; }
| comma_name COMMA NAME
{ $1.emplace_back(make_shared<Variable>($3, driver.env, @3)); $$ = $1; }
;
if_begin : IF { driver.pushContext(); }
;
if : if_begin expr EOL statements ENDIF
{
auto ifContext = driver.popContext();
ifContext.emplace_back(make_shared<TextNode>("\n", driver.env, @5));
$$ = make_shared<If>($2, ifContext, driver.env, @$);
}
| if_begin expr EOL statements ELSE EOL { driver.pushContext(); } statements ENDIF
{
auto elseContext = driver.popContext();
elseContext.emplace_back(make_shared<TextNode>("\n", driver.env, @9));
auto ifContext = driver.popContext();
ifContext.emplace_back(make_shared<TextNode>("\n", driver.env, @5));
$$ = make_shared<If>($2, ifContext, elseContext, driver.env, @$);
}
;
ifdef_begin : IFDEF { driver.pushContext(); }
;
ifdef : ifdef_begin expr EOL statements ENDIF
{
auto ifContext = driver.popContext();
ifContext.emplace_back(make_shared<TextNode>("\n", driver.env, @5));
$$ = make_shared<Ifdef>($2, ifContext, driver.env, @$);
}
| ifdef_begin expr EOL statements ELSE EOL { driver.pushContext(); } statements ENDIF
{
auto elseContext = driver.popContext();
elseContext.emplace_back(make_shared<TextNode>("\n", driver.env, @9));
auto ifContext = driver.popContext();
ifContext.emplace_back(make_shared<TextNode>("\n", driver.env, @5));
$$ = make_shared<Ifdef>($2, ifContext, elseContext, driver.env, @$);
}
;
ifndef_begin : IFNDEF { driver.pushContext(); }
;
ifndef : ifndef_begin expr EOL statements ENDIF
{
auto ifContext = driver.popContext();
ifContext.emplace_back(make_shared<TextNode>("\n", driver.env, @5));
$$ = make_shared<Ifndef>($2, ifContext, driver.env, @$);
}
| ifndef_begin expr EOL statements ELSE EOL { driver.pushContext(); } statements ENDIF
{
auto elseContext = driver.popContext();
elseContext.emplace_back(make_shared<TextNode>("\n", driver.env, @9));
auto ifContext = driver.popContext();
ifContext.emplace_back(make_shared<TextNode>("\n", driver.env, @5));
$$ = make_shared<Ifndef>($2, ifContext, elseContext, driver.env, @$);
}
;
text : TEXT
{ $$ = make_shared<TextNode>($1, driver.env, @$); }
| EOL
{ $$ = make_shared<TextNode>($1, driver.env, @$); }
;
eval : BEGIN_EVAL expr END_EVAL
{ $$ = make_shared<Eval>($2, driver.env, @$); }
;
symbol : NAME
{ $$ = make_shared<Variable>($1, driver.env, @$); }
;
function : NAME LPAREN RPAREN
{ $$ = make_shared<Function>($1, vector<ExpressionPtr>(), driver.env, @$); }
| NAME LPAREN function_args RPAREN
{ $$ = make_shared<Function>($1, $3, driver.env, @$); }
;
function_args : symbol
{ $$ = vector<ExpressionPtr>{$1}; }
| function_args COMMA symbol
{ $1.emplace_back($3); $$ = $1; }
;
comma_expr : %empty
{ $$ = vector<ExpressionPtr>{}; } // Empty array
| expr
{ $$ = vector<ExpressionPtr>{$1}; }
| comma_expr COMMA expr
{ $1.emplace_back($3); $$ = $1; }
;
tuple_comma_expr : %empty
{ $$ = vector<ExpressionPtr>{}; } // Empty tuple
| expr COMMA
{ $$ = vector<ExpressionPtr>{$1}; }
| expr COMMA expr
{ $$ = vector<ExpressionPtr>{$1, $3}; }
| tuple_comma_expr COMMA expr
{ $1.emplace_back($3); $$ = $1; }
;
colon_expr : expr COLON expr
{ $$ = vector<ExpressionPtr>{$1, $3}; }
| colon_expr COLON expr
{ $1.emplace_back($3); $$ = $1; }
;
expr : LPAREN expr RPAREN
{ $$ = $2; }
| symbol
{ $$ = $1; }
| NAME LPAREN comma_expr RPAREN
{ $$ = make_shared<Function>($1, $3, driver.env, @$); }
| TRUE
{ $$ = make_shared<Bool>(true, driver.env, @$); }
| FALSE
{ $$ = make_shared<Bool>(false, driver.env, @$); }
| NUMBER
{ $$ = make_shared<Real>($1, driver.env, @$); }
| QUOTED_STRING
{ $$ = make_shared<String>($1, driver.env, @$); }
| colon_expr
{
if ($1.size() == 2)
$$ = make_shared<Array>($1[0], $1[1], driver.env, @$);
else if ($1.size() == 3)
$$ = make_shared<Array>($1[0], $1[1], $1[2], driver.env, @$);
else
error(@$, "The colon operator only works with 2 or 3 arguments");
}
| LBRACKET comma_expr RBRACKET
{ $$ = make_shared<Array>($2, driver.env, @$); }
| symbol LBRACKET comma_expr RBRACKET
{ $1->addIndexing($3); $$ = $1; }
| LPAREN tuple_comma_expr RPAREN
{ $$ = make_shared<Tuple>($2, driver.env, @$); }
| LBRACKET expr IN expr WHEN expr RBRACKET
{ $$ = make_shared<Comprehension>(true, $2, $4, $6, driver.env, @$); }
| LBRACKET expr FOR expr IN expr RBRACKET
{ $$ = make_shared<Comprehension>($2, $4, $6, driver.env, @$); }
| LBRACKET expr FOR expr IN expr WHEN expr RBRACKET
{ $$ = make_shared<Comprehension>($2, $4, $6, $8, driver.env, @$); }
| LPAREN BOOL RPAREN expr %prec CAST_BOOL
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::cast_bool, $4, driver.env, @$); }
| LPAREN REAL RPAREN expr %prec CAST_REAL
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::cast_real, $4, driver.env, @$); }
| LPAREN STRING RPAREN expr %prec CAST_STRING
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::cast_string, $4, driver.env, @$); }
| LPAREN TUPLE RPAREN expr %prec CAST_TUPLE
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::cast_tuple, $4, driver.env, @$); }
| LPAREN ARRAY RPAREN expr %prec CAST_ARRAY
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::cast_array, $4, driver.env, @$); }
| NOT expr
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::logical_not, $2, driver.env, @$); }
| MINUS expr %prec UMINUS
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::unary_minus, $2, driver.env, @$); }
| PLUS expr %prec UPLUS
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::unary_plus, $2, driver.env, @$); }
| LENGTH LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::length, $3, driver.env, @$); }
| ISEMPTY LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::isempty, $3, driver.env, @$); }
| EXP LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::exp, $3, driver.env, @$); }
| LOG LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::ln, $3, driver.env, @$); }
| LN LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::ln, $3, driver.env, @$); }
| LOG10 LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::log10, $3, driver.env, @$); }
| SIN LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::sin, $3, driver.env, @$); }
| COS LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::cos, $3, driver.env, @$); }
| TAN LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::tan, $3, driver.env, @$); }
| ASIN LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::asin, $3, driver.env, @$); }
| ACOS LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::acos, $3, driver.env, @$); }
| ATAN LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::atan, $3, driver.env, @$); }
| SQRT LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::sqrt, $3, driver.env, @$); }
| CBRT LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::cbrt, $3, driver.env, @$); }
| SIGN LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::sign, $3, driver.env, @$); }
| FLOOR LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::floor, $3, driver.env, @$); }
| CEIL LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::ceil, $3, driver.env, @$); }
| TRUNC LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::trunc, $3, driver.env, @$); }
| SUM LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::sum, $3, driver.env, @$); }
| ERF LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::erf, $3, driver.env, @$); }
| ERFC LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::erfc, $3, driver.env, @$); }
| GAMMA LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::gamma, $3, driver.env, @$); }
| LGAMMA LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::lgamma, $3, driver.env, @$); }
| ROUND LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::round, $3, driver.env, @$); }
| NORMPDF LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::normpdf, $3, driver.env, @$); }
| NORMCDF LPAREN expr RPAREN
{ $$ = make_shared<UnaryOp>(codes::UnaryOp::normcdf, $3, driver.env, @$); }
| expr PLUS expr
{ $$ = make_shared<BinaryOp>(codes::BinaryOp::plus, $1, $3, driver.env, @$); }
| expr MINUS expr
{ $$ = make_shared<BinaryOp>(codes::BinaryOp::minus, $1, $3, driver.env, @$); }
| expr TIMES expr
{ $$ = make_shared<BinaryOp>(codes::BinaryOp::times, $1, $3, driver.env, @$); }
| expr DIVIDE expr
{ $$ = make_shared<BinaryOp>(codes::BinaryOp::divide, $1, $3, driver.env, @$); }
| expr POWER expr
{ $$ = make_shared<BinaryOp>(codes::BinaryOp::power, $1, $3, driver.env, @$); }
| expr EQUAL_EQUAL expr
{ $$ = make_shared<BinaryOp>(codes::BinaryOp::equal_equal, $1, $3, driver.env, @$); }
| expr NOT_EQUAL expr
{ $$ = make_shared<BinaryOp>(codes::BinaryOp::not_equal, $1, $3, driver.env, @$); }
| expr LESS expr
{ $$ = make_shared<BinaryOp>(codes::BinaryOp::less, $1, $3, driver.env, @$); }
| expr GREATER expr
{ $$ = make_shared<BinaryOp>(codes::BinaryOp::greater, $1, $3, driver.env, @$); }
| expr LESS_EQUAL expr
{ $$ = make_shared<BinaryOp>(codes::BinaryOp::less_equal, $1, $3, driver.env, @$); }
| expr GREATER_EQUAL expr
{ $$ = make_shared<BinaryOp>(codes::BinaryOp::greater_equal, $1, $3, driver.env, @$); }
| expr AND expr
{ $$ = make_shared<BinaryOp>(codes::BinaryOp::logical_and, $1, $3, driver.env, @$); }
| expr OR expr
{ $$ = make_shared<BinaryOp>(codes::BinaryOp::logical_or, $1, $3, driver.env, @$); }
| expr IN expr
{ $$ = make_shared<BinaryOp>(codes::BinaryOp::in, $1, $3, driver.env, @$); }
| expr UNION expr
{ $$ = make_shared<BinaryOp>(codes::BinaryOp::set_union, $1, $3, driver.env, @$); }
| expr INTERSECTION expr
{ $$ = make_shared<BinaryOp>(codes::BinaryOp::set_intersection, $1, $3, driver.env, @$); }
| MAX LPAREN expr COMMA expr RPAREN
{ $$ = make_shared<BinaryOp>(codes::BinaryOp::max, $3, $5, driver.env, @$); }
| MIN LPAREN expr COMMA expr RPAREN
{ $$ = make_shared<BinaryOp>(codes::BinaryOp::min, $3, $5, driver.env, @$); }
| MOD LPAREN expr COMMA expr RPAREN
{ $$ = make_shared<BinaryOp>(codes::BinaryOp::mod, $3, $5, driver.env, @$); }
| NORMPDF LPAREN expr COMMA expr COMMA expr RPAREN
{ $$ = make_shared<TrinaryOp>(codes::TrinaryOp::normpdf, $3, $5, $7, driver.env, @$); }
| NORMCDF LPAREN expr COMMA expr COMMA expr RPAREN
{ $$ = make_shared<TrinaryOp>(codes::TrinaryOp::normcdf, $3, $5, $7, driver.env, @$); }
;
%%
void
Tokenizer::parser::error(const Tokenizer::parser::location_type &l, const string &m)
{
driver.error(l, m);
}