Addressing the issues, first:
BOOST_FUSION_ADAPT_STRUCT needs to be at global namespace
The macro should be used at global scope, and struct_name should be the fully namespace qualified name of the struct to be adapted. [documentation]
you needed a skipper to allow for whitespace as in your sample input
- this also implies you need
qi::phrase_parse
to supply a skipper - this also implies that
begin == end
was redundant (becauseqi::parse
always consumes the whole input on success AFAIR) - this also implies you need
qi::lexeme
to control when you expect whitespace as part of the parsed content
- this also implies you need
you needed the callbacks to be
static
- you needed to modify the
_fruit
rule to read until the closing]
With these taken care of, your code works: See it Live on Coliru
Bonus material
Just in case you didn't know, you don't have to jump through all these fusion/phoenix hoops if you don't want. You'd usually parse into datastructures first, and then process there.
See this demonstrated Live On Coliru too.
E.g., data structures:
namespace ast {
struct fruit {
std::string name;
std::string color;
};
struct person {
std::string name;
int age;
};
typedef boost::variant<fruit, person> record;
typedef std::vector<record> records;
// for our demo output:
static std::ostream& operator<<(std::ostream& os, fruit const& f);
static std::ostream& operator<<(std::ostream& os, person const& p);
}
And the code that handles it (note improved status reporting):
int main()
{
typedef std::string::const_iterator It;
std::string const input("(jane, 23000)(david, 19)(mary, 30)[yello,100][green, 60.6][red, 30.5]");
MyGrammar <It> my_grammar;
It begin(input.begin()), end(input.end());
ast::records data;
if (qi::phrase_parse(begin, end, my_grammar, qi::space, data))
{
std::cout << "Parse success\n";
for (auto const& record : data)
std::cout << record << "\n";
}
else
std::cout << "Parse failed\n";
if (begin != end)
std::cout << "Remaining unparsed: '" << std::string(begin, end) << "'\n";
}
Now, the clincher is, the grammar becamse simpler too (no more phoenix; I assumed that names/colors should not contain ,
,]
or )
):
_text = qi::lexeme [ *~qi::char_(",)]") ];
_person = qi::lit('(') >> _text >> ',' >> qi::int_ >> ')';
_fruit = qi::lit('[') >> _text >> ',' >> _text >> ']';
_start = *(_fruit | _person);
Here's the full code listing:
#include <string>
#include <iostream>
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
namespace ast {
struct fruit {
std::string name;
std::string color;
friend std::ostream& operator<<(std::ostream& os, fruit const& f) { return os << f.name << " is " << f.color; }
};
struct person {
std::string name;
int age;
friend std::ostream& operator<<(std::ostream& os, person const& p) { return os << p.name << " with age" << p.age << " has been seen!"; }
};
typedef boost::variant<fruit, person> record;
typedef std::vector<record> records;
}
BOOST_FUSION_ADAPT_STRUCT(ast::fruit,
(std::string, name)
(std::string, color))
BOOST_FUSION_ADAPT_STRUCT(ast::person,
(std::string, name)
(int, age))
template <typename Iterator, typename Skipper = qi::space_type>
struct MyGrammar : qi::grammar<Iterator, ast::records(), Skipper>
{
MyGrammar() : MyGrammar::base_type(_start)
{
_text = qi::lexeme [ *~qi::char_(",)]") ];
_person = qi::lit('(') >> _text >> ',' >> qi::int_ >> ')';
_fruit = qi::lit('[') >> _text >> ',' >> _text >> ']';
_start = *(_fruit | _person);
}
qi::rule<Iterator, ast::records(), Skipper> _start;
qi::rule<Iterator, ast::person(), Skipper> _person;
qi::rule<Iterator, ast::fruit(), Skipper> _fruit;
qi::rule<Iterator, std::string()> _text;
};
int main()
{
typedef std::string::const_iterator It;
std::string const input("(jane, 23000)(david, 19)(mary, 30)[yello,100][green, 60.6][red, 30.5]");
MyGrammar <It> my_grammar;
It begin(input.begin()), end(input.end());
ast::records data;
if (qi::phrase_parse(begin, end, my_grammar, qi::space, data))
{
std::cout << "Parse success\n";
for (auto& r : data)
std::cout << r << "\n";
}
else
std::cout << "Parse failed\n";
if (begin != end)
std::cout << "Remaining unparsed: '" << std::string(begin, end) << "'\n";
}