Search code examples
boostattributesboost-spiritboost-spirit-qiboost-phoenix

Boost Spirit rule with custom attribute parsing


I am writing a Boost Spirit grammar to parse text into a vector of these structs:

struct Pair
{
    double a;
    double b;
};

BOOST_FUSION_ADAPT_STRUCT(
    Pair,
    (double, a)
    (double, a)
)

This grammar has a rule like this:

qi::rule<Iterator, Pair()> pairSequence;

However, the actual grammar of pairSequence is this:

double_ % separator

I want this grammar to produce a Pair with a equal to the double and b equal to some constant. I want to do something like this:

pairSequence = double_[_val = Pair(_1, DEFAULT_B)] % separator;

The above does not compile, of course. I tried adding a constructor to Pair, but I still get compile errors (no matching function for call to 'Pair::Pair(const boost::phoenix::actor >&, double)').


Solution

  • First of all, the signature of pairSequence needs to be:

    qi::rule<Iterator, std::vector<Pair>()> pairSequence; 
    

    as the list operator exposes a std::vector<Pair> as its attribute.

    All functions called from inside a semantic action have to be 'lazy', so you need to utilize phoenix:

    namespace phx = boost::phoenix;
    
    pairSequence = 
        double_[
            phx::push_back(_val, 
                phx::construct<Pair>(_1, phx::val(DEFAULT_B))
            )
        ] % separator
    ; 
    

    Another possibility would be to add a (non-explicit) constructor to Pair:

    struct Pair         
    {         
        Pair(double a) : a(a), b(DEFAULT_B) {}
    
        double a;         
        double b;         
    };         
    

    which allows to simplify the grammar:

    pairSequence = double_ % separator; 
    

    and completely relies on Spirit's built-in attribute propagation rules.

    BTW, for any of this to work, you don't need to adapt Pair as a Fusion sequence.