From 3a21eda40ef0385e3a2558c990496fc0603ce01a Mon Sep 17 00:00:00 2001
From: Houtan Bastani <houtan@dynare.org>
Date: Mon, 26 Aug 2019 18:12:04 +0200
Subject: [PATCH] macro processor: support @#echomacrovars with symbol_list

closes #26
---
 doc/macroprocessor/macroprocessor.tex |  8 ++-
 src/macro/Directives.cc               |  4 +-
 src/macro/Directives.hh               |  4 ++
 src/macro/Environment.cc              | 72 +++++++++++++++++----------
 src/macro/Environment.hh              |  4 +-
 src/macro/Parser.yy                   | 14 ++++++
 6 files changed, 74 insertions(+), 32 deletions(-)

diff --git a/doc/macroprocessor/macroprocessor.tex b/doc/macroprocessor/macroprocessor.tex
index 4d26aa16..ed8bab86 100644
--- a/doc/macroprocessor/macroprocessor.tex
+++ b/doc/macroprocessor/macroprocessor.tex
@@ -530,13 +530,17 @@ There is also \verb+@#ifndef+, which is the opposite of \verb+@#ifdef+
   \begin{itemize}
   \item The echo directive will simply display a message on standard output
   \item The error directive will display the message and make Dynare stop (only makes sense inside a conditional directive)
-  \item The echomacrovars directive will display all of the macro variables and their values
+  \item The echomacrovars directive will display all of the macro variables (or
+    those specified) and their values, optionally saving them
   \end{itemize}
 
   \begin{block}{Syntax}
 \verb+@#echo +\textit{string\_expr} \\
 \verb+@#error +\textit{string\_expr} \\
-\verb+@#echomacrovars +
+\verb+@#echomacrovars +\\
+\verb+@#echomacrovars +\textit{list\_of\_variables}\\
+\verb+@#echomacrovars (save)+\\
+\verb+@#echomacrovars (save)+\textit{list\_of\_variables}\\
   \end{block}
 
   \begin{block}{Examples}
diff --git a/src/macro/Directives.cc b/src/macro/Directives.cc
index f2f67926..e1207a28 100644
--- a/src/macro/Directives.cc
+++ b/src/macro/Directives.cc
@@ -147,9 +147,9 @@ void
 EchoMacroVars::interpret(ostream &output, bool no_line_macro)
 {
   if (save)
-    env.print(output, location.begin.line, true);
+    env.print(output, vars, location.begin.line, true);
   else
-    env.print(cout);
+    env.print(cout, vars);
   printEndLineInfo(output, no_line_macro);
 }
 
diff --git a/src/macro/Directives.hh b/src/macro/Directives.hh
index 1c1409e5..09f34339 100644
--- a/src/macro/Directives.hh
+++ b/src/macro/Directives.hh
@@ -145,10 +145,14 @@ namespace macro
   {
   private:
     const bool save;
+    const vector<string> vars;
   public:
     EchoMacroVars(bool save_arg,
                   Environment &env_arg, Tokenizer::location location_arg) :
       Directive(env_arg, move(location_arg)), save{save_arg} { }
+    EchoMacroVars(bool save_arg, vector<string> vars_arg,
+                  Environment &env_arg, Tokenizer::location location_arg) :
+      Directive(env_arg, move(location_arg)), save{save_arg}, vars{move(vars_arg)} { }
     void interpret(ostream &output, bool no_line_macro) override;
   };
 
diff --git a/src/macro/Environment.cc b/src/macro/Environment.cc
index fcb8aa5c..17e3f756 100644
--- a/src/macro/Environment.cc
+++ b/src/macro/Environment.cc
@@ -101,42 +101,60 @@ Environment::isFunctionDefined(const string &name) const noexcept
 }
 
 void
-Environment::print(ostream &output, int line, bool save) const
+Environment::print(ostream &output, const vector<string> &vars, int line, bool save) const
 {
   if (!save && !variables.empty())
     output << "Macro Variables:" << endl;
 
-  for (auto & it : variables)
-    {
-      output << (save ? "options_.macrovars_line_" + to_string(line) + "." : "  " );
-      output << it.first << " = ";
-      getVariable(it.first)->eval()->print(output, save);
-      if (save)
-        output << ";";
-      output << endl;
-    }
+  if (vars.empty())
+    for (auto & it : variables)
+      printVariable(output, it.first, line, save);
+  else
+    for (const auto & it : vars)
+      if (isVariableDefined(it))
+        printVariable(output, it, line, save);
 
   if (!save && !functions.empty())
     output << "Macro Functions:" << endl;
 
-  for (auto & it : functions)
+  if (vars.empty())
+    for (const auto & it : functions)
+      printFunction(output, it.second, line, save);
+  else
+    for (const auto & it : vars)
+      if (isFunctionDefined(it))
+        printFunction(output, functions.find(it)->second, line, save);
+
+  if (parent)
+    parent->print(output, vars, line, save);
+}
+
+void
+Environment::printVariable(ostream &output, const string & name, int line, bool save) const
+{
+  output << (save ? "options_.macrovars_line_" + to_string(line) + "." : "  " );
+  output << name << " = ";
+  getVariable(name)->eval()->print(output, save);
+  if (save)
+    output << ";";
+  output << endl;
+}
+
+void
+Environment::printFunction(ostream &output, const tuple<FunctionPtr, ExpressionPtr> & function, int line, bool save) const
+{
+  output << (save ? "options_.macrovars_line_" + to_string(line) + ".function." : "  " );
+  if (save)
     {
-      output << (save ? "options_.macrovars_line_" + to_string(line) + ".function." : "  " );
-      if (save)
-        {
-          get<0>(it.second)->printName(output);
-          output << " = '";
-        }
-
-      get<0>(it.second)->print(output);
-      output << " = ";
-      get<1>(it.second)->print(output);
-
-      if (save)
-        output << "';";
-      output << endl;
+      get<0>(function)->printName(output);
+      output << " = '";
     }
 
-  if (parent)
-    parent->print(output, line, save);
+  get<0>(function)->print(output);
+  output << " = ";
+  get<1>(function)->print(output);
+
+  if (save)
+    output << "';";
+  output << endl;
 }
diff --git a/src/macro/Environment.hh b/src/macro/Environment.hh
index 905905ff..c37d7c61 100644
--- a/src/macro/Environment.hh
+++ b/src/macro/Environment.hh
@@ -43,7 +43,9 @@ namespace macro
     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(ostream &output, int line = -1, bool save = false) const;
+    void print(ostream &output, const vector<string> &vars, int line = -1, bool save = false) const;
+    void printVariable(ostream &output, const string & name, int line, bool save) const;
+    void printFunction(ostream &output, const tuple<FunctionPtr, ExpressionPtr> & function, int line, bool save) const;
     inline size_t size() const noexcept { return variables.size() + functions.size(); }
     inline const Environment *getGlobalEnv() const noexcept { return parent == nullptr ? this : parent->getGlobalEnv(); }
   };
diff --git a/src/macro/Parser.yy b/src/macro/Parser.yy
index b46cea4b..b84009af 100644
--- a/src/macro/Parser.yy
+++ b/src/macro/Parser.yy
@@ -100,6 +100,7 @@ using namespace macro;
 %type <VariablePtr> symbol
 
 %type <vector<ExpressionPtr>> comma_expr function_args tuple_comma_expr
+%type <vector<string>> name_list
 
 %%
 
@@ -138,10 +139,23 @@ directive_one_line : INCLUDE expr
                      { $$ = make_shared<Error>($2, driver.env, @$); }
                    | ECHOMACROVARS
                      { $$ = make_shared<EchoMacroVars>(false, driver.env, @$); }
+                   | ECHOMACROVARS name_list
+                     { $$ = make_shared<EchoMacroVars>(false, $2, driver.env, @$); }
                    | ECHOMACROVARS LPAREN SAVE RPAREN
                      { $$ = make_shared<EchoMacroVars>(true, driver.env, @$); }
+                   | ECHOMACROVARS LPAREN SAVE RPAREN name_list
+                     { $$ = make_shared<EchoMacroVars>(true, $5, driver.env, @$); }
                    ;
 
+name_list : NAME
+            { $$ = vector<string>{$1}; }
+          | name_list NAME
+            {
+              $1.emplace_back($2);
+              $$ = $1;
+            }
+          ;
+
 directive_multiline : for
                     | if
                     | ifdef
-- 
GitLab