Você está na página 1de 9

1.

Introducci�n

En 1946 se desarroll� el primer ordenador digital. En un principio, estas m�quinas


ejecutaban instrucciones consistentes en c�digos num�ricos que se�alan a los
circuitos de la m�quina los estados correspondientes a cada operaci�n. Esta
expresi�n mediante c�digos num�ricos se llam� Lenguaje M�quina, interpretado por un
secuenciador cableado o por un microprograma. Pero los c�digos num�ricos de las
m�quinas son engorrosos. Pronto los primeros usuarios de estos ordenadores
descubrieron la ventaja de escribir sus programas mediante claves m�s f�ciles de
recordar que esos c�digos num�ricos; al final, todas esas claves juntas se
traduc�an manualmente a Lenguaje M�quina. Estas claves constituyen los llamados
lenguajes ensambladores, que se generalizaron en cuanto se dio el paso decisivo de
hacer que las propias m�quinas realizaran el proceso mec�nico de la traducci�n. A
este trabajo se le llama ensamblar el programa.
Dada su correspondencia estrecha con las operaciones elementales de las m�quinas,
las instrucciones de los lenguajes ensambladores obligan a programar cualquier
funci�n de una manera minuciosa e iterativa. De hecho, normalmente, cuanto menor es
el nivel de expresi�n de un lenguaje de programaci�n, mayor rendimiento se obtiene
en el uso de los recursos f�sicos (hardware). A pesar de todo, el lenguaje
ensamblador segu�a siendo el de una m�quina, pero m�s f�cil de manejar. Los
trabajos de investigaci�n se orientaron entonces hacia la creaci�n de un lenguaje
que expresara las distintas acciones a realizar de una manera lo m�s sencilla
posible para el hombre. As�, en 1950, John Backus dirigi� una investigaci�n en
I.B.M. en un lenguaje algebraico. En 1954 se empez� a desarrollar un lenguaje que
permit�a escribir f�rmulas matem�ticas de manera traducible por un ordenador. Le
llamaron FORTRAN (FORmulae TRANslator). Fue el primer lenguaje considerado de alto
nivel. Se introdujo en 1957 para el uso de la computadora IBM modelo 704. Permit�a
una programaci�n m�s c�moda y breve que lo existente hasta ese momento, lo que
supon�a un considerable ahorro de trabajo. Surgi� as� por primera vez el concepto
de un traductor, como un programa que traduc�a un lenguaje a otro lenguaje. En el
caso particular de que el lenguaje a traducir es un lenguaje de alto nivel y el
lenguaje traducido de bajo nivel, se emplea el t�rmino compilador.
La tarea de realizar un compilador no fue f�cil. El primer compilador de FORTRAN
tard� 18 a�os-persona en realizarse y era muy sencillo. Este desarrollo del FORTRAN
estaba muy influenciado por la m�quina objeto en la que iba a ser implementado.
Como un ejemplo de ello tenemos el hecho de que los espacios en blanco fuesen
ignorados, debido a que el perif�rico que se utilizaba como entrada de programas
(una lectora de tarjetas perforadas) no contaba correctamente los espacios en
blanco. Paralelamente al desarrollo de FORTRAN en Am�rica, en Europa surgi� una
corriente m�s universitaria, que pretend�a que la definici�n de un lenguaje fuese
independiente de la m�quina y en donde los algoritmos se pudieran expresar de forma
m�s simple.
Esta corriente estuvo muy influida por los trabajos sobre gram�ticas de contexto
libre publicados por Chomsky dentro de su estudio de lenguajes naturales.Con estas
ideas surgi� un grupo europeo encabezado por el profesor F.L.Bauer (de la
Universidad de Munich). Este grupo defini� un lenguaje de usos m�ltiples
independiente de una realizaci�n concreta sobre una m�quina. Pidieron colaboraci�n
a la asociaci�n americana A.C.M. (Association for Computing Machinery) y se form�
un comit� en el que particip� J. Backus que colaboraba en esta investigaci�n. De
esa uni�n surgi� un informe definiendo un International Algebraic Language
(I.A.L.), publicado en Zurich en 1958. Posteriormente este lenguaje se llam� ALGOL
58 (ALGOritmic Language). En 1969, el lenguaje fue revisado y llev� a una nueva
versi�n que se llam� ALGOL 60. La versi�n actual es ALGOL 68, un lenguaje modular
estructurado en bloques.
En el ALGOL aparecen por primera vez muchos de los conceptos de los nuevos
lenguajes
algor�tmicos:

Definici�n de la sintaxis en notaci�n BNF (Backus-Naur Form).


Formato libre.
Declaraci�n expl�cita de tipo para todos los identificadores.
Estructuras iterativas m�s generales.
Recursividad.
Paso de par�metros por valor y por nombre.
Estructura de bloques, lo que determina la visibilidad de los identificadores.
Junto a este desarrollo en los lenguajes, tambi�n se iba avanzando en la t�cnica de
compilaci�n. En 1958, Strong y otros propon�an una soluci�n al problema de que un
compilador fuera utilizable por varias m�quinas objeto. Para ello, se divid�a por
primera vez el compilador en dos fases, designadas como el "front end" y el "back
end". La primera fase (front end) es la encargada de analizar el programa fuente y
la segunda fase (back end) es la encargada de generar c�digo para la m�quina
objeto. El puente de uni�n entre las dos fases era un lenguaje intermedio que se
design� con el nombre de UNCOL (UNiversal Computer Oriented Language). Para que un
compilador fuera utilizable por varias m�quinas bastaba �nicamente modificar su
back end. Aunque se hicieron varios intentos para definir el UNCOL, el proyecto se
ha quedado simplemente en un ejercicio te�rico. De todas formas, la divisi�n de un
compilador en front end y back end fue un adelanto importante.
Ya en estos a�os se van poniendo las bases para la divisi�n de tareas en un
compilador. As�, en 1959 Rabin y Scott proponen el empleo de aut�matas
deterministas y no deterministas para el reconocimiento lexicogr�fico de los
lenguajes. R�pidamente se aprecia que la construcci�n de analizadores l�xicos a
partir de expresiones regulares es muy �til en la implementaci�n de los
compiladores. En 1968, Johnson apunta diversas soluciones. En 1975, con la
aparici�n de LEX surge el concepto de un generador autom�tico de analizadores
l�xicos a partir de expresiones regulares, basado en el sistema operativo UNIX.
A partir de los trabajos de Chomsky ya citados, se produce una sistematizaci�n de
la sintaxis de los lenguajes de programaci�n, y con ello un desarrollo de diversos
m�todos de an�lisis sint�ctico.

Con la aparici�n de la notaci�n BNF - desarrollada en primer lugar por Backus en


1960 cuando trabajaba en un borrador del ALGOL 60, modificada en 1963 por Naur y
formalizada por Knuth en 1964 - se tiene una gu�a para el desarrollo del an�lisis
sint�ctico. Los diversos m�todos de parsers ascendentes y descendentes se
desarrollan durante la d�cada de los 60. En 1959, Sheridan describe un m�todo de
parsing de FORTRAN que introduc�a par�ntesis adicionales alrededor de los operandos
para ser capaz de analizar las expresiones. M�s adelante, Floyd introduce la
t�cnica de la precedencia de operador y el uso de las funciones de precedencia. A
mitad de la d�cada de los 60, Knuth define las gram�ticas LR y describe la
construcci�n de una tabla can�nica de parser LR. Por otra parte, el uso por primera
vez de un parsing descendente recursivo tuvo lugar en el a�o 1961. En el a�o 1968
se estudian y definen las gram�ticas LL as� como los parsers predictivos. Tambi�n
se estudia la eliminaci�n de la recursi�n a la izquierda de producciones que
contienen acciones sem�nticas sin afectar a los valores de los atributos.
En los primeros a�os de la d�cada de los 70, se describen los m�todos SLR y LALR de
parser LR. Debido a su sencillez y a su capacidad de an�lisis para una gran
variedad de lenguajes, la t�cnica de parsing LR va a ser la elegida para los
generadores autom�ticos de parsers. A mediados de los 70, Johnson crea el generador
de analizadores sint�cticos YACC para funcionar bajo un entorno UNIX . Junto al
an�lisis sint�ctico, tambi�n se fue desarrollando el an�lisis sem�ntico.
En los primeros lenguajes (FORTRAN y ALGOL 60) los tipos posibles de los datos eran
muy simples, y la comprobaci�n de tipos era muy sencilla. No se permit�a la
coerci�n de tipos, pues �sta era una cuesti�n dif�cil y era m�s f�cil no
permitirlo. Con la aparici�n del ALGOL 68 se permit�a que las expresiones de tipo
fueran construidas sistem�ticamente. M�s tarde, de ah� surgi� la equivalencia de
tipos por nombre y estructural. El manejo de la memoria como una implementaci�n
tipo pila se us� por primera vez en 1958 en el primer proyecto de LISP. La
inclusi�n en el ALGOL 60 de procedimientos recursivos potenci� el uso de la pila
como una forma c�moda de manejo de la memoria . Dijkstra introdujo posteriormente
el uso del display para acceso a variables no locales en un lenguaje de bloques.
Tambi�n se desarrollaron estrategias para mejorar las rutinas de entrada y de
salida de un procedimiento . As� mismo, y ya desde los a�os 60, se estudi� el paso
de par�metros a un procedimiento por nombre, valor y variable. Con la aparici�n de
lenguajes que permiten la localizaci�n din�mica de datos , se desarrolla otra forma
de manejo de la memoria , conocida por el nombre de heap (mont�culo). Se han
desarrollado varias t�cnicas para el manejo del heap y los problemas que con �l se
presentan, como son las referencias perdidas y la recogida de basura .
La t�cnica de la optimizaci�n apareci� desde el desarrollo del primer compilador de
FORTRAN. Backus comenta c�mo durante el desarrollo del FORTRAN se ten�a el miedo de
que el programa resultante de la compilaci�n fuera m�s lento que si se hubiera
escrito a mano. Para evitar esto, se introdujeron algunas optimizaciones en el
c�lculo de los �ndices dentro de un bucle. Pronto se sistematizan y se recoge la
divisi�n de optimizaciones independientes de la m�quina y dependientes de la
m�quina. Entre las primeras est�n la propagaci�n de valores , el arreglo de
expresiones, la eliminaci�n de redundancias, etc.

Entre las segundas se podr�a encontrar lalocalizaci�n de registros, el uso de


instrucciones propias de la m�quina y el reordenamiento de c�digo. A partir de 1970
comienza el estudio sistem�tico de las t�cnicas del an�lisis de flujo de datos. Su
repercusi�n ha sido enorme en las t�cnicas de optimizaci�n global de un programa.
En la actualidad, el proceso de la compilaci�n ya est� muy asentado. Un compilador
es una herramienta bien conocida, dividida en diversas fases. Algunas de estas
fases se pueden generar autom�ticamente (analizador l�xico y sint�ctico) y otras
requieren una mayor atenci�n por parte del escritor de compiladores (las partes de
traducci�n y generaci�n de c�digo).
De todas formas, y en contra de lo que quiz� pueda pensarse, todav�a se est�n
llevando a cabo varias v�as de investigaci�n en este fascinante campo de la
compilaci�n. Por una parte, se est�n mejorando las diversas herramientas
disponibles (por ejemplo, el generador de analizadores l�xicos Aardvark para el
lenguaje PASCAL). Tambi�n la aparici�n de nuevas generaciones de lenguajes -ya se
habla de la quinta generaci�n, como de un lenguaje cercano al de los humanos-ha
provocado la revisi�n y optimizaci�n de cada una de las fases del compilador. El
�ltimo lenguaje de programaci�n de amplia aceptaci�n que se ha dise�ado, el
lenguaje Java, establece que el compilador no genera c�digo para una m�quina
determinada sino para una virtual, la Java Virtual Machine (JVM), que
posteriormente ser� ejecutado por un int�rprete, normalmente incluido en un
navegador de Internet. El gran objetivo de esta exigencia es conseguir la m�xima
portabilidad de los programas escritos y compilados en Java, pues es �nicamente la
segunda fase del proceso la que depende de la m�quina concreta en la que se ejecuta
el int�rprete.

�Qu� es un compilador?
Un traductor es cualquier programa que toma como entrada un texto escrito en un
lenguaje, llamado fuente y da como salida otro texto en un lenguaje, denominado
objeto.

Compilador
En el caso de que el lenguaje fuente sea un lenguaje de programaci�n de alto nivel
y el objeto sea un lenguaje de bajo nivel (ensamblador o c�digo de m�quina), a
dicho traductor se le denomina compilador. Un ensamblador es un compilador cuyo
lenguaje fuente es el lenguaje ensamblador. Un int�rprete no genera un programa
equivalente, sino que toma una sentencia del programa fuente en un lenguaje de alto
nivel y la traduce al c�digo equivalente y al mismo tiempo lo ejecuta.
Hist�ricamente, con la escasez de memoria de los primeros ordenadores, se puso de
moda el uso de int�rpretes frente a los compiladores, pues el programa fuente sin
traducir y el int�rprete juntos daban una ocupaci�n de memoria menor que la
resultante de los compiladores. Por ello los primeros ordenadores personales iban
siempre acompa�ados de un int�rprete de BASIC (Spectrum, Commodore VIC-20, PC XT de
IBM, etc.). La mejor informaci�n sobre los errores por parte del compilador as�
como una mayor velocidad de ejecuci�n del c�digo resultante hizo que poco a poco se
impusieran los compiladores. Hoy en d�a, y con el problema de la memoria
pr�cticamente resuelto, se puede hablar de un gran predominio de los compiladores
frente a los int�rpretes, aunque int�rpretes como los incluidos en los navegadores
de Internet para interpretar el c�digo JVM de Java son la gran excepci�n.
Ventajas de compilar frente a interpretar:

Se compila una vez, se ejecuta n veces.


En bucles, la compilaci�n genera c�digo equivalente al bucle, pero interpret�ndolo
se traduce tantas veces una l�nea como veces se repite el bucle.
El compilador tiene una visi�n global del programa, por lo que la informaci�n de
mensajes de error es mas detallada.
Ventajas del int�rprete frente al compilador:
Un int�rprete necesita menos memoria que un compilador. En principio eran m�s
abundantes dado que los ordenadores ten�an poca memoria.
Permiten una mayor interactividad con el c�digo en tiempo de desarrollo.
Un compilador no es un programa que funciona de manera aislada, sino que necesita
de otros programas para conseguir su objetivo: obtener un programa ejecutable a
partir de un programa fuente en un lenguaje de alto nivel. Algunos de esos
programas son el preprocesador, el linker, el depurador y el ensamblador. El
preprocesador se ocupa (dependiendo del lenguaje) de incluir ficheros, expandir
macros, eliminar comentarios, y otras tareas similares. El linker se encarga de
construir el fichero ejecutable a�adiendo al fichero objeto generado por el
compilador las cabeceras necesarias y las funciones de librer�a utilizadas por el
programa fuente. El depurador permite, si el compilador ha generado adecuadamente
el programa objeto, seguir paso a paso la ejecuci�n de un programa. Finalmente,
muchos compiladores, en vez de generar c�digo objeto, generan un programa en
lenguaje ensamblador que debe despu�s convertirse en un ejecutable mediante un
programa ensamblador.

2. Clasificaci�n de Compiladores

El programa compilador traduce las instrucciones en un lenguaje de alto nivel a


instrucciones que la computadora puede interpretar y ejecutar. Para cada lenguaje
de programaci�n se requiere un compilador separado. El compilador traduce todo el
programa antes de ejecutarlo. Los compiladores son, pues, programas de traducci�n
insertados en la memoria por el sistema operativo para convertir programas de
c�mputo en pulsaciones electr�nicas ejecutables (lenguaje de m�quina). Los
compiladores pueden ser de:

una sola pasada: examina el c�digo fuente una vez, generando el c�digo o programa
objeto.
pasadas m�ltiples: requieren pasos intermedios para producir un c�digo en otro
lenguaje, y una pasada final para producir y optimizar el c�digo producido durante
los pasos anteriores.
Optimaci�n: lee un c�digo fuente, lo analiza y descubre errores potenciales sin
ejecutar el programa.
Compiladores incrementales: generan un c�digo objeto instrucci�n por instrucci�n
(en vez de hacerlo para todo el programa) cuando el usuario teclea cada orden
individual. El otro tipo de compiladores requiere que todos los enunciados o
instrucciones se compilen conjuntamente.
Ensamblador: el lenguaje fuente es lenguaje ensamblador y posee una estructura
sencilla.
Compilador cruzado: se genera c�digo en lenguaje objeto para una m�quina diferente
de la que se est� utilizando para compilar. Es perfectamente normal construir un
compilador de Pascal que genere c�digo para MS-DOS y que el compilador funcione en
Linux y se haya escrito en C++.
Compilador con montador: compilador que compila distintos m�dulos de forma
independiente y despu�s es capaz de enlazarlos.
Autocompilador: compilador que est� escrito en el mismo lenguaje que va a compilar.
Evidentemente, no se puede ejecutar la primera vez. Sirve para hacer ampliaciones
al lenguaje, mejorar el c�digo generado, etc.
Metacompilador: es sin�nimo de compilador de compiladores y se refiere a un
programa que recibe como entrada las especificaciones del lenguaje para el que se
desea obtener un compilador y genera como salida el compilador para ese lenguaje.
El desarrollo de los metacompiladores se encuentra con la dificultad de unir la
generaci�n de c�digo con la parte de an�lisis. Lo que s� se han desarrollado son
generadores de analizadores l�xicos y sint�cticos. Por ejemplo, los conocidos:
LEX: ?generador de analizadores l�xicos
YACC:? generador de analizadores sint�cticos
desarrollados para UNIX. Los inconvenientes que tienen son que los analizadores que
generan no son muy eficientes.
Descompilador: es un programa que acepta como entrada c�digo m�quina y lo traduce a
un lenguaje de alto nivel, realizando el proceso inverso a la compilaci�n.
3. Funciones de un compilador

A grandes rasgos un compilador es un programa que lee un programa escrito es un


lenguaje, el lenguaje fuente, y lo traduce a un programa equivalente en otro
lenguaje, el lenguaje objeto. Como parte importante de este proceso de traducci�n,
el compilador informa a su usuario de la presencia de errores en el programa
fuente.
A primera vista, la diversidad de compiladores puede parecer abrumadora. Hay miles
de lenguajes fuente, desde los lenguajes de programaci�n tradicionales, como
FORTRAN o Pascal, hasta los lenguajes especializados que han surgido virtualmente
en todas las �reas de aplicaci�n de la inform�tica. Los lenguajes objeto son
igualmente variados; un lenguaje objeto puede ser otro lenguaje de programaci�n o
el lenguaje de m�quina de cualquier computador entre un microprocesador y un
supercomputador. A pesar de existir una aparente complejidad por la clasificaci�n
de los compiladores, como se vio en el tema anterior, las tareas b�sicas 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 m�quinas objeto utilizando las mismas t�cnicas b�sicas.
Nuestro conocimiento sobre c�mo organizar y escribir compiladores ha aumentado
mucho desde que comenzaron a aparecer
los primeros compiladores a principios de los a�os cincuenta. Es dif�cil dar una
fecha exacta de la aparici�n del primer compilador, porque en un principio gran
parte del trabajo de experimentaci�n y aplicaci�n se realiz� de manera
independiente por varios grupos. Gran parte de los primeros trabajos de compilaci�n
estaba relacionada con la traducci�n de f�rmulas aritm�ticas a c�digo de m�quina.

En la d�cada de 1950, se consider� a los compiladores como programas notablemente


dif�ciles de escribir. EL primer compilador de FORTRAN, por ejemplo, necesit� para
su implantaci�n de 18 a�os de trabajo en grupo (Backus y otros [1975]). Desde
entonces, se han descubierto t�cnicas sistem�ticas para manejar muchas de las
importantes tareas que surgen en la compilaci�n. Tambi�n se han desarrollado buenos
lenguajes de implantaci�n, entornos de programaci�n y herramientas de software. Con
estos avances, puede hacerse un compilador real incluso como proyecto de estudio en
un curso de un semestre sobre dise�o sobre de compiladores.

4. Partes en las que trabaja un compilador

Conceptualmente un compilador opera en fases. Cada una de las cuales transforma el


programa fuente de una representaci�n en otra. En la figura 3 se muestra una
descomposici�n t�pica de un compilador. En la pr�ctica se pueden agripar fases y
las representaciones intermedias entres las fases agrupadas no necesitan ser
construidas expl�citamente.

Programa fuente

Programa objeto
Figura 3.- Fases de un compilador.
Las tres primeras fases, que forman la mayor parte de la porci�n de an�lisis de un
compilador se analizan en la secci�n IX. Otras dos actividades, la administraci�n
de la tabla se s�mbolos y el manejo de errores, se muestran en interacci�n con las
seis fases de an�lisis l�xico, an�lisis sint�ctico, an�lisis sem�ntico, generaci�n
de c�digo intermedio, optimaci�n de c�digo y generaci�n de c�digo. De modo
informal, tambi�n se llamar�n "fases" al administrador de la tabla de s�mbolos y al
manejador de errores.

Administrador de la tabla de s�mbolos


Una funci�n esencial de un compilador es registrar los identificadores utilizados
en el programa fuente y reunir informaci�n sobre los distintos atributos de cada
identificador. Estos atributos pueden proporcionar informaci�n 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 n�mero y tipos
de sus argumentos, el m�todo de pasar cada argumento (por ejemplo, por referencia)
y el tipo que devuelve, si los hay.
Una tabla de s�mbolos 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 r�pidamente el registro de cada identificador y
almacenar o consultar r�pidamente datos en un registro
Cuando el analizador l�xico detecta un indentificador en el programa fuente, el
identificador se introduce en la tabla de s�mbolos. Sin embargo, normalmente los
atributos de un identificador no se pueden determinar durante el an�lisis l�xico.
Por ejemplo, en una declaraci�n en Pascal como var posici�n, inicial, velocidad :
real;
El tipo real no se conoce cuando el analizador l�xico encuentra posici�n, inicial y
velocidad.
Las fases restantes introducen informaci�n sobre los identificadores en la tabla de
s�mbolos y despu�s la utilizan de varias formas. Por ejemplo, cuando se est�
haciendo el an�lisis sem�ntico y la generaci�n de c�digo intermedio, se necesita
saber cu�les son los tipos de los identificadores, para poder comprobar si el
programa fuente los usa de una forma v�lida y as� poder generar las operaciones
apropiadas con ellos. El generador de c�digo, por lo general, introduce y utiliza
informaci�n detallada sobre la memoria asignada a los identificadores.

Detecci�n e informaci�n de errores


Cada frase puede encontrar errores. Sin embargo, despu�s de detectar un error. Cada
fase debe tratar de alguna forma ese error, para poder continuar la compilaci�n,
permitiendo la detecci�n de m�s errores en el programa fuente. Un compilador que se
detiene cuando encuentra el primer error, no resulta tan �til como debiera.
Las fases de an�lisis sint�ctico y sem�ntico por lo general manejan una gran
proporci�n de los errores detectables por el compilador. La fase l�xica puede
detectar errores donde los caracteres restantes de la entrada no forman ning�n
componente l�xico del lenguaje. Los errores donde la cadena de componentes l�xicos
violan las reglas de estructura (sintaxis) del lenguaje son determinados por la
fase del an�lisis sint�ctico.
Durante el an�lisis sem�ntico el compilador intenta detectar construcciones que
tengan la estructura sint�ctica correcta, pero que no tengan significado para la
operaci�n 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 an�lisis
Conforme avanza la traducci�n, la representaci�n interna del programa fuente que
tiene el compilador se modifica. Para ilustrar esas representaciones, consid�rese
la traducci�n de la proposici�n
Posici�n := inicial + velocidad * 60 (1)
La figura 4 muestra la representaci�n de esa proposici�n despu�s de cada frase.
Posici�n := inicial + velocidad * 60
Id1 := id2 + id3 * 60
Tabla de s�mbolos
1
2
3
4 : =
id1 +
id2 *
id3 entareal
60
temp1 := entareal(60)
temp2 := id3 * temp1
temp3 := id2 + temp2
id1 := temp3
temp1 := id3 * 60.0
id1 := id2 + temp1
MOVF id3, R2
MULF #60.0, R2
MOVF id2, R1
ADDF R2, R1
MOVF R1, id1

Figura 4.- Representaci�n de una proposici�n.


La fase de an�lisis l�xico lee los caracteres de un programa fuente y los agrupa en
una cadena de componentes l�xicos en los que cada componente representa una
secuencia l�gicamente coherente de caracteres, como un identificador, una palabra
clave (if, while, etc), un car�cter de puntuaci�n, o un operador de varios
caracteres, como :=. La secuencia de caracteres que forman un componente l�xico se
denomina lexema del componente.
A ciertos componentes l�xicos se les agregar� un "valor l�xico". As�, cuando se
encuentra un identificador como velocidad, el analizador l�xico no s�lo genera un
componente l�xico, por ejemplo, id, sino que tambi�n introduce el lexema velocidad
en la tabla de s�mbolos, si a�n no estaba all�. El valor l�xico asociado con esta
aparici�n de id se�ala la entrada de la tabla de s�mbolos correspondiente a
velocidad.
Usaremos id1 , id2 e id3 para posici�n, inicial y velocidad, respectivamente, para
enfatizar que la representaci�n interna de un identificador es diferente de la
secuencia de caracteres que forman el identificador. Por tanto, la representaci�n
de (1) despu�s del an�lisis l�xico queda sugerida por:
id1 := id2 + id3 * 60 (2)

Se deber�an construir componentes para el operador de varios caracteres := y el


n�mero 60, para reflejar su representaci�n interna. En la secci�n IX ya se
introdujeron las fases segunda y tercera: los an�lisis sint�ctico y sem�ntico. El
an�lisis sint�ctico impone una estructura jer�rquica a la cadena de componentes
l�xicos, que se representar� por medio de �rboles sint�cticos, como se muestra en
la figura 5A). Una estructura de datos t�pica para el �rbol se muestra en la figura
5B), en la que un nodo interior es un registro con un campo para el operador y dos
campos que contienen apuntadores a los registros de los hijos izquierdo y derecho.
Una hoja es un registro con dos o m�s campos, uno para identificar el componente
l�xico de la hoja, y los otros para registrar informaci�n sobre el componente
l�xico. Se puede tener informaci�n adicional sobre las construcciones del lenguaje
a�adiendo m�s campos a les registros de los nodos.
: =
id1 +
id2 *
id360
(a)

:= � � � �

id � 1

+ � � � �

id � 1

* � � � �

id � 3

num�60

(b)

Figura 5.- La estructura de datos en (b) corresponde al �rbol en (a).

Generaci�n de c�digo intermedio


Despu�s de los an�lisis sint�ctico y sem�ntico, algunos compiladores generan una
representaci�n intermedia expl�cita del programa fuente. Se puede considerar esta
representaci�n intermedia como un programa para una m�quina abstracta. Esta
representaci�n intermedia debe tener dos propiedades importantes; debe ser f�cil de
producir y f�cil de traducir al programa objeto.
La representaci�n intermedia puede tener diversas formas. Existe una forma
intermedia llamada "c�digo de tres direcciones", que es como el lenguaje
ensamblador para una m�quina en la que cada posici�n de memoria puede actuar como
un registro. El c�digo de tres direcciones consiste en una secuencia de
instrucciones, cada una de las cuales tiene como m�ximo tres operandos. El programa
fuente de (1) puede aparecer en c�digo de tres direcciones como
temp1 := entarea1(60)
temp2 := id3 * temp1 (2)
temp3 := id2 + temp2
id1 := temp3

Esta representaci�n intermedia tiene varias propiedades. Primera, cada instrucci�n


de tres direcciones tiene a lo sumo un operador, adem�s de la asignaci�n. Por
tanto, cuando se generan esas instrucciones el compilador tiene que decidir el
orden en que deben efectuarse, las operaciones; la multiplicaci�n precede a la
adici�n al programa fuente de (1).
Segunda, el compilador debe generar un nombre temporal para guardar los valores
calculados por cada instrucci�n. Tercera, algunas instrucciones de "tres
direcciones" tienen menos de tres operadores, por ejemplo la primera y la �ltima
instrucciones de (2).
Optimaci�n de C�digo
La fase de optimaci�n de c�digo trata de mejorar el c�digo intermedio de modo que
resulte un c�digo de m�quina m�s r�pido de ejecutar. Algunas optimaciones son
triviales. Por ejemplo, un algoritmo natural genera el c�digo intermedio (2)
utilizando una instrucci�n para cada operador de la representaci�n del �rbol
despu�s del an�lisis sem�ntico, aunque hay una forma mejor de realizar los mismos
c�lculos usando las dos instrucciones
Temp1 := id3 * 60.0 (3)
Id1 := id2 + temp1
Este sencillo algoritmo no tiene nada de malo, puesto que el problema se puede
solucionar en la fase de optimaci�n de c�digo. Esto es, el compilador puede deducir
que la conversi�n de 60 de entero a real se puede hacer de una vez por todas en el
momento de la compilaci�n, de modo que la operaci�n entreal se puede eliminar.
Adem�s, temp3 se usa s�lo una vez, para transmitir su valor a id1. Entonces resulta
seguro sustituir a id1 por temp3, a partir de lo cual la �ltima proposici�n de (2)
no se necesita y se obtiene el c�digo de (3).
Hay muchas variaciones en la cantidad de optimaci�n de c�digo que ejecutan los
distintos compiladores. En lo que hacen mucha optimaci�n llamados "compiladores
optimadores", una parte significativa del tiempo del compilador se ocupa en esta
fase. Sin embargo hay optimaciones sencillas que mejoran significativamente del
tiempo del compilador se ocupa en esta fase. Sin embargo, hay optimaciones
sencillas que mejoran sensiblemente el tiempo de ejecuci�n del programa objeto sin
retardar demasiado la compilaci�n.

Você também pode gostar