#include #include #include #include #include #include #include #include #include #include namespace mbgl { namespace style { namespace expression { namespace detail { /* 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 { using Args = std::array, sizeof...(Params)>; 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 { return applyImpl(evaluationParameters, args, std::index_sequence_for{}); } 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)); } R (*evaluate)(Params...); private: template EvaluationResult applyImpl(const EvaluationContext& evaluationParameters, const Args& args, std::index_sequence) const { const std::array evaluated = {{std::get(args)->evaluate(evaluationParameters)...}}; for (const auto& arg : evaluated) { if(!arg) return arg.error(); } const R value = evaluate(*fromExpressionValue>(*(evaluated[I]))...); if (!value) return value.error(); return *value; } }; // Varargs evaluate function (const Varargs&) -> Result template struct Signature&)> : SignatureBase { using Args = std::vector>; Signature(R (*evaluate_)(const Varargs&), std::string name_) : SignatureBase( valueTypeToExpressionType>(), VarargsType { valueTypeToExpressionType() }, std::move(name_) ), evaluate(evaluate_) {} std::unique_ptr makeExpression(std::vector> args) const override { return std::make_unique>(name, *this, std::move(args)); }; EvaluationResult apply(const EvaluationContext& evaluationParameters, const Args& args) const { 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 { using Args = std::array, sizeof...(Params)>; Signature(R (*evaluate_)(const EvaluationContext&, Params...), std::string name_) : SignatureBase( valueTypeToExpressionType>(), std::vector {valueTypeToExpressionType>()...}, std::move(name_) ), evaluate(evaluate_) {} 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)); } EvaluationResult apply(const EvaluationContext& evaluationParameters, const Args& args) const { return applyImpl(evaluationParameters, args, std::index_sequence_for{}); } private: template EvaluationResult applyImpl(const EvaluationContext& evaluationParameters, const Args& args, std::index_sequence) const { const std::array evaluated = {{std::get(args)->evaluate(evaluationParameters)...}}; for (const auto& arg : evaluated) { if(!arg) return arg.error(); } // TODO: assert correct runtime type of each arg value const R value = evaluate(evaluationParameters, *fromExpressionValue>(*(evaluated[I]))...); if (!value) return value.error(); return *value; } R (*evaluate)(const EvaluationContext&, Params...); }; // Machinery to pull out function types from class methods, lambdas, etc. template struct Signature : Signature { using Signature::Signature; }; template struct Signature : Signature { using Signature::Signature; }; template struct Signature : Signature { using Signature::Signature; }; template struct Signature::value>> : Signature { using Signature::Signature; }; } // namespace detail using Definition = CompoundExpressionRegistry::Definition; template 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, name)); }; define("e", []() -> Result { return 2.718281828459045; }); define("pi", []() -> Result { return 3.141592653589793; }); define("ln2", []() -> Result { return 0.6931471805599453; }); define("typeof", [](const Value& v) -> Result { return toString(typeOf(v)); }); define("to-string", [](const Value& value) -> Result { return value.match( [](const Color& c) -> Result { return c.stringify(); }, // avoid quoting [](const std::string& s) -> Result { return s; }, // avoid quoting [](const auto& v) -> Result { return stringify(v); } ); }); define("to-boolean", [](const Value& v) -> Result { return v.match( [&] (double f) { return (bool)f; }, [&] (const std::string& s) { return s.length() > 0; }, [&] (bool b) { return b; }, [&] (const NullValue&) { return false; }, [&] (const auto&) { return true; } ); }); define("to-rgba", [](const Color& color) -> Result> { return color.toArray(); }); define("rgba", rgba); define("rgb", [](double r, double g, double b) { return rgba(r, g, b, 1.0f); }); define("zoom", [](const EvaluationContext& params) -> Result { if (!params.zoom) { return EvaluationError { "The 'zoom' expression is unavailable in the current evaluation context." }; } return *(params.zoom); }); define("heatmap-density", [](const EvaluationContext& params) -> Result { if (!params.heatmapDensity) { return EvaluationError { "The 'heatmap-density' expression is unavailable in the current evaluation context." }; } return *(params.heatmapDensity); }); define("has", [](const EvaluationContext& params, const std::string& key) -> Result { if (!params.feature) { return EvaluationError { "Feature data is unavailable in the current evaluation context." }; } return params.feature->getValue(key) ? true : false; }); define("has", [](const std::string& key, const std::unordered_map& object) -> Result { return object.find(key) != object.end(); }); define("get", [](const EvaluationContext& params, const std::string& key) -> Result { if (!params.feature) { return EvaluationError { "Feature data is unavailable in the current evaluation context." }; } auto propertyValue = params.feature->getValue(key); if (!propertyValue) { return Null; } return Value(toExpressionValue(*propertyValue)); }); define("get", [](const std::string& key, const std::unordered_map& object) -> Result { if (object.find(key) == object.end()) { return Null; } return object.at(key); }); define("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; }); define("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"; } }); define("id", [](const EvaluationContext& params) -> Result { if (!params.feature) { return EvaluationError { "Feature data is unavailable in the current evaluation context." }; } auto id = params.feature->getID(); if (!id) { return Null; } return id->match( [](const auto& idValue) { return toExpressionValue(mbgl::Value(idValue)); } ); }); define("+", [](const Varargs& args) -> Result { double sum = 0.0f; for (auto arg : args) { sum += arg; } return sum; }); define("-", [](double a, double b) -> Result { return a - b; }); define("-", [](double a) -> Result { return -a; }); define("*", [](const Varargs& args) -> Result { double prod = 1.0f; for (auto arg : args) { prod *= arg; } return prod; }); define("/", [](double a, double b) -> Result { return a / b; }); define("%", [](double a, double b) -> Result { return fmod(a, b); }); define("^", [](double a, double b) -> Result { return pow(a, b); }); define("sqrt", [](double x) -> Result { return sqrt(x); }); define("log10", [](double x) -> Result { return log10(x); }); define("ln", [](double x) -> Result { return log(x); }); define("log2", [](double x) -> Result { return util::log2(x); }); define("sin", [](double x) -> Result { return sin(x); }); define("cos", [](double x) -> Result { return cos(x); }); define("tan", [](double x) -> Result { return tan(x); }); define("asin", [](double x) -> Result { return asin(x); }); define("acos", [](double x) -> Result { return acos(x); }); define("atan", [](double x) -> Result { return atan(x); }); define("min", [](const Varargs& args) -> Result { double result = std::numeric_limits::infinity(); for (double arg : args) { result = fmin(arg, result); } return result; }); define("max", [](const Varargs& args) -> Result { double result = -std::numeric_limits::infinity(); for (double arg : args) { result = fmax(arg, result); } return result; }); define("round", [](double x) -> Result { return ::round(x); }); define("floor", [](double x) -> Result { return std::floor(x); }); define("ceil", [](double x) -> Result { return std::ceil(x); }); define("abs", [](double x) -> Result { return std::abs(x); }); define(">", [](double lhs, double rhs) -> Result { return lhs > rhs; }); define(">", [](const std::string& lhs, const std::string& rhs) -> Result { return lhs > rhs; }); define(">=", [](double lhs, double rhs) -> Result { return lhs >= rhs; }); define(">=",[](const std::string& lhs, const std::string& rhs) -> Result { return lhs >= rhs; }); define("<", [](double lhs, double rhs) -> Result { return lhs < rhs; }); define("<", [](const std::string& lhs, const std::string& rhs) -> Result { return lhs < rhs; }); define("<=", [](double lhs, double rhs) -> Result { return lhs <= rhs; }); define("<=", [](const std::string& lhs, const std::string& rhs) -> Result { return lhs <= rhs; }); define("!", [](bool e) -> Result { return !e; }); define("is-supported-script", [](const std::string& x) -> Result { return util::i18n::isStringInSupportedScript(x); }); define("upcase", [](const std::string& input) -> Result { return platform::uppercase(input); }); define("downcase", [](const std::string& input) -> Result { return platform::lowercase(input); }); define("concat", [](const Varargs& args) -> Result { std::string s; for (const std::string& arg : args) { s += arg; } return s; }); define("error", [](const std::string& input) -> Result { return EvaluationError { input }; }); return definitions; } std::unordered_map CompoundExpressionRegistry::definitions = initializeDefinitions(); using namespace mbgl::style::conversion; ParseResult parseCompoundExpression(const std::string name, const Convertible& value, ParsingContext& ctx) { assert(isArray(value) && arrayLength(value) > 0); auto it = CompoundExpressionRegistry::definitions.find(name); if (it == CompoundExpressionRegistry::definitions.end()) { ctx.error( R"(Unknown expression ")" + name + R"(". If you wanted a literal array, use ["literal", [...]].)", 0 ); return ParseResult(); } const CompoundExpressionRegistry::Definition& definition = it->second; auto length = arrayLength(value); // Check if we have a single signature with the correct number of // parameters. If so, then use that signature's parameter types for parsing // (and inferring the types of) the arguments. optional singleMatchingSignature; for (std::size_t j = 0; j < definition.size(); j++) { const std::unique_ptr& signature = definition[j]; if ( signature->params.is() || signature->params.get>().size() == length - 1 ) { if (singleMatchingSignature) { singleMatchingSignature = {}; } else { singleMatchingSignature = j; } } } // parse subexpressions first std::vector> args; args.reserve(length - 1); for (std::size_t i = 1; i < length; i++) { optional expected; if (singleMatchingSignature) { expected = definition[*singleMatchingSignature]->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) { return parsed; } args.push_back(std::move(*parsed)); } return createCompoundExpression(definition, std::move(args), ctx); } ParseResult createCompoundExpression(const std::string& name, std::vector> args, ParsingContext& ctx) { return createCompoundExpression(CompoundExpressionRegistry::definitions.at(name), std::move(args), ctx); } ParseResult createCompoundExpression(const Definition& definition, std::vector> args, ParsingContext& ctx) { ParsingContext signatureContext(ctx.getKey()); for (const std::unique_ptr& signature : definition) { 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(signature->makeExpression(std::move(args))); } } if (definition.size() == 1) { ctx.appendErrors(std::move(signatureContext)); } else { std::string signatures; for (const auto& signature : definition) { signatures += (signatures.size() > 0 ? " | " : ""); signature->params.match( [&](const VarargsType& varargs) { signatures += "(" + toString(varargs.type) + ")"; }, [&](const std::vector& params) { signatures += "("; bool first = true; for (const type::Type& param : params) { if (!first) signatures += ", "; signatures += toString(param); first = false; } signatures += ")"; } ); } std::string actualTypes; for (const auto& arg : args) { if (actualTypes.size() > 0) { actualTypes += ", "; } actualTypes += toString(arg->getType()); } ctx.error("Expected arguments of type " + signatures + ", but found (" + actualTypes + ") instead."); } return ParseResult(); } } // namespace expression } // namespace style } // namespace mbgl