Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

Click here to view the latest version of this page.
PrevUpHomeNext

Qi Confix Parser Directive

Description

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] Note

While the confix_p(prefix, subject, suffix) parser in Spirit.Classic was equivalent to the sequence prefix >> (subject - suffix) >> suffix, the Spirit.Qi confix` directive will not perform this refactoring any more. This simplifies the code and makes things more explicit.

Header
// forwards to <boost/spirit/repository/home/qi/directive/confix.hpp>
#include <boost/spirit/repository/include/qi_confix.hpp>
Synopsis
confix(prefix, suffix)[subject]
Parameters

Parameter

Description

prefix

The parser for the opening (the prefix).

suffix

The parser for the ending (the suffix).

subject

The parser for the input sequence between the prefix and suffix parts.

All three parameters can be arbitrarily complex parsers themselves.

Attribute

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] Note

This notation is used all over the Spirit documentation and reads as: Given, a, p, and s are parsers, and A, P, and S are the types of their attributes, then the type of the attribute exposed by confix(p, s)[a] will be A.

Example

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)

Prerequisites

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;

Parsing Different Comment Styles

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 */ ".

Parsing Tagged Data

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


PrevUpHomeNext