LRM Prof. Mantovani ← Aulas da disciplina
Aula 5 de 14

Gramáticas livres de contexto

A linguagem da sintaxe: gramáticas livres de contexto (GLC), derivações, árvores sintáticas, o problema da ambiguidade e sua solução via precedência e associatividade, e as notações BNF e EBNF que descrevem a estrutura das linguagens de programação.

📚 Compiladores🧪 7 simulador(es)📝 mini-quiz ao final
Objetivos da aula

O que você vai aprender

1

Ler e escrever produções de uma GLC.

2

Construir derivações e árvores sintáticas.

3

Reconhecer e resolver ambiguidade com precedência e associatividade.

4

Traduzir entre BNF e EBNF.

1 · Motivação

Por que regex não basta

A análise léxica nos deu tokens. Mas como descrever que "( ( ) )" é válido e "( ) )" não é? Aninhamento arbitrário foge das expressões regulares — precisamos de uma ferramenta mais forte.

As gramáticas livres de contexto (tipo 2 de Chomsky) são essa ferramenta. Elas descrevem a estrutura sintática das linguagens de programação.

🔑
GLC são para a sintaxe o que as expressões regulares são para o léxico: a linguagem de especificação da fase.
🔍
E dá para ver isso acontecer, em vez de acreditar. Na bancada da hierarquia de Chomsky (aula 2), o autômato finito de contador estoura ao tentar casar parênteses aninhados — ele não tem onde guardar quantos abriu; a máquina de pilha, ao lado, resolve. E na bancada de derivação desta aula o preset “parênteses balanceados” (S → ( S ) S | ε) mostra a mesma linguagem sendo gerada pela GLC. É a mesma fronteira vista dos dois lados: o que falta ao regular é memória.
2 · Mapa

O que veremos nesta aula

GLC→Derivações e árvores→Ambiguidade→BNF/EBNF
  • A definição formal de uma GLC.
  • Como gerar cadeias e registrar sua estrutura.
  • O problema da ambiguidade e como resolvê-lo.
  • As notações padrão de gramáticas.
3 · Definição

Gramática livre de contexto

GLC. Quádrupla (N, T, P, S): não-terminais N, terminais T, produções P (da forma A → α) e símbolo inicial S.

Os não-terminais são variáveis sintáticas (como E para "expressão"); os terminais são os tokens; as produções dizem como reescrever cada não-terminal; S é o ponto de partida.

Guarde os quatro nomes. A bancada que transforma essa definição em quatro botões vem duas seções adiante: ela trabalha sobre a gramática de expressões e sobre a árvore sintática, que você ainda vai ver.

4 · Aprofundamento

A gramática de expressões

O exemplo clássico, que usaremos o curso inteiro, é o das expressões aritméticas:

E → E + T | T T → T * F | F F → ( E ) | id

Há três níveis: E (expressão, soma), T (termo, produto) e F (fator). Essa estratificação não é casual — ela codifica a precedência dos operadores, como veremos.

5 · Exemplo

Derivação e árvore sintática

Derivação. Sequência de aplicações de produções, partindo de S, até chegar a uma cadeia só de terminais.
Árvore sintática. Estrutura em árvore que registra como a cadeia foi derivada, com S na raiz e os terminais nas folhas.

Para "id + id", uma derivação é: E ⇒ E + T ⇒ T + T ⇒ F + T ⇒ id + T ⇒ id + F ⇒ id + id.

Agora que N, T, P e S têm um exemplo concreto e você já sabe o que é uma árvore, a bancada abaixo vira a definição do avesso. Tire um símbolo de T, desligue uma produção de P, mova o S de lugar — e a linguagem gerada e a árvore se refazem na hora. Depois renomeie os elementos de N e confira: não muda absolutamente nada. Três dos quatro componentes decidem a linguagem; o quarto é só o nome que você escolheu para as variáveis.

6 · Interativo

Passo a passo: derivando "id * id"

Passo 1
Início: E. Aplicamos E → T, obtendo T.
Passo 2
Aplicamos T → T * F, obtendo T * F.
Passo 3
Aplicamos T → F e depois F → id no primeiro fator: id * F.
Passo 4
Aplicamos F → id no segundo fator: id * id. Derivação concluída.

A bancada logo abaixo faz esse mesmo movimento com as suas mãos: clique num não-terminal, escolha a produção, e veja a forma sentencial e a árvore crescerem juntas. Experimente também os modos mais à esquerda e mais à direita — e repare no número que a bancada mostra no fim: são muitas ordens diferentes de derivação para uma única árvore. A ordem é escolha do analisador; a árvore é o que fica.

7 · Definição

Gramática ambígua

Ambiguidade. Uma gramática é ambígua quando uma mesma cadeia admite duas (ou mais) árvores sintáticas distintas — e, portanto, dois significados.

Ambiguidade é um defeito: o compilador não saberia qual interpretação adotar.

8 · Aprofundamento

O perigo da ambiguidade

⚠️
A entrada id + id * id, numa gramática mal escrita (E → E + E | E * E | id), poderia agrupar como (id+id)*id ou id+(id*id). São resultados numéricos diferentes! A ambiguidade muda o significado.

A gramática estratificada (E, T, F) elimina esse problema: ela só produz uma árvore para cada cadeia, com o agrupamento correto id+(id*id).

9 · Analogia

A frase ambígua

🔧 Analogia
"Vi o homem com o telescópio" é ambígua em português: quem tem o telescópio, eu ou o homem? Há duas "árvores" de interpretação. Numa linguagem de programação isso seria inaceitável — por isso projetamos gramáticas sem ambiguidade, em que cada frase tem uma única leitura.
10 · Comparação

BNF × EBNF

A EBNF acrescenta açúcares sintáticos à BNF, deixando as gramáticas mais compactas:

Em BNFEm EBNFSignificado
L → a L | aL → a { a }repetição
S → if E then S | if E then S else SS → if E then S [ else S ]opcional

Em EBNF: chaves { } repetem (zero ou mais), colchetes [ ] tornam opcional, parênteses ( ) agrupam.

Açúcar não é poder novo. Na bancada abaixo, escreva a gramática em EBNF e veja a tradução mecânica para BNF pura: cada { } vira um não-terminal recursivo, cada [ ] vira uma alternativa com ε. Em seguida a bancada enumera as duas — a sua EBNF e a BNF gerada — e confere que produzem exatamente as mesmas cadeias.

11 · Fluxo

Da gramática à árvore

A gramática gera; a árvore registra. O parser, que veremos na próxima aula, faz o caminho inverso: recebe a cadeia e reconstrói a árvore.

Gramática (GLC)→Derivação→Árvore sintática
12 · Aprofundamento

Precedência e associatividade

A estrutura da gramática codifica dois conceitos:

  • Precedência: operadores que ligam mais forte ficam em níveis mais profundos. Como * está abaixo de + (no nível T), ele agrupa primeiro.
  • Associatividade: a recursão à esquerda (E → E + T) torna o operador associativo à esquerda, então a - b - c é lido como (a - b) - c.
✅
Estratificar (expressão → termo → fator) resolve precedência; escolher recursão à esquerda ou à direita define a associatividade.

Na mesa abaixo, precedência e associatividade deixam de ser explicação e viram controle: mude o nível de um operador ou o lado da recursão e a bancada reescreve a gramática, refaz a árvore e recalcula o valor. Vale testar 8 - 3 - 2 (3 ou 7?) e 2 ^ 3 ^ 2 (512 ou 64?) — expressões que separam os dois casos.

13 · Simulador

Veja a ambiguidade acontecer

Ambiguidade é um conceito que só convence quando você vê. No simulador abaixo, a cadeia 2+3*4 é derivada por uma gramática ambígua de duas maneiras — e cada árvore avalia para um número diferente: 20 e 14.

Depois troque para a gramática corrigida e observe que sobra uma única árvore. A precedência deixou de ser combinação e passou a ser estrutura: os níveis E, T e F obrigam o * a ser reduzido antes do +.

⚠️
Uma gramática ambígua não é apenas deselegante: ela torna o significado do programa indefinido. Dois compiladores corretos poderiam gerar resultados diferentes para o mesmo código.

E há um segundo botão que vale a aula inteira: a varredura. Ela enumera a linguagem da gramática, analisa cada cadeia e devolve a menor que admite duas árvores — a testemunha. Guarde o que acontece quando a varredura não acha nada: isso não é prova de que a gramática é inambígua (voltamos a esse ponto na seção 18).

14 · Verifique

Verifique você mesmo: ambígua?

A gramática E → E + E | id é ambígua para a cadeia id + id + id?

Sem estratificação nem recursão definida, a soma pode agrupar à esquerda ou à direita, gerando duas árvores. A gramática é ambígua.
15 · Caso prático

O problema do else pendente

O clássico "dangling else": em if E then if E then S else S, a quem pertence o else? Ao if interno ou ao externo?

💡
A gramática ingênua de if-then-else é ambígua justamente por isso. A convenção universal (e a solução prática) é associar o else ao if mais próximo (o interno). Linguagens reais adotam essa regra ou exigem chaves.

A bancada abaixo não para nas duas árvores: ela mostra o que cada leitura faz quando a condição externa é falsa — numa, nada acontece; na outra, o else executa. Depois compare as duas saídas reais: a gramática estratificada em comando casado e não-casado, que resolve o problema na raiz, e o delimitador (as chaves), que resolve apenas aquele texto.

16 · Erros comuns

Enganos ao escrever GLC

⚠️
Cuidado. Não confunda ambiguidade com não determinismo do parser. Uma gramática pode ser não ambígua e ainda assim difícil de analisar. E lembre: colocar todos os operadores no mesmo nível (E → E + E | E * E) destrói a precedência e gera ambiguidade.
17 · Boas práticas

Projetando boas gramáticas

✅
Dica. Comece sempre estratificando por precedência: um nível de não-terminal por nível de precedência, do menos para o mais forte. Reserve o fator (F) para os parênteses e os átomos. Esse padrão evita ambiguidade desde o início.
18 · Revelar

Reflita: ambiguidade é decidível?

Existe um algoritmo que decide se qualquer GLC é ambígua?
Não. Determinar se uma GLC arbitrária é ambígua é um problema indecidível — não há algoritmo geral que sempre responda. Na prática, projetamos gramáticas de forma a garantir a ausência de ambiguidade por construção (estratificando por precedência, definindo associatividade), em vez de testar uma gramática qualquer.

É exatamente essa fronteira que a varredura do simulador da seção 13 torna palpável: achar uma testemunha é sempre possível quando ela existe — basta procurar o suficiente; concluir que não existe nenhuma, para uma gramática qualquer, é que não tem algoritmo. Por isso a engenharia real é a da seção 17: projetar para que a ambiguidade não possa nascer.

19 · Flashcards

Termos-chave da aula

Não-terminalvirar
Variável sintática que pode ser reescrita por produções (ex.: E, T, F).
Derivaçãovirar
Sequência de aplicações de produções, de S até uma cadeia de terminais.
Árvore sintáticavirar
Registro em árvore da estrutura da derivação; raiz S, folhas terminais.
EBNFvirar
BNF estendida: { } repetição, [ ] opcional, ( ) agrupamento.
20 · Conexões

Onde isto se encaixa no curso

Esta é a aula-base da análise sintática:

  • Vem da aula 2: GLC é o tipo 2 (livre de contexto) de Chomsky.
  • Continua na aula 6: o parser descendente percorre a gramática para reconstruir a árvore.
  • Prepara a aula 7: FIRST/FOLLOW e LL(1) dependem de a gramática estar bem formada (sem ambiguidade).
21 · Síntese

Resumo visual

🔑
GLC = (N, T, P, S). Derivações geram cadeias; árvores registram a estrutura. Ambiguidade (duas árvores) muda o significado e deve ser eliminada estratificando por precedência e definindo associatividade. BNF/EBNF são as notações padrão.
Mão na massa · colaborativo

Atividade em grupo · Caça à ambiguidade

Em duplas, mostrem que uma gramática é ambígua exibindo duas árvores.

⏱️ 25 min👥 duplas🧩 prova por contraexemplo

Roteiro

  1. Dada E → E + E | E * E | ( E ) | id, derivem id + id * id de duas formas.
  2. Desenhem as duas árvores sintáticas distintas.
  3. Reescrevam a gramática em níveis (E, T, F) para eliminar a ambiguidade.
  4. Verifiquem que agora só há uma árvore.
Derivador Aagrupa pela soma primeiro
Derivador Bagrupa pelo produto primeiro
📤 Entrega: Duas árvores da gramática ambígua + a gramática estratificada corrigida.
Teste seu conhecimento

Mini-quiz · Aula 5

20 questões sobre esta aula. Escolha e veja a explicação na hora.

0/20

📌 Resumo — leve isto para a prova

  • GLC = (N, T, P, S); derivações geram cadeias; árvores registram a estrutura.
  • Ambiguidade (duas árvores) muda o significado e deve ser eliminada.
  • Estratificar a gramática codifica precedência; a recursão define a associatividade.
  • O dangling else liga-se, por convenção, ao if mais próximo.
  • BNF/EBNF são as notações padrão de gramáticas.