дух повышения семантические параметры действия

в этой статье о духе повышения семантические действия это упоминается это

Существует на самом деле еще 2 передаваемые аргумента: контекст синтаксического анализатора и ссылка на булев параметр 'хита'. Контекст синтаксического анализатора значим, только если семантическое действие присоединяется где-нибудь к правой стороне правила. Мы будем видеть больше информации об этом вскоре. Булево значение может иметь значение false в семантическом действии, делает недействительным соответствие в ретроспективе, делая сбой синтаксического анализатора.

Весь штраф, но я пытался найти пример, передающий функциональный объект как семантическое действие, которое использует другие параметры (контекст синтаксического анализатора, и поразите булевскую переменную), но я не нашел никого. Я хотел бы видеть, что пример использует регулярные функции или функциональные объекты, поскольку я едва могу grok вуду феникса

52
задан hkaiser 18 June 2010 в 10:54
поделиться

1 ответ

Это действительно хороший вопрос (а также банка с червями), потому что он попадает на интерфейс ци и феникса. Я тоже не видел примера, поэтому немного расширю статью в этом направлении.

Как вы говорите, функции для семантических действий могут принимать до трех параметров

  1. Соответствующий атрибут - описан в статье
  2. Контекст - содержит интерфейс qi-phoenix
  3. Флаг совпадения - управлять состоянием совпадения

Флаг совпадения

Как указано в статье, второй параметр не имеет смысла, если выражение не является частью правила, поэтому давайте начнем с третьего. Тем не менее, заполнитель для второго параметра все еще необходим, и для этого используйте 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 действительно полезны при создании сложных объектов непосредственно из ввода; это возможно, потому что вы можете вызывать конструкторы и функции-члены в семантических действиях.

66
ответ дан 7 November 2019 в 09:31
поделиться
Другие вопросы по тегам:

Похожие вопросы: