Boost::Spirit 2
Last dog watch, 1 bell (6:34 pm)

Last year I added Boost::Spirit to my MUD project. A few weeks ago I learned that all of the work I had done was deprecated in the new Spirit 2 library. I could still use "Spirit classic," but it is deprecated and may be removed at some point in the future. So I spent some time today figuring out how to convert my grammar rules.

For example, in Spirit classic, I had this rule to handle the most complex rule for my get command. In this case, it handles a command like get 20 items 2 from container 3:

rule6 = int_p[assign_a(mNumberToGet)] >> ' ' >>
			(+alpha_p)[assign_a(mItemToFind)] >> ' ' >>
			int_p[assign_a(mItemNumber)] >> ' ' >>
			str_p("from") >> ' ' >>
			(+alpha_p)[assign_a(mContainerToLookIn)] >> ' ' >>

which could be tested by the following code: if(parse(str.c_str(), rule6).full)

Here is the equivalent code in Spirit 2

	std::string::iterator first = str.begin();

	bool r = qi::parse(first, str.end(),
						qi::int_ >> ' '
						>> +(~qi::char_(' ')) >> ' '
						>> qi::int_ 	>> ' '
						>> qi::lit("from") >> ' '
						>> +(~qi::char_(' ')) >> ' '
						>> qi::int_

	if(r && first == command.end()) {
		return true;

In many ways, the new Spirit 2 library is better, and purports to be much faster as well. The parse function has certainly been made somewhat more convenient. Overall, I'd rather not have had to spend time on this, but the only other option is to lock in with an old version of Boost that may hamper me in the future.


5 Responses to “Boost::Spirit 2”

  1. Hey,

    instead of converting +alpha_p into +~char_(' ') you could have used +qi::alpha, and instead of interspersing your grammar with ' ' separators, you may want to utilize the API function pharse_parse() using a blank skipper (which matches spaces and tabs, but a simple ' ' as a skipper does the trick as well). Overall, this simplifies your small grammar into:

    bool r = qi::phrase_parse(first, str.end(),
    qi::int_ >> +qi::alpha >> qi::int_ >>
    "from" >> +qi::alpha >> qi::int_,
    ' ', // skipper
    mNumberToGet, mItemToFind, mItemNumber,
    mContainerToLookIn, mContainerNumber);

    Regards Hartmut

  2. Scurvy Jake says:

    I read for hours trying to find more documentation about that! I found the more I used phrase_parse, the more confusing it got, and I found very little supporting documentation about skip parsers, a list of what skip parsers are available, or how to create my own. Thanks so much!

  3. Well, the phrase_parse API is documented here:

    Generally, a skip parser is not a special parser. Any parser construct can be used as a skipper. The trick is to understand when the skipper is invoked. As a rule of thumb: the skipper is invoked by all primitive parsers _before_ the corresponding matching is performed (see the docs whether a specific parser construct is 'primitive'). So a simple

    phrase_parse(b, e, char_('a'), ' ')

    will first invoke the skipper (i.e. the ' ') multiple times as long as it matches (finds spaces) and afterwards it will try to match the 'a'. In effect, any of the following inputs will be successfully matched:

    "a", " a", " a", etc.

    A more complex

    phrase_parse(b, e, +char_('a'), ' ')

    will match any of:

    "aa", " aa", " a a", etc.

    Note, by default, phrase_parse also performs a post-skip invocation allowing to match any trailing spaces as well. This behaviour can be disabled by using the proper overload of this function.

    Hope this clarifies things.
    Regards Hartmut

  4. As an addition, I forgot to mention this article on the Spirit website: There you'll find more information about skippers.

    Regards Hartmut

  5. Scurvy Jake says:

    Thank you so much! I didn't want to go bothering people on the discussion list until I'd spent enough time figuring things out, but I'm glad you stopped by. I'm going to implement these changes tonight and see how things work!

Leave a Reply