2
0
mirror of https://github.com/boostorg/parser.git synced 2026-01-19 16:32:13 +00:00

33 Commits

Author SHA1 Message Date
Zach Laine
647cec6683 Address similar concern to the one about closures with move-only callables,
but this time for adaptors.
2025-10-31 17:30:34 -05:00
Zach Laine
c2ddd6e116 Fix ill-formedness when using range_adaptor_closure with GCC14. 2025-10-31 17:22:36 -05:00
Andreas Buhr
f7246de9db Do not call skip parser if we are already in failed state in seq_parser 2025-10-31 15:41:26 -05:00
Andreas Buhr
703a8afafe Fix error in drone CI
error: call to implicitly-deleted copy constructor of [snip]
constexpr closure(F f) : f_(f) {}
2025-10-31 15:39:46 -05:00
Zach Laine
0eacce6080 Add special-casing of nope attributes in seq_parser, so that seq_parser does
not assign over valid values in a sequence's attribute after successfully
parsing a subsquent nope-attributes parser.

Test cases by Andreas Buhr.

Fixes #279
Fixes #285
2025-10-31 15:36:22 -05:00
Zach Laine
5e61ba4e9e Fix C++17 builds. 2025-10-31 14:25:38 -05:00
Zach Laine
dc6144eeb4 Correct tuple/struct confusion in the logic of the if constexpr chain in
perm_parser.

Test case by Andreas Buhr.

Fixes #268.
2025-10-31 12:37:40 -05:00
Zach Laine
d873d7ea80 Provide failover empty symbol table tries for use inside of skippers, since
nothing prevents using a symbol_parser in a skipper, and the previous commit
introduced nullptr skipper data tables in some cases.

Related to #245
2025-10-13 18:43:08 -05:00
Zach Laine
1f5303c756 When creating the context object in detail::skip(), don't create empty structs
for symbol table tries, since they are never used.

Fixes #245
2025-10-13 18:19:04 -05:00
Zach Laine
41e891dc95 In opt_parser, don't forget to clear the attribute if the subparser fails.
Fixes #279
2025-10-13 17:46:27 -05:00
Zach Laine
159472ac6e Change moves to forwards where appropriate.
Addresses the same issues as PR #276.
2025-10-12 21:45:46 -05:00
Andreas Buhr
bfa3e33372 Reduce compilation time by using SkipParser when determining attribute type.
When using a large parser, the whole tree of parsers in instantiated with the
skip (whitespace) parser used. Then, it was instantiated again without a skip
parser, just to determine the attribute type.
This patch changes the code so the attribute type is determined *with* the
skip parser. That way, the number of template instantiations
required are halved for some use cases.

With gcc 14.2, the instantiations without the skip parser even ended up in the
binary. In that case, this patch reduces binary size.
This is most likely a compiler bug, as the usage is in decltype().
2025-10-12 21:31:32 -05:00
Andreas Buhr
8d7a64f7fe Performance improvement: Do not copy use_parser into lambda 2025-10-12 21:16:48 -05:00
Andreas Buhr
8c23054e07 Fix bug that ASCII code for z/Z was noch included in case_fold fast-path 2025-10-12 21:15:37 -05:00
Andreas Buhr
11a5d9a872 Add fast-path for lower case ASCII letters in case_fold
This improved the speed of my parser by approximately 20%
2025-10-12 21:15:37 -05:00
Andreas Buhr
d241bd7853 Make "transform" constexpr 2025-10-12 21:15:14 -05:00
Zach Laine
086241cbd9 Correct a munged bit of doc text. 2025-10-12 21:04:26 -05:00
ivanpanch
e3a3cc8bf2 Update rationale.qbk 2025-10-12 21:00:47 -05:00
ivanpanch
f789199743 Update parser_reference.xml 2025-10-12 21:00:47 -05:00
ivanpanch
f20c4cfb02 Update parser.hpp 2025-10-12 21:00:47 -05:00
ivanpanch
fcbc53ddce Update parser.hpp 2025-10-12 21:00:47 -05:00
ivanpanch
fed0a883ad Update error_handling_fwd.hpp 2025-10-12 21:00:47 -05:00
ivanpanch
bfc61fa963 Update config.hpp 2025-10-12 21:00:47 -05:00
ivanpanch
68c306bf66 Update json.cpp 2025-10-12 21:00:47 -05:00
ivanpanch
d5d080b9f2 Update tutorial.qbk 2025-10-12 21:00:47 -05:00
ivanpanch
03341ba32d Update tutorial.qbk 2025-10-12 21:00:47 -05:00
ivanpanch
7e69b27d7c Update tutorial.qbk 2025-10-12 21:00:47 -05:00
ivanpanch
17d76bc158 Update tutorial.qbk 2025-10-12 21:00:47 -05:00
ivanpanch
050b9ba800 Update tutorial.qbk 2025-10-12 21:00:47 -05:00
ivanpanch
125f12407b Update tables.qbk 2025-10-12 21:00:47 -05:00
Andreas Buhr
e4ba7c7a17 Remove g++9 github CI configuration
Because it runs on ubuntu 20.04 and github does
not offer these runners anymore.
2025-10-12 20:59:26 -05:00
Zach Laine
9a138a20f6 Correct the claim in the docs that if_(c)[p] has attribute type
optional<ATTR(p)>; it actually has attribute type ATTR(p).

Fixes #278.
2025-10-12 20:40:55 -05:00
Zach Laine
c32d594d64 Eliminate double-instantiation of context templates (due to on a difference
between a nope and nope const GlobalState template arg) by using nope const by
default.

Fixes #250.
2025-10-12 20:00:05 -05:00
15 changed files with 438 additions and 140 deletions

View File

@@ -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 }}

View File

@@ -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> &amp;&amp;</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> &amp;&amp;</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> &amp;&amp;</paramtype></parameter><parameter name="escapes"><paramtype><classname>symbols</classname>&lt; <classname>T</classname> &gt; <classname>const</classname> &amp;</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>"\\"&lt;/tt&gt; and &lt;tt&gt;"\ch"</computeroutput> are always valid escape sequences. </para></description></method>
</template><parameter name="r"><paramtype><classname>R</classname> &amp;&amp;</paramtype></parameter><parameter name="escapes"><paramtype><classname>symbols</classname>&lt; <classname>T</classname> &gt; <classname>const</classname> &amp;</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>"\\"&lt;/tt&gt; and &lt;tt&gt;"\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> &amp;</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> &amp;</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> &amp;</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> &amp;</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&lt;V&gt;</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&lt;V&gt;</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&lt;V&gt;</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>

View File

@@ -202,7 +202,7 @@ It has a different API, and other code that operates on text expects a string
instead of some other container. Arrays of characters are already considered
special by the standard library and common practice in C++.
Second, When you write a parser that parses multiple characters in a row, you
Second, when you write a parser that parses multiple characters in a row, you
are typically trying to produce a string attribute, rather than a few
individual character values. When you use multiple non-character parsers in a
row, you are typically trying to produce multiple values. For instance:
@@ -217,7 +217,7 @@ I've rarely written a parser like `parser_2` and wanted a
`std::vector<std::string>`.
_Parser_ therefore makes the common case the default behavior, and provides
you with the _merge_ and _sep_ directives to let you opt-in to generating the
you with the _merge_ and _sep_ directives to let you opt in to generating the
less-common attributes.
[heading Attribute compatibility rules are more strict than in Spirit]
@@ -261,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
@@ -338,7 +337,7 @@ 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
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.

View File

@@ -80,7 +80,7 @@ the input they match unless otherwise stated in the table below.]
[[ _attr_np_`(arg0)` ]
[ Always matches, and consumes no input. Generates the attribute `_RES_np_(arg0)`. ]
[ `decltype(_RES_np_(arg0))`. ]
[ An important use case for `_attr_` is to provide a default attribute value as a trailing alternative. For instance, an *optional* comma-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_. ]
[]]
@@ -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)`] []]
@@ -557,7 +557,7 @@ tables below:
[[`_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), ...>`]]
]

View File

@@ -13,7 +13,7 @@ A /semantic action/ is an arbitrary bit of logic associated with a parser,
that is only executed when the parser matches.
Simpler parsers can be combined to form more complex parsers. Given some
combining operation `C`, and parsers `P0`, `P1`, ... `PN`, `C(P0, P1, ... PN)`
combining operation `C`, and parsers `P0`, `P1`, ..., `PN`, `C(P0, P1, ..., PN)`
creates a new parser `Q`. This creates a /parse tree/. `Q` is the parent of
`P1`, `P2` is the child of `Q`, etc. The parsers are applied in the top-down
fashion implied by this topology. When you use `Q` to parse a string, it
@@ -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:
@@ -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.]
@@ -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.
@@ -1257,7 +1257,7 @@ were unsigned, you would use `uint_`. If it were signed, you would use
constexpr auto hex_int = bp::uint_.base<16>();
You simply chain together the constraints you want to use, like
`.base<16>().digits<2>()` or .digits<4>().base<8>()`.
`.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
@@ -1296,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.
@@ -1328,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
@@ -1394,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_.
@@ -1510,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
@@ -1686,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:
@@ -1694,7 +1694,7 @@ 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
* 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>>`,
@@ -1790,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.
@@ -1844,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;
@@ -1867,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
@@ -1900,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.
@@ -2241,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 *`.]
@@ -2265,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
@@ -2469,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:
@@ -2493,7 +2494,7 @@ the earlier expectation:
]
Not nearly as nice. The problem is that the expectation is on `(value %
',')`. So, even thought we gave `value` reasonable diagnostic text, we put the
',')`. 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.
@@ -2520,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]
@@ -2571,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:
@@ -2623,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
@@ -2661,7 +2662,7 @@ 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
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
@@ -2773,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.
@@ -3160,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"
@@ -3441,7 +3442,7 @@ 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
@@ -3610,7 +3611,7 @@ If we trace a substantial parser, we will see a *lot* of output. Each code
point of the input must be considered, one at a time, to see if a certain rule
matches. As an example, let's trace a parse using the JSON parser from
_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;
@@ -3798,7 +3799,7 @@ _Parser_ seldom allocates memory. The exceptions to this are:
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.
@@ -3806,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
@@ -3953,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

View File

@@ -5,7 +5,7 @@
// http://www.boost.org/LICENSE_1_0.txt)
//[ extended_json_example
// This header includes a type called json::value that acts as a
// Javascript-like polymorphic value type.
// JavaScript-like polymorphic value type.
#include "json.hpp"
#include <boost/parser/parser.hpp>

View File

@@ -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 &>()));

View File

@@ -16,19 +16,19 @@
/** Boost.Parser uses assertions (`BOOST_ASSERT()`) in several places to
indicate that your use of the library has an error in it. All of those
places could 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

View File

@@ -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) {

View File

@@ -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

View File

@@ -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);
}
};

View File

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

View File

@@ -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,
@@ -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);
@@ -3237,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;
//]
}
@@ -3389,7 +3513,7 @@ namespace boost { namespace parser {
bool done = false;
auto try_parser = [prev_first = first,
use_parser,
&use_parser,
&success,
flags,
&retval,
@@ -3556,9 +3680,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,
@@ -3571,9 +3693,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,
@@ -3586,10 +3708,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.
@@ -4352,11 +4480,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);
@@ -4405,7 +4533,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);
@@ -4431,7 +4560,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
} if constexpr (
(!out_container ||
!std::is_same_v<just_x, just_out>) &&
std::is_assignable_v<just_out &, just_x &&> &&
@@ -4604,7 +4735,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
@@ -5888,7 +6019,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(
@@ -5900,7 +6031,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(
@@ -5915,7 +6046,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
@@ -6013,7 +6144,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
@@ -6352,7 +6483,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
@@ -6430,7 +6561,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>
@@ -6569,7 +6700,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)};
}
@@ -7233,7 +7364,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
@@ -7244,7 +7375,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
@@ -7903,8 +8034,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>>
@@ -9671,7 +9802,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;
@@ -9694,7 +9825,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 &>()));
};

View File

@@ -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;

View File

@@ -344,6 +344,163 @@ void github_issue_248()
}
}
#if BOOST_PARSER_USE_CONCEPTS
namespace github_issue_268_ {
namespace bp = boost::parser;
constexpr bp::rule<struct name, std::string_view> name = "name";
auto name_def = bp::string_view[bp::lexeme[+(
bp::lower | bp::upper | bp::digit | bp::char_("_"))]];
BOOST_PARSER_DEFINE_RULES(name)
constexpr bp::rule<struct qd_vec, std::vector<double>> qd_vec = "qd_vec";
auto qd_vec_def = bp::lit("\"") >>
bp::double_ %
(bp::lit(",") |
(bp::lit("\"") >> bp::lit(",") >> bp::lit("\""))) >>
bp::lit('\"');
BOOST_PARSER_DEFINE_RULES(qd_vec)
struct lu_table_template_1
{
std::vector<double> index_1;
std::string_view variable_1;
};
constexpr boost::parser::
rule<struct lu_table_template_1_tag, lu_table_template_1>
lu_table_template_1_rule = "lu_table_template_1";
auto lu_table_template_1_rule_def = (bp::lit("index_1") >> '(' >> qd_vec >>
')' >> ';') >>
(bp::lit("variable_1") >> ':' >> name >>
';');
BOOST_PARSER_DEFINE_RULES(lu_table_template_1_rule)
constexpr boost::parser::
rule<struct lu_table_template_1_permut_tag, lu_table_template_1>
lu_table_template_1_permut_rule = "lu_table_template_1";
auto lu_table_template_1_permut_rule_def =
(bp::lit("index_1") >> '(' >> qd_vec >> ')' >> ';') ||
(bp::lit("variable_1") >> ':' >> name >> ';');
BOOST_PARSER_DEFINE_RULES(lu_table_template_1_permut_rule)
}
#endif
void github_issue_268()
{
#if BOOST_PARSER_USE_CONCEPTS
namespace bp = boost::parser;
using namespace github_issue_268_;
std::string inputstring = "index_1 ( \"1\" ) ; variable_1 : bier;";
auto const def_result = bp::parse(
inputstring, lu_table_template_1_rule_def, bp::blank, bp::trace::off);
std::cout << "seq_parser generates this type:\n"
<< typeid(def_result.value()).name() << std::endl;
BOOST_TEST(def_result);
auto const permut_def_result = bp::parse(
inputstring,
lu_table_template_1_permut_rule_def,
bp::blank,
bp::trace::off);
std::cout << "permut_parser generates this type:\n"
<< typeid(permut_def_result.value()).name() << std::endl;
BOOST_TEST(permut_def_result);
auto const result = bp::parse(
inputstring, lu_table_template_1_rule, bp::blank, bp::trace::off);
std::cout << "seq_parser in rule generates this type:\n"
<< typeid(result.value()).name() << std::endl;
BOOST_TEST(result);
auto const permut_result = bp::parse(
inputstring,
lu_table_template_1_permut_rule,
bp::blank,
bp::trace::off);
std::cout << "permut_parser generates this type:\n"
<< typeid(permut_result.value()).name() << std::endl;
BOOST_TEST(permut_result);
#endif
}
void github_issue_279()
{
namespace bp = boost::parser;
{
constexpr auto condition_clause =
bp::lit(U"while") > bp::lit(U"someexpression") >> bp::attr(true);
constexpr auto do_statement =
bp::lexeme[bp::lit(U"do") >> &bp::ws] > -condition_clause > bp::eol;
auto const result =
bp::parse(U"do\n", do_statement, bp::blank, bp::trace::off);
BOOST_TEST(result);
std::optional<bool> const & condition = result.value();
BOOST_TEST(!condition.has_value());
}
{
constexpr auto condition_clause =
bp::lit(U"while") > bp::lit(U"someexpression") >> bp::attr(true);
constexpr auto do_statement_reverse =
-condition_clause > bp::lexeme[bp::lit(U"do") >> &bp::ws] > bp::eol;
auto const result =
bp::parse(U"do\n", do_statement_reverse, bp::blank, bp::trace::off);
BOOST_TEST(result);
std::optional<bool> const & condition = result.value();
BOOST_TEST(!condition.has_value());
}
}
namespace github_issue_285_ {
namespace bp = boost::parser;
struct Content
{
~Content()
{
int setbreakpointhere = 0;
(void)setbreakpointhere;
}
};
constexpr bp::rule<struct content_tag, std::shared_ptr<Content>> content =
"content";
constexpr auto content_action = [](auto & ctx) {
std::shared_ptr<Content> & result = _val(ctx);
result = std::make_shared<Content>();
};
constexpr auto content_def =
(bp::lit(U"content") >> bp::eol)[content_action];
BOOST_PARSER_DEFINE_RULES(content);
}
void github_issue_285()
{
using namespace github_issue_285_;
namespace bp = boost::parser;
constexpr auto prolog = bp::lit(U"prolog") >> bp::eol;
constexpr auto epilog =
bp::no_case[bp::lexeme[bp::lit(U"epi") >> bp::lit(U"log")]] >> bp::eol;
constexpr auto full_parser = prolog >> content >> epilog;
std::string teststring =
"prolog\n"
"content\n"
"epilog\n";
// "content" produces a shared_ptr with the result.
// The "epilog" parser must not delete the result.
auto const result = bp::parse(teststring, full_parser, bp::blank);
BOOST_TEST(result);
BOOST_TEST(result.value().get() != nullptr);
}
int main()
{
@@ -356,5 +513,8 @@ int main()
github_issue_209();
github_issue_223();
github_issue_248();
github_issue_268();
github_issue_279();
github_issue_285();
return boost::report_errors();
}