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

58 Commits

Author SHA1 Message Date
Zach Laine
390cd1dbe1 parser_config_WIP 2024-03-10 20:09:59 -05:00
Zach Laine
824a208133 Add a permutation parser.
Fixes #159.
2024-03-10 17:31:50 -05:00
Zach Laine
48d5cceb8f Add more tests of seq_parser and or_parser, to cover all the permutations of
different parsers that they might use.

Fixes #126.
2024-03-10 16:48:41 -05:00
Zach Laine
262c19e441 Explain how seq_parser combining logic interacts with directives.
Fixes #161.
2024-03-09 20:54:10 -06:00
Zach Laine
f113d302de !success || !detail::gen_attrs(flags) -> !success in condition to reset
attribute in repeat_parser.  Looks like copy pasta.
2024-03-09 17:41:03 -06:00
Zach Laine
6d253beaac Cruft removal. 2024-03-09 17:41:03 -06:00
Zach Laine
2fa916530e Add missing optional<Container> overloads of detail::append. 2024-03-09 17:41:03 -06:00
Zach Laine
3ff6a575ad Add a TODO. 2024-03-08 21:19:37 -06:00
Zach Laine
a19af1b5ce Expanded the text and examples, plus provided cross-references to more
extended examples, in The Parse Context.

Fixes #145.
2024-03-08 21:19:37 -06:00
Zach Laine
4c0377a5ae Explicitly state that null-terminated string pointers are accepted by
*parse(), in the list of properties common to all the overloads.
2024-03-08 21:19:37 -06:00
Zach Laine
f55d3779de Define BOOST_PARSER_USE_CONCEPTS to be 1 when running Doxygen. This gets
concepts constraints on template parameters to show up.  Also explicitly state
requirements in the Doxygen strings for anything that adds constraints via
requires clauses, since those do not show up in Doxygen.

Fixes #109.
2024-03-08 21:19:37 -06:00
Zach Laine
87b50154cf Fix incorrect attribute returned by a rule in some cases. The problem was
that the changes to support recursive rules made all rules use the out-arg
overload of parse_rule.  This is wrong.  Now, rules use the
attribute-returning overload unless asked explicitly to use the out-arg one,
*or* if the rule is being invoked recursively, and we are in an iteration
after the 0th.

Fixes #125.
2024-03-08 21:19:37 -06:00
Zach Laine
1e011a1fac Add transform(f)[p] attribute-transforming directive.
Fixes #153.
2024-03-08 21:19:37 -06:00
Zach Laine
456a81155a When there are alternate implementations controlled by __has_include, put each
alternate implementation in its own inline namespace.

Fixes #151.
2024-03-08 21:19:37 -06:00
Zach Laine
947fd7782d Explicitly state that char_, cp, and cu can be used without args in their API
docs.

Fixes #150.
2024-03-08 21:19:37 -06:00
Zach Laine
cc9b74cb2d Add no_case support to symbol tabels.
Fixes #149.
2024-03-08 21:19:37 -06:00
Zach Laine
e129193296 Now that the library requires C++17, use std::any instead of any_copyable. 2024-03-08 21:19:37 -06:00
Zach Laine
a07efbbd72 Add note to Memory Allocations that the symbol tables are implemented using a
trie, and that those have lots of allocated nodes.
2024-03-08 21:19:37 -06:00
Zach Laine
9f46597ea9 Add --output-on-failure to make check. 2024-03-08 21:19:37 -06:00
Zach Laine
5dc826c89d Explicitly state in the Semantic Actions page of the tutorial that attaching a
semantic action nullifies the attribute type.

Fixes #148.
2024-03-08 21:19:37 -06:00
Zach Laine
374cad3c35 Get specific about what RESOLVE() means, and link usage of it to the place
where it is defined.

Fixes #144.
2024-03-08 21:19:37 -06:00
Zach Laine
4a9a45f856 Warning mitigation. 2024-03-08 21:19:37 -06:00
Zach Laine
b7f6cd8dbf Add a section about when to use auto-generated attributes vs. using semantic
actions.

Fixes #110.
2024-03-08 21:19:36 -06:00
Zach Laine
aea0ff8dab Removed the very small example of how to use rules with a much longer one
based on a real section of YAML.

Fixes #104.
2024-03-08 21:19:36 -06:00
Zach Laine
9af9b5e373 Use parser::get() instead of operator[] in the implementation of
param_t::operator().
2024-03-08 21:19:36 -06:00
Zach Laine
f05b25ea1d Use detail::print() to print the values in switch alternatives, instead of
printing them directly.
2024-03-08 21:19:36 -06:00
Zach Laine
f57de243a4 Put #ifndef BOOST_PARSER_DOXYGEN around all the structs in parser.hpp that
were forwrard declared in parser_fwd.hpp.  This was causing them to show up
without any documentation string, or default tempalte parameters!
2024-03-08 21:19:36 -06:00
Zach Laine
02c140a251 MSVC warning mitigation.
Fixes #120.
Fixes #121.
Fixes #122.
Fixes #123.
2024-03-08 21:19:36 -06:00
Zach Laine
0e2936e20d Fix broken constraint on 2-param utf_iterator ctor in C++17 mode. 2024-03-08 21:19:36 -06:00
Zach Laine
4235706764 Move DoCallbacks NTTP for all the parsers' call() members to the context as a
simplification.
2024-03-08 21:19:36 -06:00
Zach Laine
0056bd25d1 Add a DoTrace template parameter to the context, and to scoped_trace_t;
specialize scoped_trace_t for DoTrace == false to be empty.

This is an attempt to address #152.
2024-03-08 21:19:36 -06:00
Zach Laine
64e76e7b58 GCC warning mitigation. 2024-03-08 21:19:36 -06:00
Zach Laine
ed9fcc6f3a Don't construct or use a stringstream in every parser when tracing is
disabled(!).
2024-03-08 21:19:36 -06:00
Zach Laine
0a945decb6 Add a small section to the end of the Unidoce Support page indicating that
Parser is normalization-agnostic.
2024-03-08 21:19:36 -06:00
Zach Laine
9b47f3c551 Work around what looks like a Clang concept bug, in which two identical
declarations are parsed to be non-identical.

Fixes #130.
2024-03-08 21:19:36 -06:00
Zach Laine
51d9b81927 Add USE_CONCEPTS guard around unguarded concept.
Fixes #129.
2024-03-08 21:19:36 -06:00
Zach Laine
3e047101f6 Doc copy editing.
Fixes #135.
Fixes #136.
Fixes #137.
Fixes #138.
Fixes #139.
Fixes #142.
2024-03-08 21:19:36 -06:00
Zach Laine
7ae0f9f817 Add missing #include <algorithm> to error_handling.hpp. 2024-03-08 21:19:36 -06:00
Zach Laine
40b495ae5d Add a note about which header includes the as_utfN adaptors to "Explicit
transcoding".

Fixes #119.
2024-03-08 21:19:36 -06:00
Zach Laine
e3c46c10c2 Use std::from_chars, if available; otherwise, use boost::charconv::from_chars,
if available; otherwise, use the Spirit X3 number parsers.

Fixes #113.
2024-03-08 21:19:36 -06:00
Zach Laine
42f66d9079 Use some of the examples and wording from the part of the intro where I talk
about X3 rules in the More About Rules section.

Fixes #117.
2024-03-08 21:19:36 -06:00
Zach Laine
9ebc984ff8 Add a type trait, "attribute", and associated alias attribute_t, that provide
the attribute type for a parser Parser used to parse range R.

Fixes #111.
2024-03-08 21:19:36 -06:00
Zach Laine
ed6e1b4a2c Create a Cheat Sheet page in the docs, right before the tutorial. This
contains all the tables from elsewhere in the docs, gathered in one spot.

Fixes #108.
2024-03-08 21:19:36 -06:00
Zach Laine
125ddf43c2 Change the way action_parser calls invocables, to make it easier to write
those invocables.

Fixes #106.
2024-03-08 21:19:36 -06:00
Zach Laine
a2b7afc3a0 Grooming. 2024-03-08 21:19:36 -06:00
Zach Laine
112290f63e Cruft removal. 2024-03-08 21:19:36 -06:00
Zach Laine
27506af2f3 Constrain parse() overloads that take an out-attribute Attr & -- but no
skipper -- so that Attr is not derived from a specialization of
parser_interface.  This prevents this from being the best overload when using
a mutable skipper, and producing lots of diagnostics that say the skipper
(which would be mistakenly bound to Attr &) is not assignable from whatever
the attribute type is.

Fixes #105.
2024-03-08 21:19:36 -06:00
Zach Laine
db79d00848 Remove the empty concepts.hpp file from the doc build, due to the way that
Doxygen+Quickbook seems not to handle concepts.

Fixes #102.
2024-03-08 21:19:36 -06:00
Zach Laine
eda6238180 Be more explicit in the reference nad tutorial descriptions of
BOOST_PARSER_DEFINE_RULES about what exactly it does and how to use it.

Fixes #101.
2024-03-08 21:19:36 -06:00
Zach Laine
2697eebffd Remove crufty claim in the reference docs for BOOST_PARSER_DEFINE_RULES that
it is only conditionally available.

Fixes #100.
2024-03-08 21:19:29 -06:00
Zach Laine
edff0b29da Fix broken Quickbook link to "Expectation points" section.
Fixes #112.
2024-03-08 21:19:29 -06:00
Zach Laine
f3b5bcdd97 Expand explanation in Intro about why Parser is preferable to X3.
Fixes #114.
2024-03-08 21:19:29 -06:00
Zach Laine
27400587d5 Add Phil Endecott's example latlong parser from the Boost mailing list review
as a test.
2024-03-08 21:19:29 -06:00
Zach Laine
7a9126432a Fix an error in or_parser, which was clearing out-arg attributes when an
alternative failed.
2024-03-08 21:19:29 -06:00
Zach Laine
ca1ad064ff Use a more compelling example in the example code for replace().
Fixes #96.
2024-03-01 22:25:47 -06:00
Zach Laine
0ea1516b6e Call out the constraint on the ReplacementV template parameter to replace().
Partially addresses #97.
2024-03-01 22:25:47 -06:00
Zach Laine
4ff205d6ec Remove superfluous utf_pointer concept.
Partially addresses #97.
2024-03-01 22:25:47 -06:00
Zach Laine
a132e31b66 Add boost_review_changes branch for all Gihub action workflows. 2024-03-01 22:25:47 -06:00
51 changed files with 6943 additions and 2336 deletions

View File

@@ -2,9 +2,9 @@ name: macos-12 - Clang 13
on:
push:
branches: [ master, develop ]
branches: [ master, develop, boost_review_changes ]
pull_request:
branches: [ master, develop ]
branches: [ master, develop, boost_review_changes ]
env:
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)

View File

@@ -2,9 +2,9 @@ name: Ubuntu-20.04 - GCC 9,10
on:
push:
branches: [ master, develop ]
branches: [ master, develop, boost_review_changes ]
pull_request:
branches: [ master, develop ]
branches: [ master, develop, boost_review_changes ]
env:
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)

View File

@@ -2,9 +2,9 @@ name: Ubuntu-22.04 - GCC 10,11
on:
push:
branches: [ master, develop ]
branches: [ master, develop, boost_review_changes ]
pull_request:
branches: [ master, develop ]
branches: [ master, develop, boost_review_changes ]
env:
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)

View File

@@ -2,9 +2,9 @@ name: windows-2019 - Visual Studio 2019
on:
push:
branches: [ master, develop ]
branches: [ master, develop, boost_review_changes ]
pull_request:
branches: [ master, develop ]
branches: [ master, develop, boost_review_changes ]
env:
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)

View File

@@ -2,9 +2,9 @@ name: windows-2022 - Visual Studio 2022
on:
push:
branches: [ master, develop ]
branches: [ master, develop, boost_review_changes ]
pull_request:
branches: [ master, develop ]
branches: [ master, develop, boost_review_changes ]
env:
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)

View File

@@ -12,6 +12,8 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL Clang)
add_definitions(-g -Wall)
elseif (CMAKE_CXX_COMPILER_ID STREQUAL GNU)
add_definitions(-g -Wall)
elseif (CMAKE_CXX_COMPILER_ID STREQUAL MSVC)
add_definitions(/W3)
endif ()
##################################################

View File

@@ -24,6 +24,7 @@ doxygen parser_reference
# note that there is no detail::unspecified -- this is a hack to get all
# the SFINAE code out of the API docs.
<doxygen:param>"PREDEFINED=\"BOOST_PARSER_DOXYGEN=1\" \\
\"BOOST_PARSER_USE_CONCEPTS=1\" \\
\"enable_if=detail::unspecified\""
<doxygen:param>HIDE_UNDOC_MEMBERS=NO
<doxygen:param>EXTRACT_PRIVATE=NO
@@ -37,7 +38,7 @@ doxygen parser_reference
}
run_doxygen [ glob ../include/boost/parser/*.hpp ] : "Reference" ;
run_doxygen [ glob ../include/boost/parser/*.hpp : ../include/boost/parser/concepts.hpp ] : "Reference" ;
xml parser
:

View File

@@ -163,7 +163,8 @@ However, it does not suit user needs in some ways.
rule.
* Spirit X3 is missing many of the convenient interfaces to parsers that
Spirit 2 had. For instance, you cannot add parameters to a parser.
Spirit 2 had. For instance, you cannot add parameters to a parser (see
description of _locals_ in _more_about_rules_).
* All versions of Spirit have Unicode support, but it is quite difficult to
get working.
@@ -184,10 +185,104 @@ that have been retained in _Parser_. Both libraries:
* use approximately the same set of directives to influence the parse
(e.g. `lexeme[]`);
* provide loosely-coupled rules that are separately compilable (at least for
Spirit X3); and
* are built around a flexible parse context object that has state added to and
removed from it during the parse (again, comparing to Spirit X3).
[heading The Spirit X3 rule problem]
Some readers have wanted a concrete example of my claim that Spirit X3's rules
do not compose well. Consider this program.
#include <boost/spirit/home/x3.hpp>
#include <iostream>
#include <set>
#include <string>
#include <vector>
namespace x3 = boost::spirit::x3;
using ints_type = x3::rule<class ints, std::vector<int>>;
BOOST_SPIRIT_DECLARE(ints_type);
x3::rule<class ints, std::vector<int>> ints = "ints";
constexpr auto ints_def = x3::int_ % ',';
BOOST_SPIRIT_DEFINE(ints);
#define FIXED_ATTRIBUTE 0
int main()
{
std::string input = "43, 42";
auto first = input.begin();
auto const last = input.end();
#if FIXED_ATTRIBUTE
std::vector<int> result;
#else
std::set<int> result;
#endif
bool success = x3::phrase_parse(first, last, ints, x3::space, result);
if (success) {
// We want this to print "43 42\n".
for (auto x : result) {
std::cout << x << ' ';
}
std::cout << "\n";
}
return 0;
}
Defining `FIXED_ATTRIBUTE` to be `1` leads to a well-formed program that
prints `"42 43\n"` instead of the desired result. The problem here is that if
you feed an attribute out-param to `x3::phrase_parse()`, you get the
loose-match semantics that Spirit X3 and _Parser_ both do. This is a problem,
because the user explicitly asserted that the type of the `ints` rule's
attribute should be `std::vector<int>`. In my opinion, this code should be
ill-formed with `FIXED_ATTRIBUTE == 1`. To make it well-formed again, the
user could use `ints_def` directly, since it does not specify an attribute
type.
When the user explicitly states that a type is some fixed `T`, a library
should not ignore that. As a user of X3, I was bitten by this in such a way
that I considered X3 to be a nonviable option for my uses. I ran into a
problem that resulted from X3's ignoring one or more of my rules' attributes
so that it made the parse produce the wrong result, and I could see no way to
fix it.
When a library provides wider use cases via genericity, we generally consider
this a good thing. If it is too loose in its semantics, we generally say that
it is type-unsafe. Using _rs_ to nail down type flexibility is one way
_Parser_ tries to enable genericity where it is desired, and let the user turn
it off where it is not.
[endsect]
[section Cheat Sheet]
Here are all the tables containing the various _Parser_ parsers, examples,
etc., all in one place. These are repeated elsewhere in different sections of
the tutorial.
[heading The parsers]
[table_parsers_and_their_semantics]
[heading Operators defined on parsers]
[table_combining_operations]
[heading Attribute generation for certain parsers]
[table_attribute_generation]
[heading Attributes for operations on parsers]
[table_attribute_combinations]
[heading More attributes for operations on parsers]
[table_seq_or_attribute_combinations]
[endsect]

View File

@@ -39,6 +39,8 @@
[import ../example/parsing_into_a_class.cpp]
[import ../example/struct_rule.cpp]
[import ../example/user_error_handler.cpp]
[import ../test/parser.cpp]
[import ../test/parser_rule.cpp]
[import ../include/boost/parser/concepts.hpp]
[import ../include/boost/parser/error_handling_fwd.hpp]
@@ -67,6 +69,17 @@
[def _cb_r_ [classref boost::parser::callback_rule `callback_rule`]]
[def _cb_rs_ [classref boost::parser::callback_rules `callback_rule`s]]
[def _skp_p_ [classref boost::parser::skip_parser `skip_parser`]]
[def _xfm_p_ [classref boost::parser::transform_parser `tranform_parser`]]
[def _noc_p_ [classref boost::parser::no_case_parser `no_case_parser`]]
[def _sv_p_ [classref boost::parser::string_view_parser `string_view_parser`]]
[def _raw_p_ [classref boost::parser::raw_parser `raw_parser`]]
[def _omt_p_ [classref boost::parser::omit_parser `omit_parser`]]
[def _rpt_p_ [classref boost::parser::repeat_parser `repeat_parser`]]
[def _lex_p_ [classref boost::parser::lexeme_parser `lexeme_parser`]]
[def _seq_p_ [classref boost::parser::seq_parser `seq_parser`]]
[def _seq_ps_ [classref boost::parser::seq_parser `seq_parser`s]]
[def _bp_tup_ [classref boost::parser::tuple `boost::parser::tuple`]]
[def _bp_get_ [funcref boost::parser::get `boost::parser::get`]]
[def _bh_tup_ `boost::hana::tuple`]
@@ -105,6 +118,9 @@
[def _cbp_ [funcref boost::parser::callback_parse `callback_parse()`]]
[def _cbpp_ [funcref boost::parser::callback_prefix_parse `callback_prefix_parse()`]]
[def _attr_ [classref boost::parser::attribute `attribute`]]
[def _attr_t_ [classref boost::parser::attribute_t `attribute_t`]]
[def _w_glb_ [funcref boost::parser::with_globals `with_globals()`]]
[def _w_eh_ [funcref boost::parser::with_error_handler `with_error_handler()`]]
@@ -182,6 +198,7 @@
[def _skip_ [globalref boost::parser::skip `skip[]`]]
[def _merge_ [globalref boost::parser::merge `merge[]`]]
[def _sep_ [globalref boost::parser::separate `separate[]`]]
[def _transform_ [globalref boost::parser::transform `transform(f)[]`]]
[def _omit_np_ [globalref boost::parser::omit `omit`]]
[def _raw_np_ [globalref boost::parser::raw `raw`]]
@@ -191,6 +208,7 @@
[def _skip_np_ [globalref boost::parser::skip `skip`]]
[def _merge_np_ [globalref boost::parser::merge `merge`]]
[def _sep_np_ [globalref boost::parser::separate `separate`]]
[def _transform_np_ [globalref boost::parser::transform `transform`]]
[def _blank_ [globalref boost::parser::blank `blank`]]
[def _control_ [globalref boost::parser::control `control`]]
@@ -206,10 +224,11 @@
[def _ATTR_np_ ['[^ATTR]]]
[def _p_api_ [link boost_parser__proposed_.tutorial.the__parse____api The `parse()` API]]
[def _parsers_uses_ [link boost_parser__proposed_.tutorial.the_parsers_and_their_uses The Parsers And Their Uses]]
[def _parse_ctx_ [link boost_parser__proposed_.tutorial.the_parse_context The Parse Context]]
[def _rule_parsers_ [link boost_parser__proposed_.tutorial.rule_parsers Rule Parsers]]
[def _parsing_structs_ [link boost_parser__proposed_.tutorial.parsing_into__struct_s_and__class_es Parsing into `struct`s and `class`es]]
[def _expect_pts_ [link boost_parser__proposed_.tutorial.backtracking.html#boost_parser__proposed_.tutorial.backtracking.expectation_points Expectation points]]
[def _expect_pts_ [link boost_parser__proposed_.tutorial.backtracking.expectation_points Expectation points]]
[def _attr_gen_ [link boost_parser__proposed_.tutorial.attribute_generation Attribute Generation]]
[def _directives_ [link boost_parser__proposed_.tutorial.directives Directives]]
[def _eh_debugging_ [link boost_parser__proposed_.tutorial.error_handling_and_debugging Error Handling and Debugging]]
@@ -235,6 +254,7 @@
[def _emdash_ \u2014]
[include tables.qbk]
[include intro.qbk]
[include tutorial.qbk]
[include examples.qbk]

572
doc/tables.qbk Normal file
View File

@@ -0,0 +1,572 @@
[/
/ Distributed under the Boost Software License, Version 1.0. (See accompanying
/ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
/]
[template table_parsers_and_their_semantics
This table lists all the _Parser_ parsers. For the callable parsers, a
separate entry exists for each possible arity of arguments. For a parser `p`,
if there is no entry for `p` without arguments, `p` is a function, and cannot
itself be used as a parser; it must be called. In the table below:
* each entry is a global object usable directly in your parsers, unless
otherwise noted;
* "code point" is used to refer to the elements of the input range, which
assumes that the parse is being done in the Unicode-aware code path (if the
parse is being done in the non-Unicode code path, read "code point" as
"`char`");
* _RES_ is a notional macro that expands to the resolution of parse argument
or evaluation of a parse predicate (see _parsers_uses_);
* "`_RES_np_(pred) == true`" is a shorthand notation for "`_RES_np_(pred)` is
contextually convertible to `bool` and `true`"; likewise for `false`;
* `c` is a character of type `char`, `char8_t`, or `char32_t`;
* `str` is a string literal of type `char const[]`, `char8_t const []`, or
`char32_t const []`;
* `pred` is a parse predicate;
* `arg0`, `arg1`, `arg2`, ... are parse arguments;
* `a` is a semantic action;
* `r` is an object whose type models `parsable_range_like`; and
* `p`, `p1`, `p2`, ... are parsers.
[note The definition of `parsable_range_like` is:
[parsable_range_like_concept]
It is intended to be a range-like thing; a null-terminated sequence of
characters is considered range-like, given that a pointer `T *` to a
null-terminated string is isomorphic with `subrange<T *, _null_sent_>`.]
[note Some of the parsers in this table consume no input. All parsers consume
the input they match unless otherwise stated in the table below.]
[table Parsers and Their Semantics
[[Parser] [Semantics] [Attribute Type] [Notes]]
[[ _e_ ]
[ Matches /epsilon/, the empty string. Always matches, and consumes no input. ]
[ None. ]
[ Matching _e_ an unlimited number of times creates an infinite loop, which is undefined behavior in C++. _Parser_ will assert in debug mode when it encounters `*_e_`, `+_e_`, etc (this applies to unconditional _e_ only). ]]
[[ `_e_(pred)` ]
[ Fails to match the input if `_RES_np_(pred) == false`. Otherwise, the semantics are those of _e_. ]
[ None. ]
[]]
[[ _ws_ ]
[ Matches a single whitespace code point (see note), according to the Unicode White_Space property. ]
[ None. ]
[ For more info, see the [@https://www.unicode.org/Public/UCD/latest/ucd/PropList.txt Unicode properties]. _ws_ may consume one code point or two. It only consumes two code points when it matches `"\r\n"`. ]]
[[ _eol_ ]
[ Matches a single newline (see note), following the "hard" line breaks in the Unicode line breaking algorithm. ]
[ None. ]
[ For more info, see the [@https://unicode.org/reports/tr14 Unicode Line Breaking Algorithm]. _eol_ may consume one code point or two. It only consumes two code points when it matches `"\r\n"`. ]]
[[ _eoi_ ]
[ Matches only at the end of input, and consumes no input. ]
[ None. ]
[]]
[[ _attr_np_`(arg0)` ]
[ Always matches, and consumes no input. Generates the attribute `_RES_np_(arg0)`. ]
[ `decltype(_RES_np_(arg0))`. ]
[ An important use case for `_attr_` is to provide a default attribute value as a trailing alternative. For instance, an *optional* comma-delmited list is: `int_ % ',' | attr(std::vector<int>)`. Without the "`| attr(...)`", at least one `int_` match would be required. ]]
[[ _ch_ ]
[ Matches any single code point. ]
[ The code point type in Unicode parsing, or `char` in non-Unicode parsing. See _attr_gen_. ]
[]]
[[ `_ch_(arg0)` ]
[ Matches exactly the code point `_RES_np_(arg0)`. ]
[ The code point type in Unicode parsing, or `char` in non-Unicode parsing. See _attr_gen_. ]
[]]
[[ `_ch_(arg0, arg1)` ]
[ Matches the next code point `n` in the input, if `_RES_np_(arg0) <= n && n <= _RES_np_(arg1)`. ]
[ The code point type in Unicode parsing, or `char` in non-Unicode parsing. See _attr_gen_. ]
[]]
[[ `_ch_(r)` ]
[ Matches the next code point `n` in the input, if `n` is one of the code points in `r`. ]
[ The code point type in Unicode parsing, or `char` in non-Unicode parsing. See _attr_gen_. ]
[ `r` is taken to be in a UTF encoding. The exact UTF used depends on `r`'s element type. If you do not pass UTF encoded ranges for `r`, the behavior of _ch_ is undefined. Note that ASCII is a subset of UTF-8, so ASCII is fine. EBCDIC is not. `r` is not copied; a reference to it is taken. The lifetime of `_ch_(r)` must be within the lifetime of `r`. This overload of _ch_ does *not* take parse arguments. ]]
[[ _cp_ ]
[ Matches a single code point. ]
[ `char32_t` ]
[ Similar to _ch_, but with a fixed `char32_t` attribute type; _cp_ has all the same call operator overloads as _ch_, though they are not repeated here, for brevity. ]]
[[ _cu_ ]
[ Matches a single code point. ]
[ `char` ]
[ Similar to _ch_, but with a fixed `char` attribute type; _cu_ has all the same call operator overloads as _ch_, though they are not repeated here, for brevity. Even though the name "`cu`" suggests that this parser match at the code unit level, it does not. The name refers to the attribute type generated, much like the names _i_ versus _ui_. ]]
[[ `_blank_` ]
[ Equivalent to `_ws_ - _eol_`. ]
[ The code point type in Unicode parsing, or `char` in non-Unicode parsing. See the entry for _ch_. ]
[]]
[[ `_control_` ]
[ Matches a single control-character code point. ]
[ The code point type in Unicode parsing, or `char` in non-Unicode parsing. See the entry for _ch_. ]
[]]
[[ `_digit_` ]
[ Matches a single decimal digit code point. ]
[ The code point type in Unicode parsing, or `char` in non-Unicode parsing. See the entry for _ch_. ]
[]]
[[ `_punct_` ]
[ Matches a single punctuation code point. ]
[ The code point type in Unicode parsing, or `char` in non-Unicode parsing. See the entry for _ch_. ]
[]]
[[ `_hex_digit_` ]
[ Matches a single hexidecimal digit code point. ]
[ The code point type in Unicode parsing, or `char` in non-Unicode parsing. See the entry for _ch_. ]
[]]
[[ `_lower_` ]
[ Matches a single lower-case code point. ]
[ The code point type in Unicode parsing, or `char` in non-Unicode parsing. See the entry for _ch_. ]
[]]
[[ `_upper_` ]
[ Matches a single upper-case code point. ]
[ The code point type in Unicode parsing, or `char` in non-Unicode parsing. See the entry for _ch_. ]
[]]
[[ _lit_np_`(c)`]
[ Matches exactly the given code point `c`. ]
[ None. ]
[_lit_ does *not* take parse arguments. ]]
[[ `c_l` ]
[ Matches exactly the given code point `c`. ]
[ None. ]
[ This is a _udl_ that represents `_lit_np_(c)`, for example `'F'_l`. ]]
[[ _lit_np_`(r)`]
[ Matches exactly the given string `r`. ]
[ None. ]
[ _lit_ does *not* take parse arguments. ]]
[[ `str_l` ]
[ Matches exactly the given string `str`. ]
[ None. ]
[ This is a _udl_ that represents `_lit_np_(s)`, for example `"a string"_l`. ]]
[[ `_str_np_(r)`]
[ Matches exactly `r`, and generates the match as an attribute. ]
[ _std_str_ ]
[ _str_ does *not* take parse arguments. ]]
[[ `str_p`]
[ Matches exactly `str`, and generates the match as an attribute. ]
[ _std_str_ ]
[ This is a _udl_ that represents `_str_np_(s)`, for example `"a string"_p`. ]]
[[ _b_ ]
[ Matches `"true"` or `"false"`. ]
[ `bool` ]
[]]
[[ _bin_ ]
[ Matches a binary unsigned integral value. ]
[ `unsigned int` ]
[ For example, _bin_ would match `"101"`, and generate an attribute of `5u`. ]]
[[ `_bin_(arg0)` ]
[ Matches exactly the binary unsigned integral value `_RES_np_(arg0)`. ]
[ `unsigned int` ]
[]]
[[ _oct_ ]
[ Matches an octal unsigned integral value. ]
[ `unsigned int` ]
[ For example, _oct_ would match `"31"`, and generate an attribute of `25u`. ]]
[[ `_oct_(arg0)` ]
[ Matches exactly the octal unsigned integral value `_RES_np_(arg0)`. ]
[ `unsigned int` ]
[]]
[[ _hex_ ]
[ Matches a hexadecimal unsigned integral value. ]
[ `unsigned int` ]
[ For example, _hex_ would match `"ff"`, and generate an attribute of `255u`. ]]
[[ `_hex_(arg0)` ]
[ Matches exactly the hexadecimal unsigned integral value `_RES_np_(arg0)`. ]
[ `unsigned int` ]
[]]
[[ _us_ ]
[ Matches an unsigned integral value. ]
[ `unsigned short` ]
[]]
[[ `_us_(arg0)` ]
[ Matches exactly the unsigned integral value `_RES_np_(arg0)`. ]
[ `unsigned short` ]
[]]
[[ _ui_ ]
[ Matches an unsigned integral value. ]
[ `unsigned int` ]
[]]
[[ `_ui_(arg0)` ]
[ Matches exactly the unsigned integral value `_RES_np_(arg0)`. ]
[ `unsigned int` ]
[]]
[[ _ul_ ]
[ Matches an unsigned integral value. ]
[ `unsigned long` ]
[]]
[[ `_ul_(arg0)` ]
[ Matches exactly the unsigned integral value `_RES_np_(arg0)`. ]
[ `unsigned long` ]
[]]
[[ _ull_ ]
[ Matches an unsigned integral value. ]
[ `unsigned long long` ]
[]]
[[ `_ull_(arg0)` ]
[ Matches exactly the unsigned integral value `_RES_np_(arg0)`. ]
[ `unsigned long long` ]
[]]
[[ _s_ ]
[ Matches a signed integral value. ]
[ `short` ]
[]]
[[ `_s_(arg0)` ]
[ Matches exactly the signed integral value `_RES_np_(arg0)`. ]
[ `short` ]
[]]
[[ _i_ ]
[ Matches a signed integral value. ]
[ `int` ]
[]]
[[ `_i_(arg0)` ]
[ Matches exactly the signed integral value `_RES_np_(arg0)`. ]
[ `int` ]
[]]
[[ _l_ ]
[ Matches a signed integral value. ]
[ `long` ]
[]]
[[ `_l_(arg0)` ]
[ Matches exactly the signed integral value `_RES_np_(arg0)`. ]
[ `long` ]
[]]
[[ _ll_ ]
[ Matches a signed integral value. ]
[ `long long` ]
[]]
[[ `_ll_(arg0)` ]
[ Matches exactly the signed integral value `_RES_np_(arg0)`. ]
[ `long long` ]
[]]
[[ _f_ ]
[ Matches a floating-point number. _f_ uses parsing implementation details from _Spirit_. The specifics of what formats are accepted can be found in their _spirit_reals_. Note that only the default `RealPolicies` is supported by _f_. ]
[ `float` ]
[]]
[[ _d_ ]
[ Matches a floating-point number. _d_ uses parsing implementation details from _Spirit_. The specifics of what formats are accepted can be found in their _spirit_reals_. Note that only the default `RealPolicies` is supported by _d_. ]
[ `double` ]
[]]
[[ `_rpt_np_(arg0)[p]` ]
[ Matches iff `p` matches exactly `_RES_np_(arg0)` times. ]
[ `std::string` if `_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p)>` ]
[ The special value _inf_ may be used; it indicates unlimited repetition. `decltype(_RES_np_(arg0))` must be implicitly convertible to `int64_t`. Matching _e_ an unlimited number of times creates an infinite loop, which is undefined behavior in C++. _Parser_ will assert in debug mode when it encounters `_rpt_np_(_inf_)[_e_]` (this applies to unconditional _e_ only). ]]
[[ `_rpt_np_(arg0, arg1)[p]` ]
[ Matches iff `p` matches between `_RES_np_(arg0)` and `_RES_np_(arg1)` times, inclusively. ]
[ `std::string` if `_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p)>` ]
[ The special value _inf_ may be used for the upper bound; it indicates unlimited repetition. `decltype(_RES_np_(arg0))` and `decltype(_RES_np_(arg1))` each must be implicitly convertible to `int64_t`. Matching _e_ an unlimited number of times creates an infinite loop, which is undefined behavior in C++. _Parser_ will assert in debug mode when it encounters `_rpt_np_(n, _inf_)[_e_]` (this applies to unconditional _e_ only). ]]
[[ `_if_np_(pred)[p]` ]
[ Equivalent to `_e_(pred) >> p`. ]
[ `std::optional<_ATTR_np_(p)>` ]
[ It is an error to write `_if_np_(pred)`. That is, it is an error to omit the conditionally matched parser `p`. ]]
[[ `_sw_np_(arg0)(arg1, p1)(arg2, p2) ...` ]
[ Equivalent to `p1` when `_RES_np_(arg0) == _RES_np_(arg1)`, `p2` when `_RES_np_(arg0) == _RES_np_(arg2)`, etc. If there is such no `argN`, the behavior of _sw_ is undefined. ]
[ `std::variant<_ATTR_np_(p1), _ATTR_np_(p2), ...>` ]
[ It is an error to write `_sw_np_(arg0)`. That is, it is an error to omit the conditionally matched parsers `p1`, `p2`, .... ]]
[[ _symbols_t_ ]
[ _symbols_ is an associative container of key, value pairs. Each key is a _std_str_ and each value has type `T`. In the Unicode parsing path, the strings are considered to be UTF-8 encoded; in the non-Unicode path, no encoding is assumed. _symbols_ Matches the longest prefix `pre` of the input that is equal to one of the keys `k`. If the length `len` of `pre` is zero, and there is no zero-length key, it does not match the input. If `len` is positive, the generated attribute is the value associated with `k`.]
[ `T` ]
[ Unlike the other entries in this table, _symbols_ is a type, not an object. ]]
]
[important All the character parsers, like _ch_, _cp_ and _cu_ produce either
`char` or `char32_t` attributes. So when you see "`std::string` if
`_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p)>`"
in the table above, that effectively means that every sequences of character
attributes get turned into a `std::string`. The only time this does not
happen is when you introduce your own rules with attributes using another
character type (or use _attr_ to do so).]
]
[template table_combining_operations
Here are all the operator overloaded for parsers. In the tables below:
* `c` is a character of type `char` or `char32_t`;
* `a` is a semantic action;
* `r` is an object whose type models `parsable_range_like` (see _concepts_);
and
* `p`, `p1`, `p2`, ... are parsers.
[note Some of the expressions in this table consume no input. All parsers
consume the input they match unless otherwise stated in the table below.]
[table Combining Operations and Their Semantics
[[Expression] [Semantics] [Attribute Type] [Notes]]
[[`!p`] [ Matches iff `p` does not match; consumes no input. ] [None.] []]
[[`&p`] [ Matches iff `p` matches; consumes no input. ] [None.] []]
[[`*p`] [ Parses using `p` repeatedly until `p` no longer matches; always matches. ] [`std::string` if `_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p)>`] [ Matching _e_ an unlimited number of times creates an infinite loop, which is undefined behavior in C++. _Parser_ will assert in debug mode when it encounters `*_e_` (this applies to unconditional _e_ only). ]]
[[`+p`] [ Parses using `p` repeatedly until `p` no longer matches; matches iff `p` matches at least once. ] [`std::string` if `_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p)>`] [ Matching _e_ an unlimited number of times creates an infinite loop, which is undefined behavior in C++. _Parser_ will assert in debug mode when it encounters `+_e_` (this applies to unconditional _e_ only). ]]
[[`-p`] [ Equivalent to `p | _e_`. ] [`std::optional<_ATTR_np_(p)>`] []]
[[`p1 >> p2`] [ Matches iff `p1` matches and then `p2` matches. ] [`_bp_tup_<_ATTR_np_(p1), _ATTR_np_(p2)>` (See note.)] [ `>>` is associative; `p1 >> p2 >> p3`, `(p1 >> p2) >> p3`, and `p1 >> (p2 >> p3)` are all equivalent. This attribute type only applies to the case where `p1` and `p2` both generate attributes; see _attr_gen_ for the full rules. ]]
[[`p >> c`] [ Equivalent to `p >> lit(c)`. ] [`_ATTR_np_(p)`] []]
[[`p >> r`] [ Equivalent to `p >> lit(r)`. ] [`_ATTR_np_(p)`] []]
[[`p1 > p2`] [ Matches iff `p1` matches and then `p2` matches. No back-tracking is allowed after `p1` matches; if `p1` matches but then `p2` does not, the top-level parse fails. ] [`_bp_tup_<_ATTR_np_(p1), _ATTR_np_(p2)>` (See note.)] [ `>` is associative; `p1 > p2 > p3`, `(p1 > p2) > p3`, and `p1 > (p2 > p3)` are all equivalent. This attribute type only applies to the case where `p1` and `p2` both generate attributes; see _attr_gen_ for the full rules. ]]
[[`p > c`] [ Equivalent to `p > lit(c)`. ] [`_ATTR_np_(p)`] []]
[[`p > r`] [ Equivalent to `p > lit(r)`. ] [`_ATTR_np_(p)`] []]
[[`p1 | p2`] [ Matches iff either `p1` matches or `p2` matches. ] [`std::variant<_ATTR_np_(p1), _ATTR_np_(p2)>` (See note.)] [ `|` is associative; `p1 | p2 | p3`, `(p1 | p2) | p3`, and `p1 | (p2 | p3)` are all equivalent. This attribute type only applies to the case where `p1` and `p2` both generate attributes, and where the attribute types are different; see _attr_gen_ for the full rules. ]]
[[`p | c`] [ Equivalent to `p | lit(c)`. ] [`_ATTR_np_(p)`] []]
[[`p | r`] [ Equivalent to `p | lit(r)`. ] [`_ATTR_np_(p)`] []]
[[`p1 || p2`] [ Matches iff `p1` matches and `p2` matches, regardless of the order they match in. ] [`_bp_tup_<_ATTR_np_(p1), _ATTR_np_(p2)>`] [ `||` is associative; `p1 || p2 || p3`, `(p1 || p2) || p3`, and `p1 || (p2 || p3)` are all equivalent. It is an error to include a _e_ (conditional or non-conditional) in an `operator||` expression. Though the parsers are matched in any order, the attribute elements are always in the order written in the `operator||` expression. ]]
[[`p1 - p2`] [ Equivalent to `!p2 >> p1`. ] [`_ATTR_np_(p1)`] []]
[[`p - c`] [ Equivalent to `p - lit(c)`. ] [`_ATTR_np_(p)`] []]
[[`p - r`] [ Equivalent to `p - lit(r)`. ] [`_ATTR_np_(p)`] []]
[[`p1 % p2`] [ Equivalent to `p1 >> *(p2 >> p1)`. ] [`std::string` if `_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p1)>`] []]
[[`p % c`] [ Equivalent to `p % lit(c)`. ] [`std::string` if `_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p)>`] []]
[[`p % r`] [ Equivalent to `p % lit(r)`. ] [`std::string` if `_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p)>`] []]
[[`p[a]`] [ Matches iff `p` matches. If `p` matches, the semantic action `a` is executed. ] [None.] []]
]
[important All the character parsers, like _ch_, _cp_ and _cu_ produce either
`char` or `char32_t` attributes. So when you see "`std::string` if
`_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p)>`"
in the table above, that effectively means that every sequences of character
attributes get turned into a `std::string`. The only time this does not
happen is when you introduce your own rules with attributes using another
character type (or use _attr_ to do so).]
There are a couple of special rules not captured in the table above:
First, the zero-or-more and one-or-more repetitions (`operator*()` and
`operator+()`, respectively) may collapse when combined. For any parser `p`,
`+(+p)` collapses to `+p`; `**p`, `*+p`, and `+*p` each collapse to just `*p`.
Second, using _e_ in an alternative parser as any alternative *except* the
last one is a common source of errors; _Parser_ disallows it. This is true
because, for any parser `p`, `_e_ | p` is equivalent to _e_, since _e_ always
matches. This is not true for _e_ parameterized with a condition. For any
condition `cond`, `_e_(cond)` is allowed to appear anywhere within an
alternative parser.
]
[template table_attribute_generation
This table summarizes the attributes generated for all _Parser_ parsers. In
the table below:
* _RES_ is a notional macro that expands to the resolution of parse argument
or evaluation of a parse predicate (see _parsers_uses_); and
* `x` and `y` represent arbitrary objects.
[table Parsers and Their Attributes
[[Parser] [Attribute Type] [Notes]]
[[ _e_ ] [ None. ] []]
[[ _eol_ ] [ None. ] []]
[[ _eoi_ ] [ None. ] []]
[[ `_attr_np_(x)` ] [ `decltype(_RES_np_(x))` ][]]
[[ _ch_ ] [ The code point type in Unicode parsing, or `char` in non-Unicode parsing; see below. ]
[Includes all the `_p` _udls_ that take a single character, and all character class parsers like `control` and `lower`.]]
[[ _cp_ ] [ `char32_t` ] []]
[[ _cu_ ] [ `char` ] []]
[[ `_lit_np_(x)`] [ None. ]
[Includes all the `_l` _udls_.]]
[[ `_str_np_(x)`] [ _std_str_ ]
[Includes all the `_p` _udls_ that take a string.]]
[[ _b_ ] [ `bool` ] []]
[[ _bin_ ] [ `unsigned int` ] []]
[[ _oct_ ] [ `unsigned int` ] []]
[[ _hex_ ] [ `unsigned int` ] []]
[[ _us_ ] [ `unsigned short` ] []]
[[ _ui_ ] [ `unsigned int` ] []]
[[ _ul_ ] [ `unsigned long` ] []]
[[ _ull_ ] [ `unsigned long long` ] []]
[[ _s_ ] [ `short` ] []]
[[ _i_ ] [ `int` ] []]
[[ _l_ ] [ `long` ] []]
[[ _ll_ ] [ `long long` ] []]
[[ _f_ ] [ `float` ] []]
[[ _d_ ] [ `double` ] []]
[[ _symbols_t_ ] [ `T` ] []]
]
_ch_ is a bit odd, since its attribute type is polymorphic. When you use _ch_
to parse text in the non-Unicode code path (i.e. a string of `char`), the
attribute is `char`. When you use the exact same _ch_ to parse in the
Unicode-aware code path, all matching is code point based, and so the
attribute type is the type used to represent code points, `char32_t`. All
parsing of UTF-8 falls under this case.
Here, we're parsing plain `char`s, meaning that the parsing is in the
non-Unicode code path, the attribute of _ch_ is `char`:
auto result = parse("some text", boost::parser::char_);
static_assert(std::is_same_v<decltype(result), std::optional<char>>));
When you parse UTF-8, the matching is done on a code point basis, so the
attribute type is `char32_t`:
auto result = parse("some text" | boost::parser::as_utf8, boost::parser::char_);
static_assert(std::is_same_v<decltype(result), std::optional<char32_t>>));
The good news is that usually you don't parse characters individually. When
you parse with _ch_, you usually parse repetition of then, which will produce
a _std_str_, regardless of whether you're in Unicode parsing mode or not. If
you do need to parse individual characters, and want to lock down their
attribute type, you can use _cp_ and/or _cu_ to enforce a non-polymorphic
attribute type.
]
[template table_attribute_combinations
Combining operations of course affect the generation of attributes. In the
tables below:
* `m` and `n` are parse arguments that resolve to integral values;
* `pred` is a parse predicate;
* `arg0`, `arg1`, `arg2`, ... are parse arguments;
* `a` is a semantic action; and
* `p`, `p1`, `p2`, ... are parsers that generate attributes.
[table Combining Operations and Their Attributes
[[Parser] [Attribute Type]]
[[`!p`] [None.]]
[[`&p`] [None.]]
[[`*p`] [`std::string` if `_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p)>`]]
[[`+p`] [`std::string` if `_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p)>`]]
[[`+*p`] [`std::string` if `_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p)>`]]
[[`*+p`] [`std::string` if `_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p)>`]]
[[`-p`] [`std::optional<_ATTR_np_(p)>`]]
[[`p1 >> p2`] [`_bp_tup_<_ATTR_np_(p1), _ATTR_np_(p2)>`]]
[[`p1 > p2`] [`_bp_tup_<_ATTR_np_(p1), _ATTR_np_(p2)>`]]
[[`p1 >> p2 >> p3`] [`_bp_tup_<_ATTR_np_(p1), _ATTR_np_(p2), _ATTR_np_(p3)>`]]
[[`p1 > p2 >> p3`] [`_bp_tup_<_ATTR_np_(p1), _ATTR_np_(p2), _ATTR_np_(p3)>`]]
[[`p1 >> p2 > p3`] [`_bp_tup_<_ATTR_np_(p1), _ATTR_np_(p2), _ATTR_np_(p3)>`]]
[[`p1 > p2 > p3`] [`_bp_tup_<_ATTR_np_(p1), _ATTR_np_(p2), _ATTR_np_(p3)>`]]
[[`p1 | p2`] [`std::variant<_ATTR_np_(p1), _ATTR_np_(p2)>`]]
[[`p1 | p2 | p3`] [`std::variant<_ATTR_np_(p1), _ATTR_np_(p2), _ATTR_np_(p3)>`]]
[[`p1 || p2`] [`_bp_tup_<_ATTR_np_(p1), _ATTR_np_(p2)>`]]
[[`p1 || p2 || p3`] [`_bp_tup_<_ATTR_np_(p1), _ATTR_np_(p2), _ATTR_np_(p3)>`]]
[[`p1 % p2`] [`std::string` if `_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p1)>`]]
[[`p[a]`] [None.]]
[[`_rpt_np_(arg0)[p]`] [`std::string` if `_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p)>`]]
[[`_rpt_np_(arg0, arg1)[p]`] [`std::string` if `_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p)>`]]
[[`_if_np_(pred)[p]`] [`std::optional<_ATTR_np_(p)>`]]
[[`_sw_np_(arg0)(arg1, p1)(arg2, p2)...`]
[`std::variant<_ATTR_np_(p1), _ATTR_np_(p2), ...>`]]
]
[important All the character parsers, like _ch_, _cp_ and _cu_ produce either
`char` or `char32_t` attributes. So when you see "`std::string` if
`_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p)>`"
in the table above, that effectively means that every sequences of character
attributes get turned into a `std::string`. The only time this does not
happen is when you introduce your own rules with attributes using another
character type (or use _attr_ to do so).]
[important In case you did not notice it above, adding a semantic action to a
parser erases the parser's attribute. The attribute is still available inside
the semantic action as `_attr(ctx)`.]
]
[template table_seq_or_attribute_combinations
In the table: `a` is a semantic action; and `p`, `p1`, `p2`, ... are parsers
that generate attributes. Note that only `>>` is used here; `>` has the exact
same attribute generation rules.
[table Sequence and Alternative Combining Operations and Their Attributes
[[Expression] [Attribute Type]]
[[`_e_ >> _e_`] [None.]]
[[`p >> _e_`] [`_ATTR_np_(p)`]]
[[`_e_ >> p`] [`_ATTR_np_(p)`]]
[[`_cu_ >> _str_np_("str")`] [_std_str_]]
[[`_str_np_("str") >> _cu_`] [_std_str_]]
[[`*_cu_ >> _str_np_("str")`] [`_bp_tup_<std::string, std::string>`]]
[[`_str_np_("str") >> *_cu_`] [`_bp_tup_<std::string, std::string>`]]
[[`p >> p`] [`_bp_tup_<_ATTR_np_(p), _ATTR_np_(p)>`]]
[[`*p >> p`] [`std::string` if `_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p)>`]]
[[`p >> *p`] [`std::string` if `_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p)>`]]
[[`*p >> -p`] [`std::string` if `_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p)>`]]
[[`-p >> *p`] [`std::string` if `_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p)>`]]
[[`_str_np_("str") >> -_cu_`] [_std_str_]]
[[`-_cu_ >> _str_np_("str")`] [_std_str_]]
[[`!p1 | p2[a]`] [None.]]
[[`p | p`] [`_ATTR_np_(p)`]]
[[`p1 | p2`] [`std::variant<_ATTR_np_(p1), _ATTR_np_(p2)>`]]
[[`p | `_e_] [`std::optional<_ATTR_np_(p)>`]]
[[`p1 | p2 | _e_`] [`std::optional<std::variant<_ATTR_np_(p1), _ATTR_np_(p2)>>`]]
[[`p1 | p2[a] | p3`] [`std::optional<std::variant<_ATTR_np_(p1), _ATTR_np_(p3)>>`]]
]
]

File diff suppressed because it is too large Load Diff

View File

@@ -35,6 +35,7 @@ int main()
auto const result = parse("X 9 X", parser, bp::ws);
assert(result && *result == 9);
//]
(void)result;
//[ self_filling_symbol_table_after_parse
assert(!parse("X", symbols));

View File

@@ -51,7 +51,7 @@ namespace boost { namespace parser {
typename S,
typename ErrorHandler,
typename GlobalState>
using minimal_parse_context = decltype(detail::make_context(
using minimal_parse_context = decltype(detail::make_context<false, false>(
std::declval<I>(),
std::declval<S>(),
std::declval<bool &>(),

View File

@@ -57,11 +57,9 @@ namespace boost::parser::detail {
return out;
} else {
// Skip [0x41, 0x5a), handled above.
auto const first =
detail::text::detail::begin(mapping_ranges) + 1;
auto const first = text::detail::begin(mapping_ranges) + 1;
// 7th entry starts with 0x100.
auto const last =
detail::text::detail::begin(mapping_ranges) + 7;
auto const last = text::detail::begin(mapping_ranges) + 7;
if (auto out_opt = do_short_mapping(first, last, cp, out))
return *out_opt;
}
@@ -71,8 +69,8 @@ namespace boost::parser::detail {
// Single-cp-mapping path (next most common case).
{
auto const first = detail::text::detail::begin(mapping_ranges);
auto const last = detail::text::detail::end(mapping_ranges);
auto const first = text::detail::begin(mapping_ranges);
auto const last = text::detail::end(mapping_ranges);
if (auto out_opt = do_short_mapping(first, last, cp, out))
return *out_opt;
}
@@ -95,8 +93,8 @@ namespace boost::parser::detail {
return std::copy(
it->mapping_,
std::find(
detail::text::detail::begin(it->mapping_),
detail::text::detail::end(it->mapping_),
text::detail::begin(it->mapping_),
text::detail::end(it->mapping_),
0),
out);
#endif
@@ -106,7 +104,6 @@ namespace boost::parser::detail {
*out++ = cp;
return out;
}
}
#endif

View File

@@ -58,6 +58,32 @@ namespace boost { namespace parser { namespace detail::hl {
}
// apply
template<typename F, typename Tuple, std::size_t... I>
constexpr auto
apply_impl(F && f, Tuple && t, std::integer_sequence<std::size_t, I...>)
-> decltype(((F &&) f)(parser::get((Tuple &&) t, llong<I>{})...))
{
return ((F &&) f)(parser::get((Tuple &&) t, llong<I>{})...);
}
template<
typename F,
typename Tuple,
typename Enable = std::enable_if_t<detail::is_tuple<
std::remove_cv_t<std::remove_reference_t<Tuple>>>{}>>
constexpr auto apply(F && f, Tuple && t) -> decltype(hl::apply_impl(
(F &&) f,
(Tuple &&) t,
std::make_integer_sequence<std::size_t, tuple_size_<Tuple>>{}))
{
return hl::apply_impl(
(F &&) f,
(Tuple &&) t,
std::make_integer_sequence<std::size_t, tuple_size_<Tuple>>{});
}
// for_each
template<typename F, typename Tuple, std::size_t... I>

View File

@@ -12,12 +12,34 @@
#ifndef BOOST_PARSER_DETAIL_NUMERIC_HPP
#define BOOST_PARSER_DETAIL_NUMERIC_HPP
#include <boost/parser/detail/text/unpack.hpp>
#include <version>
#if defined(__cpp_lib_to_chars)
#include <charconv>
#define BOOST_PARSER_HAVE_STD_CHARCONV
#define BOOST_PARSER_NUMERIC_NS std_charconv
#elif __has_include(<boost/charconv.hpp>)
#include <boost/charconv.hpp>
#define BOOST_PARSER_HAVE_BOOST_CHARCONV
#define BOOST_PARSER_NUMERIC_NS boost_charconv
#else
#define BOOST_PARSER_NUMERIC_NS spirit_parsers
#endif
#include <type_traits>
#include <cmath>
#if defined(BOOST_PARSER_HAVE_STD_CHARCONV) || \
defined(BOOST_PARSER_HAVE_BOOST_CHARCONV)
#define BOOST_PARSER_HAVE_CHARCONV
#endif
namespace boost { namespace parser { namespace detail_spirit_x3 {
struct unused_type{};
namespace boost::parser::detail_spirit_x3 {
struct unused_type
{};
// Copied from boost/spirit/home/support/char_class.hpp (Boost 1.71), and
// modified not to use Boost.TypeTraits.
@@ -963,7 +985,106 @@ namespace boost { namespace parser { namespace detail_spirit_x3 {
{
static bool const expect_dot = true;
};
}
}}}
namespace boost::parser::detail::numeric {
template<typename I, typename S>
constexpr bool common_range = std::is_same_v<I, S>;
template<typename I, typename S>
using unpacked_iter = decltype(text::unpack_iterator_and_sentinel(
std::declval<I>(), std::declval<S>())
.first);
template<typename I, typename S>
constexpr bool unpacks_to_chars =
std::is_pointer_v<unpacked_iter<I, S>> && std::is_same_v<
std::remove_cv_t<std::remove_reference_t<
std::remove_pointer_t<unpacked_iter<I, S>>>>,
char>;
inline namespace BOOST_PARSER_NUMERIC_NS {
template<int MinDigits, int MaxDigits, typename I, typename S>
#if defined(BOOST_PARSER_HAVE_CHARCONV)
constexpr bool use_charconv_int =
MinDigits == 1 && MaxDigits == -1 &&
common_range<I, S> && unpacks_to_chars<I, S>;
#else
constexpr bool use_charconv_int = false;
#endif
template<
bool Signed,
int Radix,
int MinDigits,
int MaxDigits,
typename I,
typename S,
typename T>
bool parse_int(I & first, S last, T & attr)
{
if constexpr (use_charconv_int<MinDigits, MaxDigits, I, S>) {
#if defined(BOOST_PARSER_HAVE_CHARCONV)
auto unpacked = text::unpack_iterator_and_sentinel(first, last);
#if defined(BOOST_PARSER_HAVE_STD_CHARCONV)
std::from_chars_result const result = std::from_chars(
#else
charconv::from_chars_result const result = charconv::from_chars(
#endif
unpacked.first, unpacked.last, attr, Radix);
if (result.ec == std::errc()) {
first = unpacked.repack(result.ptr);
return true;
}
return false;
#endif
} else if constexpr (Signed) {
using extract = detail_spirit_x3::
extract_int<T, Radix, MinDigits, MaxDigits>;
return extract::call(first, last, attr);
} else {
using extract = detail_spirit_x3::
extract_uint<T, Radix, MinDigits, MaxDigits>;
return extract::call(first, last, attr);
}
}
template<typename I, typename S>
#if defined(BOOST_PARSER_HAVE_CHARCONV)
constexpr bool use_charconv_real =
common_range<I, S> && unpacks_to_chars<I, S>;
#else
constexpr bool use_charconv_real = false;
#endif
template<typename I, typename S, typename T>
bool parse_real(I & first, S last, T & attr)
{
if constexpr (use_charconv_real<I, S>) {
#if defined(BOOST_PARSER_HAVE_CHARCONV)
auto unpacked = text::unpack_iterator_and_sentinel(first, last);
#if defined(BOOST_PARSER_HAVE_STD_CHARCONV)
std::from_chars_result const result = std::from_chars(
#else
charconv::from_chars_result const result = charconv::from_chars(
#endif
unpacked.first, unpacked.last, attr);
if (result.ec == std::errc()) {
first = unpacked.repack(result.ptr);
return true;
}
return false;
#endif
} else {
detail_spirit_x3::real_policies<T> policies;
using extract = detail_spirit_x3::
extract_real<T, detail_spirit_x3::real_policies<T>>;
return extract::parse(first, last, attr, policies);
}
}
}
}
#endif

View File

@@ -71,6 +71,13 @@ namespace boost { namespace parser { namespace detail {
std::ostream & os,
int components = 0);
template<typename Context, typename ParserTuple>
void print_parser(
Context const & context,
perm_parser<ParserTuple> const & parser,
std::ostream & os,
int components = 0);
template<
typename Context,
typename ParserTuple,
@@ -90,6 +97,13 @@ namespace boost { namespace parser { namespace detail {
std::ostream & os,
int components = 0);
template<typename Context, typename Parser, typename F>
void print_parser(
Context const & context,
transform_parser<Parser, F> const & parser,
std::ostream & os,
int components = 0);
template<typename Context, typename Parser>
void print_parser(
Context const & context,
@@ -134,10 +148,10 @@ namespace boost { namespace parser { namespace detail {
std::ostream & os,
int components = 0);
template<typename Context, typename Parser, bool FailOnMatch>
template<typename Context, typename Parser, typename ParserConfig>
void print_parser(
Context const & context,
expect_parser<Parser, FailOnMatch> const & parser,
expect_parser_t<Parser, ParserConfig> const & parser,
std::ostream & os,
int components = 0);
@@ -264,13 +278,6 @@ namespace boost { namespace parser { namespace detail {
std::ostream & os,
int components = 0);
template<typename Context, typename I, typename S>
void print_parser(
Context const & context,
quoted_string_parser<I, S> const & parser,
std::ostream & os,
int components = 0);
template<typename Context, bool NewlinesOnly, bool NoNewlines>
void print_parser(
Context const & context,
@@ -561,6 +568,7 @@ namespace boost { namespace parser { namespace detail {
auto resolve(Context const &, nope n);
template<
bool DoTrace,
typename Iter,
typename Sentinel,
typename Context,
@@ -614,6 +622,16 @@ namespace boost { namespace parser { namespace detail {
std::string name_;
};
template<
typename Iter,
typename Sentinel,
typename Context,
typename Attribute>
struct scoped_trace_t<false, Iter, Sentinel, Context, Attribute>
{
scoped_trace_t() {}
};
template<
typename Parser,
typename Iter,
@@ -628,11 +646,14 @@ namespace boost { namespace parser { namespace detail {
flags f,
Attribute const & attr)
{
std::stringstream oss;
if (detail::do_trace(f))
if constexpr (Context::do_trace) {
std::stringstream oss;
detail::print_parser(context, parser, oss);
return scoped_trace_t<Iter, Sentinel, Context, Attribute>(
first, last, context, f, attr, oss.str());
return scoped_trace_t<true, Iter, Sentinel, Context, Attribute>(
first, last, context, f, attr, oss.str());
} else {
return scoped_trace_t<false, Iter, Sentinel, Context, Attribute>{};
}
}
template<typename Context, typename Attribute>

View File

@@ -10,25 +10,29 @@
#endif
#include <boost/type_index.hpp>
#define BOOST_PARSER_HAVE_BOOST_TYPEINDEX 1
#define BOOST_PARSER_TYPE_NAME_NS boost_type_index
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic pop
#endif
#else
#include <typeinfo>
#define BOOST_PARSER_HAVE_BOOST_TYPEINDEX 0
#define BOOST_PARSER_TYPE_NAME_NS std_typeinfo
#endif
namespace boost { namespace parser { namespace detail {
template<typename T>
auto type_name()
{
inline namespace BOOST_PARSER_TYPE_NAME_NS {
template<typename T>
auto type_name()
{
#if BOOST_PARSER_HAVE_BOOST_TYPEINDEX
return typeindex::type_id<T>().pretty_name();
return typeindex::type_id<T>().pretty_name();
#else
return typeid(T).name();
return typeid(T).name();
#endif
}
}
template<typename Parser>
@@ -59,6 +63,10 @@ namespace boost { namespace parser { namespace detail {
struct n_aray_parser<or_parser<ParserTuple>> : std::true_type
{};
template<typename ParserTuple>
struct n_aray_parser<perm_parser<ParserTuple>> : std::true_type
{};
template<
typename ParserTuple,
typename BacktrackingTuple,
@@ -161,6 +169,32 @@ namespace boost { namespace parser { namespace detail {
os << ")";
}
template<typename Context, typename Parser>
void print_or_like_parser(
Context const & context,
Parser const & parser,
std::ostream & os,
int components,
std::string_view or_ellipsis,
std::string_view ws_or)
{
int i = 0;
bool printed_ellipsis = false;
hl::for_each(parser.parsers_, [&](auto const & parser) {
if (components == parser_component_limit) {
if (!printed_ellipsis)
os << or_ellipsis;
printed_ellipsis = true;
return;
}
if (i)
os << ws_or;
detail::print_parser(context, parser, os, components);
++components;
++i;
});
}
template<typename Context, typename ParserTuple>
void print_parser(
Context const & context,
@@ -168,21 +202,19 @@ namespace boost { namespace parser { namespace detail {
std::ostream & os,
int components)
{
int i = 0;
bool printed_ellipsis = false;
hl::for_each(parser.parsers_, [&](auto const & parser) {
if (components == parser_component_limit) {
if (!printed_ellipsis)
os << " | ...";
printed_ellipsis = true;
return;
}
if (i)
os << " | ";
detail::print_parser(context, parser, os, components);
++components;
++i;
});
detail::print_or_like_parser(
context, parser, os, components, " | ...", " | ");
}
template<typename Context, typename ParserTuple>
void print_parser(
Context const & context,
perm_parser<ParserTuple> const & parser,
std::ostream & os,
int components)
{
detail::print_or_like_parser(
context, parser, os, components, " || ...", " || ");
}
template<
@@ -226,7 +258,7 @@ namespace boost { namespace parser { namespace detail {
detail::print_parser(context, parser, os, components);
++components;
++i;
prev_group = group;
prev_group = (int)group;
});
if (prev_group && !printed_ellipsis)
os << ']';
@@ -259,6 +291,17 @@ namespace boost { namespace parser { namespace detail {
os << "]";
}
template<typename Context, typename Parser, typename F>
void print_parser(
Context const & context,
transform_parser<Parser, F> const & parser,
std::ostream & os,
int components)
{
detail::print_directive(
context, "transform(<<f>>)", parser.parser_, os, components);
}
template<typename Context, typename Parser>
void print_parser(
Context const & context,
@@ -335,14 +378,14 @@ namespace boost { namespace parser { namespace detail {
}
}
template<typename Context, typename Parser, bool FailOnMatch>
template<typename Context, typename Parser, typename ParserConfig>
void print_parser(
Context const & context,
expect_parser<Parser, FailOnMatch> const & parser,
expect_parser_t<Parser, ParserConfig> const & parser,
std::ostream & os,
int components)
{
if (FailOnMatch)
if (ParserConfig::fail_on_match)
os << "!";
else
os << "&";
@@ -439,7 +482,8 @@ namespace boost { namespace parser { namespace detail {
template<
typename Context,
typename ResolvedExpected,
bool Integral = std::is_integral<ResolvedExpected>{}>
bool Integral = std::is_integral<ResolvedExpected>{},
int SizeofExpected = sizeof(ResolvedExpected)>
struct print_expected_char_impl
{
static void call(
@@ -451,17 +495,13 @@ namespace boost { namespace parser { namespace detail {
}
};
template<typename Context>
struct print_expected_char_impl<Context, char32_t, true>
template<typename Context, typename Expected>
struct print_expected_char_impl<Context, Expected, true, 4>
{
static void
call(Context const & context, std::ostream & os, char32_t expected)
call(Context const & context, std::ostream & os, Expected expected)
{
if (expected == '\'') {
os << "'\\''";
return;
}
std::array<char32_t, 1> cps = {{expected}};
std::array<char32_t, 1> cps = {{(char32_t)expected}};
auto const r = cps | text::as_utf8;
os << "'";
for (auto c : r) {
@@ -649,27 +689,6 @@ namespace boost { namespace parser { namespace detail {
os << "\"";
}
template<typename Context, typename I, typename S>
void print_parser(
Context const & context,
quoted_string_parser<I, S> const & parser,
std::ostream & os,
int components)
{
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, bool NewlinesOnly, bool NoNewlines>
void print_parser(
Context const & context,
@@ -849,11 +868,12 @@ namespace boost { namespace parser { namespace detail {
int components)
{
using namespace literals;
os << "("
<< detail::resolve(
context, parser::get(parser.parsers_, 0_c).pred_.value_)
<< ", ";
os << "(";
detail::print(
os,
detail::resolve(
context, parser::get(parser.parsers_, 0_c).pred_.value_));
os << ", ";
detail::print_parser(
context, parser::get(parser.parsers_, 1_c), os, components);
os << ")";

View File

@@ -131,8 +131,7 @@ namespace boost::parser::detail::text::detail {
constexpr all_impl all;
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS && \
(!defined(__GNUC__) || 12 <= __GNUC__)
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS
template<typename R>
using all_t = std::views::all_t<R>;
#else

View File

@@ -383,7 +383,7 @@ namespace boost::parser::detail { namespace text {
boost::parser::detail::text::continuation(*--retval)) {
++backup;
}
backup = std::distance(retval, it);
backup = (int)std::distance(retval, it);
if (boost::parser::detail::text::continuation(*retval)) {
if (it != first)
@@ -396,7 +396,7 @@ namespace boost::parser::detail { namespace text {
++*first_invalid;
while (first_invalid &&
std::distance(retval, *first_invalid) < backup) {
backup -= std::distance(retval, *first_invalid);
backup -= (int)std::distance(retval, *first_invalid);
retval = *first_invalid;
first_invalid = end_of_invalid_utf8(retval);
if (first_invalid == retval)
@@ -2568,6 +2568,42 @@ namespace boost::parser::detail { namespace text {
};
}
namespace detail {
struct iter_access
{
template<typename T>
static auto & buf(T & it)
{
return it.buf_;
}
template<typename T>
static auto & first_and_curr(T & it)
{
return it.first_and_curr_;
}
template<typename T>
static auto & buf_index(T & it)
{
return it.buf_index_;
}
template<typename T>
static auto & buf_last(T & it)
{
return it.buf_last_;
}
template<typename T>
static auto & to_increment(T & it)
{
return it.to_increment_;
}
template<typename T>
static auto & last(T & it)
{
return it.last_;
}
};
}
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS
template<
format FromFormat,
@@ -2641,7 +2677,7 @@ namespace boost::parser::detail { namespace text {
#if !BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS
template<
typename J = I,
typename Enable = std::enable_if_t<is_bidirectional<J>>>
typename Enable = std::enable_if_t<!is_bidirectional<J>>>
#endif
constexpr utf_iterator(I it, S last)
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS
@@ -2669,11 +2705,12 @@ namespace boost::parser::detail { namespace text {
constexpr utf_iterator(
utf_iterator<FromFormat, ToFormat, I2, S2, ErrorHandler> const &
other) :
buf_(other.buf_),
first_and_curr_(other.first_and_curr_),
buf_index_(other.buf_index_),
buf_last_(other.buf_last_),
last_(other.last_)
buf_(detail::iter_access::buf(other)),
first_and_curr_(detail::iter_access::first_and_curr(other)),
buf_index_(detail::iter_access::buf_index(other)),
buf_last_(detail::iter_access::buf_last(other)),
to_increment_(detail::iter_access::to_increment(other)),
last_(detail::iter_access::last(other))
{}
#if !BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS
@@ -3181,7 +3218,7 @@ namespace boost::parser::detail { namespace text {
auto it = encode_code_point(cp, buf_.begin());
buf_last_ = uint8_t(it - buf_.begin());
buf_index_ = buf_last_ - 1;
to_increment_ = std::distance(curr(), initial);
to_increment_ = (int)std::distance(curr(), initial);
} else {
auto buf = buf_;
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS
@@ -3217,33 +3254,17 @@ namespace boost::parser::detail { namespace text {
constexpr I & curr() { return first_and_curr_.curr; }
constexpr I curr() const { return first_and_curr_.curr; }
std::array<value_type, 4 / static_cast<int>(ToFormat)> buf_;
std::array<value_type, 4 / static_cast<int>(ToFormat)> buf_ = {};
detail::first_and_curr<I> first_and_curr_;
detail::first_and_curr<I> first_and_curr_ = {};
uint8_t buf_index_ = 0;
uint8_t buf_last_ = 0;
uint8_t to_increment_ = 0;
[[no_unique_address]] S last_;
[[no_unique_address]] S last_ = {};
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS
template<
format FromFormat2,
format ToFormat2,
std::input_iterator I2,
std::sentinel_for<I2> S2,
transcoding_error_handler ErrorHandler2>
requires std::convertible_to<std::iter_value_t<I2>, detail::format_to_type_t<FromFormat2>>
#else
template<
format FromFormat2,
format ToFormat2,
typename I2,
typename S2,
typename ErrorHandler2>
#endif
friend class utf_iterator;
friend struct detail::iter_access;
};
}}

View File

@@ -44,27 +44,13 @@ namespace boost::parser::detail { namespace text {
using iterator_to_tag_t = decltype(iterator_to_tag<I>());
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS
#if defined(__GNUC__) && __GNUC__ < 12
// This uses the final version of viewable_range. Older GCCs use a
// different one that breaks, so we use this on instead of the one
// from std::ranges::. It's missing the !initializer_list part.
template<typename T>
concept viewable_range = std::ranges::range<T> &&
((std::ranges::view<std::remove_cvref_t<T>> &&
std::constructible_from<std::remove_cvref_t<T>, T>) ||
(!std::ranges::view<std::remove_cvref_t<T>> &&
(std::is_lvalue_reference_v<T> ||
std::movable<std::remove_reference_t<T>>)));
#else
template<typename T>
concept viewable_range = std::ranges::viewable_range<T>;
#endif
template<class T>
using with_reference = T &;
template<typename T>
concept can_reference = requires { typename with_reference<T>; };
#endif
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS
template<class Char>
struct cast_to_charn {
constexpr Char operator()(Char c) const { return c; }
@@ -279,7 +265,7 @@ namespace boost::parser::detail { namespace text {
#if BOOST_PARSER_DETAIL_TEXT_USE_ALIAS_CTAD
template<class R, auto F>
project_view(R &&) -> project_view<detail::all_t<R>, F>;
project_view(R &&) -> project_view<std::views::all_t<R>, F>;
#endif
namespace detail {
@@ -295,7 +281,7 @@ namespace boost::parser::detail { namespace text {
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS
template<class R>
requires viewable_range<R> &&
requires std::ranges::viewable_range<R> &&
std::ranges::input_range<R> &&
std::regular_invocable<decltype(F)&, std::ranges::range_reference_t<R>> &&
detail::can_reference<std::invoke_result_t<decltype(F)&, std::ranges::range_reference_t<R>>>
@@ -342,7 +328,11 @@ namespace boost::parser::detail { namespace text {
#endif
{
public:
constexpr char8_view() requires std::default_initializable<V> = default;
constexpr char8_view()
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS
requires std::default_initializable<V>
#endif
= default;
constexpr char8_view(V base) :
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS
project_view<V, detail::cast_to_charn<char8_t>{}>{std::move(base)}
@@ -416,7 +406,7 @@ namespace boost::parser::detail { namespace text {
{
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS
template<class R>
requires (viewable_range<R> &&
requires (std::ranges::viewable_range<R> &&
std::ranges::input_range<R> &&
std::convertible_to<std::ranges::range_reference_t<R>, format_to_type_t<Format>>) ||
utf_pointer<std::remove_cvref_t<R>>
@@ -620,7 +610,7 @@ namespace boost::parser::detail { namespace text {
#if BOOST_PARSER_DETAIL_TEXT_USE_ALIAS_CTAD
template<format Format, class R>
utf_view(R &&) -> utf_view<Format, detail::all_t<R>>;
utf_view(R &&) -> utf_view<Format, std::views::all_t<R>>;
template<class V>
using utf8_view = utf_view<format::utf8, V>;
@@ -771,7 +761,7 @@ namespace boost::parser::detail { namespace text {
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS
template<class R>
requires is_utf_view<std::remove_cvref_t<R>> ||
(viewable_range<R> &&
(std::ranges::viewable_range<R> &&
can_utf_view<unpacked_range<R>, View>) ||
utf_pointer<std::remove_cvref_t<R>>
#else

File diff suppressed because it is too large Load Diff

View File

@@ -7,6 +7,7 @@
#include <boost/parser/detail/text/algorithm.hpp>
#include <boost/parser/detail/text/transcode_iterator.hpp>
#include <algorithm>
#include <array>
#include <functional>
#include <iostream>

File diff suppressed because it is too large Load Diff

View File

@@ -9,6 +9,7 @@
#include <boost/parser/config.hpp>
#include <boost/parser/error_handling_fwd.hpp>
#include <any>
#include <cstdint>
#include <map>
#include <memory>
@@ -43,6 +44,20 @@ namespace boost { namespace parser {
constexpr bool enable_variant<std::variant<Ts...>> = true;
#endif
/** A type trait that evaluates to the attribute type for parser `Parser`
used to parse range `R`, as if by calling `parse(r, parser)`, using
some `R r` and `Parser parser`. Note that this implies that pointers
to null-terminated strings are supported types for `R`. The result is
not wrapped in a `std::optional` like the result of a call to
`parse()` would be. If `Parser` produces no attribute, the result is
the no-attribute sentinel type `none`. */
template<typename R, typename Parser>
struct attribute;
/** An alias for `typename attribute<R, Parser>::type`. */
template<typename R, typename Parser>
using attribute_t = typename attribute<R, Parser>::type;
namespace detail {
template<typename T>
constexpr bool is_optional_v = enable_optional<T>;
@@ -55,72 +70,17 @@ namespace boost { namespace parser {
trace = 1 << 2,
in_apply_parser = 1 << 3
};
struct any_copyable
{
template<
typename T,
typename Enable = std::enable_if_t<!std::is_reference_v<T>>>
any_copyable(T && v) :
impl_(new holder<std::decay_t<T>>(std::move(v)))
{}
template<typename T>
any_copyable(T const & v) : impl_(new holder<T>(v))
{}
any_copyable() = default;
any_copyable(any_copyable const & other)
{
if (other.impl_)
impl_ = other.impl_->clone();
}
any_copyable & operator=(any_copyable const & other)
{
any_copyable temp(other);
swap(temp);
return *this;
}
any_copyable(any_copyable &&) = default;
any_copyable & operator=(any_copyable &&) = default;
bool empty() const { return impl_.get() == nullptr; }
template<typename T>
T & cast() const
{
BOOST_PARSER_DEBUG_ASSERT(impl_);
BOOST_PARSER_DEBUG_ASSERT(dynamic_cast<holder<T> *>(impl_.get()));
return static_cast<holder<T> *>(impl_.get())->value_;
}
void swap(any_copyable & other) { std::swap(impl_, other.impl_); }
private:
struct holder_base
{
virtual ~holder_base() {}
virtual std::unique_ptr<holder_base> clone() const = 0;
};
template<typename T>
struct holder : holder_base
{
holder(T && v) : value_(std::move(v)) {}
holder(T const & v) : value_(v) {}
virtual ~holder() {}
virtual std::unique_ptr<holder_base> clone() const
{
return std::unique_ptr<holder_base>(new holder<T>{value_});
}
T value_;
};
std::unique_ptr<holder_base> impl_;
};
constexpr inline flags disable_attrs(flags f);
using symbol_table_tries_t =
std::map<void *, any_copyable, std::less<void *>>;
std::map<void *, std::pair<std::any, bool>, std::less<void *>>;
template<typename Iter, typename Sentinel, typename ErrorHandler>
template<
bool DoTrace,
bool UseCallbacks,
typename Iter,
typename Sentinel,
typename ErrorHandler>
inline auto make_context(
Iter first,
Sentinel last,
@@ -157,6 +117,25 @@ namespace boost { namespace parser {
{};
struct upper_case_chars
{};
template<
bool OmitAttr = false,
bool DontConsumeInput = false,
bool FailOnMatch = false,
typename Action = nope>
struct parser_config
{
static constexpr bool omit = OmitAttr;
static constexpr bool dont_consume_input = DontConsumeInput;
static constexpr bool fail_on_match = FailOnMatch;
static constexpr detail::flags flags(detail::flags f)
{
return omit ? detail::disable_attrs(f) : f;
}
[[maybe_unused]] Action action_;
};
}
/** Repeats the application of another parser `p` of type `Parser`,
@@ -169,36 +148,15 @@ namespace boost { namespace parser {
typename Parser,
typename DelimiterParser = detail::nope,
typename MinType = int64_t,
typename MaxType = int64_t>
typename MaxType = int64_t,
typename ParserConfig = parser_config<>>
struct repeat_parser;
/** Repeats the application of another parser `p` of type `Parser`, `[0,
Inf)` times. The parse always succeeds. The attribute produced is a
sequence of the type of attribute produced by `Parser`. */
template<typename Parser>
struct zero_plus_parser;
/** Repeats the application of another parser `p` of type `Parser`, `[1,
Inf)` times. The parse succeeds iff `p` succeeds at least once. The
attribute produced is a sequence of the type of attribute produced by
`Parser`. */
template<typename Parser>
struct one_plus_parser;
/** Repeats the application of another parser `p` of type `Parser`, `[1,
Inf)` times, applying a parser `d` of type `DelimiterParser` in
between each pair of applications of `p`. The parse succeeds iff `p`
succeeds at least once, and `d` succeeds each time it is applied. The
attribute produced is a sequence of the type of attribute produced by
`Parser`. */
template<typename Parser, typename DelimiterParser>
struct delimited_seq_parser;
/** Repeats the application of another parser of type `Parser`, `[0, 1]`
times. The parse always succeeds. The attribute produced is a
`std::optional<T>`, where `T` is the type of attribute produced by
`Parser`. */
template<typename Parser>
template<typename Parser, typename ParserConfig = parser_config<>>
struct opt_parser;
/** Applies each parser in `ParserTuple`, in order, stopping after the
@@ -206,9 +164,20 @@ namespace boost { namespace parser {
one of the sub-parsers succeeds. The attribute produced is a
`std::variant` over the types of attribute produced by the parsers in
`ParserTuple`. */
template<typename ParserTuple>
template<typename ParserTuple, typename ParserConfig = parser_config<>>
struct or_parser;
/** Applies each parsers in `ParserTuple`, an any order, stopping after
all of them have matched the input. The parse succeeds iff all the
parsers match, regardless of the order in which they do. The
attribute produced is a `parser::tuple` containing the attributes of
the subparsers, in their order of the parsers' appearance in
`ParserTuple`, not the order of the parsers' matches. It is an error
to specialize `perm_parser` with a `ParserTuple` template parameter
that includes an `eps_parser`. */
template<typename ParserTuple, typename ParserConfig = parser_config<>>
struct perm_parser;
/** Applies each parser in `ParserTuple`, in order. The parse succeeds
iff all of the sub-parsers succeed. The attribute produced is a
`std::tuple` over the types of attribute produced by the parsers in
@@ -219,28 +188,43 @@ namespace boost { namespace parser {
template<
typename ParserTuple,
typename BacktrackingTuple,
typename CombiningGroups>
typename CombiningGroups,
typename ParserConfig = parser_config<>>
struct seq_parser;
/** Applies the given parser `p` of type `Parser` and an invocable `a` of
type `Action`. `Action` shall model `semantic_action`, and `a` will
only be invoked if `p` succeeds. The parse succeeds iff `p` succeeds.
Produces no attribute. */
template<typename Parser, typename Action>
template<
typename Parser,
typename Action,
typename ParserConfig = parser_config<>>
struct action_parser;
/** Applies the given parser `p` of type `Parser`. The attribute produced
by `p` is passed to the fiven invocable `f` of type `F`. `f` will
only be invoked if `p` succeeds and sttributes are currently being
generated. The parse succeeds iff `p` succeeds. The attribute
produced is the the result of the call to `f`. */
template<
typename Parser,
typename F,
typename ParserConfig = parser_config<>>
struct transform_parser;
/** Applies the given parser `p` of type `Parser`. This parser produces
no attribute, and suppresses the production of any attributes that
would otherwise be produced by `p`. The parse succeeds iff `p`
succeeds. */
template<typename Parser>
template<typename Parser, typename ParserConfig = parser_config<>>
struct omit_parser;
/** Applies the given parser `p` of type `Parser`; regardless of the
attribute produced by `Parser`, this parser's attribute is equivalent
to `_where(ctx)` within a semantic action on `p`. The parse succeeds
iff `p` succeeds. */
template<typename Parser>
template<typename Parser, typename ParserConfig = parser_config<>>
struct raw_parser;
#if defined(BOOST_PARSER_DOXYGEN) || defined(__cpp_lib_concepts)
@@ -253,43 +237,49 @@ namespace boost { namespace parser {
non-contiguous, code using `string_view_parser` is ill-formed. The
parse succeeds iff `p` succeeds. This parser is only available in
C++20 and later. */
template<typename Parser>
template<typename Parser, typename ParserConfig = parser_config<>>
struct string_view_parser;
#endif
/** Applies the given parser `p` of type `Parser`, disabling the current
skipper in use, if any. The parse succeeds iff `p` succeeds. The
attribute produced is the type of attribute produced by `Parser`. */
template<typename Parser>
template<typename Parser, typename ParserConfig = parser_config<>>
struct lexeme_parser;
/** Applies the given parser `p` of type `Parser`, enabling
case-insensitive matching, based on Unicode case folding. The parse
succeeds iff `p` succeeds. The attribute produced is the type of
attribute produced by `Parser`. */
template<typename Parser>
template<typename Parser, typename ParserConfig = parser_config<>>
struct no_case_parser;
/** Applies the given parser `p` of type `Parser`, using a parser of type
`SkipParser` as the skipper. The parse succeeds iff `p` succeeds.
The attribute produced is the type of attribute produced by
`Parser`. */
template<typename Parser, typename SkipParser = detail::nope>
template<
typename Parser,
typename SkipParser = detail::nope,
typename ParserConfig = parser_config<>>
struct skip_parser;
/** Applies the given parser `p` of type `Parser`, producing no attributes
and consuming no input. The parse succeeds iff `p`'s success is
unequal to `FailOnMatch`. */
template<typename Parser, bool FailOnMatch>
template<
typename Parser,
typename ParserConfig,
typename ParserConfig = parser_config<>>
struct expect_parser;
/** Matches one of a set S of possible inputs, each of which is associated
with an attribute value of type `T`, forming a symbol table. New
elements and their associated attributes may be added to or removed
from S dynamically, during parsing; any such changes are reverted at
the end of parsing. The parse succeeds iff an element of S is
matched. \see `symbols` */
template<typename T>
/** Matches one of a set S of possible inputs, each of which is
associated with an attribute value of type `T`, forming a symbol
table. New elements and their associated attributes may be added to
or removed from S dynamically, during parsing; any such changes are
reverted at the end of parsing. The parse succeeds iff an element of
S is matched. \see `symbols` */
template<typename T, typename ParserConfig = parser_config<>>
struct symbol_parser;
/** Applies another parser `p`, associated with this parser via `TagType`.
@@ -308,22 +298,24 @@ namespace boost { namespace parser {
typename TagType,
typename Attribute,
typename LocalState,
typename ParamsTuple>
typename ParamsTuple,
typename ParserConfig = parser_config<>>
struct rule_parser;
/** Matches anything, and consumes no input. If `Predicate` is anything
other than `detail::nope` (which it is by default), and `pred_(ctx)`
evaluates to false, where `ctx` is the parser context, the parse
fails. */
template<typename Predicate>
template<typename Predicate, typename ParserConfig = parser_config<>>
struct eps_parser;
/** Matches only the end of input. Produces no attribute. */
template<typename ParserConfig = parser_config<>>
struct eoi_parser;
/** Matches anything, consumes no input, and produces an attribute of type
`RESOLVE(Attribute)`. */
template<typename Attribute>
template<typename Attribute, typename ParserConfig = parser_config<>>
struct attr_parser;
/** A tag type that can be passed as the first parameter to `char_()` when
@@ -364,17 +356,12 @@ namespace boost { namespace parser {
character being matched. */
struct digit_parser;
/** Matches a particular string, delimited by an iterator sentinel pair;
/** Maches a particular string, delimited by an iterator sentinel pair;
produces no attribute. */
template<typename StrIter, typename StrSentinel>
struct string_parser;
/** 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
/** Maches 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
@@ -382,7 +369,7 @@ namespace boost { namespace parser {
template<bool NewlinesOnly, bool NoNewlines>
struct ws_parser;
/** Matches the strings "true" and "false", producing an attribute of
/** Maches the strings "true" and "false", producing an attribute of
`true` or `false`, respectively, and fails on any other input. */
struct bool_parser;
@@ -467,13 +454,14 @@ namespace boost { namespace parser {
typename ParamsTuple = no_params>
struct callback_rule;
#ifdef BOOST_PARSER_DOXYGEN
/** Returns a reference to the attribute(s) (i.e. return value) of the
bottommost parser; multiple attributes will be stored within a
`parser::tuple`. You may write to this value in a semantic action to
control what attribute value(s) the associated parser produces.
Returns `none` if the bottommost parser does produce an attribute. */
template<typename Context>
decltype(auto) _val(Context const & context);
#endif
/** Returns a reference to the attribute or attributes already produced by
the bottommost parser; multiple attributes will be stored within a
@@ -532,9 +520,13 @@ namespace boost { namespace parser {
/** Report that the error described in `message` occurred at `location`,
using the context's error handler. */
template<typename Iter, typename Context>
#if BOOST_PARSER_USE_CONCEPTS
template<std::forward_iterator I, typename Context>
#else
template<typename I, typename Context>
#endif
void _report_error(
Context const & context, std::string_view message, Iter location);
Context const & context, std::string_view message, I location);
/** Report that the error described in `message` occurred at
`_where(context).begin()`, using the context's error handler. */
@@ -543,9 +535,13 @@ namespace boost { namespace parser {
/** Report that the warning described in `message` occurred at `location`,
using the context's error handler. */
template<typename Iter, typename Context>
#if BOOST_PARSER_USE_CONCEPTS
template<std::forward_iterator I, typename Context>
#else
template<typename I, typename Context>
#endif
void _report_warning(
Context const & context, std::string_view message, Iter location);
Context const & context, std::string_view message, I location);
/** Report that the warning described in `message` occurred at
`_where(context).begin()`, using the context's error handler. */

View File

@@ -193,7 +193,12 @@ namespace boost::parser {
/** Produces a range of subranges of a given range `base`. Each subrange
is either a subrange of `base` that does not match the given parser
`parser`, or is the given replacement for a match, `replacement`. */
`parser`, or is the given replacement for a match, `replacement`.
In addition to the template parameter constraints, `V` and
`ReplacementV` must be ranges of `char`, or must have the same UTF
format, and `V` and `ReplacementV` must meet the same compatibility
requirements as described in `std::ranges::join_view`. */
template<
#if BOOST_PARSER_USE_CONCEPTS
std::ranges::viewable_range V,

View File

@@ -289,7 +289,7 @@ namespace boost::parser {
/** Produces a sequence of subranges of the underlying sequence of type
`V`. Each subrange is a nonoverlapping match of the given parser,
using a skip-parser if provided. */
using a skip-parser if provided. */
template<
#if BOOST_PARSER_USE_CONCEPTS
std::ranges::viewable_range V,

View File

@@ -8,7 +8,7 @@ namespace boost::parser {
/** Produces a sequence of subranges of the underlying sequence of type
`V`. the underlying sequence is split into subranges delimited by
matches of the given parser, possibly using a given skip-parser. */
matches of the given parser, possibly using a given skip-parser. */
template<
#if BOOST_PARSER_USE_CONCEPTS
std::ranges::viewable_range V,

View File

@@ -24,7 +24,11 @@ namespace boost::parser {
#else
/** A view that produces UTF-8 from an given sequence of UTF. */
/** A view that produces UTF-8 from an given sequence of UTF.
\tparam V Constrained by `std::ranges::view<V>`. Additionally, the
value type of `V` must be `char`, `wchar_t`, `char8_t`, `char16_t`, or
`char32_t`. */
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS || defined(BOOST_PARSER_DOXYGEN)
template<detail::text::utf_range V>
requires std::ranges::view<V>
@@ -44,7 +48,11 @@ namespace boost::parser {
{}
};
/** A view that produces UTF-16 from an given sequence of UTF. */
/** A view that produces UTF-16 from an given sequence of UTF.
\tparam V Constrained by `std::ranges::view<V>`. Additionally, the
value type of `V` must be `char`, `wchar_t`, `char8_t`, `char16_t`, or
`char32_t`. */
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS || defined(BOOST_PARSER_DOXYGEN)
template<detail::text::utf_range V>
requires std::ranges::view<V>
@@ -64,7 +72,11 @@ namespace boost::parser {
{}
};
/** A view that produces UTF-32 from an given sequence of UTF. */
/** A view that produces UTF-32 from an given sequence of UTF.
\tparam V Constrained by `std::ranges::view<V>`. Additionally, the
value type of `V` must be `char`, `wchar_t`, `char8_t`, `char16_t`, or
`char32_t`. */
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS || defined(BOOST_PARSER_DOXYGEN)
template<detail::text::utf_range V>
requires std::ranges::view<V>

View File

@@ -3,7 +3,8 @@
#include <boost/parser/replace.hpp>
#if (!defined(_MSC_VER) || BOOST_PARSER_USE_CONCEPTS)
#if (!defined(_MSC_VER) || BOOST_PARSER_USE_CONCEPTS) && \
(!defined(__GNUC__) || 12 <= __GNUC__ || !BOOST_PARSER_USE_CONCEPTS)
namespace boost::parser {
@@ -16,10 +17,10 @@ namespace boost::parser {
template<typename I, typename S, typename Parser>
using attr_type = decltype(std::declval<Parser const &>().call(
std::bool_constant<false>{},
std::declval<I &>(),
std::declval<S>(),
std::declval<parse_context<I, S, default_error_handler>>(),
std::declval<
parse_context<false, false, I, S, default_error_handler>>(),
ws,
detail::default_flags(),
std::declval<bool &>()));
@@ -315,7 +316,13 @@ namespace boost::parser {
/** Produces a range of subranges of a given range `base`. Each subrange
is either a subrange of `base` that does not match the given parser
`parser`, or is `f(*boost::parser::parse(match, parser))`, where `f`
is the given invocable and `match` is the matching subrange. */
is the given invocable and `match` is the matching subrange.
In addition to the template parameter constraints, `F` must be
invocable with the attribute type of `Parser`; `V` and the range type
produced by `F`, "`Rf`" must be ranges of `char`, or must have the
same UTF format; and `V` and `Rf` must meet the same compatibility
requirements as described in `std::ranges::join_view`. */
template<
#if BOOST_PARSER_USE_CONCEPTS
std::ranges::viewable_range V,
@@ -498,11 +505,11 @@ namespace boost::parser {
using base_type::operator++;
private:
detail::maybe_const<Const, transform_replace_view> * parent_;
detail::maybe_const<Const, transform_replace_view> * parent_ = {};
BOOST_PARSER_SUBRANGE<I, S> r_;
BOOST_PARSER_SUBRANGE<I> curr_;
I next_it_;
bool in_match_;
I next_it_ = {};
bool in_match_ = {};
};
template<bool Const>

View File

@@ -4,7 +4,7 @@ include(CTest)
enable_testing()
add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} -j4 -C ${CMAKE_CFG_INTDIR})
add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure -j4 -C ${CMAKE_CFG_INTDIR})
##################################################
# Parser tests
@@ -46,7 +46,6 @@ macro(add_test_executable name)
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)
@@ -54,6 +53,8 @@ add_test_executable(replace)
add_test_executable(transform_replace)
add_test_executable(hl)
add_test_executable(aggr_tuple_assignment)
add_test_executable(parser_perm)
add_test_executable(parser_attributes)
add_test_executable(parser_lazy_params)
add_test_executable(parser_if_switch)
add_test_executable(parser_rule)
@@ -69,3 +70,7 @@ add_test_executable(case_fold_generated)
add_test_executable(no_case)
add_test_executable(merge_separate)
add_test_executable(parse_coords_new)
add_test_executable(parser_seq_permutations_1)
add_test_executable(parser_seq_permutations_2)
add_test_executable(parser_or_permutations_1)
add_test_executable(parser_or_permutations_2)

File diff suppressed because it is too large Load Diff

View File

@@ -20,6 +20,11 @@ void compile_or_attribute()
using attr_t = decltype(prefix_parse(first, last, parser));
static_assert(
std::is_same_v<attr_t, std::optional<std::optional<int>>>);
static_assert(std::is_same_v<
attribute_t<
decltype(BOOST_PARSER_SUBRANGE(first, last)),
decltype(parser)>,
std::optional<int>>);
}
// scalar | scalar
@@ -27,18 +32,33 @@ void compile_or_attribute()
constexpr auto parser = char_ | char_;
using attr_t = decltype(prefix_parse(first, last, parser));
static_assert(std::is_same_v<attr_t, std::optional<char>>);
static_assert(std::is_same_v<
attribute_t<
decltype(BOOST_PARSER_SUBRANGE(first, last)),
decltype(parser)>,
char>);
}
{
constexpr auto parser = char_ | char_ | eps;
using attr_t = decltype(prefix_parse(first, last, parser));
static_assert(
std::is_same_v<attr_t, std::optional<std::optional<char>>>);
static_assert(std::is_same_v<
attribute_t<
decltype(BOOST_PARSER_SUBRANGE(first, last)),
decltype(parser)>,
std::optional<char>>);
}
{
constexpr auto parser = int_ | char_;
using attr_t = decltype(prefix_parse(first, last, parser));
static_assert(
std::is_same_v<attr_t, std::optional<std::variant<int, char>>>);
static_assert(std::is_same_v<
attribute_t<
decltype(BOOST_PARSER_SUBRANGE(first, last)),
decltype(parser)>,
std::variant<int, char>>);
}
{
constexpr auto parser = int_ | char_ | eps;
@@ -46,6 +66,11 @@ void compile_or_attribute()
static_assert(std::is_same_v<
attr_t,
std::optional<std::optional<std::variant<int, char>>>>);
static_assert(std::is_same_v<
attribute_t<
decltype(BOOST_PARSER_SUBRANGE(first, last)),
decltype(parser)>,
std::optional<std::variant<int, char>>>);
}
// -scalar | -scalar
@@ -54,12 +79,22 @@ void compile_or_attribute()
using attr_t = decltype(prefix_parse(first, last, parser));
static_assert(
std::is_same_v<attr_t, std::optional<std::optional<char>>>);
static_assert(std::is_same_v<
attribute_t<
decltype(BOOST_PARSER_SUBRANGE(first, last)),
decltype(parser)>,
std::optional<char>>);
}
{
constexpr auto parser = -char_ | -char_ | eps;
using attr_t = decltype(prefix_parse(first, last, parser));
static_assert(
std::is_same_v<attr_t, std::optional<std::optional<char>>>);
static_assert(std::is_same_v<
attribute_t<
decltype(BOOST_PARSER_SUBRANGE(first, last)),
decltype(parser)>,
std::optional<char>>);
}
{
constexpr auto parser = -int_ | -char_;
@@ -69,6 +104,11 @@ void compile_or_attribute()
attr_t,
std::optional<
std::variant<std::optional<int>, std::optional<char>>>>);
static_assert(std::is_same_v<
attribute_t<
decltype(BOOST_PARSER_SUBRANGE(first, last)),
decltype(parser)>,
std::variant<std::optional<int>, std::optional<char>>>);
}
{
constexpr auto parser = -int_ | -char_ | eps;
@@ -78,6 +118,13 @@ void compile_or_attribute()
attr_t,
std::optional<std::optional<
std::variant<std::optional<int>, std::optional<char>>>>>);
static_assert(
std::is_same_v<
attribute_t<
decltype(BOOST_PARSER_SUBRANGE(first, last)),
decltype(parser)>,
std::optional<
std::variant<std::optional<int>, std::optional<char>>>>);
}
// seq<T> | seq<T>
@@ -85,18 +132,33 @@ void compile_or_attribute()
constexpr auto parser = *char_ | *char_;
using attr_t = decltype(prefix_parse(first, last, parser));
static_assert(std::is_same_v<attr_t, std::optional<std::string>>);
static_assert(std::is_same_v<
attribute_t<
decltype(BOOST_PARSER_SUBRANGE(first, last)),
decltype(parser)>,
std::string>);
}
{
constexpr auto parser = *char_ | *char_ | eps;
using attr_t = decltype(prefix_parse(first, last, parser));
static_assert(
std::is_same_v<attr_t, std::optional<std::optional<std::string>>>);
static_assert(std::is_same_v<
attribute_t<
decltype(BOOST_PARSER_SUBRANGE(first, last)),
decltype(parser)>,
std::optional<std::string>>);
}
{
constexpr auto parser = *string("str") | *string("str");
using attr_t = decltype(prefix_parse(first, last, parser));
static_assert(
std::is_same_v<attr_t, std::optional<std::vector<std::string>>>);
static_assert(std::is_same_v<
attribute_t<
decltype(BOOST_PARSER_SUBRANGE(first, last)),
decltype(parser)>,
std::vector<std::string>>);
}
{
constexpr auto parser = *string("str") | *string("str") | eps;
@@ -104,6 +166,11 @@ void compile_or_attribute()
static_assert(std::is_same_v<
attr_t,
std::optional<std::optional<std::vector<std::string>>>>);
static_assert(std::is_same_v<
attribute_t<
decltype(BOOST_PARSER_SUBRANGE(first, last)),
decltype(parser)>,
std::optional<std::vector<std::string>>>);
}
// seq<T> | seq<U>

View File

@@ -23,6 +23,9 @@ TEST(no_case, doc_example)
auto const alpha_parser = bp::no_case[bp::char_('a', 'z')];
assert(bp::parse("a" | bp::as_utf32, bp::no_case[alpha_parser])); // Match!
assert(bp::parse("B" | bp::as_utf32, bp::no_case[alpha_parser])); // Match!
(void)street_parser;
(void)alpha_parser;
}
@@ -165,6 +168,97 @@ TEST(no_case, match_any_within_string)
}
}
TEST(no_case, symbol_table)
{
// without mutation
{
symbols<int> const roman_numerals = {
{"I", 1}, {"V", 5}, {"X", 10}, {"L", 50}, {"C", 100}};
symbols<std::string> const named_strings = {
{"I", "1"}, {"V", "5"}, {"X", "10"}, {"L", "50"}, {"C", "100"}};
{
auto const result = parse("I", no_case[roman_numerals]);
EXPECT_TRUE(result);
EXPECT_EQ(*result, 1);
}
{
auto const result = parse("i", no_case[roman_numerals]);
EXPECT_TRUE(result);
EXPECT_EQ(*result, 1);
}
{
auto const result = parse("I", no_case[named_strings]);
EXPECT_TRUE(result);
EXPECT_EQ(*result, "1");
}
{
auto const result = parse("i", no_case[named_strings]);
EXPECT_TRUE(result);
EXPECT_EQ(*result, "1");
}
{
auto const result = parse("L", no_case[roman_numerals]);
EXPECT_TRUE(result);
EXPECT_EQ(*result, 50);
}
{
auto const result = parse("l", no_case[roman_numerals]);
EXPECT_TRUE(result);
EXPECT_EQ(*result, 50);
}
{
auto const result = parse("L", no_case[named_strings]);
EXPECT_TRUE(result);
EXPECT_EQ(*result, "50");
}
{
auto const result = parse("l", no_case[named_strings]);
EXPECT_TRUE(result);
EXPECT_EQ(*result, "50");
}
}
// with mutation
{
symbols<int> roman_numerals;
roman_numerals.insert_for_next_parse("I", 1)("V", 5)("X", 10);
auto const add_numeral = [&roman_numerals](auto & context) {
using namespace boost::parser::literals;
char chars[2] = {get(_attr(context), 0_c), 0};
roman_numerals.insert(context, chars, get(_attr(context), 1_c));
};
auto const numerals_parser = omit[roman_numerals] >>
(char_ >> int_)[add_numeral] >>
no_case[roman_numerals];
{
auto const result = parse("VL50L", numerals_parser);
EXPECT_TRUE(result);
EXPECT_EQ(*result, 50);
EXPECT_FALSE(parse("L", roman_numerals));
}
{
auto const result = parse("VL50l", numerals_parser);
EXPECT_TRUE(result);
EXPECT_EQ(*result, 50);
EXPECT_FALSE(parse("L", roman_numerals));
}
{
auto const result = parse("VC100C", numerals_parser);
EXPECT_TRUE(result);
EXPECT_EQ(*result, 100);
EXPECT_FALSE(parse("C", roman_numerals));
}
{
auto const result = parse("Vc100C", numerals_parser);
EXPECT_TRUE(result);
EXPECT_EQ(*result, 100);
EXPECT_FALSE(parse("C", roman_numerals));
}
}
}
constexpr auto capital_sharp_s = u8""; // U+1E9E
constexpr auto small_sharp_s = u8"ß"; // U+00DF
constexpr auto double_s = u8"sS"; // U+0073 U+0073
@@ -290,7 +384,7 @@ TEST(no_case, detail_no_case_iter)
auto first =
detail::no_case_iter(mixed_sharp_s1, detail::text::null_sentinel);
while (first != detail::text::null_sentinel) {
folded.push_back(*first);
folded.push_back((char)*first);
++first;
}
EXPECT_EQ(folded, "sss");
@@ -302,7 +396,7 @@ TEST(no_case, detail_no_case_iter)
auto first =
detail::no_case_iter(mixed_sharp_s2, detail::text::null_sentinel);
while (first != detail::text::null_sentinel) {
folded.push_back(*first);
folded.push_back((char)*first);
++first;
}
EXPECT_EQ(folded, "sss");
@@ -315,7 +409,7 @@ TEST(no_case, detail_no_case_iter)
detail::no_case_iter(street, detail::text::null_sentinel);
auto first = first_const;
while (first != detail::text::null_sentinel) {
folded.push_back(*first);
folded.push_back((char)*first);
++first;
}
EXPECT_EQ(folded, "strasse");

View File

@@ -7,8 +7,11 @@
* http://www.boost.org/LICENSE_1_0.txt)
*/
#include <boost/parser/config.hpp>
#include "boost/parser/parser.hpp"
#if !BOOST_PARSER_USE_STD_TUPLE
#include <boost/parser/parser.hpp>
#include <cassert>
#include <iostream>
@@ -52,13 +55,13 @@ const auto degrees_symbol = bp::no_case[ bp::lit("degrees") | bp
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 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) {
>> (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;
@@ -66,7 +69,7 @@ const auto degrees_decimal_minutes_def = (bp::uint_ >> -degrees_symbol
const auto degrees_minutes_seconds_def = (bp::uint_ >> -degrees_symbol
>> uint_0_60 >> -minutes_symbol
>> (double_0_60 - '.') >> -seconds_symbol) [( [](auto ctx) {
>> (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];
@@ -80,14 +83,14 @@ const auto degrees_def = degrees_minutes_seconds
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) {
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) {
const auto longitude_def = (degrees >> eastwest) [( [](auto & ctx) {
auto d = _attr(ctx)[0_c];
auto ew = _attr(ctx)[1_c];
_pass(ctx) = d <= 180;
@@ -96,13 +99,13 @@ const auto longitude_def = (degrees >> eastwest) [( [](auto ctx) {
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 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]}; } )] )
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]}; } )] );
[( [](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);
@@ -192,3 +195,5 @@ TEST(parse_coords, all_examples)
EXPECT_LT(std::abs(result->y - 50.0083), 0.001);
}
}
#endif

View File

@@ -627,61 +627,63 @@ TEST(parser, star_and_plus_collapsing)
}
}
TEST(parser, action)
{
{{std::string str = "";
std::stringstream ss;
auto action = [&ss](auto & context) { ss << _attr(context); };
auto parser = *char_('b')[action];
EXPECT_TRUE(parse(str, parser));
EXPECT_EQ(ss.str(), "");
}
{
std::string str = "b";
std::stringstream ss;
auto action = [&ss](auto & context) { ss << _attr(context); };
auto parser = *char_('b')[action];
EXPECT_TRUE(parse(str, parser));
EXPECT_EQ(ss.str(), "b");
}
{
std::string str = "bb";
std::stringstream ss;
auto action = [&ss](auto & context) { ss << _attr(context); };
auto parser = *char_('b')[action];
EXPECT_TRUE(parse(str, parser));
EXPECT_TRUE(parse(str, parser));
EXPECT_EQ(ss.str(), "bbbb");
}
}
TEST(parser, action_)
{
{
std::string str = "";
std::stringstream ss;
auto action = [&ss](auto & context) { ss << _attr(context); };
auto parser = +char_('b')[action];
EXPECT_FALSE(parse(str, parser));
EXPECT_EQ(ss.str(), "");
{
std::stringstream ss;
auto action = [&ss](auto & ctx) { ss << _attr(ctx); };
auto parser = *char_('b')[action];
EXPECT_TRUE(parse(str, parser));
EXPECT_EQ(ss.str(), "");
}
{
str = "b";
std::stringstream ss;
auto action = [&ss](auto & ctx) { ss << _attr(ctx); };
auto parser = *char_('b')[action];
EXPECT_TRUE(parse(str, parser));
EXPECT_EQ(ss.str(), "b");
}
{
str = "bb";
std::stringstream ss;
auto action = [&ss](auto & ctx) { ss << _attr(ctx); };
auto parser = *char_('b')[action];
EXPECT_TRUE(parse(str, parser));
EXPECT_TRUE(parse(str, parser));
EXPECT_EQ(ss.str(), "bbbb");
}
}
{
std::string str = "b";
std::stringstream ss;
auto action = [&ss](auto & context) { ss << _attr(context); };
auto parser = +char_('b')[action];
EXPECT_TRUE(parse(str, parser));
EXPECT_EQ(ss.str(), "b");
{
std::string str = "";
std::stringstream ss;
auto action = [&ss](auto & ctx) { ss << _attr(ctx); };
auto parser = +char_('b')[action];
EXPECT_FALSE(parse(str, parser));
EXPECT_EQ(ss.str(), "");
}
{
std::string str = "b";
std::stringstream ss;
auto action = [&ss](auto & ctx) { ss << _attr(ctx); };
auto parser = +char_('b')[action];
EXPECT_TRUE(parse(str, parser));
EXPECT_EQ(ss.str(), "b");
}
{
std::string str = "bb";
std::stringstream ss;
auto action = [&ss](auto & ctx) { ss << _attr(ctx); };
auto parser = +char_('b')[action];
EXPECT_TRUE(parse(str, parser));
EXPECT_TRUE(parse(str, parser));
EXPECT_EQ(ss.str(), "bbbb");
}
}
{
std::string str = "bb";
std::stringstream ss;
auto action = [&ss](auto & context) { ss << _attr(context); };
auto parser = +char_('b')[action];
EXPECT_TRUE(parse(str, parser));
EXPECT_TRUE(parse(str, parser));
EXPECT_EQ(ss.str(), "bbbb");
}
}
}
TEST(parser, star_as_string_or_vector)
@@ -849,6 +851,106 @@ TEST(parser, star_as_string_or_vector)
}
}
TEST(parser, transform)
{
int calls = 0;
auto by_value_str_sum = [&](std::string s) {
++calls;
std::transform(
s.begin(), s.end(), s.begin(), [](auto ch) { return ch - '0'; });
return std::accumulate(s.begin(), s.end(), 0);
};
auto cref_str_sum = [&](std::string const & s) {
++calls;
int retval = 0;
for (auto ch : s) {
retval += ch - '0';
}
return retval;
};
auto rv_ref_str_sum = [&](std::string && s) {
++calls;
std::transform(
s.begin(), s.end(), s.begin(), [](auto ch) { return ch - '0'; });
return std::accumulate(s.begin(), s.end(), 0);
};
{
constexpr auto parser = +char_;
std::string str = "012345";
{
auto result = parse(str, parser);
EXPECT_TRUE(result);
EXPECT_EQ(*result, "012345");
}
{
calls = 0;
auto result = parse(str, transform(by_value_str_sum)[parser]);
EXPECT_TRUE(result);
EXPECT_EQ(*result, 15);
EXPECT_EQ(calls, 1);
}
{
calls = 0;
auto result = parse(str, transform(cref_str_sum)[parser]);
EXPECT_TRUE(result);
EXPECT_EQ(*result, 15);
EXPECT_EQ(calls, 1);
}
{
calls = 0;
auto result = parse(str, transform(rv_ref_str_sum)[parser]);
EXPECT_TRUE(result);
EXPECT_EQ(*result, 15);
EXPECT_EQ(calls, 1);
}
}
{
constexpr auto parser = +char_;
std::string str = "012345";
{
calls = 0;
auto result = parse(str, omit[transform(by_value_str_sum)[parser]]);
EXPECT_TRUE(result);
EXPECT_EQ(calls, 0);
}
{
calls = 0;
auto result = parse(str, omit[transform(cref_str_sum)[parser]]);
EXPECT_TRUE(result);
EXPECT_EQ(calls, 0);
}
{
calls = 0;
auto result = parse(str, omit[transform(rv_ref_str_sum)[parser]]);
EXPECT_TRUE(result);
EXPECT_EQ(calls, 0);
}
}
}
TEST(parser, transform_doc_example)
{
//[ transform_directive_example
auto str_sum = [&](std::string const & s) {
int retval = 0;
for (auto ch : s) {
retval += ch - '0';
}
return retval;
};
namespace bp = boost::parser;
constexpr auto parser = +bp::char_;
std::string str = "012345";
auto result = bp::parse(str, bp::transform(str_sum)[parser]);
assert(result);
assert(*result == 15);
static_assert(std::is_same_v<decltype(result), std::optional<int>>);
//]
(void)result;
}
TEST(parser, omit)
{
{
@@ -2042,11 +2144,7 @@ TEST(parser, combined_seq_and_or)
{
std::string str = "abc";
tuple<
boost::parser::detail::any_copyable,
boost::parser::detail::any_copyable,
boost::parser::detail::any_copyable>
chars;
tuple<std::any, std::any, std::any> chars;
EXPECT_TRUE(parse(str, parser, chars));
}
@@ -2856,3 +2954,64 @@ TEST(parser, rule_example)
using namespace rule_construction_example;
EXPECT_TRUE(bp::parse("3 4", type, bp::ws));
}
TEST(parser, utf_iterator_copy_ctor)
{
namespace bp = boost::parser;
char mut_chars[] = "foo";
char const const_chars[] = "bar";
auto mut_view = mut_chars | bp::as_utf8;
auto const_view = const_chars | bp::as_utf8;
auto mut_it = mut_view.begin();
auto mut_last = mut_view.end();
auto const_it = const_view.begin();
auto const_last = const_view.begin();
const_it = mut_it;
const_last = mut_last;
std::string copy;
copy.resize(3);
std::copy(const_it, const_last, copy.begin());
EXPECT_EQ(copy, "foo");
}
namespace github_issue_125_ {
namespace bp = boost::parser;
constexpr bp::
rule<struct replacement_field_rule, std::optional<unsigned short>>
replacement_field = "replacement_field";
constexpr auto replacement_field_def = bp::lit('{') >> -bp::ushort_;
BOOST_PARSER_DEFINE_RULES(replacement_field);
}
TEST(parser, github_issue_125)
{
namespace bp = boost::parser;
using namespace github_issue_125_;
unsigned short integer_found = 99;
auto print_repl_field = [&](auto & ctx) {
const std::optional<unsigned short> & val = bp::_attr(ctx);
if (val)
integer_found = *val;
else
integer_found = 77;
};
{
integer_found = 99;
auto result = bp::parse("{9", replacement_field[print_repl_field]);
EXPECT_TRUE(result);
EXPECT_EQ(integer_found, 9);
}
{
integer_found = 99;
auto result = bp::parse("{", replacement_field[print_repl_field]);
EXPECT_TRUE(result);
EXPECT_EQ(integer_found, 77);
}
}

View File

@@ -14,31 +14,29 @@ constexpr rule<struct abc_def_tag, std::string> abc_def = "abc or def";
constexpr auto abc_def_def = string("abc") | string("def");
BOOST_PARSER_DEFINE_RULES(abc_def);
auto const fail = [](auto & context) { _pass(context) = false; };
auto const fail = [](auto & ctx) { _pass(ctx) = false; };
constexpr rule<struct fail_abc_pass_def_tag, std::string> fail_abc_pass_def =
"abc";
constexpr auto fail_abc_pass_def_def = string("abc")[fail] | string("def");
BOOST_PARSER_DEFINE_RULES(fail_abc_pass_def);
auto const attr_to_val = [](auto & context) { _val(context) = _attr(context); };
auto const attr_to_val = [](auto & ctx) { _val(ctx) = _attr(ctx); };
constexpr rule<struct action_copy_abc_def_tag, std::string>
action_copy_abc_def = "abc or def";
constexpr auto action_copy_abc_def_def =
string("abc")[attr_to_val] | string("def")[attr_to_val];
BOOST_PARSER_DEFINE_RULES(action_copy_abc_def);
auto const abc_value = [](auto & context) { _val(context) = "abc"; };
auto const def_value = [](auto & context) { _val(context) = "def"; };
auto const abc_value = [](auto & ctx) { _val(ctx) = "abc"; };
auto const def_value = [](auto & ctx) { _val(ctx) = "def"; };
constexpr rule<struct rev_abc_def_tag, std::string> rev_abc_def = "abc or def";
constexpr auto rev_abc_def_def =
string("abc")[def_value] | string("def")[abc_value];
BOOST_PARSER_DEFINE_RULES(rev_abc_def);
auto const append_attr = [](auto & context) {
_locals(context) += _attr(context);
};
auto const locals_to_val = [](auto & context) {
_val(context) = std::move(_locals(context));
auto const append_attr = [](auto & ctx) { _locals(ctx) += _attr(ctx); };
auto const locals_to_val = [](auto & ctx) {
_val(ctx) = std::move(_locals(ctx));
};
rule<struct locals_abc_def_tag, std::string, std::string> const locals_abc_def =
"abc or def";
@@ -46,10 +44,10 @@ auto locals_abc_def_def = -string("abc")[append_attr] >>
-string("def")[append_attr] >> eps[locals_to_val];
BOOST_PARSER_DEFINE_RULES(locals_abc_def);
TEST(parser, side_effects)
TEST(action, side_effects)
{
int i = 0;
auto increment_i = [&i](auto & context) { ++i; };
auto increment_i = [&i](auto & ctx) { ++i; };
using no_attribute_return = decltype(parse("xyz", char_('a')[increment_i]));
static_assert(std::is_same_v<no_attribute_return, bool>);
@@ -75,7 +73,7 @@ TEST(parser, side_effects)
}
}
TEST(parser, pass)
TEST(action, pass)
{
{
std::string const str = "xyz";
@@ -107,7 +105,7 @@ TEST(parser, pass)
}
}
TEST(parser, val_attr)
TEST(action, val_attr)
{
{
std::string const str = "abc";
@@ -135,7 +133,7 @@ TEST(parser, val_attr)
}
}
TEST(parser, locals)
TEST(action, locals)
{
{
std::string const str = "";
@@ -162,3 +160,60 @@ TEST(parser, locals)
EXPECT_EQ(*result, "def");
}
}
auto drop_result = [](auto & ctx) {};
auto auto_assign = [](auto & ctx) { return _attr(ctx); };
auto auto_assign_multi_string_1 = [](std::string const & str1,
std::string const & str2) {
return str1 + str2;
};
auto auto_assign_multi_string_2 = [](std::string && str1, std::string && str2) {
return str1 + str2;
};
constexpr rule<struct str_rule_1_tag, std::string> str_rule_1 = "str_rule_1";
constexpr auto str_rule_1_def = +char_;
BOOST_PARSER_DEFINE_RULES(str_rule_1);
constexpr rule<struct str_rule_2_tag, std::string> str_rule_2 = "str_rule_2";
constexpr auto str_rule_2_def = (+char_)[drop_result];
BOOST_PARSER_DEFINE_RULES(str_rule_2);
constexpr rule<struct str_rule_3_tag, std::string> str_rule_3 = "str_rule_3";
constexpr auto str_rule_3_def = (+char_)[auto_assign];
BOOST_PARSER_DEFINE_RULES(str_rule_3);
constexpr rule<struct str_rule_6_tag, std::string> str_rule_6 = "str_rule_6";
constexpr auto str_rule_6_def =
(+(char_ - ' ') >> ' ' >> +char_)[auto_assign_multi_string_1];
BOOST_PARSER_DEFINE_RULES(str_rule_6);
constexpr rule<struct str_rule_7_tag, std::string> str_rule_7 = "str_rule_7";
constexpr auto str_rule_7_def =
(+(char_ - ' ') >> ' ' >> +char_)[auto_assign_multi_string_2];
BOOST_PARSER_DEFINE_RULES(str_rule_7);
TEST(action, alternate_invocables)
{
{
auto result_1 = parse("some text", str_rule_1);
EXPECT_TRUE(result_1);
EXPECT_EQ(*result_1, "some text");
auto result_2 = parse("some text", str_rule_2);
EXPECT_TRUE(result_2);
EXPECT_EQ(*result_2, "");
auto result_3 = parse("some text", str_rule_3);
EXPECT_TRUE(result_3);
EXPECT_EQ(*result_3, "some text");
auto result_6 = parse("some text", str_rule_6);
EXPECT_TRUE(result_6);
EXPECT_EQ(*result_6, "sometext");
auto result_7 = parse("some text", str_rule_7);
EXPECT_TRUE(result_7);
EXPECT_EQ(*result_7, "sometext");
}
}

View File

@@ -15,7 +15,7 @@ auto make_13 = [](auto & context) { return 13; };
auto const first_param_to_val = [](auto & context) {
using namespace boost::parser::literals;
_val(context) = get(_params(context), 0_c);
_val(context) = (int)get(_params(context), 0_c);
};
auto const second_param_to_val = [](auto & context) {
using namespace boost::parser::literals;

View File

@@ -115,11 +115,12 @@ TEST(parser, full_parse_api)
EXPECT_EQ(out, 'a');
out = 0;
first = str.c_str();
auto ws_copy = ws;
EXPECT_FALSE(prefix_parse(
first,
boost::parser::detail::text::null_sentinel,
char_('b'),
ws,
ws_copy,
out));
EXPECT_EQ(out, 0);
}
@@ -138,7 +139,8 @@ TEST(parser, full_parse_api)
EXPECT_TRUE(parse(str.c_str(), char_, ws, out));
EXPECT_EQ(out, 'a');
out = 0;
EXPECT_FALSE(parse(str.c_str(), char_('b'), ws, out));
auto ws_copy = ws;
EXPECT_FALSE(parse(str.c_str(), char_('b'), ws_copy, out));
EXPECT_EQ(out, 0);
}
@@ -156,11 +158,12 @@ TEST(parser, full_parse_api)
ws),
'a');
first = str.c_str();
auto ws_copy = ws;
EXPECT_TRUE(!prefix_parse(
first,
boost::parser::detail::text::null_sentinel,
char_('b'),
ws));
ws_copy));
}
// returned attr, using skipper, range
{
@@ -172,7 +175,8 @@ TEST(parser, full_parse_api)
{
EXPECT_TRUE(parse(str.c_str(), char_, ws));
EXPECT_EQ(*parse(str.c_str(), char_, ws), 'a');
EXPECT_FALSE(parse(str.c_str(), char_('b'), ws));
auto ws_copy = ws;
EXPECT_FALSE(parse(str.c_str(), char_('b'), ws_copy));
}
// callback, iter/sent
@@ -217,6 +221,20 @@ TEST(parser, full_parse_api)
first = str.c_str();
EXPECT_EQ(out, 'a');
}
{
char out = 0;
auto callbacks = [&out](auto tag, auto x) { out = x; };
auto first = str.c_str();
auto ws_copy = ws;
EXPECT_TRUE(callback_prefix_parse(
first,
boost::parser::detail::text::null_sentinel,
callback_char_rule,
ws_copy,
callbacks));
first = str.c_str();
EXPECT_EQ(out, 'a');
}
// callback, using skipper, range
{
char out = 0;
@@ -229,8 +247,9 @@ TEST(parser, full_parse_api)
{
char out = 0;
auto callbacks = [&out](auto tag, auto x) { out = x; };
auto ws_copy = ws;
EXPECT_TRUE(callback_parse(
str.c_str(), callback_char_rule, ws, callbacks));
str.c_str(), callback_char_rule, ws_copy, callbacks));
EXPECT_EQ(out, 'a');
}
}
@@ -1797,11 +1816,7 @@ TEST(parser, combined_seq_and_or)
{
char const * str = "abc";
tuple<
boost::parser::detail::any_copyable,
boost::parser::detail::any_copyable,
boost::parser::detail::any_copyable>
chars;
tuple<std::any, std::any, std::any> chars;
EXPECT_TRUE(parse(str, parser, chars));
}
@@ -1979,6 +1994,7 @@ TEST(parser, attr_out_param_compat)
using namespace bp::literals;
assert(success);
(void)success;
assert(bp::get(result, 0_c) == std::vector<int>({'r', U'ô', 'l', 'e'}));
assert(bp::get(result, 1_c) == "foo");
}
@@ -1996,6 +2012,7 @@ TEST(parser, attr_out_param_compat)
using namespace bp::literals;
assert(success);
(void)success;
// The 4 code points "rôle" get transcoded to 5 UTF-8 code points to fit in the std::string.
assert(bp::get(result, 0_c) == std::vector<char>({'r', (char)0xc3, (char)0xb4, 'l', 'e'}));
assert(bp::get(result, 1_c) == "foo");

View File

@@ -0,0 +1,67 @@
/**
* Copyright (C) 2024 T. Zachary Laine
*
* Distributed under the Boost Software License, Version 1.0. (See
* accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*/
#include <boost/parser/parser.hpp>
#include <gtest/gtest.h>
namespace bp = boost::parser;
bp::rule<struct seq1_tag, bp::tuple<int, char>> seq1 = "";
bp::rule<struct seq2_tag, bp::tuple<int, char>> seq2 = "";
auto const seq1_def = bp::int_ >> bp::char_('a');
auto const seq2_def = bp::int_ >> bp::char_('b');
BOOST_PARSER_DEFINE_RULES(seq1, seq2);
TEST(attributes, internal_errors_munging_attributes)
{
// These are just some assorted cases that have, or seemed likely to,
// cause problems when an internal failure in an alternative wipes out an
// existing result. This covers all the cases where "if (!success)"
// causes the attribute to be overwritten with a default-constructed
// value.
{
auto const parser =
bp::string("FOO") >> -(bp::string("bar") | bp::string("foo"));
auto result = bp::parse("FOOfoo", parser);
EXPECT_TRUE(result);
EXPECT_EQ(bp::get(*result, bp::llong<0>{}), std::string("FOO"));
EXPECT_EQ(bp::get(*result, bp::llong<1>{}), std::string("foo"));
}
{
auto const parser = bp::merge
[bp::string("FOO") >> (bp::string("bar") | bp::string("foo"))];
auto result = bp::parse("FOOfoo", parser);
EXPECT_TRUE(result);
EXPECT_EQ(*result, std::string("FOOfoo"));
}
{
auto const parser = bp::merge
[(bp::attr(std::vector<std::string>({"FOO"})) | bp::eps) >>
(bp::repeat(1)[bp::string("foo")] | bp::eps)];
auto result = bp::parse("", parser);
EXPECT_TRUE(result);
EXPECT_TRUE(*result);
EXPECT_EQ(*result, std::vector<std::string>({"FOO"}));
}
{
auto const parser = bp::merge[seq1 >> (seq2 | seq1)];
auto result = bp::parse("7a9a", parser);
EXPECT_TRUE(result);
EXPECT_EQ(*result, (bp::tuple<int, char>(9, 'a')));
}
}

View File

@@ -0,0 +1,338 @@
/**
* Copyright (C) 2024 T. Zachary Laine
*
* Distributed under the Boost Software License, Version 1.0. (See
* accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*/
#include <boost/parser/parser.hpp>
#include <gtest/gtest.h>
namespace bp = boost::parser;
namespace make {
template<typename... Ts>
auto tuple(Ts &&... xs)
{
return bp::tuple<Ts...>((Ts &&) xs...);
}
}
/*
{P0, -P0, *P0, P1, P2, P3, eps}
<cartesian product>
{P0, P1, P2, P3, eps, *P0, -P0, (P0 |/>> P2), -(P0 |/>> P1), (-P0 |/>> P1)}
P0 = bp::string("foo");
P1 = bp::string("bar");
P2 = bp::int_;
P3 = bp::char_('c');
*/
using namespace std::literals;
TEST(attributes, or_parser_permutations_1)
{
[[maybe_unused]] int dummy = 0; // for clang-format(!)
// P0
{
auto result = bp::parse("foo", bp::string("foo") | bp::string("foo"));
EXPECT_TRUE(result);
EXPECT_EQ(*result, "foo"s);
}
{
auto result = bp::parse("bar", bp::string("foo") | bp::string("bar"));
EXPECT_TRUE(result);
EXPECT_EQ(*result, "bar"s);
}
{
auto result = bp::parse("42", bp::string("foo") | bp::int_);
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 1u);
EXPECT_EQ(std::get<int>(*result), 42);
}
{
auto result = bp::parse("c", bp::string("foo") | bp::char_('c'));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 1u);
EXPECT_EQ(std::get<char>(*result), 'c');
}
{
auto result = bp::parse("foo", bp::string("foo") | bp::eps);
EXPECT_TRUE(result);
EXPECT_EQ(*result, std::optional("foo"s));
}
{
auto result = bp::parse("foo", bp::string("foo") | *bp::string("foo"));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 0);
EXPECT_EQ(std::get<std::string>(*result), "foo"s);
}
{
auto result = bp::parse("", bp::string("foo") | -bp::string("foo"));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 1u);
EXPECT_EQ(std::get<std::optional<std::string>>(*result), std::nullopt);
}
{
auto result = bp::parse(
"foo", bp::string("foo") | (bp::string("foo") | bp::int_));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 0);
EXPECT_EQ(std::get<std::string>(*result), "foo"s);
}
{
auto result = bp::parse(
"bar",
bp::string("foo") | -(bp::string("foo") | bp::string("bar")));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 1);
EXPECT_EQ(std::get<std::optional<std::string>>(*result), "bar"s);
}
{
auto result = bp::parse(
"", bp::string("foo") | (-bp::string("foo") | bp::string("bar")));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 1);
EXPECT_EQ(std::get<std::optional<std::string>>(*result), std::nullopt);
}
{
auto result = bp::parse(
"foo", bp::string("foo") | (bp::string("foo") >> bp::int_));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 0);
EXPECT_EQ(std::get<std::string>(*result), "foo"s);
}
{
auto result = bp::parse(
"", bp::string("foo") | -(bp::string("foo") >> bp::string("bar")));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 1u);
EXPECT_EQ(
(std::get<std::optional<bp::tuple<std::string, std::string>>>(
*result)),
std::nullopt);
}
{
auto result = bp::parse(
"bar",
bp::string("foo") | (-bp::string("foo") >> bp::string("bar")));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 1u);
EXPECT_EQ(
(std::get<bp::tuple<std::optional<std::string>, std::string>>(
*result)),
(make::tuple(std::optional<std::string>{}, "bar"s)));
}
// -P0
{
auto result = bp::parse("foo", -bp::string("foo") | bp::string("foo"));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 0);
EXPECT_EQ(
std::get<std::optional<std::string>>(*result),
std::optional("foo"s));
}
{
auto result = bp::parse("", -bp::string("foo") | bp::string("bar"));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 0);
EXPECT_EQ(std::get<std::optional<std::string>>(*result), std::nullopt);
}
{
auto result = bp::parse("", -bp::string("foo") | bp::int_);
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 0);
EXPECT_EQ(std::get<std::optional<std::string>>(*result), std::nullopt);
}
{
auto result = bp::parse("", -bp::string("foo") | bp::char_('c'));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 0);
EXPECT_EQ(std::get<std::optional<std::string>>(*result), std::nullopt);
}
{
auto result = bp::parse("foo", -bp::string("foo") | bp::eps);
EXPECT_TRUE(result);
EXPECT_EQ(*result, "foo"s);
}
{
auto result = bp::parse("foo", -bp::string("foo") | *bp::string("foo"));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 0);
EXPECT_EQ(std::get<std::optional<std::string>>(*result), "foo"s);
}
{
auto result = bp::parse("foo", -bp::string("foo") | -bp::string("foo"));
EXPECT_TRUE(result);
EXPECT_EQ(*result, "foo"s);
}
{
auto result =
bp::parse("", -bp::string("foo") | (bp::string("foo") | bp::int_));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 0);
EXPECT_EQ(std::get<std::optional<std::string>>(*result), std::nullopt);
}
{
auto result = bp::parse(
"foo",
-bp::string("foo") | -(bp::string("foo") | bp::string("bar")));
EXPECT_TRUE(result);
EXPECT_EQ(*result, "foo"s);
}
{
auto result = bp::parse(
"", -bp::string("foo") | (-bp::string("foo") | bp::string("bar")));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 0);
EXPECT_EQ(std::get<std::optional<std::string>>(*result), std::nullopt);
}
{
auto result = bp::parse(
"foo", -bp::string("foo") | (bp::string("foo") >> bp::int_));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 0);
EXPECT_EQ(std::get<std::optional<std::string>>(*result), "foo"s);
}
{
auto result = bp::parse(
"foo",
-bp::string("foo") | -(bp::string("foo") >> bp::string("bar")));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 0);
EXPECT_EQ(std::get<std::optional<std::string>>(*result), "foo"s);
}
{
auto result = bp::parse(
"foo",
-bp::string("foo") | (-bp::string("foo") >> bp::string("bar")));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 0);
EXPECT_EQ(std::get<std::optional<std::string>>(*result), "foo"s);
}
// *P0
{
auto result = bp::parse(
"foo", bp::lexeme[*bp::string("foo")] | bp::string("foo"));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 0);
EXPECT_EQ(
std::get<std::vector<std::string>>(*result), std::vector({"foo"s}));
}
{
auto result =
bp::parse("foofoo", *bp::string("foo") | bp::string("bar"));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 0);
EXPECT_EQ(
std::get<std::vector<std::string>>(*result),
std::vector({"foo"s, "foo"s}));
}
{
auto result = bp::parse("", *bp::string("foo") | bp::int_);
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 0);
EXPECT_EQ(
std::get<std::vector<std::string>>(*result),
std::vector<std::string>{});
}
{
auto result = bp::parse("", *bp::string("foo") | bp::char_('c'));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 0);
EXPECT_EQ(
std::get<std::vector<std::string>>(*result),
std::vector<std::string>{});
}
{
auto result = bp::parse("foofoo", *bp::string("foo") | bp::eps);
EXPECT_TRUE(result);
EXPECT_EQ(*result, std::optional(std::vector({"foo"s, "foo"s})));
}
{
auto result = bp::parse(
"foofoo", bp::lexeme[*bp::string("foo")] | *bp::string("foo"));
EXPECT_TRUE(result);
EXPECT_EQ(*result, std::vector({"foo"s, "foo"s}));
}
{
auto result =
bp::parse("foofoo", *bp::string("foo") | -bp::string("foo"));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 0);
EXPECT_EQ(
std::get<std::vector<std::string>>(*result),
std::vector({"foo"s, "foo"s}));
}
{
auto result = bp::parse(
"foofoo",
bp::lexeme[*bp::string("foo")] | (bp::string("foo") | bp::int_));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 0);
EXPECT_EQ(
std::get<std::vector<std::string>>(*result),
std::vector({"foo"s, "foo"s}));
}
{
auto result = bp::parse(
"foofoo",
bp::lexeme[*bp::string("foo")] |
-(bp::string("foo") | bp::string("bar")));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 0);
EXPECT_EQ(
std::get<std::vector<std::string>>(*result),
std::vector({"foo"s, "foo"s}));
}
{
auto result = bp::parse(
"foofoo",
bp::lexeme[*bp::string("foo")] |
(-bp::string("foo") | bp::string("bar")));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 0);
EXPECT_EQ(
std::get<std::vector<std::string>>(*result),
std::vector({"foo"s, "foo"s}));
}
{
auto result = bp::parse(
"foofoo",
bp::lexeme[*bp::string("foo")] | (bp::string("foo") >> bp::int_));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 0);
EXPECT_EQ(
std::get<std::vector<std::string>>(*result),
std::vector({"foo"s, "foo"s}));
}
{
auto result = bp::parse(
"foofoo",
bp::lexeme[*bp::string("foo")] |
-(bp::string("foo") >> bp::string("bar")));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 0);
EXPECT_EQ(
std::get<std::vector<std::string>>(*result),
std::vector({"foo"s, "foo"s}));
}
{
auto result = bp::parse(
"foofoo",
bp::lexeme[*bp::string("foo")] |
(-bp::string("foo") >> bp::string("bar")));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 0);
EXPECT_EQ(
std::get<std::vector<std::string>>(*result),
std::vector({"foo"s, "foo"s}));
}
}

View File

@@ -0,0 +1,312 @@
/**
* Copyright (C) 2024 T. Zachary Laine
*
* Distributed under the Boost Software License, Version 1.0. (See
* accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*/
#include <boost/parser/parser.hpp>
#include <gtest/gtest.h>
namespace bp = boost::parser;
namespace make {
template<typename... Ts>
auto tuple(Ts &&... xs)
{
return bp::tuple<Ts...>((Ts &&) xs...);
}
}
/*
{P0, -P0, *P0, P1, P2, P3, eps}
<cartesian product>
{P0, P1, P2, P3, eps, *P0, -P0, (P0 >>/| P2), -(P0 >>/| P1), (-P0 >>/| P1)}
P0 = bp::string("foo");
P1 = bp::string("bar");
P2 = bp::int_;
P3 = bp::char_('c');
*/
using namespace std::literals;
TEST(attributes, or_parser_permutations_2)
{
[[maybe_unused]] int dummy = 0; // for clang-format(!)
// P1
{
auto result = bp::parse("foo", bp::string("bar") | bp::string("foo"));
EXPECT_TRUE(result);
EXPECT_EQ(*result, "foo"s);
}
{
auto result = bp::parse("bar", bp::string("bar") | bp::string("bar"));
EXPECT_TRUE(result);
EXPECT_EQ(*result, "bar"s);
}
{
auto result = bp::parse("42", bp::string("bar") | bp::int_);
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 1u);
EXPECT_EQ(std::get<int>(*result), 42);
}
{
auto result = bp::parse("bar", bp::string("bar") | bp::char_('c'));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 0);
EXPECT_EQ(std::get<std::string>(*result), "bar"s);
}
{
auto result = bp::parse("", bp::string("bar") | bp::eps);
EXPECT_TRUE(result);
EXPECT_EQ(*result, std::nullopt);
}
{
auto result =
bp::parse("foofoo", bp::string("bar") | *bp::string("foo"));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 1u);
EXPECT_EQ(
std::get<std::vector<std::string>>(*result),
std::vector({"foo"s, "foo"s}));
}
{
auto result = bp::parse("bar", bp::string("bar") | -bp::string("foo"));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 0);
EXPECT_EQ(std::get<std::string>(*result), "bar"s);
}
{
auto result =
bp::parse("42", bp::string("bar") | (bp::string("foo") | bp::int_));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 1u);
EXPECT_EQ(std::get<int>(*result), 42);
}
{
auto result = bp::parse(
"bar",
bp::string("foo") | -(bp::string("foo") | bp::string("bar")));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 1u);
EXPECT_EQ(std::get<std::optional<std::string>>(*result), "bar"s);
}
{
auto result = bp::parse(
"foo",
bp::string("bar") | (-bp::string("foo") | bp::string("bar")));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 1u);
EXPECT_EQ(std::get<std::optional<std::string>>(*result), "foo"s);
}
{
auto result = bp::parse(
"bar", bp::string("bar") | (bp::string("foo") >> bp::int_));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 0);
EXPECT_EQ(std::get<std::string>(*result), "bar"s);
}
{
auto result = bp::parse(
"bar",
bp::string("bar") | -(bp::string("foo") >> bp::string("bar")));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 0);
EXPECT_EQ(std::get<std::string>(*result), "bar"s);
}
{
auto result = bp::parse(
"bar",
bp::string("bar") | (-bp::string("foo") >> bp::string("bar")));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 0);
EXPECT_EQ(std::get<std::string>(*result), "bar"s);
}
// P2
{
auto result = bp::parse("42", bp::int_ | bp::string("foo"));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 0);
EXPECT_EQ(std::get<int>(*result), 42);
}
{
auto result = bp::parse("bar", bp::int_ | bp::string("bar"));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 1u);
EXPECT_EQ(std::get<std::string>(*result), "bar"s);
}
{
auto result = bp::parse("42", bp::int_ | bp::int_, bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(*result, 42);
}
{
auto result = bp::parse("c", bp::int_ | bp::char_('c'));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 1u);
EXPECT_EQ(std::get<char>(*result), 'c');
}
{
auto result = bp::parse("", bp::int_ | bp::eps);
EXPECT_TRUE(result);
EXPECT_EQ(*result, std::nullopt);
}
{
auto result = bp::parse("foofoo", bp::int_ | *bp::string("foo"));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 1u);
EXPECT_EQ(
std::get<std::vector<std::string>>(*result),
std::vector({"foo"s, "foo"s}));
}
{
auto result = bp::parse("42", bp::int_ | -bp::string("foo"));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 0);
EXPECT_EQ(std::get<int>(*result), 42);
}
{
auto result =
bp::parse("42", bp::int_ | (bp::string("foo") | bp::int_));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 0);
EXPECT_EQ(std::get<int>(*result), 42);
}
{
auto result = bp::parse(
"bar", bp::int_ | -(bp::string("foo") | bp::string("bar")));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 1u);
EXPECT_EQ(
std::get<std::optional<std::string>>(*result),
std::optional("bar"s));
}
{
auto result =
bp::parse("", bp::int_ | (-bp::string("foo") | bp::string("bar")));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 1u);
EXPECT_EQ(std::get<std::optional<std::string>>(*result), std::nullopt);
}
{
auto result =
bp::parse("42", bp::int_ | (bp::string("foo") >> bp::int_));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 0);
EXPECT_EQ(std::get<int>(*result), 42);
}
{
auto result = bp::parse(
"42", bp::int_ | -(bp::string("foo") >> bp::string("bar")));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 0);
EXPECT_EQ(std::get<int>(*result), 42);
}
{
auto result = bp::parse(
"42", bp::int_ | (-bp::string("foo") >> bp::string("bar")));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 0);
EXPECT_EQ(std::get<int>(*result), 42);
}
// P3
{
auto result = bp::parse("c", bp::char_('c') | bp::string("foo"));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 0);
EXPECT_EQ(std::get<char>(*result), 'c');
}
{
auto result = bp::parse("bar", bp::char_('c') | bp::string("bar"));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 1u);
EXPECT_EQ(std::get<std::string>(*result), "bar"s);
}
{
auto result = bp::parse("42", bp::char_('c') | bp::int_);
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 1u);
EXPECT_EQ(std::get<int>(*result), 42);
}
{
auto result = bp::parse("c", bp::char_('c') | bp::char_('c'));
EXPECT_TRUE(result);
EXPECT_EQ(*result, 'c');
}
{
auto result = bp::parse("c", bp::char_('c') | bp::eps);
EXPECT_TRUE(result);
EXPECT_EQ(*result, 'c');
}
{
auto result = bp::parse("foofoo", bp::char_('c') | *bp::string("foo"));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 1u);
EXPECT_EQ(
std::get<std::vector<std::string>>(*result),
std::vector({"foo"s, "foo"s}));
}
{
auto result = bp::parse("c", bp::char_('c') | -bp::string("foo"));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 0);
EXPECT_EQ(std::get<char>(*result), 'c');
}
{
auto result =
bp::parse("42", bp::char_('c') | (bp::string("foo") | bp::int_));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 2u);
EXPECT_EQ(std::get<int>(*result), 42);
}
{
auto result = bp::parse(
"bar", bp::char_('c') | -(bp::string("foo") | bp::string("bar")));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 1u);
EXPECT_EQ(
std::get<std::optional<std::string>>(*result),
std::optional("bar"s));
}
{
auto result = bp::parse(
"foo", bp::char_('c') | (-bp::string("foo") | bp::string("bar")));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 1u);
EXPECT_EQ(
std::get<std::optional<std::string>>(*result),
std::optional("foo"s));
}
{
auto result = bp::parse(
"foo42", bp::char_('c') | (bp::string("foo") >> bp::int_));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 1u);
EXPECT_EQ(
(std::get<bp::tuple<std::string, int>>(*result)),
(make::tuple("foo"s, 42)));
}
{
auto result = bp::parse(
"c", bp::char_('c') | -(bp::string("foo") >> bp::string("bar")));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 0);
EXPECT_EQ(std::get<char>(*result), 'c');
}
{
auto result = bp::parse(
"c", bp::char_('c') | (-bp::string("foo") >> bp::string("bar")));
EXPECT_TRUE(result);
EXPECT_EQ(result->index(), 0);
EXPECT_EQ(std::get<char>(*result), 'c');
}
// eps | ... prohibited.
}

90
test/parser_perm.cpp Normal file
View File

@@ -0,0 +1,90 @@
/**
* Copyright (C) 2024 T. Zachary Laine
*
* Distributed under the Boost Software License, Version 1.0. (See
* accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*/
#include <boost/parser/parser.hpp>
#include <gtest/gtest.h>
namespace bp = boost::parser;
using namespace std::literals;
TEST(permutation_parser, basic)
{
{
constexpr auto parser = bp::int_ || bp::string("foo");
{
auto result = bp::parse("42 foo", parser, bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(*result, (bp::tuple<int, std::string>(42, "foo"s)));
}
{
auto result = bp::parse("42foo", parser, bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(*result, (bp::tuple<int, std::string>(42, "foo"s)));
}
{
auto result = bp::parse("foo 42", parser, bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(*result, (bp::tuple<int, std::string>(42, "foo"s)));
}
{
auto result = bp::parse("foo42", parser, bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(*result, (bp::tuple<int, std::string>(42, "foo"s)));
}
}
{
constexpr auto parser = bp::int_ || bp::string("foo") || bp::char_('g');
{
auto result = bp::parse("42 foo g", parser, bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(
*result,
(bp::tuple<int, std::string, double>(42, "foo"s, 'g')));
}
{
auto result = bp::parse("42 g foo", parser, bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(
*result,
(bp::tuple<int, std::string, double>(42, "foo"s, 'g')));
}
{
auto result = bp::parse("foo 42 g", parser, bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(
*result,
(bp::tuple<int, std::string, double>(42, "foo"s, 'g')));
}
{
auto result = bp::parse("foo g 42", parser, bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(
*result,
(bp::tuple<int, std::string, double>(42, "foo"s, 'g')));
}
{
auto result = bp::parse("g foo 42", parser, bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(
*result,
(bp::tuple<int, std::string, double>(42, "foo"s, 'g')));
}
{
auto result = bp::parse("g 42 foo", parser, bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(
*result,
(bp::tuple<int, std::string, double>(42, "foo"s, 'g')));
}
}
}

View File

@@ -557,3 +557,85 @@ namespace more_about_rules_4 {
return bp::parse(str, bp::omit[parens], bp::ws);
}
}
// clang-format off
namespace param_example {
//[ extended_param_yaml_example_rules
namespace bp = boost::parser;
// A type to represent the YAML parse context.
enum class context {
block_in,
block_out,
block_key,
flow_in,
flow_out,
flow_key
};
// A YAML value; no need to fill it in for this example.
struct value
{
// ...
};
// YAML [66], just stubbed in here.
auto const s_separate_in_line = bp::eps;
// YAML [137].
bp::rule<struct c_flow_seq_tag, value> c_flow_sequence = "c-flow-sequence";
// YAML [80].
bp::rule<struct s_separate_tag> s_separate = "s-separate";
// YAML [136].
bp::rule<struct in_flow_tag, value> in_flow = "in-flow";
// YAML [138]; just eps below.
bp::rule<struct ns_s_flow_seq_entries_tag, value> ns_s_flow_seq_entries =
"ns-s-flow-seq-entries";
// YAML [81]; just eps below.
bp::rule<struct s_separate_lines_tag> s_separate_lines = "s-separate-lines";
// Parser for YAML [137].
auto const c_flow_sequence_def =
'[' >>
-s_separate.with(bp::_p<0>, bp::_p<1>) >>
-in_flow.with(bp::_p<0>, bp::_p<1>) >>
']';
// Parser for YAML [80].
auto const s_separate_def = bp::switch_(bp::_p<1>)
(context::block_out, s_separate_lines.with(bp::_p<0>))
(context::block_in, s_separate_lines.with(bp::_p<0>))
(context::flow_out, s_separate_lines.with(bp::_p<0>))
(context::flow_in, s_separate_lines.with(bp::_p<0>))
(context::block_key, s_separate_in_line)
(context::flow_key, s_separate_in_line);
// Parser for YAML [136].
auto const in_flow_def = bp::switch_(bp::_p<1>)
(context::flow_out, ns_s_flow_seq_entries.with(bp::_p<0>, context::flow_in))
(context::flow_in, ns_s_flow_seq_entries.with(bp::_p<0>, context::flow_in))
(context::block_out, ns_s_flow_seq_entries.with(bp::_p<0>, context::flow_key))
(context::flow_key, ns_s_flow_seq_entries.with(bp::_p<0>, context::flow_key));
auto const ns_s_flow_seq_entries_def = bp::eps;
auto const s_separate_lines_def = bp::eps;
BOOST_PARSER_DEFINE_RULES(
c_flow_sequence,
s_separate,
in_flow,
ns_s_flow_seq_entries,
s_separate_lines);
//]
}
// clang-format on
TEST(parser, extended_param_example)
{
using namespace param_example;
//[ extended_param_yaml_example_use
auto const test_parser = c_flow_sequence.with(4, context::block_out);
auto result = bp::parse("[]", test_parser);
assert(result);
//]
(void)result;
}

View File

@@ -0,0 +1,339 @@
/**
* Copyright (C) 2024 T. Zachary Laine
*
* Distributed under the Boost Software License, Version 1.0. (See
* accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*/
#include <boost/parser/parser.hpp>
#include <gtest/gtest.h>
namespace bp = boost::parser;
namespace make {
template<typename... Ts>
auto tuple(Ts &&... xs)
{
return bp::tuple<Ts...>((Ts &&) xs...);
}
}
/*
{P0, -P0, *P0, P1, P2, P3, eps}
<cartesian product>
{P0, P1, P2, P3, eps, *P0, -P0, (P0 >>/| P2), -(P0 >>/| P1), (-P0 >>/| P1)}
P0 = bp::string("foo");
P1 = bp::string("bar");
P2 = bp::int_;
P3 = bp::char_('c');
*/
using namespace std::literals;
TEST(attributes, seq_parser_permutations_1)
{
[[maybe_unused]] int dummy = 0; // for clang-format(!)
// P0
{
auto result =
bp::parse("foofoo", bp::string("foo") >> bp::string("foo"));
EXPECT_TRUE(result);
EXPECT_EQ(*result, (make::tuple("foo"s, "foo"s)));
}
{
auto result =
bp::parse("foobar", bp::string("foo") >> bp::string("bar"));
EXPECT_TRUE(result);
EXPECT_EQ(*result, (make::tuple("foo"s, "bar"s)));
}
{
auto result = bp::parse("foo42", bp::string("foo") >> bp::int_);
EXPECT_TRUE(result);
EXPECT_EQ(*result, (make::tuple("foo"s, 42)));
}
{
auto result = bp::parse("fooc", bp::string("foo") >> bp::char_('c'));
EXPECT_TRUE(result);
EXPECT_EQ(*result, "fooc"s);
}
{
auto result = bp::parse("foo", bp::string("foo") >> bp::eps);
EXPECT_TRUE(result);
EXPECT_EQ(*result, "foo"s);
}
{
auto result =
bp::parse("foofoofoo", bp::string("foo") >> *bp::string("foo"));
EXPECT_TRUE(result);
EXPECT_EQ(*result, std::vector({"foo"s, "foo"s, "foo"s}));
}
{
auto result = bp::parse("foo", bp::string("foo") >> -bp::string("foo"));
EXPECT_TRUE(result);
EXPECT_EQ(*result, (make::tuple("foo"s, std::optional<std::string>{})));
}
{
auto result = bp::parse(
"foofoo42", bp::string("foo") >> (bp::string("foo") >> bp::int_));
EXPECT_TRUE(result);
EXPECT_EQ(*result, (make::tuple("foo"s, "foo"s, 42)));
}
{
auto result = bp::parse(
"foofoobar",
bp::string("foo") >> -(bp::string("foo") >> bp::string("bar")));
EXPECT_TRUE(result);
EXPECT_EQ(
*result,
(make::tuple("foo"s, std::optional(make::tuple("foo"s, "bar"s)))));
}
{
auto result = bp::parse(
"foofoobar",
bp::string("foo") >> (-bp::string("foo") >> bp::string("bar")));
EXPECT_TRUE(result);
EXPECT_EQ(
*result, (make::tuple("foo"s, std::optional("foo"s), "bar"s)));
}
{
auto result = bp::parse(
"foo42", bp::string("foo") >> (bp::string("foo") | bp::int_));
EXPECT_TRUE(result);
EXPECT_EQ(
*result, (make::tuple("foo"s, std::variant<std::string, int>(42))));
}
{
auto result = bp::parse(
"foo",
bp::string("foo") >> -(bp::string("foo") | bp::string("bar")));
EXPECT_TRUE(result);
EXPECT_EQ(*result, (make::tuple("foo"s, std::optional<std::string>{})));
}
{
auto result = bp::parse(
"foo",
bp::string("foo") >> (-bp::string("foo") | bp::string("bar")));
EXPECT_TRUE(result);
EXPECT_EQ(
*result,
(make::tuple(
"foo"s,
std::variant<std::optional<std::string>, std::string>(
std::nullopt))));
}
// -P0
{
auto result =
bp::parse("foofoo", -bp::string("foo") >> bp::string("foo"));
EXPECT_TRUE(result);
EXPECT_EQ(*result, (make::tuple(std::optional("foo"s), "foo"s)));
}
{
auto result = bp::parse("bar", -bp::string("foo") >> bp::string("bar"));
EXPECT_TRUE(result);
EXPECT_EQ(*result, (make::tuple(std::optional<std::string>{}, "bar"s)));
}
{
auto result = bp::parse("42", -bp::string("foo") >> bp::int_);
EXPECT_TRUE(result);
EXPECT_EQ(*result, (make::tuple(std::optional<std::string>{}, 42)));
}
{
auto result = bp::parse("c", -bp::string("foo") >> bp::char_('c'));
EXPECT_TRUE(result);
EXPECT_EQ(*result, (make::tuple(std::optional<std::string>{}, 'c')));
}
{
auto result = bp::parse("foo", -bp::string("foo") >> bp::eps);
EXPECT_TRUE(result);
EXPECT_EQ(*result, std::optional("foo"s));
}
{
auto result =
bp::parse("foofoo", -bp::string("foo") >> *bp::string("foo"));
EXPECT_TRUE(result);
EXPECT_EQ(*result, std::vector({"foo"s, "foo"s}));
}
{
auto result =
bp::parse("foofoo", -bp::string("foo") >> -bp::string("foo"));
EXPECT_TRUE(result);
EXPECT_EQ(
*result,
(make::tuple(std::optional("foo"s), std::optional("foo"s))));
}
{
auto result = bp::parse(
"foofoo42", -bp::string("foo") >> (bp::string("foo") >> bp::int_));
EXPECT_TRUE(result);
EXPECT_EQ(*result, (make::tuple(std::optional("foo"s), "foo"s, 42)));
}
{
auto result = bp::parse(
"foofoobar",
-bp::string("foo") >> -(bp::string("foo") >> bp::string("bar")));
EXPECT_TRUE(result);
EXPECT_EQ(
*result,
(make::tuple(
std::optional("foo"s),
std::optional((make::tuple("foo"s, "bar"s))))));
}
{
auto result = bp::parse(
"foofoobar",
-bp::string("foo") >> (-bp::string("foo") >> bp::string("bar")));
EXPECT_TRUE(result);
EXPECT_EQ(
*result,
(make::tuple(
std::optional("foo"s), std::optional("foo"s), "bar"s)));
}
{
auto result = bp::parse(
"foo42", -bp::string("foo") >> (bp::string("foo") | bp::int_));
EXPECT_TRUE(result);
EXPECT_EQ(
*result,
(make::tuple(
std::optional("foo"s), std::variant<std::string, int>(42))));
}
{
auto result = bp::parse(
"foobar",
-bp::string("foo") >> -(bp::string("foo") | bp::string("bar")));
EXPECT_TRUE(result);
EXPECT_EQ(
*result,
(make::tuple(std::optional("foo"s), std::optional("bar"s))));
}
{
auto result = bp::parse(
"foofoo",
-bp::string("foo") >> (-bp::string("foo") | bp::string("bar")));
EXPECT_TRUE(result);
EXPECT_EQ(
*result,
(make::tuple(
"foo"s,
std::variant<std::optional<std::string>, std::string>(
std::optional("foo"s)))));
}
// *P0
{
auto result = bp::parse(
"foo foo",
bp::lexeme[*bp::string("foo")] >> bp::string("foo"),
bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(*result, std::vector({"foo"s, "foo"s}));
}
{
auto result =
bp::parse("foobar", *bp::string("foo") >> bp::string("bar"));
EXPECT_TRUE(result);
EXPECT_EQ(*result, std::vector({"foo"s, "bar"s}));
}
{
auto result = bp::parse("foo42", *bp::string("foo") >> bp::int_);
EXPECT_TRUE(result);
EXPECT_EQ(*result, (make::tuple(std::vector({"foo"s}), 42)));
}
{
auto result = bp::parse("fooc", *bp::string("foo") >> bp::char_('c'));
EXPECT_TRUE(result);
EXPECT_EQ(*result, (make::tuple(std::vector({"foo"s}), 'c')));
}
{
auto result = bp::parse("foo", *bp::string("foo") >> bp::eps);
EXPECT_TRUE(result);
EXPECT_EQ(*result, std::vector({"foo"s}));
}
{
auto result = bp::parse(
"foo foo",
bp::lexeme[*bp::string("foo")] >> *bp::string("foo"),
bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(
*result,
(make::tuple(std::vector({"foo"s}), std::vector({"foo"s}))));
}
{
auto result =
bp::parse("foo", *bp::string("foo") >> -bp::string("foo"));
EXPECT_TRUE(result);
EXPECT_EQ(*result, std::vector({"foo"s}));
}
{
auto result = bp::parse(
"foo foo42",
bp::lexeme[*bp::string("foo")] >> (bp::string("foo") >> bp::int_),
bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(*result, (make::tuple(std::vector({"foo"s, "foo"s}), 42)));
}
{
auto result = bp::parse(
"foo foobar",
bp::lexeme[*bp::string("foo")] >>
-(bp::string("foo") >> bp::string("bar")),
bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(
*result,
(make::tuple(
std::vector({"foo"s}),
std::optional((make::tuple("foo"s, "bar"s))))));
}
{
auto result = bp::parse(
"foo foobar",
bp::lexeme[*bp::string("foo")] >>
(-bp::string("foo") >> bp::string("bar")),
bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(*result, std::vector({"foo"s, "foo"s, "bar"s}));
}
{
auto result = bp::parse(
"foo 42",
bp::lexeme[*bp::string("foo")] >> (bp::string("foo") | bp::int_),
bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(
*result,
(make::tuple(
std::vector({"foo"s}), std::variant<std::string, int>(42))));
}
{
auto result = bp::parse(
"foo bar",
bp::lexeme[*bp::string("foo")] >>
-(bp::string("foo") | bp::string("bar")),
bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(*result, std::vector({"foo"s, "bar"s}));
}
{
auto result = bp::parse(
"foo",
bp::lexeme[*bp::string("foo")] >>
(-bp::string("foo") | bp::string("bar")),
bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(
*result,
(make::tuple(
std::vector({"foo"s}),
std::variant<std::optional<std::string>, std::string>(
std::nullopt))));
}
}

View File

@@ -0,0 +1,370 @@
/**
* Copyright (C) 2024 T. Zachary Laine
*
* Distributed under the Boost Software License, Version 1.0. (See
* accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*/
#include <boost/parser/parser.hpp>
#include <gtest/gtest.h>
namespace bp = boost::parser;
namespace make {
template<typename... Ts>
auto tuple(Ts &&... xs)
{
return bp::tuple<Ts...>((Ts &&) xs...);
}
}
/*
{P0, -P0, *P0, P1, P2, P3, eps}
<cartesian product>
{P0, P1, P2, P3, eps, *P0, -P0, (P0 >>/| P2), -(P0 >>/| P1), (-P0 >>/| P1)}
P0 = bp::string("foo");
P1 = bp::string("bar");
P2 = bp::int_;
P3 = bp::char_('c');
*/
using namespace std::literals;
TEST(attributes, seq_parser_permutations_2)
{
[[maybe_unused]] int dummy = 0; // for clang-format(!)
// P1
{
auto result =
bp::parse("barfoo", bp::string("bar") >> bp::string("foo"));
EXPECT_TRUE(result);
EXPECT_EQ(*result, (make::tuple("bar"s, "foo"s)));
}
{
auto result =
bp::parse("barbar", bp::string("bar") >> bp::string("bar"));
EXPECT_TRUE(result);
EXPECT_EQ(*result, (make::tuple("bar"s, "bar"s)));
}
{
auto result = bp::parse("bar42", bp::string("bar") >> bp::int_);
EXPECT_TRUE(result);
EXPECT_EQ(*result, (make::tuple("bar"s, 42)));
}
{
auto result = bp::parse("barc", bp::string("bar") >> bp::char_('c'));
EXPECT_TRUE(result);
EXPECT_EQ(*result, "barc"s);
}
{
auto result = bp::parse("bar", bp::string("bar") >> bp::eps);
EXPECT_TRUE(result);
EXPECT_EQ(*result, "bar"s);
}
{
auto result =
bp::parse("barfoofoo", bp::string("bar") >> *bp::string("foo"));
EXPECT_TRUE(result);
EXPECT_EQ(*result, std::vector({"bar"s, "foo"s, "foo"s}));
}
{
auto result =
bp::parse("barfoo", bp::string("bar") >> -bp::string("foo"));
EXPECT_TRUE(result);
EXPECT_EQ(*result, (make::tuple("bar"s, std::optional("foo"s))));
}
{
auto result = bp::parse(
"barfoo42", bp::string("bar") >> (bp::string("foo") >> bp::int_));
EXPECT_TRUE(result);
EXPECT_EQ(*result, (make::tuple("bar"s, "foo"s, 42)));
}
{
auto result = bp::parse(
"barfoobar",
bp::string("bar") >> -(bp::string("foo") >> bp::string("bar")));
EXPECT_TRUE(result);
EXPECT_EQ(
*result,
(make::tuple("bar"s, std::optional(make::tuple("foo"s, "bar"s)))));
}
{
auto result = bp::parse(
"barfoobar",
bp::string("bar") >> (-bp::string("foo") >> bp::string("bar")));
EXPECT_TRUE(result);
EXPECT_EQ(
*result, (make::tuple("bar"s, std::optional("foo"s), "bar"s)));
}
{
auto result = bp::parse(
"bar42", bp::string("bar") >> (bp::string("foo") | bp::int_));
EXPECT_TRUE(result);
EXPECT_EQ(
*result, (make::tuple("bar"s, std::variant<std::string, int>(42))));
}
{
auto result = bp::parse(
"bar",
bp::string("bar") >> -(bp::string("foo") | bp::string("bar")));
EXPECT_TRUE(result);
EXPECT_EQ(*result, (make::tuple("bar"s, std::optional<std::string>{})));
}
{
auto result = bp::parse(
"bar",
bp::string("bar") >> (-bp::string("foo") | bp::string("bar")));
EXPECT_TRUE(result);
EXPECT_EQ(
*result,
make::tuple(
"bar"s,
std::variant<std::optional<std::string>, std::string>(
std::nullopt)));
}
// P2
{
auto result = bp::parse("42foo", bp::int_ >> bp::string("foo"));
EXPECT_TRUE(result);
EXPECT_EQ(*result, (make::tuple(42, "foo"s)));
}
{
auto result = bp::parse("42bar", bp::int_ >> bp::string("bar"));
EXPECT_TRUE(result);
EXPECT_EQ(*result, (make::tuple(42, "bar"s)));
}
{
auto result = bp::parse("42 42", bp::int_ >> bp::int_, bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(*result, (make::tuple(42, 42)));
}
{
auto result = bp::parse("42c", bp::int_ >> bp::char_('c'));
EXPECT_TRUE(result);
EXPECT_EQ(*result, (make::tuple(42, 'c')));
}
{
auto result = bp::parse("42", bp::int_ >> bp::eps);
EXPECT_TRUE(result);
EXPECT_EQ(*result, 42);
}
{
auto result = bp::parse("42foofoo", bp::int_ >> *bp::string("foo"));
EXPECT_TRUE(result);
EXPECT_EQ(*result, (make::tuple(42, std::vector({"foo"s, "foo"s}))));
}
{
auto result = bp::parse("42foo", bp::int_ >> -bp::string("foo"));
EXPECT_TRUE(result);
EXPECT_EQ(*result, (make::tuple(42, std::optional("foo"s))));
}
{
auto result =
bp::parse("42foo42", bp::int_ >> (bp::string("foo") >> bp::int_));
EXPECT_TRUE(result);
EXPECT_EQ(*result, (make::tuple(42, "foo"s, 42)));
}
{
auto result = bp::parse(
"42foobar", bp::int_ >> -(bp::string("foo") >> bp::string("bar")));
EXPECT_TRUE(result);
EXPECT_EQ(
*result,
(make::tuple(42, std::optional(make::tuple("foo"s, "bar"s)))));
}
{
auto result = bp::parse(
"42foobar", bp::int_ >> (-bp::string("foo") >> bp::string("bar")));
EXPECT_TRUE(result);
EXPECT_EQ(*result, (make::tuple(42, std::optional("foo"s), "bar"s)));
}
{
auto result = bp::parse(
"42 42", bp::int_ >> (bp::string("foo") | bp::int_), bp::ws);
EXPECT_TRUE(result);
EXPECT_EQ(
*result, (make::tuple(42, std::variant<std::string, int>(42))));
}
{
auto result = bp::parse(
"42", bp::int_ >> -(bp::string("foo") | bp::string("bar")));
EXPECT_TRUE(result);
EXPECT_EQ(*result, (make::tuple(42, std::optional<std::string>{})));
}
{
auto result = bp::parse(
"42", bp::int_ >> (-bp::string("foo") | bp::string("bar")));
EXPECT_TRUE(result);
EXPECT_EQ(
*result,
make::tuple(
42,
std::variant<std::optional<std::string>, std::string>(
std::nullopt)));
}
// P3
{
auto result = bp::parse("cfoo", bp::char_('c') >> bp::string("foo"));
EXPECT_TRUE(result);
EXPECT_EQ(*result, "cfoo"s);
}
{
auto result = bp::parse("cbar", bp::char_('c') >> bp::string("bar"));
EXPECT_TRUE(result);
EXPECT_EQ(*result, "cbar"s);
}
{
auto result = bp::parse("c42", bp::char_('c') >> bp::int_);
EXPECT_TRUE(result);
EXPECT_EQ(*result, (make::tuple('c', 42)));
}
{
auto result = bp::parse("cc", bp::char_('c') >> bp::char_('c'));
EXPECT_TRUE(result);
EXPECT_EQ(*result, "cc"s);
}
{
auto result = bp::parse("c", bp::char_('c') >> bp::eps);
EXPECT_TRUE(result);
EXPECT_EQ(*result, 'c');
}
{
auto result =
bp::parse("cfoofoo", bp::char_('c') >> *bp::string("foo"));
EXPECT_TRUE(result);
EXPECT_EQ(*result, (make::tuple('c', std::vector({"foo"s, "foo"s}))));
}
{
auto result = bp::parse("cfoo", bp::char_('c') >> -bp::string("foo"));
EXPECT_TRUE(result);
EXPECT_EQ(*result, (make::tuple('c', std::optional("foo"s))));
}
{
auto result = bp::parse(
"cfoo42", bp::char_('c') >> (bp::string("foo") >> bp::int_));
EXPECT_TRUE(result);
EXPECT_EQ(*result, (make::tuple("cfoo"s, 42)));
}
{
auto result = bp::parse(
"cfoobar",
bp::char_('c') >> -(bp::string("foo") >> bp::string("bar")));
EXPECT_TRUE(result);
EXPECT_EQ(
*result,
(make::tuple('c', std::optional(make::tuple("foo"s, "bar"s)))));
}
{
auto result = bp::parse(
"cfoobar",
bp::char_('c') >> (-bp::string("foo") >> bp::string("bar")));
EXPECT_TRUE(result);
EXPECT_EQ(*result, (make::tuple('c', std::optional("foo"s), "bar"s)));
}
{
auto result =
bp::parse("c42", bp::char_('c') >> (bp::string("foo") | bp::int_));
EXPECT_TRUE(result);
EXPECT_EQ(
*result, (make::tuple('c', std::variant<std::string, int>(42))));
}
{
auto result = bp::parse(
"c", bp::char_('c') >> -(bp::string("foo") | bp::string("bar")));
EXPECT_TRUE(result);
EXPECT_EQ(*result, (make::tuple('c', std::optional<std::string>{})));
}
{
auto result = bp::parse(
"c", bp::char_('c') >> (-bp::string("foo") | bp::string("bar")));
EXPECT_TRUE(result);
EXPECT_EQ(
*result,
(make::tuple(
'c',
std::variant<std::optional<std::string>, std::string>(
std::nullopt))));
}
// eps
{
auto result = bp::parse("foo", bp::eps >> bp::string("foo"));
EXPECT_TRUE(result);
EXPECT_EQ(*result, "foo"s);
}
{
auto result = bp::parse("bar", bp::eps >> bp::string("bar"));
EXPECT_TRUE(result);
EXPECT_EQ(*result, "bar"s);
}
{
auto result = bp::parse("42", bp::eps >> bp::int_);
EXPECT_TRUE(result);
EXPECT_EQ(*result, 42);
}
{
auto result = bp::parse("c", bp::eps >> bp::char_('c'));
EXPECT_TRUE(result);
EXPECT_EQ(*result, 'c');
}
{
auto result = bp::parse("", bp::eps >> bp::eps);
EXPECT_TRUE(result);
}
{
auto result = bp::parse("foofoo", bp::eps >> *bp::string("foo"));
EXPECT_TRUE(result);
EXPECT_EQ(*result, std::vector({"foo"s, "foo"s}));
}
{
auto result = bp::parse("foo", bp::eps >> -bp::string("foo"));
EXPECT_TRUE(result);
EXPECT_EQ(*result, std::optional("foo"s));
}
{
auto result =
bp::parse("foo42", bp::eps >> (bp::string("foo") >> bp::int_));
EXPECT_TRUE(result);
EXPECT_EQ(*result, (make::tuple("foo"s, 42)));
}
{
auto result = bp::parse(
"foobar", bp::eps >> -(bp::string("foo") >> bp::string("bar")));
EXPECT_TRUE(result);
EXPECT_EQ(*result, std::optional((make::tuple("foo"s, "bar"s))));
}
{
auto result = bp::parse(
"foobar", bp::eps >> (-bp::string("foo") >> bp::string("bar")));
EXPECT_TRUE(result);
EXPECT_EQ(*result, (make::tuple(std::optional("foo"s), "bar"s)));
}
{
auto result =
bp::parse("42", bp::eps >> (bp::string("foo") | bp::int_));
EXPECT_TRUE(result);
EXPECT_EQ(*result, (std::variant<std::string, int>(42)));
}
{
auto result =
bp::parse("", bp::eps >> -(bp::string("foo") | bp::string("bar")));
EXPECT_TRUE(result);
EXPECT_EQ(*result, std::optional<std::string>{});
}
{
auto result =
bp::parse("", bp::eps >> (-bp::string("foo") | bp::string("bar")));
EXPECT_TRUE(result);
EXPECT_EQ(
*result,
(std::variant<std::optional<std::string>, std::string>(
std::nullopt)));
}
}

View File

@@ -1,127 +0,0 @@
/**
* 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

View File

@@ -258,7 +258,8 @@ TEST(replace, replace_unicode)
int count = 0;
std::string_view const strs[] = {"aa", "foo", "b"};
for (auto subrange : r) {
std::string str(subrange.begin(), subrange.end());
auto u8sub = subrange | bp::as_utf8;
std::string str(u8sub.begin(), u8sub.end());
EXPECT_EQ(str, strs[count]);
++count;
}
@@ -274,7 +275,8 @@ TEST(replace, replace_unicode)
int count = 0;
std::string_view const strs[] = {"aa", "foo", "baaba", "foo"};
for (auto subrange : r) {
std::string str(subrange.begin(), subrange.end());
auto u8sub = subrange | bp::as_utf8;
std::string str(u8sub.begin(), u8sub.end());
EXPECT_EQ(str, strs[count]);
++count;
}
@@ -301,7 +303,8 @@ TEST(replace, replace_unicode)
int count = 0;
std::string_view const strs[] = {"aa", "foo", "baaba", "foo"};
for (auto subrange : r) {
std::string str(subrange.begin(), subrange.end());
auto u8sub = subrange | bp::as_utf8;
std::string str(u8sub.begin(), u8sub.end());
EXPECT_EQ(str, strs[count]);
++count;
}
@@ -314,7 +317,8 @@ TEST(replace, replace_unicode)
int count = 0;
std::string_view const strs[] = {"aa", "foo", "baaba", "foo", "foo"};
for (auto subrange : r) {
std::string str(subrange.begin(), subrange.end());
auto u8sub = subrange | bp::as_utf8;
std::string str(u8sub.begin(), u8sub.end());
EXPECT_EQ(str, strs[count]);
++count;
}
@@ -342,7 +346,8 @@ TEST(replace, replace_unicode)
std::string_view const strs[] = {
"foo", "foo", "aa", "foo", "baaba", "foo", "foo"};
for (auto subrange : r) {
std::string str(subrange.begin(), subrange.end());
auto u8sub = subrange | bp::as_utf8;
std::string str(u8sub.begin(), u8sub.end());
EXPECT_EQ(str, strs[count]);
++count;
}
@@ -356,7 +361,8 @@ TEST(replace, replace_unicode)
std::string_view const strs[] = {
"foo", "foo", "aa", "foo", "baaba", "foo", "foo"};
for (auto subrange : r) {
std::string str(subrange.begin(), subrange.end());
auto u8sub = subrange | bp::as_utf8;
std::string str(u8sub.begin(), u8sub.end());
EXPECT_EQ(str, strs[count]);
++count;
}
@@ -370,7 +376,8 @@ TEST(replace, replace_unicode)
std::string_view const strs[] = {
"foo", "foo", "aa", "foo", "baaba", "foo", "foo"};
for (auto subrange : r) {
std::string str(subrange.begin(), subrange.end());
auto u8sub = subrange | bp::as_utf8;
std::string str(u8sub.begin(), u8sub.end());
EXPECT_EQ(str, strs[count]);
++count;
}
@@ -391,7 +398,7 @@ TEST(replace, join_compat)
std::string replace_result;
for (auto ch : rng) {
static_assert(std::is_same_v<decltype(ch), char32_t>);
replace_result.push_back(ch);
replace_result.push_back((char)ch);
}
EXPECT_EQ(replace_result, "foofooaafoobaabafoofoo");
}
@@ -463,8 +470,8 @@ TEST(replace, doc_examples)
replace_result.push_back(ch);
}
assert(replace_result == "My credit card number is XXXX-XXXX-XXXX-XXXX.");
#endif
}
#endif
// clang-format on
}

View File

@@ -739,6 +739,7 @@ TEST(search, doc_examples)
assert(
std::string_view(result.begin(), result.end() - result.begin()) ==
"XYZ");
(void)result;
}
{
auto r = "XYZaaXYZbaabaXYZXYZ" | bp::search_all(bp::lit("XYZ"));

View File

@@ -103,6 +103,13 @@ int main()
PARSE(char_ | int_ | float_);
std::cout << "\n\n"
<< "----------------------------------------\n"
<< "| operator|| |\n"
<< "----------------------------------------\n";
PARSE(char_ || int_ || float_);
std::cout << "\n\n"
<< "----------------------------------------\n"
<< "| operator- (binary) |\n"
@@ -131,6 +138,14 @@ int main()
PARSE(char_[a]);
std::cout << "\n\n"
<< "----------------------------------------\n"
<< "| transform)f_[] |\n"
<< "----------------------------------------\n";
auto f = [](auto x) { return x; };
PARSE(transform(f)[char_]);
std::cout << "\n\n"
<< "----------------------------------------\n"
<< "| omit[] |\n"
@@ -297,15 +312,6 @@ 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"

View File

@@ -14,7 +14,8 @@
#include <list>
#if (!defined(_MSC_VER) || BOOST_PARSER_USE_CONCEPTS)
#if (!defined(_MSC_VER) || BOOST_PARSER_USE_CONCEPTS) && \
(!defined(__GNUC__) || 12 <= __GNUC__ || !BOOST_PARSER_USE_CONCEPTS)
namespace bp = boost::parser;
@@ -680,7 +681,8 @@ TEST(transform_replace, transform_replace_unicode)
int count = 0;
std::string replace_result;
for (auto subrange : r) {
std::string str(subrange.begin(), subrange.end());
auto u8sub = subrange | bp::as_utf8;
std::string str(u8sub.begin(), u8sub.end());
replace_result += str;
++count;
}
@@ -695,7 +697,8 @@ TEST(transform_replace, transform_replace_unicode)
int count = 0;
std::string replace_result;
for (auto subrange : r) {
std::string str(subrange.begin(), subrange.end());
auto u8sub = subrange | bp::as_utf8;
std::string str(u8sub.begin(), u8sub.end());
replace_result += str;
++count;
}
@@ -724,7 +727,8 @@ TEST(transform_replace, transform_replace_unicode)
int count = 0;
std::string replace_result;
for (auto subrange : r) {
std::string str(subrange.begin(), subrange.end());
auto u8sub = subrange | bp::as_utf8;
std::string str(u8sub.begin(), u8sub.end());
replace_result += str;
++count;
}
@@ -738,7 +742,8 @@ TEST(transform_replace, transform_replace_unicode)
int count = 0;
std::string replace_result;
for (auto subrange : r) {
std::string str(subrange.begin(), subrange.end());
auto u8sub = subrange | bp::as_utf8;
std::string str(u8sub.begin(), u8sub.end());
replace_result += str;
++count;
}
@@ -774,7 +779,7 @@ TEST(transform_replace, join_compat)
std::string transform_replace_result;
for (auto ch : rng) {
static_assert(std::is_same_v<decltype(ch), char16_t>);
transform_replace_result.push_back(ch);
transform_replace_result.push_back((char)ch);
}
EXPECT_EQ(transform_replace_result, "1_aa88_99_baaba111_2222_ 3_4_");
}
@@ -786,7 +791,7 @@ TEST(transform_replace, join_compat)
std::string transform_replace_result;
for (auto ch : rng) {
static_assert(std::is_same_v<decltype(ch), char32_t>);
transform_replace_result.push_back(ch);
transform_replace_result.push_back((char)ch);
}
EXPECT_EQ(transform_replace_result, "1_aa88_99_baaba111_2222_ 3_4_");
}