Это общий вопрос в спискеSpirit General List.: Как мне разобрать и поместить результаты в структуру C++? Конечно, на данный момент вы уже знаете различные способы сделать это, используя семантические действия. Есть много способов снять кожу с кошки. Spirit2, будучи полностью приписанным, делает его еще проще. Следующий пример демонстрирует некоторые особенности Spirit2, которые делают это легко. В процессе вы узнаете о:
- Больше об атрибутах
- Правила авто
- Несколько встроенных парсеров
- Директивы
Во-первых, давайте создадим структуру, представляющую сотрудника:
struct employee
{
int age;
std::string surname;
std::string forename;
double salary;
};
Затем мы должны рассказатьBoost.Fusionо структуре нашего сотрудника, чтобы сделать его первоклассным гражданином слияния, который может использовать грамматика. Если вы еще не знаете синтеза, это библиотекаBoostдля работы с разнородными коллекциями данных, обычно называемыми кортежами. Spirit широко использует слияние как часть своей инфраструктуры.
По мнению Фьюжна, структура — это просто форма кортежа. Вы можете адаптировать любую конструкцию, чтобы полностью соответствовать термоядерному кортежу:
BOOST_FUSION_ADAPT_STRUCT(
client::employee,
(int, age)
(std::string, surname)
(std::string, forename)
(double, salary)
)
Теперь мы напишем парсер для нашего сотрудника. Входы будут иметь форму:
employee{ age, "surname", "forename", salary }
Вот так:
template <typename Iterator>
struct employee_parser : qi::grammar<Iterator, employee(), ascii::space_type>
{
employee_parser() : employee_parser::base_type(start)
{
using qi::int_;
using qi::lit;
using qi::double_;
using qi::lexeme;
using ascii::char_;
quoted_string %= lexeme['"' >> +(char_ - '"') >> '"'];
start %=
lit("employee")
>> '{'
>> int_ >> ','
>> quoted_string >> ','
>> quoted_string >> ','
>> double_
>> '}'
;
}
qi::rule<Iterator, std::string(), ascii::space_type> quoted_string;
qi::rule<Iterator, employee(), ascii::space_type> start;
};
The full cpp file for this example can be found here: ../../example/qi/employee.cpp
Давайте пройдем этот шаг за раз (не обязательно сверху вниз).
template <typename Iterator>
struct employee_parser : grammar<Iterator, employee(), space_type>
<employee_parser
>является грамматикой. Как и раньше, мы делаем его шаблоном, чтобы мы могли повторно использовать его для различных типов итераторов. Подпись грамматики:
employee()
Это означает, что парсер генерирует структуры сотрудников.<employee_parser
>пропускает белые пространства, используя<space_type
>в качестве парсера пропуска.
employee_parser() : employee_parser::base_type(start)
Инициирует базовый класс.
rule<Iterator, std::string(), space_type> quoted_string;
rule<Iterator, employee(), space_type> start;
Объявляют два правила:<quoted_string
>и<start
>.<start
>имеет те же шаблонные параметры, что и сама грамматика.<quoted_string
>имеет<std::string
>атрибут.
lexeme['"' >> +(char_ - '"') >> '"'];
<lexeme
>препятствует пропуску пространства из открытой скобы в закрывающую скобу. Выражение parses цитирует строки.
+(char_ - '"')
Разбирает один или несколько заклинаний, кроме двойной цитаты. Он останавливается, когда видит двойную цитату.
Выражение:
a - b
<a
>, но не<b
>. Его атрибут — всего лишь<A
>; атрибут<a
>.<b
>атрибут игнорируется. Следовательно, атрибут:
char_ - '"'
Это всего лишь<char
>.
+a
Он похож на звезду Клин. Вместо того, чтобы соответствовать всему,<+a
>соответствует одному или нескольким. Как и связанная с ней функция, звезда Клин, ее атрибут —<std::vector<A>
>, где<A
>— атрибут<a
>. Собрав воедино все эти атрибуты,
+(char_ - '"')
Тогда:
std::vector<char>
Что является атрибутом
'"' >> +(char_ - '"') >> '"'
?
Ну, как правило, атрибут:
a >> b >> c
Это:
fusion::vector<A, B, C>
<A
>является атрибутом<a
>,<B
>является атрибутом<b
>и<C
>является атрибутом<c
>. Что это такое<fusion::vector
>?
![[Note]](/img/note.png) | Note |
---|
Если вы не знаете, о чем я говорю, см.Фьюжн вектор. Это может быть хорошей идеей, чтобы посмотреть наBoost.Fusionна данный момент. Вы определенно увидите больше на следующих страницах. |
Некоторые парсеры, особенно те очень маленькие буквальные парсеры, которые вы видите, как<'"'
>, не имеют атрибутов.
Узлы без атрибутов игнорируются. В последовательности, как и выше, все узлы без атрибутов отфильтровываются из<fusion::vector
>. Так как<'"'
>не имеет атрибута, а<+(char_
-'"')
>имеет<std::vector<char>
>атрибут, то весь атрибут выражения должен был быть:
fusion::vector<std::vector<char> >
Но подождите, есть еще одно рушащееся правило: Если за атрибутом следует один элемент<fusion::vector
>, элемент снимается голым с его контейнера. Чтобы сделать длинный рассказ коротким, атрибут выражения:
'"' >> +(char_ - '"') >> '"'
Это:
std::vector<char>
Типично видеть такие правила, как:
r = p[_val = _1];
Если у вас есть определение правила, такое как выше, где атрибут RHS (правая сторона) правила совместим с атрибутом LHS (левая сторона руки), то вы можете переписать его как:
r %= p;
Атрибут<p
>автоматически использует атрибут<r
>.
Возвращаясь к нашему правилу<quoted_string
>:
quoted_string %= lexeme['"' >> +(char_ - '"') >> '"'];
Это упрощенная версия:
quoted_string = lexeme['"' >> +(char_ - '"') >> '"'][_val = _1];
Атрибут правила<quoted_string
>:<std::string
>совместимс атрибутом RHS:<std::vector<char>
>. RHS извлекает парсированный атрибут непосредственно в атрибут правила in-situ.
![[Note]](/img/note.png) | Note |
---|
<r%=
p >и<r
=p >эквивалентны, если нет семантических действий, связанных с<p >. |
Мы придерживаемся одного правила, правила старта:
start %=
lit("employee")
>> '{'
>> int_ >> ','
>> quoted_string >> ','
>> quoted_string >> ','
>> double_
>> '}'
;
Применяя наши разрушающиеся правила выше, RHS имеет атрибут:
fusion::vector<int, std::string, std::string, double>
Эти узлы не имеют атрибута:
- <
lit("employee")
> - <
'{'
> - <
','
> - <
'}'
>
![[Note]](/img/note.png) | Note |
---|
Если вам интересно,<lit("employee") >это то же самое, что «работник». Мы должны были завернуть его внутрь<lit >, потому что сразу после этого<>>
'{' >. Вы не можете правильно изменить a<char[] >и a<char >- вы знаете, правила синтаксиса C++. |
Напоминаем, что атрибутом<start
>является<employee
>структура:
struct employee
{
int age;
std::string surname;
std::string forename;
double salary;
};
Теперь все ясно, верно?<struct
employee
>совместим с<fusion::vector<int,std::string,std::string,double>
>. Таким образом, RHS<start
>использует атрибут старта<struct
employee
>in-situ, когда он выполняет свою работу.