...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
The Spirit.Qi confix
directive is a unary parser component allowing to embed a parser (the subject)
inside an opening (the prefix) and a closing (the suffix):
confix(prefix, suffix)[subject]
This results in a parser that is equivalent to the sequence
omit[prefix] >> subject >> omit[suffix]
A simple example is a parser for non-nested comments which can now be written as:
confix("/*", "*/")[*(char_ - "*/")] // C style comment confix("//", eol)[*(char_ - eol)] // C++ style comment
Using the confix
directive
instead of the explicit sequence has the advantage of being able to encapsulate
the prefix and the suffix into a separate construct. The following code
snippet illustrates the idea:
namespace spirit = boost::spirit; namespace repo = boost::spirit::repository; // Define a metafunction allowing to compute the type // of the confix() construct template <typename Prefix, typename Suffix = Prefix> struct confix_spec { typedef typename spirit::result_of::terminal< repo::tag::confix(Prefix, Suffix) >::type type; }; confix_spec<std::string>::type const c_comment = repo::confix("/*", "*/"); confix_spec<std::string>::type const cpp_comment = repo::confix("//", "\n");
Now, the comment parsers can be written as
c_comment[*(char_ - "*/")] // C style comment cpp_comment[*(char_ - eol)] // C++ style comment
Note | |
---|---|
While the |
// forwards to <boost/spirit/repository/home/qi/directive/confix.hpp> #include <boost/spirit/repository/include/qi_confix.hpp>
confix(prefix, suffix)[subject]
Parameter |
Description |
---|---|
|
The parser for the opening (the prefix). |
|
The parser for the ending (the suffix). |
|
The parser for the input sequence between the |
All three parameters can be arbitrarily complex parsers themselves.
The confix
directive exposes
the attribute type of its subject as its own attribute type. If the subject
does not expose any attribute
(the type is unused_type
),
then the confix
does not
expose any attribute either.
a: A, p: P, s: S: --> confix(p, s)[a]: A
Note | |
---|---|
This notation is used all over the Spirit documentation and reads as:
Given, |
The following example shows simple use cases of the confix
directive. We will illustrate its usage by generating parsers for different
comment styles and for some simple tagged data (for the full example code
see confix.cpp)
In addition to the main header file needed to include the core components
implemented in Spirit.Qi we add the header file needed
for the new confix
directive.
#include <boost/spirit/include/qi.hpp> #include <boost/spirit/repository/include/qi_confix.hpp>
In order to make the examples below more readable we import a number of elements into the current namespace:
using boost::spirit::eol; using boost::spirit::lexeme; using boost::spirit::ascii::alnum; using boost::spirit::ascii::char_; using boost::spirit::ascii::space; using boost::spirit::qi::parse; using boost::spirit::qi::phrase_parse; using boost::spirit::repository::confix;
We will show how to parse different comment styles. First we will parse a C++ comment:
template <typename Iterator> bool parse_cpp_comment(Iterator first, Iterator last, std::string& attr) { bool r = parse(first, last, confix("//", eol)[*(char_ - eol)], // grammar attr); // attribute if (!r || first != last) // fail if we did not get a full match return false; return r; }
This function will obviously parse input such as "//
This is a comment \n
". Similarily parsing a 'C'-style
comment proves to be straightforward:
template <typename Iterator> bool parse_c_comment(Iterator first, Iterator last, std::string& attr) { bool r = parse(first, last, confix("/*", "*/")[*(char_ - "*/")], // grammar attr); // attribute if (!r || first != last) // fail if we did not get a full match return false; return r; }
which again will be able to parse e.g. "/*
This is a comment */
".
Generating a parser that extracts the body from the HTML snippet "<b>The Body</b>
"
is not very hard, either:
template <typename Iterator> bool parse_tagged(Iterator first, Iterator last, std::string& attr) { bool r = phrase_parse(first, last, confix("<b>", "</b>")[lexeme[*(char_ - '<')]], // grammar space, // skip attr); // attribute if (!r || first != last) // fail if we did not get a full match return false; return r; }