domingo, 30 de diciembre de 2012

Destripando PSeInt

PSeInt es un programa compuesto por muchos programas. Es decir, consta en realidad de varios ejecutables que se invocan y comunican entre ellos, de forma tal que para el usuario final se ven como partes de un único entorno. En este artículo voy a comentar cuales son esas partes (desde ahora módulos, cada uno un ejecutable), y cómo y cuando se comunican. La idea es que sirva de referencia para el siguiente artículo donde discutiré porqué es así, porqué las cosas están separadas o juntas, porqué se comunican de esa manera, y qué tiene esto de bueno o de malo según mi experiencia y las necesidades particulares de este proyecto. De paso, también sirve de documentación para mí y para los que quieran mirar el código fuente.

Primero voy a hacer una descripción rápida de qué hace cada módulo, y luego cómo se relacionan. En este análisis dejo de lado intencionalmente a psdraw, ya que sus funcionalidades serán absorbidas por psdraw2 (algunas ya lo fueron, y las que no, lo serán en las próximas versiones).  Los módulos que componen entonces al sistema de PSeInt son:
  • pseint: Es el principal componente. Se encarga de analizar un algoritmo en pseudocódigo, e indicar los errores si los hay, o interpretarlo en caso contrario. El análisis del algoritmo produce como resultado parcial un pseudocódigo normalizado que se utiliza como entrada en otros módulos. Es una aplicación de consola que toma el algoritmo del usuario desde un archivo de texto.
  • wxPSeInt: Es el editor de pseudocódigo (como texto), es la interfaz visual del sistema, desarrollado con wxWidgets. Presenta el editor de texto con todas sus ayudas, y se encarga de lanzar y gestionar el comportamiento de los demás módulos cuando es necesario.
  • psterm: Es la terminal donde se ejecuta pseint, y posee además la habilidad de registrar las entradas que el usuario hace por teclado para reproducir toda la ejecución desde cero cuando el algoritmo cambia, o se quiere volver en el tiempo para alterar una entrada.
  • psdraw2: Se encarga de generar, mostrar y editar el diagrama de flujo. Toma por entrada un pseudocódigo normalizado, calcula los tamaños y posiciones de las entidades del diagrama y los visualiza y edita interactivamente con una interfaz basada en OpenGL y GLUT.
  • psexport: Se encarga de traducir a código C++ un pseudocódigo normalizado. Gran parte de las tareas de traducción son independientes del lenguaje final, por lo que en su implementación está separado, desde haces una pocas versiones, lo que depende de C++ del resto, de modo que sea simple añadir otros lenguajes similares.
  • updatem: Solo se encarga de ver si hay actualizaciones una vez al día. Está separado de wxPSeInt solo para evitar que la interfaz se bloquee o muestre errores cuando hay problemas de red.

Veamos ahora cuándo entra en acción cada uno y cómo se comunican entre ellos, de acuerdo a las distintas acciones que puede requerir un usuario. La comunicación más básica es un pasaje de parámetros de un proceso a otro a través de argumentos en la linea de comandos, que suelen involucrar direcciones de archivos temporales de intercambio. Para los procesos más acoplados se utilizan tuberías (un proceso controla el flujo de entrada y salida estándar del otro), o conexiones TCP/IP locales (dos procesos dialogan en un modo cliente-servidos con un protocolo propio ad-hoc muy sencillo). El siguiente gráfico resume todos los procesos intervinientes y sus mecanismos:

  • Mientras el usuario edita el algoritmo, la interfaz realiza todas las tareas de coloreado y visualización, pero un intérprete oculto ejecutado en un modo especial que se encarga del análisis sintáctico necesario para marcar los errores en tiempo real. Además, como subproducto de ese análisis se obtienen los límites de los bloques de código que se marcan con amarillo, y la lista de procesos/subprocesos y variables para mostrar en el panel del margen izquierdo del editor. La comunicación entre ambos se realiza mediante una tubería cuando transcurre un segundo desde el último cambio realizado por el usuario al algoritmo.
  • Cuando el usuario quiere ejecutar un algoritmo desde la interfaz, la interfaz invoca a la terminal y la terminal invoca al interprete. El interprete corre por su cuenta, tomando el pseudocódigo de un archivo temporal escrito por el editor, y comunicando entradas y salidas a la terminal por medio de una tubería; mientras que la terminal se comunica con el editor para enviar o recibir eventos por medio de una conexión tcp/ip. Un evento puede ser que el pseudocódigo cambió y debe recargarse, o que la ejecución se interrumpió y deben mostrarse los errores, por ejemplo. Cuando el pseudocódigo cambia, primero el editor guarda la nueva versión en el archivo temporal, y luego le avisa a la terminal que relance el proceso del intérprete.
  • Cuando el usuario quiere ejecutar paso a paso, el proceso es similar, pero la interfaz se comunica por un socket con el intérprete en este caso y no con la consola (ya que en este modo no se permite modificar el algoritmo ni volver el tiempo hacia atrás). Se transfieren estados y ordenes para controlar la ejecución y evaluar las inspecciones.
  • Cuando el usuario quiere ver el diagrama de flujo, o exportar el algoritmo a un programa C++, primero se invoca al intérprete para verificar que el algoritmo no contenga errores y generar esa versión normalizada, que luego será mucho más fácil de parsear por parte de los otros módulos. Ambas versiones, el algoritmo original de entrada y el algoritmo normalizado de salida, se transmiten por medio de archivos temporales. Uno de estos temporales será tomado por psdraw2 o por psexport. Si psedraw2 fue invocado para editar el diagrama, se comunicará por socket con wxPSeInt, para indicarle a este cuando se ha actualizado el archivo temporal y debe por lo tanto recargar el pseudocódigo para reflejar los cambios.

Y así es, básicamente como está estructurado el código de PSeInt, separado en pequeños proyectos con responsabilidades específicas. En el próximo post abriremos entonces las discusión respecto a motivos, ventajas y desventajas.

2 comentarios: