c++ parsing boost boost-spirit parser-generator

c++ - ¿Cuáles son las desventajas del framework generador de analizadores de Spirit de boost.org?



parsing boost-spirit (5)

En boost 1.41 se lanza una nueva versión de Spirit, y supera a spirit :: classic:

Después de mucho tiempo en beta (más de 2 años con Spirit 2.0), Spirit 2.1 finalmente se lanzará con la próxima versión de Boost 1.41. El código es muy estable ahora y está listo para el código de producción. Estamos trabajando duro para terminar la documentación a tiempo para Boost 1.41. Puede echar un vistazo al estado actual de la documentación aquí. Actualmente, puede encontrar el código y la documentación en la troncal de Boost SVN. Si tiene un nuevo proyecto que involucre a Spirit, le recomendamos comenzar con Spirit 2.1 ahora. Permítanme citar la publicación de OvermindDL de la lista de correo Spirit:

Puedo comenzar a sonar como un bot con la frecuencia con que digo esto, pero Spirit.Classic es antiguo, debes cambiar a Spirit2.1, puede hacer todo lo que hiciste por encima de una gran oferta, mucho menos código, y ejecuta Más rápido. Por ejemplo, Spirit2.1 puede construir todo tu AST en línea, sin sobreescritura extraña, sin necesidad de crear cosas después, etc., todo como un paso agradable y rápido. Realmente necesitas actualizar. Vea las otras publicaciones del día anterior para enlaces a documentos y tal para Spirit2.1. Spirit2.1 se encuentra actualmente en Boost Trunk, pero se lanzará formalmente con Boost 1.41, pero está completo.

En varias preguntas, he visto recomendaciones para el marco del generador de analizadores de Spirit de boost.org , pero luego en los comentarios hay quejas de personas que usan Spirit que no están contentas. ¿Podrán esas personas ponerse de pie y explicarnos al resto de nosotros cuáles son los inconvenientes o inconvenientes del uso del Espíritu?


Es una idea genial, y me gustó; fue especialmente útil aprender realmente cómo usar plantillas C ++.

Pero su documentación recomienda el uso de espíritu para analizadores de tamaño pequeño a mediano. Un analizador para un lenguaje completo tardaría años en compilarse. Enumeraré tres razones.

  • Análisis sin escaneo. Si bien es bastante más simple, cuando se requiere retroceder, puede ralentizar el analizador. Sin embargo, es opcional: un lexer puede estar integrado, vea el preprocesador C creado con Spirit. Una gramática de ~ 300 líneas (incluidos los archivos .h y .cpp) se compila (sin optimizar) en un archivo de 6M con GCC. La alineación y las optimizaciones máximas lo reducen a ~ 1,7M.

  • Análisis lento: no hay comprobación estática de la gramática, ni para insinuar una búsqueda anticipada excesiva, ni para verificar errores básicos, como por ejemplo el uso de la recursividad izquierda (que conduce a la recursión infinita en las gramáticas LL de analizadores de descenso recursivo). Sin embargo, la recursividad a la izquierda no es un error realmente difícil de rastrear, pero una búsqueda anticipada excesiva puede causar tiempos de análisis exponenciales.

  • Uso de plantillas pesadas: si bien esto tiene ciertas ventajas, esto afecta el tiempo de compilación y el tamaño del código. Además, la definición de gramática normalmente debe ser visible para todos los demás usuarios, lo que afecta aún más los tiempos de compilación. He podido mover gramáticas a archivos .cpp al agregar instancias de plantillas explícitas con los parámetros correctos, pero no fue fácil.

ACTUALIZACIÓN: mi respuesta se limita a mi experiencia con Spirit clásico, no Spirit V2. Todavía esperaría que Spirit estuviera fuertemente basado en plantillas, pero ahora solo estoy adivinando.


Esto es lo que no me gusta de él:

  • la documentación es limitada. Hay una gran página web donde se explica "todo", pero las explicaciones actuales carecen de detalles.

  • pobre generación AST. Los AST no están bien explicados y, incluso después de golpearse la cabeza contra la pared para entender cómo funcionan los modificadores de AST, es difícil obtener un AST fácil de manipular (es decir, uno que se corresponda bien con el dominio del problema)

  • Aumenta enormemente los tiempos de compilación, incluso para gramáticas de tamaño "medio"

  • La sintaxis es muy pesada. Es un hecho de la vida que en C / C ++ debe duplicar el código (es decir, entre la declaración y la definición). Sin embargo, parece que en boost :: spirit, cuando declaras una gramática <>, debes repetir algunas cosas 3 veces: D (cuando quieras ASTs, que es lo que quiero: D)

Aparte de esto, creo que hicieron un trabajo bastante bueno con el analizador sintáctico, dadas las limitaciones de C ++. Pero creo que deberían mejorarlo más. La página de historia describe que había un espíritu "dinámico" antes del espíritu "estático" actual; Me pregunto cuánto más rápida y mejor sintaxis tenía.


Para mí, el mayor problema es que las expresiones en Spirit, como las ve el compilador o el depurador, son bastante largas (copié debajo de una parte de una expresión en Spirit Classic). Estas expresiones me asustan. Cuando trabajo en un programa que usa Spirit, me da miedo usar valgrind o imprimir backtrace en 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<


Yo diría que el mayor problema es la falta de diagnóstico u otra ayuda para problemas gramaticales. Si tu gramática es ambigua, es posible que el analizador no analice lo que esperas, y no hay una buena manera de darte cuenta.