2
0
mirror of https://github.com/boostorg/parser.git synced 2026-01-20 16:52:13 +00:00

33 Commits

Author SHA1 Message Date
Zach Laine
c674e94c3d Don't reuse the attribute-generating path in rule_parser's out-param overload
of call.  Doing so was wiping out previous partial results, in cases like foo
>> bar, where foo produces a T, and bar is a rule that produces vector<T>.

Fixes #248.
2025-07-27 17:50:38 -05:00
Zach Laine
84ee288b02 Attempt to fix odd error in happy path of code submitted with issue 223. 2025-07-26 21:13:33 -05:00
Zach Laine
39faa9ddbe Pass the sentinel type as a template parameter to the iterator template in
project_view, as a workaround to the presence/absence of a disambiguating
template keyword in iterator's implementation.  Neither adding it nor temoving
it works for all builds.  Also, re-enable the C++17 MSVC 2022 Github build.

Fixes #252.
2025-07-26 21:12:37 -05:00
Zach Laine
b2927abc6c Disable C++17 on MSVC 2022 in Github CI. 2025-07-26 20:17:56 -05:00
Zach Laine
5d6d2f7b84 Add missing special case for parsing a sequence of optional<T>s, writing the
results into a sequence container of Ts.

Fixes #223.
2025-07-26 20:15:15 -05:00
Zach Laine
fd6c56df1b Publicize project_view::{interator,sentinel} in attempt to fix VS 2022 build. 2025-07-13 15:56:35 -05:00
Zach Laine
af41e6a7c2 Add missing template keyword disambiguator in attempt to fix VS 2022 build. 2025-07-13 15:34:21 -05:00
Zach Laine
0b93a586f1 Use an R-string instead of using so many backslashes in the quoted string
examples.

Fixes #239.
2025-07-12 16:08:53 -05:00
Zach Laine
ed9a06123b Comment out unused dont_assign param in second overload defined by
BOOST_PARSER_DEFINE_IMPL.

Fixes #237.
2025-07-12 15:15:52 -05:00
Zach Laine
8ff46f394a Spelling corrections in tutorial.
Fixes #238.
2025-07-12 15:13:20 -05:00
Zach Laine
8c9ad7bdb3 Document why there are no Spirit-style charater class parsers (alnum, punct,
etc.) in the Rationale section of the docs.

Fixes #224.
2025-07-12 15:01:46 -05:00
Adem Budak
d8abe8f29e Fix some typos on documentation 2025-07-12 14:46:28 -05:00
Zach Laine
810adb43f6 Use a move asssignment instead of a copy assignment when returning a result
via detail::make_parse_result().

Supercedes PR #247.
2025-07-12 14:42:47 -05:00
Zach Laine
5788fb6967 Add missing 'template ' after dot when naming a dependent template
instantiation.

Fixes #221.
2025-05-06 01:55:23 -05:00
Rene Rivera
ec7df8a0af Add support for modular build structure. 2025-05-06 01:54:32 -05:00
Zach Laine
a93a1d2647 Use detail::hl::make_tuple() instead of CTAD in test to fix build breakage on
some compilers.
2025-04-13 14:26:47 -05:00
Zach Laine
927f35f115 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.
2025-04-12 19:59:46 -05:00
Zach Laine
87617fdec0 std::tuple -> tuple in test to fix build with -DBUILD_WITH_HANA=true. 2025-04-12 19:33:11 -05:00
Zach Laine
ead639e630 Add missing cxxstd to meta/libraries.json. 2025-04-12 13:56:00 -05:00
Zach Laine
a3ca1193b2 Add error reporting when encountering unexpected (left over) code points at
the end of an otherwise-successful parse, when doing non-prefix parsing.
2025-03-30 16:06:41 -05:00
Zach Laine
07153117ff Doc copy editing.
Fixes #217.
2025-03-30 16:06:02 -05:00
Zach Laine
6414f99e04 Remove space from declaration of UDLs, because apparently it matters?!
Fixes #216.
2025-03-13 19:03:14 -05:00
Zach Laine
78bc141d5f Add doc example of unexpected combining sequence parsers.
Example is based on #215.
2025-03-01 16:34:22 -06:00
necessarily-equal
b253d9ca53 Add unicode symbols parser (#213)
* Add symb parser to handle unicode symbols

* Add documentation for symb

* Add tests for symb

* Fix typo in the documentation

---------

Contributed by: Antoine Fontaine <antoinefontaine@posteo.net>
2025-02-20 23:51:17 -06:00
Zach Laine
0a34acc42a Add new macro BOOST_PARSER_GCC that is defined in config.hpp only for real
(non-Clang-emulated) GCC builds; replace relevant uses of the __GNUC__ macro
with BOOST_PARSER_GCC.

See discussion in PR #211.
2025-02-20 23:38:09 -06:00
Zach Laine
56c81c0b57 Use gross pointer dereference expression to implement detail::whatever
converions operator, since 2/3 of the big three compilers reject the use of
declval() there.
2025-02-20 23:38:09 -06:00
Zach Laine
821d1d4c08 Fix longstanding mysterious ill-formedness in
detail::static_assert_merge_attributes.  It was down to unavailabilty of a
default ctor for certain parser types.
2025-02-20 23:38:09 -06:00
Zach Laine
57cdd78210 Properly sort the code point values in detail::char_set<punct_chars>.
Fixes #209.
2024-12-23 17:28:07 -06:00
Zach Laine
3993efb692 Update MacOS badges in README.md. 2024-12-20 20:02:44 -06:00
Zach Laine
74bc8fc1bb Add the delimiter(p)[] directive proper (missing from previous commit).
Fixes #162.
2024-12-20 20:01:16 -06:00
Zach Laine
b42b052df4 Add the delimiter(p)[] directive, whic allows you to introduce a delimiter
into the parse of a permutation parser.

Fixes #162.
2024-12-19 22:26:08 -06:00
Zach Laine
42c9d82419 Add an optional char parser to quoted_string_parser, so that it can be made
fully general.

Fixes #196.
2024-12-17 00:17:54 -06:00
Zach Laine
958ac38256 Note for the user that they must be aware of nonobvious C++ operator
precedence impact of expression evaluation.

Fixes #205.
2024-12-16 22:33:55 -06:00
28 changed files with 4365 additions and 288 deletions

View File

@@ -57,7 +57,7 @@ Master status:
[![Windows MSVC](https://github.com/tzlaine/parser/actions/workflows/windows.yml/badge.svg?branch=master)](https://github.com/tzlaine/parser/actions/workflows/windows.yml)
[![macos-12 - Clang 14](https://github.com/tzlaine/parser/actions/workflows/macos-12.yml/badge.svg?branch=master)](https://github.com/tzlaine/parser/actions/workflows/macos-12.yml)
[![macos-13 - Clang 14](https://github.com/tzlaine/parser/actions/workflows/macos-13.yml/badge.svg?branch=master)](https://github.com/tzlaine/parser/actions/workflows/macos-13.yml)
Develop status:
@@ -67,6 +67,6 @@ Develop status:
[![Windows MSVC](https://github.com/tzlaine/parser/actions/workflows/windows.yml/badge.svg?branch=develop)](https://github.com/tzlaine/parser/actions/workflows/windows.yml)
[![macos-12 - Clang 14](https://github.com/tzlaine/parser/actions/workflows/macos-12.yml/badge.svg?branch=develop)](https://github.com/tzlaine/parser/actions/workflows/macos-12.yml)
[![macos-13 - Clang 14](https://github.com/tzlaine/parser/actions/workflows/macos-13.yml/badge.svg?branch=develop)](https://github.com/tzlaine/parser/actions/workflows/macos-13.yml)
[![License](https://img.shields.io/badge/license-boost-brightgreen.svg)](LICENSE_1_0.txt)

24
build.jam Normal file
View File

@@ -0,0 +1,24 @@
# Copyright René Ferdinand Rivera Morell 2025
# 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)
require-b2 5.2 ;
constant boost_dependencies :
/boost/assert//boost_assert
/boost/charconv//boost_charconv
/boost/hana//boost_hana
/boost/type_index//boost_type_index ;
project /boost/parser
;
explicit
[ alias boost_parser : : :
: <library>$(boost_dependencies) <include>include ]
[ alias all : boost_parser test ]
;
call-if : boost-library parser
;

View File

@@ -39,7 +39,7 @@ rule run_doxygen ( files * : name : expand ? )
}
run_doxygen [ glob $(here)/../../../boost/parser/*.hpp : $(here)/../../../boost/parser/concepts.hpp ] : "Headers" ;
run_doxygen [ glob $(here)/../include/boost/parser/*.hpp : $(here)/../include/boost/parser/concepts.hpp ] : "Headers" ;
install images_standalone : [ glob *.png ] : <location>html/parser/img ;
explicit images_standalone ;

View File

@@ -110,6 +110,7 @@
[def _std_str_ `std::string`]
[def _std_strs_ `std::string`s]
[def _std_vec_char_ `std::vector<char>`]
[def _std_vec_char32_ `std::vector<char32_t>`]
@@ -201,6 +202,7 @@
[def _merge_ [globalref boost::parser::merge `merge[]`]]
[def _sep_ [globalref boost::parser::separate `separate[]`]]
[def _transform_ [globalref boost::parser::transform `transform(f)[]`]]
[def _delimiter_ [globalref boost::parser::delimiter `delimiter(p)[]`]]
[def _omit_np_ [globalref boost::parser::omit `omit`]]
[def _raw_np_ [globalref boost::parser::raw `raw`]]
@@ -211,11 +213,13 @@
[def _merge_np_ [globalref boost::parser::merge `merge`]]
[def _sep_np_ [globalref boost::parser::separate `separate`]]
[def _transform_np_ [globalref boost::parser::transform `transform`]]
[def _delimiter_np_ [globalref boost::parser::delimiter `delimiter`]]
[def _blank_ [globalref boost::parser::blank `blank`]]
[def _control_ [globalref boost::parser::control `control`]]
[def _digit_ [globalref boost::parser::digit `digit`]]
[def _punct_ [globalref boost::parser::punct `punct`]]
[def _symb_ [globalref boost::parser::symb `symb`]]
[def _hex_digit_ [globalref boost::parser::hex_digit `hex_digit`]]
[def _lower_ [globalref boost::parser::lower `lower`]]
[def _upper_ [globalref boost::parser::upper `upper`]]
@@ -239,6 +243,7 @@
[def _more_about_rules_ [link boost_parser.tutorial.more_about_rules More About Rules]]
[def _unicode_ [link boost_parser.tutorial.unicode_support Unicode Support]]
[def _concepts_ [link boost_parser.concepts Concepts]]
[def _seq_parser_example_ [link boost_parser.tutorial.attribute_generation.a_sequence_parser_attribute_example A sequence parser attribute example]]
[def _ex_json_ [link boost_parser.extended_examples.parsing_json Parsing JSON]]
[def _ex_cb_json_ [link boost_parser.extended_examples.parsing_json_with_callbacks Parsing JSON With Callbacks]]
[def _rationale_ [link boost_parser.rationale Rationale]]

2145
doc/parser_reference.xml Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -325,4 +325,24 @@ always equal to `A()` if the parser fails. It is equal to whatever the parser
sets it to _emdash_ or its previous value, if the parser does not mutate it
_emdash_ if the parse succeeds.
[heading There are no _Spirit_-style character class parsers]
_Spirit_ has these character class parsers that recognize the same set of
characters as the C standard library's character class functions. For
instance, _Spirit_'s `alnum` recognizes the characters recognized by
`std::isalnum()`, its `punct` recognizes the characters recognized by
`std::ispunct()`, etc.
The problem with this is that those `std::is*()` functions are badly broken.
They do not even work correctly for ASCII values. This is because they use
the C standard library's locale mechanism, which can be set to anything the
current platform supports, and can be set by any code anywhere in your
program; the locale is mutable global state. So, even if you use the default
"C locale in your program, if you link against a library that sets the locale
to something that breaks ASCII character recognition (an EBCDIC locale, for
instance), your program is now incorrect, regardless of the code you wrote.
For this reason, I firmly believe that no one, anywhere, should use those C
functions in production code, and I am not supporting their use via _Parser_.
[endsect]

View File

@@ -132,6 +132,11 @@ the input they match unless otherwise stated in the table below.]
[ The code point type in Unicode parsing, or `char` in non-Unicode parsing. See the entry for _ch_. ]
[]]
[[ `_symb_` ]
[ Matches a single symbol code point. ]
[ The code point type in Unicode parsing, or `char` in non-Unicode parsing. See the entry for _ch_. ]
[]]
[[ `_hex_digit_` ]
[ Matches a single hexidecimal digit code point. ]
[ The code point type in Unicode parsing, or `char` in non-Unicode parsing. See the entry for _ch_. ]
@@ -225,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)`. ]
@@ -265,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)`. ]
@@ -385,10 +390,10 @@ consume the input they match unless otherwise stated in the table below.]
[[`*p`] [ Parses using `p` repeatedly until `p` no longer matches; always matches. ] [`std::string` if `_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p)>`] [ Matching _e_ an unlimited number of times creates an infinite loop, which is undefined behavior in C++. _Parser_ will assert in debug mode when it encounters `*_e_` (this applies to unconditional _e_ only). ]]
[[`+p`] [ Parses using `p` repeatedly until `p` no longer matches; matches iff `p` matches at least once. ] [`std::string` if `_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p)>`] [ Matching _e_ an unlimited number of times creates an infinite loop, which is undefined behavior in C++. _Parser_ will assert in debug mode when it encounters `+_e_` (this applies to unconditional _e_ only). ]]
[[`-p`] [ Equivalent to `p | _e_`. ] [`std::optional<_ATTR_np_(p)>`] []]
[[`p1 >> p2`] [ Matches iff `p1` matches and then `p2` matches. ] [`_bp_tup_<_ATTR_np_(p1), _ATTR_np_(p2)>` (See note.)] [ `>>` is associative; `p1 >> p2 >> p3`, `(p1 >> p2) >> p3`, and `p1 >> (p2 >> p3)` are all equivalent. This attribute type only applies to the case where `p1` and `p2` both generate attributes; see _attr_gen_ for the full rules. ]]
[[`p1 >> p2`] [ Matches iff `p1` matches and then `p2` matches. ] [`_bp_tup_<_ATTR_np_(p1), _ATTR_np_(p2)>` (See note.)] [ `>>` is associative; `p1 >> p2 >> p3`, `(p1 >> p2) >> p3`, and `p1 >> (p2 >> p3)` are all equivalent. This attribute type only applies to the case where `p1` and `p2` both generate attributes; see _attr_gen_ for the full rules. Differs in precedence from `operator>`. ]]
[[`p >> c`] [ Equivalent to `p >> lit(c)`. ] [`_ATTR_np_(p)`] []]
[[`p >> r`] [ Equivalent to `p >> lit(r)`. ] [`_ATTR_np_(p)`] []]
[[`p1 > p2`] [ Matches iff `p1` matches and then `p2` matches. No back-tracking is allowed after `p1` matches; if `p1` matches but then `p2` does not, the top-level parse fails. ] [`_bp_tup_<_ATTR_np_(p1), _ATTR_np_(p2)>` (See note.)] [ `>` is associative; `p1 > p2 > p3`, `(p1 > p2) > p3`, and `p1 > (p2 > p3)` are all equivalent. This attribute type only applies to the case where `p1` and `p2` both generate attributes; see _attr_gen_ for the full rules. ]]
[[`p1 > p2`] [ Matches iff `p1` matches and then `p2` matches. No back-tracking is allowed after `p1` matches; if `p1` matches but then `p2` does not, the top-level parse fails. ] [`_bp_tup_<_ATTR_np_(p1), _ATTR_np_(p2)>` (See note.)] [ `>` is associative; `p1 > p2 > p3`, `(p1 > p2) > p3`, and `p1 > (p2 > p3)` are all equivalent. This attribute type only applies to the case where `p1` and `p2` both generate attributes; see _attr_gen_ for the full rules. Differs in precedence from `operator>>`. ]]
[[`p > c`] [ Equivalent to `p > lit(c)`. ] [`_ATTR_np_(p)`] []]
[[`p > r`] [ Equivalent to `p > lit(r)`. ] [`_ATTR_np_(p)`] []]
[[`p1 | p2`] [ Matches iff either `p1` matches or `p2` matches. ] [`std::variant<_ATTR_np_(p1), _ATTR_np_(p2)>` (See note.)] [ `|` is associative; `p1 | p2 | p3`, `(p1 | p2) | p3`, and `p1 | (p2 | p3)` are all equivalent. This attribute type only applies to the case where `p1` and `p2` both generate attributes, and where the attribute types are different; see _attr_gen_ for the full rules. ]]
@@ -424,6 +429,15 @@ because, for any parser `p`, `_e_ | p` is equivalent to _e_, since _e_ always
matches. This is not true for _e_ parameterized with a condition. For any
condition `cond`, `_e_(cond)` is allowed to appear anywhere within an
alternative parser.
[important The C++ operators `>` and `>>` have different precedences. This
will sometimes come up in warnings from your compiler. No matter how you do
or do not parenthesize chains of parsers separated by `>` and `>>`, the
resulting expression evaluates the same. Feel free to add parentheses if your
compiler complains. More broadly, keep the C++ operator precedence rules in
mind when writing your parsers _emdash_ the simplest thing to write may not
have your intended semantics. ]
]
[template table_attribute_generation
@@ -491,7 +505,7 @@ attribute type is `char32_t`:
static_assert(std::is_same_v<decltype(result), std::optional<char32_t>>));
The good news is that usually you don't parse characters individually. When
you parse with _ch_, you usually parse repetition of then, which will produce
you parse with _ch_, you usually parse repetition of them, which will produce
a _std_str_, regardless of whether you're in Unicode parsing mode or not. If
you do need to parse individual characters, and want to lock down their
attribute type, you can use _cp_ and/or _cu_ to enforce a non-polymorphic
@@ -537,7 +551,7 @@ tables below:
[[`p1 || p2`] [`_bp_tup_<_ATTR_np_(p1), _ATTR_np_(p2)>`]]
[[`p1 || p2 || p3`] [`_bp_tup_<_ATTR_np_(p1), _ATTR_np_(p2), _ATTR_np_(p3)>`]]
[[`p1 % p2`] [`std::string` if `_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p1)>`]]
[[`p1 % p2`] [`std::string` if `_ATTR_np_(p1)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p1)>`]]
[[`p[a]`] [None.]]

View File

@@ -65,7 +65,7 @@ subparsers does.
Finally, there is a /permutation parser/; it is created using `operator||`,
as in `p1 || p2 || p3`. A permutation parser tries to match all of its
subparsers to the input, in any order. So the parser `p1 || p2 || p3` is equivalent to `(p1 >> p2 >> p3) | (p1 >> p3 >> p2) | (p2 >> p1 >> p3) | (p2 >> p3 >> p1) | (p3 >> p1 >> p2) | (p3 >> p2 >> p1)`. Hopefully its terseness is self-explanatory. It matches the
subparsers to the input, in any order. So the parser `p1 || p2 || p3` is equivalent to `(p1 >> p2 >> p3) | (p1 >> p3 >> p2) | (p2 >> p1 >> p3) | (p2 >> p3 >> p1) | (p3 >> p1 >> p2) | (p3 >> p2 >> p1)`. Hopefully the advantage of its terseness is self-explanatory. It matches the
input iff all of its subparsers do, regardless of the order they match in.
_Parser_ parsers each have an /attribute/ associated with them, or explicitly
@@ -584,7 +584,7 @@ things:
* This rule object itself is called `doubles`.
* We've given `doubles` the diagnstic text `"doubles"` so that _Parser_ knows
* We've given `doubles` the diagnostic text `"doubles"` so that _Parser_ knows
how to refer to it when producing a trace of the parser during debugging.
Ok, so if `doubles` is a parser, what does it do? We define the rule's
@@ -828,7 +828,7 @@ the same character must be used on both sides.
[quoted_string_example_4]
Another common thing to do in a quoted string parser is to recognize escape
sequences. If you have simple escape sequencecs that do not require any real
sequences. If you have simple escape sequences that do not require any real
parsing, like say the simple escape sequences from C++, you can provide a
_symbols_ object as well. The template parameter `T` to _symbols_t_ must be
`char` or `char32_t`. You don't need to include the escaped backslash or the
@@ -836,6 +836,14 @@ escaped quote character, since those always work.
[quoted_string_example_5]
Additionally, with each of the forms shown above, you can optionally provide a
parser as a final argument, which will be used to parse each character inside
the quotes. You have to provide an actual full parser here; you cannot
provide a character or string literal. If you do not provide a character
parser, _ch_ is used.
[quoted_string_example_6]
[endsect]
[section Parsing In Detail]
@@ -1151,7 +1159,7 @@ erase and clear for the current parse, and another that applies only to
subsequent parses. The full set of operations can be found in the _symbols_
API docs.
[mpte There are two versions of each of the _symbols_ `*_for_next_parse()`
[note There are two versions of each of the _symbols_ `*_for_next_parse()`
functions _emdash_ one that takes a context, and one that does not. The one
with the context is meant to be used within a semantic action. The one
without the context is for use outside of any parse.]
@@ -1240,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 integral 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 constraints 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]
@@ -1275,7 +1286,7 @@ parsers; we won't say much about them here.
[heading Interaction with sequence, alternative, and permutation parsers]
Sequence, alternative, and permutation parsers do not nest in most cases.
(Let's consider just sequence parsers to keep thinkgs simple, but most of this
(Let's consider just sequence parsers to keep things simple, but most of this
logic applies to alternative parsers as well.) `a >> b >> c` is the same as
`(a >> b) >> c` and `a >> (b >> c)`, and they are each represented by a single
_seq_p_ with three subparsers, `a`, `b`, and `c`. However, if something
@@ -1442,6 +1453,24 @@ _merge_ and _sep_ create a copy of the given _seq_p_.
_transform_ creates a _xfm_p_.
[heading _delimiter_]
The _delimiter_np_ directive enables the use of a delimiter within a
permutation parser. It *only* applies to permutation parsers, just as _merge_
and _sep_ only apply to sequence parsers. Consider this permutation parser.
constexpr auto parser = bp::int_ || bp::string("foo") || bp::char_('g');
This will match all of: an integer, `"foo"`, and `'g'`, in any order (for
example, `"foo g 42"`). If you also want for those three elements to be
delimited by commas, you could write this parser instead.
constexpr auto delimited_parser =
bp::delimiter(bp::lit(','))[bp::int_ || bp::string("foo") || bp::char_('g')];
`delimited_parser` will parse the same elements as `parser`, but will also
require commas between the elements (as in `"foo, g, 42"`).
[endsect]
[section Combining Operations]
@@ -1590,6 +1619,71 @@ attribute becomes `T`.
[container_concept]
]
[heading A sequence parser attribute example]
Note that the application of `OP` is done in the style of a left-fold, and
is therefore greedy. This can lead to some non-obvious results. For example,
consider this program. Thanks to Duncan Paterson for this very nice example!
#include <boost/parser/parser.hpp>
#include <print>
namespace bp = boost::parser;
int main() {
const auto id_set_action = [](auto &ctx) {
const auto& [left, right] = _attr(ctx);
std::println("{} = {}", left, right);
};
const auto id_parser = bp::char_('a', 'z') > *bp::char_('a', 'z');
const auto id_set = (id_parser >> '=' >> id_parser)[id_set_action];
bp::parse("left=right", id_set);
return 0;
}
Perhaps surprisingly, this program prints `leftr = ight`! Why is this? This
happens because `id_parser` seems to impose structure, but does not. `id_set`
is exactly equivalent to this (comments added to clarify which parts are which
below).
const auto id_set = (
/*A*/ bp::char_('a', 'z') > /*B*/ *bp::char_('a', 'z') >>
/*C*/ '=' >>
/*D*/ bp::char_('a', 'z') > /*E*/ *bp::char_('a', 'z')
)[id_set_action];
As _Parser_ applies `OP` to this sequence parser, the individual steps are:
`A` and `B` get merged into a single _std_str_; `C` is ignored, since it
produces no attribute; and `D` gets merged into the _std_str_ formed earlier
by `A` and `B`; finally, we have `E`. `E` does not combine with `D`, as `D`
was already consumed. `E` also does not combine with the _std_str_ we formed
from `A`, `B`, and `D`, since we don't combine adjacent containers. In the
end, we have a 2-tuple of _std_strs_, in which the first element contains all
the characters parsed by `A`, `B`, and `D`, and in which the second element
contains all the characters parsed by `E`.
That's clearly not what we wanted here, though. How do we get a top-level
parser that would print `left = right`? We use a _r_. The parser used inside
a _r_ can never combine with any parser(s) outside the _r_. Instances of a
rule are inherently separate from all parsers with which they are used,
whether those parsers are _rs_ or non-_r_ parsers. So, consider a _r_
equivalent to the previous `id_parser` above.
namespace bp = boost::parser;
bp::rule<struct id_parser_tag, std::string> id_parser = "identifier";
auto const id_parser_def = bp::char_('a', 'z') > *bp::char_('a', 'z');
BOOST_PARSER_DEFINE_RULES(id_parser);
Later, we can use it just as we used the previous non-rule version.
const auto id_set = (id_parser >> '=' >> id_parser)[id_set_action];
This produces the results you might expect, since only the `bp::char_('a',
'z') > *bp::char_('a', 'z')` parser inside the `id_parser` _r_ is ever
eligible for combining via `OP`.
[heading Alternative parser attribute rules]
The rules for alternative parsers are much simpler. For an alternative parer
@@ -1601,7 +1695,7 @@ the following steps applied:
wrapped in a `std::optional`, like `std::optional<std::variant</*...*/>>`;
* duplicates in the `std::variant` template parameters `<T1, T2, ... Tn>` are
removed; every type that appears does so exacly once;
removed; every type that appears does so exactly once;
* if the attribute is `std::variant<T>` or `std::optional<std::variant<T>>`,
the attribute becomes instead `T` or `std::optional<T>`, respectively; and
@@ -2211,6 +2305,8 @@ common use cases for _rs_. Use a _r_ if you want to:
* fix the attribute type produced by a parser to something other than the
default;
* control the attributes generated by adjacent sequence parsers;
* create a parser that produces useful diagnostic text;
* create a recursive rule (more on this below);
@@ -2351,6 +2447,10 @@ action if:
The notion of "compatible" is defined in _p_api_.
[heading Controlling the attributes generated]
See the _seq_parser_example_ in the _attr_gen_ section for details.
[heading Creating a parser for better diagnostics]
Each _r_ has associated diagnostic text that _Parser_ can use for failures of
@@ -2393,8 +2493,8 @@ the earlier expectation:
]
Not nearly as nice. The problem is that the expectation is on `(value %
',')`. So, even thought we gave `value` reasonable dianostic text, we put the
text on the wrong thing. We can introduce a new rule to put the diagnstic
',')`. So, even thought we gave `value` reasonable diagnostic text, we put the
text on the wrong thing. We can introduce a new rule to put the diagnostic
text in the right place.
namespace bp = boost::parser;
@@ -2480,7 +2580,7 @@ Also, consider this rule:
bp::rule<struct ints_tag, std::vector<int>> ints = "ints";
auto const ints_def = bp::int_ >> ints | bp::eps;
What is the default attribute type for ints_def? It sure looks like
What is the default attribute type for `ints_def`? It sure looks like
`std::optional<std::vector<int>>`. Inside the evaluation of `ints`, _Parser_
must evaluate `ints_def`, and then produce a `std::vector<int>` _emdash_ the
return type of `ints` _emdash_ from it. How? How do you turn a
@@ -2488,7 +2588,7 @@ return type of `ints` _emdash_ from it. How? How do you turn a
seems obvious, but the metaprogramming that properly handles this simple
example and the general case is certainly beyond me.
_Parser_ has a specific semantic for what consitutes a recursive rule. Each
_Parser_ has a specific semantic for what constitutes a recursive rule. Each
rule has a tag type associated with it, and if _Parser_ enters a rule with a
certain tag `Tag`, and the currently-evaluating rule (if there is one) also
has the tag `Tag`, then rule instance being entered is considered to be a
@@ -2562,7 +2662,7 @@ semantics, is a lot easier to read, and is a lot less code.]
The _r_ template takes another template parameter we have not discussed yet.
You can pass a third parameter `LocalState` to _r_, which will be defaulted
csontructed by the _r_, and made available within semantic actions used in the
constructed by the _r_, and made available within semantic actions used in the
rule as `_locals_np_(ctx)`. This gives your rule some local state, if it
needs it. The type of `LocalState` can be anything regular. It could be a
single value, a struct containing multiple values, or a tuple, among others.
@@ -3302,9 +3402,9 @@ _w_eh_ (see _p_api_). If you do not set one, _default_eh_ will be used.
[heading How diagnostics are generated]
_Parser_ only generates error messages like the ones in this page at failed
expectation points, like `a > b`, where you have successfully parsed `a`, but
then cannot successfully parse `b`. This may seem limited to you. It's
actually the best that we can do.
expectation points (like `a > b`, where you have successfully parsed `a`, but
then cannot successfully parse `b`), and at an unexpected end of input. This
may seem limited to you. It's actually the best that we can do.
In order for error handling to happen other than at expectation points, we
have to know that there is no further processing that might take place. This
@@ -3312,21 +3412,26 @@ is true because _Parser_ has `P1 | P2 | ... | Pn` parsers ("`or_parser`s").
If any one of these parsers `Pi` fails to match, it is not allowed to fail the
parse _emdash_ the next one (`Pi+1`) might match. If we get to the end of the
alternatives of the or_parser and `Pn` fails, we still cannot fail the
top-level parse, because the `or_parser` might be a subparser within a parent
`or_parser`.
top-level parse, because this `or_parser` might be a subparser within a parent
`or_parser`. The only exception to this is when: we have finished the
top-level parse; the top-level parse is *not* a prefix parse; and there is
still a part of the input range that is left over. In that case, there is an
implicit expectation that the end of the parse and the end of input are the
same location, and this implicit expectation has just been violated.
Ok, so what might we do? Perhaps we could at least indicate when we ran into
end-of-input. But we cannot, for exactly the same reason already stated. For
any parser `P`, reaching end-of-input is a failure for `P`, but not
necessarily for the whole parse.
Note that we cannot fail the top-level parse when we run into end-of-input.
We cannot for exactly the same reason already stated. For any parser `P`,
reaching end-of-input is a failure for `P`, but not necessarily for the whole
parse.
Perhaps we could record the farthest point ever reached during the parse, and
report that at the top level, if the top level parser fails. That would be
little help without knowing which parser was active when we reached that
point. This would require some sort of repeated memory allocation, since in
_Parser_ the progress point of the parser is stored exclusively on the stack
_emdash_ by the time we fail the top-level parse, all those far-reaching stack
frames are long gone. Not the best.
Ok, so what other kinds of error reporting might we do? Perhaps we could
record the farthest point ever reached during the parse, and report that at
the top level, if the top level parser fails. That would be little help
without knowing which parser was active when we reached that point. This
would require some sort of repeated memory allocation, since in _Parser_ the
progress point of the parser is stored exclusively on the stack _emdash_ by
the time we fail the top-level parse, all those far-reaching stack frames are
long gone. Not the best.
Worse still, knowing how far you got in the parse and which parser was active
is not very useful. Consider this.
@@ -3343,15 +3448,16 @@ Was the error in the input putting the `'a'` at the beginning or putting the
failed, and never mention `c_b`, you are potentially just steering them in the
wrong direction.
All error messages must come from failed expectation points. Consider parsing
JSON. If you open a list with `'['`, you know that you're parsing a list, and
if the list is ill-formed, you'll get an error message saying so. If you open
an object with `'{'`, the same thing is possible _emdash_ when missing the
matching `'}'`, you can tell the user, "That's not an object", and this is
useful feedback. The same thing with a partially parsed number, etc. If the
JSON parser does not build in expectations like matched braces and brackets,
how can _Parser_ know that a missing `'}'` is really a problem, and that no
later parser will match the input even without the `'}'`?
All error messages must come from failed expectation points (or unexpected end
of input). Consider parsing JSON. If you open a list with `'['`, you know
that you're parsing a list, and if the list is ill-formed, you'll get an error
message saying so. If you open an object with `'{'`, the same thing is
possible _emdash_ when missing the matching `'}'`, you can tell the user,
"That's not an object", and this is useful feedback. The same thing with a
partially parsed number, etc. If the JSON parser does not build in
expectations like matched braces and brackets, how can _Parser_ know that a
missing `'}'` is really a problem, and that no later parser will match the
input even without the `'}'`?
[important The bottom line is that you should build expectation points into
your parsers using `operator>` as much as possible.]
@@ -3454,7 +3560,7 @@ We just define a `logging_error_handler`, and pass it by reference to _w_eh_,
which decorates the top-level parser with the error handler. We *could not*
have written `bp::with_error_handler(parser,
logging_error_handler("parse.log"))`, because _w_eh_ does not accept rvalues.
This is becuse the error handler eventually goes into the parse context. The
This is because the error handler eventually goes into the parse context. The
parse context only stores pointers and iterators, keeping it cheap to copy.
If we run the example and give it the input `"1,"`, this shows up in the log
@@ -3502,7 +3608,7 @@ to `_trace_::off`.
If we trace a substantial parser, we will see a *lot* of output. Each code
point of the input must be considered, one at a time, to see if a certain rule
matches. An an example, let's trace a parse using the JSON parser from
matches. As an example, let's trace a parse using the JSON parser from
_ex_json_. The input is `"null"`. `null` is one of the types that a
Javascript value can have; the top-level parser in the JSON parser example is:
@@ -3689,7 +3795,7 @@ _Parser_ seldom allocates memory. The exceptions to this are:
which implies allocation. You can avoid this allocation by explicitly using
a different sequence container for the attribute that does not allocate.
`boost::container::static_vector` or C++26's `std::inplace_vector` may be
useful as such replacements.
useful for such replacements.
With the exception of allocating the name of the parser that was expected in a
failed expectation situation, _Parser_ does not does not allocate unless you
@@ -3766,9 +3872,9 @@ Some things to note:
want to know how to fix their input. For either rule, the fix is the same:
put a hexadecimal escape sequence there.
- `single_escaped_char` has a terrible-looking name. However, it's not really
used as a name anywhere per se. In error messages, it works nicely, though.
The error will be "Expected '"', '\', '/', 'b', 'f', 'n', 'r', or 't' here",
- `single_escaped_char` has a terrible-looking name. However, it's not
actually used as a name. In error messages, it works nicely, though. The
error will be "Expected '"', '\', '/', 'b', 'f', 'n', 'r', or 't' here",
which is pretty helpful.
[heading Have a simple test that you can run to find ill-formed-code-as-asserts]

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

@@ -73,6 +73,12 @@
#endif
// Follows logic in boost/config/detail/select_compiler_config.hpp.
#if defined(__clang__) && !defined(__ibmxl__) && !defined(__CODEGEARC__)
#elif defined(__GNUC__) && !defined(__ibmxl__)
#define BOOST_PARSER_GCC
#endif
#if defined(__cpp_lib_constexpr_algorithms)
# define BOOST_PARSER_ALGO_CONSTEXPR constexpr
#else

View File

@@ -73,10 +73,10 @@ namespace boost { namespace parser { namespace detail {
std::ostream & os,
int components = 0);
template<typename Context, typename ParserTuple>
template<typename Context, typename ParserTuple, typename DelimiterParser>
void print_parser(
Context const & context,
perm_parser<ParserTuple> const & parser,
perm_parser<ParserTuple, DelimiterParser> const & parser,
std::ostream & os,
int components = 0);
@@ -245,6 +245,13 @@ namespace boost { namespace parser { namespace detail {
std::ostream & os,
int components = 0);
template<typename Context>
void print_parser(
Context const & context,
char_set_parser<symb_chars> const & parser,
std::ostream & os,
int components = 0);
template<typename Context>
void print_parser(
Context const & context,
@@ -280,10 +287,14 @@ namespace boost { namespace parser { namespace detail {
std::ostream & os,
int components = 0);
template<typename Context, typename Quotes, typename Escapes>
template<
typename Context,
typename Quotes,
typename Escapes,
typename CharParser>
void print_parser(
Context const & context,
quoted_string_parser<Quotes, Escapes> const & parser,
quoted_string_parser<Quotes, Escapes, CharParser> const & parser,
std::ostream & os,
int components = 0);

View File

@@ -63,8 +63,9 @@ namespace boost { namespace parser { namespace detail {
struct n_aray_parser<or_parser<ParserTuple>> : std::true_type
{};
template<typename ParserTuple>
struct n_aray_parser<perm_parser<ParserTuple>> : std::true_type
template<typename ParserTuple, typename DelimiterParser>
struct n_aray_parser<perm_parser<ParserTuple, DelimiterParser>>
: std::true_type
{};
template<
@@ -206,15 +207,23 @@ namespace boost { namespace parser { namespace detail {
context, parser, os, components, " | ...", " | ");
}
template<typename Context, typename ParserTuple>
template<typename Context, typename ParserTuple, typename DelimiterParser>
void print_parser(
Context const & context,
perm_parser<ParserTuple> const & parser,
perm_parser<ParserTuple, DelimiterParser> const & parser,
std::ostream & os,
int components)
{
if constexpr (!is_nope_v<DelimiterParser>) {
os << "delimiter(";
detail::print_parser(
context, parser.delimiter_parser_, os, components);
os << ")[";
}
detail::print_or_like_parser(
context, parser, os, components, " || ...", " || ");
if constexpr (!is_nope_v<DelimiterParser>)
os << "]";
}
template<
@@ -627,6 +636,16 @@ namespace boost { namespace parser { namespace detail {
os << "punct";
}
template<typename Context>
void print_parser(
Context const & context,
char_set_parser<symb_chars> const & parser,
std::ostream & os,
int components)
{
os << "symb";
}
template<typename Context>
void print_parser(
Context const & context,
@@ -695,10 +714,14 @@ namespace boost { namespace parser { namespace detail {
os << "\"";
}
template<typename Context, typename Quotes, typename Escapes>
template<
typename Context,
typename Quotes,
typename Escapes,
typename CharParser>
void print_parser(
Context const & context,
quoted_string_parser<Quotes, Escapes> const & parser,
quoted_string_parser<Quotes, Escapes, CharParser> const & parser,
std::ostream & os,
int components)
{

View File

@@ -24,8 +24,9 @@
#define BOOST_PARSER_USE_CPP23_STD_RANGE_ADAPTOR_CLOSURE 0
#endif
#if !BOOST_PARSER_USE_CPP23_STD_RANGE_ADAPTOR_CLOSURE && \
BOOST_PARSER_DETAIL_STL_INTERFACES_USE_CONCEPTS && defined(__GNUC__) && 12 <= __GNUC__
#if !BOOST_PARSER_USE_CPP23_STD_RANGE_ADAPTOR_CLOSURE && \
BOOST_PARSER_DETAIL_STL_INTERFACES_USE_CONCEPTS && \
defined(BOOST_PARSER_GCC) && 12 <= __GNUC__
#define BOOST_PARSER_USE_LIBSTDCPP_GCC12_RANGE_ADAPTOR_CLOSURE 1
#else
#define BOOST_PARSER_USE_LIBSTDCPP_GCC12_RANGE_ADAPTOR_CLOSURE 0

View File

@@ -42,7 +42,8 @@ namespace boost::parser::detail::text::detail {
template<typename R>
constexpr bool view =
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS || \
(defined(__cpp_lib_concepts) && (!defined(__GNUC__) || 12 <= __GNUC__))
(defined(__cpp_lib_concepts) && \
(!defined(BOOST_PARSER_GCC) || 12 <= __GNUC__))
std::ranges::view<R>
#else
range_<R> && !container_<R> &&

View File

@@ -88,10 +88,12 @@ namespace boost::parser::detail { namespace text {
{
V base_ = V();
template<bool Const>
class iterator;
// HACK: SentType is here to work around irritating big-3
// implementation inconsistencies.
template<bool Const>
class sentinel;
template<bool Const, typename SentType = sentinel<Const>>
class iterator;
public:
constexpr project_view()
@@ -140,7 +142,7 @@ namespace boost::parser::detail { namespace text {
#else
template<typename V, typename F>
#endif
template<bool Const>
template<bool Const, typename SentType>
class project_view<V, F>::iterator
: public boost::parser::detail::stl_interfaces::proxy_iterator_interface<
iterator<Const>,
@@ -161,7 +163,7 @@ namespace boost::parser::detail { namespace text {
decltype(detail::function_for_tag<F>(0))
#endif
;
using sentinel = project_view<V, F>::sentinel<Const>;
using sentinel = SentType;
friend boost::parser::detail::stl_interfaces::access;
iterator_type & base_reference() noexcept { return it_; }
@@ -169,7 +171,7 @@ namespace boost::parser::detail { namespace text {
iterator_type it_ = iterator_type();
friend project_view<V, F>::sentinel<Const>;
friend project_view<V, F>::template sentinel<Const>;
template<bool OtherConst>
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS

File diff suppressed because it is too large Load Diff

View File

@@ -1015,8 +1015,8 @@ namespace boost { namespace parser {
template<typename T>
struct is_perm_p : std::false_type
{};
template<typename T>
struct is_perm_p<perm_parser<T>> : std::true_type
template<typename T, typename DelimiterParser>
struct is_perm_p<perm_parser<T, DelimiterParser>> : std::true_type
{};
template<typename T>
@@ -1548,7 +1548,7 @@ namespace boost { namespace parser {
{
std::optional<T> retval;
if (success)
retval = x;
retval = std::move(x);
return retval;
}
@@ -2715,20 +2715,28 @@ namespace boost { namespace parser {
}
}
template<typename I, typename S, typename T>
std::optional<T>
if_full_parse(I & first, S last, std::optional<T> retval)
template<typename I, typename S, typename ErrorHandler, typename T>
T if_full_parse(
I initial_first,
I & first,
S last,
ErrorHandler const & error_handler,
T retval)
{
if (first != last)
retval = std::nullopt;
return retval;
}
template<typename I, typename S>
bool if_full_parse(I & first, S last, bool retval)
{
if (first != last)
retval = false;
return retval;
if (first != last) {
if (retval && error_handler(
initial_first,
last,
parse_error<I>(first, "end of input")) ==
error_handler_result::rethrow) {
throw;
}
if constexpr (std::is_same_v<T, bool>)
retval = false;
else
retval = std::nullopt;
}
return std::move(retval);
}
// The notion of comaptibility is that, given a parser with the
@@ -2801,6 +2809,8 @@ namespace boost { namespace parser {
{
if constexpr (is_nope_v<ParserAttr>) {
return nope{};
} else if constexpr (is_optional_v<ParserAttr>) {
return ParserAttr{};
} else {
using value_type = range_value_t<GivenContainerAttr>;
return std::conditional_t<
@@ -3413,10 +3423,14 @@ namespace boost { namespace parser {
ParserTuple parsers_;
};
template<typename ParserTuple>
template<typename ParserTuple, typename DelimiterParser>
struct perm_parser
{
constexpr perm_parser(ParserTuple parsers) : parsers_(parsers) {}
constexpr perm_parser(
ParserTuple parsers, DelimiterParser delimiter_parser) :
parsers_(parsers), delimiter_parser_(delimiter_parser)
{}
#ifndef BOOST_PARSER_DOXYGEN
@@ -3643,7 +3657,19 @@ namespace boost { namespace parser {
};
// Use one of the previously-unused parsers to parse one
// alternative.
bool first_iteration = true;
auto parsed_one = [&](auto) {
if constexpr (!detail::is_nope_v<DelimiterParser>) {
if (!first_iteration) {
detail::skip(first, last, skip, flags);
bool local_success = true;
delimiter_parser_.call(
first, last, context, skip, flags, local_success);
if (!local_success)
return false;
}
first_iteration = false;
}
return (
parse_into(
Is,
@@ -3667,6 +3693,7 @@ namespace boost { namespace parser {
#endif
ParserTuple parsers_;
DelimiterParser delimiter_parser_;
};
namespace detail {
@@ -5384,9 +5411,28 @@ namespace boost { namespace parser {
if constexpr (CanUseCallbacks && Context::use_callbacks) {
call(first, last, context, skip, flags, success);
} else {
auto attr = call(first, last, context, skip, flags, success);
if (success)
detail::assign(retval, std::move(attr));
locals_type locals = detail::make_locals<locals_type>(context);
auto params = detail::resolve_rule_params(context, params_);
tag_type * const tag_ptr = nullptr;
auto const rule_context = detail::make_rule_context(
context, tag_ptr, retval, locals, params);
[[maybe_unused]] auto _ = detail::scoped_trace(
*this, first, last, rule_context, flags, retval);
bool dont_assign = false;
parse_rule(
tag_ptr,
first,
last,
rule_context,
skip,
flags,
success,
dont_assign,
retval);
if (!success || dont_assign)
retval = Attribute_();
}
}
@@ -5396,6 +5442,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>
@@ -5605,7 +5674,7 @@ namespace boost { namespace parser {
return rhs.parser_.prepend(*this);
} else {
return parser::parser_interface{
perm_parser<tuple<parser_type, ParserType2>>{
perm_parser<tuple<parser_type, ParserType2>, detail::nope>{
tuple<parser_type, ParserType2>{parser_, rhs.parser_}}};
}
}
@@ -5735,7 +5804,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,
@@ -5755,6 +5824,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_;
@@ -6046,7 +6169,7 @@ namespace boost { namespace parser {
SkipParser const & skip, \
boost::parser::detail::flags flags, \
bool & success, \
bool & dont_assign, \
bool & /*dont_assign*/, \
Attribute & retval) \
{ \
auto const & parser = BOOST_PARSER_PP_CAT(rule_name_, _def); \
@@ -6120,23 +6243,23 @@ namespace boost { namespace parser {
}
}
template<typename ParserTuple>
template<typename ParserTuple, typename DelimiterParser>
template<typename Parser>
constexpr auto perm_parser<ParserTuple>::prepend(
constexpr auto perm_parser<ParserTuple, DelimiterParser>::prepend(
parser_interface<Parser> parser) const noexcept
{
// If you're seeing this as a compile- or run-time failure, you've
// tried to put an eps parser in a permutation-parser, such as "eps ||
// int_".
BOOST_PARSER_ASSERT(!detail::is_eps_p<Parser>{});
return parser_interface{perm_parser<decltype(detail::hl::prepend(
parsers_, parser.parser_))>{
detail::hl::prepend(parsers_, parser.parser_)}};
return parser_interface{perm_parser<
decltype(detail::hl::prepend(parsers_, parser.parser_)),
detail::nope>{detail::hl::prepend(parsers_, parser.parser_)}};
}
template<typename ParserTuple>
template<typename ParserTuple, typename DelimiterParser>
template<typename Parser>
constexpr auto perm_parser<ParserTuple>::append(
constexpr auto perm_parser<ParserTuple, DelimiterParser>::append(
parser_interface<Parser> parser) const noexcept
{
// If you're seeing this as a compile- or run-time failure, you've
@@ -6144,13 +6267,14 @@ namespace boost { namespace parser {
// || eps".
BOOST_PARSER_ASSERT(!detail::is_eps_p<Parser>{});
if constexpr (detail::is_perm_p<Parser>{}) {
return parser_interface{perm_parser<decltype(detail::hl::concat(
parsers_, parser.parser_.parsers_))>{
return parser_interface{perm_parser<
decltype(detail::hl::concat(parsers_, parser.parser_.parsers_)),
detail::nope>{
detail::hl::concat(parsers_, parser.parser_.parsers_)}};
} else {
return parser_interface{perm_parser<decltype(detail::hl::append(
parsers_, parser.parser_))>{
detail::hl::append(parsers_, parser.parser_)}};
return parser_interface{perm_parser<
decltype(detail::hl::append(parsers_, parser.parser_)),
detail::nope>{detail::hl::append(parsers_, parser.parser_)}};
}
}
@@ -6305,6 +6429,36 @@ namespace boost { namespace parser {
return repeat_directive<MinType, MaxType>{min_, max_};
}
/** A directive that represents a `perm_parser`, where the items parsed
are delimited by `DelimiterParser`,
(e.g. `delimiter(delimter_parser)[some_perm_parser]`). This directive
only applies to `perm_parser`s. */
template<typename DelimiterParser>
struct delimiter_directive
{
template<typename ParserTuple, typename DelimiterParser2>
constexpr auto operator[](
parser_interface<perm_parser<ParserTuple, DelimiterParser2>> rhs)
const noexcept
{
using parser_type = perm_parser<ParserTuple, DelimiterParser>;
return parser_interface{
parser_type{rhs.parser_.parsers_, delimiter_parser_}};
}
DelimiterParser delimiter_parser_;
};
/** Returns a `delimiter_directive` whose `operator[]` returns a
`perm_parser`, where the items parsed are delimited by
`delimiter_parser`. */
template<typename DelimiterParser>
constexpr delimiter_directive<DelimiterParser>
delimiter(parser_interface<DelimiterParser> delimiter_parser) noexcept
{
return delimiter_directive<DelimiterParser>{delimiter_parser.parser_};
}
/** Represents a skip parser as a directive. When used without a skip
parser, e.g. `skip[parser_in_which_to_do_skipping]`, the skipper for
the entire parse is used. When given another parser, e.g.
@@ -7252,7 +7406,7 @@ namespace boost { namespace parser {
return parser_interface{string_parser(str)};
}
template<typename Quotes, typename Escapes>
template<typename Quotes, typename Escapes, typename CharParser>
struct quoted_string_parser
{
constexpr quoted_string_parser() : chs_(), ch_('"') {}
@@ -7265,7 +7419,11 @@ namespace boost { namespace parser {
typename Enable =
std::enable_if_t<detail::is_parsable_range_like_v<R>>>
#endif
constexpr quoted_string_parser(R && r) : chs_((R &&) r), ch_(0)
constexpr quoted_string_parser(
R && r,
parser_interface<CharParser> char_p =
parser_interface{CharParser()}) :
chs_((R &&)r), char_p_(char_p), ch_(0)
{
BOOST_PARSER_DEBUG_ASSERT(r.begin() != r.end());
}
@@ -7278,16 +7436,29 @@ namespace boost { namespace parser {
typename Enable =
std::enable_if_t<detail::is_parsable_range_like_v<R>>>
#endif
constexpr quoted_string_parser(R && r, Escapes escapes) :
chs_((R &&) r), escapes_(escapes), ch_(0)
constexpr quoted_string_parser(
R && r,
Escapes escapes,
parser_interface<CharParser> char_p =
parser_interface{CharParser()}) :
chs_((R &&)r), escapes_(escapes), char_p_(char_p), ch_(0)
{
BOOST_PARSER_DEBUG_ASSERT(r.begin() != r.end());
}
constexpr quoted_string_parser(char32_t cp) : chs_(), ch_(cp) {}
constexpr quoted_string_parser(
char32_t cp,
parser_interface<CharParser> char_p =
parser_interface{CharParser()}) :
chs_(), char_p_(char_p), ch_(cp)
{}
constexpr quoted_string_parser(char32_t cp, Escapes escapes) :
chs_(), escapes_(escapes), ch_(cp)
constexpr quoted_string_parser(
char32_t cp,
Escapes escapes,
parser_interface<CharParser> char_p =
parser_interface{CharParser()}) :
chs_(), escapes_(escapes), char_p_(char_p), ch_(cp)
{}
template<
@@ -7376,11 +7547,11 @@ namespace boost { namespace parser {
auto make_parser = [&]() {
if constexpr (detail::is_nope_v<Escapes>) {
return *((lit('\\') >> back_delim) |
(char_ - back_delim))[append] > ch;
(char_p_ - back_delim))[append] > ch;
} else {
return *((lit('\\') >> back_delim)[append] |
(lit('\\') >> parser_interface(escapes_))[append] |
(char_ - back_delim)[append]) > ch;
(char_p_ - back_delim)[append]) > ch;
}
};
@@ -7402,15 +7573,17 @@ namespace boost { namespace parser {
/** Returns a `parser_interface` containing a `quoted_string_parser`
that uses `x` as its quotation marks. */
#if BOOST_PARSER_USE_CONCEPTS
template<typename T>
template<typename T, typename Parser = char_parser<detail::nope>>
requires(!parsable_range_like<T>)
#else
template<
typename T,
typename Parser = char_parser<detail::nope>,
typename Enable =
std::enable_if_t<!detail::is_parsable_range_like_v<T>>>
#endif
constexpr auto operator()(T x) const noexcept
constexpr auto
operator()(T x, parser_interface<Parser> char_p = char_) const noexcept
{
if constexpr (!detail::is_nope_v<Quotes>) {
BOOST_PARSER_ASSERT(
@@ -7419,7 +7592,9 @@ namespace boost { namespace parser {
"quoted_string, like 'quoted_string('\"')('\\'')'. Quit "
"it!'"));
}
return parser_interface(quoted_string_parser(std::move(x)));
return parser_interface(
quoted_string_parser<detail::nope, detail::nope, Parser>(
std::move(x), char_p));
}
/** Returns a `parser_interface` containing a `quoted_string_parser`
@@ -7430,14 +7605,18 @@ namespace boost { namespace parser {
character begin matched is directly compared to the elements of
`r`. */
#if BOOST_PARSER_USE_CONCEPTS
template<parsable_range_like R>
template<
parsable_range_like R,
typename Parser = char_parser<detail::nope>>
#else
template<
typename R,
typename Parser = char_parser<detail::nope>,
typename Enable =
std::enable_if_t<detail::is_parsable_range_like_v<R>>>
#endif
constexpr auto operator()(R && r) const noexcept
constexpr auto operator()(
R && r, parser_interface<Parser> char_p = char_) const noexcept
{
BOOST_PARSER_ASSERT(((
!std::is_rvalue_reference_v<R &&> ||
@@ -7453,10 +7632,14 @@ namespace boost { namespace parser {
"'quoted_string(char-range)(char-range)'. Quit it!'"));
}
return parser_interface(
quoted_string_parser<decltype(BOOST_PARSER_SUBRANGE(
detail::make_view_begin(r), detail::make_view_end(r)))>(
quoted_string_parser<
decltype(BOOST_PARSER_SUBRANGE(
detail::make_view_begin(r), detail::make_view_end(r))),
detail::nope,
Parser>(
BOOST_PARSER_SUBRANGE(
detail::make_view_begin(r), detail::make_view_end(r))));
detail::make_view_begin(r), detail::make_view_end(r)),
char_p));
}
/** Returns a `parser_interface` containing a `quoted_string_parser`
@@ -7465,16 +7648,23 @@ namespace boost { namespace parser {
sequence, and what character(s) each escape sequence represents.
Note that `"\\"` and `"\ch"` are always valid escape sequences. */
#if BOOST_PARSER_USE_CONCEPTS
template<typename T, typename U>
template<
typename T,
typename U,
typename Parser = char_parser<detail::nope>>
requires(!parsable_range_like<T>)
#else
template<
typename T,
typename U,
typename Parser = char_parser<detail::nope>,
typename Enable =
std::enable_if_t<!detail::is_parsable_range_like_v<T>>>
#endif
auto operator()(T x, symbols<U> const & escapes) const noexcept
auto operator()(
T x,
symbols<U> const & escapes,
parser_interface<Parser> char_p = char_) const noexcept
{
if constexpr (!detail::is_nope_v<Quotes>) {
BOOST_PARSER_ASSERT(
@@ -7484,8 +7674,9 @@ namespace boost { namespace parser {
"it!'"));
}
auto symbols = symbol_parser(escapes.parser_);
auto parser = quoted_string_parser<detail::nope, decltype(symbols)>(
char32_t(x), symbols);
auto parser =
quoted_string_parser<detail::nope, decltype(symbols), Parser>(
char32_t(x), symbols, char_p);
return parser_interface(parser);
}
@@ -7500,15 +7691,22 @@ namespace boost { namespace parser {
escape sequence represents. Note that `"\\"` and `"\ch"` are
always valid escape sequences. */
#if BOOST_PARSER_USE_CONCEPTS
template<parsable_range_like R, typename T>
template<
parsable_range_like R,
typename T,
typename Parser = char_parser<detail::nope>>
#else
template<
typename R,
typename T,
typename Parser = char_parser<detail::nope>,
typename Enable =
std::enable_if_t<detail::is_parsable_range_like_v<R>>>
#endif
auto operator()(R && r, symbols<T> const & escapes) const noexcept
auto operator()(
R && r,
symbols<T> const & escapes,
parser_interface<Parser> char_p = char_) const noexcept
{
BOOST_PARSER_ASSERT(((
!std::is_rvalue_reference_v<R &&> ||
@@ -7526,14 +7724,16 @@ namespace boost { namespace parser {
auto symbols = symbol_parser(escapes.parser_);
auto quotes = BOOST_PARSER_SUBRANGE(
detail::make_view_begin(r), detail::make_view_end(r));
auto parser =
quoted_string_parser<decltype(quotes), decltype(symbols)>(
quotes, symbols);
auto parser = quoted_string_parser<
decltype(quotes),
decltype(symbols),
Parser>(quotes, symbols, char_p);
return parser_interface(parser);
}
Quotes chs_;
Escapes escapes_;
parser_interface<CharParser> char_p_;
char32_t ch_;
};
@@ -7717,12 +7917,18 @@ namespace boost { namespace parser {
control;
/** The punctuation character parser. Matches the full set of Unicode
punctuation clases (specifically, "Pc", "Pd", "Pe", "Pf", "Pi", "Ps",
punctuation classes (specifically, "Pc", "Pd", "Pe", "Pf", "Pi", "Ps",
and "Po"). */
inline BOOST_PARSER_ALGO_CONSTEXPR
parser_interface<char_set_parser<detail::punct_chars>>
punct;
/** The symbol character parser. Matches the full set of Unicode
symbol classes (specifically, "Sc", "Sk", "Sm", and "So"). */
inline BOOST_PARSER_ALGO_CONSTEXPR
parser_interface<char_set_parser<detail::symb_chars>>
symb;
/** The lower case character parser. Matches the full set of Unicode
lower case code points (class "Ll"). */
inline BOOST_PARSER_ALGO_CONSTEXPR
@@ -7730,7 +7936,7 @@ namespace boost { namespace parser {
lower;
/** The lower case character parser. Matches the full set of Unicode
lower case code points (class "Lu"). */
upper case code points (class "Lu"). */
inline BOOST_PARSER_ALGO_CONSTEXPR
parser_interface<char_set_parser<detail::upper_case_chars>>
upper;
@@ -7818,7 +8024,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)
@@ -7884,6 +8094,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_;
};
@@ -7931,6 +8168,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)
@@ -7996,6 +8235,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_;
};
@@ -8717,9 +8983,12 @@ namespace boost { namespace parser {
auto r_ = detail::make_input_subrange(r);
auto first = r_.begin();
auto const last = r_.end();
auto const initial_first = first;
return reset = detail::if_full_parse(
initial_first,
first,
last,
parser.error_handler_,
parser::prefix_parse(first, last, parser, attr, trace_mode));
}
@@ -8822,8 +9091,13 @@ namespace boost { namespace parser {
auto r_ = detail::make_input_subrange(r);
auto first = r_.begin();
auto const last = r_.end();
auto const initial_first = first;
return detail::if_full_parse(
first, last, parser::prefix_parse(first, last, parser, trace_mode));
initial_first,
first,
last,
parser.error_handler_,
parser::prefix_parse(first, last, parser, trace_mode));
}
/** Parses `[first, last)` using `parser`, skipping all input recognized
@@ -8958,9 +9232,12 @@ namespace boost { namespace parser {
auto r_ = detail::make_input_subrange(r);
auto first = r_.begin();
auto const last = r_.end();
auto const initial_first = first;
return reset = detail::if_full_parse(
initial_first,
first,
last,
parser.error_handler_,
parser::prefix_parse(
first, last, parser, skip, attr, trace_mode));
}
@@ -9069,9 +9346,12 @@ namespace boost { namespace parser {
auto r_ = detail::make_input_subrange(r);
auto first = r_.begin();
auto const last = r_.end();
auto const initial_first = first;
return detail::if_full_parse(
initial_first,
first,
last,
parser.error_handler_,
parser::prefix_parse(first, last, parser, skip, trace_mode));
}
@@ -9187,9 +9467,12 @@ namespace boost { namespace parser {
auto r_ = detail::make_input_subrange(r);
auto first = r_.begin();
auto const last = r_.end();
auto const initial_first = first;
return detail::if_full_parse(
initial_first,
first,
last,
parser.error_handler_,
parser::callback_prefix_parse(first, last, parser, callbacks));
}
@@ -9323,9 +9606,12 @@ namespace boost { namespace parser {
auto r_ = detail::make_input_subrange(r);
auto first = r_.begin();
auto const last = r_.end();
auto const initial_first = first;
return detail::if_full_parse(
initial_first,
first,
last,
parser.error_handler_,
parser::callback_prefix_parse(
first, last, parser, skip, callbacks, trace_mode));
}
@@ -9443,9 +9729,6 @@ namespace boost { namespace parser {
template<typename... Args>
constexpr void static_assert_merge_attributes(tuple<Args...> parsers)
{
// This code chokes older GCCs. I can't figure out why, and this
// is an optional check, so I'm disabling it for those GCCs.
#if !defined(__GNUC__) || 13 <= __GNUC__
using context_t = parse_context<
false,
false,
@@ -9492,7 +9775,7 @@ namespace boost { namespace parser {
"type as one of the others.");
if constexpr (!std::is_same_v<t, first_t>) {
[[maybe_unused]] detail::print_type<tuple<Args...>>
tuple_types;
tuple_types(parsers);
[[maybe_unused]] detail::print_type<all_types>
attribute_types;
[[maybe_unused]] detail::print_type<first_t> first_type;
@@ -9500,7 +9783,6 @@ namespace boost { namespace parser {
}
}
});
#endif
}
}
}}

View File

@@ -143,6 +143,8 @@ namespace boost { namespace parser {
struct punct_chars
{};
struct symb_chars
{};
struct lower_case_chars
{};
struct upper_case_chars
@@ -207,7 +209,7 @@ namespace boost { namespace parser {
`ParserTuple`, not the order of the parsers' matches. It is an error
to specialize `perm_parser` with a `ParserTuple` template parameter
that includes an `eps_parser`. */
template<typename ParserTuple>
template<typename ParserTuple, typename DelimiterParser>
struct perm_parser;
/** Applies each parser in `ParserTuple`, in order. The parse succeeds
@@ -380,7 +382,10 @@ namespace boost { namespace parser {
/** Matches a string delimited by quotation marks; produces a
`std::string` attribute. */
template<typename Quotes = detail::nope, typename Escapes = detail::nope>
template<
typename Quotes = detail::nope,
typename Escapes = detail::nope,
typename CharParser = char_parser<detail::nope>>
struct quoted_string_parser;
/** Matches an end-of-line (`NewlinesOnly == true`), whitespace
@@ -399,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,

View File

@@ -4,7 +4,8 @@
#include <boost/parser/replace.hpp>
#if (!defined(_MSC_VER) || BOOST_PARSER_USE_CONCEPTS) && \
(!defined(__GNUC__) || 12 <= __GNUC__ || !BOOST_PARSER_USE_CONCEPTS)
(!defined(BOOST_PARSER_GCC) || 12 <= __GNUC__ || \
!BOOST_PARSER_USE_CONCEPTS)
namespace boost::parser {

View File

@@ -128,7 +128,7 @@ namespace boost { namespace parser {
/** A literal that can be used to concisely name `parser::llong`
integral constants. */
template<char... chars>
constexpr auto operator"" _c()
constexpr auto operator""_c()
{
constexpr long long n =
detail::parse_llong<sizeof...(chars)>({chars...});
@@ -185,14 +185,10 @@ namespace boost { namespace parser {
template<typename T>
operator T() const && noexcept
{
#if defined(__GNUC__) && __GNUC__ < 13
// Yuck.
std::remove_reference_t<T> * ptr = nullptr;
ptr += 1; // warning mitigation
return *ptr;
#else
return std::declval<T>();
#endif
}
};

View File

@@ -4,5 +4,6 @@
"authors": [ "T. Zachary Laine" ],
"maintainers": [ "Zach Laine <whatwasthataddress -at- gmail.com>" ],
"description": "A parser combinator library.",
"category": [ "Parsing" ]
"category": [ "Parsing" ],
"cxxstd": "17"
}

View File

@@ -8,6 +8,7 @@ import testing ;
project
: requirements <library>/boost/charconv//boost_charconv
<library>/boost/parser//boost_parser
;
compile compile_all_t.cpp ;

View File

@@ -237,6 +237,113 @@ void github_issue_125()
}
}
void github_issue_209()
{
namespace bp = boost::parser;
BOOST_TEST(std::is_sorted(
std::begin(bp::detail::char_set<detail::punct_chars>::chars),
std::end(bp::detail::char_set<detail::punct_chars>::chars)));
BOOST_TEST(std::is_sorted(
std::begin(bp::detail::char_set<detail::symb_chars>::chars),
std::end(bp::detail::char_set<detail::symb_chars>::chars)));
BOOST_TEST(std::is_sorted(
std::begin(bp::detail::char_set<detail::lower_case_chars>::chars),
std::end(bp::detail::char_set<detail::lower_case_chars>::chars)));
BOOST_TEST(std::is_sorted(
std::begin(bp::detail::char_set<detail::upper_case_chars>::chars),
std::end(bp::detail::char_set<detail::upper_case_chars>::chars)));
}
void github_issue_223()
{
namespace bp = boost::parser;
// failing case
{
std::vector<char> v;
const auto parser = *('x' | bp::char_('y'));
bp::parse("xy", parser, bp::ws, v);
BOOST_TEST(v.size() == 1);
BOOST_TEST(v == std::vector<char>({'y'}));
// the assert fails since there are two elements in the vector: '\0'
// and 'y'. Seems pretty surprising to me
}
// working case
{
const auto parser = *('x' | bp::char_('y'));
const auto result = bp::parse("xy", parser, bp::ws);
BOOST_TEST(result->size() == 1);
BOOST_TEST(*(*result)[0] == 'y');
// success, the vector has only one 'y' element
}
}
namespace github_issue_248_ {
namespace bp = boost::parser;
static constexpr bp::rule<struct symbol, int> symbol = "//";
static constexpr bp::rule<struct vector, std::vector<int>> list =
"<int>(,<int>)*";
static constexpr bp::rule<struct working, std::vector<int>> working =
"working";
static constexpr bp::rule<struct failing, std::vector<int>> failing =
"failing";
static auto const symbol_def = bp::symbols<int>{{"//", 0}};
static constexpr auto list_def = bp::int_ % ',';
static constexpr auto working_def = -symbol >> (bp::int_ % ',');
static constexpr auto failing_def = -symbol >> list;
BOOST_PARSER_DEFINE_RULES(symbol, list, working, failing);
}
void github_issue_248()
{
namespace bp = boost::parser;
using namespace github_issue_248_;
{
auto const result = bp::parse("//1,2,3", working, bp::ws);
auto const expected = std::vector<int>{0, 1, 2, 3};
BOOST_TEST(result.has_value());
bool const equal = std::equal(
result->begin(), result->end(), expected.begin(), expected.end());
BOOST_TEST(equal);
if (!equal) {
std::cout << "contents of *result:\n";
for (auto x : *result) {
std::cout << x << '\n';
}
std::cout << '\n';
}
}
{
auto const result = bp::parse("//1,2,3", failing, bp::ws);
auto const expected = std::vector<int>{0, 1, 2, 3};
BOOST_TEST(result.has_value());
bool const equal = std::equal(
result->begin(), result->end(), expected.begin(), expected.end());
BOOST_TEST(equal);
if (!equal) {
std::cout << "contents of *result:\n";
for (auto x : *result) {
std::cout << x << '\n';
}
std::cout << '\n';
}
}
}
int main()
{
@@ -246,5 +353,8 @@ int main()
github_issue_78();
github_issue_90();
github_issue_125();
github_issue_209();
github_issue_223();
github_issue_248();
return boost::report_errors();
}

View File

@@ -292,6 +292,15 @@ int main()
}
BOOST_TEST(parse(str, parser_1));
BOOST_TEST(!parse(str, parser_2));
{
BOOST_TEST(!parse(str, char_));
std::ostringstream err, warn;
stream_error_handler eh("", err, warn);
BOOST_TEST(!parse(str, with_error_handler(char_, eh)));
BOOST_TEST(
err.str() ==
"1:1: error: Expected end of input here:\nab\n ^\n");
}
}
{
std::string str = "ab";
@@ -2753,6 +2762,16 @@ int main()
BOOST_TEST(result == std::vector<uint32_t>({0x21, 0xfda}));
}
// symb_
{
auto parser = +symb;
std::u32string str = U"$^\u20AC!\u2194\u220F\U0001D7C6b\u2280\U0001FACE\U0001039F";
std::vector<uint32_t> result;
BOOST_TEST(parse(str, parser, char_ - symb, result));
BOOST_TEST(result == std::vector<uint32_t>({U'$', U'^', 0x20AC, 0x2194, 0x220F, 0x2280, 0x1FACE}));
}
// lower_
{
auto parser = +lower;

View File

@@ -88,5 +88,66 @@ int main()
}
}
return boost::report_errors();
{
using namespace bp::literals;
constexpr auto parser =
bp::delimiter(','_l)[bp::int_ || bp::string("foo") || bp::char_('g')];
{
auto result = bp::parse("42 foo g", parser, bp::ws);
BOOST_TEST(!result);
result = bp::parse("42, foo ,g", parser, bp::ws);
BOOST_TEST(result);
BOOST_TEST(
*result ==
(bp::tuple<int, std::string, double>(42, "foo"s, 'g')));
}
{
auto result = bp::parse(",42, g, foo", parser, bp::ws);
BOOST_TEST(!result);
result = bp::parse("42, g , foo", parser, bp::ws);
BOOST_TEST(result);
BOOST_TEST(
*result ==
(bp::tuple<int, std::string, double>(42, "foo"s, 'g')));
}
{
auto result = bp::parse("foo, 42, g,", parser, bp::ws);
BOOST_TEST(!result);
result = bp::parse("foo, 42, g", parser, bp::ws);
BOOST_TEST(result);
BOOST_TEST(
*result ==
(bp::tuple<int, std::string, double>(42, "foo"s, 'g')));
}
{
auto result = bp::parse("foo g 42", parser, bp::ws);
BOOST_TEST(!result);
result = bp::parse("foo, g, 42", parser, bp::ws);
BOOST_TEST(result);
BOOST_TEST(
*result ==
(bp::tuple<int, std::string, double>(42, "foo"s, 'g')));
}
{
auto result = bp::parse("g foo 42", parser, bp::ws);
BOOST_TEST(!result);
result = bp::parse("g ,foo ,42", parser, bp::ws);
BOOST_TEST(result);
BOOST_TEST(
*result ==
(bp::tuple<int, std::string, double>(42, "foo"s, 'g')));
}
{
auto result = bp::parse("g 42 foo", parser, bp::ws);
BOOST_TEST(!result);
result = bp::parse("g , 42 , foo", parser, bp::ws);
BOOST_TEST(result);
BOOST_TEST(
*result ==
(bp::tuple<int, std::string, double>(42, "foo"s, 'g')));
}
}
return boost::report_errors();
}

View File

@@ -49,7 +49,7 @@ int main()
}
}
// different_char
// different_quote
{
constexpr auto parser = bp::quoted_string('\'');
@@ -75,9 +75,18 @@ int main()
BOOST_TEST(result);
BOOST_TEST(*result == "'foo'");
}
{
constexpr auto parser = bp::quoted_string('\'', bp::char_("g\t"));
auto result = bp::parse(R"('\'ggg\'')", parser, bp::ws);
BOOST_TEST(result);
BOOST_TEST(*result == "'ggg'");
BOOST_TEST(!bp::parse(R"('\'fff\'')", parser, bp::ws));
}
}
// different_char_with_escapes
// different_quote_with_escapes
{
{
auto parser = bp::quoted_string('\'', cu_escapes);
@@ -119,6 +128,58 @@ int main()
}
}
// different_quote_with_escapes_and_char_p
{
{
auto parser = bp::quoted_string('\'', cu_escapes, bp::char_("g\t"));
{
auto result = bp::parse("", parser, bp::ws);
BOOST_TEST(!result);
}
{
auto result = bp::parse("'ggg'", parser, bp::ws);
BOOST_TEST(result);
}
{
auto result = bp::parse("'fff'", parser, bp::ws);
BOOST_TEST(!result);
}
{
auto result = bp::parse(R"('ggg\t')", parser, bp::ws);
BOOST_TEST(result);
BOOST_TEST(*result == "ggg\t");
}
{
auto result = bp::parse(R"('ggg\g')", parser, bp::ws);
BOOST_TEST(!result);
}
}
{
auto parser = bp::quoted_string('\'', cp_escapes);
{
auto result = bp::parse("", parser, bp::ws);
BOOST_TEST(!result);
}
{
auto result = bp::parse(R"('\tggg')", parser, bp::ws);
BOOST_TEST(result);
BOOST_TEST(*result == "\tggg");
}
{
auto result = bp::parse(R"('g\ggg')", parser, bp::ws);
BOOST_TEST(!result);
}
}
}
// char_set
{
constexpr auto parser = bp::quoted_string("'\"");
@@ -171,6 +232,15 @@ int main()
// character.
BOOST_TEST(!bp::parse(R"("\'foo")", parser, bp::ws));
}
{
constexpr auto parser = bp::quoted_string("'\"", bp::char_("g"));
auto result = bp::parse(R"('\'ggg\'')", parser, bp::ws);
BOOST_TEST(result);
BOOST_TEST(*result == "'ggg'");
BOOST_TEST(!bp::parse(R"('\'fff\'')", parser, bp::ws));
}
}
// char_set_with_escapes
@@ -233,6 +303,15 @@ int main()
BOOST_TEST(!result);
}
}
{
auto parser = bp::quoted_string("'\"", cu_escapes, bp::char_("g"));
auto result = bp::parse(R"('\'ggg\'')", parser, bp::ws);
BOOST_TEST(result);
BOOST_TEST(*result == "'ggg'");
BOOST_TEST(!bp::parse(R"('\'fff\'')", parser, bp::ws));
}
}
// doc_examples
@@ -244,8 +323,7 @@ int main()
assert(result1);
std::cout << *result1 << "\n"; // Prints: some text
auto result2 =
bp::parse("\"some \\\"text\\\"\"", bp::quoted_string, bp::ws);
auto result2 = bp::parse(R"("some \"text\"")", bp::quoted_string, bp::ws);
assert(result2);
std::cout << *result2 << "\n"; // Prints: some "text"
//]
@@ -279,6 +357,16 @@ int main()
assert(result5);
std::cout << *result5 << "\n"; // Prints (with a CRLF newline): some text
//]
//[ quoted_string_example_6
auto result6 = bp::parse(
"'some text'", bp::quoted_string("'\"", bp::char_('g')), bp::ws);
assert(!result6);
result6 =
bp::parse("'gggg'", bp::quoted_string("'\"", bp::char_('g')), bp::ws);
assert(result6);
std::cout << *result6 << "\n"; // Prints: gggg
//]
}
return boost::report_errors();

View File

@@ -316,7 +316,7 @@ int main()
add_parser >> roman_numerals >> next_delete_parser >>
roman_numerals);
BOOST_TEST(result);
BOOST_TEST(*result == std::tuple(100, 100));
BOOST_TEST(*result == detail::hl::make_tuple(100, 100));
}
{