summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnand Thakker <github@anandthakker.net>2017-10-18 11:41:18 -0400
committerAnand Thakker <github@anandthakker.net>2017-10-25 11:53:47 -0400
commit39e0cb6eab8efe17be777db033b6300021fc4174 (patch)
treecbd7fcd52742195e835d77870872d18bb14eeb50
parent9568ccbbd59cb40fc006bf5d3f6937d5aeb49d49 (diff)
downloadqtlocation-mapboxgl-39e0cb6eab8efe17be777db033b6300021fc4174.tar.gz
Add special forms for assertions & coercions with fallbacks
-rw-r--r--cmake/core-files.cmake8
-rw-r--r--include/mbgl/style/expression/array_assertion.hpp2
-rw-r--r--include/mbgl/style/expression/assertion.hpp27
-rw-r--r--include/mbgl/style/expression/boolean_operator.hpp43
-rw-r--r--include/mbgl/style/expression/coercion.hpp30
-rw-r--r--include/mbgl/style/expression/expression.hpp1
-rw-r--r--include/mbgl/style/expression/type.hpp2
-rw-r--r--src/mbgl/style/expression/assertion.cpp66
-rw-r--r--src/mbgl/style/expression/boolean_operator.cpp0
-rw-r--r--src/mbgl/style/expression/coercion.cpp136
-rw-r--r--src/mbgl/style/expression/compound_expression.cpp115
-rw-r--r--src/mbgl/style/expression/parsing_context.cpp26
-rw-r--r--src/mbgl/style/expression/util.cpp39
-rw-r--r--src/mbgl/style/expression/util.hpp14
14 files changed, 396 insertions, 113 deletions
diff --git a/cmake/core-files.cmake b/cmake/core-files.cmake
index b0a4741873..18ac5831dc 100644
--- a/cmake/core-files.cmake
+++ b/cmake/core-files.cmake
@@ -395,10 +395,13 @@ set(MBGL_CORE_FILES
# style/expression
include/mbgl/style/expression/array_assertion.hpp
+ include/mbgl/style/expression/assertion.hpp
include/mbgl/style/expression/at.hpp
+ include/mbgl/style/expression/boolean_operator.hpp
include/mbgl/style/expression/case.hpp
include/mbgl/style/expression/check_subtype.hpp
include/mbgl/style/expression/coalesce.hpp
+ include/mbgl/style/expression/coercion.hpp
include/mbgl/style/expression/compound_expression.hpp
include/mbgl/style/expression/curve.hpp
include/mbgl/style/expression/expression.hpp
@@ -409,16 +412,21 @@ set(MBGL_CORE_FILES
include/mbgl/style/expression/type.hpp
include/mbgl/style/expression/value.hpp
src/mbgl/style/expression/array_assertion.cpp
+ src/mbgl/style/expression/assertion.cpp
src/mbgl/style/expression/at.cpp
+ src/mbgl/style/expression/boolean_operator.cpp
src/mbgl/style/expression/case.cpp
src/mbgl/style/expression/check_subtype.cpp
src/mbgl/style/expression/coalesce.cpp
+ src/mbgl/style/expression/coercion.cpp
src/mbgl/style/expression/compound_expression.cpp
src/mbgl/style/expression/curve.cpp
src/mbgl/style/expression/let.cpp
src/mbgl/style/expression/literal.cpp
src/mbgl/style/expression/match.cpp
src/mbgl/style/expression/parsing_context.cpp
+ src/mbgl/style/expression/util.cpp
+ src/mbgl/style/expression/util.hpp
src/mbgl/style/expression/value.cpp
# style/function
diff --git a/include/mbgl/style/expression/array_assertion.hpp b/include/mbgl/style/expression/array_assertion.hpp
index 6bf23f14da..038300341e 100644
--- a/include/mbgl/style/expression/array_assertion.hpp
+++ b/include/mbgl/style/expression/array_assertion.hpp
@@ -10,12 +10,10 @@
#include <mbgl/style/expression/parsing_context.hpp>
#include <mbgl/style/conversion.hpp>
-
namespace mbgl {
namespace style {
namespace expression {
-
class ArrayAssertion : public Expression {
public:
ArrayAssertion(type::Array type_, std::unique_ptr<Expression> input_) :
diff --git a/include/mbgl/style/expression/assertion.hpp b/include/mbgl/style/expression/assertion.hpp
new file mode 100644
index 0000000000..7df0d1f440
--- /dev/null
+++ b/include/mbgl/style/expression/assertion.hpp
@@ -0,0 +1,27 @@
+#pragma once
+#include <mbgl/style/expression/expression.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+class Assertion : public Expression {
+public:
+ Assertion(type::Type type_, std::vector<std::unique_ptr<Expression>> inputs_) :
+ Expression(type_),
+ inputs(std::move(inputs_))
+ {}
+
+ static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext ctx);
+
+ EvaluationResult evaluate(const EvaluationParameters& params) const override;
+ void accept(std::function<void(const Expression*)> visit) const override;
+
+private:
+ std::vector<std::unique_ptr<Expression>> inputs;
+};
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
+
diff --git a/include/mbgl/style/expression/boolean_operator.hpp b/include/mbgl/style/expression/boolean_operator.hpp
new file mode 100644
index 0000000000..ad24725422
--- /dev/null
+++ b/include/mbgl/style/expression/boolean_operator.hpp
@@ -0,0 +1,43 @@
+#pragma once
+#include <mbgl/style/expression/expression.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+class Any : public Expression {
+public:
+ Any(type::Array type_, std::vector<std::unique_ptr<Expression>> inputs_) :
+ Expression(type_),
+ inputs(std::move(inputs_))
+ {}
+
+ static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext ctx);
+
+ EvaluationResult evaluate(const EvaluationParameters& params) const override;
+ void accept(std::function<void(const Expression*)> visit) const override;
+
+private:
+ std::unique_ptr<Expression> inputs;
+};
+
+class All : public Expression {
+public:
+ All(type::Array type_, std::vector<std::unique_ptr<Expression>> inputs_) :
+ Expression(type_),
+ inputs(std::move(inputs_))
+ {}
+
+ static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext ctx);
+
+ EvaluationResult evaluate(const EvaluationParameters& params) const override;
+ void accept(std::function<void(const Expression*)> visit) const override;
+
+private:
+ std::unique_ptr<Expression> inputs;
+};
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
+
diff --git a/include/mbgl/style/expression/coercion.hpp b/include/mbgl/style/expression/coercion.hpp
new file mode 100644
index 0000000000..30c5167258
--- /dev/null
+++ b/include/mbgl/style/expression/coercion.hpp
@@ -0,0 +1,30 @@
+#pragma once
+#include <mbgl/style/expression/expression.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+/**
+ * Special form for error-coalescing coercion expressions "to-number",
+ * "to-color". Since these coercions can fail at runtime, they accept multiple
+ * arguments, only evaluating one at a time until one succeeds.
+ */
+class Coercion : public Expression {
+public:
+ Coercion(type::Type type_, std::vector<std::unique_ptr<Expression>> inputs_);
+
+ static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext ctx);
+
+ EvaluationResult evaluate(const EvaluationParameters& params) const override;
+ void accept(std::function<void(const Expression*)> visit) const override;
+
+private:
+ EvaluationResult (*coerceSingleValue) (const Value& v);
+ std::vector<std::unique_ptr<Expression>> inputs;
+};
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
+
diff --git a/include/mbgl/style/expression/expression.hpp b/include/mbgl/style/expression/expression.hpp
index 9441c0e818..b57af0906b 100644
--- a/include/mbgl/style/expression/expression.hpp
+++ b/include/mbgl/style/expression/expression.hpp
@@ -30,6 +30,7 @@ struct EvaluationParameters {
optional<float> zoom;
GeometryTileFeature const * feature;
+ optional<double> heatmapDensity;
};
template<typename T>
diff --git a/include/mbgl/style/expression/type.hpp b/include/mbgl/style/expression/type.hpp
index a730b884a3..6f011676a1 100644
--- a/include/mbgl/style/expression/type.hpp
+++ b/include/mbgl/style/expression/type.hpp
@@ -85,7 +85,7 @@ using Type = variant<
ErrorType>;
struct Array {
- Array(Type itemType_) : itemType(std::move(itemType_)) {}
+ explicit Array(Type itemType_) : itemType(std::move(itemType_)) {}
Array(Type itemType_, std::size_t N_) : itemType(std::move(itemType_)), N(N_) {}
Array(Type itemType_, optional<std::size_t> N_) : itemType(std::move(itemType_)), N(std::move(N_)) {}
std::string getName() const {
diff --git a/src/mbgl/style/expression/assertion.cpp b/src/mbgl/style/expression/assertion.cpp
new file mode 100644
index 0000000000..acef342e6f
--- /dev/null
+++ b/src/mbgl/style/expression/assertion.cpp
@@ -0,0 +1,66 @@
+#include <mbgl/style/expression/assertion.hpp>
+#include <mbgl/style/expression/check_subtype.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+ParseResult Assertion::parse(const mbgl::style::conversion::Convertible& value, ParsingContext ctx) {
+ using namespace mbgl::style::conversion;
+ static std::unordered_map<std::string, type::Type> types {
+ {"string", type::String},
+ {"number", type::Number},
+ {"boolean", type::Boolean},
+ {"object", type::Object}
+ };
+
+ std::size_t length = arrayLength(value);
+
+ if (length < 2) {
+ ctx.error("Expected at least one argument.");
+ return ParseResult();
+ }
+
+ auto it = types.find(*toString(arrayMember(value, 0)));
+ assert(it != types.end());
+
+ std::vector<std::unique_ptr<Expression>> parsed;
+ for (std::size_t i = 1; i < length; i++) {
+ ParseResult input = ctx.concat(i, {type::Value}).parse(arrayMember(value, i));
+ if (!input) return ParseResult();
+ parsed.push_back(std::move(*input));
+ }
+
+ return ParseResult(std::make_unique<Assertion>(it->second, std::move(parsed)));
+}
+
+EvaluationResult Assertion::evaluate(const EvaluationParameters& params) const {
+ for (std::size_t i = 0; i < inputs.size(); i++) {
+ EvaluationResult value = inputs[i]->evaluate(params);
+ if (!value) return value;
+ if (!type::checkSubtype(getType(), typeOf(*value))) {
+ return value;
+ } else if (i == inputs.size() - 1) {
+ return EvaluationError {
+ "Expected value to be of type " + toString(getType()) +
+ ", but found " + toString(typeOf(*value)) + " instead."
+ };
+ }
+ }
+
+ assert(false);
+ return EvaluationResult();
+};
+
+void Assertion::accept(std::function<void(const Expression*)> visit) const {
+ visit(this);
+ for(const std::unique_ptr<Expression>& input : inputs) {
+ input->accept(visit);
+ }
+};
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
+
+
diff --git a/src/mbgl/style/expression/boolean_operator.cpp b/src/mbgl/style/expression/boolean_operator.cpp
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/src/mbgl/style/expression/boolean_operator.cpp
diff --git a/src/mbgl/style/expression/coercion.cpp b/src/mbgl/style/expression/coercion.cpp
new file mode 100644
index 0000000000..3a0ef7fc1f
--- /dev/null
+++ b/src/mbgl/style/expression/coercion.cpp
@@ -0,0 +1,136 @@
+#include <mbgl/style/expression/coercion.hpp>
+#include <mbgl/style/expression/check_subtype.hpp>
+#include <mbgl/style/expression/util.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+EvaluationResult toNumber(const Value& v) {
+ optional<double> result = v.match(
+ [](const double f) -> optional<double> { return f; },
+ [](const std::string& s) -> optional<double> {
+ try {
+ return std::stof(s);
+ } catch(std::exception) {
+ return optional<double>();
+ }
+ },
+ [](const auto&) { return optional<double>(); }
+ );
+ if (!result) {
+ return EvaluationError {
+ "Could not convert " + stringify(v) + " to number."
+ };
+ }
+ return *result;
+}
+
+EvaluationResult toColor(const Value& colorValue) {
+ return colorValue.match(
+ [&](const std::string& colorString) -> EvaluationResult {
+ const optional<mbgl::Color> result = mbgl::Color::parse(colorString);
+ if (result) {
+ return *result;
+ } else {
+ return EvaluationError{
+ "Could not parse color from value '" + colorString + "'"
+ };
+ }
+ },
+ [&](const std::vector<Value>& components) -> EvaluationResult {
+ std::size_t len = components.size();
+ bool isNumeric = std::all_of(components.begin(), components.end(), [](const Value& item) {
+ return item.template is<double>();
+ });
+ if ((len == 3 || len == 4) && isNumeric) {
+ Result<mbgl::Color> c = {rgba(
+ components[0].template get<double>(),
+ components[1].template get<double>(),
+ components[2].template get<double>(),
+ len == 4 ? components[3].template get<double>() : 1.0
+ )};
+ if (!c) return c.error();
+ return *c;
+ } else {
+ return EvaluationError{
+ "Invalid rbga value " + stringify(colorValue) + ": expected an array containing either three or four numeric values."
+ };
+ }
+ },
+ [&](const auto&) -> EvaluationResult {
+ return EvaluationError{
+ "Could not parse color from value '" + stringify(colorValue) + "'"
+ };
+ }
+ );
+}
+
+Coercion::Coercion(type::Type type_, std::vector<std::unique_ptr<Expression>> inputs_) :
+ Expression(std::move(type_)),
+ inputs(std::move(inputs_))
+{
+ type::Type t = getType();
+ if (t.is<type::NumberType>()) {
+ coerceSingleValue = toNumber;
+ } else if (t.is<type::ColorType>()) {
+ coerceSingleValue = toColor;
+ } else {
+ assert(false);
+ }
+}
+
+ParseResult Coercion::parse(const mbgl::style::conversion::Convertible& value, ParsingContext ctx) {
+ using namespace mbgl::style::conversion;
+ static std::unordered_map<std::string, type::Type> types {
+ {"to-number", type::Number},
+ {"to-color", type::Color}
+ };
+
+ std::size_t length = arrayLength(value);
+
+ if (length < 2) {
+ ctx.error("Expected at least one argument.");
+ return ParseResult();
+ }
+
+ auto it = types.find(*toString(arrayMember(value, 0)));
+ assert(it != types.end());
+
+ std::vector<std::unique_ptr<Expression>> parsed;
+ for (std::size_t i = 1; i < length; i++) {
+ ParseResult input = ctx.concat(i, {type::Value}).parse(arrayMember(value, i));
+ if (!input) return ParseResult();
+ parsed.push_back(std::move(*input));
+ }
+
+ return ParseResult(std::make_unique<Coercion>(it->second, std::move(parsed)));
+}
+
+EvaluationResult Coercion::evaluate(const EvaluationParameters& params) const {
+ for (std::size_t i = 0; i < inputs.size(); i++) {
+ EvaluationResult value = inputs[i]->evaluate(params);
+ if (!value) return value;
+ EvaluationResult coerced = coerceSingleValue(*value);
+ if (coerced || i == inputs.size() - 1) {
+ return coerced;
+ }
+ }
+
+ assert(false);
+ return EvaluationResult();
+};
+
+void Coercion::accept(std::function<void(const Expression*)> visit) const {
+ visit(this);
+ for(const std::unique_ptr<Expression>& input : inputs) {
+ input->accept(visit);
+ }
+};
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
+
+
+
diff --git a/src/mbgl/style/expression/compound_expression.cpp b/src/mbgl/style/expression/compound_expression.cpp
index d6f0e4c553..0673f07736 100644
--- a/src/mbgl/style/expression/compound_expression.cpp
+++ b/src/mbgl/style/expression/compound_expression.cpp
@@ -1,4 +1,5 @@
#include <mbgl/style/expression/compound_expression.hpp>
+#include <mbgl/style/expression/util.hpp>
#include <mbgl/tile/geometry_tile_data.hpp>
#include <mbgl/util/ignore.hpp>
@@ -6,7 +7,6 @@ namespace mbgl {
namespace style {
namespace expression {
-
namespace detail {
/*
@@ -181,32 +181,6 @@ Result<T> assertion(const Value& v) {
return v.get<T>();
}
-std::string stringifyColor(double r, double g, double b, double a) {
- return stringify(r) + ", " +
- stringify(g) + ", " +
- stringify(b) + ", " +
- stringify(a);
-}
-Result<mbgl::Color> rgba(double r, double g, double b, double a) {
- if (
- r < 0 || r > 255 ||
- g < 0 || g > 255 ||
- b < 0 || b > 255
- ) {
- return EvaluationError {
- "Invalid rgba value [" + stringifyColor(r, g, b, a) +
- "]: 'r', 'g', and 'b' must be between 0 and 255."
- };
- }
- if (a < 0 || a > 1) {
- return EvaluationError {
- "Invalid rgba value [" + stringifyColor(r, g, b, a) +
- "]: 'a' must be between 0 and 1."
- };
- }
- return mbgl::Color(r / 255, g / 255, b / 255, a);
-}
-
template <typename T>
Result<bool> equal(const T& lhs, const T& rhs) { return lhs == rhs; }
@@ -236,38 +210,11 @@ std::unordered_map<std::string, CompoundExpressionRegistry::Definition> initiali
define("to-string", [](const Value& value) -> Result<std::string> {
return value.match(
- [](const std::unordered_map<std::string, Value>&) -> Result<std::string> {
- return EvaluationError {
- R"(Expected a primitive value in ["string", ...], but found Object instead.)"
- };
- },
- [](const std::vector<Value>& v) -> Result<std::string> {
- return EvaluationError {
- R"(Expected a primitive value in ["string", ...], but found )" + toString(typeOf(v)) + " instead."
- };
- },
+ [](const mbgl::Color& c) -> Result<std::string> { return c.stringify(); }, // avoid quoting
[](const auto& v) -> Result<std::string> { return stringify(v); }
);
});
- define("to-number", [](const Value& v) -> Result<double> {
- optional<double> result = v.match(
- [](const double f) -> optional<double> { return f; },
- [](const std::string& s) -> optional<double> {
- try {
- return std::stof(s);
- } catch(std::exception) {
- return optional<double>();
- }
- },
- [](const auto&) { return optional<double>(); }
- );
- if (!result) {
- return EvaluationError {
- "Could not convert " + stringify(v) + " to number."
- };
- }
- return *result;
- });
+
define("to-boolean", [](const Value& v) -> Result<bool> {
return v.match(
[&] (double f) { return (bool)f; },
@@ -281,45 +228,6 @@ std::unordered_map<std::string, CompoundExpressionRegistry::Definition> initiali
return std::array<double, 4> {{ color.r, color.g, color.b, color.a }};
});
- define("to-color", [](const Value& colorValue) -> Result<mbgl::Color> {
- return colorValue.match(
- [&](const std::string& colorString) -> Result<mbgl::Color> {
- const optional<mbgl::Color> result = mbgl::Color::parse(colorString);
- if (result) {
- return *result;
- } else {
- return EvaluationError{
- "Could not parse color from value '" + colorString + "'"
- };
- }
- },
- [&](const std::vector<Value>& components) -> Result<mbgl::Color> {
- std::size_t len = components.size();
- bool isNumeric = std::all_of(components.begin(), components.end(), [](const Value& item) {
- return item.template is<double>();
- });
- if ((len == 3 || len == 4) && isNumeric) {
- return {rgba(
- components[0].template get<double>(),
- components[1].template get<double>(),
- components[2].template get<double>(),
- len == 4 ? components[3].template get<double>() : 1.0
- )};
- } else {
- return EvaluationError{
- "Could not parse color from value '" + stringify(colorValue) + "'"
- };
- }
- },
- [&](const auto&) -> Result<mbgl::Color> {
- return EvaluationError{
- "Could not parse color from value '" + stringify(colorValue) + "'"
- };
- }
- );
-
- });
-
define("rgba", rgba);
define("rgb", [](double r, double g, double b) { return rgba(r, g, b, 1.0f); });
@@ -332,6 +240,15 @@ std::unordered_map<std::string, CompoundExpressionRegistry::Definition> initiali
return *(params.zoom);
});
+ define("heatmap-density", [](const EvaluationParameters& params) -> Result<double> {
+ if (!params.heatmapDensity) {
+ return EvaluationError {
+ "The 'heatmap-density' expression is unavailable in the current evaluation context."
+ };
+ }
+ return *(params.heatmapDensity);
+ });
+
define("has", [](const EvaluationParameters& params, const std::string& key) -> Result<bool> {
if (!params.feature) {
return EvaluationError {
@@ -472,10 +389,10 @@ std::unordered_map<std::string, CompoundExpressionRegistry::Definition> initiali
define("==", equal<bool>);
define("==", equal<NullValue>);
- define("==", notEqual<double>);
- define("==", notEqual<const std::string&>);
- define("==", notEqual<bool>);
- define("==", notEqual<NullValue>);
+ define("!=", notEqual<double>);
+ define("!=", notEqual<const std::string&>);
+ define("!=", notEqual<bool>);
+ define("!=", notEqual<NullValue>);
define(">", [](double lhs, double rhs) -> Result<bool> { return lhs > rhs; });
define(">", [](const std::string& lhs, const std::string& rhs) -> Result<bool> { return lhs > rhs; });
diff --git a/src/mbgl/style/expression/parsing_context.cpp b/src/mbgl/style/expression/parsing_context.cpp
index 4e7a9dedad..2c00bbfb22 100644
--- a/src/mbgl/style/expression/parsing_context.cpp
+++ b/src/mbgl/style/expression/parsing_context.cpp
@@ -2,8 +2,10 @@
#include <mbgl/style/expression/parsing_context.hpp>
#include <mbgl/style/expression/at.hpp>
#include <mbgl/style/expression/array_assertion.hpp>
+#include <mbgl/style/expression/assertion.hpp>
#include <mbgl/style/expression/case.hpp>
#include <mbgl/style/expression/coalesce.hpp>
+#include <mbgl/style/expression/coercion.hpp>
#include <mbgl/style/expression/compound_expression.hpp>
#include <mbgl/style/expression/curve.hpp>
#include <mbgl/style/expression/let.hpp>
@@ -65,6 +67,10 @@ ParseResult ParsingContext::parse(const mbgl::style::conversion::Convertible& va
parsed = Var::parse(value, *this);
} else if (*op == "at") {
parsed = At::parse(value, *this);
+ } else if (*op == "string" || *op == "number" || *op == "boolean" || *op == "object") {
+ parsed = Assertion::parse(value, *this);
+ } else if (*op == "to-color" || *op == "to-number") {
+ parsed = Coercion::parse(value, *this);
} else {
parsed = parseCompoundExpression(*op, value, *this);
}
@@ -80,23 +86,21 @@ ParseResult ParsingContext::parse(const mbgl::style::conversion::Convertible& va
if (!parsed) {
assert(errors.size() > 0);
} else if (expected) {
- auto wrapForType = [&](const std::string& wrapper, std::unique_ptr<Expression> expression) {
+ auto wrapForType = [&](const type::Type& target, std::unique_ptr<Expression> expression) -> std::unique_ptr<Expression> {
std::vector<std::unique_ptr<Expression>> args;
args.push_back(std::move(expression));
- return createCompoundExpression(wrapper, std::move(args), *this);
+ if (target == type::Color) {
+ return std::make_unique<Coercion>(target, std::move(args));
+ } else {
+ return std::make_unique<Assertion>(target, std::move(args));
+ }
};
const type::Type actual = (*parsed)->getType();
if (*expected == type::Color && (actual == type::String || actual == type::Value)) {
- parsed = wrapForType("to-color", std::move(*parsed));
- } else if (*expected != type::Value && actual == type::Value) {
- if (*expected == type::String) {
- parsed = wrapForType("string", std::move(*parsed));
- } else if (*expected == type::Number) {
- parsed = wrapForType("number", std::move(*parsed));
- } else if (*expected == type::Boolean) {
- parsed = wrapForType("boolean", std::move(*parsed));
- }
+ parsed = wrapForType(type::Color, std::move(*parsed));
+ } else if ((*expected == type::String || *expected == type::Number || *expected == type::Boolean) && actual == type::Value) {
+ parsed = wrapForType(*expected, std::move(*parsed));
}
checkType((*parsed)->getType());
diff --git a/src/mbgl/style/expression/util.cpp b/src/mbgl/style/expression/util.cpp
new file mode 100644
index 0000000000..09c6c3c23d
--- /dev/null
+++ b/src/mbgl/style/expression/util.cpp
@@ -0,0 +1,39 @@
+
+#include "util.hpp"
+#include <mbgl/style/expression/value.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+std::string stringifyColor(double r, double g, double b, double a) {
+ return stringify(r) + ", " +
+ stringify(g) + ", " +
+ stringify(b) + ", " +
+ stringify(a);
+}
+
+Result<mbgl::Color> rgba(double r, double g, double b, double a) {
+ if (
+ r < 0 || r > 255 ||
+ g < 0 || g > 255 ||
+ b < 0 || b > 255
+ ) {
+ return EvaluationError {
+ "Invalid rgba value [" + stringifyColor(r, g, b, a) +
+ "]: 'r', 'g', and 'b' must be between 0 and 255."
+ };
+ }
+ if (a < 0 || a > 1) {
+ return EvaluationError {
+ "Invalid rgba value [" + stringifyColor(r, g, b, a) +
+ "]: 'a' must be between 0 and 1."
+ };
+ }
+ return mbgl::Color(r / 255, g / 255, b / 255, a);
+}
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
+
diff --git a/src/mbgl/style/expression/util.hpp b/src/mbgl/style/expression/util.hpp
new file mode 100644
index 0000000000..192e11da4e
--- /dev/null
+++ b/src/mbgl/style/expression/util.hpp
@@ -0,0 +1,14 @@
+#pragma once
+
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/util/color.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+Result<mbgl::Color> rgba(double r, double g, double b, double a);
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl