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

12 Commits

Author SHA1 Message Date
Zach Laine
6414f99e04 Remove space from declaration of UDLs, because apparently it matters?!
Fixes #216.
2025-03-13 19:03:14 -05:00
Zach Laine
78bc141d5f Add doc example of unexpected combining sequence parsers.
Example is based on #215.
2025-03-01 16:34:22 -06:00
necessarily-equal
b253d9ca53 Add unicode symbols parser (#213)
* Add symb parser to handle unicode symbols

* Add documentation for symb

* Add tests for symb

* Fix typo in the documentation

---------

Contributed by: Antoine Fontaine <antoinefontaine@posteo.net>
2025-02-20 23:51:17 -06:00
Zach Laine
0a34acc42a Add new macro BOOST_PARSER_GCC that is defined in config.hpp only for real
(non-Clang-emulated) GCC builds; replace relevant uses of the __GNUC__ macro
with BOOST_PARSER_GCC.

See discussion in PR #211.
2025-02-20 23:38:09 -06:00
Zach Laine
56c81c0b57 Use gross pointer dereference expression to implement detail::whatever
converions operator, since 2/3 of the big three compilers reject the use of
declval() there.
2025-02-20 23:38:09 -06:00
Zach Laine
821d1d4c08 Fix longstanding mysterious ill-formedness in
detail::static_assert_merge_attributes.  It was down to unavailabilty of a
default ctor for certain parser types.
2025-02-20 23:38:09 -06:00
Zach Laine
57cdd78210 Properly sort the code point values in detail::char_set<punct_chars>.
Fixes #209.
2024-12-23 17:28:07 -06:00
Zach Laine
3993efb692 Update MacOS badges in README.md. 2024-12-20 20:02:44 -06:00
Zach Laine
74bc8fc1bb Add the delimiter(p)[] directive proper (missing from previous commit).
Fixes #162.
2024-12-20 20:01:16 -06:00
Zach Laine
b42b052df4 Add the delimiter(p)[] directive, whic allows you to introduce a delimiter
into the parse of a permutation parser.

Fixes #162.
2024-12-19 22:26:08 -06:00
Zach Laine
42c9d82419 Add an optional char parser to quoted_string_parser, so that it can be made
fully general.

Fixes #196.
2024-12-17 00:17:54 -06:00
Zach Laine
958ac38256 Note for the user that they must be aware of nonobvious C++ operator
precedence impact of expression evaluation.

Fixes #205.
2024-12-16 22:33:55 -06:00
19 changed files with 3933 additions and 193 deletions

View File

@@ -57,7 +57,7 @@ Master status:
[![Windows MSVC](https://github.com/tzlaine/parser/actions/workflows/windows.yml/badge.svg?branch=master)](https://github.com/tzlaine/parser/actions/workflows/windows.yml)
[![macos-12 - Clang 14](https://github.com/tzlaine/parser/actions/workflows/macos-12.yml/badge.svg?branch=master)](https://github.com/tzlaine/parser/actions/workflows/macos-12.yml)
[![macos-13 - Clang 14](https://github.com/tzlaine/parser/actions/workflows/macos-13.yml/badge.svg?branch=master)](https://github.com/tzlaine/parser/actions/workflows/macos-13.yml)
Develop status:
@@ -67,6 +67,6 @@ Develop status:
[![Windows MSVC](https://github.com/tzlaine/parser/actions/workflows/windows.yml/badge.svg?branch=develop)](https://github.com/tzlaine/parser/actions/workflows/windows.yml)
[![macos-12 - Clang 14](https://github.com/tzlaine/parser/actions/workflows/macos-12.yml/badge.svg?branch=develop)](https://github.com/tzlaine/parser/actions/workflows/macos-12.yml)
[![macos-13 - Clang 14](https://github.com/tzlaine/parser/actions/workflows/macos-13.yml/badge.svg?branch=develop)](https://github.com/tzlaine/parser/actions/workflows/macos-13.yml)
[![License](https://img.shields.io/badge/license-boost-brightgreen.svg)](LICENSE_1_0.txt)

View File

@@ -110,6 +110,7 @@
[def _std_str_ `std::string`]
[def _std_strs_ `std::string`s]
[def _std_vec_char_ `std::vector<char>`]
[def _std_vec_char32_ `std::vector<char32_t>`]
@@ -201,6 +202,7 @@
[def _merge_ [globalref boost::parser::merge `merge[]`]]
[def _sep_ [globalref boost::parser::separate `separate[]`]]
[def _transform_ [globalref boost::parser::transform `transform(f)[]`]]
[def _delimiter_ [globalref boost::parser::delimiter `delimiter(p)[]`]]
[def _omit_np_ [globalref boost::parser::omit `omit`]]
[def _raw_np_ [globalref boost::parser::raw `raw`]]
@@ -211,11 +213,13 @@
[def _merge_np_ [globalref boost::parser::merge `merge`]]
[def _sep_np_ [globalref boost::parser::separate `separate`]]
[def _transform_np_ [globalref boost::parser::transform `transform`]]
[def _delimiter_np_ [globalref boost::parser::delimiter `delimiter`]]
[def _blank_ [globalref boost::parser::blank `blank`]]
[def _control_ [globalref boost::parser::control `control`]]
[def _digit_ [globalref boost::parser::digit `digit`]]
[def _punct_ [globalref boost::parser::punct `punct`]]
[def _symb_ [globalref boost::parser::symb `symb`]]
[def _hex_digit_ [globalref boost::parser::hex_digit `hex_digit`]]
[def _lower_ [globalref boost::parser::lower `lower`]]
[def _upper_ [globalref boost::parser::upper `upper`]]
@@ -239,6 +243,7 @@
[def _more_about_rules_ [link boost_parser.tutorial.more_about_rules More About Rules]]
[def _unicode_ [link boost_parser.tutorial.unicode_support Unicode Support]]
[def _concepts_ [link boost_parser.concepts Concepts]]
[def _seq_parser_example_ [link boost_parser.tutorial.attribute_generation.a_sequence_parser_attribute_example A sequence parser attribute example]]
[def _ex_json_ [link boost_parser.extended_examples.parsing_json Parsing JSON]]
[def _ex_cb_json_ [link boost_parser.extended_examples.parsing_json_with_callbacks Parsing JSON With Callbacks]]
[def _rationale_ [link boost_parser.rationale Rationale]]

2145
doc/parser_reference.xml Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -132,6 +132,11 @@ the input they match unless otherwise stated in the table below.]
[ The code point type in Unicode parsing, or `char` in non-Unicode parsing. See the entry for _ch_. ]
[]]
[[ `_symb_` ]
[ Matches a single symbol code point. ]
[ The code point type in Unicode parsing, or `char` in non-Unicode parsing. See the entry for _ch_. ]
[]]
[[ `_hex_digit_` ]
[ Matches a single hexidecimal digit code point. ]
[ The code point type in Unicode parsing, or `char` in non-Unicode parsing. See the entry for _ch_. ]
@@ -385,10 +390,10 @@ consume the input they match unless otherwise stated in the table below.]
[[`*p`] [ Parses using `p` repeatedly until `p` no longer matches; always matches. ] [`std::string` if `_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p)>`] [ Matching _e_ an unlimited number of times creates an infinite loop, which is undefined behavior in C++. _Parser_ will assert in debug mode when it encounters `*_e_` (this applies to unconditional _e_ only). ]]
[[`+p`] [ Parses using `p` repeatedly until `p` no longer matches; matches iff `p` matches at least once. ] [`std::string` if `_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p)>`] [ Matching _e_ an unlimited number of times creates an infinite loop, which is undefined behavior in C++. _Parser_ will assert in debug mode when it encounters `+_e_` (this applies to unconditional _e_ only). ]]
[[`-p`] [ Equivalent to `p | _e_`. ] [`std::optional<_ATTR_np_(p)>`] []]
[[`p1 >> p2`] [ Matches iff `p1` matches and then `p2` matches. ] [`_bp_tup_<_ATTR_np_(p1), _ATTR_np_(p2)>` (See note.)] [ `>>` is associative; `p1 >> p2 >> p3`, `(p1 >> p2) >> p3`, and `p1 >> (p2 >> p3)` are all equivalent. This attribute type only applies to the case where `p1` and `p2` both generate attributes; see _attr_gen_ for the full rules. ]]
[[`p1 >> p2`] [ Matches iff `p1` matches and then `p2` matches. ] [`_bp_tup_<_ATTR_np_(p1), _ATTR_np_(p2)>` (See note.)] [ `>>` is associative; `p1 >> p2 >> p3`, `(p1 >> p2) >> p3`, and `p1 >> (p2 >> p3)` are all equivalent. This attribute type only applies to the case where `p1` and `p2` both generate attributes; see _attr_gen_ for the full rules. Differs in precedence from `operator>`. ]]
[[`p >> c`] [ Equivalent to `p >> lit(c)`. ] [`_ATTR_np_(p)`] []]
[[`p >> r`] [ Equivalent to `p >> lit(r)`. ] [`_ATTR_np_(p)`] []]
[[`p1 > p2`] [ Matches iff `p1` matches and then `p2` matches. No back-tracking is allowed after `p1` matches; if `p1` matches but then `p2` does not, the top-level parse fails. ] [`_bp_tup_<_ATTR_np_(p1), _ATTR_np_(p2)>` (See note.)] [ `>` is associative; `p1 > p2 > p3`, `(p1 > p2) > p3`, and `p1 > (p2 > p3)` are all equivalent. This attribute type only applies to the case where `p1` and `p2` both generate attributes; see _attr_gen_ for the full rules. ]]
[[`p1 > p2`] [ Matches iff `p1` matches and then `p2` matches. No back-tracking is allowed after `p1` matches; if `p1` matches but then `p2` does not, the top-level parse fails. ] [`_bp_tup_<_ATTR_np_(p1), _ATTR_np_(p2)>` (See note.)] [ `>` is associative; `p1 > p2 > p3`, `(p1 > p2) > p3`, and `p1 > (p2 > p3)` are all equivalent. This attribute type only applies to the case where `p1` and `p2` both generate attributes; see _attr_gen_ for the full rules. Differs in precedence from `operator>>`. ]]
[[`p > c`] [ Equivalent to `p > lit(c)`. ] [`_ATTR_np_(p)`] []]
[[`p > r`] [ Equivalent to `p > lit(r)`. ] [`_ATTR_np_(p)`] []]
[[`p1 | p2`] [ Matches iff either `p1` matches or `p2` matches. ] [`std::variant<_ATTR_np_(p1), _ATTR_np_(p2)>` (See note.)] [ `|` is associative; `p1 | p2 | p3`, `(p1 | p2) | p3`, and `p1 | (p2 | p3)` are all equivalent. This attribute type only applies to the case where `p1` and `p2` both generate attributes, and where the attribute types are different; see _attr_gen_ for the full rules. ]]
@@ -424,6 +429,15 @@ because, for any parser `p`, `_e_ | p` is equivalent to _e_, since _e_ always
matches. This is not true for _e_ parameterized with a condition. For any
condition `cond`, `_e_(cond)` is allowed to appear anywhere within an
alternative parser.
[important The C++ operators `>` and `>>` have different precedences. This
will sometimes come up in warnings from your compiler. No matter how you do
or do not parenthesize chains of parsers separated by `>` and `>>`, the
resulting expression evaluates the same. Feel free to add parentheses if your
compiler complains. More broadly, keep the C++ operator precedence rules in
mind when writing your parsers _emdash_ the simplest thing to write may not
have your intended semantics. ]
]
[template table_attribute_generation

View File

@@ -65,7 +65,7 @@ subparsers does.
Finally, there is a /permutation parser/; it is created using `operator||`,
as in `p1 || p2 || p3`. A permutation parser tries to match all of its
subparsers to the input, in any order. So the parser `p1 || p2 || p3` is equivalent to `(p1 >> p2 >> p3) | (p1 >> p3 >> p2) | (p2 >> p1 >> p3) | (p2 >> p3 >> p1) | (p3 >> p1 >> p2) | (p3 >> p2 >> p1)`. Hopefully its terseness is self-explanatory. It matches the
subparsers to the input, in any order. So the parser `p1 || p2 || p3` is equivalent to `(p1 >> p2 >> p3) | (p1 >> p3 >> p2) | (p2 >> p1 >> p3) | (p2 >> p3 >> p1) | (p3 >> p1 >> p2) | (p3 >> p2 >> p1)`. Hopefully the advantage of its terseness is self-explanatory. It matches the
input iff all of its subparsers do, regardless of the order they match in.
_Parser_ parsers each have an /attribute/ associated with them, or explicitly
@@ -836,6 +836,14 @@ escaped quote character, since those always work.
[quoted_string_example_5]
Additionally, with each of the forms shown above, you can optionally provide a
parser as a final argument, to will be used to parse each character inside the
quotes. You have to provide an actual full parser here; you cannot provide a
character or string literal. If you do not provide a character parser, _ch_
is used.
[quoted_string_example_6]
[endsect]
[section Parsing In Detail]
@@ -1442,6 +1450,24 @@ _merge_ and _sep_ create a copy of the given _seq_p_.
_transform_ creates a _xfm_p_.
[heading _delimiter_]
The _delimiter_np_ directive enables the use of a delimiter within a
permutation parser. It *only* applies to permutation parsers, just as _merge_
and _sep_ only apply to sequence parsers. Consider this permutation parser.
constexpr auto parser = bp::int_ || bp::string("foo") || bp::char_('g');
This will match all of: an integer, `"foo"`, and `'g'`, in any order (for
example, `"foo g 42"`). If you also want for those three elements to be
delimited by commas, you could write this parser instead.
constexpr auto delimited_parser =
bp::delimiter(bp::lit(','))[bp::int_ || bp::string("foo") || bp::char_('g')];
`delimited_parser` will parse the same elements as `parser`, but will also
require commas between the elements (as in `"foo, g, 42"`).
[endsect]
[section Combining Operations]
@@ -1590,6 +1616,71 @@ attribute becomes `T`.
[container_concept]
]
[heading A sequence parser attribute example]
Note that the application of `OP` is done in the style of a left-fold, and
is therefore greedy. This can lead to some non-obvious results. For example,
consider this program. Thanks to Duncan Paterson for this very nice example!
#include <boost/parser/parser.hpp>
#include <print>
namespace bp = boost::parser;
int main() {
const auto id_set_action = [](auto &ctx) {
const auto& [left, right] = _attr(ctx);
std::println("{} = {}", left, right);
};
const auto id_parser = bp::char_('a', 'z') > *bp::char_('a', 'z');
const auto id_set = (id_parser >> '=' >> id_parser)[id_set_action];
bp::parse("left=right", id_set);
return 0;
}
Perhaps surprisingly, this program prints `leftr = ight`! Why is this? This
happens because `id_parser` seems to impose structure, but does not. `id_set`
is exactly equivalent to this (comments added to clarify which parts are which
below).
const auto id_set = (
/*A*/ bp::char_('a', 'z') > /*B*/ *bp::char_('a', 'z') >>
/*C*/ '=' >>
/*D*/ bp::char_('a', 'z') > /*E*/ *bp::char_('a', 'z')
)[id_set_action];
As _Parser_ applies `OP` to this sequence parser, the individual steps are:
`A` and `B` get merged into a single _std_str_; `C` is ignored, since it
produces no attribute; and `D` gets merged into the _std_str_ formed earlier
by `A` and `B`; finally, we have `E`. `E` does not combine with `D`, as `D`
was already consumed. `E` also does not combine with the _std_str_ we formed
from `A`, `B`, and `D`, since we don't combine adjacent containers. In the
end, we have a 2-tuple of _std_strs_, in which the first element contains all
the characters parsed by `A`, `B`, and `D`, and in which the second element
contains all the characters parsed by `E`.
That's clearly not what we wanted here, though. How do we get a top-level
parser that would print `left = right`? We use a _r_. The parser used inside
a _r_ can never combine with any parser(s) outside the _r_. Instances of a
rule are inherently separate from all parsers with which they are used,
whether those parsers are _rs_ or non-_r_ parsers. So, consider a _r_
equivalent to the previous `id_parser` above.
namespace bp = boost::parser;
bp::rule<struct id_parser_tag, std::string> id_parser = "identifier";
auto const id_parser_def = bp::char_('a', 'z') > *bp::char_('a', 'z');
BOOST_PARSER_DEFINE_RULES(id_parser);
Later, we can use it just as we used the previous non-rule version.
const auto id_set = (id_parser >> '=' >> id_parser)[id_set_action];
This produces the results you might expect, since only the `bp::char_('a',
'z') > *bp::char_('a', 'z')` parser inside the `id_parser` _r_ is ever
eligible for combining via `OP`.
[heading Alternative parser attribute rules]
The rules for alternative parsers are much simpler. For an alternative parer
@@ -2211,6 +2302,8 @@ common use cases for _rs_. Use a _r_ if you want to:
* fix the attribute type produced by a parser to something other than the
default;
* control the attributes generated by adjacent sequence parsers;
* create a parser that produces useful diagnostic text;
* create a recursive rule (more on this below);
@@ -2351,6 +2444,10 @@ action if:
The notion of "compatible" is defined in _p_api_.
[heading Controlling the attributes generated]
See the _seq_parser_example_ in the _attr_gen_ section for details.
[heading Creating a parser for better diagnostics]
Each _r_ has associated diagnostic text that _Parser_ can use for failures of

View File

@@ -73,6 +73,12 @@
#endif
// Follows logic in boost/config/detail/select_compiler_config.hpp.
#if defined(__clang__) && !defined(__ibmxl__) && !defined(__CODEGEARC__)
#elif defined(__GNUC__) && !defined(__ibmxl__)
#define BOOST_PARSER_GCC
#endif
#if defined(__cpp_lib_constexpr_algorithms)
# define BOOST_PARSER_ALGO_CONSTEXPR constexpr
#else

View File

@@ -73,10 +73,10 @@ namespace boost { namespace parser { namespace detail {
std::ostream & os,
int components = 0);
template<typename Context, typename ParserTuple>
template<typename Context, typename ParserTuple, typename DelimiterParser>
void print_parser(
Context const & context,
perm_parser<ParserTuple> const & parser,
perm_parser<ParserTuple, DelimiterParser> const & parser,
std::ostream & os,
int components = 0);
@@ -245,6 +245,13 @@ namespace boost { namespace parser { namespace detail {
std::ostream & os,
int components = 0);
template<typename Context>
void print_parser(
Context const & context,
char_set_parser<symb_chars> const & parser,
std::ostream & os,
int components = 0);
template<typename Context>
void print_parser(
Context const & context,
@@ -280,10 +287,14 @@ namespace boost { namespace parser { namespace detail {
std::ostream & os,
int components = 0);
template<typename Context, typename Quotes, typename Escapes>
template<
typename Context,
typename Quotes,
typename Escapes,
typename CharParser>
void print_parser(
Context const & context,
quoted_string_parser<Quotes, Escapes> const & parser,
quoted_string_parser<Quotes, Escapes, CharParser> const & parser,
std::ostream & os,
int components = 0);

View File

@@ -63,8 +63,9 @@ namespace boost { namespace parser { namespace detail {
struct n_aray_parser<or_parser<ParserTuple>> : std::true_type
{};
template<typename ParserTuple>
struct n_aray_parser<perm_parser<ParserTuple>> : std::true_type
template<typename ParserTuple, typename DelimiterParser>
struct n_aray_parser<perm_parser<ParserTuple, DelimiterParser>>
: std::true_type
{};
template<
@@ -206,15 +207,23 @@ namespace boost { namespace parser { namespace detail {
context, parser, os, components, " | ...", " | ");
}
template<typename Context, typename ParserTuple>
template<typename Context, typename ParserTuple, typename DelimiterParser>
void print_parser(
Context const & context,
perm_parser<ParserTuple> const & parser,
perm_parser<ParserTuple, DelimiterParser> const & parser,
std::ostream & os,
int components)
{
if constexpr (!is_nope_v<DelimiterParser>) {
os << "delimiter(";
detail::print_parser(
context, parser.delimiter_parser_, os, components);
os << ")[";
}
detail::print_or_like_parser(
context, parser, os, components, " || ...", " || ");
if constexpr (!is_nope_v<DelimiterParser>)
os << "]";
}
template<
@@ -627,6 +636,16 @@ namespace boost { namespace parser { namespace detail {
os << "punct";
}
template<typename Context>
void print_parser(
Context const & context,
char_set_parser<symb_chars> const & parser,
std::ostream & os,
int components)
{
os << "symb";
}
template<typename Context>
void print_parser(
Context const & context,
@@ -695,10 +714,14 @@ namespace boost { namespace parser { namespace detail {
os << "\"";
}
template<typename Context, typename Quotes, typename Escapes>
template<
typename Context,
typename Quotes,
typename Escapes,
typename CharParser>
void print_parser(
Context const & context,
quoted_string_parser<Quotes, Escapes> const & parser,
quoted_string_parser<Quotes, Escapes, CharParser> const & parser,
std::ostream & os,
int components)
{

View File

@@ -24,8 +24,9 @@
#define BOOST_PARSER_USE_CPP23_STD_RANGE_ADAPTOR_CLOSURE 0
#endif
#if !BOOST_PARSER_USE_CPP23_STD_RANGE_ADAPTOR_CLOSURE && \
BOOST_PARSER_DETAIL_STL_INTERFACES_USE_CONCEPTS && defined(__GNUC__) && 12 <= __GNUC__
#if !BOOST_PARSER_USE_CPP23_STD_RANGE_ADAPTOR_CLOSURE && \
BOOST_PARSER_DETAIL_STL_INTERFACES_USE_CONCEPTS && \
defined(BOOST_PARSER_GCC) && 12 <= __GNUC__
#define BOOST_PARSER_USE_LIBSTDCPP_GCC12_RANGE_ADAPTOR_CLOSURE 1
#else
#define BOOST_PARSER_USE_LIBSTDCPP_GCC12_RANGE_ADAPTOR_CLOSURE 0

View File

@@ -42,7 +42,8 @@ namespace boost::parser::detail::text::detail {
template<typename R>
constexpr bool view =
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS || \
(defined(__cpp_lib_concepts) && (!defined(__GNUC__) || 12 <= __GNUC__))
(defined(__cpp_lib_concepts) && \
(!defined(BOOST_PARSER_GCC) || 12 <= __GNUC__))
std::ranges::view<R>
#else
range_<R> && !container_<R> &&

File diff suppressed because it is too large Load Diff

View File

@@ -1015,8 +1015,8 @@ namespace boost { namespace parser {
template<typename T>
struct is_perm_p : std::false_type
{};
template<typename T>
struct is_perm_p<perm_parser<T>> : std::true_type
template<typename T, typename DelimiterParser>
struct is_perm_p<perm_parser<T, DelimiterParser>> : std::true_type
{};
template<typename T>
@@ -3413,10 +3413,14 @@ namespace boost { namespace parser {
ParserTuple parsers_;
};
template<typename ParserTuple>
template<typename ParserTuple, typename DelimiterParser>
struct perm_parser
{
constexpr perm_parser(ParserTuple parsers) : parsers_(parsers) {}
constexpr perm_parser(
ParserTuple parsers, DelimiterParser delimiter_parser) :
parsers_(parsers), delimiter_parser_(delimiter_parser)
{}
#ifndef BOOST_PARSER_DOXYGEN
@@ -3643,7 +3647,19 @@ namespace boost { namespace parser {
};
// Use one of the previously-unused parsers to parse one
// alternative.
bool first_iteration = true;
auto parsed_one = [&](auto) {
if constexpr (!detail::is_nope_v<DelimiterParser>) {
if (!first_iteration) {
detail::skip(first, last, skip, flags);
bool local_success = true;
delimiter_parser_.call(
first, last, context, skip, flags, local_success);
if (!local_success)
return false;
}
first_iteration = false;
}
return (
parse_into(
Is,
@@ -3667,6 +3683,7 @@ namespace boost { namespace parser {
#endif
ParserTuple parsers_;
DelimiterParser delimiter_parser_;
};
namespace detail {
@@ -5605,7 +5622,7 @@ namespace boost { namespace parser {
return rhs.parser_.prepend(*this);
} else {
return parser::parser_interface{
perm_parser<tuple<parser_type, ParserType2>>{
perm_parser<tuple<parser_type, ParserType2>, detail::nope>{
tuple<parser_type, ParserType2>{parser_, rhs.parser_}}};
}
}
@@ -6120,23 +6137,23 @@ namespace boost { namespace parser {
}
}
template<typename ParserTuple>
template<typename ParserTuple, typename DelimiterParser>
template<typename Parser>
constexpr auto perm_parser<ParserTuple>::prepend(
constexpr auto perm_parser<ParserTuple, DelimiterParser>::prepend(
parser_interface<Parser> parser) const noexcept
{
// If you're seeing this as a compile- or run-time failure, you've
// tried to put an eps parser in a permutation-parser, such as "eps ||
// int_".
BOOST_PARSER_ASSERT(!detail::is_eps_p<Parser>{});
return parser_interface{perm_parser<decltype(detail::hl::prepend(
parsers_, parser.parser_))>{
detail::hl::prepend(parsers_, parser.parser_)}};
return parser_interface{perm_parser<
decltype(detail::hl::prepend(parsers_, parser.parser_)),
detail::nope>{detail::hl::prepend(parsers_, parser.parser_)}};
}
template<typename ParserTuple>
template<typename ParserTuple, typename DelimiterParser>
template<typename Parser>
constexpr auto perm_parser<ParserTuple>::append(
constexpr auto perm_parser<ParserTuple, DelimiterParser>::append(
parser_interface<Parser> parser) const noexcept
{
// If you're seeing this as a compile- or run-time failure, you've
@@ -6144,13 +6161,14 @@ namespace boost { namespace parser {
// || eps".
BOOST_PARSER_ASSERT(!detail::is_eps_p<Parser>{});
if constexpr (detail::is_perm_p<Parser>{}) {
return parser_interface{perm_parser<decltype(detail::hl::concat(
parsers_, parser.parser_.parsers_))>{
return parser_interface{perm_parser<
decltype(detail::hl::concat(parsers_, parser.parser_.parsers_)),
detail::nope>{
detail::hl::concat(parsers_, parser.parser_.parsers_)}};
} else {
return parser_interface{perm_parser<decltype(detail::hl::append(
parsers_, parser.parser_))>{
detail::hl::append(parsers_, parser.parser_)}};
return parser_interface{perm_parser<
decltype(detail::hl::append(parsers_, parser.parser_)),
detail::nope>{detail::hl::append(parsers_, parser.parser_)}};
}
}
@@ -6305,6 +6323,36 @@ namespace boost { namespace parser {
return repeat_directive<MinType, MaxType>{min_, max_};
}
/** A directive that represents a `perm_parser`, where the items parsed
are delimited by `DelimiterParser`,
(e.g. `delimiter(delimter_parser)[some_perm_parser]`). This directive
only applies to `perm_parser`s. */
template<typename DelimiterParser>
struct delimiter_directive
{
template<typename ParserTuple, typename DelimiterParser2>
constexpr auto operator[](
parser_interface<perm_parser<ParserTuple, DelimiterParser2>> rhs)
const noexcept
{
using parser_type = perm_parser<ParserTuple, DelimiterParser>;
return parser_interface{
parser_type{rhs.parser_.parsers_, delimiter_parser_}};
}
DelimiterParser delimiter_parser_;
};
/** Returns a `delimiter_directive` whose `operator[]` returns a
`perm_parser`, where the items parsed are delimited by
`delimiter_parser`. */
template<typename DelimiterParser>
constexpr delimiter_directive<DelimiterParser>
delimiter(parser_interface<DelimiterParser> delimiter_parser) noexcept
{
return delimiter_directive<DelimiterParser>{delimiter_parser.parser_};
}
/** Represents a skip parser as a directive. When used without a skip
parser, e.g. `skip[parser_in_which_to_do_skipping]`, the skipper for
the entire parse is used. When given another parser, e.g.
@@ -7252,7 +7300,7 @@ namespace boost { namespace parser {
return parser_interface{string_parser(str)};
}
template<typename Quotes, typename Escapes>
template<typename Quotes, typename Escapes, typename CharParser>
struct quoted_string_parser
{
constexpr quoted_string_parser() : chs_(), ch_('"') {}
@@ -7265,7 +7313,11 @@ namespace boost { namespace parser {
typename Enable =
std::enable_if_t<detail::is_parsable_range_like_v<R>>>
#endif
constexpr quoted_string_parser(R && r) : chs_((R &&) r), ch_(0)
constexpr quoted_string_parser(
R && r,
parser_interface<CharParser> char_p =
parser_interface{CharParser()}) :
chs_((R &&)r), char_p_(char_p), ch_(0)
{
BOOST_PARSER_DEBUG_ASSERT(r.begin() != r.end());
}
@@ -7278,16 +7330,29 @@ namespace boost { namespace parser {
typename Enable =
std::enable_if_t<detail::is_parsable_range_like_v<R>>>
#endif
constexpr quoted_string_parser(R && r, Escapes escapes) :
chs_((R &&) r), escapes_(escapes), ch_(0)
constexpr quoted_string_parser(
R && r,
Escapes escapes,
parser_interface<CharParser> char_p =
parser_interface{CharParser()}) :
chs_((R &&)r), escapes_(escapes), char_p_(char_p), ch_(0)
{
BOOST_PARSER_DEBUG_ASSERT(r.begin() != r.end());
}
constexpr quoted_string_parser(char32_t cp) : chs_(), ch_(cp) {}
constexpr quoted_string_parser(
char32_t cp,
parser_interface<CharParser> char_p =
parser_interface{CharParser()}) :
chs_(), char_p_(char_p), ch_(cp)
{}
constexpr quoted_string_parser(char32_t cp, Escapes escapes) :
chs_(), escapes_(escapes), ch_(cp)
constexpr quoted_string_parser(
char32_t cp,
Escapes escapes,
parser_interface<CharParser> char_p =
parser_interface{CharParser()}) :
chs_(), escapes_(escapes), char_p_(char_p), ch_(cp)
{}
template<
@@ -7376,11 +7441,11 @@ namespace boost { namespace parser {
auto make_parser = [&]() {
if constexpr (detail::is_nope_v<Escapes>) {
return *((lit('\\') >> back_delim) |
(char_ - back_delim))[append] > ch;
(char_p_ - back_delim))[append] > ch;
} else {
return *((lit('\\') >> back_delim)[append] |
(lit('\\') >> parser_interface(escapes_))[append] |
(char_ - back_delim)[append]) > ch;
(char_p_ - back_delim)[append]) > ch;
}
};
@@ -7402,15 +7467,17 @@ namespace boost { namespace parser {
/** Returns a `parser_interface` containing a `quoted_string_parser`
that uses `x` as its quotation marks. */
#if BOOST_PARSER_USE_CONCEPTS
template<typename T>
template<typename T, typename Parser = char_parser<detail::nope>>
requires(!parsable_range_like<T>)
#else
template<
typename T,
typename Parser = char_parser<detail::nope>,
typename Enable =
std::enable_if_t<!detail::is_parsable_range_like_v<T>>>
#endif
constexpr auto operator()(T x) const noexcept
constexpr auto
operator()(T x, parser_interface<Parser> char_p = char_) const noexcept
{
if constexpr (!detail::is_nope_v<Quotes>) {
BOOST_PARSER_ASSERT(
@@ -7419,7 +7486,9 @@ namespace boost { namespace parser {
"quoted_string, like 'quoted_string('\"')('\\'')'. Quit "
"it!'"));
}
return parser_interface(quoted_string_parser(std::move(x)));
return parser_interface(
quoted_string_parser<detail::nope, detail::nope, Parser>(
std::move(x), char_p));
}
/** Returns a `parser_interface` containing a `quoted_string_parser`
@@ -7430,14 +7499,18 @@ namespace boost { namespace parser {
character begin matched is directly compared to the elements of
`r`. */
#if BOOST_PARSER_USE_CONCEPTS
template<parsable_range_like R>
template<
parsable_range_like R,
typename Parser = char_parser<detail::nope>>
#else
template<
typename R,
typename Parser = char_parser<detail::nope>,
typename Enable =
std::enable_if_t<detail::is_parsable_range_like_v<R>>>
#endif
constexpr auto operator()(R && r) const noexcept
constexpr auto operator()(
R && r, parser_interface<Parser> char_p = char_) const noexcept
{
BOOST_PARSER_ASSERT(((
!std::is_rvalue_reference_v<R &&> ||
@@ -7453,10 +7526,14 @@ namespace boost { namespace parser {
"'quoted_string(char-range)(char-range)'. Quit it!'"));
}
return parser_interface(
quoted_string_parser<decltype(BOOST_PARSER_SUBRANGE(
detail::make_view_begin(r), detail::make_view_end(r)))>(
quoted_string_parser<
decltype(BOOST_PARSER_SUBRANGE(
detail::make_view_begin(r), detail::make_view_end(r))),
detail::nope,
Parser>(
BOOST_PARSER_SUBRANGE(
detail::make_view_begin(r), detail::make_view_end(r))));
detail::make_view_begin(r), detail::make_view_end(r)),
char_p));
}
/** Returns a `parser_interface` containing a `quoted_string_parser`
@@ -7465,16 +7542,23 @@ namespace boost { namespace parser {
sequence, and what character(s) each escape sequence represents.
Note that `"\\"` and `"\ch"` are always valid escape sequences. */
#if BOOST_PARSER_USE_CONCEPTS
template<typename T, typename U>
template<
typename T,
typename U,
typename Parser = char_parser<detail::nope>>
requires(!parsable_range_like<T>)
#else
template<
typename T,
typename U,
typename Parser = char_parser<detail::nope>,
typename Enable =
std::enable_if_t<!detail::is_parsable_range_like_v<T>>>
#endif
auto operator()(T x, symbols<U> const & escapes) const noexcept
auto operator()(
T x,
symbols<U> const & escapes,
parser_interface<Parser> char_p = char_) const noexcept
{
if constexpr (!detail::is_nope_v<Quotes>) {
BOOST_PARSER_ASSERT(
@@ -7484,8 +7568,9 @@ namespace boost { namespace parser {
"it!'"));
}
auto symbols = symbol_parser(escapes.parser_);
auto parser = quoted_string_parser<detail::nope, decltype(symbols)>(
char32_t(x), symbols);
auto parser =
quoted_string_parser<detail::nope, decltype(symbols), Parser>(
char32_t(x), symbols, char_p);
return parser_interface(parser);
}
@@ -7500,15 +7585,22 @@ namespace boost { namespace parser {
escape sequence represents. Note that `"\\"` and `"\ch"` are
always valid escape sequences. */
#if BOOST_PARSER_USE_CONCEPTS
template<parsable_range_like R, typename T>
template<
parsable_range_like R,
typename T,
typename Parser = char_parser<detail::nope>>
#else
template<
typename R,
typename T,
typename Parser = char_parser<detail::nope>,
typename Enable =
std::enable_if_t<detail::is_parsable_range_like_v<R>>>
#endif
auto operator()(R && r, symbols<T> const & escapes) const noexcept
auto operator()(
R && r,
symbols<T> const & escapes,
parser_interface<Parser> char_p = char_) const noexcept
{
BOOST_PARSER_ASSERT(((
!std::is_rvalue_reference_v<R &&> ||
@@ -7526,14 +7618,16 @@ namespace boost { namespace parser {
auto symbols = symbol_parser(escapes.parser_);
auto quotes = BOOST_PARSER_SUBRANGE(
detail::make_view_begin(r), detail::make_view_end(r));
auto parser =
quoted_string_parser<decltype(quotes), decltype(symbols)>(
quotes, symbols);
auto parser = quoted_string_parser<
decltype(quotes),
decltype(symbols),
Parser>(quotes, symbols, char_p);
return parser_interface(parser);
}
Quotes chs_;
Escapes escapes_;
parser_interface<CharParser> char_p_;
char32_t ch_;
};
@@ -7717,12 +7811,18 @@ namespace boost { namespace parser {
control;
/** The punctuation character parser. Matches the full set of Unicode
punctuation clases (specifically, "Pc", "Pd", "Pe", "Pf", "Pi", "Ps",
punctuation classes (specifically, "Pc", "Pd", "Pe", "Pf", "Pi", "Ps",
and "Po"). */
inline BOOST_PARSER_ALGO_CONSTEXPR
parser_interface<char_set_parser<detail::punct_chars>>
punct;
/** The symbol character parser. Matches the full set of Unicode
symbol classes (specifically, "Sc", "Sk", "Sm", and "So"). */
inline BOOST_PARSER_ALGO_CONSTEXPR
parser_interface<char_set_parser<detail::symb_chars>>
symb;
/** The lower case character parser. Matches the full set of Unicode
lower case code points (class "Ll"). */
inline BOOST_PARSER_ALGO_CONSTEXPR
@@ -9443,9 +9543,6 @@ namespace boost { namespace parser {
template<typename... Args>
constexpr void static_assert_merge_attributes(tuple<Args...> parsers)
{
// This code chokes older GCCs. I can't figure out why, and this
// is an optional check, so I'm disabling it for those GCCs.
#if !defined(__GNUC__) || 13 <= __GNUC__
using context_t = parse_context<
false,
false,
@@ -9492,7 +9589,7 @@ namespace boost { namespace parser {
"type as one of the others.");
if constexpr (!std::is_same_v<t, first_t>) {
[[maybe_unused]] detail::print_type<tuple<Args...>>
tuple_types;
tuple_types(parsers);
[[maybe_unused]] detail::print_type<all_types>
attribute_types;
[[maybe_unused]] detail::print_type<first_t> first_type;
@@ -9500,7 +9597,6 @@ namespace boost { namespace parser {
}
}
});
#endif
}
}
}}

View File

@@ -143,6 +143,8 @@ namespace boost { namespace parser {
struct punct_chars
{};
struct symb_chars
{};
struct lower_case_chars
{};
struct upper_case_chars
@@ -207,7 +209,7 @@ namespace boost { namespace parser {
`ParserTuple`, not the order of the parsers' matches. It is an error
to specialize `perm_parser` with a `ParserTuple` template parameter
that includes an `eps_parser`. */
template<typename ParserTuple>
template<typename ParserTuple, typename DelimiterParser>
struct perm_parser;
/** Applies each parser in `ParserTuple`, in order. The parse succeeds
@@ -380,7 +382,10 @@ namespace boost { namespace parser {
/** Matches a string delimited by quotation marks; produces a
`std::string` attribute. */
template<typename Quotes = detail::nope, typename Escapes = detail::nope>
template<
typename Quotes = detail::nope,
typename Escapes = detail::nope,
typename CharParser = char_parser<detail::nope>>
struct quoted_string_parser;
/** Matches an end-of-line (`NewlinesOnly == true`), whitespace

View File

@@ -4,7 +4,8 @@
#include <boost/parser/replace.hpp>
#if (!defined(_MSC_VER) || BOOST_PARSER_USE_CONCEPTS) && \
(!defined(__GNUC__) || 12 <= __GNUC__ || !BOOST_PARSER_USE_CONCEPTS)
(!defined(BOOST_PARSER_GCC) || 12 <= __GNUC__ || \
!BOOST_PARSER_USE_CONCEPTS)
namespace boost::parser {

View File

@@ -128,7 +128,7 @@ namespace boost { namespace parser {
/** A literal that can be used to concisely name `parser::llong`
integral constants. */
template<char... chars>
constexpr auto operator"" _c()
constexpr auto operator""_c()
{
constexpr long long n =
detail::parse_llong<sizeof...(chars)>({chars...});
@@ -185,14 +185,10 @@ namespace boost { namespace parser {
template<typename T>
operator T() const && noexcept
{
#if defined(__GNUC__) && __GNUC__ < 13
// Yuck.
std::remove_reference_t<T> * ptr = nullptr;
ptr += 1; // warning mitigation
return *ptr;
#else
return std::declval<T>();
#endif
}
};

View File

@@ -237,6 +237,27 @@ void github_issue_125()
}
}
void github_issue_209()
{
namespace bp = boost::parser;
BOOST_TEST(std::is_sorted(
std::begin(bp::detail::char_set<detail::punct_chars>::chars),
std::end(bp::detail::char_set<detail::punct_chars>::chars)));
BOOST_TEST(std::is_sorted(
std::begin(bp::detail::char_set<detail::symb_chars>::chars),
std::end(bp::detail::char_set<detail::symb_chars>::chars)));
BOOST_TEST(std::is_sorted(
std::begin(bp::detail::char_set<detail::lower_case_chars>::chars),
std::end(bp::detail::char_set<detail::lower_case_chars>::chars)));
BOOST_TEST(std::is_sorted(
std::begin(bp::detail::char_set<detail::upper_case_chars>::chars),
std::end(bp::detail::char_set<detail::upper_case_chars>::chars)));
}
int main()
{
@@ -246,5 +267,6 @@ int main()
github_issue_78();
github_issue_90();
github_issue_125();
github_issue_209();
return boost::report_errors();
}

View File

@@ -2753,6 +2753,16 @@ int main()
BOOST_TEST(result == std::vector<uint32_t>({0x21, 0xfda}));
}
// symb_
{
auto parser = +symb;
std::u32string str = U"$^\u20AC!\u2194\u220F\U0001D7C6b\u2280\U0001FACE\U0001039F";
std::vector<uint32_t> result;
BOOST_TEST(parse(str, parser, char_ - symb, result));
BOOST_TEST(result == std::vector<uint32_t>({U'$', U'^', 0x20AC, 0x2194, 0x220F, 0x2280, 0x1FACE}));
}
// lower_
{
auto parser = +lower;

View File

@@ -88,5 +88,66 @@ int main()
}
}
return boost::report_errors();
{
using namespace bp::literals;
constexpr auto parser =
bp::delimiter(','_l)[bp::int_ || bp::string("foo") || bp::char_('g')];
{
auto result = bp::parse("42 foo g", parser, bp::ws);
BOOST_TEST(!result);
result = bp::parse("42, foo ,g", parser, bp::ws);
BOOST_TEST(result);
BOOST_TEST(
*result ==
(bp::tuple<int, std::string, double>(42, "foo"s, 'g')));
}
{
auto result = bp::parse(",42, g, foo", parser, bp::ws);
BOOST_TEST(!result);
result = bp::parse("42, g , foo", parser, bp::ws);
BOOST_TEST(result);
BOOST_TEST(
*result ==
(bp::tuple<int, std::string, double>(42, "foo"s, 'g')));
}
{
auto result = bp::parse("foo, 42, g,", parser, bp::ws);
BOOST_TEST(!result);
result = bp::parse("foo, 42, g", parser, bp::ws);
BOOST_TEST(result);
BOOST_TEST(
*result ==
(bp::tuple<int, std::string, double>(42, "foo"s, 'g')));
}
{
auto result = bp::parse("foo g 42", parser, bp::ws);
BOOST_TEST(!result);
result = bp::parse("foo, g, 42", parser, bp::ws);
BOOST_TEST(result);
BOOST_TEST(
*result ==
(bp::tuple<int, std::string, double>(42, "foo"s, 'g')));
}
{
auto result = bp::parse("g foo 42", parser, bp::ws);
BOOST_TEST(!result);
result = bp::parse("g ,foo ,42", parser, bp::ws);
BOOST_TEST(result);
BOOST_TEST(
*result ==
(bp::tuple<int, std::string, double>(42, "foo"s, 'g')));
}
{
auto result = bp::parse("g 42 foo", parser, bp::ws);
BOOST_TEST(!result);
result = bp::parse("g , 42 , foo", parser, bp::ws);
BOOST_TEST(result);
BOOST_TEST(
*result ==
(bp::tuple<int, std::string, double>(42, "foo"s, 'g')));
}
}
return boost::report_errors();
}

View File

@@ -49,7 +49,7 @@ int main()
}
}
// different_char
// different_quote
{
constexpr auto parser = bp::quoted_string('\'');
@@ -75,9 +75,18 @@ int main()
BOOST_TEST(result);
BOOST_TEST(*result == "'foo'");
}
{
constexpr auto parser = bp::quoted_string('\'', bp::char_("g\t"));
auto result = bp::parse(R"('\'ggg\'')", parser, bp::ws);
BOOST_TEST(result);
BOOST_TEST(*result == "'ggg'");
BOOST_TEST(!bp::parse(R"('\'fff\'')", parser, bp::ws));
}
}
// different_char_with_escapes
// different_quote_with_escapes
{
{
auto parser = bp::quoted_string('\'', cu_escapes);
@@ -119,6 +128,58 @@ int main()
}
}
// different_quote_with_escapes_and_char_p
{
{
auto parser = bp::quoted_string('\'', cu_escapes, bp::char_("g\t"));
{
auto result = bp::parse("", parser, bp::ws);
BOOST_TEST(!result);
}
{
auto result = bp::parse("'ggg'", parser, bp::ws);
BOOST_TEST(result);
}
{
auto result = bp::parse("'fff'", parser, bp::ws);
BOOST_TEST(!result);
}
{
auto result = bp::parse(R"('ggg\t')", parser, bp::ws);
BOOST_TEST(result);
BOOST_TEST(*result == "ggg\t");
}
{
auto result = bp::parse(R"('ggg\g')", parser, bp::ws);
BOOST_TEST(!result);
}
}
{
auto parser = bp::quoted_string('\'', cp_escapes);
{
auto result = bp::parse("", parser, bp::ws);
BOOST_TEST(!result);
}
{
auto result = bp::parse(R"('\tggg')", parser, bp::ws);
BOOST_TEST(result);
BOOST_TEST(*result == "\tggg");
}
{
auto result = bp::parse(R"('g\ggg')", parser, bp::ws);
BOOST_TEST(!result);
}
}
}
// char_set
{
constexpr auto parser = bp::quoted_string("'\"");
@@ -171,6 +232,15 @@ int main()
// character.
BOOST_TEST(!bp::parse(R"("\'foo")", parser, bp::ws));
}
{
constexpr auto parser = bp::quoted_string("'\"", bp::char_("g"));
auto result = bp::parse(R"('\'ggg\'')", parser, bp::ws);
BOOST_TEST(result);
BOOST_TEST(*result == "'ggg'");
BOOST_TEST(!bp::parse(R"('\'fff\'')", parser, bp::ws));
}
}
// char_set_with_escapes
@@ -233,6 +303,15 @@ int main()
BOOST_TEST(!result);
}
}
{
auto parser = bp::quoted_string("'\"", cu_escapes, bp::char_("g"));
auto result = bp::parse(R"('\'ggg\'')", parser, bp::ws);
BOOST_TEST(result);
BOOST_TEST(*result == "'ggg'");
BOOST_TEST(!bp::parse(R"('\'fff\'')", parser, bp::ws));
}
}
// doc_examples
@@ -279,6 +358,16 @@ int main()
assert(result5);
std::cout << *result5 << "\n"; // Prints (with a CRLF newline): some text
//]
//[ quoted_string_example_6
auto result6 = bp::parse(
"'some text'", bp::quoted_string("'\"", bp::char_('g')), bp::ws);
assert(!result6);
result6 =
bp::parse("'gggg'", bp::quoted_string("'\"", bp::char_('g')), bp::ws);
assert(result6);
std::cout << *result6 << "\n"; // Prints: gggg
//]
}
return boost::report_errors();