From d1620d3bf948d9f14814689a206ea4ed3efa4412 Mon Sep 17 00:00:00 2001 From: "Andrew I. Christianson" Date: Fri, 9 Mar 2018 12:56:37 -0500 Subject: [PATCH] MINIFICPP-422 Refactored type system in EL to preserve type information between operations --- CMakeLists.txt | 1 + extensions/expression-language/CMakeLists.txt | 1 + extensions/expression-language/Driver.h | 2 +- extensions/expression-language/Expression.cpp | 323 ++++++++---------- .../ProcessContextExpr.cpp | 4 +- extensions/expression-language/common/Value.h | 238 +++++++++++++ .../impl/expression/Expression.h | 23 +- .../noop/expression/Expression.h | 1 + .../ExpressionLanguageTests.cpp | 154 ++++----- 9 files changed, 486 insertions(+), 261 deletions(-) create mode 100644 extensions/expression-language/common/Value.h diff --git a/CMakeLists.txt b/CMakeLists.txt index d4245d5611..166fd1682b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -110,6 +110,7 @@ include_directories(thirdparty/rapidjson-1.1.0/include) ## Expression language extensions option(DISABLE_EXPRESSION_LANGUAGE "Disables the scripting extensions." OFF) +include_directories("extensions/expression-language/common") if (DISABLE_EXPRESSION_LANGUAGE) # Build expression language NoOp implementation, if necessary include_directories("extensions/expression-language/noop") diff --git a/extensions/expression-language/CMakeLists.txt b/extensions/expression-language/CMakeLists.txt index 53c87e6d8a..b242d9a185 100644 --- a/extensions/expression-language/CMakeLists.txt +++ b/extensions/expression-language/CMakeLists.txt @@ -39,6 +39,7 @@ flex_target( add_flex_bison_dependency(el-scanner el-parser) include_directories(../../libminifi/include ../../libminifi/include/core ../../thirdparty/spdlog-20170710/include ../../thirdparty/concurrentqueue ../../thirdparty/yaml-cpp-yaml-cpp-0.5.3/include ${CIVET_THIRDPARTY_ROOT}/include ../../thirdparty/) +include_directories(common) include_directories(impl) file(GLOB SOURCES "*.cpp") diff --git a/extensions/expression-language/Driver.h b/extensions/expression-language/Driver.h index 4e66b4c2d9..71dbbe41b1 100644 --- a/extensions/expression-language/Driver.h +++ b/extensions/expression-language/Driver.h @@ -41,7 +41,7 @@ class Driver : public yyFlexLexer { public: explicit Driver(std::istream *input = nullptr, std::ostream *output = nullptr) : yyFlexLexer(input, output), - result("") { + result(Value()) { } ~Driver() override = default; int lex(Parser::semantic_type *yylval, diff --git a/extensions/expression-language/Expression.cpp b/extensions/expression-language/Expression.cpp index d0c060ac82..4824f2d404 100644 --- a/extensions/expression-language/Expression.cpp +++ b/extensions/expression-language/Expression.cpp @@ -39,138 +39,143 @@ Expression compile(const std::string &expr_str) { } Expression make_static(std::string val) { - return Expression(std::move(val)); + return Expression(Value(val)); } -Expression make_dynamic(std::function val_fn) { - return Expression("", std::move(val_fn)); +Expression make_dynamic(const std::function &val_fn) { + return Expression(Value(), std::move(val_fn)); } Expression make_dynamic_attr(const std::string &attribute_id) { - return make_dynamic([attribute_id](const Parameters ¶ms) -> std::string { + return make_dynamic([attribute_id](const Parameters ¶ms) -> Value { std::string result; - params.flow_file.lock()->getAttribute(attribute_id, result); - return result; + if (params.flow_file.lock()->getAttribute(attribute_id, result)) { + return Value(result); + } else { + return Value(); + } }); } -std::string expr_hostname(const std::vector &args) { +Value expr_hostname(const std::vector &args) { char hostname[1024]; hostname[1023] = '\0'; gethostname(hostname, 1023); - return std::string(hostname); + return Value(std::string(hostname)); } -std::string expr_toUpper(const std::vector &args) { - std::string result = args[0]; +Value expr_toUpper(const std::vector &args) { + std::string result = args[0].asString(); std::transform(result.begin(), result.end(), result.begin(), ::toupper); - return result; + return Value(result); } -std::string expr_toLower(const std::vector &args) { - std::string result = args[0]; +Value expr_toLower(const std::vector &args) { + std::string result = args[0].asString(); std::transform(result.begin(), result.end(), result.begin(), ::tolower); - return result; + return Value(result); } -std::string expr_substring(const std::vector &args) { +Value expr_substring(const std::vector &args) { if (args.size() < 3) { - return args[0].substr(std::stoul(args[1])); + return Value(args[0].asString().substr(args[1].asUnsignedLong())); } else { - return args[0].substr(std::stoul(args[1]), std::stoul(args[2])); + return Value(args[0].asString().substr(args[1].asUnsignedLong(), args[2].asUnsignedLong())); } } -std::string expr_substringBefore(const std::vector &args) { - return args[0].substr(0, args[0].find(args[1])); +Value expr_substringBefore(const std::vector &args) { + const std::string &arg_0 = args[0].asString(); + return Value(arg_0.substr(0, arg_0.find(args[1].asString()))); } -std::string expr_substringBeforeLast(const std::vector &args) { +Value expr_substringBeforeLast(const std::vector &args) { size_t last_pos = 0; - while (args[0].find(args[1], last_pos + 1) != std::string::npos) { - last_pos = args[0].find(args[1], last_pos + 1); + const std::string &arg_0 = args[0].asString(); + const std::string &arg_1 = args[1].asString(); + while (arg_0.find(arg_1, last_pos + 1) != std::string::npos) { + last_pos = arg_0.find(arg_1, last_pos + 1); } - return args[0].substr(0, last_pos); + return Value(arg_0.substr(0, last_pos)); } -std::string expr_substringAfter(const std::vector &args) { - return args[0].substr(args[0].find(args[1]) + args[1].length()); +Value expr_substringAfter(const std::vector &args) { + const std::string &arg_0 = args[0].asString(); + const std::string &arg_1 = args[1].asString(); + return Value(arg_0.substr(arg_0.find(arg_1) + arg_1.length())); } -std::string expr_substringAfterLast(const std::vector &args) { +Value expr_substringAfterLast(const std::vector &args) { size_t last_pos = 0; - while (args[0].find(args[1], last_pos + 1) != std::string::npos) { - last_pos = args[0].find(args[1], last_pos + 1); + const std::string &arg_0 = args[0].asString(); + const std::string &arg_1 = args[1].asString(); + while (arg_0.find(arg_1, last_pos + 1) != std::string::npos) { + last_pos = arg_0.find(arg_1, last_pos + 1); } - return args[0].substr(last_pos + args[1].length()); + return Value(arg_0.substr(last_pos + arg_1.length())); } -std::string expr_startsWith(const std::vector &args) { - if (args[0].substr(0, args[1].length()) == args[1]) { - return "true"; - } else { - return "false"; - } +Value expr_startsWith(const std::vector &args) { + const std::string &arg_0 = args[0].asString(); + const std::string &arg_1 = args[1].asString(); + return Value(arg_0.substr(0, arg_1.length()) == arg_1); } -std::string expr_endsWith(const std::vector &args) { - if (args[0].substr(args[0].length() - args[1].length()) == args[1]) { - return "true"; - } else { - return "false"; - } +Value expr_endsWith(const std::vector &args) { + const std::string &arg_0 = args[0].asString(); + const std::string &arg_1 = args[1].asString(); + return Value(arg_0.substr(arg_0.length() - arg_1.length()) == arg_1); } -std::string expr_contains(const std::vector &args) { - if (std::string::npos != args[0].find(args[1])) { - return "true"; - } else { - return "false"; - } +Value expr_contains(const std::vector &args) { + return Value(std::string::npos != args[0].asString().find(args[1].asString())); } -std::string expr_in(const std::vector &args) { +Value expr_in(const std::vector &args) { + const std::string &arg_0 = args[0].asString(); for (int i = 1; i < args.size(); i++) { - if (args[0] == args[i]) { - return "true"; + if (arg_0 == args[i].asString()) { + return Value(true); } } - return "false"; + return Value(false); } -std::string expr_indexOf(const std::vector &args) { - auto pos = args[0].find(args[1]); +Value expr_indexOf(const std::vector &args) { + auto pos = args[0].asString().find(args[1].asString()); if (pos == std::string::npos) { - return "-1"; + return Value(static_cast(-1)); } else { - return std::to_string(pos); + return Value(static_cast(pos)); } } -std::string expr_lastIndexOf(const std::vector &args) { +Value expr_lastIndexOf(const std::vector &args) { size_t pos = std::string::npos; - auto cur_pos = args[0].find(args[1], 0); + const std::string &arg_0 = args[0].asString(); + const std::string &arg_1 = args[1].asString(); + auto cur_pos = arg_0.find(arg_1, 0); while (cur_pos != std::string::npos) { pos = cur_pos; - cur_pos = args[0].find(args[1], pos + 1); + cur_pos = arg_0.find(arg_1, pos + 1); } if (pos == std::string::npos) { - return "-1"; + return Value(static_cast(-1)); } else { - return std::to_string(pos); + return Value(static_cast(pos)); } } #ifdef EXPRESSION_LANGUAGE_USE_REGEX -std::string expr_replace(const std::vector &args) { - std::string result = args[0]; - const std::string find = args[1]; - const std::string replace = args[2]; +Value expr_replace(const std::vector &args) { + std::string result = args[0].asString(); + const std::string &find = args[1].asString(); + const std::string &replace = args[2].asString(); std::string::size_type match_pos = 0; match_pos = result.find(find, match_pos); @@ -180,126 +185,102 @@ std::string expr_replace(const std::vector &args) { match_pos = result.find(find, match_pos + replace.size()); } - return result; + return Value(result); } -std::string expr_replaceFirst(const std::vector &args) { - std::string result = args[0]; - const std::regex find(args[1]); - const std::string replace = args[2]; - return std::regex_replace(result, find, replace, std::regex_constants::format_first_only); +Value expr_replaceFirst(const std::vector &args) { + std::string result = args[0].asString(); + const std::regex find(args[1].asString()); + const std::string &replace = args[2].asString(); + return Value(std::regex_replace(result, find, replace, std::regex_constants::format_first_only)); } -std::string expr_replaceAll(const std::vector &args) { - std::string result = args[0]; - const std::regex find(args[1]); - const std::string replace = args[2]; - return std::regex_replace(result, find, replace); +Value expr_replaceAll(const std::vector &args) { + std::string result = args[0].asString(); + const std::regex find(args[1].asString()); + const std::string &replace = args[2].asString(); + return Value(std::regex_replace(result, find, replace)); } -std::string expr_replaceNull(const std::vector &args) { - if (args[0].empty()) { +Value expr_replaceNull(const std::vector &args) { + if (args[0].isNull()) { return args[1]; } else { return args[0]; } } -std::string expr_replaceEmpty(const std::vector &args) { - std::string result = args[0]; +Value expr_replaceEmpty(const std::vector &args) { + std::string result = args[0].asString(); const std::regex find("^[ \n\r\t]*$"); - const std::string replace = args[1]; - return std::regex_replace(result, find, replace); + const std::string &replace = args[1].asString(); + return Value(std::regex_replace(result, find, replace)); } -std::string expr_matches(const std::vector &args) { - const auto &subject = args[0]; - const std::regex expr = std::regex(args[1]); +Value expr_matches(const std::vector &args) { + const auto &subject = args[0].asString(); + const std::regex expr = std::regex(args[1].asString()); - if (std::regex_match(subject.begin(), subject.end(), expr)) { - return "true"; - } else { - return "false"; - } + return Value(std::regex_match(subject.begin(), subject.end(), expr)); } -std::string expr_find(const std::vector &args) { - const auto &subject = args[0]; - const std::regex expr = std::regex(args[1]); +Value expr_find(const std::vector &args) { + const auto &subject = args[0].asString(); + const std::regex expr = std::regex(args[1].asString()); - if (std::regex_search(subject.begin(), subject.end(), expr)) { - return "true"; - } else { - return "false"; - } + return Value(std::regex_search(subject.begin(), subject.end(), expr)); } #endif // EXPRESSION_LANGUAGE_USE_REGEX -std::string expr_binaryOp(const std::vector &args, - long double (*ldop)(long double, long double), - int (*iop)(int, int), - bool long_only = false) { +Value expr_binary_op(const std::vector &args, + long double (*ldop)(long double, long double), + int64_t (*iop)(int64_t, int64_t), + bool long_only = false) { try { - if (!long_only && - args[0].find('.') == args[0].npos && - args[1].find('.') == args[1].npos && - args[1].find('e') == args[1].npos && - args[0].find('e') == args[0].npos && - args[0].find('E') == args[0].npos && - args[1].find('E') == args[1].npos) { - return std::to_string(iop(std::stoi(args[0]), std::stoi(args[1]))); + if (!long_only && !args[0].isDecimal() && !args[1].isDecimal()) { + return Value(iop(args[0].asSignedLong(), args[1].asSignedLong())); } else { - std::stringstream ss; - ss << std::fixed << std::setprecision(std::numeric_limits::digits10) - << ldop(std::stold(args[0]), std::stold(args[1])); - auto result = ss.str(); - result.erase(result.find_last_not_of('0') + 1, std::string::npos); - - if (result.find('.') == result.length() - 1) { - result.erase(result.length() - 1, std::string::npos); - } - - return result; + return Value(ldop(args[0].asLongDouble(), args[1].asLongDouble())); } } catch (const std::exception &e) { - return ""; + return Value(); } } -std::string expr_plus(const std::vector &args) { - return expr_binaryOp(args, - [](long double a, long double b) { return a + b; }, - [](int a, int b) { return a + b; }); +Value expr_plus(const std::vector &args) { + return expr_binary_op(args, + [](long double a, long double b) { return a + b; }, + [](int64_t a, int64_t b) { return a + b; }); } -std::string expr_minus(const std::vector &args) { - return expr_binaryOp(args, - [](long double a, long double b) { return a - b; }, - [](int a, int b) { return a - b; }); +Value expr_minus(const std::vector &args) { + return expr_binary_op(args, + [](long double a, long double b) { return a - b; }, + [](int64_t a, int64_t b) { return a - b; }); } -std::string expr_multiply(const std::vector &args) { - return expr_binaryOp(args, - [](long double a, long double b) { return a * b; }, - [](int a, int b) { return a * b; }); +Value expr_multiply(const std::vector &args) { + return expr_binary_op(args, + [](long double a, long double b) { return a * b; }, + [](int64_t a, int64_t b) { return a * b; }); } -std::string expr_divide(const std::vector &args) { - return expr_binaryOp(args, - [](long double a, long double b) { return a / b; }, - [](int a, int b) { return a / b; }, - true); +Value expr_divide(const std::vector &args) { + return expr_binary_op(args, + [](long double a, long double b) { return a / b; }, + [](int64_t a, int64_t b) { return a / b; }, + true); } -std::string expr_mod(const std::vector &args) { - return expr_binaryOp(args, - [](long double a, long double b) { return std::fmod(a, b); }, - [](int a, int b) { return a % b; }); +Value expr_mod(const std::vector &args) { + return expr_binary_op(args, + [](long double a, long double b) { return std::fmod(a, b); }, + [](int64_t a, int64_t b) { return a % b; }); } -std::string expr_toRadix(const std::vector &args) { - int radix = std::stoi(args[1]); +Value expr_toRadix(const std::vector &args) { + int64_t radix = args[1].asSignedLong(); if (radix < 2 || radix > 36) { throw std::runtime_error("Cannot perform conversion due to invalid radix"); @@ -308,10 +289,11 @@ std::string expr_toRadix(const std::vector &args) { int pad_width = 0; if (args.size() > 2) { - pad_width = std::stoi(args[2]); + pad_width = static_cast(args[2].asUnsignedLong()); } - auto value = std::stoll(args[0], nullptr, 10); +// auto value = std::stoll(args[0].asString(), nullptr, 10); + auto value = args[0].asSignedLong(); std::string sign; @@ -334,27 +316,27 @@ std::string expr_toRadix(const std::vector &args) { std::stringstream ss; ss << sign << std::setfill('0') << std::setw(pad_width) << str_num; - return ss.str(); + return Value(ss.str()); } -std::string expr_fromRadix(const std::vector &args) { - int radix = std::stoi(args[1]); +Value expr_fromRadix(const std::vector &args) { + auto radix = args[1].asSignedLong(); if (radix < 2 || radix > 36) { throw std::runtime_error("Cannot perform conversion due to invalid radix"); } - return std::to_string(std::stoll(args[0], nullptr, radix)); + return Value(std::to_string(std::stoll(args[0].asString(), nullptr, radix))); } -std::string expr_random(const std::vector &args) { +Value expr_random(const std::vector &args) { std::random_device random_device; std::mt19937 generator(random_device()); - std::uniform_int_distribution distribution(0, LLONG_MAX); - return std::to_string(distribution(generator)); + std::uniform_int_distribution distribution(0, LLONG_MAX); + return Value(distribution(generator)); } -template &)> +template &)> Expression make_dynamic_function_incomplete(const std::string &function_name, const std::vector &args, std::size_t num_args) { @@ -371,8 +353,8 @@ Expression make_dynamic_function_incomplete(const std::string &function_name, throw std::runtime_error(message_ss.str()); } - auto result = make_dynamic([=](const Parameters ¶ms) -> std::string { - std::vector evaluated_args; + auto result = make_dynamic([=](const Parameters ¶ms) -> Value { + std::vector evaluated_args; for (const auto &arg : args) { evaluated_args.emplace_back(arg(params)); @@ -384,7 +366,7 @@ Expression make_dynamic_function_incomplete(const std::string &function_name, return result; } -std::string expr_literal(const std::vector &args) { +Value expr_literal(const std::vector &args) { return args[0]; } @@ -485,37 +467,34 @@ Expression Expression::operator+(const Expression &other_expr) const { if (isDynamic() && other_expr.isDynamic()) { auto val_fn = val_fn_; auto other_val_fn = other_expr.val_fn_; - return make_dynamic([val_fn, other_val_fn](const Parameters ¶ms) -> std::string { - std::string result = val_fn(params); - result.append(other_val_fn(params)); - return result; + return make_dynamic([val_fn, other_val_fn](const Parameters ¶ms) -> Value { + Value result = val_fn(params); + return Value(result.asString().append(other_val_fn(params).asString())); }); } else if (isDynamic() && !other_expr.isDynamic()) { auto val_fn = val_fn_; auto other_val = other_expr.val_; - return make_dynamic([val_fn, other_val](const Parameters ¶ms) -> std::string { - std::string result = val_fn(params); - result.append(other_val); - return result; + return make_dynamic([val_fn, other_val](const Parameters ¶ms) -> Value { + Value result = val_fn(params); + return Value(result.asString().append(other_val.asString())); }); } else if (!isDynamic() && other_expr.isDynamic()) { auto val = val_; auto other_val_fn = other_expr.val_fn_; - return make_dynamic([val, other_val_fn](const Parameters ¶ms) -> std::string { - std::string result(val); - result.append(other_val_fn(params)); - return result; + return make_dynamic([val, other_val_fn](const Parameters ¶ms) -> Value { + Value result(val); + return Value(result.asString().append(other_val_fn(params).asString())); }); } else if (!isDynamic() && !other_expr.isDynamic()) { - std::string result(val_); - result.append(other_expr.val_); + std::string result(val_.asString()); + result.append(other_expr.val_.asString()); return make_static(result); } else { throw std::runtime_error("Invalid function composition"); } } -std::string Expression::operator()(const Parameters ¶ms) const { +Value Expression::operator()(const Parameters ¶ms) const { if (isDynamic()) { return val_fn_(params); } else { diff --git a/extensions/expression-language/ProcessContextExpr.cpp b/extensions/expression-language/ProcessContextExpr.cpp index c52b18cfc1..68cdc54f8e 100644 --- a/extensions/expression-language/ProcessContextExpr.cpp +++ b/extensions/expression-language/ProcessContextExpr.cpp @@ -32,7 +32,7 @@ bool ProcessContext::getProperty(const std::string &name, std::string &value, expressions_.emplace(name, expression::compile(expression_str)); } - value = expressions_[name]({flow_file}); + value = expressions_[name]({flow_file}).asString(); return true; } @@ -45,7 +45,7 @@ bool ProcessContext::getDynamicProperty(const std::string &name, std::string &va dynamic_property_expressions_.emplace(name, expression::compile(expression_str)); } - value = dynamic_property_expressions_[name]({flow_file}); + value = dynamic_property_expressions_[name]({flow_file}).asString(); return true; } diff --git a/extensions/expression-language/common/Value.h b/extensions/expression-language/common/Value.h new file mode 100644 index 0000000000..75c80bcd44 --- /dev/null +++ b/extensions/expression-language/common/Value.h @@ -0,0 +1,238 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#ifndef NIFI_MINIFI_CPP_VALUE_H +#define NIFI_MINIFI_CPP_VALUE_H + +namespace org { +namespace apache { +namespace nifi { +namespace minifi { +namespace expression { + + +/** + * Represents an expression value, which can be one of multiple types or NULL + */ +class Value { + public: + + /** + * Construct a default (NULL) value + */ + Value() { + } + + /** + * Construct a string value + */ + explicit Value(const std::string &val) { + setString(val); + } + + /** + * Construct a boolean value + */ + explicit Value(bool val) { + setBoolean(val); + } + + /** + * Construct an unsigned long value + */ + explicit Value(uint64_t val) { + setUnsignedLong(val); + } + + /** + * Construct a signed long value + */ + explicit Value(int64_t val) { + setSignedLong(val); + } + + /** + * Construct a long double value + */ + explicit Value(long double val) { + setLongDouble(val); + } + + bool isNull() const { + return is_null_; + }; + + bool isString() const { + return is_string_; + }; + + bool isDecimal() const { + if (is_long_double_) { + return true; + } else if (is_string_ && (string_val_.find('.') != string_val_.npos || + string_val_.find('e') != string_val_.npos || + string_val_.find('E') != string_val_.npos)) { + return true; + } else { + return false; + } + } + + void setSignedLong(int64_t val) { + is_null_ = false; + is_bool_ = false; + is_signed_long_ = true; + is_unsigned_long_ = false; + is_long_double_ = false; + is_string_ = false; + signed_long_val_ = val; + } + + void setUnsignedLong(uint64_t val) { + is_null_ = false; + is_bool_ = false; + is_signed_long_ = false; + is_unsigned_long_ = true; + is_long_double_ = false; + is_string_ = false; + unsigned_long_val_ = val; + } + + void setLongDouble(long double val) { + is_null_ = false; + is_bool_ = false; + is_signed_long_ = false; + is_unsigned_long_ = false; + is_long_double_ = true; + is_string_ = false; + long_double_val_ = val; + } + + void setBoolean(bool val) { + is_null_ = false; + is_bool_ = true; + is_signed_long_ = false; + is_unsigned_long_ = false; + is_long_double_ = false; + is_string_ = false; + bool_val_ = val; + } + + void setString(const std::string &val) { + is_null_ = false; + is_bool_ = false; + is_signed_long_ = false; + is_unsigned_long_ = false; + is_long_double_ = false; + is_string_ = true; + string_val_ = val; + } + + std::string asString() const { + if (is_string_) { + return string_val_; + } else if (is_bool_) { + if (bool_val_) { + return "true"; + } else { + return "false"; + } + } else if (is_signed_long_) { + return std::to_string(signed_long_val_); + } else if (is_unsigned_long_) { + return std::to_string(unsigned_long_val_); + } else if (is_long_double_) { + std::stringstream ss; + ss << std::fixed << std::setprecision(std::numeric_limits::digits10) + << long_double_val_; + auto result = ss.str(); + result.erase(result.find_last_not_of('0') + 1, std::string::npos); + + if (result.find('.') == result.length() - 1) { + result.erase(result.length() - 1, std::string::npos); + } + + return result; + } else { + return ""; + } + } + + uint64_t asUnsignedLong() const { + if (is_unsigned_long_) { + return unsigned_long_val_; + } else if (is_string_) { + return std::stoul(string_val_); + } else { + return 0.0; + } + } + + int64_t asSignedLong() const { + if (is_signed_long_) { + return signed_long_val_; + } else if (is_unsigned_long_) { + return unsigned_long_val_; + } else if (is_string_) { + return std::stoul(string_val_); + } else if (is_long_double_) { + return long_double_val_; + } else { + return 0.0; + } + } + + long double asLongDouble() const { + if (is_signed_long_) { + return signed_long_val_; + } else if (is_unsigned_long_) { + return unsigned_long_val_; + } else if (is_long_double_) { + return long_double_val_; + } else if (is_string_) { + return std::stold(string_val_); + } else { + return 0.0; + } + } + + private: + bool is_null_ = true; + bool is_string_ = false; + bool is_bool_ = false; + bool is_unsigned_long_ = false; + bool is_signed_long_ = false; + bool is_long_double_ = false; + bool bool_val_ = false; + uint64_t unsigned_long_val_ = 0; + int64_t signed_long_val_ = 0; + long double long_double_val_ = 0.0; + std::string string_val_ = ""; +}; + +} /* namespace expression */ +} /* namespace minifi */ +} /* namespace nifi */ +} /* namespace apache */ +} /* namespace org */ + +#endif //NIFI_MINIFI_CPP_VALUE_H diff --git a/extensions/expression-language/impl/expression/Expression.h b/extensions/expression-language/impl/expression/Expression.h index 9ee4a5413b..f123f77bc9 100644 --- a/extensions/expression-language/impl/expression/Expression.h +++ b/extensions/expression-language/impl/expression/Expression.h @@ -25,6 +25,7 @@ #undef EXPRESSION_LANGUAGE_USE_REGEX #endif +#include #include #include @@ -41,7 +42,7 @@ typedef struct { std::weak_ptr flow_file; } Parameters; -static const std::function NOOP_FN; +static const std::function NOOP_FN; /** * A NiFi expression language expression. @@ -49,11 +50,15 @@ static const std::function NOOP_FN; class Expression { public: - explicit Expression(std::string val = "", - std::function val_fn = NOOP_FN) - : val_(std::move(val)), - val_fn_(std::move(val_fn)), + Expression() { + val_fn_ = NOOP_FN; + } + + explicit Expression(Value val, + std::function val_fn = NOOP_FN) + : val_fn_(std::move(val_fn)), fn_args_() { + val_ = val; } /** @@ -80,11 +85,11 @@ class Expression { * @param params * @return dynamically-computed result of expression */ - std::string operator()(const Parameters ¶ms) const; + Value operator()(const Parameters ¶ms) const; protected: - std::string val_; - std::function val_fn_; + Value val_; + std::function val_fn_; std::vector fn_args_; }; @@ -110,7 +115,7 @@ Expression make_static(std::string val); * @param val_fn * @return */ -Expression make_dynamic(std::function val_fn); +Expression make_dynamic(const std::function &val_fn); /** * Creates a dynamic expression which evaluates the given flow file attribute. diff --git a/extensions/expression-language/noop/expression/Expression.h b/extensions/expression-language/noop/expression/Expression.h index 08c4ae4af2..5cbebfe5b5 100644 --- a/extensions/expression-language/noop/expression/Expression.h +++ b/extensions/expression-language/noop/expression/Expression.h @@ -19,6 +19,7 @@ #define NIFI_MINIFI_CPP_EXPRESSION_H #include +#include namespace org { namespace apache { diff --git a/libminifi/test/expression-language-tests/ExpressionLanguageTests.cpp b/libminifi/test/expression-language-tests/ExpressionLanguageTests.cpp index da0d685e36..de3292ea1f 100644 --- a/libminifi/test/expression-language-tests/ExpressionLanguageTests.cpp +++ b/libminifi/test/expression-language-tests/ExpressionLanguageTests.cpp @@ -32,24 +32,24 @@ class MockFlowFile : public core::FlowFile { }; TEST_CASE("Trivial static expression", "[expressionLanguageTestTrivialStaticExpr]") { // NOLINT - REQUIRE("a" == expression::make_static("a")({})); + REQUIRE("a" == expression::make_static("a")({}).asString()); } TEST_CASE("Text expression", "[expressionLanguageTestTextExpression]") { // NOLINT auto expr = expression::compile("text"); - REQUIRE("text" == expr({})); + REQUIRE("text" == expr({}).asString()); } TEST_CASE("Text expression with escaped dollar", "[expressionLanguageTestEscapedDollar]") { // NOLINT auto expr = expression::compile("te$$xt"); - REQUIRE("te$xt" == expr({})); + REQUIRE("te$xt" == expr({}).asString()); } TEST_CASE("Attribute expression", "[expressionLanguageTestAttributeExpression]") { // NOLINT auto flow_file = std::make_shared(); flow_file->addAttribute("attr_a", "__attr_value_a__"); auto expr = expression::compile("text_before${attr_a}text_after"); - REQUIRE("text_before__attr_value_a__text_after" == expr({flow_file})); + REQUIRE("text_before__attr_value_a__text_after" == expr({flow_file}).asString()); } TEST_CASE("Multi-attribute expression", "[expressionLanguageTestMultiAttributeExpression]") { // NOLINT @@ -57,7 +57,7 @@ TEST_CASE("Multi-attribute expression", "[expressionLanguageTestMultiAttributeEx flow_file->addAttribute("attr_a", "__attr_value_a__"); flow_file->addAttribute("attr_b", "__attr_value_b__"); auto expr = expression::compile("text_before${attr_a}text_between${attr_b}text_after"); - REQUIRE("text_before__attr_value_a__text_between__attr_value_b__text_after" == expr({flow_file})); + REQUIRE("text_before__attr_value_a__text_between__attr_value_b__text_after" == expr({flow_file}).asString()); } TEST_CASE("Multi-flowfile attribute expression", @@ -66,18 +66,18 @@ TEST_CASE("Multi-flowfile attribute expression", auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr_a", "__flow_a_attr_value_a__"); - REQUIRE("text_before__flow_a_attr_value_a__text_after" == expr({flow_file_a})); + REQUIRE("text_before__flow_a_attr_value_a__text_after" == expr({flow_file_a}).asString()); auto flow_file_b = std::make_shared(); flow_file_b->addAttribute("attr_a", "__flow_b_attr_value_a__"); - REQUIRE("text_before__flow_b_attr_value_a__text_after" == expr({flow_file_b})); + REQUIRE("text_before__flow_b_attr_value_a__text_after" == expr({flow_file_b}).asString()); } TEST_CASE("Attribute expression with whitespace", "[expressionLanguageTestAttributeExpressionWhitespace]") { // NOLINT auto flow_file = std::make_shared(); flow_file->addAttribute("attr_a", "__attr_value_a__"); auto expr = expression::compile("text_before${\n\tattr_a \r}text_after"); - REQUIRE("text_before__attr_value_a__text_after" == expr({flow_file})); + REQUIRE("text_before__attr_value_a__text_after" == expr({flow_file}).asString()); } TEST_CASE("Special characters expression", "[expressionLanguageTestSpecialCharactersExpression]") { // NOLINT @@ -85,7 +85,7 @@ TEST_CASE("Special characters expression", "[expressionLanguageTestSpecialCharac auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr_a", "__flow_a_attr_value_a__"); - REQUIRE("text_before|{}()[],:;\\/*#'\" \t\r\n__flow_a_attr_value_a__}()text_after" == expr({flow_file_a})); + REQUIRE("text_before|{}()[],:;\\/*#'\" \t\r\n__flow_a_attr_value_a__}()text_after" == expr({flow_file_a}).asString()); } TEST_CASE("UTF-8 characters expression", "[expressionLanguageTestUTF8Expression]") { // NOLINT @@ -93,7 +93,7 @@ TEST_CASE("UTF-8 characters expression", "[expressionLanguageTestUTF8Expression] auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr_a", "__flow_a_attr_value_a__"); - REQUIRE("text_before¥£€¢₡₢₣₤₥₦₧₨₩₪₫₭₮₯₹__flow_a_attr_value_a__text_after" == expr({flow_file_a})); + REQUIRE("text_before¥£€¢₡₢₣₤₥₦₧₨₩₪₫₭₮₯₹__flow_a_attr_value_a__text_after" == expr({flow_file_a}).asString()); } TEST_CASE("UTF-8 characters attribute", "[expressionLanguageTestUTF8Attribute]") { // NOLINT @@ -101,7 +101,7 @@ TEST_CASE("UTF-8 characters attribute", "[expressionLanguageTestUTF8Attribute]") auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr_a", "__¥£€¢₡₢₣₤₥₦₧₨₩₪₫₭₮₯₹__"); - REQUIRE("text_before__¥£€¢₡₢₣₤₥₦₧₨₩₪₫₭₮₯₹__text_after" == expr({flow_file_a})); + REQUIRE("text_before__¥£€¢₡₢₣₤₥₦₧₨₩₪₫₭₮₯₹__text_after" == expr({flow_file_a}).asString()); } TEST_CASE("Single quoted attribute expression", "[expressionLanguageTestSingleQuotedAttributeExpression]") { // NOLINT @@ -109,7 +109,7 @@ TEST_CASE("Single quoted attribute expression", "[expressionLanguageTestSingleQu auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("|{}()[],:;\\/*# \t\r\n$", "__flow_a_attr_value_a__"); - REQUIRE("text_before__flow_a_attr_value_a__text_after" == expr({flow_file_a})); + REQUIRE("text_before__flow_a_attr_value_a__text_after" == expr({flow_file_a}).asString()); } TEST_CASE("Double quoted attribute expression", "[expressionLanguageTestDoubleQuotedAttributeExpression]") { // NOLINT @@ -117,7 +117,7 @@ TEST_CASE("Double quoted attribute expression", "[expressionLanguageTestDoubleQu auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("|{}()[],:;\\/*# \t\r\n$", "__flow_a_attr_value_a__"); - REQUIRE("text_before__flow_a_attr_value_a__text_after" == expr({flow_file_a})); + REQUIRE("text_before__flow_a_attr_value_a__text_after" == expr({flow_file_a}).asString()); } TEST_CASE("Hostname function", "[expressionLanguageTestHostnameFunction]") { // NOLINT @@ -131,7 +131,7 @@ TEST_CASE("Hostname function", "[expressionLanguageTestHostnameFunction]") { // expected.append("text_after"); auto flow_file_a = std::make_shared(); - REQUIRE(expected == expr({flow_file_a})); + REQUIRE(expected == expr({flow_file_a}).asString()); } TEST_CASE("ToUpper function", "[expressionLanguageTestToUpperFunction]") { // NOLINT @@ -140,14 +140,14 @@ TEST_CASE("ToUpper function", "[expressionLanguageTestToUpperFunction]") { // N }text_after)"); auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr_a", "__flow_a_attr_value_a__"); - REQUIRE("text_before__FLOW_A_ATTR_VALUE_A__text_after" == expr({flow_file_a})); + REQUIRE("text_before__FLOW_A_ATTR_VALUE_A__text_after" == expr({flow_file_a}).asString()); } TEST_CASE("ToUpper function w/o whitespace", "[expressionLanguageTestToUpperFunctionWithoutWhitespace]") { // NOLINT auto expr = expression::compile(R"(text_before${attr_a:toUpper()}text_after)"); auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr_a", "__flow_a_attr_value_a__"); - REQUIRE("text_before__FLOW_A_ATTR_VALUE_A__text_after" == expr({flow_file_a})); + REQUIRE("text_before__FLOW_A_ATTR_VALUE_A__text_after" == expr({flow_file_a}).asString()); } TEST_CASE("ToLower function", "[expressionLanguageTestToLowerFunction]") { // NOLINT @@ -156,7 +156,7 @@ TEST_CASE("ToLower function", "[expressionLanguageTestToLowerFunction]") { // N }text_after)"); auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr_a", "__FLOW_A_ATTR_VALUE_A__"); - REQUIRE("text_before__flow_a_attr_value_a__text_after" == expr({flow_file_a})); + REQUIRE("text_before__flow_a_attr_value_a__text_after" == expr({flow_file_a}).asString()); } TEST_CASE("GetFile PutFile dynamic attribute", "[expressionLanguageTestGetFilePutFileDynamicAttribute]") { // NOLINT @@ -257,7 +257,7 @@ TEST_CASE("Substring 2 arg", "[expressionLanguageSubstring2]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "__flow_a_attr_value_a__"); - REQUIRE("text_before_a_attr_text_after" == expr({flow_file_a})); + REQUIRE("text_before_a_attr_text_after" == expr({flow_file_a}).asString()); } TEST_CASE("Substring 1 arg", "[expressionLanguageSubstring1]") { // NOLINT @@ -265,7 +265,7 @@ TEST_CASE("Substring 1 arg", "[expressionLanguageSubstring1]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "__flow_a_attr_value_a__"); - REQUIRE("text_before_a_attr_value_a__text_after" == expr({flow_file_a})); + REQUIRE("text_before_a_attr_value_a__text_after" == expr({flow_file_a}).asString()); } TEST_CASE("Substring Before", "[expressionLanguageSubstringBefore]") { // NOLINT @@ -273,7 +273,7 @@ TEST_CASE("Substring Before", "[expressionLanguageSubstringBefore]") { // NOLIN auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "__flow_a_attr_value_a__"); - REQUIRE("__flow_a_" == expr({flow_file_a})); + REQUIRE("__flow_a_" == expr({flow_file_a}).asString()); } TEST_CASE("Substring Before Last", "[expressionLanguageSubstringBeforeLast]") { // NOLINT @@ -281,7 +281,7 @@ TEST_CASE("Substring Before Last", "[expressionLanguageSubstringBeforeLast]") { auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "__flow_a_attr_value_a__"); - REQUIRE("__flow_a_attr_value" == expr({flow_file_a})); + REQUIRE("__flow_a_attr_value" == expr({flow_file_a}).asString()); } TEST_CASE("Substring After", "[expressionLanguageSubstringAfter]") { // NOLINT @@ -289,7 +289,7 @@ TEST_CASE("Substring After", "[expressionLanguageSubstringAfter]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "__flow_a_attr_value_a__"); - REQUIRE("_attr_value_a__" == expr({flow_file_a})); + REQUIRE("_attr_value_a__" == expr({flow_file_a}).asString()); } TEST_CASE("Substring After Last", "[expressionLanguageSubstringAfterLast]") { // NOLINT @@ -297,7 +297,7 @@ TEST_CASE("Substring After Last", "[expressionLanguageSubstringAfterLast]") { / auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "__flow_a_attr_value_a__"); - REQUIRE("__" == expr({flow_file_a})); + REQUIRE("__" == expr({flow_file_a}).asString()); } TEST_CASE("Starts With", "[expressionLanguageStartsWith]") { // NOLINT @@ -305,7 +305,7 @@ TEST_CASE("Starts With", "[expressionLanguageStartsWith]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "A BRAND TEST"); - REQUIRE("false" == expr({flow_file_a})); + REQUIRE("false" == expr({flow_file_a}).asString()); } TEST_CASE("Starts With 2", "[expressionLanguageStartsWith2]") { // NOLINT @@ -313,7 +313,7 @@ TEST_CASE("Starts With 2", "[expressionLanguageStartsWith2]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "a brand TEST"); - REQUIRE("true" == expr({flow_file_a})); + REQUIRE("true" == expr({flow_file_a}).asString()); } TEST_CASE("Ends With", "[expressionLanguageEndsWith]") { // NOLINT @@ -321,7 +321,7 @@ TEST_CASE("Ends With", "[expressionLanguageEndsWith]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "a brand new filename.TXT"); - REQUIRE("false" == expr({flow_file_a})); + REQUIRE("false" == expr({flow_file_a}).asString()); } TEST_CASE("Ends With 2", "[expressionLanguageEndsWith2]") { // NOLINT @@ -329,7 +329,7 @@ TEST_CASE("Ends With 2", "[expressionLanguageEndsWith2]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "a brand new filename.TXT"); - REQUIRE("true" == expr({flow_file_a})); + REQUIRE("true" == expr({flow_file_a}).asString()); } TEST_CASE("Contains", "[expressionLanguageContains]") { // NOLINT @@ -337,7 +337,7 @@ TEST_CASE("Contains", "[expressionLanguageContains]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "a brand new filename.txt"); - REQUIRE("true" == expr({flow_file_a})); + REQUIRE("true" == expr({flow_file_a}).asString()); } TEST_CASE("Contains 2", "[expressionLanguageContains2]") { // NOLINT @@ -345,7 +345,7 @@ TEST_CASE("Contains 2", "[expressionLanguageContains2]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "a brand new filename.txt"); - REQUIRE("false" == expr({flow_file_a})); + REQUIRE("false" == expr({flow_file_a}).asString()); } TEST_CASE("In", "[expressionLanguageIn]") { // NOLINT @@ -353,7 +353,7 @@ TEST_CASE("In", "[expressionLanguageIn]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "JOHN"); - REQUIRE("true" == expr({flow_file_a})); + REQUIRE("true" == expr({flow_file_a}).asString()); } TEST_CASE("In 2", "[expressionLanguageIn2]") { // NOLINT @@ -361,7 +361,7 @@ TEST_CASE("In 2", "[expressionLanguageIn2]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "JOHN"); - REQUIRE("false" == expr({flow_file_a})); + REQUIRE("false" == expr({flow_file_a}).asString()); } TEST_CASE("Substring Before No Args", "[expressionLanguageSubstringBeforeNoArgs]") { // NOLINT @@ -381,7 +381,7 @@ TEST_CASE("Replace", "[expressionLanguageReplace]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "a brand new filename.txt"); - REQUIRE("a brand new filename_txt" == expr({flow_file_a})); + REQUIRE("a brand new filename_txt" == expr({flow_file_a}).asString()); } TEST_CASE("Replace 2", "[expressionLanguageReplace2]") { // NOLINT @@ -389,7 +389,7 @@ TEST_CASE("Replace 2", "[expressionLanguageReplace2]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "a brand new filename.txt"); - REQUIRE("a.brand.new.filename.txt" == expr({flow_file_a})); + REQUIRE("a.brand.new.filename.txt" == expr({flow_file_a}).asString()); } TEST_CASE("Replace First", "[expressionLanguageReplaceFirst]") { // NOLINT @@ -397,7 +397,7 @@ TEST_CASE("Replace First", "[expressionLanguageReplaceFirst]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "a brand new filename.txt"); - REQUIRE("the brand new filename.txt" == expr({flow_file_a})); + REQUIRE("the brand new filename.txt" == expr({flow_file_a}).asString()); } TEST_CASE("Replace First Regex", "[expressionLanguageReplaceFirstRegex]") { // NOLINT @@ -405,7 +405,7 @@ TEST_CASE("Replace First Regex", "[expressionLanguageReplaceFirstRegex]") { // auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "a brand new filename.txt"); - REQUIRE("a grand new filename.txt" == expr({flow_file_a})); + REQUIRE("a grand new filename.txt" == expr({flow_file_a}).asString()); } TEST_CASE("Replace All", "[expressionLanguageReplaceAll]") { // NOLINT @@ -413,7 +413,7 @@ TEST_CASE("Replace All", "[expressionLanguageReplaceAll]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "a brand new filename.txt"); - REQUIRE("a brand new filename" == expr({flow_file_a})); + REQUIRE("a brand new filename" == expr({flow_file_a}).asString()); } TEST_CASE("Replace All 2", "[expressionLanguageReplaceAll2]") { // NOLINT @@ -421,7 +421,7 @@ TEST_CASE("Replace All 2", "[expressionLanguageReplaceAll2]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "a brand new filename.txt"); - REQUIRE("new filename.txt" == expr({flow_file_a})); + REQUIRE("new filename.txt" == expr({flow_file_a}).asString()); } TEST_CASE("Replace All 3", "[expressionLanguageReplaceAll3]") { // NOLINT @@ -429,7 +429,7 @@ TEST_CASE("Replace All 3", "[expressionLanguageReplaceAll3]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "a brand new filename.txt"); - REQUIRE("a brand new filename.txt" == expr({flow_file_a})); + REQUIRE("a brand new filename.txt" == expr({flow_file_a}).asString()); } TEST_CASE("Replace Null", "[expressionLanguageReplaceNull]") { // NOLINT @@ -437,7 +437,7 @@ TEST_CASE("Replace Null", "[expressionLanguageReplaceNull]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "a brand new filename.txt"); - REQUIRE("a brand new filename.txt" == expr({flow_file_a})); + REQUIRE("a brand new filename.txt" == expr({flow_file_a}).asString()); } TEST_CASE("Replace Null 2", "[expressionLanguageReplaceNull2]") { // NOLINT @@ -445,7 +445,7 @@ TEST_CASE("Replace Null 2", "[expressionLanguageReplaceNull2]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr2", "a brand new filename.txt"); - REQUIRE("abc" == expr({flow_file_a})); + REQUIRE("abc" == expr({flow_file_a}).asString()); } TEST_CASE("Replace Empty", "[expressionLanguageReplaceEmpty]") { // NOLINT @@ -453,7 +453,7 @@ TEST_CASE("Replace Empty", "[expressionLanguageReplaceEmpty]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "a brand new filename.txt"); - REQUIRE("a brand new filename.txt" == expr({flow_file_a})); + REQUIRE("a brand new filename.txt" == expr({flow_file_a}).asString()); } TEST_CASE("Replace Empty 2", "[expressionLanguageReplaceEmpty2]") { // NOLINT @@ -461,7 +461,7 @@ TEST_CASE("Replace Empty 2", "[expressionLanguageReplaceEmpty2]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", " \t \r \n "); - REQUIRE("abc" == expr({flow_file_a})); + REQUIRE("abc" == expr({flow_file_a}).asString()); } TEST_CASE("Replace Empty 3", "[expressionLanguageReplaceEmpty2]") { // NOLINT @@ -469,7 +469,7 @@ TEST_CASE("Replace Empty 3", "[expressionLanguageReplaceEmpty2]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr2", "test"); - REQUIRE("abc" == expr({flow_file_a})); + REQUIRE("abc" == expr({flow_file_a}).asString()); } TEST_CASE("Matches", "[expressionLanguageMatches]") { // NOLINT @@ -477,7 +477,7 @@ TEST_CASE("Matches", "[expressionLanguageMatches]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "At:est"); - REQUIRE("true" == expr({flow_file_a})); + REQUIRE("true" == expr({flow_file_a}).asString()); } TEST_CASE("Matches 2", "[expressionLanguageMatches2]") { // NOLINT @@ -485,7 +485,7 @@ TEST_CASE("Matches 2", "[expressionLanguageMatches2]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "At:something"); - REQUIRE("false" == expr({flow_file_a})); + REQUIRE("false" == expr({flow_file_a}).asString()); } TEST_CASE("Matches 3", "[expressionLanguageMatches3]") { // NOLINT @@ -493,7 +493,7 @@ TEST_CASE("Matches 3", "[expressionLanguageMatches3]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", " At:est"); - REQUIRE("false" == expr({flow_file_a})); + REQUIRE("false" == expr({flow_file_a}).asString()); } TEST_CASE("Find", "[expressionLanguageFind]") { // NOLINT @@ -501,7 +501,7 @@ TEST_CASE("Find", "[expressionLanguageFind]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "a brand new filename.txt"); - REQUIRE("true" == expr({flow_file_a})); + REQUIRE("true" == expr({flow_file_a}).asString()); } TEST_CASE("Find 2", "[expressionLanguageFind2]") { // NOLINT @@ -509,7 +509,7 @@ TEST_CASE("Find 2", "[expressionLanguageFind2]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "a brand new filename.txt"); - REQUIRE("false" == expr({flow_file_a})); + REQUIRE("false" == expr({flow_file_a}).asString()); } TEST_CASE("Find 3", "[expressionLanguageFind3]") { // NOLINT @@ -517,7 +517,7 @@ TEST_CASE("Find 3", "[expressionLanguageFind3]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "a brand new filename.txt"); - REQUIRE("true" == expr({flow_file_a})); + REQUIRE("true" == expr({flow_file_a}).asString()); } TEST_CASE("IndexOf", "[expressionLanguageIndexOf]") { // NOLINT @@ -525,7 +525,7 @@ TEST_CASE("IndexOf", "[expressionLanguageIndexOf]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "a brand new filename.txt"); - REQUIRE("-1" == expr({flow_file_a})); + REQUIRE("-1" == expr({flow_file_a}).asString()); } TEST_CASE("IndexOf2", "[expressionLanguageIndexOf2]") { // NOLINT @@ -533,7 +533,7 @@ TEST_CASE("IndexOf2", "[expressionLanguageIndexOf2]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "a brand new filename.txt"); - REQUIRE("20" == expr({flow_file_a})); + REQUIRE("20" == expr({flow_file_a}).asString()); } TEST_CASE("IndexOf3", "[expressionLanguageIndexOf3]") { // NOLINT @@ -541,7 +541,7 @@ TEST_CASE("IndexOf3", "[expressionLanguageIndexOf3]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "a brand new filename.txt"); - REQUIRE("0" == expr({flow_file_a})); + REQUIRE("0" == expr({flow_file_a}).asString()); } TEST_CASE("IndexOf4", "[expressionLanguageIndexOf4]") { // NOLINT @@ -549,7 +549,7 @@ TEST_CASE("IndexOf4", "[expressionLanguageIndexOf4]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "a brand new filename.txt"); - REQUIRE("1" == expr({flow_file_a})); + REQUIRE("1" == expr({flow_file_a}).asString()); } TEST_CASE("LastIndexOf", "[expressionLanguageLastIndexOf]") { // NOLINT @@ -557,7 +557,7 @@ TEST_CASE("LastIndexOf", "[expressionLanguageLastIndexOf]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "a brand new filename.txt"); - REQUIRE("-1" == expr({flow_file_a})); + REQUIRE("-1" == expr({flow_file_a}).asString()); } TEST_CASE("LastIndexOf2", "[expressionLanguageLastIndexOf2]") { // NOLINT @@ -565,7 +565,7 @@ TEST_CASE("LastIndexOf2", "[expressionLanguageLastIndexOf2]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "a brand new filename.txt"); - REQUIRE("20" == expr({flow_file_a})); + REQUIRE("20" == expr({flow_file_a}).asString()); } TEST_CASE("LastIndexOf3", "[expressionLanguageLastIndexOf3]") { // NOLINT @@ -573,7 +573,7 @@ TEST_CASE("LastIndexOf3", "[expressionLanguageLastIndexOf3]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "a brand new filename.txt"); - REQUIRE("17" == expr({flow_file_a})); + REQUIRE("17" == expr({flow_file_a}).asString()); } TEST_CASE("LastIndexOf4", "[expressionLanguageLastIndexOf4]") { // NOLINT @@ -581,7 +581,7 @@ TEST_CASE("LastIndexOf4", "[expressionLanguageLastIndexOf4]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "a brand new filename.txt"); - REQUIRE("11" == expr({flow_file_a})); + REQUIRE("11" == expr({flow_file_a}).asString()); } #endif // EXPRESSION_LANGUAGE_USE_REGEX @@ -591,7 +591,7 @@ TEST_CASE("Plus Integer", "[expressionLanguagePlusInteger]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "11"); - REQUIRE("24" == expr({flow_file_a})); + REQUIRE("24" == expr({flow_file_a}).asString()); } TEST_CASE("Plus Decimal", "[expressionLanguagePlusDecimal]") { // NOLINT @@ -599,7 +599,7 @@ TEST_CASE("Plus Decimal", "[expressionLanguagePlusDecimal]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "11.1"); - REQUIRE("-2.24567" == expr({flow_file_a})); + REQUIRE("-2.24567" == expr({flow_file_a}).asString()); } TEST_CASE("Plus Exponent", "[expressionLanguagePlusExponent]") { // NOLINT @@ -607,7 +607,7 @@ TEST_CASE("Plus Exponent", "[expressionLanguagePlusExponent]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "11"); - REQUIRE("10000011" == expr({flow_file_a})); + REQUIRE("10000011" == expr({flow_file_a}).asString()); } TEST_CASE("Plus Exponent 2", "[expressionLanguagePlusExponent2]") { // NOLINT @@ -615,7 +615,7 @@ TEST_CASE("Plus Exponent 2", "[expressionLanguagePlusExponent2]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "11.345678901234"); - REQUIRE("10000011.345678901234351" == expr({flow_file_a})); + REQUIRE("10000011.345678901234351" == expr({flow_file_a}).asString()); } TEST_CASE("Minus Integer", "[expressionLanguageMinusInteger]") { // NOLINT @@ -623,7 +623,7 @@ TEST_CASE("Minus Integer", "[expressionLanguageMinusInteger]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "11"); - REQUIRE("-2" == expr({flow_file_a})); + REQUIRE("-2" == expr({flow_file_a}).asString()); } TEST_CASE("Minus Decimal", "[expressionLanguageMinusDecimal]") { // NOLINT @@ -631,7 +631,7 @@ TEST_CASE("Minus Decimal", "[expressionLanguageMinusDecimal]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "11.1"); - REQUIRE("24.44567" == expr({flow_file_a})); + REQUIRE("24.44567" == expr({flow_file_a}).asString()); } TEST_CASE("Multiply Integer", "[expressionLanguageMultiplyInteger]") { // NOLINT @@ -639,7 +639,7 @@ TEST_CASE("Multiply Integer", "[expressionLanguageMultiplyInteger]") { // NOLIN auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "11"); - REQUIRE("143" == expr({flow_file_a})); + REQUIRE("143" == expr({flow_file_a}).asString()); } TEST_CASE("Multiply Decimal", "[expressionLanguageMultiplyDecimal]") { // NOLINT @@ -647,7 +647,7 @@ TEST_CASE("Multiply Decimal", "[expressionLanguageMultiplyDecimal]") { // NOLIN auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "11.1"); - REQUIRE("-148.136937" == expr({flow_file_a})); + REQUIRE("-148.136937" == expr({flow_file_a}).asString()); } TEST_CASE("Divide Integer", "[expressionLanguageDivideInteger]") { // NOLINT @@ -655,7 +655,7 @@ TEST_CASE("Divide Integer", "[expressionLanguageDivideInteger]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "11"); - REQUIRE("0.846153846153846" == expr({flow_file_a})); + REQUIRE("0.846153846153846" == expr({flow_file_a}).asString()); } TEST_CASE("Divide Decimal", "[expressionLanguageDivideDecimal]") { // NOLINT @@ -663,7 +663,7 @@ TEST_CASE("Divide Decimal", "[expressionLanguageDivideDecimal]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "11.1"); - REQUIRE("-0.831730441409086" == expr({flow_file_a})); + REQUIRE("-0.831730441409086" == expr({flow_file_a}).asString()); } TEST_CASE("To Radix", "[expressionLanguageToRadix]") { // NOLINT @@ -671,7 +671,7 @@ TEST_CASE("To Radix", "[expressionLanguageToRadix]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "10"); - REQUIRE("0000000000001010" == expr({flow_file_a})); + REQUIRE("0000000000001010" == expr({flow_file_a}).asString()); } TEST_CASE("To Radix 2", "[expressionLanguageToRadix2]") { // NOLINT @@ -679,7 +679,7 @@ TEST_CASE("To Radix 2", "[expressionLanguageToRadix2]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "13"); - REQUIRE("d" == expr({flow_file_a})); + REQUIRE("d" == expr({flow_file_a}).asString()); } TEST_CASE("To Radix 3", "[expressionLanguageToRadix3]") { // NOLINT @@ -687,7 +687,7 @@ TEST_CASE("To Radix 3", "[expressionLanguageToRadix3]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "-2347"); - REQUIRE("-000004a1" == expr({flow_file_a})); + REQUIRE("-000004a1" == expr({flow_file_a}).asString()); } TEST_CASE("From Radix", "[expressionLanguageFromRadix]") { // NOLINT @@ -695,7 +695,7 @@ TEST_CASE("From Radix", "[expressionLanguageFromRadix]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "0000000000001010"); - REQUIRE("10" == expr({flow_file_a})); + REQUIRE("10" == expr({flow_file_a}).asString()); } TEST_CASE("From Radix 2", "[expressionLanguageFromRadix2]") { // NOLINT @@ -703,7 +703,7 @@ TEST_CASE("From Radix 2", "[expressionLanguageFromRadix2]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "d"); - REQUIRE("13" == expr({flow_file_a})); + REQUIRE("13" == expr({flow_file_a}).asString()); } TEST_CASE("From Radix 3", "[expressionLanguageFromRadix3]") { // NOLINT @@ -711,14 +711,14 @@ TEST_CASE("From Radix 3", "[expressionLanguageFromRadix3]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "-000004a1"); - REQUIRE("-2347" == expr({flow_file_a})); + REQUIRE("-2347" == expr({flow_file_a}).asString()); } TEST_CASE("Random", "[expressionLanguageRandom]") { // NOLINT auto expr = expression::compile("${random()}"); auto flow_file_a = std::make_shared(); - auto result = std::stoll(expr({flow_file_a})); + auto result = expr({flow_file_a}).asSignedLong(); REQUIRE(result > 0); } @@ -727,14 +727,14 @@ TEST_CASE("Chained call", "[expressionChainedCall]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "7"); - REQUIRE("22" == expr({flow_file_a})); + REQUIRE("22" == expr({flow_file_a}).asString()); } 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(); - REQUIRE("42" == expr({flow_file_a})); + REQUIRE(42 == expr({flow_file_a}).asSignedLong()); } TEST_CASE("Chained call 3", "[expressionChainedCall3]") { // NOLINT @@ -742,5 +742,5 @@ TEST_CASE("Chained call 3", "[expressionChainedCall3]") { // NOLINT auto flow_file_a = std::make_shared(); flow_file_a->addAttribute("attr", "7"); - REQUIRE("238" == expr({flow_file_a})); + REQUIRE("238" == expr({flow_file_a}).asString()); }