Adapts all grammars to use expectation parsers without backtracking.

Sequence parsers using `>>` allow for backtracking, expectation parsers
`>` do not. This allows us to properly report the position where parsing
failed, by catching the expectation_failure exception and adapting the
iterator ourselves.

References:
- https://github.com/Project-OSRM/osrm-backend/pull/2188
- https://github.com/Project-OSRM/osrm-backend/issues/2168
- http://www.boost.org/doc/libs/1_55_0/libs/spirit/doc/html/spirit/qi/reference/operator/expect.html
This commit is contained in:
Daniel J. Hofmann 2016-04-08 14:05:39 +02:00 committed by Patrick Niklaus
parent def89ac079
commit 8a2bd09fd0
8 changed files with 48 additions and 32 deletions

View File

@ -11,8 +11,8 @@
//#define BOOST_SPIRIT_DEBUG //#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp> #include <boost/spirit/include/qi.hpp>
#include <string>
#include <limits> #include <limits>
#include <string>
namespace osrm namespace osrm
{ {
@ -106,15 +106,15 @@ struct BaseParametersGrammar : boost::spirit::qi::grammar<std::string::iterator>
unlimited.add("unlimited", std::numeric_limits<double>::infinity()); unlimited.add("unlimited", std::numeric_limits<double>::infinity());
radiuses_rule = qi::lit("radiuses=") >> -(unlimited | qi::double_) % ";"; radiuses_rule = qi::lit("radiuses=") > -(unlimited | qi::double_) % ";";
hints_rule = hints_rule =
qi::lit("hints=") >> qi::lit("hints=") >
qi::as_string[qi::repeat(engine::ENCODED_HINT_SIZE)[base64_char]][add_hint] % ";"; qi::as_string[qi::repeat(engine::ENCODED_HINT_SIZE)[base64_char]][add_hint] % ";";
bearings_rule = bearings_rule =
qi::lit("bearings=") >> (-(qi::short_ >> ',' >> qi::short_))[add_bearing] % ";"; qi::lit("bearings=") > (-(qi::short_ > ',' > qi::short_))[add_bearing] % ";";
polyline_rule = qi::as_string[qi::lit("polyline(") >> +polyline_chars >> qi::lit(")")] polyline_rule = qi::as_string[qi::lit("polyline(") > +polyline_chars > qi::lit(")")]
[polyline_to_coordinates]; [polyline_to_coordinates];
location_rule = (double_ >> qi::lit(',') >> double_)[add_coordinate]; location_rule = (double_ > qi::lit(',') > double_)[add_coordinate];
query_rule = (location_rule % ';') | polyline_rule; query_rule = (location_rule % ';') | polyline_rule;
base_rule = bearings_rule | radiuses_rule[set_radiuses] | hints_rule; base_rule = bearings_rule | radiuses_rule[set_radiuses] | hints_rule;

View File

@ -47,17 +47,17 @@ struct MatchParametersGrammar final : public BaseParametersGrammar
parameters.timestamps = std::move(timestamps); parameters.timestamps = std::move(timestamps);
}; };
steps_rule = qi::lit("steps=") >> qi::bool_; steps_rule = qi::lit("steps=") > qi::bool_;
geometries_rule = qi::lit("geometries=geojson")[set_geojson_type] | geometries_rule = qi::lit("geometries=geojson")[set_geojson_type] |
qi::lit("geometries=polyline")[set_polyline_type]; qi::lit("geometries=polyline")[set_polyline_type];
overview_rule = qi::lit("overview=simplified")[set_simplified_type] | overview_rule = qi::lit("overview=simplified")[set_simplified_type] |
qi::lit("overview=full")[set_full_type] | qi::lit("overview=full")[set_full_type] |
qi::lit("overview=false")[set_false_type]; qi::lit("overview=false")[set_false_type];
timestamps_rule = qi::lit("timestamps=") >> qi::uint_ % ";"; timestamps_rule = qi::lit("timestamps=") > qi::uint_ % ";";
match_rule = steps_rule[set_steps] | geometries_rule | overview_rule | match_rule = steps_rule[set_steps] | geometries_rule | overview_rule |
timestamps_rule[set_timestamps]; timestamps_rule[set_timestamps];
root_rule = root_rule =
query_rule >> -qi::lit(".json") >> -(qi::lit("?") >> (match_rule | base_rule) % '&'); query_rule > -qi::lit(".json") > -(qi::lit("?") > (match_rule | base_rule) % '&');
} }
engine::api::MatchParameters parameters; engine::api::MatchParameters parameters;

View File

@ -25,9 +25,9 @@ struct NearestParametersGrammar final : public BaseParametersGrammar
const auto set_number = [this](const unsigned number) { const auto set_number = [this](const unsigned number) {
parameters.number_of_results = number; parameters.number_of_results = number;
}; };
nearest_rule = (qi::lit("number=") >> qi::uint_)[set_number]; nearest_rule = (qi::lit("number=") > qi::uint_)[set_number];
root_rule = root_rule =
query_rule >> -qi::lit(".json") >> -(qi::lit("?") >> (nearest_rule | base_rule) % '&'); query_rule > -qi::lit(".json") > -(qi::lit("?") > (nearest_rule | base_rule) % '&');
} }
engine::api::NearestParameters parameters; engine::api::NearestParameters parameters;

View File

@ -49,19 +49,19 @@ struct RouteParametersGrammar : public BaseParametersGrammar
}; };
const auto set_uturns = [this](UturnsT uturns) { parameters.uturns = std::move(uturns); }; const auto set_uturns = [this](UturnsT uturns) { parameters.uturns = std::move(uturns); };
alternatives_rule = qi::lit("alternatives=") >> qi::bool_; alternatives_rule = qi::lit("alternatives=") > qi::bool_;
steps_rule = qi::lit("steps=") >> qi::bool_; steps_rule = qi::lit("steps=") > qi::bool_;
geometries_rule = qi::lit("geometries=geojson")[set_geojson_type] | geometries_rule = qi::lit("geometries=geojson")[set_geojson_type] |
qi::lit("geometries=polyline")[set_polyline_type]; qi::lit("geometries=polyline")[set_polyline_type];
overview_rule = qi::lit("overview=simplified")[set_simplified_type] | overview_rule = qi::lit("overview=simplified")[set_simplified_type] |
qi::lit("overview=full")[set_full_type] | qi::lit("overview=full")[set_full_type] |
qi::lit("overview=false")[set_false_type]; qi::lit("overview=false")[set_false_type];
uturns_rule = qi::lit("uturns=default") | (qi::lit("uturns=") >> qi::bool_)[set_uturns]; uturns_rule = qi::lit("uturns=default") | (qi::lit("uturns=") > qi::bool_)[set_uturns];
route_rule = steps_rule[set_steps] | alternatives_rule[set_alternatives] | geometries_rule | route_rule = steps_rule[set_steps] | alternatives_rule[set_alternatives] | geometries_rule |
overview_rule | uturns_rule; overview_rule | uturns_rule;
root_rule = root_rule =
query_rule >> -qi::lit(".json") >> -(qi::lit("?") >> (route_rule | base_rule) % '&'); query_rule > -qi::lit(".json") > -(qi::lit("?") > (route_rule | base_rule) % '&');
} }
engine::api::RouteParameters parameters; engine::api::RouteParameters parameters;

View File

@ -30,14 +30,14 @@ struct TableParametersGrammar final : public BaseParametersGrammar
const auto set_sources = [this](SourcesT sources) { const auto set_sources = [this](SourcesT sources) {
parameters.sources = std::move(sources); parameters.sources = std::move(sources);
}; };
destinations_rule = (qi::lit("destinations=") >> (qi::ulong_ % ";")[set_destiantions]) | destinations_rule = (qi::lit("destinations=") > (qi::ulong_ % ";")[set_destiantions]) |
qi::lit("destinations=all"); qi::lit("destinations=all");
sources_rule = sources_rule =
(qi::lit("sources=") >> (qi::ulong_ % ";")[set_sources]) | qi::lit("sources=all"); (qi::lit("sources=") > (qi::ulong_ % ";")[set_sources]) | qi::lit("sources=all");
table_rule = destinations_rule | sources_rule; table_rule = destinations_rule | sources_rule;
root_rule = root_rule =
query_rule >> -qi::lit(".json") >> -(qi::lit("?") >> (table_rule | base_rule) % '&'); query_rule > -qi::lit(".json") > -(qi::lit("?") > (table_rule | base_rule) % '&');
} }
engine::api::TableParameters parameters; engine::api::TableParameters parameters;

View File

@ -29,10 +29,11 @@ struct TileParametersGrammar final : boost::spirit::qi::grammar<std::string::ite
const auto set_y = [this](const unsigned y_) { parameters.y = y_; }; const auto set_y = [this](const unsigned y_) { parameters.y = y_; };
const auto set_z = [this](const unsigned z_) { parameters.z = z_; }; const auto set_z = [this](const unsigned z_) { parameters.z = z_; };
query_rule = qi::lit("tile(") >> qi::uint_[set_x] >> qi::lit(",") >> qi::uint_[set_y] >> query_rule = qi::lit("tile(") > qi::uint_[set_x] //
qi::lit(",") >> qi::uint_[set_z] >> qi::lit(")"); > qi::lit(",") > qi::uint_[set_y] > //
qi::lit(",") > qi::uint_[set_z] > qi::lit(")"); //
root_rule = query_rule >> qi::lit(".mvt"); root_rule = query_rule > qi::lit(".mvt");
} }
engine::api::TileParameters parameters; engine::api::TileParameters parameters;

View File

@ -43,7 +43,7 @@ struct TripParametersGrammar final : public BaseParametersGrammar
}; };
const auto set_steps = [this](const StepsT steps) { parameters.steps = steps; }; const auto set_steps = [this](const StepsT steps) { parameters.steps = steps; };
steps_rule = qi::lit("steps=") >> qi::bool_; steps_rule = qi::lit("steps=") > qi::bool_;
geometries_rule = qi::lit("geometries=geojson")[set_geojson_type] | geometries_rule = qi::lit("geometries=geojson")[set_geojson_type] |
qi::lit("geometries=polyline")[set_polyline_type]; qi::lit("geometries=polyline")[set_polyline_type];
overview_rule = qi::lit("overview=simplified")[set_simplified_type] | overview_rule = qi::lit("overview=simplified")[set_simplified_type] |
@ -52,7 +52,7 @@ struct TripParametersGrammar final : public BaseParametersGrammar
trip_rule = steps_rule[set_steps] | geometries_rule | overview_rule; trip_rule = steps_rule[set_steps] | geometries_rule | overview_rule;
root_rule = root_rule =
query_rule >> -qi::lit(".json") >> -(qi::lit("?") >> (trip_rule | base_rule) % '&'); query_rule > -qi::lit(".json") > -(qi::lit("?") > (trip_rule | base_rule) % '&');
} }
engine::api::TripParameters parameters; engine::api::TripParameters parameters;

View File

@ -1,11 +1,13 @@
#include "server/api/parameters_parser.hpp" #include "server/api/parameters_parser.hpp"
#include "server/api/match_parameter_grammar.hpp"
#include "server/api/nearest_parameter_grammar.hpp"
#include "server/api/route_parameters_grammar.hpp" #include "server/api/route_parameters_grammar.hpp"
#include "server/api/table_parameter_grammar.hpp" #include "server/api/table_parameter_grammar.hpp"
#include "server/api/nearest_parameter_grammar.hpp"
#include "server/api/trip_parameter_grammar.hpp"
#include "server/api/match_parameter_grammar.hpp"
#include "server/api/tile_parameter_grammar.hpp" #include "server/api/tile_parameter_grammar.hpp"
#include "server/api/trip_parameter_grammar.hpp"
#include <type_traits>
namespace osrm namespace osrm
{ {
@ -25,16 +27,29 @@ template <typename ParameterT,
typename GrammarT, typename GrammarT,
typename std::enable_if<detail::is_parameter_t<ParameterT>::value, int>::type = 0, typename std::enable_if<detail::is_parameter_t<ParameterT>::value, int>::type = 0,
typename std::enable_if<detail::is_grammar_t<GrammarT>::value, int>::type = 0> typename std::enable_if<detail::is_grammar_t<GrammarT>::value, int>::type = 0>
boost::optional<ParameterT> parseParameters(std::string::iterator &iter, const std::string::iterator end) boost::optional<ParameterT> parseParameters(std::string::iterator &iter,
const std::string::iterator end)
{ {
using It = std::decay<decltype(iter)>::type;
GrammarT grammar; GrammarT grammar;
const auto result = boost::spirit::qi::parse(iter, end, grammar);
boost::optional<ParameterT> parameters; try
if (result && iter == end) {
parameters = std::move(grammar.parameters); const auto ok = boost::spirit::qi::parse(iter, end, grammar);
return parameters; // return move(a.b) is needed to move b out of a and then return the rvalue by implicit move
if (ok && iter == end)
return std::move(grammar.parameters);
}
catch (const qi::expectation_failure<It> &failure)
{
// The grammar above using expectation parsers ">" does not automatically increment the
// iterator to the failing position. Extract the position from the exception ourselves.
iter = failure.first;
}
return boost::none;
} }
} // ns detail } // ns detail