в этой статье о духе повышения семантические действия это упоминается это
Существует на самом деле еще 2 передаваемые аргумента: контекст синтаксического анализатора и ссылка на булев параметр 'хита'. Контекст синтаксического анализатора значим, только если семантическое действие присоединяется где-нибудь к правой стороне правила. Мы будем видеть больше информации об этом вскоре. Булево значение может иметь значение false в семантическом действии, делает недействительным соответствие в ретроспективе, делая сбой синтаксического анализатора.
Весь штраф, но я пытался найти пример, передающий функциональный объект как семантическое действие, которое использует другие параметры (контекст синтаксического анализатора, и поразите булевскую переменную), но я не нашел никого. Я хотел бы видеть, что пример использует регулярные функции или функциональные объекты, поскольку я едва могу grok вуду феникса
Это действительно хороший вопрос (а также банка с червями), потому что он попадает на интерфейс ци и феникса. Я тоже не видел примера, поэтому немного расширю статью в этом направлении.
Как вы говорите, функции для семантических действий могут принимать до трех параметров
Флаг совпадения
Как указано в статье, второй параметр не имеет смысла, если выражение не является частью правила, поэтому давайте начнем с третьего. Тем не менее, заполнитель для второго параметра все еще необходим, и для этого используйте boost :: fusion :: unused_type
. Итак, модифицированная функция из статьи для использования третьего параметра:
#include <boost/spirit/include/qi.hpp>
#include <string>
#include <iostream>
void f(int attribute, const boost::fusion::unused_type& it, bool& mFlag){
//output parameters
std::cout << "matched integer: '" << attribute << "'" << std::endl
<< "match flag: " << mFlag << std::endl;
//fiddle with match flag
mFlag = false;
}
namespace qi = boost::spirit::qi;
int main(void){
std::string input("1234 6543");
std::string::const_iterator begin = input.begin(), end = input.end();
bool returnVal = qi::phrase_parse(begin, end, qi::int_[f], qi::space);
std::cout << "return: " << returnVal << std::endl;
return 0;
}
, которая выводит:
matched integer: '1234' match flag: 1 return: 0
Все, что делает этот пример, - это переключает совпадение на несовпадение, что отражается в выходных данных парсера. Согласно hkaiser, при повышении 1.44 и выше установка флага соответствия в значение false приведет к сбою соответствия обычным образом. Если альтернативы определены, синтаксический анализатор вернется и попытается сопоставить их, как и следовало ожидать. Однако при boost <= 1,43 ошибка Spirit предотвращает возврат, что вызывает странное поведение. Чтобы в этом убедиться, добавьте phoenix include boost / spirit / include / phoenix.hpp
и измените выражение на
qi::int_[f] | qi::digit[std::cout << qi::_1 << "\n"]
. Вы ожидаете, что при сбое парсера qi :: int альтернативный qi :: digit будет соответствовать началу ввода с "1", но на выходе будет:
matched integer: '1234' match flag: 1 6 return: 1
6
- первая цифра второго целого числа во входных данных, что указывает на то, что альтернатива взята с использованием шкипера и без возврата. Также обратите внимание, что совпадение считается успешным на основании альтернативы.
После прекращения повышения 1,44 флаг соответствия будет полезен для применения критериев соответствия, которые иначе было бы трудно выразить в последовательности синтаксического анализатора. Обратите внимание, что флаг соответствия может быть изменен в выражениях Phoenix с помощью заполнителя _pass
.
Параметр контекста
Более интересным параметром является второй, который содержит интерфейс qi-phoenix или, говоря языком qi, контекст семантического действия. Чтобы проиллюстрировать это, сначала рассмотрим правило:
rule<Iterator, Attribute(Arg1,Arg2,...), qi::locals<Loc1,Loc2,...>, Skipper>
Параметр context включает параметры шаблона Attribute, Arg1, ... ArgN и qi :: locals, заключенные в тип шаблона boost :: spirit :: context. Этот атрибут отличается от параметра функции: атрибут параметра функции - это проанализированное значение, а этот атрибут - значение самого правила. Семантическое действие должно отображать первое и второе. Вот пример возможного типа контекста (указаны эквиваленты выражения Phoenix):
using namespace boost;
spirit::context< //context template
fusion::cons<
int&, //return int attribute (phoenix: _val)
fusion::cons<
char&, //char argument1 (phoenix: _r1)
fusion::cons<
float&, //float argument2 (phoenix: _r2)
fusion::nil //end of cons list
>,
>,
>,
fusion::vector2< //locals container
char, //char local (phoenix: _a)
unsigned int //unsigned int local (phoenix: _b)
>
>
Обратите внимание, что возвращаемый атрибут и список аргументов имеют форму списка в стиле lisp (список минусов ).Чтобы получить доступ к этим переменным внутри функции, обратитесь к атрибуту
или locals
членам шаблона структуры context
с помощью fusion :: at <> (). Например, для переменной контекста con
//assign return attribute
fusion::at_c<0>(con.attributes) = 1;
//get the second rule argument
float arg2 = fusion::at_c<2>(con.attributes);
//assign the first local
fusion::at_c<1>(con.locals) = 42;
Чтобы изменить пример статьи, чтобы использовать второй аргумент, измените определение функции и вызовы фразе_parse:
...
typedef
boost::spirit::context<
boost::fusion::cons<int&, boost::fusion::nil>,
boost::fusion::vector0<>
> f_context;
void f(int attribute, const f_context& con, bool& mFlag){
std::cout << "matched integer: '" << attribute << "'" << std::endl
<< "match flag: " << mFlag << std::endl;
//assign output attribute from parsed value
boost::fusion::at_c<0>(con.attributes) = attribute;
}
...
int matchedInt;
qi::rule<std::string::const_iterator,int(void),ascii::space_type>
intRule = qi::int_[f];
qi::phrase_parse(begin, end, intRule, ascii::space, matchedInt);
std::cout << "matched: " << matchedInt << std::endl;
....
Это очень простой пример, который просто сопоставляет проанализированное значение с значение атрибута output, но расширения должны быть достаточно очевидными. Просто сделайте так, чтобы параметры шаблона структуры контекста соответствовали выходным, входным и локальным типам правил. Обратите внимание, что этот тип прямого соответствия между проанализированным типом / значением и типом / значением вывода может быть выполнен автоматически с использованием автоматических правил с % =
вместо =
при определении правила. :
qi::rule<std::string::const_iterator,int(void),ascii::space_type>
intRule %= qi::int_;
IMHO, написание функции для каждого действия было бы довольно утомительным по сравнению с краткими и удобочитаемыми эквивалентами выражений феникса. Я симпатизирую точке зрения вуду, но если вы немного поработаете с Phoenix, семантика и синтаксис не будут очень сложными.
Изменить: доступ к контексту правила с помощью Phoenix
Переменная контекста определяется только тогда, когда синтаксический анализатор является частью правила. Думайте о синтаксическом анализаторе как о любом выражении, которое потребляет ввод, где правило переводит значения синтаксического анализатора (qi :: _ 1) в значение правила (qi :: _ val). Разница часто нетривиальна, например, когда qi :: val имеет тип Class, который необходимо построить из значений, проанализированных POD. Ниже приведен простой пример.
Допустим, часть наших входных данных представляет собой последовательность из трех целых чисел CSV ( x1, x2, x3
), и мы заботимся только об арифметической функции этих трех целых чисел (f = x0 + (x1 + x2) * x3), где x0 - значение, полученное в другом месте. Один из вариантов - считать целые числа и вычислить функцию или использовать Phoenix для обоих.
В этом примере используйте одно правило с выходным атрибутом (значение функции), входом (x0) и локальным (для передачи информации между отдельными синтаксическими анализаторами с помощью правила). Вот полный пример.
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <string>
#include <iostream>
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
int main(void){
std::string input("1234, 6543, 42");
std::string::const_iterator begin = input.begin(), end = input.end();
qi::rule<
std::string::const_iterator,
int(int), //output (_val) and input (_r1)
qi::locals<int>, //local int (_a)
ascii::space_type
>
intRule =
qi::int_[qi::_a = qi::_1] //local = x1
>> ","
>> qi::int_[qi::_a += qi::_1] //local = x1 + x2
>> ","
>> qi::int_
[
qi::_val = qi::_a*qi::_1 + qi::_r1 //output = local*x3 + x0
];
int ruleValue, x0 = 10;
qi::phrase_parse(begin, end, intRule(x0), ascii::space, ruleValue);
std::cout << "rule value: " << ruleValue << std::endl;
return 0;
}
В качестве альтернативы все целые числа могут быть проанализированы как вектор, а функция оценена с помощью одного семантического действия (%
ниже - это оператор списка, а доступ к элементам вектора осуществляется с помощью phoenix :: at):
namespace ph = boost::phoenix;
...
qi::rule<
std::string::const_iterator,
int(int),
ascii::space_type
>
intRule =
(qi::int_ % ",")
[
qi::_val = (ph::at(qi::_1,0) + ph::at(qi::_1,1))
* ph::at(qi::_1,2) + qi::_r1
];
....
Для приведенного выше, если ввод неверен (два целых вместо трех), может произойти что-то плохое. происходят во время выполнения, поэтому было бы лучше явно указать количество анализируемых значений, чтобы синтаксический анализ не удался из-за неправильного ввода. В приведенном ниже примере используются _1
, _2
и _3
для ссылки на первое, второе и третье значения соответствия:
(qi::int_ >> "," >> qi::int_ >> "," >> qi::int_)
[
qi::_val = (qi::_1 + qi::_2) * qi::_3 + qi::_r1
];
Это надуманный пример, но он должен дать вам идею. Я обнаружил, что семантические действия Phoenix действительно полезны при создании сложных объектов непосредственно из ввода; это возможно, потому что вы можете вызывать конструкторы и функции-члены в семантических действиях.