Etapa 4: Gramática e Gerador ANTLR4

Published

17/03/2026

Modified

17/03/2026

Objetivos

  • Substituir o parser manual por um gerador de parsers industrial (ANTLR4).
  • Aprender a linguagem de descrição de gramáticas .g4.
  • Utilizar o padrão Visitor para converter a árvore concreta do ANTLR (ParseTree) na sua AST.

Fundamentação Teórica

Ferramentas como ANTLR geram automaticamente o código do Lexer e Parser a partir de uma especificação formal. Isso reduz erros e facilita a manutenção. No entanto, o ANTLR gera uma Parse Tree (Árvore Concreta), que contém todos os tokens (vírgulas, parênteses). Nosso objetivo é transformar isso na nossa AST (mais limpa) que definimos na Etapa 1.

Atividades Práticas

1. Preparação

  • Instale o plugin do ANTLR4 na sua IDE.
  • Adicione a dependência do ANTLR4 Runtime no pom.xml.
<dependency>
    <groupId>org.antlr</groupId>
    <artifactId>antlr4-runtime</artifactId>
    <version>4.13.1</version>
</dependency>

2. Definindo a Gramática (MiniPascal.g4)

Crie o arquivo na pasta src/main/antlr4/br/com/comcet/tp4/parser. Exemplo de estrutura:

grammar MiniPascal;

// Parser Rules
program: 'program' ID ';' varDecl? block '.' EOF;

varDecl: 'var' (ID (',' ID)* ':' type ';')+;

type: 'integer' | 'boolean' | 'string';

// ... outras regras (command, expression, etc) ...

// Lexer Rules
ID: [a-zA-Z][a-zA-Z0-9]*;
NUMBER: [0-9]+;
WS: [ \t\r\n]+ -> skip;

3. Gerando o Código

Execute o comando Maven (ou use o plugin da IDE) para gerar as classes Java (MiniPascalLexer, MiniPascalParser, MiniPascalBaseVisitor, etc).

4. Implementando o Visitor

Crie uma classe MyVisitor que estende MiniPascalBaseVisitor<AstNode>. Você deve sobrescrever os métodos visit para construir seus nós.

Exemplo:

public class MyVisitor extends MiniPascalBaseVisitor<AstNode> {
    
    @Override
    public AstNode visitAssignment(MiniPascalParser.AssignmentContext ctx) {
        // ctx.ID() retorna o token do identificador
        Identifier id = new Identifier(ctx.ID().getText());
        // ctx.expression() retorna o contexto da expressão filho
        Expression expr = (Expression) visit(ctx.expression());
        
        return new AssignmentCommand(id, expr);
    }
    
    @Override
    public AstNode visitBinaryExpr(MiniPascalParser.BinaryExprContext ctx) {
        Expression left = (Expression) visit(ctx.expression(0));
        Expression right = (Expression) visit(ctx.expression(1));
        String op = ctx.op.getText();
        return new BinaryExpression(left, right, op);
    }
}

5. Testes Automatizados (JUnit) — públicos

Além do teste manual (executar o compilador e imprimir a AST), crie ao menos um teste JUnit público para validar o pipeline:

ANTLR ParseTree → Visitor → AST

Crie, por exemplo, src/test/java/br/com/comcet/tp4/AntlrVisitorTest.java:

package br.com.comcet.tp4;

import br.com.comcet.tp1.ast.*;
import br.com.comcet.tp4.parser.MiniPascalLexer;
import br.com.comcet.tp4.parser.MiniPascalParser;
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.ParseTree;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

public class AntlrVisitorTest {

    @Test
    void visitorConstroiAstParaAtribuicaoComPrecedencia() {
        // Esperado: x := 10 + (5 * 2);
        String codigo = "program p; var x: integer; begin x := 10 + 5 * 2; end.";

        CharStream input = CharStreams.fromString(codigo);
        MiniPascalLexer lexer = new MiniPascalLexer(input);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        MiniPascalParser parser = new MiniPascalParser(tokens);

        ParseTree tree = parser.program();
        MyVisitor visitor = new MyVisitor();
        AstNode ast = visitor.visit(tree);

        assertNotNull(ast);
        assertTrue(ast instanceof Program);

        // Opcional: se você tiver um método para acessar comandos do programa,
        // valide que existe um AssignmentCommand cuja expressão é um BinaryExpression("+")
        // com um BinaryExpression("*") no lado direito.
    }
}

Observação: este teste assume que: - sua gramática define a regra de entrada program; - MyVisitor.visit(program) retorna um nó Program da sua AST; - sua AST usa os nós da Etapa 1 e você consegue (opcionalmente) navegar para inspecionar comandos/expressões.

O que entregar

  • Arquivo MiniPascal.g4 completo.
  • Classe MyVisitor implementando a conversão.
  • Atualize seu programa principal para usar o ANTLR Lexer/Parser, passar pelo Visitor e imprimir a AST. A saída deve ser idêntica (ou superior) à da Etapa 3.
  • Pelo menos um teste JUnit público cobrindo o pipeline ANTLR → Visitor → AST.
Back to top