7
votes

I have a grammar that has, up until now, been using the standard boost::spirit::ascii::space/boost::spirit::ascii::space_type skipper.

I have some rules that use the skipper and some that don't, like

qi::rule<Iterator, PTR<Expression>(), ascii::space_type> expression;
qi::rule<Iterator, PTR<Term>()> term;

When I use a non-skipping nonterminal (like term) inside of a skipping nonterminal (like expression), everything works like I would expect - whitespace only matters inside the term nonterminal.

Further, up until now, I have been fine including nonterminals that use the skipper inside of nonterminals that don't using qi::skip to restablish skipping, such as

index = (qi::lit('[') >> qi::skip(ascii::space)[explist >> qi::lit(']')]);

This way, whitespace is not significant inside of the [] braces, but is outside.

However, now I want to add my own custom skipper (I want to make newlines significant and later add comment-skipping). My skipper grammar looks like:

struct skip_grammar : qi::grammar<Iterator> {
  qi::rule<Iterator> start;
  skip_grammar() : skip_grammar::base_type(start) {
    start = qi::char_("\t\r ");
  }
};

I have been able to add it into my rule definitions just fine like

qi::rule<Iterator, PTR<Expression>(), skip_grammar> expression;

But I can't seem to figure out how to use my skip grammar as an argument to qi::skip (and replace ascii::space). I've tried using the type, a local instance variable, and a global instance variable. The farthest I've gotten is to get clang to complain that my skip_grammar needs a copy constructor. So I tried adding a copy constructor to my skip grammar, but apparently the boost::noncopyable base class is there for a reason, because my binary segfaulted almost immediately.

How should I be using this?

Thanks

1

1 Answers

7
votes

A qi::grammar is just a container for qi::rules. It does not have a copy constructor because this could inadvertently create dangling references in the parser expressions on the right hand side of those rules.

Using a grammar as a skipper as you want to do it is a bit tricky and amounts to passing the start rule of the grammar to the skip parser. It might be easier to just create a rule instance for that (especially if you have a single rule skipper).

In any case, the rule needs to be passed to the skipper as a reference (by calling the rule's member function alias()):

skip_grammar skippper;
index = '[' >> qi::skip(skipper.start.alias())[explist >> ']'];

or simply:

rule<iterator> skipper = qi::char_("\t\r ");
index = '[' >> qi::skip(skipper.alias())[explist >> ']'];

The aliasing is necessary because of the specifics of what it means to copy a rule. It is described in more detail in Spirit's FAQ here.