#include #include #include #include #include #include #include #include #include #include #include #include namespace mbgl { namespace style { namespace conversion { using namespace expression; using namespace expression::dsl; const static std::string tokenReservedChars = "{}"; bool hasTokens(const std::string& source) { auto pos = source.begin(); const auto end = source.end(); while (pos != end) { auto brace = std::find(pos, end, '{'); if (brace == end) return false; for (brace++; brace != end && tokenReservedChars.find(*brace) == std::string::npos; brace++); if (brace != end && *brace == '}') { return true; } pos = brace; } return false; } std::unique_ptr convertTokenStringToFormatExpression(const std::string& source) { auto textExpression = convertTokenStringToExpression(source); std::vector sections; sections.emplace_back(std::move(textExpression), nullopt, nullopt, nullopt); return std::make_unique(sections); } std::unique_ptr convertTokenStringToExpression(const std::string& source) { std::vector> inputs; auto pos = source.begin(); const auto end = source.end(); while (pos != end) { auto brace = std::find(pos, end, '{'); if (pos != brace) { inputs.push_back(literal(std::string(pos, brace))); } pos = brace; if (pos != end) { for (brace++; brace != end && tokenReservedChars.find(*brace) == std::string::npos; brace++); if (brace != end && *brace == '}') { inputs.push_back(get(literal(std::string(pos + 1, brace)))); pos = brace + 1; } else { inputs.push_back(literal(std::string(pos, brace))); pos = brace; } } } switch (inputs.size()) { case 0: return literal(source); case 1: return toString(std::move(inputs[0])); default: return concat(std::move(inputs)); } } template optional> convertFunctionToExpression(const Convertible& value, Error& error, bool convertTokens) { auto expression = convertFunctionToExpression(expression::valueTypeToExpressionType(), value, error, convertTokens); if (!expression) { return nullopt; } optional defaultValue{}; auto defaultValueValue = objectMember(value, "default"); if (defaultValueValue) { defaultValue = convert(*defaultValueValue, error); if (!defaultValue) { error.message = R"(wrong type for "default": )" + error.message; return nullopt; } } return PropertyExpression(std::move(*expression), defaultValue); } template optional> convertFunctionToExpression(const Convertible&, Error&, bool); template optional> convertFunctionToExpression(const Convertible&, Error&, bool); template optional> convertFunctionToExpression(const Convertible&, Error&, bool); template optional> convertFunctionToExpression(const Convertible&, Error&, bool); template optional> convertFunctionToExpression(const Convertible&, Error&, bool); template optional> convertFunctionToExpression(const Convertible&, Error&, bool); template optional> convertFunctionToExpression(const Convertible&, Error&, bool); template optional> convertFunctionToExpression(const Convertible&, Error&, bool); template optional> convertFunctionToExpression(const Convertible&, Error&, bool); template optional> convertFunctionToExpression(const Convertible&, Error&, bool); template optional> convertFunctionToExpression(const Convertible&, Error&, bool); template optional> convertFunctionToExpression(const Convertible&, Error&, bool); template optional>> convertFunctionToExpression>(const Convertible&, Error&, bool); template optional>> convertFunctionToExpression>(const Convertible&, Error&, bool); template optional> convertFunctionToExpression(const Convertible&, Error&, bool); template optional>> convertFunctionToExpression>(const Convertible&, Error&, bool); template optional>> convertFunctionToExpression>(const Convertible&, Error&, bool); template optional> convertFunctionToExpression(const Convertible&, Error&, bool); template optional>> convertFunctionToExpression>(const Convertible&, Error&, bool); template optional> convertFunctionToExpression(const Convertible&, Error&, bool); template optional> convertFunctionToExpression(const Convertible&, Error&, bool); template optional> convertFunctionToExpression(const Convertible&, Error&, bool); template optional> convertFunctionToExpression(const Convertible&, Error&, bool); template optional> convertFunctionToExpression(const Convertible&, Error&, bool); template optional> convertFunctionToExpression(const Convertible&, Error&, bool); template optional> convertFunctionToExpression(const Convertible&, Error&, bool); // Ad-hoc Converters for double and int64_t. We should replace float with double wholesale, // and promote the int64_t Converter to general use (and it should check that the input is // an integer). template <> struct Converter { optional operator()(const Convertible& value, Error& error) const { auto converted = convert(value, error); if (!converted) { return nullopt; } return *converted; } }; template <> struct Converter { optional operator()(const Convertible& value, Error& error) const { auto converted = convert(value, error); if (!converted) { return nullopt; } return *converted; } }; enum class FunctionType { Interval, Exponential, Categorical, Identity, Invalid }; static bool interpolatable(type::Type type) { return type.match( [&] (const type::NumberType&) { return true; }, [&] (const type::ColorType&) { return true; }, [&] (const type::Array& array) { return array.N && array.itemType == type::Number; }, [&] (const auto&) { return false; } ); } static optional> convertLiteral(type::Type type, const Convertible& value, Error& error, bool convertTokens = false) { return type.match( [&] (const type::NumberType&) -> optional> { auto result = convert(value, error); if (!result) { return nullopt; } return literal(double(*result)); }, [&] (const type::BooleanType&) -> optional> { auto result = convert(value, error); if (!result) { return nullopt; } return literal(*result); }, [&] (const type::StringType&) -> optional> { auto result = convert(value, error); if (!result) { return nullopt; } return convertTokens ? convertTokenStringToExpression(*result) : literal(*result); }, [&] (const type::ColorType&) -> optional> { auto result = convert(value, error); if (!result) { return nullopt; } return literal(*result); }, [&] (const type::Array& array) -> optional> { if (!isArray(value)) { error.message = "value must be an array"; return nullopt; } if (array.N && arrayLength(value) != *array.N) { error.message = "value must be an array of length " + util::toString(*array.N); return nullopt; } return array.itemType.match( [&] (const type::NumberType&) -> optional> { std::vector result; result.reserve(arrayLength(value)); for (std::size_t i = 0; i < arrayLength(value); ++i) { optional number = toNumber(arrayMember(value, i)); if (!number) { error.message = "value must be an array of numbers"; return nullopt; } result.emplace_back(double(*number)); } return literal(result); }, [&] (const type::StringType&) -> optional> { std::vector result; result.reserve(arrayLength(value)); for (std::size_t i = 0; i < arrayLength(value); ++i) { optional string = toString(arrayMember(value, i)); if (!string) { error.message = "value must be an array of strings"; return nullopt; } result.emplace_back(*string); } return literal(result); }, [&] (const auto&) -> optional> { assert(false); // No properties use this type. return nullopt; } ); }, [&] (const type::NullType&) -> optional> { assert(false); // No properties use this type. return nullopt; }, [&] (const type::ObjectType&) -> optional> { assert(false); // No properties use this type. return nullopt; }, [&] (const type::ErrorType&) -> optional> { assert(false); // No properties use this type. return nullopt; }, [&] (const type::ValueType&) -> optional> { assert(false); // No properties use this type. return nullopt; }, [&] (const type::CollatorType&) -> optional> { assert(false); // No properties use this type. return nullopt; }, [&] (const type::FormattedType&) -> optional> { auto result = convert(value, error); if (!result) { return nullopt; } return convertTokens ? convertTokenStringToFormatExpression(*result) : literal(Formatted(result->c_str())); } ); } static optional>> convertStops(type::Type type, const Convertible& value, Error& error, bool convertTokens) { auto stopsValue = objectMember(value, "stops"); if (!stopsValue) { error.message = "function value must specify stops"; return nullopt; } if (!isArray(*stopsValue)) { error.message = "function stops must be an array"; return nullopt; } if (arrayLength(*stopsValue) == 0) { error.message = "function must have at least one stop"; return nullopt; } std::map> stops; for (std::size_t i = 0; i < arrayLength(*stopsValue); ++i) { const auto& stopValue = arrayMember(*stopsValue, i); if (!isArray(stopValue)) { error.message = "function stop must be an array"; return nullopt; } if (arrayLength(stopValue) != 2) { error.message = "function stop must have two elements"; return nullopt; } optional t = convert(arrayMember(stopValue, 0), error); if (!t) { return nullopt; } optional> e = convertLiteral(type, arrayMember(stopValue, 1), error, convertTokens); if (!e) { return nullopt; } stops.emplace(*t, std::move(*e)); } return { std::move(stops) }; } static void omitFirstStop(std::map>& stops) { double min = std::numeric_limits::max(); for (auto& s : stops) { if (s.first < min) { min = s.first; } } stops.emplace(-std::numeric_limits::infinity(), std::move(stops[min])); stops.erase(min); } template optional>> convertBranches(type::Type type, const Convertible& value, Error& error) { auto stopsValue = objectMember(value, "stops"); if (!stopsValue) { error.message = "function value must specify stops"; return nullopt; } if (!isArray(*stopsValue)) { error.message = "function stops must be an array"; return nullopt; } if (arrayLength(*stopsValue) == 0) { error.message = "function must have at least one stop"; return nullopt; } std::map> stops; for (std::size_t i = 0; i < arrayLength(*stopsValue); ++i) { const auto& stopValue = arrayMember(*stopsValue, i); if (!isArray(stopValue)) { error.message = "function stop must be an array"; return nullopt; } if (arrayLength(stopValue) != 2) { error.message = "function stop must have two elements"; return nullopt; } optional t = convert(arrayMember(stopValue, 0), error); if (!t) { return nullopt; } optional> e = convertLiteral(type, arrayMember(stopValue, 1), error); if (!e) { return nullopt; } stops.emplace(*t, std::move(*e)); } return { std::move(stops) }; } static optional convertBase(const Convertible& value, Error& error) { auto baseValue = objectMember(value, "base"); if (!baseValue) { return 1.0; } auto base = toNumber(*baseValue); if (!base) { error.message = "function base must be a number"; return nullopt; } return *base; } static std::unique_ptr step(type::Type type, std::unique_ptr input, std::map> stops) { return std::make_unique(type, std::move(input), std::move(stops)); } static std::unique_ptr interpolate(type::Type type, Interpolator interpolator, std::unique_ptr input, std::map> stops) { ParsingContext ctx; auto result = createInterpolate(type, std::move(interpolator), std::move(input), std::move(stops), ctx); if (!result) { assert(false); return {}; } return std::move(*result); } template std::unique_ptr categorical(type::Type type, const std::string& property, std::map> branches, std::unique_ptr def) { std::unordered_map> convertedBranches; for (auto& b : branches) { convertedBranches[b.first] = std::move(b.second); } return std::make_unique>(type, get(literal(property)), std::move(convertedBranches), def ? std::move(def) : error("replaced with default")); } template <> std::unique_ptr categorical(type::Type type, const std::string& property, std::map> branches, std::unique_ptr def) { auto it = branches.find(true); std::unique_ptr trueCase = it == branches.end() ? error("replaced with default") : std::move(it->second); it = branches.find(false); std::unique_ptr falseCase = it == branches.end() ? error("replaced with default") : std::move(it->second); std::vector convertedBranches; convertedBranches.emplace_back(eq(get(literal(property)), literal(Value(true))), std::move(trueCase)); convertedBranches.emplace_back(eq(get(literal(property)), literal(Value(false))), std::move(falseCase)); return std::make_unique(type, std::move(convertedBranches), def ? std::move(def) : error("replaced with default")); } static std::unique_ptr numberOrDefault(type::Type type, std::unique_ptr get, std::unique_ptr expr, std::unique_ptr def) { if (!def) { return expr; } std::vector branches; branches.emplace_back(eq(compound("typeof", std::move(get)), literal("number")), std::move(expr)); return std::make_unique(type, std::move(branches), std::move(def)); } static optional> convertIntervalFunction(type::Type type, const Convertible& value, Error& error, std::function (bool)> makeInput, std::unique_ptr def, bool convertTokens = false) { auto stops = convertStops(type, value, error, convertTokens); if (!stops) { return nullopt; } omitFirstStop(*stops); return numberOrDefault(type, makeInput(false), step(type, makeInput(true), std::move(*stops)), std::move(def)); } static optional> convertExponentialFunction(type::Type type, const Convertible& value, Error& error, std::function (bool)> makeInput, std::unique_ptr def, bool convertTokens = false) { auto stops = convertStops(type, value, error, convertTokens); if (!stops) { return nullopt; } auto base = convertBase(value, error); if (!base) { return nullopt; } return numberOrDefault(type, makeInput(false), interpolate(type, exponential(*base), makeInput(true), std::move(*stops)), std::move(def)); } static optional> convertCategoricalFunction(type::Type type, const Convertible& value, Error& err, const std::string& property, std::unique_ptr def) { auto stopsValue = objectMember(value, "stops"); if (!stopsValue) { err.message = "function value must specify stops"; return nullopt; } if (!isArray(*stopsValue)) { err.message = "function stops must be an array"; return nullopt; } if (arrayLength(*stopsValue) == 0) { err.message = "function must have at least one stop"; return nullopt; } const auto& first = arrayMember(*stopsValue, 0); if (!isArray(first)) { err.message = "function stop must be an array"; return nullopt; } if (arrayLength(first) != 2) { err.message = "function stop must have two elements"; return nullopt; } if (toBool(arrayMember(first, 0))) { auto branches = convertBranches(type, value, err); if (!branches) { return nullopt; } return categorical(type, property, std::move(*branches), std::move(def)); } if (toNumber(arrayMember(first, 0))) { auto branches = convertBranches(type, value, err); if (!branches) { return nullopt; } return categorical(type, property, std::move(*branches), std::move(def)); } if (toString(arrayMember(first, 0))) { auto branches = convertBranches(type, value, err); if (!branches) { return nullopt; } return categorical(type, property, std::move(*branches), std::move(def)); } err.message = "stop domain value must be a number, string, or boolean"; return nullopt; } template optional> composite(type::Type type, const Convertible& value, Error& error, const Fn& makeInnerExpression) { auto base = convertBase(value, error); if (!base) { return nullopt; } auto stopsValue = objectMember(value, "stops"); // Checked by caller. assert(stopsValue); assert(isArray(*stopsValue)); std::map>> map; for (std::size_t i = 0; i < arrayLength(*stopsValue); ++i) { const auto& stopValue = arrayMember(*stopsValue, i); if (!isArray(stopValue)) { error.message = "function stop must be an array"; return nullopt; } if (arrayLength(stopValue) != 2) { error.message = "function stop must have two elements"; return nullopt; } const auto& stopInput = arrayMember(stopValue, 0); if (!isObject(stopInput)) { error.message = "stop input must be an object"; return nullopt; } auto zoomValue = objectMember(stopInput, "zoom"); if (!zoomValue) { error.message = "stop input must specify zoom"; return nullopt; } auto sourceValue = objectMember(stopInput, "value"); if (!sourceValue) { error.message = "stop input must specify value"; return nullopt; } optional z = convert(*zoomValue, error); if (!z) { return nullopt; } optional d = convert(*sourceValue, error); if (!d) { return nullopt; } optional> r = convertLiteral(type, arrayMember(stopValue, 1), error); if (!r) { return nullopt; } map[*z].emplace(*d, std::move(*r)); } std::map> stops; for (auto& e : map) { stops.emplace(e.first, makeInnerExpression(type, *base, std::move(e.second))); } if (interpolatable(type)) { return interpolate(type, linear(), zoom(), std::move(stops)); } else { return step(type, zoom(), std::move(stops)); } } optional> convertFunctionToExpression(type::Type type, const Convertible& value, Error& err, bool convertTokens) { if (!isObject(value)) { err.message = "function must be an object"; return nullopt; } FunctionType functionType = FunctionType::Invalid; auto typeValue = objectMember(value, "type"); if (!typeValue) { functionType = interpolatable(type) ? FunctionType::Exponential : FunctionType::Interval; } else { optional string = toString(*typeValue); if (string) { if (*string == "interval") functionType = FunctionType::Interval; if (*string == "exponential" && interpolatable(type)) functionType = FunctionType::Exponential; if (*string == "categorical") functionType = FunctionType::Categorical; if (*string == "identity") functionType = FunctionType::Identity; } } auto defaultExpr = [&]() -> std::unique_ptr { auto member = objectMember(value, "default"); if (member) { auto literal = convertLiteral(type, *member, err); if (literal) { return std::move(*literal); } } return nullptr; }; if (!objectMember(value, "property")) { // Camera function. switch (functionType) { case FunctionType::Interval: return convertIntervalFunction(type, value, err, [](bool) { return zoom(); }, defaultExpr(), convertTokens); case FunctionType::Exponential: return convertExponentialFunction(type, value, err, [](bool) { return zoom(); }, defaultExpr(), convertTokens); default: err.message = "unsupported function type"; return nullopt; } } auto propertyValue = objectMember(value, "property"); if (!propertyValue) { err.message = "function must specify property"; return nullopt; } auto property = toString(*propertyValue); if (!property) { err.message = "function property must be a string"; return nullopt; } if (functionType == FunctionType::Identity) { return type.match( [&] (const type::StringType&) -> optional> { return string(get(literal(*property)), defaultExpr()); }, [&] (const type::NumberType&) -> optional> { return number(get(literal(*property)), defaultExpr()); }, [&] (const type::BooleanType&) -> optional> { return boolean(get(literal(*property)), defaultExpr()); }, [&] (const type::ColorType&) -> optional> { return toColor(get(literal(*property)), defaultExpr()); }, [&] (const type::Array& array) -> optional> { return assertion(array, get(literal(*property)), defaultExpr()); }, [&] (const type::FormattedType&) -> optional> { return toFormatted(get(literal(*property)), defaultExpr()); }, [&] (const auto&) -> optional> { assert(false); // No properties use this type. return nullopt; } ); } auto stopsValue = objectMember(value, "stops"); if (!stopsValue) { err.message = "function value must specify stops"; return nullopt; } if (!isArray(*stopsValue)) { err.message = "function stops must be an array"; return nullopt; } if (arrayLength(*stopsValue) == 0) { err.message = "function must have at least one stop"; return nullopt; } const auto& first = arrayMember(*stopsValue, 0); if (!isArray(first)) { err.message = "function stop must be an array"; return nullopt; } if (arrayLength(first) != 2) { err.message = "function stop must have two elements"; return nullopt; } const auto& stop = arrayMember(first, 0); const auto getProperty = [&](bool coerce) { if (coerce) { return number(get(literal(*property))); } else { return get(literal(*property)); } }; if (!isObject(stop)) { // Source function. switch (functionType) { case FunctionType::Interval: return convertIntervalFunction(type, value, err, getProperty, defaultExpr()); case FunctionType::Exponential: return convertExponentialFunction(type, value, err, getProperty, defaultExpr()); case FunctionType::Categorical: return convertCategoricalFunction(type, value, err, *property, defaultExpr()); default: err.message = "unsupported function type"; return nullopt; } } else { // Composite function. auto sourceValue = objectMember(stop, "value"); if (!sourceValue) { err.message = "stop must specify value"; return nullopt; } if (toBool(*sourceValue)) { switch (functionType) { case FunctionType::Categorical: return composite(type, value, err, [&] (type::Type type_, double, std::map> stops) { return categorical(type_, *property, std::move(stops), defaultExpr()); }); default: err.message = "unsupported function type"; return nullopt; } } if (toNumber(*sourceValue)) { switch (functionType) { case FunctionType::Interval: return composite(type, value, err, [&] (type::Type type_, double, std::map> stops) { omitFirstStop(stops); return numberOrDefault(type, getProperty(false), step(type_, getProperty(true), std::move(stops)), defaultExpr()); }); case FunctionType::Exponential: return composite(type, value, err, [&] (type::Type type_, double base, std::map> stops) { return numberOrDefault(type, getProperty(false), interpolate(type_, exponential(base), getProperty(true), std::move(stops)), defaultExpr()); }); case FunctionType::Categorical: return composite(type, value, err, [&] (type::Type type_, double, std::map> stops) { return categorical(type_, *property, std::move(stops), defaultExpr()); }); default: err.message = "unsupported function type"; return nullopt; } } if (toString(*sourceValue)) { switch (functionType) { case FunctionType::Categorical: return composite(type, value, err, [&] (type::Type type_, double, std::map> stops) { return categorical(type_, *property, std::move(stops), defaultExpr()); }); default: err.message = "unsupported function type"; return nullopt; } } err.message = "stop domain value must be a number, string, or boolean"; return nullopt; } } } // namespace conversion } // namespace style } // namespace mbgl