Aula 18 - Análise Sintática e Árvores de Sintaxe Abstrata (AST) 🌳
Objetivo Pedagógico
Objetivo: Construção de Analisadores Sintáticos (Parsers), Gramáticas Livres de Contexto (EBNF), algoritmo Recursive Descent Parser e geração de Árvores de Sintaxe Abstrata (AST).
📑 1. Fundamentos Teóricos & Análise Técnica
A segunda fase da esteira de compilação é a Análise Sintática (Parsing). Enquanto o analisador léxico opera sobre expressões regulares e caracteres isolados, o parser opera sobre Gramáticas Livres de Contexto (Context-Free Grammars - CFG) descritas formalmente na notação EBNF (Extended Backus-Naur Form).
O objetivo do Parser é verificar se a sequência linear de tokens obedece às regras estruturais da linguagem e construir uma representação hierárquica em árvore em memória: a Árvore de Sintaxe Abstrata (AST - Abstract Syntax Tree).
Diferente da Parse Tree concreta (que armazena detalhes triviais como parênteses ou pontos-e-vírgulas), a AST retém estritamente os nós semânticos de operação: - Nós de Expressão Binária (BinaryOp: +) - Nós Literais (Literal: 42) - Nós de Declaração de Variável (VarDeclaration)
O algoritmo mais pedagógico e extensivamente adotado em compiladores industriais modernos (como GCC, Clang e Rustc) é o Parser Descendente Recursivo (Recursive Descent Parser) com tratamento de precedência de operadores via Pratt Parsing.
📐 Arquitetura Conceitual & Diagrama de Fluxo
graph TD
Tokens["Tokens: 'let total = x + 10;'"] --> Parser["Recursive Descent Parser (Gramática EBNF)"]
Parser --> AST["Árvore de Sintaxe Abstrata (AST)"]
AST --> VarDecl["Declaração: 'total'"]
VarDecl --> AddOp["Operação Binária (+)"]
AddOp --> VarX["Variável: 'x'"]
AddOp --> Const10["Constante: '10'"]
style Tokens fill:#e1f5fe,stroke:#01579b
style Parser fill:#fff3e0,stroke:#e65100
style AST fill:#e8f5e9,stroke:#2e7d32 🔍 Pilares e Diretrizes Técnicas
Nesta unidade, aprofundamos os seguintes conceitos fundamentais: - Precedência e Associatividade: Garantia de que a multiplicação * seja avaliada antes da adição + na hierarquia da árvore. - Recursão Mútua: Funções gramaticais que chamam umas às outras (parse_expression -> parse_term -> parse_factor). - Recuperação Graciosa de Erros (Panic Mode): Capacidade de avançar até o próximo ponto-e-vírgula após um erro sintático para continuar checando o arquivo. - Estruturas Polimórficas de AST: Cada nó da árvore representa uma construção formal do programa.
🛠️ 2. Implementação Prática em Compiladores e Gramáticas Livres de Contexto
Abaixo está a implementação técnica de referência, estruturada com padrões de engenharia de software e foco em robustez:
// ast_nodes.h (Estrutura de Nós da AST em C)
#include <stdlib.h>
typedef enum {
NODE_INT_LITERAL,
NODE_BINARY_OP,
NODE_VAR_REF
} NodeType;
typedef struct ASTNode {
NodeType type;
union {
int int_val;
char var_name[64];
struct {
char op;
struct ASTNode* left;
struct ASTNode* right;
} binary;
} data;
} ASTNode;
// Construtor de nó binário
ASTNode* create_binary_node(char op, ASTNode* left, ASTNode* right) {
ASTNode* node = (ASTNode*)malloc(sizeof(ASTNode));
node->type = NODE_BINARY_OP;
node->data.binary.op = op;
node->data.binary.left = left;
node->data.binary.right = right;
return node;
}
💡 Análise Passo a Passo do Código
- Union para Economia de Memória: Permite que o mesmo nó armazene tanto inteiros quanto nomes de variáveis ou ponteiros de subárvores.
- Ponteiros Recursivos:
lefterightapontam para subárvores arbitrariamente complexas na Heap. - Representação Canônica: A árvore encapsula a ordem matemática de execução dos nós sem ambiguidades.
🎯 3. Próximos Passos & Sequência Didática
-
Slides da Aula
-
Quiz de Fixação
-
Exercícios Práticos
-
Desafio de Projeto