Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Compiladores e Interpretes Teoria y Practica PDF
Compiladores e Interpretes Teoria y Practica PDF
teora y prctica
Manuel Alfonseca Moreno
Marina de la Cruz Echeanda
Alfonso Ortega de la Puente
Estrella Pulido Caabate
www.pearsoneducacion.com
Compiladores e Intrpretes: Teora y Prctica, es un texto dirigido a
Escuelas y Facultades de Informtica, en cuya titulacin existe esta asignatura
bajo el nombre usual de Procesadores de Lenguaje. El libro describe con
detalle y ejemplos las distintas fases del proceso de compilacin o
interpretacin y los algoritmos que se pueden utilizar para implementarlas.
Cada captulo se acompaa con ejercicios, cuya solucin aparece en
www.librosite.net/pulido, donde tambin se incluye el cdigo completo de
un compilador para un lenguaje sencillo, as como los pasos que hay que dar
para construirlo.
ISBN 978-84-205-5031-2
Otro libro de inters:
Alfred V. Aho, Ravi Sethi y
Jeffrey Ullman:
Compiladores: Principios, tcnicas
y herramientas. Mxico,
Pearson Addison Wesley, 1990.
ISBN: 968-4443-33-1
Incluye:
LibroSite es una pgina web
asociada al libro, con una
gran variedad de recursos y
material adicional tanto para
los profesores como para
estudiantes. Apoyos a la
docencia, ejercicios de
autocontrol, enlaces
relacionados, material de
investigacin, etc., hacen de
LibroSite el complemento
acadmico perfecto para este
libro.
C
o
m
p
i
l
a
d
o
r
e
s
e
i
n
t
e
r
p
r
e
t
e
s
Alfonseca
de la Cruz
Ortega
Pulido
www.librosite.net/pulido
00a-portadillas 9/2/06 12:22 Pgina 2
Compiladores
e intrpretes:
teora y prctica
00a-portadillas 9/2/06 12:22 Pgina 1
00a-portadillas 9/2/06 12:22 Pgina 2
Compiladores
e intrpretes:
teora y prctica
Manuel Alfonseca Moreno
Marina de la Cruz Echeanda
Alfonso Ortega de la Puente
Estrella Pulido Caabate
Departamento de Ingeniera Informtica
Universidad Autnoma de Madrid
Madrid Mxico Santaf de Bogot Buenos Aires Caracas Lima
Montevideo San Juan San Jos Santiago So Paulo Reading, Massachusetts Harlow, England
00a-portadillas 9/2/06 12:22 Pgina 3
Queda prohibida, salvo excepcin prevista en la Ley, cualquier forma de reproduccin,
distribucin, comunicacin pblica y transformacin de esta obra sin contar con autorizacin de
los titulares de propiedad intelectual. La infraccin de los derechos mencionados puede ser
constitutiva de delito contra la propiedad intelectual (arts. 270 y sgts. Cdigo Penal).
DERECHOS RESERVADOS
2006 por PEARSON EDUCACIN, S. A.
Ribera del Loira, 28
28042 Madrid (Espaa)
Alfonseca Moreno, M.; de la Cruz Echeanda, M.; Ortega de la Puente, A.;
Pulido Caabate, E.
Compiladores e intrpretes: teora y prctica
ISBN: 84-205-5031-0
ISBN 13: 978-84-205-5031-2
Depsito Legal: M.
PEARSON PRENTICE HALL es un sello editorial autorizado de PEARSON EDUCACIN, S.A.
Equipo editorial
Editor: Miguel Martn-Romo
Tcnico editorial: Marta Caicoya
Equipo de produccin:
Director: Jos A. Clares
Tcnico: Jos A. Hernn
Diseo de cubierta: Equipo de diseo de PEARSON EDUCACIN, S. A.
Composicin: JOSUR TRATAMIENTOS DE TEXTOS, S.L.
Impreso por:
IMPRESO EN ESPAA - PRINTED IN SPAIN
Este libro ha sido impreso con papel y tintas ecolgico
ALFONSECA MORENO, M.; DE LA CRUZ
ECHEANDA, M.; ORTEGA DE LA PUENTE, A.;
PULIDO CAABATE, E.
Compiladores e intrpretes: teora y prctica
PEARSON EDUCACIN, S.A., Madrid, 2006
ISBN 10: 84-205-5031-0
ISBN 13: 978-84-205-5031-2
MATERIA: Informtica, 004.4
Formato: 195 250 mm Pginas: 376
Datos de catalogacin bibliogrfica
00a-portadillas 9/2/06 12:22 Pgina 4
Contenido 1
Captulo 1. Lenguajes, gramticas y procesadores 1
1.1. Gdel y Turing 1
1.2. Autmatas 2
1.3. Lenguajes y gramticas 3
1.4. Mquinas abstractas y lenguajes formales 3
1.5. Alfabetos, smbolos y palabras 5
1.6. Operaciones con palabras 5
1.6.1. Concatenacin de dos palabras 5
1.6.2. Monoide libre 6
1.6.3. Potencia de una palabra 7
1.6.4. Reflexin de una palabra 7
1.7. Lenguajes 7
1.7.1. Unin de lenguajes 8
1.7.2. Concatenacin de lenguajes 8
1.7.3. Binoide libre 9
1.7.4. Potencia de un lenguaje 9
1.7.5. Clausura positiva de un lenguaje 10
1.7.6. Iteracin, cierre o clausura de un lenguaje 10
1.7.7. Reflexin de lenguajes 10
1.7.8. Otras operaciones 11
1.8. Ejercicios 11
1.9. Conceptos bsicos sobre gramticas 11
1.9.1. Notacin de Backus 12
1.9.2. Derivacin directa 13
1.9.3. Derivacin 13
1.9.4. Relacin de Thue 14
1.9.5. Formas sentenciales y sentencias 14
00c-CONTENIDO 9/2/06 11:47 Pgina v
1.9.6. Lenguaje asociado a una gramtica 14
1.9.7. Frases y asideros 14
1.9.8. Recursividad 15
1.9.9. Ejercicios 15
1.10. Tipos de gramticas 15
1.10.1. Gramticas de tipo 0 16
1.10.2. Gramticas de tipo 1 17
1.10.3. Gramticas de tipo 2 17
1.10.4. Gramticas de tipo 3 18
1.10.5. Gramticas equivalentes 18
1.10.6. Ejercicios 19
1.11. rboles de derivacin 19
1.11.1. Subrbol 21
1.11.2. Ambigedad 21
1.11.3. Ejercicios 22
1.12. Gramticas limpias y bien formadas 23
1.12.1. Reglas innecesarias 23
1.12.2. Smbolos inaccesibles 23
1.12.3. Reglas superfluas 23
1.12.4. Eliminacin de smbolos no generativos 24
1.12.5. Eliminacin de reglas no generativas 24
1.12.6. Eliminacin de reglas de redenominacin 24
1.12.7. Ejemplo 24
1.12.8. Ejercicio 25
1.13. Lenguajes naturales y artificiales 25
1.13.1. Lenguajes de programacin de computadoras 26
1.13.2. Procesadores de lenguaje 26
1.13.3. Partes de un procesador de lenguaje 28
1.13.4. Nota sobre sintaxis y semntica 29
1.14. Resumen 30
1.15. Bibliografa 31
Captulo 2. Tabla de smbolos 33
2.1. Complejidad temporal de los algoritmos
de bsqueda 33
2.1.1. Bsqueda lineal 34
2.1.2. Bsqueda binaria 35
2.1.3. Bsqueda con rboles binarios ordenados 35
2.1.4. Bsqueda con rboles AVL 37
2.1.5. Resumen de rendimientos 37
2.2. El tipo de datos diccionario 38
2.2.1. Estructura de datos y operaciones 38
2.2.2. Implementacin con vectores ordenados 39
2.2.3. Implementacin con rboles binarios ordenados 40
2.2.4. Implementacin con AVL 44
vi Compiladores e intrpretes: teora y prctica
00c-CONTENIDO 9/2/06 11:47 Pgina vi
2.3. Implementacin del tipo de dato diccionario con tablas hash 44
2.3.1. Conclusiones sobre rendimiento 45
2.3.2. Conceptos relacionados con tablas hash 45
2.3.3. Funciones hash 46
2.3.4. Factor de carga 50
2.3.5. Solucin de las colisiones 50
2.3.6. Hash con direccionamiento abierto 50
2.3.7. Hash con encadenamiento 55
2.4. Tablas de smbolos para lenguajes con estructuras de bloques 56
2.4.1. Conceptos 56
2.4.2. Uso de una tabla por mbito 58
2.4.3. Evaluacin de estas tcnicas 60
2.4.4. Uso de una sola tabla para todos los mbitos 60
2.5. Informacin adicional sobre los identificadores
en las tablas de smbolos 62
2.6. Resumen 62
2.7. Ejercicios y otro material prctico 62
2.8. Bibliografa 63
Captulo 3. Anlisis morfolgico 65
3.1. Introduccin 65
3.2. Expresiones regulares 67
3.3. Autmata Finito No Determinista (AFND) para una
expresin regular 68
3.4. Autmata Finito Determinista (AFD) equivalente a un AFND 71
3.5. Autmata finito mnimo equivalente a uno dado 73
3.6. Implementacin de autmatas finitos deterministas 75
3.7. Otras tareas del analizador morfolgico 76
3.8. Errores morfolgicos 77
3.9. Generacin automtica de analizadores morfolgicos:
la herramienta lex 78
3.9.1. Expresiones regulares en lex 78
3.9.2. El fichero de especificacin lex 79
3.9.3. Cmo funciona yylex()? 80
3.9.4. Condiciones de inicio 83
3.10. Resumen 85
3.11. Ejercicios 86
3.12. Bibliografa 87
Captulo 4. Anlisis sintctico 89
4.1. Conjuntos importantes en una gramtica 90
4.2. Anlisis sintctico descendente 93
4.2.1. Anlisis descendente con vuelta atrs 93
4.2.2. Anlisis descendente selectivo 99
Contenido vii
00c-CONTENIDO 9/2/06 11:47 Pgina vii
4.2.3. Anlisis LL(1) mediante el uso de la forma normal
de Greibach 99
4.2.4. Anlisis LL(1) mediante el uso de tablas de anlisis 111
4.3. Anlisis sintctico ascendente 114
4.3.1. Introduccin a las tcnicas del anlisis ascendente 114
4.3.2. Algoritmo general para el anlisis ascendente 116
4.3.3. Anlisis LR(0) 127
4.3.4. De LR(0) a SLR(1) 138
4.3.5. Anlisis SLR(1) 140
4.3.6. Ms all de SLR(1) 147
4.3.7. Anlisis LR(1) 148
4.3.8. LALR(1) 159
4.4. Gramticas de precedencia simple 168
4.4.1. Notas sobre la teora de relaciones 169
4.4.2. Relaciones y matrices booleanas 170
4.4.3. Relaciones y conjuntos importantes
de la gramtica 171
4.4.4. Relaciones de precedencia 175
4.4.5. Gramtica de precedencia simple 176
4.4.6. Construccin de las relaciones 177
4.4.7. Algoritmo de anlisis 177
4.4.8. Funciones de precedencia 180
4.5. Resumen 183
4.6. Ejercicios 183
Captulo 5. Anlisis semntico 191
5.1. Introduccin al anlisis semntico 191
5.1.1. Introduccin a la semntica de los lenguajes de
programacin de alto nivel 191
5.1.2. Objetivos del analizador semntico 192
5.1.3. Anlisis semntico y generacin de cdigo 194
5.1.4. Anlisis semntico en compiladores de un solo paso 196
5.1.5. Anlisis semntico en compiladores de ms
de un paso 199
5.2. Gramticas de atributos 199
5.2.1. Descripcin informal de las gramticas de atributos
y ejemplos de introduccin 199
5.2.2. Descripcin formal de las gramticas de atributos 203
5.2.3. Propagacin de atributos y tipos de atributos segn
su clculo 205
5.2.4. Algunas extensiones 208
5.2.5. Nociones de programacin con gramticas de atributos 211
5.3. Incorporacin del analizador semntico al sintctico 217
5.3.1. Dnde se guardan los valores de los atributos
semnticos? 217
5.3.2. Orden de recorrido del rbol de anlisis 218
5.3.3. Tipos interesantes de gramticas de atributos 221
viii Compiladores e intrpretes: teora y prctica
00c-CONTENIDO 9/2/06 11:47 Pgina viii
5.3.4. Tcnica general del anlisis semntico en compiladores
de dos o ms pasos 223
5.3.5. Evaluacin de los atributos por los analizadores
semnticos en los compiladores de slo un paso 227
5.4. Gramticas de atributos para el anlisis semntico de los
lenguajes de programacin 228
5.4.1. Algunas observaciones sobre la informacin semntica
necesaria para el anlisis de los lenguajes de
programacin de alto nivel 229
5.4.2. Declaracin de identificadores 230
5.4.3. Expresiones aritmticas 231
5.4.4. Asignacin de valor a los identificadores 231
5.4.5. Instrucciones condicionales 232
5.4.6. Instrucciones iterativas (bucles) 233
5.4.7. Procedimientos 233
5.5. Algunas herramientas para la generacin de analizadores
semnticos 233
5.5.1. Estructura del fichero fuente de yacc 234
5.5.2. Seccin de definiciones 235
5.5.3. Seccin de reglas 237
5.5.4. Seccin de funciones de usuario 239
5.5.5. Conexin entre yacc y lex 240
5.6. Resumen 240
5.7. Bibliografa 241
5.8. Ejercicios 241
Captulo 6. Generacin de cdigo 243
6.1. Generacin directa de cdigo ensamblador en un solo paso 243
6.1.1. Gestin de los registros de la mquina 246
6.1.2. Expresiones 249
6.1.3. Punteros 253
6.1.4. Asignacin 254
6.1.5. Entrada y salida de datos 254
6.1.6. Instrucciones condicionales 254
6.1.7. Bucles 256
6.1.8. Funciones 258
6.2. Cdigo intermedio 258
6.2.1. Notacin sufija 259
6.2.2. Cudruplas 267
6.3. Resumen 282
6.4. Ejercicios 282
Captulo 7. Optimizacin de cdigo 285
7.1. Tipos de optimizaciones 286
7.1.1. Optimizaciones dependientes de la mquina 286
Contenido ix
00c-CONTENIDO 9/2/06 11:47 Pgina ix
7.1.2. Optimizaciones independientes de la mquina 286
7.2. Instrucciones especiales 286
7.3. Reordenacin de cdigo 287
7.4. Ejecucin en tiempo de compilacin 288
7.4.1. Algoritmo para la ejecucin en tiempo de compilacin 289
7.5. Eliminacin de redundancias 292
7.5.1. Algoritmo para la eliminacin de redundancias 293
7.6. Reordenacin de operaciones 300
7.6.1. Orden cannico entre los operandos de las expresiones
aritmticas 301
7.6.2. Aumento del uso de operaciones mondicas 301
7.6.3. Reduccin del nmero de variables intermedias 302
7.7. Optimizacin de bucles 304
7.7.1. Algoritmo para la optimizacin de bucles mediante
reduccin de fuerza 305
7.7.2. Algunas observaciones sobre la optimizacin de bucles
por reduccin de fuerza 309
7.8. Optimizacin de regiones 309
7.8.1. Algoritmo de planificacin de optimizaciones utilizando
regiones 311
7.9. Identificacin y eliminacin de las asignaciones muertas 313
7.10. Resumen 314
7.11. Ejercicios 315
Captulo 8. Intrpretes 317
8.1. Lenguajes interpretativos 318
8.2 Comparacin entre compiladores e intrpretes 319
8.2.1. Ventajas de los intrpretes 319
8.2.2. Desventajas de los intrpretes 321
8.3. Aplicaciones de los intrpretes 322
8.4. Estructura de un intrprete 322
8.4.1. Diferencias entre un ejecutor y un generador
de cdigo 323
8.4.2. Distintos tipos de tabla de smbolos en un intrprete 324
8.5. Resumen 326
8.6. Bibliografa 326
Captulo 9. Tratamiento de errores 327
9.1. Deteccin de todos los errores verdaderos 327
9.2. Deteccin incorrecta de errores falsos 329
9.3. Generacin de mensajes de error innecesarios 330
9.4. Correccin automtica de errores 331
9.5. Recuperacin de errores en un intrprete 333
9.6. Resumen 334
x Compiladores e intrpretes: teora y prctica
00c-CONTENIDO 9/2/06 11:47 Pgina x
Captulo 10. Gestin de la memoria 337
10.1. Gestin de la memoria en un compilador 337
10.2. Gestin de la memoria en un intrprete 347
10.2.1. Algoritmos de recoleccin automtica de basura 348
10.3. Resumen 353
ndice analtico 355
Contenido xi
00c-CONTENIDO 9/2/06 11:47 Pgina xi
00c-CONTENIDO 9/2/06 11:47 Pgina xii
Captulo 1
Lenguajes, gramticas
y procesadores
Gdel y Turing
En el ao 1931 se produjo una revolucin en las ciencias matemticas, con el descubrimiento
realizado por Kurt Gdel (1906-1978) y publicado en su famoso artculo [1], que quiz deba con-
siderarse el avance matemtico ms importante del siglo XX.
En sntesis, el teorema de Gdel dice lo siguiente: Toda formulacin axiomtica consisten-
te de la teora de nmeros contiene proposiciones indecidibles. Es decir, cualquier teora ma-
temtica ha de ser incompleta. Siempre habr en ella afirmaciones que no se podrn demostrar
ni negar.
El teorema de Gdel puso punto final a las esperanzas de los matemticos de construir un sis-
tema completo y consistente, en el que fuese posible demostrar cualquier teorema. Estas espe-
ranzas haban sido expresadas en 1900 por David Hilbert (1862-1943), quien generaliz sus
puntos de vista proponiendo el problema de la decisin (Entscheidungsproblem), cuyo objetivo
era descubrir un mtodo general para decidir si una frmula lgica es verdadera o falsa.
En 1937, el matemtico ingls Alan Mathison Turing (1912-1953) public otro artculo fa-
moso sobre los nmeros calculables, que desarroll el teorema de Gdel y puede considerarse
el origen oficial de la informtica terica. En este artculo introdujo la mquina de Turing, una
entidad matemtica abstracta que formaliz por primera vez el concepto de algoritmo
1
y re-
sult ser precursora de las mquinas de calcular automticas, que comenzaron a extenderse a
partir de la dcada siguiente. Adems, el teorema de Turing demostraba que existen problemas
irresolubles, es decir, que ninguna mquina de Turing (y, por ende, ninguna computadora) ser
1.1
1
Recurdese que se llama algoritmo a un conjunto de reglas que permite obtener un resultado determinado a par-
tir de ciertos datos de partida. El nombre procede del matemtico persa Abu Jafar Mohammed ibn Musa al-Jowrizm,
autor de un tratado de aritmtica que se public hacia el ao 825 y que fue muy conocido durante la Edad Media.
01-CAPITULO 01 9/2/06 11:42 Pgina 1
capaz de obtener su solucin. Por ello se considera a Turing el padre de la teora de la com-
putabilidad.
El teorema de Turing es, en el fondo, equivalente al teorema de Gdel. Si el segundo de-
muestra que no todos los teoremas pueden demostrarse, el primero dice que no todos los proble-
mas pueden resolverse. Adems, la demostracin de ambos teoremas es muy parecida. Uno de
esos problemas que no se puede resolver es el denominadol problema de la parada de la mqui-
na de Turing. Puede demostrarse que la suposicin de que es posible predecir, dada la descrip-
cin de una mquina de Turing y la entrada que recibe, si llegar a pararse o si continuar
procesando informacin indefinidamente, lleva a una contradiccin. Esta forma de demostracin,
muy utilizada en las ciencias matemticas, se llama reduccin al absurdo.
Autmatas
El segundo eslabn en la cadena vino de un campo completamente diferente: la ingeniera elc-
trica. En 1938, otro artculo famoso [2] del matemtico norteamericano Claude Elwood Shannon
(1916-2001), quien ms tarde sera ms conocido por su teora matemtica de la comunicacin,
vino a establecer las bases para la aplicacin de la lgica matemtica a los circuitos combinato-
rios y secuenciales, construidos al principio con rels y luego con dispositivos electrnicos de va-
co y de estado slido. A lo largo de las dcadas siguientes, las ideas de Shannon se convirtieron
en la teora de las mquinas secuenciales y de los autmatas finitos.
Los autmatas son sistemas capaces de transmitir informacin. En sentido amplio, todo siste-
ma que acepta seales de su entorno y, como resultado, cambia de estado y transmite otras sea-
les al medio, puede considerarse como un autmata. Con esta definicin, cualquier mquina, una
central telefnica, una computadora, e incluso los seres vivos, los seres humanos y las socieda-
des se comportaran como autmatas. Este concepto de autmata es demasiado general para su
estudio terico, por lo que se hace necesario introducir limitaciones en su definicin.
Desde su nacimiento, la teora de autmatas encontr aplicacin en campos muy diversos,
pero que tienen en comn el manejo de conceptos como el control, la accin, la memoria. A me-
nudo, los objetos que se controlan, o se recuerdan, son smbolos, palabras o frases de algn tipo.
Estos son algunos de los campos en los que ha encontrado aplicacin la Teora de Autmatas:
Teora de la comunicacin.
Teora del control.
Lgica de los circuitos secuenciales.
Computadoras.
Redes conmutadoras y codificadoras.
Reconocimiento de patrones.
Fisiologa del sistema nervioso.
Estructura y anlisis de los lenguajes de programacin para computadoras.
Traduccin automtica de lenguajes.
Teora algebraica de lenguajes.
1.2
2 Compiladores e intrpretes: teora y prctica
01-CAPITULO 01 9/2/06 11:42 Pgina 2
Se sabe que un autmata (o una mquina secuencial) recibe informacin de su entorno (en-
trada o estmulo), la transforma y genera nueva informacin, que puede transmitirse al entorno
(salida o respuesta). Puede darse el caso de que la informacin que devuelve el autmata sea muy
reducida: podra ser una seal binaria (como el encendido o apagado de una lmpara), que indi-
ca si la entrada recibida por el autmata es aceptada o rechazada por ste. Tendramos, en este
caso, un autmata aceptador.
Lenguajes y gramticas
El tercer eslabn del proceso surgi de un campo que tradicionalmente no haba recibido con-
sideracin de cientfico: la lingstica, la teora de los lenguajes y las gramticas. En la dca-
da de 1950, el lingista norteamericano Avram Noam Chomsky (1928-) revolucion su campo
de actividad con la teora de las gramticas transformacionales [3, 4], que estableci las ba-
ses de la lingstica matemtica y proporcion una herramienta que, aunque Chomsky la des-
arroll para aplicarla a los lenguajes naturales, facilit considerablemente el estudio y la
formalizacin de los lenguajes de computadora, que comenzaban a aparecer precisamente en
aquella poca.
El estudio de los lenguajes se divide en el anlisis de la estructura de las frases (gramtica) y
de su significado (semntica). A su vez, la gramtica puede analizar las formas que toman las pa-
labras (morfologa), su combinacin para formar frases correctas (sintaxis) y las propiedades del
lenguaje hablado (fontica). Por el momento, tan slo esta ltima no se aplica a los lenguajes de
computadora.
Aunque desde el punto de vista terico la distincin entre sintaxis y semntica es un poco ar-
tificial, tiene una enorme trascendencia desde el punto de vista prctico, especialmente para el di-
seo y construccin de compiladores, objeto de este libro.
Mquinas abstractas y lenguajes formales
La teora de lenguajes formales result tener una relacin sorprendente con la teora de mqui-
nas abstractas. Los mismos fenmenos aparecen independientemente en ambas disciplinas y es
posible establecer correspondencias entre ellas (lo que los matemticos llamaran un isomor-
fismo).
Chomsky clasific las gramticas y los lenguajes formales de acuerdo con una jerarqua de
cuatro grados, cada uno de los cuales contiene a todos los siguientes. El ms general se llama gra-
mticas del tipo 0 de Chomsky. A estas gramticas no se les impone restriccin alguna. En con-
secuencia, el conjunto de los lenguajes que representan coincide con el de todos los lenguajes
posibles.
El segundo grado es el de las gramticas del tipo 1, que introducen algunas limitaciones en la
estructura de las frases, aunque se permite que el valor sintctico de las palabras dependa de su
1.4
1.3
Captulo 1. Lenguajes, gramticas y procesadores 3
01-CAPITULO 01 9/2/06 11:42 Pgina 3
posicin en la frase, es decir, de su contexto. Por ello, los lenguajes representados por estas
gramticas se llaman lenguajes sensibles al contexto.
Las gramticas del tercer nivel son las del tipo 2 de Chomsky, que restringen ms la libertad
de formacin de las reglas gramaticales: en las gramticas de este tipo, el valor sintctico de una
palabra es independiente de su posicin en la frase. Por ello, los lenguajes representados por es-
tas gramticas se denominan lenguajes independientes del contexto.
Por ltimo, las gramticas del tipo 3 de Chomsky tienen la estructura ms sencilla y corres-
ponden a los lenguajes regulares. En la prctica, todos los lenguajes de computadora quedan por
encima de este nivel, pero los lenguajes regulares no dejan por eso de tener aplicacin.
Pues bien: paralelamente a esta jerarqua de gramticas y lenguajes, existe otra de mquinas
abstractas equivalentes. A las gramticas del tipo 0 les corresponden las mquinas de Turing; a
las del tipo 1, los autmatas acotados linealmente; a las del tipo 2, los autmatas a pila; final-
mente, a las del tipo 3, corresponden los autmatas finitos. Cada uno de estos tipos de mquinas
es capaz de resolver problemas cada vez ms complicados: los ms sencillos, que corresponden
a los autmatas finitos, se engloban en el lgebra de las expresiones regulares. Los ms comple-
jos precisan de la capacidad de una mquina de Turing (o de cualquier otro dispositivo equiva-
lente, computacionalmente completo, como una computadora digital) y se denominan problemas
recursivamente enumerables. Y, por supuesto, segn descubri Turing, existen an otros proble-
mas que no tienen solucin: los problemas no computables.
La Figura 1.1 resume la relacin entre las cuatro jerarquas de las gramticas, los lenguajes,
las mquinas abstractas y los problemas que son capaces de resolver.
4 Compiladores e intrpretes: teora y prctica
Gramticas
tipo 0
de Chomsky
Gramticas
tipo 1
de Chomsky
Gramticas
tipo 2
de Chomsky
Gramticas
tipo 3
de Chomsky
Lenguajes
computables
Lenguajes
dependientes
del contexto
Lenguajes
independientes
del contexto
Lenguajes
regulares
Mquinas
de Turing
Autmatas
lineales
acotados
Autmatas
a pila
Autmatas
finitos
deterministas
Problemas no
computables
Problemas
recursivamente
enumerables
Expresiones
regulares
Figura 1.1. Relacin jerrquica de las mquinas abstractas y los lenguajes formales.
01-CAPITULO 01 9/2/06 11:42 Pgina 4
Alfabetos, smbolos y palabras
Se llama alfabeto a un conjunto finito, no vaco. Los elementos de un alfabeto se llaman smbo-
los. Un alfabeto se define por enumeracin de los smbolos que contiene. Por ejemplo:
1
= {A,B,C,D,E,...,Z}
2
= {0,1}
3
= {0,1,2,3,4,5,6,7,8,9,.}
4
= {/,\}
Se llama palabra, formada con los smbolos de un alfabeto, a una secuencia finita de los sm-
bolos de ese alfabeto. Se utilizarn letras minsculas como x o y para representar las palabras de
un alfabeto:
x = JUAN (palabra sobre
1
)
y = 1234 (palabra sobre
3
)
Se llama longitud de una palabra al nmero de letras que la componen. La longitud de la pa-
labra x se representa con la notacin |x|. La palabra cuya longitud es cero se llama palabra va-
ca y se representa con la letra griega lambda (). Evidentemente, cualquiera que sea el alfabeto
considerado, siempre puede formarse con sus smbolos la palabra vaca.
El conjunto de todas las palabras que se pueden formar con las letras de un alfabeto se llama
lenguaje universal de . De momento se utilizar la notacin W() para representarlo. Es evidente
que W() es un conjunto infinito. Incluso en el peor caso, si el alfabeto slo tiene una letra (por
ejemplo, = {a}), las palabras que podremos formar son:
W() = {, a, aa, aaa, ...}
Es obvio que este conjunto tiene infinitos elementos. Obsrvese que la palabra vaca pertene-
ce a los lenguajes universales de todos los alfabetos posibles.
Operaciones con palabras
Esta seccin define algunas operaciones sobre el conjunto W() de todas las palabras que se pue-
den construir con las letras de un alfabeto = {a
1
, a
2
, a
3
, ...}.
1.6.1. Concatenacin de dos palabras
Sean dos palabras x e y tales que x W(), y W(). Suponiendo que x tiene i letras, e y
tiene j letras:
x = a
1
a
2
...a
i
y = b
1
b
2
...b
j
1.6
1.5
Captulo 1. Lenguajes, gramticas y procesadores 5
01-CAPITULO 01 9/2/06 11:42 Pgina 5
Donde todas las letras a
p
, b
q
son smbolos del alfabeto . Se llama concatenacin de las
palabras x e y (y se representa xy) a otra palabra z, que se obtiene poniendo las letras de y a
continuacin de las letras de x:
z = xy = a
1
...a
i
b
1
...b
j
La concatenacin se representa a veces tambin x.y. Esta operacin tiene las siguientes pro-
piedades:
1. Operacin cerrada: la concatenacin de dos palabras de W() es una palabra de W().
x W() y W() xy W()
2. Propiedad asociativa:
x(yz) = (xy)z
Por cumplir las dos propiedades anteriores, la operacin de concatenacin de las palabras de
un alfabeto es un semigrupo.
3. Existencia de elemento neutro. La palabra vaca () es el elemento neutro de la concate-
nacin de palabras, tanto por la derecha, como por la izquierda. En efecto, sea x una pa-
labra cualquiera. Se cumple que:
x = x = x
Por cumplir las tres propiedades anteriores, la operacin de concatenacin de las palabras de
un alfabeto es un monoide (semigrupo con elemento neutro).
4. La concatenacin de palabras no tiene la propiedad conmutativa, como demuestra un con-
traejemplo. Sean las palabras x=abc, y=ad. Se verifica que
xy=abcad
yx=adabc
Es evidente que xy no es igual a yx.
Sea z = xy. Se dice que x es cabeza de z y que y es cola de z. Adems, x es cabeza propia de
z si y no es la palabra vaca. De igual manera, y es cola propia de z si x no es la palabra vaca.
Se observar que la funcin longitud de una palabra tiene, respecto a la concatenacin, propie-
dades semejantes a las de la funcin logaritmo respecto a la multiplicacin de nmeros reales:
|xy| = |x| + |y|
1.6.2. Monoide libre
Sea un alfabeto . Cada una de sus letras puede considerarse como una palabra de longitud igual
a 1, perteneciente a W(). Aplicando a estas palabras elementales la operacin concatenacin,
puede formarse cualquier palabra de W() excepto , la palabra vaca. Se dice entonces que es
un conjunto de generadores de W()-{}. Este conjunto, junto con la operacin concatenacin,
es un semigrupo, pero no un monoide (pues carece de elemento neutro). Se dice que W()-{}
6 Compiladores e intrpretes: teora y prctica
01-CAPITULO 01 9/2/06 11:42 Pgina 6
es el semigrupo libre engendrado por . Aadiendo ahora la palabra vaca, diremos que W() es
el monoide libre generado por .
1.6.3. Potencia de una palabra
Estrictamente hablando, sta no es una operacin nueva, sino una notacin que reduce algunos
casos de la operacin anterior.
Se llama potencia i-sima de una palabra a la operacin que consiste en concatenarla consigo
misma i veces. Como la concatenacin tiene la propiedad asociativa, no es preciso especificar el
orden en que tienen que efectuarse las operaciones.
x
i
= xxx...x (i veces)
Definiremos tambin x
1
= x.
Es evidente que se verifica que:
x
i+1
= x
i
x = xx
i
(i>0)
x
i
x
j
= x
i+j
(i,j>0)
Para que las dos relaciones anteriores se cumplan tambin para i,j = 0 bastar con definir,
para todo x,
x
0
=
Tambin en este caso, las propiedades de la funcin longitud son semejantes a las del logarit-
mo.
|x
i
| = i.|x|
1.6.4. Reflexin de una palabra
Sea x = a
1
a
2
...a
n
. Se llama palabra refleja o inversa de x, y se representa x
-1
:
x
-1
= a
n
...a
2
a
1
Es decir, a la que est formada por las mismas letras en orden inverso. La funcin longitud es
invariante respecto a la reflexin de palabras:
|x
-1
| = |x|
Lenguajes
Se llama lenguaje sobre el alfabeto a todo subconjunto del lenguaje universal de .
L W()
En particular, el conjunto vaco es un subconjunto de W() y se llama por ello lenguaje
vaco. Este lenguaje no debe confundirse con el que contiene como nico elemento la palabra
1.7
Captulo 1. Lenguajes, gramticas y procesadores 7
01-CAPITULO 01 9/2/06 11:42 Pgina 7
vaca, {}, que tambin es un subconjunto (diferente) de W(). Para distinguirlos, hay que fijar-
se en que el cardinal (el nmero de elementos) de estos dos conjuntos es distinto.
c() = 0
c({}) = 1
Obsrvese que tanto como {} son lenguajes sobre cualquier alfabeto. Por otra parte, un al-
fabeto puede considerarse tambin como uno de los lenguajes generados por l mismo: el que
contiene todas las palabras de una sola letra.
1.7.1. Unin de lenguajes
Sean dos lenguajes definidos sobre el mismo alfabeto, L
1
W(), L
2
W(). Llamamos unin
de los dos lenguajes, L
1
L
2
, al lenguaje definido as:
{x | x L
1
x L
2
}
Es decir, al conjunto formado por las palabras que pertenezcan indistintamente a uno u otro
de los dos lenguajes. La unin de lenguajes tiene las siguientes propiedades:
1. Operacin cerrada: la unin de dos lenguajes sobre el mismo alfabeto es tambin un len-
guaje sobre dicho alfabeto.
2. Propiedad asociativa: (L
1
L
2
) L
3
= L
1
(L
2
L
3
).
3. Existencia de elemento neutro: cualquiera que sea el lenguaje L, el lenguaje vaco cum-
ple que
L = L = L
Por cumplir las tres propiedades anteriores, la unin de lenguajes es un monoide.
4. Propiedad conmutativa: cualesquiera que sean L
1
y L
2
, se verifica que L
1
L
2
= L
2
L
1
.
Por tener las cuatro propiedades anteriores, la unin de lenguajes es un monoide abe-
liano.
5. Propiedad idempotente: cualquiera que sea L, se verifica que
L L = L
1.7.2. Concatenacin de lenguajes
Sean dos lenguajes definidos sobre el mismo alfabeto, L
1
W(), L
2
W(). Llamamos con-
catenacin de los dos lenguajes, L
1
L
2
, al lenguaje definido as:
{xy | xL
1
yL
2
}
Es decir: todas las palabras del lenguaje concatenacin se forman concatenando una palabra
del primer lenguaje con otra del segundo.
i=1
L
i
Es decir, el lenguaje obtenido uniendo el lenguaje L con todas sus potencias posibles, excep-
to L
0
. Obviamente, ninguna clausura positiva contiene la palabra vaca, a menos que dicha pala-
bra est en L.
Puesto que el alfabeto es tambin un lenguaje sobre , puede aplicrsele esta operacin. Se
ver entonces que
+
= W()-{}
1.7.6. Iteracin, cierre o clausura de un lenguaje
La iteracin, cierre o clausura de un lenguaje L se define as:
L
*
=
i=0
L
i
Es decir, el lenguaje obtenido uniendo el lenguaje L con todas sus potencias posibles, incluso
L
0
. Obviamente, todas las clausuras contienen la palabra vaca.
Son evidentes las siguientes identidades:
L
*
= L
+
{}
L
+
= LL
*
= L
*
L
Puesto que el alfabeto es tambin un lenguaje sobre , puede aplicrsele esta operacin. Se
ver entonces que
*
= W()
A partir de este momento, representaremos al lenguaje universal sobre el alfabeto con el
smbolo
*
.
1.7.7. Reflexin de lenguajes
Sea L un lenguaje cualquiera. Se llama lenguaje reflejo o inverso de L, y se representa con L
-1
:
{x
-1
| xL}
Es decir, al que contiene las palabras inversas a las de L.
10 Compiladores e intrpretes: teora y prctica
01-CAPITULO 01 9/2/06 11:42 Pgina 10
1.7.8. Otras operaciones
Pueden definirse tambin para los lenguajes las operaciones interseccin y complementacin
(con respecto al lenguaje universal). Dado que stas son operaciones clsicas de teora de con-
juntos, no es preciso detallarlas aqu.
Ejercicios
1. Sea ={!} y x=!. Definir las siguientes palabras: xx, xxx, x
3
, x
8
, x
0
. Cules son
sus longitudes? Definir
*
.
2. Sea ={0,1,2}, x=00, y=1, z=210. Definir las siguientes palabras: xy, xz,
yz, xyz, x
3
, x
2
y
2
, (xy)
2
, (zxx)
3
. Cules son sus longitudes, cabezas y colas?
3. Sea ={0,1,2}. Escribir seis de las cadenas ms cortas de
+
y de
*
.
Conceptos bsicos sobre gramticas
Como se ha dicho anteriormente, una gramtica describe la estructura de las frases y de las pala-
bras de un lenguaje. Aplicada a los lenguajes naturales, esta ciencia es muy antigua: los prime-
ros trabajos aparecieron en la India durante la primera mitad del primer milenio antes de Cristo,
alcanzndose el mximo apogeo con Panini, que vivi quiz entre los siglos VII y IV antes de
Cristo y desarroll la primera gramtica conocida, aplicada al lenguaje snscrito. Casi al mismo
tiempo, puede que independientemente, el sofista griego Protgoras (h. 485-ca. 411 a. de J.C.)
fund una escuela gramatical, que alcanz su mximo esplendor en el siglo II antes de Cristo.
Se llama gramtica formal a la cudrupla
G = (
T
,
N
, S, P)
donde
T
es el alfabeto de smbolos terminales, y
N
es el alfabeto de smbolos no terminales. Se
verifica que
T
N
=
y =
T
N
.
N
N
es el axioma, smbolo inicial, o smbolo distinguido. Finalmente, P es un conjunto
finito de reglas de produccin de la forma u ::= v, donde se verifica que:
u
+
u=xAy
x,y
*
A
N
v
*
1.9
1.8
Captulo 1. Lenguajes, gramticas y procesadores 11
01-CAPITULO 01 9/2/06 11:42 Pgina 11
Es decir, u es una palabra no vaca del lenguaje universal del alfabeto que contiene al menos
un smbolo no terminal y v es una palabra, posiblemente vaca, del mismo lenguaje universal.
Veamos un ejemplo de gramtica:
T
= {0,1,2,3,4,5,6,7,8,9}
N
= {N,C}
S = N
P = {
N ::= CN
N ::= C
C ::= 0
C ::= 1
C ::= 2
C ::= 3
C ::= 4
C ::= 5
C ::= 6
C ::= 7
C ::= 8
C ::= 9
}
1.9.1. Notacin de Backus
Notacin abreviada: si el conjunto de producciones contiene dos reglas de la forma
u ::= v
u ::= w
pueden representarse abreviadamente con la notacin
u ::= v | w
La notacin u::=v de las reglas de produccin, junto con la regla de abreviacin indicada,
se denomina Forma Normal de Backus, o BNF, de las iniciales de su forma inglesa Backus
Normal Form, o tambin Backus-Naur Form.
La gramtica del ejemplo anterior puede representarse en BNF de la manera siguiente:
T
= {0,1,2,3,4,5,6,7,8,9}
N
= {N,C}
S = N
P = {
N ::= CN | C
C ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
}
12 Compiladores e intrpretes: teora y prctica
01-CAPITULO 01 9/2/06 11:42 Pgina 12
1.9.2. Derivacin directa
Sea un alfabeto y x::=y una produccin sobre las palabras de ese alfabeto. Sean v y w dos
palabras del mismo alfabeto (v,w
*
). Se dice que w es derivacin directa de v, o que v pro-
duce directamente w, o que w se reduce directamente a v, si existen dos palabras z,u
*
,
tales que:
v=zxu
w=zyu
Es decir, si v contiene la palabra x y, al sustituir x por y, v se transforma en w.
Indicamos esta relacin con el smbolo v w.
COROLARIO: Si x::=y es una produccin sobre , se sigue que xy.
Ejemplos:
Sea el alfabeto castellano de las letras maysculas, y ME ::= BA una produccin sobre .
Es fcil ver que CAMELLOCABALLO.
Sea el alfabeto ={0,1,2,N,C}, y el conjunto de producciones
N ::= CN
N ::= C
C ::= 0
C ::= 1
C ::= 2
Pueden demostrarse fcilmente las siguientes derivaciones directas:
N CN CCN CCC 2CC 21C 210
1.9.3. Derivacin
Sea un alfabeto y P un conjunto de producciones sobre las palabras de ese alfabeto. Sean v y
w dos palabras del mismo alfabeto (v,w
*
). Se dice que w es derivacin de v, o que v produ-
ce w, o que w se reduce a v, si existe una secuencia finita de palabras u
0
, u
1
, ..., u
n
(n>0),
tales que
v = u
0
u
1
u
2
... u
n-1
u
n
= w
Indicamos esta relacin con el smbolo v + w. La secuencia anterior se llama derivacin de
longitud n.
En el ejemplo anterior, puede verse que N + 210 mediante una secuencia de longitud 6.
COROLARIO: Si v w, entonces v + w mediante una secuencia de longitud 1.
Captulo 1. Lenguajes, gramticas y procesadores 13
01-CAPITULO 01 9/2/06 11:42 Pgina 13
1.9.4. Relacin de Thue
Sea un alfabeto y P un conjunto de producciones sobre las palabras de ese alfabeto. Sean v y
w dos palabras del mismo alfabeto. Se dice que existe una relacin de Thue entre v y w si se
verifica que v + w o v = w. Expresaremos esta relacin con el smbolo v * w.
1.9.5. Formas sentenciales y sentencias
Sea una gramtica G = (
T
,
N
, S, P). Una palabra x
*
se denomina forma sentencial de G si
se verifica que S * x, es decir, si existe una relacin de Thue entre el axioma de la gramtica
y x. Dicho de otro modo: si x=S o x deriva de S.
Ejercicio: En el ejemplo anterior, comprobar que CCN, CN2 y 123 son formas sentenciales,
pero no lo es NCN.
Si una forma sentencial x cumple que x
T
*
(es decir, est formada nicamente por smbo-
los terminales), se dice que x es una sentencia o instruccin generada por la gramtica G.
Ejercicio: Cules de las formas sentenciales anteriores son sentencias?
1.9.6. Lenguaje asociado a una gramtica
Sea una gramtica G = (
T
,
N
, S, P). Se llama lenguaje asociado a G, o lenguaje generado por G,
o lenguaje descrito por G, al conjunto L(G) = {x | S*x x
T
*
}. Es decir, el conjunto de to-
das las sentencias de G (todas las cadenas de smbolos terminales que derivan del axioma de G).
Ejemplo: El lenguaje asociado a la gramtica de los ejemplos anteriores es el conjunto de
todos los nmeros naturales ms el cero.
1.9.7. Frases y asideros
Sea G una gramtica y v=xuy una de sus formas sentenciales. Se dice que u es una frase de la
forma sentencial v respecto del smbolo no terminal U
N
si
S * xUy
U + u
Es decir, si en la derivacin que transforma S en xuy se pasa por una situacin intermedia en
la que x e y ya han sido generados, y slo falta transformar el smbolo no terminal U en la fra-
se u.
Si U es una forma sentencial de G, entonces todas las frases que derivan de U sern, a su vez,
formas sentenciales de G.
n
t
i
c
o
Analizador
sintctico
Tabla de
identificadores
Programa
fuente
Optimizador
de cdigo
Gestin
de
memoria
Proceso
de
errores
Generador
de cdigo
Programa
objeto
Figura 1.8. Estructura de un compilador.
01-CAPITULO 01 9/2/06 11:42 Pgina 29
distincin artificial. Dado que un lenguaje de computadora permite escribir programas capaces
de resolver (en principio) cualquier problema computable, es obvio que el lenguaje completo
(sintaxis + semntica) se encuentra al nivel de una mquina de Turing o de una gramtica de
tipo 0 de Chomsky. Sin embargo, el tratamiento formal de este tipo de gramticas es complica-
do: su diseo resulta oscuro y su anlisis muy costoso. Estas dificultades desaconsejan su uso en
el diseo de compiladores e intrpretes. Para simplificar, se ha optado por separar todas aquellas
componentes del lenguaje que se pueden tratar mediante gramticas independientes del contexto
(o de tipo 2 de Chomsky), que vienen a coincidir con lo que, en los lenguajes naturales, se vena
llamando sintaxis. Su diseo resulta ms natural al ingeniero informtico y existen muchos algo-
ritmos eficientes para su anlisis. La mquina abstracta necesaria para tratar estos lenguajes es el
autmata a pila.
Por otra parte, podramos llamar semntica del lenguaje de programacin todo aquello que ha-
bra que aadir a la parte independiente del contexto del lenguaje (la sintaxis) para hacerla com-
putacionalmente completa. Por ejemplo, con reglas independientes del contexto, no es posible
expresar la condicin de que un identificador debe ser declarado antes de su uso, ni comprobar
la coincidencia en nmero, tipo y orden entre los parmetros que se pasan en una llamada a una
funcin y los de su declaracin. Para describir formalmente la semntica de los lenguajes de
programacin, se han propuesto diferentes modelos
2
, la mayora de los cuales parte de una gra-
mtica independiente del contexto que describe la sintaxis y la extiende con elementos capaces
de expresar la semntica. Para la implementacin de estos modelos, los compiladores suelen
utilizar un autmata a pila, un diccionario (o tabla de smbolos) y un conjunto de algoritmos. El
autmata analiza los aspectos independientes del contexto (la sintaxis), mientras que las restan-
tes componentes resuelven los aspectos dependientes (la semntica).
Resumen
Este captulo ha revisado la historia de la Informtica, sealando los paralelos sorprendentes que
existen entre disciplinas tan aparentemente distintas como la Computabilidad, la Teora de aut-
matas y mquinas secuenciales, y la Teora de gramticas transformacionales.
Se recuerdan y resumen las definiciones de alfabeto, palabra, lenguaje y gramtica; las ope-
raciones con palabras y lenguajes; los conceptos de derivacin, forma sentencial, sentencia, frase
y asidero; la idea de recursividad; los diversos tipos de gramticas; la representacin de las deri-
vaciones por medio de rboles sintcticos; el concepto de ambigedad sintctica y la forma de
obtener gramticas limpias.
Finalmente, la ltima parte del captulo clasifica los lenguajes, tanto naturales como artificia-
les o de programacin, introduce el concepto de procesador de lenguaje y sus diversos tipos
(compiladores, intrpretes y compiladores-intrpretes), y da paso al resto del libro, especifican-
do cules son las partes en que se divide usualmente un compilador o un intrprete.
1.14
30 Compiladores e intrpretes: teora y prctica
2
En este libro slo ser objeto de estudio el modelo de especificacin formal de la semntica de los lenguajes de
programacin basado en las gramticas de atributos [6, 7].
01-CAPITULO 01 9/2/06 11:42 Pgina 30
Bibliografa
[1] Gdel, K. (1931): ber formal unentscheidbare Stze der Principia Mathematica und verwandter
Systeme, I. Monatshefte fr Mathematik und Physik, 38, pp. 173-198.
[2] Shannon, C. (1938): A symbolic analysis of relay and switching circuits, Transactions American
Institute of Electrical Engineers, vol. 57, pp. 713-723.
[3] Chomsky, N. (1956): Three models for the description of language, IRE Transactions on
Information Theory, 2, pp. 113-124.
[4] Chomsky, N. (1959): On certain formal properties of grammars, Information and Control, 1, pp. 91-
112.
[5] Alfonseca, M.; Sancho, J., y Martnez Orga, M. (1997): Teora de lenguajes, gramticas y autmatas.
Madrid. Promo-Soft. Publicaciones R.A.E.C.
[6] Knuth, D. E. (1971): Semantics of context-free languages, Mathematical Systems Theory, 2(2),
pp.127-145, junio 1968. Corregido en Mathematical Systems Theory, 5(1), pp. 95-96, marzo 1971.
[7] Knuth, D. E. (1990): The genesis of attribute grammars, en Pierre Deransart & Martin Jourdan, edi-
tors, Attribute grammars and their applications (WAGA), vol. 461 de Lecture Notes in Computer
Science, pp. 1-12, Springer-Verlag, New York-Heidelberg-Berln, septiembre 1990.
1.15
Captulo 1. Lenguajes, gramticas y procesadores 31
01-CAPITULO 01 9/2/06 11:42 Pgina 31
01-CAPITULO 01 9/2/06 11:42 Pgina 32
Captulo 2
Tabla de smbolos
La tabla de smbolos es la componente del compilador que se encarga de todos los aspectos de-
pendientes del contexto relacionados con las restricciones impuestas a los nombres que puedan
aparecer en los programas (nombres de variables, constantes, funciones, palabras reservadas).
Estas restricciones obligan a llevar la cuenta, durante todo el proceso de la compilacin, de los
nombres utilizados (junto con toda la informacin relevante que se deduzca de la definicin del
lenguaje de programacin), para poder realizar las comprobaciones e imponer las restricciones
necesarias.
Por otro lado, una preocupacin muy importante en el diseo de algoritmos para la solucin
de problemas computables es obtener el mejor rendimiento posible. Para ello, es esencial la elec-
cin correcta de las estructuras de datos. El tiempo que necesitan los algoritmos para procesar sus
entradas suele depender del tamao de stas y difiere de unas estructuras de datos a otras. Los p-
rrafos siguientes contienen reflexiones que justifican la eleccin de las estructuras de datos y al-
goritmos ms utilizados para las tablas de smbolos de los compiladores.
Complejidad temporal de los algoritmos
de bsqueda
En informtica, la complejidad de los algoritmos se puede estudiar estimando la dependencia en-
tre el tiempo que necesitan para procesar su entrada y el tamao de sta. El mejor rendimiento se
obtiene si ambos son independientes: el tiempo es constante. En orden decreciente de eficiencia,
otros algoritmos presentan dependencia logartmica, polinmica (lineal, cuadrtica, etc.) o expo-
nencial. Para clasificar un algoritmo de esta forma, se elige una de sus instrucciones y se estima
el tiempo empleado en ejecutarla mediante el nmero de veces que el algoritmo tiene que ejecu-
tar dicha instruccin, se calcula el tiempo en funcin del tamao de la entrada y se estudia su or-
den cuando la entrada se hace arbitrariamente grande.
2.1
02-CAPITULO 02 9/2/06 11:49 Pgina 33
A lo largo de este captulo se usar n para representar el tamao de la entrada. Para la esti-
macin de los rdenes de eficiencia, los valores concretos de las constantes que aparecen en las
funciones no son relevantes, por lo que la dependencia constante se representa como 1, la loga-
rtmica como log(n), la lineal como n, la cuadrtica como n
2
y la exponencial como e
n
.
Resulta til considerar el peor tiempo posible (el tiempo utilizado en tratar la entrada que ms
dificultades plantea) y el tiempo medio (calculado sobre todas las entradas o sobre una muestra
suficientemente representativa de ellas).
Buscar un dato en una estructura implica compararlo con alguno de los datos contenidos en
ella. La instruccin seleccionada para medir el rendimiento de los algoritmos de bsqueda suele
ser esta comparacin, que recibe el nombre de comparacin de claves.
Una explicacin ms detallada de esta materia queda fuera del objetivo de este libro. El lec-
tor interesado puede consultar [1, 2].
2.1.1. Bsqueda lineal
La bsqueda lineal supone que los datos entre los que puede estar el buscado se guardan en una
lista o vector, no necesariamente ordenados.
Este algoritmo (vase la Figura 2.1) recorre la estructura desde la primera posicin, com-
parando (comparacin de clave) cada uno de los elementos que encuentra con el buscado.
Termina por una de las dos situaciones siguientes: o se llega al final de la estructura o se en-
cuentra el dato buscado. En la primera situacin, el dato no se ha encontrado y el algoritmo no
termina con xito.
34 Compiladores e intrpretes: teora y prctica
Figura 2.1. Pseudocdigo del algoritmo de bsqueda lineal del elemento k en la tabla no ne-
cesariamente ordenada T, entre las posiciones P y U.
ind BsquedaLineal (tabla T, ind P, ind U, clave k)
Para i de P a U:
Si T [i] == k:
devolver i;
devolver error;
La situacin ms costosa es la que obliga a recorrer la estructura de datos completa. Esto pue-
de ocurrir si la bsqueda termina sin xito o, en caso contrario, si el elemento buscado es el
ltimo de la estructura. En estos casos (tiempo peor) el orden coincide con el tamao de la
entrada (n). La dependencia es lineal.
02-CAPITULO 02 9/2/06 11:49 Pgina 34
2.1.2. Bsqueda binaria
La bsqueda binaria supone que los datos, entre los que puede estar el buscado, se guardan en
una lista o vector ordenados.
Este algoritmo (vase la Figura 2.2) aprovecha el orden propio de la estructura para descartar, en
cada iteracin, la mitad de la tabla donde, con seguridad, no se encuentra el dato buscado. En la si-
guiente iteracin slo queda por estudiar la otra mitad, en la que s puede encontrarse. Para ello se com-
para el elemento buscado con el que ocupa la posicin central de la estructura (comparacin de clave).
Si ste es posterior (anterior) al buscado, puede descartarse la segunda (primera) mitad de la estructu-
ra. El algoritmo termina por una de las dos razones siguientes: alguna de las comparaciones encuen-
tra el elemento buscado o la ltima tabla analizada tiene slo un elemento, que no es el buscado. La
ltima circunstancia significa que el dato no ha sido encontrado y la bsqueda termina sin xito.
Captulo 2. Tabla de smbolos 35
Figura 2.2. Pseudocdigo del algoritmo de bsqueda binaria del elemento k en la tabla
ordenada T entre las posiciones P y U.
ind BusquedaBinaria
(tabla T, ind P, ind U, clave K)
mientras PU
M= (P+U) /2
Si T[M] < K
P=M+1;
else Si T[M]>K
U=M-1;
else devolver M;
devolver Error;
Como mucho, este algoritmo realiza las iteraciones necesarias para reducir la bsqueda a una
tabla con un solo elemento. Se puede comprobar que el tamao de la tabla pendiente en la itera-
cin i-sima es igual a n/2
i
. Al despejar de esta ecuacin el nmero de iteraciones, se obtendr
una funcin de log
2
(n), que es la expresin que determina, tanto el tiempo peor, como el tiem-
po medio del algoritmo.
2.1.3. Bsqueda con rboles binarios ordenados
Los rboles binarios ordenados se pueden definir de la siguiente manera:
Si se llama T al rbol, clave(T) al elemento contenido en su raz, izquierdo(T) y de-
recho(T) a sus hijos izquierdo y derecho, respectivamente, y nodos(T) al conjunto de sus no-
dos, T es un rbol binario ordenado si y slo si cumple que:
T nodos(T) clave(izquierdo(T)) clave(T)
clave(derecho(T))
02-CAPITULO 02 9/2/06 11:49 Pgina 35
El algoritmo de bsqueda (vase la Figura 2.3) consulta la raz del rbol para decidir si la
bsqueda ha terminado con xito (en el caso en el que el elemento buscado coincida con la cla-
ve del rbol) o, en caso contrario, en qu subrbol debe seguir buscando: si la clave del rbol es
posterior (anterior) al elemento buscado, la bsqueda contina por el rbol izquierdo(T)
(derecho(T)). Si en cualquier momento se encuentra un subrbol vaco, la bsqueda termina
sin xito. Se suelen utilizar distintas variantes de este algoritmo para que el valor devuelto resul-
te de la mxima utilidad: en ocasiones basta con el dato buscado, o con una indicacin de que se
ha terminado sin xito; en otras, el retorno de la funcin apunta al subrbol donde est el ele-
mento buscado o donde debera estar.
La Figura 2.3 resalta la comparacin de claves. En el peor de los casos (que la bsqueda ter-
mine con fracaso, tras haber recorrido los subrboles ms profundos, o que el elemento buscado
est precisamente en el nivel ms profundo del rbol), el nmero de comparaciones de clave coin-
cidir con la profundidad del rbol. Es decir, los tiempos peor y medio dependen de la profundi-
dad del rbol. Se escribir prof(T) para representar la profundidad del rbol T.
36 Compiladores e intrpretes: teora y prctica
a)
b)
1
0
2
3
4
1 4
0
2
3
Figura 2.4. Dos rboles binarios distintos para el conjunto de datos {0,1,2,3,4}:
a) con profundidad 3, b) con profundidad 2.
Figura 2.3. Pseudocdigo recursivo del algoritmo de bsqueda del elemento k en el rbol
binario ordenado T.
ArbolBin Buscar(clave K, ArbolBin T)
Si vaco(T) devolver rbol_vaco;
Si k == clave(T) devolver T
Si k < clave(T)
devolver(Buscar(k,izquierdo(T));
Si k > clave(T)
devolver(Buscar(k,derecho(T));
Es interesante observar que este razonamiento no expresa una dependencia directa de n, sino
de un parmetro del rbol binario que depende tanto de n como de la manera en la que se cre
el rbol binario en el que se busca. La Figura 2.4 muestra dos posibles rboles binarios ordena-
dos y correctos formados con el conjunto de datos {0,1,2,3,4}
02-CAPITULO 02 9/2/06 11:49 Pgina 36
Posteriormente se profundizar ms en esta reflexin, para comprender cmo depende
prof(T) de n.
2.1.4. Bsqueda con rboles AVL
Los rboles de Adelson-Velskii y Landis (AVL) son un subconjunto de los rboles binarios orde-
nados. Un rbol binario ordenado es un rbol AVL si y slo si las profundidades de los hijos de
cualquier nodo no difieren en ms de una unidad. Por tanto, aunque pueda haber muchos rboles
AVL para el mismo conjunto de datos, se tiene la garanta de que la profundidad es siempre ms
o menos la del rbol binario menos profundo posible.
El rbol binario menos profundo que se puede formar con n nodos es el que tiene todos sus
niveles completos, es decir, cada nodo que no sea una hoja tiene exactamente 2 hijos. Es fcil
comprobar que el nmero de nodos que hay en el nivel i-simo de un rbol con estas caracters-
ticas es igual a 2
i
, y tambin que el total de nodos en un rbol de este tipo, de profundidad k,
es igual a 2
k+1
-1. Si se despeja la profundidad k necesaria para que el nmero de nodos sea igual
a n, quedar en funcin de log
2
(n).
Se ha dicho que la profundidad es ms o menos la del rbol binario menos profundo posible,
porque se permite una diferencia en la profundidad de como mucho una unidad, que se puede
despreciar para valores grandes de n. Por tanto, los rboles AVL garantizan que la profundidad
es del orden de log(n), mientras que en la seccin anterior se vio que el tiempo peor y medio
de la bsqueda en un rbol binario T es del orden de prof(T).
2.1.5. Resumen de rendimientos
El estudio del mejor tiempo posible para cualquier algoritmo de bsqueda que se base en la com-
paracin de claves se parece al razonamiento informal de las secciones anteriores respecto a los
rboles binarios. Intuitivamente, se puede imaginar que el mejor tiempo posible estar asociado
a la profundidad del rbol binario menos profundo que se puede formar con n nodos: log
2
(n).
Se puede concluir, por tanto, que ste es el rendimiento mejor posible para los algoritmos de
ordenacin basados en comparaciones de clave. La Tabla 2.1 muestra un resumen de los rendi-
mientos observados.
Captulo 2. Tabla de smbolos 37
Algoritmo Orden del tiempo peor Orden del tiempo medio
Bsqueda lineal n
Bsqueda binaria log(n) log(n)
Cota inferior log(n) log(n)
Tabla 2.1. Resumen de los rendimientos de los algoritmos de bsqueda con comparacin de clave.
02-CAPITULO 02 9/2/06 11:49 Pgina 37
El tipo de datos diccionario
2.2.1. Estructura de datos y operaciones
La teora de estructuras de datos define el diccionario como una coleccin ordenada de informa-
cin organizada de forma que una parte de ella se considera su clave. La clave se utiliza para
localizar la informacin en el diccionario, de la misma manera en que, en un diccionario de la
lengua, las palabras se utilizan como clave para encontrar su significado.
En un diccionario se dispondr, al menos, de las operaciones de bsqueda, insercin y
borrado:
Posicion Buscar(clave k, diccionario D):
Busca el dato k en el diccionario D.
La funcin devuelve la posicin ocupada por el dato (en caso de acabar con xito) o
una indicacin de que la bsqueda ha terminado sin encontrarlo.
Estado Insertar(clave k, diccionario D):
Aade al diccionario D la informacin k.
El retorno de la funcin informa sobre el xito de la insercin.
Estado Borrar(clave k, diccionario D):
Elimina del diccionario D la informacin k.
Aunque la implementacin de estas funciones admita variaciones, se puede considerar gene-
ral el pseudocdigo de las Figuras 2.5 y 2.6.
Puede observarse en ellas que el trabajo ms importante de la insercin y el borrado es la
bsqueda inicial de la clave tratada. Por tanto, la complejidad temporal de las tres operaciones
2.2
38 Compiladores e intrpretes: teora y prctica
Figura 2.5. Pseudocdigo del algoritmo de insercin de la clave k en
el diccionario D.
Estado Insertar (clave k, diccionario D)
Posicion = Buscar(k,D);
Si Posicion indica que no est
Modificar D para que incluya k
devolver insercin correcta
en otro caso
devolver error
02-CAPITULO 02 9/2/06 11:49 Pgina 38
queda determinada por la de la bsqueda. En las prximas secciones se elegir razonadamente
una implementacin adecuada, en cuanto a rendimiento temporal, de esta estructura de datos.
2.2.2. Implementacin con vectores ordenados
Cuando las claves del diccionario se almacenan en un vector ordenado, la operacin de bsque-
da puede realizarse con el algoritmo de bsqueda binaria descrito en secciones anteriores. Tanto
su tiempo medio como su tiempo peor suponen un rendimiento aceptable (vase la Tabla 2.1).
Se estudiar a continuacin si el trabajo extra aadido a la bsqueda en el resto de las opera-
ciones empeora su rendimiento. En el caso de la insercin, para que el vector siga ordenado des-
pus de realizarla, es necesario, tras localizar la posicin que la nueva clave debera ocupar en el
vector, desplazar los elementos siguientes para dejar una posicin libre (vase la Figura 2.7) En
el peor de los casos (si la nueva clave debe ocupar la primera posicin del vector) sera necesa-
rio mover todos los elementos, con un rendimiento temporal de orden lineal (n). La colocacin
de la informacin en su ubicacin final se realiza en tiempo constante.
Captulo 2. Tabla de smbolos 39
Figura 2.6. Pseudocdigo del algoritmo de borrado de la clave k del diccionario D.
Estado Borrar (clave k, diccionario D)
Posicion = Buscar(k,D);
Si Posicion indica que no est
devolver error
en otro caso
Modificar D para eliminar k
devolver borrado correcto
a)
x y z
b)
c)
x
y
z
x
y
z
k
k
k
Figura 2.7. Insercin de la clave k en el diccionario D (vector ordenado): a) se busca la clave
k en D; b) tras comprobar que no est se hace hueco para k; c) k ocupa su posicin en D.
02-CAPITULO 02 9/2/06 11:49 Pgina 39
El rendimiento de la insercin es la suma del de la bsqueda (log(n)), ms el del desplaza-
miento (n) y el de la asignacin (1) y, por lo tanto, est determinado por el peor de ellos: n.
El rendimiento lineal no es aceptable, por lo que no es necesario estudiar el borrado para
rechazar el uso de vectores ordenados en la implementacin del diccionario.
2.2.3. Implementacin con rboles binarios ordenados
En la Seccin 2.1.3 se ha mostrado que el rendimiento temporal de la bsqueda depende de la
profundidad del rbol y que existen diferentes rboles binarios ordenados para el mismo conjun-
to de datos con distintas profundidades. Si no se puede elegir a priori el rbol en el que se va a
buscar, lo que suele ocurrir casi siempre, ya que el uso de un diccionario suele comenzar cuando
est vaco, y se va rellenando a medida que se utiliza, tampoco se puede asegurar que no se ter-
mine utilizando el peor rbol binario posible que muestra la Figura 2.8, que, como se ve, no se
puede distinguir de una simple lista.
40 Compiladores e intrpretes: teora y prctica
0
1
2
3
4
Figura 2.8. Uno de los peores rboles binarios posibles respecto al rendimiento temporal de
la bsqueda con los datos {0,1,2,3,4}.
En este caso (vase la Seccin 2.1.1) el rendimiento temporal de la bsqueda depende lineal-
mente del tamao de la entrada (es de orden n). Este rendimiento no es aceptable, lo que basta
para rechazar los rboles binarios ordenados para implementar el diccionario. A pesar de esto, se
realizar el estudio de las operaciones de insercin y borrado, para facilitar la comprensin de la
siguiente seccin.
Insercin
La insercin de la clave k en el rbol binario ordenado T comienza con su bsqueda. Si supone-
mos que el algoritmo de bsqueda devuelve un puntero al nodo padre donde debera insertarse el
nuevo elemento, lo nico que quedara por hacer es crear un nodo nuevo y asignarlo como hijo iz-
quierdo (derecho) al retorno de la bsqueda, si k es anterior (posterior) a la clave del rbol. La
Figura 2.9 muestra grficamente esta situacin y la Figura 2.10 el pseudocdigo correspondiente.
02-CAPITULO 02 9/2/06 11:49 Pgina 40
Puede observarse que el trabajo aadido a la bsqueda consiste en una comparacin de clave
y una modificacin del valor de una variable. Este trabajo es el mismo para cualquier tamao de
la entrada (n), por lo que supondr un incremento de tiempo constante que se podr despreciar
para valores grandes de n, por lo que el rendimiento de la insercin es el mismo que el de la
bsqueda.
Captulo 2. Tabla de smbolos 41
a)
b)
2
48 Compiladores e intrpretes: teora y prctica
k kx h(k)
1 3.141592654 3
2 6.283185307 7
3 9.424777961 10
4 12.56637061 14
5 15.70796327 17
6 18.84955592 21
Tabla 2.2. Algunos valores de la funcin hash de multiplicacin que utiliza = y m=25. Se resaltan las
colisiones.
k kx h(k)
7 21.99114858 24
8 25.13274123 3
9 28.27433388 6
10 31.41592654 10
1 34.55751919 13
12 37.69911184 17
02-CAPITULO 02 9/2/06 11:49 Pgina 48
Funciones hash de divisin
Se utiliza la funcin mdulo h(k)=k%m, donde
k es, como en el caso anterior, un valor numrico asociado a la clave.
m es el valor mximo de posicin dentro de la tabla. Se le exige que sea primo.
% es la funcin mdulo, definida como el resto de la divisin de x entre m.
La Tabla 2.3 muestra ejemplos de esta funcin hash.
Captulo 2. Tabla de smbolos 49
k h (k)
1 1
2 2
3 3
4 4
5 5
6 6
Tabla 2.3. Algunos valores de la funcin hash de divisin, para m=7. Se resaltan las colisiones.
k h (k)
7 0
8 1
9 2
10 3
11 4
12 5
Otras funciones hash
A pesar de los argumentos tericos anteriores, el diseo de funciones hash tiene mucho de tra-
bajo artesanal y es una tarea complicada. Por eso, a continuacin, se describe una funcin hash
bien documentada en la literatura, que en la prctica ha mostrado ser buena. Puede encontrarse
una exposicin detallada en [3].
Dicha funcin hash es un algoritmo iterativo que calcula un valor auxiliar (h
i
) entre 0 y la
longitud m de la clave id. El valor final h(id) se obtiene a partir de alguno de los bits del va-
lor m-simo (h
m
).
h
0
=0
h
i
=kh
i-1
+c
i
i 1im
h(k)=bits(h
m
,30)%n
donde
k es una constante deducida experimentalmente.
n es el tamao de la tabla, deducido con k experimentalmente.
bits(x,j) es una funcin que obtiene los j bits menos significativos del ente-
ro x.
c
i
es el cdigo ASCII del carcter i-simo de id
02-CAPITULO 02 9/2/06 11:49 Pgina 49
2.3.4. Factor de carga
Un concepto muy importante en el estudio de la eficiencia es el factor de carga. Dada una tabla
hash con espacio para m claves, en la que ya se han insertado n, se llama factor de carga y se re-
presenta mediante la letra , al cociente entre n y m.
=
m
n
1
2
1 +
(1
1
)
2
Tambin coinciden en que, cuando se buscan claves que s estn en la tabla, el rendimiento se
puede aproximar mediante la expresin
1
2
1 +
1
1
En la prctica es poco conveniente que las claves que colisionan formen bandas contiguas.
Sondeo multiplicativo
El sondeo multiplicativo intenta superar el inconveniente que suponen las bandas de claves que
colisionan en la tabla hash. Para ello se articula un mecanismo poco costoso para dispersar los
reintentos por la tabla, impidiendo la formacin de bandas al espaciarlos uniformemente.
Intuitivamente, se usa como incremento el valor devuelvo por la funcin hash, de forma que, en
el primer reintento, se saltan h(k) posiciones; en el segundo, 2*h(k) posiciones, etc. La
Figura 2.17 muestra el pseudocdigo del sondeo multiplicativo.
Captulo 2. Tabla de smbolos 53
0
Figura 2.18. Posible estado de una tabla hash con direccionamiento abierto y sondeo multipli-
cativo. Hay tres grupos de claves que colisionan, con cuatro, tres y dos claves, respectiva-
mente. El primer grupo corresponde a la posicin inicial 10, el segundo a la 14 y el tercero a
la 11. Obsrvese que la posicin 0 no se usa y que las posiciones visitadas por cada grupo de
sondeos se entremezclan.
Figura 2.17. Pseudocdigo del algoritmo sondeo multiplicativo. Se necesitan dos argumentos,
el nmero de reintentos y la posicin inicial.
int delta(int numero_reintento, indice posicion_inicial)
{
return (posicion_inicial*numero_reintento);
}
Obsrvese que la posicin 0 de la tabla no se debe utilizar, pues el sondeo multiplicativo slo
visitara sta posicin en todos los reintentos.
La Figura 2.18 muestra grficamente un ejemplo de la gestin de una tabla hash con este mtodo.
02-CAPITULO 02 9/2/06 11:49 Pgina 53
El sondeo multiplicativo tiene una propiedad interesante: cuando el nmero de reintentos es
suficientemente grande, al sumar el desplazamiento proporcionado por el sondeo multiplicativo
se obtiene una posicin fuera de la tabla. Las Figuras 2.14 y 2.15 muestran que se utiliza la ope-
racin mod tamao(T) para seguir recorriendo la tabla circularmente en estos casos. Es fcil
comprender que, si la tabla tiene un tamao primo, los sondeos la cubrirn por completo y no se
formarn bandas contiguas.
Otros sondeos
En general, podra utilizarse cualquier algoritmo para el cdigo de la funcin delta. Se pueden
obtener as diferentes tipos de sondeo. Una variante es el sondeo cuadrtico, que generaliza el
sondeo multiplicativo de la siguiente manera: el sondeo multiplicativo realmente evala una fun-
cin lineal, f(x)=posicin_inicial*x (donde x es el nmero de reintentos). El sondeo
cuadrtico utiliza un polinomio de segundo grado g(x)=a*x
2
+b*x+c, en el que hay que de-
terminar las constantes a, b y c. La Figura 2.19 muestra el pseudocdigo del sondeo cuadrtico.
54 Compiladores e intrpretes: teora y prctica
Figura 2.19. Pseudocdigo del algoritmo del sondeo cuadrtico. Queda pendiente determinar
las constantes del polinomio de segundo grado.
int delta(int numero_reintento)
{
return (a*numero_reintento
2
+b*numero_reintento+c);
}
Figura 2.20. Pseudocdigo del algoritmo del sondeo pseudoaleatorio.
int delta( )
{
return ( random() );
}
Otra variante, que slo tiene inters terico, consiste en generar el incremento de la funcin
de manera pseudoaleatoria. La Figura 2.20 muestra el pseudocdigo del sondeo aleatorio.
Este mtodo slo tiene inters para el estudio analtico del rendimiento temporal. Se puede
demostrar, aunque queda fuera del objetivo de este libro, que la dependencia del factor de carga
del rendimiento temporal en la bsqueda de una clave que no se encuentra en la tabla hash, puede
aproximarse mediante la siguiente expresin:
1
1
1
log
1
1
1 2 3 4 5 6 7 8
{ int a, b, c, d;
{ int e, f;
. . .
L1:
. . .
}
}
}
}
L2:
{ int i, h;
{ int a;
B
1
: a, b, c, d B
1
: a, b, c, d, B
2
B
2
: e, f, L1
B
1
: a, b, c, d, B
2
B
1
: a, b, c, d, B
2
, B
3
B
3
: i, h, L2
1 2 3 4
8 7 6 5
B
1
: a, b, c, d, B
2
, B
3
B
1
: a, b, c, d, B
2
, B
3
B
3
: i, h, L2, B
4
B
1
: a, b, c, d, B
2
, B
3
B
3
: i, h, L2, B
4
B
4
: a
Figura 2.23. Ejemplo de tabla de smbolos de un programa escrito con un lenguaje con
estructura de bloques. La tabla de smbolos utiliza una tabla hash para cada mbito; el
compilador slo realiza un paso. En la pila de tablas hash se seala la cima.
02-CAPITULO 02 9/2/06 11:49 Pgina 59
Se aade su nombre, si lo tiene, como identificador en el bloque actual, antes de abrir
el nuevo bloque, ya que los nombres de las subrutinas tienen que ser locales al bloque
donde se declaran.
Se crea una nueva tabla hash para el bloque nuevo.
Se inserta en la pila (push) la nueva tabla hash, que pasa a ser la del mbito actual.
Se inserta en el mbito actual el nombre del nuevo bloque, ya que los nombres de las
subrutinas son globales a la propia subrutina.
2. Cuando se cierra un bloque:
Se saca de la pila (pop) la tabla hash del mbito actual y se elimina dicha tabla.
Compiladores de ms de un paso
El criterio general es el mismo que en el caso anterior, pero se necesita conservar las tablas hash
de los bloques cerrados, por si se requiere su informacin en pasos posteriores.
Un esquema fcil de describir consiste en modificar la pila del apartado anterior para conver-
tirla en una lista, que conserve juntas, por encima de los mbitos abiertos, las tablas hash de los
mbitos cerrados. De esta manera, el mbito actual estar siempre por debajo de los cerrados. Por
debajo de l, se encontrar la misma pila descrita anteriormente. Es necesario aadir la informa-
cin necesaria para marcar los bloques como abiertos o cerrados.
La gestin descrita en el apartado anterior slo cambia en lo relativo a los mbitos cerrados:
cuando un mbito se cierra, se marca como cerrado. Es fcil imaginar que la pila necesitar de
ciertos datos adicionales (al menos, un apuntador al mbito actual) para su gestin eficiente. La
Figura 2.24 muestra una tabla hash de este tipo, para el mismo programa fuente del ejemplo de
la Figura 2.23.
2.4.3. Evaluacin de estas tcnicas
Entre los inconvenientes de estas tcnicas se pueden mencionar los siguientes:
Se puede fragmentar en exceso el espacio destinado en el compilador a la tabla de smbo-
los, lo que origina cierta ineficiencia en cuanto al espacio utilizado.
La bsqueda, que implica la consulta de varias tablas, puede resultar ineficiente en cuan-
to al tiempo utilizado.
2.4.4. Uso de una sola tabla para todos los mbitos
Este enfoque pretende minimizar el efecto de los inconvenientes detectados en el apartado ante-
rior. Es evidente que, una vez que se conoce qu tratamiento tiene que darse a los identificadores
de los programas escritos con lenguajes con estructura de bloques, es posible implementar sus
60 Compiladores e intrpretes: teora y prctica
02-CAPITULO 02 9/2/06 11:49 Pgina 60
tablas de smbolos utilizando una sola tabla para todos los bloques. A continuacin se mencionan
los aspectos ms relevantes que hay que tener en cuenta:
1. Habr que mantener informacin, por un lado sobre los bloques, y por otro sobre los
identificadores.
2. De cada bloque se tiene que guardar, al menos, la siguiente informacin:
Identificacin del bloque.
Apuntador al bloque en el que se declar.
Apuntador al espacio donde se guardan sus identificadores.
Captulo 2. Tabla de smbolos 61
1 2 3 4 5 6 7 8
{ int a, b, c, d;
{ int e, f;
. . .
L1:
. . .
}
}
}
}
L2:
{ int i, h;
{ int a;
B
1
: a, b, c, d B
1
: a, b, c, d, B
2
B
2
: e, f, L1
B
1
: a, b, c, d, B
2
B
1
: a, b, c, d, B
2
, B
3
B
3
: i, h, L2
1 2 3 4
8 7 6 5
B
1
: a, b, c, d, B
2
, B
3
B
1
: a, b, c, d, B
2
, B
3
B
3
: i, h, L2, B
4
B
1
: a, b, c, d, B
2
, B
3
B
3
: i, h, L2, B
4
B
4
: a
B
2
: e, f, L1
B
2
: e, f, L1
B
2
: e, f, L1
B
4
: a
B
2
: e, f, L1
B
3
: i, h, L2, B
4
B
4
: a
B
2
: e, f, L1
B
1
: a, b, c, d, B
2
, B
3
B
3
: i, h, L2, B
4
B
4
: a
B
2
: e, f, L1
Figura 2.24. Ejemplo de tabla de smbolos de un programa escrito con un lenguaje con es-
tructura de bloques. La tabla de smbolos utiliza una tabla hash para cada mbito; el compila-
dor realiza ms de un paso. En la pila de tablas hash se seala la cima. Los mbitos abiertos
estn rodeados por un recuadro ms grueso que los cerrados.
02-CAPITULO 02 9/2/06 11:49 Pgina 61
No se describirn ms detalles de esta tcnica, ya que su implementacin es slo un proble-
ma de programacin.
Informacin adicional sobre los identificadores
en las tablas de smbolos
De la definicin del lenguaje de programacin utilizado depende la informacin que hay que al-
macenar en la tabla de smbolos para el tratamiento correcto del programa:
Clase del identificador, para indicar a qu tipo de objeto se refiere el identificador. Por ejem-
plo, podra ser una variable, funcin o procedimiento, una etiqueta, la definicin de un tipo
de dato, el valor concreto de una enumeracin, etc.
Tipo, para indicar el tipo de dato. Por ejemplo: entero, real, lgico, complejo, carcter, ca-
dena de caracteres, tipo estructurado, tipo declarado por el programador, subrutina que de-
vuelve un dato, subrutina que no devuelve dato alguno, operador, etc.
Resumen
Uno de los objetivos de este captulo es la justificacin de la eleccin de las tablas de dispersin
o hash para la implementacin de la tabla de smbolos de los compiladores e intrpretes. En
primer lugar se repasa, de manera informal e intuitiva, la complejidad temporal de los algorit-
mos de bsqueda ms utilizados (lineal y binaria sobre vectores de datos y los especficos de r-
boles binarios ordenados y rboles AVL). Se muestra cmo la comparacin de claves limita el
rendimiento de una manera inaceptable y se justifica el uso de las tablas hash, de las que se des-
criben con ms detalle diferentes variantes. Debido a la complejidad de la teora en la que se ba-
san estos resultados y que el mbito de este libro no presupone al lector ningn conocimiento
especfico de la materia, se ha pretendido, siempre que ha sido posible, acompaar cada resul-
tado con una justificacin intuitiva y convincente que supla la ausencia de la demostracin
formal.
El captulo termina con la descripcin de dos aspectos prcticos propios del uso que los com-
piladores e intrpretes dan a la tabla hash: las tablas de smbolos para los lenguajes de progra-
macin que tienen estructura de bloques y la informacin adicional que se necesita conservar en
la tabla de smbolos sobre los identificadores.
Ejercicios y otro material prctico
El lector encontrar en http://www.librosite.net/pulido abundante material prctico sobre el conte-
nido de este captulo con ejercicios resueltos y versiones ejecutables de los algoritmos descritos.
2.7
2.6
2.5
62 Compiladores e intrpretes: teora y prctica
02-CAPITULO 02 9/2/06 11:49 Pgina 62
Bibliografa
[1] Knuth, D. E. (1997): The art of computer programming, Addison Wesley Longman.
[2] Cormen, T. H.; Leiserson, C. E.; Rivest, R. L., y Stein, C. (2001): Introduction to algorithms, The MIT
Press, McGraw-Hill Book Company.
[3] McKenzie, B. J.; Harries R. y Bell, T. C. (1990): Selecting a hashing algorithm, Software - Practice
and Experience, 20(2), 209-224.
2.8
Captulo 2. Tabla de smbolos 63
02-CAPITULO 02 9/2/06 11:49 Pgina 63
02-CAPITULO 02 9/2/06 11:49 Pgina 64
Captulo 3
Anlisis morfolgico
Introduccin
El analizador morfolgico, tambin conocido como analizador lxico (scanner, en ingls) se en-
carga de dividir el programa fuente en un conjunto de unidades sintcticas (tokens, en ingls).
Una unidad sintctica es una secuencia de caracteres con cohesin lgica. Ejemplos de unidades
sintcticas son los identificadores, las palabras reservadas, los smbolos simples o mltiples y las
constantes (numricas o literales).
Para llevar a cabo esta divisin del programa en unidades sintcticas, el analizador morfol-
gico utiliza un subconjunto de las reglas de la gramtica del lenguaje en el que est escrito el pro-
grama que se va a compilar. Este subconjunto de reglas corresponde a un lenguaje regular, es
decir, un lenguaje definido por expresiones regulares.
El analizador morfolgico lleva a cabo tambin otra serie de tareas auxiliares como el trata-
miento de los comentarios y la eliminacin de blancos y smbolos especiales (caracteres de ta-
bulacin y saltos de lnea, entre otros).
La Tabla 3.1 muestra las unidades sintcticas de un lenguaje ejemplo y la Figura 3.1 muestra
la gramtica independiente del contexto que usar el analizador morfolgico para identificar las
unidades sintcticas de dicho lenguaje.
Un analizador morfolgico es un autmata finito determinista que reconoce el lenguaje gene-
rado por las expresiones regulares correspondientes a las unidades sintcticas del lenguaje fuen-
te. En las secciones siguientes se describe cmo programar manualmente dicho autmata
mediante un proceso que comprende los siguientes pasos:
3.1
03-CAPITULO 03 9/2/06 11:49 Pgina 65
1. Construir el Autmata Finito No Determinista (AFND) correspondiente a una expresin
regular.
2. Transformar el AFND obtenido en el paso 1 en un Autmata Finito Determinista
(AFD).
3. Minimizar el nmero de estados del AFD obtenido en el paso 2.
4. Implementar en forma de cdigo el AFD obtenido en el paso 3.
66 Compiladores e intrpretes: teora y prctica
Palabras reservadas Smbolos simples Smbolos dobles Otros
begin ; := nmero (uno o ms
end , <= dgitos)
bool +
int identificador (una
ref * letra seguida de 0 o
function ( ms letras y/o dgitos)
if =
then >
fi
else
while
do
input
output
deref
true
false
Tabla 3.1. Unidades sintcticas de un lenguaje ejemplo.
Figura 3.1. Gramtica para las unidades sintcticas del lenguaje ejemplo.
<US> ::= <p_reservada> | <s_simple> | <s_doble> | <id> | <cte_num>
<p_reservada> ::= begin | end | bool | int | ref | function | if | then | fi | else | while
| do | repeat | input | output | deref | true | false
<s_simple> ::= ; | , | + | | * | ( | ) | = | >
<s_doble> ::= := | <=
<cte_num> ::= <digito> | <cte_num> <digito>
<id> ::= <letra> | <letra><resto_id>
<resto_id> ::= <alfanumerico> | <alfanumerico><resto_id>
<alfanumerico> ::= <digito> | <letra>
<digito> ::= 0 | 1 | ... | 9
<letra> ::= a | b | ... | z | A | B | ... | Z
03-CAPITULO 03 9/2/06 11:49 Pgina 66
Tambin es posible implementar el autmata correspondiente al analizador morfolgico utili-
zando una herramienta de generacin automtica como la que se describe en la ltima seccin
del captulo.
Expresiones regulares
Una expresin regular es una forma abreviada de representar cadenas de caracteres que se ajus-
tan a un determinado patrn. Al conjunto de cadenas representado por la expresin r se lo llama
lenguaje generado por la expresin regular r y se escribe L(r).
Una expresin regular se define sobre un alfabeto y es una cadena formada por caracteres
de dicho alfabeto y por una serie de operadores tambin llamados metacaracteres.
Las expresiones regulares bsicas se definen de la siguiente forma:
1. El smbolo (conjunto vaco) es una expresin regular y L() = {}
2. El smbolo (palabra vaca) es una expresin regular y L() = {}
3. Cualquier smbolo a es una expresin regular y L(a) = {a}
A partir de estas expresiones regulares bsicas pueden construirse expresiones regulares ms
complejas aplicando las siguientes operaciones:
1. Concatenacin (se representa con el metacarcter .)
Si r y s son expresiones regulares, entonces r.s tambin es una expresin regular y
L(r.s)=L(r).L(s). El operador . puede omitirse de modo que rs tambin repre-
senta la concatenacin.
La concatenacin de dos lenguajes L
1
y L
2
se obtiene concatenando cada cadena de L
1
con
todas las cadenas de L
2
. Por ejemplo, si L
1
= {00 , 1} y L
2
= {11 , 0 , 10}, entonces
L
1
L
2
= {0011,000,0010,111,10,110}.
2. Unin (se representa con el metacarcter |)
Si r y s son expresiones regulares, entonces r | s tambin es una expresin regular y
L(r | s) = L(r) L(s). Por ejemplo, el lenguaje generado por la expresin regular
ab | c es L(ab | c) = {ab , c}.
3. Cierre o clausura (se representa con el metacarcter *)
Si r es una expresin regular, entonces r* tambin es una expresin regular y L(r*) =
L(r)*.
La operacin de cierre aplicada a un lenguaje L se define as:
L* =
i=0
L
i
3.2
Captulo 3. Anlisis morfolgico 67
03-CAPITULO 03 9/2/06 11:49 Pgina 67
donde L
i
es igual a la concatenacin de L consigo mismo i veces y L
0
= . Por ejemplo,
el lenguaje generado por la expresin regular a*ba* es L(a*ba*) = {b , ab , ba ,
aba , aab , ...}, es decir, el lenguaje formado por todas las cadenas de as y bs que
contienen una nica b.
Cuando aparecen varias operaciones en una expresin regular, el orden de precedencia es el
siguiente: cierre, concatenacin y unin. Este orden puede modificarse mediante el uso de pa-
rntesis.
Autmata Finito No Determinista (AFND) para una
expresin regular
Intuitivamente un autmata finito consta de un conjunto de estados y, partiendo de un esta-
do inicial, realiza transiciones de un estado a otro en respuesta a los smbolos de entrada que
procesa. Cuando el autmata alcanza un estado de los que se denominan finales, se dice
que ha reconocido la palabra formada por concatenacin de los smbolos de entrada proce-
sados.
Un autmata finito puede ser determinista o no determinista. La expresin no determinista
significa que desde un mismo estado puede haber ms de una transicin etiquetada con el mismo
smbolo de entrada.
Un autmata finito no determinista es una quntupla (, Q, , q0, F), donde
1. es un conjunto finito de smbolos de entrada o alfabeto.
2. Q es un conjunto finito de estados.
3. es la funcin de transicin que recibe como argumentos un estado y un smbolo de en-
trada o el smbolo y devuelve un subconjunto de Q.
4. q0 Q es el estado inicial.
5. F Q es el conjunto de estados finales.
Un autmata finito puede representarse mediante lo que se conoce como diagrama de transi-
cin, que es un grafo dirigido construido de la siguiente forma:
Cada nodo est etiquetado con un elemento de Q.
Si (p,a) = q, se dibuja un arco del nodo con etiqueta p al nodo con etiqueta q etiqueta-
do con el smbolo a.
El estado inicial aparece sealado con una flecha sin origen.
Los estados finales aparecen marcados con un doble crculo.
3.3
68 Compiladores e intrpretes: teora y prctica
03-CAPITULO 03 9/2/06 11:49 Pgina 68
La Figura 3.2 muestra el diagrama de transicin para el autmata finito determinista
({0,1},{p,q,r},,p,{q}), donde est definida de la siguiente forma:
(p,0)=q (p,1)=r (q,0)=q
(q,1)=r (r,0)=r (r,1)=r
Para toda expresin regular e es posible construir un autmata finito no determinista que
acepte el lenguaje generado por dicha expresin regular. El algoritmo es recursivo y consta de los
siguientes pasos:
1. Si e = , el autmata correspondiente es el que aparece en la Figura 3.3(a).
2. Si e = , el autmata correspondiente es el que aparece en la Figura 3.3(b).
3. Si e = a, a , el autmata correspondiente es el que aparece en la Figura 3.3(c).
Captulo 3. Anlisis morfolgico 69
p q
r
0
1 1
0,1
0
Figura 3.2. Diagrama de transicin para un autmata.
p q
a)
p q
b)
c)
p q
a
Figura 3.3. Autmatas para expresiones regulares bsicas.
4. Si e = r|s y tenemos los autmatas correspondientes a r y s, que representaremos como
aparecen en la Figura 3.4, el autmata correspondiente a la expresin r|s es el que apa-
rece en la Figura 3.5(a).
03-CAPITULO 03 9/2/06 11:49 Pgina 69
5. Si e = rs y tenemos los autmatas correspondientes a r y s, que representaremos como
aparecen en la Figura 3.4, el autmata correspondiente a la expresin rs es el que apare-
ce en la Figura 3.5(b).
70 Compiladores e intrpretes: teora y prctica
p
p
1
p
2
r
s
q
2
q
1
q
a)
p
2
q
2
s
p
1
q
1
r
b)
Figura 3.5. Autmatas correspondientes a las expresiones r|s y rs.
r
a)
q
1 q
p p
1
b)
r
q
1
p
1
digito
q
5
q
6
q
3
q
4
digito
q
2
q
1
digito
q
2
q
1
digito
c)
Figura 3.7. AFND para la expresin regular digito.digito*.
Autmata Finito Determinista (AFD) equivalente
a un AFND
Un autmata finito determinista es una quntupla (, Q, , q0, F), donde
1. es un conjunto finito de smbolos de entrada o alfabeto.
2. Q es un conjunto finito de estados.
3.4
03-CAPITULO 03 9/2/06 11:49 Pgina 71
3. es la funcin de transicin que recibe como argumentos un estado y un smbolo de en-
trada y devuelve un estado.
4. q0 Q es el estado inicial.
5. F Q es el conjunto de estados finales.
La funcin de transicin extendida recibe como argumentos un estado p y una cadena de ca-
racteres wy devuelve el estado que alcanza el autmata cuando parte del estado p y procesa la ca-
dena de caracteres w.
Dado un autmata finito no determinista N = (, Q, f, q0, F), siempre es posible cons-
truir un autmata finito determinista D = (, Q, f, q0, F) equivalente (que acepte el
mismo lenguaje). Para construir dicho autmata seguiremos el siguiente procedimiento:
Cada estado de D corresponde a un subconjunto de los estados de N. En el autmata de la
Figura 3.7(c) los subconjuntos {q1}, {q3, q4} o {q2, q4, q6} seran posibles es-
tados del autmata finito determinista equivalente.
El estado inicial q0 de D es el resultado de calcular el cierre del estado inicial q0 de
N. El cierre de un estado e se representa como e
para el
principio y el final, respectivamente.
Conjuntos importantes en una gramtica
Sea una gramtica limpia G = (
T
,
N
, S, P). Sea =
T
N
. Sea un smbolo de esta
gramtica (terminal o no terminal). Se definen los siguientes conjuntos asociados a estas gram-
ticas y a este smbolo:
Si X
N
, primero(X) = {V | X + Vx, V
T
, x
*
}
Si X
T
, primero(X) = {X}
Es decir, si X es terminal, primero(X) contiene slo a X; en caso contrario, primero(X)
es el conjunto de smbolos terminales que pueden aparecer al principio de alguna forma senten-
cial derivada a partir de X.
siguiente(X) = {V | S + xXVy, X
N
, V
T
, x,y
*
}, donde S es el axioma
de la gramtica.
Es decir, si X es un smbolo no terminal de la gramtica, siguiente(X) es el conjunto de
smbolos terminales que pueden aparecer inmediatamente a la derecha de X en alguna forma sen-
tencial. Si X puede aparecer en el extremo derecho de alguna forma sentencial, entonces el sm-
bolo de fin de cadena $ siguiente(X).
Para calcular el conjunto primero(X) X
N
T
, se aplicarn las siguientes reglas, has-
ta que no se puedan aadir ms smbolos terminales ni a dicho conjunto.
(R1) Si X
T
, se hace primero(X)={X}.
(R2) Si X ::= P, se aade a primero(X).
4.1
90 Compiladores e intrpretes: teora y prctica
04-CAPITULO 04 9/2/06 11:50 Pgina 90
(R3) Si X ::= Y
1
Y
2
... Y
k
P, se aade primero(Y
i
){} a primero(X) para
i=1,2,,j, donde j es el primer subndice tal que Yj no genera la cadena vaca (Y
j
es ter-
minal, o siendo no terminal no ocurre que Y
j
).
(R4) Si X ::= Y
1
Y
2
... Y
k
P y Y
j
j {1,2,...,k}, se aade a prime-
ro(X).
Consideremos la gramtica siguiente, en la que E es el axioma:
(1) E ::= TE
(2) E ::= +TE
(3) E ::=
(4) T ::= FT
(5) T ::= *FT
(6) T ::=
(7) F ::= (E)
(8) F ::= id
En esta gramtica, el conjunto primero(E) resulta ser igual al conjunto primero(T)
aplicando la regla (R3) a la regla (1). Para calcular el conjunto primero(T) se aplica la regla
(R3) a la regla (4), de la que se obtiene que primero(T) es igual a primero(F).
Para calcular primero(F) se aplica la regla (R3) a la regla (7), aadiendo el conjunto pri-
mero((), que es igual a {(} por la regla (R1). Al aplicar la regla (R3) a la regla (8), se aade
tambin el conjunto primero(id), que es igual a {id} por la regla (R1). Por tanto:
primero(E)=primero(T)=primero(F)={(, id}
A continuacin se calcula el conjunto primero(E), aplicando, en primer lugar, la regla
(R3) a la regla (2), que aade el conjunto primero(+), que es igual a {+} por la regla (R1).
Al aplicar la regla (R2) a la regla (3), se aade tambin . Por tanto,
primero(E)={+, }
De una forma parecida, se calcula el conjunto primero(T). Al aplicar la regla (R3) a la
regla (5) se aade el conjunto primero(*), que es igual a {*} por la regla (R1), y aplicando
la regla (R2) a la regla (6), se aade . Por tanto,
primero(T)={*, }
En alguno de los algoritmos de anlisis sintctico descritos en este captulo ser necesario ex-
tender la definicin del conjunto primero para que se aplique a una forma sentencial, lo que se
har de la siguiente manera: sea G=(
T
,
N
, S, P) una gramtica independiente del contexto.
Si es una forma sentencial de la gramtica, (
N
T
)*; es decir, si puede obtenerse
por derivacin, a partir del axioma S, en cero o ms pasos, aplicando reglas de P, llamaremos
primero() al conjunto de smbolos terminales que pueden aparecer en primer lugar en las
cadenas derivadas a partir de . Si desde se puede derivar la cadena vaca , sta tambin per-
tenecer a primero().
Ejemplo
4.1
Captulo 4. Anlisis sintctico 91
04-CAPITULO 04 9/2/06 11:50 Pgina 91
Sea =X
1
X
2
...X
n
. Para calcular primero(), se aplicar el siguiente algoritmo hasta que
no se puedan aadir ms smbolos terminales o a dicho conjunto:
Aadir a primero() todos los smbolos de primero(X
1
), excepto .
Si primero(X
1
) contiene , aadir a primero() todos los smbolos de prime-
ro(X
2
), excepto .
Si primero(X
1
) y primero(X
2
) contienen , aadir a primero() todos los sm-
bolos de primero(X
3
), excepto .
Y as sucesivamente.
Si i {1,2,..,n} primero(X
i
) contiene , entonces aadir a primero().
En la gramtica del Ejemplo 4.1, para calcular el conjunto primero(TEid) se calcula
primero(T), por lo que se aadir {*}. Como primero(T) contiene , es necesario aa-
dir tambin primero(E), por lo que se aade {+}. Como primero(E) tambin contiene
, es necesario aadir tambin primero(id), que es igual a {id}. Por tanto:
primero(TEid) = {*,+,id}
Para calcular el conjunto siguiente(X) X
N
, deben aplicarse las siguientes reglas,
hasta que no se puedan aadir ms smbolos terminales a dicho conjunto.
(R1) Para el axioma S, aadir $ a siguiente(S).
(R2) Si A::=X P, aadir todos los smbolos (excepto ) de primero() a si-
guiente(X).
(R3) Si A::=X P y primero(), aadir todos los smbolos de siguiente(A)
a siguiente(X).
(R4) Si A::=X P, aadir siguiente(A) a siguiente(X).
Como ejemplo se considerar la gramtica del Ejemplo 4.1. Se tendr que:
siguiente(E)={$,)}
El smbolo $ se aade al aplicar la regla (R1), y el smbolo ) al aplicar la regla (R2) a la
regla (7).
Al aplicar la regla (R4) a las reglas (1) y (2), el conjunto siguiente(E) resulta ser igual
al conjunto siguiente(E), ya que la afirmacin siguiente(E)= siguiente(E) de-
ducida de la regla (2) no aade ningn smbolo nuevo. Por tanto:
siguiente(E)={$,)}
Para calcular siguiente(T) se aplica la regla (R2) a las reglas (1) y (2) y se aade el sm-
bolo + por pertenecer al conjunto primero(E). Como primero(E), se aplica la re-
gla (R3) a la regla (1) y se aaden tambin los smbolos ) y $ por pertenecer a siguiente(E).
Por tanto:
siguiente(T)={+,$,)}
92 Compiladores e intrpretes: teora y prctica
04-CAPITULO 04 9/2/06 11:50 Pgina 92
Al aplicar la regla (R4) a la regla (4), siguiente(T) resulta ser igual a siguien-
te(T). Por tanto:
siguiente(T)={+,$,)}
Por ltimo, se calcula siguiente(F). Al aplicar la regla (R2) a las reglas (4) y (5), se aa-
de el smbolo * por pertenecer a primero(T). Como primero(T), se aplica la regla
(R3) a la regla (4) y se aaden los smbolos +, $ y ) por pertenecer a siguiente(T). Al apli-
car la regla (R3) a la regla (5), deberan aadirse los smbolos +, $ y ) por pertenecer a si-
guiente(T), pero no se hace porque ya pertenecen al conjunto. Por tanto:
siguiente(F)={*,+,$,)}
Anlisis sintctico descendente
Como se menciona en la introduccin de este captulo, para realizar el anlisis sintctico des-
cendente de una palabra x, se parte del axioma S y se va realizando la derivacin S*x. Un m-
todo posible para hacerlo es el anlisis descendente con vuelta atrs, que consiste en probar
sistemticamente todas las alternativas hasta llegar a la reduccin buscada o hasta que se agoten
dichas alternativas. La ineficiencia de este mtodo se soluciona si, en cada momento del proceso
de construccin de la derivacin, slo se puede aplicar una regla de la gramtica. Esta idea es el
origen de las gramticas LL(1) y del mtodo de anlisis descendente selectivo que se aplica a este
tipo de gramticas.
4.2.1. Anlisis descendente con vuelta atrs
En una gramtica dada, sea S el axioma y sean las reglas cuya parte izquierda es el axioma:
S ::= X
1
X
2
... X
n
| Y
1
Y
2
... Y
m
| ...
Sea x la palabra que se va a analizar. El objetivo del anlisis es encontrar una derivacin tal que
S * x
En el mtodo de anlisis descendente con vuelta atrs se prueba primero la regla
S ::= X
1
X
2
... X
n
Para aplicarla, es necesario descomponer x de la forma
x = x
1
x
2
... x
n
y tratar de encontrar las derivaciones
X
1
* x
1
X
2
* x
2
...
X
n
* x
n
4.2
Captulo 4. Anlisis sintctico 93
04-CAPITULO 04 9/2/06 11:50 Pgina 93
Cada una de las derivaciones anteriores es una submeta. Pueden ocurrir los siguientes casos:
(Caso 1) X
i
=x
i
: submeta reconocida. Se pasa a la submeta siguiente.
(Caso 2) X
i
x
i
y X
i
es un smbolo terminal: submeta rechazada. Se intenta encontrar otra
submeta vlida para X
i-1
. Si i=1, se elige la siguiente parte derecha para el mismo smbo-
lo no terminal a cuya parte derecha pertenece X
i
. Si ya se han probado todas las partes de-
rechas, se elige la siguiente parte derecha del smbolo no terminal del nivel superior. Si ste
es el axioma, la cadena x queda rechazada.
(Caso 3) X
i
es un smbolo no terminal. Se buscan las reglas de las que X
i
es parte izquierda:
X
i
::= X
i1
X
i2
... X
in
| Y
i1
Y
i2
... Y
im
| ...
se elige la primera opcin:
X
i
::= X
i1
X
i2
... X
in
se descompone x
i
en la forma
x
i
= x
i1
x
i2
... x
in
lo que da las nuevas submetas
X
i1
* x
i1
X
i2
* x
i2
...
X
in
* x
in
y continuamos recursivamente de la misma forma.
El proceso termina cuando en el Caso 2 no hay ms reglas que probar para el axioma (en cuyo
caso la cadena no es reconocida) o cuando se reconocen todas las submetas pendientes (en cuyo
caso la cadena s es reconocida).
Consideremos la gramtica siguiente:
S ::= aSb
S ::= a
Sea aabb la palabra a reconocer.
Se prueba primero la regla S ::= aSb y se intenta encontrar las siguientes derivaciones:
(S1) a * a
(S2) S * ab
(S3) b * b
La Figura 4.1 ilustra este primer paso. Las derivaciones primera y tercera corresponden a sub-
metas reconocidas de acuerdo con el Caso 1. La segunda derivacin corresponde al Caso 3, por
lo que se elige la regla S ::= aSb, por ser la primera regla en cuya parte izquierda aparece S.
Se obtienen dos nuevas submetas:
a * a
S * b
Ejemplo
4.2
94 Compiladores e intrpretes: teora y prctica
04-CAPITULO 04 9/2/06 11:50 Pgina 94
Como puede apreciarse en la Figura 4.2, la primera derivacin corresponde a una submeta re-
conocida de acuerdo con el Caso 1. La segunda derivacin corresponde al Caso 3, por lo que se
elige la regla S ::= aSb, por ser la primera regla en cuya parte izquierda aparece S. Se obtiene
una nueva submeta:
a * b
Captulo 4. Anlisis sintctico 95
a
a a b b
b S
S
Figura 4.1. Construccin de una derivacin para la palabra aabb: paso 1.
a
a
S
S
b
b
b b a a
S
Figura 4.2. Construccin de una derivacin para la palabra aabb: paso 2.
Esta derivacin aparece en la Figura 4.3 y corresponde a una submeta rechazada, de acuerdo
con el Caso 2. Como el smbolo a es el primer smbolo en la parte derecha de la regla S ::= aSb,
04-CAPITULO 04 9/2/06 11:50 Pgina 95
se elige la siguiente parte derecha para el smbolo S, es decir, S ::= ab. Como ilustra la
Figura 4.4, se obtiene una nueva submeta:
a * b
96 Compiladores e intrpretes: teora y prctica
a
a
S
S
b
b
b b a a
S
S b
b
Figura 4.3. Construccin de una derivacin para la palabra aabb: paso 3.
a
a
S
S
b
b
b b a a
S
b
a
Figura 4.4. Construccin de una derivacin para la palabra aabb: paso 4.
04-CAPITULO 04 9/2/06 11:50 Pgina 96
Esta derivacin tambin corresponde a una submeta rechazada, de acuerdo con el Caso 2.
Como ya no hay ms reglas para el smbolo S, se vuelve a la submeta (S2) y se elige la siguien-
te parte derecha para S, es decir, S ::= ab. Se obtienen dos nuevas submetas:
a * a
b * b
Captulo 4. Anlisis sintctico 97
a
a
S
S
b
b
b b a a
Figura 4.5. rbol de derivacin para la palabra aabb.
Ambas derivaciones corresponden a submetas reconocidas de acuerdo con el Caso 1. Como
se han encontrado todas las derivaciones, se reconoce la cadena aabb. La Figura 4.5 muestra el
rbol de derivacin para la palabra aabb.
Como habr podido observarse, este mtodo de anlisis puede ser bastante ineficiente, por-
que el nmero de submetas que hay que comprobar puede ser bastante elevado. Una forma de
optimizar el procedimiento consiste en ordenar las partes derechas de las reglas que compar-
ten la misma parte izquierda, de manera que la regla buena se analice antes. Una buena estra-
tegia para ordenar las partes derechas es poner primero las de mayor longitud. Si una parte
derecha es la cadena vaca, debe ser la ltima. Si se llega a ella, la submeta tiene xito auto-
mticamente. A esta variante del mtodo se la denomina anlisis descendente con vuelta atrs
rpida.
Como ejemplo, consideremos la siguiente gramtica:
E ::= T+E | T
T ::= F*T | F
F ::= i
04-CAPITULO 04 9/2/06 11:50 Pgina 97
Se analiza la cadena i*i. Se prueba primero E ::= T+E y se obtiene el rbol de la
Figura 4.6.
En cuanto se compruebe que el signo + no pertenece a la cadena objetivo, no es necesario
volver a las fases precedentes del anlisis, tratando de obtener otra alternativa, sino que se pue-
de pasar directamente a probar E::=T. El rbol de derivacin obtenido aparece en la
Figura 4.7.
98 Compiladores e intrpretes: teora y prctica
T
E
+
E
F T
F
*
*
i i
Figura 4.6. Construccin de una derivacin para la palabra i*i: paso 1.
T
E
F T
F
*
*
i i
Figura 4.7. rbol de derivacin para la palabra i*i.
04-CAPITULO 04 9/2/06 11:50 Pgina 98
4.2.2. Anlisis descendente selectivo
En el mtodo ptimo de anlisis descendente, en cada etapa de construccin del rbol de deriva-
cin slo debera ser posible aplicar una regla. Este mtodo de anlisis se llama anlisis descen-
dente selectivo, anlisis sin vuelta atrs o descenso recursivo, y las gramticas compatibles con
l reciben el nombre de gramticas LL(1). Esto puede conseguirse, por ejemplo, si en la gram-
tica que se est utilizando para el anlisis, las partes derechas de las reglas que tienen la misma
parte izquierda empiezan por un smbolo terminal distinto.
Las siglas LL(1) se refieren a una familia de analizadores sintcticos descendentes en los que
la entrada se lee desde la izquierda Left en ingls, las derivaciones en el rbol se hacen de
izquierda Left a derecha, y en cada paso del anlisis se necesita conocer slo un 1 sm-
bolo de la entrada.
Existen varias aproximaciones a este tipo de anlisis, lo que da lugar a definiciones diferentes,
no siempre equivalentes, de las gramticas LL(1). En este captulo se presentarn dos de ellas:
La que se basa en el uso de la forma normal de Greibach.
La que se basa en el uso de tablas de anlisis.
4.2.3. Anlisis LL(1) mediante el uso de la forma normal
de Greibach
Se dice que una gramtica est en forma normal de Greibach si todas las producciones tienen la
forma A ::= ax, donde A
N
, a
T
, x
N
*
, es decir, si la parte derecha de todas las re-
glas empieza por un smbolo terminal, seguido opcionalmente por smbolos no terminales.
De acuerdo con esta definicin, una gramtica LL(1) es una gramtica en forma normal de
Greibach en la que no existen dos reglas con la misma parte izquierda, cuya parte derecha em-
piece por el mismo smbolo terminal.
Es decir, una gramtica LL(1) cumple dos condiciones:
La parte derecha de todas las reglas empieza por un smbolo terminal, seguido opcional-
mente por smbolos no terminales.
No existen dos reglas con la misma parte izquierda, cuya parte derecha empiece por el mis-
mo smbolo terminal.
Como ejemplo, la siguiente gramtica es LL(1), porque la parte derecha de todas las reglas
empieza por un smbolo terminal, seguido opcionalmente por smbolos no terminales, y las dos
reglas cuya parte izquierda es la misma (smbolo R) empiezan por un smbolo terminal distinto.
F ::= iRP
R ::= aAC | bZ
C ::= c
P ::= p
Captulo 4. Anlisis sintctico 99
04-CAPITULO 04 9/2/06 11:50 Pgina 99
Se llama LL(1) a este tipo de gramticas, porque en cada momento basta estudiar un carcter
de la cadena objetivo para saber qu regla se debe aplicar.
Eliminacin de la recursividad a izquierdas. Para convertir una gramtica en otra equiva-
lente en forma normal de Greibach, es preciso eliminar las reglas recursivas a izquierdas, si las
hay. Una regla es recursiva a izquierdas si tiene la forma A ::= Ax, donde x
*
.
Sea la gramtica independiente del contexto G = (
T
,
N
, S, P), donde P contiene las
reglas
A ::= A
1
| A
2
| ... | A
n
|
1
|
2
| ... |
m
Se construye la gramtica G = (
T
,
N
{X}, S, P), donde P se obtiene reemplazan-
do en P las reglas anteriores por las siguientes:
A ::=
1
X |
2
X | ... |
m
X
X ::=
1
X |
2
X | ... |
n
X |
En P ya no aparecen reglas recursivas a izquierdas y puede demostrarse que L(G) = L(G).
Consideremos la gramtica siguiente:
E ::= E + T | T
T ::= T * F | F
F ::= i
Para eliminar de esta gramtica las reglas recursivas a izquierdas, se empieza por aquellas en
cuya parte izquierda aparece el smbolo E. En este caso
1
= +T y
1
= T. Por lo tanto, deben
reemplazarse las dos primeras reglas de la gramtica por las siguientes:
E ::= TX
X ::= +TX |
Si se aplica la misma transformacin a las reglas en cuya parte izquierda aparece el smbolo
T (1 = *F y 1 = F), se obtienen las reglas:
T ::= FY
Y ::= *FY |
Despus de eliminar la recursividad a izquierdas, se obtiene la siguiente gramtica equivalen-
te a la original:
E ::= TX
X ::= +TX |
T ::= FY
Y ::= *FY |
F ::= i
Eliminacin de smbolos no terminales iniciales. El algoritmo que se va a describir a con-
tinuacin tiene por objeto eliminar las reglas que empiezan por un smbolo no terminal. Para ello,
hay que establecer una relacin de orden parcial en
N
= {A
1
, A
2
, ..., A
n
} de la siguiente
Ejemplo
4.3
100 Compiladores e intrpretes: teora y prctica
04-CAPITULO 04 9/2/06 11:50 Pgina 100
forma: A
i
precede a A
j
si P contiene al menos una regla de la forma A
i
::=A
j
, donde
*
.
Si existen bucles de la forma A
i
::=A
j
, A
j
::=A
i
, se elige un orden arbitrario entre los sm-
bolos no terminales A
i
y A
j
.
En la gramtica obtenida en el Ejemplo 4.3, puede establecerse la relacin de orden E < T < F.
Despus de definir la relacin de orden, se clasifican las reglas de P en tres grupos:
1. Reglas de la forma A ::= ax, donde a
T
, x
*
.
2. Reglas de la forma A
i
::= A
j
x, donde A
i
< A
j
y x
*
.
3. Reglas de la forma A
i
::= A
j
x, donde A
i
> A
j
y x
*
.
En la gramtica obtenida en el Ejemplo 4.3, todas las reglas son de tipo 1, excepto las reglas
E ::= TX y T ::= FY, que son de tipo 2.
Para obtener una gramtica en forma normal de Greibach se debe eliminar las reglas de tipo
2 y 3. Para eliminar una regla de la forma A
i
::= A
j
x, basta sustituir A
j
en dicha regla por to-
das las partes derechas de las reglas cuya parte izquierda es A
j
.
Se eliminan primero las reglas de tipo 3. Si existen varias reglas de este tipo, se trata primero
aquella cuya parte izquierda aparece antes en la relacin de orden establecida.
A continuacin, se eliminan las reglas de tipo 2. Si existen varias reglas de este tipo, se trata
primero aquella cuya parte izquierda aparece ms tarde en la relacin de orden establecida.
Si durante este proceso de eliminacin aparecen de nuevo reglas recursivas a izquierdas, se
eliminan aplicando el procedimiento descrito anteriormente. Si aparecieran smbolos inaccesi-
bles, tambin deberan eliminarse (vase la Seccin 1.12.2).
En la gramtica obtenida en el Ejemplo 4.3, no hay reglas de tipo 3, por lo que slo hay que eli-
minar las de tipo 2: E ::= TX y T ::= FY. Como T aparece detrs de E en la relacin de orden,
se elimina primero la regla cuya parte izquierda es T, y se obtiene la siguiente gramtica:
E ::= TX
X ::= +TX |
T ::= iY
Y ::= *FY |
F ::= i
Despus de eliminar la regla en cuya parte izquierda aparece E, se obtiene la siguiente gra-
mtica:
E ::= iYX
X ::= +TX |
T ::= iY
Y ::= *FY |
F ::= i
De acuerdo con la definicin dada anteriormente, en una gramtica en forma normal de
Greibach no pueden aparecer reglas-, es decir, reglas cuya parte derecha es el smbolo . Sin
Ejemplo
4.4
Captulo 4. Anlisis sintctico 101
04-CAPITULO 04 9/2/06 11:50 Pgina 101
embargo, como el objetivo es obtener una gramtica a la que se pueda aplicar el mtodo de an-
lisis descendente selectivo, se permite una regla- A ::= si se cumple que primero(A)
siguiente(A) = .
Veamos si las reglas- que aparecen en la gramtica obtenida en el Ejemplo 4.4 pueden per-
manecer en la gramtica. Para la regla X::= se cumple que primero(X) = {+, } y si-
guiente(X) = {$}. La interseccin de estos dos conjuntos es el conjunto vaco, por lo que
la regla X::= puede permanecer en la gramtica. Para la regla Y::= se cumple que pri-
mero(Y) = {*, } y siguiente(Y) = {+,$}. La interseccin de estos dos conjuntos es el
conjunto vaco, por lo que la regla Y::= puede permanecer en la gramtica.
Si una regla- A::= no cumple la condicin indicada, a veces es posible eliminarla, apli-
cando el siguiente procedimiento.
1. Eliminar la regla.
2. Por cada aparicin de A en la parte derecha de una regla, aadir una nueva regla en la que
se elimina dicha aparicin.
Por ejemplo, si se quiere eliminar la regla A::= de una gramtica en la que aparece la re-
gla B::= uAvAw, deben aadirse las siguientes reglas:
B::= uvAw
B::= uAvw
B::= uvw
En la gramtica obtenida en el Ejemplo 4.4, las partes derechas de todas las reglas empiezan por
un smbolo terminal, seguido opcionalmente por smbolos no terminales. Puede ser que, como re-
sultado del proceso anterior, no todos los smbolos que siguen al terminal inicial sean no termina-
les. En tal caso, es necesario eliminar los smbolos terminales no situados al comienzo de la parte
derecha de una regla. El procedimiento que se debe aplicar en este caso es trivial. Sea, por ejemplo,
la regla A ::= abC. Para eliminar el smbolo b basta reemplazar esta regla por las dos siguientes:
A ::= aBC
B ::= b
Consideremos la gramtica siguiente:
A ::= Ba | a
B ::= Ab | b
Inicialmente no hay ninguna regla recursiva a izquierdas. Existen dos relaciones de orden po-
sibles: A < B (por la regla A ::= Ba) y B < A (por la regla B ::= Ab). En este caso, elegiremos
arbitrariamente el orden: B < A.
La clasificacin de las reglas de acuerdo con este orden es la siguiente:
A ::= Ba tipo 3
A ::= a tipo 1
B ::= Ab tipo 2
B ::= b tipo 1
Ejemplo
4.5
102 Compiladores e intrpretes: teora y prctica
04-CAPITULO 04 9/2/06 11:50 Pgina 102
Se elimina primero la nica regla de tipo 3 que aparece en la gramtica: A ::= Ba. El resul-
tado es el siguiente:
A ::= Aba recursiva a izquierdas
A ::= ba tipo 1
A ::= a tipo 1
B ::= Ab tipo 2
B ::= b tipo 3
En la gramtica resultante, el smbolo no terminal B es inaccesible, porque no aparece en la
parte derecha de ninguna regla, por lo que podemos eliminar las reglas en las que aparece como
parte izquierda. El resultado es el siguiente:
A ::= Aba recursiva a izquierdas
A ::= ba tipo 1
A ::= a tipo 1
Despus de eliminar la regla recursiva a izquierdas, el resultado es el siguiente:
A ::= baX tipo 1
A ::= aX tipo 1
X ::= baX tipo 1
X ::= tipo 1
Para que esta gramtica est en forma normal de Greibach, la parte derecha de todas las re-
glas debe constar de un smbolo terminal seguido de smbolos no terminales. Para ello se deben
transformar las reglas 1 y 3 con el siguiente resultado final:
A ::= bZX
A ::= aX
X ::= bZX
X ::=
Z ::= a
El siguiente paso es analizar si la regla X ::= puede permanecer en la gramtica. Para ello,
deben calcularse los conjuntos primero(X) = {b,} y siguiente(X) = {$}. Como la in-
terseccin de estos conjuntos es el conjunto vaco, la regla X ::= puede permanecer en la gra-
mtica.
Para que una gramtica en forma normal de Greibach sea una gramtica LL(1), debe cumplir
que no existan dos reglas con la misma parte izquierda, cuya parte derecha empiece por el mis-
mo smbolo terminal. La gramtica en forma normal de Greibach obtenida para el Ejemplo 4.5
cumple esta condicin, porque las dos reglas con el smbolo A en su parte izquierda empiezan por
dos smbolos terminales distintos: a y b. La gramtica obtenida en el Ejemplo 4.4 tambin cum-
ple esta condicin.
Si no fuese ste el caso, el procedimiento para conseguir que dicha condicin se cumpla es
bastante sencillo. Sea la siguiente gramtica:
Captulo 4. Anlisis sintctico 103
04-CAPITULO 04 9/2/06 11:50 Pgina 103
U ::= aV | aW
V ::= bX | cY
W ::= dZ | eT
Para que las reglas con el smbolo U en su parte izquierda cumplan la condicin LL(1), se hace
algo parecido a sacar factor comn, reemplazando las dos reglas con parte izquierda U por
U ::= aK
K ::= V | W
Ahora las reglas de parte izquierda K no estn en forma normal de Greibach. Para que lo es-
tn, se sustituyen los smbolos V y W por las partes derechas de sus reglas.
U ::= aK
K ::= bX | cY | dZ | eT
El resultado es una gramtica LL(1).
Hay que tener en cuenta que estas operaciones no siempre dan el resultado apetecido, pues no
toda gramtica independiente del contexto puede ponerse en forma LL(1).
Una funcin para cada smbolo no terminal
En una gramtica LL(1), los smbolos no terminales se clasifican en dos grupos: los que tienen
reglas- y los que no. A cada smbolo no terminal de la gramtica se le hace corresponder una
funcin que realizar la parte del anlisis descendente correspondiente a dicho smbolo. La
Figura 4.8 muestra la funcin escrita en C que corresponde a un smbolo no terminal sin regla-,
para el que la gramtica contiene las siguientes reglas:
U::=x X1 X2...Xn | y Y1 Y2...Ym |...| z Z1 Z2...Zp
Esta funcin recibe dos parmetros: la cadena que se va a analizar y un contador que indi-
ca una posicin dentro de dicha cadena. Si el valor del segundo argumento es un nmero ne-
gativo, la funcin termina y devuelve dicho valor. La funcin consta de una instruccin
switch, con un caso para cada una de las partes derechas de las reglas cuya parte izquierda
es el smbolo no terminal para el que se implementa la funcin. En todos los casos se incre-
menta el valor del contador y se invocan las funciones de los smbolos no terminales que apa-
recen en la parte derecha correspondiente. Se aade un caso por defecto, que se ejecuta si
ninguna de las partes derechas es aplicable al carcter de la cadena que se est examinando, en
el que se devuelve un nmero negativo, para indicar que no se reconoce la palabra. Este n-
mero negativo puede ser distinto para cada funcin, lo que servir para identificar cul es la
funcin que ha generado el error. Para el resto de los casos, se devuelve el valor final del con-
tador.
La Figura 4.9 muestra la funcin escrita en C que corresponde a un smbolo no terminal con
regla-, para el que la gramtica contiene las siguientes reglas:
U ::= x X1 X2 ... Xn | ... | z Z1 Z2 ... Zp |
104 Compiladores e intrpretes: teora y prctica
04-CAPITULO 04 9/2/06 11:50 Pgina 104
La nica diferencia con la funcin que aparece en la Figura 4.8 es que en la instruccin
switch no se incluye el caso por defecto, porque la aplicacin de la regla- significa que la fun-
cin correspondiente al smbolo no terminal debe devolver el mismo valor del contador que re-
cibi, es decir, no debe avanzar en la cadena de entrada.
Captulo 4. Anlisis sintctico 105
Figura 4.8. Funcin para un smbolo no terminal sin regla-.
int U (char *cadena, int i)
{
if (i<0) return i;
switch (cadena[i]) {
case x:
i++;
i = X1 (cadena, i);
i = X2 (cadena, i);
. . .
i = Xn (cadena, i);
break;
case y:
i++;
i = Y1 (cadena, i);
i = Y2 (cadena, i);
. . .
i = Ym (cadena, i);
break;
case Z:
i++;
i = Z1 (cadena, i);
i = Z2 (cadena, i);
. . .
i = Zp (cadena, i);
break;
default: return -n;
}
return i;
}
04-CAPITULO 04 9/2/06 11:50 Pgina 105
Consideremos la gramtica siguiente:
E ::= T + E
E ::= T E
E ::= T
T ::= F * T
T ::= F / T
T ::= F
F ::= i
F ::= (E)
En forma normal de Greibach, la gramtica queda as:
E ::= iPTME |(ECPTME | iDTME | (ECDTME | iME | (ECME
| iPTSE |(ECPTSE | iDTSE | (ECDTSE | iSE | (ECSE
| iPT | (ECPT | iDT | (ECDT | i | (EC
T ::= iPT | (ECPT | iDT | (ECDT | i | (EC
Ejemplo
4.6
106 Compiladores e intrpretes: teora y prctica
Figura 4.9. Funcin para un smbolo no terminal con regla-.
int U (char *cadena, int i)
{
if (i<0) return i;
switch (cadena[i]) {
case x:
i++;
i = X1 (cadena, i);
i = X2 (cadena, i);
. . .
i = Xn (cadena, i);
break;
. . .
case Z:
i++;
i = Z1 (cadena, i);
i = Z2 (cadena, i);
. . .
i = Zp (cadena, i);
break;
}
return i;
}
04-CAPITULO 04 9/2/06 11:50 Pgina 106
F ::= i | (EC
M ::= +
S ::= -
P ::= *
D ::= /
C ::= )
A partir de esta gramtica, se obtiene la siguiente gramtica LL(1):
E ::= iV | (ECV
V ::= *TX | /TX | +E | -E |
X ::= +E | -E |
T ::= iU | (ECU
U ::= *T | /T |
F ::= i | (EC
C ::= )
Las funciones correspondientes a los siete smbolos no terminales que aparecen en esta gra-
mtica se muestran en las Figuras 4.10 a 4.16.
Captulo 4. Anlisis sintctico 107
Figura 4.10. Funcin para el smbolo no terminal E.
int E (char *cadena, int i)
{
if (i<0) return i;
switch (cadena[i]) {
case i:
i++;
i = V (cadena, i);
break;
case (:
i++;
i = E (cadena, i);
i = C (cadena, i);
i = V (cadena, i);
break;
default: return -1;
}
return i;
}
04-CAPITULO 04 9/2/06 11:50 Pgina 107
108 Compiladores e intrpretes: teora y prctica
Figura 4.11. Funcin para el smbolo no terminal V.
int V (char *cadena, int i)
{
if (i<0) return i;
switch (cadena[i]) {
case *:
case /:
i++;
i = T (cadena, i);
i = X (cadena, i);
break;
case +:
case -:
i++;
i = E (cadena, i);
break;
}
return i;
}
Figura 4.12. Funcin para el smbolo no terminal X.
int X (char *cadena, int i)
{
if (i<0) return i;
switch (cadena[i]) {
case +:
case -:
i++;
i = E (cadena, i);
break;
}
return i;
}
04-CAPITULO 04 9/2/06 11:50 Pgina 108
Captulo 4. Anlisis sintctico 109
Figura 4.13. Funcin para el smbolo no terminal T.
int T (char *cadena, int i)
{
if (i<0) return i;
switch (cadena[i]) {
case i:
i++;
i = U (cadena, i);
break;
case (:
i++;
i = E (cadena, i);
i = C (cadena, i);
i = U (cadena, i);
break;
default: return -2;
}
return i;
}
Figura 4.14. Funcin para el smbolo no terminal U.
int U (char *cadena, int i)
{
if (i<0) return i;
switch (cadena[i]) {
case *:
case /:
i++;
i = T (cadena, i);
break;
}
return i;
}
04-CAPITULO 04 9/2/06 11:50 Pgina 109
110 Compiladores e intrpretes: teora y prctica
Figura 4.15. Funcin para el smbolo no terminal F.
int F (char *cadena, int i)
{
if (i<0) return i;
switch (cadena[i]) {
case i:
i++;
break;
case (:
i++;
i = E (cadena, i);
i = C (cadena, i);
break;
default: return -3;
}
return i;
}
Figura 4.16. Funcin para el smbolo no terminal C.
int C (char *cadena, int i)
{
if (i<0) return i;
switch (cadena[i]) {
case ):
i++;
break;
default: return -4;
}
return i;
}
Anlisis de cadenas
Para analizar una cadena x con este mtodo, basta invocar la funcin correspondiente al axioma
de la gramtica con los argumentos x (la cadena a analizar) y 0 (el valor inicial del contador). En
el ejemplo, la llamada sera E(x,0). Si el valor devuelto por la funcin coincide con la longi-
tud de la cadena de entrada, la cadena queda reconocida. En caso contrario, la funcin devolve-
r un nmero negativo, que indica el error detectado.
La Figura 4.17 muestra el anlisis de la palabra i+i*i. La llamada a la funcin correspon-
diente al axioma de la gramtica devuelve el valor 5, que coincide con la longitud de la cadena
de entrada, por lo que la palabra es reconocida.
04-CAPITULO 04 9/2/06 11:50 Pgina 110
Sin embargo, como ilustra la Figura 4.18, el anlisis de la palabra i+i* devuelve el valor 2,
lo que indica que no se reconoce la palabra, y que el error lo devolvi la llamada a la funcin co-
rrespondiente al smbolo no terminal T.
Captulo 4. Anlisis sintctico 111
Figura 4.17. Anlisis de la cadena i+i*i.
E (i+i*i, 0) = V (i+i*i, 1) =
= E (i+i*i, 2) =
= V (i+i*i, 3) =
= X (i+i*i, T (i+i*i, 4)) =
= X (i+i*i, U (i+i*i, 5)) =
= X (i+i*i, 5) =
= 5
Figura 4.18. Anlisis de la cadena i+i*.
E (i+i*, 0) = V (i+i*, 1) =
= E (i+i*, 2) =
= V (i+i*, 3) =
= X (i+i*, T (i+i*, 4)) =
= -2
4.2.4. Anlisis LL(1) mediante el uso de tablas de anlisis
Otra forma de realizar el anlisis descendente selectivo utiliza una tabla de anlisis, cuyas filas
son los smbolos no terminales de la gramtica y sus columnas son los smbolos terminales y el
smbolo de fin de cadena $. En las celdas de la tabla aparecen reglas de la gramtica. Las celdas
vacas corresponden a un error en el anlisis.
Las celdas de la tabla se rellenan aplicando el siguiente procedimiento para cada produccin
A ::= de la gramtica:
Para cada smbolo terminal a primero(), aadir la produccin A ::= en la celda
T[A,a].
Si primero(), para cada smbolo terminal b siguiente(A), aadir la pro-
duccin A ::= en la celda T[A,b]. Obsrvese que b tambin puede ser $.
Como ejemplo, considrese la gramtica del Ejemplo 4.1, que es la siguiente:
(1) E ::= TE
(2) E ::= +TE
(3) E ::=
(4) T ::= FT
(5) T ::= *FT
04-CAPITULO 04 9/2/06 11:50 Pgina 111
(6) T ::=
(7) F ::= (E)
(8) F ::= id
Los conjuntos primero y siguiente para los smbolos no terminales de esta gramtica son:
primero(E)=primero(T)=primero(F)={(,id}
primero(E)={+, }
primero(T)={*, }
siguiente(E)=siguiente(E)={),$}
siguiente(T)=siguiente(T)={+,),$}
siguiente(F)={*,+,),$}
La produccin E ::= TE debe colocarse en la fila correspondiente al smbolo E, en las co-
lumnas correspondientes a los smbolos terminales del conjunto primero(TE). Si se aplica
el procedimiento para calcular el conjunto primero de una forma sentencial, hay que calcular
el conjunto primero(T) = {(,id}.
La produccin E ::= +TE debe colocarse en la fila correspondiente al smbolo E y en las
columnas correspondientes a los smbolos terminales del conjunto primero(+TE). Si se apli-
ca el procedimiento para calcular el conjunto primero para una forma sentencial, hay que cal-
cular el conjunto primero(+) = {+}.
La produccin E ::= debe colocarse en la fila correspondiente al smbolo E y en las co-
lumnas correspondientes a los smbolos terminales del conjunto siguiente(E) = {),$}.
Por este procedimiento, se van rellenando las celdas de la tabla, obtenindose la que muestra
la Tabla 4.1.
Una gramtica es LL(1) si en la tabla de anlisis sintctico obtenida por el procedimiento an-
terior aparece como mximo una regla en cada celda.
112 Compiladores e intrpretes: teora y prctica
id + * ( ) $
E E::=TE E::=TE
E E::=+TE E::= E::=
T T::=FT T::=FT
T T::= T::= T::=
F F::=id F::=(E)
Tabla 4.1
Consideremos la gramtica siguiente:
P ::= iEtPP
P ::= a
Ejemplo
4.7
04-CAPITULO 04 9/2/06 11:50 Pgina 112
P::= eP
P::=
E ::= b
La Tabla 4.2 muestra la tabla de anlisis sintctico para esta gramtica. Puede observarse que
en la celda correspondiente a la interseccin de la fila P con la columna e aparecen dos reglas.
El motivo es que la interseccin del conjunto primero(P) = {e, } y siguiente(P)
= {$, e} no es el conjunto vaco, por lo que la regla- para el smbolo P debe colocarse en
una celda que ya est ocupada por la regla P::=eP.
Captulo 4. Anlisis sintctico 113
a b e i t $
P P::=a P::=iEtPP
P
P::=
P::=
P::=eP
E E::=b
Tabla 4.2
Anlisis de cadenas
La tabla de anlisis sintctico puede utilizarse para analizar cadenas mediante el siguiente algo-
ritmo:
1. Inicializar una pila con el smbolo $ y el axioma de la gramtica y aadir el smbolo $ al
final de la cadena de entrada.
2. Repetir el siguiente procedimiento: comparar el smbolo de la cima de la pila P con el si-
guiente smbolo de entrada e:
Si P y e son iguales al smbolo $, aceptar la cadena y salir.
Si son iguales y distintos del smbolo $, extraer un elemento de la pila y avanzar una
posicin en la cadena de entrada.
Si son distintos, y la celda de la tabla de anlisis T(P,e) est vaca, emitir mensaje
de error y salir.
Si son distintos, y en la celda de la tabla de anlisis T(P,e) aparece la produccin
P ::= X
1
X
2
... X
n
, extraer P de la pila e insertar los smbolos X
1
X
2
... X
n
en la pila
en el orden inverso a como aparecen en la parte derecha de la produccin.
Como ejemplo, la Tabla 4.3 muestra el anlisis de la cadena id+id utilizando la tabla de an-
lisis que aparece en la Tabla 4.1. En la Tabla 4.3 aparece el contenido de la pila en cada paso del
anlisis, as como el estado de la entrada y la accin a realizar. Veamos en detalle algunos de los
pasos del anlisis. En el primer paso, los smbolos E (smbolo de la cima de la pila) e id (si-
guiente smbolo de entrada) son distintos, por lo que se aplica la regla E::=TEque aparece en
la celda T(E,id). Como resultado, se extrae el smbolo E de la pila y se insertan los smbolos
E y T, que son los que aparecen en la parte derecha de esta regla, en sentido inverso.
04-CAPITULO 04 9/2/06 11:50 Pgina 113
En el cuarto paso del anlisis, el smbolo de la cima de la pila y el siguiente smbolo de entrada
son iguales, por lo que se extrae id de la pila y se avanza una posicin en la cadena de entrada.
Obsrvese que, como en el paso 5 del anlisis, cuando la regla a aplicar es una regla- se ex-
trae un smbolo de la pila y no se inserta ninguno.
Anlisis sintctico ascendente
4.3.1. Introduccin a las tcnicas del anlisis ascendente
En contraposicin a las tcnicas del anlisis sintctico descendente, los analizadores ascendentes re-
corren el rbol de derivacin de una cadena de entrada correcta desde las hojas (los smbolos ter-
minales) a la raz (el axioma), en una direccin grficamente ascendente, lo que da nombre a estas
tcnicas. El anlisis consiste en un proceso iterativo, que se aplica inicialmente a la cadena com-
pleta que se va a analizar y termina, bien cuando se completa el anlisis con xito, o bien cuando
ste no puede continuar, debido a algn error sintctico. En cada paso del anlisis se intenta dedu-
cir qu regla de la gramtica se tiene que utilizar en ese punto del rbol, teniendo en cuenta el esta-
do de ste y la posicin a la que se ha llegado en la cadena de entrada. En general, al final de cada
paso del anlisis se ha modificando la cadena de entrada, que queda preparada para continuar.
4.3
114 Compiladores e intrpretes: teora y prctica
Paso Pila Entrada Accin
1 $E id+id$ Aplicar E::=TE
2 $ET id+id$ Aplicar T::=FT
3 $ETF id+id$ Aplicar F::=id
4 $ETid id+id$ Avanzar
5 $ET +id$ Aplicar T::=
6 $E +id$ Aplicar E::=+TE
7 $ET+ +id$ Avanzar
8 $ET id$ Aplicar T::=FT
9 $ETF id$ Aplicar F::=id
10 $ETid id$ Avanzar
11 $ET $ Aplicar T::=
12 $E $ Aplicar E::=
13 $ $ Cadena aceptada
Tabla 4.3. Anlisis de la cadena id+id.
04-CAPITULO 04 9/2/06 11:50 Pgina 114
En este tipo de anlisis se pueden realizar dos operaciones fundamentales: la reduccin y el
desplazamiento.
Reduccin
Se aplica cuando se ha identificado en la cadena de entrada la parte derecha de alguna de las re-
glas de la gramtica. Esta operacin consiste en reemplazar, en la cadena de entrada, dicha parte
derecha por el smbolo no terminal de la regla correspondiente. La Figura 4.19 muestra grfica-
mente cmo se realiza esta operacin.
Captulo 4. Anlisis sintctico 115
A
A
t
1
t
j
t
1
t
j
1
i-1
i
n
Figura 4.19. Representacin grfica de la reduccin de la regla A
i
n
.
Desplazamiento
Intuitivamente, los analizadores ascendentes guardan informacin que les permite saber, en cada
momento, qu partes derechas de las reglas de la gramtica son compatibles con la porcin de la
cadena de entrada analizada, entre todas las reglas posibles. Como, en general, las partes dere-
chas de las reglas tienen ms de un smbolo, en cada paso del anlisis no siempre se puede re-
ducir una regla. Se llama desplazamiento la operacin mediante la cual se avanza un smbolo,
simultneamente en la cadena de entrada y en todas las reglas que siguen siendo compatibles con
la porcin de entrada analizada.
La Figura 4.20 muestra grficamente un ejemplo de esta operacin. La parte inferior de la
figura muestra la cadena de entrada. La superior, un subconjunto de reglas de la gramtica.
El bloque izquierdo muestra la situacin anterior al desplazamiento: la cadena de entrada ha
sido analizada hasta el terminal t
j
. El subconjunto de reglas contiene aquellas cuyas partes
derechas han sido compatibles con la parte de la cadena de entrada analizada, hasta el sm-
bolo t
j
inclusive. El bloque derecho muestra la situacin despus del desplazamiento. Slo
se resaltan las reglas que siguen siendo compatibles con el siguiente smbolo de entrada
(t
j+1
).
El algoritmo bsico del anlisis ascendente, que se explicar con detalle en las prximas sec-
ciones, puede describirse de la siguiente manera en funcin de las operaciones de reduccin y
desplazamiento:
Se inicia el proceso con el primer smbolo de la cadena de entrada.
04-CAPITULO 04 9/2/06 11:50 Pgina 115
Se realiza el siguiente paso del anlisis, hasta que se determine que la cadena es sintctica-
mente correcta (si se ha recorrido la entrada completa, reducindola al axioma) o incorrec-
ta (si en algn instante el anlisis no puede continuar).
1. Si una regla se puede reducir (toda su parte derecha se ha desplazado) se reduce. La
nueva cadena de anlisis es el resultado de reemplazar la parte de la cadena correspon-
diente a la parte derecha de la regla por el smbolo no terminal situado a la izquierda
de la misma. El anlisis continuar a partir de dicho smbolo no terminal.
2. En otro caso, se realiza un desplazamiento sobre el smbolo correspondiente al paso de
anlisis actual. Esto significa que se descartan las reglas cuyo smbolo siguiente, en la
parte derecha, no sea compatible con el desplazado, mientras se avanza en las partes de-
rechas en las que sea posible y en la cadena de entrada.
4.3.2. Algoritmo general para el anlisis ascendente
La mayora de los algoritmos de anlisis sintctico de tipo ascendente se realizan en dos fases:
en la primera, se construye una tabla auxiliar para el anlisis sintctico ascendente, que en el se-
gundo paso se utilizar en el anlisis de las cadenas de entrada.
Como se ha indicado previamente, los analizadores de los lenguajes independientes del con-
texto pueden basarse en el autmata a pila asociado a su gramtica. Hay dos diferencias princi-
pales entre los analizadores ascendentes: la manera en que construyen la tabla de anlisis y la
informacin que necesitan introducir en la pila. En este ltimo aspecto, las tcnicas ms poten-
tes, LR(1) y LALR(1), necesitan ms informacin que las ms simples, LR(0) y SLR(1). Para
simplificar, en este captulo se utilizar el mismo algoritmo para todos ellos, aunque sea a costa
de introducir en la pila informacin redundante para las tcnicas LR(0) y SLR(1).
Estructura general de las tablas de anlisis ascendente
Las tablas del anlisis contienen una informacin equivalente a la funcin de transicin del au-
tmata a pila que reconocera el lenguaje asociado a la gramtica que se est analizando. Dentro
116 Compiladores e intrpretes: teora y prctica
N
h
. . . t
j
t
k
N . . .
N
v
. . . t
j
t
j+1
N . . .
N
b
. . . t
j
t
j+1
N . . .
N
g
. . . t
j
t
1
N . . .
N
t
. . . t
j
t
m
N . . .
N
y
. . . t
j
t
j+1
N . . .
N
q
. . . t
j
t
p
N . . .
N
h
. . . t
j
t
k
N . . .
N
v
. . . t
j
t
j+1
N . . .
N
b
. . . t
j
t
j+1
N . . .
N
g
. . . t
j
t
1
N . . .
N
t
. . . t
j
t
m
N . . .
N
y
. . . t
j
t
j+1
N . . .
N
q
. . . t
j
t
p
N . . .
. . . t
j
t
j+1
t
j+2
t
j+3
t
j+4
t
j+5
t
j+6
t
j+7
t
j
, . . . . . . t
j
t
j+1
t
j+2
t
j+3
t
j+4
t
j+5
t
j+6
t
j+7
t
j
, . . .
Figura 4.20. Representacin grfica de la operacin de desplazamiento.
04-CAPITULO 04 9/2/06 11:50 Pgina 116
de este autmata a pila se puede distinguir la parte que tiene por objeto reconocer los asideros de
la gramtica, que en realidad es un autmata finito. A lo largo de todo el captulo se utilizar el
nombre autmata de anlisis para referirse a dicho autmata.
El nmero de filas vara en funcin de la tcnica utilizada para construir la tabla y coinci-
de con el nmero de estados del autmata. Cada tcnica puede construir un autmata dis-
tinto, con diferentes estados.
Tendrn tantas columnas como smbolos hay en el alfabeto de la gramtica. Usualmente, la ta-
bla se divide en dos secciones, que corresponden, respectivamente, a los smbolos terminales
y no terminales. Las columnas de la tabla asociadas a los smbolos terminales forman el blo-
que de accin, ya que son ellas las que determinan la accin siguiente que el analizador debe
realizar. Las restantes columnas de la tabla (las columnas asociadas a los smbolos no termi-
nales) slo conservan informacin relacionada con las transiciones del autmata y forman el
bloque ir_a. Sus casillas slo contienen la identificacin del estado al que hay que transitar.
Para poder utilizarla en el anlisis, la tabla debe especificar, en funcin del smbolo termi-
nal de la cadena de entrada que se recibe y del estado en que se encuentra el autmata, cul
ser el estado siguiente; qu modificaciones deben realizarse en la cadena de entrada y en
la pila; si se produce un desplazamiento o una reduccin, si se ha terminado con xito el
anlisis o si se ha detectado algn error. Para especificar esta informacin, a lo largo de este
captulo se utilizar la siguiente notacin:
1. d<e>, donde d significa desplazamiento y <e> identifica un estado del autmata de
anlisis. Representa la accin de desplazar el smbolo actual y pasar al estado <e>.
2. r<p>, donde <p> identifica una regla de produccin de la gramtica. Representa la ac-
cin de reduccin de la regla <p>.
3. Aceptar, que representa la finalizacin con xito del anlisis.
4. Error, que representa la finalizacin sin xito del anlisis.
La Figura 4.21 muestra grficamente la estructura de estas tablas.
Captulo 4. Anlisis sintctico 117
E
Accin Ir_a
s
0
s
k
...
T
1
T
n
N
1
N
m
T
N
...
... ...
Figura 4.21. Estructura de las tablas de anlisis de los analizadores sintcticos ascendentes.
04-CAPITULO 04 9/2/06 11:50 Pgina 117
Algoritmo de anlisis ascendente
La especificacin completa del algoritmo general de anlisis ascendente describe las manipula-
ciones realizadas sobre la tabla, la entrada y la pila, asociadas con la accin correspondiente a
cada paso del anlisis. La Figura 4.22 muestra grficamente el esquema del analizador. Tanto el
significado de las columnas de accin como de las de ir_a, as como el contenido de la pila, son
objeto de prximas secciones.
118 Compiladores e intrpretes: teora y prctica
E
Accin Ir_a
s
0
s
k
...
T
1
T
n
N
1
N
m
T
N
...
... ...
TABLA ANLISIS
cima
ENTRADA PILA
SALIDA
a
1
a
2
a
u
$
...
s
m
X
m
s
m-1
X
m-1
s
0
...
Figura 4.22. Estructura de un analizador sintctico ascendente.
1
Ms adelante se ver que la introduccin del smbolo no terminal E, el smbolo terminal $ y la regla E::=E$
son pasos generales del algoritmo de anlisis.
La Figura 4.23 muestra el algoritmo general de anlisis en pseudocdigo.
Puede observarse que el algoritmo es un bucle en el que se consulta la tabla del anlisis para
descubrir la accin que hay que realizar. A continuacin se estudia con detalle el tratamiento de
cada tipo de operacin. Se utilizar como ejemplo la cadena i+i+i y la siguiente gramtica:
{
T
={+,i,(,),$
1
},
N
={E
1
,E,T},
E,
{ E ::= E$
1
,
E ::= E+T | T,
T ::= i | (E)} }
04-CAPITULO 04 9/2/06 11:50 Pgina 118
Es fcil comprobar que el lenguaje asociado est compuesto por expresiones aritmticas for-
madas por sumas entre parntesis opcionales, y el smbolo i como nico operando.
Inicio del anlisis
Se supone que se dispone de la tabla de anlisis que se muestra en la Figura 4.24.
Captulo 4. Anlisis sintctico 119
Figura 4.23. Pseudocdigo del algoritmo general de anlisis ascendente.
Estado AnalizadorAscendente(tabla_anlisis,
entrada, pila, gramtica)
/* La entrada contiene la cadena w$ que se quiere analizar se
deja vaca la posicin 0 para las reducciones */
{
puntero smbolo_actual=1;
/* la posicin 0 est vaca por si fuese necesario al
reducir */
estado estado_actual;
push(pila,0);
while( verdadero ) /* Bucle sin fin */
{ estado_actual=cima(pila);
if (tabla_anlisis[estado_actual, entrada[smbolo_actual]]
== ds ) {
push(pila, entrada[smbolo_actual]);
push(pila, s);
smbolo_actual++;}
else
if ( tabla_anlisis[estado_actual, entrada[smbolo_ac-
tual]]
== rj ) {
/* Podemos suponer que la regla j es A::= */
`realizar 2*longitud() pop(pila)
entrada[--smbolo_actual] = A;
printf(Reduccin de A::=);)
else
if ( tabla_anlisis[estado_actual, entrada[smbolo_ac-
tual]]
== aceptar )
return CADENA_ACEPTADA;
else /* casilla vaca */
return CADENA_RECHAZADA:_ERROR_SINTCTICO);
}}
04-CAPITULO 04 9/2/06 11:50 Pgina 119
Se aade a la entrada el smbolo $, que indica que se ha llegado al final de la cadena. La pila
contendr inicialmente el estado inicial del autmata, que en este captulo ser el estado etique-
tado con el nmero 0. La Figura 4.25 muestra el paso inicial, realizado con la gramtica del
ejemplo.
Es importante resaltar que el algoritmo descrito utiliza la pila para conservar toda la informa-
cin necesaria para continuar el anlisis. Para ello, a excepcin de esta situacin inicial, en la que
slo se introduce un estado, la informacin mnima que se inserte o se saque de la pila ser usual-
mente un par de datos: el estado del analizador y el smbolo de la gramtica considerado en ese
instante. Esto permite utilizar el mismo algoritmo de anlisis en todas las tcnicas, aunque para
alguna de ellas bastara con introducir el estado.
Desplazamiento d<e>
Como se ha indicado, las filas de la tabla representan los estados del autmata asociado a la gra-
mtica y, para poder utilizarla en el anlisis, tienen que conservar simultneamente informacin
sobre todas las reglas cuyas partes derechas son compatibles con la parte de la cadena de entra-
da que ya ha sido analizada.
120 Compiladores e intrpretes: teora y prctica
Accin Ir_a
E i
T
N
+
( ) $ E T
0
1
2
3
4
5
6
7
8
d1
d1
d1
(*)
d3 d3 d3
d2
d2
d2 d2 d2
d6
d6
d2
d8
acc
r1 r1 r1
r4 r4 r4
7
5
4 3
3
Figura 4.24. Una tabla de anlisis correspondiente a la gramtica
{
T
={+,i,(,),$ },
N
={E,E,T}, E, {EE$,EE+T|T,Ti|(E)}}.
(*) Se considera que todas las casillas
vacas representan la accin de error.
04-CAPITULO 04 9/2/06 11:50 Pgina 120
La indicacin de desplazamiento significa que el smbolo actualmente estudiado en la cadena
de entrada es uno de los que espera alguna de las reglas con partes derechas parcialmente anali-
zadas. Por lo tanto, se puede avanzar una posicin en la cadena de entrada, y el autmata puede
pasar al estado que corresponde al desplazamiento de ese smbolo en las partes derechas de las
reglas en las que dicho desplazamiento sea posible. Esta operacin implicar realizar las si-
guientes operaciones:
Se introduce en la pila el smbolo de entrada.
Para tener en cuenta el cambio de estado del autmata, el estado indicado por la operacin
(<e>) tambin se introduce en la pila.
Se avanza una posicin en la cadena de entrada, de manera que el smbolo actual pase a ser
el siguiente al recin analizado.
Captulo 4. Anlisis sintctico 121
Accin Ir_a
E i
T
N
+
( ) $ E T
0
1
2
3
4
5
6
7
8
d1
d1
d1
r3 r3 r3
d2
d2
r2 r2 r2
d6
d6
d2
d8
acc
r1 r1 r1
r4 r4 r4
7
5
4 3
3
0
i
+
i + i $
(0)E E$
(1)E E+T
(2)T
(3)T i
(4)(E)
Figura 4.25. Situacin inicial para el anlisis de la cadena i+i+i.
04-CAPITULO 04 9/2/06 11:50 Pgina 121
En el ejemplo se puede comprobar que la primera accin de anlisis en el tratamiento de la
cadena i+i+i es un desplazamiento. El analizador est en el estado 0 y el prximo smbolo que
hay que analizar es i. La casilla (0,i) de la tabla del anlisis contiene la indicacin d1, es decir,
desplazamiento al estado 1. Por lo tanto, hay que introducir en la pila el smbolo i y el nme-
ro 1. La Figura 4.26 muestra grficamente este paso del anlisis.
Reduccin r<p>
La indicacin de reduccin en una casilla de la tabla significa que, teniendo en cuenta el smbo-
lo actual de la cadena de entrada, alguna de las reglas representadas en el estado actual del aut-
mata ha desplazado su parte derecha completa, que puede ser sustituida por el smbolo no
terminal de su parte izquierda. Como resultado de esta accin, el analizador debe actuar de la si-
guiente forma:
Se saca de la pila la informacin asociada a la parte derecha de la regla <p>. Supongamos
que la regla <p> es N::=. En la pila hay dos datos por cada smbolo, por lo que tendrn
que realizarse tantas operaciones pop como el doble de la longitud de .
122 Compiladores e intrpretes: teora y prctica
Accin Ir_a
E i
T
N
+ ( ) $ E T
0
1
2
3
4
5
6
7
8
d1
d1
d1
r3 r3 r3
d2
d2
r2 r2 r2
d6
d6
d2
d8
acc
r1 r1 r1
r4 r4 r4
7
5
4 3
3
0
i + i + i $
Accin Ir_a
E i
T
N
+ ( ) $ E T
0
1
2
3
4
5
6
7
8
d1
d1
d1
r3 r3 r3
d2
d2
r2 r2 r2
d6
d6
d2
d8
acc
r1 r1 r1
r4 r4 r4
7
5
4 3
3
1
i + i + i $
0 i
Figura 4.26. Ejemplo de operacin de desplazamiento en el anlisis de la cadena i+i+i.
04-CAPITULO 04 9/2/06 11:50 Pgina 122
Se introduce el smbolo no terminal de la regla (N) a la izquierda de la posicin actual de la
cadena de entrada, y se apunta a dicho smbolo.
Es fcil comprobar en el ejemplo que la segunda accin, tras el desplazamiento anterior, es
una reduccin, ya que la casilla correspondiente al smbolo actual (+) y al estado que ocupa la
cima de la pila (1), indica que debe reducirse la regla 3: T ::= i.Como slo hay un smbolo
en la parte derecha, hay que ejecutar dos pop sobre la pila e insertar a la izquierda de la por-
cin de la cadena de entrada pendiente de analizar la parte izquierda de la regla (T), que pasa
a ser el smbolo actual. El estado que queda en la pila es 0. La casilla (0, T) de la tabla de an-
lisis indica que se tiene que ir al estado 3. La Figura 4.27 muestra grficamente este paso del
anlisis.
La Figura 4.28 ilustra otra reduccin cuya regla asociada tiene una parte derecha de longi-
tud mayor que 1. Se trata de la regla 1: E ::= E+T. En este caso habr que ejecutar 6 (2*3)
operaciones pop en la pila. Tras realizarlas, la cima de la pila contiene el estado 0. Se aade
en la posicin correspondiente de la cadena de entrada la parte izquierda (E) y se contina el
anlisis.
Captulo 4. Anlisis sintctico 123
Accin Ir_a
E i
T
N
+ ( ) $ E T
0
1
2
3
4
5
6
7
8
d1
d1
d1
r3 r3 r3
d2
d2
r2 r2 r2
d6
d6
d2
d8
acc
r1 r1 r1
r4 r4 r4
7
5
4 3
3
0
T i
+
i $
Accin Ir_a
E i
T
N
+ ( ) $ E T
0
1
2
3
4
5
6
7
8
d1
d1
d1
r3 r3 r3
d2
d2
r2 r2 r2
d6
d6
d2
d8
acc
r1 r1 r1
r4 r4 r4
7
5
4 3
3
3
i + i + i $
0 T
i
+
Figura 4.27. Ejemplo de operacin de reduccin. La casilla de la tabla (3,+) contiene
la indicacin r3.
04-CAPITULO 04 9/2/06 11:50 Pgina 123
124 Compiladores e intrpretes: teora y prctica
Accin Ir_a
E i
T
N
+ ( ) $ E T
0
1
2
3
4
5
6
7
8
d1
d1
d1
r3 r3 r3
d2
d2
r2 r2 r2
d6
d6
d2
d8
acc
r1 r1 r1
r4 r4 r4
7
5
4 3
3
6
E + T + i $
4 0 + E
Accin Ir_a
E i
T
N
+ ( ) $ E T
0
1
2
3
4
5
6
7
8
d1
d1
d1
r3 r3 r3
d2
d2
r2 r2 r2
d6
d6
d2
d8
acc
r1 r1 r1
r4 r4 r4
7
5
4 3
3
7
E + T + i $
6 T + 4 E 0
Accin Ir_a
E i
T
N
+ ( ) $ E T
0
1
2
3
4
5
6
7
8
d1
d1
d1
r3 r3 r3
d2
d2
r2 r2 r2
d6
d6
d2
d8
acc
r1 r1 r1
r4 r4 r4
7
5
4 3
3
1
E + i + i $
6 i
+
4 E 0
Figura 4.28. Reduccin de la regla 1 en un estado intermedio del anlisis de la cadena
i+i+i.
04-CAPITULO 04 9/2/06 11:50 Pgina 124
Aceptacin
Cuando en la ejecucin del algoritmo se llega a una casilla que contiene esta indicacin, el an-
lisis termina y se concluye que la cadena de entrada es sintcticamente correcta. La Figura 4.29
muestra el final del anlisis de la cadena i+i+i en el caso del ejemplo de los puntos anteriores.
Obsrvese el papel del smbolo $, que se aadi precisamente para facilitar la identificacin de
esta circunstancia.
Captulo 4. Anlisis sintctico 125
Accin Ir_a
E i
T
N
+
( ) $ E T
0
1
2
3
4
5
6
7
8
d1
d1
d1
r3 r3 r3
d2
d2
r2 r2 r2
d6
d6
d2
d8
acc
r1 r1 r1
r4 r4 r4
7
5
4 3
3
4
E + E + E $
0 E
Figura 4.29. Fin de anlisis: la cadena i+i+i es correcta.
Error
Cuando la ejecucin del algoritmo llega a una casilla con esta indicacin, el anlisis termina y
se concluye que la cadena de entrada contiene errores sintcticos. Las tablas del anlisis suelen
contener ms casillas con esta indicacin que con cualquiera de las anteriores. Es frecuente de-
jar estas casillas en blanco para facilitar el manejo de la tabla. La Figura 4.30 muestra un ejem-
plo de anlisis de la cadena (+i) con la gramtica
04-CAPITULO 04 9/2/06 11:50 Pgina 125
126 Compiladores e intrpretes: teora y prctica
E i
T N
+ E T
0
1
2
3
4
5
6
7
8
d5
r2
d5
d6
d4
d7
r4 r4
d5
r6
d4
d4
d6 d11
5
4
(
+ i
)
$
0 (
Accin Ir_a
9
10
11
E T F ( ) $
*
acc
r2 r2
r4 r4
9
1 2 3
d4 8 2 3
3
10 d5
r6 r6 r6
r1 d7 r1 r1
r3 r3 r3 r3
r5 r5 r5 r5
E i
T N
+ E T
0
1
2
3
4
5
6
7
8
d5
r2
d5
d6
d4
d7
r4 r4
d5
r6
d4
d4
d6 d11
5
4
(
+ i
)
$
0 (
Accin Ir_a
9
10
11
E T F ( ) $
*
acc
r2 r2
r4 r4
9
1 2 3
d4 8 2 3
3
10 d5
r6 r6 r6
r1 d7 r1 r1
r3 r3 r3 r3
r5 r5 r5 r5
c) d)
E i
T N
+ E T
0
1
2
3
4
5
6
7
8
d5
r2
d5
d6
d4
d7
r4
r4
d5
r6
d4
d4
d6 d11
5
(
+ i
)
$
Accin Ir_a
9
10
11
E T F ( ) $
*
acc
r2 r2
r4 r4
9
1 2 3
d4 8 2 3
3
10 d5
r6 r6 r6
r1 d7 r1 r1
r3 r3 r3 r3
r5 r5 r5 r5
E i
T N
+ E T
0
1
2
3
4
5
6
7
8
d5
r2
d5
d6
d4
d7
r4 r4
d5
r6
d4
d4
d6 d11
5
(
+ i
)
$
Accin Ir_a
9
10
11
E T F ( ) $
*
acc
r2 r2
r4 r4
9
1 2 3
d4 8 2 3
3
10 d5
r6 r6 r6
r1 d7 r1 r1
r3 r3 r3 r3
r5 r5 r5 r5
a) b)
Figura 4.30. Ejemplo de anlisis que termina con error sintctico.
04-CAPITULO 04 9/2/06 11:50 Pgina 126
{
T
={+,*,i,(,)},
N
={E,T,F},
E,
{ E ::= E+T | T,
T ::= T*F | F,
F ::= (E) | i} }
y que termina con error sintctico. Es fcil comprobar que esta gramtica genera expresiones arit-
mticas con los operadores binarios + y * y con el smbolo i como nico operando. Las ex-
presiones permiten el uso opcional de parntesis.
Obsrvese:
(a) y (b) Muestran respectivamente la situacin previa e inicial al anlisis.
(c) La casilla (0,() contiene la operacin d4, por lo que se inserta en la pila el smbolo ( y
el estado 4 y se avanza una posicin en la cadena de entrada; el smbolo actual es +.
(d) La casilla (4,+) est vaca, es decir, indica que se ha producido un error sintctico. El error es
que se ha utilizado el smbolo + como operador mondico cuando la gramtica lo considera binario.
4.3.3. Anlisis LR(0)
Las siglas LR describen una familia de analizadores sintcticos que
Examinan la entrada de izquierda a derecha (del ingls Left to right).
Construyen una derivacin derecha de la cadena analizada (del ingls Rightmost deriva-
tion).
Las siglas LR(k) hacen referencia a que, para realizar el anlisis, se utilizan los k smbolos si-
guientes de la cadena de entrada a partir del actual. Por lo tanto, LR(0) es el analizador de esa
familia que realiza cada paso de anlisis teniendo en cuenta nicamente el smbolo actual.
Configuracin o elemento de anlisis LR(0)
Como se ha indicado anteriormente, los analizadores ascendentes siguen simultneamente todos
los caminos posibles; es decir, cada estado del autmata de anlisis conserva todas las reglas cu-
yas partes derechas son compatibles con la porcin de entrada ya analizada. Cada una de esas re-
glas es hipottica, pues al final slo una de ellas ser la aplicada.
En los apartados siguientes se estudiar con detalle la construccin del autmata de anlisis
LR(0). Para ello, es necesario explicitar en un objeto concreto cada una de las hiptesis mencio-
nadas. Informalmente, se llamar configuracin o elemento de anlisis a la representacin de
cada una de las hiptesis.
Una configuracin o elemento de anlisis LR(0) es una regla, junto con una indicacin res-
pecto al punto de su parte derecha hasta el que el analizador ha identificado que dicha regla es
compatible con la porcin de entrada analizada.
Captulo 4. Anlisis sintctico 127
04-CAPITULO 04 9/2/06 11:50 Pgina 127
Es posible utilizar diversas notaciones para las configuraciones LR(0). En este captulo men-
cionaremos las dos siguientes:
Notacin explcita: Consiste en escribir la regla completa y marcar con un smbolo espe-
cial la posicin de la parte derecha hasta la que se ha analizado. Suele utilizarse el smbo-
lo o el smbolo _, colocndolo entre la subcadena ya procesada de la parte derecha y
la pendiente de proceso. A lo largo de este captulo se utilizar el nombre apuntador de
anlisis para identificar este smbolo. A continuacin se muestran algunos ejemplos:
E ::= E+T
Indica que es posible que se utilice esta regla en el anlisis de la cadena de entrada, aun-
que, por el momento, el analizador an no ha procesado informacin suficiente para avan-
zar ningn smbolo en la parte derecha de la regla. Para que finalmente sea sta la regla
utilizada, ser necesario reducir parte de la cadena de entrada al smbolo no terminal E, en-
contrar a continuacin el terminal +, y reducir posteriormente otra parte de la cadena de
entrada al smbolo no terminal T.
E ::= E+T
Indica que es posible que en el anlisis de la cadena de entrada se vaya a utilizar esta re-
gla, y que el analizador ya ha podido reducir parte de la cadena de entrada al smbolo no
terminal E, encontrando a continuacin el smbolo terminal +. Antes de utilizar esta re-
gla, ser necesario reducir parte de la cadena de entrada al smbolo no terminal T.
E ::= E+T
Indica que el analizador ya ha comprobado que toda la parte derecha de la regla es com-
patible con la parte de la cadena de entrada ya analizada. En este momento se podra re-
ducir toda la parte de la cadena de entrada asociada a E+T y sustituirla por el smbolo no
terminal E (la parte izquierda de la regla). Las configuraciones de este tipo se denominan
configuraciones de reduccin. Todas las configuraciones que no son de reduccin son
configuraciones de desplazamiento.
P ::=
Esta configuracin indica que es posible reducir la regla- P ::= . Siempre que se lle-
gue a una configuracin asociada a una regla lambda, ser posible reducirla. Se trata, por
tanto, de una configuracin de reduccin.
Esta notacin es ms farragosa y menos adecuada para programar los algoritmos, pero re-
sulta ms legible, por lo que ser utilizada a lo largo de este captulo.
Notacin de pares numricos: Tambin se puede identificar una configuracin con un
par de nmeros. El primero es el nmero de orden de la regla de que se trate en el con-
junto de las reglas de produccin. El segundo indica la posicin alcanzada en la parte de-
recha de la regla, utilizando el 0 para la posicin anterior al primer smbolo de la
izquierda, e incrementando en 1 a medida que se avanza hacia la derecha. Esta notacin
es equivalente a la anterior y facilita la programacin de los algoritmos, pero resulta me-
nos legible.
128 Compiladores e intrpretes: teora y prctica
04-CAPITULO 04 9/2/06 11:50 Pgina 128
A continuacin se muestran las correspondencias entre ambas notaciones en los ejemplos an-
teriores, suponiendo que la regla E ::= E+T es la nmero 3 y la regla P ::= es la nmero 4.
E ::= E+T (3,0)
E ::= E+T (3,2)
E ::= E+T (3,3)
P ::= (4,0)
Puesto que el analizador sintctico LR(0) se basa en el autmata de anlisis LR(0), ser objetivo
de los siguientes apartados describir detalladamente su construccin. Con este ejemplo se justi-
ficarn intuitivamente los pasos necesarios, que luego se formalizarn en un algoritmo. Se utili-
zar como ejemplo la gramtica que contiene las siguientes reglas de produccin (el axioma es
E; obsrvese que la primera regla ya incorpora el smbolo de fin de cadena).
(0) E ::= E$
(1) E ::= E+T
(2) | T
(3) T ::= i
(4) |(E)
Estado inicial del autmata. El estado inicial contiene las configuraciones asociadas con
las hiptesis previas al anlisis; el apuntador de anlisis estar situado delante del primer
smbolo de la cadena de entrada, y se trata de reducir toda la cadena al axioma. La hipte-
sis inicial tiene que estar relacionada con la regla del axioma. A lo largo del captulo se
ver cmo se puede asegurar que el axioma slo tenga una regla. Esta hiptesis tiene que
procesar la parte derecha completa de esa regla completa, por lo que el estado inicial tie-
ne que contener la siguiente configuracin:
E ::= E$
Esta configuracin representa la hiptesis de que se puede reducir toda la cadena de en-
trada al smbolo no terminal E, ya que a continuacin slo hay que encontrar el smbolo
terminal que indica el fin de la cadena. Un smbolo no terminal nunca podr encontrarse
en la cadena de entrada original, pues slo aparecer como resultado de alguna reduccin.
Por ello, la hiptesis representada por una configuracin que espere encontrar a conti-
nuacin un smbolo no terminal obligar a mantener simultneamente todas las hiptesis
que esperen encontrar a continuacin cualquiera de las partes derechas de las reglas de di-
cho smbolo no terminal, ya que la reduccin de cualquiera de ellas significara la apari-
cin esperada del smbolo no terminal. La Figura 4.31 muestra grficamente esta
circunstancia.
A lo largo de la construccin del autmata aparecern muchas configuraciones que indi-
quen que el analizador est situado justo delante de un smbolo no terminal. La reflexin
anterior se podr aplicar a todas esas situaciones y se implementar en la operacin cierre,
que se aplica a conjuntos de configuraciones y produce conjuntos de configuraciones. Por
lo tanto, el estado inicial del autmata debe contener todas las hiptesis equivalentes a la
Ejemplo
4.8
Captulo 4. Anlisis sintctico 129
04-CAPITULO 04 9/2/06 11:50 Pgina 129
inicial: hay que aadir, por tanto, todas las que tengan el indicador del analizador delante
de las partes derechas de las reglas del smbolo no terminal E.
E ::= E+T
E ::= T
Por las mismas razones que antes, habr que realizar el cierre de estas dos configuracio-
nes. Para la primera, no es preciso aadir al estado inicial ninguna configuracin nueva, ya
que el apuntador de anlisis precede al mismo smbolo no terminal que acabamos de con-
siderar, y las configuraciones correspondientes a su cierre ya han sido aadidas. Para la se-
gunda, habra que aadir las siguientes configuraciones:
T ::= i
T ::= (E)
Estas dos configuraciones tienen en comn que el analizador espera encontrar a continua-
cin smbolos terminales (i y (). Para ello sera necesario localizarlos en la cadena de
entrada, pero eso slo ocurrir en pasos futuros del anlisis.
No queda nada pendiente en la situacin inicial, por lo que, si llamamos s
0
al estado ini-
cial, se puede afirmar que:
130 Compiladores e intrpretes: teora y prctica
T
$
E
E
E
(c)
+
(d)
E
E
$
T
E
$
E
(b)
E
$
(a)
Figura 4.31. Ejemplo de cierre de configuracin cuando el anlisis precede un smbolo no
terminal. (a) Situacin previa al anlisis. (b) E::=E$ (c) E::=E+T (d) E::=T.
04-CAPITULO 04 9/2/06 11:50 Pgina 130
s
0
={E ::= E$,
E ::= E+T,
E ::= T,
T ::= i,
T ::= (E)}
Completada esta reflexin, se puede describir con ms precisin cul es el contenido del
estado inicial de los autmatas de anlisis LR(0). Se ha mencionado anteriormente que
siempre se podr suponer que hay slo una regla cuya parte izquierda es el axioma y cuya
parte derecha termina con el smbolo de final de cadena $. Si esa regla es A ::= $, el
estado inicial contendr el conjunto de configuraciones resultado del cierre del conjunto de
configuraciones {A ::= $}.
Justificacin intuitiva de la operacin de ir de un conjunto de configuraciones a otro
mediante un smbolo. El analizador tiene que consultar los smbolos terminales de la ca-
dena de entrada. En una situacin intermedia de anlisis, tras alguna reduccin, tambin es
posible encontrar como siguiente smbolo pendiente de analizar uno no terminal. Una vez
estudiada la situacin inicial, se puede continuar la construccin del autmata del analiza-
dor, aadiendo las transiciones posibles desde el estado inicial.
La primera conclusin es que hay transiciones posibles, tanto ante smbolos terminales,
como ante no terminales. Cmo se comportara el analizador si, a partir del estado s
0
, en-
cuentra en la cadena de entrada un smbolo terminal distinto de i y de ( o algn smbo-
lo no terminal distinto de E o de T? Ya que no hay ninguna configuracin que espere ese
smbolo, se concluira que la cadena de entrada no puede ser generada por la gramtica es-
tudiada, es decir, que contiene un error sintctico. Por lo tanto, todas las transiciones des-
de el estado inicial con smbolos distintos de i, (, E o T conducirn a un estado de error.
En teora de autmatas, es habitual omitir los estados errneos al definir las transiciones
de un autmata, de forma que las transiciones que no se han especificado para algn sm-
bolo son consideradas como errneas.
La segunda conclusin es que, en el autmata del analizador, habr tantas transiciones a
partir de un estado que conduzcan a estados no errneos, como smbolos sigan al apunta-
dor de anlisis en alguna de las configuraciones del estado de partida. Esto significa que
desde el estado inicial slo se podr ir a otros estados mediante los smbolos terminales i
o ( o mediante los no terminales E o T.
En la situacin inicial, cuando en la cadena de entrada se encuentra el smbolo E, slo las
dos primeras configuraciones (E ::= E$ y E ::= E+T) representan hiptesis que si-
guen siendo compatibles con la entrada. En esta situacin, se puede desplazar un smbolo
hacia la derecha, tanto en la cadena de entrada como en estas configuraciones. Si se utili-
za el smbolo s
1
para identificar el nuevo estado, tienen que pertenecer a l las configura-
ciones que resultan de este desplazamiento:
E ::= E$
E ::= E+T
Captulo 4. Anlisis sintctico 131
04-CAPITULO 04 9/2/06 11:50 Pgina 131
Para completar el estado resultante de la operacin ir a, hay que aplicar la operacin cie-
rre a las nuevas configuraciones, del mismo modo que se vio antes. En este caso, dado que
el apuntador de anlisis precede slo a smbolos terminales, no se tiene que aadir ningu-
na otra configuracin. Por lo tanto, se puede afirmar que:
s
1
={E ::= E$,
E ::= E+T}
La Figura 4.32 muestra los dos estados calculados hasta este momento, y la transicin que
puede realizarse entre ellos.
132 Compiladores e intrpretes: teora y prctica
s
0
E::= E+T,
s
1
E
E::=E +T,
E::=E $,
E::= E$,
T::= i,
T::= (E),
E::= T,
s
1
E
E::=E +T,
E::=E $,
E::= E$,
T::= i,
T::= (E),
E::= T,
$
s
acc
E::=E$
E::=E$
$
s
acc
T::=(E)
T::= i,
T::= (E)
E::=E+ T,
E::=E +T
E::=E $,
E::=E +T
T::=(E ),
E::= T
T::= i
E::=
E$,
E::= E+T,
T::=( E),
T::= (E)
E::= T,
T::= i,
T
T
T
E
i
i
i
s
7
s
5
(
(
s
4
s
2
(
s
3
s
6
s
8
+
)
+
T::= (E)
T::= i,
E::= T,
E::= E+T,
s
8
s
8
E::=E+T
T::= i,
T::= (E)
E::=E+ T,
E::=E$
E::=E$
E::=E+T
T::=(E)
T::=i
E::=T
Figura 4.34. Diagrama de estados completo del analizador LR(0) del ejemplo.
04-CAPITULO 04 9/2/06 11:50 Pgina 134
Operacin ir a. Sea I un conjunto de elementos de anlisis o configuraciones y X un sm-
bolo (terminal o no) de la gramtica G del apartado anterior. Se define la operacin
ir_a(I,X) as:
A::= xI
cierre( {A ::= X} )
No se muestra ningn pseudocdigo, ya que la operacin ir_a se reduce a una serie de
aplicaciones de la operacin cierre.
Grafo de estados y transiciones del autmata
2
. En lo siguiente, estados y transi-
ciones sern los nombres de los conjuntos de estados (nodos) y transiciones (arcos) del
autmata. A partir de la gramtica aumentada G, se puede definir formalmente el grafo
de transiciones del autmata de anlisis LR(0), de la siguiente manera:
1. cierre( {A ::= A$} ) estados(G).
2. Iestados(G)
(1) X
N
T
, J=ir_a(I,X)estados(G)(I,J)
transiciones(G).
Captulo 4. Anlisis sintctico 135
Figura 4.35. Pseudocdigo para la operacin de cierre de un conjunto de configuraciones.
ConjuntoConfiguraciones Cierre(ConjuntoConfiguraciones I,
GramticaIndependienteContexto
Gic)
{
ConjuntoConfiguraciones Cierre := I;
Configuracin c;
ReglaProduccin r;
while( `se aaden configuraciones a Cierre en la itera-
cin )
{
`repetir para cada elemento c en Cierre y r en
Reglas(Gic)
/* Se supondr que c es de la forma A::=B y r B::= */
if (B::= Cierre)
Cierre := Cierre { B::= };
}
return Cierre;
}
2
Algunos autores llaman a los estados de este grafo conjunto de configuraciones cannicas LR(0).
04-CAPITULO 04 9/2/06 11:50 Pgina 135
(2) I es final N (
N
T
)
*
tales que N ::= I.
(3) I es de aceptacin A ::= A$I.
La Figura 4.36 muestra un posible pseudocdigo para el clculo de este grafo.
136 Compiladores e intrpretes: teora y prctica
Figura 4.36. Pseudocdigo para el clculo del diagrama de transiciones del autmata
de anlisis LR(0).
Grafo GrafoLR(0) (GramticaIndepenienteContexto Gic)
{
ConjuntoConfiguraciones estados[];
ParEnteros transiciones[];
ParEnteros aux_par;
/* ParEnteros, tipo de datos con dos enteros: o y d
(de origen y destino) */
entero i,j,k,it;
i:=0; /* ndice de estados */
it:=0; /* ndice de transiciones */
estados[i]:=cierre({axioma(Gic)::=axioma(Gic).$)};
/*. es la concatenacin de cadenas de caracteres */
`repetir para cada ji
{
`repetir para cada elemento X en
N
{
if ( (ir_a(s[j],X)
( k [0,i] s[k]ir_a(s[j],X) )
)
{
aux_par = nuevo ParEnteros;
aux_par.o = i; aux_par.d = j;
transiciones[ia++]=aux_par;
estado[i++]=ir_a(estado[j],X);
}
}
j++;
}
return s;
}
04-CAPITULO 04 9/2/06 11:50 Pgina 136
Construccin de la tabla de anlisis a partir del autmata LR(0)
Puede abordarse ahora la construccin de la tabla de anlisis a partir de este autmata. Para ello, hay
que identificar las condiciones en las que se anotar cada tipo de operacin en las casillas de la tabla.
Desplazamientos. Se obtienen siguiendo las transiciones del diagrama. Si el autmata
transita del estado s
i
al estado s
j
mediante el smbolo x, en la casilla (i,x) de la tabla se
aadir dj si x
T
y j si x
N
.
Reducciones. Se obtienen consultando los estados finales del diagrama, excepto el estado
de aceptacin. Por definicin, cada estado final contendr una configuracin de reduccin.
Si el estado final es s
i
y su configuracin de reduccin es N::= (donde N::= es la re-
gla k), se aadir la accin rk en todas las casillas de la fila i y las columnas correspon-
dientes a smbolos terminales (la parte de la tabla llamada accin).
Aceptacin. Se obtienen consultando los estados que transitan al estado de aceptacin (con
el smbolo $). Para todas las casillas (i, $), donde i representa un estado s
i
que tiene una
transicin con el smbolo $ al estado de aceptacin, se aade la accin de aceptar.
Error. Todas las dems casillas corresponden a errores sintcticos. Como se ha dicho an-
teriormente, estas casillas suelen dejarse vacas.
La Figura 4.37 muestra la tabla de anlisis del diagrama de transiciones del Ejemplo 4.8.
Captulo 4. Anlisis sintctico 137
Accin Ir_a
E i
T
N
+
( ) $ E T
0
1
2
3
4
5
6
7
8
d4
d4
d8
d2
d5
d5
d2
acc
r2
r4
7 6
1 7
3
d4 d5
r2
r4
r2
r4
r2
r4
r2
r4
r1
r3
r1
r3
r1
r3
r1
r3
r1
r3
Figura 4.37. Tabla de anlisis LR(0) correspondiente al diagrama de la Figura 4.34.
04-CAPITULO 04 9/2/06 11:50 Pgina 137
4.3.4. De LR(0) a SLR(1)
El anlisis LR(0) presenta limitaciones muy importantes. Para comprobarlo, se plantea la cons-
truccin de la tabla de anlisis LR(0) de la gramtica G
B
que contiene el siguiente conjunto de re-
glas de produccin, que aparecen numeradas, y en las que el axioma es el smbolo <Bloque>.
(1)<Bloque> ::= begin <Decs> ; <Ejecs> end
(2)<Decs> ::= dec
(3) | <Decs>;dec
(4)<Ejecs> ::= ejec
(5) | ejec ; <Ejecs>
Obsrvese que esta gramtica representa la estructura de los fragmentos de programas com-
puestos por bloques delimitados por los smbolos begin y end, que contienen una seccin de-
clarativa, compuesta por una lista de smbolos dec (declaraciones), separados por ;, seguida
por una serie de instrucciones ejecutables, que consta de una lista de smbolos ejec, separados
tambin por ;.
La gramtica extendida aade la siguiente regla de produccin:
(0)<Bloque> ::= <Bloque>$
Siguiendo los algoritmos y explicaciones de las secciones anteriores, se puede comprobar que
el diagrama de estados del autmata de anlisis LR(0) es el que muestra la Figura 4.38 y la tabla
de anlisis LR(0) es la de la Figura 4.39. Para simplificar, se utilizan las siguiente abreviaturas:
Ejemplo
4.9
138 Compiladores e intrpretes: teora y prctica
Smbolo original Abreviatura
<Bloque> B
<Bloque> B
<Decs> D
<Ejecs> E
begin b
dec d
end f
ejec e
Este diagrama tiene una situacin peculiar no estudiada hasta este momento: el estado
S
7
={<Ejecs> ::= ejec, <Ejecs> ::= ejec;<Ejecs>} es un estado final que con-
tiene ms de una configuracin. A continuacin se describir con detalle qu repercusiones tiene
esta situacin.
04-CAPITULO 04 9/2/06 11:50 Pgina 138
La casilla (7,;) presenta otra anomala: segn los algoritmos analizados, la presencia de la
configuracin de reduccin <Ejecs>::=ejec obliga a aadir en las casillas de las colum-
nas de la seccin accin de la fila 7 la indicacin r4 (4 es el identificador de la regla
<Ejecs>::=ejec). La presencia adicional de una configuracin de desplazamiento con el
apuntador de anlisis antes del smbolo ; posibilita que con ese smbolo se transite al estado
correspondiente (s
10
) y, por lo tanto, obliga a aadir a la misma casilla la indicacin d10. Eso
significa que, en este estado, en presencia del smbolo ;, no se sabe si se debe reducir o des-
plazar.
Definiciones
Se llama conflicto a la circunstancia en que una casilla de una tabla de anlisis contiene ms de
una accin.
Se llama conflicto reduccin / desplazamiento a la circunstancia en que una casilla contiene
una configuracin de reduccin y otra de desplazamiento.
Se llama conflicto reduccin / reduccin a la circunstancia en que una casilla contiene ms de
una configuracin de reduccin.
Captulo 4. Anlisis sintctico 139
s
0
s
1
s
acc
s
4
s
2
s
3
s
9
s
10
s
6
s
5
s
7
s
8
s
11
d
b
B
D
$
f
e
E
e
d
E
B::=
B$,
B::=
bD;Ef
D::=
d,
B::=b
D;Ef,
D::=
D;d
B::=bD;E
f
E::=e;
E,
E::=
e,
E::=
e;E
B::=bD
;Ef,
D::=D
;d
B::=B
$
E::=
e,
B::=bD;
Ef,
E::=
e;E,
D::=D;
d
;
;
D::=d
D::=d
B::=bD;Ef
B::=bD;Ef
E::=e
,
e::=e
;E
E::=e
,
E::=e
;E
D::=D;d
D::=D;d
E::=e;E
E::=e;E
B::= B$
Figura 4.38. Diagrama del autmata de anlisis LR(0) del ejemplo sobre los lmites
de LR(0).
04-CAPITULO 04 9/2/06 11:50 Pgina 139
Una gramtica independiente del contexto G es una gramtica LR(0) si y slo si su tabla de
anlisis LR(0) es determinista, es decir, no presenta conflictos.
4.3.5. Anlisis SLR(1)
SLR(1) es una tcnica de anlisis que simplifica la tcnica LR(1), que se ver posteriormente.
Toma su nombre de las siglas en ingls de la expresin LR(1) sencillo.
El estudio del ejemplo de conflicto de la seccin anterior sugiere una solucin. Es fcil com-
probar que la gramtica G
B
puede generar la palabra
begin dec ; ejec ; ejec end
La Figura 4.40 muestra su rbol de derivacin.
Al llegar a la segunda aparicin del smbolo ; (situada entre los dos smbolos ejec), el ana-
lizador LR(0) se encuentra en el estado s
7
. El smbolo siguiente que hay que procesar de la ca-
dena de entrada es ;. A continuacin se analizar el efecto de cada una de las dos opciones
sobre el rbol de derivacin.
140 Compiladores e intrpretes: teora y prctica
E d
T
N
e E T
0
1
2
3
4
5
6
7
8
d3
d2
r2 r2
d8
d9
d10/
r4
5
Accin Ir_a
9
10
11
B D E ; f $ b
acc
r2 r2
1
d5
11
r4
d7
r1 r1 r1
d7
r5 r5 r5 r5
4
r2 r2
r5 r5
r1 r1 r1
6
r3 r3 r3 r3 r3 r3
r4 r4 r4 r4
Figura 4.39. Tabla de anlisis LR(0) correspondiente al diagrama de la Figura 4.38.
04-CAPITULO 04 9/2/06 11:50 Pgina 140
La Figura 4.41 muestra lo que ocurrira si se eligiese la opcin de la reduccin.
Si se reduce la regla <Ejecs>::=ejec, el resto del subrbol que tiene a <Ejecs> como
raz, que est resaltado en la Figura 4.41, no puede ser analizado tras la reduccin, porque
<Ejecs> habra sido ya totalmente analizado. La Figura 4.42 refleja grficamente los pasos del
analizador sobre el diagrama de transiciones y contiene una flecha que indica la secuencia en la
que se visita cada estado. Se resaltan los estados y las transiciones de ese camino.
Desde el estado inicial, tras desplazar el smbolo begin, se llega al estado s
2
. Al desplazar
el siguiente terminal de la cadena de entrada (dec), se transita al estado s
3
, en el que se reduce
la regla <Decs>::=dec. Cuando se reduce una regla, el algoritmo de anlisis elimina de la pila
los smbolos almacenados en relacin con su parte derecha, y vuelve al estado en que se encon-
traba antes de comenzar a procesar dicha parte derecha. Ese estado se encuentra ahora con el sm-
bolo no terminal que forma la parte izquierda de la regla que se est reduciendo. En este caso,
Captulo 4. Anlisis sintctico 141
<Bloque>
begin dec ; ejec ; ejec end
<Decs>
<Ejecs>
<Ejecs>
Figura 4.40. rbol de derivacin de la palabra begin dec; ejec; ejec end por parte de la
gramtica G
B
.
<Bloque>
begin dec ; ejec ; ejec end
<Decs>
<Ejecs>
<Ejecs>
Reduccin
Figura 4.41. Efecto de la reduccin de la regla <Ejecs>ejec.
04-CAPITULO 04 9/2/06 11:50 Pgina 141
antes de analizar la parte derecha dec, el analizador estaba en el estado s
2
(eso es lo que re-
presenta el fragmento de la flecha que vuelve desde el estado s
3
al s
2
). A continuacin, con el
smbolo <Decs> se transita desde el estado s
2
al estado s
6
. Los dos siguientes smbolos termi-
nales (; y ejec) tambin dan lugar a desplazamientos a los estados s
5
y s
7
, respectivamen-
te. En este ltimo se reduce la regla <Ejecs>::=ejec y se vuelve al estado anterior al proceso
de la parte derecha (ejec), que es, de nuevo, s
5
. El smbolo no terminal de la parte izquierda de
la regla (<Ejecs>) hace que se transite a s
6
. Desde este estado slo se espera desplazar el ter-
minal end para llegar al estado en el que se puede reducir un bloque completo. Sin embargo, el
smbolo que hay que analizar en este instante es el terminal ejec. La transicin asociada a este
smbolo no est definida, por lo que el anlisis terminara indicando un error sintctico.
Intuitivamente, el error se ha originado porque se interpret que el smbolo ; indicaba el final
de la lista de sentencias ejecutables (ejec), cuando realmente era su separador.
El analizador slo tiene un comportamiento correcto posible: considerar el smbolo ; como
lo que es, un separador, y desplazarlo. La Figura 4.43 muestra, en el rbol de derivacin, que este
desplazamiento posibilita el xito del anlisis.
Al desplazar el smbolo ;, se posibilita la reduccin posterior, primero de la segunda apari-
cin de ejec al smbolo no terminal <Ejecs>, y luego de ejec;<Ejecs> al smbolo no ter-
142 Compiladores e intrpretes: teora y prctica
s
0
s
1
s
acc
S
4
s
2
s
3
s
9
s
10
s
6
s
5
s
7
s
8
s
11
d
b
B
D
$
f
e
E
e
d
E
B::=
B$,
B::=
bD;Ef
B::=b
D;Ef,
D::=
d,
D::=
D;d
B::=bD;E
f
B::=bD
;Ef,
D::=D
;d
B::=B
$
E::=e;E
B::=bD;
Ef,
E::=
e,
E::=
e;E,
D::=D;
d
;
;
D::=d
D::=d
B::=bD;Ef
B::=bD;Ef
E::=e
E::=e
D::=D;d
D::=D;d
E::=e;
E,
E::=
e,
E::=
e;E
E::=e;
E,
E::=
e,
E::=
e;E
E::=e;E
B::=B$
B::=B$
Figura 4.42. Recorrido del analizador sintctico sobre la cadena begin dec; ejec; ejec
end si el estado s
7
fuera slo de reduccin.
04-CAPITULO 04 9/2/06 11:50 Pgina 142
minal <Ejecs>. De esta forma, el anlisis puede terminar con xito. Las Figuras 4.44 a 4.46
muestran el recorrido por el diagrama de estados correspondiente al anlisis completo.
Captulo 4. Anlisis sintctico 143
s
0
s
1
s
acc
S
4
s
2
s
3
s
9
S
10
s
6
s
5
s
7
s
8
s
11
d
b
B
D
$
f
e
E
e
d
E
B::=bD;E
f
B::=bD
;Ef,
D::=D
;d
;
;
B::=
B$,
B::=
bD;Ef
B::=B
$
B::=B$
B::=b
D;Ef,
D::=
d,
D::=
D;d
B::=bD;Ef
D::=D;d
E::=e;
E,
E::=
e,
E::=
e;E
B::=B$
D::=D;d
E ::= e
E ::=
e;E
B::=bD;Ef
D::=d
D::=d
E::=e;E
E::=e;E
B::=bD;
Ef,
E::=
e,
E::=
e;E,
D::=D;
d
E ::= e
E ::=
e;E
Figura 4.44. Recorrido hasta el estado s
7
que el anlisis sintctico debera realizar sobre el
diagrama del analizador sintctico para analizar correctamente la cadena begin dec; ejec;
ejec end.
<Bloque>
begin dec ; ejec ; ejec end
<Decs>
<Ejecs>
<Ejecs>
Desplazamiento
Figura 4.43. Efecto de desplazar el smbolo ; en el anlisis de la palabra begin dec;
ejec; ejec end.
04-CAPITULO 04 9/2/06 11:50 Pgina 143
Obsrvese que, en este caso, se tienen que considerar las dos configuraciones del estado s
7
.
Primero se aplica la configuracin de desplazamiento, que aparece subrayada en la Figura 4.44.
De esta manera se llega al estado s
10
. Con el desplazamiento correspondiente a la siguiente apa-
ricin del terminal ejec se vuelve al estado s
7
, pero ahora, en presencia de end, que es el si-
guiente smbolo terminal analizado, slo se puede reducir y sustituir ejec por <Ejecs>.
La Figura 4.45 muestra los pasos de anlisis siguientes. Tras la reduccin, que supone elimi-
nar de la pila todo lo que corresponde a la parte derecha de la regla, el analizador se encuentra de
nuevo en el estado s
10
y tiene que procesar el smbolo no terminal recin incorporado a la cade-
na de entrada (<Ejecs>). As se llega al estado s
11
en el que se reducen las dos apariciones de
ejec. El analizador vuelve al estado en el que se encontraba antes de la primera aparicin de
ejec, es decir, en el estado s
5
. Con el smbolo no terminal de la parte izquierda (<Ejecs>) se
transita de s
5
a s
6
. El siguiente smbolo para analizar es end. Su desplazamiento lleva al estado
en el que se reduce el bloque de sentencias completo.
La Figura 4.46 muestra los pasos finales del anlisis. Tras reducir el bloque completo, se vuel-
ve al estado inicial. Con el smbolo no terminal de la parte izquierda de la regla reducida
(<Bloque>) se llega a s
1
, desde donde se desplaza el smbolo final de la cadena y se llega al
estado de aceptacin, lo que completa con xito el anlisis.
144 Compiladores e intrpretes: teora y prctica
s
0
s
1
s
acc
s
4
s
2
s
3
s
9
s
10
s
5
s
7
s
8
s
11
d
b
B
D
$
f
e
E
e
d
E
B::=
B$,
B::=
bD;Ef
B::=bD;E
f
B::=B
$
D::=D;d
;
;
E::=e
E::=
e;E
B::=bD;
Ef,
E::=
e,
E::=
e;E,
D::=D;
d
s
6
E::=e;E
E::=e;
E,
E::=
e,
E::=
e;E
B::=B$
B::=B$
D::=d
D::=d
B::=bD;Ef
B::=bD;Ef
D::=D;d
E::=e;E
B::=bD
;Ef,
D::=D
;d
B::=b
D;Ef,
D::=
d,
D::=
D;d
E::=e
E::=e
Figura 4.45. Recorrido hasta la ltima reduccin que el anlisis sintctico debera realizar so-
bre el diagrama del analizador sintctico para analizar correctamente la cadena begin dec;
ejec; ejec end.
04-CAPITULO 04 9/2/06 11:50 Pgina 144
Lo ms relevante de este anlisis es la razn por la que el analizador no debe reducir en la ca-
silla del conflicto: en el estado s
7
, el smbolo ; debe interpretarse siempre como separador de
sentencias ejecutables. La reduccin slo debe aplicarse cuando se ha llegado al final de la lista
de smbolos ejec, y esto ocurre slo cuando aparece el smbolo terminal end. Con el anlisis
LR(0) es imposible asociar el estado s
7
y el terminal end. En la prxima seccin se ver con de-
talle que esta solucin se basa en el hecho de que el smbolo no terminal <Ejecs> (la parte iz-
quierda de la regla que se reduce en s
7
) siempre debe venir seguido por el smbolo terminal end.
Este conflicto no se habra producido si en lugar de reducir la regla en todas las columnas de
la parte de accin de la fila 7, slo se hubiera hecho en las columnas de los smbolos terminales
que pueden seguir a <Ejecs>, que es la parte izquierda de la regla que se va a reducir.
Construccin de tablas de anlisis
En la Seccin 4.1 se presentaron dos conjuntos importantes de las gramticas independientes del
contexto. Uno de ellos, el conjunto siguiente, contiene el conjunto de smbolos que pueden se-
guir a otro en alguna derivacin. Este conjunto puede utilizarse para generalizar las reflexiones
de la seccin anterior, y es el origen de la tcnica llamada SLR(1).
Captulo 4. Anlisis sintctico 145
s
0
s
1
s
acc
s
4
s
2
s
3
s
9
s
10
S
6
s
5
s
7
s
8
s
11
d
b
B
D
$
f
e
E
e
d
E
B::=
B$,
B::=
bD;Ef
B::=b
D;Ef,
D::=
d,
D::=
D;d
D::=d
B::=bD;Ef
B::=bD;E
f
E::=e;
E,
E::=
e,
E::=
e;E
B::=bD
;Ef,
D::=D
;d
B::=B
$ B::=B$
E::=e;E
D::=D;d
B::=bD;
Ef,
E::=
e,
E::=
e;E,
D::=D;
d
;
;
E::=e
E::=
e;E
D::=d
B::=bD;Ef
E::=e
E::=
e;E
D::=D;d
E::=e;E
B::=B$
Figura 4.46. ltimos pasos del recorrido que el anlisis sintctico debera realizar sobre el
diagrama del analizador sintctico para analizar correctamente la cadena begin dec; ejec;
ejec end.
04-CAPITULO 04 9/2/06 11:50 Pgina 145
146 Compiladores e intrpretes: teora y prctica
E d
T
N
e E T
0
1
2
3
4
5
6
7
8
d3
d2
r2 r2
d8
d9
d10/
r4
5
Accin Ir_a
9
10
11
B D E ; f $ b
acc
r2 r2
1
d5
11
r4
d7
r1 r1 r1
d7
r5 r5 r5 r5
4
r2 r2
r5 r5
r1 r1 r1
6
r3 r3 r3 r3 r3 r3
r4 r4 r4 r4
E d
T
N
e E T
0
1
2
3
4
5
6
7
8
d3
d2
d8
d9
5
Accin Ir_a
9
10
11
B D E ; f $ b
acc
1
d5
11
d7
r1
d7
r5
4
r2
6
r3
d10 r4
(0)B B$
(1)B bD;Ef
(2)D d
(3)D D;d
(4)E e
(5)E e;E
siguiente (B)={$}
siguiente (D)={;}
siguiente (E)={f}
Figura 4.47. Tablas de anlisis LR(0) y SLR(1) para la gramtica del ejemplo. La parte supe-
rior muestra la tabla LR(0) y resalta las casillas que cambiarn de contenido. La parte inferior
muestra la tabla SLR(1). A su izquierda estn los conjuntos auxiliares que justifican su
contenido.
04-CAPITULO 04 9/2/06 11:50 Pgina 146
La tabla de anlisis se construye de la misma manera que con la tcnica LR(0) (vase la
Seccin 4.3.3), excepto por las reducciones:
Reducciones. Igual que en el caso LR(0), se consultan los estados finales del diagrama,
excepto el estado de aceptacin. Si el estado final es s
i
y su configuracin de reduccin es
N::= (donde N::= es la regla k), la accin rk se aadir slo en las casillas corres-
pondientes a las columnas de los smbolos terminales del conjunto siguiente(N), ya que
slo ellos pueden seguir a las derivaciones de N.
Veamos el contenido de los conjuntos siguiente de los smbolos no terminales del
Ejemplo 4.9:
siguiente(<Bloque>)={$}
siguiente(<Decs>)={;}
siguiente(<Ejecs>)={end}
Esto significa que en las filas correspondientes a los estados en los que se reduce una regla cuya
parte izquierda sea <Bloque>, slo hay que colocar la accin de reduccin en la casilla correspon-
diente al smbolo $. En los estados en que se reduzca una regla cuya parte izquierda sea <Decs>,
hay que hacerlo slo en la columna correspondiente a ;, y en los estados en que se reduzca una re-
gla cuya parte izquierda sea <Ejecs>, hay que hacerlo slo en la columna encabezada por end.
La Figura 4.47 compara las tablas de anlisis LR(0) y SLR(1) para el Ejemplo 4.9. Puede
comprobarse que ha desaparecido el conflicto reduccin / desplazamiento de la tabla LR(0).
Definicin de gramtica SLR(1)
Una gramtica independiente del contexto G es una gramtica SLR(1) si y slo si su tabla de an-
lisis SLR(1) es determinista, es decir, no presenta conflictos.
4.3.6. Ms all de SLR(1)
Hay alguna gramtica independiente del contexto que no sea SLR(1)? Es decir, existen gramti-
cas independientes del contexto cuyas tablas de anlisis SLR(1) siempre presentan conflictos?
Considrese la gramtica G
axb
que contiene las siguientes reglas de produccin y cuyo axioma es
el smbolo S:
(1)S ::= A
(2)S ::= xb
(3)A ::= aAb
(4)A ::= B
(5)B ::= x
Es fcil comprobar que esta gramtica genera el lenguaje {xb, a
n
xb
n
| n0}. A continua-
cin se va a construir su tabla de anlisis SLR(1). En primer lugar se aumenta la gramtica con
la produccin (0)S::=S$ y se construye el diagrama de estados del autmata de anlisis
LR(0) que muestra la Figura 4.48.
Ejemplo
4.10
Captulo 4. Anlisis sintctico 147
04-CAPITULO 04 9/2/06 11:50 Pgina 147
Es fcil calcular el valor el conjunto siguiente:
siguiente(A)={$,b}
siguiente(B)={$,b}
siguiente(S)={$}
La tabla de anlisis SLR(1) de G
axb
se muestra en la Figura 4.49.
La presencia del estado s
5
, que contiene la reduccin de la regla (5)B::=x, y el hecho de
que b siguiente(B), y que con b se pueda transitar desde s
5
a s
6
, originan un conflicto de
tipo reduccin / desplazamiento.
4.3.7. Anlisis LR(1)
Considrese G
axb
, ejemplo de una gramtica que no es SLR(1). El smbolo b pertenece a si-
guiente(B) a causa de las reglas A::=B y A::=aAb. Por la primera se llega a la conclusin
de que los smbolos que sigan al rbol de derivacin de B tienen que contener a los que sigan al
de A. Por la segunda queda claro que b es uno de esos smbolos. Por lo tanto, para que haya una
b detrs del rbol de derivacin de B, tiene que ocurrir que antes del rbol hubiera una a (ya que
148 Compiladores e intrpretes: teora y prctica
s
1
s
0
s
5
s
6
s
8
s
acc
s
9
s
7
s
4
s
3
s
2
S
B
A B
A
a
x
b
a
x
b
$
A::=aAb
S::=S$
B::=x
A::=aA
b
A::=B
S::=A
S::=S
S::=xb
S::=x
b
B::=x
S::=
S
S::=
A
S::=
xb
A::=
aAb
A::=
B
B::=
x
A::=a
Ab
A::=
aAb
A::=
B
B::=
x
A::=aAb
A::=B
S::=S$
S::=S
S::=x
b
B::=x
S::=xb
B::=x
S::=A
N
b T
0
1
2
3
4
5
6
7
8
d3
d5
Accin Ir_a
9
S A B $ x
1
d8
4 2
r5
siguiente(A)={$,b}
siguiente(B)={$,b}
siguiente(S)={$}
T
7
d3
r5
r3 r3
acc
r1
4
r5
r4
r2
r4
r5/d6
Figura 4.49. Tabla de anlisis SLR(1) de la gramtica G
axb
.
04-CAPITULO 04 9/2/06 11:50 Pgina 149
150 Compiladores e intrpretes: teora y prctica
s
1
s
0
s
5
s
6
s
8
s
9
s
7
s
4
s
3
s
2
S
B
A B
A
a
x
b
a
x
b
$
A::=aAb
S::=S$
B::=x
A::=aA
b
A::=B
S::=A
S::=xb
S::=x
b
B::=x
S::=
S
S::=
A
S::=
xb
A::=
aAb
A::=
B
B::=
x
A::=a
Ab
A::=
aAb
A::=
B
B::=
x
s
1
s
0
s
5
s
6
s
8
s
9
s
7
s
4
s
3
s
2
S
B
A B
A
a
x
b
a
x
b
$
A::=aAb
S::=S$
B::=x
A::=aA
b
A::=B
S::=A
S::=xb
S::=x
b
B::=x
S::=
S
S::=
A
S::=
xb
A::=
aAb
A::=
B
B::=
x
A::=a
Ab
A::=
aAb
A::=
B
B::=
x
a)
b)
S::=S
S::=S
A::=B
A::=aAb
S::=A
S::=S$
B::=x
S::=xb
S::=x
b
B::=x
A::=B
A::=aAb
S::=A
S::=S$
B::=x
S::=xb
S::=x
b
B::=x
s
acc
s
acc
S::=S
Figura 4.50. Comparacin entre los dos posibles recorridos previos a las reducciones de la
regla B::=x: (a) en el estado s
5
, (b) en el estado s
8
.
04-CAPITULO 04 9/2/06 11:50 Pgina 150
Captulo 4. Anlisis sintctico 151
S
S
A
B
X
$
Figura 4.51. rbol de derivacin de la cadena x$ por la gramtica G
axb
.
Descripcin intuitiva del anlisis LR(k), k>0
La posible mejora sugerida al final de la seccin anterior es generalizable. A lo largo de las pr-
ximas secciones, se formalizar mediante el conjunto de smbolos de adelanto, para describir la
tcnica de anlisis LR(k) con k>0.
A partir de este punto, se llamar conjunto de smbolos de adelanto a los smbolos que, en un
estado concreto del diagrama, se espera encontrar en cada una de sus configuraciones. Hasta aho-
ra, una configuracin representaba una hiptesis en curso, en el proceso del anlisis, definida por
la posicin del apuntador de anlisis en la parte derecha de una regla compatible con la porcin
de cadena de entrada analizada. En este nuevo tipo de anlisis se aadir a cada configuracin los
smbolos de adelanto correspondientes. Tras aadir esta informacin, la configuracin
N::= {
1
,,
m
},, N
N
,(
N
T
)
*
{
1
,,
m
}
T
significa que, en este instante, una de las hiptesis posibles est relacionada con la regla N;
en particular, el prefijo de la parte derecha es compatible con la porcin de la entrada analiza-
da hasta este momento; adems, esto slo es posible si, tras terminar con esta regla, el siguiente
smbolo terminal pertenece al conjunto {
1
,,
m
}.
En esto se basa el aumento de precisin del anlisis LR(1) sobre SLR(1). Lo que en SLR(1)
era una nica hiptesis, se multiplica ahora con tantas posibilidades como conjuntos diferentes
de smbolos de adelanto.
Introduccin al clculo de smbolos de adelanto. Vamos a construir el diagrama de estados
del autmata de anlisis LR(1) de la gramtica G
axb
del Ejemplo 4.10. Se mantendr el mismo
04-CAPITULO 04 9/2/06 11:50 Pgina 151
algoritmo bsico, al que se incorpora el clculo de los conjuntos de smbolos de adelanto de cada
configuracin de cada estado.
Empezaremos con un mecanismo para calcular el conjunto de smbolos de adelanto:
De la configuracin inicial del estado inicial (A::=A$).
De las configuraciones del cierre de una configuracin.
De las configuraciones resultado de ir a otro conjunto de configuraciones mediante un sm-
bolo.
En el ejemplo, la configuracin inicial del estado inicial es S::=S$. Para preservar la in-
tuicin es frecuente, en el anlisis LR(1), considerar que la primera configuracin de la nueva re-
gla de la gramtica ampliada es S::=S, prescindiendo del smbolo final ($). Expresada de
esta forma, la hiptesis inicial indica que el apuntador de anlisis se encuentra antes del primer
smbolo de la cadena de entrada y que se espera poder reducirla toda ella al smbolo no termi-
nal S. Resulta claro que el nico smbolo que se puede esperar, tras procesar completamente la
regla, es el smbolo que indica el final de la misma. Por tanto, el conjunto de smbolos de ade-
lanto de la configuracin inicial del estado inicial en el anlisis LR(1) es {$}.
Para completar el estado inicial, hay que aadir las configuraciones de cierre({S::=S
{$}}), lo que implica calcular los conjuntos de smbolos de adelanto para S::=A, A::=B,
B::=x, A::=aAb y S::=xb.
Hemos visto que, tras procesar por completo S::=S, hay que encontrar el smbolo $.
Esto implica que, si S se redujo mediante la regla S::=A, tras A se puede encontrar lo mismo
que se encontrara tras S, es decir, $. Este razonamiento vale para todas las configuraciones y jus-
tifica que el nuevo conjunto de smbolos de adelanto sea {$}, lo que esquematiza grficamente
la Figura 4.52, que representa los cierres sucesivos mediante rboles de derivacin concatenados.
La Figura 4.53 muestra grficamente cmo se completa el clculo del estado inicial, que re-
sulta ser:
s
0
={s ::= S {$},
S ::= A {$},
A ::= B {$},
B ::= x {$},
A ::= aAb {$},
S ::= xb {$}}
El caso analizado en este estado no es el ms general que puede aparecer al realizar la operacin
cierre. De hecho, puede inducir a engao que en este caso no se modifique el conjunto de sm-
bolos de adelanto porque, como se ver en los prximos prrafos, es esta operacin la que puede mo-
dificarlos. Como sugiere la Figura 4.52, la razn por la que en este caso no se modifican los smbolos
de adelanto es que el cierre se ha aplicado en todos los casos a configuraciones con la estructura:
N::=A {
1
,... ,
m
},, N, A
N
(
N
T
)
*
{
1
,... ,
m
}
T
Tras el smbolo no terminal que origina el cierre, no hay ningn otro smbolo terminal. Por lo
tanto, lo que se encontrar tras l (en este caso A) es lo mismo que se encuentra cuando se ter-
mina de procesar cualquiera de sus reglas (A ::= ).
152 Compiladores e intrpretes: teora y prctica
04-CAPITULO 04 9/2/06 11:50 Pgina 152
Captulo 4. Anlisis sintctico 153
S::=
S {$}
a)
b)
f)
e)
d) c)
S::=
S {$}
S::=
S {$}
S::=
S {$} S::=
S {$}
S::=
S {$}
$
$
$
$
$
$
A
A
B
xb
aAb
A
A
B
x
Figura 4.52. Ejemplo de situacin de cierre que no modifica el conjunto de smbolos de ade-
lanto: (a)S::=S {$} (b) S::=A {$} (c) A::=B {$} (d) B::=x {$} (e) A::=aAb {$} (f)
S::=xb {$}
Para ilustrar esta situacin, considrese a continuacin la operacin que calcula
s
5
=ir_a(s
0
,a). A::=aAb{$} es la nica configuracin de s
0
relacionada con esta operacin.
Hay que calcular, por tanto, el conjunto de smbolos de adelanto de A::=aAb. Parece lgico con-
cluir que lo que se espere encontrar tras terminar con la parte derecha de la regla no cambiar por-
que se vayan procesando smbolos en ella. El conjunto buscado coincide con {$} y se puede extraer
la siguiente conclusin: El conjunto de smbolos de adelanto de una configuracin no vara cuan-
do se desplaza el apuntador de anlisis hacia la derecha a causa de transiciones entre estados.
04-CAPITULO 04 9/2/06 11:50 Pgina 153
Para concluir los clculos para la operacin ir_a, se realiza el cierre de la configuracin re-
sultado de desplazar a:
ir_a(s
0
,a)=cierre( { A ::= aAb {$}} )
Hay que calcular los conjuntos de smbolos de adelanto para A::=xb y A::=B. En este caso, lo
fundamental es la b que sigue a A (A ::= aAb). Aplicando el mismo razonamiento de los prrafos
anteriores, la hiptesis de esa configuracin es que la prxima porcin de la cadena de entrada tiene que
permitir reducir alguna regla de A, y luego desplazar la b ha de que seguirla obligatoriamente. Por eso,
tras procesar completas las reglas de A, se tendr que encontrar una b (la resaltada en las lneas ante-
riores) y {b} es el conjunto de smbolos de adelanto buscado. El resto de las configuraciones no pre-
sentan novedades respecto a lo expuesto anteriormente. Obtendremos, por tanto, el siguiente estado:
s
5
={A ::= aAb {$},
A ::= aAb {b},
A ::= B {b},
B ::= x {b}}
La Figura 4.54 muestra el diagrama de estados correspondiente a esta situacin.
154 Compiladores e intrpretes: teora y prctica
s
0
S::=
S{$}
S::=
A{$}
S::=
xb{$}
A::=
aAb{$}
A::=
B{$}
B::=
x{$}
Figura 4.53. Estado inicial del autmata de anlisis LR(1) de la gramtica G
axb
.
s
0
s
5
S::=
S{$}
S::=
A{$}
S::=
xb{$}
A::=
aAb{$}
A::=
B{$}
B::=
x{$}
A::=a
Ab{$}
A::=
aAb{b}
A::=
b{b}
B::=
x{b}
a
Figura 4.54. Diagrama con los dos primeros estados del autmata del analizador LR(1) de la
gramtica G
axb
.
04-CAPITULO 04 9/2/06 11:50 Pgina 154
Como se ha visto, el conjunto de smbolos de adelanto puede variar cuando se cierra una con-
figuracin con la siguiente estructura:
P::=N {
1
,,
m
},,P,N
N
(
N
T
)
*
(
N
T
)
+
{
1
,,
m
}
T
El caso analizado contiene una cadena que se reduce a un nico smbolo no terminal. En ge-
neral, puede ser cualquier cadena.
Autmata asociado a un analizador LR(1): definiciones formales
A continuacin se describirn las diferencias entre los anlisis LR(0) y LR(1).
Gramtica aumentada. Como se ha descrito informalmente en las secciones anteriores,
la interpretacin del smbolo de adelanto para la regla aadida a la gramtica de partida
origina algunas diferencias en el anlisis LR(1).
Dada cualquier gramtica independiente del contexto G=<
T
,
N
, A, P>, la gramtica
extendida para LR(1) se define as, donde A
N
y $
T
:
G=<
T
,
N
{A}
,
A
,
P{A ::= A}>
Es fcil comprobar que el lenguaje generado por G es el mismo que el generado por G.
Obsrvese que el smbolo $ no aparece de forma explcita en G. Sin embargo, la res-
triccin impuesta sobre l es necesaria, porque en la construccin del autmata de anlisis
LR(1) el smbolo $ se utilizar como el nico smbolo de adelanto para la configuracin
del estado inicial.
Construccin de los conjuntos de smbolos de adelanto. El nico cambio en los con-
ceptos descritos formalmente en la seccin Autmata asociado a un analizador LR(0): de-
finiciones formales es la incorporacin del clculo del conjunto de smbolos de adelanto al
algoritmo general:
La configuracin inicial del estado inicial (A::=A) tiene como conjunto de smbolos de
adelanto {$}. Dicho de otro modo:
A ::= A {$} s
0
Dado un estado cualquiera (s
i
) del autmata, cuando se calcula cierre(s
i
)
P::=N {
1
,... ,
m
}s
i
,, P,N
N
,(
N
T
)
*
{
1
,... ,
m
}
T
N::= primero_LR(1)(.{
1
,... ,
m
}) cierre(s
i
).
Donde:
1. .{
1
,... ,
m
}no define ninguna operacin, sino que es una notacin que se re-
fiere a la concatenacin de una cadena y un conjunto de smbolos {
1
,... ,
m
}.
2. primero_LR(1) es un conjunto que se puede definir en funcin del conjunto pri-
mero de la siguiente manera:
primero_LR(1)(.{
1
,... ,
m
})=
i=1,...,m
{primero (
i
)}
donde
i
representa la concatenacin habitual de smbolos.
Captulo 4. Anlisis sintctico 155
04-CAPITULO 04 9/2/06 11:50 Pgina 155
Tambin es posible calcular este conjunto de la siguiente manera:
primero() si primero()
primero_LR(1)(.{
1
,... ,
m
})=
primero()- {
1
,... ,
m
}
en otro caso
Dado un estado cualquiera (s
i
) del autmata, y un smbolo cualquiera (X
N
T
), cuando se
calcula ir_a(s
i
,X) P::=X s
i
ir_a(s
i
,X) cierre({P::=X}).
La Figura 4.55 muestra el diagrama de estados completo del analizador LR(1) de la gramti-
ca G
axb
.
Observaciones sobre la naturaleza del conjunto de smbolos
de adelanto
El conjunto de smbolos de adelanto no siempre contiene smbolos aislados. La Seccin 4.3.8
se dedica al anlisis LALR(1), en el que, de forma natural, se construyen conjuntos de smbo-
los de adelanto con ms de un smbolo. Otra circunstancia en la que puede aparecer este tipo
de conjuntos es en el clculo del diagrama de estados, cuando en alguno de ellos surge la ne-
cesidad de incluir varias veces la misma configuracin con diferentes smbolos de adelanto. En
156 Compiladores e intrpretes: teora y prctica
s
0
s
5
S::=
S{$}
S::=
A{$}
1
S::=
xb{$}
1
A::=
aAb{$}
A::=
B{$}
B::=
x{$}
1
A::=a
Ab{b}
A::=
aAb{b}
2
A::=
B{b}
2
B::=
x{b}
3
a
A::=a
Ab{b}
A::=
aAb{b}
4
A::=
B{b}
4
B::=
x{b}
3
s
7
a
A::=B
{$}
A::=aAb
{$}
S::=A
{$}
A::=aA
b{$}
S::=x
b{$}
B::=x
{$} S::=xb
{$} A::=B
{b} A::=aAb
{b}
A::=aA
b{b}
B::=x
{b}
1
primero ($)={$}
2
primero (b$)={b}
3
primero (b)={b}
4
primero (bb)={b}
s
1
s
3 s
10
S
B
s
2
A
s
4
x
s
5
b
A
x
s
13
b
s
8
s
9
x
B
a
A
s
11
b
s
12
B
S::=S
{$}
Figura 4.55. Diagrama de estados del autmata del analizador LR(1) de la gramtica G
axb
.
04-CAPITULO 04 9/2/06 11:50 Pgina 156
este caso, el conjunto de smbolos de adelanto tiene que incluir todos los smbolos identifi-
cados.
Como ejemplo, considrese la siguiente gramtica independiente del contexto para un frag-
mento de un lenguaje de programacin de alto nivel que permite el tipo de dato apuntador, con
una notacin similar a la del lenguaje C. La gramtica describe algunos aspectos de las asigna-
ciones a los identificadores que incluyen posibles accesos a la informacin apuntada por un pun-
tero, mediante el uso del operador *.
G
*
={
T
={=,*,id},
S,
N
={S,L,R},
{ SL=R | R,
L*R | id,
RL } }
La Figura 4.56 muestra el diagrama de estados del autmata de anlisis LR(1).
Puede observarse en el estado s
0
la presencia de dos configuraciones cuyos conjuntos de sm-
bolos de adelanto contienen dos smbolos. En el caso de L::=*R{=,$} la presencia del sm-
bolo = se justifica porque es el que sigue al no terminal L en la configuracin que se est
cerrando (S::L=R{$}). Es necesario aadir tambin el smbolo $, ya que tambin hay que ce-
rrar la configuracin R::L{$}, y en esta ocasin el smbolo no terminal L aparece al final de
Captulo 4. Anlisis sintctico 157
s
1
s
3
s
6
s
0
s
7
s
8 s
10
s
4
s
11
s
12
s
9
s
13
s
5
id
id
id
id
S
R
s
2
L
=
L
R
*
R
*
L
L
*
R
*
S::=S
{$}
S::=
S{$}
S::=
L=R{$}
S::=
R{$}
L::=
*R{$,=}
L::=
id{$,=}
R::=
L{$}
S::=R
{$}
S::=L=
R{$}
R::=
L{$}
L::=
*R{$}
L::=
id{$}
L::=*
R{$,=}
R::=
L{$,=}
L::=
*R{$,=}
L::=
id {$,=}
S::=L
=R{$}
R::=L
{$}
L::= id
{$,=}
L::=*R
{$,=}
R::=L
{$,=} R::=L
{$}
L::=*R
{$}
S::=L=R
{$}
L::=id
{$}
L::=*
R{$}
R::=
L{$}
L::=
*R{$}
L::=
id{$}
Figura 4.56. Ejemplo de diagrama de estados de autmata de anlisis LR(1) con conjuntos
de smbolos de adelanto no unitarios.
04-CAPITULO 04 9/2/06 11:50 Pgina 157
la parte derecha de la regla y no se modifican los smbolos de adelanto ({$}). El anlisis para la
configuracin L::=id{=,$} es similar.
Construccin de tablas de anlisis LR(1)
La tabla de anlisis tiene la misma estructura que la tabla LR(0), con la excepcin de que en
LR(1) la columna del smbolo de final de cadena ($) aparece por convenio, ya que no pertene-
ce estrictamente al conjunto de terminales.
Desplazamientos. Igual que en LR(0), se obtienen siguiendo las transiciones del diagra-
ma. Si el autmata transita del estado s
i
al estado s
j
mediante el smbolo x, en la casilla
(i,x) de la tabla se aadir dj si x
T
y j si x
N
.
Reducciones. No se aplica el mismo proceso que en las tablas LR(0) ni SLR(1). Igual que
en LR(0), se consultan los estados finales del diagrama, pero en este caso se utilizan los
conjuntos de smbolos de adelanto de las configuraciones de reduccin. Si el estado final es
s
i
y su configuracin de reduccin es N::={
1
,...
m
,} (donde N::= es la regla
nmero k), la accin rk se aadir slo en las casillas correspondientes a las columnas de
los smbolos de adelanto {
1
,...
m
,}.
Aceptacin. No se aplica el mismo proceso que en las tablas LR(0), ya que el smbolo fi-
nal de la cadena $ no forma parte explcitamente de la gramtica, sino que aparece slo
en los smbolos de adelanto. La accin de aceptacin se escribe en la casilla (i, $), siempre
que la i represente al estado s
i
que contiene la configuracin A::=A{$}.
Error. Igual que en las tablas LR(0), todas las dems casillas corresponden a errores sin-
tcticos.
Como ejemplo, la Figura 4.57 muestra la tabla de anlisis LR(1) de la gramtica G
axb
.
Puede comprobarse que se ha resuelto el conflicto que haca que G
axb
no fuese SLR(1).
Definicin de gramtica LR(1)
Una gramtica independiente del contexto G es una gramtica LR(1) si y slo si su tabla de an-
lisis LR(1) es determinista, es decir, no presenta conflictos.
Evaluacin de la tcnica
Comparando el tamao de los diagramas de estado y de las tablas de anlisis SLR(1) y LR(1)
para la gramtica G
axb
, que aparecen respectivamente en las Figuras 4.48, 4.49, 4.55 y 4.56, se
comprueba que el aumento de precisin para solucionar el conflicto implica un aumento consi-
derable en el tamao de ambos elementos. Es fcil ver, sobre todo en los diagramas de estado,
que los nuevos estados s
7
, s
8
, s
11
y s
12
se originan como copias, respectivamente, de los anti-
guos estados s
3
, s
4
, s
7
y s
9
, con smbolos de adelanto distintos.
Se puede demostrar que LR(1) es el algoritmo de anlisis ms potente entre los que realizan el
recorrido de la cadena de entrada de izquierda a derecha con ayuda de un smbolo de adelanto.
Tambin tiene inters la extensin de este algoritmo de anlisis a un conjunto mayor de sm-
bolos de adelanto. Como se ha dicho previamente, estas extensiones se denominan LR(k), donde
158 Compiladores e intrpretes: teora y prctica
04-CAPITULO 04 9/2/06 11:50 Pgina 158
k representa la longitud de los elementos de los conjuntos de adelanto. En la prctica, las gra-
mticas LR(1) son capaces de expresar las construcciones presentes en la mayora de los lengua-
jes de programacin de alto nivel. El incremento observado en el tamao de los diagramas de
estado y las tablas de anlisis se acenta cuando se utiliza un valor de k mayor que 1. Por ello,
en la prctica, los compiladores e intrpretes no suelen utilizar valores de k mayores que 1.
En la seccin siguiente no se intentar incrementar la potencia expresiva de los analizadores
ascendentes, sino slo mitigar la ineficiencia derivada del aumento del tamao de las tablas de
anlisis al pasar de los analizadores LR(0) y SLR(1) a LR(1).
4.3.8. LALR(1)
Las siglas LALR hacen referencia a una familia de analizadores sintcticos ascendentes que uti-
lizan smbolos de adelanto (de la expresin inglesa Look-Ahead-Left-to-Right). El nmero que
acompaa a LALR tiene el mismo significado que en LR(k).
Captulo 4. Anlisis sintctico 159
E a
N
b
0
1
2
3
4
5
6
7
8
d4
9
S
1
T
d5
r4
r5
acc
r1
r5
r4
d13
Accin
10
11
12
13 r2
r3
A
2
B
3
Ir_a
x $ S
d7 d9
d10
d7 d9
d12
r3
6 8
8 11
Figura 4.57. Tabla de anlisis LR(1) para la gramtica G
axb
.
04-CAPITULO 04 9/2/06 11:50 Pgina 159
Motivacin
Despus de recorrer las diferentes tcnicas del anlisis ascendente, desde LR(0) hasta LR(1), pa-
sando por SLR(1), se llega a la conclusin de que la potencia del anlisis LR(1), y la falta de pre-
cisin del anlisis SLR(1), se deben a que, aunque los conjuntos de smbolos de adelanto y los
conjuntos siguiente pueden estar relacionados (los smbolos de adelanto de una configura-
cin parecen, intuitivamente, estar incluidos en el conjunto siguiente del no terminal de la
parte izquierda de su regla), tienen significados distintos. Que un smbolo terminal pueda seguir
a la parte izquierda de una regla no significa que tenga que aparecer, cada vez que se reduzca, a
continuacin de ella. De hecho, los conjuntos siguiente dependen slo de la regla, mientras
que los de adelanto dependen de la configuracin y de su historia.
El estudio del diagrama de la Figura 4.55 muestra la existencia de estados que slo se dife-
rencian en los smbolos de adelanto de sus configuraciones. Ante esta situacin cabe formularse
la siguiente pregunta: sera posible minimizar el nmero de estados distintos, realizando la unin
de todos los smbolos de adelanto y excluyendo de los conjuntos siguientes los smbolos que
realmente no pueden aparecer inmediatamente despus de reducir la regla de la configuracin co-
rrespondiente? Las prximas secciones se dedicarn a comprobar que la respuesta es afirmativa
y a articular una nueva tcnica de anlisis ascendente que hace uso de ella.
A continuacin se revisar el ejemplo de la gramtica G
axb
desde el punto de vista descrito en la
seccin anterior. La Figura 4.58 resalta cuatro parejas de estados (s
3
y s
8
, s
5
y s
7
, s
6
y s
11
, s
10
y s
12
) en el diagrama de estados del autmata de anlisis LR(1). Son cuatro parejas de estados
distintos, que slo difieren en los smbolos de adelanto de sus configuraciones.
Se intentar reducir el tamao del diagrama agrupando esos estados. El lector familiarizado
con la teora de autmatas reconocer esta situacin como la de minimizacin de un autmata.
En cualquier caso, la reduccin es un proceso iterativo, en el que dos estados se transformarn en
uno solo siempre que sean equivalentes. La equivalencia de estados se basa en las siguientes con-
diciones:
Que slo difieran en los smbolos de adelanto de las configuraciones. El estado que se ob-
tiene al unir los de partida, contendr en cada configuracin la unin de los smbolos de
adelanto.
El nuevo estado debe mantener las transiciones del diagrama, es decir, tienen que llegar a l to-
das las transiciones que llegaran a los de partida y salir de l todas las que salieran de ellos.
El proceso termina cuando no se puedan agrupar ms estados.
Pareja s
3
- s
8
: La Figura 4.59 muestra el proceso de unin de estos dos estados, que da
lugar al estado nuevo s
3_8
.
Obsrvese que la unin es posible porque
La configuracin de los dos estados slo difiere en los smbolos de adelanto.
Las dos transiciones que llegan desde s
0
a s
3
y desde s
5
y s
7
a s
8
pueden sin problemas lle-
gar a s
3_8
.
No hay transiciones que salgan de s
3
ni de s
8
.
Ejemplo
4.11
160 Compiladores e intrpretes: teora y prctica
04-CAPITULO 04 9/2/06 11:50 Pgina 160
El estado resultante es: s
3_8
={A::=B {$,b}}
Pareja s
10
- s
12
: La Figura 4.60 muestra el proceso para esta pareja.
Por razones anlogas, la unin de los dos estados es posible y su resultado es
s
10_12
={A::=aAb {$,b}}.
Pareja s
6
- s
11
: La Figura 4.61 muestra el proceso para esta pareja.
Este caso presenta una situacin nueva: tanto s
6
como s
11
tienen transiciones de salida me-
diante el smbolo b. La unin es posible, porque las dos llegan al estado nuevo s
10_12
, por lo que
el estado resultado (s
6_11
) tendr una transicin con el smbolo b al estado s
10_12
.
Es conveniente reflexionar acerca del orden en que se realizan las uniones. Si se hubiera in-
tentado unir esta pareja antes que s
10
- s
12
, la unin no habra sido posible. No debe preocupar
esta situacin, ya que, al ser el proceso iterativo, tarde o temprano se habra unificado la pareja
10 - 12, y despus de ella tambin la 6 - 11.
En cualquier caso el resultado es s
6_11
={A::=aAb {$,b}}
Captulo 4. Anlisis sintctico 161
s
0
s
5
S::=
S{$}
S::=
A{$}
S::=
xb{$}
A::=
aAb{$}
A::=
B{$}
B::=
x{$}
A::=a
Ab{$}
A::=
aAb{b}
A::=
B{b}
B::=
x{b}
a
A::=a
Ab{b}
A::=
aAb{b}
A::=
B{b}
B::=
x{b}
s
7
a
S::= S
{$}
A::= B
{$}
A::=aAb
{$}
S::=A
{$}
A::=aA
b{$}
S::=x
b{$}
B::=x
{$} S::=xb
{$} A::=B
{b}
A::=aAb
{b}
A::=aA
b{b}
B::=x
{b}
s
1
s
3 s
10
S
B
s
2
A
s
4
x
s
6
b
A
x
s
13
b
s
8
s
9
x
B
a
A
s
11
b
s
12
2 1
4
3 3
4
1 2
B
Figura 4.58. Diagrama de estados del autmata de anlisis LR(1) de la gramtica G
axb
en el
que se indican los conjuntos de smbolos distintos que slo difieren en los smbolos de
adelanto de sus configuraciones.
04-CAPITULO 04 9/2/06 11:50 Pgina 161
162 Compiladores e intrpretes: teora y prctica
s
0
s
5
S::=
S{$}
S::=
A{$}
S::=
xb{$}
A::=
aAb{$}
A::=
B{$}
B::=
x{$}
A::=a
Ab{$}
A::=
aAb{b}
A::=
B{b}
B::=
x{b}
a
A::=a
Ab{b}
A::=
aAb{b}
A::=
B{b}
B::=
x{b}
s
7
a
S::=S
{$}
A::=B
{$}
A::=aAb
{$}
s::=A
{$}
A::=aA
b{$}
S::=x
b{$}
B::=x
{$} S::=xb
{$} A::=B
{b} A::=aAb
{b}
A::=aA
b{b}
B::=x
{b}
s
1
s
3_8
s
10
S
B
s
2
A
s
4
x
s
5
b
A
x
s
13
b
s
8
s
9
x
B
a
A
s
11
b
s
12
B
B
B
s
0
s
5
S::=
S{$}
S::=
A{$}
S::=
xb{$}
A::=
aAb{$}
A::=
B{$}
B::=
x{$}
A::=a
Ab{$}
A::=
aAb{b}
A::=
B{b}
B::=
x{b}
a
A:;=a
Ab{b}
A::=
aAb{b}
A::=
B{b}
B::=
x{b}
s
7
a
S::=S
{$}
A::=B
{$,b} A::=aAb
{$}
S::=A
{$}
A::=aA
b{$}
S::=x
b{$}
B::=x
{$} S::=xb
{$} A::=aAb
{b}
A::=aA
b{b}
B::=x
{b}
s
1
s
3_8
s
10
S
B
s
2
A
s
4
x
s
6
b
A
x
s
13
b
s
9
x
a
A
s
11
b
s
12
B
B
a)
b)
Figura 4.59. Unin de los estados s
3
y s
8
en el nuevo estado s
3_8
. (a) Antes de la unin: se
resaltan las transiciones afectadas. (b) Despus de la unin: se resalta el estado resultado.
04-CAPITULO 04 9/2/06 11:50 Pgina 162
Captulo 4. Anlisis sintctico 163
s
0
s
5
S::=
S{$}
S::=
A{$}
S::=
xb{$}
A::=
aAb{$}
A::=
B{$}
B::=
x{$}
A::=a
Ab{$}
A::=
aAb{b}
A::=
B {b}
B::=
x{b}
a
A::=a
Ab{b}
A::=
aAb{b}
A::=
B{b}
B::=
x{b}
s
7
a
S::=S
{$}
A::=B
{b,$} A::=aAb
{$}
S::=A
{$}
A::=aA
b{$}
S::=x
b{$}
B::=x
{$} S::=xb
{$}
B::=x
{b}
s
1
s
3_8
s
10
S
B
s
2
A
s
4
x
s
6
b
A
x
s
13
b
s
9
x
a
A
b
B
B
s
0
s
5
S::=
S{$}
S::=
A{$}
S::=
xb{$}
A::=
aAb{$}
A::=
B{$}
B::=
x{$}
A::=a
Ab{$}
A::=
aAb{b}
A::=
B{b}
B::=
x{b}
a
A::=a
Ab{b}
A::=
aAb{b}
A::=
B{b}
B::=
x{b}
s
7
a
S::= S
{$}
A::=B
{b,$} A::=aAb
{$,b}
S::=A
{$}
A::=aA
b{$}
S::=x
b{$}
B::=x
{$} S::=xb
{$}
B::=x
{b}
s
1
s
3_8
s
10_12
S
B
s
2
A
s
4
x
s
6
b
A
x
s
13
b
s
9
x
a
B
B
a)
b)
A::=aA
b{b}
s
11
A::=aAb
{b}
s
12
A::=aA
b{b}
s
11
b
A
Figura 4.60. Unin de los estados s
10
y s
12
en el nuevo estado s
10_12
.
04-CAPITULO 04 9/2/06 11:50 Pgina 163
164 Compiladores e intrpretes: teora y prctica
a)
b)
s
0
s
5
S::=
S{$}
S::=
A{$}
S::=
xb{$}
A::=
aAb{$}
A::=
B{$}
B::=
x{$}
A::=a
Ab{$}
A::=
aAb{b}
A::=
B{b}
B::=
x{b}
a
A::=a
Ab{b}
A::=
aAb{b}
A::=
B{b}
B::=
x{b}
s
7
a
S::=S
{$}
A::=B
{b,$}
A::=aAb
{$,b}
S::=A
{$}
A::=aA
b{$}
S::=x
b{$}
B::=x
{$} S::=xb
{$}
B::=x
{b}
s
1 s
3_8 s
10_12
S
B
s
2
A
s
4
x
s
6
b
A
x
s
13
b
s
9
x
a
B
B
A::=aA
b{b}
s
11
b
A
s
0
s
5
S::=
S{$}
S::=
A{$}
S::=
xb{$}
A::=
aAb{$}
A::=
B{$}
B::=
x{$}
A::=a
Ab{$}
A::=
aAb{b}
A::=
B{b}
B::=
x{b}
a
A::=a
Ab{b}
A::=
aAb{b}
A::=
B{b}
B::=
x{b}
s
7
a
S::=S
{$}
A::=B
{b,$} A::=aAb
{$,b}
S::=A
{$}
A::=aA
b{$,b}
S::=x
b{$}
B::=x
{$} S::=xb
{$}
B::=x
{b}
s
1
s
3_8
s
10_12
S
B
s
2
A
s
4
x
s
6_11
b
A
x
s
13
b
s
9
x
a
B
B
A
Figura 4.61. Unin de los estados s
6
y s
11
en el nuevo estado s
6_11
.
04-CAPITULO 04 9/2/06 11:50 Pgina 164
Captulo 4. Anlisis sintctico 165
a)
b)
s
0
s
5
S::=
S{$}
S::=
A{$}
S::=
xb{$}
A::=
aAb{$}
A::=
B{$}
B::=
x{$}
A::=a
Ab{$}
A::=
aAb{b}
A::=
B{b}
B::=
x{b}
a
A::=a
Ab{b}
A::=
aAb{b}
A::=
B{b}
B::=
x{b}
s
7
a
S::=S
{$}
A::=B
{b,$} A::=aAb
{$,b}
S::=A
{$}
A::=aA
b{$,b}
S::=x
b{$}
B::=x
{$} S::=xb
{$}
B::=x
{b}
s
1
s
3_8
s
10_12
S
B
s
2
A
s
4
x
s
6_11
b
A
x
s
13
b
s
9
x
a
B
B
A
s
0
s
5_7
S::=
S{$}
S::=
A{$}
S::=
xb{$}
A::=
aAb{$}
A::=
B{$}
B::=
x{$}
A ::=a
Ab{$,b}
A::=
aAb{b}
A::=
B{b}
B::=
x{b}
a
a
S::=S
{$}
A::=B
{b,$}
A::=aAb
{$,b}
S::=A
{$}
A::=aA
b{$,b}
S::=x
b{$}
B::=x
{$} S::=xb
{$}
B::=x
{b}
s
1
s
3_8 s
10_12
S
B
s
2
A
s
4
x
s
6_11
b
A
x
s
13
b
s
9
B
Figura 4.62. Unin de los estados s
5
y s
7
en el nuevo estado s
5_7
.
Pareja s
5
- s
7
: La Figura 4.62 muestra el proceso para esta pareja.
04-CAPITULO 04 9/2/06 11:50 Pgina 165
Por razones anlogas (en este caso las transiciones potencialmente peligrosas llegan a s
3_8
y
a s
6_11
, que ya estn unificados) la unin es posible y el resultado es
s
5_7
={A::=aAb {$,b},
A::= aAb {b},
A::= B {b},
B::= x {b}}
Construccin del autmata de anlisis LALR(1)
a partir del autmata LR(1)
Se puede formalizar, en forma de pseudocdigo, el proceso descrito anteriormente, para calcular
el diagrama de estados del analizador LALR(1) a partir del autmata del analizador LR(1). En el
siguiente pseudocdigo, para representar la transicin desde el estado s
p
al estado s
d
mediante
el smbolo a, se utilizar la siguiente notacin:
(s
o
,a) ::= s
d
A modo de ejemplo, la parte b) de la Figura 4.62 muestra el diagrama de estados del autma-
ta de anlisis LALR(1) para la gramtica G
axb
.
Mientras haya cambios en el diagrama de estados:
Para cada pareja de estados s
i
y s
j
que cumplan que sus configuraciones slo difieren en
los smbolos de adelanto se realizar, si se puede, la siguiente unificacin:
Se crea un nuevo estado s
i_j
cuyo contenido se calcula mediante el siguiente proceso:
Para cada pareja de configuraciones
c
i
=x {s
1
,...,s
n
}s
i
c
j
=x {d
1
,...,d
m
}s
j
se aade a s
i_j
la configuracin
x {s
1
,...,s
n
}{d
1
,...,d
m
}
cuyas transiciones se calculan de la siguiente manera:
Cada transicin (s
o
,a)s
i
[dem. (s
o
,a)s
j
)] del autmata de anlisis
LR(1) origina en el autmata de anlisis LALR(1) una transicin (s
p
,a)s
i_j
.
Cada transicin (s
i
,a)s
d
[dem. (s
j
,a)s
d
)] del autmata de anlisis
LR(1) origina en el autmata de anlisis LALR(1) una transicin
(s
i_j
,a)s
d
. Obsrvese que esta operacin es la que podra causar que la uni-
ficacin fuera imposible, ya que el autmata tiene que seguir siendo determinis-
ta, y esto no sera posible si existiera algn smbolo para el que las transiciones
desde s
i
y desde s
j
no terminaran en el mismo estado.
166 Compiladores e intrpretes: teora y prctica
04-CAPITULO 04 9/2/06 11:50 Pgina 166
Construccin de tablas de anlisis LALR(1) a partir del autmata LALR(1)
El algoritmo de creacin de la tabla de anlisis LALR(1) es el mismo que en LR(1).
La Figura 4.63 muestra la tabla de anlisis LALR(1) de la gramtica G
axb
.
Captulo 4. Anlisis sintctico 167
E a
N
b
0
1
2
3_8
4
5_7
6_11
9
10 _12
d4
13
S
1
T
d5_7
r3
acc
r1
r5
r4
d13
Accin
r2
A
2
B
3_8
Ir_a
x $ S
d5_7 d9
d10_12
r5
6_11 3_8
r4
r3
Figura 4.63. Tabla de anlisis LALR(1) para la gramtica G
axb
.
Otros algoritmos para construir LALR(1) sin pasar por LR(1)
El algoritmo descrito en este captulo para llegar a LALR(1) mediante LR(1) no es el ms efi-
ciente para la generacin automtica de analizadores LALR(1). Existen versiones de este algo-
ritmo que construyen directamente el diagrama de estados del analizador LALR(1) sin necesidad
de construir el analizador LR(1).
Evaluacin de la tcnica
Es fcil comprobar que, debido al mecanismo de construccin del analizador LALR(1), no se
pueden aadir conflictos reduccin / desplazamiento a los que ya tuviera el analizador LR(1).
Por otra parte, aunque no en todos los casos se consigue reducir el tamao de las tablas y de los
diagramas, a veces se consigue la potencia de un analizador LR(1) con el tamao de un analiza-
dor LR(0). Esto hace que LALR(1) sea la tcnica de anlisis ascendente ms extendida.
04-CAPITULO 04 9/2/06 11:50 Pgina 167
De hecho, existen herramientas informticas de libre distribucin (como yacc o bison) que
construyen automticamente analizadores de este tipo. Usualmente estas herramientas no slo
generan el analizador sintctico, sino que aaden ms componentes, proporcionando esqueletos
de compiladores e intrpretes. En captulos sucesivos, tras describir otras componentes de los
compiladores necesarias para entender estas herramientas, se proporcionar una breve descripcin
de las mismas. Tambin se puede consultar en http://www.librosite.net/pulido en-
laces de inters, documentacin detallada y ejemplos de uso.
Gramticas de precedencia simple
El mtodo del anlisis sintctico mediante gramticas de precedencia simple tiene utilidad, y pue-
de ser ms eficiente que otros, en los lenguajes de expresiones, que desempean un papel im-
portante en el acceso a bases de datos, en las frmulas de las hojas de clculo, y en otras
aplicaciones.
En esta seccin se utilizarn dos conjuntos especiales, llamados first(U) y last(U), que
se definen de la siguiente manera:
Sea una gramtica G = (
T
,
N
, S, P). Sea =
T
N
. Sea U un smbolo de esta gra-
mtica. Se definen los siguientes conjuntos asociados a estas gramticas y a este smbolo:
first(U) = {V | U + Vx, V, x
*
}
last(U) = {V | U + xV, V, x
*
}
Obsrvese que, en estas funciones, U
N
, es decir, U tiene que ser no terminal.
Estos conjuntos se calculan con facilidad aplicando conceptos de la teora algebraica de rela-
ciones, aunque esto nos fuerza a establecer las siguientes restricciones en la gramtica:
Los smbolos no terminales distintos del axioma no contienen reglas no generativas
(reglas de la forma U::=). Si existieran reglas as, se puede obtener una gram-
tica equivalente que no las contenga, aplicando el procedimiento explicado en la
Seccin 1.12.5.
Si el axioma genera la palabra vaca, las reglas que definen sus derivaciones directas no de-
ben ser recursivas. Si lo fuesen, se puede construir una gramtica equivalente que cumpla
esta condicin, introduciendo un nuevo axioma que genere directamente el axioma antiguo,
y aplicando el procedimiento de la Seccin 1.12.5 para trasladar la cadena vaca al nuevo
axioma.
Por ejemplo, sea la gramtica ({a,b,c,d}, {S,B}, S, P), donde P contiene las si-
guientes reglas de produccin:
S ::= a S b | B
B ::= c B d |
4.4
168 Compiladores e intrpretes: teora y prctica
04-CAPITULO 04 9/2/06 11:50 Pgina 168
Esta gramtica tiene una regla no generativa en el smbolo B, que no es el axioma. Para eli-
minarla, hay que aadir las reglas que se obtienen sustituyendo B por en todas las partes dere-
chas, de donde resulta:
S ::= a S b | B |
B ::= c B d | cd
Esta gramtica cumple la primera condicin, pero no la segunda, pues el axioma es recursivo
y genera la palabra vaca. Aplicando el mtodo propuesto, se puede transformar en la siguiente
gramtica equivalente:
S::= S
S ::= a S b | B |
B ::= c B d | cd
Ahora se elimina la regla no generativa, con lo que se obtiene la siguiente gramtica:
S::= S |
S ::= a S b | a b | B
B ::= c B d | cd
Esta gramtica cumple las dos restricciones anteriores y es totalmente equivalente a la gra-
mtica de partida (genera el mismo lenguaje).
4.4.1. Notas sobre la teora de relaciones
Sea un conjunto A. Una relacin sobre los elementos de A se define como RAA, es decir, un
conjunto de pares de elementos de A.
Sea (a,b)R un par de elementos de A que estn en relacin R (se representa aRb). La re-
lacin R se puede definir tambin por enumeracin de sus elementos: R = {(a,b) | aRb}.
Sea una relacin R entre elementos de A. Se llama relacin transpuesta de R a la relacin
R definida as: aRb bRa.
Una relacin R se llama reflexiva si todos los elementos aA cumplen que aRa.
Una relacin R se llama transitiva si todos los elementos a,b,cA cumplen que aRb
bRc aRc.
Sean dos relaciones R, P entre elementos de A. Se dice que dos elementos a,bA estn en
la relacin producto de R y P, y se representa aRPb, si existe un elemento cA tal que aRc
cPb. El producto de relaciones cumple la propiedad asociativa.
Se llama potencia de una relacin R, y se representa R
n
, al producto de R por s misma n
veces. Se define R
1
=R y R
0
=I, donde I es la relacin identidad, definida as: aIb a=b.
Se llama clausura transitiva de una relacin R a la siguiente relacin:
R
+
=
i=1
R
i
Obviamente, aRbaR
+
b. Adems, cualquiera que sea R, R
+
es transitiva.
Captulo 4. Anlisis sintctico 169
04-CAPITULO 04 9/2/06 11:50 Pgina 169
Se llama clausura reflexiva y transitiva de una relacin R a la siguiente relacin:
R
*
=
i=0
R
i
Obviamente, aRbaR
*
b. Adems, cualquiera que sea R, R* es transitiva y reflexiva.
Teorema 4.4.1. Si A es un conjunto finito de n elementos y R es una relacin sobre los ele-
mentos de A, entonces aR
+
b aR
k
b para algn k positivo menor o igual que n. Esto significa
que, si A es finito,
R
+
=
n
i=1
R
i
Demostracin:
aR
+
b aR
p
b para algn p>0. Pero aR
p
b s
1
, s
2
, ..., s
p
tal que a=s
1
, s
1
Rs
2
,
s
2
Rs
3
, , s
p-1
Rs
p
, s
p
Rb (por definicin de R
p
). Supongamos que p es mnimo y p>n.
Entonces, como A slo contiene n elementos, mientras que la sucesin de s
i
contiene p>n, debe
haber elementos repetidos en dicha sucesin. Sea s
i
=s
j
, j>i. Entonces, a=s
1
, s
1
Rs
2
, s
2
Rs
3
,
, s
i-1
Rs
i
, s
i
=s
j
, s
j
Rs
j+1
, , s
p-1
Rs
p
, s
p
Rb, y por tanto existe una sucesin ms corta,
con p-(j-i) trminos intermedios, para pasar de a a b. Esto contradice la hiptesis de que p>n
sea mnimo. Luego p tiene que ser menor o igual que n.
4.4.2. Relaciones y matrices booleanas
Se llama matriz booleana aquella cuyos elementos son valores lgicos, representados por los n-
meros 1 (verdadero) y 0 (falso). Las operaciones lgicas clsicas (, ) pueden aplicarse a los
elementos o a las matrices en la forma usual. El producto booleano de matrices se define igual
que el producto matricial ordinario, sustituyendo la suma por la operacin y la multiplicacin
por la operacin . El producto booleano de dos matrices B y C se representa con el smbolo
B.C.
Sea A un conjunto finito de n elementos, y sea R una relacin sobre los elementos de A. Se
puede representar R mediante una matriz booleana B de n filas y n columnas, donde b
ij
=1
a
i
Ra
j
, y 0 en caso contrario. La aplicacin M(R)=B, que pasa de las relaciones a las matrices
booleanas, es un isomorfismo, pues tiene las siguientes propiedades:
M(R) = (M(R))
La matriz booleana de la relacin transpuesta de R es la matriz transpuesta de la matriz co-
rrespondiente a R.
M(R P) = M(R) M(P)
La matriz booleana de la unin de dos relaciones es la unin lgica de las dos matrices
booleanas correspondientes.
170 Compiladores e intrpretes: teora y prctica
04-CAPITULO 04 9/2/06 11:50 Pgina 170
M(RP) = M(R) . M(P)
La matriz booleana de la relacin producto de otras dos es el producto booleano de las dos
matrices correspondientes.
M(R
n
) = (M(R))
n
La matriz booleana de la potencia ensima de una relacin es la potencia ensima de la ma-
triz booleana de la relacin. Se llama potencia ensima de B el producto booleano de B por
s misma n veces. Adems, B
0
es la matriz unidad.
M(R
+
) = (M(R))
+
La matriz booleana de la clausura transitiva de una relacin es la clausura transitiva de la
matriz de la relacin, definida esta ltima operacin como:
B
+
=
i=1
B
i
Como consecuencia del Teorema 4.4.1, se cumple que
B
+
=
n
i=1
B
i
4.4.3. Relaciones y conjuntos importantes
de la gramtica
Recurdese la definicin del conjunto first(U) al principio de la Seccin 4.4:
first(U) = {V | U + Vx, V, x
*
}
Esta expresin define el conjunto first(U) en funcin de la relacin +, que a su vez ac-
ta sobre elementos de
*
, que es un conjunto infinito, por lo que no se puede aplicar el
Teorema 4.4.1. Para obtener un algoritmo que permita calcular fcilmente first(U), se puede
definir la siguiente relacin, que acta sobre conjuntos finitos:
U F V U::=Vx P, V, x
*
Se calcula el cierre transitivo de F:
U F
+
V U::=V
1
x
1
P, V
1
::=V
2
x
2
P, , V
n
::=Vx
n+1
P
V
1
,V
2
,...,V
n
N
, V, x
1
,x
2
,...,x
n+1
*
De la expresin anterior se deduce que
U F
+
V u + Vx
Captulo 4. Anlisis sintctico 171
04-CAPITULO 04 9/2/06 11:50 Pgina 171
por tanto,
first(U) = {V | U F
+
V, V}
Pero la relacin F est definida sobre un conjunto finito , y se puede aplicar el
Teorema 4.4.1.
De la misma forma en que se ha definido la relacin F y el conjunto first(U), se puede
definir la siguiente relacin y el siguiente conjunto:
U L V U::= xV P, V, x
*
last(U) = {V | U L
+
V}
Sea la gramtica ({0,1,2,3,4,5,6,7,8,9}, {N,C}, N, {N::=NC|C,
C::=0|1|2|3|4|5|6|7|8|9}). Para calcular first(C), se empieza definiendo la rela-
cin F:
Por tanto,
F = {(N,N), (N,C), (C,0), (C,1), (C,2), ..., (C,9)}
Ejemplo
4.12
172 Compiladores e intrpretes: teora y prctica
Regla Relacin
N::=NC N F N
N::=C N F C
C::=0 C F 0
C::=1 C F 1
C::=2 C F 2
C::=3 C F 3
C::=4 C F 4
C::=5 C F 5
C::=6 C F 6
C::=7 C F 7
C::=8 C F 8
C::=9 C F 9
04-CAPITULO 04 9/2/06 11:50 Pgina 172
La matriz booleana equivalente a F es la siguiente matriz B:
N C 0 1 2 3 4 5 6 7 8 9
N: 1 1 0 0 0 0 0 0 0 0 0 0
C: 0 0 1 1 1 1 1 1 1 1 1 1
0: 0 0 0 0 0 0 0 0 0 0 0 0
1: 0 0 0 0 0 0 0 0 0 0 0 0
2: 0 0 0 0 0 0 0 0 0 0 0 0
3: 0 0 0 0 0 0 0 0 0 0 0 0
4: 0 0 0 0 0 0 0 0 0 0 0 0
5: 0 0 0 0 0 0 0 0 0 0 0 0
6: 0 0 0 0 0 0 0 0 0 0 0 0
7: 0 0 0 0 0 0 0 0 0 0 0 0
8: 0 0 0 0 0 0 0 0 0 0 0 0
9: 0 0 0 0 0 0 0 0 0 0 0 0
donde las filas y las columnas corresponden a los smbolos {N,C,0,1,2,3,4,5,
6,7,8,9}, en ese orden. De aqu se puede calcular que B
2
= B
3
= ... = la siguiente matriz:
N C 0 1 2 3 4 5 6 7 8 9
N: 1 1 1 1 1 1 1 1 1 1 1 1
C: 0 0 0 0 0 0 0 0 0 0 0 0
0: 0 0 0 0 0 0 0 0 0 0 0 0
1: 0 0 0 0 0 0 0 0 0 0 0 0
2: 0 0 0 0 0 0 0 0 0 0 0 0
3: 0 0 0 0 0 0 0 0 0 0 0 0
4: 0 0 0 0 0 0 0 0 0 0 0 0
5: 0 0 0 0 0 0 0 0 0 0 0 0
6: 0 0 0 0 0 0 0 0 0 0 0 0
7: 0 0 0 0 0 0 0 0 0 0 0 0
8: 0 0 0 0 0 0 0 0 0 0 0 0
9: 0 0 0 0 0 0 0 0 0 0 0 0
(En cuanto dos potencias de la matriz coinciden, las restantes son todas iguales). B
+
es la
unin booleana de todas las matrices (las dos) anteriores:
N C 0 1 2 3 4 5 6 7 8 9
N: 1 1 1 1 1 1 1 1 1 1 1 1
C: 0 0 1 1 1 1 1 1 1 1 1 1
0: 0 0 0 0 0 0 0 0 0 0 0 0
1: 0 0 0 0 0 0 0 0 0 0 0 0
2: 0 0 0 0 0 0 0 0 0 0 0 0
3: 0 0 0 0 0 0 0 0 0 0 0 0
4: 0 0 0 0 0 0 0 0 0 0 0 0
5: 0 0 0 0 0 0 0 0 0 0 0 0
6: 0 0 0 0 0 0 0 0 0 0 0 0
7: 0 0 0 0 0 0 0 0 0 0 0 0
8: 0 0 0 0 0 0 0 0 0 0 0 0
9: 0 0 0 0 0 0 0 0 0 0 0 0
Captulo 4. Anlisis sintctico 173
04-CAPITULO 04 9/2/06 11:50 Pgina 173
Por tanto,
F
+
= {(N,N), (N,C), (N,0), (N,1), (N,2), ..., (N,9),
(C,0), (C,1), (C,2), ..., (C,9)}
Y as se tiene que:
first(C) = {0, 1, 2, ..., 9}
Ahora se calcula el conjunto last(N). Para ello, se define la relacin L:
La matriz de la relacin L
+
se calcula igual que la de F
+
y sale:
N C 0 1 2 3 4 5 6 7 8 9
N: 0 1 1 1 1 1 1 1 1 1 1 1
C: 0 0 1 1 1 1 1 1 1 1 1 1
0: 0 0 0 0 0 0 0 0 0 0 0 0
1: 0 0 0 0 0 0 0 0 0 0 0 0
2: 0 0 0 0 0 0 0 0 0 0 0 0
3: 0 0 0 0 0 0 0 0 0 0 0 0
4: 0 0 0 0 0 0 0 0 0 0 0 0
5: 0 0 0 0 0 0 0 0 0 0 0 0
6: 0 0 0 0 0 0 0 0 0 0 0 0
7: 0 0 0 0 0 0 0 0 0 0 0 0
8: 0 0 0 0 0 0 0 0 0 0 0 0
9: 0 0 0 0 0 0 0 0 0 0 0 0
Luego last(N) es igual a {C,0,1,2,3,4,5,6,7,8,9}.
174 Compiladores e intrpretes: teora y prctica
Regla Relacin L
N::=NC N L C
N::=C N L C
C::=0 C L 0
C::=1 C L 1
C::=2 C L 2
C::=3 C L 3
C::=4 C L 4
C::=5 C L 5
C::=6 C L 6
C::=7 C L 7
C::=8 C L 8
C::=9 C L 9
04-CAPITULO 04 9/2/06 11:50 Pgina 174
4.4.4. Relaciones de precedencia
Dada una gramtica limpia G de axioma S, se definen las siguientes relaciones de precedencia
entre los smbolos del vocabulario =
T
N
. Para todo U,V:
U =. V W::=xUVy P
U <. V W::=xUTy P, T F
+
V en P.
U >. V W::=xTRy P, T L
+
U, R F
*
V en P.
U <.= V R <. S R =. S
U >.= V R >. S R =. S
Teorema 4.4.2. U =. V UV aparece en el asidero de alguna forma sentencial.
Prueba:
U =. V W::=xUVy P
G es limpia S * uWv existe un rbol que genera uWv.
Se poda ese rbol hasta que U est en un asidero (todo smbolo ha de ser alguna vez parte
de un asidero).
Se aplica la regla W::=xUVy. Como U era parte del asidero, el asidero de este rbol nuevo
debe ser xUVy, q.e.d.
UV es parte de un asidero. Por definicin de asidero existe una regla W::=xUVy U =.
V, q.e.d.
Teorema 4.4.3. U >. V existe una forma sentencial xUVy donde U es el smbolo final de
un asidero.
Prueba:
U >. V W::= xTRy P, T L
+
U, R F
*
V en P
G es limpia S * uWv + uxTRyv
T L
+
U T + tU S + uxtURyv
Se dibuja este rbol y se reduce hasta que U sea parte del asidero. Por construccin, tendr
que ser su ltimo smbolo.
R F
*
V R * Vw S + uxtUVwyv
Se aade al rbol anterior esta ltima derivacin. El asidero no ha cambiado, U sigue sien-
do el ltimo smbolo y va seguido por V, q.e.d.
U es la cola de un asidero y va seguido por V. Se reduce hasta que V est en el asidero. Esto
da un rbol para la forma sentencial xTVy, donde T * tU.
Captulo 4. Anlisis sintctico 175
04-CAPITULO 04 9/2/06 11:50 Pgina 175
Si T y V estn los dos en el asidero, existe W ::= uTVv U >. V, q.e.d.
Si V es cabeza del asidero, reducimos hasta llegar a xTWw donde T est en el asidero. Ahora,
W + Vy W tiene que estar en el asidero, pues, si no, T sera la cola y habra sido aside-
ro antes que V. Luego U >. V, q.e.d.
Teorema 4.4.4. U <. V existe una forma sentencial xUVy donde V es el smbolo inicial
de un asidero.
Se demuestra de forma anloga.
4.4.5. Gramtica de precedencia simple
Definicin: G es una Gramtica de Precedencia Simple o Gramtica de Precedencia (1,1) si:
1. G cumple las condiciones especificadas al principio de la Seccin 4.4.
2. Slo existe, como mucho, una relacin de precedencia entre dos smbolos cualesquiera
del vocabulario.
3. No existen en G dos producciones con la misma parte derecha.
Se llama tambin Gramtica de Precedencia (1,1) porque slo se usa un smbolo a la izquier-
da y la derecha de un posible asidero para decidir si lo es.
Si no se cumplen las condiciones anteriores, a veces se puede manipular la gramtica para in-
tentar que se cumplan. La recursividad a izquierdas o a derechas suele dar problemas (salen dos
relaciones de precedencia con los smbolos que van antes o despus del recursivo). Para evitarlo,
se puede estratificar la gramtica, aadiendo smbolos nuevos, pero, si hay muchas reglas, esto
es muy pesado. De todos modos, no siempre es posible obtener una gramtica de precedencia
simple equivalente a una dada, pues no todos los lenguajes independientes del contexto pueden
representarse mediante gramticas de precedencia simple.
Supondremos que toda forma sentencial queda encuadrada entre dos smbolos especiales de
principio y fin de cadena,
y
+ <. U y U >.
.
Teorema 4.4.5. Una gramtica de precedencia simple no es ambigua. Adems, el asidero de
una forma sentencial U
1
... U
n
es la subcadena U
i
... U
j
, situada ms a la izquierda tal que:
U
i-1
<. U
i
=. U
i+1
=. U
i+2
=. ... =. U
j-1
=. U
j
>. U
j+1
Prueba:
Si U
i
... U
j
es asidero, se cumple la relacin por los teoremas anteriores.
Reduccin al absurdo. Se cumple la relacin y no es asidero. Entonces ninguna poda la har
asidero, pues si en algn momento posterior resultara ser la rama completa ms a la iz-
quierda, ya lo es ahora.
Si no es asidero, cada uno de los smbolos de U
i-1
U
i
... U
j
U
j+1
debe aparecer ms pron-
to o ms tarde como parte de un asidero. Sea U
k
el primero que aparece.
176 Compiladores e intrpretes: teora y prctica
04-CAPITULO 04 9/2/06 11:50 Pgina 176
1. Si U
k
= U
i-1
, de los teoremas se sigue que U
i-1
=. U
i
o U
i-1
>. U
i
, lo que contradice
que U
i-1
<. U
i
(no puede haber dos relaciones entre dos smbolos).
2. Si U
k
= U
j+1
, de los teoremas se sigue que U
j
=. U
j+1
o U
j
<. U
j+1
, lo que contradice
que U
j
>. U
j+1
.
3. Si i-1<k<j+1, ocurre lo siguiente:
a. Que U
i-1
no puede estar en el asidero (pues entonces U
i-1
=. U
i
, contradiccin).
b. Que U
j+1
no puede estar en el asidero (pues entonces U
j
=. U
j+1
, contradiccin).
c. Que ningn smbolo U
p
, i<p<=k puede ser cabeza del asidero (pues entonces U
p-1
<. U
p
, contradiccin).
d. Que ningn smbolo U
p
, k<=p<j puede ser cola del asidero (pues entonces U
p
<.
U
p+1
, contradiccin).
e. Luego la cabeza del asidero debe ser U
i
y la cola U
j
, lo que contradice que U
i
...
U
j
no era el asidero, q.e.d.
Como el asidero es nico (por construccin) y slo puede ser parte derecha de una regla (por
ser la Gramtica de Precedencia Simple), slo se puede aplicar una regla para reducir. Luego la
gramtica no es ambigua.
4.4.6. Construccin de las relaciones
La relacin =. se construye por simple observacin de las partes derechas de las reglas
(vase la definicin de =.).
La relacin <. se construye fcilmente utilizando la representacin matricial de las rela-
ciones y teniendo en cuenta que:
<. =. . F
+
donde . es el producto booleano de matrices. (Por definicin del producto de relaciones
y la definicin de <. y =.).
La relacin >. se construye de manera parecida:
>. (L
+
) . =. . F
*
donde es la transposicin de matrices. La demostracin queda como ejercicio.
4.4.7. Algoritmo de anlisis
1. Se almacenan las producciones de la gramtica en una tabla.
2. Se construye la matriz de precedencia MP de dimensiones NN (N = cardinal o nmero
de elementos de ), tal que
Captulo 4. Anlisis sintctico 177
04-CAPITULO 04 9/2/06 11:50 Pgina 177
MP(i,j) = 0 si no existe relacin entre U
i
y U
j
= 1 si U
i
<. U
j
= 2 si U
i
=. U
j
= 3 si U
i
>. U
j
3. Se inicializa una pila con el smbolo
y se aade el smbolo
al final de la cadena de
entrada.
4. Se compara el smbolo situado en la cima de la pila con el siguiente smbolo de entrada.
5. Si no existe ninguna relacin, error sintctico: cadena rechazada (fin del algoritmo).
6. Si existe la relacin <. o la relacin =., se introduce el smbolo de entrada en la pila y
se elimina de la entrada. Volver al paso 4.
7. Si la relacin es >., el asidero termina en la cima de la pila.
8. Se recupera el asidero de la pila, sacando smbolos hasta que el smbolo en la cima de
la pila est en relacin <. con el ltimo sacado.
9. Se compara el asidero con las partes derechas de las reglas.
10. Si no coincide con ninguna, error sintctico: cadena rechazada (fin del algoritmo).
11. Si coincide con una, se coloca la parte izquierda de la regla en el extremo izquierdo de
la cadena que queda por analizar.
12. Si en la pila slo queda
<. aacbb
<.a<.a.<.c >. bb
c S::=c
<.a<.a =. Sbb
<.a<.a=.S =. bb
<.a<.a=.S=.b >. b
aSb S::=aSb
<.a =. Sb
<.a=.S =. b
<.a=.S=.b >.
aSb S::=aSb
<. S
<. aabb
<.a<.a No hay bb
<. acbb
<.a<.c >. bb
c S::=c
<.a =. Sbb
<.a=.S =. bb
<.a=.S=.b >. b
aSb S::=aSb
<. Sb
<.S =. b
<.S=.b >.
Sb No hay
Luego la cadena es rechazada.
4.4.8. Funciones de precedencia
La matriz de precedencias ocupa NN posiciones de memoria. A veces es posible construir dos
funciones de precedencia que ocupen slo 2N. Esta operacin se llama linealizacin de la ma-
triz. Dichas funciones, de existir, no son nicas (hay infinitas).
Para el ejemplo anterior valen las dos funciones siguientes:
S a b c
f 0 2 2 3 3
g 2 3 2 3 0
Si es posible construir f y g, se verifica que
f(U)=g(V) U =. V
f(U)<g(V) U <. V
f(U)>g(V) U >. V
Existen matrices que no se pueden linealizar. Ejemplo:
A B
A = >
B = =
04-CAPITULO 04 9/2/06 11:50 Pgina 180
En este caso, debera cumplirse que:
f(A) = g(A)
f(A) > g(B)
f(B) = g(A)
f(B) = g(B)
Es decir:
f(A) > g(B) = f(B) = g(A) = f(A) f(A) > f(A)
Con lo que se llega a una contradiccin. Cuando no hay inconsistencias, el siguiente algorit-
mo construye las funciones de precedencia:
Se dibuja un grafo dirigido con 2N nodos, llamados f
1
, f
2
, , f
N
, g
1
, g
2
, , g
N
,
con un arco de f
i
a g
j
si U
i
>.= U
j
y un arco de g
j
a f
i
si U
i
<.= U
j
.
A cada nodo se le asigna un nmero igual al nmero total de nodos accesibles desde l (in-
cluido l mismo). El nmero asignado a f
i
se toma como valor de f(U
i
) y el asignado a
g
i
es g(U
i
).
Demostracin:
Si U
i
=. U
j
, hay una rama de f
i
a g
j
y viceversa, luego cualquier nodo accesible desde f
i
es accesible desde g
j
y viceversa. Luego f(U
i
)=g(U
j
).
Si U
i
>. U
j
, hay una rama de f
i
a g
j
. Luego cualquier nodo accesible desde g
j
es accesi-
ble desde f
i
. Luego f(U
i
)>=g(U
j
).
Si f(U
i
)=g(U
j
), existe un camino cerrado f
i
g
j
f
k
g
l
g
m
f
i
, lo que implica
que U
i
>. U
j
, U
k
<.= U
j
, U
k
>.= U
l
, U
i
<.= U
m
y reordenando:
U
i
>.U
j
>.=U
k
>.=U
l
>.=>.=U
m
>.=U
i
, es decir: f(U
i
)>f(U
i
), con lo que se llega a
una contradiccin. Luego el caso de igualdad de valores de las funciones queda excluido y
se deduce que f(U
i
)>g(U
j
).
Si U
i
<. U
j
, la demostracin es equivalente.
La Figura 4.64 describe la aplicacin de este algoritmo al ejemplo anterior, y explica cmo se
obtuvieron las funciones mencionadas al principio de esta seccin.
Captulo 4. Anlisis sintctico 181
g(S)
g(a) g(b) g(c)
f(S)
f(a) f(b) f(c)
Figura 4.64. Grafo utilizado para la construccin de las funciones de precedencia.
04-CAPITULO 04 9/2/06 11:50 Pgina 181
El algoritmo descrito puede mecanizarse. El grafo descrito equivale a la relacin existe un
arco del nodo x al nodo y. Dicha relacin posee su matriz Booleana correspondiente, de dimen-
siones 2N2N, que llamaremos B, y que puede representarse as:
(0) (>.=)
(<.=) (0)
A partir de B, construimos B*. Entonces se verifica que f(U
i
)es igual al nmero de unos en
la fila i, mientras g(U
i
) es igual al nmero de unos en la fila N+i.
En el ejemplo anterior, B resulta ser la matriz:
0 0 0 0 0 0 1 0
0 0 0 0 1 0 0 0
0 0 0 0 0 0 1 0
0 0 0 0 0 0 1 0
0 1 0 0 0 0 0 0
0 1 0 0 0 0 0 0
1 0 0 0 0 0 0 0
0 1 0 0 0 0 0 0
A partir de B, obtenemos B*, que resulta ser la matriz:
1 0 0 0 0 0 1 0
0 1 0 0 1 0 0 0
1 0 1 0 0 0 1 0
1 0 0 1 0 0 1 0
0 1 0 0 1 0 0 0
0 1 0 0 1 1 0 0
1 0 0 0 0 0 1 0
0 1 0 0 1 0 0 1
Con lo que las dos funciones f y g resultan ser las indicadas al principio de esta seccin. Para
completarlas, basta aadir los smbolos de principio y fin de cadena, a los que se asigna el va-
lor 0. A continuacin se muestran de nuevo los resultados obtenidos. Obsrvese que, si se suma
cualquier nmero entero a todos los valores de f y g, se obtienen dos nuevas funciones que tam-
bin cumplen todas las condiciones. Por eso, si existe un par de funciones f y g, tendremos in-
finitas, todas equivalentes.
S a b c
f 0 2 2 3 3
g 2 3 2 3 0
El uso de las funciones supone cierta prdida de informacin respecto al uso de la matriz, pues
desaparecen los lugares vacos en la tabla de precedencias, que conducan directamente a la de-
teccin de algunos casos de error. Sin embargo, estos casos sern detectados ms tarde, de otra
manera. Para comprobarlo, analicemos por medio de las funciones la cadena aabb y veremos
que en este caso basta con un paso ms para detectar la condicin de error.
182 Compiladores e intrpretes: teora y prctica
04-CAPITULO 04 9/2/06 11:50 Pgina 182
Resumen
En este captulo se describen algunos de los mtodos que suelen utilizarse para construir los ana-
lizadores sintcticos de los lenguajes independientes del contexto. En una primera seccin del ca-
ptulo se describen los conjuntos primero y siguiente asociados a una gramtica, pues son
necesarios para describir los mtodos que aparecen en el resto del captulo. Estos mtodos de an-
lisis pueden clasificarse en dos categoras: anlisis descendente y anlisis ascendente. Dentro del
anlisis descendente se describe, en primer lugar, el anlisis descendente con vuelta atrs cuya
ineficiencia se soluciona con las gramticas LL(1) y el mtodo de anlisis descendente selectivo.
Las operaciones fundamentales de los algoritmos de anlisis ascendente son el desplazamien-
to de los smbolos de entrada necesarios para reconocer los asideros y la reduccin de los mis-
mos. Estas tcnicas se basan en la implementacin del autmata a pila para la gramtica
independiente del contexto del lenguaje considerado y ste, a su vez, en el autmata finito que
reconoce los asideros del anlisis. Hay diferentes maneras de construir este autmata finito. Los
analizadores estudiados en orden creciente de potencia, son LR(0), SLR(1), LR(1) y LALR(1).
Su principal diferencia consiste en que, para reducir una regla, comprueban condiciones ms es-
trictas respecto a los prximos smbolos que el anlisis encontrar en la entrada: SLR(1) tiene en
cuenta el smbolo siguiente y LR(1) y LALR(1) consideran de forma explcita un smbolo de ade-
lanto. Los algoritmos LR y LALR se podran generalizar para valores de k>1 pero la compleji-
dad que implica gestionar ms de un smbolo de adelanto desaconseja su uso.
Otro mtodo de anlisis ascendente, que no se utiliza mucho en compiladores completos, pero
s en analizadores de expresiones, hojas de clculo, bases de datos, etc., utiliza gramticas de pre-
cedencia simple. El mtodo se basa en tres relaciones, llamadas de precedencia, que permiten lo-
calizar los asideros del anlisis con un algoritmo muy sencillo y eficiente. Estas relaciones
pueden construirse fcilmente, por simple observacin de las reglas de produccin, o de forma
automtica, mediante operaciones realizadas sobre matrices booleanas. Por ltimo, es posible
mejorar el algoritmo sustituyendo las matrices por dos funciones de precedencia.
Ejercicios
1. Considrese el lenguaje de los tomos en lenguaje Prolog. Todos ellos tienen un identifica-
dor, y pueden tener cero, uno o varios argumentos. Cuando el nmero de argumentos es ma-
4.6
4.5
Captulo 4. Anlisis sintctico 183
Pila f(x) g(x) Relacin Entrada Asidero Regla a aplicar
0 3 <. aabb
<.a<.a 2 2 =. bb
<.a<.a=.b 3 2 >. b
ab No hay
04-CAPITULO 04 9/2/06 11:50 Pgina 183
yor o igual que 1, stos aparecen entre parntesis, y separados por comas; en caso contra-
rio, no se escriben los parntesis. Finalmente, cada uno de los argumentos de un tomo pue-
de ser otro tomo, un nmero o una variable (que comienza con letra mayscula).
Supondremos, adems, que al final de cada palabra del lenguaje hay un punto. Por ejemplo,
las siguientes expresiones seran vlidas:
atomo.
pepe(a(b,c)).
paco(0,1,2,X).
Supondremos que el analizador morfolgico ya ha identificado los identificadores, los
nmeros y las variables, por lo que no es necesario incluir las reglas para reconocer stos
en la gramtica del lenguaje. Proporcionar una gramtica LL(1) para este lenguaje.
Construir la matriz de anlisis LL(1) para la gramtica proporcionada.
2. Con la matriz de anlisis LL(1) del ejercicio anterior, analizar las siguientes cadenas:
atomo.
pepe(a(b,c)).
paco(variable(0)).
3. Sea el lenguaje {a
n
b
m+n
a
m
| m,n>=0}. Construir una gramtica LL(1) que lo reconozca.
Construir el analizador sintctico correspondiente. Analizar las siguientes palabras: aabbba,
aaabb.
4. Dado el lenguaje L = {w | w tiene un nmero par de ceros y unos}, construir una gramti-
ca LL(1) que lo describa. Construir el analizador sintctico correspondiente.
5. Construir una gramtica independiente del contexto que describa el lenguaje
{a
n
b
p
c
m+p
d
n+m
| m,n,p>0}. Construir una gramtica LL(1) que describa el mismo len-
guaje.
6. Sea el lenguaje formado por todas las palabras con las letras a y b, con doble nmero de a
que de b, pero con todas las b seguidas, situadas en un extremo de la cadena. Construir una
gramtica LL(1) que lo reconozca.
7. Sea el lenguaje {ab(ab)
n
ac(ac)
n
| n>=0}. Construir una gramtica LL(1) que lo re-
conozca y el analizador sintctico correspondiente. Analizar las siguientes palabras: ababa-
cac, abacac.
8. Dada la gramtica
A ::= B a | a
B ::= A b | b
C ::= A c | c
Construir una gramtica LL(1) equivalente y un analizador sintctico descendente que
analice el lenguaje correspondiente. Analizar las siguientes palabras: abab, baba, ababa,
babab.
184 Compiladores e intrpretes: teora y prctica
04-CAPITULO 04 9/2/06 11:50 Pgina 184
9. Sea el lenguaje formado por todas las palabras no vacas con las letras a y b, con el mismo
nmero de a que de b, pero con todas las b seguidas, sin ninguna restriccin para las a.
Construir una gramtica LL(1) que lo reconozca. Construir un analizador sintctico des-
cendente que la analice. Analizar las siguientes palabras: bbaa, baba.
10. Sea el siguiente lenguaje sobre el alfabeto {a,b}: (aa*+)b+bb*. Construir una gra-
mtica LL(1) que reconozca el mismo lenguaje. Escribir el analizador sintctico descen-
dente correspondiente. Utilizando el analizador anterior, analizar las siguientes cadenas:
aaab, aabb.
11. Sea el siguiente lenguaje sobre el alfabeto {a,b}: {a
m
b c
m
a
n
b c
n
| m,n>0} U {b
p
|
p>0}. Construir una gramtica LL(1) que reconozca el mismo lenguaje. Escribir el anali-
zador sintctico top-down correspondiente. Utilizando el analizador anterior, analizar las si-
guientes cadenas: abbc, bbb.
12. Construir una gramtica que reconozca el lenguaje {a
n
b
m
| 0<=n<m}. Convertir la gra-
mtica anterior a la forma normal de Greibach. Convertir la gramtica anterior a la forma
LL(1). Escribir un analizador sintctico descendente que analice el lenguaje anterior.
13. Sea el siguiente lenguaje sobre el alfabeto {a,b}: { a
n
b
n
| n0 } U { a }. Construir
una gramtica LL(1) que reconozca el mismo lenguaje. Escribir el analizador sintctico top-
down correspondiente. Utilizando el analizador anterior, analizar las siguientes cadenas:
a, b, ab, aab, aabb.
14. Construir una gramtica LL(1) que reconozca el lenguaje {a
n
b
m
c
n+m
| n,m>=0}.
Escribir un analizador sintctico descendente que analice el lenguaje anterior.
15. Construir una gramtica LL(1) para el lenguaje {a
n
b
m
c
n
| n,m>=0} y programar el
analizador sintctico correspondiente.
16. Utilizar la tabla de anlisis de la Figura 4.30 para realizar el anlisis sintctico de la cade-
na id*id+id. Es sintcticamente correcta la cadena?
17. Utilizar la tabla de anlisis de la Figura 4.37 para realizar el anlisis sintctico de la cade-
na i+(i+i). Es sintcticamente correcta la cadena?
18. Utilizar la tabla de anlisis de la Figura 4.37 para realizar el anlisis sintctico de la cade-
na (i+i. Es sintcticamente correcta la cadena?
19. Utilizar la tabla de anlisis SLR(1) de la Figura 4.47 para realizar el anlisis sintctico de
la siguiente cadena y determinar si es sintcticamente correcta o no.
begin
dec;
ejec;
ejec
end
Captulo 4. Anlisis sintctico 185
04-CAPITULO 04 9/2/06 11:50 Pgina 185
20. Utilizar la tabla de anlisis SLR(1) de la Figura 4.47 para realizar el anlisis sintctico de
la siguiente cadena y determinar si es sintcticamente correcta o no.
begin
dec;
ejec;
end
21. Dada la gramtica independiente del contexto que se puede deducir de las siguientes reglas
de produccin en las que el axioma es el smbolo E:
(1)E::=E+E
(2)E::=E*E
(3)E::=i
Construir el diagrama de estados del analizador SLR(1) y la tabla de anlisis para determi-
nar si la gramtica es o no SLR(1). Obsrvese que esta gramtica es ambigua ya que no es-
tablece prioridad entre las operaciones aritmticas. Analizar el efecto de la ambigedad en
los posibles conflictos de la gramtica y el significado que aporta a la ambigedad solucio-
narlos mediante la seleccin de una de las operaciones en conflicto. Comprobar los resul-
tados en el anlisis de la cadena i*i+i*i+i.
22. Utilizar la tabla de anlisis LR(1) de la Figura 4.56 para realizar el anlisis sintctico de la
cadena aaxbb y determinar si es sintcticamente correcta o no.
23. Utilizar la tabla de anlisis LR(1) de la Figura 4.56 para realizar el anlisis sintctico de la
cadena ax y determinar si es sintcticamente correcta o no.
24. Repetir el ejercicio anterior con la tabla LALR(1) de la Figura 4.62.
25. Construir la tabla de anlisis sintctico ascendente para la siguiente gramtica que corres-
ponde a al subconjunto (dedicado a la declaracin de variables) de la gramtica de un len-
guaje de programacin imaginario.
<declaration> ::= <mode> <idlist>
<mode> ::= bool
<mode> ::= int
<idlist> ::= <id>
<idlist> ::= <id> , <idlist>
Obsrvese que en la regla 1 aparece un espacio en blanco entre los smbolos no terminales
<mode> e <idlist>. Considerar el smbolo <declaration> como el axioma.
Contestar razonadamente a las siguientes preguntas
La gramtica del apartado anterior es una gramtica LR(0)? Por qu?
La gramtica del apartado anterior es una gramtica SLR(1)? Por qu?
Utilizando la tabla de anlisis desarrollada en el apartado (1), analizar la siguiente decla-
racin:
int x,y
186 Compiladores e intrpretes: teora y prctica
04-CAPITULO 04 9/2/06 11:50 Pgina 186
26. La tabla de anlisis sintctico SLR(1) para la siguiente gramtica est incompleta. Con-
siderar que S es el axioma.
S ::= id (L)
S ::= id
L ::=
L ::= S Q
Q ::=
Q ::=, S Q
Captulo 4. Anlisis sintctico 187
Accin Ir a
id ( ) , $ S L Q
0 d2 1
1 acc
2
3 d2 5 4
4 d6
5 d8 7
6
7
8 d2 9
9 10
10
26.1. Completar las casillas sombreadas detallando los clculos realizados al efecto.
26.2. Rellenar las casillas que correspondan a operaciones de reduccin detallando los
clculos realizados al efecto.
27. Realizar el anlisis de la cadena var int inst utilizando la tabla de anlisis SLR(1) que
se proporciona y que corresponde a la gramtica
S ::= S inst
S ::= S var D
S ::=
D ::= D ident E
D ::= D ident sep
D ::= int
D ::= float
E ::= S fproc
04-CAPITULO 04 9/2/06 11:50 Pgina 187
188 Compiladores e intrpretes: teora y prctica
Accin Ir a
inst var ident sep int float fproc $ S D E
0 r3 r3 r3 r3 1
1 d2 d3 acc
2 r1 r1
3 d5 d6
4 r2 r2 d7 r2 r3
5 r6 r6 r6 r6 r6
6 r7 r7 r7 r7 r7
7 r3 r3 d10 r3 r3 9 8
8 r4 r4 r4 r4 r4
9 d2 d2 d11
10 r5 r5 r5 r5 r5
11 r8 r8 r8 r8 r8
28. Dada la siguiente gramtica (considerar que E es el axioma)
E ::= (L)
E ::= a
L ::= L,E
L ::= E
Cul sera el resultado de aplicar la operacin de clausura o cierre al estado formado por
el elemento LR(1) E::=(L) {$} (o en una notacin equivalente, el elemento LR(1)
(1,1,$))? Contestar razonadamente.
29. Sea el lenguaje sobre el alfabeto {a,b} formado por todas las palabras que empiezan por
a y acaban por b. Construir una gramtica SLR(1) que reconozca el mismo lenguaje y su
tabla de anlisis.
30. Construir una gramtica SLR(1), con la cadena vaca en alguna parte derecha, que reco-
nozca el lenguaje {a
n
b
m
| 0n<m}. Construir tambin la tabla de anlisis y utilizarla en el
anlisis de las cadenas abb, aab. La gramtica anterior es LR(0)? Por qu?
31. Construir una gramtica SLR(1) que describa el lenguaje {a
n
b
n+p+q
a
p
c
q
|n,p,q>=0}.
Construir la tabla del anlisis correspondiente. Analizar las cadenas abbac, abbbac y
abbbbac.
32. Dado el lenguaje L = { w | w tiene un nmero par de ceros y unos }, construir una gra-
mtica SLR(1) que lo describa. Construir la tabla del anlisis correspondiente.
04-CAPITULO 04 9/2/06 11:50 Pgina 188
33. Construir una gramtica SLR(1) y la tabla de anlisis para el lenguaje {a
n
c
m
b
n
|
m>0,n>=0}.
34. Sea el siguiente lenguaje sobre el alfabeto {a,b}: {a
n
b
n
| n>=0} {a}.
34.1. Construir una gramtica SLR(1) que reconozca el mismo lenguaje.
34.2. Construir la tabla de anlisis.
34.3. Utilizando el analizador anterior, analizar las siguientes cadenas: a, b, ab, aab,
aabb.
35. Sea el siguiente lenguaje sobre el alfabeto {a,b} : {a
m
bc
m
a
n
bc
n
| m,n>0 } {b
p
| p>0 }.
35.1. Construir una gramtica SLR(1), con la cadena vaca en alguna parte derecha, que
reconozca el mismo lenguaje.
35.2. Construir la tabla de anlisis.
35.3. Utilizando la tabla anterior, analizar las siguientes cadenas: abcabc, abbc.
36. Sea el siguiente lenguaje sobre el alfabeto {a,b} representado mediante su expresin re-
gular(aa*+)b+bb*, donde es la palabra vaca.
36.1. Construir una gramtica SLR(1), con la palabra vaca en alguna parte derecha, que
reconozca el mismo lenguaje.
36.2. Construir la tabla de anlisis.
36.3. Utilizando la tabla anterior, analizar las siguientes cadenas: aaab, aabb.
37. Sea el lenguaje del Ejercicio 4.29.
37.1. Construir una gramtica de precedencia simple que lo reconozca.
37.2. Construir la matriz de relaciones de precedencia.
38. Para el lenguaje del Ejercicio 4.30.
38.1. Construir una gramtica sin la cadena vaca que lo reconozca.
38.2. Construir la matriz de relaciones de precedencia. Explicar por qu no es de prece-
dencia simple.
39. En el lenguaje Smalltalk, los operadores binarios (+,-,*,/) no tienen precedencia intrnse-
ca, sino posicional: el operador situado ms a la izquierda se ejecuta primero, salvo por la
presencia de parntesis, que modifican la precedencia de la manera habitual. Los operandos
bsicos pueden ser identificadores o constantes numricas:
39.1. Construir una gramtica que represente el lenguaje de las expresiones binarias en
Smalltalk.
39.2. Es de precedencia simple esta gramtica?
40. Construir una gramtica de precedencia simple y una matriz de precedencia para el lengua-
je del Ejercicio 4.33.
41. Sea el lenguaje del Ejercicio 4.34.
41.1. Construir una gramtica de precedencia simple que lo reconozca.
41.2. Construir la matriz de relaciones de precedencia.
Captulo 4. Anlisis sintctico 189
04-CAPITULO 04 9/2/06 11:50 Pgina 189
41.3. Utilizando la matriz anterior y el algoritmo estndar, analizar las siguientes cadenas:
a, b, ab, aab, aabb.
42. Sea el lenguaje del Ejercicio 4.35.
42.1. Construir una gramtica de precedencia simple, sin la cadena vaca en ninguna par-
te derecha, que lo reconozca.
42.2. Construir la matriz de relaciones de precedencia.
42.3. Utilizando la matriz anterior y el algoritmo estndar, analizar las siguientes cadenas:
abcabc, abbcb, abbc.
43. Sea el lenguaje del Ejercicio 4.36.
43.1. Construir una gramtica de precedencia simple, sin la cadena vaca en ninguna par-
te derecha, que lo reconozca.
43.2. Construir la matriz de relaciones de precedencia.
43.3. Utilizando la matriz anterior y el algoritmo estndar, analizar las siguientes cadenas:
aaab, aabb.
44. Encontrar una gramtica cuyo lenguaje sea el conjunto de los nmeros enteros pares.
45. En la gramtica anterior, calcular las relaciones F , L , F
+
, L
+
.
46. En la gramtica anterior, construir los conjuntos first(S), last(S), donde S es el
axioma.
47. Demostrar que R
+
es transitiva, cualquiera que sea R.
190 Compiladores e intrpretes: teora y prctica
04-CAPITULO 04 9/2/06 11:50 Pgina 190
Captulo 5
Anlisis semntico
Introduccin al anlisis semntico
5.1.1. Introduccin a la semntica de los lenguajes
de programacin de alto nivel
El anlisis semntico es la fase del compilador en la que se comprueba la correccin semntica
del programa.
En la Seccin 1.13.4 se reflexion acerca de la distincin entre la sintaxis y la semntica de
los lenguajes de programacin de alto nivel. Tambin se explic que las gramticas del tipo 0 de
Chomsky, el nico tipo de gramtica que tiene la expresividad necesaria para representar todos
los aspectos de estos lenguajes, presenta demasiadas dificultades para su diseo y gestin. De
esta forma se justifica que, en el tratamiento de los lenguajes de programacin, se distingan las
construcciones sintcticas (usualmente independientes del contexto) de las semnticas (usual-
mente dependientes).
En el Captulo 4 se han tratado con detalle los algoritmos necesarios para el anlisis sintcti-
co, que normalmente aborda los aspectos independientes del contexto. Se ha podido comprobar
que todos ellos son relativamente simples. El objetivo de este captulo es incorporar la semnti-
ca al anlisis del programa que se est compilando.
Sera deseable disponer de una herramienta parecida a las gramticas independientes del
contexto, a la que se pudiera incorporar de forma sencilla la comprobacin de las condicio-
nes semnticas. Si se dispusiera de ella, el analizador semntico se reducira a una extensin
de los algoritmos de anlisis sintctico, para incorporar la gestin de los aspectos semn-
ticos.
5.1
05-CAPITULO 05 9/2/06 11:51 Pgina 191
En este captulo se ver que las gramticas de atributos proporcionan una herramienta muy ade-
cuada para el anlisis semntico, se explicar cmo pueden solucionar los problemas asociados con
la semntica de los programas compilados y se describirn algunas aplicaciones existentes, que re-
ciben como entrada gramticas de atributos y generan de forma automtica analizadores semnticos.
5.1.2. Objetivos del analizador semntico
El analizador semntico es la parte del compilador que realiza el anlisis semntico. Suele estar
compuesto por un conjunto de subrutinas independientes, que pueden ser invocadas por los ana-
lizadores morfolgico y sintctico.
Se puede considerar que el analizador semntico recibe, como entrada, el rbol del anlisis del
programa, una vez realizado el anlisis morfolgico y sintctico. Esta distincin es ms bien con-
ceptual, ya que, en los compiladores reales, a menudo estas fases se entremezclan. Suele descri-
birse el anlisis semntico como un proceso mediante el cual se aade al rbol de derivacin una
serie de anotaciones, que permiten determinar la correccin semntica del programa y preparar
la generacin de cdigo. Por lo tanto, la salida que genera el anlisis semntico, en el caso de que
no haya detectado errores, es un rbol de derivacin con anotaciones semnticas. Dichas anota-
ciones se pueden usar para comprobar que el programa es semnticamente correcto, de acuerdo
con las especificaciones del lenguaje de programacin. Hay que comprobar, por ejemplo, que:
Cuando se utiliza un identificador, ste ha sido declarado previamente.
Se ha asignado valor a las variables antes de su uso.
Los ndices para acceder a los arrays estn dentro del rango vlido.
En las expresiones aritmticas, los operandos respetan las reglas sobre los tipos de datos
permitidos por los operadores.
Cuando se invoca un procedimiento, ste ha sido declarado adecuadamente. Adems, el n-
mero, tipo y posicin de cada uno de sus argumentos debe ser compatible con la declaracin.
Las funciones contienen al menos una instruccin en la que se devuelve su valor al progra-
ma que las invoc.
Se est compilando el siguiente programa, escrito en un lenguaje de programacin ficticio, cuya
sintaxis resultar fcil de comprender para cualquier programador:
begin
int A;
A := 100;
A := A + A;
output A
end
Resulta claro que el programa declara una variable de tipo entero y nombre A, le asigna ini-
cialmente el valor 100 y posteriormente el de la suma de A consigo misma; finalmente, se escri-
Ejemplo
5.1
192 Compiladores e intrpretes: teora y prctica
05-CAPITULO 05 9/2/06 11:51 Pgina 192
be en algn medio externo el ltimo valor de A. La Figura 5.1 resume la accin del anlisis se-
mntico en relacin con este programa.
Captulo 5. Anlisis semntico 193
<Programa>
begin <declrcns> ;
<declrcn>
<tipo> <ids>
int
<sntncs>
end
<sntnc> <sntncs>
<id> <id> := <expr>
A
100
<const.int>
<asignacion> <sntnc> <sntncs> ;
<asignacion> <sntnc>
<id> := <expr> <salida>
A <expr> + <expr> <expr> output
<id> <id> <id>
A A A
A
a)
;
<Programa>
begin <declrcns> ;
<declrcn>
<tipo> <ids>
int
<sntncs>
end
<sntnc> <sntncs>
<id> <id> := <expr>
A
100
<const.int>
<asignacion> <sntnc> <sntncs> ;
<asignacion> <sntnc>
<id> := <expr> <salida>
A <expr> + <expr> <expr> output
<id> <id> <id>
A A A
A
b)
; int
int A
int
int int
int int
int
int
int
int
int
int
int
Figura 5.1. Ejemplo de un posible resultado del anlisis semntico. a) Entrada del analizador
semntico: el rbol de derivacin. b) Salida: el rbol anotado.
05-CAPITULO 05 9/2/06 11:51 Pgina 193
La parte a) de la Figura 5.1 muestra el rbol de derivacin del programa, de acuerdo con una
gramtica que no hace falta especificar. Este rbol sera la entrada que recibe el analizador se-
mntico. La parte b) muestra el rbol, tras aadirle la siguiente informacin:
En el smbolo no terminal <dclrcn>, asociado con la declaracin de la variable A, se ha
aadido el tipo de sta: int.
Al smbolo no terminal <dclrcns>, se le ha aadido la lista de identificadores declara-
dos con sus tipos: int A. Esta lista podra utilizarse para aadir en la tabla de smbolos la
informacin correspondiente.
En la primera aparicin del smbolo no terminal <id> como primer hijo del smbolo
<asignacion>, se ha anotado que el tipo del identificador A, que se conoce desde su de-
claracin, es int.
En el smbolo no terminal <expr> que aparece como hermano del recin analizado <id>,
se ha anotado que el tipo de la expresin es tambin entero, ya que la constante 100, que
es el fragmento de la entrada derivado de <expr>, es un nmero entero.
Las anotaciones de los dos ltimos puntos pueden servir para comprobar que la asignacin
es correcta, ya que los tipos del identificador y de la expresin son compatibles.
En el nodo del smbolo <asignacion>, padre del subrbol estudiado, puede anotarse
que se ha realizado una asignacin correcta de valores de tipo entero.
El subrbol cuya raz es la ltima aparicin de <asignacion> presenta un caso anlogo
al anteriormente descrito: las apariciones del identificador A en la parte derecha de la asig-
nacin obligan a consultar el tipo con que fue declarado. Se trata, por lo tanto, de asignar
una expresin de tipo entero a un identificador de tipo entero. Las anotaciones de este su-
brbol permiten realizar todas las comprobaciones necesarias.
El subrbol correspondiente a la ltima aparicin de <expr> en la instruccin que impri-
me el valor de la variable A contiene anotaciones con el tipo de la variable y el de la ex-
presin.
5.1.3. Anlisis semntico y generacin de cdigo
En funcin del procedimiento utilizado para generar el programa objeto, se distinguen los si-
guientes tipos de compiladores (vase la Figura 5.2):
Compiladores de un solo paso: integran la generacin de cdigo con el anlisis semnti-
co. Estos compiladores generan directamente el cdigo a partir del rbol de la derivacin.
En este caso, las llamadas a las rutinas que escriben el cdigo ensamblador suelen entre-
mezclarse con el anlisis semntico.
Compiladores de dos o ms pasos: en el primer paso de la compilacin, el analizador se-
mntico genera un cdigo abstracto denominado cdigo intermedio. En un segundo paso se
realiza la generacin del cdigo definitivo a partir del cdigo intermedio. A veces se sepa-
ra tambin la optimizacin de cdigo, la cual se realiza en un tercer paso independiente.
194 Compiladores e intrpretes: teora y prctica
05-CAPITULO 05 9/2/06 11:51 Pgina 194
Las representaciones intermedias facilitan la optimizacin de cdigo. En este libro se van
a describir dos tipos de representaciones intermedias: la notacin sufija, que se utiliza es-
pecialmente para las expresiones aritmticas, y la que utiliza tuplas o vectores (usualmen-
te cudruplas) para representar las instrucciones que deben ser ejecutadas.
La notacin sufija intenta sacar provecho de que la mayora de los procesadores disponen
de una pila para almacenar datos auxiliares, y de que la evaluacin de expresiones con esta
notacin puede realizarse con facilidad mediante el uso de una pila auxiliar.
Para la representacin intermedia que utiliza tuplas, se abstraen primero las operaciones
disponibles en un lenguaje ensamblador hipottico, lo suficientemente genrico para poder
representar cualquier ensamblador real. El objetivo de esa abstraccin es decidir el nmero
de componentes de las tuplas y la estructura de la informacin que contienen. Por ejemplo,
es frecuente considerar que la primera posicin sea ocupada por la operacin que se va a
realizar, las dos siguientes por sus operandos y la cuarta y ltima por el resultado. La cer-
cana de esta representacin a los lenguajes simblicos (procesados por ensambladores) fa-
cilita la generacin del cdigo. La abstraccin introducida por las tuplas independiza esta
representacin de los detalles correspondientes a una mquina concreta, lo que ofrece ven-
tajas respecto a su portabilidad.
Cuando se utilizan representaciones intermedias, la generacin de cdigo se reduce a un
nuevo problema de traduccin (de la representacin intermedia al lenguaje objeto final),
con la ventaja de que las representaciones intermedias son mucho ms fciles de traducir
que los lenguajes de programacin de alto nivel.
Las representaciones intermedias podran considerarse parte del anlisis semntico, ya que
proporcionan formalismos para la representacin de su resultado. Sin embargo, en este li-
bro se ha decidido describirlas con detalle en el captulo dedicado a la generacin de cdi-
go. Por un lado, las tcnicas y algoritmos necesarios para generar tuplas son anlogos a los
necesarios para generar cdigo simblico o en lenguaje de la mquina, lo que aconseja que
ambos tipos de generadores de cdigo sean descritos en el mismo captulo. Para simplifi-
Captulo 5. Anlisis semntico 195
Fuente
Fuente
Objeto
Objeto
Cdigo
intermedio
Anlisis
semntico
Generacin
Cdigo
Anlisis
semntico
COMPILADOR DE UN PASO
COMPILADOR DE DOS O MS PASOS
Figura 5.2. Esquema reducido de la compilacin en uno y dos pasos.
05-CAPITULO 05 9/2/06 11:51 Pgina 195
car la exposicin, tambin se incluir en el captulo de generacin de cdigo la otra repre-
sentacin intermedia: la notacin sufija.
En general, los compiladores de un solo paso suelen ser ms rpidos, pero ms complejos, por
lo que existen muchos compiladores comerciales construidos en dos y tres pasos.
5.1.4. Anlisis semntico en compiladores
de un solo paso
En los compiladores de un paso, no se utilizan representaciones intermedias, ya que la genera-
cin del cdigo objeto se entremezcla con el anlisis semntico. En estos compiladores resulta
ms complicado utilizar tcnicas de optimizacin de cdigo y de gestin de memoria. La
Figura 5.3 muestra un esquema de esta situacin.
En la parte izquierda de la figura se ven las dos primeras fases de la compilacin, que han sido
explicadas en los captulos anteriores. En la parte inferior se muestra la cadena de unidades sin-
tcticas correspondientes al programa del Ejemplo 5.1:
(<palabra clave>,begin)
(<tipo>,int)(<id>,A)(<simb>,;)
(<id>,A)(<simbm>,:=)(<cons int>,100)(<simb>,;)
(<id>,A)(<simbm>,:=)(<id>,A)(<simb>,+)(<id>,A)(<simb>,;)
(<palabra clave>,output)(<id>,A)
(palabra clave>,end)
Se ha utilizado una representacin de pares para cada unidad sintctica, en los que el primer
elemento representa el tipo y el segundo el texto concreto que corresponde en el programa a esa
unidad. En este ejemplo se utilizan los siguientes nombres de unidades sintcticas:
<palabra clave>, para las palabras claves del lenguaje.
<tipo>, para las palabras que representan tipos en las declaraciones de variables y proce-
dimientos.
<id>, para los identificadores de las variables y los procedimientos.
<simb>, para caracteres especiales.
<simbm>, para palabras formadas por ms de un carcter especial.
<cons int>, para nmeros enteros.
En el rbol de la parte izquierda de la Figura 5.3 aparece el resultado del anlisis sintctico:
el rbol de derivacin de la parte a). A veces es necesario modificar la tabla de smbolos durante
los anlisis morfolgico y sintctico. La parte superior muestra esa posibilidad.
La parte derecha de la Figura 5.3 contiene el resultado del anlisis semntico. En la mitad su-
perior est el rbol de derivacin con anotaciones semnticas; en la inferior, un posible cdigo
simblico equivalente al programa de partida. Como el compilador es de un solo paso, el cdigo
debe generarse mientras se realiza el anlisis semntico.
196 Compiladores e intrpretes: teora y prctica
05-CAPITULO 05 9/2/06 11:51 Pgina 196
Captulo 5. Anlisis semntico 197
A
.
S
i
n
t
.
y
S
e
m
.
A
n
l
i
s
i
s
m
o
r
f
o
+
s
i
n
t
c
t
i
c
o
C
d
i
g
o
<
P
r
o
g
r
a
m
a
>
b
e
g
i
n
<
d
e
c
l
r
c
n
s
>
;
<
d
e
c
l
r
c
n
>
<
t
i
p
o
>
<
i
d
s
>
i
n
t
<
s
n
t
n
c
s
>
e
n
d
<
s
n
t
n
c
>
<
s
n
t
n
c
s
>
<
i
d
>
<
i
d
>
:
=
<
e
x
p
r
>
A
1
0
0
<
c
o
n
s
t
.
i
n
t
>
<
a
s
i
g
n
a
c
i
o
n
>
<
s
n
t
n
c
>
<
s
n
t
n
c
s
>
;
<
a
s
i
g
n
a
c
i
o
n
>
<
s
n
t
n
c
>
<
i
d
>
:
=
<
e
x
p
r
>
<
s
a
l
i
d
a
>
A
<
e
x
p
r
>
+
<
e
x
p
r
>
<
e
x
p
r
>
o
u
t
p
u
t
<
i
d
>
<
i
d
>
<
i
d
>
A
A
A
A
;
i
n
t
i
n
t
A
i
n
t
i
n
t
i
n
t
i
n
t
i
n
t
i
n
t
i
n
t
i
n
t
i
n
t
i
n
t
i
n
t
<
P
r
o
g
r
a
m
a
>
b
e
g
i
n
<
d
e
c
l
r
c
n
s
>
;
<
d
e
c
l
r
c
n
>
<
t
i
p
o
>
<
i
d
s
>
i
n
t
<
s
n
t
n
c
s
>
e
n
d
<
s
n
t
n
c
>
<
s
n
t
n
c
s
>
<
i
d
>
<
i
d
>
:
=
<
e
x
p
r
>
A
1
0
0
<
c
o
n
s
t
.
i
n
t
>
<
a
s
i
g
n
a
c
i
o
n
>
<
s
n
t
n
c
>
<
s
n
t
n
c
s
>
;
<
a
s
i
g
n
a
c
i
o
n
>
<
s
n
t
n
c
>
<
i
d
>
:
=
<
e
x
p
r
>
<
s
a
l
i
d
a
>
A
<
e
x
p
r
>
+
<
e
x
p
r
>
<
e
x
p
r
>
o
u
t
p
u
t
<
i
d
>
<
i
d
>
<
i
d
>
A
A
A
A
;
s
e
g
m
e
n
t
.
d
a
t
a
_
A
d
d
0
s
e
g
m
e
n
t
.
c
o
d
i
g
o
g
l
o
b
a
l
_
m
a
i
n
_
m
a
i
n
p
u
s
h
d
w
o
r
d
1
0
0
p
o
p
e
a
x
m
o
v
[
_
A
]
.
e
a
x
p
u
s
h
d
w
o
r
d
[
_
A
]
p
o
p
e
d
x
a
d
d
e
a
x
,
e
d
x
p
u
s
h
e
a
x
p
o
p
e
a
x
m
o
v
[
_
A
]
,
e
a
x
p
u
s
h
d
w
o
r
d
[
_
A
}
p
o
p
e
a
x
p
u
s
h
e
a
x
c
a
l
l
i
m
p
r
i
m
e
_
e
n
t
e
r
o
a
d
d
e
s
p
,
4
c
a
l
l
i
m
p
r
i
m
e
_
f
i
n
_
l
i
n
e
a
r
e
t
E
l
e
m
e
n
t
o
T
i
p
o
V
a
l
o
r
<
k
f
>
i
n
t
A
(
<
p
a
l
a
b
r
a
c
l
a
v
e
>
,
b
e
g
i
n
)
(
<
t
i
p
o
>
,
i
n
t
)
(
<
i
d
>
,
A
)
(
<
s
i
m
b
>
,
;
)
(
<
i
d
>
,
A
)
(
<
s
i
m
b
m
>
,
;
=
)
(
<
c
o
n
s
t
i
n
t
>
,
1
0
0
)
(
<
s
i
m
b
>
,
;
)
(
<
i
d
>
,
A
)
(
<
s
i
m
b
m
>
,
;
=
)
(
<
i
d
>
,
A
)
(
<
s
i
m
b
>
,
+
)
(
<
i
d
>
,
A
)
(
s
i
m
b
>
,
;
)
(
<
p
a
l
a
b
r
a
c
l
a
v
e
>
,
o
u
t
p
u
t
)
(
<
i
d
>
,
A
)
(
<
p
a
l
a
b
r
a
c
l
a
v
e
>
,
e
n
d
)
i
n
t
F
i
g
u
r
a
5
.
3
.
E
s
q
u
e
m
a
g
r
f
i
c
o
d
e
t
a
l
l
a
d
o
d
e
l
p
r
o
c
e
s
o
d
e
c
o
m
p
i
l
a
c
i
n
e
n
u
n
s
o
l
o
p
a
s
o
.
05-CAPITULO 05 9/2/06 11:51 Pgina 197
198 Compiladores e intrpretes: teora y prctica
A
.
S
e
m
.
A
n
l
i
s
i
s
m
o
r
f
o
+
s
i
n
t
c
t
i
c
o
C
d
i
g
o
<
P
r
o
g
r
a
m
a
>
b
e
g
i
n
<
d
e
c
l
r
c
n
s
>
;
<
d
e
c
l
r
c
n
>
<
t
i
p
o
>
<
i
d
s
>
i
n
t
<
s
n
t
n
c
s
>
e
n
d
<
s
n
t
n
c
>
<
s
n
t
n
c
s
>
<
i
d
>
<
i
d
>
:
=
<
e
x
p
r
>
A
1
0
0
<
c
o
n
s
t
.
i
n
t
>
<
a
s
i
g
n
a
c
i
o
n
>
<
s
n
t
n
c
>
<
s
n
t
n
c
s
>
;
<
a
s
i
g
n
a
c
i
o
n
>
<
s
n
t
n
c
>
<
i
d
>
:
=
<
e
x
p
r
>
<
s
a
l
i
d
a
>
A
<
e
x
p
r
>
+
<
e
x
p
r
>
<
e
x
p
r
>
o
u
t
p
u
t
<
i
d
>
<
i
d
>
<
i
d
>
A
A
A
A
;
i
n
t
i
n
t
A
i
n
t
i
n
t
i
n
t
i
n
t
i
n
t
i
n
t
i
n
t
i
n
t
i
n
t
i
n
t
i
n
t
<
P
r
o
g
r
a
m
a
>
b
e
g
i
n
<
d
e
c
l
r
c
n
s
>
;
<
d
e
c
l
r
c
n
>
<
t
i
p
o
>
<
i
d
s
>
i
n
t
<
s
n
t
n
c
s
>
e
n
d
<
s
n
t
n
c
>
<
s
n
t
n
c
s
>
<
i
d
>
<
i
d
>
:
=
<
e
x
p
r
>
A
1
0
0
<
c
o
n
s
t
.
i
n
t
>
<
a
s
i
g
n
a
c
i
o
n
>
<
s
n
t
n
c
>
<
s
n
t
n
c
s
>
;
<
a
s
i
g
n
a
c
i
o
n
>
<
s
n
t
n
c
>
<
i
d
>
:
=
<
e
x
p
r
>
<
s
a
l
i
d
a
>
A
<
e
x
p
r
>
+
<
e
x
p
r
>
<
e
x
p
r
>
o
u
t
p
u
t
<
i
d
>
<
i
d
>
<
i
d
>
A
A
A
A
;
s
e
g
m
e
n
t
.
d
a
t
a
_
A
d
d
0
s
e
g
m
e
n
t
.
c
o
d
i
g
o
g
l
o
b
a
l
_
m
a
i
n
_
m
a
i
n
p
u
s
h
d
w
o
r
d
1
0
0
p
o
p
e
a
x
m
o
v
[
_
A
]
.
e
a
x
p
u
s
h
d
w
o
r
d
[
_
A
]
P
O
P
e
d
x
a
d
d
e
a
x
,
e
d
x
p
u
s
h
e
a
x
p
o
p
e
a
x
m
o
v
[
_
A
]
,
e
a
x
p
u
s
h
d
w
o
r
d
[
_
A
}
p
o
p
e
a
x
p
u
s
h
e
a
x
c
a
l
l
i
m
p
r
i
m
e
_
e
n
t
e
r
o
a
d
d
e
s
p
,
4
c
a
l
l
i
m
p
r
i
m
e
_
f
i
n
_
l
i
n
e
a
r
e
t
E
l
e
m
e
n
t
o
T
i
p
o
V
a
l
o
r
<
k
f
>
i
n
t
A
(
<
p
a
l
a
b
r
a
c
l
a
v
e
>
,
b
e
g
i
n
)
(
<
t
i
p
o
>
,
i
n
t
)
(
<
i
d
>
,
A
)
(
<
s
i
m
b
>
,
;
)
(
<
i
d
>
,
A
)
(
<
s
i
m
b
m
>
,
;
=
)
(
<
c
o
n
s
t
i
n
t
>
,
1
0
0
)
(
<
s
i
m
b
>
,
;
)
(
<
i
d
>
,
A
)
(
<
s
i
m
b
m
>
,
;
=
)
(
<
i
d
>
,
A
)
(
<
s
i
m
b
>
,
+
)
(
<
i
d
>
,
A
)
(
s
i
m
b
>
,
;
)
(
<
p
a
l
a
b
r
a
c
l
a
v
e
>
,
o
u
t
p
u
t
)
(
<
i
d
>
,
A
)
(
<
p
a
l
a
b
r
a
c
l
a
v
e
>
,
e
n
d
)
F
i
c
h
e
r
o
i
n
t
e
r
m
e
d
i
o
?
G
e
n
e
r
a
c
i
n
c
d
i
g
o
,
O
p
t
i
m
i
z
a
c
i
n
G
.
M
e
m
o
r
i
a
i
n
t
F
i
g
u
r
a
5
.
4
.
E
s
q
u
e
m
a
g
r
f
i
c
o
d
e
t
a
l
l
a
d
o
d
e
l
p
r
o
c
e
s
o
d
e
c
o
m
p
i
l
a
c
i
n
e
n
m
s
d
e
u
n
p
a
s
o
.
05-CAPITULO 05 9/2/06 11:51 Pgina 198
5.1.5. Anlisis semntico en compiladores de ms
de un paso
Cuando se disea un compilador de ms de un paso, se utilizan representaciones intermedias para
facilitar las fases de optimizacin de cdigo y gestin de memoria que se realizarn en los pasos
siguientes. La Figura 5.4 muestra grficamente esta situacin.
Obsrvese que la salida del anlisis semntico es una representacin intermedia, que en la fi-
gura aparece contenida en un fichero. Las representaciones intermedias no tienen necesariamen-
te que ocupar espacio en disco. En la Figura 5.4 se ve cmo un compilador, construido de acuerdo
con este esquema, puede terminar la fase de generacin de cdigo optimizado realizando pasos
adicionales sobre la representacin intermedia.
Gramticas de atributos
5.2.1. Descripcin informal de las gramticas de atributos
y ejemplos de introduccin
Informalmente, se llamar atributos de un smbolo de la gramtica a toda informacin aadida
en el rbol de derivacin por el analizador semntico, asociada a los smbolos de los nodos ano-
tados. El Ejemplo 5.1 describe de forma simplificada el valor de esos atributos y sugiere cmo
calcularlos. La otra componente importante de las gramticas de atributos es el algoritmo de
clculo de los valores. Todos estos aspectos se presentarn con ms detalle mediante ejemplos.
Considrese la gramtica independiente del contexto para las expresiones aritmticas enteras aso-
ciada a las siguientes reglas de produccin:
E::=E+E
E::=-E
E::=E*E
E::=(E)
E::=i
E::=c
Se supone que los smbolos terminales c e i hacen referencia a las constantes numricas y los
identificadores, respectivamente.
Los lenguajes de programacin suelen proporcionar reglas para determinar correctamente el
valor y el tipo de las expresiones aritmticas. En nuestro ejemplo, el ltimo aspecto es trivial, ya
que todas van a ser de tipo entero, pero en futuros ejemplos se podr comprobar su importancia.
Es evidente que los smbolos de la gramtica que representan a los operandos tienen que poseer
informacin respecto al tipo y el valor de stos, mientras los que se refieren a los operadores de-
ben realizar la operacin correspondiente, es decir, aplicar las reglas del lenguaje para calcular el
valor y el tipo de la expresin en la que aparecen.
Ejemplo
5.2
5.2
Captulo 5. Anlisis semntico 199
05-CAPITULO 05 9/2/06 11:51 Pgina 199
Si se considera la expresin (3+4)*5, que pertenece al lenguaje de la gramtica anterior, se
puede concluir que su valor ser 35 y su tipo entero. La correccin del valor se basa en que 3+4=7
y 7*5=35. El tipo es entero, porque todos los operandos elementales lo son y los operadores no
modifican el tipo. Es decir, como 3 y 4 son enteros, 7 (su suma) tambin lo es, y el producto de
la suma por otro entero (5) es tambin un valor entero (35).
Es evidente tambin que algunas partes de la expresin tienen que ser procesadas antes que
otras. Por ejemplo, no se puede evaluar la expresin completa antes que la subexpresin (3+4).
Lo mismo ocurre con el tipo. La Figura 5.5 muestra el rbol del anlisis de esta expresin, ano-
tado para gestionar su tipo y su valor.
Un rectngulo con lnea discontinua resalta las tareas propias del analizador morfolgico: al
reconocer en la entrada las constantes 3, 4 y 5, tiene que indicar al analizador sintctico que ha
identificado la unidad c, que sus valores son 3, 4 y 5, y su tipo entero (representado por i de
int). Las flechas ascendentes sugieren el orden en el que se pueden realizar las anotaciones:
primero las hojas, y las dos subexpresiones (3+4)y 5 antes que la expresin completa.
La Figura 5.5 indica que las acciones que hay que realizar para evaluar la expresin y dedu-
cir su tipo se pueden resumir de la siguiente forma: el valor de las constantes es el del nmero
asociado a ellas, y su tipo es entero; el valor de la suma es la suma de los valores de sus operan-
dos, y su tipo es entero, como ellas; lo mismo ocurre con el producto. Los parntesis no modifi-
can el valor ni el tipo de la expresin que contienen. Es fcil comprobar que estas acciones no
200 Compiladores e intrpretes: teora y prctica
E
E
E
E
E E
c
c c
5
4 3
(
+ )
*
v:4
t:i
v:3
t:i
v:3
t:i
v:4
t:1
v:5
t:i
v:5
t:i
v:35
t:i
v:7
t:i
v:7
t:i
Figura 5.5. rbol de anlisis de la expresin (3+4)*5, anotado para calcular su tipo
y su valor.
05-CAPITULO 05 9/2/06 11:51 Pgina 200
dependen de la expresin concreta, sino de la regla, es decir, que todas las sumas, productos,
constantes y expresiones entre parntesis se tratan de la misma manera.
Otra observacin importante tiene que ver con el hecho de que, en este ejemplo, todas las fle-
chas que aparecen en la figura, que indican el orden de realizacin de las operaciones, son as-
cendentes, es decir: la informacin que se asigna a la parte izquierda de la regla se ha calculado
utilizando nicamente informacin procedente de la parte derecha de la misma regla.
Se puede plantear la posibilidad de que no siempre ocurra esto: existen casos en los que la
informacin que se desea asociar a algn smbolo de la parte derecha necesite de la informacin
asociada a otro smbolo de la parte derecha o de la parte izquierda de la regla? El prximo ejem-
plo tiene como objeto responder esta cuestin.
Considrese la gramtica asociada a las siguientes reglas de produccin, donde se considera que
el smbolo D es el axioma:
D::=TL
T::=int
T::=real
L::=L,i
L::=i
Esta gramtica podra pertenecer a la parte declarativa de algn lenguaje de programacin, ya que
T representa diferentes tipos de datos (en particular entero o int, o real). Es fcil comprobar que
esta gramtica genera declaraciones de identificadores de tipo entero o real, de forma que en cada
instruccin puede declararse un nico identificador o una lista de ellos separados por comas.
Vamos a estudiar la instruccin int var1, x, y, en la que se declaran tres identificadores
de tipo entero. El objetivo de este ejemplo es asociar a cada uno de ellos el tipo con que han sido
declarados. Dicho tipo se conoce desde que se detecta el smbolo terminal int. El tipo es el
mismo para todos, y tiene que anotarse para cada uno de los identificadores de la lista.
La Figura 5.6 muestra el rbol anotado con los tipos de los identificadores. Por convenio, ini-
cialmente se asigna a los identificadores un valor igual a 0.
Obsrvese que las flechas que sugieren un posible orden en las anotaciones indican que es im-
prescindible conocer en primer lugar el tipo del smbolo no terminal T. Dicho tipo puede usarse
para anotar, si es necesario, el tipo de la raz del rbol (D) y el del smbolo L, hermano de T. A
partir de aqu, se sabe que el identificador y es de tipo entero, as como var1 y x, mediante las
dos apariciones ms profundas del smbolo L.
Las acciones que completaran las anotaciones del rbol pueden resumirse as: el tipo del sm-
bolo no terminal D debe ser igual al de su primer hijo T, y tambin pasar a ser el de su segundo
hijo L. El tipo de este smbolo (L) pasar a ser el de su primer hijo (L) y el del identificador y. El
tipo del identificador x ser el de su padre (L) y tambin lo ser del otro hijo, que termina en el
identificador var1. Por lo tanto, en la regla D::=TL, el tipo de la raz se calcula utilizando el de
su hijo T, y el de L se calcula tambin de la misma manera. En las dos apariciones de la regla
L::=L,i y en la de la regla L::=i, el tipo del padre se utiliza para calcular el de sus hijos (L e i).
Ejemplo
5.3
Captulo 5. Anlisis semntico 201
05-CAPITULO 05 9/2/06 11:51 Pgina 201
Es fcil imaginar cmo se podra incorporar aqu la gestin de la tabla de smbolos de un com-
pilador. Una vez que se conoce el nombre y el tipo de cada identificador, se podra comprobar
que no colisiona con ningn otro elemento de la tabla de smbolos, antes de realizar su insercin
en ella.
Las conclusiones de estos ejemplos son las siguientes:
Para la realizacin del anlisis semntico se necesita asociar cierta informacin a cada sm-
bolo de la gramtica, as como describir las acciones necesarias para calcular el valor de di-
cha informacin en cada punto del rbol de anlisis. En las prximas secciones se ver que
la informacin semntica se formaliza mediante los atributos semnticos, que se calculan
mediante la ejecucin de acciones semnticas.
Puede ser conveniente disponer de informacin global, que no dependa de ningn smbolo
y sea accesible desde cualquier regla de la gramtica.
La informacin que se asocia a cada smbolo depende slo de ste, es decir, ser la misma
para todas las apariciones del mismo smbolo. Evidentemente, el valor concreto que reciba
dicha informacin depender del lugar en el que aparezca cada smbolo.
Las acciones que hay que realizar para calcular los valores de los atributos dependen de
las reglas, no de los smbolos: en cada aplicacin de la misma regla en el rbol de
anlisis se aplicarn las mismas acciones para calcular los valores asociados a sus sm-
bolos.
202 Compiladores e intrpretes: teora y prctica
D
T
L
L
L
int var1 x y , ,
i i i
t:i
t:i t:i
t:i
t:i
v:0
t:i
v:0
t:i
v:0
t:i
Figura 5.6. rbol de anlisis de la expresin int var1, x, y, anotado para calcular
su tipo.
05-CAPITULO 05 9/2/06 11:51 Pgina 202
5.2.2. Descripcin formal de las gramticas de atributos
El objetivo de esta seccin es definir formalmente los conceptos que se han presentado infor-
malmente mediante ejemplos en la seccin anterior.
Se llama gramtica de atributos
1
a una extensin de las gramticas independientes del con-
texto a las que se aade un sistema de atributos. Un sistema de atributos est formado por:
Un conjunto de atributos semnticos que se asocia a cada smbolo de la gramtica.
Los datos globales de la gramtica, accesibles desde cualquiera de sus reglas, pero no aso-
ciados a ningn smbolo concreto.
Un conjunto de acciones semnticas, distribuidas por las reglas de produccin.
De forma semejante a las variables en los lenguajes de programacin de alto nivel, un atribu-
to semntico se define como un par, compuesto por un tipo de datos (la especificacin de un do-
minio o conjunto) y un nombre o identificador. En algunos ejemplos de este captulo, debido a
su simplicidad, el dominio de los atributos es irrelevante y se omitir. En cada momento, cada
atributo semntico puede tener un valor nico que tiene que pertenecer a su dominio. Dicho va-
lor puede modificarse en las acciones semnticas, sin ningn tipo de restriccin.
Una accin semntica es un algoritmo asociado a una regla de la gramtica de atributos, cu-
yas instrucciones slo pueden referirse a los atributos semnticos de los smbolos de la regla y a
la informacin global de la gramtica de atributos. El objetivo de la accin semntica es calcular
el valor de alguno de los atributos de los smbolos de su regla, sin restricciones adicionales.
En este captulo se utilizar la siguiente notacin para las gramticas de atributos:
A = (
T
,
N
, S, P, K)
Se mantiene la estructura de las gramticas independientes del contexto.
A cada smbolo de la gramtica lo acompaa la lista de sus atributos semnticos entre pa-
rntesis. El nombre de cada atributo sigue al de su dominio. Se utiliza la misma notacin
que en el resto de los ejemplos de pseudocdigo. En los smbolos sin atributos semnticos
se omitirn los parntesis.
Las reglas de produccin se modifican para distinguir distintas apariciones del mismo sm-
bolo. A cada regla de produccin lo acompaa su accin semntica, en la que tambin se uti-
lizar la misma sintaxis de los ejemplos de pseudocdigo. Las instrucciones de la accin se
escriben entre llaves. Para referirse a un atributo semntico de un smbolo dentro de las ac-
ciones semnticas, se escribir el nombre del atributo tras el del smbolo, separados por un
punto .. Si alguna regla no necesita realizar ninguna accin semntica, se escribir {}.
La informacin global se aade como nueva componente adicional, al final de la gramti-
ca de atributos (K).
Captulo 5. Anlisis semntico 203
1
A lo largo de la historia de los lenguajes formales ha habido diversas ideas que han desembocado en las gra-
mticas de atributos. Algunas slo proponen nombres distintos para el mismo concepto; otras s describen aspectos
diferentes y han tenido relevancia en distintos momentos. En este texto se utilizar la denominacin gramticas de
atributos para unificar y clarificar la exposicin.
05-CAPITULO 05 9/2/06 11:51 Pgina 203
A continuacin se muestra la gramtica de atributos del Ejemplo 5.2:
A
5_4
={
T
={+, *, (, ), c(valor,tipo), i(valor,tipo)},
N
={E(valor,tipo)},
E,
P={
E::=E
i
+E
d
{ E.valor = E
i
.valor+E
d
.valor;
E.tipo = E
i
.tipo;},
E::=-E
d
,
{ E.valor = -E
d
.valor;
E.tipo = E
d
.tipo; },
E::=E
i
*E
d
{ E.valor = E
i
.valor*E
d
.valor;
E.tipo = E
i
.tipo;},
E::=(E
d
)
{ E.valor = E
d
.valor;
E.tipo = E
d
.tipo},
E::=i
{ E.valor = i.valor,
E.tipo = i.tipo},
E::=c
{ E.valor = c.valor,
E.tipo = c.tipo}
},
K=}
A continuacin se muestra la gramtica de atributos del Ejemplo 5.3:
A
5_5
={
T
={int(tipo), real(tipo), i(tipo)},
N
={D(tipo),T(tipo),L(tipo)},
D,
P={
D::=TL
{ L.tipo = T.tipo;
D.tipo = L.tipo;},
T::=int {T.tipo = entero;},
T::=real {T.tipo = real;},
L::=L
d
,i
{ L
d
.tipo = L.tipo;
i.tipo = L.tipo;},
L::=i {i.tipo = L.tipo;}
},
K=}
Ejemplo
5.5
Ejemplo
5.4
204 Compiladores e intrpretes: teora y prctica
05-CAPITULO 05 9/2/06 11:51 Pgina 204
5.2.3. Propagacin de atributos y tipos de atributos
segn su clculo
Se llama propagacin al clculo del valor de los atributos en funcin del valor de otros. Se defi-
ne as una relacin de dependencia entre los atributos que aparecen en el rbol de anlisis de una
instruccin, ya que los atributos asociados a un nodo dependen de los atributos necesarios para
calcular su valor.
Las flechas que sugieren el orden de clculo de los atributos en las Figuras 5.5, 5.6 y 5.7 mues-
tran realmente el proceso de la propagacin de atributos o, lo que es lo mismo, su relacin de de-
pendencia. En la Seccin 5.3.5 se estudiar con ms detalle dicha relacin de dependencia y su
importancia para el anlisis semntico.
Los atributos semnticos se pueden clasificar segn la posicin, en la regla de produccin, de
los smbolos cuyos atributos se utilicen en el clculo. Se distinguen dos grupos:
Atributos sintetizados: Son los atributos asociados a los smbolos no terminales de la par-
te izquierda de las reglas de produccin cuyo valor se calcula utilizando los atributos de los
smbolos que estn en la parte derecha correspondiente.
Obsrvese la siguiente regla de la gramtica del Ejemplo 5.4:
E::=E
i
+E
d
{ E.valor = E
i
.valor+E
d
.valor;
E.tipo = E
i
.tipo;},
Los atributos valor y tipo del smbolo no terminal de la parte izquierda (E) se calculan a
partir de los de la parte derecha (E
i
y E
d
), por lo que se puede afirmar que son sintetizados.
Atributos heredados: El resto de las situaciones originan atributos heredados, es decir,
atributos asociados a smbolos de la parte derecha de las reglas cuyo valor se calcula utili-
zando los atributos de la parte izquierda o los de otros smbolos de la parte derecha. En este
grupo es necesaria la siguiente distincin:
Atributos heredados por la derecha, cuando el clculo del valor de un atributo utiliza
atributos de los smbolos que estn situados a su derecha.
Atributos heredados por la izquierda, cuando slo se utilizan los que estn a su iz-
quierda, ya sea en la parte derecha de la regla o en la parte izquierda.
Obsrvese la siguiente regla de la gramtica del Ejemplo 5.5:
D::=TL
{ L.tipo = T.tipo;
D.tipo = L.tipo;}
El atributo tipo del smbolo no terminal L lo hereda de su hermano izquierdo (T). Vanse
tambin las siguientes reglas:
L::=L
d
,i
{ L
d
.tipo = L.tipo;
Captulo 5. Anlisis semntico 205
05-CAPITULO 05 9/2/06 11:51 Pgina 205
i.tipo = L.tipo;}
L::=i {i.tipo = L.tipo;}
El atributo tipo del smbolo terminal i se hereda del padre (en ambos casos L).
Muestra una gramtica de atributos que rene los dos ltimos ejemplos, para describir un len-
guaje de programacin un poco ms realista. El lenguaje contiene la parte declarativa del
Ejemplo 5.5 y las expresiones aritmticas del Ejemplo 5.4. Se aade un nuevo axioma, para in-
dicar que esta gramtica genera programas completos, y un nuevo smbolo no terminal (A), que
realiza la asignacin de una expresin a un identificador mediante el smbolo =. La gramtica
incorpora una tabla de smbolos, donde se conserva el tipo con el que se declaran los atributos,
que ayuda a comprobar la correccin semntica de las instrucciones en las que aparecen dichos
atributos.
A
5_6
={
T
={+, *, (, ), c(valor,tipo), i(valor,tipo),
int(tipo), real(tipo), i(tipo), = },
N
={Programa, A(tipo),
E(valor,tipo), D(tipo),T(tipo),L(tipo)},
Programa,
P={
Programa::=DA {},
A::=i = E
{ SI iTablaSimbolos E.tipo=i.tipo
ENTONCES A.tipo=i.tipo;}
E::=E
i
+E
d
{ E.valor = E
i
.valor+E
d
.valor;
E.tipo = E
i
.tipo;},
E::=-E
d
,
{ E.valor = -E
d
.valor;
E.tipo = E
d
.tipo; },
E::=E
i
*E
d
{ E.valor = E
i
.valor*E
d
.valor;
E.tipo = E
i
.tipo;},
E::=(E
d
)
{ E.valor = E
d
.valor;
E.tipo = E
d
.tipo},
E::=i
{ E.valor = i.valor,
E.tipo = i.tipo},
E::=c
{ E.valor = c.valor,
E.tipo = c.tipo}
D::=TL
{ L.tipo = T.tipo;
Ejemplo
5.6
206 Compiladores e intrpretes: teora y prctica
05-CAPITULO 05 9/2/06 11:51 Pgina 206
D.tipo = L.tipo;},
T::=int {T.tipo = entero;},
T::=real {T.tipo = real;},
L::=L
d
,i
{ L
d
.tipo = L.tipo;
i.tipo = L.tipo;
insertar(TablaSimbolos, i, L.tipo);},
L::=i
{ i.tipo = L.tipo;
insertar(TablaSimbolos, i, L.tipo);}
},
K={TablaSimbolos}}
En esta gramtica, TablaSimbolos es una tabla de smbolos en la que se conserva (al me-
nos) informacin sobre el tipo de cada identificador. Asociada a esta tabla se dispone de la ope-
racin insertar(TablaSimbolos, identificador, tipo), mediante la que se deja
constancia en la tabla de que se ha declarado el identificador cuyo nombre es el segundo argu-
mento y cuyo tipo es el tercero.
Obsrvese que la regla
A::=i = E
{ SI iTablaSimbolos E.tipo=i.tipo
ENTONCES A.tipo=i.tipo;}
realiza las comprobaciones semnticas sobre la correspondencia de tipos entre la expresin y
el identificador al que se asigna su valor. Obsrvese tambin que las dos reglas en las que apa-
rece el smbolo terminal i en la parte declarativa insertan dicho identificador en la tabla de
smbolos.
L::=L
d
,i
{ L
d
.tipo = L.tipo;
i.tipo = L.tipo;
insertar(TablaSimbolos, i, L.tipo);}
L::=i
{ i.tipo = L.tipo;
insertar(TablaSimbolos, i, L.tipo);}
}
La Figura 5.7 muestra el anlisis del programa
int var1, x, y
x = (3+4)*5
Tambin puede observarse el contenido de la tabla de smbolos al final del anlisis, que ini-
cialmente se encuentra vaca.
Captulo 5. Anlisis semntico 207
05-CAPITULO 05 9/2/06 11:51 Pgina 207
Las flechas que sugieren el orden de evaluacin de los atributos muestran que es necesario
procesar primero el subrbol izquierdo que contiene la parte declarativa. Dentro de este subrbol
se aplica lo explicado en la Figura 5.6. Al terminar el proceso de dicho subrbol, la tabla de sm-
bolos contiene toda la informacin que necesita.
Al analizar la asignacin, el identificador al que se le asigna nuevo valor (x) es buscado en la
tabla de smbolos, para comprobar que ha sido declarado antes de ser usado y para consultar el
tipo con el que se lo declar (int). Esta informacin est contenida en la anotacin del nodo del
smbolo i, padre del nodo x.
A continuacin puede procesarse el subrbol de la expresin (el que tiene como raz la
aparicin menos profunda del smbolo no terminal E). En este subrbol se aplica lo explica-
do en la Figura 5.5. Finalmente, la asignacin puede concluirse correctamente en el nodo eti-
quetado con el smbolo no terminal A. Tanto el identificador como la expresin son de tipo
entero.
5.2.4. Algunas extensiones
Es frecuente que las gramticas de atributos contengan acciones semnticas situadas entre los
smbolos de la parte derecha de las reglas. Hay varias razones para permitirlo: por un lado, se
ofrece mayor flexibilidad al diseador de la gramtica; por otro, hay algunos analizadores se-
mnticos que pueden procesar las acciones en esa posicin.
La siguiente gramtica de atributos presenta una versin simplificada de las instrucciones de
asignacin de expresiones a identificadores:
Ejemplo
5.7
208 Compiladores e intrpretes: teora y prctica
Programa
TS
y(int)
x(int)
var1(int)
D
T
int var1 x y x 3 4 5 , = ( ) *
i i i i
c c c
E
E
A
L
L
L
E
E
E E
t:i
t:i
t:i
t:i
t:i
t:i
v:0
t:i
v:0
t:i
v:0
t:i
v:0
t:i
v:3
t:i
v:4
t:i
v:5
t:i
v:5
t:i
v:35
t:i
v:7
t:i
v:7
t:i
v:4
t:i
v:3
t:i
Figura 5.7. rbol de anlisis del Ejemplo 5.6.
05-CAPITULO 05 9/2/06 11:51 Pgina 208
A
5_7
={
T
={=, +, cte(valor, tipo), id(valor, tipo)},
N
={ Asignacin(valor, tipo),
Expresin(valor, tipo),
Trmino{valor, tipo}},
Asignacin,
P={
Asignacin ::= id
{ Comprobacin en la tabla de smbolos de que el
identificador ha sido declarado y recuperacin de
su tipo; }
= Expresin
{ Comprobacin de la compatibilidad de los tipos
Expresin.tipo e id.tipo;
id.valor = Expresin.valor;},
Expresin ::= Expresin
d
+ Trmino
{ Comprobacin de compatibilidad de los tipos
Expresin
d
.tipo y Trmino.tipo;
Expresin.tipo =
2
tipoSuma(Expresin
d
.tipo, Trmino.tipo);
Expresin.valor=Expresin
d
.valor+Trmino.valor;},
Expresin ::= Trmino
{ Expresin.tipo = Trmino.tipo;
Expresin.valor = Trminao.valor;},
Trmino ::= id
{ Comprobacin en la tabla de smbolos de que el
identificador ha sido declarado y recuperacin de
su tipo;
Trmino.tipo = id.tipo;
Trmino.valor = id.valor;},
Trmino ::= cte
{ Trmino.tipo = cte.tipo;
Trmino.valor = cte.valor; }
},
K=}
En este ejemplo, la informacin relacionada con el tipo y los valores se almacena con el mis-
mo criterio de los ejemplos anteriores. La primera accin semntica no est situada al final de la
primera regla.
Captulo 5. Anlisis semntico 209
2
La funcin tipoSuma toma como argumentos dos tipos y determina el tipo que le correspondera, con esos ti-
pos, a la operacin suma de dos operandos.
05-CAPITULO 05 9/2/06 11:51 Pgina 209
En este captulo se supondr, casi siempre, que las gramticas de atributos tienen las acciones
semnticas situadas al final de la regla, y cuando esto no se aplique se indicar explcitamente
que la gramtica no cumple dicha condicin. Siempre es posible transformar una gramtica de
atributos con acciones semnticas en cualquier posicin en otra equivalente con todas las accio-
nes semnticas al final de las reglas. El algoritmo correspondiente realiza los siguientes pasos,
mientras existan reglas de produccin con la siguiente estructura:
N::= X
1
X
2
... X
i-1
{Instrucciones} X
i
... X
n
1. Se aade un nuevo smbolo no terminal a la gramtica (por ejemplo, Y) sin atributos se-
mnticos asociados.
2. Se aade la siguiente regla de produccin:
Y::= {Instrucciones}
3. Se sustituye la regla inicial por la siguiente:
N::= X
1
X
2
... X
i-1
Y X
i
... X
n
Es fcil comprobar que el lenguaje generado por ambas gramticas independientes del con-
texto es el mismo, y que la informacin semntica no ha cambiado.
En este ejemplo se obtendra la siguiente gramtica:
A
5_7
,={
T
,
N
{M}, Asignacin,
P
{M::=
{ Comprobacin en la tabla de smbolos de que el
identificador ha sido declarado y recuperacin de
su tipo; }},
K=}
El problema de este enfoque es que los nuevos smbolos no terminales no recogen ninguna se-
mntica del problema, ya que son meros marcadores. Adems, es posible que no se desee aadir
reglas-. En situaciones reales, en las que puede haber muchas reglas de produccin, a veces con-
viene modificar la gramtica un poco ms, para que cada smbolo y cada regla retengan algo de
significado.
En tal caso, podra conseguirse lo mismo con el siguiente algoritmo:
1. Se aade un nuevo smbolo no terminal a la gramtica, para representar todos los smbo-
los de la parte derecha, desde su comienzo hasta el smbolo seguido por la accin semn-
tica (por ejemplo, X
1_i-1
). Este nuevo smbolo no tiene atributos semnticos asociados.
2. Se aade la siguiente regla de produccin:
X
1_i-1
::= X
1
X
2
... X
i-2
X
i-1
{Instrucciones}
3. Se sustituye la regla inicial por la siguiente:
N::= X
1_i-1
X
i
... X
n
210 Compiladores e intrpretes: teora y prctica
05-CAPITULO 05 9/2/06 11:51 Pgina 210
Por ejemplo, se podra pensar que en la gramtica anterior se necesitan realmente dos reglas
de produccin para la asignacin. La primera conservara la estructura de la actual, pero sustitu-
yendo el smbolo terminal id por el nuevo no terminal id_asignado. El nuevo smbolo ten-
dr una regla en la que, junto con la accin semntica, tambin se arrastra el resto de la
derivacin:
Asignacin ::= id_asignado Expresin
{ Comprobacin de la compatibilidad de los tipos
Expresin.tipo e id.tipo;
id_asignado.valor = Expresin.valor;},
id_asignado ::= id
{ Comprobacin en la tabla de smbolos de que el
identificador ha sido declarado y recuperacin de
su tipo;
id_asignado.tipo = id.tipo;},
Resulta fcil comprobar la equivalencia del resultado de este enfoque, basado en el segundo
algoritmo y las dos gramticas anteriores. Obsrvese que no se ha aadido ninguna regla-, aun-
que s una regla de redenominacin.
5.2.5. Nociones de programacin con gramticas
de atributos
En el desarrollo del analizador semntico de un compilador, es necesario solucionar con gra-
mticas de atributos las dificultades asociadas a todas las construcciones del lenguaje de pro-
gramacin considerado. En esta seccin se va a reflexionar, mediante ejemplos, sobre algunas
caractersticas del diseo de las gramticas de atributos. Es fcil comprobar que la posibilidad
de especificar cualquier algoritmo en las acciones semnticas de las reglas implica que las
gramticas de atributos tengan la misma potencia que las Mquinas de Turing: son capaces de
expresar el algoritmo asociado a cualquier tarea computable. Por lo tanto, podra considerarse
que las gramticas de atributos constituyen un lenguaje de programacin en s mismo. El ob-
jetivo de los prximos puntos es destacar las peculiaridades de este nuevo lenguaje de progra-
macin.
Todos los ejemplos que se van a exponer en esta seccin estn relacionados con el lenguaje
de los parntesis, compuesto por expresiones escritas exclusivamente con parntesis equilibra-
dos, en los que cada parntesis abierto se cierra dentro de la expresin siguiendo las normas ha-
bituales de las expresiones matemticas. El lenguaje considerado en estos prrafos tiene la
Captulo 5. Anlisis semntico 211
05-CAPITULO 05 9/2/06 11:51 Pgina 211
peculiaridad de que la expresin completa est encerrada siempre entre parntesis. Es decir, las
siguientes palabras pertenecen al lenguaje descrito:
( () () )
( ( ()(())() ) )
Pero las siguientes no pertenecen a l:
( ()
() ()
Todos los ejemplos utilizan como punto de partida la siguiente gramtica independiente del
contexto:
G
5_8
={
T
={ (, ) },
N
={ <lista>, <lista_interna>},
<lista>,
P={<lista> ::= ( <lista_interna> )
<lista_interna>::=<lista_interna>(<lista_interna>)
<lista_interna>::=}
}
El axioma representa la expresin completa: una lista interna entre parntesis. La lista inter-
na ms sencilla es la palabra vaca. La regla recursiva para la lista interna la describe como una
lista interna junto a otra encerrada entre parntesis. Es fcil comprobar la correspondencia entre
esta gramtica y el lenguaje especificado.
Consideraremos los dos problemas siguientes:
Clculo de la profundidad (o nivel de anidamiento) de la expresin. El concepto queda
definido por los siguientes ejemplos:
(), tiene profundidad 1
( () ( ( ) ) ), tiene profundidad 3, debido a la sublista derecha
Clculo del nmero de listas de la expresin por ejemplo,
(), contiene una lista
( () ( ( ) ) ) contiene 3 listas
( () () () () ) contiene 5 listas
( ( ((())) () )) contiene 6 listas
Aunque los ejemplos son casos particulares pequeos, las conclusiones extradas de ellos se
pueden aplicar al diseo de gramticas de atributos en general.
La siguiente gramtica de atributos calcula la profundidad de la lista estudiada. El diseo de esta
gramtica se basa en el clculo clsico de la profundidad de una lista con un algoritmo recursivo
que aproveche la estructura de las reglas de la gramtica. De esta forma, basta con introducir un
nico atributo de tipo entero (profundidad).
Ejemplo
5.8
212 Compiladores e intrpretes: teora y prctica
05-CAPITULO 05 9/2/06 11:51 Pgina 212
En la regla del axioma <lista>::=(<lista_interna>), habr que aadir una unidad
a la profundidad de la <lista_interna>.
Una de las reglas para el smbolo no terminal <lista_interna> sirve para finalizar la re-
cursividad: <lista_interna>::=, que corresponde claramente a una lista de profundidad
0. En cambio, en la regla recursiva:
<lista_interna>::=<lista_interna>
1
(<lista_interna>
2
)
hay que determinar cul de las sublistas tiene la mayor profundidad. Si es la primera, la profun-
didad de la lista interna completa coincidir con la suya; si es la segunda, ser necesario incre-
mentarla en una unidad, pues est encerrada entre parntesis. Vase la gramtica completa:
A
5_8
={
T={ (, ) },
N={<lista>(entero profundidad;),
<lista_interna>(entero profundidad)},
<lista>,
P={
<lista>::= ( <lista_interna> )
{ <lista>.profundidad=
<lista_interna>.profundidad + 1;
IMPRIMIR(PROFUNDIDAD:,<lista>.profundidad);},
<lista_interna>::=<lista_interna>
1
(<lista_interna>
2
)
{ SI ( <lista_interna>
1
.profundidad >
<lista_interna>
2
.profundidad )
<lista_interna>.profundidad =
<lista_interna>
1
.profundidad ;
EN OTRO CASO
<lista_interna>.profundidad =
<lista_interna>
2
.profundidad + 1 ;},
<lista_interna>::=
{ <lista_interna>.profundidad = 0;}
},
K=}
La Figura 5.8 muestra el clculo de la profundidad de la lista (()(())) mediante la gra-
mtica de atributos de este ejemplo. Se han utilizado flechas discontinuas para indicar la sntesis
de los atributos. Los nombres de los atributos han sido sustituidos por sus iniciales.
Se puede observar que es posible solucionar este problema utilizando nicamente atributos
sintetizados, porque lo que se desea calcular slo depende de una parte concreta de toda la ex-
presin: la sublista ms profunda.
Captulo 5. Anlisis semntico 213
05-CAPITULO 05 9/2/06 11:51 Pgina 213
La siguiente gramtica de atributos calcula el nmero de listas de la expresin. La estructura de
las reglas de la gramtica independiente del contexto sugiere que la nica regla asociada al axio-
ma, <lista>::=(<lista_interna>), debe sintetizar un atributo semntico cuyo valor
sea el nmero total de listas de la expresin. Si este atributo se llama num_listas_total,
est claro que su valor correcto se obtendr sumando una unidad al nmero de listas, calculado
por el smbolo no terminal <lista_interna>.
Para ilustrar el clculo de listas de una lista interna se puede considerar el siguiente ejemplo:
( ( ((())) () ))
Se supondr que la cadena se recorre de izquierda a derecha. Es fcil comprobar que el n-
mero de listas correspondiente a la lista resaltada es igual a 3. A su derecha hay una lista ms, y
el conjunto est incluido en otras dos listas. Eso hace un total de 6 (3+1+2). Este proceso acu-
mulativo sugiere que cada lista interna debe calcular el nmero de sus listas, sumarlo al nme-
ro de listas encontradas en la cadena que la precede y ofrecer el valor total al resto del anlisis.
Puesto que las listas tienen que estar equilibradas, si se identifica una nueva lista por su parn-
tesis de apertura, al llegar a la sublista resaltada anterior se llevarn ya contabilizadas 2 listas. El
clculo que debe realizar la lista resaltada consistir en aadir las suyas (3) y ofrecer el valor to-
tal (5) al resto del proceso. Tras la parte resaltada slo queda una lista ms, que completar el
valor correcto. Puede utilizarse un atributo para recibir el nmero de listas de la parte procesa-
da (se lo denominar num_listas_antes) y otro para el que se ofrezca al resto del anlisis
(num_listas_despues).
Ejemplo
5.9
214 Compiladores e intrpretes: teora y prctica
<lista_interna>
<lista_interna>
<lista_interna>
<lista_interna>
<lista_interna>
<lista>
<lista_
interna>
<lista_
interna>
p:2
( ( ) ( ( ) ) )
p:3
p:1
p:0
p:0
p:0
p:0
p:1
Figura 5.8. Clculo de la profundidad de la lista (()(())) para el Ejemplo 5.8.
05-CAPITULO 05 9/2/06 11:51 Pgina 214
Hay que distribuir el clculo del nmero de listas del smbolo <lista_interna> entre las
reglas donde aparece. Puesto que sus reglas son recursivas, comenzaremos por aquella que per-
mite dar por terminada la recursin: <lista_interna>::=, donde es evidente que no
hay que hacer nada, pues esta regla no aade nuevas listas, por lo que el valor de num_lis-
tas_despues coincidir con el de num_listas_antes.
Tambin est claro el clculo necesario para la regla recursiva <lista_interna>::=
<lista_interna>
1
(<lista_interna>
2
).
El valor de <lista_interna>.num_listas_antes es el nmero de listas encon-
tradas hasta el momento, y es el mismo que habr hasta <lista_interna>
1
.
El nmero de listas antes de procesar <lista_interna>
2
tiene que aadir 1 (por la
lista que aparece de forma explcita) despus de procesar <lista_interna>
1
.
El nmero de listas despus de procesada esta regla, coincide con el obtenido despus de
procesar <lista_interna>
2
.
Falta asociar un valor inicial al atributo num_listas_antes del smbolo no terminal
<lista_interna> en la regla del axioma. Es evidente que la lista que aparece de forma ex-
plcita en dicha regla es la primera de la expresin completa, por lo que el valor del atributo tie-
ne que ser 1. Vase la gramtica de atributos completa:
A
5_9
={
T
={ (, ) },
N
={ <lista>(entero num_listas_total;),
<lista_interna>( entero num_listas_antes;
entero num_listas_despues)},
<lista>,
P=
{<lista> ::= ( <lista_interna> )
{<lista_interna>.num_listas_antes = 1;
<lista>.num_listas_total =
<lista_interna>.num_listas_despues;
IMPRIMIR(ELEMENTOS,<lista>.num_listas_total);},
<lista_interna>::=<lista_interna>
1
(<lista_interna>
2
)
{ <lista_interna>
1
.num_listas_antes =
<lista_interna>.num_listas_antes;
<lista_interna>
2
.num_listas_antes =
<lista_interna>
1
.num_listas_despues+1;
<lista_interna>.num_listas_despues =
<lista_interna>
2
.num_listas_despues;},
<lista_interna>::=
{ <lista_interna>.num_listas_despues =
<lista_interna>.num_listas_antes;}},
K=}
Captulo 5. Anlisis semntico 215
05-CAPITULO 05 9/2/06 11:51 Pgina 215
La Figura 5.9 muestra el clculo del nmero de listas de la expresin (()(())) mediante la
gramtica de atributos de este ejemplo. Se han utilizado dos tipos de flechas discontinuas para
distinguir la herencia de la sntesis de atributos.
Se pueden extraer las siguientes conclusiones:
El axioma tiene asociado un atributo sintetizado de tipo entero (num_listas_total),
que representa el nmero total de listas de la expresin.
Las listas internas tienen dos atributos de tipo entero, uno (num_listas_antes) here-
dado a veces de su padre, a veces de su hermano ms a la izquierda, que indica el nmero
de listas que haba en la expresin antes del proceso de esta lista interna, y el otro
(num_listas_despues) sintetizado, que recoge las modificaciones en el nmero de
listas debidas a la regla. El atributo heredado es necesario, porque el valor que se quiere cal-
cular puede depender de partes diferentes de la expresin.
A continuacin se muestra otra gramtica de atributos que tambin calcula el nmero de lis-
tas de la expresin. Puede compararse con la del ejemplo anterior. En este caso se ha aprove-
chado la posibilidad de utilizar, como informacin global, una variable de tipo entero
(num_elementos) que se declara e inicializa con el valor 0 en la ltima componente de la
gramtica (K). Para obtener el valor correcto, bastar con incrementar el valor de dicho atri-
buto en una unidad cada vez que aparece una lista de forma explcita en la parte derecha de
alguna regla.
Ejemplo
5.10
216 Compiladores e intrpretes: teora y prctica
<lista_interna>
<lista_interna>
<lista_interna>
<lista_interna>
<lista_interna>
<lista>
<lista_
interna>
<lista_
interna>
a:1
d:4
a:1
d:2
a:1
d:1
a:2
d:2
a:3
d:3
a:4
d:4
a:3
d:4
( ( ) ( ( ) ) )
t:4
Figura 5.9. Clculo del nmero de listas de la expresin (()(())) para el Ejemplo 5.9.
05-CAPITULO 05 9/2/06 11:51 Pgina 216
A
5_10
={
T
={ (, ) },
N
={<lista>(), <lista_interna>()},
<lista>,
P={
<lista>::=( <lista_interna> )
{ num_elementos = num_elementos + 1;
IMPRIMIR(HAY ,num_elementos, LISTAS );},
<lista_interna>::= <lista_interna>
( <lista_interna> )
{ num_elementos++;},
<lista_interna>::={ }},
K={entero num_elementos=0;}
}
La comparacin de esta gramtica con la del Ejemplo 5.9 ofrece una conclusin interesante:
los atributos heredados pueden sustituirse fcilmente por informacin global. La importancia de
esta conclusin se analizar con ms detalle en las prximas secciones, en las que se ver que
esta sustitucin puede ser necesaria en determinadas circunstancias.
Incorporacin del analizador semntico
al sintctico
5.3.1. Dnde se guardan los valores de los atributos
semnticos?
La primera pregunta que hay que responder, al incorporar el anlisis semntico al sintctico, es
dnde se pueden guardar los valores de los atributos que se han aadido a la gramtica indepen-
diente del contexto. El analizador sintctico manipula los smbolos de la gramtica movindolos
entre la entrada y la pila del anlisis. De forma general, los analizadores semnticos sustituyen los
smbolos manipulados por el analizador semntico por una estructura de datos, que contendr,
adems de la unidad sintctica que se va a analizar, los atributos semnticos especificados en la
gramtica. Desde este momento se supondr que los algoritmos descritos en el Captulo 4, en lu-
gar de smbolos, manipulan estructuras que contienen la informacin semntica de cada smbolo.
Esta misma modificacin es necesaria en el analizador morfolgico. La estructura de cada uni-
dad sintctica generada por dicho analizador debe incorporar la informacin semntica que lleva
asociada. Por ejemplo, cuando se reconozca el nombre de una variable, el analizador morfolgi-
co debe proporcionar dicho nombre, porque es necesario para las acciones semnticas de bs-
queda e insercin en la tabla de smbolos; esta informacin puede sustituirse por un puntero al
elemento de la tabla asociado a la variable. De igual manera, cuando se analice una constante, se
debe proporcionar tambin su valor.
5.3
Captulo 5. Anlisis semntico 217
05-CAPITULO 05 9/2/06 11:51 Pgina 217
5.3.2. Orden de recorrido del rbol de anlisis
En las secciones y captulos anteriores se ha dicho que el analizador sintctico y la anotacin se-
mntica pueden recorrer en un orden determinado el rbol de derivacin de los programas com-
pilados. En esta seccin se reflexionar con ms profundidad sobre las repercusiones que tiene el
orden de recorrido en la construccin de compiladores.
Los analizadores descendentes siguen el que se conoce como recorrido en profundidad por
la izquierda con vuelta atrs, lo que significa que, entre todos los nodos posibles, siempre se con-
tina por uno de los ms profundos (profundidad). En caso de empate, se toma el nodo que apa-
rezca lo ms a la izquierda posible (por la izquierda). Siempre que se llegue a una hoja, hay que
volver hasta la ltima decisin tomada y elegir uno de los nodos no visitados (con vuelta atrs).
La Figura 5.10 muestra grficamente el recorrido que se hara siguiendo este orden sobre el
rbol de la Figura 5.6.
Obsrvese que los nodos se visitan en el siguiente orden (se resaltan en cursiva los nodos vi-
sitados ms de una vez): D, T, int, T, D, L, L, L, i, var1, i, L, L, ,, L, i, x, i, L, L, ,, L,
i, y, i, L, D.
Los analizadores ascendentes siguen el orden determinado por la reduccin de los asideros
que se van encontrando, realizando desplazamientos cuando la reduccin no es posible. La
Figura 5.11 muestra grficamente este recorrido para el rbol de la Figura 5.6.
218 Compiladores e intrpretes: teora y prctica
D
T
L
L
L
int var1 x y , ,
i i i
Figura 5.10. Recorrido de un analizador descendente sobre el rbol de anlisis
de la Figura 5.6.
05-CAPITULO 05 9/2/06 11:51 Pgina 218
Obsrvese que los nodos se visitan en el siguiente orden: int, T, var1, i, L, ,, x, i, L,
,, y, i, L, D.
En las secciones anteriores de este captulo se ha sealado que las acciones semnticas defi-
nen una relacin de dependencia en funcin de la propagacin de los atributos. Esa relacin in-
duce el orden en que se puede aadir la informacin semntica mientras se recorre el rbol. Las
Figuras 5.5, 5.6 y 5.7 mostraban grficamente, mediante flechas, dicha relacin. Por lo tanto, al
procesar el programa de entrada, se realizarn al menos dos recorridos que requieren un orden
determinado. Cabra plantearse las siguientes preguntas: el orden en que se realiza el anlisis
sintctico y el exigido por el anlisis semntico son siempre compatibles? En caso negativo, es
necesario conseguir que lo sean? En caso afirmativo, es siempre posible hacerlos compatibles?
Para contestar a la primera pregunta basta analizar la Figura 5.12. En ella se superponen los
rdenes de los analizadores de las Figuras 5.10 y 5.11 al que sugieren las dependencias de los
atributos.
En la parte a), que corresponde al analizador descendente, ambos rdenes son compatibles, ya
que el recorrido de la flecha discontinua encuentra los nodos en el mismo orden que sugieren las
flechas continuas. Sin embargo, en la parte b), que corresponde al analizador ascendente, los r-
denes no son compatibles. Aunque el nodo T se visita en el orden adecuado (antes que cualquie-
ra de los otros smbolos no terminales), al llegar al nodo var1, y posteriormente al L, no es
posible aadir la informacin de su tipo porque, a pesar de que es el mismo que el de T, lo reci-
be de su hermano L y ese nodo todava no ha sido visitado.
Captulo 5. Anlisis semntico 219
D
T
L
L
L
int var1 x y , ,
i i i
Figura 5.11. Recorrido de un analizador ascendente sobre el rbol de anlisis
de la Figura 5.6.
05-CAPITULO 05 9/2/06 11:51 Pgina 219
220 Compiladores e intrpretes: teora y prctica
D
T
L
L
L
int var1 x y , ,
i i i
D
T
L
L
L
int var1 x y , ,
i i i
t:i
t:i
t:i
t:i
v:0
t:i
v:0
t:i
v:0
t:i
t:i
a)
b)
t:i
t:i
t:i
t:i
v:0
t:i
v:0
t:i
v:0
t:i
t:i
Figura 5.12. Comparacin de los rdenes de recorrido del rbol por las dependencias entre
los atributos y por los analizadores sintcticos.
05-CAPITULO 05 9/2/06 11:51 Pgina 220
Para contestar a la segunda pregunta hay que tener en cuenta el nmero de pasos del compi-
lador. Si el compilador va a realizar dos o ms pasos, no ser necesario: en el primer paso, el ana-
lizador sintctico construir el rbol de anlisis; en el segundo, el analizador semntico lo
anotar. En las prximas secciones se ver que este esquema da lugar a la tcnica ms general
del anlisis semntico. En cambio, si el compilador es de slo un paso, resulta necesario conse-
guir que los dos rdenes sean compatibles, ya que, en otro caso, sera imposible realizar el an-
lisis semntico.
Para contestar a la tercera pregunta, es preciso definir algn concepto auxiliar adicional.
5.3.3. Tipos interesantes de gramticas de atributos
Esta seccin introduce algunos subconjuntos interesantes de las gramticas de atributos:
Gramticas de atributos con atributos sintetizados. Son aquellas en las que todos los
atributos son sintetizados. Las gramticas de atributos A
5_4
, A
5_8
y A
5_10
, de los ejemplos
con el mismo nmero, son de este tipo. Es interesante sealar el caso de la gramtica A
5_10
,
en la que se utiliza tambin informacin global.
Gramticas de atributos que dependen nicamente de su izquierda. Son aquellas en las
que todos sus atributos son o bien sintetizados o bien heredados de sus padres o de smbo-
los que aparecen en la parte derecha de la regla a la izquierda del smbolo estudiado. Las
gramticas de atributos A
5_5
, A
5_6
y A
5_9
, de los ejemplos con el mismo nmero, son de este
tipo. Es interesante sealar el caso de la gramtica A
5_9
, ya que sus atributos dependen slo
de su izquierda, y es equivalente a la gramtica A
5_10
, que slo tiene atributos sintetizados
e informacin global.
Cabe ahora plantearse la siguiente cuestin: existen gramticas de atributos que no perte-
nezcan a alguno de los dos tipos anteriores? La respuesta a esta pregunta es afirmativa. El hecho
de que todos los ejemplos de este captulo puedan incluirse en alguna de las dos categoras ante-
riores es puramente casual.
La siguiente gramtica proporciona un ejemplo en el que se hereda de smbolos a la derecha del
estudiado. Es una variante del Ejemplo 5.3, en el que la especificacin del tipo se escribe a la de-
recha de la lista de identificadores. Sus reglas de produccin son las siguientes (se ha resaltado
la regla modificada):
D::=LT
T::=int
T::=real
L::=L,i
L::=i
Si se mantiene la semntica del Ejemplo 5.5, es fcil comprobar que el atributo tipo de L
depende del atributo tipo de T que est a su derecha. Lo extrao de este tipo de construcciones
no debe sugerir que no sean posibles, sino constatar la naturalidad con la que se ha incorporado
Ejemplo
5.11
Captulo 5. Anlisis semntico 221
05-CAPITULO 05 9/2/06 11:51 Pgina 221
222 Compiladores e intrpretes: teora y prctica
E
E
E
E
E E
c
c c
5
4 3
(
+ )
*
v:4
t:i
v:3
t:i
v:3
t:i
v:4
t:i
v:5
t:i
v:5
t:i
v:35
t:i
v:7
t:i
v:7
t:i
E
E
E
E
E E
c
c c
5
4 3
(
+ )
*
v:4
t:i
v:3
t:i
v:3
t:i
v:4
t:i
v:5
t:i
v:5
t:i
v:35
t:i
v:7
t:i
v:7
t:i
a)
b)
Figura 5.13. Comparacin de los rdenes de recorrido del rbol por los analizadores
sintcticos y por la propagacin de atributos sintetizados.
05-CAPITULO 05 9/2/06 11:51 Pgina 222
Captulo 5. Anlisis semntico 223
a nuestra intuicin el diseo de lenguajes cuyas gramticas resultan ms adecuadas para la cons-
truccin de compiladores e intrpretes.
Las gramticas de atributos que dependen de su izquierda y las gramticas de atributos sinte-
tizados pueden representar las construcciones de todos los lenguajes de programacin de alto ni-
vel con los que se suele trabajar. Los Ejemplos 5.9 y 5.10 muestran cmo los atributos heredados
de la gramtica A
5_9
pueden sustituirse por el uso de informacin global. Algunos autores distin-
guen entre gramticas de atributos y definiciones dirigidas por la sintaxis, de forma que las pri-
meras son un subconjunto de las segundas que excluyen efectos laterales, es decir, manipulacin
de informacin global. De mantener esa categora, lo que en este captulo se llama gramtica de
atributos sera lo que otros autores llaman definicin dirigida por la sintaxis. Como se ha indi-
cado en la nota 1, se ha decidido unificar estos conceptos en el de gramtica de atributos, para
ofrecer una visin ms compacta y actual.
El inters de estos tipos de gramticas de atributos no se limita a la discusin de su potencia
expresiva. Son de importancia crucial para la comunicacin entre los analizadores sintcticos y
semnticos.
Gramticas de atributos que dependen de su izquierda y analizadores descendentes:
Es fcil comprobar que el orden inducido por el recorrido de los dos analizadores es com-
patible en las gramticas de este tipo. En este sentido, el ejemplo de la Figura 5.12.a) pue-
de generalizarse. La razn es clara: el analizador descendente visita primero los nodos
padre y luego los hermanos, de izquierda a derecha, y se es precisamente el orden nece-
sario para la propagacin de los atributos de las gramticas con dependencia de su iz-
quierda.
La sntesis de atributos tambin es compatible con los analizadores descendentes gracias al
mecanismo de vuelta atrs. La Figura 5.13.a) muestra grficamente un ejemplo. El reco-
rrido de los nodos es el siguiente: E, E, (, E, E, E, c, 3, c, E, E, +, E, E, c, 4, c, E, E, E,
), E, E, *, E, E, c, 5, c, E, E. La sntesis de los valores y tipos de las expresiones se pue-
de completar cuando, al volver atrs, se visita de nuevo las partes izquierdas de las reglas.
Gramticas con atributos sintetizados y analizadores ascendentes: Es fcil compro-
bar que el orden inducido por el recorrido de los analizadores ascendentes y de las gra-
mticas con atributos sintetizados es compatible. La Figura 5.13.b) muestra grficamente
un ejemplo. La razn es que los analizadores ascendentes desplazan la entrada hasta en-
contrar un asidero, momento en el que se reduce la regla para continuar el anlisis, bus-
cando la siguiente reduccin posible. La sntesis de los atributos slo puede realizarse
cuando se tiene seguridad de haber procesado la parte derecha completa de la regla, por
lo que la reduccin es el momento instante ideal para la propagacin.
5.3.4. Tcnica general del anlisis semntico
en compiladores de dos o ms pasos
En general, puede presentarse el caso de disponer de una gramtica de atributos que no se desea
modificar, junto con un analizador sintctico cuyo orden de anlisis sea incompatible con el de
05-CAPITULO 05 9/2/06 11:51 Pgina 223
aqulla. En tal caso, es de inters disponer de una tcnica general para realizar el anlisis se-
mntico. Dicha tcnica exige un compilador de dos o ms pasos y puede resumirse en el siguiente
esquema:
1. Construir el rbol del anlisis sintctico.
2. Determinar las dependencias entre los atributos mediante el estudio de las acciones se-
mnticas de la gramtica.
3. Determinar un orden entre los atributos del rbol, compatible con las dependencias obte-
nidas en el paso anterior.
4. Establecer un recorrido del rbol compatible con el orden del paso 3.
5. La ejecucin de las acciones semnticas que aparecen al recorrer el rbol segn indica el
paso 4 completa el anlisis semntico del programa compilado.
Sin embargo, todos los ejemplos de este captulo y la mayora de los problemas reales pueden
solucionarse sin necesidad de utilizar esta tcnica general. En los prximos prrafos se ver que
la tcnica general transforma algunos aspectos del anlisis semntico en la solucin de un pro-
blema clsico de lgebra: la construccin de un grafo que representa una relacin y la determi-
nacin de un recorrido sobre el grafo, compatible con la relacin y que visite todos los nodos.
Determinacin de las dependencias entre los atributos: En los casos ms sencillos, pue-
de hacerse por simple inspeccin visual, como se hizo en los ejemplos de las Figuras 5.5,
5.6 y 5.7. Tambin se puede utilizar, como tcnica general, un grafo de dependencias. Para
ello hay que tener en cuenta las siguientes consideraciones:
Las instrucciones de las acciones semnticas pueden representarse de la siguiente ma-
nera, que tiene en cuenta nicamente la propagacin de los atributos:
b= f(c
1
,...,c
n
)
donde tanto b como c
i
,,i{1,..,n} son atributos, y f representa el clculo me-
diante el cual, a partir de los valores de c
1
,...,c
n
, se obtiene el de b. En este caso, se
dir que el atributo b depende de los atributos c
1
,...,c
n
.
Los efectos laterales que pueden modificar la informacin global de la gramtica pue-
den representarse de la misma manera:
g(K,c
1
,...,c
n
)
donde K es la informacin global. Antes de aplicar el algoritmo de creacin del grafo,
se crea un nuevo atributo ficticio (a) para que la expresin anterior se transforme en la
siguiente:
a=g(K,c
1
,...,c
n
)
que se trata como cualquier otra instruccin.
La Figura 5.14 muestra el pseudocdigo de un algoritmo para la construccin del grafo de de-
pendencias.
224 Compiladores e intrpretes: teora y prctica
05-CAPITULO 05 9/2/06 11:51 Pgina 224
Determinacin de un orden compatible con las dependencias: Lo ms frecuente es que
esto pueda hacerse directamente sobre el rbol de anlisis. Las Figuras 5.15, 5.16 y 5.17
muestran un orden posible para los rboles de las Figuras 5.5, 5.6 y 5.7.
Captulo 5. Anlisis semntico 225
Figura 5.14. Pseudocdigo para la construccin del grafo de dependencias entre los atributos
de una gramtica.
grafo ConstruirGrafoDependencias
(arbol as, gramatica_atributos ga)
{
nodo n; atributo_semntico a; accion_semantica acc;
grafo gd = vaco;
`Recorrer cada nodo (n) del rbol sintctico as
`Recorrer cada atributo (a) del smbolo de n
AadirNodo (nuevo_nodo(a), gd);
`Recorrer cada nodo (n) del rbol sintctico as
`Recorrer acciones (acc=b:=f(c
1
,...,c
k
)) de n
`Para i de 1 a k
AadirArco (nuevo_arco(c
i
,b),gd);
}
E
E
E
E
E E
c
c c
5
4 3
(
+ )
*
v:4
t:i
v:3
t:i
v:3
t:i
v:4
t:i
v:5
t:i
v:5
t:i
v:35
t:i
v:7
t:i
v:7
t:i
1
2
3
4
5
6
9
8
7
Figura 5.15. Un orden compatible con las dependencias entre atributos del rbol
de la Figura 5.5.
05-CAPITULO 05 9/2/06 11:51 Pgina 225
226 Compiladores e intrpretes: teora y prctica
D
T
L
L
L
int var1 x y , ,
i i i
t:i
t:i t:i
t:i
t:i
v:0
t:i
v:0
t:i
v:0
t:i
1
2
3
4
5
6
7
8
Figura 5.16. Un orden compatible con las dependencias entre atributos del rbol
de la Figura 5.6.
P
TS
y(int)
x(int)
var1(int)
D
T
int var1 x y x 3 4 5 ,
=
( ) *
i i i i
c c c
E
E
A
L
L
L
E
E
E E
t:i
t:i
t:i
t:i
t:i
t:i
v:0
t:i
v:0
t:i
v:0
t:i
v:0
t:i
v:3
t:i
v:4
t:i
v:5
t:i
v:5
t:i
v:35
t:i
v:7
t:i
v:7
t:i
v:4
t:i
v:3
t:i
1
2
3
4
5
6
7
8 9
10 12 16
17
18
15
14
13
11
19
Figura 5.17. Un orden compatible con las dependencias entre atributos del rbol
de la Figura 5.7.
05-CAPITULO 05 9/2/06 11:51 Pgina 226
Dependencias circulares: Se dice que una gramtica tiene dependencias circulares cuan-
do existen al menos dos atributos b y c, e instrucciones en las acciones semnticas con la
siguiente estructura (el orden que ocupan los atributos como argumentos de las funciones
es irrelevante):
b= f
b
(c,d
1
,...,d
n
)
c= f
c
(b,a
1
,...,a
m
)
Esto significa que es posible que exista un rbol en el que aparezca un nodo etiquetado con el
smbolo b y otro con el smbolo c, tal que, de acuerdo con la primera instruccin, b tenga que
ser analizado antes que c, y de acuerdo con la segunda tenga que seguirse el orden inverso.
Las dependencias circulares presentan una dificultad insalvable para el anlisis semntico. La
nica solucin es considerar que las gramticas con dependencias circulares estn mal diseadas,
y refinarlas hasta que se elimine el problema. Si las gramticas de atributos se consideran como
un nuevo lenguaje de programacin, este error de diseo sera similar al de programar, con un
lenguaje de programacin imperativo, un bucle o una funcin recursiva sin condicin de salida,
lo que dara lugar a una ejecucin permanente.
5.3.5. Evaluacin de los atributos por los analizadores
semnticos en los compiladores de slo un paso
Se ha dicho anteriormente que en el diseo de compiladores de un solo paso es necesario com-
patibilizar el orden de recorrido inducido por el analizador sintctico utilizado con el que preci-
sa la relacin de dependencia entre los atributos. Tambin se ha dicho que las gramticas de
atributos que dependen de su izquierda aseguran la compatibilidad con los analizadores descen-
dentes, mientras que las que slo tienen atributos sintetizados aseguran la compatibilidad con los
analizadores ascendentes.
Para completar el anlisis semntico en este caso, slo queda describir cmo se puede reali-
zar la evaluacin de los atributos. De forma general, se pueden seguir las siguientes indicaciones:
En los analizadores que utilizan tablas de anlisis (por ejemplo ascendentes) se pueden eva-
luar los atributos cuando se completa la parte derecha de las reglas (en el momento de su re-
duccin). En ese instante se sacan de la pila los smbolos asociados con la parte derecha de
la regla (junto con su informacin semntica) y lo nico que hay que aadir al algoritmo es
el clculo de la informacin semntica del smbolo no terminal de la parte izquierda, antes
de ubicarlo en la posicin adecuada para continuar el anlisis. Este clculo es posible, ya que
los atributos sintetizados slo necesitan la informacin semntica de los smbolos de la par-
te derecha, y esa informacin est disponible cuando se realiza la reduccin.
Los analizadores que permiten ms libertad en la posicin de las acciones semnticas
(vase la Seccin 5.2.4) pueden ejecutarlas a medida que las encuentran. Un ejemplo de
esta situacin son los analizadores descendentes recursivos. La tcnica para su construc-
cin, descrita en el Captulo 4, codificaba una funcin recursiva para cada smbolo no ter-
minal de la gramtica. Lo nico que hay que aadir al algoritmo es la codificacin de las
Captulo 5. Anlisis semntico 227
05-CAPITULO 05 9/2/06 11:51 Pgina 227
rutinas semnticas, e invocarlas en la posicin que ocupen en la parte derecha de la regla.
La correccin del diseo de la gramtica asegurar que se dispone de los valores de todos
los atributos necesarios para la ejecucin correcta de la accin semntica.
En el desarrollo de cada compilador concreto, siempre es posible extender este modelo, aun-
que no sea mediante el uso de una tcnica general. Todos los analizadores semnticos utilizan una
pila semntica para guardar los valores de los atributos de los smbolos. Las tcnicas generales
proponen un tratamiento estndar de la pila mediante las funciones push y pop. En el desarro-
llo de un compilador concreto, sera posible consultar la pila semntica de una manera ms fle-
xible, ya que se trata de una estructura propia del analizador. En ese caso, se podra calcular un
conjunto ms amplio de atributos: todos los que dependan de los valores de los atributos que se
encuentran en la pila en un momento dado.
Gramticas de atributos para el anlisis semntico
de los lenguajes de programacin
El objetivo de esta seccin es sugerir, de la manera ms genrica posible, cmo se pueden solu-
cionar los problemas relacionados con las construcciones ms frecuentes que se utilizan en los
lenguajes de programacin de alto nivel. Se supondr que se est especificando una gramtica de
atributos sintetizados con informacin global, pues este tipo de gramticas es compatible con los
compiladores de un solo paso que utilicen analizadores sintcticos, tanto ascendentes como des-
cendentes. El lector ser capaz de solucionar otros problemas concretos adaptando el contenido
de esta seccin mediante el uso de las nociones de la Seccin 5.2.5.
A continuacin se recuerdan algunos de los aspectos ms generales de los lenguajes de pro-
gramacin de alto nivel:
El tipo de dato apuntador o, ms popularmente, puntero. Algunos lenguajes de programa-
cin permiten declarar un tipo de dato que apunta a objetos de otros tipos. Su peculiaridad
principal es que slo ocupa la memoria precisa para contener la direccin a la que apunta,
y no depende del tamao del objeto apuntado. Su existencia permite modificar los datos
apuntados mediante uno cualquiera de los punteros que apunten a ellos. El siguiente frag-
mento de cdigo C muestra un ejemplo:
int * p_int;
int *** p_p_p_int;
int a;
a = 5;
p_int = &a;
/* Se imprime 5, el valor de a y de *p_int */
printf(%d\n, *p_int);
(*p_int)++;
Ejemplo
5.12
5.4
228 Compiladores e intrpretes: teora y prctica
05-CAPITULO 05 9/2/06 11:51 Pgina 228
/* Se imprime 6, el valor de a y de *p_int */
printf(%d\n, a);
En la primera instruccin se declara un apuntador a un dato de tipo entero.
La segunda demuestra la posibilidad de anidar niveles mltiples de punteros.
En la quinta instruccin, p_int pasa a apuntar al identificador a (&a es la direccin del
identificador a), de forma que el valor apuntado por el puntero (*p_int) es el mismo que
el de la variable a, como indica la sexta instruccin, y se puede modificar el valor de a me-
diante p_int, como demuestran las dos ltimas instrucciones.
El tipo de dato apuntador abre la discusin sobre dos formas de gestionar la memoria: la me-
moria esttica y la memoria dinmica. En la Seccin 10.1 se encontrar una descripcin de-
tallada de este tema.
Los procedimientos, funciones o subrutinas presentan dificultades, tanto en su declaracin
como en su invocacin. Entre el analizador semntico y el generador de cdigo, se tienen que
gestionar la memoria asignada a las variables automticas (las variables locales de los proce-
dimientos) y el convenio de llamadas utilizado: el mecanismo mediante el que el programa
que invoca comunica al programa invocado el valor de sus argumentos y la manera en la que
el procedimiento o funcin devuelve el control al programa que la invoca, as como el valor
de retorno, si existe. En la Seccin 10.1 se encontrar una descripcin completa de estos as-
pectos.
5.4.1. Algunas observaciones sobre la informacin
semntica necesaria para el anlisis de los
lenguajes de programacin de alto nivel
Para la gestin de los lenguajes de alto nivel es necesario tener en cuenta cierta informacin, que
podra organizarse de la siguiente manera:
Atributos semnticos asociados a los operandos: Se llama operando a los elementos de
un programa asociados a un valor, como variables, funciones, etiquetas, etc. Estos datos de-
ben llevar la siguiente informacin semntica asociada:
Su nombre: el identificador por el que se los reconoce.
Su tipo: vase, por ejemplo, la Tabla 6.2, en el captulo siguiente, dedicado a la gene-
racin de cdigo.
Su direccin: para una variable, puede ser una posicin en la memoria, un puntero a la
tabla de smbolos, o el nombre de un registro. En variables de tipo array indexadas
(como en la expresin v[3]del lenguaje C) es preciso especificar dos valores: el nom-
bre de la variable y el desplazamiento necesario para localizar el elemento concreto. En
el caso de los punteros, puede que baste con especificar su nombre (como en la ins-
truccin *p del lenguaje C) o que se necesite tambin un desplazamiento u offset, como
en *(p+4).
Captulo 5. Anlisis semntico 229
05-CAPITULO 05 9/2/06 11:51 Pgina 229
Su nmero de referencias: si el lenguaje permite utilizar el tipo de dato apuntador, pue-
de ser necesario anotar su nivel de anidamiento.
Informacin global: Los mecanismos mencionados a continuacin afectan a atributos que
suelen ser heredados, o almacenados en informacin global.
Tabla de smbolos. Tiene que ser accesible desde todas las reglas de la gramtica. Desde
algunas, ser actualizada para insertar nuevos identificadores o modificar la informa-
cin que se conoce sobre ellos. Desde otras, ser consultada para comprobar la correc-
cin del programa.
Lista de registros usados. Se utiliza en la generacin de cdigo y se refiere a los atri-
butos relacionados con la direccin donde est almacenado un objeto, para el caso de
los que ocupan registros. Para asegurar la correccin del programa objeto resultado de
la compilacin, el analizador semntico tiene que proporcionar mecanismos para que
los registros no sean modificados por error, cuando su informacin todava es til.
Informacin para la gestin de etiquetas. Como se explicar en la Seccin 6.1, en el ca-
ptulo sobre generacin de cdigo, la estructura bsica de control de flujo en los len-
guajes simblicos (ensambladores) y de la mquina es el salto, tanto condicional como
incondicional, a una direccin o etiqueta, situada en el espacio de instrucciones.
Utilizando nicamente saltos a etiquetas, un compilador que genere cdigo simblico o
de mquina tiene que generar cdigo equivalente a las estructuras de control del flujo
de programa que se utilizan en los lenguajes de alto nivel (instrucciones condicionales,
bucles, etc.). El hecho de que las etiquetas tengan que tener un nombre nico dentro del
cdigo, junto con la posibilidad de mezclar estructuras o estructuras anidadas en el pro-
grama fuente (como instrucciones del tipo if-then-else dentro de otras estructuras if-
then-else), obliga a articular mecanismos para controlar que las etiquetas sean distintas
y que los saltos que conducen a ellas sean coherentes.
Informacin para la gestin del tipo de los identificadores. En los lenguajes de progra-
macin se dan dos circunstancias frecuentes que suelen requerir atributos heredados o
informacin global para su representacin con gramticas de atributos. La primera es la
gestin del tipo de los identificadores en las instrucciones en las que se puede declarar
una lista de variables del mismo tipo en una sola instruccin. La segunda es la declara-
cin de los argumentos de las funciones.
Las prximas secciones resumen de manera intuitiva la gestin semntica asociada con las
construcciones ms frecuentes de los lenguajes de programacin. Se puede encontrar un ejemplo
completo de la aplicacin de estas ideas a la construccin de un compilador para un lenguaje sen-
cillo en http://www.librosite.net/pulido
5.4.2. Declaracin de identificadores
La mayora de los lenguajes de programacin proporcionan una sintaxis para la declaracin de
identificadores, semejante a la siguiente regla de produccin:
<declaracion> ::= <tipo> <identificador>
230 Compiladores e intrpretes: teora y prctica
05-CAPITULO 05 9/2/06 11:51 Pgina 230
Algunos permiten especificar una lista de identificadores en lugar de uno solo. El analizador
semntico tendr que encargase de las siguientes tareas:
Tras procesar el smbolo no terminal <tipo>, el analizador morfolgico debe proporcio-
nar, como valor de su atributo, el tipo de dato que se est declarando. Esta informacin ten-
dr que propagarse, modificando tal vez de forma adecuada la informacin global
correspondiente, para que est accesible ms tarde, cuando se haya procesado el nombre del
identificador.
Tras procesar el smbolo no terminal <identificador>, debe consultarse la tabla de
smbolos para comprobar que no se ha declarado previamente la misma variable, recuperar
el tipo de la declaracin, e insertar el identificador nuevo en la tabla de smbolos.
Los distintos lenguajes de programacin facilitan diversos tipos de datos (arrays, apunta-
dores), junto con las condiciones que tienen que satisfacer para su declaracin correcta. La
accin semntica de esta regla debe gestionar toda la informacin necesaria para cumplir
esas condiciones.
5.4.3. Expresiones aritmticas
Las expresiones aritmticas son, posiblemente, la parte del lenguaje que resulta ms compleja
para el anlisis semntico y la generacin de cdigo. El analizador semntico tiene que asegurar
que se satisfacen las restricciones de tipo de los operandos que aparecen en las expresiones. Esto
supone aplicar las normas de transformacin de tipos compatibles (enteros de distinto tamao,
reales de distinta precisin, transformaciones entre los tipos entero y real, etc.). Es tarea del ge-
nerador obtener un programa objeto que evale correctamente las expresiones.
La Seccin 6.1.2 explica un procedimiento general para la gestin de estos dos aspectos. Este
tipo de generacin requiere que el analizador semntico mantenga y actualice la informacin
relativa al tipo, nmero de referencias y direccin de almacenamiento de los operandos, as como
la lista de registros disponibles.
Una de las dificultades para la generacin de cdigo para calcular expresiones es la determi-
nacin del lugar donde se almacenarn los resultados intermedios. El sistema ofrece un conjun-
to limitado de registros. El analizador semntico tendr que comprobar si quedan registros
disponibles, y en caso contrario localizar espacio en la pila o en la memoria esttica. Una vez
asignada memoria a un resultado intermedio, tendr que actualizar la informacin semntica co-
rrespondiente para continuar el anlisis adecuadamente.
5.4.4. Asignacin de valor a los identificadores
La mayora de los lenguajes de programacin proporcionan una sintaxis para la asignacin de va-
lor a los identificadores, semejante a la siguiente regla de produccin:
<asignacion> ::= <identificador> <expresion>
Captulo 5. Anlisis semntico 231
05-CAPITULO 05 9/2/06 11:51 Pgina 231
El analizador semntico tendr que encargase de las siguientes tareas:
El analizador morfolgico propaga la informacin semntica del nombre del identifica-
dor.
Tras procesar el smbolo <identificador>, hay que consultar la tabla de smbolos
para comprobar que ya ha sido declarado y recuperar su informacin semntica asociada,
que incluye su tipo.
Tras procesar el smbolo no terminal <expresion>, habr que comprobar la compatibi-
lidad entre los tipos de la expresin y el identificador, para que la asignacin sea correcta.
5.4.5. Instrucciones condicionales
La mayora de los lenguajes de programacin utilizan la estructura de instrucciones condiciona-
les indicada por la siguiente regla de produccin:
<condicional>::=if <expresion> then <instruccion>
Como se ha mencionado anteriormente, al generar cdigo en lenguaje simblico, hay que
construir un fragmento de programa objeto equivalente con saltos a etiquetas, lo que obliga a
que el analizador semntico gestione las etiquetas de manera adecuada. A continuacin se
muestra el esquema de un fragmento de cdigo simblico equivalente a esta instruccin con-
dicional:
FIN_THEN:
Bastar con que la instruccin de la rama then contenga otra instruccin condicional para
que las dos etiquetas FIN_THEN colisionen, originando un error en el programa. Este problema
puede solucionarse fcilmente si se aade a la etiqueta algn carcter que la haga nica. Esta so-
lucin obliga al analizador semntico a conservar la informacin necesaria para que cada estruc-
tura tenga accesible la etiqueta adecuada. Tambin tendra que asegurar que la expresin que se
comprueba en la condicin es del tipo permitido por las especificaciones del lenguaje de progra-
macin (entero, booleano, etc.).
La mayora de los lenguajes de programacin facilitan instrucciones ms potentes, como el if-
then-else, que se tratar de manera anloga:
<condicional>::=if <expresion> then <instruccion>
else <instruccion>
232 Compiladores e intrpretes: teora y prctica
05-CAPITULO 05 9/2/06 11:51 Pgina 232
5.4.6. Instrucciones iterativas (bucles)
Casi todos los lenguajes de programacin proporcionan instrucciones iterativas con una sintaxis
parecida a la de la siguiente regla de produccin:
<bucle> ::= while <expresion> do <instruccion>
Estas instrucciones presentan las mismas peculiaridades que las condicionales, tanto en lo re-
lativo a las etiquetas, como en lo referente a las comprobaciones de tipo de la condicin.
5.4.7. Procedimientos
La mayora de los lenguajes de programacin permiten declarar funciones y subrutinas con una
sintaxis parecida a la de la siguiente regla de produccin:
<subrutina>::=<tipo><identificador>(<lista_argumentos>)
<declaracion> <instruccion>
El analizador semntico se encargar de realizar las siguientes comprobaciones:
El tipo y el identificador se tratan de manera anloga a la de la declaracin de variables, excepto
que se tiene que indicar en la tabla de smbolos que el identificador representa una funcin.
La declaracin de la lista de argumentos implica actualizar la informacin que se conserva
al respecto, que tiene que ser accesible en el momento de la invocacin del procedimiento.
Lo ms frecuente es incluirla en la tabla de smbolos.
La mayora de los lenguajes de programacin realizan llamadas o invocaciones a los procedi-
mientos con una sintaxis similar a la de la siguiente regla de produccin:
<expresion>::=<identificador>(<lista_expresiones>)
El analizador semntico debe realizar las siguientes tareas:
Tras procesar el smbolo <identificador>, cuyo nombre debe propagarse, hay que
comprobar en la tabla de smbolos que se ha declarado un procedimiento con ese nombre,
y recuperar la informacin que describe la lista de sus argumentos.
Tras procesar el parntesis de cierre, se podr completar la verificacin de la correspon-
dencia del nmero, tipo y orden de los argumentos de la invocacin y los de la declaracin.
Algunas herramientas para la generacin
de analizadores semnticos
En la actualidad se encuentran disponibles en Internet, de forma gratuita, diversas herramientas
que generan analizadores sintcticos a partir de gramticas en BNF que cumplan ciertas condi-
ciones, y que a veces pueden generar tambin el analizador semntico, si se les proporciona una
5.5
Captulo 5. Anlisis semntico 233
05-CAPITULO 05 9/2/06 11:51 Pgina 233
gramtica de atributos. Una de las ms populares y conocidas es yacc (yet another compiler
compiler), objetivo de esta seccin. Yacc est incluida en las distribuciones estndares de Unix
y Linux. Otra herramienta, compatible con yacc, es Bison, que est disponible tanto para
Windows como para Linux. Aunque lo que se diga en esta seccin es aplicable tanto a yacc
como a Bison, por motivos histricos slo se har referencia a yacc.
La aplicacin yacc toma como entrada un fichero, que contiene una gramtica con atributos sin-
tetizados e informacin global, y genera una aplicacin escrita en el lenguaje de programacin C, que
implementa un analizador sintctico ascendente LALR(1), junto con el correspondiente analizador
semntico, aunque excluye el analizador morfolgico, que debe obtenerse de manera independiente.
Esta seccin no tiene por objeto proporcionar un manual exhaustivo de la herramienta, sino su-
gerir indicaciones prcticas para comprobar el funcionamiento de algunas de las gramticas de atri-
butos utilizadas en el captulo y para que, posteriormente, el lector pueda beneficiarse de su ayuda en
el diseo de las mismas. Puede encontrarse documentacin ms detallada sobre yacc, tanto en
Internet (http://www.librosite.net/pulido), como en la bibliografa especializada [1].
El resto de la seccin explicar la estructura bsica del fichero fuente de yacc, las directivas
que se utilizan para describir la gramtica de atributos de entrada, la notacin con la que deben
escribirse las reglas y las acciones semnticas, as como algunas consideraciones prcticas fun-
damentales para trabajar con yacc. Estos conceptos se ilustrarn mediante la solucin con yacc
de los ejemplos A
5_8
y A
5_10
de gramticas de atributos.
5.5.1. Estructura del fichero fuente de yacc
El fichero de entrada para yacc tiene tres secciones separadas por una lnea, que slo contiene
los caracteres %%, sin espacios a la izquierda. Las tres secciones son:
La seccin de definiciones.
La seccin de reglas.
La seccin de cdigo de usuario.
La Figura 5.18 muestra un esquema del fichero de entrada de yacc.
234 Compiladores e intrpretes: teora y prctica
Figura 5.18. Estructura del fichero de entrada para yacc.
Seccin de definiciones:
%{
/* delimitadores de cdigo C */
%}
%%
Seccin de reglas:
%%
Seccin de funciones de usuario
05-CAPITULO 05 9/2/06 11:51 Pgina 234
5.5.2. Seccin de definiciones
En esta seccin se incluyen las definiciones propias de yacc y las declaraciones escritas en len-
guaje C que necesite el analizador semntico. Estas ltimas deben separarse de las primeras, de-
limitndolas mediante dos lneas que slo contienen, respectivamente, los caracteres %{ y %}, sin
espacios a la izquierda (vase la Figura 5.18). Entre estas dos lneas se escribirn las instruccio-
nes declarativas necesarias, escritas en C.
En la parte de declaraciones propias de yacc se especifican aspectos generales, mediante el
uso de directivas.
Directiva %union: permite declarar los atributos que se van a asociar con los smbolos de
la gramtica. Su efecto es equivalente a declarar una estructura de datos de tipo union en
el lenguaje C.
Directiva %type: asocia los atributos especificados mediante la directiva %union con los
smbolos no terminales de la gramtica. Por lo tanto, tiene que haber una directiva %type
por cada smbolo que posea atributos semnticos.
Directiva %token: define los smbolos terminales de la gramtica que no estn re-
presentados literalmente. Puede utilizarse tambin para indicar los atributos semnti-
cos asociados a los smbolos terminales, con la misma notacin que la directiva
anterior.
Directiva %start: especifica el axioma de la gramtica. Es opcional; si no se utiliza, el
axioma ser la parte izquierda de la regla que aparece en primer lugar en el fichero de en-
trada de yacc.
A lo largo de esta seccin se va a solucionar con yacc la gramtica de atributos A
5_8
. A ello se
dedicar el Ejemplo 5.12, utilizndose siempre el mismo nmero de ejemplo en los pasos suce-
sivos de la escritura del fichero fuente yacc para dicha gramtica.
En este ejemplo, en las acciones semnticas y en otras funciones de usuario, se escribirn
mensajes por la salida estndar mediante la instruccin
fprintf(stdout, );
Para poder utilizar la funcin fprintf, es preciso incluir el archivo de definiciones
stdio.h. Esto tiene que hacerse en la seccin de declaraciones, en la parte reservada a las ins-
trucciones en lenguaje C. Por tanto, el fichero fuente para este ejemplo debe contener la siguien-
te seccin de declaracin C:
%{
#include <stdio.h>
%}
Ejemplo
5.13
Captulo 5. Anlisis semntico 235
05-CAPITULO 05 9/2/06 11:51 Pgina 235
Por otra parte, todos los smbolos de la gramtica A
5_8
utilizan el mismo atributo de tipo en-
tero, llamado profundidad, por lo que la directiva %union necesaria es la que se muestra a
continuacin:
%union
{
int profundidad;
}
Para especificar que todos los smbolos no terminales de la gramtica A
5_8
tienen el atributo
profundidad, tienen que especificarse las siguientes instrucciones:
%type <profundidad> lista
%type <profundidad> lista_interna
Esta gramtica no asigna atributos semnticos a los smbolos terminales, por lo que no se ne-
cesita la directiva %token. Por otra parte, el axioma es el smbolo no terminal lista. Se pue-
de aadir la siguiente instruccin:
%start lista
Uniendo las componentes anteriores, la seccin de definiciones del archivo de entrada para la
gramtica A
5_8
ser:
%{
#include <stdio.h>
%}
%union
{
int profundidad;
}
%type <profundidad> lista
%type <profundidad> lista_interna
%start lista
Como segundo ejemplo, se preparar el fichero fuente yacc para la gramtica A
5_10
. En este
caso tambin se utilizar el archivo de definiciones stdio.h. Tambin se debe inicializar la in-
formacin global de la gramtica. En este ejemplo, se utilizar una variable global de tipo ente-
ro para representar el nmero de elementos, que inicialmente tomar el valor 0. La seccin de
declaraciones en lenguaje C ser:
%{
#include <stdio.h>
int num_elementos = 0;
%}
En la gramtica A
5_10
los smbolos no tienen atributos, porque slo se utiliza informacin glo-
bal. Por tanto, no se necesita la directiva %union. Por la misma razn, tampoco se precisa la di-
Ejemplo
5.14
236 Compiladores e intrpretes: teora y prctica
05-CAPITULO 05 9/2/06 11:51 Pgina 236
rectiva %type. Tampoco se asignan atributos semnticos a los smbolos terminales, por lo que
no se utilizar la directiva %token. Finalmente, el axioma es el smbolo no terminal lista. Se
puede utilizar la siguiente instruccin:
%start lista
Uniendo las componentes anteriores, la seccin de definiciones del archivo de entrada para la
gramtica A
5_10
ser:
%{
#include <stdio.h>
int num_elementos = 0;
%}
%start lista
5.5.3. Seccin de reglas
Sintaxis para las reglas de produccin independientes del contexto: Se utiliza una no-
tacin parecida a la notacin BNF. Por ejemplo, la regla X::=Y
1
Y
2
...Y
n
se escribe de la
siguiente forma:
X : Y
1
Y
2
...Y
n
donde los espacios alrededor del smbolo : son opcionales. Cuando en la regla aparecen
smbolos terminales, pueden escribirse literalmente si se cumplen las siguientes condicio-
nes:
Su longitud es igual a 1, como en los smbolos de apertura y cierre de parntesis, ( y ).
El analizador morfolgico los devuelve literalmente, mediante instrucciones del tipo
return (;
return );
Se utiliza la notacin del lenguaje C para representar caracteres. Por ejemplo:
lista: ( lista_interna )
Es posible utilizar otros mecanismos de comunicacin entre los analizadores morfolgico y
sintctico. Por ejemplo, se podra haber definido el smbolo INICIO_LISTA en lenguaje C,
para representar el smbolo terminal (. Para ello, tendran que haberse realizado las siguientes
acciones:
El analizador semntico tiene que saber que existe un smbolo terminal llamado
INICIO_LISTA. Eso se hace en la seccin de declaraciones, con la directiva %token, de
la siguiente manera:
%token INICIO_LISTA
Captulo 5. Anlisis semntico 237
05-CAPITULO 05 9/2/06 11:51 Pgina 237
Gracias a esto, en el programa C generado por yacc se definir un smbolo con este nom-
bre. Cuando encuentre el carcter (, el analizador morfolgico tiene que devolver al ana-
lizador semntico este smbolo, por ejemplo, as:
if((c=fgetc(stdin))==()return INICIO_LISTA;
En las reglas de produccin se utilizar el nombre del smbolo en los lugares donde apare-
ca el smbolo terminal (.
lista: INICIO_LISTA lista_interna )
Para especificar una regla-, es suficiente omitir la parte derecha de la regla. As, la regla
lista_interna::= se escribira en yacc as:
lista_interna:
Las reglas deben separarse mediante el smbolo ;. Por ejemplo:
lista_interna: lista_interna ( lista_interna ) ;
lista_interna: ;
Cuando varias reglas comparten la misma parte izquierda, puede utilizarse el smbolo |
para separar sus partes derechas, como en la notacin BNF. El ejemplo anterior podra es-
cribirse tambin de la siguiente manera:
lista_interna: lista_interna ( lista_interna ) ;
| ;
Sintaxis para las acciones semnticas: Las acciones semnticas asociadas a una regla se
escriben a continuacin de sta encerradas entre llaves. En el caso de que una regla no ten-
ga ninguna accin semntica asociada, debe escribirse
{}
Dentro de la accin semntica se escriben instrucciones en el lenguaje C. En ellas, pueden
aparecer los smbolos de la tabla 5.1, que tienen significado especial para yacc, y que per-
miten acceder a la informacin semntica de la gramtica. Se supone, en la tabla, que se
est describiendo la regla X : Y
1
Y
2
...Y
n
).
238 Compiladores e intrpretes: teora y prctica
Smbolo Significado
$$ Valor semntico de X
$1 Valor semntico de Y
1
$n Valor semntico de Y
n
(n tiene que ser un nmero)
Tabla 5.1. Smbolos que se pueden utilizar en las acciones semnticas asociadas a las reglas yacc.
05-CAPITULO 05 9/2/06 11:51 Pgina 238
A continuacin se muestra la seccin de reglas yacc para la gramtica A
5_8
.
lista: ( lista_interna )
{
$$ = $2 + 1;
fprintf(stdout, PROF. TOTAL= %d\n, $$);
} ;
lista_interna: lista_interna ( lista_interna )
{
if ( $1 > $3 )
$$ = $1 ;
else
$$ = $3+1 ;
} ;
lista_interna:
{
$$ = 0;
} ;
A continuacin se muestra la seccin de reglas yacc para la gramtica A
5_10
.
lista: ( lista_interna )
{
num_elementos ++;
fprintf(stdout, NUM. LISTAS= %d\n,
num_elementos );
} ;
lista_interna: lista_interna ( lista_interna )
{
num_elementos++;
} ;
lista_interna:
{
} ;
5.5.4. Seccin de funciones de usuario
En esta seccin, el programador debe incluir las funciones escritas en lenguaje C que considere
oportunas y que puedan utilizarse en las acciones semnticas. Entre ellas, las ms significativas
son las siguientes:
La funcin int main(): Es el programa principal y debe invocar a una funcin llamada
yyparse, que realiza el anlisis semntico completo.
La funcin int yylex(): Yacc supone que esta funcin realiza el anlisis morfolgico.
Slo hay que tener en cuenta que tiene que devolver el valor 0 cuando se localiza el final
Ejemplo
5.16
Ejemplo
5.15
Captulo 5. Anlisis semntico 239
05-CAPITULO 05 9/2/06 11:51 Pgina 239
del fichero de entrada, y que las unidades sintcticas se representan mediante nmeros en-
teros. En la direccin de Internet http://www.librosite.net/pulido se dan in-
dicaciones sobre el uso de unidades sintcticas de estructura ms compleja.
En ambos casos, puede utilizarse el siguiente cdigo:
int main()
{
return( yyparse());
}
int yylex()
{
int c;
c=fgetc(stdin);
while (( c != EOF ) &&
( c != ( ) &&
( c != ) )
)
c = fgetc(stdin);
if ( c == EOF ) return 0;
else return c;
}
5.5.5. Conexin entre yacc y lex
Ya se ha explicado en la Seccin 3.9 que lex es una herramienta, similar a yacc, que genera
automticamente analizadores morfolgicos. La funcin que genera lex para que se pueda in-
vocar el analizador se llama int yylex(). La coincidencia de nombres no es casual, pues es
muy frecuente utilizar lex para generar el analizador morfolgico empleado por yacc.
En la direccin de Internet http://www.librosite.net/pulido est disponible
una explicacin del uso de estas herramientas para la implementacin de un compilador com-
pleto.
Resumen
Tras el estudio de este captulo, el lector ser capaz de abordar el desarrollo de un analizador se-
mntico y de incorporarlo al compilador o intrprete del lenguaje de programacin estudiado.
Para ello, se describen previamente los objetivos generales del analizador semntico y su relacin
con el resto de las componentes de un compilador o de un intrprete, tanto en los casos de anli-
sis en un paso como en los de dos o ms pasos.
5.6
Ejemplos
5.15
y 5.16
240 Compiladores e intrpretes: teora y prctica
05-CAPITULO 05 9/2/06 11:51 Pgina 240
Posteriormente se dedica una seccin a la descripcin detallada de la herramienta ms utili-
zada en el anlisis semntico: las gramticas de atributos. La seccin comienza con una presen-
tacin informal, mediante ejemplos, de las extensiones que hay que aadir a las gramticas
independientes para que puedan hacerse cargo del anlisis semntico. As surgen de forma natu-
ral los conceptos de atributo semntico y las diferentes formas de calcular sus valores, a saber,
sntesis y herencia. Como consecuencia de esto se descubre la existencia de una relacin de de-
pendencia entre los atributos, que sugiere un orden en el recorrido del rbol de anlisis para po-
der completar el proceso de anotacin semntica. Tras esta introduccin informal, se da una
definicin formal de los conceptos presentados previamente, lo que completa la explicacin de
las gramticas de atributos.
A continuacin se analizan distintas tcnicas para el diseo de las gramticas de atributos para
la solucin de problemas concretos. Mediante ejemplos de fcil comprensin, se resaltan las con-
diciones que tiene que cumplir un problema para que se pueda resolver con cada uno de los tipos
de atributos estudiados: sintetizados y heredados. Se indica cmo se pueden sustituir los atribu-
tos heredados por informacin global a la gramtica. Aunque los ejemplos son casos particulares
sencillos, las conclusiones se pueden generalizar.
Tras explicar qu son y cmo funcionan las gramticas de atributos, se dedican dos secciones
a su uso en el anlisis semntico de los lenguajes de programacin. La primera describe cmo se
conecta una gramtica de atributos con los analizadores sintcticos para completar el analizador
semntico, tanto en el caso de los analizadores ascendentes como en el de los descendentes. La
segunda seccin analiza las dificultades asociadas a las construcciones ms frecuentes de los len-
guajes de programacin de alto nivel, y cmo se solucionan mediante una gramtica de atributos.
La ltima seccin del captulo describe yacc, una herramienta de libre distribucin que ge-
nera automticamente analizadores sintcticos y semnticos a partir de la descripcin de su gra-
mtica de atributos. El objetivo de esa seccin es dotar al lector de una herramienta que ayuda a
comprobar la correccin de las gramticas de atributos.
Bibliografa
[1] Levine, J. R.; Mason, T., y Brown, D.: Lex & yacc. Unix Programming Tools, OReilly & Associates,
Inc., 1995.
Ejercicios
1. Construir una gramtica de atributos que represente el lenguaje de los nmeros en punto flo-
tante del tipo [-][cifras][.[cifras]][e[-][cifras]]. Debe haber al menos
una cifra en la parte entera o en la parte decimal, as como en el exponente, si lo hay. La gra-
mtica de atributos tiene que ser capaz de calcular el valor del nmero.
5.8
5.7
Captulo 5. Anlisis semntico 241
05-CAPITULO 05 9/2/06 11:51 Pgina 241
2. Construir una gramtica de atributos que represente el lenguaje de las cadenas de caracteres
correctas en el lenguaje de programacin C. La gramtica de atributos tienen que ser capaz
de almacenar en una variable auxiliar global la cadena procesada.
3. Disear una gramtica de atributos para expresiones aritmticas en las que los operadores
son la divisin (/), la suma (+) y el producto (*). Los operandos pueden ser letras del alfa-
beto. La gramtica de atributos tiene que gestionar el tipo de las expresiones. Para ello apli-
car las siguientes reglas:
Las letras del alfabeto se supone que representan variables declaradas como enteras.
Las sumas y productos tienen el mismo tipo que sus operandos; en el caso de mezclar en-
teros y reales, la expresin completa ser de tipo real.
La divisin genera una expresin de tipo real independientemente del tipo de sus operan-
dos.
Construir el rbol de propagacin de atributos en el anl0isis semntico de la siguiente ex-
presin:
a/(b+c*d)
242 Compiladores e intrpretes: teora y prctica
05-CAPITULO 05 9/2/06 11:51 Pgina 242
Captulo 6
Generacin de cdigo
El mdulo de generacin de cdigo de un compilador tiene por objeto generar el cdigo equiva-
lente al programa fuente escrito en un lenguaje diferente. En funcin del tipo de lenguaje objeti-
vo, se distinguen distintos tipos de compiladores:
Cross-compilers (compiladores cruzados): traducen de un lenguaje de alto nivel a otro
lenguaje de alto nivel.
Compiladores que generan cdigo en lenguaje simblico: generan un cdigo intermedio
que despus deber ser procesado por un ensamblador.
Compiladores que generan cdigo en lenguaje de la mquina: generan directamente
programas ejecutables (*.EXE) o bien (esto es mucho ms frecuente) en un formato espe-
cial de cdigo mquina (*.OBJ) que contiene informacin adicional, y que despus ser
procesado por un programa enlazador (linker), que generar el programa ejecutable a par-
tir de uno o ms programas en formato OBJ, algunos de los cuales pueden estar contenidos
en bibliotecas (libraries) que suelen proporcionarse junto con el compilador, y que contie-
nen funciones y subrutinas prefabricadas de uso general.
En este captulo se va a suponer que el compilador genera cdigo simblico (ensamblador),
pues los ejemplos resultan mucho ms legibles, pero todo lo que se diga podr aplicarse a cual-
quier otro tipo de compilador. En los ejemplos se supondr que el cdigo generado es compren-
sible por un ensamblador tpico aplicable a la familia 80x86 a partir del microprocesador 80386,
en modo de funcionamiento de 32 bits (vase la Seccin 10.1).
Generacin directa de cdigo ensamblador
en un solo paso
Una instruccin del ensamblador genrico para el 80x86 que vamos a utilizar tiene la siguiente
sintaxis:
etiqueta: cdigo_instruccin operandos
6.1
06-CAPITULO 06 9/2/06 11:51 Pgina 243
Los operandos de las instrucciones pueden ser registros, direcciones, constantes o expre-
siones.
Existen distintos tipos de registros. En los ejemplos de esta seccin se utilizarn los si-
guientes registros de 32 bits: EAX (acumulador), EBX, ECX, EDX, ESP (puntero a la pila),
EBP, ESI y EDI.
La direccin donde se almacena el valor de una variable se representar anteponiendo el
smbolo de subrayado (_) al nombre de la variable.
Para referirse al contenido de una posicin de memoria, es necesario encerrar su direccin
entre corchetes. Por ejemplo, la instruccin mov eax,[_x] carga el contenido de la va-
riable x (de direccin _x) en el registro EAX.
Cuando una instruccin, por ejemplo mov, hace referencia a una posicin de memoria y a
un registro, el tamao de la posicin de memoria se adapta por omisin al tamao del re-
gistro. En cambio, si hace referencia a dos posiciones de memoria, es preciso especificar el
tamao de la zona de memoria afectada por la instruccin. Por ejemplo, si se trata de una
doble palabra, se usar el indicador dword.
La instruccin mov op1,op2 copia el contenido del segundo operando en el primero. Por
ejemplo, la instruccin mov eax,ebx copia el contenido del registro EBX en el registro
EAX.
La instruccin fld operando, donde el operando es una variable en punto flotante, in-
troduce el contenido del operando en la primera posicin de la pila de registros en punto
flotante y empuja hacia abajo los contenidos anteriores de dicha pila.
La instruccin fstp operando, donde el operando es una variable en punto flotante, ex-
trae de la pila de registros en punto flotante el contenido de la primera posicin de la pila
(y lo elimina de ella) y lo almacena en el operando.
La instruccin fild operando, donde el operando es una variable entera, convierte el
valor del operando a punto flotante, introduce el resultado en la primera posicin de la
pila de registros en punto flotante y empuja hacia abajo los contenidos anteriores de di-
cha pila.
La instruccin fistp operando, donde el operando es una variable entera, extrae de la
pila de registros en punto flotante el contenido de la primera posicin de la pila (y lo eli-
mina de ella), convierte dicho valor al tipo entero y lo almacena en el operando.
Instrucciones de manejo de la pila: usualmente, la pila de una aplicacin de 32 bits gestio-
na datos con tamao de dobles palabras. Al introducir datos nuevos, la pila se extiende ha-
cia posiciones de memoria con direcciones ms pequeas, por lo que el valor del puntero a
la pila (el registro ESP) disminuye en cuatro unidades cuando se introduce un dato en la
pila y aumenta en cuatro unidades cuando se extrae un dato de la pila.
La instruccin push operando resta 4 al contenido del registro ESP (puntero a la pila)
y a continuacin inserta el operando en la pila.
244 Compiladores e intrpretes: teora y prctica
06-CAPITULO 06 9/2/06 11:51 Pgina 244
La instruccin pop operando copia el contenido que est situado en la cima de la pila
(es decir, en la direccin ms baja) sobre el operando, y a continuacin suma 4 al con-
tenido del registro ESP. Por ejemplo, la instruccin pop eax almacena el contenido de
la cima de la pila en el registro EAX.
Instrucciones aritmticas.
La instruccin add op1,op2 suma los dos operandos enteros y almacena el resultado
en el primero.
La instruccin sub op1,op2 resta el segundo operando entero del primero y almace-
na en el resultado en el primero.
La instruccin mul operando, donde el operando es un entero con un tamao de 32
bits, lo multiplica por el contenido de EAX. El resultado se almacena en la concatena-
cin de los registros EDX y EAX.
La instruccin div operando, donde el operando es un entero con un tamao de 32
bits, divide la concatenacin de los registros EDX y EAX por el operando. El cociente
se almacena en el registro EAX, y el resto de la divisin en el registro EDX.
Las instrucciones fadd operando, fsub operando, fmul operando y fdiv
operando, donde el operando es una variable en punto flotante, suman, restan, mul-
tiplican o dividen (respectivamente) el operando con el contenido de la primera posi-
cin de la pila de registros en punto flotante y almacenan el resultado en la misma
posicin de la pila.
La instruccin neg operando sustituye el contenido del operando por el comple-
mento a dos de su valor original (es decir, le cambia el signo).
Instrucciones lgicas.
La instruccin and op1,op2 lleva a cabo la operacin lgica AND, bit a bit, entre los
dos operandos, y almacena el resultado en el primero.
La instruccin or op1,op2 lleva a cabo la operacin lgica OR, bit a bit, entre los dos
operandos, y almacena el resultado en el primero.
La instruccin xor op1,op2 lleva a cabo la operacin lgica XOR, bit a bit, entre los
dos operandos, y almacena el resultado en el primero.
La instruccin de comparacin cmp op1,op2 resta el segundo operando entero del pri-
mero, sin almacenar el resultado en ningn sitio. La operacin afecta a los indicadores
(flags) de la unidad aritmtico-lgica, como si la operacin se hubiera realizado realmen-
te. El contenido de estos indicadores puede utilizarse posteriormente por instrucciones de
salto.
La instruccin fcmp operando, donde el operando es una variable en punto flotante, res-
ta el operando del contenido de la primera posicin de la pila de registros en punto flotan-
te y modifica adecuadamente los indicadores, sin almacenar el resultado en ningn sitio.
Instrucciones de salto.
La instruccin jmp etiqueta (salto incondicional) salta a la direccin especificada
por la etiqueta.
Captulo 6. Generacin de cdigo 245
06-CAPITULO 06 9/2/06 11:51 Pgina 245
Despus de una instruccin de comparacin cmp op1,op2 pueden aparecer las si-
guientes instrucciones de salto condicional:
je etiqueta salta a etiqueta si op1 es igual a op2.
jne etiqueta salta a etiqueta si op1 es distinto de op2.
jl etiqueta salta a etiqueta si op1 es menor que op2.
jle etiqueta salta a etiqueta si op1 es menor o igual que op2.
jg etiqueta salta a etiqueta si op1 es mayor que op2.
jge etiqueta salta a etiqueta si op1 es mayor o igual que op2.
La instruccin jz etiqueta salta a la direccin especificada por la etiqueta si el
indicador de resultado cero est encendido, es decir, si el resultado de la ltima opera-
cin realizada fue cero. De igual manera, la instruccin jnz etiqueta salta a la di-
reccin especificada por la etiqueta si el indicador de resultado cero est apagado,
es decir, si el resultado de la ltima operacin realizada fue distinto de cero.
Instrucciones de llamada y retorno de una subrutina.
La instruccin call etiqueta invoca a la subrutina de nombre etiqueta. Para ello,
primero almacena en la pila la direccin de la siguiente instruccin a ejecutar (la direccin
de retorno) y despus salta a la direccin de memoria correspondiente a etiqueta.
La instruccin inversa a la anterior (ret) extrae de la pila el valor de la siguiente ins-
truccin que se va a ejecutar y transfiere el control a dicha instruccin.
6.1.1. Gestin de los registros de la mquina
Cada tipo de computadora funciona con su propio lenguaje de la mquina y posee una unidad
aritmtico-lgica propia. Dicha unidad contiene cierto nmero de registros de trabajo de acceso
muy rpido, en los que se puede almacenar informacin y operar con ella realizando sumas, res-
tas, comparaciones, etc. Existen mquinas (ms bien antiguas) que poseen un solo registro de tra-
bajo especial, denominado acumulador. En otras mquinas la unidad aritmtico-lgica contiene
cierto nmero (por ejemplo, 32) de registros idnticos e intercambiables. Por ltimo, algunas m-
quinas, como la serie INTEL 80x86, poseen un nmero reducido de registros de trabajo con pro-
piedades no exactamente idnticas. Estos registros son diferentes segn que el modo de trabajo
de estas mquinas sea de 16 o de 32 bits. Como se ha indicado, en 32 bits los registros desem-
pean papeles diferentes y se llaman EAX (acumulador), EBX (registro base de indexacin),
ECX (registro contador), EDX (registro complementario del acumulador), ESP (puntero a la
pila), EBP (registro base para las variables automticas en la pila, vase la Seccin 10.1), ESI y
EDI (registros de copia origen y destino). En 16 bits, los registros correspondientes se denomi-
nan AX, BX, CX, DX, SP, BP, SI y DI. Adems, existen algunos nombres adicionales que se re-
fieren a partes de los registros anteriores, como AH y AL (Bytes superior e inferior de AX), BH
y BL, CH y CL, DH y DL.
Las instrucciones de la mquina pueden hacer uso de los registros, o bien trabajar directa-
mente sobre la memoria direccionable. Sin embargo, numerosas operaciones (clculo de expre-
siones, comparacin de los valores de dos variables, etc.) exigen que al menos uno de los
246 Compiladores e intrpretes: teora y prctica
06-CAPITULO 06 9/2/06 11:51 Pgina 246
operandos se encuentre copiado sobre uno de los registros de trabajo (a veces, como se ha visto
al mencionar las instrucciones mul y div, debe encontrarse en un registro concreto). Por ello,
una parte muy importante de todo generador de cdigo tiene que ver con la carga o copia de los
valores de las variables sobre los registros de trabajo, as como la gestin de los registros, pues
al ser varios, en un momento dado podran contener el valor de ms de una variable.
Si la unidad aritmtico-lgica estuviese provista de un solo registro acumulador (como ocu-
rre en mquinas antiguas y, hasta cierto punto, en las mquinas INTEL, pues en stas el registro
EAX desempea un papel distinguido en ciertos casos), es conveniente disponer en el compila-
dor de una rutina que el generador de cdigo puede utilizar para asegurarse de que una u otra de
las variables que toman parte en un clculo determinado se encuentra cargada en el acumulador,
y en caso contrario realice la carga de una de ellas. En algunas operaciones conmutativas (como
la suma o la comparacin de la igualdad), no nos importa cul de las dos variables est cargada
en el acumulador, pues basta que sea una cualquiera de ellas. En otras operaciones no conmuta-
tivas, como la resta o la divisin, interesa, en cambio, especificar cul de los dos operandos (nor-
malmente el izquierdo) debe encontrarse en el acumulador. La funcin CAC, escrita en el lenguaje
C, asegura todas estas condiciones:
int CAC (opd *x, opd *y)
{
if (AC!=NULL && AC==y) return 1;
if (AC!=x) {
if (AC!=NULL) GEN (MOV, AC, EAX);
GEN (MOV, EAX, x);
AC=x;
}
return 0;
}
Esta rutina puede invocarse de dos maneras diferentes:
CAC (x,y): aplicable a las operaciones conmutativas, indica que se desea cargar el valor
de la variable x o de la variable y, indistintamente.
CAC (x,NULL): aplicable a las operaciones no conmutativas, indica que se desea cargar
el valor de x, exclusivamente.
La variable auxiliar AC contiene una estructura especial, llamada plataforma, que almacena
informacin sobre la variable que est cargada en el acumulador en un momento dado. Dicha in-
formacin (que coincide con la que se guarda en la pila semntica) indica cul es el nombre de
la variable, cul es su tipo, la direccin que se le ha asignado, si se trata de un vector indexado y
con qu subndice, o si la variable es accesible a travs de un puntero y con qu desplazamiento.
Esta informacin podra sustituirse, toda o en parte, por un puntero al elemento de la tabla de sm-
bolos correspondiente a la variable de que se trate.
Esencialmente, la rutina CAC realiza los siguientes pasos:
Comprueba si el acumulador contiene ya la variable y (siempre que esta variable exista),
en cuyo caso no hace nada y devuelve un 1.
Captulo 6. Generacin de cdigo 247
06-CAPITULO 06 9/2/06 11:51 Pgina 247
Comprueba si el acumulador contena ya la variable x, en cuyo caso no hace nada y de-
vuelve un 0.
Si el acumulador estaba vaco (no contena el valor de ninguna variable), se carga el valor
de x en el acumulador y se devuelve un 0.
En caso contrario, se genera una instruccin que guarde el valor actual del acumulador en
la direccin de memoria asociada a la variable que contena, se carga el valor de x en el
acumulador y se devuelve un 0.
En cualquier caso, si CAC devuelve 0, significa que x est ahora cargado en el acumulador; si
devuelve 1, que es el valor de y el que se encuentra all.
La funcin auxiliar GEN aade una instruccin nueva al programa objeto. Esta funcin admi-
te tres argumentos: el cdigo de operacin de la instruccin, el operando izquierdo y el operan-
do derecho. Si el argumento es una cadena de caracteres, se copiar directamente sobre la
instruccin generada. Si se trata de una plataforma, la funcin GEN generar el nombre apropia-
do para el operando. Por ejemplo:
GEN(MOV, EAX, x), donde x es una plataforma que define el operando A, genera-
r la instruccin MOV EAX,A.
GEN(MOV, EAX, x), donde x es una plataforma que define el operando B[4], ge-
nerar la instruccin MOV EAX,[_B+4*sizeof(tipo de B)] (si el origen de ndices
en el lenguaje fuente es cero).
Si en vez de un solo acumulador existe un conjunto de registros intercambiables, la varia-
ble AC podra sustituirse por un vector de variables de tipo plataforma, cada uno de cuyos
elementos contendr informacin sobre el operando contenido en el registro correspon-
diente.
Ser preciso distinguir los registros en punto fijo de los de punto flotante. Por otra parte, al-
gunos registros podran estar reservados para uso interno del compilador (por ejemplo, como n-
dices de bucles).
Una rutina general de carga de registros deber comenzar por seleccionar el registro que se va
a utilizar entre todos los disponibles. Para ello hay que tener en cuenta el tipo del registro sobre
el que se desea cargar (registros enteros o en punto flotante). Si no hay ninguno del tipo desea-
do, se elegir uno de los que estn ocupados, despreciando la informacin que contiene (si ya no
es necesaria) o guardndola en la posicin de memoria asociada, en caso contrario. Una vez se-
leccionado el registro, la carga propiamente dicha depender del tipo del objeto que hay que car-
gar. Para ver con claridad qu clase de operacin se debe realizar en la carga de un operando
sobre un registro, es conveniente que el diseador del compilador construya una tabla parecida a
la 6.1, que slo contiene columnas para algunos de los tipos posibles. En esta tabla, el nombre T
se aplica a una posicin de la memoria del programa objeto que el compilador utilizara como
memoria auxiliar, para introducir en ella valores intermedios que no corresponden a ninguna va-
riable del programa fuente. En este caso, sirve como etapa intermedia para la conversin de los
datos de tipo entero a punto flotante.
248 Compiladores e intrpretes: teora y prctica
06-CAPITULO 06 9/2/06 11:51 Pgina 248
6.1.2. Expresiones
A continuacin se muestra una gramtica de expresiones tpica, como las que suelen encontrarse
en muchos lenguajes de programacin:
<exp> ::= <exp> + <exp>
| <exp> <exp>
| <exp> * <exp>
| <exp> / <exp>
| - <exp>
| id
| <constante>
| ( <exp> )
| ( <compare> )
| <dereference>
<compare> ::= <exp> = <exp>
| <exp> != <exp>
| <exp> > <exp>
| <exp> >= <exp>
| <exp> < <exp>
| <exp> <= <exp>
| <compare> + <compare>
| <compare> * <compare>
| <compare>
<constant> ::= <bool_const>
| int_const
| real_const
<bool_const> ::= true
| false
Captulo 6. Generacin de cdigo 249
Carga sobre un
Tipo del operando que hay que cargar
registro de tipo
unsigned char int constante entera real
entero XOR RH,RH MOV RX,x MOV RX,x FLD x
MOV RL,x FISTP x
MOV RX,x
punto flotante XOR RH,RH FIL D x MOV T,x FLD x
MOV RL,x FLD T
MOV T,x
FLD T
Tabla 6.1. Generacin de cdigo ensamblador para la carga de un operando sobre un registro entero
(RH-RL=RX) o en punto flotante.
06-CAPITULO 06 9/2/06 11:51 Pgina 249
En realidad, la gramtica anterior es ambigua, por lo que el analizador sintctico tendr que
emplear otra algo diferente, o bien utilizar algoritmos especiales de desambiguacin (vase el
Captulo 4). En este captulo se supondr que los smbolos id, int_const, real_const,
true y false son unidades sintcticas terminales, es decir, su construccin ha sido tratada pre-
viamente por el analizador morfolgico.
Existen tipos muy diversos de expresiones, en funcin del conjunto de valores que se pue-
de calcular. Por ejemplo, se podran aceptar variables y expresiones de los tipos indicados
en la Tabla 6.2, que tambin indica el tamao que suelen tener los objetos de los tipos indi-
cados.
250 Compiladores e intrpretes: teora y prctica
Tipo de dato Tamao de cada elemento
Boolean 1 bit, o 1, 2 o 4 Bytes
char 1 Byte
unsigned char 1 Byte
short 2 Bytes
unsigned short 2 Bytes
long 4 Bytes
unsigned long 4 Bytes
int 2 o 4 Bytes
unsigned int 2 o 4 Bytes
float 4 Bytes
double 8 Bytes
Tabla 6.2. Tipos de datos y tamao que ocupa cada elemento.
Dependiendo del lenguaje, un dato de tipo Boolean puede ocupar todos los tamaos indica-
dos en la tabla. En APL, por ejemplo, los datos de este tipo se empaquetan a razn de 8 elemen-
tos por Byte, es decir, ocupan 1 bit. En C, los datos booleanos se tratan en realidad como si
fuesen de tipo int: ocupan 2 o 4 Bytes (segn que se est usando un modelo de memoria de 16
o de 32 bits, respectivamente; vase la Seccin 10.1). Si su valor es cero, se supone que repre-
sentan el valor false; en caso contrario, representan el valor true.
Para simplificar, en este apartado se supondr que slo existen los siguientes tipos de expre-
siones: Boolean, char, short y double. Adems, los datos de tipo Boolean no se podrn
mezclar en las operaciones con los de los otros tipos, pero podran obtenerse como resultado de
operaciones de comparacin realizados con dichos tipos.
La construccin de tablas de cdigo generado, como la Tabla 6.1, a la que se hizo referencia
en el tratamiento de la carga de un operando en un registro (vase la Seccin 6.1.1), es tambin
muy til para generar el cdigo asociado a las operaciones que aparecen en las expresiones, es-
06-CAPITULO 06 9/2/06 11:51 Pgina 250
pecialmente para las didicas, en las que la tabla ser de doble entrada, en funcin de los tipos
respectivos del argumento izquierdo y del derecho. A menudo, el cambio de tipo se puede reali-
zar de forma ms o menos directa a travs de la operacin de carga en registro definida anterior-
mente. La Tabla 6.3 muestra, como ejemplo, la tabla correspondiente a la operacin suma, cuya
regla es <exp> ::= <exp> + <exp>.
Las operaciones Carga x y Carga y representan la aplicacin de la Tabla 6.1 a la varia-
ble correspondiente. La realizacin de esta operacin modifica la plataforma asociada a uno
de los operandos, pues el tipo de la variable en cuestin pasa a ser Registro entero o Registro
flotante, una vez realizada la operacin. La Carga tendr lugar sobre un registro entero si
ambas variables (x e y) pertenecen a uno de los cuatro primeros tipos de la Tabla 6.3, y so-
bre un registro flotante (double) en caso contrario. Una vez realizada esta operacin, se
vuelve a aplicar la misma tabla sobre la nueva combinacin de plataformas, lo que nos lle-
var automticamente a una casilla diferente.
La operacin Intercambio consiste, como indica su nombre, en intercambiar las dos
plataformas: la del operando izquierdo pasar a ser la del derecho, y viceversa. Este inter-
cambio no afecta al resultado de la operacin, pues la suma es conmutativa. Despus de
realizada esta operacin, es preciso volver a aplicar la Tabla 6.3, lo que nos llevar a la
casilla simtrica de la anterior respecto a la diagonal principal de la tabla.
En la operacin MOV T,x, que aparece en dos casillas de la Tabla 6.3, el nombre T se apli-
ca a una posicin de la memoria del programa objeto que el compilador utilizar como me-
moria auxiliar. Esta operacin tambin modifica la plataforma, pues la correspondiente al
operando izquierdo (x, cuyo tipo era registro entero) pasa a apuntar a la variable T, cuyo
tipo es variable de tipo int contenida en la memoria.
Captulo 6. Generacin de cdigo 251
Tipo del
Tipo del operando derecho y
operando
unsigned int Registro Constante double Registro
izquierdo x
char entero entera double
unsigned Carga x Intercambio Intercambio Carga x Carga x Carga x
char Repite suma Repite suma Repite suma Repite suma Repite suma Repite suma
int Carga y Carga y ADD y,x Carga y Carga x FIADD x
Repite suma Repite suma Repite suma Repite suma
Registro Carga ADD x,y ADD x,y ADD x,y MOV T,x MOV T,x
entero Repite suma Repite suma Repite suma
Constante Intercambio Intercambio Intercambio Carga x FADD x
entera Repite suma Repite suma Repite suma Repite suma
double Carga y Carga y Intercambio Intercambio Carga y FADD x
Repite suma Repite suma Repite suma Repite suma Repite suma
Registro Intercambio Intercambio Intercambio Intercambio Intercambio FADD y
double Repite suma Repite suma Repite suma Repite suma Repite suma
Tabla 6.3. Generacin de cdigo ensamblador para la operacin suma.
06-CAPITULO 06 9/2/06 11:51 Pgina 251
La programacin de la tabla de la suma se podra hacer como se indica en el seudocdigo si-
guiente:
Label Tabla[n][n] = {LCX, LXG, LXG, LCX, LCX, LCX}
{LCY, LCY, L1, LCY, LCX, L2}
{LCY, L3, L3, L3, L4, L4}
{LXG, LXG, LXG, 0, LCX, L5}
{LCY, LCY, LXG, LXG, LCY, L5}
{LXG, LXG, LXG, LXG, LXG, L6}
L: GOTO Tabla[tipox][tipoy];
LCX: CARGA X;
GOTO L;
LCY: CARGA Y;
GOTO L;
LXG: Intercambio (X,Y);
GOTO L;
L1: GEN (ADD, Y, X);
return;
L2: GEN (FIADD, Y, NULL);
return;
L3: GEN (ADD, X, Y);
return;
L4: GEN (MOV, T, X);
GOTO L;
L5: GEN (FADD, X, NULL);
return;
L6: GEN (FADD, Y, NULL);
return;
En las operaciones no conmutativas (como la resta) se construye una tabla semejante a la 6.3,
pero al no poder intercambiar los operandos, el nmero de casillas de la tabla que generan cdi-
go aumenta.
En las operaciones mondicas o unarias (con un solo argumento, normalmente situado a la de-
recha del operador en casi todos los lenguajes de programacin) la tabla se reduce normalmente
a una tabla de entrada simple. La Tabla 6.4 muestra, como ejemplo, la que correspondera a la
operacin cambio de signo ( mondico), que corresponde a la regla <exp> ::= - <exp>.
252 Compiladores e intrpretes: teora y prctica
Tipo del operando derecho y
unsigned int Registro Constante double Registro
char entero entera double
Carga y Carga y NEG y Carga y FCHS
Repite Repite Repite
Tabla 6.4. Generacin de cdigo ensamblador para la operacin de cambio de signo.
06-CAPITULO 06 9/2/06 11:51 Pgina 252
Para una expresin de comparacin correspondiente a las reglas de la gramtica
<compare> ::= <exp> = <exp>
| <exp> != <exp>
| <exp> > <exp>
| <exp> >= <exp>
| <exp> < <exp>
| <exp> <= <exp>
es posible construir tambin una tabla que ser muy semejante a la de la operacin resta, sustitu-
yendo las instrucciones SUB y FSUB por las instrucciones de comparacin CMP y FCMP.
En cuanto a las reglas
<compare> ::= <compare> + <compare>
| <compare> * <compare>
| <compare>
se ha optado en esta gramtica por representar las operaciones OR y AND mediante los mismos
smbolos + y * que se utilizan para la suma de nmeros. Esto es posible, porque se ha dicho an-
teriormente que esta gramtica no admite expresiones que mezclen datos Booleanos y numri-
cos, por lo que esta sintaxis no sera ambigua. En otros lenguajes (como C o APL) se utilizan
smbolos diferentes para estos operadores, pues los datos pueden mezclarse, dado que el tipo
booleano se reduce, en realidad, a una forma ms de dato numrico.
6.1.3. Punteros
Como se ha explicado en la Seccin 5.4, en muchos lenguajes existe un tipo especial de
variables, llamadas punteros, que permiten acceder a la informacin contenida en las varia-
bles a las que apuntan por medio de reglas sintcticas ms o menos parecidas a las
siguientes:
<dereference> ::= deref <id>
| deref <dereference>
En el lenguaje C, por ejemplo, la unidad sintctica que aqu hemos representado con el sm-
bolo terminal deref es un asterisco.
Cuando hay que desreferenciar un puntero, el cdigo generado podra ser el siguiente:
mov ebx,id
Esta instruccin introduce en el registro de indexacin ebx el contenido del puntero, es de-
cir, la direccin de memoria de la variable a la que ste apunta. A continuacin se genera una pla-
taforma, en la que la direccin del operando correspondiente es [ebx].
Captulo 6. Generacin de cdigo 253
06-CAPITULO 06 9/2/06 11:51 Pgina 253
6.1.4. Asignacin
La forma tpica de las reglas sintcticas que regulan la asignacin de un valor a una variable es
la siguiente:
<asignacion> ::= <id> := <exp>
| <dereference> := <exp>
La primera regla corresponde a la asignacin de valor a una variable ordinaria; la segunda, a
la asignacin de valor (una direccin) a un puntero. Dependiendo del lenguaje de que se trate,
pueden exigirse condiciones semnticas especiales a las variables afectadas por una asignacin a
un puntero.
Es posible (y conveniente) construir para la asignacin una tabla parecida a la de la suma
(vase la Tabla 6.3), que especifique el cdigo que hay que generar en cada una de las combina-
ciones posibles de los tipos del identificador situado a la izquierda del smbolo de asignacin y
de la expresin situada a la derecha. Esto significa que la asignacin puede considerarse como un
operador didico ms, semejante a los operadores aritmticos (suma, resta, etc.), con la nica sal-
vedad de que el operando izquierdo se pasa por referencia, y no por valor, como ocurre con el de-
recho, y con ambos operandos en las operaciones aritmticas. Por consiguiente, entre los tipos
que puede adoptar el operando izquierdo hay que incluir el tipo puntero, lo que significa aadir
una lnea ms a la tabla.
6.1.5. Entrada y salida de datos
Las instrucciones de entrada y salida de datos desde memorias o dispositivos externos al progra-
ma ejecutable (como el teclado, la pantalla, archivos en disco, etc.) varan mucho con el lengua-
je fuente, por lo que no vamos a considerarlas aqu. Baste decir respecto a ellas que normalmente
se llevan a cabo mediante llamadas a subrutinas de biblioteca, con lo que el generador de cdigo
deber generar, usualmente, una instruccin CALL.
6.1.6. Instrucciones condicionales
Una instruccin condicional tpica que slo tenga parte then podra tener una regla parecida a
la siguiente:
<condicional> ::= if <exp> then (1) <instruccion> (2)
Como se vio en la Seccin 6.1.2, el anlisis del smbolo no terminal <exp> (que en este tipo
de instrucciones se reducir a una comparacin) habr generado el cdigo apropiado para que los
indicadores sealen adecuadamente el resultado de la operacin. Hay dos maneras de generar el
cdigo correspondiente a la instruccin if:
Que el cdigo generado por la comparacin almacene en los indicadores el valor true o
false, segn corresponda, encendiendo (set) o apagando (reset) uno de los indicadores
254 Compiladores e intrpretes: teora y prctica
06-CAPITULO 06 9/2/06 11:51 Pgina 254
(por ejemplo, el de resultado cero). En tal caso, la instruccin condicional slo tendra que
generar la siguiente instruccin en la accin semntica (1) situada en la regla justo a conti-
nuacin de la unidad sintctica then:
jz fin_then#
donde fin_then# es una etiqueta interna generada por el compilador (diferente para cada
instruccin condicional, por supuesto). Por otra parte, la accin semntica (2), situada al fi-
nal de la regla, generara el siguiente cdigo:
fin_then#:
Es decir, colocara la etiqueta despus del cdigo generado por el smbolo no terminal
<instruccion>.
Que el cdigo generado por la comparacin se limite a la instruccin de comparacin, y que
se aada a la informacin semntica asociada al resultado el tipo de operador de compara-
cin que acaba de analizarse. La Tabla 6.5 indica el cdigo que habra que generar en fun-
cin de dicho operador. Esto correspondera a la accin semntica (1). La accin (2) sera
idntica al caso anterior. Este procedimiento genera cdigo algo ms optimizado que el
otro.
Captulo 6. Generacin de cdigo 255
Operacin Cdigo generado
= je fin_then
!= jne fin_then
< jl fin_then
<= jle fin_then
> jg fin_then
>= jge fin_then
Tabla 6.5. Generacin de cdigo condicional asociado a instrucciones de comparacin.
La Figura 6.1 proporciona un esquema grfico de la generacin de cdigo para la instruccin
condicional. En dicha figura y las sucesivas, los puntos de la regla donde aparece un nmero en-
cerrado en un crculo indican acciones semnticas.
La Figura 6.2 muestra el cdigo que habra que generar para la regla
<condicional> ::= if <exp> then (1) <instruccion1> (2)
else <instruccion2> (3)
La primera accin semntica genera un cdigo exactamente igual al generado por la primera
accin semntica de la Figura 6.1. La segunda genera una instruccin de salto incondicional
06-CAPITULO 06 9/2/06 11:51 Pgina 255
(jmp) a la etiqueta fin_ifelse# para evitar que se ejecuten las instrucciones de la parte else
tras ejecutar las instrucciones de la parte then. Adems se genera una lnea que define la eti-
queta fin_then#. Por ltimo, la tercera accin semntica genera nicamente una lnea que
contiene la etiqueta fin_ifelse#.
6.1.7. Bucles
La Figura 6.3 muestra el cdigo que habra que generar para la regla
<bucle> ::= while (1) <exp> do (2) <instruccion> end (3)
256 Compiladores e intrpretes: teora y prctica
<condicional> ::= if <exp> then <instruccion>
;
; <exp>
;
PILA
;
;
;
jz fin_then#
<instruccion>
fin_then#:
exp
1 2
Figura 6.1. Generacin de cdigo para la instruccin if-then.
<condicional> ::= if <exp> then <instruccion1>
else <instruccion2> 2 1
3
;
; <exp>
;
PILA
;
;
;
jz fin_then#
<instruccion1>
fin_ifelse#:
exp
jmp fin_ifelse#
fin_then#
;
;
;
<instruccion2>
Figura 6.2. Generacin de cdigo para la instruccin if-then-else.
06-CAPITULO 06 9/2/06 11:51 Pgina 256
La primera accin semntica genera nicamente una lnea que contiene la etiqueta
inicio_while#. La segunda accin semntica genera un cdigo exactamente igual al gene-
rado por la primera accin semntica de las Figuras 6.1 y 6.2. El efecto de este cdigo es salir del
bucle si el valor de la expresin es igual a 0, es decir, si la expresin es falsa. La tercera accin
semntica genera una instruccin de salto incondicional (jmp) a la etiqueta inicio_while#,
para continuar con la siguiente iteracin del bucle. Adems genera una lnea que contiene la eti-
queta fin_while#.
La Figura 6.4 muestra el cdigo que habra que generar para la regla
<bucle> ::= repeat (1) <instruccion> until <exp> (2)
Captulo 6. Generacin de cdigo 257
<bucle> ::= while
1
n
t
i
c
o
Figura 8.1. Estructura de un intrprete.
08-CAPITULO 08 9/2/06 11:58 Pgina 323
Durante el anlisis sintctico, la informacin semntica asociada a los operandos de las ex-
presiones puede generarse sobre plataformas de operandos, es decir, vectores de estructuras
que contienen toda la informacin asociada al operando izquierdo, el operando derecho y el
resultado de la operacin. Esta informacin se pone al da cada vez que el intrprete realiza
una operacin, lo que permite obtener mejoras de eficiencia significativas. Las plataformas de
operandos estn relacionadas con la pila semntica, pues la informacin que contienen puede
tener que almacenarse en dicha pila para utilizarla posteriormente, como ocurrira, por ejem-
plo, cuando el analizador detecte que se ha abierto un parntesis durante la ejecucin de una
instruccin.
8.4.2. Distintos tipos de tabla de smbolos
en un intrprete
Aunque la gestin de memoria en los intrpretes se ver con detalle en el Captulo 10, aqu se va
a mencionar la parte de dicha gestin que afecta a la estructura de las tablas de smbolos, puesto
que stas constituyen una parte independiente de los procesadores de lenguaje.
Algunos intrpretes utilizan una tabla de smbolos de tamao fijo, cuyos elementos contienen
punteros a la memoria asignada a las variables. En estos casos, la tabla de smbolos tendr
normalmente estructura de array. Esto es especialmente til cuando las variables pueden te-
ner valores que ocupan mucho espacio, como ocurre con vectores, matrices y otras estructu-
ras de datos. Por otra parte, si el valor de una variable es pequeo (como ocurre cuando se
trata de un nmero entero), a veces puede introducirse dicho valor en la propia tabla de sm-
bolos para mejorar la eficiencia del acceso al valor a travs del nombre de la variable.
Otras veces, los intrpretes disponen de tablas de smbolos cuyo tamao puede modificar-
se de forma dinmica, y la tabla de smbolos podra estructurarse como una lista encade-
nada. En estos casos tambin pueden utilizarse los tipos de tabla hash descritos en el
Captulo 2, con los algoritmos adaptados para tener en cuenta la estructura dinmica de las
tablas.
En algunos intrpretes, la tabla de smbolos no apunta directamente a la memoria asignada
a las variables, sino que lo hace a travs de una tabla intermedia de referencias, que lleva la
cuenta del nmero de punteros que apuntan en un momento dado al objeto de que se trate.
Esto simplifica la recoleccin de desechos y la gestin de la memoria, a costa de aumentar
el tiempo de acceso a los valores de las variables, pues hay que atravesar un direcciona-
miento indirecto ms. En cambio, la memoria ocupada por los programas durante su eje-
cucin puede ser significativamente menor, pues una instruccin de asignacin tal como
x:=y no supondra trasvase alguno de datos (vase la Figura 8.2): bastara apuntar desde
el elemento de la tabla de smbolos correspondiente a la variable x a la misma referencia a
la que apunta la variable y, incrementando al mismo tiempo el nmero de referencias que
apuntan a dicho elemento. De este modo, si el valor de la variable x cambiase posterior-
mente, el de la variable y no se perder, pues dicho valor no se declarar basura hasta que
el nmero de referencias que le apuntan se reduzca a cero. Naturalmente, si se modificase
parcialmente el valor de la variable x, por ejemplo, con la instruccin x[3]:=5, sera pre-
324 Compiladores e intrpretes: teora y prctica
08-CAPITULO 08 9/2/06 11:58 Pgina 324
ciso obtener una referencia independiente para esta variable, copiar su valor a una zona nue-
va de memoria, y slo entonces modificar el valor del elemento especificado (vase la
Figura 8.3).
Captulo 8. Intrpretes 325
Clave Valor
y
x
2
Valor de x e y
NumRef Valor
Figura 8.2. Uso de la tabla de referencias para minimizar el uso de memoria
en las asignaciones.
Clave Valor
y
x
1
Valor de y
NumRef Valor
1
Valor de x
Figura 8.3. La tabla de referencias despus de la ejecucin
de la instruccin x[3]:=5.
08-CAPITULO 08 9/2/06 11:58 Pgina 325
Resumen
Este captulo revisa las principales diferencias entre los compiladores y los intrpretes. Ambos ti-
pos de procesadores de lenguaje tienen una gran parte en comn, pero se diferencian esencial-
mente en la generacin de cdigo, que en los intrpretes es sustituida por una fase de ejecucin
directa del cdigo fuente interpretado.
Se analizan los principales lenguajes que usualmente exigen la utilizacin de un intrprete y
las razones por las que conviene disearlos as. Se comparan los compiladores y los intrpretes
desde el punto de vista de las ventajas y los inconvenientes del uso de unos y otros. Finalmente,
se estudia con ms detalle la estructura interna de un intrprete, con especial nfasis en la ejecu-
cin de cdigo y en las diferencias que se pueden detectar en el uso de la tabla de smbolos.
Bibliografa
[1] Papert, S. (1980): Mindstorms: children, computers and powerful ideas, The Harvester Press, ISBN:
0855271639.
8.6
8.5
326 Compiladores e intrpretes: teora y prctica
08-CAPITULO 08 9/2/06 11:58 Pgina 326
Captulo 9
Tratamiento de errores
Para un compilador o un intrprete, la deteccin de errores es indispensable, pues todos los
programas errneos deben ser rechazados automticamente. Un compilador dispondra de un
procesador de errores perfecto si realiza correctamente las tres acciones siguientes:
Detectar todos los errores que contiene el programa que se est analizando.
No generar ningn mensaje que seale errores donde no los hay.
No generar mensajes de error innecesarios.
Deteccin de todos los errores verdaderos
El primer objetivo es casi imposible de conseguir para un compilador, pero no para un intrpre-
te. Esto se debe a que un programa contiene ordinariamente errores de varios tipos:
Errores morfolgicos, como identificadores mal formados, constantes mal construidas,
smbolos no pertenecientes al lenguaje, comentarios incorrectos, etc.
Errores sintcticos, es decir, cadenas de unidades sintcticas que no se adaptan a la sintaxis
del lenguaje fuente.
Errores semnticos, como operaciones realizadas sobre tipos incompatibles. Entre stos se
incluyen los errores relacionados con el uso de la tabla de smbolos, como uso de identi-
ficadores no declarados o declaracin doble de un identificador en la misma regin de
alcance.
Errores detectables nicamente en tiempo de ejecucin, como punteros con valor nulo o
cuyo valor se sale de los lmites permitidos, o indexacin de vectores con ndices inapro-
piados.
9.1
09-CAPITULO 09 9/2/06 11:53 Pgina 327
Los tres primeros tipos de error deberan ser localizados por cualquier compilador. Por esta
razn, se llaman errores de compilacin, en oposicin a los del ltimo tipo (los errores de
ejecucin). Sin embargo, no todos los compiladores son capaces de detectar a la primera todos
los errores de compilacin. Considrese, por ejemplo, el siguiente programa escrito en el len-
guaje C:
void main () {
int i,j,k;
i=0; /* Asigno 0 a i //
j=;
=k;
}
Este programa contiene tres errores: un comentario mal escrito (no est bien sealado el pun-
to donde termina) y las dos instrucciones siguientes, cuya sintaxis es incorrecta. Pues bien, al no
detectar el final del comentario en la primera lnea errnea, algunos compiladores supondrn que
las tres lneas siguientes forman parte del comentario, no realizarn su anlisis sintctico y no de-
tectarn los errores que contienen. En cambio, adems del primer error correcto (comentario
mal terminado) seguramente se sealar un error inexistente (la funcin main no
termina correctamente), que indica que el compilador echa de menos la presencia de la
llave final de bloque (}), que sin embargo s est presente.
En un caso como el que nos ocupa, el programador corregir el primer error sealado (ce-
rrando bien el comentario, con los smbolos */), pero no habr recibido informacin que le
permita detectar los dos errores sintcticos (pinsese que puede haber decenas de instruccio-
nes entre el primer error y el segundo). En cambio, el segundo mensaje recibido le parecer ab-
surdo, pues puede ver a simple vista que la llave que seala el final de la funcin main s est
presente. En consecuencia, despus de corregir el primer error, volver a compilar el programa
fuente, y slo entonces el compilador sealar los dos errores pendientes, al mismo tiempo que
desaparece el error espurio referente a la llave final. En definitiva, ms pronto o ms tarde se
detectan todos los errores, pero a costa de perder el tiempo, teniendo que realizar varias com-
pilaciones innecesarias.
Un compilador con un procesador de errores ms perfecto podra detectar que el comentario
mal terminado se debe al cambio del carcter * por el carcter /, dara por terminado el comen-
tario al final de la lnea y continuara el anlisis sintctico a partir de ah, detectando las dos
instrucciones errneas y eliminando el mensaje de error espurio.
Por otra parte, los errores en tiempo de ejecucin suelen estar siempre fuera del alcance de
un compilador, que no tiene control alguno sobre la ejecucin de los programas que ha gene-
rado. Para un intrprete, en cambio, no existe ningn problema, pues mantiene control total so-
bre la ejecucin del programa objeto, que se lleva a cabo como parte del propio intrprete
(vase el Captulo 8). Por ejemplo, en el momento de indexar un vector de datos con una
variable, el intrprete puede comprobar si el valor de la variable se encuentra dentro del margen
permitido por el tamao del vector, pues este dato le es accesible a travs de la tabla de
smbolos.
328 Compiladores e intrpretes: teora y prctica
09-CAPITULO 09 9/2/06 11:53 Pgina 328
Deteccin incorrecta de errores falsos
El segundo objetivo puede parecer tan obvio que resulte innecesario mencionarlo. Sin embargo,
a lo largo de la historia de la Informtica, ms de un compilador comercial de prestigio no ha
cumplido este requisito. Esto ocurre cuando la presencia de un error desequilibra al compilador
hasta tal punto que a partir de entonces detecta errores en instrucciones que no los tienen. A
veces, el efecto se propaga hasta el final del anlisis, con lo que el compilador genera una serie
de mensajes de error en cadena, que afectan a todas las instrucciones a partir de la que provoc
el desequilibrio. Se ha visto un ejemplo sencillo en la seccin anterior, donde la deteccin de un
error provoc la aparicin de otro espurio (sealando que la funcin main no terminaba con una
llave }). Veamos otro ejemplo, an ms espectacular. Considrese el siguiente programa correc-
to en lenguaje C o C++:
void main () { // Lnea 1
int i, j; // Lnea 2
i=1; // Lnea 3
while (i) { // Lnea 4
int j; // Lnea 5
} // Lnea 10
j=2; // Lnea 11
if (i<j) j++; // Lnea 12
} // Lnea 65
Supngase que el programador olvid escribir la llave situada en la lnea 4, que abre el blo-
que while. En tal caso, es muy probable que el compilador genere los siguientes mensajes de
error:
Lnea 5: doble definicin del identificador j.
Lnea 11: instruccin situada fuera de una funcin.
Lnea 12: instruccin situada fuera de una funcin.
...
Lnea 65: instruccin situada fuera de una funcin.
Al faltar la llave inicial del bloque while, el compilador interpreta que las instrucciones 5 a 9
pertenecen al bloque principal de la funcin main, por lo que la declaracin de la variable j en
la lnea 6 sera incorrecta (ya haba sido declarada en dicho bloque en la lnea 2). Adems, la lla-
ve final de bloque de la lnea 10 ser interpretada como el cierre de la funcin main, con lo que
todas las instrucciones sucesivas (de la 11 a la 65) sern marcadas como errneas, por encontrarse
fuera de un bloque. Cualquier error adicional situado en alguna de estas instrucciones no sera
detectado. El compilador habr generado as 56 mensajes de error, de los que slo el primero da
alguna informacin al programador, despus de pensar un poco, permitindole detectar la ausen-
9.2
Captulo 9. Tratamiento de errores 329
09-CAPITULO 09 9/2/06 11:53 Pgina 329
cia de la llave en la lnea 4. Una vez corregido este error, en una nueva compilacin habrn des-
aparecido los 55 mensajes espurios, detectndose entonces otros posibles errores situados en las
lneas posteriores.
Cuando un compilador es capaz de evitar estos errores en cadena, se dice que posee un pro-
cesador de errores con buena capacidad de recuperacin. Es muy difcil que un compilador se re-
cupere del problema mencionado en el ejemplo. Una forma de conseguirlo sera la siguiente: al
detectar que se estn generando demasiados errores, el compilador podra intentar introducir
alguna llave adicional en algn punto que parezca razonable (quiz haciendo uso de la informa-
cin proporcionada por el indentado de las instrucciones del programa fuente) hasta conseguir
que los errores espurios desaparezcan.
Generacin de mensajes de error innecesarios
Un mensaje de error es innecesario si ya ha sido sealado previamente (en la misma o en otra ins-
truccin) o si no aade informacin nueva a la proporcionada por mensajes de error anteriores.
La razn de que se desee prescindir de los mensajes innecesarios es parecida a la de la
eliminacin de los mensajes espurios: facilitar la labor del programador, evitando que tenga que
localizar los mensajes de error buenos en medio de un frrago inabordable de mensajes, que pro-
porcionan informacin nula (los mensajes repetidos) o contraproducente (los mensajes espurios).
A continuacin se presenta un anlisis por separado de los dos casos mencionados:
Evitar que un solo error d lugar a la aparicin de ms de un mensaje. Por ejemplo, sea la
expresin a[i1;i2;i3], donde a no ha sido declarado como array. Al abrir el corche-
te, el compilador sealar el siguiente error:
a no es un array y no se le puede indexar
Al cerrar el corchete, un compilador con un procesador de errores poco sofisticado podra
sealar otro error:
El nmero de ndices no coincide con el rango de a.
Una vez sealado el primero, el segundo es innecesario. Para solucionar este problema,
una vez detectado el primer error, podra sustituirse la referencia a la variable a en la ins-
truccin que se est analizando por una referencia a un identificador interno, generado
automticamente por el compilador e introducido en la tabla de smbolos. La rutina de
tratamiento de errores podra interceptar, a partir de entonces, todos los mensajes de error
que hagan referencia al identificador interno.
Evitar que un error idntico repetido d lugar a varios mensajes. Por ejemplo, considrese
el siguiente programa correcto en lenguaje C/C++:
void main () { // Lnea 1
int i; // Lnea 2
i=1; // Lnea 3
9.3
330 Compiladores e intrpretes: teora y prctica
09-CAPITULO 09 9/2/06 11:53 Pgina 330
while (i) { // Lnea 4
int j; // Lnea 5
if (i<j) { // Lnea 10
} // Lnea 20
for (j=0; j<n; j++) { // Lnea 21
a=j-i+1; // Lnea 22
b=2*a+j; // Lnea 23
} // Lnea 24
...
} // Lnea 65
Supngase que el programador olvida escribir la llave de la lnea 10. En tal caso, la llave
de la lnea 20 cerrara el bloque que se abri en la lnea 4, con lo que se dara por termina-
do el alcance de la variable j. Pero dicha variable se utiliza cinco veces en las lneas 21, 22
y 23. Por lo tanto, un compilador con un procesador de errores poco sofisticado generara
cinco mensajes de error idnticos en dichas lneas:
La variable j no ha sido declarada
Esta situacin puede solucionarse de varias maneras:
Cuando se detecta que el identificador j no ha sido declarado, se puede introducir en la
tabla de smbolos un identificador con ese nombre y los atributos que se pueda deducir
que tiene, con el objeto de que los siguientes mensajes identificador no decla-
rado que podran generarse no lleguen a ser detectados. Esta solucin tiene el peligro
de que un error real subsiguiente no llegue a ser detectado, como consecuencia de una
asignacin errnea de atributos a la variable fantasma.
Cuando se detectan mensajes de este tipo, se puede aplazar su generacin hasta que se
est seguro de que ya no pueden detectarse ms casos, generando entonces un solo men-
saje como el siguiente:
La variable j no ha sido declarada en las lneas 21, 22 y 23
Correccin automtica de errores
En un compilador, un procesador de errores ptimo sera capaz no slo de detectar los errores,
sino tambin de corregirlos, con lo que el programa ejecutable podra generarse ya en la prime-
ra pasada de compilacin, lo que ahorrara mucho tiempo. Sin embargo, la correccin automti-
ca presenta el peligro de introducir errores difciles de detectar, si las suposiciones realizadas por
el compilador para corregir los errores fuesen incorrectas, por lo que los compiladores comer-
ciales no suelen realizar correccin automtica de errores, excepto algunos de propsito muy es-
pecfico, que usualmente generan directamente programas ejecutables (.EXE), en los que este
9.4
Captulo 9. Tratamiento de errores 331
09-CAPITULO 09 9/2/06 11:53 Pgina 331
procedimiento puede ahorrar tiempo de desarrollo, pues la mayor parte de los errores de compi-
lacin habrn sido correctamente corregidos, y los que no lo hayan sido podrn detectarse, con
un depurador conveniente, a la vez que se buscan los errores de ejecucin. En todo caso, las de-
cisiones que tome el compilador para corregir los errores detectados deben estar bien documen-
tadas, para que el programador pueda comprobarlas a posteriori y no se pierda tratando de
depurar un programa ejecutable que no corresponde exactamente a lo que haba programado.
Existen varios tipos de correcciones automticas:
Correccin ortogrfica. Corresponde al analizador morfolgico y a la tabla de smbolos,
aunque el analizador sintctico y el semntico tambin pueden desempear algn papel. Su
funcionamiento es muy similar al de los correctores ortogrficos de los procesadores de tex-
tos: cuando se detecta un identificador no declarado, el error podra ser simplemente ortogr-
fico. Se trata de considerar los errores ms tpicos que podran haberse producido y detectar
el identificador correcto, utilizando como trmino de comparacin la lista de palabras reser-
vadas del lenguaje y los identificadores correctamente definidos. Una forma de acelerar el
proceso consistira en comprobar nicamente los errores ortogrficos tpicos, que son:
Cambio de un carcter por otro (usualmente situado en el teclado cerca del carcter
correcto).
Eliminacin de un carcter.
Introduccin de un carcter espurio.
Intercambio de la posicin de dos caracteres consecutivos.
Un identificador declarado, pero no utilizado, puede ser candidato para una correccin
ortogrfica. Para detectar estos casos, podra ser til aadir en la tabla de smbolos, entre la
informacin asociada a cada identificador, un contador de usos y referencias. Sin embargo,
debe recordarse que este caso tambin puede darse cuando un identificador utilizado en
versiones anteriores del programa ha dejado de usarse, sin que el programador se acordase
de eliminar su declaracin.
El analizador sintctico puede ayudar en este proceso. Por ejemplo, si se espera en este
punto una palabra reservada, y lo que aparece es un identificador no declarado, se puede
buscar la palabra reservada ms prxima al identificador errneo, teniendo en cuenta los
errores ortogrficos tpicos mencionados.
El analizador sintctico puede ser tambin til para detectar los errores de concatenacin
(en realidad un caso particular de la eliminacin de un carcter, cuando ste es el espacio
en blanco). Por ejemplo, cuando la cadena de entrada contiene begina y lo que se espera
encontrar es begin a.
En cuanto al analizador semntico, tambin puede ayudar en estas correcciones. Por ejem-
plo, si aparece un identificador declarado correctamente en un contexto incompatible con
su tipo, el error podra consistir en que se ha utilizado un identificador parecido en lugar del
deseado, que s tiene tipo compatible.
Correccin sintctica. Cuando se detecta un error al analizar la cadena xUy, donde
x,y
*
y U es el prximo smbolo (terminal o no terminal) que se va a analizar, se
puede intentar una de las dos acciones siguientes:
332 Compiladores e intrpretes: teora y prctica
09-CAPITULO 09 9/2/06 11:53 Pgina 332
Borrar U e intentar de nuevo el anlisis. Esta opcin tiene en cuenta la posibilidad de que
se haya introducido en el programa una unidad sintctica espuria, que conviene eliminar.
Ejemplo: sea la instruccin
else x=0;
donde no existe ninguna instruccin if previa pendiente. El analizador sintctico de-
tectara un error al tratar de analizar la palabra reservada else. Una posible accin po-
dra ser eliminar dicha unidad sintctica y tratar esta instruccin como si se tratase de:
x=0;
Hay que insistir de nuevo en la necesidad de que todas estas correcciones automticas
del compilador estn muy bien documentadas.
Insertar una cadena de smbolos terminales z entre x y U y continuar el anlisis a partir
de z.
Ejemplo: sea la instruccin
if (i<j) {x=1; y=2; else x=0;
ste parece un caso muy semejante al ejemplo anterior, en el que tambin se podra
intentar eliminar la palabra reservada else. Sin embargo, obsrvese que es ms
probable que el error, en esta instruccin if-then-else, consista en la falta de una
llave que cierre el bloque de instrucciones correspondiente a la parte then. En este
caso, el analizador sintctico, al detectar un error en la unidad sintctica else, hara
mejor en tratar de insertar la llave } delante de dicha unidad sintctica y volver a
intentar el anlisis, que probablemente resultara correcto.
Una tercera opcin posible (borrar smbolos del final de x) no es aconsejable, pues su-
pondra echar marcha atrs en el anlisis y deshara la informacin semntica asociada.
Recuperacin de errores en un intrprete
En el caso de un intrprete, como se mencion en el Captulo 8, el tratamiento de errores es un
poco diferente al de un compilador.
En primer lugar, una cosa es la ejecucin de un programa en condiciones normales, bajo con-
trol total del intrprete, y otra el programa ejecutable generado por algunos intrpretes, que empa-
queta el propio intrprete junto con un programa fuente, o bien con dicho programa precompilado
y traducido a un formato intermedio. En el segundo caso se supone que, antes de generar el pro-
grama empaquetado, la aplicacin ya habr sido depurada previamente, bajo control total del in-
trprete y en la forma que vamos a ver a continuacin, por lo que no es probable que d lugar a
nuevos errores, aunque ya se sabe que toda aplicacin informtica grande contiene errores ocultos.
Sin embargo, y dado que es imposible asegurar que una aplicacin informtica est totalmen-
te libre de errores (tal demostracin es un problema NP-completo), el programa empaquetado de-
9.5
Captulo 9. Tratamiento de errores 333
09-CAPITULO 09 9/2/06 11:53 Pgina 333
bera tener previsto algn tipo de actuacin en el caso de que se detecte un error. Normalmente,
lo mejor es generar un mensaje de error apropiado y dar por terminada la ejecucin del progra-
ma, que en todo caso terminara de una forma ms ordenada y elegante que lo que suele ocurrir
cuando un programa generado por un compilador topa con un error de ejecucin que no ha sido
previsto y detectado por el programador. Esto significa que el intrprete que se empaqueta con el
programa para generar una aplicacin independiente llevar usualmente un procesador de errores
ms simple y restringido que el que corresponde al intrprete completo.
Si un intrprete detecta un error durante el anlisis y ejecucin de un programa fuente bajo el
control total del intrprete (recurdese que ambas fases estn entrelazadas, pues cada instruccin
se analiza y se ejecuta sucesivamente), lo mejor es detener la ejecucin, sealar el error detectado
con un mensaje apropiado, y permitir al programador que tome alguna accin adecuada, eligien-
do entre las siguientes:
Revisar los valores de las variables.
Revisar el cdigo que se est ejecutando.
Modificar el cdigo para corregir el error sobre la marcha. En cuanto sea posible, hacerlo
sin afectar a la ejecucin interrumpida.
Reanudar la ejecucin, continuando con el programa corregido a partir del punto al que se
haba llegado. Tngase en cuenta que, si el cambio realizado es muy drstico (como una
reorganizacin total del programa), quiz no sea posible reanudar la ejecucin.
Reanudar la ejecucin en un punto diferente del programa que se ha interrumpido, situado
en la misma funcin o procedimiento que estaba activo en el momento de la deteccin del
error. Esto puede significar saltarse algunas lneas, reejecutar otras (echar marcha atrs) o
pasar a una zona totalmente diferente de la funcin o procedimiento.
Reanudar la ejecucin en un punto diferente del programa que se ha interrumpido, pero
abandonando la funcin o procedimiento donde se detect el error. Normalmente esto
significa que el intrprete tendr que manipular las pilas sintctica y semntica para asegu-
rar que el proceso se reanuda de una forma ordenada.
Abandonar totalmente la ejecucin para empezar de nuevo, al estilo de lo que se hace du-
rante la depuracin de un programa compilado, aunque en este caso no hace falta distinguir
entre las dos fases de compilacin y depuracin separadas, que corresponden al uso de un
compilador. Dicho de otro modo, no es preciso alternar entre dos aplicaciones informticas
diferentes (el compilador y el depurador), siendo posible volver a comenzar la ejecucin
desde el principio, sin que el intrprete tenga que ceder control a una aplicacin diferente.
Resumen
En este captulo se revisa el tema de la deteccin y correccin de errores, con especial nfasis en
los siguientes puntos:
Cmo detectar todos los errores de compilacin que contiene un programa.
9.6
334 Compiladores e intrpretes: teora y prctica
09-CAPITULO 09 9/2/06 11:53 Pgina 334
Cmo evitar que el procesador seale errores que no lo son.
Cmo evitar que la deteccin de un error provoque que el compilador genere una cascada
de errores espurios.
Cmo interceptar los mensajes de error innecesarios.
Debe el compilador corregir un error automticamente? En qu circunstancias?
Qu diferencias separan la deteccin de errores en un compilador y en un intrprete.
Captulo 9. Tratamiento de errores 335
09-CAPITULO 09 9/2/06 11:53 Pgina 335
09-CAPITULO 09 9/2/06 11:53 Pgina 336
Captulo 10
Gestin de memoria
El mdulo de gestin de la memoria es muy diferente en los compiladores y en los intrpretes:
en los primeros tiene por objeto gestionar la memoria del programa objeto generado por el com-
pilador, mientras que en los segundos debe gestionar su propia memoria, parte de la cul va a ser
asignada a las variables propias del programa objeto, que se ejecuta bajo control directo del in-
trprete.
Gestin de la memoria en un compilador
Al generar el programa objeto, un compilador debe tener en cuenta que ste est formado por dos
partes, claramente distintas en las computadoras que siguen el modelo de John von Neumann (ac-
tualmente, todos):
El cdigo ejecutable o instrucciones (que es generado, como indica su nombre, por el ge-
nerador de cdigo).
Los datos.
En los primeros microprocesadores de INTEL de los aos ochenta (8086 y 8088), la distin-
cin entre el cdigo ejecutable y los datos estaba muy clara: aquellas mquinas utilizaban direc-
cionamiento de 16 bits, que daba acceso nicamente a 64 kBytes de memoria, un tamao
demasiado pequeo para muchas aplicaciones informticas. Por ello, dichos procesadores utili-
zaban un procedimiento especial para acceder a memorias ms grandes, para lo cul su unidad
aritmtico-lgica vena provista de ciertos registros especiales (registros de segmentacin) que
permitan acceder a una memoria de 12 bits, es decir, un mximo de 1 MByte.
Los registros de segmentacin son cuatro: CS, DS, ES y SS. El primero (Code Segment) per-
mite acceder a una zona reservada para el cdigo ejecutable; los dos siguientes (Data Segment y
Extended data Segment), a dos zonas reservadas para datos; el cuarto (Stack Segment) a la pila de
almacenamiento temporal, utilizada, por ejemplo, en las llamadas a subrutinas. Una direccin
10.1
10-CAPITULO 10 9/2/06 11:54 Pgina 337
concreta de cualquiera de las cuatro zonas se obtiene multiplicando por 16 el valor del registro
de segmentacin correspondiente y sumando un desplazamiento de 16 bits (que en el caso del c-
digo ejecutable est guardado en el registro IP). As se consigue acceder a 16 65536 = 1 MByte
de memoria.
Los compiladores que generaban cdigo para mquinas INTEL de este tipo tenan que tener
en cuenta la existencia de los registros de segmentacin y permitan al usuario elegir entre varios
modelos de gestin de memoria completamente diferentes:
Modelo tiny (diminuto). En este modelo CS=DS=ES=SS, constante durante la ejecucin
del programa objeto. Es decir, tanto el cdigo, como los datos, como la pila, comparten una
sola zona de memoria de 64 kBytes.
Modelo small (pequeo). En este modelo DS=ES=SS y distinto de CS, ambos constantes
durante la ejecucin del programa objeto. Es decir, los datos y la pila comparten una zona
de 64 kBytes, pero el cdigo ejecutable se almacena en otra. El tamao mximo de un pro-
grama ms los datos que utiliza es, por tanto, igual a 128 kBytes.
Modelo medium (intermedio). En este modelo CS es constante durante la ejecucin del
programa, pero DS, ES y SS pueden variar a lo largo de la misma. Es decir, el cdigo eje-
cutable cabe en 64 kBytes, pero los datos pueden distribuirse entre varios segmentos, sin
rebasar el mximo terico total de 1 MByte. El generador de cdigo debe tener esto en
cuenta para asignar a los registros de segmentacin de datos los valores apropiados a lo lar-
go de la ejecucin.
Modelo compact (compacto). En este modelo DS=ES=SS, constantes durante la ejecu-
cin del programa, pero CS puede variar a lo largo de ella. Es decir, los datos caben en 64
kBytes, pero el cdigo ejecutable puede distribuirse entre varios segmentos, sin rebasar el
mximo terico total de 1 MByte. El generador de cdigo debe utilizar instrucciones con
cdigo de operacin diferente para llamar a una subrutina y volver al punto en que se la
llam, ya que la direccin de retorno debe recuperar no slo el valor del registro de des-
plazamiento IP, sino tambin el registro de segmentacin CS.
Modelo large (grande). En este modelo CS, DS, ES y SS pueden variar independiente-
mente a lo largo de la ejecucin del programa objeto. Es decir, la memoria total mxima de
1 MByte puede distribuirse arbitrariamente en zonas de datos y zonas de cdigo ejecutable.
Este modelo viene a ser una combinacin de los dos anteriores.
Modelo huge (gigantesco). En todos los modelos anteriores, una sola variable no puede
rebasar el tamao mximo de 64 kBytes. En el modelo huge este lmite puede rebasarse. Al
generar cdigo con este modelo para indexar esas variables, el compilador debe modificar
adecuadamente el valor del registro de segmentacin durante la propia operacin de inde-
xacin.
En los modelos medium, large y huge, la memoria de datos suele dividirse en dos zonas dife-
rentes, llamadas near heap (montn prximo, con un tamao de 64 kBytes) y far heap (montn
lejano, con el resto de la memoria disponible). Inicialmente, los compiladores hacen que los re-
gistros de segmentacin de los datos y la pila del programa objeto apunten al near heap, donde
se colocan la pila de llamadas de subrutina (normalmente al final de la regin) y las variables
338 Compiladores e intrpretes: teora y prctica
10-CAPITULO 10 9/2/06 11:54 Pgina 338
estticas del programa. El far heap se reserva para la memoria dinmica (vase ms adelante). La
mayor parte del tiempo, los registros de segmentacin de datos permanecen invariables. El com-
pilador slo tiene que cambiar su contenido cuando es preciso apuntar a la memoria dinmica, y
recupera el valor usual en cuanto deja de ser necesario hacerlo. Para guardar y recuperar dicho
valor, se utiliza la pila de llamadas a subrutina, mediante las instrucciones PUSH y POP.
Uno de los errores de ejecucin tpicos en los programas que usaban este modelo de memo-
ria se produce cuando la memoria asignada a la pila de llamadas de subrutina (que, a medida que
se requiere memoria, se extiende desde las direcciones ms altas del near heap hacia abajo) se
superpone con la memoria asignada a las variables estticas, destruyendo los valores de stas.
Esta situacin se llama rebosamiento de pila (stack overflow, en ingls) y, aunque los compila-
dores podan generar cdigo que comprobara la presencia de esta situacin, era posible inhabili-
tarlo mediante una opcin de compilacin que casi todos los programadores utilizaban para
ahorrar memoria y tiempo de ejecucin en los programas objetos generados.
A mediados de los aos ochenta, INTEL desarroll un nuevo procesador, 80286, que, aunque
mantena el funcionamiento anterior por compatibilidad con los programas desarrollados para
mquinas ms antiguas, posea un nuevo modo de funcionamiento que aumentaba la memoria
disponible a un direccionamiento de 24 bits, es decir, 16 MBytes.
A finales de los ochenta apareci un tercer procesador de INTEL, 80386, que mantena la
compatibilidad con los dos modos anteriores, pero aada un tercer modo de funcionamiento con
direcciones de 32 bits, lo que permita alcanzar memorias de 4 Gbytes. ste es el modo ms uti-
lizado hoy, pues ha seguido siendo aplicable a todos los procesadores posteriores de INTEL:
80486, Pentium, Pentium II, Pentium III y Pentium IV.
A continuacin se va a estudiar con ms detalle el tratamiento que debe dar un compilador a
los distintos tipos de variables de datos que suele haber en un programa objeto.
Variables estticas: En algunos lenguajes (como FORTRAN) todas las variables pertene-
cen a este grupo. En otros lenguajes (como C o C++) slo son estticas las variables de-
claradas como tales (con la palabra reservada static) o como externas (con la palabra
reservada extern), adems de las constantes que precisen que se les asigne memoria
(como las cadenas de caracteres y algunas de las constantes que aparecen como parmetro
en las llamadas a subrutinas). Adems, las variables que se han declarado fuera del alcan-
ce de una funcin son estticas, por omisin.
La gestin de las variables estticas por el compilador es relativamente fcil. Como se ha
indicado ms arriba, suelen colocarse en el near heap o zona de memoria equivalente, si-
tuada dentro del campo de accin ms usual de los registros de segmentacin de datos (DS
y ES). Usualmente, la direccin de memoria asignada a estas variables en el programa ob-
jeto forma parte de la informacin asociada a la variable en la tabla de smbolos, aunque,
si el compilador genera cdigo simblico o ensamblador, dicha informacin puede omitir-
se, sustituyndola por etiquetas, que el ensamblador se encargar de convertir en las direc-
ciones adecuadas.
La direccin asignada a una variable puede ser absoluta o relativa. Las direcciones relati-
vas se expresan mediante un offset (desplazamiento) respecto a una direccin base, que sue-
Captulo 10. Gestin de memoria 339
10-CAPITULO 10 9/2/06 11:54 Pgina 339
le coincidir con el principio de la zona donde se almacenan todas las variables estticas.
Dado que muchos programas deben ejecutarse aunque se instalen en direcciones de me-
moria diferentes (se dice entonces que son reubicables), en general es mejor trabajar con
direcciones relativas que con direcciones absolutas. Cuando se utilizan registros de seg-
mentacin de datos, todas las direcciones son, por definicin, relativas al contenido del
registro de segmentacin.
Otra informacin til asociada a las variables en la tabla de smbolos es su longitud, que es
funcin del tipo de la variable y del nmero de elementos que contiene si se trata de un
array (vector, matriz, etc.), o de su composicin, si se trata de una estructura (struct en
C, class o struct en C++, record en Pascal, etc.). La Tabla 10.1 muestra el tamao
usual de los distintos tipos atmicos que pueden tener las variables en el lenguaje C.
340 Compiladores e intrpretes: teora y prctica
Tipo de dato Tamao de cada elemento
Boolean 1 bit
char 1 Byte
short 2 Bytes
long 4 Bytes
int 2 o 4 Bytes
float 4 Bytes
double 8 Bytes
Tabla 10.1. Tamaos de algunos tipos atmicos de datos.
A veces, un compilador puede compactar las variables estticas para optimizar el uso de la
memoria. Sean, por ejemplo, las siguientes variables estticas en el lenguaje C:
const int meses[] = {31, 28, 31, 30, 31, 30,
31, 31, 30, 31, 30, 31};
const int base = 31;
El compilador podra asignarle a la variable base la misma direccin que a la variable
meses[0], pues ambas tienen el mismo valor. Lo mismo podra hacerse con cadenas
de caracteres: supongamos que a lo largo de un programa se utilizan las cadenas de
caracteres constantes abcd y d; para ahorrar memoria, el compilador podra asig-
nar a la segunda la direccin del cuarto elemento de la primera, como indica la
Figura 10.1.
Obsrvese que esto slo se puede hacer cuando la segunda cadena de caracteres es subca-
dena final de la primera, pues el smbolo que indica el final de la cadena en el lenguaje C
(\0) debe estar incluido en ambas cadenas. Tambin hay que tener mucho cuidado de
que este tipo de optimizaciones se haga nicamente cuando las dos variables que se super-
10-CAPITULO 10 9/2/06 11:54 Pgina 340
ponen sean constantes (es decir, que su valor no pueda cambiar durante la ejecucin del
programa objeto), ya que, en caso contrario, al cambiar una de ellas cambiara la otra, lo
que no sera correcto.
Variables automticas: Son las variables locales de las subrutinas o de los bloques de da-
tos, as como los parmetros que se pasan a las subrutinas (a menos que sean muy grandes,
en cuyo caso se les puede asignar memoria esttica, pasando su direccin como variable au-
tomtica). Usualmente, a estas variables se les asigna espacio en la pila de llamadas de su-
brutinas.
El conjunto de todas las variables automticas asociadas a una subrutina se llama activa-
tion record (bloque o registro de activacin) y a veces se maneja en bloque, reservando la
memoria asociada a todas las variables en una sola operacin, cuando se invoca la subruti-
na, y liberndola asimismo toda a la vez al terminar su ejecucin. Vase como ejemplo el
cdigo que podra generarse en la invocacin de una subrutina, tal como la expresin
rutina(a, b), donde a y b son variables enteras:
PUSH b
PUSH a
CALL rutina
ADD SP,4
Las dos primeras instrucciones introducen en la pila los dos argumentos de la subrutina (en
orden inverso, como exigen las reglas del lenguaje C). A continuacin se produce la llama-
da a la subrutina. La ltima instruccin libera de un golpe el espacio ocupado en la pila por
los dos argumentos, sin necesidad de ejecutar dos instrucciones POP.
Captulo 10. Gestin de memoria 341
Clave Valor
abcd
d
a b c d \0
Figura 10.1. Asignacin optimizada de memoria a dos cadenas de caracteres.
10-CAPITULO 10 9/2/06 11:54 Pgina 341
Inmediatamente despus de la ejecucin de la instruccin CALL rutina por el programa
objeto, el aspecto de la pila sera semejante al indicado por la Figura 10.2, donde se ha su-
puesto que la instruccin CALL guarda en la pila la direccin de retorno (la direccin de la
instruccin ADD SP,4) y que sta ocupa 4 Bytes (cada espacio pequeo sealado en la fi-
gura ocupa 2 Bytes).
Vase ahora el cdigo generado para la definicin de la subrutina, suponiendo que el cdi-
go fuente correspondiente fuese el siguiente:
int rutina (int a, char *b)
{
int i, j, k;
double r;
...
return k;
}
El cdigo generado por el compilador para esta subrutina podra ser:
rutina:
PUSH BP
MOV BP,SP
SUB SP,14
...
MOV SP,BP
POP BP
RET
El registro BP desempea el papel de registro apuntador del bloque de activacin de la su-
brutina. Por eso, la segunda instruccin generada (MOV BP,SP) le asigna el valor actual del
342 Compiladores e intrpretes: teora y prctica
b
a
Espacio libre
SP
Direccin
de retorno
Figura 10.2. La pila de llamadas a subrutinas despus de llamar a rutina(a,b).
10-CAPITULO 10 9/2/06 11:54 Pgina 342
registro SP, que apunta a la cabecera de la pila, tal como est en ese momento. Antes de ha-
cerlo, sin embargo, el valor antiguo de BP se guarda en la propia pila, para poder recupe-
rarlo al final de esta subrutina, ya que dicho valor antiguo de BP apuntaba al bloque de
activacin de la subrutina que ha llamado a sta.
A continuacin, la tercera instruccin generada (SUB SP,14) reserva memoria en la pila
para el resto del bloque de activacin de la subrutina, que est formado por las variables lo-
cales declaradas dentro de sta (i, j, k y r), cuyo tamao total es de 14 Bytes.
Al llegar la ejecucin del programa objeto a los puntos suspensivos, el estado de la pila ser
el que indica la Figura 10.3.
Obsrvese que el registro BP (apuntador del bloque de activacin de la subrutina) apunta a
la direccin en que se ha guardado en la pila el valor anterior del propio registro BP. Por
encima de esa direccin (recurdese que la pila se expande hacia abajo) estn la direccin
de retorno a la subrutina que invoc a sta, y los valores de los argumentos que se le han
pasado, situados en las posiciones BP+6 (a) y BP+8 (b). En cuanto al resto del bloque de
activacin (las variables locales), el espacio reservado para ellas se encuentra situado justo
por debajo del valor actual de BP. El compilador ha asignado a dichas variables las direc-
ciones indicadas en la Tabla 10.2.
Al final de la ejecucin de la subrutina, en el momento de realizar el retorno al punto des-
de el que se la invoc, es preciso deshacer todo este trabajo inicial, para devolver la pila a
las condiciones en que se encontraba antes de la invocacin de la subrutina, con objeto de
que el programa anterior pueda seguir funcionando correctamente (en particular, el registro
BP debe volver a apuntar al bloque de activacin de dicho programa). Para ello, basta con
que el compilador genere las tres instrucciones siguientes:
Captulo 10. Gestin de memoria 343
b
a
Espacio libre
BP
Direccin
de retorno
BP antiguo
i
j
k
r SP
[BP+8]
[BP+6]
[BP+2]
[BP]
[BP-2]
[BP-4]
[BP-6]
[BP-14]
[BP-16]
Figura 10.3. La pila de llamadas a subrutinas despus de ejecutar el principio
de rutina(a,b).
10-CAPITULO 10 9/2/06 11:54 Pgina 343
MOV SP,BP, que deshace la reserva de espacio para las variables locales de la subruti-
na. Obsrvese que se habra conseguido lo mismo ejecutando la instruccin ADD
SP,14, pero sta es menos eficiente, por incluir una operacin de suma, mientras que
la instruccin elegida slo tiene que copiar el contenido de un registro en otro.
POP BP, que devuelve al registro BP el valor que tena en la rutina que invoc a la su-
brutina actual (con lo que pasa a apuntar al bloque de activacin de aqulla). Despus
de ejecutar esta instruccin, el registro SP pasa a apuntar a la direccin de retorno.
RET, que pasa el control a la direccin apuntada por SP (es decir, devuelve la ejecucin
a la instruccin siguiente a CALL rutina) en el cdigo generado para el programa que
invoc a la subrutina. En ese punto, la instruccin siguiente que se debe ejecutar es ADD
SP,4, que libera el espacio ocupado por los argumentos de la subrutina, devolviendo
la pila (y el registro apuntador SP) al estado en que se encontraba antes de la invoca-
cin de la subrutina.
La Figura 10.4 resume la ejecucin de la llamada a la subrutina y de la ejecucin de sta.
Las variables automticas que aparecen definidas en bloques internos se tratan exactamen-
te igual, pero excluyendo la llamada de subrutina, el tratamiento del registro apuntador BP
y el retorno. Vase un ejemplo en la instruccin siguiente:
for (i=0; i<n; i++) {
int j, k;
. . .
}
El cdigo generado por el compilador para este bloque ser:
SUB SP,4
...
ADD SP,4
344 Compiladores e intrpretes: teora y prctica
Variable Direccin asignada
b [BP+8]
a [BP+6]
direccin de retorno [BP+2]
BP antiguo [BP]
i [BP-2]
j [BP-4]
k [BP-6]
r [BP-14]
Tabla 10.2. Direcciones asignadas a las variables en el bloque de
activacin del programa rutina.
10-CAPITULO 10 9/2/06 11:54 Pgina 344
La primera instruccin reserva espacio para las variables locales del bloque, j y k, al final
del bloque de activacin de la subrutina que contiene a esta instruccin. Si se tratase de la
subrutina del ejemplo anterior, esto significa que a las dos variables locales al bloque se les
asignaran las direcciones [BP-16] y [BP-18], respectivamente. Obsrvese que, en este
caso, el compilador no puede utilizar la instruccin MOV SP,BP, en lugar de ADD SP,4,
pues en tal caso liberara todo el bloque de activacin de la subrutina, en lugar del espacio
local a este bloque.
Variables dinmicas: El propio programa objeto pide espacio para ellas durante su ejecu-
cin. Usualmente esto se hace mediante una llamada a alguna funcin u operador adecua-
dos (como malloc en el lenguaje C, o new en C++). El cdigo objeto suele venir
prefabricado, dentro de alguna biblioteca (library) de subrutinas asociada al compilador,
que se proporciona con ste para que el programador o el compilador puedan utilizarlas, cu-
yas llamadas sern resueltas ms tarde por el programa enlazador (linker). La nica res-
ponsabilidad del compilador, en este caso, es generar la llamada a dicha subrutina de
biblioteca.
Es importante que las subrutinas asociadas a la gestin de la memoria dinmica estn bien
diseadas. A este respecto, se puede mencionar un caso concreto. En un compilador para el
lenguaje C comercializado por Microsoft a principios de los aos noventa, la subrutina pre-
definida malloc utilizaba el siguiente criterio para la reasignacin de la memoria dinmi-
ca. Buscaba espacio en primer lugar en el bloque inicial (que al principio abarcaba toda la
memoria dinmica), y slo si no encontraba espacio en l buscaba en los bloques reutiliza-
bles (liberados anteriormente por el programa objeto). Esto tena la consecuencia de que la
memoria dinmica se fragmentaba progresivamente, aunque tambin se obtena a cambio
alguna mejora en el tiempo de ejecucin, pues era ms probable (al principio, al menos) en-
Captulo 10. Gestin de memoria 345
Programa Fuente:
...
rutina (a,b)
...
int rutina (int a, char *b)
{
int i, j, k;
double r;
...
return k;
}
Programa Objeto:
PUSH b
PUSH a
CALL rutina
ADD SP,4
rutina:
PUSH BP
MOV BP, SP
SUB SP, 14
...
MOV SP, BP
POP BP
RET
Figura 10.4. Resumen del cdigo generado para la ejecucin de una llamada de subrutina.
10-CAPITULO 10 9/2/06 11:54 Pgina 345
contrar espacio en dicho bloque, cuyo tamao sola ser el ms grande de todos, hasta que
su fragmentacin progresiva lo iba reduciendo.
El problema se presentaba en aplicaciones de funcionamiento permanente (como, por ejem-
plo, programas de gestin del negocio en una tienda) que estn continuamente requiriendo
espacio dinmico y liberndolo y que deben funcionar 24 horas al da y siete das por se-
mana. Al cabo de algn tiempo, el programa fallaba porque la memoria se haba fragmen-
tado hasta tal punto que la siguiente peticin de espacio ya no encontraba un bloque de
memoria del tamao suficiente. Para evitarlo, hubo que sustituir la versin prefabricada de
malloc contenida en la biblioteca asociada al compilador por una versin propia, que in-
verta el criterio de asignacin de espacio, reutilizando los bloques liberados antes de in-
tentar buscarlo en el bloque inicial. Con este cambio se resolvi totalmente el problema, y
la aplicacin pudo funcionar indefinidamente sin que se le acabase la memoria.
Aunque las variables dinmicas presentan menos dificultades para un compilador, en reali-
dad la carga de trabajo asociada a su utilizacin correcta se traslada al programador. En par-
ticular, es importante que el programador tenga cuidado de liberar de forma ordenada toda
la memoria dinmica que ha ido reservando a lo largo del programa. Si no lo hace, pueden
producirse errores de ejecucin muy difciles de detectar, pues no suelen hacer efecto has-
ta mucho despus de haberse producido, lo que dificulta su depuracin.
Ante el tratamiento de la liberacin de la memoria dinmica, existen varias posibilidades:
No liberar nada. La memoria dinmica que va reservando el programa no puede reuti-
lizarse, aunque el programa haya dejado de necesitarla. Cuando se acabe totalmente la
memoria disponible, el programa fallar. Antes de que esto ocurra, si el sistema opera-
tivo utiliza el disco duro como extensin de la memoria principal (paginacin o swap,
en ingls), la eficiencia del programa puede deteriorarse.
Liberacin explcita, que se realiza mediante llamadas a subrutinas u operadores espe-
ciales (free en el lenguaje C, delete en C++, dispose en PASCAL). En este caso
hay que tener un cuidado especial, pues un mal uso de la liberacin puede dar lugar a
errores catastrficos, como los siguientes:
Liberacin de una memoria que no haba sido asignada.
Liberacin incorrecta de una variable, antes de que el programa objeto haya termi-
nado de utilizarla. Si dicha memoria es reasignada a otra variable por la subrutina u
operador de asignacin de memoria dinmica, el programa no funcionar bien, pues
una de sus variables habr recibido un valor incorrecto, que podra ser incluso in-
compatible con su tipo. Este error se detectar cuando se vuelva a usar dicha varia-
ble, lo que puede ocurrir mucho despus de la liberacin incorrecta, a veces despus
de transcurrir horas de ejecucin del programa.
Doble liberacin de la misma memoria dinmica asignada a una variable: se reduce
al caso anterior.
Liberacin implcita. En los compiladores, slo suele aplicarse a las variables autom-
ticas, como se explic anteriormente. En los intrpretes desempea un papel muy im-
portante, como se ver en el apartado siguiente.
346 Compiladores e intrpretes: teora y prctica
10-CAPITULO 10 9/2/06 11:54 Pgina 346
Gestin de la memoria en un intrprete
En los intrpretes, la gestin de la memoria es muy distinta a la de un compilador. En primer lu-
gar, toda la memoria asignada al programa objeto suele tratarse como memoria dinmica (ex-
cepto las variables que ocupan poco espacio, cuyo valor puede incluirse en la propia tabla de
smbolos), recabndose espacio para cada variable cuando comienza a ser necesaria, y libern-
dolo automticamente en cuanto deja de serlo. En este caso, la gestin de la memoria dinmica
la lleva el propio intrprete, lo que quita mucho trabajo al programador y permite simplificar con-
siderablemente el lenguaje fuente, que gracias a ello puede prescindir de declaraciones de varia-
bles y de todos los problemas asociados a la gestin y liberacin de la memoria dinmica, que se
acaban de comentar en el apartado anterior.
La liberacin automtica de memoria dinmica por el intrprete puede provocar la fragmen-
tacin de dicha memoria, por lo que ms pronto o ms tarde es preciso realizar su compactacin,
para poder reutilizarla correctamente. La gestin de la memoria dinmica se denomina recolec-
cin de basuras o de desechos (garbage collection, en ingls).
Recordando lo que se dijo en la Seccin 8.4, al hablar de la estructura de la tabla de smbolos
en un intrprete, existen cuatro mtodos principales para liberar la memoria asociada a una va-
riable:
Liberacin explcita o definicin esttica de qu es basura: El programador decide cun-
do debe liberarse una variable, con un mtodo semejante al que se usa en un compilador.
Sin declaracin explcita de basura: La liberacin de espacio innecesario no se produce
cuando la memoria asignada a una variable deja de ser necesaria, sino que se retarda hasta
el momento en que la memoria dinmica se agota y es indispensable realizar una recolec-
cin de desechos. Este mtodo parece ms rpido a primera vista, pero esa ventaja es en-
gaosa, pues, cuando se produce el agotamiento de la memoria, la recoleccin de desechos
suele ser difcil y lleva mucho tiempo, con lo que interrumpe durante perodos considera-
bles la ejecucin de los programas. En algunos intrpretes que se comercializaron durante
los aos ochenta, a menudo ocurra que apareca un mensaje en la pantalla advirtiendo que
la recoleccin de basuras estaba en proceso, y a veces se tardaba media hora en reanudar la
ejecucin del programa objeto. En ciertos entornos (como en el control de instrumentos de
laboratorio o en sistemas en tiempo real) este funcionamiento es inadmisible, por lo que
este mtodo de asignacin dinmica de memoria ha dejado prcticamente de utilizarse, ex-
cepto en casos muy concretos y especializados.
Referencia nica: Cuando a un puntero se le asigna un nuevo valor, la zona de memoria a
la que apuntaba previamente se declara basura. Esto tiene la restriccin de que todo espa-
cio reservado en la memoria dinmica slo puede estar apuntado por un nico puntero:
asignar un puntero a otro estara prohibido, pues en caso contrario, al declarar basura el pri-
mero, el segundo quedara apuntando a una regin inutilizada, lo que podra dar lugar a
errores peligrosos. Este procedimiento, que se emplea cuando la tabla de smbolos del in-
trprete no utiliza una tabla auxiliar de referencias, obliga a duplicar la asignacin de la me-
moria cuando se desea disponer de dos punteros que apunten a los mismos valores, como
10.2
Captulo 10. Gestin de memoria 347
10-CAPITULO 10 9/2/06 11:54 Pgina 347
ocurre, por ejemplo, cuando se asigna el valor de una variable a otra, o cuando se introdu-
ce una variable como argumento de una subrutina, con la modalidad de paso por valor.
Aunque este procedimiento ocupa mucha memoria, es algo ms rpido que el que vamos a
ver a continuacin, por lo que, en la prctica, se utilizan ambos en diversos intrpretes.
Referencia mltiple: Cuando los punteros de la tabla de smbolos no apuntan directa-
mente a la memoria dinmica asignada a las variables, sino a travs de una tabla de refe-
rencias, como se explic en la Seccin 8.4. En este caso, pueden existir varios punteros
apuntando a la misma zona de datos dinmicos. La tabla de referencias contiene informa-
cin sobre el nmero de punteros que apuntan al mismo espacio, que no se declarar ba-
sura hasta que dicho nmero se reduzca a cero, lo que indica que el espacio ha dejado de
ser necesario.
10.2.1. Algoritmos de recoleccin automtica de basura
Existen diversos algoritmos para la recoleccin de desechos, que se utilizan en diversos intrpre-
tes. A veces pueden combinarse con xito varios de ellos en un solo intrprete. Mencionaremos
los siguientes:
Asignacin de memoria por un extremo del bloque libre inicial y recoleccin global de
basuras cuando no queda espacio en dicho bloque: Este procedimiento tiene la ventaja
de que los bloques ms usados acabarn ocupando espacio en direcciones bajas de memo-
ria y ya no se movern ms. En cambio, lo que va quedando del bloque libre inicial a lo lar-
go de la ejecucin se encuentra siempre en las direcciones ms altas, y su localizacin ser
factible con un solo puntero. La desventaja principal de este mtodo es que, cuando un blo-
que antiguo queda libre, para reutilizar ese espacio hay que mover casi todo lo dems. Su
problema principal es que el tiempo necesario para llevar a cabo la ejecucin de un pro-
grama concreto no es predecible con exactitud, pues en la mitad de dicha ejecucin puede
interponerse una recoleccin de basuras, de duracin impredecible. En una de estas opera-
ciones, para un espacio total de 20 MBytes, puede ser necesario cambiar de sitio de 2 a
4 MBytes. Si el sistema est paginado (swap), la cosa puede ser peor si el sistema operati-
vo disminuye automticamente la prioridad de las zonas de memoria afectadas.
Cada vez que se mueve la posicin de un bloque, es preciso modificar al mismo tiempo to-
dos los apuntadores que contenan su direccin. Con los sistemas de referencia nica y de
tabla de referencias vistos anteriormente, esto significa cambiar el valor de un solo punte-
ro. A veces se incluye en cada bloque un puntero inverso que apunta a su propio apuntador,
lo que reduce el tiempo necesario para la recoleccin de basuras.
Para reducir el nmero de veces que se realiza un desplazamiento completo, retrasando la
aparicin de la necesidad de hacerlo, lo que ocurre cuando una peticin de memoria din-
mica no puede satisfacerse, porque no existe ningn bloque del tamao adecuado, suelen
utilizarse diversos mecanismos paliativos, como los siguientes:
Fusin automtica del ltimo bloque: Si el bloque liberado es el ltimo, y a conti-
nuacin comienza lo que queda de la zona libre inicial, en lugar de someter dicho blo-
348 Compiladores e intrpretes: teora y prctica
10-CAPITULO 10 9/2/06 11:54 Pgina 348
que al sistema general de recoleccin de basuras, bastara fusionarlo con la zona libre
inicial, modificando el valor del puntero que apunta a sta.
Fusin de bloques libres: Siempre que dos bloques contiguos quedan libres, se fusio-
nan para formar un solo bloque ms grande.
Desplazamiento de bloques ocupados para permitir la fusin de bloques libres no
contiguos: Es un paso adicional que facilita la realizacin del paso anterior y retrasa
an ms la necesidad de la recoleccin de basuras global.
Asignacin de memoria alternativa por los dos extremos del bloque libre inicial y re-
coleccin global de basuras cuando no queda espacio en dicho bloque (algoritmo ping-
pong): En este caso, lo que va quedando del bloque libre inicial a lo largo de la ejecucin
se encuentra ms o menos centrado en la memoria dinmica disponible, por lo que ser pre-
ciso localizar su posicin mediante dos punteros. Este procedimiento puede reducir consi-
derablemente el nmero de recolecciones de basuras que ha de realizarse durante la
ejecucin de un programa. Para ver cmo puede ocurrir esto, y cmo difiere este mtodo
del anterior, considrese el siguiente programa escrito en el lenguaje APL:
A 1 3 2.5
A 2 + 3 A
La primera instruccin asigna a la variable A el vector formado por los tres valores
reales (1 3 2.5). La segunda instruccin calcula el resultado de sumar 2 al triple de la
parte entera de A. La expresin A significa la parte entera de A. En APL, las opera-
ciones aritmticas se aplican automticamente a vectores, matrices y estructuras com-
pletas ms complejas, de modo que las operaciones indicadas en la segunda instruccin
necesitaran de la asignacin de memoria dinmica para almacenar los distintos resul-
tados parciales, de los que slo el ltimo ser asignado como nuevo valor de la varia-
ble A.
Las Figuras 10.5 y 10.6 muestran cmo se ejecutaran estas dos instrucciones con el pro-
cedimiento de la asignacin de memoria por un solo extremo y por los dos extremos del
bloque inicial, respectivamente.
Caso de la asignacin de memoria por un solo extremo:
a) En la situacin inicial, la memoria est vaca. El puntero apunta al principio de dicha
memoria.
b) Durante la ejecucin de la instruccin A 1 3 2.5, se obtiene de la memoria libre
el espacio dinmico necesario para introducir un vector de tres elementos. Dicho es-
pacio queda asignado a la variable A, mientras el puntero a la memoria libre se ha des-
plazado adecuadamente.
c) En la segunda instruccin, la primera operacin que hay que realizar es el clculo de
la parte entera de (A), cuyo valor resultar ser el vector de tres elementos (1 3 2). Para
calcularlo, se necesita otro bloque de memoria dinmica, que se extrae del principio
de la zona libre. El puntero se desplaza adecuadamente.
Captulo 10. Gestin de memoria 349
10-CAPITULO 10 9/2/06 11:54 Pgina 349
d) La segunda operacin que hay que realizar es el producto de la constante 3 por el re-
sultado de la operacin anterior. Para ello, hay que pedir espacio para otro vector de
tres elementos, al que se asignarn los valores (3 9 6). Mientras no se haya calculado
ese producto, no se puede prescindir del resultado de A. Pero una vez calculado
3 A ya se puede declarar basura el espacio asignado a A.
e) A continuacin, hay que sumar la constante 2 al resultado de la operacin anterior.
Para ello, hay que pedir nuevo espacio. Con el algoritmo que estamos considerando,
dicho espacio se obtiene del bloque libre inicial, sin reutilizar el que fue declarado ba-
sura en el paso anterior, que no estar directamente accesible hasta que se realice una
recoleccin de desechos. En el bloque nuevo se calcula la operacin 2 + 3 A, cuyo
resultado es (5 11 8). Slo en este punto se puede declarar basura el bloque corres-
pondiente al resultado de la operacin anterior (3 A).
f) Finalmente, se ejecuta la asignacin del ltimo resultado a la variable A. El valor an-
tiguo se declara basura. Obsrvese que el bloque libre inicial ha quedado fragmenta-
do en dos zonas libres, separadas por una zona utilizada por el valor actual de la
variable A.
350 Compiladores e intrpretes: teora y prctica
Puntero al bloque inicial
a) b) c) d) e) f)
A A A A A
A
A
3 x A
3 x A
3 x A
2 + 3 x A
Figura 10.5. Ejecucin de dos instrucciones APL con asignacin dinmica de memoria
por un solo extremo del bloque inicial.
10-CAPITULO 10 9/2/06 11:54 Pgina 350
Caso de la asignacin de memoria alternativamente por los dos extremos:
a) En la situacin inicial, la memoria est vaca. Los dos punteros apuntan al principio y
al fin de dicha memoria.
b) Durante la ejecucin de la instruccin A 1 3 2.5, se obtiene de la parte superior
de la memoria libre el espacio dinmico necesario para introducir un vector de tres ele-
mentos. Dicho espacio queda asignado a la variable A, mientras el primer puntero a la
memoria libre se ha desplazado adecuadamente.
c) En la segunda instruccin, la primera operacin que hay que realizar es el clculo de
la parte entera de (A), cuyo valor resultar ser el vector de tres elementos (1 3 2). Para
calcularlo, se necesita otro bloque de memoria dinmica, que se extrae del final de la
zona libre. El segundo puntero se desplaza adecuadamente.
d) La segunda operacin que hay que realizar es el producto de la constante 3 por el re-
sultado de la operacin anterior. Para ello, hay que pedir espacio para otro vector de
tres elementos, que se obtendr del extremo superior de la memoria libre, y al que se
asignarn los valores (3 9 6). Una vez calculado el producto 3 A ya se puede
declarar basura el espacio asignado a A. Como este espacio es frontero con el final
Captulo 10. Gestin de memoria 351
Puntero al bloque inicial
a) b) c) d) e) f)
A A A A A
A
3 x A
3 x A
2 + 3 x A
Figura 10.6. Ejecucin de dos instrucciones APL con asignacin dinmica de memoria
por los dos extremos del bloque inicial.
10-CAPITULO 10 9/2/06 11:54 Pgina 351
actual de la memoria libre, puede fusionarse directamente con sta, modificando el
valor de dicho puntero.
e) A continuacin, hay que sumar la constante 2 al resultado de la operacin anterior.
Para ello, hay que pedir nuevo espacio, que se obtendr del extremo inferior del blo-
que libre inicial, con lo que se reutilizar automticamente el bloque que qued li-
bre en el paso anterior. En el bloque nuevo se calcula la operacin 2 + 3 A, cuyo
resultado es (5 11 8). En este punto se puede declarar basura el bloque correspon-
diente al resultado de la operacin anterior (3 A). Como dicho bloque es fronte-
rizo con el extremo superior de la memoria libre, el algoritmo que estamos
utilizando lo fusionar automticamente con sta, modificando el valor del puntero
correspondiente.
f) Finalmente, se ejecuta la asignacin del ltimo resultado a la variable A. El valor an-
tiguo se declara basura. Dado que dicho valor es fronterizo con el bloque libre, puede
fusionarse con l inmediatamente. Obsrvese que el bloque libre inicial no ha queda-
do fragmentado, y que el valor asignado a la variable A se encuentra ahora en el otro
extremo de la memoria. Es evidente que, con este algoritmo, el nmero de recoleccio-
nes de basura necesarias disminuye considerablemente.
Algoritmo basado en la utilizacin de clulas colega (buddy cells): Este algoritmo, que
se debe a Knuth, Markowitz y Knowlton, reparte el espacio disponible en celdas cuyo
tamao es una potencia de 2. Si se necesita un bloque de un tamao determinado, dicho
tamao se extiende a la potencia de 2 inmediata superior. Existen tantas listas de zonas
libres como tamaos posibles. Si una lista est vaca y se precisa un bloque de ese ta-
mao, se extrae un bloque de la lista de tamao superior y se divide en dos. Uno queda
asignado como respuesta a la peticin, y el otro pasa a la lista de bloques libres. El pro-
cedimiento es recursivo. Estos dos bloques estn ligados entre s permanentemente (se los
llama colegas).
Cuando se declara basura un bloque, se examina el estado en que se encuentra su compa-
ero. Si est libre tambin, ambos bloques se fusionan para formar un solo bloque de ta-
mao doble. Este procedimiento tambin es recursivo.
Veamos un ejemplo. Supongamos que la memoria libre est formada por un solo bloque
de 4 kBytes y que ste es el tamao mximo posible. Los tamaos menores que vamos
a considerar son 128, 256 y 512 Bytes, 1 kByte y 2 kBytes. Existirn, por tanto, seis
listas de memoria libre, cinco de las cuales estarn inicialmente vacas [vase la
Figura 10.7.a)].
Se pide espacio para 80 Bytes. La peticin se redondea a la potencia de 2 inmediata supe-
rior (128 Bytes). Dado que la lista correspondiente est vaca, se pasa a la lista siguiente
(256), que tambin est vaca, y as sucesivamente hasta llegar a la nica lista con elemen-
tos libres (la de 4 kBytes). Se divide el bloque de 4 kBytes en dos zonas iguales de 2 kBytes.
Se pone una en la lista de bloques correspondiente, mientras la otra se divide en dos zonas
de 1 kByte. Se pone una en la lista de bloques de 1 kByte y se divide la otra en dos de 512
Bytes, y as sucesivamente hasta que obtenemos dos zonas de 128 Bytes, una de las cuales
352 Compiladores e intrpretes: teora y prctica
10-CAPITULO 10 9/2/06 11:54 Pgina 352
ir a la memoria libre y la otra ser devuelta como respuesta a la peticin inicial [vase la
Figura 10.7.b)].
Si la memoria libre de 4 kBytes empieza originalmente en una direccin mltiplo de
4 kBytes, cada bloque parcial de 2
n
Bytes tendr una direccin cuyos n bits inferiores son
iguales a cero. Esto significa que la direccin de la celda colega de una celda dada se pue-
de obtener fcilmente haciendo una operacin O exclusivo de su direccin con su tamao.
Cada zona debe estar asociada con dos datos: un bit que indique si est libre y un campo
que indique su tamao. Con eso, se puede localizar la celda colega. Cuado se libera un blo-
que, se mira a ver si su pareja tiene el mismo tamao y si est libre. En tal caso, ambas pue-
den fusionarse en un bloque de tamao doble.
Para evitar fusiones y divisiones consecutivas, se puede dejar un nmero mnimo de blo-
ques en cada cadena o retardar la fusin hasta que se requiera un bloque grande. Se calcu-
la que la utilizacin media de cada bloque no pasa del 75% (en la prctica es algo menor,
pues los bloques menores son ms frecuentes que los mayores).
Resumen
Este captulo aborda el tema de la gestin de memoria en los procesadores de lenguaje. Se trata
de una componente que funciona de modo completamente diferente para los compiladores y para
los intrpretes, por lo que el captulo se divide de forma natural en las dos secciones correspon-
dientes.
10.3
Captulo 10. Gestin de memoria 353
Lista de 128 Bytes
Lista de 256 Bytes
Lista de 512 Bytes
Lista de 1 kByte
Lista de 2 kBytes
Lista de 4 kBytes
Espacio asignado
Lista de 128 Bytes
Lista de 256 Bytes
Lista de 512 Bytes
Lista de 1 kByte
Lista de 2 kBytes
Lista de 4 kBytes
a) b)
Figura 10.7. Ejemplo de recoleccin de basura utilizando clulas colega.
10-CAPITULO 10 9/2/06 11:54 Pgina 353
En la primera seccin, se analiza el modo en que un compilador debe gestionar la utilizacin
de la memoria por el programa objeto que est generando. Se analizan distintos modos del uso
de la memoria por los programas objetos en las arquitecturas INTEL, y se distingue entre el tra-
tamiento que se puede aplicar a las variables estticas, automticas y dinmicas.
En la segunda seccin, se revisan distintos mtodos para la gestin de la memoria de un in-
trprete (memoria que utilizan en comn el propio intrprete y el programa objeto). El problema
principal que hay que resolver en este contexto es la eliminacin de la memoria innecesaria, que
tuvo utilidad en algn momento pero posteriormente ha dejado de tenerla. Se comparan distin-
tos procedimientos para la compactacin de la memoria utilizable y la eliminacin de basuras o
desechos.
Bibliografa
[1] Aho, A. V.; Sthi, R. y Ullman, J. D. (1986): Compiler: Principles, techniques and Tools,
Reading, MA, Addison-Wesley Publishing Company.
[2] Alfonseca, N.; Sancho, J. y Martnez, M. (1990): Teora de lenguajes, gramticas y
Autmatas, Madrid, Ed. Universidad y Cultura.
[3] Fischer, C. N. y LeBlanc Jr., R. J. (1991): Crafting a compiler with C. Redwood City, Ben-
jamin/Cummings.
[4] Gries, D. (1971): Compiler construction for Digital Computers, New York, John Wiley and
Sons, Inc. Existe traduccin espaola de F. J. Sanchs Llorca, 1975.
[5] Grune, D. (2000): Modern Compiler Design, Wiley.
[6] Hopcroft, J. E.; Motwani, R. y Ullman, J. D. (2001): Introduction to Automata Thory,
Languages, and Computation, Addison Wesley. Existe versin espaola: Hopcroff, J. E.,
Motwani, R. y Ullman, J. D. (2002): Introduccin a la Teora de Autmatas, Lenguajes y
Computacin, Madrid, Addison Wesley.
[7] Ed. Koskimies, K. (1998), Compiler construction, Proc. 7th Int. Conf. CC98, Springer.
[8] Linz, P. (1990): An introduction to Formal Languages and Automata, Lexington, D. C.,
Heath and Co.
[9] Louden, K. C. (2004): Construccin de compiladores. Principios y prctica, Thomson.
[10] Marcotty, M.; Ledgard, H. F. y Bochmann, G. V. (1976): A sampler of Formal Definitions,
Computing Surveys, 8:2, pp. 181-276.
[11] Wirth, N. (1996): Compiler Construction, Addison-Wesley.
354 Compiladores e intrpretes: teora y prctica
10-CAPITULO 10 9/2/06 11:54 Pgina 354
ndice analtico
A
accin semntica, 200-203, 210-211, 219,
224, 228, 234, 238-239, 255-258, 268,
274-275, 280
acumulador, 244, 246, 247, 248, 281
ADA, lenguaje de programacin, 26, 27
Adelson-Velskii, vase bsqueda con rboles
AVL
algoritmo, 1, 23, 30, 177-180, 181-182
de recoleccin automtica de basuras, 348-
353
por llenado de tabla, 73
al-Jowritzm, Abu Jafar Mohammed ibn
Musa, 1
ambigedad, 21-22, 23, 75, 176-177, 279
mbito, 56, 59, 61
anlisis
ascendente, 89, 114, 127, 219, 223, 227,
260, 268
LALR(1), 89, 116, 159, 234
LR, 127
LR(0), 89, 116, 127, 129, 138, 140,
143, 145-147, 155, 159-160, 167
LR(1), 89, 116, 148, 152, 155-156,
159-160, 167
LR(k), 127, 151, 158-159
SLR(1), 89, 116, 138, 140, 145-147,
159-160
de precedencia simple, 89, 177-180
descendente, 89, 93, 114, 218-219, 223,
227, 260-261, 268
con vuelta atrs, 93
con vuelta atrs rpida, 97
LL(1), 99, 111
selectivo, sin vuelta atrs o descenso
recursivo, 93, 99, 102, 111, 227
analizador
ascendente, vase anlisis ascendente
descendente, vase anlisis descendente
LALR(1), vase anlisis LALR(1)
LR(0), vase anlisis LR(0)
LR(1), vase anlisis LR(1)
morfolgico o lxico, 28-29, 65, 192, 196,
200, 217, 231-232, 234, 237, 240, 322,
332
semntico, 29, 192, 194, 211, 217-218,
221, 223, 228, 230, 232-235, 322, 332
sintctico, 29, 89, 142-143, 196, 200, 217-
218, 220-223, 237, 322, 332, 333
anotacin semntica, 192, 194, 208
APL, lenguaje de programacin, 26, 28, 253,
318, 322
apuntador, 247, 253, 254, 324, 327, 342-344,
347-352
del anlisis, 128, 130-131, 139, 151, 153
tipo de dato, 157, 217, 228-230
rbol
de derivacin, 19-21, 97-99, 114, 141, 148-
149, 151, 175, 192-194, 196, 202, 205,
208, 221, 224
con anotaciones semnticas, 192-
193, 196, 200-201
AVL, vase bsqueda
Indice alfabtico 9/2/06 11:54 Pgina 355
binario ordenado, vase bsqueda
arco, 68, 181, 182
argumento, de un procedimiento, 58, 192, 230,
233, 258, 341-344, 348
array, 192, 229, 301, 324, 330, 340
ASCII, 286
asidero, 14-15, 21, 89, 117, 132, 218, 223, 260
asignacin, 157, 206-208, 231, 254, 266, 287,
294, 314, 324, 352
muerta, 311-314
atributo,
heredado, 205, 216-217, 221, 223, 230
semntico, 199, 202-203, 205, 210, 212,
214, 216-217, 235-236
sintetizado, 205, 214, 216, 221, 223
atributos
evaluacin de, 208
propagacin de, 205, 231
sistema de, 203
autmata
a pila, 4, 29, 30, 90, 116-117
aceptador, 3
concepto de, 2,3, 68
de anlisis, 117, 127, 129, 131-133, 135-
139, 151, 154-156, 158, 160-161, 166-
167
finito, 2, 4, 117
determinista, 29, 65-66, 69, 71-76
no determinista, 66, 68-71
autmatas, teora de, 2
axioma, 11, 14, 18, 19, 22, 23, 24, 29, 89-94,
110, 113-114, 116, 129, 168-169, 235
B
Backus, forma normal de, vase B.N.F.
BASIC, lenguaje de programacin, 26, 28,
318, 319
basura, vase recoleccin de basuras
binoide, 9
Bison, 234
bloque, vase tb. estructura de bloques, len-
guaje con , 59-62, 279, 310, 314
abierto, 57-58
actual, 57-58, 60
cerrado, 57, 60
de entrada, 309-313
predecesor, 309-312
B.N.F., 12, 237-238
Borrar, operacin de diccionario, 38-39, 41,
44
bucle, vase instruccin iterativa
buddy cells, vase clulas colega
Buscar, operacin de diccionario, 38
bsqueda, algoritmo de
binaria, 35, 39
con rboles AVL, 37, 44
con rboles binarios ordenados, 35-36, 40
lineal, 34, 56
listas ordenadas, 56
tabla hash o de smbolos, 51, 55, 59, 60
Bytecode, 28, 317, 318, 321
C
C, lenguaje de programacin, 26, 27, 56, 79-
81, 104, 157, 228-229, 234-239, 253,
258-259, 287, 290, 292, 304, 307, 319,
328, 329, 330, 339, 340, 345, 346
C++, lenguaje de programacin, 26, 27, 56,
258, 259, 314, 319, 320, 329, 330, 339,
340, 345, 346
cardinal, 8, 177
clulas colega, 352, 353
Chomsky, Avram Noam , 3, 4, 15, 16, 25, 30,
90
cierre, vase clausura
de un conjunto de configuraciones, 129-
130, 132-136, 152-155, 157
cierre l, 72
clausura, 10, 67-68, 78
clave, 196
COBOL, lenguaje de programacin, 26, 27
cdigo intermedio, 194, 258-282
colisin, 46-47, 50, 55
comentario, 29, 65, 76, 84-85
compilador, 3, 27-29, 30, 243, 318, 319-321
compilador-intrprete, 28, 317
complejidad, 33
complementacin, 11
356 Compiladores e intrpretes: teora y prctica
Indice alfabtico 9/2/06 11:54 Pgina 356
computabilidad, 2
concatenacin, 5-6, 7, 8-9, 67-68, 78, 245,
332
condicin de inicio, 80, 83-85
exclusiva, 84
configuracin
de anlisis, 129-132, 134-136, 138, 144,
151-153, 156-157, 160-161, 166
de anlisis LR(0), 127-128
de desplazamiento, 128, 139
de reduccin, 57, 128, 138-139, 147, 149,
158
conflicto, 139, 145, 147, 158, 167
conjunto
de generadores, 6, 9
de smbolos de adelanto, 151-153, 155-
158, 160-161, 166
first y last, 168, 171-174
primero, 90-93, 102-103, 111-113, 155-
156
siguiente, 90-93, 102-103, 111-113, 145,
147-149, 160
cudrupla, 258-259, 267-282, 289-300, 302-
303, 306-308, 311-312, 317
D
declaracin, 201, 233
de una funcin o procedimiento, 30
de un identificador, 194, 230-231, 318,
328, 329, 332
definicin dirigida por la sintaxis, 223
delimitador, 76
dependencia
circular, 227
constante, 45
en eliminacin de redundancias, 293-300
entre atributos , 219, 222, 224, 226
lineal, 33, 40
logartmica, 35, 44-45
depuracin de programas, 285, 319, 320-321,
334, 346
derivacin, 13, 14, 16, 17, 19-22, 89, 91, 93-
95, 97, 147, 175
desplazamiento, 229, 247, 260, 268, 339
accin de anlisis, 115-117, 120-122,
131, 137, 141-144, 149, 154, 158, 218,
223
diccionario, 30, 38-39
direccin , 229-231
de un identificador, 229, 244-248, 253,
339-340, 341, 344
de retorno, 246, 338, 342, 343, 344
direccionamiento, vase hash, tabla
directiva, 79, 234-235
dispersin, vase hash, tabla
do, vase instruccin iterativa
E
encadenamiento, vase hash, tabla
ensamblador, 27, 195, 243-258, 289, 339
entrada calculada, vase hash, tabla
equivalencia de gramticas, 17, 18, 22, 168,
169, 176
error
de compilacin, 328
de ejecucin, 327
morfolgico, 29, 77, 327
semntico, 192, 327
sintctico, 125-127, 131, 142, 158, 178,
182, 327
errores
correccin de, 319, 331-333
recuperacin de, 29, 329-331, 333-334
tratamiento de, 77, 320-321, 322,
327-335
estado , 120-121, 123
de aceptacin, 132, 144, 158
de reduccin, 132
final, 68, 72-76, 132, 147, 158
inicial, 68, 72-76, 154
estructura de bloques, lenguaje con, 56, 60
etiqueta, 57, 62, 230, 232, 233, 243, 245-246,
255-258, 266, 275-279, 339
expresin
aritmtica, 29, 119, 126, 192, 199, 206,
231, 249-253, 288-289, 292, 300-302
con subndices, 266, 267, 330
regular, 4, 66-71, 75-84
ndice analtico 357
Indice alfabtico 9/2/06 11:54 Pgina 357
F
factor de carga, 50, 52, 54-56
first, vase conjunto first
flag, vase indicador
for, instruccin, vase instruccin iterativa
forma sentencial, 14-15, 20, 21, 90-91, 112,
175-176
FORTRAN, lenguaje de programacin, 26, 27,
339
frase, 3-4, 11, 14-15, 21
funcin
de precedencia, 180-183
de transicin, 68, 72, 74, 75
de transicin extendida, 72
hash, vase hash, funcin
G
garbage collection, vase recoleccin de basu-
ras
generador de cdigo, 29, 232, 243-285, 323-
324, 337-338
Gdel, Kurt, 1-2
GOTO, instruccin, vase instruccin GOTO
grafo
de estados y transiciones del autmata, 68,
135
de dependendencias, 224-225
de regiones de un programa, 309-310
de una expresin, 303
gramtica , 129
ambigua, 21-22, 23, 176-177
aumentada, 133-134, 138, 147, 152, 155
bien formada, 23-25
de atributos, 30, 192, 199, 203, 210-212,
214, 216-217, 221, 223, 227-228, 234
de estructura de frases, 16
de precedencia simple, 168-182
formal, 11
independiente del contexto, 17-18, 23, 24,
30, 65, 91, 100, 104, 116, 147, 155, 157,
191, 203, 210, 212, 214
limpia, 23-24, 90
lineal, 18
LL(1), 89, 93, 99-100, 103, 104, 107, 112
LALR(1), 89
LR(0), 89, 140
LR(1), 89, 158-159
reducida, 23-24
SLR(1), 89, 147, 158
tipo 0, 3-4, 16, 17, 18, 30, 191
tipo 1, 3-4, 17, 19
tipo 2, 4, 17-18, 19, 25, 30, 90
tipo 3, 4, 18, 19
transformacional, 3, 25
Greibach, forma normal de, 99-104, 106
H
hash
funcin , 45-46, 48-49, 55-56
tabla , 45-46, 50, 55, 58-61, 324
hoja, 20
I
identificador
activo, 57, 58
global, 58
local, 58, 314
if-then-else, instruccin, vase instruccin
condicional
indicador , 245
ndice, 192, 248, 267, 327, 330
informacin semntica, 76-77, 232, 255, 260,
324, 333
insercin, 56, 77
en tabla hash o de smbolos, 51, 59
Insertar, operacin de diccionario, 38-41, 44
instruccin
condicional, 230, 232, 254-256, 266, 268,
272-275, 310, 333
iterativa, 66, 233, 248, 256-258, 280, 314,
321, 322, 329, 344
GOTO, 252, 275-279
intrprete, 27-29, 30, 89, 317-326
interseccin, 11, 102, 103, 113
invariante, 7, 304, 306-307, 311-312
iteracin, vase clausura
358 Compiladores e intrpretes: teora y prctica
Indice alfabtico 9/2/06 11:54 Pgina 358
J
JAVA, lenguaje de programacin, 26, 28, 56,
317, 318, 320, 321, 322
L
LALR(1), vase gramtica o anlisis
Landis , vase bsqueda con rboles AVL
last, vase conjunto last
lenguaje
asociado a una gramtica, 14
de los parntesis, 211
de programacin, 2, 17, 25, 26, 30, 76, 191,
195, 201, 206, 211, 223, 227-230
dependiente del contexto, vase lenguaje
sensible al contexto
fuente, 27-28, 65, 73, 75, 77, 230, 248,
254, 285, 287, 318, 327, 347
independiente del contexto, 17-18, 26, 89,
116
intermedio, 28, 196, 199, 317, 318
objeto, 27, 29, 232, 248, 251, 285, 287, 302
regular, 18, 65
sensible al contexto, 4, 17
simblico, 26, 27, 195, 230, 232, 243, 285,
287, 317, 320
universal, 5, 7, 10, 11, 12
vaco, 7, 8, 9
lex, 78-85, 240
linker, vase programa enlazador
LISP, lenguaje de programacin, 26, 28, 56,
318, 322
lista, 55, 56, 60, 324, 352-353
LL(1), vase gramtica o anlisis
LOGO, lenguaje de programacin, 322
Look-Ahead-Left-to-Right , vase gramtica
LR, vase gramtica o anlisis
LR(0), vase gramtica o anlisis
LR(1), vase gramtica o anlisis
LR(k), vase gramtica o anlisis
M
main, vase programa main
mquina de Turing, vase Turing, mquina de
matriz Booleana, 170-171, 173-174, 182
memoria
dinmica, 56, 229, 319, 339, 345-353
esttica , 229, 231, 258, 302, 339-341
gestin de, 29, 196, 258, 259, 322, 324,
337-354
meta-carcter, 67, 78
monoide, 6-7, 8, 9
N
nodo, 19-21, 68, 181-182
notacin
de pares numricos, para configuraciones
de anlisis, 128
explcita, para configuraciones de anlisis,
128
infija, 258
prefija, 258, 259
sufija, 195, 196, 258, 259-266, 317
nmeros, teora de, 1
O
offset, vase desplazamiento
operador , 67, 252, 259, 268, 318, 319, 322
de comparacin, 255
didico, 254, 258, 265
mondico , 252, 259, 265, 302
operando, 229, 243-245, 247-254, 258-259,
266-268, 324
optimizacin, 195, 286
de bucles, 304
de regiones, 309-312
planificacin, 311
optimizacin de cdigo, 29
optimizador de cdigo, 29, 322
P
palabra, 3, 4, 5-7, 68, 93, 94, 97, 104, 110, 111
doble, 244
longitud de una, 5, 6, 7
refleja o inversa, 7
reservada, 65, 77, 80, 83, 332, 333, 339
ndice analtico 359
Indice alfabtico 9/2/06 11:54 Pgina 359
vaca, 5-10, 15, 17, 24, 67, 168-169
palndromo, 19
Pappert, Seymour, 322
parser, vase analizador sintctico
PASCAL, lenguaje de programacin, 26, 27,
56, 340, 346
paso
del anlisis, 57, 99, 113-114, 116, 118,
123, 127
de un compilador, 29, 58-60, 194, 196,
199, 221, 227-228, 258, 285
pila, 58-60, 113-114, 116-118, 120-123, 126,
141, 144, 178-180, 195, 217, 228, 231,
244-247, 258, 260-261, 268, 272, 274-
275, 302, 324, 337-339, 341-344,
vase tambin autmata a pila
plataforma, 247, 248, 253, 324
pop, 60, 119, 122-123, 228, 245, 265, 266,
273, 274, 339, 341
potencia, 7, 9-10, 169, 171, 173
primero_LR(1), conjunto, 155-156
problemas
no computables, 4
recursivamente enumerables, 4
procedimiento, 62, 192, 229, 233, 334
produccin, vase regla de produccin
programa enlazador, 243, 345
programa main, 80, 81, 82, 239-240, 328, 329,
330
PROLOG, lenguaje de programacin, 26, 28,
56, 318, 322
puntero, vase apuntador
puntero a la pila, 244, 246
push, 60, 119, 228, 244, 261, 264-266, 269-
274, 339
R
raz, 19, 21
recoleccin de basuras, 347-353
recorrido en profundidad por la izquierda con
vuelta atrs, 218
recuperacin de errores, 29, 77, 329-331
recursividad, 15, 176, 352
a izquierdas, 100
redimensionamiento, 55
reduccin, 29, 93
accin de anlisis, 115-117, 122-124, 128-
129, 131, 137, 141-142, 144-145, 147-
150, 154, 158, 218, 223, 227
de fuerza, 304-306, 311-313
redundancia, eliminacin de, 292-293
reflexin, 7, 10
regin, 309-313, 327
fuertemente conexa, 309
registro, 230-231, 286-287, 302
de activacin, 341-343
de segmentacin, 337-340
de trabajo, 246, 323
gestin de, 246-249
regla
de produccin, 11-12, 15, 16-18, 19, 30,
114-117, 120-122, 124, 128-129, 141,
148-150, 203, 209, 211, 214-215, 234,
237
de redenominacin, 23, 24, 211
innecesaria, 23
no generativa, 23, 24, 168, 169
recursiva, 15, 168, 169, 176, 212, 215
superflua, 23
regla-l, 101-102, 104-106, 113, 114, 210-211,
238
rehash, vase sondeo
relaciones
de precedencia, 175-176
teora de, 168, 169-174
reordenacin
de cdigo, 287
de operaciones, 300
S
salto, 230, 232
condicional, 246, 266
incondicional, 245, 255, 257, 258, 266,
276, 277
scanner, vase analizador morfolgico
semntica, 3, 29-30, 191
semigrupo, 6-7
360 Compiladores e intrpretes: teora y prctica
Indice alfabtico 9/2/06 11:54 Pgina 360
sentencia, 14, 16, 17, 20, 21, 22, 81
Shannon, Claude Elwood, 2
smbolo
de adelanto, 155
inaccesible, 23
no generativo, 23, 24
no terminal, 11-12, 14, 16, 20, 23-24, 90,
94, 99, 100, 102, 103, 104, 105, 111,
117, 128-130, 132, 141-142, 144, 145,
147, 152, 157, 160, 168, 194, 210-211,
215, 231-232, 235, 254, 255, 260, 332
terminal, 11, 14, 20, 21, 23, 28, 94, 99, 102,
103, 111, 114, 117, 130-131, 142, 144,
145, 152, 160, 168, 211, 235, 237, 253,
260, 268, 332, 333
sintaxis, 3, 17, 25-26, 29-30, 89, 191, 243,
253, 259, 327, 328
Smalltalk, lenguaje de programacin, 26, 28,
259, 318, 320, 322
SNOBOL, lenguaje de programacin, 318,
322
sondeo, 50, 51, 52, 55
aleatorio, 54
cuadrtico, 54
lineal , 52
multipicativo, 53
subrbol, 21
submeta, 94-97
subrutina, 29, 56, 58, 229, 233, 243, 246, 254,
320, 337, 338, 339, 341-345, 346, 348
SLR(1), vase gramtica o anlisis
T
tabla
de anlisis, 227
de anlisis ascendente, 118-119, 122-123,
125, 137-138, 140, 145-149, 158, 167
de referencias, 324-325, 348
de smbolos o identificadores, 28, 30, 33,
56, 58-59, 61-62, 202, 196, 206-208,
217, 230-233, 289, 293, 320, 322, 324-
325, 327, 328, 330, 331, 332, 339, 340,
347, 348
hash, vase hash, tabla
terminal, vase smbolo terminal
tipo, de dato, 62, 192, 194, 196, 199-203, 206-
207, 209, 223, 230-233, 288
Thue, relacin de , 14
transicin, 68, 72, 73, 75, 76
diagrama de, 68, 69, 141, 143-145, 147-
149, 156-157, 160-161
extendida, funcin de, vase funcin de
transicin extendida
funcin de, vase funcin de transicin
triplete, 259, 267-268
indirecto, 267-268
tupla, 195, 259
Turing, Alan Mathison, 1-2, 4
Turing, mquina de, 1, 2, 4, 30
U
unidad sintctica, 28, 65, 75, 76, 77, 80, 81,
82, 83, 196, 217, 240, 253, 255, 327,
333
unin, 8, 9, 67, 68, 78, 170, 173
V
variable 57
automtica, 341-345
esttica, 338, 339-341
intermedia, 292-293, 311-312
de bucle, 305-308
vector, 55, 247, 323, 324, 327, 328, 340, 349-
352
W
while, instruccin, vase instruccin iterativa
Y
yacc, 168, 234, 235, 238, 239, 240
ndice analtico 361
Indice alfabtico 9/2/06 11:54 Pgina 361