viernes, 28 de marzo de 2014

Sobre el futuro a mediano plazo de ZinjaI

El fin de semana pasado publiqué una versión nueva de ZinjaI con muchas mejoras, aunque la mayoría no son fácilmente visibles. Hay pequeños detalles por aquí y por allá en la interfaz, hay mejoras en casos particulares para el autocompletado, hay algún que otro cambio en la configuración de proyecto para simplificar algunos nuevos complementos, etc. Aunque también hay cosas que se ven, como la nueva ventana de ayuda sobre C++ que permite explorar offline el contenido ofrecido por cppreference (con licencia CC, instalable como complemento); o el nuevo cuadro de configuración para la integración de proyectos con wxFormBuilder (de lo que hablaré en breve en otro post). Pero el tópico de hoy apunta a comentar qué grandes cambios (visibles o no) estoy pensando a futuro. Algunos para la próxima versión, otros tal vez tarden un poco más en llegar.

Hay varias cosas que reescribir o rediseñar para mejorar las tripas de ZinjaI. Si mejoro las estructuras de datos y el modelo de clases que sustenta su funcionamiento, el usuario final, al que no le interesa ese código fuente, no vería, en principio, ninguna mejora. Pero, el solo hecho de tener un diseño más prolijo y flexible, hará que no tenga que pensarlo dos veces ni tomarme tanto tiempo a la hora de hacer futuras mejoras. Por ejemplo, hace un par de versiones reescribí gran parte del código que realiza el manejo de puntos de interrupción. Esto simplificó lo suficiente las cosas como para que cuando un compañero me sugirió algo como "podrías asociar una descripción al breakpoint para recordar porqué lo pusimos y qué teníamos que ver" yo pudiera implementarlo en cinco minutos sin miedo a romper el resto de las funcionalidades.

Un punto a mejorar en particular es el tratamiento de las inspecciones. No se ve tan mal desde afuera, pero el código se ha hecho rebuscado y además está demasiado distribuido por diferentes partes. Entonces, un objetivo a mediano plazo es reescribir el código que gestiona esto, simplificando las interfaces de las clases que intervienen y clarificando las estructuras de datos. Esto podría darse para la próxima versión y sería un cambio interno (las funcionalidades básicas se mantendrán igual). Pero me permitiría luego corregir o mejorar varias cosas, como implementar una exploración de las inspecciones de objetos en árbol de forma similar a cómo lo hace Visual C++. Esto se puede hacer en ZinjaI, pero abriendo la inspección en una ventana de exploración separada del panel, y solo para ciertas inspecciones (no es muy estable esta funcionalidad). Sería bueno implementar esa ventana de forma genérica para poder embeberla en el panel de inspecciones, o en las evaluaciones al vuelo que se muestran  a modo de tooltip al dejar el puntero del mouse sobre una variable.

ZinjaI dispone actualmente un mecanismo para explorar la estructura de un objeto en 
forma de árbol, donde se van evaluando las inspecciones parciales bajo demanda. 
Pero la forma de uso del mismo hace que no sea tan cómodo utilizarlo con frecuencia.

Siguiendo con el tema de las inspecciones, ahora, por ejemplo, la mayoría de las acciones del menú contextual del panel se pueden aplicar solamente sobre una inspección. Sin embargo, por su significado, varias acciones deberían poderse aplicar sobre una selección de múltiples inspecciones en simultáneo. El problema está en cómo está implementado internamente, y cambiarlo manteniendo la misma base es agregar más ruido y seguramente más bugs. Por esta clase de cosas les debo la parte 4 de la serie de posts sobre Depuración. Quiero primero actualizar la interfaz para que luego lo que escriba no quede obsoleto, y de paso sea un poco más simple.

Finalmente, un punto bastante denso a analizar es el del parseo y autocompletado del código. Tengo limitaciones importantes (que ya comenté anteriormente), en su mayoría inofensivas para el estudiante que recién empieza, pero bastante molestas a la hora de llevar a cabo un proyecto real. Para colmo de "males", las "nuevas" características de C++ incorporadas en 2011 (que comienzan a ser completamente usables recién ahora) acentúan particularmente estas debilidades, ya que nos inducen a un estilo de programación algo diferente. Ya comenté qué me llevó a implementar el mecanismo que actualmente tengo, pero tanto cbrowser como mis propios pseudo-parseos se están quedando muy cortos para lo que se viene.

Y toda esta complejidad hace que sea muy difícil que me embarque en la titánica tarea de rehacer/mejorar mi propio parser. Esta cada vez más claro que necesito delegar esto en algún otro proyecto. Y el plan A supongo que sería utilizar clang. Ha evolucionado mucho, incluso para refutar algunos de mis argumentos en contra de utilizar parte de un compilador real. Yo decía cosas como "mientras escribo el código está incompleto o con errores y entonces un parser de compilador no podrá interpretar correctamente lo que debe autocompletar". Pues bien, clang hace cosas como tratar de imaginar qué quisimos poner en realidad al encontrarse con un error, para seguir analizando el resto del código como si hubiésemos escrito lo correcto. Por ejemplo, en el caso de un error de tipeo de un nombre de clase o función, si el error no es grosero clang puede darse cuenta y asumir al analizar que escribimos el nombre como se debe. Lo detectará como error, pero eso no ensuciará el resto del análisis con errores ficticios que solo son consecuencia de ese único error de tipeo.

Un compilador "normal" no reconoce ifstriam (con i en lugar de e), y entonces gran parte del
código que sigue deja de tener sentido. Clang busca algo parecido, deduce que probablemente
quisimos poner ifstream, y además de sugerirlo continúa analizando como si no hubiese error.

Así, clang está de a poco convenciéndome de que sería la mejor opción. Además, está pensado para ser utilizado como biblioteca para estas cosas (hasta tiene funciones específicas para autocompletado), y tiene sus varias otras ventajas. El punto en contra es que me va a obligar a cambiar y reescribir realmente mucho, y que voy a tener que investigar bastante su funcionamiento antes de poder hacerlo. Pero me traerá seguramente ventajas colaterales. Por ejemplo, podría implementar fácilmente opciones de refactory teniendo un parseo 100% confiable como el de clang (de hecho hay ya herramientas muy mágicas basadas en clang, como una que convierte automágicamente código C++98 a C++11). Otra gran ventaja es que podría usarlo para ir marcando ciertos errores casi en tiempo real, antes de compilar, como pasa en otros IDEs, mayormente en entornos para Java (ya que el parseo de Java es muchísimo más simple que el de C/C++). Resta por ver entonces si podré integrarlo, y si clang será lo suficientemente "liviano" como para mantenerlo todo el tiempo analizando cada cosa que cambiamos. Aunque si la integración no es tan dolorosa, podría ser opcional y convivir con el sistema actual. Es poco probable que la integración no sea dolorosa, ya que el manejo de la información parseada en ZinjaI está programado pensando más en la eficiencia que en otros valores deseables como la claridad y flexibilidad (lo cual creo que fue un error, no solo por la falta de claridad, sino porque mejor pensado la ganancia en eficiencia podría no ser tan notoria).

Finalmente, el otro cambio a corto plazo es migrar a wxWidgets 3.0 (actualmente me baso en la versión 2.8). Esto me permitirá simplificar algunas cosas en el código de la interfaz, eliminar algunos bugs, y por sobre todo utilizar una versión mucho más moderna de Scintilla, el control que se encarga de mostrar el código fuente, con su formato, coloreado, y varios otros detalles. Esto de a poco mejorará no solo el aspecto, sino también la funcionalidad del editor. Pero las mejoras vendrán de a poco, por ahora el cambio grande será migrar lo que ya está a esta nueva versión. Si no tengo que publicar en breve correcciones a la versión que subí este fin de semana, es muy probable que este cambio también llegue a tiempo para la próxima.

2 comentarios:

  1. Hola. Comencé a utilizar Zinjai ayer. Podrías decirme cual es la parte de código que se encarga de generar los diagramas de flujo ? Es decir, los archivos*.cpp y *.h que forman parte de la herramienta de generar los flowcharts. Estoy pensando en un proyecto de este tipo de herramientas y leer ese código me iría muy bien.

    ResponderEliminar
    Respuestas
    1. mxFlowCanvas tiene el código que hace el análisis del fuente a representar (ayudándose con el coloreado de scintilla), y el código que arma el dibujo. De todas formas hay varias situaciones en que no funciona bien. Creo que en algún momento en lugar de corregirlo (al dibujo), voy a reemplazarlo completamente por algo basado en graphviz. El diagrama se verá menos estructurado pero tal vez sería más fácil mostrar así los caminos raros que puede seguir el flujo de ejecución en C++. El plan B es marcar breaks, returns, y cosas raras sin flechas, sino con asterisco o numeros.

      Eliminar