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

6 Commits

Author SHA1 Message Date
Zach Laine
e575cbd9f3 WIP 2024-12-14 14:32:36 -06:00
Zach Laine
3c65bcf187 WIP 2024-12-14 14:32:36 -06:00
Zach Laine
c965c512ad WIP 2024-12-14 14:32:36 -06:00
Zach Laine
702c509ea1 Apply the same sort of change as the previous two commits, but to all the
parsers not already modified.  Generalize with_parser_mods().  Remove
omit_parser, since it is now moot.

See #160.
2024-12-14 14:32:25 -06:00
Zach Laine
974154c578 Change string_parser to support the use of parser_modifiers, and change lit()
to return a parameterized string_parser instead of string_parser wrapped in an
omit_parser.

See #160.
2024-12-09 02:25:01 -06:00
Zach Laine
35a7bd3c09 Add a new template, parser_modifiers, that will hold common modifications to
parsers, like turning off attribute generation (replacing omit_parser).
Change char_parser to support the use of parser_modifiers, and change lit() to
return a parameterized char_parser instead of char_parser wrapped in an
omit_parser.

See #160.
2024-12-09 02:24:50 -06:00
37 changed files with 2517 additions and 5913 deletions

View File

@@ -1,4 +1,4 @@
name: macos-13 - Clang 14
name: macos-12 - Clang 14
on:
push:
@@ -16,7 +16,7 @@ jobs:
matrix:
cxx_std: [17]
runs-on: macos-13
runs-on: macos-12
steps:
- uses: actions/checkout@v4

View File

@@ -18,6 +18,10 @@ jobs:
compiler_version: [g++-10, g++-11]
cxx_std: [17, 20]
os: [ubuntu-22.04]
include:
- compiler_version: g++-9
cxx_std: 17
os: ubuntu-20.04
runs-on: ${{ matrix.os }}

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-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)
[![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)
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-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)
[![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)
[![License](https://img.shields.io/badge/license-boost-brightgreen.svg)](LICENSE_1_0.txt)

View File

@@ -1,24 +0,0 @@
# Copyright René Ferdinand Rivera Morell 2025
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
require-b2 5.2 ;
constant boost_dependencies :
/boost/assert//boost_assert
/boost/charconv//boost_charconv
/boost/hana//boost_hana
/boost/type_index//boost_type_index ;
project /boost/parser
;
explicit
[ alias boost_parser : : :
: <library>$(boost_dependencies) <include>include ]
[ alias all : boost_parser test ]
;
call-if : boost-library parser
;

View File

@@ -39,7 +39,7 @@ rule run_doxygen ( files * : name : expand ? )
}
run_doxygen [ glob $(here)/../include/boost/parser/*.hpp : $(here)/../include/boost/parser/concepts.hpp ] : "Headers" ;
run_doxygen [ glob $(here)/../../../boost/parser/*.hpp : $(here)/../../../boost/parser/concepts.hpp ] : "Headers" ;
install images_standalone : [ glob *.png ] : <location>html/parser/img ;
explicit images_standalone ;

View File

@@ -110,7 +110,6 @@
[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>`]
@@ -202,7 +201,6 @@
[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`]]
@@ -213,13 +211,11 @@
[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`]]
@@ -243,7 +239,6 @@
[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]]

File diff suppressed because it is too large Load Diff

View File

@@ -202,7 +202,7 @@ It has a different API, and other code that operates on text expects a string
instead of some other container. Arrays of characters are already considered
special by the standard library and common practice in C++.
Second, when you write a parser that parses multiple characters in a row, you
Second, When you write a parser that parses multiple characters in a row, you
are typically trying to produce a string attribute, rather than a few
individual character values. When you use multiple non-character parsers in a
row, you are typically trying to produce multiple values. For instance:
@@ -217,7 +217,7 @@ I've rarely written a parser like `parser_2` and wanted a
`std::vector<std::string>`.
_Parser_ therefore makes the common case the default behavior, and provides
you with the _merge_ and _sep_ directives to let you opt in to generating the
you with the _merge_ and _sep_ directives to let you opt-in to generating the
less-common attributes.
[heading Attribute compatibility rules are more strict than in Spirit]
@@ -261,12 +261,13 @@ Also, Spirit-style looseness is more complicated than `parser` above
indicates. Remember, `int_ | eps` and `-int_` are supposed to be semantically
equivalent. To do otherwise this would be a profound violation of the
principle of least surprise. So, if they're equivalent, we would need to
apply the same rule to `int_ | eps`. This is a lot to remember, and this is
complicated to implement and maintain.
apply the same rule to `int_ | eps`. Also, we would probably need to apply it
to `if_(cond)[int_]`, which is also a `std::optional<int>`. This is a lot to
remember, and this is complicated to implement and maintain.
I've been using Spirit 1 and later Spirit 2 since they were released. I did
not know about the particular looseness discussed here; a user pointed it out
on GitHub. In many years of using these libraries, I never fully learned all
on Github. In many years of using these libraries, I never fully learned all
the attribute-compatibility rules, and was often surprised by them.
Having a small set of rules that the user can internalize is vital; if the
@@ -298,7 +299,7 @@ that it succeeds (if it had failed, it would have cleared its attribute). It
does not know that there is nothing after it that could continue the parse,
nor that it is being used in to do a full parse. So, the over-all parse
fails, but the part of the parse that fills in the out-param attribute does
not know to clear its attribute.
not know do clear its attribute.
This is why the explicit clearing behavior happens at the end of _p_. This is
not without its downsides, though. Consider this.
@@ -314,7 +315,7 @@ not without its downsides, though. Consider this.
Here, the explicit clearing replaces the previous value of `3`, even though
the parser never touched the value! Destroying users' variables' state
without need may seem like a bad idea, but consider the alternative _emdash_
in the previous example, we had spurious values left in the out-param
In the previous example, we had spurious values left in the out-param
attribute. Here, without clearing, we would have had a value left in the
out-param attribute, not because it was a partial result of the parse, but
because the parse never touched it. This is certain to be confusing, or at
@@ -324,24 +325,4 @@ always equal to `A()` if the parser fails. It is equal to whatever the parser
sets it to _emdash_ or its previous value, if the parser does not mutate it
_emdash_ if the parse succeeds.
[heading There are no _Spirit_-style character class parsers]
_Spirit_ has these character class parsers that recognize the same set of
characters as the C standard library's character class functions. For
instance, _Spirit_'s `alnum` recognizes the characters recognized by
`std::isalnum()`, its `punct` recognizes the characters recognized by
`std::ispunct()`, etc.
The problem with this is that those `std::is*()` functions are badly broken.
They do not even work correctly for ASCII values. This is because they use
the C standard library's locale mechanism, which can be set to anything the
current platform supports, and can be set by any code anywhere in your
program; the locale is mutable global state. So, even if you use the default
C locale in your program, if you link against a library that sets the locale
to something that breaks ASCII character recognition (an EBCDIC locale, for
instance), your program is now incorrect, regardless of the code you wrote.
For this reason, I firmly believe that no one, anywhere, should use those C
functions in production code, and I am not supporting their use via _Parser_.
[endsect]

View File

@@ -80,7 +80,7 @@ the input they match unless otherwise stated in the table below.]
[[ _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-delimited list is: `int_ % ',' | attr(std::vector<int>)`. Without the "`| attr(...)`", at least one `int_` match would be required. ]]
[ 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. ]
@@ -132,13 +132,8 @@ 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 hexadecimal digit code point. ]
[ 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_. ]
[]]
@@ -230,7 +225,7 @@ the input they match unless otherwise stated in the table below.]
[[ _ui_ ]
[ Matches an unsigned integral value. ]
[ `unsigned int` ]
[ To specify a base/radix of `N`, use _ui_`.base<N>()`. To specify exactly `D` digits, use _ui_`.digits<D>()`. To specify a minimum of `LO` digits and a maximum of `HI` digits, use _ui_`.digits<LO, HI>()`. These calls can be chained, as in _ui_`.base<2>().digits<8>()`. ]]
[]]
[[ `_ui_(arg0)` ]
[ Matches exactly the unsigned integral value `_RES_np_(arg0)`. ]
@@ -270,7 +265,7 @@ the input they match unless otherwise stated in the table below.]
[[ _i_ ]
[ Matches a signed integral value. ]
[ `int` ]
[ To specify a base/radix of `N`, use _i_`.base<N>()`. To specify exactly `D` digits, use _i_`.digits<D>()`. To specify a minimum of `LO` digits and a maximum of `HI` digits, use _i_`.digits<LO, HI>()`. These calls can be chained, as in _i_`.base<2>().digits<8>()`. ]]
[]]
[[ `_i_(arg0)` ]
[ Matches exactly the signed integral value `_RES_np_(arg0)`. ]
@@ -319,7 +314,7 @@ the input they match unless otherwise stated in the table below.]
[[ `_if_np_(pred)[p]` ]
[ Equivalent to `_e_(pred) >> p`. ]
[ `_ATTR_np_(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) ...` ]
@@ -328,9 +323,9 @@ the input they match unless otherwise stated in the table below.]
[ 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`.]
[ _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. Inside of skippers, all _symbols_ will appear empty. ]]
[ Unlike the other entries in this table, _symbols_ is a type, not an object. ]]
[[ _quot_str_ ]
[ Matches `'"'`, followed by zero or more characters, followed by `'"'`. ]
@@ -368,7 +363,7 @@ character type (or use _attr_ to do so).]
]
[template table_combining_operations
Here are all the operators overloaded for parsers. In the tables below:
Here are all the operator overloaded for parsers. In the tables below:
* `c` is a character of type `char` or `char32_t`;
@@ -390,16 +385,16 @@ 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. Differs in precedence from `operator>`. ]]
[[`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. Differs in precedence from `operator>>`. ]]
[[`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 an _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`] [ 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)`] []]
@@ -429,15 +424,6 @@ 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
@@ -505,7 +491,7 @@ attribute type is `char32_t`:
static_assert(std::is_same_v<decltype(result), std::optional<char32_t>>));
The good news is that usually you don't parse characters individually. When
you parse with _ch_, you usually parse repetition of them, which will produce
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
@@ -551,13 +537,13 @@ tables below:
[[`p1 || p2`] [`_bp_tup_<_ATTR_np_(p1), _ATTR_np_(p2)>`]]
[[`p1 || p2 || p3`] [`_bp_tup_<_ATTR_np_(p1), _ATTR_np_(p2), _ATTR_np_(p3)>`]]
[[`p1 % p2`] [`std::string` if `_ATTR_np_(p1)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p1)>`]]
[[`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]`] [`_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), ...>`]]
]

View File

@@ -13,7 +13,7 @@ A /semantic action/ is an arbitrary bit of logic associated with a parser,
that is only executed when the parser matches.
Simpler parsers can be combined to form more complex parsers. Given some
combining operation `C`, and parsers `P0`, `P1`, ..., `PN`, `C(P0, P1, ..., PN)`
combining operation `C`, and parsers `P0`, `P1`, ... `PN`, `C(P0, P1, ... PN)`
creates a new parser `Q`. This creates a /parse tree/. `Q` is the parent of
`P1`, `P2` is the child of `Q`, etc. The parsers are applied in the top-down
fashion implied by this topology. When you use `Q` to parse a string, it
@@ -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 the advantage of 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 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
@@ -113,7 +113,7 @@ Also, just ignore for now the fact that _Parser_ somehow figured out that the
result type of the `*bp::char_` parser is a _std_str_. There are clear rules
for this that we'll cover later.
The effects of this call to _p_ are not very interesting _emdash_ since the
The effects of this call to _p_ is not very interesting _emdash_ since the
parser we gave it cannot ever fail, and because we're placing the output in
the same type as the input, it just copies the contents of `input` to
`result`.
@@ -403,7 +403,7 @@ Copying the entire context when mutating the context is therefore fast. The
context does no memory allocation.
[tip All these functions that take the parse context as their first parameter
will be found by Argument-Dependent Lookup. You will probably never need
will find by found by Argument-Dependent Lookup. You will probably never need
to qualify them with `boost::parser::`.]
[heading Accessors for data that are always available]
@@ -414,7 +414,7 @@ underscore.
[heading _pass_]
_pass_ returns a reference to a `bool` indicating the success or failure of
_pass_ returns a reference to a `bool` indicating the success of failure of
the current parse. This can be used to force the current parse to pass or
fail:
@@ -584,7 +584,7 @@ things:
* This rule object itself is called `doubles`.
* We've given `doubles` the diagnostic text `"doubles"` so that _Parser_ knows
* We've given `doubles` the diagnstic text `"doubles"` so that _Parser_ knows
how to refer to it when producing a trace of the parser during debugging.
Ok, so if `doubles` is a parser, what does it do? We define the rule's
@@ -632,7 +632,7 @@ All this is intended to introduce the notion of _rs_. It still may be a bit
unclear why you would want to use _rs_. The use cases for, and lots of detail
about, _rs_ is in a later section, _more_about_rules_.
[note The existence of _rs_ means that you will probably never have to write a
[note The existence of _rs_ means that will probably never have to write a
low-level parser. You can just put existing parsers together into _rs_
instead.]
@@ -828,7 +828,7 @@ the same character must be used on both sides.
[quoted_string_example_4]
Another common thing to do in a quoted string parser is to recognize escape
sequences. If you have simple escape sequences that do not require any real
sequences. If you have simple escape sequencecs that do not require any real
parsing, like say the simple escape sequences from C++, you can provide a
_symbols_ object as well. The template parameter `T` to _symbols_t_ must be
`char` or `char32_t`. You don't need to include the escaped backslash or the
@@ -836,14 +836,6 @@ escaped quote character, since those always work.
[quoted_string_example_5]
Additionally, with each of the forms shown above, you can optionally provide a
parser as a final argument, which will be used to parse each character inside
the quotes. You have to provide an actual full parser here; you cannot
provide a character or string literal. If you do not provide a character
parser, _ch_ is used.
[quoted_string_example_6]
[endsect]
[section Parsing In Detail]
@@ -901,7 +893,7 @@ all parser in its sequence. It then produces its attribute, a
`bp::parse()`.
Something to take note of between Steps #3 and #4: at the beginning of #4, the
input position had returned to where it was at the beginning of #3. This kind
input position had returned to where is was at the beginning of #3. This kind
of backtracking happens in alternative parsers when an alternative fails. The
next page has more details on the semantics of backtracking.
@@ -1159,7 +1151,7 @@ erase and clear for the current parse, and another that applies only to
subsequent parses. The full set of operations can be found in the _symbols_
API docs.
[note There are two versions of each of the _symbols_ `*_for_next_parse()`
[mpte There are two versions of each of the _symbols_ `*_for_next_parse()`
functions _emdash_ one that takes a context, and one that does not. The one
with the context is meant to be used within a semantic action. The one
without the context is for use outside of any parse.]
@@ -1248,25 +1240,22 @@ these parsers is in a subsequent section. The attributes are repeated here so
you can use see all the properties of the parsers in one place.]
If you have an integral type `IntType` that is not covered by any of the
_Parser_ parsers, you can explicitly specify a base/radix or bounds on the
number of digits. You do this by calling the `base()` and `digits()` member
functions on an existing parser of the right integral type. So if `IntType`
were unsigned, you would use `uint_`. If it were signed, you would use
`int_`. For example:
_Parser_ parsers, you can use a more verbose declaration to declare a parser
for `IntType`. If `IntType` were unsigned, you would use `uint_parser`. If
it were signed, you would use `int_parser`. For example:
constexpr auto hex_int = bp::uint_.base<16>();
constexpr parser_interface<int_parser<IntType>> hex_int;
You simply chain together the constraints you want to use, like
`.base<16>().digits<2>()` or `.digits<4>().base<8>()`.
`uint_parser` and `int_parser` accept three more non-type template parameters
after the type parameter. They are `Radix`, `MinDigits`, and `MaxDigits`.
`Radix` defaults to `10`, `MinDigits` to `1`, and `MaxDigits` to `-1`, which
is a sentinel value meaning that there is no max number of digits.
So, if you wanted to parse exactly eight hexadecimal digits in a row in order
to recognize Unicode character literals like C++ has (e.g. `\Udeadbeef`), you
could use this parser for the digits at the end:
constexpr auto hex_4_def = bp::uint_.base<16>().digits<8>();
If you want to specify an acceptable range of digits, use `.digits<LO, HI>()`.
Both `HI` and `LO` are inclusive bounds.
constexpr parser_interface<uint_parser<unsigned int, 16, 8, 8>> hex_int;
[endsect]
@@ -1286,7 +1275,7 @@ parsers; we won't say much about them here.
[heading Interaction with sequence, alternative, and permutation parsers]
Sequence, alternative, and permutation parsers do not nest in most cases.
(Let's consider just sequence parsers to keep things simple, but most of this
(Let's consider just sequence parsers to keep thinkgs simple, but most of this
logic applies to alternative parsers as well.) `a >> b >> c` is the same as
`(a >> b) >> c` and `a >> (b >> c)`, and they are each represented by a single
_seq_p_ with three subparsers, `a`, `b`, and `c`. However, if something
@@ -1296,7 +1285,7 @@ instance, `lexeme[a >> b] >> c` is a _seq_p_ containing two parsers, `lexeme[a
in a _lex_p_. This in turn turns off the sequence parser combining logic,
since both sides of the second `operator>>` in `lexeme[a >> b] >> c` are not
_seq_ps_. Sequence parsers have several rules that govern what the overall
attribute type of the parser is, based on the positions and attributes of its
attribute type of the parser is, based on the positions and attributes of it
subparsers (see _attr_gen_). Therefore, it's important to know which
directives create a new parser (and what kind), and which ones do not; this is
indicated for each directive below.
@@ -1328,7 +1317,7 @@ Creates an _omt_p_.
[heading _raw_]
`_raw_np_[p]` changes the attribute from `_ATTR_np_(p)` to a view that
`_raw_np_[p]` changes the attribute from `_ATTR_np_(p)` to to a view that
delimits the subrange of the input that was matched by `p`. The type of the
view is `_v_<I>`, where `I` is the type of the iterator used within the parse.
Note that this may not be the same as the iterator type passed to _p_. For
@@ -1394,7 +1383,7 @@ need to support the multi-expanding code points, use the other overload, like:
and if you do it to two bits of text `A` and `B`, then you can compare them
bitwise to see if they are the same, except of case. Case folding may
sometimes expand a code point into multiple code points (e.g. case folding
`"ẞ"` yields `"ss"`). When such a multi-code point expansion occurs, the
`"ẞ"` yields `"ss"`. When such a multi-code point expansion occurs, the
expanded code points are in the NFKC normalization form.]
Creates a _noc_p_.
@@ -1453,24 +1442,6 @@ _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]
@@ -1510,7 +1481,7 @@ pretty useless. You should never see this type in practice. Within semantic
actions, asking for the attribute of a non-attribute-producing parser (using
`_attr(ctx)`) will yield a value of the special type `boost::parser::none`.
When calling _p_ in a form that returns the attribute parsed, when there is no
attribute, simply returns `bool`; this indicates the success or failure of the
attribute, simply returns `bool`; this indicates the success of failure of the
parse.]
[warning _Parser_ assumes that all attributes are semi-regular (see
@@ -1619,74 +1590,9 @@ 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 parser
The rules for alternative parsers are much simpler. For an alternative parer
`p`, let the list of attribute types for the subparsers of `p` be `a0, a1, a2,
..., an`. The attribute of `p` is `std::variant<a0, a1, a2, ..., an>`, with
the following steps applied:
@@ -1694,8 +1600,8 @@ the following steps applied:
* all the `none` attributes are left out, and if any are, the attribute is
wrapped in a `std::optional`, like `std::optional<std::variant</*...*/>>`;
* duplicates in the `std::variant` template parameters `<T1, T2, ..., Tn>` are
removed; every type that appears does so exactly once;
* duplicates in the `std::variant` template parameters `<T1, T2, ... Tn>` are
removed; every type that appears does so exacly once;
* if the attribute is `std::variant<T>` or `std::optional<std::variant<T>>`,
the attribute becomes instead `T` or `std::optional<T>`, respectively; and
@@ -1790,7 +1696,7 @@ subparsers in the sequence parser to use the same variable for their
attribute.
Another directive, _sep_, also applies only to sequence parsers, but does the
opposite of _merge_. It forces all the attributes produced by the subparsers
opposite of _merge_. If forces all the attributes produced by the subparsers
of the sequence parser to stay separate, even if they would have combined.
For instance, consider this parser.
@@ -1844,7 +1750,7 @@ and _sep_ do not. Since they operate only on sequence parsers, all they do is
create a copy of the sequence parser they are given. The _seq_p_ template has
a template parameter `CombiningGroups`, and all _merge_ and _sep_ do is take a
given _seq_p_ and create a copy of it with a different `CombiningGroups`
template parameter. This means that _merge_ and _sep_ can be ignored in
template parameter. This means that _merge_ and _sep_ are can be ignored in
`operator>>` expressions much like parentheses are. Consider an example.
namespace bp = boost::parser;
@@ -1867,7 +1773,7 @@ given function `f`. For example:
Here, we have a function `str_sum` that we use for `f`. It assumes each
character in the given _std_str_ `s` is a digit, and returns the sum of all
the digits in `s`. Our parser `parser` would normally return a _std_str_.
the digits in `s`. Out parser `parser` would normally return a _std_str_.
However, since `str_sum` returns a different type _emdash_ `int` _emdash_ that
is the attribute type of the full parser,
`bp::transform(by_value_str_sum)[parser]`, as you can see from the
@@ -1900,7 +1806,7 @@ common:
* They each return a value contextually convertible to `bool`.
* They each take at least a range to parse and a parser. The "range to parse"
may be an iterator/sentinel pair or a single range object.
may be an iterator/sentinel pair or an single range object.
* They each require forward iterability of the range to parse.
@@ -2241,7 +2147,7 @@ encoding. Here is how it deduces which case the call falls under:
* Otherwise, the input is in a UTF encoding.
[tip If you want to parse in ASCII-only mode, or in some other
[tip if you want to want to parse in ASCII-only mode, or in some other
non-Unicode encoding, use only sequences of `char`, like _std_str_ or `char
const *`.]
@@ -2265,7 +2171,7 @@ _eh_debugging_ section of the tutorial for details.
[heading Globals and error handlers]
Each call to _p_ can optionally have a globals object associated with it. To
use a particular globals object with your parser, you call _w_glb_ to create a
use a particular globals object with you parser, you call _w_glb_ to create a
new parser with the globals object in it:
struct globals_t
@@ -2305,8 +2211,6 @@ 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);
@@ -2447,10 +2351,6 @@ 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
@@ -2469,12 +2369,11 @@ defined somewhere.
BOOST_PARSER_DEFINE_RULES(value);
Notice the two expectation points. One before `(value % ',')`, one before the
final `'}'`. Later, you parse in some input:
final `'}'`. Later, you call parse in some input:
bp::parse("{ 4, 5 a", value, bp::ws);
This runs afoul of the second expectation point, and produces output like
this:
This runs should of the second expectation point, and produces output like this:
[pre
1:7: error: Expected '}' here:
@@ -2494,8 +2393,8 @@ the earlier expectation:
]
Not nearly as nice. The problem is that the expectation is on `(value %
',')`. So, even though we gave `value` reasonable diagnostic text, we put the
text on the wrong thing. We can introduce a new rule to put the diagnostic
',')`. So, even thought we gave `value` reasonable dianostic text, we put the
text on the wrong thing. We can introduce a new rule to put the diagnstic
text in the right place.
namespace bp = boost::parser;
@@ -2521,7 +2420,7 @@ message:
]
The _r_ `value` might be useful elsewhere in our code, perhaps in another
parser. Its diagnostic text is appropriate for those other potential uses.
parser. It's diagnostic text is appropriate for those other potential uses.
[heading Recursive rules]
@@ -2572,7 +2471,7 @@ of a recursive _r_. This is because each instance of the rule needs a place
to put the attribute it generates from its parse. However, we only want a
single return value for the uppermost rule; if each instance had a separate
value in `_val(ctx)`, then it would be impossible to build up the result of a
recursive rule step by step during the evaluation of the recursive
recursive rule step by step during in the evaluation of the recursive
instantiations.
Also, consider this rule:
@@ -2581,7 +2480,7 @@ Also, consider this rule:
bp::rule<struct ints_tag, std::vector<int>> ints = "ints";
auto const ints_def = bp::int_ >> ints | bp::eps;
What is the default attribute type for `ints_def`? It sure looks like
What is the default attribute type for ints_def? It sure looks like
`std::optional<std::vector<int>>`. Inside the evaluation of `ints`, _Parser_
must evaluate `ints_def`, and then produce a `std::vector<int>` _emdash_ the
return type of `ints` _emdash_ from it. How? How do you turn a
@@ -2589,7 +2488,7 @@ return type of `ints` _emdash_ from it. How? How do you turn a
seems obvious, but the metaprogramming that properly handles this simple
example and the general case is certainly beyond me.
_Parser_ has a specific semantic for what constitutes a recursive rule. Each
_Parser_ has a specific semantic for what consitutes a recursive rule. Each
rule has a tag type associated with it, and if _Parser_ enters a rule with a
certain tag `Tag`, and the currently-evaluating rule (if there is one) also
has the tag `Tag`, then rule instance being entered is considered to be a
@@ -2624,7 +2523,7 @@ use each other without introducing cycles:
BOOST_PARSER_DEFINE_RULES(string, object_element, object, array, value);
Here we have a parser for a JavaScript-value-like type `value_type`.
Here we have a parser for a Javascript-value-like type `value_type`.
`value_type` may be an array, which itself may contain other arrays, objects,
strings, etc. Since we need to be able to parse objects within arrays and
vice versa, we need each of those two parsers to be able to refer to each
@@ -2662,8 +2561,8 @@ semantics, is a lot easier to read, and is a lot less code.]
[heading Locals]
The _r_ template takes another template parameter we have not discussed yet.
You can pass a third parameter `LocalState` to _r_, which will be default
constructed by the _r_, and made available within semantic actions used in the
You can pass a third parameter `LocalState` to _r_, which will be defaulted
csontructed by the _r_, and made available within semantic actions used in the
rule as `_locals_np_(ctx)`. This gives your rule some local state, if it
needs it. The type of `LocalState` can be anything regular. It could be a
single value, a struct containing multiple values, or a tuple, among others.
@@ -2774,7 +2673,7 @@ rewritten as:
auto const foo_def = bp::repeat(bp::_p<0>)[' '_l];
Using __p_ can prevent you from having to write a bunch of lambdas that
Using __p_ can prevent you from having to write a bunch of lambdas that get
each get an argument out of the parse context using `_params_np_(ctx)[0_c]` or
similar.
@@ -3161,7 +3060,7 @@ worrying if the input is Unicode or not because, under the covers, what takes
place is a simple comparison of two integral values.
[note _Parser_ actually promotes any two values to a common type using
`std::common_type` before comparing them. This almost always works because
`std::common_type` before comparing them. This is almost always works because
the input and any parameter passed to _ch_ must be character types. ]
Since matches are always done at a code point level (remember, a "code point"
@@ -3403,9 +3302,9 @@ _w_eh_ (see _p_api_). If you do not set one, _default_eh_ will be used.
[heading How diagnostics are generated]
_Parser_ only generates error messages like the ones in this page at failed
expectation points (like `a > b`, where you have successfully parsed `a`, but
then cannot successfully parse `b`), and at an unexpected end of input. This
may seem limited to you. It's actually the best that we can do.
expectation points, like `a > b`, where you have successfully parsed `a`, but
then cannot successfully parse `b`. This may seem limited to you. It's
actually the best that we can do.
In order for error handling to happen other than at expectation points, we
have to know that there is no further processing that might take place. This
@@ -3413,26 +3312,21 @@ is true because _Parser_ has `P1 | P2 | ... | Pn` parsers ("`or_parser`s").
If any one of these parsers `Pi` fails to match, it is not allowed to fail the
parse _emdash_ the next one (`Pi+1`) might match. If we get to the end of the
alternatives of the or_parser and `Pn` fails, we still cannot fail the
top-level parse, because this `or_parser` might be a subparser within a parent
`or_parser`. The only exception to this is when: we have finished the
top-level parse; the top-level parse is *not* a prefix parse; and there is
still a part of the input range that is left over. In that case, there is an
implicit expectation that the end of the parse and the end of input are the
same location, and this implicit expectation has just been violated.
top-level parse, because the `or_parser` might be a subparser within a parent
`or_parser`.
Note that we cannot fail the top-level parse when we run into end-of-input.
We cannot for exactly the same reason already stated. For any parser `P`,
reaching end-of-input is a failure for `P`, but not necessarily for the whole
parse.
Ok, so what might we do? Perhaps we could at least indicate when we ran into
end-of-input. But we cannot, for exactly the same reason already stated. For
any parser `P`, reaching end-of-input is a failure for `P`, but not
necessarily for the whole parse.
Ok, so what other kinds of error reporting might we do? Perhaps we could
record the farthest point ever reached during the parse, and report that at
the top level, if the top level parser fails. That would be little help
without knowing which parser was active when we reached that point. This
would require some sort of repeated memory allocation, since in _Parser_ the
progress point of the parser is stored exclusively on the stack _emdash_ by
the time we fail the top-level parse, all those far-reaching stack frames are
long gone. Not the best.
Perhaps we could record the farthest point ever reached during the parse, and
report that at the top level, if the top level parser fails. That would be
little help without knowing which parser was active when we reached that
point. This would require some sort of repeated memory allocation, since in
_Parser_ the progress point of the parser is stored exclusively on the stack
_emdash_ by the time we fail the top-level parse, all those far-reaching stack
frames are long gone. Not the best.
Worse still, knowing how far you got in the parse and which parser was active
is not very useful. Consider this.
@@ -3442,23 +3336,22 @@ is not very useful. Consider this.
auto c_b = bp::char_('c') >> bp::char_('b');
auto result = bp::parse("acb", a_b | c_b);
If we reported the farthest-reaching parser and its position, it would be the
If we reported the farthest-reaching parser and it's position, it would be the
`a_b` parser, at position `"bc"` in the input. Is this really enlightening?
Was the error in the input putting the `'a'` at the beginning or putting the
`'c'` in the middle? If you point the user at `a_b` as the parser that
failed, and never mention `c_b`, you are potentially just steering them in the
wrong direction.
All error messages must come from failed expectation points (or unexpected end
of input). Consider parsing JSON. If you open a list with `'['`, you know
that you're parsing a list, and if the list is ill-formed, you'll get an error
message saying so. If you open an object with `'{'`, the same thing is
possible _emdash_ when missing the matching `'}'`, you can tell the user,
"That's not an object", and this is useful feedback. The same thing with a
partially parsed number, etc. If the JSON parser does not build in
expectations like matched braces and brackets, how can _Parser_ know that a
missing `'}'` is really a problem, and that no later parser will match the
input even without the `'}'`?
All error messages must come from failed expectation points. Consider parsing
JSON. If you open a list with `'['`, you know that you're parsing a list, and
if the list is ill-formed, you'll get an error message saying so. If you open
an object with `'{'`, the same thing is possible _emdash_ when missing the
matching `'}'`, you can tell the user, "That's not an object", and this is
useful feedback. The same thing with a partially parsed number, etc. If the
JSON parser does not build in expectations like matched braces and brackets,
how can _Parser_ know that a missing `'}'` is really a problem, and that no
later parser will match the input even without the `'}'`?
[important The bottom line is that you should build expectation points into
your parsers using `operator>` as much as possible.]
@@ -3561,7 +3454,7 @@ We just define a `logging_error_handler`, and pass it by reference to _w_eh_,
which decorates the top-level parser with the error handler. We *could not*
have written `bp::with_error_handler(parser,
logging_error_handler("parse.log"))`, because _w_eh_ does not accept rvalues.
This is because the error handler eventually goes into the parse context. The
This is becuse the error handler eventually goes into the parse context. The
parse context only stores pointers and iterators, keeping it cheap to copy.
If we run the example and give it the input `"1,"`, this shows up in the log
@@ -3609,9 +3502,9 @@ to `_trace_::off`.
If we trace a substantial parser, we will see a *lot* of output. Each code
point of the input must be considered, one at a time, to see if a certain rule
matches. As an example, let's trace a parse using the JSON parser from
matches. An an example, let's trace a parse using the JSON parser from
_ex_json_. The input is `"null"`. `null` is one of the types that a
JavaScript value can have; the top-level parser in the JSON parser example is:
Javascript value can have; the top-level parser in the JSON parser example is:
auto const value_p_def =
number | bp::bool_ | null | string | array_p | object_p;
@@ -3796,10 +3689,10 @@ _Parser_ seldom allocates memory. The exceptions to this are:
which implies allocation. You can avoid this allocation by explicitly using
a different sequence container for the attribute that does not allocate.
`boost::container::static_vector` or C++26's `std::inplace_vector` may be
useful for such replacements.
useful as such replacements.
With the exception of allocating the name of the parser that was expected in a
failed expectation situation, _Parser_ does not allocate unless you
failed expectation situation, _Parser_ does not does not allocate unless you
tell it to, by using _symbols_, using a particular error_handler, turning on
trace, or parsing into attributes that allocate.
@@ -3807,7 +3700,7 @@ trace, or parsing into attributes that allocate.
[section Best Practices]
[heading Parse Unicode from the start]
[heading Parse unicode from the start]
If you want to parse ASCII, using the Unicode parsing API will not actually
cost you anything. Your input will be parsed, `char` by `char`, and compared
@@ -3873,9 +3766,9 @@ Some things to note:
want to know how to fix their input. For either rule, the fix is the same:
put a hexadecimal escape sequence there.
- `single_escaped_char` has a terrible-looking name. However, it's not
actually used as a name. In error messages, it works nicely, though. The
error will be "Expected '"', '\', '/', 'b', 'f', 'n', 'r', or 't' here",
- `single_escaped_char` has a terrible-looking name. However, it's not really
used as a name anywhere per se. In error messages, it works nicely, though.
The error will be "Expected '"', '\', '/', 'b', 'f', 'n', 'r', or 't' here",
which is pretty helpful.
[heading Have a simple test that you can run to find ill-formed-code-as-asserts]
@@ -3954,7 +3847,7 @@ First, let's look at the template and function parameters.
`true` if the parse succeeds, and `false` otherwise.
Now the body of the function. Notice that it just dispatches to the other
`call()` overload. This is really common, since both overloads need to do the
`call()` overload. This is really common, since both overloads need to to the
same parsing; only the attribute may differ. The first line of the body
defines `attr_t`, the default attribute type of our wrapped parser `parser_`.
It does this by getting the `decltype()` of a use of `parser_.call()`. (This

View File

@@ -126,7 +126,7 @@ namespace json {
}
};
auto const hex_4_def = boost::parser::uint_.base<16>().digits<4>();
bp::parser_interface<bp::uint_parser<uint32_t, 16, 4, 4>> const hex_4_def;
auto const escape_seq_def = "\\u" > hex_4;

View File

@@ -5,7 +5,7 @@
// http://www.boost.org/LICENSE_1_0.txt)
//[ extended_json_example
// This header includes a type called json::value that acts as a
// JavaScript-like polymorphic value type.
// Javascript-like polymorphic value type.
#include "json.hpp"
#include <boost/parser/parser.hpp>
@@ -151,10 +151,12 @@ namespace json {
}
};
// We don't want to use boost::parser::hex directly, since it has a
// variable number of digits. We want to match exactly 4 digits, and this
// is how we declare a hexadecimal parser that matches exactly 4.
auto const hex_4_def = boost::parser::uint_.base<16>().digits<4>();
// This is the verbose form of declaration for the integer and unsigned
// integer parsers int_parser and uint_parser. In this case, we don't
// want to use boost::parser::hex directly, since it has a variable number
// of digits. We want to match exactly 4 digits, and this is how we
// declare a hexadecimal parser that matches exactly 4.
bp::parser_interface<bp::uint_parser<uint32_t, 16, 4, 4>> const hex_4_def;
// We use > here instead of >>, because once we see \u, we know that
// exactly four hex digits must follow -- no other production rule starts

View File

@@ -59,7 +59,7 @@ namespace boost { namespace parser {
std::declval<bool &>(),
std::declval<int &>(),
std::declval<ErrorHandler const &>(),
std::declval<detail::nope const &>(),
std::declval<detail::nope &>(),
std::declval<detail::symbol_table_tries_t &>(),
std::declval<detail::pending_symbol_table_operations_t &>()));

View File

@@ -16,19 +16,19 @@
/** Boost.Parser uses assertions (`BOOST_ASSERT()`) in several places to
indicate that your use of the library has an error in it. All of those
places could have instead been ill-formed code, caught at compile time.
places could heve instead been ill-formed code, caught at compile time.
It is far quicker and easier to determine exactly where in your code such
an error is located if this is a runtime failure; you can just look at the
stack in your favorite debugger. However, if you want to make these kinds
stack in your favorite debugger. However, if you want to make thes kinds
of errors always ill-formed code, define this macro. */
# define BOOST_PARSER_NO_RUNTIME_ASSERTIONS
/** Asserts that the given condition is true. If
`BOOST_PARSER_NO_RUNTIME_ASSERTIONS` macro is defined by the user,
`BOOST_PARSER_ASSERT` expands to a compile-time `static_assert()`.
`BOOST_PARSER_ASSERT` expends to a compile-time `static_assert()`.
Otherwise, it expands to a run-time `BOOST_ASSERT()`. Note that defining
`BOOST_DISABLE_ASSERTS` disables the use of C `assert`, even when
`BOOST_ASSERT` is unavailable. */
`BOOST_ASSERT` is unavailble. */
# define BOOST_PARSER_ASSERT(condition)
/** Boost.Parser will automatically use concepts to constrain templates when
@@ -73,12 +73,6 @@
#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

@@ -47,10 +47,7 @@ namespace boost::parser::detail {
// One-byte fast path.
if (cp < 0x100) {
// ASCII letter fast path.
if (0x61 <= cp && cp <= 0x7a) {
*out++ = cp;
return out;
} else if (0x41 <= cp && cp <= 0x5a) {
if (0x41 <= cp && cp < 0x5a) {
*out++ = cp + 0x20;
return out;
} else if (cp == 0x00DF) {

View File

@@ -52,110 +52,125 @@ namespace boost { namespace parser { namespace detail {
typename Parser,
typename DelimiterParser,
typename MinType,
typename MaxType>
void print_parser(
typename MaxType,
typename ParserMods>
void print_parser_impl(
Context const & context,
repeat_parser<Parser, DelimiterParser, MinType, MaxType> const & parser,
repeat_parser<
Parser,
DelimiterParser,
MinType,
MaxType,
ParserMods> const & parser,
std::ostream & os,
int components = 0);
int components);
template<typename Context, typename Parser>
void print_parser(
template<typename Context, typename Parser, typename ParserMods>
void print_parser_impl(
Context const & context,
opt_parser<Parser> const & parser,
opt_parser<Parser, ParserMods> const & parser,
std::ostream & os,
int components = 0);
int components);
template<typename Context, typename ParserTuple>
void print_parser(
template<typename Context, typename ParserTuple, typename ParserMods>
void print_parser_impl(
Context const & context,
or_parser<ParserTuple> const & parser,
or_parser<ParserTuple, ParserMods> const & parser,
std::ostream & os,
int components = 0);
int components);
template<typename Context, typename ParserTuple, typename DelimiterParser>
void print_parser(
template<typename Context, typename ParserTuple, typename ParserMods>
void print_parser_impl(
Context const & context,
perm_parser<ParserTuple, DelimiterParser> const & parser,
perm_parser<ParserTuple, ParserMods> const & parser,
std::ostream & os,
int components = 0);
int components);
template<
typename Context,
typename ParserTuple,
typename BacktrackingTuple,
typename CombiningGroups>
void print_parser(
typename CombiningGroups,
typename ParserMods>
void print_parser_impl(
Context const & context,
seq_parser<ParserTuple, BacktrackingTuple, CombiningGroups> const &
parser,
seq_parser<
ParserTuple,
BacktrackingTuple,
CombiningGroups,
ParserMods> const & parser,
std::ostream & os,
int components = 0);
int components);
template<typename Context, typename Parser, typename Action>
void print_parser(
template<
typename Context,
typename Parser,
typename Action,
typename ParserMods>
void print_parser_impl(
Context const & context,
action_parser<Parser, Action> const & parser,
action_parser<Parser, Action, ParserMods> const & parser,
std::ostream & os,
int components = 0);
int components);
template<typename Context, typename Parser, typename F>
void print_parser(
template<typename Context, typename Parser, typename F, typename ParserMods>
void print_parser_impl(
Context const & context,
transform_parser<Parser, F> const & parser,
transform_parser<Parser, F, ParserMods> const & parser,
std::ostream & os,
int components = 0);
int components);
template<typename Context, typename Parser>
void print_parser(
template<typename Context, typename Parser, typename ParserMods>
void print_parser_impl(
Context const & context,
omit_parser<Parser> const & parser,
raw_parser<Parser, ParserMods> const & parser,
std::ostream & os,
int components = 0);
template<typename Context, typename Parser>
void print_parser(
Context const & context,
raw_parser<Parser> const & parser,
std::ostream & os,
int components = 0);
int components);
#if defined(BOOST_PARSER_DOXYGEN) || BOOST_PARSER_USE_CONCEPTS
template<typename Context, typename Parser>
void print_parser(
template<typename Context, typename Parser, typename ParserMods>
void print_parser_impl(
Context const & context,
string_view_parser<Parser> const & parser,
string_view_parser<Parser, ParserMods> const & parser,
std::ostream & os,
int components = 0);
int components);
#endif
template<typename Context, typename Parser>
void print_parser(
template<typename Context, typename Parser, typename ParserMods>
void print_parser_impl(
Context const & context,
lexeme_parser<Parser> const & parser,
lexeme_parser<Parser, ParserMods> const & parser,
std::ostream & os,
int components = 0);
int components);
template<typename Context, typename Parser>
void print_parser(
template<typename Context, typename Parser, typename ParserMods>
void print_parser_impl(
Context const & context,
no_case_parser<Parser> const & parser,
no_case_parser<Parser, ParserMods> const & parser,
std::ostream & os,
int components = 0);
int components);
template<typename Context, typename Parser, typename SkipParser>
void print_parser(
template<
typename Context,
typename Parser,
typename SkipParser,
typename ParserMods>
void print_parser_impl(
Context const & context,
skip_parser<Parser, SkipParser> const & parser,
skip_parser<Parser, SkipParser, ParserMods> const & parser,
std::ostream & os,
int components = 0);
int components);
template<typename Context, typename Parser, bool FailOnMatch>
void print_parser(
template<
typename Context,
typename Parser,
bool FailOnMatch,
typename ParserMods>
void print_parser_impl(
Context const & context,
expect_parser<Parser, FailOnMatch> const & parser,
expect_parser<Parser, FailOnMatch, ParserMods> const & parser,
std::ostream & os,
int components = 0);
int components);
template<
typename Context,
@@ -163,154 +178,147 @@ namespace boost { namespace parser { namespace detail {
typename Parser,
typename Attribute,
typename LocalState,
typename ParamsTuple>
void print_parser(
typename ParamsTuple,
typename ParserMods>
void print_parser_impl(
Context const & context,
rule_parser<
UseCallbacks,
Parser,
Attribute,
LocalState,
ParamsTuple> const & parser,
ParamsTuple,
ParserMods> const & parser,
std::ostream & os,
int components = 0);
int components);
template<typename Context, typename T>
void print_parser(
template<typename Context, typename T, typename ParserMods>
void print_parser_impl(
Context const & context,
symbol_parser<T> const & parser,
symbol_parser<T, ParserMods> const & parser,
std::ostream & os,
int components = 0);
int components);
template<typename Context, typename Predicate>
void print_parser(
template<typename Context, typename Predicate, typename ParserMods>
void print_parser_impl(
Context const & context,
eps_parser<Predicate> const & parser,
eps_parser<Predicate, ParserMods> const & parser,
std::ostream & os,
int components = 0);
int components);
template<typename Context>
void print_parser(
template<typename Context, typename ParserMods>
void print_parser_impl(
Context const & context,
eps_parser<nope> const & parser,
eps_parser<nope, ParserMods> const & parser,
std::ostream & os,
int components = 0);
int components);
template<typename Context>
void print_parser(
template<typename Context, typename ParserMods>
void print_parser_impl(
Context const & context,
eoi_parser const & parser,
eoi_parser<ParserMods> const & parser,
std::ostream & os,
int components = 0);
int components);
template<typename Context, typename Atribute>
void print_parser(
template<typename Context, typename Atribute, typename ParserMods>
void print_parser_impl(
Context const & context,
attr_parser<Atribute> const & parser,
attr_parser<Atribute, ParserMods> const & parser,
std::ostream & os,
int components = 0);
int components);
template<typename Context, typename Expected, typename AttributeType>
void print_parser(
template<
typename Context,
typename Expected,
typename AttributeType,
typename ParserMods>
void print_parser_impl(
Context const & context,
char_parser<Expected, AttributeType> const & parser,
char_parser<Expected, AttributeType, ParserMods> const & parser,
std::ostream & os,
int components = 0);
int components);
template<typename Context>
void print_parser(
template<typename Context, typename ParserMods>
void print_parser_impl(
Context const & context,
digit_parser const & parser,
digit_parser<ParserMods> const & parser,
std::ostream & os,
int components = 0);
int components);
template<typename Context>
void print_parser(
template<typename Context, typename ParserMods>
void print_parser_impl(
Context const & context,
char_subrange_parser<hex_digit_subranges> const & parser,
char_subrange_parser<hex_digit_subranges, ParserMods> const & parser,
std::ostream & os,
int components = 0);
int components);
template<typename Context>
void print_parser(
template<typename Context, typename ParserMods>
void print_parser_impl(
Context const & context,
char_subrange_parser<control_subranges> const & parser,
char_subrange_parser<control_subranges, ParserMods> const & parser,
std::ostream & os,
int components = 0);
int components);
template<typename Context>
void print_parser(
template<typename Context, typename ParserMods>
void print_parser_impl(
Context const & context,
char_set_parser<punct_chars> const & parser,
char_set_parser<punct_chars, ParserMods> const & parser,
std::ostream & os,
int components = 0);
int components);
template<typename Context>
void print_parser(
template<typename Context, typename ParserMods>
void print_parser_impl(
Context const & context,
char_set_parser<symb_chars> const & parser,
char_set_parser<lower_case_chars, ParserMods> const & parser,
std::ostream & os,
int components = 0);
int components);
template<typename Context>
void print_parser(
template<typename Context, typename ParserMods>
void print_parser_impl(
Context const & context,
char_set_parser<lower_case_chars> const & parser,
char_set_parser<upper_case_chars, ParserMods> const & parser,
std::ostream & os,
int components = 0);
int components);
template<typename Context>
void print_parser(
template<
typename Context,
typename StrIter,
typename StrSentinel,
typename ParserMods>
void print_parser_impl(
Context const & context,
char_set_parser<upper_case_chars> const & parser,
string_parser<StrIter, StrSentinel, ParserMods> const & parser,
std::ostream & os,
int components = 0);
template<typename Context, typename Expected, typename AttributeType>
void print_parser(
Context const & context,
omit_parser<char_parser<Expected, AttributeType>> const & parser,
std::ostream & os,
int components = 0);
template<typename Context, typename StrIter, typename StrSentinel>
void print_parser(
Context const & context,
string_parser<StrIter, StrSentinel> const & parser,
std::ostream & os,
int components = 0);
template<typename Context, typename StrIter, typename StrSentinel>
void print_parser(
Context const & context,
omit_parser<string_parser<StrIter, StrSentinel>> const & parser,
std::ostream & os,
int components = 0);
int components);
template<
typename Context,
typename Quotes,
typename Escapes,
typename CharParser>
void print_parser(
typename ParserMods>
void print_parser_impl(
Context const & context,
quoted_string_parser<Quotes, Escapes, CharParser> const & parser,
quoted_string_parser<Quotes, Escapes, ParserMods> const & parser,
std::ostream & os,
int components = 0);
int components);
template<typename Context, bool NewlinesOnly, bool NoNewlines>
void print_parser(
template<
typename Context,
bool NewlinesOnly,
bool NoNewlines,
typename ParserMods>
void print_parser_impl(
Context const & context,
ws_parser<NewlinesOnly, NoNewlines> const & parser,
ws_parser<NewlinesOnly, NoNewlines, ParserMods> const & parser,
std::ostream & os,
int components = 0);
int components);
template<typename Context>
void print_parser(
template<typename Context, typename ParserMods>
void print_parser_impl(
Context const & context,
bool_parser const & parser,
bool_parser<ParserMods> const & parser,
std::ostream & os,
int components = 0);
int components);
template<
typename Context,
@@ -318,12 +326,19 @@ namespace boost { namespace parser { namespace detail {
int Radix,
int MinDigits,
int MaxDigits,
typename Expected>
void print_parser(
typename Expected,
typename ParserMods>
void print_parser_impl(
Context const & context,
uint_parser<T, Radix, MinDigits, MaxDigits, Expected> const & parser,
uint_parser<
T,
Radix,
MinDigits,
MaxDigits,
Expected,
ParserMods> const & parser,
std::ostream & os,
int components = 0);
int components);
template<
typename Context,
@@ -331,38 +346,51 @@ namespace boost { namespace parser { namespace detail {
int Radix,
int MinDigits,
int MaxDigits,
typename Expected>
void print_parser(
typename Expected,
typename ParserMods>
void print_parser_impl(
Context const & context,
int_parser<T, Radix, MinDigits, MaxDigits, Expected> const & parser,
int_parser<T, Radix, MinDigits, MaxDigits, Expected, ParserMods> const &
parser,
std::ostream & os,
int components = 0);
int components);
template<typename Context, typename T>
void print_parser(
template<typename Context, typename T, typename ParserMods>
void print_parser_impl(
Context const & context,
float_parser<T> const & parser,
float_parser<T, ParserMods> const & parser,
std::ostream & os,
int components = 0);
int components);
template<typename Context>
void print_parser(
template<typename Context, typename ParserMods>
void print_parser_impl(
Context const & context,
float_parser<float> const & parser,
float_parser<float, ParserMods> const & parser,
std::ostream & os,
int components = 0);
int components);
template<typename Context>
void print_parser(
template<typename Context, typename ParserMods>
void print_parser_impl(
Context const & context,
float_parser<double> const & parser,
float_parser<double, ParserMods> const & parser,
std::ostream & os,
int components = 0);
int components);
template<typename Context, typename SwitchValue, typename OrParser>
template<
typename Context,
typename SwitchValue,
typename OrParser,
typename ParserMods>
void print_parser_impl(
Context const & context,
switch_parser<SwitchValue, OrParser, ParserMods> const & parser,
std::ostream & os,
int components);
template<typename Context, typename Parser>
void print_parser(
Context const & context,
switch_parser<SwitchValue, OrParser> const & parser,
Parser const & parser,
std::ostream & os,
int components = 0);

View File

@@ -43,37 +43,44 @@ namespace boost { namespace parser { namespace detail {
typename Parser,
typename DelimiterParser,
typename MinType,
typename MaxType>
typename MaxType,
typename ParserMods>
struct n_aray_parser<
repeat_parser<Parser, DelimiterParser, MinType, MaxType>>
repeat_parser<Parser, DelimiterParser, MinType, MaxType, ParserMods>>
: std::true_type
{};
template<typename Parser, typename MinType, typename MaxType>
struct n_aray_parser<repeat_parser<Parser, detail::nope, MinType, MaxType>>
template<
typename Parser,
typename MinType,
typename MaxType,
typename ParserMods>
struct n_aray_parser<
repeat_parser<Parser, detail::nope, MinType, MaxType, ParserMods>>
: std::false_type
{};
template<typename Parser, typename DelimiterParser>
struct n_aray_parser<delimited_seq_parser<Parser, DelimiterParser>>
template<typename Parser, typename DelimiterParser, typename ParserMods>
struct n_aray_parser<
delimited_seq_parser<Parser, DelimiterParser, ParserMods>>
: std::true_type
{};
template<typename ParserTuple>
struct n_aray_parser<or_parser<ParserTuple>> : std::true_type
template<typename ParserTuple, typename ParserMods>
struct n_aray_parser<or_parser<ParserTuple, ParserMods>> : std::true_type
{};
template<typename ParserTuple, typename DelimiterParser>
struct n_aray_parser<perm_parser<ParserTuple, DelimiterParser>>
: std::true_type
template<typename ParserTuple, typename ParserMods>
struct n_aray_parser<perm_parser<ParserTuple, ParserMods>> : std::true_type
{};
template<
typename ParserTuple,
typename BacktrackingTuple,
typename CombiningGroups>
typename CombiningGroups,
typename ParserMods>
struct n_aray_parser<
seq_parser<ParserTuple, BacktrackingTuple, CombiningGroups>>
seq_parser<ParserTuple, BacktrackingTuple, CombiningGroups, ParserMods>>
: std::true_type
{};
@@ -102,10 +109,16 @@ namespace boost { namespace parser { namespace detail {
typename Parser,
typename DelimiterParser,
typename MinType,
typename MaxType>
void print_parser(
typename MaxType,
typename ParserMods>
void print_parser_impl(
Context const & context,
repeat_parser<Parser, DelimiterParser, MinType, MaxType> const & parser,
repeat_parser<
Parser,
DelimiterParser,
MinType,
MaxType,
ParserMods> const & parser,
std::ostream & os,
int components)
{
@@ -117,7 +130,7 @@ namespace boost { namespace parser { namespace detail {
os << "*";
if (n_ary_child)
os << "(";
detail::print_parser(
detail::print_parser_impl(
context, parser.parser_, os, components + 1);
if (n_ary_child)
os << ")";
@@ -125,7 +138,7 @@ namespace boost { namespace parser { namespace detail {
os << "+";
if (n_ary_child)
os << "(";
detail::print_parser(
detail::print_parser_impl(
context, parser.parser_, os, components + 1);
if (n_ary_child)
os << ")";
@@ -142,22 +155,22 @@ namespace boost { namespace parser { namespace detail {
detail::print(os, max_);
os << ")[";
}
detail::print_parser(
detail::print_parser_impl(
context, parser.parser_, os, components + 1);
os << "]";
}
} else {
detail::print_parser(context, parser.parser_, os, components + 1);
detail::print_parser_impl(context, parser.parser_, os, components + 1);
os << " % ";
detail::print_parser(
detail::print_parser_impl(
context, parser.delimiter_parser_, os, components + 2);
}
}
template<typename Context, typename Parser>
void print_parser(
template<typename Context, typename Parser, typename ParserMods>
void print_parser_impl(
Context const & context,
opt_parser<Parser> const & parser,
opt_parser<Parser, ParserMods> const & parser,
std::ostream & os,
int components)
{
@@ -165,7 +178,7 @@ namespace boost { namespace parser { namespace detail {
constexpr bool n_ary_child = n_aray_parser_v<Parser>;
if (n_ary_child)
os << "(";
detail::print_parser(context, parser.parser_, os, components + 1);
detail::print_parser_impl(context, parser.parser_, os, components + 1);
if (n_ary_child)
os << ")";
}
@@ -190,16 +203,16 @@ namespace boost { namespace parser { namespace detail {
}
if (i)
os << ws_or;
detail::print_parser(context, parser, os, components);
detail::print_parser_impl(context, parser, os, components);
++components;
++i;
});
}
template<typename Context, typename ParserTuple>
void print_parser(
template<typename Context, typename ParserTuple, typename ParserMods>
void print_parser_impl(
Context const & context,
or_parser<ParserTuple> const & parser,
or_parser<ParserTuple, ParserMods> const & parser,
std::ostream & os,
int components)
{
@@ -207,34 +220,30 @@ namespace boost { namespace parser { namespace detail {
context, parser, os, components, " | ...", " | ");
}
template<typename Context, typename ParserTuple, typename DelimiterParser>
void print_parser(
template<typename Context, typename ParserTuple, typename ParserMods>
void print_parser_impl(
Context const & context,
perm_parser<ParserTuple, DelimiterParser> const & parser,
perm_parser<ParserTuple, ParserMods> 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<
typename Context,
typename ParserTuple,
typename BacktrackingTuple,
typename CombiningGroups>
void print_parser(
typename CombiningGroups,
typename ParserMods>
void print_parser_impl(
Context const & context,
seq_parser<ParserTuple, BacktrackingTuple, CombiningGroups> const &
parser,
seq_parser<
ParserTuple,
BacktrackingTuple,
CombiningGroups,
ParserMods> const & parser,
std::ostream & os,
int components)
{
@@ -264,7 +273,7 @@ namespace boost { namespace parser { namespace detail {
os << (backtrack ? " >> " : " > ");
if (group != prev_group && group)
os << (group == -1 ? "separate[" : "merge[");
detail::print_parser(context, parser, os, components);
detail::print_parser_impl(context, parser, os, components);
++components;
++i;
prev_group = (int)group;
@@ -273,14 +282,18 @@ namespace boost { namespace parser { namespace detail {
os << ']';
}
template<typename Context, typename Parser, typename Action>
void print_parser(
template<
typename Context,
typename Parser,
typename Action,
typename ParserMods>
void print_parser_impl(
Context const & context,
action_parser<Parser, Action> const & parser,
action_parser<Parser, Action, ParserMods> const & parser,
std::ostream & os,
int components)
{
detail::print_parser(context, parser.parser_, os, components);
detail::print_parser_impl(context, parser.parser_, os, components);
os << "[<<action>>]";
}
@@ -296,14 +309,14 @@ namespace boost { namespace parser { namespace detail {
if (++components == parser_component_limit)
os << "...";
else
detail::print_parser(context, parser, os, components + 1);
detail::print_parser_impl(context, parser, os, components + 1);
os << "]";
}
template<typename Context, typename Parser, typename F>
void print_parser(
template<typename Context, typename Parser, typename F, typename ParserMods>
void print_parser_impl(
Context const & context,
transform_parser<Parser, F> const & parser,
transform_parser<Parser, F, ParserMods> const & parser,
std::ostream & os,
int components)
{
@@ -311,21 +324,10 @@ namespace boost { namespace parser { namespace detail {
context, "transform(<<f>>)", parser.parser_, os, components);
}
template<typename Context, typename Parser>
void print_parser(
template<typename Context, typename Parser, typename ParserMods>
void print_parser_impl(
Context const & context,
omit_parser<Parser> const & parser,
std::ostream & os,
int components)
{
detail::print_directive(
context, "omit", parser.parser_, os, components);
}
template<typename Context, typename Parser>
void print_parser(
Context const & context,
raw_parser<Parser> const & parser,
raw_parser<Parser, ParserMods> const & parser,
std::ostream & os,
int components)
{
@@ -333,10 +335,10 @@ namespace boost { namespace parser { namespace detail {
}
#if defined(BOOST_PARSER_DOXYGEN) || BOOST_PARSER_USE_CONCEPTS
template<typename Context, typename Parser>
void print_parser(
template<typename Context, typename Parser, typename ParserMods>
void print_parser_impl(
Context const & context,
string_view_parser<Parser> const & parser,
string_view_parser<Parser, ParserMods> const & parser,
std::ostream & os,
int components)
{
@@ -345,10 +347,10 @@ namespace boost { namespace parser { namespace detail {
}
#endif
template<typename Context, typename Parser>
void print_parser(
template<typename Context, typename Parser, typename ParserMods>
void print_parser_impl(
Context const & context,
lexeme_parser<Parser> const & parser,
lexeme_parser<Parser, ParserMods> const & parser,
std::ostream & os,
int components)
{
@@ -356,10 +358,10 @@ namespace boost { namespace parser { namespace detail {
context, "lexeme", parser.parser_, os, components);
}
template<typename Context, typename Parser>
void print_parser(
template<typename Context, typename Parser, typename ParserMods>
void print_parser_impl(
Context const & context,
no_case_parser<Parser> const & parser,
no_case_parser<Parser, ParserMods> const & parser,
std::ostream & os,
int components)
{
@@ -367,10 +369,14 @@ namespace boost { namespace parser { namespace detail {
context, "no_case", parser.parser_, os, components);
}
template<typename Context, typename Parser, typename SkipParser>
void print_parser(
template<
typename Context,
typename Parser,
typename SkipParser,
typename ParserMods>
void print_parser_impl(
Context const & context,
skip_parser<Parser, SkipParser> const & parser,
skip_parser<Parser, SkipParser, ParserMods> const & parser,
std::ostream & os,
int components)
{
@@ -379,7 +385,7 @@ namespace boost { namespace parser { namespace detail {
context, "skip", parser.parser_, os, components);
} else {
os << "skip(";
detail::print_parser(
detail::print_parser_impl(
context, parser.skip_parser_.parser_, os, components);
os << ")";
detail::print_directive(
@@ -387,10 +393,14 @@ namespace boost { namespace parser { namespace detail {
}
}
template<typename Context, typename Parser, bool FailOnMatch>
void print_parser(
template<
typename Context,
typename Parser,
bool FailOnMatch,
typename ParserMods>
void print_parser_impl(
Context const & context,
expect_parser<Parser, FailOnMatch> const & parser,
expect_parser<Parser, FailOnMatch, ParserMods> const & parser,
std::ostream & os,
int components)
{
@@ -401,7 +411,7 @@ namespace boost { namespace parser { namespace detail {
constexpr bool n_ary_child = n_aray_parser_v<Parser>;
if (n_ary_child)
os << "(";
detail::print_parser(context, parser.parser_, os, components + 1);
detail::print_parser_impl(context, parser.parser_, os, components + 1);
if (n_ary_child)
os << ")";
}
@@ -412,15 +422,17 @@ namespace boost { namespace parser { namespace detail {
typename Parser,
typename Attribute,
typename LocalState,
typename ParamsTuple>
void print_parser(
typename ParamsTuple,
typename ParserMods>
void print_parser_impl(
Context const & context,
rule_parser<
UseCallbacks,
Parser,
Attribute,
LocalState,
ParamsTuple> const & parser,
ParamsTuple,
ParserMods> const & parser,
std::ostream & os,
int components)
{
@@ -437,10 +449,10 @@ namespace boost { namespace parser { namespace detail {
}
}
template<typename Context, typename T>
void print_parser(
template<typename Context, typename T, typename ParserMods>
void print_parser_impl(
Context const & context,
symbol_parser<T> const & parser,
symbol_parser<T, ParserMods> const & parser,
std::ostream & os,
int components)
{
@@ -450,40 +462,40 @@ namespace boost { namespace parser { namespace detail {
os << parser.diagnostic_text_;
}
template<typename Context, typename Predicate>
void print_parser(
template<typename Context, typename Predicate, typename ParserMods>
void print_parser_impl(
Context const & context,
eps_parser<Predicate> const & parser,
eps_parser<Predicate, ParserMods> const & parser,
std::ostream & os,
int components)
{
os << "eps(<<pred>>)";
}
template<typename Context>
void print_parser(
template<typename Context, typename ParserMods>
void print_parser_impl(
Context const & context,
eps_parser<nope> const & parser,
eps_parser<nope, ParserMods> const & parser,
std::ostream & os,
int components)
{
os << "eps";
}
template<typename Context>
void print_parser(
template<typename Context, typename ParserMods>
void print_parser_impl(
Context const & context,
eoi_parser const & parser,
eoi_parser<ParserMods> const & parser,
std::ostream & os,
int components)
{
os << "eoi";
}
template<typename Context, typename Atribute>
void print_parser(
template<typename Context, typename Atribute, typename ParserMods>
void print_parser_impl(
Context const & context,
attr_parser<Atribute> const & parser,
attr_parser<Atribute, ParserMods> const & parser,
std::ostream & os,
int components)
{
@@ -536,7 +548,7 @@ namespace boost { namespace parser { namespace detail {
}
template<typename Context, typename T>
struct char_print_parser_impl
struct char_print_parser_impl_impl
{
static void call(Context const & context, std::ostream & os, T expected)
{
@@ -545,7 +557,7 @@ namespace boost { namespace parser { namespace detail {
};
template<typename Context, typename T, typename U>
struct char_print_parser_impl<Context, char_pair<T, U>>
struct char_print_parser_impl_impl<Context, char_pair<T, U>>
{
static void call(
Context const & context,
@@ -559,7 +571,7 @@ namespace boost { namespace parser { namespace detail {
};
template<typename Context, typename Iter, typename Sentinel, bool B>
struct char_print_parser_impl<Context, char_range<Iter, Sentinel, B>>
struct char_print_parser_impl_impl<Context, char_range<Iter, Sentinel, B>>
{
static void call(
Context const & context,
@@ -575,153 +587,134 @@ namespace boost { namespace parser { namespace detail {
}
};
template<typename Context, typename Expected, typename AttributeType>
void print_parser(
template<
typename Context,
typename Expected,
typename AttributeType,
typename ParserMods>
void print_parser_impl(
Context const & context,
char_parser<Expected, AttributeType> const & parser,
char_parser<Expected, AttributeType, ParserMods> const & parser,
std::ostream & os,
int components)
{
if (std::is_same_v<AttributeType, uint32_t>)
os << "cp";
else if (std::is_same_v<AttributeType, char>)
os << "cu";
else
os << "char_";
if constexpr (!is_nope_v<Expected>) {
os << "(";
char_print_parser_impl<Context, Expected>::call(
context, os, parser.expected_);
os << ")";
if constexpr (parser.mods_.omit_attr) {
if constexpr (is_nope_v<Expected>) {
os << "char_";
} else {
char_print_parser_impl_impl<Context, Expected>::call(
context, os, parser.expected_);
}
} else {
if (std::is_same_v<AttributeType, uint32_t>)
os << "cp";
else if (std::is_same_v<AttributeType, char>)
os << "cu";
else
os << "char_";
if constexpr (!is_nope_v<Expected>) {
os << "(";
char_print_parser_impl_impl<Context, Expected>::call(
context, os, parser.expected_);
os << ")";
}
}
}
template<typename Context>
void print_parser(
template<typename Context, typename ParserMods>
void print_parser_impl(
Context const & context,
digit_parser const & parser,
digit_parser<ParserMods> const & parser,
std::ostream & os,
int components)
{
os << "digit";
}
template<typename Context>
void print_parser(
template<typename Context, typename ParserMods>
void print_parser_impl(
Context const & context,
char_subrange_parser<hex_digit_subranges> const & parser,
char_subrange_parser<hex_digit_subranges, ParserMods> const & parser,
std::ostream & os,
int components)
{
os << "hex_digit";
}
template<typename Context>
void print_parser(
template<typename Context, typename ParserMods>
void print_parser_impl(
Context const & context,
char_subrange_parser<control_subranges> const & parser,
char_subrange_parser<control_subranges, ParserMods> const & parser,
std::ostream & os,
int components)
{
os << "control";
}
template<typename Context>
void print_parser(
template<typename Context, typename ParserMods>
void print_parser_impl(
Context const & context,
char_set_parser<punct_chars> const & parser,
char_set_parser<punct_chars, ParserMods> const & parser,
std::ostream & os,
int components)
{
os << "punct";
}
template<typename Context>
void print_parser(
template<typename Context, typename ParserMods>
void print_parser_impl(
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,
char_set_parser<lower_case_chars> const & parser,
char_set_parser<lower_case_chars, ParserMods> const & parser,
std::ostream & os,
int components)
{
os << "lower";
}
template<typename Context>
void print_parser(
template<typename Context, typename ParserMods>
void print_parser_impl(
Context const & context,
char_set_parser<upper_case_chars> const & parser,
char_set_parser<upper_case_chars, ParserMods> const & parser,
std::ostream & os,
int components)
{
os << "upper";
}
template<typename Context, typename Expected, typename AttributeType>
void print_parser(
template<
typename Context,
typename StrIter,
typename StrSentinel,
typename ParserMods>
void print_parser_impl(
Context const & context,
omit_parser<char_parser<Expected, AttributeType>> const & parser,
string_parser<StrIter, StrSentinel, ParserMods> const & parser,
std::ostream & os,
int components)
{
if constexpr (is_nope_v<Expected>) {
os << "omit[char_]";
} else {
char_print_parser_impl<Context, Expected>::call(
context, os, parser.parser_.expected_);
if constexpr (!parser.mods_.omit_attr) {
os << "string(";
}
}
template<typename Context, typename StrIter, typename StrSentinel>
void print_parser(
Context const & context,
string_parser<StrIter, StrSentinel> const & parser,
std::ostream & os,
int components)
{
os << "string(\"";
os << "\"";
for (auto c : BOOST_PARSER_DETAIL_TEXT_SUBRANGE(
parser.expected_first_, parser.expected_last_) |
text::as_utf8) {
detail::print_char(os, c);
}
os << "\")";
}
template<typename Context, typename StrIter, typename StrSentinel>
void print_parser(
Context const & context,
omit_parser<string_parser<StrIter, StrSentinel>> const & parser,
std::ostream & os,
int components)
{
os << "\"";
for (auto c : BOOST_PARSER_DETAIL_TEXT_SUBRANGE(
parser.parser_.expected_first_,
parser.parser_.expected_last_) |
text::as_utf8) {
detail::print_char(os, c);
if constexpr (!parser.mods_.omit_attr) {
os << ")";
}
os << "\"";
}
template<
typename Context,
typename Quotes,
typename Escapes,
typename CharParser>
void print_parser(
typename ParserMods>
void print_parser_impl(
Context const & context,
quoted_string_parser<Quotes, Escapes, CharParser> const & parser,
quoted_string_parser<Quotes, Escapes, ParserMods> const & parser,
std::ostream & os,
int components)
{
@@ -739,10 +732,14 @@ namespace boost { namespace parser { namespace detail {
os << ')';
}
template<typename Context, bool NewlinesOnly, bool NoNewlines>
void print_parser(
template<
typename Context,
bool NewlinesOnly,
bool NoNewlines,
typename ParserMods>
void print_parser_impl(
Context const & context,
ws_parser<NewlinesOnly, NoNewlines> const & parser,
ws_parser<NewlinesOnly, NoNewlines, ParserMods> const & parser,
std::ostream & os,
int components)
{
@@ -754,10 +751,10 @@ namespace boost { namespace parser { namespace detail {
os << "ws";
}
template<typename Context>
void print_parser(
template<typename Context, typename ParserMods>
void print_parser_impl(
Context const & context,
bool_parser const & parser,
bool_parser<ParserMods> const & parser,
std::ostream & os,
int components)
{
@@ -770,10 +767,17 @@ namespace boost { namespace parser { namespace detail {
int Radix,
int MinDigits,
int MaxDigits,
typename Expected>
void print_parser(
typename Expected,
typename ParserMods>
void print_parser_impl(
Context const & context,
uint_parser<T, Radix, MinDigits, MaxDigits, Expected> const & parser,
uint_parser<
T,
Radix,
MinDigits,
MaxDigits,
Expected,
ParserMods> const & parser,
std::ostream & os,
int components)
{
@@ -814,27 +818,29 @@ namespace boost { namespace parser { namespace detail {
int Radix,
int MinDigits,
int MaxDigits,
typename Expected>
void print_parser(
typename Expected,
typename ParserMods>
void print_parser_impl(
Context const & context,
int_parser<T, Radix, MinDigits, MaxDigits, Expected> const & parser,
int_parser<T, Radix, MinDigits, MaxDigits, Expected, ParserMods> const &
parser,
std::ostream & os,
int components)
{
if (Radix == 10 && MinDigits == 1 && MaxDigits == -1) {
if (std::is_same_v<T, short>) {
if constexpr (std::is_same_v<T, short>) {
os << "short_";
detail::print_expected(context, os, parser.expected_);
return;
} else if (std::is_same_v<T, int>) {
} else if constexpr (std::is_same_v<T, int>) {
os << "int_";
detail::print_expected(context, os, parser.expected_);
return;
} else if (std::is_same_v<T, long>) {
} else if constexpr (std::is_same_v<T, long>) {
os << "long_";
detail::print_expected(context, os, parser.expected_);
return;
} else if (std::is_same_v<T, long long>) {
} else if constexpr (std::is_same_v<T, long long>) {
os << "long_long";
detail::print_expected(context, os, parser.expected_);
return;
@@ -844,61 +850,31 @@ namespace boost { namespace parser { namespace detail {
<< MinDigits << ", " << MaxDigits << ">";
detail::print_expected(context, os, parser.expected_);
}
template<typename Context>
void print_parser(
template<typename Context, typename T, typename ParserMods>
void print_parser_impl(
Context const & context,
int_parser<short> const & parser,
std::ostream & os,
int components)
{
os << "short_";
}
template<typename Context>
void print_parser(
Context const & context,
int_parser<long> const & parser,
std::ostream & os,
int components)
{
os << "long_";
}
template<typename Context>
void print_parser(
Context const & context,
int_parser<long long> const & parser,
std::ostream & os,
int components)
{
os << "long_long";
}
template<typename Context, typename T>
void print_parser(
Context const & context,
float_parser<T> const & parser,
float_parser<T, ParserMods> const & parser,
std::ostream & os,
int components)
{
os << "float<" << detail::type_name<T>() << ">";
}
template<typename Context>
void print_parser(
template<typename Context, typename ParserMods>
void print_parser_impl(
Context const & context,
float_parser<float> const & parser,
float_parser<float, ParserMods> const & parser,
std::ostream & os,
int components)
{
os << "float_";
}
template<typename Context>
void print_parser(
template<typename Context, typename ParserMods>
void print_parser_impl(
Context const & context,
float_parser<double> const & parser,
float_parser<double, ParserMods> const & parser,
std::ostream & os,
int components)
{
@@ -909,10 +885,11 @@ namespace boost { namespace parser { namespace detail {
typename Context,
typename ParserTuple,
typename BacktrackingTuple,
typename CombiningGroups>
typename CombiningGroups,
typename ParserMods>
void print_switch_matchers(
Context const & context,
seq_parser<ParserTuple, BacktrackingTuple, CombiningGroups> const &
seq_parser<ParserTuple, BacktrackingTuple, CombiningGroups, ParserMods> const &
parser,
std::ostream & os,
int components)
@@ -924,15 +901,15 @@ namespace boost { namespace parser { namespace detail {
detail::resolve(
context, parser::get(parser.parsers_, 0_c).pred_.value_));
os << ", ";
detail::print_parser(
detail::print_parser_impl(
context, parser::get(parser.parsers_, 1_c), os, components);
os << ")";
}
template<typename Context, typename ParserTuple>
template<typename Context, typename ParserTuple, typename ParserMods>
void print_switch_matchers(
Context const & context,
or_parser<ParserTuple> const & parser,
or_parser<ParserTuple, ParserMods> const & parser,
std::ostream & os,
int components)
{
@@ -951,10 +928,14 @@ namespace boost { namespace parser { namespace detail {
});
}
template<typename Context, typename SwitchValue, typename OrParser>
void print_parser(
template<
typename Context,
typename SwitchValue,
typename OrParser,
typename ParserMods>
void print_parser_impl(
Context const & context,
switch_parser<SwitchValue, OrParser> const & parser,
switch_parser<SwitchValue, OrParser, ParserMods> const & parser,
std::ostream & os,
int components)
{
@@ -965,6 +946,67 @@ namespace boost { namespace parser { namespace detail {
context, parser.or_parser_, os, components);
}
template<typename ParserMods>
struct scoped_print_parser_mods
{
template<typename Parser>
scoped_print_parser_mods(std::ostream & os, Parser const & parser) :
os_(os), mods_(parser.mods_)
{
if constexpr (ParserMods::omit_attr) {
skip_omit_ = skip_omit(parser);
if (!skip_omit_)
os_ << "omit[";
}
}
~scoped_print_parser_mods()
{
if constexpr (ParserMods::omit_attr) {
if (!skip_omit_)
os_ << "]";
}
}
template<typename T>
static bool skip_omit(T const &)
{
return false;
}
template<
typename Expected,
typename AttributeType,
typename ParserMods2>
static bool
skip_omit(char_parser<Expected, AttributeType, ParserMods2> const &)
{
return !std::is_same_v<Expected, detail::nope>;
}
template<typename StrIter, typename StrSentinel, typename ParserMods2>
static bool
skip_omit(string_parser<StrIter, StrSentinel, ParserMods2> const &)
{
return true;
}
std::ostream & os_;
ParserMods const & mods_;
bool skip_omit_ = false;
};
// TODO: Document how 'omit[]' will reappear for subparsers sometimes, as
// in omt[*omit[char_]] (see test/tracing.cpp).
template<typename Context, typename Parser>
void print_parser(
Context const & context,
Parser const & parser,
std::ostream & os,
int components)
{
scoped_print_parser_mods<std::decay_t<decltype(parser.mods_)>> _(
os, parser);
detail::print_parser_impl(context, parser, os, components);
}
}}}
#endif

View File

@@ -24,12 +24,8 @@
#define BOOST_PARSER_USE_CPP23_STD_RANGE_ADAPTOR_CLOSURE 0
#endif
#if !BOOST_STL_INTERFACES_USE_CPP23_STD_RANGE_ADAPTOR_CLOSURE && \
BOOST_STL_INTERFACES_USE_CONCEPTS && defined(BOOST_GCC) && 14 <= __GNUC__
#define BOOST_PARSER_USE_LIBSTDCPP_GCC14_RANGE_ADAPTOR_CLOSURE 1
#elif !BOOST_PARSER_USE_CPP23_STD_RANGE_ADAPTOR_CLOSURE && \
BOOST_PARSER_DETAIL_STL_INTERFACES_USE_CONCEPTS && \
defined(BOOST_PARSER_GCC) && 12 <= __GNUC__
#if !BOOST_PARSER_USE_CPP23_STD_RANGE_ADAPTOR_CLOSURE && \
BOOST_PARSER_DETAIL_STL_INTERFACES_USE_CONCEPTS && defined(__GNUC__) && 12 <= __GNUC__
#define BOOST_PARSER_USE_LIBSTDCPP_GCC12_RANGE_ADAPTOR_CLOSURE 1
#else
#define BOOST_PARSER_USE_LIBSTDCPP_GCC12_RANGE_ADAPTOR_CLOSURE 0
@@ -201,11 +197,6 @@ namespace boost::parser::detail { namespace stl_interfaces {
template<typename D>
using range_adaptor_closure = std::ranges::range_adaptor_closure<D>;
#elif BOOST_PARSER_USE_LIBSTDCPP_GCC14_RANGE_ADAPTOR_CLOSURE
template<typename D>
using range_adaptor_closure = std::views::__adaptor::_RangeAdaptorClosure<D>;
#elif BOOST_PARSER_USE_LIBSTDCPP_GCC12_RANGE_ADAPTOR_CLOSURE
template<typename D>
@@ -267,7 +258,7 @@ namespace boost::parser::detail { namespace stl_interfaces {
template<typename F>
struct closure : range_adaptor_closure<closure<F>>
{
constexpr closure(F f) : f_(std::move(f)) {}
constexpr closure(F f) : f_(f) {}
#if BOOST_PARSER_DETAIL_STL_INTERFACES_USE_CONCEPTS
template<typename T>
@@ -335,7 +326,7 @@ namespace boost::parser::detail { namespace stl_interfaces {
template<typename F>
struct adaptor
{
constexpr adaptor(F f) : f_(std::move(f)) {}
constexpr adaptor(F f) : f_(f) {}
template<typename... Args>
constexpr auto operator()(Args &&... args) const

View File

@@ -42,8 +42,7 @@ namespace boost::parser::detail::text::detail {
template<typename R>
constexpr bool view =
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS || \
(defined(__cpp_lib_concepts) && \
(!defined(BOOST_PARSER_GCC) || 12 <= __GNUC__))
(defined(__cpp_lib_concepts) && (!defined(__GNUC__) || 12 <= __GNUC__))
std::ranges::view<R>
#else
range_<R> && !container_<R> &&
@@ -126,7 +125,7 @@ namespace boost::parser::detail::text::detail {
else if constexpr (can_ref_view<R>)
return ref_view(r);
else
return owning_view<T>((R &&)r);
return owning_view<T>(std::move(r));
}
};

View File

@@ -88,12 +88,10 @@ namespace boost::parser::detail { namespace text {
{
V base_ = V();
// HACK: SentType is here to work around irritating big-3
// implementation inconsistencies.
template<bool Const>
class iterator;
template<bool Const>
class sentinel;
template<bool Const, typename SentType = sentinel<Const>>
class iterator;
public:
constexpr project_view()
@@ -142,7 +140,7 @@ namespace boost::parser::detail { namespace text {
#else
template<typename V, typename F>
#endif
template<bool Const, typename SentType>
template<bool Const>
class project_view<V, F>::iterator
: public boost::parser::detail::stl_interfaces::proxy_iterator_interface<
iterator<Const>,
@@ -163,7 +161,7 @@ namespace boost::parser::detail { namespace text {
decltype(detail::function_for_tag<F>(0))
#endif
;
using sentinel = SentType;
using sentinel = project_view<V, F>::sentinel<Const>;
friend boost::parser::detail::stl_interfaces::access;
iterator_type & base_reference() noexcept { return it_; }
@@ -171,7 +169,7 @@ namespace boost::parser::detail { namespace text {
iterator_type it_ = iterator_type();
friend project_view<V, F>::template sentinel<Const>;
friend project_view<V, F>::sentinel<Const>;
template<bool OtherConst>
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS

File diff suppressed because it is too large Load Diff

View File

@@ -104,7 +104,7 @@ namespace boost { namespace parser {
/** The error handler used when the user does not specify a custom one.
This error handler prints warnings and errors to `std::cerr`, and does
not have an associated filename. */
not have an associcated filename. */
struct default_error_handler
{
constexpr default_error_handler() = default;

File diff suppressed because it is too large Load Diff

View File

@@ -115,7 +115,7 @@ namespace boost { namespace parser {
bool & success,
int & indent,
ErrorHandler const & error_handler,
nope const &,
nope &,
symbol_table_tries_t & symbol_table_tries,
pending_symbol_table_operations_t &
pending_symbol_table_operations) noexcept;
@@ -143,14 +143,22 @@ namespace boost { namespace parser {
struct punct_chars
{};
struct symb_chars
{};
struct lower_case_chars
{};
struct upper_case_chars
{};
}
template<bool OmitAttr = false, typename Action = detail::nope>
struct parser_modifiers
{
using action_type = Action;
static constexpr bool omit_attr = OmitAttr;
[[no_unique_address]] Action action;
};
/** Repeats the application of another parser `p` of type `Parser`,
optionally applying another parser `d` of type `DelimiterParser` in
between each pair of applications of `p`. The parse succeeds if `p`
@@ -161,20 +169,21 @@ namespace boost { namespace parser {
typename Parser,
typename DelimiterParser = detail::nope,
typename MinType = int64_t,
typename MaxType = int64_t>
typename MaxType = int64_t,
typename ParserMods = parser_modifiers<>>
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>
template<typename Parser, typename ParserMods>
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>
template<typename Parser, typename ParserMods>
struct one_plus_parser;
/** Repeats the application of another parser `p` of type `Parser`, `[1,
@@ -183,14 +192,14 @@ namespace boost { namespace parser {
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>
template<typename Parser, typename DelimiterParser, typename ParserMods>
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 ParserMods>
struct opt_parser;
/** Applies each parser in `ParserTuple`, in order, stopping after the
@@ -198,7 +207,7 @@ 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 ParserMods>
struct or_parser;
/** Applies each parsers in `ParserTuple`, an any order, stopping after
@@ -209,7 +218,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, typename DelimiterParser>
template<typename ParserTuple, typename ParserMods>
struct perm_parser;
/** Applies each parser in `ParserTuple`, in order. The parse succeeds
@@ -222,14 +231,15 @@ namespace boost { namespace parser {
template<
typename ParserTuple,
typename BacktrackingTuple,
typename CombiningGroups>
typename CombiningGroups,
typename ParserMods>
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 ParserMods>
struct action_parser;
/** Applies the given parser `p` of type `Parser`. The attribute produced
@@ -237,21 +247,14 @@ namespace boost { namespace parser {
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>
template<typename Parser, typename F, typename ParserMods>
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>
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 ParserMods>
struct raw_parser;
#if defined(BOOST_PARSER_DOXYGEN) || BOOST_PARSER_USE_CONCEPTS
@@ -264,34 +267,37 @@ 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 ParserMods>
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 ParserMods>
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 ParserMods>
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 ParserMods = parser_modifiers<>>
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, bool FailOnMatch, typename ParserMods>
struct expect_parser;
/** Matches one of a set S of possible inputs, each of which is associated
@@ -300,7 +306,7 @@ namespace boost { namespace parser {
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>
template<typename T, typename ParserMods>
struct symbol_parser;
/** Applies another parser `p`, associated with this parser via `TagType`.
@@ -319,22 +325,24 @@ namespace boost { namespace parser {
typename TagType,
typename Attribute,
typename LocalState,
typename ParamsTuple>
typename ParamsTuple,
typename ParserMods>
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 ParserMods>
struct eps_parser;
/** Matches only the end of input. Produces no attribute. */
template<typename ParserMods>
struct eoi_parser;
/** Matches anything, consumes no input, and produces an attribute of type
`RESOLVE(Attribute)`. */
template<typename Attribute>
template<typename Attribute, typename ParserMods>
struct attr_parser;
/** A tag type that can be passed as the first parameter to `char_()` when
@@ -351,7 +359,10 @@ namespace boost { namespace parser {
parse fails only if the parser is constructed with a specific set of
expected code point values that does not include the matched code
point. */
template<typename Expected, typename AttributeType = void>
template<
typename Expected,
typename AttributeType = void,
typename ParserMods = parser_modifiers<>>
struct char_parser;
/** Matches a single code point that is equal to one of the code points
@@ -359,7 +370,7 @@ namespace boost { namespace parser {
characters for matching Unicode character classes like punctuation or
lower case. Attribute type is the attribute type of the character
being matched. */
template<typename Tag>
template<typename Tag, typename ParserMods>
struct char_set_parser;
/** Matches a single code point that falls into one of the subranges of
@@ -367,17 +378,18 @@ namespace boost { namespace parser {
sets of characters for matching Unicode character classes like hex
digits or control characters. Attribute type is the attribute type of
the character being matched. */
template<typename Tag>
template<typename Tag, typename ParserMods>
struct char_subrange_parser;
/** Matches a single decimal digit code point, using the Unicode character
class Hex_Digit. Attribute type is the attribute type of the
character being matched. */
template<typename ParserMods>
struct digit_parser;
/** Matches a particular string, delimited by an iterator sentinel pair;
produces no attribute. */
template<typename StrIter, typename StrSentinel>
template<typename StrIter, typename StrSentinel, typename ParserMods>
struct string_parser;
/** Matches a string delimited by quotation marks; produces a
@@ -385,7 +397,7 @@ namespace boost { namespace parser {
template<
typename Quotes = detail::nope,
typename Escapes = detail::nope,
typename CharParser = char_parser<detail::nope>>
typename ParserMods = parser_modifiers<>>
struct quoted_string_parser;
/** Matches an end-of-line (`NewlinesOnly == true`), whitespace
@@ -393,25 +405,27 @@ namespace boost { namespace parser {
but not newline) code point, based on the Unicode definitions of each
(also matches the two code points `"\r\n"`). Produces no
attribute. */
template<bool NewlinesOnly, bool NoNewlines>
template<bool NewlinesOnly, bool NoNewlines, typename ParserMods>
struct ws_parser;
/** Matches the strings "true" and "false", producing an attribute of
`true` or `false`, respectively, and fails on any other input. */
template<typename ParserMods>
struct bool_parser;
/** Matches an unsigned number of radix `Radix`, of at least `MinDigits`
and at most `MaxDigits`, producing an attribute of type `T`. Fails on
any other input. The parse will also fail if `Expected` is anything
but `detail::nope` (which it is by default), and the produced
attribute is not equal to `expected_`. `Radix` must be one of `2`,
`8`, `10`, or `16`. */
attribute is not equal to `expected_`. `Radix` must be in `[2,
36]`. */
template<
typename T,
int Radix = 10,
int MinDigits = 1,
int MaxDigits = -1,
typename Expected = detail::nope>
typename Expected = detail::nope,
typename ParserMods = parser_modifiers<>>
struct uint_parser;
/** Matches a signed number of radix `Radix`, of at least `MinDigits` and
@@ -425,12 +439,13 @@ namespace boost { namespace parser {
int Radix = 10,
int MinDigits = 1,
int MaxDigits = -1,
typename Expected = detail::nope>
typename Expected = detail::nope,
typename ParserMods = parser_modifiers<>>
struct int_parser;
/** Matches a floating point number, producing an attribute of type
`T`. */
template<typename T>
template<typename T, typename ParserMods>
struct float_parser;
/** Applies at most one of the parsers in `OrParser`. If `switch_value_`
@@ -438,7 +453,10 @@ namespace boost { namespace parser {
first such parser is applied, and the success or failure and attribute
of the parse are those of the applied parser. Otherwise, the parse
fails. */
template<typename SwitchValue, typename OrParser = detail::nope>
template<
typename SwitchValue,
typename OrParser = detail::nope,
typename ParserMods = parser_modifiers<>>
struct switch_parser;
/** A wrapper for parsers that provides the operations that must be

View File

@@ -451,7 +451,7 @@ namespace boost::parser {
Parser,
GlobalState,
ErrorHandler,
parser_interface<eps_parser<detail::phony>>>;
parser_interface<eps_parser<detail::phony, parser_modifiers<>>>>;
template<
typename V,
@@ -469,7 +469,7 @@ namespace boost::parser {
Parser,
GlobalState,
ErrorHandler,
parser_interface<eps_parser<detail::phony>>>;
parser_interface<eps_parser<detail::phony, parser_modifiers<>>>>;
namespace detail {
template<
@@ -571,7 +571,8 @@ namespace boost::parser {
Parser,
GlobalState,
ErrorHandler,
parser_interface<eps_parser<detail::phony>>>
parser_interface<
eps_parser<detail::phony, parser_modifiers<>>>>
[[nodiscard]] constexpr auto operator()(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const &
@@ -582,7 +583,8 @@ namespace boost::parser {
return (*this)(
(R &&)r,
parser,
parser_interface<eps_parser<detail::phony>>{},
parser_interface<
eps_parser<detail::phony, parser_modifiers<>>>{},
(ReplacementR &&)replacement,
trace_mode);
}
@@ -623,10 +625,11 @@ namespace boost::parser {
std::is_same_v<Trace, trace>) {
// (r, parser, replacement, trace) case
return impl(
(R &&) r,
(R &&)r,
parser,
parser_interface<eps_parser<detail::phony>>{},
(SkipParser &&) skip,
parser_interface<
eps_parser<detail::phony, parser_modifiers<>>>{},
(SkipParser &&)skip,
replacement);
} else {
static_assert(

View File

@@ -116,7 +116,9 @@ namespace boost::parser {
return BOOST_PARSER_SUBRANGE(first, first);
auto const search_parser = omit[*(char_ - parser)] >> -raw[parser];
if constexpr (std::is_same_v<SkipParser, eps_parser<phony>>) {
if constexpr (std::is_same_v<
SkipParser,
eps_parser<phony, parser_modifiers<>>>) {
auto result = parser::prefix_parse(
first, last, search_parser, trace_mode);
if (*result)
@@ -255,9 +257,9 @@ namespace boost::parser {
trace trace_mode = trace::off)
{
return parser::search(
(R &&) r,
(R &&)r,
parser,
parser_interface<eps_parser<detail::phony>>{},
parser_interface<eps_parser<detail::phony, parser_modifiers<>>>{},
trace_mode);
}
@@ -292,7 +294,7 @@ namespace boost::parser {
return parser::search(
BOOST_PARSER_SUBRANGE(first, last),
parser,
parser_interface<eps_parser<detail::phony>>{},
parser_interface<eps_parser<detail::phony, parser_modifiers<>>>{},
trace_mode);
}
@@ -483,7 +485,7 @@ namespace boost::parser {
Parser,
GlobalState,
ErrorHandler,
parser_interface<eps_parser<detail::phony>>>;
parser_interface<eps_parser<detail::phony, parser_modifiers<>>>>;
template<
typename V,
@@ -496,7 +498,7 @@ namespace boost::parser {
Parser,
GlobalState,
ErrorHandler,
parser_interface<eps_parser<detail::phony>>>;
parser_interface<eps_parser<detail::phony, parser_modifiers<>>>>;
namespace detail {
template<
@@ -569,7 +571,8 @@ namespace boost::parser {
Parser,
GlobalState,
ErrorHandler,
parser_interface<eps_parser<detail::phony>>>
parser_interface<
eps_parser<detail::phony, parser_modifiers<>>>>
[[nodiscard]] constexpr auto operator()(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const &
@@ -579,7 +582,8 @@ namespace boost::parser {
return (*this)(
(R &&)r,
parser,
parser_interface<eps_parser<detail::phony>>{},
parser_interface<
eps_parser<detail::phony, parser_modifiers<>>>{},
trace_mode);
}
@@ -590,8 +594,8 @@ namespace boost::parser {
typename Parser,
typename GlobalState,
typename ErrorHandler,
typename SkipParser =
parser_interface<eps_parser<detail::phony>>,
typename SkipParser = parser_interface<
eps_parser<detail::phony, parser_modifiers<>>>,
typename Trace = trace,
typename Enable = std::enable_if_t<is_parsable_range_v<R>>>
[[nodiscard]] constexpr auto operator()(
@@ -607,9 +611,10 @@ namespace boost::parser {
std::is_same_v<Trace, trace>) {
// (r, parser, trace) case
return impl(
(R &&) r,
(R &&)r,
parser,
parser_interface<eps_parser<detail::phony>>{},
parser_interface<
eps_parser<detail::phony, parser_modifiers<>>>{},
skip);
} else if constexpr (
detail::is_parser_iface<SkipParser> &&

View File

@@ -193,14 +193,13 @@ namespace boost::parser {
typename Parser,
typename GlobalState,
typename ErrorHandler>
split_view(
V &&, parser_interface<Parser, GlobalState, ErrorHandler>, trace)
split_view(V &&, parser_interface<Parser, GlobalState, ErrorHandler>, trace)
-> split_view<
detail::text::detail::all_t<V>,
Parser,
GlobalState,
ErrorHandler,
parser_interface<eps_parser<detail::phony>>>;
parser_interface<eps_parser<detail::phony, parser_modifiers<>>>>;
template<
typename V,
@@ -213,7 +212,7 @@ namespace boost::parser {
Parser,
GlobalState,
ErrorHandler,
parser_interface<eps_parser<detail::phony>>>;
parser_interface<eps_parser<detail::phony, parser_modifiers<>>>>;
namespace detail {
template<
@@ -286,7 +285,8 @@ namespace boost::parser {
Parser,
GlobalState,
ErrorHandler,
parser_interface<eps_parser<detail::phony>>>
parser_interface<
eps_parser<detail::phony, parser_modifiers<>>>>
[[nodiscard]] constexpr auto operator()(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const &
@@ -296,7 +296,8 @@ namespace boost::parser {
return (*this)(
(R &&)r,
parser,
parser_interface<eps_parser<detail::phony>>{},
parser_interface<
eps_parser<detail::phony, parser_modifiers<>>>{},
trace_mode);
}
@@ -307,8 +308,8 @@ namespace boost::parser {
typename Parser,
typename GlobalState,
typename ErrorHandler,
typename SkipParser =
parser_interface<eps_parser<detail::phony>>,
typename SkipParser = parser_interface<
eps_parser<detail::phony, parser_modifiers<>>>,
typename Trace = trace,
typename Enable = std::enable_if_t<is_parsable_range_v<R>>>
[[nodiscard]] constexpr auto operator()(
@@ -324,9 +325,10 @@ namespace boost::parser {
std::is_same_v<Trace, trace>) {
// (r, parser, trace) case
return impl(
(R &&) r,
(R &&)r,
parser,
parser_interface<eps_parser<detail::phony>>{},
parser_interface<
eps_parser<detail::phony, parser_modifiers<>>>{},
skip);
} else if constexpr (
detail::is_parser_iface<SkipParser> &&

View File

@@ -4,8 +4,7 @@
#include <boost/parser/replace.hpp>
#if (!defined(_MSC_VER) || BOOST_PARSER_USE_CONCEPTS) && \
(!defined(BOOST_PARSER_GCC) || 12 <= __GNUC__ || \
!BOOST_PARSER_USE_CONCEPTS)
(!defined(__GNUC__) || 12 <= __GNUC__ || !BOOST_PARSER_USE_CONCEPTS)
namespace boost::parser {
@@ -255,7 +254,9 @@ namespace boost::parser {
BOOST_PARSER_SUBRANGE(first, first), parse_result{});
}
if constexpr (std::is_same_v<SkipParser, eps_parser<phony>>) {
if constexpr (std::is_same_v<
SkipParser,
eps_parser<phony, parser_modifiers<>>>) {
auto result = parser::prefix_parse(
first, last, search_parser, trace_mode);
if (*result) {
@@ -579,7 +580,7 @@ namespace boost::parser {
Parser,
GlobalState,
ErrorHandler,
parser_interface<eps_parser<detail::phony>>>;
parser_interface<eps_parser<detail::phony, parser_modifiers<>>>>;
template<
typename V,
@@ -595,7 +596,7 @@ namespace boost::parser {
Parser,
GlobalState,
ErrorHandler,
parser_interface<eps_parser<detail::phony>>>;
parser_interface<eps_parser<detail::phony, parser_modifiers<>>>>;
namespace detail {
template<
@@ -698,7 +699,8 @@ namespace boost::parser {
Parser,
GlobalState,
ErrorHandler,
parser_interface<eps_parser<detail::phony>>>
parser_interface<
eps_parser<detail::phony, parser_modifiers<>>>>
[[nodiscard]] constexpr auto operator()(
R && r,
parser_interface<Parser, GlobalState, ErrorHandler> const &
@@ -709,7 +711,8 @@ namespace boost::parser {
return (*this)(
(R &&)r,
parser,
parser_interface<eps_parser<detail::phony>>{},
parser_interface<
eps_parser<detail::phony, parser_modifiers<>>>{},
(F &&)f,
trace_mode);
}
@@ -754,10 +757,11 @@ namespace boost::parser {
std::is_same_v<Trace, trace>) {
// (r, parser, f, trace) case
return impl(
to_range<R>::call((R &&) r),
to_range<R>::call((R &&)r),
parser,
parser_interface<eps_parser<detail::phony>>{},
(SkipParser &&) skip,
parser_interface<
eps_parser<detail::phony, parser_modifiers<>>>{},
(SkipParser &&)skip,
f);
} else {
static_assert(

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,10 +185,14 @@ 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

@@ -4,6 +4,5 @@
"authors": [ "T. Zachary Laine" ],
"maintainers": [ "Zach Laine <whatwasthataddress -at- gmail.com>" ],
"description": "A parser combinator library.",
"category": [ "Parsing" ],
"cxxstd": "17"
"category": [ "Parsing" ]
}

View File

@@ -8,7 +8,6 @@ import testing ;
project
: requirements <library>/boost/charconv//boost_charconv
<library>/boost/parser//boost_parser
;
compile compile_all_t.cpp ;

View File

@@ -237,270 +237,6 @@ void github_issue_125()
}
}
void github_issue_209()
{
namespace bp = boost::parser;
BOOST_TEST(std::is_sorted(
std::begin(bp::detail::char_set<detail::punct_chars>::chars),
std::end(bp::detail::char_set<detail::punct_chars>::chars)));
BOOST_TEST(std::is_sorted(
std::begin(bp::detail::char_set<detail::symb_chars>::chars),
std::end(bp::detail::char_set<detail::symb_chars>::chars)));
BOOST_TEST(std::is_sorted(
std::begin(bp::detail::char_set<detail::lower_case_chars>::chars),
std::end(bp::detail::char_set<detail::lower_case_chars>::chars)));
BOOST_TEST(std::is_sorted(
std::begin(bp::detail::char_set<detail::upper_case_chars>::chars),
std::end(bp::detail::char_set<detail::upper_case_chars>::chars)));
}
void github_issue_223()
{
namespace bp = boost::parser;
// failing case
{
std::vector<char> v;
const auto parser = *('x' | bp::char_('y'));
bp::parse("xy", parser, bp::ws, v);
BOOST_TEST(v.size() == 1);
BOOST_TEST(v == std::vector<char>({'y'}));
// the assert fails since there are two elements in the vector: '\0'
// and 'y'. Seems pretty surprising to me
}
// working case
{
const auto parser = *('x' | bp::char_('y'));
const auto result = bp::parse("xy", parser, bp::ws);
BOOST_TEST(result->size() == 1);
BOOST_TEST(*(*result)[0] == 'y');
// success, the vector has only one 'y' element
}
}
namespace github_issue_248_ {
namespace bp = boost::parser;
static constexpr bp::rule<struct symbol, int> symbol = "//";
static constexpr bp::rule<struct vector, std::vector<int>> list =
"<int>(,<int>)*";
static constexpr bp::rule<struct working, std::vector<int>> working =
"working";
static constexpr bp::rule<struct failing, std::vector<int>> failing =
"failing";
static auto const symbol_def = bp::symbols<int>{{"//", 0}};
static constexpr auto list_def = bp::int_ % ',';
static constexpr auto working_def = -symbol >> (bp::int_ % ',');
static constexpr auto failing_def = -symbol >> list;
BOOST_PARSER_DEFINE_RULES(symbol, list, working, failing);
}
void github_issue_248()
{
namespace bp = boost::parser;
using namespace github_issue_248_;
{
auto const result = bp::parse("//1,2,3", working, bp::ws);
auto const expected = std::vector<int>{0, 1, 2, 3};
BOOST_TEST(result.has_value());
bool const equal = std::equal(
result->begin(), result->end(), expected.begin(), expected.end());
BOOST_TEST(equal);
if (!equal) {
std::cout << "contents of *result:\n";
for (auto x : *result) {
std::cout << x << '\n';
}
std::cout << '\n';
}
}
{
auto const result = bp::parse("//1,2,3", failing, bp::ws);
auto const expected = std::vector<int>{0, 1, 2, 3};
BOOST_TEST(result.has_value());
bool const equal = std::equal(
result->begin(), result->end(), expected.begin(), expected.end());
BOOST_TEST(equal);
if (!equal) {
std::cout << "contents of *result:\n";
for (auto x : *result) {
std::cout << x << '\n';
}
std::cout << '\n';
}
}
}
#if BOOST_PARSER_USE_CONCEPTS
namespace github_issue_268_ {
namespace bp = boost::parser;
constexpr bp::rule<struct name, std::string_view> name = "name";
auto name_def = bp::string_view[bp::lexeme[+(
bp::lower | bp::upper | bp::digit | bp::char_("_"))]];
BOOST_PARSER_DEFINE_RULES(name)
constexpr bp::rule<struct qd_vec, std::vector<double>> qd_vec = "qd_vec";
auto qd_vec_def = bp::lit("\"") >>
bp::double_ %
(bp::lit(",") |
(bp::lit("\"") >> bp::lit(",") >> bp::lit("\""))) >>
bp::lit('\"');
BOOST_PARSER_DEFINE_RULES(qd_vec)
struct lu_table_template_1
{
std::vector<double> index_1;
std::string_view variable_1;
};
constexpr boost::parser::
rule<struct lu_table_template_1_tag, lu_table_template_1>
lu_table_template_1_rule = "lu_table_template_1";
auto lu_table_template_1_rule_def = (bp::lit("index_1") >> '(' >> qd_vec >>
')' >> ';') >>
(bp::lit("variable_1") >> ':' >> name >>
';');
BOOST_PARSER_DEFINE_RULES(lu_table_template_1_rule)
constexpr boost::parser::
rule<struct lu_table_template_1_permut_tag, lu_table_template_1>
lu_table_template_1_permut_rule = "lu_table_template_1";
auto lu_table_template_1_permut_rule_def =
(bp::lit("index_1") >> '(' >> qd_vec >> ')' >> ';') ||
(bp::lit("variable_1") >> ':' >> name >> ';');
BOOST_PARSER_DEFINE_RULES(lu_table_template_1_permut_rule)
}
#endif
void github_issue_268()
{
#if BOOST_PARSER_USE_CONCEPTS
namespace bp = boost::parser;
using namespace github_issue_268_;
std::string inputstring = "index_1 ( \"1\" ) ; variable_1 : bier;";
auto const def_result = bp::parse(
inputstring, lu_table_template_1_rule_def, bp::blank, bp::trace::off);
std::cout << "seq_parser generates this type:\n"
<< typeid(def_result.value()).name() << std::endl;
BOOST_TEST(def_result);
auto const permut_def_result = bp::parse(
inputstring,
lu_table_template_1_permut_rule_def,
bp::blank,
bp::trace::off);
std::cout << "permut_parser generates this type:\n"
<< typeid(permut_def_result.value()).name() << std::endl;
BOOST_TEST(permut_def_result);
auto const result = bp::parse(
inputstring, lu_table_template_1_rule, bp::blank, bp::trace::off);
std::cout << "seq_parser in rule generates this type:\n"
<< typeid(result.value()).name() << std::endl;
BOOST_TEST(result);
auto const permut_result = bp::parse(
inputstring,
lu_table_template_1_permut_rule,
bp::blank,
bp::trace::off);
std::cout << "permut_parser generates this type:\n"
<< typeid(permut_result.value()).name() << std::endl;
BOOST_TEST(permut_result);
#endif
}
void github_issue_279()
{
namespace bp = boost::parser;
{
constexpr auto condition_clause =
bp::lit(U"while") > bp::lit(U"someexpression") >> bp::attr(true);
constexpr auto do_statement =
bp::lexeme[bp::lit(U"do") >> &bp::ws] > -condition_clause > bp::eol;
auto const result =
bp::parse(U"do\n", do_statement, bp::blank, bp::trace::off);
BOOST_TEST(result);
std::optional<bool> const & condition = result.value();
BOOST_TEST(!condition.has_value());
}
{
constexpr auto condition_clause =
bp::lit(U"while") > bp::lit(U"someexpression") >> bp::attr(true);
constexpr auto do_statement_reverse =
-condition_clause > bp::lexeme[bp::lit(U"do") >> &bp::ws] > bp::eol;
auto const result =
bp::parse(U"do\n", do_statement_reverse, bp::blank, bp::trace::off);
BOOST_TEST(result);
std::optional<bool> const & condition = result.value();
BOOST_TEST(!condition.has_value());
}
}
namespace github_issue_285_ {
namespace bp = boost::parser;
struct Content
{
~Content()
{
int setbreakpointhere = 0;
(void)setbreakpointhere;
}
};
constexpr bp::rule<struct content_tag, std::shared_ptr<Content>> content =
"content";
constexpr auto content_action = [](auto & ctx) {
std::shared_ptr<Content> & result = _val(ctx);
result = std::make_shared<Content>();
};
constexpr auto content_def =
(bp::lit(U"content") >> bp::eol)[content_action];
BOOST_PARSER_DEFINE_RULES(content);
}
void github_issue_285()
{
using namespace github_issue_285_;
namespace bp = boost::parser;
constexpr auto prolog = bp::lit(U"prolog") >> bp::eol;
constexpr auto epilog =
bp::no_case[bp::lexeme[bp::lit(U"epi") >> bp::lit(U"log")]] >> bp::eol;
constexpr auto full_parser = prolog >> content >> epilog;
std::string teststring =
"prolog\n"
"content\n"
"epilog\n";
// "content" produces a shared_ptr with the result.
// The "epilog" parser must not delete the result.
auto const result = bp::parse(teststring, full_parser, bp::blank);
BOOST_TEST(result);
BOOST_TEST(result.value().get() != nullptr);
}
int main()
{
@@ -510,11 +246,5 @@ int main()
github_issue_78();
github_issue_90();
github_issue_125();
github_issue_209();
github_issue_223();
github_issue_248();
github_issue_268();
github_issue_279();
github_issue_285();
return boost::report_errors();
}

View File

@@ -292,15 +292,6 @@ int main()
}
BOOST_TEST(parse(str, parser_1));
BOOST_TEST(!parse(str, parser_2));
{
BOOST_TEST(!parse(str, char_));
std::ostringstream err, warn;
stream_error_handler eh("", err, warn);
BOOST_TEST(!parse(str, with_error_handler(char_, eh)));
BOOST_TEST(
err.str() ==
"1:1: error: Expected end of input here:\nab\n ^\n");
}
}
{
std::string str = "ab";
@@ -2762,16 +2753,6 @@ 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,66 +88,5 @@ int main()
}
}
{
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();
return boost::report_errors();
}

View File

@@ -49,7 +49,7 @@ int main()
}
}
// different_quote
// different_char
{
constexpr auto parser = bp::quoted_string('\'');
@@ -75,18 +75,9 @@ 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_quote_with_escapes
// different_char_with_escapes
{
{
auto parser = bp::quoted_string('\'', cu_escapes);
@@ -128,58 +119,6 @@ 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("'\"");
@@ -232,15 +171,6 @@ 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
@@ -303,15 +233,6 @@ 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
@@ -323,7 +244,8 @@ int main()
assert(result1);
std::cout << *result1 << "\n"; // Prints: some text
auto result2 = bp::parse(R"("some \"text\"")", bp::quoted_string, bp::ws);
auto result2 =
bp::parse("\"some \\\"text\\\"\"", bp::quoted_string, bp::ws);
assert(result2);
std::cout << *result2 << "\n"; // Prints: some "text"
//]
@@ -357,16 +279,6 @@ 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();

View File

@@ -316,7 +316,7 @@ int main()
add_parser >> roman_numerals >> next_delete_parser >>
roman_numerals);
BOOST_TEST(result);
BOOST_TEST(*result == detail::hl::make_tuple(100, 100));
BOOST_TEST(*result == std::tuple(100, 100));
}
{

View File

@@ -156,6 +156,8 @@ int main()
<< "----------------------------------------\n";
PARSE(omit[char_]);
PARSE(omit[omit[char_]]);
PARSE(omit[*omit[char_]]);
std::cout << "\n\n"
<< "----------------------------------------\n"