1 #include <mbgl/style/expression/collator.hpp>
2 #include <mbgl/style/expression/equals.hpp>
3 
4 namespace mbgl {
5 namespace style {
6 namespace expression {
7 
isComparableType(const type::Type & type)8 static bool isComparableType(const type::Type& type) {
9     return type == type::String ||
10         type == type::Number ||
11         type == type::Boolean ||
12         type == type::Null;
13 }
14 
Equals(std::unique_ptr<Expression> lhs_,std::unique_ptr<Expression> rhs_,optional<std::unique_ptr<Expression>> collator_,bool negate_)15 Equals::Equals(std::unique_ptr<Expression> lhs_, std::unique_ptr<Expression> rhs_, optional<std::unique_ptr<Expression>> collator_, bool negate_)
16     : Expression(Kind::Equals, type::Boolean),
17       lhs(std::move(lhs_)),
18       rhs(std::move(rhs_)),
19       collator(std::move(collator_)),
20       negate(negate_) {
21     assert(isComparableType(lhs->getType()) || isComparableType(rhs->getType()));
22     assert(lhs->getType() == rhs->getType() || lhs->getType() == type::Value || rhs->getType() == type::Value);
23 }
24 
evaluate(const EvaluationContext & params) const25 EvaluationResult Equals::evaluate(const EvaluationContext& params) const {
26     EvaluationResult lhsResult = lhs->evaluate(params);
27     if (!lhsResult) return lhsResult;
28 
29     EvaluationResult rhsResult = rhs->evaluate(params);
30     if (!rhsResult) return lhsResult;
31 
32     bool result;
33 
34     if (collator) {
35         auto collatorResult = (*collator)->evaluate(params);
36         const Collator& c = collatorResult->get<Collator>();
37         result = c.compare(lhsResult->get<std::string>(), rhsResult->get<std::string>()) == 0;
38     } else {
39        result = *lhsResult == *rhsResult;
40     }
41     if (negate) {
42         result = !result;
43     }
44     return result;
45 }
46 
eachChild(const std::function<void (const Expression &)> & visit) const47 void Equals::eachChild(const std::function<void(const Expression&)>& visit) const {
48     visit(*lhs);
49     visit(*rhs);
50     if (collator) {
51         visit(**collator);
52     }
53 }
54 
operator ==(const Expression & e) const55 bool Equals::operator==(const Expression& e) const {
56     if (e.getKind() == Kind::Equals) {
57         auto eq = static_cast<const Equals*>(&e);
58         return eq->negate == negate && *eq->lhs == *lhs && *eq->rhs == *rhs;
59     }
60     return false;
61 }
62 
possibleOutputs() const63 std::vector<optional<Value>> Equals::possibleOutputs() const {
64     return {{ true }, { false }};
65 }
66 
67 using namespace mbgl::style::conversion;
parse(const Convertible & value,ParsingContext & ctx)68 ParseResult Equals::parse(const Convertible& value, ParsingContext& ctx) {
69     std::size_t length = arrayLength(value);
70 
71     if (length != 3 && length != 4) {
72         ctx.error("Expected two or three arguments.");
73         return ParseResult();
74     }
75 
76     bool negate = toString(arrayMember(value, 0)) == std::string("!=");
77 
78     ParseResult lhs = ctx.parse(arrayMember(value, 1), 1, {type::Value});
79     if (!lhs) return ParseResult();
80 
81     ParseResult rhs = ctx.parse(arrayMember(value, 2), 2, {type::Value});
82     if (!rhs) return ParseResult();
83 
84     type::Type lhsType = (*lhs)->getType();
85     type::Type rhsType = (*rhs)->getType();
86 
87     if (!isComparableType(lhsType) && !isComparableType(rhsType)) {
88         ctx.error("Expected at least one argument to be a string, number, boolean, or null, but found (" +
89             toString(lhsType) + ", " + toString(rhsType) + ") instead.");
90         return ParseResult();
91     }
92 
93     if (lhsType != rhsType && lhsType != type::Value && rhsType != type::Value) {
94         ctx.error("Cannot compare " + toString(lhsType) + " and " + toString(rhsType) + ".");
95         return ParseResult();
96     }
97 
98     ParseResult collatorParseResult;
99     if (length == 4) {
100         if (lhsType != type::String && rhsType != type::String) {
101             ctx.error("Cannot use collator to compare non-string types.");
102             return ParseResult();
103         }
104         collatorParseResult = ctx.parse(arrayMember(value, 3), 3, {type::Collator});
105         if (!collatorParseResult) return ParseResult();
106     }
107 
108     return ParseResult(std::make_unique<Equals>(std::move(*lhs), std::move(*rhs), std::move(collatorParseResult), negate));
109 }
110 
111 } // namespace expression
112 } // namespace style
113 } // namespace mbgl
114