Lenguajes naturales y artificiales
1.13.2. Procesadores de lenguaje
Los programas escritos en lenguaje de la máquina son los únicos que se pueden ejecutar directa- mente en una computadora. Los restantes hay que traducirlos.
• Los lenguajes simbólicos se traducen mediante programas llamados ensambladores, que convierten cada instrucción simbólica en la instrucción máquina equivalente. Estos progra- mas suelen ser relativamente sencillos y no se van a considerar aquí.
• Los programas escritos en lenguajes de alto nivel se traducen mediante programas llama- dos, en general, traductores o procesadores de lenguaje. Existen tres tipos de estos traduc- tores:
— Compilador: analiza un programa escrito en un lenguaje de alto nivel (programa fuen- te) y, si es correcto, genera un código equivalente (programa objeto) escrito en otro len- guaje, que puede ser de primera generación (de la máquina), de segunda generación (simbólico) o de tercera generación. El programa objeto puede guardarse y ejecutarse tantas veces como se quiera, sin necesidad de traducirlo de nuevo.
Un compilador se representa con el símbolo de la Figura 1.5, donde A es el lenguaje
fuente, B es el lenguaje objeto y C es el lenguaje en que está escrito el propio compila-
dor, que al ser un programa que debe ejecutarse en una computadora, también habrá te- nido que ser escrito en algún lenguaje, no necesariamente el mismo que el lenguaje fuente o el lenguaje objeto.
A C
B
Figura 1.5. Representación simbólica de un compilador.
A
C
Figura 1.6. Representación simbólica de un intérprete.
Entre los lenguajes que usualmente se compilan podemos citar FORTRAN, COBOL, C, C++, PASCAL y ADA.
— Intérprete: analiza un programa escrito en un lenguaje de alto nivel y, si es correcto, lo ejecuta directamente en el lenguaje de la máquina en que se está ejecutando el intér- prete. Cada vez que se desea ejecutar el programa, es preciso interpretarlo de nuevo. Un intérprete se representa con el símbolo de la Figura 1.6, donde A es el lenguaje fuen-
te y C es el lenguaje en que está escrito el propio intérprete, que también debe ejecu-
Entre los lenguajes que usualmente se interpretan citaremos LISP, APL, SMALLTALK, JAVA y PROLOG. De algún lenguaje, como BASIC, existen a la vez compiladores e intérpretes.
— Compilador-intérprete: traduce el programa fuente a un formato o lenguaje interme- dio, que después se interpreta.
Un compilador-intérprete se representa con los símbolos de la Figura 1.7, donde A es el
lenguaje fuente, B es el lenguaje intermedio, C es el lenguaje en que está escrito el com-
pilador y D es el lenguaje en que está escrito el intérprete, no necesariamente el mismo que A, B o C.
A B
C B
D
Figura 1.7. Representación simbólica de un compilador-intérprete.
JAVA es un ejemplo típico de lenguaje traducido mediante un compilador-intérprete, pues primero se compila a BYTECODE, y posteriormente éste se interpreta mediante una máquina virtual de JAVA, que no es otra cosa que un intérprete de BYTECODE. En este caso, A es JAVA, B es BYTECODE, C es el lenguaje en que esté escrito el com- pilador de JAVA a BYTECODE, y D es el lenguaje en que esté escrita la máquina vir- tual de JAVA.
Los compiladores generan código más rápido que los intérpretes, pues éstos tienen que ana- lizar el código cada vez que lo ejecutan. Sin embargo, los intérpretes proporcionan ciertas ven- tajas, que en algunos compensan dicha pérdida de eficiencia, como la protección contra virus, la independencia de la máquina y la posibilidad de ejecutar instrucciones de alto nivel generadas durante la ejecución del programa. Los compiladores-intérpretes tratan de obtener estas ventajas con una pérdida menor de tiempo de ejecución.
1.13.3.
Partes de un procesador de lenguaje
Un compilador se compone de las siguientes partes (véase la Figura 1.8): • Tabla de símbolos o identificadores.
• Analizador morfológico, también llamado analizador lexical, preprocesador o scanner, en inglés. Realiza la primera fase de la compilación. Convierte el programa que va a ser compi- lado en una serie de unidades más complejas (unidades sintácticas) que desempeñan el papel de símbolos terminales para el analizador sintáctico. Esto puede hacerse generando dichas
unidades de una en una o línea a línea. Elimina espacios en blanco y comentarios, y detecta errores morfológicos. Usualmente se implementa mediante un autómata finito determinista. • Analizador sintáctico, también llamado parser, en inglés. Es el elemento fundamental del
procesador, pues lleva el control del proceso e invoca como subrutinas a los restantes ele- mentos del compilador. Realiza el resto de la reducción al axioma de la gramática para com- probar que la instrucción es correcta. Usualmente se implementa mediante un autómata a pila o una construcción equivalente.
• Analizador semántico. Comprueba la corrección semántica de la instrucción, por ejemplo, la compatibilidad del tipo de las variables en una expresión.
• Generador de código. Traduce el programa fuente al lenguaje objeto utilizando toda la información proporcionada por las restantes partes del compilador.
• Optimizador de código. Mejora la eficiencia del programa objeto en ocupación de memo- ria o en tiempo de ejecución.
• Gestión de memoria, tanto en el procesador de lenguaje como en el programa objeto. • Recuperación de errores detectados.
En los compiladores de un solo paso o etapa, suele fundirse el analizador semántico con el generador de código. Otros compiladores pueden ejecutarse en varios pasos. Por ejemplo, en el primero se puede generar un código intermedio en el que ya se han realizado los análisis morfo- lógico, sintáctico y semántico. El segundo paso es otro programa que parte de ese código inter- medio y, a partir de él, genera el código objeto. Todavía es posible que un compilador se ejecute en tres pasos, dedicándose el tercero a la optimización del código generado por la segunda fase.
En un intérprete no existen las fases de generación y optimización de código, que se sustitu- yen por una fase de ejecución de código.