Implement slice expression (#1133)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Bart Louwers <bart@emeel.net>
This commit is contained in:
committed by
GitHub
parent
ecd59d4cc4
commit
09f17c0fc4
@@ -241,6 +241,7 @@ list(APPEND INCLUDE_FILES
|
|||||||
${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/match.hpp
|
${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/match.hpp
|
||||||
${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/number_format.hpp
|
${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/number_format.hpp
|
||||||
${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/parsing_context.hpp
|
${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/parsing_context.hpp
|
||||||
|
${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/slice.hpp
|
||||||
${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/step.hpp
|
${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/step.hpp
|
||||||
${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/type.hpp
|
${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/type.hpp
|
||||||
${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/value.hpp
|
${PROJECT_SOURCE_DIR}/include/mbgl/style/expression/value.hpp
|
||||||
@@ -641,6 +642,7 @@ list(APPEND SRC_FILES
|
|||||||
${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/match.cpp
|
${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/match.cpp
|
||||||
${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/number_format.cpp
|
${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/number_format.cpp
|
||||||
${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/parsing_context.cpp
|
${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/parsing_context.cpp
|
||||||
|
${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/slice.cpp
|
||||||
${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/step.cpp
|
${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/step.cpp
|
||||||
${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/util.cpp
|
${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/util.cpp
|
||||||
${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/util.hpp
|
${PROJECT_SOURCE_DIR}/src/mbgl/style/expression/util.hpp
|
||||||
|
|||||||
@@ -398,6 +398,7 @@ MLN_CORE_SOURCE = [
|
|||||||
"src/mbgl/style/expression/match.cpp",
|
"src/mbgl/style/expression/match.cpp",
|
||||||
"src/mbgl/style/expression/number_format.cpp",
|
"src/mbgl/style/expression/number_format.cpp",
|
||||||
"src/mbgl/style/expression/parsing_context.cpp",
|
"src/mbgl/style/expression/parsing_context.cpp",
|
||||||
|
"src/mbgl/style/expression/slice.cpp",
|
||||||
"src/mbgl/style/expression/step.cpp",
|
"src/mbgl/style/expression/step.cpp",
|
||||||
"src/mbgl/style/expression/util.cpp",
|
"src/mbgl/style/expression/util.cpp",
|
||||||
"src/mbgl/style/expression/util.hpp",
|
"src/mbgl/style/expression/util.hpp",
|
||||||
@@ -719,6 +720,7 @@ MLN_CORE_HEADERS = [
|
|||||||
"include/mbgl/style/expression/match.hpp",
|
"include/mbgl/style/expression/match.hpp",
|
||||||
"include/mbgl/style/expression/number_format.hpp",
|
"include/mbgl/style/expression/number_format.hpp",
|
||||||
"include/mbgl/style/expression/parsing_context.hpp",
|
"include/mbgl/style/expression/parsing_context.hpp",
|
||||||
|
"include/mbgl/style/expression/slice.hpp",
|
||||||
"include/mbgl/style/expression/step.hpp",
|
"include/mbgl/style/expression/step.hpp",
|
||||||
"include/mbgl/style/expression/type.hpp",
|
"include/mbgl/style/expression/type.hpp",
|
||||||
"include/mbgl/style/expression/value.hpp",
|
"include/mbgl/style/expression/value.hpp",
|
||||||
|
|||||||
@@ -179,7 +179,8 @@ enum class Kind : int32_t {
|
|||||||
In,
|
In,
|
||||||
Within,
|
Within,
|
||||||
Distance,
|
Distance,
|
||||||
IndexOf
|
IndexOf,
|
||||||
|
Slice
|
||||||
};
|
};
|
||||||
|
|
||||||
class Expression {
|
class Expression {
|
||||||
|
|||||||
39
include/mbgl/style/expression/slice.hpp
Normal file
39
include/mbgl/style/expression/slice.hpp
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <mbgl/style/expression/expression.hpp>
|
||||||
|
#include <mbgl/style/conversion.hpp>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace mbgl {
|
||||||
|
namespace style {
|
||||||
|
namespace expression {
|
||||||
|
|
||||||
|
class Slice : public Expression {
|
||||||
|
public:
|
||||||
|
Slice(std::unique_ptr<Expression> input_,
|
||||||
|
std::unique_ptr<Expression> fromIndex_,
|
||||||
|
std::unique_ptr<Expression> toIndex_);
|
||||||
|
|
||||||
|
static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx);
|
||||||
|
|
||||||
|
EvaluationResult evaluate(const EvaluationContext& params) const override;
|
||||||
|
void eachChild(const std::function<void(const Expression&)>&) const override;
|
||||||
|
|
||||||
|
bool operator==(const Expression& e) const override;
|
||||||
|
|
||||||
|
std::vector<std::optional<Value>> possibleOutputs() const override;
|
||||||
|
std::string getOperator() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
EvaluationResult evaluateForStringInput(const std::string& input, int fromIndexValue, int toIndexValue) const;
|
||||||
|
EvaluationResult evaluateForArrayInput(const std::vector<Value>& input, int fromIndexValue, int toIndexValue) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<Expression> input;
|
||||||
|
std::unique_ptr<Expression> fromIndex;
|
||||||
|
std::unique_ptr<Expression> toIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace expression
|
||||||
|
} // namespace style
|
||||||
|
} // namespace mbgl
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"expression": ["slice", ["array", ["get", "val"]], ["get", "index"]],
|
||||||
|
"inputs": [
|
||||||
|
[{}, {"properties": {"val": [1, 2, 3, 4, 5], "index": 2}}],
|
||||||
|
[{}, {"properties": {"val": [1, 2, 3, 4, 5], "index": 0}}],
|
||||||
|
[{}, {"properties": {"val": [1, 2, 3, 4, 5], "index": 99}}],
|
||||||
|
[{}, {"properties": {"val": [1, 2, 3, 4, 5], "index": -2}}]
|
||||||
|
],
|
||||||
|
"expected": {
|
||||||
|
"serialized": ["slice", ["array", ["get", "val"]], ["get", "index"]],
|
||||||
|
"compiled": {
|
||||||
|
"result": "success",
|
||||||
|
"isFeatureConstant": false,
|
||||||
|
"isZoomConstant": true,
|
||||||
|
"type": "array"
|
||||||
|
},
|
||||||
|
"outputs": [[3, 4, 5], [1, 2, 3, 4, 5], [], [4, 5]]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"expression": [
|
||||||
|
"slice",
|
||||||
|
["array", ["get", "val"]],
|
||||||
|
["get", "i1"],
|
||||||
|
["get", "i2"]
|
||||||
|
],
|
||||||
|
"inputs": [
|
||||||
|
[{}, {"properties": {"val": [1, 2, 3, 4, 5], "i1": 2, "i2": 4}}],
|
||||||
|
[{}, {"properties": {"val": [1, 2, 3, 4, 5], "i1": 1, "i2": 5}}],
|
||||||
|
[{}, {"properties": {"val": [1, 2, 3, 4, 5], "i1": 1, "i2": 99}}],
|
||||||
|
[{}, {"properties": {"val": [1, 2, 3, 4, 5], "i1": -4, "i2": -2}}],
|
||||||
|
[{}, {"properties": {"val": [1, 2, 3, 4, 5], "i1": 0, "i2": -1}}],
|
||||||
|
[{}, {"properties": {"val": [1, 2, 3, 4, 5], "i1": 0, "i2": 0}}]
|
||||||
|
],
|
||||||
|
"expected": {
|
||||||
|
"serialized": [
|
||||||
|
"slice",
|
||||||
|
["array", ["get", "val"]],
|
||||||
|
["get", "i1"],
|
||||||
|
["get", "i2"]
|
||||||
|
],
|
||||||
|
"compiled": {
|
||||||
|
"result": "success",
|
||||||
|
"isFeatureConstant": false,
|
||||||
|
"isZoomConstant": true,
|
||||||
|
"type": "array"
|
||||||
|
},
|
||||||
|
"outputs": [[3, 4], [2, 3, 4, 5], [2, 3, 4, 5], [2, 3], [1, 2, 3, 4], []]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
"expression": ["slice", ["get", "input"], ["get", "i1"]],
|
||||||
|
"inputs": [
|
||||||
|
[{}, {"properties": {"input": false, "i1": 1}}],
|
||||||
|
[{}, {"properties": {"input": null, "i1": 1}}],
|
||||||
|
[{}, {"properties": {"input": 12, "i1": 1}}],
|
||||||
|
[{}, {"properties": {"input": {}, "i1": 1}}],
|
||||||
|
[{}, {"properties": {"other": 1, "i1": 1}}],
|
||||||
|
[{}, {"properties": {"input": "correct", "i1": "one"}}]
|
||||||
|
],
|
||||||
|
"expected": {
|
||||||
|
"serialized": ["slice", ["get", "input"], ["get", "i1"]],
|
||||||
|
"compiled": {
|
||||||
|
"result": "success",
|
||||||
|
"isFeatureConstant": false,
|
||||||
|
"isZoomConstant": true,
|
||||||
|
"type": "value"
|
||||||
|
},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"error": "Expected first argument to be of type array or string, but found boolean instead."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"error": "Expected first argument to be of type array or string, but found null instead."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"error": "Expected first argument to be of type array or string, but found number instead."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"error": "Expected first argument to be of type array or string, but found object instead."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"error": "Expected first argument to be of type array or string, but found null instead."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"error": "Expected value to be of type number, but found string instead."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"expression": ["slice", ["get", "val"], ["get", "index"]],
|
||||||
|
"inputs": [
|
||||||
|
[{}, {"properties": {"val": "0123456789", "index": 0}}],
|
||||||
|
[{}, {"properties": {"val": "0123456789", "index": 4}}],
|
||||||
|
[{}, {"properties": {"val": "0123456789", "index": 99}}],
|
||||||
|
[{}, {"properties": {"val": "0123456789", "index": -2}}]
|
||||||
|
],
|
||||||
|
"expected": {
|
||||||
|
"serialized": ["slice", ["get", "val"], ["get", "index"]],
|
||||||
|
"compiled": {
|
||||||
|
"result": "success",
|
||||||
|
"isFeatureConstant": false,
|
||||||
|
"isZoomConstant": true,
|
||||||
|
"type": "value"
|
||||||
|
},
|
||||||
|
"outputs": ["0123456789", "456789", "", "89"]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"expression": ["slice", ["get", "val"], ["get", "i1"], ["get", "i2"]],
|
||||||
|
"inputs": [
|
||||||
|
[{}, {"properties": {"val": "0123456789", "i1": 1, "i2": 8}}],
|
||||||
|
[{}, {"properties": {"val": "0123456789", "i1": 4, "i2": -2}}],
|
||||||
|
[{}, {"properties": {"val": "0123456789", "i1": -3, "i2": -1}}],
|
||||||
|
[{}, {"properties": {"val": "0123456789", "i1": 0, "i2": -1}}],
|
||||||
|
[{}, {"properties": {"val": "0123456789", "i1": 0, "i2": 99}}]
|
||||||
|
],
|
||||||
|
"expected": {
|
||||||
|
"serialized": ["slice", ["get", "val"], ["get", "i1"], ["get", "i2"]],
|
||||||
|
"compiled": {
|
||||||
|
"result": "success",
|
||||||
|
"isFeatureConstant": false,
|
||||||
|
"isZoomConstant": true,
|
||||||
|
"type": "value"
|
||||||
|
},
|
||||||
|
"outputs": ["1234567", "4567", "78", "012345678", "0123456789"]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ MapLibre welcomes participation and contributions from everyone. Please read [`C
|
|||||||
## main
|
## main
|
||||||
|
|
||||||
### ✨ Features and improvements
|
### ✨ Features and improvements
|
||||||
|
* Add support for the [`slice` expression](https://maplibre.org/maplibre-style-spec/expressions/#slice) ([#1113](https://github.com/maplibre/maplibre-native/pull/1133))
|
||||||
|
|
||||||
- 💥 Breaking: Change package of all classes from `com.mapbox.mapboxsdk` to `org.maplibre.android` ([#1201](https://github.com/maplibre/maplibre-native/pull/1201)). This means you will need to fix your imports.
|
- 💥 Breaking: Change package of all classes from `com.mapbox.mapboxsdk` to `org.maplibre.android` ([#1201](https://github.com/maplibre/maplibre-native/pull/1201)). This means you will need to fix your imports.
|
||||||
|
|
||||||
|
|||||||
@@ -1565,7 +1565,7 @@ public class Expression {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the first position at which a `needle` can be found in a `haystack`.
|
* Returns the first position at which a `needle` can be found in a `haystack`.
|
||||||
*
|
*
|
||||||
* @param needle the item expression
|
* @param needle the item expression
|
||||||
* @param haystack the array or string expression
|
* @param haystack the array or string expression
|
||||||
@@ -1577,6 +1577,33 @@ public class Expression {
|
|||||||
return new Expression("index-of", keyword, input, fromIndex);
|
return new Expression("index-of", keyword, input, fromIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns items from an array or a substring from a string from a specified start index.
|
||||||
|
* The return value is inclusive of the start index.
|
||||||
|
*
|
||||||
|
* @param input the array or string expression
|
||||||
|
* @param fromIndex the index to start slice from
|
||||||
|
* @return array or string
|
||||||
|
* @see <a href="https://maplibre.org/maplibre-style-spec/expressions/#slice">Style specification</a>
|
||||||
|
*/
|
||||||
|
public static Expression slice(@NonNull Expression input, @NonNull Expression fromIndex) {
|
||||||
|
return new Expression("slice", input, fromIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns items from an array or a substring from a string between a start index and an end index if set.
|
||||||
|
* The return value is inclusive of the start index, but not of the end index.
|
||||||
|
*
|
||||||
|
* @param input the array or string expression
|
||||||
|
* @param fromIndex the index to start slice from
|
||||||
|
* @param toIndex the index to end slice at
|
||||||
|
* @return array or string
|
||||||
|
* @see <a href="https://maplibre.org/maplibre-style-spec/expressions/#slice">Style specification</a>
|
||||||
|
*/
|
||||||
|
public static Expression slice(@NonNull Expression input, @NonNull Expression fromIndex, @NonNull Expression toIndex) {
|
||||||
|
return new Expression("slice", input, fromIndex, toIndex);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves whether an item exists in an array or a substring exists in a string.
|
* Retrieves whether an item exists in an array or a substring exists in a string.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -477,6 +477,26 @@ class ExpressionTest {
|
|||||||
Assert.assertTrue("expression should match", Arrays.deepEquals(expected, actual))
|
Assert.assertTrue("expression should match", Arrays.deepEquals(expected, actual))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun testSlice() {
|
||||||
|
val expected = arrayOf<Any>("slice", arrayOf<Any>("literal", arrayOf<Any>(1f, 2f, 3f)), 1f)
|
||||||
|
val actual =
|
||||||
|
Expression.slice(Expression.literal(arrayOf<Any>(1f, 2f, 3f)), Expression.literal(1f))
|
||||||
|
.toArray()
|
||||||
|
Assert.assertTrue("expression should match", Arrays.deepEquals(expected, actual))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun testSliceWithToIndex() {
|
||||||
|
val expected = arrayOf<Any>("slice", arrayOf<Any>("literal", arrayOf<Any>(1f, 2f, 3f)), 1f, 3f)
|
||||||
|
val actual =
|
||||||
|
Expression.slice(Expression.literal(arrayOf<Any>(1f, 2f, 3f)), Expression.literal(1f), Expression.literal(3f))
|
||||||
|
.toArray()
|
||||||
|
Assert.assertTrue("expression should match", Arrays.deepEquals(expected, actual))
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
fun testInNumber() {
|
fun testInNumber() {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
#include <mbgl/style/conversion_impl.hpp>
|
#include <mbgl/style/conversion_impl.hpp>
|
||||||
#include <mbgl/style/expression/index_of.hpp>
|
#include <mbgl/style/expression/index_of.hpp>
|
||||||
#include <mbgl/util/string.hpp>
|
#include <mbgl/util/string.hpp>
|
||||||
#include <iostream>
|
|
||||||
namespace mbgl {
|
namespace mbgl {
|
||||||
namespace style {
|
namespace style {
|
||||||
namespace expression {
|
namespace expression {
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
#include <mbgl/style/expression/step.hpp>
|
#include <mbgl/style/expression/step.hpp>
|
||||||
#include <mbgl/style/expression/within.hpp>
|
#include <mbgl/style/expression/within.hpp>
|
||||||
#include <mbgl/style/expression/index_of.hpp>
|
#include <mbgl/style/expression/index_of.hpp>
|
||||||
|
#include <mbgl/style/expression/slice.hpp>
|
||||||
|
|
||||||
#include <mbgl/style/expression/find_zoom_curve.hpp>
|
#include <mbgl/style/expression/find_zoom_curve.hpp>
|
||||||
#include <mbgl/style/expression/dsl.hpp>
|
#include <mbgl/style/expression/dsl.hpp>
|
||||||
@@ -105,43 +106,42 @@ ParseResult ParsingContext::parse(const Convertible& value,
|
|||||||
|
|
||||||
using ParseFunction = ParseResult (*)(const conversion::Convertible&, ParsingContext&);
|
using ParseFunction = ParseResult (*)(const conversion::Convertible&, ParsingContext&);
|
||||||
MAPBOX_ETERNAL_CONSTEXPR const auto expressionRegistry =
|
MAPBOX_ETERNAL_CONSTEXPR const auto expressionRegistry =
|
||||||
mapbox::eternal::hash_map<mapbox::eternal::string, ParseFunction>({
|
mapbox::eternal::hash_map<mapbox::eternal::string, ParseFunction>({{"==", parseComparison},
|
||||||
{"==", parseComparison},
|
{"!=", parseComparison},
|
||||||
{"!=", parseComparison},
|
{">", parseComparison},
|
||||||
{">", parseComparison},
|
{"<", parseComparison},
|
||||||
{"<", parseComparison},
|
{">=", parseComparison},
|
||||||
{">=", parseComparison},
|
{"<=", parseComparison},
|
||||||
{"<=", parseComparison},
|
{"all", All::parse},
|
||||||
{"all", All::parse},
|
{"any", Any::parse},
|
||||||
{"any", Any::parse},
|
{"array", Assertion::parse},
|
||||||
{"array", Assertion::parse},
|
{"at", At::parse},
|
||||||
{"at", At::parse},
|
{"in", In::parse},
|
||||||
{"in", In::parse},
|
{"boolean", Assertion::parse},
|
||||||
{"boolean", Assertion::parse},
|
{"case", Case::parse},
|
||||||
{"case", Case::parse},
|
{"coalesce", Coalesce::parse},
|
||||||
{"coalesce", Coalesce::parse},
|
{"collator", CollatorExpression::parse},
|
||||||
{"collator", CollatorExpression::parse},
|
{"distance", Distance::parse},
|
||||||
{"distance", Distance::parse},
|
{"format", FormatExpression::parse},
|
||||||
{"format", FormatExpression::parse},
|
{"image", ImageExpression::parse},
|
||||||
{"image", ImageExpression::parse},
|
{"interpolate", parseInterpolate},
|
||||||
{"interpolate", parseInterpolate},
|
{"length", Length::parse},
|
||||||
{"length", Length::parse},
|
{"let", Let::parse},
|
||||||
{"let", Let::parse},
|
{"literal", Literal::parse},
|
||||||
{"literal", Literal::parse},
|
{"match", parseMatch},
|
||||||
{"match", parseMatch},
|
{"number", Assertion::parse},
|
||||||
{"number", Assertion::parse},
|
{"number-format", NumberFormat::parse},
|
||||||
{"number-format", NumberFormat::parse},
|
{"object", Assertion::parse},
|
||||||
{"object", Assertion::parse},
|
{"step", Step::parse},
|
||||||
{"step", Step::parse},
|
{"string", Assertion::parse},
|
||||||
{"string", Assertion::parse},
|
{"to-boolean", Coercion::parse},
|
||||||
{"to-boolean", Coercion::parse},
|
{"to-color", Coercion::parse},
|
||||||
{"to-color", Coercion::parse},
|
{"to-number", Coercion::parse},
|
||||||
{"to-number", Coercion::parse},
|
{"to-string", Coercion::parse},
|
||||||
{"to-string", Coercion::parse},
|
{"var", Var::parse},
|
||||||
{"var", Var::parse},
|
{"within", Within::parse},
|
||||||
{"within", Within::parse},
|
{"index-of", IndexOf::parse},
|
||||||
{"index-of", IndexOf::parse},
|
{"slice", Slice::parse}});
|
||||||
});
|
|
||||||
|
|
||||||
bool isExpression(const std::string& name) {
|
bool isExpression(const std::string& name) {
|
||||||
return expressionRegistry.contains(name.c_str());
|
return expressionRegistry.contains(name.c_str());
|
||||||
|
|||||||
156
src/mbgl/style/expression/slice.cpp
Normal file
156
src/mbgl/style/expression/slice.cpp
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
#include "mbgl/style/expression/expression.hpp"
|
||||||
|
#include <limits>
|
||||||
|
#include <mbgl/style/conversion_impl.hpp>
|
||||||
|
#include <mbgl/style/expression/slice.hpp>
|
||||||
|
#include <mbgl/util/string.hpp>
|
||||||
|
|
||||||
|
namespace mbgl {
|
||||||
|
namespace style {
|
||||||
|
namespace expression {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
int normalizeIndex(int index, int length) {
|
||||||
|
if (index < 0) {
|
||||||
|
index += length;
|
||||||
|
}
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
Slice::Slice(std::unique_ptr<Expression> input_,
|
||||||
|
std::unique_ptr<Expression> fromIndex_,
|
||||||
|
std::unique_ptr<Expression> toIndex_)
|
||||||
|
: Expression(Kind::Slice, input_->getType()),
|
||||||
|
input(std::move(input_)),
|
||||||
|
fromIndex(std::move(fromIndex_)),
|
||||||
|
toIndex(std::move(toIndex_)) {}
|
||||||
|
|
||||||
|
EvaluationResult Slice::evaluate(const EvaluationContext ¶ms) const {
|
||||||
|
const EvaluationResult evaluatedInput = input->evaluate(params);
|
||||||
|
if (!evaluatedInput) {
|
||||||
|
return evaluatedInput.error();
|
||||||
|
}
|
||||||
|
const EvaluationResult evaluatedFromIndex = fromIndex->evaluate(params);
|
||||||
|
if (!fromIndex) {
|
||||||
|
return evaluatedFromIndex.error();
|
||||||
|
}
|
||||||
|
if (!evaluatedFromIndex->is<double>()) {
|
||||||
|
return EvaluationError{"Expected value to be of type number, but found " +
|
||||||
|
toString(typeOf(*evaluatedFromIndex)) + " instead."};
|
||||||
|
}
|
||||||
|
|
||||||
|
int fromIndexValue = static_cast<int>(evaluatedFromIndex->get<double>());
|
||||||
|
int toIndexValue = std::numeric_limits<int>::max();
|
||||||
|
|
||||||
|
if (toIndex) {
|
||||||
|
const EvaluationResult evaluatedToIndex = toIndex->evaluate(params);
|
||||||
|
if (!evaluatedToIndex) {
|
||||||
|
return evaluatedToIndex.error();
|
||||||
|
}
|
||||||
|
if (!evaluatedToIndex->is<double>()) {
|
||||||
|
return EvaluationError{"Expected value to be of type number, but found " +
|
||||||
|
toString(typeOf(*evaluatedFromIndex)) + " instead."};
|
||||||
|
}
|
||||||
|
toIndexValue = static_cast<int>(evaluatedToIndex->get<double>());
|
||||||
|
}
|
||||||
|
|
||||||
|
return evaluatedInput->match(
|
||||||
|
[&](const std::string &s) { return evaluateForStringInput(s, fromIndexValue, toIndexValue); },
|
||||||
|
[&](const std::vector<Value> &v) { return evaluateForArrayInput(v, fromIndexValue, toIndexValue); },
|
||||||
|
[&](const auto &) -> EvaluationResult {
|
||||||
|
return EvaluationError{"Expected first argument to be of type array or string, but found " +
|
||||||
|
toString(typeOf(*evaluatedInput)) + " instead."};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
EvaluationResult Slice::evaluateForArrayInput(const std::vector<Value> &array,
|
||||||
|
int fromIndexValue,
|
||||||
|
int toIndexValue) const {
|
||||||
|
int length = static_cast<int>(array.size());
|
||||||
|
if (toIndexValue == std::numeric_limits<int>::max()) {
|
||||||
|
toIndexValue = length;
|
||||||
|
}
|
||||||
|
fromIndexValue = normalizeIndex(fromIndexValue, length);
|
||||||
|
toIndexValue = normalizeIndex(toIndexValue, length);
|
||||||
|
|
||||||
|
if (fromIndexValue >= toIndexValue) {
|
||||||
|
return std::vector<Value>{};
|
||||||
|
}
|
||||||
|
toIndexValue = std::min(toIndexValue, length);
|
||||||
|
|
||||||
|
std::vector<Value> result;
|
||||||
|
result.reserve(toIndexValue - fromIndexValue);
|
||||||
|
for (int index = fromIndexValue; index < toIndexValue; ++index) {
|
||||||
|
result.push_back(array[index]);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
EvaluationResult Slice::evaluateForStringInput(const std::string &string, int fromIndexValue, int toIndexValue) const {
|
||||||
|
int length = static_cast<int>(string.size());
|
||||||
|
if (toIndexValue == std::numeric_limits<int>::max()) {
|
||||||
|
toIndexValue = length;
|
||||||
|
}
|
||||||
|
fromIndexValue = normalizeIndex(fromIndexValue, length);
|
||||||
|
toIndexValue = normalizeIndex(toIndexValue, length);
|
||||||
|
if (fromIndexValue >= length) {
|
||||||
|
return std::string{};
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.substr(fromIndexValue, toIndexValue - fromIndexValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Slice::eachChild(const std::function<void(const Expression &)> &visit) const {
|
||||||
|
visit(*input);
|
||||||
|
visit(*fromIndex);
|
||||||
|
if (toIndex) {
|
||||||
|
visit(*toIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
using namespace mbgl::style::conversion;
|
||||||
|
ParseResult Slice::parse(const Convertible &value, ParsingContext &ctx) {
|
||||||
|
assert(isArray(value));
|
||||||
|
|
||||||
|
std::size_t length = arrayLength(value);
|
||||||
|
if (length != 3 && length != 4) {
|
||||||
|
ctx.error("Expected 2 or 3 arguments, but found " + util::toString(length - 1) + " instead.");
|
||||||
|
return ParseResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
ParseResult input = ctx.parse(arrayMember(value, 1), 1);
|
||||||
|
ParseResult fromIndex = ctx.parse(arrayMember(value, 2), 2);
|
||||||
|
|
||||||
|
ParseResult toIndex = length == 4 ? ctx.parse(arrayMember(value, 3), 3) : ParseResult();
|
||||||
|
|
||||||
|
if (!input || !fromIndex) {
|
||||||
|
return ParseResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ParseResult(
|
||||||
|
std::make_unique<Slice>(std::move(*input), std::move(*fromIndex), toIndex ? std::move(*toIndex) : nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Slice::operator==(const Expression &e) const {
|
||||||
|
if (e.getKind() == Kind::Slice) {
|
||||||
|
auto rhs = static_cast<const Slice *>(&e);
|
||||||
|
const auto toIndexEqual = (toIndex && rhs->toIndex && *toIndex == *(rhs->toIndex)) ||
|
||||||
|
(!toIndex && !rhs->toIndex);
|
||||||
|
return *input == *(rhs->input) && *fromIndex == *(rhs->fromIndex) && toIndexEqual;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::optional<Value>> Slice::possibleOutputs() const {
|
||||||
|
return {std::nullopt};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Slice::getOperator() const {
|
||||||
|
return "slice";
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace expression
|
||||||
|
} // namespace style
|
||||||
|
} // namespace mbgl
|
||||||
Reference in New Issue
Block a user