martes, 23 de octubre de 2012

Parseo y mayusculización en PSeInt

Estoy casi seguro de que esa palabra (mayusculización) no existe, pero viene bien para hablar de algo que pasa en PSeInt cuando le pedimos que haga algo con un algoritmo que no sea simplemente guardarlo. Por ejemplo, si le pedimos que genere el diagrama de flujo, ya sea para verlo o editarlo, todos los identificadores pasan a estar en mayúsculas. Si le pedimos que exporte a código C++ ocurre algo similar, pero en este caso con minúsculas. Si le pedimos que lo ejecute y el intérprete encuentra errores, los identificadores que pone para aclarar entre paréntesis en los mensajes de error están siempre en mayúsculas. Y así ocurre con algunas otras situaciones menos frecuentes.

La justificación es bastante simple: para simplificar el "parseo" del pseucódigo, todo se pasa a mayúsculas. "To parse" significa analizar, o procesar, y el "parser" es una parte fundamental de cualquier intérprete/compilador. Sin embargo, es muy frecuente no dejarle al parser toodo el trabajo, sino hacer antes un preprocesamiento del código para simplificar algunas tareas posteriores (que no son difíciles en realidad, solo tediosas o poco eficientes de otra forma). En muchos casos, parte de este preproceso está en el parser mismo, ya que lo realiza internamente.
Preprocesar el algotirmo en pseudocódigo antes de intentar entender su significado hace que luego, cada vez que pregunte, por ejemplo,  si una palabra clave es "Escribir", tenga que preguntar solo por "ESCRIBIR", sin preocuparme si el usuario en realidad ingresó "Escribir", "esCRIBir", "EsCrIbIr" o cualquier otra variación que se les ocurra. Es decir, cuando analizo el significado de una línea, ya sea para verificar si es correcto, para ejecutarla, o para saber cómo incluirla en el diagrama, todo es más simple si hay menos variantes posibles. Lo mismo ocurre con las comillas dobles que se reemplazan por simples, los corchetes por paréntesis, los comentarios que se eliminan, los espacios extras, tabulados y líneas en blanco que también se eliminan, las líneas con más de una instrucción que se separan en varias lineas de una instrucción, la sintaxis flexible que se reemplaza por su equivalente estricta, etc. Entonces, el pseudocódigo que realmente termino interpretando está preprocesado de forma que a veces no se parece taanto al original.

En esta captura el diagrama de flujo ya muestra los identificadores correctamente, 
pero el panel de variables todavía utiliza los nombres mayusculizados

Si estoy pensando en analizar el algoritmo y marcar un error en una línea, por ejemplo, tengo que mantener de alguna forma una relación entre los números de línea antes y después de ese preprocesamiento, y esto se hace actualmente. De igual forma tendría que hacer con todas los demás "detalles" que se pierden, pero esos otros no estaban contemplados en el diseño original del intérprete. Para solucionar el tema de la "mayusculicidad" o "minusculicidad" de los identificadores, un camino sería por ejemplo reemplazar la clase std::string que uso para guardar y procesar las cadenas que forman el pseudocódigo por una variante con la misma interfaz, pero que mantenga internamente dos copias de alguna forma "sincronizadas" de cada cadena. Pero no es la opción que menos trabajo implica. La versión que se encuentra en el repositorio git utiliza un contenedor de tipo map donde guarda las correspondencias entre la versión preprocesada y la versión sin preprocesar de cada palabra. Este mapeo se arma cortando todas las palabras que encuentra antes de hacer cualquier otra cosa. Entonces, después de procesar el código, si el objetivo era mostrarlo de alguna forma (por ejemplo en el editor de diagramas de flujo), se aplica una especie de semi-desprocesado, donde se vuelve a los nombres originales. Con el detalle de que guarda una sola correspondencia por identificador, así que si escriben de distintas formas el mismo identificador, la última es la que vale.

Respecto a cambios de tabulados y dobles espacios, no me interesa demasiado recuperarlos, ya que a veces hasta dejan de tener sentido luego de editado el algoritmo, y otras veces ya no lo tenían antes de empezar; mientras que a la salida del editor siempre se corresponden con el anidamiento lógico de las estructuras. El caso de las mayúsculas y minúsculas me hacía un poco más de ruido porque, sobretodo en el editor de diagramas de flujo, quedaban bastante feas las variables así (a menos que uno haya aprendido a programar en Basic a fines de los 80 y todavía no haya dejado esa mala costumbre). Lo que sí me gustaría recuperar son los comentarios, pero para esto tengo primero que andar adivinando a qué "anclarlos". Es decir, supongamos que vengo parseando el código y veo una línea con un comentario. Ese comentario ¿pertenece a la linea anterior? ¿a la que sigue? ¿a todo un bloque? ¿a ninguna porque era una linea de código que se comentó para no borrar?, ¿o que? Y aún suponiendo que imponga un par de reglas para decidir cómo anclar los comentarios y diga "que se joda quien comente de otra forma", todavía queda el para nada menor detalle de cómo integrarlo en el diagrama de flujo. Tendría que cambiar un poco las estructuras de datos para incluir estas "anclas", pero más que todo tendría que complicar un poco el manejo de eventos y la forma de dibujar (de ordenar las cosas en el espacio) para tener alguna manera simple y visual de leer/editar/agregar/eliminar estos comentarios sin interferir con el verdadero algoritmo.

Como verán, esto no era un bug, sino una decisión de diseño, que sonaba totalmente razonable cuando comencé el proyecto y los requerimientos eran mucho menores. De hecho, si lo volviese a empezar, este preproceso estaría nuevamente presente sin dudas, de una u otra forma. Entonces, siendo inevitable, vamos a poner algunos parches para tratar de que no se note. Lo de los comentarios está un poco bastante verde todavía, pero lo de las mayúsculas y minúsculas ya estará más o menos solucionado en la próxima versión.

No hay comentarios:

Publicar un comentario