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

时间:2018-04-08 20:28:53

标签: c++ c++14 boost-spirit boost-spirit-x3

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

♦如何在运行时组合规则。 (与Nabialek的伎俩)

♦可以返回这样的规则:

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

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

1 个答案:

答案 0 :(得分:2)

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

主要是因为解析器表达式在分配给变量时不具有保留对临时变量的引用的倾向,就像它们在Qi¹时代一样。

限制:声明具有外部链接的解析器更多...在X3中更复杂,需要与您显示的宏BOOST_SPIRIT_{DECLARE,DEFINE}共舞。

您的样本

这不会飞,因为宏意味着在命名空间范围内使用。好消息是您可能不需要它,因为除非您正在处理递归所需的规则,否则无需将该规则与定义分开声明。

撇开x3::rule<char>可能是个错误。 char是该声明中的标记类型,并且这不是一个好的标记类型。如果您想要一个属性类型,那么它需要是第二个模板参数。

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的东西,如凤凰太太