From d35f2609fe57398d00101000907d2cdfc5cca6e6 Mon Sep 17 00:00:00 2001
From: Houtan Bastani <houtan@dynare.org>
Date: Wed, 7 Aug 2019 11:47:50 -0400
Subject: [PATCH] macro processor: implement `isempty`

---
 doc/macroprocessor/macroprocessor.tex    | 5 +++--
 src/macro/Expressions.cc                 | 7 +++++++
 src/macro/Expressions.hh                 | 4 ++++
 src/macro/ForwardDeclarationsAndEnums.hh | 1 +
 src/macro/Parser.yy                      | 4 ++++
 src/macro/Tokenizer.ll                   | 1 +
 6 files changed, 20 insertions(+), 2 deletions(-)

diff --git a/doc/macroprocessor/macroprocessor.tex b/doc/macroprocessor/macroprocessor.tex
index 8f7c4d19..a3b56db7 100644
--- a/doc/macroprocessor/macroprocessor.tex
+++ b/doc/macroprocessor/macroprocessor.tex
@@ -215,6 +215,7 @@
     \item comparison operators: \texttt{< > <= >= == !=}
     \item concatenation: \texttt{+}
     \item string length: \texttt{length()}
+    \item string emptiness: \texttt{isempty()}
     \item extraction of substrings: if \texttt{s} is a string, then one can write \texttt{s[3]} or \texttt{s[4:6]}
     \end{itemize}
   \end{block}
@@ -227,7 +228,7 @@
   \begin{block}{Operators on tuples}
     \begin{itemize}
     \item comparison operators: \texttt{== !=}
-    \item functions: \texttt{length, empty}
+    \item functions: \texttt{length, isempty}
     \item testing membership in tuple: \texttt{in} operator \\ (example:
       \texttt{"b" in ("a", "b", "c")} returns \texttt{1})
     \end{itemize}
@@ -243,7 +244,7 @@
     \item comparison operators: \texttt{== !=}
     \item dereferencing: if \texttt{v} is an array, then \texttt{v[2]} is its $2^{\textrm{nd}}$ element
     \item concatenation: \texttt{+}
-    \item functions: \texttt{sum, length, empty}
+    \item functions: \texttt{sum, length, isempty}
     \item difference \texttt{-}: returns the first operand from which the elements of the second operand have been removed
     \item Cartesian product of two arrays: \texttt{*}
     \item Cartesian product of one array \texttt{N} times: \texttt{\^{}N}
diff --git a/src/macro/Expressions.cc b/src/macro/Expressions.cc
index ed1c6abd..23784ab7 100644
--- a/src/macro/Expressions.cc
+++ b/src/macro/Expressions.cc
@@ -803,6 +803,8 @@ UnaryOp::eval()
           return argbt->unary_plus();
         case codes::UnaryOp::length:
           return argbt->length();
+        case codes::UnaryOp::isempty:
+          return argbt->isempty();
         case codes::UnaryOp::exp:
           return argbt->exp();
         case codes::UnaryOp::ln:
@@ -1133,6 +1135,8 @@ UnaryOp::to_string() const noexcept
       return "+" + retval;
     case codes::UnaryOp::length:
       return "length(" + retval + ")";
+    case codes::UnaryOp::isempty:
+      return "isempty(" + retval + ")";
     case codes::UnaryOp::exp:
       return "exp(" + retval + ")";
     case codes::UnaryOp::ln:
@@ -1352,6 +1356,9 @@ UnaryOp::print(ostream &output, bool matlab_output) const noexcept
     case codes::UnaryOp::length:
       output << "length(";
       break;
+    case codes::UnaryOp::isempty:
+      output << "isempty(";
+      break;
     case codes::UnaryOp::exp:
       output << "exp(";
       break;
diff --git a/src/macro/Expressions.hh b/src/macro/Expressions.hh
index 099f6920..9153e112 100644
--- a/src/macro/Expressions.hh
+++ b/src/macro/Expressions.hh
@@ -148,6 +148,7 @@ namespace macro
     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 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"); }
@@ -337,6 +338,7 @@ namespace macro
     BoolPtr is_greater_equal(const BaseTypePtr &btp) const override;
     BoolPtr is_equal(const BaseTypePtr &btp) const override;
     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); }
@@ -373,6 +375,7 @@ namespace macro
     BoolPtr is_equal(const BaseTypePtr &btp) const override;
     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); }
@@ -418,6 +421,7 @@ namespace macro
     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); }
     RealPtr sum() const override;
     BoolPtr cast_bool() const override;
     RealPtr cast_real() const override;
diff --git a/src/macro/ForwardDeclarationsAndEnums.hh b/src/macro/ForwardDeclarationsAndEnums.hh
index a606482c..35112c6b 100644
--- a/src/macro/ForwardDeclarationsAndEnums.hh
+++ b/src/macro/ForwardDeclarationsAndEnums.hh
@@ -76,6 +76,7 @@ namespace macro
        unary_minus,
        unary_plus,
        length,
+       isempty,
        exp,
        ln,
        log10,
diff --git a/src/macro/Parser.yy b/src/macro/Parser.yy
index cb51900f..98e2b38f 100644
--- a/src/macro/Parser.yy
+++ b/src/macro/Parser.yy
@@ -65,6 +65,8 @@ using namespace macro;
 %token SQRT CBRT SIGN MAX MIN FLOOR CEIL TRUNC SUM MOD
 %token ERF ERFC GAMMA LGAMMA ROUND NORMPDF NORMCDF LENGTH
 
+%token ISEMPTY
+
 %token BOOL REAL STRING TUPLE ARRAY
 
 %left OR
@@ -340,6 +342,8 @@ expr : LPAREN expr RPAREN
        { $$ = make_shared<UnaryOp>(codes::UnaryOp::unary_plus, $2, driver.env, @$); }
      | LENGTH LPAREN expr RPAREN
        { $$ = make_shared<UnaryOp>(codes::UnaryOp::length, $3, driver.env, @$); }
+     | ISEMPTY LPAREN expr RPAREN
+       { $$ = make_shared<UnaryOp>(codes::UnaryOp::isempty, $3, driver.env, @$); }
      | EXP LPAREN expr RPAREN
        { $$ = make_shared<UnaryOp>(codes::UnaryOp::exp, $3, driver.env, @$); }
      | LOG LPAREN expr RPAREN
diff --git a/src/macro/Tokenizer.ll b/src/macro/Tokenizer.ll
index 756e8d9b..c661d4a1 100644
--- a/src/macro/Tokenizer.ll
+++ b/src/macro/Tokenizer.ll
@@ -141,6 +141,7 @@ CONT \\\\{SPC}*
 <expr,eval>length          { return token::LENGTH; }
 <expr,eval>normpdf         { return token::NORMPDF; }
 <expr,eval>normcdf         { return token::NORMCDF; }
+<expr,eval>isempty         { return token::ISEMPTY; }
 
 <expr,eval>bool            { return token::BOOL; }
 <expr,eval>real            { return token::REAL; }
-- 
GitLab