Saving Boost Spirit recursive match results to a C++ struct

118 Views Asked by At

I succeeded to parse strings like "A, (B, C), (D, E, (F, G)), H".

But, I failed to save the matched results to a C++ struct.

I couldn't figure out the rule's right attribute types.

Following is a minimum test case for the parsing.

TEST_CASE("recursive match", "[qi]")
{
    namespace qi = boost::spirit::qi;
    using qi::char_;

    struct recursive_match
        : qi::grammar<std::string::iterator, qi::ascii::space_type>
    {
        recursive_match() : recursive_match::base_type(div_)
        {
            subdiv_ = '(' >> div_ >> ')';
            div_ = (char_("A-Z") | subdiv_) % ',';
        }

        qi::rule<std::string::iterator, qi::ascii::space_type> subdiv_;
        qi::rule<std::string::iterator, qi::ascii::space_type> div_;
    };

    std::string s = "A, (B, C), (D, E, (F, G)), H";
    auto begin = s.begin();
    auto end = s.end();
    recursive_match rule_;
    bool r = qi::phrase_parse(begin, end, rule_, qi::ascii::space);
    REQUIRE(r);
    REQUIRE(begin == end);
}

Anything will be helpful for me. Thanks.

1

There are 1 best solutions below

1
On BEST ANSWER

I would suggest a recursive variant:

using node = boost::make_recursive_variant<
        char,
        std::vector<boost::recursive_variant_>
    >::type;

using nodes = std::vector<node>;

Now, you'd declare the rules slightly more simply:

qi::rule<It, Ast::node(),  qi::ascii::space_type> node_;
qi::rule<It, Ast::nodes(), qi::ascii::space_type> list_;

Where the definition is something like

node_ = char_("A-Z") | '(' >> list_ >> ')';
list_ = node_ % ',';

Full Demo

Adding some code for debug output:

Live On Coliru

//#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/io.hpp>
namespace qi = boost::spirit::qi;

namespace std {
    // for debug output
    template <typename T>
    inline static std::ostream& operator<<(std::ostream& os, std::vector<T> const& v) {
        os << "(";
        bool first = true;
        for (auto& el : v) {
            (first?os:os << ", ") << el;
            first = false;
        }
        return os << ")";
    }
}

namespace Ast {
    using node = boost::make_recursive_variant<
            char,
            std::vector<boost::recursive_variant_>
        >::type;

    using nodes = std::vector<node>;
}

template <typename It = std::string::const_iterator>
struct recursive_match : qi::grammar<It, Ast::nodes(), qi::ascii::space_type> {
    recursive_match() : recursive_match::base_type(list_) {
        using namespace qi;

        node_ = char_("A-Z") | '(' >> list_ >> ')';
        list_ = node_ % ',';

        BOOST_SPIRIT_DEBUG_NODES((node_)(list_))
    }

  private:
    qi::rule<It, Ast::node(),  qi::ascii::space_type> node_;
    qi::rule<It, Ast::nodes(), qi::ascii::space_type> list_;
};

int main() {
    using qi::char_;

    std::string const s = "A, (B, C), (D, E, (F, G)), H";
    auto begin = s.begin();
    auto end = s.end();
    recursive_match<> rule_;
    Ast::nodes parsed;
    bool ok = qi::phrase_parse(begin, end, rule_, qi::ascii::space, parsed);

    if (ok)
        std::cout << "Parsed: " << parsed << "\n";
    else
        std::cout << "Parse failed\n";

    if (begin != end)
        std::cout << "Remaining unparsed input: '" << std::string(begin, end) << "'\n";
}

Prints:

Parsed: (A, (B, C), (D, E, (F, G)), H)