El analizador semántico es la tercera gran etapa del front-end de un compilador, después del léxico y el sintáctico, y su papel es dar significado y coherencia al código más allá de la estructura.
En pocas palabras:
-
El léxico pregunta: “¿Esto es un token válido?”
-
El sintáctico pregunta: “¿Estos tokens están en un orden gramatical correcto?”
-
El semántico pregunta: “¿Esto tiene sentido en el contexto del lenguaje?”
1. Función del analizador semántico
Relacionado: Puntero. Herramientas.
El analizador semántico trabaja sobre el árbol sintáctico generado por el parser y, usando información de la tabla de símbolos, realiza:
-
Comprobaciones estáticas (en tiempo de compilación):
-
Tipos compatibles en operaciones (
int + float,string + int, etc.). -
Que las variables y funciones estén declaradas antes de usarse.
-
Que el número y tipo de parámetros en llamadas a funciones coincidan con su definición.
-
Que los identificadores no estén duplicados en el mismo ámbito.
-
Que las constantes no se modifiquen.
-
-
Comprobaciones dinámicas (insertando código que se verificará en ejecución):
-
Acceso a índices de arrays dentro de rango.
-
División entre cero.
-
Comprobaciones de punteros nulos.
-
-
Decoración del árbol sintáctico:
-
Añadir atributos a cada nodo: tipo, valor, ubicación en memoria, etc.
-
Ejemplo: el nodo
a + brecibe como atributo el tipo resultante tras aplicar las reglas de promoción de tipos.
-
2. Herramientas y mecanismos
-
Gramáticas de atributos:
Extienden la gramática sintáctica añadiendo reglas semánticas para calcular propiedades.-
Atributos sintetizados: calculados desde los hijos hacia el padre.
-
Atributos heredados: calculados desde el padre o hermanos hacia un nodo.
-
-
Traducción dirigida por la sintaxis:
El significado se asocia directamente con la forma del árbol sintáctico. -
Grafos de dependencias:
Representan qué atributos dependen de cuáles para calcularlos en orden correcto.
3. Ejemplos de comprobaciones semánticas
Ejemplo 1 — Tipos compatibles:
VAR x : INTEGER;
y : REAL;
BEGIN
x := y + 3.14;
END;El semántico detecta que y + 3.14 es REAL y exige conversión antes de asignar a x.
Ejemplo 2 — Variable no declarada:
int main() {
z = 10; // Error: 'z' no ha sido declarada
}Ejemplo 3 — Parámetros correctos:
void f(int a, float b);
...
f(3, "hola"); // Error: el segundo argumento debería ser float4. Relación con las otras fases
-
Léxico → reconoce que
if,x,<,10son tokens válidos. -
Sintáctico → verifica que
if (x < 10) { y = y + 1; }sigue la gramática del lenguaje. -
Semántico → confirma que
xyyestán declaradas, que<es válido para sus tipos, quey+1puede asignarse ay.
Si quieres, puedo montarte un ejemplo completo donde tomemos un fragmento de código, lo pasemos por léxico, sintáctico y semántico, para que veas qué detecta cada uno y dónde el sintáctico no basta pero el semántico sí detecta el error.
¿Quieres que lo haga?