i am trying to parse a character stream and do a lookup on the second character parsed to get the number of repeats required for the third character. Variable objSize however does not get referenced correctly within repeat and full parse fails. If i assign objSize = 3 i get Full Parse Pass. Here is the code i am using. Any help much appreciated.
#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <iostream>
#include <iomanip>
#include <boost/spirit/include/qi.hpp>
#include <boost/phoenix/phoenix.hpp>
#include <boost/spirit/include/qi_char.hpp>
#include <boost/spirit/include/qi_repeat.hpp>
#include <string>
#include <vector>
namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;
void write_to_con (const std::vector<unsigned char> &v){
for (int i = 0; i <v.size(); ++i)
std::cout << std::setw(2) << std::setfill('0') << std::hex <<int(v[i]) <<" ";
std::cout << std::endl;
}
int getObjSize(unsigned char t)
{
if(t == '\x02')
return 3;
else return 0;
}
int main()
{
std::vector<unsigned char> v = {'\x01','\x02','\x03','\x03','\x03'};
int objSize = 0;
auto it = v.begin();
bool match = boost::spirit::qi::parse(it, v.end(),
//Begin Grammar
(
(qi::char_('\x01'))>>(qi::char_[([&](unsigned char c){phx::ref(objSize) = getObjSize(c);})])>>(qi::repeat(phx::ref(objSize))[qi::char_])
)
//End Grammar
);
if(match){
std::cout << "Parse Success\n";
}
else
std::cout << "Parse Fail\n";
if (it != v.end()){
std::vector<unsigned char> ret(it, v.end());
std::cout << "Full Parse Failed at:";
write_to_con(ret);
}
else
std::cout << "Full Parse Pass\t Size:" <<v.size() << "\n";
return 0;
}
You action is a lambda:
[&](unsigned char c) { phx::ref(objSize) = getObjSize(c); };
That's not a Phoenix Actor. Yet you are using phx::ref
inside it (that's not gonna do what you want).
One valid way to specify the action would seem:
qi::char_[phx::ref(objSize) = phx::bind(getObjSize, qi::_1)]
Here's a cleanified way to write that:
qi::rule<decltype(it)> p;
{
using namespace qi;
p = (
(char_('\x01'))
>> (char_[phx::ref(objSize) = phx::bind(getObjSize, _1)])
>> (repeat(phx::ref(objSize))[char_])
);
}
bool match = boost::spirit::qi::parse(it, v.end(), p);
Prints Live On Coliru
Parse Success
Full Parse Pass Size:5
This grammar has drawbacks: it refers to a local (objectSize
) and as such has lifetime issues. Ideally the local should be part of the rule. This would, at once, make the rule reentrant.
Enter qi::locals<>
:
qi::rule<decltype(it), qi::locals<int> > p;
{
using namespace qi;
_a_type objSize; // the first local placeholder
p = (
(char_('\x01'))
>> (char_[objSize = phx::bind(getObjSize, _1)])
>> (repeat(objSize)[char_])
);
}
Because getObjSize
is so simple, the phx::bind
seems too ugly. Why not integrate it:
qi::rule<decltype(it), qi::locals<int> > p;
{
using namespace qi;
_a_type objSize; // the first local placeholder
struct size_f { int operator()(unsigned char t) const { return t=='\x02'?3:0; } };
phx::function<size_f> get_size;
p = (
char_('\x01')
>> char_[objSize = get_size(_1)]
>> repeat(objSize)[char_]
);
}
See it Live On Coliru