Подтвердить что ты не робот

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

в этой статье посвященной синтаксическим смысловым воздействиям, упоминается, что

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

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

4b9b3361

Ответ 1

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

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

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

Флажок соответствия

Как говорится в статье, второй параметр не имеет смысла, если выражение не является частью правила, поэтому давайте начнем с третьего. Заполнитель для второго параметра все еще необходим, и для этого используйте 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 и up, если флаг соответствия false, приведет к тому, что совпадение завершится неудачно. Если альтернативы определены, синтаксический анализатор отступит и попытается сопоставить их, как можно было бы ожидать. Однако в boost <= 1.43 ошибка Spirit предотвращает обратное отслеживание, что вызывает странное поведение. Чтобы увидеть это, добавьте phoenix include boost/spirit/include/phoenix.hpp и измените выражение на

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

Вы ожидаете, что когда qi:: int parser завершится с ошибкой, альтернативный qi:: digit будет соответствовать началу ввода на "1", но на выходе будет:

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

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

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

Контекстный параметр

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

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

Параметр контекста воплощает параметры шаблона Attribute, Arg1,... ArgN и qi:: locals, завернутые в тип шаблона boost:: spirit:: context. Этот атрибут отличается от параметра функции: атрибут параметра функции - это разобранное значение, а этот атрибут - значение самого правила. Семантическое действие должно сопоставить первое с последним. Здесь приведен пример возможного типа контекста (эквивалент выражения феникса):

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)
    > 
>

Обратите внимание, что атрибут return и список аргументов принимают форму списка lisp -style (

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

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

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

Изменить: доступ к контексту правила w/Phoenix

Контекстная переменная определяется только тогда, когда парсер является частью правила. Подумайте о синтаксическом анализаторе как о любом выражении, которое потребляет вход, где правило переводит значения парсера (qi:: _ 1) в значение правила (qi:: _ val). Разница часто нетривиальна, например, когда qi:: val имеет тип класса, который должен быть сконструирован из проанализированных значений POD. Ниже приведен простой пример.

Скажем, часть нашего ввода представляет собой последовательность из трех целых чисел CSV (x1, x2, x3), и мы заботимся только об арифметической функции этих трех целых чисел (f = x0 + (x1 + x2) * x3), где x0 это значение, полученное в другом месте. Один из вариантов состоит в том, чтобы читать целые числа и вычислять функцию, или, альтернативно, использовать феникс для выполнения обоих.

В этом примере используйте одно правило с выходным атрибутом (значением функции) и вводом (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;
}

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