复制或引用 boost::spirit 规则的语义?

发布于 2024-09-14 02:40:32 字数 853 浏览 1 评论 0原文

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

查看文档,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();

有人可以告诉我规则副本和引用的详细语义,并可能纠正本文中的任何误解吗?

I am trying to write a shell language parser in Boost.Spirit. However, I am unclear about some basic issues regarding semantics of rules.

Looking at the documentation, there are members r.alias() and r.copy() of rule. IIUC, these members should return a reference to the rule and a copy of the rule's contents, respectively. However, it is not clearly specified what happens when I just use the rule in a definition of another rule. From my experiments, I found mutually recursive rules can be defined by:

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

which suggests the rules are taken by reference inside parser expressions. The problem is, what does it do when the variable goes out of scope, eg:

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

On the same note, would assigning to a rule from a parsing expression containing a rvalue of type rule work (r.copy() would be a rvalue of type rule too, isn't it)? eg.

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

Can anybody enlighten me on the detailed semantics of rule's copies and references, and possibly correct any misconceptions in this post?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

素罗衫 2024-09-21 02:40:32

答案取决于您所指的 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 >> ...;

有时需要对规则进行深层复制,因此仍然有成员函数 复制。

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

r1 = r2;

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

r1 = r2.alias();

无论如何,在 Spirit 的两个版本中,如果从解析器引用了部分规则,最终都会得到悬空引用表达式超出范围。

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

The answer depends on what version of Spirit you're referring to.


Spirit.Classic (the former Spirit V1.x) implements special copy semantics for rules. The documentation says:

When a rule is referenced anywhere in
the right hand side of an EBNF
expression, the rule is held by the
expression by reference. It is the
responsibility of the client to ensure
that the referenced rule stays in
scope and does not get destructed
while it is being referenced.

The assignment operator essentially references the rhs rule without creating a deep copy as well. This was done to allow:

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

But this turned out to be highly confusion as it prevented handling rules the same way as 'normal' objects.

For that reason there was the member function rule::copy(), allowing to make explicit deep copies of a rule (for instance to store them in an STL container).

At the same time this:

r2 = r1.copy();

is plain wrong. r2 would refer to the (destructed) temporary copy of r1 returned from the function copy().


In Spirit.Qi (i.e. Spirit V2.x) the behaviour is partially changed. rules are now behaving as expected when handled outside of parsers. You can store them normally in containers (the assignment operator exposes the expected behavior). But beware, that inside a parser expression rules are still held by reference, which still allows to refer to a rule the same way as before:

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

Sometimes it's necessary to make a deep copy of a rule, so there is still the member functon copy.

The changed copy semantics have another side effect. Constructs like:

r1 = r2;

are now creating a (deep) copy of r2, which might not be what you expect, especially if r2 will get its rhs assigned only after being 'assigned' to r1. For that reason there is the new member function alias enabling reference semantics for this corner case:

r1 = r2.alias();

In any case, in both versions of Spirit you will end up with dangling references if part of the rules referenced from a parser expression go out of scope.

BTW, neither Spirit version implements a function rule::ref().

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文