diff --git a/src/MacroExpandModFile.cc b/src/MacroExpandModFile.cc
index 8a3fd6091d4e0daba61f31577946521e250a7d84..ea383421e84d580d4f367ea3f4fa56cb53151ba0 100644
--- a/src/MacroExpandModFile.cc
+++ b/src/MacroExpandModFile.cc
@@ -34,8 +34,8 @@ macroExpandModFile(const string &filename, const string &basename, const istream
   // Do macro processing
   stringstream macro_output;
   macro::Environment env = macro::Environment();
-  macro::Driver m(env);
-  m.parse(filename, basename, modfile, debug, defines, paths, macro_output);
+  macro::Driver m;
+  m.parse(filename, basename, modfile, debug, defines, env, paths, macro_output);
   if (save_macro)
     {
       if (save_macro_file.empty())
diff --git a/src/macro/Directives.cc b/src/macro/Directives.cc
index e76bf4499589c10e30bb42aa239620bbeb078fb1..51abcedf64e803684c35e0886ddfa11171dab3fb 100644
--- a/src/macro/Directives.cc
+++ b/src/macro/Directives.cc
@@ -25,11 +25,11 @@
 using namespace macro;
 
 void
-Eval::interpret(ostream &output, vector<filesystem::path> &paths)
+Eval::interpret(ostream &output, Environment &env, vector<filesystem::path> &paths)
 {
   try
     {
-      output << expr->eval()->to_string();
+      output << expr->eval(env)->to_string();
     }
   catch (StackTrace &ex)
     {
@@ -43,12 +43,12 @@ Eval::interpret(ostream &output, vector<filesystem::path> &paths)
 }
 
 void
-Include::interpret(ostream &output, vector<filesystem::path> &paths)
+Include::interpret(ostream &output, Environment &env, vector<filesystem::path> &paths)
 {
   using namespace filesystem;
   try
     {
-      StringPtr msp = dynamic_pointer_cast<String>(expr->eval());
+      StringPtr msp = dynamic_pointer_cast<String>(expr->eval(env));
       if (!msp)
         throw StackTrace("File name does not evaluate to a string");
       path filename = msp->to_string();
@@ -71,11 +71,11 @@ Include::interpret(ostream &output, vector<filesystem::path> &paths)
                                +". The following directories were searched:\n" + errmsg.str(), location));
             }
         }
-      Driver m(env);
+      Driver m;
       // Calling `string()` method on filename and filename.stem() because of bug in
       // MinGW 8.3.0 that ignores implicit conversion to string from filename::path.
       // Test if bug exists when version of MinGW is upgraded on Debian runners
-      m.parse(filename.string(), filename.stem().string(), incfile, false, {}, paths, output);
+      m.parse(filename.string(), filename.stem().string(), incfile, false, {}, env, paths, output);
     }
   catch (StackTrace &ex)
     {
@@ -90,12 +90,12 @@ Include::interpret(ostream &output, vector<filesystem::path> &paths)
 }
 
 void
-IncludePath::interpret(ostream &output, vector<filesystem::path> &paths)
+IncludePath::interpret(ostream &output, Environment &env, vector<filesystem::path> &paths)
 {
   using namespace filesystem;
   try
     {
-      StringPtr msp = dynamic_pointer_cast<String>(expr->eval());
+      StringPtr msp = dynamic_pointer_cast<String>(expr->eval(env));
       if (!msp)
         throw StackTrace("File name does not evaluate to a string");
       path ip = static_cast<string>(*msp);
@@ -117,7 +117,7 @@ IncludePath::interpret(ostream &output, vector<filesystem::path> &paths)
 }
 
 void
-Define::interpret(ostream &output, vector<filesystem::path> &paths)
+Define::interpret(ostream &output, Environment &env, vector<filesystem::path> &paths)
 {
   try
     {
@@ -140,11 +140,11 @@ Define::interpret(ostream &output, vector<filesystem::path> &paths)
 }
 
 void
-Echo::interpret(ostream &output, vector<filesystem::path> &paths)
+Echo::interpret(ostream &output, Environment &env, vector<filesystem::path> &paths)
 {
   try
     {
-      cout << "@#echo (" << getLocation() << "): " << expr->eval()->to_string() << endl;
+      cout << "@#echo (" << getLocation() << "): " << expr->eval(env)->to_string() << endl;
     }
   catch (StackTrace &ex)
     {
@@ -159,11 +159,11 @@ Echo::interpret(ostream &output, vector<filesystem::path> &paths)
 }
 
 void
-Error::interpret(ostream &output, vector<filesystem::path> &paths)
+Error::interpret(ostream &output, Environment &env, vector<filesystem::path> &paths)
 {
   try
     {
-      throw StackTrace(expr->eval()->to_string());
+      throw StackTrace(expr->eval(env)->to_string());
     }
   catch (StackTrace &ex)
     {
@@ -177,7 +177,7 @@ Error::interpret(ostream &output, vector<filesystem::path> &paths)
 }
 
 void
-EchoMacroVars::interpret(ostream &output, vector<filesystem::path> &paths)
+EchoMacroVars::interpret(ostream &output, Environment &env, vector<filesystem::path> &paths)
 {
   if (save)
     env.print(output, vars, location.begin.line, true);
@@ -187,12 +187,12 @@ EchoMacroVars::interpret(ostream &output, vector<filesystem::path> &paths)
 }
 
 void
-For::interpret(ostream &output, vector<filesystem::path> &paths)
+For::interpret(ostream &output, Environment &env, vector<filesystem::path> &paths)
 {
   ArrayPtr ap;
   try
     {
-      ap = dynamic_pointer_cast<Array>(index_vals->eval());
+      ap = dynamic_pointer_cast<Array>(index_vals->eval(env));
       if (!ap)
         throw StackTrace("The index must loop through an array");
     }
@@ -236,14 +236,14 @@ For::interpret(ostream &output, vector<filesystem::path> &paths)
               statement->printLineInfo(output);
               printLine = false;
             }
-          statement->interpret(output, paths);
+          statement->interpret(output, env, paths);
         }
     }
   printEndLineInfo(output);
 }
 
 void
-If::interpret(ostream &output, vector<filesystem::path> &paths)
+If::interpret(ostream &output, Environment &env, vector<filesystem::path> &paths)
 {
   bool first_clause = true;
   for (const auto & [expr, body] : expr_and_body)
@@ -257,13 +257,13 @@ If::interpret(ostream &output, vector<filesystem::path> &paths)
             if ((ifdef && env.isVariableDefined(vp->getName()))
                 || (ifndef && !env.isVariableDefined(vp->getName())))
               {
-                interpretBody(body, output, paths);
+                interpretBody(body, output, env, paths);
                 break;
               }
           }
         else
           {
-            auto tmp = expr->eval();
+            auto tmp = expr->eval(env);
             RealPtr dp = dynamic_pointer_cast<Real>(tmp);
             BoolPtr bp = dynamic_pointer_cast<Bool>(tmp);
             if (!bp && !dp)
@@ -271,7 +271,7 @@ If::interpret(ostream &output, vector<filesystem::path> &paths)
                                "The condition must evaluate to a boolean or a double", location));
             if ((bp && *bp) || (dp && *dp))
               {
-                interpretBody(body, output, paths);
+                interpretBody(body, output, env, paths);
                 break;
               }
           }
@@ -289,7 +289,7 @@ If::interpret(ostream &output, vector<filesystem::path> &paths)
 }
 
 void
-If::interpretBody(const vector<DirectivePtr> &body, ostream &output, vector<filesystem::path> &paths)
+If::interpretBody(const vector<DirectivePtr> &body, ostream &output, Environment &env, vector<filesystem::path> &paths)
 {
   bool printLine = true;
   for (const auto &statement : body)
@@ -299,6 +299,6 @@ If::interpretBody(const vector<DirectivePtr> &body, ostream &output, vector<file
           statement->printLineInfo(output);
           printLine = false;
         }
-      statement->interpret(output, paths);
+      statement->interpret(output, env, paths);
     }
 }
diff --git a/src/macro/Directives.hh b/src/macro/Directives.hh
index cc7ec6e1293d9ad0a4270a807344de0a110da7b2..426e17f3d5b3b0b48a069225d5a88701a35d5bb0 100644
--- a/src/macro/Directives.hh
+++ b/src/macro/Directives.hh
@@ -30,10 +30,10 @@ namespace macro
   {
     // A Parent class just for clarity
   public:
-    Directive(Environment &env_arg, Tokenizer::location location_arg) :
-      Node(env_arg, move(location_arg)) { }
+    explicit Directive(Tokenizer::location location_arg) :
+      Node(move(location_arg)) { }
     // Directives can be interpreted
-    virtual void interpret(ostream &output, vector<filesystem::path> &paths) = 0;
+    virtual void interpret(ostream &output, Environment &env, vector<filesystem::path> &paths) = 0;
   };
 
 
@@ -45,9 +45,9 @@ namespace macro
   private:
     const string text;
   public:
-    TextNode(string text_arg, Environment &env_arg, Tokenizer::location location_arg) :
-      Directive(env_arg, move(location_arg)), text{move(text_arg)} { }
-    inline void interpret(ostream &output, vector<filesystem::path> &paths) override { output << text; }
+    TextNode(string text_arg, Tokenizer::location location_arg) :
+      Directive(move(location_arg)), text{move(text_arg)} { }
+    inline void interpret(ostream &output, Environment &env, vector<filesystem::path> &paths) override { output << text; }
   };
 
 
@@ -59,9 +59,9 @@ namespace macro
   private:
     const ExpressionPtr expr;
   public:
-    Eval(ExpressionPtr expr_arg, Environment &env_arg, Tokenizer::location location_arg) :
-      Directive(env_arg, move(location_arg)), expr{move(expr_arg)} { }
-    void interpret(ostream &output, vector<filesystem::path> &paths) override;
+    Eval(ExpressionPtr expr_arg, Tokenizer::location location_arg) :
+      Directive(move(location_arg)), expr{move(expr_arg)} { }
+    void interpret(ostream &output, Environment &env, vector<filesystem::path> &paths) override;
   };
 
 
@@ -70,9 +70,9 @@ namespace macro
   private:
     const ExpressionPtr expr;
   public:
-    Include(ExpressionPtr expr_arg, Environment &env_arg, Tokenizer::location location_arg) :
-      Directive(env_arg, move(location_arg)), expr{move(expr_arg)} { }
-    void interpret(ostream &output, vector<filesystem::path> &paths) override;
+    Include(ExpressionPtr expr_arg, Tokenizer::location location_arg) :
+      Directive(move(location_arg)), expr{move(expr_arg)} { }
+    void interpret(ostream &output, Environment &env, vector<filesystem::path> &paths) override;
   };
 
 
@@ -81,9 +81,9 @@ namespace macro
   private:
     const ExpressionPtr expr;
   public:
-    IncludePath(ExpressionPtr expr_arg, Environment &env_arg, Tokenizer::location location_arg) :
-      Directive(env_arg, move(location_arg)), expr{move(expr_arg)} { }
-    void interpret(ostream &output, vector<filesystem::path> &paths) override;
+    IncludePath(ExpressionPtr expr_arg, Tokenizer::location location_arg) :
+      Directive(move(location_arg)), expr{move(expr_arg)} { }
+    void interpret(ostream &output, Environment &env, vector<filesystem::path> &paths) override;
   };
 
 
@@ -96,13 +96,13 @@ namespace macro
   public:
     Define(VariablePtr var_arg,
            ExpressionPtr value_arg,
-           Environment &env_arg, Tokenizer::location location_arg) :
-      Directive(env_arg, move(location_arg)), var{move(var_arg)}, value{move(value_arg)} { }
+           Tokenizer::location location_arg) :
+      Directive(move(location_arg)), var{move(var_arg)}, value{move(value_arg)} { }
     Define(FunctionPtr func_arg,
            ExpressionPtr value_arg,
-           Environment &env_arg, Tokenizer::location location_arg) :
-      Directive(env_arg, move(location_arg)), func{move(func_arg)}, value{move(value_arg)} { }
-    void interpret(ostream &output, vector<filesystem::path> &paths) override;
+           Tokenizer::location location_arg) :
+      Directive(move(location_arg)), func{move(func_arg)}, value{move(value_arg)} { }
+    void interpret(ostream &output, Environment &env, vector<filesystem::path> &paths) override;
   };
 
 
@@ -112,9 +112,9 @@ namespace macro
     const ExpressionPtr expr;
   public:
     Echo(ExpressionPtr expr_arg,
-         Environment &env_arg, Tokenizer::location location_arg) :
-      Directive(env_arg, move(location_arg)), expr{move(expr_arg)} { }
-    void interpret(ostream &output, vector<filesystem::path> &paths) override;
+         Tokenizer::location location_arg) :
+      Directive(move(location_arg)), expr{move(expr_arg)} { }
+    void interpret(ostream &output, Environment &env, vector<filesystem::path> &paths) override;
   };
 
 
@@ -124,9 +124,9 @@ namespace macro
     const ExpressionPtr expr;
   public:
     Error(ExpressionPtr expr_arg,
-          Environment &env_arg, Tokenizer::location location_arg) :
-      Directive(env_arg, move(location_arg)), expr{move(expr_arg)} { }
-    void interpret(ostream &output, vector<filesystem::path> &paths) override;
+          Tokenizer::location location_arg) :
+      Directive(move(location_arg)), expr{move(expr_arg)} { }
+    void interpret(ostream &output, Environment &env, vector<filesystem::path> &paths) override;
   };
 
 
@@ -137,12 +137,12 @@ namespace macro
     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} { }
+                  Tokenizer::location location_arg) :
+      Directive(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, vector<filesystem::path> &paths) override;
+                  Tokenizer::location location_arg) :
+      Directive(move(location_arg)), save{save_arg}, vars{move(vars_arg)} { }
+    void interpret(ostream &output, Environment &env, vector<filesystem::path> &paths) override;
   };
 
 
@@ -156,10 +156,10 @@ namespace macro
     For(vector<VariablePtr> index_vec_arg,
         ExpressionPtr index_vals_arg,
         vector<DirectivePtr> statements_arg,
-        Environment &env_arg, Tokenizer::location location_arg) :
-      Directive(env_arg, move(location_arg)), index_vec{move(index_vec_arg)},
+        Tokenizer::location location_arg) :
+      Directive(move(location_arg)), index_vec{move(index_vec_arg)},
       index_vals{move(index_vals_arg)}, statements{move(statements_arg)} { }
-    void interpret(ostream &output, vector<filesystem::path> &paths) override;
+    void interpret(ostream &output, Environment &env, vector<filesystem::path> &paths) override;
   };
 
 
@@ -178,22 +178,22 @@ namespace macro
     const bool ifdef, ifndef;
   public:
     If(vector<pair<ExpressionPtr, vector<DirectivePtr>>> expr_and_body_arg,
-       Environment &env_arg, Tokenizer::location location_arg,
+       Tokenizer::location location_arg,
        bool ifdef_arg = false, bool ifndef_arg = false) :
-      Directive(env_arg, move(location_arg)), expr_and_body{move(expr_and_body_arg)},
+      Directive(move(location_arg)), expr_and_body{move(expr_and_body_arg)},
       ifdef{ifdef_arg}, ifndef{ifndef_arg} { }
-    void interpret(ostream &output, vector<filesystem::path> &paths) override;
+    void interpret(ostream &output, Environment &env, vector<filesystem::path> &paths) override;
   protected:
     void interpretBody(const vector<DirectivePtr> &body, ostream &output,
-                       vector<filesystem::path> &paths);
+                       Environment &env, vector<filesystem::path> &paths);
   };
 
   class Ifdef : public If
   {
   public:
     Ifdef(vector<pair<ExpressionPtr, vector<DirectivePtr>>> expr_and_body_arg,
-          Environment &env_arg, Tokenizer::location location_arg) :
-      If(move(expr_and_body_arg), env_arg, move(location_arg), true, false) { }
+          Tokenizer::location location_arg) :
+      If(move(expr_and_body_arg), move(location_arg), true, false) { }
   };
 
 
@@ -201,8 +201,8 @@ namespace macro
   {
   public:
     Ifndef(vector<pair<ExpressionPtr, vector<DirectivePtr>>> expr_and_body_arg,
-           Environment &env_arg, Tokenizer::location location_arg) :
-      If(move(expr_and_body_arg), env_arg, move(location_arg), false, true) { }
+           Tokenizer::location location_arg) :
+      If(move(expr_and_body_arg), move(location_arg), false, true) { }
   };
 }
 #endif
diff --git a/src/macro/Driver.cc b/src/macro/Driver.cc
index c05ec35bfafef3d570c91505efd257776b21ece9..6e80a7c403049dd0f6e97cc77d470814e3ded969 100644
--- a/src/macro/Driver.cc
+++ b/src/macro/Driver.cc
@@ -25,7 +25,7 @@ using namespace macro;
 void
 Driver::parse(const string &file_arg, const string &basename_arg, const istream &modfile,
               bool debug, const vector<pair<string, string>> &defines,
-              vector<filesystem::path> &paths, ostream &output)
+              Environment &env, vector<filesystem::path> &paths, ostream &output)
 {
   file = file_arg;
   basename = basename_arg;
@@ -35,9 +35,9 @@ Driver::parse(const string &file_arg, const string &basename_arg, const istream
       stringstream command_line_defines_with_endl;
       for (const auto & [var, val] : defines)
         command_line_defines_with_endl << "@#define " << var << " = " << val << endl;
-      Driver m(env);
+      Driver m;
       istream is(command_line_defines_with_endl.rdbuf());
-      m.parse("command_line_defines", "command_line_defines", is, debug, {}, paths, output);
+      m.parse("command_line_defines", "command_line_defines", is, debug, {}, env, paths, output);
     }
 
   // Handle empty files
@@ -65,7 +65,7 @@ Driver::parse(const string &file_arg, const string &basename_arg, const istream
           statement->printLineInfo(output);
           printLine = false;
         }
-      statement->interpret(output, paths);
+      statement->interpret(output, env, paths);
     }
 }
 
diff --git a/src/macro/Driver.hh b/src/macro/Driver.hh
index 7bf9765559fa4116702e9489bb25e80271037f95..c6ab34d1109f1da03a2ffc29fef8f5e904a2a6ef 100644
--- a/src/macro/Driver.hh
+++ b/src/macro/Driver.hh
@@ -61,14 +61,11 @@ namespace macro
   //! Implements the macro expansion using a Flex scanner and a Bison parser
   class Driver
   {
-  public:
-    Environment &env;
   private:
     vector<DirectivePtr> statements;
     stack<vector<DirectivePtr>> directive_stack;
   public:
-    Driver(Environment &env_arg) :
-      env{env_arg} { }
+    Driver() = default;
     Driver(const Driver &) = delete;
     Driver(Driver &&) = delete;
     Driver &operator=(const Driver &) = delete;
@@ -84,12 +81,11 @@ namespace macro
       }
     };
 
-    //! Starts parsing a file, modifies `paths` and `output`
-    //! Both `paths` and `output` are passed as reference
+    //! Starts parsing a file, modifies `env`, `paths` and `output`
     //! as they are modified by various macro directives
     void parse(const string &file, const string &basename, const istream &modfile,
                bool debug, const vector<pair<string, string>> &defines,
-               vector<filesystem::path> &paths, ostream &output);
+               Environment &env, vector<filesystem::path> &paths, ostream &output);
 
     //! Name of main file being parsed
     string file;
diff --git a/src/macro/Environment.cc b/src/macro/Environment.cc
index 25826a5fbb216e978c36c72528f85da8e06b6081..17b6e1bed3880d431f16a64f4f45985b5dae7b9d 100644
--- a/src/macro/Environment.cc
+++ b/src/macro/Environment.cc
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2019 Dynare Team
+ * Copyright © 2019-2020 Dynare Team
  *
  * This file is part of Dynare.
  *
@@ -28,7 +28,7 @@ Environment::define(VariablePtr var, ExpressionPtr value)
   string name = var->getName();
   if (functions.count(name))
     throw StackTrace("Variable " + name + " was previously defined as a function");
-  variables[move(name)] = value->eval();
+  variables[move(name)] = value->eval(*this);
 }
 
 void
@@ -67,7 +67,7 @@ Environment::getFunction(const string &name) const
 codes::BaseType
 Environment::getType(const string &name) const
 {
-  return getVariable(name)->eval()->getType();
+  return getVariable(name)->eval(const_cast<Environment &>(*this))->getType();
 }
 
 bool
@@ -132,7 +132,7 @@ Environment::printVariable(ostream &output, const string &name, int line, bool s
 {
   output << (save ? "options_.macrovars_line_" + to_string(line) + "." : "  ")
          << name << " = ";
-  getVariable(name)->eval()->print(output, save);
+  getVariable(name)->eval(const_cast<Environment &>(*this))->print(output, save);
   if (save)
     output << ";";
   output << endl;
diff --git a/src/macro/Expressions.cc b/src/macro/Expressions.cc
index a14b380fafd361827ccd58b38718bb8327dbd973..e3fcb5f8f1ce92ce8cda770f103d4070c008ffb7 100644
--- a/src/macro/Expressions.cc
+++ b/src/macro/Expressions.cc
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2019 Dynare Team
+ * Copyright © 2019-2020 Dynare Team
  *
  * This file is part of Dynare.
  *
@@ -25,8 +25,8 @@ BoolPtr
 BaseType::is_different(const BaseTypePtr &btp) const
 {
   if (*(this->is_equal(btp)))
-    return make_shared<Bool>(false, env);
-  return make_shared<Bool>(true, env);
+    return make_shared<Bool>(false);
+  return make_shared<Bool>(true);
 }
 
 BoolPtr
@@ -34,38 +34,38 @@ Bool::is_equal(const BaseTypePtr &btp) const
 {
   auto btp2 = dynamic_pointer_cast<Bool>(btp);
   if (!btp2)
-    return make_shared<Bool>(false, env);
-  return make_shared<Bool>(value == btp2->value, env);
+    return make_shared<Bool>(false);
+  return make_shared<Bool>(value == btp2->value);
 }
 
 BoolPtr
-Bool::logical_and(const ExpressionPtr &ep) const
+Bool::logical_and(const ExpressionPtr &ep, Environment &env) const
 {
   if (!value)
-    return make_shared<Bool>(false, env);
+    return make_shared<Bool>(false);
 
-  auto btp = ep->eval();
+  auto btp = ep->eval(env);
   if (auto btp2 = dynamic_pointer_cast<Bool>(btp); btp2)
-    return make_shared<Bool>(*btp2, env);
+    return make_shared<Bool>(*btp2);
 
   if (auto btp2 = dynamic_pointer_cast<Real>(btp); btp2)
-    return make_shared<Bool>(*btp2, env);
+    return make_shared<Bool>(*btp2);
 
   throw StackTrace("Type mismatch for operands of && operator");
 }
 
 BoolPtr
-Bool::logical_or(const ExpressionPtr &ep) const
+Bool::logical_or(const ExpressionPtr &ep, Environment &env) const
 {
   if (value)
-    return make_shared<Bool>(true, env);
+    return make_shared<Bool>(true);
 
-  auto btp = ep->eval();
+  auto btp = ep->eval(env);
   if (auto btp2 = dynamic_pointer_cast<Bool>(btp); btp2)
-    return make_shared<Bool>(*btp2, env);
+    return make_shared<Bool>(*btp2);
 
   if (auto btp2 = dynamic_pointer_cast<Real>(btp); btp2)
-    return make_shared<Bool>(*btp2, env);
+    return make_shared<Bool>(*btp2);
 
   throw StackTrace("Type mismatch for operands of || operator");
 }
@@ -73,7 +73,7 @@ Bool::logical_or(const ExpressionPtr &ep) const
 BoolPtr
 Bool::logical_not() const
 {
-  return make_shared<Bool>(!value, env);
+  return make_shared<Bool>(!value);
 }
 
 BaseTypePtr
@@ -82,7 +82,7 @@ Real::plus(const BaseTypePtr &btp) const
   auto btp2 = dynamic_pointer_cast<Real>(btp);
   if (!btp2)
     throw StackTrace("Type mismatch for operands of + operator");
-  return make_shared<Real>(value + btp2->value, env);
+  return make_shared<Real>(value + btp2->value);
 }
 
 BaseTypePtr
@@ -91,7 +91,7 @@ Real::minus(const BaseTypePtr &btp) const
   auto btp2 = dynamic_pointer_cast<Real>(btp);
   if (!btp2)
     throw StackTrace("Type mismatch for operands of - operator");
-  return make_shared<Real>(value - btp2->value, env);
+  return make_shared<Real>(value - btp2->value);
 }
 
 BaseTypePtr
@@ -100,7 +100,7 @@ Real::times(const BaseTypePtr &btp) const
   auto btp2 = dynamic_pointer_cast<Real>(btp);
   if (!btp2)
     throw StackTrace("Type mismatch for operands of * operator");
-  return make_shared<Real>(value * btp2->value, env);
+  return make_shared<Real>(value * btp2->value);
 }
 
 BaseTypePtr
@@ -109,7 +109,7 @@ Real::divide(const BaseTypePtr &btp) const
   auto btp2 = dynamic_pointer_cast<Real>(btp);
   if (!btp2)
     throw StackTrace("Type mismatch for operands of / operator");
-  return make_shared<Real>(value / btp2->value, env);
+  return make_shared<Real>(value / btp2->value);
 }
 
 BaseTypePtr
@@ -118,7 +118,7 @@ Real::power(const BaseTypePtr &btp) const
   auto btp2 = dynamic_pointer_cast<Real>(btp);
   if (!btp2)
     throw StackTrace("Type mismatch for operands of ^ operator");
-  return make_shared<Real>(pow(value, btp2->value), env);
+  return make_shared<Real>(pow(value, btp2->value));
 }
 
 BoolPtr
@@ -127,7 +127,7 @@ Real::is_less(const BaseTypePtr &btp) const
   auto btp2 = dynamic_pointer_cast<Real>(btp);
   if (!btp2)
     throw StackTrace("Type mismatch for operands of < operator");
-  return make_shared<Bool>(isless(value, btp2->value), env);
+  return make_shared<Bool>(isless(value, btp2->value));
 }
 
 BoolPtr
@@ -136,7 +136,7 @@ Real::is_greater(const BaseTypePtr &btp) const
   auto btp2 = dynamic_pointer_cast<Real>(btp);
   if (!btp2)
     throw StackTrace("Type mismatch for operands of > operator");
-  return make_shared<Bool>(isgreater(value, btp2->value), env);
+  return make_shared<Bool>(isgreater(value, btp2->value));
 }
 
 BoolPtr
@@ -145,7 +145,7 @@ Real::is_less_equal(const BaseTypePtr &btp) const
   auto btp2 = dynamic_pointer_cast<Real>(btp);
   if (!btp2)
     throw StackTrace("Type mismatch for operands of <= operator");
-  return make_shared<Bool>(islessequal(value, btp2->value), env);
+  return make_shared<Bool>(islessequal(value, btp2->value));
 }
 
 BoolPtr
@@ -154,7 +154,7 @@ Real::is_greater_equal(const BaseTypePtr &btp) const
   auto btp2 = dynamic_pointer_cast<Real>(btp);
   if (!btp2)
     throw StackTrace("Type mismatch for operands of >= operator");
-  return make_shared<Bool>(isgreaterequal(value, btp2->value), env);
+  return make_shared<Bool>(isgreaterequal(value, btp2->value));
 }
 
 BoolPtr
@@ -162,38 +162,38 @@ Real::is_equal(const BaseTypePtr &btp) const
 {
   auto btp2 = dynamic_pointer_cast<Real>(btp);
   if (!btp2)
-    return make_shared<Bool>(false, env);
-  return make_shared<Bool>(value == btp2->value, env);
+    return make_shared<Bool>(false);
+  return make_shared<Bool>(value == btp2->value);
 }
 
 BoolPtr
-Real::logical_and(const ExpressionPtr &ep) const
+Real::logical_and(const ExpressionPtr &ep, Environment &env) const
 {
   if (!value)
-    return make_shared<Bool>(false, env);
+    return make_shared<Bool>(false);
 
-  auto btp = ep->eval();
+  auto btp = ep->eval(env);
   if (auto btp2 = dynamic_pointer_cast<Real>(btp); btp2)
-    return make_shared<Bool>(*btp2, env);
+    return make_shared<Bool>(*btp2);
 
   if (auto btp2 = dynamic_pointer_cast<Bool>(btp); btp2)
-    return make_shared<Bool>(*btp2, env);
+    return make_shared<Bool>(*btp2);
 
   throw StackTrace("Type mismatch for operands of && operator");
 }
 
 BoolPtr
-Real::logical_or(const ExpressionPtr &ep) const
+Real::logical_or(const ExpressionPtr &ep, Environment &env) const
 {
   if (value)
-    return make_shared<Bool>(true, env);
+    return make_shared<Bool>(true);
 
-  auto btp = ep->eval();
+  auto btp = ep->eval(env);
   if (auto btp2 = dynamic_pointer_cast<Real>(btp); btp2)
-    return make_shared<Bool>(*btp2, env);
+    return make_shared<Bool>(*btp2);
 
   if (auto btp2 = dynamic_pointer_cast<Bool>(btp); btp2)
-    return make_shared<Bool>(*btp2, env);
+    return make_shared<Bool>(*btp2);
 
   throw StackTrace("Type mismatch for operands of || operator");
 }
@@ -201,7 +201,7 @@ Real::logical_or(const ExpressionPtr &ep) const
 BoolPtr
 Real::logical_not() const
 {
-  return make_shared<Bool>(!value, env);
+  return make_shared<Bool>(!value);
 }
 
 RealPtr
@@ -210,7 +210,7 @@ Real::max(const BaseTypePtr &btp) const
   auto btp2 = dynamic_pointer_cast<Real>(btp);
   if (!btp2)
     throw StackTrace("Type mismatch for operands of `max` operator");
-  return make_shared<Real>(std::max(value, btp2->value), env);
+  return make_shared<Real>(std::max(value, btp2->value));
 }
 
 RealPtr
@@ -219,7 +219,7 @@ Real::min(const BaseTypePtr &btp) const
   auto btp2 = dynamic_pointer_cast<Real>(btp);
   if (!btp2)
     throw StackTrace("Type mismatch for operands of `min` operator");
-  return make_shared<Real>(std::min(value, btp2->value), env);
+  return make_shared<Real>(std::min(value, btp2->value));
 }
 
 RealPtr
@@ -228,7 +228,7 @@ Real::mod(const BaseTypePtr &btp) const
   auto btp2 = dynamic_pointer_cast<Real>(btp);
   if (!btp2)
     throw StackTrace("Type mismatch for operands of `mod` operator");
-  return make_shared<Real>(std::fmod(value, btp2->value), env);
+  return make_shared<Real>(std::fmod(value, btp2->value));
 }
 
 RealPtr
@@ -238,7 +238,7 @@ Real::normpdf(const BaseTypePtr &btp1, const BaseTypePtr &btp2) const
   auto btp22 = dynamic_pointer_cast<Real>(btp2);
   if (!btp12 || !btp22)
     throw StackTrace("Type mismatch for operands of `normpdf` operator");
-  return make_shared<Real>((1/(btp22->value*std::sqrt(2*M_PI)*std::exp(pow((value-btp12->value)/btp22->value, 2)/2))), env);
+  return make_shared<Real>((1/(btp22->value*std::sqrt(2*M_PI)*std::exp(pow((value-btp12->value)/btp22->value, 2)/2))));
 }
 
 RealPtr
@@ -248,7 +248,7 @@ Real::normcdf(const BaseTypePtr &btp1, const BaseTypePtr &btp2) const
   auto btp22 = dynamic_pointer_cast<Real>(btp2);
   if (!btp12 || !btp22)
     throw StackTrace("Type mismatch for operands of `normpdf` operator");
-  return make_shared<Real>((0.5*(1+std::erf((value-btp12->value)/btp22->value/M_SQRT2))), env);
+  return make_shared<Real>((0.5*(1+std::erf((value-btp12->value)/btp22->value/M_SQRT2))));
 }
 
 BaseTypePtr
@@ -257,7 +257,7 @@ String::plus(const BaseTypePtr &btp) const
   auto btp2 = dynamic_pointer_cast<String>(btp);
   if (!btp2)
     throw StackTrace("Type mismatch for operands of + operator");
-  return make_shared<String>(value + btp2->value, env);
+  return make_shared<String>(value + btp2->value);
 }
 
 BoolPtr
@@ -266,7 +266,7 @@ String::is_less(const BaseTypePtr &btp) const
   auto btp2 = dynamic_pointer_cast<String>(btp);
   if (!btp2)
     throw StackTrace("Type mismatch for operands of < operator");
-  return make_shared<Bool>(value < btp2->value, env);
+  return make_shared<Bool>(value < btp2->value);
 }
 
 BoolPtr
@@ -275,7 +275,7 @@ String::is_greater(const BaseTypePtr &btp) const
   auto btp2 = dynamic_pointer_cast<String>(btp);
   if (!btp2)
     throw StackTrace("Type mismatch for operands of > operator");
-  return make_shared<Bool>(value > btp2->value, env);
+  return make_shared<Bool>(value > btp2->value);
 }
 
 BoolPtr
@@ -284,7 +284,7 @@ String::is_less_equal(const BaseTypePtr &btp) const
   auto btp2 = dynamic_pointer_cast<String>(btp);
   if (!btp2)
     throw StackTrace("Type mismatch for operands of <= operator");
-  return make_shared<Bool>(value <= btp2->value, env);
+  return make_shared<Bool>(value <= btp2->value);
 }
 
 BoolPtr
@@ -293,7 +293,7 @@ String::is_greater_equal(const BaseTypePtr &btp) const
   auto btp2 = dynamic_pointer_cast<String>(btp);
   if (!btp2)
     throw StackTrace("Type mismatch for operands of >= operator");
-  return make_shared<Bool>(value >= btp2->value, env);
+  return make_shared<Bool>(value >= btp2->value);
 }
 
 BoolPtr
@@ -301,20 +301,20 @@ String::is_equal(const BaseTypePtr &btp) const
 {
   auto btp2 = dynamic_pointer_cast<String>(btp);
   if (!btp2)
-    return make_shared<Bool>(false, env);
-  return make_shared<Bool>(value == btp2->value, env);
+    return make_shared<Bool>(false);
+  return make_shared<Bool>(value == btp2->value);
 }
 
 BoolPtr
-String::cast_bool() const
+String::cast_bool(Environment &env) const
 {
   auto f = [](const char &a, const char &b) { return (tolower(a) == tolower(b)); };
 
   if (string tf = "true"; equal(value.begin(), value.end(), tf.begin(), tf.end(), f))
-    return make_shared<Bool>(true, env);
+    return make_shared<Bool>(true);
 
   if (string tf = "false"; equal(value.begin(), value.end(), tf.begin(), tf.end(), f))
-    return make_shared<Bool>(false, env);
+    return make_shared<Bool>(false);
 
   try
     {
@@ -322,7 +322,7 @@ String::cast_bool() const
       double value_d = stod(value, &pos);
       if (pos != value.length())
         throw StackTrace("Entire string not converted");
-      return make_shared<Bool>(static_cast<bool>(value_d), env);
+      return make_shared<Bool>(static_cast<bool>(value_d));
     }
   catch (...)
     {
@@ -331,7 +331,7 @@ String::cast_bool() const
 }
 
 RealPtr
-String::cast_real() const
+String::cast_real(Environment &env) const
 {
   try
     {
@@ -339,7 +339,7 @@ String::cast_real() const
       double value_d = stod(value, &pos);
       if (pos != value.length())
         throw StackTrace("Entire string not converted");
-      return make_shared<Real>(value_d, env);
+      return make_shared<Real>(value_d);
     }
   catch (...)
     {
@@ -356,7 +356,7 @@ Array::plus(const BaseTypePtr &btp) const
 
   vector<ExpressionPtr> arr_copy{arr};
   arr_copy.insert(arr_copy.end(), btp2->arr.begin(), btp2->arr.end());
-  return make_shared<Array>(arr_copy, env);
+  return make_shared<Array>(arr_copy);
 }
 
 BaseTypePtr
@@ -379,7 +379,7 @@ Array::minus(const BaseTypePtr &btp) const
       if (it2 == btp2->arr.cend())
         arr_copy.emplace_back(itbtp);
     }
-  return make_shared<Array>(arr_copy, env);
+  return make_shared<Array>(arr_copy);
 }
 
 BaseTypePtr
@@ -409,10 +409,10 @@ Array::times(const BaseTypePtr &btp) const
         else
           throw StackTrace("Array::times: unsupported type on rhs");
 
-        values.emplace_back(make_shared<Tuple>(new_tuple, env));
+        values.emplace_back(make_shared<Tuple>(new_tuple));
       }
 
-  return make_shared<Array>(values, env);
+  return make_shared<Array>(values);
 }
 
 BaseTypePtr
@@ -422,11 +422,11 @@ Array::power(const BaseTypePtr &btp) const
   if (!btp2 || !*(btp2->isinteger()))
     throw StackTrace("The second argument of the power operator (^) must be an integer");
 
-  auto retval = make_shared<Array>(arr, env);
+  auto retval = make_shared<Array>(arr);
   for (int i = 1; i < *btp2; i++)
     {
-      auto btpv = retval->times(make_shared<Array>(arr, env));
-      retval = make_shared<Array>(dynamic_pointer_cast<Array>(btpv)->getValue(), env);
+      auto btpv = retval->times(make_shared<Array>(arr));
+      retval = make_shared<Array>(dynamic_pointer_cast<Array>(btpv)->getValue());
     }
   return retval;
 }
@@ -436,19 +436,19 @@ Array::is_equal(const BaseTypePtr &btp) const
 {
   auto btp2 = dynamic_pointer_cast<Array>(btp);
   if (!btp2)
-    return make_shared<Bool>(false, env);
+    return make_shared<Bool>(false);
 
   if (arr.size() != btp2->arr.size())
-    return make_shared<Bool>(false, env);
+    return make_shared<Bool>(false);
 
   for (size_t i = 0; i < arr.size(); i++)
     {
       auto bt = dynamic_pointer_cast<BaseType>(arr[i]);
       auto bt2 = dynamic_pointer_cast<BaseType>(btp2->arr[i]);
       if (!*(bt->is_equal(bt2)))
-        return make_shared<Bool>(false, env);
+        return make_shared<Bool>(false);
     }
-  return make_shared<Bool>(true, env);
+  return make_shared<Bool>(true);
 }
 
 ArrayPtr
@@ -479,7 +479,7 @@ Array::set_union(const BaseTypePtr &btp) const
       if (!found)
         new_values.push_back(it);
     }
-  return make_shared<Array>(new_values, env);
+  return make_shared<Array>(new_values);
 }
 
 ArrayPtr
@@ -507,7 +507,7 @@ Array::set_intersection(const BaseTypePtr &btp) const
             }
         }
     }
-  return make_shared<Array>(new_values, env);
+  return make_shared<Array>(new_values);
 }
 
 BoolPtr
@@ -519,9 +519,9 @@ Array::contains(const BaseTypePtr &btp) const
       if (!v2)
         throw StackTrace("Type mismatch for operands of in operator");
       if (*(v2->is_equal(btp)))
-        return make_shared<Bool>(true, env);
+        return make_shared<Bool>(true);
     }
-  return make_shared<Bool>(false, env);
+  return make_shared<Bool>(false);
 }
 
 RealPtr
@@ -535,23 +535,23 @@ Array::sum() const
         throw StackTrace("Type mismatch for operands of in operator");
       retval += *v2;
     }
-  return make_shared<Real>(retval, env);
+  return make_shared<Real>(retval);
 }
 
 BoolPtr
-Array::cast_bool() const
+Array::cast_bool(Environment &env) const
 {
   if (arr.size() != 1)
     throw StackTrace("Array must be of size 1 to be cast to a boolean");
-  return arr.at(0)->eval()->cast_bool();
+  return arr.at(0)->eval(env)->cast_bool(env);
 }
 
 RealPtr
-Array::cast_real() const
+Array::cast_real(Environment &env) const
 {
   if (arr.size() != 1)
     throw StackTrace("Array must be of size 1 to be cast to a real");
-  return arr.at(0)->eval()->cast_real();
+  return arr.at(0)->eval(env)->cast_real(env);
 }
 
 BoolPtr
@@ -559,19 +559,19 @@ Tuple::is_equal(const BaseTypePtr &btp) const
 {
   auto btp2 = dynamic_pointer_cast<Tuple>(btp);
   if (!btp2)
-    return make_shared<Bool>(false, env);
+    return make_shared<Bool>(false);
 
   if (tup.size() != btp2->tup.size())
-    return make_shared<Bool>(false, env);
+    return make_shared<Bool>(false);
 
   for (size_t i = 0; i < tup.size(); i++)
     {
       auto bt = dynamic_pointer_cast<BaseType>(tup[i]);
       auto bt2 = dynamic_pointer_cast<BaseType>(btp2->tup[i]);
       if (!*(bt->is_equal(bt2)))
-        return make_shared<Bool>(false, env);
+        return make_shared<Bool>(false);
     }
-  return make_shared<Bool>(true, env);
+  return make_shared<Bool>(true);
 }
 
 BoolPtr
@@ -583,35 +583,35 @@ Tuple::contains(const BaseTypePtr &btp) const
       if (!v2)
         throw StackTrace("Type mismatch for operands of in operator");
       if (*(v2->is_equal(btp)))
-        return make_shared<Bool>(true, env);
+        return make_shared<Bool>(true);
     }
-  return make_shared<Bool>(false, env);
+  return make_shared<Bool>(false);
 }
 
 BoolPtr
-Tuple::cast_bool() const
+Tuple::cast_bool(Environment &env) const
 {
   if (tup.size() != 1)
     throw StackTrace("Tuple must be of size 1 to be cast to a boolean");
-  return tup.at(0)->eval()->cast_bool();
+  return tup.at(0)->eval(env)->cast_bool(env);
 }
 
 RealPtr
-Tuple::cast_real() const
+Tuple::cast_real(Environment &env) const
 {
   if (tup.size() != 1)
     throw StackTrace("Tuple must be of size 1 to be cast to a real");
-  return tup.at(0)->eval()->cast_real();
+  return tup.at(0)->eval(env)->cast_real(env);
 }
 
 BaseTypePtr
-Range::eval()
+Range::eval(Environment &env)
 {
-  RealPtr incdbl = make_shared<Real>(1, env);
+  RealPtr incdbl = make_shared<Real>(1);
   if (inc)
-    incdbl = dynamic_pointer_cast<Real>(inc->eval());
-  RealPtr startdbl = dynamic_pointer_cast<Real>(start->eval());
-  RealPtr enddbl = dynamic_pointer_cast<Real>(end->eval());
+    incdbl = dynamic_pointer_cast<Real>(inc->eval(env));
+  RealPtr startdbl = dynamic_pointer_cast<Real>(start->eval(env));
+  RealPtr enddbl = dynamic_pointer_cast<Real>(end->eval(env));
   if (!startdbl || !enddbl || !incdbl)
     throw StackTrace("To create an array from a range using the colon operator, "
                      "the arguments must evaluate to reals");
@@ -619,38 +619,38 @@ Range::eval()
   vector<ExpressionPtr> arr;
   if (*incdbl > 0 && *startdbl <= *enddbl)
     for (double i = *startdbl; i <= *enddbl; i += *incdbl)
-      arr.emplace_back(make_shared<Real>(i, env));
+      arr.emplace_back(make_shared<Real>(i));
   else if (*startdbl >= *enddbl && *incdbl < 0)
     for (double i = *startdbl; i >= *enddbl; i += *incdbl)
-      arr.emplace_back(make_shared<Real>(i, env));
+      arr.emplace_back(make_shared<Real>(i));
 
-  return make_shared<Array>(arr, env, location);
+  return make_shared<Array>(arr, location);
 }
 
 BaseTypePtr
-Array::eval()
+Array::eval(Environment &env)
 {
   vector<ExpressionPtr> retval;
   for (const auto &it : arr)
-    retval.emplace_back(it->eval());
-  return make_shared<Array>(retval, env);
+    retval.emplace_back(it->eval(env));
+  return make_shared<Array>(retval);
 }
 
 BaseTypePtr
-Tuple::eval()
+Tuple::eval(Environment &env)
 {
   vector<ExpressionPtr> retval;
   for (const auto &it : tup)
-    retval.emplace_back(it->eval());
-  return make_shared<Tuple>(retval, env);
+    retval.emplace_back(it->eval(env));
+  return make_shared<Tuple>(retval);
 }
 
 BaseTypePtr
-Variable::eval()
+Variable::eval(Environment &env)
 {
   if (indices && !indices->empty())
     {
-      ArrayPtr map = dynamic_pointer_cast<Array>(indices->eval());
+      ArrayPtr map = dynamic_pointer_cast<Array>(indices->eval(env));
       vector<ExpressionPtr> index = map->getValue();
       vector<int> ind;
       for (const auto &it : index)
@@ -703,7 +703,7 @@ Variable::eval()
                 {
                   throw StackTrace("variable", "Index out of range", location);
                 }
-            return make_shared<String>(retvals, env);
+            return make_shared<String>(retvals);
           }
         case codes::BaseType::Array:
           {
@@ -712,7 +712,7 @@ Variable::eval()
             for (auto it : ind)
               try
                 {
-                  retval.emplace_back(ap->at(it - 1)->eval());
+                  retval.emplace_back(ap->at(it - 1)->eval(env));
                 }
               catch (const out_of_range &ex)
                 {
@@ -722,15 +722,15 @@ Variable::eval()
             if (retval.size() == 1)
               return retval.at(0);
             vector<ExpressionPtr> retvala(retval.begin(), retval.end());
-            return make_shared<Array>(retvala, env);
+            return make_shared<Array>(retvala);
           }
         }
     }
-  return env.getVariable(name)->eval();
+  return env.getVariable(name)->eval(env);
 }
 
 BaseTypePtr
-Function::eval()
+Function::eval(Environment &env)
 {
   FunctionPtr func;
   ExpressionPtr body;
@@ -755,9 +755,9 @@ Function::eval()
       for (size_t i = 0; i < func->args.size(); i++)
         {
           VariablePtr mvp = dynamic_pointer_cast<Variable>(func->args.at(i));
-          env.define(mvp, args.at(i)->eval());
+          env.define(mvp, args.at(i)->eval(env));
         }
-      auto retval = body->eval();
+      auto retval = body->eval(env);
       env = env_orig;
       return retval;
     }
@@ -769,90 +769,90 @@ Function::eval()
 }
 
 BaseTypePtr
-UnaryOp::eval()
+UnaryOp::eval(Environment &env)
 {
   try
     {
       switch (op_code)
         {
         case codes::UnaryOp::cast_bool:
-          return arg->eval()->cast_bool();
+          return arg->eval(env)->cast_bool(env);
         case codes::UnaryOp::cast_real:
-          return arg->eval()->cast_real();
+          return arg->eval(env)->cast_real(env);
         case codes::UnaryOp::cast_string:
-          return arg->eval()->cast_string();
+          return arg->eval(env)->cast_string();
         case codes::UnaryOp::cast_tuple:
-          return arg->eval()->cast_tuple();
+          return arg->eval(env)->cast_tuple();
         case codes::UnaryOp::cast_array:
-          return arg->eval()->cast_array();
+          return arg->eval(env)->cast_array();
         case codes::UnaryOp::logical_not:
-          return arg->eval()->logical_not();
+          return arg->eval(env)->logical_not();
         case codes::UnaryOp::unary_minus:
-          return arg->eval()->unary_minus();
+          return arg->eval(env)->unary_minus();
         case codes::UnaryOp::unary_plus:
-          return arg->eval()->unary_plus();
+          return arg->eval(env)->unary_plus();
         case codes::UnaryOp::length:
-          return arg->eval()->length();
+          return arg->eval(env)->length();
         case codes::UnaryOp::isempty:
-          return arg->eval()->isempty();
+          return arg->eval(env)->isempty();
         case codes::UnaryOp::isboolean:
-          return arg->eval()->isboolean();
+          return arg->eval(env)->isboolean();
         case codes::UnaryOp::isreal:
-          return arg->eval()->isreal();
+          return arg->eval(env)->isreal();
         case codes::UnaryOp::isstring:
-          return arg->eval()->isstring();
+          return arg->eval(env)->isstring();
         case codes::UnaryOp::istuple:
-          return arg->eval()->istuple();
+          return arg->eval(env)->istuple();
         case codes::UnaryOp::isarray:
-          return arg->eval()->isarray();
+          return arg->eval(env)->isarray();
         case codes::UnaryOp::exp:
-          return arg->eval()->exp();
+          return arg->eval(env)->exp();
         case codes::UnaryOp::ln:
-          return arg->eval()->ln();
+          return arg->eval(env)->ln();
         case codes::UnaryOp::log10:
-          return arg->eval()->log10();
+          return arg->eval(env)->log10();
         case codes::UnaryOp::sin:
-          return arg->eval()->sin();
+          return arg->eval(env)->sin();
         case codes::UnaryOp::cos:
-          return arg->eval()->cos();
+          return arg->eval(env)->cos();
         case codes::UnaryOp::tan:
-          return arg->eval()->tan();
+          return arg->eval(env)->tan();
         case codes::UnaryOp::asin:
-          return arg->eval()->asin();
+          return arg->eval(env)->asin();
         case codes::UnaryOp::acos:
-          return arg->eval()->acos();
+          return arg->eval(env)->acos();
         case codes::UnaryOp::atan:
-          return arg->eval()->atan();
+          return arg->eval(env)->atan();
         case codes::UnaryOp::sqrt:
-          return arg->eval()->sqrt();
+          return arg->eval(env)->sqrt();
         case codes::UnaryOp::cbrt:
-          return arg->eval()->cbrt();
+          return arg->eval(env)->cbrt();
         case codes::UnaryOp::sign:
-          return arg->eval()->sign();
+          return arg->eval(env)->sign();
         case codes::UnaryOp::floor:
-          return arg->eval()->floor();
+          return arg->eval(env)->floor();
         case codes::UnaryOp::ceil:
-          return arg->eval()->ceil();
+          return arg->eval(env)->ceil();
         case codes::UnaryOp::trunc:
-          return arg->eval()->trunc();
+          return arg->eval(env)->trunc();
         case codes::UnaryOp::sum:
-          return arg->eval()->sum();
+          return arg->eval(env)->sum();
         case codes::UnaryOp::erf:
-          return arg->eval()->erf();
+          return arg->eval(env)->erf();
         case codes::UnaryOp::erfc:
-          return arg->eval()->erfc();
+          return arg->eval(env)->erfc();
         case codes::UnaryOp::gamma:
-          return arg->eval()->gamma();
+          return arg->eval(env)->gamma();
         case codes::UnaryOp::lgamma:
-          return arg->eval()->lgamma();
+          return arg->eval(env)->lgamma();
         case codes::UnaryOp::round:
-          return arg->eval()->round();
+          return arg->eval(env)->round();
         case codes::UnaryOp::normpdf:
-          return arg->eval()->normpdf();
+          return arg->eval(env)->normpdf();
         case codes::UnaryOp::normcdf:
-          return arg->eval()->normcdf();
+          return arg->eval(env)->normcdf();
         case codes::UnaryOp::defined:
-          return arg->eval()->defined();
+          return arg->eval(env)->defined(env);
         }
     }
   catch (StackTrace &ex)
@@ -869,50 +869,50 @@ UnaryOp::eval()
 }
 
 BaseTypePtr
-BinaryOp::eval()
+BinaryOp::eval(Environment &env)
 {
   try
     {
       switch (op_code)
         {
         case codes::BinaryOp::plus:
-          return arg1->eval()->plus(arg2->eval());
+          return arg1->eval(env)->plus(arg2->eval(env));
         case codes::BinaryOp::minus:
-          return arg1->eval()->minus(arg2->eval());
+          return arg1->eval(env)->minus(arg2->eval(env));
         case codes::BinaryOp::times:
-          return arg1->eval()->times(arg2->eval());
+          return arg1->eval(env)->times(arg2->eval(env));
         case codes::BinaryOp::divide:
-          return arg1->eval()->divide(arg2->eval());
+          return arg1->eval(env)->divide(arg2->eval(env));
         case codes::BinaryOp::power:
-          return arg1->eval()->power(arg2->eval());
+          return arg1->eval(env)->power(arg2->eval(env));
         case codes::BinaryOp::equal_equal:
-          return arg1->eval()->is_equal(arg2->eval());
+          return arg1->eval(env)->is_equal(arg2->eval(env));
         case codes::BinaryOp::not_equal:
-          return arg1->eval()->is_different(arg2->eval());
+          return arg1->eval(env)->is_different(arg2->eval(env));
         case codes::BinaryOp::less:
-          return arg1->eval()->is_less(arg2->eval());
+          return arg1->eval(env)->is_less(arg2->eval(env));
         case codes::BinaryOp::greater:
-          return arg1->eval()->is_greater(arg2->eval());
+          return arg1->eval(env)->is_greater(arg2->eval(env));
         case codes::BinaryOp::less_equal:
-          return arg1->eval()->is_less_equal(arg2->eval());
+          return arg1->eval(env)->is_less_equal(arg2->eval(env));
         case codes::BinaryOp::greater_equal:
-          return arg1->eval()->is_greater_equal(arg2->eval());
+          return arg1->eval(env)->is_greater_equal(arg2->eval(env));
         case codes::BinaryOp::logical_and:
-          return arg1->eval()->logical_and(arg2);
+          return arg1->eval(env)->logical_and(arg2, env);
         case codes::BinaryOp::logical_or:
-          return arg1->eval()->logical_or(arg2);
+          return arg1->eval(env)->logical_or(arg2, env);
         case codes::BinaryOp::in:
-          return arg2->eval()->contains(arg1->eval());
+          return arg2->eval(env)->contains(arg1->eval(env));
         case codes::BinaryOp::set_union:
-          return arg1->eval()->set_union(arg2->eval());
+          return arg1->eval(env)->set_union(arg2->eval(env));
         case codes::BinaryOp::set_intersection:
-          return arg1->eval()->set_intersection(arg2->eval());
+          return arg1->eval(env)->set_intersection(arg2->eval(env));
         case codes::BinaryOp::max:
-          return arg1->eval()->max(arg2->eval());
+          return arg1->eval(env)->max(arg2->eval(env));
         case codes::BinaryOp::min:
-          return arg1->eval()->min(arg2->eval());
+          return arg1->eval(env)->min(arg2->eval(env));
         case codes::BinaryOp::mod:
-          return arg1->eval()->mod(arg2->eval());
+          return arg1->eval(env)->mod(arg2->eval(env));
         }
     }
   catch (StackTrace &ex)
@@ -929,16 +929,16 @@ BinaryOp::eval()
 }
 
 BaseTypePtr
-TrinaryOp::eval()
+TrinaryOp::eval(Environment &env)
 {
   try
     {
       switch (op_code)
         {
         case codes::TrinaryOp::normpdf:
-          return arg1->eval()->normpdf(arg2->eval(), arg3->eval());
+          return arg1->eval(env)->normpdf(arg2->eval(env), arg3->eval(env));
         case codes::TrinaryOp::normcdf:
-          return arg1->eval()->normcdf(arg2->eval(), arg3->eval());
+          return arg1->eval(env)->normcdf(arg2->eval(env), arg3->eval(env));
         }
     }
   catch (StackTrace &ex)
@@ -955,14 +955,14 @@ TrinaryOp::eval()
 }
 
 BaseTypePtr
-Comprehension::eval()
+Comprehension::eval(Environment &env)
 {
   ArrayPtr input_set;
   VariablePtr vp;
   TuplePtr mt;
   try
     {
-      input_set = dynamic_pointer_cast<Array>(c_set->eval());
+      input_set = dynamic_pointer_cast<Array>(c_set->eval(env));
       if (!input_set)
         throw StackTrace("Comprehension", "The input set must evaluate to an array", location);
       vp = dynamic_pointer_cast<Variable>(c_vars);
@@ -1009,14 +1009,14 @@ Comprehension::eval()
         if (!c_expr)
           throw StackTrace("Comprehension", "Internal Error: Impossible case", location);
         else
-          values.emplace_back(c_expr->clone()->eval());
+          values.emplace_back(c_expr->clone()->eval(env));
       else
         {
           RealPtr dp;
           BoolPtr bp;
           try
             {
-              auto tmp = c_when->eval();
+              auto tmp = c_when->eval(env);
               dp = dynamic_pointer_cast<Real>(tmp);
               bp = dynamic_pointer_cast<Bool>(tmp);
               if (!bp && !dp)
@@ -1029,12 +1029,12 @@ Comprehension::eval()
             }
           if ((bp && *bp) || (dp && *dp))
             if (c_expr)
-              values.emplace_back(c_expr->clone()->eval());
+              values.emplace_back(c_expr->clone()->eval(env));
             else
               values.emplace_back(btp);
         }
     }
-  return make_shared<Array>(values, env);
+  return make_shared<Array>(values);
 }
 
 ExpressionPtr
@@ -1043,7 +1043,7 @@ Tuple::clone() const noexcept
   vector<ExpressionPtr> tup_copy;
   for (const auto &it : tup)
     tup_copy.emplace_back(it->clone());
-  return make_shared<Tuple>(tup_copy, env, location);
+  return make_shared<Tuple>(tup_copy, location);
 }
 
 ExpressionPtr
@@ -1052,7 +1052,7 @@ Array::clone() const noexcept
   vector<ExpressionPtr> arr_copy;
   for (const auto &it : arr)
     arr_copy.emplace_back(it->clone());
-  return make_shared<Array>(arr_copy, env, location);
+  return make_shared<Array>(arr_copy, location);
 }
 
 ExpressionPtr
@@ -1061,18 +1061,18 @@ Function::clone() const noexcept
   vector<ExpressionPtr> args_copy;
   for (const auto &it : args)
     args_copy.emplace_back(it->clone());
-  return make_shared<Function>(name, args_copy, env, location);
+  return make_shared<Function>(name, args_copy, location);
 }
 
 ExpressionPtr
 Comprehension::clone() const noexcept
 {
   if (c_expr && c_when)
-    return make_shared<Comprehension>(c_expr->clone(), c_vars->clone(), c_set->clone(), c_when->clone(), env, location);
+    return make_shared<Comprehension>(c_expr->clone(), c_vars->clone(), c_set->clone(), c_when->clone(), location);
   else if (c_expr)
-    return make_shared<Comprehension>(c_expr->clone(), c_vars->clone(), c_set->clone(), env, location);
+    return make_shared<Comprehension>(c_expr->clone(), c_vars->clone(), c_set->clone(), location);
   else
-    return make_shared<Comprehension>(true, c_vars->clone(), c_set->clone(), c_when->clone(), env, location);
+    return make_shared<Comprehension>(true, c_vars->clone(), c_set->clone(), c_when->clone(), location);
 }
 
 string
diff --git a/src/macro/Expressions.hh b/src/macro/Expressions.hh
index ec7c69f17394f8af540a7e8b388ed6de5f5cb8c3..3715fd651c1e754ecbb384f7f1b0914a473398e6 100644
--- a/src/macro/Expressions.hh
+++ b/src/macro/Expressions.hh
@@ -81,11 +81,10 @@ namespace macro
   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)} { }
+    explicit Node(Tokenizer::location location_arg) :
+      location{move(location_arg)} { }
     virtual ~Node() = default;
   public:
     inline Tokenizer::location getLocation() const noexcept { return location; }
@@ -113,11 +112,11 @@ namespace macro
   class Expression : public Node
   {
   public:
-    Expression(Environment &env_arg, Tokenizer::location location_arg) :
-      Node(env_arg, move(location_arg)) { }
+    explicit Expression(Tokenizer::location location_arg) :
+      Node(move(location_arg)) { }
     virtual string to_string() const noexcept = 0;
     virtual void print(ostream &output, bool matlab_output = false) const noexcept = 0;
-    virtual BaseTypePtr eval() = 0;
+    virtual BaseTypePtr eval(Environment &env) = 0;
     virtual ExpressionPtr clone() const noexcept = 0;
   };
 
@@ -125,10 +124,10 @@ namespace macro
   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)) { }
+    explicit BaseType(Tokenizer::location location_arg = Tokenizer::location()) :
+      Expression(move(location_arg)) { }
     virtual codes::BaseType getType() const noexcept = 0;
-    inline BaseTypePtr eval() override { return shared_from_this(); }
+    inline BaseTypePtr eval(Environment &env) 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"); }
@@ -143,20 +142,20 @@ namespace macro
     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 ExpressionPtr &ep) const { throw StackTrace("Operator && does not exist for this type"); }
-    virtual BoolPtr logical_or(const ExpressionPtr &ep) const { throw StackTrace("Operator || does not exist for this type"); }
+    virtual BoolPtr logical_and(const ExpressionPtr &ep, Environment &env) const { throw StackTrace("Operator && does not exist for this type"); }
+    virtual BoolPtr logical_or(const ExpressionPtr &ep, Environment &env) 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 RealPtr length() const { throw StackTrace("Operator `length` does not exist for this type"); }
     virtual BoolPtr isempty() const { throw StackTrace("Operator `isempty` does not exist for this type"); }
-    virtual BoolPtr isboolean() const noexcept { return make_shared<Bool>(false, env, location); }
-    virtual BoolPtr isreal() const noexcept { return make_shared<Bool>(false, env, location); }
-    virtual BoolPtr isinteger() const noexcept { return make_shared<Bool>(false, env, location); }
-    virtual BoolPtr isstring() const noexcept { return make_shared<Bool>(false, env, location); }
-    virtual BoolPtr istuple() const noexcept { return make_shared<Bool>(false, env, location); }
-    virtual BoolPtr isarray() const noexcept { return make_shared<Bool>(false, env, location); }
+    virtual BoolPtr isboolean() const noexcept { return make_shared<Bool>(false, location); }
+    virtual BoolPtr isreal() const noexcept { return make_shared<Bool>(false, location); }
+    virtual BoolPtr isinteger() const noexcept { return make_shared<Bool>(false, location); }
+    virtual BoolPtr isstring() const noexcept { return make_shared<Bool>(false, location); }
+    virtual BoolPtr istuple() const noexcept { return make_shared<Bool>(false, location); }
+    virtual BoolPtr isarray() const noexcept { return make_shared<Bool>(false, location); }
     virtual RealPtr max(const BaseTypePtr &btp) const { throw StackTrace("Operator `max` does not exist for this type"); }
     virtual RealPtr min(const BaseTypePtr &btp) const { throw StackTrace("Operator `min` does not exist for this type"); }
     virtual RealPtr mod(const BaseTypePtr &btp) const { throw StackTrace("Operator `mod` does not exist for this type"); }
@@ -189,12 +188,12 @@ namespace macro
     virtual RealPtr normpdf(const BaseTypePtr &btp1, const BaseTypePtr &btp2) const { throw StackTrace("Operator `normpdf` does not exist for this type"); }
     virtual RealPtr normcdf() const { throw StackTrace("Operator `normcdf` does not exist for this type"); }
     virtual RealPtr normcdf(const BaseTypePtr &btp1, const BaseTypePtr &btp2) const { throw StackTrace("Operator `normcdf` does not exist for this type"); }
-    virtual BoolPtr cast_bool() const { throw StackTrace("This type cannot be cast to a boolean"); }
-    virtual RealPtr cast_real() const { throw StackTrace("This type cannot be cast to a real"); }
+    virtual BoolPtr cast_bool(Environment &env) const { throw StackTrace("This type cannot be cast to a boolean"); }
+    virtual RealPtr cast_real(Environment &env) const { throw StackTrace("This type cannot be cast to a real"); }
     virtual StringPtr cast_string() const { throw StackTrace("This type cannot be cast to a string"); }
     virtual TuplePtr cast_tuple() const { throw StackTrace("This type cannot be cast to a tuple"); }
     virtual ArrayPtr cast_array() const { throw StackTrace("This type cannot be cast to an array"); }
-    virtual BoolPtr defined() const { throw StackTrace("Operator `defined` does not exist for this type"); }
+    virtual BoolPtr defined(const Environment &env) const { throw StackTrace("Operator `defined` does not exist for this type"); }
   };
 
 
@@ -204,30 +203,30 @@ namespace macro
     const bool value;
   public:
     Bool(bool value_arg,
-         Environment &env_arg, Tokenizer::location location_arg = Tokenizer::location()) :
-      BaseType(env_arg, move(location_arg)),
+         Tokenizer::location location_arg = Tokenizer::location()) :
+      BaseType(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, bool matlab_output = false) const noexcept override { output << to_string(); }
-    inline ExpressionPtr clone() const noexcept override { return make_shared<Bool>(value, env, location); }
+    inline ExpressionPtr clone() const noexcept override { return make_shared<Bool>(value, location); }
   public:
     operator bool() const { return value; }
     BoolPtr is_equal(const BaseTypePtr &btp) const override;
-    BoolPtr logical_and(const ExpressionPtr &ep) const override;
-    BoolPtr logical_or(const ExpressionPtr &ep) const override;
+    BoolPtr logical_and(const ExpressionPtr &ep, Environment &env) const override;
+    BoolPtr logical_or(const ExpressionPtr &ep, Environment &env) const override;
     BoolPtr logical_not() const override;
-    inline BoolPtr isboolean() const noexcept override { return make_shared<Bool>(true, env, location); }
-    inline BoolPtr cast_bool() const override { return make_shared<Bool>(value, env); }
-    inline RealPtr cast_real() const override { return value ? make_shared<Real>(1, env) : make_shared<Real>(0, env); }
-    inline StringPtr cast_string() const override { return make_shared<String>(this->to_string(), env); }
+    inline BoolPtr isboolean() const noexcept override { return make_shared<Bool>(true, location); }
+    inline BoolPtr cast_bool(Environment &env) const override { return make_shared<Bool>(value); }
+    inline RealPtr cast_real(Environment &env) const override { return value ? make_shared<Real>(1) : make_shared<Real>(0); }
+    inline StringPtr cast_string() const override { return make_shared<String>(this->to_string()); }
     inline TuplePtr cast_tuple() const override
     {
-      return make_shared<Tuple>(vector<ExpressionPtr>{make_shared<Bool>(value, env)}, env);
+      return make_shared<Tuple>(vector<ExpressionPtr>{make_shared<Bool>(value)});
     }
     inline ArrayPtr cast_array() const override
     {
-      return make_shared<Array>(vector<ExpressionPtr>{make_shared<Bool>(value, env)}, env);
+      return make_shared<Array>(vector<ExpressionPtr>{make_shared<Bool>(value)});
     }
   };
 
@@ -240,12 +239,12 @@ namespace macro
     // Use strtod to handle extreme cases (e.g. 1e500, 1e-500), nan, inf
     // See Note in NumericalConstants::AddNonNegativeConstant
     Real(const string &value_arg,
-         Environment &env_arg, Tokenizer::location location_arg = Tokenizer::location()) :
-      BaseType(env_arg, move(location_arg)),
+         Tokenizer::location location_arg = Tokenizer::location()) :
+      BaseType(move(location_arg)),
       value{strtod(value_arg.c_str(), nullptr)} { }
     Real(double value_arg,
-         Environment &env_arg, Tokenizer::location location_arg = Tokenizer::location()) :
-      BaseType(env_arg, move(location_arg)),
+         Tokenizer::location location_arg = Tokenizer::location()) :
+      BaseType(move(location_arg)),
       value{value_arg} { }
     inline codes::BaseType getType() const noexcept override { return codes::BaseType::Real; }
     inline string to_string() const noexcept override
@@ -255,13 +254,13 @@ namespace macro
       return strs.str();
     }
     inline void print(ostream &output, bool matlab_output = false) const noexcept override { output << to_string(); }
-    inline ExpressionPtr clone() const noexcept override { return make_shared<Real>(value, env, location); }
+    inline ExpressionPtr clone() const noexcept override { return make_shared<Real>(value, location); }
   public:
     operator double() const { return value; }
     BaseTypePtr plus(const BaseTypePtr &bt) const override;
-    inline BaseTypePtr unary_plus() const override { return make_shared<Real>(value, env); }
+    inline BaseTypePtr unary_plus() const override { return make_shared<Real>(value); }
     BaseTypePtr minus(const BaseTypePtr &bt) const override;
-    inline BaseTypePtr unary_minus() const override { return make_shared<Real>(-value, env); }
+    inline BaseTypePtr unary_minus() const override { return make_shared<Real>(-value); }
     BaseTypePtr times(const BaseTypePtr &bt) const override;
     BaseTypePtr divide(const BaseTypePtr &bt) const override;
     BaseTypePtr power(const BaseTypePtr &btp) const override;
@@ -270,65 +269,65 @@ namespace macro
     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 BoolPtr isreal() const noexcept override { return make_shared<Bool>(true, env, location); }
+    inline BoolPtr isreal() const noexcept override { return make_shared<Bool>(true, location); }
     inline BoolPtr isinteger() const noexcept override
     {
       double intpart;
-      return make_shared<Bool>(modf(value, &intpart) == 0.0, env, location);
+      return make_shared<Bool>(modf(value, &intpart) == 0.0, location);
     }
-    BoolPtr logical_and(const ExpressionPtr &ep) const override;
-    BoolPtr logical_or(const ExpressionPtr &ep) const override;
+    BoolPtr logical_and(const ExpressionPtr &ep, Environment &env) const override;
+    BoolPtr logical_or(const ExpressionPtr &ep, Environment &env) const override;
     BoolPtr logical_not() const override;
     RealPtr max(const BaseTypePtr &btp) const override;
     RealPtr min(const BaseTypePtr &btp) const override;
     RealPtr mod(const BaseTypePtr &btp) const override;
-    inline RealPtr exp() const override { return make_shared<Real>(std::exp(value), env); }
-    inline RealPtr ln() const override { return make_shared<Real>(std::log(value), env); }
-    inline RealPtr log10() const override { return make_shared<Real>(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 RealPtr sin() const override { return make_shared<Real>(std::sin(value), env); }
-    inline RealPtr cos() const override { return make_shared<Real>(std::cos(value), env); }
-    inline RealPtr tan() const override { return make_shared<Real>(std::tan(value), env); }
-    inline RealPtr asin() const override { return make_shared<Real>(std::asin(value), env); }
-    inline RealPtr acos() const override { return make_shared<Real>(std::acos(value), env); }
-    inline RealPtr atan() const override { return make_shared<Real>(std::atan(value), env); }
-    inline RealPtr sqrt() const override { return make_shared<Real>(std::sqrt(value), env); }
-    inline RealPtr cbrt() const override { return make_shared<Real>(std::cbrt(value), env); }
+    inline RealPtr exp() const override { return make_shared<Real>(std::exp(value)); }
+    inline RealPtr ln() const override { return make_shared<Real>(std::log(value)); }
+    inline RealPtr log10() const override { return make_shared<Real>(std::log10(value)); }
+    inline BoolPtr isinf() const override { return make_shared<Bool>(std::isinf(value)); }
+    inline BoolPtr isnan() const override { return make_shared<Bool>(std::isnan(value)); }
+    inline BoolPtr isfinite() const override { return make_shared<Bool>(std::isfinite(value)); }
+    inline BoolPtr isnormal() const override { return make_shared<Bool>(std::isnormal(value)); }
+    inline RealPtr sin() const override { return make_shared<Real>(std::sin(value)); }
+    inline RealPtr cos() const override { return make_shared<Real>(std::cos(value)); }
+    inline RealPtr tan() const override { return make_shared<Real>(std::tan(value)); }
+    inline RealPtr asin() const override { return make_shared<Real>(std::asin(value)); }
+    inline RealPtr acos() const override { return make_shared<Real>(std::acos(value)); }
+    inline RealPtr atan() const override { return make_shared<Real>(std::atan(value)); }
+    inline RealPtr sqrt() const override { return make_shared<Real>(std::sqrt(value)); }
+    inline RealPtr cbrt() const override { return make_shared<Real>(std::cbrt(value)); }
     inline RealPtr sign() const override
     {
-      return make_shared<Real>((value > 0) ? 1. : ((value < 0) ? -1. : 0.), env);
+      return make_shared<Real>((value > 0) ? 1. : ((value < 0) ? -1. : 0.));
     }
-    inline RealPtr floor() const override { return make_shared<Real>(std::floor(value), env); }
-    inline RealPtr ceil() const override { return make_shared<Real>(std::ceil(value), env); }
-    inline RealPtr trunc() const override { return make_shared<Real>(std::trunc(value), env); }
-    inline RealPtr erf() const override { return make_shared<Real>(std::erf(value), env); }
-    inline RealPtr erfc() const override { return make_shared<Real>(std::erfc(value), env); }
-    inline RealPtr gamma() const override { return make_shared<Real>(std::tgamma(value), env); }
-    inline RealPtr lgamma() const override { return make_shared<Real>(std::lgamma(value), env); }
-    inline RealPtr round() const override { return make_shared<Real>(std::round(value), env); }
+    inline RealPtr floor() const override { return make_shared<Real>(std::floor(value)); }
+    inline RealPtr ceil() const override { return make_shared<Real>(std::ceil(value)); }
+    inline RealPtr trunc() const override { return make_shared<Real>(std::trunc(value)); }
+    inline RealPtr erf() const override { return make_shared<Real>(std::erf(value)); }
+    inline RealPtr erfc() const override { return make_shared<Real>(std::erfc(value)); }
+    inline RealPtr gamma() const override { return make_shared<Real>(std::tgamma(value)); }
+    inline RealPtr lgamma() const override { return make_shared<Real>(std::lgamma(value)); }
+    inline RealPtr round() const override { return make_shared<Real>(std::round(value)); }
     inline RealPtr normpdf() const override
     {
-      return normpdf(make_shared<Real>(0, env), make_shared<Real>(1, env));
+      return normpdf(make_shared<Real>(0), make_shared<Real>(1));
     }
     RealPtr normpdf(const BaseTypePtr &btp1, const BaseTypePtr &btp2) const override;
     inline RealPtr normcdf() const override
     {
-      return normcdf(make_shared<Real>(0, env), make_shared<Real>(1, env));
+      return normcdf(make_shared<Real>(0), make_shared<Real>(1));
     }
     RealPtr normcdf(const BaseTypePtr &btp1, const BaseTypePtr &btp2) const override;
-    inline BoolPtr cast_bool() const override { return make_shared<Bool>(static_cast<bool>(value), env); }
-    inline RealPtr cast_real() const override { return make_shared<Real>(value, env); }
-    inline StringPtr cast_string() const override { return make_shared<String>(this->to_string(), env); }
+    inline BoolPtr cast_bool(Environment &env) const override { return make_shared<Bool>(static_cast<bool>(value)); }
+    inline RealPtr cast_real(Environment &env) const override { return make_shared<Real>(value); }
+    inline StringPtr cast_string() const override { return make_shared<String>(this->to_string()); }
     inline TuplePtr cast_tuple() const override
     {
-      return make_shared<Tuple>(vector<ExpressionPtr>{make_shared<Real>(value, env)}, env);
+      return make_shared<Tuple>(vector<ExpressionPtr>{make_shared<Real>(value)});
     }
     inline ArrayPtr cast_array() const override
     {
-      return make_shared<Array>(vector<ExpressionPtr>{make_shared<Real>(value, env)}, env);
+      return make_shared<Array>(vector<ExpressionPtr>{make_shared<Real>(value)});
     }
   };
 
@@ -338,13 +337,13 @@ namespace macro
     const string value;
   public:
     String(string value_arg,
-           Environment &env_arg, Tokenizer::location location_arg = Tokenizer::location()) :
-      BaseType(env_arg, move(location_arg)),
+           Tokenizer::location location_arg = Tokenizer::location()) :
+      BaseType(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; }
     void print(ostream &output, bool matlab_output = false) const noexcept override;
-    inline ExpressionPtr clone() const noexcept override { return make_shared<String>(value, env, location); }
+    inline ExpressionPtr clone() const noexcept override { return make_shared<String>(value, location); }
   public:
     operator string() const { return value; }
     BaseTypePtr plus(const BaseTypePtr &bt) const override;
@@ -353,23 +352,23 @@ namespace macro
     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 BoolPtr isstring() const noexcept override { return make_shared<Bool>(true, env, location); }
-    inline RealPtr length() const override { return make_shared<Real>(value.size(), env); }
-    inline BoolPtr isempty() const override { return make_shared<Bool>(value.empty(), env); }
-    BoolPtr cast_bool() const override;
-    RealPtr cast_real() const override;
-    inline StringPtr cast_string() const override { return make_shared<String>(value, env); }
+    inline BoolPtr isstring() const noexcept override { return make_shared<Bool>(true, location); }
+    inline RealPtr length() const override { return make_shared<Real>(value.size()); }
+    inline BoolPtr isempty() const override { return make_shared<Bool>(value.empty()); }
+    BoolPtr cast_bool(Environment &env) const override;
+    RealPtr cast_real(Environment &env) const override;
+    inline StringPtr cast_string() const override { return make_shared<String>(value); }
     inline TuplePtr cast_tuple() const override
     {
-      return make_shared<Tuple>(vector<ExpressionPtr>{make_shared<String>(value, env)}, env);
+      return make_shared<Tuple>(vector<ExpressionPtr>{make_shared<String>(value)});
     }
     inline ArrayPtr cast_array() const override
     {
-      return make_shared<Array>(vector<ExpressionPtr>{make_shared<String>(value, env)}, env);
+      return make_shared<Array>(vector<ExpressionPtr>{make_shared<String>(value)});
     }
-    inline BoolPtr defined() const override
+    inline BoolPtr defined(const Environment &env) const override
     {
-      return make_shared<Bool>(env.isSymbolDefined(value), env);
+      return make_shared<Bool>(env.isSymbolDefined(value));
     }
   };
 
@@ -380,13 +379,13 @@ namespace macro
     const vector<ExpressionPtr> tup;
   public:
     Tuple(vector<ExpressionPtr> tup_arg,
-          Environment &env_arg, Tokenizer::location location_arg = Tokenizer::location()) :
-      BaseType(env_arg, move(location_arg)),
+          Tokenizer::location location_arg = Tokenizer::location()) :
+      BaseType(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, bool matlab_output = false) const noexcept override;
-    BaseTypePtr eval() override;
+    BaseTypePtr eval(Environment &env) override;
     ExpressionPtr clone() const noexcept override;
   public:
     inline size_t size() const { return tup.size(); }
@@ -394,15 +393,15 @@ namespace macro
     inline const vector<ExpressionPtr> &getValue() const { return tup; }
     inline const ExpressionPtr &at(int i) const { return tup.at(i); }
     BoolPtr is_equal(const BaseTypePtr &btp) const override;
-    inline BoolPtr istuple() const noexcept override { return make_shared<Bool>(true, env, location); }
+    inline BoolPtr istuple() const noexcept override { return make_shared<Bool>(true, location); }
     BoolPtr contains(const BaseTypePtr &btp) const override;
-    inline RealPtr length() const override { return make_shared<Real>(tup.size(), env); }
-    inline BoolPtr isempty() const override { return make_shared<Bool>(empty(), env); }
-    BoolPtr cast_bool() const override;
-    RealPtr cast_real() const override;
-    inline StringPtr cast_string() const override { return make_shared<String>(this->to_string(), env); }
-    inline TuplePtr cast_tuple() const override { return make_shared<Tuple>(tup, env); }
-    inline ArrayPtr cast_array() const override { return make_shared<Array>(tup, env); }
+    inline RealPtr length() const override { return make_shared<Real>(tup.size()); }
+    inline BoolPtr isempty() const override { return make_shared<Bool>(empty()); }
+    BoolPtr cast_bool(Environment &env) const override;
+    RealPtr cast_real(Environment &env) const override;
+    inline StringPtr cast_string() const override { return make_shared<String>(this->to_string()); }
+    inline TuplePtr cast_tuple() const override { return make_shared<Tuple>(tup); }
+    inline ArrayPtr cast_array() const override { return make_shared<Array>(tup); }
   };
 
 
@@ -412,12 +411,12 @@ namespace macro
     const vector<ExpressionPtr> arr;
   public:
     Array(vector<ExpressionPtr> arr_arg,
-          Environment &env_arg, Tokenizer::location location_arg = Tokenizer::location()) :
-      BaseType(env_arg, move(location_arg)), arr{move(arr_arg)} { }
+          Tokenizer::location location_arg = Tokenizer::location()) :
+      BaseType(move(location_arg)), arr{move(arr_arg)} { }
     inline codes::BaseType getType() const noexcept override { return codes::BaseType::Array; }
     string to_string() const noexcept override;
     void print(ostream &output, bool matlab_output = false) const noexcept override;
-    BaseTypePtr eval() override;
+    BaseTypePtr eval(Environment &env) override;
     ExpressionPtr clone() const noexcept override;
   public:
     inline size_t size() const { return arr.size(); }
@@ -429,18 +428,18 @@ namespace macro
     BaseTypePtr times(const BaseTypePtr &bt) const override;
     BaseTypePtr power(const BaseTypePtr &btp) const override;
     BoolPtr is_equal(const BaseTypePtr &btp) const override;
-    inline BoolPtr isarray() const noexcept override { return make_shared<Bool>(true, env, location); }
+    inline BoolPtr isarray() const noexcept override { return make_shared<Bool>(true, location); }
     ArrayPtr set_union(const BaseTypePtr &btp) const override;
     ArrayPtr set_intersection(const BaseTypePtr &btp) const override;
     BoolPtr contains(const BaseTypePtr &btp) const override;
-    inline RealPtr length() const override { return make_shared<Real>(arr.size(), env); }
-    inline BoolPtr isempty() const override { return make_shared<Bool>(empty(), env); }
+    inline RealPtr length() const override { return make_shared<Real>(arr.size()); }
+    inline BoolPtr isempty() const override { return make_shared<Bool>(empty()); }
     RealPtr sum() const override;
-    BoolPtr cast_bool() const override;
-    RealPtr cast_real() const override;
-    inline StringPtr cast_string() const override { return make_shared<String>(this->to_string(), env); }
-    inline TuplePtr cast_tuple() const override { return make_shared<Tuple>(arr, env); }
-    inline ArrayPtr cast_array() const override { return make_shared<Array>(arr, env); }
+    BoolPtr cast_bool(Environment &env) const override;
+    RealPtr cast_real(Environment &env) const override;
+    inline StringPtr cast_string() const override { return make_shared<String>(this->to_string()); }
+    inline TuplePtr cast_tuple() const override { return make_shared<Tuple>(arr); }
+    inline ArrayPtr cast_array() const override { return make_shared<Array>(arr); }
   };
 
 
@@ -450,11 +449,11 @@ namespace macro
     const ExpressionPtr start, inc, end;
   public:
     Range(ExpressionPtr start_arg, ExpressionPtr end_arg,
-          Environment &env_arg, Tokenizer::location location_arg) :
-      BaseType(env_arg, move(location_arg)), start{move(start_arg)}, end{move(end_arg)} { }
+          Tokenizer::location location_arg) :
+      BaseType(move(location_arg)), start{move(start_arg)}, end{move(end_arg)} { }
     Range(ExpressionPtr start_arg, ExpressionPtr inc_arg, ExpressionPtr end_arg,
-          Environment &env_arg, Tokenizer::location location_arg) :
-      BaseType(env_arg, move(location_arg)),
+          Tokenizer::location location_arg) :
+      BaseType(move(location_arg)),
       start{move(start_arg)}, inc{move(inc_arg)}, end{move(end_arg)} { }
     inline codes::BaseType getType() const noexcept override { return codes::BaseType::Range; }
     inline string to_string() const noexcept override
@@ -465,12 +464,12 @@ namespace macro
       return retval + end->to_string() + "]";
     }
     inline void print(ostream &output, bool matlab_output = false) const noexcept override { output << to_string(); }
-    BaseTypePtr eval() override;
+    BaseTypePtr eval(Environment &env) override;
     inline ExpressionPtr clone() const noexcept override
     {
       return inc ?
-        make_shared<Range>(start, inc, end, env, location)
-        : make_shared<Range>(start, end, env, location);
+        make_shared<Range>(start, inc, end, location)
+        : make_shared<Range>(start, end, location);
     }
   public:
     inline BoolPtr is_equal(const BaseTypePtr &btp) const override
@@ -487,22 +486,22 @@ namespace macro
     const ArrayPtr indices; // for indexing strings/arrays
   public:
     Variable(string name_arg,
-             Environment &env_arg, Tokenizer::location location_arg) :
-      Expression(env_arg, move(location_arg)), name{move(name_arg)} { }
+             Tokenizer::location location_arg) :
+      Expression(move(location_arg)), name{move(name_arg)} { }
     Variable(string name_arg, ArrayPtr indices_arg,
-             Environment &env_arg, Tokenizer::location location_arg) :
-      Expression(env_arg, move(location_arg)), name{move(name_arg)}, indices{move(indices_arg)} { }
+             Tokenizer::location location_arg) :
+      Expression(move(location_arg)), name{move(name_arg)}, indices{move(indices_arg)} { }
     inline string to_string() const noexcept override { return name; }
     inline void print(ostream &output, bool matlab_output = false) const noexcept override { output << name; }
-    BaseTypePtr eval() override;
+    BaseTypePtr eval(Environment &env) override;
     inline ExpressionPtr clone() const noexcept override
     {
-      return indices ? make_shared<Variable>(name, indices, env, location) :
-        make_shared<Variable>(name, env, location);
+      return indices ? make_shared<Variable>(name, indices, location) :
+        make_shared<Variable>(name, location);
     }
   public:
     inline const string &getName() const noexcept { return name; }
-    inline codes::BaseType getType() const { return env.getType(name); }
+    inline codes::BaseType getType(const Environment &env) const { return env.getType(name); }
   };
 
 
@@ -514,14 +513,14 @@ namespace macro
   public:
     Function(string name_arg,
              vector<ExpressionPtr> args_arg,
-             Environment &env_arg, Tokenizer::location location_arg) :
-      Expression(env_arg, move(location_arg)), name{move(name_arg)}, args{move(args_arg)} { }
+             Tokenizer::location location_arg) :
+      Expression(move(location_arg)), name{move(name_arg)}, args{move(args_arg)} { }
     string to_string() const noexcept override;
     inline void print(ostream &output, bool matlab_output = false) const noexcept override
     {
       printName(output); printArgs(output);
     }
-    BaseTypePtr eval() override;
+    BaseTypePtr eval(Environment &env) override;
     ExpressionPtr clone() const noexcept override;
   public:
     inline void printName(ostream &output) const noexcept { output << name; }
@@ -539,14 +538,14 @@ namespace macro
   public:
     UnaryOp(codes::UnaryOp op_code_arg,
             ExpressionPtr arg_arg,
-            Environment &env_arg, Tokenizer::location location_arg) :
-      Expression(env_arg, move(location_arg)), op_code{move(op_code_arg)}, arg{move(arg_arg)} { }
+            Tokenizer::location location_arg) :
+      Expression(move(location_arg)), op_code{move(op_code_arg)}, arg{move(arg_arg)} { }
     string to_string() const noexcept override;
     void print(ostream &output, bool matlab_output = false) const noexcept override;
-    BaseTypePtr eval() override;
+    BaseTypePtr eval(Environment &env) override;
     inline ExpressionPtr clone() const noexcept override
     {
-      return make_shared<UnaryOp>(op_code, arg->clone(), env, location);
+      return make_shared<UnaryOp>(op_code, arg->clone(), location);
     }
   };
 
@@ -559,16 +558,16 @@ namespace macro
   public:
     BinaryOp(codes::BinaryOp op_code_arg,
              ExpressionPtr arg1_arg, ExpressionPtr arg2_arg,
-             Environment &env_arg, Tokenizer::location location_arg) :
-      Expression(env_arg, move(location_arg)), op_code{op_code_arg},
+             Tokenizer::location location_arg) :
+      Expression(move(location_arg)), op_code{op_code_arg},
       arg1{move(arg1_arg)}, arg2{move(arg2_arg)} { }
   public:
     string to_string() const noexcept override;
     void print(ostream &output, bool matlab_output = false) const noexcept override;
-    BaseTypePtr eval() override;
+    BaseTypePtr eval(Environment &env) override;
     inline ExpressionPtr clone() const noexcept override
     {
-      return make_shared<BinaryOp>(op_code, arg1->clone(), arg2->clone(), env, location);
+      return make_shared<BinaryOp>(op_code, arg1->clone(), arg2->clone(), location);
     }
   };
 
@@ -581,15 +580,15 @@ namespace macro
   public:
     TrinaryOp(codes::TrinaryOp op_code_arg,
               ExpressionPtr arg1_arg, ExpressionPtr arg2_arg, ExpressionPtr arg3_arg,
-              Environment &env_arg, Tokenizer::location location_arg) :
-      Expression(env_arg, move(location_arg)), op_code{op_code_arg},
+              Tokenizer::location location_arg) :
+      Expression(move(location_arg)), op_code{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, bool matlab_output = false) const noexcept override;
-    BaseTypePtr eval() override;
+    BaseTypePtr eval(Environment &env) override;
     inline ExpressionPtr clone() const noexcept override
     {
-      return make_shared<TrinaryOp>(op_code, arg1->clone(), arg2->clone(), arg3->clone(), env, location);
+      return make_shared<TrinaryOp>(op_code, arg1->clone(), arg2->clone(), arg3->clone(), location);
     }
   };
 
@@ -608,26 +607,26 @@ namespace macro
                   ExpressionPtr c_vars_arg,
                   ExpressionPtr c_set_arg,
                   ExpressionPtr c_when_arg,
-                  Environment &env_arg, Tokenizer::location location_arg) :
-      Expression(env_arg, move(location_arg)),
+                  Tokenizer::location location_arg) :
+      Expression(move(location_arg)),
       c_expr{move(c_expr_arg)}, c_vars{move(c_vars_arg)},
       c_set{move(c_set_arg)}, c_when{move(c_when_arg)} { }
     Comprehension(ExpressionPtr c_expr_arg,
                   ExpressionPtr c_vars_arg,
                   ExpressionPtr c_set_arg,
-                  Environment &env_arg, Tokenizer::location location_arg) :
-      Expression(env_arg, move(location_arg)),
+                  Tokenizer::location location_arg) :
+      Expression(move(location_arg)),
       c_expr{move(c_expr_arg)}, c_vars{move(c_vars_arg)}, c_set{move(c_set_arg)} { }
     Comprehension(bool filter_only_arg,
                   ExpressionPtr c_vars_arg,
                   ExpressionPtr c_set_arg,
                   ExpressionPtr c_when_arg,
-                  Environment &env_arg, Tokenizer::location location_arg) :
-      Expression(env_arg, move(location_arg)),
+                  Tokenizer::location location_arg) :
+      Expression(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, bool matlab_output = false) const noexcept override;
-    BaseTypePtr eval() override;
+    BaseTypePtr eval(Environment &env) override;
     ExpressionPtr clone() const noexcept override;
   };
 }
diff --git a/src/macro/Parser.yy b/src/macro/Parser.yy
index 2c72ea84cf0182549aeafc53c91eba5d8ed7a5af..9537824c8130a10a4532f6ea597a44b9ea909a7f 100644
--- a/src/macro/Parser.yy
+++ b/src/macro/Parser.yy
@@ -126,35 +126,35 @@ directive : directive_one_line EOL
           ;
 
 directive_one_line : INCLUDE expr
-                     { $$ = make_shared<Include>($2, driver.env, @$); }
+                     { $$ = make_shared<Include>($2, @$); }
                    | INCLUDEPATH expr
-                     { $$ = make_shared<IncludePath>($2, driver.env, @$); }
+                     { $$ = make_shared<IncludePath>($2, @$); }
                    | DEFINE symbol
                      {
-                       auto tmp = make_shared<Real>("1", driver.env, @$);
-                       $$ = make_shared<Define>($2, tmp, driver.env, @$);
+                       auto tmp = make_shared<Real>("1", @$);
+                       $$ = make_shared<Define>($2, tmp, @$);
                      }
                    | DEFINE symbol EQUAL expr
-                     { $$ = make_shared<Define>($2, $4, driver.env, @$); }
+                     { $$ = make_shared<Define>($2, $4, @$); }
                    | DEFINE function EQUAL expr
-                     { $$ = make_shared<Define>($2, $4, driver.env, @$); }
+                     { $$ = make_shared<Define>($2, $4, @$); }
                    | D_ECHO expr
-                     { $$ = make_shared<Echo>($2, driver.env, @$); }
+                     { $$ = make_shared<Echo>($2, @$); }
                    | ERROR expr
-                     { $$ = make_shared<Error>($2, driver.env, @$); }
+                     { $$ = make_shared<Error>($2, @$); }
                    | ECHOMACROVARS
-                     { $$ = make_shared<EchoMacroVars>(false, driver.env, @$); }
+                     { $$ = make_shared<EchoMacroVars>(false, @$); }
                    | ECHOMACROVARS name_list
-                     { $$ = make_shared<EchoMacroVars>(false, $2, driver.env, @$); }
+                     { $$ = make_shared<EchoMacroVars>(false, $2, @$); }
                    | ECHOMACROVARS LPAREN SAVE RPAREN
-                     { $$ = make_shared<EchoMacroVars>(true, driver.env, @$); }
+                     { $$ = make_shared<EchoMacroVars>(true, @$); }
                    | ECHOMACROVARS LPAREN SAVE RPAREN name_list
-                     { $$ = make_shared<EchoMacroVars>(true, $5, driver.env, @$); }
+                     { $$ = make_shared<EchoMacroVars>(true, $5, @$); }
                    | LINE QUOTED_STRING NUMBER
                      {
                        // `@#line` is ignored; adjust newlines in output to accord
                        auto l = static_cast<Tokenizer::parser::location_type>(@$);
-                       $$ = make_shared<TextNode>(string(l.end.line - l.begin.line + 1, '\n'), driver.env, @$);
+                       $$ = make_shared<TextNode>(string(l.end.line - l.begin.line + 1, '\n'), @$);
                      }
                    ;
 
@@ -198,28 +198,28 @@ for : FOR { driver.pushContext(); } expr IN expr for_when EOL statements ENDFOR
           error(@1, "For loop indices must be a variable or a tuple");
 
         auto vdp = driver.popContext();
-        vdp.emplace_back(make_shared<TextNode>("\n", driver.env, @9));
+        vdp.emplace_back(make_shared<TextNode>("\n", @9));
 
         if (!$6)
-          $$ = make_shared<For>(vvnp, $5, vdp, driver.env, @$);
+          $$ = make_shared<For>(vvnp, $5, vdp, @$);
         else
           {
-            auto tmpc = make_shared<Comprehension>(true, $3, $5, $6, driver.env, @6);
-            $$ = make_shared<For>(vvnp, tmpc, vdp, driver.env, @$);
+            auto tmpc = make_shared<Comprehension>(true, $3, $5, $6, @6);
+            $$ = make_shared<For>(vvnp, tmpc, vdp, @$);
           }
       }
     ;
 
 if : IF { driver.pushContext(); } if_list ENDIF
-     { $$ = make_shared<If>($3, driver.env, @$); }
+     { $$ = make_shared<If>($3, @$); }
    ;
 
 ifdef : IFDEF { driver.pushContext(); } if_list ENDIF
-        { $$ = make_shared<Ifdef>($3, driver.env, @$); }
+        { $$ = make_shared<Ifdef>($3, @$); }
       ;
 
 ifndef : IFNDEF { driver.pushContext(); } if_list ENDIF
-         { $$ = make_shared<Ifndef>($3, driver.env, @$); }
+         { $$ = make_shared<Ifndef>($3, @$); }
        ;
 
 if_list : if_list1
@@ -233,13 +233,13 @@ if_list : if_list1
 if_list1 : expr EOL
            {
              auto context = driver.popContext();
-             context.emplace_back(make_shared<TextNode>("\n", driver.env, @2));
+             context.emplace_back(make_shared<TextNode>("\n", @2));
              $$ = {{$1, context}};
            }
          | expr EOL statements
            {
              auto context = driver.popContext();
-             context.emplace_back(make_shared<TextNode>("\n", driver.env, @3));
+             context.emplace_back(make_shared<TextNode>("\n", @3));
              $$ = {{$1, context}};
            }
          | if_list1 elseif
@@ -254,13 +254,13 @@ elseif_begin : ELSEIF { driver.pushContext(); } ;
 elseif : elseif_begin expr EOL
          {
            auto context = driver.popContext();
-           context.emplace_back(make_shared<TextNode>("\n", driver.env, @3));
+           context.emplace_back(make_shared<TextNode>("\n", @3));
            $$ = {$2, context};
          }
        | elseif_begin expr EOL statements
          {
            auto context = driver.popContext();
-           context.emplace_back(make_shared<TextNode>("\n", driver.env, @4));
+           context.emplace_back(make_shared<TextNode>("\n", @4));
            $$ = {$2, context};
          }
        ;
@@ -270,35 +270,35 @@ else_begin : ELSE { driver.pushContext(); } ;
 else : else_begin EOL
        {
          auto context = driver.popContext();
-         context.emplace_back(make_shared<TextNode>("\n", driver.env, @2));
-         $$ = {make_shared<Bool>(true, driver.env, @1), context};
+         context.emplace_back(make_shared<TextNode>("\n", @2));
+         $$ = {make_shared<Bool>(true, @1), context};
        }
      | else_begin EOL statements
        {
          auto context = driver.popContext();
-         context.emplace_back(make_shared<TextNode>("\n", driver.env, @3));
-         $$ = {make_shared<Bool>(true, driver.env, @1), context};
+         context.emplace_back(make_shared<TextNode>("\n", @3));
+         $$ = {make_shared<Bool>(true, @1), context};
        }
      ;
 
 text : TEXT
-       { $$ = make_shared<TextNode>($1, driver.env, @$); }
+       { $$ = make_shared<TextNode>($1, @$); }
      | EOL
-       { $$ = make_shared<TextNode>($1, driver.env, @$); }
+       { $$ = make_shared<TextNode>($1, @$); }
      ;
 
 eval : BEGIN_EVAL expr END_EVAL
-       { $$ = make_shared<Eval>($2, driver.env, @$); }
+       { $$ = make_shared<Eval>($2, @$); }
      ;
 
 symbol : NAME
-         { $$ = make_shared<Variable>($1, driver.env, @$); }
+         { $$ = make_shared<Variable>($1, @$); }
        ;
 
 function : NAME LPAREN RPAREN
-           { $$ = make_shared<Function>($1, vector<ExpressionPtr>(), driver.env, @$); }
+           { $$ = make_shared<Function>($1, vector<ExpressionPtr>(), @$); }
          | NAME LPAREN function_args RPAREN
-           { $$ = make_shared<Function>($1, $3, driver.env, @$); }
+           { $$ = make_shared<Function>($1, $3, @$); }
          ;
 
 function_args : symbol
@@ -331,164 +331,163 @@ primary_expr : LPAREN expr RPAREN
                { $$ = $1; } // Explicit rule needed for type conversion
              | NAME LBRACKET comma_expr RBRACKET
                {
-                 $$ = make_shared<Variable>($1, make_shared<Array>($3, driver.env, @3),
-                                            driver.env, @$);
+                 $$ = make_shared<Variable>($1, make_shared<Array>($3, @3), @$);
                }
              | NAME LPAREN comma_expr RPAREN
-               { $$ = make_shared<Function>($1, $3, driver.env, @$); }
+               { $$ = make_shared<Function>($1, $3, @$); }
              | TRUE
-               { $$ = make_shared<Bool>(true, driver.env, @$); }
+               { $$ = make_shared<Bool>(true, @$); }
              | FALSE
-               { $$ = make_shared<Bool>(false, driver.env, @$); }
+               { $$ = make_shared<Bool>(false, @$); }
              | NUMBER
-               { $$ = make_shared<Real>($1, driver.env, @$); }
+               { $$ = make_shared<Real>($1, @$); }
              | QUOTED_STRING
-               { $$ = make_shared<String>($1, driver.env, @$); }
+               { $$ = make_shared<String>($1, @$); }
              | LBRACKET comma_expr RBRACKET
-               { $$ = make_shared<Array>($2, driver.env, @$); }
+               { $$ = make_shared<Array>($2, @$); }
              | LPAREN tuple_comma_expr RPAREN
-               { $$ = make_shared<Tuple>($2, driver.env, @$); }
+               { $$ = make_shared<Tuple>($2, @$); }
              | LBRACKET expr IN expr WHEN expr RBRACKET
-               { $$ = make_shared<Comprehension>(true, $2, $4, $6, driver.env, @$); }
+               { $$ = make_shared<Comprehension>(true, $2, $4, $6, @$); }
              | LBRACKET expr FOR expr IN expr RBRACKET
-               { $$ = make_shared<Comprehension>($2, $4, $6, driver.env, @$); }
+               { $$ = make_shared<Comprehension>($2, $4, $6, @$); }
              | LBRACKET expr FOR expr IN expr WHEN expr RBRACKET
-               { $$ = make_shared<Comprehension>($2, $4, $6, $8, driver.env, @$); }
+               { $$ = make_shared<Comprehension>($2, $4, $6, $8, @$); }
              | LENGTH LPAREN expr RPAREN
-               { $$ = make_shared<UnaryOp>(codes::UnaryOp::length, $3, driver.env, @$); }
+               { $$ = make_shared<UnaryOp>(codes::UnaryOp::length, $3, @$); }
              | ISEMPTY LPAREN expr RPAREN
-               { $$ = make_shared<UnaryOp>(codes::UnaryOp::isempty, $3, driver.env, @$); }
+               { $$ = make_shared<UnaryOp>(codes::UnaryOp::isempty, $3, @$); }
              | ISBOOLEAN LPAREN expr RPAREN
-               { $$ = make_shared<UnaryOp>(codes::UnaryOp::isboolean, $3, driver.env, @$); }
+               { $$ = make_shared<UnaryOp>(codes::UnaryOp::isboolean, $3, @$); }
              | ISREAL LPAREN expr RPAREN
-               { $$ = make_shared<UnaryOp>(codes::UnaryOp::isreal, $3, driver.env, @$); }
+               { $$ = make_shared<UnaryOp>(codes::UnaryOp::isreal, $3, @$); }
              | ISSTRING LPAREN expr RPAREN
-               { $$ = make_shared<UnaryOp>(codes::UnaryOp::isstring, $3, driver.env, @$); }
+               { $$ = make_shared<UnaryOp>(codes::UnaryOp::isstring, $3, @$); }
              | ISTUPLE LPAREN expr RPAREN
-               { $$ = make_shared<UnaryOp>(codes::UnaryOp::istuple, $3, driver.env, @$); }
+               { $$ = make_shared<UnaryOp>(codes::UnaryOp::istuple, $3, @$); }
              | ISARRAY LPAREN expr RPAREN
-               { $$ = make_shared<UnaryOp>(codes::UnaryOp::isarray, $3, driver.env, @$); }
+               { $$ = make_shared<UnaryOp>(codes::UnaryOp::isarray, $3, @$); }
              | EXP LPAREN expr RPAREN
-               { $$ = make_shared<UnaryOp>(codes::UnaryOp::exp, $3, driver.env, @$); }
+               { $$ = make_shared<UnaryOp>(codes::UnaryOp::exp, $3, @$); }
              | LOG LPAREN expr RPAREN
-               { $$ = make_shared<UnaryOp>(codes::UnaryOp::ln, $3, driver.env, @$); }
+               { $$ = make_shared<UnaryOp>(codes::UnaryOp::ln, $3, @$); }
              | LN LPAREN expr RPAREN
-               { $$ = make_shared<UnaryOp>(codes::UnaryOp::ln, $3, driver.env, @$); }
+               { $$ = make_shared<UnaryOp>(codes::UnaryOp::ln, $3, @$); }
              | LOG10 LPAREN expr RPAREN
-               { $$ = make_shared<UnaryOp>(codes::UnaryOp::log10, $3, driver.env, @$); }
+               { $$ = make_shared<UnaryOp>(codes::UnaryOp::log10, $3, @$); }
              | SIN LPAREN expr RPAREN
-               { $$ = make_shared<UnaryOp>(codes::UnaryOp::sin, $3, driver.env, @$); }
+               { $$ = make_shared<UnaryOp>(codes::UnaryOp::sin, $3, @$); }
              | COS LPAREN expr RPAREN
-               { $$ = make_shared<UnaryOp>(codes::UnaryOp::cos, $3, driver.env, @$); }
+               { $$ = make_shared<UnaryOp>(codes::UnaryOp::cos, $3, @$); }
              | TAN LPAREN expr RPAREN
-               { $$ = make_shared<UnaryOp>(codes::UnaryOp::tan, $3, driver.env, @$); }
+               { $$ = make_shared<UnaryOp>(codes::UnaryOp::tan, $3, @$); }
              | ASIN LPAREN expr RPAREN
-               { $$ = make_shared<UnaryOp>(codes::UnaryOp::asin, $3, driver.env, @$); }
+               { $$ = make_shared<UnaryOp>(codes::UnaryOp::asin, $3, @$); }
              | ACOS LPAREN expr RPAREN
-               { $$ = make_shared<UnaryOp>(codes::UnaryOp::acos, $3, driver.env, @$); }
+               { $$ = make_shared<UnaryOp>(codes::UnaryOp::acos, $3, @$); }
              | ATAN LPAREN expr RPAREN
-               { $$ = make_shared<UnaryOp>(codes::UnaryOp::atan, $3, driver.env, @$); }
+               { $$ = make_shared<UnaryOp>(codes::UnaryOp::atan, $3, @$); }
              | SQRT LPAREN expr RPAREN
-               { $$ = make_shared<UnaryOp>(codes::UnaryOp::sqrt, $3, driver.env, @$); }
+               { $$ = make_shared<UnaryOp>(codes::UnaryOp::sqrt, $3, @$); }
              | CBRT LPAREN expr RPAREN
-               { $$ = make_shared<UnaryOp>(codes::UnaryOp::cbrt, $3, driver.env, @$); }
+               { $$ = make_shared<UnaryOp>(codes::UnaryOp::cbrt, $3, @$); }
              | SIGN LPAREN expr RPAREN
-               { $$ = make_shared<UnaryOp>(codes::UnaryOp::sign, $3, driver.env, @$); }
+               { $$ = make_shared<UnaryOp>(codes::UnaryOp::sign, $3, @$); }
              | FLOOR LPAREN expr RPAREN
-               { $$ = make_shared<UnaryOp>(codes::UnaryOp::floor, $3, driver.env, @$); }
+               { $$ = make_shared<UnaryOp>(codes::UnaryOp::floor, $3, @$); }
              | CEIL LPAREN expr RPAREN
-               { $$ = make_shared<UnaryOp>(codes::UnaryOp::ceil, $3, driver.env, @$); }
+               { $$ = make_shared<UnaryOp>(codes::UnaryOp::ceil, $3, @$); }
              | TRUNC LPAREN expr RPAREN
-               { $$ = make_shared<UnaryOp>(codes::UnaryOp::trunc, $3, driver.env, @$); }
+               { $$ = make_shared<UnaryOp>(codes::UnaryOp::trunc, $3, @$); }
              | SUM LPAREN expr RPAREN
-               { $$ = make_shared<UnaryOp>(codes::UnaryOp::sum, $3, driver.env, @$); }
+               { $$ = make_shared<UnaryOp>(codes::UnaryOp::sum, $3, @$); }
              | ERF LPAREN expr RPAREN
-               { $$ = make_shared<UnaryOp>(codes::UnaryOp::erf, $3, driver.env, @$); }
+               { $$ = make_shared<UnaryOp>(codes::UnaryOp::erf, $3, @$); }
              | ERFC LPAREN expr RPAREN
-               { $$ = make_shared<UnaryOp>(codes::UnaryOp::erfc, $3, driver.env, @$); }
+               { $$ = make_shared<UnaryOp>(codes::UnaryOp::erfc, $3, @$); }
              | GAMMA LPAREN expr RPAREN
-               { $$ = make_shared<UnaryOp>(codes::UnaryOp::gamma, $3, driver.env, @$); }
+               { $$ = make_shared<UnaryOp>(codes::UnaryOp::gamma, $3, @$); }
              | LGAMMA LPAREN expr RPAREN
-               { $$ = make_shared<UnaryOp>(codes::UnaryOp::lgamma, $3, driver.env, @$); }
+               { $$ = make_shared<UnaryOp>(codes::UnaryOp::lgamma, $3, @$); }
              | ROUND LPAREN expr RPAREN
-               { $$ = make_shared<UnaryOp>(codes::UnaryOp::round, $3, driver.env, @$); }
+               { $$ = make_shared<UnaryOp>(codes::UnaryOp::round, $3, @$); }
              | NORMPDF LPAREN expr RPAREN
-               { $$ = make_shared<UnaryOp>(codes::UnaryOp::normpdf, $3, driver.env, @$); }
+               { $$ = make_shared<UnaryOp>(codes::UnaryOp::normpdf, $3, @$); }
              | NORMCDF LPAREN expr RPAREN
-               { $$ = make_shared<UnaryOp>(codes::UnaryOp::normcdf, $3, driver.env, @$); }
+               { $$ = make_shared<UnaryOp>(codes::UnaryOp::normcdf, $3, @$); }
              | MAX LPAREN expr COMMA expr RPAREN
-               { $$ = make_shared<BinaryOp>(codes::BinaryOp::max, $3, $5, driver.env, @$); }
+               { $$ = make_shared<BinaryOp>(codes::BinaryOp::max, $3, $5, @$); }
              | MIN LPAREN expr COMMA expr RPAREN
-               { $$ = make_shared<BinaryOp>(codes::BinaryOp::min, $3, $5, driver.env, @$); }
+               { $$ = make_shared<BinaryOp>(codes::BinaryOp::min, $3, $5, @$); }
              | MOD LPAREN expr COMMA expr RPAREN
-               { $$ = make_shared<BinaryOp>(codes::BinaryOp::mod, $3, $5, driver.env, @$); }
+               { $$ = make_shared<BinaryOp>(codes::BinaryOp::mod, $3, $5, @$); }
              | NORMPDF LPAREN expr COMMA expr COMMA expr RPAREN
-               { $$ = make_shared<TrinaryOp>(codes::TrinaryOp::normpdf, $3, $5, $7, driver.env, @$); }
+               { $$ = make_shared<TrinaryOp>(codes::TrinaryOp::normpdf, $3, $5, $7, @$); }
              | NORMCDF LPAREN expr COMMA expr COMMA expr RPAREN
-               { $$ = make_shared<TrinaryOp>(codes::TrinaryOp::normcdf, $3, $5, $7, driver.env, @$); }
+               { $$ = make_shared<TrinaryOp>(codes::TrinaryOp::normcdf, $3, $5, $7, @$); }
              | DEFINED LPAREN NAME RPAREN
-               { $$ = make_shared<UnaryOp>(codes::UnaryOp::defined, make_shared<String>($3, driver.env, @3), driver.env, @$); }
+               { $$ = make_shared<UnaryOp>(codes::UnaryOp::defined, make_shared<String>($3, @3), @$); }
              ;
 
 oper_expr : primary_expr
           | LPAREN BOOL RPAREN oper_expr %prec CAST
-            { $$ = make_shared<UnaryOp>(codes::UnaryOp::cast_bool, $4, driver.env, @$); }
+            { $$ = make_shared<UnaryOp>(codes::UnaryOp::cast_bool, $4, @$); }
           | LPAREN REAL RPAREN oper_expr %prec CAST
-            { $$ = make_shared<UnaryOp>(codes::UnaryOp::cast_real, $4, driver.env, @$); }
+            { $$ = make_shared<UnaryOp>(codes::UnaryOp::cast_real, $4, @$); }
           | LPAREN STRING RPAREN oper_expr %prec CAST
-            { $$ = make_shared<UnaryOp>(codes::UnaryOp::cast_string, $4, driver.env, @$); }
+            { $$ = make_shared<UnaryOp>(codes::UnaryOp::cast_string, $4, @$); }
           | LPAREN TUPLE RPAREN oper_expr %prec CAST
-            { $$ = make_shared<UnaryOp>(codes::UnaryOp::cast_tuple, $4, driver.env, @$); }
+            { $$ = make_shared<UnaryOp>(codes::UnaryOp::cast_tuple, $4, @$); }
           | LPAREN ARRAY RPAREN oper_expr %prec CAST
-            { $$ = make_shared<UnaryOp>(codes::UnaryOp::cast_array, $4, driver.env, @$); }
+            { $$ = make_shared<UnaryOp>(codes::UnaryOp::cast_array, $4, @$); }
           | NOT oper_expr
-            { $$ = make_shared<UnaryOp>(codes::UnaryOp::logical_not, $2, driver.env, @$); }
+            { $$ = make_shared<UnaryOp>(codes::UnaryOp::logical_not, $2, @$); }
           | MINUS oper_expr %prec UNARY
-            { $$ = make_shared<UnaryOp>(codes::UnaryOp::unary_minus, $2, driver.env, @$); }
+            { $$ = make_shared<UnaryOp>(codes::UnaryOp::unary_minus, $2, @$); }
           | PLUS oper_expr %prec UNARY
-            { $$ = make_shared<UnaryOp>(codes::UnaryOp::unary_plus, $2, driver.env, @$); }
+            { $$ = make_shared<UnaryOp>(codes::UnaryOp::unary_plus, $2, @$); }
           | oper_expr PLUS oper_expr
-            { $$ = make_shared<BinaryOp>(codes::BinaryOp::plus, $1, $3, driver.env, @$); }
+            { $$ = make_shared<BinaryOp>(codes::BinaryOp::plus, $1, $3, @$); }
           | oper_expr MINUS oper_expr
-            { $$ = make_shared<BinaryOp>(codes::BinaryOp::minus, $1, $3, driver.env, @$); }
+            { $$ = make_shared<BinaryOp>(codes::BinaryOp::minus, $1, $3, @$); }
           | oper_expr TIMES oper_expr
-            { $$ = make_shared<BinaryOp>(codes::BinaryOp::times, $1, $3, driver.env, @$); }
+            { $$ = make_shared<BinaryOp>(codes::BinaryOp::times, $1, $3, @$); }
           | oper_expr DIVIDE oper_expr
-            { $$ = make_shared<BinaryOp>(codes::BinaryOp::divide, $1, $3, driver.env, @$); }
+            { $$ = make_shared<BinaryOp>(codes::BinaryOp::divide, $1, $3, @$); }
           | oper_expr POWER oper_expr
-            { $$ = make_shared<BinaryOp>(codes::BinaryOp::power, $1, $3, driver.env, @$); }
+            { $$ = make_shared<BinaryOp>(codes::BinaryOp::power, $1, $3, @$); }
           | oper_expr UNION oper_expr
-            { $$ = make_shared<BinaryOp>(codes::BinaryOp::set_union, $1, $3, driver.env, @$); }
+            { $$ = make_shared<BinaryOp>(codes::BinaryOp::set_union, $1, $3, @$); }
           | oper_expr INTERSECTION oper_expr
-            { $$ = make_shared<BinaryOp>(codes::BinaryOp::set_intersection, $1, $3, driver.env, @$); }
+            { $$ = make_shared<BinaryOp>(codes::BinaryOp::set_intersection, $1, $3, @$); }
           ;
 
 colon_expr : oper_expr COLON oper_expr
-             { $$ = make_shared<Range>($1, $3, driver.env, @$); }
+             { $$ = make_shared<Range>($1, $3, @$); }
            | oper_expr COLON oper_expr COLON oper_expr
-             { $$ = make_shared<Range>($1, $3, $5, driver.env, @$); }
+             { $$ = make_shared<Range>($1, $3, $5, @$); }
            ;
 
 expr : oper_expr
      | colon_expr
      | expr EQUAL_EQUAL expr
-       { $$ = make_shared<BinaryOp>(codes::BinaryOp::equal_equal, $1, $3, driver.env, @$); }
+       { $$ = make_shared<BinaryOp>(codes::BinaryOp::equal_equal, $1, $3, @$); }
      | expr NOT_EQUAL expr
-       { $$ = make_shared<BinaryOp>(codes::BinaryOp::not_equal, $1, $3, driver.env, @$); }
+       { $$ = make_shared<BinaryOp>(codes::BinaryOp::not_equal, $1, $3, @$); }
      | expr LESS expr
-       { $$ = make_shared<BinaryOp>(codes::BinaryOp::less, $1, $3, driver.env, @$); }
+       { $$ = make_shared<BinaryOp>(codes::BinaryOp::less, $1, $3, @$); }
      | expr GREATER expr
-       { $$ = make_shared<BinaryOp>(codes::BinaryOp::greater, $1, $3, driver.env, @$); }
+       { $$ = make_shared<BinaryOp>(codes::BinaryOp::greater, $1, $3, @$); }
      | expr LESS_EQUAL expr
-       { $$ = make_shared<BinaryOp>(codes::BinaryOp::less_equal, $1, $3, driver.env, @$); }
+       { $$ = make_shared<BinaryOp>(codes::BinaryOp::less_equal, $1, $3, @$); }
      | expr GREATER_EQUAL expr
-       { $$ = make_shared<BinaryOp>(codes::BinaryOp::greater_equal, $1, $3, driver.env, @$); }
+       { $$ = make_shared<BinaryOp>(codes::BinaryOp::greater_equal, $1, $3, @$); }
      | expr AND expr
-       { $$ = make_shared<BinaryOp>(codes::BinaryOp::logical_and, $1, $3, driver.env, @$); }
+       { $$ = make_shared<BinaryOp>(codes::BinaryOp::logical_and, $1, $3, @$); }
      | expr OR expr
-       { $$ = make_shared<BinaryOp>(codes::BinaryOp::logical_or, $1, $3, driver.env, @$); }
+       { $$ = make_shared<BinaryOp>(codes::BinaryOp::logical_or, $1, $3, @$); }
      | expr IN expr
-       { $$ = make_shared<BinaryOp>(codes::BinaryOp::in, $1, $3, driver.env, @$); }
+       { $$ = make_shared<BinaryOp>(codes::BinaryOp::in, $1, $3, @$); }
      ;
 
 %%