Копировать или ссылаться на семантику boost :: spirit's rule <>? - PullRequest
11 голосов
/ 12 августа 2010

Я пытаюсь написать парсер языка оболочки в Boost.Spirit. Однако мне неясно, какие основные вопросы касаются семантики rule s.

Глядя на документацию, есть члены 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-значение типа правила работы (r.copy() также будет r-значением типа rule, не так ли)? например.

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

Может ли кто-нибудь рассказать мне о детальной семантике копий и ссылок rule и, возможно, исправить любые неправильные представления в этом посте?

1 Ответ

13 голосов
/ 13 августа 2010

Ответ зависит от того, на какую версию Духа вы ссылаетесь.


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().

...