Skip to main content

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