diff options
author | Anand Thakker <github@anandthakker.net> | 2017-07-20 16:29:41 -0400 |
---|---|---|
committer | Anand Thakker <github@anandthakker.net> | 2017-08-11 21:54:51 -0400 |
commit | 0925a779e866b64899cdd6c7cf2dc042e865eac7 (patch) | |
tree | 6df9ba68742a9f7c50715856f26c2472a8da8e5b | |
parent | 52edbeb1a0a1dd1ba0d7e3e651dcf4d5ea5abe89 (diff) | |
download | qtlocation-mapboxgl-0925a779e866b64899cdd6c7cf2dc042e865eac7.tar.gz |
Implement special forms: match, curve, coalesce
Port 'match'
Implement curve
Implement coalesce
-rw-r--r-- | cmake/core-files.cmake | 5 | ||||
-rw-r--r-- | include/mbgl/style/expression/case.hpp | 0 | ||||
-rw-r--r-- | include/mbgl/style/expression/coalesce.hpp | 116 | ||||
-rw-r--r-- | include/mbgl/style/expression/curve.hpp | 339 | ||||
-rw-r--r-- | include/mbgl/style/expression/let.hpp | 0 | ||||
-rw-r--r-- | include/mbgl/style/expression/match.hpp | 287 | ||||
-rw-r--r-- | include/mbgl/style/expression/parse.hpp | 11 | ||||
-rw-r--r-- | src/mbgl/style/expression/compound_expression.cpp | 4 |
8 files changed, 758 insertions, 4 deletions
diff --git a/cmake/core-files.cmake b/cmake/core-files.cmake index a86900f06b..24d7fb7013 100644 --- a/cmake/core-files.cmake +++ b/cmake/core-files.cmake @@ -390,8 +390,13 @@ set(MBGL_CORE_FILES src/mbgl/style/conversion/stringify.hpp # style/expression + include/mbgl/style/expression/case.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/let.hpp + include/mbgl/style/expression/match.hpp include/mbgl/style/expression/parse.hpp include/mbgl/style/expression/parsing_context.hpp include/mbgl/style/expression/type.hpp diff --git a/include/mbgl/style/expression/case.hpp b/include/mbgl/style/expression/case.hpp new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/include/mbgl/style/expression/case.hpp diff --git a/include/mbgl/style/expression/coalesce.hpp b/include/mbgl/style/expression/coalesce.hpp new file mode 100644 index 0000000000..5c7cc0c7c1 --- /dev/null +++ b/include/mbgl/style/expression/coalesce.hpp @@ -0,0 +1,116 @@ +#pragma once + +#include <map> +#include <mbgl/util/interpolate.hpp> +#include <mbgl/style/expression/expression.hpp> +#include <mbgl/style/expression/parsing_context.hpp> +#include <mbgl/style/conversion.hpp> + +namespace mbgl { +namespace style { +namespace expression { + +class TypedCoalesce : public TypedExpression { +public: + using Args = std::vector<std::unique_ptr<TypedExpression>>; + TypedCoalesce(const type::Type& type, Args args_) : + TypedExpression(type), + args(std::move(args_)) + {} + + EvaluationResult evaluate(const EvaluationParameters& params) const override { + for (auto it = args.begin(); it != args.end(); it++) { + const auto& result = (*it)->evaluate(params); + if (!result && (std::next(it) != args.end())) { + continue; + } + return result; + } + + return Null; + } + + bool isFeatureConstant() const override { + for (const auto& arg : args) { + if (!arg->isFeatureConstant()) return false; + } + return true; + } + + bool isZoomConstant() const override { + for (const auto& arg : args) { + if (!arg->isZoomConstant()) return false; + } + return true; + } + +private: + Args args; +}; + +class UntypedCoalesce : public UntypedExpression { +public: + using Args = std::vector<std::unique_ptr<UntypedExpression>>; + + UntypedCoalesce(const std::string& key, Args args_) : + UntypedExpression(key), + args(std::move(args_)) + {} + + template <typename V> + static ParseResult parse(const V& value, const ParsingContext& ctx) { + using namespace mbgl::style::conversion; + assert(isArray(value)); + auto length = arrayLength(value); + if (length < 2) { + return CompileError { + "Expected at least one argument to \"coalesce\".", + ctx.key() + }; + } + + Args args; + for (std::size_t i = 1; i < length; i++) { + auto parsed = parseExpression(arrayMember(value, i), ParsingContext(ctx, {i}, {"coalesce"})); + if (parsed.template is<CompileError>()) { + return parsed; + } + args.push_back(std::move(parsed.template get<std::unique_ptr<UntypedExpression>>())); + } + return std::make_unique<UntypedCoalesce>(ctx.key(), std::move(args)); + } + + TypecheckResult typecheck(std::vector<CompileError>& errors) const override { + optional<type::Type> outputType; + TypedCoalesce::Args checkedArgs; + + for (const auto& arg : args) { + auto checked = arg->typecheck(errors); + if (!checked) { + continue; + } + if (!outputType) { + outputType = (*checked)->getType(); + } else { + const auto& error = matchType(*outputType, (*checked)->getType()); + if (error) { + errors.push_back({ *error, arg->getKey() }); + continue; + } + } + + checkedArgs.push_back(std::move(*checked)); + } + + if (errors.size() > 0) return TypecheckResult(); + + return TypecheckResult(std::make_unique<TypedCoalesce>(*outputType, std::move(checkedArgs))); + } + +private: + Args args; +}; + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/expression/curve.hpp b/include/mbgl/style/expression/curve.hpp new file mode 100644 index 0000000000..00ea15eb46 --- /dev/null +++ b/include/mbgl/style/expression/curve.hpp @@ -0,0 +1,339 @@ +#pragma once + +#include <map> +#include <mbgl/util/interpolate.hpp> +#include <mbgl/style/expression/expression.hpp> +#include <mbgl/style/expression/parsing_context.hpp> +#include <mbgl/style/conversion.hpp> + +namespace mbgl { + +namespace util { + +struct InterpolateExpressionValue { + const double t; + + template <typename T, typename Enabled = void> + optional<Value> operator()(const T&, const T&) const { + return optional<Value>(); + }; + + template <typename T, typename std::enable_if_t<Interpolatable<T>::value>> + optional<Value> operator()(const T& a, const T& b) const { + return util::interpolate(a, b, t); + } + + template <typename T, typename U> + optional<Value> operator()(const T&, const U&) const { + return {}; + } +}; + +template<> +struct Interpolator<std::vector<Value>> { + std::vector<Value> operator()(const std::vector<Value>& a, const std::vector<Value>& b, const double t) const { + assert(a.size() == b.size()); + if (a.size() == 0) return {}; + std::vector<Value> result; + InterpolateExpressionValue visitor {t}; + for (std::size_t i = 0; i < a.size(); i++) { + const auto& v = Value::binary_visit(a[i], b[i], visitor); + assert(v); + result.push_back(*v); + } + return result; + } +}; + +} // namespace util + +namespace style { +namespace expression { + +template <class T = void> +class ExponentialCurve { +public: + using Stops = std::map<float, std::unique_ptr<TypedExpression>>; + + ExponentialCurve(Stops stops_, float base_) + : stops(std::move(stops_)), + base(base_) + {} + + Stops stops; + float base; + + EvaluationResult evaluate(float x, const EvaluationParameters& parameters) const { + if (stops.empty()) { + return EvaluationError { "No stops in exponential curve." }; + } + + auto it = stops.upper_bound(x); + if (it == stops.end()) { + return stops.rbegin()->second->evaluate(parameters); + } else if (it == stops.begin()) { + return stops.begin()->second->evaluate(parameters); + } else { + const auto& lower = std::prev(it)->second->template evaluate<T>(parameters); + if (!lower) { return lower.error(); } + const auto& upper = it->second->template evaluate<T>(parameters); + if (!upper) { return upper.error(); } + return util::interpolate(*lower, *upper, + util::interpolationFactor(base, { std::prev(it)->first, it->first }, x)); + } + } +}; + +class StepCurve { +public: + using Stops = std::map<float, std::unique_ptr<TypedExpression>>; + StepCurve(Stops stops_) : stops(std::move(stops_)) {} + + Stops stops; + + EvaluationResult evaluate(float x, const EvaluationParameters& parameters) const { + if (stops.empty()) { + return EvaluationError { "No stops in exponential curve." }; + } + + auto it = stops.upper_bound(x); + if (it == stops.end()) { + return stops.rbegin()->second->evaluate(parameters); + } else if (it == stops.begin()) { + return stops.begin()->second->evaluate(parameters); + } else { + return std::prev(it)->second->evaluate(parameters); + } + } +}; + +template <typename Curve> +class TypedCurve : public TypedExpression { +public: + TypedCurve(const type::Type& type, std::unique_ptr<TypedExpression> input_, Curve curve_) : + TypedExpression(type), + input(std::move(input_)), + curve(std::move(curve_)) + {} + + EvaluationResult evaluate(const EvaluationParameters& params) const override { + const auto& x = input->evaluate<float>(params); + if (!x) { return x.error(); } + return curve.evaluate(*x, params); + } + + bool isFeatureConstant() const override { + if (!input->isFeatureConstant()) { return false; } + for (const auto& stop : curve.stops) { + if (!stop.second->isFeatureConstant()) { return false; } + } + return true; + } + + bool isZoomConstant() const override { + if (!input->isZoomConstant()) { return false; } + for (const auto& stop : curve.stops) { + if (!stop.second->isZoomConstant()) { return false; } + } + return true; + } + +private: + std::unique_ptr<TypedExpression> input; + Curve curve; +}; + +struct ExponentialInterpolation { float base; std::string name = "exponential"; }; +struct StepInterpolation {}; + +class UntypedCurve : public UntypedExpression { +public: + using Stops = std::vector<std::pair<float, std::unique_ptr<UntypedExpression>>>; + using Interpolation = variant< + StepInterpolation, + ExponentialInterpolation>; + UntypedCurve(const std::string& key, + std::unique_ptr<UntypedExpression> input_, + Stops stops_, + Interpolation interpolation_ + ) : UntypedExpression(key), + input(std::move(input_)), + stops(std::move(stops_)), + interpolation(interpolation_) + {} + + template <typename V> + static ParseResult parse(const V& value, const ParsingContext& ctx) { + using namespace mbgl::style::conversion; + assert(isArray(value)); + auto length = arrayLength(value); + if (length < 4) { + return CompileError { + "Expected at least 3 arguments, but found only " + std::to_string(length - 1) + ".", + ctx.key() + }; + } + + // [curve, interp, input, 2 * (n pairs)...] + if (length % 2 != 1) { + return CompileError { + "Missing final output value for \"curve\" expression.", + ctx.key() + }; + } + + const auto& interp = arrayMember(value, 1); + if (!isArray(interp) || arrayLength(interp) == 0) { + return CompileError { + "Expected an interpolation type expression, e.g. [\"linear\"].", + ctx.key(1) + }; + } + + Interpolation interpolation; + const auto& interpName = toString(arrayMember(interp, 0)); + if (interpName && *interpName == "step") { + interpolation = StepInterpolation {}; + } else if (interpName && *interpName == "linear") { + interpolation = ExponentialInterpolation { 1.0f, "linear" }; + } else if (interpName && *interpName == "exponential") { + optional<double> base; + if (arrayLength(interp) == 2) { + base = toDouble(arrayMember(interp, 1)); + } + if (!base) { + return CompileError { + "Exponential interpolation requires a numeric base", + ctx.key(1) + }; + } + interpolation = ExponentialInterpolation { static_cast<float>(*base) }; + } else { + std::string name = interpName ? *interpName : ""; + return CompileError { + "Unknown interpolation type " + name, + ctx.key(1) + }; + } + + auto input = parseExpression(arrayMember(value, 2), ParsingContext(ctx, {2}, {"curve"})); + if (input.template is<CompileError>()) { + return input; + } + + Stops stops; + double previous = - std::numeric_limits<double>::infinity(); + for (std::size_t i = 3; i + 1 < length; i += 2) { + const auto& inputValue = toDouble(arrayMember(value, i)); + if (!inputValue) { + return CompileError { + "Input/output pairs for \"curve\" expressions must be defined using literal numeric values (not computed expressions) for the input values.", + ctx.key(i) + }; + } + if (*inputValue < previous) { + return CompileError { + "Input/output pairs for \"curve\" expressions must be arranged with input values in strictly ascending order.", + ctx.key(i) + }; + } + previous = *inputValue; + + auto output = parseExpression(arrayMember(value, i + 1), ParsingContext(ctx, {i + 1}, {"curve"})); + if (output.template is<CompileError>()) { + return output; + } + stops.push_back(std::make_pair( + *inputValue, + std::move(output.template get<std::unique_ptr<UntypedExpression>>()) + )); + } + + return std::make_unique<UntypedCurve>(ctx.key(), + std::move(input.template get<std::unique_ptr<UntypedExpression>>()), + std::move(stops), + interpolation); + } + + TypecheckResult typecheck(std::vector<CompileError>& errors) const override { + auto checkedInput = input->typecheck(errors); + if (!checkedInput) { + return TypecheckResult(); + } + auto error = matchType(type::Number, (*checkedInput)->getType()); + if (error) { + errors.push_back({*error, input->getKey()}); + } + + optional<type::Type> outputType; + std::map<float, std::unique_ptr<TypedExpression>> checkedStops; + for (const auto& stop : stops) { + auto checkedOutput = stop.second->typecheck(errors); + if (!checkedOutput) { + continue; + } + if (!outputType) { + outputType = (*checkedOutput)->getType(); + } else { + error = matchType(*outputType, (*checkedOutput)->getType()); + if (error) { + errors.push_back({ *error, stop.second->getKey() }); + continue; + } + } + checkedStops.emplace(stop.first, std::move(*checkedOutput)); + } + + if (stops.size() > 0) return TypecheckResult(); + + return interpolation.match( + [&](const StepInterpolation&) -> TypecheckResult { + return {std::make_unique<TypedCurve<StepCurve>>( + *outputType, + std::move(*checkedInput), + StepCurve(std::move(checkedStops)))}; + }, + [&](const ExponentialInterpolation& interp) { + TypecheckResult result = outputType->match( + [&](const type::NumberType&) -> TypecheckResult { + return makeExponential<float>(*outputType, std::move(*checkedInput), std::move(checkedStops), interp.base); + }, + [&](const type::ColorType&) -> TypecheckResult { + return makeExponential<mbgl::Color>(*outputType, std::move(*checkedInput), std::move(checkedStops), interp.base); + }, + [&](const type::Array& arrayType) -> TypecheckResult { + if (toString(arrayType.itemType) == type::Number.getName() && arrayType.N) { + return makeExponential<std::vector<Value>>(*outputType, std::move(*checkedInput), std::move(checkedStops), interp.base); + } else { + return TypecheckResult(); + } + }, + [&](const auto&) { return TypecheckResult(); } + ); + if (!result) { + errors.push_back({"Type " + toString(*outputType) + " is not interpolatable, and thus cannot be used as an exponential curve's output type", stops.begin()->second->getKey() }); + } + return result; + } + ); + } + + +private: + template <typename T> + std::unique_ptr<TypedExpression> makeExponential(const type::Type type, std::unique_ptr<TypedExpression> checkedInput, std::map<float, std::unique_ptr<TypedExpression>> checkedStops, float base) const { + return std::make_unique<TypedCurve<ExponentialCurve<T>>>( + type, + std::move(checkedInput), + ExponentialCurve<T>(std::move(checkedStops), base) + ); + } + + std::unique_ptr<UntypedExpression> input; + Stops stops; + Interpolation interpolation; +}; + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/expression/let.hpp b/include/mbgl/style/expression/let.hpp new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/include/mbgl/style/expression/let.hpp diff --git a/include/mbgl/style/expression/match.hpp b/include/mbgl/style/expression/match.hpp new file mode 100644 index 0000000000..8bd2e1d238 --- /dev/null +++ b/include/mbgl/style/expression/match.hpp @@ -0,0 +1,287 @@ +#pragma once + +#include <mbgl/style/expression/expression.hpp> +#include <mbgl/style/expression/parsing_context.hpp> +#include <mbgl/style/conversion.hpp> + +namespace mbgl { +namespace style { +namespace expression { + +using InputType = variant<int64_t, std::string>; +using MatchKey = variant<InputType, std::vector<InputType>>; + +template <typename T> +class TypedMatch : public TypedExpression { +public: + TypedMatch(std::unique_ptr<TypedExpression> input_, + std::vector<std::pair<MatchKey, std::unique_ptr<TypedExpression>>> cases_, + std::unique_ptr<TypedExpression> otherwise_ + ) : TypedExpression(otherwise_->getType()), + input(std::move(input_)), + otherwise(std::move(otherwise_)) + { + for(auto& pair : cases_) { + pair.first.match( + [&](const InputType& key) { + cases.emplace(key.get<T>(), std::move(pair.second)); + }, + [&](const std::vector<InputType>& keys) { + std::shared_ptr<TypedExpression> output = std::move(pair.second); + for (const auto& key : keys) { + cases.emplace(key.get<T>(), output); + } + } + ); + } + } + + bool isFeatureConstant() const override { + if (!input->isFeatureConstant()) { return false; } + if (!otherwise->isFeatureConstant()) { return false; } + for (const auto& pair : cases) { + if (!pair.second->isFeatureConstant()) { return false; } + } + return true; + } + + bool isZoomConstant() const override { + if (!input->isZoomConstant()) { return false; } + if (!otherwise->isZoomConstant()) { return false; } + for (const auto& pair : cases) { + if (!pair.second->isZoomConstant()) { return false; } + } + return true; + } + + EvaluationResult evaluate(const EvaluationParameters& params) const override { + const auto& inputValue = input->evaluate<T>(params); + if (!inputValue) { + return inputValue.error(); + } + if (cases.find(*inputValue) == cases.end()) { + return otherwise->evaluate(params); + } + return cases.at(*inputValue)->evaluate(params); + } + +private: + std::unique_ptr<TypedExpression> input; + std::unordered_map<T, std::shared_ptr<TypedExpression>> cases; + std::unique_ptr<TypedExpression> otherwise; +}; + +template <> EvaluationResult TypedMatch<int64_t>::evaluate(const EvaluationParameters& params) const { + const auto& inputValue = input->evaluate<float>(params); + if (!inputValue) { + return inputValue.error(); + } + int64_t rounded = ceilf(*inputValue); + if (*inputValue != rounded || cases.find(rounded) == cases.end()) { + return otherwise->evaluate(params); + } + return cases.at(rounded)->evaluate(params); +} + +class UntypedMatch : public UntypedExpression { +public: + using Cases = std::vector<std::pair<MatchKey, std::unique_ptr<UntypedExpression>>>; + + UntypedMatch(std::string key, + std::unique_ptr<UntypedExpression> input_, + Cases cases_, + std::unique_ptr<UntypedExpression> otherwise_, + const type::Type& inputType_) : + UntypedExpression(key), + input(std::move(input_)), + cases(std::move(cases_)), + otherwise(std::move(otherwise_)), + inputType(inputType_) + {} + + template <class V> + static ParseResult parse(const V& value, const ParsingContext& ctx) { + using namespace mbgl::style::conversion; + + assert(isArray(value)); + auto length = arrayLength(value); + if (length < 3) { + return CompileError { + "Expected at least 2 arguments, but found only " + std::to_string(length - 1) + ".", + ctx.key() + }; + } + + // Expect odd-length array: ["match", input, 2 * (n pairs)..., otherwise] + if (length % 2 != 1) { + return CompileError { + "Missing final output value for \"match\" expression.", + ctx.key() + }; + } + + auto parsedInput = parseExpression(arrayMember(value, 1), ParsingContext(ctx, {1}, {"match"})); + if (parsedInput.template is<CompileError>()) { + return parsedInput; + } + + auto otherwise = parseExpression(arrayMember(value, length - 1), ParsingContext(ctx, {length - 1}, {"match"})); + if (otherwise.template is<CompileError>()) { + return otherwise; + } + + Cases cases; + optional<type::Type> inputType; + for (size_t i = 2; i + 1 < length; i += 2) { + auto output = parseExpression(arrayMember(value, i + 1), ParsingContext(ctx, {i + 1}, {"match"})); + if (output.template is<CompileError>()) { + return output.template get<CompileError>(); + } + + const auto& inputArg = arrayMember(value, i); + // Match pair inputs are provided as either a literal value or a + // raw JSON array of string / number / boolean values. + if (isArray(inputArg)) { + auto groupLength = arrayLength(inputArg); + if (groupLength == 0) return CompileError { + "Expected at least one input value.", + ctx.key(i) + }; + std::vector<InputType> inputGroup; + for (size_t j = 0; j < groupLength; j++) { + const auto& inputValue = parseInputValue(arrayMember(inputArg, j), ctx.key(i), inputType); + if (inputValue.template is<CompileError>()) { + return inputValue.template get<CompileError>(); + } + inputGroup.emplace_back(inputValue.template get<InputType>()); + } + cases.push_back(std::make_pair( + inputGroup, + std::move(output.template get<std::unique_ptr<UntypedExpression>>())) + ); + } else { + const auto& inputValue = parseInputValue(inputArg, ctx.key(i), inputType); + if (inputValue.template is<CompileError>()) { + return inputValue.template get<CompileError>(); + } + cases.push_back(std::make_pair( + inputValue.template get<InputType>(), + std::move(output.template get<std::unique_ptr<UntypedExpression>>())) + ); + } + } + + return std::make_unique<UntypedMatch>(ctx.key(), + std::move(parsedInput.template get<std::unique_ptr<UntypedExpression>>()), + std::move(cases), + std::move(otherwise.template get<std::unique_ptr<UntypedExpression>>()), + *inputType); + } + + template <typename V> + static variant<CompileError, InputType> parseInputValue(const V& input, const std::string& key, optional<type::Type>& inputType) { + using namespace mbgl::style::conversion; + optional<InputType> result; + optional<type::Type> type; + + auto value = toValue(input); + if (value && isIntegerValue(*value)) { + type = {type::Number}; + result = {*numericValue<int64_t>(*value)}; + } else if (value && value->template is<std::string>()) { + type = {type::String}; + result = {value->template get<std::string>()}; + } else { + return CompileError { + "Match inputs must be either literal integer or string values or arrays of integer or string values.", + key + }; + } + + if (!inputType) { + inputType = type; + } else { + const auto& expected = toString(*inputType); + const auto& actual = toString(*type); + // TODO: replace with real == check + if (expected != actual) { + return CompileError { + "Expected " + expected + " but found " + actual + " instead.", + key + }; + } + } + + return *result; + } + + TypecheckResult typecheck(std::vector<CompileError>& errors) const override { + auto checkedInput = input->typecheck(errors); + if (!checkedInput) { + return TypecheckResult(); + } + auto checkedOtherwise = otherwise->typecheck(errors); + if (!checkedOtherwise) { + return TypecheckResult(); + } + + auto error = matchType(inputType, (*checkedInput)->getType()); + if (error) { + errors.push_back({ *error, input->getKey() }); + } + + optional<type::Type> outputType; + std::vector<std::pair<MatchKey, std::unique_ptr<TypedExpression>>> checkedCases; + for (const auto& pair : cases) { + auto checkedOutput = pair.second->typecheck(errors); + if (!checkedOutput) continue; + if (!outputType) { + outputType = (*checkedOutput)->getType(); + } else { + error = matchType(*outputType, (*checkedOutput)->getType()); + if (error) { + errors.push_back({ *error, pair.second->getKey() }); + continue; + } + } + checkedCases.emplace_back(pair.first, std::move(*checkedOutput)); + } + + error = matchType(*outputType, (*checkedOtherwise)->getType()); + if (error) { + errors.push_back({ *error, otherwise->getKey() }); + } + + if (inputType.is<type::StringType>()) { + return TypecheckResult(std::make_unique<TypedMatch<std::string>>( + std::move(*checkedInput), std::move(checkedCases), std::move(*checkedOtherwise) + )); + } else if (inputType.is<type::NumberType>()) { + return TypecheckResult(std::make_unique<TypedMatch<int64_t>>( + std::move(*checkedInput), std::move(checkedCases), std::move(*checkedOtherwise) + )); + } + + assert(false); + return TypecheckResult(); + } + +private: + static bool isIntegerValue(const mbgl::Value& v) { + return v.match( + [] (uint64_t) { return true; }, + [] (int64_t) { return true; }, + [] (double t) { return t == ceilf(t); }, + [] (const auto&) { return false; } + ); + } + + std::unique_ptr<UntypedExpression> input; + Cases cases; + std::unique_ptr<UntypedExpression> otherwise; + type::Type inputType; +}; + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/expression/parse.hpp b/include/mbgl/style/expression/parse.hpp index e621441c3b..97ad880ae5 100644 --- a/include/mbgl/style/expression/parse.hpp +++ b/include/mbgl/style/expression/parse.hpp @@ -4,6 +4,9 @@ #include <mbgl/style/expression/parsing_context.hpp> #include <mbgl/style/expression/expression.hpp> #include <mbgl/style/expression/compound_expression.hpp> +#include <mbgl/style/expression/match.hpp> +#include <mbgl/style/expression/curve.hpp> +#include <mbgl/style/expression/coalesce.hpp> #include <mbgl/style/conversion.hpp> namespace mbgl { @@ -65,6 +68,14 @@ ParseResult parseExpression(const V& value, const ParsingContext& context) return UntypedLiteral::parse(arrayMember(value, 1), ParsingContext(context, {1}, {"literal"})); } + if (*op == "match") { + return UntypedMatch::parse(value, context); + } else if (*op == "curve") { + return UntypedCurve::parse(value, context); + } else if (*op == "coalesce") { + return UntypedCoalesce::parse(value, context); + } + return UntypedCompoundExpression::parse(value, context); } diff --git a/src/mbgl/style/expression/compound_expression.cpp b/src/mbgl/style/expression/compound_expression.cpp index 189cb0f6a1..6767842b9a 100644 --- a/src/mbgl/style/expression/compound_expression.cpp +++ b/src/mbgl/style/expression/compound_expression.cpp @@ -172,10 +172,6 @@ std::unordered_map<std::string, CompoundExpression::Definition> CompoundExpressi define("-", [](float a, float b) -> Result<float> { return a - b; }) ); -// if (*op == "string") return LambdaExpression::parse<Assertion<std::string>>(value, context); -// if (*op == "number") return LambdaExpression::parse<Assertion<float>>(value, context); -// if (*op == "boolean") return LambdaExpression::parse<Assertion<bool>>(value, context); -// if (*op == "array") return Array::parse(value, context); // if (*op == "to_string") return LambdaExpression::parse<ToString>(value, context); // if (*op == "to_number") return LambdaExpression::parse<ToNumber>(value, context); // if (*op == "to_boolean") return LambdaExpression::parse<ToBoolean>(value, context); |