From 8635dab4c38fcd67962819224093d0be95f5ed43 Mon Sep 17 00:00:00 2001 From: Chris Loer Date: Thu, 8 Feb 2018 15:23:29 -0800 Subject: [core] Implement Expression::serialize() Issue #10714 - Each expression stores its operator as a string, and default serialization is [operator, serialize(child1), ...] - Custom implementations of `serialize` for Expression types that don't follow the pattern - expression::Value -> mbgl::Value converter - node_expression bindings to expose `serialize` --- src/mbgl/style/expression/array_assertion.cpp | 19 ++++++++++ src/mbgl/style/expression/assertion.cpp | 4 +++ src/mbgl/style/expression/coercion.cpp | 7 ++++ src/mbgl/style/expression/compound_expression.cpp | 42 +++++++++++------------ src/mbgl/style/expression/interpolate.cpp | 24 +++++++++++++ src/mbgl/style/expression/let.cpp | 15 ++++++++ src/mbgl/style/expression/literal.cpp | 8 +++++ src/mbgl/style/expression/match.cpp | 36 +++++++++++++++++++ src/mbgl/style/expression/step.cpp | 12 +++++++ src/mbgl/style/expression/value.cpp | 33 +++++++++++++++++- 10 files changed, 177 insertions(+), 23 deletions(-) (limited to 'src/mbgl/style/expression') diff --git a/src/mbgl/style/expression/array_assertion.cpp b/src/mbgl/style/expression/array_assertion.cpp index 29f6a47b10..4049301b0b 100644 --- a/src/mbgl/style/expression/array_assertion.cpp +++ b/src/mbgl/style/expression/array_assertion.cpp @@ -81,6 +81,25 @@ ParseResult ArrayAssertion::parse(const Convertible& value, ParsingContext& ctx) )); } +mbgl::Value ArrayAssertion::serialize() const { + std::vector serialized; + serialized.emplace_back(getOperator()); + + + const auto array = getType().get(); + if (array.itemType.is() + || array.itemType.is() + || array.itemType.is()) { + serialized.emplace_back(type::toString(array.itemType)); + if (array.N) { + serialized.emplace_back(uint64_t(*array.N)); + } + } + + serialized.emplace_back(input->serialize()); + return serialized; +} + } // namespace expression } // namespace style } // namespace mbgl diff --git a/src/mbgl/style/expression/assertion.cpp b/src/mbgl/style/expression/assertion.cpp index 0187921af9..d6f3f1b584 100644 --- a/src/mbgl/style/expression/assertion.cpp +++ b/src/mbgl/style/expression/assertion.cpp @@ -35,6 +35,10 @@ ParseResult Assertion::parse(const Convertible& value, ParsingContext& ctx) { return ParseResult(std::make_unique(it->second, std::move(parsed))); } +std::string Assertion::getOperator() const { + return type::toString(getType()); +} + EvaluationResult Assertion::evaluate(const EvaluationContext& params) const { for (std::size_t i = 0; i < inputs.size(); i++) { EvaluationResult value = inputs[i]->evaluate(params); diff --git a/src/mbgl/style/expression/coercion.cpp b/src/mbgl/style/expression/coercion.cpp index 56ab33fcfd..d9cd3ffdc9 100644 --- a/src/mbgl/style/expression/coercion.cpp +++ b/src/mbgl/style/expression/coercion.cpp @@ -81,6 +81,13 @@ Coercion::Coercion(type::Type type_, std::vector> in } } +std::string Coercion::getOperator() const { + return getType().match( + [](const type::NumberType&) { return "to-number"; }, + [](const type::ColorType&) { return "to-color"; }, + [](const auto&) { assert(false); return ""; }); +} + using namespace mbgl::style::conversion; ParseResult Coercion::parse(const Convertible& value, ParsingContext& ctx) { static std::unordered_map types { diff --git a/src/mbgl/style/expression/compound_expression.cpp b/src/mbgl/style/expression/compound_expression.cpp index 42cb655024..86d968c521 100644 --- a/src/mbgl/style/expression/compound_expression.cpp +++ b/src/mbgl/style/expression/compound_expression.cpp @@ -42,20 +42,19 @@ template struct Signature : SignatureBase { using Args = std::array, sizeof...(Params)>; - Signature(R (*evaluate_)(Params...)) : + Signature(R (*evaluate_)(Params...), std::string name_) : SignatureBase( valueTypeToExpressionType>(), - std::vector {valueTypeToExpressionType>()...} + std::vector {valueTypeToExpressionType>()...}, + std::move(name_) ), - evaluate(evaluate_) - {} + evaluate(evaluate_) {} EvaluationResult apply(const EvaluationContext& evaluationParameters, const Args& args) const { return applyImpl(evaluationParameters, args, std::index_sequence_for{}); } - std::unique_ptr makeExpression(const std::string& name, - std::vector> args) const override { + std::unique_ptr makeExpression(std::vector> args) const override { typename Signature::Args argsArray; std::copy_n(std::make_move_iterator(args.begin()), sizeof...(Params), argsArray.begin()); return std::make_unique>(name, *this, std::move(argsArray)); @@ -80,16 +79,16 @@ template struct Signature&)> : SignatureBase { using Args = std::vector>; - Signature(R (*evaluate_)(const Varargs&)) : + Signature(R (*evaluate_)(const Varargs&), std::string name_) : SignatureBase( valueTypeToExpressionType>(), - VarargsType { valueTypeToExpressionType() } + VarargsType { valueTypeToExpressionType() }, + std::move(name_) ), evaluate(evaluate_) {} - std::unique_ptr makeExpression(const std::string& name, - std::vector> args) const override { + std::unique_ptr makeExpression(std::vector> args) const override { return std::make_unique>(name, *this, std::move(args)); }; @@ -115,16 +114,16 @@ template struct Signature : SignatureBase { using Args = std::array, sizeof...(Params)>; - Signature(R (*evaluate_)(const EvaluationContext&, Params...)) : + Signature(R (*evaluate_)(const EvaluationContext&, Params...), std::string name_) : SignatureBase( valueTypeToExpressionType>(), - std::vector {valueTypeToExpressionType>()...} + std::vector {valueTypeToExpressionType>()...}, + std::move(name_) ), evaluate(evaluate_) {} - std::unique_ptr makeExpression(const std::string& name, - std::vector> args) const override { + std::unique_ptr makeExpression(std::vector> args) const override { typename Signature::Args argsArray; std::copy_n(std::make_move_iterator(args.begin()), sizeof...(Params), argsArray.begin()); return std::make_unique>(name, *this, std::move(argsArray)); @@ -176,14 +175,14 @@ struct Signature::value>> using Definition = CompoundExpressionRegistry::Definition; template -static std::unique_ptr makeSignature(Fn evaluateFunction) { - return std::make_unique>(evaluateFunction); +static std::unique_ptr makeSignature(Fn evaluateFunction, std::string name) { + return std::make_unique>(evaluateFunction, std::move(name)); } std::unordered_map initializeDefinitions() { std::unordered_map definitions; auto define = [&](std::string name, auto fn) { - definitions[name].push_back(makeSignature(fn)); + definitions[name].push_back(makeSignature(fn, name)); }; define("e", []() -> Result { return 2.718281828459045; }); @@ -461,7 +460,7 @@ ParseResult parseCompoundExpression(const std::string name, const Convertible& v } args.push_back(std::move(*parsed)); } - return createCompoundExpression(name, definition, std::move(args), ctx); + return createCompoundExpression(definition, std::move(args), ctx); } @@ -469,12 +468,11 @@ ParseResult createCompoundExpression(const std::string& name, std::vector> args, ParsingContext& ctx) { - return createCompoundExpression(name, CompoundExpressionRegistry::definitions.at(name), std::move(args), ctx); + return createCompoundExpression(CompoundExpressionRegistry::definitions.at(name), std::move(args), ctx); } -ParseResult createCompoundExpression(const std::string& name, - const Definition& definition, +ParseResult createCompoundExpression(const Definition& definition, std::vector> args, ParsingContext& ctx) { @@ -512,7 +510,7 @@ ParseResult createCompoundExpression(const std::string& name, } if (signatureContext.getErrors().size() == 0) { - return ParseResult(signature->makeExpression(name, std::move(args))); + return ParseResult(signature->makeExpression(std::move(args))); } } diff --git a/src/mbgl/style/expression/interpolate.cpp b/src/mbgl/style/expression/interpolate.cpp index 4cb22a3e4f..30b2cba81b 100644 --- a/src/mbgl/style/expression/interpolate.cpp +++ b/src/mbgl/style/expression/interpolate.cpp @@ -216,6 +216,30 @@ std::vector> InterpolateBase::possibleOutputs() const { return result; } +template +mbgl::Value Interpolate::serialize() const { + std::vector serialized; + serialized.emplace_back(getOperator()); + + interpolator.match( + [&](const ExponentialInterpolator& exponential) { + serialized.emplace_back(std::vector{{ std::string("exponential"), exponential.base }}); + }, + [&](const CubicBezierInterpolator& cubicBezier) { + static const std::string cubicBezierTag("cubic-bezier"); + auto p1 = cubicBezier.ub.getP1(); + auto p2 = cubicBezier.ub.getP2(); + serialized.emplace_back(std::vector{{ cubicBezierTag, p1.first, p1.second, p2.first, p2.second }}); + } + ); + serialized.emplace_back(input->serialize()); + for (auto& entry : stops) { + serialized.emplace_back(entry.first); + serialized.emplace_back(entry.second->serialize()); + }; + return serialized; +} + } // namespace expression } // namespace style } // namespace mbgl diff --git a/src/mbgl/style/expression/let.cpp b/src/mbgl/style/expression/let.cpp index fe48138ac3..242a995b0b 100644 --- a/src/mbgl/style/expression/let.cpp +++ b/src/mbgl/style/expression/let.cpp @@ -65,6 +65,17 @@ ParseResult Let::parse(const Convertible& value, ParsingContext& ctx) { return ParseResult(std::make_unique(std::move(bindings_), std::move(*result_))); } +mbgl::Value Let::serialize() const { + std::vector serialized; + serialized.emplace_back(getOperator()); + for (auto entry : bindings) { + serialized.emplace_back(entry.first); + serialized.emplace_back(entry.second->serialize()); + } + serialized.emplace_back(result->serialize()); + return serialized; +} + EvaluationResult Var::evaluate(const EvaluationContext& params) const { return value->evaluate(params); } @@ -95,6 +106,10 @@ ParseResult Var::parse(const Convertible& value_, ParsingContext& ctx) { return ParseResult(std::make_unique(name_, std::move(*bindingValue))); } +mbgl::Value Var::serialize() const { + return std::vector{{ getOperator(), name }}; +} + } // namespace expression } // namespace style } // namespace mbgl diff --git a/src/mbgl/style/expression/literal.cpp b/src/mbgl/style/expression/literal.cpp index 7e79fcbfe6..8a63980dba 100644 --- a/src/mbgl/style/expression/literal.cpp +++ b/src/mbgl/style/expression/literal.cpp @@ -102,6 +102,14 @@ ParseResult Literal::parse(const Convertible& value, ParsingContext& ctx) { } } +mbgl::Value Literal::serialize() const { + if (getType().is() || getType().is()) { + return std::vector{{ getOperator(), *fromExpressionValue(value) }}; + } else { + return *fromExpressionValue(value); + } +} + } // namespace expression } // namespace style } // namespace mbgl diff --git a/src/mbgl/style/expression/match.cpp b/src/mbgl/style/expression/match.cpp index 0b2790b688..3d41f0bdd3 100644 --- a/src/mbgl/style/expression/match.cpp +++ b/src/mbgl/style/expression/match.cpp @@ -40,6 +40,42 @@ std::vector> Match::possibleOutputs() const { return result; } +template +mbgl::Value Match::serialize() const { + std::vector serialized; + serialized.emplace_back(getOperator()); + serialized.emplace_back(input->serialize()); + + // Sort so serialization has an arbitrary defined order, even though branch order doesn't affect evaluation + std::map> sortedBranches(branches.begin(), branches.end()); + + // Group branches by unique match expression to support condensed serializations + // of the form [case1, case2, ...] -> matchExpression + std::map outputLookup; + std::vector>> groupedByOutput; + for (auto& entry : sortedBranches) { + auto outputIndex = outputLookup.find(entry.second.get()); + if (outputIndex == outputLookup.end()) { + // First time seeing this output, add it to the end of the grouped list + outputLookup[entry.second.get()] = groupedByOutput.size(); + groupedByOutput.emplace_back(entry.second.get(), std::vector{{entry.first}}); + } else { + // We've seen this expression before, add the label to that output's group + groupedByOutput[outputIndex->second].second.emplace_back(entry.first); + } + }; + + for (auto& entry : groupedByOutput) { + entry.second.size() == 1 + ? serialized.emplace_back(entry.second[0]) // Only a single label matches this output expression + : serialized.emplace_back(entry.second); // Array of literal labels pointing to this output expression + serialized.emplace_back(entry.first->serialize()); // The output expression itself + } + + serialized.emplace_back(otherwise->serialize()); + return serialized; +} + template<> EvaluationResult Match::evaluate(const EvaluationContext& params) const { const EvaluationResult inputValue = input->evaluate(params); diff --git a/src/mbgl/style/expression/step.cpp b/src/mbgl/style/expression/step.cpp index 34537d48ae..ddaf9417cb 100644 --- a/src/mbgl/style/expression/step.cpp +++ b/src/mbgl/style/expression/step.cpp @@ -168,6 +168,18 @@ ParseResult Step::parse(const mbgl::style::conversion::Convertible& value, Parsi return ParseResult(std::make_unique(*outputType, std::move(*input), std::move(stops))); } +mbgl::Value Step::serialize() const { + std::vector serialized; + serialized.emplace_back(getOperator()); + serialized.emplace_back(input->serialize()); + for (auto& entry : stops) { + if (entry.first > -std::numeric_limits::infinity()) { + serialized.emplace_back(entry.first); + } + serialized.emplace_back(entry.second->serialize()); + } + return serialized; +} } // namespace expression } // namespace style diff --git a/src/mbgl/style/expression/value.cpp b/src/mbgl/style/expression/value.cpp index faa44e78aa..72779d4956 100644 --- a/src/mbgl/style/expression/value.cpp +++ b/src/mbgl/style/expression/value.cpp @@ -103,6 +103,37 @@ Value ValueConverter::toExpressionValue(const mbgl::Value& value) { return mbgl::Value::visit(value, FromMBGLValue()); } +mbgl::Value ValueConverter::fromExpressionValue(const Value& value) { + return value.match( + [&](const Color& color)->mbgl::Value { + return std::vector{ + std::string("rgba"), + double(255 * color.r / color.a), + double(255 * color.g / color.a), + double(255 * color.b / color.a), + double(color.a) + }; + }, + [&](const std::vector& values)->mbgl::Value { + std::vector converted; + converted.reserve(values.size()); + for (const Value& v : values) { + converted.emplace_back(fromExpressionValue(v)); + } + return converted; + }, + [&](const std::unordered_map& values)->mbgl::Value { + std::unordered_map converted; + converted.reserve(values.size()); + for(const auto& entry : values) { + converted.emplace(entry.first, fromExpressionValue(entry.second)); + } + return converted; + }, + [&](const auto& a)->mbgl::Value { return a; } + ); +} + Value ValueConverter::toExpressionValue(const float value) { return static_cast(value); } @@ -237,7 +268,7 @@ template <> type::Type valueTypeToExpressionType() { return typ template Value toExpressionValue(const mbgl::Value&); - +template optional fromExpressionValue(const Value&); // for to_rgba expression template type::Type valueTypeToExpressionType>(); -- cgit v1.2.1