Etapa 4: Gramática e Gerador ANTLR4
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óProgramda 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.g4completo. - Classe
MyVisitorimplementando 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.