I'm writing a parser for PureData patches using Boost spirit and C++.
I have the following simple test of parsing canvas records:
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/adapted/adt/adapt_adt.hpp>
#include <boost/fusion/include/adapt_adt.hpp>
struct PdCanvas {
int topX;
int topY;
int wX;
int wY;
std::string name;
int openOnLoad;
};
BOOST_FUSION_ADAPT_STRUCT(
PdCanvas,
(int, topX)
(int, topY)
(int, wX)
(int, wY)
(std::string, name)
(int, openOnLoad));
template <typename Iterator>
struct PdCanvasGrammar : qi::grammar<Iterator, PdCanvas(), ascii::space_type> {
PdCanvasGrammar() : PdCanvasGrammar::base_type(canvasRule){
canvasRule = qi::lit("#N canvas") >> qi::int_ >> qi::int_ >> qi::int_ >> qi::int_ >> +(qi::char_ - qi::space) >> qi::int_ >> ";";
}
qi::rule<Iterator, PdCanvas(), ascii::space_type> canvasRule;
};
int main(int argc, char** argv)
{
if(argc != 2)
{
std::cout << "Usage: " <<argv[0] << " <PatchFile>" << std::endl;
exit(1);
}
std::ifstream inputFile(argv[1]);
std::string inputString(std::istreambuf_iterator<char>(inputFile), {});
PdCanvas root;
PdCanvasGrammar<std::string::iterator> parser;
std::cout << "Loaded file:\n " << inputString << std::endl;
bool success = qi::phrase_parse(inputString.begin(), inputString.end(), parser, boost::spirit::ascii::space, root);
std::cout << "Success: " << success << std::endl;
return 0;
}
As one can see, the format of a canvas record is
#N canvas <int> <int> <int> <int> <string> <int>;
And that's what the rule should expect, but when I try to parse the following:
#N canvas 0 0 400 300 moo 1;
qi::phrase_parse returns false, indicating an unsuccessful parse.
As an aside, there is another form of the canvas grammar in PD, specifically for the root, which is of the form:
#N canvas <int> <int> <int> <int> <int>;
Which I have successfully parsed using a different rule, so my assumption is the problem comes from attempting to parse the string in the middle of the integers.
So my question is thus: What is wrong with my qi::rule and how can I change it to properly parse?
Two things:
Greedy Parsing
Note that PEG grammars are "greedy left-to-right", so you will want to make sure that the
int_ >> ";"is not parsed into the name:Live On Coliru
Prints:
Skipping Whitespace
I chose some outrageous "names" on purpose:
Your rule has a skipper:
space_type. This - by definition - means that+(qi::char_ - qi::space)is equivalent to+qi::char_because spaces aren't even seen by the expression.To alleviate the issue make sure that the space-sensitive expression does not execute under the skipper, see Boost spirit skipper issues.
Using
lexeme[]here is the quickest solution:Prints Live:
To also disallow space in the name, use
qi::graphinstead ofqi::char_:Prints Live:
Bonus Tips
To make things easier to maintain, debug (!!) and also express intent, I'd
qi::eoi)Live On Coliru
Prints
Or, with debug enabled, e.g.