Vous êtes sur la page 1sur 113

Pgina N 1

COMPILADORES



MENSAJ E DE
ERROR
PROGRAMA
OBJ ETO
PROGRAMA
FUENTE
COMPILADOR


Fig. 1.1 Un compilador


Un compilador es un programa que lee un programa escrito en un lenguaje, el
lenguaje fuente, y lo traduce a un programa equivalente a otro lenguaje, el lenguaje objeto.
Como parte importante de este proceso de traduccin, el compilador informa a su usuario de
la presencia de errores en el programa fuente.

Hay miles de lenguajes fuentes, desde los lenguajes de programacin tradicionales,
como ser FORTRAN o Pascal, hasta los lenguajes especializados como han surgido
virtualmente en todas las reas de aplicacin de la informtica. Los lenguajes objetos son
igualmente variados: un lenguaje objeto puede ser otro lenguaje de programacin o el
lenguaje de maquina de cualquier computador entre un microprocesador y un
supercomputador. Los computadores a menudo se clasifican como de una pasada de
mltiple pasadas, de carga y ejecucin, de depuracin o de optimizacin, dependiendo de
como hayan sido construidos o de que funcin se supone que realizan. A pesar de esta
aparente complejidad, las tareas bsicas que debe realizar cualquier compilador son
esencialmente las mismas. Al comprender tales tareas, se pueden construir compiladores
para una gran diversidad de lenguajes fuente y maquinas objeto utilizando las mismas
tcnicas bsicas.

Gran parte de los primeros trabajos de compilacin estaba relacionada con la
traduccin de frmulas aritmticas a cdigo de mquina.

En la dcada de 1.950, se considero a los compiladores como programas
notablemente difciles de escribir. El primer compilador de FORTRAN, por ejemplo necesito
para su implementacin 18 aos de trabajo en grupo. Desde entonces se han descubierto
tcnicas sistemticas para manejar muchas de las importantes tareas que surgen en la
compilacin. Tambin se han desarrollado buenos lenguajes de implementacin, entornos de
programacin y herramientas de software.
Pgina N 2

Modelo de anlisis y sntesis de la compilacin


En la compilacin hay dos partes : anlisis y sntesis. La parte del anlisis divide al
programa fuente en sus elementos componentes y crea una representacin intermedia del
programa fuente. La parte de la sntesis constituye el programa objeto deseado a partir de la
representacin intermedia.

Durante el anlisis, se determinan las operaciones que implica el programa fuente y
se registran en una estructura jerrquica llamada rbol. A menudo, se usa una clase especial
de rbol llamado rbol sintctico, donde cada nodo representa una operacin y los hijos de
un nodo son los argumentos de la operacin



rbol sintctico para una proposicin de asignacin


posicin
inicial
velocidad 60
*
+
: =


posicin : = inicial + velocidad * 60

Fig. 1.2


Muchas herramientas que manipulan programas fuentes realizan primero algn tipo
de anlisis. Algunos ejemplos de tales herramientas son :


1) Editores de estructuras : un editor de estructuras toma como entrada una
secuencia de ordenes para construir un programa fuente. El editor de estructura no solo
realiza las funciones de creacin y modificacin de textos de un editor de textos ordinario,
sino que tambin analiza el texto del programa, imponiendo al programa fuente una
estructura jerarquica apropiada. De esta manera, el editor de estructuras puede realizar
tareas adicionales tiles para la preparacin de programas. Por ejemplo, puede comprobar si
la entrada esta formada correctamente, puede proporcionar palabras clave de manera
Pgina N 3
automtica ( por ejemplo cuando el usuario escribe while, el editor proporciona el
correspondiente do y le recuerda al usuario que entre las dos palabras debe ir un condicional
) y puede saltar desde un begin o un parntesis izquierdo hasta su correspondiente end o
parntesis derecho. Ademas, la salida de tal editor suele ser similar a la salida de la fase de
anlisis de un compilador.


2) Impresoras estticas : una impresora esttica analiza un programa y lo imprime
de forma que la estructura del programa resulte claramente visible ( por ejemplo, los
comentarios pueden aparecer con un tipo de letra especial, y las proposiciones pueden
aparecer con una identacin proporcional a la profundidad de su anidamiento en la
organizacin jerrquica de las proposiciones.


3) Verificadores estticos : un verificador esttico lee un programa, lo analiza e
intenta descubrir errores potenciales sin ejecutar el programa. As, un verificador esttico
puede detectar si hay partes de un programa que nunca se podrn ejecutar o si cierta variable
se usa antes de ser definida. Ademas, puede detectar errores de lgica, como intentar usar
una variable real como apuntador.


4) Interpretes : un interprete realiza las operaciones que implica el programa fuente.
Para una proposicin de asignacin, por ejemplo, un interprete podra construir un rbol
( como el de asignacin Fig. 1.2 ) y despus efectuar las operaciones de los nodos conforme
"recorre" el rbol .

En la raz descubrir que tiene que realizar una asignacin, y llamara a una rutina
para evaluar la expresin de la derecha y despus almacenara el valor resultante en la
localidad de memoria asociada con el identificador posicin. En el hijo derecho de la raz, la
rutina descubrira que tiene que calcular la suma de dos expresiones. Se llamara a si misma
de manera recursiva para calcular el valor de la expresin velocidad * 60. Despus sumaria
ese valor al valor de la variable inicial.

Algunos lenguajes de " muy alto nivel", como APL, normalmente son interpretados
porque hay muchas cosas sobre los datos, como el tamao y la forma de las matrices, que no
se pueden deducir en el momento de la compilacin.


El contexto de un compilador


Ademas de un compilador, se pueden necesitar otros programas para crear un
programa objeto ejecutable. Un programa fuente se puede dividir en mdulos almacenados
en archivos distintos. La tarea de reunir el programa fuente a menudo se confia a un
Pgina N 4
programa distinto, llamado procesador. El preprocesador tambin puede abreviaturas,
llamadas macro o preposiciones del lenguaje fuente.


BIBLIOTECA, ARCHIVOS
OBJ ETO RELOCALIZABLE
CODIGO DE MAQUINA ABSOLUTO
EDITOR DE CARGA Y
ENLACE
CODIGO DE MAQUINA RELOCALIZABLE
ENSAMBLADOR
PROGRAMA OBJ ETO EN LENGUAJ E ENSAMBLADOR
COMPILADOR
PROGRAMA FUENTE
PREPROCESADOR
ESTRUCTURA DEL PROGRAMA FUENTE




Fig. 1.3 Sistema para procesamiento de un lenguaje

Pgina N 5


La figura 1.3 muestra una compilacin tpica: el programa objeto creado por el
compilador puede requerir procesamiento adicional antes de poder ejecutar. El compilador
de la figura 1.3 crea cdigo en el lenguaje ensamblador el cual es traducido por un
ensamblador a cdigo de mquina y despus se enlaza a algunas rutinas de biblioteca para
producir el cdigo que realmente se ejecuta en la mquina.


Anlisis del programa fuente


El anlisis consta de tres fases :

1) Anlisis inicial : en el que la cadena de caracteres que constituye el programa
fuente se lee de izquierda a derecha y se agrupa en componentes lxicos, que son secuencias
de caracteres que tienen un significado colectivo

2) Anlisis jerrquico : en el que los caracteres o los componentes lxicos se
agrupan jerrquicamente en colecciones anidadas con un significado colectivo.

3) Anlisis semntico : en el que se realizan ciertas revisiones para asegurar que los
componentes de un programa se ejecuten de un modo significativo.


Anlisis lxico o anlisis lineal


En un compilador, el anlisis lineal se llama anlisis lxico o exploracin. Por
ejemplo, en el anlisis lxico los caracteres de la proposicin de asignacin

posicin :=inicial +velocidad * 60


Se agrupan en los componentes lxicos siguientes :


1. El identificador posicin.
2. El smbolo de asignacin : =.
3. El identificador inicial.
4. El signo de suma.
5. El identificador velocidad.
6. El signo de multiplicacin.
7. El nmero 60.

Pgina N 6

Los espacios en blanco que separan los caracteres de estos componentes lxicos
normalmente se eliminan durante el anlisis lxico.


Anlisis sintctico


El anlisis jerrquico se denomina anlisis sintctico. Este implica agrupar los
componentes lxicos del programa fuente en frases gramaticales que el compilador utiliza
para sintetizar la salida. Por lo general, las frases gramaticales del programa fuente se
representan mediante un rbol de anlisis sintctico como el que se ilustra en la figura 1.4.

60
numero
expresion *
velocidad
identificador
expresion
inicial
identificador
+
posicion
identificador : =
expresion expresion
expresion
proposicin de
asignacion




Fig. 1.4 Arbol de analisis sintactico


para posicin : =inicial +velocidad * 60


Explicacion de la fig. 1.4 :

En la expresin inicial +velocidad * 60, la frase velocidad * 60 es una unidad lgica,
porque las convenciones usuales de las expresiones aritmticas indican que la multiplicacin
se hace antes que la suma. Puesto que la expresin inicial +velocidad va seguida de un *, no
se agrupa en una sola frase independiente en la figura 1.4.

Pgina N 7
La estructura jerarquica de un programa normalmente se expresa utilizando reglas
recursivas. Por ejemplo: se pueden dar las siguientes reglas como parte de la definicin de
expresiones:

1. Cualquier identificador es una expresin.
2. Cualquier numero es una expresin.
3. Si expresin1 y expresin2 son expresiones, entonces tambin lo son

expresin1 +expresin2
expresin1 * expresin2
( expresin1 )

Las reglas (1) y (2) son reglas bsicas ( no recursivas ), en tanto que la regla (3)
define expresiones en funcin de operadores aplicados a otras expresiones. As, por la regla
(1), inicial y velocidad son expresiones. Por la regla (2), 60 es una expresin, mientras que
por la regla (3), primero podemos inferir que la velocidad * 60 es una expresin, y
finalmente, que inicial +velocidad * 60 tambin es una expresin.

De manera similar, muchos lenguajes definen recursivamente las proposiciones
mediante reglas como:

1. Si identificador1 es un identificador y expresin2 es una expresin, entonces

identificador1 : =expresin2

es una proposicion.

2. Si expresin1 es una expresin y proposicin2 es una proposicin, entonces

while ( expresin1 ) do proposicin2
if ( expresin1 ) then proposicin2

son proposiciones.

La divisin entre anlisis lxico y anlisis sintctico es algo arbitraria. Generalmente
se elige una divisin que simplifique la tarea completa del anlisis. Un factor para determinar
la divisin es si una construccin del lenguaje fuente es inherentemente recursiva o no. Las
construcciones lxicas no requieren recursin, mientras que la construcciones sintcticas
suelen requerirla ( Las gramticas independientes del contexto son una formalizacin de
reglas recursivas que se pueden usar para guiar el anlisis sintctico).

Por ejemplo, no se requiere recursin para reconocer los identificadores, que suelen
ser cadenas de letras y dgitos que comienzan con una letra. Normalmente, se reconocen los
identificadores por el simple exmen del flujo de entrada, esperando hasta encontrar un
caracter que no sea ni letra ni dgito, y agrupando despus todas las letras y dgitos
Pgina N 8
encontrados hasta ese punto en un componente lxico identificador. Los caracteres as
agrupados se registran en una tabla, llamada tabla de smbolos, y se retiran de la entrada,
para que pueda empezar el procesamiento del siguiente elemento lxico.

Por otra parte, esta clase de anlisis lxico lineal no es suficientemente poderoso para
analizar expresiones o proposiciones. Por ejemplo, no podemos emparejar de manera
apropiada los parntesis de las expresiones, o las palabras begin y end en proposiciones sin
imponer alguna clase de estructura jerrquica o de anidamiento a la entrada. \ se usa :

El rbol de anlisis sintctico de la figura describe la estructura sintctica de la
entrada.

posicin
inicial
velocidad 60
*
+
: =

60
posicin
inicial
velocidad
entareal
*
+
: =

(a) (b)



Fig. 1.5 El analisis semantico inserta una conversion de entero a real



Anlisis semntico


La fase de anlisis semntico revisa el programa fuente para tratar de encontrar
errores semnticos y rene la informacin sobre los tipos para la fase posterior de
generacin de cdigo. En ella se utiliza la estructura jerarquica determinada por la fase de
anlisis sintctico para identificar los operadores y operandos de expresiones y
proposiciones.

Un componente importante del anlisis semntico es la verificacin de tipos.

Aqu, el compilador verifica si cada operador tiene operadores permitidos por la
especificacin del lenguaje fuente. Por ejemplo, las definiciones de muchos lenguajes de
programacin requieren que el compilador indique un error cada vez que se use un nmero
Pgina N 9
real como ndice de una matriz. Sin embargo las especificaciones del lenguaje pueden
permitir ciertas coerciones a los operandos, por ejemplo, cuando un operador aritmtico
binario se aplica a un numero real. En este caso, el compilador puede necesitar convertir el
numero entero a real.


Ejemplo 1.1.

Por ejemplo, supongamos que todos los identificadores de la figura 1.5 se han
declarado reales y que tan solo 60 se supone entero. La verificacin de tipos de la
figura 1.5 ( a ) revela que * se aplica a un real, velocidad, y a un entero, 60. El tratamiento
general es convertir el entero a real. Esto se ha logrado en la figura 15 (b) creando un nodo
extra para el operador entareal que de manera explcita convierte un entero a real. Por otra
parte, como el operando de entareal es una constante, el compilador podra reemplazar la
constante entera por una constante real equivalente


Anlisis en formadores de textos


Es til considerar que la entrada de un formador de textos especifica una jerarqua de
cajas, regiones rectangulares que se van a llenar con algn patrn de bits, representando
pixeles claros y oscuros para ser impresos por el dispositivo de salida.

Por ejemplo, el sistema TEX considera su entrada de esta manera. Cada caracter que
no sea parte de una orden representa una caja que contiene el patrn de bits de ese caracter
con el tipo y tamao apropiados. Los carcteres consecutivos no separados por " espacios en
blanco " se agrupan en palabras, que consisten en una secuencia de cajas dispuestas
horizontalmente, mostradas en la figura 1.6 . El agrupamiento de caracteres en palabras es el
aspecto linel o lxico del anlisis en un formador de textos.

s a r b a l a p s o d


Fig. 1.6 Agrupamiento de caracteres y palabras en cajas


La caja TEX se puede construir a partir de cajas mas pequeas en combinaciones
arbitrarias horizontales y verticales. Por ejemplo,

\hbox{<lista de cajas>}

Pgina N 10
Agrupa la lista de cajas yuxtaponindolas horizontalmente, mientras que el operador
\ybox agrupa de manera similar una lista de cajas por yuxtaposicin vertical. As si se indica
en TEX

\hbox{\vbox{! 1} \vbox{@ 2}}

Se obtiene la disposicin de cajas que se muestra en la siguiente figura 1.7.
Determinar la disposicin jerarquica de las cajas implicadas en las entradas es parte del
anlisis sintctico en TEX
2
1
@ !


Fig. 1.7 Jerarquia de cajas en TEX

Como otro ejemplo, el preprocesador EQN para matemticas, o el procesador
matemtico de TEX, constituye expresiones matemticas a partir de operadores como sub y
sup para subndices y superndices. Si EQN encuentra un texto de entrada de la forma

CAJA sub caja

Reduce el tamao de caja y la une a CAJA cerca de la esquina inferior derecha, como
se ilustra en la figura 1.8. De manera similar, el operador ssup une caja a la esquina superior
derecha.


caja
CAJA



Fig. 1.8 Construccin de la estructura de subindice en texto matematico


Estos operadores se pueden aplicar recursivamente, as que por ejemplo, el texto en
EQN

a sub {i sup 2}

Pgina N 11
Da como resultado ai 2. Agrupar los operadores sub y sup en componentes lxico es
parte del anlisis lxico del texto en EQN. Sin embargo se necesita la estructura sintctica
del texto para determinar el tamao y la posicin de las cajas.



LAS FASES DE UN COMPILADOR

Conceptualmente, un compilador opera en fases, cada una de las cuales transforma al
programa fuente de una representacin en otra.
En la practica se pueden agrupar algunas fases, y las representaciones intermedias
entre las fases agrupadas no necesitan ser construidas explcitamente

Pgina N 12
ADMINISTRADOR DE LA
TABLA DE SIMBOLOS
MANEJ ADOR DE
ERRORES
PROGRAMA OBJ ETO
GENERADOR DE
CODIGO
OPTIMADOR DE
CODIGO
GENERADOR DE
CODIGO INTERMEDIO
ANALIZADOR
SEMANTICO
ANALIZADOR
SINTACTICO
ANALIZADOR
LEXICO
PROGRAMA FUENTE


Fig. 1.9 Fases de un compilador
Las tres primeras fases, que conforman la mayor parte de la porcin de anlisis de un
compilador, se describieron anteriormente. La administracin de la tabla de smbolos y el
manejo de errores, se muestran en interaccin con las seis fases de anlisis lxico, anlisis
sintctico, anlisis semntico, generacin de cdigo intermedio, optimizacin de cdigo y
generacin de cdigo. De modo informal, tambin se llaman "fases" al administrador de la
tabla de smbolos y al manejador de errores.

Pgina N 13

Administrador de la tabla de smbolos


Una funcin escencial de un compilador es registrar los identificadores utilizados en
el programa fuente y reunir la informacin sobre los distintos atributos de cada identificador.
Estos atributos pueden proporcionar informacin sobre la memoria asignada a un
identificador, su tipo, su mbito ( la parte del programa donde tiene validez ) y, en el caso de
nombres de procedimientos, cosas como el nombre y tipos de sus argumentos, el mtodo de
pasar de cada argumento ( por ejemplo, por referencia ) y el tipo que devuelve, si lo hay.

Una tabla de smbolos es una estructura de datos que contiene un registro por cada
identificador, con los campos para los atributos del identificador. La estructura de datos
permite encontrar rpidamente el registro de cada identificador y almacenar o consultar
rpidamente datos de ese registro.

Cuando el analizador lxico detecta un identificador en el programa fuente, el
identificador se introduce en la tabla de smbolos. Sin embargo, normalmente los atributos
de un identificador no se pueden determinar durante el anlisis lxico. Por ejemplo, en una
declaracin en Pascal como

var posicin, inicial, velocidad : real ;

el tipo real no se conoce cuando el analizador lxico encuentra posicin, inicial, y velocidad.

Las fases restantes introducen informacin sobre los identificadores en la tabla de
smbolos y despus la utiliza de varias formas. Por ejemplo, cuando se esta haciendo el
anlisis semntico y la generacin de cdigos intermedios, se necesita saber cuales son los
tipos de los identificadores, para poder comprobar si el programa fuente los usa de una
forma valida y as poder generar las operaciones apropiadas con ellos.

El generador de cdigo, por lo general, introduce y utiliza informacin detallada
sobre la memoria asignada a los identificadores.





Deteccin e informacin de errores


Cada fase puede encontrar errores. Sin embargo, despus de detectar un error, cada
fase debe tratar de alguna forma ese error, para poder continuar la compilacin, permitiendo
la deteccin de mas errores en el programa fuente.

Pgina N 14
Las fases de anlisis sintctico y semntico por lo general manejan una gran porcin
de los errores detectables por el compilador. La fase lxica puede detectar errores donde los
caracteres restantes de la entrada no forman ningn componente lxico del lenguaje. Los
errores donde la cadena de componentes lxicos violan las reglas de estructura (sintaxis) del
lenguaje son determinados por la fase de anlisis sintctico. Durante el anlisis semntico el
compilador intenta detectar construcciones que tengan la estructura sintctica correcta, pero
que no tengan significado para la operacin implicada, por ejemplo, si se intenta sumar dos
identificadores, uno de los cuales es el nombre de una matriz, y el otro, el nombre de un
procedimiento.


Las fases de anlisis


Conforme avanza la traduccin, la representacin interna del programa fuente que
tiene el compilador se modifica. Para ilustrar esas representaciones, considrese la
traduccin de la proposicin

posicin : = inicial +velocidad * 60 ( 1.1 )

La figura 1.10 muestra la representacion de esta proposicion despues de cada fase.

La fase de anlisis lxico lee los caracteres en el programa fuente y los agrupa en una
cadena de componentes lxicos en los que cada componente representa una secuencia
lgicamente coherente de caracteres, como un identificador una palabra clave ( if, while,
etc.), un caracter de puntuacin, o un operador de varios caracteres como : =. La secuencia
de caracter que forma un componente lxico se denomina lexema del componente.

A ciertos componentes lxicos se le agrega un " valor lxico ". As, cuando se
encuentra un identificador como velocidad, el analizador lxico no solo genera un
componente lxico, por ejemplo id, sino que tambin introduce el lexema velocidad en la
tabla de smbolos, si no esta aun all. El valor lxico asociado con esta operacin de id
seala la entrada de la tabla de smbolos correspondiente a velocidad.

Por lo tanto usaremos, id1, id2 e id3 para posicin, inicial, velocidad, respectivamente,
para enfatizar que la representacin interna de un identificador es diferente de la secuencia
de caracteres que forman el identificador. Por lo tanto la representacin de ( 1.1 ) despus
del anlisis lxico queda sugerida por :

id1 : =id2 +id3 * 60 ( 1.2 )

El anlisis sintctico impone una estructura jerarquica a la cadena de componentes
lxicos, que se representan por medio de arboles sintcticos ,como se muestra en la
figura 1.11 ( b ), en la que un nodo interior es un registro con un campo para el operador y
dos campos que contienen apuntadores a los registros hijos izquierdo y derecho. Una hoja es
Pgina N 15
un registro con dos o mas campos, uno para identificar al componente lxico de la hoja, y
los otros para registrar informacin sobre el componente lxico. Se puede tener informacin
adicional sobre las construcciones del lenguaje aadiendo mas campo a los registros de los
nodos.


Generacin de cdigo intermedio






































Pgina N 16









































Fig. 1.10 Traduccion de una proposicion ( Pag. anterior )



Pgina N 17
id1
id2
id3
60
*
+
: =

(a)
60 num
2 id
3 id
*
1 id
+
+


(b)


Fig. 1.11 La estructura de datos en (b) corresponde al arbol en (a)


Despus de los anlisis sintctico y semntico, algunos compiladores generan una
representacin intermedia explcita del programa fuente. Se puede considerar esta
representacin intermedia como un programa para una maquina abstracta. Esta
representacin intermedia debe tener dos propiedades importantes; debe ser fcil de producir
y fcil de traducir al programa objeto.

La representacin intermedia puede tener diversas formas. El cdigo de tres
direcciones consiste en una secuencia de instrucciones, cada una de las cuales tiene como
mximo tres operandos. El programa fuente de ( 1.1 ) puede aparecer en cdigo de tres
direcciones como

temp1 : =entareal (60)
temp2 : =id3 * temp1 (1.3)
temp3 : =id2 +temp2
Pgina N 18
id1 : =temp3


Esta representacin intermedia tiene varias propiedades :


1) cada instruccin de tres direcciones tiene a lo sumo un operador, ademas de la
asignacin. Por lo tanto, cuando se generan esas instrucciones, el compilador tiene que
decidir el orden en que deben efectuarse las operaciones: la multiplicacion precede a la
adicion en el programa fuente de ( 1.1 ).

2) El compilador debe generar un nombre temporal para guardar los valores
calculados por la instruccin.

3) Algunas instrucciones de "tres direcciones" tienen menos de tres operadores, por
ejemplo, la primera y la ultima instruccin.


Optimizacin de cdigo


La fase de optimizacin de cdigo trata de mejorar el cdigo intermedio, de modo
que resulte un cdigo de maquina mas rpido de ejecutar.

Ejemplo

temp1 : =id3 * 60.0 (1.4)
id1 : =id2 +temp1

Este sencillo algoritmo no tiene nada de malo, puesto que el problema se puede
solucionar en la fase de optimizacin de cdigo. Esto es el compilador puede deducir que la
conversin de 60 de entero a real se puede hacer de una vez por todas en el momento de la
compilacin, de modo que la operacin entareal se puede eliminar. Ademas temp3 se usa
una sola vez para transmitir su valor a id1. Entonces resulta seguro sustituir id1 por temp3, a
partir de lo cual la ultima proposicion de ( 1.3 ) no se necesita y se obtiene el cdigo (1.4).

Hay mucha variacin en la cantidad de optimizacin de cdigos que ejecutan los
distintos compiladores. En los que hacen mucha optimizacin, llamados "compiladores
optimadores" , una parte significativa del tiempo del compilador se ocupa en esta fase. Sin
embargo, hay opciones sencillas que mejoran sensiblemente el tiempo de ejecucin del
programa objeto sin retardar demasiado la compilacin.


Generacin de cdigo

Pgina N 19

La fase de un compilador es la generacin de cdigo objeto, que por lo general
consiste en cdigo de maquina relocalizable o cdigo ensamblador. Las posiciones de
memoria se seleccionan para cada una de las variables usadas por el programa.

Despus, cada una de las instrucciones intermedias se traduce a una secuencia de
instrucciones de maquina que ejecuta la misma tarea.

Por ejemplo:
utilizado los registros 1 y 2, la traduccin del cdigo de (1.4) podra
convertirse en :

MOVF id3, R2 ( 1.5 )
MULF #60.0, R2
MOVF id2, R1
ADDF R2, R1
MOVF R1, id1

El primero y segundo operandos de cada instruccin especifican una fuente y un
destino, respectivamente. La F de cada instruccin indica que las instrucciones trabajan con
numeros de punto flotante. Este cdigo traslada el contenido de la direccin id3 al registro
2, despus lo multiplica por la constante real 60.0 . El signo #significa que 60.0 se trata
como una constante. La tercera instruccin pasa id2 al registro1. La cuarta instruccin le
suma el valor previamente calculado en el registro 2. Por ultimo, el valor del registro 1 se
pasa a la direccin de id1, de modo que el cdigo aplica la asignacin de la fig 1.10.


PROGRAMAS DE SISTEMAS RELACIONADOS CON UN COMPILADOR


La entrada para un compilador puede producirse por uno o varios preprocesadores, y
puede necesitarse otro procesamiento de la salida que produce el compilador antes de
obtener un cdigo maquina ejecutable. En esta seccin se analiza el contexto en el que suele
funcionar un compilador.

Preprocesadores


Los preprocesadores producen la entrada para un compilador, y pueden realizar las
funciones siguientes:

1) Procesamiento de macros : Un preprocesador puede permitir a un usuario definir
macros que son abreviaturas de construcciones ms grandes.

Pgina N 20
2) Inclusin de archivos : Un preprocesador puede insertar archivos de encabezamiento en
el texto del programa. Por ejemplo, el preprocesador de C hace que el contenido del archivo
<globa1.h >reemplace a la proposicin #include <globa1.h >cuando procesa un archivo
que contenga a esa proposicin.

3) Preprocesadores "racionales " : Estos preprocesadores enriquecen los lenguajes
antiguos con recursos ms modernos de flujo de control y de estructuras de datos. Por
ejemplo, un preprocesador de este tipo podra proporcionar al usuario macros incorporadas
para construcciones, como proposiciones while o if, en un lenguaje de programacin que no
las tenga.

4) Extensin a lenguajes : Estos procesadores tratan de crear posibilidades al lenguaje que
equivalen a macros incorporadas. Por ejemplo, el lenguaje Equel es un lenguaje de consulta
de base de datos integrado en C. El preprocesador considera las preposiciones que empiezan
con ##como proposiciones de acceso a la base de datos, sin relacin con C, y se traduce a
llamadas de procesamiento a rutinas que realizan el acceso a la base de datos.
Los procesadores de macro tratan dos clase de proposiciones: definicin de macros y
uso de macros. Las definiciones normalmente se indican con algn caracter exclusivo o
palabra clave, como define o macro. Constan de un nombre para la macro que se esta
definiendo y de un cuerpo, que constituye su definicin. A menudo, los procesadores de
macros admiten parmetros formales en su definicin, esto es, smbolos que se reemplazan
por valores ( en este contexto, un " valor " es una cadena de caracteres ). El uso de una
macro consiste en dar nombre a la macro y proporcionar parmetros reales, es decir,
valores para sus parmetros formales. El procesador de macro sustituye los parmetros
reales por los parmetros formales del cuerpo de la macro; despus, el cuerpo transformado
reemplaza el uso de la propia macro.

Ejemplo 1.2 :
El sistema de composicin tipogrfica TEX mencionado
anteriormente, contiene un recurso de macro general. Las definiciones de macros son de la
forma

\define<nombre de la macro><plantilla>{<cuerpo>}

El nombre de una macro es cualquier cadena de letras precedida por una diagonal
invertida. La plantilla es cualquier cadena de caracteres en donde las cadenas de la forma #1,
#2,...,#9 se consideran parmetros formales. Estos smbolos, tambin pueden aparecer en el
cuerpo las veces que se quiera. Por ejemplo, la siguiente macro define una cita del Journa of
the ACM.

\define\JACM #1; #2 ; #3 .
{{\ s1 J. ACM} {\bf #1}: #2 , pags. #3 .}

El nombre de la macro es JACM, y la planilla es " #1; #2 ; #3 ." '; los smbolos de
punto y coma separan los parmetros y despus del ultimo parmetro se pone un punto. Un
Pgina N 21
uso de esta macro debe tomar la forma de la plantilla, excepto que se puede sustituir cadenas
arbitrarias por los parmetros formales(*). As, se puede escribir

\JACM 17; 4; 715 - 728.

y se espera que aparezca

J. ACM 17:4, pags. 715-728.

La parte del cuerpo {\s1 J. ACM} pide"J. ACM" en cursiva (si es por slantedd. "inclinado"
en ingles). La expresin{bf #1} indica que el primer parmetro real se escribir en negritas
(bf es por boldface, "negrita" en ingles); este parmetro es el numero de volumen.

TEX admite cualquier puntuacin o cadena de texto para separar el volumen, el
nmero del ejemplar y los nmeros de pgina de la definicin de la macro \ JACM. Incluso
se podra haber prescindido totalmente de la puntuacin, en cuyo caso TEX tomara cada
parmetro real como un solo caracter o una cadena encerrada entre { }

(*) Bueno, cadena casi arbitrarias, puesto que solo se hace un simple anlisis lxico de izquierda a derecha del
uso de la macro, y tan pronto como se encuentre un smbolo que concuerde con el texto que sigue a un smbolo # i
de la plantilla, se considera que la cadena precedente concuerda con # i. Por tanto, si se intenta sustituir ab; cd
por # 1, resultara que solo ab concuerda con # 1 y que cd concuerda con # 2.


Ensambladores

Algunos compiladores producen cdigo ensamblador , como en el caso ( 1.5 ), que
se pasa , a un ensamblador para su procesamiento. Otros compiladores realizan el trabajo del
ensamblador produciendo cdigo mquina relocalizable que se puede pasar directamente al
editor de carga y enlace. Se supone que el lector tiene cierta familiaridad sobre como es un
lenguaje ensamblador y que hace el ensamblador, aqu se revisara la relacin entre el cdigo
ensamblador y el cdigo de mquina.

El cdigo ensamblador es una versin mnemotcnica del cdigo de mquina, donde
se usan nombres en lugar del cdigos binarios para operaciones, y tambin se usan nombres
para las direcciones de memoria. Una secuencia tpica de instrucciones en ensamblador
puede ser

MOV a, R1
ADD #2, R1 (1.6)
MOV R1, b

Este cdigo pasa el contenido de la direccin a al registro 1; despus le suma la
constante 2, tratando el contenido del registro 1 como un numero de punto fijo, y por ltimo
almacena el resultado en la posicin de memoria que presenta b. De ese modo calcula
b: =a +2.
Pgina N 22

Es comn que los lenguajes ensambladores tengan recursos para macros que son
similares a las consideradas antes para los procesadores de macros.


Ensambladores de dos pasadas


La forma mas simple de un ensamblador hace dos pasadas sobre la entrada, en donde
una pasada consiste en leer una vez un archivo de entrada. En la primera pasada, se
encuentran todos los identificadores que denotan posiciones de memoria y se almacenan en
una tabla de smbolos ( distinta de la del compilador). Cuando se encuentran por primera vez
los identificadores, se les asigna posiciones de memoria, de modo que despus de leer (1.6),
por ejemplo la tabla de smbolos contendra las entradas que aparecen en la figura 1.12. En
esa figura, se supone que se reserva una palabra, que consta de cuatro byte, para cada
identificador, y que las direcciones se asignan empezando a partir del byte 0.



Figura 1.12. Tabla de smbolos de un ensamblador con los identificadores de (1.6)



IDENTIFICADOR

DIRECCIN
a 0
b 4


En la segunda pasada, el ensamblador examina el archivo de entrada de nuevo. Esta
vez traduce cada cdigo de operacin a la secuencia de bits que representa esa operacin en
lenguaje de mquina, y traduce cada identificador que representa una posicin de memoria a
la direccin dada por ese identificador en la tabla de smbolos.

El resultado de la segunda pasada normalmente es cdigo de mquina relocalizable,
lo cual significa que puede cargarse empezando en cualquier posicin L de la memoria; es
decir, si la suma L a todas las direcciones del cdigo, entonces todas las referencias sern
correctas. Por tanto, la salida del ensamblador debe distinguir aquellas partes de intrucciones
que se refieren a direcciones que se pueden realizar.

Ejemplo 1.3 :
El siguiente es un cdigo de mquina hipottico al que se pueden traducir las
instrucciones en ensamblador (1.6).

0001 01 00 00000000 *
0011 01 10 00000010 (1.7)
Pgina N 23
0010 01 00 00000100 *

Se concibe una pequea palabra de instruccin, en la que los cuatro primeros bits son
el cdigo de la instruccin, donde 0001,0010 y 0011 representan las instrucciones LOAD,
STORE y ADD, respectivamente. LOAD y STORE significan trasladar de memoria a un
registro y viceversa. Los dos bits siguientes designan un registro y 01 se refiere al registro 1
de cada una de las tres instrucciones anteriores. Los dos bits siguientes representan un
marcador, donde 00 es el modo de direccionamineto ordinario, y los ltimos ocho bits se
refieren a una direccin de memoria.

El marcador 10 es el modo " inmediato ", donde los ltimos ocho bits se toman
literalmente como el operando. Este modo aparece en la segunda instruccin (1.7).

En (1.7) tambin se ve un * asociado con la primera y la tercera instrucciones.

Este * representa el bit de relocalizacin que se asocia con cada operando en cdigo
de mquina relocalizable. Supngase que el espacio de direcciones que contiene los datos se
va a cargar empezando en la posicin L. La presencia del * significa que se debe sumar L a
la direccin de la instruccin. Por tanto, si L =00001111, esto es, 15, entonces a y b
estaran en la posicin 15 y 19, respectivamente, y las instrucciones de (1.7) apareceran
como

0001 01 00 00001111
0011 01 10 00000010 (1.8)
0010 01 00 00000100

en cdigo de maquina absoluto o no relocalizable. Ntese que no hay ningn * asociado
con la segunda instruccin de (1.7), de modo que L no se sumo a su direccin en (1.8), lo
cual es correcto, porque los bits representan la constante 2 y no la posicin 2.


Cargadores y editores de enlace


Por lo general, un programa llamado cargador realiza las dos funciones de carga y
edicin de enlaces. El proceso de carga consiste en tomar el cdigo de mquina
relocalizable, modificar las direcciones relocalizables, como se indica en el ejemplo 1.3, y
ubicar las instrucciones y los datos modificados en las posiciones apropiadas de la memoria.

El editor de enlace permite formar un slo programa a partir de varios archivos de
cdigo de mquina relocalizable. Estos archivos pueden haber sido el resultado de varias
compilaciones distintas, y uno o varios de ellos pueden ser archivos de biblioteca de rutinas
proporcionadas por el sistema y disponibles para cualquier programa que las necesite.

Pgina N 24
Si los archivos se van a usar juntos de manera til, puede haber algunas referencias
externas, en las que el cdigo de un archivo hace referencia a una posicin de otro archivo.
Esta referencia puede ser a una posicion de datos definida en un archivo y utilizada en otro,
o puede ser el punto de entrada de un procedimiento que aparece en el cdigo de un archivo
y se llama desde otro. El archivo con el cdigo de mquina relocalizable debe conservar la
informacin de la tabla de smbolos para cada posicin de datos o etiqueta de instruccin a
la que hace referencia externamente. Si no se sabe por anticipado a qu se va a hacer
referencia, es preciso incluir completa la tabla de smbolos del ensamblador como parte del
cdigo de mquina relocalizable.

Por ejemplo, el cdigo de (1.7) ira precedido de

a 0
b 4

Si un archivo cargado con (1.7) hiciera referencia a b, entonces esa referencia se
reemplazara por 4 ms el desplazamiento con el que se localizaron las posiciones del
archivo (1.7).



EL AGRUPAMIENTO DE LAS FASES


El estudio de las fases, trata la organizacin lgica de un compilador. En una
implantacin, a menudo se agrupan las actividades en dos o mas fases.
Con frecuencia, las fases se agrupan en una etapa inicial y una etapa final.

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 incluyen 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. La etapa inicial incluye, ademas, el
manejo de errores correspodiente a cada una de esas fases.

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,
ademas de la generacin de cdigo, junto con el manejo de errores y las operaciones con la
tabla de smbolos.

Se ha convertido en rutina el tomar la etapa inicial de un compilador y rehacer su
etapa final asociada para producir un compilador para el mismo lenguaje fuente en una
Pgina N 25
mquina distinta. Si la etapa final se disea con cuidado, incluso puede no ser necesario
redisearla demasiado.

Tambin resulta tentador compilar varios lenguajes distintos en el mismo lenguaje
intermedio y usar una etapa final comn para distintas etapas iniciales, obtenindose as
varios compiladores para una mquina. Sin embargo, dadas las sutiles diferencias en los
puntos de vista de los distintos lenguajes, slo se ha obtenido un xito limitado en ese
aspecto.

Pasadas :
Normalmente se aplican varias fases de la compilacin en una sola
pasada, que consiste en la lectura de un archivo de entrada y en la escritura de un archivo de
salida.

En la prctica hay muchas formas de agrupar en pasadas las fases de un compilador,
as que es preferible organizar el anlisis de la compilacin por las fases, en lugar de por las
pasadas.

Es comn agrupar varias fases en una pasada, y entrelazar la actividad de estas fases
durante la pasada. Por ejemplo, el anlisis lxico, el anlisis sintctico, el anlisis semntico y
la generacin de cdigo intermedio pueden agruparse en una pasada. En ese caso, la cadena
de componentes lxicos despus del anlisis lxico puede traducirse directamente a cdigo
intermedio. Con mas detalle, el analizador sintctico puede considerarse como el "
encargado " del control. Este obtiene los componentes lxicos cuando los necesita, llamando
al analizador lxico para que le proporcione el siguiente componente lxico. A medida que
se descubre la estructura gramatical, el analizador sintctico llama al generador de cdigo
intermedio para que haga el anlisis semntico y genere una parte del cdigo.


Reduccin del numero de pasadas


Es deseable tener relativamente pocas pasadas, dado que la lectura y escritura de
archivos intermedios lleva tiempo. Ademas si se agrupan varias fases dentro de una pasada,
pueden ser necesario tener que mantener el programa completo en memoria, porque una fase
puede necesitar informacin en un orden distinto al que produce una fase previa. La forma
interna del programa puede ser considerablemente mayor que el programa fuente o el
programa objeto, de modo que este espacio no es un tema trivial.

Para algunas fases, el agrupamiento en una pasada presenta pocos problemas. Por
ejemplo, como se menciono antes, la interfaz entre los analizadores lxicos y sintcticos a
menudo puede limitarse a un solo componente lxico. Por otra parte, muchas veces resulta
muy difcil generar cdigo hasta que se halla generado por completo la representacin
intermedia. Por ejemplo, los lenguajes como PL/I y ALGOL 68 permiten usar las variables
antes de declararlas. No se puede generar el cdigo objeto para una construccin si no se
Pgina N 26
conocen los tipos de las variables implicadas en esa construccin. De manera similar, la
mayora de los lenguajes admiten construcciones goto que saltan hacia adelante en el
cdigo. No se puede determinar la direccin objeto de dichos saltos hasta haber visto el
cdigo fuente implicado y haber generado cdigo objeto para el.

En algunos casos, es posible dejar un segmento en blanco para la informacin que
falta, y llenar la ranura cuando la informacin esta disponible. En particular, la generacin de
cdigo intermedio y de cdigo objeto a menudo se pueden fusionar en una sola pasada
utilizando la llamada tcnica " relleno de retroceso " ( backpatching ).

Recurdese que ya se analizo un ensamblador de dos pasadas, en el que la primera
pasada descubra todos los identificadores que representaban posiciones de memoria y
deduca sus direcciones al descubrirlas. Despus, en una segunda pasada sustitua las
direcciones por identificadores.

Se puede combinar la accin de las pasadas como sigue. Al encontrar una
proposicin en ensamblador que sea una referencia hacia adelante, por ejemplo,

GOTO destino

Se genera la estructura de una instruccin, con el cdigo de operacin de mquina
para GOTO y se dejan espacios en blanco para la direccin. Todas las instrucciones con
espacio en blanco para la direccin de destino se guardan en una lista asociada con la
entrada de destino en la tabla de smbolos. Los espacios se llenan cuando por fin se
encuentran una instruccin como

destino : MOV algo , R1

Y se determina el valor de destino; es la direccin de la instruccin en curso.
Entonces se hace el relleno de retroceso, recorriendo la lista de destino de todas las
instrucciones que necesitan su direccin, sustituyendo la direccin de destino en los espacios
en blanco que aparecen en los campos de direccin de esas instrucciones. Este enfoque es
fcil de implantar si las instrucciones se pueden guardar en memoria hasta que se hayan
determinado todas las direcciones de destino.

Este enfoque es razonable para un ensamblador que pueda guardar toda una salida en
memoria. Como las representaciones intermedia y final del cdigo para un ensamblador son
aproximadamente iguales, y con seguridad casi de la misma longitud, el relleno de retroceso
en toda la longitud del programa ensamblador no es inviable. Sin embargo, en un
compilador, con un cdigo intermedio que consuma mucho espacio, habr que tener cuidado
con la distancia en que se hace el relleno de retroceso.


HERRAMIENTAS PARA LA CONSTRUCCION DE COMPILADORES

Pgina N 27

El escritor del compilador, como cualquier programador, puede usar con provecho
herramientas de software tales como depuradores, administradores de versiones,
analizadores, etc. Ademas se han creado herramientas mas especializadas para ayudar a
implantar varias fases de un compilador.

Poco despus de escribirse el primer compilador, aparecieron sistemas para ayudar
en el proceso de escritura de compiladores. A menudo se hace referencia a estos sistemas
como compiladores de compiladores, generadores de compiladores o sistemas de
generadores de traductores. En gran parte, se orientan en torno a un modelo particular de
lenguaje, y son ms adecuados para generar compiladores de lenguajes similares al del
modelo.

Por ejemplo, es tentador suponer que los analizadores lxicos para todos los
lenguajes son en esencia iguales, excepto por las palabras clave y signos particulares que se
reconocen. Muchos compiladores de compiladores de hecho producen rutinas fijas de
anlisis lxico para usar en el compilador generado. Estas rutinas solo difieren en la lista de
palabras clave que reconocen, y esta lista es todo lo que debe proporcionar el usuario. El
planteamiento es valido, pero puede no ser funcional si se requiere que reconozca
componentes lxicos no estndar, como identificadores que pueden incluir ciertos caracteres
distintos de letras y dgitos.

Se han creado algunas herramientas generales para el diseo automtico de
componentes especficos de compilador. Estas herramientas utilizan lenguajes especializados
para especificar e implantar el componente, y puede utilizar algoritmos bastantes complejos.
Las herramientas ms efectivas son las que ocultan los detalles del algoritmo de generacin y
producen componentes que se pueden integrar con facilidad al resto del compilador.

La siguiente es una lista de algunas herramientas tiles para la construccin de
compiladores :


1) Generadores de analizadores sintcticos. Estos generadores producen
analizadores sintcticos, normalmente a partir de una entrada fundamentada en una
gramtica independiente del contexto. En los primeros compiladores, el anlisis sintctico
consuma no slo gran parte del tiempo de ejecucin del compilador, sino gran parte del
esfuerzo intelectual del escritor. Esta fase se considera ahora una de las mas fciles de
aplicar. Muchos de los generadores de analizadores sintcticos utilizan poderosos algoritmos
de anlisis sintctico, y son demasiado complejos para realizarlos manualmente.

2) Generadores de analizadores lxicos. Estas herramientas generan
automticamente analizadores lxicos, por lo general a partir de una especificacin basada
en expresiones regulares. La organizacin bsica del analizador lxico resultante es en
realidad un autmata finito.

Pgina N 28
3) Dispositivos de traduccin dirigida por la sintaxis. Estos producen grupos de
rutinas que recorren el rbol de anlisis sintctico, generando cdigo intermedio. La idea
bsica es que se asocian una o mas " traducciones " con cada nodo del rbol de anlisis
sintctico, y cada traduccin se define partiendo de traducciones en sus nodos vecinos en el
rbol.

4) Generadores automticos de cdigo. Tales herramientas toman un conjunto de
reglas que definen la traduccin de cada operacin del lenguaje intermedio al lenguaje de
mquina para la mquina objeto. Las reglas deben incluir suficiente detalle para poder
manejar los distintos mtodos de acceso posibles a los datos; por ejemplo, las variables
pueden estar en registros, en una posicin fija ( esttica ) de memoria o pueden tener
asignada una posicin en una pila. La tcnica fundamental es la de " concordancia de
plantillas ". Las proposiciones de cdigo intermedio se reemplazan por " plantillas " que
representan secuencias de instrucciones de mquina, de modo que las suposiciones sobre el
almacenamiento de las variables concuerden de plantilla a plantilla. Como suele haber
muchas opciones en relacin con la ubicacin de las variables ( por ejemplo, en uno o varios
registros de memoria), hay muchas formas posibles de " cubrir " el cdigo intermedio con un
conjunto dado de plantillas, y es necesario seleccionar una buena cobertura sin una explosin
combinatoria en el tiempo de ejecucin del compilador.

5) Dispositivos para anlisis de flujo de datos. Mucha de la informacin necesaria
para hacer una buena optimizacin de cdigo implica hacer un " anlisis de flujo de datos ",
que consiste en la recoleccin de informacin sobre la forma en que se transmiten los valores
de una parte de un programa a cada una de las otras partes. Las distintas tareas de esta
naturaleza se pueden efectuar escencialmente con la misma rutina, en la que el usuario
proporciona los detalles relativos a la relacin que hay entre las proposiciones en cdigo
intermedio y la informacin que se esta recolectando.



UN COMPILADOR SENCILLO DE UNA PASADA


Aqu se hace nfasis en la etapa inicial de un compilador, esto es, en el anlisis lxico,
el anlisis sintctico y la generacin de cdigo intermedio.


Perspectiva


Se puede definir un lenguaje de programacin describiendo el aspecto de sus
programas ( la sintaxis del lenguaje ) y el significado de sus programas ( la semntica del
lenguaje ). Para especificar la sintaxis de un lenguaje, se presenta una notacin muy usada
llamada gramticas independientes del contexto o BNF ( abreviatura en ingles de forma de
Pgina N 29
Backus-Naur ). Con las notaciones disponibles hoy es mucho mas difcil describir la
semntica de un lenguaje que su sintaxis.

Ademas, se puede usar de apoyo una gramtica independiente del contexto para
guiar la traduccin de programas. Una tcnica de compilacin orientada a la gramtica,
conocida como traduccin dirigida por la sintaxis, es muy til para organizar la etapa inicial
de un compilador.

Durante el estudio de la traduccin dirigida por la sintaxis, se construir un
compilador que traduce expresiones infijas a la forma posfija, una notacin en la que los
operadores aparecen despus de sus operandos. Por ejemplo, la forma postfija de la
expresin 9 - 5 +2 es 95 - 2 +.

La notacin postfija puede ser convertida directamente en cdigo por un computador
que haga todos sus clculos utilizando una estructura de datos de pila ( stack ). Se empieza a
construir un programa sencillo para traducir expresiones consistentes en dgitos separados
por los signos mas y menos en la forma postfija. Cuando las ideas bsicas resulten evidentes,
se extender el programa para poder manejar construcciones de lenguajes de programacin
ms generales. Cada traductor se forma por la extensin sistemtica del traductor anterior.

En este compilador, el analizador lxico convierte la cadena de caracteres de entrada
en una cadena de componentes lxicos que se convierte en la entrada para la siguiente fase,
como se muestra en la figura 2.1. El " traductor dirigido por la sintaxis" de la figura es una
combinacin de un analizador sintctico y un generador de cdigo intermedio. Una razn
para empezar con expresiones formadas por dgitos y operadores consiste en hacer que el
analizador lxico sea en un principio muy fcil; cada caracter de entrada forma un
componente lxico nico. Ms adelante, se ampla el lenguaje para incluir construcciones
lxicas, como nmeros, identificadores y palabras clave.

CADENA DE
COMPONENTES
LEXICOS
REPRESENTACION
INTERMEDIA
TRADUCTOR
DIRIGIDO POR
LA SINTAXIS
CADENA DE
CARACTERES
ANALIZADOR
LEXICO


Fig. 2.1 Estructura de la etapa inicial del compilador


DEFINICIN DE LA SINTAXIS


En esta seccin se introduce una notacin, llamada gramtica independiente del
contexto ( para abreviar, gramtica ), para especificar la sintaxis de un lenguaje.

Pgina N 30
Una gramtica describe la forma natural la estructura jerarquica de muchas
construcciones de los lenguajes de programacin. Por ejemplo, una proposicin if else en C
tiene la forma.

if ( expresin ) proposicin else proposicin

Esto es, la proposicin es la concatenacin de la palabra clave if, un parntesis que
abre, una expresin, un parntesis que cierra, una proposicin, la palabra clave else y otra
proposicin. ( En C no existe la palabra clave then ). Empleando la variable expr para
denotar una expresin, y la variable prop, para una proposicin, esta regla de estructuracin
se expresa

prop if ( expr ) prop else prop

Donde es posible leer la flecha como " puede tener la forma ". Dicha regla se
denominaproduccin. En una produccin, los elementos lxicos, como la palabra clave if y
los parntesis, se llaman componentes lxicos. Las variables expr y prop representan
secuencias de componentes lxicos y se llaman no terminales.


Una gramtica independiente del contexto tiene cuatro componentes :


1) Un conjunto de componentes lxicos, denominados smbolos terminales.

2) Un conjunto de no terminales.

3) Un conjunto de producciones, en el que cada produccin consta de un no
terminal, llamado lado izquierdo de la produccin, una flecha y una secuencia de
composicin de lxicos y no terminales, o ambos, llamado lado derecho de la produccin.

4) La denominacin de uno de los no terminales como smbolo inicial.

Se sigue la regla convencional de especificar las gramticas dando una lista de sus
producciones, donde las producciones del smbolo inicial se listan primero. Se supone que
los dgitos, los signos como <=y las cadenas en negritas, como while son terminales. Un
nombre en cursiva es un no terminal, y se supondr que cualquier nombre o smbolo que no
este en cursiva es un componente lxico. Por comodidad de notacin, las producciones con
el mismo no terminal del lado izquierdo pueden tener sus lados derechos agrupados, con los
lados derechos agrupados, con los lados derechos alternativos separados por el smbolo |,
que se leer "o".

Ejemplo:

Pgina N 31
En varios ejemplos de esta unidad se utilizan expresiones formadas por dgitos y
signos mas y menos, sea el caso, 9 - 5 +2, 3 - 1, y 7. Como un signo mas y menos debe
aparecer entre dos dgitos, se dice de dichas expresiones que son " listas de dgitos
separados por signos mas o menos ". La siguiente gramatica describe la sintaxis de esas
expresiones. Las producciones son :

lista lista +dgito (2.2)
lista lista - dgito (2.3)
lista dgito (2.4)
dgito 0 |1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 (2.5)

Los lados derechos de las tres producciones con no terminal lista del lado izquierdo
pueden agruparse de forma equivalente :

lista lista +dgito | lista - dgito | dgito

De acuerdo con las conversiones, los componentes lxicos de la gramtica son los
smbolos

+- 0 1 2 3 4 5 6 7 8 9

Los no terminales son los nombres en cursivas lista y dgito, siendo lista el no
terminal inicial, porque sus producciones se dieron primero.

Se dice que una produccin es para un no terminal si el no terminal aparece en el
lado izquierdo de la produccin. Una cadena de componentes lxicos es una secuencia de
cero o mas componentes lxicos. La cadena que contiene cero componentes lxicos, que se
escribeE , recibe el nombre de cadena vaca.

De una gramtica se derivan cadenas empezando con el smbolo inicial y
reemplazando repetidamente un no terminal por el lado derecho de una produccin para ese
no terminal. Las cadenas de componentes lxicos derivadas del smbolo inicial forman el
lenguaje que define la gramatica.

Ejemplo 2.2 :

El lenguaje definido por la gramatica del ejemplo 2.1 esta formado por listas de
digitos separados por los signos mas y menos.

Las diez producciones para el no terminal dgito hacen posible la representacin de
cualquiera de los componentes lxicos 0,1, . . . , 9. A partir de la produccin (2.4), un dgito
por si solo es una lista. Las producciones (2.2) y (2.3) expresan el hecho de que al tomar
cualquier lista y poner a continuacin un signo ms o menos, y despus otro dgito, se tiene
otra lista nueva.

Pgina N 32
Todo lo que se precisa para definir el lenguaje que interesa son las producciones
(2.2) a (2.5). Por ejemplo, se puede deducir que 9 - 5 +2 es una lista como sigue:

a) 9 es unalista de la produccin (2.4), dado que 9 es un dgito.

b) 9 - 5 es unalista de la produccin (2.3), dado que 9 es una lista y 5 es un dgito.

c) 9 - 5 +2 es unalista de la produccin (2.2), dado que 9 - 5 es una lista y 2 es un
dgito.

Este razonamiento se ilustra en la figura 2.2. Cada nodo en el rbol est etiquetado
con un smbolo de gramtica. Un nodo interior y sus hijos corresponden a una produccin;
el nodo interior corresponde al lado izquierdo de la produccin, los hijos, al lado derecho.
Estos rboles se conocen con el nombre de rboles de anlisis sintctico.

+
lista
2
digito
5 - 9
digito
lista digito
lista


Fig. 2.2 rbol de anlisis sintctico para 9-5+2 segn la gramtica del ejemplo 2.1


Ejemplo 2.3 :

Una clase algo distinta de listas es la secuencia de proposiciones separadas por los
smbolos de punto y coma que se encuentran en los bloques begin end de Pascal. Una
caracterstica de estas listas es que una lista vaca de proposiciones puede encontrarse entre
los componentes lxicos begin y end. Se puede empezar a desarrollar una gramatica para
los bloques begin-end incluyendo las producciones:

bloque begin props_opc end
props_opc lista_props | E
lista_props lista_props ; prop | prop

Pgina N 33
Obsrvese que el segundo lado derecho posible para props_opc ( " lista de
proposiciones opcional " ) es E , que representa la cadena de smbolos vaca. Esto es,
props_opc se puede representar por la cadena vacia, de modo que un bloque puede estar
formado por la cadena de dos componentes lxicos begin end. Fjese que las producciones
para lista_props son anlogas a las de lista del ejemplo 2.1, con un punto y coma en lugar
de un operador aritmtico, y prop, en lugar de dgito.


Arboles de anlisis sintctico


Un rbol de anlisis sintctico indica grficamente como el smbolo inicial de una
gramtica deriva una cadena del lenguaje. Si el no terminal A tiene una produccin :
A XYZ, entonces un rbol de anlisis sintctico puede tener un nodo interior etiquetado
con A y tres hijos etiquetados con X, Y y Z, de izquierda a derecha :
+ expresion expresion
expresion


Formalmente, dada una gramtica independiente del contexto, un rbol de anlisis
sintctico es un rbol con las propiedades siguientes :

1) La raz esta etiquetada con el smbolo inicial.

2) Cada hoja etiquetada con un componente lxico o con E

3) Si A es el no terminal que etiqueta a algn nodo interior y X1,X2,...,Xn son las
etiquetas de los hijos de ese nodo, de izquierda a derecha, entonces A X1,X2,...,Xn es una
produccin. Aqu X1,X2,...,Xn representa un smbolo que es un terminal o un no terminal.
Como caso especial, si A E , entonces un nodo etiquetado con A tiene solo un hijo
etiquetado con E .

Ejemplo 2.4 :

En la fig. 2.2 la raz esta etiquetada con lista, que es el smbolo inicial de la gramatica
del ejemplo 2.1. Los hijos de la raz estn etiquetados, de izquierda a derecha, lista, +, y
dgito. Obsrvese que

lista lista +dgito

es una produccin de la gramtica del ejemplo 2.1. El mismo patrn con - se repite en el hijo
izquierdo de la raz, y cada uno de los tres nodos etiquetados con dgito tiene un hijo de la
Pgina N 34
raz, y cada uno de los tres nodos etiquetados con dgito tiene un hijo que esta etiquetado
con un dgito.

Las hojas de un rbol de anlisis sintctico, ledas de izquierda a derecha, forman la
produccin del rbol, que es la cadena generada o derivada del no terminal de la raz del
rbol de anlisis sintctico. En la fig. 2.2 la cadena generada es 9 - 5 +2, y todas las hojas se
muestran en el nivel inferior. A partir de aqu, las hojas no se alinearan de esa forma.
Cualquier rbol imparte un orden natural, de izquierda a derecha, a sus hojas, basndose en
la idea de que si a y b son dos hijos con el mismo padre, y a esta a la izquierda de b,
entonces todos los descendientes de a estn a la izquierda de los descendientes de b.

Otra definicin del lenguaje generado por una gramatica es el conjunto de cadenas
que pueden ser generadas por un rbol de anlisis sintctico. El proceso de bsqueda de un
rbol de anlisis sintctico para una cadena dada de componentes lxicos se denomina
anlisis sintctico de esa cadena.

Ambigedad


Se ha de tener cuidado al considerar la estructura de una cadena segn una
gramtica. Aunque es evidente que cada rbol de anlisis sintctico deriva exactamente la
cadena que se lee en sus hojas, una gramatica puede tener mas de un rbol de anlisis
sintctico que genere una cadena dada de componentes lxicos. Esta clase de gramatica se
dice que es ambigua. Como una cadena que cuenta con mas de un rbol de anlisis
sintctico suele tener mas de un significado, para aplicaciones de compilacin es necesario
disear gramticas no ambiguas o utilizar gramticas ambiguas con reglas adicionales para
resolver las ambigedades.

Ejemplo 2.5 :

Supngase que no se hizo la definicin entre dgitos y listas segn el ejemplo 2.1. Se
podra haber escrito la gramatica

cadena cadena +cadena | cadena - cadena |0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |8 | 9

Combinando la nocin de dgito y lista en el no terminal cadena parece tener sentido
superficial, porque un solo dgito es un caso especial de una lista.

Sin embargo, en la figura 2.3 se muestra la expresin 9 - 5 +2 tiene ahora ms de un
rbol de anlisis sintctico. Los dos rboles de 9 - 5 +2 corresponden a dos formas de
agrupamiento entre parntesis de la expresin : ( 9 - 5 ) +2 y 9 - ( 5 +2 ). Esta segunda
forma de agrupamiento entre parntesis da a la expresin el valor 2, en lugar del valor
acostumbrado 6. La gramatica del ejemplo 2.1 no permite esta interpretacin.


Pgina N 35
Asociacin de operadores


Por conversin, 9 +5 +2 es equivalente a ( 9 +5 ) +2, y 9 - 5 - 2 es equivalente a
(9- 5 ) - 2. Cuando un operando con 5 tiene operadores a su izquierda y derecha, se
necesitan convenciones para decidir que operador considera ese operando. Se dice que el
operador +asocia a la izquierda, porque un operando que tenga un signo mas a ambos lados
es tomado por el operador que est a su izquierda. En la mayora de los lenguajes de
programacin, los cuatro operadores aritmticos, adicin, sustraccin, multiplicacin y
divisin son asociativos por la izquierda.


2
5
cadena -
9
cadena
+
cadena
cadena
cadena
2 5
+
9
Cadena -
cadena cadena
cadena
Cadena



Fig. 2.3 Dos rboles de anlisis sintctico para 9-5+2


Algunos operadores comunes, como la exponenciacin, son asociativos por la
derecha. Otro ejemplo anlogo, el operador de asignacin =en C es asociativo por la
derecha; en C, la expresin a =b =c se trata igual que la expresin a =( b =c ).

Las cadenas como a =b =c, con un operador asociativo por la derecha, son
generadas por la siguiente gramtica :
derecha letra = derecha | letra
letra a | b | . . . | z

El contraste entre un rbol de anlisis sintctico para un operador asociativo por la
izquierda como -, y un rbol de anlisis sintctico para un operador asociativo por la derecha
como =, se muestra la figura 2.4. Adviertase que el rbol de anlisis sintctico para 9 - 5 - 2
desciende hacia la izquierda, mientras que el rbol de anlisis sintctico para a =b =c
desciende hacia la derecha.

Pgina N 36
-
lista
2
digito
5
-
9
digito
lista digito
lista



c
letra b
=
a
letra =
letra derecha
derecha
derecha


Fig. 2.4 Arboles de analisis sintactico para operadores asociativos por la izquierda y
por la derecha




Precedencia de operadores


Considrese la expresin 9 +5 * 2 . Hay dos interpretaciones posibles de esta
expresin: ( 9 +5 ) * 2 o 9 +( 5 * 2 ). La asociacin de +y * no resuelve esa
ambigedad. Por esta razn, se necesita conocer la precedencia relativa de los operadores
cuando este presente mas de una clase de operadores.
Se dice que * tiene mayor precedencia que +si * considera sus operandos antes de
que lo haga +. En aritmtica elemental, la multiplicacin y divisin tienen mayor precedencia
Pgina N 37
que la adicin y la sustraccin. Por tanto, 5 es considerado por * en 9 +5 * 2 y en 9 * 5 +2;
es decir, las expresiones son equivalentes a 9 +( 5 * 2 ) y ( 9 * 5 ) +2, respectivamente.

Sintaxis de expresiones

Utilizando una tabla que muestre la asociatividad y precedencia de operadores se
puede construir una gramtica para expresiones aritmticas. Se empieza con los cuatro
operadores aritmticos bsicos y una tabla de precedencias, mostrando los operadores en
orden de precedencia creciente, con los operadores de la misma precedencia en la misma
linea :


Asociativos por la izquierda + -
Asociativos por la izquierda * /

Se crean dos no terminales expr y termino paro los dos niveles de precedencia, y un
no terminal adicional factor para generar unidades bsicas en las expresiones. Las unidades
bsicas de las expresiones son de momento dgitos y expresiones entre parntesis.

factor dgito | ( expr )

Ahora, considrese los operadores binarios * y /, que tienen mayor precedencia.
Como estos operadores asocian por la izquierda, las producciones son similares a las de las
listas que asocian por la izquierda.

termino termino * factor
| termino | factor
| factor

De manera similar, expr genera listas de trminos separados por los operadores
aditivos.
expr expr + termino
| expr - termino
| termino

Por lo tanto, la gramatica resultante es

expr expr + termino | expr - termino | termino
termino termino * factor | termino * factor | factor
factor dgito | ( expr )

Esta gramatica considera una lista de trminos separados por los signos +o -, y un
termino, como una lista de factores separados por los signos * o /. Advirtase que cualquier
expresin entre parntesis es un factor, de manera que con los parentesis se pueden
Pgina N 38
desarrollar expresiones que tengan anidamiento de profundidad arbitraria ( y tambin arboles
de profundidad arbitraria ).


Sintaxis de proposiciones


Las palabras clave permiten reconocer proposiciones en la mayora de los lenguajes.
Todas las proposiciones de Pascal comienzan con una palabra clave, excepto las
asignaciones y las llamadas a procedimientos. Algunas proposiciones de Pascal se definen
por medio de la siguiente gramatica ( ambigua ) en la que el componente lxico id
representa un identificador.




prop id : =expr
| if expr then prop
| if expr then prop else prop
| while expr do prop
| begin props_opc end

El no terminal props_opc genera una lista de proposiciones, posiblemente vaca,
separada por los smbolos de punto y coma, utilizando las producciones del ejemplo 2.3.


TRADUCCIN DIRIGIDA POR LA SINTAXIS


Para traducir una construccin de un lenguaje de programacin, un compilador
puede necesitar tener en cuenta muchas caractersticas, adems del cdigo generado por la
construccin. Por ejemplo, puede ocurrir que el compilador necesite conocer el tipo de la
construccin, la posicin de la primera instruccin del cdigo objeto o el nmero de
instrucciones generadas. Por tanto, los atributos asociados con las construcciones se
mencionan de manera abstracta. Un atributo puede representar cualquier cantidad, por
ejemplo, un tipo, una cadena, una posicin de memoria o cualquier otra cosa.

En esta seccin, se presenta un formalismo llamado definicin dirigida por la sintaxis
para especificar las traducciones para las construcciones de lenguajes de programacin. Una
definicin dirigida por la sintaxis especifica la traduccin de una construccin en funcin de
atributos asociados con sus componentes sintcticos.

Para especificar traducciones, se introduce tambin una notacin mas orientada a
procedimientos, denominada esquema de traduccin. Utilizaremos esquemas de traduccin
para producir expresiones infijas a la forma posfija.
Pgina N 39


Notacin posfija


La notacin posfija de una expresin E se puede definir de manera inductiva como
sigue :


1. Si E es una variable o una constante, entonces la notacin posfija E es tambin E.

2. Si E es una expresin de la forma E1 op E2, donde op es cualquier operador
binario, entonces la notacin posfija de E es E1' E2' op, donde E1' y E2' son las notaciones
posfijas de E1 y E2, respectivamente.

3. Si E es una expresin de la forma ( E1 ), entonces la notacin posfija de E1 es
tambin la notacin posfija E.

La notacin posfija no necesita parntesis, porque la posicin y la ariedad ( nmero
de argumento ) de los operadores permiten solo una descodificacin de una expresin
posfija por ejemplo, la notacion posfija de ( 9 - 5 ) +2 es 95 - 2 + y la notacin posfija de
9 - ( 5 +2 ) es 952 +-.


Definiciones dirigidas por la sintaxis


Una definicin dirigida por la sintaxis utiliza una gramtica independiente del
contexto para especificar la estructura sintctica de la entrada. A cada smbolo de la
gramatica le asocia un conjunto de atributos y a cada produccin, un conjunto de reglas
semnticas para calcular los valores de los atributos asociados con los smbolos que
aparecen en esa produccin. La gramatica y el conjunto de reglas semnticas constituyen la
definicin dirigida por la sintaxis.

Una traduccin es una transformacin de una entrada en una salida. La salida para
cada entrada x se especifica de la forma siguiente. Primero, se construye un rbol de anlisis
sintctico para x. Supngase que un nodo n del rbol de anlisis sintctico esta etiquetado
con el smbolo X de la gramatica. Se describe X.a para indicar el valor del atributo a de X en
ese nodo. El valo X.a en n se calcula por la regla semantica para el atributo a asociado con
la produccion de X utilizada en el nodo n. Al rbol de anlisis sintctico que muestre los
valores de los atributos en cada nodo se dice que es un rbol de anlisis sintctico con
anotaciones.

Atributos sintetizados

Pgina N 40

Se dice que un atributo esta sintetizado si su valor en un nodo del rbol de anlisis
sintctico se determina a partir de los valores de atributos de los hijos del nodo. Los
atributos sintetizados tienen la atractiva propiedad de que se pueden calcular durante un solo
recorrido ascendente del rbol de anlisis sintctico.

Ejemplo 2.6 :
En la figura 2.5 se muestra una definicin dirigida por la sintaxis para
traducir expresiones formadas por dgitos separados por los signos ms o menos, a notacin
posfija. A cada no terminal est asociado un atributo t con un valor de la cadena que
representa la notacin posfija de la expresion ( produccin ) generada por ese no terminal en
un rbol de anlisis sintctico.






Produccin Regla Semntica

expr expr1 + trmino
expr expr1 - trmino
expr trmino
trmino 0
trmino 1
. . .

trmino 9


expr.t : =expr1.t || trmino.t || '+'
expr.t : = expr1.t || trmino.t || '-'
expr.t : = trmino.t
trmino.t : = '0'
trmino.t : = '1'
. . .

trmino.t : = '9'


FIG. 2.5 Definicin dirigida por la sintaxis para traduccin de infija a posfija


La forma posfija de un dgito es el propio dgito; por ejemplo, la regla semntica
asociada con la produccin termino 9 define que termino.t es 9 cuando esta produccin
se use en un nodo de un rbol de anlisis sintctico.

Cuando se aplica la produccin expr trmino, el valor de trmino.t se transforma
en el valor de expr.t.

La produccin expr expr1 +termino deriva una expresin con un operador ms
( el subindice en expr1 distingue el caso deexpr en el lado derecho de aquel que est en el
lado izquierdo ). El operando izquierdo del operador ms est dado por expr1, y el operando
derecho, por trmino. La regla semntica
Pgina N 41

expr.t : =expr1.t|| trmino.t || '+'

asociada con esta produccin define el valor del atributo expr.t mediante la concatenacin de
las formas podfijas expr1.t y trmino.t de los operandos izquierdo y derecho,
respectivamente, y despus agregando el signo ms. El operador || en las reglas semnticas
representa la concatenacin de cadenas.

La figura 2.6 comprende el rbol de anlisis sintctico con anotaciones
correspondientes al rbol de la figura 2.2. El valor del atributo t en cada nodo se calcul por
la regla semntica asociada con la produccin empleada en ese nodo. El valor del atributo en
la raz es la anotacin posfija de la cadena generada por el rbol de anlisis sintctico.

+
expr.t =95-2+
2
termino.t =2
5 - 9
termino.t =9
expr.t =9 termino.t =5
expr.t =95-



Fig. 2.6 Valores de atributos en los nodos de un rbol de anlisis sintctico

Ejemplo 2.7 :

Supngase que un robot se puede instruir para moverse un paso al este, norte, oeste
o sur desde su posicin inicial. Una secuencia de estas instrucciones se genera con la
gramatica siguiente:

sec sec instr | comienza
instr este | norte | oeste | sur

En la figura 2.7 se muestran los cambios en la posicin del robot si se le proporciona
la entrada
comienza oeste sur este este este norte norte
Pgina N 42
X
Y
.
norte
sur norte
este este
este
oeste

En la figura, una posicin se marca con un par ( x,y ), donde x e y representan el
numero de pasos al este y al norte, respectivamente, desde la posicin inicial. ( Si x es
negativo, entonces el robot se encuentra al oeste de la posicin inicial; de manera similar, si
y es negativo, entonces el robot se encuentra al sur de la posicin inicial ).

Para traducir una secuencia de instrucciones a una posicin del robot, se construir
una definicin dirigida por la sintaxis. Se usaran dos atributos, sec.x y sec.y para seguir la
posicin que resulta de una secuencia de instrucciones generada por el no terminal sec. Al
principio, sec genera comienza, asignado el valor inicial 0 a sec.x y sec.y, segn se indica
en el nodo interior del rbol de anlisis sintctico de comienza oeste sur, situado en el
extremo izquierdo de la figura 2.8.


sec.x =- 1
sec.y =- 1
sur
instr.dx =0
instr.dy =- 1
oeste comienza
sec.x =0
sec.y =0
instr.dx =- 1
instr.dy =0
sec.x =- 1
sec.y =0



Fig. 2.8 rbol de anlisis sintctico con anotaciones para comienza oeste sur.

Pgina N 43

El cambio en la posicin a causa de una instruccin individual derivada de instr se da
por los atributos instr.dx e instr.dy. Por ejemplo, si instr deriva oeste, entonces
instr.dx =-1 einstr.dy =0. Supngase que una secuencia sec se forma con una secuencia
sec1 seguida de una nueva instruccin instr. Entonces, la nueva posicin del robot esta dada
por las reglas


sec.x : =sec1.x +instr.dx
sec.y : =sec1.y +instr.dy


En la figura 2.9 se muestra una definicin dirigida por la sintaxis para traducir una
secuencia de instrucciones a una posicin del robot.



Produccin Regla Semntica
sec comienza sec.x : =0
sec.y : =0
sec sec1 instr sec.x : =sec1.x +instr.dx
sec.y : =sec1.y +instr.dy
instr este instr.dx : =1
instr.dy : =0
instr norte instr.dx : =0
instr.dy : =1
instr oeste instr.dx : =-1
instr.dy : =0
instr sur instr.dx : =0
instr.dy : =-1


Fig. 2.9 Definicin dirigida por la sintaxis de la posicin del robot



Recorridos en profundidad


Una definicin dirigida por la sintaxis no impone ningn orden especfico a la
evaluacin de atributos en un rbol de anlisis sintctico; cualquier orden de evaluacin que
calcule un atributo a, despus de haber calculado todos los dems atributos de los que a
depende es aceptable. En general, es posible que haya que evaluar algunos atributos cuando
se llega por primera vez a un nodo durante un recorrido del rbol de anlisis sintctico,
Pgina N 44
otros, despus de haber visitado todos sus hijos o en algn punto entre las visitas a los hijos
del nodo.

Todas las traducciones de esta seccin se pueden hacer evaluando las reglas
semnticas de los atributos en un rbol de anlisis sintctico en un orden predeterminado. Un
recorrido de un rbol comienza en la raz y visita cada nodo del rbol en un orden
indeterminado. Nosotros mediante el recorrido en profundidad que se define en la figura
2.10 evaluaremos las reglas semanticas. Este recorrido empieza en la raz y visita
recursivamente a los hijos de cada nodo en orden de izquierda a derecha, como se muestra
en la figura 2.11. Las reglas semnticas en un nodo dado se evalan cuando todos los
descendentes de ese nodo hayan sido visitados. Se llama " en profundidad " porque
siempre que pueda, visita a un hijo no visitado de un nodo, de modo que intenta visitar los
nodos mas alejados de la raz lo antes posible.






procedure visita (n: nodo);
begin
for cada hijo m de n, de izquierda a derecha do
visita (m);
evala reglas semnticas en el nodo n
end


Fig. 2.10 Un recorrido en profundidad de un rbol


Esquema de traduccin


En el resto de este captulo, se usa una especificacin orientada a procedimientos
para definir una traduccin. Un esquema de traduccin es una gramatica independiente del
contexto en la que se encuentran intercalados, en los lados derechos de las producciones,
fragmentos de programa llamados acciones semnticas. Un esquema de traduccin es como
una definicin dirigida por la sintaxis, con la exencin de que el orden de evaluacin de las
reglas semnticas se muestra explcitamente. La posicin en la que se ejecuta alguna accin
se da entre llaves y se escribe en el lado derecho de una produccin, por ejemplo,

resto +trmino { print ('+')} resto1

Pgina N 45


Fig. 2.11. Ejemplo de recorrido en profundidad de un rbol

Un esquema de traduccin genera una salida para cada frase x generada por la
gramtica subyacente mediante la ejecucin de las acciones en el orden en que aparecen
durante un recorrido en profundidad de un rbol de anlisis sintctico para x.

Sea el caso de un rbol de anlisis sintctico con un nodo etiquetado con resto que
represente a esta produccin. La accin {print ('+') } se efectuara despus de recorrer el
subarbol de termino, pero antes de visitar al hijo resto1.

resto1 + termi {print ('+')}
resto


Fig. 2.12. Construccin de una hoja adicional correspondiente a una secuencia
semntica


Cuando se dibuja un rbol de anlisis sintctico de un esquema de traduccin, se
indica una accin construyendo un hijo adicional, conectado al nodo para su produccin por
una linea de puntos. Por ejemplo, la parte del rbol de anlisis sintctico para la produccin
y la accin anteriores se representan en la figura 2.12. El nodo para una accin semntica no
tiene hijos, de modo que la accin se realiza cuando se ve por primera vez ese nodo.


Emisin de una traduccin


Pgina N 46
En este captulo, las acciones semnticas en los esquemas de traduccin escribirn la
salida de una traduccin en un archivo, una cadena o un caracter a la vez. Por ejemplo, se
traduce 9 - 5 +2 a 95 - 2 + imprimiendo cada caracter de 9 - 5 +2 justo una vez, sin usar
ningn almacenamiento para la traduccin de subexpresiones. Cuando la salida se crea
incrementalmente de este modo, es importante el orden en que se imprimen los caracteres.

Advirtase que las definiciones dirigidas por las sintaxis mencionadas hasta ahora
tienen la siguiente propiedad importante. La cadena que representa la traduccin del no
terminal del lado izquierdo de cada produccin es la concatenacin de las traducciones de
los no terminales de la derecha, en igual orden que en la produccin, con algunas cadenas
adicionales ( tal vez ninguna ) intercaladas. Con esta propiedad, una definicin dirigida por
la sintaxis se denomina simple. Por ejemplo, considrense la primera produccin y la regla
semntica de la definicin dirigida por la sintaxis de la figura 2.5 :



Ejemplo 1 :


Produccin

Regla Semntica

expr expr1 +trmino

expr.t : =expr1.t || trmino.t || '+' (2.6)


Aqu, la traduccin expr.t es la concatenacin de las traducciones de expr1 y termino,
seguida del smbolo +. Advirtase que expre1 aparece antes que termino en el lado derecho
de la produccin.
Entre termino.t y resto1.t aparece una cadena adicional en

Ejemplo 2 :


Produccin

Regla Semntica

resto +trmino resto1

resto.t : =trmino.t || '+' || resto1.t (2.7)


pero de nuevo, el no terminal termino aparece antes que resto1 en el lado derecho.

Las definiciones simples dirigidas por la sintaxis se pueden implantar con esquemas
de traduccin en los que las acciones impriman las cadenas adicionales en el orden en que
aparecen en la definicin. Las acciones de las siguientes producciones imprimen las cadenas
adicionales de (2.6) y (2.7), respectivamente:

expr.t expr1 + trmino { print ('+') }
resto.t +trmino { print ('+') } resto1
Pgina N 47

Ejemplo 2.8

La figura 2.5 contiene una definicin simple para traducir expresiones a la forma
posfija. En la figura 2.13 se da un esquema de traduccin derivado de esta definicin y en la
figura 2.14 se muestra un rbol de anlisis sintctico con acciones para 9 - 5 +2. Obsrvese
que aunque las figuras 2.6 y 2.14 representan la misma transformacin de entrada a salida, la
traduccin se construye de manera distinta en los dos casos; la figura 2.6 vincula la salida a
la raz del rbol de anlisis sintctico, mientras que la figura 2.14 imprime la salida de forma
incremental.

La raz de la figura 2.14 representa la primera produccin de la figura 2.13.


expr expr + trmino { print ('+') }
expr expr - trmino { print ('-') }
expr trmino
trmino 0 { print ('0') }
trmino 1 { print ('1') }
. . .
trmino 9 { print ('9') }


Fig 2.13. Acciones que traducen expresiones a la notacin posfija
En un recorrido en profundidad, primero se realizan todas las acciones del subarbol
para el operando izquierdo expr cuando se recorre el subarbol situado ms a la izquierda de
la raz, despus se visita la hoja +, en la que no hay ninguna accin, a continuacin realizan
las acciones del subarbol para el operando derecho trmino y, por ultimo, se realiza
la accin semntica { print ('+') } en el nodo adicional.

Como las producciones para termino tienen solo un dgito en el lado derecho, ese
dgito se imprime por medio de las acciones para las producciones. No se necesita ninguna
salida para la produccin expr trmino, y solo se requiere imprimir el operador en las dos
primeras producciones. Cuando se ejecutan durante un recorrido en profundidad del rbol de
anlisis sintctico, las acciones de la figura 2.14 imprimen 95 - 2 +.

Pgina N 48
{print ('9')} 9
termino
{print ('5')}
{print ('-')} {print ('2')}
{print ('+')}
+
expr
2
termino
5
- expr termino
expr



Fig. 2.14 Acciones que traducen 9 - 5 + 2 a 95 - 2 +


Como regla general, la mayora de los mtodos de anlisis sintctico procesan su
entrada de izquierda a derecha de forma " voraz ; esto es, construyendo el mximo posible
de un rbol de anlisis sintctico antes de leer el siguiente componente lxico de la entrada.
En un esquema de traduccin simple ( obtenido de una definicin simple dirigida por la
sintaxis ) las acciones se efectan tambin de izquierda a derecha. Por lo tanto, para
implementar un esquema de traduccin simple se pueden ejecutar las acciones semnticas
durante el anlisis sintctico; no es necesario construir el rbol de anlisis sintctico.


ANLISIS SINTCTICO


El anlisis es el proceso de determinar si una cadena de componentes lxicos puede
ser generada por una gramatica. En el estudio de este problema, es til pensar en construir
un rbol de anlisis sintctico, aunque de hecho, un compilador no lo construya. Sin
embargo, un analizador sintctico deber poder construir el rbol, pues de otro modo, no se
puede garantizar que la traduccin sea correcta.

En esta seccin se introduce un mtodo de anlisis sintctico que puede aplicarse en
la construccin de traductores dirigidos por la sintaxis.

La mayora de los mtodos de anlisis sintcticos estn comprendidos en dos clases,
llamadas descendentes y ascendentes. Estos trminos hacen referencia al, orden en que se
construyen los nodos del rbol de anlisis sintctico.

El anlisis sintctico ascendente puede manejar una clase mayor de gramticas y
esquemas de traduccin, de modo que las herramientas de software para generar
Pgina N 49
analizadores sintcticos directamente a partir de las gramticas tienden a utilizar mtodos
ascendentes.


Anlisis sintctico descendente


La siguiente gramatica genera un subconjunto de los tipos de Pascal. Se utiliza el
componente lxico puntopunto para enfatizar que la secuencia de caracteres se trata como
una unidad.


tipo simple
| l id
| array [ simple ] of tipo
simple integer (2.8)
| char
| nm puntopunto nm


La construccin descendente de un rbol de anlisis sintctico se hace empezando
por la raz, etiquetada con el no terminal inicial, y realizando de forma repetida los dos pasos
siguientes


Pgina N 50
E )
D )
C )
B )
A )
integer
num simple num puntopunto
array simple tipo ]
tipo
of [
num simple num puntopunto
array simple tipo ]
tipo
of [
num puntopunto num
array simple tipo ]
tipo
of [
tipo
array simple tipo ]
tipo
of [


Fig. 2.15. Pasos en la construccin descendente de un rbol de anlisis
sintctico.


1. En el nodo n, etiquetado con el no terminal A, seleccinese una de las
producciones para A y constryase los hijos de n para los smbolos del lado derecho de la
produccin.
2. Encuntrese el siguiente nodo en el que ha de construirse un subarbol.

Para algunas gramticas, los pasos anteriores se aplican durante un examen sencillo,
de izquierda a derecha, de la cadena de entrada. Muchas veces, el componente lxico en
Pgina N 51
curso analizado en la entrada se denomina smbolo de preanlisis. Inicialmente, el smbolo
de preanlisis es el primero, es decir, el componente lxico situado ms a la izquierda de la
cadena de entrada. La figura 2.16 ilustra el anlisis sintctico de la cadena.

array [ nm puntopunto nm ] of integer

Inicialmente, el componente lxico array es el smbolo de preanlisis y la parte
conocida del rbol de anlisis sintctico consiste en la raz, etiquetada con el no terminal
inicial tipo en la figura 2.16 (a). El objetivo es construir el resto del rbol de anlisis de
modo que la cadena generada por l concuerde con la cadena de entrada.

Para que ocurra una concordancia, el no terminal tipo de la figura 2.16 (a) debe
derivar una cadena que empiece con el smbolo del preanlisis array. En la gramatica (2.8)
hay exactamente una produccin para tipo que deriva tal cadena, por lo que se elige, y se
construyen los hijos de la raz etiquetados con los smbolos del lado derecho de la
produccin.

Cada una de las tres imgenes de la figura 2.16 tiene flechas que indican el smbolo
de preanlisis de la entrada y el nodo que se esta considerando. Cuando se han construido
los hijos de un nodo, despus se considera el hijo que esta mas a la izquierda. En la figura
2.16 (b), se han construido los hijos de la raz, y se est considerando el hijo situado ms a la
izquierda, etiquetado con array.

cuando el nodo que se esta considerando en el rbol de anlisis sintctico es el de un
terminal y el terminal concuerda con el smbolo de preanlisis, se avanza en el rbol de
anlisis sintctico y en la entrada. El siguiente componente lxico de la entrada se convierte
en el nuevo smbolo de preanlisis y se considera el siguiente hijo del rbol de anlisis
sintctico. En la figura 2.16 (c), la flecha del rbol de anlisis sintctico avanza hasta el
siguiente hijo de la raz y la flecha de la entrada avanza hasta el siguiente componente lxico.
Despus del siguiente avance, la flecha del rbol de anlisis sintctico apuntara al hijo
etiquetado con el no terminal simple.

Cuando se considera un nodo etiquetado con un no terminal, se repite el proceso de
seleccionar una produccin para el no terminal.

En general, la seleccin de una produccin para un no terminal puede implicar un
proceso de prueba y error, esto es, se puede probar con una produccin y retroceder para
hacer el intento con otra produccin si la primera resulta inadecuada.

Una produccin es inadecuada cuando, despus de usar las producciones, no se
puede completar el rbol para que concuerde con la cadena de entrada. Sin embargo, hay un
caso de especial importancia, llamado analizador sintctico predictivo, en el que no hay
retroceso.


Pgina N 52

Arbol de
analisis tipo
sintactico
a) l
array [ num puntopunto ] of
integer
entrada l


Arbol de analisis sintactico
b)
array simple tipo ]
tipo
of [

array [ num puntopunto num ] of
integer
entrada l


Arbol de analisis sintactico
c )
array simple tipo ]
tipo
of [

array [ num puntopunto num ] of
integer
entrada l



Fig. 2.16 Analisis sintactico descendente durante el examen de la entrada de izquierda
a derecha



Anlisis sintctico predictivo


El anlisis sintctico descendente recursivo es un mtodo descendente en el que se
ejecuta un conjunto de procedimientos recursivos para procesar la entrada. A cada no
Pgina N 53
terminal de una gramatica se asocia un procedimiento. Aqu, se considera una forma especial
de anlisis sintctico descendente recursivo, llamado anlisis sintctico predictivo, en el que
el smbolo de preanlisis determina sin ambigedad el procedimiento seleccionado para cada
no terminal. La secuencia de procedimientos llamados en el procesamiento de la entrada
define implcitamente un rbol de anlisis sintctico para la entrada.

El analizador sintctico predictivo de la figura 2.17 consta de procedimientos para
los no terminales tipo y simple de la gramatica (2.8) y un procedimiento adicional. parea. Se
usa parea para simplificar el cdigo de tipo y simple; si su argumento t concuerda con el
smbolo de preanlisis, avanza hacia el siguiente componente lxico de entrada. De ese
modo, parea modifica a la variable preanlisis, que es el componente lxico en curso que
acaba de entregar el anlisis lxico.

El anlisis sintctico con una llamada al procedimiento del no terminal inicial tipo de
esta gramatica. Con la misma entrada que en la figura 2.16, preanlisis es inicialmente el
primer componente lxico array. El procedimiento tipo ejecuta el cdigo.


parea (array); parea ('['); simple; parea (']') parea (of); tipo (2.9)


que corresponde al lado derecho de la produccin


tipo array [ simple ] of tipo


Obsrvese que cada terminal del lado derecho se parea con el smbolo de preanlisis
y que cada no terminal de la derecha proporciona una llamada a su procedimiento.

Con la entrada de la figura 2.16, despus de haber concordancia entre los
componentes lxicos array y [, el smbolo de preanlisis es nm. En este punto se llama al
procedimiento simple y se ejecuta el cdigo de su cuerpo.


parea ( nm ); parea ( puntopunto ); parea ( nm )




Figura 2.17 Seudocdigo de un analizador sintctico predictivo




Pgina N 54
procedure parea (t: complex);
begin
if preanlisis =t then
preanlisis : =sigcomplex
else error
end;

procedure tipo;
begin
if preanlisis is in {integer, char, nm } then
simple
else if preanlisis =' | ' then begin
parea ( ' | ' ); parea (id)
end
else if preanlisis =array then begin
parea (array); parea('['); parea(of); tipo
end;
else error
end;



procedure simple;
begin
if preanlisis =integer then
parea(integer)
else if preanlisis =char then
parea(char)
else if preanlisis =num then begin
parea (nm); parea (puntopunto); parea (nm)
end
else error
end;



El smbolo de preanlisis gua la seleccin de la produccin que se desea utilizar.

Si el lado derecho de una produccin empieza con un componente lxico, entonces la
produccin se usa cuando el smbolo de preanlisis coincide con el componente lxico.
Ahora, considrese un lado derecho que empiece con un no terminal, como en el caso

tipo simple

Pgina N 55
Esta produccin se emplea cuando el smbolo de preanlisis se puede generar a partir
de simple. Por ejemplo, durante la ejecucin del fragmento de cdigo (2.9), supngase que
el smbolo de preanlisis es integer cuando el control llega a la llamada a procedimiento tipo.
No hay ninguna produccin para tipo que comience con el componente lxico integer. Sin
embargo, si hay una produccion para simple que comience con integer, de manera que se usa
la produccin (2.10) teniendo en cuenta que tipo llama al procedimiento simple en el
smbolo de preanlisis integer.

El anlisis sintctico predictivo depende de la informacin sobre los primeros
smbolos que pueden ser generados por el lado derecho de una produccin. Para precisar,
sea o el lado derecho de una produccin para el no terminal A. Se define PRIMERO (o)
como el conjunto de componentes lxicos que opere como los primeros smbolos de una o
mas cadenas generadas a partir de o. Si o es e o puede generar e, entonces e tambin esta
en PRIMERO(o).

Por ejemplo :

PRIMERO (simple) ={ integer, char, num }
PRIMERO ( | id) ={ | }
PRIMERO ( array [ simple ] of tipo ) ={ array }


En la practica, muchos lados derechos de una produccin comienzan con
componentes lxicos, lo que simplifica la construccin de conjuntos PRIMERO.

Se deben considerar los conjuntos PRIMERO si hay dos producciones A o
y
A |. El anlisis sintctico descendente recursivo sin retroceso requiere que
PRIMERO(o) y PRIMERO (|) sean disjuntos. El smbolo de preanlisis se puede usar
entonces para decidir el tipo de produccin a utilizar; si el smbolo de preanlisis esta en
PRIMERO (o), entonces se usa o. De otro modo, si el smbolo de preanlisis esta en
PRIMERO(|), entonces se usa |.


Cuando se usan las producciones


Las producciones con e del lado derecho necesitan un tratamiento especial. El
analizador sintctico descendente recursivo usara una produccin e por defecto cuando no
se puede aplicar otra produccin. Por ejemplo, considerece:


prop begin props_opc end
props_opc lista_props | e

Pgina N 56

Durante el anlisis sintctico de props_opc, si el smbolo de preanlisis no esta en
PRIMERO(lista_props), entonces se usa la produccin e. Esta eleccin es justo la correcta
si el smbolo de preanlisis es end. Cualquier smbolo de preanlisis diferente de end dar
como resultado un error, que se detectara durante el anlisis sintctico de prop.




Diseo de un analizador sintctico predictivo


Un analizador sintctico es un programa que consiste en un procedimiento para
cada no terminal. Cada procedimiento hace dos cosas :

1. Decide la produccin que utilizar analizando el smbolo de preanlisis. Si el smbolo
de preanlisis est en PRIMERO (o), se usa la produccin con lado derecho o. Si hay un
conflicto entre dos lados derechos de cualquier smbolo de preanlisis, entonces en esa
gramatica no se puede emplear este mtodo de anlisis sintctico. Si el smbolo de
preanlisis no esta en el conjunto PRIMERO de ningn otro lado derecho, se usa una
produccin con e en el lado derecho.


2. El procedimiento usa una produccin imitando al lado derecho. Un no terminal como
resultado una llamada al procedimiento del no terminal, y un componente lxico que
coincide con el smbolo de preanlisis da como resultado que se lea el siguiente componente
lxico. Si el componente lxico de la produccin no coincide en algn punto con el smbolo
de preanlisis, se declara un error. La figura 2.17 es el resultado de aplicar estas reglas a la
gramatica (2.8).

Al igual que crea un esquema de traduccin extendiendo una gramatica, se puede
crear un traductor dirigido por la sintaxis extendiendo un analizador sintctico predictivo.


Recursividad por la izquierda


Es posible que un analizador sintctico descendente recursivo entre en un lazo
( bucle ) indefinido. Hay un problema con producciones recursivas por la izquierda del tipo

expr expr + trmino

en la que el smbolo ms a la izquierda del lado derecho es el mismo que el no terminal del
lado izquierdo de la produccin. Supngase que el procedimiento para expr decide aplicar
esta produccin. El lado derecho comienza con expr, de modo que el procedimiento para
Pgina N 57
expr se llama recursivamente, y el analizador sintctico entra en un lazo indefinido.
Obsrvese que el smbolo de preanlisis cambia solo cuando coincide un terminal del lado
derecho. Como la produccin comienza con el no terminal expr, no se realiza ningn cambio
en la entrada entre llamadas recursivas, lo cual causa el lazo infinito.

Reescribiendo la produccin transgresora, se puede eliminar una produccin
recursiva por la izquierda. Considrese un no terminal A con dos producciones

A Ao | |

donde o y | son secuencias terminales y no terminales que no comienzan con A.

(a )
A
A
A
. . .
A

( b )
*
R
R
. . .
R
R
A

Fig. 2.18 Formas recursivas izquierda y derecha de generar una cadena


Por ejemplo, en

expr expr + trmino | trmino

A = expr, o =+trmino, y | =trmino.

El no terminal A es recursivo por la izquierda, porque la produccin A Ao tiene la
propiaA como el smbolo situado mas a la izquierda del lado derecho. La aplicacin repetida
de esta produccin forma una secuencia de smbolos o a la derecha de A, como en el caso
Pgina N 58
de la figura 2.18 ( a ). Cuando finalmente se reemplaza A por |, se tiene una | seguida de
una secuencia de cero o mas o.

El mismo efecto se puede lograr, como en la figura 2.18 ( b ), reescribiendo las
producciones para A de la forma siguiente.

A | R
R o R | e

Aqu, R es un terminal nuevo. La produccin R o R es recursiva por la derecha, porque
esta produccin para R tiene el propio R como ultimo smbolo del lado derecho. Las
producciones recursivas por la derecha forman arboles de crecimiento descendente hacia la
derecha, como en la figura 2.18 ( b ).


TRADUCTOR DE EXPRESIONES SIMPLES


Utilizando las tcnicas de las tres secciones anteriores, se construir ahora un
traductor dirigido a la sintaxis, en forma de programa operativo en C, en el cual traduce
expresiones aritmticas de la forma postfija. Con objeto de conservar el programa inicial
manejablemente pequeo, se empieza con expresiones compuestas de dgitos separados por
los signos ms y menos. El lenguaje se amplia en las dos secciones siguientes para incluir
nmeros, identificadores y otros operadores. Teniendo en cuenta que las expresiones
aparecen como una construccin en tantos lenguajes, vale la pena estudiar en detalle su
traduccin.


expr expr + trmino { print ('+') }
expr expr - trmino { print ('-') }
expr trmino
trmnio 0 { print ('0')}
trmnio 1 { print ('1')}
...
trmnio 9 { print ('9')}


Fig. 2.19 Especificacin inicial del traductor infijo a posfijo



A menudo, un esquema de traduccin dirigida a la sintaxis puede servir como
especificacin de un traductor. El esquema de la figura 2.19 ( repetido de la fig. 2.13) se usa
como definicin de la traduccin que ha de ejecutarse. Muchas veces, se da el caso de tener
que modificar la gramatica subyacente de un esquema dado antes de poderla analizar por un
Pgina N 59
analizador sintctico predictivo. En particular, la gramatica subyacente al esquema de la
Fig. 2.19 es recursiva por la izquierda, y segn se expuso en la ultima seccin, un analizador
sintctico predictivo no puede manejar una gramatica recursiva por la izquierda. Eliminando
la recursin por la izquierda, se puede obtener una gramatica adecuada para ser usada en un
traductor descendente recursivo predictivo.





Sintaxis abstracta y sintaxis concreta


5 9
-
2
+


Fig. 2.20 rbol de sintaxis para 9-5 +2


Un punto de partida til para considerar la traduccin de una cadena de entrada es un
rbol de sintaxis abstracta, donde cada nodo representa un operador, y los hijos de ese
nodo, los operandos. Por contraste, un rbol de anlisis sintctico se denomina rbol de
sintaxis concreta, y la gramatica subyacente, sintaxis concreta del lenguaje. Los rboles de
sintaxis abstracta, o simplemente arboles sintcticos difieren de los arboles de anlisis
sintctico en que las distinciones superficiales de forma, sin importancia en la traduccin, no
aparecen en los arboles sintcticos.

Por ejemplo, el rbol sintctico para 9-5+2 se muestran en la figura 2.20 . Dado que
+y - tienen el mismo nivel de precedencia, y los operadores con igual nivel de precedencia
se evalan de izquierda a derecha, el rbol presenta 9-5 agrupado como una subexpresion.
Haciendo una comparacin con la figura 2.20 con el correspondiente rbol de anlisis
sintctico de la figura 2.2, se observa que el rbol sintctico asocia un operador con un nodo
interior, y no hace que el operador sea uno de los hijos.

Es deseable que un esquema de traduccin se base en una gramatica cuyos rboles de
anlisis sintctico se parezcan al mximo a arboles sintcticos. El agrupamiento de
subexpresiones hecho por la gramatica de la figura 2.19 es similar a su agrupamiento en
rboles sintcticos.
Pgina N 60

Desafortunadamente, la gramatica de la figura 2.19 es recursiva por la izquierda, y
por tanto, no es adecuada para el anlisis sintctico predictivo. En esto, parece existir una
contradiccin; por una parte, se necesita una gramatica que facilite el anlisis sintctico, y
por otra, se precisa una gramatica radicalmente distinta que facilite la traduccin. La
solucin obvia es eliminar la recursividad por la izquierda. Sin embargo, esto ha de hacerse
con cuidado, como se muestra en el siguiente ejemplo.




Ejemplo 2.9

La gramtica siguiente no es apropiada para traducir expresiones a la forma postfija,
aunque genere exactamente el mismo lenguaje que la gramatica de la figura 2.19 y se puede
usar para anlisis sintctico descendente o recursivo.

expr trmino resto
resto +expr | - expr | e
trmino 0 | 1| . . . | 9

El problema de esta gramtica es que los operandos de los operadores generados por
resto +expr y resto - expr no resultan evidentes a partir de las producciones.
Ninguna de las siguientes elecciones para formar la traduccin de resto.t a partir de
la de expr.t es aceptable:

resto - expr { resto.t : ='-'|| expr.t } (2.12)
resto - expr { resto.t : =expr|| '-' } (2.13)

( Solo se muestra la produccin y la accin semntica para el operador menos.) La
traduccin de 9-5 es 95-. Sin embargo, utilizando la accin de (2.12), entonces el signo
menos aparece antes queexpr.t y 9-5 queda incorrectamente en la traduccin como 9-5.

Por otra parte, usando (2.13) y la regla anloga para el operador ms, los operadores
se trasladan de manera consistente al extremo derecho y 9-5+2 se traduce incorrectamente a
952+- ( la traduccin correcta es 95-2+).


Adaptacin del esquema de traduccin


La tcnica para la eliminacin de la recursividad por la izquierda esbozada en la
figura 2.18 se puede aplicar tambin a producciones que contengan acciones semnticas. La
tcnica transforma las producciones A Ao | A | | en

Pgina N 61
A R
R o R | | R | e

Cuando las acciones semnticas se intercalan en las producciones, se trasladan junto
con la transformacin. Aqu, si se hace A =expr, o =+trmino { print ('+') },
|=- trmino {print ('-')} y =trmino, la transformacin anterior produce el esquema de
traduccin (2.14). Las producciones para expr y para el nuevo no terminal resto de (2.14).

Las producciones para trmino se repiten en la figura 2.19. Fjese que la gramtica
subyacente es distinta de la del ejemplo 2.9, y la diferencia hace posible la traduccin que se
desea.
expr trmino resto
resto +trmino { print ('+') } resto | - trmino { print ('-')} resto| e
trmnio 0 { print ('0') }
trmino 1 { print ('1') } (2.14)
...
trmino 9 { print ('9') }



La fig. 2.21 muestra como se traduce 9-5+2 utilizando la gramatica anterior


5 {print ('5')}
termino
9 {print ('9')}
termino
resto
2 {print ('2')}
+ termino {print ('+')}
resto
- {print ('-')}
resto
expr



La fig. 2.21 Traducion de 9-5+2 a 95-2+


Procedimientos para los no terminales expr, termino y resto


Pgina N 62
Ahora se aplica un traductor en C utilizando el esquema de traduccin dirigida por la
sintaxis (2.14). La escencia del traductor es el cdigo en C de la figura 2.22 para las
funciones expr, termino y resto. Estas funciones aplican los correspondientes no terminales
de (2.14).

La funcin parea, que se presenta mas adelante, es la contraparte en C del cdigo de
la figura 2.17 para parear un componente lxico con el smbolo de preanlisis y avanzar por
la entrada. Puesto que cada componente lxico es un solo caracter en este lenguaje, parea
puede hacerse comparando y leyendo caracteres.



expr( )
{
trmino ( ) ; resto ( ) ;
}
resto ( )
{
if (preanlisis =='+') {
parea ('+') ; trmino ( ) ; putchar ('+') ; resto ( );
}
else;
}
trmino ( )
{
if (isdigit(preanlisis)) {
putchar (preanlisis) ; parea (preanlisis) ;
}
else error ( ) ;
}


Fig. 2.22 Funciones para los no terminales expr, resto y trmino



Explicacin de algunas funciones generales en lenguaje C y Pascal


Para los lectores que no estn familiarizados con el lenguaje de programacin C. se
mencionan las principales diferencias entre C y otros lenguajes derivados de ALGOL como
Pascal. Un programa en C esta constituido por una secuencia de definiciones de funciones,
donde la ejecucin comienza con una funcin distinguida llamada main. Las definiciones de
funciones no se pueden anidar. Los parntesis que encierran a los parmetros de una funcin
son necesarios aunque no haya parmetros: por tanto, se escribe expr ( ), termino ( ) y
Pgina N 63
resto ( ). Las funciones se comunican por el paso de parmetros " por valor " o por el acceso
a datos globales a todas las funciones. Por ejemplo, las funciones termino ( ) y resto ( )
revisan el smbolo de preanlisis utilizando el identificador global preanlisis.


Los lenguajes C y Pascal usan los siguientes smbolos para asignaciones y
pruebas de igualdad :


Operacin

C

Pascal
asignacin
prueba de igualdad
prueba de desigualdad
=
==
! =
: =
=
<>

Explicacin y comparacin entre la gramatica 2.14 y el prog. o traductor


Las funciones para los no terminales imitan a los lados derechos de las producciones.
Por ejemplo, la produccin expr trmino resto se aplica para las llamadas a trmino ( ) y
resto ( ) en la funcin expr ( ).

Otro ejemplo, la funcin resto ( ) utiliza la primera produccin pararesto en (2.14) si
el smbolo de preanlisis es un signo ms; utiliza la segunda produccin si el smbolo de
preanlisis es menor, y utiliza la produccin resto e por omisin.

La primera produccin para resto queda implantada por la primera proposicin if de
la figura 2.22. Si el smbolo de preanlisis es +, el signo ms se parea mediante la llamada
parea ('+'). Despus de la llamada a termino ( ), la rutina de biblioteca standard de C putchar
('+') implanta la accin semntica mediante la impresin de un caracter "ms". Como la
tercera produccin para resto tiene e como lado derecho, el ltimo else de resto ( ) no hace
nada.

Las diez producciones para termino generan los diez dgitos. En la figura 2.22, la
rutina isdigit prueba si el smbolo de preanlisis es un dgito. Si el resultado es positivo, se
imprime y se parea el dgito; de otro modo, aparece un error. Observece que para modificar
el smbolo de preanlisis, de modo que la impresin debe producirse antes de emparejar el
dgito. Antes de mostrar un programa completo, se har una transformacin para mejorar la
velocidad del cdigo de la figura 2.22.


Optimacin del traductor


Ciertas llamadas recursivas se pueden reemplazar por iteraciones. Cuando la ultima
proposicin ejecutada en el cuerpo de un procedimiento es una llamada recursiva al mismo
Pgina N 64
procedimiento se dice que la llamada es recursiva por el final. Por ejemplo, las llamadas de
resto ( ) al final de la cuarta y sptima lineas de la funcin resto ( ) son recursivas por el
final, porque el control fluye hacia el final del cuerpo de la funcin despus de cada una de
esas llamadas.

Se puede imprimir mayor velocidad a un programa reemplazando la recursin por el
final con una construccin de iteracin. Para un procedimiento sin parmetros, una llamada
recursiva por el final simplemente se puede reemplazar por un salto al inicio del
procedimiento. El cdigo de resto se puede reescribir como :





resto ( )
{
L: if ( preanlisis =='+' ) {
parea ('+'); termino ( ) ; putchar ('+') ; goto L ;
}
else if ( preanlisis =='-' ) {
parea ('-') ; termino ( ) ; putchar ('-') ; goto L ;
}
else ;
}


Mientras el smbolo de preanlisis sea un signo ms o menos, el procedimiento resto
para el signo, llama a termino para que paree un dgito, y repite el proceso. Advirtase que,
parea elimina el signo cada vez que es llamado, este lazo ocurre solo en secuencias
alternantes de signo y dgito. Si se hace este cambio en la figura 2.22, la nica llamada que
queda de resto es desde expr ( vase la linea 3). Por tanto, las dos funciones se pueden
integrar en una sola, como en la figura 2.23. En C, una proposicin prop se puede ejecutar
repetidamente escribiendo

while (1) prop

porque la condicin 1 siempre es verdadera. Se puede salir de un ciclo ejecutando una
proposicin de interrupcin de ciclo, la proposicin break. La forma estilizada del cdigo de
la figura 2.23 permite aadir oportunamente otros operadores.



expr ( )
{
termino ( );
Pgina N 65
while(1)
if ( preanlisis =='+' ) {
parea ('+'); termino ( ) ; putchar ('+') ;
}
else if (preanlisis =='-' ) {
parea ('-') ; termino ( ) ; putchar ('-') ;
}
else break;
}


Fig. 2.23 Reemplazo de las funciones expr y reto de la figura 2.22



El programa completo


El programa completo en C para el traductor se muestra en la figura 2.24. La
primera linea, que comienza con #include, carga <ctype.h >que es un archivo de rutinas
estndar que contiene el cdigo del predicado isdigit.

Los componentes lxicos, que consisten en caracteres simples, los proporciona la
rutina de biblioteca standard getchar, que lee el siguiente caracter del archivo de entrada. Sin
embargo, preanlisis esta declarado como entero en la linea 2 de la figura 2.24. Ya que
preanlisis esta declarado fuera de cualquiera de las funciones, es global a cualquiera
funciones que se definan despus de la linea 2 de la figura 2.24.

La funcin error parea revisa componentes lxicos; lee el siguiente componente
lxico de la entrada si el smbolo de preanlisis concuerda, y llama a la rutina de error en
otro caso.

La funcin error utiliza la funcin de biblioteca estndar printf para imprimir el
mensaje " error de sintaxis ", y despus termina la ejecucin mediante la llamada exit (1), a
otra funcin de biblioteca estndar.


ANLISIS LXICO


ahora se puede aadir al traductor de la seccin anterior un analizador lxico que lea
y convierta la entrada en una cadena de componentes lxicos para el analizador sintctico.



Pgina N 66




Fig. 2.24 Programa en C para traducir una expresin de la forma infija a la forma

postfija




( pgina siguiente )




#include <ctype.h >/ * carga el archivo que contiene el predicado isdigit * /
int preanlisis ;
main ( )
{
preanlisis =getchar ( ) ;
expr ( ) ;
putchar('\n') ; / * agrega un caracter de linea al final * /
}
expr ( )
{
termino ( )
while(1)
if (preanlisis =='+') {
parea('+') ; termino ( ) ; putchar ('+') ;
}
else if (preanlisis =='-') ; {
parea ('-') ; termino ( ) ; putchar ('-') ;
}
else break ;
}
termino ( )
{
if (isdigit ( preanlisis)) {
putchar ( preanlisis) ;
parea (preanlisis) ;
}
else error ( ) ;
}
parea (t)
Pgina N 67
int t ;
{
if (preanlisis ==t )
preanlisis =getchar ( ) ;
else error ( ) ;
}
error ( )
{
printf ("error de sintaxis\n "); / * imprime mensaje de error * /
exit ; / * y despus se detiene * /
}






Pgina N 1
PARSING


Hemos hablado ya lo suficiente sobre lenguajes de contexto-libre, an cuando hemos
garantizados que esta clase de lenguajes no contempla todos los casos .

Por que deberamos estudiar en tanto detalle, gramticas tan primitivas
que no pueden hasta definir el conjunto {an bn an} ?.

No estamos jugando simplemente un juego intelectual interesante. Hay una razn
ms practica :

Los lenguajes de programacin de computadora son de contexto-libre. ( Tenemos
que ser cuidadosos aqu en decir que los lenguajes en que las palabras son instrucciones de
lenguaje de computadora son de contexto-libre. Los lenguajes en que las palabras son
programas de lenguaje de computadora no son principalmente de contexto libre ). Esto hace
que las GCL sean de fundamental importancia en el diseo de compiladores.

Permtanos comenzar con la definicin de que constituye un identificador en un
lenguaje de alto nivel como ADA, BASIC, COBOL, etc.

Estos nombres definidos por el usuario son frecuentemente llamados variables. En
algunos lenguajes su longitud esta limitado a un mximo de seis caracteres, donde el primero
tiene que ser una letra y cada carcter despus de este es ya sea una letra o un dgito.


Podemos resumir esto por la GCL :

identificador letra ( letra + dgito + dgito +A)5
letra A | B | C. . . | Z
digito 0 | 1 | 2 | 3 . . . | 9


Note que hemos utilizado una expresin regular en el lado derecho de la primera
produccin en lugar de escribir completamente todas las posibilidades:


identificador letra | letra letra |
letra dgito | letra letra letra |
letra letra dgito | letra dgito dgito | . . .


Estas son 63 cadenas diferente de no terminales representadas por :

letra ( letra + dgito +A )5
Pgina N 2
Y el uso de esta notacin es ms posible de entender que escribir completamente
toda la lista.

La primera parte del proceso de compilacin es el scanner.

Este programa lee completamente el programa fuente original y remplaza todos los
nombres identificadores definidos por el usuario que tienen significado personal para el
programador, como FECHA, SALARIO, TASA, NOMBRE, MADRE, etc., Con nombres
de computadora mas manejables que ayuden a la maquina a mover informacin que se este
procesando en y fuera de sus registros .

El scanner es tambin llamado un analizador lxico porque su trabajo es construir un
lxico ( palabra que proviene del Griego y que significa " diccionario " en latn ).

Un scanner tiene que poder hacer algunas decisiones sofisticadas como reconocer
que DO33I es un identificador en la declaracin de asignacin :

Por DO33I =1100

mientras DO33I es parte de una instruccin de lazo en la declaracin :

DO33I =1,100

( o en algunos lenguajes se define D033I de 1 a 100).

Otras cadenas de caracteres, como IF, ELSE , END,..., tienen que estar reconocidos
como palabras clave aun cuando tambin se adapten a la definicin de identificador.

Aparte, la mayora de lo que un scanner hace puede ser ejecutar por un FA, ademas
los scanner estn generalmente escritos con este modelo en mente.

Otra tarea que un compilador debe desempear es el de " entender " cual es
significado de las expresiones aritmticas como :

A3J * S +( 7 * ( BIL +4 ))

Despus de que el rastreador remplaza todos los nmeros y variables con las
etiquetas identificadoras i1, i2,...., etc. aquellas o aquellos se convierten en

i1 * i2 +( i3 * ( i4 +i5 ))

Las gramticas que presentamos anteriormente para la expresin aritmtica fue
ambigua. Esto no es aceptable en la programacin pues nosotros deseamos que la
computadora conozca y ejecute exactamente lo qu nosotros queremos significar con esta
frmula.
Pgina N 3

Dos soluciones posibles se mencionaran a continuacin :


1) Se requiere el programador inserte parntesis para evitar ambigedad.

Por ejemplo, en lugar de la ambigedad 3+4* 5 incite en

( 3 +4 ) * 5
o
3 +( 4 * 5 )


2) Encuentra una nueva gramtica para el mismo lenguaje que sea no ambigo debido a
que la interpretacin de la " jerarqua del operador " ( que es * antes que +) es construido
dentro del sistema.

Los programadores encuentran la primera solucin demasiado engorrosa y
antinatural. Afortunadamente, hay gramticas ( GCL ) que satisfacen el segundo requisito.

Nosotros presentamos tan solo para las operaciones + y * nicamente, llamados
PLUS-TIMES.


Las reglas de produccin son :


S E
E T +E | T
T F * T | F
F ( E ) | i


Hablando sinceramente, E soporta para una expresin, T para un trmino en una
suma, F para un factor en un producto, e Y para cualquier identificador.


Los terminales claramente son :

+ * ( ) i


ya que esos smbolos ocurren en el lado derecho de las producciones pero nunca en el lado
izquierdo.

Pgina N 4



Para generar la palabra i +i * i por la derivacin de ms a la izquierda tenemos que
proceder :


S E
T +E
F +E
i +E
i +T
i +F * T
i +i * T
i +i * F
i +i * i




El rbol de sintaxis para este es :



i
S
F i
E
i
F *
F
T
+
T
T
E


Pgina N 5
Aclaramos a partir de este rbol que la palabra representa la adicin de un
identificador con el producto de dos identificadores. En otras palabras, la multiplicacin se
realizara antes que la adicin, justamente como lo pretendamos para que este de acuerdo
con la convencional jerarqua de operadores. Una vez que la computadora puede descubrir
una derivacin para esta formula se puede generar un programa de lenguaje maquina para
efectuar la misma tarea.

Dada una palabra generada por una gramatica en particular la tarea de encontrar su
derivacin es llamada "parsing ". Hasta ahora hemos estado interesados solamente en si
una cadena de smbolos fue una palabra en determinado lenguaje

Estuvimos preocupados solamente acerca de la posibilidad de generacin por
gramtica o aceptacin por mquina. Ahora nos encontramos que queremos conocer ms.
Queremos conocer no justamente si una cadena puede estar generada por un GCL sino
tambin como. Sostenemos que si conocemos el ( o uno de los ) rbol de derivacin (o
arboles de derivaciones ) de una palabra dada en un lenguaje particular, entonces conocemos
algo acerca del significado de la palabra.
Este capitulo es diferente de los otros en esta parte porque aqu estamos buscando
comprender que dice una palabra para determinar como puede ser generada.

Hay muchos diferentes enfoques al problema de " Parsing GCL ". Consideremos
tres de ellos. Los dos primeros son algoritmos generales basados en nuestro estudio de
rboles de derivacin para GCL. El tercero es especfico para expresiones aritmticas y hace
uso de la relacin existente entre un GCL y un PDA.

El primer algoritmo es llamado top down parsing . Nosotros comenzamos con un
GCL y una palabra objetivo. Comenzando con el smbolo S, intentamos encontrar alguna
secuencia de producciones que generen la palabra objetivo. Hacemos estas revisando todas
las posibilidades de derivaciones desde ms a la izquierda. Para organizar esta bsqueda
construimos un rbol de todas las posibilidades. Que es como todo el rbol del lenguaje del
captulo 14. Desarrollamos cada rama hasta que vemos con claridad que la misma no puede
prolongarse en el instante considerado a una posibilidad diaria; esto es, dejamos de
desarrollar una rama de todo el rbol del lenguaje tan pronto como se hace claro que la
palabra objetivo nunca aparecer en la rama, ni si quiera en generaciones posteriores. Esto
puede ocurrir, por ejemplo, si la rama incluye en su cadena trabajadora un terminal que no
aparece en cualquier parte en la palabra objetivo o no aparece en la posicin correspondiente
de la palabra objetivo .

Este es el momento para ver una ilustracin :

Permtanos considerar la palabra objetivo :

i +i * i

en el lenguaje generado por la gramtica PLUS - TIMES.
Pgina N 6

Comenzamos con el smbolo inicial S. En este punto hay nicamente una produccin posible
que podemos aplicar, S E.


Desde E hay dos posibles producciones :


E T+E
o
E T



En cada caso, el no terminal de ms a la izquierda es T y hay dos producciones
posibles para reemplazar este T.



El rbol del parsing Top-down left-most comienza como se muestra a continuacin :



F * T +E
F
S
T
F +E
T +E
F * T
E



En cada uno de los cuatro casos del final del rbol que acabamos de ver el no
terminal que se encuentra ms a la izquierda es F el cual esta en el lado izquierdo de dos
producciones posibles.

Pgina N 7

( 8 ) ( 7 ) ( 6 ) ( 5 ) ( 4 ) ( 3 ) ( 2 ) ( 1 )
i ( E ) i +T ( E ) +T ( E ) +E i +E i * T +E ( E ) * T +E
F * T +E F
S
T
F +E
T +E
F * T
E



De estos, podemos eliminar las ramas nro. 1,3,5 y 7 de posterior consideracin
porque introdujeron el carcter terminal "( ", el cual no es ni el primero ni cualquier letra de
nuestra palabra. Una vez que el carcter terminal aparece en la cadena trabajadora, nunca
ms desaparece. Los no terminales de las producciones siempre cambian en otras cosas,
pero los terminales permanecen por siempre iguales. Todas aquellas cuatro ramas pueden
producir solamente palabras con parntesis en ellas, no i +i * i . La rama 8 ha finalizado
naturalmente su desarrollo en una cadena completa de terminales pero no es nuestra palabra
objetivo, de modo que podemos tambin terminar la investigacin de esa rama .


Nuestro rbol podado aparece como esto :


( 6 ) ( 4 ) ( 2 )
i +T i +E i * T +E
F * T +E
S
T
F +E
T +E
F * T
E


Pgina N 8
Ya que las ramas 7 y 8 ambas desaparecen ignoramos la linea que las han producido:

T F

Las tres ramas han derivado los primeras dos caracteres de terminales de las palabras
que pueden producir. Cada una de las tres ramas comienza desde la izquierda con dos
terminales que nunca pueden cambiar.
La rama 4 muestra la palabra que comienza con " i +", lo cual es correcto, pero las ramas
2 y 6 pueden por ahora producir solamente palabras que comienzan con " i * ", lo cual no
esta de acuerdo con nuestra deseada palabra objetivo. El segundo carcter de las palabras
derivadas de la ramas 2 y 6 es *; el segundo carcter de la palabra objetivo es +. por lo
tanto debemos eliminar aquellas ramas antes de que se multipliquen.

Eliminando la rama 6 poda el rbol hacia arriba hasta la derivacin E T, que ha
demostrado no tener frutos como as tambin a demostrado que ninguno de sus ramales
puede producir nuestra palabra objetivo.

Eliminando la rama 2 distinguimos que podemos eliminar toda la rama izquierda de
T +E.

Con todo lo que hemos podado hasta ahora, podemos concluir que cualquiera rama
que se dirija hacia la palabra i +i * i tiene que comenzar como :

S E T +E F +E i +E.


Permtanos continuar con este rbol dos generaciones ms. Hemos dibujados todas
posibilidades de derivacin. Ahora es tiempo de examinar las ramas ha podar.

Pgina N 9
( 12 )
i +F
S
E
T +E
( 11 ) ( 10 ) ( 9 )
i +F * T +E
F +E
i +T
i +F +E
i +T +E
i +F * T
i +E



En este momento vamos a sacar una nueva regla de nuestro sombrero. Puesto que
ninguna produccin en cualquier GCL puede disminuir la longitud de la cadena trabajadora
de terminales y no terminales sobre la cual se opera (cada produccin remplaza un smbolo
por uno o ms), entonces la longitud de la cadena trabajadora si ha pasado de cinco
caracteres nunca puede producir una palabra final de longitud solo de cinco. Podemos por
lo tanto eliminar la rama 9 en base a este fundamento. Ninguna palabras que se genera puede
tener como mnimo cinco letras.


Otra observacin que podemos hacer es que aunque la rama 10 no es demasiado
larga y an cuando comienza con una cadena correcta de terminales, puede ser eliminada
porque ha producido otro +en la cadena trabajadora. Este es un terminal que todos sus
descendientes en la rama lo tendrn incluido. Sin embargo, no hay un segundo +en la
palabra que estamos intentando derivar. Por lo tanto, tambin podemos eliminar la rama 10.







Pgina N 10

Esto nos permite que solamente las ramas 11 y 12 continen su crecimiento :


i +E
( 16 )
i +i
S
E
T +E
( 15 ) ( 14 ) ( 13 )
i +( E ) * T
F +E
i +F
i +i * t
i +F * T
i +( e )
i +T



Ahora las ramas 13 y 15 han introducido el terminal prohibido "( ", mientras que la
rama 16 ha terminado su crecimiento en la palabra equivocada. Solamente la rama 14
merece seguir viva.



( en este punto dibujamos la mitad principal del rbol horizontalmente. Ver pg. siguiente ).







Pgina N 11

S E T +E F +E i +E i +T i +F * T


i +i * T
i +i * F
i +i * i i +i * ( E )
i +i * F * T



De este modo hemos descubiertos que la palabra Y+Y* Y puede ser generada por
esta GCL y hemos encontrado la nica derivacin de ms a la izquierda por la cual se
genera.

Para recapitular el algoritmo: de todos los nodos vivos de nuestras ramas le podemos
aplicar todas las producciones empezando por el no terminal ms a la izquierda.

Eliminamos una rama por tener una cadena equivocada de terminales, ya sea que
tengamos un terminal malo en cualquier parte de la cadena, ya sea porque la cadena en
cuestin es demasiada grande o larga respecto a la cadena objetiva o simplemente porque
tenemos una cadena equivocada de terminales.

Utilizando el mtodo de arboles de bsqueda conocido como backtracking no es
necesario desarrollar todas las vidas de las ramas a la vez. En lugar de eso podemos
desarrollar una rama hacia abajo hasta que, ya sea que alcancemos la palabra deseada o la
terminamos debido a que encontremos un carcter malo o debido a que la palabra en
cuestin tenga una longitud excesiva. En este punto nosotros volvemos a un nodo previo
para viajar hacia abajo por otro camino hasta que encontramos la palabra objetivo u otro
callejn sin salida, y as sucesivamente. Los algoritmos de backtracking son temas que se
adecuan ms aun curso diferente. Como de costumbre, estamos ms interesados en mostrar
qu pueden hacer, y no en determinar cual es el mejor mtodo.


Tenemos solamente una lista de las principales razones para terminar el desarrollo de
un nodo en el rbol.




Pgina N 12

Un conjunto ms completo de reglas es :


1) Subcadena Mala :

Si una subcadena de terminales completos ( uno o ms ) han sido introducidos dentro
de una cadena trabajadora en una rama del rbol del lenguaje total, todas palabras derivadas
de esta deben tambin incluir aquella subcadena inalterada.
Por lo tanto, cualquiera subcadena que no aparezca en la palabra objetivo es causa para
eliminar la rama.


2) Subcadenas buenas pero demasiadas :

La cadena trabajadora tiene ms ocurrencias en la subcadena particular que la palabra
objetivo . En este sentido la regla 1) es un caso especial de esta.


3) Subcadenas buenas pero en orden equivocado :

Si la cadena trabajadora es YabXYbaXX pero la palabra objetivo es bbbbaab,
entonces ambas subcadenas de terminales que se desarrollaron hasta el momento, ab y ba,
son subcadenas vlidas de la palabra objetivo pero ellas no ocurren en el mismo orden en la
cadena trabajadora como en la palabra de modo que la cadena trabajadora no puede
desarrollarse dentro de la palabra objetivo.


4) Subcadenas terminales externas impropias :

Subcadenas de terminales que se desarrollan al comienzo o al final de la cadena
trabajadora siempre permanecer en los extremos en el cual ellas aparezcan primero. Tienen
que estar en perfecta coincidencia con la palabra objetivo o la rama deber ser eliminada.


5) longitud proyectada Excesiva :

Si la cadena trabajadora es aXbbYYXa y si todas las producciones con el lado
izquierdo de X tienen lados derechos de seis caracteres, entonces la longitud ms corta de la
ultima palabra derivada desde la cadena trabajadora debe tener una longitud como mnimo
de :

1 + 6 +1 +1 +1 +1 + 6 +1 =18.

Si la palabra objetivo tiene menos que 18 caracteres, entonces la rama debe ser eliminada.
Pgina N 13


6) palabra objetivo Errnea :

Si tuviramos solamente terminales pero la cadena no es la palabra objetivo, la olvidamos o
no la tenemos en cuenta. Este es un caso especial de la regla 4), donde la subcadena es la
palabra completa.

Terminamos diciendo que pueden haber ms reglas esto depender de la naturaleza exacta
de la gramtica.


EJEMPLO :


Permtanos recordar la GCL para el lenguaje EQUAL :


S aB | bA
A a | aS | bAA
B b | bS | aBB


La palabra bbabaa est en EQUAL. Permtanos determinar la derivacin de ms a la
izquierda para esta palabra por el mtodo parsing top-down.


Desde el smbolo inicia S la derivacin del rbol puede tomar dos caminos :


( 2 ) ( 1 )
bA aB
S


Todas palabras derivadas desde la rama 1 tienen que comenzar con la letra a, pero
nuestra palabra objetivo no comienza con dicha letra. Por lo tanto, por Regla 4, solamente la
rama 2 debe ser considerada. El no terminal de ms a la izquierda es A, hay tres ramas
posibles en este punto

Pgina N 14
( 5 ) ( 4 ) ( 3 )
S
bbAA
ba
baS
bA



La rama 3 es una palabra completa de terminales pero no es nuestra palabra objetivo.
La rama 4 generar solamente palabras con una cadena inicial de terminales ba, el cual no es
el caso con bbabaa . Solamente a la rama 5 le queda una posibilidad. El no terminal de ms a
la izquierda en la cadena trabajadora de la rama 5 es la primera A.

Tres producciones se pueden aplicar en este caso :

S
( 8 ) ( 7 ) ( 6 )
bA
bbbAAA
bbaA
bbaSA
bbAA


Las ramas 6 y 7 parecen ser perfectamente posibles. La rama 8, sin embargo, ha
generado la subcadena de terminales bbb, que todos sus descendientes debern soportar.
Esta subcadena no aparece en nuestra palabra objetivo, de modo que podemos eliminar esta
rama de posterior consideracin.

En la rama 6 el no terminal de ms a la izquierda es A, y en la rama 7 es S. Por lo
tanto en rama la 6 se desprenden tres posibilidades, y de la rama 7 se desprenden dos
posibilidades . As generamos las ramas 9, 10, 11 de la rama 6; 12 y 13 de la rama 7.


Pgina N 15
( 13 ) ( 12 ) ( 11 ) ( 10 ) ( 9 )
bbabAA bbaaBA bbabAA bbaaS bbaa
S
bA
bbaSA
bbaA
bbAA


La rama 9 es una cadena donde todos los caracteres son terminales, pero no es la
palabra objetivo. La rama 10 tiene la subcadena inicial bbaa. La palabra objetivo no es as.
Este detalle hace que tambin eliminemos la rama 12. La rama 11 y 13 son idnticas. Si
necesitramos todas las derivaciones de ms a la izquierda, de la palabra objetivo,
tendramos ambas rama en consideracin.
Pero ya que necesitamos solamente una derivacin, podemos justamente mantener la rama
13 y eliminar la rama 11 ( o viceversa ). Cualquiera palabras que puede ser producida sobre
alguna rama puede ser producida sobre la otra.

S bA bbAA bbaSA bbabAA

( 16 ) ( 15 ) ( 14 )
bbabbAAA
bbabaA
bbabaSA
bbabAA


Solamente la cadena trabajadora de la rama 14 no es ms larga que la palabra
objetivo. Las ramas 15 y 16 nunca pueden generar una palabra de seis letras.

S bA bbAA bbaSA bbabAA bbabaA

Pgina N 16
( 19 ) ( 18 ) ( 17 )
bbababAA
bbabaa
bbabaaS
bbabaA



Las ramas 18 y 19 son demasiadas largas, de modo que es algo bueno la rama 17 ya
que es la palabra que buscbamos.

Por lo tanto la derivacin se ha completado. El siguiente algoritmo de Parsing que
ilustraremos es el Parser Bottom-up. Esta vez, no nos preguntamos cuales fueron algunas
de las primeras producciones utilizadas para derivar la palabra, sino cuales fueron algunas de
las ltimas.
Trabajamos al revs: del fin al frente, la gente husmeadora de ste modo hace cuando
intentan resolver un laberinto.

Permtanos considerar de nuevo como nuestro ejemplo la palabra i +i * i generado
por el GCL PLUS- TIMES.

Si estuviramos intentando reconstruir una derivacin de ms a la izquierda.

Permtanos juzgar que :

El ltimo terminal (de la produccin) que se deriv, fue el ltimo
carcter de la palabra. Sin embargo, este no siempre el caso. Por ejemplo, en la gramtica.

S Abb
A a

La palabra abb fue formada en dos pasos, pero al final dos b fueron introducidas en
el primer paso de la derivacin, no en el ltimo. As en lugar de intentar reconstruir
especficamente una derivacin de ms a la izquierda, tenemos que buscar para cualquier
derivacin de nuestra palabra objetivo. Esto hace al rbol mucho ms largo. Comenzamos
desde la parte inferior del rbol de derivacin, con la misma palabra objetivo, y trabajamos
paso a paso nuestro camino, volviendo hacia arriba del rbol; buscando hasta encontrar en la
cadena trabajadora el smbolo inicial S.


Permtanos reconsiderar la GCL PLUS- TIME :

S E
E T +E | T
T F * T | F
Pgina N 17
F ( E ) | i



Para realizar una bsqueda bottom-up, reiteraremos el siguiente paso: Encuentra
toda subcadena de la presente cadena trabajadora de terminales y no terminales que son
mitades exactas de producciones y sustituir por un no terminal que las podran haber
producido.

Tres subcadenas de i +i * i son mitades exactas de producciones; es decir, las tres i's,
cualquiera de las cuales podra haber sido producida por una F. El rbol de posibilidades
comienza como sigue:

i +i * F
F +i * i
i +F * i
i +i * i


An cuando estamos yendo de la parte inferior del rbol de derivacin a la parte
superior S, dibujaremos el rbol de posibilidades, como todos nuestros rboles, de la parte
superior de la pgina hacia abajo.


Podemos salvar nosotros mismos parte del trabajo de este ejemplo particular
comprendiendo que todas las i's proviene de la produccin F i y la cadena trabajadora que
deberamos estar intentando derivar es F +F * F. Hablando estrictamente, este conocimiento
no debera estar permiti ya que requiere una idea que no incluimos en el comienzo del
algoritmo .
Pero ya que nos salva de una considerable cantidad de trabajo, sucumbimos a la tentacin y
lo escribimos en un solo paso.


i +i * i
|
F +F * F

No todas las F's tienen que provenir de T F. Alguna puede haber provenido de T F * T,
de modo que no podemos utilizar el mismo truco de nuevo.

Pgina N 18
i +i * i
F +F * T
T +F * F
F +T * F
F +F * F


Las primeras dos ramas contienen subcadenas que puede ser mitades exactas de
E T y T F. La tercera rama tiene la posibilidad adicional de T F * T.





El rbol contina de la siguiente manera :

(10) (9) (8) (7) (6) (5) (4) (3) (2) (1)
F +F * EF +T F +T * T T +F * T
F +F * T
F +T * T T +E * F T +T * F
F +T * F
T +F * T T +T * F E +F * F
i +i * i
T +F * F
F +F * F



Nunca tenemos que preocuparnos acerca de la longitud de las cadenas intermediarias
en el parsing bottom up ya que pueden nunca exceder la longitud de la palabra objetivo. En
cada etapa ellas permanecen de la misma longitud o se transforman en ms cortas. Ademas
nunca ninguno de los terminales malos son introducidos desde ninguno de los terminales
nuevos, son todos siempre introducidos nicamente desde no terminales.

Estas son eficiencias que compensan parcialmente por la ineficiencia de no restringirnos
nosotros mismos de las derivaciones de ms a la izquierdas.

Hay la posibilidad que un no terminal sea malo en ciertos contextos. Por ejemplo, la
rama 1 tiene en este momento una E como un carcter de ms a la izquierdo. La nica
produccin que puede absorber siempre a esa E es S E. Esto nos dara el no terminal S,
Pgina N 19
pero S no est en la mitad derecha de cualquier produccin. Es cierto que queremos
terminar con el smbolo inicial S; este es todo el objetivo del rbol. Sin embargo, queremos
que la cadena trabajadora completa sea esa nica S, no una cadena trabajadora ms larga
con S como su primera letra. El resto de la expresin en la rama 1, " +F * F ", no est
justamente encaminndose a desaparecer.

As la rama 1 es eliminada. Las E's en las rama 5 y 9 ninguna de las dos son demasiadas
prometedoras como veremos en un momento.

Cuando vamos hacia el revs, no tenemos demasiada garanta que la gramtica
" inversa " sea no ambiga an cuando la misma GCL pueda serlo. De hecho, este
rastrear hacia atrs es un camino probable no nico, ya que nosotros mismos no estamos
restringiendo el encontrar una derivacin de ms a la izquierda ( an cuando podemos con la
ms mnima idea; ver ms adelante problema 10 ). Deberamos tambin encontrar los
caminos de derivacin de ms a la derecha y lo que usted guste. Esto est reflejado en la
ocurrencia de expresiones repetidas en las ramas.

En nuestro ejemplo, la rama 2 es ahora la misma que la rama 4, la rama 3 es la misma que la
rama 7, y la rama 6 es la misma que la rama 8.
Ya que estamos interesados aqu en encontrar nicas derivaciones, no todas, podemos
seguramente eliminar las ramas 2,3, y 6, y a pesar de eso encontrar una derivacin, si existe
una.

El rbol crece ferozmente, como un arbusto, muy ancho pero no muy alto. Crecera
demasiado, incontrolable a menos que hagamos la siguiente observacin.


OBSERVACIN :


Ninguna cadena trabajadora intermediaria de terminales y no terminales puede tener
la subcadena " E * ". Esto es debido a que la nica produccin que introduce el * es

T F * T

De modo que el smbolo inmediato izquierdo del * es originalmente F. A partir de F
podemos solamente obtener las terminales " ) " o " i " a continuacin de * . Por lo tanto, en
una derivacin top-down nunca podremos crear la subcadena " E * " en esta GCL, as
mismo en el bottom-up esta nunca puede ocurrir en una cadena trabajadora intermediaria
encabezndola por detrs el smbolo inicial S. Igualmente, " E +" i " * E " estn tambin
prohibido en el sentido que ellas no pueden ocurrir en ninguna derivacin. La idea de
subcadenas prohibidas lo tratamos mejor en el captulo 3. Podemos ahora ver la importancia
de las tcnicas introducidas aqu para mostrar que ciertas subcadenas nunca pueden ocurrir
( y todo el mundo pensaba que por ver los teorema 2,3, y 4 ramos completamente frvolos
). Con la ayuda de esta observacin podemos eliminar las ramas 5 y 9.
Pgina N 20


El rbol ahora crece como sigue :

T +T * T
F +T * F
F +T * T
F +F * T
(16) (15)
(14)
(13) (12) (11)
F +E T +T
F +T
T +T T +T * T
T +F * T
T +T * T
i +i * i
T +T * F
F +F * F


Las ramas 11, 12 y 13 estn repetidas en 14 y 15, de modo que eliminamos dichas
ramas. La rama 14 no tiene ningn lugar para ir, ya que ninguna de las T's se pueden hacer E
sin crear la subcadena prohibida. As es que a la rama 14 tambin la eliminamos. A las ramas
15 y 16 les queda nicamente como prximo destino " T+E ", de modo que podemos
eliminar la rama 15 ya que a la 16 la tenemos all precisamente como apta por s misma.

El rbol finaliza como sigue :

i +i * i : F +F * F : F +F * T : F +T : F +E :T +E : E : S

Lo cual es lo mismos que :

S E T +E F +E F +T F +F * T F +F * F i +i * i

( el smbolo que se usa " : " es auto explicativo).


EJEMPLO :


Permtanos considerar de nuevo la gramtica :

S aB | bA
A a | aS | bAA
B b | bS | aBB

Pgina N 21

Y de nuevo permtanos buscar una derivacin de una palabra objetivo, pero esta vez
utilizando el Parsing Bottom-Up. Permtanos analizar primero la gramtica luego el parsing
antes que cualquier cosa.

Si encontrramos siempre la cadena trabajadora :

baaab

en un Parsing Boottom-Up en esta gramtica, tendremos que determinar las cadenas
trabajadoras desde las cuales podran haber sido derivadas. Nosotros examinamos de la
cadena considerada todas las subcadenas de ellas que estn en los lados derechos de las
producciones. En esta caso hay cinco de ellas:

b, ba, baa, a, ab.

Note como ellas pueden superponerse. Esta cadena trabajadora puede ser derivada
de cinco modos:

BAAaB bAAaB ( B b )
SAaB bAAaB ( S bA )
AaB bAAaB ( A bAA )
bAAAB bAAaB ( A a )
bAAS bAAaB ( S aB )


Permtanos hacer algunas observaciones particulares para esta gramtica :


1) Todas las derivaciones en esta gramtica comienzan ya sea con S ab o S ba, de
modo que la nica cadena trabajadora que puede comenzar siempre con un no terminal es la
cadena trabajadora que empieza con el smbolo inicial S. P
or ejemplo la seudo-cadena trabajadora Abba no puede ocurrir en una derivacin.


2) Puesto que la aplicacin de cada regla de produccin crea un nico nuevo terminal
en la cadena trabajadora, en cualquier derivacin de una palabra de longitud 6 ( o n ), hay
exactamente 6 ( o n ) pasos.


3) Ya que cada regla de produccin est en la forma

No terminal ( un terminal ) ( cadena de 0, 1, o 2 no terminales )

Pgina N 22
en una derivacin de ms a la izquierda, tomamos el primer no terminal de la cadena de no
terminales y lo reemplazamos con terminales seguidos por no terminales. Por lo tanto todas
las cadenas trabajadoras debern ser de la forma :


terminal terminal . . . terminal no terminal no terminal . . . no terminal
=terminal * no terminal
=( cadenas de terminales ) ( cadena de no terminales )


Si estuviramos buscando hacia atrs, es decir desde el final hacia adelante y tenemos
una cadena trabajadora delante nuestro, ademas de varias cadenas trabajadoras, aquella
puede haber provenido de todas estas, entonces podemos hacer un pequeo cambio en uno
de los mismos terminales que estn en frente de los no terminales de la cadena. Es decir
donde los terminales y los no terminales se encuentra.

Por ejemplo :

baabbababaBBABABBBAAAA

Puede haber sido producida a travs de la derivacin de ms a la izquierdo solamente desde
estas tres cadenas trabajadoras.


baabbababABBABABBBAAAA,
baabbababSBABABBBAAAA,
baabbababBABABBBAAAA


Ahora utilizamos el algoritmo Bottom Up para encontrar una derivacin desde ms a
la izquierda para la palabra objetivo bbabaa .



En la fila del final de la figura 1, hay dos S's.



( Ver pagina siguiente : FIGURA I )






Pgina N 23













































Pgina N 24























Por lo tanto, hay dos derivaciones de ms a la izquierdas de esta palabra en la
gramtica :


S bA bbAA bbaSA bbabAA bbabaA bbabaa
S bA bbAA bbaSA bbabAA bbabaA bbabaa


Note que todas las otras ramas en este rbol mueren simultneamente, ya que por
ahora no contienen terminales.

Hay, naturalmente, docenas de modificaciones de programas posibles para ambos
algoritmos de parsing. Esto incluye uzarlos a ellos en combinacin, lo cual es buena idea ya
que ambos comienzan con gran eficiencia antes de que sus arboles comiencen a esparcirse.
Ambos algoritmos se aplican a toda GCL. Por ejemplo, estos mtodos pueden aplicarse a la
siguiente GCL definida de un pequeo lenguaje de programacin como el que se muestra :


S ASSIGNMENTS$ | GOTO$ | IF$ | IO$
ASSIGNMENT$ i =ALEX
GOTO$ GOTO NUMBER
IF$ IF CONDITION THEN S | IF CONDITION THEN S ELSE S
Pgina N 25
CONDITION ALEX =ALEX | ALEX = ALEX , ALEX >ALEX
CONDITION CONDITION AND CONDITION
| CONDITION OR CONDITION | NOT CONDITION
IO$ READ i | PRINT i


( donde ALEX soporta una expresin algebrica ). Note que los nombres de las
declaraciones de tipos todas terminan en $ para distinguirlos como una clase.

Las terminales son :

{ = GOTO IF THEN ELSE = >AND OR NOT READ PRINT }

ms cualquiera de los terminales estn introducidos en la definicin de i, ALEX, y
NUMBER.

En esta gramtica podramos desear analizar la expresin :

IF i >i THEN i =i +i * i

As es que la instruccin puede ser convertida al lenguaje mquina. Esto puede hacerse para
encontrar su derivacin desde el smbolo inicial. El problema de la generacin de cdigo
desde el rbol de derivacin es la parte fcil de compilacin pero ademas los lenguaje
dependiente van ha ser nuestra preocupacin durante este curso.

Nuestro ultimo algoritmo " comprendido " , enunciado a fin de evaluar
expresiones esta basado nicamente en la notacin prefija mencionada en el captulo 14.
Esto se aplica no tan solo para expresiones aritmticas sino tambin para muchas otras
instrucciones de lenguajes de programacin.

Supondremos que estamos actualmente usando notacin pos fija, donde los dos
operandos preceden inmediatamente al operador :


A +B se pone como A B +
( A +B ) * C se pone como A B +C *
A * ( B +C * D ) se pone como A B C D * +*


Un algoritmo para convertir de notacin estndar infija a notacin pos fija fue dado
en el captulo 14. Una vez que una expresin est en pos fija, podemos evaluarla sin
encontrar su derivacin desde una GCL, aunque podemos originalmente hacer uso de su
rbol de parsing para transformarla de infija a pos fija en una primer instancia. Estamos
asumiendo aqu que nuestras expresiones involucran solamente valores numricos para los
identificadores ( i's ) y solamente las operaciones + y *, como en el lenguaje PLUS-TIMES.
Pgina N 26


Podemos evaluar estas expresiones posfijas por una nueva mquina similar a un
PDA.

Tal mquina requiere tres nuevos estados :


1) ADD :
Este estado saca las dos entradas de la parte superior de la pila, los suma, y
empuja el resultado hacia la parte superior de la pila.


2) MPY:
Este estado saca las dos entradas de la parte superior de la pila, los multiplica,
y empujan el resultado hacia la parte superior de la pila.


3) PRINT :
Este imprime la entrada que est en la parte superior de la pila y acepta la
cadena de entrada. Este es una salida y un estado de finalizacin.

La mquina para evaluar expresiones posfija puede ahora ser construida como se
muestra abajo, donde la expresin a ser evaluada ha sido puesta sobre la CINTA DE
ENTRADA en la forma usual, un carcter por celda comenzando en la primera celda.


i
*
+
PUSH i
PRINT
MPY
ADDD
READ
STAR


Pgina N 27

Permtanos esbozar la accin de esta mquina sobre la cadena de entrada :

7 5 +2 4 +* 6 +

La cual es posfija para :

( 7 +5 ) * ( 2 +4 ) +6 = 78




( Ver Tabla; pagina siguiente )














STATE


STACK

TAPE


START
READ
PUSH i


A
A
7

7 5 +2 4 +* 6 +
5 +2 4 +* 6 +
5 +2 4 +* 6 +

READ
PUSH i
READ


7
5 7
5 7

+2 4 +* 6 +
+2 4 +* 6 +
2 4 +* 6 +

ADD
READ
PUSH i


12
12
2 12

2 4 +* 6 +
4 +* 6 +
4 +* 6 +
Pgina N 28

READ
PUSH i
READ


2 12
4 2 12
4 2 12

+* 6 +
+* 6 +
* 6 +

ADD
READ
MPY


6 12
6 12
72

* 6 +
6 +
6 +

READ
PUSH i
READ


72
6 72
6 72

+
+
A

ADD
READ
PRINT


78
78
78

A
A
A




Note que cuando llegamos a PRINT la pila tiene solamente un elemento en ella.

Lo que hemos estado utilizando aqu es un PDA con capacidades aritmticas y de
salidas justamente como hemos desarrollado maquinas FA de 1000 y Moore, podemos
desarrollar PDA que son llamados pushdown transducer estos son muy importantes pero
pertenecen al estudio de la teora de compiladores

La tarea de convertir expresiones aritmticas infijas ( normales ) a posfija puede
tambin ser llevada a cabo por un push down traducer como una alternativa que esta
ntimamente relacionada con los arboles de parsing. Esta vez todo lo que requerimos es un
PDA con una instruccin adicional PRINT. La cadena de entrada ser leda desde la cinta
carcter por carcter. Si el carcter es un nmero ( o, en nuestro ejemplo, las letras a, b, c ),
es inmediatamente imprimido en la salida ya que los operandos en posfijo ocurren en el
mismo orden como en el equivalente infijo. Los operadores, sin embargo, + y * en nuestro
ejemplo, deben esperar a ser imprimidos hasta despus del segundo operando ya que ellos
son los que gobiernan cuando sern imprimidos. El lugar donde los operadores esperan es
naturalmente en la pila. Si leyramos a +b, entonces primero imprimimos a, ponemos el
signo +en la pila, luego imprimimos b, sacamos el signo +de la pila, e imprimimos el signo
+.
Por lo tanto los estados de las salidas que necesitamos son :

Pgina N 29
PUSH
+, *, (
a, b, c READ
PRINT
(
* , + POP
PRINT


" POP- PRINT " imprime cualquiera cosa que justo se este por sacar de la
pila, y :

" READ-PRINT " imprime el carcter que justo se este por leer de la pila.

EL READ-PUSH saca cualquiera carcter "+" o "* " o "( " o cualquier etiqueta que este
encabezando la pila. Estas son todas las partes de la mquina que necesitamos.

Un comentario ms que deberamos hacer acerca de cuando un operador est listo
para ser sacado.

El segundo operando es reconocido encontrando :


( 1 ) un parntesis derecho,

( 2 ) otro operador que tiene menor o igual precedencia, o

( 3 ) el final de una cadena de entrada.


Pgina N 30
Cuando un parntesis derecho es encontrado, significa que la expresin infija esta
completamente respaldada por el ultimo parntesis izquierdo.

Por ejemplo, considera la expresin :

a* ( b +c ) +b +c

El traductor push down se har como siguiente :

1. Leer a, imprimir a
2. Leer *, sacar *
3. Leer ( , poner (
4. Leer b, imprimir b
5. Leer +, poner +
6. Leer c, imprimir c
7. Leer ), sacar +, imprimir +
8. Sacar ( ,
9. Leer +, no podemos poner +por encima del *, por precedencia de operador .
As mismo sacar *, imprimir * poner +
10. Leer b, imprimir b
11. Leer +, no podemos poner + por encima del +de este modo imprimimos +
12. Leer c, imprimimos c
13. Leemos A, sacamos +, imprimimos +.


El resultado de la secuencia de la salida es :

abc +* b +c +

La cual sin duda es el equivalente posfijo correcto de la entrada. Note que la precedencia del
operador es estructurado dentro de esta mquina. Las generalizaciones de esta mquina
pueden manejar cualquier expresin aritmtico incluyendo -, /, y * *.

Note que la impresin tiene lugar sobre el final derecho de la secuencia de salida.
Otra observacin trivial es que esta mquina nunca imprimir cualesquier parntesis.
Ninguno de los parntesis son necesitados para entender la notacin posfija o prefija. Otro
observacin es que cada operador y operando de la expresin original sern imprimidos
completamente. La principal observacin es que aun cuando la salida de este traductor es
luego alimento para un traductor previo, la expresin aritmtica original infija ser evaluada
correctamente. De este modo podemos evaluar expresin PDA en una notacin aritmtica
normal, y el PDA ser evaluado posteriormente.




Pgina N 31


Pgina N 1
ANALISIS SINTCTICO




Principio de preanlisis


Si consideramos la forma en que los autmatas aceptan sus cadenas de entrada.
Recuerde que para aceptar una cadena, un autmata de pila debe cumplir con su criterio de
aceptacin despus de leer la cadena pero sin desplazar ms all su cabeza de lectura por la
cinta. Esto significa que un autmata de pila debe llegar a un estado de aceptacin sin
realmente leer la marca de fin de cadena.

El resultado de esto es lo que podramos considerar como un proceso de aceptacin
pasivo en el cual la mquina debe " caer " en un estado de aceptacin sin que se lo indique
una marca de fin de cadena. Por el contrario, si imaginamos un sistema mediante el cual la
maquina pudiera observar el siguiente el cual la mquina pudiera observar el siguiente
smbolo de la cinta ( sin tener que avanzar su cabeza de lectura ), podra detectar la marca
de fin de cadena prxima y ejecutar actividades especiales " de cierre " que de otra manera
no ejecutara.

Nos referimos a la tcnica de observar ( leer ) los smbolos siguientes sin procesarlos
como principio de preanlisis. Su aplicacin se ejemplifica con la estructura del ciclo while
del segmento de programa de la fig. 2.24. En esencia, este segmento observa el siguiente
smbolo y emplea la informacin obtenida para decidir si procesa o no el smbolo dentro de
la estructura while.

En las distintas opciones del enunciado case, la rutina finalmente decide si consume
el smbolo y se prepara para procesar otro. As, la variable smbolo es en realidad un rea de
almacenamiento temporal, o buffer, para el smbolo siguiente. Para tomar una decisin, se
puede considerar un smbolo all almacenado, sin que tenga que consumirse ( o leerse
oficialmente ) hasta tomar la decisin de procesarlo. De modos especfico, esta rutina no
consumir la marca de fin de cadena sino que permanecer en el buffer tras terminar la
estructura while, donde estar disponible como entrada para la siguiente rutina. Despus de
todo, en un compilador real es posible que la marca de fin de cadena sea el primer smbolo
de la siguiente estructura que haya que analizar.

Vemos entonces que el principio de preanlisis es una tcnica que aplica autmatas
de pila a segmentos de programas, la teora de autmatas de pila proporciona la base para
construir rutinas de anlisis sintctico destinadas a una amplia gama de lenguajes
independientes del contexto.



Pgina N 2


leer(smbolo);
while (smbolo no es la marca de fin de cadena)
case smbolo of
x : insertar (y) y leer (smbolo)
y : salir a la rutina de error
z : insertar (#) y leer (smbolo)
end case
end while


Figura 2.24 Ciclo While tpico



ANALIZADORES SINTCTICOS LL(K)


Ahora es el momento de considerar cmo pueden desarrollarse las rutinas de anlisis
sintctico a partir de los autmatas de pila.


Proceso de anlisis sintctico LL


Pero primero vemos : Una tcnica para traducir gramticas independientes del
contexto a autmatas de pila. Esta construccin produce un autmata de pila que analiza su
cadena de entrada marcando antes el fondo de la pila e insertando en la pila el smbolo inicial
de la gramatica. Luego repite los tres pasos siguientes, segn resulte aplicable :

1) Si la cima de la pila contiene un no terminal de la gramatica, reemplace ese no
terminal de acuerdo con una de las reglas de reescritura de la gramtica.

2) Si la cima de la pila contiene un terminal, elimnelo de la pila a la vez que lee de la
entrada el mismo terminal. Si el smbolo en la entrada no equivale al smbolo de la pila, se
declara que la entrada es una cadena ilegal.

3) Si aparece en la superficie de la pila la marca de fondo de pila, elimnela y declare
que es aceptable la porcin de la cadena de entrada procesada hasta el momento.

Recuerde que este proceso analiza la sintaxis de la cadena de entrada produciendo
una derivacin por la izquierda conforme lee la cadena de izquierda a derecha. Los
analizadores sintcticos desarrollados de esta manera se conocen como analizadores
sintcticos LL. La primera L denota que el analizador lee su entrada de izquierda a derecha
Pgina N 3
(Left to right, en ingles); la segunda L indica que el objetivo del analizador sintctico es
producir una derivacin por la izquierda (leftmost derivation, en ingles).

La figura 2.25b muestra un diagrama de transiciones construido a partir de la
gramtica de la figura 2.25a usando el proceso descripto.

Para producir una rutina de anlisis sintctico a partir de esta gramatica, podemos
convertir las transiciones de la mquina directamente a enunciados de programa, obteniendo
as la rutina de la fig. 2.26, donde hemos empleado la estructura while tradicional para
simular las actividades disponibles para la mquina cuando se encuentra en el estado q (
mientras el smbolo en la cima de la pila no sea la marca de fondo de pila, la mquina
permanecer en el estado q ).

Obviamente, el segmento de la fig. 2.26 no es un producto terminado. Ya que,
nuestra rutina supone que el siguiente smbolo de la entrada es una x y ejecuta la instruccin
" leer una x de la cadena de entrada ". En realidad, el siguiente smbolo puede ser
distinto de x, por lo que nuestra rutina debe tomar en cuenta esta posibilidad. Debido a ello
debemos ampliar la instruccin " leer una x de la cadena de entrada " para obtener el par de
instrucciones que se muestra a continuacin :

leer(smbolo);
if smbolo no es x then salir a la rutina de error;



a) S xSy
S



b)

f
q
p
i



Pgina N 4

Fig. 2.25 Gramtica independiente del contexto y diagrama de transiciones asociado
para un autmata de pila

Estado : =i;
insertar (#);
Estado : =p;
insertar (S);
Estado : =q;
while cima-de-la-pila = do
case cima-de-la-pila of
S: extraer (S) e inser (xSY), o extraer (S);
x: extraer (x), leer una x de la entrada;
y: extraer (y), leer una y de la entrada;
end case
end while;
extraer (#);
Estado : =f


FIG. 2.26 Segmento de " programa" obteniendo al traducir a enunciados el diagrama
de la fig. 2.25





Estado : =i;
insertar (#);
Estado : =p;
insertar (S);
Estado : =q;
leer (smbolo);
while cima-de-la-pila = #do
case cima-de-la-pila of
S : if Smbolo = x the extraer (S)
else extraer (S), insertar (xSy);
x : if smbolo no es y then salir a la rutina de error
else extraer (x), leer (smbolo);
y : if smbolo no es y then salir a la rutina de error
else extraer (y), leer (smbolo);
end case
end while;
extraer (#)
if Smbolo no es la marca de fin de cadena then salir a la rutina de error;
Pgina N 5
Estado : =f


Figura. 2.27 Rutina de anlisis sintctico basada en la gramatica de la fig. 2.25

S xSz
S xyTyz
T

Figura 2.28 Gramatica independiente del contexto que requiere un analizador
sintctico LL ( 2 )


Con base en lo anterior, podemos convertir el diagrama no determinista de la
fig. 2.25 en el segmento de programa determinista que se muestra en la fig. 2.27. Aqu
hemos utilizado la variable Smbolo como un almacenamiento temporario para el siguiente
smbolo de la entrada. A partir de este almacenamiento temporal es posible interrogar cul es
el smbolo cada vez que se tengan que tomar decisiones, pero sin procesarlo antes de que
sea necesario.

El problema que surge en el ejemplo anterior es un fenmeno comn en los
analizadores sintcticos LL, pues se origina cuando la gramtica propone ms de una forma
de reescribir el mismo no terminal. Por eso, la actividad bsica de los analizadores
sintcticos LL es predecir cual de las distintas reglas de reescritura es la que debe usarse
para procesar los smbolos de entrada restantes. Por consiguiente, a estos analizadores se les
llama analizadores sintcticos predictivos.

Si furamos a construir un analizador sintctico a partir de la gramatica de la
fig. 2.28, encontraramos que la decisin con respecto a la reescritura de S no puede
resolverse con solo observar el siguiente smbolo de entrada ( el conocimiento de que el
siguiente smbolo es x no nos indica que debemos aplicar S xSy en vez de S xyTyz ). En
cambio, la decisin depende de los dos smbolos siguientes.

Entonces, para desarrollar una rutina determinista de anlisis sintctico debemos contar con
espacio de almacenamiento temporal para dos smbolos de entrada.

Como resultado, existe una jerarqua de analizadores sintcticos LL cuya
caracterstica distintiva es el numero de smbolos de entrada que comprende su sistema de
preanlisis. Estos analizadores se llaman analizadores sintcticos LL(k), donde k es un
entero que indica el nmero de smbolos preanalizados por el analizador sintctico. El
ejemplo de la fig. 2.27 es un analizador sintctico LL(1), mientras que un analizador
sintctico basado en la gramatica de la fig. 2.28 seria un analizador sintctico LL(2).


Tablas de anlisis sintctico LL
Pgina N 6


Una tabla de anlisis sintctico para un analizador sintctico LL(1) es una matriz
bidimencional. Las filas se etiquetan con los no terminales de la gramtica sobre la cual se
basa el analizador sintctico. Las columnas se etiquetan con los terminales de la gramtica
ms una columna adicional FDC ( que representa la marca de fin de cadena ). El elemento
( m,n ) de la tabla indica la accin que deber seguirse cuando el no terminal m aparece en la
cima de la pila y el smbolo de preanlisis es n.

Una vez que se ha construido la tabla de anlisis sintctico, la tarea de escribir un
segmento de programacin que efecte el anlisis sintctico del lenguaje es bastante sencilla.

Por ejemplo, la rutina de la figura 2.30 es un analizador sintctico LL( 1 ) que utiliza
la tabla de la figura 2.29


a b z FDC
S error error zMNz error
M aMa error z error
N error bNb z error


Figura 2.29 Tabla de anlisis sintctico LL( 1 ) para la gramatica de la figura 2.5



insertar ( S );
leer ( Smbolo );
while pila no esta vaca do
case cima-de-lapila of
terminal: if cima-de-la-pila =Smbolo
then extraer de la pila y leer ( Smbolo )
else salir a la rutina de error;
no terminal: if tabla [ cima-de-la-pila, Smbolo ] = error
then reemplazar cima-de-la-pila por tabla
[ cima-de-la-pila, Smbolo ]
else salir a la rutina de error;
end case
end while
if Smbolo no es la marca de fin de cadena then salir a la rutina de error


Figura 2.30 Rutina genrica de anlisis sintctico LL ( 1 )


Pgina N 7

x y FDC
S xSy


Figura 2.31 tabla de anlisis sintctico LL ( 1 ) para la gramtica 2.7
Otra ventaja de utilizar tablas de anlisis sintctico es el que permiten normalizar el
algoritmo de anlisis. Cualquier analizador sintctico LL ( 1 ) puede emplear el mismo
algoritmo; si se desea obtener un analizador sintctico para otro lenguaje, basta con sustituir
la tabla de anlisis sintctico por una nueva.


ANALIZADORES SINTCTICOS LR ( k )


En la seccin anterior mencionamos que la naturaleza predictiva de los analizadores
sintcticos LL ( k ) restringe la clase de lenguajes que pueden manejar estos analizadores. En
esta seccin presentamos una clase de analizadores sintcticos que evitan muchos de los
problemas relacionados con sus homlogos predictivos. Estos analizadores se conocen
como analizadores sintcticos LR ( k ), ya que leen su entrada de izquierda a derecha (
Left to right, en ingles ) mientras, constituyen una derivacin por la derecha ( Right
derivation, en ingles ) de sus cadenas de entrada utilizando un sistema de preanlisis que
comprende k smbolos.


Proceso de anlisis sintctico LR


En trminos generales, un analizador sintctico LR transfiere smbolos de su entrada
a la pila hasta que los smbolos superiores de la pila sean iguales al lado derecho de alguna
regla de reescritura de la gramtica en que se basa el analizador. Al llegar a este punto, el
analizador sintctico puede reemplazar estos smbolos con el no terminal que se encuentra
en el lado izquierdo de la regla de reescritura antes de transferir otros smbolos de la entrada
a la pila. De esta manera, la pila acumula cadenas de terminales y no terminales , que a su
vez son reemplazada por no terminales " ms altos " de la gramtica. Por ltimo, todo el
contenido de la pila se reduce al smbolo inicial de la gramtica, indicando que los smbolos
ledos hasta ese punto forman una cadena que puede derivarse con la gramtica.

Con base en este esquema general, los analizadores sintcticos LR ( k ) se clasifican
como analizadores sintcticos ascendentes, ya que sus actividades corresponden a la
construccin de ocurrencias de no terminales a partir de sus componentes, hasta generar el
smbolo inicial de la gramtica. En comparacin, los analizadores sintcticos LL ( k ) se
conocen como analizadores sintcticos descendentes pues comienzan con el smbolo inicial
en la pila y repetidamente, dividen los no terminales de la pila en sus componentes, hasta
generar una cadena de smbolos equivalente a la cadena de entrada.
Pgina N 8

Un analizador sintctico LR ( k ) se basa en un autmata construido a partir de una
gramtica independiente del contexto, con la excepcin de que el autmata se construye de
la manera descripta en los cinco pasos siguientes :


1. Establezca cuatro estados : uno inicial llamado i, un estado de aceptacin llamado f
y otros dos estados llamados p y q.

2. Introduzca las transiciones ( , , ; p, # ) y ( q, , #; f, ), donde suponemos que
#es un smbolo que no se presenta en esta gramtica.

3. Para cada smbolo terminal x en la gramtica, introduzca la transicin ( p, x, ; p, x ).
Estas transiciones permiten que el autmata transfiera a la pila los smbolos de entrada
mientras se encuentra en el estado p.
La ejecucin de esta transicin se llama operacin de desplazamiento ya que su efecto es
desplazar un smbolo de la cadena de entrada a la pila.

4. Para cada regla de reescritura N w ( donde w representa una cadena de uno o ms
smbolos ) que exista en la gramtica,, introduzca la transicin ( p, , w; p, N ). ( Aqu
permitimos que una transicin elimine ms de un smbolo de la pila. Para ser ms precisos, la
transicin ( p, I, xp; p, z ) es una forma abreviada de transicion ( p, , y; p1, ) seguida por
( p1 , , x; p, z ), donde p1 es un estado al que no puede llegar ninguna otra transicin. As,
para ejecutar la transicin ( p, , xy; p, z ) un autmata debe tener una y en la cinta de la pila
con una x inmediatamente debajo ). La presencia de estas transiciones significa que si los
smbolos de la porcin superior de la pila concuerdan con los del lado derecho de la regla de
reescritura, entonces es posible reemplazar esos smbolos con el no terminal de la parte
izquierda de esa regla.

La ejecucin de esta transicin se llamaoperacin de reduccin ya que su efecto es
reducir el contenido de la pila a una forma ms simple.

5. Introduzca la transicin ( p, , S; q, ), donde S es el smbolo inicial de la gramtica.
La presencia de esta transicin significa que si se han reducido a S los smbolos de la pila, el
autmata puede pasar al estado q a la vez que extrae S de la pila.


Implantacin de analizadores sintcticos LR (k )


Se presentan dos problemas principales al tratar de convertir los autmatas de pila,
como el que se muestra en la figura 2.32, a un formato de programa ms tradicional. El
primero tiene que ver con el no determinismo, de manera similar a lo que sucede con los
analizadores sintcticos LL : si se presenta una opcin, Cmo saber si debemos desplazar
o reducir ?
Pgina N 9

Ademas, si nuestra opcin es reducir, puede existir ms de una reduccin posible ( si
z esta en la cima de la pila, reducimos con ( p, , z; p, M ), o con ( p, , z; p, N ) ?.

El segundo problema tiene que ver con los aspectos tcnicos de la interrogacin de la
pila. Por ejemplo, antes de que podamos decidir si ejecutamos la transicin
( p, , aMa; p, M ) debemos ser capaces de deducir que los tres smbolos superiores de la
pila son a, M y a.

De hecho, en el caso de los lenguajes deterministas independientes del contexto,
puede integrarse en una sola tabla de anlisis sintctico toda la informacin necesaria para
resolver este problema de interrogacin de la pila, as como el problema de eleccin de
opciones.


Los problemas mencionados en el punto anterior se resuelven con las figuras :
2.34 / 2.35 / 2,36
:



a b z FDC S M N
1 desplazar 2 14
2 desplazar 3 desplazar 7 4
3 desplazar 3 desplazar 7 8
4 desplazar 5 desplazar 9 6
5 desplazar 5 desplazar 9 10
6 desplazar 11
7 M z M z M z
8 desplazar 12
9 N z N z
10 desplazar 13
11 S zMNb
12 M aMa M aMa M aMa
13 N bNb N bNb
14 aceptar


Figura 2.34 Tabla de anlisis sintctico LR( 1 ) basada en la gramatica de la figura
2.5





Pgina N 10










Smbolo Especial : =1;
insertar ( Smbolo Especial );
leer ( Smbolo );
Valor Tabla : =[ Smbolo Especial, Smbolo ];
while Valor Tabla no es " aceptar " do
if Valor Tabla es un desplazamiento
then begin
insertar ( Smbolo ); Smbolo Especial : =Valor Tabla. Estado;
insertar ( Smbolo Especial ); leer ( Smbolo )
end
else if Valor Tabla es una reduccin
then Begin
extraer ( lado derecho de Valor Tabla. ReglaReescritura );
Smbolo Especial : =cima-de-la-pila; ( * Esto no es una extraccin * )
insertar ( lado izquierdo de Valor Tabla.ReglaReescritura );
Smbolo Especial : =Tabla [ Smbolo Especial, lado izquierdo de
Valor Tabla.ReglaReescritura ];
insertar ( Smbolo Especial )
end
else if valor Tabla esta en blanco then salir a la rutina de error;
Valor Tabla : =Tabla [ Smbolo Especial, Smbolo ];
end while;
if Smbolo no es FDC then salir a la rutina de error;
vaciar pila




Figura 2.35 Algoritmo de anlisis sintctico LR ( 1 )






Pgina N 11














Contenido de la pila Resto de la
entrada

vaca zazabzbz
zazabzbz
z azabzbz
z a zabzbz
z a z abzbz
z a M abzbz
z a M a bzbz
z M bzbz
z M b zbz
z M b z bz
z M b N z
z M b N b z
z M N
z M N z
S

vaca



Figura 2.36 Anlisis sintctico de la cadena zazabzbz con el algoritmo 2.35 y la tabla

de la figura 2.34





Pgina N 12






Pgina N 13
N N
Z
b
b
b
Z
Z
a
a
M M
a
Z
Z
Z
S
5
11
6
13
10
9
5
4
12
8 3
7
2
14
1



Figura 2.37 Autmata finito a partir del cual se construyo la tabla de la figura 2.34

Pgina N 14
Tablas de anlisis sintctico LR


Aunque la construccin de una tabla de anlisis sintctico LR corresponde ms a la
construccin de compiladores que a la teora de los lenguajes formales, no debemos ignorar
por ejemplo este tema. De hecho, la construccin de estas tablas es una importante
aplicacin de la teora de autmatas finitos. La tabla de un analizador sintctico LR ( 1 ) se
basa en la existencia de un autmata finito que acepta exactamente las cadenas de smbolos
de la gramtica ( terminales y no terminales ) que conducen a operaciones de reduccin.

El diagrama de la figura 2.37 es el diagrama de transiciones del autmata finito a
partir del cual se construyo la tabla de anlisis sintctico de la figura 2.34.

La idea es comenzar el proceso de anlisis sintctico siguiendo la ruta determinada
por la cadena de entrada hasta encontrar un estado de aceptacin. Al llegar a este punto, la
ruta recorrida por el autmata finito corresponde al patrn de smbolos que el analizador ha
desplazado a la pila. Entonces, el proceso de anlisis retrocede por esta ruta recorriendo los
smbolos que deben eliminarse de la pila del analizador durante la operacin de reduccin. A
partir de este punto, el analizador sintctico recorre el arco del diagrama de transiciones del
autmata finito que tenga etiqueta equivalente al no terminal colocado en la pila por la
operacin de reduccin correspondiente. As, una vez que se ha encontrado la operacion de
reduccion, los smbolos en la pila del analizador correspondern una vez ms a los smbolos
de la ruta que recorre el autmata finito.

Hay que hacer un comentario final antes de abandonar nuestro tratamiento de los
analizadores sintcticos LR. Para completar un compilador es necesario combinar un
analizador con un generador de cdigo que produzca instrucciones en el lenguaje objeto que
reflejen las estructuras detectada por el analizador. Al emplear un analizador sintctico LR,
este enlace entre el analizador y el generador de cdigo se presenta en relacin con cada
operacin de reduccin.

De hecho, en esta etapa del proceso de anlisis sintctico donde se ha recorrido una
estructura, por lo que es el momento indicado para solicitar al generador de cdigo que
construya el cdigo para esa estructura. En resumen, los clculos efectuados por el
analizador sintctico y el generador de cdigo siguen el patrn bsico.


repeat
analizar la sintaxis hasta ejecutar una reduccin
generar cdigo
until termina el anlisis sintctico




Pgina N 15

Comparacin entre los analizadores sintcticos LR ( k ) y LL ( k )


Para concluir, debemos confirmar que la coleccin de analizadores sintcticos
LR (k ) es ms poderosa que la de los analizadores LL ( k ) . Ya hemos planteado que
ningn analizador sintctico LL ( k ) puede manejar el lenguaje { x ^n : n n } { x ^n y
^n : n n }.

Existen, no obstante, lenguajes independientes del contexto que ningn
analizador sintctico LR (k) puede reconocer.

Un ejemplo de lenguaje independiente del contexto que un analizador sintctico
LR ( k ) no puede analizar el lenguaje

{ x ^n : n n ^+} { x ^n y ^2 n : n n ^+}

es incapaz de seleccionar la regla de reescritura correcta.

Vous aimerez peut-être aussi