在运行时组合规则并返回规则

Combining rules at runtime and returning rules

我正在尝试编写一些基于 Spirit-X3 的复杂解析器,所以我需要了解一些事情:

♦ 如何在运行时组合规则。 (使用 Nabialek 的技巧)

♦ return 这样的规则是否可以:

x3::rule<char> SomeFunction(std::string &str)
{
    x3::rule<char> foo;
    auto bar = baz;
    BOOST_SPIRIT_DEFINE(foo, bar);
    return foo;
}

PS:SomeFunction 没有固定的 return,所以我不能只使用 x3::sequence

是的,x3 使编写规则变得容易得多。

主要是因为解析器表达式在分配给变量时不倾向于保留对临时变量的引用,就像在齐国时代那样。

局限性:在 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)

一样使用它

一些鼓舞人心的例子

  • 显示 ad-hoc 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 = {};
    }
    
  • 动态组成符号查找:

    x3::symbols<char> const keyword = []{
        x3::symbols<char> kw;
        kw += "for","begin","end","function","while","break","switch";
        return kw;
    }();
    
  • 这是一个 非常 完整的示例,包含许多解析器工厂:

    // (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-generated 语义动作编写 member-wise 传播助手的非常优雅的方式:

        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);
    

我强烈建议您 per-use 通过这些示例来了解 X3 作曲的强大程度。只有当你/真的/需要它时,我才会考虑在 X3

中重新创建类似 qi::lazy 的东西

¹ 或任何东西 Proto-based,就像 Phoenix 一样