From 7a487067098b538edd9932faa66036728f9350c6 Mon Sep 17 00:00:00 2001 From: Anand Thakker Date: Fri, 13 Oct 2017 13:40:46 -0400 Subject: WIP - update to runtime polymorphic conversion --- cmake/core-files.cmake | 1 + include/mbgl/style/conversion/expression.hpp | 3 +- include/mbgl/style/expression/expression.hpp | 3 - include/mbgl/style/expression/parse.hpp | 111 +------------------ .../style/expression/parse/array_assertion.hpp | 3 +- include/mbgl/style/expression/parse/at.hpp | 3 +- include/mbgl/style/expression/parse/case.hpp | 3 +- include/mbgl/style/expression/parse/coalesce.hpp | 3 +- .../style/expression/parse/compound_expression.hpp | 3 +- include/mbgl/style/expression/parse/curve.hpp | 5 +- include/mbgl/style/expression/parse/in.hpp | 3 +- include/mbgl/style/expression/parse/let.hpp | 6 +- include/mbgl/style/expression/parse/literal.hpp | 9 +- include/mbgl/style/expression/parse/match.hpp | 6 +- platform/node/src/node_expression.cpp | 10 +- src/mbgl/style/conversion/stringify.hpp | 2 +- src/mbgl/style/expression/parse.cpp | 119 +++++++++++++++++++++ test/style/conversion/function.test.cpp | 2 +- test/style/conversion/stringify.test.cpp | 6 +- 19 files changed, 146 insertions(+), 155 deletions(-) create mode 100644 src/mbgl/style/expression/parse.cpp diff --git a/cmake/core-files.cmake b/cmake/core-files.cmake index 78454fb43b..e5700f845d 100644 --- a/cmake/core-files.cmake +++ b/cmake/core-files.cmake @@ -422,6 +422,7 @@ set(MBGL_CORE_FILES src/mbgl/style/expression/in.cpp src/mbgl/style/expression/let.cpp src/mbgl/style/expression/match.cpp + src/mbgl/style/expression/parse.cpp src/mbgl/style/expression/value.cpp # style/expression/parse diff --git a/include/mbgl/style/conversion/expression.hpp b/include/mbgl/style/conversion/expression.hpp index 8d0c30351d..4a8cf1030e 100644 --- a/include/mbgl/style/conversion/expression.hpp +++ b/include/mbgl/style/conversion/expression.hpp @@ -12,8 +12,7 @@ namespace conversion { using namespace mbgl::style::expression; template<> struct Converter> { - template - optional> operator()(const V& value, Error& error, type::Type expected) const { + optional> operator()(const mbgl::style::conversion::Value& value, Error& error, type::Type expected) const { std::vector errors; ParseResult parsed = parseExpression(value, ParsingContext(errors, expected)); if (parsed) { diff --git a/include/mbgl/style/expression/expression.hpp b/include/mbgl/style/expression/expression.hpp index 8be2ae162c..e9a1cca658 100644 --- a/include/mbgl/style/expression/expression.hpp +++ b/include/mbgl/style/expression/expression.hpp @@ -9,8 +9,6 @@ #include #include #include -#include - namespace mbgl { @@ -119,7 +117,6 @@ private: using ParseResult = optional>; - } // namespace expression } // namespace style } // namespace mbgl diff --git a/include/mbgl/style/expression/parse.hpp b/include/mbgl/style/expression/parse.hpp index 2b5bab7750..ae0d62732b 100644 --- a/include/mbgl/style/expression/parse.hpp +++ b/include/mbgl/style/expression/parse.hpp @@ -1,23 +1,9 @@ #pragma once #include -#include -#include +#include #include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include namespace mbgl { @@ -32,100 +18,7 @@ using namespace mbgl::style; type (either Literal, or the one named in value[0]) and dispatching to the appropriate ParseXxxx::parse(const V&, ParsingContext) method. */ -template -ParseResult parseExpression(const V& value, ParsingContext context) -{ - using namespace mbgl::style::conversion; - - ParseResult parsed; - - if (isArray(value)) { - const std::size_t length = arrayLength(value); - if (length == 0) { - context.error(R"(Expected an array with at least one element. If you wanted a literal array, use ["literal", []].)"); - return ParseResult(); - } - - const optional op = toString(arrayMember(value, 0)); - if (!op) { - context.error( - "Expression name must be a string, but found " + getJSONType(arrayMember(value, 0)) + - R"( instead. If you wanted a literal array, use ["literal", [...]].)", - 0 - ); - return ParseResult(); - } - - if (*op == "literal") { - if (length != 2) { - context.error( - "'literal' expression requires exactly one argument, but found " + std::to_string(length - 1) + " instead." - ); - return ParseResult(); - } - - 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 = ParseCoalesce::parse(value, context); - } else if (*op == "case") { - parsed = ParseCase::parse(value, context); - } else if (*op == "array") { - parsed = ParseArrayAssertion::parse(value, context); - } else if (*op == "let") { - parsed = ParseLet::parse(value, context); - } else if (*op == "var") { - parsed = ParseVar::parse(value, context); - } else if (*op == "at") { - parsed = ParseAt::parse(value, context); - } else if (*op == "contains") { - parsed = ParseIn::parse(value, context); - } else { - parsed = ParseCompoundExpression::parse(*op, value, context); - } - } else { - if (isObject(value)) { - context.error(R"(Bare objects invalid. Use ["literal", {...}] instead.)"); - return ParseResult(); - } - - parsed = ParseLiteral::parse(value, context); - } - - if (!parsed) { - assert(context.errors.size() > 0); - } else if (context.expected) { - auto wrapForType = [&](const std::string& wrapper, std::unique_ptr expression) { - std::vector> args; - args.push_back(std::move(expression)); - return createCompoundExpression(wrapper, std::move(args), context); - }; - - const type::Type actual = (*parsed)->getType(); - const type::Type expected = *context.expected; - if (expected == type::Color && (actual == type::String || actual == type::Value)) { - parsed = wrapForType("to-color", std::move(*parsed)); - } else if (expected != type::Value && actual == type::Value) { - if (expected == type::String) { - parsed = wrapForType("string", std::move(*parsed)); - } else if (expected == type::Number) { - parsed = wrapForType("number", std::move(*parsed)); - } else if (expected == type::Boolean) { - parsed = wrapForType("boolean", std::move(*parsed)); - } - } - - checkSubtype(*(context.expected), (*parsed)->getType(), context); - if (context.errors.size() > 0) { - return ParseResult(); - } - } - - return parsed; -} +ParseResult parseExpression(const mbgl::style::conversion::Value& value, ParsingContext context); } // namespace expression diff --git a/include/mbgl/style/expression/parse/array_assertion.hpp b/include/mbgl/style/expression/parse/array_assertion.hpp index e19573d23c..fe86c670b0 100644 --- a/include/mbgl/style/expression/parse/array_assertion.hpp +++ b/include/mbgl/style/expression/parse/array_assertion.hpp @@ -11,8 +11,7 @@ namespace style { namespace expression { struct ParseArrayAssertion { - template - static ParseResult parse(const V& value, ParsingContext ctx) { + static ParseResult parse(const mbgl::style::conversion::Value& value, ParsingContext ctx) { using namespace mbgl::style::conversion; static std::unordered_map itemTypes { diff --git a/include/mbgl/style/expression/parse/at.hpp b/include/mbgl/style/expression/parse/at.hpp index 22af5dc507..ed2f3c9ead 100644 --- a/include/mbgl/style/expression/parse/at.hpp +++ b/include/mbgl/style/expression/parse/at.hpp @@ -10,8 +10,7 @@ namespace style { namespace expression { struct ParseAt { - template - static ParseResult parse(const V& value, ParsingContext ctx) { + static ParseResult parse(const mbgl::style::conversion::Value& value, ParsingContext ctx) { using namespace mbgl::style::conversion; assert(isArray(value)); diff --git a/include/mbgl/style/expression/parse/case.hpp b/include/mbgl/style/expression/parse/case.hpp index 43aa725daa..4465182f8f 100644 --- a/include/mbgl/style/expression/parse/case.hpp +++ b/include/mbgl/style/expression/parse/case.hpp @@ -10,8 +10,7 @@ namespace style { namespace expression { struct ParseCase { - template - static ParseResult parse(const V& value, ParsingContext ctx) { + static ParseResult parse(const mbgl::style::conversion::Value& value, ParsingContext ctx) { using namespace mbgl::style::conversion; assert(isArray(value)); diff --git a/include/mbgl/style/expression/parse/coalesce.hpp b/include/mbgl/style/expression/parse/coalesce.hpp index a9d3214130..2e902cdc41 100644 --- a/include/mbgl/style/expression/parse/coalesce.hpp +++ b/include/mbgl/style/expression/parse/coalesce.hpp @@ -12,8 +12,7 @@ namespace style { namespace expression { struct ParseCoalesce { - template - static ParseResult parse(const V& value, ParsingContext ctx) { + static ParseResult parse(const mbgl::style::conversion::Value& value, ParsingContext ctx) { using namespace mbgl::style::conversion; assert(isArray(value)); auto length = arrayLength(value); diff --git a/include/mbgl/style/expression/parse/compound_expression.hpp b/include/mbgl/style/expression/parse/compound_expression.hpp index e9cc577642..ed91b14668 100644 --- a/include/mbgl/style/expression/parse/compound_expression.hpp +++ b/include/mbgl/style/expression/parse/compound_expression.hpp @@ -14,8 +14,7 @@ namespace style { namespace expression { struct ParseCompoundExpression { - template - static ParseResult parse(const std::string name, const V& value, ParsingContext ctx) { + static ParseResult parse(const std::string name, const mbgl::style::conversion::Value& value, ParsingContext ctx) { using namespace mbgl::style::conversion; assert(isArray(value) && arrayLength(value) > 0); diff --git a/include/mbgl/style/expression/parse/curve.hpp b/include/mbgl/style/expression/parse/curve.hpp index 1c7ba8d270..94d53ecef2 100644 --- a/include/mbgl/style/expression/parse/curve.hpp +++ b/include/mbgl/style/expression/parse/curve.hpp @@ -17,8 +17,7 @@ struct ParseCurve { ExponentialInterpolator, CubicBezierInterpolator>; - template - static ParseResult parse(const V& value, ParsingContext ctx) { + static ParseResult parse(const mbgl::style::conversion::Value& value, ParsingContext ctx) { using namespace mbgl::style::conversion; assert(isArray(value)); @@ -30,7 +29,7 @@ struct ParseCurve { ctx.error("Expected an interpolation type expression."); return ParseResult(); } - const V& interp = arrayMember(value, 1); + const mbgl::style::conversion::Value& interp = arrayMember(value, 1); if (!isArray(interp) || arrayLength(interp) == 0) { ctx.error("Expected an interpolation type expression."); return ParseResult(); diff --git a/include/mbgl/style/expression/parse/in.hpp b/include/mbgl/style/expression/parse/in.hpp index 2cc5994c9f..94df87807d 100644 --- a/include/mbgl/style/expression/parse/in.hpp +++ b/include/mbgl/style/expression/parse/in.hpp @@ -10,8 +10,7 @@ namespace style { namespace expression { struct ParseIn { - template - static ParseResult parse(const V& value, ParsingContext ctx) { + static ParseResult parse(const mbgl::style::conversion::Value& value, ParsingContext ctx) { using namespace mbgl::style::conversion; assert(isArray(value)); diff --git a/include/mbgl/style/expression/parse/let.hpp b/include/mbgl/style/expression/parse/let.hpp index a4017de41b..042c186a47 100644 --- a/include/mbgl/style/expression/parse/let.hpp +++ b/include/mbgl/style/expression/parse/let.hpp @@ -14,8 +14,7 @@ namespace style { namespace expression { struct ParseLet { - template - static ParseResult parse(const V& value, ParsingContext ctx) { + static ParseResult parse(const mbgl::style::conversion::Value& value, ParsingContext ctx) { using namespace mbgl::style::conversion; assert(isArray(value)); @@ -61,8 +60,7 @@ struct ParseLet { }; struct ParseVar { - template - static ParseResult parse(const V& value, ParsingContext ctx) { + static ParseResult parse(const mbgl::style::conversion::Value& value, ParsingContext ctx) { using namespace mbgl::style::conversion; assert(isArray(value)); diff --git a/include/mbgl/style/expression/parse/literal.hpp b/include/mbgl/style/expression/parse/literal.hpp index 1b4a00b074..90bea956a4 100644 --- a/include/mbgl/style/expression/parse/literal.hpp +++ b/include/mbgl/style/expression/parse/literal.hpp @@ -15,8 +15,7 @@ namespace style { namespace expression { struct ParseLiteral { - template - static ParseResult parse(const V& value, ParsingContext ctx) { + static ParseResult parse(const mbgl::style::conversion::Value& value, ParsingContext ctx) { const optional parsedValue = parseValue(value, ctx); if (!parsedValue) { @@ -40,14 +39,14 @@ struct ParseLiteral { } return ParseResult(std::make_unique(*parsedValue)); } - template - static optional parseValue(const V& value, ParsingContext ctx) { + + static optional parseValue(const mbgl::style::conversion::Value& value, ParsingContext ctx) { using namespace mbgl::style::conversion; if (isUndefined(value)) return {Null}; if (isObject(value)) { std::unordered_map result; bool error = false; - eachMember(value, [&] (const std::string& k, const V& v) -> optional { + eachMember(value, [&] (const std::string& k, const mbgl::style::conversion::Value& v) -> optional { if (!error) { optional memberValue = parseValue(v, ctx); if (memberValue) { diff --git a/include/mbgl/style/expression/parse/match.hpp b/include/mbgl/style/expression/parse/match.hpp index cc73b76b48..6841d9b94d 100644 --- a/include/mbgl/style/expression/parse/match.hpp +++ b/include/mbgl/style/expression/parse/match.hpp @@ -12,8 +12,7 @@ namespace style { namespace expression { struct ParseMatch { - template - static ParseResult parse(const V& value, ParsingContext ctx) { + static ParseResult parse(const mbgl::style::conversion::Value& value, ParsingContext ctx) { using namespace mbgl::style::conversion; assert(isArray(value)); @@ -108,8 +107,7 @@ struct ParseMatch { } private: - template - static optional parseInputValue(const V& input, ParsingContext ctx, optional& inputType) { + static optional parseInputValue(const mbgl::style::conversion::Value& input, ParsingContext ctx, optional& inputType) { using namespace mbgl::style::conversion; optional result; optional type; diff --git a/platform/node/src/node_expression.cpp b/platform/node/src/node_expression.cpp index d6df874d56..6f647dcf07 100644 --- a/platform/node/src/node_expression.cpp +++ b/platform/node/src/node_expression.cpp @@ -72,7 +72,7 @@ void NodeExpression::Parse(const Nan::FunctionCallbackInfo& info) { try { std::vector errors; - ParseResult parsed = parseExpression(expr, ParsingContext(errors, expected)); + ParseResult parsed = parseExpression(mbgl::style::conversion::Value(expr), ParsingContext(errors, expected)); if (parsed) { assert(errors.size() == 0); auto nodeExpr = new NodeExpression(std::move(*parsed)); @@ -174,15 +174,9 @@ void NodeExpression::Evaluate(const Nan::FunctionCallbackInfo& info) float zoom = info[0]->NumberValue(); - // Pending https://github.com/mapbox/mapbox-gl-native/issues/5623, - // stringify the geojson feature in order to use convert() Nan::JSON NanJSON; - Nan::MaybeLocal geojsonString = NanJSON.Stringify(Nan::To(info[1]).ToLocalChecked()); - if (geojsonString.IsEmpty()) { - return Nan::ThrowTypeError("couldn't stringify JSON"); - } conversion::Error conversionError; - mbgl::optional geoJSON = conversion::convert(*Nan::Utf8String(geojsonString.ToLocalChecked()), conversionError); + mbgl::optional geoJSON = conversion::convert(info[1], conversionError); if (!geoJSON) { Nan::ThrowTypeError(conversionError.message.c_str()); return; diff --git a/src/mbgl/style/conversion/stringify.hpp b/src/mbgl/style/conversion/stringify.hpp index 42b694c852..2469e4cfe3 100644 --- a/src/mbgl/style/conversion/stringify.hpp +++ b/src/mbgl/style/conversion/stringify.hpp @@ -76,7 +76,7 @@ void stringify(Writer& writer, const std::array& v) { } template -void stringify(Writer&, const Value&); +void stringify(Writer&, const mbgl::Value&); template void stringify(Writer& writer, const std::vector& v) { diff --git a/src/mbgl/style/expression/parse.cpp b/src/mbgl/style/expression/parse.cpp new file mode 100644 index 0000000000..173816ecc7 --- /dev/null +++ b/src/mbgl/style/expression/parse.cpp @@ -0,0 +1,119 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace mbgl { +namespace style { +namespace expression { + +ParseResult parseExpression(const mbgl::style::conversion::Value& value, ParsingContext context) +{ + using namespace mbgl::style::conversion; + + ParseResult parsed; + + if (isArray(value)) { + const std::size_t length = arrayLength(value); + if (length == 0) { + context.error(R"(Expected an array with at least one element. If you wanted a literal array, use ["literal", []].)"); + return ParseResult(); + } + + const optional op = toString(arrayMember(value, 0)); + if (!op) { + context.error( + "Expression name must be a string, but found " + getJSONType(arrayMember(value, 0)) + + R"( instead. If you wanted a literal array, use ["literal", [...]].)", + 0 + ); + return ParseResult(); + } + + if (*op == "literal") { + if (length != 2) { + context.error( + "'literal' expression requires exactly one argument, but found " + std::to_string(length - 1) + " instead." + ); + return ParseResult(); + } + + 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 = ParseCoalesce::parse(value, context); + } else if (*op == "case") { + parsed = ParseCase::parse(value, context); + } else if (*op == "array") { + parsed = ParseArrayAssertion::parse(value, context); + } else if (*op == "let") { + parsed = ParseLet::parse(value, context); + } else if (*op == "var") { + parsed = ParseVar::parse(value, context); + } else if (*op == "at") { + parsed = ParseAt::parse(value, context); + } else if (*op == "contains") { + parsed = ParseIn::parse(value, context); + } else { + parsed = ParseCompoundExpression::parse(*op, value, context); + } + } else { + if (isObject(value)) { + context.error(R"(Bare objects invalid. Use ["literal", {...}] instead.)"); + return ParseResult(); + } + + parsed = ParseLiteral::parse(value, context); + } + + if (!parsed) { + assert(context.errors.size() > 0); + } else if (context.expected) { + auto wrapForType = [&](const std::string& wrapper, std::unique_ptr expression) { + std::vector> args; + args.push_back(std::move(expression)); + return createCompoundExpression(wrapper, std::move(args), context); + }; + + const type::Type actual = (*parsed)->getType(); + const type::Type expected = *context.expected; + if (expected == type::Color && (actual == type::String || actual == type::Value)) { + parsed = wrapForType("to-color", std::move(*parsed)); + } else if (expected != type::Value && actual == type::Value) { + if (expected == type::String) { + parsed = wrapForType("string", std::move(*parsed)); + } else if (expected == type::Number) { + parsed = wrapForType("number", std::move(*parsed)); + } else if (expected == type::Boolean) { + parsed = wrapForType("boolean", std::move(*parsed)); + } + } + + checkSubtype(*(context.expected), (*parsed)->getType(), context); + if (context.errors.size() > 0) { + return ParseResult(); + } + } + + return parsed; +} + +} // namespace expression +} // namespace style +} // namespace mbgl + diff --git a/test/style/conversion/function.test.cpp b/test/style/conversion/function.test.cpp index 1bddc691f7..10683458ed 100644 --- a/test/style/conversion/function.test.cpp +++ b/test/style/conversion/function.test.cpp @@ -59,7 +59,7 @@ TEST(StyleConversion, CompositeFunctionExpression) { auto parseFunction = [&](const std::string& src) { JSDocument doc; doc.Parse<0>(src); - return convert, JSValue>(doc, error); + return convert>(doc, error); }; auto fn1 = parseFunction(R"({"expression": ["curve", ["linear"], ["zoom"], 0, ["number", ["get", "x"]], 10, 10]})"); diff --git a/test/style/conversion/stringify.test.cpp b/test/style/conversion/stringify.test.cpp index 0b2940a0e0..4b0e828b31 100644 --- a/test/style/conversion/stringify.test.cpp +++ b/test/style/conversion/stringify.test.cpp @@ -69,9 +69,9 @@ TEST(Stringify, Map) { } TEST(Stringify, Value) { - ASSERT_EQ(stringify(Value(true)), "true"); - ASSERT_EQ(stringify(Value(uint64_t(0))), "0"); - ASSERT_EQ(stringify(Value(1.2)), "1.2"); + ASSERT_EQ(stringify(mbgl::Value(true)), "true"); + ASSERT_EQ(stringify(mbgl::Value(uint64_t(0))), "0"); + ASSERT_EQ(stringify(mbgl::Value(1.2)), "1.2"); } TEST(Stringify, Filter) { -- cgit v1.2.1