From f15ba74b7d0ec07a59448517c171b52a000b0348 Mon Sep 17 00:00:00 2001 From: kevin Date: Thu, 6 Feb 2020 16:39:50 +0800 Subject: [PATCH] Implement in expression. --- include/mbgl/style/expression/expression.hpp | 3 +- include/mbgl/style/expression/in.hpp | 43 +++++++ metrics/ignores/platform-all.json | 6 - next/CMakeLists.txt | 2 + src/core-files.json | 2 + src/mbgl/style/expression/in.cpp | 116 ++++++++++++++++++ src/mbgl/style/expression/parsing_context.cpp | 2 + test/style/expression/expression.test.cpp | 3 +- 8 files changed, 168 insertions(+), 9 deletions(-) create mode 100644 include/mbgl/style/expression/in.hpp create mode 100644 src/mbgl/style/expression/in.cpp diff --git a/include/mbgl/style/expression/expression.hpp b/include/mbgl/style/expression/expression.hpp index 9893daa8c42..738de87fc20 100644 --- a/include/mbgl/style/expression/expression.hpp +++ b/include/mbgl/style/expression/expression.hpp @@ -160,7 +160,8 @@ enum class Kind : int32_t { FormatExpression, FormatSectionOverride, NumberFormat, - ImageExpression + ImageExpression, + In }; class Expression { diff --git a/include/mbgl/style/expression/in.hpp b/include/mbgl/style/expression/in.hpp new file mode 100644 index 00000000000..40db0995f42 --- /dev/null +++ b/include/mbgl/style/expression/in.hpp @@ -0,0 +1,43 @@ +#pragma once + +#include +#include +#include + +namespace mbgl { +namespace style { +namespace expression { + +class In : public Expression { +public: + In(std::unique_ptr needle_, std::unique_ptr haystack_) + : Expression(Kind::In, type::Boolean), needle(std::move(needle_)), haystack(std::move(haystack_)) {} + + static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx); + + EvaluationResult evaluate(const EvaluationContext& params) const override; + void eachChild(const std::function&) const override; + + bool operator==(const Expression& e) const override { + if (e.getKind() == Kind::In) { + auto rhs = static_cast(&e); + return *needle == *(rhs->needle) && *haystack == *(rhs->haystack); + } + return false; + } + + std::vector> possibleOutputs() const override { return {nullopt}; } + + std::string getOperator() const override { return "in"; } + +private: + std::unique_ptr needle; + std::unique_ptr haystack; + static bool isComparableType(type::Type type); + static bool isComparableRuntimeValue(type::Type type); + static bool isSearchableRuntimeValue(type::Type type); +}; + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/metrics/ignores/platform-all.json b/metrics/ignores/platform-all.json index b92310a756a..03c8894900f 100644 --- a/metrics/ignores/platform-all.json +++ b/metrics/ignores/platform-all.json @@ -1,11 +1,5 @@ { "expression-tests/collator/accent-equals-de": "Locale-specific behavior changes based on platform.", - "expression-tests/in/assert-array": "https://github.com/mapbox/mapbox-gl-native/issues/15893", - "expression-tests/in/assert-string": "https://github.com/mapbox/mapbox-gl-native/issues/15893", - "expression-tests/in/basic-array": "https://github.com/mapbox/mapbox-gl-native/issues/15893", - "expression-tests/in/basic-string": "https://github.com/mapbox/mapbox-gl-native/issues/15893", - "expression-tests/in/invalid-haystack": "https://github.com/mapbox/mapbox-gl-native/issues/15893", - "expression-tests/in/invalid-needle": "https://github.com/mapbox/mapbox-gl-native/issues/15893", "expression-tests/interpolate-hcl/linear": "https://github.com/mapbox/mapbox-gl-native/issues/8720", "expression-tests/interpolate-lab/linear": "https://github.com/mapbox/mapbox-gl-native/issues/8720", "expression-tests/is-supported-script/default": "This tests RTL text plugin behavior specific to GL JS", diff --git a/next/CMakeLists.txt b/next/CMakeLists.txt index 95b330992b3..9ccec124e30 100644 --- a/next/CMakeLists.txt +++ b/next/CMakeLists.txt @@ -162,6 +162,7 @@ add_library( ${MBGL_ROOT}/include/mbgl/style/conversion_impl.hpp ${MBGL_ROOT}/include/mbgl/style/expression/assertion.hpp ${MBGL_ROOT}/include/mbgl/style/expression/at.hpp + ${MBGL_ROOT}/include/mbgl/style/expression/in.hpp ${MBGL_ROOT}/include/mbgl/style/expression/boolean_operator.hpp ${MBGL_ROOT}/include/mbgl/style/expression/case.hpp ${MBGL_ROOT}/include/mbgl/style/expression/check_subtype.hpp @@ -630,6 +631,7 @@ add_library( ${MBGL_ROOT}/src/mbgl/style/custom_tile_loader.hpp ${MBGL_ROOT}/src/mbgl/style/expression/assertion.cpp ${MBGL_ROOT}/src/mbgl/style/expression/at.cpp + ${MBGL_ROOT}/src/mbgl/style/expression/in.cpp ${MBGL_ROOT}/src/mbgl/style/expression/boolean_operator.cpp ${MBGL_ROOT}/src/mbgl/style/expression/case.cpp ${MBGL_ROOT}/src/mbgl/style/expression/check_subtype.cpp diff --git a/src/core-files.json b/src/core-files.json index fcc508fb3a1..d391db32152 100644 --- a/src/core-files.json +++ b/src/core-files.json @@ -195,6 +195,7 @@ "src/mbgl/style/expression/get_covering_stops.cpp", "src/mbgl/style/expression/image.cpp", "src/mbgl/style/expression/image_expression.cpp", + "src/mbgl/style/expression/in.cpp", "src/mbgl/style/expression/interpolate.cpp", "src/mbgl/style/expression/is_constant.cpp", "src/mbgl/style/expression/is_expression.cpp", @@ -424,6 +425,7 @@ "mbgl/style/expression/get_covering_stops.hpp": "include/mbgl/style/expression/get_covering_stops.hpp", "mbgl/style/expression/image.hpp": "include/mbgl/style/expression/image.hpp", "mbgl/style/expression/image_expression.hpp": "include/mbgl/style/expression/image_expression.hpp", + "mbgl/style/expression/in.hpp": "include/mbgl/style/expression/in.hpp", "mbgl/style/expression/interpolate.hpp": "include/mbgl/style/expression/interpolate.hpp", "mbgl/style/expression/interpolator.hpp": "include/mbgl/style/expression/interpolator.hpp", "mbgl/style/expression/is_constant.hpp": "include/mbgl/style/expression/is_constant.hpp", diff --git a/src/mbgl/style/expression/in.cpp b/src/mbgl/style/expression/in.cpp new file mode 100644 index 00000000000..822dbef973a --- /dev/null +++ b/src/mbgl/style/expression/in.cpp @@ -0,0 +1,116 @@ +#include +#include +#include +#include +#include + +namespace mbgl { +namespace style { +namespace expression { + +EvaluationResult In::evaluate(const EvaluationContext& params) const { + const EvaluationResult evaluatedNeedle = needle->evaluate(params); + const EvaluationResult evaluatedHeystack = haystack->evaluate(params); + + if (!evaluatedNeedle) { + return evaluatedNeedle.error(); + } + if (!evaluatedHeystack) { + return evaluatedHeystack.error(); + } + + type::Type evaluatedNeedleType = typeOf(*evaluatedNeedle); + if (!isComparableRuntimeValue(evaluatedNeedleType)) { + return EvaluationError{"Expected first argument to be of type boolean, string or number, but found " + + toString(evaluatedNeedleType) + " instead."}; + } + + type::Type evaluatedHeystackType = typeOf(*evaluatedHeystack); + if (!isSearchableRuntimeValue(evaluatedHeystackType)) { + return EvaluationError{"Expected second argument to be of type array or string, but found " + + toString(evaluatedHeystackType) + " instead."}; + } + + if (evaluatedNeedleType == type::Null || evaluatedHeystackType == type::Null) { + return EvaluationResult(false); + } + + if (evaluatedHeystackType == type::String) { + const auto heystackString = evaluatedHeystack->get(); + std::string needleValue = ""; + if (evaluatedNeedleType == type::Boolean) { + needleValue = evaluatedNeedle->get() ? "true" : " false"; + } else if(evaluatedNeedleType == type::String) { + needleValue = evaluatedNeedle->get(); + } else if(evaluatedNeedleType == type::Number) { + needleValue = std::to_string(evaluatedNeedle->get()); + needleValue.erase ( needleValue.find_last_not_of('0') + 1, std::string::npos ); + needleValue.erase ( needleValue.find_last_not_of('.') + 1, std::string::npos ); + } + return EvaluationResult(heystackString.find(needleValue) != std::string::npos); + } else { + const auto heystackArray = evaluatedHeystack->get>(); + + bool result = false; + if(evaluatedNeedleType == type::Boolean) { + auto needleValue = evaluatedNeedle->get(); + result = find(heystackArray.begin(), heystackArray.end(), needleValue) != heystackArray.end(); + } else if(evaluatedNeedleType == type::String) { + auto needleValue = evaluatedNeedle->get(); + result = find(heystackArray.begin(), heystackArray.end(), needleValue) != heystackArray.end(); + } else if(evaluatedNeedleType == type::Number) { + auto needleValue = evaluatedNeedle->get(); + result = find(heystackArray.begin(), heystackArray.end(), needleValue) != heystackArray.end(); + } + return EvaluationResult(result); + } +} + +void In::eachChild(const std::function& visit) const { + visit(*needle); + visit(*haystack); +} + +using namespace mbgl::style::conversion; +ParseResult In::parse(const Convertible& value, ParsingContext& ctx) { + assert(isArray(value)); + + std::size_t length = arrayLength(value); + if (length != 3) { + ctx.error("Expected 2 arguments, but found " + util::toString(length - 1) + " instead."); + return ParseResult(); + } + + ParseResult needle = ctx.parse(arrayMember(value, 1), 1, {type::Value}); + ParseResult haystack = ctx.parse(arrayMember(value, 2), 2, {type::Value}); + + if (!needle || !haystack) return ParseResult(); + + type::Type needleType = (*needle)->getType();; + type::Type haystackType = (*haystack)->getType();; + + if (!isComparableType(needleType)) { + ctx.error("Expected first argument to be of type boolean, string or number, but found " + + toString(needleType) + " instead."); + return ParseResult(); + } + + return ParseResult(std::make_unique(std::move(*needle), std::move(*haystack))); +} + +bool In::isComparableType(type::Type type) { + return type == type::Boolean || type == type::String || type == type::Number || type == type::Null || + type == type::Value; +} + +bool In::isComparableRuntimeValue(type::Type type) { + return type == type::Boolean || type == type::String || type == type::Number || type == type::Null; +} + +bool In::isSearchableRuntimeValue(type::Type type) { + return type == type::String || type.is() || type == type::Null; +} + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/expression/parsing_context.cpp b/src/mbgl/style/expression/parsing_context.cpp index ae991b2e880..da7cf1ae7a1 100644 --- a/src/mbgl/style/expression/parsing_context.cpp +++ b/src/mbgl/style/expression/parsing_context.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -113,6 +114,7 @@ MAPBOX_ETERNAL_CONSTEXPR const auto expressionRegistry = {"any", Any::parse}, {"array", Assertion::parse}, {"at", At::parse}, + {"in", In::parse}, {"boolean", Assertion::parse}, {"case", Case::parse}, {"coalesce", Coalesce::parse}, diff --git a/test/style/expression/expression.test.cpp b/test/style/expression/expression.test.cpp index bffadf1668a..b137df769a5 100644 --- a/test/style/expression/expression.test.cpp +++ b/test/style/expression/expression.test.cpp @@ -35,8 +35,7 @@ TEST(Expression, IsExpression) { // TODO: "interpolate-hcl": https://github.com/mapbox/mapbox-gl-native/issues/8720 // TODO: "interpolate-lab": https://github.com/mapbox/mapbox-gl-native/issues/8720 - // TODO: "in": https://github.com/mapbox/mapbox-gl-native/issues/15893 - if (name == "interpolate-hcl" || name == "interpolate-lab" || name == "in") { + if (name == "interpolate-hcl" || name == "interpolate-lab") { if (expression::isExpression(conversion::Convertible(expression))) { ASSERT_TRUE(false) << "Expression name" << name << "is implemented - please update Expression.IsExpression test."; }