Qi Confix Parser Directive

| Home | Libraries | People | FAQ | More

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