2
0
mirror of https://github.com/boostorg/parser.git synced 2026-01-19 04:22:13 +00:00

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.
This commit is contained in:
Zach Laine
2025-04-12 19:53:18 -05:00
parent 87617fdec0
commit 927f35f115
6 changed files with 160 additions and 22 deletions

View File

@@ -230,7 +230,7 @@ the input they match unless otherwise stated in the table below.]
[[ _ui_ ]
[ Matches an unsigned integral value. ]
[ `unsigned int` ]
[]]
[ 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>()`. ]]
[[ `_ui_(arg0)` ]
[ Matches exactly the unsigned integral value `_RES_np_(arg0)`. ]
@@ -270,7 +270,7 @@ the input they match unless otherwise stated in the table below.]
[[ _i_ ]
[ Matches a signed integral value. ]
[ `int` ]
[]]
[ 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>()`. ]]
[[ `_i_(arg0)` ]
[ Matches exactly the signed integral value `_RES_np_(arg0)`. ]

View File

@@ -1248,22 +1248,25 @@ these parsers is in a subsequent section. The attributes are repeated here so
you can use see all the properties of the parsers in one place.]
If you have an integral type `IntType` that is not covered by any of the
_Parser_ parsers, you can use a more verbose declaration to declare a parser
for `IntType`. If `IntType` were unsigned, you would use `uint_parser`. If
it were signed, you would use `int_parser`. For example:
_Parser_ parsers, you can explicitly specify a base/radix or bounds on the
number of digits. You do this by calling the `base()` and `digits()` member
functions on an existing parser of the right intgral type. So if `IntType`
were unsigned, you would use `uint_`. If it were signed, you would use
`int_`. For example:
constexpr parser_interface<int_parser<IntType>> hex_int;
constexpr auto hex_int = bp::uint_.base<16>();
`uint_parser` and `int_parser` accept three more non-type template parameters
after the type parameter. They are `Radix`, `MinDigits`, and `MaxDigits`.
`Radix` defaults to `10`, `MinDigits` to `1`, and `MaxDigits` to `-1`, which
is a sentinel value meaning that there is no max number of digits.
You simply chain together the contraints you want to use, like
`.base<16>().digits<2>()` or .digits<4>().base<8>()`.
So, if you wanted to parse exactly eight hexadecimal digits in a row in order
to recognize Unicode character literals like C++ has (e.g. `\Udeadbeef`), you
could use this parser for the digits at the end:
constexpr parser_interface<uint_parser<unsigned int, 16, 8, 8>> hex_int;
constexpr auto hex_4_def = bp::uint_.base<16>().digits<8>();
If you want to specify an acceptable range of digits, use `.digits<LO, HI>()`.
Both `HI` and `LO` are inclusive bounds.
[endsect]

View File

@@ -126,7 +126,7 @@ namespace json {
}
};
bp::parser_interface<bp::uint_parser<uint32_t, 16, 4, 4>> const hex_4_def;
auto const hex_4_def = boost::parser::uint_.base<16>().digits<4>();
auto const escape_seq_def = "\\u" > hex_4;

View File

@@ -151,12 +151,10 @@ namespace json {
}
};
// This is the verbose form of declaration for the integer and unsigned
// integer parsers int_parser and uint_parser. In this case, we don't
// want to use boost::parser::hex directly, since it has a variable number
// of digits. We want to match exactly 4 digits, and this is how we
// declare a hexadecimal parser that matches exactly 4.
bp::parser_interface<bp::uint_parser<uint32_t, 16, 4, 4>> const hex_4_def;
// We don't want to use boost::parser::hex directly, since it has a
// variable number of digits. We want to match exactly 4 digits, and this
// is how we declare a hexadecimal parser that matches exactly 4.
auto const hex_4_def = boost::parser::uint_.base<16>().digits<4>();
// We use > here instead of >>, because once we see \u, we know that
// exactly four hex digits must follow -- no other production rule starts

View File

@@ -5421,6 +5421,29 @@ namespace boost { namespace parser {
#endif
namespace detail {
template<typename T>
using base_member_function_template_expr =
decltype(std::declval<T>().template base<2>());
template<typename T>
constexpr bool has_base_member_function_template_v =
is_detected_v<base_member_function_template_expr, T>;
template<typename T>
using has_digits1_member_function_template_expr =
decltype(std::declval<T>().template digits<1>());
template<typename T>
constexpr bool has_digits1_member_function_template_v =
is_detected_v<has_digits1_member_function_template_expr, T>;
template<typename T>
using has_digits2_member_function_template_expr =
decltype(std::declval<T>().template digits<1, 2>());
template<typename T>
constexpr bool has_digits2_member_function_template_v =
is_detected_v<has_digits2_member_function_template_expr, T>;
}
// Parser interface.
template<typename Parser, typename GlobalState, typename ErrorHandler>
@@ -5760,7 +5783,7 @@ namespace boost { namespace parser {
return parser_.call(first, last, context, skip, flags, success);
}
/** Applies `parser_`, assiging the parsed attribute, if any, to
/** Applies `parser_`, assinging the parsed attribute, if any, to
`attr`, unless the attribute is reported via callback. */
template<
typename Iter,
@@ -5780,6 +5803,60 @@ namespace boost { namespace parser {
parser_.call(first, last, context, skip, flags, success, attr);
}
/** Returns a new `parser_interface` constructed from
`parser_.base<Radix2>()`. Note that this only works for integral
numeric parsers like `int_` and `uint_`. */
template<int Radix2>
constexpr auto base() const noexcept
{
if constexpr (detail::has_base_member_function_template_v<
parser_type>) {
return parser::parser_interface{
parser_.template base<Radix2>()};
} else {
static_assert(
detail::has_base_member_function_template_v<parser_type>,
"Only certain parsers have a .base<>() member function. "
"This is not one of them.");
}
}
/** Returns a new `parser_interface` constructed from
`parser_.digits<Digits>()`. Note that this only works for
integral numeric parsers like `int_` and `uint_`. */
template<int Digits>
constexpr auto digits() const noexcept
{
if constexpr (detail::has_digits1_member_function_template_v<
parser_type>) {
return parser::parser_interface{
parser_.template digits<Digits>()};
} else {
static_assert(
detail::has_digits1_member_function_template_v<parser_type>,
"Only certain parsers have a .base<>() member function. "
"This is not one of them.");
}
}
/** Returns a new `parser_interface` constructed from
`parser_.digits<MinDigits2, MaxDigits2>()`. Note that this only
works for integral numeric parsers like `int_` and `uint_`. */
template<int MinDigits2, int MaxDigits2>
constexpr auto digits() const noexcept
{
if constexpr (detail::has_digits2_member_function_template_v<
parser_type>) {
return parser::parser_interface{
parser_.template digits<MinDigits2, MaxDigits2>()};
} else {
static_assert(
detail::has_digits2_member_function_template_v<parser_type>,
"Only certain parsers have a .base<>() member function. "
"This is not one of them.");
}
}
parser_type parser_;
global_state_type globals_;
error_handler_type error_handler_;
@@ -7926,7 +8003,11 @@ namespace boost { namespace parser {
typename Expected>
struct uint_parser
{
static_assert(2 <= Radix && Radix <= 36, "Unsupported radix.");
static_assert(
Radix == 2 || Radix == 8 || Radix == 10 || Radix == 16,
"Unsupported radix.");
static_assert(1 <= MinDigits);
static_assert(MaxDigits == -1 || MinDigits <= MaxDigits);
constexpr uint_parser() {}
explicit constexpr uint_parser(Expected expected) : expected_(expected)
@@ -7992,6 +8073,33 @@ namespace boost { namespace parser {
return parser_interface{parser_t{expected}};
}
/** Returns a `uint_parser` identical to `*this`, except that it
parses digits as base-`Radix2` instead of base-`Radix`. */
template<int Radix2>
constexpr auto base() const noexcept
{
return uint_parser<T, Radix2, MinDigits, MaxDigits, Expected>{
expected_};
}
/** Returns a `uint_parser` identical to `*this`, except that it only
accepts numbers exactly `Digits` digits. */
template<int Digits>
constexpr auto digits() const noexcept
{
return uint_parser<T, Radix, Digits, Digits, Expected>{expected_};
}
/** Returns a `uint_parser` identical to `*this`, except that it
only accepts numbers `D` digits long, where `D` is in
[`MinDigits2`, MaxDigits2`]. */
template<int MinDigits2, int MaxDigits2>
constexpr auto digits() const noexcept
{
return uint_parser<T, Radix, MinDigits2, MaxDigits2, Expected>{
expected_};
}
Expected expected_;
};
@@ -8039,6 +8147,8 @@ namespace boost { namespace parser {
static_assert(
Radix == 2 || Radix == 8 || Radix == 10 || Radix == 16,
"Unsupported radix.");
static_assert(1 <= MinDigits);
static_assert(MaxDigits == -1 || MinDigits <= MaxDigits);
constexpr int_parser() {}
explicit constexpr int_parser(Expected expected) : expected_(expected)
@@ -8104,6 +8214,33 @@ namespace boost { namespace parser {
return parser_interface{parser_t{expected}};
}
/** Returns an `int_parser` identical to `*this`, except that it
parses digits as base-`Radix2` instead of base-`Radix`. */
template<int Radix2>
constexpr auto base() const noexcept
{
return int_parser<T, Radix2, MinDigits, MaxDigits, Expected>{
expected_};
}
/** Returns an `int_parser` identical to `*this`, except that it only
accepts numbers exactly `Digits` digits. */
template<int Digits>
constexpr auto digits() const noexcept
{
return int_parser<T, Radix, Digits, Digits, Expected>{expected_};
}
/** Returns an `int_parser` identical to `*this`, except that it
only accepts numbers `D` digits long, where `D` is in
[`MinDigits2`, MaxDigits2`]. */
template<int MinDigits2, int MaxDigits2>
constexpr auto digits() const noexcept
{
return int_parser<T, Radix, MinDigits2, MaxDigits2, Expected>{
expected_};
}
Expected expected_;
};

View File

@@ -404,8 +404,8 @@ namespace boost { namespace parser {
and at most `MaxDigits`, producing an attribute of type `T`. Fails on
any other input. The parse will also fail if `Expected` is anything
but `detail::nope` (which it is by default), and the produced
attribute is not equal to `expected_`. `Radix` must be in `[2,
36]`. */
attribute is not equal to `expected_`. `Radix` must be one of `2`,
`8`, `10`, or `16`. */
template<
typename T,
int Radix = 10,