summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnand Thakker <github@anandthakker.net>2017-08-15 17:30:39 -0400
committerAnand Thakker <github@anandthakker.net>2017-10-13 12:50:50 -0400
commit6ae0a8a819dde031eaddf7bf85a34e50f196780c (patch)
treecfc4d19e148d31a6cb4ac99c7bd616311f7a58e9
parentd26c30152a4fbe7510d64430504b85d8b5ab4a1d (diff)
downloadqtlocation-mapboxgl-6ae0a8a819dde031eaddf7bf85a34e50f196780c.tar.gz
Wire up expression parsing
Also extract *Expression::parse into separate headers to avoid dependency on rapidjson conversion methods
-rw-r--r--cmake/core-files.cmake10
-rw-r--r--include/mbgl/style/conversion/data_driven_property_value.hpp14
-rw-r--r--include/mbgl/style/conversion/expression.hpp19
-rw-r--r--include/mbgl/style/conversion/property_value.hpp13
-rw-r--r--include/mbgl/style/expression/curve.hpp192
-rw-r--r--include/mbgl/style/expression/literal.hpp72
-rw-r--r--include/mbgl/style/expression/match.hpp187
-rw-r--r--include/mbgl/style/expression/parse.hpp26
-rw-r--r--include/mbgl/style/expression/parse/array_assertion.hpp73
-rw-r--r--include/mbgl/style/expression/parse/case.hpp66
-rw-r--r--include/mbgl/style/expression/parse/coalesce.hpp45
-rw-r--r--include/mbgl/style/expression/parse/compound_expression.hpp50
-rw-r--r--include/mbgl/style/expression/parse/curve.hpp207
-rw-r--r--include/mbgl/style/expression/parse/let.hpp0
-rw-r--r--include/mbgl/style/expression/parse/literal.hpp104
-rw-r--r--include/mbgl/style/expression/parse/match.hpp203
-rw-r--r--include/mbgl/style/function/camera_function.hpp7
-rw-r--r--include/mbgl/style/function/composite_function.hpp12
-rw-r--r--include/mbgl/style/function/source_function.hpp7
-rw-r--r--src/mbgl/style/expression/value.cpp37
20 files changed, 863 insertions, 481 deletions
diff --git a/cmake/core-files.cmake b/cmake/core-files.cmake
index 9b66fce69c..c4a98f3ddc 100644
--- a/cmake/core-files.cmake
+++ b/cmake/core-files.cmake
@@ -415,6 +415,16 @@ set(MBGL_CORE_FILES
src/mbgl/style/expression/match.cpp
src/mbgl/style/expression/value.cpp
+ # style/expression/parse
+ include/mbgl/style/expression/parse/array_assertion.hpp
+ include/mbgl/style/expression/parse/case.hpp
+ include/mbgl/style/expression/parse/coalesce.hpp
+ include/mbgl/style/expression/parse/compound_expression.hpp
+ include/mbgl/style/expression/parse/curve.hpp
+ include/mbgl/style/expression/parse/let.hpp
+ include/mbgl/style/expression/parse/literal.hpp
+ include/mbgl/style/expression/parse/match.hpp
+
# style/function
include/mbgl/style/function/camera_function.hpp
include/mbgl/style/function/categorical_stops.hpp
diff --git a/include/mbgl/style/conversion/data_driven_property_value.hpp b/include/mbgl/style/conversion/data_driven_property_value.hpp
index b7e3bef172..7b99935565 100644
--- a/include/mbgl/style/conversion/data_driven_property_value.hpp
+++ b/include/mbgl/style/conversion/data_driven_property_value.hpp
@@ -4,6 +4,8 @@
#include <mbgl/style/conversion.hpp>
#include <mbgl/style/conversion/constant.hpp>
#include <mbgl/style/conversion/function.hpp>
+#include <mbgl/style/conversion/expression.hpp>
+#include <mbgl/style/expression/value.hpp>
namespace mbgl {
namespace style {
@@ -20,6 +22,18 @@ struct Converter<DataDrivenPropertyValue<T>> {
return {};
}
return DataDrivenPropertyValue<T>(*constant);
+ } else if (objectMember(value, "expression")) {
+ optional<std::unique_ptr<Expression>> expression = convert<std::unique_ptr<Expression>>(*objectMember(value, "expression"), error, valueTypeToExpressionType<T>());
+ if (!expression) {
+ return {};
+ }
+ if ((*expression)->isFeatureConstant()) {
+ return DataDrivenPropertyValue<T>(CameraFunction<T>(std::move(*expression)));
+ } else if ((*expression)->isZoomConstant()) {
+ return DataDrivenPropertyValue<T>(SourceFunction<T>(std::move(*expression)));
+ } else {
+ return DataDrivenPropertyValue<T>(CompositeFunction<T>(std::move(*expression)));
+ }
} else if (!objectMember(value, "property")) {
optional<CameraFunction<T>> function = convert<CameraFunction<T>>(value, error);
if (!function) {
diff --git a/include/mbgl/style/conversion/expression.hpp b/include/mbgl/style/conversion/expression.hpp
index bce7c91396..601916250d 100644
--- a/include/mbgl/style/conversion/expression.hpp
+++ b/include/mbgl/style/conversion/expression.hpp
@@ -2,6 +2,7 @@
#include <memory>
#include <mbgl/style/expression/parse.hpp>
+#include <mbgl/style/expression/type.hpp>
#include <mbgl/style/conversion.hpp>
namespace mbgl {
@@ -12,12 +13,20 @@ using namespace mbgl::style::expression;
template<> struct Converter<std::unique_ptr<Expression>> {
template <class V>
- optional<std::unique_ptr<Expression>> operator()(const V& value, Error& error) const {
- auto parsed = parseExpression(value, ParsingContext());
- if (parsed.template is<std::unique_ptr<Expression>>()) {
- return std::move(parsed.template get<std::unique_ptr<Expression>>());
+ optional<std::unique_ptr<Expression>> operator()(const V& value, Error& error, type::Type expected) const {
+ std::vector<ParsingError> errors;
+ auto parsed = parseExpression(value, ParsingContext(errors, expected));
+ if (parsed) {
+ return std::move(*parsed);
}
- error = { parsed.template get<CompileError>().message };
+ std::string combinedError;
+ for (const ParsingError& parsingError : errors) {
+ if (combinedError.size() > 0) {
+ combinedError += "\n";
+ }
+ combinedError += parsingError.key + ": " + parsingError.message;
+ }
+ error = { combinedError };
return {};
};
};
diff --git a/include/mbgl/style/conversion/property_value.hpp b/include/mbgl/style/conversion/property_value.hpp
index 3780381b23..2caa801e34 100644
--- a/include/mbgl/style/conversion/property_value.hpp
+++ b/include/mbgl/style/conversion/property_value.hpp
@@ -4,6 +4,8 @@
#include <mbgl/style/conversion.hpp>
#include <mbgl/style/conversion/constant.hpp>
#include <mbgl/style/conversion/function.hpp>
+#include <mbgl/style/conversion/expression.hpp>
+#include <mbgl/style/expression/value.hpp>
namespace mbgl {
namespace style {
@@ -14,6 +16,17 @@ struct Converter<PropertyValue<T>> {
optional<PropertyValue<T>> operator()(const Value& value, Error& error) const {
if (isUndefined(value)) {
return PropertyValue<T>();
+ } else if (isObject(value) && objectMember(value, "expression")) {
+ optional<std::unique_ptr<Expression>> expression = convert<std::unique_ptr<Expression>>(*objectMember(value, "expression"), error, valueTypeToExpressionType<T>());
+ if (!expression) {
+ return {};
+ }
+ if ((*expression)->isFeatureConstant()) {
+ return { CameraFunction<T>(std::move(*expression)) };
+ } else {
+ error = { "data-driven style property not supported " };
+ return {};
+ }
} else if (isObject(value)) {
optional<CameraFunction<T>> function = convert<CameraFunction<T>>(value, error);
if (!function) {
diff --git a/include/mbgl/style/expression/curve.hpp b/include/mbgl/style/expression/curve.hpp
index 9b7a6b64f5..a3cf7fa63f 100644
--- a/include/mbgl/style/expression/curve.hpp
+++ b/include/mbgl/style/expression/curve.hpp
@@ -5,8 +5,6 @@
#include <mbgl/util/range.hpp>
#include <mbgl/style/expression/expression.hpp>
#include <mbgl/style/expression/compound_expression.hpp>
-#include <mbgl/style/expression/parsing_context.hpp>
-#include <mbgl/style/conversion.hpp>
namespace mbgl {
namespace style {
@@ -58,15 +56,6 @@ public:
}
};
-namespace detail {
-
-// used for storing intermediate state during parsing
-struct ExponentialInterpolation { float base; std::string name = "exponential"; };
-struct StepInterpolation {};
-
-} // namespace detail
-
-
template <typename InterpolatorT>
class Curve : public Expression {
public:
@@ -151,187 +140,6 @@ private:
std::map<float, std::unique_ptr<Expression>> stops;
};
-struct ParseCurve {
- template <typename V>
- static ParseResult parse(const V& value, ParsingContext ctx) {
- using namespace mbgl::style::conversion;
- assert(isArray(value));
- auto length = arrayLength(value);
- if (length < 5) {
- ctx.error("Expected at least 4 arguments, but found only " + std::to_string(length - 1) + ".");
- return ParseResult();
- }
-
- // [curve, interp, input, 2 * (n pairs)...]
- if (length % 2 != 1) {
- ctx.error("Expected an even number of arguments.");
- return ParseResult();
- }
-
- const auto& interp = arrayMember(value, 1);
- if (!isArray(interp) || arrayLength(interp) == 0) {
- ctx.error("Expected an interpolation type expression.");
- return ParseResult();
- }
-
- variant<detail::StepInterpolation,
- detail::ExponentialInterpolation> interpolation;
-
- const auto& interpName = toString(arrayMember(interp, 0));
- if (interpName && *interpName == "step") {
- interpolation = detail::StepInterpolation{};
- } else if (interpName && *interpName == "linear") {
- interpolation = detail::ExponentialInterpolation { 1.0f, "linear" };
- } else if (interpName && *interpName == "exponential") {
- optional<double> base;
- if (arrayLength(interp) == 2) {
- base = toDouble(arrayMember(interp, 1));
- }
- if (!base) {
- ctx.error("Exponential interpolation requires a numeric base.");
- return ParseResult();
- }
- interpolation = detail::ExponentialInterpolation { static_cast<float>(*base) };
- } else {
- ctx.error("Unknown interpolation type " + (interpName ? *interpName : ""));
- return ParseResult();
- }
-
- ParseResult input = parseExpression(arrayMember(value, 2), ParsingContext(ctx, 2, {type::Number}));
- if (!input) {
- return input;
- }
-
- std::map<float, std::unique_ptr<Expression>> stops;
- optional<type::Type> outputType = ctx.expected;
-
- double previous = - std::numeric_limits<double>::infinity();
- for (std::size_t i = 3; i + 1 < length; i += 2) {
- const optional<mbgl::Value>& labelValue = toValue(arrayMember(value, i));
- optional<double> label;
- optional<std::string> labelError;
- if (labelValue) {
- labelValue->match(
- [&](uint64_t n) {
- if (!Value::isSafeNumericValue(n)) {
- labelError = {"Numeric values must be no larger than " + std::to_string(Value::max()) + "."};
- } else {
- label = {static_cast<double>(n)};
- }
- },
- [&](int64_t n) {
- if (!Value::isSafeNumericValue(n)) {
- labelError = {"Numeric values must be no larger than " + std::to_string(Value::max()) + "."};
- } else {
- label = {static_cast<double>(n)};
- }
- },
- [&](double n) {
- if (!Value::isSafeNumericValue(n)) {
- labelError = {"Numeric values must be no larger than " + std::to_string(Value::max()) + "."};
- } else {
- label = {static_cast<double>(n)};
- }
- },
- [&](const auto&) {}
- );
- }
- if (!label) {
- ctx.error(labelError ? *labelError :
- R"(Input/output pairs for "curve" expressions must be defined using literal numeric values (not computed expressions) for the input values.)",
- i);
- return ParseResult();
- }
-
- if (*label < previous) {
- ctx.error(
- R"(Input/output pairs for "curve" expressions must be arranged with input values in strictly ascending order.)",
- i
- );
- return ParseResult();
- }
- previous = *label;
-
- auto output = parseExpression(arrayMember(value, i + 1), ParsingContext(ctx, i + 1, outputType));
- if (!output) {
- return ParseResult();
- }
- if (!outputType) {
- outputType = (*output)->getType();
- }
-
- stops.emplace(*label, std::move(*output));
- }
-
- assert(outputType);
-
- if (
- !interpolation.template is<detail::StepInterpolation>() &&
- *outputType != type::Number &&
- *outputType != type::Color &&
- !(
- outputType->is<type::Array>() &&
- outputType->get<type::Array>().itemType == type::Number
- )
- )
- {
- ctx.error("Type " + toString(*outputType) +
- " is not interpolatable, and thus cannot be used as a " +
- *interpName + " curve's output type.");
- return ParseResult();
- }
-
- return interpolation.match(
- [&](const detail::StepInterpolation&) -> ParseResult {
- return ParseResult(std::make_unique<Curve<StepInterpolator>>(
- *outputType,
- StepInterpolator(),
- std::move(*input),
- std::move(stops)
- ));
- },
- [&](const detail::ExponentialInterpolation& exponentialInterpolation) -> ParseResult {
- const float base = exponentialInterpolation.base;
- return outputType->match(
- [&](const type::NumberType&) -> ParseResult {
- return ParseResult(std::make_unique<Curve<ExponentialInterpolator<float>>>(
- *outputType,
- ExponentialInterpolator<float>(base),
- std::move(*input),
- std::move(stops)
- ));
- },
- [&](const type::ColorType&) -> ParseResult {
- return ParseResult(std::make_unique<Curve<ExponentialInterpolator<mbgl::Color>>>(
- *outputType,
- ExponentialInterpolator<mbgl::Color>(base),
- std::move(*input),
- std::move(stops)
- ));
- },
- [&](const type::Array& arrayType) -> ParseResult {
- if (arrayType.itemType == type::Number && arrayType.N) {
- return ParseResult(std::make_unique<Curve<ExponentialInterpolator<std::vector<Value>>>>(
- *outputType,
- ExponentialInterpolator<std::vector<Value>>(base),
- std::move(*input),
- std::move(stops)
- ));
- } else {
- assert(false); // interpolability already checked above.
- return ParseResult();
- }
- },
- [&](const auto&) {
- assert(false); // interpolability already checked above.
- return ParseResult();
- }
- );
- }
- );
- }
-};
-
} // namespace expression
} // namespace style
} // namespace mbgl
diff --git a/include/mbgl/style/expression/literal.hpp b/include/mbgl/style/expression/literal.hpp
index 6a6b62103b..96c48836bf 100644
--- a/include/mbgl/style/expression/literal.hpp
+++ b/include/mbgl/style/expression/literal.hpp
@@ -28,89 +28,27 @@ public:
template <class V>
static ParseResult parse(const V& value, ParsingContext ctx) {
- const optional<Value>& parsedValue = parseValue(value, ctx);
-
- if (!parsedValue) {
- return ParseResult();
- }
+ const Value& parsedValue = parseValue(value);
// special case: infer the item type if possible for zero-length arrays
if (
ctx.expected &&
ctx.expected->template is<type::Array>() &&
- parsedValue->template is<std::vector<Value>>()
+ parsedValue.template is<std::vector<Value>>()
) {
- auto type = typeOf(*parsedValue).template get<type::Array>();
+ auto type = typeOf(parsedValue).template get<type::Array>();
auto expected = ctx.expected->template get<type::Array>();
if (
type.N && (*type.N == 0) &&
(!expected.N || (*expected.N == 0))
) {
- return ParseResult(std::make_unique<Literal>(expected, parsedValue->template get<std::vector<Value>>()));
+ return ParseResult(std::make_unique<Literal>(expected, parsedValue.template get<std::vector<Value>>()));
}
}
- return ParseResult(std::make_unique<Literal>(*parsedValue));
+ return ParseResult(std::make_unique<Literal>(parsedValue));
}
private:
- template <class V>
- static optional<Value> parseValue(const V& value, ParsingContext ctx) {
- using namespace mbgl::style::conversion;
- if (isUndefined(value)) return {Null};
- if (isObject(value)) {
- std::unordered_map<std::string, Value> result;
- bool error = false;
- eachMember(value, [&] (const std::string& k, const V& v) -> optional<conversion::Error> {
- if (!error) {
- optional<Value> memberValue = parseValue(v, ctx);
- if (memberValue) {
- result.emplace(k, *memberValue);
- } else {
- error = true;
- }
- }
- return {};
- });
- return error ? optional<Value>() : optional<Value>(result);
- }
-
- if (isArray(value)) {
- std::vector<Value> result;
- const auto length = arrayLength(value);
- for(std::size_t i = 0; i < length; i++) {
- optional<Value> item = parseValue(arrayMember(value, i), ctx);
- if (item) {
- result.emplace_back(*item);
- } else {
- return optional<Value>();
- }
- }
- return optional<Value>(result);
- }
-
- optional<mbgl::Value> v = toValue(value);
- assert(v);
-
- return v->match(
- [&](uint64_t n) { return checkNumber(n, ctx); },
- [&](int64_t n) { return checkNumber(n, ctx); },
- [&](double n) { return checkNumber(n, ctx); },
- [&](const auto&) {
- return optional<Value>(toExpressionValue(*v));
- }
- );
- }
-
- template <typename T>
- static optional<Value> checkNumber(T n, ParsingContext ctx) {
- if (!Value::isSafeNumericValue(n)) {
- ctx.error("Numeric values must be no larger than " + std::to_string(Value::max()) + ".");
- return optional<Value>();
- } else {
- return {static_cast<double>(n)};
- }
- }
-
Value value;
};
diff --git a/include/mbgl/style/expression/match.hpp b/include/mbgl/style/expression/match.hpp
index 54a3cec9a1..7e9cb95969 100644
--- a/include/mbgl/style/expression/match.hpp
+++ b/include/mbgl/style/expression/match.hpp
@@ -53,193 +53,6 @@ private:
std::unique_ptr<Expression> otherwise;
};
-struct ParseMatch {
- template <class V>
- static ParseResult parse(const V& value, ParsingContext ctx) {
- using namespace mbgl::style::conversion;
-
- assert(isArray(value));
- auto length = arrayLength(value);
- if (length < 5) {
- ctx.error(
- "Expected at least 4 arguments, but found only " + std::to_string(length - 1) + "."
- );
- return ParseResult();
- }
-
- // Expect odd-length array: ["match", input, 2 * (n pairs)..., otherwise]
- if (length % 2 != 1) {
- ctx.error("Expected an even number of arguments.");
- return ParseResult();
- }
-
- optional<type::Type> inputType;
- optional<type::Type> outputType = ctx.expected;
- std::vector<std::pair<std::vector<InputType>,
- std::unique_ptr<Expression>>> cases;
-
- for (size_t i = 2; i + 1 < length; i += 2) {
- const auto& label = arrayMember(value, i);
-
- ParsingContext labelContext(ctx, i);
- std::vector<InputType> labels;
- // Match pair inputs are provided as either a literal value or a
- // raw JSON array of string / number / boolean values.
- if (isArray(label)) {
- auto groupLength = arrayLength(label);
- if (groupLength == 0) {
- labelContext.error("Expected at least one branch label.");
- return ParseResult();
- }
-
- for (size_t j = 0; j < groupLength; j++) {
- const optional<InputType>& inputValue = parseInputValue(arrayMember(label, j), ParsingContext(ctx, i), inputType);
- if (!inputValue) {
- return ParseResult();
- }
- labels.push_back(*inputValue);
- }
- } else {
- const optional<InputType>& inputValue = parseInputValue(label, ParsingContext(ctx, i), inputType);
- if (!inputValue) {
- return ParseResult();
- }
- labels.push_back(*inputValue);
- }
-
- ParseResult output = parseExpression(arrayMember(value, i + 1), ParsingContext(ctx, i + 1, outputType));
- if (!output) {
- return ParseResult();
- }
-
- if (!outputType) {
- outputType = (*output)->getType();
- }
-
- cases.push_back(std::make_pair(std::move(labels), std::move(*output)));
- }
-
- auto input = parseExpression(arrayMember(value, 1), ParsingContext(ctx, 1, inputType));
- if (!input) {
- return ParseResult();
- }
-
- auto otherwise = parseExpression(arrayMember(value, length - 1), ParsingContext(ctx, length - 1, outputType));
- if (!otherwise) {
- return ParseResult();
- }
-
- assert(inputType && outputType);
-
- return inputType->match(
- [&](const type::NumberType&) {
- return create<int64_t>(*outputType, std::move(*input), std::move(cases), std::move(*otherwise), ctx);
- },
- [&](const type::StringType&) {
- return create<std::string>(*outputType, std::move(*input), std::move(cases), std::move(*otherwise), ctx);
- },
- [&](const auto&) {
- assert(false);
- return ParseResult();
- }
- );
- }
-
-private:
- template <typename V>
- static optional<InputType> parseInputValue(const V& input, ParsingContext ctx, optional<type::Type>& inputType) {
- using namespace mbgl::style::conversion;
- optional<InputType> result;
- optional<type::Type> type;
-
- auto value = toValue(input);
-
- if (value) {
- value->match(
- [&] (uint64_t n) {
- if (!Value::isSafeNumericValue(n)) {
- ctx.error("Numeric values must be no larger than " + std::to_string(Value::max()) + ".");
- } else {
- type = {type::Number};
- result = {static_cast<int64_t>(n)};
- }
- },
- [&] (int64_t n) {
- if (!Value::isSafeNumericValue(n)) {
- ctx.error("Numeric values must be no larger than " + std::to_string(Value::max()) + ".");
- } else {
- type = {type::Number};
- result = {n};
- }
- },
- [&] (double n) {
- if (!Value::isSafeNumericValue(n)) {
- ctx.error("Numeric values must be no larger than " + std::to_string(Value::max()) + ".");
- } else if (n != ceilf(n)) {
- ctx.error("Numeric branch labels must be integer values.");
- } else {
- type = {type::Number};
- result = {static_cast<int64_t>(n)};
- }
- },
- [&] (const std::string& s) {
- type = {type::String};
- result = {s};
- },
- [&] (const auto&) {
- ctx.error("Branch labels must be numbers or strings.");
- }
- );
- } else {
- ctx.error("Branch labels must be numbers or strings.");
- }
-
- if (!type) {
- return result;
- }
-
- if (!inputType) {
- inputType = type;
- } else if (checkSubtype(*inputType, *type, ctx)) {
- return optional<InputType>();
- }
-
- return result;
- }
-
- template <typename T>
- static ParseResult create(type::Type outputType,
- std::unique_ptr<Expression>input,
- std::vector<std::pair<std::vector<InputType>,
- std::unique_ptr<Expression>>> cases,
- std::unique_ptr<Expression> otherwise,
- ParsingContext ctx) {
- typename Match<T>::Cases typedCases;
-
- std::size_t index = 2;
- for (std::pair<std::vector<InputType>,
- std::unique_ptr<Expression>>& pair : cases) {
- std::shared_ptr<Expression> result = std::move(pair.second);
- for (const InputType& label : pair.first) {
- const auto& typedLabel = label.template get<T>();
- if (typedCases.find(typedLabel) != typedCases.end()) {
- ctx.error("Branch labels must be unique.", index);
- return ParseResult();
- }
- typedCases.emplace(typedLabel, result);
- }
-
- index += 2;
- }
- return ParseResult(std::make_unique<Match<T>>(
- outputType,
- std::move(input),
- std::move(typedCases),
- std::move(otherwise)
- ));
- }
-};
-
} // namespace expression
} // namespace style
} // namespace mbgl
diff --git a/include/mbgl/style/expression/parse.hpp b/include/mbgl/style/expression/parse.hpp
index 79a3276227..68f336e3ed 100644
--- a/include/mbgl/style/expression/parse.hpp
+++ b/include/mbgl/style/expression/parse.hpp
@@ -2,15 +2,15 @@
#include <memory>
#include <mbgl/style/conversion.hpp>
-#include <mbgl/style/expression/array_assertion.hpp>
-#include <mbgl/style/expression/case.hpp>
#include <mbgl/style/expression/check_subtype.hpp>
-#include <mbgl/style/expression/coalesce.hpp>
-#include <mbgl/style/expression/compound_expression.hpp>
-#include <mbgl/style/expression/curve.hpp>
#include <mbgl/style/expression/expression.hpp>
-#include <mbgl/style/expression/literal.hpp>
-#include <mbgl/style/expression/match.hpp>
+#include <mbgl/style/expression/parse/array_assertion.hpp>
+#include <mbgl/style/expression/parse/case.hpp>
+#include <mbgl/style/expression/parse/coalesce.hpp>
+#include <mbgl/style/expression/parse/compound_expression.hpp>
+#include <mbgl/style/expression/parse/curve.hpp>
+#include <mbgl/style/expression/parse/literal.hpp>
+#include <mbgl/style/expression/parse/match.hpp>
#include <mbgl/style/expression/parsing_context.hpp>
namespace mbgl {
@@ -69,19 +69,19 @@ ParseResult parseExpression(const V& value, ParsingContext context)
return ParseResult();
}
- parsed = Literal::parse(arrayMember(value, 1), context);
+ parsed = ParseLiteral::parse(arrayMember(value, 1), context);
} else if (*op == "match") {
parsed = ParseMatch::parse(value, context);
} else if (*op == "curve") {
parsed = ParseCurve::parse(value, context);
} else if (*op == "coalesce") {
- parsed = Coalesce::parse(value, context);
+ parsed = ParseCoalesce::parse(value, context);
} else if (*op == "case") {
- parsed = Case::parse(value, context);
+ parsed = ParseCase::parse(value, context);
} else if (*op == "array") {
- parsed = ArrayAssertion::parse(value, context);
+ parsed = ParseArrayAssertion::parse(value, context);
} else {
- parsed = CompoundExpressions::parse(value, context);
+ parsed = ParseCompoundExpression::parse(value, context);
}
} else {
if (isObject(value)) {
@@ -89,7 +89,7 @@ ParseResult parseExpression(const V& value, ParsingContext context)
return ParseResult();
}
- parsed = Literal::parse(value, context);
+ parsed = ParseLiteral::parse(value, context);
}
if (!parsed) {
diff --git a/include/mbgl/style/expression/parse/array_assertion.hpp b/include/mbgl/style/expression/parse/array_assertion.hpp
new file mode 100644
index 0000000000..1549a468f6
--- /dev/null
+++ b/include/mbgl/style/expression/parse/array_assertion.hpp
@@ -0,0 +1,73 @@
+#pragma once
+
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/expression/array_assertion.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
+#include <mbgl/style/expression/type.hpp>
+#include <mbgl/util/optional.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+struct ParseArrayAssertion {
+ template <class V>
+ static ParseResult parse(const V& value, ParsingContext ctx) {
+ using namespace mbgl::style::conversion;
+
+ static std::unordered_map<std::string, type::Type> itemTypes {
+ {"string", type::String},
+ {"number", type::Number},
+ {"boolean", type::Boolean}
+ };
+
+ auto length = arrayLength(value);
+ if (length < 2 || length > 4) {
+ ctx.error("Expected 1, 2, or 3 arguments, but found " + std::to_string(length - 1) + " instead.");
+ return ParseResult();
+ }
+
+ optional<type::Type> itemType;
+ optional<std::size_t> N;
+ if (length > 2) {
+ optional<std::string> itemTypeName = toString(arrayMember(value, 1));
+ auto it = itemTypeName ? itemTypes.find(*itemTypeName) : itemTypes.end();
+ if (it == itemTypes.end()) {
+ ctx.error(
+ R"(The item type argument of "array" must be one of string, number, boolean)",
+ 1
+ );
+ return ParseResult();
+ }
+ itemType = it->second;
+ } else {
+ itemType = {type::Value};
+ }
+
+ if (length > 3) {
+ auto n = toNumber(arrayMember(value, 2));
+ if (!n || *n != ceilf(*n)) {
+ ctx.error(
+ R"(The length argument to "array" must be a positive integer literal.)",
+ 2
+ );
+ return ParseResult();
+ }
+ N = optional<std::size_t>(*n);
+ }
+
+ auto input = parseExpression(arrayMember(value, length - 1), ParsingContext(ctx, length - 1, {type::Value}));
+ if (!input) {
+ return input;
+ }
+
+ return ParseResult(std::make_unique<ArrayAssertion>(
+ type::Array(*itemType, N),
+ std::move(*input)
+ ));
+ }
+};
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/parse/case.hpp b/include/mbgl/style/expression/parse/case.hpp
new file mode 100644
index 0000000000..8dfa390328
--- /dev/null
+++ b/include/mbgl/style/expression/parse/case.hpp
@@ -0,0 +1,66 @@
+#pragma once
+
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/expression/case.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
+#include <mbgl/style/expression/type.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+struct ParseCase {
+ template <class V>
+ static ParseResult parse(const V& value, ParsingContext ctx) {
+ using namespace mbgl::style::conversion;
+
+ assert(isArray(value));
+ auto length = arrayLength(value);
+ if (length < 4) {
+ ctx.error("Expected at least 3 arguments, but found only " + std::to_string(length - 1) + ".");
+ return ParseResult();
+ }
+
+ // Expect even-length array: ["case", 2 * (n pairs)..., otherwise]
+ if (length % 2 != 0) {
+ ctx.error("Expected an odd number of arguments");
+ return ParseResult();
+ }
+
+ optional<type::Type> outputType = ctx.expected;
+
+ std::vector<Case::Branch> branches;
+ for (size_t i = 1; i + 1 < length; i += 2) {
+ auto test = parseExpression(arrayMember(value, i), ParsingContext(ctx, i, {type::Boolean}));
+ if (!test) {
+ return test;
+ }
+
+ auto output = parseExpression(arrayMember(value, i + 1), ParsingContext(ctx, i + 1, outputType));
+ if (!output) {
+ return output;
+ }
+
+ if (!outputType) {
+ outputType = (*output)->getType();
+ }
+
+ branches.push_back(std::make_pair(std::move(*test), std::move(*output)));
+ }
+
+ assert(outputType);
+
+ auto otherwise = parseExpression(arrayMember(value, length - 1), ParsingContext(ctx, length - 1, outputType));
+ if (!otherwise) {
+ return otherwise;
+ }
+
+ return ParseResult(std::make_unique<Case>(*outputType,
+ std::move(branches),
+ std::move(*otherwise)));
+ }
+};
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/parse/coalesce.hpp b/include/mbgl/style/expression/parse/coalesce.hpp
new file mode 100644
index 0000000000..fd368fd5ec
--- /dev/null
+++ b/include/mbgl/style/expression/parse/coalesce.hpp
@@ -0,0 +1,45 @@
+#pragma once
+
+#include <vector>
+#include <memory>
+#include <mbgl/style/expression/coalesce.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
+#include <mbgl/style/expression/type.hpp>
+#include <mbgl/style/conversion.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+struct ParseCoalesce {
+ template <typename V>
+ static ParseResult parse(const V& value, ParsingContext ctx) {
+ using namespace mbgl::style::conversion;
+ assert(isArray(value));
+ auto length = arrayLength(value);
+ if (length < 2) {
+ ctx.error("Expected at least one argument.");
+ return ParseResult();
+ }
+
+ Coalesce::Args args;
+ optional<type::Type> outputType = ctx.expected;
+ for (std::size_t i = 1; i < length; i++) {
+ auto parsed = parseExpression(arrayMember(value, i), ParsingContext(ctx, i, outputType));
+ if (!parsed) {
+ return parsed;
+ }
+ if (!outputType) {
+ outputType = (*parsed)->getType();
+ }
+ args.push_back(std::move(*parsed));
+ }
+
+ assert(outputType);
+ return ParseResult(std::make_unique<Coalesce>(*outputType, std::move(args)));
+ }
+};
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/parse/compound_expression.hpp b/include/mbgl/style/expression/parse/compound_expression.hpp
new file mode 100644
index 0000000000..248ba4c102
--- /dev/null
+++ b/include/mbgl/style/expression/parse/compound_expression.hpp
@@ -0,0 +1,50 @@
+#pragma once
+
+#include <array>
+#include <vector>
+#include <memory>
+#include <mbgl/style/expression/compound_expression.hpp>
+#include <mbgl/style/expression/type.hpp>
+#include <mbgl/style/expression/value.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
+#include <mbgl/style/conversion.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+struct ParseCompoundExpression {
+ template <class V>
+ static ParseResult parse(const V& value, ParsingContext ctx) {
+ using namespace mbgl::style::conversion;
+ assert(isArray(value) && arrayLength(value) > 0);
+ const auto& name = toString(arrayMember(value, 0));
+ assert(name);
+
+ auto it = CompoundExpressions::definitions.find(*name);
+ if (it == CompoundExpressions::definitions.end()) {
+ ctx.error(
+ R"(Unknown expression ")" + *name + R"(". If you wanted a literal array, use ["literal", [...]].)",
+ 0
+ );
+ return ParseResult();
+ }
+ const CompoundExpressions::Definition& definition = it->second;
+
+ // parse subexpressions first
+ std::vector<std::unique_ptr<Expression>> args;
+ auto length = arrayLength(value);
+ for (std::size_t i = 1; i < length; i++) {
+ auto parsed = parseExpression(arrayMember(value, i), ParsingContext(ctx, i));
+ if (!parsed) {
+ return parsed;
+ }
+ args.push_back(std::move(*parsed));
+ }
+ return CompoundExpressions::create(*name, definition, std::move(args), ctx);
+ }
+};
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/parse/curve.hpp b/include/mbgl/style/expression/parse/curve.hpp
new file mode 100644
index 0000000000..537c1ef1e2
--- /dev/null
+++ b/include/mbgl/style/expression/parse/curve.hpp
@@ -0,0 +1,207 @@
+#pragma once
+
+#include <map>
+#include <memory>
+#include <mbgl/util/optional.hpp>
+#include <mbgl/style/expression/curve.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
+#include <mbgl/style/conversion.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+namespace detail {
+
+// used for storing intermediate state during parsing
+struct ExponentialInterpolation { float base; std::string name = "exponential"; };
+struct StepInterpolation {};
+
+} // namespace detail
+
+struct ParseCurve {
+ template <typename V>
+ static ParseResult parse(const V& value, ParsingContext ctx) {
+ using namespace mbgl::style::conversion;
+ assert(isArray(value));
+ auto length = arrayLength(value);
+ if (length < 5) {
+ ctx.error("Expected at least 4 arguments, but found only " + std::to_string(length - 1) + ".");
+ return ParseResult();
+ }
+
+ // [curve, interp, input, 2 * (n pairs)...]
+ if (length % 2 != 1) {
+ ctx.error("Expected an even number of arguments.");
+ return ParseResult();
+ }
+
+ const auto& interp = arrayMember(value, 1);
+ if (!isArray(interp) || arrayLength(interp) == 0) {
+ ctx.error("Expected an interpolation type expression.");
+ return ParseResult();
+ }
+
+ variant<detail::StepInterpolation,
+ detail::ExponentialInterpolation> interpolation;
+
+ const auto& interpName = toString(arrayMember(interp, 0));
+ if (interpName && *interpName == "step") {
+ interpolation = detail::StepInterpolation{};
+ } else if (interpName && *interpName == "linear") {
+ interpolation = detail::ExponentialInterpolation { 1.0f, "linear" };
+ } else if (interpName && *interpName == "exponential") {
+ optional<double> base;
+ if (arrayLength(interp) == 2) {
+ base = toDouble(arrayMember(interp, 1));
+ }
+ if (!base) {
+ ctx.error("Exponential interpolation requires a numeric base.");
+ return ParseResult();
+ }
+ interpolation = detail::ExponentialInterpolation { static_cast<float>(*base) };
+ } else {
+ ctx.error("Unknown interpolation type " + (interpName ? *interpName : ""));
+ return ParseResult();
+ }
+
+ ParseResult input = parseExpression(arrayMember(value, 2), ParsingContext(ctx, 2, {type::Number}));
+ if (!input) {
+ return input;
+ }
+
+ std::map<float, std::unique_ptr<Expression>> stops;
+ optional<type::Type> outputType = ctx.expected;
+
+ double previous = - std::numeric_limits<double>::infinity();
+ for (std::size_t i = 3; i + 1 < length; i += 2) {
+ const optional<mbgl::Value>& labelValue = toValue(arrayMember(value, i));
+ optional<double> label;
+ optional<std::string> labelError;
+ if (labelValue) {
+ labelValue->match(
+ [&](uint64_t n) {
+ if (!Value::isSafeNumericValue(n)) {
+ labelError = {"Numeric values must be no larger than " + std::to_string(Value::max()) + "."};
+ } else {
+ label = {static_cast<double>(n)};
+ }
+ },
+ [&](int64_t n) {
+ if (!Value::isSafeNumericValue(n)) {
+ labelError = {"Numeric values must be no larger than " + std::to_string(Value::max()) + "."};
+ } else {
+ label = {static_cast<double>(n)};
+ }
+ },
+ [&](double n) {
+ if (!Value::isSafeNumericValue(n)) {
+ labelError = {"Numeric values must be no larger than " + std::to_string(Value::max()) + "."};
+ } else {
+ label = {static_cast<double>(n)};
+ }
+ },
+ [&](const auto&) {}
+ );
+ }
+ if (!label) {
+ ctx.error(labelError ? *labelError :
+ R"(Input/output pairs for "curve" expressions must be defined using literal numeric values (not computed expressions) for the input values.)",
+ i);
+ return ParseResult();
+ }
+
+ if (*label < previous) {
+ ctx.error(
+ R"(Input/output pairs for "curve" expressions must be arranged with input values in strictly ascending order.)",
+ i
+ );
+ return ParseResult();
+ }
+ previous = *label;
+
+ auto output = parseExpression(arrayMember(value, i + 1), ParsingContext(ctx, i + 1, outputType));
+ if (!output) {
+ return ParseResult();
+ }
+ if (!outputType) {
+ outputType = (*output)->getType();
+ }
+
+ stops.emplace(*label, std::move(*output));
+ }
+
+ assert(outputType);
+
+ if (
+ !interpolation.template is<detail::StepInterpolation>() &&
+ *outputType != type::Number &&
+ *outputType != type::Color &&
+ !(
+ outputType->is<type::Array>() &&
+ outputType->get<type::Array>().itemType == type::Number
+ )
+ )
+ {
+ ctx.error("Type " + toString(*outputType) +
+ " is not interpolatable, and thus cannot be used as a " +
+ *interpName + " curve's output type.");
+ return ParseResult();
+ }
+
+ return interpolation.match(
+ [&](const detail::StepInterpolation&) -> ParseResult {
+ return ParseResult(std::make_unique<Curve<StepInterpolator>>(
+ *outputType,
+ StepInterpolator(),
+ std::move(*input),
+ std::move(stops)
+ ));
+ },
+ [&](const detail::ExponentialInterpolation& exponentialInterpolation) -> ParseResult {
+ const float base = exponentialInterpolation.base;
+ return outputType->match(
+ [&](const type::NumberType&) -> ParseResult {
+ return ParseResult(std::make_unique<Curve<ExponentialInterpolator<float>>>(
+ *outputType,
+ ExponentialInterpolator<float>(base),
+ std::move(*input),
+ std::move(stops)
+ ));
+ },
+ [&](const type::ColorType&) -> ParseResult {
+ return ParseResult(std::make_unique<Curve<ExponentialInterpolator<mbgl::Color>>>(
+ *outputType,
+ ExponentialInterpolator<mbgl::Color>(base),
+ std::move(*input),
+ std::move(stops)
+ ));
+ },
+ [&](const type::Array& arrayType) -> ParseResult {
+ if (arrayType.itemType == type::Number && arrayType.N) {
+ return ParseResult(std::make_unique<Curve<ExponentialInterpolator<std::vector<Value>>>>(
+ *outputType,
+ ExponentialInterpolator<std::vector<Value>>(base),
+ std::move(*input),
+ std::move(stops)
+ ));
+ } else {
+ assert(false); // interpolability already checked above.
+ return ParseResult();
+ }
+ },
+ [&](const auto&) {
+ assert(false); // interpolability already checked above.
+ return ParseResult();
+ }
+ );
+ }
+ );
+ }
+};
+
+
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/parse/let.hpp b/include/mbgl/style/expression/parse/let.hpp
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/include/mbgl/style/expression/parse/let.hpp
diff --git a/include/mbgl/style/expression/parse/literal.hpp b/include/mbgl/style/expression/parse/literal.hpp
new file mode 100644
index 0000000000..0d17c9f384
--- /dev/null
+++ b/include/mbgl/style/expression/parse/literal.hpp
@@ -0,0 +1,104 @@
+#pragma once
+
+#include <vector>
+#include <memory>
+#include <mbgl/util/optional.hpp>
+#include <mbgl/style/expression/literal.hpp>
+#include <mbgl/style/expression/type.hpp>
+#include <mbgl/style/expression/value.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
+#include <mbgl/style/conversion.hpp>
+
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+struct ParseLiteral {
+ template <class V>
+ static ParseResult parse(const V& value, ParsingContext ctx) {
+ const optional<Value>& parsedValue = parseValue(value, ctx);
+
+ if (!parsedValue) {
+ return ParseResult();
+ }
+
+ // special case: infer the item type if possible for zero-length arrays
+ if (
+ ctx.expected &&
+ ctx.expected->template is<type::Array>() &&
+ parsedValue->template is<std::vector<Value>>()
+ ) {
+ auto type = typeOf(*parsedValue).template get<type::Array>();
+ auto expected = ctx.expected->template get<type::Array>();
+ if (
+ type.N && (*type.N == 0) &&
+ (!expected.N || (*expected.N == 0))
+ ) {
+ return ParseResult(std::make_unique<Literal>(expected, parsedValue->template get<std::vector<Value>>()));
+ }
+ }
+ return ParseResult(std::make_unique<Literal>(*parsedValue));
+ }
+ template <class V>
+ static optional<Value> parseValue(const V& value, ParsingContext ctx) {
+ using namespace mbgl::style::conversion;
+ if (isUndefined(value)) return {Null};
+ if (isObject(value)) {
+ std::unordered_map<std::string, Value> result;
+ bool error = false;
+ eachMember(value, [&] (const std::string& k, const V& v) -> optional<conversion::Error> {
+ if (!error) {
+ optional<Value> memberValue = parseValue(v, ctx);
+ if (memberValue) {
+ result.emplace(k, *memberValue);
+ } else {
+ error = true;
+ }
+ }
+ return {};
+ });
+ return error ? optional<Value>() : optional<Value>(result);
+ }
+
+ if (isArray(value)) {
+ std::vector<Value> result;
+ const auto length = arrayLength(value);
+ for(std::size_t i = 0; i < length; i++) {
+ optional<Value> item = parseValue(arrayMember(value, i), ctx);
+ if (item) {
+ result.emplace_back(*item);
+ } else {
+ return optional<Value>();
+ }
+ }
+ return optional<Value>(result);
+ }
+
+ optional<mbgl::Value> v = toValue(value);
+ assert(v);
+
+ return v->match(
+ [&](uint64_t n) { return checkNumber(n, ctx); },
+ [&](int64_t n) { return checkNumber(n, ctx); },
+ [&](double n) { return checkNumber(n, ctx); },
+ [&](const auto&) {
+ return optional<Value>(toExpressionValue(*v));
+ }
+ );
+ }
+
+ template <typename T>
+ static optional<Value> checkNumber(T n, ParsingContext ctx) {
+ if (!Value::isSafeNumericValue(n)) {
+ ctx.error("Numeric values must be no larger than " + std::to_string(Value::max()) + ".");
+ return optional<Value>();
+ } else {
+ return {static_cast<double>(n)};
+ }
+ }
+};
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/parse/match.hpp b/include/mbgl/style/expression/parse/match.hpp
new file mode 100644
index 0000000000..4406b74002
--- /dev/null
+++ b/include/mbgl/style/expression/parse/match.hpp
@@ -0,0 +1,203 @@
+#pragma once
+
+#include <memory>
+#include <mbgl/style/expression/check_subtype.hpp>
+#include <mbgl/style/expression/match.hpp>
+#include <mbgl/style/expression/type.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
+#include <mbgl/style/conversion.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+struct ParseMatch {
+ template <class V>
+ static ParseResult parse(const V& value, ParsingContext ctx) {
+ using namespace mbgl::style::conversion;
+
+ assert(isArray(value));
+ auto length = arrayLength(value);
+ if (length < 5) {
+ ctx.error(
+ "Expected at least 4 arguments, but found only " + std::to_string(length - 1) + "."
+ );
+ return ParseResult();
+ }
+
+ // Expect odd-length array: ["match", input, 2 * (n pairs)..., otherwise]
+ if (length % 2 != 1) {
+ ctx.error("Expected an even number of arguments.");
+ return ParseResult();
+ }
+
+ optional<type::Type> inputType;
+ optional<type::Type> outputType = ctx.expected;
+ std::vector<std::pair<std::vector<InputType>,
+ std::unique_ptr<Expression>>> cases;
+
+ for (size_t i = 2; i + 1 < length; i += 2) {
+ const auto& label = arrayMember(value, i);
+
+ ParsingContext labelContext(ctx, i);
+ std::vector<InputType> labels;
+ // Match pair inputs are provided as either a literal value or a
+ // raw JSON array of string / number / boolean values.
+ if (isArray(label)) {
+ auto groupLength = arrayLength(label);
+ if (groupLength == 0) {
+ labelContext.error("Expected at least one branch label.");
+ return ParseResult();
+ }
+
+ for (size_t j = 0; j < groupLength; j++) {
+ const optional<InputType>& inputValue = parseInputValue(arrayMember(label, j), ParsingContext(ctx, i), inputType);
+ if (!inputValue) {
+ return ParseResult();
+ }
+ labels.push_back(*inputValue);
+ }
+ } else {
+ const optional<InputType>& inputValue = parseInputValue(label, ParsingContext(ctx, i), inputType);
+ if (!inputValue) {
+ return ParseResult();
+ }
+ labels.push_back(*inputValue);
+ }
+
+ ParseResult output = parseExpression(arrayMember(value, i + 1), ParsingContext(ctx, i + 1, outputType));
+ if (!output) {
+ return ParseResult();
+ }
+
+ if (!outputType) {
+ outputType = (*output)->getType();
+ }
+
+ cases.push_back(std::make_pair(std::move(labels), std::move(*output)));
+ }
+
+ auto input = parseExpression(arrayMember(value, 1), ParsingContext(ctx, 1, inputType));
+ if (!input) {
+ return ParseResult();
+ }
+
+ auto otherwise = parseExpression(arrayMember(value, length - 1), ParsingContext(ctx, length - 1, outputType));
+ if (!otherwise) {
+ return ParseResult();
+ }
+
+ assert(inputType && outputType);
+
+ return inputType->match(
+ [&](const type::NumberType&) {
+ return create<int64_t>(*outputType, std::move(*input), std::move(cases), std::move(*otherwise), ctx);
+ },
+ [&](const type::StringType&) {
+ return create<std::string>(*outputType, std::move(*input), std::move(cases), std::move(*otherwise), ctx);
+ },
+ [&](const auto&) {
+ assert(false);
+ return ParseResult();
+ }
+ );
+ }
+
+private:
+ template <typename V>
+ static optional<InputType> parseInputValue(const V& input, ParsingContext ctx, optional<type::Type>& inputType) {
+ using namespace mbgl::style::conversion;
+ optional<InputType> result;
+ optional<type::Type> type;
+
+ auto value = toValue(input);
+
+ if (value) {
+ value->match(
+ [&] (uint64_t n) {
+ if (!Value::isSafeNumericValue(n)) {
+ ctx.error("Numeric values must be no larger than " + std::to_string(Value::max()) + ".");
+ } else {
+ type = {type::Number};
+ result = {static_cast<int64_t>(n)};
+ }
+ },
+ [&] (int64_t n) {
+ if (!Value::isSafeNumericValue(n)) {
+ ctx.error("Numeric values must be no larger than " + std::to_string(Value::max()) + ".");
+ } else {
+ type = {type::Number};
+ result = {n};
+ }
+ },
+ [&] (double n) {
+ if (!Value::isSafeNumericValue(n)) {
+ ctx.error("Numeric values must be no larger than " + std::to_string(Value::max()) + ".");
+ } else if (n != ceilf(n)) {
+ ctx.error("Numeric branch labels must be integer values.");
+ } else {
+ type = {type::Number};
+ result = {static_cast<int64_t>(n)};
+ }
+ },
+ [&] (const std::string& s) {
+ type = {type::String};
+ result = {s};
+ },
+ [&] (const auto&) {
+ ctx.error("Branch labels must be numbers or strings.");
+ }
+ );
+ } else {
+ ctx.error("Branch labels must be numbers or strings.");
+ }
+
+ if (!type) {
+ return result;
+ }
+
+ if (!inputType) {
+ inputType = type;
+ } else if (checkSubtype(*inputType, *type, ctx)) {
+ return optional<InputType>();
+ }
+
+ return result;
+ }
+
+ template <typename T>
+ static ParseResult create(type::Type outputType,
+ std::unique_ptr<Expression>input,
+ std::vector<std::pair<std::vector<InputType>,
+ std::unique_ptr<Expression>>> cases,
+ std::unique_ptr<Expression> otherwise,
+ ParsingContext ctx) {
+ typename Match<T>::Cases typedCases;
+
+ std::size_t index = 2;
+ for (std::pair<std::vector<InputType>,
+ std::unique_ptr<Expression>>& pair : cases) {
+ std::shared_ptr<Expression> result = std::move(pair.second);
+ for (const InputType& label : pair.first) {
+ const auto& typedLabel = label.template get<T>();
+ if (typedCases.find(typedLabel) != typedCases.end()) {
+ ctx.error("Branch labels must be unique.", index);
+ return ParseResult();
+ }
+ typedCases.emplace(typedLabel, result);
+ }
+
+ index += 2;
+ }
+ return ParseResult(std::make_unique<Match<T>>(
+ outputType,
+ std::move(input),
+ std::move(typedCases),
+ std::move(otherwise)
+ ));
+ }
+};
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/function/camera_function.hpp b/include/mbgl/style/function/camera_function.hpp
index 5a0f5ffe4a..55fd5cd7cc 100644
--- a/include/mbgl/style/function/camera_function.hpp
+++ b/include/mbgl/style/function/camera_function.hpp
@@ -19,6 +19,13 @@ public:
IntervalStops<T>>,
variant<
IntervalStops<T>>>;
+
+ CameraFunction(std::unique_ptr<expression::Expression> expression_)
+ : expression(std::move(expression_))
+ {
+ assert(!expression->isZoomConstant());
+ assert(expression->isFeatureConstant());
+ }
CameraFunction(Stops stops_)
: stops(std::move(stops_)),
diff --git a/include/mbgl/style/function/composite_function.hpp b/include/mbgl/style/function/composite_function.hpp
index f7cbab1b21..7d77d5e552 100644
--- a/include/mbgl/style/function/composite_function.hpp
+++ b/include/mbgl/style/function/composite_function.hpp
@@ -50,6 +50,18 @@ public:
using Interpolator = expression::ExponentialInterpolator<T>;
using Curve = expression::Curve<Interpolator>;
+ CompositeFunction(std::unique_ptr<expression::Expression> expression_)
+ : expression(std::move(expression_)),
+ interpolator([&]() -> Interpolator {
+ optional<Curve*> zoomCurve = findZoomCurve(expression.get());
+ assert(zoomCurve);
+ return (*zoomCurve)->getInterpolator();
+ }())
+ {
+ assert(!expression->isZoomConstant());
+ assert(!expression->isFeatureConstant());
+ }
+
CompositeFunction(std::string property_, Stops stops_, optional<T> defaultValue_ = {})
: property(std::move(property_)),
stops(std::move(stops_)),
diff --git a/include/mbgl/style/function/source_function.hpp b/include/mbgl/style/function/source_function.hpp
index 0ae8357d7b..d65d03857a 100644
--- a/include/mbgl/style/function/source_function.hpp
+++ b/include/mbgl/style/function/source_function.hpp
@@ -29,6 +29,13 @@ public:
CategoricalStops<T>,
IdentityStops<T>>>;
+ SourceFunction(std::unique_ptr<expression::Expression> expression_)
+ : expression(std::move(expression_))
+ {
+ assert(expression->isZoomConstant());
+ assert(!expression->isFeatureConstant());
+ }
+
SourceFunction(std::string property_, Stops stops_, optional<T> defaultValue_ = {})
: property(std::move(property_)),
stops(std::move(stops_)),
diff --git a/src/mbgl/style/expression/value.cpp b/src/mbgl/style/expression/value.cpp
index cbcf7e2206..0f257a4c8b 100644
--- a/src/mbgl/style/expression/value.cpp
+++ b/src/mbgl/style/expression/value.cpp
@@ -142,21 +142,6 @@ std::vector<Value> toArrayValue(const Container& value) {
return result;
}
-template <typename T, typename Container>
-optional<Container> fromArrayValue(const std::vector<Value>& value) {
- Container result;
- auto it = result.begin();
- for(const Value& item : value) {
- optional<T> convertedItem = Converter<T>::fromExpressionValue(item);
- if (!convertedItem) {
- return optional<Container>();
- }
- *it = *convertedItem;
- it = std::next(it);
- }
- return result;
-}
-
template <typename T, std::size_t N>
struct Converter<std::array<T, N>> {
static Value toExpressionValue(const std::array<T, N>& value) {
@@ -167,7 +152,17 @@ struct Converter<std::array<T, N>> {
return value.match(
[&] (const std::vector<Value>& v) -> optional<std::array<T, N>> {
if (v.size() != N) return optional<std::array<T, N>>();
- return fromArrayValue<T, std::array<T, N>>(v);
+ std::array<T, N> result;
+ auto it = result.begin();
+ for(const Value& item : v) {
+ optional<T> convertedItem = Converter<T>::fromExpressionValue(item);
+ if (!convertedItem) {
+ return optional<std::array<T, N>>();
+ }
+ *it = *convertedItem;
+ it = std::next(it);
+ }
+ return result;
},
[&] (const auto&) { return optional<std::array<T, N>>(); }
);
@@ -187,7 +182,15 @@ struct Converter<std::vector<T>> {
static optional<std::vector<T>> fromExpressionValue(const Value& value) {
return value.match(
[&] (const std::vector<Value>& v) -> optional<std::vector<T>> {
- return fromArrayValue<T, std::vector<T>>(v);
+ std::vector<T> result;
+ for(const Value& item : v) {
+ optional<T> convertedItem = Converter<T>::fromExpressionValue(item);
+ if (!convertedItem) {
+ return optional<std::vector<T>>();
+ }
+ result.push_back(*convertedItem);
+ }
+ return result;
},
[&] (const auto&) { return optional<std::vector<T>>(); }
);