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:
@@ -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)`. ]
|
||||
|
||||
@@ -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]
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_;
|
||||
};
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user