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]
, но это кажется немного неуклюжим - а также делает вещи медленнее в соответствии с документами повышения.
Итак, мои вопросы
- Стоит ли предотвращать обратное отслеживание?
- Зачем мне нужен оператор группировки
()
- Действительно ли мой последний пример выше останавливает отслеживание
operationRule
совпадает (я подозреваю, что нет, кажется, что если весь парсер внутри(...)
не удастся вернуться назад будет разрешено)? - Если ответом на предыдущий вопрос является / нет /, как мне создать правило, которое позволяет вернуться назад, если
operation
is / not / matched, но не позволяет вернуться после операции / is / matched? - Почему оператор группировки уничтожает грамматику атрибута - требующие действий?
Я понимаю, что это довольно широкий вопрос - любые советы, которые указывают в правильном направлении, будут высоко оценены!
1 ответ
Стоит ли предотвращать обратное отслеживание?
Абсолютно. Предотвращение обратного отслеживания в целом является проверенным способом повышения производительности анализатора.
- сократить использование (отрицательного) прогнозирования (оператор!, оператор - и некоторые оператор &)
- Порядок ветвей (оператор |, оператор ||, оператор ^ и некоторый оператор */-/+) таков, что наиболее частая / вероятная ветвь упорядочена первой, или что самая дорогая ветвь пробуется последней
Использование точек ожидания (
>
) существенно не уменьшает возврат: он просто запрещает это. Это включит целевые сообщения об ошибках, предотвратит бесполезный "анализ в неизвестность".Зачем мне группировка
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;
Полный код ниже показывает, как я бы предпочел сделать это, используя адаптированные структуры.
Действительно ли мой приведенный выше пример действительно останавливает обратное отслеживание после сопоставления operationRule (я подозреваю, что нет, кажется, что, если весь парсер внутри (...) завершится неудачно, будет разрешено возвращение)?
Абсолютно. Если ожидания не оправдались,
qi::expectation_failure<>
исключение брошено. Это по умолчанию прерывает анализ. Вы можете использовать qi::on_error дляretry
,fail
,accept
или жеrethrow
, Пример MiniXML имеет очень хорошие примеры использования точек ожидания сqi::on_error
Если ответом на предыдущий вопрос является / нет /,
как мне создать правило, которое позволяет возвращать данные, если операция не соответствует / не соответствует, но не разрешает возвращение после операции / соответствует / соответствует?Почему оператор группировки уничтожает грамматику атрибута - требующие действий?
Он не разрушает грамматику атрибута, он просто меняет открытый тип. Таким образом, если вы связываете соответствующую ссылку на атрибут с правилом / грамматикой, вам не понадобятся семантические действия. Теперь я чувствую, что должны быть способы обойтись без группировки
, поэтому позвольте мне попробовать это (предпочтительно на вашем коротком автономном образце).И действительно, я не нашел такой необходимости. Я добавил полный пример, чтобы помочь вам увидеть, что происходит в моем тестировании, а не использовать семантические действия.
Полный код
В полном коде показано 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