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

82 Commits

Author SHA1 Message Date
Andreas Buhr
c54307b95c update CI from macos-13 to macos-15
MacOS 13 is retired in github actions, see
https://github.com/actions/runner-images/issues/13046
2025-12-11 19:32:36 -06:00
Andreas Buhr
5b3f122aa7 use fold expressions in or_parser and seq_parser 2025-12-05 23:48:02 -06:00
Andreas Buhr
6db68bc7f7 add test for constexpr switch_parser
The test led to the following error before:
github_issues.cpp(522): error C3615: constexpr function
'boost::parser::switch_' cannot result in a constant expression
2025-11-25 12:02:18 -06:00
Andreas Buhr
00fe33d2f3 add "constexpr" to switch_parser constructor
so switch(predicate)(valA, parserA)(valB, parserB)
can result in a constexpr object.
2025-11-25 12:02:18 -06:00
Andreas Buhr
bb183cf5ee Improve error visualization.
If the parsed string contains tab characters on the failing line,
the underlining in the error message pointed to the wrong position.

This patch adds as many tab characters to the underlining as
there are in the parsed line. So the underlining points to the
correct position.
2025-11-23 12:14:55 -06:00
Zach Laine
885595f7bd Parse the attribute of quoted_string into a temporary, and assign at the end,
if successful.  This makes the code well-formed when the attribute type is an
optional.

Addresses PR #290.
2025-11-15 16:39:34 -06:00
Zach Laine
6826f957a1 Add missing 'else' between two if constexpr cases. 2025-11-15 15:57:05 -06:00
Zach Laine
647cec6683 Address similar concern to the one about closures with move-only callables,
but this time for adaptors.
2025-10-31 17:30:34 -05:00
Zach Laine
c2ddd6e116 Fix ill-formedness when using range_adaptor_closure with GCC14. 2025-10-31 17:22:36 -05:00
Andreas Buhr
f7246de9db Do not call skip parser if we are already in failed state in seq_parser 2025-10-31 15:41:26 -05:00
Andreas Buhr
703a8afafe Fix error in drone CI
error: call to implicitly-deleted copy constructor of [snip]
constexpr closure(F f) : f_(f) {}
2025-10-31 15:39:46 -05:00
Zach Laine
0eacce6080 Add special-casing of nope attributes in seq_parser, so that seq_parser does
not assign over valid values in a sequence's attribute after successfully
parsing a subsquent nope-attributes parser.

Test cases by Andreas Buhr.

Fixes #279
Fixes #285
2025-10-31 15:36:22 -05:00
Zach Laine
5e61ba4e9e Fix C++17 builds. 2025-10-31 14:25:38 -05:00
Zach Laine
dc6144eeb4 Correct tuple/struct confusion in the logic of the if constexpr chain in
perm_parser.

Test case by Andreas Buhr.

Fixes #268.
2025-10-31 12:37:40 -05:00
Zach Laine
d873d7ea80 Provide failover empty symbol table tries for use inside of skippers, since
nothing prevents using a symbol_parser in a skipper, and the previous commit
introduced nullptr skipper data tables in some cases.

Related to #245
2025-10-13 18:43:08 -05:00
Zach Laine
1f5303c756 When creating the context object in detail::skip(), don't create empty structs
for symbol table tries, since they are never used.

Fixes #245
2025-10-13 18:19:04 -05:00
Zach Laine
41e891dc95 In opt_parser, don't forget to clear the attribute if the subparser fails.
Fixes #279
2025-10-13 17:46:27 -05:00
Zach Laine
159472ac6e Change moves to forwards where appropriate.
Addresses the same issues as PR #276.
2025-10-12 21:45:46 -05:00
Andreas Buhr
bfa3e33372 Reduce compilation time by using SkipParser when determining attribute type.
When using a large parser, the whole tree of parsers in instantiated with the
skip (whitespace) parser used. Then, it was instantiated again without a skip
parser, just to determine the attribute type.
This patch changes the code so the attribute type is determined *with* the
skip parser. That way, the number of template instantiations
required are halved for some use cases.

With gcc 14.2, the instantiations without the skip parser even ended up in the
binary. In that case, this patch reduces binary size.
This is most likely a compiler bug, as the usage is in decltype().
2025-10-12 21:31:32 -05:00
Andreas Buhr
8d7a64f7fe Performance improvement: Do not copy use_parser into lambda 2025-10-12 21:16:48 -05:00
Andreas Buhr
8c23054e07 Fix bug that ASCII code for z/Z was noch included in case_fold fast-path 2025-10-12 21:15:37 -05:00
Andreas Buhr
11a5d9a872 Add fast-path for lower case ASCII letters in case_fold
This improved the speed of my parser by approximately 20%
2025-10-12 21:15:37 -05:00
Andreas Buhr
d241bd7853 Make "transform" constexpr 2025-10-12 21:15:14 -05:00
Zach Laine
086241cbd9 Correct a munged bit of doc text. 2025-10-12 21:04:26 -05:00
ivanpanch
e3a3cc8bf2 Update rationale.qbk 2025-10-12 21:00:47 -05:00
ivanpanch
f789199743 Update parser_reference.xml 2025-10-12 21:00:47 -05:00
ivanpanch
f20c4cfb02 Update parser.hpp 2025-10-12 21:00:47 -05:00
ivanpanch
fcbc53ddce Update parser.hpp 2025-10-12 21:00:47 -05:00
ivanpanch
fed0a883ad Update error_handling_fwd.hpp 2025-10-12 21:00:47 -05:00
ivanpanch
bfc61fa963 Update config.hpp 2025-10-12 21:00:47 -05:00
ivanpanch
68c306bf66 Update json.cpp 2025-10-12 21:00:47 -05:00
ivanpanch
d5d080b9f2 Update tutorial.qbk 2025-10-12 21:00:47 -05:00
ivanpanch
03341ba32d Update tutorial.qbk 2025-10-12 21:00:47 -05:00
ivanpanch
7e69b27d7c Update tutorial.qbk 2025-10-12 21:00:47 -05:00
ivanpanch
17d76bc158 Update tutorial.qbk 2025-10-12 21:00:47 -05:00
ivanpanch
050b9ba800 Update tutorial.qbk 2025-10-12 21:00:47 -05:00
ivanpanch
125f12407b Update tables.qbk 2025-10-12 21:00:47 -05:00
Andreas Buhr
e4ba7c7a17 Remove g++9 github CI configuration
Because it runs on ubuntu 20.04 and github does
not offer these runners anymore.
2025-10-12 20:59:26 -05:00
Zach Laine
9a138a20f6 Correct the claim in the docs that if_(c)[p] has attribute type
optional<ATTR(p)>; it actually has attribute type ATTR(p).

Fixes #278.
2025-10-12 20:40:55 -05:00
Zach Laine
c32d594d64 Eliminate double-instantiation of context templates (due to on a difference
between a nope and nope const GlobalState template arg) by using nope const by
default.

Fixes #250.
2025-10-12 20:00:05 -05:00
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
44 changed files with 6347 additions and 1969 deletions

View File

@@ -1,4 +1,4 @@
name: macos-12 - Clang 14
name: macos-15 - Clang
on:
push:
@@ -14,9 +14,9 @@ jobs:
build:
strategy:
matrix:
cxx_std: [17]
cxx_std: [17, 20]
runs-on: macos-12
runs-on: macos-15
steps:
- uses: actions/checkout@v4

View File

@@ -18,10 +18,6 @@ jobs:
compiler_version: [g++-10, g++-11]
cxx_std: [17, 20]
os: [ubuntu-22.04]
include:
- compiler_version: g++-9
cxx_std: 17
os: ubuntu-20.04
runs-on: ${{ matrix.os }}

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

@@ -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

@@ -202,7 +202,7 @@ It has a different API, and other code that operates on text expects a string
instead of some other container. Arrays of characters are already considered
special by the standard library and common practice in C++.
Second, When you write a parser that parses multiple characters in a row, you
Second, when you write a parser that parses multiple characters in a row, you
are typically trying to produce a string attribute, rather than a few
individual character values. When you use multiple non-character parsers in a
row, you are typically trying to produce multiple values. For instance:
@@ -217,7 +217,7 @@ I've rarely written a parser like `parser_2` and wanted a
`std::vector<std::string>`.
_Parser_ therefore makes the common case the default behavior, and provides
you with the _merge_ and _sep_ directives to let you opt-in to generating the
you with the _merge_ and _sep_ directives to let you opt in to generating the
less-common attributes.
[heading Attribute compatibility rules are more strict than in Spirit]
@@ -261,13 +261,12 @@ Also, Spirit-style looseness is more complicated than `parser` above
indicates. Remember, `int_ | eps` and `-int_` are supposed to be semantically
equivalent. To do otherwise this would be a profound violation of the
principle of least surprise. So, if they're equivalent, we would need to
apply the same rule to `int_ | eps`. Also, we would probably need to apply it
to `if_(cond)[int_]`, which is also a `std::optional<int>`. This is a lot to
remember, and this is complicated to implement and maintain.
apply the same rule to `int_ | eps`. This is a lot to remember, and this is
complicated to implement and maintain.
I've been using Spirit 1 and later Spirit 2 since they were released. I did
not know about the particular looseness discussed here; a user pointed it out
on Github. In many years of using these libraries, I never fully learned all
on GitHub. In many years of using these libraries, I never fully learned all
the attribute-compatibility rules, and was often surprised by them.
Having a small set of rules that the user can internalize is vital; if the
@@ -299,7 +298,7 @@ that it succeeds (if it had failed, it would have cleared its attribute). It
does not know that there is nothing after it that could continue the parse,
nor that it is being used in to do a full parse. So, the over-all parse
fails, but the part of the parse that fills in the out-param attribute does
not know do clear its attribute.
not know to clear its attribute.
This is why the explicit clearing behavior happens at the end of _p_. This is
not without its downsides, though. Consider this.
@@ -315,7 +314,7 @@ not without its downsides, though. Consider this.
Here, the explicit clearing replaces the previous value of `3`, even though
the parser never touched the value! Destroying users' variables' state
without need may seem like a bad idea, but consider the alternative _emdash_
In the previous example, we had spurious values left in the out-param
in the previous example, we had spurious values left in the out-param
attribute. Here, without clearing, we would have had a value left in the
out-param attribute, not because it was a partial result of the parse, but
because the parse never touched it. This is certain to be confusing, or at
@@ -325,4 +324,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`.
@@ -80,7 +80,7 @@ the input they match unless otherwise stated in the table below.]
[[ _attr_np_`(arg0)` ]
[ Always matches, and consumes no input. Generates the attribute `_RES_np_(arg0)`. ]
[ `decltype(_RES_np_(arg0))`. ]
[ An important use case for `_attr_` is to provide a default attribute value as a trailing alternative. For instance, an *optional* comma-delmited list is: `int_ % ',' | attr(std::vector<int>)`. Without the "`| attr(...)`", at least one `int_` match would be required. ]]
[ An important use case for `_attr_` is to provide a default attribute value as a trailing alternative. For instance, an *optional* comma-delimited list is: `int_ % ',' | attr(std::vector<int>)`. Without the "`| attr(...)`", at least one `int_` match would be required. ]]
[[ _ch_ ]
[ Matches any single code point. ]
@@ -132,8 +132,13 @@ 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. ]
[ Matches a single hexadecimal 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)`. ]
@@ -314,7 +319,7 @@ the input they match unless otherwise stated in the table below.]
[[ `_if_np_(pred)[p]` ]
[ Equivalent to `_e_(pred) >> p`. ]
[ `std::optional<_ATTR_np_(p)>` ]
[ `_ATTR_np_(p)` ]
[ It is an error to write `_if_np_(pred)`. That is, it is an error to omit the conditionally matched parser `p`. ]]
[[ `_sw_np_(arg0)(arg1, p1)(arg2, p2) ...` ]
@@ -323,9 +328,9 @@ the input they match unless otherwise stated in the table below.]
[ It is an error to write `_sw_np_(arg0)`. That is, it is an error to omit the conditionally matched parsers `p1`, `p2`, .... ]]
[[ _symbols_t_ ]
[ _symbols_ is an associative container of key, value pairs. Each key is a _std_str_ and each value has type `T`. In the Unicode parsing path, the strings are considered to be UTF-8 encoded; in the non-Unicode path, no encoding is assumed. _symbols_ Matches the longest prefix `pre` of the input that is equal to one of the keys `k`. If the length `len` of `pre` is zero, and there is no zero-length key, it does not match the input. If `len` is positive, the generated attribute is the value associated with `k`.]
[ _symbols_ is an associative container of key, value pairs. Each key is a _std_str_ and each value has type `T`. In the Unicode parsing path, the strings are considered to be UTF-8 encoded; in the non-Unicode path, no encoding is assumed. _symbols_ matches the longest prefix `pre` of the input that is equal to one of the keys `k`. If the length `len` of `pre` is zero, and there is no zero-length key, it does not match the input. If `len` is positive, the generated attribute is the value associated with `k`.]
[ `T` ]
[ Unlike the other entries in this table, _symbols_ is a type, not an object. ]]
[ Unlike the other entries in this table, _symbols_ is a type, not an object. Inside of skippers, all _symbols_ will appear empty. ]]
[[ _quot_str_ ]
[ Matches `'"'`, followed by zero or more characters, followed by `'"'`. ]
@@ -363,7 +368,7 @@ character type (or use _attr_ to do so).]
]
[template table_combining_operations
Here are all the operator overloaded for parsers. In the tables below:
Here are all the operators overloaded for parsers. In the tables below:
* `c` is a character of type `char` or `char32_t`;
@@ -385,16 +390,16 @@ 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. ]]
[[`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 `p2` matches, regardless of the order they match in. ] [`_bp_tup_<_ATTR_np_(p1), _ATTR_np_(p2)>`] [ `||` is associative; `p1 || p2 || p3`, `(p1 || p2) || p3`, and `p1 || (p2 || p3)` are all equivalent. It is an error to include a _e_ (conditional or non-conditional) in an `operator||` expression. Though the parsers are matched in any order, the attribute elements are always in the order written in the `operator||` expression. ]]
[[`p1 || p2`] [ Matches iff `p1` matches and `p2` matches, regardless of the order they match in. ] [`_bp_tup_<_ATTR_np_(p1), _ATTR_np_(p2)>`] [ `||` is associative; `p1 || p2 || p3`, `(p1 || p2) || p3`, and `p1 || (p2 || p3)` are all equivalent. It is an error to include an _e_ (conditional or non-conditional) in an `operator||` expression. Though the parsers are matched in any order, the attribute elements are always in the order written in the `operator||` expression. ]]
[[`p1 - p2`] [ Equivalent to `!p2 >> p1`. ] [`_ATTR_np_(p1)`] []]
[[`p - c`] [ Equivalent to `p - lit(c)`. ] [`_ATTR_np_(p)`] []]
[[`p - r`] [ Equivalent to `p - lit(r)`. ] [`_ATTR_np_(p)`] []]
@@ -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,13 +551,13 @@ 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.]]
[[`_rpt_np_(arg0)[p]`] [`std::string` if `_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p)>`]]
[[`_rpt_np_(arg0, arg1)[p]`] [`std::string` if `_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p)>`]]
[[`_if_np_(pred)[p]`] [`std::optional<_ATTR_np_(p)>`]]
[[`_if_np_(pred)[p]`] [`_ATTR_np_(p)`]]
[[`_sw_np_(arg0)(arg1, p1)(arg2, p2)...`]
[`std::variant<_ATTR_np_(p1), _ATTR_np_(p2), ...>`]]
]

View File

@@ -13,7 +13,7 @@ A /semantic action/ is an arbitrary bit of logic associated with a parser,
that is only executed when the parser matches.
Simpler parsers can be combined to form more complex parsers. Given some
combining operation `C`, and parsers `P0`, `P1`, ... `PN`, `C(P0, P1, ... PN)`
combining operation `C`, and parsers `P0`, `P1`, ..., `PN`, `C(P0, P1, ..., PN)`
creates a new parser `Q`. This creates a /parse tree/. `Q` is the parent of
`P1`, `P2` is the child of `Q`, etc. The parsers are applied in the top-down
fashion implied by this topology. When you use `Q` to parse a string, it
@@ -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
@@ -113,7 +113,7 @@ Also, just ignore for now the fact that _Parser_ somehow figured out that the
result type of the `*bp::char_` parser is a _std_str_. There are clear rules
for this that we'll cover later.
The effects of this call to _p_ is not very interesting _emdash_ since the
The effects of this call to _p_ are not very interesting _emdash_ since the
parser we gave it cannot ever fail, and because we're placing the output in
the same type as the input, it just copies the contents of `input` to
`result`.
@@ -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();
@@ -403,7 +403,7 @@ Copying the entire context when mutating the context is therefore fast. The
context does no memory allocation.
[tip All these functions that take the parse context as their first parameter
will find by found by Argument-Dependent Lookup. You will probably never need
will be found by Argument-Dependent Lookup. You will probably never need
to qualify them with `boost::parser::`.]
[heading Accessors for data that are always available]
@@ -414,7 +414,7 @@ underscore.
[heading _pass_]
_pass_ returns a reference to a `bool` indicating the success of failure of
_pass_ returns a reference to a `bool` indicating the success or failure of
the current parse. This can be used to force the current parse to pass or
fail:
@@ -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
@@ -632,7 +632,7 @@ All this is intended to introduce the notion of _rs_. It still may be a bit
unclear why you would want to use _rs_. The use cases for, and lots of detail
about, _rs_ is in a later section, _more_about_rules_.
[note The existence of _rs_ means that will probably never have to write a
[note The existence of _rs_ means that you will probably never have to write a
low-level parser. You can just put existing parsers together into _rs_
instead.]
@@ -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]
@@ -893,7 +901,7 @@ all parser in its sequence. It then produces its attribute, a
`bp::parse()`.
Something to take note of between Steps #3 and #4: at the beginning of #4, the
input position had returned to where is was at the beginning of #3. This kind
input position had returned to where it was at the beginning of #3. This kind
of backtracking happens in alternative parsers when an alternative fails. The
next page has more details on the semantics of backtracking.
@@ -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
@@ -1285,7 +1296,7 @@ instance, `lexeme[a >> b] >> c` is a _seq_p_ containing two parsers, `lexeme[a
in a _lex_p_. This in turn turns off the sequence parser combining logic,
since both sides of the second `operator>>` in `lexeme[a >> b] >> c` are not
_seq_ps_. Sequence parsers have several rules that govern what the overall
attribute type of the parser is, based on the positions and attributes of it
attribute type of the parser is, based on the positions and attributes of its
subparsers (see _attr_gen_). Therefore, it's important to know which
directives create a new parser (and what kind), and which ones do not; this is
indicated for each directive below.
@@ -1317,7 +1328,7 @@ Creates an _omt_p_.
[heading _raw_]
`_raw_np_[p]` changes the attribute from `_ATTR_np_(p)` to to a view that
`_raw_np_[p]` changes the attribute from `_ATTR_np_(p)` to a view that
delimits the subrange of the input that was matched by `p`. The type of the
view is `_v_<I>`, where `I` is the type of the iterator used within the parse.
Note that this may not be the same as the iterator type passed to _p_. For
@@ -1383,7 +1394,7 @@ need to support the multi-expanding code points, use the other overload, like:
and if you do it to two bits of text `A` and `B`, then you can compare them
bitwise to see if they are the same, except of case. Case folding may
sometimes expand a code point into multiple code points (e.g. case folding
`"ẞ"` yields `"ss"`. When such a multi-code point expansion occurs, the
`"ẞ"` yields `"ss"`). When such a multi-code point expansion occurs, the
expanded code points are in the NFKC normalization form.]
Creates a _noc_p_.
@@ -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]
@@ -1481,7 +1510,7 @@ pretty useless. You should never see this type in practice. Within semantic
actions, asking for the attribute of a non-attribute-producing parser (using
`_attr(ctx)`) will yield a value of the special type `boost::parser::none`.
When calling _p_ in a form that returns the attribute parsed, when there is no
attribute, simply returns `bool`; this indicates the success of failure of the
attribute, simply returns `bool`; this indicates the success or failure of the
parse.]
[warning _Parser_ assumes that all attributes are semi-regular (see
@@ -1590,9 +1619,74 @@ 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
The rules for alternative parsers are much simpler. For an alternative parser
`p`, let the list of attribute types for the subparsers of `p` be `a0, a1, a2,
..., an`. The attribute of `p` is `std::variant<a0, a1, a2, ..., an>`, with
the following steps applied:
@@ -1600,8 +1694,8 @@ the following steps applied:
* all the `none` attributes are left out, and if any are, the attribute is
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;
* duplicates in the `std::variant` template parameters `<T1, T2, ..., Tn>` are
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
@@ -1696,7 +1790,7 @@ subparsers in the sequence parser to use the same variable for their
attribute.
Another directive, _sep_, also applies only to sequence parsers, but does the
opposite of _merge_. If forces all the attributes produced by the subparsers
opposite of _merge_. It forces all the attributes produced by the subparsers
of the sequence parser to stay separate, even if they would have combined.
For instance, consider this parser.
@@ -1750,7 +1844,7 @@ and _sep_ do not. Since they operate only on sequence parsers, all they do is
create a copy of the sequence parser they are given. The _seq_p_ template has
a template parameter `CombiningGroups`, and all _merge_ and _sep_ do is take a
given _seq_p_ and create a copy of it with a different `CombiningGroups`
template parameter. This means that _merge_ and _sep_ are can be ignored in
template parameter. This means that _merge_ and _sep_ can be ignored in
`operator>>` expressions much like parentheses are. Consider an example.
namespace bp = boost::parser;
@@ -1773,7 +1867,7 @@ given function `f`. For example:
Here, we have a function `str_sum` that we use for `f`. It assumes each
character in the given _std_str_ `s` is a digit, and returns the sum of all
the digits in `s`. Out parser `parser` would normally return a _std_str_.
the digits in `s`. Our parser `parser` would normally return a _std_str_.
However, since `str_sum` returns a different type _emdash_ `int` _emdash_ that
is the attribute type of the full parser,
`bp::transform(by_value_str_sum)[parser]`, as you can see from the
@@ -1806,7 +1900,7 @@ common:
* They each return a value contextually convertible to `bool`.
* They each take at least a range to parse and a parser. The "range to parse"
may be an iterator/sentinel pair or an single range object.
may be an iterator/sentinel pair or a single range object.
* They each require forward iterability of the range to parse.
@@ -2147,7 +2241,7 @@ encoding. Here is how it deduces which case the call falls under:
* Otherwise, the input is in a UTF encoding.
[tip if you want to want to parse in ASCII-only mode, or in some other
[tip If you want to parse in ASCII-only mode, or in some other
non-Unicode encoding, use only sequences of `char`, like _std_str_ or `char
const *`.]
@@ -2171,7 +2265,7 @@ _eh_debugging_ section of the tutorial for details.
[heading Globals and error handlers]
Each call to _p_ can optionally have a globals object associated with it. To
use a particular globals object with you parser, you call _w_glb_ to create a
use a particular globals object with your parser, you call _w_glb_ to create a
new parser with the globals object in it:
struct globals_t
@@ -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
@@ -2369,11 +2469,12 @@ defined somewhere.
BOOST_PARSER_DEFINE_RULES(value);
Notice the two expectation points. One before `(value % ',')`, one before the
final `'}'`. Later, you call parse in some input:
final `'}'`. Later, you parse in some input:
bp::parse("{ 4, 5 a", value, bp::ws);
This runs should of the second expectation point, and produces output like this:
This runs afoul of the second expectation point, and produces output like
this:
[pre
1:7: error: Expected '}' here:
@@ -2393,8 +2494,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 though 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;
@@ -2420,7 +2521,7 @@ message:
]
The _r_ `value` might be useful elsewhere in our code, perhaps in another
parser. It's diagnostic text is appropriate for those other potential uses.
parser. Its diagnostic text is appropriate for those other potential uses.
[heading Recursive rules]
@@ -2471,7 +2572,7 @@ of a recursive _r_. This is because each instance of the rule needs a place
to put the attribute it generates from its parse. However, we only want a
single return value for the uppermost rule; if each instance had a separate
value in `_val(ctx)`, then it would be impossible to build up the result of a
recursive rule step by step during in the evaluation of the recursive
recursive rule step by step during the evaluation of the recursive
instantiations.
Also, consider this rule:
@@ -2480,7 +2581,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 +2589,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
@@ -2523,7 +2624,7 @@ use each other without introducing cycles:
BOOST_PARSER_DEFINE_RULES(string, object_element, object, array, value);
Here we have a parser for a Javascript-value-like type `value_type`.
Here we have a parser for a JavaScript-value-like type `value_type`.
`value_type` may be an array, which itself may contain other arrays, objects,
strings, etc. Since we need to be able to parse objects within arrays and
vice versa, we need each of those two parsers to be able to refer to each
@@ -2561,8 +2662,8 @@ semantics, is a lot easier to read, and is a lot less code.]
[heading Locals]
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
You can pass a third parameter `LocalState` to _r_, which will be default
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.
@@ -2673,7 +2774,7 @@ rewritten as:
auto const foo_def = bp::repeat(bp::_p<0>)[' '_l];
Using __p_ can prevent you from having to write a bunch of lambdas that get
Using __p_ can prevent you from having to write a bunch of lambdas that
each get an argument out of the parse context using `_params_np_(ctx)[0_c]` or
similar.
@@ -3060,7 +3161,7 @@ worrying if the input is Unicode or not because, under the covers, what takes
place is a simple comparison of two integral values.
[note _Parser_ actually promotes any two values to a common type using
`std::common_type` before comparing them. This is almost always works because
`std::common_type` before comparing them. This almost always works because
the input and any parameter passed to _ch_ must be character types. ]
Since matches are always done at a code point level (remember, a "code point"
@@ -3302,9 +3403,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 +3413,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.
@@ -3336,22 +3442,23 @@ is not very useful. Consider this.
auto c_b = bp::char_('c') >> bp::char_('b');
auto result = bp::parse("acb", a_b | c_b);
If we reported the farthest-reaching parser and it's position, it would be the
If we reported the farthest-reaching parser and its position, it would be the
`a_b` parser, at position `"bc"` in the input. Is this really enlightening?
Was the error in the input putting the `'a'` at the beginning or putting the
`'c'` in the middle? If you point the user at `a_b` as the parser that
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 +3561,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,9 +3609,9 @@ 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:
JavaScript value can have; the top-level parser in the JSON parser example is:
auto const value_p_def =
number | bp::bool_ | null | string | array_p | object_p;
@@ -3689,10 +3796,10 @@ _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
failed expectation situation, _Parser_ does not allocate unless you
tell it to, by using _symbols_, using a particular error_handler, turning on
trace, or parsing into attributes that allocate.
@@ -3700,7 +3807,7 @@ trace, or parsing into attributes that allocate.
[section Best Practices]
[heading Parse unicode from the start]
[heading Parse Unicode from the start]
If you want to parse ASCII, using the Unicode parsing API will not actually
cost you anything. Your input will be parsed, `char` by `char`, and compared
@@ -3766,9 +3873,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]
@@ -3847,7 +3954,7 @@ First, let's look at the template and function parameters.
`true` if the parse succeeds, and `false` otherwise.
Now the body of the function. Notice that it just dispatches to the other
`call()` overload. This is really common, since both overloads need to to the
`call()` overload. This is really common, since both overloads need to do the
same parsing; only the attribute may differ. The first line of the body
defines `attr_t`, the default attribute type of our wrapped parser `parser_`.
It does this by getting the `decltype()` of a use of `parser_.call()`. (This

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

@@ -5,7 +5,7 @@
// http://www.boost.org/LICENSE_1_0.txt)
//[ extended_json_example
// This header includes a type called json::value that acts as a
// Javascript-like polymorphic value type.
// JavaScript-like polymorphic value type.
#include "json.hpp"
#include <boost/parser/parser.hpp>
@@ -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>> &&
@@ -57,7 +59,7 @@ namespace boost { namespace parser {
std::declval<bool &>(),
std::declval<int &>(),
std::declval<ErrorHandler const &>(),
std::declval<detail::nope &>(),
std::declval<detail::nope const &>(),
std::declval<detail::symbol_table_tries_t &>(),
std::declval<detail::pending_symbol_table_operations_t &>()));

View File

@@ -16,19 +16,19 @@
/** Boost.Parser uses assertions (`BOOST_ASSERT()`) in several places to
indicate that your use of the library has an error in it. All of those
places could heve instead been ill-formed code, caught at compile time.
places could have instead been ill-formed code, caught at compile time.
It is far quicker and easier to determine exactly where in your code such
an error is located if this is a runtime failure; you can just look at the
stack in your favorite debugger. However, if you want to make thes kinds
stack in your favorite debugger. However, if you want to make these kinds
of errors always ill-formed code, define this macro. */
# define BOOST_PARSER_NO_RUNTIME_ASSERTIONS
/** Asserts that the given condition is true. If
`BOOST_PARSER_NO_RUNTIME_ASSERTIONS` macro is defined by the user,
`BOOST_PARSER_ASSERT` expends to a compile-time `static_assert()`.
`BOOST_PARSER_ASSERT` expands to a compile-time `static_assert()`.
Otherwise, it expands to a run-time `BOOST_ASSERT()`. Note that defining
`BOOST_DISABLE_ASSERTS` disables the use of C `assert`, even when
`BOOST_ASSERT` is unavailble. */
`BOOST_ASSERT` is unavailable. */
# define BOOST_PARSER_ASSERT(condition)
/** Boost.Parser will automatically use concepts to constrain templates when
@@ -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

@@ -47,7 +47,10 @@ namespace boost::parser::detail {
// One-byte fast path.
if (cp < 0x100) {
// ASCII letter fast path.
if (0x41 <= cp && cp < 0x5a) {
if (0x61 <= cp && cp <= 0x7a) {
*out++ = cp;
return out;
} else if (0x41 <= cp && cp <= 0x5a) {
*out++ = cp + 0x20;
return out;
} else if (cp == 0x00DF) {

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,12 @@
#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_STL_INTERFACES_USE_CPP23_STD_RANGE_ADAPTOR_CLOSURE && \
BOOST_STL_INTERFACES_USE_CONCEPTS && defined(BOOST_GCC) && 14 <= __GNUC__
#define BOOST_PARSER_USE_LIBSTDCPP_GCC14_RANGE_ADAPTOR_CLOSURE 1
#elif !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
@@ -197,6 +201,11 @@ namespace boost::parser::detail { namespace stl_interfaces {
template<typename D>
using range_adaptor_closure = std::ranges::range_adaptor_closure<D>;
#elif BOOST_PARSER_USE_LIBSTDCPP_GCC14_RANGE_ADAPTOR_CLOSURE
template<typename D>
using range_adaptor_closure = std::views::__adaptor::_RangeAdaptorClosure<D>;
#elif BOOST_PARSER_USE_LIBSTDCPP_GCC12_RANGE_ADAPTOR_CLOSURE
template<typename D>
@@ -258,7 +267,7 @@ namespace boost::parser::detail { namespace stl_interfaces {
template<typename F>
struct closure : range_adaptor_closure<closure<F>>
{
constexpr closure(F f) : f_(f) {}
constexpr closure(F f) : f_(std::move(f)) {}
#if BOOST_PARSER_DETAIL_STL_INTERFACES_USE_CONCEPTS
template<typename T>
@@ -326,25 +335,22 @@ namespace boost::parser::detail { namespace stl_interfaces {
template<typename F>
struct adaptor
{
constexpr adaptor(F f) : f_(f) {}
constexpr adaptor(F f) : f_(std::move(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> &&
@@ -125,7 +126,7 @@ namespace boost::parser::detail::text::detail {
else if constexpr (can_ref_view<R>)
return ref_view(r);
else
return owning_view<T>(std::move(r));
return owning_view<T>((R &&)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

View File

@@ -90,6 +90,9 @@ namespace boost { namespace parser {
os << ":\n";
std::string underlining(std::distance(position.line_start, it), ' ');
std::transform(position.line_start, it,
underlining.begin(),
[](auto c) { return c == '\t' ? '\t' : ' ';});
detail::trace_input(os, position.line_start, it, false, 1u << 31);
if (it == last) {
os << '\n' << underlining << "^\n";

View File

@@ -104,7 +104,7 @@ namespace boost { namespace parser {
/** The error handler used when the user does not specify a custom one.
This error handler prints warnings and errors to `std::cerr`, and does
not have an associcated filename. */
not have an associated filename. */
struct default_error_handler
{
constexpr default_error_handler() = default;

File diff suppressed because it is too large Load Diff

View File

@@ -115,7 +115,7 @@ namespace boost { namespace parser {
bool & success,
int & indent,
ErrorHandler const & error_handler,
nope &,
nope const &,
symbol_table_tries_t & symbol_table_tries,
pending_symbol_table_operations_t &
pending_symbol_table_operations) noexcept;
@@ -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

@@ -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,326 @@ 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';
}
}
}
#if BOOST_PARSER_USE_CONCEPTS
namespace github_issue_268_ {
namespace bp = boost::parser;
constexpr bp::rule<struct name, std::string_view> name = "name";
auto name_def = bp::string_view[bp::lexeme[+(
bp::lower | bp::upper | bp::digit | bp::char_("_"))]];
BOOST_PARSER_DEFINE_RULES(name)
constexpr bp::rule<struct qd_vec, std::vector<double>> qd_vec = "qd_vec";
auto qd_vec_def = bp::lit("\"") >>
bp::double_ %
(bp::lit(",") |
(bp::lit("\"") >> bp::lit(",") >> bp::lit("\""))) >>
bp::lit('\"');
BOOST_PARSER_DEFINE_RULES(qd_vec)
struct lu_table_template_1
{
std::vector<double> index_1;
std::string_view variable_1;
};
constexpr boost::parser::
rule<struct lu_table_template_1_tag, lu_table_template_1>
lu_table_template_1_rule = "lu_table_template_1";
auto lu_table_template_1_rule_def = (bp::lit("index_1") >> '(' >> qd_vec >>
')' >> ';') >>
(bp::lit("variable_1") >> ':' >> name >>
';');
BOOST_PARSER_DEFINE_RULES(lu_table_template_1_rule)
constexpr boost::parser::
rule<struct lu_table_template_1_permut_tag, lu_table_template_1>
lu_table_template_1_permut_rule = "lu_table_template_1";
auto lu_table_template_1_permut_rule_def =
(bp::lit("index_1") >> '(' >> qd_vec >> ')' >> ';') ||
(bp::lit("variable_1") >> ':' >> name >> ';');
BOOST_PARSER_DEFINE_RULES(lu_table_template_1_permut_rule)
}
#endif
void github_issue_268()
{
#if BOOST_PARSER_USE_CONCEPTS
namespace bp = boost::parser;
using namespace github_issue_268_;
std::string inputstring = "index_1 ( \"1\" ) ; variable_1 : bier;";
auto const def_result = bp::parse(
inputstring, lu_table_template_1_rule_def, bp::blank, bp::trace::off);
std::cout << "seq_parser generates this type:\n"
<< typeid(def_result.value()).name() << std::endl;
BOOST_TEST(def_result);
auto const permut_def_result = bp::parse(
inputstring,
lu_table_template_1_permut_rule_def,
bp::blank,
bp::trace::off);
std::cout << "permut_parser generates this type:\n"
<< typeid(permut_def_result.value()).name() << std::endl;
BOOST_TEST(permut_def_result);
auto const result = bp::parse(
inputstring, lu_table_template_1_rule, bp::blank, bp::trace::off);
std::cout << "seq_parser in rule generates this type:\n"
<< typeid(result.value()).name() << std::endl;
BOOST_TEST(result);
auto const permut_result = bp::parse(
inputstring,
lu_table_template_1_permut_rule,
bp::blank,
bp::trace::off);
std::cout << "permut_parser generates this type:\n"
<< typeid(permut_result.value()).name() << std::endl;
BOOST_TEST(permut_result);
#endif
}
void github_issue_279()
{
namespace bp = boost::parser;
{
constexpr auto condition_clause =
bp::lit(U"while") > bp::lit(U"someexpression") >> bp::attr(true);
constexpr auto do_statement =
bp::lexeme[bp::lit(U"do") >> &bp::ws] > -condition_clause > bp::eol;
auto const result =
bp::parse(U"do\n", do_statement, bp::blank, bp::trace::off);
BOOST_TEST(result);
std::optional<bool> const & condition = result.value();
BOOST_TEST(!condition.has_value());
}
{
constexpr auto condition_clause =
bp::lit(U"while") > bp::lit(U"someexpression") >> bp::attr(true);
constexpr auto do_statement_reverse =
-condition_clause > bp::lexeme[bp::lit(U"do") >> &bp::ws] > bp::eol;
auto const result =
bp::parse(U"do\n", do_statement_reverse, bp::blank, bp::trace::off);
BOOST_TEST(result);
std::optional<bool> const & condition = result.value();
BOOST_TEST(!condition.has_value());
}
}
namespace github_issue_285_ {
namespace bp = boost::parser;
struct Content
{
~Content()
{
int setbreakpointhere = 0;
(void)setbreakpointhere;
}
};
constexpr bp::rule<struct content_tag, std::shared_ptr<Content>> content =
"content";
constexpr auto content_action = [](auto & ctx) {
std::shared_ptr<Content> & result = _val(ctx);
result = std::make_shared<Content>();
};
constexpr auto content_def =
(bp::lit(U"content") >> bp::eol)[content_action];
BOOST_PARSER_DEFINE_RULES(content);
}
void github_issue_285()
{
using namespace github_issue_285_;
namespace bp = boost::parser;
constexpr auto prolog = bp::lit(U"prolog") >> bp::eol;
constexpr auto epilog =
bp::no_case[bp::lexeme[bp::lit(U"epi") >> bp::lit(U"log")]] >> bp::eol;
constexpr auto full_parser = prolog >> content >> epilog;
std::string teststring =
"prolog\n"
"content\n"
"epilog\n";
// "content" produces a shared_ptr with the result.
// The "epilog" parser must not delete the result.
auto const result = bp::parse(teststring, full_parser, bp::blank);
BOOST_TEST(result);
BOOST_TEST(result.value().get() != nullptr);
}
void github_pr_290()
{
namespace bp = boost::parser;
auto const pTest = bp::lit("TEST:") > -bp::quoted_string;
auto result = bp::parse("TEST: \"foo\"", pTest, bp::blank);
BOOST_TEST(result);
BOOST_TEST(*result == "foo");
}
namespace github_pr_297_ {
namespace bp = boost::parser;
constexpr auto bar_required_f = [](auto& ctx) -> bool {
const bool& argument = bp::_p<0>(ctx);
return argument;
};
constexpr bp::rule<struct foobar_parser_tag> foobar_parser = "foobar_parser";
constexpr auto foobar_parser_def =
bp::lit("foo")
>> bp::switch_(bar_required_f)
(true, bp::lit("bar"))
(false, -bp::lit("bar"));
BOOST_PARSER_DEFINE_RULES(foobar_parser);
}
void github_pr_297()
{
namespace bp = boost::parser;
using namespace github_pr_297_;
{
const bool bar_required = true;
const bool result =
bp::parse("foo bar", foobar_parser.with(bar_required), bp::blank);
BOOST_TEST(result);
}
{
const bool bar_required = true;
const bool result =
bp::parse("foo", foobar_parser.with(bar_required), bp::blank);
BOOST_TEST(!result);
}
{
const bool bar_required = false;
const bool result =
bp::parse("foo bar", foobar_parser.with(bar_required), bp::blank);
BOOST_TEST(result);
}
{
const bool bar_required = false;
const bool result =
bp::parse("foo", foobar_parser.with(bar_required), bp::blank);
BOOST_TEST(result);
}
}
int main()
{
@@ -246,5 +566,13 @@ int main()
github_issue_78();
github_issue_90();
github_issue_125();
github_issue_209();
github_issue_223();
github_issue_248();
github_issue_268();
github_issue_279();
github_issue_285();
github_pr_290();
github_pr_297();
return boost::report_errors();
}

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

@@ -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));
}
{