Clase #1 - Introducción [Compiladores e Interpretes]

Clase #1 - Introducción [Compiladores e Interpretes] -...

Info iconThis preview shows page 1. Sign up to view the full content.

View Full Document Right Arrow Icon
This is the end of the preview. Sign up to access the rest of the document.

Unformatted text preview: Compiladores Compiladores e Interpretes Clase Introductoria Luis Ochoa ziul1979@gmail.com Algunas ideas previas El alumno debe estar familiarizado con matemáticas discretas, estructuras de datos básicas, arquitectura del computador, lenguaje ensamblador, lenguajes formales, sistemas de control. Aunque esto no es parte de la materia, se intentará suplir deficiencias en estas áreas siempre que sea posible y este al alcance del ritmo de las clases. Código de ética mínimo a cumplir a la hora de entregar trabajos: No tomar ideas de otros y presentarlas como propias (esto implica no copiar, no "traducir" artículos de la red y ponerlos en los trabajos como ideas originales, no copiar tareas, etc.) No inducir a sus compañeros a violar el punto 1 (no dejarse copiar, no vender tareas). En caso de violación de la ética mínima que debería tener un futuro profesional, la persona y su equipo recibirán cero como nota en dicho proyecto y además se volverán a revisar todos sus proyectos anteriores en búsqueda de indicios de otras violaciones éticas y se tomarán las medidas que sean correspondientes en caso de conseguirlas. Eslabones importantes • Gödel y Turing: Cambiaron radicalmente las ciencias matemáticas, con sus descubrimientos. Hechos importantes: ▫ Teorema de Gödel, “Toda formulación axiomática consistente de la teoría de números contiene preposiciones indecidibles”, es decir cualquier teoría matemática será siempre incompleta, porque existirán afirmaciones que no se podrán demostrar ni negar. ▫ Teorema de Turing, lo publico en su articulo sobre los números calculables en el cual demostraba que existen problemas irresolubles y además introdujo el concepto de la máquina de Turing, que es una entidad matemática abstracta que formalizo por primera vez el concepto de algoritmo. Lo cual en resumen significa que existen problemas que una computadora nunca podrá resolver. Eslabones importantes • Autómatas: Claude Elwood Shannon creo las bases para la aplicación de la lógica matemática a los circuitos combinatorios y secuenciales, lo cual a la larga se convirtió en la teoría de maquinas secuenciales secuenciales y autómatas finitos. Hechos importantes: ▫ Un autómata es un sistema capaz de transmitir información. Es decir acepta señales de su entorno, como resultado cambia de estado y transmite otras señales a su medio. ▫ Esta definición es muy amplia y abarca cualquier máquina, por lo que se hace demasiado general para su estudio teórico, por lo que era necesario introducir limitaciones en su definición. ▫ La salida de un autómata puede ser muy resumida como por ejemplo una señal binaria. Eslabones importantes • Lenguajes y Gramáticas: En el campo de la lingüística Noam Chomsky propuso la teoría de las gramáticas transformacionales. Hechos importantes: ▫ Esta teoría estableció las bases para la lingüística matemática que sirvió como herramienta para el estudio y formalización de los lenguajes de computadora. ▫ Dividió el estudio de los lenguajes en análisis de la estructura de las frases (gramática) y su significado (semántica), pudiendo a su vez la gramática analizar las formas que toman las palabras (morfología), su combinación para formar frases correctas (sintaxis). ▫ Aunque la distinción entre la sintaxis y la semántica desde el punto de vista teórico es un poco artificial, tiene enorme trascendencia práctica, especialmente en el diseño de compiladores. Eslabones importantes • Máquinas Abstractas y Lenguajes Formales: Ambas disciplinas poseen una relación muy estrecha, ya que los mismos fenómenos aparecen independientemente en ambas y es posible establecer correspondencia entre ellas (isomorfismo). Hechos importantes: ▫ Noam Chomsky, clasificó las gramáticas y lenguajes formales de acuerdo con una jerarquía de cuatro grados. ▫ Paralelo a esto existe una jerarquía de maquinas abstractas equivalentes para cada nivel de la clasificación de Chomsky. Eslabones importantes • Informática teórica: Rama de rama de las matemáticas que hace uso de lo anteriormente expuesto, pero con un objetivo muy concreto: conseguir mejores sistemas de computación y clasificar los problemas de acuerdo con su dificultad al ser computados por diferentes modelos. Hechos importantes: ▫ Nace a partir de problemas que se planteaban en la lógica matemática hacia principios de siglo. ▫ Tradicionalmente, Se distinguen dos grandes campos: la teoría de lenguajes formales y las teorías de la calculabilidad y de la complejidad. ▫ Sus intenciones iniciales eran formalizar el lenguaje natural (hablado). ▫ Son la base fundamental para la creación de compiladores e interpretes. Una breve historia (al menos se intento esto…) • John von Neumann fue un matemático húngaro-estadounidense, de ascendencia judía, que realizó contribuciones importantes en física cuántica, análisis funcional, teoría de conjuntos, informática, economía, análisis numérico, hidrodinámica (de explosiones), estadística y muchos otros campos de la matemática. Recibió su doctorado en matemáticas de la Universidad de Budapest a los 23 años. • Fue pionero de la computadora digital moderna publicando un artículo acerca del almacenamiento de programas. El concepto de programa almacenado permitió la lectura de un programa dentro de la memoria de la computadora, y después la ejecución de las instrucciones del mismo sin tener que volverlas a escribir. Fuente: wikipedia Una breve historia (continuación) • Debido a los programas almacenados se hizo necesario escribir secuencias de códigos o programas que permitirían que los computadores realizaran los cálculos deseados. • En un principio se uso el lenguaje de Maquina, que son códigos númericos que representan las operaciones reales de la maquina que iban a efectuarse, por ejemplo: C7 06 0000 0002 Representa la instrucción para mover el número 2 a la ubicación 0000 (hexadecimal) en un procesador Intel 80x86. • La escritura de estos códigos es tediosa y consume mucho tiempo, por lo que se debió buscar una solución a este problema. Una breve historia MOV X,2 (continuación) • Debido a esto surgió el lenguaje ensamblador, en el cual las instrucciones y las localidades de memoria son formas simbólicas dadas, por ejemplo la instrucción anterior seria: Suponiendo claro que la localidad de memoria 0000 es X. • El trabajo es traducir los códigos simbólicos y las localidades de memoria del lenguaje ensamblador a los códigos numéricos correspondientes del lenguaje de máquina. • Esto mejoro la velocidad con la que se podían escribir los programas, sin embargo su principal defecto es que no es fácil de escribir y es difícil de leer y comprender, además de ser dependiente de la plataforma para la cual se creo. Una breve historia (continuación) • Por lo que el siguiente paso fue escribir las operaciones de un programa de una manera concisa que se pareciera a la notación matemática o el lenguaje natural. • Esto implica que sea independiente de cualquier maquina en particular y se pudiese traducir mediante un programa en código ejecutable, por ejemplo la instrucción anterior se podría plasmar como: como: X=2 • En un principio se temía que esto no fuese posible y que si lo fuera, el código objeto sería tan poco eficiente que resultaría inútil. • El lenguaje FORTRAN, creado por John Backus, demostró que estos temores eran infundados. • No obstante el trabajo realizado hasta el momento no era bien comprendido. Una breve historia (continuación) • Noam Chomsky comenzó con su estudios del lenguaje natural por ese mismo momento lo cual junto con las maquinas abstractas permitieron sentar las bases de los compiladores modernos. • A medida que el problema del léxico, sintaxis y semántica se comprendía mejor gracias a los avances en estas áreas, se empezaron a realizar programas que automatizaran esta parte del desarrollo de un compilador, llamándose a estos programas compiladores de compilador. • Ejemplo para el análisis léxico: Lex. • Ejemplo para el análisis sintáctico: Yacc o CUP. Una breve historia RESUMIENDO: (continuación) • La arquitectura de Von Neumann marca el punto de partida de la informática moderna. • Si se escribían los programas utilizando claves mnemotécnicas (abreviaciones de los códigos de operación, más fáciles de recordar que los números), los programas eran más sencillos de escribir: Lenguaje Ensamblador. • En esta década de los cincuenta, la investigación se orientó hacia la creación de un lenguaje en el que las acciones fueran expresadas de la manera más natural posible por el programador y que fuese lo más independiente posible de la máquina, por lo tanto aparecen: Los lenguajes de alto nivel. • En caso de que el lenguaje fuente que hay que traducir sea de alto nivel y el lenguaje obtenido, de bajo nivel, se utiliza el término compilador. ACTIVIDAD EN CLASES: Hacer un resumen que muestre ordenados cronológicamente los aspectos más relevantes de la historia de los compiladores. Conceptos Importantes •Traductor: Transforma un código escrito en un lenguaje fuente a un código equivalente escrito en un lenguaje objeto. como función importante, el traductor informa de la presencia de errores en el programa fuente. Conceptos Importantes •Compilador: Traductor de un lenguaje de alto nivel a uno de bajo nivel. Esto se cumple en el caso de que el lenguaje fuente sea un lenguaje de alto nivel, como por ejemplo Cobol, Pascal o C, y el lenguaje lenguaje objeto sea un lenguaje de bajo nivel, como por ejemplo el lenguaje máquina o el de ensamblador. Conceptos Importantes •Ensamblador: Traductor de lenguaje de ensamblador a lenguaje máquina. Por ejemplo cuando el lenguaje fuente sea un lenguaje de ensamblador y el lenguaje objeto, un lenguaje máquina. máquina. Conceptos Importantes •Vida de un programa: Desde que se escribe hasta que se ejecuta en una plataforma, se pueden distinguir dos periodos de tiempo: El tiempo de compilación. Periodo en el que el programa fuente se traduce al programa objeto equivalente. El tiempo de ejecución. Cuando el programa objeto se ejecuta sobre una plataforma. Conceptos Importantes • Interprete: Compila y ejecuta cada sentencia del programa. No genera código objeto equivalente al código fuente, sino que compila paso a paso ejecutando al mismo tiempo: Toma una sentencia del programa fuente. La traduce a su equivalente en el lenguaje objeto. La ejecuta sobre la plataforma. Repite el proceso con la sentencia siguiente del código fuente. 1) 2) 3) 4) Conceptos Importantes • Intérprete precompilado o compilador interpretativo: hace una primera compilación para obtener un código intermedio libre de error que después se ejecuta por interpretación. interpretación. Conceptos Importantes •Preprocesador. Lee el programa fuente y en cierta manera lo modifica antes de la compilación, procesándolo según unas directivas de precompilación y las opciones del entorno de programación. Así pues, según el lenguaje, el preprocesador se ocupa de incluir archivos, eliminar comentarios, expandir macros, activar directivas de compilación, etc. Conceptos Importantes •Enlazador (linker). Muchas veces el programa fuente remite a bibliotecas de programas que ya existen (reaprovechamiento de código). El enlazador es el encargado de construir el archivo ejecutable añadiendo al archivo objeto generado por compilador las cabeceras necesarias y las bibliotecas utilizadas por el programa fuente. Conceptos Importantes •Enlazador (linker). Muchas veces el programa fuente remite a bibliotecas de programas que ya existen (reaprovechamiento de código). El enlazador es el encargado de construir el archivo ejecutable añadiendo al archivo objeto generado por compilador las cabeceras necesarias y las bibliotecas utilizadas por el programa fuente. Conceptos Importantes •Enlazador (linker). Muchas veces el programa fuente remite a bibliotecas de programas que ya existen (reaprovechamiento de código). El enlazador es el encargado de construir el archivo ejecutable añadiendo al archivo objeto generado por compilador las cabeceras necesarias y las bibliotecas utilizadas por el programa fuente. Conceptos Importantes •Depurador (debugger). Si el compilador ha generado correctamente el programa objeto, el depurador permite hacer un seguimiento de la ejecución del mismo paso a paso, muestra el contenido de las variables en tiempo de ejecución, permite introducir puntos de detención y ayuda a buscar errores de funcionamiento del programa. Conceptos Importantes •Ensamblador. Algunos compiladores, en lugar de generar directamente código objeto ejecutable, generan código de ensamblador que debe convertirse después en ejecutable utilizando un programa programa de ensamblador y un enlazador. Conceptos Importantes •Ensamblador. Algunos compiladores, en lugar de generar directamente código objeto ejecutable, generan código de ensamblador que debe convertirse después en ejecutable utilizando un programa programa de ensamblador y un enlazador. Conceptos Importantes •La integración de estos tres módulos puede observarse acá: Nociones básicas de un compilador •La tarea de crear un traductor de un lenguaje fuente a un lenguaje objeto es compleja, pero se puede reducir en gran medida si el proceso se divide en fases especializadas que realicen cada una tarea específica y el modelo básico de compilador que estudiaremos es: Nociones básicas de un compilador •Las fases del compilador, como se ve en la figura anterior, se agrupan en dos bloques: ▫ Fases de análisis (front-end). Analizan el programa fuente en busca de errores (léxicos, sintácticos y semánticos). Son fases que dependen del lenguaje fuente y que deberían ser independientes de la máquina (excepto la lectura y la escritura en disco, agrupada en módulos independientes), mediante las cuales se genera el código objeto. ▫ Fases de síntesis (back-end). A partir del código intermedio, salido del bloque anterior, estas fases generan y optimizan el código objeto. Son fases que dependen del lenguaje objeto y, en general, de la máquina en la que se ejecutará el código generado. Nociones básicas de un compilador •En el compilador hay unas estructuras de datos comunes a todas las fases. La más importante es la tabla de símbolos, que guarda la información de los objetos que se encuentran en el análisis del código fuente (variables, etiquetas, tipos, etc.). •Las diferentes fases acceden constantemente a esta tabla y, por lo tanto, las rutinas que la gestionan deberían ser muy eficientes. Nociones básicas de un compilador •Estrategia del código intermedio: si se genera el mismo tipo de código intermedio, sólo es necesario programar una única parte de análisis para cada lenguaje que se desarrolle y una única parte de síntesis para cada plataforma plataforma de ejecución. Principales Fases de un compilador Las principales fases de un compilador son: I. Análisis léxico. II.Análisis sintáctico. III.Análisis semántica. IV.Generación de Código. V.Optimización. Principales Fases de un compilador • Análisis léxico: El analizador léxico lee el archivo fuente carácter por carácter y forma grupos de caracteres (lexemas) con un significado léxico mínimo, denominados testigos, que son tratados como una entidad única. El analizador léxico también elimina los componentes no esenciales del programa fuente, e ignora los espacios en blanco, los tabuladores, los caracteres de final de línea, los comentarios y, en general, todo lo que no sea necesario en las fases posteriores. Principales Fases de un compilador • Análisis léxico Principales Fases de un compilador • Análisis sintáctico: El analizador sintáctico utiliza los testigos encontrados por el analizador léxico y comprueba si llegan en el orden correcto: el proporcionado por la gramática libre libre de contexto que define el lenguaje fuente. • La salida del análisis sintáctico suele ser un árbol sintáctico con la estructura sintáctica del programa fuente. Principales Fases de un compilador • Análisis sintáctico Siguiendo el ejemplo que hemos visto en el análisis léxico, si utilizamos la gramática parcial siguiente para reconocer la estructura sintáctica del programa fuente (las minúsculas representan variables sintácticas y las mayúsculas testigos terminales: exp -> asigna PUNTO_Y_COMA asigna -> IDENTIFICADOR ASIGNACIÓN operación operación -> operando operador operando operador -> SUMA operando -> IDENTIFICADOR operando -> ENTERO Principales Fases de un compilador • Análisis sintáctico el árbol sintáctico generado sería el que se muestra en la figura: Principales Fases de un compilador • Análisis semántico: El analizador semántico se ocupa de comprobar el significado de las sentencias: puede haber sentencias sintácticamente correctas, pero que no se puedan ejecutar por no tener ningún sentido. Generalmente, este análisis se hace al mismo tiempo que el sintáctico, e introduce unas rutinas semánticas que intentan encontrar errores de significado (semánticos) a partir del árbol sintáctico, y al mismo tiempo reúnen información sobre los tipos de datos de los nombres del programa fuente (variables, constantes, etc.) que será utilizada en la fase de generación de código. Principales Fases de un compilador •Análisis semántico Entre otras comprobaciones, el análisis semántico: ▫ Determina el tipo de los resultados intermedios de las operaciones. ▫ Comprueba que los operandos de un operador pertenezcan al conjunto de los tipos posibles para el operador y si son compatibles entre sí. Principales Fases de un compilador •Análisis semántico La salida del análisis semántico suele ser un árbol semántico, que no es más que un árbol sintáctico en el que cada nodo ha adquirido su significado. Principales Fases de un compilador •Generación de código intermedio: Una técnica para facilitar la tarea de creación de nuevos compiladores consiste en dividir el compilador en dos partes: una fase de análisis y una de síntesis, comunicadas con un lenguaje intermedio. De esta manera, sólo se deben construir m fases de análisis que traducen el programa fuente a su representación intermedia y n fases de síntesis que traducen del lenguaje intermedio al lenguaje objeto. Es decir, hemos pasado de crear m x n bloques a crear sólo m + n. Es una reducción importante que permite construir nuevos compiladores en mucho menos tiempo. Desgraciadamente, no hay un lenguaje intermedio de uso global y cada empresa suele utilizar su propio lenguaje intermedio. Principales Fases de un compilador •Generación de código intermedio Principales Fases de un compilador • Optimización del Código: Esta fase de síntesis está presente en los compiladores más sofisticados, y su propósito es producir un código objeto más eficiente: 1. 1. Reduciendo el espacio ocupado por el código generado. 2. Aumentando la rapidez de ejecución. 3. Haciendo que se necesite menos memoria cuando se ejecute. Principales Fases de un compilador •Optimización del Código Principales Fases de un compilador •Generación del Código En esta última fase de la compilación se genera el código objeto (generalmente código de ensamblador o código máquina reubicable) a partir del código intermedio (optimizado o no): Se asigna espacio de memoria para cada nombre del programa fuente (variables, tipos, constantes, etc.). Se traduce cada una de las instrucciones en código intermedio a una secuencia de instrucciones en código objeto que ejecuten la misma tarea. Principales Fases de un compilador •Generación del Código En esta última fase de la compilación se genera el código objeto (generalmente código de ensamblador o código máquina reubicable) a partir del código intermedio (optimizado o no): Se asigna espacio de memoria para cada nombre del programa fuente (variables, tipos, constantes, etc.). Se traduce cada una de las instrucciones en código intermedio a una secuencia de instrucciones en código objeto que ejecuten la misma tarea. Principales Fases de un compilador •Generación del Código Una conversión directa de código intermedio a código objeto suele comportar muchas cargas y descargas innecesarias de memoria y que los recursos de la máquina se utilicen de manera poco eficiente. Por este motivo, muchas veces, después de la generación del código objeto, también suele haber una nueva fase de optimización de código. Principales Fases de un compilador •Generación del Código Ejemplo: ’C := A + B’ podría tener como objeto equivalente: LOAD A SUM B STO C Principales Fases de un compilador • Gestión y recuperación frente a errores: Se debe prever cómo responderá frente a los errores que encuentre mientras analiza el código fuente y genera el código objeto. Cada una de las fases del compilador detecta unos tipos de errores determinados. Por lo tanto, la gestión de errores tiene que preverse individualmente en cada fase. Principales Fases de un compilador • Gestión y recuperación frente a errores ▫ En el análisis léxico: símbolos ajenos ajenos al lenguaje. ▫ En el análisis sintáctico: expresiones mal construidas. ▫ En el análisis semántico: variables sin declarar. Principales Fases de un compilador • Gestión y recuperación frente a errores Algo importante a tener en cuenta es que aunque es una de las tareas más importantes del compilador, la gestión de los errores es lo que más dificulta su programación: ▫ ▫ Unos errores pueden ocultar otros. Al detectar un error y continuar el análisis, se puede provocar un alud de errores que se solucionen automáticamente resolviendo el primero. Construcción de Compiladores Al hablar de un compilador, hay que especificar como mínimo los tipos de lenguajes siguientes: ▫ El lenguaje fuente. ▫ El lenguaje objeto y la plataforma de ejecución. ▫ El lenguaje en el que está escrito el propio compilador, denominado lenguaje de implementación. ▫ Ejemplo En un compilador de Fortran a código máquina del PC que se ejecuta sobre un PC: – El lenguaje fuente sería el Fortran. – El lenguaje objeto sería el código máquina del PC. – El lenguaje en el que está escrito también sería código máquina del PC. Construcción de Compiladores Normalmente el lenguaje fuente se especifica generalmente en tres partes: •Especificación léxica. •Especificación sintáctica . •Especificación semántica . Construcción de Compiladores •Especificación léxica. Se hace con expresiones regulares que definen los componentes léxicos del lenguaje (testigos). Utilizando estas definiciones se puede crear automáticamente un analizador léxico del del lenguaje. Construcción de Compiladores •Especificación sintáctica. Se utiliza una gramática libre de contexto, escrita generalmente en notación BNF, para detallar la estructura sintáctica del lenguaje. A partir de esta gramática, y junto con el analizador léxico obtenido en la parte anterior, se puede generar, también automáticamente, un analizador sintáctico del lenguaje. Construcción de Compiladores •Especificación semántica. Se describe el significado de cada instrucción sintáctica y las reglas semánticas que se deben cumplir. Hay notaciones formales para especificar la semántica de un lenguaje, pero no se utilizan demasiado. La semántica suele ser especificada con palabras (lenguaje natural) y se programa manualmente. A veces, algunas reglas semánticas se pueden incluir dentro de la especificación sintáctica, por ejemplo, la procedencia y la asociatividad de los operadores. A partir de esta especificación semántica se construye el analizador semántico y se genera el código. Asignación Leer en el libro: http://homepages.mty.itesm.mx/rbrena/AyL.html http://homepages.mty.itesm.mx/rbrena/AyL.pdf Desde el inicio hasta la página 29 (omita la parte de pruebas por inducción), es decir los preliminares realizando al final los ejercicios que considere acordes con lo leído. Importante!!!!: aunque esto no es obligatorio, es deseable que ud. lo haga para poder comprender posteriormente de manera más intuitiva (sin traumas) algunos conceptos más avanzados de la teoría de lenguajes formales que aplicaremos durante el transcurso de esta materia. ...
View Full Document

Ask a homework question - tutors are online