Documentos de Académico
Documentos de Profesional
Documentos de Cultura
JFlex Java CC
Kraven Alexis Nava
UNIVERSIDAD NACIONAL DE TRUJILLO
ESCUELA DE INFORMATICA
COMPILADORES
Estructura y Procesos de Compilación
2. ESTRUCTURA DE UN COMPILADOR
La estructura de un compilador, esta dividida en cuatro grandes módulos, cada uno
independiente del otro, se podría decir que un compilador esta formado por cuatros módulos
mas a su vez.
2.2. El Compilador
El segundo modulo es el compilador y es quien recibe el código fuente puro, este es él
modulo principal de un compilador, pues si ocurriera algún error en esta etapa el
compilador no podría avanzar. En esta etapa se somete al código fuente puro de entrada a
un análisis léxico gráfico, a un análisis sintáctico, a un análisis semántico, que construyen
la tabla de símbolos, se genera un código intermedio al cual se optimiza para así poder
producir un código de salida generalmente en algún lenguaje ensamblador.
2.3. El Ensamblador
El tercer modulo es el llamado modulo ensamblador, este modulo no es ni más mi menos
que otro compilador pues recibe un código fuente de entrada escrito en ensamblador, y
produce otro código de salida, llamado código binario no enlazado. Si por un momento
viéramos a este modulo como un programa independiente, veríamos que en este caso los
términos programa compilador y proceso de compilación son los mismos. Pues este
modulo no es mas que un compilador, que en su interior realiza como su antecesor un
análisis léxico gráfico, un análisis sintáctico, un análisis semántico, crea una tabla de
símbolos, genera un código intermedio lo optimiza y produce un código de salida llamado
código binario no enlazado, y a todo este conjunto de tares se los denomina proceso de
compilación. Como se puede ver este compilador (llamado ensamblador) a diferencia de
los demás compiladores no realiza una expansión del código fuente original (código
fuente de entrada), tiene solamente un proceso de compilación y por supuesto no enlaza el
código fuente. Es un compilador que carece de los módulos de preprocesado y enlazado,
y donde los módulos de compilación y ensamblado son los mismos.
Figura 2.3 Ensamblador
2.4. El Enlazador
El cuarto y ultimo modulo es el encargado de realizar el enlazador del código de fuente
de entrada (código maquina relocalizable) con las librerías que necesita, como así
también de proveer al código de las rutinas necesarias para poder ejecutarse y cargarse a
la hora de llamarlo para su ejecución, modifica las direcciones relocalizables y ubica los
datos en las posiciones apropiadas de la memoria. Este ultimo modulo es el que produce
como salida el código binario enlazado. Ya sea dinámico o estático, al decir dinámico se
refiere a que el código producido utiliza librerías dinámicas (librerías ya cargadas en el
sistema), esto implica que se obtendrá un código más corto y que se actualizara
automáticamente si aparece alguna nueva versión de las librerías, mientras que el estático
se refiere al echo que no se realiza enlace con ninguna librería y por lo tanto se obtendrá
un código mas largo con una copia de las rutinas de librería que necesita.
3. PROCESO DE COMPILACIÓN
especiales: +,*, &,$, #,!, ', / ).Con estos símbolos se representan las construcciones
En un programa fuente existen una serie de símbolos (letras, dígitos, símbolos
El programa fuente se trata inicialmente con el analizador léxico (en inglés scanner)
con el propósito de agrupar el texto en grupos de caracteres con entidad propia
llamados tokens, unidades sintácticas o componentes léxicos, tales como constantes,
identificadores (de variables, de funciones, de procedimientos, de tipos, de clases),
palabras reservadas, y operadores. Por razones de eficiencia, a cada token se le
asocia un atributo (o más de uno) que se representa internamente por un código
numérico o por un tipo enumerado.
Por ejemplo a un identificador se le puede dar una representación interna de un 1, a
una constante entera un 2, a un operador aritmético un 3,..., cada palabra reservada
tiene su propio código.
Figura 3.1 La fase de análisis léxico se halla bajo el control del análisis sintáctico
Función
Construir una secuencia de unidades significativas sintácticas llamadas token.
Reducir el Trabajo al Analizador Sintáctico.
Datos de Entrada
Cadena de caracteres
Procesamiento
Construcción de Tokens, reconocer tokens correctos mediante reconocedor de
patrones (autómatas)
Elimina los espacios en blanco, comentarios, etc.
Reconocimiento de un token, mediante autómata finito, usando tabla de símbolos
(un token reconocido es el lexema)
( A + B) *(C + D)
el análisis sintáctico de la siguiente expresión:
con las reglas de la gramática que se presenta a continuación dará lugar al árbol
sintáctico
Procesamiento
Arbol sintáctico, organiza los tokens usando reglas recursivas conocidas como
gramáticas de libre contexto (definidas formalmente por BNF).
Las rutinas semánticas deben realizar la evaluación de los atributos de las gramáticas
siguiendo las reglas semánticas asociadas a cada producción de la gramática
( A + B) *(C + D)
Por ejemplo para una expresión como:
el analizador semántico debe determinar que acciones pueden realizar los operadores
aritméticos (+,*) sobre las variables A, B, C y D.
Así cuando el analizador sintáctico reconoce un operador, tal como " + " o " * ",
llama a una rutina semántica que especifica la acción que puede llevar a cabo. Esta
rutina puede comprobar que los dos operandos han sido declarados, y que tienen el
mismo tipo. También puede comprobar si a los operandos se les ha asignado
previamente algún valor.
Procesamiento
Proceso de verificación, consultar la tabla de símbolos para encontrar información
de un identificador y la información ligada a este.
Los atributos pueden variar de unos lenguajes a otros, debido a las características
propias de cada lenguaje y a la metodología de desarrollo del traductor.
Sin embargo si se construye un lenguaje intermedio, tan sólo son necesarios 2*n
traductores.
Así por ejemplo un fabricante de compiladores puede construir un compilador para
diferentes máquinas objeto con tan sólo cambiar las dos últimas fases de la tarea de
síntesis.
Figura 3.4 n*(n-1) traductores entre n lenguajes
Las máquinas abstractas deben definirse completamente: por una parte se definirá su
arquitectura y por otra su repertorio de instrucciones. La arquitectura de la máquina
abstracta se elegirá de forma que contribuya a facilitar la portabilidad dentro del
grupo de arquitecturas hacia las que previsiblemente se dirigirá el código objeto.
Habitualmente las arquitecturas típicas de máquinas abstractas son: máquinas
basadas en pila, basadas en registros, combinación de pilas y registros, orientadas a
objetos. También se pueden clasificar desde el punto de vista de la cantidad y
complejidad de sus instrucciones en máquinas CISC (Complex Instruction Set
Computer)y RISC (Reduced Instruction Set Computer).
( A + B) *(C + D)
de la máquina abstracta. Por ejemplo la expresión:
(+, A, B, T 1)
puede traducirse al siguiente conjunto de cuartetos:
(+, C , D, T 2)
(*, T 1, T 2, T 3)
AB + CD + *
inversa es:
Otros traductores utilizan como código intermedio un lenguaje de medio nivel como
puede ser el lenguaje C, que se caracteriza por su transportabilidad, por ejemplo el
lenguaje Eiffel, También se está utilizando por su eficiencia el lenguaje C++ para el
desarrollo de lenguajes y herramientas orientadas a objetos
(+, A, B, T 1)
(+, C , D, T 2)
(*, T 1, T 2, T 3)
En el código generado los fabricantes también ponen sus marcas en binario por si
hubiera algún litigio sobre el uso correcto o incorrecto de sus compiladores. Sobre
aspectos legales en desarrollo de software puede consultarse la obra de Fishman.
sin embargo si se hace la operación con las constantes sólo quedaría PUSH 25.
Fusión de constantes. Suele realizarse con las constantes de cadena. Así el uso
de la misma constante dos o más veces tan sólo genera código para una
constante, el resto son la referencia a la misma constante.
Reacondicionamiento de instrucciones. Por ejemplo el uso de las propiedades
conmutativa y distributiva de los operadores aritméticos puede permitir la
reducción del código objeto. También se cambia en algunos casos el orden de
evaluación, para generar código optimizado.
Comprobación de rangos y de desbordamiento de la pila. Una vez que los
programas compilados han sido probados, pueden quitarse del compilador las
opciones de comprobación de rangos (habituales en los compiladores de Pascal)
y las opciones de comprobación de desbordamiento de la pila. Al quitar estas
opciones se deja de generar el código correspondiente para generar dichas
comprobaciones.
Uso de operadores de desplazamiento de bits en vez multiplicación y división.
Las operaciones como x * a , donde a es una constante y potencia de 2, se puede
generar código con operaciones de desplazamiento. Lo mismo ocurre para
operaciones de la forma x/a.
Eliminación de código muerto. El código que no se ejecuta no debe generar
código. Así los bucles vacíos no generan código.
4. BIBLIOGRAFÍA
[AJO] AHO, SETHI, ULLMAN: Compiladores: Principios, técnicas y herramientas: Addison-
Wesley Iberoamericana, 1990