1 
2 #include <mbgl/style/expression/parsing_context.hpp>
3 #include <mbgl/style/expression/check_subtype.hpp>
4 #include <mbgl/style/expression/is_constant.hpp>
5 #include <mbgl/style/expression/type.hpp>
6 
7 #include <mbgl/style/expression/expression.hpp>
8 #include <mbgl/style/expression/at.hpp>
9 #include <mbgl/style/expression/array_assertion.hpp>
10 #include <mbgl/style/expression/assertion.hpp>
11 #include <mbgl/style/expression/boolean_operator.hpp>
12 #include <mbgl/style/expression/case.hpp>
13 #include <mbgl/style/expression/coalesce.hpp>
14 #include <mbgl/style/expression/coercion.hpp>
15 #include <mbgl/style/expression/compound_expression.hpp>
16 #include <mbgl/style/expression/equals.hpp>
17 #include <mbgl/style/expression/interpolate.hpp>
18 #include <mbgl/style/expression/length.hpp>
19 #include <mbgl/style/expression/let.hpp>
20 #include <mbgl/style/expression/literal.hpp>
21 #include <mbgl/style/expression/match.hpp>
22 #include <mbgl/style/expression/step.hpp>
23 
24 #include <mbgl/style/expression/find_zoom_curve.hpp>
25 
26 #include <mbgl/style/conversion/get_json_type.hpp>
27 
28 #include <mbgl/util/string.hpp>
29 
30 namespace mbgl {
31 namespace style {
32 namespace expression {
33 
isConstant(const Expression & expression)34 bool isConstant(const Expression& expression) {
35     if (expression.getKind() == Kind::Var) {
36         auto varExpression = static_cast<const Var*>(&expression);
37         return isConstant(*varExpression->getBoundExpression());
38     }
39 
40     if (expression.getKind() == Kind::CompoundExpression) {
41         auto compound = static_cast<const CompoundExpressionBase*>(&expression);
42         if (compound->getName() == "error") {
43             return false;
44         }
45     }
46 
47     bool isTypeAnnotation = expression.getKind() == Kind::Coercion ||
48         expression.getKind() == Kind::Assertion ||
49         expression.getKind() == Kind::ArrayAssertion;
50 
51     bool childrenConstant = true;
52     expression.eachChild([&](const Expression& child) {
53         // We can _almost_ assume that if `expressions` children are constant,
54         // they would already have been evaluated to Literal values when they
55         // were parsed.  Type annotations are the exception, because they might
56         // have been inferred and added after a child was parsed.
57 
58         // So we recurse into isConstant() for the children of type annotations,
59         // but otherwise simply check whether they are Literals.
60         if (isTypeAnnotation) {
61             childrenConstant = childrenConstant && isConstant(child);
62         } else {
63             childrenConstant = childrenConstant && child.getKind() == Kind::Literal;
64         }
65     });
66     if (!childrenConstant) {
67         return false;
68     }
69 
70     return isFeatureConstant(expression) &&
71         isGlobalPropertyConstant(expression, std::array<std::string, 2>{{"zoom", "heatmap-density"}});
72 }
73 
74 using namespace mbgl::style::conversion;
75 
parse(const Convertible & value,std::size_t index_,optional<type::Type> expected_,TypeAnnotationOption typeAnnotationOption)76 ParseResult ParsingContext::parse(const Convertible& value,
77                                   std::size_t index_,
78                                   optional<type::Type> expected_,
79                                   TypeAnnotationOption typeAnnotationOption) {
80     ParsingContext child(key + "[" + util::toString(index_) + "]",
81                          errors,
82                          std::move(expected_),
83                          scope);
84     return child.parse(value, typeAnnotationOption);
85 }
86 
parse(const Convertible & value,std::size_t index_,optional<type::Type> expected_,const std::map<std::string,std::shared_ptr<Expression>> & bindings)87 ParseResult ParsingContext::parse(const Convertible& value, std::size_t index_, optional<type::Type> expected_,
88                                   const std::map<std::string, std::shared_ptr<Expression>>& bindings) {
89     ParsingContext child(key + "[" + util::toString(index_) + "]",
90                          errors,
91                          std::move(expected_),
92                          std::make_shared<detail::Scope>(bindings, scope));
93     return child.parse(value);
94 }
95 
getExpressionRegistry()96 const ExpressionRegistry& getExpressionRegistry() {
97     static ExpressionRegistry registry {{
98         {"==", Equals::parse},
99         {"!=", Equals::parse},
100         {"all", All::parse},
101         {"any", Any::parse},
102         {"array", ArrayAssertion::parse},
103         {"at", At::parse},
104         {"boolean", Assertion::parse},
105         {"case", Case::parse},
106         {"coalesce", Coalesce::parse},
107         {"collator", CollatorExpression::parse},
108         {"interpolate", parseInterpolate},
109         {"length", Length::parse},
110         {"let", Let::parse},
111         {"literal", Literal::parse},
112         {"match", parseMatch},
113         {"number", Assertion::parse},
114         {"object", Assertion::parse},
115         {"step", Step::parse},
116         {"string", Assertion::parse},
117         {"to-color", Coercion::parse},
118         {"to-number", Coercion::parse},
119         {"var", Var::parse}
120     }};
121     return registry;
122 }
123 
parse(const Convertible & value,TypeAnnotationOption typeAnnotationOption)124 ParseResult ParsingContext::parse(const Convertible& value, TypeAnnotationOption typeAnnotationOption) {
125     ParseResult parsed;
126 
127     if (isArray(value)) {
128         const std::size_t length = arrayLength(value);
129         if (length == 0) {
130             error(R"(Expected an array with at least one element. If you wanted a literal array, use ["literal", []].)");
131             return ParseResult();
132         }
133 
134         const optional<std::string> op = toString(arrayMember(value, 0));
135         if (!op) {
136             error(
137                 "Expression name must be a string, but found " + getJSONType(arrayMember(value, 0)) +
138                     R"( instead. If you wanted a literal array, use ["literal", [...]].)",
139                 0
140             );
141             return ParseResult();
142         }
143 
144         const ExpressionRegistry& registry = getExpressionRegistry();
145         auto parseFunction = registry.find(*op);
146         if (parseFunction != registry.end()) {
147             parsed = parseFunction->second(value, *this);
148         } else {
149             parsed = parseCompoundExpression(*op, value, *this);
150         }
151     } else {
152         parsed = Literal::parse(value, *this);
153     }
154 
155     if (!parsed) {
156         assert(errors->size() > 0);
157         return parsed;
158     }
159 
160     auto array = [&](std::unique_ptr<Expression> expression) {
161         std::vector<std::unique_ptr<Expression>> args;
162         args.push_back(std::move(expression));
163         return args;
164     };
165 
166     if (expected) {
167         const type::Type actual = (*parsed)->getType();
168         if ((*expected == type::String || *expected == type::Number || *expected == type::Boolean || *expected == type::Object) && actual == type::Value) {
169             if (typeAnnotationOption == includeTypeAnnotations) {
170                 parsed = { std::make_unique<Assertion>(*expected, array(std::move(*parsed))) };
171             }
172         } else if (expected->is<type::Array>() && actual == type::Value) {
173             if (typeAnnotationOption == includeTypeAnnotations) {
174                 parsed = { std::make_unique<ArrayAssertion>(expected->get<type::Array>(), std::move(*parsed)) };
175             }
176         } else if (*expected == type::Color && (actual == type::Value || actual == type::String)) {
177             if (typeAnnotationOption == includeTypeAnnotations) {
178                 parsed = { std::make_unique<Coercion>(*expected, array(std::move(*parsed))) };
179             }
180         } else {
181             checkType((*parsed)->getType());
182             if (errors->size() > 0) {
183                 return ParseResult();
184             }
185         }
186     }
187 
188     // If an expression's arguments are all constant, we can evaluate
189     // it immediately and replace it with a literal value in the
190     // parsed result.
191     if ((*parsed)->getKind() != Kind::Literal && isConstant(**parsed)) {
192         EvaluationContext params(nullptr);
193         EvaluationResult evaluated((*parsed)->evaluate(params));
194         if (!evaluated) {
195             error(evaluated.error().message);
196             return ParseResult();
197         }
198 
199         const type::Type type = (*parsed)->getType();
200         if (type.is<type::Array>()) {
201             // keep the original expression's array type, even if the evaluated
202             // type is more specific.
203             return ParseResult(std::make_unique<Literal>(
204                   type.get<type::Array>(),
205                   evaluated->get<std::vector<Value>>())
206             );
207         } else {
208             return ParseResult(std::make_unique<Literal>(*evaluated));
209         }
210     }
211 
212     return parsed;
213 }
214 
parseExpression(const Convertible & value,TypeAnnotationOption typeAnnotationOption)215 ParseResult ParsingContext::parseExpression(const Convertible& value, TypeAnnotationOption typeAnnotationOption) {
216     return parse(value, typeAnnotationOption);
217 }
218 
parseLayerPropertyExpression(const Convertible & value,TypeAnnotationOption typeAnnotationOption)219 ParseResult ParsingContext::parseLayerPropertyExpression(const Convertible& value, TypeAnnotationOption typeAnnotationOption) {
220     ParseResult parsed = parse(value, typeAnnotationOption);
221     if (parsed && !isZoomConstant(**parsed)) {
222         optional<variant<const Interpolate*, const Step*, ParsingError>> zoomCurve = findZoomCurve(parsed->get());
223         if (!zoomCurve) {
224             error(R"("zoom" expression may only be used as input to a top-level "step" or "interpolate" expression.)");
225             return ParseResult();
226         } else if (zoomCurve->is<ParsingError>()) {
227             error(zoomCurve->get<ParsingError>().message);
228             return ParseResult();
229         }
230     }
231     return parsed;
232 }
233 
getCombinedErrors() const234 const std::string ParsingContext::getCombinedErrors() const {
235     std::string combinedError;
236     for (const ParsingError& parsingError : *errors) {
237         if (combinedError.size() > 0) {
238             combinedError += "\n";
239         }
240         if (parsingError.key.size() > 0) {
241             combinedError += parsingError.key + ": ";
242         }
243         combinedError += parsingError.message;
244     }
245     return combinedError;
246 }
247 
checkType(const type::Type & t)248 optional<std::string> ParsingContext::checkType(const type::Type& t) {
249     assert(expected);
250     optional<std::string> err = type::checkSubtype(*expected, t);
251     if (err) {
252         error(*err);
253     }
254     return err;
255 }
256 
257 } // namespace expression
258 } // namespace style
259 } // namespace mbgl
260