¿Cuáles son las desventajas del marco generador de analizador Spirit de boost.org?
-
08-07-2019 - |
Pregunta
En varias preguntas he visto recomendaciones para el Spirit marco analizador-generador de boost.org , pero luego en los comentarios hay quejas de la gente usando el Espíritu que no es feliz. ¿Podrán esas personas presentarse y explicarnos al resto de nosotros cuáles son los inconvenientes o desventajas de usar Spirit?
Solución
Es una idea genial, y me gustó; fue especialmente útil aprender realmente cómo usar las plantillas de C ++.
Pero su documentación recomienda el uso del espíritu para analizadores pequeños a medianos. Un analizador de un idioma completo llevaría mucho tiempo compilar. Voy a enumerar tres razones.
-
Análisis sin escáner. Si bien es bastante más simple, cuando se requiere retroceder, puede ralentizar el analizador. Sin embargo, es opcional: un lexer podría estar integrado, vea el preprocesador C construido con Spirit. Una gramática de ~ 300 líneas (incluidos los archivos .h y .cpp) compila (sin optimizar) en un archivo de 6M con GCC. Las optimizaciones en línea y máximas lo reducen a ~ 1,7M.
-
Análisis lento: no hay comprobación estática de la gramática, ni para insinuar el exceso de búsqueda anticipada requerida, ni para verificar errores básicos, como por ejemplo el uso de la recursión izquierda (que conduce a una recursión infinita en el descenso recursivo analizadores LL gramáticas). Sin embargo, la recursión izquierda no es un error realmente difícil de rastrear, pero una anticipación excesiva puede causar tiempos de análisis exponenciales.
-
Uso intensivo de plantillas: aunque esto tiene ciertas ventajas, afecta los tiempos 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 agregando instancias explícitas de plantillas con los parámetros correctos, pero no fue fácil.
ACTUALIZACIÓN: mi respuesta se limita a mi experiencia con Spirit classic, no Spirit V2. Todavía esperaría que Spirit estuviera fuertemente basado en plantillas, pero ahora solo estoy adivinando.
Otros consejos
En el impulso 1.41 se lanza una nueva versión de Spirit, y se le quita el pantalón a Spirit :: classic:
Después de mucho tiempo en beta (más de 2 años con Spirit 2.0), Spirit 2.1 finalmente será lanzado con el próximo lanzamiento de Boost 1.41. El código es muy estable ahora y está listo para Codigo de producción. Estamos trabajando duro al terminar la documentación a tiempo para Boost 1.41. Puedes echar un vistazo a la estado actual de la documentación aquí. Actualmente, puedes encontrar el código y documentación en el Boost SVN el maletero. Si tienes un nuevo proyecto involucrando a Spirit, recomendamos comenzando con Spirit 2.1 ahora. Me permitirá para citar la publicación de OvermindDL del Lista de correo de Spirit:
Puedo comenzar a sonar como un bot con con qué frecuencia digo esto, pero Espíritu. Clásico es antiguo, deberías cambiar a Spirit2.1, puede hacer todo lo que hiciste por encima de una GRAN oferta más fácil, mucho menos código, y se ejecuta más rápido Por ejemplo, Spirit2.1 puede construir tu AST completo en línea, sin anulación extraña, sin necesidad para construir cosas después, etc. todo como un paso agradable y rápido. usted Realmente necesito actualizar. Ver el otro publicaciones del día anterior para enlaces a documentos y tal para Spirit2.1. Espíritu 2.1 está actualmente en Boost Trunk, pero lo hará ser lanzado formalmente con Boost 1.41, pero por lo demás está completo.
Para mí, el mayor problema es que las expresiones en Spirit, tal 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 dan miedo. 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<
Esto es lo que no me gusta:
-
la documentación es limitada. Hay una gran página web donde & Quot; todo & Quot; se explica, pero las explicaciones actuales carecen de detalles.
-
generación AST pobre. Los AST están mal explicados e, incluso después de golpearse la cabeza contra la pared para comprender cómo funcionan los modificadores de AST, es difícil obtener un AST fácil de manipular (es decir, uno que se asigne bien al dominio del problema)
-
Aumenta enormemente los tiempos de compilación, incluso para " gramáticas medianas "
-
La sintaxis es demasiado pesada. Es una realidad 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 & Lt; & Gt ;, debes repetir algunas cosas 3 veces: D (cuando quieres AST, que es lo que quiero: D)
Aparte de esto, creo que hicieron un buen trabajo con el analizador, dadas las limitaciones de C ++. Pero creo que deberían mejorarlo más. La página del historial describe que hubo un & "; Dinámico &"; espíritu antes de la actual & "; estática &"; espíritu; Me pregunto cuánto más rápido y qué mejor sintaxis tenía.
Diría que el mayor problema es la falta de diagnóstico u otra ayuda para los problemas de gramática. Si su gramática es ambigua, es posible que el analizador no analice lo que espera, y no hay una buena manera de notarlo.