• Nenhum resultado encontrado

Capítulo 5 Análise sintática

N/A
N/A
Protected

Academic year: 2021

Share "Capítulo 5 Análise sintática"

Copied!
183
0
0

Texto

(1)

Capítulo 5 – Análise sintática

1. Objetivo

2. Estratégias gerais de parsing

3. Análise sintática descendente (top-down)

3.1. Analisador sintático com retrocesso (backtracking)

3.2. Analisador sintático predicativo recursivo

3.3. Analisador sintático predicativo não recursivo – Parser LL

3.4. Construção de tabelas sintáticas predicativas (Tabelas Oráculo)

4. Análise sintática ascendente (bottom-up)

4.1. Parser por transição-redução (método geral)

4.2. Parser LR

(2)
(3)

Gramática da linguagem

árvore de sintaxe Sequência

de símbolos PARSER Aceitaçãoou Erro

Dado

- uma gramática livre do contexto G

- uma sequência a de símbolos terminais (frase)

pretende-se

- verificar se a é uma frase válida de L(G)

(4)

a  a a a em que:

a → sequência já percorrida

a → próximo símbolo a analisar (token)

a → sequência por analisar a é uma frase de L(G) se e só se

(5)

Em cada momento do processo, - ou existe uma derivação

*

S  m a d , com m  T* e d  (S  T)*

onde m  a . { Fase de possível aceitação }

(6)
(7)

Existem duas estratégias gerais de parsing:

- reconhecimento descendente (“top-down”) - reconhecimento ascendente (“bottom-up”).

Reconhecimento descendente (“Top-Down”)

- A árvore é construída da raiz para as folhas

- Em cada vértice (com um símbolo não terminal A):

- selecionar uma produção com A à esquerda e construir os vértices filhos de A com os símbolos à direita nessa produção;

- selecionar o vértice onde continuar.

- Termina quando todas as folhas são símbolos terminais. - A aceitação é obtida se a sequência a for esgotada.

(8)

- Os símbolos de a são associados até se reconhecer o lado direito de uma produção. - A aceitação é obtida se,

(9)

Análise sintática

(10)

- construir uma árvore gramatical para a a partir da raiz, criando os nós em pré-ordem.

Desta forma,

- a frase a é percorrida da esquerda para a direita, - vão-se identificando derivações esquerdas,

(11)

1. Análise sintática descendente com retrocesso (backtracking)

O reconhecimento é feito por expansão de regras sintáticas, substituindo - símbolos não-terminais do lado esquerdo de produções,

- pelos símbolos do lado direito das produções. Depois, a expansão das regras sintáticas é confirmada

- por emparelhamento

- dos símbolos da frase de entrada (a)

- com os símbolos terminais das regras sintáticas. - se não emparelhar,

então terá que haver retrocesso no processo (“backtracking”).

(12)

Saída: Árvore de derivação

1. Criar um nó com o símbolo inicial da gramática.

2. Substituir o símbolo não-terminal situado mais à esquerda da árvore de derivação pelos símbolos do lado direito da produção cujo lado esquerdo é o símbolo substituído.

Este passo termina quando o primeiro símbolo da produção for um símbolo terminal.

3. Se o primeiro símbolo ainda não processado de a não emparelhar com o primeiro símbolo da produção selecionada no passo anterior, recuar para o passo 2 e pesquisar outra alternativa (“backtrack”); se não existir outra alternativa, o algoritmo termina sem sucesso.

4. Emparelhar, da esquerda para a direita, os símbolos de a com os símbolos terminais nas folhas da árvore de derivação, até que aconteça uma das três hipóteses:

- o emparelhamento de símbolos falha  terminar sem sucesso;

(13)

Vantagens:

(14)

analisador entra em ciclo infinito de expansão duma produção recursiva à esquerda - A ordem da escolha das alternativas determina a linguagem aceite pelo analisador;

por ex., a frase aa é aceite pelo analisador se for expandida em primeiro a produção S ® B, o que não acontece se a primeira produção a ser expandida for S ® aA, pois o primeiro símbolo da entrada, a, emparelha com o primeiro símbolo terminal da produção S ® aA, mas o segundo símbolo de entrada, a, não pode ser derivado a partir de A.

- Em caso de entrada com erro, não é possível identificar as causas de erro, pois não há distinção entre retrocesso por erro e por necessidade de pesquisa de alternativas - A operação de retrocesso é muito demorada, degradando o desempenho

(15)

2. Analisador sintático predicativo recursivo

Definição:

Um Parser recursivo-descendente diz-se profético (ou predicativo) quando:

- a análise do a, ou lookahead, determina univocamente a produção a utilizar;

- a execução de um procedimento simula a sequência de símbolos do lado direito dessa produção.

Um Parser predicativo é caracterizado por tomar sempre decisões irrevogáveis, isto é, sem backtracking.

Para se construir um analisador predicativo, é preciso:

- dado o lookahead a (a = a) e o símbolo não terminal A a ser expandido,

(16)

Algoritmo:

{ X Î T } Reconhecer_X:

Se (X = a) Então

avançar (a) { reconhecimento parcial } Senão ERRO

{ X Î S } Reconhecer_X:

{ em função de a, escolher uma produção p : X ® X1 X2 ... Xn } Reconhecer_X1

(17)

Partindo do símbolo inicial (S) e do início de a: Parser_RD: Avançar (a); Reconhecer_S ;  Se ( a = e) Então RECONHECIMENTO Senão ERRO

Para a construção de um Parser predicativo é necessário estabelecer um método que, - conhecidos o lookahead (a) e o símbolo a reconhecer,

- determine univocamente a produção a utilizar.

Uma das formas é utilizar uma tabela sintática (Tabela Oráculo), M[X,a] = p, que significa: a produção p será aplicada se pretender-se expandir o símbolo não terminal

(18)

- constituído por uma única função, onde o símbolo é passado como parâmetro; - eficiente; Reconhecer(X): Se (X Î T) Então Se (X = a) Então Da_Simbolo (a) Senão ERRO

Senão np ¬ Oráculo(X, a) Se (np ¹ ERRO) Então

(19)

- ainda é recursivo;

- mas é mais cómodo e mais independente da linguagem: a informação relativa à linguagem está guardada em duas estruturas de dados.

Otimização da Função Oráculo:

ORÁCULO : (S È T) x T ® { skip, np, erro } (X, a) ® ação

Se (X Î T e X = a) Então

(20)

p' p'' Situação de conflito

Reconhecer (X):

ação ¬ ORÁCULO (X, a) Caso (ação) Seja

erro : ERRO

skip : Da_Simbolo(a)

np : Para (" Y Î Produção[np]) Fazer Reconhecer (Y)

(21)

Analisador sintático

(22)

- uma frase de entrada, - uma pilha,

(23)

A frase de entrada é seguida por um $ à direita (indica o fim da sequência de símbolos) A pilha Z:

- contém uma sequência de símbolos gramaticais, - com $ a indicar o fundo da pilha;

- inicialmente, a pilha contém o símbolo inicial da gramática acima de $. A tabela sintática é um “array” bidimensional M[A, a] onde,

- A é um símbolo não terminal, terminal ou $, - a é um símbolo terminal ou $.

O analisador sintático é um programa que se comporta da seguinte forma:

(24)

2. Se X = a (a é terminal), o analisador sintático remove X da pilha e avança o

apontador da entrada para o próximo símbolo (próximo lookahead), a = a.

3. Se X é um não terminal, o programa consulta a entrada M[X, a] da tabela

sintática M, que será uma produção-X (X ® a) da gramática ou ERRO.

Por ex., se M[X,a] = { X ® UVW } substitui-se X no topo da pilha por WVU (com U no topo); se M[X, a] = erro chama-se uma rotina de recuperação de erros.

Algoritmo: Análise sintática predicativa não recursiva

Entrada: Uma frase a e uma tabela sintática M para a gramática G = (S, T, P, S). Saída: Uma derivação mais à esquerda (se a  L(G)) ou uma indicação de erro.

(25)

Repetir

Se (Topo(Z) Î T) Então

Se (Topo(Z) = a) Então Pop(Z)

Da_Simbolo(a) Senão ERRO

Senão

Se (Topo(Z) Î S) Então

Se M[Topo(Z), a] = { X ® Y1Y2 … Yn } Então Pop(Z)

Para i desde n até 1 Fazer Push(Z, Yi)

Senão ERRO

(26)

a : sequência de símbolos terminais já analisados

Z : sequência de símbolos terminais e não terminais a reconhecer, mantém-se invariante: *  S  a Z  no início : a = e e Z = S 

e no fim : a = a e Z = e { se tudo correr bem } Deste modo:

- não é necessário construir a árvore de derivação 

(27)

A este analisador sintático dá-se o nome de Parser LL (scan input from Left to right and construct Leftmost derivation).

Falta apenas incluir uma ação de aceitação: Ação = { skip, np, erro, ac }. A aceitação ocorre quando:

(Z = $) e (a = $)

(28)

Push(Z, S)

Da_Simbolo(a)

Repetir

Acção ¬ Oráculo(Topo(Z), a); Pop(Z);

Caso Acção Seja erro : Erro; ac : ;

skip : Da_Simbolo(a);

(29)
(30)

- logo, o analisador sintático irá expandir A através de a quando o símbolo de entrada corrente (lookahead) for a (a = a).

 A única complicação ocorre quando a = e ou a  e.

Neste caso, deve-se expandir A de novo através de a se: - a Î Follow(A) ou

(31)

Algoritmo:

Entrada : Gramática G

Saída : Tabela Oráculo M[X, Y] em que, X  (S  T  { $ }) e

Y  (T  { $ }) Descrição do método:

1. Para cada produção A ® a de G, executar os passos 2 e 3.

2. Para cada símbolo terminal a Î First(a), fazer M[A, a] = A ® a.

3. Se e Î First(a), então para cada b Î Follow(A), fazer M[A, b] = A ® a.

Se e Î First(a) e $ Î Follow(A), fazer M[A, $] = A ® a.

4. Fazer a cada entrada da tabela M do tipo M[x, x] = SKIP, em que x  T. 5. Fazer à entrada da tabela M, M[$, $] = AC.

6. Fazer a cada entrada indefinida da tabela M, M[k, j] = ERRO.

Os passos 2 e 3 podem ser substituídos pelo seguinte único passo:

(32)
(33)
(34)
(35)

1) S DC→

(36)
(37)

3) A → Cb

(38)
(39)
(40)
(41)
(42)
(43)

9) D A→ B

(44)
(45)
(46)

Push(Z, S)

(47)
(48)
(49)
(50)
(51)
(52)

Push(Z, e)

Da_Simbolo(a)

(53)
(54)

Da_Simbolo(a)

(55)
(56)

Push(Z, f)

(57)
(58)

Da_Simbolo(a)

(59)

e b d f b e $

Pop(Z)

Push(Z, e)

(60)

Da_Simbolo(a)

(61)

Análise Sintática

(62)

O objetivo da análise sintática ascendente é a construção da árvore de derivação, a partir das folhas, em direção à raiz.

A árvore de derivação é construída percorrendo a sequência a de símbolos de entrada (frase) da esquerda para a direita (e armazenada numa string s): esta operação é designada por deslocamento (“shift”) da posição do analisador sintático.

(63)

Exemplo: Considere-se a gramática com as seguintes 6 produções :

1) S ® A B c 2) S  B A c 3) A ® a 4) A  a A 5) B ® b 6) B  b B

A frase abbc pode ser reduzida até ao símbolo inicial da gramática S em 4 etapas: S Þ A B c Þ A b B c Þ A b b c Þ a b b c

Etapa Símbolos GramaticaisSequência de Regra GramaticalReduzida

1 a b b c 3

2 A b b c 5

3 A b B c 6

4 A B c 1

5 S

(64)

s = m b e p : A  b  s = m A Redução de b em A pela produção p Cada redução corresponde a uma subida de nível na árvore de derivação.

p : A ® X Y … Z A

m X Y … Z depois disso, se :

(65)

Um parser “bottom-up” é caracterizado por dois tipos de operações básicas: Redução: s = m b  s = m A

Transição: s = m a e Avançar( ) No início do processo: s = e e = a

No fim do processo: s = S e = e { Aceitação } Num parser “top-down”:

No início do processo: s = S e = a

(66)

- Ou não existe nenhuma derivação a efetuar: estado de erro.

Assumindo (por enquanto) que todas as decisões podem ser tomadas apenas em função dos valores de S e de a, define-se:

(67)

Parser_Trans_Red:

Inicializar(s) ; Da_Simbolo(a) ; Repetir

ação ¬ ORÁCULO(s, a) ; Caso ação Seja

(68)
(69)
(70)

s a Ação

a + ( b * c ) $ Shift a + ( b * c ) $ Reduce p4

(71)
(72)
(73)
(74)
(75)
(76)
(77)
(78)
(79)
(80)
(81)
(82)
(83)

3. Parser LR

Os analisadores sintáticos LR apresentam diversas vantagens:

- Reconhecem praticamente todos os construtores de linguagens de programação estruturadas em blocos, expressas em gramáticas independentes do contexto.

- São mais gerais que outros analisadores sintáticos e revelam o mesmo grau de eficiência.

- Podem detetar cedo erros sintáticos, quando tal é possível, ao pesquisar a entrada da esquerda para a direita.

Maior inconveniente destes analisadores: - a complexidade da sua implementação.

(84)

“Rightmost derivation”, derivação da direita para a esquerda,

- pesquisa os elementos da frase da esquerda para a direita (tal como os analisadores descendentes) e

(85)

Arquitetura do analisador sintático LR

Entrada Frase$

Pilha Reconhecedor Saída

(86)

As duas tabelas do analisador sintático ascendente LR são designadas por: - tabela de ações (“actions”) e

- tabela de saltos (“goto”).

A tabela de ações A[estado, entrada] é indexada pelo estado e pelo símbolo de entrada (terminal), e possui um dos quatro valores :

1. Desloca s (“shift”), na qual s é um estado.

2. Reduz n (“reduce”), na qual n é a referência a uma produção X → a. 3. Aceita (“accept”).

(87)

A tabela de saltos G[estado, símbolo]:

- é indexada pelo estado e pelo símbolo não-terminal,

- possui, nalgumas células, uma indicação da referência a um estado.

A construção das tabelas pode ser efetuada utilizando três metodologias distintas:

1. SLR (“Simple LR”) — é o método mais simples, mas também o menos poderoso, porque não é possível construir as tabelas para algumas gramáticas.

2. LR canónico — é o método mais poderoso e, em simultâneo, o mais complexo, sendo aquele que mais memória necessita para armazenar a tabela.

(88)

Um parser LR implementa o autómato determinístico de pilha, capaz de reconhecer uma dada gramática do tipo LR(k), onde:

- os estados do autómato “representam” valores da string s - cada transição: s  s a

corresponde à determinação do próximo estado, em função do atual e de a T∈

- cada redução:

s  s − b determina o estado anterior quando do atual se retira b (∈ S T)*,∪ s  s A determina o próximo estado em função do atual e de A ∈ S

- transições são implementadas por uma tabela chamada Oráculo_T ou Ação: Oráculo_T : Q × T  { shift, reduce, ac, erro } × (Q P) ∪

(89)

- os estados anteriores são recuperados se, numa pilha F de estados forem efetuados Pop’s em número igual ao dos símbolos em β

- mudanças de estado por redução são implementadas por um Oráculo_S ou Goto: Oráculo_S : Q × S  Q

(90)

Inicializar(F) ; Da_Simbolo(a) ; Repetir

acção  Oráculo_T(Topo(F), a) ; Caso ação Seja

erro: ERRO ; ac: ; shift: q  ação.estado ; Push(F, q) ; Da_Simbolo(a) ; reduce: np  ação.Num_prod ; (A, nsd)  Prod[np] ;

Para i desde 1 até nsd Fazer Pop(F) ;

(91)

- Como associar estados a valores de s ? - Como construir os Oráculos ?

- Será sempre possível ?

Cada passo do processo LR é caracterizado por : 

( s, a ) CONFIGURAÇÃO LR

Cada decisão é tomada em função de:

A  d.b produção em reconhecimento e ponto (.) de decisão; w o prefixo de s, com s = w d;

(92)

Uma configuração ( s, a ) satisfaz a condição não-erro LR se : * 

b m  a

A cada configuração não-erro, está associado um e um só item LR. No início : (e, a)  [ e, Z  .S$, e ]

(93)

Só existem três formas de itens LR (a que correspondem três tipos de ações): 1) [ ω, A  d.ab, m ]

a que corresponde a transição: (wd , ar)  (wd a, r)

s

2) [ ω, A  d.Bb, m ]

a ação a tomar depende de B, e virá a ser tomada em função de [ wd, B  .g, bm ]

3) [ w, A  d.e, m ]

a que corresponde a redução (wd , m)  (wA, m)

r

A cada configuração (s, a ) está associado um item [ w, A  d.b, m ] que determina 

(94)

Para um dado item LR [ w, A  d.b, m ] chama-se item LR(k) a [ A  d.b, First(k, m) ]

Os estados de um autómato LR(k) são constituídos por itens LR(k). Num autómato LR(0) os estados são [ A  d.b ].

Exemplo:

Construir o autómato não determinístico LR(0) para a gramática G com as seguintes produções:

1) Z S $→ 2) S a→

(95)
(96)
(97)
(98)
(99)
(100)
(101)
(102)
(103)
(104)
(105)
(106)
(107)
(108)
(109)
(110)
(111)

Algoritmo para construir o autómato determinístico:

Alfabeto : S T ∪

Estado inicial : [ Z  . S $, e ] Cada estado é formado por :

- Um item LR(k) : [ A  d . b , n ]

- Pelo seu fecho, isto é, todos os itens da forma

[ B  . g, m ] sempre que b = B r, com m = First(k, r n) - E pelo fecho de cada um desses itens.

A função de transição : D = Q × (T ∪ S)  Q

para cada item LR(k) associado ao estado q [ A → d . B r, n ] ∀ B T ∈ ∪ S

D (q, B) = q’ com q’ = [ A  d B . r, n ]

(112)
(113)
(114)
(115)
(116)
(117)
(118)
(119)
(120)
(121)
(122)
(123)
(124)
(125)
(126)
(127)

NOTAS:

- Uma redução corresponde a recuar no grafo (com o conhecimento da produção).

{ redução por S → bAA }

(128)

{ redução por A → e } { redução por S → bAA }

{ redução por A → Aa } Por exemplo, ao estado 5 estão associados:

[ A → e, m ] e [ A → A.a, a ] Se a First(∈ m) é impossível decidir:

redução por A → e, ou

transição para [ A → Aa., a ].

(129)

A partir do autómato determinístico A constroem-se as Tabelas Oráculo, da seguinte forma:

-  q  Q,  X  S

Oráculo_S (q, X) = sq', se  (q, q')X  A Oráculo_S (q, X) = erro, caso contrário -  q  Q,  t  T

Oráculo_T (q, t) = sq', se  (q, q')t  A

Oráculo_T (q, t) = rp, se  [ A → a., n ] : t  First(k, n) Oráculo_T (q, t) = erro, caso contrário

(130)

a a b $ S A e s t a d o s 1 s3 s4 erro s2 erro

2 erro erro AC erro erro

(131)

Construir o autómato determinístico LR(1) para a mesma gramática: First(S) = { a, b }

(132)
(133)

1 [ Z  .S$,  ]

(134)
(135)

1 [ Z  .S$,  ] S 2 [ Z  S.$,  ] AC ( = $)

[ S  .a, $ ]

(136)

[ S  .a, $ ]

[ S  .bAA, $ ] a 3 [ S  a., $ ] red#2 ( = $) b

(137)

1 [ Z  .S$,  ] S 2 [ Z  S.$,  ] AC

[ S  .a, $ ]

[ S  .bAA, $ ] a 3 [ S  a., $ ] red#2 ( = $) b

4 [ S  b.AA, $ ]

[ A  ., a/$ ] First(A$) = { a, $ } [ A  .Aa, a/$ ] First(A$) = { a, $ } red#4

(138)

[ S  .a, $ ]

[ S  .bAA, $ ] a 3 [ S  a., $ ] red#2 ( = $) b

5 [ S  bA.A, $ ] 4 [ S  b.AA, $ ] A [ A  A.a, a/$ ]

[ A  ., a/$ ] [ A  .Aa, a/$ ] red#4

(139)

1 [ Z  .S$,  ] S 2 [ Z  S.$,  ] AC

[ S  .a, $ ]

[ S  .bAA, $ ] a 3 [ S  a., $ ] red#2 ( = $) b

5 [ S  bA.A, $ ] 4 [ S  b.AA, $ ] A [ A  A.a, a/$ ]

[ A  ., a/$ ] red#4 [ A  ., $ ] First($) = { $ } [ A  .Aa, a/$ ] ( = $) [ A  .Aa, $ ] First($) = { $ } red#4

(140)

[ S  .a, $ ]

[ S  .bAA, $ ] a 3 [ S  a., $ ] red#2 ( = $) b

5 [ S  bA.A, $ ] 4 [ S  b.AA, $ ] A [ A  A.a, a/$ ]

[ A  ., a/$ ] red#4 [ A  ., $ ] First($) = { $ } [ A  .Aa, a/$ ] ( = $) [ A  .Aa, $ ] First($) = { $ } red#4

( = a/$) ( = a)red#4 [ A  ., a ] [ A  .Aa, a ]

(141)

1 [ Z  .S$,  ] S 2 [ Z  S.$,  ] AC

[ S  .a, $ ]

[ S  .bAA, $ ] a 3 [ S  a., $ ] red#2 ( = $) b

5 [ S  bA.A, $ ] 4 [ S  b.AA, $ ] A [ A  A.a, a/$ ]

[ A  ., a/$ ] red#4 [ A  ., a/$ ] [ A  .Aa, a/$ ] (= a/$) [ A  .Aa, a/$ ] red#4

(142)

[ S  .a, $ ]

[ S  .bAA, $ ] a 3 [ S  a., $ ] red#2 ( = $) b

5 [ S  bA.A, $ ] 4 [ S  b.AA, $ ] A [ A  A.a, a/$ ]

[ A  ., a/$ ] red#4 [ A  ., a/$ ] [ A  .Aa, a/$ ] (= a/$) [ A  .Aa, a/$ ] red#4

( = a/$) a

(143)

1 [ Z  .S$,  ] S 2 [ Z  S.$,  ] AC

[ S  .a, $ ]

[ S  .bAA, $ ] a 3 [ S  a., $ ] red#2 ( = $) b

5 [ S  bA.A, $ ]

A 7 [ S  bAA., $ ]

4 [ S  b.AA, $ ] A [ A  A.a, a/$ ] [ A  A.a, a/$ ]

[ A  ., a/$ ] red#4 [ A  ., a/$ ] red#3 ( = $) [ A  .Aa, a/$ ] (= a/$) [ A  .Aa, a/$ ]

red#4

( = a/$) a

(144)

[ S  .a, $ ]

[ S  .bAA, $ ] a 3 [ S  a., $ ] red#2 ( = $) b

5 [ S  bA.A, $ ]

A 7 [ S  bAA., $ ]

4 [ S  b.AA, $ ] A [ A  A.a, a/$ ] [ A  A.a, a/$ ]

[ A  ., a/$ ] red#4 [ A  ., a/$ ] red#3 ( = $) [ A  .Aa, a/$ ] (= a/$) [ A  .Aa, a/$ ] a

red#4

( = a/$) a

(145)

1 [ Z  .S$,  ] S 2 [ Z  S.$,  ] AC

[ S  .a, $ ]

[ S  .bAA, $ ] a 3 [ S  a., $ ] red#2 ( = $) b

5 [ S  bA.A, $ ]

A 7 [ S  bAA., $ ]

4 [ S  b.AA, $ ] A [ A  A.a, a/$ ] [ A  A.a, a/$ ]

[ A  ., a/$ ] red#4 [ A  ., a/$ ] red#3 ( = $) [ A  .Aa, a/$ ] (= a/$) [ A  .Aa, a/$ ] a

red#4

( = a/$) a

6 [ A  Aa., a/$ ] red#5 ( = a/$)

(146)

a b $ S A e s t a d o s 1 s3 s4 erro s2 erro

2 erro erro AC erro erro

3 erro erro r2 erro erro

4 r4 erro r4 erro s5

5 s6 erro r4 erro s7

6 r5 erro r5 erro erro

7 s8 erro r3 erro erro

8 erro erro r5 erro erro

(147)

Conflito redução/transição

Resumindo e concluindo: - Um item LR(k) da forma

[ A → a.Xb, m ]

determina uma transição por X - Um item LR(k) da forma

[ A → a., m ]

determina uma redução por A → a para todo o a First(k, ∈ m). As possíveis situações de conflito são

- Conflito redução-transição

ao mesmo estado estão associados 2 itens LR(k) da forma: [ A → a.ab, m ]

(148)

[ A → a., m ] e [ B → b., n ]

com First(k, m)  First(k, n) ≠ .

(149)

Exercício:

(150)
(151)

Construção do autómato LR(1):

First(S) = First(E) = First(T) = { (, id } Follow(S) = { $ }

(152)

- (Goto) → símbolos não-terminais: S, E, T - Não ocorrem reduções no estado 1

(153)
(154)

surgem na maioria das linguagens.

- As exigências de memória destes autómatos são aceitáveis.

- O autómato LR(1) contem informação suficiente para resolver os conflitos na maioria das linguagens.

- A memória necessária neste autómatos é impraticável.

(155)

4. Parser SLR(1) — Simple LR(1)

- Os seus estados são basicamente os mesmos que os do autómato LR(0)

- A certos items é associada informação “necessária” para identificar reduções - A cada item LR(0) da forma:

[ A → a. ] é associado o Follow(A)

Haverá redução por A → a se a  Follow(A) - O correspondente item LR(1) seria:

(156)
(157)
(158)
(159)

Tabela Oráculo SLR(1):

a a b c $ S A e s t a d o s

1 erro s3 erro erro s2 erro

2 erro s4 erro AC erro erro

3 s6 erro erro erro erro s5

4 erro r1 r1 r1 erro erro

5 s7 erro erro erro erro erro

6 r5 s3 erro erro s8 erro

7 erro r2 r2 r2 erro erro

8 erro s10 s9 erro erro erro

9 r3 erro erro erro erro erro

10 r4 r1 r1 r1 erro erro

(160)

O método SLR(1) resolve conflitos LR(0) quando Conflito redução-transição: quando a Ï Follow(B) [ A ® a.ab ] [ B ® d. ] Conflito redução-redução:

quando Follow(A)  Follow(B) = Æ [ A ® a. ]

(161)

5. Parser LALR(1) — LookAhead LR(1)

- Ainda os mesmos estados que os autómatos LR(0). - Com mais informação associada a cada item

- Cada item LR(0) da forma [ A ® a. ] do estado q é substituído por [ A ® a., n ], onde n = L (q, A ® a.)

- Cada item LR(0) da forma [ A ® a.Xb ] do estado q é substituído por [ A ® a.Xb, n ], onde n = L (q, A ® a.Xb)

- Deste modo, a redução num estado com o item [ A ® a., n ]

(162)
(163)

Cálculo dos L() LALR(1):

Regra 1: Propagação por transição de estados :

L(q’, A → aX.b) = È L(q, A → a.Xb)

[A → a . X b ] Î q

Uma transição de q para q’ pelo símbolo X, propaga o L() do item [A → a.Xb] para o item [A → aX.b].

Regra 2: Concatenação por cálculo do fecho [A → a.Br]

---[B → .d]

L(q, B → .d) = È First(r L(q, A → a.Br))

[A → a . B r ] Î q

(164)

L(qf, Z → S.$) = { $ }

(165)

Exemplo:

Verificar se a gramática G = ( { S, L, R }, { id, =, * }, S, P ) é LALR(1), em que P contém as seguintes produções:

p0: Z S$→ (não faz parte de G) p1: S L=R→

p2: S R→ p3: L *R→ p4: L id→ p5: R L→

(166)
(167)
(168)
(169)
(170)
(171)
(172)
(173)
(174)
(175)
(176)
(177)
(178)

a id = * $ S L R e s t a d o s 1 s5 erro s6 erro s2 s4 s3

2 erro erro erro AC erro erro erro

3 erro erro erro r2 erro erro erro

4 erro s7 erro r5 erro erro erro

5 erro r4 erro r4 erro erro erro

6 s5 erro s6 erro erro s9 s10

7 s5 erro s6 erro erro s9 s8

8 erro erro erro r1 erro erro erro

9 erro r5 erro r5 erro erro erro

(179)

Relação entre LL(k), LR(k), SLR(k) e LALR(k):

- LL(k) é o parser mais simples e, consequentemente, o com mais problemas, pos nem todas as gramáticas podem ser tratadas com este parser (as recursivas)

- As fraquezas do parser LL(k) são superadas pelo parser LR(k)

- O parser SLR(1) – LR(1) simples; aumenta o poder de análise do LR(0)

- O parser LR(1) aumenta o poder de análise do SLR(1), pois ajuda a resolver conflitos redução-redução e redução-transição

(180)

Seja a gramática com as seguintes produções: A ( A )→

(181)
(182)
(183)

Otimização das Tabelas de Parsing:

- São geralmente matrizes esparsas - Tentativas de compactação.

Referências

Documentos relacionados

Quadro de alumínio 6061 com suporte para freio a disco Amortecedor RSt Omega 100 mm com trava no guidão Câmbio Shimano Altus/Acera 24 velocidades Componentes Caloi.

Campos (2007) acrescenta que a Web 2.0 é concebida como uma plataforma. Assim, trata-se de uma plataforma com possibilidades de interoperabilidade e independência. A Web

Após o fim do período de preferência da quinta oferta de cotas, iniciou-se no mês de junho a oferta remanescente para a captação dos valores necessários para o retrofit no

Potenciais efeitos dos acidentes Medidas existentes para fazer face ao cenário de acidente Libertação de substâncias no estado gasoso que sejam tóxicas para a saúde humana

● Se a cassete chegar ao fim durante a reprodução, o gravador de vídeo pára a reprodução, rebobina a fita, ejecta a cassete e depois entra em modo de prontidão

Consolidar Resultados: Matriz de Forças Posicionar os fatores classificados dentro de cada quadrante.. Priorizar Priorizar Priorizar Monitorar Monitorar Monitorar

Dieser Schalter dreht die Phase des HIGH Signals um 180°, um einen Ausgleich für falsch gepolte Lautsprecher zu schaffen.. Zur Kontrolle leuchtet der

Junto com a rota catabólica da cascata purinérgica que age como um mecanismo de degradação de nucleotídeos extracelulares, há uma via oposta (anabólica) (Figura 8), que tem