Search code examples
c++parsingboostboost-spiritboost-phoenix

Boost spirit semantic action not invoked


I've been trying to parse a string with Boost Spirit like following:

integer_count int1 int2 int3 ... intN

Where N is the integer_count. For example,

5 1 2 3 4 5

The code is following:

#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <string>

namespace qi = boost::spirit::qi;
namespace spirit = boost::spirit;
namespace ascii = boost::spirit::ascii;
using boost::phoenix::ref;

template <typename Iterator>
struct x_grammar : public qi::grammar<Iterator, std::string(), ascii::space_type>
{   
public:
    x_grammar() : x_grammar::base_type(start_rule, "x_grammar")
    {   
        using namespace qi; 
        int repeat_count = 0;
        start_rule = int_[ref(repeat_count) = _1] > repeater > *char_;
        std::cout << "repeat_count == " << repeat_count << std::endl;
        repeater = repeat(repeat_count)[int_[std::cout << _1 << ".\n"]];
    }   
private:
    qi::rule<Iterator, std::string(), ascii::space_type> start_rule;
    qi::rule<Iterator, std::string(), ascii::space_type> repeater;
};  

int main()
{   
    typedef std::string::const_iterator iter;
    std::string storage("5 1 2 3 4 5 garbage");
    iter it_begin(storage.begin());
    iter it_end(storage.end());
    std::string read_data;
    using boost::spirit::ascii::space;
    x_grammar<iter> g;
    try {
        bool r = qi::phrase_parse(it_begin, it_end, g, space, read_data);
        if(r) {
            std::cout << "Pass!\n";
        } else {
            std::cout << "Fail!\n";
        }   
    } catch (const qi::expectation_failure<iter>& x) {
        std::cout << "Fail!\n";
    }   
}   

The output is:

repeat_count == 0
Pass!

Which means that

ref(repeat_count) = _1

Was never invoked. So is there a way to read an integer runtime into a local variable and use that value for further parsing?


Solution

  • I think you meant

    repeat(boost::phoenix::ref(repeat_count))
    

    Also, automatic attribute propagation is disabled for a rule when there are semantic actions. You can use %= to force attribute propagation

    Here's a simple fixed version Live On Coliru

    #define BOOST_SPIRIT_USE_PHOENIX_V3
    #include <boost/spirit/include/qi.hpp>
    #include <boost/spirit/include/phoenix.hpp>
    #include <string>
    
    namespace qi = boost::spirit::qi;
    namespace spirit = boost::spirit;
    namespace ascii = boost::spirit::ascii;
    using boost::phoenix::ref;
    
    template <typename Iterator>
    struct x_grammar : public qi::grammar<Iterator, ascii::space_type>
    {   
    public:
        x_grammar() : x_grammar::base_type(start_rule, "x_grammar")
        {   
            using namespace qi; 
            int repeat_count = 0;
            start_rule = int_[ref(repeat_count) = _1] > repeater > *char_;
            std::cout << "repeat_count == " << repeat_count << std::endl;
            repeater = repeat(ref(repeat_count))[int_[std::cout << _1 << ".\n"]];
        }   
    private:
        qi::rule<Iterator, ascii::space_type> start_rule;
        qi::rule<Iterator, ascii::space_type> repeater;
    };  
    
    int main()
    {   
        typedef std::string::const_iterator iter;
        std::string storage("5 1 2 3 4 5 garbage");
        iter it_begin(storage.begin());
        iter it_end(storage.end());
        using boost::spirit::ascii::space;
        x_grammar<iter> g;
        try {
            bool r = qi::phrase_parse(it_begin, it_end, g, space);
            if(r) {
                std::cout << "Pass!\n";
            } else {
                std::cout << "Fail!\n";
            }   
        } catch (const qi::expectation_failure<iter>& x) {
            std::cout << "Fail!\n";
        }   
    }   
    

    Prints

    repeat_count == 0
    1.
    2.
    3.
    4.
    5.
    Pass!
    

    Note How, obviously, repeat_count == 0 is always printed, since it's executed during grammar construction, not parsing.