In general the following directives are helpful for inhibiting/switching skippers mid-grammar:
qi::lexeme [ p ]
which inhibits a skipper, e.g. if you want to be sure you parse an identifier without internal skips) - see also no_skip
for comparison
qi::raw [ p ]
which parses like always, including skips, but returns the raw iterator range of the matched source sequence (including the skipped positions)
qi::no_skip [ p ]
Inhibiting Skipping Without Pre-skip (I've created a minimal example to demonstrate the difference here: Boost Spirit lexeme vs no_skip)
qi::skip(s) [ p ]
which replaces the skipper by another skipper s
altogether (note that you need to use appropriately declared qi::rule<>
instances inside such a skip[]
clause)
where p
is any parser expression.
Specific solution
Your problem, as you already know, might be that qi::space
eats all whitespace. I can't possibly know what is wrong in your grammar (since you don't show either the full grammar, or relevant input).
Therefore, here's what I'd write. Note
- the use of
qi::eol
to explicitely require linebreaks at specific locations
- the use of
qi::blank
as a skipper (not including eol
)
- for brevity I combined the grammars
Code:
#define BOOST_SPIRIT_DEBUG
#include <boost/fusion/adapted.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;
struct rowType {
unsigned int number;
std::list<unsigned int> list;
};
struct problemType {
unsigned int ROW;
std::vector<rowType> rows;
};
BOOST_FUSION_ADAPT_STRUCT(rowType, (unsigned int, number)(std::list<unsigned int>, list))
BOOST_FUSION_ADAPT_STRUCT(problemType, (unsigned int, ROW)(std::vector<rowType>, rows))
template<typename Iterator>
struct problem_parser : qi::grammar<Iterator,problemType(),qi::blank_type>
{
problem_parser() : problem_parser::base_type(problem)
{
using namespace qi;
list = '[' >> -(int_ % ',') >> ']';
row = int_ >> list >> eol;
problem = "ROW" >> int_ >> eol >> +row;
BOOST_SPIRIT_DEBUG_NODES((problem)(row)(list));
}
qi::rule<Iterator, problemType() , qi::blank_type> problem;
qi::rule<Iterator, rowType() , qi::blank_type> row;
qi::rule<Iterator, std::list<unsigned int>(), qi::blank_type> list;
};
int main()
{
const std::string input =
"ROW 1
"
"2 [3, 4]
"
"5 [6, 7]
";
auto f = begin(input), l = end(input);
problem_parser<std::string::const_iterator> p;
problemType data;
bool ok = qi::phrase_parse(f, l, p, qi::blank, data);
if (ok) std::cout << "success
";
else std::cout << "failed
";
if (f!=l)
std::cout << "Remaining unparsed: '" << std::string(f,l) << "'
";
}
If you really didn't want to require line breaks:
template<typename Iterator>
struct problem_parser : qi::grammar<Iterator,problemType(),qi::space_type>
{
problem_parser() : problem_parser::base_type(problem)
{
using namespace qi;
list = '[' >> -(int_ % ',') >> ']';
row = int_ >> list;
problem = "ROW" >> int_ >> +row;
BOOST_SPIRIT_DEBUG_NODES((problem)(row)(list));
}
qi::rule<Iterator, problemType() , qi::space_type> problem;
qi::rule<Iterator, rowType() , qi::space_type> row;
qi::rule<Iterator, std::list<unsigned int>(), qi::space_type> list;
};
int main()
{
const std::string input =
"ROW 1 " // NOTE whitespace, obviously required!
"2 [3, 4]"
"5 [6, 7]";
auto f = begin(input), l = end(input);
problem_parser<std::string::const_iterator> p;
problemType data;
bool ok = qi::phrase_parse(f, l, p, qi::space, data);
if (ok) std::cout << "success
";
else std::cout << "failed
";
if (f!=l)
std::cout << "Remaining unparsed: '" << std::string(f,l) << "'
";
}
Update
In response to the comment: here is a snippet that shows how to read the input from a file. This was tested and works fine for me:
std::ifstream ifs("input.txt"/*, std::ios::binary*/);
ifs.unsetf(std::ios::skipws);
boost::spirit::istream_iterator f(ifs), l;
problem_parser<boost::spirit::istream_iterator> p;