25 de abril de 2008

AZ 24: Lenguajes de programación en UNIX. Parte 3/5: YACC

Yacc es una herramienta general para imponer cierta estructura sobre la entrada de un programa. De este modo el programador debe preparar una especificación que incluiría un conjunto de reglas para describir los elementos de entrada, el código a invocarse cuando se reconoce alguna de estas reglas y una definición o declaración de una rutina de bajo nivel para examinar la entrada.

Entonces Yacc traduce la especificación a una función en lenguaje C que examina el flujo de entrada. Esta función llamada parser, trabaja llamando al checador (scanner) de bajo nivel para la entrada conocido como analizador léxico, va tomando ciertos patrones desde el flujo de entrada. Los patrones seleccionados son ahora tokens o marcas, se van comparando con las reglas de construcción o reglas de gramática. En el momento en el que se halla alguna de éstas se hace la llamada al código necesario para manipularlo adecuadamente (acción), dichas acciones o son más que fragmentos de código en C que regresan valores y hacen uso de otros arrojados por alguna otra acción.

El corazón de la especificación Yacc es la colección de reglas gramáatica. Por ejemplo, una de estas reglas podría verse así:

fecha: mes día ',' año;

En donde fecha, mes, día y año representan construcciones de interés, presumiblemente están definidos a detalle en algún lugar del código. En el ejemplo la coma se encierra en comillas simples. Los dos puntos (:) y el punto y coma (;) sirven meramente como puntuación en la regla y no tienen significancia al evaluar la entrada. Con las definiciones propias, la entrada "Agosto 8,2000" podría encajar en la regla.

El analizador léxico es una parte vital de la función parser. Esta rutina proporcionada por el programador lee el flujo de entrada, reconoce las construcciones del flujo de entrada como símbolos terminales, en cambio el parser que las toma como símbolos no terminales. Para evitar confusiones llamaremos a los símbolos terminales como marcas o tokens.

Existen discrepancias al decidir si se utiliza un analizador léxico o las reglas de gramática para reconocer las construcciones. Por ejemplo, las reglas:

mes : 'E' 'n' 'e'
mes : 'F' 'e' 'b'
. . . .
mes : 'D' 'i' 'c'
podrían emplearse en el ejemplo anterior. Mientras el analizador léxico necesita reconocer individualmente los caracteres, las rutinas de bajo nivel tienden a gastar tiempo y espacio y podrían complicar la especificación más allá de la capacidad de Yacc para lidiar con esto. Usualmente el analizador léxico reconoce los nombres de los meses y regresa un valor indicativo de que ha visto un "mes". En este casi, mes es una marca y ya no se necesitan reglas detalladas.

Los caracteres literales como las comas deben pasar también a través del analizador léxico y se consideran como marcas.

Las especificaciones son muy flexibles y es relativamente sencillo agregar al ejemplo anterior la regla:

fecha : mes '/' día '/' año;
de esta manera una entrada del tipo 8/8/2000 es sinónimo de Agosto 8,2000. En muchos casos esta nueva regla se puede añadir con un mínimo de esfuerzo y sin temor a dañar la entrada existente.

Cuando la entrada no conforma a ninguna de las especificaciones existentes se produce un error que se atrapa mediante un chequeo de izquierda a derecha tan prontamente como sea posible. Así, no solo hay la posibilidad de leer y procesar las entradas con errores pues los datos erróneos se hallan fácilmente. El manejo de errores provisto como parte de las especificaciones de entrada permiten la entrada de datos mal formados o la continuación del proceso de la entrada después de omitir los errores.

En algunos casos, Yacc falla al producir un parser cuando se da un grupo de especificaciones. Por ejemplo, cuando dichas especificaciones son contradictorias o cuando requieren de un mecanismo de reconocimiento más poderoso del que Yacc puede ofrecer. Estos casos representan errores en el diseño, en este sentido, con frecuencia se pueden hacer las correcciones necesarias volviendo a escribir las reglas gramaticales. Aunque Yacc no puede manejar todas las especificaciones posibles, su poder se compara favorablemente con sistemas similares. Aún más, las construcciones difíciles para Yacc resultan serlo también para los seres humanos. Algunos programadores reportan que la disciplina al formular especificaciones Yacc válidas para su entrada revelan errores de concepción o diseño en etapas tempranas del desarrollo de un programa

Artículo original