jeudi 18 août 2016

spirit::qi: Combining parser_factory fails

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. 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?

Aucun commentaire:

Enregistrer un commentaire