Search code examples
c++boostboost-spirit-qi

Boost spirit: Parse char_ with changing local variable value


I want to implement a grammar that requires parsing instance names and paths, where a path is a list of instance names separated by a divider. The divider can be either . (period) or / (slash) given in the input file before the paths are listed, e.g.:

DIVIDER .
a.b.c
x.y.z

Once set, the divider never changes for the whole file (i.e. if set to ., encountering a path like a/b/c should not parse correctly). Since I don't know what the divider is in advance, I'm thinking about storing it in a variable of my grammar and use that value in corresponding char_ parsers (of course, the actual grammar is much more complex, but this is the part where I'm having trouble).

This is somewhat similar to this question: Boost spirit using local variables but not quite what I want, since using the Nabialek trick allows to parse "invalid" paths after the divider is set.

I'm not asking for a complete solution here, but my question is essentially this: Can I parse values into members of my grammar and then use these values for further parsing of remaining input?


Solution

  • I'd use an inherited attribute:

    qi::rule<It, std::string(char)> element = *~qi::char_(qi::_r1);
    qi::rule<It, std::vector<std::string>(char)> path = element(qi::_r1) % qi::char_(qi::_r1);
    
    // use it like:
    std::vector<std::string> data;
    bool ok = qi::parse(f, l, path('/'), data);
    

    Alternatively you /can/ indeed bind to a local variable:

    char delim = '/';
    qi::rule<It, std::string()> element = *~qi::char_(delim);
    qi::rule<It, std::vector<std::string>()> path = element % qi::char_(delim);
    
    // use it like:
    std::vector<std::string> data;
    bool ok = qi::parse(f, l, path, data);
    

    If you need it to be dynamic, use boost::phoenix::ref:

    char delim = '/';
    qi::rule<It, std::string()> element = *~qi::char_(boost::phoenix::ref(delim));
    qi::rule<It, std::vector<std::string>()> path = element % qi::char_(boost::phoenix::ref(delim));
    
    // use it like:
    std::vector<std::string> data;
    bool ok = qi::parse(f, l, path, data);