mirror of
https://github.com/boostorg/parser.git
synced 2026-01-19 16:32:13 +00:00
Compare commits
62 Commits
boost-1.88
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c54307b95c | ||
|
|
5b3f122aa7 | ||
|
|
6db68bc7f7 | ||
|
|
00fe33d2f3 | ||
|
|
bb183cf5ee | ||
|
|
885595f7bd | ||
|
|
6826f957a1 | ||
|
|
647cec6683 | ||
|
|
c2ddd6e116 | ||
|
|
f7246de9db | ||
|
|
703a8afafe | ||
|
|
0eacce6080 | ||
|
|
5e61ba4e9e | ||
|
|
dc6144eeb4 | ||
|
|
d873d7ea80 | ||
|
|
1f5303c756 | ||
|
|
41e891dc95 | ||
|
|
159472ac6e | ||
|
|
bfa3e33372 | ||
|
|
8d7a64f7fe | ||
|
|
8c23054e07 | ||
|
|
11a5d9a872 | ||
|
|
d241bd7853 | ||
|
|
086241cbd9 | ||
|
|
e3a3cc8bf2 | ||
|
|
f789199743 | ||
|
|
f20c4cfb02 | ||
|
|
fcbc53ddce | ||
|
|
fed0a883ad | ||
|
|
bfc61fa963 | ||
|
|
68c306bf66 | ||
|
|
d5d080b9f2 | ||
|
|
03341ba32d | ||
|
|
7e69b27d7c | ||
|
|
17d76bc158 | ||
|
|
050b9ba800 | ||
|
|
125f12407b | ||
|
|
e4ba7c7a17 | ||
|
|
9a138a20f6 | ||
|
|
c32d594d64 | ||
|
|
c674e94c3d | ||
|
|
84ee288b02 | ||
|
|
39faa9ddbe | ||
|
|
b2927abc6c | ||
|
|
5d6d2f7b84 | ||
|
|
fd6c56df1b | ||
|
|
af41e6a7c2 | ||
|
|
0b93a586f1 | ||
|
|
ed9a06123b | ||
|
|
8ff46f394a | ||
|
|
8c9ad7bdb3 | ||
|
|
d8abe8f29e | ||
|
|
810adb43f6 | ||
|
|
5788fb6967 | ||
|
|
ec7df8a0af | ||
|
|
a93a1d2647 | ||
|
|
927f35f115 | ||
|
|
87617fdec0 | ||
|
|
ead639e630 | ||
|
|
a3ca1193b2 | ||
|
|
07153117ff | ||
|
|
6414f99e04 |
@@ -1,4 +1,4 @@
|
||||
name: macos-13 - Clang 14
|
||||
name: macos-15 - Clang
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -14,9 +14,9 @@ jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
cxx_std: [17]
|
||||
cxx_std: [17, 20]
|
||||
|
||||
runs-on: macos-13
|
||||
runs-on: macos-15
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
4
.github/workflows/ubuntu.yml
vendored
4
.github/workflows/ubuntu.yml
vendored
@@ -18,10 +18,6 @@ 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 }}
|
||||
|
||||
|
||||
24
build.jam
Normal file
24
build.jam
Normal file
@@ -0,0 +1,24 @@
|
||||
# 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
|
||||
;
|
||||
@@ -39,7 +39,7 @@ rule run_doxygen ( files * : name : expand ? )
|
||||
|
||||
}
|
||||
|
||||
run_doxygen [ glob $(here)/../../../boost/parser/*.hpp : $(here)/../../../boost/parser/concepts.hpp ] : "Headers" ;
|
||||
run_doxygen [ glob $(here)/../include/boost/parser/*.hpp : $(here)/../include/boost/parser/concepts.hpp ] : "Headers" ;
|
||||
|
||||
install images_standalone : [ glob *.png ] : <location>html/parser/img ;
|
||||
explicit images_standalone ;
|
||||
|
||||
@@ -562,7 +562,7 @@
|
||||
<constructor><parameter name="p"><paramtype>parser_type</paramtype></parameter><parameter name="gs"><paramtype>global_state_type</paramtype></parameter><parameter name="eh"><paramtype>error_handler_type</paramtype></parameter></constructor>
|
||||
</struct><struct name="perm_parser"><template>
|
||||
<template-type-parameter name="ParserTuple"/>
|
||||
</template><description><para>Applies each parsers in <computeroutput>ParserTuple</computeroutput>, an any order, stopping after all of them have matched the input. The parse succeeds iff all the parsers match, regardless of the order in which they do. The attribute produced is a <computeroutput>parser::tuple</computeroutput> containing the attributes of the subparsers, in their order of the parsers' appearance in <computeroutput>ParserTuple</computeroutput>, not the order of the parsers' matches. It is an error to specialize <computeroutput><classname alt="boost::parser::perm_parser">perm_parser</classname></computeroutput> with a <computeroutput>ParserTuple</computeroutput> template parameter that includes an <computeroutput><classname alt="boost::parser::eps_parser">eps_parser</classname></computeroutput>. </para></description><data-member name="parsers_"><type><classname>ParserTuple</classname></type></data-member>
|
||||
</template><description><para>Applies each parsers in <computeroutput>ParserTuple</computeroutput>, in any order, stopping after all of them have matched the input. The parse succeeds iff all the parsers match, regardless of the order in which they do. The attribute produced is a <computeroutput>parser::tuple</computeroutput> containing the attributes of the subparsers, in their order of the parsers' appearance in <computeroutput>ParserTuple</computeroutput>, not the order of the parsers' matches. It is an error to specialize <computeroutput><classname alt="boost::parser::perm_parser">perm_parser</classname></computeroutput> with a <computeroutput>ParserTuple</computeroutput> template parameter that includes an <computeroutput><classname alt="boost::parser::eps_parser">eps_parser</classname></computeroutput>. </para></description><data-member name="parsers_"><type><classname>ParserTuple</classname></type></data-member>
|
||||
<method-group name="public member functions">
|
||||
<method name="call" cv="const"><type><classname>auto</classname></type><template>
|
||||
<template-type-parameter name="Iter"/>
|
||||
@@ -597,7 +597,7 @@
|
||||
<method name="parser_interface"><type><classname>constexpr</classname> <classname>quoted_string_parser</classname>() <classname>return</classname></type><parameter name=""><paramtype><classname>quoted_string_parser</classname>(std::move(<classname>x</classname>))</paramtype></parameter></method>
|
||||
<method name="operator()" cv="const noexcept"><type><classname>constexpr</classname> <classname>auto</classname></type><template>
|
||||
<template-nontype-parameter name="R"><type><classname>parsable_range_like</classname></type></template-nontype-parameter>
|
||||
</template><parameter name="r"><paramtype><classname>R</classname> &&</paramtype></parameter><description><para>Returns a <computeroutput><classname alt="boost::parser::parser_interface">parser_interface</classname></computeroutput> containing a <computeroutput><classname alt="boost::parser::quoted_string_parser">quoted_string_parser</classname></computeroutput> that accepts any of the values in <computeroutput>r</computeroutput> as its quotation marks. If the input being matched during the parse is a a sequence of <computeroutput>char32_t</computeroutput>, the elements of <computeroutput>r</computeroutput> are transcoded from their presumed encoding to UTF-32 during the comparison. Otherwise, the character begin matched is directly compared to the elements of <computeroutput>r</computeroutput>. </para></description></method>
|
||||
</template><parameter name="r"><paramtype><classname>R</classname> &&</paramtype></parameter><description><para>Returns a <computeroutput><classname alt="boost::parser::parser_interface">parser_interface</classname></computeroutput> containing a <computeroutput><classname alt="boost::parser::quoted_string_parser">quoted_string_parser</classname></computeroutput> that accepts any of the values in <computeroutput>r</computeroutput> as its quotation marks. If the input being matched during the parse is a sequence of <computeroutput>char32_t</computeroutput>, the elements of <computeroutput>r</computeroutput> are transcoded from their presumed encoding to UTF-32 during the comparison. Otherwise, the character begin matched is directly compared to the elements of <computeroutput>r</computeroutput>. </para></description></method>
|
||||
<method name="operator()" cv="const noexcept"><type><classname>auto</classname></type><template>
|
||||
<template-type-parameter name="T"/>
|
||||
<template-type-parameter name="U"/>
|
||||
@@ -605,7 +605,7 @@
|
||||
<method name="operator()" cv="const noexcept"><type><classname>auto</classname></type><template>
|
||||
<template-nontype-parameter name="R"><type><classname>parsable_range_like</classname></type></template-nontype-parameter>
|
||||
<template-type-parameter name="T"/>
|
||||
</template><parameter name="r"><paramtype><classname>R</classname> &&</paramtype></parameter><parameter name="escapes"><paramtype><classname>symbols</classname>< <classname>T</classname> > <classname>const</classname> &</paramtype></parameter><description><para>Returns a <computeroutput><classname alt="boost::parser::parser_interface">parser_interface</classname></computeroutput> containing a <computeroutput><classname alt="boost::parser::quoted_string_parser">quoted_string_parser</classname></computeroutput> that accepts any of the values in <computeroutput>r</computeroutput> as its quotation marks. If the input being matched during the parse is a a sequence of <computeroutput>char32_t</computeroutput>, the elements of <computeroutput>r</computeroutput> are transcoded from their presumed encoding to UTF-32 during the comparison. Otherwise, the character begin matched is directly compared to the elements of <computeroutput>r</computeroutput>. <computeroutput>symbols</computeroutput> provides a list of strings that may appear after a backslash to form an escape sequence, and what character(s) each escape sequence represents. Note that <computeroutput>"\\"</tt> and <tt>"\ch"</computeroutput> are always valid escape sequences. </para></description></method>
|
||||
</template><parameter name="r"><paramtype><classname>R</classname> &&</paramtype></parameter><parameter name="escapes"><paramtype><classname>symbols</classname>< <classname>T</classname> > <classname>const</classname> &</paramtype></parameter><description><para>Returns a <computeroutput><classname alt="boost::parser::parser_interface">parser_interface</classname></computeroutput> containing a <computeroutput><classname alt="boost::parser::quoted_string_parser">quoted_string_parser</classname></computeroutput> that accepts any of the values in <computeroutput>r</computeroutput> as its quotation marks. If the input being matched during the parse is a sequence of <computeroutput>char32_t</computeroutput>, the elements of <computeroutput>r</computeroutput> are transcoded from their presumed encoding to UTF-32 during the comparison. Otherwise, the character begin matched is directly compared to the elements of <computeroutput>r</computeroutput>. <computeroutput>symbols</computeroutput> provides a list of strings that may appear after a backslash to form an escape sequence, and what character(s) each escape sequence represents. Note that <computeroutput>"\\"</tt> and <tt>"\ch"</computeroutput> are always valid escape sequences. </para></description></method>
|
||||
</method-group>
|
||||
</struct><struct name="repeat_directive"><template>
|
||||
<template-type-parameter name="MinType"/>
|
||||
@@ -1016,7 +1016,7 @@
|
||||
<template-type-parameter name="DelimiterParser"><default><classname alt="boost::parser::detail::nope">detail::nope</classname></default></template-type-parameter>
|
||||
<template-type-parameter name="MinType"><default><classname alt="boost::parser::parse_error">int64_t</classname></default></template-type-parameter>
|
||||
<template-type-parameter name="MaxType"><default><classname alt="boost::parser::parse_error">int64_t</classname></default></template-type-parameter>
|
||||
</template><description><para>Repeats the application of another parser <computeroutput>p</computeroutput> of type <computeroutput>Parser</computeroutput>, optionally applying another parser <computeroutput>d</computeroutput> of type <computeroutput>DelimiterParser</computeroutput> in between each pair of applications of <computeroutput>p</computeroutput>. The parse succeeds if <computeroutput>p</computeroutput> succeeds at least the minumum number of times, and <computeroutput>d</computeroutput> succeeds each time it is applied. The attribute produced is a sequence of the type of attribute produced by <computeroutput>Parser</computeroutput>. </para></description></struct><struct name="rule"><template>
|
||||
</template><description><para>Repeats the application of another parser <computeroutput>p</computeroutput> of type <computeroutput>Parser</computeroutput>, optionally applying another parser <computeroutput>d</computeroutput> of type <computeroutput>DelimiterParser</computeroutput> in between each pair of applications of <computeroutput>p</computeroutput>. The parse succeeds if <computeroutput>p</computeroutput> succeeds at least the minimum number of times, and <computeroutput>d</computeroutput> succeeds each time it is applied. The attribute produced is a sequence of the type of attribute produced by <computeroutput>Parser</computeroutput>. </para></description></struct><struct name="rule"><template>
|
||||
<template-type-parameter name="TagType"/>
|
||||
<template-type-parameter name="Attribute"><default>no_attribute</default></template-type-parameter>
|
||||
<template-type-parameter name="LocalState"><default>no_local_state</default></template-type-parameter>
|
||||
@@ -1049,7 +1049,7 @@
|
||||
</para></description></struct><struct name="transform_parser"><template>
|
||||
<template-type-parameter name="Parser"/>
|
||||
<template-type-parameter name="F"/>
|
||||
</template><description><para>Applies the given parser <computeroutput>p</computeroutput> of type <computeroutput>Parser</computeroutput>. The attribute produced by <computeroutput>p</computeroutput> is passed to the fiven invocable <computeroutput>f</computeroutput> of type <computeroutput>F</computeroutput>. <computeroutput>f</computeroutput> will only be invoked if <computeroutput>p</computeroutput> succeeds and sttributes are currently being generated. The parse succeeds iff <computeroutput>p</computeroutput> succeeds. The attribute produced is the the result of the call to <computeroutput>f</computeroutput>. </para></description></struct><struct name="uint_parser"><template>
|
||||
</template><description><para>Applies the given parser <computeroutput>p</computeroutput> of type <computeroutput>Parser</computeroutput>. The attribute produced by <computeroutput>p</computeroutput> is passed to the given invocable <computeroutput>f</computeroutput> of type <computeroutput>F</computeroutput>. <computeroutput>f</computeroutput> will only be invoked if <computeroutput>p</computeroutput> succeeds and attributes are currently being generated. The parse succeeds iff <computeroutput>p</computeroutput> succeeds. The attribute produced is the result of the call to <computeroutput>f</computeroutput>. </para></description></struct><struct name="uint_parser"><template>
|
||||
<template-type-parameter name="T"/>
|
||||
<template-nontype-parameter name="Radix"><type><classname>int</classname></type><default>10</default></template-nontype-parameter>
|
||||
<template-nontype-parameter name="MinDigits"><type><classname>int</classname></type><default>1</default></template-nontype-parameter>
|
||||
@@ -1112,7 +1112,7 @@
|
||||
</template><parameter name="context"><paramtype><classname>Context</classname> <classname>const</classname> &</paramtype></parameter><description><para>Returns a reference to one or more local values that the bottommost rule is declared to have; multiple values will be stored within a <computeroutput>parser::tuple</computeroutput>. Returns <computeroutput>none</computeroutput> if there is no bottommost rule, or if that rule has no locals. </para></description></function>
|
||||
<function name="_params"><type><classname>decltype</classname>(<classname>auto</classname>)</type><template>
|
||||
<template-type-parameter name="Context"/>
|
||||
</template><parameter name="context"><paramtype><classname>Context</classname> <classname>const</classname> &</paramtype></parameter><description><para>Returns a reference to one or more parameters passed to the bottommost rule <computeroutput>r</computeroutput>, by using <computeroutput>r</computeroutput> as <computeroutput>r.with(param0, param1, ... paramN)</computeroutput>; multiple values will be stored within a <computeroutput>parser::tuple</computeroutput>. Returns <computeroutput>none</computeroutput> if there is no bottommost rule, or if that rule was not given any parameters. </para></description></function>
|
||||
</template><parameter name="context"><paramtype><classname>Context</classname> <classname>const</classname> &</paramtype></parameter><description><para>Returns a reference to one or more parameters passed to the bottommost rule <computeroutput>r</computeroutput>, by using <computeroutput>r</computeroutput> as <computeroutput>r.with(param0, param1, ..., paramN)</computeroutput>; multiple values will be stored within a <computeroutput>parser::tuple</computeroutput>. Returns <computeroutput>none</computeroutput> if there is no bottommost rule, or if that rule was not given any parameters. </para></description></function>
|
||||
<function name="_globals"><type><classname>decltype</classname>(<classname>auto</classname>)</type><template>
|
||||
<template-type-parameter name="Context"/>
|
||||
</template><parameter name="context"><paramtype><classname>Context</classname> <classname>const</classname> &</paramtype></parameter><description><para>Returns a reference to the globals object associated with the top-level parser. Returns <computeroutput>none</computeroutput> if there is no associated globals object. </para></description></function>
|
||||
@@ -1512,7 +1512,7 @@ P, Q)</computeroutput>, and <computeroutput>search_all_view(E, P, Q, R)</compute
|
||||
<template-type-parameter name="GlobalState"/>
|
||||
<template-type-parameter name="ErrorHandler"/>
|
||||
<template-type-parameter name="SkipParser"/>
|
||||
</template><description><para>Produces a sequence of subranges of the underlying sequence of type <computeroutput>V</computeroutput>. the underlying sequence is split into subranges delimited by matches of the given parser, possibly using a given skip-parser. </para></description><struct name="iterator"><template>
|
||||
</template><description><para>Produces a sequence of subranges of the underlying sequence of type <computeroutput>V</computeroutput>. The underlying sequence is split into subranges delimited by matches of the given parser, possibly using a given skip-parser. </para></description><struct name="iterator"><template>
|
||||
<template-nontype-parameter name="Const"><type><classname>bool</classname></type></template-nontype-parameter>
|
||||
</template><typedef name="I"><type><emphasis>unspecified</emphasis></type></typedef>
|
||||
<typedef name="S"><type><emphasis>unspecified</emphasis></type></typedef>
|
||||
@@ -1767,21 +1767,21 @@ P, Q)</computeroutput>, and <computeroutput>split_view(E, P, Q, R)</computeroutp
|
||||
<namespace name="parser">
|
||||
<class name="utf16_view"><template>
|
||||
<template-nontype-parameter name="V"><type><emphasis>unspecified</emphasis></type><purpose><para>Constrained by <computeroutput>std::ranges::view<V></computeroutput>. Additionally, the value type of <computeroutput>V</computeroutput> must be <computeroutput>char</computeroutput>, <computeroutput>wchar_t</computeroutput>, <computeroutput>char8_t</computeroutput>, <computeroutput>char16_t</computeroutput>, or <computeroutput>char32_t</computeroutput>. </para></purpose></template-nontype-parameter>
|
||||
</template><description><para>A view that produces UTF-16 from an given sequence of UTF.</para><para>
|
||||
</template><description><para>A view that produces UTF-16 from a given sequence of UTF.</para><para>
|
||||
</para></description><method-group name="public member functions">
|
||||
</method-group>
|
||||
<constructor cv="= default"/>
|
||||
<constructor><parameter name="base"><paramtype>V</paramtype></parameter></constructor>
|
||||
</class><class name="utf32_view"><template>
|
||||
<template-nontype-parameter name="V"><type><emphasis>unspecified</emphasis></type><purpose><para>Constrained by <computeroutput>std::ranges::view<V></computeroutput>. Additionally, the value type of <computeroutput>V</computeroutput> must be <computeroutput>char</computeroutput>, <computeroutput>wchar_t</computeroutput>, <computeroutput>char8_t</computeroutput>, <computeroutput>char16_t</computeroutput>, or <computeroutput>char32_t</computeroutput>. </para></purpose></template-nontype-parameter>
|
||||
</template><description><para>A view that produces UTF-32 from an given sequence of UTF.</para><para>
|
||||
</template><description><para>A view that produces UTF-32 from a given sequence of UTF.</para><para>
|
||||
</para></description><method-group name="public member functions">
|
||||
</method-group>
|
||||
<constructor cv="= default"/>
|
||||
<constructor><parameter name="base"><paramtype>V</paramtype></parameter></constructor>
|
||||
</class><class name="utf8_view"><template>
|
||||
<template-nontype-parameter name="V"><type><emphasis>unspecified</emphasis></type><purpose><para>Constrained by <computeroutput>std::ranges::view<V></computeroutput>. Additionally, the value type of <computeroutput>V</computeroutput> must be <computeroutput>char</computeroutput>, <computeroutput>wchar_t</computeroutput>, <computeroutput>char8_t</computeroutput>, <computeroutput>char16_t</computeroutput>, or <computeroutput>char32_t</computeroutput>. </para></purpose></template-nontype-parameter>
|
||||
</template><description><para>A view that produces UTF-8 from an given sequence of UTF.</para><para>
|
||||
</template><description><para>A view that produces UTF-8 from a given sequence of UTF.</para><para>
|
||||
</para></description><method-group name="public member functions">
|
||||
</method-group>
|
||||
<constructor cv="= default"/>
|
||||
@@ -2142,4 +2142,4 @@ P, Q, R, S)</computeroutput>, respectively. </para></description></data-member>
|
||||
</namespace>
|
||||
</namespace>
|
||||
</header>
|
||||
</library-reference>
|
||||
</library-reference>
|
||||
|
||||
@@ -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,13 +261,12 @@ 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`. 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.
|
||||
apply the same rule to `int_ | eps`. 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
|
||||
@@ -299,7 +298,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 do clear its attribute.
|
||||
not know to 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.
|
||||
@@ -315,7 +314,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
|
||||
@@ -325,4 +324,24 @@ 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]
|
||||
|
||||
@@ -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-delmited 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-delimited list is: `int_ % ',' | attr(std::vector<int>)`. Without the "`| attr(...)`", at least one `int_` match would be required. ]]
|
||||
|
||||
[[ _ch_ ]
|
||||
[ Matches any single code point. ]
|
||||
@@ -138,7 +138,7 @@ the input they match unless otherwise stated in the table below.]
|
||||
[]]
|
||||
|
||||
[[ `_hex_digit_` ]
|
||||
[ Matches a single hexidecimal digit code point. ]
|
||||
[ Matches a single hexadecimal digit code point. ]
|
||||
[ The code point type in Unicode parsing, or `char` in non-Unicode parsing. See the entry for _ch_. ]
|
||||
[]]
|
||||
|
||||
@@ -230,7 +230,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 +270,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 +319,7 @@ the input they match unless otherwise stated in the table below.]
|
||||
|
||||
[[ `_if_np_(pred)[p]` ]
|
||||
[ Equivalent to `_e_(pred) >> p`. ]
|
||||
[ `std::optional<_ATTR_np_(p)>` ]
|
||||
[ `_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 +328,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. ]]
|
||||
[ Unlike the other entries in this table, _symbols_ is a type, not an object. Inside of skippers, all _symbols_ will appear empty. ]]
|
||||
|
||||
[[ _quot_str_ ]
|
||||
[ Matches `'"'`, followed by zero or more characters, followed by `'"'`. ]
|
||||
@@ -368,7 +368,7 @@ character type (or use _attr_ to do so).]
|
||||
]
|
||||
|
||||
[template table_combining_operations
|
||||
Here are all the operator overloaded for parsers. In the tables below:
|
||||
Here are all the operators overloaded for parsers. In the tables below:
|
||||
|
||||
* `c` is a character of type `char` or `char32_t`;
|
||||
|
||||
@@ -399,7 +399,7 @@ consume the input they match unless otherwise stated in the table below.]
|
||||
[[`p1 | p2`] [ Matches iff either `p1` matches or `p2` matches. ] [`std::variant<_ATTR_np_(p1), _ATTR_np_(p2)>` (See note.)] [ `|` is associative; `p1 | p2 | p3`, `(p1 | p2) | p3`, and `p1 | (p2 | p3)` are all equivalent. This attribute type only applies to the case where `p1` and `p2` both generate attributes, and where the attribute types are different; see _attr_gen_ for the full rules. ]]
|
||||
[[`p | c`] [ Equivalent to `p | lit(c)`. ] [`_ATTR_np_(p)`] []]
|
||||
[[`p | r`] [ Equivalent to `p | lit(r)`. ] [`_ATTR_np_(p)`] []]
|
||||
[[`p1 || p2`] [ Matches iff `p1` matches and `p2` matches, regardless of the order they match in. ] [`_bp_tup_<_ATTR_np_(p1), _ATTR_np_(p2)>`] [ `||` is associative; `p1 || p2 || p3`, `(p1 || p2) || p3`, and `p1 || (p2 || p3)` are all equivalent. It is an error to include a _e_ (conditional or non-conditional) in an `operator||` expression. Though the parsers are matched in any order, the attribute elements are always in the order written in the `operator||` expression. ]]
|
||||
[[`p1 || p2`] [ 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`] [ 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)`] []]
|
||||
@@ -505,7 +505,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 then, which will produce
|
||||
you parse with _ch_, you usually parse repetition of them, 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 +551,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_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p1)>`]]
|
||||
[[`p1 % p2`] [`std::string` if `_ATTR_np_(p1)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p1)>`]]
|
||||
|
||||
[[`p[a]`] [None.]]
|
||||
|
||||
[[`_rpt_np_(arg0)[p]`] [`std::string` if `_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p)>`]]
|
||||
[[`_rpt_np_(arg0, arg1)[p]`] [`std::string` if `_ATTR_np_(p)` is `char` or `char32_t`, otherwise `std::vector<_ATTR_np_(p)>`]]
|
||||
[[`_if_np_(pred)[p]`] [`std::optional<_ATTR_np_(p)>`]]
|
||||
[[`_if_np_(pred)[p]`] [`_ATTR_np_(p)`]]
|
||||
[[`_sw_np_(arg0)(arg1, p1)(arg2, p2)...`]
|
||||
[`std::variant<_ATTR_np_(p1), _ATTR_np_(p2), ...>`]]
|
||||
]
|
||||
|
||||
180
doc/tutorial.qbk
180
doc/tutorial.qbk
@@ -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
|
||||
@@ -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_ is not very interesting _emdash_ since the
|
||||
The effects of this call to _p_ are 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 find by found by Argument-Dependent Lookup. You will probably never need
|
||||
will be 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 of failure of
|
||||
_pass_ returns a reference to a `bool` indicating the success or 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 diagnstic text `"doubles"` so that _Parser_ knows
|
||||
* We've given `doubles` the diagnostic 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 will probably never have to write a
|
||||
[note The existence of _rs_ means that you 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 sequencecs that do not require any real
|
||||
sequences. If you have simple escape sequences 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
|
||||
@@ -837,10 +837,10 @@ escaped quote character, since those always work.
|
||||
[quoted_string_example_5]
|
||||
|
||||
Additionally, with each of the forms shown above, you can optionally provide a
|
||||
parser as a final argument, to will be used to parse each character inside the
|
||||
quotes. You have to provide an actual full parser here; you cannot provide a
|
||||
character or string literal. If you do not provide a character parser, _ch_
|
||||
is used.
|
||||
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]
|
||||
|
||||
@@ -901,7 +901,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 is was at the beginning of #3. This kind
|
||||
input position had returned to where it 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 +1159,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.
|
||||
|
||||
[mpte There are two versions of each of the _symbols_ `*_for_next_parse()`
|
||||
[note 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,22 +1248,25 @@ 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 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:
|
||||
_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:
|
||||
|
||||
constexpr parser_interface<int_parser<IntType>> hex_int;
|
||||
constexpr auto hex_int = bp::uint_.base<16>();
|
||||
|
||||
`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.
|
||||
You simply chain together the constraints you want to use, like
|
||||
`.base<16>().digits<2>()` or `.digits<4>().base<8>()`.
|
||||
|
||||
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 parser_interface<uint_parser<unsigned int, 16, 8, 8>> hex_int;
|
||||
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.
|
||||
|
||||
[endsect]
|
||||
|
||||
@@ -1283,7 +1286,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 thinkgs simple, but most of this
|
||||
(Let's consider just sequence parsers to keep things 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
|
||||
@@ -1293,7 +1296,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 it
|
||||
attribute type of the parser is, based on the positions and attributes of its
|
||||
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.
|
||||
@@ -1325,7 +1328,7 @@ Creates an _omt_p_.
|
||||
|
||||
[heading _raw_]
|
||||
|
||||
`_raw_np_[p]` changes the attribute from `_ATTR_np_(p)` to to a view that
|
||||
`_raw_np_[p]` changes the attribute from `_ATTR_np_(p)` 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
|
||||
@@ -1391,7 +1394,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_.
|
||||
@@ -1507,7 +1510,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 of failure of the
|
||||
attribute, simply returns `bool`; this indicates the success or failure of the
|
||||
parse.]
|
||||
|
||||
[warning _Parser_ assumes that all attributes are semi-regular (see
|
||||
@@ -1683,7 +1686,7 @@ eligible for combining via `OP`.
|
||||
|
||||
[heading Alternative parser attribute rules]
|
||||
|
||||
The rules for alternative parsers are much simpler. For an alternative parer
|
||||
The rules for alternative parsers are much simpler. For an alternative parser
|
||||
`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:
|
||||
@@ -1691,8 +1694,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 exacly once;
|
||||
* duplicates in the `std::variant` template parameters `<T1, T2, ..., Tn>` are
|
||||
removed; every type that appears does so exactly 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
|
||||
@@ -1787,7 +1790,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_. If forces all the attributes produced by the subparsers
|
||||
opposite of _merge_. It 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.
|
||||
|
||||
@@ -1841,7 +1844,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_ are can be ignored in
|
||||
template parameter. This means that _merge_ and _sep_ can be ignored in
|
||||
`operator>>` expressions much like parentheses are. Consider an example.
|
||||
|
||||
namespace bp = boost::parser;
|
||||
@@ -1864,7 +1867,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`. Out parser `parser` would normally return a _std_str_.
|
||||
the digits in `s`. Our 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
|
||||
@@ -1897,7 +1900,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 an single range object.
|
||||
may be an iterator/sentinel pair or a single range object.
|
||||
|
||||
* They each require forward iterability of the range to parse.
|
||||
|
||||
@@ -2238,7 +2241,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 want to parse in ASCII-only mode, or in some other
|
||||
[tip If you 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 *`.]
|
||||
|
||||
@@ -2262,7 +2265,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 you parser, you call _w_glb_ to create a
|
||||
use a particular globals object with your parser, you call _w_glb_ to create a
|
||||
new parser with the globals object in it:
|
||||
|
||||
struct globals_t
|
||||
@@ -2466,11 +2469,12 @@ defined somewhere.
|
||||
BOOST_PARSER_DEFINE_RULES(value);
|
||||
|
||||
Notice the two expectation points. One before `(value % ',')`, one before the
|
||||
final `'}'`. Later, you call parse in some input:
|
||||
final `'}'`. Later, you parse in some input:
|
||||
|
||||
bp::parse("{ 4, 5 a", value, bp::ws);
|
||||
|
||||
This runs should of the second expectation point, and produces output like this:
|
||||
This runs afoul of the second expectation point, and produces output like
|
||||
this:
|
||||
|
||||
[pre
|
||||
1:7: error: Expected '}' here:
|
||||
@@ -2490,8 +2494,8 @@ the earlier expectation:
|
||||
]
|
||||
|
||||
Not nearly as nice. The problem is that the expectation is on `(value %
|
||||
',')`. 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
|
||||
',')`. 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
|
||||
text in the right place.
|
||||
|
||||
namespace bp = boost::parser;
|
||||
@@ -2517,7 +2521,7 @@ message:
|
||||
]
|
||||
|
||||
The _r_ `value` might be useful elsewhere in our code, perhaps in another
|
||||
parser. It's diagnostic text is appropriate for those other potential uses.
|
||||
parser. Its diagnostic text is appropriate for those other potential uses.
|
||||
|
||||
[heading Recursive rules]
|
||||
|
||||
@@ -2568,7 +2572,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 in the evaluation of the recursive
|
||||
recursive rule step by step during the evaluation of the recursive
|
||||
instantiations.
|
||||
|
||||
Also, consider this rule:
|
||||
@@ -2577,7 +2581,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
|
||||
@@ -2585,7 +2589,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 consitutes a recursive rule. Each
|
||||
_Parser_ has a specific semantic for what constitutes 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
|
||||
@@ -2620,7 +2624,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
|
||||
@@ -2658,8 +2662,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 defaulted
|
||||
csontructed by the _r_, and made available within semantic actions used in the
|
||||
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
|
||||
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.
|
||||
@@ -2770,7 +2774,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 get
|
||||
Using __p_ can prevent you from having to write a bunch of lambdas that
|
||||
each get an argument out of the parse context using `_params_np_(ctx)[0_c]` or
|
||||
similar.
|
||||
|
||||
@@ -3157,7 +3161,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 is almost always works because
|
||||
`std::common_type` before comparing them. This 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"
|
||||
@@ -3399,9 +3403,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`. 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`), and at an unexpected end of input. 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
|
||||
@@ -3409,21 +3413,26 @@ 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 the `or_parser` might be a subparser within a parent
|
||||
`or_parser`.
|
||||
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.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
Worse still, knowing how far you got in the parse and which parser was active
|
||||
is not very useful. Consider this.
|
||||
@@ -3433,22 +3442,23 @@ 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 it's position, it would be the
|
||||
If we reported the farthest-reaching parser and its 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. 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 (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 `'}'`?
|
||||
|
||||
[important The bottom line is that you should build expectation points into
|
||||
your parsers using `operator>` as much as possible.]
|
||||
@@ -3551,7 +3561,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 becuse the error handler eventually goes into the parse context. The
|
||||
This is because 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
|
||||
@@ -3599,9 +3609,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. An an example, let's trace a parse using the JSON parser from
|
||||
matches. As 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;
|
||||
@@ -3786,10 +3796,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 as such replacements.
|
||||
useful for such replacements.
|
||||
|
||||
With the exception of allocating the name of the parser that was expected in a
|
||||
failed expectation situation, _Parser_ does not does not allocate unless you
|
||||
failed expectation situation, _Parser_ 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.
|
||||
|
||||
@@ -3797,7 +3807,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
|
||||
@@ -3863,9 +3873,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 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",
|
||||
- `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",
|
||||
which is pretty helpful.
|
||||
|
||||
[heading Have a simple test that you can run to find ill-formed-code-as-asserts]
|
||||
@@ -3944,7 +3954,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 to the
|
||||
`call()` overload. This is really common, since both overloads need to do 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
|
||||
|
||||
@@ -126,7 +126,7 @@ namespace json {
|
||||
}
|
||||
};
|
||||
|
||||
bp::parser_interface<bp::uint_parser<uint32_t, 16, 4, 4>> const hex_4_def;
|
||||
auto const hex_4_def = boost::parser::uint_.base<16>().digits<4>();
|
||||
|
||||
auto const escape_seq_def = "\\u" > hex_4;
|
||||
|
||||
|
||||
@@ -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,12 +151,10 @@ namespace json {
|
||||
}
|
||||
};
|
||||
|
||||
// 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 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>();
|
||||
|
||||
// We use > here instead of >>, because once we see \u, we know that
|
||||
// exactly four hex digits must follow -- no other production rule starts
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace boost { namespace parser {
|
||||
std::declval<bool &>(),
|
||||
std::declval<int &>(),
|
||||
std::declval<ErrorHandler const &>(),
|
||||
std::declval<detail::nope &>(),
|
||||
std::declval<detail::nope const &>(),
|
||||
std::declval<detail::symbol_table_tries_t &>(),
|
||||
std::declval<detail::pending_symbol_table_operations_t &>()));
|
||||
|
||||
|
||||
@@ -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 heve instead been ill-formed code, caught at compile time.
|
||||
places could have 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 thes kinds
|
||||
stack in your favorite debugger. However, if you want to make these 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` expends to a compile-time `static_assert()`.
|
||||
`BOOST_PARSER_ASSERT` expands 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 unavailble. */
|
||||
`BOOST_ASSERT` is unavailable. */
|
||||
# define BOOST_PARSER_ASSERT(condition)
|
||||
|
||||
/** Boost.Parser will automatically use concepts to constrain templates when
|
||||
|
||||
@@ -47,7 +47,10 @@ namespace boost::parser::detail {
|
||||
// One-byte fast path.
|
||||
if (cp < 0x100) {
|
||||
// ASCII letter fast path.
|
||||
if (0x41 <= cp && cp < 0x5a) {
|
||||
if (0x61 <= cp && cp <= 0x7a) {
|
||||
*out++ = cp;
|
||||
return out;
|
||||
} else if (0x41 <= cp && cp <= 0x5a) {
|
||||
*out++ = cp + 0x20;
|
||||
return out;
|
||||
} else if (cp == 0x00DF) {
|
||||
|
||||
@@ -24,7 +24,10 @@
|
||||
#define BOOST_PARSER_USE_CPP23_STD_RANGE_ADAPTOR_CLOSURE 0
|
||||
#endif
|
||||
|
||||
#if !BOOST_PARSER_USE_CPP23_STD_RANGE_ADAPTOR_CLOSURE && \
|
||||
#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__
|
||||
#define BOOST_PARSER_USE_LIBSTDCPP_GCC12_RANGE_ADAPTOR_CLOSURE 1
|
||||
@@ -198,6 +201,11 @@ 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>
|
||||
@@ -259,7 +267,7 @@ namespace boost::parser::detail { namespace stl_interfaces {
|
||||
template<typename F>
|
||||
struct closure : range_adaptor_closure<closure<F>>
|
||||
{
|
||||
constexpr closure(F f) : f_(f) {}
|
||||
constexpr closure(F f) : f_(std::move(f)) {}
|
||||
|
||||
#if BOOST_PARSER_DETAIL_STL_INTERFACES_USE_CONCEPTS
|
||||
template<typename T>
|
||||
@@ -327,7 +335,7 @@ namespace boost::parser::detail { namespace stl_interfaces {
|
||||
template<typename F>
|
||||
struct adaptor
|
||||
{
|
||||
constexpr adaptor(F f) : f_(f) {}
|
||||
constexpr adaptor(F f) : f_(std::move(f)) {}
|
||||
|
||||
template<typename... Args>
|
||||
constexpr auto operator()(Args &&... args) const
|
||||
|
||||
@@ -126,7 +126,7 @@ namespace boost::parser::detail::text::detail {
|
||||
else if constexpr (can_ref_view<R>)
|
||||
return ref_view(r);
|
||||
else
|
||||
return owning_view<T>(std::move(r));
|
||||
return owning_view<T>((R &&)r);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -88,10 +88,12 @@ namespace boost::parser::detail { namespace text {
|
||||
{
|
||||
V base_ = V();
|
||||
|
||||
template<bool Const>
|
||||
class iterator;
|
||||
// HACK: SentType is here to work around irritating big-3
|
||||
// implementation inconsistencies.
|
||||
template<bool Const>
|
||||
class sentinel;
|
||||
template<bool Const, typename SentType = sentinel<Const>>
|
||||
class iterator;
|
||||
|
||||
public:
|
||||
constexpr project_view()
|
||||
@@ -140,7 +142,7 @@ namespace boost::parser::detail { namespace text {
|
||||
#else
|
||||
template<typename V, typename F>
|
||||
#endif
|
||||
template<bool Const>
|
||||
template<bool Const, typename SentType>
|
||||
class project_view<V, F>::iterator
|
||||
: public boost::parser::detail::stl_interfaces::proxy_iterator_interface<
|
||||
iterator<Const>,
|
||||
@@ -161,7 +163,7 @@ namespace boost::parser::detail { namespace text {
|
||||
decltype(detail::function_for_tag<F>(0))
|
||||
#endif
|
||||
;
|
||||
using sentinel = project_view<V, F>::sentinel<Const>;
|
||||
using sentinel = SentType;
|
||||
|
||||
friend boost::parser::detail::stl_interfaces::access;
|
||||
iterator_type & base_reference() noexcept { return it_; }
|
||||
@@ -169,7 +171,7 @@ namespace boost::parser::detail { namespace text {
|
||||
|
||||
iterator_type it_ = iterator_type();
|
||||
|
||||
friend project_view<V, F>::sentinel<Const>;
|
||||
friend project_view<V, F>::template sentinel<Const>;
|
||||
|
||||
template<bool OtherConst>
|
||||
#if BOOST_PARSER_DETAIL_TEXT_USE_CONCEPTS
|
||||
|
||||
@@ -90,6 +90,9 @@ namespace boost { namespace parser {
|
||||
os << ":\n";
|
||||
|
||||
std::string underlining(std::distance(position.line_start, it), ' ');
|
||||
std::transform(position.line_start, it,
|
||||
underlining.begin(),
|
||||
[](auto c) { return c == '\t' ? '\t' : ' ';});
|
||||
detail::trace_input(os, position.line_start, it, false, 1u << 31);
|
||||
if (it == last) {
|
||||
os << '\n' << underlining << "^\n";
|
||||
|
||||
@@ -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 associcated filename. */
|
||||
not have an associated filename. */
|
||||
struct default_error_handler
|
||||
{
|
||||
constexpr default_error_handler() = default;
|
||||
|
||||
@@ -374,7 +374,10 @@ namespace boost { namespace parser {
|
||||
template<typename T>
|
||||
using print_type = typename print_t<T>::type;
|
||||
|
||||
template<typename R, typename Parser>
|
||||
struct null_parser
|
||||
{};
|
||||
|
||||
template<typename R, typename Parser, typename SkipParser = null_parser>
|
||||
struct attribute_impl;
|
||||
|
||||
// Utility types.
|
||||
@@ -412,7 +415,7 @@ namespace boost { namespace parser {
|
||||
}
|
||||
};
|
||||
|
||||
inline nope global_nope;
|
||||
inline nope const global_nope;
|
||||
|
||||
template<typename T>
|
||||
using parser_interface_tag_expr =
|
||||
@@ -433,7 +436,7 @@ namespace boost { namespace parser {
|
||||
typename I,
|
||||
typename S,
|
||||
typename ErrorHandler,
|
||||
typename GlobalState = nope,
|
||||
typename GlobalState = nope const,
|
||||
typename Callbacks = nope,
|
||||
typename Attr = nope,
|
||||
typename Val = nope,
|
||||
@@ -469,6 +472,37 @@ namespace boost { namespace parser {
|
||||
nope_or_pointer_t<Where, true> where_{};
|
||||
int no_case_depth_ = 0;
|
||||
|
||||
// These exist in order to provide an address, if requested, for
|
||||
// either kind of symbol table struct. The nonstatic member
|
||||
// pointers for these will be null if this context was created
|
||||
// inside of detail::skip(), but nothing prevents the user from
|
||||
// trying to use a symbol_parser anyway. So, we have these.
|
||||
static std::optional<symbol_table_tries_t>
|
||||
empty_symbol_table_tries_;
|
||||
static std::optional<pending_symbol_table_operations_t>
|
||||
empty_pending_symbol_table_operations_;
|
||||
|
||||
symbol_table_tries_t & get_symbol_table_tries() const
|
||||
{
|
||||
if (symbol_table_tries_)
|
||||
return *symbol_table_tries_;
|
||||
if (!empty_symbol_table_tries_)
|
||||
empty_symbol_table_tries_ = symbol_table_tries_t();
|
||||
return *empty_symbol_table_tries_;
|
||||
}
|
||||
|
||||
pending_symbol_table_operations_t &
|
||||
get_pending_symbol_table_operations() const
|
||||
{
|
||||
if (pending_symbol_table_operations_)
|
||||
return *pending_symbol_table_operations_;
|
||||
if (!empty_pending_symbol_table_operations_) {
|
||||
empty_pending_symbol_table_operations_ =
|
||||
pending_symbol_table_operations_t();
|
||||
}
|
||||
return *empty_pending_symbol_table_operations_;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static auto nope_or_address(T & x)
|
||||
{
|
||||
@@ -510,6 +544,23 @@ namespace boost { namespace parser {
|
||||
globals_(nope_or_address(globals))
|
||||
{}
|
||||
|
||||
parse_context(
|
||||
std::bool_constant<DoTrace>,
|
||||
std::bool_constant<UseCallbacks>,
|
||||
I & first,
|
||||
S last,
|
||||
bool & success,
|
||||
int & indent,
|
||||
ErrorHandler const & error_handler,
|
||||
GlobalState & globals) :
|
||||
first_(first),
|
||||
last_(last),
|
||||
pass_(std::addressof(success)),
|
||||
trace_indent_(std::addressof(indent)),
|
||||
error_handler_(std::addressof(error_handler)),
|
||||
globals_(nope_or_address(globals))
|
||||
{}
|
||||
|
||||
// With callbacks.
|
||||
parse_context(
|
||||
std::bool_constant<DoTrace>,
|
||||
@@ -628,6 +679,64 @@ namespace boost { namespace parser {
|
||||
{}
|
||||
};
|
||||
|
||||
template<
|
||||
bool DoTrace,
|
||||
bool UseCallbacks,
|
||||
typename I,
|
||||
typename S,
|
||||
typename ErrorHandler,
|
||||
typename GlobalState,
|
||||
typename Callbacks,
|
||||
typename Attr,
|
||||
typename Val,
|
||||
typename RuleTag,
|
||||
typename RuleLocals,
|
||||
typename RuleParams,
|
||||
typename Where>
|
||||
std::optional<symbol_table_tries_t> parse_context<
|
||||
DoTrace,
|
||||
UseCallbacks,
|
||||
I,
|
||||
S,
|
||||
ErrorHandler,
|
||||
GlobalState,
|
||||
Callbacks,
|
||||
Attr,
|
||||
Val,
|
||||
RuleTag,
|
||||
RuleLocals,
|
||||
RuleParams,
|
||||
Where>::empty_symbol_table_tries_;
|
||||
|
||||
template<
|
||||
bool DoTrace,
|
||||
bool UseCallbacks,
|
||||
typename I,
|
||||
typename S,
|
||||
typename ErrorHandler,
|
||||
typename GlobalState,
|
||||
typename Callbacks,
|
||||
typename Attr,
|
||||
typename Val,
|
||||
typename RuleTag,
|
||||
typename RuleLocals,
|
||||
typename RuleParams,
|
||||
typename Where>
|
||||
std::optional<pending_symbol_table_operations_t> parse_context<
|
||||
DoTrace,
|
||||
UseCallbacks,
|
||||
I,
|
||||
S,
|
||||
ErrorHandler,
|
||||
GlobalState,
|
||||
Callbacks,
|
||||
Attr,
|
||||
Val,
|
||||
RuleTag,
|
||||
RuleLocals,
|
||||
RuleParams,
|
||||
Where>::empty_pending_symbol_table_operations_;
|
||||
|
||||
template<
|
||||
bool DoTrace,
|
||||
bool UseCallbacks,
|
||||
@@ -747,7 +856,7 @@ namespace boost { namespace parser {
|
||||
bool & success,
|
||||
int & indent,
|
||||
ErrorHandler const & error_handler,
|
||||
nope & n,
|
||||
nope const & n,
|
||||
symbol_table_tries_t & symbol_table_tries,
|
||||
pending_symbol_table_operations_t &
|
||||
pending_symbol_table_operations) noexcept
|
||||
@@ -765,6 +874,33 @@ namespace boost { namespace parser {
|
||||
pending_symbol_table_operations);
|
||||
}
|
||||
|
||||
template<
|
||||
bool DoTrace,
|
||||
bool UseCallbacks,
|
||||
typename Iter,
|
||||
typename Sentinel,
|
||||
typename ErrorHandler>
|
||||
auto make_context(
|
||||
Iter first,
|
||||
Sentinel last,
|
||||
bool & success,
|
||||
int & indent,
|
||||
ErrorHandler const & error_handler,
|
||||
nope const & n,
|
||||
nope const &,
|
||||
nope const &) noexcept
|
||||
{
|
||||
return parse_context(
|
||||
std::bool_constant<DoTrace>{},
|
||||
std::bool_constant<UseCallbacks>{},
|
||||
first,
|
||||
last,
|
||||
success,
|
||||
indent,
|
||||
error_handler,
|
||||
n);
|
||||
}
|
||||
|
||||
template<
|
||||
bool DoTrace,
|
||||
bool UseCallbacks,
|
||||
@@ -1359,7 +1495,7 @@ namespace boost { namespace parser {
|
||||
auto const r = cps | text::as_utf8;
|
||||
c.insert(c.end(), r.begin(), r.end());
|
||||
} else {
|
||||
detail::insert(c, std::move(x));
|
||||
detail::insert(c, (T &&)x);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1466,9 +1602,6 @@ namespace boost { namespace parser {
|
||||
uint32_t(flags::in_apply_parser);
|
||||
}
|
||||
|
||||
struct null_parser
|
||||
{};
|
||||
|
||||
struct skip_skipper
|
||||
{
|
||||
template<
|
||||
@@ -1518,18 +1651,9 @@ namespace boost { namespace parser {
|
||||
bool success = true;
|
||||
int indent = 0;
|
||||
rethrow_error_handler eh;
|
||||
nope n;
|
||||
symbol_table_tries_t symbol_table_tries;
|
||||
pending_symbol_table_operations_t pending_symbol_table_operations;
|
||||
nope const n;
|
||||
auto const context = detail::make_context<false, false>(
|
||||
first,
|
||||
last,
|
||||
success,
|
||||
indent,
|
||||
eh,
|
||||
n,
|
||||
symbol_table_tries,
|
||||
pending_symbol_table_operations);
|
||||
first, last, success, indent, eh, n, n, n);
|
||||
while (success) {
|
||||
skip_(
|
||||
first,
|
||||
@@ -1548,7 +1672,7 @@ namespace boost { namespace parser {
|
||||
{
|
||||
std::optional<T> retval;
|
||||
if (success)
|
||||
retval = x;
|
||||
retval = std::move(x);
|
||||
return retval;
|
||||
}
|
||||
|
||||
@@ -1699,7 +1823,7 @@ namespace boost { namespace parser {
|
||||
using trie_t = text::trie_map<std::vector<char32_t>, T>;
|
||||
using result_type = std::pair<trie_t &, bool>;
|
||||
symbol_table_tries_t & symbol_table_tries =
|
||||
*context.symbol_table_tries_;
|
||||
context.get_symbol_table_tries();
|
||||
|
||||
auto & [any, has_case_folded] =
|
||||
symbol_table_tries[(void *)&sym_parser.ref()];
|
||||
@@ -1738,7 +1862,7 @@ namespace boost { namespace parser {
|
||||
Context const & context, symbol_parser<T> const & sym_parser)
|
||||
{
|
||||
void const * ptr = static_cast<void const *>(&sym_parser);
|
||||
auto & entry = (*context.pending_symbol_table_operations_)[ptr];
|
||||
auto & entry = (context.get_pending_symbol_table_operations())[ptr];
|
||||
std::vector<detail::symbol_table_operation<T>> * retval = nullptr;
|
||||
if (entry.visit_) {
|
||||
retval = std::any_cast<
|
||||
@@ -1951,9 +2075,9 @@ namespace boost { namespace parser {
|
||||
template<typename T, typename Tuple, int... Is>
|
||||
auto
|
||||
make_from_tuple_impl(Tuple && tup, std::integer_sequence<int, Is...>)
|
||||
-> decltype(T(parser::get(std::move(tup), llong<Is>{})...))
|
||||
-> decltype(T(parser::get((Tuple &&)tup, llong<Is>{})...))
|
||||
{
|
||||
return T(parser::get(std::move(tup), llong<Is>{})...);
|
||||
return T(parser::get((Tuple &&)tup, llong<Is>{})...);
|
||||
}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
@@ -1987,7 +2111,7 @@ namespace boost { namespace parser {
|
||||
auto const r = cps | text::as_utf8;
|
||||
c.insert(c.end(), r.begin(), r.end());
|
||||
} else if constexpr (std::is_convertible_v<just_u &&, just_t>) {
|
||||
detail::insert(c, std::move(x));
|
||||
detail::insert(c, (U &&)x);
|
||||
} else if constexpr (
|
||||
!is_tuple<just_t>::value && is_tuple<just_u>::value &&
|
||||
std::is_aggregate_v<just_t> &&
|
||||
@@ -1996,8 +2120,7 @@ namespace boost { namespace parser {
|
||||
auto int_seq =
|
||||
std::make_integer_sequence<int, tuple_size_<just_u>>();
|
||||
detail::insert(
|
||||
c,
|
||||
detail::tuple_to_aggregate<just_t>(std::move(x), int_seq));
|
||||
c, detail::tuple_to_aggregate<just_t>((U &&)x, int_seq));
|
||||
} else if constexpr (
|
||||
is_tuple<just_t>::value && !is_tuple<just_u>::value &&
|
||||
std::is_aggregate_v<just_u> &&
|
||||
@@ -2013,8 +2136,7 @@ namespace boost { namespace parser {
|
||||
} else if constexpr (is_constructible_from_tuple_v<
|
||||
just_t,
|
||||
just_u>) {
|
||||
detail::insert(
|
||||
c, detail::make_from_tuple<just_t>(std::move(x)));
|
||||
detail::insert(c, detail::make_from_tuple<just_t>((U &&)x));
|
||||
} else {
|
||||
static_assert(
|
||||
sizeof(U) && false,
|
||||
@@ -2029,7 +2151,7 @@ namespace boost { namespace parser {
|
||||
{
|
||||
if (!gen_attrs)
|
||||
return;
|
||||
detail::move_back_impl(c, std::move(x));
|
||||
detail::move_back_impl(c, (T &&)x);
|
||||
}
|
||||
|
||||
template<typename Container>
|
||||
@@ -2098,7 +2220,7 @@ namespace boost { namespace parser {
|
||||
"is almost certainly not what you meant to write, so "
|
||||
"Boost.Parser disallows it. If you want to do this, write "
|
||||
"a semantic action and do it explicitly.");
|
||||
t = std::move(u);
|
||||
t = (U &&)u;
|
||||
} else if constexpr (
|
||||
!is_tuple<just_t>::value && is_tuple<just_u>::value &&
|
||||
std::is_aggregate_v<just_t> &&
|
||||
@@ -2106,7 +2228,7 @@ namespace boost { namespace parser {
|
||||
is_struct_assignable_v<just_t, just_u>) {
|
||||
auto int_seq =
|
||||
std::make_integer_sequence<int, tuple_size_<just_u>>();
|
||||
t = detail::tuple_to_aggregate<just_t>(std::move(u), int_seq);
|
||||
t = detail::tuple_to_aggregate<just_t>((U &&)u, int_seq);
|
||||
} else if constexpr (
|
||||
is_tuple<just_t>::value && !is_tuple<just_u>::value &&
|
||||
std::is_aggregate_v<just_u> &&
|
||||
@@ -2120,7 +2242,7 @@ namespace boost { namespace parser {
|
||||
} else if constexpr (is_constructible_from_tuple_v<
|
||||
just_t,
|
||||
just_u>) {
|
||||
t = detail::make_from_tuple<just_t>(std::move(u));
|
||||
t = detail::make_from_tuple<just_t>((U &&)u);
|
||||
} else {
|
||||
static_assert(
|
||||
sizeof(T) && false,
|
||||
@@ -2541,7 +2663,7 @@ namespace boost { namespace parser {
|
||||
detail::skip(first, last, skip, flags);
|
||||
using attr_t = typename detail::attribute_impl<
|
||||
BOOST_PARSER_SUBRANGE<std::remove_const_t<Iter>, Sentinel>,
|
||||
Parser>::type;
|
||||
Parser, SkipParser>::type;
|
||||
try {
|
||||
attr_t attr_ =
|
||||
parser(first, last, context, skip, flags, success);
|
||||
@@ -2715,20 +2837,28 @@ namespace boost { namespace parser {
|
||||
}
|
||||
}
|
||||
|
||||
template<typename I, typename S, typename T>
|
||||
std::optional<T>
|
||||
if_full_parse(I & first, S last, std::optional<T> retval)
|
||||
template<typename I, typename S, typename ErrorHandler, typename T>
|
||||
T if_full_parse(
|
||||
I initial_first,
|
||||
I & first,
|
||||
S last,
|
||||
ErrorHandler const & error_handler,
|
||||
T retval)
|
||||
{
|
||||
if (first != last)
|
||||
retval = std::nullopt;
|
||||
return retval;
|
||||
}
|
||||
template<typename I, typename S>
|
||||
bool if_full_parse(I & first, S last, bool retval)
|
||||
{
|
||||
if (first != last)
|
||||
retval = false;
|
||||
return retval;
|
||||
if (first != last) {
|
||||
if (retval && error_handler(
|
||||
initial_first,
|
||||
last,
|
||||
parse_error<I>(first, "end of input")) ==
|
||||
error_handler_result::rethrow) {
|
||||
throw;
|
||||
}
|
||||
if constexpr (std::is_same_v<T, bool>)
|
||||
retval = false;
|
||||
else
|
||||
retval = std::nullopt;
|
||||
}
|
||||
return std::move(retval);
|
||||
}
|
||||
|
||||
// The notion of comaptibility is that, given a parser with the
|
||||
@@ -2801,6 +2931,8 @@ namespace boost { namespace parser {
|
||||
{
|
||||
if constexpr (is_nope_v<ParserAttr>) {
|
||||
return nope{};
|
||||
} else if constexpr (is_optional_v<ParserAttr>) {
|
||||
return ParserAttr{};
|
||||
} else {
|
||||
using value_type = range_value_t<GivenContainerAttr>;
|
||||
return std::conditional_t<
|
||||
@@ -3227,6 +3359,8 @@ namespace boost { namespace parser {
|
||||
|
||||
//[ opt_parser_gen_attr_path
|
||||
parser_.call(first, last, context, skip, flags, success, retval);
|
||||
if (!success)
|
||||
retval = Attribute();
|
||||
success = true;
|
||||
//]
|
||||
}
|
||||
@@ -3379,7 +3513,7 @@ namespace boost { namespace parser {
|
||||
|
||||
bool done = false;
|
||||
auto try_parser = [prev_first = first,
|
||||
use_parser,
|
||||
&use_parser,
|
||||
&success,
|
||||
flags,
|
||||
&retval,
|
||||
@@ -3395,7 +3529,9 @@ namespace boost { namespace parser {
|
||||
else
|
||||
use_parser.first_ = prev_first;
|
||||
};
|
||||
detail::hl::for_each(parsers_, try_parser); // TODO: -> fold-expr
|
||||
std::apply([&try_parser](auto&&... args) {
|
||||
((try_parser(args)), ...);
|
||||
}, parsers_);
|
||||
|
||||
if (!done)
|
||||
success = false;
|
||||
@@ -3546,9 +3682,7 @@ namespace boost { namespace parser {
|
||||
call(first, last, context, skip, flags, success, attr);
|
||||
if (success)
|
||||
detail::assign(retval, std::move(attr));
|
||||
} else if constexpr (
|
||||
detail::is_tuple<Attribute>{} ||
|
||||
detail::is_struct_compatible_v<Attribute, result_t>) {
|
||||
} else if constexpr (detail::is_tuple<Attribute>{}) {
|
||||
call_impl(
|
||||
first,
|
||||
last,
|
||||
@@ -3561,9 +3695,9 @@ namespace boost { namespace parser {
|
||||
|
||||
if (!success)
|
||||
detail::assign(retval, Attribute());
|
||||
} else if constexpr (detail::is_constructible_from_tuple_v<
|
||||
Attribute,
|
||||
result_t>) {
|
||||
} else if constexpr (
|
||||
detail::is_struct_compatible_v<Attribute, result_t> ||
|
||||
detail::is_constructible_from_tuple_v<Attribute, result_t>) {
|
||||
result_t temp_retval{};
|
||||
call_impl(
|
||||
first,
|
||||
@@ -3576,10 +3710,16 @@ namespace boost { namespace parser {
|
||||
indices);
|
||||
|
||||
if (success && detail::gen_attrs(flags)) {
|
||||
detail::assign(
|
||||
retval,
|
||||
detail::make_from_tuple<Attribute>(
|
||||
std::move(temp_retval)));
|
||||
if constexpr (detail::is_struct_compatible_v<
|
||||
Attribute,
|
||||
result_t>) {
|
||||
detail::assign(retval, temp_retval);
|
||||
} else {
|
||||
detail::assign(
|
||||
retval,
|
||||
detail::make_from_tuple<Attribute>(
|
||||
std::move(temp_retval)));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
#if 0 // TODO Seems incompatible with this parser.
|
||||
@@ -4342,11 +4482,11 @@ namespace boost { namespace parser {
|
||||
&success,
|
||||
&retval](auto const &
|
||||
parser_index_merged_and_backtrack) {
|
||||
if (!success) // Someone earlier already failed...
|
||||
return;
|
||||
auto flags = flags_;
|
||||
using namespace literals;
|
||||
detail::skip(first, last, skip, flags);
|
||||
if (!success) // Someone earlier already failed...
|
||||
return;
|
||||
|
||||
auto const & parser =
|
||||
parser::get(parser_index_merged_and_backtrack, 0_c);
|
||||
@@ -4395,7 +4535,8 @@ namespace boost { namespace parser {
|
||||
|
||||
if constexpr (
|
||||
(out_container == attr_container &&
|
||||
!was_merged_into_adjacent_container) ||
|
||||
!was_merged_into_adjacent_container &&
|
||||
!detail::is_nope_v<attr_t>) ||
|
||||
is_in_a_group) {
|
||||
parser.call(
|
||||
first, last, context, skip, flags, success, out);
|
||||
@@ -4421,7 +4562,9 @@ namespace boost { namespace parser {
|
||||
}
|
||||
using just_x = attr_t;
|
||||
using just_out = detail::remove_cv_ref_t<decltype(out)>;
|
||||
if constexpr (
|
||||
if constexpr (detail::is_nope_v<attr_t>) {
|
||||
// nothing to do
|
||||
} else if constexpr (
|
||||
(!out_container ||
|
||||
!std::is_same_v<just_x, just_out>) &&
|
||||
std::is_assignable_v<just_out &, just_x &&> &&
|
||||
@@ -4437,7 +4580,9 @@ namespace boost { namespace parser {
|
||||
|
||||
auto const parsers_and_indices =
|
||||
detail::hl::zip(parsers_, indices, merged, backtracking{});
|
||||
detail::hl::for_each(parsers_and_indices, use_parser);
|
||||
std::apply([&use_parser](auto&&... args) {
|
||||
((use_parser(args)), ...);
|
||||
}, parsers_and_indices);
|
||||
}
|
||||
|
||||
template<bool AllowBacktracking, typename Parser>
|
||||
@@ -4594,7 +4739,7 @@ namespace boost { namespace parser {
|
||||
} else {
|
||||
// If you see an error here, it's because you are using an
|
||||
// invocable for a semantic action that returns a non-void
|
||||
// type Ret, but values fo type Ret is not assignable to
|
||||
// type Ret, but values of type Ret is not assignable to
|
||||
// _val(ctx). To fix this, only use this invocable within
|
||||
// a rule whose attribute type is assignable from Ret, or
|
||||
// remove the non-void return statement(s) from your
|
||||
@@ -5401,9 +5546,28 @@ namespace boost { namespace parser {
|
||||
if constexpr (CanUseCallbacks && Context::use_callbacks) {
|
||||
call(first, last, context, skip, flags, success);
|
||||
} else {
|
||||
auto attr = call(first, last, context, skip, flags, success);
|
||||
if (success)
|
||||
detail::assign(retval, std::move(attr));
|
||||
locals_type locals = detail::make_locals<locals_type>(context);
|
||||
auto params = detail::resolve_rule_params(context, params_);
|
||||
tag_type * const tag_ptr = nullptr;
|
||||
auto const rule_context = detail::make_rule_context(
|
||||
context, tag_ptr, retval, locals, params);
|
||||
|
||||
[[maybe_unused]] auto _ = detail::scoped_trace(
|
||||
*this, first, last, rule_context, flags, retval);
|
||||
|
||||
bool dont_assign = false;
|
||||
parse_rule(
|
||||
tag_ptr,
|
||||
first,
|
||||
last,
|
||||
rule_context,
|
||||
skip,
|
||||
flags,
|
||||
success,
|
||||
dont_assign,
|
||||
retval);
|
||||
if (!success || dont_assign)
|
||||
retval = Attribute_();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5413,6 +5577,29 @@ namespace boost { namespace parser {
|
||||
|
||||
#endif
|
||||
|
||||
namespace detail {
|
||||
template<typename T>
|
||||
using base_member_function_template_expr =
|
||||
decltype(std::declval<T>().template base<2>());
|
||||
template<typename T>
|
||||
constexpr bool has_base_member_function_template_v =
|
||||
is_detected_v<base_member_function_template_expr, T>;
|
||||
|
||||
template<typename T>
|
||||
using has_digits1_member_function_template_expr =
|
||||
decltype(std::declval<T>().template digits<1>());
|
||||
template<typename T>
|
||||
constexpr bool has_digits1_member_function_template_v =
|
||||
is_detected_v<has_digits1_member_function_template_expr, T>;
|
||||
|
||||
template<typename T>
|
||||
using has_digits2_member_function_template_expr =
|
||||
decltype(std::declval<T>().template digits<1, 2>());
|
||||
template<typename T>
|
||||
constexpr bool has_digits2_member_function_template_v =
|
||||
is_detected_v<has_digits2_member_function_template_expr, T>;
|
||||
}
|
||||
|
||||
// Parser interface.
|
||||
|
||||
template<typename Parser, typename GlobalState, typename ErrorHandler>
|
||||
@@ -5752,7 +5939,7 @@ namespace boost { namespace parser {
|
||||
return parser_.call(first, last, context, skip, flags, success);
|
||||
}
|
||||
|
||||
/** Applies `parser_`, assiging the parsed attribute, if any, to
|
||||
/** Applies `parser_`, assinging the parsed attribute, if any, to
|
||||
`attr`, unless the attribute is reported via callback. */
|
||||
template<
|
||||
typename Iter,
|
||||
@@ -5772,6 +5959,60 @@ namespace boost { namespace parser {
|
||||
parser_.call(first, last, context, skip, flags, success, attr);
|
||||
}
|
||||
|
||||
/** Returns a new `parser_interface` constructed from
|
||||
`parser_.base<Radix2>()`. Note that this only works for integral
|
||||
numeric parsers like `int_` and `uint_`. */
|
||||
template<int Radix2>
|
||||
constexpr auto base() const noexcept
|
||||
{
|
||||
if constexpr (detail::has_base_member_function_template_v<
|
||||
parser_type>) {
|
||||
return parser::parser_interface{
|
||||
parser_.template base<Radix2>()};
|
||||
} else {
|
||||
static_assert(
|
||||
detail::has_base_member_function_template_v<parser_type>,
|
||||
"Only certain parsers have a .base<>() member function. "
|
||||
"This is not one of them.");
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns a new `parser_interface` constructed from
|
||||
`parser_.digits<Digits>()`. Note that this only works for
|
||||
integral numeric parsers like `int_` and `uint_`. */
|
||||
template<int Digits>
|
||||
constexpr auto digits() const noexcept
|
||||
{
|
||||
if constexpr (detail::has_digits1_member_function_template_v<
|
||||
parser_type>) {
|
||||
return parser::parser_interface{
|
||||
parser_.template digits<Digits>()};
|
||||
} else {
|
||||
static_assert(
|
||||
detail::has_digits1_member_function_template_v<parser_type>,
|
||||
"Only certain parsers have a .base<>() member function. "
|
||||
"This is not one of them.");
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns a new `parser_interface` constructed from
|
||||
`parser_.digits<MinDigits2, MaxDigits2>()`. Note that this only
|
||||
works for integral numeric parsers like `int_` and `uint_`. */
|
||||
template<int MinDigits2, int MaxDigits2>
|
||||
constexpr auto digits() const noexcept
|
||||
{
|
||||
if constexpr (detail::has_digits2_member_function_template_v<
|
||||
parser_type>) {
|
||||
return parser::parser_interface{
|
||||
parser_.template digits<MinDigits2, MaxDigits2>()};
|
||||
} else {
|
||||
static_assert(
|
||||
detail::has_digits2_member_function_template_v<parser_type>,
|
||||
"Only certain parsers have a .base<>() member function. "
|
||||
"This is not one of them.");
|
||||
}
|
||||
}
|
||||
|
||||
parser_type parser_;
|
||||
global_state_type globals_;
|
||||
error_handler_type error_handler_;
|
||||
@@ -5782,7 +6023,7 @@ namespace boost { namespace parser {
|
||||
};
|
||||
|
||||
/** Returns a `parser_interface` with the same parser and error handler,
|
||||
with `globals` added. The resut of passing any non-top-level parser
|
||||
with `globals` added. The result of passing any non-top-level parser
|
||||
for the `parser` argument is undefined. */
|
||||
template<typename Parser, typename GlobalState, typename ErrorHandler>
|
||||
auto with_globals(
|
||||
@@ -5794,7 +6035,7 @@ namespace boost { namespace parser {
|
||||
}
|
||||
|
||||
/** Returns a `parser_interface` with the same parser and globals, with
|
||||
`error_handler` added. The resut of passing any non-top-level parser
|
||||
`error_handler` added. The result of passing any non-top-level parser
|
||||
for the `parser` argument is undefined. */
|
||||
template<typename Parser, typename GlobalState, typename ErrorHandler>
|
||||
auto with_error_handler(
|
||||
@@ -5809,7 +6050,7 @@ namespace boost { namespace parser {
|
||||
|
||||
/** A `symbols<T>` represents the initial state of a symbol table parser
|
||||
that produces attributes of type `T`. The entries in the symbol table
|
||||
can be changed during parsing, but those mutations to not affect the
|
||||
can be changed during parsing, but those mutations do not affect the
|
||||
`symbols<T>` object itself; all mutations happen to a copy of the
|
||||
symbol table in the parse context. For table entries that should be
|
||||
used during every parse, add entries via `add()` or `operator()`. For
|
||||
@@ -5907,7 +6148,7 @@ namespace boost { namespace parser {
|
||||
}
|
||||
|
||||
/** Inserts an entry consisting of a UTF-8 string to match `str`, and
|
||||
an associtated attribute `x`, to the copy of the symbol table
|
||||
an associated attribute `x`, to the copy of the symbol table
|
||||
inside the parse context `context`. */
|
||||
template<typename Context>
|
||||
void insert(Context const & context, std::string_view str, T x) const
|
||||
@@ -6063,7 +6304,7 @@ namespace boost { namespace parser {
|
||||
SkipParser const & skip, \
|
||||
boost::parser::detail::flags flags, \
|
||||
bool & success, \
|
||||
bool & dont_assign, \
|
||||
bool & /*dont_assign*/, \
|
||||
Attribute & retval) \
|
||||
{ \
|
||||
auto const & parser = BOOST_PARSER_PP_CAT(rule_name_, _def); \
|
||||
@@ -6246,7 +6487,7 @@ namespace boost { namespace parser {
|
||||
|
||||
// Directives.
|
||||
|
||||
/** Represents a unparameterized higher-order parser (e.g. `omit_parser`)
|
||||
/** Represents an unparameterized higher-order parser (e.g. `omit_parser`)
|
||||
as a directive (e.g. `omit[other_parser]`). */
|
||||
template<template<class> class Parser>
|
||||
struct directive
|
||||
@@ -6324,7 +6565,7 @@ namespace boost { namespace parser {
|
||||
}
|
||||
|
||||
/** A directive that represents a `perm_parser`, where the items parsed
|
||||
are delimited by `DelimiterParser`,
|
||||
are delimited by `DelimiterParser`
|
||||
(e.g. `delimiter(delimter_parser)[some_perm_parser]`). This directive
|
||||
only applies to `perm_parser`s. */
|
||||
template<typename DelimiterParser>
|
||||
@@ -6463,7 +6704,7 @@ namespace boost { namespace parser {
|
||||
/** Returns a `transform_directive` that uses invocable `F` to do its
|
||||
work. */
|
||||
template<typename F>
|
||||
auto transform(F f)
|
||||
constexpr auto transform(F f)
|
||||
{
|
||||
return transform_directive<F>{std::move(f)};
|
||||
}
|
||||
@@ -7127,7 +7368,7 @@ namespace boost { namespace parser {
|
||||
|
||||
/** The single-character parser. The produced attribute is the type of
|
||||
the matched code point (`char` or `char32_t`). Used as-is, `char_`
|
||||
matches any code point. `char_` can also can be used to create code
|
||||
matches any code point. `char_` can also be used to create code
|
||||
point parsers that match one or more specific code point values, by
|
||||
calling it with: a single value comparable to a code point; a closed
|
||||
range of code point values `[lo, hi]`, or a set of code point values
|
||||
@@ -7138,7 +7379,7 @@ namespace boost { namespace parser {
|
||||
inline constexpr parser_interface<char_parser<detail::nope>> char_;
|
||||
|
||||
/** The code point parser. It produces a `char32_t` attribute. Used
|
||||
as-is, `cp` matches any code point. `cp` can also can be used to
|
||||
as-is, `cp` matches any code point. `cp` can also be used to
|
||||
create code point parsers that match one or more specific code point
|
||||
values, by calling it with: a single value comparable to a code point;
|
||||
a closed range of code point values `[lo, hi]`, or a set of code point
|
||||
@@ -7398,9 +7639,10 @@ namespace boost { namespace parser {
|
||||
|
||||
auto const prev_first = first;
|
||||
|
||||
auto append = [&retval,
|
||||
std::string temp;
|
||||
auto append = [&temp,
|
||||
gen_attrs = detail::gen_attrs(flags)](auto & ctx) {
|
||||
detail::move_back(retval, _attr(ctx), gen_attrs);
|
||||
detail::move_back(temp, _attr(ctx), gen_attrs);
|
||||
};
|
||||
|
||||
auto quote_ch = [&]() {
|
||||
@@ -7458,7 +7700,10 @@ namespace boost { namespace parser {
|
||||
detail::disable_skip(flags),
|
||||
success);
|
||||
|
||||
if (!success) {
|
||||
if (success) {
|
||||
if (detail::gen_attrs(flags))
|
||||
detail::assign(retval, std::move(temp));
|
||||
} else {
|
||||
retval = Attribute();
|
||||
first = prev_first;
|
||||
}
|
||||
@@ -7797,8 +8042,8 @@ namespace boost { namespace parser {
|
||||
few of which are Latin. */
|
||||
inline constexpr parser_interface<digit_parser> digit;
|
||||
|
||||
/** The hexidecimal digit parser. Matches the full set of Unicode
|
||||
hexidecimal digits (upper or lower case); in other words, all Unicode
|
||||
/** The hexadecimal digit parser. Matches the full set of Unicode
|
||||
hexadecimal digits (upper or lower case); in other words, all Unicode
|
||||
code points with the "Hex_Digit" character property. */
|
||||
inline constexpr parser_interface<
|
||||
char_subrange_parser<detail::hex_digit_subranges>>
|
||||
@@ -7830,7 +8075,7 @@ namespace boost { namespace parser {
|
||||
lower;
|
||||
|
||||
/** The lower case character parser. Matches the full set of Unicode
|
||||
lower case code points (class "Lu"). */
|
||||
upper case code points (class "Lu"). */
|
||||
inline BOOST_PARSER_ALGO_CONSTEXPR
|
||||
parser_interface<char_set_parser<detail::upper_case_chars>>
|
||||
upper;
|
||||
@@ -7918,7 +8163,11 @@ namespace boost { namespace parser {
|
||||
typename Expected>
|
||||
struct uint_parser
|
||||
{
|
||||
static_assert(2 <= Radix && Radix <= 36, "Unsupported radix.");
|
||||
static_assert(
|
||||
Radix == 2 || Radix == 8 || Radix == 10 || Radix == 16,
|
||||
"Unsupported radix.");
|
||||
static_assert(1 <= MinDigits);
|
||||
static_assert(MaxDigits == -1 || MinDigits <= MaxDigits);
|
||||
|
||||
constexpr uint_parser() {}
|
||||
explicit constexpr uint_parser(Expected expected) : expected_(expected)
|
||||
@@ -7984,6 +8233,33 @@ namespace boost { namespace parser {
|
||||
return parser_interface{parser_t{expected}};
|
||||
}
|
||||
|
||||
/** Returns a `uint_parser` identical to `*this`, except that it
|
||||
parses digits as base-`Radix2` instead of base-`Radix`. */
|
||||
template<int Radix2>
|
||||
constexpr auto base() const noexcept
|
||||
{
|
||||
return uint_parser<T, Radix2, MinDigits, MaxDigits, Expected>{
|
||||
expected_};
|
||||
}
|
||||
|
||||
/** Returns a `uint_parser` identical to `*this`, except that it only
|
||||
accepts numbers exactly `Digits` digits. */
|
||||
template<int Digits>
|
||||
constexpr auto digits() const noexcept
|
||||
{
|
||||
return uint_parser<T, Radix, Digits, Digits, Expected>{expected_};
|
||||
}
|
||||
|
||||
/** Returns a `uint_parser` identical to `*this`, except that it
|
||||
only accepts numbers `D` digits long, where `D` is in
|
||||
[`MinDigits2`, MaxDigits2`]. */
|
||||
template<int MinDigits2, int MaxDigits2>
|
||||
constexpr auto digits() const noexcept
|
||||
{
|
||||
return uint_parser<T, Radix, MinDigits2, MaxDigits2, Expected>{
|
||||
expected_};
|
||||
}
|
||||
|
||||
Expected expected_;
|
||||
};
|
||||
|
||||
@@ -8031,6 +8307,8 @@ namespace boost { namespace parser {
|
||||
static_assert(
|
||||
Radix == 2 || Radix == 8 || Radix == 10 || Radix == 16,
|
||||
"Unsupported radix.");
|
||||
static_assert(1 <= MinDigits);
|
||||
static_assert(MaxDigits == -1 || MinDigits <= MaxDigits);
|
||||
|
||||
constexpr int_parser() {}
|
||||
explicit constexpr int_parser(Expected expected) : expected_(expected)
|
||||
@@ -8096,6 +8374,33 @@ namespace boost { namespace parser {
|
||||
return parser_interface{parser_t{expected}};
|
||||
}
|
||||
|
||||
/** Returns an `int_parser` identical to `*this`, except that it
|
||||
parses digits as base-`Radix2` instead of base-`Radix`. */
|
||||
template<int Radix2>
|
||||
constexpr auto base() const noexcept
|
||||
{
|
||||
return int_parser<T, Radix2, MinDigits, MaxDigits, Expected>{
|
||||
expected_};
|
||||
}
|
||||
|
||||
/** Returns an `int_parser` identical to `*this`, except that it only
|
||||
accepts numbers exactly `Digits` digits. */
|
||||
template<int Digits>
|
||||
constexpr auto digits() const noexcept
|
||||
{
|
||||
return int_parser<T, Radix, Digits, Digits, Expected>{expected_};
|
||||
}
|
||||
|
||||
/** Returns an `int_parser` identical to `*this`, except that it
|
||||
only accepts numbers `D` digits long, where `D` is in
|
||||
[`MinDigits2`, MaxDigits2`]. */
|
||||
template<int MinDigits2, int MaxDigits2>
|
||||
constexpr auto digits() const noexcept
|
||||
{
|
||||
return int_parser<T, Radix, MinDigits2, MaxDigits2, Expected>{
|
||||
expected_};
|
||||
}
|
||||
|
||||
Expected expected_;
|
||||
};
|
||||
|
||||
@@ -8224,9 +8529,9 @@ namespace boost { namespace parser {
|
||||
template<typename SwitchValue, typename OrParser>
|
||||
struct switch_parser
|
||||
{
|
||||
switch_parser() {}
|
||||
switch_parser(SwitchValue switch_value) : switch_value_(switch_value) {}
|
||||
switch_parser(SwitchValue switch_value, OrParser or_parser) :
|
||||
constexpr switch_parser() {}
|
||||
constexpr switch_parser(SwitchValue switch_value) : switch_value_(switch_value) {}
|
||||
constexpr switch_parser(SwitchValue switch_value, OrParser or_parser) :
|
||||
switch_value_(switch_value), or_parser_(or_parser)
|
||||
{}
|
||||
|
||||
@@ -8817,9 +9122,12 @@ namespace boost { namespace parser {
|
||||
auto r_ = detail::make_input_subrange(r);
|
||||
auto first = r_.begin();
|
||||
auto const last = r_.end();
|
||||
auto const initial_first = first;
|
||||
return reset = detail::if_full_parse(
|
||||
initial_first,
|
||||
first,
|
||||
last,
|
||||
parser.error_handler_,
|
||||
parser::prefix_parse(first, last, parser, attr, trace_mode));
|
||||
}
|
||||
|
||||
@@ -8922,8 +9230,13 @@ namespace boost { namespace parser {
|
||||
auto r_ = detail::make_input_subrange(r);
|
||||
auto first = r_.begin();
|
||||
auto const last = r_.end();
|
||||
auto const initial_first = first;
|
||||
return detail::if_full_parse(
|
||||
first, last, parser::prefix_parse(first, last, parser, trace_mode));
|
||||
initial_first,
|
||||
first,
|
||||
last,
|
||||
parser.error_handler_,
|
||||
parser::prefix_parse(first, last, parser, trace_mode));
|
||||
}
|
||||
|
||||
/** Parses `[first, last)` using `parser`, skipping all input recognized
|
||||
@@ -9058,9 +9371,12 @@ namespace boost { namespace parser {
|
||||
auto r_ = detail::make_input_subrange(r);
|
||||
auto first = r_.begin();
|
||||
auto const last = r_.end();
|
||||
auto const initial_first = first;
|
||||
return reset = detail::if_full_parse(
|
||||
initial_first,
|
||||
first,
|
||||
last,
|
||||
parser.error_handler_,
|
||||
parser::prefix_parse(
|
||||
first, last, parser, skip, attr, trace_mode));
|
||||
}
|
||||
@@ -9169,9 +9485,12 @@ namespace boost { namespace parser {
|
||||
auto r_ = detail::make_input_subrange(r);
|
||||
auto first = r_.begin();
|
||||
auto const last = r_.end();
|
||||
auto const initial_first = first;
|
||||
return detail::if_full_parse(
|
||||
initial_first,
|
||||
first,
|
||||
last,
|
||||
parser.error_handler_,
|
||||
parser::prefix_parse(first, last, parser, skip, trace_mode));
|
||||
}
|
||||
|
||||
@@ -9287,9 +9606,12 @@ namespace boost { namespace parser {
|
||||
auto r_ = detail::make_input_subrange(r);
|
||||
auto first = r_.begin();
|
||||
auto const last = r_.end();
|
||||
auto const initial_first = first;
|
||||
return detail::if_full_parse(
|
||||
initial_first,
|
||||
first,
|
||||
last,
|
||||
parser.error_handler_,
|
||||
parser::callback_prefix_parse(first, last, parser, callbacks));
|
||||
}
|
||||
|
||||
@@ -9423,9 +9745,12 @@ namespace boost { namespace parser {
|
||||
auto r_ = detail::make_input_subrange(r);
|
||||
auto first = r_.begin();
|
||||
auto const last = r_.end();
|
||||
auto const initial_first = first;
|
||||
return detail::if_full_parse(
|
||||
initial_first,
|
||||
first,
|
||||
last,
|
||||
parser.error_handler_,
|
||||
parser::callback_prefix_parse(
|
||||
first, last, parser, skip, callbacks, trace_mode));
|
||||
}
|
||||
@@ -9485,7 +9810,7 @@ namespace boost { namespace parser {
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
template<typename R, typename Parser>
|
||||
template<typename R, typename Parser, typename SkipParser>
|
||||
struct attribute_impl
|
||||
{
|
||||
using parser_type = typename Parser::parser_type;
|
||||
@@ -9508,7 +9833,7 @@ namespace boost { namespace parser {
|
||||
std::declval<iterator &>(),
|
||||
std::declval<sentinel>(),
|
||||
std::declval<context>(),
|
||||
detail::null_parser{},
|
||||
SkipParser{},
|
||||
detail::flags::gen_attrs,
|
||||
std::declval<bool &>()));
|
||||
};
|
||||
|
||||
@@ -115,7 +115,7 @@ namespace boost { namespace parser {
|
||||
bool & success,
|
||||
int & indent,
|
||||
ErrorHandler const & error_handler,
|
||||
nope &,
|
||||
nope const &,
|
||||
symbol_table_tries_t & symbol_table_tries,
|
||||
pending_symbol_table_operations_t &
|
||||
pending_symbol_table_operations) noexcept;
|
||||
@@ -404,8 +404,8 @@ namespace boost { namespace parser {
|
||||
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 in `[2,
|
||||
36]`. */
|
||||
attribute is not equal to `expected_`. `Radix` must be one of `2`,
|
||||
`8`, `10`, or `16`. */
|
||||
template<
|
||||
typename T,
|
||||
int Radix = 10,
|
||||
|
||||
@@ -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...});
|
||||
|
||||
@@ -4,5 +4,6 @@
|
||||
"authors": [ "T. Zachary Laine" ],
|
||||
"maintainers": [ "Zach Laine <whatwasthataddress -at- gmail.com>" ],
|
||||
"description": "A parser combinator library.",
|
||||
"category": [ "Parsing" ]
|
||||
"category": [ "Parsing" ],
|
||||
"cxxstd": "17"
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import testing ;
|
||||
|
||||
project
|
||||
: requirements <library>/boost/charconv//boost_charconv
|
||||
<library>/boost/parser//boost_parser
|
||||
;
|
||||
|
||||
compile compile_all_t.cpp ;
|
||||
|
||||
@@ -258,6 +258,305 @@ void github_issue_209()
|
||||
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);
|
||||
}
|
||||
|
||||
void github_pr_290()
|
||||
{
|
||||
namespace bp = boost::parser;
|
||||
|
||||
auto const pTest = bp::lit("TEST:") > -bp::quoted_string;
|
||||
|
||||
auto result = bp::parse("TEST: \"foo\"", pTest, bp::blank);
|
||||
BOOST_TEST(result);
|
||||
BOOST_TEST(*result == "foo");
|
||||
}
|
||||
|
||||
namespace github_pr_297_ {
|
||||
namespace bp = boost::parser;
|
||||
constexpr auto bar_required_f = [](auto& ctx) -> bool {
|
||||
const bool& argument = bp::_p<0>(ctx);
|
||||
return argument;
|
||||
};
|
||||
constexpr bp::rule<struct foobar_parser_tag> foobar_parser = "foobar_parser";
|
||||
constexpr auto foobar_parser_def =
|
||||
bp::lit("foo")
|
||||
>> bp::switch_(bar_required_f)
|
||||
(true, bp::lit("bar"))
|
||||
(false, -bp::lit("bar"));
|
||||
BOOST_PARSER_DEFINE_RULES(foobar_parser);
|
||||
}
|
||||
|
||||
void github_pr_297()
|
||||
{
|
||||
namespace bp = boost::parser;
|
||||
using namespace github_pr_297_;
|
||||
|
||||
{
|
||||
const bool bar_required = true;
|
||||
const bool result =
|
||||
bp::parse("foo bar", foobar_parser.with(bar_required), bp::blank);
|
||||
BOOST_TEST(result);
|
||||
}
|
||||
{
|
||||
const bool bar_required = true;
|
||||
const bool result =
|
||||
bp::parse("foo", foobar_parser.with(bar_required), bp::blank);
|
||||
BOOST_TEST(!result);
|
||||
}
|
||||
{
|
||||
const bool bar_required = false;
|
||||
const bool result =
|
||||
bp::parse("foo bar", foobar_parser.with(bar_required), bp::blank);
|
||||
BOOST_TEST(result);
|
||||
}
|
||||
{
|
||||
const bool bar_required = false;
|
||||
const bool result =
|
||||
bp::parse("foo", foobar_parser.with(bar_required), bp::blank);
|
||||
BOOST_TEST(result);
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
@@ -268,5 +567,12 @@ int main()
|
||||
github_issue_90();
|
||||
github_issue_125();
|
||||
github_issue_209();
|
||||
github_issue_223();
|
||||
github_issue_248();
|
||||
github_issue_268();
|
||||
github_issue_279();
|
||||
github_issue_285();
|
||||
github_pr_290();
|
||||
github_pr_297();
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
@@ -292,6 +292,15 @@ 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";
|
||||
|
||||
@@ -323,8 +323,7 @@ int main()
|
||||
assert(result1);
|
||||
std::cout << *result1 << "\n"; // Prints: some text
|
||||
|
||||
auto result2 =
|
||||
bp::parse("\"some \\\"text\\\"\"", bp::quoted_string, bp::ws);
|
||||
auto result2 = bp::parse(R"("some \"text\"")", bp::quoted_string, bp::ws);
|
||||
assert(result2);
|
||||
std::cout << *result2 << "\n"; // Prints: some "text"
|
||||
//]
|
||||
|
||||
@@ -316,7 +316,7 @@ int main()
|
||||
add_parser >> roman_numerals >> next_delete_parser >>
|
||||
roman_numerals);
|
||||
BOOST_TEST(result);
|
||||
BOOST_TEST(*result == std::tuple(100, 100));
|
||||
BOOST_TEST(*result == detail::hl::make_tuple(100, 100));
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user