Reputation: 19704
I am learning how to use boost::spirit and I am facing some problems even with quite simple parsers. I am trying to construct a parser that accepts a list of digits (only 0 or 1) separated by colons. The list can have 3 or 4 digits. Thus, 0:0:0
and 1:0:1:0
are valid, while for instance 0:0
or 0:0:0:0:0
are not.
In the following code you can see how I used the optional operator to specify that the first digit may exist or not. It, however, does not work (parsing fails for sequence 0:0:0
). Is there anything wrong in the code? I would say it is correct, but again, I just started learning Spirit.
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
namespace phoenix = boost::phoenix;
void parse_tuple(const std::string& tuple) {
using qi::char_;
auto begin = tuple.begin();
auto end = tuple.end();
bool r = qi::parse(begin, end,
-(char_('0', '1') >> ':') >>
char_('0', '1') >> ':' >>
char_('0', '1') >> ':' >>
char_('0', '1')
);
if (!r || begin != end)
throw std::runtime_error("wrong format");
}
int main() {
parse_tuple("0:0:0"); // It fails for this one
parse_tuple("0:0:0:0");
try { parse_tuple("0:0"); } catch (...) {
std::cout << "expected error\n"; }
try { parse_tuple("0:0:0:0:0"); } catch (...) {
std::cout << "expected error\n"; }
}
Upvotes: 1
Views: 984
Reputation: 393829
This would be the most straightforward solution:
bool r = qi::parse(begin, end,
char_("01") > ':' >
char_("01") > ':' >
char_("01") > -(':' > char_("01"))
);
Full sample http://liveworkspace.org/code/3U0QJW$0:
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
namespace qi = boost::spirit::qi;
void parse_tuple(const std::string& tuple) {
using qi::char_;
auto begin = tuple.begin();
auto end = tuple.end();
bool r = qi::parse(begin, end,
char_("01") > ':' >
char_("01") > ':' >
char_("01") > -(':' > char_("01"))
);
if (!r || begin != end)
throw std::runtime_error("wrong format");
}
int main() {
parse_tuple("0:0:0"); // It fails for this one
parse_tuple("0:0:0:0");
try { parse_tuple("0:0"); } catch (...) {
std::cout << "expected error\n"; }
try { parse_tuple("0:0:0:0:0"); } catch (...) {
std::cout << "expected error\n"; }
}
Upvotes: 0
Reputation: 11201
Here
bool r = qi::parse(begin, end, -(char_('0', '1') >> ':') >> char_('0', '1') >> ':' >> char_('0', '1') >> ':' >> char_('0', '1') );
The optional char_ should be the last one and not the first one. The rules are applied sequentially, so that when you are parsing "0:0:0", the first line of your code (the optional stuff) pass the test, and then your rule expects 3 digits to follow, not two.
In my opinion you should just use the % operator to match a list and check later if you parsed 3 or 4 elements.
EDIT OR use qi::repeat to improve the readability.
Upvotes: 1