Para gestionar la complejidad de la traducción de código, la estructura de un compilador se divide conceptualmente en dos partes principales, que a su vez agrupan las distintas fases del proceso:
Se encarga de entender el código fuente. Verifica que el programa sea léxica, sintáctica y semánticamente correcto. Depende del lenguaje (C++, Java, etc.).
Genera el código objeto. Toma la salida del frontend y la traduce al código de la máquina destino. Depende de la arquitectura (x86, ARM).
Ambas partes se comunican mediante una Representación Intermedia (IR). El proceso completo sigue estas fases:
Análisis Léxico: Lee el código fuente y lo agrupa en "tokens" (unidades con significado, como id, num, op). Elimina espacios y comentarios.
Análisis Sintáctico: Revisa que los tokens sigan la gramática del lenguaje (el orden correcto). Produce un Árbol de Sintaxis Abstracta (AST) que representa la estructura jerárquica del código.
Análisis Semántico: Revisa el AST para encontrar errores lógicos. Comprueba los tipos de datos (ej. no sumar un texto a un número), que las variables estén declaradas y que las funciones se usen correctamente.
Generación de Código Intermedio (IR): Traduce el AST a un código genérico, independiente del lenguaje y la máquina. (Se detalla en el punto 4).
Optimización de Código: Mejora el código intermedio para que sea más rápido o use menos memoria (ej. elimina código muerto o instrucciones redundantes).
Generación de Código Final: Traduce la IR optimizada al lenguaje ensamblador o código máquina de la CPU destino.
Tabla de Símbolos: Una base de datos que guarda información sobre variables y funciones (tipo, ámbito, etc.) usada por todas las fases.
Manejador de Errores: Reporta errores (sintácticos, semánticos) al usuario.