Это общий вопрос в списке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, когда он выполняет свою работу.