macroprocessor: add support for functions that return strings. #3
parent
a51dd224e4
commit
244552393b
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2008-2017 Dynare Team
|
* Copyright (C) 2008-2018 Dynare Team
|
||||||
*
|
*
|
||||||
* This file is part of Dynare.
|
* This file is part of Dynare.
|
||||||
*
|
*
|
||||||
|
@ -50,6 +50,7 @@ class MacroDriver;
|
||||||
string *string_val;
|
string *string_val;
|
||||||
int int_val;
|
int int_val;
|
||||||
const MacroValue *mv;
|
const MacroValue *mv;
|
||||||
|
vector<string *> *vector_string_p_val;
|
||||||
};
|
};
|
||||||
|
|
||||||
%code {
|
%code {
|
||||||
|
@ -89,6 +90,7 @@ class MacroDriver;
|
||||||
%left UMINUS UPLUS EXCLAMATION
|
%left UMINUS UPLUS EXCLAMATION
|
||||||
%left LBRACKET
|
%left LBRACKET
|
||||||
|
|
||||||
|
%type <vector_string_p_val> func_args
|
||||||
%type <mv> expr array_expr
|
%type <mv> expr array_expr
|
||||||
%%
|
%%
|
||||||
|
|
||||||
|
@ -124,12 +126,25 @@ statement : expr
|
||||||
{ driver.printvars(@$, true); }
|
{ driver.printvars(@$, true); }
|
||||||
| ECHOMACROVARS LPAREN SAVE RPAREN
|
| ECHOMACROVARS LPAREN SAVE RPAREN
|
||||||
{ out << driver.printvars(@$, false); }
|
{ out << driver.printvars(@$, false); }
|
||||||
|
| DEFINE NAME LPAREN func_args { driver.push_args_into_func_env(*$4); } RPAREN EQUAL expr
|
||||||
|
{
|
||||||
|
TYPERR_CATCH(driver.set_string_function(*$2, *$4, $8), @$);
|
||||||
|
driver.pop_func_env();
|
||||||
|
delete $2;
|
||||||
|
delete $4;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
func_args : NAME
|
||||||
|
{ $$ = new vector<string *>(); $$->push_back($1); }
|
||||||
|
| func_args COMMA NAME
|
||||||
|
{ $$->push_back($3); }
|
||||||
;
|
;
|
||||||
|
|
||||||
expr : INTEGER
|
expr : INTEGER
|
||||||
{ $$ = new IntMV(driver, $1); }
|
{ $$ = new IntMV(driver, $1); }
|
||||||
| STRING
|
| STRING
|
||||||
{ $$ = new StringMV(driver, *$1); delete $1; }
|
{ $$ = new StringMV(driver, driver.replace_vars_in_str(*$1)); delete $1; }
|
||||||
| NAME
|
| NAME
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@ -142,6 +157,8 @@ expr : INTEGER
|
||||||
}
|
}
|
||||||
delete $1;
|
delete $1;
|
||||||
}
|
}
|
||||||
|
| NAME LPAREN array_expr RPAREN
|
||||||
|
{ TYPERR_CATCH($$ = driver.eval_string_function(*$1, $3), @$); delete $1; }
|
||||||
| LENGTH LPAREN array_expr RPAREN
|
| LENGTH LPAREN array_expr RPAREN
|
||||||
{ TYPERR_CATCH($$ = $3->length(), @$); }
|
{ TYPERR_CATCH($$ = $3->length(), @$); }
|
||||||
| LPAREN expr RPAREN
|
| LPAREN expr RPAREN
|
||||||
|
|
|
@ -19,6 +19,8 @@
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <regex>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
|
@ -87,6 +89,113 @@ MacroDriver::error(const Macro::parser::location_type &l, const string &m) const
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string
|
||||||
|
MacroDriver::replace_vars_in_str(const string &s) const
|
||||||
|
{
|
||||||
|
if (s.find("@") == string::npos)
|
||||||
|
return string(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 ("@\\s*\\{\\s*" + name_str + "\\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
|
||||||
|
{
|
||||||
|
const MacroValue *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 != nullptr)
|
||||||
|
{
|
||||||
|
// 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 MacroValue *value)
|
||||||
|
{
|
||||||
|
auto *smv = dynamic_cast<const StringMV *>(value);
|
||||||
|
if (!smv)
|
||||||
|
throw MacroValue::TypeError("The definition of a macro function must evaluate to a string");
|
||||||
|
|
||||||
|
env[name] = new FuncMV(*this, args, *(const_cast<StringMV *>(smv)));
|
||||||
|
}
|
||||||
|
|
||||||
|
const StringMV *
|
||||||
|
MacroDriver::eval_string_function(const string &name, const MacroValue *args)
|
||||||
|
{
|
||||||
|
auto it = env.find(name);
|
||||||
|
if (it == env.end())
|
||||||
|
throw UnknownVariable(name);
|
||||||
|
|
||||||
|
const auto *fmv = dynamic_cast<const FuncMV *>(env[name]);
|
||||||
|
if (!fmv)
|
||||||
|
throw MacroValue::TypeError("You are using " + name + " as if it were a macro function");
|
||||||
|
|
||||||
|
vector<string *> func_args = fmv->get_args();
|
||||||
|
if (func_args.size() != dynamic_cast<const IntMV *>(args->length())->get_int_value())
|
||||||
|
{
|
||||||
|
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 : func_args)
|
||||||
|
func_env_map[*it] = args->at(i++);
|
||||||
|
|
||||||
|
func_env.push_back(func_env_map);
|
||||||
|
StringMV *smv = new StringMV(*this, 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] = NULL;
|
||||||
|
func_env.push_back(func_env_map);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroDriver::pop_func_env()
|
||||||
|
{
|
||||||
|
func_env.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MacroDriver::set_variable(const string &name, const MacroValue *value)
|
MacroDriver::set_variable(const string &name, const MacroValue *value)
|
||||||
{
|
{
|
||||||
|
@ -217,7 +326,14 @@ MacroDriver::printvars(const Macro::parser::location_type &l, const bool tostdou
|
||||||
cout << "Macroprocessor: Printing macro variable values from " << file
|
cout << "Macroprocessor: Printing macro variable values from " << file
|
||||||
<< " at line " << l.begin.line << endl;
|
<< " at line " << l.begin.line << endl;
|
||||||
for (const auto & it : env)
|
for (const auto & it : env)
|
||||||
cout << " " << it.first << " = " << it.second->print() << endl;
|
{
|
||||||
|
cout << " ";
|
||||||
|
const auto *fmv = dynamic_cast<const FuncMV *>(it.second);
|
||||||
|
if (!fmv)
|
||||||
|
cout << it.first << " = " << it.second->print() << endl;
|
||||||
|
else
|
||||||
|
cout << it.first << it.second->print() << endl;
|
||||||
|
}
|
||||||
cout << endl;
|
cout << endl;
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2008-2017 Dynare Team
|
* Copyright (C) 2008-2018 Dynare Team
|
||||||
*
|
*
|
||||||
* This file is part of Dynare.
|
* This file is part of Dynare.
|
||||||
*
|
*
|
||||||
|
@ -158,8 +158,14 @@ private:
|
||||||
//! Stores all created macro values
|
//! Stores all created macro values
|
||||||
set<const MacroValue *> values;
|
set<const MacroValue *> values;
|
||||||
|
|
||||||
|
// The map defining an environment
|
||||||
|
typedef map<string, const MacroValue *> env_t;
|
||||||
|
|
||||||
//! Environment: maps macro variables to their values
|
//! Environment: maps macro variables to their values
|
||||||
map<string, const MacroValue *> env;
|
env_t env;
|
||||||
|
|
||||||
|
//! Environment for function currently being evaluated
|
||||||
|
vector<env_t> func_env;
|
||||||
|
|
||||||
//! Stack used to keep track of (possibly nested) loops
|
//! Stack used to keep track of (possibly nested) loops
|
||||||
//! First element is loop variable name, second is the array over which iteration is done, and third is subscript to be used by next call of iter_loop() (beginning with 0) */
|
//! First element is loop variable name, second is the array over which iteration is done, and third is subscript to be used by next call of iter_loop() (beginning with 0) */
|
||||||
|
@ -210,6 +216,22 @@ public:
|
||||||
//! Set a variable
|
//! Set a variable
|
||||||
void set_variable(const string &name, const MacroValue *value);
|
void set_variable(const string &name, const MacroValue *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 MacroValue *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
|
||||||
|
const StringMV *eval_string_function(const string &name, const MacroValue *args);
|
||||||
|
|
||||||
//! Get a variable
|
//! Get a variable
|
||||||
/*! Returns a newly allocated value (clone of the value stored in environment). */
|
/*! Returns a newly allocated value (clone of the value stored in environment). */
|
||||||
const MacroValue *get_variable(const string &name) const noexcept(false);
|
const MacroValue *get_variable(const string &name) const noexcept(false);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2008-2017 Dynare Team
|
* Copyright (C) 2008-2018 Dynare Team
|
||||||
*
|
*
|
||||||
* This file is part of Dynare.
|
* This file is part of Dynare.
|
||||||
*
|
*
|
||||||
|
@ -113,6 +113,12 @@ MacroValue::length() const noexcept(false)
|
||||||
throw TypeError("Length not supported for this type");
|
throw TypeError("Length not supported for this type");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const MacroValue *
|
||||||
|
MacroValue::at(int i) const noexcept(false)
|
||||||
|
{
|
||||||
|
throw TypeError("Length not supported for this type");
|
||||||
|
}
|
||||||
|
|
||||||
const MacroValue *
|
const MacroValue *
|
||||||
MacroValue::append(const MacroValue *mv) const noexcept(false)
|
MacroValue::append(const MacroValue *mv) const noexcept(false)
|
||||||
{
|
{
|
||||||
|
@ -351,9 +357,14 @@ const MacroValue *
|
||||||
StringMV::operator+(const MacroValue &mv) const noexcept(false)
|
StringMV::operator+(const MacroValue &mv) const noexcept(false)
|
||||||
{
|
{
|
||||||
const auto *mv2 = dynamic_cast<const StringMV *>(&mv);
|
const auto *mv2 = dynamic_cast<const StringMV *>(&mv);
|
||||||
if (mv2 == nullptr)
|
if (mv2 != nullptr)
|
||||||
throw TypeError("Type mismatch for operands of + operator");
|
return new StringMV(driver, value + mv2->value);
|
||||||
return new StringMV(driver, value + mv2->value);
|
|
||||||
|
const auto *mv3 = dynamic_cast<const FuncMV *>(&mv);
|
||||||
|
if (mv3 != nullptr)
|
||||||
|
return new StringMV(driver, value + mv3->toString());
|
||||||
|
|
||||||
|
throw TypeError("Type mismatch for operands of + operator");
|
||||||
}
|
}
|
||||||
|
|
||||||
const MacroValue *
|
const MacroValue *
|
||||||
|
@ -479,3 +490,81 @@ ArrayMV<string>::print() const
|
||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FuncMV::FuncMV(MacroDriver &driver, vector<string *> &args_arg, StringMV &value_arg) :
|
||||||
|
MacroValue(driver), args(args_arg), value(value_arg)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
FuncMV::~FuncMV()
|
||||||
|
= default;
|
||||||
|
|
||||||
|
const MacroValue *
|
||||||
|
FuncMV::operator+(const MacroValue &mv) const noexcept(false)
|
||||||
|
{
|
||||||
|
const auto *mv2 = dynamic_cast<const FuncMV *>(&mv);
|
||||||
|
if (mv2 != nullptr)
|
||||||
|
return value + mv2->value;
|
||||||
|
|
||||||
|
const auto *mv3 = dynamic_cast<const StringMV *>(&mv);
|
||||||
|
if (mv3 != nullptr)
|
||||||
|
return value + *mv3;
|
||||||
|
|
||||||
|
throw TypeError("Type mismatch for operands of + operator");
|
||||||
|
}
|
||||||
|
|
||||||
|
const MacroValue *
|
||||||
|
FuncMV::operator==(const MacroValue &mv) const noexcept(false)
|
||||||
|
{
|
||||||
|
const auto *mv2 = dynamic_cast<const FuncMV *>(&mv);
|
||||||
|
if (mv2 == nullptr)
|
||||||
|
return new IntMV(driver, 0);
|
||||||
|
|
||||||
|
if (value != mv2->value)
|
||||||
|
return new IntMV(driver, 0);
|
||||||
|
|
||||||
|
if (args.size() == mv2->args.size())
|
||||||
|
for (int i = 0; i < args.size(); i++)
|
||||||
|
if (args[i] != mv2->args[i])
|
||||||
|
return new IntMV(driver, 0);
|
||||||
|
|
||||||
|
return new IntMV(driver, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const MacroValue *
|
||||||
|
FuncMV::operator!=(const MacroValue &mv) const noexcept(false)
|
||||||
|
{
|
||||||
|
if (dynamic_cast<const IntMV *>(*this == mv)->value == 1)
|
||||||
|
return new IntMV(driver, 0);
|
||||||
|
return new IntMV(driver, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
string
|
||||||
|
FuncMV::toString() const
|
||||||
|
{
|
||||||
|
return value.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
string
|
||||||
|
FuncMV::print() const
|
||||||
|
{
|
||||||
|
bool comma_flag = false;
|
||||||
|
string retval = "(";
|
||||||
|
for (const auto it : args)
|
||||||
|
{
|
||||||
|
if (comma_flag)
|
||||||
|
retval += ", ";
|
||||||
|
retval += *it;
|
||||||
|
comma_flag = true;
|
||||||
|
}
|
||||||
|
retval += ")";
|
||||||
|
return retval + " = '" + value.toString() + "'";
|
||||||
|
}
|
||||||
|
|
||||||
|
const MacroValue *
|
||||||
|
FuncMV::toArray() const
|
||||||
|
{
|
||||||
|
// COMEBACK
|
||||||
|
vector<string> v;
|
||||||
|
v.push_back(value.toString());
|
||||||
|
return new ArrayMV<string>(driver, v);
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2008-2017 Dynare Team
|
* Copyright (C) 2008-2018 Dynare Team
|
||||||
*
|
*
|
||||||
* This file is part of Dynare.
|
* This file is part of Dynare.
|
||||||
*
|
*
|
||||||
|
@ -98,6 +98,8 @@ public:
|
||||||
virtual const MacroValue *toArray() const = 0;
|
virtual const MacroValue *toArray() const = 0;
|
||||||
//! Gets length
|
//! Gets length
|
||||||
virtual const MacroValue *length() const noexcept(false);
|
virtual const MacroValue *length() const noexcept(false);
|
||||||
|
//! Returns element at location i
|
||||||
|
virtual const MacroValue *at(int i) const noexcept(false);
|
||||||
//! Appends value at the end of an array
|
//! Appends value at the end of an array
|
||||||
/*! The argument must be an array. */
|
/*! The argument must be an array. */
|
||||||
virtual const MacroValue *append(const MacroValue *array) const noexcept(false);
|
virtual const MacroValue *append(const MacroValue *array) const noexcept(false);
|
||||||
|
@ -116,6 +118,7 @@ public:
|
||||||
class IntMV : public MacroValue
|
class IntMV : public MacroValue
|
||||||
{
|
{
|
||||||
friend class StringMV;
|
friend class StringMV;
|
||||||
|
friend class FuncMV;
|
||||||
friend class MacroDriver;
|
friend class MacroDriver;
|
||||||
private:
|
private:
|
||||||
//! Underlying integer value
|
//! Underlying integer value
|
||||||
|
@ -201,6 +204,32 @@ public:
|
||||||
const MacroValue *in(const MacroValue *array) const noexcept(false) override;
|
const MacroValue *in(const MacroValue *array) const noexcept(false) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class FuncMV : public MacroValue
|
||||||
|
{
|
||||||
|
friend class MacroDriver;
|
||||||
|
private:
|
||||||
|
//! Function args & body
|
||||||
|
const vector<string *> args;
|
||||||
|
const StringMV &value;
|
||||||
|
public:
|
||||||
|
FuncMV(MacroDriver &driver, vector<string *> &args, StringMV &value_arg);
|
||||||
|
|
||||||
|
~FuncMV() override;
|
||||||
|
|
||||||
|
//! Computes string concatenation
|
||||||
|
const MacroValue *operator+(const MacroValue &mv) const noexcept(false) override;
|
||||||
|
const MacroValue *operator==(const MacroValue &mv) const noexcept(false) override;
|
||||||
|
const MacroValue *operator!=(const MacroValue &mv) const noexcept(false) override;
|
||||||
|
string toString() const override;
|
||||||
|
string print() const override;
|
||||||
|
const MacroValue *toArray() const override;
|
||||||
|
inline const vector<string *> &
|
||||||
|
get_args() const
|
||||||
|
{
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
//! Represents an array in macro language
|
//! Represents an array in macro language
|
||||||
template<typename T>
|
template<typename T>
|
||||||
class ArrayMV : public MacroValue
|
class ArrayMV : public MacroValue
|
||||||
|
@ -236,6 +265,7 @@ public:
|
||||||
const MacroValue *toArray() const override;
|
const MacroValue *toArray() const override;
|
||||||
//! Gets length
|
//! Gets length
|
||||||
const MacroValue *length() const noexcept(false) override;
|
const MacroValue *length() const noexcept(false) override;
|
||||||
|
const MacroValue *at(int i) const noexcept(false) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
@ -353,4 +383,11 @@ ArrayMV<T>::length() const noexcept(false)
|
||||||
return new IntMV(driver, values.size());
|
return new IntMV(driver, values.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
const MacroValue *
|
||||||
|
ArrayMV<T>::at(int i) const noexcept(false)
|
||||||
|
{
|
||||||
|
return new_base_value(driver, values[i]);
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue