Subido por Armando Abello Mercado

01 Introduccion

Anuncio
Introducción
Msc. Ing. Esmeide Leal
Universidad Autónoma del Caribe
◦
◦
◦
◦
◦
◦
◦
Definición de compilador
Historia de los compiladores
Tipos de traductores
Fases de un compilador
Agrupamiento de fases
Compiladores cruzados
Herramientas automáticas
◦ Los compiladores son programas de computadora
que traducen de un lenguaje a otro. Un compilador
toma como su entrada un programa escrito en
lenguaje fuente y produce un programa equivalente
escrito en lenguaje objeto.
Código Fuente
(Lenguaje)
Compilador
Mensajes de Error
Código Objeto/Destino
(Lenguaje)
◦ Generalmente al lenguaje fuente se le asocia como
lenguaje de alto nivel, mientras al lenguaje objeto se el
conoce como código objeto (código de maquina) escrito
específicamente para una maquina objeto. A lo largo del
proceso de traducción el compilador debe informar la
presencia de errores en el lenguaje fuente.
◦ Diseñar y desarrollar un compilador, no es tarea fácil, y
quizás pocos profesionales de la computación se vean
involucrados en esta tarea.
◦ No obstante, los compiladores se utilizan en casi todas
las formas de la computación y cualquiera involucrado
en esta área debería conocer la organización y el
funcionamiento básico de un compilador.
Años
40 - 50
Años
50 - 60
Años
60 -


A finales de la década de 1940, comenzaron
a construirse las primeras computadoras
digitales y fue necesario implementar un
lenguaje capas de realizar los cálculos, es
aquí donde aparece el lenguaje de maquina
que representaba secuencias de códigos
numéricos:
C7 06 0000 0002 (instrucción que mueve el número dos a la ubicación 0000)




Desafortunadamente este lenguaje era tedioso de seguir y
complicado de mantener, por lo que esta forma de
codificación fue reemplazada por el lenguaje ensamblador,
en el cual las instrucciones y las localidades de memoria
son formas simbólicas. Un ensamblador traduce de los
códigos simbólicos a lenguaje de maquina. Aún con esta
mejora, el lenguaje ensamblador sigue siendo demasiado
difícil de mantener: |
MOV X, 2 (instrucción en ensamblador equivalente a la anterior)
En este punto se presenta la necesidad de lenguajes que
permitan escribir los programas de forma concisa, similar
a una notación matemática, y que se pudieran traducir a
código ejecutable para una máquina dada:
X=2



En los 50los compiladores eran considerados
programas muy difíciles.
FORTRAN se desarrolló en grupo durante 18
años.
Hoy en día se han desarrollado técnicas
sistemáticas, entornos de programación y
herramientas de software que facilitan la
tarea de desarrollo de compiladores,
intérpretes y traductores




En 1950, G. M. Hooper acuña el termino compilador y aparecen
los primeros trabajos sobre compiladores relacionados con la
traducción de formulas aritméticas a código de máquina.
John Backus lideró un grupo de trabajo en IBM para realizar de
un traductor de código máquina a fórmulas matemáticas.
Resultando con gran éxito: la especificación de un lenguaje de
alto nivel (FORTRAN, FORmule TRANslation) Trabajaron 18
personas durante mas de un año en el proyecto.
Fúe un compilador hecho ad-hoc (a puro corazón), pues no
existía una teoría formal, sino que se iban resolviendo las
construcciones una a una, para cada situación particular.


Noam Chomsky comienza sus estudios sobre la estructura del lenguaje natural.
Sus estudios lo condujeron a la clasificación de los lenguajes de acuerdo a una
jerarquía de sus gramáticas, además sus estudios sobre los algoritmos de
reconocimiento derivaron en una automatización del proceso de traducción mas
eficiente.
1960, se diseña el lenguaje LISP. En un principio, el código LISP se traducía
manualmente a código máquina. Se escribió en LISP un programa capaz de
interpretar programas LISP, que se tradujo manualmente a código de máquina,
construyendo de este modo un intérprete ejecutable de LISP.

Knuth desarrolla la mayoría de las técnicas de análisis sintáctico.

1970, se presentan los mayores avances en el área de lenguajes de programación.

Aparecen los primeros programas que automatizan los procesos de análisis léxico
y sintáctico. Surgiendo la llamada Torre de Babel debido a la proliferación de la
teoría para la construcción de compiladores.




Niklaus Wirth, diseña Pascal, pensado para la enseñanza.
Wirth propone el concepto de representación intermedia de código,
separando el proceso de traducción en dos fases: el front-end
encargada de analizar el programa fuente (operaciones dependientes
sólo del lenguaje fuente) y el back-end encargada de generar el código
para la máquina objeto.
1980, comienzan a proliferar las técnicas de mejoramiento de código
(optimización), se consolida y prolifera el concepto de asignación y
liberación de memoria dinámica. La programación orientada a objetos es
extensamente utilizada y madura.
1990, los lenguajes de programación y compiladores son muy similares
a lo que tenemos actualmente, surgen los ambientes de desarrollo, los
lenguajes interpretados comienza a ganar terreno en aplicaciones de
Internet y el código intermedio se vuelve a poner de moda.



Compilador: Programa que convierte un archivo de
lenguaje de programación a su correspondiente en
lenguaje objeto. La traducción se hace del texto completo.
Interpretes: Ejecutan las instrucciones del programa según
se vallan presentando. Necesitan menos memoria, pero
son más lentos que los compiladores (LISP, Prolog).
Históricamente, se pusieron de moda en los primeros años
porque los recursos de memoria eran escasos. Permiten
añadir código dinámicamente durante la ejecución. La
traducción se hace “frase a frase”
Traductores: Programa que convierte o traduce un de
lenguaje de alto nivel a otro, ejemplo: pasar del lenguaje
C/C++ a el lenguaje Pascal.


Ensamblador: Programa que convierte de
lenguaje mnemonico a lenguaje máquina,
generando un archivo con el código objeto
equivalente al código fuente completo, junto con
información necesaria para el montaje.
Formadores de Texto toman como entrada una
cadena de caracteres que incluye el texto a
componer y órdenes (TAG´s) para indicar
capítulos, secciones, párrafos, enumeraciones,
figuras, formulas, tablas, etc. (Latex, Html). •

Ventajas del compilador
◦ Se compila una vez, se ejecuta n-veces
◦ En bucles, la compilación genera código equivalente al
bucle pero un interprete 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 errores es más
detallada.

Ventajas del intérprete
◦ Un interprete necesita menos memoria que un
compilador
◦ Permite una mayor interactividad con el código en
tiempo de desarrollo

Compilación + Interpretación
◦ Los procesadores del lenguaje Java, combinan
compilación e interpretación
◦ Un código fuente java se compila primero en una
forma intermedia, llamada bytecode “.class”
◦ Luego, los bytecodes son interpretados por una
maquina virtual
◦ Entre otras ventajas, tenemos la portabilidad entre
plataformas

Para ser buen programador
◦ Saber como se obtiene un ejecutable permite saber
más sobre corrección y eficiencia

Para entender más sobre lenguajes
◦ Tipificación: estática, dinámica, fuerte,
polimorfismo, conversiones, sobrecarga de
operadores...
◦ Estructura de bloques, ámbitos
◦ Paso de parámetros
◦ Gestión de memoria, punteros

La teoría es imprescindible
◦ Antes de la aplicación de teoría de autómatas y
lenguajes formales, programación, etc. Los
compiladores eran muy malos

Aplicar la teoría y herramientas a otros
campos:
◦
◦
◦
◦
Intérpretes de comandos y consultas
Formateadores de texto (TeX, LaTeX)
Lenguajes de simulación (GPSS)
Intérpretes Gráficos (PS, GIF, JPEG, PovRAY)

Compilador cruzado:

Compilador de una o varias pasadas:

Autocompilador

Metacompilador

Decompilador
◦ Compilador que traduce un lenguaje fuente a objeto, el
objeto es para un computador distinto del que compila
◦ “Pasada”: recorrido total de todo el fuente con alguna
misión específica
◦ Compilador escrito en el propio lenguaje que compila
Facilitar la portabilidad
◦ Programa que recibe un lenguaje y genera un compilador
para ese lenguaje
◦ Programa que recibe como entrada código máquina y lo
traduce a un lenguaje de alto nivel

Cualquier compilador debe realizar dos tareas
principales:
◦ análisis del programa a compilar y síntesis de un
programa en lenguaje maquina que, cuando se ejecute,
realizara correctamente las actividades descritas en el
programa fuente.

Para el estudio de un compilador, es necesario
dividir su trabajo en fases:
◦ Cada fase representa una transformación al código
fuente para obtener el código objeto.
◦ Las tres primeras fases realizan la tarea de análisis, y las
demás la síntesis.
◦ En cada una de las fases se utiliza un administrador de
la tabla de símbolos y un manejador de errores.

Preprocesador:
◦ Es
el
encargado
de
transformar
el
código
fuente de entrada original
en el código fuente puro.
◦ Es decir en expandir las
macros, incluir las librerías,
realizar un preprocesado
racional
(capacidad
de
enriquecer a un lenguaje
antiguo con recursos más
modernos),
quitará
espacios
en
blanco,
sustitución de constantes
◦ Extender el lenguaje y todo
aquello que en el código de
entrada sea representativo
de una abreviatura para
facilitar la escritura del
mismo.
#define TASA 8
descuento = fijo + valor * TASA;
descuento = fijo + valor * 8 ;

Compilación:
◦ Recibe el código fuente puro, este es él modulo
principal de un compilador, si ocurre 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.
◦ La compilación se divide en Análisis y Síntesis

La compilación requiere dos grandes partes o tareas:
◦ análisis: en la que se analiza el programa fuente para dividirlo en
componentes y extraer de algún modo el significado crea una
representación intermedia del mismo.
 Análisis léxico: separación de cada elemento componente del programa
(“token”)
 Análisis sintáctico: separación de cada instrucción o sentencia del
lenguaje, que agrupa varios componentes léxicos o “tokens”.
 Análisis semántico: Se revisa el programa fuente para comprobar que
las reglas semánticas del lenguaje (aquellas relativas al significado de
las distintas instrucciones) se cumplen. Un ejemplo de regla semántica
es la comprobación de tipos en las expresiones.
◦ Durante el análisis, se determinan las operaciones que indica el
programa fuente obteniendo una representación del significado,
normalmente en una estructura jerárquica, de árbol, en la que cada
nodo representa una operación, y cuyos hijos son los argumentos de
dicha operación.

Análisis léxico: separación de cada elemento componente del
programa (“token”)
Analizador lexicográfico, escáner, rastreador



Elimina elementos
◦ Espacio en blanco
(´ ´)
◦ Tabulación
(´\ t´)
◦ Fin de línea
(´\ n´)
◦ Comentarios
/* */
Determina lexemas - tokens
◦ Operadores
= + - ( ) { } := < >
◦ Palabras clave
while, for, if
◦ Constantes numéricas 3, 0x34AF, -1.6e-19
◦ Cadenas caracteres posicion, velocidad
◦ Literal caracter
x, y
Existen generadores
◦ Lex, Flex

Análisis léxico: Tokenización
◦ Rastrea líneas de código en busca de lexemas y los clasifica en tokens, cada
token tiene un Tipo y un Valor
◦ Token = ( Tipo, Valor ); Token = <Tipo, Valor>

Análisis léxico: Tokenización; Ejemplo En lenguaje natural
B i e n v e n i d o s
a l
c u r s o
c omp i l a d o r e s
B i e n v e n i d o s
Adjetivo
a l
Preposición + artículo
c u r s o
Sustantivo
c omp i l a d o r e s
Sustantivo

Análisis léxico: Tokenización. Ejemplo En lenguaje de programación
t o t a l _ h
=
n _ d i a s
x
1 0 0 00 0 ;
/ / h o t e l
t o t a l _ h
< Identificador, “ total_h ” >
=
< Operador, “ = “ >
n _ d i a s
< dentificador, “ n_dias ” >
x
< Operador, “ x “ >
1 0 0 0 0 0
< Literal, “ 100000 ” >
;
< Puntuación, “ ; “>



Análisis sintáctico: separación de cada instrucción o sentencia
del lenguaje, que agrupa varios componentes léxicos o “tokens”.
Analizador gramatical o Parser
 Agrupa tokens en frases gramaticales
 Genera árbol sintáctico o de parser
 Existen generadores: yacc
total_h = n_dias x 1000; // Hotel
t o t a l _ h
=
n _ d i a s
x
1 0 0 0 0 0 ;
Expresión
Sentencia de asignación
/ / h o t e l

Análisis sintáctico: total_h = n_dias x 1000; // Hotel
Sentencia
Sentencia asignación
total_h
=
Expresión
;
Término
Término
Recursividad
x
Factor
Factor
n_dias
100000




Análisis semántico: Se revisa el programa fuente para comprobar
que las reglas semánticas del lenguaje (aquellas relativas al
significado de las distintas instrucciones) se cumplen. Un ejemplo de
regla semántica es la comprobación de tipos en las expresiones.
Chequeador de tipos
Determina significado (semántica) del programa
Funciones
◦ Verificación estática del programa
 Declaración y uso de variables





Compatibilidad de tipos
Cantidad y tipo de parámetros en invocaciones
Reales no son subíndices
Dimensión de arreglos
Conversión de tipos cuando sea posible
◦ Generación representación intermedia
 Arbol abstracto de sintaxis o árbol abstracto de estructura

Análisis semántico: total_h = n_dias x 1000; // Hotel
=
total_h
x
n_dias
1000


síntesis: en la que el significado obtenido se escribe
en el lenguaje objeto, se construye el programa
destino deseado a partir de una descripción en un
lenguaje de representación intermedia.
De las dos, la síntesis es la que requiere técnicas más
especializadas.




síntesis:
Representación intermedia del código
Código intermedio generado debe ser fácil de generar y de traducir a
instrucciones de procesador
Tipos de código intermedio

Arboles de sintaxis

Grafos
Notación posfija


Expresiones infija y posfija
INFIJA
POSFIJA
5+8-3
58+3-

Ensamblado:
◦ 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.
◦ 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.
◦ 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.

Enlazador/Linker/Cargador
◦ Es el encargado de realizar el enlazado 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.

Enlazador/Linker/Cargador

Enlazador/Linker/Cargador



Tabla de símbolos: La tabla de símbolos es una estructura de
datos que almacena los identificadores utilizados en el
programa fuente así como los atributos de cada identificador.
Estos atributos pueden proporcionar información sobre el
tipo del identificador, su tamaño, su rango de visibilidad, sus
argumentos (en caso de procedimientos), etc.
La tabla de símbolos tiene operaciones para encontrar un
identificador rápidamente, y leer sus atributos o modificarlos.
Asimismo, permite introducir nuevos identificadores. Cada
una de las fases de compilación puede realizar
modificaciones de los registros de una tabla de símbolos,
generalmente añadiendo más atributos a medida que se van
conociendo

El manejador de errores es un módulo que gestiona las
acciones a realizar por cada uno de los errores encontrados
en las diferentes fases de la compilación. En general, es
deseable que el manejador de errores permita la continuación
del proceso de compilación, con objeto de permitir encontrar
más errores en el programa. Las fases de análisis sintáctico y
semántico son habitualmente las que más errores
encuentran.
Tokens
x:=a+b*c;
y:=3+b*c;
Analizador
Léxico
<id, “x”> <op, “:=”><id, “a”>
<op, “+”> <id, “b”><op, “*”>
<id, “c”> <punt “;”>
<id, “y”> <op, “:=”><num “3”>
<op, “+”> <id, “b”><op, “*”>
<id, “c”> <punt “;”>
Tokens
<id, “x”> <op, “:=”><id, “a”>
<op, “+”> <id, “b”><op, “*”>
<id, “c”> <punt “;”>
<id, “y”> <op, “:=”><num “3”>
<op, “+”> <id, “b”><op, “*”>
<id, “c”> <punt “;”>
Analizador
Sintáctico
Analizador Sintáctico
Analizador Semántico
Optimizador de código
Generador de código

Son programas de ayuda en el proceso de escritura de
compiladores:
◦ sistemas generadores de traductores. También se les conoce como
compiler writing tools, compiler generators, compiler-compilers. A
continuación mencionaremos los mas conocidos.
◦ Generadores de analizadores léxicos: a partir de una especificación basada
en expresiones regulares. Lex / Flex/JFlex.
◦ Generadores de analizadores sintácticos: a partir de una entrada que es la
gramática independiente del contexto que representa la estructura
sintáctica del lenguaje. Yacc / Bison/CUP.
◦ Generadores de código: con rutinas para la generación del árbol de
análisis sintáctico y para su recorrido. En cada nodo se especifican las
acciones para su traducción a código objeto correspondiente.
Descargar