11

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

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

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's 副本和引用的详细语义,并可能纠正这篇文章中的任何误解吗?

4

1 回答 1

15

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


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

当在 EBNF 表达式右侧的任何位置引用规则时,该规则通过引用由表达式持有。客户有责任确保被引用的规则保持在范围内并且在被引用时不会被破坏。

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

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

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

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

同时这:

r2 = r1.copy();

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


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

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

有时需要对规则进行深拷贝,因此仍然存在成员 functon copy

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

r1 = r2;

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

r1 = r2.alias();

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

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

于 2010-08-13T01:36:22.550 回答