viernes, 13 de febrero de 2009

Parseando en Scala (1): Intro

Voy a compartir un ejemplo de programación funcional aplicado en la vida real, para resolver un problema concreto que tuve como desarrollador. Más de una vez me tocó descifrar el fuente de un programa ajeno con una lógica tirada de los pelos (a quién no). Cuando el lenguaje en cuestión es PL/SQL, se me complica tener una buena perspectiva de la estructura del código.
Allá por los '80 en la facultad me enseñaron los diagramas estructurados de Nassi-Schneiderman. Estos diagramas están buenos para leer código estructurado, y a veces garabateo alguno si tengo que armar una lógica complicada. Pero el código ajeno nunca existe en forma de diagrama, así que estaría bueno tener una herramienta que lo genere. Esa herramienta, además de ser útil, podría ser interesante de programar.

Problema #1: Generar el diagrama

Lo más práctico me pareció generar la salida en HTML con tablas, para ver algo parecido al diagrama Nassi-Schneiderman con un browser. Fue una buena opción. Las principales estructuras a visualizar son los condicionales (IF, THEN, ELSE) y los ciclos (FOR, WHILE, LOOP), y la forma en que se anidan.

Problema #2: Interpretar el código original

Dicho de otra manera: parsear. En la primera encarnación de la herramienta, en C++, parseaba a lo guapo, por no decir a lo bestia. No es que no se pueda parsear bien con C++, pero terminé con una implementación con limitaciones absurdas. Me metí en un berenjenal. Antes de parsear me veía obligado a toquetear el código de entrada para adecuarlo a ciertos requisitos inexcusables... por ejemplo, si había una cadena de ELSIF's:


IF cond1 THEN
sentencia1;
ELSIF cond2 THEN
sentencia2;
END IF;


...para que fuera parseable, tenía que modificarlo manualmente a:


IF cond1 THEN
sentencia1;
ELSE
IF cond2 THEN
sentencia2;
END IF;
END IF;


Creo que también hice una versión en C# que andaba mejor, pero el problema de fondo no estaba resuelto.

Combinator parsing al rescate

La técnica funcional salvadora fue combinator parsing, en este caso en Scala. No voy a incluir acá un tutorial de Scala ni voy a explicar cómo está implementada la librería (si bien vale la pena empaparse en el tema). Voy a enfocarme en el uso de la librería usando mi proyecto como ejemplo. Van a notar que parece un lenguaje con soporte especial para parsing, pero es solo una ilusión: el lenguaje es lo suficientemente flexible, porque permite definir operadores como | y ~ en forma de métodos, tiene conversiones implícitas, y un toque de syntax sugar que lo hace óptimo para crear DSLs (domain specific languages). 

Combinator parsing viene incluido en la librería estándar de Scala, y más que un API, parece un sublenguaje. Su implementación puede resultar un tanto sesuda para el no iniciado; pero su utilización, prometo, está al alcance de cualquier programador.

Otro problema que tenían las soluciones anteriores era el acoplamiento entre el parsing y la generación de la salida. Vamos a separar las responsabilidades para que, una vez resuelto el parseo, podamos generar todas las salidas que se nos antoje.

Continuará

No hay comentarios:

Publicar un comentario