Skip to content

Commit 551a6b6

Browse files
committed
Rewritten as a grammar, added IPv4 support
1 parent f48c117 commit 551a6b6

File tree

4 files changed

+240
-121
lines changed

4 files changed

+240
-121
lines changed

boost/network/uri/detail/parse_uri.hpp

Lines changed: 210 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -6,148 +6,243 @@
66
// (See accompanying file LICENSE_1_0.txt or copy at
77
// http://www.boost.org/LICENSE_1_0.txt)
88

9-
#include <boost/fusion/tuple.hpp>
109
#include <boost/spirit/include/phoenix_operator.hpp>
11-
#include <boost/spirit/include/qi_char_.hpp>
10+
#include <boost/spirit/include/qi_attr.hpp>
1211
#include <boost/spirit/include/qi_core.hpp>
1312
#include <boost/spirit/include/qi_eps.hpp>
13+
#include <boost/spirit/include/qi_grammar.hpp>
1414
#include <boost/spirit/include/qi_omit.hpp>
15-
#include <boost/spirit/include/qi_parse.hpp>
1615
#include <boost/spirit/include/qi_raw.hpp>
1716
#include <boost/spirit/include/qi_rule.hpp>
1817
#include <boost/spirit/include/qi_sequence.hpp>
18+
#include <boost/spirit/include/version.hpp>
1919

2020
#include <boost/network/uri/detail/uri_parts.hpp>
2121

22+
namespace boost { namespace spirit { namespace traits {
23+
24+
template <class Tag>
25+
struct transform_attribute<
26+
boost::network::uri::detail::uri_parts<Tag>,
27+
typename boost::network::uri::detail::uri_parts_tuple<Tag>::type
28+
>
29+
{
30+
typedef typename boost::network::uri::detail::uri_parts_tuple<Tag>::type type;
31+
32+
static type pre(boost::network::uri::detail::uri_parts<Tag> & parts) {
33+
typedef typename boost::network::string<Tag>::type string_type;
34+
35+
boost::fusion::tuple<
36+
boost::optional<string_type> &,
37+
boost::optional<string_type> &,
38+
boost::optional<boost::uint16_t> &,
39+
string_type &
40+
> hier_part =
41+
boost::fusion::tie(
42+
parts.user_info,
43+
parts.host,
44+
parts.port,
45+
parts.path
46+
);
47+
48+
return boost::fusion::tie(
49+
parts.scheme,
50+
hier_part,
51+
parts.query,
52+
parts.fragment
53+
);
54+
}
55+
56+
static void post(boost::network::uri::detail::uri_parts<Tag> &, type const &) { }
57+
58+
#if SPIRIT_VERSION >= 0x2030
59+
static void fail(boost::network::uri::detail::uri_parts<Tag> & val) { }
60+
#endif
61+
};
62+
63+
#if SPIRIT_VERSION < 0x2030
64+
template <typename Exposed, typename Transformed>
65+
struct transform_attribute<
66+
optional<Exposed>,
67+
Transformed,
68+
typename disable_if<is_same<optional<Exposed>, Transformed> >::type
69+
>
70+
{
71+
typedef Transformed & type;
72+
73+
static Transformed & pre(optional<Exposed> & val) {
74+
if (!val)
75+
val = Transformed();
76+
return boost::get<Transformed>(val);
77+
}
78+
79+
static void post(optional<Exposed> &, Transformed const &) { }
80+
};
81+
#endif
82+
83+
} // namespace traits
84+
85+
} // namespace spirit
86+
87+
} // namespace boost
88+
2289
namespace boost { namespace network { namespace uri {
2390

2491
namespace detail {
2592

93+
namespace qi = boost::spirit::qi;
94+
95+
template <typename Iterator, class Tag>
96+
struct uri_grammar : qi::grammar<Iterator, uri_parts<Tag>()> {
97+
uri_grammar() : uri_grammar::base_type(start, "uri") {
98+
// gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"
99+
gen_delims = qi::char_(":/?#[]@");
100+
// sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
101+
sub_delims = qi::char_("!$&'()*+,;=");
102+
// reserved = gen-delims / sub-delims
103+
reserved = gen_delims | sub_delims;
104+
// unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
105+
unreserved = qi::alnum | qi::char_("-._~");
106+
// pct-encoded = "%" HEXDIG HEXDIG
107+
pct_encoded = qi::char_("%") >> qi::repeat(2)[qi::xdigit];
108+
109+
// pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
110+
pchar = qi::raw[
111+
unreserved | pct_encoded | sub_delims | qi::char_(":@")
112+
];
113+
114+
// segment = *pchar
115+
segment = qi::raw[*pchar];
116+
// segment-nz = 1*pchar
117+
segment_nz = qi::raw[+pchar];
118+
// segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" )
119+
segment_nz_nc = qi::raw[
120+
+(unreserved | pct_encoded | sub_delims | qi::char_("@"))
121+
];
122+
// path-abempty = *( "/" segment )
123+
path_abempty = qi::raw[*(qi::char_("/") >> segment)];
124+
// path-absolute = "/" [ segment-nz *( "/" segment ) ]
125+
path_absolute = qi::raw[
126+
qi::char_("/")
127+
>> -(segment_nz >> *(qi::char_("/") >> segment))
128+
];
129+
// path-rootless = segment-nz *( "/" segment )
130+
path_rootless = qi::raw[
131+
segment_nz >> *(qi::char_("/") >> segment)
132+
];
133+
// path-empty = 0<pchar>
134+
path_empty = qi::eps;
135+
136+
// scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
137+
scheme = qi::alpha >> *(qi::alnum | qi::char_("+.-"));
138+
139+
// user_info = *( unreserved / pct-encoded / sub-delims / ":" )
140+
user_info = qi::raw[
141+
*(unreserved | pct_encoded | sub_delims | qi::char_(":"))
142+
];
143+
144+
// dec-octet = DIGIT / %x31-39 DIGIT / "1" 2DIGIT / "2" %x30-34 DIGIT / "25" %x30-35
145+
dec_octet =
146+
!(qi::lit('0') >> qi::digit)
147+
>> qi::raw[
148+
qi::uint_parser<boost::uint8_t, 10, 1, 3>()
149+
];
150+
// IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet
151+
ipv4address = qi::raw[
152+
dec_octet >> qi::repeat(3)[qi::lit('.') >> dec_octet]
153+
];
154+
// reg-name = *( unreserved / pct-encoded / sub-delims )
155+
reg_name = qi::raw[
156+
*(unreserved | pct_encoded | sub_delims)
157+
];
158+
// TODO, host = IP-literal / IPv4address / reg-name
159+
host = ipv4address | reg_name;
160+
161+
// query = *( pchar / "/" / "?" )
162+
query = qi::raw[*(pchar | qi::char_("/?"))];
163+
// fragment = *( pchar / "/" / "?" )
164+
fragment = qi::raw[*(pchar | qi::char_("/?"))];
165+
166+
// hier-part = "//" authority path-abempty / path-absolute / path-rootless / path-empty
167+
// authority = [ userinfo "@" ] host [ ":" port ]
168+
hier_part =
169+
(
170+
"//"
171+
>> -(user_info >> '@')
172+
>> host
173+
>> -(':' >> qi::ushort_)
174+
>> path_abempty
175+
)
176+
|
177+
(
178+
qi::attr(optional<typename boost::network::string<Tag>::type>())
179+
>> qi::attr(optional<typename boost::network::string<Tag>::type>())
180+
>> qi::attr(optional<boost::uint16_t>())
181+
>> (
182+
path_absolute
183+
| path_rootless
184+
| path_empty
185+
)
186+
);
187+
188+
start = uri;
189+
uri =
190+
scheme >> ':'
191+
>> hier_part
192+
>> -('?' >> query)
193+
>> -('#' >> fragment);
194+
}
195+
196+
typedef typename string<Tag>::type string_type;
197+
198+
qi::rule<Iterator, typename string_type::value_type()>
199+
gen_delims, sub_delims, reserved, unreserved;
200+
qi::rule<Iterator, string_type()>
201+
pct_encoded, pchar;
202+
203+
qi::rule<Iterator, string_type()>
204+
segment, segment_nz, segment_nz_nc;
205+
qi::rule<Iterator, string_type()>
206+
path_abempty, path_absolute, path_rootless, path_empty;
207+
208+
qi::rule<Iterator, string_type()>
209+
dec_octet, ipv4address, reg_name, host;
210+
211+
qi::rule<Iterator, string_type()>
212+
scheme, user_info, query, fragment;
213+
214+
qi::rule<Iterator, boost::fusion::tuple<
215+
optional<string_type> &,
216+
optional<string_type> &,
217+
optional<boost::uint16_t> &,
218+
string_type &
219+
>()> hier_part;
220+
221+
// start rule of grammar
222+
qi::rule<Iterator, uri_parts<Tag>()> start;
223+
224+
// actual uri parser
225+
qi::rule<Iterator, typename uri_parts_tuple<Tag>::type()> uri;
226+
};
227+
26228
template <class Tag>
27229
inline bool parse_specific(uri_parts<Tag> & parts) {
28230
return true;
29231
}
30232

31233
template <class Range, class Tag>
32234
inline bool parse_uri(Range & range, uri_parts<Tag> & parts) {
33-
namespace qi = boost::spirit::qi;
34-
35235
typedef typename range_iterator<Range>::type iterator;
36236

37-
typedef typename string<Tag>::type string_type;
38-
typedef typename string<Tag>::type::value_type char_type;
39-
40237
iterator start_ = begin(range);
41238
iterator end_ = end(range);
42-
fusion::tuple<
43-
string_type &,
44-
string_type &,
45-
boost::optional<string_type> &,
46-
boost::optional<string_type> &
47-
> result =
48-
fusion::tie(
49-
parts.scheme,
50-
parts.path,
51-
parts.query,
52-
parts.fragment
53-
);
54239

55-
// gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"
56-
qi::rule<iterator, char_type()> gen_delims, sub_delims;
57-
gen_delims = qi::char_(":/?#[]@");
58-
// sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
59-
sub_delims = qi::char_("!$&'()*+,;=");
60-
// reserved = gen-delims / sub-delims
61-
qi::rule<iterator, char_type()> reserved;
62-
reserved = gen_delims | sub_delims;
63-
// unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
64-
qi::rule<iterator, char_type()> unreserved;
65-
unreserved = qi::alnum | qi::char_("-._~");
66-
// pct-encoded = "%" HEXDIG HEXDIG
67-
qi::rule<iterator, string_type()> pct_encoded;
68-
pct_encoded = qi::char_("%") >> qi::repeat(2)[qi::xdigit];
69-
70-
// pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
71-
qi::rule<iterator, string_type()> pchar;
72-
pchar = qi::raw[
73-
unreserved | pct_encoded | sub_delims | qi::char_(":@")
74-
];
75-
76-
// segment = *pchar
77-
qi::rule<iterator, string_type()> segment = qi::raw[*pchar];
78-
// segment-nz = 1*pchar
79-
qi::rule<iterator, string_type()> segment_nz = qi::raw[+pchar];
80-
// segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" )
81-
qi::rule<iterator, string_type()> segment_nz_nc;
82-
segment_nz_nc = qi::raw[
83-
+(unreserved | pct_encoded | sub_delims | qi::char_("@"))
84-
];
85-
// path-abempty = *( "/" segment )
86-
qi::rule<iterator, string_type()> path_abempty;
87-
path_abempty = qi::raw[*(qi::char_("/") >> segment)];
88-
// path-absolute = "/" [ segment-nz *( "/" segment ) ]
89-
qi::rule<iterator, string_type()> path_absolute;
90-
path_absolute = qi::raw[
91-
qi::char_("/")
92-
>> -(segment_nz >> *(qi::char_("/") >> segment))
93-
];
94-
// path-rootless = segment-nz *( "/" segment )
95-
qi::rule<iterator, string_type()> path_rootless;
96-
path_rootless = qi::raw[
97-
segment_nz >> *(qi::char_("/") >> segment)
98-
];
99-
// path-empty = 0<pchar>
100-
qi::rule<iterator, string_type()> path_empty = qi::eps;
101-
102-
// scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
103-
qi::rule<iterator, string_type()> scheme;
104-
scheme = qi::alpha >> *(qi::alnum | qi::char_("+.-"));
105-
106-
// user_info = *( unreserved / pct-encoded / sub-delims / ":" )
107-
qi::rule<iterator, string_type()> user_info;
108-
user_info = qi::raw[
109-
*(unreserved | pct_encoded | sub_delims | qi::char_(":"))
110-
];
111-
112-
// reg-name = *( unreserved / pct-encoded / sub-delims )
113-
qi::rule<iterator, string_type()> reg_name;
114-
reg_name = qi::raw[*(unreserved | pct_encoded | sub_delims)];
115-
// FIXME, host = IP-literal / IPv4address / reg-name
116-
qi::rule<iterator, string_type()> host = reg_name;
117-
118-
qi::rule<iterator, string_type()> query, fragment;
119-
// query = *( pchar / "/" / "?" )
120-
query = qi::raw[*(pchar | qi::char_("/?"))];
121-
// fragment = *( pchar / "/" / "?" )
122-
fragment = qi::raw[*(pchar | qi::char_("/?"))];
123-
124-
bool ok = qi::parse(
125-
start_, end_,
126-
(
127-
scheme >> ':'
128-
>> (
129-
"//"
130-
>> qi::omit[
131-
-(user_info >> '@') [phoenix::ref(parts.user_info) = qi::_1]
132-
>> host [phoenix::ref(parts.host) = qi::_1]
133-
>> -(':' >> qi::ushort_) [phoenix::ref(parts.port) = qi::_1]
134-
]
135-
>> path_abempty
136-
| path_absolute
137-
| path_rootless
138-
| path_empty
139-
)
140-
>> -('?' >> query)
141-
>> -('#' >> fragment)
142-
),
143-
result
144-
);
240+
uri_grammar<iterator, Tag> grammar;
145241

146-
if (ok) {
147-
ok = parse_specific(
148-
parts
149-
);
150-
}
242+
bool ok = qi::parse(start_, end_, grammar, parts);
243+
244+
if (ok)
245+
ok = parse_specific(parts);
151246

152247
return ok && start_ == end_;
153248
}

0 commit comments

Comments
 (0)