Thomas Matthews
Thomas Matthews

Reputation: 57688

Boost::Spirit::Qi - Splitting rules into separate classes

I would like to split up my rules (productions) into separate classes. I can't find any example in Boost::Spirit::Qi for doing this.

The Boost examples all show the rules in one grammar class.

Here is my grammar:

<start> ::= @ ( <event_bool_no_param> )
<event_bool_no_param> ::= RAMPING_COMPLETED | STATE_TIMEOUT  

Here is my grammar class:

template <typename Iterator, typename Skipper>
struct Event_Grammar
    : boost::spirit::qi::grammar<Iterator, Skipper>
{
    Event_Grammar() : Event_Grammar::base_type(start)
        {
            using boost::spirit::ascii::char_;
            using boost::spirit::qi::eps;

            start = 
                (
                    char_('@') >> char_('(') >> Event_Bool_No_Param<Iterator>() >> char_(')')
                )
                ;

        }
    boost::spirit::qi::rule<Iterator, Skipper> start;
};

Here is my other grammar class:

template <typename Iterator>
struct Event_Bool_No_Param
    : qi::grammar<Iterator>
{
    Event_Bool_No_Param () 
        : Event_Bool_No_Param::base_type(start)
        {
            using qi::lexeme;
            using qi::lit;

            start =
                lit("STATE_TIMEOUT") | lit("RAMPING_COMPLETED") | lit("PASSIVE_MEAS_COMPLETED")
                ;
        }
    qi::rule<Iterator> start;
};

I'm getting an unhandled exception error with the text "@ ( STATE_TIMEOUT )".

Here's the top (recent) stack trace:

Event_Grammar.exe!boost::spirit::qi::sequence_base<boost::spirit::qi::sequence<boost::fusion::cons<boost::spirit::qi::literal_char<boost::spirit::char_encoding::ascii,0,0>,boost::fusion::cons<boost::spirit::qi::literal_char<boost::spirit::char_encoding::ascii,0,0>,boost::fusion::cons<boost::spirit::qi::reference<boost::spirit::qi::rule<std::_String_const_iterator<char,std::char_traits<char>,std::allocator<char> >,boost::spirit::unused_type,boost::spirit::unused_type,boost::spirit::unused_type,boost::spirit::unused_type> const >,boost::fusion::cons<boost::spirit::qi::literal_char<boost::spirit::char_encoding::ascii,0,0>,boost::fusion::nil_> > > > >,boost::fusion::cons<boost::spirit::qi::literal_char<boost::spirit::char_encoding::ascii,0,0>,boost::fusion::cons<boost::spirit::qi::literal_char<boost::spirit::char_encoding::ascii,0,0>,boost::fusion::cons<boost::spirit::qi::reference<boost::spirit::qi::rule<std::_String_const_iterator<char,std::char_traits<char>,std::allocator<char> >,boost::spirit::unused_type,boost::spirit::unused_type,boost::spirit::unused_type,boost::spirit::unused_type> const >,boost::fusion::cons<boost::spirit::qi::literal_char<boost::spirit::char_encoding::ascii,0,0>,boost::fusion::nil_> > > > >::parse_impl<std::_String_const_iterator<char,std::char_traits<char>,std::allocator<char> >,boost::spirit::context<boost::fusion::cons<boost::spirit::unused_type &,boost::fusion::nil_>,boost::fusion::vector0<void> >,boost::spirit::qi::char_class<boost::spirit::tag::char_code<boost::spirit::tag::space,boost::spirit::char_encoding::ascii> >,boost::spirit::unused_type>(std::_String_const_iterator<char,std::char_traits<char>,std::allocator<char> > & first, const std::_String_const_iterator<char,std::char_traits<char>,std::allocator<char> > & last, boost::spirit::context<boost::fusion::cons<boost::spirit::unused_type &,boost::fusion::nil_>,boost::fusion::vector0<void> > & context, const boost::spirit::qi::char_class<boost::spirit::tag::char_code<boost::spirit::tag::space,boost::spirit::char_encoding::ascii> > & skipper, boost::spirit::unused_type & attr_, boost::mpl::bool_<0> __formal)  Line 88 + 0x55 bytes C++
    Event_Grammar.exe!boost::spirit::qi::sequence_base<boost::spirit::qi::sequence<boost::fusion::cons<boost::spirit::qi::literal_char<boost::spirit::char_encoding::ascii,0,0>,boost::fusion::cons<boost::spirit::qi::literal_char<boost::spirit::char_encoding::ascii,0,0>,boost::fusion::cons<boost::spirit::qi::reference<boost::spirit::qi::rule<std::_String_const_iterator<char,std::char_traits<char>,std::allocator<char> >,boost::spirit::unused_type,boost::spirit::unused_type,boost::spirit::unused_type,boost::spirit::unused_type> const >,boost::fusion::cons<boost::spirit::qi::literal_char<boost::spirit::char_encoding::ascii,0,0>,boost::fusion::nil_> > > > >,boost::fusion::cons<boost::spirit::qi::literal_char<boost::spirit::char_encoding::ascii,0,0>,boost::fusion::cons<boost::spirit::qi::literal_char<boost::spirit::char_encoding::ascii,0,0>,boost::fusion::cons<boost::spirit::qi::reference<boost::spirit::qi::rule<std::_String_const_iterator<char,std::char_traits<char>,std::allocator<char> >,boost::spirit::unused_type,boost::spirit::unused_type,boost::spirit::unused_type,boost::spirit::unused_type> const >,boost::fusion::cons<boost::spirit::qi::literal_char<boost::spirit::char_encoding::ascii,0,0>,boost::fusion::nil_> > > > >::parse<std::_String_const_iterator<char,std::char_traits<char>,std::allocator<char> >,boost::spirit::context<boost::fusion::cons<boost::spirit::unused_type &,boost::fusion::nil_>,boost::fusion::vector0<void> >,boost::spirit::qi::char_class<boost::spirit::tag::char_code<boost::spirit::tag::space,boost::spirit::char_encoding::ascii> >,boost::spirit::unused_type>(std::_String_const_iterator<char,std::char_traits<char>,std::allocator<char> > & first, const std::_String_const_iterator<char,std::char_traits<char>,std::allocator<char> > & last, boost::spirit::context<boost::fusion::cons<boost::spirit::unused_type &,boost::fusion::nil_>,boost::fusion::vector0<void> > & context, const boost::spirit::qi::char_class<boost::spirit::tag::char_code<boost::spirit::tag::space,boost::spirit::char_encoding::ascii> > & skipper, boost::spirit::unused_type & attr_)  Line 125 C++
    Event_Grammar.exe!boost::spirit::qi::detail::parser_binder<boost::spirit::qi::sequence<boost::fusion::cons<boost::spirit::qi::literal_char<boost::spirit::char_encoding::ascii,0,0>,boost::fusion::cons<boost::spirit::qi::literal_char<boost::spirit::char_encoding::ascii,0,0>,boost::fusion::cons<boost::spirit::qi::reference<boost::spirit::qi::rule<std::_String_const_iterator<char,std::char_traits<char>,std::allocator<char> >,boost::spirit::unused_type,boost::spirit::unused_type,boost::spirit::unused_type,boost::spirit::unused_type> const >,boost::fusion::cons<boost::spirit::qi::literal_char<boost::spirit::char_encoding::ascii,0,0>,boost::fusion::nil_> > > > >,boost::mpl::bool_<0> >::call<std::_String_const_iterator<char,std::char_traits<char>,std::allocator<char> >,boost::spirit::qi::char_class<boost::spirit::tag::char_code<boost::spirit::tag::space,boost::spirit::char_encoding::ascii> >,boost::spirit::context<boost::fusion::cons<boost::spirit::unused_type &,boost::fusion::nil_>,boost::fusion::vector0<void> > >(std::_String_const_iterator<char,std::char_traits<char>,std::allocator<char> > & first, const std::_String_const_iterator<char,std::char_traits<char>,std::allocator<char> > & last, boost::spirit::context<boost::fusion::cons<boost::spirit::unused_type &,boost::fusion::nil_>,boost::fusion::vector0<void> > & context, const boost::spirit::qi::char_class<boost::spirit::tag::char_code<boost::spirit::tag::space,boost::spirit::char_encoding::ascii> > & skipper, boost::mpl::bool_<0> __formal)  Line 44    C++

Here's the code location where the exception takes place, function_template.hpp#761 :

result_type operator()(BOOST_FUNCTION_PARMS) const
{
  if (this->empty())
    boost::throw_exception(bad_function_call());

  return get_vtable()->invoker
           (this->functor BOOST_FUNCTION_COMMA BOOST_FUNCTION_ARGS);
}

So, my questions are:

  1. Do all rules need to be in the single grammar class?
  2. How do I reference a grammar class in a rule?
  3. Where is my mistake in my classes above?

I am using:

Upvotes: 0

Views: 271

Answers (2)

mjcaisse
mjcaisse

Reputation: 221

First the questions:

  1. Do all rules need to be in the single grammar class?

    Absolutely not. Grammars are simply a mechanism to group rules together and provide additional state if needed. A grammar is just a class/struct so it is a very handy device to encapsulate the concept of a grammar.

  2. How do I reference a grammar class in a rule?

    You need an instance of the grammar. A temporary is going to cause grief.

  3. Where is my mistake in my classes above?

    The mistake is that you are creating temporaries when building your rules. You want an instance of the grammar (or rule) that will be composed into other grammars and rules.

For example, the following:

Live On Coliru

#include <boost/spirit/home/qi.hpp>
namespace qi    = boost::spirit::qi;
namespace ascii = boost::spirit::qi::ascii;

template <typename Iterator>
struct Event_Bool_No_Param : qi::grammar<Iterator>
{
    Event_Bool_No_Param () 
        : Event_Bool_No_Param::base_type(start)
    {
        using qi::lit;

        start =
            lit("STATE_TIMEOUT") | lit("RAMPING_COMPLETED") | lit("PASSIVE_MEAS_COMPLETED")
            ;
    }
    qi::rule<Iterator> start;
};


template <typename Iterator, typename Skipper>
struct Event_Grammar : boost::spirit::qi::grammar<Iterator, Skipper>
{
    Event_Grammar() : Event_Grammar::base_type(start)
    {
        using boost::spirit::ascii::char_;

        start = 
            char_('@') >> char_('(') >> event_no_param >> char_(')')
            ;

    }
    qi::rule<Iterator, Skipper> start;
    Event_Bool_No_Param<Iterator> event_no_param;
};

int main()
{
    using iterator_t = std::string::const_iterator;
    std::string input = "@ ( STATE_TIMEOUT )";
    iterator_t iter = input.begin();
    iterator_t end = input.end();

    Event_Grammar<iterator_t,ascii::space_type> grammar;   

    bool ok = qi::phrase_parse( iter, end
            , grammar
            , ascii::space
            );

    return ok? 0 : 255;
}

Which OR?

As for which OR symbol to use, | and || are not the same thing. You very likely want | .

| - is an alternative.

A | B | C

  • parse an A or a B or a C

|| - is the sequential or operator (sequential or)

A || B * parse A followed by an optional B or parse B

    A >> -B | B

Upvotes: 3

Thomas Matthews
Thomas Matthews

Reputation: 57688

There are two issues here which, to me, are not obvious:

  • A grammar variable
  • Using "||" instead of "|".

The Grammar Instance

A rule can use other grammars, but needs to have an instance of the other grammar.

The Event_Grammar class now looks like:

    #include "event_bool_no_param.hpp"

    template <typename Iterator, typename Skipper>
    struct Event_Grammar
        : boost::spirit::qi::grammar<Iterator, Skipper>
    {
        Event_Grammar() : Event_Grammar::base_type(start)
            {
                using boost::spirit::ascii::char_;
                using boost::spirit::qi::eps;
                using qi::lit;

// Notice the identifier "grammar_bool_no_param"
// which is an instance of the grammer / rule Event_Bool_No_Param.
                start = 
                    (
                        char_('@') >> char_('(') >> grammar_bool_no_param >> char_(')')
                    )
                    ;

            }

        // *** A rule or grammar needs an instance!
        Event_Bool_No_Param<Iterator>               grammar_bool_no_param;
        boost::spirit::qi::rule<Iterator, Skipper>  start;
    };

The Correct "OR" Symbol

In a rule, the symbol for "OR" is "||" not "|":

template <typename Iterator>
struct Event_Bool_No_Param
    : qi::grammar<Iterator>
{
    Event_Bool_No_Param () 
        : Event_Bool_No_Param::base_type(start)
        {
            using qi::lexeme;
            using qi::lit;

            start =
                lit("STATE_TIMEOUT") || lit("RAMPING_COMPLETED") || lit("PASSIVE_MEAS_COMPLETED")
                ;
        }
    qi::rule<Iterator> start;
};

Upvotes: 1

Related Questions