summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnand Thakker <github@anandthakker.net>2017-07-17 12:29:14 -0400
committerAnand Thakker <github@anandthakker.net>2017-08-11 21:54:51 -0400
commit09cf58068498e81a9771243db53ab98ba1c9faa2 (patch)
tree2f70fa6ca13341893fcf4cce3fccbef0ef941609
parentfd2c1055a786c365b973a80b3f8cc36c9ba2fa8d (diff)
downloadqtlocation-mapboxgl-09cf58068498e81a9771243db53ab98ba1c9faa2.tar.gz
Refactor to infer CompoundExpression from type of evaluate()
-rw-r--r--cmake/core-files.cmake6
-rw-r--r--include/mbgl/style/expression/compound_expression.hpp334
-rw-r--r--include/mbgl/style/expression/definitions.hpp322
-rw-r--r--include/mbgl/style/expression/expression.hpp146
-rw-r--r--include/mbgl/style/expression/parse.hpp43
-rw-r--r--include/mbgl/style/expression/type_check.hpp14
-rw-r--r--platform/node/src/node_expression.cpp14
-rw-r--r--platform/node/src/node_expression.hpp4
-rw-r--r--src/mbgl/style/expression/compound_expression.cpp162
-rw-r--r--src/mbgl/style/expression/definitions.cpp355
-rw-r--r--src/mbgl/style/expression/type_check.cpp121
-rw-r--r--src/mbgl/style/function/expression.cpp23
12 files changed, 552 insertions, 992 deletions
diff --git a/cmake/core-files.cmake b/cmake/core-files.cmake
index ee15bf5b91..a86900f06b 100644
--- a/cmake/core-files.cmake
+++ b/cmake/core-files.cmake
@@ -390,16 +390,14 @@ set(MBGL_CORE_FILES
src/mbgl/style/conversion/stringify.hpp
# style/expression
- include/mbgl/style/expression/definitions.hpp
+ include/mbgl/style/expression/compound_expression.hpp
include/mbgl/style/expression/expression.hpp
include/mbgl/style/expression/parse.hpp
include/mbgl/style/expression/parsing_context.hpp
include/mbgl/style/expression/type.hpp
- include/mbgl/style/expression/type_check.hpp
include/mbgl/style/expression/value.hpp
- src/mbgl/style/expression/definitions.cpp
+ src/mbgl/style/expression/compound_expression.cpp
src/mbgl/style/expression/type.cpp
- src/mbgl/style/expression/type_check.cpp
src/mbgl/style/expression/value.cpp
# style/function
diff --git a/include/mbgl/style/expression/compound_expression.hpp b/include/mbgl/style/expression/compound_expression.hpp
new file mode 100644
index 0000000000..cbdcb115e4
--- /dev/null
+++ b/include/mbgl/style/expression/compound_expression.hpp
@@ -0,0 +1,334 @@
+#pragma once
+
+#include <array>
+#include <vector>
+#include <memory>
+#include <mbgl/util/optional.hpp>
+#include <mbgl/util/variant.hpp>
+#include <mbgl/util/color.hpp>
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/expression/type.hpp>
+#include <mbgl/style/expression/value.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
+#include <mbgl/style/conversion.hpp>
+
+#define expand_pack(...) (void) std::initializer_list<int>{((void)(__VA_ARGS__), 0)...};
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+namespace detail {
+ template <typename T>
+ std::decay_t<T> get(const Value& value);
+} // namespace detail
+
+
+struct VarargsType { type::Type type; };
+template <typename T>
+struct Varargs : std::vector<T> { using std::vector<T>::vector; };
+
+
+struct SignatureBase {
+ SignatureBase(type::Type result_, variant<std::vector<type::Type>, VarargsType> params_) :
+ result(result_),
+ params(params_)
+ {}
+ virtual ~SignatureBase() {}
+ virtual std::unique_ptr<TypedExpression> makeTypedExpression(std::vector<std::unique_ptr<TypedExpression>>) const = 0;
+ type::Type result;
+ variant<std::vector<type::Type>, VarargsType> params;
+};
+
+template <class, class Enable = void>
+struct Signature;
+
+// Signature from a zoom- or property-dependent evaluation function:
+// (const EvaluationParameters&, T1, T2, ...) => Result<U>,
+// where T1, T2, etc. are the types of successfully-evaluated subexpressions.
+template <class R, class... Params>
+struct Signature<R (const EvaluationParameters&, Params...)> : SignatureBase {
+ using Args = std::array<std::unique_ptr<TypedExpression>, sizeof...(Params)>;
+
+ Signature(R (*evaluate_)(const EvaluationParameters&, Params...),
+ bool isFeatureConstant_ = true,
+ bool isZoomConstant_ = true) :
+ SignatureBase(
+ valueTypeToExpressionType<std::decay_t<typename R::Value>>(),
+ std::vector<type::Type> {valueTypeToExpressionType<std::decay_t<Params>>()...}
+ ),
+ evaluate(evaluate_),
+ featureConstant(isFeatureConstant_),
+ zoomConstant(isZoomConstant_)
+ {}
+
+ std::unique_ptr<TypedExpression> makeTypedExpression(std::vector<std::unique_ptr<TypedExpression>> args) const override;
+
+ bool isFeatureConstant() const { return featureConstant; }
+ bool isZoomConstant() const { return zoomConstant; }
+ EvaluationResult apply(const EvaluationParameters& evaluationParameters, const Args& args) const {
+ return applyImpl(evaluationParameters, args, std::index_sequence_for<Params...>{});
+ }
+
+private:
+ template <std::size_t ...I>
+ EvaluationResult applyImpl(const EvaluationParameters& evaluationParameters, const Args& args, std::index_sequence<I...>) const {
+ const std::vector<EvaluationResult>& evaluated = {std::get<I>(args)->evaluate(evaluationParameters)...};
+ for (const auto& arg : evaluated) {
+ if(!arg) return arg.error();
+ }
+ // TODO: assert correct runtime type of each arg value
+ const R& result = evaluate(evaluationParameters, detail::get<Params>(*(evaluated.at(I)))...);
+ if (!result) return result.error();
+ return *result;
+ }
+
+ R (*evaluate)(const EvaluationParameters&, Params...);
+ bool featureConstant;
+ bool zoomConstant;
+};
+
+// Signature from varargs evaluation function: (Varargs<T>) => Result<U>,
+// where T is the type of each successfully-evaluated subexpression (Varargs<T> being
+// an alias for vector<T>).
+template <class R, typename T>
+struct Signature<R (const Varargs<T>&)> : SignatureBase {
+ using Args = std::vector<std::unique_ptr<TypedExpression>>;
+
+ Signature(R (*evaluate_)(const Varargs<T>&)) :
+ SignatureBase(
+ valueTypeToExpressionType<std::decay_t<typename R::Value>>(),
+ VarargsType { valueTypeToExpressionType<T>() }
+ ),
+ evaluate(evaluate_)
+ {}
+
+ std::unique_ptr<TypedExpression> makeTypedExpression(std::vector<std::unique_ptr<TypedExpression>> args) const override;
+
+ bool isFeatureConstant() const { return true; }
+ bool isZoomConstant() const { return true; }
+ EvaluationResult apply(const EvaluationParameters& evaluationParameters, const Args& args) const {
+ Varargs<T> evaluated;
+ for (const auto& arg : args) {
+ const auto& evaluatedArg = arg->evaluate<T>(evaluationParameters);
+ if(!evaluatedArg) return evaluatedArg.error();
+ evaluated.push_back(*evaluatedArg);
+ }
+ const R& result = evaluate(evaluated);
+ if (!result) return result.error();
+ return *result;
+ }
+
+ R (*evaluate)(const Varargs<T>&);
+};
+
+// Signature from "pure" evaluation function: (T1, T2, ...) => Result<U>,
+// where T1, T2, etc. are the types of successfully-evaluated subexpressions.
+template <class R, class... Params>
+struct Signature<R (Params...)> : SignatureBase {
+ using Args = std::array<std::unique_ptr<TypedExpression>, sizeof...(Params)>;
+
+ Signature(R (*evaluate_)(Params...)) :
+ SignatureBase(
+ valueTypeToExpressionType<std::decay_t<typename R::Value>>(),
+ std::vector<type::Type> {valueTypeToExpressionType<std::decay_t<Params>>()...}
+ ),
+ evaluate(evaluate_)
+ {}
+
+ bool isFeatureConstant() const { return true; }
+ bool isZoomConstant() const { return true; }
+ EvaluationResult apply(const EvaluationParameters& evaluationParameters, const Args& args) const {
+ return applyImpl(evaluationParameters, args, std::index_sequence_for<Params...>{});
+ }
+
+ std::unique_ptr<TypedExpression> makeTypedExpression(std::vector<std::unique_ptr<TypedExpression>> args) const override;
+
+ R (*evaluate)(Params...);
+private:
+ template <std::size_t ...I>
+ EvaluationResult applyImpl(const EvaluationParameters& evaluationParameters, const Args& args, std::index_sequence<I...>) const {
+ const std::vector<EvaluationResult>& evaluated = {std::get<I>(args)->evaluate(evaluationParameters)...};
+ for (const auto& arg : evaluated) {
+ if(!arg) return arg.error();
+ }
+ // TODO: assert correct runtime type of each arg value
+ const R& result = evaluate(detail::get<Params>(*(evaluated.at(I)))...);
+ if (!result) return result.error();
+ return *result;
+ }
+};
+
+template <class R, class... Params>
+struct Signature<R (*)(Params...)>
+ : Signature<R (Params...)>
+{ using Signature<R (Params...)>::Signature; };
+
+template <class T, class R, class... Params>
+struct Signature<R (T::*)(Params...) const>
+ : Signature<R (Params...)>
+{ using Signature<R (Params...)>::Signature; };
+
+template <class T, class R, class... Params>
+struct Signature<R (T::*)(Params...)>
+ : Signature<R (Params...)>
+{ using Signature<R (T::*)(Params...)>::Signature; };
+
+template <class Lambda>
+struct Signature<Lambda, std::enable_if_t<std::is_class<Lambda>::value>>
+ : Signature<decltype(&Lambda::operator())>
+{ using Signature<decltype(&Lambda::operator())>::Signature; };
+
+
+struct CompoundExpression {
+ using Definition = std::vector<std::unique_ptr<SignatureBase>>;
+ static std::unordered_map<std::string, Definition> definitions;
+};
+
+template <typename Signature>
+class TypedCompoundExpression : public TypedExpression {
+public:
+ TypedCompoundExpression(Signature signature_,
+ typename Signature::Args args_) :
+ TypedExpression(signature_.result),
+ signature(signature_),
+ args(std::move(args_))
+ {}
+
+ EvaluationResult evaluate(const EvaluationParameters& params) const override {
+ return signature.apply(params, args);
+ }
+
+ bool isFeatureConstant() const override {
+ if (!signature.isFeatureConstant()) {
+ return false;
+ }
+ for (const auto& arg : args) {
+ if (!arg->isFeatureConstant()) { return false; }
+ }
+ return true;
+ }
+
+ bool isZoomConstant() const override {
+ if (!signature.isZoomConstant()) {
+ return false;
+ }
+ for (const auto& arg : args) {
+ if (!arg->isZoomConstant()) { return false; }
+ }
+ return true;
+ }
+
+private:
+ Signature signature;
+ typename Signature::Args args;
+};
+
+
+class UntypedCompoundExpression : public UntypedExpression {
+public:
+ using Args = std::vector<std::unique_ptr<UntypedExpression>>;
+
+ UntypedCompoundExpression(std::string key, std::string name_, std::vector<std::unique_ptr<UntypedExpression>> args_) :
+ UntypedExpression(key),
+ name(name_),
+ args(std::move(args_))
+ {}
+
+ template <class V>
+ static ParseResult parse(const V& value, const ParsingContext& ctx) {
+ using namespace mbgl::style::conversion;
+ assert(isArray(value) && arrayLength(value) > 0);
+ const auto& name = toString(arrayMember(value, 0));
+ assert(name);
+
+ if (CompoundExpression::definitions.find(*name) == CompoundExpression::definitions.end()) {
+ return CompileError {
+ std::string("Unknown expression \"") + *name + "\". If you wanted a literal array, use [\"literal\", [...]].",
+ ctx.key(0)
+ };
+ }
+
+ std::vector<std::unique_ptr<UntypedExpression>> args;
+ auto length = arrayLength(value);
+ for (std::size_t i = 1; i < length; i++) {
+ auto parsed = parseExpression(arrayMember(value, i), ParsingContext(ctx, {i}, name));
+ if (parsed.template is<CompileError>()) {
+ return parsed;
+ }
+ args.push_back(std::move(parsed.template get<std::unique_ptr<UntypedExpression>>()));
+ }
+
+ return std::make_unique<UntypedCompoundExpression>(ctx.key(), *name, std::move(args));
+ }
+
+ TypecheckResult typecheck(std::vector<CompileError>& errors) const override {
+ const auto& definition = CompoundExpression::definitions.at(name);
+
+ std::vector<CompileError> currentSignatureErrors;
+ for (const auto& signature : definition) {
+ currentSignatureErrors.clear();
+ std::vector<std::unique_ptr<TypedExpression>> checkedArgs;
+ if (signature->params.is<std::vector<type::Type>>()) {
+ const auto& params = signature->params.get<std::vector<type::Type>>();
+ if (params.size() != args.size()) {
+ currentSignatureErrors.emplace_back(CompileError {
+ "Expected " + std::to_string(params.size()) +
+ " arguments, but found " + std::to_string(args.size()) + " instead.",
+ getKey()
+ });
+ continue;
+ }
+
+ for (std::size_t i = 0; i < args.size(); i++) {
+ const auto& arg = args.at(i);
+ auto checked = arg->typecheck(currentSignatureErrors);
+ if (checked) {
+ const auto& param = params.at(i);
+ const auto& error = matchType(param, (*checked)->getType());
+ if (error) {
+ currentSignatureErrors.emplace_back(CompileError {
+ *error,
+ getKey()
+ });
+ } else {
+ checkedArgs.push_back(std::move(*checked));
+ }
+ }
+ }
+ } else if (signature->params.is<VarargsType>()) {
+ const auto& paramType = signature->params.get<VarargsType>().type;
+ for (std::size_t i = 0; i < args.size(); i++) {
+ const auto& arg = args.at(i);
+ auto checked = arg->typecheck(currentSignatureErrors);
+ if (checked) {
+ const auto& error = matchType(paramType, (*checked)->getType());
+ if (error) {
+ currentSignatureErrors.emplace_back(CompileError {
+ *error,
+ getKey()
+ });
+ } else {
+ checkedArgs.push_back(std::move(*checked));
+ }
+ }
+ }
+ }
+
+ if (currentSignatureErrors.size() == 0) {
+ return signature->makeTypedExpression(std::move(checkedArgs));
+ }
+ }
+
+ errors.insert(errors.end(), currentSignatureErrors.begin(), currentSignatureErrors.end());
+ return {};
+ }
+
+private:
+ std::string name;
+ std::vector<std::unique_ptr<UntypedExpression>> args;
+};
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/definitions.hpp b/include/mbgl/style/expression/definitions.hpp
deleted file mode 100644
index 7557e9a682..0000000000
--- a/include/mbgl/style/expression/definitions.hpp
+++ /dev/null
@@ -1,322 +0,0 @@
-#include <mbgl/style/expression/expression.hpp>
-
-namespace mbgl {
-namespace style {
-namespace expression {
-
-
-// Concrete expression definitions
-class MathConstant : public LambdaExpression {
-public:
- MathConstant(const std::string& key, const std::string& name, float value_) :
- LambdaExpression(key, name, {}, type::Number, {{}}),
- value(value_)
- {}
-
- EvaluationResult evaluate(const EvaluationParameters&) const override { return value; }
-
- std::unique_ptr<Expression> applyInferredType(const type::Type&, Args) const override {
- return std::make_unique<MathConstant>(getKey(), getName(), value);
- }
-
- // TODO: declaring these constants like `static constexpr double E = 2.718...` caused
- // a puzzling link error.
- static std::unique_ptr<Expression> ln2(const ParsingContext& ctx) {
- return std::make_unique<MathConstant>(ctx.key(), "ln2", 0.693147180559945309417);
- }
- static std::unique_ptr<Expression> e(const ParsingContext& ctx) {
- return std::make_unique<MathConstant>(ctx.key(), "e", 2.71828182845904523536);
- }
- static std::unique_ptr<Expression> pi(const ParsingContext& ctx) {
- return std::make_unique<MathConstant>(ctx.key(), "pi", 3.14159265358979323846);
- }
-private:
- float value;
-};
-
-class TypeOf : public LambdaBase<TypeOf> {
-public:
- using LambdaBase::LambdaBase;
- static type::StringType type() { return type::String; };
- static std::vector<Params> signatures() {
- return {{type::Value}};
- };
- EvaluationResult evaluate(const EvaluationParameters& params) const override;
-};
-
-// TODO: This doesn't work with CRTP. With:
-// `template <typename T> class Assertion : public LambdaBase<Assertion<T>>`,
-// there's an error referring to member 'args'.
-template <typename T>
-class Assertion : public LambdaExpression {
-public:
- Assertion(const std::string& key, const std::string& name, Args args) :
- LambdaExpression(key, name, std::move(args), Assertion::type(), Assertion::signatures())
- {}
- Assertion(const std::string& key, const std::string& name, const type::Type& type, Args args) :
- LambdaExpression(key, name, std::move(args), type, Assertion::signatures())
- {}
-
- static type::Type type() { return valueTypeToExpressionType<T>(); }
- static std::vector<LambdaExpression::Params> signatures() { return {{type::Value}}; };
-
- std::unique_ptr<Expression> applyInferredType(const type::Type& type, Args args) const override {
- return std::make_unique<Assertion>(getKey(), getName(), type, std::move(args));
- }
-
- EvaluationResult evaluate(const EvaluationParameters& params) const override {
- const auto& result = args[0]->template evaluate<T>(params);
- if (result) return *result;
- return result.error();
- };
-};
-
-class Array : public LambdaBase<Array> {
-public:
- Array(const std::string& key, const std::string& name, type::Type type, Args args) :
- LambdaBase(key, name, type, std::move(args))
- {}
-
- static std::vector<Params> signatures() { return {{type::Value}}; }
-
- template <typename V>
- static ParseResult parse(const V& value, const ParsingContext& ctx) {
- assert(isArray(value));
- auto length = arrayLength(value);
- if (length < 2) return CompileError { "Expected at least one argument to \"array\"", ctx.key() };
- if (length > 4) return CompileError {
- "Expected one, two, or three arguments to \"array\", but found " + std::to_string(length - 1) + " instead.",
- ctx.key()
- };
-
- const std::string& name = *toString(arrayMember(value, 0));
-
- auto arg = parseExpression(arrayMember(value, 1), ParsingContext(ctx, {1}, {"array"}));
- if (arg.template is<CompileError>()) return arg.template get<CompileError>();
- Args args;
- args.push_back(std::move(arg.template get<std::unique_ptr<Expression>>()));
-
- // parse the optional item type and length arguments
- optional<type::Type> itemType;
- optional<size_t> N;
- if (length > 2) {
- const auto& itemTypeName = toString(arrayMember(value, 2));
- if (itemTypeName && *itemTypeName == "string") {
- itemType = {type::String};
- } else if (itemTypeName && *itemTypeName == "number") {
- itemType = {type::String};
- } else if (itemTypeName && *itemTypeName == "boolean") {
- itemType = {type::String};
- } else {
- return CompileError {
- "The item type argument to \"array\" must be one of ${Object.keys(types).join(', ')}",
- ctx.key(2)
- };
- }
- }
- if (length > 3) {
- const auto& arrayLength = toNumber(arrayMember(value, 3));
- if (!arrayLength) return CompileError {
- "The length argument to \"array\" must be a number literal.",
- ctx.key(3)
- };
- N = static_cast<size_t>(*arrayLength);
- }
- return std::make_unique<Array>(ctx.key(), name, type::Array(type::Value), std::move(args));
- }
-
- EvaluationResult evaluate(const EvaluationParameters& params) const override;
-};
-
-class ToString : public LambdaBase<ToString> {
-public:
- using LambdaBase::LambdaBase;
- static type::Type type() { return type::String; };
- static std::vector<Params> signatures() { return {{ type::Value }}; };
- EvaluationResult evaluate(const EvaluationParameters& params) const override;
-};
-
-class ToNumber : public LambdaBase<ToNumber> {
-public:
- using LambdaBase::LambdaBase;
- static type::Type type() { return type::Number; };
- static std::vector<Params> signatures() { return {{ type::Value }}; };
- EvaluationResult evaluate(const EvaluationParameters& params) const override;
-};
-
-class ToBoolean : public LambdaBase<ToBoolean> {
-public:
- using LambdaBase::LambdaBase;
- static type::Type type() { return type::Boolean; };
- static std::vector<Params> signatures() { return {{ type::Value }}; };
- EvaluationResult evaluate(const EvaluationParameters& params) const override;
-};
-
-class ToRGBA : public LambdaBase<ToRGBA> {
-public:
- using LambdaBase::LambdaBase;
- static type::Type type() { return type::Array(type::Number, 4); };
- static std::vector<Params> signatures() { return {{ type::Color }}; };
- EvaluationResult evaluate(const EvaluationParameters& params) const override;
-};
-
-class ParseColor : public LambdaBase<ParseColor> {
-public:
- using LambdaBase::LambdaBase;
- static type::Type type() { return type::Color; };
- static std::vector<Params> signatures() { return {{ type::String }}; };
- EvaluationResult evaluate(const EvaluationParameters& params) const override;
-};
-
-class RGB : public LambdaBase<RGB> {
-public:
- using LambdaBase::LambdaBase;
- static type::Type type() { return type::Color; };
- static std::vector<Params> signatures() { return {{ type::Number, type::Number, type::Number }}; };
- EvaluationResult evaluate(const EvaluationParameters& params) const override;
-};
-
-class RGBA : public LambdaBase<RGBA> {
-public:
- using LambdaBase::LambdaBase;
- static type::Type type() { return type::Color; };
- static std::vector<Params> signatures() { return {{ type::Number, type::Number, type::Number, type::Number }}; };
- EvaluationResult evaluate(const EvaluationParameters& params) const override;
-};
-
-class Get : public LambdaBase<Get> {
-public:
- using LambdaBase::LambdaBase;
- static type::ValueType type() { return type::Value; };
- static std::vector<Params> signatures() {
- return {{type::String, NArgs { {type::Object}, 1 }}};
- };
- bool isFeatureConstant() const override;
- EvaluationResult evaluate(const EvaluationParameters& params) const override;
-};
-
-class Has : public LambdaBase<Has> {
-public:
- using LambdaBase::LambdaBase;
- static type::Type type() { return type::Boolean; };
- static std::vector<Params> signatures() {
- return {{type::String, NArgs { {type::Object}, 1 }}};
- };
- bool isFeatureConstant() const override;
- EvaluationResult evaluate(const EvaluationParameters& params) const override;
-};
-
-class At : public LambdaBase<At> {
-public:
- using LambdaBase::LambdaBase;
- static type::Type type() { return type::Typename("T"); };
- static std::vector<Params> signatures() {
- return {{type::Number, type::Array(type::Typename("T"))}};
- };
- EvaluationResult evaluate(const EvaluationParameters& params) const override;
-};
-
-class Length : public LambdaBase<Length> {
-public:
- using LambdaBase::LambdaBase;
- static type::Type type() { return type::Number; };
- static std::vector<Params> signatures() {
- return {
- {type::Array(type::Typename("T"))},
- {type::String}
- };
- };
- EvaluationResult evaluate(const EvaluationParameters& params) const override;
-};
-
-class Properties : public LambdaBase<Properties> {
-public:
- using LambdaBase::LambdaBase;
- static type::Type type() { return type::Object; };
- static std::vector<Params> signatures() { return {{}}; };
- EvaluationResult evaluate(const EvaluationParameters& params) const override;
- bool isFeatureConstant() const override;
-};
-
-class Id : public LambdaBase<Id> {
-public:
- using LambdaBase::LambdaBase;
- static type::Type type() { return type::Value; };
- static std::vector<Params> signatures() { return {{}}; };
- EvaluationResult evaluate(const EvaluationParameters& params) const override;
- bool isFeatureConstant() const override;
-};
-
-class GeometryType : public LambdaBase<GeometryType> {
-public:
- using LambdaBase::LambdaBase;
- static type::Type type() { return type::String; };
- static std::vector<Params> signatures() { return {{}}; };
- EvaluationResult evaluate(const EvaluationParameters& params) const override;
- bool isFeatureConstant() const override;
-};
-
-class Plus : public LambdaBase<Plus> {
-public:
- using LambdaBase::LambdaBase;
- static type::NumberType type() { return type::Number; };
- static std::vector<Params> signatures() {
- return {{NArgs {{type::Number}, {}}}};
- };
- EvaluationResult evaluate(const EvaluationParameters& params) const override;
-};
-
-class Times : public LambdaBase<Times> {
-public:
- using LambdaBase::LambdaBase;
- static type::NumberType type() { return type::Number; };
- static std::vector<Params> signatures() {
- return {{NArgs {{type::Number}, {}}}};
- };
- EvaluationResult evaluate(const EvaluationParameters& params) const override;
-};
-
-class Minus : public LambdaBase<Minus> {
-public:
- using LambdaBase::LambdaBase;
- static type::NumberType type() { return type::Number; };
- static std::vector<Params> signatures() {
- return {{type::Number, type::Number}};
- };
- EvaluationResult evaluate(const EvaluationParameters& params) const override;
-};
-
-class Divide : public LambdaBase<Divide> {
-public:
- using LambdaBase::LambdaBase;
- static type::NumberType type() { return type::Number; };
- static std::vector<Params> signatures() {
- return {{type::Number, type::Number}};
- };
- EvaluationResult evaluate(const EvaluationParameters& params) const override;
-};
-
-class Mod : public LambdaBase<Mod> {
-public:
- using LambdaBase::LambdaBase;
- static type::NumberType type() { return type::Number; };
- static std::vector<Params> signatures() {
- return {{type::Number, type::Number}};
- };
- EvaluationResult evaluate(const EvaluationParameters& params) const override;
-};
-
-class Power : public LambdaBase<Power> {
-public:
- using LambdaBase::LambdaBase;
- static type::NumberType type() { return type::Number; };
- static std::vector<Params> signatures() {
- return {{type::Number, type::Number}};
- };
- EvaluationResult evaluate(const EvaluationParameters& params) const override;
-};
-
-
-} // namespace expression
-} // namespace style
-} // namespace mbgl
diff --git a/include/mbgl/style/expression/expression.hpp b/include/mbgl/style/expression/expression.hpp
index 9c4d55784a..ef94ad9cbd 100644
--- a/include/mbgl/style/expression/expression.hpp
+++ b/include/mbgl/style/expression/expression.hpp
@@ -32,6 +32,7 @@ template<typename T>
class Result : private variant<EvaluationError, T> {
public:
using variant<EvaluationError, T>::variant;
+ using Value = T;
explicit operator bool () const {
return this->template is<T>();
@@ -72,11 +73,13 @@ struct CompileError {
std::string key;
};
-class Expression {
+class TypedExpression {
public:
- Expression(std::string key_, type::Type type_) : key(key_), type(type_) {}
- virtual ~Expression() {};
+ TypedExpression(type::Type type_) : type(type_) {}
+ virtual ~TypedExpression() {};
+ virtual bool isFeatureConstant() const { return true; }
+ virtual bool isZoomConstant() const { return true; }
virtual EvaluationResult evaluate(const EvaluationParameters& params) const = 0;
/*
@@ -101,47 +104,56 @@ public:
EvaluationResult evaluate(float z, const Feature& feature) const;
type::Type getType() const { return type; }
- std::string getKey() const { return key; }
-
- virtual bool isFeatureConstant() const { return true; }
- virtual bool isZoomConstant() const { return true; }
-
-protected:
- void setType(type::Type newType) { type = newType; }
private:
- std::string key;
type::Type type;
};
+using TypecheckResult = optional<std::unique_ptr<TypedExpression>>;
+
+class UntypedExpression {
+public:
+ UntypedExpression(std::string key_) : key(key_) {}
+ virtual ~UntypedExpression() {}
+
+ std::string getKey() const { return key; }
+ virtual TypecheckResult typecheck(std::vector<CompileError>& errors) const = 0;
+private:
+ std::string key;
+};
-using ParseResult = variant<CompileError, std::unique_ptr<Expression>>;
+using ParseResult = variant<CompileError, std::unique_ptr<UntypedExpression>>;
template <class V>
ParseResult parseExpression(const V& value, const ParsingContext& context);
-using TypecheckResult = variant<std::vector<CompileError>, std::unique_ptr<Expression>>;
-
-using namespace mbgl::style::conversion;
-
-class LiteralExpression : public Expression {
+class TypedLiteral : public TypedExpression {
public:
- LiteralExpression(std::string key_, Value value_) : Expression(key_, typeOf(value_)), value(value_) {}
-
- Value getValue() const { return value; }
-
+ TypedLiteral(Value value_) : TypedExpression(typeOf(value_)), value(value_) {}
EvaluationResult evaluate(const EvaluationParameters&) const override {
return value;
}
-
+private:
+ Value value;
+};
+
+class UntypedLiteral : public UntypedExpression {
+public:
+ UntypedLiteral(std::string key_, Value value_) : UntypedExpression(key_), value(value_) {}
+
+ TypecheckResult typecheck(std::vector<CompileError>&) const override {
+ return {std::make_unique<TypedLiteral>(value)};
+ }
+
template <class V>
static ParseResult parse(const V& value, const ParsingContext& ctx) {
const Value& parsedValue = parseValue(value);
- return std::make_unique<LiteralExpression>(ctx.key(), parsedValue);
+ return std::make_unique<UntypedLiteral>(ctx.key(), parsedValue);
}
-
+
private:
template <class V>
static Value parseValue(const V& value) {
+ using namespace mbgl::style::conversion;
if (isUndefined(value)) return Null;
if (isObject(value)) {
std::unordered_map<std::string, Value> result;
@@ -164,94 +176,10 @@ private:
assert(v);
return convertValue(*v);
}
-
- Value value;
-};
-
-struct NArgs {
- std::vector<type::Type> types;
- optional<std::size_t> N;
-};
-
-class LambdaExpression : public Expression {
-public:
- using Params = std::vector<variant<type::Type, NArgs>>;
- using Args = std::vector<std::unique_ptr<Expression>>;
-
- LambdaExpression(std::string key_,
- std::string name_,
- Args args_,
- type::Type type_,
- std::vector<Params> signatures_) :
- Expression(key_, type_),
- args(std::move(args_)),
- signatures(signatures_),
- name(name_)
- {}
-
- std::string getName() const { return name; }
-
- virtual bool isFeatureConstant() const override {
- bool isFC = true;
- for (const auto& arg : args) {
- isFC = isFC && arg->isFeatureConstant();
- }
- return isFC;
- }
-
- virtual bool isZoomConstant() const override {
- bool isZC = true;
- for (const auto& arg : args) {
- isZC = isZC && arg->isZoomConstant();
- }
- return isZC;
- }
- virtual std::unique_ptr<Expression> applyInferredType(const type::Type& type, Args args) const = 0;
-
- friend TypecheckResult typecheck(const type::Type& expected, const std::unique_ptr<Expression>& e);
-
- template <class Expr, class V>
- static ParseResult parse(const V& value, const ParsingContext& ctx) {
- assert(isArray(value));
- auto length = arrayLength(value);
- const std::string& name = *toString(arrayMember(value, 0));
- Args args;
- for(size_t i = 1; i < length; i++) {
- const auto& arg = arrayMember(value, i);
- auto parsedArg = parseExpression(arg, ParsingContext(ctx, i, {}));
- if (parsedArg.template is<std::unique_ptr<Expression>>()) {
- args.push_back(std::move(parsedArg.template get<std::unique_ptr<Expression>>()));
- } else {
- return parsedArg.template get<CompileError>();
- }
- }
- return std::make_unique<Expr>(ctx.key(), name, std::move(args));
- }
-
-protected:
- Args args;
-private:
- std::vector<Params> signatures;
- std::string name;
-};
-
-template<class Expr>
-class LambdaBase : public LambdaExpression {
-public:
- LambdaBase(const std::string& key, const std::string& name, Args args) :
- LambdaExpression(key, name, std::move(args), Expr::type(), Expr::signatures())
- {}
- LambdaBase(const std::string& key, const std::string& name, const type::Type& type, Args args) :
- LambdaExpression(key, name, std::move(args), type, Expr::signatures())
- {}
-
- std::unique_ptr<Expression> applyInferredType(const type::Type& type, Args args) const override {
- return std::make_unique<Expr>(getKey(), getName(), type, std::move(args));
- }
+ Value value;
};
-
} // namespace expression
} // namespace style
} // namespace mbgl
diff --git a/include/mbgl/style/expression/parse.hpp b/include/mbgl/style/expression/parse.hpp
index 9f04cef848..e621441c3b 100644
--- a/include/mbgl/style/expression/parse.hpp
+++ b/include/mbgl/style/expression/parse.hpp
@@ -3,7 +3,7 @@
#include <memory>
#include <mbgl/style/expression/parsing_context.hpp>
#include <mbgl/style/expression/expression.hpp>
-#include <mbgl/style/expression/definitions.hpp>
+#include <mbgl/style/expression/compound_expression.hpp>
#include <mbgl/style/conversion.hpp>
namespace mbgl {
@@ -30,7 +30,7 @@ std::string getJSType(const V& value) {
);
}
-using ParseResult = variant<CompileError, std::unique_ptr<Expression>>;
+using ParseResult = variant<CompileError, std::unique_ptr<UntypedExpression>>;
template <class V>
ParseResult parseExpression(const V& value, const ParsingContext& context)
@@ -62,43 +62,10 @@ ParseResult parseExpression(const V& value, const ParsingContext& context)
"'literal' expression requires exactly one argument, but found " + std::to_string(length - 1) + " instead.",
context.key()
};
- return LiteralExpression::parse(arrayMember(value, 1), ParsingContext(context, {1}, {"literal"}));
+ return UntypedLiteral::parse(arrayMember(value, 1), ParsingContext(context, {1}, {"literal"}));
}
-
- if (*op == "e") return MathConstant::e(context);
- if (*op == "pi") return MathConstant::pi(context);
- if (*op == "ln2") return MathConstant::ln2(context);
- if (*op == "typeof") return LambdaExpression::parse<TypeOf>(value, context);
- if (*op == "string") return LambdaExpression::parse<Assertion<std::string>>(value, context);
- if (*op == "number") return LambdaExpression::parse<Assertion<float>>(value, context);
- if (*op == "boolean") return LambdaExpression::parse<Assertion<bool>>(value, context);
- if (*op == "array") return Array::parse(value, context);
- if (*op == "to_string") return LambdaExpression::parse<ToString>(value, context);
- if (*op == "to_number") return LambdaExpression::parse<ToNumber>(value, context);
- if (*op == "to_boolean") return LambdaExpression::parse<ToBoolean>(value, context);
- if (*op == "to_rgba") return LambdaExpression::parse<ToRGBA>(value, context);
- if (*op == "parse_color") return LambdaExpression::parse<ParseColor>(value, context);
- if (*op == "rgba") return LambdaExpression::parse<RGBA>(value, context);
- if (*op == "rgb") return LambdaExpression::parse<RGB>(value, context);
- if (*op == "get") return LambdaExpression::parse<Get>(value, context);
- if (*op == "has") return LambdaExpression::parse<Has>(value, context);
- if (*op == "at") return LambdaExpression::parse<At>(value, context);
- if (*op == "length") return LambdaExpression::parse<Length>(value, context);
- if (*op == "properties") return LambdaExpression::parse<Properties>(value, context);
- if (*op == "id") return LambdaExpression::parse<Id>(value, context);
- if (*op == "geometry_type") return LambdaExpression::parse<GeometryType>(value, context);
- if (*op == "+") return LambdaExpression::parse<Plus>(value, context);
- if (*op == "-") return LambdaExpression::parse<Minus>(value, context);
- if (*op == "*") return LambdaExpression::parse<Times>(value, context);
- if (*op == "/") return LambdaExpression::parse<Divide>(value, context);
- if (*op == "^") return LambdaExpression::parse<Power>(value, context);
- if (*op == "%") return LambdaExpression::parse<Mod>(value, context);
-
- return CompileError {
- std::string("Unknown expression \"") + *op + "\". If you wanted a literal array, use [\"literal\", [...]].",
- context.key(0)
- };
+ return UntypedCompoundExpression::parse(value, context);
}
if (isObject(value)) {
@@ -108,7 +75,7 @@ ParseResult parseExpression(const V& value, const ParsingContext& context)
};
}
- return LiteralExpression::parse(value, context);
+ return UntypedLiteral::parse(value, context);
}
diff --git a/include/mbgl/style/expression/type_check.hpp b/include/mbgl/style/expression/type_check.hpp
deleted file mode 100644
index d30b7be002..0000000000
--- a/include/mbgl/style/expression/type_check.hpp
+++ /dev/null
@@ -1,14 +0,0 @@
-#pragma once
-
-#include <mbgl/util/variant.hpp>
-#include <mbgl/style/expression/expression.hpp>
-
-namespace mbgl {
-namespace style {
-namespace expression {
-
-TypecheckResult typecheck(const type::Type& expected, const std::unique_ptr<Expression>& e);
-
-} // namespace expression
-} // namespace style
-} // namespace mbgl
diff --git a/platform/node/src/node_expression.cpp b/platform/node/src/node_expression.cpp
index 5392f6c041..d5c0865b1f 100644
--- a/platform/node/src/node_expression.cpp
+++ b/platform/node/src/node_expression.cpp
@@ -2,7 +2,6 @@
#include "node_expression.hpp"
#include <mbgl/style/expression/parse.hpp>
-#include <mbgl/style/expression/type_check.hpp>
#include <mbgl/style/conversion/geojson.hpp>
#include <mbgl/util/geojson.hpp>
#include <nan.h>
@@ -42,20 +41,17 @@ void NodeExpression::Parse(const Nan::FunctionCallbackInfo<v8::Value>& info) {
try {
std::vector<CompileError> errors;
auto parsed = parseExpression(expr, ParsingContext());
- if (parsed.template is<std::unique_ptr<Expression>>()) {
- const auto& e = parsed.template get<std::unique_ptr<Expression>>();
- auto checked = typecheck(e->getType(), e);
- if (checked.template is<std::unique_ptr<Expression>>()) {
- auto nodeExpr = new NodeExpression(std::move(checked.template get<std::unique_ptr<Expression>>()));
+ if (parsed.template is<std::unique_ptr<UntypedExpression>>()) {
+ const auto& e = parsed.template get<std::unique_ptr<UntypedExpression>>();
+ auto checked = e->typecheck(errors);
+ if (checked) {
+ auto nodeExpr = new NodeExpression(std::move(*checked));
const int argc = 0;
v8::Local<v8::Value> argv[0] = {};
auto wrapped = Nan::NewInstance(cons, argc, argv).ToLocalChecked();
nodeExpr->Wrap(wrapped);
info.GetReturnValue().Set(wrapped);
return;
- } else {
- const auto& typeErrors = checked.template get<std::vector<CompileError>>();
- errors.insert(errors.end(), typeErrors.begin(), typeErrors.end());
}
} else {
errors.emplace_back(parsed.template get<CompileError>());
diff --git a/platform/node/src/node_expression.hpp b/platform/node/src/node_expression.hpp
index e977b58288..9bedc9421b 100644
--- a/platform/node/src/node_expression.hpp
+++ b/platform/node/src/node_expression.hpp
@@ -22,7 +22,7 @@ public:
static void Init(v8::Local<v8::Object>);
private:
- NodeExpression(std::unique_ptr<Expression> expression_) :
+ NodeExpression(std::unique_ptr<TypedExpression> expression_) :
expression(std::move(expression_))
{};
@@ -34,7 +34,7 @@ private:
static void IsZoomConstant(const Nan::FunctionCallbackInfo<v8::Value>&);
static Nan::Persistent<v8::Function> constructor;
- std::unique_ptr<Expression> expression;
+ std::unique_ptr<TypedExpression> expression;
};
} // namespace node_mbgl
diff --git a/src/mbgl/style/expression/compound_expression.cpp b/src/mbgl/style/expression/compound_expression.cpp
new file mode 100644
index 0000000000..99f0270c1d
--- /dev/null
+++ b/src/mbgl/style/expression/compound_expression.cpp
@@ -0,0 +1,162 @@
+#include <mbgl/style/expression/compound_expression.hpp>
+#include <mbgl/tile/geometry_tile_data.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+namespace detail {
+ template <> Value get<Value const&>(const Value& value) {
+ return value;
+ }
+ template <typename T> std::decay_t<T> get(const Value& value) {
+ return value.get<std::decay_t<T>>();
+ }
+} // namespace detail
+
+template <class R, class... Params>
+std::unique_ptr<TypedExpression> Signature<R (const EvaluationParameters&, Params...)>::makeTypedExpression(std::vector<std::unique_ptr<TypedExpression>> args) const {
+ typename Signature::Args argsArray;
+ std::copy_n(std::make_move_iterator(args.begin()), sizeof...(Params), argsArray.begin());
+ return std::make_unique<TypedCompoundExpression<Signature>>(*this, std::move(argsArray));
+};
+
+template <typename R, typename T>
+std::unique_ptr<TypedExpression> Signature<R (const Varargs<T>&)>::makeTypedExpression(std::vector<std::unique_ptr<TypedExpression>> args) const {
+ return std::make_unique<TypedCompoundExpression<Signature>>(*this, std::move(args));
+};
+
+template <class R, class... Params>
+std::unique_ptr<TypedExpression> Signature<R (Params...)>::makeTypedExpression(std::vector<std::unique_ptr<TypedExpression>> args) const {
+ typename Signature::Args argsArray;
+ std::copy_n(std::make_move_iterator(args.begin()), sizeof...(Params), argsArray.begin());
+ return std::make_unique<TypedCompoundExpression<Signature>>(*this, std::move(argsArray));
+};
+
+using Definition = CompoundExpression::Definition;
+
+template <typename ...Evals, typename std::enable_if_t<sizeof...(Evals) != 0, int> = 0>
+static std::pair<std::string, Definition> define(std::string name, Evals... evalFunctions) {
+ Definition definition;
+ expand_pack(definition.push_back(std::make_unique<Signature<Evals>>(evalFunctions)));
+ const auto& t0 = definition.at(0)->result;
+ for (const auto& signature : definition) {
+ // TODO replace with real ==
+ assert(toString(t0) == toString(signature->result));
+ }
+ return std::pair<std::string, Definition>(name, std::move(definition));
+}
+
+template <typename ...Evals, typename std::enable_if_t<sizeof...(Evals) != 0, int> = 0>
+static std::pair<std::string, Definition> defineFeatureFunction(std::string name, Evals... evalFunctions) {
+ Definition definition;
+ expand_pack(definition.push_back(std::make_unique<Signature<Evals>>(evalFunctions, false)));
+ const auto& t0 = definition.at(0)->result;
+ for (const auto& signature : definition) {
+ // TODO replace with real ==
+ assert(toString(t0) == toString(signature->result));
+ }
+ return std::pair<std::string, Definition>(name, std::move(definition));
+}
+
+template <typename ...Entries>
+std::unordered_map<std::string, CompoundExpression::Definition> initializeDefinitions(Entries... entries) {
+ std::unordered_map<std::string, CompoundExpression::Definition> definitions;
+ expand_pack(definitions.insert(std::move(entries)));
+ return definitions;
+}
+
+static Definition defineGet() {
+ Definition definition;
+ definition.push_back(
+ std::make_unique<Signature<Result<Value> (const EvaluationParameters&, const std::string&)>>(
+ [](const EvaluationParameters& params, const std::string& key) -> Result<Value> {
+ const auto propertyValue = params.feature.getValue(key);
+ if (!propertyValue) {
+ return EvaluationError {
+ "Property '" + key + "' not found in feature.properties"
+ };
+ }
+ return convertValue(*propertyValue);
+ },
+ false
+ )
+ );
+ definition.push_back(
+ std::make_unique<Signature<Result<Value> (const std::string&, const std::unordered_map<std::string, Value>&)>>(
+ [](const std::string& key, const std::unordered_map<std::string, Value>& object) -> Result<Value> {
+ if (object.find(key) == object.end()) {
+ return EvaluationError {
+ "Property '" + key + "' not found in object"
+ };
+ }
+ return object.at(key);
+ }
+ )
+ );
+ return definition;
+}
+
+template <typename T>
+Result<T> assertion(const Value& v) {
+ if (!v.is<T>()) {
+ return EvaluationError {
+ "Expected value to be of type " + toString(valueTypeToExpressionType<T>()) +
+ ", but found " + toString(typeOf(v)) + " instead."
+ };
+ }
+ return v.get<T>();
+}
+
+std::unordered_map<std::string, CompoundExpression::Definition> CompoundExpression::definitions = initializeDefinitions(
+ define("e", []() -> Result<float> { return 2.7f; }),
+ define("pi", []() -> Result<float> { return 3.141f; }),
+ define("ln2", []() -> Result<float> { return 0.693f; }),
+
+ define("typeof", [](const Value& v) -> Result<std::string> { return toString(typeOf(v)); }),
+ define("number", assertion<float>),
+ define("string", assertion<std::string>),
+ define("boolean", assertion<bool>),
+ define("array", assertion<std::vector<Value>>), // TODO: [array, type, value], [array, type, length, value]
+
+ std::pair<std::string, Definition>("get", defineGet()),
+
+ define("+", [](const Varargs<float>& args) -> Result<float> {
+ float sum = 0.0f;
+ for (auto arg : args) {
+ sum += arg;
+ }
+ return sum;
+ }),
+ define("-", [](float a, float b) -> Result<float> { return a - b; })
+);
+
+// if (*op == "string") return LambdaExpression::parse<Assertion<std::string>>(value, context);
+// if (*op == "number") return LambdaExpression::parse<Assertion<float>>(value, context);
+// if (*op == "boolean") return LambdaExpression::parse<Assertion<bool>>(value, context);
+// if (*op == "array") return Array::parse(value, context);
+// if (*op == "to_string") return LambdaExpression::parse<ToString>(value, context);
+// if (*op == "to_number") return LambdaExpression::parse<ToNumber>(value, context);
+// if (*op == "to_boolean") return LambdaExpression::parse<ToBoolean>(value, context);
+// if (*op == "to_rgba") return LambdaExpression::parse<ToRGBA>(value, context);
+// if (*op == "parse_color") return LambdaExpression::parse<ParseColor>(value, context);
+// if (*op == "rgba") return LambdaExpression::parse<RGBA>(value, context);
+// if (*op == "rgb") return LambdaExpression::parse<RGB>(value, context);
+// if (*op == "get") return LambdaExpression::parse<Get>(value, context);
+// if (*op == "has") return LambdaExpression::parse<Has>(value, context);
+// if (*op == "at") return LambdaExpression::parse<At>(value, context);
+// if (*op == "length") return LambdaExpression::parse<Length>(value, context);
+// if (*op == "properties") return LambdaExpression::parse<Properties>(value, context);
+// if (*op == "id") return LambdaExpression::parse<Id>(value, context);
+// if (*op == "geometry_type") return LambdaExpression::parse<GeometryType>(value, context);
+// if (*op == "+") return LambdaExpression::parse<Plus>(value, context);
+// if (*op == "-") return LambdaExpression::parse<Minus>(value, context);
+// if (*op == "*") return LambdaExpression::parse<Times>(value, context);
+// if (*op == "/") return LambdaExpression::parse<Divide>(value, context);
+// if (*op == "^") return LambdaExpression::parse<Power>(value, context);
+// if (*op == "%") return LambdaExpression::parse<Mod>(value, context);
+// if (*op == "coalesce") return LambdaExpression::parse<Coalesce>(value, context);
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/expression/definitions.cpp b/src/mbgl/style/expression/definitions.cpp
deleted file mode 100644
index 9fb82767e7..0000000000
--- a/src/mbgl/style/expression/definitions.cpp
+++ /dev/null
@@ -1,355 +0,0 @@
-#include <mbgl/style/expression/definitions.hpp>
-#include <mbgl/tile/geometry_tile_data.hpp>
-
-namespace mbgl {
-namespace style {
-namespace expression {
-
-
-Result<std::vector<Value>> evaluateArgs(const EvaluationParameters& params,
- const LambdaExpression::Args& args)
-{
- std::vector<Value> argValues;
- for(const auto& arg : args) {
- auto argValue = arg->evaluate(params);
- if (!argValue) return argValue.error();
- argValues.emplace_back(*argValue);
- }
- return argValues;
-}
-
-template<typename T, typename EvalFunc>
-EvaluationResult evaluateFromArgs(const EvaluationParameters& params,
- const std::unique_ptr<Expression>& a0,
- EvalFunc evaluate)
-{
- const auto& a0value = a0->evaluate<T>(params);
- if (!a0value) return a0value.error();
- return evaluate(*a0value);
-}
-
-// TODO: get this working to replace all the overloads below
-
-//template <typename ...Ts>
-//struct restargs { using expression = std::unique_ptr<Expression>; };
-//
-//template <typename T, typename ...Ts, typename Eval>
-//EvaluationResult evaluateFromArgs(const EvaluationParameters& params,
-// const std::unique_ptr<Expression>& a0,
-// const typename restargs<Ts...>::expression& args,
-// Eval evaluate)
-//{
-// const auto& a0value = a0->evaluate<T>(params);
-// if (a0value.template is<EvaluationError>()) {
-// return a0value.template get<EvaluationError>();
-// }
-// return evaluateFromArgs<Ts...>(
-// params,
-// std::forward<const typename restargs<Ts...>::expression&>(args),
-// [&] (Ts... restValues) {
-// return evaluate(a0value.template get<T>(), std::forward<Ts...>(restValues)...);
-// }
-// );
-//}
-
-template<typename T, typename U, typename EvalFunc>
-EvaluationResult evaluateFromArgs(const EvaluationParameters& params,
- const std::unique_ptr<Expression>& a0,
- const std::unique_ptr<Expression>& a1,
- EvalFunc evaluate)
-{
- const auto& a0value = a0->evaluate<T>(params);
- if (!a0value) return a0value.error();
- const auto& a1value = a1->evaluate<U>(params);
- if (!a1value) return a1value.error();
- return evaluate(*a0value, *a1value);
-}
-
-template<typename T0, typename T1, typename T2, typename EvalFunc>
-EvaluationResult evaluateFromArgs(const EvaluationParameters& params,
- const std::unique_ptr<Expression>& a0,
- const std::unique_ptr<Expression>& a1,
- const std::unique_ptr<Expression>& a2,
- EvalFunc evaluate)
-{
- const auto& a0value = a0->evaluate<T0>(params);
- if (!a0value) return a0value.error();
- const auto& a1value = a1->evaluate<T1>(params);
- if (!a1value) return a1value.error();
- const auto& a2value = a2->evaluate<T2>(params);
- if (!a2value) return a2value.error();
- return evaluate(*a0value, *a1value, *a2value);
-}
-
-template<typename T0, typename T1, typename T2, typename T3, typename EvalFunc>
-EvaluationResult evaluateFromArgs(const EvaluationParameters& params,
- const std::unique_ptr<Expression>& a0,
- const std::unique_ptr<Expression>& a1,
- const std::unique_ptr<Expression>& a2,
- const std::unique_ptr<Expression>& a3,
- EvalFunc evaluate)
-{
- const auto& a0value = a0->evaluate<T0>(params);
- if (!a0value) return a0value.error();
- const auto& a1value = a1->evaluate<T1>(params);
- if (!a1value) return a1value.error();
- const auto& a2value = a2->evaluate<T2>(params);
- if (!a2value) return a2value.error();
- const auto& a3value = a3->evaluate<T3>(params);
- if (!a3value) return a3value.error();
- return evaluate(*a0value, *a1value, *a2value, *a3value);
-}
-
-
-EvaluationResult TypeOf::evaluate(const EvaluationParameters& params) const {
- const auto& result = args[0]->evaluate(params);
- if (!result) return result.error();
- return toString(typeOf(*result));
-}
-
-EvaluationResult Array::evaluate(const EvaluationParameters& params) const {
- const auto& result = args[0]->evaluate(params);
- if (!result) { return result.error(); }
- const Value& v = *result;
- const auto& expected = getType().get<type::Array>();
- const auto& actual = typeOf(v);
- if (actual.is<type::Array>()) {
- const auto& arrayType = actual.get<type::Array>();
- bool match = (!expected.N || expected.N == arrayType.N);
- if (expected.itemType.is<type::ValueType>()) {
- match = match && (arrayType.itemType.is<type::StringType>() ||
- arrayType.itemType.is<type::NumberType>() ||
- arrayType.itemType.is<type::BooleanType>());
- } else {
- match = match && (toString(expected.itemType) == toString(arrayType.itemType));
- }
-
- if (match) return EvaluationResult(v);
- }
-
- return EvaluationError {
- "Expected value to be of type " + toString(getType()) +
- ", but found " + toString(actual) + " instead."
- };
-}
-
-EvaluationResult ToString::evaluate(const EvaluationParameters& params) const {
- const auto& result = args[0]->evaluate(params);
- if (!result) return result.error();
- return result->match(
- [&] (const std::string& s) -> EvaluationResult { return s; },
- [&] (float v) -> EvaluationResult { return stringify(v); },
- [&] (bool v) -> EvaluationResult { return stringify(v); },
- [&] (const NullValue& v) -> EvaluationResult { return stringify(v); },
- [&] (const auto& v) -> EvaluationResult {
- return EvaluationError {
- "Expected a primitive value in [\"string\", ...], but found " + toString(typeOf(v)) + " instead."
- };
- }
- );
-}
-
-EvaluationResult ToNumber::evaluate(const EvaluationParameters& params) const {
- const auto& result = args[0]->evaluate(params);
- if (!result) return result.error();
- if (result->is<float>()) { return result->get<float>(); }
- if (result->is<std::string>()) {
- const std::string& s = result->get<std::string>();
- try {
- return std::stof(s);
- } catch(std::exception) {
- }
- }
- return EvaluationError {
- "Could not convert " + stringify(*result) + " to number."
- };
-}
-
-EvaluationResult ToBoolean::evaluate(const EvaluationParameters& params) const {
- const auto& result = args[0]->evaluate(params);
- if (!result) return result.error();
- return result->match(
- [&] (float f) { return (bool)f; },
- [&] (const std::string& s) { return s.length() > 0; },
- [&] (bool b) { return b; },
- [&] (const NullValue&) { return false; },
- [&] (const auto&) { return true; }
- );
-}
-
-EvaluationResult ToRGBA::evaluate(const EvaluationParameters& params) const {
- return evaluateFromArgs<mbgl::Color>(params, args[0], [&] (const mbgl::Color& color) {
- return std::vector<Value> { color.r, color.g, color.b, color.a };
- });
-}
-
-EvaluationResult ParseColor::evaluate(const EvaluationParameters& params) const {
- return evaluateFromArgs<std::string>(params, args[0], [&] (const std::string& colorString) -> EvaluationResult {
- const auto& result = mbgl::Color::parse(colorString);
- if (result) return EvaluationResult(*result);
- return EvaluationError {
- "Could not parse color from value '" + colorString + "'"
- };
- });
-}
-
-EvaluationResult RGB::evaluate(const EvaluationParameters& params) const {
- return evaluateFromArgs<float, float, float>(params, args[0], args[1], args[2],
- [&] (float r, float g, float b) -> EvaluationResult {
- return mbgl::Color(r / 255.0f, g / 255.0f, b / 255.0f, 1.0f);
- }
- );
-}
-
-EvaluationResult RGBA::evaluate(const EvaluationParameters& params) const {
- return evaluateFromArgs<float, float, float, float>(params, args[0], args[1], args[2], args[3],
- [&] (float r, float g, float b, float a) -> EvaluationResult {
- return mbgl::Color(r / 255.0f, g / 255.0f, b / 255.0f, a);
- }
- );
-}
-
-bool Get::isFeatureConstant() const {
- return args.size() == 1 ? false : LambdaExpression::isFeatureConstant();
-}
-EvaluationResult Get::evaluate(const EvaluationParameters& params) const {
- if (args.size() == 1) {
- return evaluateFromArgs<std::string>(params, args[0], [&] (const std::string& key) -> EvaluationResult {
- const auto& value = params.feature.getValue(key);
- if (!value) return EvaluationError { "Property '" + key + "' not found in feature.properties" };
- return convertValue(*value);
- });
- } else {
- return evaluateFromArgs<std::string, std::unordered_map<std::string, Value>>(
- params,
- args[0],
- args[1],
- [&] (const std::string& key, const std::unordered_map<std::string, Value>& object) -> EvaluationResult {
- if (object.find(key) == object.end()) return EvaluationError { "Property '" + key + "' not found in object" };
- return object.at(key);
- }
- );
- }
-}
-
-bool Has::isFeatureConstant() const {
- return args.size() == 1 ? false : LambdaExpression::isFeatureConstant();
-}
-EvaluationResult Has::evaluate(const EvaluationParameters& params) const {
- if (args.size() == 1) {
- return evaluateFromArgs<std::string>(params, args[0], [&] (const std::string& key) -> EvaluationResult {
- const auto& value = params.feature.getValue(key);
- return value ? true : false;
- });
- } else {
- return evaluateFromArgs<std::string, std::unordered_map<std::string, Value>>(
- params,
- args[0],
- args[1],
- [&] (const std::string& key, const std::unordered_map<std::string, Value>& object) -> EvaluationResult {
- return object.find(key) != object.end();
- }
- );
- }
-}
-
-EvaluationResult At::evaluate(const EvaluationParameters& params) const {
- return evaluateFromArgs<float, std::vector<Value>>(
- params,
- args[0],
- args[1],
- [&] (float index, const std::vector<Value>& arr) -> EvaluationResult {
- const size_t i = index;
- if (index > arr.size()) {
- return EvaluationError {
- "Array index out of bounds: " + std::to_string(i) + " >= " + std::to_string(arr.size())
- };
- }
- return arr[i];
- }
- );
-}
-
-EvaluationResult Length::evaluate(const EvaluationParameters& params) const {
- const auto& result = args[0]->evaluate(params);
- if (!result) return result.error();
- return (float) result->match(
- [&] (const std::string& s) { return s.size(); },
- [&] (const std::vector<Value>& v) { return v.size(); },
- [&] (const auto&) { assert(false); return -1; }
- );
-}
-
-bool Properties::isFeatureConstant() const { return false; }
-EvaluationResult Properties::evaluate(const EvaluationParameters& params) const {
- return convertValue(params.feature.getProperties());
-}
-
-bool Id::isFeatureConstant() const { return false; }
-EvaluationResult Id::evaluate(const EvaluationParameters& params) const {
- const auto& id = params.feature.getID();
- if (!id) return EvaluationError { "Property 'id' not found in feature" };
- return id->match(
- [&](const std::string& s) -> EvaluationResult { return s; },
- [&](const auto& n) -> EvaluationResult { return *numericValue<float>(n); }
- );
-}
-
-bool GeometryType::isFeatureConstant() const { return false; }
-EvaluationResult GeometryType::evaluate(const EvaluationParameters& params) const {
- switch(params.feature.getType()) {
- case FeatureType::Unknown: return std::string("Unknown");
- case FeatureType::LineString: return std::string("LineString");
- case FeatureType::Point: return std::string("Point");
- case FeatureType::Polygon: return std::string("Polygon");
- }
-}
-
-EvaluationResult Plus::evaluate(const EvaluationParameters& params) const {
- const auto& evaluated = evaluateArgs(params, args);
- if (!evaluated) return evaluated.error();
- float sum = 0.0f;
- for (const Value& v : *evaluated) {
- sum += v.get<float>();
- }
- return sum;
-}
-
-EvaluationResult Times::evaluate(const EvaluationParameters& params) const {
- const auto& evaluated = evaluateArgs(params, args);
- if (!evaluated) return evaluated.error();
- float product = 1.0f;
- for (const Value& v : *evaluated) {
- product *= v.get<float>();
- }
- return product;
-}
-
-EvaluationResult Minus::evaluate(const EvaluationParameters& params) const {
- return evaluateFromArgs<float, float>(params, args[0], args[1], [&] (float a, float b) {
- return a - b;
- });
-}
-
-EvaluationResult Divide::evaluate(const EvaluationParameters& params) const {
- return evaluateFromArgs<float, float>(params, args[0], args[1], [&] (float a, float b) {
- return a / b;
- });
-}
-
-EvaluationResult Mod::evaluate(const EvaluationParameters& params) const {
- return evaluateFromArgs<float, float>(params, args[0], args[1], [&] (float a, float b) {
- return (float) fmod(a, b);
- });
-}
-
-EvaluationResult Power::evaluate(const EvaluationParameters& params) const {
- return evaluateFromArgs<float, float>(params, args[0], args[1], [&] (float a, float b) {
- return (float) pow(a, b);
- });
-}
-
-
-} // namespace expression
-} // namespace style
-} // namespace mbgl
diff --git a/src/mbgl/style/expression/type_check.cpp b/src/mbgl/style/expression/type_check.cpp
deleted file mode 100644
index 37bb3db823..0000000000
--- a/src/mbgl/style/expression/type_check.cpp
+++ /dev/null
@@ -1,121 +0,0 @@
-#include <mbgl/style/expression/type_check.hpp>
-
-namespace mbgl {
-namespace style {
-namespace expression {
-
-using namespace mbgl::style::expression::type;
-
-TypecheckResult typecheck(const Type& expected, const std::unique_ptr<Expression>& e) {
- if (LiteralExpression* literal = dynamic_cast<LiteralExpression*>(e.get())) {
- const auto& error = matchType(expected, literal->getType());
- if (error) return std::vector<CompileError> {{ *error, literal->getKey() }};
- return std::make_unique<LiteralExpression>(literal->getKey(), literal->getValue());
- } else if (LambdaExpression* lambda = dynamic_cast<LambdaExpression*>(e.get())) {
- // Check if the expected type matches the expression's output type;
- // If expression's output type is generic and the expected type is concrete,
- // pick up a typename binding
- std::unordered_map<std::string, Type> initialTypenames;
- const auto& resultMatchError = matchType(expected, lambda->getType(), initialTypenames, TypenameScope::actual);
- if (resultMatchError) return std::vector<CompileError> {{ *resultMatchError, lambda->getKey() }};
-
- std::vector<CompileError> errors;
- for (const auto& params : lambda->signatures) {
- std::unordered_map<std::string, Type> typenames(initialTypenames);
- errors.clear();
- // "Unroll" NArgs if present in the parameter list:
- // argCount = nargType.type.length * n + nonNargParameterCount
- // where n is the number of times the NArgs sequence must be
- // repeated.
- std::vector<Type> expandedParams;
- for (const auto& param : params) {
- param.match(
- [&](const NArgs& nargs) {
- size_t count = ceil(
- float(lambda->args.size() - (params.size() - 1)) /
- nargs.types.size()
- );
- if (nargs.N && *(nargs.N) < count) count = *(nargs.N);
- while(count-- > 0) {
- for(const auto& type : nargs.types) {
- expandedParams.emplace_back(type);
- }
- }
- },
- [&](const Type& t) {
- expandedParams.emplace_back(t);
- }
- );
- }
-
- if (expandedParams.size() != lambda->args.size()) {
- errors.emplace_back(CompileError {
- "Expected " + std::to_string(expandedParams.size()) +
- " arguments, but found " + std::to_string(lambda->args.size()) + " instead.",
- lambda->getKey()
- });
- continue;
- }
-
- // Iterate through arguments to:
- // - match parameter type vs argument type, checking argument's result type only (don't recursively typecheck subexpressions at this stage)
- // - collect typename mappings when ^ succeeds or type errors when it fails
- for (size_t i = 0; i < lambda->args.size(); i++) {
- const auto& param = expandedParams.at(i);
- const auto& arg = lambda->args.at(i);
-// if (arg instanceof Reference) {
-// arg = scope.get(arg.name);
-// }
-
- const auto& error = matchType(
- resolveTypenamesIfPossible(param, typenames),
- arg->getType(),
- typenames
- );
- if (error) {
- errors.emplace_back(CompileError{ *error, arg->getKey() });
- }
- }
-
- const auto& resultType = resolveTypenamesIfPossible(expected, typenames);
-
- if (isGeneric(resultType)) {
- errors.emplace_back(CompileError {
- "Could not resolve " + toString(lambda->getType()) + ". This expression must be wrapped in a type conversion, e.g. [\"string\", ${stringifyExpression(e)}].",
- lambda->getKey()
- });
- };
-
- // If we already have errors, return early so we don't get duplicates when
- // we typecheck against the resolved argument types
- if (errors.size()) continue;
-
- std::vector<Type> resolvedParams;
- std::vector<std::unique_ptr<Expression>> checkedArgs;
- for (std::size_t i = 0; i < expandedParams.size(); i++) {
- const auto& t = expandedParams.at(i);
- const auto& expectedArgType = resolveTypenamesIfPossible(t, typenames);
- auto checked = typecheck(expectedArgType, lambda->args.at(i));
- if (checked.is<std::vector<CompileError>>()) {
- const auto& errs = checked.get<std::vector<CompileError>>();
- errors.insert(errors.end(), errs.begin(), errs.end());
- } else if (checked.is<std::unique_ptr<Expression>>()) {
- resolvedParams.emplace_back(expectedArgType);
- checkedArgs.emplace_back(std::move(checked.get<std::unique_ptr<Expression>>()));
- }
- }
-
- if (errors.size() == 0) {
- return lambda->applyInferredType(resultType, std::move(checkedArgs));
- }
- }
-
- return errors;
- }
-
- assert(false);
-}
-
-} // namespace expression
-} // namespace style
-} // namespace mbgl
diff --git a/src/mbgl/style/function/expression.cpp b/src/mbgl/style/function/expression.cpp
index e6760eb345..76d4b3cd1f 100644
--- a/src/mbgl/style/function/expression.cpp
+++ b/src/mbgl/style/function/expression.cpp
@@ -9,26 +9,14 @@ class GeoJSONFeature : public GeometryTileFeature {
public:
const Feature& feature;
- GeoJSONFeature(const Feature& feature_)
- : feature(feature_) {
- }
+ GeoJSONFeature(const Feature& feature_) : feature(feature_) {}
FeatureType getType() const override {
return apply_visitor(ToFeatureType(), feature.geometry);
}
-
- PropertyMap getProperties() const override {
- return feature.properties;
- }
-
- optional<FeatureIdentifier> getID() const override {
- return feature.id;
- }
-
- GeometryCollection getGeometries() const override {
- return {};
- }
-
+ PropertyMap getProperties() const override { return feature.properties; }
+ optional<FeatureIdentifier> getID() const override { return feature.id; }
+ GeometryCollection getGeometries() const override { return {}; }
optional<mbgl::Value> getValue(const std::string& key) const override {
auto it = feature.properties.find(key);
if (it != feature.properties.end()) {
@@ -39,8 +27,7 @@ public:
};
-
-EvaluationResult Expression::evaluate(float z, const Feature& feature) const {
+EvaluationResult TypedExpression::evaluate(float z, const Feature& feature) const {
std::unique_ptr<const GeometryTileFeature> f = std::make_unique<const GeoJSONFeature>(feature);
return this->evaluate(EvaluationParameters {z, *f});
}