1 #include <mbgl/style/expression/coalesce.hpp>
2 #include <mbgl/style/expression/check_subtype.hpp>
3
4 namespace mbgl {
5 namespace style {
6 namespace expression {
7
evaluate(const EvaluationContext & params) const8 EvaluationResult Coalesce::evaluate(const EvaluationContext& params) const {
9 EvaluationResult result = Null;
10 for (const auto& arg : args) {
11 result = arg->evaluate(params);
12 if (!result || *result != Null) break;
13 }
14 return result;
15 }
16
eachChild(const std::function<void (const Expression &)> & visit) const17 void Coalesce::eachChild(const std::function<void(const Expression&)>& visit) const {
18 for (const std::unique_ptr<Expression>& arg : args) {
19 visit(*arg);
20 }
21 }
22
operator ==(const Expression & e) const23 bool Coalesce::operator==(const Expression& e) const {
24 if (e.getKind() == Kind::Coalesce) {
25 auto rhs = static_cast<const Coalesce*>(&e);
26 return Expression::childrenEqual(args, rhs->args);
27 }
28 return false;
29 }
30
possibleOutputs() const31 std::vector<optional<Value>> Coalesce::possibleOutputs() const {
32 std::vector<optional<Value>> result;
33 for (const auto& arg : args) {
34 for (auto& output : arg->possibleOutputs()) {
35 result.push_back(std::move(output));
36 }
37 }
38 return result;
39 }
40
41 using namespace mbgl::style::conversion;
parse(const Convertible & value,ParsingContext & ctx)42 ParseResult Coalesce::parse(const Convertible& value, ParsingContext& ctx) {
43 assert(isArray(value));
44 auto length = arrayLength(value);
45 if (length < 2) {
46 ctx.error("Expected at least one argument.");
47 return ParseResult();
48 }
49
50 optional<type::Type> outputType;
51 optional<type::Type> expectedType = ctx.getExpected();
52 if (expectedType && *expectedType != type::Value) {
53 outputType = expectedType;
54 }
55
56 Coalesce::Args args;
57 args.reserve(length - 1);
58 for (std::size_t i = 1; i < length; i++) {
59 auto parsed = ctx.parse(arrayMember(value, i), i, outputType, ParsingContext::omitTypeAnnotations);
60 if (!parsed) {
61 return parsed;
62 }
63 if (!outputType) {
64 outputType = (*parsed)->getType();
65 }
66 args.push_back(std::move(*parsed));
67 }
68 assert(outputType);
69
70 // Above, we parse arguments without inferred type annotation so that
71 // they don't produce a runtime error for `null` input, which would
72 // preempt the desired null-coalescing behavior.
73 // Thus, if any of our arguments would have needed an annotation, we
74 // need to wrap the enclosing coalesce expression with it instead.
75 bool needsAnnotation = expectedType &&
76 std::any_of(args.begin(), args.end(), [&] (const auto& arg) {
77 return type::checkSubtype(*expectedType, arg->getType());
78 });
79
80 return ParseResult(std::make_unique<Coalesce>(needsAnnotation ? type::Value : *outputType, std::move(args)));
81 }
82
83 } // namespace expression
84 } // namespace style
85 } // namespace mbgl
86