Вы находитесь на странице: 1из 50

CONCEPTOS GENERALES

Rev. 1.0 Abril 2008 Ing. Yuri Giovani Romn Lazarinos

VISION GENERAL

Uno de los principales mecanismos de comunicacin entre un ordenador y una persona viene dado por el envo y recepcin de mensajes de tipo textual: el usuario escribe una orden mediante el teclado, y el ordenador la ejecuta devolviendo como resultado un mensaje informativo sobre las acciones llevadas a cabo.

VISION GENERAL

Aunque la evolucin de los ordenadores se encuentra dirigida actualmente hacia el empleo de novedosas y ergonmicas interfaces de usuario (como el ratn, las pantallas tctiles, las tabletas grficas, etc.), podemos decir que casi todas las acciones que el usuario realiza sobre estas interfaces se traducen antes o despus a secuencias de comandos que son ejecutadas como si hubieran sido introducidas por teclado. Por otro lado, y desde el punto de vista del profesional de la Informtica, el trabajo que ste realiza sobre el ordenador se encuentra plagado de situaciones en las que se produce una comunicacin textual directa con la mquina: utilizacin de un intrprete de comandos (shell), construccin de ficheros de trabajo por lotes, programacin mediante diversos lenguajes, etc.

VISION GENERAL

Incluso los procesadores de texto como WordPerfect y MS Word almacenan los documentos escritos por el usuario mediante una codificacin textual estructurada que, cada vez que se abre el documento, es reconocida, recorrida y presentada en pantalla. Por todo esto, ningn informtico que se precie puede esquivar la indudable necesidad de conocer los entresijos de la herramienta que utiliza durante su trabajo diario y sobre la que descansa la interaccin hombre-mquina: el traductor.

VISION GENERAL

Existe una gran cantidad de situaciones en las que puede ser muy til conocer cmo funcionan las distintas partes de un compilador, especialmente aqulla que se encarga de trocear los textos fuentes y convertirlos en frases sintcticamente vlidas. Por ejemplo, una situacin de aparente complejidad puede presentrsenos si se posee un documento de MS Word que procede de una fusin con una base de datos y se quiere, a partir de l, obtener la B.D. original. Cmo solucionar el problema?

VISION GENERAL

Pues basndose en que la estructura del documento est formada por bloques que se repiten; la solucin podra ser: Convertir el documento a formato texto puro. Procesar dicho texto con un traductor para eliminar los caracteres superfluos y dar como resultado otro texto en el que cada campo de la tabla de la B.D. est entre comillas. El texto anterior se importa con cualquier SGBD.

VISION GENERAL

Otras aplicaciones de la construccin de traductores pueden ser la creacin de preprocesadores para lenguajes que no lo tienen (por ejemplo, para trabajar fcilmente con SQL en C, se puede hacer un preprocesador para introducir SQL inmerso), o incluso la conversin del carcter ASCII 10 (LF) en <br> de HTML para pasar texto a la web. En esta primera parte, se introduce la construccin de un compilador y se describen sus componentes, el entorno en el que estos trabajan y algunas herramientas de software que facilitan su construccin.

Concepto de traductor

Un traductor se define como un programa que traduce o convierte desde un texto o programa escrito en un lenguaje fuente hasta un texto o programa equivalente escrito en un lenguaje destino produciendo, si cabe, mensajes de error. Los traductores engloban tanto a los compiladores (en los que el lenguaje destino suele ser cdigo mquina) como a los intrpretes (en los que el lenguaje destino est constituido por las acciones atmicas que puede ejecutar el intrprete).

Concepto de traductor

La figura 1.1 muestra el esquema bsico que compone a un compilador/intrprete.

Concepto de traductor

Es importante destacar la velocidad con la que hoy en da se puede construir un compilador. En la dcada de 1950, se consider a los traductores como programas notablemente difciles de escribir. El primer compilador de Fortran (Formula Translator), por ejemplo, necesit para su implementacin el equivalente a 18 aos de trabajo individual (realmente no se tard tanto puesto que el trabajo se desarroll en equipo). Hasta que la teora de autmatas y lenguajes formales no se aplic a la creacin de traductores, su desarrollo ha estado plagado de problemas y errores. Sin embargo, hoy da un compilador bsico puede ser el proyecto fin de carrera de cualquier estudiante universitario de Informtica.

Tipos de traductores

Desde los orgenes de la computacin, ha existido un abismo entre la forma en que las personas expresan sus necesidades y la forma en que un ordenador es capaz de interpretar instrucciones. Los traductores han intentado salvar este abismo para facilitarle el trabajo a los humanos, lo que ha llevado a aplicar la teora de autmatas a diferentes campos y reas concretas de la informtica, dando lugar a los distintos tipos de traductores que veremos a continuacin.

Traductores del idioma

Traducen de un idioma dado a otro, como por ejemplo del ingls al espaol. Este tipo de traductores posee multitud de problemas, a saber:

Necesidad de inteligencia artificial y problema de las frases hechas. El problema de la inteligencia artificial es que tiene mucho de artificial y poco de inteligencia, por lo que en la actualidad resulta casi imposible traducir frases con un sentido profundo.

Traductores del idioma

Como ancdota, durante la guerra fra, en un intento por realizar traducciones automticas del ruso al ingls y viceversa, se puso a prueba un prototipo introduciendo el texto en ingls: El espritu es fuerte pero la carne es dbil cuya traduccin al ruso se pas de nuevo al ingls para ver si coincida con el original. Cual fue la sorpresa de los desarrolladores cuando lo que se obtuvo fue: El vino est bueno pero la carne est podrida (en ingls spirit significa tanto espritu como alcohol ). Otros ejemplos difciles de traducir lo constituyen las frases hechas como: Piel de gallina, por si las moscas, etc.

Traductores del idioma

Difcil formalizacin en la especificacin del significado de las palabras. Cambio del sentido de las palabras segn el contexto. Ej: por decir aquello, se llev una galleta. En general, los resultados ms satisfactorios en la traduccin del lenguaje natural se han producido sobre subconjuntos restringidos del lenguaje. Y an ms, sobre subconjuntos en los que hay muy poco margen de ambigedad en la interpretacin de los textos: discursos jurdicos, documentacin tcnica, etc.

Compiladores

Es aquel traductor que tiene como entrada una sentencia en lenguaje formal y como salida tiene un fichero ejecutable, es decir, realiza una traduccin de un cdigo de alto nivel a cdigo mquina (tambin se entiende por compilador aquel programa que proporciona un fichero objeto en lugar del ejecutable final)

Intrpretes

Es como un compilador, solo que la salida es una ejecucin. El programa de entrada se reconoce y ejecuta a la vez. No se produce un resultado fsico (cdigo mquina) sino lgico (una ejecucin). Hay lenguajes que slo pueden ser interpretados, como p.ej. SNOBOL (StriNg Oriented SimBOlyc Language), LISP (LISt Processing), algunas versiones de BASIC (Beginners All-purpose Symbolic Instruction Code), etc.

Intrpretes

Su principal ventaja es que permiten una fcil depuracin. Entre los inconvenientes podemos citar, en primer lugar, la lentitud de ejecucin , ya que al ejecutar a la vez que se traduce no puede aplicarse un alto grado de optimizacin; por ejemplo, si el programa entra en un bucle y la optimizacin no est muy afinada, las mismas instrucciones se interpretarn y ejecutarn una y otra vez, ralentizando la ejecucin del programa. Otro inconveniente es que durante la ejecucin, el intrprete debe residir en memoria, por lo que consumen ms recursos.

Intrpretes

Adems de que la traduccin optimiza el programa acercndolo a la mquina, los lenguajes interpretados tienen la caracterstica de que permiten construir programas que se pueden modificar a s mismos. Algunos lenguajes intentan aunar las ventajas de los compiladores y de los intrpretes y evitar sus desventajas; son los lenguajes pseudointerpretados. En estos, el programa fuente pasa por un pseudocompilador que genera un pseudoejecutable. Para ejecutar este pseudoejecutable se le hace pasar por un motor de ejecucin que lo interpreta de manera relativamente eficiente.

Intrpretes

Esto tiene la ventaja de la portabilidad, ya que el pseudoejecutable es independiente de la mquina en que vaya a ejecutarse, y basta con que en dicha mquina se disponga del motor de ejecucin apropiado para poder interpretar cualquier pseudoejecutable. El ejemplo actual ms conocido lo constituye el lenguaje Java; tambin son pseudointerpretadas algunas versiones de Pascal y de COBOL (COmmon Bussiness Oriented Language). La figura muestra los pasos a seguir en estos lenguajes para obtener una ejecucin.

Preprocesadores

Permiten modificar el programa fuente antes de la verdadera compilacin. Hacen uso de macroinstrucciones y directivas de compilacin. Por ejemplo, en lenguaje C, el preprocesador sustituye la directiva #include Uno.c por el cdigo completo que contiene el fichero Uno.c, de manera que cuando el compilador comienza su ejecucin se encuentra con el cdigo ya insertado en el programa fuente (la figura siguiente ilustra esta situacin). Algunas otras directivas de preprocesamiento permiten compilar trozos de cdigos opcionales (lenguajes C y Clipper): #fi, #ifdef, #define, #ifndef, etc. Los preprocesadores suelen actuar de manera transparente para el programador, pudiendo incluso considerarse que son una fase preliminar del compilador.

Intrpretes de comandos

Un intrprete de comandos traduce sentencias simples a invocaciones a programas de una biblioteca. Se utilizan especialmente en los sistemas operativos (la shell de Unix es un intrprete de comandos). Los programas invocados pueden residir en el kernel (ncleo) del sistema o estar almacenados en algn dispositivo externo como rutinas ejecutables que se traen a memoria bajo demanda. Por ejemplo, si bajo MS-DOS se teclea el comando copy se ejecutar la funcin de copia de ficheros del sistema operativo, que se encuentra residente en memoria.

Intrpretes de comandos

Funcionamiento de la directiva de preprocesamiento #include en lenguaje C

Ensambladores y macroensambladores

Son los pioneros de los compiladores, ya que en los albores de la informtica, los programas se escriban directamente en cdigo mquina, y el primer paso hacia los lenguajes de alto nivel lo constituyen los ensambladores. En lenguaje ensamblador se establece una relacin biunvoca entre cada instruccin y una palabra mnemotcnica, de manera que el usuario escribe los programas haciendo uso de los mnemotcnicos, y el ensamblador se encarga de traducirlo a cdigo mquina puro. De esta manera, los ensambladores suelen producir directamente cdigo ejecutable en lugar de producir ficheros objeto.

Ensambladores y macroensambladores

Un ensamblador es un compilador sencillo, en el que el lenguaje fuente tiene una estructura tan sencilla que permite la traduccin de cada sentencia fuente a una nica instruccin en cdigo mquina. Al lenguaje que admite este compilador tambin se le llama lenguaje ensamblador. En definitiva, existe una correspondencia uno a uno entre las instrucciones ensamblador y las instrucciones mquina. Ej:

Instruccin ensamblador: LD HL, #0100 Cdigo mquina generado: 65h.00h.01h

Ensambladores y macroensambladores

Por otro lado, existen ensambladores avanzados que permiten definir macroinstrucciones que se pueden traducir a varias instrucciones mquina. A estos programas se les llama macroensambladores, y suponen el siguiente paso hacia los lenguajes de alto nivel. Desde un punto de vista formal, un macroensamblador puede entenderse como un ensamblador con un preprocesador previo.

Conversores fuente-fuente

Permiten traducir desde un lenguaje de alto nivel a otro lenguaje de alto nivel, con lo que se consigue una mayor portabilidad en los programas de alto nivel.

Conversores fuente-fuente

Por ejemplo, si un ordenador slo dispone de un compilador de Pascal, y queremos ejecutar un programa escrito para otra mquina en COBOL, pues un conversor de COBOL a Pascal solucionar el problema. No obstante el programa fuente resultado puede requerir retoques manuales debido a diversos motivos:

En situaciones en que el lenguaje destino carece de importantes caractersticas que el lenguaje origen s tiene. Por ejemplo un conversor de Java a C, necesitara modificaciones ya que C no tiene recolector de basura. En situaciones en que la traduccin no es inteligente y los programas destino son altamente ineficientes.

Compilador cruzado

Es un compilador que genera cdigo para ser ejecutado en otra mquina. Se utilizan en la fase de desarrollo de nuevos ordenadores. De esta manera es posible, p.ej., construir el sistema operativo de un nuevo ordenador recurriendo a un lenguaje de alto nivel, e incluso antes de que dicho nuevo ordenador disponga siquiera de un compilador. Ntese tambin que, para facilitar el desarrollo de software de un nuevo ordenador, uno de los primeros programas que se deben desarrollar para ste es, precisamente, un compilador de algn lenguaje de alto nivel.

Conceptos bsicos relacionados con la traduccin

A continuacin tenemos diversa terminologa relacionada con el proceso de compilacin y de construccin de compiladores.

Compilacin, enlace y carga

Estas son las tres fases bsicas que hay que seguir para que un ordenador ejecute la interpretacin de un texto escrito mediante la utilizacin de un lenguaje de alto nivel. Aunque este libro se centrar exclusivamente en la primera fase, vamos a ver en este punto algunas cuestiones relativas al proceso completo. Por regla general, el compilador no produce directamente un fichero ejecutable, sino que el cdigo generado se estructura en mdulos que se almacenan en un fichero objeto. Los ficheros objeto poseen informacin relativa tanto al cdigo mquina como a una tabla de smbolos que almacena la estructura de las variables y tipos utilizados por el programa fuente.

Compilacin, enlace y carga

Entrada y salida de un compilador real

Compilacin, enlace y carga

Pero, por qu no se genera directamente un fichero ejecutable?. Sencillamente, para permitir la compilacin separada, de manera que varios programadores puedan desarrollar simultneamente las partes de un programa ms grande y, lo que es ms importante, puedan compilarlos independientemente y realizar una depuracin en paralelo. Una vez que cada una de estas partes ha generado su correspondiente fichero objeto, estos deben fusionarse para generar un solo ejecutable.

Compilacin, enlace y carga

Como se ha comentado, un fichero objeto posee una estructura de mdulos tambin llamados registros. Estos registros tienen longitudes diferentes dependiendo de su tipo y cometido. Ciertos tipos de estos registros almacenan cdigo mquina, otros poseen informacin sobre las variables globales, y otros incluyen informacin sobre los objetos externos (p. ej, variables que se supone que estn declaradas en otro ficheros. El lenguaje C permite explcitamente esta situacin mediante el modificador extern).

Compilacin, enlace y carga

Durante la fase de enlace, el enlazador o linker resuelve las referencias cruzadas, (as se llama a la utilizacin de objetos externos), que pueden estar declarados en otros ficheros objeto, o en libreras (ficheros con extensin lib o dll), engloba en un nico bloque los distintos registros que almacenan cdigo mquina, estructura el bloque de memoria destinado a almacenar las variables en tiempo de ejecucin y genera el ejecutable final incorporando algunas rutinas adicionales procedentes de libreras, como por ejemplo las que implementan funciones matemticas o de e/s bsicas. La siguiente figura ilustra este mecanismo de funcionamiento.

Compilacin, enlace y carga

Funcionamiento de un enlazador

Compilacin, enlace y carga

De esta manera, el bloque de cdigo mquina contenido en el fichero ejecutable es un cdigo reubicable, es decir, un cdigo que en su momento se podr ejecutar en diferentes posiciones de memoria, segn la situacin de la misma en el momento de la ejecucin. Segn el modelo de estructuracin de la memoria del microprocesador, este cdigo se estructura de diferentes formas. Lo ms usual es que el fichero ejecutable est dividido en segmentos: de cdigo, de datos, de pila de datos, etc.

Compilacin, enlace y carga

Cuando el enlazador construye el fichero ejecutable, asume que cada segmento va a ser colocado en la direccin 0 de la memoria. Como el programa va a estar dividido en segmentos, las direcciones a que hacen referencia las instrucciones dentro de cada segmento (instrucciones de cambio de control de flujo, de acceso a datos, etc.), no se tratan como absolutas, sino que son direcciones relativas a partir de la direccin base en que sea colocado cada segmento en el momento de la ejecucin. El cargador carga el fichero .exe, coloca sus diferentes segmentos en memoria (donde el sistema operativo le diga que hay memoria libre para ello) y asigna los registros base a sus posiciones correctas, de manera que las direcciones relativas funcionen correctamente. La figura siguiente ilustra el trabajo que realiza un cargador de programas.

Compilacin, enlace y carga

Labor realizada por el cargador. El cargador suele ser parte del sistema operativo.

Compilacin, enlace y carga

Cada vez que una instruccin mquina hace referencia a una direccin de memoria (partiendo de la direccin 0), el microprocesador se encarga automticamente de sumar a dicha direccin la direccin absoluta de inicio de su segmento. Por ejemplo para acceder a la variable x almacenada en la direccin 1Fh, que se encuentra en el segmento de datos ubicado en la direccin 8A34h, la instruccin mquina har referencia a 1Fh, pero el microprocesador la traducir por 8A34h+1Fh dando lugar a un acceso a la direccin 8A53h: dir absoluta del segmento en memoria + dir relativa de x en el segmento = dir absoluta de x en memoria.

Pasadas de compilacin

Es el nmero de veces que un compilador debe leer el programa fuente para generar el cdigo. Hay algunas situaciones en las que, para realizar la compilacin, no es suficiente con leer el fichero fuente una sola vez. Por ejemplo, en situaciones en las que existe recursin indirecta (una funcin A llama a otra B y la B llama a la A). Cuando se lee el cuerpo de A, no se sabe si B est declarada ms adelante o se le ha olvidado al programador; o si lo est, si los parmetros reales coinciden en nmero y tipo con los formales o no. Es ms, aunque todo estuviera correcto, an no se ha generado el cdigo para B, luego no es posible generar el cdigo mquina correspondiente a la invocacin de B puesto que no se sabe su direccin de comienzo en el segmento de cdigo. Por todo esto, en una pasada posterior hay que controlar los errores y rellenar los datos que faltan.

Pasadas de compilacin

Diferentes compiladores y lenguajes solucionan este problema de maneras distintas. Una solucin consiste en hacer dos o ms pasadas de compilacin, pero ello consume demasiado tiempo puesto que las operaciones de e/s son, hoy por hoy, uno de los puntos fundamentales en la falta de eficiencia de los programas (incluidos los compiladores). Otra solucin pasa por hacer una sola pasada de compilacin y modificar el lenguaje obligando a hacer las declaraciones de funciones recursivas indirectas antes de su definicin. De esta manera, lenguajes como Pascal o Modula-2 utilizan la palabra reservada FORWARD para ello: FORWARD precede la declaracin de B, a continuacin se define A y por ltimo se define B. Lenguajes como C tambin permiten el no tener que declarar una funcin si sta carece de parmetros y devuelve un entero.

Pasadas de compilacin

Otros lenguajes dan por implcito el FORWARD, y si an no se han encontrado aqullo a que se hace referencia, continan, esperando que el linker resuelva el problema, o emita el mensaje de error. Actualmente, cuando un lenguaje necesita hacer varias pasadas de compilacin, suele colocar en memoria una representacin abstracta del fichero fuente, de manera que las pasadas de compilacin se realizan sobre dicha representacin en lugar de sobre el fichero de entrada, lo que soluciona el problema de la ineficiencia debido a operaciones de e/s.

Compilacin incremental

Cuando se desarrolla un programa fuente, ste se recompila varias veces hasta obtener una versin definitiva libre de errores. Pues bien, en una compilacin incremental slo se recompilan las modificaciones realizadas desde la ltima compilacin. Lo ideal es que slo se recompilen aquellas partes que contenan los errores o que, en general, hayan sido modificadas, y que el cdigo generado se reinserte con cuidado en el fichero objeto generado en la ltima compilacin. Sin embargo esto es muy difcil de conseguir y no suele ahorrar tiempo de compilacin ms que en casos muy concretos.

Compilacin incremental

La compilacin incremental se puede llevar a cabo con distintos grados de afinacin. Por ejemplo, si se olvida un ; en una sentencia, se podra generar un fichero objeto transitorio parcial. Si se corrige el error y se aade el ; que falta y se recompila, un compilador incremental puede funcionar a varios niveles

A nivel de carcter: se recompila el ; y se inserta en el fichero objeto la sentencia que faltaba. A nivel de sentencia: si el ; faltaba en la lnea 100 slo se compila la lnea 100 y se actualiza el fichero objeto. A nivel de bloque: si el ; faltaba en un procedimiento o bloque slo se compila ese bloque y se actualiza el fichero objeto.

Compilacin incremental

A nivel de fichero fuente: si la aplicacin completa consta de 15 ficheros fuente, y solo se modifica 1( al que le faltaba el ;), slo se compila aqul al que se le ha aadido el ;, se genera por completo su fichero objeto y luego se enlazan todos juntos para obtener el ejecutable.

Lo ideal es que se hiciese eficientemente a nivel de sentencia, pero lo normal es encontrarlo a nivel de fichero. La mayora de los compiladores actuales realizan una compilacin incremental a este nivel.

Compilacin incremental

Cuando un compilador no admite compilacin incremental, suelen suministrar una herramienta externa (como RMAKE en caso de Clipper, MAKE en algunas versiones de C) en la que el programador indica las dependencias entre ficheros, de manera que si se recompila uno, se recompilan todos los que dependen de aqul. Estas herramientas suelen estar diseadas con un propsito ms general y tambin permiten enlaces condicionales.

Autocompilador

Es un compilador escrito en el mismo lenguaje que compila (o parecido). Normalmente, cuando se extiende entre muchas mquinas diferentes el uso de un compilador, y ste se desea mejorar, el nuevo compilador se escribe utilizando el lenguaje del antiguo, de manera que pueda ser compilado por todas esas mquinas diferentes, y d como resultado un compilador ms potente de ese mismo lenguaje.

Metacompilador

Este es uno de los conceptos ms importantes con los que vamos a trabajar. Un metacompilador es un compilador de compiladores. Se trata de un programa que acepta como entrada la descripcin de un lenguaje y produce el compilador de dicho lenguaje. Hoy por hoy no existen metacompiladores completos, pero s parciales en los que se acepta como entrada una gramtica de un lenguaje y se genera un autmata que reconoce cualquier sentencia del lenguaje . A este autmata podemos aadirle cdigo para completar el resto del compilador.

Metacompilador

Ejemplos de metacompiladores son: Lex, YACC, FLex, Bison, JavaCC, JLex, Cup, PCCTS, MEDISE, etc. Los metacompiladores se suelen dividir entre los que pueden trabajar con gramticas de contexto libre y los que trabajan con gramticas regulares. Los primeros se dedican a reconocer la sintaxis del lenguaje y los segundos trocean los ficheros fuente y lo dividen en palabras.

Metacompilador

PCLex es un metacompilador que genera la parte del compilador destinada a reconocer las palabras reservadas. PCYACC es otro metacompilador que genera la parte del compilador que informa sobre si una sentencia del lenguaje es vlida o no. JavaCC es un metacompilador que ana el reconocimiento de palabras reservadas y la aceptacin o rechazo de sentencias de un lenguaje. PCLex y PCYACC generan cdigo C y admiten descripciones de un lenguaje mediante gramticas formales, mientras que JavaCC produce cdigo Java y admite descripciones de lenguajes expresadas en notacin BNF (Backus-Naur Form).