In a previous post (How do you create a generic parser using qi?) I was given some very good advice on how to create a parser-factory. Basic examples work fine, but I have problems combining factory-generated parsers. I would like to understand why my parsers fail and if my problem can be solved or I have to live with the current situation.
Example code (trimmed down as much as I could):
#include <boost/spirit/include/qi.hpp>
#include <boost/variant.hpp>
#include <string>
using namespace boost::spirit::qi;
using parse_iter = std::string::const_iterator;
// Rudimentary test of a boost::spirit qi parser
template<class Parser>
void check_parser(Parser& p,std::string s)
{
parse_iter first { s.begin() };
parse_iter end { s.end() };
bool ok = phrase_parse(first,end,p,space);
std::cout << "Parsing [" << s << "]. Status: "
<< (ok ? "OK\n": "Failed!\n");
}
// Parser factory. Should have a static get() method
// that returns something that can parse a valid T.
template<class T> struct parse_factory;
// An example of using the factory
// Specialising rule for int
template<>
struct parse_factory<int>
{
static int_type get() { return int_; }
};
// This ugly macro assures that we use the same rule for strings
//#define PARSE_STR as_string[lexeme[lit('"') >> *( ~char_("\""))>> lit('"')]| +alnum]
#define PARSE_STR (lexeme[lit('"') >> *( ~char_("\""))>> lit('"')]| +alnum)
template<>
struct parse_factory<std::string>
{
static typename boost::proto::terminal<rule<parse_iter,std::string()>>::type get()
{
rule<parse_iter,std::string()> res;
// debug(res);
res = PARSE_STR;
return res.copy();
}
};
// Testing union
// In case you wonder about naming: the real code is related to ASN.1
// parsing where "CHOICE" corresponds to a boost::variant
using my_choice = boost::variant<int,std::string>;
template<>
struct parse_factory<my_choice>
{
static boost::proto::terminal<rule<parse_iter,my_choice()>>::type get()
{
rule<parse_iter,my_choice()> r;
r = int_ | PARSE_STR;
return r.copy();
}
};
void test_choice()
{
auto choice_rule1 = int_ | PARSE_STR;
check_parser(choice_rule1,"1"); // Prints OK
check_parser(choice_rule1,"Hello1"); // Prints OK
auto choice_rule2 = (parse_factory<int>::get()
| parse_factory<std::string>::get());
check_parser(choice_rule2,"2"); // Prints OK
check_parser(choice_rule2,"Hello2"); // Prints Failed! <=========================
check_parser(parse_factory<std::string>::get(),"Hello");
auto choice_rule3 = parse_factory<my_choice>::get();
check_parser(choice_rule3,"3"); // Prints OK
check_parser(choice_rule3,"Hello3"); // Prints OK
auto choice_rule4 = int_ | parse_factory<std::string>::get();
check_parser(choice_rule4,"4"); // Prints OK
check_parser(choice_rule4,"Hello4"); // Prints Failed! <=========================
auto choice_rule5 = parse_factory<int>::get() | PARSE_STR;
check_parser(choice_rule5,"5"); // Prints OK
check_parser(choice_rule5,"Hello5"); // Prints OK
}
int main()
{
test_choice();
}
So e.g. using rule #2
(parse_factory<int>::get() | parse_factory<std::string>::get())
causes the parsing to fail. I can successfully parse an int, but not a std::string. Update: If I change "get" for int, nothing compiles (both int and std::string input fails) when I use the factory-version for int. Unless I use the my_choice factory which continues to work. Interestingly, reversing the order of the alternatives changes nothing: I can parse the int, not the string. So - any ideas if/how this problem can be solved?