Toda compilación es un proceso de transformación paulatina que convierte un programa escrito en un lenguaje fuente de alto nivel en otro programa escrito en un lenguaje objeto de bajo nivel. Ese proceso se articula Análisis léxico Análisis sintáctico Análisis Generación de código intermedio Generación de código final While ( a > b ) do e l i h w <WHILE, PR> S E > E S E > E BRZ t3 L1 0100 0001 0100 0000 0001 0010
I. Análisis léxico El analizador léxico (scanner) va leyendo caracteres del fichero hasta encontrar una entidad con significado léxico. Estas entidades se llaman tokens y son una estructura de datos que contiene información acerca del mismo (tipo, lexema, número de fila y columna, valor, etc.) La construcción de analizadores léxicos suele hacerse mediante herramientas que hay que configurar indicando el tipo de token que es necesario emitir para cada posible patrón léxico while : <WHILE, PR> do : <DO, PR> l (l d)* : <a, ID> While ( a > b ) do e l i h w <WHILE, PR>
II. Análisis sintáctico El analizador sintáctico (parser) va pidiendo tokens al analizador léxico y los va organizando en frases de acuerdo a las reglas de construcción gramatical del lenguaje. Como resultado genera un árbol de análisis sintáctico que representa todo el programa en memoria La construcción de analizadores sintácticos suele hacerse mediante herramientas que hay que configurar indicando la colección de reglas sintácticas que definen las construcciones del lenguaje gramaticalmente correctas S ::= SIf Swhlile SWhile ::= while PI E PD do S; E ::= E + E E ::= E * E E ::= E > E E ::= E < E E ::= id SWhile E > E <:=, ASIG> <a, ID> <DO, PR> <), PD> <b, ID> <>, GT> <a, ID> <(, PI> <WHILE, PR>
III. Análisis El analizador se encarga de gestionar la declaración de constantes, funciones, procedimientos y variables y de comprobar la corrección de tipos a lo largo de todo el programa. Como resultado se obtiene un árbol de análisis sintáctico anotado, con atributos en cada uno de sus nodos La implementación del analizado se realiza insertando acciones semánticas en las partes derechas de las reglas de producción gramatical S ::= SIf Swhlile SWhile ::= while PI E PD do S; {: if (E.tipo!= booleano) error (); :} SWhile E > E SWhile E > E
IV. Generación de código intermedio El generador de código intermedio traduce la representación arborescente del programa en una secuencia ordenada de instrucciones llamadas cuádruplas próximas al lenguaje máquina. Se trata de un lenguaje abstracto y genérico que aún mantiene las referencias simbólicas a los elementos declarados en el programa (variables, parámetros, funciones, etc.) La implementación del generador de código intermedio se realiza insertando acciones en las partes derechas de las reglas de producción que acumulan la traducción parcial del subárbol en cada nodo exp ::= exp:e1 > exp:e2 {: e.código = e1.código + e2.código + CMP e1.temp r2.temp e.temp :}; SWhile E > E BRZ t3 L1
V. Optimización de código intermedio En los compiladores comerciales es frecuente optimizar el resultado de la fase anterior para hacer el programa más compacto y más rápido. Suelen aplicarse estrategias de transformaciones heurísticas y elementales que ofrecen buenos resultados WHILE (A>B) AND (A<2*B-5) DO A:=A+B L1: IF A>B GOTO L2 GOTO L3 L2: T1 := 2*B T2 := T1 5 IF A< T2 GOTO L4 GOTO L3 L4: A := A + B GOTO L1 L3: Optimización L1: IF A<=B GOTO L2 T1 := 2*B T2 := T1 5 IF A>= T2 GOTO L2 A := A + B GOTO L1 L2: BRZ t3 L1 Optimización Código intermedio BRZ t3 L1
VI. Una vez que el código intermedio ha sido generado y optimizado pueden resolverse las referencias simbólicas a posiciones de memoria física y a recursos de la máquina (registros, pila, etc.). Además debe traducirse cada cuarteto a sus equivalentes instrucciones en código máquina La traducción a código final se encarga de hacerla una rutina de traducción ubicada en la acción semántica final ubicada al final de la regla de producción del axioma BRZ t3 L1 0100 0001 0100 0000 0001 0010
Etapas de compilación Etapa de análisis Etapa de síntesis While ( a > b ) do e l i h w <WHILE, PR> S E > E S E > E BRZ t3 L1 0100 0001 0100 0000 0001 0010 Expertos en lenguajes Independencia de arquitectura Dependencia de lenguaje Optimización de lenguajes Expertos en arquitecturas Dependencia de arquitectura Independencia de lenguaje Optimización de ejecución
Equipos de compilación Pascal While ( a > b ) do while ( a > b ) a++; C M Equipo de Lenguaje x Intel BRZ t3 L1 Solaris N Equipo de arquitectura Optimización Código intermedio Optimización Código intermedio
Artefactos de compilación A lo largo de todo el proceso de compilación se una colección de artefactos para dar soporte al mismo. A lo largo del curso los estudiaremos en detalle. Dos son de vital importancia I. Tabla de símbolos La tabla de símbolos se utiliza para gestionar todos los elementos declarados por programador (constantes, tipos, variables, funciones y procedimientos) Fase Descripción En compiladores antiguos utilizado para reconocer palabras clave Registra los elementos declarados por el programador y los consulta para efectuar la comprobación de tipos Se consulta para conocer el espacio de memoria que debe reservarse para gestionar la activación de subrutinas y para determinar el ámbito de declaración de los elementos Se utiliza para traducir las referencias simbólicas a posiciones de memorial Tabla de símbolos Optimización Código intermedio
Artefactos de compilación A lo largo de todo el proceso de compilación se una colección de artefactos para dar soporte al mismo. A lo largo del curso los estudiaremos en detalle. Dos son de vital importancia II. Gestor de errores Las tres primeras fases pueden generar errores durante el proceso de compilación. El gestor de errores se encarga de emitir mensajes de los mismos para informar al programador While ( a > b ) Whiñe ( a > b ) do While ( a > b ) do a := a + c ; Carácter ñ no válido SWhile ::= WHILE e DO s Gestor de errores Se esperaba un entero en expresión de suma