#include #include #include #include namespace mbgl { namespace style { namespace expression { using namespace mbgl::style::conversion; Assertion::Assertion(type::Type type_, std::vector> inputs_) : Expression(Kind::Assertion, std::move(type_)), inputs(std::move(inputs_)) { assert(!inputs.empty()); } ParseResult Assertion::parse(const Convertible& value, ParsingContext& ctx) { static std::unordered_map types{ {"string", type::String}, {"image", type::String}, // TODO: replace once we implement image expressions {"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(); } std::size_t i = 1; type::Type type; std::string name = *toString(arrayMember(value, 0)); if (name == "array") { optional itemType; if (length > 2) { optional itemTypeName = toString(arrayMember(value, 1)); auto it = itemTypeName ? types.find(*itemTypeName) : types.end(); if (it == types.end() || it->second == type::Object) { ctx.error( R"(The item type argument of "array" must be one of string, number, boolean)", 1 ); return ParseResult(); } itemType = it->second; i++; } else { itemType = {type::Value}; } optional N; if (length > 3) { auto m = arrayMember(value, 2); optional n = toNumber(m); if (!isUndefined(m) && (!n || *n < 0 || *n != std::floor(*n))) { ctx.error( R"(The length argument to "array" must be a positive integer literal.)", 2 ); return ParseResult(); } if (n) { N = optional(*n); } i++; } type = type::Array(*itemType, N); } else { type = types.at(name); } std::vector> parsed; parsed.reserve(length - 1); for (; i < length; i++) { ParseResult input = ctx.parse(arrayMember(value, i), i, {type::Value}); if (!input) return ParseResult(); parsed.push_back(std::move(*input)); } return ParseResult(std::make_unique(type, std::move(parsed))); } std::string Assertion::getOperator() const { return getType().is() ? "array" : type::toString(getType()); } EvaluationResult Assertion::evaluate(const EvaluationContext& 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 EvaluationError { "Unreachable" }; }; void Assertion::eachChild(const std::function& visit) const { for(const std::unique_ptr& input : inputs) { visit(*input); } }; bool Assertion::operator==(const Expression& e) const { if (e.getKind() == Kind::Assertion) { auto rhs = static_cast(&e); return getType() == rhs->getType() && Expression::childrenEqual(inputs, rhs->inputs); } return false; } std::vector> Assertion::possibleOutputs() const { std::vector> result; for (const auto& input : inputs) { for (auto& output : input->possibleOutputs()) { result.push_back(std::move(output)); } } return result; } mbgl::Value Assertion::serialize() const { std::vector serialized; serialized.emplace_back(getOperator()); if (getType().is()) { const auto array = getType().get(); if (array.itemType.is() || array.itemType.is() || array.itemType.is()) { serialized.emplace_back(type::toString(array.itemType)); if (array.N) { serialized.emplace_back(uint64_t(*array.N)); } else if (inputs.size() > 1) { serialized.emplace_back(mbgl::NullValue()); } } } for (const auto& input : inputs) { serialized.emplace_back(input->serialize()); } return serialized; } } // namespace expression } // namespace style } // namespace mbgl