diff --git a/doc/tables.qbk b/doc/tables.qbk index 4259711a..fff698d1 100644 --- a/doc/tables.qbk +++ b/doc/tables.qbk @@ -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()`. To specify exactly `D` digits, use _ui_`.digits()`. To specify a minimum of `LO` digits and a maximum of `HI` digits, use _ui_`.digits()`. 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()`. To specify exactly `D` digits, use _i_`.digits()`. To specify a minimum of `LO` digits and a maximum of `HI` digits, use _i_`.digits()`. These calls can be chained, as in _i_`.base<2>().digits<8>()`. ]] [[ `_i_(arg0)` ] [ Matches exactly the signed integral value `_RES_np_(arg0)`. ] diff --git a/doc/tutorial.qbk b/doc/tutorial.qbk index 1e1b8c67..7d61a4ce 100644 --- a/doc/tutorial.qbk +++ b/doc/tutorial.qbk @@ -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> 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> 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()`. +Both `HI` and `LO` are inclusive bounds. [endsect] diff --git a/example/callback_json.cpp b/example/callback_json.cpp index fbd166da..a5eb5749 100644 --- a/example/callback_json.cpp +++ b/example/callback_json.cpp @@ -126,7 +126,7 @@ namespace json { } }; - bp::parser_interface> const hex_4_def; + auto const hex_4_def = boost::parser::uint_.base<16>().digits<4>(); auto const escape_seq_def = "\\u" > hex_4; diff --git a/example/json.cpp b/example/json.cpp index 5cdc260c..750e8220 100644 --- a/example/json.cpp +++ b/example/json.cpp @@ -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> 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 diff --git a/include/boost/parser/parser.hpp b/include/boost/parser/parser.hpp index 3e18c0ab..4f3139c6 100644 --- a/include/boost/parser/parser.hpp +++ b/include/boost/parser/parser.hpp @@ -5421,6 +5421,29 @@ namespace boost { namespace parser { #endif + namespace detail { + template + using base_member_function_template_expr = + decltype(std::declval().template base<2>()); + template + constexpr bool has_base_member_function_template_v = + is_detected_v; + + template + using has_digits1_member_function_template_expr = + decltype(std::declval().template digits<1>()); + template + constexpr bool has_digits1_member_function_template_v = + is_detected_v; + + template + using has_digits2_member_function_template_expr = + decltype(std::declval().template digits<1, 2>()); + template + constexpr bool has_digits2_member_function_template_v = + is_detected_v; + } + // Parser interface. template @@ -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()`. Note that this only works for integral + numeric parsers like `int_` and `uint_`. */ + template + constexpr auto base() const noexcept + { + if constexpr (detail::has_base_member_function_template_v< + parser_type>) { + return parser::parser_interface{ + parser_.template base()}; + } else { + static_assert( + detail::has_base_member_function_template_v, + "Only certain parsers have a .base<>() member function. " + "This is not one of them."); + } + } + + /** Returns a new `parser_interface` constructed from + `parser_.digits()`. Note that this only works for + integral numeric parsers like `int_` and `uint_`. */ + template + constexpr auto digits() const noexcept + { + if constexpr (detail::has_digits1_member_function_template_v< + parser_type>) { + return parser::parser_interface{ + parser_.template digits()}; + } else { + static_assert( + detail::has_digits1_member_function_template_v, + "Only certain parsers have a .base<>() member function. " + "This is not one of them."); + } + } + + /** Returns a new `parser_interface` constructed from + `parser_.digits()`. Note that this only + works for integral numeric parsers like `int_` and `uint_`. */ + template + constexpr auto digits() const noexcept + { + if constexpr (detail::has_digits2_member_function_template_v< + parser_type>) { + return parser::parser_interface{ + parser_.template digits()}; + } else { + static_assert( + detail::has_digits2_member_function_template_v, + "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 + constexpr auto base() const noexcept + { + return uint_parser{ + expected_}; + } + + /** Returns a `uint_parser` identical to `*this`, except that it only + accepts numbers exactly `Digits` digits. */ + template + constexpr auto digits() const noexcept + { + return uint_parser{expected_}; + } + + /** Returns a `uint_parser` identical to `*this`, except that it + only accepts numbers `D` digits long, where `D` is in + [`MinDigits2`, MaxDigits2`]. */ + template + constexpr auto digits() const noexcept + { + return uint_parser{ + 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 + constexpr auto base() const noexcept + { + return int_parser{ + expected_}; + } + + /** Returns an `int_parser` identical to `*this`, except that it only + accepts numbers exactly `Digits` digits. */ + template + constexpr auto digits() const noexcept + { + return int_parser{expected_}; + } + + /** Returns an `int_parser` identical to `*this`, except that it + only accepts numbers `D` digits long, where `D` is in + [`MinDigits2`, MaxDigits2`]. */ + template + constexpr auto digits() const noexcept + { + return int_parser{ + expected_}; + } + Expected expected_; }; diff --git a/include/boost/parser/parser_fwd.hpp b/include/boost/parser/parser_fwd.hpp index 470de583..43c7eebb 100644 --- a/include/boost/parser/parser_fwd.hpp +++ b/include/boost/parser/parser_fwd.hpp @@ -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,