Search code examples
boostboost-spiritboost-spirit-qi

Compound Attribute generation in Boost::Spirit parse rule


I have the following parsing rule:

filter = (input >> (qi::repeat(0,2)[char_(';') >> input]))

input is a rule that returns a std::vector<int>, vector that I will just call vec for short.

The question is: What compound attribute would the filter rule return?

I tried:

fusion::vector <vec,std::vector <fusion::vector <char,vec> > >

But it fails and I don't know why.


Solution

  • The attribute types resulting of the parser expressions are quite well-documented. But that can be disorienting and timeconsuming.

    Here's a trick: send in a sentinel to detect the attribute type:

    struct Sniffer
    {
        typedef void result_type;
    
        template <typename T>
        void operator()(T const&) const { std::cout << typeid(T).name() << "\n"; }
    };
    

    then using the folliing parser expression

     (input >> (qi::repeat(0,2)[qi::char_(';') >> input])) [ Sniffer() ]
    

    will dump:

    N5boost6fusion7vector2ISt6vectorIsSaIsEES2_INS1_IcS4_EESaIS5_EEEE
    

    which c++filt -1 will tell you represents:

    boost::fusion::vector2<
        std::vector<short, std::allocator<short> >, 
        std::vector<boost::fusion::vector2<char, std::vector<short, std::allocator<short> > >, 
                    std::allocator<boost::fusion::vector2<char, std::vector<short, std::allocator<short> > > 
                > > 
     >
    

    See it live on Coliru: http://coliru.stacked-crooked.com/view?id=3e767990571f8d0917aae745bccfa520-5c1d29aa57205c65cfb2587775d52d22

    boost::fusion::vector2<std::vector<short, std::allocator<short> >, std::vector<std::vector<short, std::allocator<short> >, std::allocator<std::vector<short, std::allocator<short> > > > >
    

    It might be so surprisingly complicated, in part, because char_(";") could have been ';' (or more explicitely lit(';')). Constrast with this (Coliru):

    boost::fusion::vector2<
        std::vector<short, ... >, 
        std::vector<std::vector<short, std::allocator<short> >, ... > >
    

    This should answer your question.

    Sidenotes: parsing things

    Don't underestimate automatic attribute propagation in Spirit. Frequently, you don't have to bother with the exact exposed types of attributes. Instead, rely on the (many) attribute transformations that Spirit uses to assign them to your supplied attribute references.

    I trust you know the list-operator (%) in spirit? I'll show you how you can use it without further ado:

    vector<vector<short>> data;
    
    qi::parse(f, l, qi::short_ % ',' % ';', data);
    

    Now, if you need to enforce the fact that it may be 1-3 elements, you might employ an eps with a Phoenix action to assert the maximum size:

    const string x = "1,2,3;2,3,4;3,4,5";
    auto f(begin(x)), l(end(x));
    
    if (qi::parse(f, l, 
            (qi::eps(phx::size(qi::_val) < 2) > (qi::short_ % ',')) % ';'
            , data))
    {
        cout << karma::format(karma::short_ % ',' % ';', data) << "\n";
    }
    cout << "remaining unparsed: '" << std::string(f,l) << "'\n";
    

    Prints:

    1,2,3;2,3,4
    remaining unparsed: ';3,4,5'