diff --git a/src/macro/MacroBison.yy b/src/macro/MacroBison.yy
index 868379b84e954b03fc34cf33bcbf6680ad742436..14f62226fb56c84f3f92eea908e0eb51263a4a5a 100644
--- a/src/macro/MacroBison.yy
+++ b/src/macro/MacroBison.yy
@@ -82,7 +82,7 @@ class MacroDriver;
 %precedence UMINUS UPLUS EXCLAMATION
 %precedence LBRACKET
 
-%type <vector<string>> func_args
+%type <vector<string>> comma_name
 %type <MacroValuePtr> expr
 %type <vector<MacroValuePtr>> comma_expr tuple_comma_expr
 %%
@@ -103,6 +103,8 @@ statement : expr
             { driver.set_variable($2, $4); }
           | FOR NAME IN expr
             { TYPERR_CATCH(driver.init_loop($2, $4), @$); }
+          | FOR LPAREN comma_name RPAREN IN expr
+            { TYPERR_CATCH(driver.init_loop($3, $6), @$); }
           | IF expr
             { TYPERR_CATCH(driver.begin_if($2), @$); }
           | IFDEF NAME
@@ -119,18 +121,18 @@ statement : expr
             { driver.printvars(@$, true); }
           | ECHOMACROVARS LPAREN SAVE RPAREN
             { out << driver.printvars(@$, false); }
-          | DEFINE NAME LPAREN func_args { driver.push_args_into_func_env($4); } RPAREN EQUAL expr
+          | DEFINE NAME LPAREN comma_name { driver.push_args_into_func_env($4); } RPAREN EQUAL expr
             {
               TYPERR_CATCH(driver.set_string_function($2, $4, $8), @$);
               driver.pop_func_env();
             }
           ;
 
-func_args : NAME
-            { $$ = vector<string>{$1}; }
-          | func_args COMMA NAME
-            { $1.push_back($3); $$ = $1; }
-          ;
+comma_name : NAME
+             { $$ = vector<string>{$1}; }
+           | comma_name COMMA NAME
+             { $1.push_back($3); $$ = $1; }
+           ;
 
 expr : INTEGER
        { $$ = make_shared<IntMV>($1); }
diff --git a/src/macro/MacroDriver.cc b/src/macro/MacroDriver.cc
index 61f412c9431f818f3c5f1290da7ce13acf2e4667..0798ba5d21a341cf03be6c97ca59e042cec6cd03 100644
--- a/src/macro/MacroDriver.cc
+++ b/src/macro/MacroDriver.cc
@@ -205,18 +205,28 @@ MacroDriver::init_loop(const string &name, MacroValuePtr value) noexcept(false)
   auto mv = dynamic_pointer_cast<ArrayMV>(value);
   if (!mv)
     throw MacroValue::TypeError("Argument of @#for loop must be an array expression");
-  loop_stack.emplace(name, move(mv), 0);
+  loop_stack.emplace(vector<string> {name}, move(mv), 0);
 }
 
+void
+MacroDriver::init_loop(const vector<string> &names, MacroValuePtr value) noexcept(false)
+{
+  auto mv = dynamic_pointer_cast<ArrayMV>(value);
+  if (!mv)
+    throw MacroValue::TypeError("Argument of @#for loop must be an array expression");
+  loop_stack.emplace(names, move(mv), 0);
+}
+
+
 bool
-MacroDriver::iter_loop()
+MacroDriver::iter_loop()  noexcept(false)
 {
   if (loop_stack.empty())
     throw "No loop on which to iterate!";
 
   int &i = get<2>(loop_stack.top());
   auto mv = get<1>(loop_stack.top());
-  string &name = get<0>(loop_stack.top());
+  vector<string> &names = get<0>(loop_stack.top());
 
   if (i >= static_cast<int>(mv->values.size()))
     {
@@ -225,7 +235,27 @@ MacroDriver::iter_loop()
     }
   else
     {
-      env[name] = mv->values[i++];
+      if (names.size() == 1)
+        env[names.at(0)] = mv->values[i++];
+      else
+        {
+          auto tmv = dynamic_pointer_cast<TupleMV>(mv->values[i++]);
+          if (!tmv)
+             throw MacroValue::TypeError("Argument of @#for loop must be an array expression of tuples");
+          if (tmv->values.size() != names.size())
+            {
+              cerr << "Error in for loop: tuple in array contains " << tmv->length()
+                   << " elements while you are assigning to " << names.size() << " variables."
+                   << endl;
+              exit(EXIT_FAILURE);
+            }
+
+          for (auto &name: names)
+            {
+              auto idx = &name - &names[0];
+              env[name] = tmv->values.at(idx);
+            }
+        }
       return true;
     }
 }
diff --git a/src/macro/MacroDriver.hh b/src/macro/MacroDriver.hh
index db05b06ee451925968dd4c4745f7984c4e518b0c..17d3411bae44618b37495953e01dae16f388dfb5 100644
--- a/src/macro/MacroDriver.hh
+++ b/src/macro/MacroDriver.hh
@@ -165,8 +165,10 @@ private:
   vector<env_t> func_env;
 
   //! Stack used to keep track of (possibly nested) loops
-  //! First element is loop variable name, second is the array over which iteration is done, and third is subscript to be used by next call of iter_loop() (beginning with 0) */
-  stack<tuple<string, shared_ptr<ArrayMV>, int>> loop_stack;
+  //! First element is loop variable name
+  //! Second is the array over which iteration is done
+  //! Third is subscript to be used by next call of iter_loop() (beginning with 0) */
+  stack<tuple<vector<string>, shared_ptr<ArrayMV>, int>> loop_stack;
 public:
   //! Exception thrown when value of an unknown variable is requested
   class UnknownVariable
@@ -229,10 +231,13 @@ public:
   //! Initiate a for loop
   /*! Does not set name = value[1]. You must call iter_loop() for that. */
   void init_loop(const string &name, MacroValuePtr value) noexcept(false);
+  /*! Same as above but for looping over tuple array */
+  void init_loop(const vector<string> &names, MacroValuePtr value) noexcept(false);
 
   //! Iterate innermost loop
-  /*! Returns false if iteration is no more possible (end of loop); in that case it destroys the pointer given to init_loop() */
-  bool iter_loop();
+  /*! Returns false if iteration is no more possible (end of loop);
+      in that case it destroys the pointer given to init_loop() */
+  bool iter_loop() noexcept(false);
 
   //! Begins an @#if statement
   void begin_if(const MacroValuePtr &value) noexcept(false);