mirror of
https://github.com/boostorg/parser.git
synced 2026-01-20 16:52:13 +00:00
Compare commits
49 Commits
gh-pages
...
boost-1.89
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c674e94c3d | ||
|
|
84ee288b02 | ||
|
|
39faa9ddbe | ||
|
|
b2927abc6c | ||
|
|
5d6d2f7b84 | ||
|
|
fd6c56df1b | ||
|
|
af41e6a7c2 | ||
|
|
0b93a586f1 | ||
|
|
ed9a06123b | ||
|
|
8ff46f394a | ||
|
|
8c9ad7bdb3 | ||
|
|
d8abe8f29e | ||
|
|
810adb43f6 | ||
|
|
5788fb6967 | ||
|
|
ec7df8a0af | ||
|
|
a93a1d2647 | ||
|
|
927f35f115 | ||
|
|
87617fdec0 | ||
|
|
ead639e630 | ||
|
|
a3ca1193b2 | ||
|
|
07153117ff | ||
|
|
6414f99e04 | ||
|
|
78bc141d5f | ||
|
|
b253d9ca53 | ||
|
|
0a34acc42a | ||
|
|
56c81c0b57 | ||
|
|
821d1d4c08 | ||
|
|
57cdd78210 | ||
|
|
3993efb692 | ||
|
|
74bc8fc1bb | ||
|
|
b42b052df4 | ||
|
|
42c9d82419 | ||
|
|
958ac38256 | ||
|
|
354586dd76 | ||
|
|
305bba875b | ||
|
|
f468d529fe | ||
|
|
b5d4339f2c | ||
|
|
6d7fa6f105 | ||
|
|
c975f57908 | ||
|
|
063291b78c | ||
|
|
3eb827dcd6 | ||
|
|
6d796287b6 | ||
|
|
bb0fb885b8 | ||
|
|
94a9daec40 | ||
|
|
4344dd3f47 | ||
|
|
a7c7470bc1 | ||
|
|
b273133fd2 | ||
|
|
3a7ddcf936 | ||
|
|
d79efb0daa |
@@ -1,4 +1,4 @@
|
||||
name: macos-12 - Clang 14
|
||||
name: macos-13 - Clang 14
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
matrix:
|
||||
cxx_std: [17]
|
||||
|
||||
runs-on: macos-12
|
||||
runs-on: macos-13
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -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)
|
||||
|
||||
15
README.md
15
README.md
@@ -34,12 +34,7 @@ int main()
|
||||
}
|
||||
```
|
||||
|
||||
This library is header-only, and has a default dependency on Boost.Hana. The
|
||||
Boost.Hana dependency can be eliminated, and `std::tuple` will be used instead
|
||||
of `boost::hana::tuple` throughout the library, if you `#define`
|
||||
`BOOST_PARSER_DISABLE_HANA_TUPLE`. To try out the lib without mseeing with
|
||||
dependencies, add its `include/` dir as an include path in your build and
|
||||
define `BOOST_PARSER_DISABLE_HANA_TUPLE` your build.
|
||||
This library is header-only, and has no Boost dependencies by default.
|
||||
|
||||
Features:
|
||||
|
||||
@@ -52,9 +47,7 @@ Features:
|
||||
- Trace support for debugging your parsers.
|
||||
- Clever hacks to make compile time errors easier to deal with. (These are totally optional.)
|
||||
|
||||
This library targets submission to Boost.
|
||||
|
||||
Online docs: https://tzlaine.github.io/parser
|
||||
This library first appeared in Boost 1.87.0
|
||||
|
||||
Master status:
|
||||
|
||||
@@ -64,7 +57,7 @@ Master status:
|
||||
|
||||
[](https://github.com/tzlaine/parser/actions/workflows/windows.yml)
|
||||
|
||||
[](https://github.com/tzlaine/parser/actions/workflows/macos-12.yml)
|
||||
[](https://github.com/tzlaine/parser/actions/workflows/macos-13.yml)
|
||||
|
||||
Develop status:
|
||||
|
||||
@@ -74,6 +67,6 @@ Develop status:
|
||||
|
||||
[](https://github.com/tzlaine/parser/actions/workflows/windows.yml)
|
||||
|
||||
[](https://github.com/tzlaine/parser/actions/workflows/macos-12.yml)
|
||||
[](https://github.com/tzlaine/parser/actions/workflows/macos-13.yml)
|
||||
|
||||
[](LICENSE_1_0.txt)
|
||||
|
||||
24
build.jam
Normal file
24
build.jam
Normal 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
|
||||
;
|
||||
@@ -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 ;
|
||||
|
||||
@@ -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
2145
doc/parser_reference.xml
Normal file
File diff suppressed because it is too large
Load Diff
@@ -325,4 +325,24 @@ always equal to `A()` if the parser fails. It is equal to whatever the parser
|
||||
sets it to _emdash_ or its previous value, if the parser does not mutate it
|
||||
_emdash_ if the parse succeeds.
|
||||
|
||||
[heading There are no _Spirit_-style character class parsers]
|
||||
|
||||
_Spirit_ has these character class parsers that recognize the same set of
|
||||
characters as the C standard library's character class functions. For
|
||||
instance, _Spirit_'s `alnum` recognizes the characters recognized by
|
||||
`std::isalnum()`, its `punct` recognizes the characters recognized by
|
||||
`std::ispunct()`, etc.
|
||||
|
||||
The problem with this is that those `std::is*()` functions are badly broken.
|
||||
They do not even work correctly for ASCII values. This is because they use
|
||||
the C standard library's locale mechanism, which can be set to anything the
|
||||
current platform supports, and can be set by any code anywhere in your
|
||||
program; the locale is mutable global state. So, even if you use the default
|
||||
"C locale in your program, if you link against a library that sets the locale
|
||||
to something that breaks ASCII character recognition (an EBCDIC locale, for
|
||||
instance), your program is now incorrect, regardless of the code you wrote.
|
||||
|
||||
For this reason, I firmly believe that no one, anywhere, should use those C
|
||||
functions in production code, and I am not supporting their use via _Parser_.
|
||||
|
||||
[endsect]
|
||||
|
||||
@@ -34,9 +34,9 @@ itself be used as a parser; it must be called. In the table below:
|
||||
|
||||
* `a` is a semantic action;
|
||||
|
||||
* `r` is an object whose type models `parsable_range`; and
|
||||
* `r` is an object whose type models `parsable_range`;
|
||||
|
||||
* `p`, `p1`, `p2`, ... are parsers.
|
||||
* `p`, `p1`, `p2`, ... are parsers; and
|
||||
|
||||
* `escapes` is a _symbols_t_ object, where `T` is `char` or `char32_t`.
|
||||
|
||||
@@ -132,6 +132,11 @@ the input they match unless otherwise stated in the table below.]
|
||||
[ The code point type in Unicode parsing, or `char` in non-Unicode parsing. See the entry for _ch_. ]
|
||||
[]]
|
||||
|
||||
[[ `_symb_` ]
|
||||
[ Matches a single symbol code point. ]
|
||||
[ The code point type in Unicode parsing, or `char` in non-Unicode parsing. See the entry for _ch_. ]
|
||||
[]]
|
||||
|
||||
[[ `_hex_digit_` ]
|
||||
[ Matches a single hexidecimal digit code point. ]
|
||||
[ The code point type in Unicode parsing, or `char` in non-Unicode parsing. See the entry for _ch_. ]
|
||||
@@ -225,7 +230,7 @@ the input they match unless otherwise stated in the table below.]
|
||||
[[ _ui_ ]
|
||||
[ Matches an unsigned integral value. ]
|
||||
[ `unsigned int` ]
|
||||
[]]
|
||||
[ To specify a base/radix of `N`, use _ui_`.base<N>()`. To specify exactly `D` digits, use _ui_`.digits<D>()`. To specify a minimum of `LO` digits and a maximum of `HI` digits, use _ui_`.digits<LO, HI>()`. These calls can be chained, as in _ui_`.base<2>().digits<8>()`. ]]
|
||||
|
||||
[[ `_ui_(arg0)` ]
|
||||
[ Matches exactly the unsigned integral value `_RES_np_(arg0)`. ]
|
||||
@@ -265,7 +270,7 @@ the input they match unless otherwise stated in the table below.]
|
||||
[[ _i_ ]
|
||||
[ Matches a signed integral value. ]
|
||||
[ `int` ]
|
||||
[]]
|
||||
[ To specify a base/radix of `N`, use _i_`.base<N>()`. To specify exactly `D` digits, use _i_`.digits<D>()`. To specify a minimum of `LO` digits and a maximum of `HI` digits, use _i_`.digits<LO, HI>()`. These calls can be chained, as in _i_`.base<2>().digits<8>()`. ]]
|
||||
|
||||
[[ `_i_(arg0)` ]
|
||||
[ Matches exactly the signed integral value `_RES_np_(arg0)`. ]
|
||||
@@ -385,10 +390,10 @@ consume the input they match unless otherwise stated in the table below.]
|
||||
[[`*p`] [ Parses using `p` repeatedly until `p` no longer matches; always matches. ] [`std::string` if `_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p)>`] [ Matching _e_ an unlimited number of times creates an infinite loop, which is undefined behavior in C++. _Parser_ will assert in debug mode when it encounters `*_e_` (this applies to unconditional _e_ only). ]]
|
||||
[[`+p`] [ Parses using `p` repeatedly until `p` no longer matches; matches iff `p` matches at least once. ] [`std::string` if `_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p)>`] [ Matching _e_ an unlimited number of times creates an infinite loop, which is undefined behavior in C++. _Parser_ will assert in debug mode when it encounters `+_e_` (this applies to unconditional _e_ only). ]]
|
||||
[[`-p`] [ Equivalent to `p | _e_`. ] [`std::optional<_ATTR_np_(p)>`] []]
|
||||
[[`p1 >> p2`] [ Matches iff `p1` matches and then `p2` matches. ] [`_bp_tup_<_ATTR_np_(p1), _ATTR_np_(p2)>` (See note.)] [ `>>` is associative; `p1 >> p2 >> p3`, `(p1 >> p2) >> p3`, and `p1 >> (p2 >> p3)` are all equivalent. This attribute type only applies to the case where `p1` and `p2` both generate attributes; see _attr_gen_ for the full rules. ]]
|
||||
[[`p1 >> p2`] [ Matches iff `p1` matches and then `p2` matches. ] [`_bp_tup_<_ATTR_np_(p1), _ATTR_np_(p2)>` (See note.)] [ `>>` is associative; `p1 >> p2 >> p3`, `(p1 >> p2) >> p3`, and `p1 >> (p2 >> p3)` are all equivalent. This attribute type only applies to the case where `p1` and `p2` both generate attributes; see _attr_gen_ for the full rules. Differs in precedence from `operator>`. ]]
|
||||
[[`p >> c`] [ Equivalent to `p >> lit(c)`. ] [`_ATTR_np_(p)`] []]
|
||||
[[`p >> r`] [ Equivalent to `p >> lit(r)`. ] [`_ATTR_np_(p)`] []]
|
||||
[[`p1 > p2`] [ Matches iff `p1` matches and then `p2` matches. No back-tracking is allowed after `p1` matches; if `p1` matches but then `p2` does not, the top-level parse fails. ] [`_bp_tup_<_ATTR_np_(p1), _ATTR_np_(p2)>` (See note.)] [ `>` is associative; `p1 > p2 > p3`, `(p1 > p2) > p3`, and `p1 > (p2 > p3)` are all equivalent. This attribute type only applies to the case where `p1` and `p2` both generate attributes; see _attr_gen_ for the full rules. ]]
|
||||
[[`p1 > p2`] [ Matches iff `p1` matches and then `p2` matches. No back-tracking is allowed after `p1` matches; if `p1` matches but then `p2` does not, the top-level parse fails. ] [`_bp_tup_<_ATTR_np_(p1), _ATTR_np_(p2)>` (See note.)] [ `>` is associative; `p1 > p2 > p3`, `(p1 > p2) > p3`, and `p1 > (p2 > p3)` are all equivalent. This attribute type only applies to the case where `p1` and `p2` both generate attributes; see _attr_gen_ for the full rules. Differs in precedence from `operator>>`. ]]
|
||||
[[`p > c`] [ Equivalent to `p > lit(c)`. ] [`_ATTR_np_(p)`] []]
|
||||
[[`p > r`] [ Equivalent to `p > lit(r)`. ] [`_ATTR_np_(p)`] []]
|
||||
[[`p1 | p2`] [ Matches iff either `p1` matches or `p2` matches. ] [`std::variant<_ATTR_np_(p1), _ATTR_np_(p2)>` (See note.)] [ `|` is associative; `p1 | p2 | p3`, `(p1 | p2) | p3`, and `p1 | (p2 | p3)` are all equivalent. This attribute type only applies to the case where `p1` and `p2` both generate attributes, and where the attribute types are different; see _attr_gen_ for the full rules. ]]
|
||||
@@ -424,6 +429,15 @@ because, for any parser `p`, `_e_ | p` is equivalent to _e_, since _e_ always
|
||||
matches. This is not true for _e_ parameterized with a condition. For any
|
||||
condition `cond`, `_e_(cond)` is allowed to appear anywhere within an
|
||||
alternative parser.
|
||||
|
||||
[important The C++ operators `>` and `>>` have different precedences. This
|
||||
will sometimes come up in warnings from your compiler. No matter how you do
|
||||
or do not parenthesize chains of parsers separated by `>` and `>>`, the
|
||||
resulting expression evaluates the same. Feel free to add parentheses if your
|
||||
compiler complains. More broadly, keep the C++ operator precedence rules in
|
||||
mind when writing your parsers _emdash_ the simplest thing to write may not
|
||||
have your intended semantics. ]
|
||||
|
||||
]
|
||||
|
||||
[template table_attribute_generation
|
||||
@@ -491,7 +505,7 @@ attribute type is `char32_t`:
|
||||
static_assert(std::is_same_v<decltype(result), std::optional<char32_t>>));
|
||||
|
||||
The good news is that usually you don't parse characters individually. When
|
||||
you parse with _ch_, you usually parse repetition of then, which will produce
|
||||
you parse with _ch_, you usually parse repetition of them, which will produce
|
||||
a _std_str_, regardless of whether you're in Unicode parsing mode or not. If
|
||||
you do need to parse individual characters, and want to lock down their
|
||||
attribute type, you can use _cp_ and/or _cu_ to enforce a non-polymorphic
|
||||
@@ -537,7 +551,7 @@ tables below:
|
||||
[[`p1 || p2`] [`_bp_tup_<_ATTR_np_(p1), _ATTR_np_(p2)>`]]
|
||||
[[`p1 || p2 || p3`] [`_bp_tup_<_ATTR_np_(p1), _ATTR_np_(p2), _ATTR_np_(p3)>`]]
|
||||
|
||||
[[`p1 % p2`] [`std::string` if `_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p1)>`]]
|
||||
[[`p1 % p2`] [`std::string` if `_ATTR_np_(p1)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p1)>`]]
|
||||
|
||||
[[`p[a]`] [None.]]
|
||||
|
||||
|
||||
227
doc/tutorial.qbk
227
doc/tutorial.qbk
@@ -65,7 +65,7 @@ subparsers does.
|
||||
|
||||
Finally, there is a /permutation parser/; it is created using `operator||`,
|
||||
as in `p1 || p2 || p3`. A permutation parser tries to match all of its
|
||||
subparsers to the input, in any order. So the parser `p1 || p2 || p3` is equivalent to `(p1 >> p2 >> p3) | (p1 >> p3 >> p2) | (p2 >> p1 >> p3) | (p2 >> p3 >> p1) | (p3 >> p1 >> p2) | (p3 >> p2 >> p1)`. Hopefully its terseness is self-explanatory. It matches the
|
||||
subparsers to the input, in any order. So the parser `p1 || p2 || p3` is equivalent to `(p1 >> p2 >> p3) | (p1 >> p3 >> p2) | (p2 >> p1 >> p3) | (p2 >> p3 >> p1) | (p3 >> p1 >> p2) | (p3 >> p2 >> p1)`. Hopefully the advantage of its terseness is self-explanatory. It matches the
|
||||
input iff all of its subparsers do, regardless of the order they match in.
|
||||
|
||||
_Parser_ parsers each have an /attribute/ associated with them, or explicitly
|
||||
@@ -351,7 +351,7 @@ so this directive is only available in C++20 and later.
|
||||
|
||||
namespace bp = boost::parser;
|
||||
auto int_parser = bp::int_ % ','; // ATTR(int_parser) is std::vector<int>
|
||||
auto sv_parser = bp::string_view[int_parser]; // ATTR(subrange_parser) is a string_view
|
||||
auto sv_parser = bp::string_view[int_parser]; // ATTR(sv_parser) is a string_view
|
||||
|
||||
auto const str = std::string("1, 2, 3, 4, a, b, c");
|
||||
auto first = str.begin();
|
||||
@@ -584,7 +584,7 @@ things:
|
||||
|
||||
* This rule object itself is called `doubles`.
|
||||
|
||||
* We've given `doubles` the diagnstic text `"doubles"` so that _Parser_ knows
|
||||
* We've given `doubles` the diagnostic text `"doubles"` so that _Parser_ knows
|
||||
how to refer to it when producing a trace of the parser during debugging.
|
||||
|
||||
Ok, so if `doubles` is a parser, what does it do? We define the rule's
|
||||
@@ -828,7 +828,7 @@ the same character must be used on both sides.
|
||||
[quoted_string_example_4]
|
||||
|
||||
Another common thing to do in a quoted string parser is to recognize escape
|
||||
sequences. If you have simple escape sequencecs that do not require any real
|
||||
sequences. If you have simple escape sequences that do not require any real
|
||||
parsing, like say the simple escape sequences from C++, you can provide a
|
||||
_symbols_ object as well. The template parameter `T` to _symbols_t_ must be
|
||||
`char` or `char32_t`. You don't need to include the escaped backslash or the
|
||||
@@ -836,6 +836,14 @@ escaped quote character, since those always work.
|
||||
|
||||
[quoted_string_example_5]
|
||||
|
||||
Additionally, with each of the forms shown above, you can optionally provide a
|
||||
parser as a final argument, which will be used to parse each character inside
|
||||
the quotes. You have to provide an actual full parser here; you cannot
|
||||
provide a character or string literal. If you do not provide a character
|
||||
parser, _ch_ is used.
|
||||
|
||||
[quoted_string_example_6]
|
||||
|
||||
[endsect]
|
||||
|
||||
[section Parsing In Detail]
|
||||
@@ -1126,24 +1134,12 @@ the second `'X'` is recognized by the symbol table parser. However:
|
||||
|
||||
If we parse again, we find that `"X"` did not stay in the symbol table. The
|
||||
fact that `symbols` was declared const might have given you a hint that this
|
||||
would happen. Also, notice that the call to `insert()` in the semantic action
|
||||
uses the parse context; that's where all the symbol table changes are stored
|
||||
during the parse.
|
||||
would happen.
|
||||
|
||||
The full program:
|
||||
|
||||
[self_filling_symbol_table_example]
|
||||
|
||||
[tip _symbols_ also has a call operator that does exactly what
|
||||
`.insert_for_next_parse()` does. This allows you to chain additions with a
|
||||
convenient syntax, like this:
|
||||
|
||||
```
|
||||
symbols<int> roman_numerals;
|
||||
roman_numerals.insert_for_next_parse("I", 1)("V", 5)("X", 10);
|
||||
```
|
||||
]
|
||||
|
||||
[important _symbols_ stores all its strings in UTF-32 internally. If you do
|
||||
Unicode or ASCII parsing, this will not matter to you at all. If you do
|
||||
non-Unicode parsing of a character encoding that is not a subset of Unicode
|
||||
@@ -1163,6 +1159,11 @@ erase and clear for the current parse, and another that applies only to
|
||||
subsequent parses. The full set of operations can be found in the _symbols_
|
||||
API docs.
|
||||
|
||||
[note There are two versions of each of the _symbols_ `*_for_next_parse()`
|
||||
functions _emdash_ one that takes a context, and one that does not. The one
|
||||
with the context is meant to be used within a semantic action. The one
|
||||
without the context is for use outside of any parse.]
|
||||
|
||||
[endsect]
|
||||
|
||||
[section The Parsers And Their Uses]
|
||||
@@ -1247,22 +1248,25 @@ these parsers is in a subsequent section. The attributes are repeated here so
|
||||
you can use see all the properties of the parsers in one place.]
|
||||
|
||||
If you have an integral type `IntType` that is not covered by any of the
|
||||
_Parser_ parsers, you can use a more verbose declaration to declare a parser
|
||||
for `IntType`. If `IntType` were unsigned, you would use `uint_parser`. If
|
||||
it were signed, you would use `int_parser`. For example:
|
||||
_Parser_ parsers, you can explicitly specify a base/radix or bounds on the
|
||||
number of digits. You do this by calling the `base()` and `digits()` member
|
||||
functions on an existing parser of the right integral type. So if `IntType`
|
||||
were unsigned, you would use `uint_`. If it were signed, you would use
|
||||
`int_`. For example:
|
||||
|
||||
constexpr parser_interface<int_parser<IntType>> hex_int;
|
||||
constexpr auto hex_int = bp::uint_.base<16>();
|
||||
|
||||
`uint_parser` and `int_parser` accept three more non-type template parameters
|
||||
after the type parameter. They are `Radix`, `MinDigits`, and `MaxDigits`.
|
||||
`Radix` defaults to `10`, `MinDigits` to `1`, and `MaxDigits` to `-1`, which
|
||||
is a sentinel value meaning that there is no max number of digits.
|
||||
You simply chain together the constraints you want to use, like
|
||||
`.base<16>().digits<2>()` or .digits<4>().base<8>()`.
|
||||
|
||||
So, if you wanted to parse exactly eight hexadecimal digits in a row in order
|
||||
to recognize Unicode character literals like C++ has (e.g. `\Udeadbeef`), you
|
||||
could use this parser for the digits at the end:
|
||||
|
||||
constexpr parser_interface<uint_parser<unsigned int, 16, 8, 8>> hex_int;
|
||||
constexpr auto hex_4_def = bp::uint_.base<16>().digits<8>();
|
||||
|
||||
If you want to specify an acceptable range of digits, use `.digits<LO, HI>()`.
|
||||
Both `HI` and `LO` are inclusive bounds.
|
||||
|
||||
[endsect]
|
||||
|
||||
@@ -1282,7 +1286,7 @@ parsers; we won't say much about them here.
|
||||
[heading Interaction with sequence, alternative, and permutation parsers]
|
||||
|
||||
Sequence, alternative, and permutation parsers do not nest in most cases.
|
||||
(Let's consider just sequence parsers to keep thinkgs simple, but most of this
|
||||
(Let's consider just sequence parsers to keep things simple, but most of this
|
||||
logic applies to alternative parsers as well.) `a >> b >> c` is the same as
|
||||
`(a >> b) >> c` and `a >> (b >> c)`, and they are each represented by a single
|
||||
_seq_p_ with three subparsers, `a`, `b`, and `c`. However, if something
|
||||
@@ -1449,6 +1453,24 @@ _merge_ and _sep_ create a copy of the given _seq_p_.
|
||||
|
||||
_transform_ creates a _xfm_p_.
|
||||
|
||||
[heading _delimiter_]
|
||||
|
||||
The _delimiter_np_ directive enables the use of a delimiter within a
|
||||
permutation parser. It *only* applies to permutation parsers, just as _merge_
|
||||
and _sep_ only apply to sequence parsers. Consider this permutation parser.
|
||||
|
||||
constexpr auto parser = bp::int_ || bp::string("foo") || bp::char_('g');
|
||||
|
||||
This will match all of: an integer, `"foo"`, and `'g'`, in any order (for
|
||||
example, `"foo g 42"`). If you also want for those three elements to be
|
||||
delimited by commas, you could write this parser instead.
|
||||
|
||||
constexpr auto delimited_parser =
|
||||
bp::delimiter(bp::lit(','))[bp::int_ || bp::string("foo") || bp::char_('g')];
|
||||
|
||||
`delimited_parser` will parse the same elements as `parser`, but will also
|
||||
require commas between the elements (as in `"foo, g, 42"`).
|
||||
|
||||
[endsect]
|
||||
|
||||
[section Combining Operations]
|
||||
@@ -1597,6 +1619,71 @@ attribute becomes `T`.
|
||||
[container_concept]
|
||||
]
|
||||
|
||||
[heading A sequence parser attribute example]
|
||||
|
||||
Note that the application of `OP` is done in the style of a left-fold, and
|
||||
is therefore greedy. This can lead to some non-obvious results. For example,
|
||||
consider this program. Thanks to Duncan Paterson for this very nice example!
|
||||
|
||||
#include <boost/parser/parser.hpp>
|
||||
#include <print>
|
||||
|
||||
namespace bp = boost::parser;
|
||||
int main() {
|
||||
const auto id_set_action = [](auto &ctx) {
|
||||
const auto& [left, right] = _attr(ctx);
|
||||
std::println("{} = {}", left, right);
|
||||
};
|
||||
|
||||
const auto id_parser = bp::char_('a', 'z') > *bp::char_('a', 'z');
|
||||
|
||||
const auto id_set = (id_parser >> '=' >> id_parser)[id_set_action];
|
||||
bp::parse("left=right", id_set);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Perhaps surprisingly, this program prints `leftr = ight`! Why is this? This
|
||||
happens because `id_parser` seems to impose structure, but does not. `id_set`
|
||||
is exactly equivalent to this (comments added to clarify which parts are which
|
||||
below).
|
||||
|
||||
const auto id_set = (
|
||||
/*A*/ bp::char_('a', 'z') > /*B*/ *bp::char_('a', 'z') >>
|
||||
/*C*/ '=' >>
|
||||
/*D*/ bp::char_('a', 'z') > /*E*/ *bp::char_('a', 'z')
|
||||
)[id_set_action];
|
||||
|
||||
As _Parser_ applies `OP` to this sequence parser, the individual steps are:
|
||||
`A` and `B` get merged into a single _std_str_; `C` is ignored, since it
|
||||
produces no attribute; and `D` gets merged into the _std_str_ formed earlier
|
||||
by `A` and `B`; finally, we have `E`. `E` does not combine with `D`, as `D`
|
||||
was already consumed. `E` also does not combine with the _std_str_ we formed
|
||||
from `A`, `B`, and `D`, since we don't combine adjacent containers. In the
|
||||
end, we have a 2-tuple of _std_strs_, in which the first element contains all
|
||||
the characters parsed by `A`, `B`, and `D`, and in which the second element
|
||||
contains all the characters parsed by `E`.
|
||||
|
||||
That's clearly not what we wanted here, though. How do we get a top-level
|
||||
parser that would print `left = right`? We use a _r_. The parser used inside
|
||||
a _r_ can never combine with any parser(s) outside the _r_. Instances of a
|
||||
rule are inherently separate from all parsers with which they are used,
|
||||
whether those parsers are _rs_ or non-_r_ parsers. So, consider a _r_
|
||||
equivalent to the previous `id_parser` above.
|
||||
|
||||
namespace bp = boost::parser;
|
||||
bp::rule<struct id_parser_tag, std::string> id_parser = "identifier";
|
||||
auto const id_parser_def = bp::char_('a', 'z') > *bp::char_('a', 'z');
|
||||
BOOST_PARSER_DEFINE_RULES(id_parser);
|
||||
|
||||
Later, we can use it just as we used the previous non-rule version.
|
||||
|
||||
const auto id_set = (id_parser >> '=' >> id_parser)[id_set_action];
|
||||
|
||||
This produces the results you might expect, since only the `bp::char_('a',
|
||||
'z') > *bp::char_('a', 'z')` parser inside the `id_parser` _r_ is ever
|
||||
eligible for combining via `OP`.
|
||||
|
||||
|
||||
[heading Alternative parser attribute rules]
|
||||
|
||||
The rules for alternative parsers are much simpler. For an alternative parer
|
||||
@@ -1608,7 +1695,7 @@ the following steps applied:
|
||||
wrapped in a `std::optional`, like `std::optional<std::variant</*...*/>>`;
|
||||
|
||||
* duplicates in the `std::variant` template parameters `<T1, T2, ... Tn>` are
|
||||
removed; every type that appears does so exacly once;
|
||||
removed; every type that appears does so exactly once;
|
||||
|
||||
* if the attribute is `std::variant<T>` or `std::optional<std::variant<T>>`,
|
||||
the attribute becomes instead `T` or `std::optional<T>`, respectively; and
|
||||
@@ -2218,6 +2305,8 @@ common use cases for _rs_. Use a _r_ if you want to:
|
||||
* fix the attribute type produced by a parser to something other than the
|
||||
default;
|
||||
|
||||
* control the attributes generated by adjacent sequence parsers;
|
||||
|
||||
* create a parser that produces useful diagnostic text;
|
||||
|
||||
* create a recursive rule (more on this below);
|
||||
@@ -2358,6 +2447,10 @@ action if:
|
||||
|
||||
The notion of "compatible" is defined in _p_api_.
|
||||
|
||||
[heading Controlling the attributes generated]
|
||||
|
||||
See the _seq_parser_example_ in the _attr_gen_ section for details.
|
||||
|
||||
[heading Creating a parser for better diagnostics]
|
||||
|
||||
Each _r_ has associated diagnostic text that _Parser_ can use for failures of
|
||||
@@ -2400,8 +2493,8 @@ the earlier expectation:
|
||||
]
|
||||
|
||||
Not nearly as nice. The problem is that the expectation is on `(value %
|
||||
',')`. So, even thought we gave `value` reasonable dianostic text, we put the
|
||||
text on the wrong thing. We can introduce a new rule to put the diagnstic
|
||||
',')`. So, even thought we gave `value` reasonable diagnostic text, we put the
|
||||
text on the wrong thing. We can introduce a new rule to put the diagnostic
|
||||
text in the right place.
|
||||
|
||||
namespace bp = boost::parser;
|
||||
@@ -2487,7 +2580,7 @@ Also, consider this rule:
|
||||
bp::rule<struct ints_tag, std::vector<int>> ints = "ints";
|
||||
auto const ints_def = bp::int_ >> ints | bp::eps;
|
||||
|
||||
What is the default attribute type for ints_def? It sure looks like
|
||||
What is the default attribute type for `ints_def`? It sure looks like
|
||||
`std::optional<std::vector<int>>`. Inside the evaluation of `ints`, _Parser_
|
||||
must evaluate `ints_def`, and then produce a `std::vector<int>` _emdash_ the
|
||||
return type of `ints` _emdash_ from it. How? How do you turn a
|
||||
@@ -2495,7 +2588,7 @@ return type of `ints` _emdash_ from it. How? How do you turn a
|
||||
seems obvious, but the metaprogramming that properly handles this simple
|
||||
example and the general case is certainly beyond me.
|
||||
|
||||
_Parser_ has a specific semantic for what consitutes a recursive rule. Each
|
||||
_Parser_ has a specific semantic for what constitutes a recursive rule. Each
|
||||
rule has a tag type associated with it, and if _Parser_ enters a rule with a
|
||||
certain tag `Tag`, and the currently-evaluating rule (if there is one) also
|
||||
has the tag `Tag`, then rule instance being entered is considered to be a
|
||||
@@ -2569,7 +2662,7 @@ semantics, is a lot easier to read, and is a lot less code.]
|
||||
|
||||
The _r_ template takes another template parameter we have not discussed yet.
|
||||
You can pass a third parameter `LocalState` to _r_, which will be defaulted
|
||||
csontructed by the _r_, and made available within semantic actions used in the
|
||||
constructed by the _r_, and made available within semantic actions used in the
|
||||
rule as `_locals_np_(ctx)`. This gives your rule some local state, if it
|
||||
needs it. The type of `LocalState` can be anything regular. It could be a
|
||||
single value, a struct containing multiple values, or a tuple, among others.
|
||||
@@ -3309,9 +3402,9 @@ _w_eh_ (see _p_api_). If you do not set one, _default_eh_ will be used.
|
||||
[heading How diagnostics are generated]
|
||||
|
||||
_Parser_ only generates error messages like the ones in this page at failed
|
||||
expectation points, like `a > b`, where you have successfully parsed `a`, but
|
||||
then cannot successfully parse `b`. This may seem limited to you. It's
|
||||
actually the best that we can do.
|
||||
expectation points (like `a > b`, where you have successfully parsed `a`, but
|
||||
then cannot successfully parse `b`), and at an unexpected end of input. This
|
||||
may seem limited to you. It's actually the best that we can do.
|
||||
|
||||
In order for error handling to happen other than at expectation points, we
|
||||
have to know that there is no further processing that might take place. This
|
||||
@@ -3319,21 +3412,26 @@ is true because _Parser_ has `P1 | P2 | ... | Pn` parsers ("`or_parser`s").
|
||||
If any one of these parsers `Pi` fails to match, it is not allowed to fail the
|
||||
parse _emdash_ the next one (`Pi+1`) might match. If we get to the end of the
|
||||
alternatives of the or_parser and `Pn` fails, we still cannot fail the
|
||||
top-level parse, because the `or_parser` might be a subparser within a parent
|
||||
`or_parser`.
|
||||
top-level parse, because this `or_parser` might be a subparser within a parent
|
||||
`or_parser`. The only exception to this is when: we have finished the
|
||||
top-level parse; the top-level parse is *not* a prefix parse; and there is
|
||||
still a part of the input range that is left over. In that case, there is an
|
||||
implicit expectation that the end of the parse and the end of input are the
|
||||
same location, and this implicit expectation has just been violated.
|
||||
|
||||
Ok, so what might we do? Perhaps we could at least indicate when we ran into
|
||||
end-of-input. But we cannot, for exactly the same reason already stated. For
|
||||
any parser `P`, reaching end-of-input is a failure for `P`, but not
|
||||
necessarily for the whole parse.
|
||||
Note that we cannot fail the top-level parse when we run into end-of-input.
|
||||
We cannot for exactly the same reason already stated. For any parser `P`,
|
||||
reaching end-of-input is a failure for `P`, but not necessarily for the whole
|
||||
parse.
|
||||
|
||||
Perhaps we could record the farthest point ever reached during the parse, and
|
||||
report that at the top level, if the top level parser fails. That would be
|
||||
little help without knowing which parser was active when we reached that
|
||||
point. This would require some sort of repeated memory allocation, since in
|
||||
_Parser_ the progress point of the parser is stored exclusively on the stack
|
||||
_emdash_ by the time we fail the top-level parse, all those far-reaching stack
|
||||
frames are long gone. Not the best.
|
||||
Ok, so what other kinds of error reporting might we do? Perhaps we could
|
||||
record the farthest point ever reached during the parse, and report that at
|
||||
the top level, if the top level parser fails. That would be little help
|
||||
without knowing which parser was active when we reached that point. This
|
||||
would require some sort of repeated memory allocation, since in _Parser_ the
|
||||
progress point of the parser is stored exclusively on the stack _emdash_ by
|
||||
the time we fail the top-level parse, all those far-reaching stack frames are
|
||||
long gone. Not the best.
|
||||
|
||||
Worse still, knowing how far you got in the parse and which parser was active
|
||||
is not very useful. Consider this.
|
||||
@@ -3350,15 +3448,16 @@ Was the error in the input putting the `'a'` at the beginning or putting the
|
||||
failed, and never mention `c_b`, you are potentially just steering them in the
|
||||
wrong direction.
|
||||
|
||||
All error messages must come from failed expectation points. Consider parsing
|
||||
JSON. If you open a list with `'['`, you know that you're parsing a list, and
|
||||
if the list is ill-formed, you'll get an error message saying so. If you open
|
||||
an object with `'{'`, the same thing is possible _emdash_ when missing the
|
||||
matching `'}'`, you can tell the user, "That's not an object", and this is
|
||||
useful feedback. The same thing with a partially parsed number, etc. If the
|
||||
JSON parser does not build in expectations like matched braces and brackets,
|
||||
how can _Parser_ know that a missing `'}'` is really a problem, and that no
|
||||
later parser will match the input even without the `'}'`?
|
||||
All error messages must come from failed expectation points (or unexpected end
|
||||
of input). Consider parsing JSON. If you open a list with `'['`, you know
|
||||
that you're parsing a list, and if the list is ill-formed, you'll get an error
|
||||
message saying so. If you open an object with `'{'`, the same thing is
|
||||
possible _emdash_ when missing the matching `'}'`, you can tell the user,
|
||||
"That's not an object", and this is useful feedback. The same thing with a
|
||||
partially parsed number, etc. If the JSON parser does not build in
|
||||
expectations like matched braces and brackets, how can _Parser_ know that a
|
||||
missing `'}'` is really a problem, and that no later parser will match the
|
||||
input even without the `'}'`?
|
||||
|
||||
[important The bottom line is that you should build expectation points into
|
||||
your parsers using `operator>` as much as possible.]
|
||||
@@ -3461,7 +3560,7 @@ We just define a `logging_error_handler`, and pass it by reference to _w_eh_,
|
||||
which decorates the top-level parser with the error handler. We *could not*
|
||||
have written `bp::with_error_handler(parser,
|
||||
logging_error_handler("parse.log"))`, because _w_eh_ does not accept rvalues.
|
||||
This is becuse the error handler eventually goes into the parse context. The
|
||||
This is because the error handler eventually goes into the parse context. The
|
||||
parse context only stores pointers and iterators, keeping it cheap to copy.
|
||||
|
||||
If we run the example and give it the input `"1,"`, this shows up in the log
|
||||
@@ -3509,7 +3608,7 @@ to `_trace_::off`.
|
||||
|
||||
If we trace a substantial parser, we will see a *lot* of output. Each code
|
||||
point of the input must be considered, one at a time, to see if a certain rule
|
||||
matches. An an example, let's trace a parse using the JSON parser from
|
||||
matches. As an example, let's trace a parse using the JSON parser from
|
||||
_ex_json_. The input is `"null"`. `null` is one of the types that a
|
||||
Javascript value can have; the top-level parser in the JSON parser example is:
|
||||
|
||||
@@ -3696,7 +3795,7 @@ _Parser_ seldom allocates memory. The exceptions to this are:
|
||||
which implies allocation. You can avoid this allocation by explicitly using
|
||||
a different sequence container for the attribute that does not allocate.
|
||||
`boost::container::static_vector` or C++26's `std::inplace_vector` may be
|
||||
useful as such replacements.
|
||||
useful for such replacements.
|
||||
|
||||
With the exception of allocating the name of the parser that was expected in a
|
||||
failed expectation situation, _Parser_ does not does not allocate unless you
|
||||
@@ -3773,9 +3872,9 @@ Some things to note:
|
||||
want to know how to fix their input. For either rule, the fix is the same:
|
||||
put a hexadecimal escape sequence there.
|
||||
|
||||
- `single_escaped_char` has a terrible-looking name. However, it's not really
|
||||
used as a name anywhere per se. In error messages, it works nicely, though.
|
||||
The error will be "Expected '"', '\', '/', 'b', 'f', 'n', 'r', or 't' here",
|
||||
- `single_escaped_char` has a terrible-looking name. However, it's not
|
||||
actually used as a name. In error messages, it works nicely, though. The
|
||||
error will be "Expected '"', '\', '/', 'b', 'f', 'n', 'r', or 't' here",
|
||||
which is pretty helpful.
|
||||
|
||||
[heading Have a simple test that you can run to find ill-formed-code-as-asserts]
|
||||
|
||||
@@ -126,7 +126,7 @@ namespace json {
|
||||
}
|
||||
};
|
||||
|
||||
bp::parser_interface<bp::uint_parser<uint32_t, 16, 4, 4>> const hex_4_def;
|
||||
auto const hex_4_def = boost::parser::uint_.base<16>().digits<4>();
|
||||
|
||||
auto const escape_seq_def = "\\u" > hex_4;
|
||||
|
||||
|
||||
@@ -151,12 +151,10 @@ namespace json {
|
||||
}
|
||||
};
|
||||
|
||||
// This is the verbose form of declaration for the integer and unsigned
|
||||
// integer parsers int_parser and uint_parser. In this case, we don't
|
||||
// want to use boost::parser::hex directly, since it has a variable number
|
||||
// of digits. We want to match exactly 4 digits, and this is how we
|
||||
// declare a hexadecimal parser that matches exactly 4.
|
||||
bp::parser_interface<bp::uint_parser<uint32_t, 16, 4, 4>> const hex_4_def;
|
||||
// We don't want to use boost::parser::hex directly, since it has a
|
||||
// variable number of digits. We want to match exactly 4 digits, and this
|
||||
// is how we declare a hexadecimal parser that matches exactly 4.
|
||||
auto const hex_4_def = boost::parser::uint_.base<16>().digits<4>();
|
||||
|
||||
// We use > here instead of >>, because once we see \u, we know that
|
||||
// exactly four hex digits must follow -- no other production rule starts
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -31,9 +31,11 @@ namespace boost { namespace parser {
|
||||
std::forward_iterator<T> && code_unit<std::iter_value_t<T>>;
|
||||
|
||||
//[ parsable_range_like_concept
|
||||
//[ parsable_range_concept
|
||||
template<typename T>
|
||||
concept parsable_range = std::ranges::forward_range<T> &&
|
||||
code_unit<std::ranges::range_value_t<T>>;
|
||||
//]
|
||||
|
||||
template<typename T>
|
||||
concept parsable_pointer = std::is_pointer_v<std::remove_cvref_t<T>> &&
|
||||
@@ -58,7 +60,8 @@ namespace boost { namespace parser {
|
||||
std::declval<int &>(),
|
||||
std::declval<ErrorHandler const &>(),
|
||||
std::declval<detail::nope &>(),
|
||||
std::declval<detail::symbol_table_tries_t &>()));
|
||||
std::declval<detail::symbol_table_tries_t &>(),
|
||||
std::declval<detail::pending_symbol_table_operations_t &>()));
|
||||
|
||||
template<typename T, typename I, typename S, typename GlobalState>
|
||||
concept error_handler =
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -24,8 +24,9 @@
|
||||
#define BOOST_PARSER_USE_CPP23_STD_RANGE_ADAPTOR_CLOSURE 0
|
||||
#endif
|
||||
|
||||
#if !BOOST_PARSER_USE_CPP23_STD_RANGE_ADAPTOR_CLOSURE && \
|
||||
BOOST_PARSER_DETAIL_STL_INTERFACES_USE_CONCEPTS && defined(__GNUC__) && 12 <= __GNUC__
|
||||
#if !BOOST_PARSER_USE_CPP23_STD_RANGE_ADAPTOR_CLOSURE && \
|
||||
BOOST_PARSER_DETAIL_STL_INTERFACES_USE_CONCEPTS && \
|
||||
defined(BOOST_PARSER_GCC) && 12 <= __GNUC__
|
||||
#define BOOST_PARSER_USE_LIBSTDCPP_GCC12_RANGE_ADAPTOR_CLOSURE 1
|
||||
#else
|
||||
#define BOOST_PARSER_USE_LIBSTDCPP_GCC12_RANGE_ADAPTOR_CLOSURE 0
|
||||
@@ -328,23 +329,20 @@ namespace boost::parser::detail { namespace stl_interfaces {
|
||||
{
|
||||
constexpr adaptor(F f) : f_(f) {}
|
||||
|
||||
// clang-format off
|
||||
template<typename... Args>
|
||||
constexpr auto operator()(Args &&... args) const
|
||||
// clang-format on
|
||||
{
|
||||
#if BOOST_PARSER_DETAIL_STL_INTERFACES_USE_CONCEPTS
|
||||
if constexpr (std::is_invocable_v<F const &, Args...>) {
|
||||
return f_((Args &&) args...);
|
||||
return f_((Args &&)args...);
|
||||
} else {
|
||||
return closure(
|
||||
stl_interfaces::bind_back(f_, (Args &&) args...));
|
||||
return closure(stl_interfaces::bind_back(f_, (Args &&)args...));
|
||||
}
|
||||
#else
|
||||
return detail::adaptor_impl<
|
||||
F const &,
|
||||
detail::is_invocable_v<F const &, Args...>,
|
||||
Args...>::call(f_, (Args &&) args...);
|
||||
Args...>::call(f_, (Args &&)args...);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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> &&
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -88,10 +88,12 @@ namespace boost::parser::detail { namespace text {
|
||||
{
|
||||
V base_ = V();
|
||||
|
||||
template<bool Const>
|
||||
class iterator;
|
||||
// HACK: SentType is here to work around irritating big-3
|
||||
// implementation inconsistencies.
|
||||
template<bool Const>
|
||||
class sentinel;
|
||||
template<bool Const, typename SentType = sentinel<Const>>
|
||||
class iterator;
|
||||
|
||||
public:
|
||||
constexpr project_view()
|
||||
@@ -140,7 +142,7 @@ namespace boost::parser::detail { namespace text {
|
||||
#else
|
||||
template<typename V, typename F>
|
||||
#endif
|
||||
template<bool Const>
|
||||
template<bool Const, typename SentType>
|
||||
class project_view<V, F>::iterator
|
||||
: public boost::parser::detail::stl_interfaces::proxy_iterator_interface<
|
||||
iterator<Const>,
|
||||
@@ -161,7 +163,7 @@ namespace boost::parser::detail { namespace text {
|
||||
decltype(detail::function_for_tag<F>(0))
|
||||
#endif
|
||||
;
|
||||
using sentinel = project_view<V, F>::sentinel<Const>;
|
||||
using sentinel = SentType;
|
||||
|
||||
friend boost::parser::detail::stl_interfaces::access;
|
||||
iterator_type & base_reference() noexcept { return it_; }
|
||||
@@ -169,7 +171,7 @@ namespace boost::parser::detail { namespace text {
|
||||
|
||||
iterator_type it_ = iterator_type();
|
||||
|
||||
friend project_view<V, F>::sentinel<Const>;
|
||||
friend project_view<V, F>::template sentinel<Const>;
|
||||
|
||||
template<bool OtherConst>
|
||||
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS
|
||||
@@ -423,49 +425,57 @@ namespace boost::parser::detail { namespace text {
|
||||
#if defined(__cpp_char8_t)
|
||||
inline constexpr detail::as_charn_impl<char8_view, format::utf8> as_char8_t;
|
||||
#endif
|
||||
inline constexpr detail::as_charn_impl<char16_view, format::utf16> as_char16_t;
|
||||
inline constexpr detail::as_charn_impl<char32_view, format::utf32> as_char32_t;
|
||||
inline constexpr detail::as_charn_impl<char16_view, format::utf16>
|
||||
as_char16_t;
|
||||
inline constexpr detail::as_charn_impl<char32_view, format::utf32>
|
||||
as_char32_t;
|
||||
|
||||
// clang-format off
|
||||
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS
|
||||
template<utf_range V>
|
||||
requires std::ranges::view<V> && std::ranges::forward_range<V>
|
||||
requires std::ranges::view<V> && std::ranges::forward_range<V>
|
||||
#else
|
||||
template<typename V>
|
||||
#endif
|
||||
class unpacking_view : public stl_interfaces::view_interface<unpacking_view<V>> {
|
||||
V base_ = V();
|
||||
class unpacking_view
|
||||
: public stl_interfaces::view_interface<unpacking_view<V>>
|
||||
{
|
||||
V base_ = V();
|
||||
|
||||
public:
|
||||
constexpr unpacking_view()
|
||||
constexpr unpacking_view()
|
||||
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS
|
||||
requires std::default_initializable<V>
|
||||
requires std::default_initializable<V>
|
||||
#endif
|
||||
= default;
|
||||
constexpr unpacking_view(V base) : base_(std::move(base)) {}
|
||||
= default;
|
||||
constexpr unpacking_view(V base) : base_(std::move(base)) {}
|
||||
|
||||
constexpr V base() const &
|
||||
constexpr V base() const &
|
||||
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS
|
||||
requires std::copy_constructible<V>
|
||||
requires std::copy_constructible<V>
|
||||
#endif
|
||||
{ return base_; }
|
||||
constexpr V base() && { return std::move(base_); }
|
||||
{
|
||||
return base_;
|
||||
}
|
||||
constexpr V base() && { return std::move(base_); }
|
||||
|
||||
constexpr auto code_units() const noexcept {
|
||||
auto unpacked = boost::parser::detail::text::unpack_iterator_and_sentinel(detail::begin(base_), detail::end(base_));
|
||||
return BOOST_PARSER_DETAIL_TEXT_SUBRANGE(unpacked.first, unpacked.last);
|
||||
}
|
||||
constexpr auto code_units() const noexcept
|
||||
{
|
||||
auto unpacked =
|
||||
boost::parser::detail::text::unpack_iterator_and_sentinel(
|
||||
detail::begin(base_), detail::end(base_));
|
||||
return BOOST_PARSER_DETAIL_TEXT_SUBRANGE(
|
||||
unpacked.first, unpacked.last);
|
||||
}
|
||||
|
||||
constexpr auto begin() { return code_units().begin(); }
|
||||
constexpr auto begin() const { return code_units().begin(); }
|
||||
constexpr auto begin() { return code_units().begin(); }
|
||||
constexpr auto begin() const { return code_units().begin(); }
|
||||
|
||||
constexpr auto end() { return code_units().end(); }
|
||||
constexpr auto end() const { return code_units().end(); }
|
||||
constexpr auto end() { return code_units().end(); }
|
||||
constexpr auto end() const { return code_units().end(); }
|
||||
};
|
||||
|
||||
template<class R>
|
||||
unpacking_view(R &&) -> unpacking_view<detail::all_t<R>>;
|
||||
// clang-format on
|
||||
|
||||
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS
|
||||
template<format Format, utf_range V>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -11,6 +11,7 @@
|
||||
|
||||
#include <any>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
@@ -84,12 +85,24 @@ namespace boost { namespace parser {
|
||||
{
|
||||
std::any trie_;
|
||||
bool has_case_folded_;
|
||||
bool pending_operations_;
|
||||
};
|
||||
|
||||
using symbol_table_tries_t =
|
||||
std::map<void *, symbol_table_trie_element, std::less<void *>>;
|
||||
|
||||
using pending_symtab_ops_visitor = std::function<void()>;
|
||||
struct pending_symtab_ops_entry
|
||||
{
|
||||
pending_symtab_ops_visitor visit_;
|
||||
// Contains std::vector<detail::symbol_table_operation<T>> (T is
|
||||
// known to visit_).
|
||||
std::any ops_;
|
||||
};
|
||||
using pending_symbol_table_operations_t = std::map<
|
||||
void const *,
|
||||
pending_symtab_ops_entry,
|
||||
std::less<void const *>>;
|
||||
|
||||
template<
|
||||
bool DoTrace,
|
||||
bool UseCallbacks,
|
||||
@@ -103,7 +116,9 @@ namespace boost { namespace parser {
|
||||
int & indent,
|
||||
ErrorHandler const & error_handler,
|
||||
nope &,
|
||||
symbol_table_tries_t & symbol_table_tries) noexcept;
|
||||
symbol_table_tries_t & symbol_table_tries,
|
||||
pending_symbol_table_operations_t &
|
||||
pending_symbol_table_operations) noexcept;
|
||||
|
||||
struct skip_skipper;
|
||||
|
||||
@@ -128,6 +143,8 @@ namespace boost { namespace parser {
|
||||
|
||||
struct punct_chars
|
||||
{};
|
||||
struct symb_chars
|
||||
{};
|
||||
struct lower_case_chars
|
||||
{};
|
||||
struct upper_case_chars
|
||||
@@ -192,7 +209,7 @@ namespace boost { namespace parser {
|
||||
`ParserTuple`, not the order of the parsers' matches. It is an error
|
||||
to specialize `perm_parser` with a `ParserTuple` template parameter
|
||||
that includes an `eps_parser`. */
|
||||
template<typename ParserTuple>
|
||||
template<typename ParserTuple, typename DelimiterParser>
|
||||
struct perm_parser;
|
||||
|
||||
/** Applies each parser in `ParserTuple`, in order. The parse succeeds
|
||||
@@ -365,7 +382,10 @@ namespace boost { namespace parser {
|
||||
|
||||
/** Matches a string delimited by quotation marks; produces a
|
||||
`std::string` attribute. */
|
||||
template<typename Quotes = detail::nope, typename Escapes = detail::nope>
|
||||
template<
|
||||
typename Quotes = detail::nope,
|
||||
typename Escapes = detail::nope,
|
||||
typename CharParser = char_parser<detail::nope>>
|
||||
struct quoted_string_parser;
|
||||
|
||||
/** Matches an end-of-line (`NewlinesOnly == true`), whitespace
|
||||
@@ -384,8 +404,8 @@ namespace boost { namespace parser {
|
||||
and at most `MaxDigits`, producing an attribute of type `T`. Fails on
|
||||
any other input. The parse will also fail if `Expected` is anything
|
||||
but `detail::nope` (which it is by default), and the produced
|
||||
attribute is not equal to `expected_`. `Radix` must be in `[2,
|
||||
36]`. */
|
||||
attribute is not equal to `expected_`. `Radix` must be one of `2`,
|
||||
`8`, `10`, or `16`. */
|
||||
template<
|
||||
typename T,
|
||||
int Radix = 10,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
10
index.html
Normal 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>
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import testing ;
|
||||
|
||||
project
|
||||
: requirements <library>/boost/charconv//boost_charconv
|
||||
<library>/boost/parser//boost_parser
|
||||
;
|
||||
|
||||
compile compile_all_t.cpp ;
|
||||
|
||||
@@ -237,6 +237,113 @@ void github_issue_125()
|
||||
}
|
||||
}
|
||||
|
||||
void github_issue_209()
|
||||
{
|
||||
namespace bp = boost::parser;
|
||||
|
||||
BOOST_TEST(std::is_sorted(
|
||||
std::begin(bp::detail::char_set<detail::punct_chars>::chars),
|
||||
std::end(bp::detail::char_set<detail::punct_chars>::chars)));
|
||||
|
||||
BOOST_TEST(std::is_sorted(
|
||||
std::begin(bp::detail::char_set<detail::symb_chars>::chars),
|
||||
std::end(bp::detail::char_set<detail::symb_chars>::chars)));
|
||||
|
||||
BOOST_TEST(std::is_sorted(
|
||||
std::begin(bp::detail::char_set<detail::lower_case_chars>::chars),
|
||||
std::end(bp::detail::char_set<detail::lower_case_chars>::chars)));
|
||||
|
||||
BOOST_TEST(std::is_sorted(
|
||||
std::begin(bp::detail::char_set<detail::upper_case_chars>::chars),
|
||||
std::end(bp::detail::char_set<detail::upper_case_chars>::chars)));
|
||||
}
|
||||
|
||||
void github_issue_223()
|
||||
{
|
||||
namespace bp = boost::parser;
|
||||
|
||||
// failing case
|
||||
{
|
||||
std::vector<char> v;
|
||||
const auto parser = *('x' | bp::char_('y'));
|
||||
bp::parse("xy", parser, bp::ws, v);
|
||||
|
||||
BOOST_TEST(v.size() == 1);
|
||||
BOOST_TEST(v == std::vector<char>({'y'}));
|
||||
|
||||
// the assert fails since there are two elements in the vector: '\0'
|
||||
// and 'y'. Seems pretty surprising to me
|
||||
}
|
||||
|
||||
// working case
|
||||
{
|
||||
const auto parser = *('x' | bp::char_('y'));
|
||||
const auto result = bp::parse("xy", parser, bp::ws);
|
||||
|
||||
BOOST_TEST(result->size() == 1);
|
||||
BOOST_TEST(*(*result)[0] == 'y');
|
||||
|
||||
// success, the vector has only one 'y' element
|
||||
}
|
||||
}
|
||||
|
||||
namespace github_issue_248_ {
|
||||
namespace bp = boost::parser;
|
||||
|
||||
static constexpr bp::rule<struct symbol, int> symbol = "//";
|
||||
static constexpr bp::rule<struct vector, std::vector<int>> list =
|
||||
"<int>(,<int>)*";
|
||||
static constexpr bp::rule<struct working, std::vector<int>> working =
|
||||
"working";
|
||||
static constexpr bp::rule<struct failing, std::vector<int>> failing =
|
||||
"failing";
|
||||
|
||||
static auto const symbol_def = bp::symbols<int>{{"//", 0}};
|
||||
static constexpr auto list_def = bp::int_ % ',';
|
||||
static constexpr auto working_def = -symbol >> (bp::int_ % ',');
|
||||
static constexpr auto failing_def = -symbol >> list;
|
||||
|
||||
BOOST_PARSER_DEFINE_RULES(symbol, list, working, failing);
|
||||
}
|
||||
|
||||
void github_issue_248()
|
||||
{
|
||||
namespace bp = boost::parser;
|
||||
|
||||
using namespace github_issue_248_;
|
||||
|
||||
{
|
||||
auto const result = bp::parse("//1,2,3", working, bp::ws);
|
||||
auto const expected = std::vector<int>{0, 1, 2, 3};
|
||||
BOOST_TEST(result.has_value());
|
||||
bool const equal = std::equal(
|
||||
result->begin(), result->end(), expected.begin(), expected.end());
|
||||
BOOST_TEST(equal);
|
||||
if (!equal) {
|
||||
std::cout << "contents of *result:\n";
|
||||
for (auto x : *result) {
|
||||
std::cout << x << '\n';
|
||||
}
|
||||
std::cout << '\n';
|
||||
}
|
||||
}
|
||||
{
|
||||
auto const result = bp::parse("//1,2,3", failing, bp::ws);
|
||||
auto const expected = std::vector<int>{0, 1, 2, 3};
|
||||
BOOST_TEST(result.has_value());
|
||||
bool const equal = std::equal(
|
||||
result->begin(), result->end(), expected.begin(), expected.end());
|
||||
BOOST_TEST(equal);
|
||||
if (!equal) {
|
||||
std::cout << "contents of *result:\n";
|
||||
for (auto x : *result) {
|
||||
std::cout << x << '\n';
|
||||
}
|
||||
std::cout << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
@@ -246,5 +353,8 @@ int main()
|
||||
github_issue_78();
|
||||
github_issue_90();
|
||||
github_issue_125();
|
||||
github_issue_209();
|
||||
github_issue_223();
|
||||
github_issue_248();
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
@@ -228,7 +228,9 @@ constexpr auto double_s = u8"sS"; // U+0073 U+0073
|
||||
// with mutation
|
||||
{
|
||||
symbols<int> roman_numerals;
|
||||
roman_numerals.insert_for_next_parse("I", 1)("V", 5)("X", 10);
|
||||
roman_numerals.insert_for_next_parse("I", 1);
|
||||
roman_numerals.insert_for_next_parse("V", 5);
|
||||
roman_numerals.insert_for_next_parse("X", 10);
|
||||
auto const add_numeral = [&roman_numerals](auto & context) {
|
||||
using namespace boost::parser::literals;
|
||||
char chars[2] = {get(_attr(context), 0_c), 0};
|
||||
|
||||
2546
test/parser.cpp
2546
test/parser.cpp
File diff suppressed because it is too large
Load Diff
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -10,6 +10,12 @@
|
||||
|
||||
using namespace boost::parser;
|
||||
|
||||
rule<class symbol_rule, std::string_view> const symrule = "symbols";
|
||||
symbols<std::string_view> rule_symbols;
|
||||
auto const fwd_attr = [](auto & ctx) { _val(ctx) = _attr(ctx); };
|
||||
auto symrule_def = rule_symbols[fwd_attr];
|
||||
BOOST_PARSER_DEFINE_RULES(symrule);
|
||||
|
||||
int main()
|
||||
{
|
||||
// symbols_empty
|
||||
@@ -137,7 +143,9 @@ int main()
|
||||
// symbols_mutating
|
||||
{
|
||||
symbols<int> roman_numerals;
|
||||
roman_numerals.insert_for_next_parse("I", 1)("V", 5)("X", 10);
|
||||
roman_numerals.insert_for_next_parse("I", 1);
|
||||
roman_numerals.insert_for_next_parse("V", 5);
|
||||
roman_numerals.insert_for_next_parse("X", 10);
|
||||
auto const add_numeral = [&roman_numerals](auto & context) {
|
||||
using namespace boost::parser::literals;
|
||||
char chars[2] = {get(_attr(context), 0_c), 0};
|
||||
@@ -166,7 +174,9 @@ int main()
|
||||
// insert/erase/clear
|
||||
{
|
||||
symbols<int> roman_numerals;
|
||||
roman_numerals.insert_for_next_parse("I", 1)("V", 5)("X", 10);
|
||||
roman_numerals.insert_for_next_parse("I", 1);
|
||||
roman_numerals.insert_for_next_parse("V", 5);
|
||||
roman_numerals.insert_for_next_parse("X", 10);
|
||||
|
||||
auto const insert_numeral = [&roman_numerals](auto & context) {
|
||||
using namespace boost::parser::literals;
|
||||
@@ -189,15 +199,16 @@ int main()
|
||||
auto const next_insert_numeral = [&roman_numerals](auto & context) {
|
||||
using namespace boost::parser::literals;
|
||||
char chars[2] = {get(_attr(context), 0_c), 0};
|
||||
roman_numerals.insert_for_next_parse(chars, get(_attr(context), 1_c));
|
||||
roman_numerals.insert_for_next_parse(
|
||||
context, chars, get(_attr(context), 1_c));
|
||||
};
|
||||
auto const next_erase_numeral = [&roman_numerals](auto & context) {
|
||||
using namespace boost::parser::literals;
|
||||
char chars[2] = {_attr(context), 0};
|
||||
roman_numerals.erase_for_next_parse(chars);
|
||||
roman_numerals.erase_for_next_parse(context, chars);
|
||||
};
|
||||
auto const next_clear_numerals = [&roman_numerals](auto &) {
|
||||
roman_numerals.clear_for_next_parse();
|
||||
auto const next_clear_numerals = [&roman_numerals](auto & context) {
|
||||
roman_numerals.clear_for_next_parse(context);
|
||||
};
|
||||
|
||||
auto const next_add_parser =
|
||||
@@ -217,7 +228,7 @@ int main()
|
||||
{
|
||||
// add only for the next parse
|
||||
auto result = parse("next-addL50L", next_add_parser >> roman_numerals);
|
||||
BOOST_TEST(!result); // TODO
|
||||
BOOST_TEST(!result);
|
||||
|
||||
result = parse("L", roman_numerals);
|
||||
BOOST_TEST(result);
|
||||
@@ -273,8 +284,8 @@ int main()
|
||||
BOOST_TEST(*parse("V", roman_numerals) == 5);
|
||||
|
||||
auto result = parse("next-delVV", next_delete_parser >> roman_numerals);
|
||||
BOOST_TEST(result); // TODO
|
||||
BOOST_TEST(*result == 5); // TODO
|
||||
BOOST_TEST(result);
|
||||
BOOST_TEST(*result == 5);
|
||||
|
||||
result = parse("V", roman_numerals);
|
||||
BOOST_TEST(!result);
|
||||
@@ -305,7 +316,7 @@ int main()
|
||||
add_parser >> roman_numerals >> next_delete_parser >>
|
||||
roman_numerals);
|
||||
BOOST_TEST(result);
|
||||
BOOST_TEST(*result == std::tuple(100, 100));
|
||||
BOOST_TEST(*result == detail::hl::make_tuple(100, 100));
|
||||
}
|
||||
|
||||
{
|
||||
@@ -326,12 +337,68 @@ int main()
|
||||
BOOST_TEST(*parse("L", roman_numerals) == 50);
|
||||
|
||||
auto result = parse("next-clearI", next_clear_parser >> roman_numerals);
|
||||
BOOST_TEST(result); // TODO
|
||||
BOOST_TEST(*result == 1); // TODO
|
||||
BOOST_TEST(result);
|
||||
BOOST_TEST(*result == 1);
|
||||
|
||||
BOOST_TEST(!parse("I", roman_numerals));
|
||||
BOOST_TEST(!parse("L", roman_numerals));
|
||||
}
|
||||
|
||||
{
|
||||
// parse using symbols directly -- not using the table within a rule
|
||||
rule_symbols.clear_for_next_parse();
|
||||
rule_symbols.insert_for_next_parse("I", "one");
|
||||
rule_symbols.insert_for_next_parse("L", "50");
|
||||
|
||||
auto result = parse("I", rule_symbols);
|
||||
BOOST_TEST(result);
|
||||
BOOST_TEST(*result == "one");
|
||||
|
||||
result = parse("L", rule_symbols);
|
||||
BOOST_TEST(result);
|
||||
BOOST_TEST(*result == "50");
|
||||
|
||||
BOOST_TEST(!parse("X", rule_symbols));
|
||||
}
|
||||
|
||||
{
|
||||
// symbols within a rule
|
||||
rule_symbols.clear_for_next_parse();
|
||||
rule_symbols.insert_for_next_parse("foo", "foofie");
|
||||
rule_symbols.insert_for_next_parse("bar", "barrie");
|
||||
|
||||
auto result = parse("foo", symrule);
|
||||
BOOST_TEST(result);
|
||||
BOOST_TEST(*result == "foofie");
|
||||
|
||||
result = parse("bar", symrule);
|
||||
BOOST_TEST(result);
|
||||
BOOST_TEST(*result == "barrie");
|
||||
|
||||
BOOST_TEST(!parse("X", symrule));
|
||||
BOOST_TEST(!parse("I", symrule));
|
||||
}
|
||||
|
||||
{
|
||||
// symbols within a rule w/error handler
|
||||
rule_symbols.clear_for_next_parse();
|
||||
rule_symbols.insert_for_next_parse("foo", "foofie");
|
||||
rule_symbols.insert_for_next_parse("bar", "barrie");
|
||||
|
||||
callback_error_handler error_handler(
|
||||
[](std::string_view m) { std::cout << m << "\n"; });
|
||||
auto parser = with_error_handler(symrule, error_handler);
|
||||
|
||||
auto result = parse("foo", parser);
|
||||
BOOST_TEST(result);
|
||||
BOOST_TEST(*result == "foofie");
|
||||
|
||||
result = parse("bar", parser);
|
||||
BOOST_TEST(result);
|
||||
BOOST_TEST(*result == "barrie");
|
||||
|
||||
BOOST_TEST(!parse("baz", parser));
|
||||
}
|
||||
}
|
||||
|
||||
return boost::report_errors();
|
||||
|
||||
Reference in New Issue
Block a user