Sunteți pe pagina 1din 156

Universidad de Santiago de Chile Facultad de Ingeniera Departamento de Ingeniera Informtica

APUNTES DE LA ASIGNATURA

COMPILADORES

Mag. Jacqueline Khler C.

Compiladores

TABLA DE CONTENIDOS
1 INTRODUCCIN ................................................................................................................................. 7 1.1 QU ES UN PROGRAMA? ......................................................................................................... 8 1.2 DEFINICIN DE COMPILADOR ................................................................................................ 8 1.3 COMPILADORES E INTRPRETES ........................................................................................... 9 1.4 CONTEXTO EN QUE SE SITA EL COMPILADOR ................................................................ 9 1.5 FASES DE UN COMPILADOR .................................................................................................. 11 1.6 CLASIFICACIN DE LOS COMPILADORES.......................................................................... 13 1.7 HERRAMIENTAS TILES ......................................................................................................... 13 2 ANLISIS LXICO ............................................................................................................................ 14 2.1 FUNCIN DEL ANALIZADOR LXICO.................................................................................. 14 2.2 COMPONENTES LXICOS, PATRONES Y LEXEMAS ......................................................... 15 2.3 ERRORES LXICOS ................................................................................................................... 16 2.4 IMPLEMENTACIN DE ANALIZADORES LXICOS .......................................................... 17 2.4.1 MTODOS GENERALES ..................................................................................................... 17 2.4.2 CONSTRUCCIN DE UN ANALIZADOR LXICO ......................................................... 17 2.5 EJERCICIOS ................................................................................................................................. 21 3. ANLISIS SINTCTICO .................................................................................................................. 23 3.1 CONCEPTOS PREVIOS .............................................................................................................. 23 3.1.1 RECURSIVIDAD POR LA IZQUIERDA ............................................................................. 23 3.1.2 FACTORIZACIN POR LA IZQUIERDA .......................................................................... 24 3.1.3 CONJUNTOS ANULABLE, PRIMERO Y SIGUIENTE ..................................................... 27 3.1.3.1 Conjunto Anulable ........................................................................................................... 27 3.1.3.2 Conjunto Primero ............................................................................................................. 27 3.1.3.3 Conjunto Siguiente........................................................................................................... 27 3.2 DESCRIPCIN GENERAL DEL ANLISIS SINTCTICO .................................................... 28 3.3 ALGUNOS ASPECTOS DEL MANEJO DE ERRORES SINTCTICOS................................. 29 3.4 ANLISIS SINTCTICO PREDICTIVO ................................................................................... 30 3.4.1 ASPECTOS GENERALES .................................................................................................... 30 3.4.2 CONSTRUCCIN Y FUNCIONAMIENTO DE ANALIZADORES SINTCTICOS LL(1) ......................................................................................................................................................... 31 Jacqueline Khler C. - USACH

Compiladores 3.4.3 GRAMTICAS LL(1) ........................................................................................................... 35 3.4.4 RECUPERACIN DE ERRORES EN ANLISIS SINTCTICO LL(1) ........................... 35 3.4.4.1 Recuperacin de errores en modo de pnico ................................................................... 35 3.4.4.2 Recuperacin de errores a nivel de frase ......................................................................... 36 3.5 ANLISIS SINTCTICO DESCENDENTE (POR DESPLAZAMIENTO Y REDUCCIN).. 37 3.5.1 ASPECTOS GENERALES .................................................................................................... 37 3.5.2 GRAMTICAS LR(K) .......................................................................................................... 38 3.5.3 ANLISIS SINTCTICO SLR ............................................................................................. 38 3.5.3.1 Elemento LR(0)................................................................................................................ 38 3.5.3.2 Operacin Clausura .......................................................................................................... 39 3.5.3.3 Construccin del autmata ............................................................................................... 40 3.5.3.4 Construccin y operacin del analizador sintctico SLR ................................................ 42 3.5.4 USO DE GRAMTICAS AMBIGUAS ................................................................................ 48 3.5.5 RECUPERACIN DE ERRORES ........................................................................................ 49 3.5.5.1 Recuperacin en modo de pnico .................................................................................... 49 3.5.5.2 Recuperacin a nivel de frase .......................................................................................... 50 3.5.6 ANLISIS SINTCTICO LR(1)........................................................................................... 51 3.5.6.1 Elemento LR(1)................................................................................................................ 51 3.5.6.2 Operacin clausura ........................................................................................................... 52 3.5.6.3 Construccin del AFD ..................................................................................................... 53 3.5.6.4 Construccin de la tabla de anlisis sintctico LR(1) ...................................................... 53 3.5.7 ANLISIS SINTCTICO LALR .......................................................................................... 58 3.5.7.1 Notas preliminares ........................................................................................................... 58 3.5.7.2 Construccin del AFD a partir del analizador sintctico LR(1) ...................................... 58 3.5.7.3 Construccin directa del AFD.......................................................................................... 63 3.6 EJERCICIOS ................................................................................................................................. 64 4 ANLISIS SEMNTICO ................................................................................................................... 66 4.1 DEFINICIONES DIRIGIDAS POR LA SINTAXIS .................................................................... 66 4.1.1 ATRIBUTOS SINTETIZADOS ............................................................................................ 67 4.1.2 ATRIBUTOS HEREDADOS ................................................................................................. 68 4.1.3 GRAFOS DE DEPENDENCIAS ........................................................................................... 69 Jacqueline Khler C. - USACH

Compiladores 4.2 RBOLES SINTCTICOS .......................................................................................................... 71 4.3 COMPROBACIN DE TIPOS .................................................................................................... 72 4.4 OTRAS COMPROBACIONES SEMNTICAS ......................................................................... 76 4.4.1 VALORES DEL LADO IZQUIERDO .................................................................................. 76 4.4.2 PARMETROS DE FUNCIN ............................................................................................ 77 4.4.3 PALABRA CLAVE return ..................................................................................................... 78 4.4.4 CASOS DUPLICADOS EN UN switch................................................................................. 79 4.4.5 ETIQUETAS goto .................................................................................................................. 79 4.5 EJERCICOS .................................................................................................................................. 79 5 AMBIENTES PARA EL MOMENTO DE EJECUCIN .................................................................. 82 5.1 ASPECTOS DEL LENGUAJE FUENTE .................................................................................... 82 5.1.1 PROCEDIMIENTOS ............................................................................................................. 82 5.1.2 RBOLES DE ACTIVACIN .............................................................................................. 82 5.1.3 PILAS DE CONTROL ........................................................................................................... 83 5.1.4 MBITO DE UNA DECLARACIN ................................................................................... 85 5.1.5 ENLACE DE NOMBRES ...................................................................................................... 85 5.2 ORGANIZACIN DE LA MEMORIA ....................................................................................... 86 5.2.1 SUBDIVISIN DE LA MEMORIA DURANTE LA EJECUCIN..................................... 86 5.2.2 REGISTROS DE ACTIVACIN .......................................................................................... 86 5.2.3 DISPOSICIN ESPACIAL DE LOS DATOS LOCALES EN EL MOMENTO DE LA COMPILACIN.............................................................................................................................. 87 5.3 ESTRATEGIAS PARA LA ASIGNACIN DE MEMORIA ..................................................... 88 5.3.1 ASIGNACIN ESTTICA ................................................................................................... 88 5.3.2 ASIGNACIN POR MEDIO DE UNA PILA ....................................................................... 89 5.3.3 ASIGNACIN POR MEDIO DE UN MONTCULO .......................................................... 89 5.4 ACCESO A NOMBRES NO LOCALES ..................................................................................... 89 5.4.1 BLOQUES .............................................................................................................................. 90 5.4.2 MBITO LXICO SIN PROCEDIMIENTOS ANIDADOS ................................................ 91 5.4.3 MBITO LXICO CON PROCEDIMIENTOS ANIDADOS .............................................. 92 5.4.4 MBITO DINMICO ........................................................................................................... 92 5.5 PASO DE PARMETROS .......................................................................................................... 93

Jacqueline Khler C. - USACH

Compiladores 5.5.1 LLAMADA POR VALOR ..................................................................................................... 94 5.5.2 LLAMADA POR REFERENCIA .......................................................................................... 95 5.5.3 COPIA Y RESTAURACIN................................................................................................. 95 5.6 TABLA DE SMBOLOS .............................................................................................................. 96 5.6.1 ENTRADAS DE LA TABLA DE SMBOLOS .................................................................... 97 5.6.2 INFORMACIN SOBRE LA ASIGNACIN DE MEMORIA ........................................... 97 5.6.3 TIPOS DE TABLAS DE SMBOLOS ................................................................................... 98 5.6.4 IMPLEMENTACIN DE LA TABLA DE SMBOLOS ...................................................... 98 5.6.5 REPRESENTACIN DE LA INFORMACIN SOBRE EL MBITO ............................... 99 5.7 ASIGNACIN DINMICA DE LA MEMORIA ...................................................................... 101 5.7.1 INSTRUMENTOS DE LOS LENGUAJES ......................................................................... 101 5.7.2 ASIGNACIN EXPLCITA DE BLOQUES DE TAMAO FIJO .................................... 102 5.7.3 ASIGNACIN EXPLCITA DE BLOQUES DE TAMAO VARIABLE ........................ 102 5.7.4 DESASIGNACIN IMPLCITA ......................................................................................... 103 5.7.4.1 Cuenta de referencias ..................................................................................................... 104 5.7.4.2 Tcnicas de marca .......................................................................................................... 104 5.8 EJERCICIOS ............................................................................................................................... 104 6 GENERACIN DE CDIGO INTERMEDIO ................................................................................. 106 6.1 LENGUAJES INTERMEDIOS .................................................................................................. 106 6.1.1 REPRESENTACIONES GRFICAS .................................................................................. 106 6.1.2 CDIGO DE TRES DIRECCIONES .................................................................................. 107 6.1.2.1 Tipos de proposiciones de tres direcciones .................................................................... 108 6.1.2.2 Implementaciones de cdigo de tres direcciones ........................................................... 109 6.1.2.3.1 Cudruplos............................................................................................................... 109 6.1.2.3.2 Triples ...................................................................................................................... 110 6.1.2.3.3 Triples indirectos ..................................................................................................... 110 6.2 TRADUCCIN DIRIGIDA POR LA SINTAXIS ..................................................................... 111 6.2.1 DECLARACIONES ............................................................................................................. 111 6.2.1.1 Declaraciones dentro de un procedimiento .................................................................... 112 6.2.1.2 Nombres de campos dentro de registros ........................................................................ 114 6.2.3 PROPOSICIONES DE ASIGNACIN ............................................................................... 114 Jacqueline Khler C. - USACH

Compiladores 6.2.3.1 Expresiones aritmticas.................................................................................................. 115 6.2.3.2 Expresiones booleanas ................................................................................................... 117 6.2.3.3 Acceso a elementos de matrices..................................................................................... 119 6.2.3.4 Acceso a elementos de registros .................................................................................... 121 6.2.4 SENTENCIAS DE FLUJO DE CONTROL ........................................................................ 121 6.2.4.1 Sentencia goto ................................................................................................................ 121 6.2.4.2 Sentencia if ..................................................................................................................... 121 6.2.4.3 Sentencia if-else ............................................................................................................. 122 6.2.4.4 Sentencia switch ............................................................................................................. 123 6.2.4.5 Sentencia while .............................................................................................................. 124 6.2.4.6 Sentencia do-while ......................................................................................................... 125 6.2.4.7 Sentencia repeat-until ..................................................................................................... 126 6.2.4.8 Sentencia for .................................................................................................................. 126 6.2.5 LLAMADAS A PROCEDIMIENTOS ................................................................................ 127 6.3 EJERCICIOS ............................................................................................................................... 127 BIBLIOGRAFA .................................................................................................................................. 129 ANEXO: NOCIONES DE LENGUAJES FORMALES ...................................................................... 130 A.1 JERARQUA DE LOS LENGUAJES ....................................................................................... 130 A.2 GRAMTICAS .......................................................................................................................... 131 A.2.1 GRAMTICAS REGULARES .......................................................................................... 135 A.2.2 GRAMTICAS LIBRES DE CONTEXTO ....................................................................... 136 A.2 EXPRESIONES REGULARES ................................................................................................. 138 A.3 AUTMATAS FINITOS........................................................................................................... 139 A.3.1 AUTMATA FINITO DETERMINSTICO (AFD) .......................................................... 141 A.3.2 AUTMATA FINITO NO DETERMINSTICO (AFND)................................................. 144 A.3.3 EQUIVALENCIA ENTRE AFD Y AFND ......................................................................... 145 A.3.4 MINIMIZACIN DE AFD ................................................................................................. 148 A.3.5 EQUIVALENCIA ENTRE AF Y ER: MTODO DE THOMPSON ................................. 149 A.4 AUTMATAS APILADORES ................................................................................................. 153

Jacqueline Khler C. - USACH

Compiladores 1 INTRODUCCIN Un lenguaje es una abstraccin que permite la comunicacin y coordinacin mediante la creacin de conceptos que pueden ser entendidos de la misma forma por los distintos participantes. As, un lenguaje puede entenderse como una forma de hablar o de describir algo. Para poder hablar de lenguaje es importante conocer algunos elementos. Por ejemplo, cmo se crean los conceptos? Cmo hacer que todos entiendan lo mismo? El primer paso para responder esta pregunta est en el concepto de smbolo. En el caso del castellano, se puede pensar en las letras como smbolos elementales, que se emplean para construir conceptos sencillos denominados palabras. El conjunto de todos los smbolos se conoce como alfabeto. Ahora bien, las palabras tambin pueden formar parte de estructuras ms complejas: frases y oraciones. A su vez, stas pueden conformar prrafos, etc. A partir de los conceptos anteriores podra pensarse que cualquier secuencia de letras es una palabra o que una oracin podra ser una secuencia aleatoria de palabras. Pero de nuestra experiencia previa sabemos que no es as. Existe una serie de reglas que limitan la forma de las palabras o la estructura de un texto: Reglas lxicas: corresponden a las reglas de ortografa de un lenguaje, e indican la forma que deben tener las palabras. Por ejemplo, en castellano no puede haber palabras que contengan una n seguida de una b. Reglas sintcticas: definen la forma en que se debe estructurar un texto. Por ejemplo, una oracin debe tener sujeto y predicado. El predicado debe tener un ncleo que puede ir acompaado de diversos complementos. Reglas semnticas: determinan el significado de lo que se dice en el texto sintcticamente correcto y guardan relacin con el contexto. Por ejemplo, la frase se rbol puede referirse a un rbol de un parque, o bien a una estructura de datos empleada en la resolucin de un problema computacional. Con todo lo anterior, ya tenemos una primera nocin de qu es un lenguaje. Ahora bien, sabemos que existen muchos lenguajes y que en ocasiones es necesario pasar un mensaje o texto de un lenguaje a otro. De aqu surgen los conceptos de traduccin (generar una copia escrita del mensaje original en un idioma distinto) e interpretacin (repetir verbalmente un mensaje en un lenguaje diferente al empleado por el emisor). Existen diferentes tipos de lenguajes. Por ahora, nos basta distinguir entre: Lenguajes naturales: son aquellos que las personas utilizan para comunicarse entre ellas, como el castellano, el ingls o el chino. Son muy complejos, tienen una gran cantidad de reglas y sin embargo presentan situaciones de ambigedad que los hablantes resuelven recurriendo al contexto tanto de espacio como de tiempo. Lenguajes formales: son lenguajes artificiales, diseados para lograr una comunicacin (unidireccional) entre personas y mquinas. Estas ltimas deben comprender los mensajes y ejecutarlos, por lo que las reglas de los lenguajes formales deben estar muy bien definidas y no pueden dar lugar a ambigedades.

Jacqueline Khler C. - USACH

Compiladores En ciencias de la computacin, el estudio de los lenguajes formales se limita a sus caractersticas intrnsecas e internas, sin tener en cuenta su significado ni su uso como herramienta de comunicacin, puesto que esto ltimo corresponde a una atribucin asignada por un observador externo al lenguaje. En este curso daremos respuesta a una pregunta que se desprende de todo lo expuesto anteriormente: cmo aplicar la teora de lenguajes formales para poder comunicarnos con las mquinas?

1.1 QU ES UN PROGRAMA?
En cursos anteriores siempre hemos pensado en un programa como una secuencia estructurada de sentencias escritas en algn lenguaje y que son ejecutadas por un computador para resolver un problema. Sin embargo, antes de comenzar a hablar de compiladores tenemos que definir qu es un programa desde otra perspectiva: como una cadena de caracteres construida sobre un alfabeto determinado (habitualmente ASCII o un subconjunto de l) y almacenada en un archivo. En otras palabras, un programa es una palabra del lenguaje, generalmente un lenguaje libre de contexto. Sin embargo, tambin podemos considerar que esta gran palabra contiene subsecuencias que, a su vez, son palabras pertenecientes a lenguajes ms sencillos que conforman las denominadas categoras lxicas. Cada una de estas categoras puede ser definida por un lenguaje ms sencillo (casi siempre un lenguaje regular) y da origen a un tipo especfico de palabras. Entre los ms habituales podemos encontrar, por ejemplo: Identificadores predefinidos para elementos propios del lenguaje (palabras reservadas, operadores, etc.). Identificadores definidos por el programador. Representacin literal de valores. Delimitadores. Comentarios.

1.2 DEFINICIN DE COMPILADOR


Para comenzar con la definicin de compilador, lo primero que hay que tener en cuenta es que un compilador es un programa. Su funcin consiste en leer otro programa, denominado programa fuente y escrito en un lenguaje llamado lenguaje fuente, para posteriormente traducirlo a un programa equivalente (programa objeto) escrito en un segundo lenguaje denominado lenguaje objeto (ver figura 1.1). Adems, antes de efectuar la traduccin comprueba que el programa fuente est correctamente escrito e informa al usuario en caso de encontrar errores. Existe una gran diversidad de lenguajes fuente, que van desde los ms populares (Java, C/C++, Visual Basic, etc.) hasta aquellos altamente especializados. De igual manera existen muchos lenguajes objeto, que abarcan desde el lenguaje de mquina de cualquier computador hasta otros lenguajes de programacin. No es difcil imaginar que la tarea de convertir un programa fuente escrito en un lenguaje de alto nivel en un programa objeto cuyo lenguaje podra ser el de la mquina en que ser ejecutado no es sencilla. Jacqueline Khler C. - USACH

Compiladores En consecuencia, tampoco es sencillo escribir un buen compilador. Para ello se requieren conocimientos en diferentes reas: lenguajes de programacin, arquitectura de computadores, teora de lenguajes, algoritmos e ingeniera de software. En este curso solo nos centraremos en la teora de lenguajes. Pero, para qu hacer esta traduccin? Aho et al. afirman que: el 90 % del tiempo de ejecucin de un programa se encuentra en el 10 % del mismo. En consecuencia, una de las grandes ventajas de usar un compilador es la optimizacin de cdigo que realizan. Dichas optimizaciones no solo se traducen en un menor tiempo de ejecucin, sino tambin en una reduccin del consumo de energa por parte del procesador y en una menor cantidad de accesos a memoria.

FIGURA 1.1: Representacin bsica de un compilador.

1.3 COMPILADORES E INTRPRETES


Ahora que ya tenemos una primera definicin para el trmino compilador es posible destacar la gran diferencia que existe entre un compilador y un intrprete. Se seal anteriormente que el primero genera un programa equivalente al programa fuente, pero escrito en el lenguaje objeto. El intrprete, en cambio, toma una sentencia del programa fuente, la traduce al lenguaje objeto y la ejecuta, pero no guarda una copia de la traduccin. As pues, podemos asociar la idea de la compilacin a la traduccin de un texto escrito, en la que generamos un documento equivalente escrito en otro idioma. En cambio, la nocin de interpretacin se asemeja a la traduccin simultnea que se realiza en conferencias o eventos deportivos, en que una persona transmite un mensaje en forma verbal y otra emite un mensaje equivalente en otra lengua. La tabla 1.1 muestra las ventajas que ofrecen la compilacin y la interpretacin, respectivamente.

1.4 CONTEXTO EN QUE SE SITA EL COMPILADOR


Muchas veces no basta con un compilador para obtener un programa objeto ejecutable. Por ejemplo, un programa fuente podra estar dividido en mdulos almacenados en diferentes archivos. La tarea de reunir los diversos fragmentos del programa fuente a menudo se confa a un programa distinto, llamado preprocesador, el cual puede tambin expandir abreviaturas, llamadas macros y a proposiciones del lenguaje fuente. El programa objeto creado por el compilador puede requerir procesamiento adicional para poder ser ejecutado. La figura 1.2 muestra, a modo de ejemplo, un compilador que crea cdigo en lenguaje Jacqueline Khler C. - USACH

10

Compiladores ensamblador, el cual debe ser traducido a cdigo de mquina por un ensamblador y luego enlazado a algunas rutinas de biblioteca para finalmente generar el cdigo que podr ser ejecutado en la mquina. Ventajas de compilar Se compila una vez y se ejecuta muchas veces. La ejecucin del programa objeto es mucho ms rpida que la interpretacin del programa fuente. El compilador tiene una visin ms detallada del programa, por lo que la informacin de los mensajes de error es ms detallada. Ventajas de interpretar Un intrprete requiere menos memoria que un compilador. Permite mayor interaccin con el cdigo en tiempo de desarrollo. En algunos lenguajes incluso es posible aadir cdigo mientas otra parte del cdigo se est ejecutando (Smalltalk, Prolog, LISP). Al usar lenguajes interpretados se tiene una mayor portabilidad.

TABLA 1.1: Ventajas de compilar e interpretar. El lenguaje ensamblador es una versin mnemotcnica del lenguaje de mquina, donde se usan nombres para las operaciones en lugar de cdigos binarios y tambin se usan nombres para las direcciones de memoria. Adems, un ensamblador es un compilador cuyo lenguaje fuente es el lenguaje ensamblador.

FIGURA 1.2: Sistema para procesamiento de un lenguaje.

Jacqueline Khler C. - USACH

11

Compiladores Algunos elementos nuevos que aparecen en la figura 1.2 son: 1. Cdigo de mquina relocalizable: es cdigo que puede cargarse empezando en cualquier posicin L de la memoria. Es decir, si se suma L a todas las direcciones del cdigo, entonces todas las referencias sern correctas. 2. Proceso de carga: consiste en tomar el cdigo de mquina relocalizable, modificar las direcciones de memoria y colocar las instrucciones y los datos modificados en las posiciones apropiadas de la memoria. 3. Editor de enlace: permite formar un nico programa a partir de varios archivos de cdigo de mquina relocalizable.

1.5 FASES DE UN COMPILADOR


Antes de comenzar la descripcin de las diferentes etapas del proceso de compilacin, puede resultar esclarecedor el modelo que separa estas etapas en dos grandes bloques: anlisis y sntesis (figura 1.3).

FIGURA 1.3: Modelo de anlisis y sntesis de la compilacin. El anlisis separa al programa fuente en los diversos componentes lxicos (tokens) que lo componen y luego crea una representacin intermedia. Posteriormente la sntesis se sirve de la representacin intermedia generada durante el anlisis para construir el programa objeto. La figura 1.4 muestra las distintas fases de un compilador, donde las primeras cuatro etapas que recorre el programa fuente constituyen el anlisis y las siguientes, la sntesis. A continuacin se describen brevemente las diferentes etapas: 1. Anlisis lxico: lee, de izquierda a derecha, la cadena de caracteres que constituye el programa fuente y la agrupa en componentes lxicos, que son secuencias de caracteres que tienen un significado colectivo. Normalmente durante esta etapa se eliminan los espacios en blanco y los comentarios. 2. Anlisis sintctico: agrupa los componentes lxicos del programa fuente en frases gramaticales que el compilador utiliza para sintetizar la salida. 3. Anlisis semntico: se revisan las frases gramaticales que conforman el programa para detectar errores semnticos, es decir, que guarden relacin con el significado de los elementos, y rene la Jacqueline Khler C. - USACH

12

Compiladores informacin sobre los tipos para la posterior fase de generacin de cdigo. El anlisis semntico utiliza la estructura jerrquica determinada por la fase de anlisis sintctico para identificar los operadores y operandos de expresiones y proposiciones. Un componente importante del anlisis semntico es la verificacin de tipos, donde el compilador verifica que cada operador tenga operandos permitidos por la especificacin del lenguaje fuente. Sin embargo, la especificacin del lenguaje puede permitir ciertas conversiones.

FIGURA 1.4: Fases de un compilador. 4. Generacin de cdigo intermedio: algunos compiladores generan una representacin intermedia explcita del programa fuente. sta puede ser considerada como un programa para una mquina abstracta. 5. Optimizacin de cdigo: esta etapa intenta mejorar el cdigo intermedio, de modo que resulte un cdigo de mquina ms rpido de ejecutar. 6. Generacin de cdigo: corresponde a la fase final de un compilador y genera cdigo objeto, que habitualmente consiste en cdigo de mquina relocalizable o cdigo ensamblador. Aqu se seleccionan las posiciones de memoria para cada una de las variables empleadas por el programa. Un aspecto decisivo de esta etapa es la asignacin de variables a registros. 7. Administrador de la tabla de smbolos: el registro de los identificadores utilizados en el programa fuente y la recoleccin de informacin relativa a los diferentes atributos de cada identificador conforman una de las funciones esenciales de un compilador. Dichos atributos pueden proporcionar informacin sobre la memoria asignada a un identificador, su tipo, su mbito y, en el caso de nombres de procedimientos, datos como el nmero y el tipo de sus argumentos, el mtodo con que se debe pasar cada argumento y, en caso de existir, el tipo que se retorna. Jacqueline Khler C. - USACH

13

Compiladores 8. Tabla de smbolos: es una estructura de datos que contiene un registro por cada identificador, donde los campos son llenados con los atributos de este ltimo. 9. Manejador de errores: cada una de las fases descritas puede encontrar errores. No obstante, cada fase debe tratar adecuadamente el error detectado para poder continuar la compilacin y as permitir la deteccin de ms errores en el programa fuente.

1.6 CLASIFICACIN DE LOS COMPILADORES


Existen diferentes criterios para clasificar los compiladores, segn la forma en que fueron construidos o la tarea que realizan: de una pasada, de mltiples pasadas, de depuracin, de optimizacin, etc. A pesar de stas diferencias, todos los compiladores llevan a cabo, en esencia, las mismas tareas bsicas. Ahora bien, existen algunos tipos de compiladores que pueden distinguirse de los dems: Ensamblador: compilador de estructura sencilla cuyo lenguaje fuente es el lenguaje ensamblador. Compilador cruzado: genera cdigo en lenguaje objeto para una mquina distinta de la que se est usando para compilar (por ejemplo, un compilador de pascal escrito en C++, que funcione en LINUX y que genere cdigo para MS-DOS). Es la nica forma de construir un compilador para un nuevo procesador. Compilador con montador: compila diferentes mdulos en forma independiente y despus es capaz de enlazarlos. Autocompilador: compilador escrito en el mismo lenguaje que va a compilar. No puede ser usado la primera vez, pero sirve entre otras cosas para hacer ampliaciones al lenguaje. Metacompilador: es un compilador de compiladores. Recibe como entrada las especificaciones del lenguaje para el que se desea construir un compilador y genera como salida el compilador para dicho lenguaje. Descompilador: recibe como entrada cdigo escrito en lenguaje de mquina y lo traduce a un lenguaje de alto nivel (no sirven en caso de existir optimizacin de cdigo en el programa escrito en lenguaje de mquina). En otras palabras, realiza el proceso inverso a la compilacin. No obstante, hasta ahora no se han construido buenos descompiladores (salvo desensambladores).

1.7 HERRAMIENTAS TILES


Existen diversas herramientas que pueden ser de ayuda al momento de construir un compilador. Dos de ellas, sin embargo, destacan por sobre las dems: Generadores de analizadores lxicos: generan analizadores lxicos en forma automtica, por lo general a partir de una especificacin basada en expresiones regulares. La organizacin bsica del analizador lxico resultante es en realidad un autmata finito. Ejemplo: Lex. Generadores de analizadores sintcticos: generan analizadores sintcticos, normalmente a partir de una entrada fundamentada en una gramtica independiente del contexto. Ejemplo: Yacc.

Jacqueline Khler C. - USACH

14

Compiladores

2 ANLISIS LXICO
El anlisis lxico corresponde a la primera etapa del proceso de compilacin. Puede pensarse como una correccin ortogrfica del programa fuente.

2.1 FUNCIN DEL ANALIZADOR LXICO


El analizador lxico es la primera etapa de un compilador y forma parte de la fase de anlisis. Su principal funcin consiste en leer los caracteres de entrada y elaborar como salida una secuencia de componentes lxicos que ser posteriormente utilizada para efectuar el anlisis sintctico. Generalmente el analizador lxico es una subrutina del analizador sintctico y su funcionamiento es el siguiente (ver figura 2.1): el analizador sintctico solicita el siguiente componente lxico. En consecuencia, el analizador lxico lee caracteres del programa fuente hasta identificar un nuevo componente lxico, que entregar al analizador sintctico para as dar cumplimiento a su solicitud. La figura 2.1 hace mencin de la tabla de smbolos, encargada de almacenar informacin relativa a cada uno de los nombres de variables y procedimientos declarados en el programa fuente. Ser estudiada con mayor profundidad en captulos posteriores.

FIGURA 2.1: Interaccin entre el analizador lxico y el analizador sintctico. Como el analizador lxico es la parte del compilador que lee el texto fuente, tambin puede realizar ciertas funciones secundarias en la interfaz del usuario: Eliminar los comentarios del programa fuente. Eliminar espacios en blanco, tabulaciones y saltos de lnea. Relacionar los mensajes de error entregados por el compilador con el programa fuente. Por ejemplo, el analizador lxico suele contar los saltos de lnea detectados, por lo que puede asociar los errores encontrados al nmero de lnea del programa fuente correspondiente. Incluso existen compiladores en que el analizador lxico se encarga de hacer una copia del programa fuente donde se marcan los mensajes de error.

Jacqueline Khler C. - USACH

15

Compiladores Se seal anteriormente que el analizador lxico suele ser una subrutina del analizador sintctico. Existen varias razones para separar estos dos tipos de anlisis en etapas diferentes: Quiz la ms importante de las consideraciones sea la de lograr un diseo sencillo. Separar el anlisis lxico del anlisis sintctico a menudo permite simplificar una u otra de estas etapas. Por ejemplo, un analizador sintctico que incluya las convenciones de espacios en blanco y comentarios resulta mucho ms complejo que otro que solo deba comprobar que stos ya hayan sido eliminados por el analizador lxico. En el caso de la creacin de un lenguaje nuevo, la separacin de las convenciones lxicas de las sintcticas puede traducirse en un diseo ms claro del lenguaje. Mejorar la eficiencia del compilador. Un analizador lxico independiente permite construir un procesador especializado y potencialmente ms eficiente para esta funcin. Gran parte del tiempo se consume en leer el programa fuente y dividirlo en componentes lxicos. Con tcnicas especializadas de manejo de buffers para la lectura de caracteres de entrada y procesamiento de componentes lxicos se puede mejorar significativamente el rendimiento de un compilador. Mejorar la portabilidad del compilador. Las peculiaridades del alfabeto de entrada y otras anomalas propias de los dispositivos pueden limitarse al analizador lxico. Por ejemplo, la representacin de smbolos especiales o no estndar (como en Pascal) puede ser aislada en esta etapa.

2.2 COMPONENTES LXICOS, PATRONES Y LEXEMAS


Un componente lxico (token) es una secuencia de caracteres que puede ser tratada como una unidad en la gramtica del lenguaje fuente. Un lenguaje clasifica los componentes lxicos en un conjunto finito de tipos. En la mayora de los lenguajes de programacin se consideran como componentes lxicos las siguientes construcciones: Palabras clave. Operadores. Identificadores. Constantes. Cadenas literales. Signos de puntuacin. La tabla 2.1 muestra ejemplos para algunos de los tipos de componentes lxicos tpicos de un lenguaje de programacin. Puede verse en estos ejemplos que en muchos casos existe un conjunto de cadenas (strings) para las cuales se produce un mismo componente lxico. Dicho conjunto de cadenas se describe mediante una regla llamada patrn, para cuya descripcin precisa se utiliza la notacin de expresiones regulares. Se dice que el patrn concuerda con cada cadena del conjunto. Una cadena de caracteres en el programa fuente con la que concuerde el patrn correspondiente a un componente lxico dado recibe el nombre de lexema. La tabla 2.2 muestra ejemplos de componentes lxicos, lexemas y patrones.

Jacqueline Khler C. - USACH

16

Compiladores Tipo ID NUM REAL IF COMMA NOTEQ LPAREN RPAREN


foo 73 66.1 If , != ( ) 0 .5 n14 00 10.

Ejemplo
sum 515 1e67 082 5.5e-10

TABLA 2.1: Ejemplos de cadenas de caracteres y componentes lxicos asociados. En muchos lenguajes de programacin existen ciertas cadenas de caracteres que son reservadas, es decir, tienen un significado predefinido que no puede ser modificado por el usuario. Esto suele ser as para las palabras clave (for, char, break, etc.). Si las palabras clave no son reservadas, corresponde al analizador lxico la tarea de distinguir estas palabras de los identificadores, lo que puede resultar bastante complejo. Por ejemplo, en PL/1 es permitida la siguiente sentencia:
IF THEN THEN THEN = ELSE; ELSE ELSE = THEN;

Componente lxico const if relacin id num literal

Lexemas de ejemplo
const if <, <=, =, <>, >, >= pi, cuenta, D2 3.1416, 0, 6.02E23 vaciado de memoria

Descripcin informal del patrn const if < o <= o = o <> o >= o > Letra seguida de letras y nmeros Cualquier constante numrica Cualquier carcter entre y , excepto .

TABLA 2.2: Componentes lxicos, lexemas y patrones.

2.3 ERRORES LXICOS


Son pocos los errores que se pueden detectar directamente durante el anlisis lxico, puesto que el analizador lxico tiene una visin muy restringida del programa fuente. Por ejemplo, si en el programa fuente aparece:
fi(a == f(x))

El analizador lxico no puede distinguir si es un error de escritura de la palabra clave if o un identificador de funcin que no ha sido previamente declarado. De hecho, al ser un identificador vlido

Jacqueline Khler C. - USACH

17

Compiladores debe retornar el componente lxico correspondiente. La deteccin de este error depender, en consecuencia, de otra fase del compilador. Si surge una situacin en la que el analizador lxico no puede continuar porque ninguno de los patrones concuerda con el prefijo de la entrada restante se pueden efectuar diferentes acciones de recuperacin de error: Borrar caracteres hasta encontrar algn componente lxico bien formado. Insertar caracteres faltantes. Reemplazar un carcter incorrecto por otro correcto. Intercambiar caracteres adyacentes.

2.4 IMPLEMENTACIN DE ANALIZADORES LXICOS


2.4.1 MTODOS GENERALES Existen tres mtodos generales para implementar un analizador lxico (lexer): Utilizar un generador de analizadores lxicos para crear el analizador lxico deseado a partir de una especificacin basada en expresiones regulares. En este caso, el generador proporciona rutinas para leer la entrada y manejarla con buffers. Escribir el analizador lxico en un lenguaje convencional de programacin de sistemas, utilizando las posibilidades de entrada y salida de este lenguaje para leer la entrada. Escribir el analizador lxico en lenguaje ensamblador y manejar explcitamente la lectura de la entrada. Las tres opciones han sido presentadas en orden de dificultad creciente para quien deba implementarlas. Lamentablemente, muchas veces los enfoques ms difciles de implementar dan como resultado analizadores lxicos ms rpidos. Independientemente del mtodo empleado, las herramientas tericas tiles para la construccin de analizadores lxicos son las expresiones regulares, para expresar los componentes lxicos en forma sencilla, y los autmatas finitos, para la implementacin de analizadores lxicos.

2.4.2 CONSTRUCCIN DE UN ANALIZADOR LXICO Muchos generadores de analizadores lxicos trabajan tomando como base las expresiones regulares correspondientes a los patrones de los diversos componentes lxicos de un lenguaje. Lo primero que debe hacerse es ordenar los diferentes patrones (representados como expresiones regulares) de acuerdo a su prioridad. Esto debe hacerse cuidadosamente, puesto que si el analizador lxico resultante detecta que un prefijo de la entrada se ajusta a dos o ms patrones diferentes, asignar a ese lexema el componente lxico de mayor prioridad. Una vez asignadas las prioridades, se construyen los AFND- para cada patrn (una buena idea es usar el mtodo de construccin de Thompson), donde el estado final de cada autmata tendr asociada una Jacqueline Khler C. - USACH

18

Compiladores accin. Esta accin corresponde a lo que debe hacerse al momento de detectar un lexema que concuerda con el patrn asociado al AFND-. A continuacin se unen los autmatas obtenidos para dar lugar a un nico AFND-. Para efectuar esta unin basta con crear un estado inicial con transiciones vacas hacia cada uno de los estados iniciales de los AFND- de cada patrn. Los estados finales conservan sus acciones asociadas. Cuando ya se ha construido el AFND- que reconoce todos los componentes lxicos del lenguaje, se debe proceder a convertirlo en un AFD mnimo. Al momento de minimizar se debe recordar que estados finales que tengan diferentes acciones asociadas no pueden ser equivalentes. El AFD mnimo obtenido corresponde al analizador lxico para el lenguaje especificado. Ejemplo 2.1: Construir el lxico mnimo que reconoce los patrones dados en la tabla 2.3 y les asigna el componente lxico correspondiente.

TABLA 2.3: Patrones y componentes lxicos para el ejemplo 2.1. Se debe comenzar por ordenar jerrquicamente los patrones, segn su precedencia. Se asigna mayor precedencia a aquellos patrones que representen conjuntos ms pequeos de palabras. Esto es de vital importancia porque, en el caso de que dos patrones reconozcan un mismo lexema, deber ejecutarse la accin de mayor precedencia. En este caso, se tiene que el patrn solo coincide con el lexema a. Similarmente, el patrn solo coincide con el lexema abb. El patrn , en cambio, coincide con un conjunto ms amplio de lexemas: todos aquellos que tengan cero o ms a seguidas de una o ms b. En este caso, podemos observar que el lexema abb coincide con dos patrones: y . Como el patrn es ms limitado, debe tener una precedencia ms alta que . El patrn , en cambio, no tiene conflictos con los dems patrones, por lo que no importa qu lugar ocupe en la precedencia. A continuacin, necesitamos construir los AFND- reconocedores para los patrones dados, siguiendo el mtodo de Thompson (ver anexo A). Luego unimos estos tres AFND- con un nuevo estado inicial que tenga transiciones vacas hacia los estados iniciales de nuestros tres autmatas. El AFND- resultante se muestra en la figura 2.2. Es importante sealar que el reconocimiento de un lexema vlido va a estar dado por un estado final, y que cada estado final corresponde a un patrn diferente. As, a cada estado final se le asocia el componente lxico para el patrn correspondiente. Como reconoce lexemas que coincidan con el patrn , se le asocia el componente lxico . De manera similar, a se le asocia el componente lxico y a , el componente lxico .

Jacqueline Khler C. - USACH

19

Compiladores , , , , , con: , , , , , , , , , , , , , , , , ,

FIGURA 2.2: AFND- para los patrones del ejemplo 2.1. Como no es posible implementar un AFND-, necesitamos encontrar un AFD equivalente (ver anexo A). No obstante, en la construccin de analizadores lxicos este proceso tiene una diferencia con respecto al mtodo formal que se utiliza en teora de autmatas. Segn esta ltima, en caso de no existir transiciones desde uno de los nuevos estados del AFD para algn smbolo, es decir, si la clausura es el conjunto vaco, se crea un nuevo estado para el AFD del que es imposible salir una vez que ha sido alcanzado. En la construccin de analizadores lxicos, sin embargo, resulta absurda la inclusin de este estado, puesto que impedira revisar el resto del programa fuente. En consecuencia, lo que se hace es construir un AFD que no tenga la transicin en cuestin (por ejemplo en la figura 2.3 se ve que no existe transicin desde el estado con el smbolo ). Si se alcanza un estado que no tiene transicin saliente con el prximo smbolo de la entrada, al implementar el analizador lxico se notifica un error lxico. La figura 2.3 muestra el AFD resultante. , , , , , , , , , , , , , , , , ,

Jacqueline Khler C. - USACH

20

Compiladores , , , , , , , , , , , , , , , , , , , , , , , , , ,

Los estados finales del AFD tambin llevan asociada una accin. En caso de que uno de los estrados finales comprenda ms de una accin, se escoge aquella de mayor prioridad. Por ejemplo, el estado del AFD comprende los estados finales correspondientes a las acciones y , pero se le asocia solamente por tener una precedencia ms alta.

FIGURA 2.3: AFD equivalente al AFND- de la figura 2.2. Es frecuente que el mtodo empleado para obtener el AFD equivalente entregue estados redundantes, por lo que se requiere minimizar el AFD obtenido. Si se considera el mtodo de las particiones para la minimizacin (ver anexo A), la teora de autmatas indica que la particin inicial viene dada por la separacin de estados finales y no finales. No obstante, en el caso de los analizadores lxicos los estados finales tienen asociadas diferentes acciones con diferentes prioridades, por lo que es necesario particionar tambin de acuerdo a la accin a Jacqueline Khler C. - USACH

21

Compiladores realizar. As, aquellos estados finales en que se ejecute una accin no sern equivalentes a los estados finales que ejecuten una accin diferente. La figura 2.4 muestra el AFD mnimo. , , , ,

FIGURA 2.4: AFD mnimo equivalente al AFND- de la figura 2.2.

2.5 EJERCICIOS
1. Dados los patrones de la tabla 2.4 y sus componentes lxicos asociados: a. Asigne las prioridades a considerar para construir el analizador lxico correspondiente. b. Indique, para cada uno de los siguientes lexemas, el componente lxico que debiera asociarle el analizador lxico. En caso de no corresponder con ninguno, seale la existencia de un error lxico. . . . . .

TABLA 2.4: Patrones y componentes lxicos para el ejercicio 1.

Jacqueline Khler C. - USACH

22

Compiladores 2. Construya el analizador lxico mnimo para los siguientes patrones (El smbolo entre parntesis asociado a cada patrn corresponde al componente lxico asociado). ( ). ( ). ( ). 3. Construya el analizador lxico mnimo para los siguientes patrones (El smbolo entre parntesis asociado a cada patrn corresponde al componente lxico asociado). ( ). ( ). ( ).

Jacqueline Khler C. - USACH

23

Compiladores

3. ANLISIS SINTCTICO
As como en la construccin de analizadores lxicos trabajamos con lenguajes regulares, los analizadores sintcticos se construyen sobre la base de una gramtica libre de contexto o GLC (ver anexo A). Aqu la gramtica corresponde a la especificacin del lenguaje de programacin, y se usan autmatas apiladores (ver anexo A) como reconocedores de programas sintcticamente correctos.

3.1 CONCEPTOS PREVIOS


Antes de comenzar a construir analizadores sintcticos es necesario conocer algunos conceptos y algunas transformaciones que en ocasiones es necesario realizar sobre la gramtica.

3.1.1 RECURSIVIDAD POR LA IZQUIERDA Una gramtica es recursiva por la izquierda si tiene un no terminal tal que existe una derivacin para alguna cadena . Para eliminar las recursiones directas, se agrupan las producciones del no terminal A de la siguiente manera: | | | | | | | Donde ninguna de las producciones comienza por el no terminal . Luego se sustituyen las producciones de por: | | | | | | Muchas veces no basta con eliminar las recursiones directas. Pueden existir recursiones por la izquierda que tarden ms en aparecer. Para eliminar este tipo de recursiones se puede usar el algoritmo que se muestra a continuacin, siempre que la gramtica no contenga ciclos (derivaciones de la forma )ni producciones . Ntese, sin embargo, que la gramtica resultante s puede contener producciones : 1. Ordenar los no terminales en un cierto orden fijo (cualquiera) , , , . 2. Para ( 1 hasta ): Para ( 1 hasta 1): Si las producciones de son | | , reemplazar cada produccin por | | . Eliminar recursividad directa por la izquierda para . Ejemplo 3.1: Elimine toda recursin por la izquierda para , N, , , donde: , , , . N , . P | , | . .

Jacqueline Khler C. - USACH

24

Compiladores Consideremos los no terminales en el orden , . Para cada no terminal, siguiendo el orden asignado a ellos, ejecutar los siguientes pasos: Paso 1 (no aplica para el primer smbolo): en cada produccin del no terminal actual que comience por un no terminal ya revisado , crear nuevas producciones para en que se reemplace con sus respectivas producciones. Paso 2: eliminar la recursin directa por la izquierda para el no terminal actual. | : como es el primer no terminal, solo se debe eliminar la recursin directa. En este caso no existe recursin. | : en primer lugar, se busca cada produccin de que comience con algn no terminal ya revisado (en este caso ) y se crean nuevas producciones para en que sea sustituido por sus producciones. As, se obtiene | | . | | : una vez realizado el primer paso, se debe eliminar la recursin directa por la izquierda, con lo que se obtiene | , | . As, la gramtica sin recursin por la izquierda es , N, , , donde: , , , . N , , . P | , | , | . . 3.1.2 FACTORIZACIN POR LA IZQUIERDA La factorizacin por la izquierda se ocupa de agrupar producciones semejantes, descomponindolas en un fragmento comn y otro diferente. Esto es de utilidad al momento de construir algunos analizadores sintcticos, pues sirve en aquellos casos en que no est claro cul de dos o ms producciones alternativas utilizar para ampliar un no terminal . As, las producciones de pueden reescribirse para postergar la decisin hasta haber visto lo suficiente de la entrada como para tomar la decisin correcta. Tmense por ejemplo las producciones A | . Como puede ser difcil saber si escoger o , se pueden crear las siguientes producciones para postergar la decisin: A B y B | . El algoritmo para factorizar completamente una gramtica es el siguiente: 1. Repetir: a. Para cada no terminal A, encontrar el prefijo ms largo comn a dos o ms de sus producciones. b. Si , sustituir todas las producciones de A, A | | | | (donde representa a todas las producciones de A que no comienzan por ) por A B | y B | | | , donde B es un nuevo no terminal. Mientras haya dos producciones para un no terminal con un prefijo comn.

Jacqueline Khler C. - USACH

25

Compiladores Ejemplo 3.2: Factorice por la izquierda la gramtica , N, P, S: a, b N A, B, C, D P | | | | , | | | | , | | | , | | | | | | | | | | S A. En el caso de , podemos comenzar por las producciones de la forma , de donde se obtiene: | | | | Ahora podemos factorizar aquellas producciones de la forma , quedando: | | | Y terminar la factorizacin de este no terminal con las producciones de la forma , de donde se obtiene: | | Ntese que no se cre un no terminal nuevo porque ya exista uno con exactamente las mismas producciones. Para el no terminal , podemos comenzar con las producciones de la forma : | | | | Continuar con las de la forma : | | | Y terminar con las de la forma : | Para el no terminal , podemos comenzar con las producciones de la forma : | | | Y terminar con las de la forma : | Para el no terminal , podemos comenzar con las producciones de la forma : Jacqueline Khler C. - USACH

26

Compiladores | | | | | | | | | Continuar con aquellas de la forma : | | | | | | | Seguir con aquellas de la forma : | | | | | | Seguir con aquellas de la forma : | | | | | | Y ahora tomar las de forma : | | | | | Para seguir con aquellas de forma : | | | Y luego las de forma : | | Para terminar con aquellas de la forma : | As, la gramtica factorizada por la izquierda es , N, P, S: a, b N A, B, C, D, E, F, G, H, I, J, K P | , | , | , | , | , | , | , | , | , | , | S A.

Jacqueline Khler C. - USACH

27

Compiladores 3.1.3 CONJUNTOS ANULABLE, PRIMERO Y SIGUIENTE Existen dos funciones asociadas a una gramtica que facilitan la construccin de analizadores sintcticos: Primero y Siguiente. Ambas aportan informacin relativa al contexto de un smbolo dado.

3.1.3.1 Conjunto Anulable Antes de definir las dos funciones ya mencionadas es necesario introducir el conjunto anulable, , definido como el conjunto de todos aquellos no terminales que pueden, en uno o ms pasos, derivar a . Es decir, : . 3.1.3.2 Conjunto Primero Dada una cadena de smbolos gramaticales , se define su Conjunto Primero, , como el conjunto de terminales que pueden iniciar las secuencias derivadas a partir de . Sean ; , , , , , , N y N . Para calcular para todos los smbolos (terminales y no terminales) de la gramtica, se deben aplicar las siguientes reglas: 1. . 2. . 3. Si se tienen las producciones | | | , entonces . 4. Si se tiene la produccin , entonces: . Mientras , . 3.1.3.3 Conjunto Siguiente Dado un no terminal , se define su Conjunto Siguiente, , como el conjunto de terminales que pueden aparecer inmediatamente a la derecha de en alguna forma de frase, es decir, el conjunto de terminales tales que exista una derivacin de la forma para algn y algn . Adicionalmente, si es posible que sea el smbolo situado ms a la derecha en alguna forma de frase, o sea, no puede venir nada ms a continuacin de , entonces $ , donde $ indica el trmino de la secuencia (puede pensarse en $ de una manera similar al carcter especial de fin de archivo, ) . Sea el smbolo inicial de la gramtica; sean , cadenas de smbolos gramaticales (terminales y no terminales) y sean , . El clculo de requiere de la aplicacin de las siguientes reglas: 1. Agregar $ a . 2. Si se tiene la produccin , con , entonces . 3. Si existe la produccin , entonces . 4. Si se tiene la produccin , con , entonces .

Jacqueline Khler C. - USACH

28

Compiladores Note que la regla 4 es una combinacin de las dos reglas anteriores. En ella, podra no anularse, en cuyo caso debemos considerar la regla 2. Pero tambin puede darse que se anule, en cuyo caso se cumple la regla 3. En consecuencia, debemos considerar todas las posibilidades. Ejemplo 3.3: Determine el Conjunto Anulable y los conjuntos y , , para , N, , , donde: ,, , , . N , , , , . P , , | , | , | . . El conjunto anulable est conformado por todos aquellos terminales que, en una o ms derivaciones, pueden generar la secuencia vaca. As, el conjunto anulable para est dado por , . pues todas sus producciones contienen terminales. pues su nica produccin comienza por , que tampoco lo es. Lo mismo ocurre para . , , , $ $ $, $, $, $, $, , $, , $, , $, , $, , ,

3.2 DESCRIPCIN GENERAL DEL ANLISIS SINTCTICO


Segn el diccionario de la Real Academia Espaola (2011), la sintaxis es la parte de la gramtica que ensea a coordinar y unir las palabras para formar las oraciones y expresar conceptos. En una segunda acepcin, define sintaxis como un conjunto de reglas que definen las secuencias correctas de los elementos de un lenguaje de programacin. Esta ltima definicin implica que todo lenguaje de programacin tiene reglas que prescriben la estructura de programas bien formados. Generalmente, un programa est formado por bloques. A su vez, los bloques estn conformados por proposiciones, las cuales se forman a partir de expresiones, y estas ltimas se forman con componentes lxicos: Programa bloques proposiciones expresiones componentes lxicos Jacqueline Khler C. - USACH

29

Compiladores Anteriormente vimos que el analizador lxico y el analizador sintctico trabajan en conjunto y que este ltimo obtiene como entrada una cadena de componentes lxicos entregada por el primero (ver figura 3.1). En este punto el analizador sintctico comprueba que la cadena pueda ser generada por la gramtica del lenguaje fuente. Tambin informa de cualquier error de sintaxis de manera inteligible, y debera recuperarse de los errores que ocurren frecuentemente para poder continuar procesando el resto de su entrada. Como resultado del anlisis sintctico se obtiene un rbol de derivacin que genera la secuencia de componentes lxicos a partir del smbolo inicial de la gramtica. Los analizadores sintcticos empleados generalmente en los compiladores se clasifican como descendentes o ascendentes. Como sus nombres indican, los analizadores sintcticos descendentes construyen el rbol desde arriba (la raz, dada por el smbolo inicial de la gramtica) hacia abajo (las hojas, consistentes en los componentes lxicos), mientras que los analizadores sintcticos ascendentes comienzan en las hojas y suben hacia la raz. En ambos casos, se examina la entrada al analizador sintctico de izquierda a derecha, un smbolo a la vez. Los mtodos descendentes y ascendentes ms eficientes trabajan slo con subclases de las gramticas libres de contexto, pero varias de estas subclases, como las gramticas LL y LR, son lo suficientemente expresivas para describir la mayora de las construcciones sintcticas de los lenguajes de programacin. Los analizadores sintcticos implementados a mano a menudo trabajan con gramticas LL(1). Los analizadores sintcticos para la clase ms grande de gramticas LR se construyen normalmente con herramientas automatizadas.

FIGURA 3.1: Posicin del analizador sintctico en el modelo de un compilador. Como ya se seal, la salida del analizador sintctico es una representacin del rbol de anlisis sintctico para la cadena de componentes lxicos producida por el analizador lxico. En la prctica, no obstante, hay varias tareas que se pueden realizar durante el anlisis sintctico, como recoger informacin sobre distintos componentes lxicos en una tabla de smbolos, realizar la verificacin de tipo y otras clases de anlisis semntico, y generar cdigo intermedio. No obstante, estas actividades se vern ms adelante.

3.3 ALGUNOS ASPECTOS DEL MANEJO DE ERRORES SINTCTICOS


Si un compilador tuviera que procesar slo programas correctos, su diseo e implementacin se simplificaran mucho. Pero los programadores a menudo escriben programas incorrectos y un buen Jacqueline Khler C. - USACH

30

Compiladores compilador debera ayudar al programador a identificar y localizar errores. Estos errores pueden ser de diversos tipos, por ejemplo: Lxicos, como escribir mal un identificador, palabra clave u operador. Sintcticos, como una expresin aritmtica con parntesis no equilibrados. Semnticos, como un operador aplicado a un operando incompatible. Lgicos, como una llamada infinitamente recursiva. A menudo, gran parte de la deteccin y recuperacin de errores en un compilador se centra en la fase de anlisis sintctico. Una razn es que muchos errores son de naturaleza sintctica o se manifiestan cuando la cadena de componentes lxicos que proviene del analizador lxico desobedece las reglas gramaticales que definen al lenguaje de programacin. El manejador de errores en un analizador sintctico tiene objetivos fciles de establecer: Informar de la presencia de errores con claridad y exactitud. Recuperarse de cada error con la suficiente rapidez como para detectar errores posteriores. No retrasar de manera significativa el procesamiento de programas correctos. Existen diferentes estrategias generales que puede emplear un analizador sintctico para recuperarse de un error: Recuperacin en modo de pnico: al descubrir un error, el analizador sintctico desecha smbolos de entrada, de uno en uno, hasta que encuentra uno perteneciente a un conjunto designado de componentes lxicos de sincronizacin. Estos componentes lxicos de sincronizacin suelen ser elementos de los conjuntos Primero y Siguiente, como por ejemplo los delimitadores (punto y coma, parntesis derecho) o el indicador de trmino de la entrada ($). Recuperacin a nivel de frase: al descubrir un error, el analizador sintctico puede realizar una correccin local de la entrada restante; es decir, puede sustituir un prefijo de sta por alguna cadena que permita continuar al analizador sintctico (suprimir un punto y coma sobrante, reemplazar una coma por un punto y coma, etc.). Producciones de error: si se tiene una buena idea de los errores comunes que pueden encontrarse, se puede aumentar la gramtica del lenguaje con producciones que generen las construcciones errneas. Entonces se usa esta gramtica aumentada con las producciones de error para construir el analizador sintctico. Si el analizador sintctico usa una produccin de error, se pueden generar diagnsticos de error apropiados para indicar la construccin errnea reconocida en la entrada. Correccin global: idealmente, sera deseable que un compilador hiciera el mnimo de cambios posibles al procesar una cadena de entrada incorrecta. Existen algoritmos para elegir una secuencia mnima de cambios para obtener una correccin global de menor costo. Por desgracia, la implementacin de estos mtodos es, en general, demasiado costosa en trminos de tiempo y espacio, as que estas tcnicas en la actualidad slo son de inters terico.

3.4 ANLISIS SINTCTICO PREDICTIVO


3.4.1 ASPECTOS GENERALES El anlisis sintctico descendente puede verse como un intento de encontrar, usando las producciones de una gramtica dada y comenzando por el smbolo inicial, una derivacin por la izquierda para una cadena dada. Se estudiar en forma particular el anlisis sintctico predictivo (tambin denominado Jacqueline Khler C. - USACH

31

Compiladores anlisis sintctico LL(1) o anlisis sintctico por descenso recursivo), que es el ms utilizado gracias a su eficiencia. En el anlisis sintctico descendente se ejecuta un conjunto de procedimientos recursivos para procesar la entrada. En el caso particular del anlisis sintctico predictivo, el smbolo de la entrada determina sin ambigedad el procedimiento seleccionado para cada no terminal. En otras palabras, para construir un analizador sintctico de este tipo es necesario conocer, dada la entrada y el no terminal a expandir, cul de todas las posibles producciones de | | | es la nica alternativa que genera una cadena que comience por . Es decir, debe ser posible detectar la alternativa apropiada con solo ver el primer smbolo que genera. De lo anterior se desprende que no cualquier gramtica libre de contexto es apropiada para el anlisis sintctico predictivo, sino que se requieren gramticas que no contengan recursiones por la izquierda y que estn factorizadas tambin por la izquierda.

3.4.2 CONSTRUCCIN Y FUNCIONAMIENTO DE ANALIZADORES SINTCTICOS LL(1) Se seal anteriormente que los analizadores sintcticos descendentes construyen el rbol de anlisis sintctico comenzando desde la raz, es decir, desde el smbolo inicial de . Para este fin se sirve de una tabla de anlisis sintctico que indica qu produccin debe emplearse al momento de reemplazar un no terminal. Para la construccin de dicha tabla se emplea el algoritmo siguiente: 1. Para cada produccin , , hacer: a. Para cada terminal en , aadir la produccin en , . b. Si , para cada terminal en , aadir la produccin en , . 3. Hacer que cada entrada no definida de la tabla corresponda a un error. Si en alguna entrada de la tabla queda ms de una produccin, entonces se dice que no es posible construir un analizador sintctico LL(1). Cabe destacar que el analizador sintctico as construido es un autmata apilador. Inicialmente, la pila contiene al smbolo inicial de la gramtica al tope, seguido del delimitador de la entrada. Cada vez que se tenga un no terminal al tope de la pila y un terminal al comienzo de la entrada, el no terminal de la pila es reemplazado por el lado derecho de la produccin contenida en , . Adicionalmente, es necesario considerar transiciones que permitan eliminar un smbolo terminal de la pila si ste coincide con el de la entrada. La aceptacin solo ocurre si tanto la pila como la entrada quedan vacas (solo con el delimitador de la entrada). Ejemplo 3.4: Construya la tabla de anlisis sintctico predictivo para , N, , , donde: ,, , , . N , , , , . P , , | , | , Jacqueline Khler C. - USACH

32

Compiladores | . . Muestre la traza y el rbol sintctico para las entradas y . Sabemos que: , . , $, $, , $, , , En primer lugar, para cada produccin no vaca, debemos aadir dicha produccin en la posicin , de la tabla para cada terminal :

TABLA 3.1: Incorporacin de las producciones no vacas al analizador sintctico LL(1). A continuacin, en aquellos casos en que , para cada terminal , aadir la produccin en , . Ntese que en este paso no solo se agregan las producciones vacas presentes explcitamente en la gramtica, sino tambin aquellas que se pueden obtener en varios pasos. Una vez incorporadas estas producciones, el analizador sintctico predictivo ya est terminado. El resultado se muestra en la tabla 3.2.

TABLA 3.2: Analizador sintctico LL(1) completo. Las figuras 3.2 y 3.3 muestran la traza para las dos entradas solicitadas con sus respectivos rboles sintcticos.

Jacqueline Khler C. - USACH

33

Compiladores

FIGURA 3.2: Traza y rbol sintctico resultante para la entrada .

FIGURA 3.3: Traza y rbol sintctico resultante para la entrada .

Jacqueline Khler C. - USACH

34

Compiladores Ejemplo 3.5: Construya la tabla de anlisis sintctico LL(1) para , N, , , donde: , , , , ,, , #, . N , , , .
| , | , | , # | |

No requiere eliminacin de recursin ni factorizacin, Por lo que podemos trabajar con ella tal como est. Comenzamos por determinar los conjuntos Anulable, Primero y Siguiente: , , #, , , #, , #, , , , #, , #, , , #, , # # #, , $ $ $, #, , $, $, , $, , , #, , , $, $, , $, , , #, , , $, , , #, , , , Ahora, para cada produccin , debemos aadir dicha produccin en la posicin , de la tabla para cada terminal , como muestra la tabla 3.3:

TABLA 3.3: Incorporacin de las producciones no vacas al analizador sintctico predictivo. A continuacin, en aquellos casos en que , para cada terminal , aadir la produccin en , . Como se puede ver en la tabla 3.4, no es posible construir un analizador sintctico predictivo para , pues la tabla presenta conflictos en , , , , , # y , .

Jacqueline Khler C. - USACH

35

Compiladores

TABLA 3.4: Analizador sintctico LL(1) completo.

3.4.3 GRAMTICAS LL(1) El algoritmo de construccin para analizadores sintcticos LL(1) puede ser aplicado a cualquier gramtica . No obstante, si es ambigua o recursiva por la izquierda se tendr al menos una entrada de la tabla con ms de una definicin. Como para el anlisis sintctico predictivo se requiere poder determinar sin ambigedad qu produccin utilizar, ser necesario utilizar un subconjunto de las gramticas libres de contexto: las gramticas LL(1). La primera L del nombre hace referencia a que el anlisis se efecta de izquierda a derecha. La segunda, a que se deriva por a izquierda. El 1 indica que se examina un nico smbolo de la entrada antes de decidir qu produccin ocupar. Se dice que una gramtica es LL(1) cuando no es ambigua ni recursiva por la izquierda. Se puede demostrar que una gramtica es de este tipo si y solo si, cuando | sean dos producciones diferentes de , se cumplen las siguientes condiciones: Para ningn terminal , tanto como derivan a la vez cadenas que comiencen por . A lo sumo una de las producciones o pueden derivar la cadena vaca. Si , no genera ninguna cadena que comience con un terminal en . 3.4.4 RECUPERACIN DE ERRORES EN ANLISIS SINTCTICO LL(1) La pila de un analizador sintctico no recursivo hace explcitos los terminales y no terminales que el analizador espera emparejar con el resto de la tabla. En consecuencia, durante las siguientes explicaciones se har referencia a los smbolos de la pila. La deteccin de un error se produce cuando el terminal al tope de la pila no concuerda con el siguiente smbolo de la entrada o bien cuando en el tope de la pila se encuentra el no terminal , el siguiente smbolo de la entrada es y la entrada , de la tabla no se encuentra definida. 3.4.4.1 Recuperacin de errores en modo de pnico Como se explic con anterioridad, este tipo de recuperacin de error consiste en eliminar smbolos de la entrada hasta encontrar algn componente lxico que pertenezca a un conjunto de componentes lxicos de sincronizacin. No obstante, se debe tener cuidado al elegir estos componentes de sincronizacin. Algunas tcnicas que se podran emplear son: Jacqueline Khler C. - USACH

36

Compiladores Colocar dentro del conjunto de sincronizacin para el no terminal todos los smbolos contenidos en . Probablemente el anlisis sintctico podr continuar si se eliminan componentes de la entrada hasta encontrar algn elemento de y se elimina el no terminal de la pila. En ocasiones puede ser necesario un conjunto de elementos de sincronizacin ms grande. Por ejemplo, podra ser til aadir al conjunto de sincronizacin de una construccin de menor jerarqua aquellos componentes que inician las construcciones de una jerarqua mayor. Pueden aadirse al conjunto de sincronizacin aquellos smbolos terminales contenidos en . Si un no terminal puede generar , puede usarse esta produccin por omisin. Si no se puede descartar un terminal de la pila, puede eliminarse dicho terminal. Ejemplo 3.6: Considere el analizador sintctico LL(1) del ejemplo 3.4. Muestre la traza para la entrada usando recuperacin de errores en modo de pnico. Usaremos como conjunto de sincronizacin para cada no terminal: $, $, , $, , ,

TABLA 3.5: Traza con recuperacin de errores en modo de pnico para la entrada usando el analizador sintctico de la tabla 3.2.

3.4.4.2 Recuperacin de errores a nivel de frase Consiste en llenar las entradas en blanco (entradas de error) de la tabla de anlisis sintctico con punteros a funciones de error. Estas funciones podran cambiar, insertar o eliminar smbolos de la entrada y enviar los mensajes de error apropiados. No obstante, no ser tratada a fondo en este curso.

Jacqueline Khler C. - USACH

37

Compiladores

3.5 ANLISIS SINTCTICO DESCENDENTE (POR DESPLAZAMIENTO Y REDUCCIN)


3.5.1 ASPECTOS GENERALES Este tipo de anlisis sintctico intenta construir un rbol de anlisis sintctico para una secuencia de componentes lxicos comenzando desde las hojas y avanzando hacia la raz. En otras palabras, se reduce la secuencia de componentes lxicos de la entrada hasta tener solamente el smbolo inicial de la gramtica. En cada paso de reduccin se sustituye una subcadena de la entrada que concuerde con el lado derecho de una produccin por el no terminal del lado izquierdo de la misma (en otras palabras, si se tiene la produccin , la reduccin reemplaza la secuencia por el no terminal ). Si en cada paso se escoge correctamente la subcadena a reemplazar, el resultado final es la traza de una derivacin por la derecha en sentido inverso. Ejemplo 3.7: Sea , N, , , donde: , , , , . N , , .
, | ,

La cadena puede reducirse en los siguientes pasos: ( ) ( ) ( ) ( ) Estas reducciones trazan, en sentido inverso, la derivacin:
.

Una manera de implementar el anlisis sintctico por desplazamiento y reduccin es mediante un AFD con una pila asociada. Inicialmente, la pila solo contiene el estado inicial del AA, mientras que la cadena a analizar se encuentra en la entrada seguida del delimitador (es decir, $). El analizador sintctico desplaza cero o ms smbolos de la entrada a la pila hasta que se reconozca el lado derecho de una produccin y entonces se reduce reemplazndolo por el lado izquierdo de la produccin correspondiente (el no terminal ). Se repite este proceso hasta encontrar un error o hasta que la pila solo contenga al smbolo inicial de la gramtica y la entrada est vaca. Aunque las principales operaciones de este tipo de analizador sintctico son el desplazamiento y la reduccin, existen en realidad cuatro acciones diferentes: Desplazar: se desplaza el siguiente smbolo de la entrada al tope de la pila, seguido del nuevo estado del AFD. Reducir: se sustituye el lado derecho de una produccin, contenido en la pila, por su lado izquierdo. Jacqueline Khler C. - USACH

38

Compiladores Aceptar: anuncia el trmino exitoso del anlisis sintctico. Error: llama a una rutina de recuperacin de error. Ahora bien, existen algunas GLC en que un analizador sintctico por desplazamiento y reduccin puede alcanzar una configuracin donde, conociendo el contenido de la pila y el siguiente smbolo de la entrada, sea imposible decidir si efectuar un desplazamiento o una reduccin (conflicto desplazamiento/reduccin), o bien qu reduccin efectuar (conflicto reduccin/reduccin). Para evitar este tipo de problemas se utilizar un subconjunto de las gramticas independientes del contexto: la clase de gramticas LR(k). En consecuencia, el anlisis sintctico ascendente suele recibir el nombre de anlisis sintctico LR, por left-to-right parse, rightmost derivation. Lee la entrada de izquierda a derecha y construye una derivacin por la derecha en orden inverso. Adicionalmente, la k entre parntesis corresponde al nmero de smbolos de la entrada que son considerados al momento de decidir qu accin ejecutar. Cabe sealar que en la prctica no se usa 1 para la compilacin, pues se requieren tablas demasiado grandes. La familia de mtodos LR permite analizar un superconjunto de la clase de gramticas LL(1), es decir, 1 . Es posible construir analizadores sintcticos LR para reconocer prcticamente todas las construcciones de los lenguajes de programacin definidos mediante GLC, por lo que este esquema es el ms utilizado.

3.5.2 GRAMTICAS LR(K) Anteriormente se seal que existen casos en que un analizador sintctico por desplazamiento y reduccin tiene problemas para decidir qu accin ejecutar. Para que opere correctamente, este tipo de analizador sintctico debe ser capaz de decidir si reemplazar o reducir conociendo solamente los smbolos de la pila y los siguientes k elementos de la entrada. Sean dos producciones de cuyos lados derechos sean | , respectivamente. Se puede observar que dichas producciones comparten el prefijo . Supngase adems que los primeros smbolos de ambas producciones son los mismos. Como ambas producciones son iguales tanto en la pila como en la porcin de la cadena visible para el analizador, entonces la gramtica ser LR(k) si y solo si . 3.5.3 ANLISIS SINTCTICO SLR El primer mtodo de la familia LR que estudiaremos recibe su nombre por Simple Left-to-Right parser. No obstante, antes de construir un analizador sintctico de esta clase es necesario definir algunos conceptos.

3.5.3.1 Elemento LR(0) Un elemento LR(0) es una produccin de la gramtica con un punto en algn lugar del lado derecho. Por ejemplo, la produccin produce cuatro elementos LR(0): Jacqueline Khler C. - USACH

39

Compiladores Es importante sealar que una produccin producir solamente el elemento . Intuitivamente, un elemento LR(0) indica hasta dnde se ha ledo una produccin en un momento dado del proceso de anlisis sintctico. El primer elemento del ejemplo anterior indica que se espera ver en la entrada una cadena derivable a partir de . El segundo elemento indica que se ha ledo una cadena derivable a partir de y que a continuacin se espera leer otra derivable de , etc. 3.5.3.2 Operacin Clausura Si es un conjunto de elementos LR(0) para una gramtica , entonces la es el conjunto de elementos LR(0) construido a partir de segn las siguientes reglas: 1. Inicialmente, hacer . 2. Si y es una produccin, hacer . Aplicar esta regla hasta que no sea posible aadir ms elementos a . Ejemplo 3.8: Sea , N, , , donde: ,, , , . N , , , .
, | , | , |

. Sea adems . Determine . Por regla 1: Por regla 2, se deben agregar las producciones de : , , Por regla 2, se deben aadir tambin las producciones de : , , , , Por regla 2, se deben aadir ahora las producciones de : , , , , , ,

Jacqueline Khler C. - USACH

40

Compiladores 3.5.3.3 Construccin del autmata La idea central del mtodo SLR es construir, a partir de , un AFD (considerando la definicin no estricta, en que cada estado tiene a lo ms una transicin por cada smbolo del alfabeto) con una pila asociada que permita reconocer los prefijos viables (es decir, que se pueden derivar a partir de alguna produccin). Para este fin se agrupan los elementos LR(0) en conjuntos que conforman los estados del AFD. Adems, el alfabeto del AFD est dado por todos los terminales y no terminales de la gramtica. El primer paso necesario para la construccin del AFD es aumentar la gramtica con una nueva produccin , donde es un nuevo smbolo inicial, a fin de asegurar que el smbolo inicial tenga una nica produccin. Esta modificacin tiene por objeto indicar en qu momento se debe detener el anlisis sintctico y aceptar la cadena: es decir, cuando se est a punto de hacer la reduccin de . Ntese que esta nueva produccin no pasa a formar parte de la gramtica, sino que se usa exclusivamente para la construccin del estado inicial del AFD. El estado inicial del AFD est dado por . A continuacin se determinan las transiciones con todos aquellos smbolos precedidos por un punto en algn elemento LR(0) de . Para los nuevos estados, se determina la de los elementos LR(0) que le dan origen, es decir, aquellos del estado anterior con el punto desplazado en una posicin hacia la derecha. Las transiciones se determinan igual que para . Si nos encontramos ante un grupo idntico de elementos LR(0) que avanzan con un mismo smbolo que en algn estado anterior, dicha transicin avanza al estado ya conocido. Ejemplo 3.9: Sea , N, , , donde:
,, , , , ,

N , , ,

Construya el AFD asociado al analizador sintctico SLR. Comenzamos por agregar la produccin . El estado inicial del AFD queda dado por: , es decir:

| , | , | | |

Jacqueline Khler C. - USACH

41

Compiladores Ahora debemos determinar las transiciones de . Como solo podemos tener a lo ms una transicin por cada smbolo del alfabeto, tenemos que: 1 1 2 2 3 4 5 6 7 Ahora es necesario determinar los elementos LR(0) que conforman cada uno de los nuevos estados, as como las transiciones de estos ltimos: , As, se tiene que:

8 9

10 4 5 6 7 11 11 2 2 3 4 5 6 7

12 12 3 4 5 6 7 13 4 5 6 7


14 8 9

Jacqueline Khler C. - USACH

42

Compiladores La tabla 3.6 muestra las transiciones del AFD obtenido.

TABLA 3.6: Tabla de transiciones del AFD asociado al analizador sintctico SLR.

3.5.3.4 Construccin y operacin del analizador sintctico SLR Tomando como base la tabla de transiciones del AFD (tabla 3.6), se construye la tabla de anlisis sintctico SLR de la siguiente forma: 1. Incorporar la operacin de desplazamiento para cada transicin con un terminal. 2. Incorporar la aceptacin en el estado donde se tenga con $. 3. Para cada estado en que se tenga algn elemento LR(0) de la forma , incorporar una reduccin por 4. Toda casilla no definida de la tabla corresponde a un error.
dicha produccin en ese estado para cada smbolo en .

Ejemplo 3.10: Construya la tabla de anlisis sintctico SLR para la gramtica del ejemplo anterior. Muestre la traza y el rbol sintctico para .
Comenzamos por incorporar los desplazamientos y la aceptacin, como muestra la tabla 3.7.

Antes de incorporar las reducciones, necesitamos conocer los conjuntos siguientes para cada no terminal. As, tenemos que: , , , , , , , , , Jacqueline Khler C. - USACH

43

Compiladores

TABLA 3.7: Desplazamientos y configuracin de aceptacin para el analizador sintctico SLR. $ $ $, , $, , $, ,, $, ,, Ahora debemos asignar un nombre a cada produccin a fin de poder identificarlas en la tabla de anlisis sintctico:
| , | , | | |

Como en tenemos , debemos incorporar una reduccin por la segunda produccin en con cada smbolo en . Lo mismo debe hacerse para cada estado en que se termine una
produccin, como muestra la tabla 3.8, con lo que el analizador sintctico SLR queda terminado.

TABLA 3.8: Analizador sintctico SLR terminado. Jacqueline Khler C. - USACH

44

Compiladores La figura 3.4 muestra el funcionamiento del analizador sintctico SLR y muestra el rbol sintctico resultante para la entrada .

FIGURA 3.4: Traza y rbol sintctico resultante para la entrada . Ejemplo 3.11: Construya un analizador sintctico SLR para , N, , , donde:
, @

N , ,

Muestre la traza para @. Comenzamos por la construccin del AFD: Jacqueline Khler C. - USACH
1 2 3

, , @ |

4 @ 5

45

Compiladores @ @
6 3 @ 5

A continuacin, necesitamos conocer , y , y asignar nombres a las producciones: $ $ @ $ @ $, @ $, @ $, @ $, @


, , @ |

Comenzamos por la construccin del AFD: 1 2 3 @ @


4 @ 5 @ 5

@ @ @
6 3 7

A continuacin, necesitamos conocer , y , y asignar nombres a las producciones: $ $ @ $ @ $, @ $, @ $, @ $, @


, , @ |

La tabla 3.9 muestra el analizador sintctico SLR resultante, mientras que la traza 3.10 muestra la traza para la entrada @. Ejemplo 3.12: Construya un analizador sintctico SLR para , N, , , donde:
,, , ,

| | | |

Jacqueline Khler C. - USACH

46

Compiladores

TABLA 3.9: Analizador sintctico SLR del ejemplo 3.11 terminado.

TABLA 3.10: Traza para la entrada @ con el analizador sintctico SLR de la tabla 3.9. Comenzamos por la construccin del AFD: 1 1 1 1 2 3 4 4 5 6 6 6 6 2 3 Jacqueline Khler C. - USACH

7 7 7 7 7 2 3 8 8 8 8 2 3

47

Compiladores 9 4 4 5 10 4 4 10 4 4 5 A continuacin, necesitamos conocer y asignar nombres a las producciones: $ $ $, ,,


| | | |

11 11 11 2 3 4 5

Ahora construimos la tabla del analizador sintctico SLR, que se muestra en la tabla 3.11.

TABLA 3.11: Analizador sintctico SLR del ejemplo 3.12. Evidentemente, la gramtica dada no es SLR pues la tabla presenta muchos conflictos. No obstante, al ser una gramtica de operadores, podemos resolverlos. Supongamos que tiene

mayor precedencia que , que la suma es asociativa por la izquierda y que la multiplicacin es asociativa por la derecha.

Jacqueline Khler C. - USACH

48

Compiladores 3.5.4 USO DE GRAMTICAS AMBIGUAS Existen casos en que es ms cmodo trabajar con gramticas ambiguas, pues estas ofrecen una representacin ms corta y natural que una gramtica no ambigua equivalente. Al usar este tipo de gramticas se pueden producir dos tipos de conflictos: desplazamiento/reduccin y reduccin/reduccin. Para que el analizador sintctico pueda funcionar correctamente es necesario reducir estos conflictos, a fin de que en cada entrada de la tabla figure a lo ms una accin. Esta tarea resulta muy sencilla al trabajar con gramticas de operadores. Para todos los analizadores sintcticos de la famila LR se usan los mismos principios. Un conflicto reduccin/reduccin significa que, dado el estado del AFD que se encuentra al tope de la pila y el siguiente smbolo de la entrada, es posible efectuar dos reducciones diferentes (podran ser ms). Para eliminar este tipo de conflictos de la tabla de anlisis sintctico LR siempre se escoge la reduccin correspondiente a la produccin ms larga, pues las reducciones para las producciones ms cortas habrn aparecido ya en otros estados del AFD. Similarmente, un conflicto desplazamiento/reduccin significa que, dado el estado del AFD que se encuentra al tope de la pila y el siguiente smbolo de la entrada, es posible efectuar tanto un desplazamiento como una reduccin (podran ser ms desplazamientos y reducciones). La resolucin de los conflictos desplazamiento/reduccin se har en base a la precedencia y la asociatividad de los operadores de la siguiente manera: Si se tiene la configuracin y tiene mayor precedencia que , entonces se escoge la reduccin. En caso contrario, se escoge el desplazamiento. Si se tiene la configuracin es asociativo por la derecha, entonces se escoge el desplazamiento. En caso contrario, se escoge la reduccin. Ejemplo 3.13: Elimine conflictos en el analizador sintctico del ejemplo 3.12. Para los conflictos entre reducciones existentes en , habamos explicado que se conserva nicamente la produccin ms larga. En consecuencia, descartamos 2 y conservamos nicamente 3. En 7, nos encontramos ante el escenario . En otras palabras, debemos decidir si reducir la primera suma o terminar de leer la segunda. Como es asociativo por la izquierda, conservamos la reduccin. En 7, se tiene . En otras palabras, debemos decidir si reducir suma o leer la multiplicacin. Como tiene mayor precedencia, conservamos el desplazamiento. En 8, nos encontramos ante el escenario . Como tiene mayor precedencia, conservamos la reduccin. En 8, se tiene . En otras palabras, Como es asociativo por la derecha, conservamos el desplazamiento.

Jacqueline Khler C. - USACH

49

Compiladores Los conflictos restantes en 11, y 11, son anlogos a los de 8, y 8,, respectivamente, por lo que tomamos las mismas decisiones. As, la tabla 3.12 muestra el analizador sintctico SLR libre de conflictos.

TABLA 3.12: Analizador sintctico SLR del ejemplo 3.12 sin conflictos. 3.5.5 RECUPERACIN DE ERRORES Al igual que en el caso del anlisis sintctico predictivo, existen dos grandes esquemas para la recuperacin de errores en la familia de analizadores sintcticos LR: en modo de pnico y a nivel de frase.

3.5.5.1 Recuperacin en modo de pnico En el anlisis sintctico LR en general, la recuperacin de errores en modo de pnico se implementa como sigue: Se descartan elementos de la pila hasta encontrar un estado que tenga una transicin definida para un no terminal especfico. Luego se descartan smbolos de la entrada hasta que se encuentra un terminal . Incorporar y el estado dado por , al tope de la pila. Se contina normalmente con el anlisis sintctico. Ntese que este esquema no necesariamente funciona en todos los casos. Ejemplo 3.14: Muestre la traza para la entrada con el analizador sintctico obtenido en el ejemplo 3.13. Considere recuperacin de errores en modo de pnico. La traza resultante se muestra en la tabla 3.13. Jacqueline Khler C. - USACH

50

Compiladores

TABLA 3.13: Traza para la entrada con el analizador sintctico SLR de la tabla 3.12, considerando recuperacin de errores en modo de pnico.

3.5.5.2 Recuperacin a nivel de frase La idea de esta tcnica es, basndose en el uso del lenguaje, determinar el error ms probable que pudiera cometer un programador para cada entrada de la tabla correspondiente a un error e implementar una funcin que lo corrija. Ejemplo 3.15: Muestre la traza para la entrada con el analizador sintctico obtenido en el ejemplo 3.13. Considere recuperacin de errores a nivel de frase. El primer paso consiste en identificar los posibles errores e incorporarlos al analizador sintctico. 1: en este caso se encuentra en la entrada un operador ( o )o bien el fin de la entrada ($), siendo que se esperaba el inicio de un operando, es decir, o . Las acciones a realizar para corregir este error y poder seguir adelante son: Insertar en la pila. Insertar en la pila el estado 3 (transicin que se produce cada vez que se desplaza una ). Notificar el error correspondiente: falta operando. 2: en este caso se encuentra en la entrada un parntesis derecho no balanceado, . Las acciones a realizar para corregir este error y poder seguir adelante son: Eliminar de la entrada. Notificar el error correspondiente: parntesis derecho no balanceado. 3: en este caso se encuentra en la entrada un operando o un parntesis izquierdo, siendo que se esperaba un operador o el trmino de la entrada. Esto se arregla mediante las siguientes acciones: Jacqueline Khler C. - USACH

51

Compiladores Insertar un operador ( o )en la entrada. Notificar el error correspondiente: falta operador.

4: se encuentra el fin de la entrada, siendo que se espera un parntesis derecho. El procedimiento a seguir en este caso es: Insertar en la pila. Insertar en la pila el estado 9. Notificar el error correspondiente: parntesis izquierdo no balanceado. La tabla 3.14 muestra el analizador sintctico del ejemplo 3.12 sin conflictos y con el esquema de recuperacin de errores a nivel de frase, mientras la tabla 3.15 muestra la traza con este nuevo analizador sintctico para la entrada .

TABLA 3.14: Analizador sintctico SLR del ejemplo 3.12 sin conflictos.

3.5.6 ANLISIS SINTCTICO LR(1) 3.5.6.1 Elemento LR(1) Un elemento LR(1) est conformado por un elemento LR(0), denominado ncleo, y un smbolo de anticipacin que puede ser un smbolo terminal o el delimitador de la entrada ($). Un mismo ncleo puede ser comn a varios elementos LR(1). Un ejemplo de elemento LR(1) puede ser , . El smbolo de anticipacin indica lo que debiera leerse en la entrada tras reducir por la produccin . En otras palabras, el smbolo de anticipacin es un elemento de que puede, efectivamente, aparecer inmediatamente despus de una produccin en un contexto dado. Esta es la caracterstica que hace posible construir analizadores sintcticos LR(1) para un conjunto ms grande que SLR. En consecuencia, 1 1 . Sean los elementos LR(1) , y , . En el caso del anlisis sintctico SLR estos elementos podran causar un conflicto reduccin/reduccin. No obstante, la incorporacin del smbolo de anticipacin a los elementos permite decidir de manera ms eficiente qu reduccin ocupar. Dicho Jacqueline Khler C. - USACH

52

Compiladores smbolo solo ser utilizado directamente al momento de llevar a cabo una accin de reduccin, como se explica ms adelante.

TABLA 3.15: Traza para la entrada con el analizador sintctico SLRde la tabla 3.14.

3.5.6.2 Operacin clausura Si es un conjunto de elementos LR(1) para una gramtica , entonces la es el conjunto de elementos LR(1) construido a partir de segn las siguientes reglas: 1. Inicialmente, hacer . 2. Si , y es una produccin, hacer: , . Aplicar esta regla hasta que no sea posible aadir ms elementos a .

Jacqueline Khler C. - USACH

53

Compiladores La definicin de la clausura coincide en muchos aspectos con la utilizada en la construccin del AFD SLR. La nica diferencia radica en la determinacin de los smbolos de anticipacin.

3.5.6.3 Construccin del AFD El proceso de construccin del AFD LR(1) es muy similar al SLR. Tambin es necesario aumentar la gramtica con la produccin . Luego se determina el estado inicial del AFD haciendo , $. Ntese que se escoge $ como smbolo de anticipacin porque genera al smbolo inicial de la gramtica. Hay que recordar que representa cualquier secuencia vlida que pueda ser generada por la gramtica. Y Para tener esta validez, no puede venir nada ms al trmino de la secuencia. A continuacin se determinan las transiciones con todos aquellos smbolos precedidos por un punto en algn elemento LR(1) de . Para los nuevos estados, se determina la de los elementos LR(1) que le dan origen, es decir, aquellos del estado anterior con el punto desplazado en una posicin hacia la derecha. Las transiciones se determinan igual que para . Si nos encontramos ante un grupo idntico de elementos LR(1) que avanzan con un mismo smbolo que en algn estado anterior, dicha transicin avanza al estado ya conocido. Ntese que en LR(1) no basta con solo tener las mismas producciones que avancen con el mismo smbolo para ir a un mismo estado. Los smbolos de anticipacin tambin deben coincidir. En la prctica los analizadores sintcticos LR(1) no son muy utilizados, puesto que, en general, los autmatas son demasiado grandes y requieren de un espacio excesivo de almacenamiento.

3.5.6.4 Construccin de la tabla de anlisis sintctico LR(1) En la construccin de la tabla de anlisis sintctico LR(1) para una gramtica se har uso del AFD ya obtenido de la siguiente manera: 1. Incorporar la operacin de desplazamiento para cada transicin con un terminal. 2. Incorporar la aceptacin en el estado donde se tenga , $ con $. 3. Para cada estado en que se tenga algn elemento LR(1) de la forma , , incorporar una reduccin por dicha produccin en ese estado para el smbolo de anticipacin . 4. Toda casilla no definida de la tabla corresponde a un error. Si las reglas anteriores generan acciones contradictorias se dice que la gramtica no es LR(1) y no se puede construir el analizador sintctico. Una vez ms, no obstante, muchas veces es posible obtener un analizador sintctico LR(1) para una gramtica ambigua mediante reduccin de conflictos como ya se explic.

Jacqueline Khler C. - USACH

54

Compiladores Ejemplo 3.16: Construya un analizador sintctico LR(1) para , N, , , donde: , N , , | Compare el analizador sintctico LR(1) con otro SLR para la misma gramtica. Comenzamos por la construccin del AFD LR(1): , $ , $ , , , , Ntese que en el caso de las producciones de debemos considerar como smbolos de anticipacin todos los elementos de $ , , lo que nos lleva a los siguientes pares de elementos LR(1): , ; , y , ; , . Por comodidad, podemos reescribir de la siguiente forma: , $ , $ , / , /

Continuemos ahora con la construccin del AFD LR(1): , $ , $ , $ , $ , $ , $ , $ , $ , $ , / , $

, / , / , / , /

Asignamos un nombre a las producciones para poder identificarlas al hacer las reducciones: Asignamos un nombre a las producciones para poder identificarlas al hacer las reducciones: , | Ahora construimos la tabla LR(1), que se muestra en la tabla 3.16. Jacqueline Khler C. - USACH

55

Compiladores

TABLA 3.16: Analizador sintctico LR(1) del ejemplo 3.16. Construyamos ahora el AFD SLR: , , $ , , , $

Para construir la tabla SLR necesitamos conocer y : $ $ $ $, , Ahora construimos la tabla SLR:

TABLA 3.17: Analizador sintctico SLR del ejemplo 3.16. Se puede notar que, pese a la simplicidad de la gramtica, el analizador sintctico LR(1) es significativamente ms grande que el SLR. Extrapolando para una GLC que defina un lenguaje de programacin, esta diferencia podra ser del orden de miles de estados, con un conjunto de smbolos muchsimo ms grande. Jacqueline Khler C. - USACH

56

Compiladores Otra observacin importante es que, para este ejemplo, ninguna de las dos tablas presenta conflictos, por lo que es SLR. Debemos recordar que al ser SLR es tambin LR(1), pues 1. Ejemplo 3.17: Construya un analizador sintctico LR(1) para , N, , , donde: ,, N , , | , | , Compare el analizador sintctico LR(1) con otro SLR para la misma gramtica. Comenzamos por la construccin del AFD LR(1): , $ , $ , $ , /$ , /$ , $ , $ , $ , $ , $ , /$ , /$ , /$ , /$ , /$ , $ , $ , $ , $ , /$ , /$ , $ , $ , $ , $ , $ , $ , $ , $

Asignamos un nombre a las producciones para poder identificarlas al hacer las reducciones: Asignamos un nombre a las producciones para poder identificarlas al hacer las reducciones: | , | , Ahora construimos la tabla LR(1), que se muestra en la tabla 3.18.

Jacqueline Khler C. - USACH

57

Compiladores

TABLA 3.18: Analizador sintctico LR(1) del ejemplo 3.17. Construyamos ahora el AFD SLR: , Para construir la tabla SLR necesitamos conocer , y : $ $, $, $ $, Ahora construimos la tabla SLR, que se muestra en la tabla 3.19. Nuevamente la tabla SLR es ms pequea que la tabla LR(1). No obstante, podemos afirmar que no es SLR, puesto dicha tabla presenta un conflicto, mientras que s cumple con ser LR(1).

Jacqueline Khler C. - USACH

58

Compiladores

TABLA 3.19: Analizador sintctico SLR del ejemplo 3.17.

3.5.7 ANLISIS SINTCTICO LALR 3.5.7.1 Notas preliminares El anlisis sintctico con anticipacin o LALR (Lookahead Left to Right Parser) es de los ms utilizados en la prctica, pues sus tablas son del mismo tamao que las SLR y significativamente ms pequeas que las LR(1), y sin embargo puede manejar un conjunto de gramticas ms amplio que el anlisis sintctico SLR: las gramticas LALR(1), donde 1 1 . Los analizadores LR(1) y LALR tienen igual funcionamiento en caso de comprobarse una entrada correcta. Ante una entrada incorrecta, en cambio, el analizador LR(1) detecta el error de inmediato, mientras que el analizador LALR puede efectuar algunas reducciones antes de detectarlo. No obstante, el error ser detectado sin necesidad de efectuar un desplazamiento. Existen dos mtodos para construir el AFD asociado a un analizador sintctico LALR. El primero de ellos comienza a partir del AFD LR(1) y funde en uno solo aquellos estados que son idnticos excepto por los smbolos de anticipacin. El segundo, en cambio, construye directamente el AFD de manera similar al caso SLR, incorporando los smbolos de anticipacin a medida en que stos van apareciendo. En consecuencia, se puede decir que un analizador sintctico LALR es un analizador SLR con smbolos de anticipacin. La tabla de anlisis sintctico LALR se construye de acuerdo a las mismas reglas que en el caso de LALR.

3.5.7.2 Construccin del AFD a partir del analizador sintctico LR(1) Para comprender mejor este mtodo, resulta ms sencillo trabajar con un ejemplo. La idea es fusionar en uno solo aquellos estados que contienen los mismos elementos LR(0), es decir, que solo se diferencian por los smbolos de anticipacin de los elementos LR(1).

Jacqueline Khler C. - USACH

59

Compiladores Ejemplo 3.18: Construya un analizador lxico LALR para la gramtica del ejemplo 3.27. Tome como base el analizador sintctico LR(1) obtenido en dicho ejemplo. Podemos ver que es muy semejante a : , /$ , /$ , /$ , /$ Tambin es muy semejante a : , /$ Lo mismo ocurre con y : , /$ As como con y : , /$ , $ , $ , $ , $ , $ , $ , $

Si juntamos los pares de estados semejantes, para cada elemento LR(1) tendremos los smbolos de anticipacin de ambos estados. En este caso particular, nos basta simplemente con conservar , , y . Los estados que no se agrupan con otros quedan tal como estn. As, el AFD resultante es: Comenzamos por la construccin del AFD LR(1): , $ , $ , $ , /$ , /$ , $ , $ , $ , $ , $ , /$ , /$ , /$ , /$ , /$ , $ , $ , $ , $ , /$ , /$ , $

Jacqueline Khler C. - USACH

60

Compiladores Ahora construimos la tabla LALR, como se ve en la tabla 3.20.

TABLA 3.20: Analizador sintctico LALR del ejemplo 3.18. Una observacin importante es que, pese a que no es SLR, s es LALR al no existir conflictos en la tabla. Tambin es interesante notar que el AFD es idntico al SLR, y lo nico que vara en la tabla son las reducciones debidas a los smbolos de anticipacin. Ejemplo 3.19: Construya los analizadores sintcticos LR(1) y LALR para , N, , , con: , , , , N , , | | | , | , | Qu puede concluir acerca de ? Comencemos por la construccin del analizador sintctico LR(1): , $ , $ , $ , $ , $ , , , $ , $ , , , $ , $ , $ , $ , $ , , , , , , , Jacqueline Khler C. - USACH

61

Compiladores , , $ , , $ , $ , $ , $

, $ , , ,

Ahora asignamos un nombre a las producciones y construimos la tabla LR(1), que se muestra en la tabla 3.21. | | | , | , |

TABLA 3.21: Analizador sintctico LR(1) del ejemplo 3.19. Para construir el autmata LR(1) podemos encontrar las equivalencias que se muestran en la tabla 3.22.

Jacqueline Khler C. - USACH

62

Compiladores

TABLA 3.22: Estados equivalentes del analizador sintctico LR(1). La tabla LALR resultante se muestra en la tabla 3.23.

TABLA 3.23: Analizador sintctico LALR del ejemplo 3.19. Podemos concluir que es LR(1) pero no es LALR. Adems, al no ser LALR, tampoco puede ser SLR ni LL(1).

Jacqueline Khler C. - USACH

63

Compiladores 3.5.7.3 Construccin directa del AFD En este caso, se tiene que la construccin del AFD se realiza igual que en SLR, siendo necesario adems incorporar los smbolos de anticipacin. Ejemplo 3.20: Construya el analizador sintctico LALR, usando el mtodo directo, para , N, , , con: , @, , , , N | @ | | Resuelva conflictos considerando que @ es asociativo por la izquierda. Comenzamos por la construccin del AFD LALR: , $ @, $/@ , $/@ , $/@ , $/@ , $ @, $/@ , $/@// @, /@ , /@ , /@ , /@ , $/@// @, /@ , /@ , /@ , /@ @

, $/@// @ , $/@// @, $/@// , $/@// , $/@// , $/@// , $/@// @, /@ @

, $/@// @, /@ @ @, $/@// @, $/@// @ , $/@// , $/@//

Asignamos nombres a las producciones y construimos la tabla 3.24, correspondiente al analizador sintctico LALR. @ | | |

Jacqueline Khler C. - USACH

64

Compiladores

TABLA 3.24: Analizador sintctico LALR del ejemplo 3.20. El conflicto existente en 8, @ es de la forma @ @. Como @ es asociativo por la izquierda, se descarta el desplazamiento, quedando el analizador sintctico LALR como muestra la tabla 3.25.

TABLA 3.25: Analizador sintctico LALR del ejemplo 3.20 tras la eliminacin de conflictos.

3.6 EJERCICIOS
1. Determine los conjuntos Anulable, Primero y Siguiente para , N, , , con:
,

N , , ,

| @ | | @, | | | , | | , # |

2. Construya un analizador sintctico predictivo para la gramtica del ejercicio anterior. Modifquela en caso necesario.

Jacqueline Khler C. - USACH

65

Compiladores 3. Construya analizadores sintcticos SLR, LR(1) y LALR para la gramtica del ejercicio 1 y la gramtica modificada del ejercicio 2. 4. Sea , N, , , con: N ,
,, . , , , . | | | |

a. Haga las modificaciones necesarias y construya un analizador sintctico LL(1). Es posible? b. Construya analizadores sintcticos SLR, LR(1) y LALR para la gramtica original. En caso de existir conflictos, considere que las precedencias de los operadores, de ms alta a ms baja, son: c. Muestre la traza y el rbol resultante para . con cada uno de los analizadores del punto anterior. d. Construya analizadores sintcticos SLR, LR(1) y LALR para la gramtica obtenida en a. 5. Considere el analizador sintctico SLR de la tabla 3.12 y la entrada . Qu ocurre al hacer la traza usando recuperacin de errores en modo de pnico? Por qu? 6. En qu casos se tiene que las tablas SLR y LALR son iguales? Por qu? 7. Compruebe la equivalencia de los dos mtodos para construie analizadores sintcticos LALR repitiendo el ejercicio del ejemplo 3.20 usando la construccin a partir del analizador sintctico LR(1).
, ., . Asuma que . y son asociativos por la izquierda.

Jacqueline Khler C. - USACH

66

Compiladores

4 ANLISIS SEMNTICO
La palabra semntica, proveniente del griego semantikos (lo que tiene significado), se refiere a los aspectos del significado o interpretacin de un determinado cdigo simblico, lenguaje o representacin formal. Etapa del proceso de compilacin se asocia informacin a una cierta construccin del lenguaje de programacin proporcionando atributos a los smbolos de la gramtica que la conforman. A nivel conceptual, esta etapa est situada a continuacin del anlisis sintctico, donde se construye el rbol de anlisis sintctico. Durante el anlisis semntico se recorre el rbol para evaluar las reglas semnticas presentes en sus nodos (es decir, las reglas semnticas correspondientes a la produccin empleada en dicho nodo). Al evaluar una regla semntica se pueden efectuar diversas actividades, entre ellas generar cdigo, almacenar informacin en una tabla de smbolos y emitir mensajes de error. Al finalizar esta etapa se obtiene como resultado la traduccin de la cadena de componentes lxicos. En la prctica, las tareas del anlisis semntico se realizan simultneamente con el anlisis sintctico, en una dependencia similar a la que existe entre este ltimo y el anlisis lxico. Como las reglas semnticas estn asociadas a las producciones de la gramtica, pueden ser ejecutadas una vez que se ha reconocido la produccin correspondiente.

4.1 DEFINICIONES DIRIGIDAS POR LA SINTAXIS Una definicin dirigida por la sintaxis es una generalizacin de una gramtica independiente del contexto en la que cada smbolo de la gramtica tiene un conjunto de atributos asociado, que se divide en dos subconjuntos: atributos sintetizados y atributos heredados. Un atributo puede representar cualquier cosa: una cadena de caracteres, un nmero, un tipo, una posicin de memoria, etc. El valor de un atributo en un nodo del rbol de anlisis sintctico se define mediante una regla semntica asociada a la produccin empleada en dicho nodo. Para un atributo sintetizado, se calcula a partir de los valores de los atributos de los hijos del nodo en cuestin. Para un atributo heredado, se calcula a partir de los valores de los atributos de los hermanos y el padre del nodo. Las reglas semnticas establecen las dependencias entre los atributos, que se representan mediante un grafo de dependencias. De este grafo se obtiene un orden de evaluacin de las reglas semnticas. La evaluacin de las reglas semnticas permite conocer el valor de los atributos, pero adems puede tener efectos colaterales como imprimir un valor o actualizar una variable global. En una definicin dirigida por la sintaxis, cada produccin gramatical tiene asociado un conjunto de reglas semnticas, de la forma , , , , donde: es una funcin. son atributos pertenecientes a los smbolos gramaticales de la produccin. es un atributo sintetizado de o un atributo heredado de los smbolos del lado derecho de la produccin. Se dice que depende de los atributos .

Jacqueline Khler C. - USACH

67

Compiladores Se asume que los smbolos terminales solo tienen atributos sintetizados, ya que la definicin dirigida por la sintaxis no proporciona reglas semnticas para estos smbolos. El valor de estos atributos es, en general, proporcionado por el analizador lxico. Tambin se asume que el smbolo inicial de la gramtica no tiene atributos heredados, a menos que se indique lo contrario. Las funciones de las reglas semnticas a menudo se escriben como expresiones. No obstante, cuando el nico propsito de una regla semntica es crear un efecto colateral, la regla se escribe como una llamada a un procedimiento o un fragmento de programa. Ejemplo 4.1: La tabla 4.1 muestra la definicin dirigida por la sintaxis para un programa que efecta las operaciones de adicin y multiplicacin. A cada uno de los no terminales , y se le asocia un atributo sintetizado llamado y la regla semntica correspondiente calcula el valor del atributo del no terminal del lado izquierdo de la produccin a partir de los atributos de los no terminales del lado derecho. El componente lxico tiene un atributo sintetizado , cuyo valor es proporcionado por el analizador lxico. La regla semntica asociada a la produccin para el no terminal es un procedimiento que imprime el valor de la expresin aritmtica generada por . Obsrvese que es simplemente un carcter de salto de lnea.

TABLA 4.1: Definicin dirigida por la sintaxis para efectuar las operaciones de adicin y multiplicacin.

4.1.1 ATRIBUTOS SINTETIZADOS Este tipo de atributos es muy utilizado en la prctica. Una definicin dirigida por la sintaxis que solo emplee este tipo de atributos se denomina definicin con atributos sintetizados y siempre es posible generar un rbol de anlisis sintctico con anotaciones para este tipo de definiciones. La construccin se efecta evaluando en forma ascendente las reglas semnticas para los atributos en cada nodo. Ejemplo 4.2: La figura 4.1 muestra el rbol de anlisis sintctico con anotaciones para la entrada 3 5 4 obtenido con la definicin dirigida por la sintaxis de la tabla 4.1. Para comprender cmo se calculan los atributos sintetizados, considere primero el nodo interior ms bajo de la izquierda, correspondiente al uso de la produccin . La regla Jacqueline Khler C. - USACH

68

Compiladores semntica correspondiente, . . , establece el atributo . con un valor de 3, que es el valor de . . De manera similar, se asigna tambin valor 3 al atributo . . A continuacin se procede del mismo modo con el siguiente subrbol izquierdo, de donde se obtiene que . 5. Al usar la produccin , se tiene que . . . 3 5 15. Finalmente, la regla semntica asociada a imprime por pantalla el valor de la expresin generada por .

FIGURA 4.1: rbol de anlisis sintctico con anotaciones para la entrada 3 5 4. 4.1.2 ATRIBUTOS HEREDADOS Son atributos cuyo valor en un nodo del rbol de anlisis sintctico se define a partir de los atributos del padre y los hermanos de dicho nodo. Sirven para expresar la dependencia de una construccin de un lenguaje de programacin de acuerdo al contexto en que aparece. Por ejemplo, se puede determinar si un identificador aparece en el lado izquierdo o derecho de una asignacin para saber si se necesita la direccin o el valor de dicho identificador. Ejemplo 4.3: La figura 4.2 muestra una definicin dirigida por la sintaxis en que el no terminal genera una declaracin conformada por la palabra clave o seguida de una lista de identificadores. El no terminal tiene un atributo sintetizado tipo, cuyo valor se determina a partir de la palabra clave de la declaracin. La regla semntica . . , asociada a la produccin , asigna el tipo de la declaracin al atributo heredado . . Las reglas semnticas asociadas con las producciones de llaman a la funcin aadetipo para ingresar el tipo de cada identificador a su entrada en la tabla de smbolos (apuntada por el atributo ). La figura 4.2 muestra el rbol de anlisis sintctico con anotaciones para la entrada , , . Jacqueline Khler C. - USACH

69

Compiladores

TABLA 4.2: Definicin dirigida por la sintaxis para declarar variables reales y enteras.

FIGURA 4.2: rbol de anlisis sintctico con anotaciones para la entrada , , . 4.1.3 GRAFOS DE DEPENDENCIAS Si un atributo en un nodo de un rbol de anlisis sintctico depende de un atributo , entonces se debe evaluar primero la regla semntica para y luego para . Antes de construir el rbol sintctico, cada regla semntica debe ser escrita en la forma , , , . Las dependencias se pueden representar mediante un grafo dirigido llamado grafo de dependencias, en el que existe un nodo por cada atributo y una arista del nodo de al nodo de si el atributo depende del atributo . El algoritmo 4.1 muestra cmo se construye el grafo de dependencias. ALGORITMO 4.1: Construccin del grafo de dependencias.
Para cada nodo en el rbol de anlisis sintctico hacer: Para cada atributo del smbolo gramatical en el nodo hacer: Construir un nodo para en el grafo de dependencias para . Para cada nodo en el rbol de anlisis sintctico hacer: Para cada regla semntica , , , asociada a la produccin empleada en el nodo hacer: Para todo desde 1 hasta hacer: En el grafo de dependencias, construir una arista desde el

Jacqueline Khler C. - USACH

70

Compiladores
nodo de hacia el nodo de .

Ejemplo 4.4: La figura 4.3 muestra las aristas que s aaden al grafo de dependencias al usar la regla semntica . . . , asociada a la produccin .

FIGURA 4.3: . se sintetiza a partir de . y . . Ejemplo 4.5: Considere el rbol de anlisis sintctico con anotaciones de la figura 4.2. Su grafo de dependencias se muestra en la figura 4.4.

FIGURA 4.4: Grafo de dependencias para el rbol de anlisis sintctico de la figura 4.2. Jacqueline Khler C. - USACH

71

Compiladores

4.2 RBOLES SINTCTICOS


El uso de rboles sintcticos permite que se separe la traduccin del anlisis sintctico. Sin embargo, este rbol contiene muchos detalles (parntesis, comas, etc.) y depende de la estructura de la gramtica. En consecuencia, resulta interesante construir un rbol sintctico ms sencillo, que conserve solo aquella informacin relevante y permita representar adecuadamente las construcciones del lenguaje. Un rbol sintctico abstracto (AST) es una forma condensada de un rbol sintctico para representar construcciones de un lenguaje en su forma ms simple o esencial. Algunas simplificaciones que se observan en los AST son: Operadores y palabras clave no aparecen ya como hojas, sino que son llevadas a un nodo interior padre de los operadores asociados. Omitir cadenas de producciones simples, por ejemplo . Se omiten detalles sintcticos tales como parntesis y signos de puntuacin, entre otros. Ejemplo 4.6: La figura 4.5 muestra el rbol de anlisis sintctico y el AST para la estructura ifelse.

FIGURA 4.5: rbol de anlisis sintctico y AST para la estructura if else. Ejemplo 4.7: El rbol de anlisis sintctico de la figura 4.1, sin considerar las anotaciones, puede simplificarse bastante al ser llevado a un AST, como muestra la figura 4.6. La construccin de un AST para una expresin es similar a la traduccin de la expresin a una forma postfija. Es decir, se construyen subrboles para cada subexpresin mediante la creacin de un nodo por cada operador y cada operando, donde los hijos de un nodo correspondiente a un operador son las races de los nodos que representan las subexpresiones que conforman los operandos de dicho operador. Cada nodo puede implementarse como un registro con varios campos. En un nodo de un operador, el primer campo identifica el operador mientras el resto contiene punteros a los nodos de los operandos. A modo de ejemplo, se consideran aqu las siguientes funciones para crear los nodos del rbol sintctico, donde cada una de ellas devuelve un puntero a un nuevo nodo recin creado: crearNodo(, , ): crea un nodo para un operador con etiqueta y dos campos con punteros a y . crearHoja(, ): crea un nodo para un identificador con etiqueta y un campo que contiene , que es un puntero a la entrada de la tabla de smbolos correspondiente al identificador.

Jacqueline Khler C. - USACH

72

Compiladores , el valor del nmero. Ejemplo 4.8: Sea la expresin 4 . La construccin del AST se lleva a cabo en cinco pasos, en forma ascendente (el rbol resultante se muestra en la figura 4.6): 1. crearHoja(, ) _ 2. crearHoja(, 4) 3. crearNodo( , , ) 4. crearHoja(, )_ 5. crearNodo( , , )
crearHoja(, ) :

crea un nodo para un nmero con etiqueta y un campo que contiene

Donde: son punteros a nodos. _y _son punteros a las entradas de la tabla de smbolos para los identificadores y respectivamente.

FIGURA 4.6: AST para la expresin 4 . Para el caso de sentencias que no corresponden a expresiones se procede en forma similar, como muestra la tabla 4.3.

4.3 COMPROBACIN DE TIPOS


La comprobacin de tipos es la principal tarea del analizador semntico. Su propsito es evaluar cada operador y sus operandos o argumentos, y retornar las declaraciones pertinentes para incorporarlas al AST. Con lo anterior, a continuacin puede generar el cdigo intermedio apropiado (figura 4.7). Ntese que una funcin puede considerarse tambin como un operador. Cada argumento debe ser compatible con el operador. Por ejemplo, si se desea sumar una variable de tipo int a otra de tipo char (guerdando el resultado en esta ltima), los tipos no son compatibles y se debe efectuar una coercin explcita (cast) para convertir el entero en un carcter antes de poder efectuar la operacin. Jacqueline Khler C. - USACH

73

Compiladores

TABLA 4.3: Creacin de nodos del AST para sentencias de flujo de control. Se debe efectuar la comprobacin en cada nodo del AST donde se utilice la informacin de los tipos presentes en una expresin, e informar al usuario en aquellos casos en que no pueda darse una solucin a un conflicto. Este proceso consta de dos partes: Llenar el AST con los tipos correspondientes para los literales (variables o valores). Propagar los tipos por los nodos restantes del AST considerando: Tipos correctos para los operadores y operandos. Tipos correctos para los argumentos de funcin. Tipos correctos de retorno. Aplicar coercin en caso de que los tipos no coincidan.

FIGURA 4.7: Ubicacin de la comprobacin de tipos dentro del proceso de compilacin. Inicialmente se debe recorrer el AST para encontrar todos los identificadores literales o valores que se encuentren presentes y recurrir a la tabla de smbolos para conocer sus tipos. Es recomendable efectuar el recorrido con el algoritmo post-orden, que visita primero las hojas (identificadores literales y valores) y luego los nodos intermedios (operadores), pues as se puede efectuar la comprobacin de tipos en una sola pasada.

Jacqueline Khler C. - USACH

74

Compiladores

Ejemplo 4.9: Considere el AST para la expresin 1.0, que se muestra en la figura 4.8 (a). El recorrido post-orden visita primero las hojas del rbol incorporando su tipo, con lo que se obtiene el AST de la figura 4.8 (b).

FIGURA 4.8: Comprobacin de tipos para la expresin 1.0. (a) AST original. (b) AST con los tipos para valores e identificadores literales. Una vez conocidos los tipos para las hojas del AST, se deben determinar los tipos asociados a los nodos intermedios, es decir, a los operadores. Si todos los operandos son de un mismo tipo, esta tarea resulta ser trivial. No obstante, se debe efectuar una coercin en caso de que los tipos no sean iguales. Para el caso en que los operandos tienen tipos diferentes, una estrategia es construir una tabla de prioridades de conversin. As, para cada operador se determina qu tipo de datos tiene preferencia, vindose en la tabla como el primer tipo que figura para el operador. As, en caso de existir un conflicto de tipos, se escoge el del operando cuyo tipo tiene mayor prioridad. Ejemplo 4.10: Considere nuevamente la expresin 1.0 y sea la tabla de prioridades de conversin que se muestra en la tabla 4.4.

TABLA 4.4: Tabla de prioridades de conversin.

Jacqueline Khler C. - USACH

75

Compiladores La figura 4.9 (a) muestra el AST con los tipos para los valores e identificadores literales obtenido en el ejemplo anterior. Ah se puede observar que el operador de adicin tiene un operando entero y otro real. De acuerdo a la tabla de prioridades de conversin, se determina que el tipo float tiene mayor prioridad que el tipo int. En consecuencia, se utiliza la suma de nmeros reales para efectuar la operacin. Como el resultado de la suma es de tipo float y la variable a la que se asigna este valor es del mismo tipo, no es necesario efectuar ninguna conversin para efectuar la asignacin. La figura 4.9 (b) muestra el AST con los tipos para operandos y operadores.

FIGURA 4.9: Comprobacin de tipos para la expresin 1.0. (a) AST con los tipos para valores e identificadores literales. (b) AST con los tipos propagados a los nodos intermedios. Si en el ejemplo 4.10 la variable fuese de tipo float y la suma de tipo int, de acuerdo a la tabla de prioridades de conversin se tiene que la suma debe ser llevada al tipo float. No ocurre lo mismo, sin embargo, si la variable ha sido declarada con un tipo de menor prioridad que el valor a asignarle. En este caso se produce un error que debe ser notificado al usuario, pues no es posible cambiar el tipo original con que una variable fue declarada. Ahora bien, aunque ya se ha determinado el tipo para los operadores, an no es posible efectuar la operacin pues los operandos siguen teniendo tipos diferentes. Para igualar los tipos de los operandos es necesario llevar a cabo una conversin de tipos, llamada coercin. Para determinar la conversin automtica de un tipo a otro se construye una tabla de coercin, que indica qu nodo es necesario agregar al AST para llevar a cabo la conversin. Estos nodos que se agregan se llaman nodos de coercin. La conversin de tipos, no obstante, no puede ser arbitraria. Debe tener en consideracin el rango de valores posibles (por ejemplo, char int float). Ejemplo 4.11: Continuando con la expresin 1.0, sea la tabla de coerciones que se muestra en la tabla 4.5. En la figura 4.10 (a) se puede observar la necesidad de llevar el tipo de la variable b a float en lugar de int. Esta coercin es incorporada en la figura 4.10 (b).

Jacqueline Khler C. - USACH

76

Compiladores

TABLA 4.5: Tabla de coerciones.

FIGURA 4.10: Comprobacin de tipos para la expresin 1.0. (a) AST con los tipos propagados a los nodos intermedios. (b) AST con conversin de tipos.

4.4 OTRAS COMPROBACIONES SEMNTICAS


An cuando la comprobacin de tipos se haya completado exitosamente, pueden existir otros errores an no detectados: Cdigo inalcanzable. Retorno de funciones cuyo tipo es distinto de void. Chequeo de etiquetas case en switch. El lado izquierdo de una asignacin no puede ser una funcin. Cuando un goto es encontrado, la etiqueta debe existir. Revisar parmetros de las funciones.

4.4.1 VALORES DEL LADO IZQUIERDO En esta etapa se comprueba que las asignaciones efectuadas sean vlidas. La tabla 4.6 muestra un conjunto de asignaciones vlidas e invlidas, donde f() es una funcin.

Jacqueline Khler C. - USACH

77

Compiladores Se define lvalue como la abreviacin de left hand value, es decir, valor del lado izquierdo. Corresponde a una expresin o referencia que puede ser puesta en el lado izquierdo de una asignacin. Para que un lvalue sea vlido, debe ser una entidad modificable (como por ejemplo una variable).

TABLA 4.6: Asignaciones vlidas e invlidas. Una estrategia para determinar si una asignacin es vlida o no consiste en crear una lista de lvaues vlidos y despus comprobar si cada lvalue presente en el AST es o no vlido de acuerdo a la lista, usando para ello el algoritmo 4.2. ALGORITMO 4.2: Comprobar validez de asignaciones.
Situarse en la raz del AST. Para cada nodo en el AST hacer: Si el nodo contiene un operador =: hacer: Comprobar el lvalue del hijo ms izquierdo. Si es vlido hacer: Ir al siguiente nodo. En otro caso hacer: Reportar error. En otro caso hacer: Ir al siguiente nodo.

4.4.2 PARMETROS DE FUNCIN Esta comprobacin se encarga de verificar diferentes aspectos relacionados con las funciones: Que la cantidad de parmetros sea correcta. Que los tipos de los parmetros sean correctos. Que no existan mltiples main. Para estas comprobaciones se utiliza una lista cuyos elementos son una estructura con los siguientes elementos: Nombre de la funcin. Cantidad de parmetros. Lista con el nombre y tipo de cada parmetro. La obtencin de los encabezados de funcin y de la lista de parmetros se efecta a partir del AST. Ejemplo 4.12: Considrense las siguientes funciones:
function suma(int a, int b) {} function minimo(int a, int b, int c) {} function ordenar(float a, float b, float c, float d) {}

Jacqueline Khler C. - USACH

78

Compiladores La figura 4.11 muestra la lista para efectuar la comprobacin al momento de usar dichas funciones.

FIGURA 4.11: Lista para comprobar parmetros de funciones

4.4.3 PALABRA CLAVE return En el caso de aquellas funciones cuyo tipo de retorno sea distinto de void y puede, en consecuencia, ser asignado a alguna variable, es necesario verificar que el tipo retornado sea correcto. En consecuencia, es necesario comprobar que toda funcin que deba retornar algo finalice con una sentencia con la palabra clave return. Si no se encuentra dicha sentencia, se debe notificar al usuario por medio de una advertencia (warning). Para evitar estos conflictos, se debe recorrer el bloque de cdigo de la funcin y verificar la existencia de una sentencia con la palabra clave return. Esta tarea se efecta recorriendo el AST con el algoritmo pre-orden, buscando la palabra return en todo el bloque de cdigo de la funcin. Cuando la sentencia de retorno se encuentre en un bloque if-then-else se debe comprobar la existencia de una sentencia de retorno en ambos sub-bloques de cdigo (o bien fuera del bloque), ya que se ejecutan condicionalmente. Se debe proceder de manera similar para la sentencia switch. Otra comprobacin que se debe efectuar es verificar que, si corresponde, se retorne un valor que indique que se produjo un error. El no sealar un error puede ocasionar consecuencias indeseadas a raz de una ejecucin errnea del programa.

Jacqueline Khler C. - USACH

79

Compiladores Por ltimo se debe verificar que no exista cdigo inalcanzable, que corresponde al cdigo situado despus de la sentencia de retorno.

4.4.4 CASOS DUPLICADOS EN UN switch Una sentencia switch siempre contiene uno o ms bloques de cdigo: uno por cada caso definido y, eventualmente, un bloque por defecto que se ejecutar en cualquier otro caso. No obstante, tambin es sintcticamente correcto definir mltiples bloques para un mismo caso, es decir, tener valores de casos duplicados. En tal caso no hay forma de determinar cul de los bloques se debe ejecutar, por lo que se debe advertir al usuario (warning). Generalmente, ante este conflicto se genera el cdigo correspondiente al primer bloque para el valor de caso en cuestin. La comprobacin para este error es muy sencilla y trabaja de manera recursiva. Se recorre el AST comenzando por la raz, en busca de todos los nodos switch. Por cada nodo switch que se encuentre, se examinan sus nodos hijos (uno por cada bloque correspondiente a un caso) para ver si existe algn caso duplicado. En caso afirmativo, se genera la advertencia correspondiente y se contina con el anlisis.

4.4.5 ETIQUETAS goto Muchas veces el uso de la sentencia goto se traduce en programas con un comportamiento extrao y no deseado. Al igual que ocurre con las variables, es necesario declarar una etiqueta (label) goto para poder usarla. Una implementacin adecuada para comprobar que existan las etiquetas goto es incorporar dichas etiquetas en la tabla de smbolos. A continuacin se debe recorrer el AST y, por cada nodo goto, comprobar si la etiqueta de destino se encuentra en la tabla de smbolos. En caso de no encontrarse la etiqueta, se debe reportar el error.

4.5 EJERCICOS
1. Considere la tabla de prioridades y la tabla de coercin que se muestran en las tablas 4.7 y 4.8. Suponga, para este ejercicio, que la precedencia de los operadores, de mayor a menor, es: ^, /, , . El operador ^ corresponde a la exponenciacin. Sea la expresin : ^/ / ^ , donde es de tipo Complex; , , , y son de tipo Natural; e son de tipo Int; es de tipo Float. a. Construya el AST para la expresin dada. b. Construya el AST con coerciones y etiquetas de tipos. c. Indique si existe algn tipo de error semntico en la expresin dada y justifique su respuesta. d. Qu ocurrira si ahora es de tipo Int? Por qu? e. Qu ocurrira si ahora es de tipo Complex y es de tipo String? Por qu?

Jacqueline Khler C. - USACH

80

Compiladores

TABLA 4.7: Tabla de prioridades para los ejercicios 1 y 2.

TABLA 4.8: Tabla de coerciones para los ejercicios 1 y 2. 2. Considere la tabla de prioridades y la tabla de coercin que se muestran en las tablas 4.7 y 4.8. Considere adems las mismas operaciones y precedencia de operadores del ejercicio 1. Sea la expresin : /^ ^/ , donde es de tipo Complex; , y son de tipo Float; , , y son de tipo Int; , y son de tipo Natural. a. Construya el AST para la expresin dada. b. Construya el AST con coerciones y etiquetas de tipos. c. Indique si existe algn tipo de error semntico en la expresin dada y justifique su respuesta.

Jacqueline Khler C. - USACH

81

Compiladores 3. Dado el fragmento de cdigo del listado 4.1, efecte las siguientes comprobaciones semnticas: a. Cdigo inalcanzable. b. Retorno de funciones cuyo tipo es distinto de void. c. Cuando un goto es encontrado, la etiqueta debe existir. d. Que no haya trazas para las que no haya un retorno. LISTADO 4.1: Fragmento de cdigo para el ejercicio 3.
int pvoiAgregarPaquete(listaPaquete *plpqLista, int pintPaquete) { numeroPaquete *pnpqAuxiliar = (numeroPaquete *) malloc(sizeof(struct numeroPaquete)); pnpqAuxiliar->intNumero = pintPaquete; // si la lista esta vacia if (fbooVaciaListaPaquete(plpqLista)) { pnpqAuxiliar->anterior = NULL; pnpqAuxiliar->siguiente = NULL; plpqLista->primero = pnpqAuxiliar; plpqLista->ultimo = pnpqAuxiliar; return; printf("La lista fue creada exitosamente."); } // no esta vacia numeroPaquete *indice = plpqLista->primero; numeroPaquete *antecesor = NULL; while (indice) { 1: if (indice->intNumero < pintPaquete) { antecesor = indice; indice = indice->siguiente; } else { pnpqAuxiliar->siguiente = indice; plpqLista->primero = pnpqAuxiliar; return; } } pnpqAuxiliar->siguiente = NULL; plpqLista->ultimo->siguiente = pnpqAuxiliar; plpqLista->ultimo = plpqLista->ultimo->siguiente; goto 1 }

4. Dadas las siguientes funciones, dibuje la estructura de datos que permite efectuar la comprobacin de sus parmetros.
elipse(float eje1, float eje2, int x0, int y0, float orientacion). circulo(float radio, int x0, int y0) .

Jacqueline Khler C. - USACH

82

Compiladores

5 AMBIENTES PARA EL MOMENTO DE EJECUCIN


Antes de generar el cdigo objeto es necesario relacionar el cdigo fuente esttico de un programa con la acciones que deben ocurrir en el momento de ejecucin. Se debe tener tambin en consideracin que un mismo nombre puede tener distintos significados en fragmentos diferentes del cdigo (por ejemplo, tipos de datos diferentes en distintas funciones). En este captulo se estudia la problemtica de la asignacin y desasignacin de objetos.

5.1 ASPECTOS DEL LENGUAJE FUENTE


En esta seccin se distingue entre el cdigo fuente de un procedimiento o funcin y las actividades que ste realiza durante la ejecucin. Se llamar funcin a cualquier procedimiento que devuelva valores.

5.1.1 PROCEDIMIENTOS Una definicin de un procedimiento es una declaracin que, en su forma ms bsica, asocia un identificador con una proposicin. El identificador se denomina nombre del procedimiento, mientras la proposicin conforma su cuerpo. Cuando aparece el nombre de un procedimiento dentro de una proposicin ejecutable, se dice que el procedimiento es llamado. Cuando esto ocurre, la llamada provoca que el procedimiento se ejecute. Es importante recordar que las llamadas a procedimientos tambin pueden ocurrir dentro de expresiones. En la definicin de un procedimiento suelen aparecer diversos identificadores. Estos identificadores son los parmetros formales del procedimiento. Los parmetros actuales, en cambio, son los argumentos entregados a un procedimiento cuando es llamado. Estos valores son sustituidos por los parmetros formales.

5.1.2 RBOLES DE ACTIVACIN Durante la ejecucin de un programa rigen ciertos supuestos sobre el sobre el flujo de control: 1. El control fluye secuencialmente. 2. Cada ejecucin de un procedimiento comienza al inicio del cuerpo de ste y en algn momento devuelve el control al punto situado inmediatamente despus de la llamada. Cada ejecucin del cuerpo de un procedimiento se denomina activacin. Se denomina duracin de la activacin a la secuencia de pasos entre el primer y el ltimo paso de la ejecucin del procedimiento, incluyendo llamadas a otros procedimientos llamados por l. Si un procedimiento llama a otra instancia de l mismo, se dice que es recursivo. Es posible representar grficamente el flujo de control de un programa mediante un rbol de activacin.

Jacqueline Khler C. - USACH

83

Compiladores En un rbol de activacin: Cada nodo representa la activacin de un procedimiento. La raz representa la activacin del programa principal. El nodo es el padre del nodo si y solo si el control fluye de la activacin a la . El nodo est a la izquierda del nodo si y slo si la duracin de ocurre antes que la de . Ejemplo 5.1: La figura 5.1 muestra el rbol de activacin correspondiente al programa en Pascal del listado 5.1. LISTADO 5.1: Programa en Pascal que lee y ordena enteros.
Program ordenamiento(input, output); var a: array[0..10] of integer; procedure leemartriz; var i: integer; begin for i:=1 to 9 do read(a[i]) end; function particion(y, z: integer): integer; var i, j, x, v: integer; begin end; procedure clasificacion_por_particiones(m, n: integer); var i: integer; begin if(n>m) then begin i:=particion(m, n); clasificacion_por_particiones(m, i-1); clasificacion_por_particiones(i+1, n); end end; begin a[0]:=-9999; a[10]:=9999; leematriz; clasificacion_por_particiones(1, 9); end.

5.1.3 PILAS DE CONTROL El flujo de control del programa corresponde al recorrido en profundidad del rbol de activacin. Se comienza desde la raz y se visita cada nodo antes que a sus hijos. Los hijos son visitados recursivamente de izquierda a derecha. La pila de control permite llevar un registro de las activaciones de los procedimientos en curso. Se introduce el nodo a la pila cuando comienza su activacin y se saca cuando sta termina. Los Jacqueline Khler C. - USACH

84

Compiladores contenidos de la pila se relacionan con los caminos hasta la raz del rbol de activaciones: cuando un nodo n est al tope de la pila de control, la pila contendr a todos los nodos en el camino desde n hasta la raz.

FIGURA 5.1: rbol de activacin correspondiente a la salida de la ejecucin del programa del listado 5.1. Ejemplo 5.2: La figura 5.2 muestra la pila de control para un instante dado de la ejecucin del programa del listado 5.1, demarcado por las lneas continuas del rbol de activacin.

FIGURA 5.2: Pila de control para el momento de ejecucin demarcado con lneas continuas en el rbol de activacin.

Jacqueline Khler C. - USACH

85

Compiladores 5.1.4 MBITO DE UNA DECLARACIN Una declaracin es una construccin sintctica que asocia informacin a un nombre. Por otra parte, toda declaracin tiene un mbito, es decir, una parte del programa donde puede ser aplicada. Pueden existir diferentes declaraciones de un mismo nombre en distintas partes de un programa. En este caso, son las reglas de mbito del lenguaje fuente las que determinan qu declaracin utilizar al encontrarse el nombre en el programa fuente. Se dice que un nombre es local a un procedimiento si ha sido declarado al interior de ste. En caso contrario, se dice que no es local. Al momento de compilar se puede usar la tabla de smbolos para encontrar la declaracin que aplica a un nombre. Al declararse un nombre, se crea la entrada correspondiente en la tabla de smbolos. Se retornar dicha entrada al buscar el nombre mientras dure el mbito de la declaracin.

5.1.5 ENLACE DE NOMBRES Aunque un nombre se declare solo una vez en el programa, ese nombre puede indicar diferentes objetos de datos durante la ejecucin. Un objeto de datos se refiere a una posicin de memoria que puede contener valores. Se define como ambiente una funcin que transforma un nombre en una posicin de memoria. Por otra parte, estado es una funcin que transforma una posicin de memoria en el valor en ella contenido. Ntese que ambas funciones son diferentes: una asignacin modifica el estado, pero no el ambiente. Estos conceptos se ilustran en la figura 5.3. Se dice que un nombre x est enlazado a una posicin de memoria s cuando est asociado a dicha posicin de memoria. Un enlace es la contrapartida dinmica de una declaracin (puede haber ms de una activacin de un procedimiento recursivo), como se muestra en la tabla 5.1.

FIGURA 5.3: Nociones estticas y dinmicas correspondientes.

TABLA 5.1: Nociones estticas y dinmicas correspondientes.

Jacqueline Khler C. - USACH

86

Compiladores

5.2 ORGANIZACIN DE LA MEMORIA


5.2.1 SUBDIVISIN DE LA MEMORIA DURANTE LA EJECUCIN Un compilador trabaja con un bloque de memoria asignado por el sistema operativo. Esta memoria debe ser subdividida para que pueda albergar diferentes elementos (ver figura 5.4): El cdigo objeto generado. Los objetos de datos. Una contrapartida de la pila de control para registrar las activaciones de procedimientos.

FIGURA 5.4: Subdivisin tpica de la memoria durante la ejecucin. El cdigo objeto generado tiene un tamao fijo al momento de la compilacin, por lo que se puede colocar estticamente en una zona de la memoria. Lo mismo ocurre con algunos objetos de datos. Resulta conveniente asignar en forma esttica la mayor cantidad posible de datos, pues as stos pueden ser compilados al cdigo objeto. La pila de control se almacena en una porcin diferente de memoria. Se deja adems un bloque llamado montculo, que almacena toda la informacin restante. Tanto la pila como el montculo tienen tamaos variables, por lo que se sitan en extremos opuestos de la memoria a fin de que ambas puedan crecer segn sea necesario.

5.2.2 REGISTROS DE ACTIVACIN Los registros de activacin son bloques contiguos de memoria que almacenan toda la informacin necesaria para una sola ejecucin de un cierto procedimiento. La figura 5.5 muestra los campos habituales de un registro de activacin, aunque no todos los lenguajes ni todos los compiladores hacen uso de todos esos campos. Los tamaos de cada uno de estos campos pueden ser determinados en el momento en que es llamado un procedimiento o incluso durante la compilacin. La nica excepcin se produce cuando el procedimiento contiene una matriz local cuyo tamao venga dado por un parmetro actual.

Jacqueline Khler C. - USACH

87

Compiladores

Figura 5.5: Un tpico registro de activacin. Los diferentes campos del registro de activacin pueden describirse como sigue: Valor devuelto: sirve para devolver un valor al autor de la llamada. Para mayor eficiencia, se suele trasladar este valor a un registro de la mquina. Parmetros actuales: este campo es utilizado por el autor de la llamada para proporcionar parmetros al procedimiento llamado. Es habitual, no obstante, pasar los parmetros por medio de un registro. Enlace de control opcional: apunta al registro de activacin del autor de la llamada. Enlace de acceso opcional: sirve para hacer referencia a los datos no locales guardados en otros registros de activacin. Estado guardado de la mquina: mantiene la informacin del estado de la mquina justo antes de que el procedimiento fuese llamado (valor del contador del programa, valores de los registros, etc.). Estos valores deben reponerse cuando el control regresa al procedimiento llamador. Datos locales: guarda datos locales a la ejecucin del procedimiento. Temporales: almacena valores temporales como los que surgen de la evaluacin de expresiones.

5.2.3 DISPOSICIN ESPACIAL DE LOS DATOS LOCALES EN EL MOMENTO DE LA COMPILACIN Se considera el byte como la mnima unidad de memoria direccionable. En muchas mquinas se considera tambin el concepto de palabras de mquina, donde cada una de estas palabras est conformadas por un cierto nmero de bytes consecutivos. Estas palabras suelen usarse para almacenar objetos de tamao superior a un byte. En tal caso, la direccin asignada al objeto es la del primer byte. Jacqueline Khler C. - USACH

88

Compiladores La cantidad de memoria que se asigna a un nombre viene dada por el tipo de ste. Los tipos de datos elementales generalmente pueden ser almacenados en un nmero entero de bytes. En el caso de datos agregados, como matrices y registros, se debe asignar un bloque de memoria lo suficientemente grande como para almacenar todos los componentes. Lo ms usual es que se asignen bloques contiguos de bytes para facilitar el acceso a cada uno de los datos. En la seccin anterior se mostr el registro de activacin. En l, el campo para los datos locales se determina durante la compilacin, a medida que se examinan las declaraciones al interior del procedimiento (no se incluyen aqu los datos cuya longitud es variable). La direccin relativa o desplazamiento de un valor local con respecto a una posicin de memoria, como el primer byte del registro de activacin, es la diferencia entre la posicin del objeto y la direccin considerada como referencia.

5.3 ESTRATEGIAS PARA LA ASIGNACIN DE MEMORIA


Existen diferentes tcnicas de asignacin de memoria para cada una de las reas de datos de la figura 5.5. 5.3.1 ASIGNACIN ESTTICA Este tipo de asignacin se encarga de disponer la memoria para todos los objetos de datos durante el proceso de compilacin. Esto se realiza enlazando los nombres a las posiciones de memoria. La asignacin de memoria se efecta tal como se describi en la seccin 5.2.3. El compilador debe decidir la ubicacin de los registros de activacin con respecto al cdigo objeto y a los dems registros de activacin, con lo que la posicin de cada uno de dichos registros queda determinada y, en consecuencia, quedan determinadas tambin las posiciones de cada nombre dentro del registro. Todo lo anterior hace posible que sea posible entregar al cdigo objeto, durante la compilacin, las posiciones de memoria donde se encuentran los valores que requiere para su operacin y las direcciones donde se almacena la informacin al producirse una llamada a un procedimiento. Como los enlaces no cambian durante la ejecucin, cada vez que se activa un procedimiento sus nombres se enlazan a las mismas posiciones de memoria. Esta caracterstica hace posible que los valores de los nombres locales sean retenidos de una activacin a otra. No obstante, esta tcnica de asignacin tiene asociadas algunas limitaciones: El tamao de un objeto de datos y sus limitaciones en cuanto a ubicacin deben ser conocidos en el momento de la compilacin. Es muy difcil crear procedimientos recursivos, pues todas las activaciones hacen uso de los mismos enlaces para los nombres locales. No es posible crear estructuras de datos dinmicamente porque no existe un mecanismo de asignacin de memoria durante la ejecucin.

Jacqueline Khler C. - USACH

89

Compiladores 5.3.2 ASIGNACIN POR MEDIO DE UNA PILA Este tipo de asignacin est basado en la idea de una pila de control. Se da a la memoria la organizacin de una pila, y en ella se agregan y se retiran los registros de activacin cuando las llamadas a procedimientos comienzan y terminan, respectivamente. La figura 5.6 muestra un ejemplo de este de asignacin mediante una pila. Este esquema de asignacin permite que para cada activacin las variables locales se enlacen a nueva memoria, puesto que se introduce un nuevo registro de activacin a la pila. Adems, al terminar la activacin las variables locales son eliminadas pues se quita el registro de activacin de la pila. En este punto es importante definir un problema muy habitual: las referencias suspendidas, que se producen cuando se hace referencia a memoria desasignada. Este tipo de error corresponde a un error lgico y no puede ser detectado mediante ninguno de los analizadores que se emplean en un compilador. Esto se debe a que los lenguajes no consideran en su semntica el valor de la memoria desasignada y a que muchas veces la memoria puede asignarse posteriormente a otro dato y crear as errores ocultos en el programa. La asignacin por medio de una pila no puede utilizarse cuando ocurre alguna de las siguientes situaciones, en que la desasignacin de memoria no tiene por qu ser de la forma ltimo en entrar, primero en salir: Se debe retener los valores de los nombres locales cuando finaliza una activacin. Una activacin llamada sobrevive al autor de la llamada. Este caso no es posible en aquellos lenguajes en que los rboles de activacin representan correctamente el flujo de control.

5.3.3 ASIGNACIN POR MEDIO DE UN MONTCULO La asignacin por medio de un montculo divide partes de memoria contigua, conforme las necesiten los registros de activacin u otros objetos. Las distintas partes se pueden desasignar en cualquier orden, de modo que con el paso del tiempo el montculo constar de reas libres y ocupadas. En consecuencia, se debe tener mucho cuidado con el manejo del montculo. Ms adelante se muestran algunas tcnicas adecuadas.

5.4 ACCESO A NOMBRES NO LOCALES


El enfoque para tratar las referencias a nombres no locales viene dado por las reglas de mbito de un lenguaje. Las reglas de mbito lxico o mbito esttico, usadas por lenguajes como pascal, C y Ada, determinan la declaracin que se aplica a un nombre solo con examinar el texto del programa. Hacen esto en conjunto con una estipulacin de anidamiento ms cercano, como se ver ms adelante. Las reglas de mbito dinmico en cambio, usadas por lenguajes como Lisp y Snobol, determinan la declaracin que se aplica a un nombre durante la ejecucin, tomando en consideracin las actividades en curso.

Jacqueline Khler C. - USACH

90

Compiladores

FIGURA 5.6: Asignacin de registros de activacin por medio de una pila que crece hacia abajo.

5.4.1 BLOQUES Un bloque es una proposicin que contiene sus propias declaraciones de datos locales. En C, por ejemplo, un bloque tiene la siguiente sintaxis: {declaraciones proposiciones}

Jacqueline Khler C. - USACH

91

Compiladores Una caracterstica de los bloques es su estructura de anidamiento. Los delimitadores marcan el inicio y el fin de un bloque (llaves en C). stos garantizan que un bloque sea independiente de otro o bien que est anidado dentro de otro. Esta propiedad de anidamiento recibe el nombre de estructura de bloques. En un lenguaje con estructura de bloques el mbito de una declaracin viene dado por la regla de anidamiento ms cercano. sta se ejemplifica en la figura 5.7, donde se muestra un programa en C y se sealan claramente los bloques y el mbito de cada variable. La regla de anidamiento ms cercano es: 1. El mbito de una declaracin en un bloque B incluye B. 2. Si un nombre x no est declarado en un bloque B, entonces un caso de x en B est en el mbito de una declaracin de x en un bloque abarcador B que cumple las siguientes condiciones: a. B tiene una declaracin x. b. B est anidado ms cerca alrededor de B que cualquier otro bloque con una declaracin de x.

FIGURA 5.7: Bloques y mbito de las variables para un programa en C. Ntese que un bloque no es lo mismo que un procedimiento. Estos ltimos son ms simples, pues no hay paso de parmetros y el control cumple las siguientes condiciones: Fluye a un bloque desde el punto inmediatamente anterior a l en el texto fuente. Fluye desde el bloque al punto inmediatamente posterior a l en el texto fuente. Existen diferentes formas para implementar la estructura de bloques. Una de ellas es usar una pila.

5.4.2 MBITO LXICO SIN PROCEDIMIENTOS ANIDADOS Algunos lenguajes tienen reglas de mbito lxico ms complejas que otros. Por ejemplo, C no permite anidar procedimientos, mientras que Pascal si lo permite. Esto significa que en C no es posible definir un procedimiento dentro de otro y, en consecuencia, en caso de existir una referencia no local el Jacqueline Khler C. - USACH

92

Compiladores nombre debe declararse fuera de cualquier procedimiento. En este caso, el mbito de una declaracin hecha fuera de alguna funcin consta de los cuerpos de todas las funciones que aparezcan despus de dicho nombre, excepto aquellas funciones que contengan una declaracin homnima. En ausencia de procedimientos anidados es posible utilizar el esquema de asignacin mediante pilas para los nombres locales, pues es posible asignar estticamente la memoria para todos los nombres declarados fuera de cualquier procedimiento. Esto se debe a que la posicin de cada uno de estos nombres es conocida al momento de la compilacin. Una ventaja de este esquema es que los nombres no locales pueden pasarse como parmetros y devolverse como resultado.

5.4.3 MBITO LXICO CON PROCEDIMIENTOS ANIDADOS Lenguajes como Pascal difieren de C en el hecho de que es posible declarar un procedimiento dentro de otro. Un ejemplo de esto es el cdigo que se muestra en el listado 5.1. Aqu, un caso no local de un nombre a se encuentra dentro del alcance de la declaracin anidada ms cercana de a en el texto del programa fuente. En este tipo de lenguajes se tiene tambin que la regla de anidamiento ms cercano se aplica tambin a los nombres de procedimientos. Cuando se trabaja con procedimientos anidados se debe manejar un nuevo concepto: profundidad de anidamiento. La profundidad del programa principal se define como 1, y cada vez que de un procedimiento se pase a otro abarcado por l se suma 1 a la profundidad. Por ejemplo, en el listado 5.1 la funcin particin est a profundidad de anidamiento 3. Una manera directa de implementar el mbito lxico para procedimientos anidados se obtiene al incorporar a cada registro de activacin un puntero llamado enlace de acceso. As, si un procedimiento est anidado inmediatamente dentro de c en el programa fuente, entonces el enlace de acceso de un registro de activacin para apunta al enlace de acceso del registro de activacin ms reciente de . Esto puede verse ms claramente en la figura 5.8.

5.4.4 MBITO DINMICO En este caso, una nueva activacin hereda los enlaces ya existentes entre nombres no locales y la memoria. Existen dos enfoques para implementar el mbito dinmico: acceso profundo y acceso superficial. Acceso profundo: en este caso se prescinde de los enlaces de acceso y se utiliza el control para buscar el primer registro de activacin que contenga el nombre no local. El nombre se refiere a que se efecta una bsqueda en profundidad al interior de la pila y no es posible determinar la profundidad durante la compilacin. Acceso superficial: en este caso se conserva el valor en curso de cada nombre en memoria asignado estticamente. As, cuando se lleva a cabo una nueva activacin de un procedimiento, un nombre al Jacqueline Khler C. - USACH

93

Compiladores interior de l usar la memoria asignada estticamente para dicho nombre. En este caso es necesario guardar el valor previo de para restaurarlo una vez terminada la ejecucin del procedimiento.

FIGURA 5.8: Enlaces de acceso para encontrar las posiciones de memoria de los nombres no locales.

5.5 PASO DE PARMETROS


Cuando un procedimiento llama a otro, el mtodo habitual de comunicacin entre ellos es a travs de nombres no locales y de parmetros que se pasan al procedimiento llamado. Un ejemplo de esto aparece en el listado 5.2, donde y son parmetros, mientras es un nombre no local. LISTADO 5.2: Procedimiento en Pascal que opera con parmetros y nombres no locales.
procedure intercambio(i, j: integer) var x: integer begin x := a[i]; a[i] := a[j]; a[j] := x end.

Existen diferentes mtodos para asociar parmetros actuales y formales, de los cuales se estudiarn solo los tres primeros por ser de uso ms frecuente: Llamada por valor. Llamada por referencia. Copia y restauracin. Llamada por nombre o macroexpansin.

Jacqueline Khler C. - USACH

94

Compiladores Es importante conocer el mtodo de paso de parmetros que utiliza un lenguaje (o compilador), pues el resultado de un programa puede depender del mtodo empleado.

5.5.1 LLAMADA POR VALOR Es el mtodo ms sencillo para pasar parmetros. Aqu se evalan los parmetros actuales y se pasan sus valores de lado derecho al procedimiento llamado. Una posible implementacin para la llamado por valor es la siguiente: 1. Un parmetro formal se considera como un nombre local, de modo que las direcciones de memoria para los parmetros formales se encuentran en el registro de activacin del procedimiento llamado. 2. El procedimiento autor de la llamada evala los parmetros actuales y coloca sus valores de lado derecho en las direcciones de memoria de los parmetros formales. Un ejemplo para este esquema de paso de parmetros se muestra en el listado 5.3. La ejecucin de la llamada permuta(a, b) es equivalente a la siguiente secuencia de pasos:
x := y := temp x := y := a b := x y temp

LISTADO 5.3: Programa en Pascal con un procedimiento que recibe parmetros por valor.
program referencia(input, output); var a, b: integer; procedure permuta(var x, y: integer); var temp: integer; begin temp := x; x := y; y := temp; end; begin a := 1; b := 2; permuta(a,b); writeln(a = ,a); writeln(b =,b); end.

Una caracterstica distintiva de la llamada por valor es que las operaciones sobre los parmetros formales no afectan a los valores en el registro de activacin del autor de la llamada. El listado 5.4 muestra un segundo ejemplo, esta vez en C, donde se hace uso de punteros para pasar parmetros por valor. En este caso se emula el comportamiento de la llamada por referencia.

Jacqueline Khler C. - USACH

95

Compiladores LISTADO 5.4: Programa en C con un procedimiento que usa punteros y llamada por valor.
void permuta(x,y) { int *x, *y; int temp; temp = *x; *x = *y; *y = temp; } main() { int a = 1, b = 2; permuta(&a, &b); printf(a es ahora %d, b es ahora %d\n, a, b); }

5.5.2 LLAMADA POR REFERENCIA Cuando se pasan parmetros por referencia, el autor de la llamada pasa al procedimiento llamado un puntero a la direccin de memoria de cada parmetro actual: 1. Si un parmetro actual es un nombre o una expresin que tenga un valor de lado izquierdo (direccin en memoria), entonces se pasa ese mismo valor de lado izquierdo. 2. Sin embargo, si el parmetro actual es una expresin, como a + b 2, que no tiene ningn valor de lado izquierdo, entonces la expresin se evala en una nueva posicin y se pasa la direccin de dicha posicin. Para un ejemplo de la llamada por referencia, considrese la funcin en C del listado 5.5. En este caso, al efectuar la llamada cuadrado(3, y) se almacena el valor 9 en la variable y. Este estilo de paso de parmetros resulta muy til para poder retornar valores de forma implcita. LISTADO 5.5: Programa en C que pasa parmetros por referencia.
void cuadrado(int x, int& result) { result = x*x; }

5.5.3 COPIA Y RESTAURACIN Este mtodo de paso se parmetros es un hbrido entre las llamadas por valor y por referencia. Opera en dos pasos, que se muestran a continuacin: 1. Los parmetros actuales se evalan antes de que el control fluya al procedimiento llamado. Los valores del lado derecho de stos se pasan a dicho procedimiento al igual que en la llamada por valor. La diferencia radica en que, cuando es posible, los valores de lado izquierdo (posiciones de memoria) de los parmetros actuales se determinan antes de la llamada. 2. Cuando el control retorna, se copian los valores de lado derecho en curso para los parmetros formales en los valores de lado izquierdo de los parmetros actuales, para lo cual se utilizan los

Jacqueline Khler C. - USACH

96

Compiladores valores de lado izquierdo calculados antes de la llamada. Esta copia se efecta solo para aquellos parmetros actuales con valor de lado izquierdo (es decir, aquellos parmetros que son un nombre y, por ende, tienen un enlace en memoria). El listado 5.6 muestra un procedimiento en Pascal en que el resultado cambia segn qu mtodo de paso de parmetros se emplee. En este caso, existen dos maneras de acceder a la posicin de a en el registro de activacin de copiaafuera() cuando ste llama a inseguro(): como nombre no local o mediante el parmetro formal x. As, al usar las formas mencionadas de paso de parmetros se obtienen los siguientes resultados: Llamada por referencia: en este caso, las asignaciones hechas a x y a a afectan de inmediato a a, por lo que el valor final para este nombre que se muestra por pantalla es 0. Copia y restauracin: aqu el valor 1 del parmetro actual a se copia en el parmetro formal x. Justo antes de terminar la ejecucin de inseguro(), se copia el valor final de x (que es 2) en el valor de lado izquierdo de a, por lo que el valor para este nombre que se muestra por pantalla es 2. LISTADO 5.6: Programa en Pascal para el cual cambian los resultados segn si se pasan parmetros por referencia o mediante copia y restauracin.
program copiaafuera(input,output); var a : integer; procedure inseguro(var x : integer); begin x := 2; a := 0 end begin a := 1; inseguro(a); writeln(a) end.

5.6 TABLA DE SMBOLOS


Durante el proceso de compilacin se requiere almacenar en memoria los diferentes nombres (variables, funciones, etc.) declarados en un programa fuente, pues es necesario tener la capacidad de identificarlos para poder efectuar acciones tales como asignaciones o llamadas a funciones. En otras palabras, se necesita una estructura que permita llevar un registro de la informacin sobre el mbito y el enlace de los nombres; si corresponden a variables, conocer su valor, su tipo o dnde se encuentran almacenadas; para funciones, determinar si han sido declaradas previamente, etc. Esta estructura recibe el nombre de tabla de smbolos. Cada vez que se encuentra un nombre en el programa fuente se debe examinar esta tabla y, si es un nombre nuevo, se crea una nueva entrada; si se encuentra nueva informacin sobre un nombre ya existente, se debe incorporar dicha informacin a la entrada correspondiente. En consecuencia, es necesario implementar la tabla de smbolos que permita aadir nuevas entradas y encontrar las ya existentes de manera eficiente.

Jacqueline Khler C. - USACH

97

Compiladores 5.6.1 ENTRADAS DE LA TABLA DE SMBOLOS Como se puede desprender de la descripcin anterior, cada entrada de la tabla de smbolos corresponde a la declaracin de un nombre. Ahora bien, los nombres pueden corresponder a distintos tipos de datos o a funciones y procedimientos, por lo que no todos los nombres tendrn los mismos atributos. Esto hace que sea difcil e ineficiente crear un registro de tamao uniforme para poder almacenar la informacin para cada tipo de datos, por lo que una buena alternativa es almacenar esta informacin en algn lugar de la memoria fuera de la tabla de smbolos y en sta mantener un puntero a dicha informacin. Otro problema importante en la construccin de la tabla de smbolos es que no todas las entradas se agregan a la vez: Si el analizador lxico no reconoce palabras clave o reservadas, stas deben estar presentes en la tabla de smbolos antes de comenzar esta etapa de anlisis. Una entrada de la tabla de smbolos solo puede establecerse cuando se conoce claramente el papel que juega el nombre en el programa y los valores de los atributos solo se van agregando a medida que se conoce esa informacin. Cuando un nombre puede ocuparse solo una vez, es posible crear la entrada de la tabla de smbolos durante el anlisis sintctico. En otro caso, las diferentes entradas para un mismo nombre se van creando a medida que se descubre el rol sintctico de cada instancia del nombre. Los atributos de los smbolos se introducen frecuentemente como consecuencia de su declaracin, que puede ser implcita. Adems, es la sintaxis de las declaraciones de procedimientos la que especifica que algunos identificadores corresponden a parmetros formales. Un ltimo problema a considerar es que los nombres, vistos como lexemas, pueden ser difciles de manejar debido a las diferentes longitudes de los nombres. En consecuencia, se debe implementar alguna representacin de un nombre de longitud fija que pueda ser incorporado en la tabla de smbolos. No obstante, se debe tener cuidado de conservar en memoria, fuera de la tabla de smbolos, el identificador completo a fin de poder comprobar si dicho lexema ya ha aparecido.

5.6.2 INFORMACIN SOBRE LA ASIGNACIN DE MEMORIA La tabla de smbolos mantiene tambin la informacin acerca de la posicin de memoria enlazada a cada nombre durante la ejecucin. Aqu es necesario considerar diferentes casos: Nombres con posicin de memoria esttica. Para este tipo de nombres se acta diferente dependiendo del tipo de cdigo que genera el compilador (lenguaje objeto): Lenguaje ensamblador: basta con crear definiciones de datos en lenguaje ensamblador para cada nombre y luego aadirlas al programa objeto. El ensamblador se encarga posteriormente de las posiciones de memoria de cada nombre. Cdigo de mquina: En este caso, se determina la posicin de cada objeto de datos en relacin a una posicin fija, como el inicio de un registro de activacin o un bloque independiente del programa. Nombres cuya memoria se encuentra asignada en una pila o montculo: en este caso, el compilador solo puede organizar el registro de activacin para cada procedimiento.

Jacqueline Khler C. - USACH

98

Compiladores 5.6.3 TIPOS DE TABLAS DE SMBOLOS Existen tablas de smbolos estticas y dinmicas, y es importante poder determinar qu tipo de tabla se debe ocupar: Esttica: es til cuando se debe acceder a los smbolos en mltiples ocasiones. Dinmica: se usa cuando la informacin de los smbolos es utilizada en solo una pasada. La figura 5.9 muestra un pequeo programa en C y los elementos presentes en la tabla de smbolos tanto para el caso esttico como para el dinmico. Se puede observar que en el caso esttico se incorporan los nombres de todos los procedimientos a medida que van apareciendo, pero no se eliminan. En el caso dinmico, en cambio, solo figuran aquellos elementos pertenecientes a los procedimientos activos.

FIGURA 5.9: Comparacin entre tablas de smbolos esttica y dinmica.

5.6.4 IMPLEMENTACIN DE LA TABLA DE SMBOLOS La tabla de smbolos puede construirse mediante diferentes estructuras de datos: Arreglos: desde el punto de vista del programador resultan muy convenientes porque son fciles de implementar. Tienen un tamao fijo, lo que obliga a asignar memoria para la misma cantidad de atributos por cada smbolo, se use o no. En muchos casos esto se traduce en un significativo desperdicio de memoria. Se utilizan algoritmos de bsqueda lineales para acceder a los diferentes atributos. Si el arreglo se encuentra ordenado, es posible construir algoritmos de bsqueda binarios. No es posible manejar informacin relativa al alcance de las variables. Pilas: La variable al tope de la pila es la que tiene el alcance ms reciente. Resulta fcil implementar mltiples niveles de alcance. Es la opcin ms adecuada para la construccin de una tabla de smbolos dinmica. Jacqueline Khler C. - USACH

99

Compiladores Listas enlazadas: Similares a las pilas. La implementacin de operaciones resulta ms sencilla. Al igual que en los arreglos, las bsquedas toman un tiempo lineal. rboles binarios: Si los elementos se encuentran ordenados, se reduce el tiempo de bsqueda en forma significativa. El manejo del alcance de los smbolos se vuelve ms complejo. Las operaciones de insercin y eliminacin de elementos son muy costosas en tiempo. rboles n-arios: Todo nodo es un alcance, y todos los smbolos de ese nivel de alcance estn contenidos en dicho nodo. Cada nodo tiene n hijos, y cada hijo implica un nuevo alcance de su alcance padre. Tablas de dispersin (tablas hash): corresponden a una de las tcnicas ms utilizadas para la implementacin de tablas de smbolos y se ilustran en la figura 5.10. Se construye de la siguiente manera: Se crea en primer lugar una tabla de dispersin, que es una matriz fija con punteros a entradas de la tabla. Las entradas de la tabla se organizan en listas enlazadas, independientes entre s, donde cada registro de la tabla de smbolos aparece solo en una de estas listas. Para determinar en qu lista debe ir la entrada para el nombre se utiliza una funcin de dispersin que devuelve un entero entre 0 y 1. Si s se encuentra en la tabla de smbolos, estar en la lista numerada con . En caso contrario, se debe crear una nueva entrada en dicha lista.

FIGURA 5.10: Tabla de dispersin de tamao 211.

5.6.5 REPRESENTACIN DE LA INFORMACIN SOBRE EL MBITO Muchas veces resulta difcil manejar el mbito de un nombre si se trabaja con una nica tabla de smbolos. Una buena manera de resolver este problema es construir una tabla de smbolos diferente para cada procedimiento, con punteros para llegar a ellas y volver a la tabla anterior. Jacqueline Khler C. - USACH

100

Compiladores Ejemplo 5.3: Considere el fragmento de cdigo con procedimientos anidados del listado 5.7 (se listan solo las declaraciones y llamadas a procedimientos). En la tabla de smbolos dicho fragmento de cdigo (figura 5.11) se puede observar que cada tabla contiene al inicio un campo con un puntero al lugar desde donde se hizo la llamada al procedimiento. Adems, en las entradas correspondientes a nombres de procedimientos, se tienen punteros que apuntan a la tabla de smbolos correspondiente. LISTADO 5.7: Programa en pseudo-Pascal para el ejemplo 5.3.
// Main var nota; calcular_promedio(); // calcular_promedio var n_cat; var n_lab; calcular_n_cat(); calcular_n_lab(); // calcular n_cat var n_peps; var n_controles; ponderar_peps(); promediar_controles(); // calcular n_lab var i; var j;

// ponderar_peps var ponderacion; var p1; var p2; var p3; determinar_por(); // promediar_controles var c1; var c2; var c3; var c4; // determinar_por var aprueba; var promedio; var reprueba;

Jacqueline Khler C. - USACH

101

Compiladores

FIGURA 5.11: Tabla de smbolos para el programa del listado 5.7.

5.7 ASIGNACIN DINMICA DE LA MEMORIA


5.7.1 INSTRUMENTOS DE LOS LENGUAJES Algunos lenguajes proporcionan facilidades para la asignacin dinmica de memoria para los datos, para lo cual generalmente se utiliza un montculo. No obstante, la asignacin puede ser de dos tipos: Explcita: se utilizan los mtodos estndar para este fin. Por ejemplo, la ejecucin de new(p) en Pascal asigna memoria para el tipo de objeto sealado por p y p apunta al objeto recin asignado. Implcita: se produce cuando la evaluacin de una expresin tiene como resultado la obtencin de memoria para guardar los valores de la expresin. Un ejemplo de esto se puede encontrar en Snobol, que permite que la longitud de una cadena vare durante la ejecucin y administra el espacio del montculo necesario para almacenarla. Para la desasignacin o liberacin de memoria se tienen los mismos tipos: Explcita: en este caso, es el programador quien se preocupa de liberar la memoria que ya no se necesita, por lo que el compilador no debe efectuar ninguna tarea al respecto. Jacqueline Khler C. - USACH

102

Compiladores Implcita: se debe determinar cundo un bloque de memoria ya no es necesario. Un concepto importante que se debe definir es el de basura. Corresponde a todas aquellas posiciones de memoria asignadas que ya no pueden alcanzarse. Por ejemplo, considere una lista enlazada en C. Si se asigna NULL a la cabeza de la lista, ya no es posible alcanzar ninguno de los nodos restantes an cuando stos ya han sido asignados. Algunos lenguajes, como Lisp y Java, realizan un proceso de recoleccin de basura y reclaman la memoria inaccesible. Otros lenguajes, como C y Pascal, no tienen esta funcionalidad, por lo que el programador debe liberar explcitamente la memoria que ya no se ocupar. Estos lenguajes permiten reutilizar la memoria liberada, pero la basura se mantiene hasta el trmino de la ejecucin del programa. Un concepto relacionado con el de basura es el de referencias suspendidas. Si se desasigna la memora antes de la ltima referencia a ella, se crea una referencia suspendida. Si se mantiene la memoria despus de la ltima referencia, pasa a ser basura.

5.7.2 ASIGNACIN EXPLCITA DE BLOQUES DE TAMAO FIJO Es la forma ms sencilla de asignacin explcita. La asignacin y desasignacin puede hacerse rpidamente enlazando los bloques (de tamao fijo) en una lista, con poca o nula prdida de memoria. Cada bloque tiene una porcin que contiene un enlace al siguiente bloque. Cuando se lleva a cabo una asignacin, se elimina el bloque utilizado de la lista. Al liberarse la memoria, dicho bloque es incorporado nuevamente a la lista (ver figura 5.12). El compilador se abstrae del tipo y tamao del dato.

FIGURA 5.12: Lista de bloques de tamao fijo. (a) El bloque 1 se encuentra asignado. (b) Al ser liberado, el bloque 1 se aade una vez ms a la lista.

5.7.3 ASIGNACIN EXPLCITA DE BLOQUES DE TAMAO VARIABLE Al asignar y desasignar bloques puede producirse una fragmentacin de la memoria, como se muestra en la figura 5.13. Esta no causa problemas si se trabaja con bloques de tamao fijo, pero si se tienen Jacqueline Khler C. - USACH

103

Compiladores bloques de tamao variable no es posible asignar una porcin de memoria mayor que el ms grande de los bloques disponibles, an cuando se cuente con el espacio necesario.

FIGURA 5.13: Bloques libres y ocupados en un montculo. Existen diferentes mtodos para asignar bloques de tamao variable: Primer ajuste: se asigna el primer bloque de tamao mayor o igual al requerido. Mejor ajuste: se asigna el menor bloque de tamao mayor o igual al requerido. Para las dos tcnicas anteriores, si el bloque asignado es de mayor tamao que el requerido se divide en dos bloques de menor tamao: uno del tamao requerido, donde se har la asignacin; el otro, con el espacio restante, que quedar libre para ser asignado posteriormente. Una buena forma de evitar una fragmentacin mayor que lo necesario es, cada vez que se desasigne un bloque, comprobar si es adyacente a otro bloque libre. De ser as, se funden ambos bloques en uno solo. Se debe considerar tambin que existe un compromiso entre tiempo, espacio y disponibilidad de bloques.

5.7.4 DESASIGNACIN IMPLCITA Para llevar a cabo esta tarea se requiere saber cundo un bloque de memoria ha dejado de funcionar. Esto puede lograrse fijando el formato de los bloques de memoria, como se muestra en la figura 5.14.

FIGURA 5.14: Formato de un bloque. En primer lugar, se necesita reconocer las fronteras del bloque. Para bloques de tamao fijo, se puede determinar simplemente conociendo su posicin (ndice del bloque, como en la figura 5.12). Si se trabaja con bloques de tamao variable, en cambio se debe reservar un fragmento del bloque para almacenar su tamao y as poder determinar dnde comienza el bloque siguiente. Tambin es necesario saber si un bloque est en uso. Se dice que es as si el programa del usuario puede hacer referencia a la informacin contenida en dicho bloque, ya sea mediante un puntero o una Jacqueline Khler C. - USACH

104

Compiladores secuencia de ellos. Por ende, el compilador necesita conocer la posicin donde se encuentra cada uno de esos punteros. Estos pueden guardarse en una posicin fija dentro del bloque. Se supone que el rea del bloque con informacin del usuario no contiene punteros. Para llevar a cabo la desasignacin implcita pueden emplearse dos enfoques diferentes, que se describen en los puntos siguientes.

5.7.4.1 Cuenta de referencias Se cuenta la cantidad de bloques que hacen referencia al bloque actual. Si la cuenta es igual a 0, este ltimo puede ser liberado.

5.7.4.2 Tcnicas de marca Esta tcnica requiere que se conozcan todos los punteros del montculo. Consiste en detener por un momento la ejecucin del programa de usuario y hacer un seguimiento de todos los punteros para determinar qu bloques pueden ser alcanzados y, en consecuencia, estn en uso. Una forma de implementarlo anterior es marcar inicialmente los bloques como no ocupados. A continuacin, se hace un seguimiento de cada uno de los punteros del montculo y los bloques alcanzados se marcan como ocupados. Cuando ya no quedan punteros por revisar, se borran todos los bloques marcados como no ocupados. Al usar este enfoque con bloques de tamao variable, es posible llevar a cabo una compactacin de los datos para eliminar la fragmentacin de la memoria. Esta consiste en desplazar todos los bloques en uso a un extremo del montculo, con la correspondiente actualizacin de todos los punteros.

5.8 EJERCICIOS
1. Dado el fragmento de cdigo del listado 5.8: a. Muestre su rbol de activacin. b. Muestre el estado de la pila de control cuando se efecta la llamada a factorial(2). 2. Considere la porcin de memoria que se muestra en la figura 5.15, donde los bloques ms oscuros corresponden a fragmentos utilizados: a. Muestre el estado final de la memoria tras efectuar las siguientes asignaciones considerando los mtodos de primer ajuste y mejor ajuste. Un bloque de 6 bytes. Un bloque de 2 bytes. Un bloque de 4 bytes. Un bloque de 8 bytes.

Jacqueline Khler C. - USACH

105

Compiladores b. Muestre el estado final de la memoria (tras defragmentar por medio de compactacin. Considere para este fin el estado dado en la figura. LISTADO 5.8: Fragmento de cdigo para el ejercicio 1.
int fibonacci(int n) { if(n == 0) { return 0; } if(n == 1) { return 1; } return fibonacci(n - 2) + fibonacci(n - 1); } int factorial(int n) { if(n == 0) { return 1; } return n * factorial(n - 1); } void main() { unsigned long x = fibonacci(5); unsigned long y = factorial(4); }

FIGURA 5.15: Estado de un fragmento de memoria para el ejercicio 2. Jacqueline Khler C. - USACH

106

Compiladores

6 GENERACIN DE CDIGO INTERMEDIO


Desde la perspectiva del modelo estructurado de un compilador, una vez terminado el anlisis semntico se puede llevar a cabo la traduccin del programa original a un nuevo lenguaje. En la prctica, esta traduccin puede ser efectuada tambin durante la etapa de anlisis semntico. Hasta ahora se haba explicado que la traduccin llevaba el programa fuente a un programa escrito en el lenguaje objeto. Ahora bien, en muchas ocasiones es preferible traducir primero a un lenguaje intermedio. Considere, por ejemplo, la figura 6.1. En ella se puede observar la enorme cantidad de trabajo necesaria para traducir un programa escrito en cada uno de los lenguajes de alto nivel a cada uno de los lenguajes de mquina. En el caso del lado izquierdo se puede ver que, para cada lenguaje fuente, se debe construir un compilador diferente por cada lenguaje objeto. El lado derecho, en cambio, muestra la gran ventaja de la redestinacin: se puede crear un compilador para una mquina distinta uniendo una fase de anlisis que genere cdigo intermedio a una fase de sntesis que genere cdigo objeto para la mquina destino.

FIGURA 6.1: Una de las ventajas de usar un lenguaje intermedio es la redestinacin. (a) No se usa lenguaje intermedio. (b) Se usa lenguaje intermedio. Una segunda ventaja que otorga el uso de un lenguaje intermedio es que se puede aplicar un optimizador de cdigo independiente de la mquina en que va a ser usado el programa.

6.1 LENGUAJES INTERMEDIOS


6.1.1 REPRESENTACIONES GRFICAS La primera de las representaciones grficas para un lenguaje intermedio es el ya conocido rbol sintctico. Esta representacin muestra la jerarqua natural del lenguaje. Similar en gran medida al rbol sintctico, una representacin para los lenguajes intermedios son los grafos dirigidos acclicos (GDA). La diferencia entre stos y los rbol sintctico radica en que los GDA proporcionan una representacin ms compacta, pues identifican las subexpresiones comunes.

Jacqueline Khler C. - USACH

107

Compiladores La figura 6.2 muestra ambas representaciones grficas para la expresin .

FIGURA 6.2: Representaciones grficas para la expresin . (a) AST. (b) GDA. Los AST para las proposiciones de asignacin de un lenguaje se pueden producir mediante las definiciones dirigidas por la sintaxis de la tabla 6.1. Esta misma definicin se puede usar para construir los GDA, siempre y cuando las funciones y retornen un puntero a un nodo ya existente siempre que sea posible.

TABLA 6.1: Definicin dirigida por la sintaxis para construir AST para proposiciones de asignacin. Una tercera representacin grfica es la representacin postfija, que es una representacin lineal del rbol sintctico.

6.1.2 CDIGO DE TRES DIRECCIONES El cdigo de tres direcciones es una representacin linealizada de un rbol sintctico o de un GDA, y tiene la forma de una secuencia de proposiciones de la forma general : , donde: , y son nombres constantes o variables temporales. representa cualquier operador (aritmtico, lgico).

Jacqueline Khler C. - USACH

108

Compiladores Ntese que no se permiten operaciones aritmticas compuestas, pues solo se puede tener un operador al lado derecho de una proposicin. En consecuencia, una expresin de la forma se puede traducir en una secuencia: : : Donde y son variables temporales generadas por el compilador. Al hacer esto se debe tener cuidado de respetar la precedencia de los operadores. El listado 6.1 muestra el cdigo de tres direcciones que se obtiene para el AST y el GDA correspondientes a la expresin . LISTADO 6.1: Cdigo de tres direcciones. (a) Para el AST de la figura 6.2. (b) Para el GDA de la figura 6.2.

El cdigo de tres direcciones recibe ese nombre porque en cada proposicin suele contener tres direcciones de memoria: una por cada operando y otra para el resultado.

6.1.2.1 Tipos de proposiciones de tres direcciones Las proposiciones de tres direcciones son anlogas al cdigo ensamblador. Pueden tener etiquetas simblicas, y existen tambin proposiciones para el flujo de control. Las proposiciones de tres direcciones comunes son: 1. Proposiciones de asignacin de la forma : , donde es una operacin lgica o aritmtica. 2. Instrucciones de asignacin de la forma : , donde es una operacin unaria. 3. Proposiciones de copia de la forma : , donde se asigna a el valor de . 4. El salto incondicional , donde es la etiqueta de la proposicin de tres direcciones que debe ejecutarse a continuacin. 5. Los saltos condicionales como . 6. y , para llamadas a procedimientos y , donde: Jacqueline Khler C. - USACH

109

Compiladores es un parmetro. corresponde a un procedimiento o funcin. es la cantidad de parmetros del procedimiento. representa el valor devuelto (es opcional). Por ejemplo, la llamada al procedimiento , , , queda: , 7. Las asignaciones con ndices de la forma : y : . La primera asigna a el valor de la posicin en unidades de memoria ms all de la posicin . La segunda asigna el contenido de la posicin en unidades de memoria ms all de la posicin al valor de . 8. Las asignaciones de direcciones y punteros de la forma : &, : y : . La primera de estas proposiciones asigna a la direccin de . La segunda asigna a el valor contenido en la direccin apuntada por . La tercera hace que el valor del objeto apuntado por tenga igual valor que . 6.1.2.2 Implementaciones de cdigo de tres direcciones Una proposicin de 3 direcciones es una forma abstracta de cdigo intermedio. En un compilador, estas proposiciones pueden implementarse como registros con campos para el operador y los operandos. A continuacin se muestran tres implementaciones diferentes.

6.1.2.3.1 Cudruplos Corresponden a estructuras de tipo registro con cuatro campos: , que contiene un cdigo interno para el operador, y contienen los operandos y , como su nombre lo indica, contiene el resultado de la operacin. Por supuesto, solo se usan todos los campos en el paso de los operadores binarios, por lo que se debe aclarar qu ocurre con los dems tipos de instrucciones: Operadores unarios: no se utiliza . param y similares: no se utilizan ni . Saltos condicionales e incondicionales: se pone la etiqueta objeto en . En general, los contenidos de los campos , y son punteros a las entradas correspondientes de la tabla de smbolos. Adicionalmente, se debe sealar que los nombres temporales deben introducirse en la tabla de smbolos conforme van siendo creados. La tabla 6.2 muestra la representacin mediante cudruplos del cdigo de tres direcciones para la expresin : .

Jacqueline Khler C. - USACH

110

Compiladores

TABLA 6.2: Cdigo de tres direcciones para : representado mediante cudruplos. 6.1.2.3.2 Triples Esta representacin tiene la ventaja de no introducir nombres temporales a la tabla de smbolos. Para esto, los valores temporales son referenciados de acuerdo a la posicin de la proposicin en que son calculados. Como consecuencia, las proposiciones de tres direcciones se pueden representar mediante registros con solo tres campos: , y . Los campos y contienen punteros a la tabla de smbolos para las variables y constantes definidas por el programador o bien punteros dentro de la misma estructura de triples para hacer referencia a los valores temporales. La tabla 6.3 muestra la representacin mediante triples del cdigo de tres direcciones para la expresin : .

TABLA 6.3: Cdigo de tres direcciones para : representado mediante triples. En la prctica, la informacin necesaria para interpretar las distintas clases de entrada en los campos y se puede codificar dentro del campo o en campos adicionales. Obsrvese que las operaciones ternarias, como : requieren dos entradas en la estructura de triples, como se puede ver en la tabla 6.4 (a). La figura 6.4 (b) muestra que la operacin se representa mediante dos entradas en forma natural.

6.1.2.3.3 Triples indirectos En este caso, en lugar de hacer una lista de los triples mismos, se construye una lista de punteros a triples. As, por ejemplo, se puede usar una matriz proposicin para listar los punteros a triples en el orden que se desee. La tabla 6.5 muestra la representacin mediante triples indirectos de los triples de la tabla 6.3. Jacqueline Khler C. - USACH

111

Compiladores

TABLA 6.4: Otras representaciones por medio de triples. (a) : . (b) . Si bien la utilizacin de triples indirectas requiere ms espacio de almacenamiento que el uso de triples, tiene la ventaja de que la reubicacin de cdigo se vuelve mucho ms sencilla. Esto resulta til, por ejemplo, para la optimizacin.

TABLA 6.5: Cdigo de tres direcciones para : representado mediante triples indirectos.

6.2 TRADUCCIN DIRIGIDA POR LA SINTAXIS


Conceptualmente, el proceso de traduccin comienza cuando se ha determinado que el programa fuente no contiene errores, sean stos lxicos, sintcticos o semnticos. No obstante, esta tarea se lleva a cabo simultneamente con el anlisis semntico y, en consecuencia, en interaccin con el proceso de anlisis sintctico. Este proceso no se ocupa solamente de generar una secuencia de cdigo, sino que tambin debe tener en consideracin algunos aspectos de la asignacin de memoria y de la construccin de la tabla de smbolos.

6.2.1 DECLARACIONES Conforme se examina la secuencia de declaraciones dentro de un procedimiento (o funcin), un registro o un bloque, se puede distribuir la memoria para los nombres locales. Para cada uno de ellos se crea una entrada en la tabla de smbolos con informacin, por ejemplo, referente al tipo y la direccin relativa de la memoria que le corresponde. La direccin relativa consiste en un desplazamiento desde la base del rea de datos esttica o del campo para los datos locales en un registro de activacin.

Jacqueline Khler C. - USACH

112

Compiladores 6.2.1.1 Declaraciones dentro de un procedimiento La sintaxis de lenguajes como C y Pascal permite que todas las declaraciones en un mismo procedimiento se procesen como un grupo. Para este fin se utiliza una variable global, por ejemplo , que indica la siguiente direccin relativa de memoria disponible. La tabla 6.6 muestra una gramtica que permite efectuar declaraciones junto a un esquema de traduccin. La variable es inicializada en 0 antes de considerar la primera declaracin. Los nuevos nombres son incorporados a la tabla de smbolos a medida que van apareciendo, en una posicin relativa igual a . Al incorporar un nuevo smbolo, es necesario aumentar en el ancho (usualmente en bytes) del objeto de datos indicado por el nombre. El esquema de traduccin de la tabla 6.6 hace uso de las siguientes funciones: , , : crea una nueva entrada en la tabla de smbolos para y le asocia su tipo de dato ( )y su posicin relativa en la memoria (). En el caso de arreglos y punteros, corresponde a una expresin de tipos construida a partir de los tipos bsicos, enteros y reales en este caso. , : expresin de tipo que indica que indica que la variable a la que se asocia esta expresin es un arreglo de tamao cuyos elementos son de tipo . : expresin de tipo que indica que indica que la variable a la que se asocia esta expresin es un puntero a un objeto de tipo .

TABLA 6.6: Clculo de tipos y direcciones relativas de nombres declarados.

Jacqueline Khler C. - USACH

113

Compiladores En un lenguaje con procedimientos anidados, se pueden asignar direcciones relativas a los nombres locales a cada procedimiento. Se puede suponer que existe una tabla de smbolos diferente para cada procedimiento, mecanismo que puede ser implementado, por ejemplo, mediante una lista enlazada. La figura 6.3 muestra el esquema de procedimientos anidados, mientras que la tabla 6.7 muestra un esquema de traduccin (que se agrega al de la tabla 6.6) que permite crear las diferentes tablas de smbolos y sus punteros. El esquema hace uso de las siguientes funciones: : crea una nueva tabla de smbolos y retorna un puntero a la nueva tabla. El argumento corresponde a un puntero a la tabla de smbolos del procedimiento que abarca al nuevo procedimiento declarado. En la figura 6.3, podra ser, por ejemplo, el puntero desde hacia . , , , : crea en la tabla de smbolos apuntada por una nueva entrada para , con su tipo y su posicin relativa . , : registra el ancho acumulado de todas las entradas de tabla en el encabezamiento asociado a dicha tabla de smbolos. , , : crea en la tabla de smbolos apuntada por una nueva entrada para el procedimiento . El puntero apunta a la tabla de smbolos del procedimiento nombre.

FIGURA 6.3: Tablas de smbolos para procedimientos anidados. Tambin es interesante sealar que: es una pila que almacena punteros a procedimientos exteriores. Jacqueline Khler C. - USACH

114

Compiladores es una pila que almacena la siguiente posicin relativa disponible para un nombre local del procedimiento en curso. Convertir en una pila es la extensin natural para ajustar el esquema de la tabla 6.6 para procedimientos anidados.

TABLA 6.7: Proceso de declaraciones en procedimientos anidados.

6.2.1.2 Nombres de campos dentro de registros Otro tipo habitual de declaraciones son los registros o estructuras. Para poder tenerlo en cuenta, es necesario incorporar al esquema de traduccin de la tabla 6.7 la produccin , que permite generar registros. La tabla 6.8 muestra cmo se construye la tabla de smbolos para los nombres de los campos de un registro.

6.2.3 PROPOSICIONES DE ASIGNACIN Para esta seccin se asume que las expresiones pueden contener elementos de tipo entero, real, matriz, registro y puntero. Como parte de la generacin de cdigo de tres direcciones, se explica adems cmo buscar los nombres en la tabla de smbolos y acceder a los elementos de matrices y registros.

Jacqueline Khler C. - USACH

115

Compiladores 6.2.3.1 Expresiones aritmticas Al generar cdigo de tres direcciones se crean nombres temporales para los nodos interiores del rbol sintctico. El valor del no terminal al lado izquierdo de la produccin se calcula en un temporal . Se crear un nuevo temporal cada vez que sea necesario. En el caso de que una expresin contenga solamente un identificador, por ejemplo , entonces contiene el valor de la expresin.

TABLA 6.8: Creacin de la tabla de smbolos para los nombres de los campos de un registro. La tabla 6.9 muestra las reglas semnticas que permiten crear cdigo de tres direcciones para proposiciones de asignacin. Si se toma la entrada , el cdigo resultante es el del listado 6.1 (a). Volviendo a la tabla 6.9 se tiene que: El atributo sintetizado . representa el cdigo de tres direcciones para la asignacin de . . corresponde al nombre que contendr el valor de . . es la secuencia de proposiciones de tres direcciones que evalan . La funcin retorna el nombre para un nuevo temporal. La notacin : representa la proposicin de tres direcciones : . Hasta ahora se haban formado las proposiciones de tres direcciones utilizando los nombres de las variables considerados como punteros hacia sus entradas en la tabla de smbolos. El esquema de traduccin de la tabla 6.10 muestra cmo se puede efectuar la bsqueda de las entradas en la tabla de smbolos. En dicha tabla: El atributo . corresponde al lexema del nombre. La operacin verifica la existencia de una entrada en la tabla de smbolos para . En caso afirmativo retorna un puntero a la entrada, mientras que en caso contrario retorna un puntero nulo. La funcin escribe las proposiciones de tres direcciones generadas (representadas por ) a un archivo de salida. Esto elimina la necesidad del atributo de la tabla 6.9. Se haba sealado que la funcin crea un nuevo temporal cada vez que sea necesario. Esto resulta especialmente til en compiladores optimizadores. No obstante, los temporales que almacenan valores intermedios requieren espacio de almacenamiento y tienden a obstruir la tabla de smbolos. Jacqueline Khler C. - USACH

116

Compiladores Esta funcin puede ser modificada para que utilice una pequea matriz en un rea de datos del procedimiento como si fuera una pila para guardar los temporales. Como la mayora de los temporales solo se ocupan una vez, se puede crear un contador , inicializado en 0, que se decrementa en 1 cada vez que se utilice como operando un nombre temporal. Cuando se genera un temporal nuevo, se utiliza la posicin $ y se incrementa en 1. A aquellos poco frecuentes temporales que se usan ms de una vez se les puede asignar un nombre propio.

TABLA 6.9: Definicin dirigida por la sintaxis que permite producir cdigo de tres direcciones para las asignaciones. Ejemplo 6.1: Genere cdigo intermedio para la expresin 15 43 3 /2 5. El cdigo intermedio para la expresin dada se muestra en el listado 6.2. LISTADO 6.2: Cdigo de tres direcciones para la expresin 15 43 3 /2 5. 43 15 /2 3 5 Jacqueline Khler C. - USACH

117

Compiladores 6.2.3.2 Expresiones booleanas Las expresiones booleanas tienen dos grandes propsitos en los lenguajes de programacin. Se utilizan para calcular valores lgicos y como expresiones condicionales para alterar el flujo de control. Las expresiones booleanas pueden ser generadas por una gramtica con las siguientes producciones: | | | | | | , donde se considera un atributo para determinar el operador de comparacin representado por . Se asume que tanto como asocian por la izquierda, y que la precedencia de operadores (de mayor a menor) es , y . Es habitual usar una representacin numrica para los valores booleanos. En este caso se considera el valor 1 como y el valor 0 como . La evaluacin de las expresiones se efecta de izquierda a derecha y siguiendo la precedencia de operadores, de manera similar a las expresiones aritmticas.

TABLA 6.10: Esquema de traduccin para generar cdigo de tres direcciones para asignaciones. Ejemplo 6.2: Genere cdigo intermedio para la expresin . El cdigo de tres direcciones para la expresin dada se muestra en el listado 6.3.

Jacqueline Khler C. - USACH

118

Compiladores LISTADO 6.3: Cdigo intermedio para la expresin . Una expresin relacional como a<b es equivalente a la proposicin condicional 1 0, que se puede traducir al siguiente cdigo de tres direcciones (los nmeros del lado izquierdo son etiquetas para las diferentes instrucciones): 100: 103 101: 0 102: 104 103: 1 La tabla 6.11 muestra un esquema de traduccin a cdigo de tres direcciones que usa una representacin numrica para los valores booleanos y utiliza el esquema condicional para evaluar operaciones relacionales.

TABLA 6.11: Esquema de traduccin para expresiones booleanas. Jacqueline Khler C. - USACH

119

Compiladores En el esquema de la tabla 6.11 se tiene, una vez ms, que escribe proposiciones de cdigo de tres direcciones a un archivo de salida. Adems, da el ndice de la siguiente proposicin de tres direcciones en la secuencia de salida ( debe incrementar despus de generar una proposicin de tres direcciones). Ejemplo 6.3: La expresin ! se traduce a cdigo de tres direcciones, usando el esquema de la tabla 6.11, como muestra el listado 6.4. LISTADO 6.4: Cdigo intermedio para la expresin ! . 100: 103 101: 0 102: 104 103: 1 104: 107 105: 0 106: 108 107: 1 6.2.3.3 Acceso a elementos de matrices Se puede acceder rpidamente a los elementos de una matriz si stos se guardan en un bloque de posiciones consecutivas. Si el ancho de cada elemento de la matriz es , entonces el -simo elemento de la matriz comienza en la posicin , donde es el lmite inferior de los subndices y es la direccin relativa de la posicin de memoria asignada a la matriz. En el caso de una matriz bidimensional almacenada por filas, la direccin relativa de se puede calcular como , donde e son los lmites inferiores para los valores de e y corresponde a la cantidad de valores que puede tomar (cantidad de columnas de la matriz). El principal problema de generar cdigo para referencias de matrices es relacionar los clculos con una gramtica para referencias de matrices. La tabla 6.12 muestra una de estas gramticas con sus acciones semnticas, donde: . significa que corresponde a un simple. Para las expresiones aritmticas se usa el esquema de la tabla 6.10. Cuando se hace la reduccin , siendo un arreglo, se usa la indizacin para obtener el contenido de . . . La funcin . devuelve el tamao de los elementos de la matriz, mientras que . entrega la base de la matriz. 108: 109: 110: ! 113 111: 0 112: 114 113: 1 114:

Jacqueline Khler C. - USACH

120

Compiladores

TABLA 6.12: Esquema de traduccin para matrices. Ejemplo 6.4: Sea una matriz de 10 20 almacenada a partir de la posicin 1000 de la memoria. Adems, sean 1 y 4. El cdigo de tres direcciones para la asignacin se muestra en el listado 6.5.

Jacqueline Khler C. - USACH

121

Compiladores LISTADO 6.5: Cdigo intermedio para la expresin . 0: 1000 1: 1 2: 20 3: 4: 1 5: 4 6: // base // y - // // // //

6.2.3.4 Acceso a elementos de registros Se usan los esquemas anteriores, considerando los nombres de los campos como entradas de la tabla de smbolos pertinente.

6.2.4 SENTENCIAS DE FLUJO DE CONTROL En general, los programas estn no solo por expresiones y asignaciones, sino que tienen tambin proposiciones de flujo de control. Estas ltimas tambin deben ser traducidas a cdigo intermedio. En consecuencia, se deben incorporar las reglas semnticas necesarias a la definicin dirigida por la sintaxis construida hasta ahora. El ejercicio de disear las reglas semnticas queda para los alumnos, por lo que en esta seccin se muestra cmo debe quedar el cdigo generado para diferentes sentencias de esta clase.

6.2.4.1 Sentencia goto Se copia casi textualmente, realizando el salto al comienzo de la sentencia fuente etiquetada.

6.2.4.2 Sentencia if Esta sentencia, en primer lugar, debe verificar el cumplimiento de una condicin. Si es verdadera, se ejecutan las sentencias anidadas. En caso contrario, se salta a la primera sentencia fuera del bloque . En el ejemplo 6.5, las sentencias etiquetadas de 0 a 3 evalan la condicin. La sentencia 4 determina si la condicin es verdadera, y en caso de ser as salta al cdigo contenido en el bloque (sentencia 6), continuando luego en forma secuencial con el cdigo posterior (sentencia 7). En caso contrario, salta a la primera instruccin posterior al bloque . Ejemplo 6.5: Considere el fragmento de cdigo en C del listado 6.6 y tradzcalo a cdigo de tres direcciones.

Jacqueline Khler C. - USACH

122

Compiladores LISTADO 6.6: Un fragmento de cdigo en C.


if(x!=0) { y=3; } z=x*y;

El listado 6.7 muestra el cdigo intermedio resultante. LISTADO 6.7: Cdigo de tres direcciones para una sentencia . 0: ! 0 3 1: 0 2: 4 3: 1 6.2.4.3 Sentencia if-else Similar a la sentencia if, tambin comienza por verificar el cumplimiento de una condicin. Si es verdadera, se ejecutan las sentencias anidadas dentro del bloque para luego saltar a la primera instruccin fuera del bloque . En caso contrario, se salta a la primera sentencia del bloque para luego continuar secuencialmente con el cdigo posterior. Ejemplo 6.6: Considere el fragmento de cdigo en C del listado 6.8 y tradzcalo a cdigo de tres direcciones. LISTADO 6.8: Un fragmento de cdigo en C.
if(x!=0) { y=3; } else{ y=5; } z=x*y;

4: 6 5: 7 6: 3 7:

El listado 6.9 muestra el cdigo intermedio resultante. LISTADO 6.9: Cdigo de tres direcciones para una sentencia . 0: ! 0 3 1: 0 2: 4 3: 1 4: 6 Jacqueline Khler C. - USACH 5: 8 6: 3 7: 9 8: 5 9:

123

Compiladores 6.2.4.4 Sentencia switch Existen diversas maneras de implementar el cdigo de tres direcciones para esta sentencia, y algunas de ellas dependen de las especificaciones del lenguaje fuente. Un mecanismo sencillo, no obstante, puede ser el de evaluar la expresin de prueba e ir evaluando el resultado: si es distinto del caso comprobado, saltar al siguiente, hasta llegar al valor buscado, ejecutando entonces su cdigo asociado y saltando luego fuera del bloque , o al final del bloque . Si existe un valor por defecto, el cdigo asociado a l se ejecuta siempre que no se haya encontrado un valor coincidente. Ejemplo 6.7: Considere el fragmento de cdigo en C del listado 6.10 y tradzcalo a cdigo de tres direcciones. LISTADO 6.10: Un fragmento de cdigo en C.
c=2*y-1; switch(c) { case 1: z=5; case 3: z=36; case 5: z=8; default: z=0; } x=y+z;

El listado 6.11 muestra el cdigo intermedio resultante. LISTADO 6.11: Cdigo de tres direcciones para una sentencia . 0: 2 1: 1 2: ! 1 5 3: 5 4: 12 5: ! 3 8 6: 36 7: 12 8: ! 5 11 9: 8 10: 12 11: 0 12:

En el caso real del lenguaje C, la traduccin anterior es incorrecta. En C, se ejecuta el caso vlido y todos los casos siguientes a menos que se encuentre un salto al exterior del bloque switch, sealado por la palabra reservada . Ejemplo 6.8: Considere el fragmento de cdigo en C del listado 6.12 y tradzcalo a cdigo de tres direcciones. Jacqueline Khler C. - USACH

124

Compiladores LISTADO 6.12: Un fragmento de cdigo en C.


Z=0; c=2*y-1; switch(c) { case 1: z=5; case 3: z=z+2; break; case 5: z=8; } x=y+z;

El listado 6.13 muestra el cdigo intermedio resultante. LISTADO 6.13: Cdigo de tres direcciones para una sentencia segn el funcionamiento de esta sentencia en C. 0: 0 1: 2 2: 1 3: ! 1 6 4: 5 5: 7 6: ! 3 8 6.2.4.5 Sentencia while En este caso, se evala una condicin. Si sta se cumple, se ejecutan las sentencias anidadas y se vuelve a evaluar la condicin. Las repeticiones continan mientras la condicin se siga cumpliendo, para finalmente saltar fuera del bloque . Si dentro del bloque aparece la palabra reservada , se traduce como un salto fuera del . 7: 1 8: 12 9: ! 5 12 10: 8 11: 12 12: 13:

Jacqueline Khler C. - USACH

125

Compiladores Ejemplo 6.9: Considere el fragmento de cdigo en C del listado 6.14 y tradzcalo a cdigo de tres direcciones. LISTADO 6.14: Un fragmento de cdigo en C.
x=0; i=0; while(i<10){ x=x*i; i++; }

El listado 6.15 muestra el cdigo intermedio resultante. LISTADO 6.15: Cdigo de tres direcciones para una sentencia . 0: 0 1: 0 2: 10 4 3: 7 6.2.4.6 Sentencia do-while Muy similar a la sentencia anterior, solo difiere en que la condicin se evala al final, por lo que el cdigo anidado se ejecuta a lo menos una vez. La palabra reservada acta igual que en la sentencia anterior. Ejemplo 6.10: Considere el fragmento de cdigo en C del listado 6.16 y tradzcalo a cdigo de tres direcciones. LISTADO 6.16: Un fragmento de cdigo en C.
x=0; i=0; do{ x=x*i; i++; } while(i<10);

4: 5: 1 6: 2

El listado 6.16 muestra el cdigo intermedio resultante.

Jacqueline Khler C. - USACH

126

Compiladores LISTADO 6.16: Cdigo de tres direcciones para una sentencia . 0: 0 1: 0 2: 6.2.4.7 Sentencia repeat-until Anloga a , la sentencia solo difiere de la anterior en que el bloque anidado se repite mientras la condicin contine siendo falsa. Este mecanismo existe en lenguajes como Pascal. La palabra reservada acta igual que en los dems bucles. Ejemplo 6.11: Considere el fragmento de cdigo en pseudo-C del listado 6.18 y tradzcalo a cdigo de tres direcciones. LISTADO 6.18: Un fragmento de cdigo en pseudo-C.
x=0; i=0; repeat{ x=x*i; i++; } until(i==10);

3: 1 4: 10 2 5: 6

El listado 6.18 muestra el cdigo intermedio resultante. LISTADO 6.18: Cdigo de tres direcciones para una sentencia . 0: 0 1: 0 2: 6.2.4.8 Sentencia for La sentencia es diferente en su funcionamiento a los bucles anteriores. En primer lugar se inicializa el contador con el valor especificado, para luego evaluar la condicin. Adems, al trmino de las instrucciones correspondientes al bloque anidado del , es necesario incorporar las instrucciones necesarias para incrementar el contador antes de volver a evaluar la condicin. La palabra reservada acta igual que en los dems bucles. Ejemplo 6.12: Considere el fragmento de cdigo en C del listado 6.20 y tradzcalo a cdigo de tres direcciones. 3: 1 4: 10 6 5: 2

Jacqueline Khler C. - USACH

127

Compiladores LISTADO 6.20: Un fragmento de cdigo en C.


x=0; for(i=0; i<10; i++){ x=x+x*i; }

El listado 6.21 muestra el cdigo intermedio resultante. LISTADO 6.21: Cdigo de tres direcciones para una sentencia . 0: 0 1: 0 2: 10 4 3: 8 6.2.5 LLAMADAS A PROCEDIMIENTOS Un procedimiento es una construccin de programacin tan importante y utilizada tan a menudo que es fundamental que un compilador genere buen cdigo para llamarlo. Considere, por ejemplo, una gramtica sencilla para llamar procedimientos que contenga las siguientes producciones: , , | . Una posible implementacin puede ser mediante una cola de argumentos (parmetros), pues no siempre se tiene la misma cantidad de argumentos. La tabla 6.13 muestra un posible esquema de traduccin. 4: 5: 6: 1 7: 2

TABLA 6.13: Esquema de traduccin para una llamada sencilla a procedimientos.

6.3 EJERCICIOS
1. Sea la expresin aritmtica . Tradzcala a: a. Un AST. b. Cdigo de tres direcciones.

Jacqueline Khler C. - USACH

128

Compiladores 2. Considere la asignacin / . Tradzcala a: a. Un AST. b. Cdigo de tres direcciones. c. Cudruplos. d. Triples. e. Triples indirectos. 3. Proponga reglas semnticas para traducir estructuras y punteros de C. 4. Determine las reglas semnticas para traducir las sentencias de flojo de control. 5. Genere cdigo de tres direcciones para el fragmento de cdigo del listado 6.22. LISTADO 6.22: Un fragmento de cdigo en C.
y=25; z=26; x=(5+4*z)-(y/2+x*y-z); switch(c) { case 0: y=56*x/2; break; case 1: do{ x=z*(x-4); } while(y>200 && (3-5*x!=z); case 1: z=0; default: x=z*z-4*z+9; } for(i=0; i<10; i++) { N[2*i]=(x+y)*M[i]; N[2*i+1]=x/3+y*M[i]; } if(!(x>y) || y!=z && z>=x-2*y) { repeat { z=(4+(x*y+2*z)-(7-x/98))+y/5; if(z<786) { y=y-1; break; } x=x+1; } until(x==23); } else { x=0; } y=x+z*2-y;

Jacqueline Khler C. - USACH

129

Compiladores

BIBLIOGRAFA
1. AHO, A. V.; LAM, M. S.; SETHI, R.; ULLMAN, J. D. (2008) Compiladores. Principios, tcnicas y herramientas (2 ed.), Pearson Educacin, Mxico. ISBN 978-970-26-1133-2. 2. LVAREZ, J. (2005). Apuntes de la Asignatura Programacin de Lenguajes Formales, Departamento de Ingeniera Informtica, Facultad de Ingeniera, Universidad de Santiago de Chile. 3. CAMPOS, A. E. (1995). Teora de Autmatas y Lenguajes Formales, Departamento de Ciencia de la Computacin, Escuela de Ingeniera, Pontificia Universidad Catlica de Chile. 4. HOPCROFT, J. E.; ULLMAN, J. D. (1993). INTRODUCCIN A LA TEORA DE AUTMATAS, LENGUAJES Y COMPUTACIN, Compaa Editorial Continental S. A., Mxico. ISBN 968-261222-5. 5. KOLMAN, B.; BUSBY, R. C. (1986). Estructuras de Matemticas Discretas para la Computacin, Prentice-Hall Hispanoamericana S. A., Mxico. ISBN 968-880-080-5.

Jacqueline Khler C. - USACH

130

Compiladores

ANEXO: NOCIONES DE LENGUAJES FORMALES


Este apndice no pretende ser ms que una breve introduccin a aquellos aspectos de la teora de autmatas y lenguajes formales que resultan indispensables para la construccin de compiladores. Para estudiar estos contenidos con mayor profundidad, una buena referencia es el libro de Hopcroft, J. E. y Ullman, J. D.: Introduccin a la teora de autmatas, lenguajes y computacin.

A.1 JERARQUA DE LOS LENGUAJES


Un lenguaje es un conjunto, normalmente infinito, de palabras. En consecuencia, no es posible procesarlo directamente mediante un algoritmo, sino que hay que hacerlo indirectamente mediante una representacin o descripcin finita (por comprensin en vez de por extensin). De este modo, podemos tener un algoritmo para el cual podemos afirmar que termina. No obstante, el problema de la representacin de un lenguaje es, a su vez, el problema de su generacin. As, el algoritmo representativo debe generar todas las palabras del lenguaje y ninguna que no pertenezca a ste. Para este fin existen dos modelos finitos: Expresiones regulares: corresponden a combinaciones de operandos y operadores para generar las palabras de un lenguaje. Gramticas: son un conjunto de reglas de reescritura o reemplazo que permiten la generacin de palabras pertenecientes a un lenguaje. Un problema importante a este respecto es el de la capacidad de estos mecanismos de representacin para abarcar el universo de lenguajes. Para comprender este problema, es necesario tener en consideracin lo siguiente: El universo de palabras que se puede construir sobre un alfabeto es infinito contable ( N). El conjunto de todos los lenguajes que se pueden crear sobre un alfabeto , es decir, el conjunto potencia de , es infinito incontable (2 2 ). Como tenemos una cantidad finita de representaciones y un nmero infinito de lenguajes, quedan infinitos lenguajes sin representacin. En Ciencias de la Computacin solo nos interesan aquellos lenguajes que podemos representar. Este universo puede adems ser dividido en clases o familias que comparten alguna propiedad. La principal clasificacin es la jerarqua de Chomsky, que establece 4 tipos de lenguajes representables de acuerdo a los tipos de gramticas que las generan: Generales o tipo 0. Sensibles al contexto (recursivamente enumerables) o tipo 1. Libres de contexto o tipo 2. Regulares o tipo 3. Entre los tipos de lenguajes de esta clasificacin existe adems una jerarqua de inclusin, como se puede apreciar en la figura A.1, donde 3 2 1 0.

Jacqueline Khler C. - USACH

131

Compiladores

FIGURA A.1: Jerarqua de Chomsky para los tipos de lenguajes representables.

A.2 GRAMTICAS
Un mecanismo para representar lenguajes de manera finita son las gramticas. stas pueden definirse como un sistema o algoritmo para la generacin de palabras basado en el reemplazo de subsecuencias de acuerdo a determinadas reglas o producciones. Una produccin est conformada por un lado izquierdo o antecedente y un lado derecho o consecuente. El mecanismo de funcionamiento es simple: cada vez que se encuentre el antecedente en alguna palabra parcialmente generada, ste debe ser reemplazado por el consecuente, formando as una nueva palabra total o parcialmente generada. Formalmente, una gramtica es una tupla de 4 elementos, , N, P, S, donde: es un conjunto finito no vaco que contiene los smbolos que conforman el alfabeto del lenguaje generado. Tambin se conoce como conjunto de terminales. N es un conjunto finito no vaco que contiene smbolos auxiliares variables o de reemplazo denominados no terminales, que facilitan la escritura de las producciones. N conforman el vocabulario de la gramtica. P es un conjunto finito no vaco de producciones. S N es el smbolo inicial a partir del cual, mediante la aplicacin sucesiva de producciones, se puede generar cualquier palabra vlida del lenguaje. Ejemplo A.1: La siguiente gramtica genera palndromos sobre el alfabeto binario. Es importante destacar que la palabra vaca no pertenece al vocabulario de . , N, P, S: 0, 1 N A P A 0A0 | 1A1 | 0 | 1 | SA Jacqueline Khler C. - USACH

132

Compiladores A continuacin se muestra una notacin alternativa para el mismo ejemplo. , N, P, S: 0, 1 N secuencia P secuencia 0secuencia0 | 1secuencia1 | 0 | 1 | S secuencia Es importante destacar algunos aspectos de notacin, pues es fundamental en la escritura de gramticas poder distinguir claramente entre smbolos terminales y no terminales. Las convenciones que se emplean son las siguientes: 1. Los terminales o elementos de se denotan por: Letras latinas minsculas (, , , ). Palabras en negrita o subrayado (, , . Palabras encerradas entre comillas simples o dobles (ab, 0110, ). 2. Los no terminales o elementos de N se denotan por: Letras latinas maysculas (, , , ). Palabras encerradas entre parntesis triangulares (secuencia, entero, ). 3. Se usan letras griegas minsculas (, , , ) para designar indistintamente smbolos terminales o no terminales, o incluso secuencias de ellos. 4. Una produccin de se denota por bien por . Tanto el antecedente como el consecuente pueden estar conformados por cualquier secuencia no vaca de terminales y no terminales. En el caso del consecuente, puede aparecer tambin la palabra vaca. 5. Las reglas , , se denotan tambin por | | . Ahora que est bien definida la idea de gramtica, es importante estudiar cmo se generan las palabras. Para una gramtica , se dice que una secuencia N genera o deriva otra secuencia N , operacin denotada por , si y solo si para , , , N . Observacin: 1. es una derivacin de n pasos y corresponde a la clausura reflexiva y transitiva de . 2. El conjunto de las palabras generadas sobre N a partir del smbolo inicial se denota por . Ejemplo A.2: Retomemos la gramtica del ejemplo anterior, 0, 1, A, A 0A0 | 1A1 | 0 | 1 | , A. Para generar la secuencia 001101100, se tienen los siguientes pasos: A 0A0 00A00 001A100 0011A1100 001101100. Las derivaciones para la palabra 11, en cambio, son las siguientes: A 1A1 11. Jacqueline Khler C. - USACH

133

Compiladores En este caso, para la segunda derivacin se hace uso de la produccin A . Tambin es posible representar las derivaciones en forma grfica mediante un rbol de anlisis sintctico, el cual tiene las siguientes propiedades: La raz est etiquetada con el smbolo inicial. Cada hoja est etiquetada con un componente lxico o con . Cada nodo interior est etiquetado con un no terminal. Si A es el no terminal que etiqueta a algn nodo interior y X , X , ... X son las etiquetas de los hijos de ese nodo, de izquierda a derecha, entonces X1 X2 Xn es una produccin. Si bien en ocasiones este mecanismo resulta ms cmodo y claro, puede ser inadecuado en ocasiones porque no muestra el orden en que se realizan las sustituciones. La figura A.2 muestra esta representacin para las secuencias generadas en el ejemplo anterior.

FIGURA A.2: rboles de derivacin para las palabras (a) 001101100 y (b) 11 con 0, 1, A, A 0A0 | 1A1 | 0 | 1 | , A. Los ejemplos A.1 y A.2 son bastante sencillos, puesto que se trata de una gramtica con un nico no terminal. Pero podemos tener una gran cantidad de ellos, como ocurre al crear la gramtica de un lenguaje de programacin. As pues, el ejemplo A.3 muestra una gramtica que genera los nmeros naturales. Ejemplo A.3: La gramtica genera el lenguaje de todos los nmeros naturales, respetando las convenciones de notacin habituales al no permitir ceros a la izquierda. La figura A.3 muestra los rboles de derivacin para generar varios nmeros.

Jacqueline Khler C. - USACH

134

Compiladores , N, P, S: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 N signiicativo, digito, secuencia, natural P signiicativo 1 | 2 | 3 | 4 | 5 |6 | 7 |8 | 9, digito 0 |1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9, secuenciadigitosecuencia , natural0 | significativosecuencia S natural La nocin de derivacin de palabras conduce a la definicin de lenguaje generado por una gramtica , denotado por . Corresponde al conjunto de palabras generadas a partir del smbolo inicial y que solo contienen smbolos terminales. Matemticamente: : w. En general, pueden existir varias gramticas que generen un mismo lenguaje, lo que las hace equivalentes. As, .

FIGURA A.3: rboles de derivacin para las palabras (a) 0, (b) 5 y (c) 3907.

Jacqueline Khler C. - USACH

135

Compiladores A.2.1 GRAMTICAS REGULARES Un lenguaje es regular si y solo si puede ser generado constructivamente por composicin de operaciones regulares y operandos de lenguajes regulares a partir de los lenguajes bsicos. Los lenguajes bsicos son: : lenguaje nulo o conjunto vaco. : lenguaje que solo contiene la palabra vaca. : lenguajes que contienen solo una palabra de longitud 1 para cada smbolo del alfabeto. Las operaciones regulares, ordenadas segn su precedencia, son: Clausura o estrella de Kleen: operacin unaria, denotada por como superndice, que indica que el operando puede repetirse cero o ms veces. En ocaciones, se usa tambin la variante denotada por un como superndice, que indica una o ms repeticiones del operando. Concatenacin: operacin binaria que indica que el segundo operando debe ir a continuacin del primero en cualquier palabra vlida del lenguaje. Se denota por la ausencia de operador o, en ocasiones, con un punto. Unin: operacin binaria denotada por que indica que se debe escoger uno de entre sus operandos. Se dice que una gramtica , N, P, S es lineal si y solo si en cada regla de produccin existe a lo ms un no terminal en el lado derecho. En otras palabras, todas las producciones son de la forma A uBv o bien A w, con A, B y , , . Hay dos casos interesantes a considerar en esta definicin: : las producciones son de la forma A B o bien A w. En este caso, se dice que es lineal izquierda. : las producciones son de la forma A uB o bien A w. En este caso, se dice que es lineal derecha. Las gramticas lineales derechas se denominan tambin gramticas regulares, y son las que generan los lenguajes regulares. Ejemplo A.4: a, b, S, A, B, C, S aC, A aA | b, B bB | b, C A | B, S Esta gramtica genera el lenguaje sobre el alfabeto a, b cuyas palabras comienzan con y terminan con , y entre medio pueden contener solo o solo : , , , , , , , Podemos ver que, en este ejemplo, estn presentes las tres operaciones regulares. Los no terminales A, B y C tienen dos producciones cada uno, separadas por |, con lo que con cada uno de ellos tenemos la unin entre dos subsecuencias. La concatenacin est presente en toda produccin que contenga dos o ms smbolos en su lado derecho. En el caso de la produccin de S tenemos una a concatenada con alguna subsecuencia generada a partir de C. Jacqueline Khler C. - USACH

136

Compiladores En el caso de A aA | b tenemos presentes la clausura y la concatenacin, puesto que A genera todas las subsecuencias que comienzan por cero o ms a y terminan con una b. A.2.2 GRAMTICAS LIBRES DE CONTEXTO La sintaxis de los lenguajes de programacin puede ser descrita por medio de gramticas libres de contexto (GLC). stas ofrecen ventajas significativas al momento de disear un lenguaje o escribir un compilador: Especificacin sencilla y fcil de entender para lenguajes de programacin. Facilitar la tarea de construir analizadores sintcticos. Para algunas clases de gramticas incluso pueden ser generados automticamente. Una gramtica adecuada da al lenguaje de programacin una estructura til para la traduccin de cdigo fuente a cdigo objeto y para la deteccin de errores. Resulta ms fcil aadir nuevas construcciones a un lenguaje de programacin ya existente (evolucin). Se define una GLC como una cudrupla , N, , , donde: : conjunto finito de smbolos terminales. N: conjunto finito de no-terminales. P: conjunto de producciones. S: smbolo inicial, que debe ser no-terminal. Una gramtica es un modelo que permite generar secuencias sintcticamente vlidas en algn lenguaje, aunque sin tener en cuenta su significado. Por ejemplo, en castellano podemos decir que una oracin simple tiene un artculo, un sustantivo, un verbo y un punto. De acuerdo a esta definicin, son oraciones El nio corre. y Unos planeta piensa. Tambin podramos pensar en un prrafo como una secuencia de una o ms oraciones. Tomemos el ejemplo anterior del castellano para explicar la definicin de GLC: corresponde al conjunto de elementos que puede aparecer en secuencias vlidas, que en nuestro ejemplo seran los artculos, los verbos, los sustantivos y el punto. Los no terminales pueden pensarse como variables temporales que no pueden aparecer en una secuencia completamente generada, pero que pueden ser reemplazadas por alguna secuencia de terminales y no terminales. En el ejemplo anterior, los no terminales estaran dados por los conceptos de oracin, artculo, sustantivo y verbo. El conjunto de producciones indica cmo pueden ser reemplazados los no terminales. En nuestro ejemplo, tenemos que oracin es reemplazado por la secuencia artculo-sustantivo-verbo-punto. El smbolo inicial es un no terminal especfico a partir del cual se construyen las secuencias vlidas. En nuestro ejemplo sera prrafo. Para llevar esta nocin al modelo anterior, digamos que el smbolo A corresponde a prrafo; B, a oracin; C a artculo, D a sustantivo, y E, a verbo. As, tendramos que: . , El, La, , Unos, nio, planeta, casa, , lpiz, corre, salta, piensa, , vuela. N A, B, C, D, E. P | , Jacqueline Khler C. - USACH

137

Compiladores
., El | La | | Unos, nio | planeta | casa | | lpiz, corre | salta | piensa | | vuela.

S A.

Las gramticas libres de contexto (GLC) son aquellas en que todas las producciones tienen la forma , con y . As, el lado izquierdo de cada produccin contiene nicamente un smbolo no terminal. Esta restriccin no es trivial, puesto que no todos los lenguajes pueden ser generados por una gramtica independiente del contexto. En consecuencia, los lenguajes generados por este tipo de gramticas se denominan lenguajes independientes del contexto. Adems se tiene que las producciones de los no terminales pueden tener cualquier secuencia de terminales y no terminales, dejando fuera la restriccin de tener a lo ms un no terminal al extremo derecho de la produccin. Cabe destacar que el conjunto de los lenguajes regulares es un subconjunto de los lenguajes libres de contexto. Las gramticas de los ejemplos A.1 y A.3 son, en consecuencia, libres de contexto. En el caso de las gramticas lineales (y en particular de las gramticas regulares), en que toda produccin tiene a lo ms un no terminal, no es necesario preocuparse del orden en que se realicen las derivaciones o de si existen rboles distintos para generar una misma palabra, pues hay uno solo por cada secuencia. En el caso de las GLC, como podemos tener ms no terminales en una produccin, s es necesario tomar en consideracin el orden en que se efectan las derivaciones. Por la derecha: siempre se reemplaza el no terminal de ms a la derecha. Por la izquierda: siempre se reemplaza el no terminal de ms a la izquierda. Ejemplo A.5: Dada la GLC , , , ,, , | | | , , muestre una derivacin por la derecha y otra por la izquierda para la palabra . Muestre adems sus rboles de derivacin. Derivacin por la derecha: Derivacin por la izquierda: Las dos derivaciones anteriores pueden representarse por medio del mismo rbol de derivacin, que se muestra en la figura A.4.

IGURA A.4: rbol de derivacin para la palabras . Jacqueline Khler C. - USACH

138

Compiladores Ahora que est clara la idea de derivacin por la derecha y por la izquierda, es importante introducir el concepto de gramtica ambigua. Una gramtica GLC es ambigua tiene dos derivaciones por la izquierda tiene dos derivaciones por la derecha tiene dos rboles de derivacin. Ejemplo A.6: Consideremos como base el ejemplo A.5. Ahora podemos buscar nuevas derivaciones por la izquierda y por la derecha para la misma palabra, as como un nuevo rbol de derivacin, y as probar que es ambigua. Derivacin por la derecha: Derivacin por la izquierda: Al igual que en el ejemplo A.5, las dos derivaciones anteriores pueden representarse por medio del mismo rbol de derivacin (figura A.5).

FIGURA A.5: Otro rbol de derivacin para la palabras . En consecuencia, es ambigua porque existen dos rboles de derivacin diferentes para la misma palabra.

A.2 EXPRESIONES REGULARES


Las expresiones regulares (ER) se utilizan para especificar un lenguaje regular, de una manera finita ms sencilla que la gramtica. Se construyen considerando los siguientes elementos: Smbolos tomados de un alfabeto . La palabra vaca, denotada por . Si bien este smbolo no forma parte del alfabeto, se usa para indicar una palabra que existe, pero que no contiene smbolos. Se puede asociar esta idea a string en programacin que ha sido declarado pero que est inicializado como el string nulo. Los elementos anteriores se relacionan entre s por medio de las operaciones regulares: Clausura o estrella de Kleen, denotada por como superndice. En ocaciones, se usa tambin la variante denotada por un como superndice, que indica una o ms repeticiones del operando. Jacqueline Khler C. - USACH

139

Compiladores Concatenacin, que se denota por la ausencia de operador o, en ocasiones, con un punto. Unin, denotada por . Ejemplo A.7: Algunas expresiones regulares sencillas. Algunas expresiones regulares sencillas son: : representa el lenguaje de todas las palabras que contengan cero o ms y que no contengan otros smbolos, es decir, , , , , , . : representa el lenguaje de todas las palabras que contengan una o ms y que no contengan otros smbolos, es decir, , , , , . : representa el lenguaje de todas las palabras de longitud 1 sobre el alfabeto , , es decir, , . : representa el lenguaje que contiene solamente la palabra conformada por una seguida de una , es decir, . : denota el lenguaje de todas las palabras sobre el alfabeto , que tienen a lo menos un par de consecutivas. Algunos ejemplos de palabras que pertenecen a este lenguaje son: , , , , etc. : denota el lenguaje de todas las palabras sobre el alfabeto , , que comienzan con una , tienen cualquier cantidad de pares consecutivos de y y terminan con otra . Algunos ejemplos de palabras que pertenecen a este lenguaje son: , , , , , etc.

A.3 AUTMATAS FINITOS


Son mecanismos para el reconocimiento de lenguajes regulares que nos permiten automatizar esta tarea. Un autmata finito (AF) es una mquina discreta de estados que tiene las siguientes caractersticas: Es sensible al medio, es decir, puede leer (recibir entradas). Cuenta con un nmero finito de estados internos. Puede reaccionar frente a las entradas cambiando su estado. En general, no tiene mecanismos explcitos para emitir una salida, es decir, No tiene salidas; es decir, no es capaz de modificar su medio. El esquema general de funcionamiento es que, en todo momento, el autmata se encuentra en algn estado o (conjunto de estados) activo o actual, a la espera de recibir una nueva entrada. Cuando esto ocurre, reacciona cambiando su estado actual de acuerdo a transiciones bien definidas y espera una nueva entrada, y as sucesivamente. A cada instante, el AF solo conoce su estado actual, y no lleva un registro de sus estados anteriores. No obstante, el estado actual acta como sntesis de esa trayectoria, pues el alcanzar un estado depende de los estados anteriores. Un ejemplo adecuado para comprender esta idea es el funcionamiento de un ascensor: Cada piso corresponde a un estado, y en todo momento el ascensor sabe dnde se encuentra. Al apretarse un botn, el ascensor debe dirigirse a un nuevo piso de acuerdo a la entrada recibida. Adems de los AF descritos, existen mquinas semejantes que incorporan la capacidad de proporcionar una salida. stas ltimas reciben el nombre de transductores finitos. Jacqueline Khler C. - USACH

140

Compiladores Desde una perspectiva de implementacin fsica, un AF puede verse como un dispositivo conformado por los siguientes elementos, algunos de los cuales se ilustran en la figura A.6: Una cinta infinita que contiene smbolos pertenecientes a un alfabeto, formando una secuencia de entrada. Un cabezal Un cabezal capaz de leer un smbolo de la cinta y desplazarse automticamente hacia la derecha. Un control finito, compuesto de una cantidad, tambin finita, de estados y una especificacin (transicin) que permite determinar el estado siguiente de acuerdo a la lectura realizada en la cinta. Algunos estados distinguibles dentro del conjunto de estados: Un estado inicial. Uno o ms estados finales o de aceptacin (que indican que la secuencia pertenece a un lenguaje dado).

FIGURA A.6: modelo de un autmata finito. Una vez conocidos los elementos de un AF, estamos en condiciones de describir el proceso de aceptacin o reconocimiento de una palabra como parte de un lenguaje regular dado, aunque no se tocar an el tema de la construccin de estas mquinas. Inicialmente, el cabezal lector debe estar situado en el primer smbolo de la cadena de entrada y el AF debe encontrarse en su estado inicial. A continuacin, el cabezal lee el smbolo y se desplaza hacia la derecha en una posicin, y el AF modifica su estado de acuerdo a la entrada recibida y a una especificacin de transicin. La lectura de smbolos y el cambio de estado se repiten sucesivamente hasta que no queden smbolos por leer en la entrada y, en consecuencia, el AF no realice nuevos cambios de estado. Una vez alcanzado este punto, es el observador quien debe determinar si la palabra leda pertenece o no al lenguaje regular dado. Para este fin, basta observar el estado en que se encuentra el autmata. Si es uno de los distinguidos como final o de aceptacin, entonces la palabra de la entrada pertenece al lenguaje. En caso contrario, la palabra no se acepta. Ms formalmente, un AF M es una tupla de 5 elementos, , , , , , donde: es un conjunto finito no vaco de estados. es el alfabeto del lenguaje reconocido y contiene los smbolos que conforman las palabras de la entrada. corresponde a la especificacin de transicin entre estados en base al estado actual y al smbolo ledo desde la entrada. es el estado inicial. Jacqueline Khler C. - USACH

141

Compiladores F Q es el conjunto de estados finales. Podemos distinguir dos grandes clases de AF, dependiendo de la forma que tenga la definicin de transicin : determinsticos y no determinsticos. A.3.1 AUTMATA FINITO DETERMINSTICO (AFD) Su funcin de transicin es de la forma : Q. En otras palabras, dado un estado actual y un smbolo de entrada, retorna el nuevo estado al que debe pasar el AFD. As, la transicin , indica que, al estar en el estado y leer el smbolo , el AFD pasa al siguiente estado . El cabezal pasa automticamente al smbolo siguiente. Cabe destacar que de esta definicin de se desprenden las siguientes conclusiones: Al encontrarse el AFD en un estado y leer un smbolo de entrada, hay uno y solo un estado al que puede avanzar. No puede haber un cambio de estado si no se ha ledo un smbolo desde la entrada. Para todo estado y todo smbolo del alfabeto debe existir una transicin definida, por lo que la cantidad de transiciones de un AFD es de || ||. Ejemplo A.8: Considere el AFD , , , , , donde: , , , , , , , , , , , , , ,

2, 2, 3, 3, 3, 3

Otra manera de representar la funcin de transicin es mediante una tabla, como se muestra en la tabla A.1. \

TABLA A.1: Funcin de transicin para el AFD M. Grficamente, se puede mostrar M como en la figura A.7. Resulta interesante destacar que la figura A.7 ilustra todos los elementos presentes en la definicin formal de M: El conjunto de estados est denotado por el conjunto de crculos rotulados. El alfabeto aparece tcitamente reflejado en las transiciones.

Jacqueline Khler C. - USACH

142

Compiladores Cada transicin de tiene un estado de origen, sealado por la base de la flecha; un estado de destino, sealado por la cabeza de la flecha, y un smbolo asociado. El estado inicial est sealado con una flecha no rotulada que incide en l. Cada estado final de , en este caso solo , se denota por un doble crculo.

FIGURA A.7: Representacin grfica de M. Como punto de partida para algunas definiciones debemos considerar que el concepto bsico necesario para describir la operacin de un AFD es la configuracin o descripcin instantnea, es decir, el estado en que se encuentra el sistema completo en un instante de tiempo. As, la configuracin del AFD en un instante dado est determinada por dos variables: el estado del AFD en que se encuentra el control finito y la posicin del cabezal de lectura, dada por el sufijo an no ledo de la palabra de entrada (incluido el smbolo en que se encuentra el cabezal). As, una configuracin es un par ordenado , , donde es el estado actual y es el sufijo de la entrada que an no ha sido ledo, incluyendo el smbolo sealado por el cabezal. La configuracin inicial siempre est dada por el estado inicial del autmata y el primer smbolo de la entrada. La siguiente nocin importante es la de paso de computacin, correspondiente a un cambio elemental de configuracin. En otras palabras, es una relacin binaria entre dos configuraciones sucesivas de un AFD M, denotada por el smbolo . Estas dos configuraciones estn relacionadas mediante una transicin, es decir: , , , Cabe destacar que denota pasos de computacin y que corresponde a la clausura reflexiva y transitiva de . En tercer lugar, es necesario definir el concepto de aceptacin de una palabra . Se dice que es aceptada por un AFD M si y solo si, comenzando desde la configuracin inicial, al terminar de leer el AFD se encuentra en un estado final, es decir: es aceptada por M , , Ejemplo A.9: Consideremos el AFD del ejemplo A.8 y la entrada abbaab. La configuracin inicial de M est dada por , abbaab. Tras el primer paso de computacin, se tiene:

Jacqueline Khler C. - USACH

143

Compiladores , abbaab , bbaab La secuencia de pasos de computacin siguientes corresponde a: , bbaab , baab , aab , ab , b , As, , abbaab , y adems , por lo que M acepta la palabra . Ejemplo A.10: Consideremos ahora el AFD del ejemplo A.8 y la entrada bab. La configuracin inicial de M est dada por , bab. La secuencia de pasos de computacin para determinar la aceptacin de es: , bab , ab , b , As, , bab , y adems , por lo que M no acepta la palabra . La nocin de aceptacin de una palabra conduce a la nocin de lenguaje aceptado por un AFD M, denotado por , que corresponde al conjunto de todas las palabras aceptadas por M, es decir : es aceptada por M. Ejemplo A.11: Si estudiamos el AFD del ejemplo A.8, podemos notar que corresponde al lenguaje representado por la ER . Una vez conocido , es posible definir la equivalencia entre dos AF: se dice que dos AF y son equivalentes si aceptan el mismo lenguaje, es decir: Otra definicin importante es la de alcanzabilidad de un estado, para la cual se requiere previamente la nocin de transicin extendida. Una funcin de transicin extendida entre dos estados distantes (no vecinos) y y que involucra la lectura de una secuencia de smbolos puede ser definida como una funcin : Q tal que: , , , As, se tiene que un estado es alcanzable desde otro estado si existe una palabra tal que la secuencia de pasos de computacin comenzando desde y el primer smbolo de culmina en cuando no quedan smbolos por leer. Cabe destacar que tiene las siguientes propiedades: , , , , , , , , ,

Jacqueline Khler C. - USACH

144

Compiladores A.3.2 AUTMATA FINITO NO DETERMINSTICO (AFND) Habamos estudiado que el estado al que va un AFD al leer un smbolo de la entrada est completamente determinado, puesto que cada estado cuenta con una y solo una transicin por cada smbolo del alfabeto. En el caso del AFND, en cambio, el cambio de estado solo est parcialmente especificado, puesto que puede ocurrir lo siguiente: Las transiciones entre estados estn dadas por palabras ms que por smbolos individuales, pudiendo incluso haber transiciones con , denominadas transiciones vacas, en que el AF puede cambiar de estado sin necesidad de haber ledo algo desde la entrada. Puede haber ms de una transicin definida desde un estado determinado con alguna lectura dada (transiciones mltiples). En consecuencia, el AFND debe ir a alguno cualquiera de los estados de destino, de manera no determinada. De lo anterior podemos concluir que, en realidad, el AFD estudiado no es ms que un caso particular del AFND. En estos ltimos, la especificacin de transicin es una relacin , conformada por un conjunto de tros , , donde cada tro indica que, estando en el estado , al leer en la entrada una secuencia de smbolos , el AFND puede quedar en el estado . No obstante, como pueden existir dos o ms transiciones que compartan los dos primeros componentes, por ejemplo , , , , , , esto puede reformularse de modo tal que el tercer elemento del tro sea un conjunto de estados: , , , . En el caso de los AFND, las nociones de configuracin, paso de computacin, aceptacin de palabra, aceptacin de lenguaje y equivalencia son semejantes al caso de los AFD. No obstante, la existencia de transiciones vacas da origen a una nueva definicin: la clausura- . Corresponde al conjunto de estados alcanzables desde el estado mediante transiciones vacas, es decir: : , , Se puede extender la nocin de clausura- para un conjunto de estados , que corresponde simplemente a la unin de las clausuras- de cada estado en : Ejemplo A.12: Considere el AFND , , , , , donde: , , , , , , , , , , , , , , , , , , , , ,

1, ,3, 2, ,0, 2, ,3, 3, ,2,3, ,3, 3, ,4,4, ,1,4, ,3

Otra manera de representar la funcin de transicin es mediante una tabla, como se muestra en la tabla A.2.

Jacqueline Khler C. - USACH

145

Compiladores \ , ,

TABLA A.2: Funcin de transicin para el AFND M.

Grficamente, se puede mostrar M como en la figura A.8.

FIGURA A.8: Representacin grfica de M. Adems, las clausuras- para los diferentes estados son: , , , , A.3.3 EQUIVALENCIA ENTRE AFD Y AFND La incorporacin del no determinismo, a pesar de facilitar la capacidad de abstraccin al disear un AF, no aumenta el poder de aceptacin de estos reconocedores, es decir, no incrementa el conjunto de lenguajes que pueden ser reconocidos usando mquinas de estados finitos. As pues, es posible demostrar que para cada AFND existe un AFD equivalente. Para construir un AFD equivalente a un AFND dado, se deben seguir dos grandes pasos: 1. Eliminar aquellas transiciones que avanzan con secuencias cuya longitud es mayor que 1. 2. Eliminar transiciones vacas y transiciones mltiples.

Jacqueline Khler C. - USACH

146

Compiladores Para eliminar las transiciones con secuencias de longitud mayor a 1, se incorporan nuevos estados al AFND a fin de descomponer las transiciones para que solo tengan un smbolo. Sea , , , , un AFND tal que sus transiciones son de la forma , , , donde , y u . Entonces, existe un AFND , , , , tal que: . , con , , , , , tal que | | 1. Note que y comparten , y . La nueva relacin de transicin se obtiene como , , : | | 1 , , , , , , , , , para cada , , tal que y | | 1, como se ilustra en la figura 2.6. El segundo paso, entonces, consiste en eliminar las transiciones vacas y las transiciones mltiples. Para este fin, creamos un conjunto de estados para el AFD , , , , tal que cada uno de sus estados corresponde a un conjunto de estados del AFND , , , , , que sintetiza todos los caminos posibles para reconocer una determinada secuencia. Se puede garantizar que es posible realizar esta tarea, ya que para un conjunto de estados de tamao se tienen 2 subconjuntos. El primer estado del AFD que debemos determinar es el inicial, dado por la clausura- de , el estado inicial de . Los nuevos estados de se determinan junto con las transiciones. Para cada smbolo de se determina el conjunto de estados de que es posible alcanzar desde y se les incorporan sus respectivas clausuras- . Cada nuevo conjunto forma un nuevo estado del AFD. Se repite este proceso hasta que no queden estados sin transiciones definidas. Los estados finales de sern todos aquellos conjuntos de estados que contengan algn estado final de . Ejemplo A.13: Consideremos el AFND de la figura A.9 y determinemos su AFD equivalente . El estado inicial de est dado por la clausura- del estado inicial de : Ahora debemos determinar las transiciones de y sus respectivas clausuras: , 0 , , , , , , , 1 Repetimos el proceso anterior para los nuevos estados: , 0 , , , , , , , , , , 1 , , , , 0 , 1

Jacqueline Khler C. - USACH

147

Compiladores , 0 , , , 1 , , , , , , , , , , , , ,

FIGURA A.9: Eliminacin de transiciones con secuencias de longitud mayor a 1 para un AFND M. , 0 , 1 , 0 , 1 , , 0 , 1 , 0 , 1 , 0 , , 1 , 0 , 1 , 0 , 1 Jacqueline Khler C. - USACH , , , , ,

148

Compiladores , 0 , 1 , , 0 , 1 , , , ,

Note que es el conjunto vaco, por lo que al haber ledo una secuencia no vlida se convierte en un estado trampa. El conjunto de estados finales del AFD es , , , , , , , , , A.3.4 MINIMIZACIN DE AFD En muchas ocasiones, al construir un AFD obtenemos una cantidad de estados superior a la necesaria, por lo que resulta til determinar el AFD equivalente ms pequeo. Para este fin, debemos determinar si hay estados que tengan igual significado. Uno de los mtodos existentes para minimizar un AFD es el de las particiones, en que se van creando grupos cada vez ms pequeos de estados potencialmente equivalentes. Para explicar este mtodo, tomaremos como ejemplo el AFD obtenido en el ejemplo A.13. Para la particin, sabemos que un estado final no puede ser equivalente a otro no final, por lo que creamos un grupo para los estados finales y otro para los no finales: , , , , , , , , , , , Ahora asignamos un nombre a cada grupo y estudiamos el comportamiento de cada estado para cada uno de los smbolos del alfabeto. Llamemos 1 al grupo que contiene a los estados no finales y 2 al grupo de estados finales. va a al leer un 0, y est en el grupo b. Adems, va a al leer un 0, y est en el grupo a. , , , , , , , , , , , Para la siguiente particin, podemos descomponer los grupos de acuerdo a su comportamiento. Son potencialmente equivalentes aquellos estados de un mismo grupo que vayan a las mismas particiones con los mismos smbolos. Es importante destacar que lo que ya estaba separado no puede juntarse nuevamente. As, tenemos que todos los estados se comportan de manera diferente en el grupo a, mientras que hay algunos del grupo b que siguen siendo potencialmente equivalentes. Ahora, tras la nueva particin, repetimos el proceso para ver si hay ms grupos que se puedan separar: , , , , , , Jacqueline Khler C. - USACH

149

Compiladores De acuerdo al resultado anterior, la nueva particin queda: ,

Tras el anlisis de comportamiento de los grupos, se tiene que , por lo que . En consecuencia, podemos tener el AFD mnimo quitando y redirigiendo sus transiciones entrantes a . A.3.5 EQUIVALENCIA ENTRE AF Y ER: MTODO DE THOMPSON Resulta lgico que si las ER representan lenguajes regulares y los AF los reconocen, existe una equivalencia entre ellos. Existen diversos mtodos para pasar de AF a ER y viceversa. No obstante, para este curso solo nos interesa poder construir un AF a partir de una ER, para lo cual utilizaremos el mtodo de Thompson. El mtodo de Thompson opera de manera constructiva, creando AFNDs para subexpresiones sencillas y combinndolos luego de acuerdo a ciertas reglas. Una ventaja de este mtodo es que resulta sencillo de implementar computacionalmente, y facilita las operaciones de unin, concatenacin y clausura al garantizar que cada AFND tiene un nico estado final. De ms est decir que este mtodo se complementa con otros ya estudiados a fin de obtener el AFD mnimo reconocedor. Sea y sean y AFNDs reconocedores de los lenguajes representados por las expresiones regulares y , respectivamente. Ambos AF han sido construidos mediante el mtodo de Thompson, por lo que solo tienen un estado final. Los esquemas bsicos que usa este mtodo son: : el AFND reconocedor para la palabra vaca se construye creando dos estados, uno de ellos inicial que no es de aceptacin y el otro final. Para llegar desde el estado inicial al final, se crea una transicin vaca. La figura A.10 muestra este esquema.

FIGURA A.10: AFND reconocedor de la palabra vaca segn el mtodo de Thompson. : anlogo al anterior, cuando la expresin regular consiste en un solo smbolo, basta con que la transicin del estado inicial al estado final tenga asociado el mismo smbolo (figura A.11).

FIGURA A.11: AFND reconocedor para palabras de largo 1 segn el mtodo de Thompson. Jacqueline Khler C. - USACH

150

Compiladores : el mecanismo para construir un autmata reconocedor correspondiente a la unin de dos subexpresiones regulares es bastante sencillo. Basta con tomar los dos autmatas reconocedores, crear un nuevo estado inicial que vaya con transiciones vacas a los que fueran los estados iniciales y crear un nuevo estado final alcanzable con transiciones vacas desde el que fuera el estado final (figura A.12).

FIGURA A.12: Esquema para construir la unin segn el mtodo de Thompson. : para la concatenacin, basta con hacer que el estado final del primer autmata se fusione con el estado inicial del segundo en un estado no final (figura A.13).

FIGURA A.13: Esquema para construir la concatenacin segn el mtodo de Thompson. : para la clausura reflexiva y transitiva, el mecanismo consiste en incorporar un nuevo estado inicial y un nuevo estado final, a fin de poder reconocer la secuencia vaca. Para dar la opcin de reconocer el lenguaje representado por la subexpresin una nica vez, basta con pasar desde el nuevo estado inicial al estado inicial previo con una transicin vaca, y del estado final previo al nuevo con otra. Por ltimo, para establecer la repeticin de la clausura, basta con pasar con una transicin vaca desde el estado final previo al estado inicial previo. La figura A.14 ilustra esta operacin.

FIGURA A.14: Esquema para construir la estrella de Kleene segn el mtodo de Thompson. Ejemplo A.14: Construir un AF reconocedor para el lenguaje representado por la siguiente expresin regular: 01100 11 01 0.

Jacqueline Khler C. - USACH

151

Compiladores La figura A.15 muestra los pasos seguidos para construir el AFND reconocedor para la ER dada.

FIGURA A.15: Esquema para construir la estrella de Kleene segn el mtodo de Thompson.

Jacqueline Khler C. - USACH

152

Compiladores

FIGURA A.15: Continuacin.

Jacqueline Khler C. - USACH

153

Compiladores

FIGURA A.15: Continuacin.

A.4 AUTMATAS APILADORES Los lenguajes que no son regulares no pueden ser reconocidos usando los AF que ya conocemos. Por lo tanto, para el reconocimiento de lenguajes libres de contexto no regulares (recuerde que ) y que representan estructuras con anidamientos se requiere de un mecanismo que tenga la capacidad de recordar la historia de la entrada leda, en forma explcita y accesible, para tomar una decisin del movimiento a efectuar. Esta capacidad de memoria se logra dotando al AF de una memoria de pila, en la cual se van almacenando smbolos de la entrada que en algn momento son recuperados para su utilizacin. Este modelo de autmata se denomina autmata apilador (AA) o Push-Down Automaton (PDA), pues al momento de leer un smbolo de la entrada, puede empujarlo a la pila (push) o bien extraer informacin de esta ltima (pop), adems de efectuar una transicin de un estado a otro. La figura A.16 muestra el modelo de AA como dispositivo fsico.

Jacqueline Khler C. - USACH

154

Compiladores

FIGURA A.16: Modelo de un autmata apilador. Formalmente, un AA es una 7-tupla , , , , , , , donde: es un conjunto finito de estados. es el alfabeto de entrada. es el alfabeto de la pila. : . es el estado inicial. es el smbolo inicial de la pila. es el conjunto de estados finales. Para comprender adecuadamente este modelo es necesario explicar con detenimiento la relacin de transicin . Por definicin, un AA corresponde a un AFND con una pila asociada. Adems, si se tiene , , , , entonces: El estado actual del AA es , debe leer desde la entrada la secuencia y la secuencia se encuentra al tope de la pila. Una vez efectuada la lectura, pasa al estado y reemplaza por al tope de la pila. Cabe destacar como casos particulares a dos operaciones especiales: Push(a): , , , , inserta el smbolo al tope de la pila. Pop: , , , , quita el smbolo que se encuentre al tope de la pila. Se define como configuracin de un AA al estado en que se encuentra, la porcin no leda de la entrada y el contenido de su pila, es decir, a un elemento de . Un paso de computacin, denotado por , corresponde a un cambio en la configuracin del AA: , , , , . Adems, corresponde a la clausura reflexiva y transitiva de . Se dice que un AA acepta o reconoce una entrada si y solo si, comenzando desde el estado inicial y al momento de terminar de leer , cumple con a lo menos uno de los siguientes criterios: 1. se encuentra en un estado final. Es decir, , , , , , con . 2. tiene su pila vaca, es decir, , , , , . Resulta interesante la conclusin de que, al bastar con uno solo de los criterios para la aceptacin, puede darse que para un AA se tenga .

Jacqueline Khler C. - USACH

155

Compiladores Otra observacin interesante es que, puesto que , se tiene que un AF corresponde a un caso especial de AA en que no se realizan operaciones sobre la pila. El lenguaje aceptado por un AA , , est dado por: : . Dos AA y son equivalentes si y solo s . Ejemplo A.15: Sea el AA , , , , , , , donde: , . 0, 1, . , , . . . . : , 0, , , 0, , , 0, , , 1, , , 1, , , 1, , , , , , , , , , , , 0, , , 1, , , , , : 0, 1 . Ntese que este AA solo puede aceptar una palabra por medio de la pila vaca. Para ver su funcionamiento, veamos en primer lugar la traza para la entrada 011110, que se muestra en la tabla A.3.

Jacqueline Khler C. - USACH

156

Compiladores TABLA A.3: Traza para 011110. La tabla A.4 muestra que la entrada 0111 genera un error, por lo que finalmente tenemos que , pero .

TABLA A.4: Traza para 0111.

Jacqueline Khler C. - USACH

S-ar putea să vă placă și