    <rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:admin="http://webns.net/mvcb/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:content="http://purl.org/rss/1.0/modules/content/">
     <channel>
        <title>ACCU  :: With Spirit</title>
        <link>https://members.accu.org/index.php/articles/294</link>
        <description>Professionalism in Programming</description>
        <dc:language>en-us</dc:language> 
        <dc:creator>Administrator</dc:creator> 
        <admin:generatorAgent rdf:resource="http://www.xaraya.org" /> 
        <admin:errorReportsTo rdf:resource="mailto:webeditor@accu.org" />
       <sy:updatePeriod>hourly</sy:updatePeriod>
       <sy:updateFrequency>1</sy:updateFrequency>
       <docs>http://backend.userland.com/rss</docs>




<div class="xar-mod-head"><span class="xar-mod-title">Programming Topics + Overload Journal #69 - Oct 2005</span></div>

<table border="0" cellpadding="1" cellspacing="0">
    <tbody>
    <tr>
        <td valign="top">
            Browse in :
       </td>
       <td valign="top">

                                            <a href="https://members.accu.org/index.php/articles/">All</a>

                     &gt;                         <a href="https://members.accu.org/index.php/articles/c13/">Topics</a>

                     &gt;                         <a href="https://members.accu.org/index.php/articles/c65/">Programming</a>
<br />

                                            <a href="https://members.accu.org/index.php/articles/">All</a>

                     &gt;                         <a href="https://members.accu.org/index.php/articles/c76/">Journals</a>

                     &gt;                         <a href="https://members.accu.org/index.php/articles/c78/">Overload</a>

                     &gt;                         <a href="https://members.accu.org/index.php/articles/c143/">69</a>
<br />

                                            <a href="https://members.accu.org/index.php/articles/c65-143/">Any of these categories</a>

                    -                        <a href="https://members.accu.org/index.php/articles/c65+143/">All of these categories</a>
<br />
</td>
   </tr>
   </tbody>
</table>




<div class="xar-error">
   <p>
 <strong>Note:</strong> when you create a new publication type,
the articles module will automatically use the templates
<em>user-display-[publicationtype].xt</em>
and <em>user-summary-[publicationtype].xt</em>.
If those templates do not exist when you try to preview or display a new article,
you'll get this warning :-)  Please place your own templates in themes/<em>yourtheme</em>/modules/articles . The templates will get the extension .xt there. </p>
</div>
<div class="xar-norm xar-standard-box-padding">
   <h1><strong>Title:</strong>&nbsp;With Spirit</h1>
<p><strong>Author:</strong>&nbsp;</p>
<p>
<strong>Date:</strong> 02 October 2005 05:00:00 +01:00 or Sun, 02 October 2005 05:00:00 +01:00</p>
<p><strong>Summary:</strong>&nbsp;</p>
<p><strong>Body:</strong>&nbsp;<div class="sect1" lang="en">
<div class="titlepage">
<h2><a name="d0e18" id="d0e18"></a></h2>
</div>
<p>Spirit is an object-oriented recursive-descent parser generator
framework implemented using template meta-programming techniques.
Expression templates allow us to approximate the syntax of Extended
Backus-Normal[sic] Form (EBNF) completely in C++. [<a href=
"#_1">_1</a>]</p>
<p>EBNF is also known as Extended Backus-Naur Form [<a href=
"#_2">_2</a>]. EBNF is a metasyntax used to formally describe a
language. In this example the language is the set of possible
expressions that are used to restrict SQL select statements.</p>
<p>The sample code shown is all real code, shown with permission of
the owner (a financial institution that wishes to remain
anonymous). This piece of code was chosen as a &quot;proof of concept&quot;
to show how Spirit works and how it is implemented, both to the
management and to the other developers.</p>
<p>The application is a trading system in a bank, and the piece of
code is responsible for interpreting what the user enters in a
free-text field in the interface used to specify search
restrictions. For example, the user may just want to search for
certain instruments, or all trades in books starting with the
letters B through D. The function <tt class=
"function">query_parse</tt> (shown below) is the old C version that
takes this free text and produces one or more &quot;tokens&quot; for
generating the SQL where clause.</p>
<pre class="programlisting">
---- some header.h
/***********************************************
 * SQL  Token: consists of :
 * 1. logical operator      : and, or, like
 * 2. mathematical operator : &lt;, &gt;, =, &lt;=,
 *                            &gt;=, &lt;&gt;,
 * 3. value                 : the real value 
 *    - i.e. &lt; 30, 30 is the value     
 * Before any cell string gets built into an SQL 
 * sub-clause, it'll be parsed by query_parse() 
 * into a linked-list of SQLTokens, and 
 * query_doit() will build using such SQLTokens, 
 * instead of cell strings directly.
 **********************************************/

typedef struct _SQLToken
{
  char* logic_op;
  char* math_op;
  char* value;
  struct _SQLToken *next;
} SQLToken;
---- source file.cpp
static SQLToken* query_parse (char *string)
{
  typedef enum { NEUTRAL, LOP, MOP, VALUE } 
     STATE;
  char c;
  int index = 0, blank = 0;
  SQLToken *token, *tmp=0, *head; 
  // fix compiler warning - tmp
  STATE state = NEUTRAL;

  head = sqltoken_alloc();
  token = head;

  while( ( c = string[index] ) &amp;&amp;
     ( c != '\n' ) ) {
    blank = 0;
    switch(state) {
    /*****************************************/
    case NEUTRAL:
      switch(c) {
        case ' ':
        case '\t':
          blank = 1;
          ++index;
          break;
        case '+':
        case '|':
          state = LOP;
          break;
        case '&lt;':
        case '&gt;':
        case '=':
        case '!':
        /* if ( first != 0) return NULL; */ 
        /* only the begin of string may have */
        /* no LOP first = 1; */
          state = MOP;
          break;
            
        default :
          /* return NULL; */
          state = VALUE;
          break;
       }

  /* alloc space for next SQLToken, if needed */
       if ((token == NULL) &amp;&amp; (!blank)) {
         token = sqltoken_alloc();
         tmp-&gt;next = token;
       } 
       break;
         
/*******************************************/
     case LOP:
       switch(c) {
       case '|':
         while ((c != ' ') &amp;&amp; (c!= '\0') &amp;&amp; 
            (c != '&quot;') &amp;&amp; (c != '&gt;') &amp;&amp; 
            (c!= '&lt;') &amp;&amp; (c != '=') &amp;&amp; 
            ( c != '!')) 
            c = string[++index];
         strcat(token-&gt;logic_op, &quot;or&quot;);
         break;
       case '+':
         while ((c != ' ') &amp;&amp; (c!= '\0') &amp;&amp; 
            (c != '&quot;') &amp;&amp; (c != '&gt;') &amp;&amp;
            (c!= '&lt;') &amp;&amp; (c != '=') &amp;&amp;
            ( c != '!')) 
            c = string[++index];
         strcat(token-&gt;logic_op, &quot;and&quot;);
         break;
       default:
         return NULL;
       }
         
       state = NEUTRAL;
       if ((c != '&quot;') &amp;&amp; (c != '&gt;') &amp;&amp; (c!= '&lt;') 
          &amp;&amp; (c != '=') &amp;&amp; ( c != '!')) 
         index++;
       break;
     /*******************************************/
     case MOP:
       switch(c) {
       case ' ':
       case '\t':
         state = VALUE;
         index++;
         break;
       case '&lt;':
       case '&gt;':
       case '=':
       case '!':
         strncat(token-&gt;math_op, &amp;c, 1);
         index++;
         break;
         default:
         /* if (token-&gt;math_op == NULL) 
            return NULL; MOP missing */
       state = VALUE;
       } 
       break;

     /*******************************************/
     case VALUE:
       switch(c) {
       case ' ':
         index++;
         break;
       case '&quot;':
          while (((c = string[++index]) != '&quot;')
            &amp;&amp; (c != '\0') &amp;&amp; (c != '\n'))
           strncat(token-&gt;value, &amp;c, 1);
         index++;
         state = NEUTRAL;
         tmp = token;
         token = token-&gt;next;
         break;
       default:
         while ((c != ' ') &amp;&amp; (c != '\0')&amp;&amp; 
            (c != '\n')&amp;&amp; (c != '&quot;')) 
           {
             strncat(token-&gt;value, &amp;c, 1);
             c = string[++index];  
           }
         state = NEUTRAL;
         tmp = token;
         token = token-&gt;next;    
       }
       break;
     }
   }
   return head;
}
</pre>
<p>You can see that this code is not very easy to follow, and not
overly descriptive in what it does. Clearly it iterates over the
character array switching on a remembered state to build up the
<tt class="classname">SQLToken</tt> instance. However it is not
apparent if there is a bug in the code, and should this method need
to be extended due to a change in the grammar, much rework may be
needed.</p>
<p>A small piece of history. The application was started around 12
years ago and was originally all C. Policy is now that new
development should be in C++, updating old code where necessary. So
to bring the interface more into line with C++ the signature was
changed to:</p>
<pre class="programlisting">
std::vector&lt;SQLToken&gt; query_parse(
   char const* input)
</pre>
<p>The input parameter was not changed to a string as that would
not really have gained anything. The calling function had the data
as a <tt class="type">char const*</tt>, and that is also the type
for the parameter for the parser. Also the <tt class=
"classname">SQLToken</tt> definition changed to use <tt class=
"classname">std::string</tt>:</p>
<pre class="programlisting">
   struct SQLToken
   {
         std::string logic_op;
         std::string comp_op;
         std::string value;
   };
</pre>
<p>In order to move the legacy function to Spirit, the grammar had
to be defined. By meticulous iteration of the existing function
with sample input, the following grammar was extracted.</p>
<pre class="programlisting">
comp_op ::= '&lt;' | '&lt;=' | '&lt;&gt;' | '&gt;' | '&gt;=' |
   '=' | '!='
logic_op ::= '+' | '|'
value ::= '&quot;' not_quote+ '&quot;' | not_space+
element ::= (logic_op? comp_op? value)+
</pre>
<p>where <tt class="literal">not_quote</tt> is any character except
the quote character (&quot;), and <tt class="literal">not_space</tt> is
any character except white space (space, tab, or new line).</p>
<p>Now the documentation of the boost website for Spirit gives a
great, easy to follow introduction [<a href="#_3">_3</a>]. The
management summary equivalent goes something like this:</p>
<div class="itemizedlist">
<ul type="disc">
<li>
<p>a parser is made up from rules</p>
</li>
<li>
<p>rules are place holders for expressions</p>
</li>
<li>
<p>expressions are either primitives or combinations</p>
</li>
</ul>
</div>
<p>Spirit provides classes that define rules and parsers. It also
provides a fairly complete set of primitives. The main primitives
used for this example are <tt class="classname">spirit::str_p</tt>
and <tt class="classname">spirit::ch_p</tt>. <tt class=
"classname">str_p</tt> matches a <tt class="type">string</tt>, and
<tt class="classname">ch_p</tt> matches a single character.</p>
<p>Expressions can be grouped with brackets, alternatives defined
by | (bar character), and combined using the &gt;&gt; operator. The
bar operator is overloaded in Spirit allowing us to not explicitly
wrap alternatives in constructor calls. This is a convenience
especially when trying to fit examples in a small text area.</p>
<p>The first two grammar components are quite simple. For now just
accept that what is being assigned is some form of rule class and
the declaration will come later.</p>
<pre class="programlisting">
comp_op = spirit::str_p(&quot;&lt;&gt;&quot;) | &quot;&lt;=&quot; | &quot;&lt;&quot; |
   &quot;&gt;=&quot; | &quot;&gt;&quot; | &quot;=&quot; | &quot;!=&quot;;
logic_op = spirit::ch_p('+') | '|';
</pre>
<p>The quirky parts of this are that the expressions are evaluated
in a short circuit manner, so for the comparison operators you need
to list the longest first, so &lt;&gt; needs to come before &lt;
otherwise the &lt; will be matched for that expression. The Spirit
library does provide a way to get around the short circuit nature
with a directive. Directives could be thought of as modifiers to an
expression. Here use of the <tt class="literal">longest_d</tt>
directive would suffice, which would give:</p>
<pre class="programlisting">
comp_op = spirit::longest_d[ 
   spirit::str_p('&lt;') | '&lt;=' | '&lt;&gt;' | '&gt;' |
   '&gt;=' | '=' | '!=' ]
</pre>
<p>However the choice was to go with the simpler definition and a
comment.</p>
<p>Now for the value rule. Some of the predefined character parsers
were used for this.</p>
<p><tt class="literal">ch_p('&quot;')</tt> matches the quote character,
<tt class="literal">~ch_p('&quot;')</tt> matches any character except
the quote character, and <tt class="literal">+(~ch_p('&quot;'))</tt>
matches one or more non-quote characters. So the first part of the
value is</p>
<pre class="programlisting">
'&quot;' &gt;&gt; (+(~spirit::ch_p('&quot;'))) &gt;&gt; '&quot;' 
</pre>
<p>The alternative to a quote enclosed string is a single word,
where the contents of the word is anything that isn't whitespace.
Spirit provides a <tt class="literal">space_p</tt> that matches
whitespace characters, so <tt class="literal">~space_p</tt> will
match non-whitespace characters. To make a word, we use</p>
<pre class="programlisting">
(+(~spirit::space_p))
</pre>
<p>Most of the time when parsing, whitespace is ignored, however in
this case whitespace matters. This rule as it stands actually
matches the string <tt class="literal">&quot;a b c d&quot;</tt> as <tt class=
"literal">&quot;abcd&quot;</tt>. In order to tell the parser that we are
concerned about the whitespace, we use the directive <tt class=
"literal">lexeme_d</tt>. The full rule for value is then:</p>
<pre class="programlisting">
value = '&quot;' 
     &gt;&gt; (+(~spirit::ch_p('&quot;')))
     &gt;&gt; '&quot;' 
     | spirit::lexeme_d[(+(~spirit::space_p))];
</pre>
<p>The element then is an accumulation of the other rules.
<tt class="literal">operator!</tt> is used as zero or one, so the
element is then</p>
<pre class="programlisting">
element = +(!logic_op &gt;&gt; !comp_op &gt;&gt; value);
</pre>
<p>The complete definition for the grammar object is then:</p>
<pre class="programlisting">
struct query_grammar : public spirit::
   grammar&lt;query_grammar&gt;
{
  template &lt;typename ScannerT&gt;
  struct definition
  {
    definition(query_grammar const&amp; self)
    {
      // short circuit, so do longer 
      // possibilities first
      comp_op = spirit::str_p(&quot;&lt;&gt;&quot;) | &quot;&lt;=&quot; 
         | &quot;&lt;&quot; | &quot;&gt;=&quot; | &quot;&gt;&quot; | &quot;=&quot; | &quot;!=&quot;;
      logic_op = spirit::ch_p('+') | '|';
      value = '&quot;' 
         &gt;&gt; (+(~spirit::ch_p('&quot;'))) 
         &gt;&gt; '&quot;' | spirit::lexeme_d[
         (+(~spirit::space_p)) ];
        element = +(!logic_op &gt;&gt; 
           !comp_op &gt;&gt; value);
        BOOST_SPIRIT_DEBUG_RULE(comp_op);
        BOOST_SPIRIT_DEBUG_RULE(logic_op);
        BOOST_SPIRIT_DEBUG_RULE(value);
        BOOST_SPIRIT_DEBUG_RULE(element);
      }
    spirit::rule&lt;ScannerT&gt; comp_op, 
       logic_op, value, element;
    spirit::rule&lt;ScannerT&gt; const&amp; start() 
       const { return element; }
  };
};
</pre>
<p>The <tt class="literal">BOOST_SPIRIT_DEBUG_RULE</tt> macro
enables some very useful debugging output which is handy when
tracing your grammar if it is going wrong. A quick interactive test
program allows us to test the grammar.</p>
<pre class="programlisting">
int main()
{
   std::cout &lt;&lt; &quot;&gt; &quot;;
   std::string input;
   std::getline(std::cin, input);
   query_grammar parser;
   while (input != &quot;quit&quot;) {
      if (spirit::parse(input.c_str(), parser,
         spirit::space_p).full)
         std::cout &lt;&lt; &quot;parse succeeded&quot;;
      else
         std::cout &lt;&lt; &quot;parse failed&quot;;
      std::cout &lt;&lt; &quot;\n&gt; &quot;;
      std::getline(std::cin, input);
   }
}
</pre>
<p>Ths was used to prove that the grammar was correct. The next
challenge is how to get the parser to populate the vector of
<tt class="classname">SQLToken</tt> objects while parsing? I want
the <tt class="classname">SQLToken</tt> object to be populated
during parsing and, once a complete token has been processed
<tt class="literal">(!logic_op &gt;&gt; !comp_op &gt;&gt;
value)</tt>, it should be pushed on to the vector.</p>
<p>The interesting part of handling assignment is that the
definition <tt class="literal">struct</tt> constructor takes a
constant reference to the outer grammar structure, so you cannot
change normal member variables. This leaves the choices of mutable
and references, and personally I tend to shy away from mutable
where there is another choice. So the outer grammar stuct holds
references to objects that we want to populate.</p>
<pre class="programlisting">
struct query_grammar : 
   public spirit::grammar&lt;query_grammar&gt;
{
    // definition structure here... 
    query_grammar(std::vector&lt;SQLToken&gt;&amp;
       tokens, SQLToken&amp; token)
       : tokens_(tokens), token_(token) {}
    std::vector&lt;SQLToken&gt;&amp; tokens_;
    SQLToken&amp; token_;
};
</pre>
<p>The next step is to add the actions to the rules, and this is
done through the use of &quot;actors&quot;. There are a number of predefined
actors. The main one used here is <tt class=
"literal">assign_a</tt>. The function call operator on this actor
takes one or two parameters. The first parameter is a reference to
the string object to populate. If the second parameter is passed
in, it assigns the second parameter to the first, and if not, the
text that is matched for the rule is assigned.</p>
<p>There is the situation where we want to assign &quot;<tt class=
"literal">and</tt>&quot; when the parser finds <tt class=
"literal">'+'</tt>, and <tt class="literal">&quot;or&quot;</tt> for
<tt class="literal">'|'</tt>, so the <tt class=
"literal">logic_op</tt> rule is changed to look like this:</p>
<pre class="programlisting">
logic_op = spirit::ch_p('+')[spirit::assign_a(
   self.token_.logic_op, &quot;and&quot;)]
         | spirit::ch_p('|')[spirit::assign_a(
   self.token_.logic_op, &quot;or&quot;)];
</pre>
<p>Since the action is being used on the components of the rule,
the definition now has to specify <tt class=
"literal">ch_p('|')</tt> instead of just <tt class=
"literal">'|'</tt>, as there is no <tt class=
"methodname">operator[]</tt> on a <tt class="type">char</tt>.</p>
<p>For the value, if it was quote enclosed, the value is the
contents of the string without the quotes, otherwise the value is
the whole single word, so the actor is applied to the parts of the
value rule, not on the rule as a whole.</p>
<pre class="programlisting">
value = '&quot;' 
   &gt;&gt; (+(~spirit::ch_p('&quot;'))) 
   [spirit::assign_a(self.token_.value)]
   &gt;&gt; '&quot;' | spirit::lexeme_d[
   (+(~spirit::space_p))
   [spirit::assign_a(self.token_.value)] ];
</pre>
<p>The comparison operator can be handled at the whole rule level
as the text of the parsed rule is the string value that we want to
store for the <tt class="classname">SQLToken</tt>. This is achieved
by specifying the action for the <tt class="literal">comp_op</tt>
rule in the element.</p>
<pre class="programlisting">
element = +(!logic_op
   &gt;&gt; !(comp_op[spirit::assign_a(
   self.token_.comp_op)]) &gt;&gt; value);
</pre>
<p>The last part of the parsing is to add the token to the vector.
One way of doing this is through a functor object. Standard Spirit
functors need to handle two <tt class="type">char const*</tt>
parameters. These are the start and end of the &quot;match&quot; for the
rule. In this case they aren't used at all, but instead the functor
operates on the references that it is constructed with.</p>
<pre class="programlisting">
struct push_token
{
   push_token(std::vector&lt;SQLToken&gt;&amp; tokens,
      SQLToken&amp; token) : tokens_(tokens),
      token_(token) {}
   void operator()(char const*, 
      char const*) const
     {
       tokens_.push_back(token_);
       // reset token_ to blanks
       token_ = SQLToken();
     }
   std::vector&lt;SQLToken&gt;&amp; tokens_;
   SQLToken&amp; token_;
};
</pre>
<p>To incorporate this functor into our element rule, we specify it
as the action and construct it with the same references as the
grammar.</p>
<pre class="programlisting">
element = +(!logic_op
   &gt;&gt; !(comp_op[spirit::assign_a(
   self.token_.comp_op)])
   &gt;&gt; value)[push_token(self.tokens_,
   self.token_)];
</pre>
<p>Now it's done. After testing the results, which to my initial
surprise worked perfectly, the old function was replaced with
this:</p>
<pre class="programlisting">
namespace {
  using namespace boost;
  struct push_token
  {
    push_token(std::vector&lt;SQLToken&gt;&amp; tokens,
       SQLToken&amp; token) : tokens_(tokens), 
       token_(token) {}
    void operator()(char const*, 
       char const*) const
    {
      tokens_.push_back(token_);
      // reset token_ to blanks
      token_ = SQLToken();
    }
    std::vector&lt;SQLToken&gt;&amp; tokens_;
    SQLToken&amp; token_;
  };
  struct query_grammar : public spirit
     ::grammar&lt;query_grammar&gt;
  {
    template &lt;typename ScannerT&gt;
    struct definition
    {
      definition(query_grammar const&amp; self)
      {
        // short circuit, so do longer
        // possibilities first
        comp_op = spirit::str_p(&quot;&lt;&gt;&quot;) | &quot;&lt;=&quot; |
           &quot;&lt;&quot; | &quot;&gt;=&quot; | &quot;&gt;&quot; | &quot;=&quot; | &quot;!=&quot;;
        // + -&gt; and, | -&gt; or.  Could now 
        // easily add in &quot;and&quot; and &quot;or&quot;
        logic_op = spirit::ch_p('+')[spirit
           ::assign_a(self.token_.logic_op,
           &quot;and&quot;)] | spirit::ch_p('|')[spirit
           ::assign_a(self.token_.logic_op,
           &quot;or&quot;)];
        // values are single words or 
        // enclosed in quotes.
        value = '&quot;' &gt;&gt; (+(~spirit::ch_p('&quot;'))) 
           [spirit::assign_a(self.token_.value)]
        &gt;&gt; '&quot;' | spirit::lexeme_d[
           (+(~spirit::space_p))
           [spirit::assign_a(self.token_.value)]
           ];
        // EBNF:  (logic_op? comp_op? value)+
        // parsing fails if there are no values.
        element = +(!logic_op
        &gt;&gt; !(comp_op[spirit::assign_a(
           self.token_.comp_op)])
        &gt;&gt; value)[push_token(self.tokens_,
           self.token_)];
      }
    spirit::rule&lt;ScannerT&gt; comp_op, logic_op,
       value, element;
    spirit::rule&lt;ScannerT&gt; const&amp; start() const
       { return element; }
  };
  query_grammar(std::vector&lt;SQLToken&gt;&amp; tokens,
     SQLToken&amp; token)
     : tokens_(tokens), token_(token) {}
     std::vector&lt;SQLToken&gt;&amp; tokens_;
     SQLToken&amp; token_;
  };
  std::vector&lt;SQLToken&gt; query_parse(
     char const* input)
  {
    Logger logger(&quot;gds.query.engine.parse&quot;);
    GDS_DEBUG_STREAM(logger) 
       &lt;&lt; &quot;query_parse input: &quot; &lt;&lt; input;
      std::vector&lt;SQLToken&gt; tokens;
      SQLToken token;
      query_grammar parser(tokens, token);
      if (spirit::parse(input, parser, 
         spirit::space_p).full)
      {
        if (logger.isDebugEnabled()) {
          for (unsigned i = 0;
          i &lt; tokens.size();
          ++i) GDS_DEBUG_STREAM(logger) 
          &lt;&lt; tokens[i];
        }
      }
      else
      {
        GDS_DEBUG(logger, &quot;parse failed&quot;);
        tokens.clear();
      }
    return tokens;
  }
} // anon namespace
</pre>
<p>An anonymous namespace is used instead of the old static C
function, some logging was added using our logging classes, but
apart from that, the code went in without other modifications.</p>
<p>In total, I achieved a reduction of about 40 lines of code,
which in itself is completely meaningless. The general complexity
of the code increased, but at least in my opinion, it is now more
maintainable and extensible. Should the client want to make
modifications to the grammar it is now a relatively simple
operation compared to the nightmare of altering the original
embedded switch statements.</p>
<p>Special thanks to Phil Bass and David Carter-Hitchin for
reviewing this article.</p>
</div>
<div class="bibliography">
<div class="titlepage">
<h2><a name="d0e266" id="d0e266"></a>References</h2>
</div>
<div class="bibliomixed"><a name="_1"></a>
<p class="bibliomixed">[_1] <span class="bibliomisc"><a href=
"http://www.boost.org/libs/spirit/doc/introduction.html" target=
"_top">http://www.boost.org/libs/spirit/doc/introduction.html</a></span></p>
</div>
<div class="bibliomixed"><a name="_2"></a>
<p class="bibliomixed">[_2] <span class="bibliomisc"><a href=
"http://en.wikipedia.org/wiki/Extended_Backus-Naur_form" target=
"_top">http://en.wikipedia.org/wiki/Extended_Backus-Naur_form</a></span></p>
</div>
<div class="bibliomixed"><a name="_3"></a>
<p class="bibliomixed">[_3] <span class="bibliomisc"><a href=
"http://www.boost.org/libs/spirit/doc/basic_concepts.html" target=
"_top">http://www.boost.org/libs/spirit/doc/basic_concepts.html</a></span></p>
</div>
</div>
</p>
<p><strong>Notes:</strong>&nbsp;</p>
<p><em>More fields may be available via dynamicdata ..</em></p>
</div>
</channel>
</rss>
