The Boost C++ Libraries

Rules

In Boost.Spirit, parsers consist of rules. As rules are typically based on parsers provided by Boost.Spirit, there is no clear distinction. For example, boost::spirit::ascii::digit can be both a parser and a rule. Typically, rules refer to more complicated expressions like qi::int_ % ','.

In all of the examples thus far, rules were passed to boost::spirit::qi::parse() or boost::spirit::qi::phrase_parse() directly. With boost::spirit::qi::rule, Boost.Spirit provides a class to define rule variables. For example, boost::spirit::qi::rule is required if a rule should be stored in a member variable of a class.

Example 11.13. Defining rules with boost::spirit::qi::rule
#include <boost/spirit/include/qi.hpp>
#include <string>
#include <vector>
#include <iterator>
#include <algorithm>
#include <iostream>

using namespace boost::spirit;

int main()
{
  std::string s;
  std::getline(std::cin, s);
  auto it = s.begin();
  qi::rule<std::string::iterator, std::vector<int>(),
    ascii::space_type> values = qi::int_ % ',';
  std::vector<int> v;
  if (qi::phrase_parse(it, s.end(), values, ascii::space, v))
  {
    std::ostream_iterator<int> out{std::cout, ";"};
    std::copy(v.begin(), v.end(), out);
  }
}

Example 11.13 works like Example 11.12. If you enter multiple integers delimited by commas, they are displayed with semicolons. In contrast to the previous example, the parser isn’t passed directly to boost::spirit::qi::phrase_parse(), but defined in a boost::spirit::qi::rule variable.

boost::spirit::qi::rule is a class template. The only mandatory parameter is the iterator type of the string being parsed. In the example, two more optional template parameters are also passed.

The second template parameter is std::vector<int>(), which is the signature of a function that returns a vector of type std::vector<int> and expects no parameter. This template parameter indicates that the type of the attribute parsed is an int vector.

The third template parameter is the type of the skipper used by boost::spirit::qi::phrase_parse(). In the example, the skipper boost::spirit::ascii::space is used. The type of this skipper is available through boost::spirit::ascii::space_type and is passed as a template parameter to boost::spirit::qi::rule.

Note

If you want your code to be platform independent and work with a C++11 development environment, you should prefer boost::spirit::qi::rule over the keyword auto. If values is defined with auto, the example works as expected with GCC and Clang. However with Visual C++ 2013, only the first number is parsed and written to standard output.

Example 11.14. Nesting Rules
#include <boost/spirit/include/qi.hpp>
#include <boost/variant.hpp>
#include <string>
#include <vector>
#include <algorithm>
#include <iostream>

using namespace boost::spirit;

struct print : public boost::static_visitor<>
{
  template <typename T>
  void operator()(T t) const
  {
    std::cout << std::boolalpha << t << ';';
  }
};

int main()
{
  std::string s;
  std::getline(std::cin, s);
  auto it = s.begin();
  qi::rule<std::string::iterator, boost::variant<int, bool>(),
    ascii::space_type> value = qi::int_ | qi::bool_;
  qi::rule<std::string::iterator, std::vector<boost::variant<int, bool>>(),
    ascii::space_type> values = value % ',';
  std::vector<boost::variant<int, bool>> v;
  if (qi::phrase_parse(it, s.end(), values, ascii::space, v))
  {
    for (const auto &elem : v)
      boost::apply_visitor(print{}, elem);
  }
}

Example 11.14 defines two rules, one of which refers to the other: values is defined as value % ',', and value is set to qi::int_ | qi::bool_. values says that any number of values delimited by commas can be parsed. value defines a value as an integer or bool. Together, the rules say that integers and boolean values separated with commas can be entered in any order.

To store any number of values, a container of type std::vector is provided. Because the type of the values is either int or bool, a class is required that can store an int or a bool value. According to the overview on attribute types and operators, the class boost::variant from Boost.Variant must be used.

If you start the example and enter integers and boolean values delimited by commas, the values are written to the standard output stream delimited by semicolons. This is accomplished with the help of the function boost::apply_visitor(), which is provided by Boost.Variant. This function expects a visitor – an object of the class print in this example.

Please note that boolean values must be entered as true and false.