Let's look at a slightly more complicated example, even if it is still trivial.
Instead of taking any old char
s
we're given, let's require some structure. Let's parse one or more double
s, 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 double
s
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 | |
---|---|
There's a type trait that can tell you the attribute type for a parser,
|
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 double
s.
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.