Compiladores

Page 1

Martin Jobany Pontón Martínez Ingeniería de Sistemas Uniremington Cúcuta

COMPILADORES

MARTIN JOBANY PONTÓN MARTÍNEZ

UNIREMINGTON SEDE CÚCUTA San José de Cúcuta, Diciembre 01 de 2015


Martin Jobany Pontón Martínez Ingeniería de Sistemas Uniremington Cúcuta

COMPILADORES

MARTIN JOBANY PONTÓN MARTÍNEZ

INGENIERÍA DE SISTEMAS

ING.GUIDO RODRIGUEZ TORRENTE

UNIREMINGTON SEDE CÚCUTA San José de Cúcuta, Diciembre 01 de 2015


Martin Jobany Pontón Martínez Ingeniería de Sistemas Uniremington Cúcuta

Historia del desarrollo de los compiladores  1946, se desarrolla el primer ordenador digital Las instrucciones que se ejecutaban eran códigos numéricos, lenguaje de máquina., esto es engorroso, entonces surgen los ensamblador. Al inicio el programa se escribía mediante claves y luego se traducía manualmente al lenguaje de máquina. Cuando esto lo hizo la misma máquina, a este trabajo se le llamó ensamblar el programa.  1950, John Backus dirige una investigación en IBM en un lenguaje algebraico  1954, se comienza a desarrollar FORTRAN  1957, FORTRAN se utiliza en la IBM modelo 704  Surge el concepto traductor  El primer compilador de FORTRAN tardó 18 años-persona en realizarse  FORTRAN era dependiente de la máquina  Paralelamente al desarrollo de FORTRAN en América, en Europa surge una corriente que pretende que los lenguajes fuesen independientes de la máquina, esta corriente estaba influida por los trabajos sobre GLC de Chomsky  Surge un grupo Europeo encabezado por F.L. Bauer, en la que participó ACM y John Backus. De este grupo surge un informe que define un Lenguaje Algebraico Internacional, publicado en Zurich en 1958  1969, aparece Algol 60  Junto con los lenguajes también la técnica de los compiladores avanza  1958, Strong y otros proponen una solución al problema de que un compilador fuera portable, y esta era dividir al compilador en dos fases “front end” (analiza el programa fuente) y “back end” (genera código objeto para la máquina objeto).  El puente de unión era un lenguaje intermedio denominado UNCOL – Universal Computer Oriented Language(no funcionó)  1959, Rabin y Scott proponen el empleo de AFD y AFN para el reconocimiento lexicográfico de los lenguajes


Martin Jobany Pontón Martínez Ingeniería de Sistemas Uniremington Cúcuta

 Aparece BNF (Backus-1960, Naur-1963, Knuth-1964) como una guía para el desarrollo del análisis sintáctico  1959, Sheridan describe un método de parsing de FORTRAN para introducir paréntesis en una expresión  En los 60’s se desarrollan diversos métodos de parsers ascendentes y descendentes  Floyd más adelante introduce la técnica de precedencia de operadores y uso de funciones de precedencia  1961, se usa por primera vez un parsing descendente recursivo  En los 60’s se estudia el paso de parámetros por nombre, valor y referencia y se incluyen los procedimientos recursivos para Algol 60  Se desarrolla la localización dinámica de datos  1968, se estudia y definen las GLC, los parsers predictivos y la eliminación de recursividad izquierda  1975, aparece LEX generador automático de analizadores léxicos a partir de expresiones regulares bajo UNIX  A mitad de los 70’s Johnson crea YACC para UNIX (generador de analizadores sintácticos)  Ahora un compilador de divide en varias fases  El último lenguaje de programación de amplia aceptación es JAVA (es interpretado)


Martin Jobany Pontón Martínez Ingeniería de Sistemas Uniremington Cúcuta

COMPILADORES

Un compilador es un programa informático, que se encarga de traducir el código fuente de una aplicación que este en desarrollo, es decir convierte un programa hecho en lenguaje de programación de alto nivel a un lenguaje de máquina, el cual es conocido como de bajo nivel, de tal forma que sea más entendible y mucho más fácil de procesar en el equipo en el que se está ejecutando. De igual manera un traductor es el que toma como entrada un texto escrito y da como salida otro texto en un lenguaje llamado objeto. 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


Martin Jobany Pontón Martínez Ingeniería de Sistemas Uniremington Cúcuta

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 más 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.


Martin Jobany Pontón Martínez Ingeniería de Sistemas Uniremington Cúcuta

PARTES DE UN COMPILADOR

Un compilador se divide en dos partes principalmente: Análisis y Síntesis, el análisis desglosa el programa fuente y forma una representación intermedia, la síntesis requiere de técnicas mas especializadas de los cuales se derivan las siguientes fases.

ANALISIS LEXICO El analizador léxico opera bajo petición del analizador sintáctico con el fin de que este pueda avanzar en la gramática. Tiene como propósito agrupar las expresiones en fichas. Está compuesto por 3 elementos: Componentes Léxicos (Token), Lexemas, Patrón Distancia = velocidad + aceleración * 30

FUNCIONES ANALISIS LEXICO - Eliminación de espacios en blanco - Reconocimiento de identificadores y palabras clave - Reconocimiento de constantes - Mejorar la portabilidad del compilador

ANALISIS SINTACTICO Esta fase se puede practicar, siempre y cuando la anterior haya sido realizada correctamente, en este analizador se agrupan los componentes para construir frases, verifica que el lenguaje fuente cumpla con las especificaciones que necesita el compilador donde se va a ejecutar. Es la fase más importante en el proceso de compilación.


Martin Jobany Pontón Martínez Ingeniería de Sistemas Uniremington Cúcuta

FUNCIONES ANALISIS SINTACTICO  El lenguaje se convierte en la gramática, la cual ofrece ventajas a los programadores ya que es precisa y fácil de entender.  Comprueba si la cadena se puede generar por la gramática del código fuente.  Debe recuperarse de los errores para continuar con el resto de la entrada.

ANALISIS SEMANTICO Determina si los componentes se están usando correctamente, según el contexto en el que aparecen, esta etapa cumple una función principal que es la de verificación, para finalmente generar la salida (árbol semántico) con el fin de que se genere el código intermedio.

FUNCION ANALISIS SEMANTICO  Dentro de sus funciones está la de comprobar que el significado de lo que se va leyendo sea válido.  Debe verificar que los caracteres sean compatibles con el operador que se va a utilizar.

GENERACION CODIGO INTERMEDIO Los compiladores deben analizar y generar secuencias de instrucciones iguales a un programa fuente con el fin de aprovechar mucho más la memoria del equipo, en esta etapa se genera un código con el fin de que este sea optimizando en la siguiente etapa.

OPTIMIZACION DE CODIGO Con esta fase se mejora el código fuente con el fin de mejorar en la velocidad de ejecución y se aprovechen al máximo los recursos de la máquina, una vez se ha optimizado el código queda listo para ser entendible por la maquina


Martin Jobany Pontón Martínez Ingeniería de Sistemas Uniremington Cúcuta

COMO FUNCIONA UN COMPILADOR

Tomando una sencilla instrucción del lenguaje pascal se explicará cuál es la forma en que el compilador se comporta en cada fase y etapa respectiva. Sea la instrucción: Suma:= ultimo + penúltimo Inicialmente el analizador léxico leerá la línea carácter a carácter usando como referencia los espacios en blanco, que indican donde comienza y donde termina cada componen. El analizador léxico será invocado de manera sucesiva por el analizador sintáctico y a cada llamada, en su orden devolviendo lo siguiente: El identificador: Suma El símbolo de asignación: = El identificador: último El operador de suma: + El identificador: penúltimo Luego comienza el análisis sintáctico para verificar que la línea escrita en el lenguaje esta correcta. Para lo anterior representa en una estructura jerárquica denominada árbol gramatical así: Asignación

Identificador

Suma

:=

Operador

identificador

Último

+

identificador

penúltimo


Martin Jobany Pontón Martínez Ingeniería de Sistemas Uniremington Cúcuta

1. El código fuente se lee en la memoria de la computadora. 2. El código fuente se convierte en código objeto o módulo de objeto. Un programa puede tener muchos objetos y bibliotecas que necesitan ser unidas entre sí para crear el ejecutable. 3. Se crean los conectores, para enlazar todos los componentes del programa. 4. Se reasignan los bloques de memoria dentro del programa de modo que una pieza no sobreponga a otra parte en la memoria. 5. Los archivos compilados se graban en el disco u otro tipo de memoria permanente. 6. El resultado es un archivo o programa ejecutable.

USOS Los compiladores trabajan en fases, las cuales transforman el programa fuente de una representación en otra. Se usa con el fin de analizar las secciones, administrar la taba de símbolos y manejar los errores, para esto utiliza el 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. Dentro de la característica importante de un compilador es que traduce de un Lenguaje de alto nivel a uno de bajo nivel.

FUNCION La función principal que cumple es traducir a un lenguaje mucho más sencillo y entendible por la máquina, informa al usuario si existen errores en el código fuente con el fin de ejecutar la aplicación sin problema, si existe algún error simplemente se detiene. También registra los identificadores utilizados en el programa fuente y toma la información de los atributos de cada identificador.


Martin Jobany Pontón Martínez Ingeniería de Sistemas Uniremington Cúcuta

ANALIZADOR LEXICO Se encarga de buscar los componentes léxicos o palabras que componen el programa fuente, según unas reglas o patrones. La entrada del analizador léxico podemos definirla como una secuencia de caracteres. Usa Gramática (N T P S) Árbol Sintáctico

Secuencia de Terminales

Secuencia de Caracteres

LEXICO

SINTACTICO

Gramática (N, T, P, S) N Símbolos no terminales. T Símbolos terminales P Reglas de producción S Axioma inicial El analizador léxico tiene que dividir la secuencia de caracteres en palabras con significado propio y después convertirlo a una secuencia de terminales desde el punto de vista del analizador sintáctico, que es la entrada del analizador sintáctico. El analizador léxico reconoce las palabras en función de una gramática regular de manera que sus NO TERMINALES se convierten en los elementos de entrada de fases posteriores. En LEX, por ejemplo, esta gramática se expresa mediante expresiones regulares. FUNCIONES DE UN ANALIZADOR LEXICO El analizador léxico es la primera fase de un compilador. Su principal función consiste en leer los caracteres de entrada y elaborar como salida una secuencia de componentes léxicos que utiliza el analizador sintáctico para hacer el análisis. Esta interacción, suele aplicarse convirtiendo al analizador léxico en una subrutina o corrutina del analizador sintáctico. Recibida la orden “Dame el siguiente componente léxico” del analizador sintáctico, el analizador léxico lee los caracteres de entrada hasta que pueda identificar el siguiente componente léxico.


Martin Jobany Pontón Martínez Ingeniería de Sistemas Uniremington Cúcuta

Además cuenta con otras funciones:  Eliminar los comentarios del programa.  Eliminar espacios en blanco, tabuladores, retorno de carro, etc, y en general, todo  aquello que carezca de significado según la sintaxis del lenguaje.  Reconocer los identificadores de usuario, números, palabras reservadas del lenguaje, ...,  y tratarlos correctamente con respecto a la tabla de símbolos (solo en los casos que debe  de tratar con la tabla de símbolos).  Llevar la cuenta del número de línea por la que va leyendo, por si se produce algún error,  dar información sobre donde se ha producido.  Avisar de errores léxicos. Por ejemplo, si @ no pertenece al lenguaje, avisar de un error.  Puede hacer funciones de preprocesador.

IMPORTANCIA DE UN ANALIZADOR LEXICO Un tema importante es porqué se separan los dos análisis lexicográfico y sintáctico, en vez de realizar sólo el análisis sintáctico, del programa fuente, cosa perfectamente posible aunque no plausible. Algunas razones de esta separación son:  Un diseño sencillo es quizás la consideración más importante. Separar el análisis léxico del análisis sintáctico a menudo permite simplificar una u otra de dichas fases. El analizador léxico nos permite simplificar el analizador sintáctico.


Martin Jobany Pontón Martínez Ingeniería de Sistemas Uniremington Cúcuta

Si el sintáctico tuviera la gramática de la Opción 1, el lexicográfico sería: Opción 1:

(0 | 1 | 2 | ... | 9) + (“+” | “-” | ”*“ | ”/“)

NUM OPARIT

Si en cambio el sintáctico toma la Opción 2, el lexicográfico sería: Opción 2:

(0 | 1 | 2 | ... | 9) + “+” “-” “*” “/” Es más, si ni siquiera hubiera análisis léxico, vería incrementado su número de reglas: NUM 0 |1 |2 |3 .... | NUM NUM

NUM MAS MENOS MULT DIV el propio análisis sintáctico

A modo de conclusión, diremos que tenemos dos gramáticas, una que se encarga del análisis léxico y otra que se encarga del análisis sintáctico. ¿Que consideramos componente básico?, ¿Dónde ponemos el punto divisor de qué se encarga cada gramática?. Si las divisiones se hacen muy pequeñas estamos complicando la gramática, por ejemplo, en la opción 2, la gramática sintáctica se nos complica un poco. Seguiremos dos reglas para que no se nos complique. La primera es que tendremos que hacer divisiones de forma que no perdamos información, esto quedará más claro en capítulos posteriores, y nos veremos ayudados por el concepto de atributo. La segunda es que por regla general el analizador lexicográfico debe de encargarse de la parte que involucra una gramática regular (que nosotros expresaremos mediante expresiones regulares).  Se mejora la eficiencia del compilador. Un analizador léxico independiente permite construir un procesador especializado y potencialmente más eficiente para esa función. Gran parte del tiempo se consume en leer el programa fuente y dividirlo en componentes léxicos. Con técnicas especializadas de manejo de buffers para la lectura de caracteres de entrada y procesamiento de componentes léxicos se puede mejorar significativamente el rendimiento de un compilador.


Martin Jobany Pontón Martínez Ingeniería de Sistemas Uniremington Cúcuta

 Se mejora la portabilidad del compilador. Las peculiaridades del alfabeto de entrada y otras anomalías propias de los dispositivos pueden limitarse al analizador léxico. La representación de símbolos especiales o no estándares, como en Pascal, pueden ser aisladas en el analizador léxico.  Otra razón por la que se separan los dos análisis es para que el analizador léxico se centre en el reconocimiento de componentes básicos complejos. Por ejemplo en FORTRAN, existen el siguiente par de proposiciones: DO 5 I = 2.5 (Asignación de 2.5 a la variable DO5I) DO 5 I = 2,5 (Bucle que se repite para I = 2, 3, 4, 5) En éste lenguaje los espacios en blancos no son significativos fuera de los comentarios y de un cierto tipo de cadenas, de modo que supóngase que todos los espacios en blanco eliminables se suprimen antes de comenzar el análisis léxico. En tal caso, las proposiciones anteriores aparecerían al analizador léxico como DO5I = 2.5 DO5I = 2,5 El analizador léxico no sabe si DO es una palabra reservada o es el prefijo de una variable hasta que llegue a la coma. El analizador ha tenido que mirar más allá de la propia palabra a reconocer haciendo lo que se denomina lookahead (o pre búsqueda).

COMPONENETES

Patrón: Representa un conjunto de símbolos con sentido para el lenguaje. Es una expresión regular. Token: Es el terminal asociado a un patrón. Cada token se convierte en un número que es un código identificativo de cada patrón. En algunos casos, cada número tiene asociado un puntero a la tabla de símbolos. Utilizamos la palabra terminal desde el punto de vista de la gramática utilizada por el analizador sintáctico. Lexema: Es cada secuencia de caracteres concreta que encaja con un patrón, es decir, es como una instancia de un patrón.


Martin Jobany Pontón Martínez Ingeniería de Sistemas Uniremington Cúcuta

Ej: 8, 23, 50 (son lexemas que encajan con el patrón ( 0 | 1 | 2 | ... | 9) + ) Una vez detectado que un grupo de caracteres coincide con un patrón, se ha detectado un lexema. A continuación se le asocia un número, que se le pasará al sintáctico, y, si es necesario, información adicional, como puede ser una entrada en la tabla de símbolos. La tabla de símbolos suelen ser listas encadenadas de registros con parte variable: listas ordenadas, árboles binarios de búsqueda, tablas hash, etc. Ejemplo: Hacer un analizador léxico que nos reconozca los números enteros, los números reales y los identificadores de usuario. Vamos a hacer este ejemplo en C.

Asociado a la categoría gramatical de número entero tendremos el token NUM_ENT que puede equivaler por ejemplo al número 280; asociado a la categoría gramatical número real tendremos el token NUM_REAL que equivale al número 281; asociado a la categoría gramatical identificador de usuario tendremos el token ID que equivale al número 282.

Si tuviéramos como texto de entrada el siguiente: 95.7 99 cont El analizador léxico intenta leer el lexema más grande; el 95 encaja con el primer patrón, pero sigue, al encontrarse el punto, se da cuenta de que también encaja con el segundo patrón, entonces como este es más grande, toma la acción del segundo patrón, return NUM_REAL. El 99 coincide con el patrón NUM_ENT, y la palabra con ID. Los espacios en blanco no coinciden con ningún patrón, y veremos más adelante como tratarlos. En vez de trabajar con los números 280, 281, 282, se definen mnemotécnicos.


Martin Jobany Pontón Martínez Ingeniería de Sistemas Uniremington Cúcuta

Tabla: Conjunto de pares clave-valor, llamados elementos de la tabla. La tabla de símbolos es una componente necesaria de un compilador. Al declarar un identificador (normalmente una sola vez), éste es insertado en la tabla. Cada vez que se utilice el identificador se realizará una búsqueda en la tabla para obtener la información asociada (el valor). Búsqueda: dada la clave de un elemento, encontrar su valor. Inserción: Dado un par clave-valor, añadir un elemento nuevo a la tabla. Cambio de valor: Buscar el elemento y cambiar su valor. Borrado: Eliminar un elemento de la tabla. Longitud de búsqueda (o tiempo de acceso) De una clave: Li = número de comparaciones con elementos de la tabla para encontrar esa clave. Máxima: LM = número máximo de comparaciones para encontrar cualquier clave. Media (esperada): Lm = número medio de comparaciones para encontrar un valor. Si la frecuencia de todas las claves es la misma: Lm = (S Li)/N Si la frecuencia de todas las claves no es la misma: Lm = S pi.Li Grado de ocupación: s = n/N donde n=número de elementos en la tabla y N=capacidad máxima de la tabla. Función de búsqueda: B : K→E asocia a cada clave k un elemento B(k). Valor asociado a una clave k: v(B(k)). Puede ser múltiple, en cuyo caso normalmente se convierte en un puntero. Si está en la tabla puede


Martin Jobany Pontón Martínez Ingeniería de Sistemas Uniremington Cúcuta

almacenarse consecutivamente o en subtablas paralelas. Tablas de símbolos (identificadores) La clave es el identificador. El valor está formado por: Atributos del identificador. Puntero a la posición de memoria asignada. La clave puede sustituirse por un puntero. Los identificadores pueden estar empaquetados. La longitud del identificador puede especificarse en la tabla o delante del nombre, o ser implícita. Tablas consecutivas: Todos los elementos ocupan posiciones de memoria adyacentes. Tablas ligadas: cada elemento apunta al siguiente. Tablas doblemente ligadas: cada elemento apunta al siguiente y al anterior. Tablas no ordenadas Inserción: en el primer lugar vacío.


Issuu converts static files into: digital portfolios, online yearbooks, online catalogs, digital photo albums and more. Sign up and create your flipbook.