В чем недостатки фреймворка парсера-генератора Spirit от boost.org?

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

хорошая обработка текста в ускоренном видео от Эрика Ниблера: video.google.de/videoplay?docid=3723782552647089226. Слайды здесь: nwcpp.org/Downloads/2007/Text_Processing_With_Boost.ppt. Повеселись!

Johannes Schaub - litb 11.01.2009 21:12

Я думаю, что рабочий видеоролик здесь youtube.com/watch?v=6eVtvQ3IEfc / cc @ JohannesSchaub-litb

sehe 30.06.2016 14:18
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
42
2
9 423
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Ответ принят как подходящий

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

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

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

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

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

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

+1 за время компиляции и раздувание кода ... Однажды я использовал дух для небольшого проекта (парсинг конфигурационных файлов) и позже решил удалить его именно по этим причинам.

Nils Pipenbrinck 20.10.2009 05:04

Время компиляции настоящего полезного грамматика, скажем, более десятка правил, ужасно велико.

ravenspoint 13.09.2011 02:13

@Blaisorblade - Этот ответ относится к "Духовной классике" или к "Духу V2" ?? (см. другой ответ: stackoverflow.com/questions/432173/…)

Martin Ba 07.11.2011 15:41

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

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

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

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

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

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

Не уверен, но я не думаю, что динамичный дух мог бы быть намного лучше нынешнего. Это могло быть намного медленнее из-за дополнительных виртуальных вызовов, которые у него были; IIRC, в настоящее время существуют виртуальные вызовы только при входе в парсер правила <...>, в динамическом духе каждый вызов между составным синтаксическим анализатором и одним из его компонентов был бы виртуальным.

Blaisorblade 20.12.2009 19:15

Забыл согласиться с вашими комментариями по документации и генерации AST. Когда я использовал Spirit, некоторые детали не были определены в документации, и код, казалось, давал непоследовательные и ошибочные результаты по ним, возможно, потому, что разработчики не указали их.

Blaisorblade 20.12.2009 19:18

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

Кроме того, у вас может быть экспоненциальный возврат из-за чрезмерного упреждения и возникнут проблемы с его отладкой. Однажды я быстро собрал грамматику, которая не могла закончить разбор (в течение нескольких минут) 1 выражение из ~ 100 токенов. Однако (я считаю) это не относится к Spirit, а относится ко всем текущим библиотекам комбинаторов синтаксического анализатора (доступным на большинстве функциональных языков, таких как Haskell или Scala), которые позволяют определять грамматику на вашем языке программирования, а не использовать отдельный инструмент.

Blaisorblade 24.03.2011 00:55

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

After a long time in beta (more than 2 years with Spirit 2.0), Spirit 2.1 will finally be released with the upcoming Boost 1.41 release. The code is very stable now and is ready for production code. We are working hard on finishing the documentation in time for Boost 1.41. You can peek at the current state of the documentation here. Currently, you can find the code and documentation in the Boost SVN trunk. If you have a new project involving Spirit, we highly recommend starting with Spirit 2.1 now. Allow me to quote OvermindDL's post from the Spirit mailing list:

I may start to sound like a bot with how often I say this, but Spirit.Classic is ancient, you should switch to Spirit2.1, it can do everything you did above a GREAT deal easier, a lot less code, and it executes faster. For example, Spirit2.1 can build your entire AST inline, no weird overriding, no need to build things up afterwards, etc..., all as one nice and fast step. You really need to update. See the other posts from the past day for links to docs and such for Spirit2.1. Spirit2.1 is currently in Boost Trunk, but will be formally released with Boost 1.41, but is otherwise complete.

Spirit X3 повторил упражнение. Он компилируется на порядок быстрее и поддерживает современный C++ (например, семантику перемещения), делая среду выполнения более производительной. Однако он отказался от некоторых хороших опор, что сделало его еще более ориентированным на небольшие парсеры, IMO.

sehe 30.06.2016 13:22

Для меня самая большая проблема в том, что выражения в Spirit, с точки зрения компилятора или отладчика, довольно длинные (я скопировал ниже отдельно одного выражения в Spirit Classic). Эти выражения меня пугают. Когда я работаю над программой, использующей Spirit, я боюсь использовать valgrind или печатать backtrace в 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 ()(char 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<

Другие вопросы по теме