Search code examples
c++parsingc++14boost-spiritboost-spirit-x3

parsing a single value into an ast node with a container


My problem is the following. I have an ast node which is defined as like the following:

struct foo_node{
    std::vector<std::string> value;
}

and I have a parser like this for parsing into the struct, which works fine:

typedef x3::rule<struct foo_node_class, foo_node> foo_node_type;
const foo_node_type foo_node = "foo_node";
auto const foo_node_def = "(" >> +x3::string("bar") >> ")";

Now I want to achieve that the parser also parses "bar", without brackets, but only if its a single bar. I tried to do it like this:

    auto const foo_node_def = x3::string("bar") 
                             | "(" > +x3::string("bar") > ")";

but this gives me a compile time error, since x3::string("bar") returns a string and not a std::vector<std::string>. My question is, how can I achieve, that the x3::string("bar") parser (and every other parser which returns a string) parses into a vector?


Solution

  • The way to parse a single element and expose it as a single-element container attribute is x3::repeat(1) [ p ]:

    Live On Coliru

    #include <boost/fusion/adapted/struct.hpp>
    #include <boost/spirit/home/x3.hpp>
    #include <iostream>
    
    namespace x3 = boost::spirit::x3;
    
    struct foo_node {
        std::vector<std::string> value;
    };
    
    BOOST_FUSION_ADAPT_STRUCT(foo_node, value)
    
    namespace rules {
        auto const bar 
            = x3::string("bar");
    
        auto const foo_node
            = '(' >> +bar >> ')'
            | x3::repeat(1) [ +bar ]
            ;
    }
    
    int main() {
        for (std::string const input : {
                "bar",
                "(bar)",
                "(barbar)",
                })
        {
            auto f = input.begin(), l = input.end();
    
            foo_node data;
            bool ok = x3::parse(f, l, rules::foo_node, data);
    
            if (ok) {
                std::cout << "Parse success: " << data.value.size() << " elements\n";
            } else {
                std::cout << "Parse failed\n";
            }
    
            if (f != l)
                std::cout << "Remaining unparsed: '" << std::string(f,l) << "'\n";
        }
    }
    

    Prints

    Parse success: 1 elements
    Parse success: 1 elements
    Parse success: 2 elements