Download - Compiladores - Da Teoria a Pratica.pdf

Transcript

UNIVERSIDADE TÉCNICA DE LISBOA

INSTITUTO SUPERIOR TÉCNICO

COMPILADORES:da teoria à prática

Pedro Reis dos Santos

Departamento de Engenharia Informática

(Fevereiro, 2006)

18 CAPÍTULO 1. INTRODUÇÃO

����HHHHHHHH

-

PPPPPq

- interpretadorprograma resultados

dados

-

?

-

?

"

#

!

dados

resultados

compiladorexecutávelprograma

1.2 Linguagens e gramáticas

Foram os matemáticos Axel Thue, Emil Post e Stephen Kleene, entre outros, que come-çaram a estudar as propriedades matemáticas das cadeias de caracteres e sequênciasdestas. No entanto, foi Noam Chomsky que, em 1956, que procurou caracterizar deuma forma precisa a estrutura das linguagens naturais. O seu objectivo consistia emutilizar regras matemáticas precisas para especificar a sintaxe das linguagens. Com adivulgação dos computadores, nos anos seguintes, veio-se a verificar que as linguagensa utilizar em computadores podiam ser especificadas por um dos modelos gramati-cais identificados por Chomsky. Estas linguagens, designadas por livres de contexto,podem ser linguagens de programação, podem ser linguagens de descrição de dados,como imagens ou som, ou outro tipo de processos.

Nesta secção abordaremos as propriedades matemáticas fundamentais das linguagense dos sistemas usados para as gerar, tais como as gramáticas. As linguagens de pro-gramação, desde o Fortran, podem ser especificadas de uma forma precisa através deuma gramática. Além disso, a gramática permite escrever programas, designados poranalisadores sintácticos, que permitem determinar se a cadeia de caracteres está sin-tacticamente correcta de acordo com a linguagem. Apesar dos avanços efectuados nosúltimos anos, a análise precisa de linguagens naturais, como o português ou o inglês,

1.2. LINGUAGENS E GRAMÁTICAS 19

ainda é limitada. O problema reside, principalmente, na inexistência de um acordo so-bre quais as frases sintacticamente correctas, nem foi ainda proposta uma gramáticasuficientemente precisa e consensual.

Nesta secção apresentaremos alguns sistemas formais que permitem definir famíliasde linguagens utilizadas frequentemente em computação. A nossa atenção principalorienta-se para as linguagens livres de contexto, pois são as mais usadas para descrevera sintaxe das linguagens de computação.

1.2.1 Terminologia

Antes de introduzir a especificação formal de linguagens apresentam-se algumas defi-nições e terminologia.

alfabeto Um alfabeto, designado por Σ, é um conjunto finito não vazio de símbolosindivisíveis. Por exemplo, a lingua inglesa contém as 26 letras alfabéticas e alguns sinaisde pontuação. A lingua portuguesa usa apenas 23 letras, mas inclui certos símbolos deacentuação para algumas letras. O alfabeto ASCII ( American Standard Code for Infor-

mation Interchange ) utiliza 128 símbolos, sendo utilizado na maioria das comunicaçõesentre computadores. O alfabeto UNICODE, mais recente que o ASCII, pretende supor-tar os símbolos de todas as linguagens utilizadas no planeta e possui, no momento daescrita deste documento, mais de 38 mil símbolos. No entanto, os aspectos mais impor-tantes das linguagens formais podem ser modelados com apenas duas letras.

cadeia Uma cadeia de um alfabeto Σ é uma sequência finita de símbolos de Σ. Onúmero de símbolos na cadeia x é designada por comprimento da cadeia e designadopor |x|. A cadeia vazia, de comprimento 0 ( zero ), é designada por ε.

conjuntos de cadeias O conjunto de todas as possíveis cadeias de um alfabeto desig-naremos por Σ∗, enquanto Σ+ designa todas as cadeias não vazias e ∅ o conjunto vazio.

linguagem Uma linguagem sobre um alfabeto Σ é o conjunto de cadeias consideradasválidas sobre esse alfabeto. Os membros da linguagem são também designados porfrases da linguagem.

Uma vez que as linguagens são apenas conjuntos é aceitável aplicar as operações tra-dicionais, como a união, intersecção e complemento. Além disso, existem mais duasoperações base sobre linguagens: a concatenação e o fecho de Kleene.

20 CAPÍTULO 1. INTRODUÇÃO

concatenação A concatenação de duas linguagens L1 e L2 sobre um alfabeto Σ é oconjunto {xy|x ∈ L1, y ∈ L2}. Onde x = a1a2 . . . an e y = b1b2 . . . bn são cadeias domesmo alfabeto e a sua concatenação, designada por xy, é a cadeia a1a2 . . . anb1b2 . . . bn.

fecho de Kleene Seja L0 = {ε} e Li = LLi para i ≥ 1, designa-se por fecho de Kleenede L, representado por L∗, a linguagem L∗ =

⋃i≥0 Li. O fecho transitivo de L, repre-

sentado por L+, é a linguagem L+ =⋃

i≥1 Li. Por outras palavras, o fecho de Kleene dalinguagem L consiste em todas as cadeias que podem ser formadas pela concatenaçãode zero ou mais frases de L.

1.2.2 Representação de linguagens

Uma linguagem é, em geral, um subconjunto de Σ∗ sobre alfabeto Σ. Enquanto umalinguagem finita pode ser definida através da enumeração dos seus elementos, umalinguagem infinita não pode ser exaustivamente enumerada. Algumas linguagens in-finitas podem ser definidas por regras que caracterizam todos os seus elementos. Umalarga gama de linguagens pode ser caracterizada por regras definidas por métodos sis-temáticos. De entre estes métodos, as gramáticas são o formalismomais frequentementeutilizado.

As gramáticas formais foram introduzidas em 1943 por Emil Post, que se baseou notrabalho de Axel Thue e outros. No entanto, foi Noam Chomsky em 1956 que estudoua sua utilização rigorosa na descrição formal e natural das linguagens.

O sistema mais genérico e útil para representar linguagens é baseado na noção formalde gramática. Uma gramática é um quádruplo (Σ, V, S, P ), onde

1. Σ é um conjunto finito não vazio designado por alfabeto terminal, onde cada ele-mento é designado por símbolo terminal.

2. V é um conjunto finito não vazio disjunto de Σ, cujos elementos são designadospor símbolos não terminais.

3. S ∈ V é um símbolo não terminal específico designado por símbolo inicial.

4. P é um conjunto finito de regras ( ou produções ) da forma α → β onde α ∈

(Σ⋃

V )∗V (Σ⋃

V )∗ e β ∈ (Σ⋃

V ), ou seja, α é uma cadeia de terminais e não ter-minais contendo pelo menos um não terminal e β é uma cadeia de terminais e nãoterminais.

1.2. LINGUAGENS E GRAMÁTICAS 21

Exemplo 1.1 A gramática G = ({0, 1, 2}, {S, A}, S, P ), onde P é constituído pelas regras

S → 0 S A 2

S → ε

2 A → A 2

0 A → 0 1

1 A → 1 1

descreve as frases {0n1n2n, n ≥ 0}.

Hierarquia de gramáticas

As gramáticas podem ser divididas em quatro classes de restrições gradualmente cres-centes à forma das regras. Esta classificação é designada por hierarquia de Chomsky.

Seja uma gramática G = (Σ, V, S, P )

1. G é também designada gramática tipo-0 ou gramática sem restrições.

2. G é tipo-1 ou gramática dependente do contexto se cada regra α → β de P obe-dece a |α| ≤ |β|. Uma gramática ainda é do tipo-1 se tiver uma regra S → ε, desdeque S não surja no lado direito de nenhuma regra.

3. G é tipo-2 ou gramática livre do contexto se cada regra α → β de P obedece a|α| = 1, ou seja, α é constituído por um só não terminal.

4. G é tipo-3 ou gramática regular se cada regra tiver uma das formas A → cB, A → c

ou A → ε, onde A e B são não terminais ( podendo B = A ) e c é um terminal.

Cada classe de linguagens do tipo-i contém a classe de linguagens do tipo-i+1, parai = 0, 1, 2.

Contudo, as classes de linguagens identificadas por Chomsky não reflectem apenas aspropriedades formais das mesmas mas também traduzem as características fundamen-tais da sua computação. De facto, cada classe de gramática é processável por uma classede ferramentas,

máquinas de Turing processam gramáticas sem restrições.

autómatos linearmente limitados processam gramáticas dependentes do contexto.

autómatos de pilha processam gramáticas livre do contexto.

22 CAPÍTULO 1. INTRODUÇÃO

autómatos finitos processam gramáticas regulares.

Destes apenas trataremos os autómatos finitos e autómatos de pilha, bem como os ana-lisadores que realizam esses autómatos.

Propriedades das gramáticas

fecho Uma classe de linguagens diz-se fechada numa operação particular se cada apli-cação da operação nas linguagens da classe ainda é uma linguagem dessa classe. Asoperações designam operações de união, intersecção, complemento, concatenação e fe-cho de Kleene. As propriedades de fecho são úteis na construção de novas linguagens apartir de linguagens existentes e para provar muitas propriedades teóricas das lingua-gens e gramáticas.

As gramáticas sem restrições só não são fechadas pela operação de complemento. Asgramáticas livres de contexto não são fechadas pelas operações de complemento e inter-secção. Finalmente, as linguagens regulares e as linguagens dependentes do contextosão fechadas em todas as cinco operações.

Capítulo 2

Linguagens regulares

As linguagens regulares, designadas por tipo-3 na hierarquia de Chomsky ( ver 1.2.2 ),são linguagens muito simples. Assim, a maioria das linguagens utilizadas em computa-ção não são regulares, ou seja, não podem ser completamente descritas por gramáticasregulares. No entanto, como o seu processamento é simples e eficiente, as gramáticas re-gulares são frequentemente utilizadas para processar partes, por vezes significativas, delinguagens mais complexas. Desta separação do processamento da linguagem por umagramática regular e por outra gramática, em geral livre de contexto, resulta uma maioreficiência em termos de espaço ocupado pelo analisador e do tempo gasto na análise dasdescrições. Resulta, ainda, uma simplificação da gramática livre de contexto, facilitandoa sua descrição, realização e execução.

O processamento da parte regular de uma linguagem designa-se por análise lexical. Aanálise lexical tem a vantagem de ser um processo que podendo ser formalmente des-crito através de expressões regulares pode produzir uma rotina que realiza essa análise.Essa rotina modela um autómato finito derivado matematicamente das expressões re-gulares especificadas.

A análise lexical é utilizada essencialmente na categorização dos elementos de uma lin-guagem em classes de símbolos, em vez de caracteres individuais. Nomeadamente,permite separar nomes em palavra reservadas, identificadores e literais, bem como clas-sificar outros símbolos como terminadores, separadores e operadores. Permite aindaencapsular dependências do sistema operativo, como por exemplo, o carácter ou carac-teres de mudança de linha. Localiza a manipulação dos diferentes formatos de repre-sentação de caracteres, como o ASCII, UNICODE ou, no caso da lingua portuguesa, oISO-LATIN-15 ou a página 860. Esconde do restante compilador elementos como co-mentários e caracteres brancos ( em geral designam o espaço e o tabulador horizontal ).Realiza a substituição de macros simples. Em resumo, facilita o processamento transfor-mando uma linguagem descrita por uma sequência de caracteres numa sequência de

25

26 CAPÍTULO 2. LINGUAGENS REGULARES

elementos lexicais, designados por tokens. A restante análise da linguagem pode assimser baseada em elementos categorizados.

2.1 Expressões regulares

Expressões regulares foram introduzidas em 1956 por Stephen Kleene para o estudodas propriedades das redes neuronais. As linguagens representadas por expressões re-gulares são designadas por linguagens regulares. As expressões regulares apresentamrepresentações das linguagens que são frequentemente claras e concisas, contudo, mui-tas linguagens não são regulares.

Uma expressão regular sobre um alfabeto Σ, e a linguagem regular que a expressãoregular representa, pode ser definida por:

1. o símbolo ∅ é uma expressão regular e representa a linguagem vazia.

2. o símbolo ε é uma expressão regular e representa a linguagem cujo único membroé a cadeia vazia, ou seja, {ε}.

3. para cada c ∈ Σ, c é uma expressão regular e representa a linguagem {c}, onde oúnico membro é a cadeia de um só carácter c.

4. se r e s são expressões regulares e representam as linguagensR e S, então (r|s), (rs)e (r∗) são expressões regulares que representam R

⋃S, RS e R∗, respectivamente.

Exemplo 2.1 A expressão regular ((0(0|1)∗)|((0|1) ∗ 0)) sobre o alfabeto {0, 1} representa a

linguagem regular que consiste em todas as cadeias de digitos binários que começam ou terminam

com 0.

2.1.1 Operadores das expressões regulares

De acordo com a definição acima, as expressões regulares são construídas por operado-res com determinadas propriedades. A esses operadores atribuiremos prioridades porforma a simplificar a escrita das expressões regulares. Considerando as propriedadesalgébricas dos operadores base é possível introduzir identidades algébricas para expres-sões regulares por forma a construir expressões equivalentes. Duas ou mais expressõesregulares que representam a mesma linguagem são designadas por equivalentes.

2.1. EXPRESSÕES REGULARES 27

Uma expressão regular pode ser formada pelos seguintes operadores, considerandoduas expressões regulares p, q:

união: designado por p|q é comutativo, associativo e idempotente ( p|p = p ), represen-tando a união das expressões regulares originais. ∅ é o elemento neutro da união.

concatenação: designado por p q é associativo e distributivo em realção à escolha emais prioritário que a escolha. ε é elemento neutro e ∅ é o elemento absorvente naconcatenação.

fecho de Kleene: designado por p∗ é idempotente ( p ∗ ∗ = p∗ ) e mais prioritário quea concatenação. Representa o conjunto de frases constituídas por zero ou maisrepetições das frases de p. Além disso p∗ = (p|ε)∗.

parênteses: permite alterar a prioridade dos operadores.

Exemplo 2.2 Atendendo às prioridades dos operadores, a expressão regular a|bca∗ sobre o alfa-

beto {a, b, c} corresponde a a|(b(c(a∗))).

Além dos operadores base é ainda usual utilizar outros operadores, construídos a partirdos anteriores, que simplificam a descrição das linguagens através de expressões regu-lares.

opção: designado por p? designa zero ou uma ocorrências de p, ou seja, p|ε.

fecho transitivo: designado por p+ designa uma ou mais ocorrências de p.p+ é equivalente a p p∗, e p∗ é equivalente a p + |ε.

parênteses rectos: designado por [pq] ou [p − q] designam p|q e os símbolos de ordementre p e q (inclusivé), respectivamente.A ordem depende da representação utilizada ( ASCII, UNICODE, EBCDIC, etc. )pelo que apenas se garante a ordem das letras maiúsculas [A − Z], minúsculas[a − z] e digitos decimais [0 − 9].

complemento: designado por [ˆpq] ou [ˆp−q] designam todos os símbolos deΣ exceptop|q e todos os símbolos de Σ excepto os símbolos de ordem entre p e q (inclusivé),respectivamente.

28 CAPÍTULO 2. LINGUAGENS REGULARES

2.1.2 Gramáticas regulares

Uma linguagem regular é descrita por uma gramática regular. Tal como vimos em 1.2.2uma gramática regular tem apenas regras da forma A → c, A → ε, eA → cB ou A → Bc,onde A e B são não terminais ( podendo B = A ) e c é um terminal. A gramática diz-selinear à direita se usar regras da forma A → cB ou linear à esquerda se usar regras daforma A → Bc, não podendo usar ambas as formas simultaneamente. Além disso, aregra A → c pode ser deduzida por substituição da regra B → ε numa das outras.

Se L tem uma gramática regular, então L é uma expressão regular. 1 Se L é uma ex-pressão regular, então L é gerado por alguma gramática linear à esquerda e por algumagramática linear à direita. 2 Em resumo, gramáticas regulares caracterizam expressõesregulares, pois uma linguagem é regular se e só se tiver uma gramática linear à es-querda e se e só se tiver uma gramática linear à direita. Ou seja, gramáticas regulares eexpressões regulares são representações equivalentes de linguagens regulares.

2.1.3 Propriedades das expressões regulares

As expressões regulares são apenas uma forma mais concisa de descrever linguagensregulares que as gramáticas regulares. Como as gramáticas regulares são fechadas nas5 operações base ( ver 1.2.2 ) pode-se combinar livremente as expressões regulares queainda se tem uma expressão regular. Como uma expressão regular é fechada na con-catenação qualquer técnica aplicada às expressões regulares a e b pode ser aplicada aab. Como é fechada na união, o que se pode fazer a uma expressão regular a1|a2|...|an

pode-se fazer individualmente a a1, a a2 etc. Como é fechada no fecho de Kleene pode-se escrever regras concisas sem indicar o limite, por exemplo, criar expressões regularespara padrões de dimensão finita mas arbitrária. Nomeadamente, os identificadores nãotêm número limite de caracteres em muitas linguagens.

2.2 Autómatos finitos

A modelação de linguagens regulares, quer tenham sido especificadas por expressõesregulares como por gramáticas regulares, pode ser efectuada por autómatos finitos. Damesma forma, uma linguagem reconhecida por um autómato finito é uma linguagem

1teorema 9.1 de [HU79]

2teorema 9.2 de [HU79]

2.2. AUTÓMATOS FINITOS 29

regular. Existem diversos algoritmos, representados pelas setas da figura seguinte, quepermitem converter expressões regulares de e para diversos tipos de autómatos finitos,cada um com o seu campo de aplicação.

-

?

HHHHHHHHHHHY

~ZZ

ZZ

ZZ}

��

����

Thompson

subconjuntos

Hopcroft

autómatofinitofinito det.

minimizado

regularesexpressões

autómato

autómatofinito nãodeterminista

determinista

Kleene

gramáticaregular

Nas secções seguintes abordaremos esses autómatos e os algoritmos que permitem obtê-los.

Os autómatos finitos são constituídos por um conjunto de estados e por transições di-rigidas e etiquetadas entre esses estados. As etiquetas são símbolos da gramática ouε. Um desses estados é designado por inicial, podendo ter um mais estados finais. Oautómato finito pode ser representado graficamente ou por tabela.

2.2.1 Diagrama de transição

Um autómato finito pode ser presentado graficamente por um diagrama de transição,ou seja, um quádruplo D = (S, δ, SF , q0) onde:

• S é um conjunto de estados, representados por círculos.

• δ(qi, x) = qj , onde qi ∈ S, qj ∈ S e x ∈ Σ ∪ {ε}, são transições entre estadosrepresentadas por setas etiquetadas por um dos símbolos de entrada ou ε.

• SF ∈ S e #S ≥ 1, é um conjunto constituído por um ou mais estados finais,representados por círculos duplos concêntricos.

• q0 ∈ S é um estado inicial, indicado por uma seta sem origem em outro estado.

30 CAPÍTULO 2. LINGUAGENS REGULARES

O reconhecimento de uma frase pode ser efectuado por simulação da rede, começandoo processamento no estado inicial. Se no fim do processamento nos encontrarmos numestado final a frase está correcta e faz parte da linguagem em questão. Se o estadocorrente, no fim do processamento, não for um estado final devem-se procurar outroscaminhos.

Exemplo 2.3 A expressão regular (a|b) ∗ abb, que designa as frases do alfabeto Σ = {′a′,′ b′}

que terminam em abb, pode ser representada por

����I ����I ��

��I��

������

- -- -ll

##AAU

��bb�

��

0’b’

1 I 2 3

’b’

’a’ ’b’

’a’

2.2.2 Tabela de transição

Uma outra representação, menos intuitiva mas mais útil do ponto de vista computaci-onal, é a tabela de transição. Na tabela de transição os estados representam uma dasdimensões da tabela bidimensional e as etiquetas a outra dimensão. Cada posição databela representa os estados que podem ser atingidos a partir desse estado com essesímbolo ou etiqueta. Os estados finais são representados dentro de caixas e o estadoinicial assume-se ser o primeiro estado da tabela.

Exemplo 2.4 A expressão regular do exemplo 2.3, pode ser representada por

estado ’a’ ’b’

0 {0, 1} {0}

1 ∅ {2}

2 ∅ {3}

0 ∅ ∅

2.2.3 Autómato finito não determinista

Um autómato finito não determista é um quintuplo (S, Σ ∪ {ε}, δ, SF , s0), onde S são osestados, Σ o alfabeto, uma função de transição δ, um estado inicial s0 ∈ S e um conjuntode estados finais SF ⊆ S. A função de transição δ : S × Σ 7→ 2S faz corresponder a cadapar estado e símbolo de entrada um conjunto, eventualmente vazio, de estados. Onde oconjunto de todos os subconjuntos de S, designado por powerset, é representado por 2S.

2.2. AUTÓMATOS FINITOS 31

A utilização de transições vazias, que não consomem nenhum símbolo da sequência deentrada, facilita a construção do autómato, mas existe sempre um autómato não deter-minista equivalente sem transições vazias 3. Quer o exemplo 2.3 como o exemplo 2.4são duas representações de um autómato finito não determinista.

Para descobrir se uma frase, ou sequência de entrada, faz parte da linguagem é neces-sário procurar um caminho entre o estado inicial e um dos estados finais do autómato.A procura pode ser efectuada de diversas formas, como por exemplo em profundidadee em largura. Se a procura for em profundidade segue-se um caminho possível e, casonão seja atingido um estado terminal, recua-se e tenta-se outra possível solução. Naprocura em largura vão sendo simultaneamente consideradas as diversas opções emcada estado. Computacionalmente a solução de procura em profundidade parece maissimples de realizar mas a sua execução é pesada.

Algoritmo de Thompson

Existe sempre um autómato não determinista, com transições vazias, que aceita a lin-guagem descrita por uma expressão regular 4. O algoritmo de Thompson não é maisque a demonstração da afirmação anterior. Para tal começamos pela três expressõescom que definimos as expressões regulares ( ver 2.1 ),

��������

- q 0 ����

- q 0����

- q 0 ��������

��������

-q qf f’a’

r = ε r = ∅ r = a

como as expressões regulares são fechadas nas operações base ( ver 1.2.2 ), temos que aoperação de união de duas expressões regulares r e s é também a união das linguagensdescritas por cada uma destas expressões regulares L(r|s) = L(r) ∪ L(s),

����

- q 0

����q 2 ����

2f

����

1f����q 1

��������

ZZ

Z~ ���3

QQQs�

��>

0f

ε

ε ε

ε

L(s)

L(r)

3teorema 2.2 de [HU79]

4teorema 2.3 de [HU79]

32 CAPÍTULO 2. LINGUAGENS REGULARES

a operação de concatenação de duas expressões regulares r e s é a concatenação daslinguagens descritas por cada uma destas expressões regulares L(rs) = L(r)L(s),

����q 2 ����

2f����

1f����q 1 ��

��- -ε

L(r) L(s)

na prática, o estados f1 e q2 podem ser fundidos num só, poupando um estado e elimi-nando a transição vazia.

O fecho de Kleene de uma expressão regular s é o fecho de Kleene da linguagem descritapela expressão regular L(s∗) = L(s)∗,

����

- q 0 ��������

0f����

1f����q 1

--

6

?ε ε

L(s)ε

ε

Notar o sentido das setas nas transições vazias.

A construção de uma expressão regular complexa resume-se à sucessiva aplicação dasdiversas ocorrências das operações por ordem decrescente de prioridade.

Exemplo 2.5 A expressão regular do exemplo 2.3 é representada pelo autómato finito não deter-

minista da figura, construído pelo algoritmo de thompson,

������������

��������

����������������

����

����

����������������

- -��*

-

-HHj

ZZ~

��>

- -

?�

?

6

� � �

10

3

2 4

5

6 7 8

910

a

b

ε

ε ε

εε ε

ε

ε

13b

12ε

11b ε

ε

a

2.2. AUTÓMATOS FINITOS 33

2.2.4 Autómato finito determinista

Um autómato finito determista é um quintuplo (S, Σ, δ, SF , s0), onde S são os estados, Σo alfabeto, uma função de transição δ, um estado inicial s0 ∈ S e um conjunto de estadosfinais iSF ⊆ S. A função de transição δ : S × Σ 7→ S faz corresponder a cada par estadoe símbolo de entrada um conjunto, eventualmente vazio, de estados.

Num autómato finito não determinista as transições a partir de cada estado são disjun-tas e não vazias. Ou seja, não podem existir transições vazias e, no máximo, só podehaver um transição para cada símbolo a partir de um estado.

Do ponto de vista da tabela de transição, a restrição introduzida pelos autómatos fini-tos determinista, implica que cada posição da tabela só contém um único estado. Secada estado for representado pelo seu número, basta representar o conjunto vazio porum número inválido, correspondente a uma situação de erro, por exemplo -1. Assim,cada expressão regular é representada por uma tabela de transição, a que teremos deacrescentar uma coluna para indicar os estados finais.

Por outro lado, como o autómato é determinista, não existe necessidade de tentar di-versas transições para encontrar uma solução válida. De facto, ou existe uma transiçãoválida ou a sequência de entrada não obedece à expressão regular que originou a tabela.Desta forma, por cada carácter lido da sequência de entrada, o analisador executa umpasso, consultando a tabela que indica o estado seguinte. Ou seja, em linguagem C,algo do tipo,

int lexical(int tabela[][256], int final[]) {

int ch, estado = 0;

while ((ch = getchar()) != EOF)

if ((estado = tabela[estado][ch]) == EOF) return FALSE;

return final[estado];

}

onde a tabela suporta todos os 256 caracteres que podem ser devolvidos pela função deleitura getchar() e EOFrepresenta quer o fim da sequência de entrada, quer o estadode erro na tabela. A tabela final tem tantos elementos quantos os estados, ou seja onúmero de linhas da tabela , indicando se cada estado é final ou não.

34 CAPÍTULO 2. LINGUAGENS REGULARES

2.2.5 Conversão por construção de subconjuntos

Se uma linguagem é aceite por um autómato finito não determinista então existe umautómato finito determinista que aceita essa mesma linguagem. 5 O algoritmo de cons-trução de subconjuntos permite obter um autómato finitos determinista a partir de umnão determinista, através de uma procura em largura.

A construção de subconjuntos inicia-se no estado inicial e, a partir deste estado, calcula-se todos os restantes estados que se podem atingir a partir deste estado apenas atravésde transições vazia. Este conjunto de estados, incluindo o estado inicial, é designadopor fecho − ε({q0}) e passa a representar o estado inicial do autómato finito determi-nista, que designaremos por I0. A partir de cada um dos estados que contituem I0

vamos determinar quais os estados que são atingidos através de uma só transição paracada um dos símbolos do alfabeto Σ. Estes novos conjuntos, designados pormove(I0, a),onde a ∈ Σ, formam o núcleo dos novos estados deterministas. O estado deterministaé completamente calculado através do fecho − ε(move(I0, a)), para cada símbolo. Noentanto, se o núcleo de um novo estado é igual ao núcleo de um estado já existente, nãoé necessário calcular o seu fecho, pois trata-se de uma repetição do estado anterior, queidentificaremos colocando o estado entre parênteses e colocaremos ... para designar osrestantes estados do fecho. O processo termina quando não houver mais estados deter-ministas por calcular. Os estados deterministas considerados finais são todos aquelesque contenham pelo menos um estado final do autómato não determinista, que repre-sentaremos colocando um quadrado à volta do estado.

Para construir os subconjuntos utilizaremos uma tabela com 5 colunas, que representamrespectivamente: o estado determinista a calcular, o símbolo de entrada a considerar, onúcleo não determinista que obtém, o restante fecho − ε e, finalmente, o novo estadodeterminista. Notar que o estado determinista indicado na última coluna é constituídopelos estados não deterministas das duas colunas anteriores.

Exemplo 2.6 A tabela de construção de subconjuntos da expressão regular do exemplo 2.3 re-

presentada pelo autómato finito não determinista do exemplo 2.5 é

5teorema 2.1 de [HU79]

2.2. AUTÓMATOS FINITOS 35

estado entrada move fecho − ε\move novo estado

0 1, 2, 3, 7, 8 I0

I0 a 4, 9 1, 2, 3, 6, 7, 8, 10 I1

b 5 1, 2, 3, 6, 7, 8 I2

I1 a 4, 9 . . . (I1)

b 5, 11 1, 2, 3, 6, 7, 8, 12 I3

I2 a 4, 9 . . . (I1)

b 5 . . . (I2)

I3 a 4, 9 . . . (I1)

b 5, 13 1, 2, 3, 6, 7, 8 I4

I4 a 4, 9 . . . (I1)

b 5 . . . (I2)

A partir da tabela, e eliminando as duas colunas referentes aos estados do autómatonão determinista, ficamos com as transições do autómato determinista. O estado daprimeira coluna tem uma transição etiquetada pelo símbolo da segunda coluna parao estado da última coluna. A partir desta informação é possível construir a tabela detransição ou a representação gráfica.

Exemplo 2.7 A tabela do exemplo 2.6 permite determinar o diagrama de transição, de onde

eliminámos o prefixo I pois já não existe confusão com os estados não deterministas,

����

����

����

����

����

����

- - - -?��/

SSo

��7

-

JJ

0 3 4

2

b

b

ba bb

a

a

a

a

1

A correspondente tabela de transições pode ser igualmente deduzida da tabela do exemplo 2.6 ou

do diagrama de transição acima,

estado a b

0 1 2

1 1 3

2 1 2

3 1 4

4 1 2

36 CAPÍTULO 2. LINGUAGENS REGULARES

A representação computacional da tabela é directa, contendo apenas duas colunas pois o alfabeto

tem apenas dois elementos.

2.2.6 Minimização da tabela

O número de estados do autómato finito determinista obtido através da construção desubconjuntos não é, necessariamente, mínimo. De facto, é frequente encontrar estadosabsolutamente equivalentes a outros, podendo ser fundidos num só estado. O númerodestes estados pode ser significativo para tabelas que representam linguagens regularesde interesse prático.

O algoritmo de Hopcroft permite determinar os estados equivalentes a partir de suces-sivas partições dos estados calculados. A partição inicial é constituída por três grupos,um contendo os estados finais, outra os estados não finais e uma última contendo oestado de erro, correspondente ao conjunto vazio. De facto, mesmo que existissem esta-dos equivalentes entre esses grupos a sua fusão implicaria a perda da informação sobrese a sequência era aceite ( estado final ) ou não.

O algoritmo consiste em fragmentar os grupos iniciais em outros subgrupos, se os res-pectivos estados não poderem ficar juntos. Para que os estado possam permanecer nummesmo grupo é necessário que, qualquer que seja o símbolo de entrada, todos esses es-tados transitem para estados que, também eles, pertençam a um mesmo grupo. Comoem cada nova interação, que se designa por partição, vão sendo criados novos grupos,estados que antes pertenciam ao mesmo grupo podem não poder continuar juntos. Oprocesso repete-se até que não sejam criados novos subgrupos.

Notar que os grupos criados podem conter vários estados, desde que estes contenhamtodos transições para os mesmos estados da partição anterior. Também não se deveassumir que apenas estados com entradas iguais na tabela é que podem ser agrupados,podendo ter transições para estados distintos de um mesmo grupo para cada símbolo.

Exemplo 2.8 A tabela do exemplo 2.7 não tem estado de erro pelo que a partição inicial contém

apenas dois grupos. Para mais, todos os estados transitam para o estado 1 com o símbolo de

entrada a. Desta forma, e neste caso, apenas o símbolo b pode forçar a separação dos estados. Dos

quatro estados não terminais, apenas o estado 3 transita com b para um grupo diferente, ou seja

o grupo dos estados finais. Assim, o estado 3 deve ser separado dos restantes passando a haver 3

grupos na nova partição. Agora é o estado 1 que transita também com b para um grupo distinto

dos outros dois estados, pois na nova partição o estado 3 já está separado.

2.2. AUTÓMATOS FINITOS 37

��������4

��������4

��������4

��������

����

����

����

?

6

-

?

6

��7

-

@@

-?

ZZ}@@

���

��

6 ��7 ��7

1

3

0, 20, 1, 2

3

b

a

b

b

aa

ab

b

a,b

0, 1, 2, 3

aa,b

P PP0 1 2

a a

Notar que os estados 0, 1, 2, 3 da partição P0 transitam para eles próprio com o símbolo a, mas

divergem quanto às transições com o símbolo b. Conseqeuntemente, na partição P1 o estado 3

é separado pois é o único que transita para fora do grupo com o símbolo b. No entanto, os três

estados restantes ainda não concordam quanto à transição com o símbolo b, pois com a saída do

estado 3 do grupo apareceram novas inconsistências. Desta forma, na partição P2 o estado 1 é

igualmente isolado, já sendo possível representar todas as transições do autómato inicial. Assim,

apenas os estados 0 e 2 podem ser agrupados.

Graficamente podemos representar as sucessivas partições, de uma forma mais simples, por uma

árvore,

��

��

@@

@R

-S

SSSw

-PPPPPPq

-

-- 44

313

4

0, 2

0P P P1 2

0, 1, 2, 3 0, 1, 2

A partir do autómato finito determinista, na sua representação gráfica ou por tabela,pode-se obter directamente uma gramática regular. Para tal basta considerar os tiposde regras apresentadas em 2.1.2, transformando cada transição δ(qi, a) = qj numa regraSi → aSj e acrescentando para cada estado final qf uma regra Sf → ε.

38 CAPÍTULO 2. LINGUAGENS REGULARES

Exemplo 2.9 Considerando a tabela, já minimizada, do exemplo 2.7, obtém-se directamente a

gramática regular correspondente, que representaremos com 4 símbolos não terminais cujo índice

reproduz o número do estado do autómato,

estado a b

0 1 0

1 1 3

3 1 4

4 1 0

S0 → a S1 | b S0

S1 → a S1 | b S3

S3 → a S1 | b S4

S4 → a S1 | b S0 | ε

2.2.7 Construção de Kleene∗

Se uma linguagem é aceite por um autómato finito determinista, então pode ser descritapor uma expressão regular 6. Ou seja, se a partir do autómato finito determinista geradoé possível produzir a expressão regular inicial, ou uma expressão regular equivalente.Assim, fecha-se o ciclo e garante-se a correcção de todo o processo.

Considere-se que Rkij é o conjunto de todas as sequências do autómato entre os estado

qi e qj sem passar por nenhum estado com número superior a k. Notar que para passarpor um estado é necessário entrar e sair. Logo i ou j podem ser maiores que k. Uma vezque nenhum estado tem um número superior a n, Rn

ij representa todas as sequênciasentre qi e qj .

O autómato não pode ter entradas no estado inicial, logo caso tais entradas existam énecessário acrescentar um novo estado inicial com uma transição vazia para o anteriorestado inicial.

O algoritmo consiste em introduzir as transições existentes em R0ij = {a|δ(qi, a) = qj} ∪

{ε|i = j}, ou seja, se i = j então ε faz parte de R0ij e se existirem transições directas entre

os estados qi e qj então os símbolos dessas transições também fazem parte de R0ij .

Os restantes valores são calculados recursivamente com base nos anteriores através daexpressão Rk

ij = Rk−1ik (Rk−1

kk ) ∗Rk−1kj |Rk−1

ij para k ≤ n onde n é o estado de maior número.Ou seja, construindo caminhos sucessivamente mais compridos e que passam por maisestados. As expressão regular final é a união de todas as expressões regulares entre oestado inicial e cada um dos estados finais L = |sj∈SF

Rn0j .

Exemplo 2.10 Considerando a tabela do exemplo 2.7, verifica-se que como existe um só estado

terminal 4 , uma expressão regular que representa o autómato é obtida por R404. A expressões

R0ij que representam as todas as transições possíveis entre os vários estados,

6teorema 2.4 de [HU79]

2.3. ANALISADOR LEXICAL 39

R0ij j = 0 j = 1 j = 2 j = 3 j = 4

i = 0 ε a b ∅ ∅

i = 1 ∅ a | ε ∅ b ∅

i = 2 ∅ a b | ε ∅ ∅

i = 3 ∅ a ∅ ε b

i = 4 ∅ a b ∅ ε

Calculando apenas as expressões intermédias necessárias ao cálculo de R404, e simplificando

R404 = R3

04(R344) ∗ R3

44 | R304 = R3

04(R344)∗, temos,

Rkij expressão resultante simplificação

R102 a(a | ε) ∗ ∅ | b b

R103 a(a | ε) ∗ b | ∅ a + b

R104 a(a | ε) ∗ ∅ | ∅ ∅

R122 a(a | ε) ∗ ∅ | (b | ε) b | ε

R123 a(a | ε) ∗ b | ∅ a + b

R124 a(a | ε) ∗ ∅ | ∅ ∅

R132 a(a | ε) ∗ ∅ | ∅ ∅

R133 a(a | ε) ∗ b | ε (a + b) | ε

R134 a(a | ε) ∗ ∅ | b b

R142 a(a | ε) ∗ ∅ | b b

R143 a(a | ε) ∗ b | ∅ a + b

R144 a(a | ε) ∗ ∅ | ε ε

R203 b(b | ε) ∗ (a + b) | (a + b) b ∗ a + b

R204 b(b | ε) ∗ ∅ | ∅ ∅

R233 ∅(b | ε) ∗ (a + b) | (a + b | ε) a + b | ε

R234 ∅(b | ε) ∗ ∅ | b b

R243 b(b | ε) ∗ (a + b) | (a + b) b ∗ a + b

R244 b(b | ε) ∗ ∅ | ε ε

R304 (b ∗ a + b)(a + b | ε) ∗ b | ∅ b ∗ (a + b) + b

R344 (b ∗ a + b)(a + b | ε) ∗ b | ε b ∗ (a + b) + b | ε

R404 (b ∗ (a + b) + b)(b ∗ (a + b) + b | ε)∗ (b ∗ (a + b) + b)+

A expressão resultante obtida (b ∗ (a + b) + b)+ pode ser convertida em (b ∗ (a ∗ ab) ∗ a ∗ abb)+

que é equivalente à expressão original (a|b) ∗ abb.

2.3 Analisador lexical

Saber se uma frase, ou sequência de entrada, obedece a uma gramática regular é apenasparte do problema. Na realidade, a análise lexical de linguagens deve conseguir identi-

40 CAPÍTULO 2. LINGUAGENS REGULARES

ficar repetições de subconjuntos da linguagem e permitir associar acções à identificaçãodesses conjuntos. Só dessa forma é possível produzir resultados como consequência daidentificação de componentes da linguagem.

A solução consiste em dividir a linguagem numa união de expressões regulares L =

(r1|r2| . . . |rn)∗. Devido às propriedades de fecho das expressões regulares sobre a ope-ração de união, podemos subdividir o problema. Para tal basta considerar um estadoinicial que deriva, através de transições vazias, cada uma das expressões regulares. Oautómato resultante considera, simultaneamente, as diversas expressões regulares.

Para determinar qual a expressão regular reconhecida, é necessário associar cada estadofinal com a respectiva expressão regular. O processamento de uma frase pode atravessardiversos estados finais, só parando quando se atinge um estado de erro. Neste ponto énecessário procurar o último estado final atingido e considerar reconhecida a respectivaexpressão regular. O analisador recomeça o processamento no estado inicial e os sím-bolos lidos após o último estado final considerado devem ser novamente processados.

Notar que este processamento considera sempre o reconhecimento da sequência de en-trada mais comprida. Convém não confundir com a expressão regular mais comprida.Caso contrário, as sequências de entrada curtas mas válidas impediriam o reconheci-mento das restantes.

A minimização da tabela segue o mesmo princípio enunciado atrás, mas a partição ini-cial tem de considerar os estados finais de expressões regulares distintas como perten-cendo a grupos iniciais distintos. Assim, além do grupo de estados não terminais e dogrupo de erro, é necessário considerar tantos grupos de estados finais quantas as ex-pressões regulares existentes. Se o grupo de estados finais de uma expressão regular forum conjunto vazio, então essa expressão regular nunca é reconhecida.

Exemplo 2.11 O autómato finito não determinista que descreve a linguagem composta pela

união das expressões regulares aaa e (a|b) pode ser representada por,

����

-a������������������������

������������

��������

����-

��

���

ZZ

Z~

-- -

��3

-Q

QQs

ZZ~

��>

0

e

e

aa1 2 a 43r1

b

e

e e

e

5 10r2

97

86

A tabela de análise resultante é

2.3. ANALISADOR LEXICAL 41

estado a b regra

0 1 2

1 3 E 2

2 E E 2

3 4 E

4 E E 1

Notar que os estados 2 e 4 não podem ser agrupados, embora sejam ambos finais e contenham as

mesmas transições, pois pertencem a grupos finais distintos. Relembra-se que os estados podem

ser agrupados mesmo que não tenham transições iguais, basta que tenham transições para estados

que pertençam ao mesmo grupo.

A análise da frase aa termina no estado 3, depois de ter passado pelo estado 1. Como este estado

não é final é necessário procurar o último estado final, neste caso o estado 1. Caso não exista umestado final no caminho percorrido a sequência de entrada é incorrecta. No caso da frase aa é

necessário recuar um estado, e consequentemente repor o último símbolo de volta na sequência

a processar, aceitando a expressão regular (a|b). O analisador é depois reiniciado no estado 0 e

processa novamente a letra a, que acabou de repor, aceitando mais uma vez a mesma expressão

regular e terminando o processamento por ter atingido o fim da frase.

No caso de um estado final, do autómato determinista, conter mais de um estado fi-nal do autómato não determinista, existe um conflito. O conflito é resolvido, em geral,optando por uma das possíveis expressões regulares. A solução mais frequentementeadoptada pelas ferramentas consiste em considerar prioritária a expressão regular quesurje em primeiro lugar na especificação da gramática. Contudo, esta solução obriga auma cuidada escrita das expressões regulares, por forma a garantir que todas são devi-damente reconhecidas. Notar que os conflitos surjem apenas entre expressões regularesque possam reconhecer sequências de igual comprimento.

Exemplo 2.12 Considere-se a linguagem descritas pela união das seguintes expressões regulares

identificadas por ordem de prioridade decrescente, bem como a tabela de análise resultante,

r1 aabr2 aar3 (a|b)*

estado a b regra

0 1 2 3

1 3 2 3

2 4 2 3

3 4 5 2,3

4 4 2 3

5 4 2 1,3

da tabela de análise conclui-se que caso a expressão regular 3 tivesse prioridade sobre as restantes,estas nunca seriam reconhecidas.

42 CAPÍTULO 2. LINGUAGENS REGULARES

2.3.1 Compactação da tabela

Na prática, na tabela de um linguagem, com um alfabeto constituídos pormuitos símbo-los distintos, é frequente encontrar colunas iguais. Também podem existir linhas iguaisse pertencerem a grupos de minimização distintos, mas tais ocorrências são escassas.Notar que a utilização de digitos decimais é em geral indistinto do seu valor, ficandocada uma das 10 colunas com entradas iguais na tabela. O mesmo pode acontecer comcertas classes de caracteres e operadores ou separadores.

Estes conjuntos de caracteres, cujas entradas na tabela têm os mesmos valores para to-dos os estados do autómato finito determinista minimizado, são designados por classesde equivalência. A compactação da tabela é efectuada introduzindo um vector de com-primento igual ao alfabeto da linguagem, onde cada entrada indica a coluna da tabelaa utilizar. Nesta situação podem-se remover todas as colunas duplicadas, apontandotodos os símbolos da mesma classe de equivalência para a mesma coluna.

Do ponto de vista do analisador lexical é desejável que todos os operadores e separado-res constituídos por um único carácter possam ser reconhecidos por uma só regra, porexemplo [− + ∗/(); , :], em vez de se reconhecer individualmente cada um. Na grandemaioria dos casos, esta solução não tem implicações na restante análise das linguagenspois os analisadores sintácticos já reconhecem caracteres individuais. O reconhecimentoindividual de cada carácter obriga a considerar expressões regulares distintas, o queobriga criar mais estados finais distintos.

A tabela de análise resultante, após a compressão das colunas iguais, pode ainda sermais comprimida através de métodos genéricos de compressão de matrizes esparsas.Estes métodos, que trataremos mais adiante, permitem tirar partido do facto de certovalor, por exemplo 0, existir em mais de metade das entradas da tabela. Embora astabelas de análise lexical não apresentem tais características, ao contrário das tabelas deanálise sintáctica, pode-se considerar entradas por omissão ( default, em inglês ). Paratal cria-se uma nova coluna na qual se coloca o valor mais frequente em cada linha.As ocorrências desse valor em cada linha são substituídas por uma entrada especial, emgeral codificada com o valor 0, o que significa que quando este valor é encontrado deve-se considerar a entrada por omissão. A tabela resultante passa a conter uma quantidadesignificativa de valores por omissão, podendo em casos reais atingir 90% de todas asentradas da tabela. A tabela pode agora ser compactada por algoritmos genéricos decompressão de matrizes esparsas.

2.3. ANALISADOR LEXICAL 43

2.3.2 Analisadores explicitamente codificados

Até ao momento considerámos que o autómato finito determinista era codificado comouma tabela, como a do exemplo 2.7, e que um pequeno programa, como o apresentadona secção 2.2.4, determina o estado seguinte com base na tabela e carácter lido. Con-tudo, existem soluções que não utilizam tabelas e que se designam por explicitamentecodificados ou hardcoded. Estas soluções são em geral mais rápidas à custa de um exe-cutável de maiores dimensões, em especial no caso de máquinas RISC.

Consideremos uma abordagem baseada em saltos (gotos) que, contudo, não pode serutilizada em linguagens sem essa instrução como o Java. Cada estado é identificadopor uma etiqueta, efectuando saltos de acordo com carácter lido logo após a etiqueta.Por exemplo, a partir do estado 4 da tabela do exemplo 2.9 obtém-se

state4:

in = * input++;

if (in == ’a’) goto state1;

if (in == ’b’) goto state0;

if (in == 0) return 1; / * só para estados finais * /

goto error; / * termina cada estado * /

Outra solução possível, sem recurso a saltos, consiste em codificar cada estado comouma função, sendo os saltos para as etiquetas subtituídos por chamadas às rotinas querepresentam os respectivos estados. No entanto, esta solução não é tão eficiente, poisas chamadas e retorno das rotinas são em geral operações lentas. Além disso, utiliza apilha do processador para coleccionar os estados por onde vai passando. Assim, a pro-fundidade máxima da pilha limita a maior sequência a ser reconhecida. Tal limitaçãonão tem vantagem pois, ao contrário dos analisadores sintácticos descendentes predi-tivos que veremos adiante, a informação deixada na pilha não tem interesse prático(excepto, eventualmente, para efeitos de backtracking). A partir da gramática regular doexemplo 2.9, a regra S4 correspondente ao estado 4 pode ser codificada como

static int state4() {

register char in = * input++;

if (in == ’a’) return s1();

if (in == ’b’) return s0();

return 1; / * estados não finais retornam 0 * /

}

44 CAPÍTULO 2. LINGUAGENS REGULARES

2.4 Exercícios

Exercício 2.1 Considere o diagrama de transição do autómato finito não determinista do exem-

plo 2.3. Determine, através do método dos subconjuntos, a tabela de transição do autómato finito

determinista. Compare o resultado que obteve com a tabela do exemplo 2.9.

Exercício 2.2 Considere a expressão regular (aa|ab|ba|bb)∗ definida sobre o alfabeto Σ = {a, b}:

1. Construa o autómato finito não determinista (NFA) pelo algoritmo de Thompson a que

corresponde à expressão regular.

2. Construa algoritmicamente o autómato finito determinista (DFA) equivalente a partir do

NFA da alínea anterior, e represente o seu diagrama de estados.

Explicite quais os estados do NFA realizado em cada estado do DFA.

3. Minimize algoritmicamente o número de estados do autómato finito determinista (DFA),

indicando todas as partições intermédias e respectivos grupos.

Exercício 2.3 Construa o diagrama de estados do autómato finito determinista da expressão

regular (ε|b)((ε|a)b)∗a, indicando em quantos passos é analisada a sequência de entrada bbabba.

Construa a tabela de transição minimizada e justifique quantos passos são agora necessários para

processar a mesma sequência de entrada.

Exercício 2.4 Construa a tabela de transição determinista minimizada das seguintes expressões

regulares:

1. a(a|b) ∗ a

2. (a?b∗)∗

3. a ∗ ba ∗ ba∗

4. (a|b) ∗ abb(a|b)∗

5. a(a|b) + b

6. (aba|bab)∗

2.4. EXERCÍCIOS 45

Exercício 2.5 Construa a tabela de transição determinista minimizada da gramática regular,

sobre o alfabeto {x, y, z} e onde A é símbolo não terminal inicial,

A → xB

B → y C | z D | ε

C → y C | z D | ε

D → y C | z D | ε

Indique em quantos passos é processada a sequência de entrada xxzy.

Exercício 2.6 Considere a sequência ordenada de expressões regulares aa, a∗, a|b definida sobre

o alfabeto Σ = {a, b}:

1. Determine algoritmicamente os subconjuntos do autómato finito determinista (DFA) equi-

valente a partir do autómato finito não determinista (NFA) construído pelo algoritmo de

Thompson a que corresponde a sequência ordenada de expressões regulares.

Explicite quais os estados do NFA realizado em cada estado do DFA.

2. Minimize algoritmicamente o número de estados do autómato finito determinista (DFA),

indicando todas as partições intermédias e respectivos grupos.

Represente a tabela de transição de estados resultante.

3. Indique, justificando, em quantos passos é processada a sequência de entrada: aabbaaa.

Exercício 2.7 Construa a tabela de análise da linguagem definida pela sequência ordenada das

seguintes expressões regulares ab, ab∗, a|b definida sobre o alfabeto Σ = {a, b}. Indique, justifi-

cando, em quantos passos é processada a sequência de entrada: abaabb.

Exercício 2.8 ∗ Determine uma expressão regular que defina a linguagem descrita pela tabela

de análise:

est a b

0 1 2

1 E 2

2 1 2

Exercício 2.9 Apresente a tabela de análise minimizada e compactada que analisa a linguagem

definida pela expressão regular u(a(e|o)) ∗ i, definida sobre o alfabeto Σ = {a, e, i, o, u}.

Capítulo 5

Análise sintáctica ascendente por tabela

Um analisador ascendente constroí a árvore sintáctica das folhas para a raiz. Ao con-trário da análise descendente, que necessita prever qual a regra a utilizar, a análiseascendente atrasa a escolha da regra até ter lido todos os símbolos que constituem aderivação ( lado direito de uma produção ), mais os símbolos de antevisão necessários.A principal decisão de um analisador ascendente consiste em determinar quando umasequência de símbolos corresponde a alguma das regras da gramática. Esta tarefa nãoé trivial, pois pode haver mais de uma regra com a mesma derivação e casos em que,apesar da semelhança, não se tratam de derivações possíveis.

Como os analisadores ascendentes substituem uma derivação de uma regra pelo sím-bolo não terminal que a origina são globalmente designados por LR(k). A análise LR(k)é efectuada lendo a sequência de entrada da esquerda para a direita (Left to right), em-parelhando as derivações das regras da direita para esquerda (Right to left), usando nomáximo k símbolos de antevisão. Estes analisadores são os mais utilizados pois sãomenos limitativos que os correspondentes analisadores preditivos descentes LL.

Neste capítulo estudaremos em detalhe apenas os analisadores mais simples e menosexigentes, em termos da dimensão das tabelas necessárias. Contudo, a análise determi-nista da grande maioria das linguagens usadas em computação pode ser efectuada combase em gramáticas processáveis por analisadores LALR(1). Os analisadores LALR(1)apresentam tabelas da mesma dimensão das formas mais simples SLR(1) e LR(0), maspermitem analisar um maior número de gramáticas. Relembra-se que uma mesma lin-guagem pode ser descrita por diversas gramáticas equivalentes, bastando encontraruma dessas gramáticas que seja pelo menos LALR(1).

O funcionamento de um analisador ascendente baseia-se em duas operações fundamen-tais: o deslocamento (shift) de símbolos da sequência de entrada para a pilha auxiliar ea redução (reduce) dos símbolos de derivação de uma regra ao símbolo não terminal que

57

58 CAPÍTULO 5. ANÁLISE SINTÁCTICA ASCENDENTE POR TABELA

a origina. Inicialmente a pilha começa vazia, sendo deslocados símbolos até ser encon-trada uma derivação válida. O processamento termina quando se atínge uma operaçãode aceitação (accept) ou de erro (error).

5.1 Construção do analisador

Para que a análise determinista de uma linguagem por um analisador ascendente sejacorrecta basta que os deslocamentos e a aceitação sejam permitidos apenas em certassituações. A diferença entre os analisadores LR(0), SLR(1) e LALR(1) consiste apenasna colocação das reduções. No entanto, se uma redução for erradamente executada,o seu resultado não poderá ser deslocado, pelo que as sequências de entrada erradassão igualmente detectadas, mas com algum atraso. A determinação dos deslocamentosé efectuada com base num autómato determinista muito semelhante ao utilizado naanálise das linguagens regulares.

gramática aumentada Para que a sequência de entrada seja correctamente detectadapela gramática é necessário introduzir uma regra auxiliar que explicite o fim do pro-cessamento. Desta forma, não é possível terminar prematuramente o processamento,deixando símbolos por processar. Esta regra auxiliar contém uma só derivação com osímbolo inicial da gramática e o símbolo $, símbolo que utilizaremos para designar ofim da sequência de entrada (end of file).

Exemplo 5.1 Considerando uma gramática com símbolo inicial S,

S → ’(’ A ’)’

A → ’a’

| S

| A ’,’ ’a’

a gramática aumentada inclui também a regra S’→ S $, passando S’ a ser o novo símbolo inicial

da gramática aumentada, ou seja,

S’ → S $

S → ’(’ A ’)’

A → ’a’

| S

| A ’,’ ’a’

5.1. CONSTRUÇÃO DO ANALISADOR 59

estados do autómato A análise ascendente baseia-se na construção de um autómatonão determinista e na sua conversão em determinista, pelos mesmos processos utili-zados para o tratamento de linguagens regulares. Para tal, cada regra é dividida nosdiversos estados em que o processamento da regra se pode encontrar, designados poritens, que correspondem aos estados do autómato não determinista. Assim, uma regraem que a derivação é composta porN símbolos, terminais ou não terminais, é compostapor N+1 itens, sendo o primeiro correspondente ao estado antes de iniciar o processa-mento da regra, o segundo após consumir o primeiro símbolo e o último após o proces-samento de todos os símbolos da derivação da regra. A cada um destes itens é atribuídoum número único que destingue cada estado do autómato finito não determinista. Osdiversos itens de cada regra são ligados, pela ordem de processamento, por transiçõesque consomem os símbolos da derivação da regra.

Exemplo 5.2 Considerando a primeira regra do exemplo 5.1 S → ’(’ A ’)’ , são gerados qua-

tro itens, onde o símbolo • representa o ponto de processamento da regra no respectivo estado do

autómato não determinista,

S → • ’(’ A ’)’ 1

′(′

−→ S→ ’(’ • A ’)’ 2A

−→ S → ’(’ A • ’)’ 3

′)′

−→ S→ ’(’ A ’)’ • 4

Para tornar a leitura mais legível e compacta representaremos cada estado, do autómatofinito não determinista, pelo seu número colocado no ponto de processamento da regra,ficando a regra do exemplo 5.2 reduzida a S → 1 ’(’ 2 A 3 ’)’ 4.

transições vazias Além das transições entre os diversos itens de uma regra, etiqueta-das pelos símbolos consumidos, é necessário considerar que ao processar um símbolonão terminal devem ser consideradas todas as regras que esse símbolo pode derivar.Assim, no item que antecede o processamento de um símbolo não terminal inserem-setransições vazias para todos os itens iniciais das regras que esse símbolo não terminalderiva. Na representação que utilizaremos, não etiquetaremos essas transições com osímbolo ε, como fizemos nas linguagens regulares, nem representaremos por completoo arco que une os estados. Desta forma, o item 2 da regra do exemplo 5.2 será repre-sentado por 2

ր A, significando que no item 2 existem transições vazias para os itensiniciais das três regras que o símbolo A deriva. Para posterior referência, indicaremosantes de cada regra o seu número de ordem, correspondendo r0 ( regra zero ) à regra au-xiliar da gramática aumentada. Tal representação pretende apenas simplificar e tornarmais compacto e legível o autómato, sendo funcionamente equivalente à representaçãoutilizada nas linguagens regulares.

60 CAPÍTULO 5. ANÁLISE SINTÁCTICA ASCENDENTE POR TABELA

Exemplo 5.3

O autómato finito não determinista da gramática aumentada usada no exemplo 5.1 fica

r0 S’ → 0ր S 1 $

r1 S → 2 ’(’ 3ր A 4 ’)’ 5

r2 A → 6 ’a’ 7

r3 | 8ր S 9

r4 | 10ր A 11 ’,’ 12 ’a’ 13

aconselha-se a numerar os itens sequencialmente dentro de cada regra por forma a facilitar a

consistência das transições, embora apenas se exija que cada item tenha um número distinto de

todos os outros do autómato finito não determinista.

Notar que na regra auxiliar da gramática aumentada existem apenas dois itens, pois não é possí-

vel passar além do fim da sequência de entrada.

autómato determinista A construção do autómato finito determinista segue exacta-mente o mesmo processo utilizado nas linguagens regulares. No entanto, como númerode símbolos é potencialmente grande omitiremos todas as transições que produzam es-tados deterministas que sejam conjuntos vazios.

Exemplo 5.4

A determinação dos estados do autómato determinista do exemplo 5.3 resume-se a

estado entrada move fecho − ε\move novo estado

0 2 I0

I0 S 1 I1

’(’ 3 6, 8, 10, 3 I2

I2 A 4, 11 I3

’a’ 7 I4

S 9 I5

’(’ 3 . . . (I2)

I3 ’)’ 5 I6

’,’ 12 I7

I7 ’a’ 13 I8

5.1. CONSTRUÇÃO DO ANALISADOR 61

construção da tabela de análise Tal como no caso das linguagens regulares, tambéma análise sintáctica ascendente utiliza uma tabela de análise. No entanto, para o seuprocessamento é necessária uma pilha de dados auxiliar, onde são colocados tempora-riamente os símbolos até que uma regra seja identificada e a sua derivação substituídapelo símbolo não terminal que lhe dá origem. Neste caso, a tabela inclui cinco tipos deoperações distintas:

deslocamento (shift) : corresponde a retirar o símbolo da sequência de entrada e co-lo-cá-lo no topo da pilha, seguido do estado indicado na operação de deslocamento.Por exemplo s2 ( ou seja, shift 2 ), corresponde a deslocar o símbolo que estiverà cabeça da sequência de entrada e mover para o estado 2 ( do autómato finitodeterminista ).

movimento (goto) : corresponde a mover para o estado indicado, colocando-o no topoda pilha, representado por g2 ( ou seja, goto 2 ).

erro (error) : não é representado na tabela de análise, correspondendo às posições dei-xadas em branco.

redução (reduce) : corresponde a substituir os símbolos de derivação de uma regra exis-tente no topo da pilha pelo símbolo não terminal que deriva essa mesma regra. Érepresentado por r2 ( ou seja, reduce 2 ), mas apenas neste caso o número 2 nãorepresenta um estado do autómato mas sim o número da regra

aceitação (accept) : corresponde à redução da regra auxiliar da gramática aumentada,representando o fim do processamente, podendo apenas existir na coluna corres-pondente ao fim do processamento ( representado por $ ).

A tabela de análise é constituída por todos símbolos terminais, incluindo o fim de pro-cessamento $ ( que pode ser lido de um ficheiro ), e todos os símbolos não terminaisda gramática inicial ( exclui-se a regra auxiliar da gramática aumentada ). Muita daliteratura separa a tabela de deslocamentos da tabela de movimentos pelo que, emborautilizaremos uma única tabela, explicitaremos a sua individualização por uma duplabarra.

O autómato finito determinista permite preencher as operações de deslocamento e mo-vimento da tabela de análise. O preenchimento é idêntico, diferindo apenas no factode se tratar de um deslocamento para símbolo terminais ( aqueles que podem ser ob-tidos da sequência de entrada ) e de um movimento para os símbolos não terminais.Com base no cálculo do autómato finito determinista, e ignorando as duas colunas quecontêm os estados não deterministas, ficamos com os valores a preencher

62 CAPÍTULO 5. ANÁLISE SINTÁCTICA ASCENDENTE POR TABELA

Exemplo 5.5

Eliminando os estados do autómato finito não determinista da tabela do exemplo 5.4, o preen-

chimento das operações de deslocamento e movimento na tabela de análise é directo. As duas

primeiras colunas designam, por ordem, a linha e a coluna da tabela do autómato finito determi-

nista e a última designa o valor a preencher nessa posição.

estado entrada novo estado

I0

I0 S I1

’(’ I2

I2 A I3

’a’ I4

S I5

’(’ (I2)

I3 ’)’ I6

’,’ I7

I7 ’a’ I8

′(′ ′a′ ′)′ ′,′ $ S A

0 s2 g1

1

2 s2 s4 g5 g3

3 s6 s7

4

5

6

7 s8

8

5.1.1 Gramáticas LR(0)

O autómato LR(0) anterior permite construir tabelas para analisadores LR(0), SLR(1) eLALR(1), diferindo apenas na forma como as reduções são inseridas na tabela. Comojá foi referido, são os deslocamentos e movimentos que determinam se a sequência deentrada está correcta ou não, pelo que mesmo que sejam colocadas reduções desneces-sárias, mais tarde ou mais cedo a falha será detectada.

O analisador mais simples é o analisador LR(0), que como o nome sugere não usa sím-bolos de antevisão ( representado pelo número entre parenteses, ou seja 0 símbolos deantevisão ). Para compreender o seu funcionamento basta representar graficamente oautómato finito determinista, considerando que uma vez atingido o último estado deuma regra esta fica pronta a ser reduzida.

Tal como no processamento de expressões regulares, a determinação de quais os estadosdo autómato finito determinista em que as diversas regras são reduzidas baseia-se naidentificação dos estados finais de cada regra no autómato finito não determinista. As-sim, todos os estados deterministas, que incluam esse estado não determinista, reduzema regra.

Exemplo 5.6 A representação gráfica do autómato finito determinista cálculado no exemplo 5.4

pode ser representado pela figura abaixo.

5.1. CONSTRUÇÃO DO ANALISADOR 63

I

I

I

I

I

I II

I r35

r1

r40

1

2

3

4

6

7 8

r2

SA

S

’(’

’)’

’a’’a’

’,’

’(’

acc (r0)

Como nos analisadores LR(0) não existem símbolos de antevisão, a redução será efectu-ada para todos os símbolos terminais da linguagem em questão. Excepção feita à regraauxiliar da gramática aumentada, que apenas pode ser reduzida para o símbolo de fimde processamento $, pois para qualquer outro símbolo significaria que a sequência nãoestava completamente processada.

Exemplo 5.7 A tabela de análise LR(0) para a gramática do exemplo 5.1 fica concluída com

o preenchimento das reduções das regras e da operação de aceitação para a regra auxiliar da

gramática aumentada. Todas as posições deixadas em branco representam situações de erro.

′(′ ′a′ ′)′ ′,′ $ S A

0 s2 g1

1 acc

2 s2 s4 g5 g3

3 s6 s7

4 r2 r2 r2 r2 r2

5 r3 r3 r3 r3 r3

6 r1 r1 r1 r1 r1

7 s8

8 r4 r4 r4 r4 r4

Notar que nenhuma linha da tabela pode ficar totalmente em branco, pois esse estado não seria

necessário. Da mesma forma, nenhuma coluna da tabela pode ficar totalmente em branco por

esse símbolo nunca seria processado. No caso de um símbolo terminal, significa que esse símbolo

nunca pode ser lido, enquanto no caso de um símbolo não terminal, significa que nenhuma das

regras que ele deriva é alguma vez reduzida ( uma vez que após uma redução existe sempre um

movimento, excepto para a regra auxiliar ).

64 CAPÍTULO 5. ANÁLISE SINTÁCTICA ASCENDENTE POR TABELA

5.1.2 Análise ascendente por tabela

Oprocessamento de uma sequência de entrada por um analisador ascendente por tabelautiliza as operações atrás referidas. Tal como no caso da análise preditiva descendentepor tabela, recorre-se a uma pilha. Neste caso, a pilha irá conter alternadamente estadosdo autómato finito determinista e símbolos, terminais e não terminais, da gramática.

Inicialmente a pilha contém apenas o estado inicial ( sempre designado por 0 nos exem-plos aqui apresentados ). A determinação da operação a realizar utiliza o elemento notopo da pilha e o símbolo à cabeça da sequência de entrada, correspondendo respec-tivamente às linha e coluna da tabela de análise. Após uma redução, o topo da pilhafica com o símbolo não terminal que deriva a regra reduzida, utilizando-se neste casoos dois elementos do topo da pilha para determinar a operação a realizar.

Exemplo 5.8 Considere-se a análise da sequência de entrada (a,a) através da tabela do exem-

plo 5.7 para a gramática do exemplo 5.1:

pilha entrada acção

0 (a, a)$ shift-2

0(2 a, a)$ shift-4

0(2a4 , a)$ reduce-2

0(2A , a)$ goto-3

0(2A3 , a)$ shift-7

0(2A3, 7 a)$ shift-8

0(2A3, 7a8 )$ reduce-4

0(2A )$ goto-3

0(2A3 )$ shift-6

0(2A3)6 $ reduce-1

0S $ goto-1

0S1 $ accept

No primeiro passo da análise procura-se na tabela o estado 0 ( topo da pilha ), correspondente à

primeira linha, e na coluna ’(’ a operação a executar, ou seja s2 ( shift-2 ). No terceiro passo, para

efectuar a redução da regra 2 necessitamos retirar do topo da pilha todos os símbolo que derivam a

regra e respectivos estados colocando em seu lugar o símbolo não terminal que deriva essa regra.

Neste caso apenas a 4 é retirado da pilha, sendo colocado o símbolo A, pois a regra 2 é apenas

A → a. Para as restantes reduções do exemplo acima o processo é idêntico, mas para as regras 4e 1, ou seja, A → A′,′ a e S →′ (′A′)′, respectivamente. Notar que, ao contrário dos analisadores

preditivos descendentes, a pilha cresce da esquerda para a direita para que as derivações da regra

5.1. CONSTRUÇÃO DO ANALISADOR 65

apareçam na pilha pela mesma ordem que são escritas na gramática, apenas contendo os números

dos estados entre os símbolos.

Após cada redução existe sempre uma operação de movimento, utilizando-se os dois elementos

do topo da pilha para determinar as linha e coluna na tabela. Assim, no quarto passo, o topo da

pilha indica linha 2 e coluna A, ou seja, g3 ( goto-3 ).

5.1.3 Gramáticas SLR(1)

As gramáticas LR(0), ou seja aquelas que podem produzem tabelas LR(0) sem gerarconflitos ( mais de uma operação por posição na tabela de análise ), são bastante restriti-vas. Embora não tenha as limitações de recursividade ou factorização dos analisadorespreditivos descendentes, o método de construção LR(0) produz frequentemente con-flitos. Para uma sequência de entrada correcta, uma regra só pode ser seguida pelosseus símbolos de FOLLOW, todas as restantes reduções estão erradas. Assim, em vezde efectuar a redução e vir a detectar o erro no próximo deslocamento, o método deconstrução SLR(1) apenas preenche as reduções para os símbolos de FOLLOW.

Exemplo 5.9 Considerando o exemplo 5.1 temos que FOLLOW(S) = {$ , ′)′ , ′,′ } e FOL-LOW(A) = {′)′ , ′,′ } logo a regra 1 reduz apenas nos três símbolos de FOLLOW e as regras

2, 3 e 4 nos dois símbolos atrás calculados, ficando a tabela

′(′ ′a′ ′)′ ′,′ $ S A

0 s2 g1

1 acc

2 s2 s4 g5 g3

3 s6 s7

4 r2 r2

5 r3 r3

6 r1 r1 r1

7 s8

8 r4 r4

Notar que a análise da tabela gerada pelo método SLR(1) é igual à análise da tabelaLR(0), apenas em caso de redução com símbolo de antevisão que não pertença ao con-junto FOLLOW é imediatamente gerado um erro e parado o processamento. Alémdisso, caso existam operações de deslocamento nessas posições, deixa de haver confli-tos, sendo essas gramáticas SLR(1) mas não LR(0).

66 CAPÍTULO 5. ANÁLISE SINTÁCTICA ASCENDENTE POR TABELA

5.2 Gramáticas LALR(1)

O método de construção LALR(1) leva o princípio do SLR(1) um passo mais adiante.Nomeadamente, nem todos os símbolos de FOLLOW são utilizados em todas as regras,mas apenas um subconjunto destes, ou seja os símbolos de antevisão ( LOOKAHEAD ).Cada ocorrência da regra, e não apenas a regra em si, pode apresentar símbolos deantevisão distintos. Determinando caso a caso, quais são efectivamente os símbolosde antevisão na activação regra, permite-se que em cada redução se possa restringirainda mais o número de posições preenchidas com reduções. Tal facto permite, emprimeiro lugar evitar conflitos com outras reduções ou com deslocamentos, tornandoas gramáticas LALR(1) mais gerais que as que vimos até ao momento. Em segundolugar, torna a tabela mais esparsa, ou seja contém mais posições vazias, o que acelera adeterminação de erros e permite uma maior compressão da tabela resultante.

Exemplo 5.10 Considere-se a gramática seguinte e o respectivo autómato finito não determi-

nista LR(0)

S → A ’b’ ’b’

| ’a’ ’a’ ’b’

| ’b’ A ’a’

A → ’a’

r0 S’ → 0ր S 1 $

r1 S → 2ր A 3 ’b’ 4 ’b’ 5

r2 | 6 ’a’ 7 ’a’ 8 ’b’ 9

r3 | 10 ’b’ 11ր A 12 ’a’ 13

r4 A → 14 ’a’ 15

a conversão em autómato finito determinista LR(0)

estado entrada move fecho − ε\move novo estado

0 2, 6, 10, 14 I0

I0 S 1 I1

A 3 I2

’a’ 7, 15 I3

’b’ 11 14 I4

I2 ’b’ 4 I5

I3 ’a’ 8 I6

I4 A 12 I7

’a’ 15 I8

I5 ’b’ 5 I9

I6 ’b’ 9 I10

I7 ’a’ 13 I11

a que corresponde a tabela de análise SLR(1), sabendo queFOLLOW(S) = {$}

FOLLOW(A) = {′a′ , ′b′}

5.2. GRAMÁTICAS LALR(1) 67

′a′ ′b′ $ S A

0 s3 s4 g1 g2

1 acc

2 s5

3 s6/r4 r4

4 s8 g7

5 s9

6 s10

7 s11

8 r4 r4

9 r1

10 r2

11 r3

Pode-se verificar que no estado 3 para o símbolo ’a’ existe um conflito deslocamento-redução. No

entanto, fazendo o processamento mental, verificamos que ao ler o primeiro carácter ’a’, ficamos

no fim da regra 4 vindo da regra 1, bem como no segundo estado da regra 2. Porém, dependendo

do carácter seguinte podemos determinar qual das duas opções é efectivamente a correcta. Tal

deve-se ao facto de quando se processa a regra 4 vindo da regra 1 apenas o símbolo ’b’ se podeseguir, embora quer ’a’ como ’b’ serem FOLLOW do símbolo não terminal A.

O método de construção de tabelas de análise LALR(1) produz autómatos de igual di-mensão que o LR(0), mas onde cada ocorrência do estado não determinista transportao conjunto de símbolos de antevisão necessários. A redução é efectuada apenas para ossímbolos de antevisão associados a cada ocorrência do estado não determinista, corres-pondente ao final da regra.

Notar que o estado 14 da regra 4 do exemplo 5.10 terá símbolos de antevisão distintosconsoante têm origem na regra 1 ou na regra 3, respectivamente ’b’ e ’a’. Os símbolosde antevisão devem ser depois ser sucessivamente transportados até ao último itemda regra, o estado 15 no exemplo, por forma a poderem ser utilizados na sua redução.Desta forma, os símbolos de antevisão necessitam apenas ser calculados nas transiçõesvazias, coorespondendo à antevisão após o não terminal que lhe está associado. Casoesse não terminal seja o último símbolo da regra, então são símbolos de antevisão todosos símbolos de antevisão do estado anterior à transição vazia.

O estado inicial tem por antevisão o fim do ficheiro, representado por 0$.

Exemplo 5.11 Associando os símbolos de antevisão às transições vazias ficamos com um autó-

mato finito não determinista semelhante ao do exemplo 5.10

68 CAPÍTULO 5. ANÁLISE SINTÁCTICA ASCENDENTE POR TABELA

r0 S’ → 0ր$

S 1 $

r1 S → 2ր

′b′

A 3 ’b’ 4 ’b’ 5

r2 | 6 ’a’ 7 ’a’ 8 ’b’ 9

r3 | 10 ’b’ 11ր

′a′

A 12 ’a’ 13

r4 A → 14 ’a’ 15

o primeiro passo no cálculo do autómato determinista tem presente que o estado 0 transporta o

símbolo de antevisão $ para os estados 2, 6, 10, mas que o estado 2 apenas transporta o símbolo

de antevisão ’b’ para o estado 14, pois quando a regra A →′ a′ reduzir só poderá ter a seguir o

símbolo ′b′,

estado entrada move fecho − ε\move novo estado

0$ 2$, 6$, 10$, 14′b′ I0

no segundo passo todos os símbolos de antevisão são transportados para o respectivo estado se-

guinte da regra, que de acordo com a sequência de numeração utilizada corresponde também ao

número seguinte. Apenas a transição vazia do estado 11 para o estado 14 necessita calcular novossímbolos de antevisão,

estado entrada move fecho − ε\move novo estado

0$ 2$, 6$, 10$, 14′b′ I0

I0 S 1$ I1

A 3$ I2

’a’ 7$, 15′b′ I3

’b’ 11$ 14′a′

I4

a tabela fica completa transportando os restantes símbolos de antevisão,

estado entrada move fecho − ε\move novo estado

0$ 2$, 6$, 10$, 14′b′ I0

I0 S 1$ I1

A 3$ I2

’a’ 7$, 15′b′ I3

’b’ 11$ 14′a′

I4

I2 ’b’ 4$ I5

I3 ’a’ 8$ I6

I4 A 12$ I7

’a’ 15′a′

I8

I5 ’b’ 5$ I9

I6 ’b’ 9$ I10

I7 ’a’ 13$ I11

5.2. GRAMÁTICAS LALR(1) 69

Notar que agora o estado 15 aparece com o símbolo de antevisão ’b’ em I3, mas com ’a’ em I8.

Desta forma, a redução da regra 4 é apenas colocada na coluna ’b’ para o estado I3 e apenas na

coluna ’a’ para o estado I8. Podemos verificar, através de uma observação cuidada da gramá-

tica do exemplo que este é efectivamente o caso, ficando a tabela de análise gerada pelo método

LALR(1)

′a′ ′b′ $ S A

0 s3 s4 g1 g2

1 acc

2 s5

3 s6 r4

4 s8 g7

5 s9

6 s10

7 s11

8 r4

9 r1

10 r2

11 r3

Quando os símbolos de antevisão não podem ser identificados no autómato não deter-minista, quando o não terminal é o último símbolo da regra, utilizaremos o símbolo ∝

para indicar o transporte dos símbolos de antevisão anteriores para os estados atingidospela transição vazia. Por exemplo, S → 25

ր∝

A 26.

Como foi referido, os autómatos não determinista e determinista LALR(1) são iguaisaos LR(0), com a excepção da anotação dos símbolos de antevisão a cada ocorrência doestado não determinista. Assim, quando se repete um conjunto de estados não determi-nista é porque se está na presença do mesmo estado determinista. Contudo, o cálculodos símbolos de antevisão tem de ser completamente calculada, pois pode diferir daocorrência inicial. Sempre que existe discrepância entre os símbolos de antevisão dosdiversos estados não determinista, de um mesmo estado determinista, estes devem serfundidos. No entanto, caso já tenham sido calculados novos estados, os símbolos deantevisão devem ser propagados a esses estados.

Exemplo 5.12 Considere-se uma simplificação da instrução if com e sem else da linguagem C

r0 S’ → 0ր$

S 1 $r1 S → 2 ’i’ 3

ր∝

S 4

r2 | 5 ’i’ 6ր

′e′

S 7 ’e’ 8ր∝

S 9

r3 | 10 ’x’ 11

70 CAPÍTULO 5. ANÁLISE SINTÁCTICA ASCENDENTE POR TABELA

o cálculo do autómato finito determinista até à repetição do estado I2 é idêntica ao exemplo 5.11

estado entrada move fecho − ε\move novo estado

0$ 2$, 5$, 10$ I0

I0 S 1$ I1

’i’ 3$, 6$ 2$, 5$, 10$ I2

’x’ 11$ I3

I0 S 4$, 7$ I4

’i’ 3$, 6$ 2′e′ , 5

′e′ , 10′e′ (I2)

neste ponto surje uma nova ocorrência do estado I2 = {3, 6, 2, 5, 10} mas com símbolos de

antevisão distintos nos estados 2, 5, 10. Para que os símbolos de antevisão das duas ocorrências

fiquem coerentes é necessário que esses três estados passem a ter ambos os símbolos de antevisão,

passando I2 = {3$, 6$, 2$,′e′ , 5$,′e′ , 10$,′e′} em ambas as ocorrências. No entanto, os estados I3

e I4 já foram entretanto calculados com base nos anteriores símbolos de antevisão de I2, embora

haja situações em os novos símbolos de antevisão de I2 não afectam I3 e I4 este não é o caso.

Assim, o processo deve ser repetido a partir da primeira ocorrência de I2, mas agora com os novos

símbolos de antevisão

estado entrada move fecho − ε\move novo estado

0$ 2$, 5$, 10$ I0

I0 S 1$ I1

’i’ 3$, 6$ 2$,′e′ , 5$,′e′ , 10$,′e′ I2

’x’ 11$,′e′ I3

I0 S 4$,′e′ , 7$,′e′ I4

’i’ 3$,′e′ , 6$,′e′ 2$,′e′ , 5$,′e′ , 10$,′e′ (I2)

notar que mais uma vez a segunda ocorrência de I2 ficou com símbolos de antevisão distintos da

primeira ocorrência, mas desta vez nos estados 3 e 6. O processo repete-se, mas desta vez sem

consequências para os estados entretanto já re-calculados, ficando a tabela completa

5.3. ELIMINAÇÃODE CONFLITOS 71

estado entrada move fecho − ε\move novo estado

0$ 2$, 5$, 10$ I0

I0 S 1$ I1

’i’ 3$,′e′ , 6$,′e′ 2$,′e′ , 5$,′e′ , 10$,′e′ I2

’x’ 11$,′e′ I3

I0 S 4$,′e′ , 7$,′e′ I4

’i’ 3$,′e′ , 6$,′e′ 2$,′e′ , 5$,′e′ , 10$,′e′ (I2)

’x’ 11$,′e′ (I3)

I4 ’e’ 8$,′e′ , 7$,′e′ 2$,′e′ , 5$,′e′ , 10$,′e′ I5

I5 S 9$,′e′ I6

’i’ 3$,′e′ , 6$,′e′ 2$,′e′ , 5$,′e′ , 10$,′e′ (I2)

’x’ 11$,′e′ (I3)

finalmente a tabela de análise apresenta, como seria de esperar, um conflito deslocamento-redução,

′i′ ′e′ ′x′ $ S

0 s2 s3 g1

1 acc

2 s2 s3 g4

3 r3 r3

4 s5/r1 r1

5 s2 s3 g6

6 r2 r2

5.3 Eliminação de conflitos

Se surjem conflitos na construção de tabelas de análise pelo método LALR(1) é neces-sário determinar a origem desses conflitos. Uma gramática ambígua produz sempreconflitos e, muito provavelemente, não reflecte a linguagem que pretende modelar. Defacto, as linguagens utilizadas em computadores, sejam linguagens de programaçãoou outras, não são ambíguas, excepto em casos muitos particulares. Noutras situaçõesapenas, um símbolo de antevisão pode não ser suficiente, sendo necessário resolver oproblema noutras fases do processamento, como a análise lexical ou a análise semântica.

No entanto, a maioria das linguagens utilizadas pelos computadores não só não é am-bígua como pode ser processada com apenas um símbolo de antevisão. Tal deve-se aofacto de quem criou a linguagem ter tido presente as ferramentas de análise existen-tes, salvo poucas excepções. O problema reside no facto de encontrar uma gramáticaLALR(1) que descreva a linguagem em questão. De facto, mesmo que tal gramática, ou

72 CAPÍTULO 5. ANÁLISE SINTÁCTICA ASCENDENTE POR TABELA

gramáticas, existam nada me garante que a minha análise da linguagem produza umadessas gramáticas.

Nesta secção tentaremos ver alguns procedimentos simples que permitem modificar agramática por forma a evitar conflitos desnecessários e que resultam, em geral, de víciosdo utilizador ou de descrições que dirigem o utilizador para soluções menos conflituo-sas.

Os conflitos têm sempre origem em reduções, sendo estas efectuadas quando se atínge oúltimo estado de uma regra, para certos símbolos de antevisão. Se o estado deterministacontiver outros estados não deterministas em posição de redução ou deslocamento, parao mesmo símbolo de antevisão, então esse estado tem um conflito. Desta forma a pri-meira regra para evitar conflitos é criar regras compridas, ou seja com muitos símbolos,face a muitas regras curtas, pois cada regra usada necessita ser reduzida. Existe aindauma vantagem no desempenho do analisador já que cada regra usada consome doispassos de processamento, um para a redução e outro para o movimento, e provavel-mente necessitará de mais estados na tabela de análise. Além disso cada novo símbolonão terminal acrescenta mais uma coluna à tabela.

5.3.1 Conflitos deslocamento-redução

Um conflito deslocamento-redução tem origem numa regra estar em condições de serreduzida para o símbolo de antevisão em questão, enquanto outra regra ou regras po-dem deslocar esse símbolo.

Exemplo 5.13 Considere-se a gramática

S → ’x’ ’y’ ’z’

| A ’y’

| A ’z’

A → ’x’

onde a sequência xy pode ser reconhecida pela primeira regra ou pela segunda regra seguida da

quarta. Após a leitura do carácter ’x’, quando o carácter de antevisão é ’y’, a primeira regra pode

deslocar-se mais um símbolo, mas a quarta regra pode reduzir para processar o ’y’ na segunda

regra.

A redução do número de regras, nomeadamente com a aplicação da propriedade distri-butiva, enumera as diversas soluções e elimina as reduções problemáticas.

A utilização de regras vazias, por transportarem todos os símbolos de antevisão, está naorigem demuitos conflitos deslocamento-redução. Estas regras têm em geral origem no

5.3. ELIMINAÇÃODE CONFLITOS 73

facto de certa entidade poder ocorrer zero ou mais vezes. No entanto, tal facto pode serdescrito com duas regras idênticas: uma das regras contém a entidade como obrigatóriae a outra não contém a entidade. As duas regras representam uma ou mais ocorrênciasda entidade e a sua não ocrrência, ou seja, no seu conjunto zero ou mais ocorrências daentidade.

Exemplo 5.14 Na linguagem C um bloco pode conter zero ou mais declarações seguida de zero

ou mais instruções, qualquer delas terminada em ’;’. Assim, em vez de indicar

bloco → ’{’ decl_opt instr_opt ’}’

decl_opt →

| decl

instr_opt →

| instr

enumeram-se a quatro possibilidades, ou seja,

bloco → ’{’ decl instr ’}’

| ’{’ decl ’}’

| ’{’ instr ’}’

| ’{’ ’}’

Quando a gramática apresenta um conflito deslocamento-redução não admissível nalinguagem, pode-se criar dois novos símbolos não terminais. Um deles trata a situaçãogenérica, enquanto o outro enumera as regras anteriormente em conflito, mas referindoagora os novos símbolos não terminais.

Exemplo 5.15 No caso do problema if then else já abordado no exemplo 5.12

instr → if expr then instr else instr

| if expr then instr

| outras

pode ser reescrito resolvendo a ambiguidade, garantindo que não pode aparecer um bloco com

apenas if then entre um then e um else,

instr → genérica

| anterior

genérica → if expr then genérica else genérica

| outrasanterior → if expr then genérica else anterior

| if expr then instr

74 CAPÍTULO 5. ANÁLISE SINTÁCTICA ASCENDENTE POR TABELA

Finalmente, caso não se consiga encontrar uma solução viável, pode-se sempre mantero conflito e assumir que o deslocamento é a solução adoptada. No entanto, isto sig-nifica que tabela de análise gerada não corresponde à gramática inicial, atendendo aométodo de geração utilizado. Para evitar estar dependente do comportamento da ferra-menta, o utilizador pode indicar prioridades e associatividades das regras como formade controlar o processamento de gramáticas ambíguas ( ver 5.4 ). No caso de conflitosredução-redução esta perspectiva já não se aplica.

5.3.2 Conflitos redução-redução

Um conflito redução-redução surje quando duas regras se encontram simultaneamenteem posição de redução para o mesmo símbolo de antevisão. Tal acontece quando duasregras, total ou parcialmente, idênticas podem ser utilizadas no mesmo contexto.

Exemplo 5.16 Considere-se a gramática

S → A

| B

A → ’x’

| ’y’

B → ’x’

| ’z’

onde após a leitura do símbolo ’x’ quer a regra A →′ x′ como a regra B →′ x′ podem podem ser

reduzidas para o símbolo de antevisão $.

Uma solução consiste em considerar que ambas as ocorrências da parte comum da re-gra podem ser tratadas sintacticamente como a mesma, sendo quaisquer distinções serefectuadas semânticamente. Ou seja, embora fosse desejável separar o processamento,as técnicas existentes não permitem fazê-lo através de regras sintácticas. Assim, cria-se uma única regra, com a parte comum, e trata-se as possíveis diferenças através deatributos.

Exemplo 5.17 Na gramática do exemplo 5.16 transporta-se a parte comum das regras em con-

flito para a regra comum que as deriva,

S → A

| B

| ’x’

A → ’y’

B → ’z’

5.4. UTILIZAÇÃODETERMINISTA DE GRAMÁTICAS AMBÍGUAS 75

A solução de atrasar, para a análise semântica, as diferenças no contexto de utilização daparte comum das regras em conflito permite eliminar conflitos redução-redução. Umasituação comum consiste na utilização de regras idênticas para manipular diversos ti-pos de dados, por exemplo em operações polimórifcas. A utilização de uma só regra,permite reduzir a gramática, em especial se houver muitos tipos de dados possíveis, etornar a análise sintáctica mais rápida. No entanto, a escolha da operação a realizar teráde ser efectuada pela análise semântica, podendo recorrer para tal a técnicas específicas,a estudar na devida altura.

Uma outra solução consiste em permitir ao analisador lexical, através de informaçãosemântica como a tabela símbolos ou informação de tipos, possa separar um elementolexical em dois ou mais. Por exemplo, um identificador pode ser caracterizadocomo tipo, variável, constante ou função, permitindo criar regras específicas que nãodependam todas de um identificador comum. Namesma perspectiva, o analisadorlexical pode procurar agrupar um maior número de elementos, de tal forma que nãotenha de ser a capacidade de antevisão limitada do analisador sintáctico a desenvolveresse esforço.

Exemplo 5.18 Considerando o exemplo 5.16, basta contextualizar a utilização do carácter ’x’por forma a diferenciá-lo em ’x’ e ’w’

S → A

| B

A → ’w’

| ’y’

B → ’x’

| ’z’

5.4 Utilização determinista de gramáticas ambíguas

Quando a gramática em questão não é LALR(1), a sua transformação em LALR(1) é,em geral, acessível. Para mais, muitos dos conflitos encontrados ao fazer uma análiseLALR(1) devem-se a ambiguidades na própria gramática ( não podendo ser analisadapor métodos deterministas ) e não necessariamente à falta de antevisão da gramática.Muitas destas ambiguidades podem ser removidas através de directivas que produzemtabelas deterministas. Notar que embora a tabela resultante não contenha conflitos, agramática utilizada ainda é ambígua, ou seja, a tabela não reflecte apenas da aplicaçãodo método.

76 CAPÍTULO 5. ANÁLISE SINTÁCTICA ASCENDENTE POR TABELA

Quando a gramática produzida contém conflitos, mas o utilizador sabe qual o compor-tamento que pretende em cada conflito, deve indicá-lo explícitamente. Tal evita dúvidasfuturas e documenta claramente o comportamento desejado.

5.4.1 Atribuição de associatividades e prioridades

Amanutenção de conflitos deslocamento-redução traduz, para ferramentas como o ge-rador yacc, na opção pelo deslocamento. Embora esta possa ser, de uma forma geral,a opção mais desejada, tal não é necessariamente o caso. A utilização de prioridades eassociatividades nas regras permite remover conflitos deslocamento-redução, optandoexplicitamente pelo deslocamento ou pela redução. Claro que a utilização destas técni-cas de forma cega, embora removendo os conflitos, pode traduzir-se numa gramáticacompletamente distinta da especificada e, por vezes, não reflectindo a linguagem quepretende efectivamente processar. Desta forma, a utilização de prioridades e associati-vidades para eliminar conflitos deve ser utilizada conscientemente.

A determinação de prioridades e associatividades num analisador sintáctico ascendenteestá associada a regras e a operadores. Embora algumas ferramentas, como o geradoryacc, permitam indicar a prioridade e associatividade de um operador, na realidade outilizador está a indicar a prioridade e associatividade todas as regras que usam essesímbolo. Notar que quando se pretende atribuir prioridades distintas regras que usamo mesmo operador, como o operador ’-’ unário e binário, torna-se necessário identificara regra através de uma etiqueta única.

Num conflito deslocamento-redução, ao optar pela redução estamos a obrigar a aplicarimediatamente a regra, antes de ver o que se segue, ou seja estamos a ser associativos àesquerda. Da mesma forma, ao optar pelo deslocamento vamos acumulando na pilhaas diversas ocorrências, sendo posteriormente obrigados a reduzir as regras pela ordeminversa, pois trata-se de uma pilha. Finalmente, caso uma regra não seja associativa,não podemos permitir nem a redução nem o deslocamente, limitando-nos a removerambos e deixar em seu lugar uma situação de erro.

Exemplo 5.19 Considermos um exemplo apenas com o operador subtração. Este operador tem a

vantagem de produzir resultados distintos consoante é usado associativo à esquerda ou à direita,

mas do ponto de vista gramatical o problema subsiste para operadores comutativos, como por

exemplo a soma.

E → E ’-’ E

| ’id’

a tabela de análise LALR(1) apresenta um conflito deslocamento-redução

5.4. UTILIZAÇÃODETERMINISTA DE GRAMÁTICAS AMBÍGUAS 77

′−′ ′id′ $ E

0 s2 g1

1 s3 acc

2 r2 r2

3 s2 g4

4 s3/r1 r1

Considerando a sequência de entrada x - y - z, o estado 4 que contém o conflito é atingido duas

vezes, correspondentes às posições na sequência de entrada x - y • - z e x - y - z •. Na última

posição, o símbolo de antevisão é $, a que não corresponde o conflito. O conflito é atingido em x- y • - z, onde se optarmos pela redução obtemos ( x - y ) - z, pois ao reduzir aplicamos a regra

e efectuamos a operação de subtração indicada. Por outro lado, se optarmos pelo deslocamento

obtemos x - (y - z) pois quando chegarmos ao fim da sequência reduzimos os símbolos no topo da

pilha, e só depois este resultado é subtraído de x.

A determinação de prioridades envolve a interação entre regras distintas, ou seja, quala acção a realizar no estado associado à regra em questão na presença do símbolo deantevisão associado à outra regra. Se o símbolo de antevisão é mais prioritário somosobrigados a deslocar por forma a atrasar a redução do actual, o menos prioritário. In-versamente, é necessário reduzir na presença de um símbolo de antevisão menos prio-ritário. Quando se tratam de situações de igual prioridade, como entre as operações desoma e subtração, aplicam-se as regras da associatividade atrás descritas.

A identificação dos estados associados à regras é simples, pois como se trata de umconflito deslocamento-redução, a regra em causa é aquela pode ser reduzida.

Exemplo 5.20 Consideremos uma gramática, deliberandamente ambígua, mas onde a lingua-

gem a processar apresenta, por ordem crescente de prioridades, operações de soma, multiplicação,

potência, e valor simétrico. Do ponto de vista de associatividade pretende-se que quer a soma

como a multiplicação sejam associativas à esquerda, a potência pretende-se associativa à direita

e, finalmente, o valor simétrico não seja associativo.

E → E ’+’ E

| E ’*’ E

| E ’ˆ’ E

| ’-’ E

| ’id’

a que corresponde a tabela de análise LALR(1)

78 CAPÍTULO 5. ANÁLISE SINTÁCTICA ASCENDENTE POR TABELA

′+′ ′∗′ ’ˆ’ ′−′ ′id′ $ E

0 s2 s3 g1

1 s4 s5 s6 acc

2 s2 s3 g7

3 r5 r5 r5 r5

4 s2 s3 g8

5 s2 s3 g9

6 s2 s3 g10

7 s4/r4 s5/r4 s6/r4 r4

8 s4/r1 s5/r1 s6/r1 r1

9 s4/r2 s5/r2 s6/r2 r2

10 s4/r3 s5/r3 s6/r3 r3

se nos concentrarmos na sub-tabela constituída pelos 12 conflitos e pelo símbolo de antevisão ’-’

estado da regra ′+′ ′∗′ ’ˆ’ ′−′

E → ’-’ E• 7 r4 r4 r4

E → E ’+’ E• 8 r1 s5 s6 r1

E → E ’*’ E• 9 r2 r2 s6 r2

E → E ’ˆ’ E• 10 r3 r3 s6 r3

Notar que a atribuição de prioridades não se aplica exclusivamente a operadores, masa qualquer tipo de regras.

Exemplo 5.21 Ao atribuir maior prioridade à instrução if com else que àquela sem o else,estamos a fazer o conjunto else ficar associado ao if mais próximo. Tal é o comportamente usual

nas linguagens de programação, como a linguagem C por exemplo.

Na tabela de análise do exemplo 5.12 devemos optar pelo deslocamento ficando,

′i′ ′e′ ′x′ $ S

0 s2 s3 g1

1 acc

2 s2 s3 g4

3 r3 r3

4 s5 r1

5 s2 s3 g6

6 r2 r2

Embora seja esse o comportamento por omissão das ferramentas, a indicação explícita das priori-

dades evita mensagens desnecessárias e afirma inequivocamente a opção do utilizador.

5.4. UTILIZAÇÃODETERMINISTA DE GRAMÁTICAS AMBÍGUAS 79

Finalmente convém referir que a atribuição de prioridades e associatividades a gramá-ticas ambíguas apresenta ganhos significativos na dimensão das tabelas de análise eno número de passos de processamento, quando comparado com gramáticas não am-bíguas que resolvem explicitam essas prioridades e associatividades gramaticalmente.De facto, numa linguagem com 10 níveis de prioridade têm de existir 9 regras adicio-nais para transitar entre prioridades, na presença de símbolos de antevisão associadosa regras de menor prioridade. Tais regras aumentam, necessariamente, a dimensão dastabelas. Por outro lado, cada vez que o operador menos prioritário é utilizado, as 9 re-gras de transição têm de ser reduzidas, e após cada redução é ainda necessário efectuara movimentação para o novo estado. Esta situação é mais frequente que se pode supore justifica técnicas específcas para obviar esta sobrecarga ( ver 5.5.3 ).

5.4.2 Conflitos redução-redução

A manutenção de conflitos redução-redução, obriga a ferramenta a optar por usar umadas reduções, sendo em geral escolhida a regra de número mais baixo. Nesta situação,a ordem pela qual as regras são enumeradas na gramática é crítica. De qualquer forma,não é de mais repetir, que a tabela de análise resultante não reflecte a gramática especi-ficada. Notar que uma escolha criteriosa da ordem das regras permite que duas regrasem conflito possam ambas ser reduzidas para símbolos de antevisão distintos. Umavez que o conflito deve-se apenas a certos símbolos de antevisão, todos os restantes sãocorrectamente tratados. A escolha da ordem de enumeração das regras na gramáticapermite apenas que uma das regras se sobreponha a outra para os símbolos de antevi-são em conflito. Em geral, a regra mais longa, ou seja com maior número de símbolos,deve ser preferida, e consequentemente colocada primeiro na gramática, que uma maiscurta.

Exemplo 5.22 Considere-se o exemplo, onde os operadores ’ˆ’ e ’_’ permitem elevar o indiciar

uma variável. As três primeiras regras permitem, respectivamente, representar Xyz , X

y e Xz. No

entanto, a composição das segunda e terceira regra apenas permite obter situações do tipo Xzy ou

Xyz , dependendo da ordem com que são aplicadas.

E → E ’ˆ’ E ’_’ E

| E ’ˆ’ E

| E ’_’ E

| ’(’ E ’)’

| ’id’

80 CAPÍTULO 5. ANÁLISE SINTÁCTICA ASCENDENTE POR TABELA

’ˆ’ ′_′ ′(′ ′)′ ′id′ $ E

0 s2 s3 g1

1 s4 s5 acc

2 s2 s3 g6

3 r5 r5 r5 r5

4 s2 s3 g7

5 s2 s3 g8

6 s4 s5 s9

7 s4/r2 s10/r2 r2 r2

8 s4/r3 s5/r3 r3 r3

9 r4 r4 r4 r4

10 s2 s3 g11

11 s4/r1/r3 s5/r1/r3 r1/r3 r1/r3

a tabela contém 8 conflitos deslocamento-redução e 2 conflitos redução-redução. Notar que os dois

estados com um conflito triplo representam apenas dois conflitos deslocamento-redução, sendo

ignorados os conflitos entre reduções, pois existe um deslocamento nesse estado.

Notar que se impusermos uma associatividade à esquerda aos operadores ’ˆ’ e ’_’ ficamos com

4 conflitos redução-redução, pois optamos por reduções em detrimento dos deslocamentos nos

estados de conflito triplo. Por outro lado, ao impor uma associatividade à direita a esses mesmos

operadores sobram apenas os 2 conflitos redução-redução anteriormente existentes. Contudo, a

alteração das associatividades altera a interpretação das sequências de entrada quando não se

utilizam parênteses.

5.5 Compactação de tabelas LR

Embora os métodos estudados, quer descendente como ascendentes, produzam tabelasde análise de dimensões aceitáveis, para linguagens reais, é possível compactar as tabe-las com reduzido custo de acesso. Nesta secção abordaremos alguns métodos que tirampartido das características específicas das tabelas geradas por analisadores ascendentes.

Um dos métodos genéricos consiste na possibilidade de fusão de linhas iguais, com acriação de um nível de indirecção no acesso. Este método é frequentemente utilizadoem tabelas de analisadores ascendentes quando se separa as colunas dos elementos ter-minais dos não terminais. Notar que este processo é idêntico ao utilizado nas tabelas deanálise de expressões regulares, mas neste último caso para colunas e não para linhas.

5.5. COMPACTAÇÃODE TABELAS LR 81

5.5.1 Tabelas esparsas

As tabelas de análise ascendente estudadas produzem muitas situações de erro, repre-sentadas por posições em branco nas tabelas. Em geral, mais de metade das posições, eem muitos casos quase 90%, das entradas da tabela de análise são situações de erro. Osmétodos de compactação de tabelas permitem reduzir significativamente a dimensãodestas.

Embora existam métodos genéricos de compactação de tabelas esparsas, nesta secçãotrataremos apenas métodos específicos para tabelas de análise ascendente. Depois daaplicação destes métodos é sempre possível aplicar ummétodo genérico para aumentarainda mais a compactação.

Um desses métodos genéricos já referido, no caso das tabelas para analisadores predi-tivos descendentes, é designado por double-offset indexing ( ver 4.3.1 ). Neste métodoprocura-se sobrepor as diversas linhas, por qualquer ordem e não necessariamente ali-nhadas umas com as outras, por forma a que o número de posições vazias seja mínimo.

5.5.2 Propagação de reduções

Como vimos atrás, as tabelas de análise geradas pelos métodos LR(0), SLR(1) e LALR(1)apenas diferem no número de reduções. Caso não originem conflitos, o método LR(0)tem maior número de reduções que os outros mas não compromete a correcção dasequência em análise. De facto, se em vez de reportar um erro, efectuarmos uma re-dução possível nesse estado, o resultado produzido não consiguirá ser posteriormentedeslocado. Assim, à parte de atrasar a detecção de erros, a propagação de uma reduçãopara outros símbolos de antevisão desse estado não afecta a correcção da análise, desdeque não introduza conflitos.

Propagação de reduções unitárias

Se um estado da tabela de análise inclui apenas reduções de uma regra e situações deerro designa-se por uma redução unitária. Nesta situação, podemos propagar a reduçãoda regra em questão por todos os símbolos de antevisão, como é o caso das tabelasgeradas pelo método LR(0), sem produzir conflitos. Assim, ao atingir este estado, querseja através de um deslocamento ou de um movimento, iremos reduzir a regra.

Na prática não existe necessidade de o analisador se mover para esse estado, pois a re-dução pode ser efectuada em substituição do salto. Este estado pode pois ser eliminado,desde que os movimentos para ele sejam substituídos pela redução que ele representa.

82 CAPÍTULO 5. ANÁLISE SINTÁCTICA ASCENDENTE POR TABELA

Por exemplo em vez de g7 será colocado r4 assumindo que o estado 7 apenas contémreduções da regra 4.

As operações de deslocamento são na realidade duas operações em sequência, um des-locamento do símbolo à cabeça da sequência de entrada para o topo da pilha, seguido deum movimento para o estado indicado. Se for criada uma nova operação que mantémo deslocamento mas substitui o movimento pela redução, o estado contendo a reduçãounitária pode ser removido da tabela. Assim, na tabela uma operação designada porL4 representa um deslocamento seguido da redução da regra 4 e substitui um desloca-mento para estado que continha a redução unitária, por exemplo s7.

Exemplo 5.23 A tabela do exemplo 5.7 foi criada pelo método LR(0) pelo que as reduções já

estão propagadas para todos os símbolos de antevisão. Neste caso são eliminados 4 estados e

substituídos os saltos, resultando uma tabela com quase metade da dimensão original,

′(′ ′a′ ′)′ ′,′ $ S A

0 s2 g1

1 acc

2 s2 L2 r3 g3

3 L1 s7

7 L4

os números dos estados originais foi mantido para referência com o exemplo 5.7.

Exemplo 5.24 No exemplo 5.11 apenas os 4 últimos estados podem ser eliminados, depois de

propagadas as reduções, já que o estado 3 inclui um deslocamento além de uma redução

′a′ ′b′ $ S A

0 s3 s4 g1 g2

1 acc

2 s5

3 s6 r4

4 L4 g7

5 L1

6 L2

7 L3

5.5. COMPACTAÇÃODE TABELAS LR 83

Propagação de reduções quase unitárias

Caso um estado inclua deslocamentos ou reduções de mais de uma regra já não é pos-sível eliminá-lo, como no caso da propagação de reduções unitárias. No entanto, se umestado contiver apenas reduções de uma só regra e deslocamentos, as reduções aindapodem ser propagadas para todos os símbolos de antevisão que representem situaçõesde erro. A propagação de reduções para situações de erro do mesmo estado, só por sinão torna a tabelas mais esparsas, antes pelo contrário.

Podemos considerar que as posições vazias de um estado, correspondentes a situaçõesde erro, representam o comportamento por omissão ( default, em inglês ) do estado.Nos estados em que se deu a propagação das reduções quase unitárias, desaparecendoas situações de erro existentes, a redução propagada passa a ser o seu comportamentopor omissão.

Criando uma nova coluna na tabela para o comportamento por omissão ( que desig-naremos por def ) e transportando para esta coluna a operação por omissão do estado,pode-se remover das restantes colunas os valores de omissão. Nesta situação, as posi-ções vazias da tabela indicam a necessidade de consultar a coluna de omissão. Comoresultado, em gramáticas de linguagens reais, a tabela resultante fica mais esparsa. O re-sultado só é vantajoso se o aumento de uma coluna ( a de omissão ) for compensada poruma significativa redução no número de reduções. Tal facto só se verifica em gramáticascom muitos símbolos de antevisão.

Exemplo 5.25 No exemplo 5.24, logo depois de removidos os estados de reduções unitárias, o

estado 3 inclui uma redução quase unitária.

′a′ ′b′ $ def S A

0 s3 s4 g1 g2

1 acc

2 s5

3 s6 r4

4 L4 g7

5 L1

6 L2

7 L3

Notar que neste exemplo, em que existe uma única redução quase unitária, limitamo-nos a des-

locar a redução de uma coluna para outra, embora a tabela passe a ter mais uma coluna. No

entanto, como as operações por omissão são quase todas situações de erro, e consequentemente

representadas por posições vazias, a tabela é igualmente esparsa. De facto, utilizando o método

84 CAPÍTULO 5. ANÁLISE SINTÁCTICA ASCENDENTE POR TABELA

de compressão double-offset indexing ambas as soluções ocupam 13 posições. Como a tabela

é pequena a redução pelo método genérico de double-offset indexing é de apenas 30%, ou seja

cerca de metade da tabela original do exemplo 5.11.

5.5.3 Reduções unitárias

Regras unitárias, cuja derivação tem um único símbolo, estão frequentemente associa-das a restrições de prioridade entre operadores. Atendendo a que uma linguagem deprogramação pode ter mais de 10 níveis de prioridade, podemos ter sequências de 11reduções consecutivas e respectivos movimentos.

A compactação de reduções unitárias pretende, com base no símbolo de antevisão re-duzir directamente para a regra associada a esse símbolo, poupando todas as reduçõesintermédias. De um ponto de vista prático, isto só pode ser efectuado se não houveracções semânticas associadas a essas regras intermédias, pois seriam ignoradas. Tal é,geralmente, o caso.

Note-se que este expediente não diminui a dimensão das tabelas, muito pelo contrá-rio. De facto, cada estado em vez de conter reduções de uma só regra, possibilitandoa propagação de reduções unitárias ou quase unitárias, vamos passar a ter diversas re-duções distintas, consoante o símbolo de antevisão. A vantagem reside no facto de oprocessamento ser mais eficiente, pois é efectuado em menos passos.

Uma solução mais compacta e igualmente eficiente, consiste em especificar uma gramá-tica ambígua e atribuir prioridades e associatividades aos operadores ( ver 5.4.1 ). Esteexpediente reduz efectivamente o número de regras e consequentemente o número deestados da tabela de análise, mas a sua especificação depende da ferramenta utilizada edas suas capacidades.

5.6 Recuperação de erros

Até aqui temos considerado que ao encontrar uma posição de erro, o erro é reportadoe, o processamento termina. De facto, após ser encontrado um erro sintáctico deixa deser possível produzir resultados úteis, pois a sequência de entrada não é válida. Noentanto, pode ser útil continuar o processamento à procura de mais erros. Notar quetal procedimento pode ser, e muitas vezes é, contraproducente já que os restantes errossão consequência do primeiro. Nestes casos, após a correcção do primeiro erro, todos

5.6. RECUPERAÇÃODE ERROS 85

os restantes desaparecem. Desta forma, embora a recuperação de erros seja desejável, asua utilidade é limitada.

5.6.1 Recuperação por símbolo de erro

Uma solução para a recuperação de erros consiste em introduzir regras específicas parao efeito, permitindo que o processamento recomece em locais específicos da gramática.Estas novas regras incluem um símbolo terminal reservado para a recuperação de erro.

Em caso de erro, o analisador considera o símbolo de erro como símbolo de antevisão.Se o estado actual não permite a deslocação deste símbolo, o analisador vai retirandosucessivamente elementos da pilha até atingir um estado que o permita. Descobertoo estado, o analisador vai removendo símbolos da sequência de entrada até que existauma acção válida nesse estado.

Exemplo 5.26 Considerando a gramática com símbolo inicial S, onde error é o símbolo de

recuperação de erro,

S → ’(’ A ’)’

A → ’a’

| S

| A ’,’ S

| error

e a tabela LALR(1) resultante é

′(′ ′a′ ′)′ ′,′ error $ S A

0 s2 g1

1 acc

2 s2 s4 s6 g5 g3

3 s7 s8

4 r2 r2

5 r3 r3

6 r5 r5

7 r1 r1 r1

8 s2 g9

9 r4 r4

onde o estado 6 é o estado de recuperação de erro. Considere-se a análise da sequência de entrada

(a a), não pertencente à linguagem descrita pela gramática,

86 CAPÍTULO 5. ANÁLISE SINTÁCTICA ASCENDENTE POR TABELA

pilha entrada acção

0 (aa)$ shift-2

0(2 aa)$ shift-4

0(2a4 a)$ error

0(2a4 error a)$ discard-4

0(2 error a)$ shift-6

0(2error6 a)$ discard-a

0(2error6 )$ reduce-5

0(2A )$ goto-3

0(2A3 )$ shift-7

0(2A3)7 $ reduce-1

0S $ goto-1

0S1 $ accept

Ao compactar a tabela de análise acima obtém-se

′(′ ′a′ ′)′ ′,′ error $ def S A

0 s2 g1

1 acc

2 s2 L2 L5 r3 g3

3 L1 s8

8 s2 r4

não havendo agora um estado de recuperação de erro explícito, devendo a recuperação do símbolo

de antevisão ser efectuada na primeira utilização do símbolo de antevisão, após o deslocamento

do símbolo de erro, error.

pilha entrada acção

0 (aa)$ shift-2

0(2 aa)$ L-2

0(2A a)$ goto-3

0(2A3 a)$ error

0(2A3 error a)$ discard-3

0(2 error a)$ L-5

0(2A a)$ goto-3

0(2A3 a)$ discard-a

0(2A3 )$ L-1

0S $ goto-1

0S1 $ accept

5.6. RECUPERAÇÃODE ERROS 87

No entanto, a análise da sequência de entrada ((a), também não pertencente à linguagem descrita

pela gramática, mas entra em ciclo na recuperação de erro, pelo que a recuperação não é possível.

pilha entrada acção

0 ((a)$ shift-2

0(2 (a)$ shift-2

0(2(2 a)$ shift-4

0(2(2a4 )$ reduce-2

0(2(2A )$ goto-3

0(2(2A3 )$ shift-7

0(2(2A3)7 $ reduce-1

0(2S $ goto-5

0(2S5 $ error

0(2S5 error$ discard-5

0(2 error$ shift-6

0(2error6 $ error

0(2error6 error$ discard-6

0(2 error$ shift-6

Se a primeira acção válida, após a descoberta do estado de erro, for um deslocamento, oanalisador conseguiu recuperar. Contudo, se a acção for uma redução, quando a regrade recuperação de erro termina com o símbolo de erro, esta redução pode ser falsa poissó no deslocamento seguinte teremos a certeza. Desta forma, as regras de recuperaçãode erro devem ter, preferencialmente, um símbolo terminal após o símbolo de erro.

Exemplo 5.27 A gramática do exemplo 5.26 pode ser alterada para recuperar explicitamente

no parênteses de fechar em S. No entanto, tais alterações não evitam que possam aparecer ciclos

na recuperação de erros, pois estes evidenciam a incapacidade de recuperar de alguns erros com

este método.

S → ’(’ A ’)’

| error ’)’

A → ’a’

| S

| A ’,’ S

A colocação de regras de erro deve ser cuidada pois, em primeiro lugar a introduçãode novas regras pode sempre gerar conflitos, além disso o excesso destas regras podedificultar a determinação de qual a regra que será activada em cada situação, a menosque se tenha presente o conteúdo da pilha do analisador. Assim, nas linguagens de

88 CAPÍTULO 5. ANÁLISE SINTÁCTICA ASCENDENTE POR TABELA

programação aconselha-se a recuperar de declarações e instruções, sendo a declaraçãoou instrução errada ignorada pelo processamento do erro.

A detecção de um erro interrompe o normal processamento das regras, nomeadamentedas acções semânticas a elas associadas. No caso destas acções terem efeitos secun-dários, isto é, modificarem variáveis globais, a recuperação dos erros pode conduzir ainconsistências. Por exemplo, ao recuperar da declaração de uma função, qualquer ac-ção que necessitasse ser executada no fim da identificação da mesma não o será. Damesma forma, se as acções semânticas influenciam a forma como os símbolos terminaissão enviados por um analisador lexical, por exemplo a definição de um novo tipo dedados ( typedef, na linguagem C ), os resultados podem ficar incorrectos após o primeiroerro.

Para evitar comportamentos inesperados na recuperação de erros teremos de ter apenasacções semânticas sem efeitos secundários. A construção de árvore sintácticas é umasolução que permite obviar os efeitos secundários indesejáveis, entre outras vantagensque abordaremos mais adiante.

5.6.2 Recuperação automática∗

Nos processos automáticos é o próprio analisador que vai manipulando os símbolosna pilha e na sequência de entrada até encontrar uma derivação válida. Estes métodosembora complexos não exigem alterações na gramática.

A recuperação global de erros procura o menor conjunto de inserções e remoções desímbolos que transforma a sequência de entrada numa sequência correcta, mesmo queessas inserções e remoções não sejam no ponto onde o erro foi detectado.

O método, designado por panic-mode, é um método de recuperação automática simplesque se resume a:

1. guardar uma cópia da pilha do analisador;

2. retirar o elemento do topo e verificar se existe uma entrada válida na tabela. Seexistir a recuperação está concluída;

3. se não existir entrada válida voltar a 2. até esvaziar a pilha;

4. quando a pilha fica vazia, avançar um elemento lexical, repor uma cópia da pilhainicial (antes do erro) e voltar a 2.;

5. se se atingiu o fim do ficheiro (último elemento lexical), não foi possível recuperardo erro.

5.7. OUTRAS GRAMÁTICAS LR∗ 89

Devido ao risco de erros em cascata, não devem ser impressas mensagens nos 4 a 5passos (deslocamentos ou reduções) que se seguem ao erro.

5.7 Outras gramáticas LR∗

Existem outros métodos de análise LR além dos atrás apresentados. Na prática estastécnicas são de pouca utilidade pois a dimensão das tabelas cresce rapidamente. Poroutro lado, estas técnicas não são de facto necessárias, pois todas as linguagens proces-sáveis deterministicamente têm gramáticas SLR(1). Nesta linha, até o método LALR(1)não seria necessário, mas como produz tabelas da mesma dimensão e suporta ummaiornúmero de gramáticas torna-se na escolha mais frequente.

5.7.1 Gramáticas LR(1)

O método LR(1), também designado por LR canónico, é o método ascendente mais ge-ral com um só símbolo de antevisão. O autómato LR(1) replica todos os estados decada regra para todos os símbolos de FOLLOW. Assim, uma regra com 3 símbolos deFOLLOW e que derive 4 símbolos terá 15 estados no autómato finito não deterministaLR(1), ao contrário dos 5 estados do autómato LR(0). Cada estado não determinista temassociado um dos símbolos de FOLLOW. As transições vazias, no autómato não de-terminista LR(1), são efectuadas apenas para os primeiros estados do não terminal quetêm associado o símbolo de FOLLOW que é antevisão da transição vazia. Na realidade,o símbolo de FOLLOW é apenas utilizado quando a regra termina num símbolo nãoterminal. Neste caso, em vez de utilizar os símbolos de antevisão precedentes, como nométodo LALR(1), cada transição vazia atinge apenas os estados iniciais do não terminalcom mesmo símbolo de antevisão. As reduções são apenas efectuadas para o símbolode antevisão associado ao estado que reduz.

Se atendermos que as regras das gramáticas de linguagens reais podem ter dezenasde símbolos de FOLLOW, o autómato não determinista LR(1) fica muito extenso e,comparativamente, também o respectivo autómato determinista.

Exemplo 5.28 Como no método LR(1) o símbolo de antevisão é apenas utilizado no último

estado, este será colocado após os restantes símbolos da regra. Aliás, este já era o expediente para

introduzir o símbolo de fim de sequência utilizado na regra auxiliar da gramática aumentada.

90 CAPÍTULO 5. ANÁLISE SINTÁCTICA ASCENDENTE POR TABELA

r0 S’ → 0ր$

S 1 $

r1 S → 2ր

′c′,′d′

A 3ր$

A 4 $

r2 A → 5 ’c’ 6ր

′c′

A 7 ’c’

8 ’c’ 9ր

′d′

A 10 ’d’11 ’c’ 12

ր$

A 13 $r3 | 14 ’d’ 15 ’c’

16 ’d’ 17 ’d’18 ’d’ 19 $

Agora, os símbolos associados às transições vazias não representam, como no caso do método

LALR(1), os símbolos de antevisão a transportar mas os símbolos de antevisão associados aos

estados não terminais atingidos. Por exemplo, a transição vazia do estado 2 dirige-se apenas

para os estado iniciais do não terminal A com os caracteres de antevisão ’c’ e ’d’, ou seja, os

estados 5, 8, 14 e 16. A determinação dos estados do autómato determinista é, a partir deste

ponto, a mesma já utilizada nos restantes métodos ascendentes, não necessitando de indicar os

símbolos de antevisão por estado não terminal, tal como no método LR(0) e SLR(1).

estado entrada move fecho − ε\move novo estado

0 2, 5, 8, 14, 16 I0

I0 S 1 I1

A 3 11, 18 I2

’c’ 6, 9 5, 8 I3

’d’ 15, 17 I4

I2 A 4 I5

’c’ 12 I6

’d’ 19 I7

I3 A 7, 10 I8

’c’ 6, 9 . . . (I3)

I6 A 13 I9

’c’ 12 (I6)

’d’ 19 (I7)

a tabela de análise é construída da mesma forma utilizada nos métodos anteriores, tendo o cuidado

de utilizar apenas o símbolo de antevisão associado ao estado a reduzir. Por exemplo, o estado 10reduz apenas para o símbolo ’d’.

5.7. OUTRAS GRAMÁTICAS LR∗ 91

′c′ ′d′ $ S A

0 s3 s4 g1 g2

1 acc

2 s6 s7 g5

3 s3 g8

4 r3 r3

5 r1

6 s6 s7 g9

7 r3

8 r2 r2

9 r2

A tabela resultante, com 10 estados, é apenas 43% maior que utilizando os métodos anteriores,

mas a gramática é muito pequena e usa muito poucos símbolos de antevisão. De facto esta

gramática também é LALR(1) e até LR(0), embora a tabela LR(1) resultante seja distinta.

O método LR(1) produz tabelas de grandes dimensões para gramáticas de linguagensusadas na prática. Por exemplo, linguagens como a linguagem C necessitam de poucomais de 200 regras ( correspondendo a quase 400 estados pelo método LALR(1) ), en-quanto a linguagem Java requer quase 400 regras e a linguagemC++mais de 600 regras.Se atendermos a que o método LR(1), gera cerca de dez vezes mais estados que o corres-pondente LALR(1), a sua utilização torna-se bastante limitativa. Na realidade, a maioriadas ferramentas de software existentes geram analisadores ascendentes LALR(1).

5.7.2 LALR(1) por agrupamento de estados LR(1)

A duplicação dos estados por símbolos de antevisão, utilizada pelo método LR(1), nãose justifica quando alguns desses símbolos são frequentemente utilizados em conjunto.Nestes casos, em que a gramática é LALR(1), as tabelas de análise resultantes ficamdesnecessariamente maiores. O método de agrupamento de estados permite obter ta-belas de análise LALR(1) a partir de estados LR(1). Caso a gramática não seja LALR(1)resultaram conflitos e o processo de agrupamento deverá ser abandonado.

O agrupamento baseia-se em procurar outros estados deterministas que contenham es-tados não deterministas, ou itens, distintos apenas nos respectivos símbolos de ante-visão. Diz-se que estes estados têm o mesmo núcleo, ou seja, se não se utilizassemsímbolos de antevisão associados aos itens, como no caso do método SLR(1), os esta-dos com o mesmo núcleo seriam exactamente iguais. Notar que cada item tem de terum item equivalente ( que difere apenas nos símbolos de antevisão ) no outro estadodeterminista. Não se trata de relações de contém ou está contido , pois basta que

92 CAPÍTULO 5. ANÁLISE SINTÁCTICA ASCENDENTE POR TABELA

um item não tenha equivalente para não se poder efectuar o agrupamento, mesmo quetodos os outros tenham.

Exemplo 5.29 No exemplo 5.28, os estados 15, 17 e 19 diferem apenas no símbolo de antevisão.

Como o estado I4 = {15, 17} e o estado I7 = {19} contêm estados que diferem apenas nos

símbolos de antevisão pode ser agrupados num estado I4,7 = {15, 17, 19}, ou seja o estado não

determinista LR(0) A → ′c′•, com os três símbolos de antevisão {′c′,′ d′, $}.

Também os estados I3 e I6 se podem agrupar em I3,6 = {5, 8, 11, 6, 9, 12, 14, 16, 18} ≡ {A →

•′c′A, A → ′c′ • A, A → •′d′, {′c′,′ d′, $}}, tal como I8 e I9 em I8,9 = {7, 10, 13} ≡ {A →′c′A•, {′c′,′ d′, $}}, ficando a tabela de análise reduzida a

′c′ ′d′ $ S A

0 s3,6 s4,7 g1 g2

1 acc

2 s3,6 s4,7 g5

3,6 s3,6 g8,9

4,7 r3 r3 r3

5 r1

8,9 r2 r2 r2

indicamos os estados com os seus números originais para facilitar a compreensão do processo de

agrupamento, mas através da renumeração dos estados obtemos uma tabela igual à gerada pelo

método LALR(1), pois a gramática é LALR(1).

O processo de agrupamento é pesado, pois tem de ser construído um autómato não de-terminista LR(1), efectuada a sua conversão em determinista, o que só por si é pesado,como ainda é necessário descobrir quais os estados a grupar. Identificar quais os esta-dos a agrupar é computacionalmente pesado, pois temos de comparar cada estado comtodos os restantes, mesmo os já entretanto agrupados. Quando realizado mentalmenteo processo simplifica-se pois podemos identificar os itens equivalentes e testar apenasos estados deterministas que contêm pelo menos um desses estados como possíveiscandidatos.

5.7.3 Gramáticas quase LALR(1)

A ideia que está na base das gramáticas quase LALR(1) consiste agrupar apenas os es-tados que não geram conflitos, podendo não chegar a obter uma tabela LALR(1). Noentanto, ao agrupar dois estados compatíveis podemos forçar outros estados não com-patíveis a serem agrupados, provocando um conflito redução-redução. Caso seja gerado

5.7. OUTRAS GRAMÁTICAS LR∗ 93

um conflito devemos recuar ( backtrack, em inglês ) e tentar outros agrupamentos. Paramais, a ordem de agrupamento pode influir no número final de estados, só podendo serobtido o número mínimo depois de tentadas todas as ordenações possíveis.

Para tornar o algoritmo mais acessível, é possível ir agrupando os estados à medida quevão sendo criados, nunca chegando a construir todos os estados do autómato LR(1).Nesta aproximação, designada por algoritmo de Pager, em vez de agrupar os estadose verificar os resultados obtidos, definem-se critérios que garantem um agrupamentoseguro.

O critério mais simples, designado por compatibilidade fraca, baseia-se em dois conjun-tos de símbolos de antevisão L1 e L2 de estados do núcleo de s e outros dois L1 e L2 donúcleo de s, onde os estados deterministas s e s são compatíveis. Os estados s e s sãofracamente compatíveis, e podem ser agrupados com segurança, se e só se garantiremcomulativamente as condições: L1 ∩ L2 = ∅, L1 ∩ L2 = ∅, L1 ∩ L2 6= ∅ e L1 ∩ L2 6= ∅.

O algoritmo, embora gere um analisador correcto, não produz necessariamente o nú-mero mínimo de estados. Os estados que não são fracamente compatíveis podem aindaser agrupados se o potencial conflito nunca é atingido, ou seja se os estados são forte-mente compatíveis. De facto, os símbolos de antevisão podem nem chegar a ser utiliza-dos, como vimos em alguns exemplos de exercícios anteriores. O agrupamento ainda épossível depois de verificar se as regras, cujos itens incluem os símbolos que geram opotencial conflito, fazem parte dos mesmos estados até à respectiva redução.

5.7.4 Gramáticas LR(k), k > 1

As técnicas de análise SLR(1), LALR(1) e LR(1) podem ser extendidas para utilizar maisum símbolo de antevisão. As gramáticas processáveis por estes métodos também vãosendo cada vez mais gerais, ou seja, para k > 0 temos que LR(k − 1) ⊂ SLR(k) ⊂

LALR(k) ⊂ LR(k) ⊂ SLR(k + 1). Para tal é necessário calcular os conjuntos de FIRSTe FOLLOW para k-ésimo símbolo, designados por FIRSTk e FOLLOWk . Os diversosmétodos podem ser resumidos por:

LR(k) : ao processar a → α • Bβ, x, antever B → •γ, y, onde y ∈ Firstk(βx). Reduzir aregraA → α para o símbolo de antevisão x se o estado corrente contiverA → α•, x.Deslocar para o símbolo de antevisão x se o estado corrente contiver A → α•aβ, y,onde a ∈ Vt e x ∈ First(aβy).

SLR(k) : uma produção é reduzida para o símbolo x se x ∈ FOLLOWk.

LALR(k) : agrupar os estados do autómato LR(k) com o mesmo núcleo.

94 CAPÍTULO 5. ANÁLISE SINTÁCTICA ASCENDENTE POR TABELA

Exemplo 5.30 Dois exemplos de gramáticas que são LR(2), mas não são LR(1),

S → A ’x’

| B ’c’

A → ’a’ ’c’

B → ’a’

S → X ’a’ ’b’

| Y ’a’ ’c’

X → ε

Y → ε

a gramática da esquerda apresenta um conflito deslocamento-redução para o símbolo ′c′, que

só pode ser resolvido com o segundo símbolo de antevisão: ′x′ ou $. A gramática da direita

apresenta um conflito redução-redução pois quer X ou Y são seguidos do símbolo ′a′, só podendo

ser efectuada a distinção no segundo símbolo de antevisão: ′b′ ou ′c′.

As tabelas LR(2) apresentam 2 símbolos de antevisão, estando apenas representadas as sequências

com pelo menos uma acção válida,

′a′,′ c′ ′c′,′ x′ ′x′, $ ′c′, $ $, $ S A B

0 s4 g1 g2 g3

1 acc

2 s5

3 s6

4 s7 r4

5 r1

6 r2

7 r3

′a′,′ b′ ′b′, $ ′a′,′ c′ ′c′, $ $, $ S X Y

0 r3 r4 g1 g2 g3

1 acc

2 s4

3 s5

4 s6

5 s7

6 r1

7 r2

5.8 Propriedades da análise ascendente

Todos os métodos de análise ascendente LR que referimos partilham características co-muns importantes. Nomeadamente, o facto de uma gramática ambígua nunca ser LR,embora nenhum dos métodos LR consiga processar todas as gramáticas livre de con-texto. Contudo, um analisador LR é:

5.8. PROPRIEDADES DA ANÁLISE ASCENDENTE 95

eficiente : o processamento é proporcional à dimensão da entrada.

correcto : detecta todas as entradas não válidas e produz resultados correctos para en-tradas válidas.

não ambíguo : produz resultados deterministas.

linear : no espaço e no tempo face ao número de símbolo na sequência de entrada.

5.8.1 Recursão em analisadores LR

Exceptuando o casos em que os operadores, ou outras regras gramaticais, têm asso-ciatividades definidas na própria linguagem, o utilizador pode optar por efectuar asrepetições através de recursividades à direita ou à esquerda. Como sabemos, as gra-máticas descendentes não podem ter recursividades à esquerda, necessitando ser cons-truída uma gramática equivalente de onde foram retiradas da todas as recursividades àesquerda. As gramáticas LR tornam-se mais apelativas pois não apresentam tais limi-tações. De facto, é possível construir tabelas de análise LR para gramáticas que tenhamquer recursividades à direita como à esquerda.

lista → lista term| term

lista → term lista| term

No entanto, os dois exemplos de recursividade acima apresentam comportamentosmuitos distintos ao derivar listas. Por exemplo, uma sequência de 5 elementos terminaisproduz sucessivamente as seguintes derivações

listalista term5

lista term4 term5

lista term3 term4 term5

lista term2 term3 term4 term5

term1 term2 term3 term4 term5

listaterm1 listaterm1 term2 listaterm1 term2 term3 listaterm1 term2 term3 term4 listaterm1 term2 term3 term4 term5

Um analisador ascendente constroi a estas sequências por ordem inversa, de baixo paracima.

recursão à esquerda Ao recorrer à recursão do lado esquerdo o analisador desloca oprimeiro terminal term1 para a pilha e de seguida aplica a segunda regra, reduzindo-o alista. Seguidamente, desloca term2 e, novamente, reduz o conjunto a lista. Desta forma,a pilha contém no máximo dois elementos e 10

6em média.

96 CAPÍTULO 5. ANÁLISE SINTÁCTICA ASCENDENTE POR TABELA

recursão à direita Na recursão à direita todos os cinco terminais são deslocados paraa pilha, antes da primeira redução. Depois temos uma sucessão de cinco reduções,a começar no term5 e a terminar no term1. Assim, a pilha contém no máximo cincoelementos e em média 20

6.

Em resumo, a recursão à direita requer uma pilha auxiliar maior que a recursão à es-querda e efectua as reduções pela ordem inversa da sequência de entrada. A dimensãoda pilha é pois função da maior sequência de entrada possível, no caso da recursãoà direita, e função da gramática, no caso da recursão à esquerda. Consequentemente,aconselha-se a utilizar, sempre que possível, recursão à esquerda em gramáticas LR,pois podemos controlar as regras da gramática, mas não a sequência de entrada.

associatividade Como acabamos de ver, em analisadores LR a recursão à esquerdaproduz associatividade à esquerda e recursão à direita produz associatividade à direita.Quando se pretende que o operador tenha associatividade à direita somos levados autilizar recursão à direita. Para poder ter associatividade à direita com recursão à es-querda podemos alterar as regras gramaticais de tal forma que as acções semânticas aelas associadas possam construir uma árvore sintáctica inversa.

lista → lista term criar a lista| ε adicionar ao fim da lista

inverte → lista verificar se é uma lista vazia

Num analisador ascendente a primeira redução é efectuada para a derivação vazia (ε),que cria a lista. Cada novo elemento é adicionado a essa lista pela ordem inversa, per-mitindo o seu posterior processamento pela ordem correcta, a que corresponde a as-sociatividade à direita. No fim, como com a gramática alterada passámos a permitirlistas vazia, necessitamos de verificar se a lista não contém elementos e, caso seja o caso,reportar um erro.

Notar que o processo não é estritamente gramatical, requerendo que as acções associa-das a cada regra colaborem no processo. O mesmo processo também pode ser utilizadopara permitir associatividade à esquerda com recursividade à direita. No entanto, mui-tos compiladores não recorrem a este processo, apresentando um limite de recursão noprocessamento de operadores associativos à direita. Embora a maioria das linguagensnão tenha muitos operadores associativos à direita, um teste pode ser realizado repe-tindo sucessivamente, em geral 5000 vezes é suficiente, tal operador. Por exemplo, nalinguagem C pode-se repetir sucessivas atribuições de uma variável a ela própria.

5.9. EXERCÍCIOS 97

5.8.2 Análise ascendente versus descendente

As duas técnicas de análise sintácticas mais utilizadas nas escrita de compiladores sãoo método descendente LL(1) e o método ascendente LALR(1). O método LL(1) mais in-tuitivo pela forma como as regras são derivadas. Por outro lado, as gramáticas LALR(1)são, em geral, mais fáceis de obter e menos limitativas em termos de recursividades efactorizações. A colocação de acções é mais permissiva em analisadores LL(1) além de,como veremos mais adiante, permite a utilização de atributos sintetizados e herdados.A recuperação de erros é também mais simples nos analisadores LL(1) pois a pilha con-tém informação sobre o que falta processar, ao contrário dos ascendentes que contêm ainformação lida.

A dimensão das tabelas nos analisadores ascendentes pode ser, no pior caso, exponen-cial com a dimensão da gramática. Contudo, as tabelas LL(1) contêm 10% de entradasnão nulas enquanto as tabelas LALR(1) apenas 5%, permitindo maiores compressões.Mesmo assim, as tabelas LALR(1) resultam cerca de 50% maiores que as tabelas LL(1),para algumas das linguagens de programação mais conhecidas.

Em resumo, o método LL(1) é mais vantanjaso em todos os aspectos excepto nas limita-ções da gramática. Contudo, como a maioria dos esforço desenvolvido pelo utilizadorna análise sintáctica reside na escrita da gramática, o método LALR(1) acaba por sermais utilizado, excepto se já existir um gramática LL(1) disponível.

5.9 Exercícios

Exercício 5.1 Desconhecendo a gramática e o método ascendente utilizado para gerar a tabela

seguinte, justifique se a gramática é LR(1), LALR(1), SLR(1) ou LR(0):

′a′ ′b′ $ S A B

0 s3 g1 g2

1 acc

2 s5 g4

3 r2

4 r1

5 r3

Exercício 5.2 Diga, justificadamente, porque a gramática seguinte não é SLR(1):

S → Y ’b’

Y → ’a’

| ’a’ ’b’

98 CAPÍTULO 5. ANÁLISE SINTÁCTICA ASCENDENTE POR TABELA

Exercício 5.3 Construa a tabela de análise SLR(1) da gramática

M → M N

| ’a’

| ε

Y → ’b’

| ε

1. Através da análise da tabela produzida justifique porque a gramática não é LR(0).

2. Diga justificadamente se a gramática é LALR(1)

Exercício 5.4 Diga, justificadamente, porque a gramática seguinte é LALR(1), mas não SLR(1):

X → M ’a’

| ’b’ M ’c’

| ’d’ ’c’

| ’b’ ’d’ ’a’

M → ’a’

Exercício 5.5 Apresente a tabela de análise, pelo método LALR(1), da gramática

E → ’[’ E ’;’ L ’]’

| ’id’

L → E

| E ’;’ L

Exercício 5.6 Pretende-se desenvolver um analisador sintáctico ascendente para a gramática

seguinte:

A → y | z

B→ B A x | A B z y | B y | x

Onde B é o símbolo inicial e { x, y, z } é o conjunto de símbolos terminais da gramática.

1. Construa a tabela de parsing para um analisador sintáctico ascendente LALR(1) para pro-

cessar a gramática acima, indicando o conjunto de estados do parser, e os símbolos de

antevisão.

2. Compacte a tabela de análise que obteve, eliminando as reduções unitárias e quase unitá-

rias, bem como propagar as reduções que permitam compactar a tabela.

5.9. EXERCÍCIOS 99

3. Apresente uma tabela com o conteúdo da pilha, a entrada e a acção realizada em cada

passo da análise, quando a sequência de entrada é: x y z x . Indique em quantos passos é

processada a sequência indicada.

(em caso de conflito assuma o mesmo comportamento da ferramenta YACC)

Exercício 5.7 Considere a gramática,

S → ’a’

| ’(’ L ’)’

| ’(’ error ’)’

L → S

| L ’,’ S

Construa uma tabela de análise, pelo método SLR(1), e apresente os passos necessários para a

análise das sequências:

1. (a, (a, , a, ), (a))

2. (a, , (a), a)

3. (a, a, (a, (a, a)

Exercício 5.8 Construa a tabela de análise para a gramática seguinte, resolvendo os conflitos

que encontrar por forma a que as operações tenham a precedência usual,

E → ’while’ E ’do’ E

| ’id’ ’:=’ E

| E ’+’ E

| ’id’

Exercício 5.9 Considere a gramática onde se pretende que os operadores tenham a prioridade e

associatividade usado na linguagem C.

E → E bin E

| una E

| ’(’ E ’)’

| ’id’

bin → ’=’ | ’+’ ’ | ’-’ | ’*’ | ’/’ | ’%’

una → ’-’ | ’*’

1. Justifique porque são necessárias alterações à gramática, não bastando resolver os conflitos

de acordo com as prioridades.

100 CAPÍTULO 5. ANÁLISE SINTÁCTICA ASCENDENTE POR TABELA

2. Apresente a tabela de análise de uma gramática ambígua equivalente, mas que já permita

definir as prioridades e associatidades por resolução dos conflitos criados.

3. Apresente a tabela de análise de uma gramática não ambígua equivalente, onde as priori-

dades e associatidades foram resolvidas gramaticalmente

4. Indique em quantos passos é analisada a sequência x = −a + b ∗ (c − d), por ambas as

tabelas de análise. Compare as duas soluções quanto à dimensão da tabela e número de

passos necessários.

5. Construa a tabela de análise para a gramática não ambígua aplicando as reduções unitá-

rias existentes e compare o número de passos agora necessários para processar a mesma

sequência.