Каковы недостатки фреймворка Spirit parser-generator от boost.org?
-
08-07-2019 - |
Вопрос
В нескольких вопросах я видел рекомендации по Дух фреймворк генератора парсеров из boost.org, но затем в комментариях раздается ворчание от людей, использующих Spirit, которые недовольны.Не могли бы эти люди, пожалуйста, выступить вперед и объяснить остальным из нас, каковы недостатки использования Spirit?
Решение
Это довольно классная идея, и она мне понравилась;было особенно полезно по-настоящему научиться использовать шаблоны C ++.
Но их документация рекомендует использовать spirit для анализаторов малого и среднего размера.Компиляция парсера для полноценного языка заняла бы целую вечность.Я перечислю три причины.
Анализ без сканирования.Хотя это довольно просто, когда требуется обратный поиск, это может замедлить работу анализатора.Однако это необязательно - может быть интегрирован лексер, см. препроцессор C, созданный с помощью Spirit.Грамматика из ~ 300 строк (включая файлы .h и .cpp) компилируется (неоптимизировано) в файл размером 6 миллионов с GCC.Встраивание и максимальная оптимизация сокращают это значение до ~ 1,7M.
Медленный синтаксический анализ - нет статической проверки грамматики, ни для того, чтобы намекнуть на необходимость чрезмерного поиска, ни для проверки основных ошибок, таких как, например, использование левой рекурсии (что приводит к бесконечной рекурсии в парсерах с рекурсивным спуском и грамматиках).Однако левую рекурсию не так уж сложно отследить, но чрезмерный просмотр может привести к экспоненциальному увеличению времени синтаксического анализа.
Интенсивное использование шаблонов - хотя это имеет определенные преимущества, это влияет на время компиляции и размер кода.Кроме того, определение грамматики обычно должно быть видно всем другим пользователям, что еще больше увеличивает время компиляции.Я смог переместить грамматики в файлы .cpp, добавив явные экземпляры шаблонов с нужными параметрами, но это было непросто.
Обновить:мой ответ ограничен моим опытом работы с Spirit classic, а не с Spirit V2.Я бы по-прежнему ожидал, что Spirit будет в значительной степени основан на шаблонах, но сейчас я просто предполагаю.
Другие советы
В бусте 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, но в остальном завершено.
Для меня самая большая проблема заключается в том, что выражения в Spirit, видимые компилятором или отладчиком, довольно длинные (я скопировал ниже часть одного выражения в духе Классики).Эти выражения пугают меня.Когда я работаю над программой, использующей 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 ()(символ const, 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<
Вот что мне в этом не нравится:
объем документации ограничен.Существует одна большая веб-страница, где объясняется "все", но в текущих объяснениях не хватает подробностей.
бедное поколение AST.AST плохо объяснены, и даже после того, как вы ударились головой о стену, чтобы понять, как работают модификаторы AST, трудно получить простой в манипулировании AST (т. е.тот, который хорошо соответствует проблемной области)
Это значительно увеличивает время компиляции даже для грамматик "среднего" размера
Синтаксис слишком тяжеловесный.Это факт жизни, что в C / C ++ вы должны дублировать код (т.е.между декларацией и определением).Однако, похоже, что в boost::spirit, когда вы объявляете грамматику<>, вы должны повторить некоторые вещи 3 раза : D (когда вы хотите ASTS, чего и я хочу: D)
Помимо этого, я думаю, что они проделали довольно хорошую работу с синтаксическим анализатором, учитывая ограничения C ++.Но я думаю, что они должны улучшить его еще больше.Страница истории описывает, что до нынешнего "статического" духа существовал "динамический" дух;Мне интересно, насколько быстрее и насколько лучше у него был синтаксис.
Я бы сказал, что самая большая проблема - это отсутствие какого-либо диагноза или другой помощи для грамматических проблем. Если ваша грамматика неоднозначна, синтаксический анализатор может не выполнить синтаксический анализ того, что вы ожидаете, и нет хорошего способа заметить это.