这个问题很容易制定。我有一个递归规则,我不知道规则的合成属性类型,但我还有一些关于内部工作的问题。
在我看来,返回类型为variant<tuple<return_type_of_a, SEQ>, tuple<return_type_of_b, SEQ>>
,其中SEQ
是递归规则,a
和b
是终端:
rule<class myrule, ??> SEQ = a >> SEQ | b >> SEQ;
以下是不被接受的,因为规则是递归的,我无法确切地找出返回类型:
rule<class myrule, decltype (a>> SEQ | b >> SEQ)> seq = a >> seq | b >> seq;
rule<class myrule, ??> SEQ = (a | b) >> SEQ;
感谢您的帮助。
答案 0 :(得分:1)
关于
seq = a >> seq | b >> seq;
首先:你的语法是严格循环的,永远不会解析:它将无限地递归规则,直到不匹配。我假设你想要类似的东西:
expr = var | "!" >> expr;
(注意并非所有分支都无条件递归)。
如何正确实现此递归规则,是否可能?
是。教程样本应该显示出来。
假设我们有一个非常简单的语法,如
expr = var | '!' >> expr;
我们创建一个AST来反映:
namespace Ast {
using var = std::string;
struct negated;
using expr = boost::variant<var, boost::recursive_wrapper<negated> >;
struct negated {
expr e;
};
}
因为expr
规则将是递归的,所以我们必须在它的定义之前声明它:
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;
剩下的就是递归规则本身:BOOST_SPIRIT_DEFINE
auto expr_def = var | neg;
BOOST_SPIRIT_DEFINE(expr)
就是这样。
让我们添加一些测试用例:
<强> Live On Coliru 强>
#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";
}
}
打印
Parsed: foo
Parsed: NOT(bar)
Parsed: NOT(NOT(NOT(qux)))
可选(#define BOOST_SPIRIT_X3_DEBUG
)
<强> Live On Coliru 强>
<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)))