Каковы недостатки фреймворка-парсера-генератора Spirit от boost.org? - PullRequest
42 голосов
/ 11 января 2009

В нескольких вопросах я видел рекомендации для Spirit фреймворка-парсера-генератора от boost.org , но затем в комментариях есть ворчание от людей, использующих Spirit, которые счастливый. Будут ли эти люди, пожалуйста, выступать и объяснять остальным из нас, каковы недостатки или недостатки использования Духа?

Ответы [ 5 ]

37 голосов
/ 13 января 2009

Это довольно крутая идея, и мне она понравилась; особенно полезно было по-настоящему научиться использовать шаблоны C ++.

Но их документация рекомендует использовать Spirit для небольших и средних парсеров. Парсер для полного языка потребовал бы много лет для компиляции. Я перечислю три причины.

  • Разбор без сканирования. Хотя это намного проще, когда требуется возврат, он может замедлить работу синтаксического анализатора. Хотя это необязательно - лексер может быть интегрирован, см. Препроцессор C, созданный с помощью Spirit. Грамматика ~ 300 строк (включая файлы .h и .cpp) компилируется (неоптимизировано) в файл 6M с GCC. Максимальное количество вложений и максимальная оптимизация уменьшаются до ~ 1,7 млн.

  • Медленный синтаксический анализ - нет статической проверки грамматики, не требуется ни намека на чрезмерный упреждающий запрос, ни проверка основных ошибок, таких как, например, использование левой рекурсии (что приводит к бесконечной рекурсии в рекурсивном спуске). парсеры LL грамматики). Левая рекурсия - не очень сложная ошибка для отслеживания, однако, чрезмерный прогноз может привести к экспоненциальному разбору.

  • Интенсивное использование шаблона - хотя это имеет определенные преимущества, это влияет на время компиляции и размер кода. Кроме того, определение грамматики обычно должно быть видно всем остальным пользователям, что влияет на время компиляции. Я смог переместить грамматику в файлы .cpp, добавив явные экземпляры шаблона с правильными параметрами, но это было нелегко.

ОБНОВЛЕНИЕ: мой ответ ограничен моим опытом с Spirit classic, а не Spirit V2. Я все еще ожидал бы, что Spirit будет в значительной степени основан на шаблонах, но теперь я просто догадываюсь.

24 голосов
/ 20 октября 2009

В бусте 1.41 выпускается новая версия Spirit, которая выбивает брюки из духа :: classic:

После долгого времени в бета-версии (более 2 лет с Духом 2.0), Дух 2.1 наконец будет выпущен с Предстоящий релиз Boost 1.41. Код сейчас очень стабильный и готов к производственный код. Мы усердно работаем на своевременное завершение документации для повышения 1.41. Вы можете посмотреть на текущее состояние документации Вот. В настоящее время вы можете найти код и документация в Boost SVN хобот. Если у вас есть новый проект с участием Духа, мы настоятельно рекомендуем начиная с Spirit 2.1 сейчас. Разреши мне процитировать сообщение OvermindDL от Список рассылки Spirit:

Я могу начать походить на бота с как часто я говорю это, но Spirit.Classic древний, вы должны переключиться на Spirit2.1, это может сделать все, что вы сделали выше отличной сделки проще, намного меньше кода, и это выполняется быстрее. Например, Spirit2.1 может построить весь ваш AST встроенный, нет странного переопределения, нет необходимости строить вещи потом и т.д ..., все как один хороший и быстрый шаг. Вы действительно нужно обновить. См другой сообщения за прошедший день для ссылок на документы и прочее для Spirit2.1. Spirit2.1 в настоящее время в Boost Trunk, но будет быть официально выпущенным с Boost 1.41, но в остальном завершено.

19 голосов
/ 04 июня 2010

Для меня самой большой проблемой является то, что выражения в Spirit, как видно из компилятора или отладчика, довольно длинные (я скопировал ниже часть одного выражения в Spirit Classic). Эти выражения меня пугают. Когда я работаю над программой, которая использует Spirit, я боюсь использовать valgrind или печатать обратную трассировку в gdb.

boost::spirit::classic::parser_result<boost::spirit::classic::action<boost::spirit::classic::sequence<boost::spirit::classic::action<boost::spirit::classic::action<optional_suffix_parser<char const*>, boost::spirit::classic::ref_actor<std::vector<std::string, std::allocator<std::string> >, boost::spirit::classic::clear_action> >, boost::spirit::classic::ref_actor<std::vector<int, std::allocator<int> >, boost::spirit::classic::clear_action> >, boost::spirit::classic::sequence<boost::spirit::classic::alternative<boost::spirit::classic::alternative<boost::spirit::classic::action<boost::spirit::classic::contiguous<boost::spirit::classic::sequence<boost::spirit::classic::alternative<boost::spirit::classic::chlit<char>, boost::spirit::classic::chlit<char> >, boost::spirit::classic::positive<boost::spirit::classic::alternative<boost::spirit::classic::alternative<boost::spirit::classic::alnum_parser, boost::spirit::classic::chlit<char> >, boost::spirit::classic::chlit<char> > > > >, boost::spirit::classic::ref_value_actor<std::vector<std::string, std::allocator<std::string> >, boost::spirit::classic::push_back_action> >, boost::spirit::classic::action<boost::spirit::classic::rule<boost::spirit::classic::scanner<char const*, boost::spirit::classic::scanner_policies<boost::spirit::classic::skipper_iteration_policy<boost::spirit::classic::iteration_policy>, boost::spirit::classic::match_policy, boost::spirit::classic::action_policy> >, boost::spirit::classic::nil_t, boost::spirit::classic::nil_t>, boost::spirit::classic::ref_const_ref_actor<std::vector<std::string, std::allocator<std::string> >, std::string, boost::spirit::classic::push_back_action> > >, boost::spirit::classic::contiguous<boost::spirit::classic::sequence<boost::spirit::classic::chlit<char>, boost::spirit::classic::action<boost::spirit::classic::uint_parser<unsigned int, 10, 1u, -1>, boost::spirit::classic::ref_value_actor<std::vector<int, std::allocator<int> >, boost::spirit::classic::push_back_action> > > > >, boost::spirit::classic::kleene_star<boost::spirit::classic::sequence<boost::spirit::classic::chlit<char>, boost::spirit::classic::alternative<boost::spirit::classic::alternative<boost::spirit::classic::action<boost::spirit::classic::contiguous<boost::spirit::classic::sequence<boost::spirit::classic::alternative<boost::spirit::classic::chlit<char>, boost::spirit::classic::chlit<char> >, boost::spirit::classic::positive<boost::spirit::classic::alternative<boost::spirit::classic::alternative<boost::spirit::classic::alnum_parser, boost::spirit::classic::chlit<char> >, boost::spirit::classic::chlit<char> > > > >, boost::spirit::classic::ref_value_actor<std::vector<std::string, std::allocator<std::string> >, boost::spirit::classic::push_back_action> >, boost::spirit::classic::action<boost::spirit::classic::rule<boost::spirit::classic::scanner<char const*, boost::spirit::classic::scanner_policies<boost::spirit::classic::skipper_iteration_policy<boost::spirit::classic::iteration_policy>, boost::spirit::classic::match_policy, boost::spirit::classic::action_policy> >, boost::spirit::classic::nil_t, boost::spirit::classic::nil_t>, boost::spirit::classic::ref_const_ref_actor<std::vector<std::string, std::allocator<std::string> >, std::string, boost::spirit::classic::push_back_action> > >, boost::spirit::classic::contiguous<boost::spirit::classic::sequence<boost::spirit::classic::chlit<char>, boost::spirit::classic::action<boost::spirit::classic::uint_parser<unsigned int, 10, 1u, -1>, boost::spirit::classic::ref_value_actor<std::vector<int, std::allocator<int> >, boost::spirit::classic::push_back_action> > > > > > > > >, void (<em>)(char const</em>, char const*)>, boost::spirit::classic::scanner<char const*, boost::spirit::classic::scanner_policies<boost::spirit::classic::skipper_iteration_policy<boost::spirit::classic::iteration_policy>, boost::spirit::classic::match_policy, boost::spirit::classic::action_policy> > >::type boost::spirit::classic::action<boost::spirit::classic::sequence<boost::spirit::classic::action<boost::spirit::classic::action<

14 голосов
/ 30 января 2009

Вот что мне не нравится в этом:

  • документация ограничена. Есть одна большая веб-страница, где объясняется «все», но в текущих пояснениях нет подробностей.

  • плохое поколение АСТ. AST плохо объяснены, и даже после того, как вы ударите головой о стену, чтобы понять, как работают модификаторы AST, трудно получить простое манипулирование AST (то есть тот, который хорошо сопоставляется с проблемной областью)

  • Это значительно увеличивает время компиляции, даже для грамматик среднего размера

  • Синтаксис слишком тяжелый. Это жизненный факт, что в C / C ++ вы должны дублировать код (то есть между объявлением и определением). Однако, кажется, что в boost :: spirit, когда вы объявляете грамматику <>, вы должны повторить некоторые вещи 3 раза: D (когда вы хотите AST, что я и хочу: D)

Кроме этого, я думаю, что они неплохо справились с анализатором, учитывая ограничения C ++. Но я думаю, что они должны улучшить это больше. Страница истории описывает, что существовал «динамический» дух перед текущим «статичным» духом; Мне интересно, насколько быстрее и насколько лучше у него был синтаксис.

3 голосов
/ 30 января 2009

Я бы сказал, что самая большая проблема - это отсутствие какого-либо диагноза или другой помощи для грамматических проблем. Если ваша грамматика неоднозначна, синтаксический анализатор может не выполнить синтаксический анализ того, что вы ожидаете, и нет хорошего способа заметить это.

...