The Semantic Analyzer is the third phase of compilation. After the Parser creates a syntactically correct AST, the Semantic Analyzer verifies that the program makes sense logically.
Analogy: Grammar vs. MeaningGrammatically correct but meaningless:
“Colorless green ideas sleep furiously.”Semantically correct:
“The cat sleeps quietly.”Similarly, let x = y + 1; is syntactically valid, but meaningless if y doesn’t exist.
Analyze the expression first (may reference other variables)
Add 'x' to variables_declaradas
Continue to next statement
def _analizar_declaracion(self, decl: DeclaracionVariable): nombre = decl.nombre.lexema # Check expression first self._analizar_expresion(decl.expresion) # Then register variable self.variables_declaradas.add(nombre)
2
Usage
When encountering an identifier in an expression:
Check if variable is in variables_declaradas
If not, add error to self.errores
Continue analysis (don’t stop)
def _analizar_expresion(self, expr: Expresion): if isinstance(expr, Identificador): if expr.nombre not in self.variables_declaradas: self.errores.append( f"Error semántico: la variable '{expr.nombre}' no ha sido declarada" )
def _analizar_declaracion(self, decl: DeclaracionVariable): nombre = decl.nombre.lexema # Warn on redeclaration (not error) if nombre in self.variables_declaradas: self.errores.append( f"Advertencia: la variable '{nombre}' ya fue declarada" ) # Analyze initializer expression self._analizar_expresion(decl.expresion) # Register variable self.variables_declaradas.add(nombre)
def _analizar_print(self, stmt: SentenciaPrint): # Just analyze the expression self._analizar_expresion(stmt.expresion)
def _analizar_expresion(self, expr: Expresion): # Literals are always valid if isinstance(expr, NumeroLiteral): pass # Variables must be declared elif isinstance(expr, Identificador): if expr.nombre not in self.variables_declaradas: self.errores.append(...) # Binary: check both sides + division by zero elif isinstance(expr, ExpresionBinaria): self._analizar_expresion(expr.izquierda) self._analizar_expresion(expr.derecha) # Division by literal zero? if expr.operador.tipo == TipoToken.DIVISION: if isinstance(expr.derecha, NumeroLiteral) and expr.derecha.valor == 0: self.errores.append(...) # Grouped: recurse into inner expression elif isinstance(expr, ExpresionAgrupada): self._analizar_expresion(expr.expresion)
let x = 5;let y = x + z; // z is not declaredprint y;
# Statement 1: let x = 5;variables_declaradas = set() # EmptyAnalyze: NumeroLiteral(5) ✓Add 'x' to variables_declaradasvariables_declaradas = {'x'}# Statement 2: let y = x + z;Analyze left: Identificador('x') → 'x' in variables_declaradas ✓Analyze right: Identificador('z') → 'z' in variables_declaradas ✗ → Error: "la variable 'z' no ha sido declarada"Add 'y' to variables_declaradas (despite error)variables_declaradas = {'x', 'y'}# Statement 3: print y;Analyze: Identificador('y') → 'y' in variables_declaradas ✓# Result: 1 error found
Error semántico en línea 2, columna 13: la variable 'z' no ha sido declarada
let x = 10 / 0; // Literal zerolet y = 10 / z; // Variable (not caught)
# Statement 1: let x = 10 / 0;Analyze: ExpresionBinaria(/) Left: NumeroLiteral(10) ✓ Right: NumeroLiteral(0) ✓ Operator: DIVISION Right is NumeroLiteral with valor == 0 ✗ → Error: "división entre cero detectada"Add 'x' to variables_declaradas# Statement 2: let y = 10 / z;Analyze: ExpresionBinaria(/) Left: NumeroLiteral(10) ✓ Right: Identificador('z') → 'z' not in variables_declaradas ✗ → Error: "la variable 'z' no ha sido declarada" Right is NOT NumeroLiteral → Division by zero check NOT performedAdd 'y' to variables_declaradas# Result: 2 errors found
Error semántico en línea 1, columna 10: división entre cero detectadaError semántico en línea 2, columna 13: la variable 'z' no ha sido declarada
let x = 5;let x = 10; // Redeclaration (warning, not error)print x;
# Statement 1: let x = 5;variables_declaradas = set()'x' in variables_declaradas → FalseAdd 'x'variables_declaradas = {'x'}# Statement 2: let x = 10;'x' in variables_declaradas → True→ Warning: "la variable 'x' ya fue declarada anteriormente"Still add 'x' (idempotent in set)variables_declaradas = {'x'}# Statement 3: print x;'x' in variables_declaradas → True ✓# Result: 1 warning (not counted as error)
Advertencia en línea 2, columna 5: la variable 'x' ya fue declarada anteriormente
def _analizar_expresion(self, expr: Expresion): if isinstance(expr, Identificador): if expr.nombre not in self.variables_declaradas: # Add error but DON'T raise exception self.errores.append(...) # Continue analyzing
Benefits:
Better UX
User sees all errors at once, not just the first one.Saves time: fix multiple issues in one iteration.
More Context
Multiple errors can reveal root cause.Example: 5 “undeclared variable” errors → likely missing one let statement.
Example:
let x = a + b; // 2 errorslet y = c * d; // 2 errorsprint z; // 1 error
Output:
Error semántico en línea 1, columna 9: la variable 'a' no ha sido declaradaError semántico en línea 1, columna 13: la variable 'b' no ha sido declaradaError semántico en línea 2, columna 9: la variable 'c' no ha sido declaradaError semántico en línea 2, columna 13: la variable 'd' no ha sido declaradaError semántico en línea 3, columna 7: la variable 'z' no ha sido declarada5 errores encontrados
let x = y + 1; // Error: 'y' undeclaredlet y = 10 / 0; // Error: division by zerolet z = x * y; // OK: both declaredprint w; // Error: 'w' undeclared
Analysis:
✗ Statement 1: Variable 'y' not declared✗ Statement 2: Division by zero✓ Statement 3: Variables 'x' and 'y' exist✗ Statement 4: Variable 'w' not declaredErrores semánticos: 1. Error semántico en línea 1, columna 9: la variable 'y' no ha sido declarada 2. Error semántico en línea 2, columna 10: división entre cero detectada 3. Error semántico en línea 4, columna 7: la variable 'w' no ha sido declarada✗ Compilación fallida: 3 errores semánticos encontrados