/* * Copyright (C) 2008-2018 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 . */ %language "c++" %require "3.0" %defines %define api.value.type variant %define parse.assert %code top { class MacroDriver; } %name-prefix "Macro" %parse-param { MacroDriver &driver } %parse-param { ostream &out } %lex-param { MacroDriver &driver } %locations %initial-action { // Initialize the location filenames @$.begin.filename = @$.end.filename = &driver.file; }; %debug %error-verbose %code requires { #include "MacroValue.hh" } %code { #include "MacroDriver.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 #define TYPERR_CATCH(statement, loc) try \ { \ statement; \ } \ catch(MacroValue::TypeError &e) \ { \ driver.error(loc, e.message); \ } } %token DEFINE LINE FOR IN IF ELSE ENDIF ECHO_DIR ERROR IFDEF IFNDEF %token LPAREN RPAREN LBRACKET RBRACKET EQUAL EOL LENGTH ECHOMACROVARS SAVE %token INTEGER %token NAME STRING %left COMMA %left LOGICAL_OR %left LOGICAL_AND %left LESS GREATER LESS_EQUAL GREATER_EQUAL EQUAL_EQUAL EXCLAMATION_EQUAL %nonassoc IN %nonassoc COLON %left PLUS MINUS %left TIMES DIVIDE %left UMINUS UPLUS EXCLAMATION %left LBRACKET %type > func_args %type expr %type > comma_expr %% %start statement_list_or_nothing; statement_list_or_nothing : /* empty */ | statement_list ; statement_list : statement EOL | statement_list statement EOL ; statement : expr { out << $1->toString(); } | DEFINE NAME EQUAL expr { driver.set_variable($2, $4); } | FOR NAME IN expr { TYPERR_CATCH(driver.init_loop($2, $4), @$); } | IF expr { TYPERR_CATCH(driver.begin_if($2), @$); } | IFDEF NAME { TYPERR_CATCH(driver.begin_ifdef($2), @$); } | IFNDEF NAME { TYPERR_CATCH(driver.begin_ifndef($2), @$); } | ECHO_DIR expr { TYPERR_CATCH(driver.echo(@$, $2), @$); } | ERROR expr { TYPERR_CATCH(driver.error(@$, $2), @$); } | LINE STRING INTEGER /* Ignore @#line declarations */ | ECHOMACROVARS { driver.printvars(@$, true); } | ECHOMACROVARS LPAREN SAVE RPAREN { out << driver.printvars(@$, false); } | DEFINE NAME LPAREN func_args { driver.push_args_into_func_env($4); } RPAREN EQUAL expr { TYPERR_CATCH(driver.set_string_function($2, $4, $8), @$); driver.pop_func_env(); } ; func_args : NAME { $$ = vector{$1}; } | func_args COMMA NAME { $1.push_back($3); $$ = $1; } ; expr : INTEGER { $$ = make_shared($1); } | STRING { $$ = make_shared(driver.replace_vars_in_str($1)); } | NAME { try { $$ = driver.get_variable($1); } catch(MacroDriver::UnknownVariable(&e)) { error(@$, "Unknown variable: " + e.name); } } | NAME LPAREN comma_expr RPAREN { TYPERR_CATCH($$ = driver.eval_string_function($1, $3), @$); } | LENGTH LPAREN expr RPAREN { TYPERR_CATCH($$ = $3->length(), @$); } | LPAREN expr RPAREN { $$ = $2; } | expr PLUS expr { TYPERR_CATCH($$ = $1->plus($3), @$); } | expr MINUS expr { TYPERR_CATCH($$ = $1->minus($3), @$); } | expr TIMES expr { TYPERR_CATCH($$ = $1->times($3), @$); } | expr DIVIDE expr { TYPERR_CATCH($$ = $1->divide($3), @$) catch(MacroValue::DivisionByZeroError) { error(@$, "Division by zero"); } } | expr LESS expr { TYPERR_CATCH($$ = $1->is_less($3), @$); } | expr GREATER expr { TYPERR_CATCH($$ = $1->is_greater($3), @$); } | expr LESS_EQUAL expr { TYPERR_CATCH($$ = $1->is_less_equal($3), @$); } | expr GREATER_EQUAL expr { TYPERR_CATCH($$ = $1->is_greater_equal($3), @$); } | expr EQUAL_EQUAL expr { $$ = $1->is_equal($3); } | expr EXCLAMATION_EQUAL expr { $$ = $1->is_different($3); } | expr LOGICAL_OR expr { TYPERR_CATCH($$ = $1->logical_or($3), @$); } | expr LOGICAL_AND expr { TYPERR_CATCH($$ = $1->logical_and($3), @$); } | MINUS expr %prec UMINUS { TYPERR_CATCH($$ = $2->unary_minus(), @$); } | PLUS expr %prec UPLUS { TYPERR_CATCH($$ = $2->unary_plus(), @$); } | EXCLAMATION expr { TYPERR_CATCH($$ = $2->logical_not(), @$); } | expr LBRACKET expr RBRACKET { TYPERR_CATCH($$ = $1->subscript($3), @$) catch(MacroValue::OutOfBoundsError) { error(@$, "Index out of bounds"); } } | LBRACKET comma_expr RBRACKET { $$ = make_shared($2); } | expr COLON expr { TYPERR_CATCH($$ = ArrayMV::range($1, $3), @$); } | expr IN expr { TYPERR_CATCH($$ = $3->in($1), @$); } ; comma_expr : { $$ = vector{}; } // Empty array | expr { $$ = vector{$1}; } | comma_expr COMMA expr { $1.push_back($3); $$ = $1; } ; %% void Macro::parser::error(const Macro::parser::location_type &l, const string &m) { driver.error(l, m); }