-
Notifications
You must be signed in to change notification settings - Fork 60
Expand file tree
/
Copy pathrange_rule.hpp
More file actions
698 lines (580 loc) · 16.5 KB
/
Copy pathrange_rule.hpp
File metadata and controls
698 lines (580 loc) · 16.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
//
// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/url
//
#ifndef BOOST_URL_GRAMMAR_RANGE_RULE_HPP
#define BOOST_URL_GRAMMAR_RANGE_RULE_HPP
#include <boost/url/detail/config.hpp>
#include <boost/url/error.hpp>
#include <boost/core/detail/string_view.hpp>
#include <boost/url/grammar/parse.hpp>
#include <boost/url/grammar/type_traits.hpp>
#include <boost/url/grammar/detail/range_rule.hpp>
#include <boost/core/detail/static_assert.hpp>
#include <cstddef>
#include <iterator>
#include <type_traits>
#include <utility>
#include <stddef.h> // ::max_align_t
namespace boost {
namespace urls {
namespace grammar {
namespace implementation_defined {
template<class R0, class R1>
struct range_rule_t;
} // implementation_defined
namespace implementation_defined
{
template<class RangeRule, class = void>
struct range_value_type
{
using type = void;
};
template<class RangeRule>
struct range_value_type<
RangeRule,
urls::void_t<typename RangeRule::value_type>>
{
using type = typename RangeRule::value_type;
};
template<class RangeRule, class ValueType, class = void>
struct is_range_rule : std::false_type
{
};
template<class RangeRule, class ValueType>
struct is_range_rule<
RangeRule,
ValueType,
urls::void_t<
decltype(std::declval<RangeRule const&>().first(
std::declval<char const*&>(),
std::declval<char const*>())),
decltype(std::declval<RangeRule const&>().next(
std::declval<char const*&>(),
std::declval<char const*>()))>>
: std::integral_constant<bool,
std::is_same<
decltype(std::declval<RangeRule const&>().first(
std::declval<char const*&>(),
std::declval<char const*>())),
system::result<ValueType>>::value &&
std::is_same<
decltype(std::declval<RangeRule const&>().next(
std::declval<char const*&>(),
std::declval<char const*>())),
system::result<ValueType>>::value>
{
};
}
template<class RangeRule>
using is_range_rule = implementation_defined::is_range_rule<
RangeRule,
typename implementation_defined::range_value_type<
RangeRule>::type>;
#ifdef BOOST_URL_HAS_CONCEPTS
template <class T>
concept RangeRule =
requires (T r, char const*& it, char const* end)
{
typename T::value_type;
{ r.first(it, end) } -> std::same_as<system::result<typename T::value_type>>;
{ r.next(it, end) } -> std::same_as<system::result<typename T::value_type>>;
};
#endif
template<class T>
class any_rule;
template<class T>
class any_rule
{
public:
using value_type = T;
any_rule() noexcept;
any_rule(any_rule const&) noexcept;
any_rule(any_rule&&) noexcept;
any_rule& operator=(any_rule const&) noexcept;
any_rule& operator=(any_rule&&) noexcept;
~any_rule();
template<class R>
explicit
any_rule(R const& next);
template<class R0, class R1>
any_rule(
R0 const& first,
R1 const& next);
system::result<T>
first(
char const*& it,
char const* end) const noexcept;
system::result<T>
next(
char const*& it,
char const* end) const noexcept;
private:
static constexpr
std::size_t BufferSize = 128;
struct small_buffer
{
alignas(alignof(::max_align_t))
unsigned char buf[BufferSize];
void const* addr() const noexcept
{
return buf;
}
void* addr() noexcept
{
return buf;
}
};
struct impl_base;
template<class R, bool>
struct impl1;
template<
class R0, class R1, bool>
struct impl2;
impl_base&
get() noexcept;
impl_base const&
get() const noexcept;
small_buffer sb_;
};
/** A forward range of parsed elements
Objects of this type are forward ranges
returned when parsing using the
@ref range_rule.
Iteration is performed by re-parsing the
underlying character buffer. Ownership
of the buffer is not transferred; the
caller is responsible for ensuring that
the lifetime of the buffer extends until
it is no longer referenced by the range.
@note
The implementation may type-erase the
rule responsible for iterating the
underlying character buffer. Objects
of type `range` are intended to be used
ephemerally. That is, for short durations
such as within a function scope. If it is
necessary to store the range for a long
period of time or with static storage
duration, it is necessary to copy the
contents to an object of a different type.
@tparam T The value type of the range
@tparam RangeRule The implementation used to
iterate the range. The default is a
type-erased rule.
@see
@ref parse,
@ref range_rule.
*/
template<
class T,
class RangeRule = any_rule<T>>
class range
: private detail::range_base_storage<
RangeRule>
{
private:
#ifdef BOOST_URL_HAS_CONCEPTS
static_assert(
::boost::urls::grammar::RangeRule<RangeRule>,
"RangeRule requirements not met");
#else
static_assert(
::boost::urls::grammar::is_range_rule<RangeRule>::value,
"RangeRule requirements not met");
#endif
static_assert(
std::is_class<
detail::range_base_storage<
RangeRule>>::value,
"range_base_storage requirements not met");
using storage_type =
detail::range_base_storage<
RangeRule>;
using storage_type::rule;
core::string_view s_;
std::size_t n_ = 0;
template<
class R0, class R1>
friend struct implementation_defined::range_rule_t;
range(
core::string_view s,
std::size_t n,
RangeRule const& rule) noexcept;
range(
core::string_view s,
std::size_t n,
RangeRule&& rule) noexcept;
public:
/** The type of each element of the range
*/
using value_type = T;
/** The type of each element of the range
*/
using reference = T const&;
/** The type of each element of the range
*/
using const_reference = T const&;
/** Provided for compatibility, unused
*/
using pointer = void const*;
/** The type used to represent unsigned integers
*/
using size_type = std::size_t;
/** The type used to represent signed integers
*/
using difference_type = std::ptrdiff_t;
/** A constant, forward iterator to elements of the range
*/
class iterator;
/** A constant, forward iterator to elements of the range
*/
using const_iterator = iterator;
/** Destructor
*/
~range();
/** Constructor
Default-constructed ranges have
zero elements.
@par Exception Safety
Throws nothing.
*/
range() noexcept;
/** Constructor
The new range references the
same underlying character buffer.
Ownership is not transferred; the
caller is responsible for ensuring
that the lifetime of the buffer
extends until it is no longer
referenced. The moved-from object
becomes as if default-constructed.
@par Exception Safety
Throws nothing.
*/
range(range&&) noexcept;
/** Constructor
The copy references the same
underlying character buffer.
Ownership is not transferred; the
caller is responsible for ensuring
that the lifetime of the buffer
extends until it is no longer
referenced.
@par Exception Safety
Throws nothing.
*/
range(range const&) noexcept;
/** Assignment
After the move, this references the
same underlying character buffer. Ownership
is not transferred; the caller is responsible
for ensuring that the lifetime of the buffer
extends until it is no longer referenced.
The moved-from object becomes as if
default-constructed.
@par Exception Safety
Throws nothing.
@return `*this`
*/
range&
operator=(range&&) noexcept;
/** Assignment
The copy references the same
underlying character buffer.
Ownership is not transferred; the
caller is responsible for ensuring
that the lifetime of the buffer
extends until it is no longer
referenced.
@par Exception Safety
Throws nothing.
@return `*this`
*/
range&
operator=(range const&) noexcept;
/** Return an iterator to the beginning
@return An iterator to the first element
*/
iterator begin() const noexcept;
/** Return an iterator to the end
@return An iterator to one past the last element
*/
iterator end() const noexcept;
/** Return true if the range is empty
@return `true` if the range is empty
*/
bool
empty() const noexcept
{
return n_ == 0;
}
/** Return the number of elements in the range
@return The number of elements
*/
std::size_t
size() const noexcept
{
return n_;
}
/** Return the matching part of the string
@return A string view representing the range
*/
core::string_view
string() const noexcept
{
return s_;
}
};
//------------------------------------------------
namespace implementation_defined {
template<
class R0,
class R1 = void>
struct range_rule_t;
}
//------------------------------------------------
namespace implementation_defined {
template<class R>
struct range_rule_t<R>
{
using value_type =
range<typename R::value_type>;
BOOST_URL_CXX20_CONSTEXPR
system::result<value_type>
parse(
char const*& it,
char const* end) const;
constexpr
range_rule_t(
R const& next,
std::size_t N,
std::size_t M) noexcept
: next_(next)
, N_(N)
, M_(M)
{
}
private:
R const next_;
std::size_t N_;
std::size_t M_;
};
} // implementation_defined
/** Match a repeating number of elements
Elements are matched using the passed rule.
<br>
Normally when the rule returns an error,
the range ends and the input is rewound to
one past the last character that matched
successfully. However, if the rule returns
the special value @ref error::end_of_range, the
input is not rewound. This allows for rules
which consume input without producing
elements in the range. For example, to
relax the grammar for a comma-delimited
list by allowing extra commas in between
elements.
@par Value Type
@code
using value_type = range< typename Rule::value_type >;
@endcode
@par Example
Rules are used with the function @ref parse.
@code
// range = 1*( ";" token )
system::result< range<core::string_view> > rv = parse( ";alpha;xray;charlie",
range_rule(
tuple_rule(
squelch( delim_rule( ';' ) ),
token_rule( alpha_chars ) ),
1 ) );
@endcode
@par BNF
@code
range = <N>*<M>next
@endcode
@par Specification
@li <a href="https://datatracker.ietf.org/doc/html/rfc5234#section-3.6"
>3.6. Variable Repetition (rfc5234)</a>
@param next The rule to use for matching
each element. The range extends until this
rule returns an error.
@param N The minimum number of elements for
the range to be valid. If omitted, this
defaults to zero.
@param M The maximum number of elements for
the range to be valid. If omitted, this
defaults to unlimited.
@return A rule that matches the range.
@see
@ref alpha_chars,
@ref delim_rule,
@ref error::end_of_range,
@ref parse,
@ref range,
@ref tuple_rule,
@ref squelch.
*/
template<BOOST_URL_CONSTRAINT(Rule) R>
constexpr
implementation_defined::range_rule_t<R>
range_rule(
R const& next,
std::size_t N = 0,
std::size_t M =
std::size_t(-1)) noexcept
{
// If you get a compile error here it
// means that your rule does not meet
// the type requirements. Please check
// the documentation.
static_assert(
is_rule<R>::value,
"Rule requirements not met");
return implementation_defined::range_rule_t<R>{
next, N, M};
}
//------------------------------------------------
namespace implementation_defined {
template<class R0, class R1>
struct range_rule_t
{
using value_type =
range<typename R0::value_type>;
BOOST_URL_CXX20_CONSTEXPR
system::result<value_type>
parse(
char const*& it,
char const* end) const;
constexpr
range_rule_t(
R0 const& first,
R1 const& next,
std::size_t N,
std::size_t M) noexcept
: first_(first)
, next_(next)
, N_(N)
, M_(M)
{
}
private:
R0 const first_;
R1 const next_;
std::size_t N_;
std::size_t M_;
};
} // implementation_defined
/** Match a repeating number of elements
Two rules are used for match. The rule
`first` is used for matching the first
element, while the `next` rule is used
to match every subsequent element.
<br>
Normally when the rule returns an error,
the range ends and the input is rewound to
one past the last character that matched
successfully. However, if the rule returns
the special value @ref error::end_of_range, the
input is not rewound. This allows for rules
which consume input without producing
elements in the range. For example, to
relax the grammar for a comma-delimited
list by allowing extra commas in between
elements.
@par Value Type
@code
using value_type = range< typename Rule::value_type >;
@endcode
@par Example
Rules are used with the function @ref parse.
@code
// range = [ token ] *( "," token )
system::result< range< core::string_view > > rv = parse( "whiskey,tango,foxtrot",
range_rule(
token_rule( alpha_chars ), // first
tuple_rule( // next
squelch( delim_rule(',') ),
token_rule( alpha_chars ) ) ) );
@endcode
@par BNF
@code
range = <1>*<1>first
/ first <N-1>*<M-1>next
@endcode
@par Specification
@li <a href="https://datatracker.ietf.org/doc/html/rfc5234#section-3.6"
>3.6. Variable Repetition (rfc5234)</a>
@param first The rule to use for matching
the first element. If this rule returns
an error, the range is empty.
@param next The rule to use for matching
each subsequent element. The range extends
until this rule returns an error.
@param N The minimum number of elements for
the range to be valid. If omitted, this
defaults to zero.
@param M The maximum number of elements for
the range to be valid. If omitted, this
defaults to unlimited.
@return A rule that matches the range.
@see
@ref alpha_chars,
@ref delim_rule,
@ref error::end_of_range,
@ref parse,
@ref range,
@ref tuple_rule,
@ref squelch.
*/
template<
BOOST_URL_CONSTRAINT(Rule) R1,
BOOST_URL_CONSTRAINT(Rule) R2>
constexpr
auto
range_rule(
R1 const& first,
R2 const& next,
std::size_t N = 0,
std::size_t M =
std::size_t(-1)) noexcept ->
#if 1
typename std::enable_if<
! std::is_integral<R2>::value,
implementation_defined::range_rule_t<R1, R2>>::type
#else
range_rule_t<R1, R2>
#endif
{
// If you get a compile error here it
// means that your rule does not meet
// the type requirements. Please check
// the documentation.
static_assert(
is_rule<R1>::value,
"Rule requirements not met");
static_assert(
is_rule<R2>::value,
"Rule requirements not met");
// If you get a compile error here it
// means that your rules do not have
// the exact same value_type. Please
// check the documentation.
static_assert(
std::is_same<
typename R1::value_type,
typename R2::value_type>::value,
"Rule requirements not met");
return implementation_defined::range_rule_t<R1, R2>{
first, next, N, M};
}
} // grammar
} // urls
} // boost
#include <boost/url/grammar/impl/range_rule.hpp>
#endif