O que você vai aprender
Explicar o parsing recursivo-descendente e o método de um procedimento por não-terminal.
Diagnosticar e eliminar recursão à esquerda.
Aplicar fatoração à esquerda.
Reconhecer essas transformações como pré-requisitos de LL(1).
Reconstruir a árvore
A gramática gera cadeias. Mas o compilador recebe a cadeia (os tokens) e precisa do caminho inverso: reconstruir a árvore sintática. Esse é o trabalho do parser.
Há duas estratégias gerais: descendente (de cima para baixo) e ascendente (de baixo para cima). Esta aula foca na descendente, intuitiva e fácil de programar à mão.
O que veremos nesta aula
- A estratégia descendente.
- Um procedimento por não-terminal.
- Por que a recursão à esquerda quebra tudo e como removê-la.
- A fatoração à esquerda.
Análise descendente (top-down)
"Descendente" porque parte do topo da árvore e desce, prevendo a estrutura antes de ver todos os tokens.
Um procedimento por não-terminal
No recursivo-descendente, cada não-terminal vira uma função. A função tenta reconhecer a parte da entrada que aquele não-terminal gera, chamando as funções dos outros não-terminais conforme a produção.
Reconhecendo "id + id"
Acompanhe as chamadas para a entrada id + id, com a gramática E → T E', E' → + T E' | ε:
| Chamada | Token atual | Ação |
|---|---|---|
| E → T E' | id | chama T |
| T → id | id | consome id |
| E' → + T E' | + | consome +, chama T |
| T → id | id | consome id |
| E' → ε | fim | retorna |
Passo a passo: a pilha de chamadas
Recursão à esquerda
Por que ela quebra o parser
A solução é reescrever a gramática, transformando a recursão à esquerda em recursão à direita:
| Com recursão à esquerda | Reescrita (à direita) |
|---|---|
| E → E + T | T | E → T E' E' → + T E' | ε |
A porta giratória infinita
Descendente × ascendente
| Descendente (top-down) | Ascendente (bottom-up) | |
|---|---|---|
| Constrói a árvore | Da raiz às folhas | Das folhas à raiz |
| Decisão | Qual produção aplicar | Quando reduzir |
| Recursão à esquerda | Proíbe | Aceita |
| Exemplo | LL(1), recursivo-descendente | LR(1), LALR |
Preparando a gramática
Antes de escrever um parser descendente, a gramática passa por duas transformações de saneamento:
Fatoração à esquerda
Quando duas produções de um não-terminal começam igual, o parser não sabe, ao ver o prefixo comum, qual escolher. A fatoração à esquerda adia a decisão até depois do prefixo:
| Antes | Depois |
|---|---|
| S → if E then S | if E then S else S | S → if E then S S' S' → else S | ε |
Agora há uma só produção começando com "if E then S", e a escolha do else fica isolada em S'.
Verifique você mesmo: precisa fatorar?
A produção A → a b | a c precisa de fatoração à esquerda?
Parsers escritos à mão
Muitos compiladores reais (incluindo versões de GCC e Clang) usam parsers recursivo-descendentes escritos à mão.
Armadilhas do parser descendente
Ordem das transformações
Reflita: descendente x ascendente
Se o parser descendente proíbe recursão à esquerda, por que ela é tão comum nas gramáticas?
Termos-chave da aula
Onde isto se encaixa no curso
Esta aula é o elo entre gramática e algoritmo:
- Vem da aula 5: usa a gramática de expressões e a noção de árvore.
- Prepara a aula 7: eliminar recursão e fatorar são pré-condições para LL(1) e a tabela preditiva.
- Conecta com a aula 8: as funções do parser serão o lugar das ações semânticas (tradução dirigida pela sintaxe).
Resumo visual
Atividade em grupo · Refatorando gramáticas
Em trios, deixem uma gramática pronta para parsing descendente.
Roteiro
- Dada A → A α | β, eliminem a recursão à esquerda.
- Dada B → x y | x z, façam a fatoração à esquerda.
- Escrevam o procedimento recursivo-descendente para a gramática resultante.
- Testem o procedimento com uma entrada de exemplo.
Mini-quiz · Aula 6
20 questões sobre esta aula. Escolha e veja a explicação na hora.
📌 Resumo — leve isto para a prova
- Parser descendente constrói a árvore da raiz às folhas; recursivo-descendente usa uma função por não-terminal.
- Recursão à esquerda (direta ou indireta) causa laço infinito e precisa ser eliminada.
- Fatoração à esquerda resolve prefixos comuns, adiando a decisão.
- Elimine a recursão à esquerda antes de fatorar.
- Ambas são pré-requisitos para LL(1).