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


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

查看文档,有成员 r.alias() 和 r.copy() 的 rule。 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的副本和参考,并可能纠正这篇文章中的任何误解?


10973
2017-08-12 18:09


起源



答案:


答案取决于你所指的精神版本。


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

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

赋值运算符实际上引用了rhs规则,而不创建深层副本。这样做是为了允许:

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

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

因此,有成员函数 rule::copy(),允许制作规则的显式深层副本(例如将它们存储在STL容器中)。

同时这个:

r2 = r1.copy();

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


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

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

有时需要制作规则的深层副本,因此仍然存在成员功能 copy

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

r1 = r2;

现在正在创建一个(深层)副本 r2,这可能不是你所期望的,特别是如果 r2 只有在被“分配”之后才会分配它的rhs r1。因此,有新的成员函数 alias 为这个角点案例启用引用语义:

r1 = r2.alias();

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

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


13
2017-08-13 01:36



谢谢你的回答。我只是有一个跟进问题:无论如何可能在解析器表达式中使用某种类型的解析器表达式的rvalues(临时)来允许像'r1 = r1 |这样的语句。字符串(“abc”)'或在函数中生成规则? - jpalecek
而表达式'r1 = r1 | string(“abc”)'在理论上是可能的,它是一个左递归,当Spirit生成递归下降解析器时,它将导致无限递归。但表达式'r1 = string(“abc”)| r1'将按预期工作。如果确保它不引用超出范围的任何其他规则,则可以在函数中生成规则。此外,在Spirit.Classic中,您需要从函数返回r.copy()。 - hkaiser
'r1 = string(“abc”)| r1'也是左递归:)但我想做的是让r1匹配之前匹配的r1和“abc”。顺便说一下如何在函数中生成规则?这对我不起作用: pastebin.org/482764 - jpalecek
好吧,我仍然相信'r1 = string(“abc”)| r1'是正确的递归。尝试一下,它将与Spirit一起使用,而另一个版本不起作用。与您的其他问题相关,这个: pastebin.org/483007 做你想要的。您的代码版本不起作用,因为从函数返回的规则是临时的,然后通过引用保存在解析器表达式中,这是悬空的。我的版本首先存储从函数返回的规则的副本,可以通过引用保存。 - hkaiser
@hkaiser:感谢你的回答,有没有相同的 rule.alias() 功能 qi::grammar当只有一个时 qi::grammar 在规则定义的右侧?例如,应该如何完成以下工作: r1 = employee_parser.???(); - Hossein


答案:


答案取决于你所指的精神版本。


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

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

赋值运算符实际上引用了rhs规则,而不创建深层副本。这样做是为了允许:

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

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

因此,有成员函数 rule::copy(),允许制作规则的显式深层副本(例如将它们存储在STL容器中)。

同时这个:

r2 = r1.copy();

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


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

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

有时需要制作规则的深层副本,因此仍然存在成员功能 copy

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

r1 = r2;

现在正在创建一个(深层)副本 r2,这可能不是你所期望的,特别是如果 r2 只有在被“分配”之后才会分配它的rhs r1。因此,有新的成员函数 alias 为这个角点案例启用引用语义:

r1 = r2.alias();

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

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


13
2017-08-13 01:36



谢谢你的回答。我只是有一个跟进问题:无论如何可能在解析器表达式中使用某种类型的解析器表达式的rvalues(临时)来允许像'r1 = r1 |这样的语句。字符串(“abc”)'或在函数中生成规则? - jpalecek
而表达式'r1 = r1 | string(“abc”)'在理论上是可能的,它是一个左递归,当Spirit生成递归下降解析器时,它将导致无限递归。但表达式'r1 = string(“abc”)| r1'将按预期工作。如果确保它不引用超出范围的任何其他规则,则可以在函数中生成规则。此外,在Spirit.Classic中,您需要从函数返回r.copy()。 - hkaiser
'r1 = string(“abc”)| r1'也是左递归:)但我想做的是让r1匹配之前匹配的r1和“abc”。顺便说一下如何在函数中生成规则?这对我不起作用: pastebin.org/482764 - jpalecek
好吧,我仍然相信'r1 = string(“abc”)| r1'是正确的递归。尝试一下,它将与Spirit一起使用,而另一个版本不起作用。与您的其他问题相关,这个: pastebin.org/483007 做你想要的。您的代码版本不起作用,因为从函数返回的规则是临时的,然后通过引用保存在解析器表达式中,这是悬空的。我的版本首先存储从函数返回的规则的副本,可以通过引用保存。 - hkaiser
@hkaiser:感谢你的回答,有没有相同的 rule.alias() 功能 qi::grammar当只有一个时 qi::grammar 在规则定义的右侧?例如,应该如何完成以下工作: r1 = employee_parser.???(); - Hossein