CES-41 Teoria Cap 5-a

Download Report

Transcript CES-41 Teoria Cap 5-a

CES-41
COMPILADORES
Capítulo V
Análise Sintática
Capítulo V – Análise Sintática
5.1 – Classificação dos analisadores sintáticos
5.2 – Tratamento de gramáticas
5.3 – Escolha de uma derivação
5.4 – Análise top-down
5.5 – Análise bottom-up
5.6 – Complementos de Yacc
5.1 – Classificação dos Analisadores
Sintáticos

Papel do analisador sintático (visto anteriormente):
–
Verificar a estrutura sintática de um programa
–
Detectar, sinalizar e tratar erros sintáticos
–
Servir de esqueleto para o front-end

Classes de métodos de análise sintática:
Analisadores universais
– Analisadores top-down
– Analisadores bottom-up
–

Analisadores universais:
Conseguem analisar qualquer GLC
– São muito ineficientes para uso em compiladores
–

Analisadores top-down e bottom-up:
–
Varrem o programa da esquerda para a direita, um
símbolo de cada vez; alguns deles por vezes precisam
voltar da direita para a esquerda
–
Englobam os métodos mais comumente usados em
compiladores
–
Os métodos mais eficientes trabalham apenas com
subclasses de gramáticas
–
Várias dessas subclasses (gramáticas LL e LR, descritas
mais adiante) conseguem descrever a maioria das
construções sintáticas das linguagens de programação

Analisadores top-down:
–
Caminham pela árvore sintática dos programas, da
raiz para as folhas
–
Trabalham com gramáticas LL (Left-Left)
–
Englobam os métodos preditores, dentre os quais, os
diagramas de transições
–
As implementações manuais utilizam métodos topdown

Analisadores bottom-up:
–
Caminham pela árvore sintática dos programas, das
folhas para a raiz
–
Trabalham com gramáticas LR (Left-Right)
–
Englobam importantes métodos:
•
•
–
Análise de precedência de operadores
Análise LR
São muito usados na geração automática de
analisadores sintáticos
5.2 – Tratamento de Gramáticas

Métodos de análise sintática se aplicam a determinadas
classes de GLC’s

Algumas gramáticas devem ser alteradas para que um
método possa ser aplicado

É a busca por uma GLC equivalente à original

Duas GLC’s são equivalentes quando geram a mesma
linguagem
5.2.1 – Eliminação de ambiguidades

Alguns métodos de análise sintática exigem que a gramática
seja não-ambígua

Outros métodos mantém a ambiguidade das gramáticas e
usam regras para eliminar árvores sintáticas indesejáveis
Exemplo: comando IF-ELSE da Linguagem C

Produções típicas:
Cmd  IF Expr Cmd | IF Expr Cmd ELSE Cmd
| c1 | c2

A sentença: IF e1 IF e2 c1 ELSE c2
árvores sintáticas:
tem duas

A regra usual para resolver a ambiguidade é fazer o ELSE
corresponder ao segundo IF

Esta regra pode ser incorporada à gramática

Gramática equivalente:
Cmd  CmdCasado | CmdNãoCasado
CmdCasado  IF Expr CmdCasado ELSE CmdCasado
| c1 | c2
CmdNãoCasado  IF Expr Cmd
| IF Expr CmdCasado ELSE CmdNãoCasado
Cmd  CmdCasado | CmdNãoCasado
CmdCasado  IF Expr CmdCasado ELSE CmdCasado
| c1 | c2
CmdNãoCasado  IF Expr Cmd
| IF Expr CmdCasado ELSE CmdNãoCasado

CmdCasado nunca produz IF sem ELSE

Esta gramática impede o aparecimento de comandos nãocasados entre Expr e ELSE
Cmd  CmdCasado | CmdNãoCasado
CmdCasado  IF Expr CmdCasado ELSE CmdCasado
| c1 | c2
CmdNãoCasado  IF Expr Cmd
| IF Expr CmdCasado ELSE CmdNãoCasado

Essa nova gramática dificulta análise preditora:
–

Em Cmd, se aparecer um IF, qual das duas produções
tomar?
Cada tipo de ambiguidade deve ser eliminada mediante um
tratamento particular
5.2.2 – Eliminação de recursividade à esquerda

Uma GLC é recursiva à esquerda, se tiver um não-terminal
A tal que haja uma derivação A + A, onde   (N 
)*

Os métodos de análise top-down não conseguem trabalhar
com gramáticas recursivas à esquerda
Exemplo: sejam as produções
Expr  Term  Expr + Term

Num método top-down típico, há um procedimento para
cada um dos não-terminais Expr e Term:
Expr ( ) {
if ( func (átomos analisados e/ou futuros) == TRUE ) {
Expr ( );
if (atom != ‘+’) erro (“+ esperado”)
else Term ( );
}
else Term ( );
}
Expr ( ) {
if ( func (átomos analisados e/ou futuros) == TRUE ) {
Expr ( );
if (atom != ‘+’) erro (“+ esperado”)
else Term ( );
}
else Term ( );
}

Caso func (átomos analisados e/ou futuros) seja TRUE,
na chamada interna de Expr, os átomos serão os mesmos

O valor de func se repete, resultando em infinitas chamadas
recursivas

Para aplicar um método top-down, toda recursividade à
esquerda deve ser eliminada da gramática

Há recursividade imediata e não-imediata à esquerda

Exemplos:
Imediata: S  S b S  a
Não-imediata: S  Aa  b , A  Sc  d
a) Recursividade imediata à esquerda:

É o caso de uma produção ser recursiva à esquerda

Um caso bem simples é o das seguintes produções:
A  A  
(,   (N  )* e não iniciam com A)

Estas produções podem ser substituídas pelas seguintes:
A  R
R  R  ε
que geram as mesmas formas sentenciais que as originais,
conforme o próximo slide
{A  A  }

{A  R R  R  ε}
Resolve muitos casos, mas não é geral
Exemplo: seja a seguinte gramática, com duas produções
contendo recursividade imediata à esquerda:
{A  A  }
Expr  Expr + Termo  Termo
Termo  Termo * Fator  Fator
Fator  ( Expr )  ID
{A  R
R  R  ε}
Transformação da gramática:
Nas produções de Expr,
substituindo:
Expr  Termo Eaux
Eaux  + Termo Eaux  ε
Termo  Fator Taux
Taux  * Fator Taux  ε
Fator  ( Expr ) | ID
A


R
por
por
por
por
Expr
+ Termo
Termo
Eaux
De modo análogo, na
produção de Termo
Generalização: várias produções do mesmo não-terminal, com
recursividade a esquerda
Algoritmo 5.1: As seguintes produções:
A  A 1  A 2  - - -  A m  1  2  - - -  n
Podem ser substituídas por:
A  1 A'  2 A'  - - -  n A'
A'  1 A'  2 A'  - - -  m A'  ε
Nenhum i
pode ser ε
b) Recursividade não-imediata à esquerda:

O algoritmo a seguir elimina toda recursividade à esquerda
de uma gramática

Condições da gramática: não deve ter ciclos nem produções
vazias

Gramática sem ciclos: sem derivações da forma
A + A

Gramática sem produções vazias: a única produção vazia
permitida é S   (S: símbolo inicial) e S não aparece do
lado direito de qualquer produção

Existem algoritmos para eliminar ciclos e produções vazias
de GLC’s
Algoritmo 5.2: eliminação de recursividade à esquerda
Arranjar os não-terminais numa ordem A1, A2, ... , An
Para (i = 1; i <= n; i++) {
Para (j = 1; j <= i-1; j++) {
Sendo
Aj  1  2  - - - m
as produções de Aj, no momento,
Substituir cada produção da forma Ai  Aj  pelas produções
Ai  1   2   - - - m 
}
Eliminar as recursividades imediatas a esquerda entre
as produções de Ai;
}
Exemplo: Eliminar as recursividades à esquerda da gramática:
SSaTb c
TSdUe f
USgUh i

Renomeando os não-terminais:
S = A1, T = A2, U = A3

A gramática torna-se em:
A1  A1 a  A 2 b  c
A2  A1 d  A 3 e  f
A3  A1 g  A 3 h  i
Para i = 1:

Eliminação das recursividades imediatas à esquerda de A1:
Algoritmo 5.1
A  A1  1  2
A1  A2 b X  c X
X  aX ε
A  1 A'  2 A'
A'  1 A'  ε
A1  A2 b X  c X
X  aX ε
A2  A1 d  A3 e  f
Novo estado
das produções A  A g  A h  i
3
1
3
Para i = 2, j = 1:

Produções de A2:
A2  A1 d  A 3 e  f
Produção da forma
A2  A1 

Produções de A1 no momento: A1  A2 b X  c X

Substitui-se A1 d por A2 b X d  c X d :
Para i = 2:

Eliminação das recursividades imediatas à esquerda de A2:
Algoritmo 5.1
A  A1  1  2  3
A2  c X d Y  A3 e Y  f Y
YbXdYε
Novo estado
das produções
A  1 A'  2 A'  3 A'
A'  1 A'  ε
A1  A2 b X  c X
X  aX ε
A2  c X d Y  A3 e Y  f Y
YbXdYε
A3  A1 g  A3 h  i
Para i = 3, j = 1:

Produções de A3:
A3  A1 g  A 3 h  i
Produção da forma
A3  A1 

Produções de A1 no momento: A1  A2 b X  c X

Substitui-se A1 g por A2 b X g  c X g :
A3  A2 b X g  c X g  A3 h  i
Para i = 3, j = 2:

Produções de A3:
A3  A2 b X g  c X g  A3 h  i
Produção da forma
A3  A2 


Produções de A2 no momento: A2  c X d Y  A3 e Y  f Y
Substitui-se A2 b X g por
c X d Y b X g  A3 e Y b X g  f Y b X g :
Para i = 3:

Eliminação das recursividades imediatas à esquerda de A3:
Algoritmo 5.1
A  A1  A2  1  2
 3  4
A  1 A'  2 A'
 3 A'  4 A'
A'  1 A'  2 A'  ε
A3  c X d Y b X g Z  f Y b X g Z  c X g Z  i Z
ZeYbXgZhZε

Novo estado das produções:
A1  A2 b X  c X
X  aX ε
A2  c X d Y  A 3 e Y  f Y
YbXdYε
A3  c X d Y b X g Z  f Y b X g Z  c X g Z  i Z
ZeYbXgZhZε
A1  A2 b X  c X
A2  c X d Y  A3 e Y  f Y
A3  c X d Y b X g Z  f Y b X g Z  c X g Z  i Z
XaX ε
YbXdYε
ZeYbXgZhZε

Restaurando os nomes originais dos não-terminais:
S = A1, T = A2, U = A3
STbX cX
TcXdYUeY fY
UcXdYbXgZfYbXgZcXgZiZ
XaX ε
Não há mais recursividade a
YbXdYε
esquerda
ZeYbXgZhZε
5.2.3 – Fatoração à esquerda

Sejam as seguintes produções do não-terminal A:
A
onde , ,   (N  )* e o primeiro símbolo de  é
diferente do primeiro símbolo de 

Para um método preditor, não é claro qual produção usar
para expandir A

Pode-se atrasar a decisão, aplicando nesta gramática a
seguinte transformação:
A   A’ , A’    
A


A   A’ , A’    
Isso ainda não garante a possibilidade de aplicação de um
método preditor
-
O primeiro símbolo de  e  pode ser um nãoterminal, permanecendo a indecisão
■ Fatoração é apenas um passo para adequar gramáticas a um
método preditor
5.3 – Escolha de uma Derivação


Mesmo numa GLC não-ambígua, a sequência de derivações
diretas para produzir uma sentença pode variar
Exemplo: seja a gramática S  ε | S ( S )
A sentença ( ) pode ser gerada pelas seguintes derivações:
S  S(S)  (S)  ()
S  S(S)  S()  ()

Essas derivações diferem no trecho marcado
S  S(S)  (S)  ()
S  S(S)  S()  ()

Na primeira, escolheu-se o não-terminal S mais à esquerda
para ser expandido

Na segunda, foi escolhido o não-terminal S mais à direita

Simbolicamente
S(S) me (S)

e
S(S) md S()
Ou seja,
S(S) deriva diretamente mais à esquerda (S)
S(S) deriva diretamente mais à direita S()

Derivação mais à esquerda ou mais à direita: quando
todas as derivações diretas forem me ou md

Simbolicamente: *me

No exemplo anterior
ou
*md
{ S *me ( ) } = { S  S(S)  (S)  ( ) }
{ S *md ( ) } = { S  S(S)  S( )  ( ) }

Qualquer árvore sintática tem uma única derivação mais à
esquerda e uma única derivação mais à direita

Gramática ambígua – outra definição: produz mais de uma
derivação mais à esquerda ou mais à direita para pelo menos
uma de suas sentenças

Os analisadores sintáticos mais conhecidos analisam
derivações mais à esquerda ou mais à direita dos
programas

Exemplo: Produção de derivações mais a esquerda da
gramática:
E  T | T opad E
T  F | F opmult T
F  id | cte | ( E )
Derivação mais à esquerda para a expressão:
( x + y ) * 10 / ( n – 2 )
E  T  F opmult T  ( E ) opmult T 
( T opad E ) opmult T  ( F opad E ) opmult T 
( id opad E ) opmult T  ( id opad T ) opmult T 
( id opad F ) opmult T  ( id opad id ) opmult T 
( id opad id ) opmult F opmult T 
Analisadores preditores
( id opad id ) opmult cte opmult T 
examinam derivações
( id opad id ) opmult cte opmult F 
mais a esquerda
( id opad id ) opmult cte opmult ( E ) 
( id opad id ) opmult cte opmult ( T opad E ) 
( id opad id ) opmult cte opmult ( F opad E ) 
( id opad id ) opmult cte opmult ( id opad E ) 
( id opad id ) opmult cte opmult ( id opad T ) 
( id opad id ) opmult cte opmult ( id opad F ) 
( id opad id ) opmult cte opmult ( id opad cte )
( x + y ) *
10 /
( n – 2 )

Exemplo: Produção de derivações mais a direita da
gramática:
E  T | E opad T
T  F | T opmult F
F  id | cte | ( E )
Derivação mais à direita para a expressão:
( x + y ) * 10 / ( n – 2 )
E  T  T opmult F  T opmult ( E ) 
T opmult ( E opad T )  T opmult ( E opad F ) 
T opmult ( E opad cte )  T opmult ( T opad cte ) 
T opmult ( F opad cte )  T opmult ( id opad cte ) 
T opmult F opmult ( id opad cte ) 
Analisadores bottom-up
T opmult cte opmult ( id opad cte )  examinam derivações
F opmult cte opmult ( id opad cte )  mais a direita reversas
( E ) opmult cte opmult ( id opad cte ) 
( E opad T ) opmult cte opmult ( id opad cte ) 
( E opad F ) opmult cte opmult ( id opad cte ) 
( E opad id ) opmult cte opmult ( id opad cte ) 
( T opad id ) opmult cte opmult ( id opad cte ) 
( F opad id ) opmult cte opmult ( id opad cte ) 
( id opad id ) opmult cte opmult ( id opad cte )
(x + y )
*
10
/
( n
–
2 )
(x
+ y )
*
10
/
(n
–
2 )
( id opad id ) opmult cte opmult ( id opad cte ) 
( F opad id ) opmult cte opmult ( id opad cte ) 
( T opad id ) opmult cte opmult ( id opad cte ) 
( E opad id ) opmult cte opmult ( id opad cte ) 
( E opad F ) opmult cte opmult ( id opad cte ) 
( E opad T ) opmult cte opmult ( id opad cte ) 
( E ) opmult cte opmult ( id opad cte ) 
Em negrito,
F opmult cte opmult ( id opad cte ) 
lados direitos a
T opmult cte opmult ( id opad cte ) 
serem reduzidos
T opmult F opmult ( id opad cte ) 
T opmult ( id opad cte )  T opmult ( F opad cte ) 
T opmult ( T opad cte )  T opmult ( E opad cte ) 
T opmult ( E opad F )  T opmult ( E opad T ) 
T opmult ( E )  T opmult F  T  E
5.4 – Análise Top-Down

Tentativa de construir uma árvore sintática para a sentença
analisada, começando da raiz, indo em direção às folhas e
criando os nós em pré-ordem
Exemplo:
5.4 – Análise Top-Down

Tentativa de construir uma árvore sintática para a sentença
analisada, começando da raiz, indo em direção às folhas e
criando os nós em pré-ordem

Também: tentativa de achar uma derivação mais à
esquerda para a sentença ou programa analisado

Análise preditora: os átomos são analisados linearmente da
esquerda para a direita, sem a necessidade de observar
átomos futuros e de caminhar de volta, da direita para a
esquerda, sobre a sequência de átomos
5.4.1 – Análise backtracking

Por vezes, a produção escolhida para produzir uma
derivação direta mais à esquerda não leva à árvore sintática
da sentença analisada

Então, o analisador é obrigado a voltar para a esquerda na
sequência de átomos e a eliminar uma subárvore da árvore
em construção, substituindo-a por outra

Exemplo: seja a gramática
SaAdaB
Abc
B  ccd  ddc
A seguir, análise da
sentença:
w=accd
SaAdaB
Abc
S
B  ccd  ddc
1) Início:
Árvore contendo somente o
símbolo inicial
Cursor na raiz da árvore
Cursor no primeiro átomo da
sentença
w = a c c d
SaAdaB
Abc
2) 1a produção para expandir S
S
a
A
B  ccd  ddc
d
Liga-se S ao átomo da sentença
apontado pelo cursor, pois é
diante dele que ocorreu a
expansão
Cursor da árvore avança em
pré-ordem
w = a c c d
Os cursores se casam:
a=a
SaAdaB
Abc
3) Os dois cursores avançam
S
a
A
B  ccd  ddc
d
w = a c c d
SaAdaB
Abc
4) 1a produção para expandir A
S
a
A
B  ccd  ddc
d
b
w = a c c d
Liga-se A ao átomo da sentença
apontado pelo cursor, pois é
diante dele que ocorreu a
expansão
Cursor da árvore avança em préordem
Os cursores não se casam:
b≠c
SaAdaB
Abc
B  ccd  ddc
5) Elimina-se a subárvore da
expansão anterior
S
2a produção para expandir A
a
A
d
c
b
w = a c c d
Cursor da sentença retrocede
para onde A foi expandido, ou
seja, permanece onde está:
Cursor da árvore avança em préordem
Os cursores se casam:
c=c
SaAdaB
Abc
B  ccd  ddc
6) Os dois cursores avançam
S
a
A
d
c
w = a c c d
Os cursores não se casam:
d≠c
SaAdaB
Abc
B  ccd  ddc
7) Elimina-se a subárvore da
expansão anterior
a
S
Não existe outra produção para
expandir A
A
Elimina-se também a subárvore da
expansão imediatamente anterior,
ou seja, a de S
dB
c
Cursor da sentença retrocede até o
ponto em que S foi expandido
2a produção para expandir S
w = a c c d
O cursor da árvore avança
Os cursores se casam:
a=a
SaAdaB
Abc
S
B  ccd  ddc
8) Os cursores avançam
1a produção para expandir B
a
B
c c d
w = a c c d
Cursor da árvore avança
Casamento nos três
últimos movimentos dos
cursores:
ccd =ccd
A sentença é reconhecida

Só se chega à conclusão de que há erro na sentença, quando
não houver mais opções para expandir o não-terminal da
raiz

Como detectar e tratar erros?

No pior caso (erro na sentença), o tempo gasto na análise
cresce exponencialmente com tamanho da sentença

E se a gramática tivesse recursividade à esquerda?
Exemplo: S  b  S b

O uso de backtracking é raramente necessário para
analisar construções de linguagens de programação

Análise sem backtracking: no momento de expansão
escolhe a produção correta, dispensando tentativas
desnecessárias

Nem todas as GLC’s podem ser analisadas sem
backtracking, mas, na prática, só ela é usada

Existe análise sem backtracking em que o número de
átomos necessários para a tomada de decisão é maior que 1

Ou seja, existem métodos sem backtracking também não
preditores
5.4.2 – Gramáticas LL(k) e LL(1)


Gramáticas LL(k): admitem analisadores sem backtracking que:

Analisam as sentenças da esquerda para a direita (o
primeiro L - left)

Produzem derivações mais à esquerda (o segundo L)

Precisam analisar no máximo, os próximos k símbolos,
para decidir que produção usar
Gramáticas LL(1): gramáticas LL(k) em que k = 1

Métodos preditores só analisam gramáticas LL(1)

Nesta disciplina serão abordados somente métodos
preditores

Livro que aborda com detalhes muitos outros métodos:

Aho, A.V., Ullman, J.D. – The Theory of Parsing,
Translation and Compiling – Prentice-Hall – 1972

É o primeiro Livro do Dragão – hoje são três

Espectro dos métodos top-down:
Top-down
Backtracking
Não Preditores
Sem Backtracking
Preditores
5.4.3 – Análise preditora recursiva

É uma análise preditora estruturada num conjunto de
subprogramas recursivos

O método dos diagramas de transições é preditor recursivo

Outro método: também orienta-se por uma gramática
fatorada à esquerda, mas dispensa os diagramas de
transições

Exemplo: seja a gramática
Expressão  Termo | Termo OPAD Expressão
Termo  Fator | Fator OPMULT Termo
Fator  ID | CTE | ( Expressão ) | OPNEG Fator
Fatorando:
Expressão  Termo Eaux
Eaux  OPAD Expressão | ε
Termo  Fator Taux
Taux  OPMULT Termo | ε
Fator  ID | CTE | ( Expressão )
A seguir:
Esquema de programa
analisador sintático
em C
| OPNEG Fator

Programa principal:
void main () {
printf ("Digite a expressao:\n\n");
gets (expressao);
printf ("\n");
ptexpr = 0;
carac = NovoCarac (); NovoAtomo ();
Expressao ();
if (atom.tipo != ENDOFFILE)
Esperado (ENDOFFILE);
}
Expressão  Termo Eaux
Eaux  OPAD Expressão | ε
void Expressao () {
Termo ();
Eaux ();
}
void Eaux () {
if (atom.tipo == OPAD) {
NovoAtomo ();
Expressao ();
}
}
Termo  Fator Taux
Taux  OPMULT Termo | ε
void Termo () {
Fator ();
Taux ();
}
void Taux () {
if (atom.tipo == OPMULT) {
NovoAtomo (); Termo ();
}
}
Fator  ID | CTE | ( Expressão ) | OPNEG Fator
void Fator () {
switch (atom.tipo) {
case CTE: NovoAtomo (); break;
case ID: NovoAtomo (); break;
case ABPAR: NovoAtomo (); Expressao ();
if (atom.tipo != FPAR)
Esperado (FPAR);
NovoAtomo (); break;
case OPNEG: NovoAtomo (); Fator (); break;
default: NaoEsperado (atom.tipo);
NovoAtomo (); break;
}
}
5.4.4 – Análise preditora não-recursiva

Para melhor desempenho, é conveniente trocar uma solução
recursiva por outra não-recursiva, usando pilha

Chamadas de subprogramas são consumidoras de tempo

Chamadas recursivas numa análise preditora podem ser
substituídas por empilhamentos e por consultas a uma
tabela de produções

Um analisador não-recursivo, diante de um átomo da
sentença e de um não-terminal a ser expandido, no topo da
pilha, consulta uma tabela de produções para saber como
expandi-lo

Os símbolos do lado direito da produção escolhida são então
empilhados no lugar desse não-terminal

A próxima figura ilustra o esquema de um analisador
sintático preditor não-recursivo

Pilha: local para fazer expansões de não-terminais

Tabela de produções: matriz bidimensional M para indicar a
produção a ser usada para expandir um não-terminal, diante
de um átomo na entrada

Funcionamento: seja X o elemento do topo da pilha e a o
átomo em análise
Se X = a = $: encerra com sucesso
Se X = a  $: desempilha X e avança na entrada
Se X  a e ambos são terminais: erro
Se X é um não terminal: consulta a entrada M[X, a] da
tabela M
a
SeM[X,
Se,
M[X,a]
por a]
exemplo,
pode
for um
sererro:
M[X,a]
uma produção
for a produção
de X
Acionar
de
ou um tratamento
erro
XYZW:
erro
Desempilhar X
Empilhar W, Z, Y, com
Y no topo
X
M[X, a]
Algoritmo 5.3: Análise preditora não recursiva
Empilhar ($); Empilhar (S) /* S é o símbolo inicial */ ;
w  sentença || $; p  &(w[0]);
Repetir {
X  topo da pilha; a  *p;
Se (X = terminal ou X = $) {
Se (X = a) {Desempilhar (X); Avançar (p);}
Senão Erro ( X era esperado ); }
A saída pode ser a
Senão {
/* X é não-terminal */
produção usada
Se M[X, a] = {XY1Y2Y3 ... Yk} {
Desempilhar (X);
Empilhar (Yk ... Y3Y3Y1) /* Y1 no topo */ ;
Imprimir a produção {XY1Y2Y3 ... Yk};}
Senão Erro ( a não era esperado ); }
} enquanto (X  $)
/* enquanto pilha ainda não vazia */
Exemplo: seja a seguinte gramática de expressões:
E  T E’
T  F T’
E’  + T E’ | ε
T’  * F T’ | ε
F  ( E ) | id

Seja a seguinte sentença em análise: id + id * id

Supor a seguinte tabela de produções (sua construção é
apresentada após o exemplo – as posições vazias são erros):
Ação:
Expandir
Estado inicial:
Sentença de entrada
id
T
E’
E
$
Pilha
+
id
*
id
Programa
analisador
preditor
$
Saída
E  TE’
Ação:
Expandir
Sentença de entrada
id
+
id
*
id
$
F
T’
E’
$
Pilha
Programa
analisador
preditor
Saída
E  TE’
T
E
 FT’
TE’
Ação:
Expandir
Sentença de entrada
id
+
id
*
id
$
id
F
T’
E’
$
Pilha
Programa
analisador
preditor
Saída
E  TE’
E  FT’
TE’
T
T  id
FT’
F
Sentença de entrada
id
+
id
*
id
Ação:
Desempilhar
Avançar
$
id
T’
E’
$
Pilha
Programa
analisador
preditor
Saída
E  TE’
T  FT’
F  id
Ação:
Expandir
Sentença de entrada
id
T’
E’
$
Pilha
+
id
*
id
Programa
analisador
preditor
$
Saída
E  TE’
T
E
 FT’
TE’
F
T
 id
FT’
T’
ε
F  id
Ação:
Expandir
Sentença de entrada
id
+
T
E’
$
Pilha
+
id
*
id
Programa
analisador
preditor
$
Saída
E  TE’
E
TE’
T  FT’
T  id
FT’
F
F  id
T’
ε
T’
E’ ε+T’E’
Sentença de entrada
id
+
T
E’
$
Pilha
+
id
*
id
Programa
analisador
preditor
Ação:
Desempilhar
Avançar
$
Saída
E  TE’
T  FT’
F  id
T’ ε
E’  +T’E’
Ação:
Expandir
Sentença de entrada
id
+
id
*
id
$
F
T’
T
E’
$
Pilha
Programa
analisador
preditor
Saída
E  TE’
E  FT’
TE’
T
T  id
FT’
F
F
 id
T’
ε
T’
E’ ε+T’E’
E’
FT’
+T’E’
T
Ação:
Expandir
Sentença de entrada
id
id
F
T’
E’
$
Pilha
+
id
*
id
Programa
analisador
preditor
$
Saída
E  TE’
E
TE’
T  FT’
T
FT’
F  id
F
 id
T’
ε
T’
E’
ε+T’E’
E’
FT’
+T’E’
T
T
FT’
F  id
Sentença de entrada
id
id
T’
E’
$
Pilha
+
id
*
id
Programa
analisador
preditor
Ação:
Desempilhar
Avançar
$
Saída
E  TE’
T  FT’
F  id
T’ ε
E’  +T’E’
T  FT’
F  id
Ação:
Expandir
Sentença de entrada
id
+
id
*
id
$
*
F
T’
E’
$
Pilha
Programa
analisador
preditor
Saída
E  TE’
E
TE’
T  FT’
T  id
FT’
F
F  id
T’
ε
T’
E’ ε+T’E’
E’
FT’
+T’E’
T
T
FT’
F  id
F
T’
id
*FT’
Sentença de entrada
id
+
id
*
id
$
*
F
T’
E’
$
Pilha
Programa
analisador
preditor
Ação:
Desempilhar
Avançar
Saída
E  TE’
T  FT’
F  id
T’ ε
E’  +T’E’
T  FT’
F  id
T’  *FT’
Ação:
Expandir
Sentença de entrada
id
id
F
T’
E’
$
Pilha
+
id
*
id
Programa
analisador
preditor
$
Saída
E  TE’
T
E
 FT’
TE’
F
T
 id
FT’
T’
ε
F  id
E’
ε+T’E’
T’
T
E’
FT’
+T’E’
F
T
 id
FT’
T’
id
*FT’
F
F
T’
id
*FT’
Sentença de entrada
id
id
T’
E’
$
Pilha
+
id
*
id
Programa
analisador
preditor
$
Saída
Ação:
Desempilhar
Avançar
E  TE’
T  FT’
F  id
T’ ε
E’  +T’E’
T  FT’
F  id
T’  *FT’
F  id
Ação:
Expandir
Sentença de entrada
id
T’
E’
$
Pilha
+
id
*
id
Programa
analisador
preditor
$
Saída
E  TE’
E
TE’
T  FT’
T  id
FT’
F
F  id
T’
ε
T’
E’ ε+T’E’
E’
FT’
+T’E’
T
T
FT’
F  id
F
T’
id
*FT’
T’
id
*FT’
F
F  id
T’
ε
Ação:
Expandir
Sentença de entrada
id
E’
$
Pilha
+
id
*
id
Programa
analisador
preditor
$
Saída
E  TE’
E  FT’
TE’
T
T  id
FT’
F
F
 id
T’
ε
T’
E’ ε+T’E’
E’
FT’
+T’E’
T
T  id
FT’
F
F
T’
id
*FT’
T’
id
*FT’
F
F
 id
T’
ε
T’
E’ ε
Sentença de entrada
id
Resta saber como
construir a tabela
de produções !!!
$
Pilha
+
id
*
id
Programa
analisador
preditor
$
Saída
Ação:
Encerrar
Desempilhar
com
Avançar
sucesso
E  TE’
T  FT’
F  id
T’ ε
E’  +T’E’
T  FT’
F  id
T’  *FT’
F  id
T’ ε
E’ ε
Derivação
E  T E’
E’  + T E’ | ε
T  F T’
T’  * F T’ | ε
F  ( E ) | id

Há gramáticas cujas tabelas apresentam duas ou mais
produções para a mesma entrada

Então não se pode aplicar um método preditor
E  T E’

T  F T’
T’  * F T’ | ε
F  ( E ) | id
Produção de X em que o primeiro símbolo do lado direito é
um terminal a: pode ser colocada na entrada [X, a]
–

E’  + T E’ | ε
Prepara um casamento entre pilha e entrada
Exemplos: M[E’, +] = E’  +TE’
M[T’, *] = T’  *FT’
M[F, (] = F  (E)
M[F, id] = F  id
E  T E’

T  F T’
T’  * F T’ | ε
F  ( E ) | id
Produção de X em que o primeiro símbolo do lado direito é
um não terminal Y:
–

E’  + T E’ | ε
Para ser colocada numa entrada [X, a] é necessário ver
se Y pode derivar uma forma sentencial iniciada por a
É o caso das produções E  TE’ e T  FT’
E  T E’

E’  + T E’ | ε
T  F T’
T’  * F T’ | ε
F  ( E ) | id
Produção vazia de X:
–
Para ser colocada numa entrada [X, a] é necessário ver
se a pode aparecer em alguma forma sentencial depois
de X
5.4.5 – As funções Primeiro e Seguinte


A construção da tabela de produções é auxiliada por duas
funções: Primeiro e Seguinte
Sendo   (N  )*, Primeiro () é o conjunto dos
terminais que iniciam todas as sentenças derivadas de 


Se  * ε, então ε  Primeiro ()
Observa-se que, se o primeiro símbolo de  for um terminal
qualquer a, então Primeiro () = {a}
Exemplo: seja  = A a B C b c d D
 +
______________
____________
_______________

____________
_____________
Todas as possíveis
e talvez infinitas
cadeias só de
terminais
Eliminando-se os
terminais repetidos,
obtém-se Primeiro()
Como  possui
terminais,  * ε
portanto:
ε  Primeiro ()
Utilidade da função Primeiro () em análise preditora:


Sejam as produções A   | 
Na expansão de A diante do terminal b, a decisão por  ou
, depende de b pertencer a Primeiro() ou Primeiro()

Caso b pertença aos dois conjuntos, a indecisão persiste

Caso b não pertença a nenhum dos dois conjuntos: erro

Sendo A um não-terminal qualquer, Seguinte (A) é o
conjunto dos terminais que podem aparecer imediatamente
à direita de A, em alguma forma sentencial

Mais precisamente, é o conjunto dos terminais b, tais que
existe uma derivação da forma S * Ab
(,  (N  )*)

Se A for o símbolo mais a direita de alguma forma
sentencial, então $  Seguinte (A)
Exemplo: sejam todas as possivelmente infinitas formas
sentenciais de uma GLC
S *
______________
____________
_______________
______________
____________
_______________

____________
_____________
____________
_____________
____________
_____________
Em algumas
(possivelmente infinitas)
o não-terminal A aparece
Exemplo: sejam todas as possivelmente infinitas formas
sentenciais de uma GLC
S *
______________
_____A______
_______________
______________
_______A____
______________A

____________
____A________
____________
_____________
_________A__
_____________
Colocando-se o
finalizador ‘$’ em
cada uma
Exemplo: sejam todas as possivelmente infinitas formas
sentenciais de uma GLC
S *
______________$
_____A______$
_______________$
______________$
_______A____$
______________A$

____________$
____A________$
____________$
_____________$
_________A__$
_____________$
Colocando-se os
símbolos que
seguem A
Exemplo: sejam todas as possivelmente infinitas formas
sentenciais de uma GLC
S *
______________$
_____AB_____$
_______________$
______________$
_______Aa___$
______________A$

____________$
____Ab_______$
____________$
_____________$
_________AD_$
_____________$
a, b, $  Seguinte (A)
Cálculo do Primeiro ():

Seja  = X1 X2 X3 ... Xn, onde Xi (1  i  n) é um terminal ou
um não terminal:

O cálculo do Primeiro () exige o cálculo de Primeiro
(Xi)
Algoritmo 5.4: Calcula Primeiro (X), onde X  (N  )

O cálculo é feito simultaneamente para todos os
símbolos da gramática

O algoritmo tem 2 etapas:
1. Inicialização
2. Processo rotativo convergente
Algoritmo 5.4:
1.
Inicialização
Para todo símbolo X da gramática {
Se (X é terminal) Primeiro (X)  {X};
Senão se (X   é uma produção)
Primeiro (X)  {};
Senão Primeiro (X)  { };
}
2. Processo rotativo convergente
Aplicar a seguinte regra a toda produção não-vazia da
gramática de modo rotativo, até que nenhum terminal
ou  seja acrescentado ao conjunto Primeiro de
qualquer não-terminal:
Para cada produção do tipo X  Y1Y2Y3 ... Yk (k  1) {
Para (i  1; i  k; i++)
Se (i = 1) || (j | (1  j  i-1),   Primeiro (Yj ))
Acrescentar
(Primeiro (Yi ) - {}) a Primeiro (X)
Se (i | (1  i  k),   Primeiro (Yi ))
Acrescentar  a Primeiro (X);
}
Comentários: seja a produção X  Y1Y2Y3 ... Yk (k  1)



Todo o terminal (excluído ) presente em Primeiro (Y1)
certamente pertence a Primeiro (X)
Se Y1 não deriva , então nada mais deve ser acrescentado a
Primeiro (X)
Mas se Y1 deriva , então deve-se acrescentar a Primeiro
(X) todo terminal (excluído ) contido em Primeiro (Y2)
Comentários: seja a produção X  Y1Y2Y3 ... Yk (k  1)



Se Y1, Y2 e Y3 derivam , então deve-se acrescentar a
Primeiro (X) todo terminal contido em Primeiro (Y4)
E assim por diante
Se Y1, Y2 , Y3, ... , Yk derivam , então deve-se acrescentar 
a Primeiro (X)
Exemplo: seja a gramática:
E  T E’
T  F T’

E’  + T E’ | 
T’  * F T’ | 
F  ( E ) | id
Inicialização do Algoritmo 5.4:
Primeiro (+) = {+}
Primeiro (‘(’) = { ( }
Primeiro (id) = {id}
Primeiro (*) = {*}
Primeiro (‘)’) = { ) }
Primeiro (E’) = Primeiro (T’) = {}
Primeiro (E) = Primeiro (T) = Primeiro (F) = { }
E  T E’
T  F T’

E’  + T E’ | 
T’  * F T’ | 
F  ( E ) | id
Processo rotativo do Algoritmo 5.4, 1a rotação:
Primeiro (+) = {+}
Primeiro (*) = {*}
Primeiro (‘(’) = { ( }
Primeiro (‘)’) = { ) }
Primeiro (id) = {id}
Primeiro (E’) = {}
Primeiro (T’) = {}
Primeiro (E) = { }
Primeiro (T) = { }
Primeiro (F) = { }
E  T E’ : Primeiro (T) = { }
nada acontece
E’  + T E’ : Primeiro (+) = {+}, então
Primeiro (E’) = {, +}
E  T E’
T  F T’

E’  + T E’ | 
T’  * F T’ | 
F  ( E ) | id
Processo rotativo do Algoritmo 5.4, 1a rotação:
Primeiro (+) = {+}
Primeiro (*) = {*}
Primeiro (‘(’) = { ( }
Primeiro (‘)’) = { ) }
Primeiro (id) = {id}
Primeiro (E’) = {, +}
Primeiro (T’) = {}
Primeiro (E) = { }
Primeiro (T) = { }
Primeiro (F) = { }
T  F T’ : Primeiro (F) = { }
nada acontece
T’  *F T’ : Primeiro (*) = {*}, então
Primeiro (T’) = {, *}
E  T E’
T  F T’

E’  + T E’ | 
T’  * F T’ | 
F  ( E ) | id
Processo rotativo do Algoritmo 5.4, 1a rotação:
Primeiro (+) = {+}
Primeiro (*) = {*}
Primeiro (‘(’) = { ( }
Primeiro (‘)’) = { ) }
Primeiro (id) = {id}
Primeiro (E’) = {, +}
Primeiro (T’) = {, *}
Primeiro (E) = { }
Primeiro (T) = { }
Primeiro (F) = { }
F  ( E ) : Primeiro (‘(’) = { ( }, então
Primeiro (F) = { ( }
E  T E’
T  F T’

E’  + T E’ | 
T’  * F T’ | 
F  ( E ) | id
Processo rotativo do Algoritmo 5.4, 1a rotação:
Primeiro (+) = {+}
Primeiro (*) = {*}
Primeiro (‘(’) = { ( }
Primeiro (‘)’) = { ) }
Primeiro (id) = {id}
Primeiro (E’) = {, +}
Primeiro (T’) = {, *}
Primeiro (E) = { }
Primeiro (T) = { }
Primeiro (F) = { ( }
F  id : Primeiro (id) = { id }, então
Primeiro (F) = { (, id }
E  T E’
T  F T’

E’  + T E’ | 
T’  * F T’ | 
F  ( E ) | id
Processo rotativo do Algoritmo 5.4, final da 1a rotação
Primeiro (+) = {+}
Primeiro (*) = {*}
Primeiro (‘(’) = { ( }
Primeiro (‘)’) = { ) }
Primeiro (id) = {id}
Primeiro (E’) = {, +}
Primeiro (T’) = {, *}
Primeiro (E) = { }
Primeiro (T) = { }
Primeiro (F) = { (, id }
E  T E’
T  F T’

E’  + T E’ | 
T’  * F T’ | 
F  ( E ) | id
Processo rotativo do Algoritmo 5.4, 2a rotação:
Primeiro (+) = {+}
E  T E’ : Primeiro (T) = { }
Primeiro (*) = {*}
nada acontece
E’  + T E’ : Primeiro (+) = {+},
Primeiro (‘(’) = { ( }
nada acontece
Primeiro (‘)’) = { ) }
T  F T’ : Primeiro (F) = { (, id }, então
Primeiro (id) = {id}
Primeiro (T) = { (, id }
Primeiro (E’) = {, +}
Primeiro (T’) = {, *}
Primeiro (E) = { }
Primeiro (T) = { }
Primeiro (F) = { (, id }
E  T E’
T  F T’

E’  + T E’ | 
T’  * F T’ | 
F  ( E ) | id
Processo rotativo do Algoritmo 5.4, 2a rotação:
Primeiro (+) = {+}
T’  * F T’ : Primeiro (*) = {*},
Primeiro (*) = {*}
nada acontece
F  ( E ) : Primeiro (‘(’) = { ( },
Primeiro (‘(’) = { ( }
nada acontece
Primeiro (‘)’) = { ) }
F  id : Primeiro (id) = { id },
Primeiro (id) = {id}
nada acontece
Primeiro (E’) = {, +}
Primeiro (T’) = {, *}
Primeiro (E) = { }
Primeiro (T) = { (, id }
Primeiro (F) = { (, id }
E  T E’
T  F T’

E’  + T E’ | 
T’  * F T’ | 
F  ( E ) | id
Processo rotativo do Algoritmo 5.4, final da 2a rotação
Primeiro (+) = {+}
Primeiro (*) = {*}
Primeiro (‘(’) = { ( }
Primeiro (‘)’) = { ) }
Primeiro (id) = {id}
Primeiro (E’) = {, +}
Primeiro (T’) = {, *}
Primeiro (E) = { }
Primeiro (T) = { (, id }
Primeiro (F) = { (, id }
E  T E’
T  F T’

E’  + T E’ | 
T’  * F T’ | 
F  ( E ) | id
Processo rotativo do Algoritmo 5.4, 3a rotação:
Primeiro (+) = {+}
E  T E’ : Primeiro (T) = { (, id }
Primeiro (*) = {*}
Primeiro (E) = { (, id }
Primeiro (‘(’) = { ( }
Primeiro (‘)’) = { ) }
Primeiro (id) = {id}
Primeiro (E’) = {, +}
Primeiro (T’) = {, *}
Primeiro (E) = { }
Primeiro (T) = { (, id }
Primeiro (F) = { (, id }
E  T E’
T  F T’

E’  + T E’ | 
T’  * F T’ | 
F  ( E ) | id
Processo rotativo do Algoritmo 5.4, 3a rotação:
Primeiro (+) = {+}
Com as outras produções
Primeiro (*) = {*}
nada acontece
Primeiro (‘(’) = { ( }
Primeiro (‘)’) = { ) }
Primeiro (id) = {id}
Primeiro (E’) = {, +}
Primeiro (T’) = {, *}
Primeiro (E) = { (, id }
Primeiro (T) = { (, id }
Primeiro (F) = { (, id }
E  T E’
T  F T’

E’  + T E’ | 
T’  * F T’ | 
F  ( E ) | id
Processo rotativo do Algoritmo 5.4, final da 3a rotação
Primeiro (+) = {+}
Primeiro (*) = {*}
Primeiro (‘(’) = { ( }
Primeiro (‘)’) = { ) }
Primeiro (id) = {id}
Primeiro (E’) = {, +}
Primeiro (T’) = {, *}
Primeiro (E) = { (, id }
Primeiro (T) = { (, id }
Primeiro (F) = { (, id }
E  T E’
T  F T’

E’  + T E’ | 
T’  * F T’ | 
F  ( E ) | id
Processo rotativo do Algoritmo 5.4, 4a rotação:
Primeiro (+) = {+}
Nada acontece
Primeiro (*) = {*}
Ficam determinados os Primeiro’s de
Primeiro (‘(’) = { ( }
todos os símbolos
Primeiro (‘)’) = { ) }
Primeiro (id) = {id}
Primeiro (E’) = {, +}
Primeiro (T’) = {, *}
Primeiro (E) = { (, id }
Primeiro (T) = { (, id }
Primeiro (F) = { (, id }
Algoritmo 5.5: Cálculo do Primeiro (),
onde  = X1 X2 X3 ... Xn e Xi  (N  ), (1  i  n)
Primeiro ()  { };
Para (i  1; i  n; i++)
Se (i = 1) || (j | (1  j  i-1),   Primeiro (Xj ))
Acrescentar (Primeiro (Xi ) - {}) a Primeiro () ;
Se (i | (1  i  n),   Primeiro (Xi ))
Acrescentar  a Primeiro ();
Se ( = ) Primeiro ()  {};
Comentários: seja  = X1 X2 X3 ... Xn




Todos os terminais de Primeiro (X1) devem pertencer a
Primeiro ()
Caso (  Primeiro (X1)), todos os terminais de Primeiro
(X2 ) devem pertencer a Primeiro ()
Caso (  Primeiro (X1) e   Primeiro (X2)), todos os
terminais de Primeiro (X3) devem pertencer a Primeiro ()
E assim por diante
Algoritmo 5.6: Calcula Seguinte (A), onde A é um nãoterminal

O cálculo é feito simultaneamente para todos os nãoterminais da gramática

O algoritmo tem 3 etapas
1. Inicialização trivial
2. Inicialização não-trivial
3. Processo rotativo convergente
Algoritmo 5.6:
1.
Inicialização trivial
Seguinte (S)  {$}; /* S é o símbolo inicial */
/* $ é marca de fim da sentença
*/
Inicializar Seguinte de todos os outros não-terminais com o
conjunto vazio
Comentários:
S *
______________$
___________$
_______________$
______________$
___________$
______________$

____________$
S$
____________$
_____________$
___________$
_____________$
$  Seguinte (S)
2. Inicialização não-trivial
Pelo menos um
não-terminal
do lado direito
Para toda produção do tipo
A  w1 B1 w2 B2 w3 ... wn Bn wn+1,
(n  1; wi  *, 1  i  n+1; Bi  N, 1  i  n) {
Para (i  1; i  n; i++) {
Expressão da produção: A  i Bi i,
(i, i  (N  )*);
Se (i não é vazia) Acrescentar
(Primeiro (i ) - {}) a Seguinte (Bi) ;
}
}
Comentários: seja a produção A  i Bi i


Sendo A um não-terminal útil, haverá pelo menos uma forma
sentencial do tipo
____________A____$
i pode ser vazia ou
derivar a cadeia vazia
E haverá outra do tipo
Se for vazia,
Primeiro(i) = {}
_ _ _ _ _ _ _ _ _ _ _ _  i Bi  i _ _ _ _ $

Então (Primeiro (i) – {})  Seguinte (Bi)
3. Processo rotativo convergente
Pelo menos um nãoterminal do lado direito
Aplicar a seguinte regra a toda produção não-vazia da
gramática de modo rotativo, até que nenhum terminal
ou $ seja acrescentado ao conjunto Seguinte de
qualquer não terminal:
Seja A  w1 B1 w2 B2 w3 ... wn Bn wn+1, a produção em questão,
onde (n  1; wi  *, 1  i  n+1; Bi  N, 1  i  n):
Para (i  1; i  n; i++) {
Expressão da produção: A  i Bi i,
(i, i  (N  )*);
Se ((i é vazia) ou (  Primeiro (i)))
Acrescentar Seguinte (A) a Seguinte (Bi) ;
}
Comentários: seja a produção A  i Bi i

Se a, b, c, $  Seguinte (A), há formas sentenciais dos tipos
______Aa___$
____Ab___$
_____Ac___$
______A$

E também dos tipos
Se i é vazia: a, b, c, $  Seguinte (Bi)
Se   Primeiro (i), ou seja,
se i * : há formas sentenciais em
que a, b, c, $ aparecem imediatamente
depois de Bi
_ _ _ _ _ _ i Bi i a _ _ _ $ Então, acrescentar a, b, c, $ ao
_ _ _ _ i Bi i b _ _ _ $
Seguinte (Bi)
_ _ _ _ _ i Bi i c _ _ _ $
Ou seja, acrescentar Seguinte (A) ao
_ _ _ _ _ _ i Bi i $
Seguinte (Bi)
Exemplo: seja a mesma gramática:
E  T E’
T  F T’

E’  + T E’ | 
T’  * F T’ | 
F  ( E ) | id
Seja a tabela dos Primeiro’s de seus não-terminais calculada
no exemplo anterior:

Inicialização trivial do Algoritmo 5.6:
Seguinte (E) = {$}
Seguinte (E’) = { }
Seguinte (T) = { }
Seguinte (T’) = { }
Seguinte (F) = { }

Inicialização não-trivial do Algoritmo 5.6:
Seguinte (E) = {$}
Seguinte (E’) = { }
Seguinte (T) = { }
Seguinte (T’) = { }
Seguinte (F) = { }
Produção E  T E’:
Seguinte (T) = Seguinte (T)  (Primeiro (E’) – {ε}) =
{ }  ({+, ε} – {ε}) = {+}

Inicialização não-trivial do Algoritmo 5.6:
Seguinte (E) = {$}
Seguinte (E’) = { }
Seguinte (T) = {+}
Seguinte (T’) = { }
Seguinte (F) = { }
Produção E  T E’:
Seguinte (T) = Seguinte (T)  (Primeiro (E’) – {ε}) =
{ }  ({+, ε} – {ε}) = {+}

Inicialização não-trivial do Algoritmo 5.6:
Seguinte (E) = {$}
Seguinte (E’) = { }
Seguinte (T) = {+}
Seguinte (T’) = { }
Seguinte (F) = { }
Produção E’  +T E’:
Seguinte (T) = Seguinte (T)  (Primeiro (E’) – {ε}) =
{+}  ({+, ε} – {ε}) = {+}

Inicialização não-trivial do Algoritmo 5.6:
Seguinte (E) = {$}
Seguinte (E’) = { }
Seguinte (T) = {+}
Seguinte (T’) = { }
Seguinte (F) = { }
Produção T  FT’:
Seguinte (F) = Seguinte (F)  (Primeiro (T’) – {ε}) =
{ }  ({*, ε} – {ε}) = {*}

Inicialização não-trivial do Algoritmo 5.6:
Seguinte (E) = {$}
Seguinte (E’) = { }
Seguinte (T) = {+}
Seguinte (T’) = { }
Seguinte (F) = {*}
Produção T  FT’:
Seguinte (F) = Seguinte (F)  (Primeiro (T’) – {ε}) =
{ }  ({*, ε} – {ε}) = {*}

Inicialização não-trivial do Algoritmo 5.6:
Seguinte (E) = {$}
Seguinte (E’) = { }
Seguinte (T) = {+}
Seguinte (T’) = { }
Seguinte (F) = {*}
Produção T’  *FT’:
Seguinte (F) = Seguinte (F)  (Primeiro (T’) – {ε}) =
{*}  ({*, ε} – {ε}) = {*}

Inicialização não-trivial do Algoritmo 5.6:
Seguinte (E) = {$}
Seguinte (E’) = { }
Seguinte (T) = {+}
Seguinte (T’) = { }
Seguinte (F) = {*}
Produção F  (E):
Seguinte (E) = Seguinte (E)  (Primeiro (‘)’) – {ε}) =
{$}  ({ ) } – {ε}) = {$, )}

Inicialização não-trivial do Algoritmo 5.6:
Seguinte (E) = {$, )}
Seguinte (E’) = { }
Seguinte (T) = {+}
Seguinte (T’) = { }
Seguinte (F) = {*}

Final da inicialização não-trivial do Algoritmo 5.6:
Seguinte (E) = {$, )}
Seguinte (E’) = { }
Seguinte (T) = {+}
Seguinte (T’) = { }
Seguinte (F) = {*}

Processo rotativo do Algoritmo 5.6, 1a rotação:
Seguinte (E) = {$, )}
Seguinte (E’) = { }
Seguinte (T) = {+}
Seguinte (T’) = { }
Seguinte (F) = {*}
Produção E  T E’: (ε  Primeiro (E’))
Seguinte (T) = Seguinte (T)  Seguinte (E) =
{+}  {$, )} = {+, $, )}

Processo rotativo do Algoritmo 5.6, 1a rotação:
Seguinte (E) = {$, )}
Seguinte (E’) = { }
Seguinte (T) = {+, $, )}
Seguinte (T’) = { }
Seguinte (F) = {*}
Produção E  T E’:
Seguinte (T) = Seguinte (T)  Seguinte (E) =
{+}  {$, )} = {+, $, )}
Seguinte (E’) = Seguinte (E’)  Seguinte (E) =
{ }  {$, )} = {$, )}

Processo rotativo do Algoritmo 5.6, 1a rotação:
Seguinte (E) = {$, )}
Seguinte (E’) = {$, )}
Seguinte (T) = {+, $, )}
Seguinte (T’) = { }
Seguinte (F) = {*}
Produção T  F T’: (ε  Primeiro (T’))
Seguinte (F) = Seguinte (F)  Seguinte (T) =
{*}  {+, $, )} = {*, +, $, )}

Processo rotativo do Algoritmo 5.6, 1a rotação:
Seguinte (E) = {$, )}
Seguinte (E’) = {$, )}
Seguinte (T) = {+, $, )}
Seguinte (T’) = { }
Seguinte (F) = {*, +, $, )}
Produção T  F T’:
Seguinte (F) = Seguinte (F)  Seguinte (T) =
{*}  {+, $, )} = {*, +, $, )}
Seguinte (T’) = Seguinte (T’)  Seguinte (T) =
{ }  {+, $, )} = {+, $, )}

Processo rotativo do Algoritmo 5.6, 1a rotação:
Seguinte (E) = {$, )}
Seguinte (E’) = {$, )}
Seguinte (T) = {+, $, )}
Seguinte (T’) = {+, $, )}
Seguinte (F) = {*, +, $, )}
Produção T’  * F T’: (ε  Primeiro (T’))
Seguinte (F) = Seguinte (F)  Seguinte (T’) =
{*, +, $, )}  {+, $, )} = {*, +, $, )}
Seguinte (T’) = Seguinte (T’)  Seguinte (T’) =
{+, $, )}  {+, $, )} = {+, $, )}

Processo rotativo do Algoritmo 5.6, 1a rotação:
Seguinte (E) = {$, )}
Seguinte (E’) = {$, )}
Seguinte (T) = {+, $, )}
Seguinte (T’) = {+, $, )}
Seguinte (F) = {*, +, $, )}
Produção F ( E ): (Não se aplica a regra, pois ε  Primeiro (‘)’))

Final da 1a rotação do processo rotativo do Algoritmo 5.6:
Seguinte (E) = {$, )}
Seguinte (E’) = {$, )}
Seguinte (T) = {+, $, )}
Seguinte (T’) = {+, $, )}
Seguinte (F) = {*, +, $, )}

Processo rotativo do Algoritmo 5.6, 2a rotação:
Seguinte (E) = {$, )}
Seguinte (E’) = {$, )}
Seguinte (T) = {+, $, )}
Seguinte (T’) = {+, $, )}
Seguinte (F) = {*, +, $, )}
Nada é alterado
Ficam determinados os Seguinte’s de todos os não-terminais
5.4.6 – Construção da tabela de produções

Seja A um não-terminal e a um terminal

Sejam A       as produções de A

Se a  Primeiro ()
Então:
A pode expandir para
, na presença de a na
entrada
 pode gerar subsentenças iniciadas
por a, para casar com
o a na entrada


Sejam A       as produções de A
Se a  (Primeiro () e Primeiro () e Primeiro ())
E se ,  e  não derivarem ε
Então:
Erro com A na presença
de a na entrada
A só gera sub-sentenças
não-vazias e não
iniciadas por a
Nunca aparecerá um a
no topo da pilha para
casar com o a na entrada



Sejam A       as produções de A
Se a  (Primeiro () e Primeiro () e Primeiro ()), mas
se pelo menos  derivar ε
E se a  Seguinte (A)
Então:
A pode expandir para 
na presença de a na
entrada
Existe a possibilidade
de haver um a abaixo do
A, na pilha, ou um nãoterminal que gere algo
iniciado por a
Algoritmo 5.7: Construção da tabela de produções
Para cada produção A   da gramática {
Para cada terminal a em Primeiro ()
Acrescentar A   a M[A, a];
Se (ε  Primeiro ()) {
Para todo terminal b em Seguinte (A)
Acrescentar A   a M[A, b];
Se ($  Seguinte (A))
Acrescentar A   a M[A, $];
}
}
Colocar Erro em cada posição indefinida da tabela.
Exemplo: seja a mesma gramática:
E  T E’
T  F T’

E’  + T E’ | 
T’  * F T’ | 
F  ( E ) | id
Tabelas dos Primeiro’s e Seguinte’s já calculadas:
E  T E’
T  F T’

E’  + T E’ | 
T’  * F T’ | 
Tabela de produções:
F  ( E ) | id

Sejam A     as produções de A

Se a  (Primeiro () e Primeiro ())
Então: indecisão
A pode expandir para
 ou , na presença de
a na entrada
A gramática não é
LL(1), ou seja, não
admite análise
preditora


Sejam A     as produções de A
Se a  (Primeiro () e Seguinte (A))
e se  derivar ε
Indecisão: duas ou mais produções
numa posição M[A, a] da tabela
Então: indecisão
A pode expandir para
 ou , na presença de
a na entrada
A gramática não é
LL(1), ou seja, não
admite análise
preditora
Exemplo: seja a gramática para if-else:
S  i b S S’ | a

Tabelas:
S’  e S | 
M[S’, e] tem duas
produções
Tipicamente decide-se
por
S’  e S
O else corresponde ao
último if
5.4.7 – Tratamento de erros
Um erro é detectado quando:
Um terminal no topo da pilha não
casa com o próximo símbolo na
entrada
b era esperado
O não-terminal no topo da pilha e
o próximo símbolo de entrada
determinam uma posição de erro
na tabela de produções
a não era esperado
O terminal no topo da pilha não casa com o próximo símbolo na
entrada:

Mensagem: b era esperado

Ação típica: desempilhar o terminal da pilha
Resolve o caso de
esquecimento de b
O não-terminal no topo da pilha e o próximo símbolo de
entrada determinam erro na tabela de produções:

Mensagem: a não era esperado

É difícil escolher a melhor ação

Uma das técnicas mais usadas é o uso de um conjunto de
terminais de sincronismo
Descarta-se terminais de
entrada até que se encontre
um desses terminais
Seja sincr a designação
desse terminal
Terminais de sincronismo:

Terminais do Seguinte (A): desempilhar A - o que estiver
abaixo do A na pilha pode ser:
Um terminal também pertencente a Seguinte (A) que
case com sincr
 Um não-terminal que expanda para uma cadeia iniciada
por sincr

Terminais de sincronismo:

Terminais do Primeiro (A): não desempilhar A:
Haverá uma posição sem erro na tabela
 Há possibilidade de retomar a análise

Ainda para não-terminal A no topo da pilha:

Se houver a produção A  ε, pode-se usá-la para expandir A,
retardando o tratamento do erro

Pode-se incluir também, no conjunto de terminais de
sincronismo, palavras reservadas que iniciam comandos

Consultar a literatura para mais detalhes