From 4a0aab8eb8754b2773cc29baa2c1b7d3ff01b336 Mon Sep 17 00:00:00 2001 From: Chris Loer Date: Thu, 9 Aug 2018 16:14:49 -0700 Subject: [core] Add automatic argument coercion for compound expressions with multiple overloads. --- src/mbgl/style/expression/compound_expression.cpp | 155 +++++++++++++--------- 1 file changed, 91 insertions(+), 64 deletions(-) diff --git a/src/mbgl/style/expression/compound_expression.cpp b/src/mbgl/style/expression/compound_expression.cpp index fc47b2d78e..e4411fa335 100644 --- a/src/mbgl/style/expression/compound_expression.cpp +++ b/src/mbgl/style/expression/compound_expression.cpp @@ -633,6 +633,48 @@ std::unordered_map initiali std::unordered_map CompoundExpressionRegistry::definitions = initializeDefinitions(); using namespace mbgl::style::conversion; + +std::string expectedTypesError(const Definition& definition, + const std::vector>& args) { + std::vector availableOverloads; // Only used if there are no overloads with matching number of args + std::vector overloads; + for (const auto& signature : definition) { + signature->params.match( + [&](const VarargsType& varargs) { + std::string overload = "(" + toString(varargs.type) + ")"; + overloads.push_back(overload); + }, + [&](const std::vector& params) { + std::string overload = "("; + bool first = true; + for (const type::Type& param : params) { + if (!first) overload += ", "; + overload += toString(param); + first = false; + } + overload += ")"; + if (params.size() == args.size()) { + overloads.push_back(overload); + } else { + availableOverloads.push_back(overload); + } + } + ); + } + std::string signatures = overloads.empty() ? + boost::algorithm::join(availableOverloads, " | ") : + boost::algorithm::join(overloads, " | "); + + std::string actualTypes; + for (const auto& arg : args) { + if (actualTypes.size() > 0) { + actualTypes += ", "; + } + actualTypes += toString(arg->getType()); + } + + return "Expected arguments of type " + signatures + ", but found (" + actualTypes + ") instead."; +} static ParseResult createCompoundExpression(const Definition& definition, std::vector> args, @@ -679,43 +721,7 @@ static ParseResult createCompoundExpression(const Definition& definition, if (definition.size() == 1) { ctx.appendErrors(std::move(signatureContext)); } else { - std::vector availableOverloads; // Only used if there are no overloads with matching number of args - std::vector overloads; - for (const auto& signature : definition) { - signature->params.match( - [&](const VarargsType& varargs) { - std::string overload = "(" + toString(varargs.type) + ")"; - overloads.push_back(overload); - }, - [&](const std::vector& params) { - std::string overload = "("; - bool first = true; - for (const type::Type& param : params) { - if (!first) overload += ", "; - overload += toString(param); - first = false; - } - overload += ")"; - if (params.size() == args.size()) { - overloads.push_back(overload); - } else { - availableOverloads.push_back(overload); - } - } - ); - - } - std::string signatures = overloads.empty() ? - boost::algorithm::join(availableOverloads, " | ") : - boost::algorithm::join(overloads, " | "); - std::string actualTypes; - for (const auto& arg : args) { - if (actualTypes.size() > 0) { - actualTypes += ", "; - } - actualTypes += toString(arg->getType()); - } - ctx.error("Expected arguments of type " + signatures + ", but found (" + actualTypes + ") instead."); + ctx.error(expectedTypesError(definition, args)); } return ParseResult(); @@ -735,46 +741,67 @@ ParseResult parseCompoundExpression(const std::string name, const Convertible& v const CompoundExpressionRegistry::Definition& definition = it->second; auto length = arrayLength(value); - - // Check if we have a single signature with the correct number of - // parameters. If so, then use that signature's parameter types for parsing - // (and inferring the types of) the arguments. - optional singleMatchingSignature; + + bool attemptedParse = false; + for (std::size_t j = 0; j < definition.size(); j++) { const std::unique_ptr& signature = definition[j]; + if ( signature->params.is() || signature->params.get>().size() == length - 1 - ) { - if (singleMatchingSignature) { - singleMatchingSignature = {}; + ) { + // First parse all the args, potentially coercing to the + // types expected by this overload. + bool argParseFailed = false; + std::vector> args; + args.reserve(length - 1); + + for (std::size_t i = 1; i < length; i++) { + optional expected = definition[j]->params.match( + [](const VarargsType& varargs) { return varargs.type; }, + [&](const std::vector& params_) { return params_[i - 1]; } + ); + + auto parsed = ctx.parse(arrayMember(value, i), i, expected); + if (!parsed) { + argParseFailed = true; + break; + } + args.push_back(std::move(*parsed)); + } + if (argParseFailed) { + // Couldn't coerce args of this overload to expected type, move + // on to next one. + ctx.clearErrors(); + continue; } else { - singleMatchingSignature = j; + attemptedParse = true; + ParseResult parseWithArgs = createCompoundExpression(definition, std::move(args), ctx); + if (parseWithArgs) { + return parseWithArgs; + } } } } - - // parse subexpressions first - std::vector> args; - args.reserve(length - 1); - for (std::size_t i = 1; i < length; i++) { - optional expected; - - if (singleMatchingSignature) { - expected = definition[*singleMatchingSignature]->params.match( - [](const VarargsType& varargs) { return varargs.type; }, - [&](const std::vector& params_) { return params_[i - 1]; } - ); + if (!attemptedParse) { + // The args couldn't be coerced to any of the expected types. + // Parse the arguments again without expected types just for the error message + std::vector> args; + args.reserve(length - 1); + + for (std::size_t i = 1; i < length; i++) { + auto parsed = ctx.parse(arrayMember(value, i), i); + if (!parsed) { + return ParseResult(); + } + args.push_back(std::move(*parsed)); } - auto parsed = ctx.parse(arrayMember(value, i), i, expected); - if (!parsed) { - return parsed; - } - args.push_back(std::move(*parsed)); + ctx.error(expectedTypesError(definition, args)); } - return createCompoundExpression(definition, std::move(args), ctx); + return ParseResult(); } ParseResult createCompoundExpression(const std::string& name, -- cgit v1.2.1