diff options
author | Anand Thakker <github@anandthakker.net> | 2017-07-07 17:06:28 -0400 |
---|---|---|
committer | Anand Thakker <github@anandthakker.net> | 2017-07-07 17:17:44 -0400 |
commit | a6d0cf83b1cba21e4b7b8420a69f6c2643bb7899 (patch) | |
tree | 23ffc5c07e80356a7190d8a77c87ad4c18b44236 | |
parent | d1b85505cde735cf40b37c994423380d6c0b02ac (diff) | |
download | qtlocation-mapboxgl-a6d0cf83b1cba21e4b7b8420a69f6c2643bb7899.tar.gz |
Add initial impl of +, -, *, /
-rw-r--r-- | include/mbgl/style/expression/expression.hpp | 150 | ||||
-rw-r--r-- | include/mbgl/style/expression/parse.hpp | 20 | ||||
-rw-r--r-- | include/mbgl/style/function/type.hpp | 1 | ||||
-rw-r--r-- | platform/node/src/node_expression.cpp | 2 |
4 files changed, 165 insertions, 8 deletions
diff --git a/include/mbgl/style/expression/expression.hpp b/include/mbgl/style/expression/expression.hpp index d7053fc1b8..20fe5be8f8 100644 --- a/include/mbgl/style/expression/expression.hpp +++ b/include/mbgl/style/expression/expression.hpp @@ -44,10 +44,19 @@ public: // Exposed for use with pure Feature objects (e.g. beyond the context of tiled map data). optional<OutputValue> evaluate(float, const Feature&, EvaluationError&) const; - type::Type getType() { + type::Type getType() const { return type; } + type::ValueType getResultType() const { + return type.match( + [&] (const type::Lambda& lambdaType) { + return lambdaType.getResult(); + }, + [&] (const auto& t) -> type::ValueType { return t; } + ); + } + bool isFeatureConstant() { return true; } @@ -110,20 +119,149 @@ private: class LambdaExpression : public Expression { public: + using Args = std::vector<std::unique_ptr<Expression>>; + LambdaExpression(std::string key, - type::Lambda type, std::string name_, - std::vector<std::unique_ptr<Expression>> args_) : + Args args_, + type::Lambda type) : Expression(key, type), - name(name_), - args(std::move(args_)) + args(std::move(args_)), + name(name_) {} + template <class V> + static variant<CompileError, Args> parseArgs(const V& value, const ParsingContext& ctx) { + assert(isArray(value)); + auto length = arrayLength(value); + 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::move(args); + } + +protected: + Args args; private: std::string name; - std::vector<std::unique_ptr<Expression>> args; }; +template <typename T, typename Rfunc> +optional<OutputValue> evaluateBinaryOperator(float z, + const GeometryTileFeature& f, + EvaluationError& e, + const LambdaExpression::Args& args, + optional<T> initial, + Rfunc reduce) +{ + optional<T> memo = initial; + for(const auto& arg : args) { + auto argValue = arg->evaluate(z, f, e); + if (!argValue) return {}; + if (!memo) memo = {argValue->get<T>()}; + else memo = reduce(*memo, argValue->get<T>()); + } + return {*memo}; +} + +class PlusExpression : public LambdaExpression { +public: + PlusExpression(std::string key, Args args) : + LambdaExpression(key, "+", std::move(args), + {type::Primitive::Number, {type::NArgs({type::Primitive::Number})}}) + {} + + optional<OutputValue> evaluate(float zoom, const GeometryTileFeature& feature, EvaluationError& error) const override { + return evaluateBinaryOperator<float>(zoom, feature, error, args, + {}, [](float memo, float next) { return memo + next; }); + } + + template <class V> + static ParseResult parse(const V& value, const ParsingContext& ctx) { + auto args = LambdaExpression::parseArgs(value, ctx); + if (args.template is<LambdaExpression::Args>()) { + return std::make_unique<PlusExpression>(ctx.key(), std::move(args.template get<Args>())); + } else { + return args.template get<CompileError>(); + } + } +}; + +class TimesExpression : public LambdaExpression { +public: + TimesExpression(std::string key, Args args) : + LambdaExpression(key, "*", std::move(args), + {type::Primitive::Number, {type::NArgs({type::Primitive::Number})}}) + {} + + optional<OutputValue> evaluate(float zoom, const GeometryTileFeature& feature, EvaluationError& error) const override { + return evaluateBinaryOperator<float>(zoom, feature, error, args, + {}, [](float memo, float next) { return memo * next; }); + } + + template <class V> + static ParseResult parse(const V& value, const ParsingContext& ctx) { + auto args = LambdaExpression::parseArgs(value, ctx); + if (args.template is<LambdaExpression::Args>()) { + return std::make_unique<TimesExpression>(ctx.key(), std::move(args.template get<Args>())); + } else { + return args.template get<CompileError>(); + } + } +}; + +class MinusExpression : public LambdaExpression { +public: + MinusExpression(std::string key, Args args) : + LambdaExpression(key, "-", std::move(args), + {type::Primitive::Number, {type::Primitive::Number, type::Primitive::Number}}) + {} + + optional<OutputValue> evaluate(float zoom, const GeometryTileFeature& feature, EvaluationError& error) const override { + return evaluateBinaryOperator<float>(zoom, feature, error, args, + {}, [](float memo, float next) { return memo - next; }); + } + + template <class V> + static ParseResult parse(const V& value, const ParsingContext& ctx) { + auto args = LambdaExpression::parseArgs(value, ctx); + if (args.template is<LambdaExpression::Args>()) { + return std::make_unique<MinusExpression>(ctx.key(), std::move(args.template get<Args>())); + } else { + return args.template get<CompileError>(); + } + } +}; + +class DivideExpression : public LambdaExpression { +public: + DivideExpression(std::string key, Args args) : + LambdaExpression(key, "/", std::move(args), + {type::Primitive::Number, {type::Primitive::Number, type::Primitive::Number}}) + {} + + optional<OutputValue> evaluate(float zoom, const GeometryTileFeature& feature, EvaluationError& error) const override { + return evaluateBinaryOperator<float>(zoom, feature, error, args, + {}, [](float memo, float next) { return memo / next; }); + } + + template <class V> + static ParseResult parse(const V& value, const ParsingContext& ctx) { + auto args = LambdaExpression::parseArgs(value, ctx); + if (args.template is<LambdaExpression::Args>()) { + return std::make_unique<DivideExpression>(ctx.key(), std::move(args.template get<Args>())); + } else { + return args.template get<CompileError>(); + } + } +}; diff --git a/include/mbgl/style/expression/parse.hpp b/include/mbgl/style/expression/parse.hpp index 3d7fad70b3..4776706749 100644 --- a/include/mbgl/style/expression/parse.hpp +++ b/include/mbgl/style/expression/parse.hpp @@ -54,8 +54,26 @@ ParseResult parseExpression(const V& value, const ParsingContext& context) }; return error; } - } + + if (*op == "+") return PlusExpression::parse(value, context); + if (*op == "-") return MinusExpression::parse(value, context); + if (*op == "*") return TimesExpression::parse(value, context); + if (*op == "/") return DivideExpression::parse(value, context); + + return CompileError { + std::string("Unknown expression \"") + *op + "\". If you wanted a literal array, use [\"literal\", [...]].", + context.key(0) + }; + } + + if (isObject(value)) { + return CompileError { + "Bare objects invalid. Use [\"literal\", {...}] instead.", + context.key() + }; + } + return LiteralExpression::parse(value, context); } diff --git a/include/mbgl/style/function/type.hpp b/include/mbgl/style/function/type.hpp index 461c95eb0d..94f710c005 100644 --- a/include/mbgl/style/function/type.hpp +++ b/include/mbgl/style/function/type.hpp @@ -104,6 +104,7 @@ public: std::string getName() const { return "lambda"; } + ValueType getResult() const { return result; } private: ValueType result; std::vector<ValueType> params; diff --git a/platform/node/src/node_expression.cpp b/platform/node/src/node_expression.cpp index dc9a50abd4..c6c2a04bfb 100644 --- a/platform/node/src/node_expression.cpp +++ b/platform/node/src/node_expression.cpp @@ -131,7 +131,7 @@ void NodeExpression::GetType(const Nan::FunctionCallbackInfo<v8::Value>& info) { NodeExpression* nodeExpr = ObjectWrap::Unwrap<NodeExpression>(info.Holder()); const auto& expression = nodeExpr->expression; - const auto& type = expression->getType(); + const auto& type = expression->getResultType(); const auto& name = type.match([&] (const auto& t) { return t.getName(); }); info.GetReturnValue().Set(Nan::New(name.c_str()).ToLocalChecked()); } |