是的,x3 让编写规则变得更加容易。
主要是因为解析器表达式在分配给变量时不倾向于保留对临时变量的引用,就像它们在 Qi 时代所使用的¹。
限制:使用外部链接声明解析器要复杂得多...在 X3 中复杂,需要与您显示的宏一起跳舞 BOOST_SPIRIT_{DECLARE,DEFINE}。
您的样品
这行不通,因为宏是在命名空间范围内使用的。好消息是您可能不需要它,因为除非您处理递归需要的规则,否则无需将规则与定义分开声明。
顺便说一句,x3::rule<char> 可能是个错误。 char 是该声明中的 标记类型,这不是一个好的标记类型。如果你想要一个 attribute 类型,那需要是第二个模板参数。
auto SomeFunction(std::string &str)
{
return x3::rule<struct _tag, std::string> {"dynamic"}
= '[' >> x3::lit(str) >> ']';
}
事实上,我经常在我的报关点做小工厂:
template <typename Attr>
auto compose = [](auto p1, auto p2) {
return rule<struct _, Attr> {"compose"}
= nocase [
lexeme [ "property:" << as_parser(p1) ]
>> '='
lexeme [ "value:" << as_parser(p2) ]
];
};
这有点做作,但应该会给你一些想法。像 compose<int>("number", x3::int_) 或 compose<std::string>("name", +x3::graph) 一样使用它
一些鼓舞人心的例子
-
Understanding the List Operator (%) in Boost.Spirit 显示一个临时的 as<>[] 设施:
namespace {
template <typename T>
struct as_type {
template <typename Expr>
auto operator[](Expr&& expr) const {
return x3::rule<struct _, T>{"as"} = x3::as_parser(std::forward<Expr>(expr));
}
};
template <typename T> static const as_type<T> as = {};
}
-
Avoid throwing expectation_failure when expectation parser fails 动态组成符号查找:
x3::symbols<char> const keyword = []{
x3::symbols<char> kw;
kw += "for","begin","end","function","while","break","switch";
return kw;
}();
-
Dynamically switching symbol tables in x3 这是一个具有许多解析器工厂的非常完整示例:
// (case insensitive) keyword handling
static auto kw = [](auto p) { return x3::lexeme[p >> !(x3::graph - x3::char_("/=,()"))]; };
static auto ikw = [](auto p) { return x3::no_case [kw(p)]; };
static auto qualifier = [](auto p) { return x3::lexeme['/' >> ikw(p)]; };
甚至展示了如何为您自己的类型覆盖as_spirit_parser:
// Options and CiOptions
namespace util {
template <typename Tag>
auto as_spirit_parser(Options<Tag> const& o, bool to_lower = false) {
x3::symbols<typename Options<Tag>::type> p;
int n = 0;
for (std::string el : o._options) {
if (to_lower) boost::to_lower(el);
p.add(el, n++);
}
return kw(p);
}
template <typename Tag>
auto as_spirit_parser(IcOptions<Tag> const& o) {
return x3::no_case [ as_spirit_parser(o, true) ];
}
}
使用自动生成的语义操作编写成员传播助手的非常优雅的方式:
auto set = [](auto member, auto p) {
auto propagate = [member](auto& ctx) {
traits::move_to(_attr(ctx), _val(ctx).*(member));
};
return as_parser(p)[propagate];
};
using T = ast::ShowSymbolsCommand;;
return qualifier("all") >> set(&T::all, attr(true))
| qualifier("full") >> set(&T::full, attr(true))
| qualifier("out") >> set(&T::out, '=' >> Filespec)
| qualifier("type") >> set(&T::types, '=' >> SymbolTypes)
| set(&T::wildcard, Wildcard);
我强烈建议您仔细阅读这些示例,以了解 X3 组合功能的强大程度。只有当你/真的/需要它时,我才会考虑在 X3 中重新创建类似 qi::lazy 的东西
¹ 或者实际上任何基于 Proto 的东西,比如 Phoenix