代码之家  ›  专栏  ›  技术社区  ›  bartop

Boost spirit x3-惰性解析器

  •  0
  • bartop  · 技术社区  · 4 年前

    做最新的 boost::spirit::x3 实施 lazy 分析器?我在里面找到的 documentation 但找不到 source code on github boost::spirit::x3::lazy . 是我遗漏了什么还是懒惰的语法分析器从 spirit 或改名或其他?

    0 回复  |  直到 4 年前
        1
  •  3
  •   sehe    4 年前

    我想我会在这里试试我的手。

    qi::rule 在过去。

    为了完整起见,我们实际上还可以删除或转换上下文(例如,在惰性规则中传播skipper),但我选择这里是为了简单起见。

    在我们的用例中,让我们分析这些输入:

    integer_value: 42
    quoted_string: "hello world"
    bool_value: true
    double_value: 3.1415926
    

    我们将使用variant属性类型,并从创建 lazy_rule 允许我们删除类型的解析器:

    using Value = boost::variant<int, bool, double, std::string>;
    using It    = std::string::const_iterator;
    using Rule  = x3::any_parser<It, Value>;
    

    把懒惰的话题传来传去

    现在,我们从哪里“得到”懒惰的对象?

    在灵气方面,我们有 Nabialek Trick qi::locals<> inherited attributes ,这基本上都归结为使用菲尼克斯懒惰的演员( qi::_r1 qi::_a 等)估价 从分析器上下文 在运行时。

    在X3中没有Phoenix,我们必须自己使用语义操作来操作上下文。

    基本的构造块是 x3::with<T>[] directive

    x3::symbols<Rule> options;
    

    现在我们可以在选项中添加任何解析表达式,例如。 options.add("anything", x3::eps); .

    auto const parser = x3::with<Rule>(Rule{}) [
        set_context<Rule>[options] >> ':' >> lazy<Rule>
    ];
    

    Rule 上下文的值,可以设置( set_context )和“执行”( lazy ).

    template <typename Tag>
    struct set_context_type {
        template <typename P>
        auto operator[](P p) const {
            auto action = [](auto& ctx) {
                x3::get<Tag>(ctx) = x3::_attr(ctx);
            };
            return x3::omit [ p [ action ] ];
        }
    };
    
    template <typename Tag>
    struct lazy_type : x3::parser<lazy_type<Tag>> {
        using attribute_type = typename Tag::attribute_type; // TODO FIXME?
    
        template<typename It, typename Ctx, typename RCtx, typename Attr>
        bool parse(It& first, It last, Ctx& ctx, RCtx& rctx, Attr& attr) const {
            auto& subject = x3::get<Tag>(ctx);
    
            return x3::as_parser(subject)
                .parse(
                    first, last, 
                    std::forward<Ctx>(ctx),
                    std::forward<RCtx>(rctx),
                    attr);
        }
    };
    
    template <typename T> static const set_context_type<T> set_context{};
    template <typename T> static const lazy_type<T> lazy{};
    

    这就是它的全部。

    演示时间

    在这个演示中,我们运行上面的输入(在函数中 run_tests() )它将使用如下所示的解析器:

    auto run_tests = [=] {
        for (std::string const& input : {
                "integer_value: 42",
                "quoted_string: \"hello world\"",
                "bool_value: true",
                "double_value: 3.1415926",
            })
        {
            Value attr;
            std::cout << std::setw(36) << std::quoted(input);
            if (phrase_parse(begin(input), end(input), parser, x3::space, attr)) {
                std::cout << " -> success (" << attr << ")\n";
            } else {
                std::cout << " -> failed\n";
            }
        }
    };
    

    options.add("integer_value", x3::int_);
    options.add("quoted_string", as<std::string> [
            // lexeme is actually redundant because we don't use surrounding skipper yet
            x3::lexeme [ '"' >> *('\\' >> x3::char_ | ~x3::char_('"')) >> '"' ]
        ]);
    run_tests();
    

    将打印:

    "integer_value: 42"                  -> success (42)
    "quoted_string: \"hello world\""     -> success (hello world)
    "bool_value: true"                   -> failed
    "double_value: 3.1415926"            -> failed
    

    现在,我们可以通过扩展 options :

    options.add("double_value", x3::double_);
    options.add("bool_value", x3::bool_);
    
    run_tests();
    

    输出变成:

    "integer_value: 42"                  -> success (42)
    "quoted_string: \"hello world\""     -> success (hello world)
    "bool_value: true"                   -> success (true)
    "double_value: 3.1415926"            -> success (3.14159)
    

    注意,我又找了一个帮手 as<> 这使得将属性类型强制为 std::string ideas in earlier answers

    Coliru上的实时完整列表

    看看吧 Live On Coliru

    #include <boost/spirit/home/x3.hpp>
    #include <iostream>
    #include <iomanip>
    
    namespace x3 = boost::spirit::x3;
    
    namespace {
        template <typename T>
        struct as_type {
            template <typename...> struct Tag{};
    
            template <typename P>
            auto operator[](P p) const {
                return x3::rule<Tag<T, P>, T> {"as"} = x3::as_parser(p);
            }
        };
    
        template <typename Tag>
        struct set_context_type {
            template <typename P>
            auto operator[](P p) const {
                auto action = [](auto& ctx) {
                    x3::get<Tag>(ctx) = x3::_attr(ctx);
                };
                return x3::omit [ p [ action ] ];
            }
        };
    
        template <typename Tag>
        struct lazy_type : x3::parser<lazy_type<Tag>> {
            using attribute_type = typename Tag::attribute_type; // TODO FIXME?
    
            template<typename It, typename Ctx, typename RCtx, typename Attr>
            bool parse(It& first, It last, Ctx& ctx, RCtx& rctx, Attr& attr) const {
                auto& subject = x3::get<Tag>(ctx);
    
                return x3::as_parser(subject)
                    .parse(
                        first, last, 
                        std::forward<Ctx>(ctx),
                        std::forward<RCtx>(rctx),
                        attr);
            }
        };
    
        template <typename T> static const as_type<T>          as{};
        template <typename T> static const set_context_type<T> set_context{};
        template <typename T> static const lazy_type<T>        lazy{};
    }
    
    int main() {
        std::cout << std::boolalpha << std::left;
    
        using Value = boost::variant<int, bool, double, std::string>;
        using It    = std::string::const_iterator;
        using Rule  = x3::any_parser<It, Value>;
    
        x3::symbols<Rule> options;
    
        auto const parser = x3::with<Rule>(Rule{}) [
            set_context<Rule>[options] >> ':' >> lazy<Rule>
        ];
    
        auto run_tests = [=] {
            for (std::string const& input : {
                    "integer_value: 42",
                    "quoted_string: \"hello world\"",
                    "bool_value: true",
                    "double_value: 3.1415926",
                })
            {
                Value attr;
                std::cout << std::setw(36) << std::quoted(input);
                if (phrase_parse(begin(input), end(input), parser, x3::space, attr)) {
                    std::cout << " -> success (" << attr << ")\n";
                } else {
                    std::cout << " -> failed\n";
                }
            }
        };
    
    
        std::cout << "Supporting only integer_value and quoted_string:\n";
        options.add("integer_value", x3::int_);
        options.add("quoted_string", as<std::string> [
                // lexeme is actually redundant because we don't use surrounding skipper yet
                x3::lexeme [ '"' >> *('\\' >> x3::char_ | ~x3::char_('"')) >> '"' ]
            ]);
        run_tests();
    
        std::cout << "\nAdded support for double_value and bool_value:\n";
        options.add("double_value", x3::double_);
        options.add("bool_value", x3::bool_);
    
        run_tests();
    }
    

    Supporting only integer_value and quoted_string:
    "integer_value: 42"                  -> success (42)
    "quoted_string: \"hello world\""     -> success (hello world)
    "bool_value: true"                   -> failed
    "double_value: 3.1415926"            -> failed
    
    Added support for double_value and bool_value:
    "integer_value: 42"                  -> success (42)
    "quoted_string: \"hello world\""     -> success (hello world)
    "bool_value: true"                   -> success (true)
    "double_value: 3.1415926"            -> success (3.14159)
    

    可悲的是文件在行动中丢失了

    推荐文章