Boost.Spirit.Qi: взять атрибут правила и установить его в качестве поля атрибута структуры включающего правила? - PullRequest
7 голосов
/ 07 января 2011

Как и во многих других вопросах, я пытаюсь разобрать простую грамматику в дерево структур, используя Boost.Spirit.Qi.

Я попытаюсь отфильтровать то, что пытаюсьсделать в простейшем случае.У меня есть:

struct Integer {
  int value;
};
BOOST_FUSION_ADAPT_STRUCT(Integer, (int, value))

Позже, внутри грамматической структуры, у меня есть следующая переменная-член:

qi::rule<Iterator, Integer> integer;

, которую я определяю с помощью

integer = qi::int_;

Однако когда я пытаюсь разобрать целое число, использование

qi::phrase_parse(iter, end, g, space, myInteger);

myInteger.value всегда неинициализируется после успешного анализа.Точно так же я попробовал следующие определения (очевидно, что те, которые не компилируются, неправильны):

integer = qi::int_[qi::_val = qi::_1]; //compiles, uninitialized value
integer = qi::int_[qi::_r1 = qi::_1]; //doesn't compile
integer = qi::int_[phoenix::bind(&Integer::value, qi::_val) = qi::_1]; //doesn't
integer = qi::int_[phoenix::at_c<0>(qi::_val) = qi::_1]; //doesn't

Очевидно, что я неправильно понимаю что-то о Духе, Фениксе или что-то еще.Насколько я понимаю, qi::_1 является первым атрибутом qi::int_ здесь и должен представлять проанализированное целое число, когда часть в квадратных скобках выполняется как функциональный объект.Затем я предполагаю, что объект функции возьмет включающий атрибут integer qi::_val и попытается присвоить ему проанализированное целое число.Я предположил, что из-за моего BOOST_FUSION_ADAPT_STRUCT вызова они будут совместимы, и это, безусловно, имеет место с точки зрения статического анализа, но данные не сохраняются.

Есть ли ссылка(&) обозначение, я где-то пропустил или что-то?

1 Ответ

13 голосов
/ 07 января 2011

Если предполагается, что Integer является атрибутом, предоставляемым правилом, его необходимо объявить как:

qi::rule<Iterator, Integer()> integer; 

(обратите внимание на круглые скобки).Spirit требует использовать синтаксис объявления функции для описания «интерфейса» правила.Он используется не только в Spirit, но и несколькими другими библиотеками (см. Например, boost :: function).

Основная причина этого заключается в том, что это хороший лаконичный способ указания интерфейса функции.Если вы думаете о том, что такое правило, вы быстро понимаете, что оно похоже на функцию: оно может возвращать значение (анализируемый результат, то есть синтезированный атрибут).Кроме того, может потребоваться один или несколько аргументов (унаследованных атрибутов).

Вторая, но незначительная причина заключается в том, что Spirit должен уметь различать различные параметры шаблона правила.Параметры шаблона могут быть заданы в любом порядке (кроме итератора), поэтому для его определения нужны какие-то средства.Синтаксис объявления функции достаточно отличается от шкипера или кодировки (два других возможных параметра шаблона), чтобы его можно было распознать во время компиляции.

Давайте посмотрим на ваши различные попытки:

Это можно заставить работать, если вы измените определение правила, как описано выше.

integer = qi::int_[qi::_val = qi::_1]; 

_val относится к вашему Integer, а _1 относится к int.Поэтому вам нужно определить оператор присваивания из int, чтобы сделать эту работу:

struct Integer {
    int value;
    Integer& operator=(int) {...}
};                    

В этом случае вам не нужно адаптировать ваш тип как последовательность Fusion.

Но вы можете написать это еще проще:

integer = qi::int_ >> qi::eps;

, что эквивалентно 100% (eps - это уловка, используемая для преобразования правой части в последовательность синтаксического анализатора, которая позволяет использовать встроенное распространение атрибутовотображение элементов вашей адаптированной последовательности Fusion на атрибуты элементов последовательности).

Это:

integer = qi::int_[qi::_r1 = qi::_1]; 

не будет работать, так как _r1 ссылается на первый унаследованный атрибутправило.Однако ваше правило не имеет наследуемых атрибутов.

Это будет работать:

integer = qi::int_[phoenix::bind(&Integer::value, qi::_val) = qi::_1];

, но не требует адаптации вашего типа в качестве последовательности Fusion.

И так будетэто:

integer = qi::int_[phoenix::at_c<0>(qi::_val) = qi::_1]; 
...