Revisão e Integração: Etapas 1 a 4
Visão Geral do Processo (Front-End do Compilador)
Da Etapa 1 à Etapa 4, o projeto prático guiou a construção de todo o Front-End do compilador para a linguagem Mini-Pascal. O objetivo principal do front-end é garantir que o código fonte obedeça à estrutura léxica e sintática da linguagem, transformando um fluxo de texto bruto em uma estrutura de dados processável (a AST).
Esta revisão relaciona o que foi exigido em cada etapa prática com os fundamentos teóricos vistos em sala de aula.
Etapa 1: Infraestrutura e AST
O que foi pedido: - Implementação de classes seguindo o padrão de projeto Composite (AstNode, Expression, Command). - Criação de uma Tabela de Símbolos inicial. - Capacidade de imprimir a árvore com indentação (printTree()).
Relação com o Conteúdo Teórico: - Abstract Syntax Tree (AST): Na teoria, vimos que a árvore de derivação (Parse Tree) gerada pela gramática contém muitos detalhes desnecessários para as próximas fases (ex: parênteses, ponto-e-vírgula). A AST, exigida nesta etapa, é a materialização do conceito teórico de “árvore abstrata”, onde mantemos apenas a essência das operações lógicas e semânticas. - Tabela de Símbolos: É a estrutura de dados central do compilador que armazena os atributos (tipo, escopo, valor) de cada identificador. O compilador a construirá e consultará constantemente nas etapas de semântica e geração de código (Etapas 5 a 7).
Etapa 2: Analisador Léxico Manual
O que foi pedido: - Leitura do arquivo texto caractere por caractere. - Ignorar espaços em branco e comentários. - Geração de tokens válidos (TokenType e valor textual) e lançamento de erros léxicos com indicação precisa de linha e coluna.
Relação com o Conteúdo Teórico: - Autômatos Finitos Determinísticos (AFD) e Expressões Regulares: O scanner codificado manualmente (método nextToken()) é a simulação em código (utilizando estruturas condicionais e laços de repetição) do comportamento de um Autômato Finito que transita entre estados lendo os caracteres de entrada. - Lexemas vs Tokens: A distinção teórica torna-se prática e visível no código. A sequência de caracteres identificada, como "123" (lexema), é avaliada e empacotada em uma estrutura de dados [NUMBER, 123] (token), que será a unidade atômica processada pelo analisador sintático.
Etapa 3: Analisador Sintático LL Manual
O que foi pedido: - Implementação de um Parser Descendente Recursivo (Recursive Descent). - Tratamento explícito da precedência de operadores matemáticos via estratificação de chamadas em diferentes níveis. - Tratamento e recuperação de erros sintáticos usando Panic Mode.
Relação com o Conteúdo Teórico: - Gramáticas Livres de Contexto (GLC) e Parsers LL(1): Cada não-terminal da gramática matemática se transforma diretamente em uma função no código (ex: parseExpression(), parseTerm()). O uso do método match(TokenType) lendo o token atual demonstra empiricamente o uso de lookahead (tamanho 1) para a tomada de decisão no caminho da descida na árvore sintática. - Precedência e Associatividade: Para remover a ambiguidade inata da gramática em expressões, estratificamos as regras de produção na teoria. Na prática, codificamos métodos encadeados (onde parseExpression invoca parseTerm, que por sua vez invoca parseFactor) para forçar que operadores de maior precedência, como multiplicações, sejam processados nos níveis mais baixos da árvore. - Panic Mode: Colocamos em prática o conceito teórico de recuperação de erros por sincronização, descartando os tokens ofensores até encontrar um “ponto seguro” (como o ;), permitindo que a compilação prossiga para detectar mais erros no restante do arquivo.
Etapa 4: Gramática e Gerador ANTLR4
O que foi pedido: - Escrever uma especificação formal de sintaxe e léxico utilizando a notação .g4. - Gerar as classes do Lexer e do Parser automaticamente pela ferramenta. - Empregar o padrão de projeto Visitor para varrer a árvore concreta (Parse Tree) construída pelo ANTLR e instanciar nossos próprios nós customizados da AST (criados na Etapa 1).
Relação com o Conteúdo Teórico: - Geradores de Parsers (Yacc / Bison / ANTLR): Enquanto estudamos algoritmos poderosos e complexos para construção de tabelas (como os autômatos de itens LALR ou LL(*)), vimos que é custoso implementá-los à mão. A Etapa 4 é a vivência do padrão de mercado: terceirizar a complexidade da autômata para ferramentas geradoras validadas. - Parse Tree vs AST: A conversão obrigatória usando a classe Visitor deixa evidente, em código funcional, a diferença entre a árvore concreta gigantesca e literal, que contém as regras de produção gramatical exigidas pelo parser, e a árvore enxuta (AST) livre de ambiguidades sintáticas que o backend de nosso compilador realmente requer para entender a lógica. - Engenharia de Compiladores: Destaca que a escrita formal de uma linguagem de programação via uma especificação de gramática BNF estendida (no caso do ANTLR4) é uma abordagem de software infinitamente mais segura, limpa e escalável do que os parsers codificados manualmente (como visto nas Etapas 2 e 3).