2
0
mirror of https://github.com/boostorg/parser.git synced 2026-01-20 04:42:22 +00:00

93 Commits

Author SHA1 Message Date
Zach Laine
e3fbd6c849 WIP_quoted_string 2024-02-22 00:58:00 -06:00
Zach Laine
2b4243151a Add Phil Endecott's example latlong parser from the Boost mailing list review
as a test.
2024-02-22 00:56:54 -06:00
Zach Laine
63884d2efa Fix an error in seq_parser when a non-attribute parser fails internally, after
the successful parse of an adjacent attribute-generating parser.  Since the
non-attribute parser points to the same spot in the output tuple, if the
non-attribute parser fails internally, it is likely to do "out_attr =
decltype(out_attr)()", which erases the previous seuccessfully generated
attribute.
2024-02-22 00:13:07 -06:00
Zach Laine
3487211426 Use a more compelling example in the example code for replace().
Fixes #96.
2024-02-19 21:50:36 -06:00
Zach Laine
c12c512a94 Call out the constraint on the ReplacementV template parameter to replace().
Partially addresses #97.
2024-02-19 21:33:52 -06:00
Zach Laine
8ce60a8e53 Remove superfluous utf_pointer concept.
Partially addresses #97.
2024-02-19 18:33:13 -06:00
Zach Laine
4287c9b7b3 Fix transform_replace* in C++20 mode on GCC < 12. 2024-02-11 18:56:59 -06:00
Zach Laine
875eb9e4aa Adjust outdated comment. 2024-02-11 18:55:01 -06:00
Zach Laine
5c7b907904 Cruft removal. 2024-02-11 18:14:40 -06:00
Zach Laine
f99ae3b94a Use bp::get() instead of Hana literals in example, so as not to break non-Hana
builds.
2024-02-11 16:54:44 -06:00
Zach Laine
b6b77bac3c Explain more fully in More about Rules that you need to write a semantic
action sometimes for a rule, and under what cirumstances you do.

Fixes #93.
2024-02-11 16:46:47 -06:00
Zach Laine
f326cd9d23 Add a note about using "| join" to the transform_replace docs. 2024-02-11 16:10:00 -06:00
Zach Laine
ab20bdd87f Add note about how to write a lambda directly in a semantic action. Modify
the claim that directives can be spotted by looking for "[]", by mentioning
the exception of semantic actions.

Partially addresses #93.
2024-02-11 16:08:32 -06:00
Zach Laine
e6b59a4784 Fix GCC 12 C++20 build. 2024-02-11 00:24:39 -06:00
Zach Laine
2f8270809c Disable transform_replace* for GCC < 12 in C++20 mode. 2024-02-11 00:24:39 -06:00
Zach Laine
cdb86d8c23 Add special-case logic for detail::range_utf_format() to recognize that
utf8_view is UTF-8-encoded in non-concpets mode.
2024-02-10 23:25:54 -06:00
Zach Laine
150e09e309 Flesh out conditional transform_replace_view enable_borrowed_range; fix
missing std::ranges:: on it and other enable_borrowed_range specializations.
2024-02-10 19:43:07 -06:00
Zach Laine
d0208fb12c Add transform_replace range adaptor and transform_replace_view. 2024-02-10 18:42:18 -06:00
Zach Laine
129a0ec531 Add a test to parser.cpp that covers #90. This just verifies that #90 is not
an issue.

Fixes #90.
2024-02-04 16:52:40 -06:00
Zach Laine
95a5e088bc Remove reference to the unified header build, since it's broken for some
unknown reason.
2024-02-04 16:51:44 -06:00
Zach Laine
bb3f66db5f Document The incompatibility of tuple->aggregate conversions with assignment
to variants.

Fixes #91.
2024-02-04 16:16:47 -06:00
Zach Laine
09322b8eb6 Add more information about problematic alternative parsers.
Fixes #92.
2024-02-04 16:16:47 -06:00
Zach Laine
ae0448e321 Restore enable_variant, to fix errors in printing of the badly-broken
boost::variant.

Fixes #88.
2024-02-04 16:16:47 -06:00
Zach Laine
c990799c51 Add spacing to some tests to make sure that raw[] functions correctly with a
skipper.
2024-02-01 22:55:42 -06:00
Zach Laine
7f287c5525 Fix error in no_case[] evaluation. 2024-02-01 20:38:41 -06:00
Zach Laine
f9ebf50228 auto -> bool for null_sentinel_t op== for compatibility with
equality_comparable_with on Clang.
2024-02-01 17:21:53 -06:00
Zach Laine
d0cf708ef5 Address TODO about needed fix in compile_seq_attribute test. 2024-01-31 20:52:16 -06:00
Zach Laine
47061a8716 Remove some stray references to the now-removed ascii:: namespace parsers. 2024-01-31 20:31:35 -06:00
Zach Laine
ec5fc2ed85 Turn detail::char_{set,subranges} into structs to fix MSVC build. 2024-01-30 23:31:01 -06:00
Zach Laine
4b59719e44 Add Unicode versions of most of the ascii namespace parsers; remove the ascii
namespace.
2024-01-29 23:36:03 -06:00
Zach Laine
e06f415134 Fix broken macro guards on use of std::ranges which was breaking GCC >= 12. 2024-01-29 00:54:22 -06:00
Zach Laine
495a3fffbf Add another case similar to the failing case in compile_seq_attribute.cpp. 2024-01-28 21:24:53 -06:00
Zach Laine
3d528fa531 Remove mooted TODOs. 2024-01-28 21:23:06 -06:00
Zach Laine
68c33a0181 Address TODO about non-obviously ill-formed code using merge[]. 2024-01-28 20:54:28 -06:00
Zach Laine
b58b3a0779 Address TODO about documenting the unavailability of replace{,_view} on MSVC
in C++17 mode.
2024-01-28 20:54:24 -06:00
Zach Laine
5b7889df61 Address a TODO about documenting part of the loose attribute match behavior. 2024-01-28 20:54:22 -06:00
Zach Laine
f7d26dabae Remove moot TODO. 2024-01-28 20:54:18 -06:00
Zach Laine
935165b798 Explain more fully why error messages are only generated at failed expectation
points.

Fixes #80.
2024-01-28 20:54:16 -06:00
Zach Laine
7d8f4d811a Fix older GCC builds. 2024-01-28 16:39:34 -06:00
Zach Laine
a337c16e3c Add missing #include <array> to all_t.hpp. 2024-01-28 16:23:40 -06:00
Zach Laine
2e49e5d61b Another MSVC C++17 build fix; make text::detail::ref_view reject rvalues. 2024-01-28 16:18:43 -06:00
Zach Laine
df6a3db364 Fix MSVC C++17 build, by adjusting the heuristic used by view trait used in
all().
2024-01-28 15:49:04 -06:00
Zach Laine
7d983817cc Re-enable conditional use of concepts in detail::text code on MSVC. 2024-01-28 15:38:15 -06:00
Zach Laine
4d14fba7fd Change concept can_utf_view to use the form MSVC uses in its std lib, and turn
off code relying on alias CTAD on MSVC.  Add test cases for {w,}string{,_view}
| as_utfN in C++20 builds.
2024-01-28 15:34:51 -06:00
Zach Laine
01f2f21899 Fix MSVC build. 2024-01-28 14:09:12 -06:00
Zach Laine
473910bd52 Remove spurious is_pointer_v check in detail::to_range. 2024-01-28 14:04:25 -06:00
Zach Laine
9d1a6aeeb3 Extend all_t test to test arrays; fix errors. 2024-01-28 14:03:02 -06:00
Zach Laine
33edb6c4e8 Add all_t, and use it in non-concepts builds.
Fixes #85.
2024-01-28 02:07:07 -06:00
Zach Laine
de74eecabe Fix broken macro guard on replace join_compat test. 2024-01-28 02:01:47 -06:00
Zach Laine
91b2a36b37 Add replace.hpp to unified-header build. 2024-01-28 01:17:26 -06:00
Zach Laine
ab4d708c30 Set the out-param to Attr() on parse failure (for the overloads taking an
out-param), making alls to parse() consistent with calls to parsers. Parsers
clear the attribute on failure. Add a section to rationale on why this is the
right choice.

Fixes #78.
2024-01-28 01:17:17 -06:00
Zach Laine
e7efe90a03 In detail::assign, static_assert that the assignment is not arithmetic-type ->
std::string.

Fixes #76.
2024-01-28 01:17:06 -06:00
Zach Laine
4380d278aa Make it clearer, in both sections about rules, that not providing an attribute
template paramter implies that the rule has no attribute.

Fixes #75.
2024-01-28 01:16:59 -06:00
Zach Laine
ac84ed63b6 Cruft removal. 2024-01-27 20:36:00 -06:00
Zach Laine
fc6a643a99 Add a real no-format enumerator to text::format. 2024-01-27 20:35:56 -06:00
Andrzej Krzemieński
9fead755ca docs: typo in __no_case 2024-01-27 12:34:06 -06:00
Zach Laine
c328a18121 Add a Doxygen comment description for replace_view. 2024-01-22 21:51:23 -06:00
Zach Laine
3549ad64a8 Disable replace/replace_view entirely in MSVC C++17 builds. 2024-01-23 03:36:38 -06:00
Zach Laine
275874f3c6 Fix misplaced #endif. 2024-01-23 03:08:34 -06:00
Zach Laine
319b39a67b Fix MSVC build. 2024-01-23 02:44:30 -06:00
Zach Laine
edebfc3f57 Fix tests in C++20 mode on older GCCs. 2024-01-22 19:39:55 -06:00
Zach Laine
385a6a4ca6 Fix C++20 builds on older GCCs. 2024-01-22 19:28:48 -06:00
Zach Laine
4ce4d8ba0e Fix typo in stl_interfaces::adaptor (f/f_ confusion). 2024-01-22 01:35:26 -06:00
Zach Laine
a68d4f61b2 Add replace_view and range adaptor replace. 2024-01-22 01:11:17 -06:00
Zach Laine
d1309560df Add detail::maybe_const, and use it properly in search_all_view and
split_view.
2024-01-21 15:40:51 -06:00
Zach Laine
bbb1cba804 Fix the broken support for operating on C-style strings in search{(),_all} and
split.
2024-01-21 15:31:31 -06:00
Zach Laine
a6db478691 Add is_range_like trait to stand in for the new concept in C++17 code. 2024-01-21 15:31:31 -06:00
Zach Laine
9bd38a51f6 Concept parsable_code_unit -> code_unit; add range_like concept. 2024-01-21 15:31:31 -06:00
Zach Laine
8df56b083d Bring in fix for dangling temporaries from Boost.STLInterfaces. 2024-01-21 15:31:31 -06:00
Zach Laine
37e56dd241 Update deploy_unified_header.yml to include search.hpp and split.hpp.
Partially addresses #64.
2024-01-20 03:42:46 -06:00
Zach Laine
1aa95127f0 Only enable the previous fix in post-C++17 builds. Sigh. 2024-01-20 03:36:44 -06:00
Zach Laine
eb68bacfbb Fix improperly macro-disabled enable_borrowed_range specializations, to fix
MSVC builds.
2024-01-20 09:28:44 -06:00
Zach Laine
a68c1d3187 Add documentation section on algorithms views and view adaptors, to document
search(), search_all, and split.

Partially addresses #64.
2024-01-20 03:11:43 -06:00
Zach Laine
5adef16a4d Add split_view + split range adaptor. split produces a range of subranges,
each of which is a nonoveralpping match of the given parser.

Fixes #64.
2024-01-19 21:19:53 -06:00
Zach Laine
aa76e9f85e Add search() algorithm, and search_all_view + search_all range adaptor.
search() finds the first occurrence of a match of the given parser.
search_all produces a range of subranges, each of which is a nonoveralpping
match of the given parser.

Partially addresses #64.
2024-01-19 21:19:53 -06:00
Zach Laine
52e8b187e8 Document BOOST_PARSER_SUBRANGE in Configuration and Optional Features.
Fixes #73.
2024-01-19 21:17:25 -06:00
Zach Laine
591ac9921b Remove BOOST_PARSER_DEFINE_RULE now that BOOST_PARSER_DEFINE_RULES is always
available.

Partially addresses #71.
2024-01-19 21:06:33 -06:00
Zach Laine
ce7804a003 Refer to the text asociated with a rule as diagnostic text rather than a name,
in the code and the docs.

Fixes #72.
2024-01-19 20:54:58 -06:00
Zach Laine
4c3e0a7448 Add another reason for using rules -- callback parsing. 2024-01-19 20:54:51 -06:00
Zach Laine
ffdc3f967b Add docs for the new automatic subitutability of non-aggregate class types for
tuples.

Partially addresses #49.
2024-01-19 20:15:07 -06:00
Zach Laine
62002e886a Add support for parsing into an arbitrary class type T, as long as the default
attribute we're parsing is a tuple whose elements can be used to construct T.
Needs docs.

Fixes #49.
2024-01-19 01:00:39 -06:00
Zach Laine
ee00c0393a Put the default behaviors at the first of the constexpr if chains in
detail::{assign,move_back}().
2024-01-19 01:00:39 -06:00
Zach Laine
a7384b82ad Fix non-advancing iterator error in seq_parser, ue to first/first_ name
confusion(!).
2024-01-19 01:00:38 -06:00
Zach Laine
338a519bd6 In deploy_unified_header.yml, only commit and push if the generated file
indicates diffs.
2024-01-18 01:12:01 -06:00
Zach Laine
7f4e8ed904 Add a note to the README about the unified header and Compiler Explorer.
Fixes #70.
2024-01-18 00:59:23 -06:00
Zach Laine
f1c77a05cd Change the way deploy_unified_header.yml generates a unified header to make
well-formed code.
2024-01-18 00:55:35 -06:00
Zach Laine
a0d18feedc Add git push to deploy_unified_header.yml. 2024-01-18 00:26:33 -06:00
Zach Laine
22bf642024 Add git config user and email to deploy_unified_header.yml. 2024-01-18 00:23:35 -06:00
Zach Laine
33d0e2aef8 Add another missing $ to deploy_unified_header.yml. 2024-01-18 00:18:29 -06:00
Zach Laine
cfbabba598 Add a git fetch to the Deploy step of deploy_unified_header.yml. 2024-01-18 00:16:59 -06:00
Zach Laine
7e2bfb691c Use continue-on-error: true in the build step of deploy_unified_header.yml. 2024-01-18 00:11:41 -06:00
Zach Laine
a64d58aaff Fix missing $ in Github action. 2024-01-18 00:07:03 -06:00
Zach Laine
2b30e172e4 Add deploy_unified_header.yml. Fingers crossed. 2024-01-18 00:03:48 -06:00
47 changed files with 10188 additions and 606 deletions

View File

@@ -0,0 +1,34 @@
name: Build+deploy to single_header.
on:
push:
branches: [ "master" ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup
run: |
mkdir ${{github.workspace}}/pcpp
pip install -t ${{github.workspace}}/pcpp pcpp
- name: Build
continue-on-error: true
run: PYTHONPATH=${{github.workspace}}/pcpp:$PYTHONPATH python3 ${{github.workspace}}/pcpp/bin/pcpp -I ${{github.workspace}}/include --passthru-defines --passthru-comments --line-directive -o ${{github.workspace}}/onebig.hpp ${{github.workspace}}/include/boost/parser/split.hpp ${{github.workspace}}/include/boost/parser/replace.hpp ${{github.workspace}}/include/boost/parser/transcode_view.hpp
- name: Deploy
working-directory: ${{github.workspace}}
run: |
git fetch
git switch single_header
mv ${{github.workspace}}/onebig.hpp ${{github.workspace}}/include/boost/parser/parser_unified.hpp
if [ `git diff` ] ; then
git config user.name 'Zach Laine'
git config user.email 'whatwasthataddress@gmail.com'
git commit -am 'Generated by Github action on push to master.'
git push
fi

View File

@@ -100,22 +100,43 @@ significantly increase compile times. Also, MSVC seems to have a hard time
with large values; I successfully set this value to `50` on MSVC, but `100`
broke the MSVC build entirely.
_Parser_ uses `std::optional` internally. There is no way to change this.
However, when _Parser_ generates values as a result of the parse (see
_attr_gen_), it can place them into other implementations of optional, if you
tell it to do so. You tell it a template is usable as an optional by
specializing the `enable_optional` template. For instance, here is how you
would tell _Parser_ that `boost::optional` is an optional-type:
_Parser_ uses `std::optional` and `std::variant` internally. There is no way
to change this. However, when _Parser_ generates values as a result of the
parse (see _attr_gen_), it can place them into other implementations of
optional and/or variant, if you tell it to do so. You tell it which templates
are usable as an optional or variant by specializing the associated variable
template. For instance, here is how you would tell _Parser_ that
`boost::optional` is an optional-type:
template<typename T>
constexpr bool boost::parser::enable_optional<boost::optional<T>> = true;
Here's how you would do the same thing for `boost::variant2::variant`:
template<typename... Ts>
constexpr bool boost::parser::enable_variant<boost::variant2::variant<Ts...>> = true;
The requirements on a template used as an optional are pretty simple, since
_Parser_ does almost nothing but assign to them. For a type `O` to be a
usable optional, you must be able to assign to `O`, and `O` must have an
`operator*` that returns the stored value, or a (possibly cv-qualified)
reference to the stored value.
For variants, the requirement is even simpler; the variant type only needs to
be assignable.
[note The only thing affected by `enable_variant` is printing. If your
variant template can be printed with just `std::cout << v` (where `v` is a
variant, obviously), then you don't need to define `enable_variant` for your
variant template.]
_Parser_ uses `std::ranges::subrange` extensively. However, there is no C++17
equivalent. So, there is a `boost::parser::subrange` for C++17 builds. To
switch between these transparently in the code, while keeping CTAD
operational, _Parser_ defines _SUBRNG_. This is the name of the template, so
if you use it in your own code you would use it like `_SUBRNG_<I>` to
instantiate it.
[endsect]
[section This Library's Relationship to Boost.Spirit]

View File

@@ -36,6 +36,7 @@
[import ../example/json.cpp]
[import ../example/callback_json.cpp]
[import ../example/parsing_into_a_struct.cpp]
[import ../example/parsing_into_a_class.cpp]
[import ../example/struct_rule.cpp]
[import ../example/user_error_handler.cpp]
@@ -76,6 +77,22 @@
[def _cb_eh_ [classref boost::parser::callback_error_handler `callback_error_handler`]]
[def _rethrow_eh_ [classref boost::parser::rethrow_error_handler `rethrow_error_handler`]]
[def _trace_ [enumref boost::parser::trace `boost::parser::trace`]]
[def _search_ [funcref boost::parser::search `boost::parser::search()`]]
[def _search_all_ [globalref boost::parser::search_all `boost::parser::search_all`]]
[def _search_all_v_ [classref boost::parser::search_all_view `boost::parser::search_all_view`]]
[def _search_all_vs_ [classref boost::parser::search_all_view `boost::parser::search_all_view`s]]
[def _split_ [globalref boost::parser::split `boost::parser::split`]]
[def _split_v_ [classref boost::parser::split_view `boost::parser::split_view`]]
[def _split_vs_ [classref boost::parser::split_view `boost::parser::split_view`s]]
[def _replace_ [globalref boost::parser::replace `boost::parser::replace`]]
[def _replace_v_ [classref boost::parser::replace_view `boost::parser::replace_view`]]
[def _replace_vs_ [classref boost::parser::replace_view `boost::parser::replace_view`s]]
[def _trans_replace_ [globalref boost::parser::transform_replace `boost::parser::transform_replace`]]
[def _trans_replace_v_ [classref boost::parser::transform_replace_view `boost::parser::transform_replace_view`]]
[def _trans_replace_vs_ [classref boost::parser::transform_replace_view `boost::parser::transform_replace_view`s]]
[def _std_str_ `std::string`]
[def _std_vec_char_ `std::vector<char>`]
@@ -109,9 +126,9 @@
[def _locals_np_ [funcref boost::parser::_locals `_locals`]]
[def _params_np_ [funcref boost::parser::_params `_params`]]
[def _RULE_ [macroref BOOST_PARSER_DEFINE_RULE `BOOST_PARSER_DEFINE_RULE`]]
[def _RULES_ [macroref BOOST_PARSER_DEFINE_RULES `BOOST_PARSER_DEFINE_RULES`]]
[def _AGGR_SIZE_ [macroref BOOST_PARSER_MAX_AGGREGATE_SIZE `BOOST_PARSER_MAX_AGGREGATE_SIZE`]]
[def _SUBRNG_ [macroref BOOST_PARSER_SUBRANGE `BOOST_PARSER_SUBRANGE`]]
[def __p_ [globalref boost::parser::_p `_p`]]
@@ -175,18 +192,13 @@
[def _merge_np_ [globalref boost::parser::merge `merge`]]
[def _sep_np_ [globalref boost::parser::separate `separate`]]
[def _alnum_ [globalref boost::parser::ascii::alnum `ascii::alnum`]]
[def _alpha_ [globalref boost::parser::ascii::alpha `ascii::alpha`]]
[def _blank_ [globalref boost::parser::ascii::blank `ascii::blank`]]
[def _cntrl_ [globalref boost::parser::ascii::cntrl `ascii::cntrl`]]
[def _digit_ [globalref boost::parser::ascii::digit `ascii::digit`]]
[def _graph_ [globalref boost::parser::ascii::graph `ascii::graph`]]
[def _print_ [globalref boost::parser::ascii::print `ascii::print`]]
[def _punct_ [globalref boost::parser::ascii::punct `ascii::punct`]]
[def _space_ [globalref boost::parser::ascii::space `ascii::space`]]
[def _xdigit_ [globalref boost::parser::ascii::xdigit `ascii::xdigit`]]
[def _lower_ [globalref boost::parser::ascii::lower `ascii::lower`]]
[def _upper_ [globalref boost::parser::ascii::upper `ascii::upper`]]
[def _blank_ [globalref boost::parser::blank `blank`]]
[def _control_ [globalref boost::parser::control `control`]]
[def _digit_ [globalref boost::parser::digit `digit`]]
[def _punct_ [globalref boost::parser::punct `punct`]]
[def _hex_digit_ [globalref boost::parser::hex_digit `hex_digit`]]
[def _lower_ [globalref boost::parser::lower `lower`]]
[def _upper_ [globalref boost::parser::upper `upper`]]
[def _RES_ ['[^RESOLVE]]`()`]
[def _RES_np_ ['[^RESOLVE]]]
@@ -196,7 +208,7 @@
[def _p_api_ [link boost_parser__proposed_.tutorial.the__parse____api The `parse()` API]]
[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__struct_s Parsing `struct`s]]
[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 _attr_gen_ [link boost_parser__proposed_.tutorial.attribute_generation Attribute Generation]]
[def _directives_ [link boost_parser__proposed_.tutorial.directives Directives]]

View File

@@ -275,4 +275,54 @@ attribute generated is different from my expressed intent, that's a problem.
For this not to be a problem, I need to be able to understand the rules, so I
can express my intent, and not be surprised.
[heading Out-parameter attributes passed to _p_ are cleared on parse failure]
At the end of a call to any of the _p_ overloads that takes an attribute
out-param (including variants like _cbp_, etc.), the parse either succeeds or
fails. If the call fails, the attribute is explicitly "cleared" by assigning
its default-constructed value.
This is done because it's the less bad of two options. Consider the other
option first.
// Without explicit clearing.
namespace bp = boost::parser;
std::vector<int> result;
auto b = bp::parse("3 4 c", +bp::int_, bp::ws, result);
assert(!b);
assert(result == std::vector<int>({3, 4}));
This is odd _emdash_ the parse failed, but the out-param has partial results
in it anyway. This happens because the parser `+bp::int_` only fails if it
cannot match at `bp::int_` at least once. Above, it matches it twice, meaning
that it succeeds (if it had failed, it would have cleared its attribute). It
does not know that there is nothing after it that could continue the parse,
nor that it is being used in to do a full parse. So, the over-all parse
fails, but the part of the parse that fills in the out-param attribute does
not know do clear its attribute.
This is why the explicit clearing behavior happens at the end of _p_. This is
not without its downsides, though. Consider this.
// With explicit clearing.
namespace bp = boost::parser;
std::string str = "-42";
int i = 3;
bool b = parse(str, bp::uint_, i);
assert(!b);
assert(i == 0);
Here, the explicit clearing replaces the previous value of `3`, even though
the parser never touched the value! Destroying users' variables' state
without need may seem like a bad idea, but consider the alternative _emdash_
In the previous example, we had spurious values left in the out-param
attribute. Here, without clearing, we would have had a value left in the
out-param attribute, not because it was a partial result of the parse, but
because the parse never touched it. This is certain to be confusing, or at
least surprising, behavior. I deemed it better to make the failed parse case
consistent, to reduce confusion. The out-param attribute of type `A` is
always equal to `A()` if the parser fails. It is equal to whatever the parser
sets it to _emdash_ or its previous value, if the parser does not mutate it
_emdash_ if the parse succeeds.
[endsect]

File diff suppressed because it is too large Load Diff

View File

@@ -25,6 +25,7 @@ add_sample(semantic_actions)
add_sample(rule_intro)
add_sample(struct_rule)
add_sample(parsing_into_a_struct)
add_sample(parsing_into_a_class)
add_sample(roman_numerals)
add_sample(user_error_handler)

View File

@@ -172,13 +172,13 @@ namespace json {
}
};
auto const number_def = bp::raw
[bp::lexeme
[-bp::char_('-') >>
(bp::char_('1', '9') >> *bp::ascii::digit | bp::char_('0')) >>
-(bp::char_('.') >> +bp::ascii::digit) >>
-(bp::char_("eE") >> -bp::char_("+-") >> +bp::ascii::digit)]]
[parse_double];
auto const number_def =
bp::raw[bp::lexeme
[-bp::char_('-') >>
(bp::char_('1', '9') >> *bp::digit | bp::char_('0')) >>
-(bp::char_('.') >> +bp::digit) >>
-(bp::char_("eE") >> -bp::char_("+-") >> +bp::digit)]]
[parse_double];
// The object_element_key parser is exactly the same as the string parser.
// Note that we did *not* use string here, though; we used string_def. If

View File

@@ -227,13 +227,13 @@ namespace json {
// As indicated above, we want to match the specific formats JSON allows,
// and then re-parse the resulting matched range within the semantic
// action.
auto const number_def = bp::raw
[bp::lexeme
[-bp::char_('-') >>
(bp::char_('1', '9') >> *bp::ascii::digit | bp::char_('0')) >>
-(bp::char_('.') >> +bp::ascii::digit) >>
-(bp::char_("eE") >> -bp::char_("+-") >> +bp::ascii::digit)]]
[parse_double];
auto const number_def =
bp::raw[bp::lexeme
[-bp::char_('-') >>
(bp::char_('1', '9') >> *bp::digit | bp::char_('0')) >>
-(bp::char_('.') >> +bp::digit) >>
-(bp::char_("eE") >> -bp::char_("+-") >> +bp::digit)]]
[parse_double];
// Note how, in the next three parsers, we turn off backtracking by using
// > instead of >>, once we know that there is no backtracking alternative

View File

@@ -0,0 +1,48 @@
// 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)
//[ parsing_into_a_class_example
#include <boost/parser/parser.hpp>
#include <iostream>
#include <string>
namespace bp = boost::parser;
int main()
{
std::cout << "Enter a string followed by two unsigned integers. ";
std::string input;
std::getline(std::cin, input);
//[ parsing_into_a_class_str
constexpr auto string_uint_uint =
bp::lexeme[+(bp::char_ - ' ')] >> bp::uint_ >> bp::uint_;
std::string string_from_parse;
if (parse(input, string_uint_uint, bp::ws, string_from_parse))
std::cout << "That yields this string: " << string_from_parse << "\n";
else
std::cout << "Parse failure.\n";
//]
std::cout << "Enter an unsigned integer followed by a string. ";
std::getline(std::cin, input);
std::cout << input << "\n";
//[ parsing_into_a_class_vec_of_strs
constexpr auto uint_string = bp::uint_ >> bp::char_ >> bp::char_;
std::vector<std::string> vector_from_parse;
if (parse(input, uint_string, bp::ws, vector_from_parse)) {
std::cout << "That yields this vector of strings:\n";
for (auto && str : vector_from_parse) {
std::cout << " '" << str << "'\n";
}
} else {
std::cout << "Parse failure.\n";
}
//]
}
//]

View File

@@ -22,7 +22,7 @@ bp::rule<struct doubles, std::vector<double>> doubles = "doubles";
auto const doubles_def = bp::double_ % ',';
//]
//[ rule_intro_rule_definition_macro
BOOST_PARSER_DEFINE_RULE(doubles);
BOOST_PARSER_DEFINE_RULES(doubles);
//]
//]

View File

@@ -19,7 +19,7 @@ namespace boost { namespace parser {
//[ all_concepts
template<typename T>
concept parsable_code_unit =
concept code_unit =
std::same_as<std::remove_cv_t<T>, char> ||
std::same_as<std::remove_cv_t<T>, wchar_t> ||
std::same_as<std::remove_cv_t<T>, char8_t> ||
@@ -28,21 +28,24 @@ namespace boost { namespace parser {
template<typename T>
concept parsable_iter =
std::forward_iterator<T> && parsable_code_unit<std::iter_value_t<T>>;
std::forward_iterator<T> && code_unit<std::iter_value_t<T>>;
//[ parsable_range_like_concept
template<typename T>
concept parsable_range = std::ranges::forward_range<T> &&
parsable_code_unit<std::ranges::range_value_t<T>>;
code_unit<std::ranges::range_value_t<T>>;
template<typename T>
concept parsable_pointer = std::is_pointer_v<std::remove_cvref_t<T>> &&
parsable_code_unit<std::remove_pointer_t<std::remove_cvref_t<T>>>;
code_unit<std::remove_pointer_t<std::remove_cvref_t<T>>>;
template<typename T>
concept parsable_range_like = parsable_range<T> || parsable_pointer<T>;
//]
template<typename T>
concept range_like = std::ranges::range<T> || parsable_pointer<T>;
template<
typename I,
typename S,

View File

@@ -62,6 +62,12 @@
#endif
#if defined(__cpp_lib_constexpr_algorithms)
# define BOOST_PARSER_ALGO_CONSTEXPR constexpr
#else
# define BOOST_PARSER_ALGO_CONSTEXPR
#endif
#if defined(__cpp_lib_concepts) && !defined(BOOST_PARSER_DISABLE_CONCEPTS)
# define BOOST_PARSER_USE_CONCEPTS 1
#else

View File

@@ -201,6 +201,48 @@ namespace boost { namespace parser { namespace detail {
std::ostream & os,
int components = 0);
template<typename Context>
void print_parser(
Context const & context,
digit_parser const & parser,
std::ostream & os,
int components = 0);
template<typename Context>
void print_parser(
Context const & context,
char_subrange_parser<hex_digit_subranges> const & parser,
std::ostream & os,
int components = 0);
template<typename Context>
void print_parser(
Context const & context,
char_subrange_parser<control_subranges> const & parser,
std::ostream & os,
int components = 0);
template<typename Context>
void print_parser(
Context const & context,
char_set_parser<punct_chars> const & parser,
std::ostream & os,
int components = 0);
template<typename Context>
void print_parser(
Context const & context,
char_set_parser<lower_case_chars> const & parser,
std::ostream & os,
int components = 0);
template<typename Context>
void print_parser(
Context const & context,
char_set_parser<upper_case_chars> const & parser,
std::ostream & os,
int components = 0);
template<typename Context, typename Expected, typename AttributeType>
void print_parser(
Context const & context,
@@ -222,17 +264,17 @@ namespace boost { namespace parser { namespace detail {
std::ostream & os,
int components = 0);
template<typename Context>
template<typename Context, typename I, typename S>
void print_parser(
Context const & context,
ws_parser<true> const & parser,
quoted_string_parser<I, S> const & parser,
std::ostream & os,
int components = 0);
template<typename Context>
template<typename Context, bool NewlinesOnly, bool NoNewlines>
void print_parser(
Context const & context,
ws_parser<false> const & parser,
ws_parser<NewlinesOnly, NoNewlines> const & parser,
std::ostream & os,
int components = 0);
@@ -467,9 +509,7 @@ namespace boost { namespace parser { namespace detail {
};
template<typename T>
constexpr bool is_variant_v = false;
template<typename... Ts>
constexpr bool is_variant_v<std::variant<Ts...>> = true;
constexpr bool is_variant_v = enable_variant<T>;
template<typename Attribute>
inline void print(std::ostream & os, Attribute const & attr)

View File

@@ -372,7 +372,7 @@ namespace boost { namespace parser { namespace detail {
std::ostream & os,
int components)
{
os << parser.name_;
os << parser.diagnostic_text_;
if constexpr (!is_nope_v<ParamsTuple>) {
os << ".with(";
int i = 0;
@@ -439,8 +439,7 @@ namespace boost { namespace parser { namespace detail {
template<
typename Context,
typename ResolvedExpected,
bool Integral = std::is_integral<ResolvedExpected>{},
int SizeofExpected = sizeof(ResolvedExpected)>
bool Integral = std::is_integral<ResolvedExpected>{}>
struct print_expected_char_impl
{
static void call(
@@ -452,13 +451,17 @@ namespace boost { namespace parser { namespace detail {
}
};
template<typename Context, typename Expected>
struct print_expected_char_impl<Context, Expected, true, 4>
template<typename Context>
struct print_expected_char_impl<Context, char32_t, true>
{
static void
call(Context const & context, std::ostream & os, Expected expected)
call(Context const & context, std::ostream & os, char32_t expected)
{
std::array<char32_t, 1> cps = {{(char32_t)expected}};
if (expected == '\'') {
os << "'\\''";
return;
}
std::array<char32_t, 1> cps = {{expected}};
auto const r = cps | text::as_utf8;
os << "'";
for (auto c : r) {
@@ -500,13 +503,13 @@ namespace boost { namespace parser { namespace detail {
}
};
template<typename Context, typename Iter, typename Sentinel>
struct char_print_parser_impl<Context, char_range<Iter, Sentinel>>
template<typename Context, typename Iter, typename Sentinel, bool B>
struct char_print_parser_impl<Context, char_range<Iter, Sentinel, B>>
{
static void call(
Context const & context,
std::ostream & os,
char_range<Iter, Sentinel> expected)
char_range<Iter, Sentinel, B> expected)
{
os << "\"";
auto const r = expected.chars_ | text::as_utf8;
@@ -524,70 +527,80 @@ namespace boost { namespace parser { namespace detail {
std::ostream & os,
int components)
{
if (std::is_same_v<
Expected,
ascii_char_class<ascii_char_class_t::alnum>>) {
os << "ascii::alnum";
} else if (std::is_same_v<
Expected,
ascii_char_class<ascii_char_class_t::alpha>>) {
os << "ascii::alpha";
} else if (std::is_same_v<
Expected,
ascii_char_class<ascii_char_class_t::blank>>) {
os << "ascii::blank";
} else if (std::is_same_v<
Expected,
ascii_char_class<ascii_char_class_t::cntrl>>) {
os << "ascii::cntrl";
} else if (std::is_same_v<
Expected,
ascii_char_class<ascii_char_class_t::digit>>) {
os << "ascii::digit";
} else if (std::is_same_v<
Expected,
ascii_char_class<ascii_char_class_t::graph>>) {
os << "ascii::graph";
} else if (std::is_same_v<
Expected,
ascii_char_class<ascii_char_class_t::print>>) {
os << "ascii::print";
} else if (std::is_same_v<
Expected,
ascii_char_class<ascii_char_class_t::punct>>) {
os << "ascii::punct";
} else if (std::is_same_v<
Expected,
ascii_char_class<ascii_char_class_t::space>>) {
os << "ascii::space";
} else if (std::is_same_v<
Expected,
ascii_char_class<ascii_char_class_t::xdigit>>) {
os << "ascii::xdigit";
} else if (std::is_same_v<
Expected,
ascii_char_class<ascii_char_class_t::lower>>) {
os << "ascii::lower";
} else if (std::is_same_v<
Expected,
ascii_char_class<ascii_char_class_t::upper>>) {
os << "ascii::upper";
} else {
if (std::is_same_v<AttributeType, uint32_t>)
os << "cp";
else if (std::is_same_v<AttributeType, char>)
os << "cu";
else
os << "char_";
if constexpr (!is_nope_v<Expected>) {
os << "(";
char_print_parser_impl<Context, Expected>::call(
context, os, parser.expected_);
os << ")";
}
if (std::is_same_v<AttributeType, uint32_t>)
os << "cp";
else if (std::is_same_v<AttributeType, char>)
os << "cu";
else
os << "char_";
if constexpr (!is_nope_v<Expected>) {
os << "(";
char_print_parser_impl<Context, Expected>::call(
context, os, parser.expected_);
os << ")";
}
}
template<typename Context>
void print_parser(
Context const & context,
digit_parser const & parser,
std::ostream & os,
int components)
{
os << "digit";
}
template<typename Context>
void print_parser(
Context const & context,
char_subrange_parser<hex_digit_subranges> const & parser,
std::ostream & os,
int components)
{
os << "hex_digit";
}
template<typename Context>
void print_parser(
Context const & context,
char_subrange_parser<control_subranges> const & parser,
std::ostream & os,
int components)
{
os << "control";
}
template<typename Context>
void print_parser(
Context const & context,
char_set_parser<punct_chars> const & parser,
std::ostream & os,
int components)
{
os << "punct";
}
template<typename Context>
void print_parser(
Context const & context,
char_set_parser<lower_case_chars> const & parser,
std::ostream & os,
int components)
{
os << "lower";
}
template<typename Context>
void print_parser(
Context const & context,
char_set_parser<upper_case_chars> const & parser,
std::ostream & os,
int components)
{
os << "upper";
}
template<typename Context, typename Expected, typename AttributeType>
void print_parser(
Context const & context,
@@ -636,24 +649,40 @@ namespace boost { namespace parser { namespace detail {
os << "\"";
}
template<typename Context>
template<typename Context, typename I, typename S>
void print_parser(
Context const & context,
ws_parser<true> const & parser,
quoted_string_parser<I, S> const & parser,
std::ostream & os,
int components)
{
os << "eol";
os << "quoted_string(";
if (parser.chs_.empty()) {
detail::print_expected_char_impl<Context, char32_t>::call(
context, os, parser.ch_);
} else {
os << '"';
for (auto c : parser.chs_ | text::as_utf8) {
detail::print_char(os, c);
}
os << '"';
}
os << ')';
}
template<typename Context>
template<typename Context, bool NewlinesOnly, bool NoNewlines>
void print_parser(
Context const & context,
ws_parser<false> const & parser,
ws_parser<NewlinesOnly, NoNewlines> const & parser,
std::ostream & os,
int components)
{
os << "ws";
if constexpr (NoNewlines)
os << "blank";
else if constexpr (NewlinesOnly)
os << "eol";
else
os << "ws";
}
template<typename Context>

View File

@@ -14,8 +14,7 @@
#else
// This is now hard-coded to use the pre-C++20 code path. There are a bunch
// of really odd compile errorswith Clang+libstdc++ I can't be bothered to
// address right now. (The latest version of Boost.Text might fix these
// errors, but there's also no pre-C++20 code path in that version of Text.)
// address right now.
# define BOOST_PARSER_DETAIL_STL_INTERFACES_USE_CONCEPTS 0
#endif

View File

@@ -242,11 +242,24 @@ namespace boost::parser::detail { namespace stl_interfaces {
typename Enable =
std::enable_if_t<detail::is_invocable_v<F const &, T>>>
#endif
constexpr decltype(auto) operator()(T && t) const
constexpr decltype(auto) operator()(T && t) const &
{
return f_((T &&) t);
}
#if BOOST_PARSER_DETAIL_STL_INTERFACES_USE_CONCEPTS
template<typename T>
requires std::invocable<F &&, T>
#else
template<
typename T,
typename Enable = std::enable_if_t<detail::is_invocable_v<F &&, T>>>
#endif
constexpr decltype(auto) operator()(T && t) &&
{
return std::move(f_)((T &&) t);
}
private:
F f_;
};
@@ -295,7 +308,7 @@ namespace boost::parser::detail { namespace stl_interfaces {
{
#if BOOST_PARSER_DETAIL_STL_INTERFACES_USE_CONCEPTS
if constexpr (std::is_invocable_v<F const &, Args...>) {
return f((Args &&) args...);
return f_((Args &&) args...);
} else {
return closure(
stl_interfaces::bind_back(f_, (Args &&) args...));

View File

@@ -325,6 +325,8 @@ namespace boost::parser::detail { namespace text {
if (detail::next(first2) == last2) {
auto const it = parser::detail::text::find(first1, last1, *first2);
if (it == last1)
return {it, it};
return {it, detail::next(it)};
}

View File

@@ -12,7 +12,7 @@
#include <iterator>
#if !BOOST_PARSER_USE_CONCEPTS || defined(_MSC_VER)
#if !BOOST_PARSER_USE_CONCEPTS
# define BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS 0
#else
# define BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS 1
@@ -20,7 +20,8 @@
// GCC 12 claims to support 201907L <= __cpp_deduction_guides, but does not.
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS && defined(__cpp_deduction_guides) && \
201907L <= __cpp_deduction_guides && (!defined(__GNUC__) || 13 <= __GNUC__)
201907L <= __cpp_deduction_guides && (!defined(__GNUC__) || 13 <= __GNUC__) && \
!defined(_MSC_VER)
#define BOOST_PARSER_DETAIL_TEXT_USE_ALIAS_CTAD 1
#else
#define BOOST_PARSER_DETAIL_TEXT_USE_ALIAS_CTAD 0

View File

@@ -0,0 +1,145 @@
// 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)
#ifndef BOOST_PARSER_DETAIL_TEXT_DETAIL_ALL_T_HPP
#define BOOST_PARSER_DETAIL_TEXT_DETAIL_ALL_T_HPP
#include <boost/parser/detail/stl_interfaces/view_interface.hpp>
#include <boost/parser/detail/text/detail/begin_end.hpp>
#include <boost/parser/detail/detection.hpp>
#include <array>
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS
#include <ranges>
#endif
namespace boost::parser::detail::text::detail {
template<typename T>
using iterator_ = decltype(text::detail::begin(std::declval<T &>()));
template<typename T>
using sentinel_ = decltype(text::detail::end(std::declval<T &>()));
template<typename T>
constexpr bool range_ =
is_detected_v<iterator_, T> && is_detected_v<sentinel_, T>;
template<typename T>
using has_insert_ = decltype(std::declval<T &>().insert(
std::declval<T>().begin(), *std::declval<T>().begin()));
template<typename T>
constexpr bool container_ = is_detected_v<has_insert_, T>;
template<typename T>
constexpr bool is_std_array_v = false;
template<typename T, size_t N>
constexpr bool is_std_array_v<std::array<T, N>> = false;
template<typename R>
constexpr bool view =
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS || \
(defined(__cpp_lib_concepts) && (!defined(__GNUC__) || 12 <= __GNUC__))
std::ranges::view<R>
#else
range_<R> && !container_<R> &&
!std::is_array_v<std::remove_reference_t<R>> &&
!is_std_array_v<std::remove_reference_t<R>>
#endif
;
template<
typename R,
typename Enable = std::enable_if_t<range_<R> && std::is_object_v<R>>>
struct ref_view : stl_interfaces::view_interface<ref_view<R>>
{
private:
static void rvalue_poison(R &);
static void rvalue_poison(R &&) = delete;
public:
template<
typename T,
typename Enable2 = std::enable_if_t<
!std::
is_same_v<remove_cv_ref_t<T>, remove_cv_ref_t<ref_view>> &&
std::is_convertible_v<T, R &>>,
typename Enable3 = decltype(rvalue_poison(std::declval<T>()))>
constexpr ref_view(T && t) :
r_(std::addressof(static_cast<R &>((T &&) t)))
{}
constexpr R & base() const { return *r_; }
constexpr iterator_<R> begin() const
{
return text::detail::begin(*r_);
}
constexpr sentinel_<R> end() const { return text::detail::end(*r_); }
private:
R * r_;
};
template<typename R>
ref_view(R &) -> ref_view<R>;
template<typename R>
struct owning_view : stl_interfaces::view_interface<owning_view<R>>
{
owning_view() = default;
constexpr owning_view(R && t) : r_(std::move(t)) {}
owning_view(owning_view &&) = default;
owning_view & operator=(owning_view &&) = default;
constexpr R & base() & noexcept { return r_; }
constexpr const R & base() const & noexcept { return r_; }
constexpr R && base() && noexcept { return std::move(r_); }
constexpr const R && base() const && noexcept { return std::move(r_); }
constexpr iterator_<R> begin() { return text::detail::begin(r_); }
constexpr sentinel_<R> end() { return text::detail::end(r_); }
constexpr auto begin() const { return text::detail::begin(r_); }
constexpr auto end() const { return text::detail::end(r_); }
private:
R r_ = R();
};
template<typename T>
using can_ref_view_expr = decltype(ref_view(std::declval<T>()));
template<typename T>
constexpr bool can_ref_view = is_detected_v<can_ref_view_expr, T>;
struct all_impl
{
template<typename R, typename Enable = std::enable_if_t<range_<R>>>
[[nodiscard]] constexpr auto operator()(R && r) const
{
using T = remove_cv_ref_t<R>;
if constexpr (view<T>)
return (R &&) r;
else if constexpr (can_ref_view<R>)
return ref_view(r);
else
return owning_view<T>(std::move(r));
}
};
constexpr all_impl all;
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS && \
(!defined(__GNUC__) || 12 <= __GNUC__)
template<typename R>
using all_t = std::views::all_t<R>;
#else
template<typename R>
using all_t = decltype(all(std::declval<R>()));
#endif
}
#endif

View File

@@ -943,23 +943,23 @@ namespace boost::parser::detail { namespace text {
#else
template<typename I>
#endif
friend constexpr auto operator==(I it, null_sentinel_t)
friend constexpr bool operator==(I it, null_sentinel_t)
{
return *it == detail::iter_value_t<I>{};
}
#if !defined(__cpp_impl_three_way_comparison)
template<typename I>
friend constexpr auto operator==(null_sentinel_t, I it)
friend constexpr bool operator==(null_sentinel_t, I it)
{
return *it == detail::iter_value_t<I>{};
}
template<typename I>
friend constexpr auto operator!=(I it, null_sentinel_t)
friend constexpr bool operator!=(I it, null_sentinel_t)
{
return *it != detail::iter_value_t<I>{};
}
template<typename I>
friend constexpr auto operator!=(null_sentinel_t, I it)
friend constexpr bool operator!=(null_sentinel_t, I it)
{
return *it != detail::iter_value_t<I>{};
}

View File

@@ -8,6 +8,7 @@
#include <boost/parser/detail/text/transcode_algorithm.hpp>
#include <boost/parser/detail/text/transcode_iterator.hpp>
#include <boost/parser/detail/text/detail/all_t.hpp>
#include <boost/parser/detail/stl_interfaces/view_interface.hpp>
#include <boost/parser/detail/stl_interfaces/view_adaptor.hpp>
@@ -43,13 +44,27 @@ 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; }
@@ -264,7 +279,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<std::views::all_t<R>, F>;
project_view(R &&) -> project_view<detail::all_t<R>, F>;
#endif
namespace detail {
@@ -280,7 +295,7 @@ namespace boost::parser::detail { namespace text {
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS
template<class R>
requires std::ranges::viewable_range<R> &&
requires 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>>>
@@ -386,11 +401,11 @@ namespace boost::parser::detail { namespace text {
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS
template<class R>
char8_view(R &&) -> char8_view<std::views::all_t<R>>;
char8_view(R &&) -> char8_view<detail::all_t<R>>;
template<class R>
char16_view(R &&) -> char16_view<std::views::all_t<R>>;
char16_view(R &&) -> char16_view<detail::all_t<R>>;
template<class R>
char32_view(R &&) -> char32_view<std::views::all_t<R>>;
char32_view(R &&) -> char32_view<detail::all_t<R>>;
#endif
#endif
@@ -401,7 +416,7 @@ namespace boost::parser::detail { namespace text {
{
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS
template<class R>
requires (std::ranges::viewable_range<R> &&
requires (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>>
@@ -481,10 +496,8 @@ namespace boost::parser::detail { namespace text {
constexpr auto end() const { return code_units().end(); }
};
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS
template<class R>
unpacking_view(R &&) -> unpacking_view<std::views::all_t<R>>;
#endif
unpacking_view(R &&) -> unpacking_view<detail::all_t<R>>;
// clang-format on
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS
@@ -519,11 +532,9 @@ namespace boost::parser::detail { namespace text {
}
public:
constexpr utf_view()
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS
requires std::default_initializable<V>
constexpr utf_view() requires std::default_initializable<V> = default;
#endif
= default;
constexpr utf_view(V base) : base_{std::move(base)} {}
constexpr V base() const &
@@ -609,7 +620,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, std::views::all_t<R>>;
utf_view(R &&) -> utf_view<Format, detail::all_t<R>>;
template<class V>
using utf8_view = utf_view<format::utf8, V>;
@@ -675,13 +686,13 @@ namespace boost::parser::detail { namespace text {
{}
};
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS
#if !BOOST_PARSER_DETAIL_TEXT_USE_ALIAS_CTAD
template<class R>
utf8_view(R &&) -> utf8_view<std::views::all_t<R>>;
utf8_view(R &&) -> utf8_view<detail::all_t<R>>;
template<class R>
utf16_view(R &&) -> utf16_view<std::views::all_t<R>>;
utf16_view(R &&) -> utf16_view<detail::all_t<R>>;
template<class R>
utf32_view(R &&) -> utf32_view<std::views::all_t<R>>;
utf32_view(R &&) -> utf32_view<detail::all_t<R>>;
#endif
#endif
@@ -702,7 +713,7 @@ namespace boost::parser::detail { namespace text {
namespace detail {
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS
template<class R, template<class> class View>
concept can_utf_view = requires { View(std::declval<R>()); };
concept can_utf_view = requires(R && r) { View((R &&)r); };
#else
template<class R, class View>
using can_utf_view_expr = decltype(View(std::declval<R>()));
@@ -760,7 +771,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>> ||
(std::ranges::viewable_range<R> &&
(viewable_range<R> &&
can_utf_view<unpacked_range<R>, View>) ||
utf_pointer<std::remove_cvref_t<R>>
#else
@@ -800,12 +811,14 @@ namespace boost::parser::detail { namespace text {
}}
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS
#if defined(__cpp_lib_ranges)
namespace std::ranges {
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS
template<class V, auto F>
inline constexpr bool enable_borrowed_range<boost::parser::detail::text::project_view<V, F>> =
enable_borrowed_range<V>;
#endif
template<class V>
inline constexpr bool enable_borrowed_range<boost::parser::detail::text::unpacking_view<V>> =

View File

@@ -16,7 +16,7 @@
namespace boost::parser::detail { namespace text {
/** The Unicode Transformation Formats. */
enum class format { utf8 = 1, utf16 = 2, utf32 = 4 };
enum class format { none = 0, utf8 = 1, utf16 = 2, utf32 = 4 };
namespace detail {
template<typename T>

View File

@@ -0,0 +1,706 @@
// 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)
#ifndef BOOST_PARSER_UNICODE_CHAR_SETS_HPP
#define BOOST_PARSER_UNICODE_CHAR_SETS_HPP
#include <boost/parser/parser_fwd.hpp>
namespace boost::parser::detail {
// Produced from https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp,
// using "[:Pc:][:Pd:][:Pe:][:Pf:][:Pi:][:Ps:][:Po:]" for the Input field,
// using the categories found at
// https://www.fileformat.info/info/unicode/category/index.htm
template<>
struct char_set<punct_chars>
{
static constexpr uint32_t chars[] = {
0x21, 0x22, 0x23, 0x25, 0x26, 0x27, 0x28,
0x29, 0x2A, 0x5B, 0x5C, 0x5D, 0x5F, 0x7B,
0x7D, 0x2C, 0x2D, 0x2E, 0x2F, 0x3A, 0x3B,
0x3F, 0x40, 0xA1, 0xA7, 0xAB, 0xB6, 0xB7,
0xBB, 0xBF, 0x37E, 0x387, 0x55A, 0x55B, 0x55C,
0x55D, 0x55E, 0x55F, 0x589, 0x58A, 0x5BE, 0x5C0,
0x5C3, 0x5C6, 0x5F3, 0x5F4, 0x609, 0x60A, 0x60C,
0x60D, 0x61B, 0x61D, 0x61E, 0x61F, 0x66A, 0x66B,
0x66C, 0x66D, 0x6D4, 0x700, 0x701, 0x702, 0x703,
0x704, 0x705, 0x706, 0x707, 0x708, 0x709, 0x70A,
0x70B, 0x70C, 0x70D, 0x7F7, 0x7F8, 0x7F9, 0x830,
0x831, 0x832, 0x833, 0x834, 0x835, 0x836, 0x837,
0x838, 0x839, 0x83A, 0x83B, 0x83C, 0x83D, 0x83E,
0x85E, 0x964, 0x965, 0x970, 0x9FD, 0xA76, 0xAF0,
0xC77, 0xC84, 0xDF4, 0xE4F, 0xE5A, 0xE5B, 0xF04,
0xF05, 0xF06, 0xF07, 0xFD3, 0xFD4, 0xF08, 0xF09,
0xF0A, 0xF0B, 0xF0C, 0xF0D, 0xF0E, 0xF0F, 0xF10,
0xF11, 0xF12, 0xF14, 0xF85, 0xFD0, 0xFD1, 0xFD2,
0xF3A, 0xF3B, 0xF3C, 0xF3D, 0xFD9, 0xFDA, 0x104A,
0x104B, 0x104C, 0x104D, 0x104E, 0x104F, 0x10FB, 0x1360,
0x1361, 0x1362, 0x1363, 0x1364, 0x1365, 0x1366, 0x1367,
0x1368, 0x1400, 0x166E, 0x169B, 0x169C, 0x16EB, 0x16EC,
0x16ED, 0x1735, 0x1736, 0x17D4, 0x17D5, 0x17D6, 0x17D8,
0x17D9, 0x17DA, 0x1800, 0x1801, 0x1802, 0x1803, 0x1804,
0x1805, 0x1806, 0x1807, 0x1808, 0x1809, 0x180A, 0x1944,
0x1945, 0x1A1E, 0x1A1F, 0x1AA0, 0x1AA1, 0x1AA2, 0x1AA3,
0x1AA4, 0x1AA5, 0x1AA6, 0x1AA8, 0x1AA9, 0x1AAA, 0x1AAB,
0x1AAC, 0x1AAD, 0x1B5A, 0x1B5B, 0x1B5C, 0x1B5D, 0x1B5E,
0x1B5F, 0x1B60, 0x1B7D, 0x1B7E, 0x1BFC, 0x1BFD, 0x1BFE,
0x1BFF, 0x1C3B, 0x1C3C, 0x1C3D, 0x1C3E, 0x1C3F, 0x1C7E,
0x1C7F, 0x1CC0, 0x1CC1, 0x1CC2, 0x1CC3, 0x1CC4, 0x1CC5,
0x1CC6, 0x1CC7, 0x1CD3, 0x2010, 0x2011, 0x2012, 0x2013,
0x2014, 0x2015, 0x2016, 0x2017, 0x2020, 0x2021, 0x2022,
0x2023, 0x2024, 0x2025, 0x2026, 0x2027, 0x2030, 0x2031,
0x2032, 0x2033, 0x2034, 0x2035, 0x2036, 0x2037, 0x2038,
0x203B, 0x203D, 0x203E, 0x203F, 0x2040, 0x2041, 0x2042,
0x2043, 0x204A, 0x204B, 0x204C, 0x204D, 0x204E, 0x204F,
0x2050, 0x2051, 0x2053, 0x2054, 0x2055, 0x2057, 0x2018,
0x2019, 0x201A, 0x201B, 0x201C, 0x201D, 0x201E, 0x201F,
0x2039, 0x203A, 0x203C, 0x2047, 0x2048, 0x2049, 0x2045,
0x2046, 0x2056, 0x2058, 0x2059, 0x205A, 0x205B, 0x205C,
0x205D, 0x205E, 0x207D, 0x207E, 0x208D, 0x208E, 0x2308,
0x2309, 0x230A, 0x230B, 0x2329, 0x232A, 0x2768, 0x2769,
0x276A, 0x276B, 0x276C, 0x276D, 0x276E, 0x276F, 0x2770,
0x2771, 0x2772, 0x2773, 0x2774, 0x2775, 0x27C5, 0x27C6,
0x27E6, 0x27E7, 0x27E8, 0x27E9, 0x27EA, 0x27EB, 0x27EC,
0x27ED, 0x27EE, 0x27EF, 0x2983, 0x2984, 0x2985, 0x2986,
0x2987, 0x2988, 0x2989, 0x298A, 0x298B, 0x298C, 0x2991,
0x2992, 0x2993, 0x2994, 0x2995, 0x2996, 0x2997, 0x2998,
0x29FC, 0x29FD, 0x298D, 0x298E, 0x298F, 0x2990, 0x29D8,
0x29D9, 0x29DA, 0x29DB, 0x2CF9, 0x2CFA, 0x2CFB, 0x2CFC,
0x2CFE, 0x2CFF, 0x2D70, 0x2E00, 0x2E01, 0x2E02, 0x2E03,
0x2E04, 0x2E05, 0x2E06, 0x2E07, 0x2E08, 0x2E09, 0x2E0A,
0x2E0B, 0x2E0C, 0x2E0D, 0x2E0E, 0x2E0F, 0x2E10, 0x2E11,
0x2E12, 0x2E13, 0x2E14, 0x2E15, 0x2E16, 0x2E17, 0x2E18,
0x2E19, 0x2E1A, 0x2E1B, 0x2E1E, 0x2E1F, 0x2E1C, 0x2E1D,
0x2E20, 0x2E21, 0x2E26, 0x2E27, 0x2E28, 0x2E29, 0x2E55,
0x2E56, 0x2E57, 0x2E58, 0x2E22, 0x2E23, 0x2E24, 0x2E25,
0x2E2A, 0x2E2B, 0x2E2C, 0x2E2D, 0x2E2E, 0x2E30, 0x2E31,
0x2E33, 0x2E34, 0x2E3F, 0x2E4A, 0x2E4B, 0x2E4C, 0x2E4D,
0x2E4E, 0x2E4F, 0x2E52, 0x2E53, 0x2E54, 0x2E32, 0x2E35,
0x2E36, 0x2E37, 0x2E38, 0x2E39, 0x2E3A, 0x2E3B, 0x2E3C,
0x2E3D, 0x2E3E, 0x2E40, 0x2E41, 0x2E42, 0x2E43, 0x2E44,
0x2E45, 0x2E46, 0x2E47, 0x2E48, 0x2E49, 0x2E59, 0x2E5A,
0x2E5B, 0x2E5C, 0x2E5D, 0x3001, 0x3002, 0x3003, 0x3008,
0x3009, 0x300A, 0x300B, 0x300C, 0x300D, 0x300E, 0x300F,
0x3010, 0x3011, 0x3014, 0x3015, 0x3016, 0x3017, 0x3018,
0x3019, 0x301A, 0x301B, 0x301C, 0x301D, 0x301E, 0x301F,
0x3030, 0x303D, 0x30A0, 0x30FB, 0xA4FE, 0xA4FF, 0xA60D,
0xA60E, 0xA60F, 0xA673, 0xA67E, 0xA6F2, 0xA6F3, 0xA6F4,
0xA6F5, 0xA6F6, 0xA6F7, 0xA874, 0xA875, 0xA876, 0xA877,
0xA8CE, 0xA8CF, 0xA8F8, 0xA8F9, 0xA8FA, 0xA8FC, 0xA92E,
0xA92F, 0xA95F, 0xA9C1, 0xA9C2, 0xA9C3, 0xA9C4, 0xA9C5,
0xA9C6, 0xA9C7, 0xA9C8, 0xA9C9, 0xA9CA, 0xA9CB, 0xA9CC,
0xA9CD, 0xA9DE, 0xA9DF, 0xAA5C, 0xAA5D, 0xAA5E, 0xAA5F,
0xAADE, 0xAADF, 0xAAF0, 0xAAF1, 0xABEB, 0xFD3E, 0xFD3F,
0xFE10, 0xFE11, 0xFE12, 0xFE13, 0xFE14, 0xFE15, 0xFE16,
0xFE17, 0xFE18, 0xFE19, 0xFE30, 0xFE31, 0xFE32, 0xFE33,
0xFE34, 0xFE35, 0xFE36, 0xFE37, 0xFE38, 0xFE39, 0xFE3A,
0xFE3B, 0xFE3C, 0xFE3D, 0xFE3E, 0xFE3F, 0xFE40, 0xFE41,
0xFE42, 0xFE43, 0xFE44, 0xFE47, 0xFE48, 0xFE45, 0xFE46,
0xFE49, 0xFE4A, 0xFE4B, 0xFE4C, 0xFE4D, 0xFE4E, 0xFE4F,
0xFE50, 0xFE51, 0xFE52, 0xFE54, 0xFE55, 0xFE56, 0xFE57,
0xFE58, 0xFE59, 0xFE5A, 0xFE5B, 0xFE5C, 0xFE5D, 0xFE5E,
0xFE5F, 0xFE60, 0xFE61, 0xFE63, 0xFE68, 0xFE6A, 0xFE6B,
0xFF01, 0xFF02, 0xFF03, 0xFF05, 0xFF06, 0xFF07, 0xFF08,
0xFF09, 0xFF0A, 0xFF0C, 0xFF0D, 0xFF0E, 0xFF0F, 0xFF1A,
0xFF1B, 0xFF1F, 0xFF20, 0xFF3B, 0xFF3C, 0xFF3D, 0xFF3F,
0xFF5B, 0xFF5D, 0xFF5F, 0xFF60, 0xFF61, 0xFF62, 0xFF63,
0xFF64, 0xFF65, 0x10100, 0x10101, 0x10102, 0x1039F, 0x103D0,
0x1056F, 0x10857, 0x1091F, 0x1093F, 0x10A50, 0x10A51, 0x10A52,
0x10A53, 0x10A54, 0x10A55, 0x10A56, 0x10A57, 0x10A58, 0x10A7F,
0x10AF0, 0x10AF1, 0x10AF2, 0x10AF3, 0x10AF4, 0x10AF5, 0x10AF6,
0x10B39, 0x10B3A, 0x10B3B, 0x10B3C, 0x10B3D, 0x10B3E, 0x10B3F,
0x10B99, 0x10B9A, 0x10B9B, 0x10B9C, 0x10EAD, 0x10F55, 0x10F56,
0x10F57, 0x10F58, 0x10F59, 0x10F86, 0x10F87, 0x10F88, 0x10F89,
0x11047, 0x11048, 0x11049, 0x1104A, 0x1104B, 0x1104C, 0x1104D,
0x110BB, 0x110BC, 0x110BE, 0x110BF, 0x110C0, 0x110C1, 0x11140,
0x11141, 0x11142, 0x11143, 0x11174, 0x11175, 0x111C5, 0x111C6,
0x111C7, 0x111C8, 0x111CD, 0x111DB, 0x111DD, 0x111DE, 0x111DF,
0x11238, 0x11239, 0x1123A, 0x1123B, 0x1123C, 0x1123D, 0x112A9,
0x1144B, 0x1144C, 0x1144D, 0x1144E, 0x1144F, 0x1145A, 0x1145B,
0x1145D, 0x114C6, 0x115C1, 0x115C2, 0x115C3, 0x115C4, 0x115C5,
0x115C6, 0x115C7, 0x115C8, 0x115C9, 0x115CA, 0x115CB, 0x115CC,
0x115CD, 0x115CE, 0x115CF, 0x115D0, 0x115D1, 0x115D2, 0x115D3,
0x115D4, 0x115D5, 0x115D6, 0x115D7, 0x11641, 0x11642, 0x11643,
0x11660, 0x11661, 0x11662, 0x11663, 0x11664, 0x11665, 0x11666,
0x11667, 0x11668, 0x11669, 0x1166A, 0x1166B, 0x1166C, 0x116B9,
0x1173C, 0x1173D, 0x1173E, 0x1183B, 0x11944, 0x11945, 0x11946,
0x119E2, 0x11A3F, 0x11A40, 0x11A45, 0x11A46, 0x11A41, 0x11A42,
0x11A43, 0x11A44, 0x11A9A, 0x11A9B, 0x11A9C, 0x11A9E, 0x11A9F,
0x11AA0, 0x11AA1, 0x11AA2, 0x11B00, 0x11B01, 0x11B02, 0x11B03,
0x11B04, 0x11B05, 0x11B06, 0x11B07, 0x11B08, 0x11B09, 0x11C41,
0x11C42, 0x11C43, 0x11C44, 0x11C45, 0x11C70, 0x11C71, 0x11EF7,
0x11EF8, 0x11F43, 0x11F44, 0x11F45, 0x11F46, 0x11F47, 0x11F48,
0x11F49, 0x11F4A, 0x11F4B, 0x11F4C, 0x11F4D, 0x11F4E, 0x11F4F,
0x11FFF, 0x12470, 0x12471, 0x12472, 0x12473, 0x12474, 0x12FF1,
0x12FF2, 0x16A6E, 0x16A6F, 0x16AF5, 0x16B37, 0x16B38, 0x16B39,
0x16B3A, 0x16B3B, 0x16B44, 0x16E97, 0x16E98, 0x16E99, 0x16E9A,
0x16FE2, 0x1BC9F, 0x1DA87, 0x1DA88, 0x1DA89, 0x1DA8A, 0x1DA8B,
0x1E95E, 0x1E95F};
};
// Produced from https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp,
// using "[:Ll:]" for the Input field, using the categories found at
// https://www.fileformat.info/info/unicode/category/index.htm
template<>
struct char_set<lower_case_chars>
{
static constexpr uint32_t chars[] = {
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70,
0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
0x79, 0x7A, 0xB5, 0xDF, 0xE0, 0xE1, 0xE2, 0xE3,
0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB,
0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3,
0xF4, 0xF5, 0xF6, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC,
0xFD, 0xFE, 0xFF, 0x101, 0x103, 0x105, 0x107, 0x109,
0x10B, 0x10D, 0x10F, 0x111, 0x113, 0x115, 0x117, 0x119,
0x11B, 0x11D, 0x11F, 0x121, 0x123, 0x125, 0x127, 0x129,
0x12B, 0x12D, 0x12F, 0x131, 0x133, 0x135, 0x137, 0x138,
0x13A, 0x13C, 0x13E, 0x140, 0x142, 0x144, 0x146, 0x148,
0x149, 0x14B, 0x14D, 0x14F, 0x151, 0x153, 0x155, 0x157,
0x159, 0x15B, 0x15D, 0x15F, 0x161, 0x163, 0x165, 0x167,
0x169, 0x16B, 0x16D, 0x16F, 0x171, 0x173, 0x175, 0x177,
0x17A, 0x17C, 0x17E, 0x17F, 0x180, 0x183, 0x185, 0x188,
0x18C, 0x18D, 0x192, 0x195, 0x199, 0x19A, 0x19B, 0x19E,
0x1A1, 0x1A3, 0x1A5, 0x1A8, 0x1AA, 0x1AB, 0x1AD, 0x1B0,
0x1B4, 0x1B6, 0x1B9, 0x1BA, 0x1BD, 0x1BE, 0x1BF, 0x1C6,
0x1C9, 0x1CC, 0x1CE, 0x1D0, 0x1D2, 0x1D4, 0x1D6, 0x1D8,
0x1DA, 0x1DC, 0x1DD, 0x1DF, 0x1E1, 0x1E3, 0x1E5, 0x1E7,
0x1E9, 0x1EB, 0x1ED, 0x1EF, 0x1F0, 0x1F3, 0x1F5, 0x1F9,
0x1FB, 0x1FD, 0x1FF, 0x201, 0x203, 0x205, 0x207, 0x209,
0x20B, 0x20D, 0x20F, 0x211, 0x213, 0x215, 0x217, 0x219,
0x21B, 0x21D, 0x21F, 0x221, 0x223, 0x225, 0x227, 0x229,
0x22B, 0x22D, 0x22F, 0x231, 0x233, 0x234, 0x235, 0x236,
0x237, 0x238, 0x239, 0x23C, 0x23F, 0x240, 0x242, 0x247,
0x249, 0x24B, 0x24D, 0x24F, 0x250, 0x251, 0x252, 0x253,
0x254, 0x255, 0x256, 0x257, 0x258, 0x259, 0x25A, 0x25B,
0x25C, 0x25D, 0x25E, 0x25F, 0x260, 0x261, 0x262, 0x263,
0x264, 0x265, 0x266, 0x267, 0x268, 0x269, 0x26A, 0x26B,
0x26C, 0x26D, 0x26E, 0x26F, 0x270, 0x271, 0x272, 0x273,
0x274, 0x275, 0x276, 0x277, 0x278, 0x279, 0x27A, 0x27B,
0x27C, 0x27D, 0x27E, 0x27F, 0x280, 0x281, 0x282, 0x283,
0x284, 0x285, 0x286, 0x287, 0x288, 0x289, 0x28A, 0x28B,
0x28C, 0x28D, 0x28E, 0x28F, 0x290, 0x291, 0x292, 0x293,
0x295, 0x296, 0x297, 0x298, 0x299, 0x29A, 0x29B, 0x29C,
0x29D, 0x29E, 0x29F, 0x2A0, 0x2A1, 0x2A2, 0x2A3, 0x2A4,
0x2A5, 0x2A6, 0x2A7, 0x2A8, 0x2A9, 0x2AA, 0x2AB, 0x2AC,
0x2AD, 0x2AE, 0x2AF, 0x371, 0x373, 0x377, 0x37B, 0x37C,
0x37D, 0x390, 0x3AC, 0x3AD, 0x3AE, 0x3AF, 0x3B0, 0x3B1,
0x3B2, 0x3B3, 0x3B4, 0x3B5, 0x3B6, 0x3B7, 0x3B8, 0x3B9,
0x3BA, 0x3BB, 0x3BC, 0x3BD, 0x3BE, 0x3BF, 0x3C0, 0x3C1,
0x3C2, 0x3C3, 0x3C4, 0x3C5, 0x3C6, 0x3C7, 0x3C8, 0x3C9,
0x3CA, 0x3CB, 0x3CC, 0x3CD, 0x3CE, 0x3D0, 0x3D1, 0x3D5,
0x3D6, 0x3D7, 0x3D9, 0x3DB, 0x3DD, 0x3DF, 0x3E1, 0x3E3,
0x3E5, 0x3E7, 0x3E9, 0x3EB, 0x3ED, 0x3EF, 0x3F0, 0x3F1,
0x3F2, 0x3F3, 0x3F5, 0x3F8, 0x3FB, 0x3FC, 0x430, 0x431,
0x432, 0x433, 0x434, 0x435, 0x436, 0x437, 0x438, 0x439,
0x43A, 0x43B, 0x43C, 0x43D, 0x43E, 0x43F, 0x440, 0x441,
0x442, 0x443, 0x444, 0x445, 0x446, 0x447, 0x448, 0x449,
0x44A, 0x44B, 0x44C, 0x44D, 0x44E, 0x44F, 0x450, 0x451,
0x452, 0x453, 0x454, 0x455, 0x456, 0x457, 0x458, 0x459,
0x45A, 0x45B, 0x45C, 0x45D, 0x45E, 0x45F, 0x461, 0x463,
0x465, 0x467, 0x469, 0x46B, 0x46D, 0x46F, 0x471, 0x473,
0x475, 0x477, 0x479, 0x47B, 0x47D, 0x47F, 0x481, 0x48B,
0x48D, 0x48F, 0x491, 0x493, 0x495, 0x497, 0x499, 0x49B,
0x49D, 0x49F, 0x4A1, 0x4A3, 0x4A5, 0x4A7, 0x4A9, 0x4AB,
0x4AD, 0x4AF, 0x4B1, 0x4B3, 0x4B5, 0x4B7, 0x4B9, 0x4BB,
0x4BD, 0x4BF, 0x4C2, 0x4C4, 0x4C6, 0x4C8, 0x4CA, 0x4CC,
0x4CE, 0x4CF, 0x4D1, 0x4D3, 0x4D5, 0x4D7, 0x4D9, 0x4DB,
0x4DD, 0x4DF, 0x4E1, 0x4E3, 0x4E5, 0x4E7, 0x4E9, 0x4EB,
0x4ED, 0x4EF, 0x4F1, 0x4F3, 0x4F5, 0x4F7, 0x4F9, 0x4FB,
0x4FD, 0x4FF, 0x501, 0x503, 0x505, 0x507, 0x509, 0x50B,
0x50D, 0x50F, 0x511, 0x513, 0x515, 0x517, 0x519, 0x51B,
0x51D, 0x51F, 0x521, 0x523, 0x525, 0x527, 0x529, 0x52B,
0x52D, 0x52F, 0x560, 0x561, 0x562, 0x563, 0x564, 0x565,
0x566, 0x567, 0x568, 0x569, 0x56A, 0x56B, 0x56C, 0x56D,
0x56E, 0x56F, 0x570, 0x571, 0x572, 0x573, 0x574, 0x575,
0x576, 0x577, 0x578, 0x579, 0x57A, 0x57B, 0x57C, 0x57D,
0x57E, 0x57F, 0x580, 0x581, 0x582, 0x583, 0x584, 0x585,
0x586, 0x587, 0x588, 0x10D0, 0x10D1, 0x10D2, 0x10D3, 0x10D4,
0x10D5, 0x10D6, 0x10D7, 0x10D8, 0x10D9, 0x10DA, 0x10DB, 0x10DC,
0x10DD, 0x10DE, 0x10DF, 0x10E0, 0x10E1, 0x10E2, 0x10E3, 0x10E4,
0x10E5, 0x10E6, 0x10E7, 0x10E8, 0x10E9, 0x10EA, 0x10EB, 0x10EC,
0x10ED, 0x10EE, 0x10EF, 0x10F0, 0x10F1, 0x10F2, 0x10F3, 0x10F4,
0x10F5, 0x10F6, 0x10F7, 0x10F8, 0x10F9, 0x10FA, 0x10FD, 0x10FE,
0x10FF, 0x13F8, 0x13F9, 0x13FA, 0x13FB, 0x13FC, 0x13FD, 0x1C80,
0x1C81, 0x1C82, 0x1C83, 0x1C84, 0x1C85, 0x1C86, 0x1C87, 0x1C88,
0x1D00, 0x1D01, 0x1D02, 0x1D03, 0x1D04, 0x1D05, 0x1D06, 0x1D07,
0x1D08, 0x1D09, 0x1D0A, 0x1D0B, 0x1D0C, 0x1D0D, 0x1D0E, 0x1D0F,
0x1D10, 0x1D11, 0x1D12, 0x1D13, 0x1D14, 0x1D15, 0x1D16, 0x1D17,
0x1D18, 0x1D19, 0x1D1A, 0x1D1B, 0x1D1C, 0x1D1D, 0x1D1E, 0x1D1F,
0x1D20, 0x1D21, 0x1D22, 0x1D23, 0x1D24, 0x1D25, 0x1D26, 0x1D27,
0x1D28, 0x1D29, 0x1D2A, 0x1D2B, 0x1D6B, 0x1D6C, 0x1D6D, 0x1D6E,
0x1D6F, 0x1D70, 0x1D71, 0x1D72, 0x1D73, 0x1D74, 0x1D75, 0x1D76,
0x1D77, 0x1D79, 0x1D7A, 0x1D7B, 0x1D7C, 0x1D7D, 0x1D7E, 0x1D7F,
0x1D80, 0x1D81, 0x1D82, 0x1D83, 0x1D84, 0x1D85, 0x1D86, 0x1D87,
0x1D88, 0x1D89, 0x1D8A, 0x1D8B, 0x1D8C, 0x1D8D, 0x1D8E, 0x1D8F,
0x1D90, 0x1D91, 0x1D92, 0x1D93, 0x1D94, 0x1D95, 0x1D96, 0x1D97,
0x1D98, 0x1D99, 0x1D9A, 0x1E01, 0x1E03, 0x1E05, 0x1E07, 0x1E09,
0x1E0B, 0x1E0D, 0x1E0F, 0x1E11, 0x1E13, 0x1E15, 0x1E17, 0x1E19,
0x1E1B, 0x1E1D, 0x1E1F, 0x1E21, 0x1E23, 0x1E25, 0x1E27, 0x1E29,
0x1E2B, 0x1E2D, 0x1E2F, 0x1E31, 0x1E33, 0x1E35, 0x1E37, 0x1E39,
0x1E3B, 0x1E3D, 0x1E3F, 0x1E41, 0x1E43, 0x1E45, 0x1E47, 0x1E49,
0x1E4B, 0x1E4D, 0x1E4F, 0x1E51, 0x1E53, 0x1E55, 0x1E57, 0x1E59,
0x1E5B, 0x1E5D, 0x1E5F, 0x1E61, 0x1E63, 0x1E65, 0x1E67, 0x1E69,
0x1E6B, 0x1E6D, 0x1E6F, 0x1E71, 0x1E73, 0x1E75, 0x1E77, 0x1E79,
0x1E7B, 0x1E7D, 0x1E7F, 0x1E81, 0x1E83, 0x1E85, 0x1E87, 0x1E89,
0x1E8B, 0x1E8D, 0x1E8F, 0x1E91, 0x1E93, 0x1E95, 0x1E96, 0x1E97,
0x1E98, 0x1E99, 0x1E9A, 0x1E9B, 0x1E9C, 0x1E9D, 0x1E9F, 0x1EA1,
0x1EA3, 0x1EA5, 0x1EA7, 0x1EA9, 0x1EAB, 0x1EAD, 0x1EAF, 0x1EB1,
0x1EB3, 0x1EB5, 0x1EB7, 0x1EB9, 0x1EBB, 0x1EBD, 0x1EBF, 0x1EC1,
0x1EC3, 0x1EC5, 0x1EC7, 0x1EC9, 0x1ECB, 0x1ECD, 0x1ECF, 0x1ED1,
0x1ED3, 0x1ED5, 0x1ED7, 0x1ED9, 0x1EDB, 0x1EDD, 0x1EDF, 0x1EE1,
0x1EE3, 0x1EE5, 0x1EE7, 0x1EE9, 0x1EEB, 0x1EED, 0x1EEF, 0x1EF1,
0x1EF3, 0x1EF5, 0x1EF7, 0x1EF9, 0x1EFB, 0x1EFD, 0x1EFF, 0x1F00,
0x1F01, 0x1F02, 0x1F03, 0x1F04, 0x1F05, 0x1F06, 0x1F07, 0x1F10,
0x1F11, 0x1F12, 0x1F13, 0x1F14, 0x1F15, 0x1F20, 0x1F21, 0x1F22,
0x1F23, 0x1F24, 0x1F25, 0x1F26, 0x1F27, 0x1F30, 0x1F31, 0x1F32,
0x1F33, 0x1F34, 0x1F35, 0x1F36, 0x1F37, 0x1F40, 0x1F41, 0x1F42,
0x1F43, 0x1F44, 0x1F45, 0x1F50, 0x1F51, 0x1F52, 0x1F53, 0x1F54,
0x1F55, 0x1F56, 0x1F57, 0x1F60, 0x1F61, 0x1F62, 0x1F63, 0x1F64,
0x1F65, 0x1F66, 0x1F67, 0x1F70, 0x1F71, 0x1F72, 0x1F73, 0x1F74,
0x1F75, 0x1F76, 0x1F77, 0x1F78, 0x1F79, 0x1F7A, 0x1F7B, 0x1F7C,
0x1F7D, 0x1F80, 0x1F81, 0x1F82, 0x1F83, 0x1F84, 0x1F85, 0x1F86,
0x1F87, 0x1F90, 0x1F91, 0x1F92, 0x1F93, 0x1F94, 0x1F95, 0x1F96,
0x1F97, 0x1FA0, 0x1FA1, 0x1FA2, 0x1FA3, 0x1FA4, 0x1FA5, 0x1FA6,
0x1FA7, 0x1FB0, 0x1FB1, 0x1FB2, 0x1FB3, 0x1FB4, 0x1FB6, 0x1FB7,
0x1FBE, 0x1FC2, 0x1FC3, 0x1FC4, 0x1FC6, 0x1FC7, 0x1FD0, 0x1FD1,
0x1FD2, 0x1FD3, 0x1FD6, 0x1FD7, 0x1FE0, 0x1FE1, 0x1FE2, 0x1FE3,
0x1FE4, 0x1FE5, 0x1FE6, 0x1FE7, 0x1FF2, 0x1FF3, 0x1FF4, 0x1FF6,
0x1FF7, 0x210A, 0x210E, 0x210F, 0x2113, 0x212F, 0x2134, 0x2139,
0x213C, 0x213D, 0x2146, 0x2147, 0x2148, 0x2149, 0x214E, 0x2184,
0x2C30, 0x2C31, 0x2C32, 0x2C33, 0x2C34, 0x2C35, 0x2C36, 0x2C37,
0x2C38, 0x2C39, 0x2C3A, 0x2C3B, 0x2C3C, 0x2C3D, 0x2C3E, 0x2C3F,
0x2C40, 0x2C41, 0x2C42, 0x2C43, 0x2C44, 0x2C45, 0x2C46, 0x2C47,
0x2C48, 0x2C49, 0x2C4A, 0x2C4B, 0x2C4C, 0x2C4D, 0x2C4E, 0x2C4F,
0x2C50, 0x2C51, 0x2C52, 0x2C53, 0x2C54, 0x2C55, 0x2C56, 0x2C57,
0x2C58, 0x2C59, 0x2C5A, 0x2C5B, 0x2C5C, 0x2C5D, 0x2C5E, 0x2C5F,
0x2C61, 0x2C65, 0x2C66, 0x2C68, 0x2C6A, 0x2C6C, 0x2C71, 0x2C73,
0x2C74, 0x2C76, 0x2C77, 0x2C78, 0x2C79, 0x2C7A, 0x2C7B, 0x2C81,
0x2C83, 0x2C85, 0x2C87, 0x2C89, 0x2C8B, 0x2C8D, 0x2C8F, 0x2C91,
0x2C93, 0x2C95, 0x2C97, 0x2C99, 0x2C9B, 0x2C9D, 0x2C9F, 0x2CA1,
0x2CA3, 0x2CA5, 0x2CA7, 0x2CA9, 0x2CAB, 0x2CAD, 0x2CAF, 0x2CB1,
0x2CB3, 0x2CB5, 0x2CB7, 0x2CB9, 0x2CBB, 0x2CBD, 0x2CBF, 0x2CC1,
0x2CC3, 0x2CC5, 0x2CC7, 0x2CC9, 0x2CCB, 0x2CCD, 0x2CCF, 0x2CD1,
0x2CD3, 0x2CD5, 0x2CD7, 0x2CD9, 0x2CDB, 0x2CDD, 0x2CDF, 0x2CE1,
0x2CE3, 0x2CE4, 0x2CEC, 0x2CEE, 0x2CF3, 0x2D00, 0x2D01, 0x2D02,
0x2D03, 0x2D04, 0x2D05, 0x2D06, 0x2D07, 0x2D08, 0x2D09, 0x2D0A,
0x2D0B, 0x2D0C, 0x2D0D, 0x2D0E, 0x2D0F, 0x2D10, 0x2D11, 0x2D12,
0x2D13, 0x2D14, 0x2D15, 0x2D16, 0x2D17, 0x2D18, 0x2D19, 0x2D1A,
0x2D1B, 0x2D1C, 0x2D1D, 0x2D1E, 0x2D1F, 0x2D20, 0x2D21, 0x2D22,
0x2D23, 0x2D24, 0x2D25, 0x2D27, 0x2D2D, 0xA641, 0xA643, 0xA645,
0xA647, 0xA649, 0xA64B, 0xA64D, 0xA64F, 0xA651, 0xA653, 0xA655,
0xA657, 0xA659, 0xA65B, 0xA65D, 0xA65F, 0xA661, 0xA663, 0xA665,
0xA667, 0xA669, 0xA66B, 0xA66D, 0xA681, 0xA683, 0xA685, 0xA687,
0xA689, 0xA68B, 0xA68D, 0xA68F, 0xA691, 0xA693, 0xA695, 0xA697,
0xA699, 0xA69B, 0xA723, 0xA725, 0xA727, 0xA729, 0xA72B, 0xA72D,
0xA72F, 0xA730, 0xA731, 0xA733, 0xA735, 0xA737, 0xA739, 0xA73B,
0xA73D, 0xA73F, 0xA741, 0xA743, 0xA745, 0xA747, 0xA749, 0xA74B,
0xA74D, 0xA74F, 0xA751, 0xA753, 0xA755, 0xA757, 0xA759, 0xA75B,
0xA75D, 0xA75F, 0xA761, 0xA763, 0xA765, 0xA767, 0xA769, 0xA76B,
0xA76D, 0xA76F, 0xA771, 0xA772, 0xA773, 0xA774, 0xA775, 0xA776,
0xA777, 0xA778, 0xA77A, 0xA77C, 0xA77F, 0xA781, 0xA783, 0xA785,
0xA787, 0xA78C, 0xA78E, 0xA791, 0xA793, 0xA794, 0xA795, 0xA797,
0xA799, 0xA79B, 0xA79D, 0xA79F, 0xA7A1, 0xA7A3, 0xA7A5, 0xA7A7,
0xA7A9, 0xA7AF, 0xA7B5, 0xA7B7, 0xA7B9, 0xA7BB, 0xA7BD, 0xA7BF,
0xA7C1, 0xA7C3, 0xA7C8, 0xA7CA, 0xA7D1, 0xA7D3, 0xA7D5, 0xA7D7,
0xA7D9, 0xA7F6, 0xA7FA, 0xAB30, 0xAB31, 0xAB32, 0xAB33, 0xAB34,
0xAB35, 0xAB36, 0xAB37, 0xAB38, 0xAB39, 0xAB3A, 0xAB3B, 0xAB3C,
0xAB3D, 0xAB3E, 0xAB3F, 0xAB40, 0xAB41, 0xAB42, 0xAB43, 0xAB44,
0xAB45, 0xAB46, 0xAB47, 0xAB48, 0xAB49, 0xAB4A, 0xAB4B, 0xAB4C,
0xAB4D, 0xAB4E, 0xAB4F, 0xAB50, 0xAB51, 0xAB52, 0xAB53, 0xAB54,
0xAB55, 0xAB56, 0xAB57, 0xAB58, 0xAB59, 0xAB5A, 0xAB60, 0xAB61,
0xAB62, 0xAB63, 0xAB64, 0xAB65, 0xAB66, 0xAB67, 0xAB68, 0xAB70,
0xAB71, 0xAB72, 0xAB73, 0xAB74, 0xAB75, 0xAB76, 0xAB77, 0xAB78,
0xAB79, 0xAB7A, 0xAB7B, 0xAB7C, 0xAB7D, 0xAB7E, 0xAB7F, 0xAB80,
0xAB81, 0xAB82, 0xAB83, 0xAB84, 0xAB85, 0xAB86, 0xAB87, 0xAB88,
0xAB89, 0xAB8A, 0xAB8B, 0xAB8C, 0xAB8D, 0xAB8E, 0xAB8F, 0xAB90,
0xAB91, 0xAB92, 0xAB93, 0xAB94, 0xAB95, 0xAB96, 0xAB97, 0xAB98,
0xAB99, 0xAB9A, 0xAB9B, 0xAB9C, 0xAB9D, 0xAB9E, 0xAB9F, 0xABA0,
0xABA1, 0xABA2, 0xABA3, 0xABA4, 0xABA5, 0xABA6, 0xABA7, 0xABA8,
0xABA9, 0xABAA, 0xABAB, 0xABAC, 0xABAD, 0xABAE, 0xABAF, 0xABB0,
0xABB1, 0xABB2, 0xABB3, 0xABB4, 0xABB5, 0xABB6, 0xABB7, 0xABB8,
0xABB9, 0xABBA, 0xABBB, 0xABBC, 0xABBD, 0xABBE, 0xABBF, 0xFB00,
0xFB01, 0xFB02, 0xFB03, 0xFB04, 0xFB05, 0xFB06, 0xFB13, 0xFB14,
0xFB15, 0xFB16, 0xFB17, 0xFF41, 0xFF42, 0xFF43, 0xFF44, 0xFF45,
0xFF46, 0xFF47, 0xFF48, 0xFF49, 0xFF4A, 0xFF4B, 0xFF4C, 0xFF4D,
0xFF4E, 0xFF4F, 0xFF50, 0xFF51, 0xFF52, 0xFF53, 0xFF54, 0xFF55,
0xFF56, 0xFF57, 0xFF58, 0xFF59, 0xFF5A, 0x10428, 0x10429, 0x1042A,
0x1042B, 0x1042C, 0x1042D, 0x1042E, 0x1042F, 0x10430, 0x10431, 0x10432,
0x10433, 0x10434, 0x10435, 0x10436, 0x10437, 0x10438, 0x10439, 0x1043A,
0x1043B, 0x1043C, 0x1043D, 0x1043E, 0x1043F, 0x10440, 0x10441, 0x10442,
0x10443, 0x10444, 0x10445, 0x10446, 0x10447, 0x10448, 0x10449, 0x1044A,
0x1044B, 0x1044C, 0x1044D, 0x1044E, 0x1044F, 0x104D8, 0x104D9, 0x104DA,
0x104DB, 0x104DC, 0x104DD, 0x104DE, 0x104DF, 0x104E0, 0x104E1, 0x104E2,
0x104E3, 0x104E4, 0x104E5, 0x104E6, 0x104E7, 0x104E8, 0x104E9, 0x104EA,
0x104EB, 0x104EC, 0x104ED, 0x104EE, 0x104EF, 0x104F0, 0x104F1, 0x104F2,
0x104F3, 0x104F4, 0x104F5, 0x104F6, 0x104F7, 0x104F8, 0x104F9, 0x104FA,
0x104FB, 0x10597, 0x10598, 0x10599, 0x1059A, 0x1059B, 0x1059C, 0x1059D,
0x1059E, 0x1059F, 0x105A0, 0x105A1, 0x105A3, 0x105A4, 0x105A5, 0x105A6,
0x105A7, 0x105A8, 0x105A9, 0x105AA, 0x105AB, 0x105AC, 0x105AD, 0x105AE,
0x105AF, 0x105B0, 0x105B1, 0x105B3, 0x105B4, 0x105B5, 0x105B6, 0x105B7,
0x105B8, 0x105B9, 0x105BB, 0x105BC, 0x10CC0, 0x10CC1, 0x10CC2, 0x10CC3,
0x10CC4, 0x10CC5, 0x10CC6, 0x10CC7, 0x10CC8, 0x10CC9, 0x10CCA, 0x10CCB,
0x10CCC, 0x10CCD, 0x10CCE, 0x10CCF, 0x10CD0, 0x10CD1, 0x10CD2, 0x10CD3,
0x10CD4, 0x10CD5, 0x10CD6, 0x10CD7, 0x10CD8, 0x10CD9, 0x10CDA, 0x10CDB,
0x10CDC, 0x10CDD, 0x10CDE, 0x10CDF, 0x10CE0, 0x10CE1, 0x10CE2, 0x10CE3,
0x10CE4, 0x10CE5, 0x10CE6, 0x10CE7, 0x10CE8, 0x10CE9, 0x10CEA, 0x10CEB,
0x10CEC, 0x10CED, 0x10CEE, 0x10CEF, 0x10CF0, 0x10CF1, 0x10CF2, 0x118C0,
0x118C1, 0x118C2, 0x118C3, 0x118C4, 0x118C5, 0x118C6, 0x118C7, 0x118C8,
0x118C9, 0x118CA, 0x118CB, 0x118CC, 0x118CD, 0x118CE, 0x118CF, 0x118D0,
0x118D1, 0x118D2, 0x118D3, 0x118D4, 0x118D5, 0x118D6, 0x118D7, 0x118D8,
0x118D9, 0x118DA, 0x118DB, 0x118DC, 0x118DD, 0x118DE, 0x118DF, 0x16E60,
0x16E61, 0x16E62, 0x16E63, 0x16E64, 0x16E65, 0x16E66, 0x16E67, 0x16E68,
0x16E69, 0x16E6A, 0x16E6B, 0x16E6C, 0x16E6D, 0x16E6E, 0x16E6F, 0x16E70,
0x16E71, 0x16E72, 0x16E73, 0x16E74, 0x16E75, 0x16E76, 0x16E77, 0x16E78,
0x16E79, 0x16E7A, 0x16E7B, 0x16E7C, 0x16E7D, 0x16E7E, 0x16E7F, 0x1D41A,
0x1D41B, 0x1D41C, 0x1D41D, 0x1D41E, 0x1D41F, 0x1D420, 0x1D421, 0x1D422,
0x1D423, 0x1D424, 0x1D425, 0x1D426, 0x1D427, 0x1D428, 0x1D429, 0x1D42A,
0x1D42B, 0x1D42C, 0x1D42D, 0x1D42E, 0x1D42F, 0x1D430, 0x1D431, 0x1D432,
0x1D433, 0x1D44E, 0x1D44F, 0x1D450, 0x1D451, 0x1D452, 0x1D453, 0x1D454,
0x1D456, 0x1D457, 0x1D458, 0x1D459, 0x1D45A, 0x1D45B, 0x1D45C, 0x1D45D,
0x1D45E, 0x1D45F, 0x1D460, 0x1D461, 0x1D462, 0x1D463, 0x1D464, 0x1D465,
0x1D466, 0x1D467, 0x1D482, 0x1D483, 0x1D484, 0x1D485, 0x1D486, 0x1D487,
0x1D488, 0x1D489, 0x1D48A, 0x1D48B, 0x1D48C, 0x1D48D, 0x1D48E, 0x1D48F,
0x1D490, 0x1D491, 0x1D492, 0x1D493, 0x1D494, 0x1D495, 0x1D496, 0x1D497,
0x1D498, 0x1D499, 0x1D49A, 0x1D49B, 0x1D4B6, 0x1D4B7, 0x1D4B8, 0x1D4B9,
0x1D4BB, 0x1D4BD, 0x1D4BE, 0x1D4BF, 0x1D4C0, 0x1D4C1, 0x1D4C2, 0x1D4C3,
0x1D4C5, 0x1D4C6, 0x1D4C7, 0x1D4C8, 0x1D4C9, 0x1D4CA, 0x1D4CB, 0x1D4CC,
0x1D4CD, 0x1D4CE, 0x1D4CF, 0x1D4EA, 0x1D4EB, 0x1D4EC, 0x1D4ED, 0x1D4EE,
0x1D4EF, 0x1D4F0, 0x1D4F1, 0x1D4F2, 0x1D4F3, 0x1D4F4, 0x1D4F5, 0x1D4F6,
0x1D4F7, 0x1D4F8, 0x1D4F9, 0x1D4FA, 0x1D4FB, 0x1D4FC, 0x1D4FD, 0x1D4FE,
0x1D4FF, 0x1D500, 0x1D501, 0x1D502, 0x1D503, 0x1D51E, 0x1D51F, 0x1D520,
0x1D521, 0x1D522, 0x1D523, 0x1D524, 0x1D525, 0x1D526, 0x1D527, 0x1D528,
0x1D529, 0x1D52A, 0x1D52B, 0x1D52C, 0x1D52D, 0x1D52E, 0x1D52F, 0x1D530,
0x1D531, 0x1D532, 0x1D533, 0x1D534, 0x1D535, 0x1D536, 0x1D537, 0x1D552,
0x1D553, 0x1D554, 0x1D555, 0x1D556, 0x1D557, 0x1D558, 0x1D559, 0x1D55A,
0x1D55B, 0x1D55C, 0x1D55D, 0x1D55E, 0x1D55F, 0x1D560, 0x1D561, 0x1D562,
0x1D563, 0x1D564, 0x1D565, 0x1D566, 0x1D567, 0x1D568, 0x1D569, 0x1D56A,
0x1D56B, 0x1D586, 0x1D587, 0x1D588, 0x1D589, 0x1D58A, 0x1D58B, 0x1D58C,
0x1D58D, 0x1D58E, 0x1D58F, 0x1D590, 0x1D591, 0x1D592, 0x1D593, 0x1D594,
0x1D595, 0x1D596, 0x1D597, 0x1D598, 0x1D599, 0x1D59A, 0x1D59B, 0x1D59C,
0x1D59D, 0x1D59E, 0x1D59F, 0x1D5BA, 0x1D5BB, 0x1D5BC, 0x1D5BD, 0x1D5BE,
0x1D5BF, 0x1D5C0, 0x1D5C1, 0x1D5C2, 0x1D5C3, 0x1D5C4, 0x1D5C5, 0x1D5C6,
0x1D5C7, 0x1D5C8, 0x1D5C9, 0x1D5CA, 0x1D5CB, 0x1D5CC, 0x1D5CD, 0x1D5CE,
0x1D5CF, 0x1D5D0, 0x1D5D1, 0x1D5D2, 0x1D5D3, 0x1D5EE, 0x1D5EF, 0x1D5F0,
0x1D5F1, 0x1D5F2, 0x1D5F3, 0x1D5F4, 0x1D5F5, 0x1D5F6, 0x1D5F7, 0x1D5F8,
0x1D5F9, 0x1D5FA, 0x1D5FB, 0x1D5FC, 0x1D5FD, 0x1D5FE, 0x1D5FF, 0x1D600,
0x1D601, 0x1D602, 0x1D603, 0x1D604, 0x1D605, 0x1D606, 0x1D607, 0x1D622,
0x1D623, 0x1D624, 0x1D625, 0x1D626, 0x1D627, 0x1D628, 0x1D629, 0x1D62A,
0x1D62B, 0x1D62C, 0x1D62D, 0x1D62E, 0x1D62F, 0x1D630, 0x1D631, 0x1D632,
0x1D633, 0x1D634, 0x1D635, 0x1D636, 0x1D637, 0x1D638, 0x1D639, 0x1D63A,
0x1D63B, 0x1D656, 0x1D657, 0x1D658, 0x1D659, 0x1D65A, 0x1D65B, 0x1D65C,
0x1D65D, 0x1D65E, 0x1D65F, 0x1D660, 0x1D661, 0x1D662, 0x1D663, 0x1D664,
0x1D665, 0x1D666, 0x1D667, 0x1D668, 0x1D669, 0x1D66A, 0x1D66B, 0x1D66C,
0x1D66D, 0x1D66E, 0x1D66F, 0x1D68A, 0x1D68B, 0x1D68C, 0x1D68D, 0x1D68E,
0x1D68F, 0x1D690, 0x1D691, 0x1D692, 0x1D693, 0x1D694, 0x1D695, 0x1D696,
0x1D697, 0x1D698, 0x1D699, 0x1D69A, 0x1D69B, 0x1D69C, 0x1D69D, 0x1D69E,
0x1D69F, 0x1D6A0, 0x1D6A1, 0x1D6A2, 0x1D6A3, 0x1D6A4, 0x1D6A5, 0x1D6C2,
0x1D6C3, 0x1D6C4, 0x1D6C5, 0x1D6C6, 0x1D6C7, 0x1D6C8, 0x1D6C9, 0x1D6CA,
0x1D6CB, 0x1D6CC, 0x1D6CD, 0x1D6CE, 0x1D6CF, 0x1D6D0, 0x1D6D1, 0x1D6D2,
0x1D6D3, 0x1D6D4, 0x1D6D5, 0x1D6D6, 0x1D6D7, 0x1D6D8, 0x1D6D9, 0x1D6DA,
0x1D6DC, 0x1D6DD, 0x1D6DE, 0x1D6DF, 0x1D6E0, 0x1D6E1, 0x1D6FC, 0x1D6FD,
0x1D6FE, 0x1D6FF, 0x1D700, 0x1D701, 0x1D702, 0x1D703, 0x1D704, 0x1D705,
0x1D706, 0x1D707, 0x1D708, 0x1D709, 0x1D70A, 0x1D70B, 0x1D70C, 0x1D70D,
0x1D70E, 0x1D70F, 0x1D710, 0x1D711, 0x1D712, 0x1D713, 0x1D714, 0x1D716,
0x1D717, 0x1D718, 0x1D719, 0x1D71A, 0x1D71B, 0x1D736, 0x1D737, 0x1D738,
0x1D739, 0x1D73A, 0x1D73B, 0x1D73C, 0x1D73D, 0x1D73E, 0x1D73F, 0x1D740,
0x1D741, 0x1D742, 0x1D743, 0x1D744, 0x1D745, 0x1D746, 0x1D747, 0x1D748,
0x1D749, 0x1D74A, 0x1D74B, 0x1D74C, 0x1D74D, 0x1D74E, 0x1D750, 0x1D751,
0x1D752, 0x1D753, 0x1D754, 0x1D755, 0x1D770, 0x1D771, 0x1D772, 0x1D773,
0x1D774, 0x1D775, 0x1D776, 0x1D777, 0x1D778, 0x1D779, 0x1D77A, 0x1D77B,
0x1D77C, 0x1D77D, 0x1D77E, 0x1D77F, 0x1D780, 0x1D781, 0x1D782, 0x1D783,
0x1D784, 0x1D785, 0x1D786, 0x1D787, 0x1D788, 0x1D78A, 0x1D78B, 0x1D78C,
0x1D78D, 0x1D78E, 0x1D78F, 0x1D7AA, 0x1D7AB, 0x1D7AC, 0x1D7AD, 0x1D7AE,
0x1D7AF, 0x1D7B0, 0x1D7B1, 0x1D7B2, 0x1D7B3, 0x1D7B4, 0x1D7B5, 0x1D7B6,
0x1D7B7, 0x1D7B8, 0x1D7B9, 0x1D7BA, 0x1D7BB, 0x1D7BC, 0x1D7BD, 0x1D7BE,
0x1D7BF, 0x1D7C0, 0x1D7C1, 0x1D7C2, 0x1D7C4, 0x1D7C5, 0x1D7C6, 0x1D7C7,
0x1D7C8, 0x1D7C9, 0x1D7CB, 0x1DF00, 0x1DF01, 0x1DF02, 0x1DF03, 0x1DF04,
0x1DF05, 0x1DF06, 0x1DF07, 0x1DF08, 0x1DF09, 0x1DF0B, 0x1DF0C, 0x1DF0D,
0x1DF0E, 0x1DF0F, 0x1DF10, 0x1DF11, 0x1DF12, 0x1DF13, 0x1DF14, 0x1DF15,
0x1DF16, 0x1DF17, 0x1DF18, 0x1DF19, 0x1DF1A, 0x1DF1B, 0x1DF1C, 0x1DF1D,
0x1DF1E, 0x1DF25, 0x1DF26, 0x1DF27, 0x1DF28, 0x1DF29, 0x1DF2A, 0x1E922,
0x1E923, 0x1E924, 0x1E925, 0x1E926, 0x1E927, 0x1E928, 0x1E929, 0x1E92A,
0x1E92B, 0x1E92C, 0x1E92D, 0x1E92E, 0x1E92F, 0x1E930, 0x1E931, 0x1E932,
0x1E933, 0x1E934, 0x1E935, 0x1E936, 0x1E937, 0x1E938, 0x1E939, 0x1E93A,
0x1E93B, 0x1E93C, 0x1E93D, 0x1E93E, 0x1E93F, 0x1E940, 0x1E941, 0x1E942,
0x1E943};
};
// Produced from https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp,
// using "[:Lu:]" for the Input field, using the categories found at
// https://www.fileformat.info/info/unicode/category/index.htm
template<>
struct char_set<upper_case_chars>
{
static constexpr uint32_t chars[] = {
0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E,
0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55,
0x56, 0x57, 0x58, 0x59, 0x5A, 0xC0, 0xC1,
0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8,
0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6,
0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE,
0x100, 0x102, 0x104, 0x106, 0x108, 0x10A, 0x10C,
0x10E, 0x110, 0x112, 0x114, 0x116, 0x118, 0x11A,
0x11C, 0x11E, 0x120, 0x122, 0x124, 0x126, 0x128,
0x12A, 0x12C, 0x12E, 0x130, 0x132, 0x134, 0x136,
0x139, 0x13B, 0x13D, 0x13F, 0x141, 0x143, 0x145,
0x147, 0x14A, 0x14C, 0x14E, 0x150, 0x152, 0x154,
0x156, 0x158, 0x15A, 0x15C, 0x15E, 0x160, 0x162,
0x164, 0x166, 0x168, 0x16A, 0x16C, 0x16E, 0x170,
0x172, 0x174, 0x176, 0x178, 0x179, 0x17B, 0x17D,
0x181, 0x182, 0x184, 0x186, 0x187, 0x189, 0x18A,
0x18B, 0x18E, 0x18F, 0x190, 0x191, 0x193, 0x194,
0x196, 0x197, 0x198, 0x19C, 0x19D, 0x19F, 0x1A0,
0x1A2, 0x1A4, 0x1A6, 0x1A7, 0x1A9, 0x1AC, 0x1AE,
0x1AF, 0x1B1, 0x1B2, 0x1B3, 0x1B5, 0x1B7, 0x1B8,
0x1BC, 0x1C4, 0x1C7, 0x1CA, 0x1CD, 0x1CF, 0x1D1,
0x1D3, 0x1D5, 0x1D7, 0x1D9, 0x1DB, 0x1DE, 0x1E0,
0x1E2, 0x1E4, 0x1E6, 0x1E8, 0x1EA, 0x1EC, 0x1EE,
0x1F1, 0x1F4, 0x1F6, 0x1F7, 0x1F8, 0x1FA, 0x1FC,
0x1FE, 0x200, 0x202, 0x204, 0x206, 0x208, 0x20A,
0x20C, 0x20E, 0x210, 0x212, 0x214, 0x216, 0x218,
0x21A, 0x21C, 0x21E, 0x220, 0x222, 0x224, 0x226,
0x228, 0x22A, 0x22C, 0x22E, 0x230, 0x232, 0x23A,
0x23B, 0x23D, 0x23E, 0x241, 0x243, 0x244, 0x245,
0x246, 0x248, 0x24A, 0x24C, 0x24E, 0x370, 0x372,
0x376, 0x37F, 0x386, 0x388, 0x389, 0x38A, 0x38C,
0x38E, 0x38F, 0x391, 0x392, 0x393, 0x394, 0x395,
0x396, 0x397, 0x398, 0x399, 0x39A, 0x39B, 0x39C,
0x39D, 0x39E, 0x39F, 0x3A0, 0x3A1, 0x3A3, 0x3A4,
0x3A5, 0x3A6, 0x3A7, 0x3A8, 0x3A9, 0x3AA, 0x3AB,
0x3CF, 0x3D2, 0x3D3, 0x3D4, 0x3D8, 0x3DA, 0x3DC,
0x3DE, 0x3E0, 0x3E2, 0x3E4, 0x3E6, 0x3E8, 0x3EA,
0x3EC, 0x3EE, 0x3F4, 0x3F7, 0x3F9, 0x3FA, 0x3FD,
0x3FE, 0x3FF, 0x400, 0x401, 0x402, 0x403, 0x404,
0x405, 0x406, 0x407, 0x408, 0x409, 0x40A, 0x40B,
0x40C, 0x40D, 0x40E, 0x40F, 0x410, 0x411, 0x412,
0x413, 0x414, 0x415, 0x416, 0x417, 0x418, 0x419,
0x41A, 0x41B, 0x41C, 0x41D, 0x41E, 0x41F, 0x420,
0x421, 0x422, 0x423, 0x424, 0x425, 0x426, 0x427,
0x428, 0x429, 0x42A, 0x42B, 0x42C, 0x42D, 0x42E,
0x42F, 0x460, 0x462, 0x464, 0x466, 0x468, 0x46A,
0x46C, 0x46E, 0x470, 0x472, 0x474, 0x476, 0x478,
0x47A, 0x47C, 0x47E, 0x480, 0x48A, 0x48C, 0x48E,
0x490, 0x492, 0x494, 0x496, 0x498, 0x49A, 0x49C,
0x49E, 0x4A0, 0x4A2, 0x4A4, 0x4A6, 0x4A8, 0x4AA,
0x4AC, 0x4AE, 0x4B0, 0x4B2, 0x4B4, 0x4B6, 0x4B8,
0x4BA, 0x4BC, 0x4BE, 0x4C0, 0x4C1, 0x4C3, 0x4C5,
0x4C7, 0x4C9, 0x4CB, 0x4CD, 0x4D0, 0x4D2, 0x4D4,
0x4D6, 0x4D8, 0x4DA, 0x4DC, 0x4DE, 0x4E0, 0x4E2,
0x4E4, 0x4E6, 0x4E8, 0x4EA, 0x4EC, 0x4EE, 0x4F0,
0x4F2, 0x4F4, 0x4F6, 0x4F8, 0x4FA, 0x4FC, 0x4FE,
0x500, 0x502, 0x504, 0x506, 0x508, 0x50A, 0x50C,
0x50E, 0x510, 0x512, 0x514, 0x516, 0x518, 0x51A,
0x51C, 0x51E, 0x520, 0x522, 0x524, 0x526, 0x528,
0x52A, 0x52C, 0x52E, 0x531, 0x532, 0x533, 0x534,
0x535, 0x536, 0x537, 0x538, 0x539, 0x53A, 0x53B,
0x53C, 0x53D, 0x53E, 0x53F, 0x540, 0x541, 0x542,
0x543, 0x544, 0x545, 0x546, 0x547, 0x548, 0x549,
0x54A, 0x54B, 0x54C, 0x54D, 0x54E, 0x54F, 0x550,
0x551, 0x552, 0x553, 0x554, 0x555, 0x556, 0x10A0,
0x10A1, 0x10A2, 0x10A3, 0x10A4, 0x10A5, 0x10A6, 0x10A7,
0x10A8, 0x10A9, 0x10AA, 0x10AB, 0x10AC, 0x10AD, 0x10AE,
0x10AF, 0x10B0, 0x10B1, 0x10B2, 0x10B3, 0x10B4, 0x10B5,
0x10B6, 0x10B7, 0x10B8, 0x10B9, 0x10BA, 0x10BB, 0x10BC,
0x10BD, 0x10BE, 0x10BF, 0x10C0, 0x10C1, 0x10C2, 0x10C3,
0x10C4, 0x10C5, 0x10C7, 0x10CD, 0x13A0, 0x13A1, 0x13A2,
0x13A3, 0x13A4, 0x13A5, 0x13A6, 0x13A7, 0x13A8, 0x13A9,
0x13AA, 0x13AB, 0x13AC, 0x13AD, 0x13AE, 0x13AF, 0x13B0,
0x13B1, 0x13B2, 0x13B3, 0x13B4, 0x13B5, 0x13B6, 0x13B7,
0x13B8, 0x13B9, 0x13BA, 0x13BB, 0x13BC, 0x13BD, 0x13BE,
0x13BF, 0x13C0, 0x13C1, 0x13C2, 0x13C3, 0x13C4, 0x13C5,
0x13C6, 0x13C7, 0x13C8, 0x13C9, 0x13CA, 0x13CB, 0x13CC,
0x13CD, 0x13CE, 0x13CF, 0x13D0, 0x13D1, 0x13D2, 0x13D3,
0x13D4, 0x13D5, 0x13D6, 0x13D7, 0x13D8, 0x13D9, 0x13DA,
0x13DB, 0x13DC, 0x13DD, 0x13DE, 0x13DF, 0x13E0, 0x13E1,
0x13E2, 0x13E3, 0x13E4, 0x13E5, 0x13E6, 0x13E7, 0x13E8,
0x13E9, 0x13EA, 0x13EB, 0x13EC, 0x13ED, 0x13EE, 0x13EF,
0x13F0, 0x13F1, 0x13F2, 0x13F3, 0x13F4, 0x13F5, 0x1C90,
0x1C91, 0x1C92, 0x1C93, 0x1C94, 0x1C95, 0x1C96, 0x1C97,
0x1C98, 0x1C99, 0x1C9A, 0x1C9B, 0x1C9C, 0x1C9D, 0x1C9E,
0x1C9F, 0x1CA0, 0x1CA1, 0x1CA2, 0x1CA3, 0x1CA4, 0x1CA5,
0x1CA6, 0x1CA7, 0x1CA8, 0x1CA9, 0x1CAA, 0x1CAB, 0x1CAC,
0x1CAD, 0x1CAE, 0x1CAF, 0x1CB0, 0x1CB1, 0x1CB2, 0x1CB3,
0x1CB4, 0x1CB5, 0x1CB6, 0x1CB7, 0x1CB8, 0x1CB9, 0x1CBA,
0x1CBD, 0x1CBE, 0x1CBF, 0x1E00, 0x1E02, 0x1E04, 0x1E06,
0x1E08, 0x1E0A, 0x1E0C, 0x1E0E, 0x1E10, 0x1E12, 0x1E14,
0x1E16, 0x1E18, 0x1E1A, 0x1E1C, 0x1E1E, 0x1E20, 0x1E22,
0x1E24, 0x1E26, 0x1E28, 0x1E2A, 0x1E2C, 0x1E2E, 0x1E30,
0x1E32, 0x1E34, 0x1E36, 0x1E38, 0x1E3A, 0x1E3C, 0x1E3E,
0x1E40, 0x1E42, 0x1E44, 0x1E46, 0x1E48, 0x1E4A, 0x1E4C,
0x1E4E, 0x1E50, 0x1E52, 0x1E54, 0x1E56, 0x1E58, 0x1E5A,
0x1E5C, 0x1E5E, 0x1E60, 0x1E62, 0x1E64, 0x1E66, 0x1E68,
0x1E6A, 0x1E6C, 0x1E6E, 0x1E70, 0x1E72, 0x1E74, 0x1E76,
0x1E78, 0x1E7A, 0x1E7C, 0x1E7E, 0x1E80, 0x1E82, 0x1E84,
0x1E86, 0x1E88, 0x1E8A, 0x1E8C, 0x1E8E, 0x1E90, 0x1E92,
0x1E94, 0x1E9E, 0x1EA0, 0x1EA2, 0x1EA4, 0x1EA6, 0x1EA8,
0x1EAA, 0x1EAC, 0x1EAE, 0x1EB0, 0x1EB2, 0x1EB4, 0x1EB6,
0x1EB8, 0x1EBA, 0x1EBC, 0x1EBE, 0x1EC0, 0x1EC2, 0x1EC4,
0x1EC6, 0x1EC8, 0x1ECA, 0x1ECC, 0x1ECE, 0x1ED0, 0x1ED2,
0x1ED4, 0x1ED6, 0x1ED8, 0x1EDA, 0x1EDC, 0x1EDE, 0x1EE0,
0x1EE2, 0x1EE4, 0x1EE6, 0x1EE8, 0x1EEA, 0x1EEC, 0x1EEE,
0x1EF0, 0x1EF2, 0x1EF4, 0x1EF6, 0x1EF8, 0x1EFA, 0x1EFC,
0x1EFE, 0x1F08, 0x1F09, 0x1F0A, 0x1F0B, 0x1F0C, 0x1F0D,
0x1F0E, 0x1F0F, 0x1F18, 0x1F19, 0x1F1A, 0x1F1B, 0x1F1C,
0x1F1D, 0x1F28, 0x1F29, 0x1F2A, 0x1F2B, 0x1F2C, 0x1F2D,
0x1F2E, 0x1F2F, 0x1F38, 0x1F39, 0x1F3A, 0x1F3B, 0x1F3C,
0x1F3D, 0x1F3E, 0x1F3F, 0x1F48, 0x1F49, 0x1F4A, 0x1F4B,
0x1F4C, 0x1F4D, 0x1F59, 0x1F5B, 0x1F5D, 0x1F5F, 0x1F68,
0x1F69, 0x1F6A, 0x1F6B, 0x1F6C, 0x1F6D, 0x1F6E, 0x1F6F,
0x1FB8, 0x1FB9, 0x1FBA, 0x1FBB, 0x1FC8, 0x1FC9, 0x1FCA,
0x1FCB, 0x1FD8, 0x1FD9, 0x1FDA, 0x1FDB, 0x1FE8, 0x1FE9,
0x1FEA, 0x1FEB, 0x1FEC, 0x1FF8, 0x1FF9, 0x1FFA, 0x1FFB,
0x2102, 0x2107, 0x210B, 0x210C, 0x210D, 0x2110, 0x2111,
0x2112, 0x2115, 0x2119, 0x211A, 0x211B, 0x211C, 0x211D,
0x2124, 0x2126, 0x2128, 0x212A, 0x212B, 0x212C, 0x212D,
0x2130, 0x2131, 0x2132, 0x2133, 0x213E, 0x213F, 0x2145,
0x2183, 0x2C00, 0x2C01, 0x2C02, 0x2C03, 0x2C04, 0x2C05,
0x2C06, 0x2C07, 0x2C08, 0x2C09, 0x2C0A, 0x2C0B, 0x2C0C,
0x2C0D, 0x2C0E, 0x2C0F, 0x2C10, 0x2C11, 0x2C12, 0x2C13,
0x2C14, 0x2C15, 0x2C16, 0x2C17, 0x2C18, 0x2C19, 0x2C1A,
0x2C1B, 0x2C1C, 0x2C1D, 0x2C1E, 0x2C1F, 0x2C20, 0x2C21,
0x2C22, 0x2C23, 0x2C24, 0x2C25, 0x2C26, 0x2C27, 0x2C28,
0x2C29, 0x2C2A, 0x2C2B, 0x2C2C, 0x2C2D, 0x2C2E, 0x2C2F,
0x2C60, 0x2C62, 0x2C63, 0x2C64, 0x2C67, 0x2C69, 0x2C6B,
0x2C6D, 0x2C6E, 0x2C6F, 0x2C70, 0x2C72, 0x2C75, 0x2C7E,
0x2C7F, 0x2C80, 0x2C82, 0x2C84, 0x2C86, 0x2C88, 0x2C8A,
0x2C8C, 0x2C8E, 0x2C90, 0x2C92, 0x2C94, 0x2C96, 0x2C98,
0x2C9A, 0x2C9C, 0x2C9E, 0x2CA0, 0x2CA2, 0x2CA4, 0x2CA6,
0x2CA8, 0x2CAA, 0x2CAC, 0x2CAE, 0x2CB0, 0x2CB2, 0x2CB4,
0x2CB6, 0x2CB8, 0x2CBA, 0x2CBC, 0x2CBE, 0x2CC0, 0x2CC2,
0x2CC4, 0x2CC6, 0x2CC8, 0x2CCA, 0x2CCC, 0x2CCE, 0x2CD0,
0x2CD2, 0x2CD4, 0x2CD6, 0x2CD8, 0x2CDA, 0x2CDC, 0x2CDE,
0x2CE0, 0x2CE2, 0x2CEB, 0x2CED, 0x2CF2, 0xA640, 0xA642,
0xA644, 0xA646, 0xA648, 0xA64A, 0xA64C, 0xA64E, 0xA650,
0xA652, 0xA654, 0xA656, 0xA658, 0xA65A, 0xA65C, 0xA65E,
0xA660, 0xA662, 0xA664, 0xA666, 0xA668, 0xA66A, 0xA66C,
0xA680, 0xA682, 0xA684, 0xA686, 0xA688, 0xA68A, 0xA68C,
0xA68E, 0xA690, 0xA692, 0xA694, 0xA696, 0xA698, 0xA69A,
0xA722, 0xA724, 0xA726, 0xA728, 0xA72A, 0xA72C, 0xA72E,
0xA732, 0xA734, 0xA736, 0xA738, 0xA73A, 0xA73C, 0xA73E,
0xA740, 0xA742, 0xA744, 0xA746, 0xA748, 0xA74A, 0xA74C,
0xA74E, 0xA750, 0xA752, 0xA754, 0xA756, 0xA758, 0xA75A,
0xA75C, 0xA75E, 0xA760, 0xA762, 0xA764, 0xA766, 0xA768,
0xA76A, 0xA76C, 0xA76E, 0xA779, 0xA77B, 0xA77D, 0xA77E,
0xA780, 0xA782, 0xA784, 0xA786, 0xA78B, 0xA78D, 0xA790,
0xA792, 0xA796, 0xA798, 0xA79A, 0xA79C, 0xA79E, 0xA7A0,
0xA7A2, 0xA7A4, 0xA7A6, 0xA7A8, 0xA7AA, 0xA7AB, 0xA7AC,
0xA7AD, 0xA7AE, 0xA7B0, 0xA7B1, 0xA7B2, 0xA7B3, 0xA7B4,
0xA7B6, 0xA7B8, 0xA7BA, 0xA7BC, 0xA7BE, 0xA7C0, 0xA7C2,
0xA7C4, 0xA7C5, 0xA7C6, 0xA7C7, 0xA7C9, 0xA7D0, 0xA7D6,
0xA7D8, 0xA7F5, 0xFF21, 0xFF22, 0xFF23, 0xFF24, 0xFF25,
0xFF26, 0xFF27, 0xFF28, 0xFF29, 0xFF2A, 0xFF2B, 0xFF2C,
0xFF2D, 0xFF2E, 0xFF2F, 0xFF30, 0xFF31, 0xFF32, 0xFF33,
0xFF34, 0xFF35, 0xFF36, 0xFF37, 0xFF38, 0xFF39, 0xFF3A,
0x10400, 0x10401, 0x10402, 0x10403, 0x10404, 0x10405, 0x10406,
0x10407, 0x10408, 0x10409, 0x1040A, 0x1040B, 0x1040C, 0x1040D,
0x1040E, 0x1040F, 0x10410, 0x10411, 0x10412, 0x10413, 0x10414,
0x10415, 0x10416, 0x10417, 0x10418, 0x10419, 0x1041A, 0x1041B,
0x1041C, 0x1041D, 0x1041E, 0x1041F, 0x10420, 0x10421, 0x10422,
0x10423, 0x10424, 0x10425, 0x10426, 0x10427, 0x104B0, 0x104B1,
0x104B2, 0x104B3, 0x104B4, 0x104B5, 0x104B6, 0x104B7, 0x104B8,
0x104B9, 0x104BA, 0x104BB, 0x104BC, 0x104BD, 0x104BE, 0x104BF,
0x104C0, 0x104C1, 0x104C2, 0x104C3, 0x104C4, 0x104C5, 0x104C6,
0x104C7, 0x104C8, 0x104C9, 0x104CA, 0x104CB, 0x104CC, 0x104CD,
0x104CE, 0x104CF, 0x104D0, 0x104D1, 0x104D2, 0x104D3, 0x10570,
0x10571, 0x10572, 0x10573, 0x10574, 0x10575, 0x10576, 0x10577,
0x10578, 0x10579, 0x1057A, 0x1057C, 0x1057D, 0x1057E, 0x1057F,
0x10580, 0x10581, 0x10582, 0x10583, 0x10584, 0x10585, 0x10586,
0x10587, 0x10588, 0x10589, 0x1058A, 0x1058C, 0x1058D, 0x1058E,
0x1058F, 0x10590, 0x10591, 0x10592, 0x10594, 0x10595, 0x10C80,
0x10C81, 0x10C82, 0x10C83, 0x10C84, 0x10C85, 0x10C86, 0x10C87,
0x10C88, 0x10C89, 0x10C8A, 0x10C8B, 0x10C8C, 0x10C8D, 0x10C8E,
0x10C8F, 0x10C90, 0x10C91, 0x10C92, 0x10C93, 0x10C94, 0x10C95,
0x10C96, 0x10C97, 0x10C98, 0x10C99, 0x10C9A, 0x10C9B, 0x10C9C,
0x10C9D, 0x10C9E, 0x10C9F, 0x10CA0, 0x10CA1, 0x10CA2, 0x10CA3,
0x10CA4, 0x10CA5, 0x10CA6, 0x10CA7, 0x10CA8, 0x10CA9, 0x10CAA,
0x10CAB, 0x10CAC, 0x10CAD, 0x10CAE, 0x10CAF, 0x10CB0, 0x10CB1,
0x10CB2, 0x118A0, 0x118A1, 0x118A2, 0x118A3, 0x118A4, 0x118A5,
0x118A6, 0x118A7, 0x118A8, 0x118A9, 0x118AA, 0x118AB, 0x118AC,
0x118AD, 0x118AE, 0x118AF, 0x118B0, 0x118B1, 0x118B2, 0x118B3,
0x118B4, 0x118B5, 0x118B6, 0x118B7, 0x118B8, 0x118B9, 0x118BA,
0x118BB, 0x118BC, 0x118BD, 0x118BE, 0x118BF, 0x16E40, 0x16E41,
0x16E42, 0x16E43, 0x16E44, 0x16E45, 0x16E46, 0x16E47, 0x16E48,
0x16E49, 0x16E4A, 0x16E4B, 0x16E4C, 0x16E4D, 0x16E4E, 0x16E4F,
0x16E50, 0x16E51, 0x16E52, 0x16E53, 0x16E54, 0x16E55, 0x16E56,
0x16E57, 0x16E58, 0x16E59, 0x16E5A, 0x16E5B, 0x16E5C, 0x16E5D,
0x16E5E, 0x16E5F, 0x1D400, 0x1D401, 0x1D402, 0x1D403, 0x1D404,
0x1D405, 0x1D406, 0x1D407, 0x1D408, 0x1D409, 0x1D40A, 0x1D40B,
0x1D40C, 0x1D40D, 0x1D40E, 0x1D40F, 0x1D410, 0x1D411, 0x1D412,
0x1D413, 0x1D414, 0x1D415, 0x1D416, 0x1D417, 0x1D418, 0x1D419,
0x1D434, 0x1D435, 0x1D436, 0x1D437, 0x1D438, 0x1D439, 0x1D43A,
0x1D43B, 0x1D43C, 0x1D43D, 0x1D43E, 0x1D43F, 0x1D440, 0x1D441,
0x1D442, 0x1D443, 0x1D444, 0x1D445, 0x1D446, 0x1D447, 0x1D448,
0x1D449, 0x1D44A, 0x1D44B, 0x1D44C, 0x1D44D, 0x1D468, 0x1D469,
0x1D46A, 0x1D46B, 0x1D46C, 0x1D46D, 0x1D46E, 0x1D46F, 0x1D470,
0x1D471, 0x1D472, 0x1D473, 0x1D474, 0x1D475, 0x1D476, 0x1D477,
0x1D478, 0x1D479, 0x1D47A, 0x1D47B, 0x1D47C, 0x1D47D, 0x1D47E,
0x1D47F, 0x1D480, 0x1D481, 0x1D49C, 0x1D49E, 0x1D49F, 0x1D4A2,
0x1D4A5, 0x1D4A6, 0x1D4A9, 0x1D4AA, 0x1D4AB, 0x1D4AC, 0x1D4AE,
0x1D4AF, 0x1D4B0, 0x1D4B1, 0x1D4B2, 0x1D4B3, 0x1D4B4, 0x1D4B5,
0x1D4D0, 0x1D4D1, 0x1D4D2, 0x1D4D3, 0x1D4D4, 0x1D4D5, 0x1D4D6,
0x1D4D7, 0x1D4D8, 0x1D4D9, 0x1D4DA, 0x1D4DB, 0x1D4DC, 0x1D4DD,
0x1D4DE, 0x1D4DF, 0x1D4E0, 0x1D4E1, 0x1D4E2, 0x1D4E3, 0x1D4E4,
0x1D4E5, 0x1D4E6, 0x1D4E7, 0x1D4E8, 0x1D4E9, 0x1D504, 0x1D505,
0x1D507, 0x1D508, 0x1D509, 0x1D50A, 0x1D50D, 0x1D50E, 0x1D50F,
0x1D510, 0x1D511, 0x1D512, 0x1D513, 0x1D514, 0x1D516, 0x1D517,
0x1D518, 0x1D519, 0x1D51A, 0x1D51B, 0x1D51C, 0x1D538, 0x1D539,
0x1D53B, 0x1D53C, 0x1D53D, 0x1D53E, 0x1D540, 0x1D541, 0x1D542,
0x1D543, 0x1D544, 0x1D546, 0x1D54A, 0x1D54B, 0x1D54C, 0x1D54D,
0x1D54E, 0x1D54F, 0x1D550, 0x1D56C, 0x1D56D, 0x1D56E, 0x1D56F,
0x1D570, 0x1D571, 0x1D572, 0x1D573, 0x1D574, 0x1D575, 0x1D576,
0x1D577, 0x1D578, 0x1D579, 0x1D57A, 0x1D57B, 0x1D57C, 0x1D57D,
0x1D57E, 0x1D57F, 0x1D580, 0x1D581, 0x1D582, 0x1D583, 0x1D584,
0x1D585, 0x1D5A0, 0x1D5A1, 0x1D5A2, 0x1D5A3, 0x1D5A4, 0x1D5A5,
0x1D5A6, 0x1D5A7, 0x1D5A8, 0x1D5A9, 0x1D5AA, 0x1D5AB, 0x1D5AC,
0x1D5AD, 0x1D5AE, 0x1D5AF, 0x1D5B0, 0x1D5B1, 0x1D5B2, 0x1D5B3,
0x1D5B4, 0x1D5B5, 0x1D5B6, 0x1D5B7, 0x1D5B8, 0x1D5B9, 0x1D5D4,
0x1D5D5, 0x1D5D6, 0x1D5D7, 0x1D5D8, 0x1D5D9, 0x1D5DA, 0x1D5DB,
0x1D5DC, 0x1D5DD, 0x1D5DE, 0x1D5DF, 0x1D5E0, 0x1D5E1, 0x1D5E2,
0x1D5E3, 0x1D5E4, 0x1D5E5, 0x1D5E6, 0x1D5E7, 0x1D5E8, 0x1D5E9,
0x1D5EA, 0x1D5EB, 0x1D5EC, 0x1D5ED, 0x1D608, 0x1D609, 0x1D60A,
0x1D60B, 0x1D60C, 0x1D60D, 0x1D60E, 0x1D60F, 0x1D610, 0x1D611,
0x1D612, 0x1D613, 0x1D614, 0x1D615, 0x1D616, 0x1D617, 0x1D618,
0x1D619, 0x1D61A, 0x1D61B, 0x1D61C, 0x1D61D, 0x1D61E, 0x1D61F,
0x1D620, 0x1D621, 0x1D63C, 0x1D63D, 0x1D63E, 0x1D63F, 0x1D640,
0x1D641, 0x1D642, 0x1D643, 0x1D644, 0x1D645, 0x1D646, 0x1D647,
0x1D648, 0x1D649, 0x1D64A, 0x1D64B, 0x1D64C, 0x1D64D, 0x1D64E,
0x1D64F, 0x1D650, 0x1D651, 0x1D652, 0x1D653, 0x1D654, 0x1D655,
0x1D670, 0x1D671, 0x1D672, 0x1D673, 0x1D674, 0x1D675, 0x1D676,
0x1D677, 0x1D678, 0x1D679, 0x1D67A, 0x1D67B, 0x1D67C, 0x1D67D,
0x1D67E, 0x1D67F, 0x1D680, 0x1D681, 0x1D682, 0x1D683, 0x1D684,
0x1D685, 0x1D686, 0x1D687, 0x1D688, 0x1D689, 0x1D6A8, 0x1D6A9,
0x1D6AA, 0x1D6AB, 0x1D6AC, 0x1D6AD, 0x1D6AE, 0x1D6AF, 0x1D6B0,
0x1D6B1, 0x1D6B2, 0x1D6B3, 0x1D6B4, 0x1D6B5, 0x1D6B6, 0x1D6B7,
0x1D6B8, 0x1D6B9, 0x1D6BA, 0x1D6BB, 0x1D6BC, 0x1D6BD, 0x1D6BE,
0x1D6BF, 0x1D6C0, 0x1D6E2, 0x1D6E3, 0x1D6E4, 0x1D6E5, 0x1D6E6,
0x1D6E7, 0x1D6E8, 0x1D6E9, 0x1D6EA, 0x1D6EB, 0x1D6EC, 0x1D6ED,
0x1D6EE, 0x1D6EF, 0x1D6F0, 0x1D6F1, 0x1D6F2, 0x1D6F3, 0x1D6F4,
0x1D6F5, 0x1D6F6, 0x1D6F7, 0x1D6F8, 0x1D6F9, 0x1D6FA, 0x1D71C,
0x1D71D, 0x1D71E, 0x1D71F, 0x1D720, 0x1D721, 0x1D722, 0x1D723,
0x1D724, 0x1D725, 0x1D726, 0x1D727, 0x1D728, 0x1D729, 0x1D72A,
0x1D72B, 0x1D72C, 0x1D72D, 0x1D72E, 0x1D72F, 0x1D730, 0x1D731,
0x1D732, 0x1D733, 0x1D734, 0x1D756, 0x1D757, 0x1D758, 0x1D759,
0x1D75A, 0x1D75B, 0x1D75C, 0x1D75D, 0x1D75E, 0x1D75F, 0x1D760,
0x1D761, 0x1D762, 0x1D763, 0x1D764, 0x1D765, 0x1D766, 0x1D767,
0x1D768, 0x1D769, 0x1D76A, 0x1D76B, 0x1D76C, 0x1D76D, 0x1D76E,
0x1D790, 0x1D791, 0x1D792, 0x1D793, 0x1D794, 0x1D795, 0x1D796,
0x1D797, 0x1D798, 0x1D799, 0x1D79A, 0x1D79B, 0x1D79C, 0x1D79D,
0x1D79E, 0x1D79F, 0x1D7A0, 0x1D7A1, 0x1D7A2, 0x1D7A3, 0x1D7A4,
0x1D7A5, 0x1D7A6, 0x1D7A7, 0x1D7A8, 0x1D7CA, 0x1E900, 0x1E901,
0x1E902, 0x1E903, 0x1E904, 0x1E905, 0x1E906, 0x1E907, 0x1E908,
0x1E909, 0x1E90A, 0x1E90B, 0x1E90C, 0x1E90D, 0x1E90E, 0x1E90F,
0x1E910, 0x1E911, 0x1E912, 0x1E913, 0x1E914, 0x1E915, 0x1E916,
0x1E917, 0x1E918, 0x1E919, 0x1E91A, 0x1E91B, 0x1E91C, 0x1E91D,
0x1E91E, 0x1E91F, 0x1E920, 0x1E921};
};
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -31,9 +31,16 @@ namespace boost { namespace parser {
template<typename T>
constexpr bool enable_optional = false;
/** A variable template that indicates that type `T` is an variant-like
type. */
template<typename T>
constexpr bool enable_variant = false;
#ifndef BOOST_PARSER_DOXYGEN
template<typename T>
constexpr bool enable_optional<std::optional<T>> = true;
template<typename... Ts>
constexpr bool enable_variant<std::variant<Ts...>> = true;
#endif
namespace detail {
@@ -124,6 +131,32 @@ namespace boost { namespace parser {
symbol_table_tries_t & symbol_table_tries) noexcept;
struct skip_skipper;
struct char_subrange
{
char32_t lo_;
char32_t hi_;
};
template<typename Tag>
struct char_subranges
{};
struct hex_digit_subranges
{};
struct control_subranges
{};
template<typename Tag>
struct char_set
{};
struct punct_chars
{};
struct lower_case_chars
{};
struct upper_case_chars
{};
}
/** Repeats the application of another parser `p` of type `Parser`,
@@ -267,9 +300,9 @@ namespace boost { namespace parser {
`CanUseCallbacks` is `true`, and if this parser is used within a call
to `callback_parse()`, the attribute is produced via callback;
otherwise, the attribute is produced as normal (as a return value, or
as an out-param). The rule may be constructed with a user-friendly
name that will appear if the top-level parse is executed with
`trace_mode == boost::parser::trace::on`. */
as an out-param). The rule may be constructed with user-friendly
diagnostic text that will appear if the top-level parse is executed
with `trace_mode == boost::parser::trace::on`. */
template<
bool CanUseCallbacks,
typename TagType,
@@ -293,6 +326,14 @@ namespace boost { namespace parser {
template<typename Attribute>
struct attr_parser;
/** A tag type that can be passed as the first parameter to `char_()` when
the second parameter is a sorted, random access sequence that can be
matched using a binary search.*/
struct sorted_t
{};
inline constexpr sorted_t sorted;
/** Matches a single code point. If `AttributeType` is not `void`,
`AttributeType` is the attribute type produced; otherwise, the
attribute type is the decayed type of the matched code point. The
@@ -302,19 +343,46 @@ namespace boost { namespace parser {
template<typename Expected, typename AttributeType = void>
struct char_parser;
/** Maches a particular string, delimited by an iterator sentinel pair;
/** Matches a single code point that is equal to one of the code points
associated with tag type `Tag`. This is used to create sets of
characters for matching Unicode character classes like punctuation or
lower case. Attribute type is the attribute type of the character
being matched. */
template<typename Tag>
struct char_set_parser;
/** Matches a single code point that falls into one of the subranges of
code points associated with tag type `Tag`. This is used to create
sets of characters for matching Unicode character classes like hex
digits or control characters. Attribute type is the attribute type of
the character being matched. */
template<typename Tag>
struct char_subrange_parser;
/** Matches a single decimal digit code point, using the Unicode character
class Hex_Digit. Attribute type is the attribute type of the
character being matched. */
struct digit_parser;
/** Matches a particular string, delimited by an iterator sentinel pair;
produces no attribute. */
template<typename StrIter, typename StrSentinel>
struct string_parser;
/** Maches an end-of-line (`NewlinesOnly == true`) or whitespace
(`NewlinesOnly == false`) code point, based on the Unicode definitions
of each (also matches the two code points `"\r\n"`). Produces no
/** Matches a string delimited by quotation marks; produces a
`std::string` attribute. */
template<typename I, typename S>
struct quoted_string_parser;
/** Matches an end-of-line (`NewlinesOnly == true`), whitespace
(`NewlinesOnly == false`), or (`NoNewlines == true`) blank (whitespace
but not newline) code point, based on the Unicode definitions of each
(also matches the two code points `"\r\n"`). Produces no
attribute. */
template<bool NewlinesOnly>
template<bool NewlinesOnly, bool NoNewlines>
struct ws_parser;
/** Maches the strings "true" and "false", producing an attribute of
/** Matches the strings "true" and "false", producing an attribute of
`true` or `false`, respectively, and fails on any other input. */
struct bool_parser;

View File

@@ -0,0 +1,720 @@
#ifndef BOOST_PARSER_REPLACE_HPP
#define BOOST_PARSER_REPLACE_HPP
#include <boost/parser/search.hpp>
#if !defined(_MSC_VER) || BOOST_PARSER_USE_CONCEPTS
namespace boost::parser {
namespace detail {
template<typename T, bool = std::is_pointer_v<remove_cv_ref_t<T>>>
constexpr auto range_value_type =
wrapper<remove_cv_ref_t<range_value_t<T>>>{};
template<typename T>
constexpr auto range_value_type<T, true> = wrapper<
remove_cv_ref_t<std::remove_pointer_t<remove_cv_ref_t<T>>>>{};
template<typename T>
constexpr text::format range_utf_format()
{
#if !BOOST_PARSER_USE_CONCEPTS
// Special case: the metafunctions above will not detect char8_t
// in C++17 mode, since it does not exit yet! So, we need to
// detect utf8_view in particular, and know that its use implies
// format::utf8.
if constexpr (is_utf8_view<T>{}) {
return format::utf8;
} else {
#endif
using value_t = typename decltype(range_value_type<T>)::type;
if constexpr (std::is_same_v<value_t, char>) {
return no_format;
#if defined(__cpp_char8_t)
} else if constexpr (std::is_same_v<value_t, char8_t>) {
return format::utf8;
#endif
} else if constexpr (
std::is_same_v<value_t, char16_t>
#ifdef _MSC_VER
|| std::is_same_v<T, wchar_t>
#endif
) {
return format::utf16;
} else if constexpr (
std::is_same_v<value_t, char32_t>
#ifndef _MSC_VER
|| std::is_same_v<T, wchar_t>
#endif
) {
return format::utf32;
} else {
static_assert(
sizeof(T) && false,
"Looks like you're trying to pass a range to replace "
"or transform_replace that has a non-character type "
"for its value type. This is not supported.");
}
#if !BOOST_PARSER_USE_CONCEPTS
}
#endif
}
template<typename T>
constexpr text::format
range_utf_format_v = detail::range_utf_format<remove_cv_ref_t<T>>();
template<typename V1, typename V2>
using concat_reference_t =
std::common_type_t<range_reference_t<V1>, range_reference_t<V2>>;
template<typename V1, typename V2>
using concat_value_t =
std::common_type_t<range_value_t<V1>, range_value_t<V2>>;
template<typename V1, typename V2>
using concat_rvalue_reference_t = std::common_type_t<
range_rvalue_reference_t<V1>,
range_rvalue_reference_t<V2>>;
#if BOOST_PARSER_USE_CONCEPTS
// clang-format off
template<typename ReplacementV, typename V>
concept concatable = requires {
typename detail::concat_reference_t<ReplacementV, V>;
typename detail::concat_value_t<ReplacementV, V>;
typename detail::concat_rvalue_reference_t<ReplacementV, V>;
};
// clang-format on
#else
template<typename ReplacementV, typename V>
// clang-format off
using concatable_expr = decltype(
std::declval<concat_reference_t<ReplacementV, V>>(),
std::declval<concat_value_t<ReplacementV, V>>(),
std::declval<concat_rvalue_reference_t<ReplacementV, V>>());
// clang-format on
template<typename ReplacementV, typename V>
constexpr bool concatable =
is_detected_v<concatable_expr, ReplacementV, V>;
#endif
template<
typename V1,
typename V2
#if !BOOST_PARSER_USE_CONCEPTS
,
typename Enable = std::enable_if_t<concatable<V1, V2>>
#endif
>
#if BOOST_PARSER_USE_CONCEPTS
requires concatable<V1, V2>
#endif
struct either_iterator_impl
: detail::stl_interfaces::iterator_interface<
either_iterator_impl<V1, V2>,
std::forward_iterator_tag,
concat_value_t<V1, V2>,
concat_reference_t<V1, V2>>
{
constexpr either_iterator_impl() = default;
constexpr either_iterator_impl(iterator_t<V1> it) : it_(it) {}
template<typename V = V2>
constexpr either_iterator_impl(iterator_t<V> it) : it_(it)
{}
constexpr concat_reference_t<V1, V2> operator*() const
{
if (it_.index() == 0) {
return *std::get<0>(it_);
} else {
return *std::get<1>(it_);
}
}
constexpr either_iterator_impl & operator++()
{
if (it_.index() == 0)
++std::get<0>(it_);
else
++std::get<1>(it_);
return *this;
}
friend constexpr bool
operator==(either_iterator_impl lhs, either_iterator_impl rhs)
{
if (lhs.it_.index() != rhs.it_.index())
return false;
if (lhs.it_.index() == 0)
return std::get<0>(lhs.it_) == std::get<0>(rhs.it_);
else
return std::get<1>(lhs.it_) == std::get<1>(rhs.it_);
}
using base_type = detail::stl_interfaces::iterator_interface<
either_iterator_impl<V1, V2>,
std::forward_iterator_tag,
concat_value_t<V1, V2>,
concat_reference_t<V1, V2>>;
using base_type::operator++;
private:
std::variant<iterator_t<V1>, iterator_t<V2>> it_;
};
template<typename V1, typename V2>
using either_iterator = std::conditional_t<
std::is_same_v<iterator_t<V1>, iterator_t<V2>>,
iterator_t<V1>,
either_iterator_impl<V1, V2>>;
#if BOOST_PARSER_USE_CONCEPTS
// clang-format off
template<typename ReplacementV, typename V>
concept replacement_for = requires (ReplacementV replacement, V base) {
{ either_iterator<V, ReplacementV>(replacement.begin()) };
{ either_iterator<V, ReplacementV>(replacement.end()) };
{ either_iterator<V, ReplacementV>(base.begin()) };
};
// clang-format on
#else
template<typename ReplacementV, typename V>
using replacement_for_expr = decltype(
either_iterator<V, ReplacementV>(
std::declval<ReplacementV&>().begin()),
either_iterator<V, ReplacementV>(
std::declval<ReplacementV&>().end()),
either_iterator<V, ReplacementV>(std::declval<V&>().begin()));
template<typename ReplacementV, typename V>
constexpr bool replacement_for =
is_detected_v<replacement_for_expr, ReplacementV, V>;
#endif
}
/** 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`. */
template<
#if BOOST_PARSER_USE_CONCEPTS
std::ranges::viewable_range V,
std::ranges::viewable_range ReplacementV,
#else
typename V,
typename ReplacementV,
#endif
typename Parser,
typename GlobalState,
typename ErrorHandler,
typename SkipParser
#if !BOOST_PARSER_USE_CONCEPTS
,
typename Enable = std::enable_if_t<
detail::replacement_for<ReplacementV, V> &&
(detail::range_utf_format_v<V> ==
detail::range_utf_format_v<ReplacementV>)>
#endif
>
#if BOOST_PARSER_USE_CONCEPTS
requires detail::replacement_for<ReplacementV, V> &&
(detail::range_utf_format_v<V> ==
detail::range_utf_format_v<ReplacementV>)
#endif
struct replace_view
: detail::stl_interfaces::view_interface<replace_view<
V,
ReplacementV,
Parser,
GlobalState,
ErrorHandler,
SkipParser>>
{
constexpr replace_view() = default;
constexpr replace_view(
V base,
parser_interface<Parser, GlobalState, ErrorHandler> const & parser,
parser_interface<SkipParser> const & skip,
ReplacementV replacement,
trace trace_mode = trace::off) :
base_(std::move(base)),
replacement_(std::move(replacement)),
parser_(parser),
skip_(skip),
trace_mode_(trace_mode)
{}
constexpr replace_view(
V base,
parser_interface<Parser, GlobalState, ErrorHandler> const & parser,
ReplacementV replacement,
trace trace_mode = trace::off) :
base_(std::move(base)),
replacement_(std::move(replacement)),
parser_(parser),
skip_(),
trace_mode_(trace_mode)
{}
constexpr V base() const &
#if BOOST_PARSER_USE_CONCEPTS
requires std::copy_constructible<V>
#endif
{
return base_;
}
constexpr V base() && { return std::move(base_); }
constexpr V replacement() const &
#if BOOST_PARSER_USE_CONCEPTS
requires std::copy_constructible<V>
#endif
{
return replacement_;
}
constexpr V replacement() && { return std::move(replacement_); }
constexpr auto begin() { return iterator<false>{this}; }
constexpr auto end() { return sentinel<false>{}; }
constexpr auto begin() const
#if BOOST_PARSER_USE_CONCEPTS
requires std::ranges::range<const V>
#endif
{
return iterator<true>{this};
}
constexpr auto end() const
#if BOOST_PARSER_USE_CONCEPTS
requires std::ranges::range<const V>
#endif
{
return sentinel<true>{};
}
template<bool Const>
struct sentinel
{};
template<bool Const>
struct iterator : detail::stl_interfaces::proxy_iterator_interface<
iterator<Const>,
std::forward_iterator_tag,
BOOST_PARSER_SUBRANGE<detail::either_iterator<
detail::maybe_const<Const, V>,
detail::maybe_const<Const, ReplacementV>>>>
{
using I = detail::iterator_t<detail::maybe_const<Const, V>>;
using S = detail::sentinel_t<detail::maybe_const<Const, V>>;
using ref_t_iter = detail::either_iterator<
detail::maybe_const<Const, V>,
detail::maybe_const<Const, ReplacementV>>;
using reference_type = BOOST_PARSER_SUBRANGE<ref_t_iter>;
constexpr iterator() = default;
constexpr iterator(
detail::maybe_const<Const, replace_view> * parent) :
parent_(parent),
r_(parent_->base_.begin(), parent_->base_.end()),
curr_(r_.begin(), r_.begin()),
next_it_(r_.begin()),
in_match_(true)
{
++*this;
}
constexpr iterator & operator++()
{
if (in_match_) {
r_ = BOOST_PARSER_SUBRANGE<I, S>(next_it_, r_.end());
auto const new_match = parser::search(
r_,
parent_->parser_,
parent_->skip_,
parent_->trace_mode_);
if (new_match.begin() == curr_.end()) {
curr_ = new_match;
} else {
curr_ =
BOOST_PARSER_SUBRANGE(next_it_, new_match.begin());
in_match_ = false;
}
next_it_ = new_match.end();
} else {
if (!curr_.empty()) {
curr_ = BOOST_PARSER_SUBRANGE(curr_.end(), next_it_);
in_match_ = true;
}
if (curr_.empty())
r_ = BOOST_PARSER_SUBRANGE<I, S>(next_it_, r_.end());
}
return *this;
}
constexpr reference_type operator*() const
{
if (in_match_) {
return reference_type(
ref_t_iter(parent_->replacement_.begin()),
ref_t_iter(parent_->replacement_.end()));
} else {
return reference_type(
ref_t_iter(curr_.begin()), ref_t_iter(curr_.end()));
}
}
friend constexpr bool operator==(iterator lhs, iterator rhs)
{
return lhs.r_.begin() == rhs.r_.begin();
}
friend constexpr bool operator==(iterator it, sentinel<Const>)
{
return it.r_.begin() == it.r_.end();
}
using base_type = detail::stl_interfaces::proxy_iterator_interface<
iterator,
std::forward_iterator_tag,
reference_type>;
using base_type::operator++;
private:
detail::maybe_const<Const, replace_view> * parent_;
BOOST_PARSER_SUBRANGE<I, S> r_;
BOOST_PARSER_SUBRANGE<I> curr_;
I next_it_;
bool in_match_;
};
template<bool Const>
friend struct iterator;
private:
V base_;
ReplacementV replacement_;
parser_interface<Parser, GlobalState, ErrorHandler> parser_;
parser_interface<SkipParser> skip_;
trace trace_mode_;
};
// deduction guides
template<
typename V,
typename ReplacementV,
typename Parser,
typename GlobalState,
typename ErrorHandler,
typename SkipParser>
replace_view(
V &&,
parser_interface<Parser, GlobalState, ErrorHandler>,
parser_interface<SkipParser>,
ReplacementV &&,
trace)
-> replace_view<
detail::text::detail::all_t<V>,
detail::text::detail::all_t<ReplacementV>,
Parser,
GlobalState,
ErrorHandler,
SkipParser>;
template<
typename V,
typename ReplacementV,
typename Parser,
typename GlobalState,
typename ErrorHandler,
typename SkipParser>
replace_view(
V &&,
parser_interface<Parser, GlobalState, ErrorHandler>,
parser_interface<SkipParser>,
ReplacementV &&)
-> replace_view<
detail::text::detail::all_t<V>,
detail::text::detail::all_t<ReplacementV>,
Parser,
GlobalState,
ErrorHandler,
SkipParser>;
template<
typename V,
typename ReplacementV,
typename Parser,
typename GlobalState,
typename ErrorHandler>
replace_view(
V &&,
parser_interface<Parser, GlobalState, ErrorHandler>,
ReplacementV &&,
trace)
-> replace_view<
detail::text::detail::all_t<V>,
detail::text::detail::all_t<ReplacementV>,
Parser,
GlobalState,
ErrorHandler,
parser_interface<eps_parser<detail::phony>>>;
template<
typename V,
typename ReplacementV,
typename Parser,
typename GlobalState,
typename ErrorHandler>
replace_view(
V &&,
parser_interface<Parser, GlobalState, ErrorHandler>,
ReplacementV &&)
-> replace_view<
detail::text::detail::all_t<V>,
detail::text::detail::all_t<ReplacementV>,
Parser,
GlobalState,
ErrorHandler,
parser_interface<eps_parser<detail::phony>>>;
namespace detail {
template<
typename V,
typename ReplacementV,
typename Parser,
typename GlobalState,
typename ErrorHandler,
typename SkipParser>
using replace_view_expr = decltype(replace_view<
V,
ReplacementV,
Parser,
GlobalState,
ErrorHandler,
SkipParser>(
std::declval<V>(),
std::declval<
parser_interface<Parser, GlobalState, ErrorHandler> const &>(),
std::declval<parser_interface<SkipParser> const &>(),
std::declval<ReplacementV>(),
trace::on));
template<
typename V,
typename ReplacementV,
typename Parser,
typename GlobalState,
typename ErrorHandler,
typename SkipParser>
constexpr bool can_replace_view = is_detected_v<
replace_view_expr,
V,
ReplacementV,
Parser,
GlobalState,
ErrorHandler,
SkipParser>;
struct replace_impl
{
#if BOOST_PARSER_USE_CONCEPTS
template<
parsable_range_like R,
range_like ReplacementR,
typename Parser,
typename GlobalState,
typename ErrorHandler,
typename SkipParser>
requires
// clang-format off
(std::is_pointer_v<std::remove_cvref_t<R>> ||
std::ranges::viewable_range<R>) &&
(std::is_pointer_v<std::remove_cvref_t<ReplacementR>> ||
std::ranges::viewable_range<ReplacementR>) &&
// clang-format on
can_replace_view<
to_range_t<R>,
decltype(to_range<
ReplacementR,
true,
detail::range_utf_format_v<R>>::
call(std::declval<ReplacementR>())),
Parser,
GlobalState,
ErrorHandler,
SkipParser>
// clang-format off
[[nodiscard]] constexpr auto operator()(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const &
parser,
parser_interface<SkipParser> const & skip,
ReplacementR && replacement,
trace trace_mode = trace::off) const
// clang-format on
{
return replace_view(
to_range<R>::call((R &&) r),
parser,
skip,
to_range<
ReplacementR,
true,
detail::range_utf_format_v<R>>::call((ReplacementR &&)
replacement),
trace_mode);
}
template<
parsable_range_like R,
range_like ReplacementR,
typename Parser,
typename GlobalState,
typename ErrorHandler>
requires
// clang-format off
(std::is_pointer_v<std::remove_cvref_t<R>> ||
std::ranges::viewable_range<R>) &&
(std::is_pointer_v<std::remove_cvref_t<ReplacementR>> ||
std::ranges::viewable_range<ReplacementR>) &&
// clang-format on
can_replace_view<
to_range_t<R>,
decltype(to_range<
ReplacementR,
true,
detail::range_utf_format_v<R>>::
call(std::declval<ReplacementR>())),
Parser,
GlobalState,
ErrorHandler,
parser_interface<eps_parser<detail::phony>>>
// clang-format off
[[nodiscard]] constexpr auto operator()(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const &
parser,
ReplacementR && replacement,
trace trace_mode = trace::off) const
// clang-format on
{
return (*this)(
(R &&) r,
parser,
parser_interface<eps_parser<detail::phony>>{},
(ReplacementR &&) replacement,
trace_mode);
}
#else
template<
typename R,
typename Parser,
typename GlobalState,
typename ErrorHandler,
typename SkipParser,
typename ReplacementR = trace,
typename Trace = trace,
typename Enable = std::enable_if_t<is_parsable_range_like_v<R>>>
[[nodiscard]] constexpr auto operator()(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const &
parser,
SkipParser && skip,
ReplacementR && replacement = ReplacementR{},
Trace trace_mode = Trace{}) const
{
if constexpr (
is_parser_iface<remove_cv_ref_t<SkipParser>> &&
is_range_like<remove_cv_ref_t<ReplacementR>> &&
std::is_same_v<Trace, trace>) {
// (r, parser, skip, replacement, trace) case
return impl(
(R &&) r,
parser,
skip,
(ReplacementR &&) replacement,
trace_mode);
} else if constexpr (
is_range_like<remove_cv_ref_t<SkipParser>> &&
std::is_same_v<remove_cv_ref_t<ReplacementR>, trace> &&
std::is_same_v<Trace, trace>) {
// (r, parser, replacement, trace) case
return impl(
(R &&) r,
parser,
parser_interface<eps_parser<detail::phony>>{},
(SkipParser &&) skip,
replacement);
} else {
static_assert(
sizeof(R) == 1 && false,
"Only the signatures replace(R, parser, skip, "
"replcement trace = trace::off) and replace(R, parser, "
"replacement, trace = trace::off) are supported.");
}
}
private:
template<
typename R,
typename ReplacementR,
typename Parser,
typename GlobalState,
typename ErrorHandler,
typename SkipParser>
[[nodiscard]] constexpr auto impl(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const &
parser,
parser_interface<SkipParser> const & skip,
ReplacementR && replacement,
trace trace_mode = trace::off) const
{
return replace_view(
to_range<R>::call((R &&) r),
parser,
skip,
to_range<
ReplacementR,
true,
detail::range_utf_format_v<R>>::call((ReplacementR &&)
replacement),
trace_mode);
}
#endif
};
}
/** A range adaptor object ([range.adaptor.object]). Given subexpressions
`E` and `P`, `Q`, `R`, and 'S', each of the expressions `replace(E,
P)`, `replace(E, P, Q)`. `replace(E, P, Q, R)`, and `replace(E, P, Q,
R, S)` are expression-equivalent to `replace_view(E, P)`,
`replace_view(E, P, Q)`, `replace_view(E, P, Q, R)`, `replace_view(E,
P, Q, R, S)`, respectively. */
inline constexpr detail::stl_interfaces::adaptor<detail::replace_impl>
replace = detail::replace_impl{};
}
#if BOOST_PARSER_USE_CONCEPTS
template<
typename V,
typename ReplacementV,
typename Parser,
typename GlobalState,
typename ErrorHandler,
typename SkipParser>
constexpr bool std::ranges::enable_borrowed_range<boost::parser::replace_view<
V,
ReplacementV,
Parser,
GlobalState,
ErrorHandler,
SkipParser>> = std::ranges::enable_borrowed_range<V> &&
std::ranges::enable_borrowed_range<ReplacementV>;
#endif
#endif
#endif

View File

@@ -0,0 +1,673 @@
#ifndef BOOST_PARSER_SEARCH_HPP
#define BOOST_PARSER_SEARCH_HPP
#include <boost/parser/parser.hpp>
#include <boost/parser/transcode_view.hpp>
#include <cstring>
namespace boost::parser {
namespace detail {
template<bool Const, typename T>
using maybe_const = std::conditional_t<Const, const T, T>;
inline constexpr text::format no_format = text::format::none;
template<text::format Format = text::format::utf8>
constexpr auto as_utf =
text::detail::as_utf_impl<text::utf8_view, text::format::utf8>{};
template<>
constexpr auto as_utf<text::format::utf16> =
text::detail::as_utf_impl<text::utf16_view, text::format::utf16>{};
template<>
constexpr auto as_utf<text::format::utf32> =
text::detail::as_utf_impl<text::utf32_view, text::format::utf32>{};
template<
typename R_,
bool ToCommonRange = false,
text::format OtherRangeFormat = no_format,
bool = std::is_pointer_v<remove_cv_ref_t<R_>> ||
text::detail::is_bounded_array_v<remove_cv_ref_t<R_>>>
struct to_range
{
template<typename R>
static constexpr auto call(R && r)
{
static_assert(std::is_same_v<R, R_>);
using T = remove_cv_ref_t<R>;
if constexpr (std::is_pointer_v<T>) {
if constexpr (OtherRangeFormat == no_format) {
if constexpr (ToCommonRange)
return BOOST_PARSER_SUBRANGE(r, r + std::strlen(r));
else
return BOOST_PARSER_SUBRANGE(r, null_sentinel_t{});
} else {
if constexpr (ToCommonRange) {
return BOOST_PARSER_SUBRANGE(
r, r + std::strlen(r)) |
as_utf<OtherRangeFormat>;
} else {
return BOOST_PARSER_SUBRANGE(r, null_sentinel_t{}) |
as_utf<OtherRangeFormat>;
}
}
} else if constexpr (text::detail::is_bounded_array_v<T>) {
auto const first = std::begin(r);
auto last = std::end(r);
constexpr auto n = std::extent_v<T>;
if (n && !r[n - 1])
--last;
if constexpr (OtherRangeFormat == no_format) {
return BOOST_PARSER_SUBRANGE(first, last);
} else {
return BOOST_PARSER_SUBRANGE(first, last) |
as_utf<OtherRangeFormat>;
}
} else {
return (R &&) r | as_utf<OtherRangeFormat>;
}
}
};
template<typename R_, bool ToCommonRange>
struct to_range<R_, ToCommonRange, no_format, false>
{
template<typename R>
static constexpr R && call(R && r)
{
return (R &&) r;
}
};
template<typename R>
using to_range_t = decltype(to_range<R>::call(std::declval<R>()));
struct phony
{};
template<
typename R,
typename Parser,
typename GlobalState,
typename ErrorHandler,
typename SkipParser>
auto search_impl(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const & parser,
parser_interface<SkipParser> const & skip,
trace trace_mode)
{
auto first = text::detail::begin(r);
auto const last = text::detail::end(r);
if (first == last)
return BOOST_PARSER_SUBRANGE(first, first);
auto const search_parser = omit[*(char_ - parser)] >> -raw[parser];
if constexpr (std::is_same_v<SkipParser, eps_parser<phony>>) {
auto result = parser::prefix_parse(
first, last, search_parser, trace_mode);
if (*result)
return **result;
} else {
auto result = parser::prefix_parse(
first, last, search_parser, skip, trace_mode);
if (*result)
return **result;
}
return BOOST_PARSER_SUBRANGE(first, first);
}
template<
typename R,
typename Parser,
typename GlobalState,
typename ErrorHandler,
typename SkipParser>
#if BOOST_PARSER_USE_CONCEPTS
std::ranges::borrowed_subrange_t<R>
#else
auto
#endif
search_repack_shim(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const & parser,
parser_interface<SkipParser> const & skip,
trace trace_mode)
{
using value_type = range_value_t<decltype(r)>;
if constexpr (std::is_same_v<value_type, char>) {
return detail::search_impl((R &&) r, parser, skip, trace_mode);
} else {
auto r_unpacked = detail::text::unpack_iterator_and_sentinel(
text::detail::begin(r), text::detail::end(r));
auto result =
detail::search_impl(r | as_utf32, parser, skip, trace_mode);
return BOOST_PARSER_SUBRANGE(
r_unpacked.repack(text::detail::begin(result).base()),
r_unpacked.repack(text::detail::end(result).base()));
}
}
template<typename T>
constexpr bool is_parser_iface = false;
template<typename T>
constexpr bool is_parser_iface<parser_interface<T>> = true;
}
/** Returns a subrange to the first match for parser `parser` in `r`,
using skip-parser `skip`. This function has a similar interface and
semantics to `std::ranges::search()`. Returns `std::ranges::dangling`
in C++20 and later if `r` is a non-borrowable rvalue. */
template<
#if BOOST_PARSER_USE_CONCEPTS
parsable_range_like R,
#else
typename R,
#endif
typename Parser,
typename GlobalState,
typename ErrorHandler,
typename SkipParser
#if !BOOST_PARSER_USE_CONCEPTS
,
typename Enable = std::enable_if_t<detail::is_parsable_range_like_v<R>>
#endif
>
auto search(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const & parser,
parser_interface<SkipParser> const & skip,
trace trace_mode = trace::off)
{
return detail::search_repack_shim(
detail::to_range<R>::call((R &&) r), parser, skip, trace_mode);
}
/** Returns a subrange to the first match for parser `parser` in `[first,
last)`, using skip-parser `skip`. This function has a similar
interface and semantics to `std::ranges::search()`. */
template<
#if BOOST_PARSER_USE_CONCEPTS
parsable_iter I,
std::sentinel_for<I> S,
#else
typename I,
typename S,
#endif
typename Parser,
typename SkipParser,
typename GlobalState,
#if BOOST_PARSER_USE_CONCEPTS
error_handler<I, S, GlobalState> ErrorHandler
#else
typename ErrorHandler,
typename Enable = std::enable_if_t<
detail::is_parsable_iter_v<I> &&
detail::is_equality_comparable_with_v<I, S>>
#endif
>
auto search(
I first,
S last,
parser_interface<Parser, GlobalState, ErrorHandler> const & parser,
parser_interface<SkipParser> const & skip,
trace trace_mode = trace::off)
{
return parser::search(
BOOST_PARSER_SUBRANGE(first, last), parser, skip, trace_mode);
}
/** Returns a subrange to the first match for parser `parser` in `r`.
This function has a similar interface and semantics to
`std::ranges::search()`. Returns `std::ranges::dangling` in C++20 and
later if `r` is a non-borrowable rvalue. */
template<
#if BOOST_PARSER_USE_CONCEPTS
parsable_range_like R,
#else
typename R,
#endif
typename Parser,
typename GlobalState,
typename ErrorHandler
#if !BOOST_PARSER_USE_CONCEPTS
,
typename Enable = std::enable_if_t<detail::is_parsable_range_like_v<R>>
#endif
>
auto search(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const & parser,
trace trace_mode = trace::off)
{
return parser::search(
(R &&) r,
parser,
parser_interface<eps_parser<detail::phony>>{},
trace_mode);
}
/** Returns a subrange to the first match for parser `parser` in `[first,
last)`. This function has a similar interface and semantics to
`std::ranges::search()`. */
template<
#if BOOST_PARSER_USE_CONCEPTS
parsable_iter I,
std::sentinel_for<I> S,
#else
typename I,
typename S,
#endif
typename Parser,
typename GlobalState,
#if BOOST_PARSER_USE_CONCEPTS
error_handler<I, S, GlobalState> ErrorHandler
#else
typename ErrorHandler,
typename Enable = std::enable_if_t<
detail::is_parsable_iter_v<I> &&
detail::is_equality_comparable_with_v<I, S>>
#endif
>
auto search(
I first,
S last,
parser_interface<Parser, GlobalState, ErrorHandler> const & parser,
trace trace_mode = trace::off)
{
return parser::search(
BOOST_PARSER_SUBRANGE(first, last),
parser,
parser_interface<eps_parser<detail::phony>>{},
trace_mode);
}
/** 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. */
template<
#if BOOST_PARSER_USE_CONCEPTS
std::ranges::viewable_range V,
#else
typename V,
#endif
typename Parser,
typename GlobalState,
typename ErrorHandler,
typename SkipParser>
struct search_all_view
: detail::stl_interfaces::view_interface<
search_all_view<V, Parser, GlobalState, ErrorHandler, SkipParser>>
{
constexpr search_all_view() = default;
constexpr search_all_view(
V base,
parser_interface<Parser, GlobalState, ErrorHandler> const & parser,
parser_interface<SkipParser> const & skip,
trace trace_mode = trace::off) :
base_(std::move(base)),
parser_(parser),
skip_(skip),
trace_mode_(trace_mode)
{}
constexpr search_all_view(
V base,
parser_interface<Parser, GlobalState, ErrorHandler> const & parser,
trace trace_mode = trace::off) :
base_(std::move(base)),
parser_(parser),
skip_(),
trace_mode_(trace_mode)
{}
constexpr V base() const &
#if BOOST_PARSER_USE_CONCEPTS
requires std::copy_constructible<V>
#endif
{
return base_;
}
constexpr V base() && { return std::move(base_); }
constexpr auto begin() { return iterator<false>{this}; }
constexpr auto end() { return sentinel<false>{}; }
constexpr auto begin() const
#if BOOST_PARSER_USE_CONCEPTS
requires std::ranges::range<const V>
#endif
{
return iterator<true>{this};
}
constexpr auto end() const
#if BOOST_PARSER_USE_CONCEPTS
requires std::ranges::range<const V>
#endif
{
return sentinel<true>{};
}
template<bool Const>
struct sentinel
{};
template<bool Const>
struct iterator
: detail::stl_interfaces::proxy_iterator_interface<
iterator<Const>,
std::forward_iterator_tag,
BOOST_PARSER_SUBRANGE<
detail::iterator_t<detail::maybe_const<Const, V>>>>
{
using I = detail::iterator_t<detail::maybe_const<Const, V>>;
using S = detail::sentinel_t<detail::maybe_const<Const, V>>;
constexpr iterator() = default;
constexpr iterator(
detail::maybe_const<Const, search_all_view> * parent) :
parent_(parent),
r_(parent_->base_.begin(), parent_->base_.end()),
curr_(r_.begin(), r_.begin()),
next_it_(r_.begin())
{
++*this;
}
constexpr iterator & operator++()
{
r_ = BOOST_PARSER_SUBRANGE<I, S>(next_it_, r_.end());
curr_ = parser::search(
r_, parent_->parser_, parent_->skip_, parent_->trace_mode_);
next_it_ = curr_.end();
if (curr_.begin() == curr_.end())
r_ = BOOST_PARSER_SUBRANGE<I, S>(next_it_, r_.end());
return *this;
}
constexpr BOOST_PARSER_SUBRANGE<I> operator*() const
{
return curr_;
}
friend constexpr bool operator==(iterator lhs, iterator rhs)
{
return lhs.r_.begin() == rhs.r_.begin();
}
friend constexpr bool operator==(iterator it, sentinel<Const>)
{
return it.r_.begin() == it.r_.end();
}
using base_type = detail::stl_interfaces::proxy_iterator_interface<
iterator,
std::forward_iterator_tag,
BOOST_PARSER_SUBRANGE<I>>;
using base_type::operator++;
private:
detail::maybe_const<Const, search_all_view> * parent_;
BOOST_PARSER_SUBRANGE<I, S> r_;
BOOST_PARSER_SUBRANGE<I> curr_;
I next_it_;
};
template<bool Const>
friend struct iterator;
private:
V base_;
parser_interface<Parser, GlobalState, ErrorHandler> parser_;
parser_interface<SkipParser> skip_;
trace trace_mode_;
};
// deduction guides
template<
typename V,
typename Parser,
typename GlobalState,
typename ErrorHandler,
typename SkipParser>
search_all_view(
V &&,
parser_interface<Parser, GlobalState, ErrorHandler>,
parser_interface<SkipParser>,
trace)
-> search_all_view<
detail::text::detail::all_t<V>,
Parser,
GlobalState,
ErrorHandler,
SkipParser>;
template<
typename V,
typename Parser,
typename GlobalState,
typename ErrorHandler,
typename SkipParser>
search_all_view(
V &&,
parser_interface<Parser, GlobalState, ErrorHandler>,
parser_interface<SkipParser>)
-> search_all_view<
detail::text::detail::all_t<V>,
Parser,
GlobalState,
ErrorHandler,
SkipParser>;
template<
typename V,
typename Parser,
typename GlobalState,
typename ErrorHandler>
search_all_view(
V &&, parser_interface<Parser, GlobalState, ErrorHandler>, trace)
-> search_all_view<
detail::text::detail::all_t<V>,
Parser,
GlobalState,
ErrorHandler,
parser_interface<eps_parser<detail::phony>>>;
template<
typename V,
typename Parser,
typename GlobalState,
typename ErrorHandler>
search_all_view(V &&, parser_interface<Parser, GlobalState, ErrorHandler>)
-> search_all_view<
detail::text::detail::all_t<V>,
Parser,
GlobalState,
ErrorHandler,
parser_interface<eps_parser<detail::phony>>>;
namespace detail {
template<
typename V,
typename Parser,
typename GlobalState,
typename ErrorHandler,
typename SkipParser>
using search_all_view_expr = decltype(search_all_view<
V,
Parser,
GlobalState,
ErrorHandler,
SkipParser>(
std::declval<V>(),
std::declval<
parser_interface<Parser, GlobalState, ErrorHandler> const &>(),
std::declval<parser_interface<SkipParser> const &>(),
trace::on));
template<
typename V,
typename Parser,
typename GlobalState,
typename ErrorHandler,
typename SkipParser>
constexpr bool can_search_all_view = is_detected_v<
search_all_view_expr,
V,
Parser,
GlobalState,
ErrorHandler,
SkipParser>;
struct search_all_impl
{
#if BOOST_PARSER_USE_CONCEPTS
template<
parsable_range_like R,
typename Parser,
typename GlobalState,
typename ErrorHandler,
typename SkipParser>
requires(
std::is_pointer_v<std::remove_cvref_t<R>> ||
std::ranges::viewable_range<R>) &&
can_search_all_view<
to_range_t<R>,
Parser,
GlobalState,
ErrorHandler,
SkipParser>
// clang-format off
[[nodiscard]] constexpr auto operator()(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const &
parser,
parser_interface<SkipParser> const & skip,
trace trace_mode = trace::off) const
// clang-format on
{
return search_all_view(
to_range<R>::call((R &&) r), parser, skip, trace_mode);
}
template<
parsable_range_like R,
typename Parser,
typename GlobalState,
typename ErrorHandler>
requires(
std::is_pointer_v<std::remove_cvref_t<R>> ||
std::ranges::viewable_range<R>) &&
can_search_all_view<
to_range_t<R>,
Parser,
GlobalState,
ErrorHandler,
parser_interface<eps_parser<detail::phony>>>
// clang-format off
[[nodiscard]] constexpr auto operator()(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const &
parser,
trace trace_mode = trace::off) const
// clang-format on
{
return (*this)(
(R &&) r,
parser,
parser_interface<eps_parser<detail::phony>>{},
trace_mode);
}
#else
template<
typename R,
typename Parser,
typename GlobalState,
typename ErrorHandler,
typename SkipParser =
parser_interface<eps_parser<detail::phony>>,
typename Trace = trace,
typename Enable = std::enable_if_t<is_parsable_range_like_v<R>>>
[[nodiscard]] constexpr auto operator()(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const &
parser,
SkipParser const & skip = SkipParser{},
Trace trace_mode = Trace{}) const
{
if constexpr (
std::
is_same_v<detail::remove_cv_ref_t<SkipParser>, trace> &&
std::is_same_v<Trace, trace>) {
// (r, parser, trace) case
return impl(
(R &&) r,
parser,
parser_interface<eps_parser<detail::phony>>{},
skip);
} else if constexpr (
detail::is_parser_iface<SkipParser> &&
std::is_same_v<Trace, trace>) {
// (r, parser, skip, trace) case
return impl((R &&) r, parser, skip, trace_mode);
} else {
static_assert(
sizeof(R) == 1 && false,
"Only the signatures search_all(R, parser, skip, trace "
"= trace::off) and search_all(R, parser, trace = "
"trace::off) are supported.");
}
}
private:
template<
typename R,
typename Parser,
typename GlobalState,
typename ErrorHandler,
typename SkipParser>
[[nodiscard]] constexpr auto impl(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const &
parser,
parser_interface<SkipParser> const & skip,
trace trace_mode = trace::off) const
{
return search_all_view(
to_range<R>::call((R &&) r), parser, skip, trace_mode);
}
#endif
};
}
/** A range adaptor object ([range.adaptor.object]). Given subexpressions
`E` and `P`, `Q`, and `R`, each of the expressions `search_all(E, P)`,
`search_all(E, P, Q)`, and `search_all(E, P, Q, R)` are
expression-equivalent to `search_all_view(E, P)`, `search_all_view(E,
P, Q)`, and `search_all_view(E, P, Q, R)`, respectively. */
inline constexpr detail::stl_interfaces::adaptor<detail::search_all_impl>
search_all = detail::search_all_impl{};
}
#if BOOST_PARSER_USE_CONCEPTS
template<
typename V,
typename Parser,
typename GlobalState,
typename ErrorHandler,
typename SkipParser>
constexpr bool std::ranges::enable_borrowed_range<
boost::parser::
search_all_view<V, Parser, GlobalState, ErrorHandler, SkipParser>> =
std::ranges::enable_borrowed_range<V>;
#endif
#endif

View File

@@ -0,0 +1,399 @@
#ifndef BOOST_PARSER_SPLIT_HPP
#define BOOST_PARSER_SPLIT_HPP
#include <boost/parser/search.hpp>
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. */
template<
#if BOOST_PARSER_USE_CONCEPTS
std::ranges::viewable_range V,
#else
typename V,
#endif
typename Parser,
typename GlobalState,
typename ErrorHandler,
typename SkipParser>
struct split_view
: detail::stl_interfaces::view_interface<
split_view<V, Parser, GlobalState, ErrorHandler, SkipParser>>
{
constexpr split_view() = default;
constexpr split_view(
V base,
parser_interface<Parser, GlobalState, ErrorHandler> const & parser,
parser_interface<SkipParser> const & skip,
trace trace_mode = trace::off) :
base_(std::move(base)),
parser_(parser),
skip_(skip),
trace_mode_(trace_mode)
{}
constexpr split_view(
V base,
parser_interface<Parser, GlobalState, ErrorHandler> const & parser,
trace trace_mode = trace::off) :
base_(std::move(base)),
parser_(parser),
skip_(),
trace_mode_(trace_mode)
{}
constexpr V base() const &
#if BOOST_PARSER_USE_CONCEPTS
requires std::copy_constructible<V>
#endif
{
return base_;
}
constexpr V base() && { return std::move(base_); }
constexpr auto begin() { return iterator<false>{this}; }
constexpr auto end() { return sentinel<false>{}; }
constexpr auto begin() const
#if BOOST_PARSER_USE_CONCEPTS
requires std::ranges::range<const V>
#endif
{
return iterator<true>{this};
}
constexpr auto end() const
#if BOOST_PARSER_USE_CONCEPTS
requires std::ranges::range<const V>
#endif
{
return sentinel<true>{};
}
template<bool Const>
struct sentinel
{};
template<bool Const>
struct iterator
: detail::stl_interfaces::proxy_iterator_interface<
iterator<Const>,
std::forward_iterator_tag,
BOOST_PARSER_SUBRANGE<
detail::iterator_t<detail::maybe_const<Const, V>>>>
{
using I = detail::iterator_t<detail::maybe_const<Const, V>>;
using S = detail::sentinel_t<detail::maybe_const<Const, V>>;
constexpr iterator() = default;
constexpr iterator(
detail::maybe_const<Const, split_view> * parent) :
parent_(parent),
r_(parent_->base_.begin(), parent_->base_.end()),
curr_(r_.begin(), r_.begin()),
next_it_(r_.begin()),
next_follows_match_(false)
{
++*this;
}
constexpr iterator & operator++()
{
if (next_it_ == r_.end() && next_follows_match_) {
curr_ = BOOST_PARSER_SUBRANGE(next_it_, next_it_);
next_follows_match_ = false;
return *this;
}
r_ = BOOST_PARSER_SUBRANGE<I, S>(next_it_, r_.end());
auto const curr_match = parser::search(
r_, parent_->parser_, parent_->skip_, parent_->trace_mode_);
curr_ = BOOST_PARSER_SUBRANGE(next_it_, curr_match.begin());
next_it_ = curr_match.end();
next_follows_match_ = !curr_match.empty();
return *this;
}
constexpr BOOST_PARSER_SUBRANGE<I> operator*() const
{
return curr_;
}
friend constexpr bool operator==(iterator lhs, iterator rhs)
{
return lhs.r_.begin() == rhs.r_.begin();
}
friend constexpr bool operator==(iterator it, sentinel<Const>)
{
return it.r_.begin() == it.r_.end();
}
using base_type = detail::stl_interfaces::proxy_iterator_interface<
iterator,
std::forward_iterator_tag,
BOOST_PARSER_SUBRANGE<I>>;
using base_type::operator++;
private:
detail::maybe_const<Const, split_view> * parent_;
BOOST_PARSER_SUBRANGE<I, S> r_;
BOOST_PARSER_SUBRANGE<I> curr_;
I next_it_;
bool next_follows_match_;
};
template<bool Const>
friend struct iterator;
private:
V base_;
parser_interface<Parser, GlobalState, ErrorHandler> parser_;
parser_interface<SkipParser> skip_;
trace trace_mode_;
};
// deduction guides
template<
typename V,
typename Parser,
typename GlobalState,
typename ErrorHandler,
typename SkipParser>
split_view(
V &&,
parser_interface<Parser, GlobalState, ErrorHandler>,
parser_interface<SkipParser>,
trace)
-> split_view<
detail::text::detail::all_t<V>,
Parser,
GlobalState,
ErrorHandler,
SkipParser>;
template<
typename V,
typename Parser,
typename GlobalState,
typename ErrorHandler,
typename SkipParser>
split_view(
V &&,
parser_interface<Parser, GlobalState, ErrorHandler>,
parser_interface<SkipParser>)
-> split_view<
detail::text::detail::all_t<V>,
Parser,
GlobalState,
ErrorHandler,
SkipParser>;
template<
typename V,
typename Parser,
typename GlobalState,
typename ErrorHandler>
split_view(
V &&, parser_interface<Parser, GlobalState, ErrorHandler>, trace)
-> split_view<
detail::text::detail::all_t<V>,
Parser,
GlobalState,
ErrorHandler,
parser_interface<eps_parser<detail::phony>>>;
template<
typename V,
typename Parser,
typename GlobalState,
typename ErrorHandler>
split_view(V &&, parser_interface<Parser, GlobalState, ErrorHandler>)
-> split_view<
detail::text::detail::all_t<V>,
Parser,
GlobalState,
ErrorHandler,
parser_interface<eps_parser<detail::phony>>>;
namespace detail {
template<
typename V,
typename Parser,
typename GlobalState,
typename ErrorHandler,
typename SkipParser>
using split_view_expr = decltype(split_view<
V,
Parser,
GlobalState,
ErrorHandler,
SkipParser>(
std::declval<V>(),
std::declval<
parser_interface<Parser, GlobalState, ErrorHandler> const &>(),
std::declval<parser_interface<SkipParser> const &>(),
trace::on));
template<
typename V,
typename Parser,
typename GlobalState,
typename ErrorHandler,
typename SkipParser>
constexpr bool can_split_view = is_detected_v<
split_view_expr,
V,
Parser,
GlobalState,
ErrorHandler,
SkipParser>;
struct split_impl
{
#if BOOST_PARSER_USE_CONCEPTS
template<
parsable_range_like R,
typename Parser,
typename GlobalState,
typename ErrorHandler,
typename SkipParser>
requires(
std::is_pointer_v<std::remove_cvref_t<R>> ||
std::ranges::viewable_range<R>) &&
can_split_view<
to_range_t<R>,
Parser,
GlobalState,
ErrorHandler,
SkipParser>
// clang-format off
[[nodiscard]] constexpr auto operator()(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const &
parser,
parser_interface<SkipParser> const & skip,
trace trace_mode = trace::off) const
// clang-format on
{
return split_view(
to_range<R>::call((R &&) r), parser, skip, trace_mode);
}
template<
parsable_range_like R,
typename Parser,
typename GlobalState,
typename ErrorHandler>
requires(
std::is_pointer_v<std::remove_cvref_t<R>> ||
std::ranges::viewable_range<R>) &&
can_split_view<
to_range_t<R>,
Parser,
GlobalState,
ErrorHandler,
parser_interface<eps_parser<detail::phony>>>
// clang-format off
[[nodiscard]] constexpr auto operator()(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const &
parser,
trace trace_mode = trace::off) const
// clang-format on
{
return (*this)(
(R &&) r,
parser,
parser_interface<eps_parser<detail::phony>>{},
trace_mode);
}
#else
template<
typename R,
typename Parser,
typename GlobalState,
typename ErrorHandler,
typename SkipParser =
parser_interface<eps_parser<detail::phony>>,
typename Trace = trace,
typename Enable = std::enable_if_t<is_parsable_range_like_v<R>>>
[[nodiscard]] constexpr auto operator()(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const &
parser,
SkipParser const & skip = SkipParser{},
Trace trace_mode = Trace{}) const
{
if constexpr (
std::
is_same_v<detail::remove_cv_ref_t<SkipParser>, trace> &&
std::is_same_v<Trace, trace>) {
// (r, parser, trace) case
return impl(
(R &&) r,
parser,
parser_interface<eps_parser<detail::phony>>{},
skip);
} else if constexpr (
detail::is_parser_iface<SkipParser> &&
std::is_same_v<Trace, trace>) {
// (r, parser, skip, trace) case
return impl((R &&) r, parser, skip, trace_mode);
} else {
static_assert(
sizeof(R) == 1 && false,
"Only the signatures split(R, parser, skip, trace "
"= trace::off) and split(R, parser, trace = "
"trace::off) are supported.");
}
}
private:
template<
typename R,
typename Parser,
typename GlobalState,
typename ErrorHandler,
typename SkipParser>
[[nodiscard]] constexpr auto impl(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const &
parser,
parser_interface<SkipParser> const & skip,
trace trace_mode = trace::off) const
{
return split_view(
to_range<R>::call((R &&) r), parser, skip, trace_mode);
}
#endif
};
}
/** A range adaptor object ([range.adaptor.object]). Given subexpressions
`E` and `P`, `Q`, and `R`, each of the expressions `split(E, P)`,
`split(E, P, Q)`, and `split(E, P, Q, R)` are
expression-equivalent to `split_view(E, P)`, `split_view(E,
P, Q)`, and `split_view(E, P, Q, R)`, respectively. */
inline constexpr detail::stl_interfaces::adaptor<detail::split_impl>
split = detail::split_impl{};
}
#if BOOST_PARSER_USE_CONCEPTS
template<
typename V,
typename Parser,
typename GlobalState,
typename ErrorHandler,
typename SkipParser>
constexpr bool std::ranges::enable_borrowed_range<
boost::parser::
split_view<V, Parser, GlobalState, ErrorHandler, SkipParser>> =
std::ranges::enable_borrowed_range<V>;
#endif
#endif

View File

@@ -0,0 +1,841 @@
#ifndef BOOST_PARSER_TRANSFORM_REPLACE_HPP
#define BOOST_PARSER_TRANSFORM_REPLACE_HPP
#include <boost/parser/replace.hpp>
#if (!defined(_MSC_VER) || BOOST_PARSER_USE_CONCEPTS)
namespace boost::parser {
namespace detail {
template<typename F>
constexpr bool tidy_func = std::is_trivially_copyable_v<F> &&
sizeof(F) <= sizeof(void *) * 2;
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>>(),
ws,
detail::default_flags(),
std::declval<bool &>()));
template<typename R, typename Parser>
using range_attr_t = attr_type<iterator_t<R>, sentinel_t<R>, Parser>;
#if BOOST_PARSER_USE_CONCEPTS
// clang-format off
template<typename F, typename V, typename Parser>
concept transform_replacement_for =
std::regular_invocable<F &, range_attr_t<V, Parser>> &&
detail::replacement_for<
std::invoke_result_t<F &, range_attr_t<V, Parser>>, V> &&
(detail::range_utf_format_v<V> ==
detail::range_utf_format_v<
std::invoke_result_t<F &, range_attr_t<V, Parser>>>);
// clang-format on
#else
template<typename F, typename V, typename Parser>
using transform_replacement_for_expr = decltype(std::declval<F &>()(
std::declval<range_attr_t<V, Parser>>()));
template<
typename F,
typename V,
typename Parser,
bool = is_detected_v<transform_replacement_for_expr, F, V, Parser>>
constexpr bool transform_replacement_for = false;
template<typename F, typename V, typename Parser>
constexpr bool transform_replacement_for<F, V, Parser, true> =
replacement_for<transform_replacement_for_expr<F, V, Parser>, V> &&
(detail::range_utf_format_v<V> ==
detail::range_utf_format_v<
transform_replacement_for_expr<F, V, Parser>>);
#endif
template<
typename R,
typename Result,
text::format OtherFormat = range_utf_format_v<remove_cv_ref_t<R>>,
text::format Format = range_utf_format_v<remove_cv_ref_t<Result>>>
struct utf_wrap
{
template<typename R_ = R>
static auto call(R_ && r)
{
return (R_ &&) r | as_utf<OtherFormat>;
}
};
template<typename R, typename Result, text::format Format>
struct utf_wrap<R, Result, Format, Format>
{
template<typename R_ = R>
static R_ && call(R_ && r)
{
return (R_ &&) r;
}
};
template<typename R, typename Result>
struct utf_wrap<R, Result, no_format, no_format>
{
template<typename R_ = R>
static R_ && call(R_ && r)
{
return (R_ &&) r;
}
};
template<typename R, typename Result, text::format Format>
struct utf_wrap<R, Result, no_format, Format>
{
// Looks like you tried to use transform_replace() to replace
// subranges of chars with subranges of some UTF-N (for N=8, 16,
// or 32). Transcoding from char (unkown encoding) is not
// supported. Check the return type of your transform function.
};
template<typename R, typename Result, text::format Format>
struct utf_wrap<R, Result, Format, no_format>
{
// Looks like you tried to use transform_replace() to replace
// subranges of some UTF-N (for N=8, 16, or 32) with subranges of
// chars. Transcoding to char (unkown encoding) is not supported.
// Check the return type of your transform function.
};
template<typename T>
struct regular_ref_wrapper
{
regular_ref_wrapper() = default;
regular_ref_wrapper(T & ref) : ptr_(&ref) {}
T & get() const { return *ptr_; }
T * ptr_;
};
// This type catches results of calling F, to accommodate when F
// returns an rvalue or a type that needs to be transcoded to a
// different UTF.
template<typename R, typename F, typename Attr>
struct utf_rvalue_shim
{
using result_type = std::invoke_result_t<F &, Attr>;
using maybe_wrapped_result_type =
decltype(utf_wrap<R, result_type>::call(
std::declval<result_type>()));
static constexpr bool final_type_is_reference =
std::is_lvalue_reference_v<maybe_wrapped_result_type>;
using final_type = std::conditional_t<
final_type_is_reference,
regular_ref_wrapper<
std::remove_reference_t<maybe_wrapped_result_type>>,
remove_cv_ref_t<maybe_wrapped_result_type>>;
template<typename F_ = F>
utf_rvalue_shim(F_ && f) : f_((F_ &&) f)
{}
// These two only have return values for testing and metaprogramming
// purposes.
template<
bool B = final_type_is_reference,
typename Enable = std::enable_if_t<B>>
decltype(auto) operator()(Attr && attr) const
{
result_ = final_type(
utf_wrap<R, result_type>::call((*f_)((Attr &&) attr)));
return result_->get();
}
template<
bool B = final_type_is_reference,
typename Enable = std::enable_if_t<B>>
decltype(auto) operator()(Attr && attr)
{
result_ = final_type(
utf_wrap<R, result_type>::call((*f_)((Attr &&) attr)));
return result_->get();
}
template<
bool B = final_type_is_reference,
typename Enable = std::enable_if_t<!B>>
final_type & operator()(Attr && attr) const
{
result_ = utf_wrap<R, result_type>::call((*f_)((Attr &&) attr));
return *result_;
}
template<
bool B = final_type_is_reference,
typename Enable = std::enable_if_t<!B>>
final_type & operator()(Attr && attr)
{
result_ = utf_wrap<R, result_type>::call((*f_)((Attr &&) attr));
return *result_;
}
template<
bool B = final_type_is_reference,
typename Enable = std::enable_if_t<B>>
decltype(auto) get() const
{
return result_->get();
}
template<
bool B = final_type_is_reference,
typename Enable = std::enable_if_t<B>>
decltype(auto) get()
{
return result_->get();
}
template<
bool B = final_type_is_reference,
typename Enable = std::enable_if_t<!B>>
final_type & get() const
{
return *result_;
}
template<
bool B = final_type_is_reference,
typename Enable = std::enable_if_t<!B>>
final_type & get()
{
return *result_;
}
std::optional<F> f_;
mutable std::optional<final_type> result_;
};
template<
typename R,
typename Parser,
typename GlobalState,
typename ErrorHandler,
typename SkipParser>
auto attr_search_impl(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const & parser,
parser_interface<SkipParser> const & skip,
trace trace_mode)
{
auto first = text::detail::begin(r);
auto const last = text::detail::end(r);
auto match_first = first;
auto match_last = first;
auto before = [&match_first](auto & ctx) {
match_first = _where(ctx).begin();
};
auto after = [&match_last](auto & ctx) {
match_last = _where(ctx).begin();
};
auto const search_parser =
omit[*(char_ - parser)] >>
-lexeme[eps[before] >> parser::skip[parser] >> eps[after]];
using parse_result_outer = decltype(parser::prefix_parse(
first, last, search_parser, trace_mode));
static_assert(
!std::is_same_v<parse_result_outer, bool>,
"If you're seeing this error, you passed a parser to "
"transform_replace() that has no attribute. Please fix.");
using parse_result =
remove_cv_ref_t<decltype(**std::declval<parse_result_outer>())>;
using return_tuple = tuple<
decltype(BOOST_PARSER_SUBRANGE(first, first)),
parse_result>;
if (first == last) {
return return_tuple(
BOOST_PARSER_SUBRANGE(first, first), parse_result{});
}
if constexpr (std::is_same_v<SkipParser, eps_parser<phony>>) {
auto result = parser::prefix_parse(
first, last, search_parser, trace_mode);
if (*result) {
return return_tuple(
BOOST_PARSER_SUBRANGE(match_first, match_last),
std::move(**result));
}
} else {
auto result = parser::prefix_parse(
first, last, search_parser, skip, trace_mode);
if (*result) {
return return_tuple(
BOOST_PARSER_SUBRANGE(match_first, match_last),
std::move(**result));
}
}
return return_tuple(
BOOST_PARSER_SUBRANGE(first, first), parse_result{});
}
template<
typename R,
typename Parser,
typename GlobalState,
typename ErrorHandler,
typename SkipParser>
auto attr_search_repack_shim(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const & parser,
parser_interface<SkipParser> const & skip,
trace trace_mode)
{
using value_type = range_value_t<decltype(r)>;
if constexpr (std::is_same_v<value_type, char>) {
return detail::attr_search_impl(
(R &&) r, parser, skip, trace_mode);
} else {
auto r_unpacked = detail::text::unpack_iterator_and_sentinel(
text::detail::begin(r), text::detail::end(r));
auto result = detail::attr_search_impl(
r | as_utf32, parser, skip, trace_mode);
auto subrng = parser::get(result, llong<0>{});
auto & attr = parser::get(result, llong<1>{});
return tuple<
decltype(BOOST_PARSER_SUBRANGE(
r_unpacked.repack(subrng.begin().base()),
r_unpacked.repack(subrng.end().base()))),
remove_cv_ref_t<decltype(attr)>>(
BOOST_PARSER_SUBRANGE(
r_unpacked.repack(subrng.begin().base()),
r_unpacked.repack(subrng.end().base())),
std::move(attr));
}
}
}
/** 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. */
template<
#if BOOST_PARSER_USE_CONCEPTS
std::ranges::viewable_range V,
std::move_constructible F,
#else
typename V,
typename F,
#endif
typename Parser,
typename GlobalState,
typename ErrorHandler,
typename SkipParser
#if !BOOST_PARSER_USE_CONCEPTS
,
typename Enable =
std::enable_if_t<detail::transform_replacement_for<F, V, Parser>>
#endif
>
#if BOOST_PARSER_USE_CONCEPTS
requires detail::transform_replacement_for<F, V, Parser>
#endif
struct transform_replace_view
: detail::stl_interfaces::view_interface<transform_replace_view<
V,
F,
Parser,
GlobalState,
ErrorHandler,
SkipParser>>
{
private:
using attr_t = detail::range_attr_t<V, Parser>;
using replacement_range = std::invoke_result_t<F &, attr_t>;
public:
constexpr transform_replace_view() = default;
constexpr transform_replace_view(
V base,
parser_interface<Parser, GlobalState, ErrorHandler> const & parser,
parser_interface<SkipParser> const & skip,
F f,
trace trace_mode = trace::off) :
base_(std::move(base)),
f_(std::move(f)),
parser_(parser),
skip_(skip),
trace_mode_(trace_mode)
{}
constexpr transform_replace_view(
V base,
parser_interface<Parser, GlobalState, ErrorHandler> const & parser,
F f,
trace trace_mode = trace::off) :
base_(std::move(base)),
f_(std::move(f)),
parser_(parser),
skip_(),
trace_mode_(trace_mode)
{}
constexpr V base() const &
#if BOOST_PARSER_USE_CONCEPTS
requires std::copy_constructible<V>
#endif
{
return base_;
}
constexpr V base() && { return std::move(base_); }
constexpr F const & f() const { return *f_.f_; }
constexpr auto begin() { return iterator<false>{this}; }
constexpr auto end() { return sentinel<false>{}; }
constexpr auto begin() const
#if BOOST_PARSER_USE_CONCEPTS
requires std::ranges::range<const V>
#endif
{
return iterator<true>{this};
}
constexpr auto end() const
#if BOOST_PARSER_USE_CONCEPTS
requires std::ranges::range<const V>
#endif
{
return sentinel<true>{};
}
template<bool Const>
struct sentinel
{};
template<bool Const>
struct iterator
: detail::stl_interfaces::proxy_iterator_interface<
iterator<Const>,
std::forward_iterator_tag,
BOOST_PARSER_SUBRANGE<detail::either_iterator<
detail::maybe_const<Const, V>,
detail::maybe_const<Const, replacement_range>>>>
{
using I = detail::iterator_t<detail::maybe_const<Const, V>>;
using S = detail::sentinel_t<detail::maybe_const<Const, V>>;
using ref_t_iter = detail::either_iterator<
detail::maybe_const<Const, V>,
detail::maybe_const<Const, replacement_range>>;
using reference_type = BOOST_PARSER_SUBRANGE<ref_t_iter>;
constexpr iterator() = default;
constexpr iterator(
detail::maybe_const<Const, transform_replace_view> * parent) :
parent_(parent),
r_(parent_->base_.begin(), parent_->base_.end()),
curr_(r_.begin(), r_.begin()),
next_it_(r_.begin()),
in_match_(true)
{
++*this;
}
constexpr iterator & operator++()
{
if (in_match_) {
r_ = BOOST_PARSER_SUBRANGE<I, S>(next_it_, r_.end());
auto new_match_and_attr = detail::attr_search_repack_shim(
r_,
parent_->parser_,
parent_->skip_,
parent_->trace_mode_);
auto const new_match =
parser::get(new_match_and_attr, llong<0>{});
parent_->f_(
parser::get(std::move(new_match_and_attr), llong<1>{}));
if (new_match.begin() == curr_.end()) {
curr_ = new_match;
} else {
curr_ =
BOOST_PARSER_SUBRANGE(next_it_, new_match.begin());
in_match_ = false;
}
next_it_ = new_match.end();
} else {
if (!curr_.empty()) {
curr_ = BOOST_PARSER_SUBRANGE(curr_.end(), next_it_);
in_match_ = true;
}
if (curr_.empty())
r_ = BOOST_PARSER_SUBRANGE<I, S>(next_it_, r_.end());
}
return *this;
}
constexpr reference_type operator*() const
{
if (in_match_) {
return reference_type(
ref_t_iter(parent_->f_.get().begin()),
ref_t_iter(parent_->f_.get().end()));
} else {
return reference_type(
ref_t_iter(curr_.begin()), ref_t_iter(curr_.end()));
}
}
friend constexpr bool operator==(iterator lhs, iterator rhs)
{
return lhs.r_.begin() == rhs.r_.begin();
}
friend constexpr bool operator==(iterator it, sentinel<Const>)
{
return it.r_.begin() == it.r_.end();
}
using base_type = detail::stl_interfaces::proxy_iterator_interface<
iterator,
std::forward_iterator_tag,
reference_type>;
using base_type::operator++;
private:
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_;
};
template<bool Const>
friend struct iterator;
private:
V base_;
F f_;
parser_interface<Parser, GlobalState, ErrorHandler> parser_;
parser_interface<SkipParser> skip_;
trace trace_mode_;
};
// deduction guides
template<
typename V,
typename F,
typename Parser,
typename GlobalState,
typename ErrorHandler,
typename SkipParser>
transform_replace_view(
V &&,
parser_interface<Parser, GlobalState, ErrorHandler>,
parser_interface<SkipParser>,
F &&,
trace)
-> transform_replace_view<
detail::text::detail::all_t<V>,
detail::remove_cv_ref_t<F>,
Parser,
GlobalState,
ErrorHandler,
SkipParser>;
template<
typename V,
typename F,
typename Parser,
typename GlobalState,
typename ErrorHandler,
typename SkipParser>
transform_replace_view(
V &&,
parser_interface<Parser, GlobalState, ErrorHandler>,
parser_interface<SkipParser>,
F &&)
-> transform_replace_view<
detail::text::detail::all_t<V>,
detail::remove_cv_ref_t<F>,
Parser,
GlobalState,
ErrorHandler,
SkipParser>;
template<
typename V,
typename F,
typename Parser,
typename GlobalState,
typename ErrorHandler>
transform_replace_view(
V &&, parser_interface<Parser, GlobalState, ErrorHandler>, F &&, trace)
-> transform_replace_view<
detail::text::detail::all_t<V>,
detail::remove_cv_ref_t<F>,
Parser,
GlobalState,
ErrorHandler,
parser_interface<eps_parser<detail::phony>>>;
template<
typename V,
typename F,
typename Parser,
typename GlobalState,
typename ErrorHandler>
transform_replace_view(
V &&, parser_interface<Parser, GlobalState, ErrorHandler>, F &&)
-> transform_replace_view<
detail::text::detail::all_t<V>,
detail::remove_cv_ref_t<F>,
Parser,
GlobalState,
ErrorHandler,
parser_interface<eps_parser<detail::phony>>>;
namespace detail {
template<
typename V,
typename F,
typename Parser,
typename GlobalState,
typename ErrorHandler,
typename SkipParser>
using transform_replace_view_expr = decltype(transform_replace_view<
V,
F,
Parser,
GlobalState,
ErrorHandler,
SkipParser>(
std::declval<V>(),
std::declval<
parser_interface<Parser, GlobalState, ErrorHandler> const &>(),
std::declval<parser_interface<SkipParser> const &>(),
std::declval<F>(),
trace::on));
template<
typename V,
typename F,
typename Parser,
typename GlobalState,
typename ErrorHandler,
typename SkipParser>
constexpr bool can_transform_replace_view = is_detected_v<
transform_replace_view_expr,
V,
F,
Parser,
GlobalState,
ErrorHandler,
SkipParser>;
struct transform_replace_impl
{
#if BOOST_PARSER_USE_CONCEPTS
template<
parsable_range_like R,
std::move_constructible F,
typename Parser,
typename GlobalState,
typename ErrorHandler,
typename SkipParser>
requires
// clang-format off
(std::is_pointer_v<std::remove_cvref_t<R>> ||
std::ranges::viewable_range<R>) &&
std::regular_invocable<
F &,
range_attr_t<to_range_t<R>, Parser>> &&
// clang-format on
can_transform_replace_view<
to_range_t<R>,
utf_rvalue_shim<
to_range_t<R>,
std::remove_cvref_t<F>,
range_attr_t<to_range_t<R>, Parser>>,
Parser,
GlobalState,
ErrorHandler,
SkipParser>
// clang-format off
[[nodiscard]] constexpr auto operator()(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const &
parser,
parser_interface<SkipParser> const & skip,
F && f,
trace trace_mode = trace::off) const
// clang-format on
{
return transform_replace_view(
to_range<R>::call((R &&) r),
parser,
skip,
utf_rvalue_shim<
to_range_t<R>,
std::remove_cvref_t<F>,
range_attr_t<to_range_t<R>, Parser>>((F &&) f),
trace_mode);
}
template<
parsable_range_like R,
std::move_constructible F,
typename Parser,
typename GlobalState,
typename ErrorHandler>
requires
// clang-format off
(std::is_pointer_v<std::remove_cvref_t<R>> ||
std::ranges::viewable_range<R>) &&
std::regular_invocable<
F &,
range_attr_t<to_range_t<R>, Parser>> &&
// clang-format on
can_transform_replace_view<
to_range_t<R>,
utf_rvalue_shim<
to_range_t<R>,
std::remove_cvref_t<F>,
range_attr_t<to_range_t<R>, Parser>>,
Parser,
GlobalState,
ErrorHandler,
parser_interface<eps_parser<detail::phony>>>
// clang-format off
[[nodiscard]] constexpr auto operator()(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const &
parser,
F && f,
trace trace_mode = trace::off) const
// clang-format on
{
return (*this)(
(R &&) r,
parser,
parser_interface<eps_parser<detail::phony>>{},
(F &&) f,
trace_mode);
}
#else
template<
typename R,
typename Parser,
typename GlobalState,
typename ErrorHandler,
typename SkipParser,
typename F = trace,
typename Trace = trace,
typename Enable = std::enable_if_t<is_parsable_range_like_v<R>>>
[[nodiscard]] constexpr auto operator()(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const &
parser,
SkipParser && skip,
F && f = F{},
Trace trace_mode = Trace{}) const
{
if constexpr (
is_parser_iface<remove_cv_ref_t<SkipParser>> &&
std::is_invocable_v<
F &,
range_attr_t<to_range_t<R>, Parser>> &&
std::is_same_v<Trace, trace>) {
// (r, parser, skip, f, trace) case
return impl(
to_range<R>::call((R &&) r),
parser,
skip,
(F &&) f,
trace_mode);
} else if constexpr (
std::is_invocable_v<
SkipParser &,
range_attr_t<to_range_t<R>, Parser>> &&
std::is_same_v<remove_cv_ref_t<F>, trace> &&
std::is_same_v<Trace, trace>) {
// (r, parser, f, trace) case
return impl(
to_range<R>::call((R &&) r),
parser,
parser_interface<eps_parser<detail::phony>>{},
(SkipParser &&) skip,
f);
} else {
static_assert(
sizeof(R) == 1 && false,
"Only the signatures replace(R, parser, skip, "
"replcement trace = trace::off) and replace(R, parser, "
"f, trace = trace::off) are supported.");
}
}
private:
template<
typename R,
typename F,
typename Parser,
typename GlobalState,
typename ErrorHandler,
typename SkipParser>
[[nodiscard]] constexpr auto impl(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const &
parser,
parser_interface<SkipParser> const & skip,
F && f,
trace trace_mode = trace::off) const
{
return transform_replace_view(
(R &&) r,
parser,
skip,
utf_rvalue_shim<
R,
remove_cv_ref_t<F>,
range_attr_t<R, Parser>>((F &&) f),
trace_mode);
}
#endif
};
}
/** A range adaptor object ([range.adaptor.object]). Given subexpressions
`E` and `P`, `Q`, `R`, and 'S', each of the expressions `replace(E,
P)`, `replace(E, P, Q)`. `replace(E, P, Q, R)`, and `replace(E, P, Q,
R, S)` are expression-equivalent to `replace_view(E, P)`,
`replace_view(E, P, Q)`, `replace_view(E, P, Q, R)`, `replace_view(E,
P, Q, R, S)`, respectively. */
inline constexpr detail::stl_interfaces::adaptor<
detail::transform_replace_impl>
transform_replace = detail::transform_replace_impl{};
}
#if BOOST_PARSER_USE_CONCEPTS
template<
typename V,
typename F,
typename Parser,
typename GlobalState,
typename ErrorHandler,
typename SkipParser>
constexpr bool
std::ranges::enable_borrowed_range<boost::parser::transform_replace_view<
V,
F,
Parser,
GlobalState,
ErrorHandler,
SkipParser>> = std::ranges::enable_borrowed_range<V> &&
(std::ranges::enable_borrowed_range<F> ||
boost::parser::detail::tidy_func<F>);
#endif
#endif
#endif

View File

@@ -13,7 +13,7 @@ add_executable(parser_ parser.cpp)
set_property(TARGET parser_ PROPERTY CXX_STANDARD ${CXX_STD})
target_link_libraries(parser_ parser gtest gtest_main ${link_flags})
if (MSVC)
target_compile_options(parser_ PRIVATE /source-charset:utf-8)
target_compile_options(parser_ PRIVATE /source-charset:utf-8 /bigobj)
endif ()
add_test(NAME parser_ COMMAND parser_ ---gtest_catch_exceptions=1)
@@ -21,7 +21,7 @@ add_executable(parser_api parser_api.cpp)
set_property(TARGET parser_api PROPERTY CXX_STANDARD ${CXX_STD})
target_link_libraries(parser_api parser gtest gtest_main ${link_flags})
if (MSVC)
target_compile_options(parser_api PRIVATE /source-charset:utf-8)
target_compile_options(parser_api PRIVATE /source-charset:utf-8 /bigobj)
endif ()
add_test(NAME parser_api COMMAND parser_api ---gtest_catch_exceptions=1)
@@ -41,11 +41,17 @@ macro(add_test_executable name)
set_property(TARGET ${name} PROPERTY CXX_STANDARD ${CXX_STD})
target_link_libraries(${name} parser gtest gtest_main ${link_flags})
if (MSVC)
target_compile_options(${name} PRIVATE /source-charset:utf-8)
target_compile_options(${name} PRIVATE /source-charset:utf-8 /bigobj)
endif ()
add_test(NAME ${name} COMMAND ${name} --gtest_catch_exceptions=1)
endmacro()
#add_test_executable(quoted_string)
add_test_executable(all_t)
add_test_executable(search)
add_test_executable(split)
add_test_executable(replace)
add_test_executable(transform_replace)
add_test_executable(hl)
add_test_executable(aggr_tuple_assignment)
add_test_executable(parser_lazy_params)
@@ -58,6 +64,8 @@ add_test_executable(parser_symbol_table)
add_test_executable(tracing)
add_test_executable(parse_empty)
add_test_executable(tuple_aggregate)
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)

104
test/all_t.cpp Normal file
View File

@@ -0,0 +1,104 @@
/**
* 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>
#include <boost/parser/detail/text/detail/all_t.hpp>
#include <gtest/gtest.h>
using namespace boost::parser::detail::text;
TEST(all_t, basic)
{
std::string str;
std::string const const_str;
{
auto && result = detail::all(
BOOST_PARSER_SUBRANGE(const_str.begin(), const_str.end()));
static_assert(std::is_same_v<
decltype(result),
BOOST_PARSER_SUBRANGE<decltype(const_str.begin())> &&>);
}
{
auto && result = detail::all(str);
static_assert(
std::is_same_v<decltype(result), detail::ref_view<std::string> &&>);
}
{
auto && result = detail::all(const_str);
static_assert(std::is_same_v<
decltype(result),
detail::ref_view<std::string const> &&>);
}
{
auto && result = detail::all(std::string{});
static_assert(std::is_same_v<
decltype(result),
detail::owning_view<std::string> &&>);
}
static_assert(
std::is_same_v<
detail::all_t<BOOST_PARSER_SUBRANGE<decltype(const_str.begin())>>,
BOOST_PARSER_SUBRANGE<decltype(const_str.begin())>>);
static_assert(std::is_same_v<
detail::all_t<std::string &>,
detail::ref_view<std::string>>);
static_assert(std::is_same_v<
detail::all_t<std::string const &>,
detail::ref_view<std::string const>>);
static_assert(std::is_same_v<
detail::all_t<std::string &&>,
detail::owning_view<std::string>>);
}
TEST(all_t, array)
{
char str[] = "text";
char const const_str[] = "text";
static_assert(detail::range_<char[5]>);
static_assert(std::is_object_v<char[5]>);
detail::ref_view<char[5]> ref_view_(str);
{
auto && result = detail::all(
BOOST_PARSER_SUBRANGE(std::begin(const_str), std::end(const_str)));
static_assert(std::is_same_v<
decltype(result),
BOOST_PARSER_SUBRANGE<decltype(std::begin(const_str))> &&>);
}
{
auto && result = detail::all(str);
static_assert(
std::is_same_v<decltype(result), detail::ref_view<char[5]> &&>);
}
{
auto && result = detail::all(const_str);
static_assert(std::is_same_v<
decltype(result),
detail::ref_view<char const[5]> &&>);
}
static_assert(
std::is_same_v<
detail::all_t<BOOST_PARSER_SUBRANGE<decltype(std::begin(const_str))>>,
BOOST_PARSER_SUBRANGE<decltype(std::begin(const_str))>>);
static_assert(
std::
is_same_v<detail::all_t<char(&)[5]>, detail::ref_view<char[5]>>);
static_assert(std::is_same_v<
detail::all_t<char const(&)[5]>,
detail::ref_view<char const[5]>>);
}

908
test/class_type.cpp Normal file
View File

@@ -0,0 +1,908 @@
/**
* 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>
using namespace boost::parser;
TEST(class_type, std_type_from_tuple)
{
{
constexpr auto parser = lexeme[+(char_ - ' ')] >> uint_ >> uint_;
std::string out;
auto result = parse("something 4 5", parser, ws, out);
EXPECT_TRUE(result);
EXPECT_EQ(out, "thing");
}
{
constexpr auto p = lexeme[+(char_ - ' ')] >> uint_ >> uint_;
constexpr auto parser = +p >> -p;
std::vector<std::string> out;
auto result = parse("something 4 5 some 0 2", parser, ws, out);
EXPECT_TRUE(result);
EXPECT_EQ(out.size(), 2u);
EXPECT_EQ(out[0], "thing");
EXPECT_EQ(out[1], "so");
}
{
constexpr auto p = lexeme[+(char_ - ' ')] >> uint_ >> uint_;
constexpr auto parser = -p;
std::optional<std::string> out;
auto result = parse("something 4 5", parser, ws, out);
EXPECT_TRUE(result);
EXPECT_TRUE(out);
EXPECT_EQ(*out, "thing");
}
{
constexpr auto parser = lexeme[+(char_ - ' ')] >> uint_ >> uint_;
std::optional<std::string> out;
auto result = parse("something 4 5", parser, ws, out);
EXPECT_TRUE(result);
EXPECT_TRUE(out);
EXPECT_EQ(*out, "thing");
}
{
constexpr auto parser = uint_ >> char_ >> char_;
std::vector<std::string> out;
auto result = parse("4 ab", parser, ws, out);
EXPECT_TRUE(result);
EXPECT_EQ(out, std::vector<std::string>({"ab", "ab", "ab", "ab"}));
}
}
struct s0_rule_tag
{};
struct s1_rule_a_tag
{};
struct s1_rule_b_tag
{};
struct s2_rule_a_tag
{};
struct s2_rule_b_tag
{};
struct s0
{
s0() = default;
s0(int i, std::string str, std::vector<int> vec) :
i_(i), str_(std::move(str)), vec_(std::move(vec))
{}
int i() const { return i_; }
std::string str() const { return str_; }
std::vector<int> vec() const { return vec_; }
private:
int i_;
std::string str_;
std::vector<int> vec_;
};
using s0_tuple = tuple<int, std::string, std::vector<int>>;
auto s0_parser = "s0" >> int_ >> lexeme[+(char_ - ' ')] >> *int_;
callback_rule<s0_rule_tag, s0> s0_rule = "s0_rule";
auto s0_rule_def = s0_parser;
struct s1
{
s1() = default;
s1(int i, std::string str, std::vector<int> vec) :
i_(i), str_(std::move(str)), vec_(std::move(vec))
{}
s1(int i, double str, std::vector<int> vec) :
i_(i), str_(str), vec_(std::move(vec))
{}
int i() const { return i_; }
std::variant<std::string, double> str() const { return str_; }
std::vector<int> vec() const { return vec_; }
private:
int i_;
std::variant<std::string, double> str_;
std::vector<int> vec_;
};
using s1_tuple =
tuple<int, std::variant<std::string, double>, std::vector<int>>;
auto s1_parser_a = "s1" >> int_ >> lexeme[+(char_ - ' ')] >> *int_;
auto s1_parser_b = "s1" >> int_ >> double_ >> *int_;
callback_rule<s1_rule_a_tag, s1> s1_rule_a = "s1_rule_a";
callback_rule<s1_rule_b_tag, s1> s1_rule_b = "s1_rule_b";
auto s1_rule_a_def = s1_parser_a;
auto s1_rule_b_def = s1_parser_b;
struct s2
{
s2() = default;
s2(int i, std::string str, std::optional<std::vector<int>> vec) :
i_(i), str_(std::move(str)), vec_(std::move(vec))
{}
int i() const { return i_; }
std::string str() const { return str_; }
std::optional<std::vector<int>> vec() const { return vec_; }
private:
int i_;
std::string str_;
std::optional<std::vector<int>> vec_;
};
using s2_tuple = tuple<int, std::string, std::optional<std::vector<int>>>;
auto s2_parser_a = "s2" >> int_ >> lexeme[+(char_ - ' ')] >> *int_;
auto s2_parser_b = "s2" >> int_ >> lexeme[+(char_ - ' ')] >> -+int_;
callback_rule<s2_rule_a_tag, s2> s2_rule_a = "s2_rule_a";
callback_rule<s2_rule_b_tag, s2> s2_rule_b = "s2_rule_b";
auto s2_rule_a_def = s2_parser_a;
auto s2_rule_b_def = s2_parser_b;
BOOST_PARSER_DEFINE_RULES(s0_rule, s1_rule_a, s1_rule_b, s2_rule_a, s2_rule_b);
TEST(class_type, seq_parser_struct_rule)
{
////////////////////////////////////////////////////////////////////////////
// Parse-generated attribute.
{
std::optional<s0> result = parse("s0 42 text 1 2 3", s0_rule, ws);
EXPECT_TRUE(result);
s0 & struct_ = *result;
EXPECT_EQ(struct_.i(), 42);
EXPECT_EQ(struct_.str(), "text");
EXPECT_EQ(struct_.vec(), std::vector<int>({1, 2, 3}));
}
{
std::optional<s1> const result =
parse("s1 42 text 1 2 3", s1_rule_a, ws);
EXPECT_TRUE(result);
s1 const & struct_ = *result;
EXPECT_EQ(struct_.i(), 42);
EXPECT_EQ(struct_.str().index(), 0u);
EXPECT_EQ(std::get<0>(struct_.str()), "text");
EXPECT_EQ(struct_.vec(), std::vector<int>({1, 2, 3}));
}
{
std::optional<s1> const result =
parse("s1 42 13.0 1 2 3", s1_rule_b, ws);
EXPECT_TRUE(result);
s1 const & struct_ = *result;
EXPECT_EQ(struct_.i(), 42);
EXPECT_EQ(struct_.str().index(), 1u);
EXPECT_EQ(std::get<1>(struct_.str()), 13.0);
EXPECT_EQ(struct_.vec(), std::vector<int>({1, 2, 3}));
}
{
std::optional<s2> const result =
parse("s2 42 text 1 2 3", s2_rule_a, ws);
EXPECT_TRUE(result);
s2 const & struct_ = *result;
EXPECT_EQ(struct_.i(), 42);
EXPECT_EQ(struct_.str(), "text");
EXPECT_EQ(struct_.vec(), std::vector<int>({1, 2, 3}));
}
{
std::optional<s2> const result =
parse("s2 42 text 1 2 3", s2_rule_b, ws);
EXPECT_TRUE(result);
s2 const & struct_ = *result;
EXPECT_EQ(struct_.i(), 42);
EXPECT_EQ(struct_.str(), "text");
EXPECT_EQ(struct_.vec(), std::vector<int>({1, 2, 3}));
}
// Use the rule as part of a larger parse.
{
std::optional<tuple<int, s0>> const result =
parse("99 s0 42 text 1 2 3", int_ >> s0_rule, ws);
auto i_ = get(*result, llong<0>{});
EXPECT_EQ(i_, 99);
auto struct_ = get(*result, llong<1>{});
EXPECT_EQ(struct_.i(), 42);
EXPECT_EQ(struct_.str(), "text");
EXPECT_EQ(struct_.vec(), std::vector<int>({1, 2, 3}));
}
{
std::optional<tuple<int, s1>> const result =
parse("99 s1 42 text 1 2 3", int_ >> s1_rule_a, ws);
auto i_ = get(*result, llong<0>{});
EXPECT_EQ(i_, 99);
auto struct_ = get(*result, llong<1>{});
EXPECT_EQ(struct_.i(), 42);
EXPECT_EQ(struct_.str().index(), 0u);
EXPECT_EQ(std::get<0>(struct_.str()), "text");
EXPECT_EQ(struct_.vec(), std::vector<int>({1, 2, 3}));
}
{
std::optional<tuple<int, s1>> const result =
parse("99 s1 42 13.0 1 2 3", int_ >> s1_rule_b, ws);
auto i_ = get(*result, llong<0>{});
EXPECT_EQ(i_, 99);
auto struct_ = get(*result, llong<1>{});
EXPECT_EQ(struct_.i(), 42);
EXPECT_EQ(struct_.str().index(), 1u);
EXPECT_EQ(std::get<1>(struct_.str()), 13.0);
EXPECT_EQ(struct_.vec(), std::vector<int>({1, 2, 3}));
}
{
std::optional<tuple<int, s2>> const result =
parse("99 s2 42 text 1 2 3", int_ >> s2_rule_a, ws);
auto i_ = get(*result, llong<0>{});
EXPECT_EQ(i_, 99);
auto struct_ = get(*result, llong<1>{});
EXPECT_EQ(struct_.i(), 42);
EXPECT_EQ(struct_.str(), "text");
EXPECT_EQ(struct_.vec(), std::vector<int>({1, 2, 3}));
}
{
std::optional<tuple<int, s2>> const result =
parse("99 s2 42 text 1 2 3", int_ >> s2_rule_b, ws);
auto i_ = get(*result, llong<0>{});
EXPECT_EQ(i_, 99);
auto struct_ = get(*result, llong<1>{});
EXPECT_EQ(struct_.i(), 42);
EXPECT_EQ(struct_.str(), "text");
EXPECT_EQ(struct_.vec(), std::vector<int>({1, 2, 3}));
}
////////////////////////////////////////////////////////////////////////////
// Pass attribute to parse.
{
s0 struct_;
EXPECT_TRUE(parse("s0 42 text 1 2 3", s0_rule, ws, struct_));
EXPECT_EQ(struct_.i(), 42);
EXPECT_EQ(struct_.str(), "text");
EXPECT_EQ(struct_.vec(), std::vector<int>({1, 2, 3}));
}
{
s1 struct_;
EXPECT_TRUE(parse("s1 42 text 1 2 3", s1_rule_a, ws, struct_));
EXPECT_EQ(struct_.i(), 42);
EXPECT_EQ(struct_.str().index(), 0u);
EXPECT_EQ(std::get<0>(struct_.str()), "text");
EXPECT_EQ(struct_.vec(), std::vector<int>({1, 2, 3}));
}
{
s1 struct_;
EXPECT_TRUE(parse("s1 42 13.0 1 2 3", s1_rule_b, ws, struct_));
EXPECT_EQ(struct_.i(), 42);
EXPECT_EQ(struct_.str().index(), 1u);
EXPECT_EQ(std::get<1>(struct_.str()), 13.0);
EXPECT_EQ(struct_.vec(), std::vector<int>({1, 2, 3}));
}
{
s2 struct_;
EXPECT_TRUE(parse("s2 42 text 1 2 3", s2_rule_a, ws, struct_));
EXPECT_EQ(struct_.i(), 42);
EXPECT_EQ(struct_.str(), "text");
EXPECT_EQ(struct_.vec(), std::vector<int>({1, 2, 3}));
}
{
s2 struct_;
EXPECT_TRUE(parse("s2 42 text 1 2 3", s2_rule_b, ws, struct_));
EXPECT_EQ(struct_.i(), 42);
EXPECT_EQ(struct_.str(), "text");
EXPECT_EQ(struct_.vec(), std::vector<int>({1, 2, 3}));
}
// Use the rule as part of a larger parse.
{
tuple<int, s0> result;
EXPECT_TRUE(parse("99 s0 42 text 1 2 3", int_ >> s0_rule, ws, result));
auto i_ = get(result, llong<0>{});
EXPECT_EQ(i_, 99);
auto struct_ = get(result, llong<1>{});
EXPECT_EQ(struct_.i(), 42);
EXPECT_EQ(struct_.str(), "text");
EXPECT_EQ(struct_.vec(), std::vector<int>({1, 2, 3}));
}
{
tuple<int, s1> result;
EXPECT_TRUE(parse("99 s1 42 text 1 2 3", int_ >> s1_rule_a, ws, result));
auto i_ = get(result, llong<0>{});
EXPECT_EQ(i_, 99);
auto struct_ = get(result, llong<1>{});
EXPECT_EQ(struct_.i(), 42);
EXPECT_EQ(struct_.str().index(), 0u);
EXPECT_EQ(std::get<0>(struct_.str()), "text");
EXPECT_EQ(struct_.vec(), std::vector<int>({1, 2, 3}));
}
{
tuple<int, s1> result;
EXPECT_TRUE(parse("99 s1 42 13.0 1 2 3", int_ >> s1_rule_b, ws, result));
auto i_ = get(result, llong<0>{});
EXPECT_EQ(i_, 99);
auto struct_ = get(result, llong<1>{});
EXPECT_EQ(struct_.i(), 42);
EXPECT_EQ(struct_.str().index(), 1u);
EXPECT_EQ(std::get<1>(struct_.str()), 13.0);
EXPECT_EQ(struct_.vec(), std::vector<int>({1, 2, 3}));
}
{
tuple<int, s2> result;
EXPECT_TRUE(parse("99 s2 42 text 1 2 3", int_ >> s2_rule_a, ws, result));
auto i_ = get(result, llong<0>{});
EXPECT_EQ(i_, 99);
auto struct_ = get(result, llong<1>{});
EXPECT_EQ(struct_.i(), 42);
EXPECT_EQ(struct_.str(), "text");
EXPECT_EQ(struct_.vec(), std::vector<int>({1, 2, 3}));
}
{
tuple<int, s2> result;
EXPECT_TRUE(parse("99 s2 42 text 1 2 3", int_ >> s2_rule_b, ws, result));
auto i_ = get(result, llong<0>{});
EXPECT_EQ(i_, 99);
auto struct_ = get(result, llong<1>{});
EXPECT_EQ(struct_.i(), 42);
EXPECT_EQ(struct_.str(), "text");
EXPECT_EQ(struct_.vec(), std::vector<int>({1, 2, 3}));
}
}
TEST(class_type, repeated_seq_parser_struct_rule)
{
////////////////////////////////////////////////////////////////////////////
// Parse-generated attribute.
{
std::optional<std::vector<s0>> result =
parse("s0 42 text 1 2 3 s0 41 texty 1 3 2", *s0_rule, ws);
EXPECT_TRUE(result);
std::vector<s0> & structs_ = *result;
EXPECT_EQ(structs_[0].i(), 42);
EXPECT_EQ(structs_[0].str(), "text");
EXPECT_EQ(structs_[0].vec(), std::vector<int>({1, 2, 3}));
EXPECT_EQ(structs_[1].i(), 41);
EXPECT_EQ(structs_[1].str(), "texty");
EXPECT_EQ(structs_[1].vec(), std::vector<int>({1, 3, 2}));
}
{
std::optional<std::vector<s1>> const result =
parse("s1 42 text 1 2 3 s1 41 texty 1 3 2", *s1_rule_a, ws);
EXPECT_TRUE(result);
std::vector<s1> const & structs_ = *result;
EXPECT_EQ(structs_[0].i(), 42);
EXPECT_EQ(structs_[0].str().index(), 0u);
EXPECT_EQ(std::get<0>(structs_[0].str()), "text");
EXPECT_EQ(structs_[0].vec(), std::vector<int>({1, 2, 3}));
EXPECT_EQ(structs_[1].i(), 41);
EXPECT_EQ(structs_[1].str().index(), 0u);
EXPECT_EQ(std::get<0>(structs_[1].str()), "texty");
EXPECT_EQ(structs_[1].vec(), std::vector<int>({1, 3, 2}));
}
{
std::optional<std::vector<s1>> const result =
parse("s1 42 13.0 1 2 3 s1 41 12.0 1 3 2", *s1_rule_b, ws);
EXPECT_TRUE(result);
std::vector<s1> const & structs_ = *result;
EXPECT_EQ(structs_[0].i(), 42);
EXPECT_EQ(structs_[0].str().index(), 1u);
EXPECT_EQ(std::get<1>(structs_[0].str()), 13.0);
EXPECT_EQ(structs_[0].vec(), std::vector<int>({1, 2, 3}));
EXPECT_EQ(structs_[1].i(), 41);
EXPECT_EQ(structs_[1].str().index(), 1u);
EXPECT_EQ(std::get<1>(structs_[1].str()), 12.0);
EXPECT_EQ(structs_[1].vec(), std::vector<int>({1, 3, 2}));
}
{
std::optional<std::vector<s2>> const result =
parse("s2 42 text 1 2 3 s2 41 texty 1 3 2", *s2_rule_a, ws);
EXPECT_TRUE(result);
std::vector<s2> const & structs_ = *result;
EXPECT_EQ(structs_[0].i(), 42);
EXPECT_EQ(structs_[0].str(), "text");
EXPECT_EQ(structs_[0].vec(), std::vector<int>({1, 2, 3}));
EXPECT_EQ(structs_[1].i(), 41);
EXPECT_EQ(structs_[1].str(), "texty");
EXPECT_EQ(structs_[1].vec(), std::vector<int>({1, 3, 2}));
}
{
std::optional<std::vector<s2>> const result =
parse("s2 42 text 1 2 3 s2 41 texty 1 3 2", *s2_rule_b, ws);
EXPECT_TRUE(result);
std::vector<s2> const & structs_ = *result;
EXPECT_EQ(structs_[0].i(), 42);
EXPECT_EQ(structs_[0].str(), "text");
EXPECT_EQ(structs_[0].vec(), std::vector<int>({1, 2, 3}));
EXPECT_EQ(structs_[1].i(), 41);
EXPECT_EQ(structs_[1].str(), "texty");
EXPECT_EQ(structs_[1].vec(), std::vector<int>({1, 3, 2}));
}
// Use the rule as part of a larger parse.
{
std::optional<tuple<int, std::vector<s0>>> const result =
parse("99 s0 42 text 1 2 3 s0 41 texty 1 3 2", int_ >> *s0_rule, ws);
auto i_ = get(*result, llong<0>{});
EXPECT_EQ(i_, 99);
auto structs_ = get(*result, llong<1>{});
EXPECT_EQ(structs_[0].i(), 42);
EXPECT_EQ(structs_[0].str(), "text");
EXPECT_EQ(structs_[0].vec(), std::vector<int>({1, 2, 3}));
EXPECT_EQ(structs_[1].i(), 41);
EXPECT_EQ(structs_[1].str(), "texty");
EXPECT_EQ(structs_[1].vec(), std::vector<int>({1, 3, 2}));
}
{
std::optional<tuple<int, std::vector<s1>>> const result =
parse("99 s1 42 text 1 2 3 s1 41 texty 1 3 2", int_ >> *s1_rule_a, ws);
auto i_ = get(*result, llong<0>{});
EXPECT_EQ(i_, 99);
auto structs_ = get(*result, llong<1>{});
EXPECT_EQ(structs_[0].i(), 42);
EXPECT_EQ(structs_[0].str().index(), 0u);
EXPECT_EQ(std::get<0>(structs_[0].str()), "text");
EXPECT_EQ(structs_[0].vec(), std::vector<int>({1, 2, 3}));
EXPECT_EQ(structs_[1].i(), 41);
EXPECT_EQ(structs_[1].str().index(), 0u);
EXPECT_EQ(std::get<0>(structs_[1].str()), "texty");
EXPECT_EQ(structs_[1].vec(), std::vector<int>({1, 3, 2}));
}
{
std::optional<tuple<int, std::vector<s1>>> const result =
parse("99 s1 42 13.0 1 2 3 s1 41 12.0 1 3 2", int_ >> *s1_rule_b, ws);
auto i_ = get(*result, llong<0>{});
EXPECT_EQ(i_, 99);
auto structs_ = get(*result, llong<1>{});
EXPECT_EQ(structs_[0].i(), 42);
EXPECT_EQ(structs_[0].str().index(), 1u);
EXPECT_EQ(std::get<1>(structs_[0].str()), 13.0);
EXPECT_EQ(structs_[0].vec(), std::vector<int>({1, 2, 3}));
EXPECT_EQ(structs_[1].i(), 41);
EXPECT_EQ(structs_[1].str().index(), 1u);
EXPECT_EQ(std::get<1>(structs_[1].str()), 12.0);
EXPECT_EQ(structs_[1].vec(), std::vector<int>({1, 3, 2}));
}
{
std::optional<tuple<int, std::vector<s2>>> const result =
parse("99 s2 42 text 1 2 3 s2 41 texty 1 3 2", int_ >> *s2_rule_a, ws);
auto i_ = get(*result, llong<0>{});
EXPECT_EQ(i_, 99);
auto structs_ = get(*result, llong<1>{});
EXPECT_EQ(structs_[0].i(), 42);
EXPECT_EQ(structs_[0].str(), "text");
EXPECT_EQ(structs_[0].vec(), std::vector<int>({1, 2, 3}));
EXPECT_EQ(structs_[1].i(), 41);
EXPECT_EQ(structs_[1].str(), "texty");
EXPECT_EQ(structs_[1].vec(), std::vector<int>({1, 3, 2}));
}
{
std::optional<tuple<int, std::vector<s2>>> const result =
parse("99 s2 42 text 1 2 3 s2 41 texty 1 3 2", int_ >> *s2_rule_b, ws);
auto i_ = get(*result, llong<0>{});
EXPECT_EQ(i_, 99);
auto structs_ = get(*result, llong<1>{});
EXPECT_EQ(structs_[0].i(), 42);
EXPECT_EQ(structs_[0].str(), "text");
EXPECT_EQ(structs_[0].vec(), std::vector<int>({1, 2, 3}));
EXPECT_EQ(structs_[1].i(), 41);
EXPECT_EQ(structs_[1].str(), "texty");
EXPECT_EQ(structs_[1].vec(), std::vector<int>({1, 3, 2}));
}
////////////////////////////////////////////////////////////////////////////
// Pass attribute to parse.
{
std::vector<s0> structs_;
EXPECT_TRUE(parse(
"s0 42 text 1 2 3 s0 41 texty 1 3 2", *s0_rule, ws, structs_));
EXPECT_EQ(structs_[0].i(), 42);
EXPECT_EQ(structs_[0].str(), "text");
EXPECT_EQ(structs_[0].vec(), std::vector<int>({1, 2, 3}));
EXPECT_EQ(structs_[1].i(), 41);
EXPECT_EQ(structs_[1].str(), "texty");
EXPECT_EQ(structs_[1].vec(), std::vector<int>({1, 3, 2}));
}
{
std::vector<s1> structs_;
EXPECT_TRUE(parse(
"s1 42 text 1 2 3 s1 41 texty 1 3 2", *s1_rule_a, ws, structs_));
EXPECT_EQ(structs_[0].i(), 42);
EXPECT_EQ(structs_[0].str().index(), 0u);
EXPECT_EQ(std::get<0>(structs_[0].str()), "text");
EXPECT_EQ(structs_[0].vec(), std::vector<int>({1, 2, 3}));
EXPECT_EQ(structs_[1].i(), 41);
EXPECT_EQ(structs_[1].str().index(), 0u);
EXPECT_EQ(std::get<0>(structs_[1].str()), "texty");
EXPECT_EQ(structs_[1].vec(), std::vector<int>({1, 3, 2}));
}
{
std::vector<s1> structs_;
EXPECT_TRUE(parse(
"s1 42 13.0 1 2 3 s1 41 12.0 1 3 2", *s1_rule_b, ws, structs_));
EXPECT_EQ(structs_[0].i(), 42);
EXPECT_EQ(structs_[0].str().index(), 1u);
EXPECT_EQ(std::get<1>(structs_[0].str()), 13.0);
EXPECT_EQ(structs_[0].vec(), std::vector<int>({1, 2, 3}));
EXPECT_EQ(structs_[1].i(), 41);
EXPECT_EQ(structs_[1].str().index(), 1u);
EXPECT_EQ(std::get<1>(structs_[1].str()), 12.0);
EXPECT_EQ(structs_[1].vec(), std::vector<int>({1, 3, 2}));
}
{
std::vector<s2> structs_;
EXPECT_TRUE(parse(
"s2 42 text 1 2 3 s2 41 texty 1 3 2", *s2_rule_a, ws, structs_));
EXPECT_EQ(structs_[0].i(), 42);
EXPECT_EQ(structs_[0].str(), "text");
EXPECT_EQ(structs_[0].vec(), std::vector<int>({1, 2, 3}));
EXPECT_EQ(structs_[1].i(), 41);
EXPECT_EQ(structs_[1].str(), "texty");
EXPECT_EQ(structs_[1].vec(), std::vector<int>({1, 3, 2}));
}
{
std::vector<s2> structs_;
EXPECT_TRUE(parse(
"s2 42 text 1 2 3 s2 41 texty 1 3 2", *s2_rule_b, ws, structs_));
EXPECT_EQ(structs_[0].i(), 42);
EXPECT_EQ(structs_[0].str(), "text");
EXPECT_EQ(structs_[0].vec(), std::vector<int>({1, 2, 3}));
EXPECT_EQ(structs_[1].i(), 41);
EXPECT_EQ(structs_[1].str(), "texty");
EXPECT_EQ(structs_[1].vec(), std::vector<int>({1, 3, 2}));
}
// Use the rule as part of a larger parse.
{
tuple<int, std::vector<s0>> result;
EXPECT_TRUE(parse(
"99 s0 42 text 1 2 3 s0 41 texty 1 3 2",
int_ >> *s0_rule,
ws,
result));
auto i_ = get(result, llong<0>{});
EXPECT_EQ(i_, 99);
auto structs_ = get(result, llong<1>{});
EXPECT_EQ(structs_[0].i(), 42);
EXPECT_EQ(structs_[0].str(), "text");
EXPECT_EQ(structs_[0].vec(), std::vector<int>({1, 2, 3}));
EXPECT_EQ(structs_[1].i(), 41);
EXPECT_EQ(structs_[1].str(), "texty");
EXPECT_EQ(structs_[1].vec(), std::vector<int>({1, 3, 2}));
}
{
tuple<int, std::vector<s1>> result;
EXPECT_TRUE(parse(
"99 s1 42 text 1 2 3 s1 41 texty 1 3 2",
int_ >> *s1_rule_a,
ws,
result));
auto i_ = get(result, llong<0>{});
EXPECT_EQ(i_, 99);
auto structs_ = get(result, llong<1>{});
EXPECT_EQ(structs_[0].i(), 42);
EXPECT_EQ(structs_[0].str().index(), 0u);
EXPECT_EQ(std::get<0>(structs_[0].str()), "text");
EXPECT_EQ(structs_[0].vec(), std::vector<int>({1, 2, 3}));
EXPECT_EQ(structs_[1].i(), 41);
EXPECT_EQ(structs_[1].str().index(), 0u);
EXPECT_EQ(std::get<0>(structs_[1].str()), "texty");
EXPECT_EQ(structs_[1].vec(), std::vector<int>({1, 3, 2}));
}
{
tuple<int, std::vector<s1>> result;
EXPECT_TRUE(parse(
"99 s1 42 13.0 1 2 3 s1 41 12.0 1 3 2",
int_ >> *s1_rule_b,
ws,
result));
auto i_ = get(result, llong<0>{});
EXPECT_EQ(i_, 99);
auto structs_ = get(result, llong<1>{});
EXPECT_EQ(structs_[0].i(), 42);
EXPECT_EQ(structs_[0].str().index(), 1u);
EXPECT_EQ(std::get<1>(structs_[0].str()), 13.0);
EXPECT_EQ(structs_[0].vec(), std::vector<int>({1, 2, 3}));
EXPECT_EQ(structs_[1].i(), 41);
EXPECT_EQ(structs_[1].str().index(), 1u);
EXPECT_EQ(std::get<1>(structs_[1].str()), 12.0);
EXPECT_EQ(structs_[1].vec(), std::vector<int>({1, 3, 2}));
}
{
tuple<int, std::vector<s2>> result;
EXPECT_TRUE(parse(
"99 s2 42 text 1 2 3 s2 41 texty 1 3 2",
int_ >> *s2_rule_a,
ws,
result));
auto i_ = get(result, llong<0>{});
EXPECT_EQ(i_, 99);
auto structs_ = get(result, llong<1>{});
EXPECT_EQ(structs_[0].i(), 42);
EXPECT_EQ(structs_[0].str(), "text");
EXPECT_EQ(structs_[0].vec(), std::vector<int>({1, 2, 3}));
EXPECT_EQ(structs_[1].i(), 41);
EXPECT_EQ(structs_[1].str(), "texty");
EXPECT_EQ(structs_[1].vec(), std::vector<int>({1, 3, 2}));
}
{
tuple<int, std::vector<s2>> result;
EXPECT_TRUE(parse(
"99 s2 42 text 1 2 3 s2 41 texty 1 3 2",
int_ >> *s2_rule_b,
ws,
result));
auto i_ = get(result, llong<0>{});
EXPECT_EQ(i_, 99);
auto structs_ = get(result, llong<1>{});
EXPECT_EQ(structs_[0].i(), 42);
EXPECT_EQ(structs_[0].str(), "text");
EXPECT_EQ(structs_[0].vec(), std::vector<int>({1, 2, 3}));
EXPECT_EQ(structs_[1].i(), 41);
EXPECT_EQ(structs_[1].str(), "texty");
EXPECT_EQ(structs_[1].vec(), std::vector<int>({1, 3, 2}));
}
}
struct callbacks_t
{
void operator()(s0_rule_tag, s0 s) const { s0s.push_back(std::move(s)); }
void operator()(s1_rule_a_tag, s1 s) const { s1s.push_back(std::move(s)); }
void operator()(s1_rule_b_tag, s1 s) const { s1s.push_back(std::move(s)); }
void operator()(s2_rule_a_tag, s2 s) const { s2s.push_back(std::move(s)); }
void operator()(s2_rule_b_tag, s2 s) const { s2s.push_back(std::move(s)); }
mutable std::vector<s0> s0s;
mutable std::vector<s1> s1s;
mutable std::vector<s2> s2s;
};
TEST(class_type, seq_parser_struct_cb_rule)
{
{
callbacks_t callbacks;
EXPECT_TRUE(callback_parse("s0 42 text 1 2 3", s0_rule, ws, callbacks));
EXPECT_EQ(callbacks.s0s.size(), 1u);
s0 const & struct_ = callbacks.s0s[0];
EXPECT_EQ(struct_.i(), 42);
EXPECT_EQ(struct_.str(), "text");
EXPECT_EQ(struct_.vec(), std::vector<int>({1, 2, 3}));
}
{
callbacks_t callbacks;
EXPECT_TRUE(callback_parse("s1 42 text 1 2 3", s1_rule_a, ws, callbacks));
EXPECT_EQ(callbacks.s1s.size(), 1u);
s1 const & struct_ = callbacks.s1s[0];
EXPECT_EQ(struct_.i(), 42);
EXPECT_EQ(struct_.str().index(), 0u);
EXPECT_EQ(std::get<0>(struct_.str()), "text");
EXPECT_EQ(struct_.vec(), std::vector<int>({1, 2, 3}));
}
{
callbacks_t callbacks;
EXPECT_TRUE(callback_parse("s1 42 13.0 1 2 3", s1_rule_b, ws, callbacks));
EXPECT_EQ(callbacks.s1s.size(), 1u);
s1 const & struct_ = callbacks.s1s[0];
EXPECT_EQ(struct_.i(), 42);
EXPECT_EQ(struct_.str().index(), 1u);
EXPECT_EQ(std::get<1>(struct_.str()), 13.0);
EXPECT_EQ(struct_.vec(), std::vector<int>({1, 2, 3}));
}
{
callbacks_t callbacks;
EXPECT_TRUE(callback_parse("s2 42 text 1 2 3", s2_rule_a, ws, callbacks));
EXPECT_EQ(callbacks.s2s.size(), 1u);
s2 const & struct_ = callbacks.s2s[0];
EXPECT_EQ(struct_.i(), 42);
EXPECT_EQ(struct_.str(), "text");
EXPECT_EQ(struct_.vec(), std::vector<int>({1, 2, 3}));
}
{
callbacks_t callbacks;
EXPECT_TRUE(callback_parse("s2 42 text 1 2 3", s2_rule_b, ws, callbacks));
EXPECT_EQ(callbacks.s2s.size(), 1u);
s2 const & struct_ = callbacks.s2s[0];
EXPECT_EQ(struct_.i(), 42);
EXPECT_EQ(struct_.str(), "text");
EXPECT_EQ(struct_.vec(), std::vector<int>({1, 2, 3}));
}
// Use the rule as part of a larger parse.
{
callbacks_t callbacks;
EXPECT_TRUE(callback_parse(
"99 s0 42 text 1 2 3", int_ >> s0_rule, ws, callbacks));
EXPECT_EQ(callbacks.s0s.size(), 1u);
s0 const & struct_ = callbacks.s0s[0];
EXPECT_EQ(struct_.i(), 42);
EXPECT_EQ(struct_.str(), "text");
EXPECT_EQ(struct_.vec(), std::vector<int>({1, 2, 3}));
}
{
callbacks_t callbacks;
EXPECT_TRUE(callback_parse(
"99 s1 42 text 1 2 3", int_ >> s1_rule_a, ws, callbacks));
EXPECT_EQ(callbacks.s1s.size(), 1u);
s1 const & struct_ = callbacks.s1s[0];
EXPECT_EQ(struct_.i(), 42);
EXPECT_EQ(struct_.str().index(), 0u);
EXPECT_EQ(std::get<0>(struct_.str()), "text");
EXPECT_EQ(struct_.vec(), std::vector<int>({1, 2, 3}));
}
{
callbacks_t callbacks;
EXPECT_TRUE(callback_parse(
"99 s1 42 13.0 1 2 3", int_ >> s1_rule_b, ws, callbacks));
EXPECT_EQ(callbacks.s1s.size(), 1u);
s1 const & struct_ = callbacks.s1s[0];
EXPECT_EQ(struct_.i(), 42);
EXPECT_EQ(struct_.str().index(), 1u);
EXPECT_EQ(std::get<1>(struct_.str()), 13.0);
EXPECT_EQ(struct_.vec(), std::vector<int>({1, 2, 3}));
}
{
callbacks_t callbacks;
EXPECT_TRUE(callback_parse(
"99 s2 42 text 1 2 3", int_ >> s2_rule_a, ws, callbacks));
EXPECT_EQ(callbacks.s2s.size(), 1u);
s2 const & struct_ = callbacks.s2s[0];
EXPECT_EQ(struct_.i(), 42);
EXPECT_EQ(struct_.str(), "text");
EXPECT_EQ(struct_.vec(), std::vector<int>({1, 2, 3}));
}
{
callbacks_t callbacks;
EXPECT_TRUE(callback_parse(
"99 s2 42 text 1 2 3", int_ >> s2_rule_b, ws, callbacks));
EXPECT_EQ(callbacks.s2s.size(), 1u);
s2 const & struct_ = callbacks.s2s[0];
EXPECT_EQ(struct_.i(), 42);
EXPECT_EQ(struct_.str(), "text");
EXPECT_EQ(struct_.vec(), std::vector<int>({1, 2, 3}));
}
}
TEST(class_type, parse_into_struct)
{
{
s0 struct_;
EXPECT_TRUE(parse("s0 42 text 1 2 3", s0_parser, ws, struct_));
EXPECT_EQ(struct_.i(), 42);
EXPECT_EQ(struct_.str(), "text");
EXPECT_EQ(struct_.vec(), std::vector<int>({1, 2, 3}));
}
{
s1 struct_;
EXPECT_TRUE(parse("s1 42 text 1 2 3", s1_parser_a, ws, struct_));
EXPECT_EQ(struct_.i(), 42);
EXPECT_EQ(struct_.str().index(), 0u);
EXPECT_EQ(std::get<0>(struct_.str()), "text");
EXPECT_EQ(struct_.vec(), std::vector<int>({1, 2, 3}));
}
{
s1 struct_;
EXPECT_TRUE(parse("s1 42 13.0 1 2 3", s1_parser_b, ws, struct_));
EXPECT_EQ(struct_.i(), 42);
EXPECT_EQ(struct_.str().index(), 1u);
EXPECT_EQ(std::get<1>(struct_.str()), 13.0);
EXPECT_EQ(struct_.vec(), std::vector<int>({1, 2, 3}));
}
{
s2 struct_;
EXPECT_TRUE(parse("s2 42 text 1 2 3", s2_parser_a, ws, struct_));
EXPECT_EQ(struct_.i(), 42);
EXPECT_EQ(struct_.str(), "text");
EXPECT_EQ(struct_.vec(), std::vector<int>({1, 2, 3}));
}
{
s2 struct_;
EXPECT_TRUE(parse("s2 42 text 1 2 3", s2_parser_b, ws, struct_));
EXPECT_EQ(struct_.i(), 42);
EXPECT_EQ(struct_.str(), "text");
EXPECT_EQ(struct_.vec(), std::vector<int>({1, 2, 3}));
}
}
TEST(class_type, repeated_parse_into_struct)
{
{
std::vector<s0> structs_;
EXPECT_TRUE(parse(
"s0 42 text 1 2 3 s0 41 texty 1 3 2", *s0_parser, ws, structs_));
EXPECT_EQ(structs_[0].i(), 42);
EXPECT_EQ(structs_[0].str(), "text");
EXPECT_EQ(structs_[0].vec(), std::vector<int>({1, 2, 3}));
EXPECT_EQ(structs_[1].i(), 41);
EXPECT_EQ(structs_[1].str(), "texty");
EXPECT_EQ(structs_[1].vec(), std::vector<int>({1, 3, 2}));
}
{
std::vector<s1> structs_;
EXPECT_TRUE(parse(
"s1 42 text 1 2 3 s1 41 texty 1 3 2", *s1_parser_a, ws, structs_));
EXPECT_EQ(structs_[0].i(), 42);
EXPECT_EQ(structs_[0].str().index(), 0u);
EXPECT_EQ(std::get<0>(structs_[0].str()), "text");
EXPECT_EQ(structs_[0].vec(), std::vector<int>({1, 2, 3}));
EXPECT_EQ(structs_[1].i(), 41);
EXPECT_EQ(structs_[1].str().index(), 0u);
EXPECT_EQ(std::get<0>(structs_[1].str()), "texty");
EXPECT_EQ(structs_[1].vec(), std::vector<int>({1, 3, 2}));
}
{
std::vector<s1> structs_;
EXPECT_TRUE(parse(
"s1 42 13.0 1 2 3 s1 41 12.0 1 3 2", *s1_parser_b, ws, structs_));
EXPECT_EQ(structs_[0].i(), 42);
EXPECT_EQ(structs_[0].str().index(), 1u);
EXPECT_EQ(std::get<1>(structs_[0].str()), 13.0);
EXPECT_EQ(structs_[0].vec(), std::vector<int>({1, 2, 3}));
EXPECT_EQ(structs_[1].i(), 41);
EXPECT_EQ(structs_[1].str().index(), 1u);
EXPECT_EQ(std::get<1>(structs_[1].str()), 12.0);
EXPECT_EQ(structs_[1].vec(), std::vector<int>({1, 3, 2}));
}
{
std::vector<s2> structs_;
EXPECT_TRUE(parse(
"s2 42 text 1 2 3 s2 41 texty 1 3 2", *s2_parser_a, ws, structs_));
EXPECT_EQ(structs_[0].i(), 42);
EXPECT_EQ(structs_[0].str(), "text");
EXPECT_EQ(structs_[0].vec(), std::vector<int>({1, 2, 3}));
EXPECT_EQ(structs_[1].i(), 41);
EXPECT_EQ(structs_[1].str(), "texty");
EXPECT_EQ(structs_[1].vec(), std::vector<int>({1, 3, 2}));
}
{
std::vector<s2> structs_;
EXPECT_TRUE(parse(
"s2 42 text 1 2 3 s2 41 texty 1 3 2", *s2_parser_b, ws, structs_));
EXPECT_EQ(structs_[0].i(), 42);
EXPECT_EQ(structs_[0].str(), "text");
EXPECT_EQ(structs_[0].vec(), std::vector<int>({1, 2, 3}));
EXPECT_EQ(structs_[1].i(), 41);
EXPECT_EQ(structs_[1].str(), "texty");
EXPECT_EQ(structs_[1].vec(), std::vector<int>({1, 3, 2}));
}
}

View File

@@ -297,13 +297,13 @@ void compile_attribute_unicode_utf32()
[[maybe_unused]] rule<class test_rule, std::string> const test_rule =
"test_rule";
auto const test_rule_def = +char_;
BOOST_PARSER_DEFINE_RULE(test_rule);
BOOST_PARSER_DEFINE_RULES(test_rule);
[[maybe_unused]] rule<class ints, std::vector<int>> const ints = "ints";
auto twenty_zeros = [](auto & ctx) { _val(ctx).resize(20, 0); };
auto push_back = [](auto & ctx) { _val(ctx).push_back(_attr(ctx)); };
auto const ints_def = lit("20-zeros")[twenty_zeros] | +int_[push_back];
BOOST_PARSER_DEFINE_RULE(ints);
BOOST_PARSER_DEFINE_RULES(ints);
void compile_attribute_sentinel()
{

View File

@@ -213,6 +213,18 @@ void compile_seq_attribute()
static_assert(
std::is_same_v<attr_t, std::optional<std::vector<std::string>>>);
}
{
constexpr auto parser = *(char_ - "str") >> eps >> *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>>>);
}
{
constexpr auto parser = *(char_ >> eps) >> eps >> *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>>>);
}
{
constexpr auto parser = *char_ >> eps >> *string("str");
using attr_t = decltype(prefix_parse(first, last, parser));

View File

@@ -208,8 +208,7 @@ TEST(merge_separate, merge_)
*result, detail::hl::make_tuple('z', std::string("abcdefghi")));
}
}
#if 0 // TODO: Document that this does not work, and why (flattening), and how
// making a rule for the parethesized part is a fix.
#if 0 // Intentionally ill-formed.
{
constexpr auto parser =
char_ >> merge[(string("abc") >> char_ >> char_) >> string("ghi")];

View File

@@ -125,9 +125,10 @@ TEST(no_case, match_any_within_string)
EXPECT_TRUE(*result == U's');
}
{
// Non-Unicode parsing fails to match, since 'ß' is not treated as a
// single character.
auto const result = parse("s", _trasse_p);
EXPECT_TRUE(result);
EXPECT_EQ(*result, 's');
EXPECT_FALSE(result);
}
{
auto const result = parse(U"S", _trasse_p);
@@ -136,8 +137,7 @@ TEST(no_case, match_any_within_string)
}
{
auto const result = parse("S", _trasse_p);
EXPECT_TRUE(result);
EXPECT_EQ(*result, 'S');
EXPECT_FALSE(result);
}
{
auto const result = parse(U"t", _trasse_p);
@@ -159,6 +159,10 @@ TEST(no_case, match_any_within_string)
EXPECT_TRUE(result);
EXPECT_EQ(*result, 'T');
}
{
auto const result = parse("X", _trasse_p);
EXPECT_FALSE(result);
}
}
constexpr auto capital_sharp_s = u8""; // U+1E9E
@@ -304,6 +308,51 @@ TEST(no_case, detail_no_case_iter)
EXPECT_EQ(folded, "sss");
EXPECT_TRUE(first.base() == detail::text::null_sentinel);
}
{
auto const street = U"Straße";
std::string folded;
auto const first_const =
detail::no_case_iter(street, detail::text::null_sentinel);
auto first = first_const;
while (first != detail::text::null_sentinel) {
folded.push_back(*first);
++first;
}
EXPECT_EQ(folded, "strasse");
EXPECT_TRUE(first.base() == detail::text::null_sentinel);
first = first_const;
std::u32string_view const sv = U"strasse";
auto mismatches = detail::text::mismatch(
first, detail::text::null_sentinel, sv.begin(), sv.end());
EXPECT_TRUE(mismatches.first == detail::text::null_sentinel);
EXPECT_TRUE(mismatches.second == sv.end());
{
first = first_const;
auto search_result = detail::text::search(
first, detail::text::null_sentinel, sv.begin(), sv.end());
EXPECT_TRUE(search_result.begin() == first);
EXPECT_TRUE(search_result.end() == detail::text::null_sentinel);
}
{
first = first_const;
auto search_result = detail::text::search(
sv.begin(), sv.end(), first, detail::text::null_sentinel);
EXPECT_TRUE(search_result.begin() == sv.begin());
EXPECT_TRUE(search_result.end() == sv.end());
}
{
detail::case_fold_array_t folded_char;
auto folded_last = detail::case_fold('X', folded_char.begin());
auto search_result = detail::text::search(
sv.begin(), sv.end(), folded_char.begin(), folded_last);
EXPECT_TRUE(search_result.begin() == sv.end());
EXPECT_TRUE(search_result.end() == sv.end());
}
}
}
TEST(no_case, detail_no_case_mismatch)

194
test/parse_coords_new.cpp Normal file
View File

@@ -0,0 +1,194 @@
/**
* 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/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);
}
}

View File

@@ -21,6 +21,172 @@
using namespace boost::parser;
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS && defined(_MSC_VER)
#include <algorithm>
TEST(parser, msvc_string_view)
{
std::string_view sv = "text";
std::wstring_view wsv = L"text";
{
auto r = detail::text::as_utf8(sv);
std::string str = " ";
std::ranges::copy(r, str.begin());
EXPECT_EQ(str, sv);
}
{
auto r = detail::text::as_utf8(wsv);
std::wstring wstr = L" ";
std::ranges::copy(r, wstr.begin());
EXPECT_EQ(wstr, wsv);
}
{
auto r = sv | detail::text::as_utf8;
std::string str = " ";
std::ranges::copy(r, str.begin());
EXPECT_EQ(str, sv);
}
{
auto r = wsv | detail::text::as_utf8;
std::wstring wstr = L" ";
std::ranges::copy(r, wstr.begin());
EXPECT_EQ(wstr, wsv);
}
{
auto r = detail::text::as_utf16(sv);
std::string str = " ";
std::ranges::copy(r, str.begin());
EXPECT_EQ(str, sv);
}
{
auto r = detail::text::as_utf16(wsv);
std::wstring wstr = L" ";
std::ranges::copy(r, wstr.begin());
EXPECT_EQ(wstr, wsv);
}
{
auto r = sv | detail::text::as_utf16;
std::string str = " ";
std::ranges::copy(r, str.begin());
EXPECT_EQ(str, sv);
}
{
auto r = wsv | detail::text::as_utf16;
std::wstring wstr = L" ";
std::ranges::copy(r, wstr.begin());
EXPECT_EQ(wstr, wsv);
}
{
auto r = detail::text::as_utf32(sv);
std::string str = " ";
std::ranges::copy(r, str.begin());
EXPECT_EQ(str, sv);
}
{
auto r = detail::text::as_utf32(wsv);
std::wstring wstr = L" ";
std::ranges::copy(r, wstr.begin());
EXPECT_EQ(wstr, wsv);
}
{
auto r = sv | detail::text::as_utf32;
std::string str = " ";
std::ranges::copy(r, str.begin());
EXPECT_EQ(str, sv);
}
{
auto r = wsv | detail::text::as_utf32;
std::wstring wstr = L" ";
std::ranges::copy(r, wstr.begin());
EXPECT_EQ(wstr, wsv);
}
}
TEST(parser, msvc_string)
{
std::string sv = "text";
std::wstring wsv = L"text";
{
auto r = detail::text::as_utf8(sv);
std::string str = " ";
std::ranges::copy(r, str.begin());
EXPECT_EQ(str, sv);
}
{
auto r = detail::text::as_utf8(wsv);
std::wstring wstr = L" ";
std::ranges::copy(r, wstr.begin());
EXPECT_EQ(wstr, wsv);
}
{
auto r = sv | detail::text::as_utf8;
std::string str = " ";
std::ranges::copy(r, str.begin());
EXPECT_EQ(str, sv);
}
{
auto r = wsv | detail::text::as_utf8;
std::wstring wstr = L" ";
std::ranges::copy(r, wstr.begin());
EXPECT_EQ(wstr, wsv);
}
{
auto r = detail::text::as_utf16(sv);
std::string str = " ";
std::ranges::copy(r, str.begin());
EXPECT_EQ(str, sv);
}
{
auto r = detail::text::as_utf16(wsv);
std::wstring wstr = L" ";
std::ranges::copy(r, wstr.begin());
EXPECT_EQ(wstr, wsv);
}
{
auto r = sv | detail::text::as_utf16;
std::string str = " ";
std::ranges::copy(r, str.begin());
EXPECT_EQ(str, sv);
}
{
auto r = wsv | detail::text::as_utf16;
std::wstring wstr = L" ";
std::ranges::copy(r, wstr.begin());
EXPECT_EQ(wstr, wsv);
}
{
auto r = detail::text::as_utf32(sv);
std::string str = " ";
std::ranges::copy(r, str.begin());
EXPECT_EQ(str, sv);
}
{
auto r = detail::text::as_utf32(wsv);
std::wstring wstr = L" ";
std::ranges::copy(r, wstr.begin());
EXPECT_EQ(wstr, wsv);
}
{
auto r = sv | detail::text::as_utf32;
std::string str = " ";
std::ranges::copy(r, str.begin());
EXPECT_EQ(str, sv);
}
{
auto r = wsv | detail::text::as_utf32;
std::wstring wstr = L" ";
std::ranges::copy(r, wstr.begin());
EXPECT_EQ(wstr, wsv);
}
}
#endif
#if TEST_BOOST_OPTIONAL
template<typename T>
constexpr bool boost::parser::enable_optional<boost::optional<T>> = true;
@@ -71,10 +237,8 @@ TEST(parser, basic)
}
{
std::string str = " ";
char c = '\0';
EXPECT_TRUE(parse(str, ascii::blank, c));
EXPECT_EQ(c, ' ');
EXPECT_FALSE(parse(str, ascii::lower));
EXPECT_TRUE(parse(str, blank));
EXPECT_FALSE(parse(str, lower));
}
{
std::string str = "ab";
@@ -189,6 +353,15 @@ TEST(parser, basic)
EXPECT_EQ(c, boost::none);
}
#endif
{
constexpr auto parser = *(char_ - "str") >> *string("str");
std::string str = "somethingstrstrstr";
auto result = boost::parser::parse(str, parser);
EXPECT_TRUE(result);
EXPECT_EQ(
*result,
std::vector<std::string>({"something", "str", "str", "str"}));
}
}
TEST(parser, int_uint)
@@ -209,7 +382,7 @@ TEST(parser, int_uint)
std::string str = "-42";
int i = 3;
EXPECT_FALSE(parse(str, uint_, i));
EXPECT_EQ(i, 3);
EXPECT_EQ(i, 0);
}
{
std::string str = "42";
@@ -790,6 +963,18 @@ TEST(parser, repeat)
}
}
}
{
constexpr auto parser = *char_ >> eps >> *string("str");
auto result = parse("abcdefg", parser);
EXPECT_TRUE(result);
EXPECT_EQ(*result, std::vector<std::string>({"abcdefg"}));
}
{
constexpr auto parser = *(char_ - "str") >> eps >> *string("str");
auto result = parse("abcdefgstrstr", parser);
EXPECT_TRUE(result);
EXPECT_EQ(*result, std::vector<std::string>({"abcdefg", "str", "str"}));
}
}
TEST(parser, raw)
@@ -810,8 +995,7 @@ TEST(parser, raw)
std::string const str = "z";
range_t r;
EXPECT_FALSE(parse(str, parser, r));
EXPECT_EQ(r.begin(), str.begin());
EXPECT_EQ(r.end(), str.begin());
EXPECT_EQ(r.begin(), r.end());
}
{
std::string const str = "z";
@@ -966,7 +1150,6 @@ TEST(parser, string_view)
EXPECT_TRUE(prefix_parse(first, str.end(), parser, r));
EXPECT_TRUE(r == U"");
}
#if 0 // TODO Odd failure on MSVC.
{
std::u32string const str = U"zs";
range_t r;
@@ -979,7 +1162,6 @@ TEST(parser, string_view)
EXPECT_TRUE(parse(str, parser, r));
EXPECT_TRUE(r == U"zszs");
}
#endif
{
std::u32string const str = U"";
std::optional<range_t> result = parse(str, parser);
@@ -999,7 +1181,6 @@ TEST(parser, string_view)
EXPECT_TRUE(result);
EXPECT_TRUE(*result == U"");
}
#if 0 // TODO: Same as above.
{
std::u32string const str = U"zs";
std::optional<range_t> result = parse(str, parser);
@@ -1012,7 +1193,6 @@ TEST(parser, string_view)
EXPECT_TRUE(result);
EXPECT_TRUE(*result == U"zszs");
}
#endif
}
}
#endif
@@ -1088,7 +1268,7 @@ TEST(parser, delimited)
{
std::vector<std::string> chars;
EXPECT_FALSE(parse(str, parser, chars));
EXPECT_EQ(chars, std::vector<std::string>({"yay"}));
EXPECT_EQ(chars, std::vector<std::string>{});
}
{
std::vector<std::string> chars;
@@ -1470,6 +1650,91 @@ TEST(parser, delimited)
EXPECT_EQ(*chars, std::vector<std::string>({"yay", "yay", "yay"}));
}
}
{
constexpr auto yay = string("yay") % ',';
constexpr auto aww = string("aww") % ',';
constexpr auto parser = yay >> ',' >> aww;
{
std::string str = "yay, yay, yay, aww, aww";
auto result = parse(str, parser, ws);
EXPECT_TRUE(result);
EXPECT_EQ(
*result,
(tuple<std::vector<std::string>, std::vector<std::string>>(
std::vector<std::string>({"yay", "yay", "yay"}),
std::vector<std::string>({"aww", "aww"}))));
}
{
std::string str = "yay, yay, yay , aww, aww";
auto result = parse(str, parser, ws);
EXPECT_TRUE(result);
EXPECT_EQ(
*result,
(tuple<std::vector<std::string>, std::vector<std::string>>(
std::vector<std::string>({"yay", "yay", "yay"}),
std::vector<std::string>({"aww", "aww"}))));
}
}
{
constexpr auto yay = string("yay") % ',';
constexpr auto aww = string("aww") % ',';
constexpr auto parser = raw[yay] >> ',' >> raw[aww];
{
std::string str = "yay, yay, yay, aww, aww";
auto result = parse(str, parser, ws);
EXPECT_TRUE(result);
auto subrange_0 = boost::parser::get(*result, llong<0>{});
auto subrange_1 = boost::parser::get(*result, llong<1>{});
EXPECT_EQ(subrange_0.begin(), str.begin());
EXPECT_EQ(subrange_0.end(), str.begin() + 13);
EXPECT_EQ(subrange_1.begin(), str.begin() + 15);
EXPECT_EQ(subrange_1.end(), str.begin() + 23);
}
{
std::string str = "yay, yay, yay , aww, aww";
auto result = parse(str, parser, ws);
EXPECT_TRUE(result);
auto subrange_0 = boost::parser::get(*result, llong<0>{});
auto subrange_1 = boost::parser::get(*result, llong<1>{});
EXPECT_EQ(subrange_0.begin(), str.begin());
EXPECT_EQ(subrange_0.end(), str.begin() + 13);
EXPECT_EQ(subrange_1.begin(), str.begin() + 16);
EXPECT_EQ(subrange_1.end(), str.begin() + 24);
}
}
{
constexpr auto yay = *string("yay");
constexpr auto aww = *string("aww");
constexpr auto parser = raw[yay] >> ',' >> raw[aww];
{
std::string str = "yay yay yay, aww aww";
auto result = parse(str, parser, ws);
EXPECT_TRUE(result);
auto subrange_0 = boost::parser::get(*result, llong<0>{});
auto subrange_1 = boost::parser::get(*result, llong<1>{});
EXPECT_EQ(subrange_0.begin(), str.begin());
EXPECT_EQ(subrange_0.end(), str.begin() + 11);
EXPECT_EQ(subrange_1.begin(), str.begin() + 13);
EXPECT_EQ(subrange_1.end(), str.begin() + 20);
}
{
std::string str = "yay yay yay , aww aww";
auto result = parse(str, parser, ws);
EXPECT_TRUE(result);
auto subrange_0 = boost::parser::get(*result, llong<0>{});
auto subrange_1 = boost::parser::get(*result, llong<1>{});
EXPECT_EQ(subrange_0.begin(), str.begin());
EXPECT_EQ(subrange_0.end(), str.begin() + 11);
EXPECT_EQ(subrange_1.begin(), str.begin() + 14);
EXPECT_EQ(subrange_1.end(), str.begin() + 21);
}
}
}
TEST(parser, lexeme)
@@ -1587,6 +1852,99 @@ TEST(parser, lexeme)
}
}
}
{
auto const parser = string("abc");
// Follows the parser used in transform_replace().
auto before = [&](auto & ctx) {};
auto after = [&](auto & ctx) {};
auto const search_parser =
omit[*(char_ - parser)] >>
-lexeme[eps[before] >> skip[parser] >> eps[after]];
{
std::string str = "abc";
std::optional<std::string> result;
EXPECT_TRUE(parse(str, search_parser, char_(' '), result));
EXPECT_EQ(*result, "abc");
{
std::string str = "abc";
auto first = detail::text::detail::begin(str);
auto last = detail::text::detail::end(str);
auto const result =
prefix_parse(first, last, search_parser, char_(' '));
static_assert(std::is_same_v<
decltype(result),
std::optional<std::optional<std::string>> const>);
EXPECT_TRUE(result);
EXPECT_EQ(**result, "abc");
}
}
{
std::string str = " abc";
std::optional<std::string> result;
EXPECT_TRUE(parse(str, search_parser, char_(' '), result));
EXPECT_EQ(*result, "abc");
{
std::string str = " abc";
auto const result = parse(str, search_parser, char_(' '));
static_assert(std::is_same_v<
decltype(result),
std::optional<std::optional<std::string>> const>);
EXPECT_TRUE(result);
EXPECT_EQ(**result, "abc");
}
}
}
{
auto const parser = int_ % ',';
// Follows the parser used in transform_replace().
auto before = [&](auto & ctx) {};
auto after = [&](auto & ctx) {};
auto const search_parser =
omit[*(char_ - parser)] >>
-lexeme[eps[before] >> skip[parser] >> eps[after]];
{
std::string str = "1, 2, 4";
std::optional<std::vector<int>> result;
EXPECT_TRUE(parse(str, search_parser, char_(' '), result));
EXPECT_EQ(*result, std::vector<int>({1, 2, 4}));
{
std::string str = "1, 2, 4";
auto const result = parse(str, search_parser, char_(' '));
static_assert(
std::is_same_v<
decltype(result),
std::optional<std::optional<std::vector<int>>> const>);
EXPECT_TRUE(result);
EXPECT_EQ(**result, std::vector<int>({1, 2, 4}));
}
}
{
std::string str = " 1, 2, 4";
std::optional<std::vector<int>> result;
EXPECT_TRUE(parse(str, search_parser, char_(' '), result));
EXPECT_EQ(*result, std::vector<int>({1, 2, 4}));
{
std::string str = " 1, 2, 4";
auto const result = parse(str, search_parser, char_(' '));
static_assert(
std::is_same_v<
decltype(result),
std::optional<std::optional<std::vector<int>>> const>);
EXPECT_TRUE(result);
EXPECT_EQ(**result, std::vector<int>({1, 2, 4}));
}
}
}
}
TEST(parser, skip)
@@ -1634,7 +1992,7 @@ TEST(parser, combined_seq_and_or)
std::string str = "abc";
tuple<char, char, char> chars;
EXPECT_TRUE(parse(str, parser, chars));
EXPECT_EQ(chars, tup('c', '\0', '\0')); // TODO: Document this behavior.
EXPECT_EQ(chars, tup('c', '\0', '\0'));
}
{
@@ -1809,6 +2167,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_)
@@ -2000,24 +2365,225 @@ TEST(parser, ws_)
}
}
TEST(parser, blank_)
{
{
constexpr auto parser = blank;
constexpr auto alt_parser = ws - eol;
{
std::string str = "y";
EXPECT_FALSE(parse(str, parser));
}
{
std::string s = (char const *)u8"\u0009";
auto str = boost::parser::detail::text::as_utf8(s);
EXPECT_EQ(parse(str, parser), parse(str, alt_parser));
}
{
std::string s = (char const *)u8"\u000a";
auto str = boost::parser::detail::text::as_utf8(s);
EXPECT_EQ(parse(str, parser), parse(str, alt_parser));
}
{
std::string s = (char const *)u8"\u000d\u000a";
auto str = boost::parser::detail::text::as_utf8(s);
EXPECT_EQ(parse(str, parser), parse(str, alt_parser));
}
{
std::string s = (char const *)u8"\u000b";
auto str = boost::parser::detail::text::as_utf8(s);
EXPECT_EQ(parse(str, parser), parse(str, alt_parser));
}
{
std::string s = (char const *)u8"\u000c";
auto str = boost::parser::detail::text::as_utf8(s);
EXPECT_EQ(parse(str, parser), parse(str, alt_parser));
}
{
std::string s = (char const *)u8"\u000d";
auto str = boost::parser::detail::text::as_utf8(s);
EXPECT_EQ(parse(str, parser), parse(str, alt_parser));
}
{
std::string s = (char const *)u8"\u0085";
auto str = boost::parser::detail::text::as_utf8(s);
EXPECT_EQ(parse(str, parser), parse(str, alt_parser));
}
{
std::string s = (char const *)u8"\u00a0";
auto str = boost::parser::detail::text::as_utf8(s);
EXPECT_EQ(parse(str, parser), parse(str, alt_parser));
}
{
std::string s = (char const *)u8"\u1680";
auto str = boost::parser::detail::text::as_utf8(s);
EXPECT_EQ(parse(str, parser), parse(str, alt_parser));
}
{
std::string s = (char const *)u8"\u2000";
auto str = boost::parser::detail::text::as_utf8(s);
EXPECT_EQ(parse(str, parser), parse(str, alt_parser));
}
{
std::string s = (char const *)u8"\u2001";
auto str = boost::parser::detail::text::as_utf8(s);
EXPECT_EQ(parse(str, parser), parse(str, alt_parser));
}
{
std::string s = (char const *)u8"\u2002";
auto str = boost::parser::detail::text::as_utf8(s);
EXPECT_EQ(parse(str, parser), parse(str, alt_parser));
}
{
std::string s = (char const *)u8"\u2003";
auto str = boost::parser::detail::text::as_utf8(s);
EXPECT_EQ(parse(str, parser), parse(str, alt_parser));
}
{
std::string s = (char const *)u8"\u2004";
auto str = boost::parser::detail::text::as_utf8(s);
EXPECT_EQ(parse(str, parser), parse(str, alt_parser));
}
{
std::string s = (char const *)u8"\u2005";
auto str = boost::parser::detail::text::as_utf8(s);
EXPECT_EQ(parse(str, parser), parse(str, alt_parser));
}
{
std::string s = (char const *)u8"\u2006";
auto str = boost::parser::detail::text::as_utf8(s);
EXPECT_EQ(parse(str, parser), parse(str, alt_parser));
}
{
std::string s = (char const *)u8"\u2007";
auto str = boost::parser::detail::text::as_utf8(s);
EXPECT_EQ(parse(str, parser), parse(str, alt_parser));
}
{
std::string s = (char const *)u8"\u2008";
auto str = boost::parser::detail::text::as_utf8(s);
EXPECT_EQ(parse(str, parser), parse(str, alt_parser));
}
{
std::string s = (char const *)u8"\u2009";
auto str = boost::parser::detail::text::as_utf8(s);
EXPECT_EQ(parse(str, parser), parse(str, alt_parser));
}
{
std::string s = (char const *)u8"\u200a";
auto str = boost::parser::detail::text::as_utf8(s);
EXPECT_EQ(parse(str, parser), parse(str, alt_parser));
}
{
std::string s = (char const *)u8"\u2028";
auto str = boost::parser::detail::text::as_utf8(s);
EXPECT_EQ(parse(str, parser), parse(str, alt_parser));
}
{
std::string s = (char const *)u8"\u2029";
auto str = boost::parser::detail::text::as_utf8(s);
EXPECT_EQ(parse(str, parser), parse(str, alt_parser));
}
{
std::string s = (char const *)u8"\u202F";
auto str = boost::parser::detail::text::as_utf8(s);
EXPECT_EQ(parse(str, parser), parse(str, alt_parser));
}
{
std::string s = (char const *)u8"\u205F";
auto str = boost::parser::detail::text::as_utf8(s);
EXPECT_EQ(parse(str, parser), parse(str, alt_parser));
}
{
std::string s = (char const *)u8"\u3000";
auto str = boost::parser::detail::text::as_utf8(s);
EXPECT_EQ(parse(str, parser), parse(str, alt_parser));
}
}
}
TEST(parser, digit_)
{
constexpr auto parser = +digit;
std::u32string str = U"a/09:\x0659\x0660\x0669\x066a";
std::vector<uint32_t> result;
EXPECT_TRUE(parse(str, parser, char_ - digit, result));
EXPECT_EQ(result, std::vector<uint32_t>({'0', '9', 0x0660, 0x0669}));
}
TEST(parser, hex_digit_)
{
constexpr auto parser = +hex_digit;
std::u32string str = U"a/09:A\uff0f\uff10\uff19\uff1a";
std::vector<uint32_t> result;
EXPECT_TRUE(parse(str, parser, char_ - hex_digit, result));
EXPECT_EQ(
result,
std::vector<uint32_t>({'a', '0', '9', 'A', U'\uff10', U'\uff19'}));
}
TEST(parser, control_)
{
constexpr auto parser = +control;
std::u32string str = U"\u0001\u001f\u0020\u007e\u007f\u009f\u00a0";
std::vector<uint32_t> result;
EXPECT_TRUE(parse(str, parser, char_ - control, result));
EXPECT_EQ(result, std::vector<uint32_t>({1, 0x001f, 0x007f, 0x009f}));
}
TEST(parser, punct_)
{
auto parser = +punct;
std::u32string str = U"\u0020\u0021\u0fda\u0fdb";
std::vector<uint32_t> result;
EXPECT_TRUE(parse(str, parser, char_ - punct, result));
EXPECT_EQ(result, std::vector<uint32_t>({0x21, 0xfda}));
}
TEST(parser, lower_)
{
auto parser = +lower;
std::u32string str = U"aA\u016F\u0170";
std::vector<uint32_t> result;
EXPECT_TRUE(parse(str, parser, char_ - lower, result));
EXPECT_EQ(result, std::vector<uint32_t>({'a', 0x16f}));
}
TEST(parser, upper_)
{
auto parser = +upper;
std::u32string str = U"aA\u0105\u0106";
std::vector<uint32_t> result;
EXPECT_TRUE(parse(str, parser, char_ - upper, result));
EXPECT_EQ(result, std::vector<uint32_t>({'A', 0x106}));
}
TEST(parser, github_issue_36)
{
namespace bp = boost::parser;
auto id = bp::lexeme[+(bp::ascii::alnum | bp::char_('_'))];
auto id = bp::lexeme[+bp::char_(
"ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz0123456789")];
auto ids = +id;
std::string str;
std::vector<std::string> vec;
bp::parse("id1", id, bp::ascii::space, str); // (1)
bp::parse("id1", id, bp::ws, str); // (1)
EXPECT_EQ(str, "id1");
str.clear();
bp::parse("id1 id2", ids, bp::ascii::space, vec); // (2)
bp::parse("id1 id2", ids, bp::ws, vec); // (2)
EXPECT_EQ(vec, std::vector<std::string>({"id1", "id2"}));
// Intentionally ill-formed.
// bp::parse("i1 i2", ids, bp::ascii::space, str); // (3)
// bp::parse("i1 i2", ids, bp::ws, str); // (3)
}
namespace issue_50 {
@@ -2146,6 +2712,26 @@ TEST(parser, github_issue_52)
}
}
namespace issue_90 { namespace parser {
const auto string =
'"' >> boost::parser::lexeme[*(boost::parser::char_ - '"')] > '"';
const auto specifier = string > ':' > string;
}}
TEST(parser, github_issue_90)
{
using namespace issue_90;
namespace bp = boost::parser;
std::string input = R"( "dd" : "2" )";
std::pair<std::string, std::string> result;
auto b = bp::parse(input, parser::specifier, bp::ws, result);
EXPECT_TRUE(b);
EXPECT_EQ(result.first, "dd");
EXPECT_EQ(result.second, "2");
}
TEST(parser, no_need_for_sprit_2_hold_directive)
{
namespace bp = boost::parser;
@@ -2222,3 +2808,51 @@ TEST(parser, string_view_doc_example)
static_assert(std::is_same_v<decltype(sv2), std::optional<std::string_view>>);
}
#endif
TEST(parser, variant_compat_example)
{
struct key_value
{
int key;
double value;
};
namespace bp = boost::parser;
std::variant<key_value, double> kv_or_d;
key_value kv;
bp::parse("42 13.0", bp::int_ >> bp::double_, kv); // Ok.
#if 0
bp::parse("42 13.0", bp::int_ >> bp::double_, kv_or_d); // Error: ill-formed!
#endif
}
namespace rule_construction_example {
struct type_t
{
type_t() = default;
explicit type_t(double x) : x_(x) {}
// etc.
double x_;
};
namespace bp = boost::parser;
auto doubles_to_type = [](auto & ctx) {
_val(ctx) = type_t(
bp::get(_attr(ctx), bp::llong<0>{}) *
bp::get(_attr(ctx), bp::llong<1>{}));
};
bp::rule<struct type_tag, type_t> type = "type";
auto const type_def = (bp::double_ >> bp::double_)[doubles_to_type];
BOOST_PARSER_DEFINE_RULES(type);
}
TEST(parser, rule_example)
{
namespace bp = boost::parser;
using namespace rule_construction_example;
EXPECT_TRUE(bp::parse("3 4", type, bp::ws));
}

View File

@@ -22,15 +22,13 @@ using namespace boost::parser;
constexpr callback_rule<struct callback_char_rule_tag, char>
callback_char_rule = "callback_char_rule";
constexpr auto callback_char_rule_def = char_;
BOOST_PARSER_DEFINE_RULE(callback_char_rule);
BOOST_PARSER_DEFINE_RULES(callback_char_rule);
struct callback_char_rule_tag
{};
TEST(parser, full_parse_api)
{
constexpr auto skip_ws = ascii::space;
std::string const str = "a";
// attr out param, iter/sent
@@ -111,7 +109,7 @@ TEST(parser, full_parse_api)
first,
boost::parser::detail::text::null_sentinel,
char_,
skip_ws,
ws,
out));
first = str.c_str();
EXPECT_EQ(out, 'a');
@@ -121,26 +119,26 @@ TEST(parser, full_parse_api)
first,
boost::parser::detail::text::null_sentinel,
char_('b'),
skip_ws,
ws,
out));
EXPECT_EQ(out, 0);
}
// attr out param, using skipper, range
{
char out = 0;
EXPECT_TRUE(parse(str, char_, skip_ws, out));
EXPECT_TRUE(parse(str, char_, ws, out));
EXPECT_EQ(out, 'a');
out = 0;
EXPECT_FALSE(parse(str, char_('b'), skip_ws, out));
EXPECT_FALSE(parse(str, char_('b'), ws, out));
EXPECT_EQ(out, 0);
}
// attr out param, using skipper, pointer-as-range
{
char out = 0;
EXPECT_TRUE(parse(str.c_str(), char_, skip_ws, out));
EXPECT_TRUE(parse(str.c_str(), char_, ws, out));
EXPECT_EQ(out, 'a');
out = 0;
EXPECT_FALSE(parse(str.c_str(), char_('b'), skip_ws, out));
EXPECT_FALSE(parse(str.c_str(), char_('b'), ws, out));
EXPECT_EQ(out, 0);
}
@@ -148,33 +146,33 @@ TEST(parser, full_parse_api)
{
auto first = str.c_str();
EXPECT_TRUE(prefix_parse(
first, boost::parser::detail::text::null_sentinel, char_, skip_ws));
first, boost::parser::detail::text::null_sentinel, char_, ws));
first = str.c_str();
EXPECT_EQ(
*prefix_parse(
first,
boost::parser::detail::text::null_sentinel,
char_,
skip_ws),
ws),
'a');
first = str.c_str();
EXPECT_TRUE(!prefix_parse(
first,
boost::parser::detail::text::null_sentinel,
char_('b'),
skip_ws));
ws));
}
// returned attr, using skipper, range
{
EXPECT_TRUE(parse(str, char_, skip_ws));
EXPECT_EQ(*parse(str, char_, skip_ws), 'a');
EXPECT_FALSE(parse(str, char_('b'), skip_ws));
EXPECT_TRUE(parse(str, char_, ws));
EXPECT_EQ(*parse(str, char_, ws), 'a');
EXPECT_FALSE(parse(str, char_('b'), ws));
}
// returned attr, using skipper, pointer-as-range
{
EXPECT_TRUE(parse(str.c_str(), char_, skip_ws));
EXPECT_EQ(*parse(str.c_str(), char_, skip_ws), 'a');
EXPECT_FALSE(parse(str.c_str(), char_('b'), skip_ws));
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));
}
// callback, iter/sent
@@ -214,7 +212,7 @@ TEST(parser, full_parse_api)
first,
boost::parser::detail::text::null_sentinel,
callback_char_rule,
skip_ws,
ws,
callbacks));
first = str.c_str();
EXPECT_EQ(out, 'a');
@@ -224,7 +222,7 @@ TEST(parser, full_parse_api)
char out = 0;
auto callbacks = [&out](auto tag, auto x) { out = x; };
EXPECT_TRUE(
callback_parse(str, callback_char_rule, skip_ws, callbacks));
callback_parse(str, callback_char_rule, ws, callbacks));
EXPECT_EQ(out, 'a');
}
// callback, using skipper, pointer-as-range
@@ -232,7 +230,7 @@ TEST(parser, full_parse_api)
char out = 0;
auto callbacks = [&out](auto tag, auto x) { out = x; };
EXPECT_TRUE(callback_parse(
str.c_str(), callback_char_rule, skip_ws, callbacks));
str.c_str(), callback_char_rule, ws, callbacks));
EXPECT_EQ(out, 'a');
}
}
@@ -282,10 +280,8 @@ TEST(parser, basic)
}
{
char const * str = " ";
char c = '\0';
EXPECT_TRUE(parse(str, ascii::blank, c));
EXPECT_EQ(c, ' ');
EXPECT_FALSE(parse(str, ascii::lower));
EXPECT_TRUE(parse(str, blank));
EXPECT_FALSE(parse(str, lower));
}
{
char const * str = "ab";
@@ -475,7 +471,7 @@ TEST(parser, uint_)
char const * str = "-42";
unsigned int i = 3;
EXPECT_FALSE(parse(str, uint_, i));
EXPECT_EQ(i, 3);
EXPECT_EQ(i, 0);
}
{
char const * str = "42";
@@ -2005,3 +2001,12 @@ TEST(parser, attr_out_param_compat)
assert(bp::get(result, 1_c) == "foo");
}
}
TEST(parser, github_issue_78)
{
namespace bp = boost::parser;
std::vector<int> result;
auto b = bp::parse("3 4 c", +bp::int_, bp::ws, result);
EXPECT_FALSE(b);
EXPECT_TRUE(result.empty());
}

View File

@@ -12,11 +12,11 @@ using namespace boost::parser;
constexpr rule<struct flat_rule_tag> flat_rule = "flat_rule";
constexpr auto flat_rule_def = string("abc") | string("def");
BOOST_PARSER_DEFINE_RULE(flat_rule);
BOOST_PARSER_DEFINE_RULES(flat_rule);
constexpr rule<struct recursive_rule_tag> recursive_rule = "recursive_rule";
constexpr auto recursive_rule_def = string("abc") >> -('a' >> recursive_rule);
BOOST_PARSER_DEFINE_RULE(recursive_rule);
BOOST_PARSER_DEFINE_RULES(recursive_rule);
TEST(parser, no_attribute_rules)
{
@@ -53,7 +53,7 @@ TEST(parser, no_attribute_rules)
constexpr rule<struct flat_string_rule_tag, std::string> flat_string_rule =
"flat_string_rule";
constexpr auto flat_string_rule_def = string("abc") | string("def");
BOOST_PARSER_DEFINE_RULE(flat_string_rule);
BOOST_PARSER_DEFINE_RULES(flat_string_rule);
constexpr callback_rule<struct recursive_string_rule_tag, std::string>
recursive_string_rule = "recursive_string_rule";
@@ -64,7 +64,7 @@ auto append_string = [](auto & ctx) {
};
constexpr auto recursive_string_rule_def = string("abc")[append_string] >>
-('a' >> recursive_string_rule);
BOOST_PARSER_DEFINE_RULE(recursive_string_rule);
BOOST_PARSER_DEFINE_RULES(recursive_string_rule);
TEST(parser, string_attribute_rules)
{
@@ -112,7 +112,7 @@ TEST(parser, string_attribute_rules)
constexpr rule<struct flat_vector_rule_tag, std::vector<char>>
flat_vector_rule = "flat_vector_rule";
constexpr auto flat_vector_rule_def = string("abc") | string("def");
BOOST_PARSER_DEFINE_RULE(flat_vector_rule);
BOOST_PARSER_DEFINE_RULES(flat_vector_rule);
TEST(parser, vector_attribute_rules)
{
@@ -155,12 +155,12 @@ TEST(parser, vector_attribute_rules)
constexpr callback_rule<struct callback_vector_rule_tag, std::vector<char>>
callback_vector_rule = "callback_vector_rule";
constexpr auto callback_vector_rule_def = string("abc") | string("def");
BOOST_PARSER_DEFINE_RULE(callback_vector_rule);
BOOST_PARSER_DEFINE_RULES(callback_vector_rule);
constexpr callback_rule<struct callback_void_rule_tag> callback_void_rule =
"callback_void_rule";
constexpr auto callback_void_rule_def = string("abc") | string("def");
BOOST_PARSER_DEFINE_RULE(callback_void_rule);
BOOST_PARSER_DEFINE_RULES(callback_void_rule);
struct callback_vector_rule_tag
{};
@@ -353,7 +353,7 @@ constexpr callback_rule<recursive_strings_rule_tag, std::vector<std::string>>
auto push_back = [](auto & ctx) { _val(ctx).push_back(std::move(_attr(ctx))); };
constexpr auto recursive_strings_rule_def = string("abc")[push_back] >>
-('a' >> recursive_strings_rule);
BOOST_PARSER_DEFINE_RULE(recursive_strings_rule);
BOOST_PARSER_DEFINE_RULES(recursive_strings_rule);
TEST(param_parser, container_populating_recursive_rule)
{
@@ -485,7 +485,7 @@ namespace more_about_rules_1 {
auto const ints = '{' > (value % ',') > '}';
auto const value_def = bp::int_ | ints;
BOOST_PARSER_DEFINE_RULE(value);
BOOST_PARSER_DEFINE_RULES(value);
}
namespace more_about_rules_2 {

View File

@@ -15,11 +15,11 @@ auto make_13 = [](auto & context) { return 13; };
constexpr rule<struct flat_rule_tag> flat_rule = "flat_rule";
constexpr auto flat_rule_def = string("abc") | string("def");
BOOST_PARSER_DEFINE_RULE(flat_rule);
BOOST_PARSER_DEFINE_RULES(flat_rule);
constexpr rule<struct recursive_rule_tag> recursive_rule = "recursive_rule";
constexpr auto recursive_rule_def = string("abc") >> -('a' >> recursive_rule);
BOOST_PARSER_DEFINE_RULE(recursive_rule);
BOOST_PARSER_DEFINE_RULES(recursive_rule);
TEST(param_parser, no_attribute_rules)
{
@@ -62,7 +62,7 @@ TEST(param_parser, no_attribute_rules)
constexpr rule<struct flat_string_rule_tag, std::string> flat_string_rule =
"flat_string_rule";
constexpr auto flat_string_rule_def = string("abc") | string("def");
BOOST_PARSER_DEFINE_RULE(flat_string_rule);
BOOST_PARSER_DEFINE_RULES(flat_string_rule);
constexpr rule<struct recursive_string_rule_tag, std::string>
recursive_string_rule = "recursive_string_rule";
@@ -73,7 +73,7 @@ auto append_string = [](auto & ctx) {
};
constexpr auto recursive_string_rule_def = string("abc")[append_string] >>
-('a' >> recursive_string_rule);
BOOST_PARSER_DEFINE_RULE(recursive_string_rule);
BOOST_PARSER_DEFINE_RULES(recursive_string_rule);
TEST(param_parser, string_attribute_rules)
{
@@ -123,7 +123,7 @@ TEST(param_parser, string_attribute_rules)
constexpr rule<struct flat_vector_rule_tag, std::vector<char>>
flat_vector_rule = "flat_vector_rule";
constexpr auto flat_vector_rule_def = string("abc") | string("def");
BOOST_PARSER_DEFINE_RULE(flat_vector_rule);
BOOST_PARSER_DEFINE_RULES(flat_vector_rule);
TEST(param_parser, vector_attribute_rules)
{
@@ -177,12 +177,12 @@ TEST(param_parser, vector_attribute_rules)
constexpr callback_rule<struct callback_vector_rule_tag, std::vector<char>>
callback_vector_rule = "callback_vector_rule";
constexpr auto callback_vector_rule_def = string("abc") | string("def");
BOOST_PARSER_DEFINE_RULE(callback_vector_rule);
BOOST_PARSER_DEFINE_RULES(callback_vector_rule);
constexpr callback_rule<struct callback_void_rule_tag> callback_void_rule =
"callback_void_rule";
constexpr auto callback_void_rule_def = string("abc") | string("def");
BOOST_PARSER_DEFINE_RULE(callback_void_rule);
BOOST_PARSER_DEFINE_RULES(callback_void_rule);
struct callback_vector_rule_tag
{};

127
test/quoted_string.cpp Normal file
View File

@@ -0,0 +1,127 @@
/**
* 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 <boost/parser/transcode_view.hpp>
#include <gtest/gtest.h>
namespace bp = boost::parser;
TEST(quoted_string, basic)
{
constexpr auto parser = bp::quoted_string;
{
auto result = bp::parse("", parser, bp::ws);
EXPECT_FALSE(result);
}
{
auto result = bp::parse(R"("foo")", parser, bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(*result, "foo");
}
{
auto result = bp::parse(R"("foo\\")", parser, bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(*result, "foo\\");
}
{
auto result = bp::parse(R"("\"foo\"")", parser, bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(*result, "\"foo\"");
}
}
TEST(quoted_string, different_char)
{
constexpr auto parser = bp::quoted_string('\'');
{
auto result = bp::parse("", parser, bp::ws);
EXPECT_FALSE(result);
}
{
auto result = bp::parse(R"('foo')", parser, bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(*result, "foo");
}
{
auto result = bp::parse(R"('foo\\')", parser, bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(*result, "foo\\");
}
{
auto result = bp::parse(R"('\'foo\'')", parser, bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(*result, "'foo'");
}
}
#if 0
TEST(quoted_string, char_set)
{
constexpr auto parser = bp::quoted_string("'\"");
{
auto result = bp::parse("", parser, bp::ws);
EXPECT_FALSE(result);
}
{
EXPECT_FALSE(bp::parse(R"('foo")", parser, bp::ws));
EXPECT_FALSE(bp::parse(R"("foo')", parser, bp::ws));
}
{
auto result = bp::parse(R"('foo')", parser, bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(*result, "foo");
}
{
auto result = bp::parse(R"("foo")", parser, bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(*result, "foo");
}
{
auto result = bp::parse(R"('foo\\')", parser, bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(*result, "foo\\");
}
{
auto result = bp::parse(R"("foo\\")", parser, bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(*result, "foo\\");
}
{
auto result = bp::parse(R"('\'foo\'')", parser, bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(*result, "'foo'");
}
{
auto result = bp::parse(R"("\"foo\"")", parser, bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(*result, "'foo'");
}
{
// Can't escape arbitrary characters, only backslash and the quote
// character.
EXPECT_FALSE(bp::parse(R"("\'foo")", parser, bp::ws));
}
}
#endif

471
test/replace.cpp Normal file
View File

@@ -0,0 +1,471 @@
/**
* 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/replace.hpp>
#include <gtest/gtest.h>
#include "ill_formed.hpp"
#include <list>
#if !defined(_MSC_VER) || BOOST_PARSER_USE_CONCEPTS
namespace bp = boost::parser;
#if BOOST_PARSER_USE_CONCEPTS
namespace deduction {
using namespace std::literals;
std::string str;
auto const parser = bp::char_;
auto const skip = bp::ws;
auto deduced_1 = bp::replace_view(str, parser, skip, "foo", bp::trace::on);
auto deduced_2 = bp::replace_view(str, parser, skip, "foo");
auto deduced_3 = bp::replace_view(str, parser, "foo", bp::trace::on);
auto deduced_4 = bp::replace_view(str, parser, "foo");
}
#endif
static_assert(
bp::detail::range_utf_format<char const *&>() == bp::detail::no_format);
TEST(replace, either_iterator)
{
{
std::list<int> l({1, 2, 3});
std::vector<int> v({4, 5, 6});
bp::detail::either_iterator<std::list<int>, std::vector<int>>
either_l_begin(l.begin());
bp::detail::either_iterator<std::list<int>, std::vector<int>>
either_l_end(l.end());
bp::detail::either_iterator<std::list<int>, std::vector<int>>
either_v_begin(v.begin());
bp::detail::either_iterator<std::list<int>, std::vector<int>>
either_v_end(v.end());
int const l_array[] = {1, 2, 3};
auto l_array_curr = l_array;
for (auto it = either_l_begin; it != either_l_end;
++it, ++l_array_curr) {
EXPECT_EQ(*it, *l_array_curr);
}
int const v_array[] = {4, 5, 6};
auto v_array_curr = v_array;
for (auto it = either_v_begin; it != either_v_end;
++it, ++v_array_curr) {
EXPECT_EQ(*it, *v_array_curr);
}
}
{
auto r1 = bp::detail::to_range<decltype("")>::call("");
auto r2 = bp::detail::to_range<decltype("foo")>::call("foo");
bp::detail::either_iterator<decltype(r1), decltype(r2)> either_r1_begin(
r1.begin());
bp::detail::either_iterator<decltype(r1), decltype(r2)> either_r1_end(
r1.end());
bp::detail::either_iterator<decltype(r1), decltype(r2)> either_r2_begin(
r2.begin());
bp::detail::either_iterator<decltype(r1), decltype(r2)> either_r2_end(
r2.end());
EXPECT_EQ(either_r1_begin, either_r1_end);
std::string copy;
for (auto it = either_r2_begin; it != either_r2_end; ++it) {
copy.push_back(*it);
}
EXPECT_EQ(copy, "foo");
}
}
TEST(replace, replace)
{
{
auto r = bp::replace("", bp::lit("XYZ"), bp::ws, "foo");
int count = 0;
for (auto subrange : r) {
(void)subrange;
++count;
}
EXPECT_EQ(count, 0);
}
{
char const str[] = "aaXYZb";
auto r = bp::replace(str, bp::lit("XYZ"), bp::ws, "foo");
int count = 0;
std::string_view const strs[] = {"aa", "foo", "b"};
for (auto subrange : r) {
std::string str(subrange.begin(), subrange.end());
EXPECT_EQ(str, strs[count]);
++count;
}
EXPECT_EQ(count, 3);
}
{
char const str[] = "a a XYZ baa ba XYZ";
auto r =
str | bp::replace(bp::lit("XYZ"), bp::ws, "foo", bp::trace::off);
int count = 0;
std::string_view const strs[] = {"a a ", "foo", " baa ba ", "foo"};
for (auto subrange : r) {
std::string str(subrange.begin(), subrange.end());
EXPECT_EQ(str, strs[count]);
++count;
}
EXPECT_EQ(count, 4);
}
#if !defined(__GNUC__) || 12 <= __GNUC__
// Older GCCs don't like the use of temporaries like the
// std::string("foo") below.
{
char const str[] = "aaXYZbaabaXYZ";
auto r = str | bp::replace(
bp::lit("XYZ"), std::string("foo"), bp::trace::off);
int count = 0;
std::string_view const strs[] = {"aa", "foo", "baaba", "foo"};
for (auto subrange : r) {
std::string str(subrange.begin(), subrange.end());
EXPECT_EQ(str, strs[count]);
++count;
}
EXPECT_EQ(count, 4);
}
#endif
{
char const str[] = "aaXYZbaabaXYZ";
const auto r = str | bp::replace(bp::lit("XYZ"), "foo");
int count = 0;
std::string_view const strs[] = {"aa", "foo", "baaba", "foo"};
for (auto subrange : r) {
std::string str(subrange.begin(), subrange.end());
EXPECT_EQ(str, strs[count]);
++count;
}
EXPECT_EQ(count, 4);
}
{
char const str[] = "aaXYZbaabaXYZXYZ";
auto r = str | bp::replace(bp::lit("XYZ"), "foo");
int count = 0;
std::string_view const strs[] = {"aa", "foo", "baaba", "foo", "foo"};
for (auto subrange : r) {
std::string str(subrange.begin(), subrange.end());
EXPECT_EQ(str, strs[count]);
++count;
}
EXPECT_EQ(count, 5);
}
{
char const str[] = "XYZaaXYZbaabaXYZXYZ";
auto r = str | bp::replace(bp::lit("XYZ"), "foo");
int count = 0;
std::string_view const strs[] = {
"foo", "aa", "foo", "baaba", "foo", "foo"};
for (auto subrange : r) {
std::string str(subrange.begin(), subrange.end());
EXPECT_EQ(str, strs[count]);
++count;
}
EXPECT_EQ(count, 6);
}
{
char const str[] = "XYZXYZaaXYZbaabaXYZXYZ";
auto r = str | bp::replace(bp::lit("XYZ"), "foo");
int count = 0;
std::string_view const strs[] = {
"foo", "foo", "aa", "foo", "baaba", "foo", "foo"};
for (auto subrange : r) {
std::string str(subrange.begin(), subrange.end());
EXPECT_EQ(str, strs[count]);
++count;
}
EXPECT_EQ(count, 7);
}
{
char const * str = "XYZXYZaaXYZbaabaXYZXYZ";
char const * replacement = "foo";
auto r = str | bp::replace(bp::lit("XYZ"), replacement);
int count = 0;
std::string_view const strs[] = {
"foo", "foo", "aa", "foo", "baaba", "foo", "foo"};
for (auto subrange : r) {
std::string str(subrange.begin(), subrange.end());
EXPECT_EQ(str, strs[count]);
++count;
}
EXPECT_EQ(count, 7);
}
{
char const * str = "XYZXYZaaXYZbaabaXYZXYZ";
char const * replacement = "foo";
auto const r = str | bp::replace(bp::lit("XYZ"), replacement);
int count = 0;
std::string_view const strs[] = {
"foo", "foo", "aa", "foo", "baaba", "foo", "foo"};
for (auto subrange : r) {
std::string str(subrange.begin(), subrange.end());
EXPECT_EQ(str, strs[count]);
++count;
}
EXPECT_EQ(count, 7);
}
}
// MSVC produces hard errors here, so ill_formed does not work.
#if defined(__cpp_char8_t) && !defined(_MSC_VER)
char const empty_str[] = "";
template<typename T>
using char_str_utf8_replacement =
decltype(std::declval<T>() | bp::replace(bp::lit("XYZ"), std::declval<T>() | bp::as_utf8));
static_assert(ill_formed<char_str_utf8_replacement, decltype(empty_str)>{});
template<typename T>
using char_str_utf16_replacement =
decltype(std::declval<T>() | bp::replace(bp::lit("XYZ"), std::declval<T>() | bp::as_utf16));
static_assert(ill_formed<char_str_utf16_replacement, decltype(empty_str)>{});
template<typename T>
using utf8_str_char_replacement =
decltype(std::declval<T>() | bp::as_utf8 | bp::replace(bp::lit("XYZ"), std::declval<T>()));
static_assert(ill_formed<utf8_str_char_replacement, decltype(empty_str)>{});
#endif
TEST(replace, replace_unicode)
{
{
char const str_[] = "";
auto str = str_ | bp::as_utf8;
auto r = bp::replace(str, bp::lit("XYZ"), bp::ws, "foo" | bp::as_utf8);
int count = 0;
for (auto subrange : r) {
(void)subrange;
++count;
}
EXPECT_EQ(count, 0);
}
{
char const * str_ = "aaXYZb";
auto str = str_ | bp::as_utf16;
auto r = bp::replace(str, bp::lit("XYZ"), bp::ws, "foo" | bp::as_utf16);
int count = 0;
std::string_view const strs[] = {"aa", "foo", "b"};
for (auto subrange : r) {
std::string str(subrange.begin(), subrange.end());
EXPECT_EQ(str, strs[count]);
++count;
}
EXPECT_EQ(count, 3);
}
{
char const str_[] = "aaXYZbaabaXYZ";
auto str = str_ | bp::as_utf32;
auto r =
str |
bp::replace(
bp::lit("XYZ"), bp::ws, "foo" | bp::as_utf32, bp::trace::off);
int count = 0;
std::string_view const strs[] = {"aa", "foo", "baaba", "foo"};
for (auto subrange : r) {
std::string str(subrange.begin(), subrange.end());
EXPECT_EQ(str, strs[count]);
++count;
}
EXPECT_EQ(count, 4);
}
{
char const str_[] = "aaXYZbaabaXYZ";
auto str = str_ | bp::as_utf8;
auto r = str | bp::replace(
bp::lit("XYZ"), "foo" | bp::as_utf8, bp::trace::off);
int count = 0;
std::string_view const strs[] = {"aa", "foo", "baaba", "foo"};
for (auto subrange : r) {
std::string str(subrange.begin(), subrange.end());
EXPECT_EQ(str, strs[count]);
++count;
}
EXPECT_EQ(count, 4);
}
{
char const str_[] = "aaXYZbaabaXYZ";
auto str = str_ | bp::as_utf16;
auto r = str | bp::replace(bp::lit("XYZ"), "foo");
int count = 0;
std::string_view const strs[] = {"aa", "foo", "baaba", "foo"};
for (auto subrange : r) {
std::string str(subrange.begin(), subrange.end());
EXPECT_EQ(str, strs[count]);
++count;
}
EXPECT_EQ(count, 4);
}
{
char const str_[] = "aaXYZbaabaXYZXYZ";
auto str = str_ | bp::as_utf32;
auto r = str | bp::replace(bp::lit("XYZ"), "foo");
int count = 0;
std::string_view const strs[] = {"aa", "foo", "baaba", "foo", "foo"};
for (auto subrange : r) {
std::string str(subrange.begin(), subrange.end());
EXPECT_EQ(str, strs[count]);
++count;
}
EXPECT_EQ(count, 5);
}
{
char const str_[] = "XYZaaXYZbaabaXYZXYZ";
auto str = str_ | bp::as_utf8;
auto r = str | bp::replace(bp::lit("XYZ"), "foo" | bp::as_utf8);
int count = 0;
std::string_view const strs[] = {
"foo", "aa", "foo", "baaba", "foo", "foo"};
for (auto subrange : r) {
std::string str(subrange.begin(), subrange.end());
EXPECT_EQ(str, strs[count]);
++count;
}
EXPECT_EQ(count, 6);
}
{
char const str_[] = "XYZXYZaaXYZbaabaXYZXYZ";
auto str = str_ | bp::as_utf16;
auto r = str | bp::replace(bp::lit("XYZ"), "foo");
int count = 0;
std::string_view const strs[] = {
"foo", "foo", "aa", "foo", "baaba", "foo", "foo"};
for (auto subrange : r) {
std::string str(subrange.begin(), subrange.end());
EXPECT_EQ(str, strs[count]);
++count;
}
EXPECT_EQ(count, 7);
}
{
char const str_[] = "XYZXYZaaXYZbaabaXYZXYZ";
auto str = str_ | bp::as_utf16;
auto r = str | bp::replace(bp::lit("XYZ"), "foo" | bp::as_utf8);
int count = 0;
std::string_view const strs[] = {
"foo", "foo", "aa", "foo", "baaba", "foo", "foo"};
for (auto subrange : r) {
std::string str(subrange.begin(), subrange.end());
EXPECT_EQ(str, strs[count]);
++count;
}
EXPECT_EQ(count, 7);
}
{
char const str_[] = "XYZXYZaaXYZbaabaXYZXYZ";
auto str = str_ | bp::as_utf16;
auto r = str | bp::replace(bp::lit("XYZ"), "foo" | bp::as_utf32);
int count = 0;
std::string_view const strs[] = {
"foo", "foo", "aa", "foo", "baaba", "foo", "foo"};
for (auto subrange : r) {
std::string str(subrange.begin(), subrange.end());
EXPECT_EQ(str, strs[count]);
++count;
}
EXPECT_EQ(count, 7);
}
}
#if BOOST_PARSER_USE_CONCEPTS && (!defined(__GNUC__) || 12 <= __GNUC__)
// Older GCCs don't like the use of temporaries like the std::string("foo")
// below. This causes | join to break.
TEST(replace, join_compat)
{
{
char const str[] = "XYZXYZaaXYZbaabaXYZXYZ";
auto rng = str | bp::as_utf32 |
bp::replace(bp::lit("XYZ"), "foo" | bp::as_utf8) |
std::views::join;
std::string replace_result;
for (auto ch : rng) {
static_assert(std::is_same_v<decltype(ch), char32_t>);
replace_result.push_back(ch);
}
EXPECT_EQ(replace_result, "foofooaafoobaabafoofoo");
}
{
char const str[] = "XYZXYZaaXYZbaabaXYZXYZ";
auto rng = str | bp::replace(bp::lit("XYZ"), "foo") | std::views::join;
std::string replace_result;
for (auto ch : rng) {
replace_result.push_back(ch);
}
EXPECT_EQ(replace_result, "foofooaafoobaabafoofoo");
}
{
std::string str = "XYZXYZaaXYZbaabaXYZXYZ";
auto rng = str | bp::replace(bp::lit("XYZ"), "foo") | std::views::join;
std::string replace_result;
for (auto ch : rng) {
replace_result.push_back(ch);
}
EXPECT_EQ(replace_result, "foofooaafoobaabafoofoo");
}
{
std::string const str = "XYZXYZaaXYZbaabaXYZXYZ";
auto rng = str | bp::replace(bp::lit("XYZ"), "foo") | std::views::join;
std::string replace_result;
for (auto ch : rng) {
replace_result.push_back(ch);
}
EXPECT_EQ(replace_result, "foofooaafoobaabafoofoo");
}
{
auto rng = std::string("XYZXYZaaXYZbaabaXYZXYZ") |
bp::replace(bp::lit("XYZ"), "foo") | std::views::join;
std::string replace_result;
for (auto ch : rng) {
replace_result.push_back(ch);
}
EXPECT_EQ(replace_result, "foofooaafoobaabafoofoo");
}
}
#endif
TEST(replace, doc_examples)
{
// clang-format off
{
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 My credit card number is XXXX-XXXX-XXXX-XXXX.
for (auto subrange : rng) {
std::cout << std::string_view(subrange.begin(), subrange.end() - subrange.begin());
++count;
}
std::cout << "\n";
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

754
test/search.cpp Normal file
View File

@@ -0,0 +1,754 @@
/**
* 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/search.hpp>
#include <gtest/gtest.h>
namespace bp = boost::parser;
#if BOOST_PARSER_USE_CONCEPTS
namespace deduction {
std::string str;
auto const parser = bp::char_;
auto const skip = bp::ws;
auto deduced_1 = bp::search_all_view(str, parser, skip, bp::trace::on);
auto deduced_2 = bp::search_all_view(str, parser, skip);
auto deduced_3 = bp::search_all_view(str, parser, bp::trace::on);
auto deduced_4 = bp::search_all_view(str, parser);
}
#endif
TEST(search, search_range_skip)
{
// array of char
{
char const str[] = "";
auto result = bp::search(str, bp::lit("XYZ"), bp::ws);
EXPECT_EQ(result.begin(), str);
EXPECT_EQ(result.end(), str);
}
{
char const str[] = "not here";
auto result = bp::search(str, bp::lit("XYZ"), bp::ws);
EXPECT_EQ(result.begin(), std::end(str) - 1);
EXPECT_EQ(result.end(), std::end(str) - 1);
}
{
char const str[] = "aaXYZb";
auto result = bp::search(str, bp::lit("XYZ"), bp::ws);
EXPECT_EQ(result.begin(), str + 2);
EXPECT_EQ(result.end(), str + 5);
}
{
char const str[] = "XYZab";
auto result = bp::search(str, bp::lit("XYZ"), bp::ws);
EXPECT_EQ(result.begin(), str + 0);
EXPECT_EQ(result.end(), str + 3);
}
{
char const str[] = "gbXYZ";
auto result = bp::search(str, bp::lit("XYZ"), bp::ws);
EXPECT_EQ(result.begin(), str + 2);
EXPECT_EQ(result.end(), str + 5);
}
{
char const str[] = "XYZ";
auto result = bp::search(str, bp::lit("XYZ"), bp::ws);
EXPECT_EQ(result.begin(), str + 0);
EXPECT_EQ(result.end(), str + 3);
}
{
char const str[] = "XXYZZ";
auto result = bp::search(str, bp::lit("XYZ"), bp::ws);
EXPECT_EQ(result.begin(), str + 1);
EXPECT_EQ(result.end(), str + 4);
}
{
char const * str = "XXYZZ";
auto result = bp::search(str, bp::lit("XYZ"), bp::ws);
EXPECT_EQ(result.begin(), str + 1);
EXPECT_EQ(result.end(), str + 4);
}
#if BOOST_PARSER_USE_CONCEPTS
{
auto result = bp::search(std::string("XXYZZ"), bp::lit("XYZ"), bp::ws);
static_assert(std::same_as<decltype(result), std::ranges::dangling>);
}
#endif
// array of char | as_utf32
{
char const str[] = "";
auto result = bp::search(str | bp::as_utf32, bp::lit("XYZ"), bp::ws);
EXPECT_EQ(result.begin(), str);
EXPECT_EQ(result.end(), str);
}
{
char const str[] = "not here";
auto result = bp::search(str | bp::as_utf32, bp::lit("XYZ"), bp::ws);
EXPECT_EQ(result.begin(), std::end(str) - 1);
EXPECT_EQ(result.end(), std::end(str) - 1);
}
{
char const str[] = "aaXYZb";
auto result = bp::search(str | bp::as_utf32, bp::lit("XYZ"), bp::ws);
EXPECT_EQ(result.begin(), str + 2);
EXPECT_EQ(result.end(), str + 5);
}
{
char const str[] = "XYZab";
auto result = bp::search(str | bp::as_utf32, bp::lit("XYZ"), bp::ws);
EXPECT_EQ(result.begin(), str + 0);
EXPECT_EQ(result.end(), str + 3);
}
{
char const str[] = "gbXYZ";
auto result = bp::search(str | bp::as_utf32, bp::lit("XYZ"), bp::ws);
EXPECT_EQ(result.begin(), str + 2);
EXPECT_EQ(result.end(), str + 5);
}
{
char const str[] = "XYZ";
auto result = bp::search(str | bp::as_utf32, bp::lit("XYZ"), bp::ws);
EXPECT_EQ(result.begin(), str + 0);
EXPECT_EQ(result.end(), str + 3);
}
{
char const str[] = "XXYZZ";
auto result = bp::search(str | bp::as_utf32, bp::lit("XYZ"), bp::ws);
EXPECT_EQ(result.begin(), str + 1);
EXPECT_EQ(result.end(), str + 4);
}
// pointer
{
char const * str = "";
auto result = bp::search(str, bp::lit("XYZ"), bp::ws);
EXPECT_EQ(result.begin(), str);
EXPECT_EQ(result.end(), str);
}
{
char const * str = "not here";
auto result = bp::search(str, bp::lit("XYZ"), bp::ws);
EXPECT_TRUE(result.begin() == bp::null_sentinel_t{});
EXPECT_TRUE(result.end() == bp::null_sentinel_t{});
}
{
char const * str = "aaXYZb";
auto result = bp::search(str, bp::lit("XYZ"), bp::ws);
EXPECT_EQ(result.begin(), str + 2);
EXPECT_EQ(result.end(), str + 5);
}
{
char const * str = "XYZab";
auto result = bp::search(str, bp::lit("XYZ"), bp::ws);
EXPECT_EQ(result.begin(), str + 0);
EXPECT_EQ(result.end(), str + 3);
}
{
char const * str = "gbXYZ";
auto result = bp::search(str, bp::lit("XYZ"), bp::ws);
EXPECT_EQ(result.begin(), str + 2);
EXPECT_EQ(result.end(), str + 5);
}
{
char const * str = "XYZ";
auto result = bp::search(str, bp::lit("XYZ"), bp::ws);
EXPECT_EQ(result.begin(), str + 0);
EXPECT_EQ(result.end(), str + 3);
}
{
char const * str = "XXYZZ";
auto result = bp::search(str, bp::lit("XYZ"), bp::ws);
EXPECT_EQ(result.begin(), str + 1);
EXPECT_EQ(result.end(), str + 4);
}
// pointer
{
char const * str_ = "";
auto str = str_ | bp::as_utf32;
auto result = bp::search(str, bp::lit("XYZ"), bp::ws);
EXPECT_EQ(result.begin(), str.begin());
EXPECT_EQ(result.end(), str.end());
}
{
char const * str_ = "not here";
auto str = str_ | bp::as_utf16;
auto result = bp::search(str, bp::lit("XYZ"), bp::ws);
EXPECT_TRUE(result.begin() == str.end());
EXPECT_TRUE(result.end() == str.end());
}
{
char const * str_ = "aaXYZb";
auto str = str_ | bp::as_utf8;
auto result = bp::search(str, bp::lit("XYZ"), bp::ws);
EXPECT_EQ(result.begin(), std::next(str.begin(), 2));
EXPECT_EQ(result.end(), std::next(str.begin(), 5));
}
{
char const * str_ = "XYZab";
auto str = str_ | bp::as_utf32;
auto result = bp::search(str, bp::lit("XYZ"), bp::ws);
EXPECT_EQ(result.begin(), std::next(str.begin(), 0));
EXPECT_EQ(result.end(), std::next(str.begin(), 3));
}
{
char const * str_ = "gbXYZ";
auto str = str_ | bp::as_utf16;
auto result = bp::search(str, bp::lit("XYZ"), bp::ws);
EXPECT_EQ(result.begin(), std::next(str.begin(), 2));
EXPECT_EQ(result.end(), std::next(str.begin(), 5));
}
{
char const * str_ = "XYZ";
auto str = str_ | bp::as_utf8;
auto result = bp::search(str, bp::lit("XYZ"), bp::ws);
EXPECT_EQ(result.begin(), std::next(str.begin(), 0));
EXPECT_EQ(result.end(), std::next(str.begin(), 3));
}
{
char const * str_ = "XXYZZ";
auto str = str_ | bp::as_utf32;
auto result = bp::search(str, bp::lit("XYZ"), bp::ws);
EXPECT_EQ(result.begin(), std::next(str.begin(), 1));
EXPECT_EQ(result.end(), std::next(str.begin(), 4));
}
}
TEST(search, search_iters_skip)
{
// array of char
{
char const str[] = "XYZab";
auto result =
bp::search(std::begin(str), std::end(str), bp::lit("XYZ"), bp::ws);
EXPECT_EQ(result.begin(), str + 0);
EXPECT_EQ(result.end(), str + 3);
}
{
char const str[] = "gbXYZ";
auto result =
bp::search(std::begin(str), std::end(str), bp::lit("XYZ"), bp::ws);
EXPECT_EQ(result.begin(), str + 2);
EXPECT_EQ(result.end(), str + 5);
}
{
char const str[] = "XYZ";
auto result =
bp::search(std::begin(str), std::end(str), bp::lit("XYZ"), bp::ws);
EXPECT_EQ(result.begin(), str + 0);
EXPECT_EQ(result.end(), str + 3);
}
// array of char | as_utf32
{
char const str_[] = "";
auto str = str_ | bp::as_utf32;
auto result =
bp::search(str.begin(), str.end(), bp::lit("XYZ"), bp::ws);
EXPECT_EQ(result.begin(), str_);
EXPECT_EQ(result.end(), str_);
}
{
char const str_[] = "XYZ";
auto str = str_ | bp::as_utf32;
auto result =
bp::search(str.begin(), str.end(), bp::lit("XYZ"), bp::ws);
EXPECT_EQ(result.begin(), str_ + 0);
EXPECT_EQ(result.end(), str_ + 3);
}
{
char const str_[] = "XXYZZ";
auto str = str_ | bp::as_utf32;
auto result =
bp::search(str.begin(), str.end(), bp::lit("XYZ"), bp::ws);
EXPECT_EQ(result.begin(), str_ + 1);
EXPECT_EQ(result.end(), str_ + 4);
}
// pointer
{
char const * str = "";
auto result =
bp::search(str, bp::null_sentinel_t{}, bp::lit("XYZ"), bp::ws);
EXPECT_EQ(result.begin(), str);
EXPECT_EQ(result.end(), str);
}
{
char const * str = "not here";
auto result =
bp::search(str, bp::null_sentinel_t{}, bp::lit("XYZ"), bp::ws);
EXPECT_TRUE(result.begin() == bp::null_sentinel_t{});
EXPECT_TRUE(result.end() == bp::null_sentinel_t{});
}
{
char const * str = "XXYZZ";
auto result =
bp::search(str, bp::null_sentinel_t{}, bp::lit("XYZ"), bp::ws);
EXPECT_EQ(result.begin(), str + 1);
EXPECT_EQ(result.end(), str + 4);
}
// pointer
{
char const * str_ = "XYZab";
auto str = str_ | bp::as_utf32;
auto result =
bp::search(str.begin(), str.end(), bp::lit("XYZ"), bp::ws);
EXPECT_EQ(result.begin(), std::next(str.begin(), 0));
EXPECT_EQ(result.end(), std::next(str.begin(), 3));
}
{
char const * str_ = "gbXYZ";
auto str = str_ | bp::as_utf16;
auto result =
bp::search(str.begin(), str.end(), bp::lit("XYZ"), bp::ws);
EXPECT_EQ(result.begin(), std::next(str.begin(), 2));
EXPECT_EQ(result.end(), std::next(str.begin(), 5));
}
{
char const * str_ = "XYZ";
auto str = str_ | bp::as_utf8;
auto result =
bp::search(str.begin(), str.end(), bp::lit("XYZ"), bp::ws);
EXPECT_EQ(result.begin(), std::next(str.begin(), 0));
EXPECT_EQ(result.end(), std::next(str.begin(), 3));
}
}
TEST(search, search_range_no_skip)
{
// array of char
{
char const str[] = "XYZab";
auto result = bp::search(str, bp::lit("XYZ"));
EXPECT_EQ(result.begin(), str + 0);
EXPECT_EQ(result.end(), str + 3);
}
{
char const str[] = "gbXYZ";
auto result = bp::search(str, bp::lit("XYZ"));
EXPECT_EQ(result.begin(), str + 2);
EXPECT_EQ(result.end(), str + 5);
}
{
char const str[] = "XYZ";
auto result = bp::search(str, bp::lit("XYZ"));
EXPECT_EQ(result.begin(), str + 0);
EXPECT_EQ(result.end(), str + 3);
}
// array of char | as_utf32
{
char const str[] = "";
auto result = bp::search(str | bp::as_utf32, bp::lit("XYZ"));
EXPECT_EQ(result.begin(), str);
EXPECT_EQ(result.end(), str);
}
{
char const str[] = "XYZ";
auto result = bp::search(str | bp::as_utf32, bp::lit("XYZ"));
EXPECT_EQ(result.begin(), str + 0);
EXPECT_EQ(result.end(), str + 3);
}
{
char const str[] = "XXYZZ";
auto result = bp::search(str | bp::as_utf32, bp::lit("XYZ"));
EXPECT_EQ(result.begin(), str + 1);
EXPECT_EQ(result.end(), str + 4);
}
// pointer
{
char const * str = "";
auto result = bp::search(str, bp::lit("XYZ"));
EXPECT_EQ(result.begin(), str);
EXPECT_EQ(result.end(), str);
}
{
char const * str = "not here";
auto result = bp::search(str, bp::lit("XYZ"));
EXPECT_TRUE(result.begin() == bp::null_sentinel_t{});
EXPECT_TRUE(result.end() == bp::null_sentinel_t{});
}
{
char const * str = "XXYZZ";
auto result = bp::search(str, bp::lit("XYZ"));
EXPECT_EQ(result.begin(), str + 1);
EXPECT_EQ(result.end(), str + 4);
}
// pointer
{
char const * str_ = "XYZab";
auto str = str_ | bp::as_utf32;
auto result = bp::search(str, bp::lit("XYZ"));
EXPECT_EQ(result.begin(), std::next(str.begin(), 0));
EXPECT_EQ(result.end(), std::next(str.begin(), 3));
}
{
char const * str_ = "gbXYZ";
auto str = str_ | bp::as_utf16;
auto result = bp::search(str, bp::lit("XYZ"));
EXPECT_EQ(result.begin(), std::next(str.begin(), 2));
EXPECT_EQ(result.end(), std::next(str.begin(), 5));
}
{
char const * str_ = "XYZ";
auto str = str_ | bp::as_utf8;
auto result = bp::search(str, bp::lit("XYZ"));
EXPECT_EQ(result.begin(), std::next(str.begin(), 0));
EXPECT_EQ(result.end(), std::next(str.begin(), 3));
}
}
TEST(search, search_iters_no_skip)
{
// array of char
{
char const str[] = "XYZab";
auto result =
bp::search(std::begin(str), std::end(str), bp::lit("XYZ"));
EXPECT_EQ(result.begin(), str + 0);
EXPECT_EQ(result.end(), str + 3);
}
{
char const str[] = "gbXYZ";
auto result =
bp::search(std::begin(str), std::end(str), bp::lit("XYZ"));
EXPECT_EQ(result.begin(), str + 2);
EXPECT_EQ(result.end(), str + 5);
}
{
char const str[] = "XYZ";
auto result =
bp::search(std::begin(str), std::end(str), bp::lit("XYZ"));
EXPECT_EQ(result.begin(), str + 0);
EXPECT_EQ(result.end(), str + 3);
}
// array of char | as_utf32
{
char const str_[] = "";
auto str = str_ | bp::as_utf32;
auto result = bp::search(str.begin(), str.end(), bp::lit("XYZ"));
EXPECT_EQ(result.begin(), str_);
EXPECT_EQ(result.end(), str_);
}
{
char const str_[] = "XYZ";
auto str = str_ | bp::as_utf32;
auto result = bp::search(str.begin(), str.end(), bp::lit("XYZ"));
EXPECT_EQ(result.begin(), str_ + 0);
EXPECT_EQ(result.end(), str_ + 3);
}
{
char const str_[] = "XXYZZ";
auto str = str_ | bp::as_utf32;
auto result = bp::search(str.begin(), str.end(), bp::lit("XYZ"));
EXPECT_EQ(result.begin(), str_ + 1);
EXPECT_EQ(result.end(), str_ + 4);
}
// pointer
{
char const * str = "";
auto result = bp::search(str, bp::null_sentinel_t{}, bp::lit("XYZ"));
EXPECT_EQ(result.begin(), str);
EXPECT_EQ(result.end(), str);
}
{
char const * str = "not here";
auto result = bp::search(str, bp::null_sentinel_t{}, bp::lit("XYZ"));
EXPECT_TRUE(result.begin() == bp::null_sentinel_t{});
EXPECT_TRUE(result.end() == bp::null_sentinel_t{});
}
{
char const * str = "XXYZZ";
auto result = bp::search(str, bp::null_sentinel_t{}, bp::lit("XYZ"));
EXPECT_EQ(result.begin(), str + 1);
EXPECT_EQ(result.end(), str + 4);
}
// pointer
{
char const * str_ = "XYZab";
auto str = str_ | bp::as_utf32;
auto result = bp::search(str.begin(), str.end(), bp::lit("XYZ"));
EXPECT_EQ(result.begin(), std::next(str.begin(), 0));
EXPECT_EQ(result.end(), std::next(str.begin(), 3));
}
{
char const * str_ = "gbXYZ";
auto str = str_ | bp::as_utf16;
auto result = bp::search(str.begin(), str.end(), bp::lit("XYZ"));
EXPECT_EQ(result.begin(), std::next(str.begin(), 2));
EXPECT_EQ(result.end(), std::next(str.begin(), 5));
}
{
char const * str_ = "XYZ";
auto str = str_ | bp::as_utf8;
auto result = bp::search(str.begin(), str.end(), bp::lit("XYZ"));
EXPECT_EQ(result.begin(), std::next(str.begin(), 0));
EXPECT_EQ(result.end(), std::next(str.begin(), 3));
}
}
TEST(search, search_all)
{
{
auto r = bp::search_all("", bp::lit("XYZ"), bp::ws);
int count = 0;
for (auto subrange : r) {
(void)subrange;
++count;
}
EXPECT_EQ(count, 0);
}
{
char const str[] = "aaXYZb";
auto r = bp::search_all(str, bp::lit("XYZ"), bp::ws);
int count = 0;
int const offsets[] = {2, 5};
for (auto subrange : r) {
EXPECT_EQ(subrange.begin() - str, offsets[count * 2 + 0]);
EXPECT_EQ(subrange.end() - str, offsets[count * 2 + 1]);
++count;
}
EXPECT_EQ(count, 1);
}
{
char const str[] = "aaXYZbaabaXYZ";
auto r = str | bp::search_all(bp::lit("XYZ"), bp::ws, bp::trace::off);
int count = 0;
int const offsets[] = {2, 5, 10, 13};
for (auto subrange : r) {
EXPECT_EQ(subrange.begin() - str, offsets[count * 2 + 0]);
EXPECT_EQ(subrange.end() - str, offsets[count * 2 + 1]);
++count;
}
EXPECT_EQ(count, 2);
}
{
char const str[] = "aaXYZbaabaXYZ";
auto r = str | bp::search_all(bp::lit("XYZ"), bp::trace::off);
int count = 0;
int const offsets[] = {2, 5, 10, 13};
for (auto subrange : r) {
EXPECT_EQ(subrange.begin() - str, offsets[count * 2 + 0]);
EXPECT_EQ(subrange.end() - str, offsets[count * 2 + 1]);
++count;
}
EXPECT_EQ(count, 2);
}
{
char const str[] = "aaXYZbaabaXYZ";
auto r = str | bp::search_all(bp::lit("XYZ"));
int count = 0;
int const offsets[] = {2, 5, 10, 13};
for (auto subrange : r) {
EXPECT_EQ(subrange.begin() - str, offsets[count * 2 + 0]);
EXPECT_EQ(subrange.end() - str, offsets[count * 2 + 1]);
++count;
}
EXPECT_EQ(count, 2);
}
{
char const str[] = "aaXYZbaabaXYZXYZ";
auto r = str | bp::search_all(bp::lit("XYZ"));
int count = 0;
int const offsets[] = {2, 5, 10, 13, 13, 16};
for (auto subrange : r) {
EXPECT_EQ(subrange.begin() - str, offsets[count * 2 + 0]);
EXPECT_EQ(subrange.end() - str, offsets[count * 2 + 1]);
++count;
}
EXPECT_EQ(count, 3);
}
{
char const str[] = "XYZaaXYZbaabaXYZXYZ";
auto r = str | bp::search_all(bp::lit("XYZ"));
int count = 0;
int const offsets[] = {0, 3, 5, 8, 13, 16, 16, 19};
for (auto subrange : r) {
EXPECT_EQ(subrange.begin() - str, offsets[count * 2 + 0]);
EXPECT_EQ(subrange.end() - str, offsets[count * 2 + 1]);
++count;
}
EXPECT_EQ(count, 4);
}
{
char const str[] = "XYZXYZaaXYZbaabaXYZXYZ";
auto r = str | bp::search_all(bp::lit("XYZ"));
int count = 0;
int const offsets[] = {0, 3, 3, 6, 8, 11, 16, 19, 19, 22};
for (auto subrange : r) {
EXPECT_EQ(subrange.begin() - str, offsets[count * 2 + 0]);
EXPECT_EQ(subrange.end() - str, offsets[count * 2 + 1]);
++count;
}
EXPECT_EQ(count, 5);
}
{
char const * str = "XYZXYZaaXYZbaabaXYZXYZ";
auto r = str | bp::search_all(bp::lit("XYZ"));
int count = 0;
int const offsets[] = {0, 3, 3, 6, 8, 11, 16, 19, 19, 22};
for (auto subrange : r) {
EXPECT_EQ(subrange.begin() - str, offsets[count * 2 + 0]);
EXPECT_EQ(subrange.end() - str, offsets[count * 2 + 1]);
++count;
}
EXPECT_EQ(count, 5);
}
{
char const * str = "XYZXYZaaXYZbaabaXYZXYZ";
auto const r = str | bp::search_all(bp::lit("XYZ"));
int count = 0;
int const offsets[] = {0, 3, 3, 6, 8, 11, 16, 19, 19, 22};
for (auto subrange : r) {
EXPECT_EQ(subrange.begin() - str, offsets[count * 2 + 0]);
EXPECT_EQ(subrange.end() - str, offsets[count * 2 + 1]);
++count;
}
EXPECT_EQ(count, 5);
}
}
TEST(search, search_all_unicode)
{
{
char const str_[] = "";
auto str = str_ | bp::as_utf8;
auto r = bp::search_all(str, bp::lit("XYZ"), bp::ws);
int count = 0;
for (auto subrange : r) {
(void)subrange;
++count;
}
EXPECT_EQ(count, 0);
}
{
char const * str_ = "aaXYZb";
auto str = str_ | bp::as_utf16;
auto r = bp::search_all(str, bp::lit("XYZ"), bp::ws);
int count = 0;
int const offsets[] = {2, 5};
for (auto subrange : r) {
EXPECT_EQ(subrange.begin().base() - str_, offsets[count * 2 + 0]);
EXPECT_EQ(subrange.end().base() - str_, offsets[count * 2 + 1]);
++count;
}
EXPECT_EQ(count, 1);
}
{
char const str_[] = "aaXYZbaabaXYZ";
auto str = str_ | bp::as_utf32;
auto r = str | bp::search_all(bp::lit("XYZ"), bp::ws, bp::trace::off);
int count = 0;
int const offsets[] = {2, 5, 10, 13};
for (auto subrange : r) {
EXPECT_EQ(subrange.begin().base() - str_, offsets[count * 2 + 0]);
EXPECT_EQ(subrange.end().base() - str_, offsets[count * 2 + 1]);
++count;
}
EXPECT_EQ(count, 2);
}
{
char const str_[] = "aaXYZbaabaXYZ";
auto str = str_ | bp::as_utf8;
auto r = str | bp::search_all(bp::lit("XYZ"), bp::trace::off);
int count = 0;
int const offsets[] = {2, 5, 10, 13};
for (auto subrange : r) {
EXPECT_EQ(subrange.begin().base() - str_, offsets[count * 2 + 0]);
EXPECT_EQ(subrange.end().base() - str_, offsets[count * 2 + 1]);
++count;
}
EXPECT_EQ(count, 2);
}
{
char const str_[] = "aaXYZbaabaXYZ";
auto str = str_ | bp::as_utf16;
auto r = str | bp::search_all(bp::lit("XYZ"));
int count = 0;
int const offsets[] = {2, 5, 10, 13};
for (auto subrange : r) {
EXPECT_EQ(subrange.begin().base() - str_, offsets[count * 2 + 0]);
EXPECT_EQ(subrange.end().base() - str_, offsets[count * 2 + 1]);
++count;
}
EXPECT_EQ(count, 2);
}
{
char const str_[] = "aaXYZbaabaXYZXYZ";
auto str = str_ | bp::as_utf32;
auto r = str | bp::search_all(bp::lit("XYZ"));
int count = 0;
int const offsets[] = {2, 5, 10, 13, 13, 16};
for (auto subrange : r) {
EXPECT_EQ(subrange.begin().base() - str_, offsets[count * 2 + 0]);
EXPECT_EQ(subrange.end().base() - str_, offsets[count * 2 + 1]);
++count;
}
EXPECT_EQ(count, 3);
}
{
char const str_[] = "XYZaaXYZbaabaXYZXYZ";
auto str = str_ | bp::as_utf8;
auto r = str | bp::search_all(bp::lit("XYZ"));
int count = 0;
int const offsets[] = {0, 3, 5, 8, 13, 16, 16, 19};
for (auto subrange : r) {
EXPECT_EQ(subrange.begin().base() - str_, offsets[count * 2 + 0]);
EXPECT_EQ(subrange.end().base() - str_, offsets[count * 2 + 1]);
++count;
}
EXPECT_EQ(count, 4);
}
{
char const str_[] = "XYZXYZaaXYZbaabaXYZXYZ";
auto str = str_ | bp::as_utf16;
auto r = str | bp::search_all(bp::lit("XYZ"));
int count = 0;
int const offsets[] = {0, 3, 3, 6, 8, 11, 16, 19, 19, 22};
for (auto subrange : r) {
EXPECT_EQ(subrange.begin().base() - str_, offsets[count * 2 + 0]);
EXPECT_EQ(subrange.end().base() - str_, offsets[count * 2 + 1]);
++count;
}
EXPECT_EQ(count, 5);
}
}
TEST(search, doc_examples)
{
{
namespace bp = boost::parser;
auto result = bp::search("aaXYZq", bp::lit("XYZ"), bp::ws);
assert(!result.empty());
assert(
std::string_view(result.begin(), result.end() - result.begin()) ==
"XYZ");
}
{
auto r = "XYZaaXYZbaabaXYZXYZ" | bp::search_all(bp::lit("XYZ"));
int count = 0;
// Prints XYZ XYZ XYZ XYZ.
for (auto subrange : r) {
std::cout << std::string_view(subrange.begin(), subrange.end() - subrange.begin()) << " ";
++count;
}
std::cout << "\n";
assert(count == 4);
}
}

269
test/split.cpp Normal file
View File

@@ -0,0 +1,269 @@
/**
* 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/split.hpp>
#include <gtest/gtest.h>
namespace bp = boost::parser;
#if BOOST_PARSER_USE_CONCEPTS
namespace deduction {
std::string str;
auto const parser = bp::char_;
auto const skip = bp::ws;
auto deduced_1 = bp::split_view(str, parser, skip, bp::trace::on);
auto deduced_2 = bp::split_view(str, parser, skip);
auto deduced_3 = bp::split_view(str, parser, bp::trace::on);
auto deduced_4 = bp::split_view(str, parser);
}
#endif
TEST(split, split_)
{
{
auto r = bp::split("", bp::lit("XYZ"), bp::ws);
int count = 0;
for (auto subrange : r) {
(void)subrange;
++count;
}
EXPECT_EQ(count, 0);
}
{
char const str[] = "aaXYZb";
auto r = bp::split(str, bp::lit("XYZ"), bp::ws);
int count = 0;
int const offsets[] = {0, 2, 5, 6};
for (auto subrange : r) {
EXPECT_EQ(subrange.begin() - str, offsets[count * 2 + 0]);
EXPECT_EQ(subrange.end() - str, offsets[count * 2 + 1]);
++count;
}
EXPECT_EQ(count, 2);
}
{
char const str[] = "aaXYZbaabaXYZ";
auto r = str | bp::split(bp::lit("XYZ"), bp::ws, bp::trace::off);
int count = 0;
int const offsets[] = {0, 2, 5, 10, 13, 13};
for (auto subrange : r) {
EXPECT_EQ(subrange.begin() - str, offsets[count * 2 + 0]);
EXPECT_EQ(subrange.end() - str, offsets[count * 2 + 1]);
++count;
}
EXPECT_EQ(count, 3);
}
{
char const str[] = "aaXYZbaabaXYZ";
auto r = str | bp::split(bp::lit("XYZ"), bp::trace::off);
int count = 0;
int const offsets[] = {0, 2, 5, 10, 13, 13};
for (auto subrange : r) {
EXPECT_EQ(subrange.begin() - str, offsets[count * 2 + 0]);
EXPECT_EQ(subrange.end() - str, offsets[count * 2 + 1]);
++count;
}
EXPECT_EQ(count, 3);
}
{
char const str[] = "aaXYZbaabaXYZ";
auto r = str | bp::split(bp::lit("XYZ"));
int count = 0;
int const offsets[] = {0, 2, 5, 10, 13, 13};
for (auto subrange : r) {
EXPECT_EQ(subrange.begin() - str, offsets[count * 2 + 0]);
EXPECT_EQ(subrange.end() - str, offsets[count * 2 + 1]);
++count;
}
EXPECT_EQ(count, 3);
}
{
char const str[] = "aaXYZbaabaXYZXYZ";
auto r = str | bp::split(bp::lit("XYZ"));
int count = 0;
int const offsets[] = {0, 2, 5, 10, 13, 13, 16, 16};
for (auto subrange : r) {
EXPECT_EQ(subrange.begin() - str, offsets[count * 2 + 0]);
EXPECT_EQ(subrange.end() - str, offsets[count * 2 + 1]);
++count;
}
EXPECT_EQ(count, 4);
}
{
char const str[] = "XYZaaXYZbaabaXYZXYZ";
auto r = str | bp::split(bp::lit("XYZ"));
int count = 0;
int const offsets[] = {0, 0, 3, 5, 8, 13, 16, 16, 19, 19};
for (auto subrange : r) {
EXPECT_EQ(subrange.begin() - str, offsets[count * 2 + 0]);
EXPECT_EQ(subrange.end() - str, offsets[count * 2 + 1]);
++count;
}
EXPECT_EQ(count, 5);
}
{
char const str[] = "XYZXYZaaXYZbaabaXYZXYZ";
auto r = str | bp::split(bp::lit("XYZ"));
int count = 0;
int const offsets[] = {0, 0, 3, 3, 6, 8, 11, 16, 19, 19, 22, 22};
for (auto subrange : r) {
EXPECT_EQ(subrange.begin() - str, offsets[count * 2 + 0]);
EXPECT_EQ(subrange.end() - str, offsets[count * 2 + 1]);
++count;
}
EXPECT_EQ(count, 6);
}
{
char const * str = "XYZXYZaaXYZbaabaXYZXYZ";
auto r = str | bp::split(bp::lit("XYZ"));
int count = 0;
int const offsets[] = {0, 0, 3, 3, 6, 8, 11, 16, 19, 19, 22, 22};
for (auto subrange : r) {
EXPECT_EQ(subrange.begin() - str, offsets[count * 2 + 0]);
EXPECT_EQ(subrange.end() - str, offsets[count * 2 + 1]);
++count;
}
EXPECT_EQ(count, 6);
}
{
char const * str = "XYZXYZaaXYZbaabaXYZXYZ";
auto const r = str | bp::split(bp::lit("XYZ"));
int count = 0;
int const offsets[] = {0, 0, 3, 3, 6, 8, 11, 16, 19, 19, 22, 22};
for (auto subrange : r) {
EXPECT_EQ(subrange.begin() - str, offsets[count * 2 + 0]);
EXPECT_EQ(subrange.end() - str, offsets[count * 2 + 1]);
++count;
}
EXPECT_EQ(count, 6);
}
}
TEST(split, split_unicode)
{
{
char const str_[] = "";
auto str = str_ | bp::as_utf8;
auto r = bp::split(str, bp::lit("XYZ"), bp::ws);
int count = 0;
for (auto subrange : r) {
(void)subrange;
++count;
}
EXPECT_EQ(count, 0);
}
{
char const * str_ = "aaXYZb";
auto str = str_ | bp::as_utf16;
auto r = bp::split(str, bp::lit("XYZ"), bp::ws);
int count = 0;
int const offsets[] = {0, 2, 5, 6};
for (auto subrange : r) {
EXPECT_EQ(subrange.begin().base() - str_, offsets[count * 2 + 0]);
EXPECT_EQ(subrange.end().base() - str_, offsets[count * 2 + 1]);
++count;
}
EXPECT_EQ(count, 2);
}
{
char const str_[] = "aaXYZbaabaXYZ";
auto str = str_ | bp::as_utf32;
auto r = str | bp::split(bp::lit("XYZ"), bp::ws, bp::trace::off);
int count = 0;
int const offsets[] = {0, 2, 5, 10, 13, 13};
for (auto subrange : r) {
EXPECT_EQ(subrange.begin().base() - str_, offsets[count * 2 + 0]);
EXPECT_EQ(subrange.end().base() - str_, offsets[count * 2 + 1]);
++count;
}
EXPECT_EQ(count, 3);
}
{
char const str_[] = "aaXYZbaabaXYZ";
auto str = str_ | bp::as_utf8;
const auto r = str | bp::split(bp::lit("XYZ"), bp::trace::off);
int count = 0;
int const offsets[] = {0, 2, 5, 10, 13, 13};
for (auto subrange : r) {
EXPECT_EQ(subrange.begin().base() - str_, offsets[count * 2 + 0]);
EXPECT_EQ(subrange.end().base() - str_, offsets[count * 2 + 1]);
++count;
}
EXPECT_EQ(count, 3);
}
{
char const str_[] = "aaXYZbaabaXYZ";
auto str = str_ | bp::as_utf16;
auto r = str | bp::split(bp::lit("XYZ"));
int count = 0;
int const offsets[] = {0, 2, 5, 10, 13, 13};
for (auto subrange : r) {
EXPECT_EQ(subrange.begin().base() - str_, offsets[count * 2 + 0]);
EXPECT_EQ(subrange.end().base() - str_, offsets[count * 2 + 1]);
++count;
}
EXPECT_EQ(count, 3);
}
{
char const str_[] = "aaXYZbaabaXYZXYZ";
auto str = str_ | bp::as_utf32;
auto r = str | bp::split(bp::lit("XYZ"));
int count = 0;
int const offsets[] = {0, 2, 5, 10, 13, 13, 16, 16};
for (auto subrange : r) {
EXPECT_EQ(subrange.begin().base() - str_, offsets[count * 2 + 0]);
EXPECT_EQ(subrange.end().base() - str_, offsets[count * 2 + 1]);
++count;
}
EXPECT_EQ(count, 4);
}
{
char const str_[] = "XYZaaXYZbaabaXYZXYZ";
auto str = str_ | bp::as_utf8;
auto r = str | bp::split(bp::lit("XYZ"));
int count = 0;
int const offsets[] = {0, 0, 3, 5, 8, 13, 16, 16, 19, 19};
for (auto subrange : r) {
EXPECT_EQ(subrange.begin().base() - str_, offsets[count * 2 + 0]);
EXPECT_EQ(subrange.end().base() - str_, offsets[count * 2 + 1]);
++count;
}
EXPECT_EQ(count, 5);
}
{
char const str_[] = "XYZXYZaaXYZbaabaXYZXYZ";
auto str = str_ | bp::as_utf16;
auto r = str | bp::split(bp::lit("XYZ"));
int count = 0;
int const offsets[] = {0, 0, 3, 3, 6, 8, 11, 16, 19, 19, 22, 22};
for (auto subrange : r) {
EXPECT_EQ(subrange.begin().base() - str_, offsets[count * 2 + 0]);
EXPECT_EQ(subrange.end().base() - str_, offsets[count * 2 + 1]);
++count;
}
EXPECT_EQ(count, 6);
}
}
TEST(split, doc_examples)
{
{
auto r = "XYZaaXYZbaabaXYZXYZ" | bp::split(bp::lit("XYZ"));
int count = 0;
// Prints '' 'aa' 'baaba' '' ''.
for (auto subrange : r) {
std::cout << "'" << std::string_view(subrange.begin(), subrange.end() - subrange.begin()) << "' ";
++count;
}
std::cout << "\n";
assert(count == 5);
}
}

View File

@@ -297,6 +297,15 @@ int main()
PARSE(string("h"));
std::cout << "\n\n"
<< "----------------------------------------\n"
<< "| quoted_string() |\n"
<< "----------------------------------------\n";
PARSE(quoted_string);
PARSE(quoted_string('\''));
PARSE(quoted_string("'\""));
std::cout << "\n\n"
<< "----------------------------------------\n"
<< "| eol |\n"
@@ -313,21 +322,52 @@ int main()
std::cout << "\n\n"
<< "----------------------------------------\n"
<< "| ascii character classes |\n"
<< "| blank |\n"
<< "----------------------------------------\n";
PARSE(ascii::alnum);
PARSE(ascii::alpha);
PARSE(ascii::blank);
PARSE(ascii::cntrl);
PARSE(ascii::digit);
PARSE(ascii::graph);
PARSE(ascii::print);
PARSE(ascii::punct);
PARSE(ascii::space);
PARSE(ascii::xdigit);
PARSE(ascii::lower);
PARSE(ascii::upper);
PARSE(blank);
std::cout << "\n\n"
<< "----------------------------------------\n"
<< "| digit |\n"
<< "----------------------------------------\n";
PARSE(digit);
std::cout << "\n\n"
<< "----------------------------------------\n"
<< "| control |\n"
<< "----------------------------------------\n";
PARSE(control);
std::cout << "\n\n"
<< "----------------------------------------\n"
<< "| punct |\n"
<< "----------------------------------------\n";
PARSE(punct);
std::cout << "\n\n"
<< "----------------------------------------\n"
<< "| lower |\n"
<< "----------------------------------------\n";
PARSE(lower);
std::cout << "\n\n"
<< "----------------------------------------\n"
<< "| upper |\n"
<< "----------------------------------------\n";
PARSE(upper);
std::cout << "\n\n"
<< "----------------------------------------\n"
<< "| hex_digit |\n"
<< "----------------------------------------\n";
PARSE(hex_digit);
std::cout << "\n\n"
<< "----------------------------------------\n"

859
test/transform_replace.cpp Normal file
View File

@@ -0,0 +1,859 @@
/**
* 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/transform_replace.hpp>
#include <gtest/gtest.h>
#include "ill_formed.hpp"
#include <list>
#if (!defined(_MSC_VER) || BOOST_PARSER_USE_CONCEPTS)
namespace bp = boost::parser;
auto f_str = [](std::vector<int> const & ints) {
std::string retval;
for (auto x : ints) {
retval += std::to_string(x);
retval += '_';
}
return retval;
};
auto f_str_ref = [](std::vector<int> const & ints) -> std::string & {
static std::string retval;
for (auto x : ints) {
retval += std::to_string(x);
retval += '_';
}
return retval;
};
#if BOOST_PARSER_USE_CONCEPTS
namespace deduction {
using namespace std::literals;
std::string str;
auto const parser = bp::int_ % ',';
auto const skip = bp::ws;
using attr_t = std::vector<int>;
auto deduced_1 = bp::transform_replace_view(
str,
parser,
skip,
bp::detail::utf_rvalue_shim<std::string, decltype(f_str), attr_t>(
f_str),
bp::trace::on);
auto deduced_2 = bp::transform_replace_view(
str,
parser,
skip,
bp::detail::utf_rvalue_shim<std::string, decltype(f_str), attr_t>(
f_str));
auto deduced_3 = bp::transform_replace_view(
str,
parser,
bp::detail::utf_rvalue_shim<std::string, decltype(f_str), attr_t>(
f_str),
bp::trace::on);
auto deduced_4 = bp::transform_replace_view(
str,
parser,
bp::detail::utf_rvalue_shim<std::string, decltype(f_str), attr_t>(
f_str));
}
#endif
namespace detail_attr_type {
constexpr auto int_char_p = bp::int_ >> bp::char_;
static_assert(
std::is_same_v<
bp::detail::range_attr_t<std::string, decltype(int_char_p.parser_)>,
bp::tuple<int, char>>);
static_assert(
std::is_same_v<
bp::detail::
range_attr_t<std::u32string, decltype(int_char_p.parser_)>,
bp::tuple<int, char32_t>>);
constexpr auto ints_p = *bp::int_;
static_assert(
std::is_same_v<
bp::detail::range_attr_t<std::u32string, decltype(ints_p.parser_)>,
std::vector<int>>);
}
#if defined(__cpp_char8_t)
auto f_u8str = [](std::vector<int> ints) {
std::u8string retval;
for (auto x : ints) {
auto const s = std::to_string(x);
retval.insert(retval.end(), s.begin(), s.end());
retval += '_';
}
return retval;
};
// NOTE: *const* & return type!
auto f_u8str_ref = [](std::vector<int> ints) -> std::u8string const & {
static std::u8string retval;
for (auto x : ints) {
auto const s = std::to_string(x);
retval.insert(retval.end(), s.begin(), s.end());
retval += '_';
}
return retval;
};
#endif
auto f_u16str = [](std::vector<int> ints) {
std::u16string retval;
for (auto x : ints) {
auto const s = std::to_string(x);
retval.insert(retval.end(), s.begin(), s.end());
retval += '_';
}
return retval;
};
auto f_u16str_ref = [](std::vector<int> ints) -> std::u16string & {
static std::u16string retval;
for (auto x : ints) {
auto const s = std::to_string(x);
retval.insert(retval.end(), s.begin(), s.end());
retval += '_';
}
return retval;
};
auto f_u32str = [](std::vector<int> ints) {
std::u32string retval;
for (auto x : ints) {
auto const s = std::to_string(x);
retval.insert(retval.end(), s.begin(), s.end());
retval += '_';
}
return retval;
};
auto f_u32str_ref = [](std::vector<int> ints) -> std::u32string & {
static std::u32string retval;
for (auto x : ints) {
auto const s = std::to_string(x);
retval.insert(retval.end(), s.begin(), s.end());
retval += '_';
}
return retval;
};
namespace detail_utf_rvalue_shim {
constexpr auto ints_p = *bp::int_;
using attr_t = std::vector<int>;
// char -> char
bp::detail::utf_rvalue_shim<std::string, decltype(f_str), attr_t>
char_char_shim(f_str);
static_assert(
std::is_same_v<decltype(char_char_shim(attr_t{})), std::string &>);
static_assert(bp::detail::transform_replacement_for<
decltype(char_char_shim),
std::string,
decltype(ints_p.parser_)>);
bp::detail::utf_rvalue_shim<std::string, decltype(f_str_ref), attr_t>
char_char_ref_shim(f_str_ref);
static_assert(
std::is_same_v<decltype(char_char_ref_shim(attr_t{})), std::string &>);
static_assert(bp::detail::transform_replacement_for<
decltype(char_char_ref_shim),
std::string,
decltype(ints_p.parser_)>);
#if defined(__cpp_char8_t) && BOOST_PARSER_USE_CONCEPTS
// char8_t -> char8_t
bp::detail::utf_rvalue_shim<std::u8string, decltype(f_u8str), attr_t>
u8_u8_shim(f_u8str);
static_assert(
std::is_same_v<decltype(u8_u8_shim(attr_t{})), std::u8string &>);
static_assert(bp::detail::transform_replacement_for<
decltype(u8_u8_shim),
std::u8string,
decltype(ints_p.parser_)>);
bp::detail::utf_rvalue_shim<std::u8string, decltype(f_u8str_ref), attr_t>
u8_u8_ref_shim(f_u8str_ref);
static_assert(std::is_same_v<
decltype(u8_u8_ref_shim(attr_t{})),
std::u8string const &>);
static_assert(bp::detail::transform_replacement_for<
decltype(u8_u8_ref_shim),
std::u8string,
decltype(ints_p.parser_)>);
// char8_t -> char16_t
bp::detail::utf_rvalue_shim<std::u8string, decltype(f_u16str), attr_t>
u8_u16_shim(f_u16str);
#if BOOST_PARSER_DETAIL_TEXT_USE_ALIAS_CTAD
static_assert(std::is_same_v<
decltype(u8_u16_shim(attr_t{})),
bp::detail::text::utf_view<
bp::detail::text::format::utf8,
std::ranges::owning_view<std::u16string>> &>);
#endif
static_assert(bp::detail::transform_replacement_for<
decltype(u8_u16_shim),
std::u8string,
decltype(ints_p.parser_)>);
bp::detail::utf_rvalue_shim<std::u8string, decltype(f_u16str_ref), attr_t>
u8_u16_ref_shim(f_u16str_ref);
#if BOOST_PARSER_DETAIL_TEXT_USE_ALIAS_CTAD
static_assert(std::is_same_v<
decltype(u8_u16_ref_shim(attr_t{})),
bp::detail::text::utf_view<
bp::detail::text::format::utf8,
std::ranges::ref_view<std::u16string>> &>);
#endif
static_assert(bp::detail::transform_replacement_for<
decltype(u8_u16_ref_shim),
std::u8string,
decltype(ints_p.parser_)>);
// char8_t -> char32_t
bp::detail::utf_rvalue_shim<std::u8string, decltype(f_u32str), attr_t>
u8_u32_shim(f_u32str);
#if BOOST_PARSER_DETAIL_TEXT_USE_ALIAS_CTAD
static_assert(std::is_same_v<
decltype(u8_u32_shim(attr_t{})),
bp::detail::text::utf_view<
bp::detail::text::format::utf8,
std::ranges::owning_view<std::u32string>> &>);
#endif
static_assert(bp::detail::transform_replacement_for<
decltype(u8_u32_shim),
std::u8string,
decltype(ints_p.parser_)>);
bp::detail::utf_rvalue_shim<std::u8string, decltype(f_u32str_ref), attr_t>
u8_u32_ref_shim(f_u32str_ref);
#if BOOST_PARSER_DETAIL_TEXT_USE_ALIAS_CTAD
static_assert(std::is_same_v<
decltype(u8_u32_ref_shim(attr_t{})),
bp::detail::text::utf_view<
bp::detail::text::format::utf8,
std::ranges::ref_view<std::u32string>> &>);
#endif
static_assert(bp::detail::transform_replacement_for<
decltype(u8_u32_ref_shim),
std::u8string,
decltype(ints_p.parser_)>);
// char16_t -> char8_t
bp::detail::utf_rvalue_shim<std::u16string, decltype(f_u8str), attr_t>
u16_u8_shim(f_u8str);
#if BOOST_PARSER_DETAIL_TEXT_USE_ALIAS_CTAD
static_assert(std::is_same_v<
decltype(u16_u8_shim(attr_t{})),
bp::detail::text::utf_view<
bp::detail::text::format::utf16,
std::ranges::owning_view<std::u8string>> &>);
#endif
static_assert(bp::detail::transform_replacement_for<
decltype(u16_u8_shim),
std::u16string,
decltype(ints_p.parser_)>);
bp::detail::utf_rvalue_shim<std::u16string, decltype(f_u8str_ref), attr_t>
u16_u8_ref_shim(f_u8str_ref);
#if BOOST_PARSER_DETAIL_TEXT_USE_ALIAS_CTAD
static_assert(std::is_same_v<
decltype(u16_u8_ref_shim(attr_t{})),
bp::detail::text::utf_view<
bp::detail::text::format::utf16,
std::ranges::ref_view<std::u8string const>> &>);
#endif
static_assert(bp::detail::transform_replacement_for<
decltype(u16_u8_ref_shim),
std::u16string,
decltype(ints_p.parser_)>);
// char32_t -> char8_t
bp::detail::utf_rvalue_shim<std::u32string, decltype(f_u8str), attr_t>
u32_u8_shim(f_u8str);
#if BOOST_PARSER_DETAIL_TEXT_USE_ALIAS_CTAD
static_assert(std::is_same_v<
decltype(u32_u8_shim(attr_t{})),
bp::detail::text::utf_view<
bp::detail::text::format::utf32,
std::ranges::owning_view<std::u8string>> &>);
#endif
static_assert(bp::detail::transform_replacement_for<
decltype(u32_u8_shim),
std::u32string,
decltype(ints_p.parser_)>);
bp::detail::utf_rvalue_shim<std::u32string, decltype(f_u8str_ref), attr_t>
u32_u8_ref_shim(f_u8str_ref);
#if BOOST_PARSER_DETAIL_TEXT_USE_ALIAS_CTAD
static_assert(std::is_same_v<
decltype(u32_u8_ref_shim(attr_t{})),
bp::detail::text::utf_view<
bp::detail::text::format::utf32,
std::ranges::ref_view<std::u8string const>> &>);
#endif
static_assert(bp::detail::transform_replacement_for<
decltype(u32_u8_ref_shim),
std::u32string,
decltype(ints_p.parser_)>);
#endif
// char16_t -> char16_t
bp::detail::utf_rvalue_shim<std::u16string, decltype(f_u16str), attr_t>
u16_u16_shim(f_u16str);
static_assert(
std::is_same_v<decltype(u16_u16_shim(attr_t{})), std::u16string &>);
static_assert(bp::detail::transform_replacement_for<
decltype(u16_u16_shim),
std::u16string,
decltype(ints_p.parser_)>);
bp::detail::utf_rvalue_shim<std::u16string, decltype(f_u16str_ref), attr_t>
u16_u16_ref_shim(f_u16str_ref);
static_assert(
std::is_same_v<decltype(u16_u16_ref_shim(attr_t{})), std::u16string &>);
static_assert(bp::detail::transform_replacement_for<
decltype(u16_u16_ref_shim),
std::u16string,
decltype(ints_p.parser_)>);
// char16_t -> char32_t
bp::detail::utf_rvalue_shim<std::u16string, decltype(f_u32str), attr_t>
u16_u32_shim(f_u32str);
#if BOOST_PARSER_USE_CONCEPTS
#if BOOST_PARSER_DETAIL_TEXT_USE_ALIAS_CTAD
static_assert(std::is_same_v<
decltype(u16_u32_shim(attr_t{})),
bp::detail::text::utf_view<
bp::detail::text::format::utf16,
std::ranges::owning_view<std::u32string>> &>);
#endif
#else
static_assert(
std::is_same_v<
decltype(u16_u32_shim(attr_t{})),
bp::detail::text::utf16_view<
bp::detail::text::detail::owning_view<std::u32string>> &>);
#endif
static_assert(bp::detail::transform_replacement_for<
decltype(u16_u32_shim),
std::u16string,
decltype(ints_p.parser_)>);
bp::detail::utf_rvalue_shim<std::u16string, decltype(f_u32str_ref), attr_t>
u16_u32_ref_shim(f_u32str_ref);
#if BOOST_PARSER_USE_CONCEPTS
#if BOOST_PARSER_DETAIL_TEXT_USE_ALIAS_CTAD
static_assert(std::is_same_v<
decltype(u16_u32_ref_shim(attr_t{})),
bp::detail::text::utf_view<
bp::detail::text::format::utf16,
std::ranges::ref_view<std::u32string>> &>);
#endif
#else
static_assert(std::is_same_v<
decltype(u16_u32_ref_shim(attr_t{})),
bp::detail::text::utf16_view<
bp::detail::text::detail::ref_view<std::u32string>> &>);
#endif
static_assert(bp::detail::transform_replacement_for<
decltype(u16_u32_ref_shim),
std::u16string,
decltype(ints_p.parser_)>);
// char32_t -> char32_t
bp::detail::utf_rvalue_shim<std::u32string, decltype(f_u32str), attr_t>
u32_u32_shim(f_u32str);
static_assert(
std::is_same_v<decltype(u32_u32_shim(attr_t{})), std::u32string &>);
static_assert(bp::detail::transform_replacement_for<
decltype(u32_u32_shim),
std::u32string,
decltype(ints_p.parser_)>);
bp::detail::utf_rvalue_shim<std::u32string, decltype(f_u32str_ref), attr_t>
u32_u32_ref_shim(f_u32str_ref);
static_assert(
std::is_same_v<decltype(u32_u32_ref_shim(attr_t{})), std::u32string &>);
static_assert(bp::detail::transform_replacement_for<
decltype(u32_u32_ref_shim),
std::u32string,
decltype(ints_p.parser_)>);
// char32_t -> char16_t
bp::detail::utf_rvalue_shim<std::u32string, decltype(f_u16str), attr_t>
u32_u16_shim(f_u16str);
#if BOOST_PARSER_USE_CONCEPTS
#if BOOST_PARSER_DETAIL_TEXT_USE_ALIAS_CTAD
static_assert(std::is_same_v<
decltype(u32_u16_shim(attr_t{})),
bp::detail::text::utf_view<
bp::detail::text::format::utf32,
std::ranges::owning_view<std::u16string>> &>);
#endif
#else
static_assert(
std::is_same_v<
decltype(u32_u16_shim(attr_t{})),
bp::detail::text::utf32_view<
bp::detail::text::detail::owning_view<std::u16string>> &>);
#endif
static_assert(bp::detail::transform_replacement_for<
decltype(u32_u16_shim),
std::u32string,
decltype(ints_p.parser_)>);
bp::detail::utf_rvalue_shim<std::u32string, decltype(f_u16str_ref), attr_t>
u32_u16_ref_shim(f_u16str_ref);
#if BOOST_PARSER_USE_CONCEPTS
#if BOOST_PARSER_DETAIL_TEXT_USE_ALIAS_CTAD
static_assert(std::is_same_v<
decltype(u32_u16_ref_shim(attr_t{})),
bp::detail::text::utf_view<
bp::detail::text::format::utf32,
std::ranges::ref_view<std::u16string>> &>);
#endif
#else
static_assert(std::is_same_v<
decltype(u32_u16_ref_shim(attr_t{})),
bp::detail::text::utf32_view<
bp::detail::text::detail::ref_view<std::u16string>> &>);
#endif
static_assert(bp::detail::transform_replacement_for<
decltype(u32_u16_ref_shim),
std::u32string,
decltype(ints_p.parser_)>);
}
TEST(transform_replace, detail_attr_search_repack_shim)
{
using namespace bp::literals;
{
std::string str = "";
auto parser = bp::string("XYZ");
// Follows body of attr_search_impl() that constructs a custom parser
// from the given one.
auto first = bp::detail::text::detail::begin(str);
auto const last = bp::detail::text::detail::end(str);
auto match_first = first;
auto match_last = first;
auto before = [&match_first](auto & ctx) {
match_first = _where(ctx).begin();
};
auto after = [&match_last](auto & ctx) {
match_last = _where(ctx).begin();
};
auto const search_parser =
bp::omit[*(bp::char_ - parser)] >>
-bp::lexeme[bp::eps[before] >> bp::skip[parser] >> bp::eps[after]];
auto result = bp::prefix_parse(
first, last, search_parser, bp::ws, bp::trace::off);
static_assert(std::is_same_v<
decltype(result),
std::optional<std::optional<std::string>>>);
static_assert(std::is_same_v<
decltype(bp::prefix_parse(
first, last, search_parser, bp::ws, bp::trace::off)),
std::optional<std::optional<std::string>>>);
}
{
std::string str = "";
auto parser = bp::string("XYZ");
bp::detail::attr_search_impl(str, parser, bp::ws, bp::trace::off);
}
{
std::string str = "";
auto result = bp::detail::attr_search_repack_shim(
str, bp::string("XYZ"), bp::ws, bp::trace::off);
auto subrng = bp::get(result, 0_c);
EXPECT_EQ(subrng.begin(), std::begin(str));
EXPECT_EQ(subrng.end(), std::begin(str));
auto result_str = bp::get(result, 1_c);
EXPECT_EQ(result_str, "");
}
{
char const str[] = "not here";
auto result = bp::detail::attr_search_repack_shim(
str, bp::string("XYZ"), bp::ws, bp::trace::off);
auto subrng = bp::get(result, 0_c);
EXPECT_EQ(subrng.begin(), std::end(str));
EXPECT_EQ(subrng.end(), std::end(str));
auto result_str = bp::get(result, 1_c);
EXPECT_EQ(result_str, "");
}
{
char const str[] = "aaXYZb";
auto result = bp::detail::attr_search_repack_shim(
str, bp::string("XYZ"), bp::ws, bp::trace::off);
auto subrng = bp::get(result, 0_c);
EXPECT_EQ(subrng.begin(), str + 2);
EXPECT_EQ(subrng.end(), str + 5);
auto result_str = bp::get(result, 1_c);
EXPECT_EQ(result_str, "XYZ");
}
{
char const str[] = "XYZab";
auto result = bp::detail::attr_search_repack_shim(
str, bp::string("XYZ"), bp::ws, bp::trace::off);
auto subrng = bp::get(result, 0_c);
EXPECT_EQ(subrng.begin(), str + 0);
EXPECT_EQ(subrng.end(), str + 3);
auto result_str = bp::get(result, 1_c);
EXPECT_EQ(result_str, "XYZ");
}
{
char const str[] = "gbXYZ";
auto result = bp::detail::attr_search_repack_shim(
str, bp::string("XYZ"), bp::ws, bp::trace::off);
auto subrng = bp::get(result, 0_c);
EXPECT_EQ(subrng.begin(), str + 2);
EXPECT_EQ(subrng.end(), str + 5);
auto result_str = bp::get(result, 1_c);
EXPECT_EQ(result_str, "XYZ");
}
{
char const str[] = "XYZ";
auto result = bp::detail::attr_search_repack_shim(
str, bp::string("XYZ"), bp::ws, bp::trace::off);
auto subrng = bp::get(result, 0_c);
EXPECT_EQ(subrng.begin(), str + 0);
EXPECT_EQ(subrng.end(), str + 3);
auto result_str = bp::get(result, 1_c);
EXPECT_EQ(result_str, "XYZ");
}
{
char const str[] = "XXYZZ";
auto result = bp::detail::attr_search_repack_shim(
str, bp::string("XYZ"), bp::ws, bp::trace::off);
auto subrng = bp::get(result, 0_c);
EXPECT_EQ(subrng.begin(), str + 1);
EXPECT_EQ(subrng.end(), str + 4);
auto result_str = bp::get(result, 1_c);
EXPECT_EQ(result_str, "XYZ");
}
{
char const str[] = "XXYZZ";
auto result = bp::detail::attr_search_repack_shim(
str, bp::string("XYZ"), bp::ws, bp::trace::off);
auto subrng = bp::get(result, 0_c);
EXPECT_EQ(subrng.begin(), str + 1);
EXPECT_EQ(subrng.end(), str + 4);
auto result_str = bp::get(result, 1_c);
EXPECT_EQ(result_str, "XYZ");
}
}
TEST(transform_replace, transform_replace)
{
{
auto r = bp::transform_replace("", bp::int_ % ',', bp::ws, f_str);
int count = 0;
for (auto subrange : r) {
(void)subrange;
++count;
}
EXPECT_EQ(count, 0);
}
{
char const str[] = "ab c 1, 2, 3 d e f";
auto r = bp::transform_replace(str, bp::int_ % ',', bp::ws, f_str);
int count = 0;
std::string replace_result;
for (auto subrange : r) {
std::string str(subrange.begin(), subrange.end());
replace_result += str;
++count;
}
EXPECT_EQ(count, 3);
EXPECT_EQ(replace_result, "ab c 1_2_3_ d e f");
}
{
char const str[] = "ab c 1, 2, 3 d e f";
auto r = bp::transform_replace(str, bp::int_ % ',', bp::ws, f_str_ref);
int count = 0;
std::string replace_result;
for (auto subrange : r) {
std::string str(subrange.begin(), subrange.end());
replace_result += str;
++count;
}
EXPECT_EQ(count, 3);
EXPECT_EQ(replace_result, "ab c 1_2_3_ d e f");
}
{
char const str[] = "a a 1,2,3baa ba1 ,2 , 3";
auto r = str | bp::transform_replace(
bp::int_ % ',', bp::ws, f_str, bp::trace::off);
int count = 0;
std::string replace_result;
for (auto subrange : r) {
std::string str(subrange.begin(), subrange.end());
replace_result += str;
++count;
}
EXPECT_EQ(replace_result, "a a 1_2_3_baa ba1_2_3_");
EXPECT_EQ(count, 4);
}
{
char const str[] = "aa1,2,3baaba1,2,3 4,5,6";
auto r = str | bp::transform_replace(bp::int_ % ',', f_str);
int count = 0;
std::string replace_result;
for (auto subrange : r) {
std::string str(subrange.begin(), subrange.end());
replace_result += str;
++count;
}
EXPECT_EQ(replace_result, "aa1_2_3_baaba1_2_3_ 4_5_6_");
EXPECT_EQ(count, 6);
}
{
char const str[] = "0,0aa1,2,3baaba1,2,3 4,5,6";
auto r = str | bp::transform_replace(bp::int_ % ',', f_str);
int count = 0;
std::string replace_result;
for (auto subrange : r) {
std::string str(subrange.begin(), subrange.end());
replace_result += str;
++count;
}
EXPECT_EQ(replace_result, "0_0_aa1_2_3_baaba1_2_3_ 4_5_6_");
EXPECT_EQ(count, 7);
}
{
char const str[] = "88,88 0,0aa1,2,3baaba1,2,3 4,5,6";
auto r = str | bp::transform_replace(bp::int_ % ',', f_str);
int count = 0;
std::string replace_result;
for (auto subrange : r) {
std::string str(subrange.begin(), subrange.end());
replace_result += str;
++count;
}
EXPECT_EQ(replace_result, "88_88_ 0_0_aa1_2_3_baaba1_2_3_ 4_5_6_");
EXPECT_EQ(count, 9);
}
}
TEST(transform_replace, transform_replace_unicode)
{
{
char const str_[] = "";
auto str = str_ | bp::as_utf8;
auto r = bp::transform_replace(str, bp::int_ % ',', bp::ws, f_u16str);
int count = 0;
for (auto subrange : r) {
(void)subrange;
++count;
}
EXPECT_EQ(count, 0);
}
{
char const * str_ = "aa2,3,4b";
auto str = str_ | bp::as_utf16;
auto r = bp::transform_replace(str, bp::int_ % ',', bp::ws, f_u16str);
int count = 0;
std::string replace_result;
for (auto subrange : r) {
std::string str(subrange.begin(), subrange.end());
replace_result += str;
++count;
}
EXPECT_EQ(replace_result, "aa2_3_4_b");
EXPECT_EQ(count, 3);
}
{
char const str_[] = "a a 3,4,5 baaba7, 8 ,9";
auto str = str_ | bp::as_utf32;
auto r = str | bp::transform_replace(
bp::int_ % ',', bp::ws, f_u32str, bp::trace::off);
int count = 0;
std::string replace_result;
for (auto subrange : r) {
std::string str(subrange.begin(), subrange.end());
replace_result += str;
++count;
}
EXPECT_EQ(replace_result, "a a 3_4_5_ baaba7_8_9_");
EXPECT_EQ(count, 4);
}
{
char const str_[] = "aa88,99baaba111,2222";
auto str = str_ | bp::as_utf8;
const auto r = str | bp::transform_replace(
bp::int_ % ',', f_u16str, bp::trace::off);
int count = 0;
std::string replace_result;
for (auto subrange : r) {
std::string str(subrange.begin(), subrange.end());
replace_result += str;
++count;
}
EXPECT_EQ(replace_result, "aa88_99_baaba111_2222_");
EXPECT_EQ(count, 4);
}
{
char const str_[] = "aa88,99baaba111,2222";
auto str = str_ | bp::as_utf16;
auto r = str | bp::transform_replace(bp::int_ % ',', f_u32str);
int count = 0;
std::string replace_result;
for (auto subrange : r) {
std::string str(subrange.begin(), subrange.end());
replace_result += str;
++count;
}
EXPECT_EQ(replace_result, "aa88_99_baaba111_2222_");
EXPECT_EQ(count, 4);
}
{
char const str_[] = "aa88,99baaba111,2222 3,4";
auto str = str_ | bp::as_utf32;
auto r = str | bp::transform_replace(bp::int_ % ',', f_u16str);
int count = 0;
std::string replace_result;
for (auto subrange : r) {
std::string str(subrange.begin(), subrange.end());
replace_result += str;
++count;
}
EXPECT_EQ(replace_result, "aa88_99_baaba111_2222_ 3_4_");
EXPECT_EQ(count, 6);
}
{
char const str_[] = "1aa88,99baaba111,2222 3,4";
auto str = str_ | bp::as_utf8;
auto r = str | bp::transform_replace(bp::int_ % ',', f_u32str);
int count = 0;
std::string replace_result;
for (auto subrange : r) {
std::string str(subrange.begin(), subrange.end());
replace_result += str;
++count;
}
EXPECT_EQ(replace_result, "1_aa88_99_baaba111_2222_ 3_4_");
EXPECT_EQ(count, 7);
}
}
#if BOOST_PARSER_USE_CONCEPTS && (!defined(__GNUC__) || 12 <= __GNUC__)
// Older GCCs don't like the use of temporaries like the std::string("foo")
// below. This causes | join to break.
TEST(transform_replace, join_compat)
{
{
char const str_[] = "1aa88,99baaba111,2222 3,4";
auto str = str_ | bp::as_utf16;
auto rng = str | bp::transform_replace(bp::int_ % ',', f_u32str) |
std::views::join;
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);
}
EXPECT_EQ(transform_replace_result, "1_aa88_99_baaba111_2222_ 3_4_");
}
{
char const str[] = "1aa88,99baaba111,2222 3,4";
auto rng = str | bp::as_utf32 |
bp::transform_replace(bp::int_ % ',', f_u8str) |
std::views::join;
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);
}
EXPECT_EQ(transform_replace_result, "1_aa88_99_baaba111_2222_ 3_4_");
}
{
char const str[] = "1aa88,99baaba111,2222 3,4";
auto rng = str | bp::transform_replace(bp::int_ % ',', f_str) |
std::views::join;
std::string transform_replace_result;
for (auto ch : rng) {
transform_replace_result.push_back(ch);
}
EXPECT_EQ(transform_replace_result, "1_aa88_99_baaba111_2222_ 3_4_");
}
{
std::string str = "1aa88,99baaba111,2222 3,4";
auto rng = str | bp::transform_replace(bp::int_ % ',', f_str) |
std::views::join;
std::string transform_replace_result;
for (auto ch : rng) {
transform_replace_result.push_back(ch);
}
EXPECT_EQ(transform_replace_result, "1_aa88_99_baaba111_2222_ 3_4_");
}
{
std::string const str = "1aa88,99baaba111,2222 3,4";
auto rng = str | bp::transform_replace(bp::int_ % ',', f_str) |
std::views::join;
std::string transform_replace_result;
for (auto ch : rng) {
transform_replace_result.push_back(ch);
}
EXPECT_EQ(transform_replace_result, "1_aa88_99_baaba111_2222_ 3_4_");
}
{
auto rng = std::string("1aa88,99baaba111,2222 3,4") |
bp::transform_replace(bp::int_ % ',', f_str) |
std::views::join;
std::string transform_replace_result;
for (auto ch : rng) {
transform_replace_result.push_back(ch);
}
EXPECT_EQ(transform_replace_result, "1_aa88_99_baaba111_2222_ 3_4_");
}
}
#endif
TEST(transform_replace, doc_examples)
{
{
auto string_sum = [](std::vector<int> const & ints) {
return std::to_string(std::accumulate(ints.begin(), ints.end(), 0));
};
auto rng = "There are groups of [1, 2, 3, 4, 5] in the set." |
bp::transform_replace(
'[' >> bp::int_ % ',' >> ']', bp::ws, string_sum);
int count = 0;
// Prints "There are groups of 15 in the set".
for (auto subrange : rng) {
for (auto ch : subrange) {
std::cout << ch;
}
++count;
}
std::cout << "\n";
assert(count == 3);
}
}
#endif