递归x3解析器,结果通过 [英] Recursive x3 parser with results passing around

查看:101
本文介绍了递归x3解析器,结果通过的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

(1)说我们要解析一个由{}包围的简单递归块.

(1) Say we want to parse a simple recursive block surrounded by {}.

{
    Some text.
    {
        {
            Some more text.
        }
        Some Text again.
        {}
    }
}

此递归解析器非常简单.

This recursive parser is quite simple.

x3::rule<struct idBlock1> const ruleBlock1{"Block1"};
auto const ruleBlock1_def =
    x3::lit('{') >>
    *(
        ruleBlock1 |
        (x3::char_ - x3::lit('}'))
    ) >>
    x3::lit('}');

BOOST_SPIRIT_DEFINE(ruleBlock1)

(2)然后,该块变得更加复杂.也可以用[]包围.

(2) Then the block becomes more complex. It could also be surrounded by [].

{
    Some text.
    [
        {
            Some more text.
        }
        Some Text again.
        []
    ]
}

我们需要在某个地方存储我们所拥有的那种开括号.由于x3没有本地语言,我们可以改用属性(x3::_val).

We need somewhere to store what kind of opening bracket that we have. Since x3 does not have locals, we may use attribute (x3::_val) instead.

x3::rule<struct idBlock2, char> const ruleBlock2{"Block2"};
auto const ruleBlock2_def = x3::rule<struct _, char>{} =
    (
        x3::lit('{')[([](auto& ctx){x3::_val(ctx)='}';})] |
        x3::lit('[')[([](auto& ctx){x3::_val(ctx)=']';})]
    ) >>
    *(
        ruleBlock2 |
        (
            x3::char_ - 
            (
                x3::eps[([](auto& ctx){x3::_pass(ctx)='}'==x3::_val(ctx);})] >> x3::lit('}') |
                x3::eps[([](auto& ctx){x3::_pass(ctx)=']'==x3::_val(ctx);})] >> x3::lit(']')
            )
        )
    ) >>
    (
        x3::eps[([](auto& ctx){x3::_pass(ctx)='}'==x3::_val(ctx);})] >> x3::lit('}') |
        x3::eps[([](auto& ctx){x3::_pass(ctx)=']'==x3::_val(ctx);})] >> x3::lit(']')
    );

BOOST_SPIRIT_DEFINE(ruleBlock2)

(3)我们称其为自变量的块内容(环绕的部分)可能比此示例复杂得多.因此,我们决定为其创建一个规则.在这种情况下,此属性解决方案不起作用.幸运的是,我们还有x3::with指令.我们可以将左括号(或期望右括号)保存在堆栈引用中,并将其传递到下一级.

(3) The block content (surrounded part), we call it argument, may be much more complicated than this example. So we decide to create a rule for it. This attribute solution is not working in this case. Luckily we still have x3::with directive. We can save the open bracket (or expecting close bracket) in a stack reference and pass it to the next level.

struct SBlockEndTag {};
x3::rule<struct idBlockEnd> const ruleBlockEnd{"BlockEnd"};
x3::rule<struct idArg> const ruleArg{"Arg"};
x3::rule<struct idBlock3> const ruleBlock3{"Block3"};
auto const ruleBlockEnd_def =
    x3::eps[([](auto& ctx){
        assert(!x3::get<SBlockEndTag>(ctx).get().empty());
        x3::_pass(ctx)='}'==x3::get<SBlockEndTag>(ctx).get().top();
    })] >> 
    x3::lit('}') 
    |
    x3::eps[([](auto& ctx){
        assert(!x3::get<SBlockEndTag>(ctx).get().empty());
        x3::_pass(ctx)=']'==x3::get<SBlockEndTag>(ctx).get().top();
    })] >>
    x3::lit(']');
auto const ruleArg_def =
    *(
        ruleBlock3 |
        (x3::char_ - ruleBlockEnd)
    );
auto const ruleBlock3_def =
    (
        x3::lit('{')[([](auto& ctx){x3::get<SBlockEndTag>(ctx).get().push('}');})] |
        x3::lit('[')[([](auto& ctx){x3::get<SBlockEndTag>(ctx).get().push(']');})]
    ) >>
    ruleArg >>
    ruleBlockEnd[([](auto& ctx){
        assert(!x3::get<SBlockEndTag>(ctx).get().empty());
        x3::get<SBlockEndTag>(ctx).get().pop();
    })];

BOOST_SPIRIT_DEFINE(ruleBlockEnd, ruleArg, ruleBlock3)

代码位于 Coliru 上.

问题:这是我们为此类问题编写递归x3解析器的方式吗?有了Spirit Qi的本地人和继承的属性,解决方案似乎要简单得多.谢谢.

Question: is this how we write recursive x3 parser for this kind of problem? With spirit Qi's locals and inherited attributes, the solution seems to be much simpler. Thanks.

推荐答案

您可以使用x3::with<>.

但是,我只写这个:

auto const block_def =
    '{' >> *( block  | (char_ - '}')) >> '}'
  | '[' >> *( block  | (char_ - ']')) >> ']';

演示

在Coliru上直播

#include <boost/spirit/home/x3.hpp>
#include <iostream>

namespace Parser {
    using namespace boost::spirit::x3;

    rule<struct idBlock1> const block {"Block"};
    auto const block_def =
        '{' >> *( block  | (char_ - '}')) >> '}'
      | '[' >> *( block  | (char_ - ']')) >> ']';

    BOOST_SPIRIT_DEFINE(block)
}

int main() {
    std::string const input = R"({
    Some text.
    [
        {
            Some more text.
        }
        Some Text again.
        []
    ]
})";

    std::cout << "Parsed: " << std::boolalpha << parse(input.begin(), input.end(), Parser::block) << "\n";
}

打印:

Parsed: true

但是-代码重复!

如果您坚持一概而论:

BUT - Code Duplication!

If you insist on generalizing:

auto dyna_block = [](auto open, auto close) {
    return open >> *(block | (char_ - close)) >> close;
};

auto const block_def =
    dyna_block('{', '}')
  | dyna_block('[', ']');

这篇关于递归x3解析器,结果通过的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆