c++ - Spirit X3,指的是之前匹配的值

标签 c++ boost boost-spirit-x3

我正在 Spirit X3 中编写一个解析器,以便熟悉它,尽管我非常熟悉 Qi,但在 X3 中我仍然遇到了一些障碍。

例如,Qi 示例包括一个基本的 XML 解析器,您可以通过该解析器了解如何使用 Phoenix 占位符来匹配先前匹配的值。然而,我只能在 X3 中弄清楚这一点:

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

namespace x3 = boost::spirit::x3;

namespace mytest
{

struct SimpleElement
{
    std::string tag;
    std::string content;
};

} // namespace bbspirit

BOOST_FUSION_ADAPT_STRUCT
(
    mytest::SimpleElement, tag, content
)

namespace mytest
{

namespace x3 = boost::spirit::x3;
namespace ascii = boost::spirit::x3::ascii;

using x3::lit;
using x3::lexeme;
using ascii::char_;

const x3::rule<class SimpleElementID, SimpleElement> simpleTag = "simpleTag";

auto assignTag = [](auto& ctx)
{
    x3::_val(ctx).tag = x3::_attr(ctx);
};

auto testTag = [](auto& ctx)
{
    x3::_pass(ctx) = 
        (x3::_val(ctx).tag == x3::_attr(ctx));
};

auto assignContent = [](auto& ctx)
{
    x3::_val(ctx).content = x3::_attr(ctx);
};

auto const simpleTag_def
    = '['
    >> x3::lexeme[+(char_ - ']')][assignTag]
    >> ']'
    >> x3::lexeme[
        +(char_ - x3::lit("[/"))]
            [assignContent]
    >> "[/"
    >> x3::lexeme[+(char_ - ']')][testTag]
    >> ']'
    ;

BOOST_SPIRIT_DEFINE(simpleTag);

} // namespace bbspirit


int main() 
{

const std::string text = "[test]Hello World![/test]";
std::string::const_iterator start = std::begin(text);
const std::string::const_iterator stop = std::end(text);

mytest::SimpleElement element{};

bool result = 
    phrase_parse(start, stop, mytest::simpleTag, x3::ascii::space, element);

if (!result)
{
    std::cout << "failed to parse!\n";
}
else
{
    std::cout << "tag    : " << element.tag << '\n';
    std::cout << "content: " << element.content << '\n';
}

}

(链接:https://wandbox.org/permlink/xLZN9plcOwkSKCrD)

这是可行的,但是如果我尝试解析类似 [test]Hello [/World[/test] 的内容,它就不起作用,因为我没有在此处指定正确的省略:

    >> x3::lexeme[
        +(char_ - x3::lit("[/"))]
            [assignContent]

本质上我想告诉解析器类似的事情:

    >> x3::lexeme[
        +(char_ - (x3::lit("[/")  << *the start tag* << ']') )]
            [assignContent]

我怎样才能做到这一点?另外,我引用开始标记并随后将其匹配的方式是在 X3 中执行此操作的“最佳”方式还是有更好/更优选的方式?

谢谢!

最佳答案

好问题。

最好的答案就是按照 XML 的方式去做:outlaw [/在标签数据内。事实上,XML 是非法的 < (因为它可能打开一个嵌套标签,并且您不希望必须提前读取整个流来查找它是否是有效的子标签)。

XML uses character entities ("escapes" like &lt; and &gt;) or unparsed character data (CDATA[]) to encode contents that requires these characters.

接下来,您当然可以使用 !closeTag 进行否定前瞻断言( -closeTagtag )属性成员就像你已经做的那样。

重新调整拼写规则,这还不算太糟糕

Note I removed the need for manual propagation of the tag/contents using the , true> template argument on simpleTag rule. See Boost Spirit: "Semantic actions are evil"?

const x3::rule<class SimpleElementID, SimpleElement, true> simpleTag = "simpleTag";
auto testTag = [](auto& ctx) { _pass(ctx) = (_val(ctx).tag == _attr(ctx)); };

auto openTag     = '[' >> x3::lexeme[+(char_ - ']')] >> ']';
auto closeTag    = "[/" >> x3::lexeme[+(char_ - ']')] [testTag] >> ']';
auto tagContents = x3::lexeme[ +(char_ - closeTag) ];

auto const simpleTag_def
    =  openTag
    >> tagContents
    >> x3::omit [ closeTag ]
    ;

查看 Live On Coliru

背景

这可行,但最终变得非常笨拙,因为这意味着到处使用语义操作,并且也违背了属性引用的自然绑定(bind)。

跳出框框思考:

在 Qi 中你可以使用 qi::locals inherited attributes为此(请参阅文档中非常相似的示例: MiniXML )。

这两者都会产生用您的信息扩展解析器上下文的最终效果。

X3没有这样的“高级”功能。但它确实具有扩展您的上下文的构建 block :x3::witt<>(data) [ p ] .

x3::with

在这个简单的示例中,这似乎有点矫枉过正,但在某些时候,您会欣赏如何在规则中使用额外的上下文,而不会将属性类型作为人质:

struct TagName{};
auto openTag
    = x3::rule<struct openTagID, std::string, true> {"openTag"}
    = ('[' >> x3::lexeme[+(char_ - ']')] >> ']')
        [([](auto& ctx) { x3::get<TagName>(ctx) = _attr(ctx); })]
    ;
auto closeTag
    = x3::rule<struct closeTagID, std::string, true> {"closeTag"}
    = ("[/" >> x3::lexeme[+(char_ - ']')] >> ']')
        [([](auto& ctx) { _pass(ctx) = (x3::get<TagName>(ctx) == _attr(ctx)); })]
    ;
auto tagContents
    = x3::rule<struct openTagID, std::string> {"tagContents"}
    = x3::lexeme[ +(char_ - closeTag) ];

auto const simpleTag
    = x3::rule<class SimpleElementID, SimpleElement, true> {"simpleTag"}
    = x3::with<TagName>(std::string()) [
        openTag
        >> tagContents
        >> x3::omit [ closeTag ]
    ];

查看 Live On Coliru


关于c++ - Spirit X3,指的是之前匹配的值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62143841/

相关文章:

c++ - 如何在GPU内核中使用Eigen稀疏矩阵

c++ - 使用 boost::karma 格式化纬度/经度字符串

c++ - 为什么这个 boost 示例在 OS X 上没有链接?

c++ - Spirit X3,如何在非 ascii 输入上解析失败?

c++ - spirit X3 : Custom number parser yield unexpected leading zero in the result

C++ 虚类函数返回具体子类的实例

c++ - std::string 和 UTF-8 编码的 unicode

c++ - boost::noncopyable 的 unordered_map 无法从 operator[] 返回引用

c++ - 使用 vector<char> 作为缓冲区而不在 resize() 上初始化它

c++ - 使用 boost spirit x3 解析逗号分隔的 0 个或多个列表