Anda di halaman 1dari 90

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

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.

- 2 -
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 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
- 3 -
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 programa distinto, llamado
procesador. El preprocesador tambin puede abreviaturas, llamadas macro o preposiciones del
lenguaje fuente.


- 4 -
BIBLIOTECA, ARCHIVO
OBJ ETO RELOCALIZAB
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



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
- 5 -
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.


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.

- 6 -
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.

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.

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

- 8 -


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


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

\hbox{<lista de cajas>}

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

CAJ A sub caja

Reduce el tamao de caja y la une a CAJ A 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}

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.
- 10 -



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

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


- 11 -
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.


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.

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









- 13 -










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



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.

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


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.
- 15 -

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.

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.
- 16 -
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\J ACM #1; #2 ; #3 .
{{\ s1 J . ACM}{\bf #1}: #2 , pags. #3 .}

El nombre de la macro es J ACM, 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 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

\J ACM 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 \ J ACM. 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
- 17 -
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.

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 :
- 18 -
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)
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.

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.

- 19 -
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 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
- 20 -
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 nmero de pasadas


Es deseable tener relativamente pocas pasadas, dado que la lectura y escritura de
archivos intermedios lleva tiempo. Adems si se agrupan varias fases dentro de una pasada,
pueden ser necesarios 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 haya 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 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
- 21 -
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


El escritor del compilador, como cualquier programador, puede usar con provecho
herramientas de software tales como depuradores, administradores de versiones, analizadores,
etc. Adems 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.

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 ms " traducciones " con cada nodo del rbol de anlisis sintctico, y cada
traduccin se define partiendo de traducciones en sus nodos vecinos en el rbol.

- 22 -
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 esencialmente 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 Backus-Naur). Con las
notaciones disponibles hoy es mucho ms difcil describir la semntica de un lenguaje que su
sintaxis.

Adems, 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 infijos a la forma posfijo, una notacin en la que los operadores aparecen
despus de sus operndoos. 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
ms 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.

- 23 -
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 carcter 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
REPRESENTACIO
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.

Una gramtica describe la forma natural la estructura jerrquica 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 denomina
produccin. 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.
- 24 -

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:

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 escribe E , 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.

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

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

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

c) 9 - 5 +2 es una lista 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

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


- 26 -
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 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
- 27 -
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.


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

- 28 -
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.

-
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

- 29 -

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 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 desarrollar
expresiones que tengan anidamiento de profundidad arbitraria ( y tambin arboles de profundidad
arbitraria ).


Sintaxis de proposiciones

- 30 -

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.


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.

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


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'


- 32 -
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 de expr 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

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

asociada con esta produccin define el valor del atributo expr.t mediante la concatenacin de las
formas posfijas 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 gramtica
siguiente:

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

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


- 34 -
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 e
instr.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, 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
semnticas. 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.


- 35 -




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



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 subrbol
de termino, pero antes de visitar al hijo resto1.

- 36 -
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 lnea de
puntos. Por ejemplo, la parte del rbol de anlisis sintctico para la produccin y la accin anterior
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


En este captulo, las acciones semnticas en los esquemas de traduccin escribirn la
salida de una traduccin en un archivo, una cadena o un carcter a la vez. Por ejemplo, se traduce
9 - 5 +2 a 95 - 2 + imprimiendo cada carcter 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
- 37 -

resto +trmino resto1

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


pero de nuevo, el no terminal trmino 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

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 subrbol para el
operando izquierdo expr cuando se recorre el subrbol 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 subrbol para el operando derecho trmino y, por ultimo, se realiza la accin semntica {
print ('+') }en el nodo adicional.

Como las producciones para trmino 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 +.

- 38 -
{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 gramtica. 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 analizadores
sintcticos directamente a partir de las gramticas tienden a utilizar mtodos ascendentes.


Anlisis sintctico descendente


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

- 39 -

tipo simple
| 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


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 [


- 40 -
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 subrbol.

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 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 gramtica (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.



- 41 -
rbol de
anlisis tipo
sintctico
a)
array [ num puntopunto ] of integer
entrada


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

array [ num puntopunto num ] of integer
entrada


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

array [ num puntopunto num ] of integer
entrada



Fig. 2.16 Anlisis sintctico 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 terminal de
una gramtica 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 gramtica (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.
- 42 -

El anlisis sintctico con una llamada al procedimiento del no terminal inicial tipo de esta
gramtica. 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




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

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
produccin 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 el lado derecho
de una produccin para el no terminal A. Se define PRIMERO () como el conjunto de
componentes lxicos que opere como los primeros smbolos de una o mas cadenas generadas a
partir de . Si es o puede generar , entonces tambin esta en PRIMERO().

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 y
A . El anlisis sintctico descendente recursivo sin retroceso requiere que
PRIMERO() 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 (),
entonces se usa . De otro modo, si el smbolo de preanlisis esta en PRIMERO(), entonces se
usa .


Cuando se usan las producciones


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


prop begin props_opc end
props_opc lista_props |


Durante el anlisis sintctico de props_opc, si el smbolo de preanlisis no esta en
PRIMERO(lista_props), entonces se usa la produccin . 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 (), se usa la produccin con lado derecho . Si hay un conflicto
entre dos lados derechos de cualquier smbolo de preanlisis, entonces en esa gramtica 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 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 gramtica (2.8).

Al igual que crea un esquema de traduccin extendiendo una gramtica, 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 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.
- 45 -

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

A A |

donde 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, =+trmino, y =trmino.

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

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 R

- 46 -
Aqu, R es un terminal nuevo. La produccin R 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 gramtica subyacente de un esquema dado antes de poderla analizar por un
analizador sintctico predictivo. En particular, la gramtica 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 gramtica recursiva por la izquierda. Eliminando la recursin por
la izquierda, se puede obtener una gramtica adecuada para ser usada en un traductor
descendente recursivo predictivo.





Sintaxis abstracta y sintaxis concreta


- 47 -
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 gramtica subyacente, sintaxis concreta del lenguaje. Los rboles de sintaxis abstracta, o
simplemente rboles sintcticos difieren de los rboles de anlisis sintctico en que las distinciones
superficiales de forma, sin importancia en la traduccin, no aparecen en los rboles 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 gramtica cuyos rboles de
anlisis sintctico se parezcan al mximo a rboles sintcticos. El agrupamiento de
subexpresiones hecho por la gramtica de la figura 2.19 es similar a su agrupamiento en rboles
sintcticos.

Desafortunadamente, la gramtica 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 gramtica que facilite el anlisis sintctico, y por otra,
se precisa una gramtica 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 gramtica de la figura 2.19 y se puede usar
para anlisis sintctico descendente o recursivo.

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

El problema de esta gramtica es que los operandos de los operadores generados por
- 48 -
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 que expr.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 A A en

A R
R R R

Cuando las acciones semnticas se intercalan en las producciones, se trasladan junto con
la transformacin. Aqu, si se hace A =expr, =+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
trmino 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 gramtica anterior


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



La Fig. 2.21 Traduccin de 9-5+2 a 95-2+


Procedimientos para los no terminales expr, termino y resto


Ahora se aplica un traductor en C utilizando el esquema de traduccin dirigida por la
sintaxis (2.14). La esencia 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 carcter 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
- 50 -



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 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 gramtica 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 para resto 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 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 carcter "ms". Como la tercera
produccin para resto tiene 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. Obsrvese 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


- 51 -
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
procedimiento se dice que la llamada es recursiva por el final. Por ejemplo, las llamadas de resto (
) al final de la cuarta y sptima lneas 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 lnea 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 ( );
while(1)
if ( preanlisis =='+' ) {
parea ('+'); termino ( ) ; putchar ('+') ;
}
else if (preanlisis =='-' ) {
parea ('-') ; termino ( ) ; putchar ('-') ;
}
else break;
}


- 52 -
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 carcter 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.







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 carcter de linea al final * /
- 53 -
}
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)
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 +)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 + )5
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 .

Pgina N 2
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.

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.

Pgina N 3

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.




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 :


Pgina N 4

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


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
Pgina N 5
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.

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 6


( 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


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

T F
Pgina N 7

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.

( 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



Pgina N 8
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.








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


Pgina N 9

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








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.


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





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.


6) palabra objetivo Errnea :

Pgina N 11
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

( 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
Pgina N 12
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.


( 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.
Pgina N 13
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

( 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
Pgina N 14
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
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 15
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, 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.

Pgina N 16
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.


El rbol ahora crece como sigue :

Pgina N 17
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


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
Pgina N 18
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 )

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.

Pgina N 19

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 20


































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
CONDITION ALEX =ALEX | ALEX ALEX | ALEX >ALEX
Pgina N 21
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.


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:
Pgina N 22
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



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 )







Pgina N 23







STATE


STACK

TAPE


START
READ
PUSH i




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 +

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

+
+


ADD
READ
PRINT


78
78
78





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

Pgina N 24
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 :

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,
Pgina N 25

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

( 3 ) el final de una cadena de entrada.


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 , 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 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.





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


Pgina N 2
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 (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;

Pgina N 3


a) S xSy
S



b)

f
q
p
i




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

Estado : =;
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 : =;
insertar (#);
Estado : =p;
Pgina N 4
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;
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).
Pgina N 5


Tablas de anlisis sintctico LL


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 )



x y FDC
S xSy


Figura 2.31 tabla de anlisis sintctico LL ( 1 ) para la gramtica 2.7
Pgina N 6
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.

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.

Pgina N 7
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 llama operacin 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 ?

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
Pgina N 8
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















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 9















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

Tablas de anlisis sintctico LR
Pgina N 11


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





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
Pgina N 12

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

es incapaz de seleccionar la regla de reescritura correcta.

Anda mungkin juga menyukai