#include #include #include #include #include namespace mbgl { namespace style { namespace expression { EvaluationResult toNumber(const Value& v) { optional result = v.match( [](NullValue) -> optional { return 0.0; }, [](const double f) -> optional { return f; }, [](const std::string& s) -> optional { try { return util::stof(s); } catch (const std::exception&) { return optional(); } }, [](const auto&) { return optional(); } ); if (!result) { return EvaluationError { "Could not convert " + stringify(v) + " to number." }; } return *result; } EvaluationResult toColor(const Value& colorValue) { return colorValue.match( [&](const Color& color) -> EvaluationResult { return color; }, [&](const std::string& colorString) -> EvaluationResult { const optional result = Color::parse(colorString); if (result) { return *result; } else { return EvaluationError{ "Could not parse color from value '" + colorString + "'" }; } }, [&](const std::vector& components) -> EvaluationResult { std::size_t len = components.size(); bool isNumeric = std::all_of(components.begin(), components.end(), [](const Value& item) { return item.template is(); }); if ((len == 3 || len == 4) && isNumeric) { Result c = {rgba( components[0].template get(), components[1].template get(), components[2].template get(), len == 4 ? components[3].template get() : 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> inputs_) : Expression(Kind::Coercion, std::move(type_)), inputs(std::move(inputs_)) { assert(!inputs.empty()); type::Type t = getType(); if (t.is()) { coerceSingleValue = toNumber; } else if (t.is()) { coerceSingleValue = toColor; } else { assert(false); } } std::string Coercion::getOperator() const { return getType().match( [](const type::NumberType&) { return "to-number"; }, [](const type::ColorType&) { return "to-color"; }, [](const auto&) { assert(false); return ""; }); } using namespace mbgl::style::conversion; ParseResult Coercion::parse(const Convertible& value, ParsingContext& ctx) { static std::unordered_map 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> parsed; parsed.reserve(length - 1); for (std::size_t i = 1; 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(it->second, std::move(parsed))); } EvaluationResult Coercion::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; EvaluationResult coerced = coerceSingleValue(*value); if (coerced || i == inputs.size() - 1) { return coerced; } } assert(false); return EvaluationError { "Unreachable" }; }; void Coercion::eachChild(const std::function& visit) const { for(const std::unique_ptr& input : inputs) { visit(*input); } }; bool Coercion::operator==(const Expression& e) const { if (e.getKind() == Kind::Coercion) { auto rhs = static_cast(&e); return getType() == rhs->getType() && Expression::childrenEqual(inputs, rhs->inputs); } return false; } std::vector> Coercion::possibleOutputs() const { std::vector> result; for (const auto& input : inputs) { for (auto& output : input->possibleOutputs()) { result.push_back(std::move(output)); } } return result; } } // namespace expression } // namespace style } // namespace mbgl