Search code examples
c++11boostboost-spiritboost-spirit-qi

Boost::spirit::qi - How do I build a parse rule that sets a property?


I'd like to build a rule that takes in a few parameters from a parsed line then sets a few as constant. Is that possible? An (invalid) example to illustrate what I'm trying to do is below. I think I'm using _r1 incorrectly here, but I'm not sure how to get at the right thing. Assume I don't want to just modify r before sending it into the parser.

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/qi_plus.hpp>
#include <boost/spirit/include/qi_sequence.hpp>
#include <boost/spirit/include/qi_string.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_object.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/adapted/struct/adapt_struct.hpp>
#include <boost/phoenix/bind/bind_function.hpp>
#include <string>

using namespace boost::spirit::qi;
struct Sample
{
    int a;
    int b;
};
BOOST_FUSION_ADAPT_STRUCT(Sample, a , b)

const rule<std::string::const_iterator, Sample()> AnythingAndOne = int_ >> eps[_r1.b = 1] >> eoi;


int main()
{
    std::string z("3");
    Sample r;
    parse(z.begin(), z.end(), AnythingAndOne, r);
    return 0;
}

Solution

  • Again, with reference to Boost Spirit: "Semantic actions are evil"? I'd avoid the semantic action.

    You can directly synthesize a particular attribute value by using qi::attr:

    Live On Coliru

    #include <boost/spirit/include/qi.hpp>
    #include <boost/fusion/include/adapt_struct.hpp>
    #include <boost/fusion/include/io.hpp>
    
    struct Sample {
        int a;
        int b;
    };
    BOOST_FUSION_ADAPT_STRUCT(Sample, a , b)
    
    namespace qi = boost::spirit::qi;
    
    int main()
    {
        std::string const z("3");
        Sample r;
        qi::rule<std::string::const_iterator, Sample()> AnythingAndOne
             = qi::int_ >> qi::attr(1) >> qi::eoi;
    
        if (parse(z.begin(), z.end(), AnythingAndOne, r))
            std::cout << "Parsed: " << boost::fusion::as_vector(r) << "\n";
        else
            std::cout << "Parse failed\n";
    }
    

    Prints

    Parsed: (3 1)