параметры семантического действия boost spirit


в этой статья о boost spirit semantic actions указано, что

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

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

1   51  

1 ответ:

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

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

  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, в boost 1.44 и выше установка флага матча на false приведет к тому, что матч не удастся обычным способом. Если альтернативы определены, парсер вернется и попытается сопоставить их, как и следовало ожидать. Однако в boostboost/spirit/include/phoenix.hpp и измените выражение на

qi::int_[f] | qi::digit[std::cout << qi::_1 << "\n"]

вы ожидаете что, когда парсер qi::int терпит неудачу, альтернативная цифра qi:: соответствует началу ввода на "1", но выход:

matched integer: '1234'
match flag: 1
6
return: 1

The 6 - это первая цифра второго int на входе, которая указывает, что альтернатива берется с помощью шкипера и без возврата. Обратите также внимание, что матч считается успешным, исходя из Альтернативы.

как только boost 1.44 выйдет, флаг соответствия будет полезен для применения критериев соответствия, которые могут в противном случае будет трудно выразить в последовательности парсера. Обратите внимание, что флаг соответствия можно манипулировать в выражениях phoenix с помощью _pass заполнитель.

Context параметр

более интересным параметром является второй, который содержит интерфейс qi-phoenix, или, говоря языком qi, контекст семантического действия. Чтобы проиллюстрировать это, сначала рассмотрим правило:

rule<Iterator, Attribute(Arg1,Arg2,...), qi::locals<Loc1,Loc2,...>, Skipper>

параметр контекста воплощает атрибут, 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 (a минусы). Чтобы получить доступ к этим переменным внутри функции, откройте attribute или 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;

чтобы изменить пример статьи для использования второго аргумента, измените определение функции и вызовы phrase_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;
....

это очень простой пример, который просто сопоставляет анализируемое значение с выходным атрибутом значение, но расширения должны быть довольно очевидными. Просто сделайте так, чтобы параметры шаблона структуры контекста соответствовали выходным, входным и локальным типам правил. Обратите внимание, что этот тип прямого соответствия между анализируемым типом / значением и выходным типом / значением может быть выполнен автоматически с помощью автоматических правил, с %= вместо a = при определении правила:

qi::rule<std::string::const_iterator,int(void),ascii::space_type> 
    intRule %= qi::int_;

ИМХО, написание функции для каждого действия было бы довольно утомительно, по сравнению с кратким и читаемым выражением Феникса эквиваленты. Я сочувствую точке зрения вуду, но как только вы немного поработаете с Фениксом, семантика и синтаксис не очень сложны.

Edit: доступ к контексту правила w / Phoenix

контекстная переменная определяется только тогда, когда синтаксический анализатор является частью правила. Подумайте о синтаксическом анализаторе как о любом выражении, которое потребляет входные данные, где правило преобразует значения синтаксического анализатора (qi::_1) в значение правила (qi::_val). Разница часто бывает нетривиально, например, когда qi::val имеет тип класса, который должен быть построен из анализируемых значений 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;
}

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

для вышеуказанного, если входной сигнал неверно (два int вместо трех), плохая вещь может произойти во время выполнения, поэтому было бы лучше явно указать количество анализируемых значений, поэтому синтаксический анализ не будет выполнен для плохого ввода. Ниже использует _1,_2 и _3 для ссылки на первое, второе и третье значение соответствия:

(qi::int_ >> "," >> qi::int_ >> "," >> qi::int_)
[
    qi::_val = (qi::_1 + qi::_2) * qi::_3 + qi::_r1
];

это надуманный пример, но должно дать вам идею. Я обнаружил, что семантические действия phoenix действительно полезны при построении сложных объектов непосредственно из входных данных; это возможно, потому что вы можете вызывать конструкторы и функции-члены в семантических действиях.