boost::spirit::qi Ожидаемый синтаксический анализатор и группировка синтаксического анализатора

Я надеюсь, что кто-то может пролить свет на мое невежество использования > а также >> операторы в разборе духа.

У меня есть рабочая грамматика, где правило верхнего уровня выглядит

test = identifier >> operationRule >> repeat(1,3)[any_string] >> arrow >> any_string >> conditionRule;

Он опирается на атрибуты для автоматически распределенных анализируемых значений в адаптированной структуре (то есть, буст-кортеж).

Тем не менее, я знаю, что, как только мы сопоставим операцию с правами, мы должны продолжить или потерпеть неудачу (то есть мы не хотим, чтобы возвратный путь пробовал другие правила, начинающиеся с identifier).

test = identifier >> 
           operationRule > repeat(1,3)[any_string] > arrow > any_string > conditionRule;

Это вызывает загадочную ошибку компилятора ('boost::Container' : use of class template requires template argument list). Futz вокруг немного и следующие компиляции:

test = identifier >> 
           (operationRule > repeat(1,3)[any_string] > arrow > any_string > conditionRule);

но настройка атрибута больше не работает - моя структура данных содержит мусор после анализа. Это можно исправить, добавив такие действия, как [at_c<0>(_val) = _1], но это кажется немного неуклюжим - а также делает вещи медленнее в соответствии с документами повышения.

Итак, мои вопросы

  1. Стоит ли предотвращать обратное отслеживание?
  2. Зачем мне нужен оператор группировки ()
  3. Действительно ли мой последний пример выше останавливает отслеживание operationRule совпадает (я подозреваю, что нет, кажется, что если весь парсер внутри (...) не удастся вернуться назад будет разрешено)?
  4. Если ответом на предыдущий вопрос является / нет /, как мне создать правило, которое позволяет вернуться назад, если operation is / not / matched, но не позволяет вернуться после операции / is / matched?
  5. Почему оператор группировки уничтожает грамматику атрибута - требующие действий?

Я понимаю, что это довольно широкий вопрос - любые советы, которые указывают в правильном направлении, будут высоко оценены!

1 ответ

Решение
  1. Стоит ли предотвращать обратное отслеживание?

    Абсолютно. Предотвращение обратного отслеживания в целом является проверенным способом повышения производительности анализатора.

    • сократить использование (отрицательного) прогнозирования (оператор!, оператор - и некоторые оператор &)
    • Порядок ветвей (оператор |, оператор ||, оператор ^ и некоторый оператор */-/+) таков, что наиболее частая / вероятная ветвь упорядочена первой, или что самая дорогая ветвь пробуется последней

    Использование точек ожидания (>) существенно не уменьшает возврат: он просто запрещает это. Это включит целевые сообщения об ошибках, предотвратит бесполезный "анализ в неизвестность".

  2. Зачем мне группировка operator ()

    Я не уверен. У меня был чек с использованием моего what_is_the_attr помощники отсюда

    • ident >> op >> repeat(1,3)[any] >> "->" >> any
      синтезирует атрибут:

      fusion::vector4<string, string, vector<string>, string>
      
    • ident >> op > repeat(1,3)[any] > "->" > any
      синтезирует атрибут:

      fusion::vector3<fusion::vector2<string, string>, vector<string>, string>
      

    Я не нашел необходимости группировать подвыражения, используя скобки (вещи компилируются), но очевидно DataT необходимо изменить, чтобы соответствовать измененному макету.

    typedef boost::tuple<
        boost::tuple<std::string, std::string>, 
        std::vector<std::string>, 
        std::string
    > DataT;
    

Полный код ниже показывает, как я бы предпочел сделать это, используя адаптированные структуры.

  1. Действительно ли мой приведенный выше пример действительно останавливает обратное отслеживание после сопоставления operationRule (я подозреваю, что нет, кажется, что, если весь парсер внутри (...) завершится неудачно, будет разрешено возвращение)?

    Абсолютно. Если ожидания не оправдались, qi::expectation_failure<> исключение брошено. Это по умолчанию прерывает анализ. Вы можете использовать qi::on_error для retry, fail, accept или же rethrow, Пример MiniXML имеет очень хорошие примеры использования точек ожидания с qi::on_error

  2. Если ответом на предыдущий вопрос является / нет /, как мне создать правило, которое позволяет возвращать данные, если операция не соответствует / не соответствует, но не разрешает возвращение после операции / соответствует / соответствует?

  3. Почему оператор группировки уничтожает грамматику атрибута - требующие действий?

    Он не разрушает грамматику атрибута, он просто меняет открытый тип. Таким образом, если вы связываете соответствующую ссылку на атрибут с правилом / грамматикой, вам не понадобятся семантические действия. Теперь я чувствую, что должны быть способы обойтись без группировки , поэтому позвольте мне попробовать это (предпочтительно на вашем коротком автономном образце). И действительно, я не нашел такой необходимости. Я добавил полный пример, чтобы помочь вам увидеть, что происходит в моем тестировании, а не использовать семантические действия.

Полный код

В полном коде показано 5 сценариев:

  • ВАРИАНТ 1: Оригинал без ожиданий

    (без соответствующих изменений)

  • ВАРИАНТ 2: с ожиданиями

    Использование модифицированного typedef для DataT (как показано выше)

  • ВАРИАНТ 3: адаптированная структура, без ожиданий

    Использование пользовательской структуры с BOOST_FUSION_ADAPT_STRUCT

  • ВАРИАНТ 4: адаптированная структура с ожиданиями

    Изменение адаптированной структуры из ВАРИАНТА 3

  • ВАРИАНТ 5: предвкушение взлома

    Этот использует "умный" (?) Взлом, делая все >> в ожидании, и обнаружение присутствия operationRule заранее. Это, конечно, неоптимально, но позволяет сохранить DataT без изменений и без использования семантических действий.

Очевидно, определить OPTION до желаемого значения перед компиляцией.

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/fusion/adapted.hpp>
#include <iostream>

namespace qi    = boost::spirit::qi; 
namespace karma = boost::spirit::karma; 

#ifndef OPTION
#define OPTION 5
#endif

#if OPTION == 1 || OPTION == 5 // original without expectations (OR lookahead hack)
    typedef boost::tuple<std::string, std::string, std::vector<std::string>, std::string> DataT;
#elif OPTION == 2 // with expectations
    typedef boost::tuple<boost::tuple<std::string, std::string>, std::vector<std::string>, std::string> DataT;
#elif OPTION == 3 // adapted struct, without expectations
    struct DataT
    {
        std::string identifier, operation;
        std::vector<std::string> values;
        std::string destination;
    };

    BOOST_FUSION_ADAPT_STRUCT(DataT, (std::string, identifier)(std::string, operation)(std::vector<std::string>, values)(std::string, destination));
#elif OPTION == 4 // adapted struct, with expectations
    struct IdOpT
    {
        std::string identifier, operation;
    };
    struct DataT
    {
        IdOpT idop;
        std::vector<std::string> values;
        std::string destination;
    };

    BOOST_FUSION_ADAPT_STRUCT(IdOpT, (std::string, identifier)(std::string, operation));
    BOOST_FUSION_ADAPT_STRUCT(DataT, (IdOpT, idop)(std::vector<std::string>, values)(std::string, destination));
#endif

template <typename Iterator>
struct test_parser : qi::grammar<Iterator, DataT(), qi::space_type, qi::locals<char> >
{
    test_parser() : test_parser::base_type(test, "test")
    {
        using namespace qi;

        quoted_string = 
               omit    [ char_("'\"") [_a =_1] ]             
            >> no_skip [ *(char_ - char_(_a))  ]
             > lit(_a); 

        any_string = quoted_string | +qi::alnum;

        identifier = lexeme [ alnum >> *graph ];

        operationRule = string("add") | "sub";
        arrow = "->";

#if OPTION == 1 || OPTION == 3   // without expectations
        test = identifier >> operationRule >> repeat(1,3)[any_string] >> arrow >> any_string;
#elif OPTION == 2 || OPTION == 4 // with expectations
        test = identifier >> operationRule  > repeat(1,3)[any_string]  > arrow  > any_string;
#elif OPTION == 5                // lookahead hack
        test = &(identifier >> operationRule) > identifier > operationRule > repeat(1,3)[any_string] > arrow > any_string;
#endif
    }

    qi::rule<Iterator, qi::space_type/*, qi::locals<char> */> arrow;
    qi::rule<Iterator, std::string(), qi::space_type/*, qi::locals<char> */> operationRule;
    qi::rule<Iterator, std::string(), qi::space_type/*, qi::locals<char> */> identifier;
    qi::rule<Iterator, std::string(), qi::space_type, qi::locals<char> > quoted_string, any_string;
    qi::rule<Iterator, DataT(),       qi::space_type, qi::locals<char> > test;
};

int main()
{
    std::string str("addx001 add 'str1'   \"str2\"       ->  \"str3\"");
    test_parser<std::string::const_iterator> grammar;
    std::string::const_iterator iter = str.begin();
    std::string::const_iterator end  = str.end();

    DataT data;
    bool r = phrase_parse(iter, end, grammar, qi::space, data);

    if (r)
    {
        using namespace karma;
        std::cout << "OPTION " << OPTION << ": " << str << " --> ";
#if OPTION == 1 || OPTION == 3 || OPTION == 5 // without expectations (OR lookahead hack)
        std::cout << format(delimit[auto_ << auto_ << '[' << auto_ << ']' << " --> " << auto_], data) << "\n";
#elif OPTION == 2 || OPTION == 4 // with expectations
        std::cout << format(delimit[auto_ << '[' << auto_ << ']' << " --> " << auto_], data) << "\n";
#endif
    }
    if (iter!=end)
        std::cout << "Remaining: " << std::string(iter,end) << "\n";
}

Выход для всех опций:

for a in 1 2 3 4 5; do g++ -DOPTION=$a -I ~/custom/boost/ test.cpp -o test$a && ./test$a; done
OPTION 1: addx001 add 'str1'   "str2"       ->  "str3" --> addx001 add [ str1 str2 ]  -->  str3 
OPTION 2: addx001 add 'str1'   "str2"       ->  "str3" --> addx001 add [ str1 str2 ]  -->  str3 
OPTION 3: addx001 add 'str1'   "str2"       ->  "str3" --> addx001 add [ str1 str2 ]  -->  str3 
OPTION 4: addx001 add 'str1'   "str2"       ->  "str3" --> addx001 add [ str1 str2 ]  -->  str3 
OPTION 5: addx001 add 'str1'   "str2"       ->  "str3" --> addx001 add [ str1 str2 ]  -->  str3 
Другие вопросы по тегам