Пример в предыдущем разделе был очень упрощенным. Он распознавал только данные, но ничего с ними не делал. Он ответил на вопрос: «Соответствовал ли входной сигнал?» Теперь мы хотим извлечь информацию из того, что было разобрано. Например, мы хотели бы сохранить парсинговый номер после успешного матча. Для этого вам потребуетсясемантических действий..
Семантические действия могут быть прикреплены к любой точке грамматической спецификации. Эти действия являются функциями C++ или функциональными объектами, которые называются всякий раз, когда часть парсера успешно распознает часть ввода. Предположим, у вас есть парсер<P
>и функция C++<F
>. Вы можете сделать парсерный вызов<F
>всякий раз, когда он соответствует входу, прикрепив<F
>:
P[F]
Выражение выше связывает<F
>с парсером<P
>.
Подпись функции/функции объекта зависит от типа парсера, к которому он прикреплен. При этом<double_
>число падает. Таким образом, если бы мы прикрепили функцию<F
>к<double_
>, мы должны были бы объявить<F
>:
void F(double n);
На самом деле, есть еще 2 аргумента (контекст парсера и ссылка на фулевый параметр «удар»). На данный момент они нам не нужны, но мы увидим больше других аргументов позже. Дух. Ци позволяет нам связать одну функцию аргумента, как выше. Остальные аргументы просто игнорируются.
Представлены различные способы прикрепления семантических действий:
- Использование функции Simple Pointer
- Использование простого функционального объекта
- Boost.Bindс простой функцией
- ИспользованиеBoost.Bindс функцией участника
- ИспользованиеBoost.Lambda
Учитывая:
namespace client
{
namespace qi = boost::spirit::qi;
void print(int const& i)
{
std::cout << i << std::endl;
}
struct writer
{
void print(int const& i) const
{
std::cout << i << std::endl;
}
};
struct print_action
{
void operator()(int const& i, qi::unused_type, qi::unused_type) const
{
std::cout << i << std::endl;
}
};
}
Обратите внимание, что с функциональными объектами нам нужно иметь<operator()
>с 3 аргументами. Поскольку мы не заботимся о двух других, мы можем использовать<unused_type
>для них. Мы увидим больше<unused_type
>в другом месте.<unused_type
>— класс поддержки, обеспеченный Духом.
Все примеры анализируют входы формы:
"{integer}"
Целое число внутри кудрявых брекетов.
Первый пример показывает, как прикрепить простую функцию:
parse(first, last, '{' >> int_[&print] >> '}');
Что нового?<int_
>является родным братом<double_
>. Уверен, вы догадаетесь, что делает этот парсер.
Следующий пример показывает, как прикрепить простой функциональный объект:
parse(first, last, '{' >> int_[print_action()] >> '}');
Мы можем использоватьBoost.Bindдля связывания функций членов:
writer w;
parse(first, last, '{' >> int_[boost::bind(&writer::print, &w, _1)] >> '}');
Также мы можем использоватьBoost.Bindдля связывания простых функций:
parse(first, last, '{' >> int_[boost::bind(&print, _1)] >> '}');
Да, мы также можем использоватьBoost.Lambda:
parse(first, last, '{' >> int_[std::cout << _1 << '\n'] >> '}');
Существует больше способов связывания семантических функций действия, но примеры выше являются наиболее распространенными. Привязка семантических действий является первым препятствием, которое нужно преодолеть, когда вы начинаете разбираться с Духом. Ознакомьтесь с этой задачей и свяжитесь с инструментами, стоящими за ней, такими какBoost.BindиBoost.Lambda.
The examples above can be found here: ../../example/qi/actions.cpp
Феникс, библиотека-компаньон в комплекте с Духом, специально подходит для связывания смысловых действий. Это какBoost.Lambdaна стероидах, с особыми пользовательскими функциями, которые позволяют легко интегрировать семантические действия с Духом. Если ваши требования выходят за рамки простого и умеренного анализа, рекомендуется использовать эту библиотеку. Все следующие примеры в этом руководстве будут использоватьФениксдля семантических действий.
![[Important]](/img/important.png) | Important |
---|
Существуют различные способы написания семантических действий дляSpirit.Qi: с использованием простых функций,Boost.Bind,Boost.LambdaилиPhoenix. Последние три позволяют использовать специальные заполнители для управления размещением параметров<_1 >,<_2 >и т.д. Каждая из этих библиотек имеет свою собственную реализацию заполнителей, все в разных пространствах имен. Вы должны убедиться, что не смешиваете заполнители с библиотекой, к которой они не принадлежат, и не используете разные библиотеки при написании семантического действия. Как правило, дляBoost.Bind, использовать<::_1 >,<::_2 >и т. Д. (Да, эти заполнители определены в глобальном пространстве имен). ДляBoost.Lambdaиспользуйте заполнители, определенные в пространстве имен<boost::lambda >. Для семантических действий, написанных с использованиемФеникса, используйте заполнители, определенные в пространстве имен<boost::spirit >. Обратите внимание, что все существующие держатели для вашего удобства также доступны из пространства имен<boost::spirit::qi >. |