2
0
mirror of https://github.com/boostorg/parser.git synced 2026-01-28 07:22:29 +00:00
Files
parser/doc/html/boost_parser__proposed_/tutorial/rules.html
2023-12-23 13:48:22 -06:00

336 lines
46 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Rules</title>
<link rel="stylesheet" href="../../boostbook.css" type="text/css">
<meta name="generator" content="DocBook XSL Stylesheets V1.79.1">
<link rel="home" href="../../index.html" title="Chapter 1. Boost.Parser (Proposed)">
<link rel="up" href="../tutorial.html" title="Tutorial">
<link rel="prev" href="the__parse____api.html" title="The parse() API">
<link rel="next" href="unicode_support.html" title="Unicode Support">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
<div class="spirit-nav">
<a accesskey="p" href="the__parse____api.html"><img src="../../images/prev.png" alt="Prev"></a><a accesskey="u" href="../tutorial.html"><img src="../../images/up.png" alt="Up"></a><a accesskey="h" href="../../index.html"><img src="../../images/home.png" alt="Home"></a><a accesskey="n" href="unicode_support.html"><img src="../../images/next.png" alt="Next"></a>
</div>
<div class="section">
<div class="titlepage"><div><div><h3 class="title">
<a name="boost_parser__proposed_.tutorial.rules"></a><a class="link" href="rules.html" title="Rules">Rules</a>
</h3></div></div></div>
<p>
We saw in the previous section how <code class="computeroutput"><a class="link" href="../../boost/parser/parse_idm20127.html" title="Function template parse">parse()</a></code>
is flexible in what types it will accept as attribute out-parameters.
</p>
<p>
That flexibility is a blessing and a curse. For instance, say you wanted
to use the parser <code class="computeroutput"><span class="special">+</span><a class="link" href="../../boost/parser/char_.html" title="Global char_">char_</a></code> to parse a <code class="computeroutput"><span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span></code>,
and capture the result in a <code class="computeroutput"><span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span></code>.
<code class="computeroutput"><span class="special">+</span><a class="link" href="../../boost/parser/char_.html" title="Global char_">char_</a></code> generates an
attribute of <code class="computeroutput"><span class="identifier">std</span><span class="special">::</span><span class="identifier">vector</span><span class="special">&lt;</span><span class="keyword">char</span><span class="special">&gt;</span></code> when
parsing a sequence of <code class="computeroutput"><span class="keyword">char</span></code>,
so you'd have to write the result into a vector first, including all the
allocations that implies, and then you'd have to allocate space in a string,
and copy the entire result. Not great. The flexibility of attribute out-parameters
lets you avoid that. On the other hand, if you want to parse your result
into a <code class="computeroutput"><span class="identifier">std</span><span class="special">::</span><span class="identifier">vector</span><span class="special">&lt;</span><span class="keyword">char</span><span class="special">&gt;</span></code>,
but <span class="bold"><strong>accidentally</strong></span> pass a <code class="computeroutput"><span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span></code>,
the code is well-formed. Usually, we expect type mismatches like this to
be ill-formed in C++. Fortunately, <code class="computeroutput"><a class="link" href="../../boost/parser/rule.html" title="Struct template rule">rules</a></code> help you address both
these concerns.
</p>
<h5>
<a name="boost_parser__proposed_.tutorial.rules.h0"></a>
<span class="phrase"><a name="boost_parser__proposed_.tutorial.rules.using__classname_alt__boost__parser__rule___code__phrase_role__identifier__rule__phrase___code_s__classname__to_nail_down_attribute_flexibility"></a></span><a class="link" href="rules.html#boost_parser__proposed_.tutorial.rules.using__classname_alt__boost__parser__rule___code__phrase_role__identifier__rule__phrase___code_s__classname__to_nail_down_attribute_flexibility">Using
rules
to nail down attribute flexibility</a>
</h5>
<p>
Every rule has a specific attribute type. If one is not specified, the rule
has no attribute. The fact that the attribute is a specific type allows you
to remove attribute flexibility. For instance, say we have a rule defined
like this:
</p>
<p>
</p>
<pre class="programlisting"><span class="identifier">bp</span><span class="special">::</span><span class="identifier">rule</span><span class="special">&lt;</span><span class="keyword">struct</span> <span class="identifier">doubles</span><span class="special">,</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">vector</span><span class="special">&lt;</span><span class="keyword">double</span><span class="special">&gt;&gt;</span> <span class="identifier">doubles</span> <span class="special">=</span> <span class="string">"doubles"</span><span class="special">;</span>
<span class="keyword">auto</span> <span class="keyword">const</span> <span class="identifier">doubles_def</span> <span class="special">=</span> <span class="identifier">bp</span><span class="special">::</span><span class="identifier">double_</span> <span class="special">&gt;&gt;</span> <span class="special">*(</span><span class="char">','</span> <span class="special">&gt;&gt;</span> <span class="identifier">bp</span><span class="special">::</span><span class="identifier">double_</span><span class="special">);</span>
<span class="identifier">BOOST_PARSER_DEFINE_RULE</span><span class="special">(</span><span class="identifier">doubles</span><span class="special">);</span>
</pre>
<p>
</p>
<p>
You can then use it in a call to <code class="computeroutput"><a class="link" href="../../boost/parser/parse_idm20127.html" title="Function template parse">parse()</a></code>,
and <code class="computeroutput"><a class="link" href="../../boost/parser/parse_idm20127.html" title="Function template parse">parse()</a></code> will return a <code class="computeroutput"><span class="identifier">std</span><span class="special">::</span><span class="identifier">optional</span><span class="special">&lt;</span><span class="identifier">std</span><span class="special">::</span><span class="identifier">vector</span><span class="special">&lt;</span><span class="keyword">double</span><span class="special">&gt;&gt;</span></code>:
</p>
<p>
</p>
<pre class="programlisting"><span class="keyword">auto</span> <span class="keyword">const</span> <span class="identifier">result</span> <span class="special">=</span> <span class="identifier">bp</span><span class="special">::</span><span class="identifier">parse</span><span class="special">(</span><span class="identifier">input</span><span class="special">,</span> <span class="identifier">doubles</span><span class="special">,</span> <span class="identifier">bp</span><span class="special">::</span><span class="identifier">ws</span><span class="special">);</span>
</pre>
<p>
</p>
<p>
If you call <code class="computeroutput"><a class="link" href="../../boost/parser/parse_idm20127.html" title="Function template parse">parse()</a></code> with an attribute out-parameter,
it must be exactly <code class="computeroutput"><span class="identifier">std</span><span class="special">::</span><span class="identifier">vector</span><span class="special">&lt;</span><span class="keyword">double</span><span class="special">&gt;</span></code>:
</p>
<pre class="programlisting"><span class="identifier">std</span><span class="special">::</span><span class="identifier">vector</span><span class="special">&lt;</span><span class="keyword">double</span><span class="special">&gt;</span> <span class="identifier">vec_result</span><span class="special">;</span>
<span class="identifier">bp</span><span class="special">::</span><span class="identifier">parse</span><span class="special">(</span><span class="identifier">input</span><span class="special">,</span> <span class="identifier">doubles</span><span class="special">,</span> <span class="identifier">bp</span><span class="special">::</span><span class="identifier">ws</span><span class="special">,</span> <span class="identifier">vec_result</span><span class="special">);</span> <span class="comment">// Ok.</span>
<span class="identifier">std</span><span class="special">::</span><span class="identifier">deque</span><span class="special">&lt;</span><span class="keyword">double</span><span class="special">&gt;</span> <span class="identifier">deque_result</span><span class="special">;</span>
<span class="identifier">bp</span><span class="special">::</span><span class="identifier">parse</span><span class="special">(</span><span class="identifier">input</span><span class="special">,</span> <span class="identifier">doubles</span><span class="special">,</span> <span class="identifier">bp</span><span class="special">::</span><span class="identifier">ws</span><span class="special">,</span> <span class="identifier">deque_result</span><span class="special">);</span> <span class="comment">// Ill-formed!</span>
</pre>
<p>
If we wanted to use a <code class="computeroutput"><span class="identifier">std</span><span class="special">::</span><span class="identifier">deque</span><span class="special">&lt;</span><span class="keyword">double</span><span class="special">&gt;</span></code> as the attribute type of our rule:
</p>
<pre class="programlisting"><span class="comment">// Attribute changed to std::deque&lt;double&gt;.</span>
<span class="identifier">bp</span><span class="special">::</span><span class="identifier">rule</span><span class="special">&lt;</span><span class="keyword">struct</span> <span class="identifier">doubles</span><span class="special">,</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">deque</span><span class="special">&lt;</span><span class="keyword">double</span><span class="special">&gt;&gt;</span> <span class="identifier">doubles</span> <span class="special">=</span> <span class="string">"doubles"</span><span class="special">;</span>
<span class="keyword">auto</span> <span class="keyword">const</span> <span class="identifier">doubles_def</span> <span class="special">=</span> <span class="identifier">bp</span><span class="special">::</span><span class="identifier">double_</span> <span class="special">&gt;&gt;</span> <span class="special">*(</span><span class="char">','</span> <span class="special">&gt;&gt;</span> <span class="identifier">bp</span><span class="special">::</span><span class="identifier">double_</span><span class="special">);</span>
<span class="identifier">BOOST_PARSER_DEFINE_RULES</span><span class="special">(</span><span class="identifier">doubles</span><span class="special">);</span>
<span class="keyword">int</span> <span class="identifier">main</span><span class="special">()</span>
<span class="special">{</span>
<span class="identifier">std</span><span class="special">::</span><span class="identifier">deque</span><span class="special">&lt;</span><span class="keyword">double</span><span class="special">&gt;</span> <span class="identifier">deque_result</span><span class="special">;</span>
<span class="identifier">bp</span><span class="special">::</span><span class="identifier">parse</span><span class="special">(</span><span class="identifier">input</span><span class="special">,</span> <span class="identifier">doubles</span><span class="special">,</span> <span class="identifier">bp</span><span class="special">::</span><span class="identifier">ws</span><span class="special">,</span> <span class="identifier">deque_result</span><span class="special">);</span> <span class="comment">// Ok.</span>
<span class="special">}</span>
</pre>
<p>
So, the attribute flexibility is still available, but only <span class="bold"><strong>within</strong></span>
the rule — the parser <code class="computeroutput"><span class="identifier">bp</span><span class="special">::</span><span class="identifier">double_</span> <span class="special">&gt;&gt;</span> <span class="special">*(</span><span class="char">','</span> <span class="special">&gt;&gt;</span> <span class="identifier">bp</span><span class="special">::</span><span class="identifier">double_</span><span class="special">)</span></code> can parse into a <code class="computeroutput"><span class="identifier">std</span><span class="special">::</span><span class="identifier">vector</span><span class="special">&lt;</span><span class="keyword">double</span><span class="special">&gt;</span></code> or a <code class="computeroutput"><span class="identifier">std</span><span class="special">::</span><span class="identifier">deque</span><span class="special">&lt;</span><span class="keyword">double</span><span class="special">&gt;</span></code>, but the rule <code class="computeroutput"><span class="identifier">doubles</span></code>
must parse into only the exact attribute it was declared to generate.
</p>
<p>
The reason for this is that, inside the rule parsing implementation, there
is code something like this:
</p>
<pre class="programlisting"><span class="keyword">using</span> <span class="identifier">attr_t</span> <span class="special">=</span> <span class="emphasis"><em><code class="literal">ATTR</code></em></span><span class="special">(</span><span class="identifier">doubles_def</span><span class="special">);</span>
<span class="identifier">attr_t</span> <span class="identifier">attr</span><span class="special">;</span>
<span class="identifier">parse</span><span class="special">(</span><span class="identifier">first</span><span class="special">,</span> <span class="identifier">last</span><span class="special">,</span> <span class="identifier">parser</span><span class="special">,</span> <span class="identifier">attr</span><span class="special">);</span>
<span class="identifier">attribute_out_param</span> <span class="special">=</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">move</span><span class="special">(</span><span class="identifier">attr</span><span class="special">);</span>
</pre>
<p>
Where <code class="computeroutput"><span class="identifier">attribute_out_param</span></code>
is the attribute out-parameter we pass to <code class="computeroutput"><a class="link" href="../../boost/parser/parse_idm20127.html" title="Function template parse">parse()</a></code>.
If that final move assignment is ill-formed, the call to <code class="computeroutput"><a class="link" href="../../boost/parser/parse_idm20127.html" title="Function template parse">parse()</a></code>
is too.
</p>
<h5>
<a name="boost_parser__proposed_.tutorial.rules.h1"></a>
<span class="phrase"><a name="boost_parser__proposed_.tutorial.rules.using_rules_to_exploit_attribute_flexibility"></a></span><a class="link" href="rules.html#boost_parser__proposed_.tutorial.rules.using_rules_to_exploit_attribute_flexibility">Using
rules to exploit attribute flexibility</a>
</h5>
<p>
So, even though a rule reduces the flexibility of attributes it can generate,
the fact that it is so easy to write a new rule means that we can use rules
themselves to get the attribute flexibility we want across our code:
</p>
<pre class="programlisting"><span class="keyword">namespace</span> <span class="identifier">bp</span> <span class="special">=</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">parser</span><span class="special">;</span>
<span class="comment">// We only need to write the definition once...</span>
<span class="keyword">auto</span> <span class="keyword">const</span> <span class="identifier">generic_doubles_def</span> <span class="special">=</span> <span class="identifier">bp</span><span class="special">::</span><span class="identifier">double_</span> <span class="special">&gt;&gt;</span> <span class="special">*(</span><span class="char">','</span> <span class="special">&gt;&gt;</span> <span class="identifier">bp</span><span class="special">::</span><span class="identifier">double_</span><span class="special">);</span>
<span class="identifier">bp</span><span class="special">::</span><span class="identifier">rule</span><span class="special">&lt;</span><span class="keyword">struct</span> <span class="identifier">vec_doubles</span><span class="special">,</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">vector</span><span class="special">&lt;</span><span class="keyword">double</span><span class="special">&gt;&gt;</span> <span class="identifier">vec_doubles</span> <span class="special">=</span> <span class="string">"vec_doubles"</span><span class="special">;</span>
<span class="keyword">auto</span> <span class="keyword">const</span> <span class="special">&amp;</span> <span class="identifier">vec_doubles_def</span> <span class="special">=</span> <span class="identifier">generic_doubles_def</span><span class="special">;</span> <span class="comment">// ... and re-use it,</span>
<span class="identifier">BOOST_PARSER_DEFINE_RULES</span><span class="special">(</span><span class="identifier">vec_doubles</span><span class="special">);</span>
<span class="comment">// Attribute changed to std::deque&lt;double&gt;.</span>
<span class="identifier">bp</span><span class="special">::</span><span class="identifier">rule</span><span class="special">&lt;</span><span class="keyword">struct</span> <span class="identifier">deque_doubles</span><span class="special">,</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">deque</span><span class="special">&lt;</span><span class="keyword">double</span><span class="special">&gt;&gt;</span> <span class="identifier">deque_doubles</span> <span class="special">=</span> <span class="string">"deque_doubles"</span><span class="special">;</span>
<span class="keyword">auto</span> <span class="keyword">const</span> <span class="special">&amp;</span> <span class="identifier">deque_doubles_def</span> <span class="special">=</span> <span class="identifier">generic_doubles_def</span><span class="special">;</span> <span class="comment">// ... and re-use it again.</span>
<span class="identifier">BOOST_PARSER_DEFINE_RULES</span><span class="special">(</span><span class="identifier">deque_doubles</span><span class="special">);</span>
</pre>
<p>
Now we have one of each, and we did not have to copy any parsing logic that
would have to be maintained in two places.
</p>
<h5>
<a name="boost_parser__proposed_.tutorial.rules.h2"></a>
<span class="phrase"><a name="boost_parser__proposed_.tutorial.rules.forward_declaration"></a></span><a class="link" href="rules.html#boost_parser__proposed_.tutorial.rules.forward_declaration">Forward
declaration</a>
</h5>
<p>
One of the advantages of using rules is that you can declare all your rules
up front and then use them immediately afterward. This lets you make rules
that use each other without introducing cycles:
</p>
<pre class="programlisting"><span class="keyword">namespace</span> <span class="identifier">bp</span> <span class="special">=</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">parser</span><span class="special">;</span>
<span class="comment">// Assume we have some polymorphic type that can be an object/dictionary,</span>
<span class="comment">// array, string, or int, called `value_type`.</span>
<span class="identifier">bp</span><span class="special">::</span><span class="identifier">rule</span><span class="special">&lt;</span><span class="keyword">class</span> <span class="identifier">string</span><span class="special">,</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span><span class="special">&gt;</span> <span class="keyword">const</span> <span class="identifier">string</span> <span class="special">=</span> <span class="string">"string"</span><span class="special">;</span>
<span class="identifier">bp</span><span class="special">::</span><span class="identifier">rule</span><span class="special">&lt;</span><span class="keyword">class</span> <span class="identifier">object_element</span><span class="special">,</span> <span class="identifier">hana</span><span class="special">::</span><span class="identifier">tuple</span><span class="special">&lt;</span><span class="identifier">std</span><span class="special">::</span><span class="identifier">string</span><span class="special">,</span> <span class="identifier">value_type</span><span class="special">&gt;&gt;</span> <span class="keyword">const</span> <span class="identifier">object_element</span> <span class="special">=</span> <span class="string">"object-element"</span><span class="special">;</span>
<span class="identifier">bp</span><span class="special">::</span><span class="identifier">rule</span><span class="special">&lt;</span><span class="keyword">class</span> <span class="identifier">object</span><span class="special">,</span> <span class="identifier">value_type</span><span class="special">&gt;</span> <span class="keyword">const</span> <span class="identifier">object</span> <span class="special">=</span> <span class="string">"object"</span><span class="special">;</span>
<span class="identifier">bp</span><span class="special">::</span><span class="identifier">rule</span><span class="special">&lt;</span><span class="keyword">class</span> <span class="identifier">array</span><span class="special">,</span> <span class="identifier">value_type</span><span class="special">&gt;</span> <span class="keyword">const</span> <span class="identifier">array</span> <span class="special">=</span> <span class="string">"array"</span><span class="special">;</span>
<span class="identifier">bp</span><span class="special">::</span><span class="identifier">rule</span><span class="special">&lt;</span><span class="keyword">class</span> <span class="identifier">value_tag</span><span class="special">,</span> <span class="identifier">value_type</span><span class="special">&gt;</span> <span class="keyword">const</span> <span class="identifier">value</span> <span class="special">=</span> <span class="string">"value"</span><span class="special">;</span>
<span class="keyword">auto</span> <span class="keyword">const</span> <span class="identifier">string_def</span> <span class="special">=</span> <span class="identifier">bp</span><span class="special">::</span><span class="identifier">lexeme</span><span class="special">[</span><span class="char">'"'</span> <span class="special">&gt;&gt;</span> <span class="special">*(</span><span class="identifier">bp</span><span class="special">::</span><span class="identifier">char_</span> <span class="special">-</span> <span class="char">'"'</span><span class="special">)</span> <span class="special">&gt;</span> <span class="char">'"'</span><span class="special">];</span>
<span class="keyword">auto</span> <span class="keyword">const</span> <span class="identifier">object_element_def</span> <span class="special">=</span> <span class="identifier">string</span> <span class="special">&gt;</span> <span class="char">':'</span> <span class="special">&gt;</span> <span class="identifier">value</span><span class="special">;</span>
<span class="keyword">auto</span> <span class="keyword">const</span> <span class="identifier">object_def</span> <span class="special">=</span> <span class="char">'{'</span><span class="identifier">_l</span> <span class="special">&gt;&gt;</span> <span class="special">-(</span><span class="identifier">object_element</span> <span class="special">%</span> <span class="char">','</span><span class="special">)</span> <span class="special">&gt;</span> <span class="char">'}'</span><span class="special">;</span>
<span class="keyword">auto</span> <span class="keyword">const</span> <span class="identifier">array_def</span> <span class="special">=</span> <span class="char">'['</span><span class="identifier">_l</span> <span class="special">&gt;&gt;</span> <span class="special">-(</span><span class="identifier">value</span> <span class="special">%</span> <span class="char">','</span><span class="special">)</span> <span class="special">&gt;</span> <span class="char">']'</span><span class="special">;</span>
<span class="keyword">auto</span> <span class="keyword">const</span> <span class="identifier">value_def</span> <span class="special">=</span> <span class="identifier">bp</span><span class="special">::</span><span class="identifier">int_</span> <span class="special">|</span> <span class="identifier">bp</span><span class="special">::</span><span class="identifier">bool_</span> <span class="special">|</span> <span class="identifier">string</span> <span class="special">|</span> <span class="identifier">array</span> <span class="special">|</span> <span class="identifier">object</span><span class="special">;</span>
<span class="identifier">BOOST_PARSER_DEFINE_RULES</span><span class="special">(</span><span class="identifier">string</span><span class="special">,</span> <span class="identifier">object_element</span><span class="special">,</span> <span class="identifier">object</span><span class="special">,</span> <span class="identifier">array</span><span class="special">,</span> <span class="identifier">value</span><span class="special">);</span>
</pre>
<p>
Here we have a parser for a Javascript-value-like type <code class="computeroutput"><span class="identifier">value_type</span></code>.
<code class="computeroutput"><span class="identifier">value_type</span></code> 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 other.
</p>
<h5>
<a name="boost_parser__proposed_.tutorial.rules.h3"></a>
<span class="phrase"><a name="boost_parser__proposed_.tutorial.rules._functionname_alt__boost__parser___val___code__phrase_role__identifier___val__phrase__phrase_role__special______phrase___code___functionname_"></a></span><a class="link" href="rules.html#boost_parser__proposed_.tutorial.rules._functionname_alt__boost__parser___val___code__phrase_role__identifier___val__phrase__phrase_role__special______phrase___code___functionname_">_val()</a>
</h5>
<p>
Inside all of a rule's semantic actions, the expression <code class="computeroutput"><a class="link" href="../../boost/parser/_val.html" title="Function template _val">_val</a><span class="special">(</span><span class="identifier">ctx</span><span class="special">)</span></code>
is a reference to the attribute that the rule generates. This can be useful
when you want subparsers to build up the attribute in a specific way:
</p>
<pre class="programlisting"><span class="keyword">namespace</span> <span class="identifier">bp</span> <span class="special">=</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">parser</span><span class="special">;</span>
<span class="keyword">using</span> <span class="keyword">namespace</span> <span class="identifier">bp</span><span class="special">::</span><span class="identifier">literals</span><span class="special">;</span>
<span class="identifier">bp</span><span class="special">::</span><span class="identifier">rule</span><span class="special">&lt;</span><span class="keyword">class</span> <span class="identifier">ints</span><span class="special">,</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">vector</span><span class="special">&lt;</span><span class="keyword">int</span><span class="special">&gt;&gt;</span> <span class="keyword">const</span> <span class="identifier">ints</span> <span class="special">=</span> <span class="string">"ints"</span><span class="special">;</span>
<span class="keyword">auto</span> <span class="identifier">twenty_zeros</span> <span class="special">=</span> <span class="special">[](</span><span class="keyword">auto</span> <span class="special">&amp;</span> <span class="identifier">ctx</span><span class="special">)</span> <span class="special">{</span> <span class="identifier">_val</span><span class="special">(</span><span class="identifier">ctx</span><span class="special">).</span><span class="identifier">resize</span><span class="special">(</span><span class="number">20</span><span class="special">,</span> <span class="number">0</span><span class="special">);</span> <span class="special">};</span>
<span class="keyword">auto</span> <span class="identifier">push_back</span> <span class="special">=</span> <span class="special">[](</span><span class="keyword">auto</span> <span class="special">&amp;</span> <span class="identifier">ctx</span><span class="special">)</span> <span class="special">{</span> <span class="identifier">_val</span><span class="special">(</span><span class="identifier">ctx</span><span class="special">).</span><span class="identifier">push_back</span><span class="special">(</span><span class="identifier">_attr</span><span class="special">(</span><span class="identifier">ctx</span><span class="special">));</span> <span class="special">};</span>
<span class="keyword">auto</span> <span class="keyword">const</span> <span class="identifier">ints_def</span> <span class="special">=</span> <span class="string">"20-zeros"</span><span class="identifier">_l</span><span class="special">[</span><span class="identifier">twenty_zeros</span><span class="special">]</span> <span class="special">|</span> <span class="special">+</span><span class="identifier">bp</span><span class="special">::</span><span class="identifier">int_</span><span class="special">[</span><span class="identifier">push_back</span><span class="special">];</span>
<span class="identifier">BOOST_PARSER_DEFINE_RULES</span><span class="special">(</span><span class="identifier">ints</span><span class="special">);</span>
</pre>
<div class="tip"><table border="0" summary="Tip">
<tr>
<td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="../../images/tip.png"></td>
<th align="left">Tip</th>
</tr>
<tr><td align="left" valign="top"><p>
That's just an example. It's almost always better to do things without
using semantic actions. We could have instead written <code class="computeroutput"><span class="identifier">ints_def</span></code>
as <code class="computeroutput"><span class="string">"20-zeros"</span> <span class="special">&gt;&gt;</span>
<span class="identifier">bp</span><span class="special">::</span><span class="identifier">attr</span><span class="special">(</span><span class="identifier">std</span><span class="special">::</span><span class="identifier">vector</span><span class="special">&lt;</span><span class="keyword">int</span><span class="special">&gt;(</span><span class="number">20</span><span class="special">))</span> <span class="special">|</span>
<span class="special">+</span><span class="identifier">bp</span><span class="special">::</span><span class="identifier">int_</span></code>,
which has the same semantics, is a lot easier to read, and is a lot less
code.
</p></td></tr>
</table></div>
<h5>
<a name="boost_parser__proposed_.tutorial.rules.h4"></a>
<span class="phrase"><a name="boost_parser__proposed_.tutorial.rules.locals"></a></span><a class="link" href="rules.html#boost_parser__proposed_.tutorial.rules.locals">Locals</a>
</h5>
<p>
The <code class="computeroutput"><a class="link" href="../../boost/parser/rule.html" title="Struct template rule">rule</a></code>
template takes another template parameter we have not discussed yet. You
can pass a third parameter to <code class="computeroutput"><a class="link" href="../../boost/parser/rule.html" title="Struct template rule">rule</a></code>, which will be available
within semantic actions used in the rule as <code class="computeroutput"><a class="link" href="../../boost/parser/_locals.html" title="Function template _locals">_locals</a><span class="special">(</span><span class="identifier">ctx</span><span class="special">)</span></code>. This
gives your rule some local state, if it needs it:
</p>
<pre class="programlisting"><span class="keyword">struct</span> <span class="identifier">foo_locals</span>
<span class="special">{</span>
<span class="keyword">char</span> <span class="identifier">first_value</span> <span class="special">=</span> <span class="number">0</span><span class="special">;</span>
<span class="special">};</span>
<span class="keyword">namespace</span> <span class="identifier">bp</span> <span class="special">=</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">parser</span><span class="special">;</span>
<span class="identifier">bp</span><span class="special">::</span><span class="identifier">rule</span><span class="special">&lt;</span><span class="keyword">class</span> <span class="identifier">foo</span><span class="special">,</span> <span class="keyword">int</span><span class="special">,</span> <span class="identifier">foo_locals</span><span class="special">&gt;</span> <span class="keyword">const</span> <span class="identifier">foo</span> <span class="special">=</span> <span class="string">"foo"</span><span class="special">;</span>
<span class="keyword">auto</span> <span class="identifier">record_first</span> <span class="special">=</span> <span class="special">[](</span><span class="keyword">auto</span> <span class="special">&amp;</span> <span class="identifier">ctx</span><span class="special">)</span> <span class="special">{</span> <span class="identifier">_locals</span><span class="special">(</span><span class="identifier">ctx</span><span class="special">).</span><span class="identifier">first_value</span> <span class="special">=</span> <span class="identifier">_attr</span><span class="special">(</span><span class="identifier">ctx</span><span class="special">);</span> <span class="special">}</span>
<span class="keyword">auto</span> <span class="identifier">check_against_first</span> <span class="special">=</span> <span class="special">[](</span><span class="keyword">auto</span> <span class="special">&amp;</span> <span class="identifier">ctx</span><span class="special">)</span> <span class="special">{</span>
<span class="keyword">char</span> <span class="keyword">const</span> <span class="identifier">first</span> <span class="special">=</span> <span class="identifier">_locals</span><span class="special">(</span><span class="identifier">ctx</span><span class="special">).</span><span class="identifier">first_value</span><span class="special">;</span>
<span class="keyword">char</span> <span class="keyword">const</span> <span class="identifier">attr</span> <span class="special">=</span> <span class="identifier">_attr</span><span class="special">(</span><span class="identifier">ctx</span><span class="special">);</span>
<span class="keyword">if</span> <span class="special">(</span><span class="identifier">attr</span> <span class="special">==</span> <span class="identifier">first</span><span class="special">)</span>
<span class="identifier">_pass</span><span class="special">(</span><span class="identifier">ctx</span><span class="special">)</span> <span class="special">=</span> <span class="keyword">false</span><span class="special">;</span>
<span class="identifier">_val</span><span class="special">(</span><span class="identifier">ctx</span><span class="special">)</span> <span class="special">=</span> <span class="special">(</span><span class="keyword">int</span><span class="special">(</span><span class="identifier">first</span><span class="special">)</span> <span class="special">&lt;&lt;</span> <span class="number">8</span><span class="special">)</span> <span class="special">|</span> <span class="keyword">int</span><span class="special">(</span><span class="identifier">attr</span><span class="special">);</span>
<span class="special">};</span>
<span class="keyword">auto</span> <span class="keyword">const</span> <span class="identifier">foo_def</span> <span class="special">=</span> <span class="identifier">bp</span><span class="special">::</span><span class="identifier">cu</span><span class="special">[</span><span class="identifier">record_first</span><span class="special">]</span> <span class="special">&gt;&gt;</span> <span class="identifier">bp</span><span class="special">::</span><span class="identifier">cu</span><span class="special">[</span><span class="identifier">check_against_first</span><span class="special">];</span>
<span class="identifier">BOOST_PARSER_DEFINE_RULES</span><span class="special">(</span><span class="identifier">foo</span><span class="special">);</span>
</pre>
<p>
<code class="computeroutput"><span class="identifier">foo</span></code> matches the input if
it can match two elements of the input in a row, but only if they are not
the same value. Without locals, it's a lot harder to write parsers that have
to track state as they parse.
</p>
<h5>
<a name="boost_parser__proposed_.tutorial.rules.h5"></a>
<span class="phrase"><a name="boost_parser__proposed_.tutorial.rules.parameters"></a></span><a class="link" href="rules.html#boost_parser__proposed_.tutorial.rules.parameters">Parameters</a>
</h5>
<p>
Sometimes, it is convenient to parameterize parsers. Consider this parsing
rule from the <a href="https://yaml.org/spec/1.2/spec.html" target="_top">YAML 1.2</a>
spec:
</p>
<pre class="programlisting">[137] c-flow-sequence(n,c) ::= “[” s-separate(n,c)?
ns-s-flow-seq-entries(n,in-flow(c))? “]”
</pre>
<p>
This YAML rule says that the parsing should proceed into two YAML subrules,
both of which have these <code class="computeroutput"><span class="identifier">n</span></code>
and <code class="computeroutput"><span class="identifier">c</span></code> parameters. It is certainly
possible to transliterate these YAML parsing rules to something that uses
unparameterized Boost.Parser <code class="computeroutput"><a class="link" href="../../boost/parser/rule.html" title="Struct template rule">rules</a></code>, but it is quite painful
to do so.
</p>
<p>
You give parameters to a <code class="computeroutput"><a class="link" href="../../boost/parser/rule.html" title="Struct template rule">rule</a></code> by calling its <code class="computeroutput"><span class="identifier">with</span><span class="special">()</span></code>
member. The values you pass to <code class="computeroutput"><span class="identifier">with</span><span class="special">()</span></code> are used to create a <code class="computeroutput"><span class="identifier">hana</span><span class="special">::</span><span class="identifier">tuple</span></code>
that is available in semantic actions attached to the rule, using <code class="computeroutput"><a class="link" href="../../boost/parser/_params.html" title="Function template _params">_params</a><span class="special">(</span><span class="identifier">ctx</span><span class="special">)</span></code>.
</p>
<pre class="programlisting"><span class="keyword">namespace</span> <span class="identifier">bp</span> <span class="special">=</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">parser</span><span class="special">;</span>
<span class="comment">// Declare our rules.</span>
<span class="identifier">bp</span><span class="special">::</span><span class="identifier">rule</span><span class="special">&lt;/*</span> <span class="special">...</span> <span class="special">*/&gt;</span> <span class="identifier">foo</span> <span class="special">=</span> <span class="string">"foo"</span><span class="special">;</span>
<span class="identifier">bp</span><span class="special">::</span><span class="identifier">rule</span><span class="special">&lt;/*</span> <span class="special">...</span> <span class="special">*/&gt;</span> <span class="identifier">bar</span> <span class="special">=</span> <span class="string">"bar"</span><span class="special">;</span>
<span class="comment">// Get the first parameter for this rule.</span>
<span class="keyword">auto</span> <span class="identifier">first_param</span> <span class="special">=</span> <span class="special">[](</span><span class="keyword">auto</span> <span class="special">&amp;</span> <span class="identifier">ctx</span><span class="special">)</span> <span class="special">{</span>
<span class="keyword">using</span> <span class="keyword">namespace</span> <span class="identifier">boost</span><span class="special">::</span><span class="identifier">hana</span><span class="special">::</span><span class="identifier">literals</span><span class="special">;</span>
<span class="keyword">return</span> <span class="identifier">_params</span><span class="special">(</span><span class="identifier">ctx</span><span class="special">)[</span><span class="number">0</span><span class="identifier">_c</span><span class="special">];</span>
<span class="special">};</span>
<span class="keyword">auto</span> <span class="keyword">const</span> <span class="identifier">foo_def</span> <span class="special">=</span> <span class="identifier">bp</span><span class="special">::</span><span class="identifier">repeat</span><span class="special">(</span><span class="identifier">first_param</span><span class="special">)[</span><span class="char">' '</span><span class="identifier">_l</span><span class="special">];</span> <span class="comment">// Match ' ' the number of times indicated by the first parameter to foo.</span>
<span class="comment">// Assume that bar has a locals struct with a local_indent member, and</span>
<span class="comment">// that set_local_indent and local_indent are lambdas that respectively write</span>
<span class="comment">// and read _locals(ctx).local_indent.</span>
<span class="comment">// Parse an integer, and then pass that as a parameter to foo.</span>
<span class="keyword">auto</span> <span class="keyword">const</span> <span class="identifier">bar_def</span> <span class="special">=</span> <span class="identifier">bp</span><span class="special">::</span><span class="identifier">int_</span><span class="special">[</span><span class="identifier">set_local_indent</span><span class="special">]</span> <span class="special">&gt;&gt;</span> <span class="identifier">foo</span><span class="special">.</span><span class="identifier">with</span><span class="special">(</span><span class="identifier">local_indent</span><span class="special">);</span>
<span class="identifier">BOOST_PARSER_DEFINE_RULES</span><span class="special">(</span><span class="identifier">foo</span><span class="special">,</span> <span class="identifier">bar</span><span class="special">);</span>
</pre>
<p>
Passing parameters to <code class="computeroutput"><a class="link" href="../../boost/parser/rule.html" title="Struct template rule">rules</a></code> like this allows you
to easily write parsers that change the way they parse depending on contextual
data that they have already parsed.
</p>
<h5>
<a name="boost_parser__proposed_.tutorial.rules.h6"></a>
<span class="phrase"><a name="boost_parser__proposed_.tutorial.rules.the__globalname_alt__boost__parser___p___code__phrase_role__identifier___p__phrase___code___globalname__variable_template"></a></span><a class="link" href="rules.html#boost_parser__proposed_.tutorial.rules.the__globalname_alt__boost__parser___p___code__phrase_role__identifier___p__phrase___code___globalname__variable_template">The
_p
variable template</a>
</h5>
<p>
Getting at one of a rule's arguments and passing it as an argument to another
parser can be very verbose. <code class="computeroutput"><a class="link" href="../../boost/parser/_p.html" title="Global _p">_p</a></code> is a variable template
that allows you to refer to the <code class="computeroutput"><span class="identifier">n</span></code>th
argument to the current rule, so that you can, in turn, pass it to one of
the rule's subparsers. Using this, <code class="computeroutput"><span class="identifier">foo_def</span></code>
above can be rewritten as:
</p>
<pre class="programlisting"><span class="keyword">auto</span> <span class="keyword">const</span> <span class="identifier">foo_def</span> <span class="special">=</span> <span class="identifier">bp</span><span class="special">::</span><span class="identifier">repeat</span><span class="special">(</span><span class="identifier">bp</span><span class="special">::</span><span class="identifier">_p</span><span class="special">&lt;</span><span class="number">0</span><span class="special">&gt;)[</span><span class="char">' '</span><span class="identifier">_l</span><span class="special">];</span>
</pre>
<p>
Using <code class="computeroutput"><a class="link" href="../../boost/parser/_p.html" title="Global _p">_p</a></code>
can prevent you from having to write a bunch of lambdas that get each get
an argument out of the parse context using <code class="computeroutput"><a class="link" href="../../boost/parser/_params.html" title="Function template _params">_params</a><span class="special">(</span><span class="identifier">ctx</span><span class="special">)[</span><span class="number">0</span><span class="identifier">_c</span><span class="special">]</span></code> or
similar.
</p>
</div>
<div class="copyright-footer">Copyright © 2020 T. Zachary Laine<p>
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at <a href="http://www.boost.org/LICENSE_1_0.txt" target="_top">http://www.boost.org/LICENSE_1_0.txt</a>)
</p>
</div>
<hr>
<div class="spirit-nav">
<a accesskey="p" href="the__parse____api.html"><img src="../../images/prev.png" alt="Prev"></a><a accesskey="u" href="../tutorial.html"><img src="../../images/up.png" alt="Up"></a><a accesskey="h" href="../../index.html"><img src="../../images/home.png" alt="Home"></a><a accesskey="n" href="unicode_support.html"><img src="../../images/next.png" alt="Next"></a>
</div>
</body>
</html>