Compare commits

...

7 Commits

Author SHA1 Message Date
Siarhei Fedartsou
c8d03e4c3b wip 2024-05-23 19:56:27 +02:00
Siarhei Fedartsou
11a5804d36 wip 2024-05-22 22:42:43 +02:00
Siarhei Fedartsou
14bdf07464 wip 2024-05-22 20:54:19 +02:00
Siarhei Fedartsou
c2c0d3e565 wip 2024-05-22 20:50:17 +02:00
Siarhei Fedartsou
75a5d6d586 wip 2024-05-22 20:32:46 +02:00
Siarhei Fedartsou
3552443896 wip 2024-05-22 18:07:59 +02:00
Siarhei Fedartsou
7766a0f42b Use Boost X3 for URL parsing 2024-05-22 18:05:07 +02:00
3 changed files with 55 additions and 62 deletions

View File

@ -359,7 +359,7 @@ if(ENABLE_CONAN)
KEEP_RPATHS
NO_OUTPUT_DIRS
OPTIONS boost:filesystem_version=3 # https://stackoverflow.com/questions/73392648/error-with-boost-filesystem-version-in-cmake
onetbb:shared=${TBB_SHARED}
# onetbb:shared=${TBB_SHARED}
boost:without_stacktrace=True # Apple Silicon cross-compilation fails without it
BUILD missing
)

View File

@ -2,88 +2,72 @@
#include "engine/polyline_compressor.hpp"
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/repository/include/qi_iter_pos.hpp>
#include <boost/optional.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/position_tagged.hpp>
#include <boost/spirit/home/x3/support/utility/annotate_on_success.hpp>
#include <iostream>
#include <sstream>
#include <string>
#include <type_traits>
BOOST_FUSION_ADAPT_STRUCT(osrm::server::api::ParsedURL,
(std::string, service)(unsigned, version)(std::string,
profile)(std::string, query))
// Keep impl. TU local
namespace
{
namespace ph = boost::phoenix;
namespace qi = boost::spirit::qi;
template <typename Iterator, typename Into> //
struct URLParser final : qi::grammar<Iterator, Into>
{
URLParser() : URLParser::base_type(start)
{
using boost::spirit::repository::qi::iter_pos;
identifier = qi::char_("a-zA-Z0-9_.~:-");
percent_encoding =
qi::char_('%') > qi::uint_parser<unsigned char, 16, 2, 2>()[qi::_val = qi::_1];
polyline_chars = qi::char_("a-zA-Z0-9_[]{}@?|\\~`^") | percent_encoding;
all_chars = polyline_chars | qi::char_("=,;:&().-");
service = +identifier;
version = qi::uint_;
profile = +identifier;
query = +all_chars;
// Example input: /route/v1/driving/7.416351,43.731205;7.420363,43.736189
start = qi::lit('/') > service > qi::lit('/') > qi::lit('v') > version > qi::lit('/') >
profile > qi::lit('/') >
qi::omit[iter_pos[ph::bind(&osrm::server::api::ParsedURL::prefix_length, qi::_val) =
qi::_1 - qi::_r1]] > query;
BOOST_SPIRIT_DEBUG_NODES((start)(service)(version)(profile)(query))
}
qi::rule<Iterator, Into> start;
qi::rule<Iterator, std::string()> service;
qi::rule<Iterator, unsigned()> version;
qi::rule<Iterator, std::string()> profile;
qi::rule<Iterator, std::string()> query;
qi::rule<Iterator, char()> identifier;
qi::rule<Iterator, char()> all_chars;
qi::rule<Iterator, char()> polyline_chars;
qi::rule<Iterator, char()> percent_encoding;
};
} // namespace
namespace osrm::server::api
{
namespace x3 = boost::spirit::x3;
struct ParsedURLClass : x3::annotate_on_success
{
};
const x3::rule<struct Service, std::string> service = "service";
const x3::rule<struct Version, unsigned> version = "version";
const x3::rule<struct Profile, std::string> profile = "profile";
const x3::rule<struct Query, std::string> query = "query";
const x3::rule<struct ParsedURL, ParsedURL> start = "start";
const auto identifier = x3::char_("a-zA-Z0-9_.~:-");
const auto service_def = +identifier;
const auto version_def = x3::uint_;
const auto profile_def = +identifier;
const auto percent_encoding = x3::lit('%') >> x3::uint_parser<unsigned char, 16, 2, 2>();
const auto query_char = percent_encoding | x3::char_("a-zA-Z0-9_[]{}@?|\\~`^=,;:&().-");
const auto query_def = +query_char;
const auto start_def = x3::lit('/') > service > x3::lit('/') > x3::lit('v') > version
> x3::lit('/') > profile > x3::lit('/') > query;
BOOST_SPIRIT_DEFINE(service, version, profile, query, start)
boost::optional<ParsedURL> parseURL(std::string::iterator &iter, const std::string::iterator end)
{
using It = std::decay<decltype(iter)>::type;
static URLParser<It, ParsedURL(It)> const parser;
ParsedURL out;
try
{
const auto ok = boost::spirit::qi::parse(iter, end, parser(boost::phoenix::val(iter)), out);
auto iter_copy = iter;
bool r = x3::phrase_parse(iter_copy, end, start, x3::space, out);
if (ok && iter == end)
if (r && iter_copy == end)
{
iter = iter_copy;
// TODO: find a way to do it more effective
std::string parsed_part =
"/" + out.service + "/v" + std::to_string(out.version) + "/" + out.profile + "/";
out.prefix_length = parsed_part.length();
return boost::make_optional(out);
}
}
catch (const qi::expectation_failure<It> &failure)
catch (const x3::expectation_failure<std::string::iterator> &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;
iter = failure.where();
}
return boost::none;

View File

@ -130,6 +130,15 @@ BOOST_AUTO_TEST_CASE(valid_urls)
BOOST_CHECK_EQUAL(reference_9.profile, result_9->profile);
CHECK_EQUAL_RANGE(reference_9.query, result_9->query);
BOOST_CHECK_EQUAL(reference_9.prefix_length, result_9->prefix_length);
api::ParsedURL reference_10{"match", 1, "car", "poly line ", 14UL};
auto result_10 = api::parseURL("/match/v1/car/poly%20line%20");
BOOST_CHECK(result_10);
BOOST_CHECK_EQUAL(reference_10.service, result_10->service);
BOOST_CHECK_EQUAL(reference_10.version, result_10->version);
BOOST_CHECK_EQUAL(reference_10.profile, result_10->profile);
CHECK_EQUAL_RANGE(reference_10.query, result_10->query);
BOOST_CHECK_EQUAL(reference_10.prefix_length, result_10->prefix_length);
}
BOOST_AUTO_TEST_SUITE_END()