macro processor 2.0
parent
69f2f1ca88
commit
17e040f3f6
|
@ -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
|
||||
|
|
|
@ -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 ¯o_output)
|
||||
bool no_line_macro_arg, bool no_empty_line_macro, const vector<pair<string, string>> &defines, const vector<string> &path,
|
||||
stringstream ¯o_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())
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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();
|
||||
}
|
|
@ -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
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue