macro processor 2.0

issue#70
Houtan Bastani 2019-05-30 20:40:56 +02:00
parent 69f2f1ca88
commit 17e040f3f6
No known key found for this signature in database
GPG Key ID: 000094FB955BE169
21 changed files with 3523 additions and 2736 deletions

6
.gitignore vendored
View File

@ -33,9 +33,9 @@ TAGS
/src/location.hh
/src/position.hh
/src/stack.hh
/src/macro/MacroBison.cc
/src/macro/MacroBison.hh
/src/macro/MacroFlex.cc
/src/macro/Parser.cc
/src/macro/Parser.hh
/src/macro/Tokenizer.cc
/src/macro/location.hh
/src/macro/position.hh
/src/macro/stack.hh

View File

@ -20,7 +20,7 @@
#include <sstream>
#include <fstream>
#include "macro/MacroDriver.hh"
#include "macro/Driver.hh"
bool compareNewline (int i, int j) {
return i == '\n' && j == '\n';
@ -28,12 +28,13 @@ bool compareNewline (int i, int j) {
void
main1(const string &filename, const string &basename, istream &modfile, bool debug, bool save_macro, string &save_macro_file,
bool no_line_macro, bool no_empty_line_macro, const vector<pair<string, string>> &defines, const vector<string> &path, stringstream &macro_output)
bool no_line_macro_arg, bool no_empty_line_macro, const vector<pair<string, string>> &defines, const vector<string> &path,
stringstream &macro_output)
{
// Do macro processing
MacroDriver m;
m.parse(filename, basename, modfile, macro_output, debug, no_line_macro, defines, path);
macro::Environment env = macro::Environment();
macro::Driver m(env, no_line_macro_arg);
m.parse(filename, basename, modfile, macro_output, debug, defines, path);
if (save_macro)
{
if (save_macro_file.empty())

View File

@ -21,7 +21,7 @@
#define _PARSING_DRIVER_HH
#ifdef _MACRO_DRIVER_HH
# error Impossible to include both ParsingDriver.hh and MacroDriver.hh
# error Impossible to include both ParsingDriver.hh and macro/Driver.hh
#endif
#include <string>

282
src/macro/Directives.cc Normal file
View File

@ -0,0 +1,282 @@
/*
* 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/>.
*/
#include "Directives.hh"
using namespace macro;
void
Eval::interpret(ostream &output, bool no_line_macro)
{
try
{
output << expr->eval()->to_string();
}
catch (StackTrace &ex)
{
ex.push("Evaluation", location);
error(ex);
}
catch (exception &e)
{
error(StackTrace("Evaluation", e.what(), location));
}
}
void
Include::interpret(ostream &output, bool no_line_macro)
{
try
{
StringPtr msp = dynamic_pointer_cast<String>(expr->eval());
if (!msp)
throw StackTrace("File name does not evaluate to a string");
name = *msp;
}
catch (StackTrace &ex)
{
ex.push("@#include", location);
error(ex);
}
catch (exception &e)
{
error(StackTrace("@#include", e.what(), location));
}
}
void
IncludePath::interpret(ostream &output, bool no_line_macro)
{
try
{
StringPtr msp = dynamic_pointer_cast<String>(expr->eval());
if (!msp)
throw StackTrace("File name does not evaluate to a string");
path = *msp;
}
catch (StackTrace &ex)
{
ex.push("@#includepath", location);
error(ex);
}
catch (exception &e)
{
error(StackTrace("@#includepath", e.what(), location));
}
}
void
Define::interpret(ostream &output, bool no_line_macro)
{
try
{
if (var)
env.define(var, value);
else if (func)
env.define(func, value);
else
throw StackTrace("LHS of can be either a variable or a function");
}
catch (StackTrace &ex)
{
ex.push("@#define", location);
error(ex);
}
catch (exception &e)
{
error(StackTrace("@#define", e.what(), location));
}
}
void
Echo::interpret(ostream &output, bool no_line_macro)
{
try
{
cout << "@#echo (" << getLocation() << "): " << expr->eval()->to_string() << endl;
}
catch (StackTrace &ex)
{
ex.push("@#echo", location);
error(ex);
}
catch (exception &e)
{
error(StackTrace("@#echo", e.what(), location));
}
printLineInfo(output, no_line_macro);
}
void
Error::interpret(ostream &output, bool no_line_macro)
{
try
{
throw StackTrace(expr->eval()->to_string());
}
catch (StackTrace &ex)
{
ex.push("@#error", location);
error(ex);
}
catch (exception &e)
{
error(StackTrace("@#error", e.what(), location));
}
}
void
For::interpret(ostream &output, bool no_line_macro)
{
ArrayPtr ap;
try
{
ap = dynamic_pointer_cast<Array>(index_vals->eval());
if (!ap)
throw StackTrace("The index must loop through an array");
}
catch (StackTrace &ex)
{
ex.push("@#for", location);
error(ex);
}
catch (exception &e)
{
error(StackTrace("@#for", e.what(), location));
}
for (size_t i = 0; i < ap->size(); i++)
{
if (index_vec.size() == 1)
env.define(index_vec.at(0), ap->at(i));
else
{
BaseTypePtr btp = dynamic_pointer_cast<BaseType>(ap->at(i));
if (!btp)
error(StackTrace("@#for", "Unexpected error encountered in for loop", location));
if (btp->getType() == codes::BaseType::Tuple)
{
TuplePtr mtp = dynamic_pointer_cast<Tuple>(btp);
if (index_vec.size() != mtp->size())
error(StackTrace("@#for", "Encountered tuple of size " + to_string(mtp->size())
+ " but only have " + to_string(index_vec.size()) + " index variables", location));
else
for (size_t j = 0; j < index_vec.size(); j++)
env.define(index_vec.at(j), mtp->at(j));
}
}
bool printLine = true;
for (auto & statement : statements)
{
if (printLine)
{
statement->printLineInfo(output, no_line_macro);
printLine = false;
}
statement->interpret(output, no_line_macro);
}
}
printEndLineInfo(output, no_line_macro);
}
void
If::interpret(ostream &output, bool no_line_macro)
{
DoublePtr dp;
BoolPtr bp;
try
{
dp = dynamic_pointer_cast<Double>(condition->eval());
bp = dynamic_pointer_cast<Bool>(condition->eval());
if (!bp && !dp)
error(StackTrace("@#if", "The condition must evaluate to a boolean or a double", location));
}
catch (StackTrace &ex)
{
ex.push("@#if", location);
error(ex);
}
catch (exception &e)
{
error(StackTrace("@#if", e.what(), location));
}
if ((bp && *bp) || (dp && *dp))
loopIf(output, no_line_macro);
else
loopElse(output, no_line_macro);
printEndLineInfo(output, no_line_macro);
}
void
If::loopIf(ostream &output, bool no_line_macro)
{
bool printLine = !no_line_macro;
for (auto & statement : if_statements)
{
if (printLine)
{
statement->printLineInfo(output, no_line_macro);
printLine = false;
}
statement->interpret(output, no_line_macro);
}
}
void
If::loopElse(ostream &output, bool no_line_macro)
{
bool printLine = !no_line_macro;
for (auto & statement : else_statements)
{
if (printLine)
{
statement->printLineInfo(output, no_line_macro);
printLine = false;
}
statement->interpret(output, no_line_macro);
}
}
void
Ifdef::interpret(ostream &output, bool no_line_macro)
{
BaseTypePtr btnp = dynamic_pointer_cast<BaseType>(condition);
VariablePtr vp = dynamic_pointer_cast<Variable>(condition);
if (btnp || (vp && env.isVariableDefined(vp->getName())))
loopIf(output, no_line_macro);
else
loopElse(output, no_line_macro);
printEndLineInfo(output, no_line_macro);
}
void
Ifndef::interpret(ostream &output, bool no_line_macro)
{
BaseTypePtr btnp = dynamic_pointer_cast<BaseType>(condition);
VariablePtr vp = dynamic_pointer_cast<Variable>(condition);
if (!(btnp || (vp && env.isVariableDefined(vp->getName()))))
loopIf(output, no_line_macro);
else
loopElse(output, no_line_macro);
printEndLineInfo(output, no_line_macro);
}

218
src/macro/Directives.hh Normal file
View File

@ -0,0 +1,218 @@
/*
* Copyright (C) 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/>.
*/
#ifndef _DIRECTIVES_HH
#define _DIRECTIVES_HH
#include "Expressions.hh"
using namespace std;
namespace macro
{
class Directive : public Node
{
// A Parent class just for clarity
public:
Directive(Environment &env_arg, const Tokenizer::location location_arg) : Node(env_arg, move(location_arg)) { }
// Directives can be interpreted
virtual void interpret(ostream &output, bool no_line_macro) = 0;
};
using DirectivePtr = shared_ptr<Directive>;
class TextNode : public Directive
{
// Class for text not interpreted by macroprocessor
// Not a real directive node
// Treated as such as the output is only to be interpreted
private:
const string text;
public:
TextNode(const string text_arg, Environment &env_arg, const Tokenizer::location location_arg) :
Directive(env_arg, move(location_arg)), text{move(text_arg)} { }
inline void interpret(ostream &output, bool no_line_macro) override { output << text; }
};
class Eval : public Directive
{
// Class for @{} statements
// Not a real directive node
// Treated as such as the output is only to be interpreted
private:
const ExpressionPtr expr;
public:
Eval(const ExpressionPtr expr_arg, Environment &env_arg, const Tokenizer::location location_arg) :
Directive(env_arg, move(location_arg)), expr{move(expr_arg)} { }
void interpret(ostream &output, bool no_line_macro) override;
};
class Include : public Directive
{
private:
const ExpressionPtr expr;
string name;
public:
Include(const ExpressionPtr expr_arg, Environment &env_arg, const Tokenizer::location location_arg) :
Directive(env_arg, move(location_arg)), expr{move(expr_arg)} { }
void interpret(ostream &output, bool no_line_macro) override;
inline string getName() const { return name; }
};
class IncludePath : public Directive
{
private:
const ExpressionPtr expr;
string path;
public:
IncludePath(const ExpressionPtr expr_arg, Environment &env_arg, const Tokenizer::location location_arg) :
Directive(env_arg, move(location_arg)), expr{move(expr_arg)} { }
void interpret(ostream &output, bool no_line_macro) override;
inline string getPath() const { return path; }
};
class Define : public Directive
{
private:
const VariablePtr var;
const FunctionPtr func;
const ExpressionPtr value;
public:
Define(const VariablePtr var_arg,
const ExpressionPtr value_arg,
Environment &env_arg, const Tokenizer::location location_arg) :
Directive(env_arg, move(location_arg)), var{move(var_arg)}, value{move(value_arg)} { }
Define(const FunctionPtr func_arg,
const ExpressionPtr value_arg,
Environment &env_arg, const Tokenizer::location location_arg) :
Directive(env_arg, move(location_arg)), func{move(func_arg)}, value{move(value_arg)} { }
void interpret(ostream &output, bool no_line_macro) override;
};
class Echo : public Directive
{
private:
const ExpressionPtr expr;
public:
Echo(const ExpressionPtr expr_arg,
Environment &env_arg, const Tokenizer::location location_arg) :
Directive(env_arg, move(location_arg)), expr{move(expr_arg)} { }
void interpret(ostream &output, bool no_line_macro) override;
};
class Error : public Directive
{
private:
const ExpressionPtr expr;
public:
Error(const ExpressionPtr expr_arg,
Environment &env_arg, const Tokenizer::location location_arg) :
Directive(env_arg, move(location_arg)), expr{move(expr_arg)} { }
void interpret(ostream &output, bool no_line_macro) override;
};
class EchoMacroVars : public Directive
{
public:
EchoMacroVars(Environment &env_arg, const Tokenizer::location location_arg) : Directive(env_arg, move(location_arg)) { }
inline void interpret(ostream &output, bool no_line_macro) override { env.print(); }
};
class For : public Directive
{
private:
const vector<VariablePtr> index_vec;
const ExpressionPtr index_vals;
vector<DirectivePtr> statements;
public:
For(const vector<VariablePtr> index_vec_arg,
const ExpressionPtr index_vals_arg,
const vector<DirectivePtr> statements_arg,
Environment &env_arg, const Tokenizer::location location_arg) :
Directive(env_arg, move(location_arg)), index_vec{move(index_vec_arg)},
index_vals{move(index_vals_arg)}, statements{statements_arg} { }
void interpret(ostream &output, bool no_line_macro) override;
};
class If : public Directive
{
protected:
const ExpressionPtr condition;
vector<DirectivePtr> if_statements;
vector<DirectivePtr> else_statements;
public:
If(const ExpressionPtr condition_arg,
const vector<DirectivePtr> if_statements_arg,
Environment &env_arg, const Tokenizer::location location_arg) :
Directive(env_arg, move(location_arg)), condition{move(condition_arg)}, if_statements{if_statements_arg} { }
If(const ExpressionPtr condition_arg,
const vector<DirectivePtr> if_statements_arg,
const vector<DirectivePtr> else_statements_arg,
Environment &env_arg, const Tokenizer::location location_arg) :
Directive(env_arg, move(location_arg)),
condition{move(condition_arg)}, if_statements{if_statements_arg}, else_statements{else_statements_arg} { }
void interpret(ostream &output, bool no_line_macro) override;
protected:
void loopIf(ostream &output, bool no_line_macro);
void loopElse(ostream &output, bool no_line_macro);
};
class Ifdef : public If
{
public:
Ifdef(const ExpressionPtr condition_arg,
const vector<DirectivePtr> if_statements_arg,
Environment &env_arg, const Tokenizer::location location_arg) :
If(move(condition_arg), move(if_statements_arg), env_arg, move(location_arg)) { }
Ifdef(const ExpressionPtr condition_arg,
const vector<DirectivePtr> if_statements_arg,
const vector<DirectivePtr> else_statements_arg,
Environment &env_arg, const Tokenizer::location location_arg) :
If(move(condition_arg), move(if_statements_arg), move(else_statements_arg), env_arg, move(location_arg)) { }
void interpret(ostream &output, bool no_line_macro) override;
};
class Ifndef : public If
{
public:
Ifndef(const ExpressionPtr condition_arg,
const vector<DirectivePtr> if_statements_arg,
Environment &env_arg, const Tokenizer::location location_arg) :
If(move(condition_arg), move(if_statements_arg), env_arg, move(location_arg)) { }
Ifndef(const ExpressionPtr condition_arg,
const vector<DirectivePtr> if_statements_arg,
const vector<DirectivePtr> else_statements_arg,
Environment &env_arg, const Tokenizer::location location_arg) :
If(move(condition_arg), move(if_statements_arg), move(else_statements_arg), env_arg, move(location_arg)) { }
void interpret(ostream &output, bool no_line_macro) override;
};
}
#endif

128
src/macro/Driver.cc Normal file
View File

@ -0,0 +1,128 @@
/*
* 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/>.
*/
#include "Driver.hh"
#include <fstream>
#include <sstream>
using namespace macro;
void
Driver::parse(const string &file_arg, const string &basename_arg, istream &modfile,
ostream &output, bool debug, const vector<pair<string, string>> &defines,
vector<string> paths_arg)
{
#ifdef _WIN32
string FILESEP = "\\";
#else
string FILESEP = "/";
#endif
file = file_arg;
basename = basename_arg;
paths = move(paths_arg);
if (!defines.empty())
{
stringstream command_line_defines_with_endl;
for (auto & define : defines)
try
{
stoi(define.second);
command_line_defines_with_endl << "@#define " << define.first << " = " << define.second << endl;
}
catch (const invalid_argument &)
{
if (!define.second.empty() && define.second.at(0) == '[' && define.second.at(define.second.length()-1) == ']')
// If the input is an array. Issue #1578
command_line_defines_with_endl << "@#define " << define.first << " = " << define.second << endl;
else
command_line_defines_with_endl << "@#define " << define.first << " = \"" << define.second << "\"" << endl;
}
Driver m(env, true);
istream is(command_line_defines_with_endl.rdbuf());
m.parse("command_line_defines", "command_line_defines", is, output, debug, vector<pair<string, string>>{}, paths);
}
stringstream file_with_endl;
file_with_endl << modfile.rdbuf() << endl;
lexer = make_unique<TokenizerFlex>(&file_with_endl);
lexer->set_debug(debug);
Tokenizer::parser parser(*this);
parser.set_debug_level(debug);
// Launch macro-processing
parser.parse();
// Interpret parsed statements
bool printLine = true;
for (auto & statement : statements)
{
if (printLine)
{
statement->printLineInfo(output, no_line_macro);
printLine = false;
}
statement->interpret(output, no_line_macro);
auto ipp = dynamic_pointer_cast<IncludePath>(statement);
if (ipp)
paths.emplace_back(ipp->getPath());
auto ip = dynamic_pointer_cast<Include>(statement);
if (ip)
{
string filename = ip->getName();
ifstream incfile(filename, ios::binary);
if (incfile.fail())
{
ostringstream dirs;
dirs << "." << FILESEP << endl;
for (const auto & path : paths)
{
string testfile = path + FILESEP + filename;
incfile = ifstream(testfile, ios::binary);
if (incfile.good())
break;
dirs << path << endl;
}
if (incfile.fail())
error(statement->getLocation(), "Could not open " + filename +
". The following directories were searched:\n" + dirs.str());
}
string basename = filename;
size_t pos = basename.find_last_of('.');
if (pos != string::npos)
basename.erase(pos);
Driver m(env, no_line_macro);
m.parse(filename, basename, incfile, output, debug, vector<pair<string, string>>{}, paths);
}
}
}
void
Driver::error(const Tokenizer::parser::location_type &location, const string &message) const
{
cerr << "ERROR in macro-processor: " << location << ": " << message << endl;
exit(EXIT_FAILURE);
}

120
src/macro/Driver.hh Normal file
View File

@ -0,0 +1,120 @@
/*
* 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/>.
*/
#ifndef _MACRO_DRIVER_HH
#define _MACRO_DRIVER_HH
#ifdef _PARSING_DRIVER_HH
# error Impossible to include both ../ParsingDriver.hh and Driver.hh
#endif
#include "Parser.hh"
#include "Environment.hh"
#include "Expressions.hh"
#include <stack>
using namespace std;
// Declare TokenizerFlexLexer class
#ifndef __FLEX_LEXER_H
# define yyFlexLexer TokenizerFlexLexer
# include <FlexLexer.h>
# undef yyFlexLexer
#endif
namespace macro
{
/* The lexer class
* It was necessary to subclass the TokenizerFlexLexer class generated by Flex,
* since the prototype for TokenizerFlexLexer::yylex() was not convenient.
*/
class TokenizerFlex : public TokenizerFlexLexer
{
public:
TokenizerFlex(istream *in) : TokenizerFlexLexer{in} { }
TokenizerFlex(const TokenizerFlex &) = delete;
TokenizerFlex(TokenizerFlex &&) = delete;
TokenizerFlex & operator=(const TokenizerFlex &) = delete;
TokenizerFlex & operator=(TokenizerFlex &&) = delete;
//! The main lexing function
Tokenizer::parser::token_type lex(Tokenizer::parser::semantic_type *yylval,
Tokenizer::parser::location_type *yylloc,
macro::Driver &driver);
};
//! Implements the macro expansion using a Flex scanner and a Bison parser
class Driver
{
public:
Environment &env;
private:
bool no_line_macro;
vector<DirectivePtr> statements;
stack<vector<DirectivePtr>> directive_stack;
//! The paths to search when looking for .mod files
vector<string> paths;
public:
Driver(Environment &env_arg, bool no_line_macro_arg) :
env{env_arg}, no_line_macro(no_line_macro_arg) { }
Driver(const Driver &) = delete;
Driver(Driver &&) = delete;
Driver & operator=(const Driver &) = delete;
Driver & operator=(Driver &&) = delete;
//! Exception thrown when value of an unknown variable is requested
class UnknownVariable
{
public:
const string name;
explicit UnknownVariable(string name_arg) : name{move(name_arg)}
{
}
};
//! Starts parsing a file, returns output in out
void parse(const string &file_arg, const string &basename_arg, istream &modfile,
ostream &output, bool debug, const vector<pair<string, string>> &defines,
vector<string> paths_arg);
//! Name of main file being parsed
string file;
//! Basename of main file being parsed
string basename;
//! Reference to the lexer
unique_ptr<TokenizerFlex> lexer;
//! Error handler
void error(const Tokenizer::parser::location_type &location, const string &message) const;
inline bool inContext() const { return !directive_stack.empty(); }
inline void pushContext() { directive_stack.emplace(vector<DirectivePtr>()); }
inline void pushContextTop(DirectivePtr statement) { directive_stack.top().emplace_back(statement); }
inline void pushStatements(DirectivePtr statement) { statements.emplace_back(statement); }
inline vector<DirectivePtr> popContext() { auto top = directive_stack.top(); directive_stack.pop(); return top; }
};
}
#endif // ! MACRO_DRIVER_HH

132
src/macro/Environment.cc Normal file
View File

@ -0,0 +1,132 @@
/*
* 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/>.
*/
#include "Environment.hh"
#include "Expressions.hh"
using namespace macro;
void
Environment::define(VariablePtr var, ExpressionPtr value)
{
string name = var->getName();
if (functions.find(name) != functions.end())
throw StackTrace("Variable " + name + " was previously defined as a function");
variables[move(name)] = value->eval();
}
void
Environment::define(FunctionPtr func, ExpressionPtr value)
{
string name = func->getName();
if (variables.find(name) != variables.end())
throw StackTrace("Variable " + name + " was previously defined as a variable");
functions[name] = {move(func), move(value)};
}
ExpressionPtr
Environment::getVariable(const string &name) const
{
auto it = variables.find(name);
if (it != variables.end())
return it->second;
if (!parent)
throw StackTrace("Unknown variable " + name);
return getGlobalEnv()->getVariable(name);
}
tuple<FunctionPtr, ExpressionPtr>
Environment::getFunction(const string &name) const
{
auto it = functions.find(name);
if (it != functions.end())
return it->second;
if (!parent)
throw StackTrace("Unknown function " + name);
return parent->getFunction(name);
}
codes::BaseType
Environment::getType(const string &name)
{
return getVariable(name)->eval()->getType();
}
bool
Environment::isVariableDefined(const string &name) const noexcept
{
try
{
getVariable(name);
return true;
}
catch (StackTrace &ex)
{
return false;
}
}
bool
Environment::isFunctionDefined(const string &name) const noexcept
{
try
{
getFunction(name);
return true;
}
catch (StackTrace &ex)
{
return false;
}
}
void
Environment::print() const
{
if (!variables.empty())
{
cout << "Macro Variables:" << endl;
for (auto & it : variables)
{
cout << " " << it.first << " = ";
getVariable(it.first)->eval()->print(cout);
cout << endl;
}
}
if (!functions.empty())
{
cout << "Macro Functions:" << endl;
for (auto & it : functions)
{
cout << " ";
get<0>(it.second)->print(cout);
cout << " = ";
get<1>(it.second)->print(cout);
cout << endl;
}
}
if (parent)
parent->print();
}

54
src/macro/Environment.hh Normal file
View File

@ -0,0 +1,54 @@
/*
* 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/>.
*/
#ifndef _ENVIRONMENT_HH
#define _ENVIRONMENT_HH
#include "ForwardDeclarationsAndEnums.hh"
#include <map>
#include <ostream>
using namespace std;
namespace macro
{
class Environment
{
private:
const Environment *parent;
map<string, ExpressionPtr> variables;
map<string, tuple<FunctionPtr, ExpressionPtr>> functions;
public:
Environment() : parent{nullptr} { }
Environment(const Environment *parent_arg) : parent{parent_arg} { }
void define(VariablePtr var, ExpressionPtr value);
void define(FunctionPtr func, ExpressionPtr value);
ExpressionPtr getVariable(const string &name) const;
tuple<FunctionPtr, ExpressionPtr> getFunction(const string &name) const;
codes::BaseType getType(const string &name);
bool isVariableDefined(const string &name) const noexcept;
bool isFunctionDefined(const string &name) const noexcept;
inline bool isSymbolDefined(const string &name) const noexcept { return isVariableDefined(name) || isFunctionDefined(name); }
void print() const;
inline size_t size() const noexcept { return variables.size() + functions.size(); }
inline const Environment *getGlobalEnv() const noexcept { return parent == nullptr ? this : parent->getGlobalEnv(); }
};
}
#endif

1344
src/macro/Expressions.cc Normal file

File diff suppressed because it is too large Load Diff

479
src/macro/Expressions.hh Normal file
View File

@ -0,0 +1,479 @@
/*
* 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/>.
*/
#ifndef _EXPRESSIONS_HH
#define _EXPRESSIONS_HH
#include "ForwardDeclarationsAndEnums.hh"
#include "Environment.hh"
#include "location.hh"
#include <cmath>
#include <vector>
#include <sstream>
#include <iomanip>
using namespace std;
namespace macro
{
class StackTrace final : public exception
{
private:
vector<string> message;
public:
StackTrace (string message_arg) : message{{move(message_arg)}} { }
StackTrace (const string &prefix, const char *standard_exception_message, const Tokenizer::location &location)
{
stringstream ss;
ss << prefix << ": " << location << " " << standard_exception_message;
message = {ss.str()};
}
StackTrace (const string &prefix, const string &msg, const Tokenizer::location &location)
{
stringstream ss;
ss << prefix << ": " << location << " " << msg;
message = {ss.str()};
}
void push(const string &prefix, const Tokenizer::location &location)
{
stringstream ss;
unsigned end_col = 0 < location.end.column ? location.end.column - 1 : 0;
ss << prefix << ": "
<< R"(")" << *location.begin.filename << R"(" line )" << location.begin.line << ", col " << location.begin.column;
if (location.end.filename
&& (!location.begin.filename
|| *location.begin.filename != *location.end.filename))
ss << R"( to ")" << location.end.filename << R"(")" << " line " << location.end.line << ", col " << end_col;
else if (location.begin.line < location.end.line)
ss << " to line " << location.end.line << ", col " << end_col;
else if (location.begin.column < end_col)
ss << "-" << end_col;
message.emplace_back(ss.str());
}
string trace() const
{
stringstream ss;
for (auto & msg : message)
ss << "- " << msg << endl;
return ss.str();
}
};
class Node
{
protected:
Environment &env;
const Tokenizer::location location;
public:
Node(Environment &env_arg, Tokenizer::location location_arg) :
env{env_arg}, location{move(location_arg)} { }
virtual ~Node() = default;
public:
inline Tokenizer::location getLocation() const noexcept { return location; }
inline void error(const StackTrace &e) const noexcept
{
cerr << endl << "Macro-processing error: backtrace..." << endl << e.trace();
exit(EXIT_FAILURE);
}
inline void printLineInfo(ostream &output, bool no_line_macro) const noexcept
{
if (!no_line_macro)
output << R"(@#line ")" << *(location.begin.filename) << R"(" )" << location.begin.line << endl;
}
inline void printEndLineInfo(ostream &output, bool no_line_macro) const noexcept
{
if (!no_line_macro)
// Add one to end line because we want to print the line number of the line *following* the end statement
output << R"(@#line ")" << *(location.begin.filename) << R"(" )" << location.end.line + 1 << endl;
}
};
class Expression : public Node
{
public:
Expression(Environment &env_arg, const Tokenizer::location location_arg) :
Node(env_arg, move(location_arg)) { }
virtual string to_string() const noexcept = 0;
virtual void print(ostream &output) const noexcept = 0;
virtual BaseTypePtr eval() = 0;
};
class BaseType : public Expression, public enable_shared_from_this<BaseType>
{
public:
BaseType(Environment &env_arg, Tokenizer::location location_arg = Tokenizer::location()) :
Expression(env_arg, move(location_arg)) { }
virtual codes::BaseType getType() const noexcept = 0;
inline BaseTypePtr eval() override { return shared_from_this(); }
public:
virtual BaseTypePtr plus(const BaseTypePtr &bt) const { throw StackTrace("Operator + does not exist for this type"); }
virtual BaseTypePtr unary_plus() const { throw StackTrace("Unary operator + does not exist for this type"); }
virtual BaseTypePtr minus(const BaseTypePtr &bt) const { throw StackTrace("Operator - does not exist for this type"); }
virtual BaseTypePtr unary_minus() const { throw StackTrace("Unary operator - does not exist for this type"); }
virtual BaseTypePtr times(const BaseTypePtr &bt) const { throw StackTrace("Operator * does not exist for this type"); }
virtual BaseTypePtr divide(const BaseTypePtr &bt) const { throw StackTrace("Operator / does not exist for this type"); }
virtual BaseTypePtr power(const BaseTypePtr &btp) const { throw StackTrace("Operator ^ does not exist for this type"); }
virtual BoolPtr is_less(const BaseTypePtr &btp) const { throw StackTrace("Operator < does not exist for this type"); }
virtual BoolPtr is_greater(const BaseTypePtr &btp) const { throw StackTrace("Operator > does not exist for this type"); }
virtual BoolPtr is_less_equal(const BaseTypePtr &btp) const { throw StackTrace("Operator <= does not exist for this type"); }
virtual BoolPtr is_greater_equal(const BaseTypePtr &btp) const { throw StackTrace("Operator >= does not exist for this type"); }
virtual BoolPtr is_equal(const BaseTypePtr &btp) const = 0;
virtual BoolPtr is_different(const BaseTypePtr &btp) const final;
virtual BoolPtr logical_and(const BaseTypePtr &btp) const { throw StackTrace("Operator && does not exist for this type"); }
virtual BoolPtr logical_or(const BaseTypePtr &btp) const { throw StackTrace("Operator || does not exist for this type"); }
virtual BoolPtr logical_not() const { throw StackTrace("Operator ! does not exist for this type"); }
virtual ArrayPtr set_union(const BaseTypePtr &btp) const { throw StackTrace("Operator | does not exist for this type"); }
virtual ArrayPtr set_intersection(const BaseTypePtr &btp) const { throw StackTrace("Operator & does not exist for this type"); }
virtual BoolPtr contains(const BaseTypePtr &btp) const { throw StackTrace("Second argument of `in` operator must be an array"); }
virtual DoublePtr length() const { throw StackTrace("Operator `length` does not exist for this type"); }
virtual DoublePtr max(const BaseTypePtr &btp) const { throw StackTrace("Operator `max` does not exist for this type"); }
virtual DoublePtr min(const BaseTypePtr &btp) const { throw StackTrace("Operator `min` does not exist for this type"); }
virtual DoublePtr mod(const BaseTypePtr &btp) const { throw StackTrace("Operator `mod` does not exist for this type"); }
virtual DoublePtr exp() const { throw StackTrace("Operator `exp` does not exist for this type"); }
virtual DoublePtr ln() const { throw StackTrace("Operator `ln` does not exist for this type"); }
virtual DoublePtr log10() const { throw StackTrace("Operator `log10` does not exist for this type"); }
virtual BoolPtr isinf() const { throw StackTrace("Operator `isinf` does not exist for this type"); }
virtual BoolPtr isnan() const { throw StackTrace("Operator `isnan` does not exist for this type"); }
virtual BoolPtr isfinite() const { throw StackTrace("Operator `isfinite` does not exist for this type"); }
virtual BoolPtr isnormal() const { throw StackTrace("Operator `isnormal` does not exist for this type"); }
virtual DoublePtr sin() const { throw StackTrace("Operator `sin` does not exist for this type"); }
virtual DoublePtr cos() const { throw StackTrace("Operator `cos` does not exist for this type"); }
virtual DoublePtr tan() const { throw StackTrace("Operator `tan` does not exist for this type"); }
virtual DoublePtr asin() const { throw StackTrace("Operator `asin` does not exist for this type"); }
virtual DoublePtr acos() const { throw StackTrace("Operator `acos` does not exist for this type"); }
virtual DoublePtr atan() const { throw StackTrace("Operator `atan` does not exist for this type"); }
virtual DoublePtr sqrt() const { throw StackTrace("Operator `sqrt` does not exist for this type"); }
virtual DoublePtr cbrt() const { throw StackTrace("Operator `cbrt` does not exist for this type"); }
virtual DoublePtr sign() const { throw StackTrace("Operator `sign` does not exist for this type"); }
virtual DoublePtr floor() const { throw StackTrace("Operator `floor` does not exist for this type"); }
virtual DoublePtr ceil() const { throw StackTrace("Operator `ceil` does not exist for this type"); }
virtual DoublePtr trunc() const { throw StackTrace("Operator `trunc` does not exist for this type"); }
virtual DoublePtr sum() const { throw StackTrace("Operator `sum` does not exist for this type"); }
virtual DoublePtr erf() const { throw StackTrace("Operator `erf` does not exist for this type"); }
virtual DoublePtr erfc() const { throw StackTrace("Operator `erfc` does not exist for this type"); }
virtual DoublePtr gamma() const { throw StackTrace("Operator `gamma` does not exist for this type"); }
virtual DoublePtr lgamma() const { throw StackTrace("Operator `lgamma` does not exist for this type"); }
virtual DoublePtr round() const { throw StackTrace("Operator `round` does not exist for this type"); }
virtual DoublePtr normpdf() const { throw StackTrace("Operator `normpdf` does not exist for this type"); }
virtual DoublePtr normpdf(const BaseTypePtr &btp1, const BaseTypePtr &btp2) const { throw StackTrace("Operator `normpdf` does not exist for this type"); }
virtual DoublePtr normcdf() const { throw StackTrace("Operator `normcdf` does not exist for this type"); }
virtual DoublePtr normcdf(const BaseTypePtr &btp1, const BaseTypePtr &btp2) const { throw StackTrace("Operator `normcdf` does not exist for this type"); }
};
class Bool final : public BaseType
{
private:
bool value;
public:
Bool(const bool value_arg,
Environment &env_arg, Tokenizer::location location_arg = Tokenizer::location()) :
BaseType(env_arg, move(location_arg)),
value{value_arg} { }
inline codes::BaseType getType() const noexcept override { return codes::BaseType::Bool; }
inline string to_string() const noexcept override { return value ? "true" : "false"; }
inline void print(ostream &output) const noexcept override { output << to_string(); }
public:
operator bool() const { return value; }
BoolPtr is_equal(const BaseTypePtr &btp) const override;
BoolPtr logical_and(const BaseTypePtr &btp) const override;
BoolPtr logical_or(const BaseTypePtr &btp) const override;
BoolPtr logical_not() const override;
};
class Double final : public BaseType
{
private:
double value;
public:
// Use strtod to handle extreme cases (e.g. 1e500, 1e-500), nan, inf
// See Note in NumericalConstants::AddNonNegativeConstant
Double(const string value_arg,
Environment &env_arg, Tokenizer::location location_arg = Tokenizer::location()) :
BaseType(env_arg, move(location_arg)),
value{strtod(value_arg.c_str(), nullptr)} { }
Double(double value_arg,
Environment &env_arg) :
BaseType(env_arg),
value{value_arg} { }
inline codes::BaseType getType() const noexcept override { return codes::BaseType::Double; }
inline string to_string() const noexcept override
{
ostringstream strs;
strs << setprecision(15) << value;
return strs.str();
}
inline void print(ostream &output) const noexcept override { output << to_string(); }
public:
operator double() const { return value; }
BaseTypePtr plus(const BaseTypePtr &bt) const override;
inline BaseTypePtr unary_plus() const override { return make_shared<Double>(value, env); }
BaseTypePtr minus(const BaseTypePtr &bt) const override;
inline BaseTypePtr unary_minus() const override { return make_shared<Double>(-value, env); }
BaseTypePtr times(const BaseTypePtr &bt) const override;
BaseTypePtr divide(const BaseTypePtr &bt) const override;
BaseTypePtr power(const BaseTypePtr &btp) const override;
BoolPtr is_less(const BaseTypePtr &btp) const override;
BoolPtr is_greater(const BaseTypePtr &btp) const override;
BoolPtr is_less_equal(const BaseTypePtr &btp) const override;
BoolPtr is_greater_equal(const BaseTypePtr &btp) const override;
BoolPtr is_equal(const BaseTypePtr &btp) const override;
BoolPtr logical_and(const BaseTypePtr &btp) const override;
BoolPtr logical_or(const BaseTypePtr &btp) const override;
BoolPtr logical_not() const override;
DoublePtr max(const BaseTypePtr &btp) const override;
DoublePtr min(const BaseTypePtr &btp) const override;
DoublePtr mod(const BaseTypePtr &btp) const override;
inline DoublePtr exp() const override { return make_shared<Double>(std::exp(value), env); }
inline DoublePtr ln() const override { return make_shared<Double>(std::log(value), env); }
inline DoublePtr log10() const override { return make_shared<Double>(std::log10(value), env); }
inline BoolPtr isinf() const override { return make_shared<Bool>(std::isinf(value), env); }
inline BoolPtr isnan() const override { return make_shared<Bool>(std::isnan(value), env); }
inline BoolPtr isfinite() const override { return make_shared<Bool>(std::isfinite(value), env); }
inline BoolPtr isnormal() const override { return make_shared<Bool>(std::isnormal(value), env); }
inline DoublePtr sin() const override { return make_shared<Double>(std::sin(value), env); }
inline DoublePtr cos() const override { return make_shared<Double>(std::cos(value), env); }
inline DoublePtr tan() const override { return make_shared<Double>(std::tan(value), env); }
inline DoublePtr asin() const override { return make_shared<Double>(std::asin(value), env); }
inline DoublePtr acos() const override { return make_shared<Double>(std::acos(value), env); }
inline DoublePtr atan() const override { return make_shared<Double>(std::atan(value), env); }
inline DoublePtr sqrt() const override { return make_shared<Double>(std::sqrt(value), env); }
inline DoublePtr cbrt() const override { return make_shared<Double>(std::cbrt(value), env); }
inline DoublePtr sign() const override { return make_shared<Double>((value > 0) ? 1. : ((value < 0) ? -1. : 0.), env); }
inline DoublePtr floor() const override { return make_shared<Double>(std::floor(value), env); }
inline DoublePtr ceil() const override { return make_shared<Double>(std::ceil(value), env); }
inline DoublePtr trunc() const override { return make_shared<Double>(std::trunc(value), env); }
inline DoublePtr erf() const override { return make_shared<Double>(std::erf(value), env); }
inline DoublePtr erfc() const override { return make_shared<Double>(std::erfc(value), env); }
inline DoublePtr gamma() const override { return make_shared<Double>(std::tgamma(value), env); }
inline DoublePtr lgamma() const override { return make_shared<Double>(std::lgamma(value), env); }
inline DoublePtr round() const override { return make_shared<Double>(std::round(value), env); }
inline DoublePtr normpdf() const override { return normpdf(make_shared<Double>(0, env), make_shared<Double>(1, env)); }
DoublePtr normpdf(const BaseTypePtr &btp1, const BaseTypePtr &btp2) const override;
inline DoublePtr normcdf() const override { return normcdf(make_shared<Double>(0, env), make_shared<Double>(1, env)); }
DoublePtr normcdf(const BaseTypePtr &btp1, const BaseTypePtr &btp2) const override;
};
class String final : public BaseType
{
private:
string value;
public:
String(const string value_arg,
Environment &env_arg, Tokenizer::location location_arg = Tokenizer::location()) :
BaseType(env_arg, move(location_arg)),
value{move(value_arg)} { }
inline codes::BaseType getType() const noexcept override { return codes::BaseType::String; }
inline string to_string() const noexcept override { return value; }
inline void print(ostream &output) const noexcept override { output << R"(")" << value << R"(")"; }
public:
operator string() const { return value; }
BaseTypePtr plus(const BaseTypePtr &bt) const override;
BoolPtr is_less(const BaseTypePtr &btp) const override;
BoolPtr is_greater(const BaseTypePtr &btp) const override;
BoolPtr is_less_equal(const BaseTypePtr &btp) const override;
BoolPtr is_greater_equal(const BaseTypePtr &btp) const override;
BoolPtr is_equal(const BaseTypePtr &btp) const override;
inline DoublePtr length() const override { return make_shared<Double>(value.size(), env); }
};
class Tuple final : public BaseType
{
private:
vector<ExpressionPtr> tup;
public:
Tuple(const vector<ExpressionPtr> tup_arg,
Environment &env_arg, Tokenizer::location location_arg = Tokenizer::location()) :
BaseType(env_arg, move(location_arg)),
tup{move(tup_arg)} { }
inline codes::BaseType getType() const noexcept override { return codes::BaseType::Tuple; }
string to_string() const noexcept override;
void print(ostream &output) const noexcept override;
BaseTypePtr eval() override;
public:
inline size_t size() const { return tup.size(); }
inline bool empty() const { return tup.empty(); }
inline vector<ExpressionPtr> getValue() const { return tup; }
inline ExpressionPtr at(int i) const { return tup.at(i); }
BoolPtr is_equal(const BaseTypePtr &btp) const override;
BoolPtr contains(const BaseTypePtr &btp) const override;
inline DoublePtr length() const override { return make_shared<Double>(tup.size(), env); }
};
class Array final : public BaseType
{
private:
vector<ExpressionPtr> arr;
ExpressionPtr range1, increment, range2;
public:
Array(const vector<ExpressionPtr> arr_arg,
Environment &env_arg, Tokenizer::location location_arg = Tokenizer::location()) :
BaseType(env_arg, move(location_arg)),
arr{move(arr_arg)} { }
Array(const ExpressionPtr range1_arg, const ExpressionPtr range2_arg,
Environment &env_arg, Tokenizer::location location_arg) :
BaseType(env_arg, move(location_arg)),
range1{move(range1_arg)}, range2{move(range2_arg)} { }
Array(const ExpressionPtr range1_arg, const ExpressionPtr increment_arg, const ExpressionPtr range2_arg,
Environment &env_arg, Tokenizer::location location_arg) :
BaseType(env_arg, move(location_arg)),
range1{move(range1_arg)}, increment{move(increment_arg)}, range2{move(range2_arg)} { }
inline codes::BaseType getType() const noexcept override { return codes::BaseType::Array; }
string to_string() const noexcept override;
void print(ostream &output) const noexcept override;
BaseTypePtr eval() override;
private:
BaseTypePtr evalArray();
public:
inline size_t size() const { return arr.size(); }
inline vector<ExpressionPtr> getValue() const { return arr; }
inline ExpressionPtr at(int i) const { return arr.at(i); }
inline bool empty() const { return arr.empty() && !range1 && !range2; }
BaseTypePtr plus(const BaseTypePtr &bt) const override;
BaseTypePtr minus(const BaseTypePtr &bt) const override;
BaseTypePtr times(const BaseTypePtr &bt) const override;
BaseTypePtr power(const BaseTypePtr &btp) const override;
BoolPtr is_equal(const BaseTypePtr &btp) const override;
ArrayPtr set_union(const BaseTypePtr &btp) const override;
ArrayPtr set_intersection(const BaseTypePtr &btp) const override;
BoolPtr contains(const BaseTypePtr &btp) const override;
inline DoublePtr length() const override { return make_shared<Double>(arr.size(), env); }
DoublePtr sum() const override;
};
class Variable final : public Expression
{
private:
const string name;
ArrayPtr indices; // for strings
public:
Variable(const string name_arg,
Environment &env_arg, const Tokenizer::location location_arg) :
Expression(env_arg, move(location_arg)), name{move(name_arg)} { }
inline void addIndexing(ExpressionPtr indices_arg)
{
indices = dynamic_pointer_cast<Array>(indices_arg);
if (!indices)
indices = make_shared<Array>(vector<ExpressionPtr>{indices_arg}, env);
}
inline string to_string() const noexcept override { return name; }
inline void print(ostream &output) const noexcept override { output << name; }
BaseTypePtr eval() override;
public:
inline string getName() const noexcept { return name; }
inline codes::BaseType getType() const { return env.getType(name); }
};
class Function final : public Expression
{
private:
const string name;
const vector<ExpressionPtr> args;
public:
Function(const string &name_arg,
const vector<ExpressionPtr> &args_arg,
Environment &env_arg, const Tokenizer::location location_arg) :
Expression(env_arg, move(location_arg)), name{move(name_arg)}, args{move(args_arg)} { }
string to_string() const noexcept override;
void print(ostream &output) const noexcept override;
BaseTypePtr eval() override;
public:
inline string getName() const { return name; }
inline vector<ExpressionPtr> getArgs() const { return args; }
};
class UnaryOp final : public Expression
{
private:
const codes::UnaryOp op_code;
const ExpressionPtr arg;
public:
UnaryOp(codes::UnaryOp op_code_arg,
const ExpressionPtr arg_arg,
Environment &env_arg, const Tokenizer::location location_arg) :
Expression(env_arg, move(location_arg)), op_code{move(op_code_arg)}, arg{move(arg_arg)} { }
string to_string() const noexcept override;
void print(ostream &output) const noexcept override;
BaseTypePtr eval() override;
};
class BinaryOp final : public Expression
{
private:
const codes::BinaryOp op_code;
const ExpressionPtr arg1, arg2;
public:
BinaryOp(codes::BinaryOp op_code_arg,
const ExpressionPtr arg1_arg, const ExpressionPtr arg2_arg,
Environment &env_arg, const Tokenizer::location location_arg) :
Expression(env_arg, move(location_arg)), op_code{move(op_code_arg)},
arg1{move(arg1_arg)}, arg2{move(arg2_arg)} { }
public:
string to_string() const noexcept override;
void print(ostream &output) const noexcept override;
BaseTypePtr eval() override;
};
class TrinaryOp final : public Expression
{
private:
const codes::TrinaryOp op_code;
const ExpressionPtr arg1, arg2, arg3;
public:
TrinaryOp(codes::TrinaryOp op_code_arg,
const ExpressionPtr arg1_arg, const ExpressionPtr arg2_arg, const ExpressionPtr arg3_arg,
Environment &env_arg, const Tokenizer::location location_arg) :
Expression(env_arg, move(location_arg)), op_code{move(op_code_arg)},
arg1{move(arg1_arg)}, arg2{move(arg2_arg)}, arg3{move(arg3_arg)} { }
string to_string() const noexcept override;
void print(ostream &output) const noexcept override;
BaseTypePtr eval() override;
};
class Comprehension final : public Expression
{
private:
const ExpressionPtr c_vars, c_set, c_when;
public:
Comprehension(const ExpressionPtr c_vars_arg,
const ExpressionPtr c_set_arg,
const ExpressionPtr c_when_arg,
Environment &env_arg, const Tokenizer::location location_arg) :
Expression(env_arg, move(location_arg)),
c_vars{move(c_vars_arg)}, c_set{move(c_set_arg)}, c_when{move(c_when_arg)} { }
string to_string() const noexcept override;
void print(ostream &output) const noexcept override;
BaseTypePtr eval() override;
};
}
#endif

View File

@ -0,0 +1,130 @@
/*
* 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/>.
*/
#ifndef _FORWARDDECLARATIONSANDENUMS_HH
#define _FORWARDDECLARATIONSANDENUMS_HH
#include <memory>
using namespace std;
namespace macro
{
// For Expressions.hh
class BaseType;
using BaseTypePtr = shared_ptr<BaseType>;
class Bool;
using BoolPtr = shared_ptr<Bool>;
class Double;
using DoublePtr = shared_ptr<Double>;
class String;
using StringPtr = shared_ptr<String>;
class Tuple;
using TuplePtr = shared_ptr<Tuple>;
class Array;
using ArrayPtr = shared_ptr<Array>;
// For Environment.hh
class Expression;
using ExpressionPtr = shared_ptr<Expression>;
class Variable;
using VariablePtr = shared_ptr<Variable>;
class Function;
using FunctionPtr = shared_ptr<Function>;
// For Parser.yy
class Directive;
using DirectivePtr = shared_ptr<Directive>;
class Eval;
using EvalPtr = shared_ptr<Eval>;
namespace codes
{
enum class BaseType
{
Bool,
Double,
String,
Array,
Tuple
};
enum class UnaryOp
{
logical_not,
unary_minus,
unary_plus,
length,
exp,
ln,
log10,
sin,
cos,
tan,
asin,
acos,
atan,
sqrt,
cbrt,
sign,
floor,
ceil,
trunc,
sum,
erf,
erfc,
gamma,
lgamma,
round,
normpdf,
normcdf
};
enum class BinaryOp
{
plus,
minus,
times,
divide,
power,
equal_equal,
not_equal,
less,
greater,
less_equal,
greater_equal,
logical_and,
logical_or,
in,
set_union,
set_intersection,
max,
min,
mod
};
enum class TrinaryOp
{
normpdf,
normcdf
};
}
}
#endif

View File

@ -1,282 +0,0 @@
// -*- C++ -*-
/*
* Copyright © 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 <http://www.gnu.org/licenses/>.
*/
%language "c++"
%require "3.0"
%defines
%define api.value.type variant
%define api.namespace {Macro}
%define parse.assert
%define parse.error verbose
%define parse.trace
%code top {
class MacroDriver;
}
%param { MacroDriver &driver }
%parse-param { ostream &out }
%locations
%initial-action
{
// Initialize the location filenames
@$.begin.filename = @$.end.filename = &driver.file;
};
%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 COMMA DEFINE LINE FOR IN IF ECHO_DIR ERROR IFDEF IFNDEF POWER
%token LPAREN RPAREN LBRACKET RBRACKET EQUAL EOL LENGTH ECHOMACROVARS SAVE
%token SEMICOLON ATSIGN
%token <int> INTEGER
%token <string> NAME STRING
%left LOGICAL_OR
%left LOGICAL_AND
%left EQUAL_EQUAL EXCLAMATION_EQUAL
%left LESS GREATER LESS_EQUAL GREATER_EQUAL
%nonassoc IN
%nonassoc COLON
%left UNION
%left INTERSECTION
%left PLUS MINUS
%left TIMES DIVIDE
%precedence UMINUS UPLUS EXCLAMATION
%nonassoc POWER
%precedence LBRACKET
%type <vector<string>> comma_name
%type <MacroValuePtr> expr
%type <vector<MacroValuePtr>> comma_expr tuple_comma_expr comprehension_clause
%%
%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), @$); }
| FOR LPAREN comma_name RPAREN IN expr
{ TYPERR_CATCH(driver.init_loop($3, $6), @$); }
| 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 comma_name { driver.push_args_into_func_env($4); } RPAREN EQUAL expr
{
TYPERR_CATCH(driver.set_string_function($2, $4, $8), @$);
driver.pop_func_env();
}
;
comma_name : NAME
{ $$ = vector<string>{$1}; }
| comma_name COMMA NAME
{ $1.push_back($3); $$ = $1; }
;
expr : INTEGER
{ $$ = make_shared<IntMV>($1); }
| STRING
{ $$ = make_shared<StringMV>(driver.replace_vars_in_str($1)); }
| LPAREN tuple_comma_expr RPAREN
{ $$ = make_shared<TupleMV>($2); }
| 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<ArrayMV>($2); }
| expr COLON expr
{ TYPERR_CATCH($$ = ArrayMV::range($1, $3), @$); }
| expr IN expr
{ TYPERR_CATCH($$ = $3->in($1), @$); }
| expr UNION expr
{ TYPERR_CATCH($$ = $1->set_union($3), @$); }
| expr INTERSECTION expr
{ TYPERR_CATCH($$ = $1->set_intersection($3), @$); }
| expr POWER expr
{ TYPERR_CATCH($$ = $1->power($3), @$); }
| LBRACKET NAME IN expr SEMICOLON
{
driver.init_comprehension(vector<string>{$2}, $4);
driver.iter_comprehension();
}
comprehension_clause RBRACKET
{
$$ = make_shared<ArrayMV>($7);
}
| LBRACKET LPAREN comma_name RPAREN IN expr SEMICOLON
{
driver.init_comprehension($3, $6);
driver.iter_comprehension();
}
comprehension_clause RBRACKET
{
$$ = make_shared<ArrayMV>($9);
}
;
comma_expr : %empty
{ $$ = vector<MacroValuePtr>{}; } // Empty array
| expr
{ $$ = vector<MacroValuePtr>{$1}; }
| comma_expr COMMA expr
{ $1.push_back($3); $$ = $1; }
;
tuple_comma_expr : %empty
{ $$ = vector<MacroValuePtr>{}; } // Empty tuple
| expr COMMA
{ $$ = vector<MacroValuePtr>{$1}; }
| expr COMMA expr
{ $$ = vector<MacroValuePtr>{$1, $3}; }
| tuple_comma_expr COMMA expr
{ $1.push_back($3); $$ = $1; }
;
/* The lexer will repeat the comprehension clause as many times as there are
elements in the set to be filtered. It also adds a dummy at-sign (@) at the
end of every repetition (for making parsing of repetitions unambiguous). */
comprehension_clause : expr ATSIGN
{
$$ = vector<MacroValuePtr>{};
driver.possibly_add_comprehension_element($$, $1);
driver.iter_comprehension();
}
| comprehension_clause expr ATSIGN
{
$$ = $1;
driver.possibly_add_comprehension_element($$, $2);
driver.iter_comprehension();
}
;
%%
void
Macro::parser::error(const Macro::parser::location_type &l,
const string &m)
{
driver.error(l, m);
}

View File

@ -1,437 +0,0 @@
/*
* Copyright © 2008-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/>.
*/
#include <cstdlib>
#include <iostream>
#include <string>
#include <regex>
#include <fstream>
#include <sstream>
#include "MacroDriver.hh"
void
MacroDriver::parse(const string &file_arg, const string &basename_arg, istream &modfile,
ostream &out, bool debug, bool no_line_macro_arg, const vector<pair<string, string>> &defines,
vector<string> path)
{
file = file_arg;
basename = basename_arg;
no_line_macro = no_line_macro_arg;
if (!defines.empty())
{
/*
Parse commandline defines separately so as not to impact the line numbers in the original .mod file
The result of the code in this conditional is to modify the `env` variable
*/
stringstream commandline_defines;
for (auto & define : defines)
try
{
stoi(define.second);
commandline_defines << "@#define " << define.first << " = " << define.second << endl;
}
catch (const invalid_argument &)
{
if (!define.second.empty() && define.second.at(0) == '[' && define.second.at(define.second.length()-1) == ']')
// If the input is an array. Issue #1578
commandline_defines << "@#define " << define.first << " = " << define.second << endl;
else
commandline_defines << "@#define " << define.first << R"( = ")" << define.second << R"(")" << endl;
}
stringstream defines_out;
lexer = make_unique<MacroFlex>(&commandline_defines, &defines_out, no_line_macro, path);
lexer->set_debug(debug);
Macro::parser defines_parser(*this, defines_out);
defines_parser.set_debug_level(debug);
defines_parser.parse();
}
/*
Copy the file into a stringstream, and add an extra end-of-line. This is a
workaround for trac ticket #73: with this workaround, MOD files ending with
an @#endif or an @#endfor - but no newline - no longer trigger an error.
*/
stringstream file_with_endl;
file_with_endl << modfile.rdbuf() << endl;
lexer = make_unique<MacroFlex>(&file_with_endl, &out, no_line_macro, path);
lexer->set_debug(debug);
Macro::parser parser(*this, out);
parser.set_debug_level(debug);
// Output first @#line statement
if (!no_line_macro)
out << R"(@#line ")" << file << R"(" 1)" << endl;
// Launch macro-processing
parser.parse();
}
void
MacroDriver::error(const Macro::parser::location_type &l, const string &m) const
{
cerr << "ERROR in macro-processor: " << l << ": " << m << endl;
exit(EXIT_FAILURE);
}
string
MacroDriver::replace_vars_in_str(const string &s) const
{
if (s.find("@") == string::npos)
return s;
string retval(s);
smatch name;
string name_str ("[A-Za-z_][A-Za-z0-9_]*");
regex name_regex (name_str); // Matches NAME
regex macro_regex (R"(@\s*\{\s*)" + name_str + R"(\s*\})"); // Matches @{NAME} with potential whitespace
for(sregex_iterator it = sregex_iterator(s.begin(), s.end(), macro_regex);
it != std::sregex_iterator(); ++it)
{
string macro(it->str());
regex_search(macro, name, name_regex);
try
{
MacroValuePtr mv;
bool found_in_func_env = false;
for (unsigned i = func_env.size(); i-- > 0;)
{
auto it = func_env[i].find(name.str());
if (it != func_env[i].end())
{
found_in_func_env = true;
mv = it->second;
}
}
if (!found_in_func_env)
mv = get_variable(name.str());
if (mv)
{
// mv will equal nullptr if we have
// @#define y = 1
// @#define func(y) = @{y}
// In this case we don't want @{y} to be replaced by its value in the environment
size_t index = retval.find(macro);
retval.replace(index, macro.length(), mv->toString());
}
}
catch (UnknownVariable &)
{
// don't replace if name not defined
}
}
return retval;
}
void
MacroDriver::set_string_function(const string &name, vector<string> &args, const MacroValuePtr &value)
{
auto smv = dynamic_pointer_cast<StringMV>(value);
if (!smv)
throw MacroValue::TypeError("The definition of a macro function must evaluate to a string");
env[name] = make_shared<FuncMV>(args, smv->value);
}
MacroValuePtr
MacroDriver::eval_string_function(const string &name, const vector<MacroValuePtr> &args)
{
auto it = env.find(name);
if (it == env.end())
throw UnknownVariable(name);
auto fmv = dynamic_pointer_cast<FuncMV>(it->second);
if (!fmv)
throw MacroValue::TypeError("You are using " + name + " as if it were a macro function");
if (fmv->args.size() != args.size())
{
cerr << "Macroprocessor: The evaluation of: " << name << " could not be completed" << endl
<< "because the number of arguments provided is different than the number of" << endl
<< "arguments used in its definition" << endl;
exit(EXIT_FAILURE);
}
int i = 0;
env_t func_env_map;
for (const auto it : fmv->args)
func_env_map[it] = args[i++];
func_env.push_back(func_env_map);
auto smv = make_shared<StringMV>(replace_vars_in_str(fmv->toString()));
pop_func_env();
return smv;
}
void
MacroDriver::push_args_into_func_env(const vector<string> &args)
{
env_t func_env_map;
for (const auto it : args)
func_env_map[it] = MacroValuePtr();
func_env.push_back(func_env_map);
}
void
MacroDriver::pop_func_env()
{
func_env.pop_back();
}
void
MacroDriver::set_variable(const string &name, MacroValuePtr value)
{
env[name] = move(value);
}
MacroValuePtr
MacroDriver::get_variable(const string &name) const noexcept(false)
{
auto it = env.find(name);
if (it == env.end())
throw UnknownVariable(name);
return it->second;
}
void
MacroDriver::init_loop(const string &name, MacroValuePtr value) noexcept(false)
{
auto mv = dynamic_pointer_cast<ArrayMV>(value);
if (!mv)
throw MacroValue::TypeError("Argument of @#for loop must be an array expression");
loop_stack.emplace(vector<string> {name}, move(mv), 0);
}
void
MacroDriver::init_loop(const vector<string> &names, MacroValuePtr value) noexcept(false)
{
auto mv = dynamic_pointer_cast<ArrayMV>(value);
if (!mv)
throw MacroValue::TypeError("Argument of @#for loop must be an array expression");
loop_stack.emplace(names, move(mv), 0);
}
bool
MacroDriver::iter_loop() noexcept(false)
{
if (loop_stack.empty())
throw "No loop on which to iterate!";
int &i = get<2>(loop_stack.top());
auto mv = get<1>(loop_stack.top());
vector<string> &names = get<0>(loop_stack.top());
if (i >= static_cast<int>(mv->values.size()))
{
loop_stack.pop();
return false;
}
else
{
if (names.size() == 1)
env[names.at(0)] = mv->values[i++];
else
{
auto tmv = dynamic_pointer_cast<TupleMV>(mv->values[i++]);
if (!tmv)
throw MacroValue::TypeError("Argument of @#for loop must be an array expression of tuples");
if (tmv->values.size() != names.size())
{
cerr << "Error in for loop: tuple in array contains " << tmv->length()
<< " elements while you are assigning to " << names.size() << " variables."
<< endl;
exit(EXIT_FAILURE);
}
for (auto &name: names)
{
auto idx = &name - &names[0];
env[name] = tmv->values.at(idx);
}
}
return true;
}
}
void
MacroDriver::init_comprehension(const vector<string> &names, MacroValuePtr value)
{
auto mv = dynamic_pointer_cast<ArrayMV>(value);
if (!mv)
throw MacroValue::TypeError("In a comprehension, the expression after the 'in' keyword must be an array");
comprehension_stack.emplace(names, move(mv), 0);
}
int
MacroDriver::get_comprehension_iter_nb() const
{
assert(!comprehension_stack.empty());
auto &mv = get<1>(comprehension_stack.top());
return mv->values.size();
}
void
MacroDriver::iter_comprehension()
{
assert(!comprehension_stack.empty());
int &i = get<2>(comprehension_stack.top());
auto &mv = get<1>(comprehension_stack.top());
vector<string> &names = get<0>(comprehension_stack.top());
assert(i <= static_cast<int>(mv->values.size()));
if (i == static_cast<int>(mv->values.size()))
comprehension_stack.pop();
else
{
if (names.size() == 1)
env[names.at(0)] = mv->values[i++];
else
{
auto tmv = dynamic_pointer_cast<TupleMV>(mv->values[i++]);
if (!tmv)
throw MacroValue::TypeError("The expression after the 'in' keyword must be an array expression of tuples");
if (tmv->values.size() != names.size())
{
cerr << "Error in comprehension loop: tuple in array contains " << tmv->length()
<< " elements while you are assigning to " << names.size() << " variables."
<< endl;
exit(EXIT_FAILURE);
}
for (auto &name: names)
{
auto idx = &name - &names[0];
env[name] = tmv->values.at(idx);
}
}
}
}
void
MacroDriver::possibly_add_comprehension_element(vector<MacroValuePtr> &v, MacroValuePtr test_expr) const
{
auto ival = dynamic_pointer_cast<IntMV>(test_expr);
if (!ival)
throw MacroValue::TypeError("In a comprehension, the expression after the semicolon must evaluate to an integer");
if (ival->value)
{
assert(!comprehension_stack.empty());
const int &i = get<2>(comprehension_stack.top());
auto &mv = get<1>(comprehension_stack.top());
v.push_back(mv->values.at(i-1));
}
}
void
MacroDriver::begin_if(const MacroValuePtr &value) noexcept(false)
{
auto ival = dynamic_pointer_cast<IntMV>(value);
if (!ival)
throw MacroValue::TypeError("Argument of @#if must be an integer");
last_if = static_cast<bool>(ival->value);
}
void
MacroDriver::begin_ifdef(const string &name)
{
try
{
get_variable(name);
begin_if(make_shared<IntMV>(1));
}
catch (UnknownVariable &)
{
begin_if(make_shared<IntMV>(0));
}
}
void
MacroDriver::begin_ifndef(const string &name)
{
try
{
get_variable(name);
begin_if(make_shared<IntMV>(0));
}
catch (UnknownVariable &)
{
begin_if(make_shared<IntMV>(1));
}
}
void
MacroDriver::echo(const Macro::parser::location_type &l, const MacroValuePtr &value) const noexcept(false)
{
auto sval = dynamic_pointer_cast<StringMV>(value);
if (!sval)
throw MacroValue::TypeError("Argument of @#echo must be a string");
cerr << "ECHO in macro-processor: " << l << ": " << sval->value << endl;
}
void
MacroDriver::error(const Macro::parser::location_type &l, const MacroValuePtr &value) const noexcept(false)
{
auto sval = dynamic_pointer_cast<StringMV>(value);
if (!sval)
throw MacroValue::TypeError("Argument of @#error must be a string");
error(l, sval->value);
}
string
MacroDriver::printvars(const Macro::parser::location_type &l, const bool tostdout) const
{
if (tostdout)
{
cout << "Macroprocessor: Printing macro variable values from " << file
<< " at line " << l.begin.line << endl;
for (const auto & it : env)
{
cout << " ";
auto fmv = dynamic_pointer_cast<FuncMV>(it.second);
if (!fmv)
cout << it.first << " = " << it.second->print() << endl;
else
cout << it.first << it.second->print() << endl;
}
cout << endl;
return "";
}
stringstream intomfile;
if (!no_line_macro)
intomfile << R"(@#line ")" << file << R"(" )" << l.begin.line << endl;
for (const auto & it : env)
intomfile<< "options_.macrovars_line_" << l.begin.line << "." << it.first << " = " << it.second->print() << ";" << endl;
return intomfile.str();
}

View File

@ -1,311 +0,0 @@
/*
* Copyright © 2008-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/>.
*/
#ifndef _MACRO_DRIVER_HH
#define _MACRO_DRIVER_HH
#ifdef _PARSING_DRIVER_HH
# error Impossible to include both ParsingDriver.hh and MacroDriver.hh
#endif
#include <string>
#include <iostream>
#include <stack>
#include <map>
#include <set>
#include <utility>
#include "MacroValue.hh"
#include "MacroBison.hh"
using namespace std;
// Declare MacroFlexLexer class
#ifndef __FLEX_LEXER_H
# define yyFlexLexer MacroFlexLexer
# include <FlexLexer.h>
# undef yyFlexLexer
#endif
//! The lexer class
/*! Actually it was necessary to subclass the MacroFlexLexer class generated by Flex,
since the prototype for MacroFlexLexer::yylex() was not convenient.
*/
class MacroFlex : public MacroFlexLexer
{
private:
//! Used to backup all the information related to a given scanning context
class ScanContext
{
public:
istream *input;
struct yy_buffer_state *buffer;
const Macro::parser::location_type yylloc;
const bool is_for_context;
const string for_body;
const Macro::parser::location_type for_body_loc;
const bool is_comprehension_context;
const string comprehension_clause;
const Macro::parser::location_type comprehension_clause_loc;
const int comprehension_start_condition;
const int comprehension_iter_nb;
ScanContext(istream *input_arg, struct yy_buffer_state *buffer_arg,
Macro::parser::location_type &yylloc_arg, bool is_for_context_arg,
string for_body_arg,
Macro::parser::location_type &for_body_loc_arg,
bool is_comprehension_context_arg,
string comprehension_clause_arg,
Macro::parser::location_type &comprehension_clause_loc_arg,
int comprehension_start_condition_arg,
int comprehension_iter_nb_arg) :
input(input_arg), buffer(buffer_arg), yylloc(yylloc_arg), is_for_context(is_for_context_arg),
for_body(move(for_body_arg)), for_body_loc(for_body_loc_arg),
is_comprehension_context{is_comprehension_context_arg},
comprehension_clause{comprehension_clause_arg},
comprehension_clause_loc{comprehension_clause_loc_arg},
comprehension_start_condition{comprehension_start_condition_arg},
comprehension_iter_nb{comprehension_iter_nb_arg}
{
}
};
//! The stack used to keep track of nested scanning contexts
stack<ScanContext> context_stack;
//! Input stream used for initialization of current scanning context
/*! Kept for deletion at end of current scanning buffer */
istream *input;
//! Should we omit the @#line statements ?
const bool no_line_macro;
//! The paths to search when looking for .mod files
vector<string> path;
//! True iff current context is the body of a loop
bool is_for_context{false};
//! If current context is the body of a loop, contains the string of the loop body
string for_body;
//! If current context is the body of a loop, contains the location of the beginning of the body
Macro::parser::location_type for_body_loc;
//! Temporary variable used in FOR_BODY mode
string for_body_tmp;
//! Temporary variable used in FOR_BODY mode
Macro::parser::location_type for_body_loc_tmp;
//! Temporary variable used in FOR_BODY mode. Keeps track of the location of the @#for statement, for reporting messages
Macro::parser::location_type for_stmt_loc_tmp;
//! Temporary variable used in FOR_BODY mode. Keeps track of number of nested @#for/@#endfor
int nested_for_nb;
//! Set to true while parsing a FOR statement (only the statement, not the loop body)
bool reading_for_statement{false};
//! Temporary variable used in THEN_BODY and ELSE_BODY modes. Keeps track of number of nested @#if
int nested_if_nb;
//! Temporary variable used in THEN_BODY mode
string then_body_tmp;
//! Temporary variable used in THEN_BODY mode
Macro::parser::location_type then_body_loc_tmp;
//! Temporary variable used in THEN_BODY mode. Keeps track of the location of the @#if statement, for reporting messages
Macro::parser::location_type if_stmt_loc_tmp;
//! Temporary variable used in ELSE_BODY mode
string else_body_tmp;
//! Temporary variable used in ELSE_BODY mode
Macro::parser::location_type else_body_loc_tmp;
//! Set to true while parsing an IF statement (only the statement, not the body)
bool reading_if_statement{false};
//! Set to true while parsing the comprehension in a new buffer
bool is_comprehension_context{false};
//! Counter for the current comprehension
int comprehension_iter_nb{0};
//! The lexer start condition (EXPR or STMT) before entering the comprehension
int comprehension_start_condition;
//! Number of nested comprehensions, used during the construction of the new lexer buffer
int nested_comprehension_nb{0};
//! Temporary stores for the new lexer buffer
string comprehension_clause, comprehension_clause_tmp;
//! Stores for the location of the comprehension clause
Macro::parser::location_type comprehension_clause_loc, comprehension_clause_loc_tmp;
//! Output the @#line declaration
void output_line(Macro::parser::location_type *yylloc) const;
//! Save current scanning context
void save_context(Macro::parser::location_type *yylloc);
//! Restore last scanning context
void restore_context(Macro::parser::location_type *yylloc);
//! pushes the colon-separated paths passed to @#includepath onto the path vector
void push_path(string *includepath, Macro::parser::location_type *yylloc,
MacroDriver &driver);
//! Saves current scanning context and create a new context with content of filename
/*! Filename must be a newly allocated string which will be deleted by the lexer */
void create_include_context(string *filename, Macro::parser::location_type *yylloc,
MacroDriver &driver);
//! Saves current scanning context and create a new context based on the "then" body
void create_then_context(Macro::parser::location_type *yylloc);
//! Saves current scanning context and create a new context based on the "else" body
void create_else_context(Macro::parser::location_type *yylloc);
//! Initialise a new flex buffer with the loop body
void new_loop_body_buffer(Macro::parser::location_type *yylloc);
//! Initialize a new flex buffer with the comprehension conditional clause
void new_comprehension_clause_buffer(Macro::parser::location_type *yylloc);
public:
MacroFlex(istream *in, ostream *out, bool no_line_macro_arg, vector<string> path_arg);
MacroFlex(const MacroFlex &) = delete;
MacroFlex(MacroFlex &&) = delete;
MacroFlex & operator=(const MacroFlex &) = delete;
MacroFlex & operator=(MacroFlex &&) = delete;
//! The main lexing function
Macro::parser::token_type lex(Macro::parser::semantic_type *yylval,
Macro::parser::location_type *yylloc,
MacroDriver &driver);
};
//! Implements the macro expansion using a Flex scanner and a Bison parser
class MacroDriver
{
friend class MacroValue;
private:
// The map defining an environment
using env_t = map<string, MacroValuePtr>;
//! Environment: maps macro variables to their values
env_t env;
//! Environment for function currently being evaluated
vector<env_t> func_env;
//! Stack used to keep track of (possibly nested) loops
//! First element is loop variable name(s)
//! Second is the array over which iteration is done
//! Third is subscript to be used by next call of iter_loop() (beginning with 0) */
stack<tuple<vector<string>, shared_ptr<ArrayMV>, int>> loop_stack;
stack<tuple<vector<string>, shared_ptr<ArrayMV>, int>> comprehension_stack;
public:
MacroDriver() = default;
MacroDriver(const MacroDriver &) = delete;
MacroDriver(MacroDriver &&) = delete;
MacroDriver & operator=(const MacroDriver &) = delete;
MacroDriver & operator=(MacroDriver &&) = delete;
//! Exception thrown when value of an unknown variable is requested
class UnknownVariable
{
public:
const string name;
explicit UnknownVariable(string name_arg) : name{move(name_arg)}
{
}
};
//! Starts parsing a file, returns output in out
/*! \param no_line_macro should we omit the @#line statements ? */
void parse(const string &file_arg, const string &basename_arg, istream &modfile, ostream &out, bool debug, bool no_line_macro_arg,
const vector<pair<string, string>> &defines, vector<string> path);
//! Name of main file being parsed
string file;
//! Basename of main file being parsed
string basename;
//! Whether or not to print @#line
bool no_line_macro;
//! Reference to the lexer
unique_ptr<MacroFlex> lexer;
//! Used to store the value of the last @#if condition
bool last_if;
//! Error handler
void error(const Macro::parser::location_type &l, const string &m) const;
//! Print variables
string printvars(const Macro::parser::location_type &l, const bool save) const;
//! Set a variable
void set_variable(const string &name, MacroValuePtr value);
//! Replace "@{x}" with the value of x (if it exists in the environment) in a string
//! Check for variable existence first in func_env before checking in env
string replace_vars_in_str(const string &s) const;
//! Set a function with arguments
void set_string_function(const string &name, vector<string> &args, const MacroValuePtr &value);
//! Push function arguments onto func_env stack setting equal to NULL
void push_args_into_func_env(const vector<string> &args);
//! Remove last entry in func_env vector
void pop_func_env();
//! Evaluate a function with arguments
MacroValuePtr eval_string_function(const string &name, const vector<MacroValuePtr> &args);
//! Get a variable
MacroValuePtr get_variable(const string &name) const noexcept(false);
//! Initiate a for loop
/*! Does not set name = value[1]. You must call iter_loop() for that. */
void init_loop(const string &name, MacroValuePtr value) noexcept(false);
/*! Same as above but for looping over tuple array */
void init_loop(const vector<string> &names, MacroValuePtr value) noexcept(false);
//! Iterate innermost loop
/*! Returns false if iteration is no more possible (end of loop);
in that case it destroys the pointer given to init_loop() */
bool iter_loop() noexcept(false);
//! Initializes the evaluation of a comprehension
void init_comprehension(const vector<string> &names, MacroValuePtr value);
//! Iterates during the evaluation of the comprehension
void iter_comprehension();
//! Helper to construct the value corresponding to the comprehension
void possibly_add_comprehension_element(vector<MacroValuePtr> &v, MacroValuePtr test_expr) const;
//! Returns the size of the set over which the current comprehension iterates
int get_comprehension_iter_nb() const;
//! Begins an @#if statement
void begin_if(const MacroValuePtr &value) noexcept(false);
//! Begins an @#ifdef statement
void begin_ifdef(const string &name);
//! Begins an @#ifndef statement
void begin_ifndef(const string &name);
//! Executes @#echo directive
void echo(const Macro::parser::location_type &l, const MacroValuePtr &value) const noexcept(false);
//! Executes @#error directive
void error(const Macro::parser::location_type &l, const MacroValuePtr &value) const noexcept(false);
};
#endif // ! MACRO_DRIVER_HH

View File

@ -1,645 +0,0 @@
/* -*- C++ -*- */
/*
* Copyright © 2008-2017 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/>.
*/
%{
using namespace std;
#include <fstream>
#include <boost/algorithm/string/trim.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/tokenizer.hpp>
#include "MacroDriver.hh"
// Announce to Flex the prototype we want for lexing function
#define YY_DECL \
Macro::parser::token_type \
MacroFlex::lex(Macro::parser::semantic_type *yylval, \
Macro::parser::location_type *yylloc, \
MacroDriver &driver)
// Shortcut to access tokens defined by Bison
using token = Macro::parser::token;
/* By default yylex returns int, we use token_type.
Unfortunately yyterminate by default returns 0, which is
not of token_type. */
#define yyterminate() return Macro::parser::token_type (0);
%}
%option c++
%option prefix="Macro"
%option case-insensitive noyywrap nounput batch debug never-interactive
%x STMT
%x EXPR
%x FOR_BODY
%x THEN_BODY
%x ELSE_BODY
%x COMPREHENSION_CLAUSE
%{
// Increments location counter for every token read
#define YY_USER_ACTION yylloc->columns(yyleng);
%}
SPC [ \t]+
EOL (\r)?\n
CONT \\\\
%%
/* Code put at the beginning of yylex() */
%{
// Reset location before reading token
yylloc->step();
%}
<INITIAL>^{SPC}*@#{SPC}*includepath{SPC}+\"([^\"\r\n:;|<>]*){1}(:[^\"\r\n:;|<>]*)*\"{SPC}*{EOL} {
yylloc->lines(1);
yylloc->step();
// Get path
string *includepath = new string(yytext);
int dblq_idx1 = includepath->find('"');
int dblq_idx2 = includepath->find('"', dblq_idx1 + 1);
includepath->erase(dblq_idx2);
includepath->erase(0, dblq_idx1 + 1);
push_path(includepath, yylloc, driver);
BEGIN(INITIAL);
}
<INITIAL>^{SPC}*@#{SPC}*includepath{SPC}+[^\"\r\n]*{SPC}*{EOL} {
yylloc->lines(1);
yylloc->step();
// Get variable name
string pathvar = string(yytext);
int dblq_idx1 = pathvar.find("includepath");
pathvar.erase(0, dblq_idx1 + 11);
pathvar.erase(0, pathvar.find_first_not_of(" \t"));
size_t p = pathvar.find_last_not_of(" \t\n\r");
if (string::npos != p)
pathvar.erase(p+1);
string *includepath = nullptr;
try
{
includepath = new string(driver.get_variable(pathvar)->toString());
}
catch(MacroDriver::UnknownVariable(&e))
{
driver.error(*yylloc, "Unknown variable: " + pathvar);
}
push_path(includepath, yylloc, driver);
BEGIN(INITIAL);
}
<INITIAL>^{SPC}*@#{SPC}*include{SPC}+\"[^\"\r\n]*\"{SPC}*{EOL} {
yylloc->lines(1);
yylloc->step();
// Get filename
string *filename = new string(yytext);
int dblq_idx1 = filename->find('"');
int dblq_idx2 = filename->find('"', dblq_idx1 + 1);
filename->erase(dblq_idx2);
filename->erase(0, dblq_idx1 + 1);
create_include_context(filename, yylloc, driver);
BEGIN(INITIAL);
}
<INITIAL>^{SPC}*@#{SPC}*include{SPC}+[^\"\r\n]*{SPC}*{EOL} {
yylloc->lines(1);
yylloc->step();
// Get variable name
string modvarname = string(yytext);
int dblq_idx1 = modvarname.find("include");
modvarname.erase(0, dblq_idx1 + 7);
modvarname.erase(0, modvarname.find_first_not_of(" \t"));
size_t p = modvarname.find_last_not_of(" \t\n\r");
if (string::npos != p)
modvarname.erase(p+1);
string *filename = nullptr;
try
{
filename = new string(driver.get_variable(modvarname)->toString());
}
catch(MacroDriver::UnknownVariable(&e))
{
driver.error(*yylloc, "Unknown variable: " + modvarname);
}
create_include_context(filename, yylloc, driver);
BEGIN(INITIAL);
}
<INITIAL>^{SPC}*@# { yylloc->step(); BEGIN(STMT); }
<INITIAL>@\{ { yylloc->step(); BEGIN(EXPR); }
<EXPR>\} { BEGIN(INITIAL); return token::EOL; }
<STMT>{CONT}{SPC}*{EOL} { yylloc->lines(1); yylloc->step(); }
<STMT>{EOL} {
/* If parsing a @#for or an @#if, keep the location
for reporting message in case of error */
if (reading_for_statement)
for_stmt_loc_tmp = *yylloc;
else if (reading_if_statement)
if_stmt_loc_tmp = *yylloc;
yylloc->lines(1);
yylloc->step();
if (reading_for_statement)
{
reading_for_statement = false;
for_body_tmp.erase();
for_body_loc_tmp = *yylloc;
nested_for_nb = 0;
BEGIN(FOR_BODY);
}
else if (reading_if_statement)
{
reading_if_statement = false;
then_body_tmp.erase();
then_body_loc_tmp = *yylloc;
nested_if_nb = 0;
BEGIN(THEN_BODY);
}
else
{
#if (YY_FLEX_MAJOR_VERSION > 2) || (YY_FLEX_MAJOR_VERSION == 2 && YY_FLEX_MINOR_VERSION >= 6)
yyout << endl;
#else
*yyout << endl;
#endif
BEGIN(INITIAL);
}
return token::EOL;
}
<STMT,EXPR>{SPC}+ { yylloc->step(); }
<STMT,EXPR>[0-9]+ {
yylval->build<int>(atoi(yytext));
return token::INTEGER;
}
<STMT,EXPR>\( { return token::LPAREN; }
<STMT,EXPR>\) { return token::RPAREN; }
<STMT,EXPR>\[ { return token::LBRACKET; }
<STMT,EXPR>\] { return token::RBRACKET; }
<STMT,EXPR>: { return token::COLON; }
<STMT,EXPR>, { return token::COMMA; }
<STMT,EXPR>= { return token::EQUAL; }
<STMT,EXPR>! { return token::EXCLAMATION; }
<STMT,EXPR>"||" { return token::LOGICAL_OR; }
<STMT,EXPR>&& { return token::LOGICAL_AND; }
<STMT,EXPR>\| { return token::UNION; }
<STMT,EXPR>& { return token::INTERSECTION; }
<STMT,EXPR>\^ { return token::POWER; }
<STMT,EXPR><= { return token::LESS_EQUAL; }
<STMT,EXPR>>= { return token::GREATER_EQUAL; }
<STMT,EXPR>< { return token::LESS; }
<STMT,EXPR>> { return token::GREATER; }
<STMT,EXPR>== { return token::EQUAL_EQUAL; }
<STMT,EXPR>!= { return token::EXCLAMATION_EQUAL; }
<STMT,EXPR>\+ { return token::PLUS; }
<STMT,EXPR>- { return token::MINUS; }
<STMT,EXPR>\* { return token::TIMES; }
<STMT,EXPR>\/ { return token::DIVIDE; }
<STMT,EXPR>in { return token::IN; }
<STMT,EXPR>length { return token::LENGTH; }
<STMT,EXPR>\"[^\"]*\" {
yylval->build<string>(yytext + 1).pop_back();
return token::STRING;
}
<STMT,EXPR>; {
comprehension_clause_tmp.erase();
nested_comprehension_nb = 0;
// Save start condition (either STMT or EXPR)
comprehension_start_condition = YY_START;
// Save location
comprehension_clause_loc_tmp = *yylloc;
BEGIN(COMPREHENSION_CLAUSE);
return token::SEMICOLON;
}
<EXPR>@ { return token::ATSIGN; } // Used for separation of repeated comprehension clauses
<STMT>line { return token::LINE; }
<STMT>define { return token::DEFINE; }
<STMT>echomacrovars { return token::ECHOMACROVARS; }
<STMT>save { return token::SAVE; }
<STMT>for { reading_for_statement = true; return token::FOR; }
<STMT>endfor { driver.error(*yylloc, "@#endfor is not matched by a @#for statement"); }
<STMT>ifdef { reading_if_statement = true; return token::IFDEF; }
<STMT>ifndef { reading_if_statement = true; return token::IFNDEF; }
<STMT>if { reading_if_statement = true; return token::IF; }
<STMT>else { driver.error(*yylloc, "@#else is not matched by an @#if/@#ifdef/@#ifndef statement"); }
<STMT>endif { driver.error(*yylloc, "@#endif is not matched by an @#if/@#ifdef/@#ifndef statement"); }
<STMT>echo { return token::ECHO_DIR; }
<STMT>error { return token::ERROR; }
<STMT,EXPR>[a-z_][a-z0-9_]* {
yylval->build<string>(yytext);
return token::NAME;
}
<EXPR><<EOF>> {
if (!is_comprehension_context)
driver.error(*yylloc, "Unexpected end of file while parsing a macro expression");
else
{
if (--comprehension_iter_nb > 0)
new_comprehension_clause_buffer(yylloc);
else
{
restore_context(yylloc);
BEGIN(comprehension_start_condition);
return token::RBRACKET;
}
}
}
<STMT><<EOF>> { driver.error(*yylloc, "Unexpected end of file while parsing a macro statement"); }
<FOR_BODY>{EOL} { yylloc->lines(1); yylloc->step(); for_body_tmp.append(yytext); }
<FOR_BODY>^{SPC}*@#{SPC}*for({SPC}|{CONT}) {
nested_for_nb++;
for_body_tmp.append(yytext);
yylloc->step();
}
<FOR_BODY>. { for_body_tmp.append(yytext); yylloc->step(); }
<FOR_BODY><<EOF>> { driver.error(for_stmt_loc_tmp, "@#for loop not matched by an @#endfor or file does not end with a new line (unexpected end of file)"); }
<FOR_BODY>^{SPC}*@#{SPC}*endfor{SPC}*(\/\/.*)?{EOL} {
yylloc->lines(1);
yylloc->step();
if (nested_for_nb)
{
/* This @#endfor is not the end of the loop body,
but only that of a nested @#for loop */
nested_for_nb--;
for_body_tmp.append(yytext);
}
else
{
// Switch to loop body context, except if iterating over an empty array
if (driver.iter_loop())
{
// Save old buffer state and location
save_context(yylloc);
is_for_context = true;
for_body = for_body_tmp;
for_body_loc = for_body_loc_tmp;
new_loop_body_buffer(yylloc);
}
BEGIN(INITIAL);
}
}
<THEN_BODY>{EOL} { yylloc->lines(1); yylloc->step(); then_body_tmp.append(yytext); }
<THEN_BODY>^{SPC}*@#{SPC}*if({SPC}|{CONT}) {
nested_if_nb++;
then_body_tmp.append(yytext);
yylloc->step();
}
<THEN_BODY>^{SPC}*@#{SPC}*ifdef({SPC}|{CONT}) {
nested_if_nb++;
then_body_tmp.append(yytext);
yylloc->step();
}
<THEN_BODY>^{SPC}*@#{SPC}*ifndef({SPC}|{CONT}) {
nested_if_nb++;
then_body_tmp.append(yytext);
yylloc->step();
}
<THEN_BODY>. { then_body_tmp.append(yytext); yylloc->step(); }
<THEN_BODY><<EOF>> { driver.error(if_stmt_loc_tmp, "@#if/@#ifdef/@#ifndef not matched by an @#endif or file does not end with a new line (unexpected end of file)"); }
<THEN_BODY>^{SPC}*@#{SPC}*else{SPC}*(\/\/.*)?{EOL} {
yylloc->lines(1);
yylloc->step();
if (nested_if_nb)
then_body_tmp.append(yytext);
else
{
else_body_tmp.erase();
else_body_loc_tmp = *yylloc;
BEGIN(ELSE_BODY);
}
}
<THEN_BODY>^{SPC}*@#{SPC}*endif{SPC}*(\/\/.*)?{EOL} {
yylloc->lines(1);
yylloc->step();
if (nested_if_nb)
{
/* This @#endif is not the end of the @#if we're parsing,
but only that of a nested @#if */
nested_if_nb--;
then_body_tmp.append(yytext);
}
else
{
if (driver.last_if)
create_then_context(yylloc);
else
output_line(yylloc);
BEGIN(INITIAL);
}
}
<ELSE_BODY>{EOL} { yylloc->lines(1); yylloc->step(); else_body_tmp.append(yytext); }
<ELSE_BODY>^{SPC}*@#{SPC}*if({SPC}|{CONT}) {
nested_if_nb++;
else_body_tmp.append(yytext);
yylloc->step();
}
<ELSE_BODY>. { else_body_tmp.append(yytext); yylloc->step(); }
<ELSE_BODY><<EOF>> { driver.error(if_stmt_loc_tmp, "@#if/@#ifdef/@#ifndef not matched by an @#endif or file does not end with a new line (unexpected end of file)"); }
<ELSE_BODY>^{SPC}*@#{SPC}*endif{SPC}*(\/\/.*)?{EOL} {
yylloc->lines(1);
yylloc->step();
if (nested_if_nb)
{
/* This @#endif is not the end of the @#if we're parsing,
but only that of a nested @#if */
nested_if_nb--;
else_body_tmp.append(yytext);
}
else
{
if (driver.last_if)
create_then_context(yylloc);
else
create_else_context(yylloc);
BEGIN(INITIAL);
}
}
<COMPREHENSION_CLAUSE>{EOL} { driver.error(*yylloc, "Unexpected line break in comprehension"); }
<COMPREHENSION_CLAUSE><<EOF>> { driver.error(*yylloc, "Unexpected end of file in comprehension"); }
<COMPREHENSION_CLAUSE>[^\[\]] { comprehension_clause_tmp.append(yytext); yylloc->step(); }
<COMPREHENSION_CLAUSE>\[ { nested_comprehension_nb++; comprehension_clause_tmp.append(yytext); yylloc->step(); }
<COMPREHENSION_CLAUSE>\] {
yylloc->step();
if (nested_comprehension_nb)
{
nested_comprehension_nb--;
comprehension_clause_tmp.append(yytext);
}
else
{
int comprehension_iter_nb_tmp = driver.get_comprehension_iter_nb();
comprehension_clause_tmp.append(" @ ");
if (comprehension_iter_nb_tmp > 0)
{
// Save old buffer state and location
save_context(yylloc);
is_comprehension_context = true;
comprehension_iter_nb = comprehension_iter_nb_tmp;
comprehension_clause = comprehension_clause_tmp;
comprehension_clause_loc = comprehension_clause_loc_tmp;
new_comprehension_clause_buffer(yylloc);
}
BEGIN(EXPR);
}
}
<INITIAL><<EOF>> {
// Quit lexer if end of main file
if (context_stack.empty())
{
yyterminate();
}
// Else clean current scanning context
yy_delete_buffer(YY_CURRENT_BUFFER);
delete input;
delete yylloc->begin.filename;
/* If we are not in a loop body, or if the loop has terminated,
pop a context */
if (is_for_context && driver.iter_loop())
new_loop_body_buffer(yylloc);
else
restore_context(yylloc);
}
/* We don't use echo, because under Cygwin it will add an extra \r */
<INITIAL>{EOL} { yylloc->lines(1); yylloc->step();
#if (YY_FLEX_MAJOR_VERSION > 2) || (YY_FLEX_MAJOR_VERSION == 2 && YY_FLEX_MINOR_VERSION >= 6)
yyout << endl;
#else
*yyout << endl;
#endif
}
/* Copy everything else to output */
<INITIAL>. { yylloc->step(); ECHO; }
<*>. { driver.error(*yylloc, "Macro lexer error: '" + string(yytext) + "'"); }
%%
MacroFlex::MacroFlex(istream* in, ostream* out, bool no_line_macro_arg, vector<string> path_arg)
: MacroFlexLexer{in, out}, input{in}, no_line_macro{no_line_macro_arg}, path{path_arg}
{
}
void
MacroFlex::output_line(Macro::parser::location_type *yylloc) const
{
if (!no_line_macro)
#if (YY_FLEX_MAJOR_VERSION > 2) || (YY_FLEX_MAJOR_VERSION == 2 && YY_FLEX_MINOR_VERSION >= 6)
const_cast<ostream&>(yyout)
#else
*yyout
#endif
<< endl << "@#line \"" << *yylloc->begin.filename << "\" "
<< yylloc->begin.line << endl;
}
void
MacroFlex::save_context(Macro::parser::location_type *yylloc)
{
context_stack.push(ScanContext(input, YY_CURRENT_BUFFER, *yylloc, is_for_context,
for_body, for_body_loc, is_comprehension_context,
comprehension_clause, comprehension_clause_loc,
comprehension_start_condition, comprehension_iter_nb));
}
void
MacroFlex::restore_context(Macro::parser::location_type *yylloc)
{
input = context_stack.top().input;
yy_switch_to_buffer(context_stack.top().buffer);
*yylloc = context_stack.top().yylloc;
is_for_context = context_stack.top().is_for_context;
for_body = context_stack.top().for_body;
for_body_loc = context_stack.top().for_body_loc;
if (!is_comprehension_context)
output_line(yylloc); // Dump @#line instruction
is_comprehension_context = context_stack.top().is_comprehension_context;
comprehension_clause = context_stack.top().comprehension_clause;
comprehension_clause_loc = context_stack.top().comprehension_clause_loc;
comprehension_start_condition = context_stack.top().comprehension_start_condition;
comprehension_iter_nb = context_stack.top().comprehension_iter_nb;
// Remove top of stack
context_stack.pop();
}
void
MacroFlex::push_path(string *includepath, Macro::parser::location_type *yylloc,
MacroDriver &driver)
{
using namespace boost;
vector<string> tokenizedPath;
split(tokenizedPath, *includepath, is_any_of(":"), token_compress_on);
for (vector<string>::iterator it = tokenizedPath.begin();
it != tokenizedPath.end(); it++ )
if (!it->empty())
{
trim(*it);
path.push_back(*it);
}
}
void
MacroFlex::create_include_context(string *filename, Macro::parser::location_type *yylloc,
MacroDriver &driver)
{
#ifdef _WIN32
string FILESEP = "\\";
#else
string FILESEP = "/";
#endif
save_context(yylloc);
// Open new file
input = new ifstream(*filename, ios::binary);
if (input->fail())
{
ostringstream dirs;
dirs << "." << FILESEP << endl;
for (vector<string>::const_iterator it = path.begin(); it != path.end(); it++)
{
string testfile = *it + FILESEP + *filename;
input = new ifstream(testfile, ios::binary);
if (input->good())
break;
dirs << *it << endl;
}
if (input->fail())
driver.error(*yylloc, "Could not open " + *filename +
". The following directories were searched:\n" + dirs.str());
}
// Reset location
yylloc->begin.filename = yylloc->end.filename = filename;
yylloc->begin.line = yylloc->end.line = 1;
yylloc->begin.column = yylloc->end.column = 0;
// We are not in a loop body
is_for_context = false;
for_body.clear();
// Output @#line information
output_line(yylloc);
// Switch to new buffer
yy_switch_to_buffer(yy_create_buffer(input, YY_BUF_SIZE));
}
void
MacroFlex::create_then_context(Macro::parser::location_type *yylloc)
{
save_context(yylloc);
input = new stringstream(then_body_tmp);
*yylloc = then_body_loc_tmp;
yylloc->begin.filename = yylloc->end.filename = new string(*then_body_loc_tmp.begin.filename);
is_for_context = false;
for_body.clear();
output_line(yylloc);
yy_switch_to_buffer(yy_create_buffer(input, YY_BUF_SIZE));
}
void
MacroFlex::create_else_context(Macro::parser::location_type *yylloc)
{
save_context(yylloc);
input = new stringstream(else_body_tmp);
*yylloc = else_body_loc_tmp;
yylloc->begin.filename = yylloc->end.filename = new string(*else_body_loc_tmp.begin.filename);
is_for_context = false;
for_body.clear();
output_line(yylloc);
yy_switch_to_buffer(yy_create_buffer(input, YY_BUF_SIZE));
}
void
MacroFlex::new_loop_body_buffer(Macro::parser::location_type *yylloc)
{
input = new stringstream(for_body);
*yylloc = for_body_loc;
yylloc->begin.filename = yylloc->end.filename = new string(*for_body_loc.begin.filename);
output_line(yylloc);
yy_switch_to_buffer(yy_create_buffer(input, YY_BUF_SIZE));
}
void
MacroFlex::new_comprehension_clause_buffer(Macro::parser::location_type *yylloc)
{
input = new stringstream(comprehension_clause);
*yylloc = comprehension_clause_loc;
yylloc->begin.filename = yylloc->end.filename = new string(*comprehension_clause_loc.begin.filename);
is_for_context = false;
for_body.clear();
yy_switch_to_buffer(yy_create_buffer(input, YY_BUF_SIZE));
}
/* This implementation of MacroFlexLexer::yylex() is required to fill the
* vtable of the class MacroFlexLexer. We define the scanner's main yylex
* function via YY_DECL to reside in the MacroFlex class instead. */
#ifdef yylex
# undef yylex
#endif
int
MacroFlexLexer::yylex()
{
cerr << "MacroFlexLexer::yylex() has been called, that should never happen!" << endl;
exit(EXIT_FAILURE);
}

View File

@ -1,789 +0,0 @@
/*
* Copyright © 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 <http://www.gnu.org/licenses/>.
*/
#include <utility>
#include <cmath>
#include "MacroDriver.hh"
MacroValuePtr
MacroValue::plus(const MacroValuePtr &mv) noexcept(false)
{
throw TypeError("Operator + does not exist for this type");
}
MacroValuePtr
MacroValue::unary_plus() noexcept(false)
{
throw TypeError("Unary operator + does not exist for this type");
}
MacroValuePtr
MacroValue::minus(const MacroValuePtr &mv) noexcept(false)
{
throw TypeError("Operator - does not exist for this type");
}
MacroValuePtr
MacroValue::unary_minus() noexcept(false)
{
throw TypeError("Unary operator - does not exist for this type");
}
MacroValuePtr
MacroValue::times(const MacroValuePtr &mv) noexcept(false)
{
throw TypeError("Operator * does not exist for this type");
}
MacroValuePtr
MacroValue::divide(const MacroValuePtr &mv) noexcept(false)
{
throw TypeError("Operator / does not exist for this type");
}
shared_ptr<IntMV>
MacroValue::is_less(const MacroValuePtr &mv) noexcept(false)
{
throw TypeError("Operator < does not exist for this type");
}
shared_ptr<IntMV>
MacroValue::is_greater(const MacroValuePtr &mv) noexcept(false)
{
throw TypeError("Operator > does not exist for this type");
}
shared_ptr<IntMV>
MacroValue::is_less_equal(const MacroValuePtr &mv) noexcept(false)
{
throw TypeError("Operator <= does not exist for this type");
}
shared_ptr<IntMV>
MacroValue::is_greater_equal(const MacroValuePtr &mv) noexcept(false)
{
throw TypeError("Operator >= does not exist for this type");
}
shared_ptr<IntMV>
MacroValue::is_different(const MacroValuePtr &mv)
{
if (is_equal(mv)->value)
return make_shared<IntMV>(0);
else
return make_shared<IntMV>(1);
}
shared_ptr<IntMV>
MacroValue::logical_and(const MacroValuePtr &mv) noexcept(false)
{
throw TypeError("Operator && does not exist for this type");
}
shared_ptr<IntMV>
MacroValue::logical_or(const MacroValuePtr &mv) noexcept(false)
{
throw TypeError("Operator || does not exist for this type");
}
shared_ptr<IntMV>
MacroValue::logical_not() noexcept(false)
{
throw TypeError("Operator ! does not exist for this type");
}
MacroValuePtr
MacroValue::subscript(const MacroValuePtr &mv) noexcept(false)
{
throw TypeError("Operator [] does not exist for this type");
}
shared_ptr<IntMV>
MacroValue::length() noexcept(false)
{
throw TypeError("Length not supported for this type");
}
shared_ptr<IntMV>
MacroValue::in(const MacroValuePtr &mv) noexcept(false)
{
throw TypeError("Second argument of 'in' operator must be an array");
}
shared_ptr<ArrayMV>
MacroValue::set_union(const MacroValuePtr &mv) noexcept(false)
{
throw TypeError("Operator | does not exist for this type");
}
shared_ptr<ArrayMV>
MacroValue::set_intersection(const MacroValuePtr &mv) noexcept(false)
{
throw TypeError("Operator & does not exist for this type");
}
MacroValuePtr
MacroValue::power(const MacroValuePtr &mv) noexcept(false)
{
throw TypeError("Operator ^ does not exist for this type");
}
IntMV::IntMV(int value_arg) : value{value_arg}
{
}
MacroValuePtr
IntMV::plus(const MacroValuePtr &mv) noexcept(false)
{
auto mv2 = dynamic_pointer_cast<IntMV>(mv);
if (!mv2)
throw TypeError("Type mismatch for operands of + operator");
return make_shared<IntMV>(value + mv2->value);
}
MacroValuePtr
IntMV::unary_plus() noexcept(false)
{
return make_shared<IntMV>(value);
}
MacroValuePtr
IntMV::minus(const MacroValuePtr &mv) noexcept(false)
{
auto mv2 = dynamic_pointer_cast<IntMV>(mv);
if (!mv2)
throw TypeError("Type mismatch for operands of - operator");
return make_shared<IntMV>(value - mv2->value);
}
MacroValuePtr
IntMV::unary_minus() noexcept(false)
{
return make_shared<IntMV>(-value);
}
MacroValuePtr
IntMV::times(const MacroValuePtr &mv) noexcept(false)
{
auto mv2 = dynamic_pointer_cast<IntMV>(mv);
if (!mv2)
throw TypeError("Type mismatch for operands of * operator");
return make_shared<IntMV>(value * mv2->value);
}
MacroValuePtr
IntMV::divide(const MacroValuePtr &mv) noexcept(false)
{
auto mv2 = dynamic_pointer_cast<IntMV>(mv);
if (!mv2)
throw TypeError("Type mismatch for operands of / operator");
if (!mv2->value)
throw DivisionByZeroError();
return make_shared<IntMV>(value / mv2->value);
}
MacroValuePtr
IntMV::power(const MacroValuePtr &mv) noexcept(false)
{
auto mv2 = dynamic_pointer_cast<IntMV>(mv);
if (!mv2)
throw TypeError("Type mismatch for operands of ^ operator");
return make_shared<IntMV>(pow(value, mv2->value));
}
shared_ptr<IntMV>
IntMV::is_less(const MacroValuePtr &mv) noexcept(false)
{
auto mv2 = dynamic_pointer_cast<IntMV>(mv);
if (!mv2)
throw TypeError("Type mismatch for operands of < operator");
return make_shared<IntMV>(value < mv2->value);
}
shared_ptr<IntMV>
IntMV::is_greater(const MacroValuePtr &mv) noexcept(false)
{
auto mv2 = dynamic_pointer_cast<IntMV>(mv);
if (!mv2)
throw TypeError("Type mismatch for operands of > operator");
return make_shared<IntMV>(value > mv2->value);
}
shared_ptr<IntMV>
IntMV::is_less_equal(const MacroValuePtr &mv) noexcept(false)
{
auto mv2 = dynamic_pointer_cast<IntMV>(mv);
if (!mv2)
throw TypeError("Type mismatch for operands of <= operator");
return make_shared<IntMV>(value <= mv2->value);
}
shared_ptr<IntMV>
IntMV::is_greater_equal(const MacroValuePtr &mv) noexcept(false)
{
auto mv2 = dynamic_pointer_cast<IntMV>(mv);
if (!mv2)
throw TypeError("Type mismatch for operands of >= operator");
return make_shared<IntMV>(value >= mv2->value);
}
shared_ptr<IntMV>
IntMV::is_equal(const MacroValuePtr &mv)
{
auto mv2 = dynamic_pointer_cast<IntMV>(mv);
if (!mv2)
return make_shared<IntMV>(0);
else
return make_shared<IntMV>(value == mv2->value);
}
shared_ptr<IntMV>
IntMV::logical_and(const MacroValuePtr &mv) noexcept(false)
{
auto mv2 = dynamic_pointer_cast<IntMV>(mv);
if (!mv2)
throw TypeError("Type mismatch for operands of && operator");
return make_shared<IntMV>(value && mv2->value);
}
shared_ptr<IntMV>
IntMV::logical_or(const MacroValuePtr &mv) noexcept(false)
{
auto mv2 = dynamic_pointer_cast<IntMV>(mv);
if (!mv2)
throw TypeError("Type mismatch for operands of || operator");
return make_shared<IntMV>(value || mv2->value);
}
shared_ptr<IntMV>
IntMV::logical_not() noexcept(false)
{
return make_shared<IntMV>(!value);
}
string
IntMV::toString()
{
return to_string(value);
}
string
IntMV::print()
{
return toString();
}
StringMV::StringMV(string value_arg) :
value{move(value_arg)}
{
}
MacroValuePtr
StringMV::plus(const MacroValuePtr &mv) noexcept(false)
{
auto mv2 = dynamic_pointer_cast<StringMV>(mv);
if (mv2)
return make_shared<StringMV>(value + mv2->value);
throw TypeError("Type mismatch for operands of + operator");
}
shared_ptr<IntMV>
StringMV::is_equal(const MacroValuePtr &mv)
{
auto mv2 = dynamic_pointer_cast<StringMV>(mv);
if (mv2)
return make_shared<IntMV>(value == mv2->value);
else
return make_shared<IntMV>(0);
}
MacroValuePtr
StringMV::subscript(const MacroValuePtr &mv) noexcept(false)
{
string result;
auto copy_element = [&](int i) {
if (i < 1 || i > static_cast<int>(value.length()))
throw OutOfBoundsError();
result.append(1, value.at(i - 1));
};
auto mv2 = dynamic_pointer_cast<IntMV>(mv);
auto mv3 = dynamic_pointer_cast<ArrayMV>(mv);
if (mv2)
copy_element(mv2->value);
else if (mv3)
for (auto &v : mv3->values)
{
auto v2 = dynamic_pointer_cast<IntMV>(v);
if (!v2)
throw TypeError("Expression inside [] must be an integer or an integer array");
copy_element(v2->value);
}
else
throw TypeError("Expression inside [] must be an integer or an integer array");
return make_shared<StringMV>(result);
}
string
StringMV::toString()
{
return value;
}
string
StringMV::print()
{
return "'" + value + "'";
}
shared_ptr<IntMV>
StringMV::length() noexcept(false)
{
return make_shared<IntMV>(value.length());
}
FuncMV::FuncMV(vector<string> args_arg, string body_arg) :
args{move(args_arg)}, body{move(body_arg)}
{
}
shared_ptr<IntMV>
FuncMV::is_equal(const MacroValuePtr &mv)
{
auto mv2 = dynamic_pointer_cast<FuncMV>(mv);
if (!mv2 || body != mv2->body)
return make_shared<IntMV>(0);
if (args.size() == mv2->args.size())
for (size_t i = 0; i < args.size(); i++)
if (args[i] != mv2->args[i])
return make_shared<IntMV>(0);
return make_shared<IntMV>(1);
}
string
FuncMV::toString()
{
return body;
}
string
FuncMV::print()
{
bool comma_flag = false;
string retval = "(";
for (auto it : args)
{
if (comma_flag)
retval += ", ";
retval += it;
comma_flag = true;
}
retval += ")";
return retval + " = '" + body + "'";
}
ArrayMV::ArrayMV(vector<MacroValuePtr> values_arg) : values{move(values_arg)}
{
}
MacroValuePtr
ArrayMV::plus(const MacroValuePtr &mv) noexcept(false)
{
auto mv2 = dynamic_pointer_cast<ArrayMV>(mv);
if (!mv2)
throw TypeError("Type mismatch for operands of + operator");
vector<MacroValuePtr> values_copy{values};
values_copy.insert(values_copy.end(), mv2->values.begin(), mv2->values.end());
return make_shared<ArrayMV>(values_copy);
}
MacroValuePtr
ArrayMV::minus(const MacroValuePtr &mv) noexcept(false)
{
auto mv2 = dynamic_pointer_cast<ArrayMV>(mv);
if (!mv2)
throw TypeError("Type mismatch for operands of - operator");
/* Highly inefficient algorithm for computing set difference
(but vector<T> is not suited for that...) */
vector<MacroValuePtr> new_values;
for (auto &it : values)
{
auto it2 = mv2->values.cbegin();
for (; it2 != mv2->values.cend(); ++it2)
if (it->is_equal(*it2)->value)
break;
if (it2 == mv2->values.cend())
new_values.push_back(it);
}
return make_shared<ArrayMV>(new_values);
}
MacroValuePtr
ArrayMV::times(const MacroValuePtr &mv) noexcept(false)
{
auto mv2 = dynamic_pointer_cast<ArrayMV>(mv);
if (!mv2)
throw TypeError("Type mismatch for operands of * operator");
vector<MacroValuePtr> new_values;
for (auto &itl : values)
for (auto &itr : mv2->values)
{
vector<MacroValuePtr> new_tuple;
auto lmvi = dynamic_pointer_cast<IntMV>(itl);
if (lmvi)
new_tuple.push_back(lmvi);
else
{
auto lmvs = dynamic_pointer_cast<StringMV>(itl);
if (lmvs)
new_tuple.push_back(lmvs);
else
{
auto lmvt = dynamic_pointer_cast<TupleMV>(itl);
if (lmvt)
new_tuple = lmvt->values;
else
{
cerr << "ArrayMV::times: unsupported type on lhs" << endl;
exit(EXIT_FAILURE);
}
}
}
auto rmvi = dynamic_pointer_cast<IntMV>(itr);
if (rmvi)
new_tuple.push_back(rmvi);
else
{
auto rmvs = dynamic_pointer_cast<StringMV>(itr);
if (rmvs)
new_tuple.push_back(rmvs);
else
{
auto rmvt = dynamic_pointer_cast<TupleMV>(itr);
if (rmvt)
for (auto &tit : rmvt->values)
new_tuple.push_back(tit);
else
{
cerr << "ArrayMV::times: unsupported type on rhs" << endl;
exit(EXIT_FAILURE);
}
}
}
new_values.push_back(make_shared<TupleMV>(new_tuple));
}
return make_shared<ArrayMV>(new_values);
}
shared_ptr<IntMV>
ArrayMV::is_equal(const MacroValuePtr &mv)
{
auto mv2 = dynamic_pointer_cast<ArrayMV>(mv);
if (!mv2 || values.size() != mv2->values.size())
return make_shared<IntMV>(0);
auto it = values.cbegin();
auto it2 = mv2->values.cbegin();
while (it != values.cend())
{
if ((*it)->is_different(*it2)->value)
return make_shared<IntMV>(0);
++it;
++it2;
}
return make_shared<IntMV>(1);
}
MacroValuePtr
ArrayMV::subscript(const MacroValuePtr &mv) noexcept(false)
{
vector<MacroValuePtr> result;
auto copy_element = [&](int i) {
if (i < 1 || i > static_cast<int>(values.size()))
throw OutOfBoundsError();
result.push_back(values[i - 1]);
};
auto mv2 = dynamic_pointer_cast<IntMV>(mv);
auto mv3 = dynamic_pointer_cast<ArrayMV>(mv);
if (mv2)
copy_element(mv2->value);
else if (mv3)
for (auto &v : mv3->values)
{
auto v2 = dynamic_pointer_cast<IntMV>(v);
if (!v2)
throw TypeError("Expression inside [] must be an integer or an integer array");
copy_element(v2->value);
}
else
throw TypeError("Expression inside [] must be an integer or an integer array");
if (result.size() > 1 || result.size() == 0)
return make_shared<ArrayMV>(result);
else
return result[0];
}
string
ArrayMV::toString()
{
ostringstream ss;
for (auto &v : values)
ss << v->toString();
return ss.str();
}
shared_ptr<IntMV>
ArrayMV::length() noexcept(false)
{
return make_shared<IntMV>(values.size());
}
string
ArrayMV::print()
{
ostringstream ss;
ss << "[";
for (auto it = values.begin();
it != values.end(); it++)
{
if (it != values.begin())
ss << ", ";
ss << (*it)->print();
}
ss << "]";
return ss.str();
}
shared_ptr<ArrayMV>
ArrayMV::append(MacroValuePtr mv) noexcept(false)
{
vector<MacroValuePtr> v{values};
v.push_back(move(mv));
return make_shared<ArrayMV>(v);
}
shared_ptr<IntMV>
ArrayMV::in(const MacroValuePtr &mv) noexcept(false)
{
for (auto &v : values)
if (v->is_equal(mv)->value)
return make_shared<IntMV>(1);
return make_shared<IntMV>(0);
}
shared_ptr<ArrayMV>
ArrayMV::range(const MacroValuePtr &mv1, const MacroValuePtr &mv2) noexcept(false)
{
auto mv1i = dynamic_pointer_cast<IntMV>(mv1);
auto mv2i = dynamic_pointer_cast<IntMV>(mv2);
if (!mv1i || !mv2i)
throw TypeError("Arguments of range operator (:) must be integers");
vector<MacroValuePtr> result;
for (int v1 = mv1i->value, v2 = mv2i->value; v1 <= v2; v1++)
result.push_back(make_shared<IntMV>(v1));
return make_shared<ArrayMV>(result);
}
shared_ptr<ArrayMV>
ArrayMV::set_union(const MacroValuePtr &mv) noexcept(false)
{
auto mv2 = dynamic_pointer_cast<ArrayMV>(mv);
if (!mv2)
throw TypeError("Arguments of the union operator (|) must be sets");
vector<MacroValuePtr> new_values = values;
for (auto &it : mv2->values)
{
bool found = false;
for (auto &nvit : new_values)
if (nvit->is_equal(it)->value)
{
found = true;
break;
}
if (!found)
new_values.push_back(it);
}
return make_shared<ArrayMV>(new_values);
}
shared_ptr<ArrayMV>
ArrayMV::set_intersection(const MacroValuePtr &mv) noexcept(false)
{
auto mv2 = dynamic_pointer_cast<ArrayMV>(mv);
if (!mv2)
throw TypeError("Arguments of the intersection operator (|) must be sets");
vector<MacroValuePtr> new_values;
for (auto &it : mv2->values)
for (auto &nvit : values)
if (nvit->is_equal(it)->value)
{
new_values.push_back(it);
break;
}
return make_shared<ArrayMV>(new_values);
}
MacroValuePtr
ArrayMV::power(const MacroValuePtr &mv) noexcept(false)
{
auto mv2 = dynamic_pointer_cast<IntMV>(mv);
if (!mv2)
throw TypeError("The second argument of the power operator (^) must be an integer");
shared_ptr<ArrayMV> retval = make_shared<ArrayMV>(values);
for (int i = 1; i < mv2->value; i++)
{
shared_ptr<MacroValue> mvp = retval->times(make_shared<ArrayMV>(values));
retval = make_shared<ArrayMV>(dynamic_pointer_cast<ArrayMV>(mvp)->values);
}
return retval;
}
TupleMV::TupleMV(vector<MacroValuePtr> values_arg) : values{move(values_arg)}
{
}
shared_ptr<IntMV>
TupleMV::is_equal(const MacroValuePtr &mv)
{
auto mv2 = dynamic_pointer_cast<TupleMV>(mv);
if (!mv2 || values.size() != mv2->values.size())
return make_shared<IntMV>(0);
auto it = values.cbegin();
auto it2 = mv2->values.cbegin();
while (it != values.cend())
{
if ((*it)->is_different(*it2)->value)
return make_shared<IntMV>(0);
++it;
++it2;
}
return make_shared<IntMV>(1);
}
MacroValuePtr
TupleMV::subscript(const MacroValuePtr &mv) noexcept(false)
{
vector<MacroValuePtr> result;
auto copy_element = [&](int i) {
if (i < 1 || i > static_cast<int>(values.size()))
throw OutOfBoundsError();
result.push_back(values[i - 1]);
};
auto mv2 = dynamic_pointer_cast<IntMV>(mv);
auto mv3 = dynamic_pointer_cast<TupleMV>(mv);
if (mv2)
copy_element(mv2->value);
else if (mv3)
for (auto &v : mv3->values)
{
auto v2 = dynamic_pointer_cast<IntMV>(v);
if (!v2)
throw TypeError("Expression inside [] must be an integer or an integer array");
copy_element(v2->value);
}
else
throw TypeError("Expression inside [] must be an integer or an integer array");
if (result.size() > 1 || result.size() == 0)
return make_shared<TupleMV>(result);
else
return result[0];
}
string
TupleMV::toString()
{
ostringstream ss;
bool print_comma = false;
ss << "(";
for (auto &v : values)
{
if (print_comma)
ss << ", ";
else
print_comma = true;
ss << v->toString();
}
ss << ")";
return ss.str();
}
shared_ptr<IntMV>
TupleMV::length() noexcept(false)
{
return make_shared<IntMV>(values.size());
}
string
TupleMV::print()
{
ostringstream ss;
ss << "(";
for (auto it = values.begin();
it != values.end(); it++)
{
if (it != values.begin())
ss << ", ";
ss << (*it)->print();
}
ss << ")";
return ss.str();
}
shared_ptr<IntMV>
TupleMV::in(const MacroValuePtr &mv) noexcept(false)
{
for (auto &v : values)
if (v->is_equal(mv)->value)
return make_shared<IntMV>(1);
return make_shared<IntMV>(0);
}

View File

@ -1,250 +0,0 @@
/*
* Copyright © 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 <http://www.gnu.org/licenses/>.
*/
#ifndef _MACRO_VALUE_HH
#define _MACRO_VALUE_HH
#include <string>
#include <utility>
#include <vector>
#include <sstream>
#include <memory>
using namespace std;
class MacroDriver;
class MacroValue;
class IntMV;
class ArrayMV;
using MacroValuePtr = shared_ptr<MacroValue>;
//! Base class for representing values in macro language
/*! All its derived types are immutable, so that we don't need to carry const
qualifiers everywhere in the code */
class MacroValue
{
public:
virtual ~MacroValue() = default;
//! Exception thrown when type error occurs in macro language
class TypeError
{
public:
const string message;
explicit TypeError(string message_arg) : message{move(message_arg)}
{
};
};
//! Exception thrown when doing an out-of-bounds access through [] operator
class OutOfBoundsError
{
};
//! Exception thrown when dividing by zero
class DivisionByZeroError
{
};
//! Applies + operator
virtual MacroValuePtr plus(const MacroValuePtr &mv) noexcept(false);
//! Applies unary + operator
virtual MacroValuePtr unary_plus() noexcept(false);
//! Applies - operator
virtual MacroValuePtr minus(const MacroValuePtr &mv) noexcept(false);
//! Applies unary - operator
virtual MacroValuePtr unary_minus() noexcept(false);
//! Applies * operator
virtual MacroValuePtr times(const MacroValuePtr &mv) noexcept(false);
//! Applies / operator
virtual MacroValuePtr divide(const MacroValuePtr &mv) noexcept(false);
//! Less comparison
/*! Returns an IntMV, equal to 0 or 1 */
virtual shared_ptr<IntMV> is_less(const MacroValuePtr &mv) noexcept(false);
//! Greater comparison
/*! Returns an IntMV, equal to 0 or 1 */
virtual shared_ptr<IntMV> is_greater(const MacroValuePtr &mv) noexcept(false);
//! Less or equal comparison
/*! Returns an IntMV, equal to 0 or 1 */
virtual shared_ptr<IntMV> is_less_equal(const MacroValuePtr &mv) noexcept(false);
//! Greater or equal comparison
/*! Returns an IntMV, equal to 0 or 1 */
virtual shared_ptr<IntMV> is_greater_equal(const MacroValuePtr &mv) noexcept(false);
//! Equal comparison
/*! Returns an IntMV, equal to 0 or 1 */
virtual shared_ptr<IntMV> is_equal(const MacroValuePtr &mv) = 0;
//! Not equal comparison
/*! Returns an IntMV, equal to 0 or 1 */
shared_ptr<IntMV> is_different(const MacroValuePtr &mv);
//! Applies && operator
virtual shared_ptr<IntMV> logical_and(const MacroValuePtr &mv) noexcept(false);
//! Applies || operator
virtual shared_ptr<IntMV> logical_or(const MacroValuePtr &mv) noexcept(false);
//! Applies unary ! operator
virtual shared_ptr<IntMV> logical_not() noexcept(false);
//! Applies [] operator
virtual MacroValuePtr subscript(const MacroValuePtr &mv) noexcept(false);
//! Converts value to string
virtual string toString() = 0;
//! Converts value to be printed
virtual string print() = 0;
//! Gets length
virtual shared_ptr<IntMV> length() noexcept(false);
//! Applies "in" operator
/*! The argument is the element to be tested for inclusion. Returns an IntMV, equal to 0 or 1 */
virtual shared_ptr<IntMV> in(const MacroValuePtr &mv) noexcept(false);
//! Creates the union of two sets
virtual shared_ptr<ArrayMV> set_union(const MacroValuePtr &mv) noexcept(false);
//! Creates the intersection of two sets
virtual shared_ptr<ArrayMV> set_intersection(const MacroValuePtr &mv) noexcept(false);
//! Power as shortcut for Cartesian product
virtual MacroValuePtr power(const MacroValuePtr &mv) noexcept(false);
};
//! Represents an integer value in macro language
class IntMV : public MacroValue
{
public:
explicit IntMV(int value_arg);
//! Underlying integer value
const int value;
//! Computes arithmetic addition
MacroValuePtr plus(const MacroValuePtr &mv) noexcept(false) override;
//! Unary plus
/*! Returns itself */
MacroValuePtr unary_plus() noexcept(false) override;
//! Computes arithmetic substraction
MacroValuePtr minus(const MacroValuePtr &mv) noexcept(false) override;
//! Computes opposite
MacroValuePtr unary_minus() noexcept(false) override;
//! Computes arithmetic multiplication
MacroValuePtr times(const MacroValuePtr &mv) noexcept(false) override;
//! Computes arithmetic division
MacroValuePtr divide(const MacroValuePtr &mv) noexcept(false) override;
MacroValuePtr power(const MacroValuePtr &mv) noexcept(false) override;
shared_ptr<IntMV> is_less(const MacroValuePtr &mv) noexcept(false) override;
shared_ptr<IntMV> is_greater(const MacroValuePtr &mv) noexcept(false) override;
shared_ptr<IntMV> is_less_equal(const MacroValuePtr &mv) noexcept(false) override;
shared_ptr<IntMV> is_greater_equal(const MacroValuePtr &mv) noexcept(false) override;
shared_ptr<IntMV> is_equal(const MacroValuePtr &mv) override;
//! Computes logical and
shared_ptr<IntMV> logical_and(const MacroValuePtr &mv) noexcept(false) override;
//! Computes logical or
shared_ptr<IntMV> logical_or(const MacroValuePtr &mv) noexcept(false) override;
//! Computes logical negation
shared_ptr<IntMV> logical_not() noexcept(false) override;
string toString() override;
string print() override;
};
//! Represents a string value in macro language
class StringMV : public MacroValue
{
public:
explicit StringMV(string value_arg);
//! Underlying string value
const string value;
//! Computes string concatenation
MacroValuePtr plus(const MacroValuePtr &mv) noexcept(false) override;
shared_ptr<IntMV> is_equal(const MacroValuePtr &mv) override;
//! Subscripting operator
/*! Argument must be an ArrayMV<int>. Indexes begin at 1. Returns a StringMV. */
MacroValuePtr subscript(const MacroValuePtr &mv) noexcept(false) override;
//! Returns underlying string value
string toString() override;
string print() override;
shared_ptr<IntMV> length() noexcept(false) override;
};
class FuncMV : public MacroValue
{
public:
FuncMV(vector<string> args, string body_arg);
//! Function args & body
const vector<string> args;
const string body;
shared_ptr<IntMV> is_equal(const MacroValuePtr &mv) override;
string toString() override;
string print() override;
};
//! Represents an array in macro language
class ArrayMV : public MacroValue
{
public:
explicit ArrayMV(vector<MacroValuePtr> values_arg);
//! Underlying vector
const vector<MacroValuePtr> values;
//! Computes array concatenation
/*! Both array must be of same type */
MacroValuePtr plus(const MacroValuePtr &mv) noexcept(false) override;
//! Returns an array in which the elements of the second array have been removed from the first
/*! It is close to a set difference operation, except that if an element appears two times in the first array, it will also be in the returned value (provided it is not in the second array) */
MacroValuePtr minus(const MacroValuePtr &mv) noexcept(false) override;
shared_ptr<IntMV> is_equal(const MacroValuePtr &mv) override;
//! Subscripting operator
/*! Argument must be an ArrayMV<int>. Indexes begin at 1.
If argument is a one-element array, returns the corresponding array element.
Otherwise returns an array. */
MacroValuePtr subscript(const MacroValuePtr &mv) noexcept(false) override;
//! Returns a string containing the concatenation of string representations of elements
string toString() override;
string print() override;
//! Gets length
shared_ptr<IntMV> length() noexcept(false) override;
shared_ptr<ArrayMV> append(MacroValuePtr mv) noexcept(false);
shared_ptr<IntMV> in(const MacroValuePtr &mv) noexcept(false) override;
/*! Arguments must be of type IntMV.
Returns an integer array containing all integers between mv1 and mv2.
If mv2 < mv1, returns an empty range (for consistency with MATLAB).
*/
static shared_ptr<ArrayMV> range(const MacroValuePtr &mv1, const MacroValuePtr &mv2) noexcept(false);
shared_ptr<ArrayMV> set_union(const MacroValuePtr &mvp) noexcept(false) override;
shared_ptr<ArrayMV> set_intersection(const MacroValuePtr &mvp) noexcept(false) override;
// Computes the Cartesian product of two sets
MacroValuePtr times(const MacroValuePtr &mv) noexcept(false) override;
// Shortcut for Cartesian product of two sets
MacroValuePtr power(const MacroValuePtr &mv) noexcept(false) override;
};
//! Represents a tuple value in macro language
class TupleMV : public MacroValue
{
public:
explicit TupleMV(vector<MacroValuePtr> values_arg);
//! Underlying vector
const vector<MacroValuePtr> values;
shared_ptr<IntMV> is_equal(const MacroValuePtr &mv) override;
//! Subscripting operator
/*! Argument must be an ArrayMV<int>. Indexes begin at 1. */
MacroValuePtr subscript(const MacroValuePtr &mv) noexcept(false) override;
string toString() override;
string print() override;
shared_ptr<IntMV> length() noexcept(false) override;
shared_ptr<IntMV> in(const MacroValuePtr &mv) noexcept(false) override;
};
#endif

View File

@ -1,25 +1,30 @@
noinst_LIBRARIES = libmacro.a
BUILT_SOURCES = MacroBison.hh stack.hh position.hh location.hh MacroBison.cc MacroFlex.cc
BUILT_SOURCES = Parser.hh stack.hh position.hh location.hh Parser.cc Tokenizer.cc
# We don't put BUILT_SOURCES in libmacro_a_SOURCES, otherwise MacroBison.o and MacroFlex.o will be linked two times (Automake translates MacroFlex.ll and MacroBison.yy into their respective .o); so BUILT_SOURCES is in EXTRA_DIST
# We don't put BUILT_SOURCES in libmacro_a_SOURCES, otherwise Parser.o and Tokenizer.o will be linked two times (Automake translates Tokenizer.ll and Parser.yy into their respective .o); so BUILT_SOURCES is in EXTRA_DIST
libmacro_a_SOURCES = \
MacroFlex.ll \
MacroBison.yy \
MacroDriver.cc \
MacroDriver.hh \
MacroValue.cc \
MacroValue.hh
Tokenizer.ll \
Parser.yy \
ForwardDeclarationsAndEnums.hh \
Driver.cc \
Driver.hh \
Environment.cc \
Environment.hh \
Expressions.cc \
Expressions.hh \
Directives.cc \
Directives.hh
EXTRA_DIST = $(BUILT_SOURCES)
# The -I.. is for <FlexLexer.h>
libmacro_a_CPPFLAGS = $(BOOST_CPPFLAGS) -I..
MacroFlex.cc: MacroFlex.ll
$(LEX) -o MacroFlex.cc MacroFlex.ll
Tokenizer.cc: Tokenizer.ll
$(LEX) -o Tokenizer.cc Tokenizer.ll
libmacro_a-MacroFlex.$(OBJEXT): CXXFLAGS += -Wno-old-style-cast
libmacro_a-Tokenizer.$(OBJEXT): CXXFLAGS += -Wno-old-style-cast
MacroBison.cc MacroBison.hh location.hh stack.hh position.hh: MacroBison.yy
$(YACC) -W -o MacroBison.cc MacroBison.yy
Parser.cc Parser.hh location.hh stack.hh position.hh: Parser.yy
$(YACC) -W -o Parser.cc Parser.yy

404
src/macro/Parser.yy Normal file
View File

@ -0,0 +1,404 @@
// -*- 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; }
#include <ostream>
#include <vector>
#include <string>
using namespace std;
}
%param { macro::Driver &driver }
%locations
%initial-action
{
// Initialize the location filenames
@$.begin.filename = @$.end.filename = &driver.file;
};
%code requires {
#include "Expressions.hh"
#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 LENGTH ECHOMACROVARS
%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
%left OR
%left AND
%left EQUAL_EQUAL NOT_EQUAL
%left LESS GREATER LESS_EQUAL GREATER_EQUAL
%nonassoc IN
%nonassoc COLON
%left UNION
%left INTERSECTION
%left PLUS MINUS
%left TIMES DIVIDE
%precedence UMINUS UPLUS NOT
%nonassoc POWER
//%precedence LBRACKET
%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
%%
%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>(driver.env, @$); }
;
directive_multiline : for
{ $$ = $1; }
| if
{ $$ = $1; }
| ifdef
{ $$ = $1; }
| ifndef
{ $$ = $1; }
;
for : FOR { driver.pushContext(); } comma_name IN expr EOL statements ENDFOR
{
vector<VariablePtr> vvnp;
for (auto & it : $3)
{
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, @8));
$$ = make_shared<For>(vvnp, $5, 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 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; }
;
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<Double>($1, driver.env, @$); }
| QUOTED_STRING
{ $$ = make_shared<String>($1, driver.env, @$); }
| expr COLON expr
{ $$ = make_shared<Array>($1, $3, driver.env, @$); }
| LBRACKET comma_expr RBRACKET
{ $$ = make_shared<Array>($2, driver.env, @$); }
| symbol LBRACKET 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>($2, $4, $6, 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, @$); }
| 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);
}

204
src/macro/Tokenizer.ll Normal file
View File

@ -0,0 +1,204 @@
/* -*- 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/>.
*/
%{
#include "Driver.hh"
#include <fstream>
using namespace std;
using namespace macro;
// Announce to Flex the prototype we want for lexing function
#define YY_DECL \
Tokenizer::parser::token_type \
TokenizerFlex::lex(Tokenizer::parser::semantic_type *yylval, \
Tokenizer::parser::location_type *yylloc, \
macro::Driver &driver)
// Shortcut to access tokens defined by Bison
using token = Tokenizer::parser::token;
/* By default yylex returns int, we use token_type.
Unfortunately yyterminate by default returns 0, which is
not of token_type. */
#define yyterminate() return Tokenizer::parser::token_type (0);
%}
%option c++
%option prefix="Tokenizer"
%option case-insensitive noinput noyywrap nounput batch debug never-interactive noyymore
%x directive
%x eval
%x expr
%x end_line
%{
// Increments location counter for every token read
# define YY_USER_ACTION yylloc->columns(yyleng);
%}
SPC [ \t]+
EOL (\r)?\n
CONT \\\\{SPC}*
%%
/* Code put at the beginning of yylex() */
%{
// Reset location before reading token
yylloc->step();
%}
<directive>include { BEGIN(expr); return token::INCLUDE; }
<directive>includepath { BEGIN(expr); return token::INCLUDEPATH; }
<directive>define { BEGIN(expr); return token::DEFINE; }
<directive>echo { BEGIN(expr); return token::D_ECHO; }
<directive>error { BEGIN(expr); return token::ERROR; }
<directive>if { BEGIN(expr); return token::IF; }
<directive>ifdef { BEGIN(expr); return token::IFDEF; }
<directive>ifndef { BEGIN(expr); return token::IFNDEF; }
<directive>else { BEGIN(end_line); return token::ELSE; }
<directive>endif { BEGIN(end_line); return token::ENDIF; }
<directive>for { BEGIN(expr); return token::FOR; }
<directive>endfor { BEGIN(end_line); return token::ENDFOR; }
<directive>echomacrovars { BEGIN(end_line); return token::ECHOMACROVARS; }
<expr,eval>\+ { return token::PLUS; }
<expr,eval>- { return token::MINUS; }
<expr,eval>\* { return token::TIMES; }
<expr,eval>\/ { return token::DIVIDE; }
<expr,eval>= { return token::EQUAL; }
<expr,eval>\^ { return token::POWER; }
<expr,eval>< { return token::LESS; }
<expr,eval>> { return token::GREATER; }
<expr,eval>>= { return token::GREATER_EQUAL; }
<expr,eval><= { return token::LESS_EQUAL; }
<expr,eval>== { return token::EQUAL_EQUAL; }
<expr,eval>!= { return token::NOT_EQUAL; }
<expr,eval>&& { return token::AND; }
<expr,eval>"||" { return token::OR; }
<expr,eval>! { return token::NOT; }
<expr,eval>\| { return token::UNION; }
<expr,eval>& { return token::INTERSECTION; }
<expr,eval>, { return token::COMMA; }
<expr,eval>: { return token::COLON; }
<expr,eval>\( { return token::LPAREN; }
<expr,eval>\) { return token::RPAREN; }
<expr,eval>\[ { return token::LBRACKET; }
<expr,eval>\] { return token::RBRACKET; }
<expr,eval>IN { return token::IN; }
<expr,eval>WHEN { return token::WHEN; }
<expr,eval>LENGTH { return token::LENGTH; }
<expr,eval>true { return token::TRUE; }
<expr,eval>false { return token::FALSE; }
<expr,eval>exp { return token::EXP; }
<expr,eval>log { return token::LOG; }
<expr,eval>ln { return token::LN; }
<expr,eval>log10 { return token::LOG10; }
<expr,eval>sin { return token::SIN; }
<expr,eval>cos { return token::COS; }
<expr,eval>tan { return token::TAN; }
<expr,eval>asin { return token::ATAN; }
<expr,eval>acos { return token::ACOS; }
<expr,eval>atan { return token::ATAN; }
<expr,eval>sqrt { return token::SQRT; }
<expr,eval>cbrt { return token::CBRT; }
<expr,eval>sign { return token::SIGN; }
<expr,eval>max { return token::MAX; }
<expr,eval>min { return token::MIN; }
<expr,eval>floor { return token::FLOOR; }
<expr,eval>ceil { return token::CEIL; }
<expr,eval>trunc { return token::TRUNC; }
<expr,eval>mod { return token::MOD; }
<expr,eval>sum { return token::SUM; }
<expr,eval>erf { return token::ERF; }
<expr,eval>erfc { return token::ERFC; }
<expr,eval>gamma { return token::GAMMA; }
<expr,eval>lgamma { return token::LGAMMA; }
<expr,eval>round { return token::ROUND; }
<expr,eval>normpdf { return token::NORMPDF; }
<expr,eval>normcdf { return token::NORMCDF; }
<expr,eval>((([0-9]*\.[0-9]+)|([0-9]+\.))([ed][-+]?[0-9]+)?)|([0-9]+([ed][-+]?[0-9]+)?)|nan|inf {
yylval->build<string>(yytext);
return token::NUMBER;
}
<expr,eval>[A-Za-z_][A-Za-z0-9_]* {
yylval->build<string>(yytext);
return token::NAME;
}
<expr,eval>\"[^\"]+\" {
yylval->build<string>(yytext + 1).pop_back();
return token::QUOTED_STRING;
}
<expr,eval>{SPC}+ { }
<eval>{EOL}+ { yylloc->lines(yyleng); yylloc->lines(yyleng); }
<eval>\} { BEGIN(INITIAL); return token::END_EVAL; }
<expr,end_line>{CONT}("//".*)?{SPC}*{EOL} { yylloc->lines(1); yylloc->step(); }
<expr,end_line>{SPC}*("//".*)?{EOL} {
yylval->build<string>("\n");
yylloc->lines(1);
BEGIN(INITIAL);
return token::EOL;
}
<INITIAL>^{SPC}*@#{SPC}* { BEGIN(directive); }
<INITIAL>@\{ { BEGIN(eval); return token::BEGIN_EVAL; }
<INITIAL>{SPC}*{EOL} {
yylval->build<string>(yytext);
yylloc->lines(1);
return token::EOL;
}
<INITIAL><<EOF>> { yyterminate(); }
<directive,expr,eval,end_line><<EOF>> { driver.error(*yylloc, "unexpected end of file"); }
<*>. { yylval->build<string>(yytext); return token::TEXT; }
<*>.|{EOL} { driver.error(*yylloc, "character unrecognized by lexer"); }
%%
/* This implementation of TokenizerFlexLexer::yylex() is required to fill the
* vtable of the class TokenizerFlexLexer. We define the scanner's main yylex
* function via YY_DECL to reside in the TokenizerFlex class instead. */
#ifdef yylex
# undef yylex
#endif
int
TokenizerFlexLexer::yylex()
{
cerr << "TokenizerFlexLexer::yylex() has been called; shouldn't arrive here." << endl;
exit(EXIT_FAILURE);
}