O que você vai aprender
Estruturar uma tabela de símbolos e descrever suas operações.
Modelar escopos aninhados como uma pilha de tabelas.
Explicar a verificação de tipos e os conceitos de coerção e sombreamento.
Relacionar escopo léxico ao aninhamento de blocos no código.
Quem é "x"?
Quando o compilador lê x = x + 1, precisa saber: qual x? De que tipo? Onde mora na memória? Essas respostas vivem na tabela de símbolos — a memória de longo prazo do compilador — e a checagem de compatibilidade vive no sistema de tipos.
Sem essas duas estruturas, o compilador seria incapaz de distinguir um nome válido de um erro, ou uma operação legítima de uma absurda como somar um número a uma função.
Estruturas transversais
A tabela de símbolos não pertence a uma única fase: ela atravessa quase todas.
A análise semântica é onde ela mais trabalha: preenche-a nas declarações, consulta-a nos usos e usa-a para verificar tipos. A geração de código volta a consultá-la para saber posições de memória.
O que é a tabela de símbolos
Implementada quase sempre como tabela hash, por oferecer inserção e busca por nome em tempo praticamente constante — operações executadas milhares de vezes durante a compilação.
As operações essenciais
Toda tabela de símbolos oferece, no mínimo:
inserir(nome, info)— registra uma declaração nova no escopo atual.buscar(nome)— localiza a declaração visível mais próxima, percorrendo escopos de dentro para fora.abrirEscopo()/fecharEscopo()— empilha e desempilha escopos ao entrar e sair de blocos.
Escopos no código
O código a seguir mostra três escopos e um nome reutilizado:
Há três x possíveis em jogo? Não: dentro de f, todo uso de x liga-se ao local; o global fica oculto (sombreado).
Resolução de nome passo a passo
Escopo léxico e a pilha de tabelas
Escopo léxico (estático). A visibilidade é determinada pela estrutura textual do código (aninhamento de blocos), não pela ordem de execução.
Implementa-se como uma pilha de tabelas: ao entrar num bloco, empilha-se um novo escopo; ao sair, desempilha-se. A busca varre a pilha do topo (interno) para a base (global).
Por que a busca vai de dentro para fora
A regra do escopo mais interno diz que, havendo vários nomes iguais visíveis, vence o declarado mais perto do uso. Isso reflete a intuição do programador: a variável "mais local" é a que ele quis usar.
A tabela como agenda telefônica
Escopo estático × dinâmico
| Escopo estático (léxico) | Escopo dinâmico | |
|---|---|---|
| Definido por | Estrutura textual do código | Ordem das chamadas em execução |
| Resolvido | Em tempo de compilação | Em tempo de execução |
| Previsibilidade | Alta (lendo o código) | Baixa (depende do caminho) |
| Usado por | C, Java, Python, a maioria | Lisp antigo, alguns shells |
A esmagadora maioria das linguagens modernas usa escopo estático — daí o foco na pilha de tabelas resolvida na compilação.
O ciclo de vida de um escopo
abrirEscopo→Declara nomes
inserir→Usa nomes
buscar→Sai do bloco
fecharEscopo
Ao fechar o escopo, suas declarações deixam de ser visíveis — é assim que y "desaparece" ao sair do bloco interno do exemplo.
O sistema de tipos
O verificador de tipos percorre a árvore, sintetizando o tipo de cada subexpressão (um atributo, como na aula 8!) e comparando-o com o tipo esperado pelo contexto. Tipos incompatíveis geram um erro de tipo.
- Tipagem estática: tipos verificados na compilação (C, Java).
- Tipagem dinâmica: tipos verificados na execução (Python, JavaScript).
Verifique seu entendimento
Com escopos aninhados, em que ordem buscar(nome) procura a declaração?
Caçando um erro de tipo
Considere o trecho com tipagem estática:
O verificador sintetiza tipo(n)=int e tipo(s)=ponteiro. A regra de + não admite essa combinação, então emite um erro de tipo — antes de o programa rodar e quebrar.
Onde o compilador (e o aluno) tropeça
Confundir sombreamento com erro. Reusar um nome em escopo interno é legal (sombreia); não é redeclaração ilegal.
Coerção silenciosa perigosa. Conversões implícitas (int→float, int→char) podem mascarar bugs. Nem todo "funciona" é "correto".
Buscar de fora para dentro. Inverter o sentido da busca leva a ligar usos às declarações erradas.
Boas práticas de projeto
Modele escopos como pilha: abrir/fechar são apenas push/pop, e a busca é uma varredura natural.
Guarde na ficha tudo que fases posteriores vão precisar: tipo, posição de memória, assinatura.
Trate coerção explicitamente: defina onde ela é permitida em vez de deixá-la implícita e imprevisível.
Pense antes de revelar
No exemplo "int x; void f(){ int x; { int y; } }", quantos identificadores distintos existem e quais escopos são fechados?
Revise os termos
Como esta aula se liga ao curso
- Vem da aula 8 (atributos): o tipo de cada expressão é um atributo sintetizado;
inseriré uma ação semântica. - Usa a aula 2 (Chomsky): "declarar antes de usar" e checagem de tipos são sensíveis ao contexto, fora da GLC.
- Prepara a aula 11 (execução): a posição de memória guardada na ficha vira o deslocamento no registro de ativação.
- Prepara a aula 12 (código): a geração consulta a tabela para localizar variáveis.
O essencial da aula
Atividade em grupo · Detetives de escopo
Em duplas, resolvam a quem cada uso de variável se refere.
Roteiro
- Recebam um trecho com blocos aninhados e variáveis de mesmo nome.
- Para cada uso, indiquem qual declaração ele referencia (qual escopo).
- Apontem possíveis erros de tipo no mesmo trecho.
- Justifiquem usando a regra do escopo mais interno.
Mini-quiz · Aula 9
20 questões sobre esta aula. Escolha e veja a explicação na hora.
📌 Resumo — leve isto para a prova
- A tabela de símbolos (hash) guarda nome → (tipo, escopo, posição, atributos).
- Escopos aninhados formam uma pilha; a busca vai do interno ao externo.
- Sombreamento é resolvido pela regra do escopo mais interno.
- A verificação de tipos sintetiza tipos e barra operações incompatíveis.
- Escopo léxico é resolvido na compilação pela estrutura textual do código.