You are on page 1of 61

Apuntes de Compiladores I

Compiladores I
Introduccin
Desde el punto de vista de un informtico, prcticamente todas las acciones que se va a ver
obligado a desarrollar en el transcurso de su carrera profesional, tendr que ver con traductores: la
programacin, la creacin de ficheros batch, la utilizacin de un intrprete de comando, etc.

Por ejemplo Que ocurre si nos dan un documento de Word que procede de una fusin con una base de
datos y se quiere, a partir de l, obtener la B.D. original?. Pues se puede:
a) Convertirla a texto.
b) Procesarla con un traductor para quitar el texto superfluo y dar como resultado un texto en el
que cada campo est entre comillas.
c) El texto anterior se importa con cualquier SGBD.
Otro ejemplo
Creacin de preprocesadores para lenguajes que no lo tienen . Por ejemplo para trabajar fcilmente con
SQL en C, se puede hacer un preprocesador para meter SQL inmerso.

Qu es un traductor?
Un traductor es un programa que traduce o convierte desde un texto o programa escrito en un lenguaje
fuente hasta un texto o programa escrito en un lenguaje destino produciendo, si cabe, mensajes de error.
* Los traductores engloban tanto al compilador como al intrprete.
* Esquema inicial para un traductor

Programa Fuente
escrito en Lenguaje
Fuente

TRADUCTORES

Programa Destino
escrito en Lenguaje
Destino

Mensajes de Error

* Es importante destacar la velocidad en la que hoy en da se hacen. En la dcada de 1950, se consider a


los traductores como programas notablemente difciles de escribir.
El primer compilador de FORTRAN, por ejemplo, necesit para su implementacin 18 aos de trabajo en
grupo. Hasta que apareci la teora de autmatas no se pudo acelerar ni formalizar la creacin de
traductores.

Tipos de Traductores
Traductores del idioma : Traducen de un idioma dado a otro, por ejemplo, un traductor de Ingls a
Espaol..
* Problemas:
Inteligencia Artificial y problemas de las frases hechas: El problema de la inteligencia artificial es que tiene
mucho de artificial y poco de inteligencia. Por ejemplo una vez se tradujo del Ingles al Ruso (por lo de la
guerra fra) : El espritu es fuerte pero la carne es dbil que, de nuevo, se pas al Ingls, y dio: El vino
est bueno pero la carne est podrida ( En ingls spirit significa tanto espritu como alcohol ).
Falta de formalizacin en la especificacin del significado de las palabras.
Cambio del sentido de las palabras segn el contexto. Ej: Por decir aquello, se llev una galleta.
Slo un subconjunto del lenguaje.

Preparado por Prof. Julio Surez

Apuntes de Compiladores I

Compiladores : Es aquel traductor que tiene como entrada una sentencia en lenguaje formal y como salida
tiene un fichero ejecutable, es decir, hace una traduccin de alto nivel a cdigo mquina.
Intrpretes : Es como un compilador, solo que la salida es una ejecucin. El programa de entrada se
interpreta y ejecuta a la vez.
* Hay lenguajes que solo pueden ser interpretados.
Ej: SNOBOL (StriNg Oriented SimBOlyc Language),
LISP (LISt Processing)
BASIC (Beginners All ...)
La principal ventaja es que permiten una fcil depuracin. Los inconvenientes son, en primer lugar la lentitud
de ejecucin , ya que si uno ejecuta a la vez que traduce no puede aplicarse mucha optimizacin, adems si
el programa entra en un bucle tiene que interpretar y ejecutar todas las veces que se realice el bucle. Otro
inconveniente es que durante la ejecucin, es necesario el intrprete en memoria por lo que consumen ms
recursos.
Preprocesadores : Permiten modificar el programa fuente antes de la verdadera compilacin. Hacen uso
de macroinstrucciones y directivas.
Ej:

//Uno.c
#include Dos.c
Void main( ) {
xxxx
xxxxxxx
}

PREPROCESADOR

//Dos.c
yyy
yyyy

//Uno.c
yyy
yyyy
void main( ) {
xxxx
xxxxxxx
}

COMPILADOR

El preprocesador sustituye la instruccin #include Uno.c por el cdigo que tiene Uno.c, cuando el
compilador empieza se encuentra con el cdigo ya incluido en el programa fuente.
Ejemplos de algunas directivas de procesador (Clipper, C): #fi, #ifdef, #define, #ifndef, #define, #include ...
que permiten compilar trozos de cdigos opcionales.

Intrpretes de comandos : Lo que hace es traducir sentencias simples a llamadas a programas de una
biblioteca. Son especialmente utilizados por Sistemas Operativos.
Ej: El shell del DOS o del UNIX. Desencadenan la ejecucin de programas que pueden estar residentes en
memoria o encontrarse en disco.
Por ejemplo, si ponemos en MS-DOS el comando copy se ejecuta la funcin copy del
sistema operativo.
Ensambladores y Macroensambladores : Son los pioneros de los compiladores, ya que en los albores de
la informtica, los programas se escriban directamente en cdigo mquina, y los ensambladores establecen
una relacin biunvoca entre cada instruccin y una palabra nemotcnica, de manera que el usuario escribe
los programas haciendo uso de los mnemotcnicos, y el ensamblador se encarga de traducirlo al cdigo
mquina puro.
Preparado por Prof. Julio Surez

Apuntes de Compiladores I

El lenguaje que utiliza se llama lenguaje ensamblador y tiene una correspondencia uno a uno entre sus
instrucciones y el cdigo mquina.
Ej: Cdigo mquina 65h.00h.01h
Ensamblador LD HL, #0100
- Macroensamblador : Hay ensambladores que tienen macroinstrucciones que se suelen traducir a varias
instrucciones mquinas, pues bien, un macroensamblador es un ensamblador con un preprocesador
delante.
Conversores fuente - fuente : Pasan un lenguaje de alto nivel a otro lenguaje de alto nivel, para conseguir
mayor portabilidad.
Por ejemplo en un ordenador slo hay un compilador de PASCAL, y queremos ejecutar un programa escrito
en COBOL; Un conversor COBOL > PASCAL nos solucionara el problema.
Compilador cruzado : Es un compilador que obtiene cdigo para ejecutar en otra mquina. Se utilizan en la
fase de desarrollo de nuevos ordenadores.

Otros Conceptos Referido a Traductores


Compilar-linkar-ejecuta : Estas son las tres fases bsicas de un computador. Nosotros
nos centraremos en la primera fase a lo largo de la asignatura.
* El compilador obtiene un cdigo objeto, junto con una tabla de smbolos.

Archivo.fue

Archivo.obj

COMPILAR

* Porqu no hace directamente un fichero ejecutable?


Para permitir la compilacin separada, de manera que puedan fusionarse diversos ficheros OBJ en un solo
ejecutable.
* Un fichero OBJ es un fichero que posee una estructura de registros. Estos registros tienen longitudes
diferentes. Unos de estos registros tienen cdigo mquina, otros registros van a tener informacin. Tambin
incluye informacin sobre los objetos
externos. P.ej: Variables que estn en otros ficheros declaradas (EXTERN)

* El enlazador resuelve las referencias cruzadas, o externas, que pueden estar o en otros OBJ, o en
libreras LIB, y se encarga de generar el ejecutable final.
* Se obtiene un cdigo reubicable, es decir, un cdigo que en su momento se podr ejecutar en diferentes
posiciones de memoria, segn la situacin de la misma en el momento de la ejecucin.

Pasadas de compilacin : Es el nmero de veces que se lee el programa fuente. Hay algunas situaciones
en las que, para realizar la compilacin, no es suficiente con leer el fichero fuente una sola vez. Por ejemplo:
Que ocurre si tenemos una recursin indirecta?
A llama a B
B llama a A
Preparado por Prof. Julio Surez

Apuntes de Compiladores I

Cuando se lee el cuerpo de A, no se sabe si B va a existir o no, y no se sabe su direccin de comienzo,


luego en una pasada posterior hay que rellenar estos datos.
* Para solucionar el problema
1.- Hacer dos pasadas de compilacin.
2.- Hacer una sola pasada de compilacin utilizando la palabra reservada
FORWARD.
FORWARD B( )
A( )
* Algunos compiladores dan por implcito el FORWARD. Si no encuentra aquello a que se hace referencia,
continan, esperando que el linkador resuelva el problema, o emita el mensaje de error.
Compilacin incremental: Es aquella que compila un programa en el que si despus se descubren errores,
en vez de corregir el programa fuente y compilarlo por completo, se compilan solo las modificaciones. Lo
ideal es que solo se recompilen aquellas partes que contenan los errores, y que el cdigo generado se
reinserte con cuidado en el OBJ generado cuando se encontraron los errores. Sin embargo esto es muy
difcil.

Autocompilador: Es un compilador escrito en el mismo lenguaje que compila.


* Cuando se extiende entre muchas mquinas diferentes el uso de un compilador, y ste se desea mejorar,
el nuevo compilador se escribe con el antiguo, de manera que pueda ser compilado por todas esas
mquinas diferentes, y d como resultado un compilador ms potente de ese mismo lenguaje.

Metacompilador: Es un programa que acepta la descripcin de un lenguaje y obtiene el compilador de


dicho lenguaje, es decir, acepta como entrada una gramtica de un lenguaje y genera un autmata que
reconoce cualquier sentencia del lenguaje . A este autmata podemos aadirle cdigo para realizar el
compilador.
* Por ejemplo LEX y YACC, FLEX, Bison, JavaCC, PCCTS, MEDISE, etc.
* Unos metacompiladores pueden trabajar con gramticas de contexto libre y otros trabajan con gramtica
regular. Los que trabajan con gramticas de contexto libre se dedican a reconocer la sintaxis del lenguaje y
los de gramtica regular trocean la entrada y la dividen en palabras.
* El PCLEX es un metacompilador cuya funcin es generar un programa que es la parte del compilador que
reconoce las palabras reservadas y otros componentes lxicos.
* El PCYACC es un metacompilador cuya funcin es generar un programa que es la parte del compilador
que indica si una sentencia del lenguaje es vlida o no (anlisis sintctico).
Descompilador: Pasa de un cdigo mquina (o programa de salida) al lenguaje que lo gener ( o programa
fuente). Cada descompilador trabaja con un lenguaje de alto nivel concreto.
* Es una operacin casi imposible, porque al cdigo mquina casi siempre se le aplica una optimizacin. Por
eso lo que hay suelen ser desensambladores, ya que existe una biseccin entre cada instruccin mquina y
cada instruccin ensamblador.
* Se utilizan especialmente cuando el cdigo mquina ha sido generado con opciones de depuracin, y
contiene informacin adicional de ayuda a la depuracin de errores ( puntos de ruptura, opciones de
visualizacin de variables, etc)
Tambin se emplea cuando el compilador original no gener cdigo mquina puro, sino pseudocdigo (para
ejecutarlo a travs de un pseudointrprete)

Preparado por Prof. Julio Surez

Apuntes de Compiladores I

Estructura de un Compilador
Un compilador se divide en dos fases : Una parte que analiza la entrada y genera estructuras intermedias y
otra parte que sintetiza la salida. En base a tales estructuras intermedias

El esquema de traductor es ahora

Fuente

ANLISIS

SNTESIS

Destino

Mensajes de Error

Bsicamente los objetivos de la fase de Anlisis son:


* Controlar la correccin del programa fuente
* Generar estructuras necesarias para comenzar la sntesis.
Para llevar esto a cabo el Anlisis consta de las siguientes tareas:
* Anlisis Lexicogrfico : Divide el programa fuente en los componentes bsicos:
nmeros, identificadores de usuario (variables, constantes, tipos, nombres de procedimientos,...), palabras
reservadas, signos de puntuacin. A cada componente le asocia la categora a la que pertenece.
* Anlisis Sintctico : Comprueba que la estructura de los componentes bsicos sea correcta segn
ciertas reglas gramaticales.
* Anlisis semntico : Comprueba todo lo dems posible, es decir ,todo lo relacionado con el significado,
chequeo de tipos, rangos de valores, existencia de variables, etc.
* En cualquiera de los tres anlisis puede haber errores.

El objetivo de la fase de sntesis consiste en:


* Construir el programa objeto deseado a partir de las estructuras generadas por la fase de anlisis. Para
ello realiza tres tareas fundamentales.
* Generacin de cdigo intermedio : Genera un cdigo independiente de la mquina.
Ventajas, es fcil hacer seudo compiladores y adems facilita la optimizacin de cdigo.
* Generacin del cdigo mquina : Crea un fichero .exe directamente o un fichero .obj. Aqu tambin
se puede hacer optimizacin propia del microprocesador.

Preparado por Prof. Julio Surez

Apuntes de Compiladores I

* Fase de optimizacin: La optimizacin puede realizarse durante las fases de generacin de cdigo
intermedio y/o generacin de cdigo mquina y puede ser una fase aislada de stas, o estar integrada con
ellas.
La optimizacin del cdigo intermedio debe ser independiente de la mquina.
El siguiente cuadro muestra un ejemplo de compilacin de una sentencia de asignacin, que incluye una
expresin aritmtica:

TRADUCCION DE UNA PROPOSICION


posicion = inicial + velocidad * 60

Analizador sintctico

id1

=
*
id3

temp1 = id3 * 60.0


id1 = id2 + temp1

Analizador
Semntico

+
id2

Generador de
Cdigo intermedio

id1=id2 + id3 * num

Analizador lxico

60

temp1= entareal (60)


temp2 = id3 * temp1
temp3 = id2 + temp2
id1 = temp3

Generador de Cdigo

Optimizador de Cdigo
MOVF id3, R2
MULF #60.0, R2
MOVF id2, R1
ADDF R2, R1
MOVF R1, id1

En este esquema, se supone que el compilador realiza todas las tareas listadas:
La entrada fuente es la sentencia posicion = inicial + velocidad * 60
El anlisis lxico separa la sentencia en sus componentes lxicos: id: es un terminal, en una gramtica libre
de contexto, que representa a cualquier nombre o identificador de variable de memoria, presente en el
programa fuente. num: es un terminal, de la misma gramtica, que representa a un nmero entero.
La salida del anlisis lxico ser la entrada para el anlisis sintctico.

Con frecuencia, las fases se agrupan en una etapa inicial (Front-End) y una etapa final (Back- End). La
etapa inicial comprende aquellas fases, o partes de fases que dependen principalmente del lenguaje fuente
y que son en gran parte independientes de la mquina objeto. Ah normalmente se introducen los anlisis
lxicos y sintcticos, la creacin de la tabla de smbolos, el anlisis semntico y la generacin de cdigo
intermedio. La etapa inicial tambin puede hacer cierta optimizacin de cdigo e incluye adems, el manejo
de errores correspondiente a cada una de esas fases.
La etapa final incluye aquellas partes del compilador que dependen de la mquina objeto y, en
general, esas partes no dependen del lenguaje fuente, sino slo del lenguaje intermedio. En la etapa final,
se encuentran aspectos de la fase de optimizacin de cdigo adems de la generacin de cdigo, junto con
el manejo de errores necesario y las operaciones con la tabla de smbolos.
Se ha convertido en rutina el toma r la etapa inicial de un compilador y rehacer su etapa final
asociada para producir un compilador para el mismo lenguaje fuente en una mquina distinta. Tambin
resulta tentador compilar varios lenguajes distintos en el mismo lenguaje intermedio y usar una etapa final
Preparado por Prof. Julio Surez

Apuntes de Compiladores I

comn para las distintas etapas iniciales, obtenindose as varios compiladores para una mquina. Veamos
ejemplos:

Una funcin esencial de un compilador es registrar los identificadores utilizados en el programa


fuente y reunir informacin sobre los distintos atributos de cada identificador.
Preparado por Prof. Julio Surez

Apuntes de Compiladores I

Estos atributos pueden proporcionar informacin sobre la memoria asignada a un identificador, su


tipo, su mbito (la parte del programa donde tiene validez),...
Tabla de smbolos : Posee informacin sobre los identificadores definidos por el usuario, ya sean
constantes, variables o tipos. Dado que puede contener informacin de diversa ndole, debe hacerse de
forma que no sea uniforme. Hace funciones de diccionario de datos y su estructura puede ser una tabla
hash, un rbol binario de bsqueda, etc.

Esquema definitivo de un traductor

Fuente

ANLISIS

SNTESIS

Mensajes de Error

Tabla de
Smbolos

Destino

Ejercicios
Captulo de Introduccin

Trabaja individualmente, y luego en forma grupal para completar el siguiente ejercitario.


1) Completa las definiciones de los siguientes trminos
Traductores:

Compiladores:

Interpretes:

2) Menciona otros tipos de traductores y haz una breve explicacin de cada uno.

3) Escribe ejemplos conocidos de los traductores mencionados en el item 2


Preparado por Prof. Julio Surez

Apuntes de Compiladores I

4) Explica el concepto de compilacin incremental, con un programa fuente escrito en un hipottico


lenguaje Alfa.

5) Grafica estructuralmente las fases de un proceso de compilacin, incluyendo una breve explicacin
al costado de cada fase.

6) Las fases de un compilador pueden agruparse en dos etapas de construccin:


a). y b)
La diferencia entre ambas etapas se refiere a que la etapa de .., tiene en cuenta los
aspectos ..
....y
la
etapa
....,
se
basa
en
los
aspectos,.
..
..

7) Si el siguiente grfico se refiere a las etapas constructivas del compilador de un lenguaje de


programacin, explica su significado.
Front-End 1

Front-End 2

Front-End 3

Preparado por Prof. Julio Surez

Back-End 1

Back-End 2

Back-End 3

Apuntes de Compiladores I

Interpretacin del grfico:

8) Diferencia entre cdigo fuente y cdigo objeto.

9) En todos los casos los cdigos objetos se encuentran en lenguaje de mquina. Justifica.

10) Qu utilidad ofrece la utilizacin de la tabla de smbolos en un proceso de compilacin?

2. Anlisis Lxico
Este captulo estudia la primera fase de un compilador, es decir su anlisis lexicogrfico, o ms
concisamente anlisis lxico. Las tcnicas utilizadas para construir analizadores lxicos tambin se pueden
aplicar a otras reas, como, por ejemplo, a lenguajes de consulta y sistemas de recuperacin de
informacin. En cada aplicacin, el problema de fondo es la especificacin y diseo de programas que
ejecuten las acciones activadas por palabras que siguen ciertos patrones dentro de las cadenas a
reconocer. Como la programacin dirigida por patrones es de mucha utilidad, se introduce un lenguaje de
patrn-accin, llamado LEX, para especificar los analizadores lxicos. En este lenguaje, los patrones se
especifican por medio de expresiones regulares, y un compilador de LEX puede generar un reconocedor de
las expresiones regulares mediante un autmata finito eficiente.

Que es un analizador lxico?


Se encarga de buscar los componentes lxicos o palabras que componen el programa fuente,
segn unas reglas o patrones.
La entrada del analizador lxico podemos definirla como una secuencia de caracteres.

Preparado por Prof. Julio Surez

10

Apuntes de Compiladores I

El analizador lxico tiene que dividir la secuencia de caracteres en palabras con significado propio y
despus convertirlo a una secuencia de terminales desde el punto de vista del analizador sintctico, que es
la entrada del analizador sintctico.
El analizador lxico reconoce las palabras en funcin de una gramtica regular de manera que sus
SENTENCIAS se convierten en los elementos de entrada de fases posteriores.

2.1 Funciones del analizador lxico


El analizador lxico es la primera fase de un compilador. Su principal funcin consiste en leer los
caracteres de entrada y elaborar como salida una secuencia de componentes lxicos que utiliza el
analizador sintctico para hacer el anlisis. Esta interaccin, suele aplicarse convirtiendo al analizador lxico
en una subrutina o co-rutina del analizador sintctico. Recibida la orden Dame el siguiente componente
lxico del analizador sintctico, el analizador lxico lee los caracteres de entrada hasta que pueda
identificar el siguiente componente lxico.

Fig. Interaccin de la fase de Anlisis Lxico con el Analizador Sintctico


Otras funciones:
Como parte de la funcin de explorar el programa fuente, carcter por carcter, en esta fase
tambin es posible:
Eliminar los comentarios del programa.
Eliminar espacios en blanco, tabuladores, retorno de carro, etc, y en general, todo aquello que carezca de
significado segn la sintaxis del lenguaje.
Reconocer los identificadores de usuario, nmeros, palabras reservadas del lenguaje, ..., y tratarlos
correctamente con respecto a la tabla de smbolos (solo en los casos que debe de tratar con la tabla de
smbolos).
Llevar la cuenta del nmero de lnea por la que va leyendo, por si se produce algn error, dar informacin
sobre donde se ha producido.
Preparado por Prof. Julio Surez

11

Apuntes de Compiladores I

Avisar de errores lxicos. Por ejemplo, si @ no pertenece al lenguaje, avisar de un error.


Puede hacer funciones de pre-procesador.
2.2 Necesidad del Analizador Lxico
Un tema importante es el porqu se separan los dos anlisis lexicogrfico y sintctico, en vez de
realizar slo el anlisis sintctico, del programa fuente, cosa perfectamente posible aunque no plausible.
Algunas razones de esta separacin son:

Un diseo sencillo es quizs la consideracin ms importante. Separar el anlisis lxico del


anlisis sintctico a menudo permite simplificar una u otra de dichas fases. El analizador lxico nos permite
simplificar el analizador sintctico.
Se mejora la eficiencia del compilador. Un analizador lxico independiente permite construir un
procesador especializado y potencialmente ms eficiente para esa funcin.
Gran parte del tiempo se consume en leer el programa fuente y dividirlo en componentes lxicos.
Con tcnicas especializadas de manejo de buffers para la lectura de caracteres de entrada y procesamiento
de componentes lxicos se puede mejorar significativamente el rendimiento de un compilador.
Se mejora la portabilidad del compilador. Las peculiaridades del alfabeto de entrada y otras
anomalas propias de los dispositivos pueden limitarse al analizador lxico. La representacin de smbolos
especiales o no estndares, como _ en Pascal, pueden ser aisladas en el analizador lxico.
Otra razn por la que se separan los dos anlisis es para que el analizador lxico se centre en el
reconocimiento de componentes bsicos complejos. Por ejemplo en FORTRAN, existen el siguiente par de
proposiciones:
DO 5 I = 2.5 (Asignacin de 2.5 a la variable DO5I)
DO 5 I = 2,5 (Bucle que se repite para I = 2, 3, 4, 5)
En ste lenguaje los espacios en blancos no son significativos fuera de los comentarios y de un
cierto tipo de cadenas, de modo que supngase que todos los espacios en blanco eliminables se suprimen
antes de comenzar el anlisis lxico. En tal caso, las proposiciones anteriores apareceran al analizador
lxico como
DO5I = 2.5
DO5I = 2,5
El analizador lxico no sabe si DO es una palabra reservada o es el prefijo de una variable hasta
que llegue a la coma. El analizador ha tenido que mirar ms all de la propia palabra a reconocer haciendo
lo que se denomina lookahead (o prebsqueda).

Componentes lxicos, patrones y lexemas


Cuando se menciona el anlisis sintctico, los trminos componente lxico(token), patrn y lexema se
emplean con significados especficos. En el cuadro de abajo, aparecen ejemplos de dichos usos. En
general, hay un conjunto de cadenas en la entrada para el cual se produce como salida el mismo
componente lxico. Este conjunto de cadenas se describe mediante una regla llamada patrn asociado al
componente lxico. Se dice que el patrn concuerda con cada cadena del conjunto. Un lexema es una
secuencia de caracteres en el programa fuente con la que concuerda el patrn para un componente lxico.
Por ejemplo, en la proposicin de Pascal
const pi = 3.1416;
La subcadena pi es un lexema para el componente lxico identificador.

Componente Lxico

Lexemas de ejemplo

Descripcin Informal del patrn

const

const

const palabra reservada

Preparado por Prof. Julio Surez

12

Apuntes de Compiladores I

if

if

if palabra reservada

relacin

<,<=,=,<>,>, >=

< o <= o = o <> o > o >=

id

pi, cuenta, D2

Letra seguida de letras y digitos

num

3.1416, 0 , 6.02E23

Cualquier constante numrica

literal

vaciado de pila

Cualquier carcter entre y ,


excepto

Los componentes lxicos se tratan como smbolos terminales de la gramtica del lenguaje fuente.
Los lexemas para el componente lxico que concuerdan con el patrn representan cadenas de caracteres
en el programa fuente que se pueden tratar juntos como una unidad lxica.
En la mayora de los lenguajes de programacin, se consideran componentes lxicos las siguientes
construcciones: palabras clave, operadores, identificadores, constantes, cadenas literales y signos de
puntuacin, como parntesis, coma y punto y coma.
Un patrn es una regla que describe el conjunto de lexemas que pueden representar a un
determinado componente lxico en los programas fuente. El patrn para el componente lxico const, de la
tabla anterior, es simplemente la cadena sencilla const. El patrn para el componente lxico relacin es el
conjunto de los seis operadores relacionales de Pascal. Para describir con precisin los patrones para
componentes lxicos ms complejos, como id (para identificador) y num(para nmero), se utilizar la
notacin de expresiones regulares.

2.3 Atributos de los componentes lxicos


Cuando concuerda con un lexema ms de un patrn, el analizador lxico debe proporcionar
informacin adicional sobre el lexema concreto que concord con las siguientes fases del compilador. Por
ejemplo, el patrn nm concuerda con las cadenas 0 y 1, pero es indispensable que el generador de cdigo
conozca qu cadena fue realmente la que se emparej.
El analizador lxico recoge informacin sobre los componentes lxicos en sus atributos asociados.
Los componentes lxicos influyen en las decisiones del anlisis sintctico, y los atributos, en la traduccin
de los componentes lxicos.
Ejemplo. Los componentes lxicos y los valores de atributos asociados para la proposicin de FORTRAN.
E = M * C ** 2
Se escriben a continuacin como una secuencia de parejas:

<id, apuntador a la entrada de la tabla de smbolos para E>


<op_asign>
<id, apuntador a la entrada de la tabla de smbolos para M>
<op_multip>
< id, apuntador a la entrada de la tabla de smbolos para C>
<op_exp>
<num, valo entero 2>

2.4 Errores lxicos

Preparado por Prof. Julio Surez

13

Apuntes de Compiladores I

Son pocos los errores que se pueden detectar simplemente en el nivel lxico porque un analizador
lxico tiene una visin muy restringida de un programa fuente. Si aparece la cadena fi por primera vez en un
programa C en el contexto
fi ( a == f(x))
un analizador lxico no puede distinguir si fi es un error de escritura de la palabra clave if o si es un
identificador de funcin no declarado. Como fi es un identificador vlido, el analizador lxico debe devolver
el componente lxico de un identificador y dejar que alguna otra fase del compilador se ocupe de los
errores.
Pero, supngase que surge una situacin en la que el analizador lxico no puede continuar porque
ninguno de los patrones concuerda con un prefijo de la entrada restante.

2.5 Un lenguaje para la especificacin de Analizadores Lexicos


Se han desarrollado algunas herramientas para construir analizadores lxicos a partir de notaciones
de propsito especial basadas en expresiones regulares. Ya se ha estudiado el uso de expresiones
regulares en la especificacin de patrones de componentes lxicos. Antes de considerar los algoritmos para
compilar expresiones regulares en programas de concordancia de patrones, se da un ejemplo de una
herramienta que pueda ser utilizada por dicho algoritmo.
En esta seccin se describe una herramienta concreta, llamada LEX, muy utilizada en la
especificacin de analizadores lxicos para varios lenguajes. Esa herramienta se denomina compilador
LEX, y la especificacin de su entrada, lenguaje LEX.

Esquema para creacin de un analizador lxico con LEX


Programa fuente en LEX
Prog1.l

Compilador

prog1. c

de LEX

prog1.c

Archivo de entrada

Compilador de C

prog1.exe

prog1.exe

Secuencia de componentes lxicos

Especificaciones en LEX

Un programa en LEX consta de tres partes:


declaraciones
%%
reglas de traduccin
%%
procedimientos auxiliares

Preparado por Prof. Julio Surez

14

Apuntes de Compiladores I

La seccin de declaraciones incluye declaraciones de variables, constantes manifiestas y definiciones


regulares ( Una constante manifiesta es un identificador que se declara para representar una constante).
Las reglas de traduccin de un programa en LEX son proposiciones de la forma
p1

{accin 1}

p2

{accin 2}

pn

{accin n}

donde pi es una expresin regular y cada accin es un fragmento de programa que describe cul ha de ser
la accin del analizador lxico cuando el patrn pi concuerda con un lexema. En LEX, las acciones se
esriben en C, en general, sin embargo, pueden estar en cualquier lenguaje de implantacin.
La tercera seccin contiene todos los procedimientos auxiliares que puedan necesitar las acciones. A veces,
estos procedimientos se pueden compilar por separado y cargar con el analizador lxico.
Un analizador lxico creado por LEX se comporta en sincrona con un analizador sintctico como sigue.
Cuando es activado por el analizador sintctico, el analizador lxico comienza a leer su entrada restante, un
carcter a la vez, hasta que encuentre el mayor prefijo de la entrada que concuerde con una de las
expresiones regulares pi. Entonces, ejecuta accin i. Generalmente, accin i devolver el control al
analizador sintctico. Sin embargo, si no lo hace, el analizador lxico se dispone a encontrar ms lexemas,
hasta que una accin hace que el control regrese al analizador sintctico. La bsqueda repetida de lexemas
hasta encontrar una instruccin return explcita permite al analizador lxico procesar espacios en blanco y
comentarios de manera apropiada.
El analizador lxico devuelve una nica cantidad, el componente lxico, al analizador sintctico. Para pasar
un valor de atributo con la informacin del lexema, se puede asignar una variable global llamada yylval.

Expresiones del lex


Una expresin especifica un conjunto de literales que se van a comparar.
Esta contiene caracteres de texto (que coinciden con los caracteres correspondientes del literal que
se est comparando) y caracteres operador (estos especifican repeticiones, selecciones, y otras
caractersticas). Las letras del alfabeto y los dgitos son siempre caracteres de texto. Por lo tanto, la
expresin integer coincide con el literal integer siempre que ste aparezca y la expresin
a57d
busca el literal a57d.
Los caracteres operadores son:
\[]^-?.*+|()$/{}%<>
Si cualquiera de estos caracteres se va a usar literalmente, es necesario incluirlos individualmente
entre caracteres barra invertida ( \ ) o como un grupo dentro de comillas ( ).
El operador comillas ( ) indica que siempre que est incluido dentro de un par de comillas se va a
tomar como un carcter de texto. Por lo tanto
xyz++
coincide con el literal xyz++ cuando aparezca. Ntese que una parte del literal puede estar entre
comillas. No produce ningn efecto y es innecesario poner entre comillas caracteres de texto normal; la
expresin
xyz++
es la misma que la anterior. Por lo tanto poniendo entre comillas cada carcter no alfanumrico que se est
usando como carcter de texto, no es necesario memorizar la lista anterior de caracteres operador.
Un carcter operador tambin se puede convertir en un carcter de texto ponindole delante una barra
invertida ( \ ) como en
xyz\+\+
el cual, aunque menos legible, es otro equivalente de las expresiones anteriores.
Preparado por Prof. Julio Surez

15

Apuntes de Compiladores I

Este mecanismo tambin se puede usar para incluir un espacio en blanco dentro de una expresin;
normalmente, segn se explicaba anteriormente, los espacios en blanco y los tabuladores terminan una
orden. Cualquier carcter en blanco que no est contenido entre corchete tiene que ponerse entre comillas.
Se reconocen varios escapes C normales con la barra invertida ( \ ):
\ n newline
\ t tabulador
\ b backspace
\ \ barra invertida
Puesto que el carcter newline es ilegal en una expresin, es necesario usar n; no se requiere dar escape al
carcter tabulador y el backspace. Cada carcter excepto el espacio en blanco, el tabulador y el newline y la
lista anterior es siempre un carcter de texto.

Especificacin de clases de caracteres.


Las clases de caracteres se pueden especificar usando corchetes: [y]. La construccin
[ abc ]
coincide con cualquier carcter, que pueda ser una a, b, o c. Dentro de los corchetes, la mayora de
los significados de los operadores se ignoran. Slo tres caracteres son especiales: stos son la barra
invertida ( \ ), el guin ( - ), y el signo de intercalacin ( ^ ). El carcter guin indica rangos, por ejemplo
[ a-z0-9<>_ ]
indica la clase de carcter que contiene todos los caracteres en minsculas, los dgitos, los ngulos
y el subrayado. Los rangos se pueden especificar en cualquier orden. Usando el guin entre cualquier par
de caracteres que ambos no sean letras maysculas, letras minsculas, o dgitos, depende de la
implementacin y produce un mensaje de aviso. Si se desea incluir el guin en una clase de
caracteres, ste deber ser el primero o el ltimo; por lo tanto
[ -+0-9 ]
coincide con todos los dgitos y los signos ms y menos.
En las clases de caracteres, el operador ( ^ ) debe aparecer como el primer carcter despus del
corchete izquierdo; esto indica que el literal resultante va a ser complementado con respecto al conjunto de
caracteres del ordenador. Por lo tanto
[ ^abc ]
coincide con todos los caracteres excepto a, b, o c, incluyendo todos los caracteres especiales o de
control; o [ ^a-zA-Z ]
es cualquier carcter que no sea una letra. El carcter barra invertida ( \ ) proporciona un
mecanismo de escape dentro de los corchete de clases de caracteres, de forma que stos se pueden
introducir literalmente precedindolos con este carcter.
Especificar expresiones opcionales.
El operador signo de interrogacin ( ? ) indica un elemento opcional de una expresin. Por lo tanto
ab?c coincide o con ac o con abc. Ntese que aqu el significado del signo de interrogacin difiere
de su significado en la shell.
Especificacin de expresiones repetidas.
Las repeticiones de clases se indican con los operadores asterisco ( * ) y el signo ms ( + ). Por
ejemplo
a*
coincide con cualquier nmero de caracteres consecutivos, incluyendo cero; mientras que a+
coincide con una o ms apariciones de a. Por ejemplo,
[ a-z ]+
coincide con todos los literales de letras minsculas, y
[ A-Za-z ] [A-Za-z0-9 ]*
coincide con todos los literales alfanumricos con un carcter alfabtico al principio; sta es una
expresin tpica para reconocer identificadores en lenguajes informticos.
Especificacin de alternacin y de agrupamiento.
El operador barra vertical ( | ) indica alternacin. Por ejemplo
( ab|cd )

Preparado por Prof. Julio Surez

16

Apuntes de Compiladores I

coincide con ab o con cd. Ntese que los parntesis se usan para agrupar, aunque stos no son
necesarios en el nivel exterior. Por ejemplo
ab | cd
hubiese sido suficiente en el ejemplo anterior. Los parntesis se debern usar para expresiones
ms complejas, tales como
( ab | cd+ )?( ef )*
la cual coincide con tales literales como abefef, efefef, cdef, cddd, pero no abc, abcd, o abcdef.
Especificacin de sensitividad de contexto
El lex reconoce una pequea cantidad del contexto que le rodea. Los dos operadores ms simples
para stos son el signo de intercalacin ( ^ ) y el signo de dlar ( $ ). Si el primer carcter de una expresin
es un signo ^, entonces la expresin slo coincide al principio de la lnea (despus de un carcter newline, o
al principio del input). Esto nunca se puede confundir con el otro significado
del signo ^, complementacin de las clases de caracteres, puesto que la complementacin slo se
aplica dentro de corchetes. Si el ltimo carcter es el signo de dlar, la expresin slo coincide al final de
una lnea (cuando va seguido inmediatamente de un carcter newline). Este ltimo operador es un caso
especial del operador barra ( / ) , el cual indica contexto al final.
La expresin
ab/cd
coincide con el literal ab, pero slo si va seguido de cd. Por lo tanto
ab$
es lo mismo que
ab/\n
Especificacin de repeticin de expresiones.
Las llaves ( { y } ) especifican o bien repeticiones ( si stas incluyen nmeros) o definicin de expansin (si
incluyen un nombre). Por ejemplo
{dgito}
busca un literal predefinido llamado dgito y lo inserta en la expresin, en ese punto.
Especificar definiciones.
Las definiciones se dan en la primera parte del input del lex, antes de las rdenes. En contraste,
a{1,5}
busca de una a cinco apariciones del carcter a.
Finalmente, un signo de tanto por ciento inicial ( % ) es especial puesto que es el separador para los
segmentos fuente del lex.
Especificacin de acciones.
Cuando una expresin coincide con un modelo de texto en el input el lex ejecuta la accin
correspondiente. Esta seccin describe algunas caractersticas del lex, las cuales ayudan a escribir
acciones. Ntese que hay una accin por defecto, la cual consiste en copiar el input en el output. Esto se
lleva a cabo en todos los literales que de otro modo no coincidiran. Por lo tanto el usuario del lex que desee
absorber el input completo, sin producir ningn output, debe proporcionar rdenes para hacer que coincida
todo. Cuando se est usando el lex con el yacc, sta es la situacin normal. Se puede tener en cuenta qu
acciones son las que se hacen en vez de copiar el input en el output; por lo tanto, en general, una orden que
simplemente copia se puede omitir.
Una de las cosas ms simples que se pueden hacer es ignorar el input.
Especificar una sentencia nula de C; como una accin produce este resultado.
La orden frecuente es
[ \ t \ n] ;
la cual hace que se ignoren tres caracteres de espaciado (espacio en blanco, tabulador, y newline).
Otra forma fcil de evitar el escribir acciones es usar el carcter de repeticin de accin, | , el cual
indica que la accin de esta orden es la accin para la orden siguiente. El ejemplo previo tambin se poda
haber escrito:
|
\ t |
Preparado por Prof. Julio Surez

17

Apuntes de Compiladores I

\ n ;
con el mismo resultado, aunque en un estilo diferente. Las comillas alrededor de
\ny\t
no son necesarias.
En acciones ms complejas, a menudo se quiere conocer el texto actual que coincida con algunas
expresiones como:
[ a-z ] +
El lex deja este texto en una matriz de caracteres externos llamada yytext. Por lo tanto, para
imprimir el nombre localizado, una orden como [ a-z ] + printf (%s , yytext); imprime el literal de yytext. La
funcin C printf acepta un argumento de formato y datos para imprimir; en este caso , el formato es print
literal donde el signo de tanto por ciento ( % ) indica conversin de datos, y la s indica el tipo de literal, y
los datos son los caracteres de yytext. Por lo tanto esto simplemente coloca el literal que ha
coincidido en el output. Esta accin es tan comn que se puede escribir como ECHO. Por ejemplo
[ a-z ]+ ECHO;
es lo mismo que el ejemplo anterior. Puesto que la accin por defecto es simplemente imprimir los
caracteres que se han encontrado, uno se puede preguntar Porqu especificar una orden, como sta, la
cual simplemente especifica la accin por defecto? Tales rdenes se requieren a menudo para
evitar la coincidencia con algunas otras rdenes que no se desean. Por ejemplo, si hay una orden que
coincide con read, sta normalmente coincidir con las apariciones de read contenidas en bread o en
readjust; para evitar esto, una orden de la forma
[ a-z ] +
es necesaria. Esto se explica ms ampliamente a continuacin.
A veces es ms conveniente conocer el final de lo que se ha encontrado; aqu el lex tambin
proporciona un total del nmero de caracteres que coinciden en la variable yyleng. Para contar el nmero
de palabras y el nmero de caracteres en las palabras del input, ser necesario escribir
[ a-zA-Z ] + {words++ ; chars += yyleng;}
lo cual acumula en las variables chars el nmero de caracteres que hay en las
palabras reconocidas. Al ltimo carcter del literal que ha coincidido se puede
acceder por medio de
yytext[ yyleng - 1]
La accin REJECT quiere decir, ve y ejecuta la siguiente alternativa. Esto hace que se ejecute
cualquiera que fuese la segunda orden despus de la orden en curso. La posicin del puntero de input se
ajusta adecuadamente. Suponga que el usuario quiere realmente contar las apariciones incluidas en she:
she { s++; REJECT;}
he { h++; REJECT;}
\n|
.;
Estas rdenes son una forma de cambiar el ejemplo anterior para hacer justamente eso. Despus
de contar cada expresin, sta se desecha; siempre que sea apropiado, la otra expresin se contar. En
este ejemplo, naturalmente, el usuario podra tener en cuenta que she incluye a he, pero no viceversa, y
omitir la accin REJECT en he; en otros casos, no sera posible decir qu caracteres de
input estaban en ambas clases.
Considere las dos rdenes

Preparado por Prof. Julio Surez

18

Apuntes de Compiladores I

a [ bc ] + { ... ; REJECT;}
a [ cd ] + { ... ; REJECT;}
Si el input es ab, slo coincide la primera orden, y en ad slo coincide la segunda. La cadena de
caracteres del input accb, cuatro caracteres coinciden con la primera orden, y despus la segunda orden
con tres caracteres. En contrate con esto, el input accd coincide con la segunda orden en cuatro caracteres
y despus la primera orden con tres.
En general, REJECT es muy til cuando el propsito de lex no es dividir el input, sino detectar todos
los ejemplares de algunos items del input, y las apariciones de estos items pueden solaparse o incluirse uno
dentro de otro.
Suponga que se desea una tabla diagrama del input; normalmente los diagramas se solapan, es
decir, la palabra the se considera que contiene a th y a he.
Asumiendo una matriz bidimensional llamada digram que se va a incrementar, el fuente apropiado
es
%%
[ a-z ] [ a-z ] {digram[yytext[0]] [yytext[1]] ++;
REJECT; }
.;
\n;
donde el REJECT es necesario para tomar un par de letras que comienzan en cada carcter, en vez de en
un carcter si y otro no.
Recuerde que REJECT no vuelve a explorar el input. En vez de esto recuerda los resultados de la
exploracin anterior. Esto quiere decir que si se encuentra una orden con un contexto, y se ejecuta
REJECT, no debera haber usado unput para cambiar los caracteres que vienen del input. Esta es la nica
restriccin de la habilidad de manipular el input que an no ha sido manipulado.

Ejemplos Sencillos de Programas Lex

%{
/*PROG1.L: Cuenta cantidad de letras y nmeros encontrados*/
#include <stdio.h>
int contal,contad;
%}
%%
[a-zA-Z] {contal++;}
[0-9] {contad++;}
.
;
%%
main()
{
yylex();
printf ("Cantidad de letras :%d", contal);
printf ("Cantidad de digitos :%d", contad);
}
%{
/*Prog2.l: imprime OK cada vez que encuentre la palabra automata*/
#include <stdio.h>
int conta;
%}
%%
"automata " {printf ("OK\n"); conta++;}
.
;
%%
Preparado por Prof. Julio Surez

19

Apuntes de Compiladores I

main()
{
yylex();
printf ("\nCantidad de apariciones de la palaba automata :%d", conta);
}

%{
/* PROG 3.l*/
/*imprime Cantidad de lineas que terminan con a o con o*/
#include <stdio.h>
int conta;
%}
%%
(a|o)$ {conta++;}
.
;
%%
main()
{
yylex();
printf ("\nCantidad de lineas que terminan con a or o :%d", conta);
}

%{
/* PROG 4.l */
/*imprime Cantidad de cadenas que tienen de 2 a 3 "a" */
/* seguidas,como mximo*/
#include <stdio.h>
int conta=0;
%}
%%
^(a{2,3})" "|" "(a{2,3})" " {printf("Encontrado \n");conta++;}
.
;
%%
main()
{
yylex();
printf ("\nCantidad de subcadenas con 2 o 3 a :%d", conta);
}

%{
/* PROG5.L: imprime Cantidad de palabras con letras minsculas y nmeros enteros*/
#include <stdio.h>
int conta, contan;
%}
%%
[a-z]+ {conta++;}
[0-9]+
{contan++;}
.
;
%%
main()
Preparado por Prof. Julio Surez

20

Apuntes de Compiladores I

{
yylex();
printf ("\nCantidad de cadenas :%d", conta);
printf ("\nCantidad de nmeros :%d", contan);
}
%{
/* PROG6.L: Reconoce tres palabras reservadas, identificadores y numeros enteros y reales*/
/* En cada caso imprime la cadena encontrada*/
/* Indentificadores : empieza con a, continua con _ y luego cualquier letra y/o digito*/
/* pudiendo esta ltima seccin, ser vacia*/
#include <stdio.h>
%}
%%
if" "
{printf("Reservada: %s\n", yytext);}
else" "
{printf("Reservada: %s\n", yytext);}
endif" "
{printf("Reservada: %s\n", yytext);}
(w|W)(h|H)(i|I)(l|L)(e|E) {printf("Reservada: %s\n", yytext);}
(e|E)(n|N)(d|D)(w|W)(h|H)(i|I)(l|L)(e|E) {printf("Reservada: %s\n", yytext);}
([0-9]+)
{printf("Entero : %s\n", yytext);}
([0-9]+\.[0-9]+)
{printf("Real : %s\n",yytext);}
a_[a-zA-Z0-9]*
{printf("Identificador:%s\n", yytext);}
\n |
.
{;}
%%
main()
{
yylex();
}
%{
/* PROG7.L*/
/* Preparado por : Prof. Julio Surez ao 2003 */
/* Reconoce tres palabras reservadas, identificadores y numeros enteros y reales*/
/* En cada caso imprime la cadena encontrada*/
/* A ms de la cadena, imprimir la lnea en la que se encuentra*/
/*Caracteristicas Similares al PROG6*/
#include <stdio.h>
int conta=1;
%}
%%
if" "
{printf("Reservada: %s en linea %d\n", yytext,conta);}
else" " {printf("Reservada: %s en linea %d\n", yytext,conta);}
endif" " {printf("Reservada: %s en linea %d\n", yytext,conta);}
([0-9]+) {printf("Entero : %s en linea %d\n", yytext,conta);}
([0-9]+\.[0-9]+) {printf("Real : %s en linea %d\n",yytext,conta);}
a_[a-zA-Z0-9]* {printf("Identificador:%s en linea %d\n",yytext,conta);}
\n
{conta++;}
.
{;}
%%
main()
{
yylex(); }

Ejercitario
Trabaja individualmente y luego en grupo, para completar lo siguiente :
Preparado por Prof. Julio Surez

21

Apuntes de Compiladores I

1) Describe la principal funcin de la fase de Anlisis Lxico

2) Explica las dems funciones de del analizador Lxico

3) Por qu se afirma que el analizador lxico, es una subrutina del analizador sintctico?

4) Con respecto al item anterior, grafica un esquema que represente este trabajo coordinado entre las
dos fases mencionadas.

5) Explica tres razones por los cuales es conveniente construir un analizador lxico, para el
compilador.

6) Explica que diferencia existe entre los conceptos de lexemas y componentes lxicos o tokens

Preparado por Prof. Julio Surez

22

Apuntes de Compiladores I

7) Cmo se relacionan los componentes lxicos y los patrones?

8) Escribe un ejemplo de lexemas, patrones y componentes lxicos, para los siguientes casos:
identificadores, nmeros enteros, una palabra reservada, un operador matemtico.

9) Escribe y explica dos ejemplos de errores lxicos que puede tener un programa fuente.

Los siguientes ejercicios son para desarrollarlos en mquina


10) Modifica el programa prog1.l, del material, de tal manera que informe la cantidad de smbolos
encontrados, en el programa de entrada, correspondientes a cada uno de los digitos numricos. Es
decir cantidad de 0, cantidad de 1, cantidad de 2, etc.

11) Escribe programas lex para :


a. Un Procesador de texto que corrija uso de maysculas despus de un punto, cuente
cantidad de lneas del texto, separe con una lnea en blanco dos prrafos, y otras 3
funciones ms a agregar.
b. Un scanner que lea archivos de textos con nmeros romanos y los convierta al sistema
numrico decimal, ignorando los valores incorrectos. Los nmeros romanos deben estar
separados por un espacio delante y detrs, con excepcin de los inician una lnea o los que
terminan la lnea.

c.

Un scanner que lea archivos de textos con numeros binarios de longitud 8. y convierta, al
sistema numrico decimal y hexadecimal. Los nmeros binarios deben estar separados por
un espacio delante y detrs, con excepcin de los inician una lnea o los que terminan la
lnea.

d. Un scanner que lea archivos de textos y corrija errores ortograficos como : n antes de b, v
despues de m, uso de h intermedia en 5 palabras, 5 palabras que empiecen con h y que en

Preparado por Prof. Julio Surez

23

Apuntes de Compiladores I

el texto no se haya escrito correctamente, puntuacin de coma o punto y coma que debe
escribirse a continuacin de la letra de la palabra anterior, sin espacio intermedio.

12) Escribe un programa lex que busque las palabras reservadas mientras, para, y numeros reales
que pueden no tener ningn digito como parte entero, pero si al menos un cero en la parte decimal,
utilizando la coma como separador entre ambos. Si se detecta una cadena que no corresponda a
ninguno de estos tokens, imprimir un mensaje de error, y continuar el anlisis.
13) Con el uso de LEX escribe programas que generen los autmatas finitos para las siguientes
expresiones regulares :
13.1
13.2

( a+ ( b* | 2) + | b)
(a|b) (5* 4+ | abc) (d |e )

14) Con el uso de LEX escribe programas que generen los autmatas finitos para los lenguajes de las
siguientes gramticas regulares :
G( Z )
ZZ0 | Z1| P0
PP0| T1| 0
T Z1|P1| T1| 0 | 1
VT= { 0, 1}
G( P )
Pb | a | Pa| Fb
FFb | Eb | Ea | b
EEb | a | Fa| Fb
VT={a, b}
3. Anlisis

sintctico

3.1 Introduccin
El anlisis sintctico (parser en ingls) recibe la cadena de tokens, que le enva el analizador lxico
y comprueba si con ellos se puede formar alguna sentencia vlida generada por la gramtica del lenguaje
fuente.
La sintaxis de los lenguajes de programacin habitualmente se describe mediante gramticas libres
de contexto.
3.2 Funciones del analizador sintctico
Comprobar si la cadena de tokens proporcionada por el analizador lxico puede se generada por la
gramtica que define el lenguaje fuente ( GLC)

Construir el rbol de anlisis sintctico que define la estructura jerrquica de un programa y obtener
la serie de derivaciones para generar la cadena de tokens. El rbol sintctico, puede ser utilizado
como representacin intermedia en la generacin de cdigo.

Informar de los errores sintcticos de forma precisa y significativa. Deber contener un mecanismo
de recuperacin de errores para continuar con el anlisis.

Preparado por Prof. Julio Surez

24

Apuntes de Compiladores I

token
programa
fuente

Analizador
Lxico

Obtener
siguiente
componente
lxico

Analizador
Sintctico

rbol de
anlisis
sintctico

Resto del
Front-End

Representacin
intermedia

Gestor de
errores
Tabla de
Smbolos

Fig. Posicin del analizador sintctico en el modelo del Compilador


El anlisis sintctico se puede considerar como una funcin que toma como entrada la secuencia de
componentes lxicos (tokens) producida por el anlisis lxico y produce como salida el rbol sintctico. En
realidad, el anlisis sintctico hace una peticin al anlisis lxico del token siguiente en la entrada
(terminales) conforme lo va necesitando en el proceso de anlisis.
En la prctica, el analizador sintctico tambin hace:
Acceder a la tabla de smbolos (para hacer parte del trabajo del analizador
semntico).
Chequeo de tipos ( del analizador semntico).
Generar cdigo intermedio.
Generar errores cuando se producen.
En definitiva, realiza casi todas las operaciones de la compilacin. Este mtodo de trabajo
da lugar a los mtodos de compilacin dirigidos por sintaxis.
3.3 Manejo de errores sintcticos
Si un compilador tuviera que procesar slo programas correctos, su diseo e implantacin se
simplificaran mucho.

El manejador de errores en un analizador sintctico tiene objetivos fciles de establecer:

Debe informar de la presencia de errores con claridad y exactitud

Se debe recuperar de cada error con la suficiente rapidez como para detectar errores posteriores.

No debe retrasar de manera significativa el procesamiento de programas correctos.

3.4 Estrategias de recuperacin de errores


Recuperacin en modo de pnico: Es el mtodo ms sencillo de implantar. Consiste en ignorar el resto
de la entrada hasta llegar a una condicin de seguridad. Una condicin tal se produce cuando nos
encontramos un token especial (por ejemplo un ; o un END).A partir de este punto se sigue analizando
normalmente.
Preparado por Prof. Julio Surez

25

Apuntes de Compiladores I

Recuperacin a nivel de frase: Intenta recuperar el error una vez descubierto. En el caso anterior, por
ejemplo, podra haber sido lo suficientemente inteligente como para insertar el token ; . Hay que tener
cuidado con este mtodo, pues puede dar lugar a recuperaciones infinitas.
Reglas de produccin adicionales para el control de errores: La gramtica se puede aumentar con las
reglas que reconocen los errores ms comunes. En el caso anterior, se podra haber puesto algo como:

Lo cual nos da mayor control en ciertas circunstancias


Correccin Global : Dada una secuencia completa de tokens a ser reconocida, si hay algn error por el
que no se puede reconocer, consiste en encontrar la secuencia completa ms parecida que s se pueda
reconocer. Es decir, el analizador sintctico le pide toda la secuencia de tokens al lxico, y lo que hace es
devolver lo ms parecido a la cadena de entrada pero sin errores, as como el rbol que lo reconoce.

3.4 Tipo de gramtica que acepta un analizador sintctico


Nosotros nos centraremos en el anlisis sintctico para lenguajes basados en gramticas formales,
ya que de otra forma se hace muy difcil la comprensin del compilador, y se pueden corregir, quizs ms
fcilmente, errores de muy difcil localizacin, como es la ambigedad en el reconocimiento de ciertas
sentencias.
La gramtica que acepta el analizador sintctico es una gramtica de contexto libre:
Gramtica : G (N, T, P, S)
N = No terminales.
T = Terminales.
P = Reglas de Produccin.
S = Axioma Inicial.

Preparado por Prof. Julio Surez

26

Apuntes de Compiladores I

3.6 Tipos de Anlisis


De la forma de construir el rbol sintctico se desprenden dos tipos o clases de analizadores
sintcticos. Pueden ser descendentes o ascendentes.
Descendentes: Parten del axioma inicial, y van efectuando derivaciones a izquierda hasta obtener la
secuencia de derivaciones que reconoce a la sentencia.
Pueden ser:
_ Con retroceso.
_ Con recursin.
_ LL(1)

Ascendentes: Parten de la sentencia de entrada, y van aplicando reglas de produccin hacia atrs (desde
el consecuente hasta el antecedente), hasta llegar al axioma inicial.
Pueden ser:
_ Con retroceso.
_ LR(1)

4 Anlisis Descendente Predictivo No-Recursivo


4.1 Consideraciones Previas
Como se mencion anteriormente, el mtodo de construccin de una analizador sintctico
descendente, implica considerar que ste verificar la cadena de tokens, construyendo el rbol sintctico en
forma descendente. Por los conocimientos de la teora de Lenguajes y Autmatas, sabemos que esto
implica una secuencia de operaciones de derivaciones desde el Axioma de la gramtica, raz del rbol,
hasta las hojas del mismo (sentencia).
En este proceso de construccin del rbol, pueden surgir los siguientes inconvenientes:
4.2 El problema del retroceso

Preparado por Prof. Julio Surez

27

Apuntes de Compiladores I

El primer problema que se presenta con el anlisis sintctico descendente, es que a partir del nodo
raz el analizador sintctico no elija las producciones adecuadas para alcanzar la sentencia a reconocer.
Cuando el analizador se da cuenta de que se ha equivocado de produccin, se tienen que deshacer las
producciones aplicadas hasta encontrar otras producciones alternativas, volviendo a tener que reconstruir
parte del rbol sintctico. A este fenmeno se le denomina retroceso, vuelta a atrs o en ingls
backtracking.
El proceso de retroceso puede afectar a otros mdulos del compilador tales como la tabla de
smbolos , cdigo generado, interpretacin, etc. teniendo que deshacerse tambin los procesos
desarrollados en estos mdulos.
Ejemplo de retroceso : Sea la gramtica G(<PROGRAMA>)

Se desea analizar la sentencia module d ; d ; p ; p end


A continuacin se construye el rbol sintctico de forma descendente:
1. Se parte del smbolo inicial <PROGRAMA>
2. Aplicando la primera regla de produccin de la gramtica se obtiene:

3. Aplicando las derivaciones ms a la izquierda, se tiene que :


3.1 module es un terminal, que coincide con el primero de la cadena a reconocer
3.2 se deriva <DECLARACIONES> con la primera alternativa (consecuente).

Preparado por Prof. Julio Surez

28

Apuntes de Compiladores I

Se observa que el siguiente terminal generado, tampoco coincide con el token de la cadena de
entrada. Entonces el analizador sintctico debe volver atrs, hasta encontrar la ltima derivacin de un no
terminal, y comprobar si tiene alguna alternativa ms. En caso afirmativo se debe de elegir la siguiente y
probar. En caso negativo, volver ms atrs para probar con el no terminal anterior. Este fenmeno de vuelta
atrs es el que se ha definido anteriormente como retroceso.

Llegamos a este punto, tambin se debe de retroceder en la cadena de entrada hasta la primera d,
ya que en el proceso de vuelta atrs lo nico valido que nos ha quedado del rbol ha sido el primer token
module. Si se deriva <DECLARACIONES> nuevamente con la primera alternativa, se tiene

Preparado por Prof. Julio Surez

29

Apuntes de Compiladores I

En este momento se tienen reconocidos los primeros 5 tokens de la cadena.


Se deriva el no terminal <PROCEDIMIENTOS>, con la primera alternativa, y el rbol resultante se
muestra a continuacin. Se ha reconocido el sexto token de la cadena.

El siguiente token de la cadena es ; mientras que en el rbol se tiene end, por lo tanto habr de
volver atrs hasta el anterior no terminal, y mirar si tiene alguna otra alternativa.
El ltimo no terminal derivado es <PROCEDIMIENTOS>, si se deriva con su otra alternativa se tiene
el rbol mostrado a continuacin. Con esta derivacin se ha reconocido la parte de la cadena de entrada
module d ; d; p;

Se deriva <PROCEDIMIENTOS> con la primera alternativa y se obtiene el siguiente rbol sintctico,


reconocindose module d; d; p; p

El rbol sintctico ya acepta el siguiente token de la cadena (end) y por tanto la cadena completa.
Puede concluirse, que los tiempos de reconocimiento de sentencias de un lenguaje pueden
dispararse a causa del retroceso, por lo tanto los analizadores sintcticos deben eliminar las causa que
producen el retroceso.

Preparado por Prof. Julio Surez

30

Apuntes de Compiladores I

4.3 Anlisis Descendente sin retroceso


Para eliminar el retroceso en el anlisis descendente, se ha de elegir correctamente la regla
correspondiente a cada no terminal que se deriva. Es decir que el anlisis descendente debe ser
determinista, y solo se debe tomar una opcin en la derivacin de cada no terminal.
A este tipo de anlisis se le denomina LL(K). La primera L representa la forma de exploracin de la
sentencia : de Izquierda a Derecha ( L : left to right). La segunda L por la forma de construccin del rbol
(descendente), utilizando la derivacin del no terminal ms a la izquierda, en cada paso (L : left most). El
smbolo K representa la cantidad de tokens de la sentencia, que se tendrn en cuenta, para que el
analizador sepa, predictivamente, que regla aplicar. Generalmente K = 1.

4.3.1

Gramticas LL(1)

Las gramticas LL(1) son un subconjunto de las gramticas libres de contexto. Permiten un anlisis
descendente determinista ( sin retroceso ).
Las gramticas LL(1), como ya se mencion, permiten construir un analizador determinista
descendente con tan solo examinar en cada momento el smbolo actual de la cadena de entrada ( smbolo
de preanlisis) para saber que produccin aplicar.
Una gramtica LL(1) debe cumplir con las siguientes condiciones :
Debe ser una gramtica limpia ( como toda gramtica formal )
No debe ser ambigua
No debe tener recursividades por izquierda, en sus reglas
Por lo tanto, previo a la construccin de un analizador sintctico LL(1), deber tratarse la gramtica
libre de contexto de manera que cumpla con las condiciones sealadas.
4.3.2

Gramticas limpias

Las gramticas de los lenguajes de programacin estn formadas por un conjunto de reglas, cuyo
nmero suele ser bastante amplio, lo cual incide en la ocultacin de distintos problemas que pueden
producirse, tales como tener reglas que produzcan smbolos que se usen despus, o que nunca se llegue a
cadenas de terminales. Todo esto se puede solucionar transformando la gramtica inicial sucia a una
gramtica limpia.

Definiciones
Smbolo muerto : es un Smbolo no terminal que no genera sentencia.

Smbolo inaccesible: es un smbolo no terminal al que no se puede llegar por medio de


producciones desde el smbolo inicial de la gramtica.

Gramtica sucia: es toda gramtica que contiene smbolos muertos y/o inaccesibles.

Gramtica limpia: es toda gramtica que no contiene smbolos muertos y/o inaccesibles.

Smbolo vivo: es un smbolo no terminal del cual se puede derivar una sentencia. Todos los
terminales son smbolos vivos. Es decir son smbolos vivos los que no son muertos

Smbolo accesible: es un smbolo que aparece en una cadena derivada el smbolo inicial. Es decir,
aquel smbolo que no es inaccesible.

Preparado por Prof. Julio Surez

31

Apuntes de Compiladores I

Limpieza de Gramticas
Como prctica constante, deber procederse a la limpieza de las gramticas, luego que las
mismas hayan sido diseadas. Para el efecto, existen algoritmos para la depuracin.
El mtodo consiste en detectar y eliminar, en primer lugar, a los smbolos muertos de la gramtica.
Posteriormente, se proceder a detectar y eliminar los smbolos inaccesibles.
Es importe respetar el orden en que se citaron los tipos de smbolos a eliminar, debido a que la
eliminacin de smbolos muertos, puede generar smbolos inaccesibles en la gramtica.

4.3.2.1 Teorema de los smbolos vivos


Si todos los smbolos de la parte derecha de una produccin son vivos, entonces el smbolo de la
parte izquierda tambin lo es.
El procedimiento consiste en iniciar una lista de no terminales que sepamos que son smbolos vivos, y
aplicando el teorema anterior para detectar otros smbolos no terminales vivos para aadirlos a la lista.
Dicho de otra forma, los pasos del algoritmo son:
1. Hacer una lista de no terminales que tengan al menos una produccin sin smbolos no terminales en
su consecuente.
2. Dada una produccin, si todos los no-terminales de la parte derecha pertenecen a la lista, entonces
podemos incluir al no terminal del antecedente.
3. Cuando no se puedan incluir mas smbolos mediante la aplicacin del paso 2, la lista contendr
todos los smbolos vivos, el resto sern muertos.
Ejemplo
Sea la gramtica escrito en la BNF G(<inicial>)

Determinamos los smbolos muertos :


Paso 1: la lista empieza con los smbolos: <NOTER2> y <NOTER3>
Paso 2: En la lista se agregan los smbolos: <NOTER1> y <INICIAL>
Paso 3: Como ya no se pueden aadir nuevos smbolos a la lista, sta contiene a los smbolos
vivos, y lo que no estn incluidas en ella, son smbolos muertos: <NOTER4> y <NOTER5>

Entonces se procede a eliminar de la gramtica los smbolos muertos <NOTER4> y <NOTER5>, y


las reglas que contienen a estos smbolos, quedando el conjunto de producciones como sigue :

Preparado por Prof. Julio Surez

32

Apuntes de Compiladores I

<INICIAL> : := a <NOTER1><NOTER2><NOTER3>
<NOTER1> : := b <NOTER2> <NOTER3>
<NOTER2> : := e | d e
<NOTER3> : := g <NOTER2> | h

4.3. 2.2 Teorema de smbolos accesibles


Si el smbolo no terminal de la parte izquierda de una produccin es accesible, entonces todos los
smbolos de la parte derecha tambin lo son.
Se hace una lista de smbolos accesibles, y aplicando el teorema para detectar nuevos smbolos accesibles
para aadir a la lista, hasta que no se pueden encontrar ms.
Los pasos a seguir son:
1. Se comienza la lista con un nico no terminal, el smbolo inicial de la gramtica
2. Si la parte izquierda de la produccin est en la lista, entonces se incluyen en la misma a todos los
no terminales que aparezcan en la parte derecha.
3. Cuando ya no se puedan incluir ms smbolos mediante la aplicacin del paso 2, la lista contendr
todos los smbolos accesibles, y el resto ser inaccesible.

Ejemplo
Sea la gramtica en la BNF , G(<INICIAL>)

Determinamos los smbolos inaccesibles :


Paso 1: la lista empieza con los smbolos: <INICIAL>
Paso 2: En la lista se agregan los smbolos: <NOTER1> y <NORTE2>
Paso 3: Como ya no se pueden aadir nuevos smbolos a la lista, sta contiene a los smbolos
accesibles, y lo que no estn incluidas en ella, son smbolos inaccesibles: <NOTER3> y <NOTER4>

Entonces se procede a eliminar de la gramtica los smbolos inaccesibles <NOTER3> y


<NOTER4>, y las reglas que contienen a estos smbolos, quedando el conjunto de producciones como
sigue :

Preparado por Prof. Julio Surez

33

Apuntes de Compiladores I

<INICIAL> : := a <NOTER1><NOTER2>| <NOTER1>


<NOTER1> : := c <NOTER2> d
<NOTER2> : := e | f <INICIAL>
<NOTER3> : := g <NOTER2> | h

4.3.3

Recursividad

Las reglas de produccin de una gramtica estn definidas de forma que al realizar derivaciones
dan lugar a recursividades. Entonces se dice que una regla de derivacin es recursiva si es de la forma:
A A a

donde A pertenece a VN y a pertenece a (VT U VN )*

Los analizadores LL(1), deben evitar las gramticas con recursividad por izquierda, debido a que
ests pueden producir ciclos infinitos en las derivaciones por izquierda.
Cabe aqu repasar el algoritmo para eliminar dichas recursividades.
Algoritmos para eliminacin de recursividades:
A lo largo del curso de Lenguajes y autmatas, hemos aprendido dos mtodos distintos con
idnticos efectos.
En el primero que repasaremos, se presenta la generacin de cadenas vacas como consecuentes
de no terminales de la gramtica.
En el segundo mtodo, los consecuentes equivalentes generan nuevos casos de factores comunes,
que debern ser nuevamente factorizados (sobre la factorizacin veremos ms adelante).
Mtodo 1:
Por cada caso de recursividad de la forma :
A A a1 | A a2 | .| Aan | 1 | 2 | | m
Donde A pertenece a VN, ai y j pertenecen a (VT U VN)* con la aclaracin que j no empieza con A.
Se reemplazan estas reglas por las siguientes, con C es un nuevo no terminal en la gramtica :
A 1 C | 2 C | | m C
C a1 C | a2 C | .| an C | e

Preparado por Prof. Julio Surez

34

Apuntes de Compiladores I

Mtodo 2 :
Por cada caso de recursividad de la forma :
A A a1 | A a2 | .| Aan | 1 | 2 | | m
Donde A pertenece a VN, ai y j pertenecen a (VT U VN)* con la aclaracin que j no empieza con A.
Se reemplazan estas reglas por las siguientes, con C es un nuevo no terminal en la gramtica :
A 1 | 2 | | m | 1 C | 2 C | | m C
C a1 | a2 | .| an | a1 C | a2 C | .| an C
Ejemplo
Dada la gramtica G( S ) donde VT = {(,), a, ,} y
P:{
S ( L ) | a
L L , S | S
}
La recursividad se presenta en L, entonces habr que eliminarlo, antes de construir el analizador LL(1)
Por el mtodo 1:
L L , S | S

se convierte en su equivalente, introduciendo el no terminal A :

LSA
A,SA| e
Entonces las reglas de la gramtica equivalente, sin recursividad por izquierda quedn:
S ( L ) | a
LSA
A,SA| e

Por el mtodo 2:
L L , S | S

se convierte en su equivalente, introduciendo el no terminal B :

L S | S B
B , S | , S B
Entonces las reglas de la gramtica equivalente, sin recursividad por izquierda quedn:
S ( L ) | a
L S | S B
B , S | , S B

Preparado por Prof. Julio Surez

35

Apuntes de Compiladores I

4.3.4

Gramticas Ambiguas

Una sentencia generada por una gramtica es ambigua si existe ms de un rbol sintctico para
ella. Una gramtica es ambigua si genera al menos una sentencia ambigua, en caso contrario es no
ambigua. Hay muchas gramticas equivalentes que pueden generar el mismo lenguaje, algunas son
ambiguas y otras no. Sin embargo existen ciertos lenguajes para los cuales no pueden encontrarse
gramticas no ambiguas. A tales lenguajes se les denomina ambiguos intrnsecos.
La ambigedad de una gramtica es una propiedad indecidible, lo que significa que no existe ningn
algoritmo que acepte una gramtica y determine con certeza y en un tiempo finito si la gramtica es
ambigua o no.
Ejemplo de gramtica ambigua :
G (<EXP>)

Las siguientes son derivaciones posibles de la cadena 5 c * 6

Definicin de precedencia y asociatividad


Una las principales formas de evitar la ambigedad es definiendo la precedencia y asociatividad de
los operadores. Lgicamente para las gramticas con expresiones de cualquier tipo.
Se define el orden de precedencia de evaluacin de las expresiones y los operadores. A
continuacin se presenta este orden de precedencia de mayor a menor
1) ( ) , identificadores, constantes
2) - operador unario de negacin
3) ^ operador de potenciacin
4) * /
5) + -

Preparado por Prof. Julio Surez

36

Apuntes de Compiladores I

La asociatividad se define de forma diferente para el operador de potenciacin que para el resto de los
operadores. El operador ^ es asociativo de derecha a izquierda

Mientras que el resto de los operadores binarios, son asociativos de izquierda a derecha, si hay casos de
igual precedencia.

Estas dos propiedades, precedencia y asociatividad, son suficientes para convertir la gramtica
ambigua basada en operadores en no ambigua, es decir, que cada sentencia tenga slo un rbol sintctico.
Para introducir las propiedades anteriores de las operaciones en la gramtica se tiene que escribir
otra gramtica equivalente en la cual se introduce un smbolo no terminal por cada nivel de precedencia.
Ejemplo
Sea la gramtica G(<EXP>
Con las reglas :

Nivel 1 de precedencia:
Introducimos el no terminal <A>, para describir una expresin indivisible, con la mxima precedencia,
entonces las reglas quedan:
<A> ::= ( <EXP>) | identificador | constante
Nivel 2 de precedencia:
Se introduce un nuevo no terminal, <B>, que describe el operador unario de negacin y el no terminal del
nivel anterior
<B> : : = - <B> | <A>

Nivel 3 de precedencia:
Agregamos el no terminal <C>, para representar al operador de potenciacin, considerando su
asociatividad; y el no terminal del nivel anterior.
<C> : : = <B> ^ <C> | <B>
Preparado por Prof. Julio Surez

37

Apuntes de Compiladores I

Como el operador ^ es binario, la regla de potenciacin debe incluir dos operadores. Esta consideracin se
tendr igualmente con los operadores binarios siguientes.
Nivel 4 de precedencia:
Para este nivel, introducimos el no terminal <D>, y representamos las operaciones de producto y divisin; y
como en los casos anteriores, el no terminal del nivel inmediato superior.
<D> : : = <D> * <C> | <D> / <C> | <C>
Como puede verse, el nuevo no terminal aparece a la izquierda de los operadores, por la asociatividad de
izquierda a derecha de los mismos.

Nivel 5 de precedencia:
Para el ltimo nivel de precedencia, se procede a utilizar el mismo no terminal de la gramtica original,
utilizada para representar las operaciones de este nivel.
<EXP> ::= <EXP> + <D> | <EXP> - <D> | <D>

La gramtica queda entonces:


G(<EXP>)
S = <EXP>
VT= {+, -, *, /, ^, (, ), identificador, constante}
VN= {<EXP>, <D>, <C> ,<B> , <A>}
P:{
<EXP> ::= <EXP> + <D> | <EXP> - <D> | <D>
<D> : : = <D> * <C> | <D> / <C> | <C>
<C> : : = <B> ^ <C> | <B>
<B> : : = - <B> | <A>
<A> ::= ( <EXP>) | identificador | constante
}
Siendo sta una gramtica equivalente de la anterior, pero no ambigua

4.3.5 Factorizacin
Hay veces en las que una gramtica no es recursiva por izquierda y sin embargo no es LL(1)
Veamos un caso:
<SENT> ::= if <COND> then <SENT> else <SENT> | if <COND> then <SENT>

Segn estas producciones de <SENT>, no podemos optar por una alternativa con slo el primer token ledo if. Esta
situacin se puede resolver usando la segunda tcnica de transformacin de gramticas: la factorizacin.
En una forma general, las reglas con factor comn tienen la forma:

<A>::= a | a d

donde a , , d ? ( VT U VN )+

La transformacin de las reglas, implicar la insercin en la gramtica, de un nuevo no terminal por cada
caso de factor comn existente en la misma:
<A> ::= a <C>
<C>::= | d

Preparado por Prof. Julio Surez

38

Apuntes de Compiladores I

Ejemplo del if
<SENT> ::= if <COND> then <SENT> else <SENT> | if <COND> then <SENT>

Transformado a:
<SENT> ::= if <COND> then <SENT> <NUEVO>
<NUEVO> ::= else <SENT> | e

4.4 Esquema del Analizador Descendente Predictivo no Recursivo

Entrada

X
Y
Pila

Programa para anlisis


sintctico predictivo

SALIDA

Z
$
Tabla de anlisis sintctico

El analizador descendente LL(1) utiliza una pila auxiliar para almacenar los smbolos de la
gramtica libre de contexto, en base al cual se construy el analizador sintctico.
La tabla de anlisis sintctico, contiene las configuraciones de las reglas que predictivamente
debern ser utilizadas en cada paso de derivacin por izquierda.
La salida del anlisis sintctico, es la secuencia de reglas utilizadas en la derivacin descendente (
o por izquierda).

Preparado por Prof. Julio Surez

39

Apuntes de Compiladores I

4.4.1 Algoritmo para el anlisis LL(1)


La pila se carga, inicialmente con los smbolos $ y Z, donde $ indica el final de cadena de una
sentencia, y Z corresponde al smbolo inicial de la gramtica.
El mtodo consiste en seguir el algoritmo partiendo:

La cadena a reconocer

Una pila de smbolos ( terminales y no terminales)

Una tabla ( M ) asociada de forma unvoca a la gramtica

La cadena de entrada acabar en el smbolo $ ( como ya se explic )

Sea X el elemento en la cima de la pila y a el terminal apuntado en la entrada. El algoritmo consiste en:

1- Si X = a = $ entonces aceptar la sentencia de entrada


2- Si X = a <> $ entonces
Se quita X de la pila y se avanza el apuntador de entrada
3- Si X es un terminal y X <> a entonces rechazar la sentencia de entrada
4- Si X es un no terminal entonces consultamos la tabla de acuerdo a la siguiente indexacin: M(X,a)
Si M(X,a) es vaca entonces rechazar la sentencia de entrada
Si M(X,a) no es vaca entonces: se quita X de la pila y se inserta el consecuente de la regla cargada
en dicha posicin, en orden inverso.
5- Repetir desde el paso 1

Preparado por Prof. Julio Surez

40

Apuntes de Compiladores I

4.4.2 Construccin de la Tabla de Anlisis LL(1)


La tabla de anlisis sintctico LL(1), es un elemento fundamental, en el proceso de anlisis descendente
predictivo. Esta contiene las reglas gramaticales que sern utilizadas en cada paso de un reconocimiento
sintctico, por este mtodo.
La tabla es una matriz, donde una de sus dimensiones se indexa de acuerdo a cada no terminal de la
gramtica, y la otra de acuerdo a cada terminal a ms del smbolo de fin de cadena ($).
Para la construccin de la tabla es necesario el clculo de los conjuntos llamados de cabecera o primero, y
siguiente. Posterior a estos clculos, se desarrolla el algoritmo de construccin de la tabla.

4.4.2.1 Funcin Primero o Cabecera


Si a es un cadena de smbolos gramaticales, se considera PRIMERO(a) como el conjunto de
terminales que inician las cadenas derivadas de a. Si a =>*e, entonces e tambin est en PRIMERO(a).
Para calcular PRIMERO(X), para todos los smbolos gramaticales X, aplquense las siguientes
reglas, hasta que no se puedan aadir ms terminales o e a ningn conjunto PRIMERO.
1. Si X es terminal, entonces PRIMERO(X) es {X}. Un solo elemento en el conjunto PRIMERO.
2. Si X e es una produccin, entonces adase e a PRIMERO(X).
3. Si X es un no-terminal y XY1Y2...Yk , es una produccin : ( cada Yi es un smbolo del
consecuente)
3.1 Agregar a a PRIMERO(X), si a est en algn Yi y e est en todos los
PRIMERO(Y1)...,PRIMERO(Yi-1), es decir en los elementos que estn antes de Yi, en la parte derecha de la
regla.
3.2 Si e est en PRIMERO( Yj) para j=1,2,...k, agregar e a PRIMERO(X). Si Y1 no deriva a
un e, no se aade ms que todo lo que est en PRIMERO(Y1) a PRIMERO(X).

Ejemplo:

PRIMERO(A) = {d, a, c}
PRIMERO(S)=PRIMERO(A) = {d, a, c}
PRIMERO(B)=PRIMERO(A) U {b} = {d, a, c, b}

Preparado por Prof. Julio Surez

41

Apuntes de Compiladores I

4.4.2.2 Funcin Siguiente


Se define Siguiente (A), para el no Terminal A, como el conjunto de terminales a que pueden
aparecer inmediatamente a la derecha de A, en alguna forma de frase.
Para calcular SIGUIENTE(A), para todos los no-terminales A, aplquense las siguientes reglas,
hasta que no se puedan aadir nada ms a ningn conjunto SIGUIENTE
1. Pngase $ en SIGUIENTE(S), donde S es el smbolo inicial de la gramtica, y $ el indicador de fin de
cadena (FDC).
2. Si hay un produccin A--> aB, entonces todo lo que est en PRIMERO(), excepto e, se pone en
SIGUIENTE(B) .
3. Si hay una produccin A-->aB o una produccin A--> aB, donde PRIMERO() contenga e, entonces todo
lo que est en SIGUIENTE(A) se pone en SIGUIENTE(B).

Ejemplo: G( S )

SIGUIENTE (S) = {$, d, a, c, b }


SIGUIENTE (A)= PRIMERO(B) U PRIMERO(S)= { d, a, c, b }
SIGUIENTE (B)= e U SIGUIENTE(A)= { d, a, c, b, e}

4.2.2.3 Algoritmo para construccin de tabla LL(1)

Mtodo: Suponemos que la tabla se identifica con M


1. Para cada produccin A-->a de la gramtica, aplicar los pasos 2 y 3.
2. Para cada terminal a en PRIMERO(a), cargar A-->a, en la posicin M[A,a]
3. Si e est en PRIMERO(a), cargar A-->a a M[A,b] para cada terminal b que pertenezca a SIGUIENTE(A).
Si e est en PRIMERO(a) y $ est en SIGUIENTE(A), cargar A-->a a M[A,$] .
4. Todas las posiciones de M, que no contengan una produccin, sern consideradas como entradas de
ERROR.

Preparado por Prof. Julio Surez

42

Apuntes de Compiladores I

Ejercicios
1) Dada la gramtica G(S)
S ( L ) | a
L L , S | S
Grfica rboles sintcticos, ascendentes y descendentes, para las siguientes sentencias :
i)
(a,a)
ii)
(a,(a,a))
iii)
(a,((a,a),(a,a)))
2) Eliminar recursividad por izquierda de la gramtica G(S) del ejercicio anterior

3) Dada las gramticas a) G ( S ) VT = {(,),;,x,a}, Verifica si son gramticas limpias


S ( A )
A C B
B;A|e
C x | S | C a | F a x
DxDa| Exa
F F xa| EFax
E(E)| ax
b) G ( K) VT = {a,e,c,m,n, d, j}
KaZb| eMP
ZcA|D|mm
A M a | Bn
Fn
R cd R
PT|jBP
M nd
4) Considera la gramtica G( Y )
Y Y | Y
donde | es un Terminal de la gramtica que indica separador de alternativa
Y Y Y
implica concatenacin de dos Y
Y Y * | Y+
* es operador de cierre absoluto de una Y, + operador de cierre positivo
Y ( Y )
Y a
Y b
* y + : tienen la mayor precedencia y son asociativos por la izquierda
Concatenacin: tiene la segunda mayor precedencia y es asociativa por izquierda
| : tiene la menor precedencia y es asociativa por la izquierda
Construye una gramtica no ambigua equivalente de acuerdo a las precedencias y las asociatividades.
5) Dada la siguiente gramtica, modifica y adecua sus producciones, de manera que sea factible para un
anlisis LL(1) :
G(F) VT={ >,<,=,id,*,num,numreal}
F F >= F | F <= F | F <> F | T num * num
F id | num | id * numreal | T id | F num T | F < T
E num = num
D T num | numreal * num

Preparado por Prof. Julio Surez

43

Apuntes de Compiladores I

6) Dada la siguiente tabla de anlisis LL(1), si las sentencias : bbdbaba$


,
bbdbdbaab$
son vlidas sintcticamente. Construye el rbol descendente de la(s) sentencia(s) vlida(s).
G(S)
a
b
d
$
S
S b P A
A
A a M
A Pb
A e
M
B
P
C

M a b B

Ce

B P
P b C
Ce

Be
C dP

Ce

7) Dada la siguiente gramtica, construye la tabla de anlisis LL(1), para la misma


VT = { %, *, x , ( , ) , + , - }
G(S)
S A
A B E
E % A | C
B D F
F e | * B
D x | ( C )
C + x | - x
8) Contesta correctamente los siguientes:
a) Menciona tres funciones de la fase de anlisis sintctico, en un proceso de compilacin.

b) Qu significa, que el anlisis sintctico descendente sea predictivo?

c) Qu importancia tienen los resultados de la funcin primero para el anlisis LL(1)?

d) Qu condiciones deben cumplirse, para que una gramtica pueda ser utilizada en la construccin de
un analizador sintctico LL(1)?

Preparado por Prof. Julio Surez

44

Apuntes de Compiladores I

e) A qu se denomina smbolos no generativos, en una gramtica?

f)

En que consiste la recursividad por izquierda, en una gramtica? Cmo afecta a un anlisis
predictivo?

g) A qu se refiere la smbologa LL(1)?

h) Qu tipos de smbolos se almacenan en la pila auxiliar de una analizador predictivo no recursivo?

9) Desarrolla un software para un analizador sintctico predictivo, de acuerdo a los elementos y


algoritmos vistos en este captulo. Escoge una de las gramticas presentadas en esta seccin de ejercicios
y haz el esfuerzo en la construccin.

Preparado por Prof. Julio Surez

45

Apuntes de Compiladores I

ANLISIS SINTCTICO ASCENDENTE

5.1 Introduccin
Se denominan analizadores sintcticos ascendentes (botton-up en ingls) porque procuran construir
el rbol de anlisis sintctico de una sentencia, a partir de esta, es decir desde las hojas del rbol hasta la
raz; o smbolo inicial de la gramtica.
Este proceso requiere la ejecucin de la operacin de reduccin secuencial. Recordemos que una
reduccin implica sustituir en la pila de anlisis, un pivote o parte derecha de una regla de produccin, por
su antecedente o parte izquierda
Reducir : es la inversa de la derivacin. Sustituir el consecuente de una produccin por su
antecedente.
Desplazar : se refiere a la accin de cargar elementos de la entrada que se analiza, a la Pila auxiliar
del analizador.
Pivote o Mango : de una cadena, es una subcadena que concuerda con el lado derecho de una
produccin, y cuya reduccin al no-terminal del lado izquierdo de la produccin, representa un paso a lo
largo de la inversa de una derivacin por la derecha.
Implantacin por medio de una Pila del Anlisis Ascendente por Desplazamiento - Reduccin
Funcionamiento: se desplazan cero o ms smbolos de la entrada a la pila hasta que un pivote est en
su cima. Entonces el analizador reduce por el lado izquierdo de la produccin adecuada. El analizador
repite este ciclo hasta que detecta un error o hasta que la Pila contiene el smbolo inicial de la gramtica y la
entrada est vaca.
Ejemplo:
G(E), donde P: E--> E + E | E*E | ( E ) | id
Anlisis para la sentencia id + id * id

PILA
$

ENTRADA
id + id * id $

ACCION
Desplazar

$id

+ id * id $

Reducir E-->id

$E

+ id * id $

Desplazar

id * id $

Desplazar

$E+
$E+id

* id $

Reducir E-->id

$E+E

* id $

Desplazar

id $

Desplazar

$E+E*
$E+E*id

Reducir E-->id

$E+E*E

Reducir E-->E * E

$E+E

Reducir E-->E + E

$E

Aceptar

Preparado por Prof. Julio Surez

46

Si se reduce aqu, la
traduccin que
podra acompaar al
anlisis, dara
resultado errneo

Apuntes de Compiladores I

5.2 Conflictos durante el anlisis sintctico por desplazamiento reduccin


Existen gramticas independientes del contexto para los cuales no se puede utilizar el analizador
sintctico por desplazamiento-reduccin.
El analizador puede alcanzar una configuracin en la que el mismo, conociendo el contenido total de
la pila y el siguiente smbolo de la entrada, no puede decidir si Desplazar o Reducir ( conflicto Desplazar /
Reducir ), o no puede decidir qu tipo de reduccin efectuar ( conflicto Reducir / Reducir ).
En general ninguna gramtica ambigua, como la del ejemplo aplicarse a este mtodo.

5.3 Analizadores Sintcticos LR


Es una tcnica eficiente de anlisis sintctico ascendente que se puede utilizar para analizar una
clase ms amplia de gramticas independientes del contexto.
Los analizadores LR reconocen lenguajes realizando las dos operaciones vistas, desplazar y
reducir ( shift/reduce en ingls). Lo que hacen es leer los tokens de la entrada e ir cargndolos en una pila,
de forma que se puedan explorar los n tokens superiores que contiene sta y ver si se corresponden con la
parte derecha de alguna de las reglas de la gramtica.
Si es as se realiza una reduccin, consistente en sacar de la pila esos n tokens y en su lugar
colocar el smbolo que aparezca en la parte izquierda de esa regla. En caso contrario, se carga en la pila el
siguiente token y una vez hecho esto se vuelve a intentar una reduccin.
La tcnica se denomina LR(K); la L es por la lectura de la sentencia de izquierda a derecha, la R
por construir una derivacin por la derecha en orden inverso, y la K por el nmero de smbolos de entrada
de examen por anticipado utilizados para tomar decisiones en el anlisis sintctico. Cuando se omite K = 1.
Una gramtica que puede ser analizada por un analizador LR mirando hasta k smbolos de entrada
por delante (lookaheads), en cada movimiento, se dice que es una gramtica LR (k).
5.4 Ventajas

Se pueden construir analizadores LR para reconocer prcticamente todas las construcciones de los
lenguajes de programacin para los que se pueden escribir GLC.

El mtodo de anlisis sintctico LR es el mtodo de anlisis por desplazamiento y reduccin sin


retroceso ms general y eficiente.

Los analizadores que utilizan la tcnica LR pueden analizar un nmero mayor de gramticas que los
analizadores descendentes.

Un analizador sintctico LR puede detectar un error sintctico tan pronto como sea posible hacerlo
en un examen de izquierda a derecha de la entrada

5.5 Desventajas

La principal desventaja del mtodo es el volumen de trabajo que supone construir un analizador
sintctico LR, a mano.

Se necesita de una herramienta especializada ( un generador de anlisis LR ), por ejemplo el


YACC.

Preparado por Prof. Julio Surez

47

Apuntes de Compiladores I

5.6 Estructura y funcionamiento de un Analizador LR


Un analizador LR consta de un programa analizador LR, una tabla de anlisis, y una pila en la que
se van cargando los estados por los que pasa el analizador y los smbolos de la gramtica que se van
leyendo. Se le da una entrada para que la analice y produce a partir de ella una salida
Lo nico que cambia de un analizador a otro, es la tabla de anlisis, que consta de dos partes: una
funcin de accin que se ocupa de las acciones a realizar y una funcin goto (Transicin), que se ocupa de
decidir a qu estado se pasa a continuacin.

5.6.1 Modelo del Analizador LR

Entrada

a1

...

ai

...

an

Programa para anlisis


sintctico LR

Sm

SALIDA

Xm
Pila

Sm-1
Xm-1
...
S0

Accin

Ir_a (Transicin)

Tabla de Anlisis
LR

El programa lee caracteres de un buffer de entrada de uno en uno. Utiliza una pila para almacenar una
cadena de la forma S0X1S1X2S2...XmSm, donde Sm est en la cima. Cada Xi es un smbolo gramatical y
cada Si es un estado del autmata.

5.6.2 Acciones en el Anlisis LR


El programa que maneja el analizador LR, se comporta como sigue :
Determina Sm, el estado de la cima de la pila y ai, el smbolo en curso de la entrada. Se consulta la
entrada Accin[Sm,ai] de la tabla de acciones, que puede tener uno de estos cuatro valores :
1. Desplazar S, donde S es un estado
2. Reducir por una produccin gramatical A-->
3. Aceptar
4. Error
Preparado por Prof. Julio Surez

48

Apuntes de Compiladores I

5.6.3 Funcin Ir-A en Anlisis LR


La funcin Ir-A toma un estado y un smbolo gramatical como argumentos y produce un estado. Se
ver que la funcin IR-A de una tabla de anlisis sintctico construida a partir de la gramtica G, utilizando
cualquiera de los mtodos, que se vern, es la funcin de transiciones de un autmata finito determinista
que reconoce los prefijos viables de G.
5.6.4 Algoritmo de Anlisis Sintctico LR
Entrada : una cadena w y una tabla de anlisis sintctico LR con las funciones ACCION e IR-A para
la Gramtica G.
Salida : si w L(G), un anlisis ascendente de w, sino se indica error.
Mtodo : Inicialmente, S0 est en la pila, donde S0 es el estado inicial, y w$ est en el buffer de
entrada. El analizador ejecuta entonces el siguiente algoritmo:
Apuntar al primer smbolo de w$
Repetir
Sea S el estado en la cima de la pila y a el smbolo apuntado
SI ACCION[S,a] = desplazar S
apilar a y luego S en la cima de la pila. Avanzar al siguiente smbolo de w$
SINO
SI ACCION [S,a] = reducir A-->
Desapilar 2*| | . Donde , representa a la cadena de estados y smbolos pertenecientes al pivote,
desde la cima de la pila. Sea S el estado que ahora est en la cima de la pila; apilar A
y despus IR-A[S,A] en la cima de la pila.
Salida de la produccin A-->
SINO
SI ACCION[S,a] = Aceptar
Retornar
SINO
error( )
FINSI
FINSI
FINSI
Fin Repetir

Preparado por Prof. Julio Surez

49

Apuntes de Compiladores I

Ejemplo: Sea la gramtica G(E), donde P :


(1) E-->E+T
(2) E--> T
(3) T-->T*F
(4) T--> F
(5) F-->(E)
(6) F--> id

Su tabla de Anlisis sintctico LR, la sgt. :

Anlisis de la sentencia id * id + id $
PILA
0

ENTRADA
id * id + id $

ACCION
Desplazar

0id5

* id + id $

reducir por F-->id

0F3

* id + id $

reducir por T-->F

0T2

* id + id $

Desplazar

id + id $

Desplazar

0T2*7
0T2*7id5

+ id $

reducir F-->id

0T2*7F10

+ id $

reducir T-->T*F

0T2

+ id $

reducir E-->T

0E1

+ id $

Desplazar

id $

Desplazar

0E+6
0E+6id5

reducir F-->id

Preparado por Prof. Julio Surez

50

Apuntes de Compiladores I

0E+6F3
0E+6T9

$
$

reducir T-->F
reducir E-->E+T

0E1

Aceptar

5.7 Clculos previos para la construccin de las tablas de Anlisis LR


ITEM LR(0) o simplemente item de una Gramtica
CUALQUIER PRODUCCION CON UN PUNTO EN LA PARTE DERECHA DE LA REGLA
EJEMPLO : SEA LA REGLA

S aA

ITEM : S a . A

COLECCIN DE ITEMS o Coleccin LR(0)


Conjunto de items. Cada conjunto de esta coleccin representa un estado AFD. Este autmata
servir para construir la tabla de anlisis SLR
Para construir la Coleccin LR(0) necesitamos definir lo que se conoce como gramtica
aumentada, y las operaciones de CLAUSURA y GOTO

GRAMATICA AUMENTADA
Si G es una GLC con axioma S , la gramtica aumentada G para G, se construye aadiendo S ->
S , siendo el nuevo axioma S
Sea la gramtica G(S):
S->E$
E-> E+T | E T | T
T-> (E ) | a
Funcin CLAUSURA o CIERRE
Sobre un conjunto I de items, se define la funcin cierre(I) como el conjunto de items resultante de
aplicar las siguientes reglas :
Regla 1: todos los items pertenecientes a I pertenecen a cierre (I)
Regla 2: Si [A-> . B] es un item que pertenece a cierre(I) y existe una regla de la forma B->, entonces
el item [B->.] pertenece a cierre(I)
Regla 3: Repetir la regla anterior hasta que no se aada ningn nuevo item al conjunto cierre(I)
Ejemplo si I={ S->.E$}
Cierre( I )={ S->.E$, E->.E+T, E->.E-T, E->.T , T-> .( E ), T-> .a}
Funcin GOTO o de Transicin
Se define la funcin de transicin , que se aplica a un conjunto de tems y a un smbolo
(terminal o no terminal) de la gramtica, y da como resultado un nuevo conjunto de tems.
, pertenece(VN U VT)*
(I,X) es igual al cierre del conjunto de todos los items de la forma [A-> X.], tales que
Preparado por Prof. Julio Surez

51

Apuntes de Compiladores I
[A-> .X] a I

Ejemplo:
Sea I={ S->.E$, E->.E+T, E->.E-T, E->.T , T-> .( E ), T-> .a}
Si X = E
(I,E) = cierre(S->E.$, E->E.+T, E->E.-T)={ S->E.$, E->E.+T, E->E.-T }
Si X= a
(I,a)= cierre(T->a.)={T->a.}

COLECCIN DE CONJUNTOS DE ITEMS LR(0)


Sea C un conjunto de items LR(0) compuesto por los conjuntos de items
C={I0 , I1 , I2 , ......}
Paso 1:
I0 = cierre( item asociado al axioma de la gramtica)
Paso 2:
Para cada I perteneciente a C y para cada smbolo X ( N U T), se halla GOTO(I,X). Si este conjunto no
es vacio y no pertenece ya a la coleccin C, se aade a la misma.
Ejemplo de la gramtica anterior:
I0 = cierre(S->.E$) = { S->.E$, E->.E+T, E->.E-T, E->.T , T-> .( E ), T-> .a}
Ahora para I0 ( nico elemento de C) y para cada X (terminal o no terminal ) de la gramtica se halla el
conjunto (I,X). Si este no es vaco y no pertenece a C, se aade a C

I1=(I0,E)= cierre (S->E.$, E->E.+T, E->E.-T} = { S->E.$, E->E.+T, E->E.-T }


I2=(I0,T)=cierre(E->T.)={E->T.}
I3=(I0, a ) = cierre(T->a.) ={T->a.}
De la misma forma se obtienen los demas conjuntos de items I, y la coleccin completa ser:

I0 = {[S -> E$],[E -> E+T],[E -> E-T],[E -> T], [T -> (E)],[T -> a]}
I1 = {[S -> E$],[E -> E+T],[E -> E-T]}
I2 = {[E -> T]}
I3 = {[T -> a]}
I4 = {[T -> (E)],[E -> E+T],[E -> E-T],[E -> T],[T -> (E)],[T -> a]}
I5 = {[S -> E$]}
I6 = {[E -> E+T],[T -> (E)],[T -> a]}

Preparado por Prof. Julio Surez

52

Apuntes de Compiladores I

I7 = {[E -> E-T],[T -> (E)],[T -> a]}


I8 = {[E -> E+T]}
I9 = {[E -> E-T]}
I10 = {[T -> (E)],[E -> E+T],[E -> E-T]}
I11 = {[T -> (E)]}

5.8 Algoritmo para la Construccin de la tabla de Anlisis SLR

Preparado por Prof. Julio Surez

53

Apuntes de Compiladores I

Como prctica construimos en clase, la tabla de anlisis SLR, segn la coleccin anterior

5.9 Tabla de Anlisis LR-Cannica


El mtodo LR-cannico es ms general que el SLR, ya que en algunos casos resuelve algn
conflicto que puede darse al intentar construir una tabla de anlisis SLR. La razn es que maneja una
informacin ms precisa que ayuda decidir cundo se deben aplicar reducciones
Llamamos item LR(1) de una gramtica aumentada G, a un elemento [A a.,a] donde Aa ? P
y a ? Vt U {$}.
Decimos que un item LR(1) [A 1 . 2,a ] es vlido para el prefijo viable a1, si:

S =>* r.m. a A x => r.m. a12 x, x ? Vt*


Y ( a es el primer smbolo de x) o (x es ? y a= $).
Ahora, igual que se hizo antes, hay que construir una coleccin de tems LR(1) vlidos para cada
prefijo viable de la gramtica. Para ello se modifican ligeramente las definiciones de clausura y goto

Preparado por Prof. Julio Surez

54

Apuntes de Compiladores I

Preparado por Prof. Julio Surez

55

Apuntes de Compiladores I

Preparado por Prof. Julio Surez

56

Apuntes de Compiladores I

Preparado por Prof. Julio Surez

57

Apuntes de Compiladores I

5.10 Tabla de Anlisis LALR


El mtodo LALR es menos potente que LR-Cannico (hay gramticas que son LR-Cannicas pero
no LALR) y ms potente que el SLR. Adems el tamao de la tabla de anlisis LALR es igual que el de una
SLR. Esto hace que sea un mtodo muy utilizado en la prctica.
Para construir una tabla de anlisis LALR partimos de la coleccin de conjuntos de tems LR(1) y del
AFD, como en el caso del mtodo LR-Cannico. Despus nos fijamos en los estados que tienen las mismas
producciones punteadas en los tems y slo se diferencian en los smbolos de anticipacin del item.
Una vez agrupados los estados de esta forma y modificando las transiciones de forma conveniente,
se llegara a otro AFD simplificado, a partir del cual se construye la tabla LALR, exactamente igual que la
tabla LR-Cannica.
Los nuevos estados del AFD constituyen lo que se llama coleccin LALR.
Si la tabla resultante no tiene conflictos, entonces la gramtica es LALR

Preparado por Prof. Julio Surez

58

Apuntes de Compiladores I

TABLA ANALISIS LALR


ESTADO
0
1
2
36
47
5
89

c
D36
D36
D36
R3
R2

ACCION
d
$
D47
aceptar
D47
D47
R3
R3
R1
R2
R2

S
1

IR-A
C
2
5
89

Preparado por Prof. Julio Surez

59

Apuntes de Compiladores I

Ejercicios
1) CONTESTA EL SIGUIENTE CUESTIONARIO
a) Qu representa un item LR(1), en la construccin de tablas para el anlisis SLR? Escribe un ejemplo.

b) Qu significan los trminos Desplazar y Reducir en un anlisis sintctico ascendente?

c) Qu tipos de acciones se verifican en un anlisis LR?

d) Por los mtodos estudiados, cuntos estados finales puede tener una analizador sintctico LR?

e) Cmo se determina el estado final para el autmata del analizador sintctico LR?

f)

Qu ventaja encuentra al disear un analizador ascendente, en sustitucin de un analizador


descendente?

g) Explica el significado del item [ P id + . P]

2) Dada la siguiente gramtica G (S)


Preparado por Prof. Julio Surez

60

Apuntes de Compiladores I
SA
AB%A|BC
B D|D*B
Dx|(C)
C +x | -x
VT= { %, *, x, (, ), +, - }
Obtener la tabla de anlisis LALR

3) Construye la Coleccin de conjuntos de items LR(1) y la tabla de anlisis LR-Cannica, para la siguiente
gramtica:
(1) Z RdT
VN= {Z, R TB}
(2) R dRd
VT= {d,H,b}
(3) R h
(4) T b

4) Escribe programas en Yacc/Lex para reconocer conjuntos de sentencias del lenguaje, separadas por un
retorno de carro. Correspondiente a la gramtica del item anterior.
Imprimir al final del reconocimiento de un texto con sentencias, la cantidad de sentencias vlidas analizadas.
Incluir el programa en LEX, que genere el analizador lxico correspondiente.

5) Considere la gramtica :

i)
ii)
iii)

S (L) |a
L L,S | S

Construya una derivacin de ms a la derecha para (a,(a,a)) y muestre el handle de cada


forma sentencial derecha.
Muestre los pasos de un parser shift-reduce correspondiente a una derivacin de ms a la
derecha de (a).
Muestre los pasos en la construccin bottom-up de un rbol de parser, durante el parser
shift-reduce de ii).

6) Considere la siguiente gramtica escrita en formato estilo YACC (ignore las acciones por el momento):
decl
: lista_id ':' tipo_id
{ printf("DECL\n"); }
tipo_id : ID
{ printf("TIPO: %s\n", yytext); }
lista_id : id
{ printf("UNO\n"); }
| id ' , ' lista_id
{ printf { ("LISTA\n"; }
id
: ID
{ printf("VAR: %s\n", yytext); }
i)
ii)

Muestre una derivacin de ms a la derecha para la tira de entrada : a, b, c: int


Considere ahora las acciones, y de la salida que un parser bottom-up imprimira para la
misma tira de entrada.

7) Considere la siguiente gramtica que genera el lenguaje de las expresiones aritmticas:


EEBE | (E)
E id | num
B+|*| /|i)

Usando YACC escriba la gramtica para reconocer el lenguaje.

ii)

Agrguele el manejo de errores.

Preparado por Prof. Julio Surez

61

You might also like