Вы находитесь на странице: 1из 13

Anlise Sinttica de Programas

1 INTRODUO
Cada linguagem de programao possui as regras que descrevem a estrutura sinttica
dos programas bem formados. Em Pascal por exemplo, um programa constitudo por
blocos, um bloco por comandos, um comando por expresses , uma expresso por
tokens e assim por diante. A sintaxe das construes de uma linguagem de programao
pode ser descrita pelas gramticas livres de contexto ou pela notao BNF (Forma de
Bakcus Naur). As gramticas oferecem vantagens significativas tanto para os
projetistas de linguagens quanto para os escritores de compiladores.
Uma gramtica oferece, para uma linguagem de programao, uma
especificao sinttica precisa e fcil de entender.

Para certas classes de gramticas, podemos construir automaticamente um


analisador sinttico que determine se um programa-fonte est sintaticamente
bem-formado. Como benefcio adicional, o processo de construo do analisador
pode revelar ambigidades sintticas bem como outras construes difceis de se
analisar gramaticalmente, as quais poderiam, de outra forma, seguir indetectadas
na fase de projeto inicial de uma linguagem e de seu compilador.

Uma gramtica propriamente projetada implica uma estrutura de linguagem de


programao til traduo correta de programas-fonte em cdigos objeto e
tambm deteco de erros. Existem ferramentas disponveis para a converso
de descries de tradues, baseadas em gramticas, em programas operativos.

As linguagens evoluram ao longo de um certo perodo de tempo, adquirindo


novas construes e realizando tarefas adicionais. Essas novas construes
podem ser mais facilmente includas quando existe uma implementao baseada
numa descrio gramatical da linguagem.

2 O PAPEL DO ANALISADOR SINTTICO


Existem trs tipos gerais de analisadores sintticos. Os mtodos universais de anlise
sinttica, tais como o algoritmo de Cocke-younger-Kasami e o de Earley, podem tratar
qualquer gramtica. Esses mtodos, entretanto, saio muito ineficientes parta se usar num
compilador de produo. Os mtodos mais comumente usados nos compiladores so
classificados como top-down ou bottom-up. Como indicado por seus nomes, os
analisadores sintticos top-down, constroem rvores do topo (raiz) para o fundo
(folhas), enquanto que os bottom-up comeam pelas folhas e trabalham rvore acima at
a raiz. Em ambos os casos, a entrada varrida da esquerda para a direita, um smbolo de
cada vez.
Os mtodos de anlise sinttica mais eficientes, tanto top-down quanto bottom-up,
trabalham somente em determinadas subclasses de gramticas, mas vrias dessas
subclasses, como as das gramticas LL e LR, so suficientemente expressivas para

descrever a maioria das construes sintticas das linguagens de programao. Os


analisadores implementados manualmente trabalham freqentemente com gramticas
LL; por exemplo. Assumimos que a sada de um analisador sinttico seja alguma
representao da rvore gramatical para o fluxo de tokens produzido pelo analisador
lxico. Na prtica, existe um certo nmero de tarefas que poderiam ser conduzidas
durante a anlise sinttica, tais como coletar informaes sobre os vrios tokens na
tabela de smbolos, realizar a verificao de tipos e outras formas de anlise semntica,
assim como gerar o cdigo intermedirio.
3 TRATAMENTO DE ERROS DE SINTAXE
Se um compilador tivesse que processar somente programas corretos, seu projeto e sua
implementao seriam grandemente simplificados. Mas os programadores
freqentemente escrevem programas incorretos, e um bom compilador deveria assistir o
programador na identificao e localizao de erros. gritando que, apesar dos erros
serem lugar-comum, poucas linguagens sejam projetadas tendo-se o tratamento de erros
em mente. Nossa civilizao seria radicalmente diferente se as linguagens faladas
tivessem as mesmas exigncias de correo sinttica que as das linguagens de
computadores. A maioria das especificaes das linguagens de programao no
descreve como um compilador deveria responder aos erros; tal tarefa deixada para o
projetista desde o incio poderia ser tanto simplificar a estrutura de um compilador
quanto melhorar sua resposta aos erros.
Sabemos que os programas podem conter erros em muitos nveis diferentes. Por
exemplo, os erros podem ser:
lxicos, tais como errar a grafia de um identificador, palavra-chave ou operador
sintticos, tais como uma expresso aritmtica com parnteses no balanceados

semnticos, tais como um operador aplicado a um operando incompatvel

lgicos, tais como uma chamada infinitamente recursiva

Freqentemente, boa parte da deteco e recuperao de erros num compilador gira em


torno da fase de anlise sinttica. Isto porque os erros ou so sintticos por natureza ou
so expostos quando o fluxo de tokens proveniente do analisador lxico desobedece s
regras gramaticais que definem a linguagem de programao. Outra razo est na
preciso dos modernos mtodos de anlise sinttica; podem detectar muito
eficientemente a presena de erros sintticos num programa. Detectar precisamente a
presena de erros semnticos ou lgicos em tempo de compilao muito mais difcil.
Um tratador de erros num analisador sinttico possui metas simples de serem
estabelecidas:
- Deve relatar a presena de erros clara e acuradamente.
- Deve se recuperar de cada erro suficientemente rpido a fim de ser capaz de detectar
erros subseqentes.
- No deve retardar significativamente o processamento de programas corretos.
A realizao efetiva dessas metas apresenta desafios difceis.

Felizmente, os erros comuns so simples e freqentemente basta um mecanismo de


tratamento de erros relativamente direto. Em alguns casos, entretanto, um erro pode ter
ocorrido muito antes de sua presena ter sido detectada e sua natureza precisa pode ser
muito difcil de ser deduzida. Em casos difceis, o tratador de erros pode ter que
advinhar o que o programador tinha em mente quando o programa foi escrito.
Vrios mtodos de anlise sinttica, tais como os mtodos LL e LR, detectam os erros
to cedo quanto possvel. Mais precisamente, possuem a propriedade do prefixo vivel,
significando que detectam que um erro ocorreu to logo tenham examinado um prefixo
da entrada que no seja o de qualquer cadeia da linguagem.
Como deveria um tratador de erros reportar a presena de um erro? No mnimo, deveria
informar o local no programa fonte onde o mesmo foi detectado, uma vez que existe
uma boa chance de o erro efetivo ter ocorrido uns poucos tokens antes. Uma estratgia
comum empregada por muitos compiladores a de imprimir a linha ilegal com um
apontador para a posio na qual o erro foi detectado. Se existir um razovel
prognstico de que o erro realmente foi, uma compreensvel mensagem de diagnstico
informativa tambm includa; por exemplo, ponto-e-vrgula ausente nesta posio.
Uma vez que o erro tenha sido detectado, como deveria o analisador sinttico se
recuperar? Existe um nmero de estratgias gerais, mas nenhum mtodo claramente se
impe sobre os demais. Na maioria dos casos, no adequado para o analisador
sinttico encerrar logo aps detectar o primeiro erro, porque o processamento da entrada
restante ainda pode revelar outros. Usualmente, existe alguma forma de recuperao de
erros na qual o analisador tenta restaurar a si mesmo para um estado onde o
processamento da entrada possa continuar com uma razovel esperana de que o resto
correto da entrada ser analisado e tratado adequadamente pelo compilador.
Um trabalho inadequado de recuperao pode introduzir uma avalancha de erros
esprios, que no foram cometidos pelo programador, mas introduzidos pelas
modificaes no estado do analisador sinttico durante a recuperao de erros. Numa
forma similar, uma recuperao de erros sintticos pode introduzir erros semnticos
esprios que sero detectados posteriormente pelas fases de anlise semtntica e de
gerao de cdigo. Por exemplo, ao se recuperar de um erro, o analisador pode pular a
declarao de alguma varivel, digamos zap. Quando zap for posteriormente encontrada
nas expresses, no haver nada sintaticamente errado, mas como no h uma entrada
na tabela de smbolos para zap, a mensagem zap no definido ser gerada.
Uma estratgia cautelosa para o compilador a de inibir as mensagens de erro que
provenham de erros descobertos muito proximamente no fluxo de entrada. Em alguns
casos, pode haver erros demais para o compilador continuar um processamento sensvel
(por exemplo, como deveria um compilador Pascal responder ao receber um programa
Fortran como entrada?). Parece que uma estratgia de recuperao de erros tem que ser
um compromisso cuidadosamente considerado levando em conta os tipos de erros que
so mais propensos a ocorrer e razoveis de processar.
Alguns compiladores tentam reparar os erros, num processo em que tentam adivinhar o
que o programador queria escrever. O compilador PL/C (Conway e Wilcox [1973])
um exemplo desse tipo. Exceto, possivelmente, num ambiente de pequenos programas
escritos por estudantes principiantes, a reparao extensiva de erros no propensa a

pagar o seu custo. De fato, com a nfase crescente na computao interativa e bons
ambientes de programao, a tendncia parece estar na direo de mecanismos simples
de recuperao de erros.
4 ANLISE SINTTICA TOP-DOWN
A anlise sinttica top-down pode ser vista como uma tentativa de ser encontrar uma
derivao mais esquerda para uma cadeia de entrada. Equivalentemente, pode ser vista
como uma tentativa de se construir uma rvore gramatical, para a cadeia de entrada, a
partir da raiz, criando os ns da rvore gramatical em pr ordem. Consideramos agora
uma forma geral de anlise sinttica top-down, chamada de descendncia recursiva, que
pode envolver retrocesso, ou seja, a realizao de esquadrinhamentos repetidos da
entrada. Por outro lado, os analisadores sintticos com retrocesso no so vistos muito
freqentemente. Uma razo est em que o retrocesso raramente necessitado para
analisar sintaticamente construes de linguagens de programao. Em situaes tais
como a anlise sinttica de linguagens naturais, o retrocesso ainda ineficiente e
mtodos tabulares, tais como o algoritmo de programao dinmica ou mtodo de
Earley [1970] so preferidos.
O retrocesso exigido no prximo exemplo, e iremos sugerir uma forma de controlar a
entrada quando o mesmo ocorrer.
Exemplo: Consideremos a gramtica
S cAd
A ab | a
E a cadeia de entrada w=cad. Para construir uma rvore gramatical para esta cadeia, de
cima para baixo, criamos inicialmente uma rvore constituindo de um nico n rotulado
S. O apontador da entrada aponta para c, o primeiro smbolo de w. Em seguida, usamos
a primeira produo para S a fim de expandir a rvore.
A folha mais esquerda, rotulada c, reconhece o primeiro smbolo de w e, por
conseguinte, avanamos o apontador da entrada para a, o segundo smbolo de w, e
consideramos a prxima filha, rotulada A. Em seguida, expandimos A usando a sua
primeira alternativa, obtendo a rvore da figura (b). Temos agora um reconhecimento
para o segundo smbolo da entrada e, conseqentemente, avanamos para o apontador
da entrada para d, o terceiro smbolo da entrada, e comparamos d com a prxima folha,
rotulada b. Como b no igual a d, reportamos uma falha e retornamos a A a fim de
verificar se existe uma outra alternativa que no tenhamos tentado ainda, mas que
poderia produzir um reconhecimento.
Ao irmos de volta para A, precisamos restabelecer o apontador da entrada para a
posio 2, aquela que o mesmo detinha quando passamos pela primeira vez por A, o que
significa que o procedimento para A precisa armazenar o apontador da entrada numa
varivel local. Tentamos agora a segunda alternativa de A afim de obter a rvore na
figura (c). A folha a reconhece o segundo smbolo de w e a folha d o terceiro. Uma vez
que produzimos uma rvore gramatical para w, paramos e anunciamos o trmino com
sucesso da anlise sinttica.

Uma gramtica recursiva esquerda pode levar um analisador sinttico de descendncia


recursiva, mesmo com retrocesso, a um lao infinito. Isto , quando tentamos expandir
A, podemos eventualmente nos encontrar de novo tentando expandir A sem ter
consumido nenhum smbolo da entrada.
5 ANALISADORES SINTTICOS PREDITIVOS
Em muitos casos, escrevendo-se cuidadosamente uma gramtica, eliminando-se a
recurso a esquerda e fatorando-se esquerda a gramtica resultante, podemos obter
uma nova gramtica processvel por um analisador sinttico de descendncia recursiva
que no necessite de retrocesso, isto , um analisador preditivo. Para construir um
analisador sinttico preditivo, precisamos conhecer, dado o smbolo corrente de entrada
a e o no terminal A a ser expandido, qual das alternativas de produo A 1 | 2 |... |
n a nica que deriva uma cadeia comeando por a. Ou seja, a alternativa adequada
precisa ser detectvel examinando-se apenas para o primeiro smbolo da cadeia que a
mesma deriva. As construes de controle de fluxo na maioria das linguagens de
programao, com suas palavras-chave distintivas, so usualmente detectveis dessa
forma. Por exemplo, se tivermos as produes:
cmd if expr then cmd else cmd
| while expr do cmd
| begin lista_de_comandos end
ento as palavras-chave if, while e begin nos informam qual alternativa a nica que
possivelmente teria sucesso, se quisssemos encontrar um comando.
5.1 Diagramas de Transies para Analisadores Sintticos Preditivos
As vrias diferenas entre os diagramas de transies para um analisador lxico e um
analisador sinttico preditivo so imediatamente aparentes. No caso de um analisador
sinttico, existe um diagrama para cada no terminal. Os rtulos dos lados so tokens e
no terminais. Uma transio em um token (terminal) significa que devemos realiz-la
se aquele token for o prximo smbolo da entrada. Uma transio num no-terminal A
chamada de procedimento para A.
Para construir um diagrama de transies de um analisador sinttico preditivo a partir de
uma gramtica, eliminamos primeiro da gramtica a recursividade esquerda e, em
seguida, a fatoramos esquerda. Para cada no-terminal A, ento fazemos o seguinte:
1. Criamos um estado inicial e um final (de retorno).
2. 2. Para cada produo A X1,X2...Xn, criamos um percurso a partir do estado
inicial at o estado final, com os lados rotulados X1,X2,...,Xn.
O analisador preditivo ao trabalhar sobre os diagramas de transies se comporta como
segue. Comea no estado inicial para o smbolo de partida. Se aps algumas aes
estiver no estado s, o qual possui um lado rotulado pelo terminal a apontado para o
estado t, e se o prximo smbolo de entrada for a, move o cursor de entrada uma posio
direita e vai para o estado t. Se, de outra feita, o lado for rotulado pelo no terminal A,
vai para o estado de partida A, sem movimentar o cursor de entrada. Se em algum
instante for atingido o estado final de A, vai imediatamente para o estado t, tendo como

efeito, lido A a partir da entrada, durante o tempo em que se movia do estado s para t.
Finalmente, se existir um lado de s para t rotulado , vai, a partir do estado s,
imediatamente para o estado t, sem avanar na entrada.
Um programa de anlise sinttica preditiva baseado num diagrama de transies tenta
reconhecer smbolos terminais na entrada e faz uma chamada de procedimento
potencialmente recursiva sempre que precisar seguir um lado rotulado por um no
terminal. Uma implementao no recursiva pode ser obtida empilhando-se o estado s
quando existir uma transio em um no terminal para fora de s e removendo-se o topo
da pilha quando o estado final para o no terminal for atingido.
A abordagem acima funcionar se o diagrama de transies dado for determinstico, isto
, no existir mais de uma transio de um mesmo para outros mesma entrada. Se a
ambigidade ocorrer, deveremos estar capacitados a resolv-la de uma forma ad-hoc. Se
o no determinismo no puder ser eliminado, no poderemos construir um analisador
sinttico preditivo, mas poderemos construir um analisador de descendncia recursiva
com retrocesso, de forma a tentar sistematicamente todas as possibilidades, se esta fosse
a melhor estratgia de anlise que pudssemos encontrar.
5.2 Anlise Sinttica Preditiva No-Recursiva
possvel construir um analisador preditivo no-recursivo mantendo explicitamente
uma pilha, ao invs de implicitamente atravs de chamadas recursivas. O problemachave durante a anlise preditiva determinar que produo deve ser aplicada a um
dado no terminal.
Um analisador sinttico preditivo dirigido por uma tabela possui um buffer de entrada,
uma pilha, uma tabela sinttica e um fluxo de sada. O buffer de entrada possui a cadeia
a ser analisada, seguida por um $ direita para indicar o fim da cadeia de entrada. A
pilha contm uma seqncia de smbolos gramaticais, com $ indicando o fundo da
pilha. Inicialmente, a pilha contm o smbolo de partida da gramtica acima de $. Uma
tabela sinttica um array bidimensional M[A,a], onde A um no terminal e a um
terminal ou outro smbolo $.
O analisador sinttico controlado por um programa que se comporta como segue. O
programa considera X o smbolo ao topo da pilha e a o smbolo corrente de entrada.
Esses dois smbolos determinam a ao do analisador. Existem trs possibilidades:
1. Se X=A=$, o analisador pra e anuncia o trmino com sucesso da anlise
sinttica.
2. Se X=a$, o analisador sinttico remove X da pilha e avana o apontador da
entrada para o prximo smbolo.
3. Se X um no terminal, o programa consulta a entrada M[X,a] da tabela
sinttica M. Essa entrada ser uma produo X da gramtica ou uma entrada
de erro. Se, por exemplo, M[X,a]={X UVW}, o analisador substitui X no topo
da pilha por WVU (com U ao topo). Como sada, iremos assumir que o
analisador sinttico simplesmente imprima a produo usada; de fato, qualquer
outro cdigo poderia ser executado aqui. Se M[X,a]=erro, o analisador chama
uma rotina de recuperao de erros.

O comportamento do analisador sinttico pode ser descrito em termos de suas


configuraes, que do o contedo da pilha e a entrada restante.
5.2.1 Primeiro e Seguinte
A construo de um analisador sinttico preditivo auxiliada por duas funes
associadas gramtica G. Essas funes, Primeiro e Seguinte, nos permitem preencher
as entradas de uma tabela sinttica preditiva para G, sempre que possvel. Os conjuntos
de tokens produzidos pela funo seguinte podem tambm ser usados como tokens de
sincronizao durante a recuperao de erros na modalidade do desespero.
Se for qualquer cadeia de smbolos gramaticais, seja primeiro() o conjunto de
terminais que comeam as cadeias derivadas a partir de . Definamos seguinte(A), para
o no terminal A, como sendo um conjunto de terminais a que podem figurar
imediatamente direita de A em alguma forma sentencial, isto , o conjunto de
terminais a tais que exista uma derivao para algum e . Se A puder ser o smbolo
mais direita em alguma forma sentencial, ento $ est em SEGUINTE(A).
5.3 Recuperao de Erros na anlise Preditiva
A pilha de um analisador preditivo no recursivo torna explcitos os terminais e no
terminais que o mesmo espera reconhecer com o restante da entrada. Iremos
conseqentemente nos referir aos smbolos na pilha do analisador da discusso que se
segue. Um erro detectado durante a anlise preditiva quando o terminal ao topo da
pilha no reconhece o prximo smbolo de entrada ou quando o no terminal A est ao
topo da pilha, a o prximo smbolo de entrada e a entrada da tabela sinttica M[A,a]
est vazia.
A recuperao de erros na modalidade do desespero est baseada na idia de se pular
smbolos na entrada at que surja um token pertencente a um conjunto pr selecionado
de tokens de sincronizao. Sua efetividade depende da escolha do conjunto de
sincronizao. Os conjuntos deveriam ser escolhidos de tal forma que o analisador se
recuperasse rapidamente dos erros que tendessem a ocorrer na prtica. Algumas tcnicas
heursticas so:
Como ponto de partida, podemos colocar todos os smbolos de SEGUINTE(A)
no conjunto de tokens de sincronizao para o no terminal A. Se pularmos
tokens at que um elemento de SEGUINTE(A) seja visto e removermos A da
pilha, provvel que a anlise sinttica possa continuar.

No suficiente usar SEGUINTE(A) como o conjunto de sincronizao para A.


Por exemplo, se os pontos-e-vrgulas terminarem os enunciados, como em C,
ento as palavras-chave que iniciam os enunciados no devem aparecer no
conjunto SEGUINTE do no-terminal que gera expresses. Um ponto-e-vrgula
ausente aps uma atribuio pode conseqentemente resultar na omisso da
palavra chave que inicia o prximo enunciado. Freqentemente, existe uma
estrutura hierrquica nas construes da linguagem; por exemplo, as expresses
aparecem dentro de enunciados, que figuram dentro de blocos e assim por
diante. Podemos adicionar ao conjunto de sincronizao de uma construo mais
baixa os smbolos que comeam as construes mais altas. Por exemplo,

poderamos adicionar palavras-chave que iniciam comandos aos conjuntos de


sincronizao para os no-terminais que geram expresses.

Se adicionarmos os smbolos em PRIMEIRO(A) ao conjunto de sincronizao


para o no terminal A, pode ser possvel retornar a anlise a partir de A, se um
smbolo em PRIMEIRO(A) figurar na entrada.

Se um no terminal puder gerar a cadeia vazia, ento a produo que deriva


pode ser usada como default. Agindo-se assim, pode-se postergar a deteco de
algum erro, mas no se pode fazer com que um erro seja perdido. Esse enfoque
reduz o nmero de no terminais que tenham de ser considerados durante a
recuperao de erros.

Se um terminal ao topo da pilha no puder ser reconhecido, uma idia simples


a de remov-lo, emitir uma mensagem informando da remoo e prosseguir a
anlise sinttica. Com efeito, este enfoque faz com que o conjunto de
sincronizao de um token consista em todos os demais tokens.

6 ANLISE SINTTICA BOTTOM-UP


A anlise sinttica bottom-up conhecida como anlise de empilhar e reduzir. A anlise
gramatical de empilhar e reduzir tenta construir uma rvore gramatical para uma cadeia
de entrada comeando pelas folhas (o fundo) e trabalhando rvore acima em direo
raiz (o topo). Podemos pensar neste processo como o de reduzir uma cadeia w ao
smbolo de partida de uma gramtica. A cada passo de reduo, uma subcadeia
particular, que reconhea o lado direito de uma produo, substituda pelo smbolo
esquerda daquela produo e, se a subcadeia tiver sido escolhida corretamente a cada
passo, uma derivao mais direita ter sido rastreada na ordem inversa.
Exemplo:
Considerando a gramtica
SaABe
AAbc | b
B d
A sentena abbcde pode ser reduzida a S pelos seguintes passos:
Aabbcde
aAbcde
aAde
aABe
S
Podemos esquadrinhar abbcde procurando por uma subcadeia que reconhea o lado
direito de alguma produo. As subcadeias b e d se qualificam. Vamos escolher o b mais
esquerda e substitu-lo por A, o lado esquerdo da produo Ab; obtemos dessa forma
a cadeia aAbcde. Agora as subcadeias Abc, b e d reconhecem o lado direito de alguma
produo. Apesar de b ser a subcadeia mais esquerda que reconhea o lado direito de

alguma produo, escolhemos substituir a subcadeia Abc por A, o lado esquerdo da


produo AAbc. Obtemos agora aAde. Com a substituio de d por B, o lado esquerdo
da produo Bd, obtemos aABe. Podemos agora substituir toda esta cadeia por S.
Conseqentemente, atravs de uma seqncia de quatro redues, estamos capacitados a
reduzir abbcde a S. Essas redues, de fato, rastreiam a seguinte derivao mais
direita, na ordem reversa:
S aABe aAde aAbcde abbcde
7 HANDLES
Informalmente, um handle uma subcadeia que reconhece o lado direito de uma
produo e cuja reduo ao no terminal do lado esquerdo da produo representa um
passo ao longo do percurso de uma derivao mais direita. Em muitos casos, a
subcadeia mais esquerda que reconhece o lado direito de uma produo A no
um handle, porque uma reduo pela produo A produz uma cadeia que no pode
ser reduzida ao smbolo de partida.
7.1 A Poda do Handle
Uma derivao mais esquerda na ordem inversa pode ser obtida podando-se os
handles. Ou seja, comeamos pela primeira cadeia de terminais w que desejamos
decompor. Se w for uma sentena da gramtica em questo, ento w=yn, onde yn a
ensima forma sentencial direita de alguma derivao mais direita ainda
desconhecida.
Para reconstruir esta derivao na ordem inversa, localizamos o handle n em yn e
substitumos n pelo lado direito de alguma produo An n de modo a obtermos a
ensima menos uma forma sentencial direita yn-1.
Repetimos, em seguida, esse processo. Isto , localizamos o handle n-1 em yn-1 e o
reduzimos de forma a obter a forma sentencial direita yn-2. Continuando esse processo,
produzimos uma forma sentencial direita consistindo somente no smbolo de partida S
e ento paramos e anunciamos o trmino com sucesso da anlise sinttica. O reverso da
seqncia de produes usadas nas redues uma derivao mais direita para a
cadeia de entrada.
8 IMPLEMENTAO DE PILHA DA ANLISE SINTTICA DE EMPILHAR E
REDUZIR
Existem dois problemas que precisam ser resolvidos se estivermos dispostos a analisar
sintaticamente atravs da poda de handles. O primeiro o de localizar a subcadeia a ser
reduzida numa forma sentencial direita e o segundo o de determinar que produo
escolher no caso de existir mais de uma produo com aquela subcadeia no lado
direito.
Uma forma conveniente de implementar um analisador sinttico de empilhar e reduzir
usar uma pilha para guardar os smbolos gramaticais e um buffer de entrada para a
cadeia w a ser decomposta. Usamos $ para marcar o fundo da pilha e tambm o final

direita da entrada. Inicialmente, a pilha est vazia e a cadeia w est entrada como
segue
Pilha
$

Entrada
w$

O analisador sinttico opera empilhando zero ou mais smbolos (na pilha) at que um
handle surja no topo da pilha. Reduz, ento, para o lado esquerdo da produo
apropriada. Repete este ciclo at que tenha detectado um erro ou que a pilha contenha o
smbolo de partida e a entrada esteja vazia:
Pilha
$S

Entrada
$

Aps entrar nesta configurao, pra e anuncia o trmino com sucesso da anlise
sinttica.
8.1 Prefixos Viveis
Os prefixos de uma forma sentencial direita que podem figurar na pilha deu m
analisador sinttico de empilhar e reduzir so chamados de prefixos viveis. Uma
definio equivalente de um prefixo vivel a de ser um prefixo de uma forma
sentencial direita, o qual no se estende para alm do limite direita do handle mais
direita, daquela forma sentencial. Por esta definio sempre possvel adicionar
smbolos terminais ao final de um prefixo vivel de modo a obter uma forma sentencial
direita. Por conseguinte, no h aparentemente erro na medida em que a poro da
entrada enxergada at um dado ponto possa ser reduzida a um prefixo vivel.
9 ANLISE SINTTICA DE PRECEDNCIA DE OPERADORES
A mais ampla classe de gramticas, para a qual os analisadores sintticos de empilhar e
reduzir podem ser construdos com sucesso so as gramticas LR. Entretanto, para uma
pequena, porm importante classe de gramticas, podemos facilmente construir
manualmente eficientes analisadores sintticos de empilhar e reduzir. Essas gramticas
possuem a propriedade (dentre outras exigncias essenciais) de que nenhum lado direito
de produo seja , ou tenha dois no terminais adjacentes. Uma gramtica com a
ltima propriedade chamada de uma gramtica de operadores.
Exemplo:
A seguinte gramtica para expresses
E EAE | (E) | -E |id
A+|-|*|/|
No uma gramtica de operadores porque o lado direito EAE possui dois (de fato trs)
no terminais consecutivos. Entretanto, se substituirmos A por cada uma de suas
alternativas, obtemos a seguinte gramtica de operadores:
E E + E | E E | E * E| E / E | E E | (E) | -E | id

Descrevemos agora uma tcnica de anlise sinttica fcil de implementar chamada de


anlise sinttica de precedncia de operadores. Historicamente, esta tcnica foi
primeiramente descrita como uma manipulao sobre tokens sem qualquer referncia a
uma gramtica subjacente. De fato, uma vez que tenhamos terminado de construir um
analisador sinttico de precedncia de operadores a partir de uma gramtica, podemos
ignorar esta ltima usando os no terminais na pilha apenas como marcadores de lugar
para os atributos associados aos no terminais.
Como uma tcnica geral de anlise sinttica, a de precedncia de operadores possui uma
srie de desvantagens. Por exemplo, difcil tratar os tokens como o sinal de menos,
que possui duas diferentes precedncias (dependendo de ser unrio ou binrio).
Sobretudo, uma vez que o relacionamento entre a gramtica para a linguagem analisada
e o analisador sinttico de precedncia de operadores tnue, no se pode estar sempre
certo de que o analisador aceite exatamente a linguagem desejada. Finalmente, somente
uma pequena classe de gramticas pode ser decomposta usando as tcnicas de
precedncia de operadores.
Apesar de tudo, em virtude de sua simplicidade, numerosos compiladores usando as
tcnicas de anlise sinttica de precedncia de operadores tm sido construdos com
sucesso. Freqentemente, esses analisadores so de descendncia recursiva.
Analisadores sintticos de precedncia de operadores tem sido construdos at mesmo
para linguagens inteiras.
10 ANALISADORES SINTTICOS LR
Uma tcnica eficiente de anlise sinttica bottom-up, que pode ser usada para decompor
uma ampla classe de gramticas livres de contexto chamada anlise sinttica LR(k);
oL significa varredura da entrada da esquerda para a direita (left-to-right), o R,
construir uma derivao mais direita ao contrrio (right)most derivation) e o k, o
nmero de smbolos de entrada de lookahead que so usados ao se tomar decises na
anlise sinttica. Quando (k) for omitido, k assumido ser 1. A tcnica de anlise
sinttica LR atrativa por uma srie de razes.

Analisadores sintticos LR podem ser elaborados para reconhecer virtualmente


todas as construes de linguagens de programao para as quais as gramticas
livres de contexto podem ser escritas.

O mtodo de decomposio LR o mais geral dentre os mtodos sem retrocesso


de empilhar e reduzir conhecidos e ainda pode ser implementado to
eficientemente quanto os demais mtodos de empilhar e reduzir, .

A classe de gramticas que podem ser decompostas usando-se os mtodos LR


um superconjunto prprio da classe de gramticas que podem ser decompostas
usando-se analisadores sintticos preditivos.

Um analisador sinttico LR pode detectar um erro sinttico to cedo quanto


possvel numa varredura da entrada da esquerda para a direita.

A principal desvantagem deste mtodo est em ser muito trabalhoso construir um


analisador sinttico LR manualmente para uma gramtica tpica de linguagem de
programao. Necessita-se em geral de uma ferramenta especializada um gerador de
analisadores LR. Felizmente, muitos de tais geradores esto disponveis. Com um tal
gerador, podemos escrever uma gramtica livre de contexto e us-lo para produzir
automaticamente um analisador sinttico para a mesma. Se a gramtica contiver
ambigidades ou outras construes que sejam difceis de decompor, numa varredura da
entrada da esquerda para a direita, o gerador de analisadores pode localiz-las e
informar ao projetista do compilador de suas ocorrncias.
10.1 O Algoritmo de Anlise Sinttica LR
Consiste em uma entrada, uma sada, uma pilha, um programa diretor e uma tabela
sinttica que possui duas partes (ao e desvio). O programa diretor o mesmo para
todos os trs tipos de analisadores LR; somente a tabela sinttica muda de um analisador
para o outro. O programa de anlise sinttica l os caracteres provenientes de um buffer
de entrada, um de cada vez. Usa uma pilha para armazenar as cadeias sob a forma
s0X1s1X2s2...Xmsm onde sm est ao topo. Cada Xi um smbolo gramatical e cada si, um
smbolo chamado de estado. Cada estado sumariza a informao contida na pilha abaixo
do mesmo e a combinao do smbolo do estado no topo da pilha e o smbolo corrente
de entrada usada para indexar a tabela sinttica e determinar a deciso de empilhar ou
reduzir durante a anlise. Numa implementao, os smbolos gramaticais no precisam
figurar na pilha; entretanto, iremos sempre inclu-los em nossas discusses, a fim de
auxiliar a explicao do comportamento de uma analisador sinttico LR.
A tabela sinttica consiste em duas partes, uma uno de aes sintticas, ao, e uma
funo de desvio, desvio. O programa diretor do analisador LR se comporta como se
segue. Determina sm, o estado correntemente no topo da pilha, e ai, o smbolo corrente
de entrada. Consulta, ento ao[sm,ai], a entrada da tabela de aes sintticas para o
estada sm e a entrada ai, que pode ter um dos seguintes valores:
1. empilhar s, onde s um estado,
2. reduzir atravs da produo gramatical A ,
3. aceitar, e
4. erro.
A funo desvio toma um estado e um smbolo gramatical como argumentos e produz
um estado como sada. Veremos que a funo desvio de uma tabela sinttica, construda
a partir de uma gramtica G, usando os mtodos SLR, LR cannico ou LALR, a
funo de transio de um autmato finito determinstico que reconhece os prefixos
viveis de G. Relembremos que os prefixos viveis de G so aqueles das formas
sentenciais direita que podem aparecer na pilha de um analisador sinttico de empilhar
e reduzir, porque os mesmos no se estendem para depois do handle mais direita. O
estado inicial deste AFD o estado inicialmente colocado no topo da pilha do analisador
sinttico LR.
Uma configurao de um analisador sinttico LR um par cujo primeiro componente
o contedo da pilha e cujo segundo componente a entrada ainda no consumida:
(s0X1S1x2S2...Xmsm,aiai+1...an$)

Esta configurao representa a forma sentencial direita


X1X2...Xmaiai+1...an
Essencialmente da mesma maneira que um analisador de empilhar e reduzir faria:
somente a presena dos estados na pilha inovadora.
O prprio movimento do analisador determinado pela leitura de ai, o smbolo corrente
da entrada e de sm, o estado no topo d pilha, e pela consulta entrada da tabela de aes,
ao[sm,a i]. As configuraes resultantes aps cada um dos quatro tipos de movimentos
so como se segue:
1. Se ao [sm,a i]=empilhar s, o analisador executa um movimento e empilhar,
entrando na configurao
(s0X1s1X 2s2...Xmsm,ais,ai+1...an$)
Aqui, o analisador sinttico empilhou tanto o smbolo corrente de entrada como o
prximo estado s, que dado por ao[sm,a i]; ai+1 se torna o smbolo corrente da
entrada.
2. Se ao[sm,a i]=reduzir A , o analisador executa um movimento de reduo,
entrando na configurao
(s0X1s1X 2s2...Xm-rsm-r,As,ai ai+1...an$)
onde s=desvio[sm-r,A] e r o comprimento de , o lado direito da produo. Aqui o
analisador sinttico remove primeiro 2r smbolos para fora da pilha (r smbolos de
estados e r smbolos gramaticais), expondo o estado sm-r. Em seguida, empilha tanto A, o
lado esquerdo da produo, quanto s, a entrada para desvio[sm-r,A]. Para os analisadores
sintticos LR que iremos construir, Xm-r+1... Xm, a seqncia de smbolos gramaticais
removidos da pilha ir sempre reconhecer , o lado direito da produo redutora.
A sada de um analisador sinttico LR gerada aps um movimento de reduo, atravs
da execuo de uma ao semntica associada produo redutora. Para o momento,
assumiremos que a sada consista somente na impresso da produo redutora.
3. Se ao[sm,a i]=aceitar, a anlise sinttica est completa.
4. Se ao[sm,a i]=erro, o analisador sinttico descobriu um erro e chama um
procedimento de recuperao de erros.
Autoria: Elisson Oliveira Lima

Вам также может понравиться