Análise top-down com
tabela preditiva
• Os dois métodos apresentados até agora para
fazer análise descendente usam recursividade.
Compiladores
– Cada não-terminal tem um procedimento associado;
– Chamadas com ou sem retrocesso.
• Para gramáticas LL1 não tem retrocesso.
Análise sintática (3)
• Chamadas recursivas usam uma pilha implícita
Análise LL(1) com tabela preditiva.
– A pilha das chamadas!
– Sobrecusto!
• Idéia: retirar a recursão do procedimento:
– Usa-se uma pilha para armazenar os não-terminais
encontrados;
– Usa-se uma tabela para orientar as derivações.
1
Funcionamento do parser
Reconhecedor preditivo com Pilha
• Tem um buffer em entrada;
1) Seja X o símbolo no topo da pilha
2) Seja a o símbolo de entrada a analisar
– $ marca seu fim.
• Tem um fluxo de saída;
• Uma pilha cujo fundo é marcado por $
•
• Uma tabela sintática preditiva M
*
1
0
$
a é um terminal
3) Se X = $ e a = $
– Inicializada com S (Start)
0
2
Buffer de entrada
•
Reconheceu uma sentença
•
Termina execução
4) Se X = a != $
Pilha S
Parser preditivo top-down
Saída
$
Desempilha X
•
Avança de um símbolo na entrada
5) Se X é não-terminal: Consulta a tabela M(X, a)
•
•
Tabela
3
A tabela preditiva M(X, t)
4
• Tabela Bi-dimensional:
– Dimensão 1: Não-terminal X
– Dimensão 2: Caractere da entrada (terminal) t
– A entrada (X,t) contém a regra de produção a aplicar
a
Se a entrada for vazia: ERRO
Se a entrada contém X → UVW
•
Então substitui X no topo da pilha por UVW
Exemplo de tabela M(X, t)
• Tabela Bi-dimensional:
b
c
– Dimensão 1: Não-terminal X
– Dimensão 2: Caractere da entrada (terminal) t
– A entrada (X,t) contém a regra de produção a aplicar
$
a
S
A
B
S
A
B
S → cAa
First(A) = {b, c, ε}
A → cB | B
First(B) = {b, ε}
First(S) = {c}
B → bcB | ε
•
Follow(S) = {$}
Follow(A) = {a}
Follow(B) = {a}
5
b
c
S → cAa
First(A) = {b, c, ε}
A → cB | B
First(B) = {b, ε}
First(S) = {c}
B → bcB | ε
$
S → cAa
Follow(S) = {$}
Follow(A) = {a}
Follow(B) = {a}
6
Exemplo de tabela M(X, t)
Exemplo de tabela M(X, t)
• Tabela Bi-dimensional:
• Tabela Bi-dimensional:
– Dimensão 1: Não-terminal X
– Dimensão 2: Caractere da entrada (terminal) t
– A entrada (X,t) contém a regra de produção a aplicar
a
S
A
B
b
c
– Dimensão 1: Não-terminal X
– Dimensão 2: Caractere da entrada (terminal) t
– A entrada (X,t) contém a regra de produção a aplicar
$
a
S → cAa
A→B
A→B
S → cAa
First(A) = {b, c, ε}
A → cB | B
First(B) = {b, ε}
First(S) = {c}
B → bcB | ε
S
A
B
Follow(S) = {$}
Follow(A) = {a}
Follow(B) = {a}
7
Exemplo de tabela M(X, t)
A→B
B→ε
b
c
A→B
S → cAa
A → cB
S → cAa
First(A) = {b, c, ε}
A → cB | B
First(B) = {b, ε}
First(S) = {c}
B → bcB | ε
b
ERRO
Follow(S) = {$}
Follow(A) = {a}
Follow(B) = {a}
Follow(S) = {$}
Follow(A) = {a}
Follow(B) = {a}
8
a
9
S → cAa
First(A) = {b, c, ε}
First(B) = {b, ε}
First(S) = {c}
b
A→B
B→ε
A→B
B →bcB
c
S → cAa
First(A) = {b, c, ε}
A → cB | B
First(B) = {b, ε}
First(S) = {c}
B → bcB | ε
$
S → cAa
A → cB
Follow(S) = {$}
Follow(A) = {a}
Follow(B) = {a}
10
Usando a tabela
• String: “cbca”
c
S → cAa
A → B A → cB
B →bcB ERRO
A → cB | B
B → bcB | ε
First(B) = {b, ε}
First(S) = {c}
S
A
B
– Dimensão 1: Não-terminal X
– Dimensão 2: Caractere da entrada (terminal) t
– A entrada (X,t) contém a regra de produção a aplicar
A→B
B→ε
First(A) = {b, c, ε}
A → cB | B
B → bcB | ε
$
• Tabela Bi-dimensional:
a
ERRO
S → cAa
– Dimensão 1: Não-terminal X
– Dimensão 2: Caractere da entrada (terminal) t
– A entrada (X,t) contém a regra de produção a aplicar
Exemplo de tabela M(X, t)
S
A
B
A→B
$
• Tabela Bi-dimensional:
– Dimensão 1: Não-terminal X
– Dimensão 2: Caractere da entrada (terminal) t
– A entrada (X,t) contém a regra de produção a aplicar
a
c
S → cAa
A → cB
Exemplo de tabela M(X, t)
• Tabela Bi-dimensional:
S
A
B
A→B
b
Pilha
S$
cAa$
Aa$
Ba$
bcBa$
cBa$
Ba$
a$
$
$
ERRO
ERRO
ERRO
Follow(S) = {$}
Follow(A) = {a}
Follow(B) = {a}
11
Entrada
Ação
cbca$
cbca$
bca$
bca$
bca$
ca$
a$
a$
$
S->cAa
casar c
A->B
B->bcB
casar b
casar c
B-> ε
casar a
casar $, sucesso
12
Algoritmo para construir a tabela
– Re-escrever a gramática para satisfazer
condições de LL(1)
– Calcular os conjuntos First e Follow
– Para cada produção A → 1. Para cada a ∈First()
– incluir a produção A → em M[A,a]
2. Se ε ∈ First()
– incluir a produção A → em M[A,b] para
cada b em Follow(A)
3. Se ε ∈ First() e $ ∈ Follow(A)
– incluir A → to M[A,$]
– Todas entradas não definidas são erros
Mais um exemplo...
E → TE’
E’ → +TE’| ε
T → FT’
T’ → *FT’ | ε
F → (E)| Id
E → TE’
T → FT’
T’ → *FT’ | ε
F → (E)| Id
*
id
F
E
First
Follow
E
E’
T
T’
F
{(, id}
{+, ε}
{(, id}
{*, ε}
{(, id}
{$, )}
{$, )}
{+, $, )}
{+, $, )}
{*, +, $, )}
(
F → (E)
E → TE’
T → FT’
T → FT’
E’
T
T’
+
)
{$, )}
{$, )}
{+, $, )}
{+, $, )}
{*, +, $, )}
13
14
Para: G = (T,N,P,S)
T = {a,b,c,d,e,f}
N = {S,X,Y,Z}
E’ →ε
E’ →ε
T’ →ε
T’ →ε
T’ →ε
P: S → XYZ
X → aXb | ε
Y → cYZcX | d
Z → eZYe | f
Construir Tabela e Analisar a string: abcdfcf
$
E’ → +TE’
T’ → *FT’
Follow
{(, id}
{+, ε}
{(, id}
{*, ε}
{(, id}
Exercício LL(1)
Símbolo
F → id
E → TE’
First
E
E’
T
T’
F
Construir Tabela
Mais um exemplo...
E’ → +TE’| ε
Símbolo
15
First(X) = {a, ε}
Follow(X) = {c, d, b, e, f}
First(Y) = {c, d}
Follow(Y) = {e, f}
First(Z) = {e, f}
Follow(Z) = {$, c, d}
First(S) = {a, c, d}
Follow(S) = {$}
Observação sobre a Tabela
16
Gerenciamento de Erros
• A tabela indica se há ambigüidade!
• Relatar erros & recuperar
– Mais de uma regra numa entrada!
–
–
–
–
• Soluções?
– Tornar a gramática LL(1)
• Eliminar ambiguidade, recursividade...
– Usar uma heurística para desempatar as regras
Relatar erros assim que possível
Mensagens de erro adequadas
Continua após o erro
Evitar a cascata de erros
• Nível-Frase (local) x Modo-Pânico
• Qual?
– Nível frase: tenta-se alterar UM símbolo para
recuperar.
– Modo pânico: pula x tokens de entrada até poder
voltar a fazer a análise.
– Usar outros algoritmos do que os top-down!
• Exemplo total: if... Then... Else:
S→iEtS|iEtSeS|a
• = até encontrar um token de sincronização.
E→b
17
18
Recuperação em Modo Pânico
Sumário
• Análise top-down possibilita o reconhecimento
eficiente e simples de gramáticas LL(1):
• Pula tokens até que um “conjunto de
sincronização” é encontrado
– Implementação preditiva com tabela.
– Baseada nos cálculos dos conjuntos First/Follow
– Follow(A) (A sendo no topo da pilha)
– Símbolos de alta hierarquia na gramática
• Obs: têm casos que não foram tratados (e.g. o ‘+’)
• { , for, while, if…
• Limitação:
– First(A)
– Epsilon produção
– Pop/Insert um terminal no topo da pilha.
– Quando a gramática não é LL(1) !
• Por isso: usa-se também análise ascendente
(bottom-up).
• Adicione ações de sincronia para a tabela
19
20
Bibliografia
Exercícios
Leituras:
• A. M. A. Price, S. S. Toscani. Implementação de
Linguagens de Programação: Compiladores. 3ª ed. Porto
Alegre: Sagra-Luzzatto. 2005. Cap 3, Seção 3.2.3.
A. M. A. Price, S. S. Toscani. Implementação de
Linguagens de Programação: Compiladores. 3ª ed. Porto
Alegre: Sagra-Luzzatto. 2005. Cap 3.
– 1a7
• A. V. Aho, R. Sethi, J. D. Ullman. Compilers: Principles,
Techniques and Tools. Reading: Addison-Wesley. 1985.
Seção 4.4 (parser não recursivo, preditivo).
21
22
Download

Apoio 6