c++ - 结果传递的递归 x3 解析器

标签 c++ recursion boost boost-spirit boost-spirit-x3

(1) 假设我们要解析一个被 {} 包围的简单递归 block 。

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

这个递归解析器非常简单。

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) 然后 block 变得更复杂。它也可以被 [] 包围。

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

我们需要在某个地方存储我们拥有的左括号类型。由于 x3 没有局部变量,我们可以使用属性 (x3::_val) 代替。

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) block 内容(包围部分),我们称之为argument,可能比这个例子要复杂的多。所以我们决定为它创建一个规则。此属性解决方案在这种情况下不起作用。幸运的是我们还有 x3::with 指令。我们可以将左括号(或期望的右括号)保存在堆栈引用中并将其传递到下一层。

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 解析器的方式吗?有了灵气的本地人和继承属性,解决起来似乎简单多了。谢谢。

最佳答案

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

不过,我会这样写:

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

演示

Live On 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

但是 - 代码重复!

如果你坚持概括:

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

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

关于c++ - 结果传递的递归 x3 解析器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44131508/

相关文章:

c++ - Eigen Library::如何从现有稀疏矩阵中创建 block 对角稀疏矩阵?

java - 艰巨的递归任务

c++ - 捕获 boost::interrupted_exception 是否正确?

c++ - 如何在 boost::spirit::qi 中捕获角色而不消耗它

c++ - 在 C++ 中进行 'constify' 操作有意义吗?

c++ - 在编译时使用给定函数初始化普通二维数组

c++ - 在 C++ 中返回对象的问题

Javascript i++ 递归过多,尾递归 i+1 ok

php - 使用 mysql/php 中的 id/parent_id 模型获取记录的所有父项的最简单方法是什么?

c++ - boost shared_ptr 取消引用成本