Skip to main content

Overview

The visitor pattern enables traversal and interpretation of the Abstract Syntax Tree (AST) produced by the parser. The Expresiones compiler uses two visitor classes:
  1. ExpresionesVisitor - Base visitor class generated by ANTLR (provides default traversal)
  2. Visitor - Custom implementation that performs semantic analysis and interpretation

Base Visitor Class (ExpresionesVisitor)

Generated by ANTLR from Expresiones.g, this class provides empty visit methods for all grammar rules. Location: ~/workspace/source/ExpresionesVisitor.py
from antlr4 import *
if "." in __name__:
    from .ExpresionesParser import ExpresionesParser
else:
    from ExpresionesParser import ExpresionesParser

class ExpresionesVisitor(ParseTreeVisitor):
    # Visit methods for all grammar rules...

Available Visit Methods

From ExpresionesVisitor.py:12-84:
def visitProg(self, ctx:ExpresionesParser.ProgContext):
    return self.visitChildren(ctx)

def visitInstrDecl(self, ctx:ExpresionesParser.InstrDeclContext):
    return self.visitChildren(ctx)

def visitInstrAsig(self, ctx:ExpresionesParser.InstrAsigContext):
    return self.visitChildren(ctx)

def visitInstrIf(self, ctx:ExpresionesParser.InstrIfContext):
    return self.visitChildren(ctx)

def visitBloque(self, ctx:ExpresionesParser.BloqueContext):
    return self.visitChildren(ctx)

def visitDeclaracion(self, ctx:ExpresionesParser.DeclaracionContext):
    return self.visitChildren(ctx)

def visitAsignacion(self, ctx:ExpresionesParser.AsignacionContext):
    return self.visitChildren(ctx)

def visitRelacional(self, ctx:ExpresionesParser.RelacionalContext):
    return self.visitChildren(ctx)

def visitParentesisCond(self, ctx:ExpresionesParser.ParentesisCondContext):
    return self.visitChildren(ctx)

def visitNotLogica(self, ctx:ExpresionesParser.NotLogicaContext):
    return self.visitChildren(ctx)

def visitLogica(self, ctx:ExpresionesParser.LogicaContext):
    return self.visitChildren(ctx)

def visitNumero(self, ctx:ExpresionesParser.NumeroContext):
    return self.visitChildren(ctx)

def visitVariable(self, ctx:ExpresionesParser.VariableContext):
    return self.visitChildren(ctx)

def visitAritmetica(self, ctx:ExpresionesParser.AritmeticaContext):
    return self.visitChildren(ctx)

def visitParentesisExpr(self, ctx:ExpresionesParser.ParentesisExprContext):
    return self.visitChildren(ctx)
The base visitor simply calls visitChildren() for each node. Custom implementations override these methods to add functionality.

Custom Visitor Implementation

The Visitor class extends ExpresionesVisitor to provide semantic analysis, symbol table management, and interpretation. Location: ~/workspace/source/Visitor.py

Class Structure

From Visitor.py:10-12:
class Visitor(ExpresionesVisitor):
    def __init__(self):
        self.tabla_simbolos = {}  # Symbol table: {name: Simbolo}
The visitor maintains state through a symbol table that maps variable names to Simbolo objects (see Symbol Table for details).

Visit Method Implementations

1. visitProg - Program Entry Point

From Visitor.py:14-16:
def visitProg(self, ctx):
    print("Iniciando ejecución...")
    return self.visitChildren(ctx)
Called when visiting the root ProgContext. Prints a startup message and traverses all child nodes (instructions). Example:
program {
    int x = 10;
}
Output: Iniciando ejecución...

2. visitInstrDecl - Variable Declaration

From Visitor.py:18-31:
def visitInstrDecl(self, ctx):
    tipo = ctx.declaracion().TIPO().getText()
    nombre = ctx.declaracion().ID().getText()
    
    valor_inicial = None
    if ctx.declaracion().ASIGNACION():
        valor_inicial = self.visit(ctx.declaracion().expr())
    
    if nombre in self.tabla_simbolos:
        print(f"Error Semántico: Variable '{nombre}' ya declarada.")
    else:
        self.tabla_simbolos[nombre] = Simbolo(nombre, tipo, valor_inicial)
        print(f"Declaración: {nombre} ({tipo}) = {valor_inicial}")
    return None
Functionality:
  • Extracts type and identifier from the declaration context
  • Evaluates initialization expression if present
  • Checks for duplicate declarations (semantic error)
  • Adds symbol to symbol table
  • Prints declaration information
Example 1: Declaration without initialization
int x;
Output: Declaración: x (int) = None Example 2: Declaration with initialization
float pi = 3.14;
Output: Declaración: pi (float) = 3.14 Example 3: Duplicate declaration (error)
int x = 5;
int x = 10;
Output:
Declaración: x (int) = 5
Error Semántico: Variable 'x' ya declarada.

3. visitInstrAsig - Variable Assignment

From Visitor.py:33-42:
def visitInstrAsig(self, ctx):
    nombre = ctx.asignacion().ID().getText()
    valor = self.visit(ctx.asignacion().expr())
    
    if nombre in self.tabla_simbolos:
        self.tabla_simbolos[nombre].valor = valor
        print(f"Asignación: {nombre} = {valor}")
    else:
        print(f"Error: Variable '{nombre}' no declarada.")
    return valor
Functionality:
  • Extracts variable name and evaluates expression
  • Checks if variable exists in symbol table
  • Updates variable value or reports error
  • Returns the assigned value
Example 1: Valid assignment
int x = 5;
x = 10;
Output:
Declaración: x (int) = 5
Asignación: x = 10
Example 2: Undeclared variable (error)
y = 20;
Output: Error: Variable 'y' no declarada.

4. visitAritmetica - Arithmetic Operations

From Visitor.py:44-53:
def visitAritmetica(self, ctx):
    izq = self.visit(ctx.expr(0))  # Left operand
    der = self.visit(ctx.expr(1))  # Right operand
    op = ctx.getChild(1).getSymbol().type  # Operator token type
    
    if op == ExpresionesParser.SUMA: return izq + der
    if op == ExpresionesParser.RESTA: return izq - der
    if op == ExpresionesParser.MULT: return izq * der
    if op == ExpresionesParser.DIV: return izq // der if der != 0 else 0
    return 0
Functionality:
  • Recursively evaluates left and right expressions
  • Determines operator type from middle child
  • Performs arithmetic operation based on operator
  • Handles division by zero (returns 0)
Division uses integer division (//). For floating-point division, modify to use /.
Example:
int result = (10 + 5) * 2;
Evaluation:
  1. visitAritmetica for +: 10 + 5 = 15
  2. visitAritmetica for *: 15 * 2 = 30
  3. Result: 30

5. visitRelacional - Relational Comparisons

From Visitor.py:55-66:
def visitRelacional(self, ctx):
    izq = self.visit(ctx.expr(0))
    der = self.visit(ctx.expr(1))
    op = ctx.op.type  # Operator stored in ctx.op field
    
    if op == ExpresionesParser.MAYOR: return izq > der
    if op == ExpresionesParser.MENOR: return izq < der
    if op == ExpresionesParser.IGUAL: return izq == der
    if op == ExpresionesParser.DIFERENTE: return izq != der
    if op == ExpresionesParser.MAYOR_IGUAL: return izq >= der
    if op == ExpresionesParser.MENOR_IGUAL: return izq <= der
    return False
Functionality:
  • Evaluates both expressions
  • Retrieves operator from ctx.op field (set by parser)
  • Performs comparison and returns boolean result
Example:
if (x > 5) { ... }
If x = 10, returns True

6. visitLogica - Logical Operations

From Visitor.py:68-74:
def visitLogica(self, ctx):
    izq = self.visit(ctx.condicion(0))
    der = self.visit(ctx.condicion(1))
    op = ctx.getChild(1).getSymbol().type
    if op == ExpresionesParser.Y_LOGICO: return bool(izq and der)
    if op == ExpresionesParser.O_LOGICO: return bool(izq or der)
    return False
Functionality:
  • Evaluates both condition expressions
  • Performs logical AND or OR
  • Converts result to boolean
Example:
if (x > 5 && y < 10) { ... }
Returns True only if both conditions are true

7. visitNotLogica - Logical Negation

From Visitor.py:76-77:
def visitNotLogica(self, ctx):
    return not self.visit(ctx.condicion())
Functionality:
  • Evaluates condition
  • Returns boolean negation
Example:
if (!(x > 5)) { ... }
Returns True if x <= 5

8. visitNumero - Number Literals

From Visitor.py:79-81:
def visitNumero(self, ctx):
    val = ctx.NUMERO().getText()
    return float(val) if '.' in val else int(val)
Functionality:
  • Extracts number text from token
  • Returns float if contains decimal point, otherwise int
Examples:
  • 42int(42)
  • 3.14float(3.14)

9. visitVariable - Variable Reference

From Visitor.py:83-88:
def visitVariable(self, ctx):
    nombre = ctx.ID().getText()
    if nombre in self.tabla_simbolos:
        val = self.tabla_simbolos[nombre].valor
        return val if val is not None else 0
    return 0
Functionality:
  • Looks up variable in symbol table
  • Returns stored value or 0 if uninitialized/undeclared
Returning 0 for undeclared variables masks errors. Consider raising an exception instead.

10. visitInstrIf - Conditional Statements

From Visitor.py:90-95:
def visitInstrIf(self, ctx):
    if self.visit(ctx.condicion()):
        return self.visit(ctx.bloque(0))  # Then block
    elif ctx.bloque(1):
        return self.visit(ctx.bloque(1))  # Else block
    return None
Functionality:
  • Evaluates condition
  • Executes then-block if true
  • Executes else-block if false (when present)
Example:
if (x > 10) {
    y = x * 2;
} else {
    y = x / 2;
}

11. visitParentesisExpr - Parenthesized Expression

From Visitor.py:97:
def visitParentesisExpr(self, ctx): 
    return self.visit(ctx.expr())
Functionality:
  • Simply evaluates the wrapped expression
  • Parentheses only affect parsing precedence, not evaluation

12. visitParentesisCond - Parenthesized Condition

From Visitor.py:98:
def visitParentesisCond(self, ctx): 
    return self.visit(ctx.condicion())
Functionality:
  • Evaluates the wrapped condition

Visitor Pattern Flow

1

Parser Creates AST

The parser builds a tree of context objects (ProgContext, InstrDeclContext, etc.)
2

Visitor Initialization

Create a Visitor instance with empty symbol table
3

Start Traversal

Call visitor.visit(tree) with the root context
4

Visit Method Dispatch

ANTLR automatically calls the appropriate visit method based on context type
5

Recursive Traversal

Each visit method calls visit() on child contexts as needed
6

Result Propagation

Return values bubble up from leaf nodes (literals, variables) to root

Complete Example

Source Code:
program {
    int x = 10;
    int y = 5;
    int result = x + y * 2;
}
Visitor Execution Trace:
  1. visitProg() - Prints “Iniciando ejecución…”
  2. visitInstrDecl() - Declaration: x (int) = 10
  3. visitInstrDecl() - Declaration: y (int) = 5
  4. visitInstrDecl() - Evaluates x + y * 2:
    • visitVariable('x')10
    • visitVariable('y')5
    • visitNumero('2')2
    • visitAritmetica('*')5 * 2 = 10
    • visitAritmetica('+')10 + 10 = 20
    • Declaration: result (int) = 20
Output:
Iniciando ejecución...
Declaración: x (int) = 10
Declaración: y (int) = 5
Declaración: result (int) = 20

Extending the Visitor

To add new functionality:
1

Identify Grammar Rule

Determine which grammar rule(s) need new behavior
2

Override Visit Method

Override the corresponding visit method in the Visitor class
3

Access Context Data

Use context methods to access tokens and child nodes
4

Implement Logic

Add semantic analysis, code generation, or interpretation logic
5

Return Value

Return appropriate value for parent visit methods
def visitInstrAsig(self, ctx):
    nombre = ctx.asignacion().ID().getText()
    valor = self.visit(ctx.asignacion().expr())
    
    if nombre in self.tabla_simbolos:
        simbolo = self.tabla_simbolos[nombre]
        
        # Type checking
        if simbolo.tipo == 'int' and not isinstance(valor, int):
            print(f"Error de Tipo: Se esperaba 'int', se obtuvo '{type(valor).__name__}'")
            return None
        elif simbolo.tipo == 'float' and not isinstance(valor, (int, float)):
            print(f"Error de Tipo: Se esperaba 'float', se obtuvo '{type(valor).__name__}'")
            return None
        
        simbolo.valor = valor
        print(f"Asignación: {nombre} = {valor}")
    else:
        print(f"Error: Variable '{nombre}' no declarada.")
    return valor
See also:
  • Symbol Table - Variable storage and management
  • Parser - Context classes created by the parser

Build docs developers (and LLMs) love