From d7e70a40630985a154f81631ef894cda7194db43 Mon Sep 17 00:00:00 2001
From: Houtan Bastani <houtan@dynare.org>
Date: Wed, 2 Oct 2019 17:38:42 +0200
Subject: [PATCH] macro processor: fix bug: interpret @#include at node level
 instead of at the Driver level

---
 src/DynareMain1.cc                       |  3 +-
 src/macro/Directives.cc                  | 40 +++++++++++++++++++---
 src/macro/Directives.hh                  | 14 +++-----
 src/macro/Driver.cc                      | 42 +-----------------------
 src/macro/ForwardDeclarationsAndEnums.hh |  3 ++
 src/macro/Parser.yy                      |  2 +-
 6 files changed, 47 insertions(+), 57 deletions(-)

diff --git a/src/DynareMain1.cc b/src/DynareMain1.cc
index 28cefbe4..7b44da8c 100644
--- a/src/DynareMain1.cc
+++ b/src/DynareMain1.cc
@@ -33,7 +33,8 @@ main1(const string &filename, const string &basename, istream &modfile, bool deb
 {
   // Do macro processing
   macro::Environment env = macro::Environment();
-  macro::Driver m(env, no_line_macro_arg);
+  vector<string> paths;
+  macro::Driver m(env, paths, no_line_macro_arg);
   m.parse(filename, basename, modfile, macro_output, debug, defines, path);
   if (save_macro)
     {
diff --git a/src/macro/Directives.cc b/src/macro/Directives.cc
index 13777551..b7c09e74 100644
--- a/src/macro/Directives.cc
+++ b/src/macro/Directives.cc
@@ -18,6 +18,9 @@
  */
 
 #include "Directives.hh"
+#include "Driver.hh"
+
+#include <fstream>
 
 using namespace macro;
 
@@ -39,15 +42,45 @@ Eval::interpret(ostream &output, bool no_line_macro)
     }
 }
 
-string
-Include::interpretAndGetName() const
+void
+Include::interpret(ostream &output, bool no_line_macro)
 {
+#ifdef _WIN32
+  string FILESEP = "\\";
+#else
+  string FILESEP = "/";
+#endif
   try
     {
       StringPtr msp = dynamic_pointer_cast<String>(expr->eval());
       if (!msp)
         throw StackTrace("File name does not evaluate to a string");
-      return msp->to_string();
+      string filename = msp->to_string();
+      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(StackTrace("@#includepath", "Could not open " + filename +
+                             ". The following directories were searched:\n" + dirs.str(), location));
+        }
+
+      string basename = filename;
+      size_t pos = basename.find_last_of('.');
+      if (pos != string::npos)
+        basename.erase(pos);
+
+      Driver m(env, paths, no_line_macro);
+      m.parse(filename, basename, incfile, output, false, vector<pair<string, string>>{}, paths);
     }
   catch (StackTrace &ex)
     {
@@ -58,7 +91,6 @@ Include::interpretAndGetName() const
     {
       error(StackTrace("@#include", e.what(), location));
     }
-  return "";
 }
 
 void
diff --git a/src/macro/Directives.hh b/src/macro/Directives.hh
index a0bc92e5..045caa6a 100644
--- a/src/macro/Directives.hh
+++ b/src/macro/Directives.hh
@@ -66,17 +66,11 @@ namespace macro
   {
   private:
     const ExpressionPtr expr;
+    vector<string> &paths;
   public:
-    Include(ExpressionPtr expr_arg, Environment &env_arg, Tokenizer::location location_arg) :
-      Directive(env_arg, move(location_arg)), expr{move(expr_arg)} { }
-    // Not interpretable because we want the class to be immutable (for use with shared_ptr)
-    // If it were interpretable, the name would need to be stored in a non-const variable
-    // rendering the class mutable
-    inline void interpret(ostream &output, bool no_line_macro) override
-    {
-      error(StackTrace("@#include", "should never be interpreted", location));
-    }
-    string interpretAndGetName() const;
+    Include(ExpressionPtr expr_arg, Environment &env_arg, vector<string> &paths_arg, Tokenizer::location location_arg) :
+      Directive(env_arg, move(location_arg)), expr{move(expr_arg)}, paths{paths_arg} { }
+    void interpret(ostream &output, bool no_line_macro) override;
   };
 
 
diff --git a/src/macro/Driver.cc b/src/macro/Driver.cc
index cccdeece..49553d1a 100644
--- a/src/macro/Driver.cc
+++ b/src/macro/Driver.cc
@@ -19,8 +19,6 @@
 
 #include "Driver.hh"
 
-#include <fstream>
-
 using namespace macro;
 
 void
@@ -28,12 +26,6 @@ Driver::parse(const string &file_arg, const string &basename_arg, istream &modfi
               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);
@@ -80,39 +72,7 @@ Driver::parse(const string &file_arg, const string &basename_arg, istream &modfi
           statement->printLineInfo(output, no_line_macro);
           printLine = false;
         }
-
-      auto ip = dynamic_pointer_cast<Include>(statement);
-      if (ip)
-        {
-          string filename = ip->interpretAndGetName();
-          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, paths, no_line_macro);
-          m.parse(filename, basename, incfile, output, debug, vector<pair<string, string>>{}, paths);
-        }
-      else
-        statement->interpret(output, no_line_macro);
+      statement->interpret(output, no_line_macro);
     }
 }
 
diff --git a/src/macro/ForwardDeclarationsAndEnums.hh b/src/macro/ForwardDeclarationsAndEnums.hh
index 71802223..484ea77e 100644
--- a/src/macro/ForwardDeclarationsAndEnums.hh
+++ b/src/macro/ForwardDeclarationsAndEnums.hh
@@ -56,6 +56,9 @@ namespace macro
   class Eval;
   using EvalPtr = shared_ptr<Eval>;
 
+  // For Directives.cc
+  class Driver;
+
   namespace codes
   {
     enum class BaseType
diff --git a/src/macro/Parser.yy b/src/macro/Parser.yy
index cbd1dd19..51b25245 100644
--- a/src/macro/Parser.yy
+++ b/src/macro/Parser.yy
@@ -126,7 +126,7 @@ directive : directive_one_line EOL
           ;
 
 directive_one_line : INCLUDE expr
-                     { $$ = make_shared<Include>($2, driver.env, @$); }
+                     { $$ = make_shared<Include>($2, driver.env, driver.paths, @$); }
                    | INCLUDEPATH expr
                      { $$ = make_shared<IncludePath>($2, driver.env, driver.paths, @$); }
                    | DEFINE symbol EQUAL expr
-- 
GitLab