PrevUpHomeNext

A Trivial Example

Let's look at a slightly more complicated example, even if it is still trivial. Instead of taking any old chars we're given, let's require some structure. Let's parse one or more doubles, separated by commas.

The Boost.Parser parser for double is double_. So, to parse a single double, we'd just use that. If we wanted to parse two doubles in a row, we'd use:

boost::parser::double_ >> boost::parser::double_

operator>> in this expression is the sequence-operator; read it as "followed by". If we combine the sequence-operator with Kleene star, we can get the parser we want by writing:

boost::parser::double_ >> *(',' >> boost::parser::double_)

This is a parser that matches at least one double — because of the first double_ in the expression above — followed by zero or more instances of a-comma-followed-by-a-double. Notice that we can use ',' directly. Though it is not a parser, operator>> and the other operators defined on Boost.Parser parsers have overloads that accept character/parser pairs of arguments; these operator overloads will create the right parser to recognize ','.

#include <boost/parser/parser.hpp>

#include <iostream>
#include <string>


namespace bp = boost::parser;

int main()
{
    std::cout << "Enter a list of doubles, separated by commas.  No pressure. ";
    std::string input;
    std::getline(std::cin, input);

    auto const result = bp::parse(input, bp::double_ >> *(',' >> bp::double_));

    if (result) {
        std::cout << "Great! It looks like you entered:\n";
        for (double x : *result) {
            std::cout << x << "\n";
        }
    } else {
        std::cout
            << "Good job!  Please proceed to the recovery annex for cake.\n";
    }
}

The first example filled in an out-parameter to deliver the result of the parse. This call to parse() returns a result instead. As you can see, the result is contextually convertible to bool, and *result is some sort of range. In fact, the return type of this call to parse() is std::optional<std::vector<double>>. Naturally, if the parse fails, std::nullopt is returned. We'll look at how Boost.Parser maps the type of the parser to the return type, or the filled in out-parameter's type, a bit later.

[Note] Note

There's a type trait that can tell you the attribute type for a parser, attribute (and an associated alias attribute_t). We'll discuss it more in the Attribute Generation section.

If I run it in a shell, this is the result:

$ example/trivial
Enter a list of doubles, separated by commas.  No pressure. 5.6,8.9
Great! It looks like you entered:
5.6
8.9
$ example/trivial
Enter a list of doubles, separated by commas.  No pressure. 5.6, 8.9
Good job!  Please proceed to the recovery annex for cake.

It does not recognize "5.6, 8.9". This is because it expects a comma followed immediately by a double, but I inserted a space after the comma. The same failure to parse would occur if I put a space before the comma, or before or after the list of doubles.

One more thing: there is a much better way to write the parser above. Instead of repeating the double_ subparser, we could have written this:

bp::double_ % ','

That's semantically identical to bp::double_ >> *(',' >> bp::double_). This pattern — some bit of input repeated one or more times, with a separator between each instance — comes up so often that there's an operator specifically for that, operator%. We'll be using that operator from now on.


PrevUpHomeNext