mirror of
https://github.com/boostorg/parser.git
synced 2026-01-19 16:32:13 +00:00
Compare commits
58 Commits
xform_repl
...
parser_con
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
390cd1dbe1 | ||
|
|
824a208133 | ||
|
|
48d5cceb8f | ||
|
|
262c19e441 | ||
|
|
f113d302de | ||
|
|
6d253beaac | ||
|
|
2fa916530e | ||
|
|
3ff6a575ad | ||
|
|
a19af1b5ce | ||
|
|
4c0377a5ae | ||
|
|
f55d3779de | ||
|
|
87b50154cf | ||
|
|
1e011a1fac | ||
|
|
456a81155a | ||
|
|
947fd7782d | ||
|
|
cc9b74cb2d | ||
|
|
e129193296 | ||
|
|
a07efbbd72 | ||
|
|
9f46597ea9 | ||
|
|
5dc826c89d | ||
|
|
374cad3c35 | ||
|
|
4a9a45f856 | ||
|
|
b7f6cd8dbf | ||
|
|
aea0ff8dab | ||
|
|
9af9b5e373 | ||
|
|
f05b25ea1d | ||
|
|
f57de243a4 | ||
|
|
02c140a251 | ||
|
|
0e2936e20d | ||
|
|
4235706764 | ||
|
|
0056bd25d1 | ||
|
|
64e76e7b58 | ||
|
|
ed9fcc6f3a | ||
|
|
0a945decb6 | ||
|
|
9b47f3c551 | ||
|
|
51d9b81927 | ||
|
|
3e047101f6 | ||
|
|
7ae0f9f817 | ||
|
|
40b495ae5d | ||
|
|
e3c46c10c2 | ||
|
|
42f66d9079 | ||
|
|
9ebc984ff8 | ||
|
|
ed6e1b4a2c | ||
|
|
125ddf43c2 | ||
|
|
a2b7afc3a0 | ||
|
|
112290f63e | ||
|
|
27506af2f3 | ||
|
|
db79d00848 | ||
|
|
eda6238180 | ||
|
|
2697eebffd | ||
|
|
edff0b29da | ||
|
|
f3b5bcdd97 | ||
|
|
27400587d5 | ||
|
|
7a9126432a | ||
|
|
ca1ad064ff | ||
|
|
0ea1516b6e | ||
|
|
4ff205d6ec | ||
|
|
a132e31b66 |
4
.github/workflows/macos-12.yml
vendored
4
.github/workflows/macos-12.yml
vendored
@@ -2,9 +2,9 @@ name: macos-12 - Clang 13
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, develop ]
|
||||
branches: [ master, develop, boost_review_changes ]
|
||||
pull_request:
|
||||
branches: [ master, develop ]
|
||||
branches: [ master, develop, boost_review_changes ]
|
||||
|
||||
env:
|
||||
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
|
||||
|
||||
4
.github/workflows/ubuntu-20.04.yml
vendored
4
.github/workflows/ubuntu-20.04.yml
vendored
@@ -2,9 +2,9 @@ name: Ubuntu-20.04 - GCC 9,10
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, develop ]
|
||||
branches: [ master, develop, boost_review_changes ]
|
||||
pull_request:
|
||||
branches: [ master, develop ]
|
||||
branches: [ master, develop, boost_review_changes ]
|
||||
|
||||
env:
|
||||
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
|
||||
|
||||
4
.github/workflows/ubuntu-22.04.yml
vendored
4
.github/workflows/ubuntu-22.04.yml
vendored
@@ -2,9 +2,9 @@ name: Ubuntu-22.04 - GCC 10,11
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, develop ]
|
||||
branches: [ master, develop, boost_review_changes ]
|
||||
pull_request:
|
||||
branches: [ master, develop ]
|
||||
branches: [ master, develop, boost_review_changes ]
|
||||
|
||||
env:
|
||||
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
|
||||
|
||||
4
.github/workflows/windows-2019.yml
vendored
4
.github/workflows/windows-2019.yml
vendored
@@ -2,9 +2,9 @@ name: windows-2019 - Visual Studio 2019
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, develop ]
|
||||
branches: [ master, develop, boost_review_changes ]
|
||||
pull_request:
|
||||
branches: [ master, develop ]
|
||||
branches: [ master, develop, boost_review_changes ]
|
||||
|
||||
env:
|
||||
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
|
||||
|
||||
4
.github/workflows/windows-2022.yml
vendored
4
.github/workflows/windows-2022.yml
vendored
@@ -2,9 +2,9 @@ name: windows-2022 - Visual Studio 2022
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, develop ]
|
||||
branches: [ master, develop, boost_review_changes ]
|
||||
pull_request:
|
||||
branches: [ master, develop ]
|
||||
branches: [ master, develop, boost_review_changes ]
|
||||
|
||||
env:
|
||||
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
|
||||
|
||||
@@ -12,6 +12,8 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL Clang)
|
||||
add_definitions(-g -Wall)
|
||||
elseif (CMAKE_CXX_COMPILER_ID STREQUAL GNU)
|
||||
add_definitions(-g -Wall)
|
||||
elseif (CMAKE_CXX_COMPILER_ID STREQUAL MSVC)
|
||||
add_definitions(/W3)
|
||||
endif ()
|
||||
|
||||
##################################################
|
||||
|
||||
@@ -24,6 +24,7 @@ doxygen parser_reference
|
||||
# note that there is no detail::unspecified -- this is a hack to get all
|
||||
# the SFINAE code out of the API docs.
|
||||
<doxygen:param>"PREDEFINED=\"BOOST_PARSER_DOXYGEN=1\" \\
|
||||
\"BOOST_PARSER_USE_CONCEPTS=1\" \\
|
||||
\"enable_if=detail::unspecified\""
|
||||
<doxygen:param>HIDE_UNDOC_MEMBERS=NO
|
||||
<doxygen:param>EXTRACT_PRIVATE=NO
|
||||
@@ -37,7 +38,7 @@ doxygen parser_reference
|
||||
|
||||
}
|
||||
|
||||
run_doxygen [ glob ../include/boost/parser/*.hpp ] : "Reference" ;
|
||||
run_doxygen [ glob ../include/boost/parser/*.hpp : ../include/boost/parser/concepts.hpp ] : "Reference" ;
|
||||
|
||||
xml parser
|
||||
:
|
||||
|
||||
103
doc/intro.qbk
103
doc/intro.qbk
@@ -163,7 +163,8 @@ However, it does not suit user needs in some ways.
|
||||
rule.
|
||||
|
||||
* Spirit X3 is missing many of the convenient interfaces to parsers that
|
||||
Spirit 2 had. For instance, you cannot add parameters to a parser.
|
||||
Spirit 2 had. For instance, you cannot add parameters to a parser (see
|
||||
description of _locals_ in _more_about_rules_).
|
||||
|
||||
* All versions of Spirit have Unicode support, but it is quite difficult to
|
||||
get working.
|
||||
@@ -184,10 +185,104 @@ that have been retained in _Parser_. Both libraries:
|
||||
* use approximately the same set of directives to influence the parse
|
||||
(e.g. `lexeme[]`);
|
||||
|
||||
* provide loosely-coupled rules that are separately compilable (at least for
|
||||
Spirit X3); and
|
||||
|
||||
* are built around a flexible parse context object that has state added to and
|
||||
removed from it during the parse (again, comparing to Spirit X3).
|
||||
|
||||
[heading The Spirit X3 rule problem]
|
||||
|
||||
Some readers have wanted a concrete example of my claim that Spirit X3's rules
|
||||
do not compose well. Consider this program.
|
||||
|
||||
#include <boost/spirit/home/x3.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace x3 = boost::spirit::x3;
|
||||
using ints_type = x3::rule<class ints, std::vector<int>>;
|
||||
BOOST_SPIRIT_DECLARE(ints_type);
|
||||
|
||||
x3::rule<class ints, std::vector<int>> ints = "ints";
|
||||
constexpr auto ints_def = x3::int_ % ',';
|
||||
BOOST_SPIRIT_DEFINE(ints);
|
||||
|
||||
#define FIXED_ATTRIBUTE 0
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
std::string input = "43, 42";
|
||||
auto first = input.begin();
|
||||
auto const last = input.end();
|
||||
#if FIXED_ATTRIBUTE
|
||||
std::vector<int> result;
|
||||
#else
|
||||
std::set<int> result;
|
||||
#endif
|
||||
bool success = x3::phrase_parse(first, last, ints, x3::space, result);
|
||||
if (success) {
|
||||
// We want this to print "43 42\n".
|
||||
for (auto x : result) {
|
||||
std::cout << x << ' ';
|
||||
}
|
||||
std::cout << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Defining `FIXED_ATTRIBUTE` to be `1` leads to a well-formed program that
|
||||
prints `"42 43\n"` instead of the desired result. The problem here is that if
|
||||
you feed an attribute out-param to `x3::phrase_parse()`, you get the
|
||||
loose-match semantics that Spirit X3 and _Parser_ both do. This is a problem,
|
||||
because the user explicitly asserted that the type of the `ints` rule's
|
||||
attribute should be `std::vector<int>`. In my opinion, this code should be
|
||||
ill-formed with `FIXED_ATTRIBUTE == 1`. To make it well-formed again, the
|
||||
user could use `ints_def` directly, since it does not specify an attribute
|
||||
type.
|
||||
|
||||
When the user explicitly states that a type is some fixed `T`, a library
|
||||
should not ignore that. As a user of X3, I was bitten by this in such a way
|
||||
that I considered X3 to be a nonviable option for my uses. I ran into a
|
||||
problem that resulted from X3's ignoring one or more of my rules' attributes
|
||||
so that it made the parse produce the wrong result, and I could see no way to
|
||||
fix it.
|
||||
|
||||
When a library provides wider use cases via genericity, we generally consider
|
||||
this a good thing. If it is too loose in its semantics, we generally say that
|
||||
it is type-unsafe. Using _rs_ to nail down type flexibility is one way
|
||||
_Parser_ tries to enable genericity where it is desired, and let the user turn
|
||||
it off where it is not.
|
||||
|
||||
[endsect]
|
||||
|
||||
[section Cheat Sheet]
|
||||
|
||||
Here are all the tables containing the various _Parser_ parsers, examples,
|
||||
etc., all in one place. These are repeated elsewhere in different sections of
|
||||
the tutorial.
|
||||
|
||||
[heading The parsers]
|
||||
|
||||
[table_parsers_and_their_semantics]
|
||||
|
||||
[heading Operators defined on parsers]
|
||||
|
||||
[table_combining_operations]
|
||||
|
||||
[heading Attribute generation for certain parsers]
|
||||
|
||||
[table_attribute_generation]
|
||||
|
||||
[heading Attributes for operations on parsers]
|
||||
|
||||
[table_attribute_combinations]
|
||||
|
||||
[heading More attributes for operations on parsers]
|
||||
|
||||
[table_seq_or_attribute_combinations]
|
||||
|
||||
[endsect]
|
||||
|
||||
@@ -39,6 +39,8 @@
|
||||
[import ../example/parsing_into_a_class.cpp]
|
||||
[import ../example/struct_rule.cpp]
|
||||
[import ../example/user_error_handler.cpp]
|
||||
[import ../test/parser.cpp]
|
||||
[import ../test/parser_rule.cpp]
|
||||
|
||||
[import ../include/boost/parser/concepts.hpp]
|
||||
[import ../include/boost/parser/error_handling_fwd.hpp]
|
||||
@@ -67,6 +69,17 @@
|
||||
[def _cb_r_ [classref boost::parser::callback_rule `callback_rule`]]
|
||||
[def _cb_rs_ [classref boost::parser::callback_rules `callback_rule`s]]
|
||||
|
||||
[def _skp_p_ [classref boost::parser::skip_parser `skip_parser`]]
|
||||
[def _xfm_p_ [classref boost::parser::transform_parser `tranform_parser`]]
|
||||
[def _noc_p_ [classref boost::parser::no_case_parser `no_case_parser`]]
|
||||
[def _sv_p_ [classref boost::parser::string_view_parser `string_view_parser`]]
|
||||
[def _raw_p_ [classref boost::parser::raw_parser `raw_parser`]]
|
||||
[def _omt_p_ [classref boost::parser::omit_parser `omit_parser`]]
|
||||
[def _rpt_p_ [classref boost::parser::repeat_parser `repeat_parser`]]
|
||||
[def _lex_p_ [classref boost::parser::lexeme_parser `lexeme_parser`]]
|
||||
[def _seq_p_ [classref boost::parser::seq_parser `seq_parser`]]
|
||||
[def _seq_ps_ [classref boost::parser::seq_parser `seq_parser`s]]
|
||||
|
||||
[def _bp_tup_ [classref boost::parser::tuple `boost::parser::tuple`]]
|
||||
[def _bp_get_ [funcref boost::parser::get `boost::parser::get`]]
|
||||
[def _bh_tup_ `boost::hana::tuple`]
|
||||
@@ -105,6 +118,9 @@
|
||||
[def _cbp_ [funcref boost::parser::callback_parse `callback_parse()`]]
|
||||
[def _cbpp_ [funcref boost::parser::callback_prefix_parse `callback_prefix_parse()`]]
|
||||
|
||||
[def _attr_ [classref boost::parser::attribute `attribute`]]
|
||||
[def _attr_t_ [classref boost::parser::attribute_t `attribute_t`]]
|
||||
|
||||
[def _w_glb_ [funcref boost::parser::with_globals `with_globals()`]]
|
||||
[def _w_eh_ [funcref boost::parser::with_error_handler `with_error_handler()`]]
|
||||
|
||||
@@ -182,6 +198,7 @@
|
||||
[def _skip_ [globalref boost::parser::skip `skip[]`]]
|
||||
[def _merge_ [globalref boost::parser::merge `merge[]`]]
|
||||
[def _sep_ [globalref boost::parser::separate `separate[]`]]
|
||||
[def _transform_ [globalref boost::parser::transform `transform(f)[]`]]
|
||||
|
||||
[def _omit_np_ [globalref boost::parser::omit `omit`]]
|
||||
[def _raw_np_ [globalref boost::parser::raw `raw`]]
|
||||
@@ -191,6 +208,7 @@
|
||||
[def _skip_np_ [globalref boost::parser::skip `skip`]]
|
||||
[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 _blank_ [globalref boost::parser::blank `blank`]]
|
||||
[def _control_ [globalref boost::parser::control `control`]]
|
||||
@@ -206,10 +224,11 @@
|
||||
[def _ATTR_np_ ['[^ATTR]]]
|
||||
|
||||
[def _p_api_ [link boost_parser__proposed_.tutorial.the__parse____api The `parse()` API]]
|
||||
[def _parsers_uses_ [link boost_parser__proposed_.tutorial.the_parsers_and_their_uses The Parsers And Their Uses]]
|
||||
[def _parse_ctx_ [link boost_parser__proposed_.tutorial.the_parse_context The Parse Context]]
|
||||
[def _rule_parsers_ [link boost_parser__proposed_.tutorial.rule_parsers Rule Parsers]]
|
||||
[def _parsing_structs_ [link boost_parser__proposed_.tutorial.parsing_into__struct_s_and__class_es Parsing into `struct`s and `class`es]]
|
||||
[def _expect_pts_ [link boost_parser__proposed_.tutorial.backtracking.html#boost_parser__proposed_.tutorial.backtracking.expectation_points Expectation points]]
|
||||
[def _expect_pts_ [link boost_parser__proposed_.tutorial.backtracking.expectation_points Expectation points]]
|
||||
[def _attr_gen_ [link boost_parser__proposed_.tutorial.attribute_generation Attribute Generation]]
|
||||
[def _directives_ [link boost_parser__proposed_.tutorial.directives Directives]]
|
||||
[def _eh_debugging_ [link boost_parser__proposed_.tutorial.error_handling_and_debugging Error Handling and Debugging]]
|
||||
@@ -235,6 +254,7 @@
|
||||
|
||||
[def _emdash_ \u2014]
|
||||
|
||||
[include tables.qbk]
|
||||
[include intro.qbk]
|
||||
[include tutorial.qbk]
|
||||
[include examples.qbk]
|
||||
|
||||
572
doc/tables.qbk
Normal file
572
doc/tables.qbk
Normal file
@@ -0,0 +1,572 @@
|
||||
[/
|
||||
/ 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)
|
||||
/]
|
||||
|
||||
[template table_parsers_and_their_semantics
|
||||
This table lists all the _Parser_ parsers. For the callable parsers, a
|
||||
separate entry exists for each possible arity of arguments. For a parser `p`,
|
||||
if there is no entry for `p` without arguments, `p` is a function, and cannot
|
||||
itself be used as a parser; it must be called. In the table below:
|
||||
|
||||
* each entry is a global object usable directly in your parsers, unless
|
||||
otherwise noted;
|
||||
|
||||
* "code point" is used to refer to the elements of the input range, which
|
||||
assumes that the parse is being done in the Unicode-aware code path (if the
|
||||
parse is being done in the non-Unicode code path, read "code point" as
|
||||
"`char`");
|
||||
|
||||
* _RES_ is a notional macro that expands to the resolution of parse argument
|
||||
or evaluation of a parse predicate (see _parsers_uses_);
|
||||
|
||||
* "`_RES_np_(pred) == true`" is a shorthand notation for "`_RES_np_(pred)` is
|
||||
contextually convertible to `bool` and `true`"; likewise for `false`;
|
||||
|
||||
* `c` is a character of type `char`, `char8_t`, or `char32_t`;
|
||||
|
||||
* `str` is a string literal of type `char const[]`, `char8_t const []`, or
|
||||
`char32_t const []`;
|
||||
|
||||
* `pred` is a parse predicate;
|
||||
|
||||
* `arg0`, `arg1`, `arg2`, ... are parse arguments;
|
||||
|
||||
* `a` is a semantic action;
|
||||
|
||||
* `r` is an object whose type models `parsable_range_like`; and
|
||||
|
||||
* `p`, `p1`, `p2`, ... are parsers.
|
||||
|
||||
[note The definition of `parsable_range_like` is:
|
||||
|
||||
[parsable_range_like_concept]
|
||||
|
||||
It is intended to be a range-like thing; a null-terminated sequence of
|
||||
characters is considered range-like, given that a pointer `T *` to a
|
||||
null-terminated string is isomorphic with `subrange<T *, _null_sent_>`.]
|
||||
|
||||
[note Some of the parsers in this table consume no input. All parsers consume
|
||||
the input they match unless otherwise stated in the table below.]
|
||||
|
||||
[table Parsers and Their Semantics
|
||||
[[Parser] [Semantics] [Attribute Type] [Notes]]
|
||||
|
||||
[[ _e_ ]
|
||||
[ Matches /epsilon/, the empty string. Always matches, and consumes no input. ]
|
||||
[ None. ]
|
||||
[ 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_`, `+_e_`, etc (this applies to unconditional _e_ only). ]]
|
||||
|
||||
[[ `_e_(pred)` ]
|
||||
[ Fails to match the input if `_RES_np_(pred) == false`. Otherwise, the semantics are those of _e_. ]
|
||||
[ None. ]
|
||||
[]]
|
||||
|
||||
[[ _ws_ ]
|
||||
[ Matches a single whitespace code point (see note), according to the Unicode White_Space property. ]
|
||||
[ None. ]
|
||||
[ For more info, see the [@https://www.unicode.org/Public/UCD/latest/ucd/PropList.txt Unicode properties]. _ws_ may consume one code point or two. It only consumes two code points when it matches `"\r\n"`. ]]
|
||||
|
||||
[[ _eol_ ]
|
||||
[ Matches a single newline (see note), following the "hard" line breaks in the Unicode line breaking algorithm. ]
|
||||
[ None. ]
|
||||
[ For more info, see the [@https://unicode.org/reports/tr14 Unicode Line Breaking Algorithm]. _eol_ may consume one code point or two. It only consumes two code points when it matches `"\r\n"`. ]]
|
||||
|
||||
[[ _eoi_ ]
|
||||
[ Matches only at the end of input, and consumes no input. ]
|
||||
[ None. ]
|
||||
[]]
|
||||
|
||||
[[ _attr_np_`(arg0)` ]
|
||||
[ Always matches, and consumes no input. Generates the attribute `_RES_np_(arg0)`. ]
|
||||
[ `decltype(_RES_np_(arg0))`. ]
|
||||
[ An important use case for `_attr_` is to provide a default attribute value as a trailing alternative. For instance, an *optional* comma-delmited list is: `int_ % ',' | attr(std::vector<int>)`. Without the "`| attr(...)`", at least one `int_` match would be required. ]]
|
||||
|
||||
[[ _ch_ ]
|
||||
[ Matches any single code point. ]
|
||||
[ The code point type in Unicode parsing, or `char` in non-Unicode parsing. See _attr_gen_. ]
|
||||
[]]
|
||||
|
||||
[[ `_ch_(arg0)` ]
|
||||
[ Matches exactly the code point `_RES_np_(arg0)`. ]
|
||||
[ The code point type in Unicode parsing, or `char` in non-Unicode parsing. See _attr_gen_. ]
|
||||
[]]
|
||||
|
||||
[[ `_ch_(arg0, arg1)` ]
|
||||
[ Matches the next code point `n` in the input, if `_RES_np_(arg0) <= n && n <= _RES_np_(arg1)`. ]
|
||||
[ The code point type in Unicode parsing, or `char` in non-Unicode parsing. See _attr_gen_. ]
|
||||
[]]
|
||||
|
||||
[[ `_ch_(r)` ]
|
||||
[ Matches the next code point `n` in the input, if `n` is one of the code points in `r`. ]
|
||||
[ The code point type in Unicode parsing, or `char` in non-Unicode parsing. See _attr_gen_. ]
|
||||
[ `r` is taken to be in a UTF encoding. The exact UTF used depends on `r`'s element type. If you do not pass UTF encoded ranges for `r`, the behavior of _ch_ is undefined. Note that ASCII is a subset of UTF-8, so ASCII is fine. EBCDIC is not. `r` is not copied; a reference to it is taken. The lifetime of `_ch_(r)` must be within the lifetime of `r`. This overload of _ch_ does *not* take parse arguments. ]]
|
||||
|
||||
[[ _cp_ ]
|
||||
[ Matches a single code point. ]
|
||||
[ `char32_t` ]
|
||||
[ Similar to _ch_, but with a fixed `char32_t` attribute type; _cp_ has all the same call operator overloads as _ch_, though they are not repeated here, for brevity. ]]
|
||||
|
||||
[[ _cu_ ]
|
||||
[ Matches a single code point. ]
|
||||
[ `char` ]
|
||||
[ Similar to _ch_, but with a fixed `char` attribute type; _cu_ has all the same call operator overloads as _ch_, though they are not repeated here, for brevity. Even though the name "`cu`" suggests that this parser match at the code unit level, it does not. The name refers to the attribute type generated, much like the names _i_ versus _ui_. ]]
|
||||
|
||||
[[ `_blank_` ]
|
||||
[ Equivalent to `_ws_ - _eol_`. ]
|
||||
[ The code point type in Unicode parsing, or `char` in non-Unicode parsing. See the entry for _ch_. ]
|
||||
[]]
|
||||
|
||||
[[ `_control_` ]
|
||||
[ Matches a single control-character code point. ]
|
||||
[ The code point type in Unicode parsing, or `char` in non-Unicode parsing. See the entry for _ch_. ]
|
||||
[]]
|
||||
|
||||
[[ `_digit_` ]
|
||||
[ Matches a single decimal digit code point. ]
|
||||
[ The code point type in Unicode parsing, or `char` in non-Unicode parsing. See the entry for _ch_. ]
|
||||
[]]
|
||||
|
||||
[[ `_punct_` ]
|
||||
[ Matches a single punctuation 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_. ]
|
||||
[]]
|
||||
|
||||
[[ `_lower_` ]
|
||||
[ Matches a single lower-case code point. ]
|
||||
[ The code point type in Unicode parsing, or `char` in non-Unicode parsing. See the entry for _ch_. ]
|
||||
[]]
|
||||
|
||||
[[ `_upper_` ]
|
||||
[ Matches a single upper-case code point. ]
|
||||
[ The code point type in Unicode parsing, or `char` in non-Unicode parsing. See the entry for _ch_. ]
|
||||
[]]
|
||||
|
||||
[[ _lit_np_`(c)`]
|
||||
[ Matches exactly the given code point `c`. ]
|
||||
[ None. ]
|
||||
[_lit_ does *not* take parse arguments. ]]
|
||||
|
||||
[[ `c_l` ]
|
||||
[ Matches exactly the given code point `c`. ]
|
||||
[ None. ]
|
||||
[ This is a _udl_ that represents `_lit_np_(c)`, for example `'F'_l`. ]]
|
||||
|
||||
[[ _lit_np_`(r)`]
|
||||
[ Matches exactly the given string `r`. ]
|
||||
[ None. ]
|
||||
[ _lit_ does *not* take parse arguments. ]]
|
||||
|
||||
[[ `str_l` ]
|
||||
[ Matches exactly the given string `str`. ]
|
||||
[ None. ]
|
||||
[ This is a _udl_ that represents `_lit_np_(s)`, for example `"a string"_l`. ]]
|
||||
|
||||
[[ `_str_np_(r)`]
|
||||
[ Matches exactly `r`, and generates the match as an attribute. ]
|
||||
[ _std_str_ ]
|
||||
[ _str_ does *not* take parse arguments. ]]
|
||||
|
||||
[[ `str_p`]
|
||||
[ Matches exactly `str`, and generates the match as an attribute. ]
|
||||
[ _std_str_ ]
|
||||
[ This is a _udl_ that represents `_str_np_(s)`, for example `"a string"_p`. ]]
|
||||
|
||||
[[ _b_ ]
|
||||
[ Matches `"true"` or `"false"`. ]
|
||||
[ `bool` ]
|
||||
[]]
|
||||
|
||||
[[ _bin_ ]
|
||||
[ Matches a binary unsigned integral value. ]
|
||||
[ `unsigned int` ]
|
||||
[ For example, _bin_ would match `"101"`, and generate an attribute of `5u`. ]]
|
||||
|
||||
[[ `_bin_(arg0)` ]
|
||||
[ Matches exactly the binary unsigned integral value `_RES_np_(arg0)`. ]
|
||||
[ `unsigned int` ]
|
||||
[]]
|
||||
|
||||
[[ _oct_ ]
|
||||
[ Matches an octal unsigned integral value. ]
|
||||
[ `unsigned int` ]
|
||||
[ For example, _oct_ would match `"31"`, and generate an attribute of `25u`. ]]
|
||||
|
||||
[[ `_oct_(arg0)` ]
|
||||
[ Matches exactly the octal unsigned integral value `_RES_np_(arg0)`. ]
|
||||
[ `unsigned int` ]
|
||||
[]]
|
||||
|
||||
[[ _hex_ ]
|
||||
[ Matches a hexadecimal unsigned integral value. ]
|
||||
[ `unsigned int` ]
|
||||
[ For example, _hex_ would match `"ff"`, and generate an attribute of `255u`. ]]
|
||||
|
||||
[[ `_hex_(arg0)` ]
|
||||
[ Matches exactly the hexadecimal unsigned integral value `_RES_np_(arg0)`. ]
|
||||
[ `unsigned int` ]
|
||||
[]]
|
||||
|
||||
[[ _us_ ]
|
||||
[ Matches an unsigned integral value. ]
|
||||
[ `unsigned short` ]
|
||||
[]]
|
||||
|
||||
[[ `_us_(arg0)` ]
|
||||
[ Matches exactly the unsigned integral value `_RES_np_(arg0)`. ]
|
||||
[ `unsigned short` ]
|
||||
[]]
|
||||
|
||||
[[ _ui_ ]
|
||||
[ Matches an unsigned integral value. ]
|
||||
[ `unsigned int` ]
|
||||
[]]
|
||||
|
||||
[[ `_ui_(arg0)` ]
|
||||
[ Matches exactly the unsigned integral value `_RES_np_(arg0)`. ]
|
||||
[ `unsigned int` ]
|
||||
[]]
|
||||
|
||||
[[ _ul_ ]
|
||||
[ Matches an unsigned integral value. ]
|
||||
[ `unsigned long` ]
|
||||
[]]
|
||||
|
||||
[[ `_ul_(arg0)` ]
|
||||
[ Matches exactly the unsigned integral value `_RES_np_(arg0)`. ]
|
||||
[ `unsigned long` ]
|
||||
[]]
|
||||
|
||||
[[ _ull_ ]
|
||||
[ Matches an unsigned integral value. ]
|
||||
[ `unsigned long long` ]
|
||||
[]]
|
||||
|
||||
[[ `_ull_(arg0)` ]
|
||||
[ Matches exactly the unsigned integral value `_RES_np_(arg0)`. ]
|
||||
[ `unsigned long long` ]
|
||||
[]]
|
||||
|
||||
[[ _s_ ]
|
||||
[ Matches a signed integral value. ]
|
||||
[ `short` ]
|
||||
[]]
|
||||
|
||||
[[ `_s_(arg0)` ]
|
||||
[ Matches exactly the signed integral value `_RES_np_(arg0)`. ]
|
||||
[ `short` ]
|
||||
[]]
|
||||
|
||||
[[ _i_ ]
|
||||
[ Matches a signed integral value. ]
|
||||
[ `int` ]
|
||||
[]]
|
||||
|
||||
[[ `_i_(arg0)` ]
|
||||
[ Matches exactly the signed integral value `_RES_np_(arg0)`. ]
|
||||
[ `int` ]
|
||||
[]]
|
||||
|
||||
[[ _l_ ]
|
||||
[ Matches a signed integral value. ]
|
||||
[ `long` ]
|
||||
[]]
|
||||
|
||||
[[ `_l_(arg0)` ]
|
||||
[ Matches exactly the signed integral value `_RES_np_(arg0)`. ]
|
||||
[ `long` ]
|
||||
[]]
|
||||
|
||||
[[ _ll_ ]
|
||||
[ Matches a signed integral value. ]
|
||||
[ `long long` ]
|
||||
[]]
|
||||
|
||||
[[ `_ll_(arg0)` ]
|
||||
[ Matches exactly the signed integral value `_RES_np_(arg0)`. ]
|
||||
[ `long long` ]
|
||||
[]]
|
||||
|
||||
[[ _f_ ]
|
||||
[ Matches a floating-point number. _f_ uses parsing implementation details from _Spirit_. The specifics of what formats are accepted can be found in their _spirit_reals_. Note that only the default `RealPolicies` is supported by _f_. ]
|
||||
[ `float` ]
|
||||
[]]
|
||||
|
||||
[[ _d_ ]
|
||||
[ Matches a floating-point number. _d_ uses parsing implementation details from _Spirit_. The specifics of what formats are accepted can be found in their _spirit_reals_. Note that only the default `RealPolicies` is supported by _d_. ]
|
||||
[ `double` ]
|
||||
[]]
|
||||
|
||||
[[ `_rpt_np_(arg0)[p]` ]
|
||||
[ Matches iff `p` matches exactly `_RES_np_(arg0)` times. ]
|
||||
[ `std::string` if `_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p)>` ]
|
||||
[ The special value _inf_ may be used; it indicates unlimited repetition. `decltype(_RES_np_(arg0))` must be implicitly convertible to `int64_t`. 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 `_rpt_np_(_inf_)[_e_]` (this applies to unconditional _e_ only). ]]
|
||||
|
||||
[[ `_rpt_np_(arg0, arg1)[p]` ]
|
||||
[ Matches iff `p` matches between `_RES_np_(arg0)` and `_RES_np_(arg1)` times, inclusively. ]
|
||||
[ `std::string` if `_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p)>` ]
|
||||
[ The special value _inf_ may be used for the upper bound; it indicates unlimited repetition. `decltype(_RES_np_(arg0))` and `decltype(_RES_np_(arg1))` each must be implicitly convertible to `int64_t`. 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 `_rpt_np_(n, _inf_)[_e_]` (this applies to unconditional _e_ only). ]]
|
||||
|
||||
[[ `_if_np_(pred)[p]` ]
|
||||
[ Equivalent to `_e_(pred) >> p`. ]
|
||||
[ `std::optional<_ATTR_np_(p)>` ]
|
||||
[ It is an error to write `_if_np_(pred)`. That is, it is an error to omit the conditionally matched parser `p`. ]]
|
||||
|
||||
[[ `_sw_np_(arg0)(arg1, p1)(arg2, p2) ...` ]
|
||||
[ Equivalent to `p1` when `_RES_np_(arg0) == _RES_np_(arg1)`, `p2` when `_RES_np_(arg0) == _RES_np_(arg2)`, etc. If there is such no `argN`, the behavior of _sw_ is undefined. ]
|
||||
[ `std::variant<_ATTR_np_(p1), _ATTR_np_(p2), ...>` ]
|
||||
[ It is an error to write `_sw_np_(arg0)`. That is, it is an error to omit the conditionally matched parsers `p1`, `p2`, .... ]]
|
||||
|
||||
[[ _symbols_t_ ]
|
||||
[ _symbols_ is an associative container of key, value pairs. Each key is a _std_str_ and each value has type `T`. In the Unicode parsing path, the strings are considered to be UTF-8 encoded; in the non-Unicode path, no encoding is assumed. _symbols_ Matches the longest prefix `pre` of the input that is equal to one of the keys `k`. If the length `len` of `pre` is zero, and there is no zero-length key, it does not match the input. If `len` is positive, the generated attribute is the value associated with `k`.]
|
||||
[ `T` ]
|
||||
[ Unlike the other entries in this table, _symbols_ is a type, not an object. ]]
|
||||
]
|
||||
|
||||
[important All the character parsers, like _ch_, _cp_ and _cu_ produce either
|
||||
`char` or `char32_t` attributes. So when you see "`std::string` if
|
||||
`_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p)>`"
|
||||
in the table above, that effectively means that every sequences of character
|
||||
attributes get turned into a `std::string`. The only time this does not
|
||||
happen is when you introduce your own rules with attributes using another
|
||||
character type (or use _attr_ to do so).]
|
||||
]
|
||||
|
||||
[template table_combining_operations
|
||||
Here are all the operator overloaded for parsers. In the tables below:
|
||||
|
||||
* `c` is a character of type `char` or `char32_t`;
|
||||
|
||||
* `a` is a semantic action;
|
||||
|
||||
* `r` is an object whose type models `parsable_range_like` (see _concepts_);
|
||||
and
|
||||
|
||||
* `p`, `p1`, `p2`, ... are parsers.
|
||||
|
||||
[note Some of the expressions in this table consume no input. All parsers
|
||||
consume the input they match unless otherwise stated in the table below.]
|
||||
|
||||
[table Combining Operations and Their Semantics
|
||||
[[Expression] [Semantics] [Attribute Type] [Notes]]
|
||||
|
||||
[[`!p`] [ Matches iff `p` does not match; consumes no input. ] [None.] []]
|
||||
[[`&p`] [ Matches iff `p` matches; consumes no input. ] [None.] []]
|
||||
[[`*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. ]]
|
||||
[[`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. ]]
|
||||
[[`p > c`] [ Equivalent to `p > lit(c)`. ] [`_ATTR_np_(p)`] []]
|
||||
[[`p > r`] [ Equivalent to `p > lit(r)`. ] [`_ATTR_np_(p)`] []]
|
||||
[[`p1 | p2`] [ Matches iff either `p1` matches or `p2` matches. ] [`std::variant<_ATTR_np_(p1), _ATTR_np_(p2)>` (See note.)] [ `|` is associative; `p1 | p2 | p3`, `(p1 | p2) | p3`, and `p1 | (p2 | p3)` are all equivalent. This attribute type only applies to the case where `p1` and `p2` both generate attributes, and where the attribute types are different; see _attr_gen_ for the full rules. ]]
|
||||
[[`p | c`] [ Equivalent to `p | lit(c)`. ] [`_ATTR_np_(p)`] []]
|
||||
[[`p | r`] [ Equivalent to `p | lit(r)`. ] [`_ATTR_np_(p)`] []]
|
||||
[[`p1 || p2`] [ Matches iff `p1` matches and `p2` matches, regardless of the order they match in. ] [`_bp_tup_<_ATTR_np_(p1), _ATTR_np_(p2)>`] [ `||` is associative; `p1 || p2 || p3`, `(p1 || p2) || p3`, and `p1 || (p2 || p3)` are all equivalent. It is an error to include a _e_ (conditional or non-conditional) in an `operator||` expression. Though the parsers are matched in any order, the attribute elements are always in the order written in the `operator||` expression. ]]
|
||||
[[`p1 - p2`] [ Equivalent to `!p2 >> p1`. ] [`_ATTR_np_(p1)`] []]
|
||||
[[`p - c`] [ Equivalent to `p - lit(c)`. ] [`_ATTR_np_(p)`] []]
|
||||
[[`p - r`] [ Equivalent to `p - lit(r)`. ] [`_ATTR_np_(p)`] []]
|
||||
[[`p1 % p2`] [ Equivalent to `p1 >> *(p2 >> p1)`. ] [`std::string` if `_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p1)>`] []]
|
||||
[[`p % c`] [ Equivalent to `p % lit(c)`. ] [`std::string` if `_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p)>`] []]
|
||||
[[`p % r`] [ Equivalent to `p % lit(r)`. ] [`std::string` if `_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p)>`] []]
|
||||
[[`p[a]`] [ Matches iff `p` matches. If `p` matches, the semantic action `a` is executed. ] [None.] []]
|
||||
]
|
||||
|
||||
[important All the character parsers, like _ch_, _cp_ and _cu_ produce either
|
||||
`char` or `char32_t` attributes. So when you see "`std::string` if
|
||||
`_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p)>`"
|
||||
in the table above, that effectively means that every sequences of character
|
||||
attributes get turned into a `std::string`. The only time this does not
|
||||
happen is when you introduce your own rules with attributes using another
|
||||
character type (or use _attr_ to do so).]
|
||||
|
||||
There are a couple of special rules not captured in the table above:
|
||||
|
||||
First, the zero-or-more and one-or-more repetitions (`operator*()` and
|
||||
`operator+()`, respectively) may collapse when combined. For any parser `p`,
|
||||
`+(+p)` collapses to `+p`; `**p`, `*+p`, and `+*p` each collapse to just `*p`.
|
||||
|
||||
Second, using _e_ in an alternative parser as any alternative *except* the
|
||||
last one is a common source of errors; _Parser_ disallows it. This is true
|
||||
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.
|
||||
]
|
||||
|
||||
[template table_attribute_generation
|
||||
|
||||
This table summarizes the attributes generated for all _Parser_ parsers. In
|
||||
the table below:
|
||||
|
||||
* _RES_ is a notional macro that expands to the resolution of parse argument
|
||||
or evaluation of a parse predicate (see _parsers_uses_); and
|
||||
|
||||
* `x` and `y` represent arbitrary objects.
|
||||
|
||||
[table Parsers and Their Attributes
|
||||
[[Parser] [Attribute Type] [Notes]]
|
||||
|
||||
[[ _e_ ] [ None. ] []]
|
||||
[[ _eol_ ] [ None. ] []]
|
||||
[[ _eoi_ ] [ None. ] []]
|
||||
[[ `_attr_np_(x)` ] [ `decltype(_RES_np_(x))` ][]]
|
||||
[[ _ch_ ] [ The code point type in Unicode parsing, or `char` in non-Unicode parsing; see below. ]
|
||||
[Includes all the `_p` _udls_ that take a single character, and all character class parsers like `control` and `lower`.]]
|
||||
[[ _cp_ ] [ `char32_t` ] []]
|
||||
[[ _cu_ ] [ `char` ] []]
|
||||
[[ `_lit_np_(x)`] [ None. ]
|
||||
[Includes all the `_l` _udls_.]]
|
||||
[[ `_str_np_(x)`] [ _std_str_ ]
|
||||
[Includes all the `_p` _udls_ that take a string.]]
|
||||
[[ _b_ ] [ `bool` ] []]
|
||||
|
||||
[[ _bin_ ] [ `unsigned int` ] []]
|
||||
[[ _oct_ ] [ `unsigned int` ] []]
|
||||
[[ _hex_ ] [ `unsigned int` ] []]
|
||||
[[ _us_ ] [ `unsigned short` ] []]
|
||||
[[ _ui_ ] [ `unsigned int` ] []]
|
||||
[[ _ul_ ] [ `unsigned long` ] []]
|
||||
[[ _ull_ ] [ `unsigned long long` ] []]
|
||||
|
||||
[[ _s_ ] [ `short` ] []]
|
||||
[[ _i_ ] [ `int` ] []]
|
||||
[[ _l_ ] [ `long` ] []]
|
||||
[[ _ll_ ] [ `long long` ] []]
|
||||
[[ _f_ ] [ `float` ] []]
|
||||
[[ _d_ ] [ `double` ] []]
|
||||
|
||||
[[ _symbols_t_ ] [ `T` ] []]
|
||||
]
|
||||
|
||||
_ch_ is a bit odd, since its attribute type is polymorphic. When you use _ch_
|
||||
to parse text in the non-Unicode code path (i.e. a string of `char`), the
|
||||
attribute is `char`. When you use the exact same _ch_ to parse in the
|
||||
Unicode-aware code path, all matching is code point based, and so the
|
||||
attribute type is the type used to represent code points, `char32_t`. All
|
||||
parsing of UTF-8 falls under this case.
|
||||
|
||||
Here, we're parsing plain `char`s, meaning that the parsing is in the
|
||||
non-Unicode code path, the attribute of _ch_ is `char`:
|
||||
|
||||
auto result = parse("some text", boost::parser::char_);
|
||||
static_assert(std::is_same_v<decltype(result), std::optional<char>>));
|
||||
|
||||
When you parse UTF-8, the matching is done on a code point basis, so the
|
||||
attribute type is `char32_t`:
|
||||
|
||||
auto result = parse("some text" | boost::parser::as_utf8, boost::parser::char_);
|
||||
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
|
||||
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
|
||||
attribute type.
|
||||
]
|
||||
|
||||
[template table_attribute_combinations
|
||||
Combining operations of course affect the generation of attributes. In the
|
||||
tables below:
|
||||
|
||||
* `m` and `n` are parse arguments that resolve to integral values;
|
||||
|
||||
* `pred` is a parse predicate;
|
||||
|
||||
* `arg0`, `arg1`, `arg2`, ... are parse arguments;
|
||||
|
||||
* `a` is a semantic action; and
|
||||
|
||||
* `p`, `p1`, `p2`, ... are parsers that generate attributes.
|
||||
|
||||
[table Combining Operations and Their Attributes
|
||||
[[Parser] [Attribute Type]]
|
||||
|
||||
[[`!p`] [None.]]
|
||||
[[`&p`] [None.]]
|
||||
|
||||
[[`*p`] [`std::string` if `_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p)>`]]
|
||||
[[`+p`] [`std::string` if `_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p)>`]]
|
||||
[[`+*p`] [`std::string` if `_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p)>`]]
|
||||
[[`*+p`] [`std::string` if `_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p)>`]]
|
||||
[[`-p`] [`std::optional<_ATTR_np_(p)>`]]
|
||||
|
||||
[[`p1 >> p2`] [`_bp_tup_<_ATTR_np_(p1), _ATTR_np_(p2)>`]]
|
||||
[[`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 >> p3`] [`_bp_tup_<_ATTR_np_(p1), _ATTR_np_(p2), _ATTR_np_(p3)>`]]
|
||||
[[`p1 >> p2 > p3`] [`_bp_tup_<_ATTR_np_(p1), _ATTR_np_(p2), _ATTR_np_(p3)>`]]
|
||||
[[`p1 > p2 > p3`] [`_bp_tup_<_ATTR_np_(p1), _ATTR_np_(p2), _ATTR_np_(p3)>`]]
|
||||
|
||||
[[`p1 | p2`] [`std::variant<_ATTR_np_(p1), _ATTR_np_(p2)>`]]
|
||||
[[`p1 | p2 | p3`] [`std::variant<_ATTR_np_(p1), _ATTR_np_(p2), _ATTR_np_(p3)>`]]
|
||||
|
||||
[[`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)>`]]
|
||||
|
||||
[[`p[a]`] [None.]]
|
||||
|
||||
[[`_rpt_np_(arg0)[p]`] [`std::string` if `_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p)>`]]
|
||||
[[`_rpt_np_(arg0, arg1)[p]`] [`std::string` if `_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p)>`]]
|
||||
[[`_if_np_(pred)[p]`] [`std::optional<_ATTR_np_(p)>`]]
|
||||
[[`_sw_np_(arg0)(arg1, p1)(arg2, p2)...`]
|
||||
[`std::variant<_ATTR_np_(p1), _ATTR_np_(p2), ...>`]]
|
||||
]
|
||||
|
||||
[important All the character parsers, like _ch_, _cp_ and _cu_ produce either
|
||||
`char` or `char32_t` attributes. So when you see "`std::string` if
|
||||
`_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p)>`"
|
||||
in the table above, that effectively means that every sequences of character
|
||||
attributes get turned into a `std::string`. The only time this does not
|
||||
happen is when you introduce your own rules with attributes using another
|
||||
character type (or use _attr_ to do so).]
|
||||
|
||||
[important In case you did not notice it above, adding a semantic action to a
|
||||
parser erases the parser's attribute. The attribute is still available inside
|
||||
the semantic action as `_attr(ctx)`.]
|
||||
]
|
||||
|
||||
[template table_seq_or_attribute_combinations
|
||||
In the table: `a` is a semantic action; and `p`, `p1`, `p2`, ... are parsers
|
||||
that generate attributes. Note that only `>>` is used here; `>` has the exact
|
||||
same attribute generation rules.
|
||||
|
||||
[table Sequence and Alternative Combining Operations and Their Attributes
|
||||
[[Expression] [Attribute Type]]
|
||||
|
||||
[[`_e_ >> _e_`] [None.]]
|
||||
[[`p >> _e_`] [`_ATTR_np_(p)`]]
|
||||
[[`_e_ >> p`] [`_ATTR_np_(p)`]]
|
||||
|
||||
[[`_cu_ >> _str_np_("str")`] [_std_str_]]
|
||||
[[`_str_np_("str") >> _cu_`] [_std_str_]]
|
||||
[[`*_cu_ >> _str_np_("str")`] [`_bp_tup_<std::string, std::string>`]]
|
||||
[[`_str_np_("str") >> *_cu_`] [`_bp_tup_<std::string, std::string>`]]
|
||||
|
||||
[[`p >> p`] [`_bp_tup_<_ATTR_np_(p), _ATTR_np_(p)>`]]
|
||||
[[`*p >> p`] [`std::string` if `_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p)>`]]
|
||||
[[`p >> *p`] [`std::string` if `_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p)>`]]
|
||||
[[`*p >> -p`] [`std::string` if `_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p)>`]]
|
||||
[[`-p >> *p`] [`std::string` if `_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p)>`]]
|
||||
|
||||
[[`_str_np_("str") >> -_cu_`] [_std_str_]]
|
||||
[[`-_cu_ >> _str_np_("str")`] [_std_str_]]
|
||||
|
||||
[[`!p1 | p2[a]`] [None.]]
|
||||
[[`p | p`] [`_ATTR_np_(p)`]]
|
||||
[[`p1 | p2`] [`std::variant<_ATTR_np_(p1), _ATTR_np_(p2)>`]]
|
||||
[[`p | `_e_] [`std::optional<_ATTR_np_(p)>`]]
|
||||
[[`p1 | p2 | _e_`] [`std::optional<std::variant<_ATTR_np_(p1), _ATTR_np_(p2)>>`]]
|
||||
[[`p1 | p2[a] | p3`] [`std::optional<std::variant<_ATTR_np_(p1), _ATTR_np_(p3)>>`]]
|
||||
]
|
||||
]
|
||||
1197
doc/tutorial.qbk
1197
doc/tutorial.qbk
File diff suppressed because it is too large
Load Diff
@@ -35,6 +35,7 @@ int main()
|
||||
auto const result = parse("X 9 X", parser, bp::ws);
|
||||
assert(result && *result == 9);
|
||||
//]
|
||||
(void)result;
|
||||
|
||||
//[ self_filling_symbol_table_after_parse
|
||||
assert(!parse("X", symbols));
|
||||
|
||||
@@ -44,18 +44,14 @@ namespace boost { namespace parser {
|
||||
//]
|
||||
|
||||
template<typename T>
|
||||
concept utf_pointer =
|
||||
std::is_pointer_v<T> && code_unit<std::iter_value_t<T>>;
|
||||
template<typename T>
|
||||
concept range_like =
|
||||
std::ranges::range<T> || utf_pointer<std::remove_cvref_t<T>>;
|
||||
concept range_like = std::ranges::range<T> || parsable_pointer<T>;
|
||||
|
||||
template<
|
||||
typename I,
|
||||
typename S,
|
||||
typename ErrorHandler,
|
||||
typename GlobalState>
|
||||
using minimal_parse_context = decltype(detail::make_context(
|
||||
using minimal_parse_context = decltype(detail::make_context<false, false>(
|
||||
std::declval<I>(),
|
||||
std::declval<S>(),
|
||||
std::declval<bool &>(),
|
||||
|
||||
@@ -57,11 +57,9 @@ namespace boost::parser::detail {
|
||||
return out;
|
||||
} else {
|
||||
// Skip [0x41, 0x5a), handled above.
|
||||
auto const first =
|
||||
detail::text::detail::begin(mapping_ranges) + 1;
|
||||
auto const first = text::detail::begin(mapping_ranges) + 1;
|
||||
// 7th entry starts with 0x100.
|
||||
auto const last =
|
||||
detail::text::detail::begin(mapping_ranges) + 7;
|
||||
auto const last = text::detail::begin(mapping_ranges) + 7;
|
||||
if (auto out_opt = do_short_mapping(first, last, cp, out))
|
||||
return *out_opt;
|
||||
}
|
||||
@@ -71,8 +69,8 @@ namespace boost::parser::detail {
|
||||
|
||||
// Single-cp-mapping path (next most common case).
|
||||
{
|
||||
auto const first = detail::text::detail::begin(mapping_ranges);
|
||||
auto const last = detail::text::detail::end(mapping_ranges);
|
||||
auto const first = text::detail::begin(mapping_ranges);
|
||||
auto const last = text::detail::end(mapping_ranges);
|
||||
if (auto out_opt = do_short_mapping(first, last, cp, out))
|
||||
return *out_opt;
|
||||
}
|
||||
@@ -95,8 +93,8 @@ namespace boost::parser::detail {
|
||||
return std::copy(
|
||||
it->mapping_,
|
||||
std::find(
|
||||
detail::text::detail::begin(it->mapping_),
|
||||
detail::text::detail::end(it->mapping_),
|
||||
text::detail::begin(it->mapping_),
|
||||
text::detail::end(it->mapping_),
|
||||
0),
|
||||
out);
|
||||
#endif
|
||||
@@ -106,7 +104,6 @@ namespace boost::parser::detail {
|
||||
*out++ = cp;
|
||||
return out;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -58,6 +58,32 @@ namespace boost { namespace parser { namespace detail::hl {
|
||||
}
|
||||
|
||||
|
||||
// apply
|
||||
|
||||
template<typename F, typename Tuple, std::size_t... I>
|
||||
constexpr auto
|
||||
apply_impl(F && f, Tuple && t, std::integer_sequence<std::size_t, I...>)
|
||||
-> decltype(((F &&) f)(parser::get((Tuple &&) t, llong<I>{})...))
|
||||
{
|
||||
return ((F &&) f)(parser::get((Tuple &&) t, llong<I>{})...);
|
||||
}
|
||||
template<
|
||||
typename F,
|
||||
typename Tuple,
|
||||
typename Enable = std::enable_if_t<detail::is_tuple<
|
||||
std::remove_cv_t<std::remove_reference_t<Tuple>>>{}>>
|
||||
constexpr auto apply(F && f, Tuple && t) -> decltype(hl::apply_impl(
|
||||
(F &&) f,
|
||||
(Tuple &&) t,
|
||||
std::make_integer_sequence<std::size_t, tuple_size_<Tuple>>{}))
|
||||
{
|
||||
return hl::apply_impl(
|
||||
(F &&) f,
|
||||
(Tuple &&) t,
|
||||
std::make_integer_sequence<std::size_t, tuple_size_<Tuple>>{});
|
||||
}
|
||||
|
||||
|
||||
// for_each
|
||||
|
||||
template<typename F, typename Tuple, std::size_t... I>
|
||||
|
||||
@@ -12,12 +12,34 @@
|
||||
#ifndef BOOST_PARSER_DETAIL_NUMERIC_HPP
|
||||
#define BOOST_PARSER_DETAIL_NUMERIC_HPP
|
||||
|
||||
#include <boost/parser/detail/text/unpack.hpp>
|
||||
|
||||
#include <version>
|
||||
#if defined(__cpp_lib_to_chars)
|
||||
#include <charconv>
|
||||
#define BOOST_PARSER_HAVE_STD_CHARCONV
|
||||
#define BOOST_PARSER_NUMERIC_NS std_charconv
|
||||
#elif __has_include(<boost/charconv.hpp>)
|
||||
#include <boost/charconv.hpp>
|
||||
#define BOOST_PARSER_HAVE_BOOST_CHARCONV
|
||||
#define BOOST_PARSER_NUMERIC_NS boost_charconv
|
||||
#else
|
||||
#define BOOST_PARSER_NUMERIC_NS spirit_parsers
|
||||
#endif
|
||||
|
||||
#include <type_traits>
|
||||
#include <cmath>
|
||||
|
||||
#if defined(BOOST_PARSER_HAVE_STD_CHARCONV) || \
|
||||
defined(BOOST_PARSER_HAVE_BOOST_CHARCONV)
|
||||
#define BOOST_PARSER_HAVE_CHARCONV
|
||||
#endif
|
||||
|
||||
namespace boost { namespace parser { namespace detail_spirit_x3 {
|
||||
|
||||
struct unused_type{};
|
||||
namespace boost::parser::detail_spirit_x3 {
|
||||
|
||||
struct unused_type
|
||||
{};
|
||||
|
||||
// Copied from boost/spirit/home/support/char_class.hpp (Boost 1.71), and
|
||||
// modified not to use Boost.TypeTraits.
|
||||
@@ -963,7 +985,106 @@ namespace boost { namespace parser { namespace detail_spirit_x3 {
|
||||
{
|
||||
static bool const expect_dot = true;
|
||||
};
|
||||
}
|
||||
|
||||
}}}
|
||||
namespace boost::parser::detail::numeric {
|
||||
|
||||
template<typename I, typename S>
|
||||
constexpr bool common_range = std::is_same_v<I, S>;
|
||||
|
||||
template<typename I, typename S>
|
||||
using unpacked_iter = decltype(text::unpack_iterator_and_sentinel(
|
||||
std::declval<I>(), std::declval<S>())
|
||||
.first);
|
||||
|
||||
template<typename I, typename S>
|
||||
constexpr bool unpacks_to_chars =
|
||||
std::is_pointer_v<unpacked_iter<I, S>> && std::is_same_v<
|
||||
std::remove_cv_t<std::remove_reference_t<
|
||||
std::remove_pointer_t<unpacked_iter<I, S>>>>,
|
||||
char>;
|
||||
|
||||
inline namespace BOOST_PARSER_NUMERIC_NS {
|
||||
|
||||
template<int MinDigits, int MaxDigits, typename I, typename S>
|
||||
#if defined(BOOST_PARSER_HAVE_CHARCONV)
|
||||
constexpr bool use_charconv_int =
|
||||
MinDigits == 1 && MaxDigits == -1 &&
|
||||
common_range<I, S> && unpacks_to_chars<I, S>;
|
||||
#else
|
||||
constexpr bool use_charconv_int = false;
|
||||
#endif
|
||||
|
||||
template<
|
||||
bool Signed,
|
||||
int Radix,
|
||||
int MinDigits,
|
||||
int MaxDigits,
|
||||
typename I,
|
||||
typename S,
|
||||
typename T>
|
||||
bool parse_int(I & first, S last, T & attr)
|
||||
{
|
||||
if constexpr (use_charconv_int<MinDigits, MaxDigits, I, S>) {
|
||||
#if defined(BOOST_PARSER_HAVE_CHARCONV)
|
||||
auto unpacked = text::unpack_iterator_and_sentinel(first, last);
|
||||
#if defined(BOOST_PARSER_HAVE_STD_CHARCONV)
|
||||
std::from_chars_result const result = std::from_chars(
|
||||
#else
|
||||
charconv::from_chars_result const result = charconv::from_chars(
|
||||
#endif
|
||||
unpacked.first, unpacked.last, attr, Radix);
|
||||
if (result.ec == std::errc()) {
|
||||
first = unpacked.repack(result.ptr);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
#endif
|
||||
} else if constexpr (Signed) {
|
||||
using extract = detail_spirit_x3::
|
||||
extract_int<T, Radix, MinDigits, MaxDigits>;
|
||||
return extract::call(first, last, attr);
|
||||
} else {
|
||||
using extract = detail_spirit_x3::
|
||||
extract_uint<T, Radix, MinDigits, MaxDigits>;
|
||||
return extract::call(first, last, attr);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename I, typename S>
|
||||
#if defined(BOOST_PARSER_HAVE_CHARCONV)
|
||||
constexpr bool use_charconv_real =
|
||||
common_range<I, S> && unpacks_to_chars<I, S>;
|
||||
#else
|
||||
constexpr bool use_charconv_real = false;
|
||||
#endif
|
||||
|
||||
template<typename I, typename S, typename T>
|
||||
bool parse_real(I & first, S last, T & attr)
|
||||
{
|
||||
if constexpr (use_charconv_real<I, S>) {
|
||||
#if defined(BOOST_PARSER_HAVE_CHARCONV)
|
||||
auto unpacked = text::unpack_iterator_and_sentinel(first, last);
|
||||
#if defined(BOOST_PARSER_HAVE_STD_CHARCONV)
|
||||
std::from_chars_result const result = std::from_chars(
|
||||
#else
|
||||
charconv::from_chars_result const result = charconv::from_chars(
|
||||
#endif
|
||||
unpacked.first, unpacked.last, attr);
|
||||
if (result.ec == std::errc()) {
|
||||
first = unpacked.repack(result.ptr);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
#endif
|
||||
} else {
|
||||
detail_spirit_x3::real_policies<T> policies;
|
||||
using extract = detail_spirit_x3::
|
||||
extract_real<T, detail_spirit_x3::real_policies<T>>;
|
||||
return extract::parse(first, last, attr, policies);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -71,6 +71,13 @@ namespace boost { namespace parser { namespace detail {
|
||||
std::ostream & os,
|
||||
int components = 0);
|
||||
|
||||
template<typename Context, typename ParserTuple>
|
||||
void print_parser(
|
||||
Context const & context,
|
||||
perm_parser<ParserTuple> const & parser,
|
||||
std::ostream & os,
|
||||
int components = 0);
|
||||
|
||||
template<
|
||||
typename Context,
|
||||
typename ParserTuple,
|
||||
@@ -90,6 +97,13 @@ namespace boost { namespace parser { namespace detail {
|
||||
std::ostream & os,
|
||||
int components = 0);
|
||||
|
||||
template<typename Context, typename Parser, typename F>
|
||||
void print_parser(
|
||||
Context const & context,
|
||||
transform_parser<Parser, F> const & parser,
|
||||
std::ostream & os,
|
||||
int components = 0);
|
||||
|
||||
template<typename Context, typename Parser>
|
||||
void print_parser(
|
||||
Context const & context,
|
||||
@@ -134,10 +148,10 @@ namespace boost { namespace parser { namespace detail {
|
||||
std::ostream & os,
|
||||
int components = 0);
|
||||
|
||||
template<typename Context, typename Parser, bool FailOnMatch>
|
||||
template<typename Context, typename Parser, typename ParserConfig>
|
||||
void print_parser(
|
||||
Context const & context,
|
||||
expect_parser<Parser, FailOnMatch> const & parser,
|
||||
expect_parser_t<Parser, ParserConfig> const & parser,
|
||||
std::ostream & os,
|
||||
int components = 0);
|
||||
|
||||
@@ -554,6 +568,7 @@ namespace boost { namespace parser { namespace detail {
|
||||
auto resolve(Context const &, nope n);
|
||||
|
||||
template<
|
||||
bool DoTrace,
|
||||
typename Iter,
|
||||
typename Sentinel,
|
||||
typename Context,
|
||||
@@ -607,6 +622,16 @@ namespace boost { namespace parser { namespace detail {
|
||||
std::string name_;
|
||||
};
|
||||
|
||||
template<
|
||||
typename Iter,
|
||||
typename Sentinel,
|
||||
typename Context,
|
||||
typename Attribute>
|
||||
struct scoped_trace_t<false, Iter, Sentinel, Context, Attribute>
|
||||
{
|
||||
scoped_trace_t() {}
|
||||
};
|
||||
|
||||
template<
|
||||
typename Parser,
|
||||
typename Iter,
|
||||
@@ -621,11 +646,14 @@ namespace boost { namespace parser { namespace detail {
|
||||
flags f,
|
||||
Attribute const & attr)
|
||||
{
|
||||
std::stringstream oss;
|
||||
if (detail::do_trace(f))
|
||||
if constexpr (Context::do_trace) {
|
||||
std::stringstream oss;
|
||||
detail::print_parser(context, parser, oss);
|
||||
return scoped_trace_t<Iter, Sentinel, Context, Attribute>(
|
||||
first, last, context, f, attr, oss.str());
|
||||
return scoped_trace_t<true, Iter, Sentinel, Context, Attribute>(
|
||||
first, last, context, f, attr, oss.str());
|
||||
} else {
|
||||
return scoped_trace_t<false, Iter, Sentinel, Context, Attribute>{};
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Context, typename Attribute>
|
||||
|
||||
@@ -10,25 +10,29 @@
|
||||
#endif
|
||||
#include <boost/type_index.hpp>
|
||||
#define BOOST_PARSER_HAVE_BOOST_TYPEINDEX 1
|
||||
#define BOOST_PARSER_TYPE_NAME_NS boost_type_index
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
#else
|
||||
#include <typeinfo>
|
||||
#define BOOST_PARSER_HAVE_BOOST_TYPEINDEX 0
|
||||
#define BOOST_PARSER_TYPE_NAME_NS std_typeinfo
|
||||
#endif
|
||||
|
||||
|
||||
namespace boost { namespace parser { namespace detail {
|
||||
|
||||
template<typename T>
|
||||
auto type_name()
|
||||
{
|
||||
inline namespace BOOST_PARSER_TYPE_NAME_NS {
|
||||
template<typename T>
|
||||
auto type_name()
|
||||
{
|
||||
#if BOOST_PARSER_HAVE_BOOST_TYPEINDEX
|
||||
return typeindex::type_id<T>().pretty_name();
|
||||
return typeindex::type_id<T>().pretty_name();
|
||||
#else
|
||||
return typeid(T).name();
|
||||
return typeid(T).name();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Parser>
|
||||
@@ -59,6 +63,10 @@ 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 BacktrackingTuple,
|
||||
@@ -161,6 +169,32 @@ namespace boost { namespace parser { namespace detail {
|
||||
os << ")";
|
||||
}
|
||||
|
||||
template<typename Context, typename Parser>
|
||||
void print_or_like_parser(
|
||||
Context const & context,
|
||||
Parser const & parser,
|
||||
std::ostream & os,
|
||||
int components,
|
||||
std::string_view or_ellipsis,
|
||||
std::string_view ws_or)
|
||||
{
|
||||
int i = 0;
|
||||
bool printed_ellipsis = false;
|
||||
hl::for_each(parser.parsers_, [&](auto const & parser) {
|
||||
if (components == parser_component_limit) {
|
||||
if (!printed_ellipsis)
|
||||
os << or_ellipsis;
|
||||
printed_ellipsis = true;
|
||||
return;
|
||||
}
|
||||
if (i)
|
||||
os << ws_or;
|
||||
detail::print_parser(context, parser, os, components);
|
||||
++components;
|
||||
++i;
|
||||
});
|
||||
}
|
||||
|
||||
template<typename Context, typename ParserTuple>
|
||||
void print_parser(
|
||||
Context const & context,
|
||||
@@ -168,21 +202,19 @@ namespace boost { namespace parser { namespace detail {
|
||||
std::ostream & os,
|
||||
int components)
|
||||
{
|
||||
int i = 0;
|
||||
bool printed_ellipsis = false;
|
||||
hl::for_each(parser.parsers_, [&](auto const & parser) {
|
||||
if (components == parser_component_limit) {
|
||||
if (!printed_ellipsis)
|
||||
os << " | ...";
|
||||
printed_ellipsis = true;
|
||||
return;
|
||||
}
|
||||
if (i)
|
||||
os << " | ";
|
||||
detail::print_parser(context, parser, os, components);
|
||||
++components;
|
||||
++i;
|
||||
});
|
||||
detail::print_or_like_parser(
|
||||
context, parser, os, components, " | ...", " | ");
|
||||
}
|
||||
|
||||
template<typename Context, typename ParserTuple>
|
||||
void print_parser(
|
||||
Context const & context,
|
||||
perm_parser<ParserTuple> const & parser,
|
||||
std::ostream & os,
|
||||
int components)
|
||||
{
|
||||
detail::print_or_like_parser(
|
||||
context, parser, os, components, " || ...", " || ");
|
||||
}
|
||||
|
||||
template<
|
||||
@@ -226,7 +258,7 @@ namespace boost { namespace parser { namespace detail {
|
||||
detail::print_parser(context, parser, os, components);
|
||||
++components;
|
||||
++i;
|
||||
prev_group = group;
|
||||
prev_group = (int)group;
|
||||
});
|
||||
if (prev_group && !printed_ellipsis)
|
||||
os << ']';
|
||||
@@ -259,6 +291,17 @@ namespace boost { namespace parser { namespace detail {
|
||||
os << "]";
|
||||
}
|
||||
|
||||
template<typename Context, typename Parser, typename F>
|
||||
void print_parser(
|
||||
Context const & context,
|
||||
transform_parser<Parser, F> const & parser,
|
||||
std::ostream & os,
|
||||
int components)
|
||||
{
|
||||
detail::print_directive(
|
||||
context, "transform(<<f>>)", parser.parser_, os, components);
|
||||
}
|
||||
|
||||
template<typename Context, typename Parser>
|
||||
void print_parser(
|
||||
Context const & context,
|
||||
@@ -335,14 +378,14 @@ namespace boost { namespace parser { namespace detail {
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Context, typename Parser, bool FailOnMatch>
|
||||
template<typename Context, typename Parser, typename ParserConfig>
|
||||
void print_parser(
|
||||
Context const & context,
|
||||
expect_parser<Parser, FailOnMatch> const & parser,
|
||||
expect_parser_t<Parser, ParserConfig> const & parser,
|
||||
std::ostream & os,
|
||||
int components)
|
||||
{
|
||||
if (FailOnMatch)
|
||||
if (ParserConfig::fail_on_match)
|
||||
os << "!";
|
||||
else
|
||||
os << "&";
|
||||
@@ -825,11 +868,12 @@ namespace boost { namespace parser { namespace detail {
|
||||
int components)
|
||||
{
|
||||
using namespace literals;
|
||||
|
||||
os << "("
|
||||
<< detail::resolve(
|
||||
context, parser::get(parser.parsers_, 0_c).pred_.value_)
|
||||
<< ", ";
|
||||
os << "(";
|
||||
detail::print(
|
||||
os,
|
||||
detail::resolve(
|
||||
context, parser::get(parser.parsers_, 0_c).pred_.value_));
|
||||
os << ", ";
|
||||
detail::print_parser(
|
||||
context, parser::get(parser.parsers_, 1_c), os, components);
|
||||
os << ")";
|
||||
|
||||
@@ -131,8 +131,7 @@ namespace boost::parser::detail::text::detail {
|
||||
|
||||
constexpr all_impl all;
|
||||
|
||||
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS && \
|
||||
(!defined(__GNUC__) || 12 <= __GNUC__)
|
||||
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS
|
||||
template<typename R>
|
||||
using all_t = std::views::all_t<R>;
|
||||
#else
|
||||
|
||||
@@ -383,7 +383,7 @@ namespace boost::parser::detail { namespace text {
|
||||
boost::parser::detail::text::continuation(*--retval)) {
|
||||
++backup;
|
||||
}
|
||||
backup = std::distance(retval, it);
|
||||
backup = (int)std::distance(retval, it);
|
||||
|
||||
if (boost::parser::detail::text::continuation(*retval)) {
|
||||
if (it != first)
|
||||
@@ -396,7 +396,7 @@ namespace boost::parser::detail { namespace text {
|
||||
++*first_invalid;
|
||||
while (first_invalid &&
|
||||
std::distance(retval, *first_invalid) < backup) {
|
||||
backup -= std::distance(retval, *first_invalid);
|
||||
backup -= (int)std::distance(retval, *first_invalid);
|
||||
retval = *first_invalid;
|
||||
first_invalid = end_of_invalid_utf8(retval);
|
||||
if (first_invalid == retval)
|
||||
@@ -2568,6 +2568,42 @@ namespace boost::parser::detail { namespace text {
|
||||
};
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
struct iter_access
|
||||
{
|
||||
template<typename T>
|
||||
static auto & buf(T & it)
|
||||
{
|
||||
return it.buf_;
|
||||
}
|
||||
template<typename T>
|
||||
static auto & first_and_curr(T & it)
|
||||
{
|
||||
return it.first_and_curr_;
|
||||
}
|
||||
template<typename T>
|
||||
static auto & buf_index(T & it)
|
||||
{
|
||||
return it.buf_index_;
|
||||
}
|
||||
template<typename T>
|
||||
static auto & buf_last(T & it)
|
||||
{
|
||||
return it.buf_last_;
|
||||
}
|
||||
template<typename T>
|
||||
static auto & to_increment(T & it)
|
||||
{
|
||||
return it.to_increment_;
|
||||
}
|
||||
template<typename T>
|
||||
static auto & last(T & it)
|
||||
{
|
||||
return it.last_;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS
|
||||
template<
|
||||
format FromFormat,
|
||||
@@ -2641,7 +2677,7 @@ namespace boost::parser::detail { namespace text {
|
||||
#if !BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS
|
||||
template<
|
||||
typename J = I,
|
||||
typename Enable = std::enable_if_t<is_bidirectional<J>>>
|
||||
typename Enable = std::enable_if_t<!is_bidirectional<J>>>
|
||||
#endif
|
||||
constexpr utf_iterator(I it, S last)
|
||||
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS
|
||||
@@ -2669,11 +2705,12 @@ namespace boost::parser::detail { namespace text {
|
||||
constexpr utf_iterator(
|
||||
utf_iterator<FromFormat, ToFormat, I2, S2, ErrorHandler> const &
|
||||
other) :
|
||||
buf_(other.buf_),
|
||||
first_and_curr_(other.first_and_curr_),
|
||||
buf_index_(other.buf_index_),
|
||||
buf_last_(other.buf_last_),
|
||||
last_(other.last_)
|
||||
buf_(detail::iter_access::buf(other)),
|
||||
first_and_curr_(detail::iter_access::first_and_curr(other)),
|
||||
buf_index_(detail::iter_access::buf_index(other)),
|
||||
buf_last_(detail::iter_access::buf_last(other)),
|
||||
to_increment_(detail::iter_access::to_increment(other)),
|
||||
last_(detail::iter_access::last(other))
|
||||
{}
|
||||
|
||||
#if !BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS
|
||||
@@ -3181,7 +3218,7 @@ namespace boost::parser::detail { namespace text {
|
||||
auto it = encode_code_point(cp, buf_.begin());
|
||||
buf_last_ = uint8_t(it - buf_.begin());
|
||||
buf_index_ = buf_last_ - 1;
|
||||
to_increment_ = std::distance(curr(), initial);
|
||||
to_increment_ = (int)std::distance(curr(), initial);
|
||||
} else {
|
||||
auto buf = buf_;
|
||||
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS
|
||||
@@ -3217,33 +3254,17 @@ namespace boost::parser::detail { namespace text {
|
||||
constexpr I & curr() { return first_and_curr_.curr; }
|
||||
constexpr I curr() const { return first_and_curr_.curr; }
|
||||
|
||||
std::array<value_type, 4 / static_cast<int>(ToFormat)> buf_;
|
||||
std::array<value_type, 4 / static_cast<int>(ToFormat)> buf_ = {};
|
||||
|
||||
detail::first_and_curr<I> first_and_curr_;
|
||||
detail::first_and_curr<I> first_and_curr_ = {};
|
||||
|
||||
uint8_t buf_index_ = 0;
|
||||
uint8_t buf_last_ = 0;
|
||||
uint8_t to_increment_ = 0;
|
||||
|
||||
[[no_unique_address]] S last_;
|
||||
[[no_unique_address]] S last_ = {};
|
||||
|
||||
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS
|
||||
template<
|
||||
format FromFormat2,
|
||||
format ToFormat2,
|
||||
std::input_iterator I2,
|
||||
std::sentinel_for<I2> S2,
|
||||
transcoding_error_handler ErrorHandler2>
|
||||
requires std::convertible_to<std::iter_value_t<I2>, detail::format_to_type_t<FromFormat2>>
|
||||
#else
|
||||
template<
|
||||
format FromFormat2,
|
||||
format ToFormat2,
|
||||
typename I2,
|
||||
typename S2,
|
||||
typename ErrorHandler2>
|
||||
#endif
|
||||
friend class utf_iterator;
|
||||
friend struct detail::iter_access;
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
@@ -44,27 +44,13 @@ namespace boost::parser::detail { namespace text {
|
||||
using iterator_to_tag_t = decltype(iterator_to_tag<I>());
|
||||
|
||||
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS
|
||||
#if defined(__GNUC__) && __GNUC__ < 12
|
||||
// This uses the final version of viewable_range. Older GCCs use a
|
||||
// different one that breaks, so we use this on instead of the one
|
||||
// from std::ranges::. It's missing the !initializer_list part.
|
||||
template<typename T>
|
||||
concept viewable_range = std::ranges::range<T> &&
|
||||
((std::ranges::view<std::remove_cvref_t<T>> &&
|
||||
std::constructible_from<std::remove_cvref_t<T>, T>) ||
|
||||
(!std::ranges::view<std::remove_cvref_t<T>> &&
|
||||
(std::is_lvalue_reference_v<T> ||
|
||||
std::movable<std::remove_reference_t<T>>)));
|
||||
#else
|
||||
template<typename T>
|
||||
concept viewable_range = std::ranges::viewable_range<T>;
|
||||
#endif
|
||||
|
||||
template<class T>
|
||||
using with_reference = T &;
|
||||
template<typename T>
|
||||
concept can_reference = requires { typename with_reference<T>; };
|
||||
#endif
|
||||
|
||||
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS
|
||||
template<class Char>
|
||||
struct cast_to_charn {
|
||||
constexpr Char operator()(Char c) const { return c; }
|
||||
@@ -279,7 +265,7 @@ namespace boost::parser::detail { namespace text {
|
||||
|
||||
#if BOOST_PARSER_DETAIL_TEXT_USE_ALIAS_CTAD
|
||||
template<class R, auto F>
|
||||
project_view(R &&) -> project_view<detail::all_t<R>, F>;
|
||||
project_view(R &&) -> project_view<std::views::all_t<R>, F>;
|
||||
#endif
|
||||
|
||||
namespace detail {
|
||||
@@ -295,7 +281,7 @@ namespace boost::parser::detail { namespace text {
|
||||
|
||||
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS
|
||||
template<class R>
|
||||
requires viewable_range<R> &&
|
||||
requires std::ranges::viewable_range<R> &&
|
||||
std::ranges::input_range<R> &&
|
||||
std::regular_invocable<decltype(F)&, std::ranges::range_reference_t<R>> &&
|
||||
detail::can_reference<std::invoke_result_t<decltype(F)&, std::ranges::range_reference_t<R>>>
|
||||
@@ -342,7 +328,11 @@ namespace boost::parser::detail { namespace text {
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
constexpr char8_view() requires std::default_initializable<V> = default;
|
||||
constexpr char8_view()
|
||||
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS
|
||||
requires std::default_initializable<V>
|
||||
#endif
|
||||
= default;
|
||||
constexpr char8_view(V base) :
|
||||
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS
|
||||
project_view<V, detail::cast_to_charn<char8_t>{}>{std::move(base)}
|
||||
@@ -416,7 +406,7 @@ namespace boost::parser::detail { namespace text {
|
||||
{
|
||||
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS
|
||||
template<class R>
|
||||
requires (viewable_range<R> &&
|
||||
requires (std::ranges::viewable_range<R> &&
|
||||
std::ranges::input_range<R> &&
|
||||
std::convertible_to<std::ranges::range_reference_t<R>, format_to_type_t<Format>>) ||
|
||||
utf_pointer<std::remove_cvref_t<R>>
|
||||
@@ -620,7 +610,7 @@ namespace boost::parser::detail { namespace text {
|
||||
#if BOOST_PARSER_DETAIL_TEXT_USE_ALIAS_CTAD
|
||||
|
||||
template<format Format, class R>
|
||||
utf_view(R &&) -> utf_view<Format, detail::all_t<R>>;
|
||||
utf_view(R &&) -> utf_view<Format, std::views::all_t<R>>;
|
||||
|
||||
template<class V>
|
||||
using utf8_view = utf_view<format::utf8, V>;
|
||||
@@ -771,7 +761,7 @@ namespace boost::parser::detail { namespace text {
|
||||
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS
|
||||
template<class R>
|
||||
requires is_utf_view<std::remove_cvref_t<R>> ||
|
||||
(viewable_range<R> &&
|
||||
(std::ranges::viewable_range<R> &&
|
||||
can_utf_view<unpacked_range<R>, View>) ||
|
||||
utf_pointer<std::remove_cvref_t<R>>
|
||||
#else
|
||||
|
||||
1228
include/boost/parser/detail/text/trie_map.hpp
Normal file
1228
include/boost/parser/detail/text/trie_map.hpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -7,6 +7,7 @@
|
||||
#include <boost/parser/detail/text/algorithm.hpp>
|
||||
#include <boost/parser/detail/text/transcode_iterator.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,6 +9,7 @@
|
||||
#include <boost/parser/config.hpp>
|
||||
#include <boost/parser/error_handling_fwd.hpp>
|
||||
|
||||
#include <any>
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
@@ -43,6 +44,20 @@ namespace boost { namespace parser {
|
||||
constexpr bool enable_variant<std::variant<Ts...>> = true;
|
||||
#endif
|
||||
|
||||
/** A type trait that evaluates to the attribute type for parser `Parser`
|
||||
used to parse range `R`, as if by calling `parse(r, parser)`, using
|
||||
some `R r` and `Parser parser`. Note that this implies that pointers
|
||||
to null-terminated strings are supported types for `R`. The result is
|
||||
not wrapped in a `std::optional` like the result of a call to
|
||||
`parse()` would be. If `Parser` produces no attribute, the result is
|
||||
the no-attribute sentinel type `none`. */
|
||||
template<typename R, typename Parser>
|
||||
struct attribute;
|
||||
|
||||
/** An alias for `typename attribute<R, Parser>::type`. */
|
||||
template<typename R, typename Parser>
|
||||
using attribute_t = typename attribute<R, Parser>::type;
|
||||
|
||||
namespace detail {
|
||||
template<typename T>
|
||||
constexpr bool is_optional_v = enable_optional<T>;
|
||||
@@ -55,72 +70,17 @@ namespace boost { namespace parser {
|
||||
trace = 1 << 2,
|
||||
in_apply_parser = 1 << 3
|
||||
};
|
||||
|
||||
struct any_copyable
|
||||
{
|
||||
template<
|
||||
typename T,
|
||||
typename Enable = std::enable_if_t<!std::is_reference_v<T>>>
|
||||
any_copyable(T && v) :
|
||||
impl_(new holder<std::decay_t<T>>(std::move(v)))
|
||||
{}
|
||||
template<typename T>
|
||||
any_copyable(T const & v) : impl_(new holder<T>(v))
|
||||
{}
|
||||
|
||||
any_copyable() = default;
|
||||
any_copyable(any_copyable const & other)
|
||||
{
|
||||
if (other.impl_)
|
||||
impl_ = other.impl_->clone();
|
||||
}
|
||||
any_copyable & operator=(any_copyable const & other)
|
||||
{
|
||||
any_copyable temp(other);
|
||||
swap(temp);
|
||||
return *this;
|
||||
}
|
||||
any_copyable(any_copyable &&) = default;
|
||||
any_copyable & operator=(any_copyable &&) = default;
|
||||
|
||||
bool empty() const { return impl_.get() == nullptr; }
|
||||
|
||||
template<typename T>
|
||||
T & cast() const
|
||||
{
|
||||
BOOST_PARSER_DEBUG_ASSERT(impl_);
|
||||
BOOST_PARSER_DEBUG_ASSERT(dynamic_cast<holder<T> *>(impl_.get()));
|
||||
return static_cast<holder<T> *>(impl_.get())->value_;
|
||||
}
|
||||
|
||||
void swap(any_copyable & other) { std::swap(impl_, other.impl_); }
|
||||
|
||||
private:
|
||||
struct holder_base
|
||||
{
|
||||
virtual ~holder_base() {}
|
||||
virtual std::unique_ptr<holder_base> clone() const = 0;
|
||||
};
|
||||
template<typename T>
|
||||
struct holder : holder_base
|
||||
{
|
||||
holder(T && v) : value_(std::move(v)) {}
|
||||
holder(T const & v) : value_(v) {}
|
||||
virtual ~holder() {}
|
||||
virtual std::unique_ptr<holder_base> clone() const
|
||||
{
|
||||
return std::unique_ptr<holder_base>(new holder<T>{value_});
|
||||
}
|
||||
T value_;
|
||||
};
|
||||
|
||||
std::unique_ptr<holder_base> impl_;
|
||||
};
|
||||
constexpr inline flags disable_attrs(flags f);
|
||||
|
||||
using symbol_table_tries_t =
|
||||
std::map<void *, any_copyable, std::less<void *>>;
|
||||
std::map<void *, std::pair<std::any, bool>, std::less<void *>>;
|
||||
|
||||
template<typename Iter, typename Sentinel, typename ErrorHandler>
|
||||
template<
|
||||
bool DoTrace,
|
||||
bool UseCallbacks,
|
||||
typename Iter,
|
||||
typename Sentinel,
|
||||
typename ErrorHandler>
|
||||
inline auto make_context(
|
||||
Iter first,
|
||||
Sentinel last,
|
||||
@@ -157,6 +117,25 @@ namespace boost { namespace parser {
|
||||
{};
|
||||
struct upper_case_chars
|
||||
{};
|
||||
|
||||
template<
|
||||
bool OmitAttr = false,
|
||||
bool DontConsumeInput = false,
|
||||
bool FailOnMatch = false,
|
||||
typename Action = nope>
|
||||
struct parser_config
|
||||
{
|
||||
static constexpr bool omit = OmitAttr;
|
||||
static constexpr bool dont_consume_input = DontConsumeInput;
|
||||
static constexpr bool fail_on_match = FailOnMatch;
|
||||
|
||||
static constexpr detail::flags flags(detail::flags f)
|
||||
{
|
||||
return omit ? detail::disable_attrs(f) : f;
|
||||
}
|
||||
|
||||
[[maybe_unused]] Action action_;
|
||||
};
|
||||
}
|
||||
|
||||
/** Repeats the application of another parser `p` of type `Parser`,
|
||||
@@ -169,36 +148,15 @@ namespace boost { namespace parser {
|
||||
typename Parser,
|
||||
typename DelimiterParser = detail::nope,
|
||||
typename MinType = int64_t,
|
||||
typename MaxType = int64_t>
|
||||
typename MaxType = int64_t,
|
||||
typename ParserConfig = parser_config<>>
|
||||
struct repeat_parser;
|
||||
|
||||
/** Repeats the application of another parser `p` of type `Parser`, `[0,
|
||||
Inf)` times. The parse always succeeds. The attribute produced is a
|
||||
sequence of the type of attribute produced by `Parser`. */
|
||||
template<typename Parser>
|
||||
struct zero_plus_parser;
|
||||
|
||||
/** Repeats the application of another parser `p` of type `Parser`, `[1,
|
||||
Inf)` times. The parse succeeds iff `p` succeeds at least once. The
|
||||
attribute produced is a sequence of the type of attribute produced by
|
||||
`Parser`. */
|
||||
template<typename Parser>
|
||||
struct one_plus_parser;
|
||||
|
||||
/** Repeats the application of another parser `p` of type `Parser`, `[1,
|
||||
Inf)` times, applying a parser `d` of type `DelimiterParser` in
|
||||
between each pair of applications of `p`. The parse succeeds iff `p`
|
||||
succeeds at least once, and `d` succeeds each time it is applied. The
|
||||
attribute produced is a sequence of the type of attribute produced by
|
||||
`Parser`. */
|
||||
template<typename Parser, typename DelimiterParser>
|
||||
struct delimited_seq_parser;
|
||||
|
||||
/** Repeats the application of another parser of type `Parser`, `[0, 1]`
|
||||
times. The parse always succeeds. The attribute produced is a
|
||||
`std::optional<T>`, where `T` is the type of attribute produced by
|
||||
`Parser`. */
|
||||
template<typename Parser>
|
||||
template<typename Parser, typename ParserConfig = parser_config<>>
|
||||
struct opt_parser;
|
||||
|
||||
/** Applies each parser in `ParserTuple`, in order, stopping after the
|
||||
@@ -206,9 +164,20 @@ namespace boost { namespace parser {
|
||||
one of the sub-parsers succeeds. The attribute produced is a
|
||||
`std::variant` over the types of attribute produced by the parsers in
|
||||
`ParserTuple`. */
|
||||
template<typename ParserTuple>
|
||||
template<typename ParserTuple, typename ParserConfig = parser_config<>>
|
||||
struct or_parser;
|
||||
|
||||
/** Applies each parsers in `ParserTuple`, an any order, stopping after
|
||||
all of them have matched the input. The parse succeeds iff all the
|
||||
parsers match, regardless of the order in which they do. The
|
||||
attribute produced is a `parser::tuple` containing the attributes of
|
||||
the subparsers, in their order of the parsers' appearance in
|
||||
`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, typename ParserConfig = parser_config<>>
|
||||
struct perm_parser;
|
||||
|
||||
/** Applies each parser in `ParserTuple`, in order. The parse succeeds
|
||||
iff all of the sub-parsers succeed. The attribute produced is a
|
||||
`std::tuple` over the types of attribute produced by the parsers in
|
||||
@@ -219,28 +188,43 @@ namespace boost { namespace parser {
|
||||
template<
|
||||
typename ParserTuple,
|
||||
typename BacktrackingTuple,
|
||||
typename CombiningGroups>
|
||||
typename CombiningGroups,
|
||||
typename ParserConfig = parser_config<>>
|
||||
struct seq_parser;
|
||||
|
||||
/** Applies the given parser `p` of type `Parser` and an invocable `a` of
|
||||
type `Action`. `Action` shall model `semantic_action`, and `a` will
|
||||
only be invoked if `p` succeeds. The parse succeeds iff `p` succeeds.
|
||||
Produces no attribute. */
|
||||
template<typename Parser, typename Action>
|
||||
template<
|
||||
typename Parser,
|
||||
typename Action,
|
||||
typename ParserConfig = parser_config<>>
|
||||
struct action_parser;
|
||||
|
||||
/** Applies the given parser `p` of type `Parser`. The attribute produced
|
||||
by `p` is passed to the fiven invocable `f` of type `F`. `f` will
|
||||
only be invoked if `p` succeeds and sttributes are currently being
|
||||
generated. The parse succeeds iff `p` succeeds. The attribute
|
||||
produced is the the result of the call to `f`. */
|
||||
template<
|
||||
typename Parser,
|
||||
typename F,
|
||||
typename ParserConfig = parser_config<>>
|
||||
struct transform_parser;
|
||||
|
||||
/** Applies the given parser `p` of type `Parser`. This parser produces
|
||||
no attribute, and suppresses the production of any attributes that
|
||||
would otherwise be produced by `p`. The parse succeeds iff `p`
|
||||
succeeds. */
|
||||
template<typename Parser>
|
||||
template<typename Parser, typename ParserConfig = parser_config<>>
|
||||
struct omit_parser;
|
||||
|
||||
/** Applies the given parser `p` of type `Parser`; regardless of the
|
||||
attribute produced by `Parser`, this parser's attribute is equivalent
|
||||
to `_where(ctx)` within a semantic action on `p`. The parse succeeds
|
||||
iff `p` succeeds. */
|
||||
template<typename Parser>
|
||||
template<typename Parser, typename ParserConfig = parser_config<>>
|
||||
struct raw_parser;
|
||||
|
||||
#if defined(BOOST_PARSER_DOXYGEN) || defined(__cpp_lib_concepts)
|
||||
@@ -253,43 +237,49 @@ namespace boost { namespace parser {
|
||||
non-contiguous, code using `string_view_parser` is ill-formed. The
|
||||
parse succeeds iff `p` succeeds. This parser is only available in
|
||||
C++20 and later. */
|
||||
template<typename Parser>
|
||||
template<typename Parser, typename ParserConfig = parser_config<>>
|
||||
struct string_view_parser;
|
||||
#endif
|
||||
|
||||
/** Applies the given parser `p` of type `Parser`, disabling the current
|
||||
skipper in use, if any. The parse succeeds iff `p` succeeds. The
|
||||
attribute produced is the type of attribute produced by `Parser`. */
|
||||
template<typename Parser>
|
||||
template<typename Parser, typename ParserConfig = parser_config<>>
|
||||
struct lexeme_parser;
|
||||
|
||||
/** Applies the given parser `p` of type `Parser`, enabling
|
||||
case-insensitive matching, based on Unicode case folding. The parse
|
||||
succeeds iff `p` succeeds. The attribute produced is the type of
|
||||
attribute produced by `Parser`. */
|
||||
template<typename Parser>
|
||||
template<typename Parser, typename ParserConfig = parser_config<>>
|
||||
struct no_case_parser;
|
||||
|
||||
/** Applies the given parser `p` of type `Parser`, using a parser of type
|
||||
`SkipParser` as the skipper. The parse succeeds iff `p` succeeds.
|
||||
The attribute produced is the type of attribute produced by
|
||||
`Parser`. */
|
||||
template<typename Parser, typename SkipParser = detail::nope>
|
||||
template<
|
||||
typename Parser,
|
||||
typename SkipParser = detail::nope,
|
||||
typename ParserConfig = parser_config<>>
|
||||
struct skip_parser;
|
||||
|
||||
/** Applies the given parser `p` of type `Parser`, producing no attributes
|
||||
and consuming no input. The parse succeeds iff `p`'s success is
|
||||
unequal to `FailOnMatch`. */
|
||||
template<typename Parser, bool FailOnMatch>
|
||||
template<
|
||||
typename Parser,
|
||||
typename ParserConfig,
|
||||
typename ParserConfig = parser_config<>>
|
||||
struct expect_parser;
|
||||
|
||||
/** Matches one of a set S of possible inputs, each of which is associated
|
||||
with an attribute value of type `T`, forming a symbol table. New
|
||||
elements and their associated attributes may be added to or removed
|
||||
from S dynamically, during parsing; any such changes are reverted at
|
||||
the end of parsing. The parse succeeds iff an element of S is
|
||||
matched. \see `symbols` */
|
||||
template<typename T>
|
||||
/** Matches one of a set S of possible inputs, each of which is
|
||||
associated with an attribute value of type `T`, forming a symbol
|
||||
table. New elements and their associated attributes may be added to
|
||||
or removed from S dynamically, during parsing; any such changes are
|
||||
reverted at the end of parsing. The parse succeeds iff an element of
|
||||
S is matched. \see `symbols` */
|
||||
template<typename T, typename ParserConfig = parser_config<>>
|
||||
struct symbol_parser;
|
||||
|
||||
/** Applies another parser `p`, associated with this parser via `TagType`.
|
||||
@@ -308,22 +298,24 @@ namespace boost { namespace parser {
|
||||
typename TagType,
|
||||
typename Attribute,
|
||||
typename LocalState,
|
||||
typename ParamsTuple>
|
||||
typename ParamsTuple,
|
||||
typename ParserConfig = parser_config<>>
|
||||
struct rule_parser;
|
||||
|
||||
/** Matches anything, and consumes no input. If `Predicate` is anything
|
||||
other than `detail::nope` (which it is by default), and `pred_(ctx)`
|
||||
evaluates to false, where `ctx` is the parser context, the parse
|
||||
fails. */
|
||||
template<typename Predicate>
|
||||
template<typename Predicate, typename ParserConfig = parser_config<>>
|
||||
struct eps_parser;
|
||||
|
||||
/** Matches only the end of input. Produces no attribute. */
|
||||
template<typename ParserConfig = parser_config<>>
|
||||
struct eoi_parser;
|
||||
|
||||
/** Matches anything, consumes no input, and produces an attribute of type
|
||||
`RESOLVE(Attribute)`. */
|
||||
template<typename Attribute>
|
||||
template<typename Attribute, typename ParserConfig = parser_config<>>
|
||||
struct attr_parser;
|
||||
|
||||
/** A tag type that can be passed as the first parameter to `char_()` when
|
||||
@@ -462,13 +454,14 @@ namespace boost { namespace parser {
|
||||
typename ParamsTuple = no_params>
|
||||
struct callback_rule;
|
||||
|
||||
#ifdef BOOST_PARSER_DOXYGEN
|
||||
/** Returns a reference to the attribute(s) (i.e. return value) of the
|
||||
bottommost parser; multiple attributes will be stored within a
|
||||
`parser::tuple`. You may write to this value in a semantic action to
|
||||
control what attribute value(s) the associated parser produces.
|
||||
Returns `none` if the bottommost parser does produce an attribute. */
|
||||
template<typename Context>
|
||||
decltype(auto) _val(Context const & context);
|
||||
#endif
|
||||
|
||||
/** Returns a reference to the attribute or attributes already produced by
|
||||
the bottommost parser; multiple attributes will be stored within a
|
||||
@@ -527,9 +520,13 @@ namespace boost { namespace parser {
|
||||
|
||||
/** Report that the error described in `message` occurred at `location`,
|
||||
using the context's error handler. */
|
||||
template<typename Iter, typename Context>
|
||||
#if BOOST_PARSER_USE_CONCEPTS
|
||||
template<std::forward_iterator I, typename Context>
|
||||
#else
|
||||
template<typename I, typename Context>
|
||||
#endif
|
||||
void _report_error(
|
||||
Context const & context, std::string_view message, Iter location);
|
||||
Context const & context, std::string_view message, I location);
|
||||
|
||||
/** Report that the error described in `message` occurred at
|
||||
`_where(context).begin()`, using the context's error handler. */
|
||||
@@ -538,9 +535,13 @@ namespace boost { namespace parser {
|
||||
|
||||
/** Report that the warning described in `message` occurred at `location`,
|
||||
using the context's error handler. */
|
||||
template<typename Iter, typename Context>
|
||||
#if BOOST_PARSER_USE_CONCEPTS
|
||||
template<std::forward_iterator I, typename Context>
|
||||
#else
|
||||
template<typename I, typename Context>
|
||||
#endif
|
||||
void _report_warning(
|
||||
Context const & context, std::string_view message, Iter location);
|
||||
Context const & context, std::string_view message, I location);
|
||||
|
||||
/** Report that the warning described in `message` occurred at
|
||||
`_where(context).begin()`, using the context's error handler. */
|
||||
|
||||
@@ -193,7 +193,12 @@ namespace boost::parser {
|
||||
|
||||
/** Produces a range of subranges of a given range `base`. Each subrange
|
||||
is either a subrange of `base` that does not match the given parser
|
||||
`parser`, or is the given replacement for a match, `replacement`. */
|
||||
`parser`, or is the given replacement for a match, `replacement`.
|
||||
|
||||
In addition to the template parameter constraints, `V` and
|
||||
`ReplacementV` must be ranges of `char`, or must have the same UTF
|
||||
format, and `V` and `ReplacementV` must meet the same compatibility
|
||||
requirements as described in `std::ranges::join_view`. */
|
||||
template<
|
||||
#if BOOST_PARSER_USE_CONCEPTS
|
||||
std::ranges::viewable_range V,
|
||||
|
||||
@@ -289,7 +289,7 @@ namespace boost::parser {
|
||||
|
||||
/** Produces a sequence of subranges of the underlying sequence of type
|
||||
`V`. Each subrange is a nonoverlapping match of the given parser,
|
||||
using a skip-parser if provided. */
|
||||
using a skip-parser if provided. */
|
||||
template<
|
||||
#if BOOST_PARSER_USE_CONCEPTS
|
||||
std::ranges::viewable_range V,
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace boost::parser {
|
||||
|
||||
/** Produces a sequence of subranges of the underlying sequence of type
|
||||
`V`. the underlying sequence is split into subranges delimited by
|
||||
matches of the given parser, possibly using a given skip-parser. */
|
||||
matches of the given parser, possibly using a given skip-parser. */
|
||||
template<
|
||||
#if BOOST_PARSER_USE_CONCEPTS
|
||||
std::ranges::viewable_range V,
|
||||
|
||||
@@ -24,7 +24,11 @@ namespace boost::parser {
|
||||
|
||||
#else
|
||||
|
||||
/** A view that produces UTF-8 from an given sequence of UTF. */
|
||||
/** A view that produces UTF-8 from an given sequence of UTF.
|
||||
|
||||
\tparam V Constrained by `std::ranges::view<V>`. Additionally, the
|
||||
value type of `V` must be `char`, `wchar_t`, `char8_t`, `char16_t`, or
|
||||
`char32_t`. */
|
||||
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS || defined(BOOST_PARSER_DOXYGEN)
|
||||
template<detail::text::utf_range V>
|
||||
requires std::ranges::view<V>
|
||||
@@ -44,7 +48,11 @@ namespace boost::parser {
|
||||
{}
|
||||
};
|
||||
|
||||
/** A view that produces UTF-16 from an given sequence of UTF. */
|
||||
/** A view that produces UTF-16 from an given sequence of UTF.
|
||||
|
||||
\tparam V Constrained by `std::ranges::view<V>`. Additionally, the
|
||||
value type of `V` must be `char`, `wchar_t`, `char8_t`, `char16_t`, or
|
||||
`char32_t`. */
|
||||
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS || defined(BOOST_PARSER_DOXYGEN)
|
||||
template<detail::text::utf_range V>
|
||||
requires std::ranges::view<V>
|
||||
@@ -64,7 +72,11 @@ namespace boost::parser {
|
||||
{}
|
||||
};
|
||||
|
||||
/** A view that produces UTF-32 from an given sequence of UTF. */
|
||||
/** A view that produces UTF-32 from an given sequence of UTF.
|
||||
|
||||
\tparam V Constrained by `std::ranges::view<V>`. Additionally, the
|
||||
value type of `V` must be `char`, `wchar_t`, `char8_t`, `char16_t`, or
|
||||
`char32_t`. */
|
||||
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS || defined(BOOST_PARSER_DOXYGEN)
|
||||
template<detail::text::utf_range V>
|
||||
requires std::ranges::view<V>
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
|
||||
#include <boost/parser/replace.hpp>
|
||||
|
||||
#if (!defined(_MSC_VER) || BOOST_PARSER_USE_CONCEPTS)
|
||||
#if (!defined(_MSC_VER) || BOOST_PARSER_USE_CONCEPTS) && \
|
||||
(!defined(__GNUC__) || 12 <= __GNUC__ || !BOOST_PARSER_USE_CONCEPTS)
|
||||
|
||||
|
||||
namespace boost::parser {
|
||||
@@ -16,10 +17,10 @@ namespace boost::parser {
|
||||
|
||||
template<typename I, typename S, typename Parser>
|
||||
using attr_type = decltype(std::declval<Parser const &>().call(
|
||||
std::bool_constant<false>{},
|
||||
std::declval<I &>(),
|
||||
std::declval<S>(),
|
||||
std::declval<parse_context<I, S, default_error_handler>>(),
|
||||
std::declval<
|
||||
parse_context<false, false, I, S, default_error_handler>>(),
|
||||
ws,
|
||||
detail::default_flags(),
|
||||
std::declval<bool &>()));
|
||||
@@ -315,7 +316,13 @@ namespace boost::parser {
|
||||
/** Produces a range of subranges of a given range `base`. Each subrange
|
||||
is either a subrange of `base` that does not match the given parser
|
||||
`parser`, or is `f(*boost::parser::parse(match, parser))`, where `f`
|
||||
is the given invocable and `match` is the matching subrange. */
|
||||
is the given invocable and `match` is the matching subrange.
|
||||
|
||||
In addition to the template parameter constraints, `F` must be
|
||||
invocable with the attribute type of `Parser`; `V` and the range type
|
||||
produced by `F`, "`Rf`" must be ranges of `char`, or must have the
|
||||
same UTF format; and `V` and `Rf` must meet the same compatibility
|
||||
requirements as described in `std::ranges::join_view`. */
|
||||
template<
|
||||
#if BOOST_PARSER_USE_CONCEPTS
|
||||
std::ranges::viewable_range V,
|
||||
@@ -498,11 +505,11 @@ namespace boost::parser {
|
||||
using base_type::operator++;
|
||||
|
||||
private:
|
||||
detail::maybe_const<Const, transform_replace_view> * parent_;
|
||||
detail::maybe_const<Const, transform_replace_view> * parent_ = {};
|
||||
BOOST_PARSER_SUBRANGE<I, S> r_;
|
||||
BOOST_PARSER_SUBRANGE<I> curr_;
|
||||
I next_it_;
|
||||
bool in_match_;
|
||||
I next_it_ = {};
|
||||
bool in_match_ = {};
|
||||
};
|
||||
|
||||
template<bool Const>
|
||||
|
||||
@@ -4,7 +4,7 @@ include(CTest)
|
||||
|
||||
enable_testing()
|
||||
|
||||
add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} -j4 -C ${CMAKE_CFG_INTDIR})
|
||||
add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure -j4 -C ${CMAKE_CFG_INTDIR})
|
||||
|
||||
##################################################
|
||||
# Parser tests
|
||||
@@ -53,6 +53,8 @@ add_test_executable(replace)
|
||||
add_test_executable(transform_replace)
|
||||
add_test_executable(hl)
|
||||
add_test_executable(aggr_tuple_assignment)
|
||||
add_test_executable(parser_perm)
|
||||
add_test_executable(parser_attributes)
|
||||
add_test_executable(parser_lazy_params)
|
||||
add_test_executable(parser_if_switch)
|
||||
add_test_executable(parser_rule)
|
||||
@@ -67,3 +69,8 @@ add_test_executable(class_type)
|
||||
add_test_executable(case_fold_generated)
|
||||
add_test_executable(no_case)
|
||||
add_test_executable(merge_separate)
|
||||
add_test_executable(parse_coords_new)
|
||||
add_test_executable(parser_seq_permutations_1)
|
||||
add_test_executable(parser_seq_permutations_2)
|
||||
add_test_executable(parser_or_permutations_1)
|
||||
add_test_executable(parser_or_permutations_2)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -20,6 +20,11 @@ void compile_or_attribute()
|
||||
using attr_t = decltype(prefix_parse(first, last, parser));
|
||||
static_assert(
|
||||
std::is_same_v<attr_t, std::optional<std::optional<int>>>);
|
||||
static_assert(std::is_same_v<
|
||||
attribute_t<
|
||||
decltype(BOOST_PARSER_SUBRANGE(first, last)),
|
||||
decltype(parser)>,
|
||||
std::optional<int>>);
|
||||
}
|
||||
|
||||
// scalar | scalar
|
||||
@@ -27,18 +32,33 @@ void compile_or_attribute()
|
||||
constexpr auto parser = char_ | char_;
|
||||
using attr_t = decltype(prefix_parse(first, last, parser));
|
||||
static_assert(std::is_same_v<attr_t, std::optional<char>>);
|
||||
static_assert(std::is_same_v<
|
||||
attribute_t<
|
||||
decltype(BOOST_PARSER_SUBRANGE(first, last)),
|
||||
decltype(parser)>,
|
||||
char>);
|
||||
}
|
||||
{
|
||||
constexpr auto parser = char_ | char_ | eps;
|
||||
using attr_t = decltype(prefix_parse(first, last, parser));
|
||||
static_assert(
|
||||
std::is_same_v<attr_t, std::optional<std::optional<char>>>);
|
||||
static_assert(std::is_same_v<
|
||||
attribute_t<
|
||||
decltype(BOOST_PARSER_SUBRANGE(first, last)),
|
||||
decltype(parser)>,
|
||||
std::optional<char>>);
|
||||
}
|
||||
{
|
||||
constexpr auto parser = int_ | char_;
|
||||
using attr_t = decltype(prefix_parse(first, last, parser));
|
||||
static_assert(
|
||||
std::is_same_v<attr_t, std::optional<std::variant<int, char>>>);
|
||||
static_assert(std::is_same_v<
|
||||
attribute_t<
|
||||
decltype(BOOST_PARSER_SUBRANGE(first, last)),
|
||||
decltype(parser)>,
|
||||
std::variant<int, char>>);
|
||||
}
|
||||
{
|
||||
constexpr auto parser = int_ | char_ | eps;
|
||||
@@ -46,6 +66,11 @@ void compile_or_attribute()
|
||||
static_assert(std::is_same_v<
|
||||
attr_t,
|
||||
std::optional<std::optional<std::variant<int, char>>>>);
|
||||
static_assert(std::is_same_v<
|
||||
attribute_t<
|
||||
decltype(BOOST_PARSER_SUBRANGE(first, last)),
|
||||
decltype(parser)>,
|
||||
std::optional<std::variant<int, char>>>);
|
||||
}
|
||||
|
||||
// -scalar | -scalar
|
||||
@@ -54,12 +79,22 @@ void compile_or_attribute()
|
||||
using attr_t = decltype(prefix_parse(first, last, parser));
|
||||
static_assert(
|
||||
std::is_same_v<attr_t, std::optional<std::optional<char>>>);
|
||||
static_assert(std::is_same_v<
|
||||
attribute_t<
|
||||
decltype(BOOST_PARSER_SUBRANGE(first, last)),
|
||||
decltype(parser)>,
|
||||
std::optional<char>>);
|
||||
}
|
||||
{
|
||||
constexpr auto parser = -char_ | -char_ | eps;
|
||||
using attr_t = decltype(prefix_parse(first, last, parser));
|
||||
static_assert(
|
||||
std::is_same_v<attr_t, std::optional<std::optional<char>>>);
|
||||
static_assert(std::is_same_v<
|
||||
attribute_t<
|
||||
decltype(BOOST_PARSER_SUBRANGE(first, last)),
|
||||
decltype(parser)>,
|
||||
std::optional<char>>);
|
||||
}
|
||||
{
|
||||
constexpr auto parser = -int_ | -char_;
|
||||
@@ -69,6 +104,11 @@ void compile_or_attribute()
|
||||
attr_t,
|
||||
std::optional<
|
||||
std::variant<std::optional<int>, std::optional<char>>>>);
|
||||
static_assert(std::is_same_v<
|
||||
attribute_t<
|
||||
decltype(BOOST_PARSER_SUBRANGE(first, last)),
|
||||
decltype(parser)>,
|
||||
std::variant<std::optional<int>, std::optional<char>>>);
|
||||
}
|
||||
{
|
||||
constexpr auto parser = -int_ | -char_ | eps;
|
||||
@@ -78,6 +118,13 @@ void compile_or_attribute()
|
||||
attr_t,
|
||||
std::optional<std::optional<
|
||||
std::variant<std::optional<int>, std::optional<char>>>>>);
|
||||
static_assert(
|
||||
std::is_same_v<
|
||||
attribute_t<
|
||||
decltype(BOOST_PARSER_SUBRANGE(first, last)),
|
||||
decltype(parser)>,
|
||||
std::optional<
|
||||
std::variant<std::optional<int>, std::optional<char>>>>);
|
||||
}
|
||||
|
||||
// seq<T> | seq<T>
|
||||
@@ -85,18 +132,33 @@ void compile_or_attribute()
|
||||
constexpr auto parser = *char_ | *char_;
|
||||
using attr_t = decltype(prefix_parse(first, last, parser));
|
||||
static_assert(std::is_same_v<attr_t, std::optional<std::string>>);
|
||||
static_assert(std::is_same_v<
|
||||
attribute_t<
|
||||
decltype(BOOST_PARSER_SUBRANGE(first, last)),
|
||||
decltype(parser)>,
|
||||
std::string>);
|
||||
}
|
||||
{
|
||||
constexpr auto parser = *char_ | *char_ | eps;
|
||||
using attr_t = decltype(prefix_parse(first, last, parser));
|
||||
static_assert(
|
||||
std::is_same_v<attr_t, std::optional<std::optional<std::string>>>);
|
||||
static_assert(std::is_same_v<
|
||||
attribute_t<
|
||||
decltype(BOOST_PARSER_SUBRANGE(first, last)),
|
||||
decltype(parser)>,
|
||||
std::optional<std::string>>);
|
||||
}
|
||||
{
|
||||
constexpr auto parser = *string("str") | *string("str");
|
||||
using attr_t = decltype(prefix_parse(first, last, parser));
|
||||
static_assert(
|
||||
std::is_same_v<attr_t, std::optional<std::vector<std::string>>>);
|
||||
static_assert(std::is_same_v<
|
||||
attribute_t<
|
||||
decltype(BOOST_PARSER_SUBRANGE(first, last)),
|
||||
decltype(parser)>,
|
||||
std::vector<std::string>>);
|
||||
}
|
||||
{
|
||||
constexpr auto parser = *string("str") | *string("str") | eps;
|
||||
@@ -104,6 +166,11 @@ void compile_or_attribute()
|
||||
static_assert(std::is_same_v<
|
||||
attr_t,
|
||||
std::optional<std::optional<std::vector<std::string>>>>);
|
||||
static_assert(std::is_same_v<
|
||||
attribute_t<
|
||||
decltype(BOOST_PARSER_SUBRANGE(first, last)),
|
||||
decltype(parser)>,
|
||||
std::optional<std::vector<std::string>>>);
|
||||
}
|
||||
|
||||
// seq<T> | seq<U>
|
||||
|
||||
100
test/no_case.cpp
100
test/no_case.cpp
@@ -23,6 +23,9 @@ TEST(no_case, doc_example)
|
||||
auto const alpha_parser = bp::no_case[bp::char_('a', 'z')];
|
||||
assert(bp::parse("a" | bp::as_utf32, bp::no_case[alpha_parser])); // Match!
|
||||
assert(bp::parse("B" | bp::as_utf32, bp::no_case[alpha_parser])); // Match!
|
||||
|
||||
(void)street_parser;
|
||||
(void)alpha_parser;
|
||||
}
|
||||
|
||||
|
||||
@@ -165,6 +168,97 @@ TEST(no_case, match_any_within_string)
|
||||
}
|
||||
}
|
||||
|
||||
TEST(no_case, symbol_table)
|
||||
{
|
||||
// without mutation
|
||||
{
|
||||
symbols<int> const roman_numerals = {
|
||||
{"I", 1}, {"V", 5}, {"X", 10}, {"L", 50}, {"C", 100}};
|
||||
symbols<std::string> const named_strings = {
|
||||
{"I", "1"}, {"V", "5"}, {"X", "10"}, {"L", "50"}, {"C", "100"}};
|
||||
|
||||
{
|
||||
auto const result = parse("I", no_case[roman_numerals]);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, 1);
|
||||
}
|
||||
{
|
||||
auto const result = parse("i", no_case[roman_numerals]);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, 1);
|
||||
}
|
||||
{
|
||||
auto const result = parse("I", no_case[named_strings]);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, "1");
|
||||
}
|
||||
{
|
||||
auto const result = parse("i", no_case[named_strings]);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, "1");
|
||||
}
|
||||
|
||||
{
|
||||
auto const result = parse("L", no_case[roman_numerals]);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, 50);
|
||||
}
|
||||
{
|
||||
auto const result = parse("l", no_case[roman_numerals]);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, 50);
|
||||
}
|
||||
{
|
||||
auto const result = parse("L", no_case[named_strings]);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, "50");
|
||||
}
|
||||
{
|
||||
auto const result = parse("l", no_case[named_strings]);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, "50");
|
||||
}
|
||||
}
|
||||
// with mutation
|
||||
{
|
||||
symbols<int> roman_numerals;
|
||||
roman_numerals.insert_for_next_parse("I", 1)("V", 5)("X", 10);
|
||||
auto const add_numeral = [&roman_numerals](auto & context) {
|
||||
using namespace boost::parser::literals;
|
||||
char chars[2] = {get(_attr(context), 0_c), 0};
|
||||
roman_numerals.insert(context, chars, get(_attr(context), 1_c));
|
||||
};
|
||||
auto const numerals_parser = omit[roman_numerals] >>
|
||||
(char_ >> int_)[add_numeral] >>
|
||||
no_case[roman_numerals];
|
||||
|
||||
{
|
||||
auto const result = parse("VL50L", numerals_parser);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, 50);
|
||||
EXPECT_FALSE(parse("L", roman_numerals));
|
||||
}
|
||||
{
|
||||
auto const result = parse("VL50l", numerals_parser);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, 50);
|
||||
EXPECT_FALSE(parse("L", roman_numerals));
|
||||
}
|
||||
{
|
||||
auto const result = parse("VC100C", numerals_parser);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, 100);
|
||||
EXPECT_FALSE(parse("C", roman_numerals));
|
||||
}
|
||||
{
|
||||
auto const result = parse("Vc100C", numerals_parser);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, 100);
|
||||
EXPECT_FALSE(parse("C", roman_numerals));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constexpr auto capital_sharp_s = u8"ẞ"; // U+1E9E
|
||||
constexpr auto small_sharp_s = u8"ß"; // U+00DF
|
||||
constexpr auto double_s = u8"sS"; // U+0073 U+0073
|
||||
@@ -290,7 +384,7 @@ TEST(no_case, detail_no_case_iter)
|
||||
auto first =
|
||||
detail::no_case_iter(mixed_sharp_s1, detail::text::null_sentinel);
|
||||
while (first != detail::text::null_sentinel) {
|
||||
folded.push_back(*first);
|
||||
folded.push_back((char)*first);
|
||||
++first;
|
||||
}
|
||||
EXPECT_EQ(folded, "sss");
|
||||
@@ -302,7 +396,7 @@ TEST(no_case, detail_no_case_iter)
|
||||
auto first =
|
||||
detail::no_case_iter(mixed_sharp_s2, detail::text::null_sentinel);
|
||||
while (first != detail::text::null_sentinel) {
|
||||
folded.push_back(*first);
|
||||
folded.push_back((char)*first);
|
||||
++first;
|
||||
}
|
||||
EXPECT_EQ(folded, "sss");
|
||||
@@ -315,7 +409,7 @@ TEST(no_case, detail_no_case_iter)
|
||||
detail::no_case_iter(street, detail::text::null_sentinel);
|
||||
auto first = first_const;
|
||||
while (first != detail::text::null_sentinel) {
|
||||
folded.push_back(*first);
|
||||
folded.push_back((char)*first);
|
||||
++first;
|
||||
}
|
||||
EXPECT_EQ(folded, "strasse");
|
||||
|
||||
199
test/parse_coords_new.cpp
Normal file
199
test/parse_coords_new.cpp
Normal file
@@ -0,0 +1,199 @@
|
||||
/**
|
||||
* Copyright (C) 2024 Phil Endecott
|
||||
* Copyright (C) 2024 T. Zachary Laine
|
||||
*
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <boost/parser/config.hpp>
|
||||
|
||||
#if !BOOST_PARSER_USE_STD_TUPLE
|
||||
|
||||
#include <boost/parser/parser.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
|
||||
namespace bp = boost::parser;
|
||||
|
||||
using namespace boost::hana::literals;
|
||||
|
||||
namespace g2d {
|
||||
struct Vector
|
||||
{
|
||||
double x, y;
|
||||
};
|
||||
|
||||
std::ostream & operator<<(std::ostream & os, Vector vec)
|
||||
{
|
||||
os << "vec{" << vec.x << ", " << vec.y << "}";
|
||||
return os;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
bp::rule<struct uint_0_60, unsigned int> uint_0_60 = "uint_0_60";
|
||||
bp::rule<struct double_0_60, double> double_0_60 = "double_0_60";
|
||||
bp::rule<struct degrees_decimal_minutes, double> degrees_decimal_minutes = "degrees_decimal_minutes";
|
||||
bp::rule<struct degrees_minutes_seconds, double> degrees_minutes_seconds = "degrees_minutes_seconds";
|
||||
bp::rule<struct degrees, double> degrees = "degrees";
|
||||
bp::rule<struct latitude, double> latitude = "latitude";
|
||||
bp::rule<struct longitude, double> longitude = "longitude";
|
||||
bp::rule<struct signed_latitude, double> signed_latitude = "signed_latitude";
|
||||
bp::rule<struct signed_longitude, double> signed_longitude = "signed_longitude";
|
||||
bp::rule<struct latlon, g2d::Vector> latlon = "latlon";
|
||||
|
||||
|
||||
const auto degrees_symbol = bp::no_case[ bp::lit("degrees") | bp::lit("deg") | bp::lit('d') ];
|
||||
const auto minutes_symbol = bp::no_case[ bp::lit('\'') | bp::lit("minutes") | bp::lit("min") | bp::lit('m') ];
|
||||
const auto seconds_symbol = bp::no_case[ bp::lit('"') | bp::lit("seconds") | bp::lit("sec") | bp::lit('s') ];
|
||||
|
||||
const auto uint_0_60_def = bp::uint_ [( [](auto & ctx) { _pass(ctx) = _attr(ctx) < 60U; _val(ctx) = _attr(ctx); } )];
|
||||
const auto double_0_60_def = bp::double_ [( [](auto & ctx) { _pass(ctx) = _attr(ctx) < 60; _val(ctx) = _attr(ctx); } )];
|
||||
|
||||
const auto decimal_degrees = bp::double_ >> -degrees_symbol;
|
||||
|
||||
const auto degrees_decimal_minutes_def = (bp::uint_ >> -degrees_symbol
|
||||
>> (double_0_60 - '.') >> -minutes_symbol) [( [](auto & ctx) {
|
||||
auto d = _attr(ctx)[0_c];
|
||||
auto m = _attr(ctx)[1_c];
|
||||
_val(ctx) = d + m/60.0;
|
||||
} )];
|
||||
|
||||
const auto degrees_minutes_seconds_def = (bp::uint_ >> -degrees_symbol
|
||||
>> uint_0_60 >> -minutes_symbol
|
||||
>> (double_0_60 - '.') >> -seconds_symbol) [( [](auto & ctx) {
|
||||
auto d = _attr(ctx)[0_c];
|
||||
auto m = _attr(ctx)[1_c];
|
||||
auto s = _attr(ctx)[2_c];
|
||||
_val(ctx) = d + m/60.0 + s/3600.0;
|
||||
} )];
|
||||
|
||||
const auto degrees_def = degrees_minutes_seconds
|
||||
| degrees_decimal_minutes
|
||||
| decimal_degrees;
|
||||
|
||||
const auto northsouth = bp::no_case[ bp::char_("ns") ];
|
||||
const auto eastwest = bp::no_case[ bp::char_("ew") ];
|
||||
|
||||
const auto latitude_def = (degrees >> northsouth) [( [](auto & ctx) {
|
||||
auto d = _attr(ctx)[0_c];
|
||||
auto ns = _attr(ctx)[1_c];
|
||||
_pass(ctx) = d <= 90;
|
||||
_val(ctx) = ns=='S' || ns=='s' ? -d : d;
|
||||
} )];
|
||||
|
||||
const auto longitude_def = (degrees >> eastwest) [( [](auto & ctx) {
|
||||
auto d = _attr(ctx)[0_c];
|
||||
auto ew = _attr(ctx)[1_c];
|
||||
_pass(ctx) = d <= 180;
|
||||
_val(ctx) = ew=='W' || ew=='w' ? -d : d;
|
||||
} )];
|
||||
|
||||
const auto signed_degrees = bp::double_ >> -degrees_symbol;
|
||||
|
||||
const auto signed_latitude_def = signed_degrees [( [](auto & ctx) { auto d = _attr(ctx); _pass(ctx) = -90 <= d && d <= 90; _val(ctx) = _attr(ctx); } )];
|
||||
const auto signed_longitude_def = signed_degrees [( [](auto & ctx) { auto d = _attr(ctx); _pass(ctx) = -180 <= d && d <= 180; _val(ctx) = _attr(ctx); } )];
|
||||
|
||||
const auto latlon_def = ((latitude >> longitude) [( [](auto & ctx) { _val(ctx) = g2d::Vector{_attr(ctx)[1_c], _attr(ctx)[0_c]}; } )] )
|
||||
| ((longitude >> latitude) [( [](auto & ctx) { _val(ctx) = g2d::Vector{_attr(ctx)[0_c], _attr(ctx)[1_c]}; } )] )
|
||||
| ((signed_longitude >> signed_latitude)
|
||||
[( [](auto & ctx) { _val(ctx) = g2d::Vector{_attr(ctx)[0_c], _attr(ctx)[1_c]}; } )] );
|
||||
|
||||
BOOST_PARSER_DEFINE_RULES(uint_0_60);
|
||||
BOOST_PARSER_DEFINE_RULES(double_0_60);
|
||||
BOOST_PARSER_DEFINE_RULES(degrees_decimal_minutes);
|
||||
BOOST_PARSER_DEFINE_RULES(degrees_minutes_seconds);
|
||||
BOOST_PARSER_DEFINE_RULES(degrees);
|
||||
BOOST_PARSER_DEFINE_RULES(latitude);
|
||||
BOOST_PARSER_DEFINE_RULES(longitude);
|
||||
BOOST_PARSER_DEFINE_RULES(signed_latitude);
|
||||
BOOST_PARSER_DEFINE_RULES(signed_longitude);
|
||||
BOOST_PARSER_DEFINE_RULES(latlon);
|
||||
|
||||
|
||||
#if 0
|
||||
static std::optional<g2d::Vector> try_parse_latlon(std::string_view s)
|
||||
{
|
||||
auto input = latlon >> bp::eoi;
|
||||
return parse(s,input, bp::ws|bp::lit(','));
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
{
|
||||
assert(argc==2);
|
||||
|
||||
auto opt_coords = try_parse_latlon(argv[1]);
|
||||
|
||||
if (!opt_coords) {
|
||||
std::cerr << "parse error\n";
|
||||
return 1;
|
||||
} else {
|
||||
std::cout << opt_coords->x << " " << opt_coords->y << "\n";
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(parse_coords, all_examples)
|
||||
{
|
||||
std::vector<std::string> test_coords = {
|
||||
"12.34 N, 56.78 E",
|
||||
"56.78,-12.34",
|
||||
"12d 30' n 45d 15' 7\" w",
|
||||
"12 30 45 N, 45 15 7 W",
|
||||
"12d 30m 15s S 50d 59m 59s W",
|
||||
"50d 0.5m n 50d 59m 59s W"};
|
||||
|
||||
{
|
||||
auto result = bp::parse(test_coords[0], latlon, bp::ws | bp::lit(','));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_LT(std::abs(result->x - 56.78), 0.001);
|
||||
EXPECT_LT(std::abs(result->y - 12.34), 0.001);
|
||||
}
|
||||
|
||||
{
|
||||
auto result = bp::parse(test_coords[1], latlon, bp::ws | bp::lit(','));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_LT(std::abs(result->x - 56.78), 0.001);
|
||||
EXPECT_LT(std::abs(result->y - -12.34), 0.001);
|
||||
}
|
||||
|
||||
{
|
||||
auto result = bp::parse(test_coords[2], latlon, bp::ws | bp::lit(','));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_LT(std::abs(result->x - -45.2519), 0.001);
|
||||
EXPECT_LT(std::abs(result->y - 12.5), 0.001);
|
||||
}
|
||||
|
||||
{
|
||||
auto result = bp::parse(test_coords[3], latlon, bp::ws | bp::lit(','));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_LT(std::abs(result->x - -45.2519), 0.001);
|
||||
EXPECT_LT(std::abs(result->y - 12.5125), 0.001);
|
||||
}
|
||||
|
||||
{
|
||||
auto result = bp::parse(test_coords[4], latlon, bp::ws | bp::lit(','));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_LT(std::abs(result->x - -50.9997), 0.001);
|
||||
EXPECT_LT(std::abs(result->y - -12.5042), 0.001);
|
||||
}
|
||||
|
||||
{
|
||||
auto result = bp::parse(test_coords[5], latlon, bp::ws | bp::lit(','));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_LT(std::abs(result->x - -50.9997), 0.001);
|
||||
EXPECT_LT(std::abs(result->y - 50.0083), 0.001);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
274
test/parser.cpp
274
test/parser.cpp
@@ -627,61 +627,63 @@ TEST(parser, star_and_plus_collapsing)
|
||||
}
|
||||
}
|
||||
|
||||
TEST(parser, action)
|
||||
{
|
||||
{{std::string str = "";
|
||||
std::stringstream ss;
|
||||
auto action = [&ss](auto & context) { ss << _attr(context); };
|
||||
auto parser = *char_('b')[action];
|
||||
EXPECT_TRUE(parse(str, parser));
|
||||
EXPECT_EQ(ss.str(), "");
|
||||
}
|
||||
{
|
||||
std::string str = "b";
|
||||
std::stringstream ss;
|
||||
auto action = [&ss](auto & context) { ss << _attr(context); };
|
||||
auto parser = *char_('b')[action];
|
||||
EXPECT_TRUE(parse(str, parser));
|
||||
EXPECT_EQ(ss.str(), "b");
|
||||
}
|
||||
{
|
||||
std::string str = "bb";
|
||||
std::stringstream ss;
|
||||
auto action = [&ss](auto & context) { ss << _attr(context); };
|
||||
auto parser = *char_('b')[action];
|
||||
EXPECT_TRUE(parse(str, parser));
|
||||
EXPECT_TRUE(parse(str, parser));
|
||||
EXPECT_EQ(ss.str(), "bbbb");
|
||||
}
|
||||
}
|
||||
|
||||
TEST(parser, action_)
|
||||
{
|
||||
{
|
||||
std::string str = "";
|
||||
std::stringstream ss;
|
||||
auto action = [&ss](auto & context) { ss << _attr(context); };
|
||||
auto parser = +char_('b')[action];
|
||||
EXPECT_FALSE(parse(str, parser));
|
||||
EXPECT_EQ(ss.str(), "");
|
||||
{
|
||||
std::stringstream ss;
|
||||
auto action = [&ss](auto & ctx) { ss << _attr(ctx); };
|
||||
auto parser = *char_('b')[action];
|
||||
EXPECT_TRUE(parse(str, parser));
|
||||
EXPECT_EQ(ss.str(), "");
|
||||
}
|
||||
{
|
||||
str = "b";
|
||||
std::stringstream ss;
|
||||
auto action = [&ss](auto & ctx) { ss << _attr(ctx); };
|
||||
auto parser = *char_('b')[action];
|
||||
EXPECT_TRUE(parse(str, parser));
|
||||
EXPECT_EQ(ss.str(), "b");
|
||||
}
|
||||
{
|
||||
str = "bb";
|
||||
std::stringstream ss;
|
||||
auto action = [&ss](auto & ctx) { ss << _attr(ctx); };
|
||||
auto parser = *char_('b')[action];
|
||||
EXPECT_TRUE(parse(str, parser));
|
||||
EXPECT_TRUE(parse(str, parser));
|
||||
EXPECT_EQ(ss.str(), "bbbb");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::string str = "b";
|
||||
std::stringstream ss;
|
||||
auto action = [&ss](auto & context) { ss << _attr(context); };
|
||||
auto parser = +char_('b')[action];
|
||||
EXPECT_TRUE(parse(str, parser));
|
||||
EXPECT_EQ(ss.str(), "b");
|
||||
{
|
||||
std::string str = "";
|
||||
std::stringstream ss;
|
||||
auto action = [&ss](auto & ctx) { ss << _attr(ctx); };
|
||||
auto parser = +char_('b')[action];
|
||||
EXPECT_FALSE(parse(str, parser));
|
||||
EXPECT_EQ(ss.str(), "");
|
||||
}
|
||||
{
|
||||
std::string str = "b";
|
||||
std::stringstream ss;
|
||||
auto action = [&ss](auto & ctx) { ss << _attr(ctx); };
|
||||
auto parser = +char_('b')[action];
|
||||
EXPECT_TRUE(parse(str, parser));
|
||||
EXPECT_EQ(ss.str(), "b");
|
||||
}
|
||||
{
|
||||
std::string str = "bb";
|
||||
std::stringstream ss;
|
||||
auto action = [&ss](auto & ctx) { ss << _attr(ctx); };
|
||||
auto parser = +char_('b')[action];
|
||||
EXPECT_TRUE(parse(str, parser));
|
||||
EXPECT_TRUE(parse(str, parser));
|
||||
EXPECT_EQ(ss.str(), "bbbb");
|
||||
}
|
||||
}
|
||||
{
|
||||
std::string str = "bb";
|
||||
std::stringstream ss;
|
||||
auto action = [&ss](auto & context) { ss << _attr(context); };
|
||||
auto parser = +char_('b')[action];
|
||||
EXPECT_TRUE(parse(str, parser));
|
||||
EXPECT_TRUE(parse(str, parser));
|
||||
EXPECT_EQ(ss.str(), "bbbb");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(parser, star_as_string_or_vector)
|
||||
@@ -849,6 +851,106 @@ TEST(parser, star_as_string_or_vector)
|
||||
}
|
||||
}
|
||||
|
||||
TEST(parser, transform)
|
||||
{
|
||||
int calls = 0;
|
||||
auto by_value_str_sum = [&](std::string s) {
|
||||
++calls;
|
||||
std::transform(
|
||||
s.begin(), s.end(), s.begin(), [](auto ch) { return ch - '0'; });
|
||||
return std::accumulate(s.begin(), s.end(), 0);
|
||||
};
|
||||
auto cref_str_sum = [&](std::string const & s) {
|
||||
++calls;
|
||||
int retval = 0;
|
||||
for (auto ch : s) {
|
||||
retval += ch - '0';
|
||||
}
|
||||
return retval;
|
||||
};
|
||||
auto rv_ref_str_sum = [&](std::string && s) {
|
||||
++calls;
|
||||
std::transform(
|
||||
s.begin(), s.end(), s.begin(), [](auto ch) { return ch - '0'; });
|
||||
return std::accumulate(s.begin(), s.end(), 0);
|
||||
};
|
||||
{
|
||||
constexpr auto parser = +char_;
|
||||
std::string str = "012345";
|
||||
{
|
||||
auto result = parse(str, parser);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, "012345");
|
||||
}
|
||||
{
|
||||
calls = 0;
|
||||
auto result = parse(str, transform(by_value_str_sum)[parser]);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, 15);
|
||||
EXPECT_EQ(calls, 1);
|
||||
}
|
||||
{
|
||||
calls = 0;
|
||||
auto result = parse(str, transform(cref_str_sum)[parser]);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, 15);
|
||||
EXPECT_EQ(calls, 1);
|
||||
}
|
||||
{
|
||||
calls = 0;
|
||||
auto result = parse(str, transform(rv_ref_str_sum)[parser]);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, 15);
|
||||
EXPECT_EQ(calls, 1);
|
||||
}
|
||||
}
|
||||
{
|
||||
constexpr auto parser = +char_;
|
||||
std::string str = "012345";
|
||||
{
|
||||
calls = 0;
|
||||
auto result = parse(str, omit[transform(by_value_str_sum)[parser]]);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(calls, 0);
|
||||
}
|
||||
{
|
||||
calls = 0;
|
||||
auto result = parse(str, omit[transform(cref_str_sum)[parser]]);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(calls, 0);
|
||||
}
|
||||
{
|
||||
calls = 0;
|
||||
auto result = parse(str, omit[transform(rv_ref_str_sum)[parser]]);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(calls, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(parser, transform_doc_example)
|
||||
{
|
||||
//[ transform_directive_example
|
||||
auto str_sum = [&](std::string const & s) {
|
||||
int retval = 0;
|
||||
for (auto ch : s) {
|
||||
retval += ch - '0';
|
||||
}
|
||||
return retval;
|
||||
};
|
||||
|
||||
namespace bp = boost::parser;
|
||||
constexpr auto parser = +bp::char_;
|
||||
std::string str = "012345";
|
||||
|
||||
auto result = bp::parse(str, bp::transform(str_sum)[parser]);
|
||||
assert(result);
|
||||
assert(*result == 15);
|
||||
static_assert(std::is_same_v<decltype(result), std::optional<int>>);
|
||||
//]
|
||||
(void)result;
|
||||
}
|
||||
|
||||
TEST(parser, omit)
|
||||
{
|
||||
{
|
||||
@@ -2042,11 +2144,7 @@ TEST(parser, combined_seq_and_or)
|
||||
|
||||
{
|
||||
std::string str = "abc";
|
||||
tuple<
|
||||
boost::parser::detail::any_copyable,
|
||||
boost::parser::detail::any_copyable,
|
||||
boost::parser::detail::any_copyable>
|
||||
chars;
|
||||
tuple<std::any, std::any, std::any> chars;
|
||||
EXPECT_TRUE(parse(str, parser, chars));
|
||||
}
|
||||
|
||||
@@ -2167,6 +2265,13 @@ TEST(parser, combined_seq_and_or)
|
||||
EXPECT_EQ(chars, "xyz");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
constexpr auto parser = int_ >> -(lit('a') | 'b');
|
||||
auto result = parse("34b", parser);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, 34);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(parser, eol_)
|
||||
@@ -2849,3 +2954,64 @@ TEST(parser, rule_example)
|
||||
using namespace rule_construction_example;
|
||||
EXPECT_TRUE(bp::parse("3 4", type, bp::ws));
|
||||
}
|
||||
|
||||
TEST(parser, utf_iterator_copy_ctor)
|
||||
{
|
||||
namespace bp = boost::parser;
|
||||
|
||||
char mut_chars[] = "foo";
|
||||
char const const_chars[] = "bar";
|
||||
|
||||
auto mut_view = mut_chars | bp::as_utf8;
|
||||
auto const_view = const_chars | bp::as_utf8;
|
||||
|
||||
auto mut_it = mut_view.begin();
|
||||
auto mut_last = mut_view.end();
|
||||
auto const_it = const_view.begin();
|
||||
auto const_last = const_view.begin();
|
||||
|
||||
const_it = mut_it;
|
||||
const_last = mut_last;
|
||||
|
||||
std::string copy;
|
||||
copy.resize(3);
|
||||
std::copy(const_it, const_last, copy.begin());
|
||||
EXPECT_EQ(copy, "foo");
|
||||
}
|
||||
|
||||
namespace github_issue_125_ {
|
||||
namespace bp = boost::parser;
|
||||
constexpr bp::
|
||||
rule<struct replacement_field_rule, std::optional<unsigned short>>
|
||||
replacement_field = "replacement_field";
|
||||
constexpr auto replacement_field_def = bp::lit('{') >> -bp::ushort_;
|
||||
BOOST_PARSER_DEFINE_RULES(replacement_field);
|
||||
}
|
||||
|
||||
TEST(parser, github_issue_125)
|
||||
{
|
||||
namespace bp = boost::parser;
|
||||
using namespace github_issue_125_;
|
||||
|
||||
unsigned short integer_found = 99;
|
||||
auto print_repl_field = [&](auto & ctx) {
|
||||
const std::optional<unsigned short> & val = bp::_attr(ctx);
|
||||
if (val)
|
||||
integer_found = *val;
|
||||
else
|
||||
integer_found = 77;
|
||||
};
|
||||
|
||||
{
|
||||
integer_found = 99;
|
||||
auto result = bp::parse("{9", replacement_field[print_repl_field]);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(integer_found, 9);
|
||||
}
|
||||
{
|
||||
integer_found = 99;
|
||||
auto result = bp::parse("{", replacement_field[print_repl_field]);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(integer_found, 77);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,31 +14,29 @@ constexpr rule<struct abc_def_tag, std::string> abc_def = "abc or def";
|
||||
constexpr auto abc_def_def = string("abc") | string("def");
|
||||
BOOST_PARSER_DEFINE_RULES(abc_def);
|
||||
|
||||
auto const fail = [](auto & context) { _pass(context) = false; };
|
||||
auto const fail = [](auto & ctx) { _pass(ctx) = false; };
|
||||
constexpr rule<struct fail_abc_pass_def_tag, std::string> fail_abc_pass_def =
|
||||
"abc";
|
||||
constexpr auto fail_abc_pass_def_def = string("abc")[fail] | string("def");
|
||||
BOOST_PARSER_DEFINE_RULES(fail_abc_pass_def);
|
||||
|
||||
auto const attr_to_val = [](auto & context) { _val(context) = _attr(context); };
|
||||
auto const attr_to_val = [](auto & ctx) { _val(ctx) = _attr(ctx); };
|
||||
constexpr rule<struct action_copy_abc_def_tag, std::string>
|
||||
action_copy_abc_def = "abc or def";
|
||||
constexpr auto action_copy_abc_def_def =
|
||||
string("abc")[attr_to_val] | string("def")[attr_to_val];
|
||||
BOOST_PARSER_DEFINE_RULES(action_copy_abc_def);
|
||||
|
||||
auto const abc_value = [](auto & context) { _val(context) = "abc"; };
|
||||
auto const def_value = [](auto & context) { _val(context) = "def"; };
|
||||
auto const abc_value = [](auto & ctx) { _val(ctx) = "abc"; };
|
||||
auto const def_value = [](auto & ctx) { _val(ctx) = "def"; };
|
||||
constexpr rule<struct rev_abc_def_tag, std::string> rev_abc_def = "abc or def";
|
||||
constexpr auto rev_abc_def_def =
|
||||
string("abc")[def_value] | string("def")[abc_value];
|
||||
BOOST_PARSER_DEFINE_RULES(rev_abc_def);
|
||||
|
||||
auto const append_attr = [](auto & context) {
|
||||
_locals(context) += _attr(context);
|
||||
};
|
||||
auto const locals_to_val = [](auto & context) {
|
||||
_val(context) = std::move(_locals(context));
|
||||
auto const append_attr = [](auto & ctx) { _locals(ctx) += _attr(ctx); };
|
||||
auto const locals_to_val = [](auto & ctx) {
|
||||
_val(ctx) = std::move(_locals(ctx));
|
||||
};
|
||||
rule<struct locals_abc_def_tag, std::string, std::string> const locals_abc_def =
|
||||
"abc or def";
|
||||
@@ -46,10 +44,10 @@ auto locals_abc_def_def = -string("abc")[append_attr] >>
|
||||
-string("def")[append_attr] >> eps[locals_to_val];
|
||||
BOOST_PARSER_DEFINE_RULES(locals_abc_def);
|
||||
|
||||
TEST(parser, side_effects)
|
||||
TEST(action, side_effects)
|
||||
{
|
||||
int i = 0;
|
||||
auto increment_i = [&i](auto & context) { ++i; };
|
||||
auto increment_i = [&i](auto & ctx) { ++i; };
|
||||
|
||||
using no_attribute_return = decltype(parse("xyz", char_('a')[increment_i]));
|
||||
static_assert(std::is_same_v<no_attribute_return, bool>);
|
||||
@@ -75,7 +73,7 @@ TEST(parser, side_effects)
|
||||
}
|
||||
}
|
||||
|
||||
TEST(parser, pass)
|
||||
TEST(action, pass)
|
||||
{
|
||||
{
|
||||
std::string const str = "xyz";
|
||||
@@ -107,7 +105,7 @@ TEST(parser, pass)
|
||||
}
|
||||
}
|
||||
|
||||
TEST(parser, val_attr)
|
||||
TEST(action, val_attr)
|
||||
{
|
||||
{
|
||||
std::string const str = "abc";
|
||||
@@ -135,7 +133,7 @@ TEST(parser, val_attr)
|
||||
}
|
||||
}
|
||||
|
||||
TEST(parser, locals)
|
||||
TEST(action, locals)
|
||||
{
|
||||
{
|
||||
std::string const str = "";
|
||||
@@ -162,3 +160,60 @@ TEST(parser, locals)
|
||||
EXPECT_EQ(*result, "def");
|
||||
}
|
||||
}
|
||||
|
||||
auto drop_result = [](auto & ctx) {};
|
||||
auto auto_assign = [](auto & ctx) { return _attr(ctx); };
|
||||
auto auto_assign_multi_string_1 = [](std::string const & str1,
|
||||
std::string const & str2) {
|
||||
return str1 + str2;
|
||||
};
|
||||
auto auto_assign_multi_string_2 = [](std::string && str1, std::string && str2) {
|
||||
return str1 + str2;
|
||||
};
|
||||
|
||||
constexpr rule<struct str_rule_1_tag, std::string> str_rule_1 = "str_rule_1";
|
||||
constexpr auto str_rule_1_def = +char_;
|
||||
BOOST_PARSER_DEFINE_RULES(str_rule_1);
|
||||
|
||||
constexpr rule<struct str_rule_2_tag, std::string> str_rule_2 = "str_rule_2";
|
||||
constexpr auto str_rule_2_def = (+char_)[drop_result];
|
||||
BOOST_PARSER_DEFINE_RULES(str_rule_2);
|
||||
|
||||
constexpr rule<struct str_rule_3_tag, std::string> str_rule_3 = "str_rule_3";
|
||||
constexpr auto str_rule_3_def = (+char_)[auto_assign];
|
||||
BOOST_PARSER_DEFINE_RULES(str_rule_3);
|
||||
|
||||
constexpr rule<struct str_rule_6_tag, std::string> str_rule_6 = "str_rule_6";
|
||||
constexpr auto str_rule_6_def =
|
||||
(+(char_ - ' ') >> ' ' >> +char_)[auto_assign_multi_string_1];
|
||||
BOOST_PARSER_DEFINE_RULES(str_rule_6);
|
||||
|
||||
constexpr rule<struct str_rule_7_tag, std::string> str_rule_7 = "str_rule_7";
|
||||
constexpr auto str_rule_7_def =
|
||||
(+(char_ - ' ') >> ' ' >> +char_)[auto_assign_multi_string_2];
|
||||
BOOST_PARSER_DEFINE_RULES(str_rule_7);
|
||||
|
||||
TEST(action, alternate_invocables)
|
||||
{
|
||||
{
|
||||
auto result_1 = parse("some text", str_rule_1);
|
||||
EXPECT_TRUE(result_1);
|
||||
EXPECT_EQ(*result_1, "some text");
|
||||
|
||||
auto result_2 = parse("some text", str_rule_2);
|
||||
EXPECT_TRUE(result_2);
|
||||
EXPECT_EQ(*result_2, "");
|
||||
|
||||
auto result_3 = parse("some text", str_rule_3);
|
||||
EXPECT_TRUE(result_3);
|
||||
EXPECT_EQ(*result_3, "some text");
|
||||
|
||||
auto result_6 = parse("some text", str_rule_6);
|
||||
EXPECT_TRUE(result_6);
|
||||
EXPECT_EQ(*result_6, "sometext");
|
||||
|
||||
auto result_7 = parse("some text", str_rule_7);
|
||||
EXPECT_TRUE(result_7);
|
||||
EXPECT_EQ(*result_7, "sometext");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ auto make_13 = [](auto & context) { return 13; };
|
||||
|
||||
auto const first_param_to_val = [](auto & context) {
|
||||
using namespace boost::parser::literals;
|
||||
_val(context) = get(_params(context), 0_c);
|
||||
_val(context) = (int)get(_params(context), 0_c);
|
||||
};
|
||||
auto const second_param_to_val = [](auto & context) {
|
||||
using namespace boost::parser::literals;
|
||||
|
||||
@@ -115,11 +115,12 @@ TEST(parser, full_parse_api)
|
||||
EXPECT_EQ(out, 'a');
|
||||
out = 0;
|
||||
first = str.c_str();
|
||||
auto ws_copy = ws;
|
||||
EXPECT_FALSE(prefix_parse(
|
||||
first,
|
||||
boost::parser::detail::text::null_sentinel,
|
||||
char_('b'),
|
||||
ws,
|
||||
ws_copy,
|
||||
out));
|
||||
EXPECT_EQ(out, 0);
|
||||
}
|
||||
@@ -138,7 +139,8 @@ TEST(parser, full_parse_api)
|
||||
EXPECT_TRUE(parse(str.c_str(), char_, ws, out));
|
||||
EXPECT_EQ(out, 'a');
|
||||
out = 0;
|
||||
EXPECT_FALSE(parse(str.c_str(), char_('b'), ws, out));
|
||||
auto ws_copy = ws;
|
||||
EXPECT_FALSE(parse(str.c_str(), char_('b'), ws_copy, out));
|
||||
EXPECT_EQ(out, 0);
|
||||
}
|
||||
|
||||
@@ -156,11 +158,12 @@ TEST(parser, full_parse_api)
|
||||
ws),
|
||||
'a');
|
||||
first = str.c_str();
|
||||
auto ws_copy = ws;
|
||||
EXPECT_TRUE(!prefix_parse(
|
||||
first,
|
||||
boost::parser::detail::text::null_sentinel,
|
||||
char_('b'),
|
||||
ws));
|
||||
ws_copy));
|
||||
}
|
||||
// returned attr, using skipper, range
|
||||
{
|
||||
@@ -172,7 +175,8 @@ TEST(parser, full_parse_api)
|
||||
{
|
||||
EXPECT_TRUE(parse(str.c_str(), char_, ws));
|
||||
EXPECT_EQ(*parse(str.c_str(), char_, ws), 'a');
|
||||
EXPECT_FALSE(parse(str.c_str(), char_('b'), ws));
|
||||
auto ws_copy = ws;
|
||||
EXPECT_FALSE(parse(str.c_str(), char_('b'), ws_copy));
|
||||
}
|
||||
|
||||
// callback, iter/sent
|
||||
@@ -217,6 +221,20 @@ TEST(parser, full_parse_api)
|
||||
first = str.c_str();
|
||||
EXPECT_EQ(out, 'a');
|
||||
}
|
||||
{
|
||||
char out = 0;
|
||||
auto callbacks = [&out](auto tag, auto x) { out = x; };
|
||||
auto first = str.c_str();
|
||||
auto ws_copy = ws;
|
||||
EXPECT_TRUE(callback_prefix_parse(
|
||||
first,
|
||||
boost::parser::detail::text::null_sentinel,
|
||||
callback_char_rule,
|
||||
ws_copy,
|
||||
callbacks));
|
||||
first = str.c_str();
|
||||
EXPECT_EQ(out, 'a');
|
||||
}
|
||||
// callback, using skipper, range
|
||||
{
|
||||
char out = 0;
|
||||
@@ -229,8 +247,9 @@ TEST(parser, full_parse_api)
|
||||
{
|
||||
char out = 0;
|
||||
auto callbacks = [&out](auto tag, auto x) { out = x; };
|
||||
auto ws_copy = ws;
|
||||
EXPECT_TRUE(callback_parse(
|
||||
str.c_str(), callback_char_rule, ws, callbacks));
|
||||
str.c_str(), callback_char_rule, ws_copy, callbacks));
|
||||
EXPECT_EQ(out, 'a');
|
||||
}
|
||||
}
|
||||
@@ -1797,11 +1816,7 @@ TEST(parser, combined_seq_and_or)
|
||||
|
||||
{
|
||||
char const * str = "abc";
|
||||
tuple<
|
||||
boost::parser::detail::any_copyable,
|
||||
boost::parser::detail::any_copyable,
|
||||
boost::parser::detail::any_copyable>
|
||||
chars;
|
||||
tuple<std::any, std::any, std::any> chars;
|
||||
EXPECT_TRUE(parse(str, parser, chars));
|
||||
}
|
||||
|
||||
@@ -1979,6 +1994,7 @@ TEST(parser, attr_out_param_compat)
|
||||
using namespace bp::literals;
|
||||
|
||||
assert(success);
|
||||
(void)success;
|
||||
assert(bp::get(result, 0_c) == std::vector<int>({'r', U'ô', 'l', 'e'}));
|
||||
assert(bp::get(result, 1_c) == "foo");
|
||||
}
|
||||
@@ -1996,6 +2012,7 @@ TEST(parser, attr_out_param_compat)
|
||||
using namespace bp::literals;
|
||||
|
||||
assert(success);
|
||||
(void)success;
|
||||
// The 4 code points "rôle" get transcoded to 5 UTF-8 code points to fit in the std::string.
|
||||
assert(bp::get(result, 0_c) == std::vector<char>({'r', (char)0xc3, (char)0xb4, 'l', 'e'}));
|
||||
assert(bp::get(result, 1_c) == "foo");
|
||||
|
||||
67
test/parser_attributes.cpp
Normal file
67
test/parser_attributes.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* Copyright (C) 2024 T. Zachary Laine
|
||||
*
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <boost/parser/parser.hpp>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
|
||||
namespace bp = boost::parser;
|
||||
|
||||
bp::rule<struct seq1_tag, bp::tuple<int, char>> seq1 = "";
|
||||
bp::rule<struct seq2_tag, bp::tuple<int, char>> seq2 = "";
|
||||
auto const seq1_def = bp::int_ >> bp::char_('a');
|
||||
auto const seq2_def = bp::int_ >> bp::char_('b');
|
||||
BOOST_PARSER_DEFINE_RULES(seq1, seq2);
|
||||
|
||||
TEST(attributes, internal_errors_munging_attributes)
|
||||
{
|
||||
// These are just some assorted cases that have, or seemed likely to,
|
||||
// cause problems when an internal failure in an alternative wipes out an
|
||||
// existing result. This covers all the cases where "if (!success)"
|
||||
// causes the attribute to be overwritten with a default-constructed
|
||||
// value.
|
||||
|
||||
{
|
||||
auto const parser =
|
||||
bp::string("FOO") >> -(bp::string("bar") | bp::string("foo"));
|
||||
|
||||
auto result = bp::parse("FOOfoo", parser);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(bp::get(*result, bp::llong<0>{}), std::string("FOO"));
|
||||
EXPECT_EQ(bp::get(*result, bp::llong<1>{}), std::string("foo"));
|
||||
}
|
||||
|
||||
{
|
||||
auto const parser = bp::merge
|
||||
[bp::string("FOO") >> (bp::string("bar") | bp::string("foo"))];
|
||||
|
||||
auto result = bp::parse("FOOfoo", parser);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, std::string("FOOfoo"));
|
||||
}
|
||||
|
||||
{
|
||||
auto const parser = bp::merge
|
||||
[(bp::attr(std::vector<std::string>({"FOO"})) | bp::eps) >>
|
||||
(bp::repeat(1)[bp::string("foo")] | bp::eps)];
|
||||
|
||||
auto result = bp::parse("", parser);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_TRUE(*result);
|
||||
EXPECT_EQ(*result, std::vector<std::string>({"FOO"}));
|
||||
}
|
||||
|
||||
{
|
||||
auto const parser = bp::merge[seq1 >> (seq2 | seq1)];
|
||||
|
||||
auto result = bp::parse("7a9a", parser);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, (bp::tuple<int, char>(9, 'a')));
|
||||
}
|
||||
}
|
||||
338
test/parser_or_permutations_1.cpp
Normal file
338
test/parser_or_permutations_1.cpp
Normal file
@@ -0,0 +1,338 @@
|
||||
/**
|
||||
* Copyright (C) 2024 T. Zachary Laine
|
||||
*
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <boost/parser/parser.hpp>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
|
||||
namespace bp = boost::parser;
|
||||
|
||||
namespace make {
|
||||
template<typename... Ts>
|
||||
auto tuple(Ts &&... xs)
|
||||
{
|
||||
return bp::tuple<Ts...>((Ts &&) xs...);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
{P0, -P0, *P0, P1, P2, P3, eps}
|
||||
<cartesian product>
|
||||
{P0, P1, P2, P3, eps, *P0, -P0, (P0 |/>> P2), -(P0 |/>> P1), (-P0 |/>> P1)}
|
||||
|
||||
P0 = bp::string("foo");
|
||||
P1 = bp::string("bar");
|
||||
P2 = bp::int_;
|
||||
P3 = bp::char_('c');
|
||||
*/
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
TEST(attributes, or_parser_permutations_1)
|
||||
{
|
||||
[[maybe_unused]] int dummy = 0; // for clang-format(!)
|
||||
|
||||
// P0
|
||||
{
|
||||
auto result = bp::parse("foo", bp::string("foo") | bp::string("foo"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, "foo"s);
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("bar", bp::string("foo") | bp::string("bar"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, "bar"s);
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("42", bp::string("foo") | bp::int_);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 1u);
|
||||
EXPECT_EQ(std::get<int>(*result), 42);
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("c", bp::string("foo") | bp::char_('c'));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 1u);
|
||||
EXPECT_EQ(std::get<char>(*result), 'c');
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("foo", bp::string("foo") | bp::eps);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, std::optional("foo"s));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("foo", bp::string("foo") | *bp::string("foo"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 0);
|
||||
EXPECT_EQ(std::get<std::string>(*result), "foo"s);
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("", bp::string("foo") | -bp::string("foo"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 1u);
|
||||
EXPECT_EQ(std::get<std::optional<std::string>>(*result), std::nullopt);
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"foo", bp::string("foo") | (bp::string("foo") | bp::int_));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 0);
|
||||
EXPECT_EQ(std::get<std::string>(*result), "foo"s);
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"bar",
|
||||
bp::string("foo") | -(bp::string("foo") | bp::string("bar")));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 1);
|
||||
EXPECT_EQ(std::get<std::optional<std::string>>(*result), "bar"s);
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"", bp::string("foo") | (-bp::string("foo") | bp::string("bar")));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 1);
|
||||
EXPECT_EQ(std::get<std::optional<std::string>>(*result), std::nullopt);
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"foo", bp::string("foo") | (bp::string("foo") >> bp::int_));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 0);
|
||||
EXPECT_EQ(std::get<std::string>(*result), "foo"s);
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"", bp::string("foo") | -(bp::string("foo") >> bp::string("bar")));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 1u);
|
||||
EXPECT_EQ(
|
||||
(std::get<std::optional<bp::tuple<std::string, std::string>>>(
|
||||
*result)),
|
||||
std::nullopt);
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"bar",
|
||||
bp::string("foo") | (-bp::string("foo") >> bp::string("bar")));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 1u);
|
||||
EXPECT_EQ(
|
||||
(std::get<bp::tuple<std::optional<std::string>, std::string>>(
|
||||
*result)),
|
||||
(make::tuple(std::optional<std::string>{}, "bar"s)));
|
||||
}
|
||||
|
||||
// -P0
|
||||
{
|
||||
auto result = bp::parse("foo", -bp::string("foo") | bp::string("foo"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 0);
|
||||
EXPECT_EQ(
|
||||
std::get<std::optional<std::string>>(*result),
|
||||
std::optional("foo"s));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("", -bp::string("foo") | bp::string("bar"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 0);
|
||||
EXPECT_EQ(std::get<std::optional<std::string>>(*result), std::nullopt);
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("", -bp::string("foo") | bp::int_);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 0);
|
||||
EXPECT_EQ(std::get<std::optional<std::string>>(*result), std::nullopt);
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("", -bp::string("foo") | bp::char_('c'));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 0);
|
||||
EXPECT_EQ(std::get<std::optional<std::string>>(*result), std::nullopt);
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("foo", -bp::string("foo") | bp::eps);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, "foo"s);
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("foo", -bp::string("foo") | *bp::string("foo"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 0);
|
||||
EXPECT_EQ(std::get<std::optional<std::string>>(*result), "foo"s);
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("foo", -bp::string("foo") | -bp::string("foo"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, "foo"s);
|
||||
}
|
||||
{
|
||||
auto result =
|
||||
bp::parse("", -bp::string("foo") | (bp::string("foo") | bp::int_));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 0);
|
||||
EXPECT_EQ(std::get<std::optional<std::string>>(*result), std::nullopt);
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"foo",
|
||||
-bp::string("foo") | -(bp::string("foo") | bp::string("bar")));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, "foo"s);
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"", -bp::string("foo") | (-bp::string("foo") | bp::string("bar")));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 0);
|
||||
EXPECT_EQ(std::get<std::optional<std::string>>(*result), std::nullopt);
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"foo", -bp::string("foo") | (bp::string("foo") >> bp::int_));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 0);
|
||||
EXPECT_EQ(std::get<std::optional<std::string>>(*result), "foo"s);
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"foo",
|
||||
-bp::string("foo") | -(bp::string("foo") >> bp::string("bar")));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 0);
|
||||
EXPECT_EQ(std::get<std::optional<std::string>>(*result), "foo"s);
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"foo",
|
||||
-bp::string("foo") | (-bp::string("foo") >> bp::string("bar")));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 0);
|
||||
EXPECT_EQ(std::get<std::optional<std::string>>(*result), "foo"s);
|
||||
}
|
||||
|
||||
// *P0
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"foo", bp::lexeme[*bp::string("foo")] | bp::string("foo"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 0);
|
||||
EXPECT_EQ(
|
||||
std::get<std::vector<std::string>>(*result), std::vector({"foo"s}));
|
||||
}
|
||||
{
|
||||
auto result =
|
||||
bp::parse("foofoo", *bp::string("foo") | bp::string("bar"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 0);
|
||||
EXPECT_EQ(
|
||||
std::get<std::vector<std::string>>(*result),
|
||||
std::vector({"foo"s, "foo"s}));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("", *bp::string("foo") | bp::int_);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 0);
|
||||
EXPECT_EQ(
|
||||
std::get<std::vector<std::string>>(*result),
|
||||
std::vector<std::string>{});
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("", *bp::string("foo") | bp::char_('c'));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 0);
|
||||
EXPECT_EQ(
|
||||
std::get<std::vector<std::string>>(*result),
|
||||
std::vector<std::string>{});
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("foofoo", *bp::string("foo") | bp::eps);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, std::optional(std::vector({"foo"s, "foo"s})));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"foofoo", bp::lexeme[*bp::string("foo")] | *bp::string("foo"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, std::vector({"foo"s, "foo"s}));
|
||||
}
|
||||
{
|
||||
auto result =
|
||||
bp::parse("foofoo", *bp::string("foo") | -bp::string("foo"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 0);
|
||||
EXPECT_EQ(
|
||||
std::get<std::vector<std::string>>(*result),
|
||||
std::vector({"foo"s, "foo"s}));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"foofoo",
|
||||
bp::lexeme[*bp::string("foo")] | (bp::string("foo") | bp::int_));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 0);
|
||||
EXPECT_EQ(
|
||||
std::get<std::vector<std::string>>(*result),
|
||||
std::vector({"foo"s, "foo"s}));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"foofoo",
|
||||
bp::lexeme[*bp::string("foo")] |
|
||||
-(bp::string("foo") | bp::string("bar")));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 0);
|
||||
EXPECT_EQ(
|
||||
std::get<std::vector<std::string>>(*result),
|
||||
std::vector({"foo"s, "foo"s}));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"foofoo",
|
||||
bp::lexeme[*bp::string("foo")] |
|
||||
(-bp::string("foo") | bp::string("bar")));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 0);
|
||||
EXPECT_EQ(
|
||||
std::get<std::vector<std::string>>(*result),
|
||||
std::vector({"foo"s, "foo"s}));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"foofoo",
|
||||
bp::lexeme[*bp::string("foo")] | (bp::string("foo") >> bp::int_));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 0);
|
||||
EXPECT_EQ(
|
||||
std::get<std::vector<std::string>>(*result),
|
||||
std::vector({"foo"s, "foo"s}));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"foofoo",
|
||||
bp::lexeme[*bp::string("foo")] |
|
||||
-(bp::string("foo") >> bp::string("bar")));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 0);
|
||||
EXPECT_EQ(
|
||||
std::get<std::vector<std::string>>(*result),
|
||||
std::vector({"foo"s, "foo"s}));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"foofoo",
|
||||
bp::lexeme[*bp::string("foo")] |
|
||||
(-bp::string("foo") >> bp::string("bar")));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 0);
|
||||
EXPECT_EQ(
|
||||
std::get<std::vector<std::string>>(*result),
|
||||
std::vector({"foo"s, "foo"s}));
|
||||
}
|
||||
}
|
||||
312
test/parser_or_permutations_2.cpp
Normal file
312
test/parser_or_permutations_2.cpp
Normal file
@@ -0,0 +1,312 @@
|
||||
/**
|
||||
* Copyright (C) 2024 T. Zachary Laine
|
||||
*
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <boost/parser/parser.hpp>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
|
||||
namespace bp = boost::parser;
|
||||
|
||||
namespace make {
|
||||
template<typename... Ts>
|
||||
auto tuple(Ts &&... xs)
|
||||
{
|
||||
return bp::tuple<Ts...>((Ts &&) xs...);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
{P0, -P0, *P0, P1, P2, P3, eps}
|
||||
<cartesian product>
|
||||
{P0, P1, P2, P3, eps, *P0, -P0, (P0 >>/| P2), -(P0 >>/| P1), (-P0 >>/| P1)}
|
||||
|
||||
P0 = bp::string("foo");
|
||||
P1 = bp::string("bar");
|
||||
P2 = bp::int_;
|
||||
P3 = bp::char_('c');
|
||||
*/
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
TEST(attributes, or_parser_permutations_2)
|
||||
{
|
||||
[[maybe_unused]] int dummy = 0; // for clang-format(!)
|
||||
|
||||
// P1
|
||||
{
|
||||
auto result = bp::parse("foo", bp::string("bar") | bp::string("foo"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, "foo"s);
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("bar", bp::string("bar") | bp::string("bar"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, "bar"s);
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("42", bp::string("bar") | bp::int_);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 1u);
|
||||
EXPECT_EQ(std::get<int>(*result), 42);
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("bar", bp::string("bar") | bp::char_('c'));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 0);
|
||||
EXPECT_EQ(std::get<std::string>(*result), "bar"s);
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("", bp::string("bar") | bp::eps);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, std::nullopt);
|
||||
}
|
||||
{
|
||||
auto result =
|
||||
bp::parse("foofoo", bp::string("bar") | *bp::string("foo"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 1u);
|
||||
EXPECT_EQ(
|
||||
std::get<std::vector<std::string>>(*result),
|
||||
std::vector({"foo"s, "foo"s}));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("bar", bp::string("bar") | -bp::string("foo"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 0);
|
||||
EXPECT_EQ(std::get<std::string>(*result), "bar"s);
|
||||
}
|
||||
{
|
||||
auto result =
|
||||
bp::parse("42", bp::string("bar") | (bp::string("foo") | bp::int_));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 1u);
|
||||
EXPECT_EQ(std::get<int>(*result), 42);
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"bar",
|
||||
bp::string("foo") | -(bp::string("foo") | bp::string("bar")));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 1u);
|
||||
EXPECT_EQ(std::get<std::optional<std::string>>(*result), "bar"s);
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"foo",
|
||||
bp::string("bar") | (-bp::string("foo") | bp::string("bar")));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 1u);
|
||||
EXPECT_EQ(std::get<std::optional<std::string>>(*result), "foo"s);
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"bar", bp::string("bar") | (bp::string("foo") >> bp::int_));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 0);
|
||||
EXPECT_EQ(std::get<std::string>(*result), "bar"s);
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"bar",
|
||||
bp::string("bar") | -(bp::string("foo") >> bp::string("bar")));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 0);
|
||||
EXPECT_EQ(std::get<std::string>(*result), "bar"s);
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"bar",
|
||||
bp::string("bar") | (-bp::string("foo") >> bp::string("bar")));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 0);
|
||||
EXPECT_EQ(std::get<std::string>(*result), "bar"s);
|
||||
}
|
||||
|
||||
// P2
|
||||
{
|
||||
auto result = bp::parse("42", bp::int_ | bp::string("foo"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 0);
|
||||
EXPECT_EQ(std::get<int>(*result), 42);
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("bar", bp::int_ | bp::string("bar"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 1u);
|
||||
EXPECT_EQ(std::get<std::string>(*result), "bar"s);
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("42", bp::int_ | bp::int_, bp::ws);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, 42);
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("c", bp::int_ | bp::char_('c'));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 1u);
|
||||
EXPECT_EQ(std::get<char>(*result), 'c');
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("", bp::int_ | bp::eps);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, std::nullopt);
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("foofoo", bp::int_ | *bp::string("foo"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 1u);
|
||||
EXPECT_EQ(
|
||||
std::get<std::vector<std::string>>(*result),
|
||||
std::vector({"foo"s, "foo"s}));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("42", bp::int_ | -bp::string("foo"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 0);
|
||||
EXPECT_EQ(std::get<int>(*result), 42);
|
||||
}
|
||||
{
|
||||
auto result =
|
||||
bp::parse("42", bp::int_ | (bp::string("foo") | bp::int_));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 0);
|
||||
EXPECT_EQ(std::get<int>(*result), 42);
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"bar", bp::int_ | -(bp::string("foo") | bp::string("bar")));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 1u);
|
||||
EXPECT_EQ(
|
||||
std::get<std::optional<std::string>>(*result),
|
||||
std::optional("bar"s));
|
||||
}
|
||||
{
|
||||
auto result =
|
||||
bp::parse("", bp::int_ | (-bp::string("foo") | bp::string("bar")));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 1u);
|
||||
EXPECT_EQ(std::get<std::optional<std::string>>(*result), std::nullopt);
|
||||
}
|
||||
{
|
||||
auto result =
|
||||
bp::parse("42", bp::int_ | (bp::string("foo") >> bp::int_));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 0);
|
||||
EXPECT_EQ(std::get<int>(*result), 42);
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"42", bp::int_ | -(bp::string("foo") >> bp::string("bar")));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 0);
|
||||
EXPECT_EQ(std::get<int>(*result), 42);
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"42", bp::int_ | (-bp::string("foo") >> bp::string("bar")));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 0);
|
||||
EXPECT_EQ(std::get<int>(*result), 42);
|
||||
}
|
||||
|
||||
// P3
|
||||
{
|
||||
auto result = bp::parse("c", bp::char_('c') | bp::string("foo"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 0);
|
||||
EXPECT_EQ(std::get<char>(*result), 'c');
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("bar", bp::char_('c') | bp::string("bar"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 1u);
|
||||
EXPECT_EQ(std::get<std::string>(*result), "bar"s);
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("42", bp::char_('c') | bp::int_);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 1u);
|
||||
EXPECT_EQ(std::get<int>(*result), 42);
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("c", bp::char_('c') | bp::char_('c'));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, 'c');
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("c", bp::char_('c') | bp::eps);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, 'c');
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("foofoo", bp::char_('c') | *bp::string("foo"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 1u);
|
||||
EXPECT_EQ(
|
||||
std::get<std::vector<std::string>>(*result),
|
||||
std::vector({"foo"s, "foo"s}));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("c", bp::char_('c') | -bp::string("foo"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 0);
|
||||
EXPECT_EQ(std::get<char>(*result), 'c');
|
||||
}
|
||||
{
|
||||
auto result =
|
||||
bp::parse("42", bp::char_('c') | (bp::string("foo") | bp::int_));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 2u);
|
||||
EXPECT_EQ(std::get<int>(*result), 42);
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"bar", bp::char_('c') | -(bp::string("foo") | bp::string("bar")));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 1u);
|
||||
EXPECT_EQ(
|
||||
std::get<std::optional<std::string>>(*result),
|
||||
std::optional("bar"s));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"foo", bp::char_('c') | (-bp::string("foo") | bp::string("bar")));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 1u);
|
||||
EXPECT_EQ(
|
||||
std::get<std::optional<std::string>>(*result),
|
||||
std::optional("foo"s));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"foo42", bp::char_('c') | (bp::string("foo") >> bp::int_));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 1u);
|
||||
EXPECT_EQ(
|
||||
(std::get<bp::tuple<std::string, int>>(*result)),
|
||||
(make::tuple("foo"s, 42)));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"c", bp::char_('c') | -(bp::string("foo") >> bp::string("bar")));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 0);
|
||||
EXPECT_EQ(std::get<char>(*result), 'c');
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"c", bp::char_('c') | (-bp::string("foo") >> bp::string("bar")));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(result->index(), 0);
|
||||
EXPECT_EQ(std::get<char>(*result), 'c');
|
||||
}
|
||||
|
||||
// eps | ... prohibited.
|
||||
}
|
||||
90
test/parser_perm.cpp
Normal file
90
test/parser_perm.cpp
Normal file
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* Copyright (C) 2024 T. Zachary Laine
|
||||
*
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <boost/parser/parser.hpp>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
|
||||
namespace bp = boost::parser;
|
||||
using namespace std::literals;
|
||||
|
||||
TEST(permutation_parser, basic)
|
||||
{
|
||||
{
|
||||
constexpr auto parser = bp::int_ || bp::string("foo");
|
||||
|
||||
{
|
||||
auto result = bp::parse("42 foo", parser, bp::ws);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, (bp::tuple<int, std::string>(42, "foo"s)));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("42foo", parser, bp::ws);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, (bp::tuple<int, std::string>(42, "foo"s)));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("foo 42", parser, bp::ws);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, (bp::tuple<int, std::string>(42, "foo"s)));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("foo42", parser, bp::ws);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, (bp::tuple<int, std::string>(42, "foo"s)));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
constexpr auto parser = bp::int_ || bp::string("foo") || bp::char_('g');
|
||||
|
||||
{
|
||||
auto result = bp::parse("42 foo g", parser, bp::ws);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(
|
||||
*result,
|
||||
(bp::tuple<int, std::string, double>(42, "foo"s, 'g')));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("42 g foo", parser, bp::ws);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(
|
||||
*result,
|
||||
(bp::tuple<int, std::string, double>(42, "foo"s, 'g')));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("foo 42 g", parser, bp::ws);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(
|
||||
*result,
|
||||
(bp::tuple<int, std::string, double>(42, "foo"s, 'g')));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("foo g 42", parser, bp::ws);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(
|
||||
*result,
|
||||
(bp::tuple<int, std::string, double>(42, "foo"s, 'g')));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("g foo 42", parser, bp::ws);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(
|
||||
*result,
|
||||
(bp::tuple<int, std::string, double>(42, "foo"s, 'g')));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("g 42 foo", parser, bp::ws);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(
|
||||
*result,
|
||||
(bp::tuple<int, std::string, double>(42, "foo"s, 'g')));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -557,3 +557,85 @@ namespace more_about_rules_4 {
|
||||
return bp::parse(str, bp::omit[parens], bp::ws);
|
||||
}
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
namespace param_example {
|
||||
//[ extended_param_yaml_example_rules
|
||||
namespace bp = boost::parser;
|
||||
|
||||
// A type to represent the YAML parse context.
|
||||
enum class context {
|
||||
block_in,
|
||||
block_out,
|
||||
block_key,
|
||||
flow_in,
|
||||
flow_out,
|
||||
flow_key
|
||||
};
|
||||
|
||||
// A YAML value; no need to fill it in for this example.
|
||||
struct value
|
||||
{
|
||||
// ...
|
||||
};
|
||||
|
||||
// YAML [66], just stubbed in here.
|
||||
auto const s_separate_in_line = bp::eps;
|
||||
|
||||
// YAML [137].
|
||||
bp::rule<struct c_flow_seq_tag, value> c_flow_sequence = "c-flow-sequence";
|
||||
// YAML [80].
|
||||
bp::rule<struct s_separate_tag> s_separate = "s-separate";
|
||||
// YAML [136].
|
||||
bp::rule<struct in_flow_tag, value> in_flow = "in-flow";
|
||||
// YAML [138]; just eps below.
|
||||
bp::rule<struct ns_s_flow_seq_entries_tag, value> ns_s_flow_seq_entries =
|
||||
"ns-s-flow-seq-entries";
|
||||
// YAML [81]; just eps below.
|
||||
bp::rule<struct s_separate_lines_tag> s_separate_lines = "s-separate-lines";
|
||||
|
||||
// Parser for YAML [137].
|
||||
auto const c_flow_sequence_def =
|
||||
'[' >>
|
||||
-s_separate.with(bp::_p<0>, bp::_p<1>) >>
|
||||
-in_flow.with(bp::_p<0>, bp::_p<1>) >>
|
||||
']';
|
||||
// Parser for YAML [80].
|
||||
auto const s_separate_def = bp::switch_(bp::_p<1>)
|
||||
(context::block_out, s_separate_lines.with(bp::_p<0>))
|
||||
(context::block_in, s_separate_lines.with(bp::_p<0>))
|
||||
(context::flow_out, s_separate_lines.with(bp::_p<0>))
|
||||
(context::flow_in, s_separate_lines.with(bp::_p<0>))
|
||||
(context::block_key, s_separate_in_line)
|
||||
(context::flow_key, s_separate_in_line);
|
||||
// Parser for YAML [136].
|
||||
auto const in_flow_def = bp::switch_(bp::_p<1>)
|
||||
(context::flow_out, ns_s_flow_seq_entries.with(bp::_p<0>, context::flow_in))
|
||||
(context::flow_in, ns_s_flow_seq_entries.with(bp::_p<0>, context::flow_in))
|
||||
(context::block_out, ns_s_flow_seq_entries.with(bp::_p<0>, context::flow_key))
|
||||
(context::flow_key, ns_s_flow_seq_entries.with(bp::_p<0>, context::flow_key));
|
||||
|
||||
auto const ns_s_flow_seq_entries_def = bp::eps;
|
||||
auto const s_separate_lines_def = bp::eps;
|
||||
|
||||
BOOST_PARSER_DEFINE_RULES(
|
||||
c_flow_sequence,
|
||||
s_separate,
|
||||
in_flow,
|
||||
ns_s_flow_seq_entries,
|
||||
s_separate_lines);
|
||||
//]
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
TEST(parser, extended_param_example)
|
||||
{
|
||||
using namespace param_example;
|
||||
|
||||
//[ extended_param_yaml_example_use
|
||||
auto const test_parser = c_flow_sequence.with(4, context::block_out);
|
||||
auto result = bp::parse("[]", test_parser);
|
||||
assert(result);
|
||||
//]
|
||||
(void)result;
|
||||
}
|
||||
|
||||
339
test/parser_seq_permutations_1.cpp
Normal file
339
test/parser_seq_permutations_1.cpp
Normal file
@@ -0,0 +1,339 @@
|
||||
/**
|
||||
* Copyright (C) 2024 T. Zachary Laine
|
||||
*
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <boost/parser/parser.hpp>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
|
||||
namespace bp = boost::parser;
|
||||
|
||||
namespace make {
|
||||
template<typename... Ts>
|
||||
auto tuple(Ts &&... xs)
|
||||
{
|
||||
return bp::tuple<Ts...>((Ts &&) xs...);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
{P0, -P0, *P0, P1, P2, P3, eps}
|
||||
<cartesian product>
|
||||
{P0, P1, P2, P3, eps, *P0, -P0, (P0 >>/| P2), -(P0 >>/| P1), (-P0 >>/| P1)}
|
||||
|
||||
P0 = bp::string("foo");
|
||||
P1 = bp::string("bar");
|
||||
P2 = bp::int_;
|
||||
P3 = bp::char_('c');
|
||||
*/
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
TEST(attributes, seq_parser_permutations_1)
|
||||
{
|
||||
[[maybe_unused]] int dummy = 0; // for clang-format(!)
|
||||
|
||||
// P0
|
||||
{
|
||||
auto result =
|
||||
bp::parse("foofoo", bp::string("foo") >> bp::string("foo"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, (make::tuple("foo"s, "foo"s)));
|
||||
}
|
||||
{
|
||||
auto result =
|
||||
bp::parse("foobar", bp::string("foo") >> bp::string("bar"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, (make::tuple("foo"s, "bar"s)));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("foo42", bp::string("foo") >> bp::int_);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, (make::tuple("foo"s, 42)));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("fooc", bp::string("foo") >> bp::char_('c'));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, "fooc"s);
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("foo", bp::string("foo") >> bp::eps);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, "foo"s);
|
||||
}
|
||||
{
|
||||
auto result =
|
||||
bp::parse("foofoofoo", bp::string("foo") >> *bp::string("foo"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, std::vector({"foo"s, "foo"s, "foo"s}));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("foo", bp::string("foo") >> -bp::string("foo"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, (make::tuple("foo"s, std::optional<std::string>{})));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"foofoo42", bp::string("foo") >> (bp::string("foo") >> bp::int_));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, (make::tuple("foo"s, "foo"s, 42)));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"foofoobar",
|
||||
bp::string("foo") >> -(bp::string("foo") >> bp::string("bar")));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(
|
||||
*result,
|
||||
(make::tuple("foo"s, std::optional(make::tuple("foo"s, "bar"s)))));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"foofoobar",
|
||||
bp::string("foo") >> (-bp::string("foo") >> bp::string("bar")));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(
|
||||
*result, (make::tuple("foo"s, std::optional("foo"s), "bar"s)));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"foo42", bp::string("foo") >> (bp::string("foo") | bp::int_));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(
|
||||
*result, (make::tuple("foo"s, std::variant<std::string, int>(42))));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"foo",
|
||||
bp::string("foo") >> -(bp::string("foo") | bp::string("bar")));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, (make::tuple("foo"s, std::optional<std::string>{})));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"foo",
|
||||
bp::string("foo") >> (-bp::string("foo") | bp::string("bar")));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(
|
||||
*result,
|
||||
(make::tuple(
|
||||
"foo"s,
|
||||
std::variant<std::optional<std::string>, std::string>(
|
||||
std::nullopt))));
|
||||
}
|
||||
|
||||
// -P0
|
||||
{
|
||||
auto result =
|
||||
bp::parse("foofoo", -bp::string("foo") >> bp::string("foo"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, (make::tuple(std::optional("foo"s), "foo"s)));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("bar", -bp::string("foo") >> bp::string("bar"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, (make::tuple(std::optional<std::string>{}, "bar"s)));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("42", -bp::string("foo") >> bp::int_);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, (make::tuple(std::optional<std::string>{}, 42)));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("c", -bp::string("foo") >> bp::char_('c'));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, (make::tuple(std::optional<std::string>{}, 'c')));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("foo", -bp::string("foo") >> bp::eps);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, std::optional("foo"s));
|
||||
}
|
||||
{
|
||||
auto result =
|
||||
bp::parse("foofoo", -bp::string("foo") >> *bp::string("foo"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, std::vector({"foo"s, "foo"s}));
|
||||
}
|
||||
{
|
||||
auto result =
|
||||
bp::parse("foofoo", -bp::string("foo") >> -bp::string("foo"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(
|
||||
*result,
|
||||
(make::tuple(std::optional("foo"s), std::optional("foo"s))));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"foofoo42", -bp::string("foo") >> (bp::string("foo") >> bp::int_));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, (make::tuple(std::optional("foo"s), "foo"s, 42)));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"foofoobar",
|
||||
-bp::string("foo") >> -(bp::string("foo") >> bp::string("bar")));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(
|
||||
*result,
|
||||
(make::tuple(
|
||||
std::optional("foo"s),
|
||||
std::optional((make::tuple("foo"s, "bar"s))))));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"foofoobar",
|
||||
-bp::string("foo") >> (-bp::string("foo") >> bp::string("bar")));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(
|
||||
*result,
|
||||
(make::tuple(
|
||||
std::optional("foo"s), std::optional("foo"s), "bar"s)));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"foo42", -bp::string("foo") >> (bp::string("foo") | bp::int_));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(
|
||||
*result,
|
||||
(make::tuple(
|
||||
std::optional("foo"s), std::variant<std::string, int>(42))));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"foobar",
|
||||
-bp::string("foo") >> -(bp::string("foo") | bp::string("bar")));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(
|
||||
*result,
|
||||
(make::tuple(std::optional("foo"s), std::optional("bar"s))));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"foofoo",
|
||||
-bp::string("foo") >> (-bp::string("foo") | bp::string("bar")));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(
|
||||
*result,
|
||||
(make::tuple(
|
||||
"foo"s,
|
||||
std::variant<std::optional<std::string>, std::string>(
|
||||
std::optional("foo"s)))));
|
||||
}
|
||||
|
||||
// *P0
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"foo foo",
|
||||
bp::lexeme[*bp::string("foo")] >> bp::string("foo"),
|
||||
bp::ws);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, std::vector({"foo"s, "foo"s}));
|
||||
}
|
||||
{
|
||||
auto result =
|
||||
bp::parse("foobar", *bp::string("foo") >> bp::string("bar"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, std::vector({"foo"s, "bar"s}));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("foo42", *bp::string("foo") >> bp::int_);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, (make::tuple(std::vector({"foo"s}), 42)));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("fooc", *bp::string("foo") >> bp::char_('c'));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, (make::tuple(std::vector({"foo"s}), 'c')));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("foo", *bp::string("foo") >> bp::eps);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, std::vector({"foo"s}));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"foo foo",
|
||||
bp::lexeme[*bp::string("foo")] >> *bp::string("foo"),
|
||||
bp::ws);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(
|
||||
*result,
|
||||
(make::tuple(std::vector({"foo"s}), std::vector({"foo"s}))));
|
||||
}
|
||||
{
|
||||
auto result =
|
||||
bp::parse("foo", *bp::string("foo") >> -bp::string("foo"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, std::vector({"foo"s}));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"foo foo42",
|
||||
bp::lexeme[*bp::string("foo")] >> (bp::string("foo") >> bp::int_),
|
||||
bp::ws);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, (make::tuple(std::vector({"foo"s, "foo"s}), 42)));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"foo foobar",
|
||||
bp::lexeme[*bp::string("foo")] >>
|
||||
-(bp::string("foo") >> bp::string("bar")),
|
||||
bp::ws);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(
|
||||
*result,
|
||||
(make::tuple(
|
||||
std::vector({"foo"s}),
|
||||
std::optional((make::tuple("foo"s, "bar"s))))));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"foo foobar",
|
||||
bp::lexeme[*bp::string("foo")] >>
|
||||
(-bp::string("foo") >> bp::string("bar")),
|
||||
bp::ws);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, std::vector({"foo"s, "foo"s, "bar"s}));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"foo 42",
|
||||
bp::lexeme[*bp::string("foo")] >> (bp::string("foo") | bp::int_),
|
||||
bp::ws);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(
|
||||
*result,
|
||||
(make::tuple(
|
||||
std::vector({"foo"s}), std::variant<std::string, int>(42))));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"foo bar",
|
||||
bp::lexeme[*bp::string("foo")] >>
|
||||
-(bp::string("foo") | bp::string("bar")),
|
||||
bp::ws);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, std::vector({"foo"s, "bar"s}));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"foo",
|
||||
bp::lexeme[*bp::string("foo")] >>
|
||||
(-bp::string("foo") | bp::string("bar")),
|
||||
bp::ws);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(
|
||||
*result,
|
||||
(make::tuple(
|
||||
std::vector({"foo"s}),
|
||||
std::variant<std::optional<std::string>, std::string>(
|
||||
std::nullopt))));
|
||||
}
|
||||
}
|
||||
370
test/parser_seq_permutations_2.cpp
Normal file
370
test/parser_seq_permutations_2.cpp
Normal file
@@ -0,0 +1,370 @@
|
||||
/**
|
||||
* Copyright (C) 2024 T. Zachary Laine
|
||||
*
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include <boost/parser/parser.hpp>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
|
||||
namespace bp = boost::parser;
|
||||
|
||||
namespace make {
|
||||
template<typename... Ts>
|
||||
auto tuple(Ts &&... xs)
|
||||
{
|
||||
return bp::tuple<Ts...>((Ts &&) xs...);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
{P0, -P0, *P0, P1, P2, P3, eps}
|
||||
<cartesian product>
|
||||
{P0, P1, P2, P3, eps, *P0, -P0, (P0 >>/| P2), -(P0 >>/| P1), (-P0 >>/| P1)}
|
||||
|
||||
P0 = bp::string("foo");
|
||||
P1 = bp::string("bar");
|
||||
P2 = bp::int_;
|
||||
P3 = bp::char_('c');
|
||||
*/
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
TEST(attributes, seq_parser_permutations_2)
|
||||
{
|
||||
[[maybe_unused]] int dummy = 0; // for clang-format(!)
|
||||
|
||||
// P1
|
||||
{
|
||||
auto result =
|
||||
bp::parse("barfoo", bp::string("bar") >> bp::string("foo"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, (make::tuple("bar"s, "foo"s)));
|
||||
}
|
||||
{
|
||||
auto result =
|
||||
bp::parse("barbar", bp::string("bar") >> bp::string("bar"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, (make::tuple("bar"s, "bar"s)));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("bar42", bp::string("bar") >> bp::int_);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, (make::tuple("bar"s, 42)));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("barc", bp::string("bar") >> bp::char_('c'));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, "barc"s);
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("bar", bp::string("bar") >> bp::eps);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, "bar"s);
|
||||
}
|
||||
{
|
||||
auto result =
|
||||
bp::parse("barfoofoo", bp::string("bar") >> *bp::string("foo"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, std::vector({"bar"s, "foo"s, "foo"s}));
|
||||
}
|
||||
{
|
||||
auto result =
|
||||
bp::parse("barfoo", bp::string("bar") >> -bp::string("foo"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, (make::tuple("bar"s, std::optional("foo"s))));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"barfoo42", bp::string("bar") >> (bp::string("foo") >> bp::int_));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, (make::tuple("bar"s, "foo"s, 42)));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"barfoobar",
|
||||
bp::string("bar") >> -(bp::string("foo") >> bp::string("bar")));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(
|
||||
*result,
|
||||
(make::tuple("bar"s, std::optional(make::tuple("foo"s, "bar"s)))));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"barfoobar",
|
||||
bp::string("bar") >> (-bp::string("foo") >> bp::string("bar")));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(
|
||||
*result, (make::tuple("bar"s, std::optional("foo"s), "bar"s)));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"bar42", bp::string("bar") >> (bp::string("foo") | bp::int_));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(
|
||||
*result, (make::tuple("bar"s, std::variant<std::string, int>(42))));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"bar",
|
||||
bp::string("bar") >> -(bp::string("foo") | bp::string("bar")));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, (make::tuple("bar"s, std::optional<std::string>{})));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"bar",
|
||||
bp::string("bar") >> (-bp::string("foo") | bp::string("bar")));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(
|
||||
*result,
|
||||
make::tuple(
|
||||
"bar"s,
|
||||
std::variant<std::optional<std::string>, std::string>(
|
||||
std::nullopt)));
|
||||
}
|
||||
|
||||
// P2
|
||||
{
|
||||
auto result = bp::parse("42foo", bp::int_ >> bp::string("foo"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, (make::tuple(42, "foo"s)));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("42bar", bp::int_ >> bp::string("bar"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, (make::tuple(42, "bar"s)));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("42 42", bp::int_ >> bp::int_, bp::ws);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, (make::tuple(42, 42)));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("42c", bp::int_ >> bp::char_('c'));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, (make::tuple(42, 'c')));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("42", bp::int_ >> bp::eps);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, 42);
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("42foofoo", bp::int_ >> *bp::string("foo"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, (make::tuple(42, std::vector({"foo"s, "foo"s}))));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("42foo", bp::int_ >> -bp::string("foo"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, (make::tuple(42, std::optional("foo"s))));
|
||||
}
|
||||
{
|
||||
auto result =
|
||||
bp::parse("42foo42", bp::int_ >> (bp::string("foo") >> bp::int_));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, (make::tuple(42, "foo"s, 42)));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"42foobar", bp::int_ >> -(bp::string("foo") >> bp::string("bar")));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(
|
||||
*result,
|
||||
(make::tuple(42, std::optional(make::tuple("foo"s, "bar"s)))));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"42foobar", bp::int_ >> (-bp::string("foo") >> bp::string("bar")));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, (make::tuple(42, std::optional("foo"s), "bar"s)));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"42 42", bp::int_ >> (bp::string("foo") | bp::int_), bp::ws);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(
|
||||
*result, (make::tuple(42, std::variant<std::string, int>(42))));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"42", bp::int_ >> -(bp::string("foo") | bp::string("bar")));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, (make::tuple(42, std::optional<std::string>{})));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"42", bp::int_ >> (-bp::string("foo") | bp::string("bar")));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(
|
||||
*result,
|
||||
make::tuple(
|
||||
42,
|
||||
std::variant<std::optional<std::string>, std::string>(
|
||||
std::nullopt)));
|
||||
}
|
||||
|
||||
// P3
|
||||
{
|
||||
auto result = bp::parse("cfoo", bp::char_('c') >> bp::string("foo"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, "cfoo"s);
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("cbar", bp::char_('c') >> bp::string("bar"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, "cbar"s);
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("c42", bp::char_('c') >> bp::int_);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, (make::tuple('c', 42)));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("cc", bp::char_('c') >> bp::char_('c'));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, "cc"s);
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("c", bp::char_('c') >> bp::eps);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, 'c');
|
||||
}
|
||||
{
|
||||
auto result =
|
||||
bp::parse("cfoofoo", bp::char_('c') >> *bp::string("foo"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, (make::tuple('c', std::vector({"foo"s, "foo"s}))));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("cfoo", bp::char_('c') >> -bp::string("foo"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, (make::tuple('c', std::optional("foo"s))));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"cfoo42", bp::char_('c') >> (bp::string("foo") >> bp::int_));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, (make::tuple("cfoo"s, 42)));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"cfoobar",
|
||||
bp::char_('c') >> -(bp::string("foo") >> bp::string("bar")));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(
|
||||
*result,
|
||||
(make::tuple('c', std::optional(make::tuple("foo"s, "bar"s)))));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"cfoobar",
|
||||
bp::char_('c') >> (-bp::string("foo") >> bp::string("bar")));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, (make::tuple('c', std::optional("foo"s), "bar"s)));
|
||||
}
|
||||
{
|
||||
auto result =
|
||||
bp::parse("c42", bp::char_('c') >> (bp::string("foo") | bp::int_));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(
|
||||
*result, (make::tuple('c', std::variant<std::string, int>(42))));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"c", bp::char_('c') >> -(bp::string("foo") | bp::string("bar")));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, (make::tuple('c', std::optional<std::string>{})));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"c", bp::char_('c') >> (-bp::string("foo") | bp::string("bar")));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(
|
||||
*result,
|
||||
(make::tuple(
|
||||
'c',
|
||||
std::variant<std::optional<std::string>, std::string>(
|
||||
std::nullopt))));
|
||||
}
|
||||
|
||||
// eps
|
||||
{
|
||||
auto result = bp::parse("foo", bp::eps >> bp::string("foo"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, "foo"s);
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("bar", bp::eps >> bp::string("bar"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, "bar"s);
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("42", bp::eps >> bp::int_);
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, 42);
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("c", bp::eps >> bp::char_('c'));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, 'c');
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("", bp::eps >> bp::eps);
|
||||
EXPECT_TRUE(result);
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("foofoo", bp::eps >> *bp::string("foo"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, std::vector({"foo"s, "foo"s}));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse("foo", bp::eps >> -bp::string("foo"));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, std::optional("foo"s));
|
||||
}
|
||||
{
|
||||
auto result =
|
||||
bp::parse("foo42", bp::eps >> (bp::string("foo") >> bp::int_));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, (make::tuple("foo"s, 42)));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"foobar", bp::eps >> -(bp::string("foo") >> bp::string("bar")));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, std::optional((make::tuple("foo"s, "bar"s))));
|
||||
}
|
||||
{
|
||||
auto result = bp::parse(
|
||||
"foobar", bp::eps >> (-bp::string("foo") >> bp::string("bar")));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, (make::tuple(std::optional("foo"s), "bar"s)));
|
||||
}
|
||||
{
|
||||
auto result =
|
||||
bp::parse("42", bp::eps >> (bp::string("foo") | bp::int_));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, (std::variant<std::string, int>(42)));
|
||||
}
|
||||
{
|
||||
auto result =
|
||||
bp::parse("", bp::eps >> -(bp::string("foo") | bp::string("bar")));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(*result, std::optional<std::string>{});
|
||||
}
|
||||
{
|
||||
auto result =
|
||||
bp::parse("", bp::eps >> (-bp::string("foo") | bp::string("bar")));
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_EQ(
|
||||
*result,
|
||||
(std::variant<std::optional<std::string>, std::string>(
|
||||
std::nullopt)));
|
||||
}
|
||||
}
|
||||
@@ -258,7 +258,8 @@ TEST(replace, replace_unicode)
|
||||
int count = 0;
|
||||
std::string_view const strs[] = {"aa", "foo", "b"};
|
||||
for (auto subrange : r) {
|
||||
std::string str(subrange.begin(), subrange.end());
|
||||
auto u8sub = subrange | bp::as_utf8;
|
||||
std::string str(u8sub.begin(), u8sub.end());
|
||||
EXPECT_EQ(str, strs[count]);
|
||||
++count;
|
||||
}
|
||||
@@ -274,7 +275,8 @@ TEST(replace, replace_unicode)
|
||||
int count = 0;
|
||||
std::string_view const strs[] = {"aa", "foo", "baaba", "foo"};
|
||||
for (auto subrange : r) {
|
||||
std::string str(subrange.begin(), subrange.end());
|
||||
auto u8sub = subrange | bp::as_utf8;
|
||||
std::string str(u8sub.begin(), u8sub.end());
|
||||
EXPECT_EQ(str, strs[count]);
|
||||
++count;
|
||||
}
|
||||
@@ -301,7 +303,8 @@ TEST(replace, replace_unicode)
|
||||
int count = 0;
|
||||
std::string_view const strs[] = {"aa", "foo", "baaba", "foo"};
|
||||
for (auto subrange : r) {
|
||||
std::string str(subrange.begin(), subrange.end());
|
||||
auto u8sub = subrange | bp::as_utf8;
|
||||
std::string str(u8sub.begin(), u8sub.end());
|
||||
EXPECT_EQ(str, strs[count]);
|
||||
++count;
|
||||
}
|
||||
@@ -314,7 +317,8 @@ TEST(replace, replace_unicode)
|
||||
int count = 0;
|
||||
std::string_view const strs[] = {"aa", "foo", "baaba", "foo", "foo"};
|
||||
for (auto subrange : r) {
|
||||
std::string str(subrange.begin(), subrange.end());
|
||||
auto u8sub = subrange | bp::as_utf8;
|
||||
std::string str(u8sub.begin(), u8sub.end());
|
||||
EXPECT_EQ(str, strs[count]);
|
||||
++count;
|
||||
}
|
||||
@@ -342,7 +346,8 @@ TEST(replace, replace_unicode)
|
||||
std::string_view const strs[] = {
|
||||
"foo", "foo", "aa", "foo", "baaba", "foo", "foo"};
|
||||
for (auto subrange : r) {
|
||||
std::string str(subrange.begin(), subrange.end());
|
||||
auto u8sub = subrange | bp::as_utf8;
|
||||
std::string str(u8sub.begin(), u8sub.end());
|
||||
EXPECT_EQ(str, strs[count]);
|
||||
++count;
|
||||
}
|
||||
@@ -356,7 +361,8 @@ TEST(replace, replace_unicode)
|
||||
std::string_view const strs[] = {
|
||||
"foo", "foo", "aa", "foo", "baaba", "foo", "foo"};
|
||||
for (auto subrange : r) {
|
||||
std::string str(subrange.begin(), subrange.end());
|
||||
auto u8sub = subrange | bp::as_utf8;
|
||||
std::string str(u8sub.begin(), u8sub.end());
|
||||
EXPECT_EQ(str, strs[count]);
|
||||
++count;
|
||||
}
|
||||
@@ -370,7 +376,8 @@ TEST(replace, replace_unicode)
|
||||
std::string_view const strs[] = {
|
||||
"foo", "foo", "aa", "foo", "baaba", "foo", "foo"};
|
||||
for (auto subrange : r) {
|
||||
std::string str(subrange.begin(), subrange.end());
|
||||
auto u8sub = subrange | bp::as_utf8;
|
||||
std::string str(u8sub.begin(), u8sub.end());
|
||||
EXPECT_EQ(str, strs[count]);
|
||||
++count;
|
||||
}
|
||||
@@ -391,7 +398,7 @@ TEST(replace, join_compat)
|
||||
std::string replace_result;
|
||||
for (auto ch : rng) {
|
||||
static_assert(std::is_same_v<decltype(ch), char32_t>);
|
||||
replace_result.push_back(ch);
|
||||
replace_result.push_back((char)ch);
|
||||
}
|
||||
EXPECT_EQ(replace_result, "foofooaafoobaabafoofoo");
|
||||
}
|
||||
@@ -437,17 +444,35 @@ TEST(replace, join_compat)
|
||||
|
||||
TEST(replace, doc_examples)
|
||||
{
|
||||
// clang-format off
|
||||
{
|
||||
auto rng = "XYZaaXYZbaabaXYZXYZ" | bp::replace(bp::lit("XYZ"), "foo");
|
||||
namespace bp = boost::parser;
|
||||
auto card_number = bp::int_ >> bp::repeat(3)['-' >> bp::int_];
|
||||
auto rng = "My credit card number is 1234-5678-9012-3456." | bp::replace(card_number, "XXXX-XXXX-XXXX-XXXX");
|
||||
int count = 0;
|
||||
// Prints foo aa foo baaba foo foo.
|
||||
// Prints My credit card number is XXXX-XXXX-XXXX-XXXX.
|
||||
for (auto subrange : rng) {
|
||||
std::cout << std::string_view(subrange.begin(), subrange.end() - subrange.begin()) << " ";
|
||||
std::cout << std::string_view(subrange.begin(), subrange.end() - subrange.begin());
|
||||
++count;
|
||||
}
|
||||
std::cout << "\n";
|
||||
assert(count == 6);
|
||||
assert(count == 3);
|
||||
}
|
||||
#if BOOST_PARSER_USE_CONCEPTS && (!defined(__GNUC__) || 12 <= __GNUC__)
|
||||
{
|
||||
namespace bp = boost::parser;
|
||||
auto card_number = bp::int_ >> bp::repeat(3)['-' >> bp::int_];
|
||||
auto rng = "My credit card number is 1234-5678-9012-3456." |
|
||||
bp::replace(card_number, "XXXX-XXXX-XXXX-XXXX") |
|
||||
std::views::join;
|
||||
std::string replace_result;
|
||||
for (auto ch : rng) {
|
||||
replace_result.push_back(ch);
|
||||
}
|
||||
assert(replace_result == "My credit card number is XXXX-XXXX-XXXX-XXXX.");
|
||||
}
|
||||
#endif
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -739,6 +739,7 @@ TEST(search, doc_examples)
|
||||
assert(
|
||||
std::string_view(result.begin(), result.end() - result.begin()) ==
|
||||
"XYZ");
|
||||
(void)result;
|
||||
}
|
||||
{
|
||||
auto r = "XYZaaXYZbaabaXYZXYZ" | bp::search_all(bp::lit("XYZ"));
|
||||
|
||||
@@ -103,6 +103,13 @@ int main()
|
||||
|
||||
PARSE(char_ | int_ | float_);
|
||||
|
||||
std::cout << "\n\n"
|
||||
<< "----------------------------------------\n"
|
||||
<< "| operator|| |\n"
|
||||
<< "----------------------------------------\n";
|
||||
|
||||
PARSE(char_ || int_ || float_);
|
||||
|
||||
std::cout << "\n\n"
|
||||
<< "----------------------------------------\n"
|
||||
<< "| operator- (binary) |\n"
|
||||
@@ -131,6 +138,14 @@ int main()
|
||||
|
||||
PARSE(char_[a]);
|
||||
|
||||
std::cout << "\n\n"
|
||||
<< "----------------------------------------\n"
|
||||
<< "| transform)f_[] |\n"
|
||||
<< "----------------------------------------\n";
|
||||
|
||||
auto f = [](auto x) { return x; };
|
||||
PARSE(transform(f)[char_]);
|
||||
|
||||
std::cout << "\n\n"
|
||||
<< "----------------------------------------\n"
|
||||
<< "| omit[] |\n"
|
||||
|
||||
@@ -14,7 +14,8 @@
|
||||
|
||||
#include <list>
|
||||
|
||||
#if (!defined(_MSC_VER) || BOOST_PARSER_USE_CONCEPTS)
|
||||
#if (!defined(_MSC_VER) || BOOST_PARSER_USE_CONCEPTS) && \
|
||||
(!defined(__GNUC__) || 12 <= __GNUC__ || !BOOST_PARSER_USE_CONCEPTS)
|
||||
|
||||
namespace bp = boost::parser;
|
||||
|
||||
@@ -680,7 +681,8 @@ TEST(transform_replace, transform_replace_unicode)
|
||||
int count = 0;
|
||||
std::string replace_result;
|
||||
for (auto subrange : r) {
|
||||
std::string str(subrange.begin(), subrange.end());
|
||||
auto u8sub = subrange | bp::as_utf8;
|
||||
std::string str(u8sub.begin(), u8sub.end());
|
||||
replace_result += str;
|
||||
++count;
|
||||
}
|
||||
@@ -695,7 +697,8 @@ TEST(transform_replace, transform_replace_unicode)
|
||||
int count = 0;
|
||||
std::string replace_result;
|
||||
for (auto subrange : r) {
|
||||
std::string str(subrange.begin(), subrange.end());
|
||||
auto u8sub = subrange | bp::as_utf8;
|
||||
std::string str(u8sub.begin(), u8sub.end());
|
||||
replace_result += str;
|
||||
++count;
|
||||
}
|
||||
@@ -724,7 +727,8 @@ TEST(transform_replace, transform_replace_unicode)
|
||||
int count = 0;
|
||||
std::string replace_result;
|
||||
for (auto subrange : r) {
|
||||
std::string str(subrange.begin(), subrange.end());
|
||||
auto u8sub = subrange | bp::as_utf8;
|
||||
std::string str(u8sub.begin(), u8sub.end());
|
||||
replace_result += str;
|
||||
++count;
|
||||
}
|
||||
@@ -738,7 +742,8 @@ TEST(transform_replace, transform_replace_unicode)
|
||||
int count = 0;
|
||||
std::string replace_result;
|
||||
for (auto subrange : r) {
|
||||
std::string str(subrange.begin(), subrange.end());
|
||||
auto u8sub = subrange | bp::as_utf8;
|
||||
std::string str(u8sub.begin(), u8sub.end());
|
||||
replace_result += str;
|
||||
++count;
|
||||
}
|
||||
@@ -774,7 +779,7 @@ TEST(transform_replace, join_compat)
|
||||
std::string transform_replace_result;
|
||||
for (auto ch : rng) {
|
||||
static_assert(std::is_same_v<decltype(ch), char16_t>);
|
||||
transform_replace_result.push_back(ch);
|
||||
transform_replace_result.push_back((char)ch);
|
||||
}
|
||||
EXPECT_EQ(transform_replace_result, "1_aa88_99_baaba111_2222_ 3_4_");
|
||||
}
|
||||
@@ -786,7 +791,7 @@ TEST(transform_replace, join_compat)
|
||||
std::string transform_replace_result;
|
||||
for (auto ch : rng) {
|
||||
static_assert(std::is_same_v<decltype(ch), char32_t>);
|
||||
transform_replace_result.push_back(ch);
|
||||
transform_replace_result.push_back((char)ch);
|
||||
}
|
||||
EXPECT_EQ(transform_replace_result, "1_aa88_99_baaba111_2222_ 3_4_");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user