diff options
author | Anand Thakker <github@anandthakker.net> | 2017-07-17 12:29:14 -0400 |
---|---|---|
committer | Anand Thakker <github@anandthakker.net> | 2017-08-11 21:54:51 -0400 |
commit | 09cf58068498e81a9771243db53ab98ba1c9faa2 (patch) | |
tree | 2f70fa6ca13341893fcf4cce3fccbef0ef941609 | |
parent | fd2c1055a786c365b973a80b3f8cc36c9ba2fa8d (diff) | |
download | qtlocation-mapboxgl-09cf58068498e81a9771243db53ab98ba1c9faa2.tar.gz |
Refactor to infer CompoundExpression from type of evaluate()
-rw-r--r-- | cmake/core-files.cmake | 6 | ||||
-rw-r--r-- | include/mbgl/style/expression/compound_expression.hpp | 334 | ||||
-rw-r--r-- | include/mbgl/style/expression/definitions.hpp | 322 | ||||
-rw-r--r-- | include/mbgl/style/expression/expression.hpp | 146 | ||||
-rw-r--r-- | include/mbgl/style/expression/parse.hpp | 43 | ||||
-rw-r--r-- | include/mbgl/style/expression/type_check.hpp | 14 | ||||
-rw-r--r-- | platform/node/src/node_expression.cpp | 14 | ||||
-rw-r--r-- | platform/node/src/node_expression.hpp | 4 | ||||
-rw-r--r-- | src/mbgl/style/expression/compound_expression.cpp | 162 | ||||
-rw-r--r-- | src/mbgl/style/expression/definitions.cpp | 355 | ||||
-rw-r--r-- | src/mbgl/style/expression/type_check.cpp | 121 | ||||
-rw-r--r-- | src/mbgl/style/function/expression.cpp | 23 |
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}); } |