Skip to content

Commit 927f35f

Browse files
committed
Provide a way to specify radix, and min/max digits for {u,}int_parser, without
using the template parameters directly, since this also requires the user to type parser_interface. Fixes #220.
1 parent 87617fd commit 927f35f

File tree

6 files changed

+160
-22
lines changed

6 files changed

+160
-22
lines changed

doc/tables.qbk

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ the input they match unless otherwise stated in the table below.]
230230
[[ _ui_ ]
231231
[ Matches an unsigned integral value. ]
232232
[ `unsigned int` ]
233-
[]]
233+
[ To specify a base/radix of `N`, use _ui_`.base<N>()`. To specify exactly `D` digits, use _ui_`.digits<D>()`. To specify a minimum of `LO` digits and a maximum of `HI` digits, use _ui_`.digits<LO, HI>()`. These calls can be chained, as in _ui_`.base<2>().digits<8>()`. ]]
234234

235235
[[ `_ui_(arg0)` ]
236236
[ Matches exactly the unsigned integral value `_RES_np_(arg0)`. ]
@@ -270,7 +270,7 @@ the input they match unless otherwise stated in the table below.]
270270
[[ _i_ ]
271271
[ Matches a signed integral value. ]
272272
[ `int` ]
273-
[]]
273+
[ To specify a base/radix of `N`, use _i_`.base<N>()`. To specify exactly `D` digits, use _i_`.digits<D>()`. To specify a minimum of `LO` digits and a maximum of `HI` digits, use _i_`.digits<LO, HI>()`. These calls can be chained, as in _i_`.base<2>().digits<8>()`. ]]
274274

275275
[[ `_i_(arg0)` ]
276276
[ Matches exactly the signed integral value `_RES_np_(arg0)`. ]

doc/tutorial.qbk

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1248,22 +1248,25 @@ these parsers is in a subsequent section. The attributes are repeated here so
12481248
you can use see all the properties of the parsers in one place.]
12491249

12501250
If you have an integral type `IntType` that is not covered by any of the
1251-
_Parser_ parsers, you can use a more verbose declaration to declare a parser
1252-
for `IntType`. If `IntType` were unsigned, you would use `uint_parser`. If
1253-
it were signed, you would use `int_parser`. For example:
1251+
_Parser_ parsers, you can explicitly specify a base/radix or bounds on the
1252+
number of digits. You do this by calling the `base()` and `digits()` member
1253+
functions on an existing parser of the right intgral type. So if `IntType`
1254+
were unsigned, you would use `uint_`. If it were signed, you would use
1255+
`int_`. For example:
12541256

1255-
constexpr parser_interface<int_parser<IntType>> hex_int;
1257+
constexpr auto hex_int = bp::uint_.base<16>();
12561258

1257-
`uint_parser` and `int_parser` accept three more non-type template parameters
1258-
after the type parameter. They are `Radix`, `MinDigits`, and `MaxDigits`.
1259-
`Radix` defaults to `10`, `MinDigits` to `1`, and `MaxDigits` to `-1`, which
1260-
is a sentinel value meaning that there is no max number of digits.
1259+
You simply chain together the contraints you want to use, like
1260+
`.base<16>().digits<2>()` or .digits<4>().base<8>()`.
12611261

12621262
So, if you wanted to parse exactly eight hexadecimal digits in a row in order
12631263
to recognize Unicode character literals like C++ has (e.g. `\Udeadbeef`), you
12641264
could use this parser for the digits at the end:
12651265

1266-
constexpr parser_interface<uint_parser<unsigned int, 16, 8, 8>> hex_int;
1266+
constexpr auto hex_4_def = bp::uint_.base<16>().digits<8>();
1267+
1268+
If you want to specify an acceptable range of digits, use `.digits<LO, HI>()`.
1269+
Both `HI` and `LO` are inclusive bounds.
12671270

12681271
[endsect]
12691272

example/callback_json.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ namespace json {
126126
}
127127
};
128128

129-
bp::parser_interface<bp::uint_parser<uint32_t, 16, 4, 4>> const hex_4_def;
129+
auto const hex_4_def = boost::parser::uint_.base<16>().digits<4>();
130130

131131
auto const escape_seq_def = "\\u" > hex_4;
132132

example/json.cpp

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -151,12 +151,10 @@ namespace json {
151151
}
152152
};
153153

154-
// This is the verbose form of declaration for the integer and unsigned
155-
// integer parsers int_parser and uint_parser. In this case, we don't
156-
// want to use boost::parser::hex directly, since it has a variable number
157-
// of digits. We want to match exactly 4 digits, and this is how we
158-
// declare a hexadecimal parser that matches exactly 4.
159-
bp::parser_interface<bp::uint_parser<uint32_t, 16, 4, 4>> const hex_4_def;
154+
// We don't want to use boost::parser::hex directly, since it has a
155+
// variable number of digits. We want to match exactly 4 digits, and this
156+
// is how we declare a hexadecimal parser that matches exactly 4.
157+
auto const hex_4_def = boost::parser::uint_.base<16>().digits<4>();
160158

161159
// We use > here instead of >>, because once we see \u, we know that
162160
// exactly four hex digits must follow -- no other production rule starts

include/boost/parser/parser.hpp

Lines changed: 139 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5421,6 +5421,29 @@ namespace boost { namespace parser {
54215421

54225422
#endif
54235423

5424+
namespace detail {
5425+
template<typename T>
5426+
using base_member_function_template_expr =
5427+
decltype(std::declval<T>().template base<2>());
5428+
template<typename T>
5429+
constexpr bool has_base_member_function_template_v =
5430+
is_detected_v<base_member_function_template_expr, T>;
5431+
5432+
template<typename T>
5433+
using has_digits1_member_function_template_expr =
5434+
decltype(std::declval<T>().template digits<1>());
5435+
template<typename T>
5436+
constexpr bool has_digits1_member_function_template_v =
5437+
is_detected_v<has_digits1_member_function_template_expr, T>;
5438+
5439+
template<typename T>
5440+
using has_digits2_member_function_template_expr =
5441+
decltype(std::declval<T>().template digits<1, 2>());
5442+
template<typename T>
5443+
constexpr bool has_digits2_member_function_template_v =
5444+
is_detected_v<has_digits2_member_function_template_expr, T>;
5445+
}
5446+
54245447
// Parser interface.
54255448

54265449
template<typename Parser, typename GlobalState, typename ErrorHandler>
@@ -5760,7 +5783,7 @@ namespace boost { namespace parser {
57605783
return parser_.call(first, last, context, skip, flags, success);
57615784
}
57625785

5763-
/** Applies `parser_`, assiging the parsed attribute, if any, to
5786+
/** Applies `parser_`, assinging the parsed attribute, if any, to
57645787
`attr`, unless the attribute is reported via callback. */
57655788
template<
57665789
typename Iter,
@@ -5780,6 +5803,60 @@ namespace boost { namespace parser {
57805803
parser_.call(first, last, context, skip, flags, success, attr);
57815804
}
57825805

5806+
/** Returns a new `parser_interface` constructed from
5807+
`parser_.base<Radix2>()`. Note that this only works for integral
5808+
numeric parsers like `int_` and `uint_`. */
5809+
template<int Radix2>
5810+
constexpr auto base() const noexcept
5811+
{
5812+
if constexpr (detail::has_base_member_function_template_v<
5813+
parser_type>) {
5814+
return parser::parser_interface{
5815+
parser_.template base<Radix2>()};
5816+
} else {
5817+
static_assert(
5818+
detail::has_base_member_function_template_v<parser_type>,
5819+
"Only certain parsers have a .base<>() member function. "
5820+
"This is not one of them.");
5821+
}
5822+
}
5823+
5824+
/** Returns a new `parser_interface` constructed from
5825+
`parser_.digits<Digits>()`. Note that this only works for
5826+
integral numeric parsers like `int_` and `uint_`. */
5827+
template<int Digits>
5828+
constexpr auto digits() const noexcept
5829+
{
5830+
if constexpr (detail::has_digits1_member_function_template_v<
5831+
parser_type>) {
5832+
return parser::parser_interface{
5833+
parser_.template digits<Digits>()};
5834+
} else {
5835+
static_assert(
5836+
detail::has_digits1_member_function_template_v<parser_type>,
5837+
"Only certain parsers have a .base<>() member function. "
5838+
"This is not one of them.");
5839+
}
5840+
}
5841+
5842+
/** Returns a new `parser_interface` constructed from
5843+
`parser_.digits<MinDigits2, MaxDigits2>()`. Note that this only
5844+
works for integral numeric parsers like `int_` and `uint_`. */
5845+
template<int MinDigits2, int MaxDigits2>
5846+
constexpr auto digits() const noexcept
5847+
{
5848+
if constexpr (detail::has_digits2_member_function_template_v<
5849+
parser_type>) {
5850+
return parser::parser_interface{
5851+
parser_.template digits<MinDigits2, MaxDigits2>()};
5852+
} else {
5853+
static_assert(
5854+
detail::has_digits2_member_function_template_v<parser_type>,
5855+
"Only certain parsers have a .base<>() member function. "
5856+
"This is not one of them.");
5857+
}
5858+
}
5859+
57835860
parser_type parser_;
57845861
global_state_type globals_;
57855862
error_handler_type error_handler_;
@@ -7926,7 +8003,11 @@ namespace boost { namespace parser {
79268003
typename Expected>
79278004
struct uint_parser
79288005
{
7929-
static_assert(2 <= Radix && Radix <= 36, "Unsupported radix.");
8006+
static_assert(
8007+
Radix == 2 || Radix == 8 || Radix == 10 || Radix == 16,
8008+
"Unsupported radix.");
8009+
static_assert(1 <= MinDigits);
8010+
static_assert(MaxDigits == -1 || MinDigits <= MaxDigits);
79308011

79318012
constexpr uint_parser() {}
79328013
explicit constexpr uint_parser(Expected expected) : expected_(expected)
@@ -7992,6 +8073,33 @@ namespace boost { namespace parser {
79928073
return parser_interface{parser_t{expected}};
79938074
}
79948075

8076+
/** Returns a `uint_parser` identical to `*this`, except that it
8077+
parses digits as base-`Radix2` instead of base-`Radix`. */
8078+
template<int Radix2>
8079+
constexpr auto base() const noexcept
8080+
{
8081+
return uint_parser<T, Radix2, MinDigits, MaxDigits, Expected>{
8082+
expected_};
8083+
}
8084+
8085+
/** Returns a `uint_parser` identical to `*this`, except that it only
8086+
accepts numbers exactly `Digits` digits. */
8087+
template<int Digits>
8088+
constexpr auto digits() const noexcept
8089+
{
8090+
return uint_parser<T, Radix, Digits, Digits, Expected>{expected_};
8091+
}
8092+
8093+
/** Returns a `uint_parser` identical to `*this`, except that it
8094+
only accepts numbers `D` digits long, where `D` is in
8095+
[`MinDigits2`, MaxDigits2`]. */
8096+
template<int MinDigits2, int MaxDigits2>
8097+
constexpr auto digits() const noexcept
8098+
{
8099+
return uint_parser<T, Radix, MinDigits2, MaxDigits2, Expected>{
8100+
expected_};
8101+
}
8102+
79958103
Expected expected_;
79968104
};
79978105

@@ -8039,6 +8147,8 @@ namespace boost { namespace parser {
80398147
static_assert(
80408148
Radix == 2 || Radix == 8 || Radix == 10 || Radix == 16,
80418149
"Unsupported radix.");
8150+
static_assert(1 <= MinDigits);
8151+
static_assert(MaxDigits == -1 || MinDigits <= MaxDigits);
80428152

80438153
constexpr int_parser() {}
80448154
explicit constexpr int_parser(Expected expected) : expected_(expected)
@@ -8104,6 +8214,33 @@ namespace boost { namespace parser {
81048214
return parser_interface{parser_t{expected}};
81058215
}
81068216

8217+
/** Returns an `int_parser` identical to `*this`, except that it
8218+
parses digits as base-`Radix2` instead of base-`Radix`. */
8219+
template<int Radix2>
8220+
constexpr auto base() const noexcept
8221+
{
8222+
return int_parser<T, Radix2, MinDigits, MaxDigits, Expected>{
8223+
expected_};
8224+
}
8225+
8226+
/** Returns an `int_parser` identical to `*this`, except that it only
8227+
accepts numbers exactly `Digits` digits. */
8228+
template<int Digits>
8229+
constexpr auto digits() const noexcept
8230+
{
8231+
return int_parser<T, Radix, Digits, Digits, Expected>{expected_};
8232+
}
8233+
8234+
/** Returns an `int_parser` identical to `*this`, except that it
8235+
only accepts numbers `D` digits long, where `D` is in
8236+
[`MinDigits2`, MaxDigits2`]. */
8237+
template<int MinDigits2, int MaxDigits2>
8238+
constexpr auto digits() const noexcept
8239+
{
8240+
return int_parser<T, Radix, MinDigits2, MaxDigits2, Expected>{
8241+
expected_};
8242+
}
8243+
81078244
Expected expected_;
81088245
};
81098246

include/boost/parser/parser_fwd.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -404,8 +404,8 @@ namespace boost { namespace parser {
404404
and at most `MaxDigits`, producing an attribute of type `T`. Fails on
405405
any other input. The parse will also fail if `Expected` is anything
406406
but `detail::nope` (which it is by default), and the produced
407-
attribute is not equal to `expected_`. `Radix` must be in `[2,
408-
36]`. */
407+
attribute is not equal to `expected_`. `Radix` must be one of `2`,
408+
`8`, `10`, or `16`. */
409409
template<
410410
typename T,
411411
int Radix = 10,

0 commit comments

Comments
 (0)