使用 X3 的解析器依赖注入

Dependency injection of parsers using X3

有时我在解析器之间有紧密的耦合/循环依赖。我可能有这样的东西:

parser.hpp

#pragma once

namespace parser {
    using a_type = x3::rule<class a_class>;
    a_type const a = "a";

    using b_type = x3::rule<class b_class>;
    b_type const b = "b";

    auto const a_def = "(" >> b >> ")";
    auto const b_def = "<" >> a >> ">";

    BOOST_SPIRIT_DEFINE(a, b);
}

但是,我想将它们分成不同的 headers,因为这将在我进行单元测试时缩短编译时间,并且有多个 header 文件(而不是一个整体文件) .实际上,我有的不仅仅是 2 个解析器。

我希望能够做这样的事情:

a.hpp

#pragma once

#include "b.hpp"

namespace parser {
    using a_type = x3::rule<class a_class>;
    a_type const a = "a";
    auto const a_def = "(" >> b_wrapper<a>::b >> ")";

    BOOST_SPIRIT_DEFINE(a);
}

b.hpp

#pragma once

namespace parser {
    template <auto Injected>
    struct b_wrapper {
        using b_type = x3::rule<class b_class>;
        static b_type const b = "b";
        static auto const b_def = "(" >> Injected >> ")";

        BOOST_SPIRIT_DEFINE(b);
    };
}

这自然不行。如何使用解析器实现依赖注入?


我不关心注入的确切语法。但是,我真的需要这些点:

如果我每次注入不同的解析器时都必须有一堆样板,我也没关系。

您可以通过函数生成 foo_def 对象。也就是说,b.hpp 看起来像这样:

#pragma once

namespace parser {
    template <typename Injected>
    auto b_impl(Injected const& injected) {
        return "(" >> injected >> ")";
    }
}

在你想注入实际解析器的地方,你可以这样做:

using b_type = x3::rule<class b_class>;
b_type const b = "b";
auto const b_def = b_impl(a);

BOOST_SPIRIT_DEFINE(b);

请注意,当您要创建解析器时,仍然存在一些样板文件。