#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace mbgl { namespace style { namespace expression { /* Represents the parameter list for an expression that takes an arbitrary number of arguments (of a specific type). */ struct VarargsType { type::Type type; }; bool operator==(const VarargsType& lhs, const VarargsType& rhs) { return lhs.type == rhs.type; } template struct Varargs : std::vector { template Varargs(Args&&... args) : std::vector(std::forward(args)...) {} }; namespace detail { // Base class for the Signature structs that are used to determine // each CompoundExpression definition's type::Type data from the type of its // "evaluate" function. struct SignatureBase { using Args = std::vector>; SignatureBase(type::Type result_, variant, VarargsType> params_, std::string name_) : result(std::move(result_)), params(std::move(params_)), name(std::move(name_)) {} virtual ~SignatureBase() = default; virtual EvaluationResult apply(const EvaluationContext&, const Args&) const = 0; type::Type result; variant, VarargsType> params; std::string name; }; /* The Signature structs are wrappers around an "evaluate()" function whose purpose is to extract the necessary Type data from the evaluate function's type. There are three key (partial) specializations: Signature: Wraps a simple evaluate function (const T0&, const T1&, ...) -> Result Signature&)>: Wraps an evaluate function that takes an arbitrary number of arguments (via a Varargs, which is just an alias for std::vector). Signature: Wraps an evaluate function that needs to access the expression evaluation parameters in addition to its subexpressions, i.e., (const EvaluationParams&, const T0&, const T1&, ...) -> Result. Needed for expressions like ["zoom"], ["get", key], etc. In each of the above evaluate signatures, T0, T1, etc. are the types of the successfully evaluated subexpressions. */ template struct Signature; // Simple evaluate function (const T0&, const T1&, ...) -> Result template struct Signature : SignatureBase { Signature(R (*evaluate_)(Params...), std::string name_) : SignatureBase( valueTypeToExpressionType>(), std::vector {valueTypeToExpressionType>()...}, std::move(name_) ), evaluate(evaluate_) {} EvaluationResult apply(const EvaluationContext& evaluationParameters, const Args& args) const override { return applyImpl(evaluationParameters, args, std::index_sequence_for{}); } R (*evaluate)(Params...); private: template EvaluationResult applyImpl(const EvaluationContext& evaluationParameters, const Args& args, std::index_sequence) const { std::array evaluated; for (std::size_t i = 0; i < sizeof...(Params); ++i) { const EvaluationResult evaluatedArg = args.at(i)->evaluate(evaluationParameters); if (!evaluatedArg) return evaluatedArg.error(); evaluated[i] = std::move(*evaluatedArg); } const R value = evaluate(*fromExpressionValue>(evaluated[I])...); if (!value) return value.error(); return *value; } }; // Varargs evaluate function (const Varargs&) -> Result template struct Signature&)> : SignatureBase { Signature(R (*evaluate_)(const Varargs&), std::string name_) : SignatureBase( valueTypeToExpressionType>(), VarargsType { valueTypeToExpressionType() }, std::move(name_) ), evaluate(evaluate_) {} EvaluationResult apply(const EvaluationContext& evaluationParameters, const Args& args) const override { Varargs evaluated; evaluated.reserve(args.size()); for (const auto& arg : args) { const EvaluationResult evaluatedArg = arg->evaluate(evaluationParameters); if(!evaluatedArg) return evaluatedArg.error(); evaluated.push_back(*fromExpressionValue>(*evaluatedArg)); } const R value = evaluate(evaluated); if (!value) return value.error(); return *value; } R (*evaluate)(const Varargs&); }; // Evaluate function needing parameter access, // (const EvaluationParams&, const T0&, const T1&, ...) -> Result template struct Signature : SignatureBase { Signature(R (*evaluate_)(const EvaluationContext&, Params...), std::string name_) : SignatureBase( valueTypeToExpressionType>(), std::vector {valueTypeToExpressionType>()...}, std::move(name_) ), evaluate(evaluate_) {} EvaluationResult apply(const EvaluationContext& evaluationParameters, const Args& args) const override { return applyImpl(evaluationParameters, args, std::index_sequence_for{}); } private: template EvaluationResult applyImpl(const EvaluationContext& evaluationParameters, const Args& args, std::index_sequence) const { std::array evaluated; for (std::size_t i = 0; i < sizeof...(Params); ++i) { const EvaluationResult evaluatedArg = args.at(i)->evaluate(evaluationParameters); if (!evaluatedArg) return evaluatedArg.error(); evaluated[i] = std::move(*evaluatedArg); } const R value = evaluate(evaluationParameters, *fromExpressionValue>(evaluated[I])...); if (!value) return value.error(); return *value; } R (*evaluate)(const EvaluationContext&, Params...); }; // Evaluate function needing EvaluationContext and Varargs // (const EvaluationContext&, const Varargs&) -> Result template struct Signature&)> : SignatureBase { Signature(R (*evaluate_)(const EvaluationContext&, const Varargs&), std::string name_) : SignatureBase( valueTypeToExpressionType>(), VarargsType { valueTypeToExpressionType() }, std::move(name_) ), evaluate(evaluate_) {} EvaluationResult apply(const EvaluationContext& evaluationParameters, const Args& args) const override { Varargs evaluated; evaluated.reserve(args.size()); for (const auto& arg : args) { const EvaluationResult evaluatedArg = arg->evaluate(evaluationParameters); if(!evaluatedArg) return evaluatedArg.error(); evaluated.push_back(*fromExpressionValue>(*evaluatedArg)); } const R value = evaluate(evaluationParameters, evaluated); if (!value) return value.error(); return *value; } R (*evaluate)(const EvaluationContext&, const Varargs&); }; // Machinery to pull out function type from lambdas. template struct SignatureType; template struct SignatureType { using Type = R (Params...); }; template struct SignatureType::value>> { using Type = typename SignatureType::Type; }; template static std::unique_ptr makeSignature(std::string name, Fn evaluateFunction) { return std::make_unique::Type>>(evaluateFunction, std::move(name)); } } // namespace detail Value featureIdAsExpressionValue(EvaluationContext params) { assert(params.feature); auto id = params.feature->getID(); if (id.is()) return Null; return id.match([](const auto& idid) { return toExpressionValue(mbgl::Value(idid)); }); }; optional featurePropertyAsExpressionValue(EvaluationContext params, const std::string& key) { assert(params.feature); const PropertyMap& properties = params.feature->getProperties(); const auto it = properties.find(key); if (it == properties.cend()) { return optional(); } return toExpressionValue(it->second); }; optional featureTypeAsString(FeatureType type) { switch(type) { case FeatureType::Point: return optional("Point"); case FeatureType::LineString: return optional("LineString"); case FeatureType::Polygon: return optional("Polygon"); case FeatureType::Unknown: return optional("Unknown"); default: return {}; } }; optional featurePropertyAsDouble(EvaluationContext params, const std::string& key) { assert(params.feature); return params.feature->getValue(key).match( [](double value) { return value; }, [](uint64_t value) { return optional(static_cast(value)); }, [](int64_t value) { return optional(static_cast(value)); }, [](auto) { return optional(); } ); }; optional featurePropertyAsString(EvaluationContext params, const std::string& key) { assert(params.feature); return params.feature->getValue(key).match( [](std::string value) { return value; }, [](auto) { return optional(); } ); }; optional featureIdAsDouble(EvaluationContext params) { assert(params.feature); auto id = params.feature->getID(); return id.match( [](double value) { return value; }, [](uint64_t value) { return optional(static_cast(value)); }, [](int64_t value) { return optional(static_cast(value)); }, [](auto) { return optional(); } ); }; optional featureIdAsString(EvaluationContext params) { assert(params.feature); auto id = params.feature->getID(); return id.match( [](std::string value) { return value; }, [](auto) { return optional(); } ); }; const auto& eCompoundExpression() { static auto signature = detail::makeSignature("e", []() -> Result { return 2.718281828459045; }); return signature; } const auto& piCompoundExpression() { static auto signature = detail::makeSignature("pi", []() -> Result { return 3.141592653589793; }); return signature; } const auto& ln2CompoundExpression() { static auto signature = detail::makeSignature("ln2", []() -> Result { return 0.6931471805599453; }); return signature; } const auto& typeofCompoundExpression() { static auto signature = detail::makeSignature("typeof", [](const Value& v) -> Result { return toString(typeOf(v)); }); return signature; } const auto& toRgbaCompoundExpression() { static auto signature = detail::makeSignature("to-rgba", [](const Color& color) -> Result> { return color.toArray(); }); return signature; } const auto& rgbaCompoundExpression() { static auto signature = detail::makeSignature("rgba", [](double r, double g, double b, double a) { return rgba(r, g, b, a); }); return signature; } const auto& rgbCompoundExpression() { static auto signature = detail::makeSignature("rgb", [](double r, double g, double b) { return rgba(r, g, b, 1.0f); }); return signature; } const auto& zoomCompoundExpression() { static auto signature = detail::makeSignature("zoom", [](const EvaluationContext& params) -> Result { if (!params.zoom) { return EvaluationError { "The 'zoom' expression is unavailable in the current evaluation context." }; } return *(params.zoom); }); return signature; } const auto& heatmapDensityCompoundExpression() { static auto signature = detail::makeSignature("heatmap-density", [](const EvaluationContext& params) -> Result { if (!params.colorRampParameter) { return EvaluationError { "The 'heatmap-density' expression is unavailable in the current evaluation context." }; } return *(params.colorRampParameter); }); return signature; } const auto& lineProgressCompoundExpression() { static auto signature = detail::makeSignature("line-progress", [](const EvaluationContext& params) -> Result { if (!params.colorRampParameter) { return EvaluationError { "The 'line-progress' expression is unavailable in the current evaluation context." }; } return *(params.colorRampParameter); }); return signature; } const auto& hasContextCompoundExpression() { static auto signature = detail::makeSignature("has", [](const EvaluationContext& params, const std::string& key) -> Result { if (!params.feature) { return EvaluationError { "Feature data is unavailable in the current evaluation context." }; } const PropertyMap& properties = params.feature->getProperties(); return properties.find(key) != properties.cend(); }); return signature; } const auto& hasObjectCompoundExpression() { static auto signature = detail::makeSignature("has", [](const std::string& key, const std::unordered_map& object) -> Result { return object.find(key) != object.end(); }); return signature; } const auto& getContextCompoundExpression() { static auto signature = detail::makeSignature("get", [](const EvaluationContext& params, const std::string& key) -> Result { if (!params.feature) { return EvaluationError { "Feature data is unavailable in the current evaluation context." }; } const PropertyMap& properties = params.feature->getProperties(); const auto it = properties.find(key); if (it == properties.cend()) { return Null; } return toExpressionValue(it->second); }); return signature; } const auto& getObjectCompoundExpression() { static auto signature = detail::makeSignature("get", [](const std::string& key, const std::unordered_map& object) -> Result { if (object.find(key) == object.end()) { return Null; } return object.at(key); }); return signature; } const auto& propertiesCompoundExpression() { static auto signature = detail::makeSignature("properties", [](const EvaluationContext& params) -> Result> { if (!params.feature) { return EvaluationError { "Feature data is unavailable in the current evaluation context." }; } std::unordered_map result; const PropertyMap properties = params.feature->getProperties(); for (const auto& entry : properties) { result[entry.first] = toExpressionValue(entry.second); } return result; }); return signature; } const auto& geometryTypeCompoundExpression() { static auto signature = detail::makeSignature("geometry-type", [](const EvaluationContext& params) -> Result { if (!params.feature) { return EvaluationError { "Feature data is unavailable in the current evaluation context." }; } auto type = params.feature->getType(); if (type == FeatureType::Point) { return "Point"; } else if (type == FeatureType::LineString) { return "LineString"; } else if (type == FeatureType::Polygon) { return "Polygon"; } else { return "Unknown"; } }); return signature; } const auto& idCompoundExpression() { static auto signature = detail::makeSignature("id", [](const EvaluationContext& params) -> Result { if (!params.feature) { return EvaluationError { "Feature data is unavailable in the current evaluation context." }; } auto id = params.feature->getID(); return id.match( [](const auto& idValue) { return toExpressionValue(mbgl::Value(idValue)); } ); }); return signature; } const auto& plusCompoundExpression() { static auto signature = detail::makeSignature("+", [](const Varargs& args) -> Result { double sum = 0.0f; for (auto arg : args) { sum += arg; } return sum; }); return signature; } const auto& minusCompoundExpression() { static auto signature = detail::makeSignature("-", [](double a, double b) -> Result { return a - b; }); return signature; } const auto& negateCompoundExpression() { static auto signature = detail::makeSignature("-", [](double a) -> Result { return -a; }); return signature; } const auto& multiplyCompoundExpression() { static auto signature = detail::makeSignature("*", [](const Varargs& args) -> Result { double prod = 1.0f; for (auto arg : args) { prod *= arg; } return prod; }); return signature; } const auto& divideCompoundExpression() { static auto signature = detail::makeSignature("/", [](double a, double b) -> Result { return a / b; }); return signature; } const auto& modCompoundExpression() { static auto signature = detail::makeSignature("%", [](double a, double b) -> Result { return fmod(a, b); }); return signature; } const auto& powCompoundExpression() { static auto signature = detail::makeSignature("^", [](double a, double b) -> Result { return pow(a, b); }); return signature; } const auto& sqrtCompoundExpression() { static auto signature = detail::makeSignature("sqrt", [](double x) -> Result { return sqrt(x); }); return signature; } const auto& log10CompoundExpression() { static auto signature = detail::makeSignature("log10", [](double x) -> Result { return log10(x); }); return signature; } const auto& lnCompoundExpression() { static auto signature = detail::makeSignature("ln", [](double x) -> Result { return log(x); }); return signature; } const auto& log2CompoundExpression() { static auto signature = detail::makeSignature("log2", [](double x) -> Result { return util::log2(x); }); return signature; } const auto& sinCompoundExpression() { static auto signature = detail::makeSignature("sin", [](double x) -> Result { return sin(x); }); return signature; } const auto& cosCompoundExpression() { static auto signature = detail::makeSignature("cos", [](double x) -> Result { return cos(x); }); return signature; } const auto& tanCompoundExpression() { static auto signature = detail::makeSignature("tan", [](double x) -> Result { return tan(x); }); return signature; } const auto& asinCompoundExpression() { static auto signature = detail::makeSignature("asin", [](double x) -> Result { return asin(x); }); return signature; } const auto& acosCompoundExpression() { static auto signature = detail::makeSignature("acos", [](double x) -> Result { return acos(x); }); return signature; } const auto& atanCompoundExpression() { static auto signature = detail::makeSignature("atan", [](double x) -> Result { return atan(x); }); return signature; } const auto& minCompoundExpression() { static auto signature = detail::makeSignature("min", [](const Varargs& args) -> Result { double result = std::numeric_limits::infinity(); for (double arg : args) { result = fmin(arg, result); } return result; }); return signature; } const auto& maxCompoundExpression() { static auto signature = detail::makeSignature("max", [](const Varargs& args) -> Result { double result = -std::numeric_limits::infinity(); for (double arg : args) { result = fmax(arg, result); } return result; }); return signature; } const auto& roundCompoundExpression() { static auto signature = detail::makeSignature("round", [](double x) -> Result { return ::round(x); }); return signature; } const auto& floorCompoundExpression() { static auto signature = detail::makeSignature("floor", [](double x) -> Result { return std::floor(x); }); return signature; } const auto& ceilCompoundExpression() { static auto signature = detail::makeSignature("ceil", [](double x) -> Result { return std::ceil(x); }); return signature; } const auto& absCompoundExpression() { static auto signature = detail::makeSignature("abs", [](double x) -> Result { return std::abs(x); }); return signature; } const auto& notCompoundExpression() { static auto signature = detail::makeSignature("!", [](bool e) -> Result { return !e; }); return signature; } const auto& isSupportedScriptCompoundExpression() { static auto signature = detail::makeSignature("is-supported-script", [](const std::string& x) -> Result { return util::i18n::isStringInSupportedScript(x); }); return signature; } const auto& upcaseCompoundExpression() { static auto signature = detail::makeSignature("upcase", [](const std::string& input) -> Result { return platform::uppercase(input); }); return signature; } const auto& downcaseCompoundExpression() { static auto signature = detail::makeSignature("downcase", [](const std::string& input) -> Result { return platform::lowercase(input); }); return signature; } const auto& concatCompoundExpression() { static auto signature = detail::makeSignature("concat", [](const Varargs& args) -> Result { std::string s; for (const Value& arg : args) { s += toString(arg); } return s; }); return signature; } const auto& resolvedLocaleCompoundExpression() { static auto signature = detail::makeSignature("resolved-locale", [](const Collator& collator) -> Result { return collator.resolvedLocale(); }); return signature; } const auto& errorCompoundExpression() { static auto signature = detail::makeSignature("error", [](const std::string& input) -> Result { return EvaluationError { input }; }); return signature; } // Legacy Filters const auto& filterEqualsCompoundExpression() { static auto signature = detail::makeSignature("filter-==", [](const EvaluationContext& params, const std::string& key, const Value &lhs) -> Result { const auto rhs = featurePropertyAsExpressionValue(params, key); return rhs ? lhs == *rhs : false; }); return signature; } const auto& filterIdEqualsCompoundExpression() { static auto signature = detail::makeSignature("filter-id-==", [](const EvaluationContext& params, const Value &lhs) -> Result { return lhs == featureIdAsExpressionValue(params); }); return signature; } const auto& filterTypeEqualsCompoundExpression() { static auto signature = detail::makeSignature("filter-type-==", [](const EvaluationContext& params, const std::string &lhs) -> Result { if (!params.feature) return false; return featureTypeAsString(params.feature->getType()) == lhs; }); return signature; } const auto& filterLessThanNumberCompoundExpression() { static auto signature = detail::makeSignature("filter-<", [](const EvaluationContext& params, const std::string& key, double lhs) -> Result { auto rhs = featurePropertyAsDouble(params, key); return rhs ? rhs < lhs : false; }); return signature; } const auto& filterLessThanStringCompoundExpression() { static auto signature = detail::makeSignature("filter-<", [](const EvaluationContext& params, const std::string& key, std::string lhs) -> Result { auto rhs = featurePropertyAsString(params, key); return rhs ? rhs < lhs : false; }); return signature; } const auto& filterIdLessThanNumberCompoundExpression() { static auto signature = detail::makeSignature("filter-id-<", [](const EvaluationContext& params, double lhs) -> Result { auto rhs = featureIdAsDouble(params); return rhs ? rhs < lhs : false; }); return signature; } const auto& filterIdLessThanStringCompoundExpression() { static auto signature = detail::makeSignature("filter-id-<", [](const EvaluationContext& params, std::string lhs) -> Result { auto rhs = featureIdAsString(params); return rhs ? rhs < lhs : false; }); return signature; } const auto& filterMoreThanNumberCompoundExpression() { static auto signature = detail::makeSignature("filter->", [](const EvaluationContext& params, const std::string& key, double lhs) -> Result { auto rhs = featurePropertyAsDouble(params, key); return rhs ? rhs > lhs : false; }); return signature; } const auto& filterMoreThanStringCompoundExpression() { static auto signature = detail::makeSignature("filter->", [](const EvaluationContext& params, const std::string& key, std::string lhs) -> Result { auto rhs = featurePropertyAsString(params, key); return rhs ? rhs > lhs : false; }); return signature; } const auto& filterIdMoreThanNumberCompoundExpression() { static auto signature = detail::makeSignature("filter-id->", [](const EvaluationContext& params, double lhs) -> Result { auto rhs = featureIdAsDouble(params); return rhs ? rhs > lhs : false; }); return signature; } const auto& filterIdMoreThanStringCompoundExpression() { static auto signature = detail::makeSignature("filter-id->", [](const EvaluationContext& params, std::string lhs) -> Result { auto rhs = featureIdAsString(params); return rhs ? rhs > lhs : false; }); return signature; } const auto& filterLessOrEqualThanNumberCompoundExpression() { static auto signature = detail::makeSignature("filter-<=", [](const EvaluationContext& params, const std::string& key, double lhs) -> Result { auto rhs = featurePropertyAsDouble(params, key); return rhs ? rhs <= lhs : false; }); return signature; } const auto& filterLessOrEqualThanStringCompoundExpression() { static auto signature = detail::makeSignature("filter-<=", [](const EvaluationContext& params, const std::string& key, std::string lhs) -> Result { auto rhs = featurePropertyAsString(params, key); return rhs ? rhs <= lhs : false; }); return signature; } const auto& filterIdLessOrEqualThanNumberCompoundExpression() { static auto signature = detail::makeSignature("filter-id-<=", [](const EvaluationContext& params, double lhs) -> Result { auto rhs = featureIdAsDouble(params); return rhs ? rhs <= lhs : false; }); return signature; } const auto& filterIdLessOrEqualThanStringCompoundExpression() { static auto signature = detail::makeSignature("filter-id-<=", [](const EvaluationContext& params, std::string lhs) -> Result { auto rhs = featureIdAsString(params); return rhs ? rhs <= lhs : false; }); return signature; } const auto& filterGreaterOrEqualThanNumberCompoundExpression() { static auto signature = detail::makeSignature("filter->=", [](const EvaluationContext& params, const std::string& key, double lhs) -> Result { auto rhs = featurePropertyAsDouble(params, key); return rhs ? rhs >= lhs : false; }); return signature; } const auto& filterGreaterOrEqualThanStringCompoundExpression() { static auto signature = detail::makeSignature("filter->=", [](const EvaluationContext& params, const std::string& key, std::string lhs) -> Result { auto rhs = featurePropertyAsString(params, key); return rhs ? rhs >= lhs : false; }); return signature; } const auto& filterIdGreaterOrEqualThanNumberCompoundExpression() { static auto signature = detail::makeSignature("filter-id->=", [](const EvaluationContext& params, double lhs) -> Result { auto rhs = featureIdAsDouble(params); return rhs ? rhs >= lhs : false; }); return signature; } const auto& filterIdGreaterOrEqualThanStringCompoundExpression() { static auto signature = detail::makeSignature("filter-id->=", [](const EvaluationContext& params, std::string lhs) -> Result { auto rhs = featureIdAsString(params); return rhs ? rhs >= lhs : false; }); return signature; } const auto& filterHasCompoundExpression() { static auto signature = detail::makeSignature("filter-has", [](const EvaluationContext& params, const std::string& key) -> Result { assert(params.feature); const PropertyMap& properties = params.feature->getProperties(); return properties.find(key) != properties.cend(); }); return signature; } const auto& filterHasIdCompoundExpression() { static auto signature = detail::makeSignature("filter-has-id", [](const EvaluationContext& params) -> Result { assert(params.feature); return !params.feature->getID().is(); }); return signature; } const auto& filterTypeInCompoundExpression() { static auto signature = detail::makeSignature("filter-type-in", [](const EvaluationContext& params, const Varargs& types) -> Result { assert(params.feature); optional type = featureTypeAsString(params.feature->getType()); return std::find(types.begin(), types.end(), type) != types.end(); }); return signature; } const auto& filterIdInCompoundExpression() { static auto signature = detail::makeSignature("filter-id-in", [](const EvaluationContext& params, const Varargs& ids) -> Result { auto id = featureIdAsExpressionValue(params); return std::find(ids.begin(), ids.end(), id) != ids.end(); }); return signature; } const auto& filterInCompoundExpression() { static auto signature = detail::makeSignature("filter-in", [](const EvaluationContext& params, const Varargs& varargs) -> Result { if (varargs.size() < 2) return false; assert(varargs[0].is()); auto value = featurePropertyAsExpressionValue(params, varargs[0].get()); return value ? std::find(varargs.begin() + 1, varargs.end(), *value) != varargs.end() : false; }); return signature; } using ParseCompoundFunction = const std::unique_ptr& (*)(); MAPBOX_ETERNAL_CONSTEXPR const auto compoundExpressionRegistry = mapbox::eternal::hash_map({ { "e", eCompoundExpression }, { "pi", piCompoundExpression }, { "ln2", ln2CompoundExpression }, { "typeof", typeofCompoundExpression }, { "to-rgba", toRgbaCompoundExpression }, { "rgba", rgbaCompoundExpression }, { "rgb", rgbCompoundExpression }, { "zoom", zoomCompoundExpression }, { "heatmap-density", heatmapDensityCompoundExpression }, { "line-progress", lineProgressCompoundExpression }, { "has", hasContextCompoundExpression }, { "has", hasObjectCompoundExpression }, { "get", getContextCompoundExpression }, { "get", getObjectCompoundExpression }, { "properties", propertiesCompoundExpression }, { "geometry-type", geometryTypeCompoundExpression }, { "id", idCompoundExpression }, { "+", plusCompoundExpression }, { "-", minusCompoundExpression }, { "-", negateCompoundExpression }, { "*", multiplyCompoundExpression }, { "/", divideCompoundExpression }, { "%", modCompoundExpression }, { "^", powCompoundExpression }, { "sqrt", sqrtCompoundExpression }, { "log10", log10CompoundExpression }, { "ln", lnCompoundExpression }, { "log2", log2CompoundExpression }, { "sin", sinCompoundExpression }, { "cos", cosCompoundExpression }, { "tan", tanCompoundExpression }, { "asin", asinCompoundExpression }, { "acos", acosCompoundExpression }, { "atan", atanCompoundExpression }, { "min", minCompoundExpression }, { "max", maxCompoundExpression }, { "round", roundCompoundExpression }, { "floor", floorCompoundExpression }, { "ceil", ceilCompoundExpression }, { "abs", absCompoundExpression }, { "!", notCompoundExpression }, { "is-supported-script", isSupportedScriptCompoundExpression }, { "upcase", upcaseCompoundExpression }, { "downcase", downcaseCompoundExpression }, { "concat", concatCompoundExpression }, { "resolved-locale", resolvedLocaleCompoundExpression }, { "error", errorCompoundExpression }, // Legacy Filters { "filter-==", filterEqualsCompoundExpression }, { "filter-id-==", filterIdEqualsCompoundExpression }, { "filter-type-==", filterTypeEqualsCompoundExpression }, { "filter-<", filterLessThanNumberCompoundExpression }, { "filter-<", filterLessThanStringCompoundExpression }, { "filter-id-<", filterIdLessThanNumberCompoundExpression }, { "filter-id-<", filterIdLessThanStringCompoundExpression }, { "filter->", filterMoreThanNumberCompoundExpression }, { "filter->", filterMoreThanStringCompoundExpression }, { "filter-id->", filterIdMoreThanNumberCompoundExpression }, { "filter-id->", filterIdMoreThanStringCompoundExpression }, { "filter-<=", filterLessOrEqualThanNumberCompoundExpression }, { "filter-<=", filterLessOrEqualThanStringCompoundExpression }, { "filter-id-<=", filterIdLessOrEqualThanNumberCompoundExpression }, { "filter-id-<=", filterIdLessOrEqualThanStringCompoundExpression }, { "filter->=", filterGreaterOrEqualThanNumberCompoundExpression }, { "filter->=", filterGreaterOrEqualThanStringCompoundExpression }, { "filter-id->=", filterIdGreaterOrEqualThanNumberCompoundExpression }, { "filter-id->=", filterIdGreaterOrEqualThanStringCompoundExpression }, { "filter-has", filterHasCompoundExpression }, { "filter-has-id", filterHasIdCompoundExpression }, { "filter-type-in", filterTypeInCompoundExpression }, { "filter-id-in", filterIdInCompoundExpression }, { "filter-in", filterInCompoundExpression }, }); using namespace mbgl::style::conversion; using DefinitionIterator = decltype(compoundExpressionRegistry)::const_iterator; using Definitions = std::pair; std::string expectedTypesError(const Definitions& definitions, const std::vector>& args) { std::vector availableOverloads; // Only used if there are no overloads with matching number of args std::vector overloads; for (auto it = definitions.first; it != definitions.second; ++it) { const auto& signature = it->second(); signature->params.match( [&](const VarargsType& varargs) { std::string overload = "(" + toString(varargs.type) + ")"; overloads.push_back(overload); }, [&](const std::vector& params) { std::string overload = "("; bool first = true; for (const type::Type& param : params) { if (!first) overload += ", "; overload += toString(param); first = false; } overload += ")"; if (params.size() == args.size()) { overloads.push_back(overload); } else { availableOverloads.push_back(overload); } } ); } std::string signatures = overloads.empty() ? boost::algorithm::join(availableOverloads, " | ") : boost::algorithm::join(overloads, " | "); std::string actualTypes; for (const auto& arg : args) { if (actualTypes.size() > 0) { actualTypes += ", "; } actualTypes += toString(arg->getType()); } return "Expected arguments of type " + signatures + ", but found (" + actualTypes + ") instead."; } static ParseResult createCompoundExpression(const Definitions& definitions, std::vector> args, ParsingContext& ctx) { ParsingContext signatureContext(ctx.getKey()); for (auto it = definitions.first; it != definitions.second; ++it) { const auto& signature = it->second(); signatureContext.clearErrors(); if (signature->params.is>()) { const std::vector& params = signature->params.get>(); if (params.size() != args.size()) { signatureContext.error( "Expected " + util::toString(params.size()) + " arguments, but found " + util::toString(args.size()) + " instead." ); continue; } for (std::size_t i = 0; i < args.size(); i++) { const std::unique_ptr& arg = args[i]; optional err = type::checkSubtype(params.at(i), arg->getType()); if (err) { signatureContext.error(*err, i + 1); } } } else if (signature->params.is()) { const type::Type& paramType = signature->params.get().type; for (std::size_t i = 0; i < args.size(); i++) { const std::unique_ptr& arg = args[i]; optional err = type::checkSubtype(paramType, arg->getType()); if (err) { signatureContext.error(*err, i + 1); } } } if (signatureContext.getErrors().size() == 0) { return ParseResult(std::make_unique(*signature, std::move(args))); } } if (definitions.second - definitions.first == 1) { ctx.appendErrors(std::move(signatureContext)); } else { ctx.error(expectedTypesError(definitions, args)); } return ParseResult(); } ParseResult parseCompoundExpression(const std::string name, const Convertible& value, ParsingContext& ctx) { assert(isArray(value) && arrayLength(value) > 0); const auto definitions = compoundExpressionRegistry.equal_range(name.c_str()); if (definitions.first == definitions.second) { ctx.error( R"(Unknown expression ")" + name + R"(". If you wanted a literal array, use ["literal", [...]].)", 0 ); return ParseResult(); } auto length = arrayLength(value); for (auto it = definitions.first; it != definitions.second; ++it) { const auto& signature = it->second(); if ( signature->params.is() || signature->params.get>().size() == length - 1 ) { // First parse all the args, potentially coercing to the // types expected by this overload. ctx.clearErrors(); bool argParseFailed = false; std::vector> args; args.reserve(length - 1); for (std::size_t i = 1; i < length; i++) { optional expected = signature->params.match( [](const VarargsType& varargs) { return varargs.type; }, [&](const std::vector& params_) { return params_[i - 1]; } ); auto parsed = ctx.parse(arrayMember(value, i), i, expected); if (!parsed) { argParseFailed = true; break; } args.push_back(std::move(*parsed)); } if (argParseFailed) { // Couldn't coerce args of this overload to expected type, move // on to next one. continue; } else { ParseResult parseWithArgs = createCompoundExpression(definitions, std::move(args), ctx); if (parseWithArgs) { return parseWithArgs; } } } } // The args couldn't be coerced to any of the expected types. // Parse the arguments again without expected types just for the error message ctx.clearErrors(); std::vector> args; args.reserve(length - 1); for (std::size_t i = 1; i < length; i++) { auto parsed = ctx.parse(arrayMember(value, i), i); if (!parsed) { return ParseResult(); } args.push_back(std::move(*parsed)); } ctx.error(expectedTypesError(definitions, args)); return ParseResult(); } ParseResult createCompoundExpression(const std::string& name, std::vector> args, ParsingContext& ctx) { return createCompoundExpression(compoundExpressionRegistry.equal_range(name.c_str()), std::move(args), ctx); } CompoundExpression::CompoundExpression(const detail::SignatureBase& signature_, std::vector> args_) : Expression(Kind::CompoundExpression, signature_.result), signature(signature_), args(std::move(args_)) {} std::string CompoundExpression::getOperator() const { return signature.name; } EvaluationResult CompoundExpression::evaluate(const EvaluationContext& evaluationParams) const { return signature.apply(evaluationParams, args); } optional CompoundExpression::getParameterCount() const { return signature.params.match( [&](const VarargsType&) { return optional(); }, [&](const std::vector& p) -> optional { return p.size(); } ); } std::vector> CompoundExpression::possibleOutputs() const { return { nullopt }; } void CompoundExpression::eachChild(const std::function& visit) const { for (const std::unique_ptr& e : args) { visit(*e); } } bool CompoundExpression::operator==(const Expression& e) const { if (e.getKind() == Kind::CompoundExpression) { auto rhs = static_cast(&e); return signature.name == rhs->signature.name && signature.result == rhs->signature.result && signature.params == rhs->signature.params && Expression::childrenEqual(args, rhs->args); } return false; } bool CompoundExpression::exists(const std::string& name) { return compoundExpressionRegistry.contains(name.c_str()); } } // namespace expression } // namespace style } // namespace mbgl