Carregar apresentação
A apresentação está carregando. Por favor, espere
1
Capítulo V Análise Sintática
CES-41 COMPILADORES Capítulo V Análise Sintática
2
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
3
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
4
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
5
Analisadores top-down e bottom-up:
Varrem o programa da esquerda para a direita, um símbolo de cada vez 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
6
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 top-down
7
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
8
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
9
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
10
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 tem duas árvores sintáticas:
11
Esta regra pode ser incorporada à gramática Gramática equivalente:
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
12
CmdCasado nunca produz IF sem ELSE
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ão-casados entre Expr e ELSE
13
Essa nova gramática dificulta análise preditora:
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
14
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
15
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 ( ); }
16
Expr ( ) { if ( func (átomos analisados e/ou futuros) == TRUE ) { Expr ( ); if (atom != ‘+’) erro (“+ esperado”) 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
17
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
18
(, (N )* e não iniciam com A)
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
19
{A A } {A R R R ε}
Resolve muitos casos, mas não é geral
20
Transformação da gramática:
Exemplo: seja a seguinte gramática, com duas produções contendo recursividade imediata à esquerda: Expr Expr + Termo Termo Termo Termo * Fator Fator Fator ( Expr ) ID Transformação da gramática: Expr Termo Eaux Eaux + Termo Eaux ε Termo Fator Taux Taux * Fator Taux ε Fator ( Expr ) | ID {A A } {A R R R ε} Nas produções de Expr, substituindo: A por Expr por + Termo por Termo R por Eaux De modo análogo, na produção de Termo
21
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 ε
22
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
23
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;
24
Exemplo: Eliminar as recursividades à esquerda da gramática:
S S a T b c T S d U e f U S g U h i Renomeando os não-terminais: S = A1, T = A2, U = A3 A gramática torna-se em: A1 A1 a A2 b c A2 A1 d A3 e f A3 A1 g A3 h i
25
Eliminação das recursividades imediatas à esquerda de A1:
Para i = 1: Eliminação das recursividades imediatas à esquerda de A1: A1 A2 b X c X X a X ε Algoritmo 5.1 A A1 1 2 A 1 A' 2 A' A' 1 A' ε A1 A2 b X c X X a X ε A2 A1 d A3 e f A3 A1 g A3 h i Novo estado das produções
26
Produções de A1 no momento: A1 A2 b X c X
Para i = 2, j = 1: Produções de A2: A2 A1 d A3 e f Produções de A1 no momento: A1 A2 b X c X Substitui-se A1 d por A2 b X d c X d : Produção da forma A2 A1
27
Eliminação das recursividades imediatas à esquerda de A2:
Para i = 2: Eliminação das recursividades imediatas à esquerda de A2: Algoritmo 5.1 A A1 1 2 3 A 1 A' 2 A' 3 A' A' 1 A' ε A2 c X d Y A3 e Y f Y Y b X d Y ε A1 A2 b X c X X a X ε A2 c X d Y A3 e Y f Y Y b X d Y ε A3 A1 g A3 h i Novo estado das produções
28
Produções de A1 no momento: A1 A2 b X c X
Para i = 3, j = 1: Produções de A3: A3 A1 g A3 h i 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 Produção da forma A3 A1
29
c X d Y b X g A3 e Y b X g f Y b X g :
Para i = 3, j = 2: Produções de A3: A3 A2 b X g c X g A3 h i 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 : Produção da forma A3 A2
30
Eliminação das recursividades imediatas à esquerda de A3:
Para i = 3: Eliminação das recursividades imediatas à esquerda de A3: A3 c X d Y b X g Z f Y b X g Z c X g Z i Z Z e Y b X g Z h Z ε A A1 A2 1 2 3 4 A 1 A' 2 A' 3 A' 4 A' A' 1 A' 2 A' ε Algoritmo 5.1
31
Novo estado das produções:
A1 A2 b X c X X a X ε A2 c X d Y A3 e Y f Y Y b X d Y ε A3 c X d Y b X g Z f Y b X g Z c X g Z i Z Z e Y b X g Z h Z ε
32
Restaurando os nomes originais dos não-terminais:
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 a X ε Y b X d Y ε Z e Y b X g Z h Z ε Restaurando os nomes originais dos não-terminais: S T b X c X T c X d Y U e Y f Y U c X d Y b X g Z f Y b X g Z c X g Z i Z S = A1, T = A2, U = A3 Não há mais recursividade a esquerda
33
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’
34
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ão- terminal, permanecendo a indecisão Fatoração é apenas um passo para adequar gramáticas a um método preditor
35
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
36
S(S) me (S) e S(S) md S()
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()
37
Derivação mais à esquerda ou mais à direita: quando todas as derivações diretas forem me ou md
Simbolicamente: *me ou *md No exemplo anterior { S *me ( ) } = { S S(S) (S) ( ) } { S *md ( ) } = { S S(S) S( ) ( ) }
38
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
39
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 )
40
( 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 ( id opad id ) opmult cte opmult T ( id opad id ) opmult cte opmult F ( 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 ) * / ( n – ) Analisadores preditores examinam derivações mais a esquerda
41
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 )
42
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 ) T opmult cte opmult ( id opad cte ) F opmult cte opmult ( id opad cte ) ( 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 ) * / ( n – ) Analisadores bottom-up examinam derivações mais a direita reversas
43
( E ) opmult cte opmult ( id opad cte )
( x y ) * / ( n – ) ( 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 ) F opmult cte opmult ( id opad cte ) T opmult cte opmult ( id opad cte ) 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 Em negrito, lados direitos a serem reduzidos
44
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:
45
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 o 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
46
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 a A d a B A b c B ccd ddc A seguir, análise da sentença: w = a c c d
47
S w = a c c d S a A d a B A b c 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 S w = a c c d
48
Os cursores se casam: a = a
S a A d a B A b c B ccd ddc 2) 1a produção para expandir S 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 S a A d w = a c c d Os cursores se casam: a = a
49
S a A d w = a c c d S a A d a B A b c B ccd ddc
3) Os dois cursores avançam S a A d w = a c c d
50
Os cursores não se casam: b ≠ c
S a A d a B A b c B ccd ddc 4) 1a produção para expandir A 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 S a A d b w = a c c d Os cursores não se casam: b ≠ c
51
Os cursores se casam: c = c
S a A d a B A b c B ccd ddc 5) Elimina-se a subárvore da expansão anterior 2a produção para expandir A Cursor da sentença retrocede para onde A foi expandido, ou seja, permanece onde está: Cursor da árvore avança em pré-ordem S a A d b c w = a c c d Os cursores se casam: c = c
52
Os cursores não se casam: d ≠ c
S a A d a B 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
53
Os cursores se casam: a = a
S a A d a B A b c B ccd ddc 7) Elimina-se a subárvore da expansão anterior Não existe outra produção para expandir A Elimina-se também a subárvore da expansão imediatamente anterior, ou seja, a de S Cursor da sentença retrocede até ao ponto em que S foi expandido 2a produção para expandir S O cursor da árvore avança S a A d c a B w = a c c d Os cursores se casam: a = a
54
S a B c c d w = a c c d S a A d a B A b c B ccd ddc
8) Os cursores avançam 1a produção para expandir B Cursor da árvore avança Casamento nos três últimos movimentos dos cursores: c c d = c c d A sentença é reconhecida w = a c c d
55
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
56
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
57
5.4.2 – Gramáticas LL(k) e LL(1)
Gramáticas LL(k): admitem analisadores sem back-tracking 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
58
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
59
Espectro dos métodos top-down:
Backtracking Sem Backtracking Não Preditores Preditores
60
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
61
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 | ε A seguir: Esquema de programa analisador sintático em C
62
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); }
63
Expressão Termo Eaux Eaux OPAD Expressão | ε void Expressao () { Termo (); Eaux (); } void Eaux () { if (atom.tipo == OPAD) { NovoAtomo (); Expressao ();
64
Termo Fator Taux Taux OPMULT Termo | ε void Termo () { Fator (); Taux (); } void Taux () { if (atom.tipo == OPMULT) { NovoAtomo (); Termo ();
65
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); }
66
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
67
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
68
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
69
Se X = a = $: encerra com sucesso
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 M[X, a] X a Se M[X,a] for um erro: Acionar tratamento de erro Se, por exemplo, M[X,a] for a produção XYZW: Desempilhar X Empilhar W, Z, Y, com Y no topo M[X, a] pode ser uma produção de X ou um erro
70
A saída pode ser a produção usada
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 ); } Senão { /* X é não-terminal */ Se M[X, a] = {XY1Y2Y3 ... Yk} { Desempilhar (X); Empilhar (Yk ... Y3Y3Y1) /* Y1 no topo */ ; Imprimir a produção {XY1Y2Y3 ... Yk};} Senão Erro ( a não era esperado ); } } enquanto (X $) /* enquanto pilha ainda não vazia */ A saída pode ser a produção usada
71
Exemplo: seja a seguinte gramática de expressões:
E T E’ E’ + T E’ | ε T F T’ 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):
72
Programa analisador preditor
Estado inicial: Ação: Expandir Sentença de entrada id + * $ T Programa analisador preditor Saída E’ E E TE’ $ Pilha
73
Programa analisador preditor
Ação: Expandir Sentença de entrada id + * $ F T T’ Programa analisador preditor Saída E TE’ T FT’ E’ E TE’ $ Pilha
74
Programa analisador preditor
Ação: Expandir Sentença de entrada id + * $ id F T’ Programa analisador preditor E TE’ T FT’ F id Saída E TE’ T FT’ E’ $ Pilha
75
Programa analisador preditor
Ação: Desempilhar Avançar Sentença de entrada id + * $ id T’ Programa analisador preditor E TE’ T FT’ F id Saída E’ $ Pilha
76
Programa analisador preditor
Ação: Expandir Sentença de entrada id + * $ E TE’ T FT’ F id T’ ε T’ Programa analisador preditor E TE’ T FT’ F id Saída E’ $ Pilha
77
Programa analisador preditor
Ação: Expandir Sentença de entrada id + * $ E TE’ T FT’ F id T’ ε E’ +T’E’ + E TE’ T FT’ F id T’ ε T Programa analisador preditor Saída E’ E’ $ Pilha
78
Programa analisador preditor
Ação: Desempilhar Avançar Sentença de entrada id + * $ E TE’ T FT’ F id T’ ε E’ +T’E’ + T Programa analisador preditor Saída E’ $ Pilha
79
Programa analisador preditor
Ação: Expandir Sentença de entrada id + * $ E TE’ T FT’ F id T’ ε E’ +T’E’ F E TE’ T FT’ F id T’ ε E’ +T’E’ T’ T Programa analisador preditor Saída E’ $ Pilha
80
Programa analisador preditor
Ação: Expandir Sentença de entrada id + * $ E TE’ T FT’ F id T’ ε E’ +T’E’ E TE’ T FT’ F id T’ ε E’ +T’E’ id F T’ Programa analisador preditor Saída E’ $ Pilha
81
Programa analisador preditor
Ação: Desempilhar Avançar Sentença de entrada id + * $ E TE’ T FT’ F id T’ ε E’ +T’E’ id T’ Programa analisador preditor Saída E’ $ Pilha
82
Programa analisador preditor
Ação: Expandir Sentença de entrada id + * $ E TE’ T FT’ F id T’ ε E’ +T’E’ T’ *FT’ E TE’ T FT’ F id T’ ε E’ +T’E’ * F T’ Programa analisador preditor Saída E’ $ Pilha
83
Programa analisador preditor
Ação: Desempilhar Avançar Sentença de entrada id + * $ E TE’ T FT’ F id T’ ε E’ +T’E’ T’ *FT’ * F T’ Programa analisador preditor Saída E’ $ Pilha
84
Programa analisador preditor
Ação: Expandir Sentença de entrada id + * $ E TE’ T FT’ F id T’ ε E’ +T’E’ T’ *FT’ E TE’ T FT’ F id T’ ε E’ +T’E’ T’ *FT’ id F T’ Programa analisador preditor Saída E’ $ Pilha
85
Programa analisador preditor
Ação: Desempilhar Avançar Sentença de entrada id + * $ E TE’ T FT’ F id T’ ε E’ +T’E’ T’ *FT’ id T’ Programa analisador preditor Saída E’ $ Pilha
86
Programa analisador preditor
Ação: Expandir Sentença de entrada id + * $ E TE’ T FT’ F id T’ ε E’ +T’E’ T’ *FT’ E TE’ T FT’ F id T’ ε E’ +T’E’ T’ *FT’ T’ Programa analisador preditor Saída E’ $ Pilha
87
Programa analisador preditor
Ação: Expandir Sentença de entrada E TE’ T FT’ F id T’ ε E’ +T’E’ T’ *FT’ E’ ε id + * $ E TE’ T FT’ F id T’ ε E’ +T’E’ T’ *FT’ Programa analisador preditor Saída E’ $ Pilha
88
Programa analisador preditor
Ação: Encerrar com sucesso Ação: Desempilhar Avançar Sentença de entrada E TE’ T FT’ F id T’ ε E’ +T’E’ T’ *FT’ E’ ε id + * $ Resta saber como construir a tabela de produções !!! Programa analisador preditor Saída Derivação $ Pilha
89
Então não se pode aplicar um método preditor
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
90
Prepara um casamento entre pilha e entrada
E T E’ 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] Prepara um casamento entre pilha e entrada Exemplos: M[E’, +] = E’ +TE’ M[F, (] = F (E) M[T’, *] = T’ *FT’ M[F, id] = F id
91
É 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 de X em que o primeiro símbolo do lado direito é um não terminal Y: 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’
92
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
93
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}
94
Exemplo: seja = A a B C b c d D + _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ Todas as possíveis e talvez infinitas cadeias só de terminais Como possui terminais, * ε portanto: ε Primeiro () Eliminando-se os terminais repetidos, obtém-se Primeiro()
95
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
96
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)
97
Exemplo: sejam todas as possivelmente infinitas formas sentenciais de uma GLC
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ Em algumas (possivelmente infinitas) o não-terminal A aparece
98
Exemplo: sejam todas as possivelmente infinitas formas sentenciais de uma GLC
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ A _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ A _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ A _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ A _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ A _ _ Colocando-se o finalizador ‘$’ em cada uma
99
Exemplo: sejam todas as possivelmente infinitas formas sentenciais de uma GLC
_ _ _ _ _ _ _ _ _ _ _ _ _ _ $ _ _ _ _ _ A _ _ _ _ _ _ $ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ $ _ _ _ _ _ _ _ A _ _ _ _ $ _ _ _ _ _ _ _ _ _ _ _ _ _ _ A $ _ _ _ _ _ _ _ _ _ _ _ _ $ _ _ _ _ A _ _ _ _ _ _ _ _ $ _ _ _ _ _ _ _ _ _ _ _ _ _ $ _ _ _ _ _ _ _ _ _ A _ _ $ Colocando-se os símbolos que seguem A
100
Exemplo: sejam todas as possivelmente infinitas formas sentenciais de uma GLC
_ _ _ _ _ _ _ _ _ _ _ _ _ _ $ _ _ _ _ _ A B _ _ _ _ _ $ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ $ _ _ _ _ _ _ _ A a _ _ _ $ _ _ _ _ _ _ _ _ _ _ _ _ _ _ A $ _ _ _ _ _ _ _ _ _ _ _ _ $ _ _ _ _ A b _ _ _ _ _ _ _ $ _ _ _ _ _ _ _ _ _ _ _ _ _ $ _ _ _ _ _ _ _ _ _ A D _ $ a, b, $ Seguinte (A)
101
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)
102
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: Inicialização Processo rotativo convergente
103
Algoritmo 5.4: 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) { }; }
104
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); }
105
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)
106
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)
107
Exemplo: seja a gramática: E T E’ E’ + T E’ |
T F T’ T’ * F T’ | F ( E ) | id Inicialização do Algoritmo 5.4: Primeiro (+) = {+} Primeiro (*) = {*} Primeiro (‘(’) = { ( } Primeiro (‘)’) = { ) } Primeiro (id) = {id} Primeiro (E’) = Primeiro (T’) = {} Primeiro (E) = Primeiro (T) = Primeiro (F) = { }
108
T F T’ T’ * F T’ | F ( E ) | id
E T E’ E’ + T E’ | T F T’ 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’) = {, +}
109
T F T’ T’ * F T’ | F ( E ) | id
E T E’ E’ + T E’ | T F T’ 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’) = {, *}
110
T F T’ T’ * F T’ | F ( E ) | id
E T E’ E’ + T E’ | T F T’ 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) = { ( }
111
T F T’ T’ * F T’ | F ( E ) | id
E T E’ E’ + T E’ | T F T’ 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 }
112
T F T’ T’ * F T’ | F ( E ) | id
E T E’ E’ + T E’ | T F T’ 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 }
113
T F T’ T’ * F T’ | F ( E ) | id
E T E’ E’ + T E’ | T F T’ T’ * F T’ | F ( E ) | id Processo rotativo do Algoritmo 5.4, 2a rotação: Primeiro (+) = {+} Primeiro (*) = {*} Primeiro (‘(’) = { ( } Primeiro (‘)’) = { ) } Primeiro (id) = {id} Primeiro (E’) = {, +} Primeiro (T’) = {, *} Primeiro (E) = { } Primeiro (T) = { } Primeiro (F) = { (, id } E T E’ : Primeiro (T) = { } nada acontece E’ + T E’ : Primeiro (+) = {+}, T F T’ : Primeiro (F) = { (, id }, então Primeiro (T) = { (, id }
114
T F T’ T’ * F T’ | F ( E ) | id
E T E’ E’ + T E’ | T F T’ T’ * F T’ | F ( E ) | id Processo rotativo do Algoritmo 5.4, 2a rotação: Primeiro (+) = {+} Primeiro (*) = {*} Primeiro (‘(’) = { ( } Primeiro (‘)’) = { ) } Primeiro (id) = {id} Primeiro (E’) = {, +} Primeiro (T’) = {, *} Primeiro (E) = { } Primeiro (T) = { (, id } Primeiro (F) = { (, id } T’ * F T’ : Primeiro (*) = {*}, nada acontece F ( E ) : Primeiro (‘(’) = { ( }, F id : Primeiro (id) = { id },
115
T F T’ T’ * F T’ | F ( E ) | id
E T E’ E’ + T E’ | T F T’ 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 }
116
T F T’ T’ * F T’ | F ( E ) | id
E T E’ E’ + T E’ | T F T’ T’ * F T’ | F ( E ) | id Processo rotativo do Algoritmo 5.4, 3a rotação: Primeiro (+) = {+} Primeiro (*) = {*} Primeiro (‘(’) = { ( } Primeiro (‘)’) = { ) } Primeiro (id) = {id} Primeiro (E’) = {, +} Primeiro (T’) = {, *} Primeiro (E) = { } Primeiro (T) = { (, id } Primeiro (F) = { (, id } E T E’ : Primeiro (T) = { (, id } Primeiro (E) = { (, id }
117
T F T’ T’ * F T’ | F ( E ) | id
E T E’ E’ + T E’ | T F T’ T’ * F T’ | F ( E ) | id Processo rotativo do Algoritmo 5.4, 3a rotação: Primeiro (+) = {+} Primeiro (*) = {*} Primeiro (‘(’) = { ( } Primeiro (‘)’) = { ) } Primeiro (id) = {id} Primeiro (E’) = {, +} Primeiro (T’) = {, *} Primeiro (E) = { (, id } Primeiro (T) = { (, id } Primeiro (F) = { (, id } Com as outras produções nada acontece
118
T F T’ T’ * F T’ | F ( E ) | id
E T E’ E’ + T E’ | T F T’ 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 }
119
T F T’ T’ * F T’ | F ( E ) | id
E T E’ E’ + T E’ | T F T’ T’ * F T’ | F ( E ) | id Processo rotativo do Algoritmo 5.4, 4a rotação: Primeiro (+) = {+} Primeiro (*) = {*} Primeiro (‘(’) = { ( } Primeiro (‘)’) = { ) } Primeiro (id) = {id} Primeiro (E’) = {, +} Primeiro (T’) = {, *} Primeiro (E) = { (, id } Primeiro (T) = { (, id } Primeiro (F) = { (, id } Nada acontece Ficam determinados os Primeiro’s de todos os símbolos
120
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 () {};
121
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
122
Algoritmo 5.6: Calcula Seguinte (A), onde A é um não-terminal
O cálculo é feito simultaneamente para todos os não-terminais da gramática O algoritmo tem 3 etapas Inicialização trivial Inicialização não-trivial Processo rotativo convergente
123
Algoritmo 5.6: 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
124
Comentários: S * $ Seguinte (S) _ _ _ _ _ _ _ _ _ _ _ _ _ _ $
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ $ _ _ _ _ _ _ _ _ _ _ _ _ $ S $ _ _ _ _ _ _ _ _ _ _ _ _ _ $ $ Seguinte (S)
125
Inicialização não-trivial 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 )*); Acrescentar (Primeiro (i ) - {}) a Seguinte (Bi) ; } Pelo menos um não-terminal do lado direito
126
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 _ _ _ _ $ E haverá outra do tipo _ _ _ _ _ _ _ _ _ _ _ _ i Bi i _ _ _ _ $ Então (Primeiro (i) – {}) Seguinte (Bi) i pode ser vazio ou derivar a cadeia vazia
127
3. 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 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) ; } Pelo menos um não-terminal do lado direito
128
Comentários: seja a produção A i Bi i
Se a, b, c, $ Seguinte (A), há formas sentenciais dos tipos _ _ _ _ _ _ A a _ _ _ $ _ _ _ _ A b _ _ _ $ _ _ _ _ _ A c _ _ _ $ _ _ _ _ _ _ A $ E também dos tipos _ _ _ _ _ _ i Bi i a _ _ _ $ _ _ _ _ i Bi i b _ _ _ $ _ _ _ _ _ i Bi i c _ _ _ $ _ _ _ _ _ _ i Bi i $ 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 Então, acrescentar a, b, c, $ ao Seguinte (Bi) Ou seja, acrescentar Seguinte (A) ao Seguinte (Bi)
129
Exemplo: seja a mesma gramática:
E T E’ E’ + T E’ | T F T’ T’ * F T’ | F ( E ) | id Seja a tabela dos Primeiro’s de seus não-terminais calculada no exemplo anterior:
130
Inicialização trivial do Algoritmo 5.6:
Seguinte (E) = {$} Seguinte (E’) = { } Seguinte (T) = { } Seguinte (T’) = { } Seguinte (F) = { }
131
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’) – {ε}) = { } ({+, ε} – {ε}) = {+}
132
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’) – {ε}) = { } ({+, ε} – {ε}) = {+} Seguinte (E’) = Seguinte (E’) (Primeiro (ε) – {ε}) = { } ({ε} – {ε}) = { }
133
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’) – {ε}) = {+} ({+, ε} – {ε}) = {+} Seguinte (E’) = Seguinte (E’) (Primeiro (ε) – {ε}) = { } ({ε} – {ε}) = { }
134
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’) – {ε}) = { } ({*, ε} – {ε}) = {*}
135
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’) – {ε}) = { } ({*, ε} – {ε}) = {*} Seguinte (T’) = Seguinte (T’) (Primeiro (ε) – {ε}) = { } ({ε} – {ε}) = { }
136
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’) – {ε}) = {*} ({*, ε} – {ε}) = {*} Seguinte (T’) = Seguinte (T’) (Primeiro (ε) – {ε}) = { } ({ε} – {ε}) = { }
137
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 (‘)’) – {ε}) = {$} ({ ) } – {ε}) = {$, )}
138
Inicialização não-trivial do Algoritmo 5.6:
Seguinte (E) = {$, )} Seguinte (E’) = { } Seguinte (T) = {+} Seguinte (T’) = { } Seguinte (F) = {*}
139
Final da inicialização não-trivial do Algoritmo 5.6:
Seguinte (E) = {$, )} Seguinte (E’) = { } Seguinte (T) = {+} Seguinte (T’) = { } Seguinte (F) = {*}
140
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) = {+} {$, )} = {+, $, )}
141
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) = { } {$, )} = {$, )}
142
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) = {*} {+, $, )} = {*, +, $, )}
143
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) = { } {+, $, )} = {+, $, )}
144
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’) = {+, $, )} {+, $, )} = {+, $, )}
145
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 (‘)’))
146
Final da 1a rotação do processo rotativo do Algoritmo 5.6:
Seguinte (E) = {$, )} Seguinte (E’) = {$, )} Seguinte (T) = {+, $, )} Seguinte (T’) = {+, $, )} Seguinte (F) = {*, +, $, )}
147
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
148
5.4.6 – Construção da tabela de produções
Seja A um não-terminal e a um terminal
149
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 sub-sentenças iniciadas por a, para casar com o a na entrada
150
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
151
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ão-terminal que gere algo iniciado por a
152
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.
153
Exemplo: seja a mesma gramática:
E T E’ E’ + T E’ | T F T’ T’ * F T’ | F ( E ) | id Tabelas dos Primeiro’s e Seguinte’s já calculadas:
154
E T E’ E’ + T E’ | T F T’ T’ * F T’ | F ( E ) | id Tabela de produções:
155
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
156
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
157
Exemplo: seja a gramática para if-else: S i b S S’ | a S’ e S |
Tabelas: M[S’, e] tem duas produções Tipicamente decide-se por S’ e S O else corresponde ao último if
158
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
159
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
160
Mensagem: a não era esperado É difícil escolher a melhor ação
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
161
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
162
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
163
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
164
Pode-se incluir também, no conjunto de terminais de sincronismo, palavras reservadas que iniciam comandos Consultar a literatura para mais detalhes
Apresentações semelhantes
© 2024 SlidePlayer.com.br Inc.
All rights reserved.