The question is easy to formulate. I have a recursive rule, I do not know the synthesized attribute type for the rule, but I also have a few more questions about the inner workings.
It looks to me that the return type is variant<tuple<return_type_of_a, SEQ>, tuple<return_type_of_b, SEQ>>
where SEQ
is a recursive rule and a
and b
are terminals:
rule<class myrule, ??> SEQ = a >> SEQ | b >> SEQ;
The following is not accepted because the rule is recursive, and I cannot figure out the return type exactly:
rule<class myrule, decltype (a>> SEQ | b >> SEQ)> seq = a >> seq | b >> seq;
rule<class myrule, ??> SEQ = (a | b) >> SEQ;
Thanks for your help.
About
seq = a >> seq | b >> seq;
First and foremost: your grammar is strictly circular and never parses: it will infinitely recurse the rule, until mismatch. I'm going to assume you wanted something like:
expr = var | "!" >> expr;
(Note how not all branches recurse unconditionally).
how to implement this recursive rule correctly, is it possible?
Yes. The tutorial samples should probably show this.
Let's say we have a very very simple grammar like
expr = var | '!' >> expr;
We create an AST to reflect that:
namespace Ast {
using var = std::string;
struct negated;
using expr = boost::variant<var, boost::recursive_wrapper<negated> >;
struct negated {
expr e;
};
}
Because the expr
rule is going to be recursive, we have to declare it before its definition:
static x3::rule<struct expr_, Ast::expr> expr {"expr"};
Let's imagine it's already defined, we would write the sub expressions like:
auto var = x3::rule<struct var_, Ast::var> {"var"}
= x3::lexeme [ x3::alpha >> *x3::alnum ];
auto neg = x3::rule<struct var_, Ast::negated> {"neg"}
= "!" >> expr;
All that's left is the recursive rule itself: BOOST_SPIRIT_DEFINE
auto expr_def = var | neg;
BOOST_SPIRIT_DEFINE(expr)
That's all.
Let's add some test cases:
#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/adapted/struct.hpp>
namespace Ast {
using var = std::string;
struct negated;
using expr = boost::variant<var, boost::recursive_wrapper<negated> >;
struct negated {
expr e;
};
static inline std::ostream& operator <<(std::ostream& os, Ast::negated const& n) {
return os << "NOT(" << n.e << ")";
}
}
BOOST_FUSION_ADAPT_STRUCT(Ast::negated, e)
namespace Parsers {
namespace x3 = boost::spirit::x3;
namespace detail {
static x3::rule<struct expr_, Ast::expr> expr {"expr"};
auto var = x3::rule<struct var_, Ast::var> {"var"}
= x3::lexeme [ x3::alpha >> *x3::alnum ];
auto neg = x3::rule<struct var_, Ast::negated> {"neg"}
= "!" >> expr;
auto expr_def = var | neg;
BOOST_SPIRIT_DEFINE(expr)
}
auto demo = x3::skip(x3::space) [ detail::expr ];
}
#include <iostream>
int main() {
for (std::string const input : { "foo", "! bar", "!!!qux" }) {
auto f = input.begin(), l = input.end();
Ast::expr e;
if (parse(f, l, Parsers::demo, e)) {
std::cout << "Parsed: " << e << "\n";
} else {
std::cout << "Parse failed\n";
}
if (f != l)
std::cout << "Remaining unparsed input: '" << std::string(f,l) << "'\n";
}
}
Prints
Parsed: foo
Parsed: NOT(bar)
Parsed: NOT(NOT(NOT(qux)))
And optionally (#define BOOST_SPIRIT_X3_DEBUG
)
<expr>
<try>foo</try>
<var>
<try>foo</try>
<success></success>
<attributes>[f, o, o]</attributes>
</var>
<success></success>
<attributes>[f, o, o]</attributes>
</expr>
Parsed: foo
<expr>
<try>! bar</try>
<var>
<try>! bar</try>
<fail/>
</var>
<neg>
<try>! bar</try>
<expr>
<try> bar</try>
<var>
<try> bar</try>
<success></success>
<attributes>[b, a, r]</attributes>
</var>
<success></success>
<attributes>[b, a, r]</attributes>
</expr>
<success></success>
<attributes>[[b, a, r]]</attributes>
</neg>
<success></success>
<attributes>[[b, a, r]]</attributes>
</expr>
Parsed: NOT(bar)
<expr>
<try>!!!qux</try>
<var>
<try>!!!qux</try>
<fail/>
</var>
<neg>
<try>!!!qux</try>
<expr>
<try>!!qux</try>
<var>
<try>!!qux</try>
<fail/>
</var>
<neg>
<try>!!qux</try>
<expr>
<try>!qux</try>
<var>
<try>!qux</try>
<fail/>
</var>
<neg>
<try>!qux</try>
<expr>
<try>qux</try>
<var>
<try>qux</try>
<success></success>
<attributes>[q, u, x]</attributes>
</var>
<success></success>
<attributes>[q, u, x]</attributes>
</expr>
<success></success>
<attributes>[[q, u, x]]</attributes>
</neg>
<success></success>
<attributes>[[q, u, x]]</attributes>
</expr>
<success></success>
<attributes>[[[q, u, x]]]</attributes>
</neg>
<success></success>
<attributes>[[[q, u, x]]]</attributes>
</expr>
<success></success>
<attributes>[[[[q, u, x]]]]</attributes>
</neg>
<success></success>
<attributes>[[[[q, u, x]]]]</attributes>
</expr>
Parsed: NOT(NOT(NOT(qux)))