Está en la página 1de 12

1.

PROCESO DE TRADUCCIN

Un compilador se compone internamente de vanas e t a p a s , q u e r e a l i z a n d i s t i n t a s o p e r a c i o n e s l g i c a s . Es til pensar en estas fases como en piezas separadas d e n t r o d e l c o m pilador, y p u e d e n e n r e a l i d a d e s c r i b i r s e c o m o o p e r a c i o n e s c o d i f i c a d a s s e p a r a d a m e n t e a u n - que e n l a p r c t i c a a m e n u d o s e i n t e g r e n j u n t a s . Las f a s e s d e u n c o m p i l a d o r s e i l u s t r a n e n l a f i g u r a , ju n t o c o n l o s t r e s componentes auxiliares que interactan con alguna de e l l a s o con todas: la tabla de literales, la tabla de smbolos y el manejador de errores.

ANALIZADOR LXICO O RASTREADOR (SCANNER)

Esta fase del compilador efecta la lectura real del programa fuente, el cual generalmente est en la forma de un flujo de caracteres. El rastreador realiza lo que se conoce como anlisis lxico: recolecta secuencias de caracteres en unidades significativas denominadas tokens, las cuales son como las palabras de un lenguaje natural, como el ingls. De este modo, se puede imaginar que un rastreador realiza una funcin similar al deletreo. Como ejemplo, considere la siguiente lnea de cdigo, que podra ser parte de un programa en C: Este cdigo contiene 12 caracteres diferentes de un espacio en blanco pero slo 8 tokens:

A I index

1
= 4

+
2

identificador corchete izquierdo identificador corchete derecho asignacin nmero signo ms nmero

Cada token se compone de uno o ms caracteres que se renen en una unidad antes de que ocurra un procesamiento adicional. Un analizador lxico puede realizar otras funciones junto con la de reconocimiento de tokens. Por ejemplo, puede introducir identificadores en la tabla de smbolos, y puede introducir literales en la tabla de literales (las literales incluyen constantes numricas tales como 3.1415926535 y cadenas de texto entrecomilladas como "iHola, mundo!").

ANALliADOR SINT~CTICO (PARSER)

El analizador sintctico recibe el cdigo fuente en la forma de tokens proveniente del analizador lxico y realiza el anlisis sintctico, que determina la estructura del programa. Esto es semejante a realizar el anlisis gramatical sobre una frase en un lenguaje natural. El anlisis sintctico determina los elementos estructurales del programa y sus relaciones. Los resultados del anlisis sintctico por lo regular se representan como un rbol de anlisis gramatical o un rbol sintctico. Como ejemplo, consideremos otra vez la lnea de cdigo en C que ya habamos dado. Repreienta un elemento estructural denominado expresin, la cual es una expresin de aqignacin compuesta de una expresin con subndice a la izquierda y una expre~in aritmtica entera a la derecha. Esta estructura se puede representar como un jrbol de anllisis gramatical de la forma siguiente:

Advierta que los nodos Internos del rbol de anlisis gramatical estn etiquetados con los nombres de las estructuras que representan y que las hojas del rbol representan la secuencia de tokens de la entrada. (Los nombres de las estructuras estn escritos en un tipo de letra diferente para distinguirlos de los tokens.) Un rbol de anlisis gramatical es un auxiliar til para visualizar la sintaxis de un programa o de un elemento de programa, pero no es eficaz en su representacin de esa estructura. Los analizadores sintcticos tienden a generar un rbol sintctico en su lugar, el cual es una condensacin de la informacin contenida en el rbol de anlisis gramatical.

(En ocasiones los rboles sintcticos se denominan rboles sintcticos abstractos porque representan una abstraccin adicional de los rboles de anlisis gramatical.) Un rbol sintctico abstracto para nuestro ejemplo de una expresin de asignacin en C es el siguiente:

Advierta que en el rbol sintctico muchos de los nodos han desaparecido (incluyendo los nodos de tokens). Por ejemplo, si sabemos que una expresin es una operacin de subndice, entonces ya no ser necesario mantener los parntesis cuadrados [ y ] que representan esta operacin en la entrada original.

ANALIiADOR SEMANTICO

La semntica de un programa es su "significado", en oposicin a su sintaxis, o estructura. La semntica de un programa determina su comportamiento durante el tiempo de ejecucin, pero la mayora de los lenguajes de programacin tienen caractersticas que se pueden determinar antes de la ejecucin e incluso no se pueden expresar de manera adecuada como sintaxis y analizarse mediante el analizador sintctico. Se hace referencia a tales caractersticas como semntica esttica, y el anlisis de tal semntica es la tarea del analizador semntico. (La semntica "dinmica" de un programa, es decir, aquellas propiedades del programa que solamente se pueden determinar al ejecutarlo, no se pueden determinar mediante un compilador porque ste no ejecuta el programa.) Las caractersticas tpicas de la semntica esttica en los lenguajes de programacin comunes iicluyen las declaraciones y la verificacin de tipos. Las partes extra de la informacin (como los tipos de datos) que se calculan mediante el analizador semntico se llaman atributos y con frecuencia se agregan al rbol como anotaciones o "decoraciones". (Los atributos tambin se pueden introducir en la tabla de smbolos.) En nuestro ejemplo de ejecucin de la expresin en C

la informacin de tipo especfica que se tendra que obtener antes del anlisis de esta lnea sera que a sea un arreglo de valores enteros con subndices proveniente de un subintervalo de los enteros y que index sea una vaiabie entera. Entonces el 'analizador semntico registrda el rbol sintc6co con los tipos de todas las subexpresiones y posteriormente verificara que la asignacin tuviera sentido para estos tipos, y declarara un error de correspondencia de tipo si no fuera as. En nuestro ejemplo todos los tipos tienen sentido, y el resultado del anlisis semntico en el rbol sintctico podra representarse por el siguiente rhol con anotaciones:

OPTlMlZADOR DE CDIGO FUENTE

Los compiladores a menudo incluyen varias etapas para el mejoramiento, u optimizacin, del cdigo. El punto ms anticipado en el que la mayora de las etapas de optimizacin se pueden realizar es precisamente despus del anlisis semntico, y puede haber posibilidades para el mejoramiento del cdigo que dependern slo del cdigo fuente. Indicamos esta posibilidad al proporcionar esta operacin como una fase por separado en el de compilacin. Los compiladores individuales muestran una amplia variacin no slo en los tipos de optimizaciones realizadas sino tambin en la colocacin de las fases de optimizacin. En nuestro ejemplo incluimos una oportunidad para la optimizacin a nivel de fuente; a saber, la expresin 4 + 2 se puede calcular previamente por el compilador para ciar como resultado 6. (Esta optimizacin particular es conocida como incorporacin de constantes.) Claro que existen posibilidades mucho nis con>plejas (algunas de las cuales se mencionarn en el captulo 8). En nuestro ejemplo esta optimizaci6n se puede realizar de manera directa sobre el rbol sintctico (con anotaciones) al colapsar el subrbol secundario de la derecha del nodo raz a su valor constante:

Muchas optimizaciones se pueden efectuar directamente sobre el rhol. pero en varios casos es irs I'cil optiniizar una forma linealizada del irbol que est ms cercana al cdigo ensamblador. Existen muchas variedades diferentes de tal cdigo, pero una elecciGn estndar es el cdigo en tres direcciones, denominado as porque contine las direcciones de (y hasta) tres localidbdes en memoria. Otra seleccin popular es el cdigo P. el cual se ha utilizado en muchos compiladores de Pascal. En nuestro ejemplo el cdigo en tres direcciones para la expresin original en C podra parecerse a esto:
t = 4 + 2

a [index] = t

(Advierta el uso de una variable temporal adicional t para almacenar el resulrado intermedio de la suma.) Ahora el optimizador mejorara este cdigo en dos etapas, en primer lugar calculando el resultado de la suma:

y despus reemplazando a t por su valor para obtener la sentencia en tres direcciones

En la figura indicamos la posibilidad de que el optimizador del cdigo fuente pueda emplear cdigo en tres direcciones al referirnos a su salida como a un cdigo intermedio. Histricamente, el cdigo intermedio ha hecho referencia a una forma de representacin de cdigo intermedia entre el cdigo fuente y el cdigo objeto, tal como el cdigo de tres direcciones o una representacin lineal semejante. Sin embargo, tambin puede referirse de manera ms general a crcalquier representacin interna para el cdigo fuente utilizado por el compilador. En este sentido, tambin se puede hacer referencia al rbol sintctico como un cdigo intermedio. y efectivamente el optimizador del cdigo fiieiite podra continuar el uso de esta representacin en su salida. En ocasiones este sentido ms general se indica al hacer referencia al cdigo intermedio como representacin intermedia, o RI.

GENERADOR DE CODIGO

El generador de cdigo toma el cdigo intermedio o RI y genera el cdigo par3 la niquina objetivo. En este texto escribiremos el c6digo objetivo en la fornia de lenguaje ensarnh1;idor para facilitar su coinprerisin, aunque la mayora tle los conipiladores generan el cdigo objeto de manera directa. Es en esta fase de la compilacin en la que las propiedades de la mquina objetivo se convierten en el factor principal. No slo es necesario emplear instrucciones que existan en la mquina objetivo, sino que las decisiones respecto a la representacin de los datos desempearn ahora tambin un papel principal, tal como cuntos bytes o palabras de memoria ocuparn las variables de tipos de datos enteros y de punto flotante.

En iruestro ejemplo debemos decidir ahora cuntos enteros se almacenarn para generar el cdigo para la indizacin del arreglo. Por ejemplo, una posible secuencia de cdigo muestra para la expresin dada podra ser (en un hipottico lenguaje ensamblado~)
MOV MUL MOV ADD MOV RO, index ; ; valor de index -> RO RO, 2 ;; doble valor en RO R1, &a ;; direccin de a -> R1 R1, RO ;; sumar RO a R1 *R1, 6 ;; constante 6 -> direccin en R1

En este cdigo utilizamos una convencin propia de C para direccionar modos, de manera que &a es la direccin de a (es decir, la direccin base del arreglo) y que *R1 significa direccionamiento indirecto de registro (de modo que la ltima instruccin almacena el valor 6 en la direccin contenida en Rl). En este cdigo tambin partimos del supuesto de que la mquina realiza direccionamiento de byte y que los enteros ocupan dos bytes de memoria (de aqu el uso del 2 como el factor de multiplicacin en la segunda instruccin).

OPTlMlZADOR DE C~DIGO OBJETIVO

En esta fase el compilador intenta mejorar el cdigo objetivo generado por el generador de cdigo. Dichas mejoras incluyen la seleccin de modos de direccionamiento para mejorar el rendimiento, reemplazando las instrucciones lentas por otras rpidas, y eliminando las operaciones redundantes o innecesarias. En el cdigo objetivo de muestra dado es posible hacer varias mejoras. Una de ellas es utilizar una instruccin de desplazamiento para reemplazar la multiplicacin en la segunda instruccin (la cual por lo regular es costosa en trminos del tiempo de ejecucin). Otra es emplear un modo de direccionamiento ms poderoso, tal como el direccionamiento indizado para realizar el almacenamiento en el arreglo. Con estas dos optimizaciones nuestro cdigo objetivo se convierte en
MOV RO, index ; ; valor de index -> RO SHL RO ; doble valor en RO MOV &a[ROl, 6 ;; constante 6 -> direccin a

+ RO

Esto completa nuestra breve descripcin de las fases de un compilador. Querernos enfatizar que esta descripcin slo es esquemtica y no necesariamente representa la organizacin real de un compilador trabajando. En su Lugar, los compiladores muestran una amplia variacin en sus detalles de organizacin. No obstante, las fases que describimos estn presentes de alguna forma en casi todos los compiladores.

Tambin analizamos slo de manera superficial las estructuras de datos necesarias para mantener la informacin necesaria en cada fase, tales como el rbol sintctico, el cdigo intermedio (suponiendo que stos no sean iguales), la tabla de literales y la tabla de smbolos. Dedicamos la siguiente seccin a una breve perspectiva general de las estructuras de datos principales cn un compilador.

1.4 ESTRUCTURAS DE DATOS PRINCIPALES EN UN COMPIIADOR


La interaccin entre los algoritmos utilizados por las fases de un compilador y las estructuras de datos que soportan estas fases es, naturalmente, muy fuerte. El escritor del compiladar se esfuerza por implementar estos algoritmos de una manera tan eticaz como sea posible, sin aumentar demasiado la complejidad. De manera ideal, un compilador debera poder compilar un programa en un tiempo proporcional a1 tamao del programa, es decir, en O(n) tiempo, donde n es una medida del t a m o del programa (por lo general el nmero de caracteres). En esta seccin sealaremos algunas de las principales estructuras de datos que son necesarias para las fases como parte de su operacin y que sirven para comunicar la informacin entre las fases.

TOKENS

Cuando un rastreador o analizador lxico rene los caracteres en un token, generalmente representa el token de manera simblica, es decir, como un valor de un tipo de datos enumerado que representa el conjunto de tokens del Lenguaje fuente. En ocasiones tambi6n es necesario mantener la cadena de caracteres misma u otra informacin derivada de ella, tal como el nombre asociado con un token identificador o el valor de un token de nmero. En la mayora de los lenguajes el analizador lxico slo necesita generar un token a la vez (esto se conoce como bsqueda de smbolo simple). En este caso se puede utilizar una variable global simple para mantener la informacin del token. En otros casos (cuyo ejemplo ms notable es FORTRAN), puede ser necesario un arreglo de tokens.

RBOL SINTCTICO

Si el analizador sintictico genera un rbol sintctico, por lo regular se construye como una estructura estndar basada en un apuntador que se asigna de manera dinmica a medida que se efecta el anlisis sintiictico. El rbol entero puede entonces conservarse como una variable simple que apunta al nodo raz. Cada nodo en la estructura es un registro cuyos campos representan la informacin recolectada tanto por el analizador sintctico como, posteriornmte, por el analizador semntico. Por ejemplo, el tipo de datos de una expresin puede conservarse como un campo en el nodo del rbol sintctico para la expresin. En ocasiones, para ahorrar espacio, estos campos se asignan de manera dinmica, o se almacenan en otras estructuras de datos, tales como la tabla de smbolos, que permiten una asignacin y desasignacin selectivas. En realidad, cada nodo de rbol sintctico por s mismo puede requerir de atributos diferentes para ser almacenado, de acuerdo con la clase de estructura del lenguaje que represente (por ejemplo, un nodo de expresin tiene requerimientos diferentes de los de un nodo de sentencia o un nodo de declaracin). En este caso, cada nodo en el rbol sintctico puede estar representado por un registro variable,

con cada clase de nodo conteniendo solamente la informacin necesaria para ese caso.

TABU DE S~MBOLOS

Esta estructura de datos mantiene la informacin asociada con los identificadores: funciones, variables, constantes y tipos de datos. La tabla de smbolos interacta con casi todaslas fases del compilador: el rastreador o analizador lxico, el analizador sintctico o el analizador semntico puede introducir identificadores dentro de la tabla; el analizador semntico agregar tipos de datos y otra informacin; y las fases de optimizacin y generacin de cdigo utilizarn la informacin proporcionada por la tabla de smbolos para efectuar selecciones apropiadas de cdigo objeto. Puesto que la tabla de smbolos tendr solicitudes de acceso con tanta frecuencia, las operaciones de insercin, eliminacin y acceso necesitan ser eficientes, preferiblemente operaciones de tiempo constante. Una estructura de datos estndar pasa este propsito es la tabla de dispersin o de clculo de direccin, aunque tambin se pueden utilizar diversas estructuras de irbol. En ocasiones se utilizan varias tablas y se mantienen cn una lista o pila.

TABLA DE LITERALES

La bsqueda y la insercin rpida son esenciales tambin para la tabla de literales, la cual almacena constantes y cadenas utilkzadas en el programa. Sin embargo, una tabla de literales necesita impedir las eliminaciones porque sus datos se aplican globalmente al programa y una constante o cadena aparecer slo una vez en esta tabla. La tabla de literales es importante en la reduccin del tamao de un programa en la memoria al permitir la reutilizacin de constantes y cadenas. Tambin es necesaria para que el generador de cdigo construya direcciones simblicas para las literales y para introducir definiciones de datos en el archivo de cdigo objetivo.

CDIGO INTERMEDIO

De acuerdo con la clase de cdigo intermedio (por ejemplo, cdigo de tres direcciones y cdigo P) y de las clases de optimizaciones realizadas, este cdigo puede conservarse como un arreglo de cadenas de texto, un archivo de texto temporal o bien una lista de estructuras ligadas. En los compiladores que realizan optimizaciones complejas debe ponerse particular atencin a la seleccin de representaciones que permitan una fcil reorganizacin.

ARCHIVOS TEMPORALES

Al principio las computadoras no posean suficiente memoria para guardar un programa completo durante la compilacin. Este problema se resolvi mediante el uso de archivos temporales para mantener los productos de los pasos intermedios durante la traduccin o bien al compilar "al vuelo", es decir, manteniendo slo la informacin suficiente de las partes anteriores del programa fuente que permita proceder a la traduccin. Las limitantes de memoria son ahora un problema mucho menor, y es posible requerir que una unidad de compilacin entera se mantenga en la memoria, en especial si se dispone de la compilacin por separado en el lenguaje. Con todo, los compiladores ocasionalmente encuentran til generar archivos intermedios durante alguna de las etapas del procesamiento. Algo tpico de stos es la necesidad de direcciones de correccin hacia atrs durante la generacin del cdigo. Por ejemplo, cuando se traduce una sentencia condicional tal como

if x = O then

. . . else . .

debe generarse un salto desde la prueba para la parte bicondicional "else" antes de conocer la ubicacin del cdigo para el "else":

CMP X,O JNE NEXT ;; ubicacin del NEXT an no conocida

<cdigo para la parte "thenM>


NEXT :

<cdigo para la parte "else">

Por Lo regular debe dejarse un espacio en blanco para el valor de NEXT, el cual ae llena una vez que 5e logra conocer el valor. Esto se consigue fcilmente con el u\o de un archivo temporal.