You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by ph...@apache.org on 2018/01/22 14:56:27 UTC

nifi-minifi-cpp git commit: MINIFICPP-376 Fixed failure to apply chained function calls

Repository: nifi-minifi-cpp
Updated Branches:
  refs/heads/master 233d1d44c -> f08dc770e


MINIFICPP-376 Fixed failure to apply chained function calls

This closes #245.

Signed-off-by: Marc Parisi <ph...@apache.org>


Project: http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/repo
Commit: http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/commit/f08dc770
Tree: http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/tree/f08dc770
Diff: http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/diff/f08dc770

Branch: refs/heads/master
Commit: f08dc770e4a6b5c40749fed597a2cc1f5ca4746b
Parents: 233d1d4
Author: Andrew I. Christianson <an...@andyic.org>
Authored: Fri Jan 19 15:01:53 2018 -0500
Committer: Marc Parisi <ph...@apache.org>
Committed: Mon Jan 22 09:55:49 2018 -0500

----------------------------------------------------------------------
 extensions/expression-language/Expression.cpp   | 64 +++++++++++---------
 extensions/expression-language/Parser.yy        | 25 +++-----
 .../impl/expression/Expression.h                | 23 +++----
 .../ExpressionLanguageTests.cpp                 | 35 ++++++++---
 4 files changed, 76 insertions(+), 71 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/f08dc770/extensions/expression-language/Expression.cpp
----------------------------------------------------------------------
diff --git a/extensions/expression-language/Expression.cpp b/extensions/expression-language/Expression.cpp
index 5dc5405..2fdc541 100644
--- a/extensions/expression-language/Expression.cpp
+++ b/extensions/expression-language/Expression.cpp
@@ -18,7 +18,6 @@
 #include <utility>
 #include <iostream>
 #include <iomanip>
-#include <string>
 #include <random>
 
 #include <expression/Expression.h>
@@ -271,37 +270,34 @@ template<std::string T(const std::vector<std::string> &)>
 Expression make_dynamic_function_incomplete(const std::string &function_name,
                                             const std::vector<Expression> &args,
                                             std::size_t num_args) {
-  if (args.size() >= num_args) {
-    auto result = make_dynamic([=](const Parameters &params) -> std::string {
-      std::vector<std::string> evaluated_args;
 
-      for (const auto &arg : args) {
-        evaluated_args.emplace_back(arg(params));
-      }
+  if (args.size() < num_args) {
+    std::stringstream message_ss;
+    message_ss << "Expression language function "
+               << function_name
+               << " called with "
+               << args.size()
+               << " argument(s), but "
+               << num_args
+               << " are required";
+    throw std::runtime_error(message_ss.str());
+  }
 
-      return T(evaluated_args);
-    });
+  auto result = make_dynamic([=](const Parameters &params) -> std::string {
+    std::vector<std::string> evaluated_args;
 
-    result.complete = [function_name, args](Expression expr) -> Expression {
-      std::vector<Expression> complete_args = {expr};
-      complete_args.insert(complete_args.end(), args.begin(), args.end());
-      return make_dynamic_function(function_name, complete_args);
-    };
+    for (const auto &arg : args) {
+      evaluated_args.emplace_back(arg(params));
+    }
 
-    return result;
-  } else {
-    auto result = make_dynamic([](const Parameters &params) -> std::string {
-      throw std::runtime_error("Attempted to call incomplete function");
-    });
+    return T(evaluated_args);
+  });
 
-    result.complete = [function_name, args](Expression expr) -> Expression {
-      std::vector<Expression> complete_args = {expr};
-      complete_args.insert(complete_args.end(), args.begin(), args.end());
-      return make_dynamic_function(function_name, complete_args);
-    };
+  return result;
+}
 
-    return result;
-  }
+std::string expr_literal(const std::vector<std::string> &args) {
+  return args[0];
 }
 
 Expression make_dynamic_function(const std::string &function_name,
@@ -348,6 +344,8 @@ Expression make_dynamic_function(const std::string &function_name,
     return make_dynamic_function_incomplete<expr_toRadix>(function_name, args, 1);
   } else if (function_name == "random") {
     return make_dynamic_function_incomplete<expr_random>(function_name, args, 0);
+  } else if (function_name == "literal") {
+    return make_dynamic_function_incomplete<expr_literal>(function_name, args, 1);
   } else {
     std::string msg("Unknown expression function: ");
     msg.append(function_name);
@@ -355,8 +353,18 @@ Expression make_dynamic_function(const std::string &function_name,
   }
 }
 
-Expression make_dynamic_function_postfix(const Expression &subject, const Expression &fn) {
-  return fn.complete(subject);
+Expression make_function_composition(const Expression &arg,
+                                     const std::vector<std::pair<std::string, std::vector<Expression>>> &chain) {
+
+  auto expr = arg;
+
+  for (const auto &chain_part : chain) {
+    std::vector<Expression> complete_args = {expr};
+    complete_args.insert(complete_args.end(), chain_part.second.begin(), chain_part.second.end());
+    expr = make_dynamic_function(chain_part.first, complete_args);
+  }
+
+  return expr;
 }
 
 bool Expression::isDynamic() const {

http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/f08dc770/extensions/expression-language/Parser.yy
----------------------------------------------------------------------
diff --git a/extensions/expression-language/Parser.yy b/extensions/expression-language/Parser.yy
index 7ac52e0..2e053f9 100644
--- a/extensions/expression-language/Parser.yy
+++ b/extensions/expression-language/Parser.yy
@@ -106,6 +106,8 @@
 %type <std::string>             quoted_text_content
 %type <Expression>              text_or_exp
 
+%type <std::vector<std::pair<std::string, std::vector<Expression>>>> fn_calls
+
 %%
 
 %start root;
@@ -171,22 +173,6 @@ exp_whitespaces: %empty {}
 attr_id: quoted_text exp_whitespaces { std::swap($$, $1); }
        | IDENTIFIER exp_whitespaces { std::swap($$, $1); }
        ;
-/*
-number_decimal: %empty { $$ = ""; }
-              | PERIOD NUMBER { $$ = "." + std::to_string($2); }
-              ;
-
-number_sign: %empty { $$ = ""; }
-           | MINUS { $$ = "-"; }
-           ;
-
-number_exponent: %empty { $$ = ""; }
-               | BIGE number_sign NUMBER { $$ = "E" + $2 + std::to_string($3); }
-               | LITTLEE number_sign NUMBER { $$ = "e" + $2 + std::to_string($3); }
-               ;
-
-number: number_sign NUMBER number_decimal number_exponent { $$ = $1 + std::to_string($2) + $3 + $4; }
-      ;*/
 
 fn_arg: quoted_text exp_whitespaces { $$ = make_static($1); }
       | NUMBER exp_whitespaces { $$ = make_static($1); }
@@ -199,12 +185,15 @@ fn_args: %empty {}
 
 fn_call: attr_id LPAREN exp_whitespaces fn_args RPAREN exp_whitespaces { $$ = make_dynamic_function(std::move($1), $4); }
 
+fn_calls: %empty {}
+        | COLON exp_whitespaces attr_id LPAREN exp_whitespaces fn_args RPAREN exp_whitespaces fn_calls { $$.push_back({$3, $6}); $$.insert($$.end(), $9.begin(), $9.end()); }
+        ;
+
 exp_content_val: attr_id { $$ = make_dynamic_attr(std::move($1)); }
                | fn_call { $$ = $1; }
                ;
 
-exp_content: exp_content_val { $$ = $1; }
-           | exp_content_val COLON exp_whitespaces fn_call { $$ = make_dynamic_function_postfix($1, $4); }
+exp_content: exp_content_val fn_calls { $$ = make_function_composition($1, $2); }
            ;
 
 exp: DOLLAR LCURLY exp_whitespaces exp_content RCURLY { $$ = $4; }

http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/f08dc770/extensions/expression-language/impl/expression/Expression.h
----------------------------------------------------------------------
diff --git a/extensions/expression-language/impl/expression/Expression.h b/extensions/expression-language/impl/expression/Expression.h
index e245370..9ee4a54 100644
--- a/extensions/expression-language/impl/expression/Expression.h
+++ b/extensions/expression-language/impl/expression/Expression.h
@@ -44,8 +44,7 @@ typedef struct {
 static const std::function<std::string(const Parameters &params)> NOOP_FN;
 
 /**
- * A NiFi expression langauge expression which can be composed with other
- * expressions (via the + operator.
+ * A NiFi expression language expression.
  */
 class Expression {
  public:
@@ -67,7 +66,7 @@ class Expression {
   bool isDynamic() const;
 
   /**
-   * Combine this expression with another expression. Intermediate results
+   * Combine this expression with another expression by concatenation. Intermediate results
    * are computed when non-dynamic expressions are composed.
    *
    * @param other_expr
@@ -83,14 +82,6 @@ class Expression {
    */
   std::string operator()(const Parameters &params) const;
 
-  /**
-   * If this expression is incomplete (i.e. a function with incomplete arguments), then this
-   * will return a completed expression based on the provided expression.
-   */
-  std::function<Expression(Expression)> complete = [](Expression expr) -> Expression {
-    throw std::runtime_error("Attempted to complete already complete expression.");
-  };
-
  protected:
   std::string val_;
   std::function<std::string(const Parameters &params)> val_fn_;
@@ -140,14 +131,14 @@ Expression make_dynamic_attr(const std::string &attribute_id);
 Expression make_dynamic_function(const std::string &function_name, const std::vector<Expression> &args);
 
 /**
- * Creates a dynamic expression which feeds the subject as the first argument into
- * the provided function expression. This enables expressions like attr:toLower()
+ * Creates a chained function composition.
  *
- * @param subject
- * @param fn
+ * @param arg
+ * @param chain
  * @return
  */
-Expression make_dynamic_function_postfix(const Expression &subject, const Expression &fn);
+Expression make_function_composition(const Expression &arg,
+                                     const std::vector<std::pair<std::string, std::vector<Expression>>> &chain);
 
 } /* namespace expression */
 } /* namespace minifi */

http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/f08dc770/libminifi/test/expression-language-tests/ExpressionLanguageTests.cpp
----------------------------------------------------------------------
diff --git a/libminifi/test/expression-language-tests/ExpressionLanguageTests.cpp b/libminifi/test/expression-language-tests/ExpressionLanguageTests.cpp
index 5dd51f1..27d3304 100644
--- a/libminifi/test/expression-language-tests/ExpressionLanguageTests.cpp
+++ b/libminifi/test/expression-language-tests/ExpressionLanguageTests.cpp
@@ -292,17 +292,13 @@ TEST_CASE("Substring After Last", "[expressionLanguageSubstringAfterLast]") {  /
 }
 
 TEST_CASE("Substring Before No Args", "[expressionLanguageSubstringBeforeNoArgs]") {  // NOLINT
-  auto expr = expression::compile("${attr:substringBefore()}");
-  auto flow_file_a = std::make_shared<MockFlowFile>();
-  flow_file_a->addAttribute("attr", "__flow_a_attr_value_a__");
-  REQUIRE_THROWS_WITH(expr({flow_file_a}), "Attempted to call incomplete function");
+  REQUIRE_THROWS_WITH(expression::compile("${attr:substringBefore()}"),
+                      "Expression language function substringBefore called with 1 argument(s), but 2 are required");
 }
 
 TEST_CASE("Substring After No Args", "[expressionLanguageSubstringAfterNoArgs]") {  // NOLINT
-  auto expr = expression::compile("${attr:substringAfter()}");
-  auto flow_file_a = std::make_shared<MockFlowFile>();
-  flow_file_a->addAttribute("attr", "__flow_a_attr_value_a__");
-  REQUIRE_THROWS_WITH(expr({flow_file_a}), "Attempted to call incomplete function");
+  REQUIRE_THROWS_WITH(expression::compile("${attr:substringAfter()}"); ,
+                      "Expression language function substringAfter called with 1 argument(s), but 2 are required");
 }
 
 #ifdef EXPRESSION_LANGUAGE_USE_REGEX
@@ -541,4 +537,25 @@ TEST_CASE("Random", "[expressionLanguageRandom]") {  // NOLINT
   REQUIRE(result > 0);
 }
 
-// ${literal(64):toDouble():math("cbrt"):toNumber():math("max", 5)}
+TEST_CASE("Chained call", "[expressionChainedCall]") {  // NOLINT
+  auto expr = expression::compile("${attr:multiply(3):plus(1)}");
+
+  auto flow_file_a = std::make_shared<MockFlowFile>();
+  flow_file_a->addAttribute("attr", "7");
+  REQUIRE("22" == expr({flow_file_a}));
+}
+
+TEST_CASE("Chained call 2", "[expressionChainedCall2]") {  // NOLINT
+  auto expr = expression::compile("${literal(10):multiply(2):plus(1):multiply(2)}");
+
+  auto flow_file_a = std::make_shared<MockFlowFile>();
+  REQUIRE("42" == expr({flow_file_a}));
+}
+
+TEST_CASE("Chained call 3", "[expressionChainedCall3]") {  // NOLINT
+  auto expr = expression::compile("${literal(10):multiply(2):plus(${attr:multiply(2)}):multiply(${attr})}");
+
+  auto flow_file_a = std::make_shared<MockFlowFile>();
+  flow_file_a->addAttribute("attr", "7");
+  REQUIRE("238" == expr({flow_file_a}));
+}