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.

Ventajas de interpretar
Un intrprete requiere menos memoria
que un compilador.
Permite mayor interaccin con el cdigo
en tiempo de desarrollo.

El compilador tiene una visin ms


detallada del programa, por lo que la
informacin de los mensajes de error es
ms detallada.

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

Ejemplo
foo

n14

73

66.1

sum
00

.5

515

10.

1e67

082
5.5e-10

If
,
!=
(
)

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
ya
, 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
por tener una precedencia ms alta.
y , pero se le asocia solamente

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).
/+0 /+0
( ).

/+0
( ).
//
( ).

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
2 para alguna cadena 2. Para eliminar las recursiones directas, se agrupan las producciones del
no terminal A de la siguiente manera:
2 | 2 | | 25 | 6 | 6 | | 67
Donde ninguna de las producciones 68 comienza por el no terminal . Luego se sustituyen las
producciones de por:
6 | 6 : | | 67 :
2 | 2 : | | 25 : |
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) , , , 7 .
2. Para (; = 1 hasta =):
Para (> = 1 hasta ; 1):
Si las producciones de ? son ? | | @ , reemplazar cada produccin 8 ? A por
A | | @ A.
8
Eliminar recursividad directa por la izquierda para 8 .
Ejemplo 3.1:
Elimine toda recursin por la izquierda para $ = , N, %, C , donde:
= , , , .
N = C, D .
P= C D | ,
DD C|C .
C = C.

Jacqueline Khler C. - USACH

24

Compiladores
Consideremos los no terminales en el orden C, D. 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.
C D | : como C es el primer no terminal, solo se debe eliminar la recursin directa. En este
caso no existe recursin.
D D C | C: en primer lugar, se busca cada produccin de D que comience con algn no
terminal ya revisado (en este caso C) y se crean nuevas producciones para D en que C sea
sustituido por sus producciones. As, se obtiene D D C | D | .
D D C | D | : una vez realizado el primer paso, se debe eliminar la recursin directa por la
izquierda, con lo que se obtiene D D | , C | .
As, la gramtica sin recursin por la izquierda es $ = , N, %, C , donde:
= , , , .
N = C, D, .
P = C D | ,
D D | ,
C | .
C = C.
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 | | | L | (donde representa
a todas las producciones de A que no comienzan por ) por A B | y B | | | L,
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
donde se obtiene:

| "
"
|!

, de

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
T|
| !
T
$|
Y terminar con las de la forma
T| $

Para el no terminal , podemos comenzar con las producciones de la forma

U| | !
U!|
Y terminar con las de la forma

U| U

Para el no terminal !, podemos comenzar con las producciones de la forma !


Jacqueline Khler C. - USACH

26

Compiladores
!

U|

!|

|
!| !

| !
!|

|
!

Continuar con aquellas de la forma !


!
!
U|
!
U|
| !

:
|

Seguir con aquellas de la forma !


!
U|
!
U|

U|

Seguir con aquellas de la forma !


!
V|
| !
|
V
U|!
U
Y ahora tomar las de forma !
! W|
| !
|
W V| U

Para seguir con aquellas de forma !


! W|
U| !
| !
Y luego las de forma ! !
! W|
U| !
U

:
| !
U|

!| !

!| !

!| !

!
:

Para terminar con aquellas de la forma ! :


! W| V
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 =

| ",
T | $,

U | U,
!
W | V,
"
|! ,
"| ,
$ |! ,
T
$| ,
U!| ,
V
U|!
U,
W V| U
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 2, se define su Conjunto Primero, % 2 , como el conjunto
de terminales que pueden iniciar las secuencias derivadas a partir de 2.
Sean ; , , , , 8 , , 7 N y 2 N . Para calcular % c para todos los smbolos c
(terminales y no terminales) de la gramtica, se deben aplicar las siguientes reglas:
1. %
= .
2. % 2 =
.
|
| | 7 , entonces %
=%
3. Si se tienen las producciones
%
% 7 .
4. Si se tiene la produccin
7 , entonces:
%
=%
.
Mientras 8 _` , %
=%
% 8 .
3.1.3.3 Conjunto Siguiente
Dado un no terminal , se define su Conjunto Siguiente, C , 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 C 2 6 para algn 2 y algn 6.
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 $ C , donde $ indica el trmino de la
secuencia (puede pensarse en $ de una manera similar al carcter especial de fin de archivo, "e ).
Sea C el smbolo inicial de la gramtica; sean 2, 6 cadenas de smbolos gramaticales (terminales y no
terminales) y sean , _. El clculo de C c c _requiere de la aplicacin de las siguientes
reglas:
1. Agregar $ a C C .
2. Si se tiene la produccin 2 6, con 6 , entonces C
=% 6 .
3. Si existe la produccin 2 , entonces C
=C .
4. Si se tiene la produccin 2 6, con 6 + , entonces C
=% 6 C .

Jacqueline Khler C. - USACH

28

Compiladores
Note que la regla 4 es una combinacin de las dos reglas anteriores. En ella, 6 podra no anularse, en cuyo
caso debemos considerar la regla 2. Pero tambin puede darse que 6 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 % c y C c , c _, para $ = , N, %, C ,
donde:
= +,, , , / .
N = , , , !, " .
P=
!,
",

| /,
!+ !| ,
" " | .
C= .
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 .
%
%
%

= %h i % / = % / =
=% " =%
= ,/
=% ! =%
= ,/

% ! =% + ! %
% " = % !" %

/ =

,/

= % + = +
=% =

C
= $ %
= $
C ! =C
C ! = C
C
=% ! C
% !
C " =C
C " = C
C
= % " C
% "

= $,
= $,
C ! = + $, $, = $, , +
= $, , +
C " = $, , + $, , + = $, , +,

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 2 | 2 | | 27 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 2, 2 , hacer:
a. Para cada terminal en % 2 , aadir la produccin 2 en jk , l.
b. Si _` , para cada terminal en C , aadir la produccin en jk , l.
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 jk , l. 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, %, C , donde:
= +,, , , / .
N = , , , !, " .
P=
!,
",

| /,
!+ !| ,
Jacqueline Khler C. - USACH

32

Compiladores
" " |
C= .

Muestre la traza y el rbol sintctico para las entradas m = / + / / y m = / /.


Sabemos que:
_` = !, " .
%
=%
=%
=
% ! = +
% " =
C
= C ! = $,
C
= C " = $, , +
C
= $, , +,

,/

En primer lugar, para cada produccin 2 no vaca, debemos aadir dicha produccin en la
posicin jk , l de la tabla para cada terminal % 2 :

TABLA 3.1: Incorporacin de las producciones no vacas al analizador sintctico LL(1).


A continuacin, en aquellos casos en que _` , para cada terminal C , aadir la
produccin en jk , l. 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 m = / + / / .

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

Jacqueline Khler C. - USACH

34

Compiladores
Ejemplo 3.5:
Construya la tabla de anlisis sintctico LL(1) para $ = , N, %, C , donde:
n = k, l, <, >, +,, , #, / .
N = , , ,! .
%=

k l|
,
!| ,
! | ,
! #! | < > | /

C= .

$ 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:
_` = , ,
= % k l %
= #, <, /, , k
%
=% ! %
%
=% ! %
% ! = % #! % <
%

C
C
C
C !

= k %

= k #, <, /, #, <, / =

=%
% = #, <, / = #, <, /,
= % ! = #, <, /
> % / = # < / = #, <, /

= $ % l = $ l =
=%
C
C
%
=C
% ! = $, l
=C
% C ! =

$, l
> = #, <, / $, l $, l, > = $, l, , #, <, /, >
= $, l,
$, l, , #, <, /, > = $, l, , #, <, /, >,

Ahora, para cada produccin 2, debemos aadir dicha produccin en la posicin jk , l de


la tabla para cada terminal % 2 , 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 C , aadir la
produccin en jk , l. Como se puede ver en la tabla 3.4, no es posible construir un
analizador sintctico predictivo para $, pues la tabla presenta conflictos en jk , <l, jk , l,
jk , #l y jk , /l.

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 2 | 6 sean dos producciones
diferentes de $, se cumplen las siguientes condiciones:
Para ningn terminal , tanto 2 como 6 derivan a la vez cadenas que comiencen por .
A lo sumo una de las producciones 2 o 6 pueden derivar la cadena vaca.
Si 6 , 2 no genera ninguna cadena que comience con un terminal en C .

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 jk , l 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 C . Probablemente el anlisis sintctico podr continuar si se eliminan componentes de la
entrada hasta encontrar algn elemento de C
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 m =
/ / usando recuperacin de errores en modo de pnico.
Usaremos C c como conjunto de sincronizacin para cada no terminal:
C
= C ! = $,
= C " = $, , +
C
C
= $, , +,

TABLA 3.5: Traza con recuperacin de errores en modo de pnico para la entrada m = / / 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 2, la reduccin reemplaza la secuencia 2 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, %, C , donde:
n = , , ), ', * .
N = C, , .
%= C
*,
)| ,
'

C = C.
La cadena m =
)'*
)'*
'*
*
C

)'* puede reducirse en los siguientes pasos:


( )
( ))
( ')
(C
*)

Estas reducciones trazan, en sentido inverso, la derivacin:


C

'*

)'*

)'*.

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 m a analizar se encuentra en la entrada seguida del delimitador (es decir, m$). El analizador
sintctico desplaza cero o ms smbolos de la entrada a la pila hasta que se reconozca el lado derecho 2
de una produccin 2 y entonces se reduce 2 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 r > 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,
DD 1 Dt. 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 26m | 260, respectivamente. Se puede
observar que dichas producciones comparten el prefijo 26. Supngase adems que los primeros r
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 26m = 260.

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 cuv produce cuatro elementos LR(0):
Jacqueline Khler C. - USACH

39

Compiladores
cuv
c uv
cu v
cuv
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 cuv. El segundo elemento indica que se ha ledo una cadena
derivable a partir de c y que a continuacin se espera leer otra derivable de uv, etc.
3.5.3.2 Operacin Clausura
Si es un conjunto de elementos LR(0) para una gramtica $, entonces la x yzy{
es el conjunto
de elementos LR(0) construido a partir de segn las siguientes reglas:
1. Inicialmente, hacer x yzy{
= .
2. Si
2 6 x yzy{
y
A es una produccin, hacer
x yzy{
=
x yzy{

A . Aplicar esta regla hasta que no sea posible aadir ms elementos a


x yzy{
.
Ejemplo 3.8:
Sea $ = , N, %, C , donde:
n = +,, , , / .
N = , , ,! .
%=

,
+ | ,
! | !,
!
|/

C= .
Sea adems

Por regla 1:
x yzy{

. Determine x yzy{

Por regla 2, se deben agregar las producciones de :


x yzy{
=
, + ,
Por regla 2, se deben aadir tambin las producciones de :
x yzy{
=
, + , , !, !
Por regla 2, se deben aadir ahora las producciones de !:
x yzy{
=
, + , , !, !, !

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 C C, donde C 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 C C.
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
= x yzy{
= C C . 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 x yzy{
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, %, C , donde:
n = ,, , , , , +

N=
%=

, , ,!

| ,
| ,
||+
|

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

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:
= C
1

2
2

Ahora es necesario determinar los elementos LR(0) que conforman cada uno de los nuevos
estados, as como las transiciones de estos ltimos:
= x yzy{
= C ,
As, se tiene que:
=C

Jacqueline Khler C. - USACH

10

7
11

11

12
12

7
13

14
8

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 C C con $.
3. Para cada estado en que se tenga algn elemento LR(0) de la forma 2 , incorporar una reduccin por
dicha produccin en ese estado para cada smbolo en C

4. Toda casilla no definida de la tabla corresponde a un error.


Ejemplo 3.10:
Construya la tabla de anlisis sintctico SLR para la gramtica del ejemplo anterior. Muestre la
traza y el rbol sintctico para m = + .
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:
_` =
%
%
%

= % %h i % % + =
+ = , , , +
=%
%
=%
= , , , +
=% %
=% %
=%
%
= , , , +

Jacqueline Khler C. - USACH

43

Compiladores

TABLA 3.7: Desplazamientos y configuracin de aceptacin para el analizador sintctico SLR.


C
C
C

= $ %
=C
C
=C
C

%
= $
= $, ,
% = $, , = $, ,,
C
= $, ,,

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 C . 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 m = + .

FIGURA 3.4: Traza y rbol sintctico resultante para la entrada m = + .


Ejemplo 3.11:
Construya un analizador sintctico SLR para $ = , N, %, C , donde:
n=

N=

,@

%=

, ,

,
,
@

C=
Muestre la traza para m = @ .
Comenzamos por la construccin del AFD:
= C

=C

Jacqueline Khler C. - USACH

45

Compiladores
=

A continuacin, necesitamos conocer C , C


y C , y asignar nombres a las producciones:
C
= $ %
C
= $ % @
%
C
= $ @ C
=
= $, @ C
= $, @
C
=C
C
= $, @ C
C
= $, @
%=

Comenzamos por la construccin del AFD:


= C
1

2
3

=C
=


@
@

5
5

,C

= $ %
C
= $ % @
= $, @ C
= $, @
=C
C
= $, @ C
C

C
%=

A continuacin, necesitamos conocer C


C

yC
%

, y asignar nombres a las producciones:


C

= $ @ C

= $, @

La tabla 3.9 muestra el analizador sintctico SLR resultante, mientras que la traza 3.10 muestra
la traza para la entrada m = @ .
Ejemplo 3.12:
Construya un analizador sintctico SLR para $ = , N, %, C , donde:
n = +,, , , /

N= "

% = " " + " | " " |" + " " | " | /

C="

Jacqueline Khler C. - USACH

46

Compiladores

TABLA 3.9: Analizador sintctico SLR del ejemplo 3.11 terminado.

TABLA 3.10: Traza para la entrada m = @ con el analizador sintctico SLR de la tabla 3.9.
Comenzamos por la construccin del AFD:
= C "
" 1
" " + "
" 1
" " + " " " 1
" " "
" 1
" "
2
" /
/ 3
=C"
" " +"
+
" " +" " +
" " "

4
4

=" "
" 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
=" "

" " +"
+
" " +" " +
" " "

=" "+"
" " + " "
" " +"
+
" " +" " +
" " "

=" ""
" " +"
"
" " +" " "
"
" " "

= " " + " "


" " "
11
" " + "
" 11
" " + " " "
" " "
" 11
" "
2
/ 3
" /

4
4

10
4

=" "+""
" ""
" " +"
+ 4
" " +" " +
" " "
5

10
4
4
5

=" "
A continuacin, necesitamos conocer C " y asignar nombres a las producciones:
C " = $ % +" C " % " C " % +" " % " C " %
= $ + +
= $, +,,
% = " " + " | " " |" + " " | "

|/

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 2 6 A y tiene mayor precedencia que , entonces se
escoge la reduccin. En caso contrario, se escoge el desplazamiento.
Si se tiene la configuracin 2 6 A 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 t2 y conservamos
nicamente t3.
En jk7, +l 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 j k7,l se tiene " + " ". En otras palabras, debemos decidir si reducir suma o leer la
multiplicacin. Como tiene mayor precedencia, conservamos el desplazamiento.
En jk8, +l nos encontramos ante el escenario " " +". Como tiene mayor precedencia,
conservamos la reduccin.
En jk8,l se tiene " " ". En otras palabras, Como es asociativo por la derecha,
conservamos el desplazamiento.

Jacqueline Khler C. - USACH

49

Compiladores
Los conflictos restantes en jk11, +l y jk11,l son anlogos a los de j k8, +l y j k8,l,
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 C .
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 m = // + / + / 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 m = // + / + / 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 m = // + / + / 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 m = // + / + /.

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 2 6, . El
smbolo de anticipacin indica lo que debiera leerse en la entrada tras reducir por la produccin
26. En otras palabras, el smbolo de anticipacin es un elemento de C
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, DD 1 CDt Dt 1 $D$.
Sean los elementos LR(1) 2 , y 2 , . 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 m = // + / + / 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 x yzy{
es el conjunto
de elementos LR(1) construido a partir de segn las siguientes reglas:
1. Inicialmente, hacer x yzy{
= .
2. Si 2 6, x yzy{
y A es una produccin, hacer:
x yzy{
= x yzy{

A, % 6 . Aplicar esta regla hasta que no sea


posible aadir ms elementos a x yzy{
.

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 C C. Luego se determina el estado inicial del AFD haciendo
=
x yzy{ C : C, $ . Ntese que se escoge $ como smbolo de anticipacin porque C : genera al
smbolo inicial de la gramtica. Hay que recordar que C 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 x yzy{
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 C : C , $ con $.
3. Para cada estado en que se tenga algn elemento LR(1) de la forma 2 , , 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, %, C , donde:
n= ,
N= ,
%=

|
C=
Compare el analizador sintctico LR(1) con otro SLR para la misma gramtica.
Comenzamos por la construccin del AFD LR(1):
= C , $

,$

,

,

,

,

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
= C , $

,$
, /
, /

de la siguiente forma:

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


= C , $
=

, $

,$
, $
, $

,$
, $
, $

, /
, /
, /

, /

, $

, /

, $

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:
= C


,

=C
=

,$

Para construir la tabla SLR necesitamos conocer C


yC
C
= $
C
=%
C
C
=%
%
$ =

, $

$ = $, ,

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
CDt Dt 1 .
Ejemplo 3.17:
Construya un analizador sintctico LR(1) para $ = , N, %, C , donde:
n = =,, /
N = C, D, t
% = C D = t | t,
D t | /,
tD
C=C
Compare el analizador sintctico LR(1) con otro SLR para la misma gramtica.
Comenzamos por la construccin del AFD LR(1):
= C C, $
C
C D = t, $ D
C t, $
t
D t, =/$
D /, =/$
/
t D, $
D

= D t , =/$

= C D = t , $
= t D , $

= C t , $
= D t, =/$
t D, =/$
D t, =/$
D /, =/$

t
D

/

= t D , =/$

= C C , $
= C D = t, $
t D , $

= C D = t, $
t D, $
D t, $
D /, $

t
D

/

= D / , =/$

= D t, $
t D, $
D t, $
D /, $

t
D

/

= D / , $
= D t , $

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:
% = C D = t |t , D t | / , t D
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:
= C C
C
C D = t
D
C t,
t
D t

D /
/
t D
D
= C C
= C D = t
tD

=Ct

= D t
t D
D t
D /

t
D

/

=D/
= C D = t
t D
D t
D /

t
D

/

= D t
=tD
=CD=t

Para construir la tabla SLR necesitamos conocer C C , C D y C t :


C C = $
C D = % = t C t = = C t = = $, = = $, =
C t = C C C C C D = $ = C t = $, =
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 DD 1 CDt D Dt Dt 1 $D .
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
= D t, =/$ t
t D, =/$
D
D t, =/$
D /, =/$
/
Tambin es muy semejante a
= D / , =/$
Lo mismo ocurre con
= D t , =/$
As como con y
= t D , =/$

= D t, $
t D, $
D t, $
D /, $

t
D

/

= D / , $
= D t , $
= t D , $

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):
= C C, $
C
C D = t, $ D
C t, $
t
D t, =/$
D /, =/$
/
t D, $
D

= t D , =/$
= C D = t , $

= C t , $
= D t, =/$
t D, =/$
D t, =/$
D /, =/$

= C D = t, $
t D, $
D t, $
D /, $
= D t , =/$

= C C , $
= C D = t, $
t D , $

= D / , =/$

t
D

/

Jacqueline Khler C. - USACH

t
D

/

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, %, C , con:
n = , , ), ', *
N= , ,
%=

|
|
|
,
) | ',
)|*
C=
Qu puede concluir acerca de $?
Comencemos por la construccin del analizador sintctico LR(1):
= C , $

=

,$

,$

),

,$

',

,$

),
*,
= C , $
=

= ,$
,$
=

),
)
= ) ,
',
'
) ,
),
)
*,
*
= ' ,
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, %, C , con:
n = , @, , , k, l
N=
%=
@ |
|k l|
C=
Resuelva conflictos considerando que @ es asociativo por la izquierda.
Comenzamos por la construccin del AFD LALR:
= C , $

@ , $/@

, $/@

k l, $/@
k
, $/@

=C

, $
@ , $/@

= C , $/@/ /l
@ , /@

, /@
k l, /@
, /@

= C k l, $/@/ /l
@ , l/@

, l/@
k l, l/@
, l/@

, $/@/ /l

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

, $/@/ /l
k l, $/@/ /l
, $/@/ /l

=C

, $/@/ /l
@ , /@

=Ck

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

=C

, $/@/ /l

= C k l , $/@/ /l

Asignamos nombres a las producciones y construimos la tabla 3.24, correspondiente al


analizador sintctico LALR.
%=
@ |
|k l |

Jacqueline Khler C. - USACH

64

Compiladores

TABLA 3.24: Analizador sintctico LALR del ejemplo 3.20.


El conflicto existente en jk8, @l 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, %, C , con:
n=

N=

%=

C=

, , ,!

@ |

| @/ | ,

| 0 | 0 |

k l| !/ | 0, ! # |

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, %, C , con:
n=

N=
%=

C=

,, . , +, ,

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:
, ., +. Asuma que . y + son asociativos por la izquierda.

c. Muestre la traza y el rbol resultante para m = + . 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 m = / / . 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).

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 2 tiene asociado un
conjunto de reglas semnticas, de la forma + ) , ) , , )@ , donde:
+ es una funcin.
)8 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 )8 .

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 ", j y se le asocia
un atributo sintetizado llamado x y la regla semntica correspondiente calcula el valor del
atributo x del no terminal del lado izquierdo de la produccin a partir de los atributos x de
los no terminales del lado derecho. El componente lxico tiene un atributo sintetizado
x*/, cuyo valor es proporcionado por el analizador lxico. La regla semntica asociada a la
produccin D " para el no terminal D 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, . x . x*/, establece el atributo . x con un valor de
3, que es el valor de . x*/. De manera similar, se asigna tambin valor 3 al atributo
j. x. A continuacin se procede del mismo modo con el siguiente subrbol izquierdo, de
donde se obtiene que . x 5. Al usar la produccin j j , se tiene que j. x
j . x . x = 3 5 = 15. Finalmente, la regla semntica asociada a D " 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 j tiene un atributo sintetizado tipo, cuyo valor se determina a partir de la palabra
clave de la declaracin. La regla semntica D. *{ j. ;, asociada a la produccin ! jD,
asigna el tipo de la declaracin al atributo heredado D. *{. Las reglas semnticas asociadas con
las producciones de D 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 r hacer:
En el grafo de dependencias, construir una arista desde el

Jacqueline Khler C. - USACH

70

Compiladores
nodo de )8 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 ". x " . x + " . x, asociada a la produccin " " + " .

FIGURA 4.3: ". x se sintetiza a partir de " . x y " . x.


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(, ; y;*{' , '*{*) ): crea un nodo para un operador con etiqueta y dos
campos con punteros a ; y;*{' 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
crearHoja(

, x ): crea un nodo para un nmero con etiqueta y un campo que contiene


x, 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( + , , )
Donde:
8 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
se muestra en la tabla 4.4.

+ 1.0 y sea la tabla de prioridades de conversin que

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 z se utiliza una funcin de dispersin z que devuelve un
entero entre 0 y 1. Si s se encuentra en la tabla de smbolos, estar en la lista numerada con
z . 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 /: = 0 , donde:
/, 0 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 / + 0 se puede
traducir en una secuencia:
:= 0
:= / +
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 /: = 0 , donde es una operacin lgica o
aritmtica.
2. Instrucciones de asignacin de la forma /: = 0, donde es una operacin unaria.
3. Proposiciones de copia de la forma /: = 0, donde se asigna a / el valor de 0.
4. El salto incondicional ", donde " es la etiqueta de la proposicin de tres direcciones que
debe ejecutarse a continuacin.
5. Los saltos condicionales como / 0 ".
6.

/ y , = para llamadas a procedimientos y 0, donde:

Jacqueline Khler C. - USACH

109

Compiladores
/ es un parmetro.
corresponde a un procedimiento o funcin.
= es la cantidad de parmetros del procedimiento.
0 representa el valor devuelto (es opcional).
Por ejemplo, la llamada al procedimiento % / , / , , /7 queda:
/
/

/7
, =
7. Las asignaciones con ndices de la forma /: = 0k;l y /k;l: = 0. La primera asigna a / el valor de la
posicin en ; unidades de memoria ms all de la posicin 0. La segunda asigna el contenido de la
posicin en ; unidades de memoria ms all de la posicin / al valor de 0.
8. Las asignaciones de direcciones y punteros de la forma /: = &0, /: = 0 y /: = 0. La primera de
estas proposiciones asigna a / la direccin de 0. La segunda asigna a / el valor contenido en la
direccin apuntada por 0. La tercera hace que el valor del objeto apuntado por / tenga igual valor
que 0.
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 {*zyx ', 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 {*zyx '.
Saltos condicionales e incondicionales: se pone la etiqueta objeto en {*zyx '.
En general, los contenidos de los campos { , { y {*zyx ' 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 /k;l: = 0 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 / 0k;l 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) /k;l: = 0.
(b) / 0k;l.
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 : = ) +


indirectos.

) representado mediante triples

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
'*zx ;*=, 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 '*zx ;*= 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 '*zx ;*=. Al incorporar un nuevo smbolo, es
necesario aumentar '*zx ;*= 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:
;={'y);{ = {*, ;, '*zx ;*= : crea una nueva entrada en la tabla de smbolos para
= {* y le asocia su tipo de dato (;) y su posicin relativa en la memoria ('*zx ;*=).
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.
{{ 0 x, ; : expresin de tipo que indica que indica que la variable a la que se asocia esta
expresin es un arreglo de tamao x 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:
){* {j x {*; : 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 *=) * '.
;={'y);{ x , = {*, ;, '*zx ;*= : crea en la tabla de smbolos apuntada por
x una nueva entrada para = {*, con su tipo ; y su posicin relativa '*zx ;*=.
';{ =) x , =) : registra el ancho acumulado de todas las entradas de tabla en el
encabezamiento asociado a dicha tabla de smbolos.
;={'y);{%{) x , = {*, x _y* : crea en la tabla de smbolos apuntada por x
una nueva entrada para el procedimiento = {*. El puntero x _y* apunta a la tabla de
smbolos del procedimiento nombre.

FIGURA 6.3: Tablas de smbolos para procedimientos anidados.


Tambin es interesante sealar que:
x %{ es una pila que almacena punteros a procedimientos exteriores.
Jacqueline Khler C. - USACH

114

Compiladores
'*zx ;*= es una pila que almacena la siguiente posicin relativa disponible para un
nombre local del procedimiento en curso. Convertir '*zx ;*= 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 j D! , 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 0, entonces 0
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 C. )'; representa el cdigo de tres direcciones para la asignacin de C.
". xy { corresponde al nombre que contendr el valor de ".
". )'; es la secuencia de proposiciones de tres direcciones que evalan ".
La funcin *_y* retorna el nombre para un nuevo temporal.
La notacin *= / : = 0 + representa la proposicin de tres direcciones /: = 0 + .
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 yz) { = {* 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 *_y* 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 +

/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 +
/2 + 5.

*=zy 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, z* da el ndice de la siguiente proposicin de tres
direcciones en la secuencia de salida (*;;{ debe incrementar z* 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

<

) >=

! = ).

108:
109:
110: ! = ) 113
111: 0
112: 114
113: 1
114:

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 z* + ; ;=+ , donde ;=+ es el lmite inferior de los
subndices y z* 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 k; lk; l se puede
calcular como z* + h ; ;=+ = + ; ;=+ i , 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:
D. '*zx ;*= = significa que D corresponde a un simple.
Para las expresiones aritmticas se usa el esquema de la tabla 6.10.
Cuando se hace la reduccin " D, siendo D un arreglo, se usa la indizacin para obtener el
contenido de D. xy {kD. '*zx ;*=l.
La funcin =) x;z ". {; devuelve el tamao de los elementos de la matriz, mientras que
) x;z ". {; entrega la base de la matriz.

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 / k0lkl se
muestra en el listado 6.5.

Jacqueline Khler C. - USACH

121

Compiladores
LISTADO 6.5: Cdigo intermedio para la expresin / k0lkl.
0:
1:
2:
3:
4:
5:
6: /

1000
01
20
+
1
4
k l

// base
// y - ;=+
// ; ;=+
// ; ;=+
// ; ;=+
// h ; ;=+

=
= +;
= + ; ;=+
= + ; ;=+ i

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

4: 6
5: 7
6: 0 3
7: / 0

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;

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: 0 3
7: 9
8: 0 5
9: / 0

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 0
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: / 0

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 0
2: ) 1
3: )! = 1 6
4: 5
5: 7
6: )! = 3 8

7: + 1
8: 12
9: )! = 5 12
10: 8
11: 12
12: 13: / 0

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 .

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

4: / / ;
5: ; ; + 1
6: 2

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);

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: / / ;

3: ; ; + 1
4: ; < 10 2
5: 6

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);

El listado 6.18 muestra el cdigo intermedio resultante.


LISTADO 6.18: Cdigo de tres direcciones para una sentencia .
0: / 0
1: ; 0
2: / / ;

3: ; ; + 1
4: ; == 10 6
5: 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.

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

4: / ;
5: / / +
6: ; ; + 1
7: 2

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: C D;z " , x;z " x;z ", " | ". 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.

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

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

Jacqueline Khler C. - USACH

+ ) . Tradzcala a:

128

Compiladores
2. Considere la asignacin / =
a. Un AST.
b. Cdigo de tres direcciones.
c. Cudruplos.
d. Triples.
e. Triples indirectos.

)+' +h /

) i. Tradzcala a:

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 j; 3 j; 2 j; 1 j; 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 |
S=A
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 (, m;x* , .
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 (2, 6, A, ) para designar indistintamente smbolos terminales o
no terminales, o incluso secuencias de ellos.
4. Una produccin de % se denota por 2 6 bien por 2 = 6. 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 2 6, 2 A, 2

se denotan tambin por 2 6 | A | .

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 2 : = 2 22 N genera o deriva otra
secuencia 6 : = 2 62 N , operacin denotada por 2 6, si y solo si 2 6 % para
2 , 2 , 2, 6 N .
Observacin:
1. L 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 C se denota por
C $ .
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 , ... XL 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 = significativo, digito, secuencia, natural
P = significativo 1 | 2 | 3 | 4 | 5 |6 | 7 |8 | 9, digito
0 |1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9, secuencia digitosecuencia | , natural
0 | significativosecuencia
S = natural
La nocin de derivacin de palabras conduce a la definicin de lenguaje generado por una gramtica $,
denotado por D $ . Corresponde al conjunto de palabras generadas a partir del smbolo inicial C y que
solo contienen smbolos terminales. Matemticamente: D $ = C $ = m : C w .
En general, pueden existir varias gramticas que generen un mismo lenguaje, lo que las hace
equivalentes. As, $ $ : zz; D $ = D $ .

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 y, , m . Hay dos casos interesantes a considerar en esta
definicin:
y = : 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
terminan con , y entre medio pueden contener solo o solo :
D=

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, %, C , 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
2, con _ y 2 _ . 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 m D $ m tiene dos
derivaciones por la izquierda m tiene dos derivaciones por la derecha m 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, D = , , ,
,
, .
: representa el lenguaje de todas las palabras que contengan una o ms
y que no
contengan otros smbolos, es decir, D = , ,
,
, .
+ : representa el lenguaje de todas las palabras de longitud 1 sobre el alfabeto = , ,
es decir, D = , .
: representa el lenguaje que contiene solamente la palabra conformada por una seguida
de una , es decir, D =
.

+
+
: 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:
,

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
, m , donde es el estado actual y m 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:
,m

, m m = m :

, =

Cabe destacar que 7 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 m . Se dice que es
aceptada por un AFD M si y solo si, comenzando desde la configuracin inicial, al terminar de leer m
el AFD se encuentra en un estado final, es decir:
m es aceptada por M

, m

Ejemplo A.9:
Consideremos el AFD del ejemplo A.8 y la entrada m = 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
As,

, baab

, abbaab

, aab
y adems

, ab

,b

, por lo que M acepta la palabra m.

Ejemplo A.10:
Consideremos ahora el AFD del ejemplo A.8 y la entrada m = bab. La configuracin inicial de
M est dada por
, bab . La secuencia de pasos de computacin para determinar la aceptacin
de m es:
, bab
As,

, ab

, bab

,b

y adems

, por lo que M no acepta la palabra m.

La nocin de aceptacin de una palabra conduce a la nocin de lenguaje aceptado por un AFD M,
denotado por D
, que corresponde al conjunto de todas las palabras aceptadas por M, es decir

D
= m : m es aceptada por M .
Ejemplo A.11:
Si estudiamos el AFD del ejemplo A.8, podemos notar que D

+ .
representado por la ER { =

corresponde al lenguaje

Una vez conocido D


, es posible definir la equivalencia entre dos AF: se dice que dos AF
son equivalentes si aceptan el mismo lenguaje, es decir:

=D

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:
:

, m = m

, m

As, se tiene que un estado es alcanzable desde otro estado si existe una palabra m tal que la
secuencia de pasos de computacin comenzando desde y el primer smbolo de m culmina en
cuando no quedan smbolos por leer. Cabe destacar que : tiene las siguientes propiedades:
:
, =
:
, =
,
:
, m = :
, ,m
:
, m = : , , m

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 , m, donde cada tro indica que, estando en el estado , al
leer en la entrada una secuencia de smbolos m, el AFND puede quedar en el estado . No obstante,
como pueden existir dos o ms transiciones que compartan los dos primeros componentes, por ejemplo
, m, , , m, , esto puede reformularse de modo tal que el tercer elemento del tro sea un
conjunto de estados: , m, : , .
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:

= z : , z,
Se puede extender la nocin de clausura- para un conjunto de estados j, que corresponde
simplemente a la unin de las clausuras- de cada estado en j:

j =

Ejemplo A.12:
Considere el AFND = , , , , , donde:
=
, , , ,
= ,
=
, ,
,
, ,
,
, ,
,
, ,
,
, ,
, ,
,
, ,
,
, ,
,
, ,
,
, ,
,
,
=
=
,

, ,

, ,

Otra manera de representar la funcin de transicin es mediante una tabla, como se muestra en
la tabla A.2.
\m
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 , y, , donde ,

u . Entonces, existe un AFND = , , , , tal que:



.
:
= %8 , con %8 = , , , @ , y, tal que |y| = r > 1.
Note que y comparten , y .

:
nueva relacin de transicin
se obtiene como
=
, y, : |y| 1
, , , , , , , @ , @ , para cada , y, tal que y = @ y |y| = r >
1, como se ilustra en la figura 2.6.

La

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 27 subconjuntos.
El primer estado del AFD
el estado inicial de .

que debemos determinar es el inicial,

dado por la clausura- 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
inicial de est dado por la clausura- del estado inicial de :

Ahora debemos determinar las transiciones de


,0 = ,$

,$ =
,1 =
==

y sus respectivas clausuras:


, , !, ", $ =

Repetimos el proceso anterior para los nuevos estados:


,0 = , ,$

, , $ = , , , !, ", $ =
,1 = ,T

,T = ,T =
,0 =
,1 =

Jacqueline Khler C. - USACH

=
=

. El estado

147

Compiladores
,0 =
,1 =

, ,$
, ,T

, ,$
, ,T

=
=

, , , !, ", $ =
, ,T =

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:
%

'

x x

,
;

>

. En
Tras el anlisis de comportamiento de los grupos, se tiene que % = % , por lo que
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:
{ 011 00 + 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 Dt DD ) 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
, , , ,
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.

, ,

, donde:

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
h ? , 6i, entonces:
8 , m, 2
El estado actual del AA es 8 , debe leer desde la entrada la secuencia m y la secuencia 2 se
encuentra al tope de la pila.
Una vez efectuada la lectura, pasa al estado ? y reemplaza 2 por 6 al tope de la pila.
Cabe destacar como casos particulares a dos operaciones especiales:
h ? , i, inserta el smbolo al tope de la pila.
Push(a):
8 , m,
h ? , i, quita el smbolo que se encuentre al tope de la pila.
Pop:
8 , m,
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:

8 , m, 62 h ? , m, A2i. Adems, corresponde a la clausura reflexiva y transitiva de .


Se dice que un AA acepta o reconoce una entrada m si y solo si, comenzando desde el estado
inicial y al momento de terminar de leer m, cumple con a lo menos uno de los siguientes criterios:
1.
se encuentra en un estado final. Es decir,
, m, 8 , , 2 , con 8 .

2.
tiene su pila vaca, es decir,
, m,
.
8, ,
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 Dt DD , se tiene que un AF corresponde a un caso
especial de AA en que no se realizan operaciones sobre la pila.
,D

El lenguaje aceptado por un AA


Dos AA

son equivalentes si y solo s D

Ejemplo A.15:
Sea el AA

, est dado por: D

,
.
0, 1, ) .
t, , $ .
.
t.
.

, ,

, , , ,

, 0, t
, 0,
, 0, $
, 1, t
, 1,
, 1, $
, ), t
, ),
, ), $
, 0,
, 1, $
, ,t

m: *z )* ' {

, donde:

, t
,
, $
, $t
,$
, $$
,t
,
,$
,
,
,

m)m : m 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 m
011)110, que se muestra
en la tabla A.3.

Jacqueline Khler C. - USACH

156

Compiladores
TABLA A.3: Traza para m
La tabla A.4 muestra que la entrada m
tenemos que m D
, pero m D
.

01)11 genera un error, por lo que finalmente

TABLA A.4: Traza para m

Jacqueline Khler C. - USACH

011)110.

01)11.

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