Página nueva

Te ayudo a generar el código en C++ para tu gramática de ABB y a entender la diferencia entre Listener y Visitor.


1. Generación del Código C++ con ANTLR


Primero, descarga los archivos abbLexer.g4 y abbParser.g4 en el mismo directorio. Luego, ejecuta este comando en la terminal para generar el código C++ :


```bash

antlr4 -Dlanguage=Cpp -visitor -listener abbLexer.g4 abbParser.g4

```


Esto creará una serie de archivos .h y .cpp. Los más importantes son:


· abbLexer.h/cpp: El Analizador Léxico.

· abbParser.h/cpp: El Analizador Sintáctico.

· abbBaseListener.h/cpp y abbBaseVisitor.h/cpp: Clases base para tu propio código.


2. Crear un main.cpp para probar un programa


Para probarlo, necesitas un archivo main.cpp que lea un archivo de entrada, lo procese y muestre el árbol sintáctico. Aquí tienes una plantilla que puedes usar :


```cpp

#include <iostream>

#include <fstream>


#include "antlr4-runtime.h"

#include "abbLexer.h"

#include "abbParser.h"


using namespace antlr4;


int main(int argc, const char* argv[]) {

    if (argc < 2) {

        std::cerr << "Uso: " << argv[0] << " <archivo_de_entrada>" << std::endl;

        return 1;

    }


    // 1. Crear el flujo de entrada desde el archivo

    std::ifstream stream;

    stream.open(argv[1]);

    if (!stream) {

        std::cerr << "Error: No se pudo abrir el archivo " << argv[1] << std::endl;

        return 1;

    }


    // 2. Crear el lexer y el parser

    ANTLRInputStream input(stream);

    abbLexer lexer(&input);

    CommonTokenStream tokens(&lexer);

    abbParser parser(&tokens);


    // 3. Obtener el token de errores e iniciar el parsing

    parser.removeErrorListeners();

    parser.addErrorListener(new ConsoleErrorListener());

    

    tree::ParseTree* tree = parser.module_(); // 'module_' es la regla principal en abbParser.g4


    // 4. Imprimir el árbol sintáctico en formato LISP

    std::cout << "Árbol Sintáctico: " << tree->toStringTree(&parser) << std::endl;


    return 0;

}

```


3. Para qué sirven Listener y Visitor


Ambos son patrones de diseño para recorrer el árbol sintáctico y ejecutar tu propia lógica (como calcular valores, comprobar errores o traducir el código), pero su filosofía es distinta .


Cuándo usar cada uno:


· Visitor (Visitante): Te da el control total. Debes llamar explícitamente a visit() para recorrer los nodos hijos. Es ideal si necesitas retornar valores de los nodos (ej. al evaluar una expresión matemática) y quieres controlar el orden de la visita .

· Listener (Oyente): Es automático. El ParseTreeWalker de ANTLR recorre el árbol y va llamando a los métodos enterRegla y exitRegla . Es muy útil cuando necesitas ejecutar acciones antes y después de procesar un bloque, como manejar alcances de variables (entrar/salir de una función) o transformar el formato de salida.


Resumen práctico:


· Si estás creando un intérprete que calcula valores, te conviene Visitor.

· Si estás haciendo un traductor o analizador de alcances, el Listener puede ser más sencillo.


Importante: Para compilar, necesitarás la librería de runtime de ANTLR para C++. Si usas CMake, te recomiendo el script de este repositorio de ejemplo, que descarga y compila el runtime por ti automáticamente .


Espero que esto te aclare los pasos. Si tienes una duda más concreta sobre qué patrón usar para tu proyecto, puedes decírmelo

 

Por supuesto. Aquí tienes una versión más divulgativa y estructurada, lista para ser usada como la primera parte de un artículo de blog sobre ANTLR y C++.


---


De la Gramática al Código: Tu Primer Compilador con ANTLR y C++


Si alguna vez te has enfrentado a la tarea de leer un archivo de configuración extraño, un lenguaje de juguete o un formato de datos propietario, sabrás lo tedioso que es escribir un analizador (parser) a mano contando paréntesis y comas. Ahí es donde brilla ANTLR (ANother Tool for Language Recognition).


En esta primera entrada, vamos a tomar una gramática real de un robot industrial (ABB RAPID) y la vamos a convertir en un programa funcional en C++ que lea y valide archivos .sys. Veremos cómo generar el esqueleto del compilador y, lo más importante, entenderemos para qué demonios sirven el Listener y el Visitor (las dos grandes superestrellas o grandes dolores de cabeza de ANTLR).


1. Paso 1: Las Materias Primas (Lexer y Parser)


Partimos de dos archivos que definen el lenguaje:


· abbLexer.g4: Define cómo se parten las palabras y símbolos (módulos, :=, PROC, números).

· abbParser.g4: Define la gramática, es decir, el orden lógico de esas palabras (un módulo contiene datos, procedimientos, etc.).


2. Paso 2: Invocando al Mago (Generación de Código C++)


El primer paso es casi mágico. Con los archivos .g4 en una carpeta, abrimos la terminal y lanzamos el hechizo de ANTLR para C++:


```bash

antlr4 -Dlanguage=Cpp -visitor -listener abbLexer.g4 abbParser.g4

```


¿Qué hace este comando?


· -Dlanguage=Cpp: Le dice a ANTLR que no queremos Java ni Python, queremos clases C++ de alto rendimiento.

· -visitor -listener: Le pedimos que nos prepare el terreno para ambas estrategias de recorrido del árbol (luego veremos la diferencia).


Tras ejecutar esto, la carpeta se llenará de archivos .h y .cpp. No te asustes, tú solo vas a tocar main.cpp.


3. Paso 3: El Punto de Entrada (main.cpp)


Necesitamos un programa que coja un archivo de texto, se lo pase a nuestro recién nacido analizador y nos diga si es válido o no. Aquí tienes el main.cpp mínimo viable para C++:


```cpp

#include <iostream>

#include <fstream>

#include "antlr4-runtime.h"

#include "abbLexer.h"

#include "abbParser.h"


using namespace antlr4;


int main(int argc, const char* argv[]) {

    // 1. Abrir el archivo de ejemplo

    std::ifstream stream(argv[1]);

    ANTLRInputStream input(stream);

    

    // 2. Fase Léxica: Romper el texto en Tokens

    abbLexer lexer(&input);

    CommonTokenStream tokens(&lexer);

    

    // 3. Fase Sintáctica: Construir el Árbol

    abbParser parser(&tokens);

    tree::ParseTree* tree = parser.module_(); // 'module_' es la regla raíz


    // 4. Imprimir el resultado

    std::cout << "Árbol generado: " << tree->toStringTree(&parser) << std::endl;

    

    return 0;

}

```


Si compilas este código (enlazando con la librería de runtime de ANTLR) y le pasas un archivo .sys válido, verás en consola un montón de paréntesis representando la estructura jerárquica del código. ¡Ya tienes un reconocedor sintáctico!


4. El Dilema: ¿Listener o Visitor?


Aquí viene la parte filosófica. Ya sabemos leer el archivo, pero ahora queremos hacer algo con esa información (traducirlo a otro lenguaje, calcular posiciones del robot, pintar un diagrama...). ANTLR nos da dos herramientas para recorrer ese árbol que acabas de imprimir. Elegir bien te ahorrará muchas horas de depuración.


Característica Listener (El Piloto Automático) Visitor (El Conductor Manual)

Recorrido Automático. Un "Andador" (ParseTreeWalker) recorre cada nodo en profundidad. Manual. Tú escribes visit() explícitamente para navegar a los hijos que quieras.

Control de Flujo No puedes pararlo. Siempre visita todos los nodos. Tú decides si visitas el hijo izquierdo, el derecho o ninguno.

Retorno de Datos No devuelves valores directamente (normalmente usas variables externas o una pila). Sí devuelve valores. Ideal para expresiones matemáticas (visitSuma devuelve un int).

Caso de Uso Ideal Traducción de formato (ej. pasar de ABB a Python) o Análisis de alcance (entrar/salir de una función). Evaluación/Interpretación directa o Análisis Semántico que requiere calcular tipos complejos.


En resumen para tu blog:


· Si tu objetivo es imprimir el código en otro lenguaje o contar cuántas veces aparece la palabra PROC, usa Listener.

· Si tu objetivo es ejecutar el programa del robot en un simulador virtual (hacer que el robot se mueva según las coordenadas del archivo), usa Visitor.


5. Próximos Pasos


En la siguiente parte de esta serie, implementaremos un Visitor personalizado para extraer todas las declaraciones de variables TOOLDATA del archivo del robot y generar un archivo JSON con la configuración. ¡Nos vemos en el código!


Revision #2
Created 15 April 2026 21:50:53 by Rafael Carbonell Lázaro
Updated 15 April 2026 21:58:10 by Rafael Carbonell Lázaro