From 866e4d6d39815f37ed472e694d3aa3835b9e1627 Mon Sep 17 00:00:00 2001
From: Houtan Bastani <houtan@dynare.org>
Date: Mon, 15 Jul 2019 14:59:49 -0400
Subject: [PATCH] macro processor: introduce colon operator with three args
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This introduces a shift/reduce compilation warning in the macro processor because at expr COLON expr . COLON expr the parser doesn’t know whether to shift or reduce
---
 src/macro/Expressions.cc | 41 ++++++++++++++++------------------------
 src/macro/Parser.yy      | 21 ++++++++++++++++----
 2 files changed, 33 insertions(+), 29 deletions(-)

diff --git a/src/macro/Expressions.cc b/src/macro/Expressions.cc
index 731cd4ab..813866ec 100644
--- a/src/macro/Expressions.cc
+++ b/src/macro/Expressions.cc
@@ -531,38 +531,29 @@ Array::eval()
 {
   if (arr.empty() && range1 && range2)
     {
-      DoublePtr range1int = dynamic_pointer_cast<Double>(range1->eval());
-      DoublePtr range2int = dynamic_pointer_cast<Double>(range2->eval());
-      if (!range1int || !range2int)
+      DoublePtr range1dbl = dynamic_pointer_cast<Double>(range1->eval());
+      DoublePtr range2dbl = dynamic_pointer_cast<Double>(range2->eval());
+      if (!range1dbl || !range2dbl)
         throw StackTrace("To create an array from a range using the colon operator, "
-                         "both arguments must be doubles");
+                         "the arguments must evaluate to doubles");
 
+      DoublePtr incdbl = make_shared<Double>(1, env);
       if (increment)
         {
-          auto incrementp = dynamic_pointer_cast<Double>(increment->eval());
-          if (*incrementp == 0)
-            throw StackTrace("the increment cannot be equal to zero");
-
-          if (*range1int <= *range2int)
-            if (*incrementp < 0)
-              throw StackTrace("In this case the increment cannot be negative");
-            else
-              for (int i = *range1int; i <= *range2int; i += *incrementp)
-                arr.emplace_back(make_shared<Double>(i, env));
-          else
-            if (*incrementp > 0)
-              throw StackTrace("In this case the increment cannot be positive");
-            else
-              for (int i = *range1int; i >= *range2int; i += *incrementp)
-                arr.emplace_back(make_shared<Double>(i, env));
+          incdbl = dynamic_pointer_cast<Double>(increment->eval());
+          if (!incdbl)
+            throw StackTrace("To create an array from a range using the colon operator, "
+                             "the increment must evaluate to a double");
         }
-      else
-        for (int i = *range1int; i <= *range2int; i++)
+
+      if (*incdbl > 0 && *range1dbl < *range2dbl)
+        for (double i = *range1dbl; i <= *range2dbl; i += *incdbl)
+          arr.emplace_back(make_shared<Double>(i, env));
+      else if (*range1dbl > *range2dbl && *incdbl < 0)
+        for (double i = *range1dbl; i >= *range2dbl; i += *incdbl)
           arr.emplace_back(make_shared<Double>(i, env));
 
-      range1 = nullptr;
-      increment = nullptr;
-      range2 = nullptr;
+      range1 = increment = range2 = nullptr;
     }
 
   vector<ExpressionPtr> retval;
diff --git a/src/macro/Parser.yy b/src/macro/Parser.yy
index 89011455..1cf97875 100644
--- a/src/macro/Parser.yy
+++ b/src/macro/Parser.yy
@@ -70,7 +70,7 @@ using namespace macro;
 %left EQUAL_EQUAL NOT_EQUAL
 %left LESS GREATER LESS_EQUAL GREATER_EQUAL
 %nonassoc IN
-%nonassoc COLON
+%left COLON
 %left UNION
 %left INTERSECTION
 %left PLUS MINUS
@@ -88,7 +88,7 @@ using namespace macro;
 %type <VariablePtr> symbol
 
 %type <vector<VariablePtr>> comma_name
-%type <vector<ExpressionPtr>> comma_expr function_args tuple_comma_expr
+%type <vector<ExpressionPtr>> comma_expr function_args tuple_comma_expr colon_expr
 
 %%
 
@@ -278,6 +278,12 @@ tuple_comma_expr : %empty
                    { $1.emplace_back($3); $$ = $1; }
                  ;
 
+colon_expr : expr COLON expr
+             { $$ = vector<ExpressionPtr>{$1, $3}; }
+           | colon_expr COLON expr
+             { $1.emplace_back($3); $$ = $1; }
+           ;
+
 expr : LPAREN expr RPAREN
        { $$ = $2; }
      | symbol
@@ -292,8 +298,15 @@ expr : LPAREN expr RPAREN
        { $$ = make_shared<Double>($1, driver.env, @$); }
      | QUOTED_STRING
        { $$ = make_shared<String>($1, driver.env, @$); }
-     | expr COLON expr
-       { $$ = make_shared<Array>($1, $3, driver.env, @$); }
+     | colon_expr
+       {
+         if ($1.size() == 2)
+           $$ = make_shared<Array>($1[0], $1[1], driver.env, @$);
+         else if ($1.size() == 3)
+           $$ = make_shared<Array>($1[0], $1[1], $1[2], driver.env, @$);
+         else
+           error(@$, "The colon operator only works with 2 or 3 arguments");
+       }
      | LBRACKET comma_expr RBRACKET
        { $$ = make_shared<Array>($2, driver.env, @$); }
      | symbol LBRACKET comma_expr RBRACKET
-- 
GitLab