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?

¿Fue útil?

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.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top