【问题标题】:Copy or reference semantics of boost::spirit's rule<>?复制或引用 boost::spirit 规则<>的语义?
【发布时间】:2011-03-29 02:13:35
【问题描述】:

我正在尝试在 Boost.Spirit 中编写一个 shell 语言解析器。但是,我不清楚rules 的一些基本语义问题。

查看文档,有rule的成员r.alias()r.copy()。 IIUC,这些成员应分别返回对规则的引用和规则内容的副本。但是,当我只在另一个规则的定义中使用该规则时,并没有明确说明会发生什么。从我的实验中,我发现相互递归的规则可以定义为:

rule<Iter> r1, r2;
r1 = ... >> r2 >> ...;
r2 = ... >> r1 >> ...;

这表明规则是在解析器表达式中通过引用来获取的。问题是,当变量超出范围时它会做什么,例如:

rule<Iter> r1;
{ 
  rule<Iter> r2;
  r1 = ... >> r2 >> ...;
  r2 = ... >> r1 >> ...;
}
... // use r1

同样,从包含规则类型右值的解析表达式中分配规则是否可行(r.copy() 也将是 rule 类型的右值,不是吗)?例如。

rule<Iter> f() { return char_('a') << char_('b'); }
rule<Iter> r1 = ... << f();

谁能告诉我rule的副本和引用的详细语义,并可能纠正这篇文章中的任何误解?

【问题讨论】:

    标签: c++ boost-spirit


    【解决方案1】:

    答案取决于您所指的 Spirit 版本。


    Spirit.Classic(以前的 Spirit V1.x)为规则实现了特殊的复制语义。文档说:

    当规则在任何地方被引用时 EBNF 的右侧 表达式,规则由 通过引用表达。它是 客户有责任确保 引用的规则保留在 范围并且不会被破坏 当它被引用时。

    赋值运算符本质上引用了 rhs 规则,但也没有创建深层副本。这样做是为了允许:

    rule<> r1, r2;
    r1 = ...;
    r2 = r1;
    

    但事实证明这是高度混乱的,因为它阻止了以与“普通”对象相同的方式处理规则。

    出于这个原因,有成员函数rule::copy(),允许对规则进行显式的深拷贝(例如将它们存储在 STL 容器中)。

    同时这个:

    r2 = r1.copy();
    

    完全错误。 r2 将引用从函数copy() 返回的r1 的(已破坏的)临时副本。


    在 Spirit.Qi(即 Spirit V2.x)中,行为发生了部分变化。在解析器之外处理规则时,现在的行为符合预期。您可以将它们正常存储在容器中(赋值运算符公开了预期的行为)。但请注意,解析器表达式中的规则仍然通过引用来保存,这仍然允许以与以前相同的方式引用规则:

    rule<> r1, r2;
    r1 = ... >> r2 >> ...;
    r2 = ... >> r1 >> ...;
    

    有时需要对规则进行深拷贝,所以仍然有成员函数copy

    更改后的复制语义还有另一个副作用。构造如下:

    r1 = r2;
    

    现在正在创建r2 的(深层)副本,这可能不是您所期望的,尤其是如果r2 仅在被“分配”到r1 后才分配其rhs。出于这个原因,有新的成员函数alias 为这种极端情况启用了引用语义:

    r1 = r2.alias();
    

    在任何情况下,如果从解析器表达式引用的部分规则超出范围,那么在 Spirit 的两个版本中,您最终都会得到悬空引用。

    顺便说一句,Spirit 版本都没有实现函数rule::ref()

    【讨论】:

    • 感谢您的回答。我只是有一个后续问题:是否有可能在解析器表达式中使用某种类型的解析器表达式的右值(临时)来允许像'r1 = r1 |这样的语句? string("abc")' 还是在函数中生成规则?
    • 而表达式 'r1 = r1 | string("abc")' 理论上可能是左递归,这将导致无限递归,因为 Spirit 生成递归下降解析器。但是表达式 'r1 = string("abc") | r1' 将按预期工作。如果您确保它不引用超出范围的任何其他规则,则可以在函数中生成规则。此外,在 Spirit.Classic 中,您需要从函数中返回 r.copy()。
    • 'r1 = string("abc") | r1' 也是左递归 :) 但我想做的是让 r1 匹配 r1 之前匹配的内容和“abc”。顺便说一句,我如何在函数中生成规则?这对我不起作用:pastebin.org/482764
    • 好吧,我还是相信 'r1 = string("abc") | r1' 是右递归。试试吧,它可以与 Spirit 一起使用,而其他版本则无法使用。与您的其他问题相关,这:pastebin.org/483007 可以满足您的需求。您的代码版本不起作用,因为从函数返回的规则是一个临时的,然后由解析器表达式中的引用保存,它是悬空的。我的版本首先存储了从函数返回的规则的副本,可以通过引用来保存。
    • @hkaiser:感谢您的回答,当规则定义的右侧只有一个qi::grammar 时,是否有等效于qi::grammars 的rule.alias() 函数?例如应该如何做:r1 = employee_parser.???();
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-08
    • 1970-01-01
    • 2021-07-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多