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

49 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
Zach Laine
354586dd76 Github runner macos-12 -> macos-13. 2024-12-16 19:24:39 -06:00
Zach Laine
305bba875b Grooming. 2024-12-09 01:57:04 -06:00
Zach Laine
f468d529fe Fix stale comment in doc example. 2024-11-29 16:29:20 -06:00
Zach Laine
b5d4339f2c Fix semicolon-delimited bullet list in parsers table. 2024-11-29 16:29:13 -06:00
Zach Laine
6d7fa6f105 Add missing Docbook tag to fix broken reference in docs. 2024-11-29 16:20:14 -06:00
Mohammad Nejati
c975f57908 Add index.html
This redirects links pointing to the root directory to the documentation
page, such as:
https://www.boost.org/doc/libs/develop/libs/parser/
2024-11-27 20:10:11 -06:00
Zach Laine
063291b78c Correct the parser used in the second half of the "Parse Into a Class"
example.

Fixes #206.
2024-11-24 22:55:21 -06:00
Zach Laine
3eb827dcd6 Fix error in seq_parser::append() that causes AllowBacktracking=false to be treated as =true. 2024-11-14 22:49:51 -06:00
Zach Laine
6d796287b6 Disable the tests for in-Boost builds. 2024-11-13 20:54:34 -06:00
Zach Laine
bb0fb885b8 Doc update for previous symbol table changes.
Related to #183.
2024-10-31 23:26:07 -05:00
Zach Laine
94a9daec40 Change the way that the pending operations are applied to symbol table
parsers.  Instead of trying to find all of them at the start of the top-level
parse, they are recorded in the context, and then applied at the end of the
top-level parse.  The previous technique did not work, simplt because the
top-level parse cannot see all the parser involded -- some are behind rule
firewalls, by design.

Related to #183.
Fixes #204.
2024-10-31 23:07:17 -05:00
Zach Laine
4344dd3f47 Remove mooted TODOs. 2024-10-31 23:07:17 -05:00
Vernon Mauery
a7c7470bc1 Remove unused lambda capture
str is unused in the lambda; remove it from the capture

Signed-off-by: Vernon Mauery <vernon.mauery@intel.com>
2024-10-27 14:54:56 -05:00
Zach Laine
b273133fd2 Remove Hana dependency note from README. 2024-10-23 19:35:19 -05:00
Zach Laine
3a7ddcf936 Remove doc link from README. 2024-10-23 19:33:07 -05:00
Marshall Clow
d79efb0daa Update README.md 2024-10-23 19:25:31 -05:00
41 changed files with 6146 additions and 1976 deletions

View File

@@ -1,4 +1,4 @@
name: macos-12 - Clang 14
name: macos-13 - Clang 14
on:
push:
@@ -16,7 +16,7 @@ jobs:
matrix:
cxx_std: [17]
runs-on: macos-12
runs-on: macos-13
steps:
- uses: actions/checkout@v4

View File

@@ -22,12 +22,6 @@ target_link_libraries(boost_parser
Boost::type_index
)
if(BUILD_TESTING AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/test/CMakeLists.txt")
add_subdirectory(test)
endif()
else()
cmake_minimum_required(VERSION 3.14...3.20)

View File

@@ -34,12 +34,7 @@ int main()
}
```
This library is header-only, and has a default dependency on Boost.Hana. The
Boost.Hana dependency can be eliminated, and `std::tuple` will be used instead
of `boost::hana::tuple` throughout the library, if you `#define`
`BOOST_PARSER_DISABLE_HANA_TUPLE`. To try out the lib without mseeing with
dependencies, add its `include/` dir as an include path in your build and
define `BOOST_PARSER_DISABLE_HANA_TUPLE` your build.
This library is header-only, and has no Boost dependencies by default.
Features:
@@ -52,9 +47,7 @@ Features:
- Trace support for debugging your parsers.
- Clever hacks to make compile time errors easier to deal with. (These are totally optional.)
This library targets submission to Boost.
Online docs: https://tzlaine.github.io/parser
This library first appeared in Boost 1.87.0
Master status:
@@ -64,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:
@@ -74,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

@@ -34,9 +34,9 @@ itself be used as a parser; it must be called. In the table below:
* `a` is a semantic action;
* `r` is an object whose type models `parsable_range`; and
* `r` is an object whose type models `parsable_range`;
* `p`, `p1`, `p2`, ... are parsers.
* `p`, `p1`, `p2`, ... are parsers; and
* `escapes` is a _symbols_t_ object, where `T` is `char` or `char32_t`.
@@ -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
@@ -351,7 +351,7 @@ so this directive is only available in C++20 and later.
namespace bp = boost::parser;
auto int_parser = bp::int_ % ','; // ATTR(int_parser) is std::vector<int>
auto sv_parser = bp::string_view[int_parser]; // ATTR(subrange_parser) is a string_view
auto sv_parser = bp::string_view[int_parser]; // ATTR(sv_parser) is a string_view
auto const str = std::string("1, 2, 3, 4, a, b, c");
auto first = str.begin();
@@ -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]
@@ -1126,24 +1134,12 @@ the second `'X'` is recognized by the symbol table parser. However:
If we parse again, we find that `"X"` did not stay in the symbol table. The
fact that `symbols` was declared const might have given you a hint that this
would happen. Also, notice that the call to `insert()` in the semantic action
uses the parse context; that's where all the symbol table changes are stored
during the parse.
would happen.
The full program:
[self_filling_symbol_table_example]
[tip _symbols_ also has a call operator that does exactly what
`.insert_for_next_parse()` does. This allows you to chain additions with a
convenient syntax, like this:
```
symbols<int> roman_numerals;
roman_numerals.insert_for_next_parse("I", 1)("V", 5)("X", 10);
```
]
[important _symbols_ stores all its strings in UTF-32 internally. If you do
Unicode or ASCII parsing, this will not matter to you at all. If you do
non-Unicode parsing of a character encoding that is not a subset of Unicode
@@ -1163,6 +1159,11 @@ 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.
[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.]
[endsect]
[section The Parsers And Their Uses]
@@ -1247,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]
@@ -1282,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
@@ -1449,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]
@@ -1597,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
@@ -1608,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
@@ -2218,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);
@@ -2358,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
@@ -2400,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;
@@ -2487,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
@@ -2495,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
@@ -2569,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.
@@ -3309,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
@@ -3319,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.
@@ -3350,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.]
@@ -3461,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
@@ -3509,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:
@@ -3696,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
@@ -3773,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

@@ -33,7 +33,7 @@ int main()
std::cout << input << "\n";
//[ parsing_into_a_class_vec_of_strs
constexpr auto uint_string = bp::uint_ >> bp::char_ >> bp::char_;
constexpr auto uint_string = bp::uint_ >> +bp::char_;
std::vector<std::string> vector_from_parse;
if (parse(input, uint_string, bp::ws, vector_from_parse)) {
std::cout << "That yields this vector of strings:\n";

View File

@@ -31,9 +31,11 @@ namespace boost { namespace parser {
std::forward_iterator<T> && code_unit<std::iter_value_t<T>>;
//[ parsable_range_like_concept
//[ parsable_range_concept
template<typename T>
concept parsable_range = std::ranges::forward_range<T> &&
code_unit<std::ranges::range_value_t<T>>;
//]
template<typename T>
concept parsable_pointer = std::is_pointer_v<std::remove_cvref_t<T>> &&
@@ -58,7 +60,8 @@ namespace boost { namespace parser {
std::declval<int &>(),
std::declval<ErrorHandler const &>(),
std::declval<detail::nope &>(),
std::declval<detail::symbol_table_tries_t &>()));
std::declval<detail::symbol_table_tries_t &>(),
std::declval<detail::pending_symbol_table_operations_t &>()));
template<typename T, typename I, typename S, typename GlobalState>
concept error_handler =

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

@@ -47,11 +47,9 @@ namespace boost::parser::detail { namespace stl_interfaces {
`T`. */
template<typename T>
#if defined(BOOST_STL_INTERFACES_DOXYGEN) || BOOST_PARSER_USE_CONCEPTS
// clang-format off
requires std::is_object_v<T>
#endif
struct proxy_arrow_result
// clang-format on
{
constexpr proxy_arrow_result(T const & value) noexcept(
noexcept(T(value))) :
@@ -619,33 +617,25 @@ namespace boost::parser::detail { namespace stl_interfaces { BOOST_PARSER_DETAIL
using iter_concept_t = typename iter_concept<Iterator>::type;
template<typename D, typename DifferenceType>
// clang-format off
concept plus_eq = requires (D d) { d += DifferenceType(1); };
// clang-format on
concept plus_eq = requires(D d) { d += DifferenceType(1); };
template<typename D, typename D2 = D>
// clang-format off
concept base_3way =
#if defined(__cpp_impl_three_way_comparison)
requires (D d, D2 d2) { access::base(d) <=> access::base(d2); };
requires(D d, D2 d2) { access::base(d) <=> access::base(d2); };
#else
false;
#endif
// clang-format on
template<typename D1, typename D2 = D1>
// clang-format off
concept base_eq =
requires (D1 d1, D2 d2) { access::base(d1) == access::base(d2); };
// clang-format on
requires(D1 d1, D2 d2) { access::base(d1) == access::base(d2); };
template<typename D, typename D2 = D>
// clang-format off
concept iter_sub = requires (D d, D2 d2) {
concept iter_sub = requires(D d, D2 d2) {
typename D::difference_type;
{d - d2} -> std::convertible_to<typename D::difference_type>;
{ d - d2 } -> std::convertible_to<typename D::difference_type>;
};
// clang-format on
// This iterator concept -> category mapping scheme follows the one
// from zip_transform_view; see

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
@@ -328,23 +329,20 @@ namespace boost::parser::detail { namespace stl_interfaces {
{
constexpr adaptor(F f) : f_(f) {}
// clang-format off
template<typename... Args>
constexpr auto operator()(Args &&... args) const
// clang-format on
{
#if BOOST_PARSER_DETAIL_STL_INTERFACES_USE_CONCEPTS
if constexpr (std::is_invocable_v<F const &, Args...>) {
return f_((Args &&) args...);
return f_((Args &&)args...);
} else {
return closure(
stl_interfaces::bind_back(f_, (Args &&) args...));
return closure(stl_interfaces::bind_back(f_, (Args &&)args...));
}
#else
return detail::adaptor_impl<
F const &,
detail::is_invocable_v<F const &, Args...>,
Args...>::call(f_, (Args &&) args...);
Args...>::call(f_, (Args &&)args...);
#endif
}

View File

@@ -97,17 +97,14 @@ namespace boost::parser::detail { namespace text { BOOST_PARSER_DETAIL_TEXT_NAME
template<typename T>
concept grapheme_iter =
// clang-format off
std::input_iterator<T> &&
code_point_range<std::iter_reference_t<T>> &&
std::input_iterator<T> && code_point_range<std::iter_reference_t<T>> &&
requires(T t) {
{ t.base() } -> code_point_iter;
// clang-format on
};
{ t.base() } -> code_point_iter;
};
template<typename T>
concept grapheme_range = std::ranges::input_range<T> &&
grapheme_iter<std::ranges::iterator_t<T>>;
grapheme_iter<std::ranges::iterator_t<T>>;
template<typename R>
using code_point_iterator_t = decltype(std::declval<R>().begin().base());
@@ -116,75 +113,63 @@ namespace boost::parser::detail { namespace text { BOOST_PARSER_DETAIL_TEXT_NAME
using code_point_sentinel_t = decltype(std::declval<R>().end().base());
template<typename T, format F>
concept grapheme_iter_code_unit =
// clang-format off
grapheme_iter<T> &&
requires(T t) {
concept grapheme_iter_code_unit = grapheme_iter<T> && requires(T t) {
{ t.base().base() } -> code_unit_iter<F>;
// clang-format on
};
template<typename T, format F>
concept grapheme_range_code_unit = grapheme_range<T> &&
concept grapheme_range_code_unit =
grapheme_range<T> &&
grapheme_iter_code_unit<std::ranges::iterator_t<T>, F>;
namespace dtl {
template<typename T, class CodeUnit>
concept eraseable_insertable_sized_bidi_range =
// clang-format off
std::ranges::sized_range<T> &&
std::ranges::input_range<T> &&
std::ranges::sized_range<T> && std::ranges::input_range<T> &&
requires(T t, CodeUnit const * it) {
{ t.erase(t.begin(), t.end()) } ->
std::same_as<std::ranges::iterator_t<T>>;
{ t.insert(t.end(), it, it) } ->
std::same_as<std::ranges::iterator_t<T>>;
{
t.erase(t.begin(), t.end())
} -> std::same_as<std::ranges::iterator_t<T>>;
{
t.insert(t.end(), it, it)
} -> std::same_as<std::ranges::iterator_t<T>>;
};
// clang-format on
}
template<typename T>
concept utf8_string =
// clang-format off
utf8_code_unit<std::ranges::range_value_t<T>> &&
dtl::eraseable_insertable_sized_bidi_range<
T, std::ranges::range_value_t<T>>;
// clang-format on
concept utf8_string = utf8_code_unit<std::ranges::range_value_t<T>> &&
dtl::eraseable_insertable_sized_bidi_range<
T,
std::ranges::range_value_t<T>>;
template<typename T>
concept utf16_string =
// clang-format off
utf16_code_unit<std::ranges::range_value_t<T>> &&
dtl::eraseable_insertable_sized_bidi_range<
T, std::ranges::range_value_t<T>>;
// clang-format on
concept utf16_string = utf16_code_unit<std::ranges::range_value_t<T>> &&
dtl::eraseable_insertable_sized_bidi_range<
T,
std::ranges::range_value_t<T>>;
template<typename T>
concept utf_string = utf8_string<T> || utf16_string<T>;
template<typename T>
// clang-format off
concept transcoding_error_handler = requires(T t, std::string_view msg) {
concept transcoding_error_handler = requires(T t, std::string_view msg) {
{ t(msg) } -> std::same_as<char32_t>;
// clang-format on
};
//]
// Clang 13 defines __cpp_lib_concepts but not std::indirectly copyable.
#if defined(__clang_major__) && __clang_major__ <= 13
template<typename In, typename Out>
// clang-format off
concept indirectly_copyable =
std::indirectly_readable<In> &&
std::indirectly_writable<Out, std::iter_reference_t<In>>;
// clang-format on
#else
template<typename In, typename Out>
concept indirectly_copyable = std::indirectly_copyable<In, Out>;
#endif
}}}
}
}}
#endif

View File

@@ -51,13 +51,10 @@ namespace boost::parser::detail { namespace text { namespace detail {
// that is comparable with T's interator type.
template<typename T>
concept cp_sentinel_gr_rng =
// clang-format off
grapheme_range<T> &&
!grapheme_iter<sentinel_t<T>> &&
grapheme_range<T> && !grapheme_iter<sentinel_t<T>> &&
requires(iterator_t<T> first, sentinel_t<T> last) {
{ first.base() == last } -> std::convertible_to<bool>;
// clang-format on
};
{ first.base() == last } -> std::convertible_to<bool>;
};
template<typename T>
using gr_rng_cp_iter_t = decltype(std::declval<iterator_t<T>>().base());

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

@@ -2350,11 +2350,9 @@ namespace boost::parser::detail { namespace text { BOOST_PARSER_DETAIL_TEXT_NAME
}
template<typename Cont>
// clang-format off
requires requires { typename Cont::value_type; } &&
utf_code_unit<typename Cont::value_type>
utf_code_unit<typename Cont::value_type>
constexpr auto from_utf8_inserter(Cont & c, typename Cont::iterator it)
// clang-format on
{
if constexpr (sizeof(typename Cont::value_type) == 1) {
return std::insert_iterator<Cont>(c, it);
@@ -2366,11 +2364,9 @@ namespace boost::parser::detail { namespace text { BOOST_PARSER_DETAIL_TEXT_NAME
}
template<typename Cont>
// clang-format off
requires requires { typename Cont::value_type; } &&
utf_code_unit<typename Cont::value_type>
utf_code_unit<typename Cont::value_type>
constexpr auto from_utf16_inserter(Cont & c, typename Cont::iterator it)
// clang-format on
{
if constexpr (sizeof(typename Cont::value_type) == 1) {
return utf_16_to_8_insert_iterator<Cont>(c, it);
@@ -2382,11 +2378,9 @@ namespace boost::parser::detail { namespace text { BOOST_PARSER_DETAIL_TEXT_NAME
}
template<typename Cont>
// clang-format off
requires requires { typename Cont::value_type; } &&
utf_code_unit<typename Cont::value_type>
utf_code_unit<typename Cont::value_type>
constexpr auto from_utf32_inserter(Cont & c, typename Cont::iterator it)
// clang-format on
{
if constexpr (sizeof(typename Cont::value_type) == 1) {
return utf_32_to_8_insert_iterator<Cont>(c, it);
@@ -2398,11 +2392,9 @@ namespace boost::parser::detail { namespace text { BOOST_PARSER_DETAIL_TEXT_NAME
}
template<typename Cont>
// clang-format off
requires requires { typename Cont::value_type; } &&
utf_code_unit<typename Cont::value_type>
utf_code_unit<typename Cont::value_type>
constexpr auto from_utf8_back_inserter(Cont & c)
// clang-format on
{
if constexpr (sizeof(typename Cont::value_type) == 1) {
return std::back_insert_iterator<Cont>(c);
@@ -2414,11 +2406,9 @@ namespace boost::parser::detail { namespace text { BOOST_PARSER_DETAIL_TEXT_NAME
}
template<typename Cont>
// clang-format off
requires requires { typename Cont::value_type; } &&
utf_code_unit<typename Cont::value_type>
utf_code_unit<typename Cont::value_type>
constexpr auto from_utf16_back_inserter(Cont & c)
// clang-format on
{
if constexpr (sizeof(typename Cont::value_type) == 1) {
return utf_16_to_8_back_insert_iterator<Cont>(c);
@@ -2430,11 +2420,9 @@ namespace boost::parser::detail { namespace text { BOOST_PARSER_DETAIL_TEXT_NAME
}
template<typename Cont>
// clang-format off
requires requires { typename Cont::value_type; } &&
utf_code_unit<typename Cont::value_type>
utf_code_unit<typename Cont::value_type>
constexpr auto from_utf32_back_inserter(Cont & c)
// clang-format on
{
if constexpr (sizeof(typename Cont::value_type) == 1) {
return utf_32_to_8_back_insert_iterator<Cont>(c);
@@ -2446,11 +2434,9 @@ namespace boost::parser::detail { namespace text { BOOST_PARSER_DETAIL_TEXT_NAME
}
template<typename Cont>
// clang-format off
requires requires { typename Cont::value_type; } &&
utf_code_unit<typename Cont::value_type>
utf_code_unit<typename Cont::value_type>
constexpr auto from_utf8_front_inserter(Cont & c)
// clang-format on
{
if constexpr (sizeof(typename Cont::value_type) == 1) {
return std::front_insert_iterator<Cont>(c);
@@ -2462,11 +2448,9 @@ namespace boost::parser::detail { namespace text { BOOST_PARSER_DETAIL_TEXT_NAME
}
template<typename Cont>
// clang-format off
requires requires { typename Cont::value_type; } &&
utf_code_unit<typename Cont::value_type>
utf_code_unit<typename Cont::value_type>
constexpr auto from_utf16_front_inserter(Cont & c)
// clang-format on
{
if constexpr (sizeof(typename Cont::value_type) == 1) {
return utf_16_to_8_front_insert_iterator<Cont>(c);
@@ -2478,11 +2462,9 @@ namespace boost::parser::detail { namespace text { BOOST_PARSER_DETAIL_TEXT_NAME
}
template<typename Cont>
// clang-format off
requires requires { typename Cont::value_type; } &&
utf_code_unit<typename Cont::value_type>
utf_code_unit<typename Cont::value_type>
constexpr auto from_utf32_front_inserter(Cont & c)
// clang-format on
{
if constexpr (sizeof(typename Cont::value_type) == 1) {
return utf_32_to_8_front_insert_iterator<Cont>(c);
@@ -2492,7 +2474,6 @@ namespace boost::parser::detail { namespace text { BOOST_PARSER_DETAIL_TEXT_NAME
return std::front_insert_iterator<Cont>(c);
}
}
}}}
#endif

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
@@ -423,49 +425,57 @@ namespace boost::parser::detail { namespace text {
#if defined(__cpp_char8_t)
inline constexpr detail::as_charn_impl<char8_view, format::utf8> as_char8_t;
#endif
inline constexpr detail::as_charn_impl<char16_view, format::utf16> as_char16_t;
inline constexpr detail::as_charn_impl<char32_view, format::utf32> as_char32_t;
inline constexpr detail::as_charn_impl<char16_view, format::utf16>
as_char16_t;
inline constexpr detail::as_charn_impl<char32_view, format::utf32>
as_char32_t;
// clang-format off
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS
template<utf_range V>
requires std::ranges::view<V> && std::ranges::forward_range<V>
requires std::ranges::view<V> && std::ranges::forward_range<V>
#else
template<typename V>
#endif
class unpacking_view : public stl_interfaces::view_interface<unpacking_view<V>> {
V base_ = V();
class unpacking_view
: public stl_interfaces::view_interface<unpacking_view<V>>
{
V base_ = V();
public:
constexpr unpacking_view()
constexpr unpacking_view()
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS
requires std::default_initializable<V>
requires std::default_initializable<V>
#endif
= default;
constexpr unpacking_view(V base) : base_(std::move(base)) {}
= default;
constexpr unpacking_view(V base) : base_(std::move(base)) {}
constexpr V base() const &
constexpr V base() const &
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS
requires std::copy_constructible<V>
requires std::copy_constructible<V>
#endif
{ return base_; }
constexpr V base() && { return std::move(base_); }
{
return base_;
}
constexpr V base() && { return std::move(base_); }
constexpr auto code_units() const noexcept {
auto unpacked = boost::parser::detail::text::unpack_iterator_and_sentinel(detail::begin(base_), detail::end(base_));
return BOOST_PARSER_DETAIL_TEXT_SUBRANGE(unpacked.first, unpacked.last);
}
constexpr auto code_units() const noexcept
{
auto unpacked =
boost::parser::detail::text::unpack_iterator_and_sentinel(
detail::begin(base_), detail::end(base_));
return BOOST_PARSER_DETAIL_TEXT_SUBRANGE(
unpacked.first, unpacked.last);
}
constexpr auto begin() { return code_units().begin(); }
constexpr auto begin() const { return code_units().begin(); }
constexpr auto begin() { return code_units().begin(); }
constexpr auto begin() const { return code_units().begin(); }
constexpr auto end() { return code_units().end(); }
constexpr auto end() const { return code_units().end(); }
constexpr auto end() { return code_units().end(); }
constexpr auto end() const { return code_units().end(); }
};
template<class R>
unpacking_view(R &&) -> unpacking_view<detail::all_t<R>>;
// clang-format on
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS
template<format Format, utf_range V>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -11,6 +11,7 @@
#include <any>
#include <cstdint>
#include <functional>
#include <map>
#include <memory>
#include <optional>
@@ -84,12 +85,24 @@ namespace boost { namespace parser {
{
std::any trie_;
bool has_case_folded_;
bool pending_operations_;
};
using symbol_table_tries_t =
std::map<void *, symbol_table_trie_element, std::less<void *>>;
using pending_symtab_ops_visitor = std::function<void()>;
struct pending_symtab_ops_entry
{
pending_symtab_ops_visitor visit_;
// Contains std::vector<detail::symbol_table_operation<T>> (T is
// known to visit_).
std::any ops_;
};
using pending_symbol_table_operations_t = std::map<
void const *,
pending_symtab_ops_entry,
std::less<void const *>>;
template<
bool DoTrace,
bool UseCallbacks,
@@ -103,7 +116,9 @@ namespace boost { namespace parser {
int & indent,
ErrorHandler const & error_handler,
nope &,
symbol_table_tries_t & symbol_table_tries) noexcept;
symbol_table_tries_t & symbol_table_tries,
pending_symbol_table_operations_t &
pending_symbol_table_operations) noexcept;
struct skip_skipper;
@@ -128,6 +143,8 @@ namespace boost { namespace parser {
struct punct_chars
{};
struct symb_chars
{};
struct lower_case_chars
{};
struct upper_case_chars
@@ -192,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
@@ -365,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
@@ -384,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

@@ -77,22 +77,16 @@ namespace boost::parser {
range_rvalue_reference_t<V2>>;
#if BOOST_PARSER_USE_CONCEPTS
// clang-format off
template<typename ReplacementV, typename V>
concept concatable = requires {
typename detail::concat_reference_t<ReplacementV, V>;
typename detail::concat_value_t<ReplacementV, V>;
typename detail::concat_rvalue_reference_t<ReplacementV, V>;
};
// clang-format on
#else
template<typename ReplacementV, typename V>
// clang-format off
using concatable_expr = decltype(
std::declval<concat_reference_t<ReplacementV, V>>(),
std::declval<concat_value_t<ReplacementV, V>>(),
std::declval<concat_rvalue_reference_t<ReplacementV, V>>());
// clang-format on
using concatable_expr =
decltype(std::declval<concat_reference_t<ReplacementV, V>>(), std::declval<concat_value_t<ReplacementV, V>>(), std::declval<concat_rvalue_reference_t<ReplacementV, V>>());
template<typename ReplacementV, typename V>
constexpr bool concatable =
is_detected_v<concatable_expr, ReplacementV, V>;
@@ -107,7 +101,7 @@ namespace boost::parser {
#endif
>
#if BOOST_PARSER_USE_CONCEPTS
requires concatable<V1, V2>
requires concatable<V1, V2>
#endif
struct either_iterator_impl
: detail::stl_interfaces::iterator_interface<
@@ -169,14 +163,12 @@ namespace boost::parser {
either_iterator_impl<V1, V2>>;
#if BOOST_PARSER_USE_CONCEPTS
// clang-format off
template<typename ReplacementV, typename V>
concept replacement_for = requires (ReplacementV replacement, V base) {
concept replacement_for = requires(ReplacementV replacement, V base) {
{ either_iterator<V, ReplacementV>(replacement.begin()) };
{ either_iterator<V, ReplacementV>(replacement.end()) };
{ either_iterator<V, ReplacementV>(base.begin()) };
};
// clang-format on
#else
template<typename ReplacementV, typename V>
using replacement_for_expr = decltype(
@@ -528,23 +520,19 @@ namespace boost::parser {
typename GlobalState,
typename ErrorHandler,
typename SkipParser>
requires
// clang-format off
std::ranges::viewable_range<R> &&
std::ranges::viewable_range<ReplacementR> &&
// clang-format on
can_replace_view<
to_range_t<R>,
decltype(to_range<
ReplacementR,
true,
detail::range_utf_format_v<R>>::
call(std::declval<ReplacementR>())),
Parser,
GlobalState,
ErrorHandler,
SkipParser>
// clang-format off
requires std::ranges::viewable_range<R> &&
std::ranges::viewable_range<ReplacementR> &&
can_replace_view<
to_range_t<R>,
decltype(to_range<
ReplacementR,
true,
detail::range_utf_format_v<R>>::
call(std::declval<ReplacementR>())),
Parser,
GlobalState,
ErrorHandler,
SkipParser>
[[nodiscard]] constexpr auto operator()(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const &
@@ -552,10 +540,9 @@ namespace boost::parser {
parser_interface<SkipParser> const & skip,
ReplacementR && replacement,
trace trace_mode = trace::off) const
// clang-format on
{
return replace_view(
to_range<R>::call((R &&) r),
to_range<R>::call((R &&)r),
parser,
skip,
to_range<
@@ -572,36 +559,31 @@ namespace boost::parser {
typename Parser,
typename GlobalState,
typename ErrorHandler>
requires
// clang-format off
std::ranges::viewable_range<R> &&
std::ranges::viewable_range<ReplacementR> &&
// clang-format on
can_replace_view<
to_range_t<R>,
decltype(to_range<
ReplacementR,
true,
detail::range_utf_format_v<R>>::
call(std::declval<ReplacementR>())),
Parser,
GlobalState,
ErrorHandler,
parser_interface<eps_parser<detail::phony>>>
// clang-format off
requires std::ranges::viewable_range<R> &&
std::ranges::viewable_range<ReplacementR> &&
can_replace_view<
to_range_t<R>,
decltype(to_range<
ReplacementR,
true,
detail::range_utf_format_v<R>>::
call(std::declval<ReplacementR>())),
Parser,
GlobalState,
ErrorHandler,
parser_interface<eps_parser<detail::phony>>>
[[nodiscard]] constexpr auto operator()(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const &
parser,
ReplacementR && replacement,
trace trace_mode = trace::off) const
// clang-format on
{
return (*this)(
(R &&) r,
(R &&)r,
parser,
parser_interface<eps_parser<detail::phony>>{},
(ReplacementR &&) replacement,
(ReplacementR &&)replacement,
trace_mode);
}

View File

@@ -541,25 +541,21 @@ namespace boost::parser {
typename GlobalState,
typename ErrorHandler,
typename SkipParser>
requires(
std::ranges::viewable_range<R>) &&
can_search_all_view<
to_range_t<R>,
Parser,
GlobalState,
ErrorHandler,
SkipParser>
// clang-format off
requires(std::ranges::viewable_range<R>) && can_search_all_view<
to_range_t<R>,
Parser,
GlobalState,
ErrorHandler,
SkipParser>
[[nodiscard]] constexpr auto operator()(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const &
parser,
parser_interface<SkipParser> const & skip,
trace trace_mode = trace::off) const
// clang-format on
{
return search_all_view(
to_range<R>::call((R &&) r), parser, skip, trace_mode);
to_range<R>::call((R &&)r), parser, skip, trace_mode);
}
template<
@@ -567,24 +563,21 @@ namespace boost::parser {
typename Parser,
typename GlobalState,
typename ErrorHandler>
requires(
std::ranges::viewable_range<R>) &&
can_search_all_view<
to_range_t<R>,
Parser,
GlobalState,
ErrorHandler,
parser_interface<eps_parser<detail::phony>>>
// clang-format off
requires(std::ranges::viewable_range<R>) &&
can_search_all_view<
to_range_t<R>,
Parser,
GlobalState,
ErrorHandler,
parser_interface<eps_parser<detail::phony>>>
[[nodiscard]] constexpr auto operator()(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const &
parser,
trace trace_mode = trace::off) const
// clang-format on
{
return (*this)(
(R &&) r,
(R &&)r,
parser,
parser_interface<eps_parser<detail::phony>>{},
trace_mode);

View File

@@ -258,25 +258,21 @@ namespace boost::parser {
typename GlobalState,
typename ErrorHandler,
typename SkipParser>
requires(
std::ranges::viewable_range<R>) &&
can_split_view<
to_range_t<R>,
Parser,
GlobalState,
ErrorHandler,
SkipParser>
// clang-format off
requires(std::ranges::viewable_range<R>) && can_split_view<
to_range_t<R>,
Parser,
GlobalState,
ErrorHandler,
SkipParser>
[[nodiscard]] constexpr auto operator()(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const &
parser,
parser_interface<SkipParser> const & skip,
trace trace_mode = trace::off) const
// clang-format on
{
return split_view(
to_range<R>::call((R &&) r), parser, skip, trace_mode);
to_range<R>::call((R &&)r), parser, skip, trace_mode);
}
template<
@@ -284,24 +280,21 @@ namespace boost::parser {
typename Parser,
typename GlobalState,
typename ErrorHandler>
requires(
std::ranges::viewable_range<R>) &&
can_split_view<
to_range_t<R>,
Parser,
GlobalState,
ErrorHandler,
parser_interface<eps_parser<detail::phony>>>
// clang-format off
requires(std::ranges::viewable_range<R>) &&
can_split_view<
to_range_t<R>,
Parser,
GlobalState,
ErrorHandler,
parser_interface<eps_parser<detail::phony>>>
[[nodiscard]] constexpr auto operator()(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const &
parser,
trace trace_mode = trace::off) const
// clang-format on
{
return (*this)(
(R &&) r,
(R &&)r,
parser,
parser_interface<eps_parser<detail::phony>>{},
trace_mode);

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 {
@@ -28,16 +29,15 @@ namespace boost::parser {
using range_attr_t = attr_type<iterator_t<R>, sentinel_t<R>, Parser>;
#if BOOST_PARSER_USE_CONCEPTS
// clang-format off
template<typename F, typename V, typename Parser>
concept transform_replacement_for =
std::regular_invocable<F &, range_attr_t<V, Parser>> &&
detail::replacement_for<
std::invoke_result_t<F &, range_attr_t<V, Parser>>, V> &&
std::invoke_result_t<F &, range_attr_t<V, Parser>>,
V> &&
(detail::range_utf_format_v<V> ==
detail::range_utf_format_v<
std::invoke_result_t<F &, range_attr_t<V, Parser>>>);
// clang-format on
#else
template<typename F, typename V, typename Parser>
using transform_replacement_for_expr = decltype(std::declval<F &>()(
@@ -646,24 +646,20 @@ namespace boost::parser {
typename GlobalState,
typename ErrorHandler,
typename SkipParser>
requires
// clang-format off
std::ranges::viewable_range<R> &&
std::regular_invocable<
F &,
range_attr_t<to_range_t<R>, Parser>> &&
// clang-format on
can_transform_replace_view<
to_range_t<R>,
utf_rvalue_shim<
to_range_t<R>,
std::remove_cvref_t<F>,
range_attr_t<to_range_t<R>, Parser>>,
Parser,
GlobalState,
ErrorHandler,
SkipParser>
// clang-format off
requires std::ranges::viewable_range<R> &&
std::regular_invocable<
F &,
range_attr_t<to_range_t<R>, Parser>> &&
can_transform_replace_view<
to_range_t<R>,
utf_rvalue_shim<
to_range_t<R>,
std::remove_cvref_t<F>,
range_attr_t<to_range_t<R>, Parser>>,
Parser,
GlobalState,
ErrorHandler,
SkipParser>
[[nodiscard]] constexpr auto operator()(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const &
@@ -671,16 +667,15 @@ namespace boost::parser {
parser_interface<SkipParser> const & skip,
F && f,
trace trace_mode = trace::off) const
// clang-format on
{
return transform_replace_view(
to_range<R>::call((R &&) r),
to_range<R>::call((R &&)r),
parser,
skip,
utf_rvalue_shim<
to_range_t<R>,
std::remove_cvref_t<F>,
range_attr_t<to_range_t<R>, Parser>>((F &&) f),
range_attr_t<to_range_t<R>, Parser>>((F &&)f),
trace_mode);
}
@@ -690,37 +685,32 @@ namespace boost::parser {
typename Parser,
typename GlobalState,
typename ErrorHandler>
requires
// clang-format off
std::ranges::viewable_range<R> &&
std::regular_invocable<
F &,
range_attr_t<to_range_t<R>, Parser>> &&
// clang-format on
can_transform_replace_view<
to_range_t<R>,
utf_rvalue_shim<
to_range_t<R>,
std::remove_cvref_t<F>,
range_attr_t<to_range_t<R>, Parser>>,
Parser,
GlobalState,
ErrorHandler,
parser_interface<eps_parser<detail::phony>>>
// clang-format off
requires std::ranges::viewable_range<R> &&
std::regular_invocable<
F &,
range_attr_t<to_range_t<R>, Parser>> &&
can_transform_replace_view<
to_range_t<R>,
utf_rvalue_shim<
to_range_t<R>,
std::remove_cvref_t<F>,
range_attr_t<to_range_t<R>, Parser>>,
Parser,
GlobalState,
ErrorHandler,
parser_interface<eps_parser<detail::phony>>>
[[nodiscard]] constexpr auto operator()(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const &
parser,
F && f,
trace trace_mode = trace::off) const
// clang-format on
{
return (*this)(
(R &&) r,
(R &&)r,
parser,
parser_interface<eps_parser<detail::phony>>{},
(F &&) f,
(F &&)f,
trace_mode);
}

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
}
};

10
index.html Normal file
View File

@@ -0,0 +1,10 @@
<html>
<head>
<title>Boost.Parser</title>
<meta http-equiv="refresh" content="0; URL=../../doc/html/parser.html">
</head>
<body>
Automatic redirection failed, please go to
<a href="../../doc/html/parser.html">../../doc/html/parser.html</a>
</body>
</html>

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

@@ -228,7 +228,9 @@ constexpr auto double_s = u8"sS"; // U+0073 U+0073
// with mutation
{
symbols<int> roman_numerals;
roman_numerals.insert_for_next_parse("I", 1)("V", 5)("X", 10);
roman_numerals.insert_for_next_parse("I", 1);
roman_numerals.insert_for_next_parse("V", 5);
roman_numerals.insert_for_next_parse("X", 10);
auto const add_numeral = [&roman_numerals](auto & context) {
using namespace boost::parser::literals;
char chars[2] = {get(_attr(context), 0_c), 0};

File diff suppressed because it is too large Load Diff

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

@@ -10,6 +10,12 @@
using namespace boost::parser;
rule<class symbol_rule, std::string_view> const symrule = "symbols";
symbols<std::string_view> rule_symbols;
auto const fwd_attr = [](auto & ctx) { _val(ctx) = _attr(ctx); };
auto symrule_def = rule_symbols[fwd_attr];
BOOST_PARSER_DEFINE_RULES(symrule);
int main()
{
// symbols_empty
@@ -137,7 +143,9 @@ int main()
// symbols_mutating
{
symbols<int> roman_numerals;
roman_numerals.insert_for_next_parse("I", 1)("V", 5)("X", 10);
roman_numerals.insert_for_next_parse("I", 1);
roman_numerals.insert_for_next_parse("V", 5);
roman_numerals.insert_for_next_parse("X", 10);
auto const add_numeral = [&roman_numerals](auto & context) {
using namespace boost::parser::literals;
char chars[2] = {get(_attr(context), 0_c), 0};
@@ -166,7 +174,9 @@ int main()
// insert/erase/clear
{
symbols<int> roman_numerals;
roman_numerals.insert_for_next_parse("I", 1)("V", 5)("X", 10);
roman_numerals.insert_for_next_parse("I", 1);
roman_numerals.insert_for_next_parse("V", 5);
roman_numerals.insert_for_next_parse("X", 10);
auto const insert_numeral = [&roman_numerals](auto & context) {
using namespace boost::parser::literals;
@@ -189,15 +199,16 @@ int main()
auto const next_insert_numeral = [&roman_numerals](auto & context) {
using namespace boost::parser::literals;
char chars[2] = {get(_attr(context), 0_c), 0};
roman_numerals.insert_for_next_parse(chars, get(_attr(context), 1_c));
roman_numerals.insert_for_next_parse(
context, chars, get(_attr(context), 1_c));
};
auto const next_erase_numeral = [&roman_numerals](auto & context) {
using namespace boost::parser::literals;
char chars[2] = {_attr(context), 0};
roman_numerals.erase_for_next_parse(chars);
roman_numerals.erase_for_next_parse(context, chars);
};
auto const next_clear_numerals = [&roman_numerals](auto &) {
roman_numerals.clear_for_next_parse();
auto const next_clear_numerals = [&roman_numerals](auto & context) {
roman_numerals.clear_for_next_parse(context);
};
auto const next_add_parser =
@@ -217,7 +228,7 @@ int main()
{
// add only for the next parse
auto result = parse("next-addL50L", next_add_parser >> roman_numerals);
BOOST_TEST(!result); // TODO
BOOST_TEST(!result);
result = parse("L", roman_numerals);
BOOST_TEST(result);
@@ -273,8 +284,8 @@ int main()
BOOST_TEST(*parse("V", roman_numerals) == 5);
auto result = parse("next-delVV", next_delete_parser >> roman_numerals);
BOOST_TEST(result); // TODO
BOOST_TEST(*result == 5); // TODO
BOOST_TEST(result);
BOOST_TEST(*result == 5);
result = parse("V", roman_numerals);
BOOST_TEST(!result);
@@ -305,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));
}
{
@@ -326,12 +337,68 @@ int main()
BOOST_TEST(*parse("L", roman_numerals) == 50);
auto result = parse("next-clearI", next_clear_parser >> roman_numerals);
BOOST_TEST(result); // TODO
BOOST_TEST(*result == 1); // TODO
BOOST_TEST(result);
BOOST_TEST(*result == 1);
BOOST_TEST(!parse("I", roman_numerals));
BOOST_TEST(!parse("L", roman_numerals));
}
{
// parse using symbols directly -- not using the table within a rule
rule_symbols.clear_for_next_parse();
rule_symbols.insert_for_next_parse("I", "one");
rule_symbols.insert_for_next_parse("L", "50");
auto result = parse("I", rule_symbols);
BOOST_TEST(result);
BOOST_TEST(*result == "one");
result = parse("L", rule_symbols);
BOOST_TEST(result);
BOOST_TEST(*result == "50");
BOOST_TEST(!parse("X", rule_symbols));
}
{
// symbols within a rule
rule_symbols.clear_for_next_parse();
rule_symbols.insert_for_next_parse("foo", "foofie");
rule_symbols.insert_for_next_parse("bar", "barrie");
auto result = parse("foo", symrule);
BOOST_TEST(result);
BOOST_TEST(*result == "foofie");
result = parse("bar", symrule);
BOOST_TEST(result);
BOOST_TEST(*result == "barrie");
BOOST_TEST(!parse("X", symrule));
BOOST_TEST(!parse("I", symrule));
}
{
// symbols within a rule w/error handler
rule_symbols.clear_for_next_parse();
rule_symbols.insert_for_next_parse("foo", "foofie");
rule_symbols.insert_for_next_parse("bar", "barrie");
callback_error_handler error_handler(
[](std::string_view m) { std::cout << m << "\n"; });
auto parser = with_error_handler(symrule, error_handler);
auto result = parse("foo", parser);
BOOST_TEST(result);
BOOST_TEST(*result == "foofie");
result = parse("bar", parser);
BOOST_TEST(result);
BOOST_TEST(*result == "barrie");
BOOST_TEST(!parse("baz", parser));
}
}
return boost::report_errors();