Search code examples
c++boost-spiritboost-spirit-qiboost-phoenix

Getting compilation errors while binding a rule production to my struct members


Writing Qi grammar with Phoenix bind I got a compilation error like

boost/spirit/home/support/context.hpp(180): error C2338: index_is_out_of_bounds

here

>> ruleHandId_[phx::bind(&parseContext::handId_, qi::_r1) = qi::_1];

I just havent too much expirience with phoenix binding but perv bind in the line

ruleStart_ = ruleEncoding_[phx::bind(&parseContext::encoding_, qi::_r1) = qi::_1]

works good without compilation errors

It's all under MSVC from VS2013 with boost 1.56 x86

Whats I do wrong under code with the compilation error?

Source Code

#include <boost/spirit/include/qi.hpp>
#include <boost/phoenix/phoenix.hpp>
#include <boost/shared_ptr.hpp>
#include <sstream>

namespace sp = boost::spirit;
namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;

struct parseContext {
  std::string encoding_;
  uint64_t    handId_;
};

typedef boost::shared_ptr<parseContext> parseContextShPtr;

template <typename Iterator>
struct parseGrammar : qi::grammar<Iterator, void(parseContext&)> {
  parseGrammar() : parseGrammar::base_type(ruleStart_)
  {
    ruleStart_ = ruleEncoding_[phx::bind(&parseContext::encoding_, qi::_r1) = qi::_1]
      >> ruleHandHeader_;
    ruleEncoding_ = qi::lit("ABC");

    ruleHandHeader_ = qi::lit("DEF") >> qi::space
      >> qi::lit("XYZ #")
      >> ruleHandId_[phx::bind(&parseContext::handId_, qi::_r1) = qi::_1];
    ruleHandId_ = qi::long_long;
  }

  // Rules
  qi::rule<Iterator, void(parseContext&)> ruleStart_;
  qi::rule<Iterator, std::string()> ruleEncoding_;
  qi::rule<Iterator> ruleHandHeader_;
  qi::rule<Iterator, uint64_t> ruleHandId_;
};

void test()
{
  std::string s("ABCDEF XYZ #555: PQI #777");
  std::stringstream sb;
  sb.unsetf(std::ios::skipws);
  sb << s;
  const parseGrammar<sp::istream_iterator> p;
  sp::istream_iterator b(sb);
  sp::istream_iterator e;

  parseContextShPtr ctx(new parseContext);
  bool r = qi::parse(b, e, p(phx::ref(*ctx.get())));
  if (r) {
    std::cout << "Success" << std::endl;
  }
  else {
    std::cout << "Failure" << std::endl;
  }

  std::cout << std::string(b, e).substr(0, 32) << std::endl;
}

Solution

  • Some of the placeholders cannot be bound.

    This could be because ruleEncoding_ doesn't expose an attribute (for _1) (unlikely) or ruleStart_ doesn't have the inherited attribute (_r1).

    That's all I can tell you right now.

    Edit It was the latter. ruleHandHeader doesn't declare any attributes, let alone an inherited attribute to bind to _r1

    Update To the comment.

    Here are some suggestions. Much in the vein of my oft-repeated advice to avoid semantic actions (Boost Spirit: "Semantic actions are evil"?), I'd adapt the structure as a fusion sequence:

    And use much simplified grammar rules:

        ruleStart_      = ruleEncoding_ >> ruleHandHeader_;
        ruleEncoding_   = "ABC";
        ruleHandId_     = qi::long_long;
        ruleHandHeader_ = "DEF XYZ #" >> ruleHandId_;
    

    Now, adding in BOOST_SPIRIT_DEBUG macros and fixing uint64_t to uint64_t() in the rule definition:

    Live On Coliru

    #define BOOST_SPIRIT_DEBUG
    #include <boost/spirit/include/qi.hpp>
    #include <boost/fusion/adapted/struct.hpp>
    #include <boost/shared_ptr.hpp>
    #include <sstream>
    
    namespace qi = boost::spirit::qi;
    
    struct parseContext {
        std::string encoding_;
        uint64_t    handId_;
    };
    
    BOOST_FUSION_ADAPT_STRUCT(parseContext, (std::string, encoding_)(uint64_t,handId_))
    
    typedef boost::shared_ptr<parseContext> parseContextShPtr;
    
    template <typename Iterator>
    struct parseGrammar : qi::grammar<Iterator, parseContext()> {
        parseGrammar() : parseGrammar::base_type(ruleStart_)
        {
            ruleStart_      = ruleEncoding_ >> ruleHandHeader_;
            ruleEncoding_   = "ABC";
            ruleHandId_     = qi::long_long;
            ruleHandHeader_ = "DEF XYZ #" >> ruleHandId_;
    
            BOOST_SPIRIT_DEBUG_NODES((ruleStart_)(ruleEncoding_)(ruleHandId_)(ruleHandHeader_))
        }
    
        // Rules
        qi::rule<Iterator, parseContext()> ruleStart_;
        qi::rule<Iterator, std::string()> ruleEncoding_;
        qi::rule<Iterator, uint64_t()> ruleHandId_, ruleHandHeader_;
    };
    
    void test()
    {
        std::stringstream sb("ABCDEF XYZ #555: PQI #777");
        sb.unsetf(std::ios::skipws);
    
        typedef boost::spirit::istream_iterator It;
        const parseGrammar<It> p;
        It b(sb), e;
    
        parseContextShPtr ctx(new parseContext);
        bool r = qi::parse(b, e, p, *ctx);
        if (r) {
            std::cout << "Success: " << ctx->encoding_ << ", " << ctx->handId_ << std::endl;
        }
    
        else {
            std::cout << "Failure" << std::endl;
        }
    
        if (b!=e)
            std::cout << "Remaining: '" << std::string(b, e).substr(0, 32) << "'...\n";
    }
    
    int main()
    {
        test();
    }
    

    Prints

    Success: ABC, 555
    Remaining: ': PQI #777'...