1

I am trying to declare a recursive AST for a spirit x3 parser. The parser grammar is working, and since it is recommended to avoid semantic actions I am trying to adapt the Rexpr official documentation example.

In the main documentation, the parsed structure can be represented by a map where keys are strings and values are either a string or another map.

In my case, the structure being parsed is a simple n-ary tree, and I hoped I could get my way out with a recursive struct and forward_ast:

#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>

#include <boost/fusion/adapted/struct/adapt_struct.hpp>
#include <boost/fusion/include/adapt_struct.hpp>

namespace ast
{
  struct node
  {
    std::string name;
    double length;
    std::vector<boost::spirit::x3::forward_ast<node>> children;
  };
}  
BOOST_FUSION_ADAPT_STRUCT(ast::node, name, length, children)

But I did not succeed having this compile - boost fusion tells me that

error: explicit specialization of undeclared template struct 'tag_of'
BOOST_FUSION_ADAPT_STRUCT(ast::node, name, length, children)

So I guess I failed to understand either:

  • the logic of recursive ast,
  • the syntax to use to tell boost fusion about this nested type?
  • or both?
sehe
  • 374,641
  • 47
  • 450
  • 633
WaterFox
  • 850
  • 6
  • 18

1 Answers1

1

I'm not sure I follow the problem, but perhaps this helps:

Live On Compiler Explorer

#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>

#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/io.hpp>
#include <iostream>

namespace ast {
    using boost::spirit::x3::forward_ast;
    struct node {
        std::string                    name;
        double                         length;
        std::vector<forward_ast<node>> children;
    };

} // namespace ast

BOOST_FUSION_ADAPT_STRUCT(ast::node, name, length, children)

namespace ast {
    using boost::fusion::operator<<;
    static inline std::ostream& operator<<(std::ostream& os, std::vector<forward_ast<node>> const& nn) {
        os << "[";
        auto sep = "";
        for (auto& n : nn)
            os << std::exchange(sep, ", ") << n.get();
        return os << "]";
    }
} // namespace ast

int main() {
    ast::node n{
        "demo",
        42,
        {
            ast::node{"nested1", 43, {}},
            ast::node{"nested4",
                      47,
                      {
                          ast::node{"nested2", 45, {}},
                          ast::node{"nested3", 46, {}},
                      }},
            ast::node{"nested5", 48, {}},
        },
    };

    std::cout << "No problem: " << n << "\n";
}

Prints

No problem: (demo 42 [(nested1 43 []), (nested4 47 [(nested2 45 []), (nested3 46 [])]), (nested5 48 [])])
sehe
  • 374,641
  • 47
  • 450
  • 633
  • 1
    Note that from c++17 you can do without the `forward_ast`: https://godbolt.org/z/87MEWPEfr and otherwise, Boost Container supports incomplete types. – sehe Nov 21 '22 at 01:44
  • Thank you, yes it helped a lot knowing the syntax I used was correct, so I found the error: a curly bracket went missing :/ Since I now know this ast is compile-wise correct, it also helps me figuring out the next step, that is how to feed it to the grammar, as`x3::rule const tree{"tree"};`does not seem to do the trick! Baby steps forward lol ! – WaterFox Nov 21 '22 at 03:07