Sei sulla pagina 1di 6

Problema 3 : Construo de um Analisador Sinttico

Marcus Vinicius Araujo Martins Universidade Estadual de Feira de Santana (UEFS) Feira de Santana BA Brasil
{viniciusfsa@gmail.com}

Resumo: Este relatrio visa descrever um projeto de analisador sinttico para um compilador baseado na produo de uma gramtica desenvolvida em etapa anterior, usando a notao EBNF. Aqui sero tratadas as principais tcnicas de anlise sinttica, destacando vantagens e desvantagens de cada uma, bem como tambm salientado o processo de tratamento de erros sintticos e breve descrio do projeto prtico.

1 - Fundamentao terica
1.1 - O analisador sinttico
O analisador sinttico tem por funo receber uma cadeia ou conjunto de tokens proveniente do analisador lxico e verificar se a mesma pode ser gerada pela gramtica da linguagem fonte [1]. Neste sentido, a gramtica quem d as diretrizes para construo de um analisador capaz de reconhecer expresses que so aceitas em suas regras. O analisador sinttico tem o papel ainda de se recuperar dos erros que ocorram, no intuito de continuar a processar toda entrada de tokens, e no apenas at encontrar o primeiro erro. Quando falado aqui em erros sintticos, no mais se est preocupado com erros de escrita ou tipos mal formados. Isso foi um papel do analisador lxico que, no poder de suas atribuies, pde localizar e classificar a ocorrncia de cada token dentro do cdigo, bem como relatar possveis erros lxicos presentes no corpo de cdigo do programador. Passada essa fase, subtende-se que a tabela de smbolos mantm um conjunto de valores que precisam agora ser vistos como unidades e, desta forma, seu contedo, se formado por dgitos ou letras, no mais o objetivo. Aqui, vistos de forma atmica, os tokens precisam passar por uma etapa onde sua ordem verificada, se um identificador vem imediatamente antes de um operador ou se um tipo primitivo antecede um identificador, numa declarao, por exemplo, no poder de construo definido na gramtica. Nesse contexto, a construo do analisador sinttico a ser apresentada se baseia nas construes definidas na gramtica, em etapa anterior, baseando-se na notao EBNF. As possveis estratgias da anlise so esplanadas no tpico 1.2, bem como tambm apresentado os critrios de escolha de determinada estratgia, a partir de decises tericas e de projeto.

1.2 - Estratgias para anlise sinttica


Existem trs tipos gerais de analisadores sintticos[1]: A universal, que pode analisar qualquer gramtica. Ex: o algoritmo de Coke-Younger-Kasami e o algoritmo de Earley. A descendente (top-down), que constroi as rvores de derivao de cima (raiz) para baixo (folhas), e a ascendente (bottom-up), que constroi as rvores de derivao de baixo (folhas) para cima (raiz). Todavia, o primeiro mtodo citado se torna bastante ineficiente, principalmente por sua complexidade em abranger todas as gramticas. Os mtodos de anlise sinttica mais eficientes so o top-down e o bottom-up que, embora trabalhem com apenas um grupo de gramticas, so suficientemente expressivos para descrever a maioria das construes sintticas das linguagens de programao[1]. Como apresentado em etapas anteriores, existem diversos tipos de gramtica, como por exemplo, gramtica fatorada esquerda, recursiva esquerda, simplificada, etc. Alm disso, existem outras classificaes, de acordo com os nveis na hierarquia de Chomsky, tambm j apresentada. Dessas caractersticas, foi desenvolvida uma gramtica fatorada esquerda, sem recurso a esquerda e sem ambigidade. Assim, para o analisador implementado, se fez necessrio utilizar a estratgia top-down, uma vez que reconhecedores desse tipo podem processar apenas gramticas que no apresentem recursividade a esquerda[2]. Para a estratgia bottom-up, o requisito seria o inverso, que a gramtica no apresentasse recursividade a direita. Assim, os mtodos de reconhecimento podem exigir transformaes na gramtica, caso ela j no tenha sido construda nesse sentido. As transformaes podem incluir: eliminao de produes vazias, retirada de recurso esquerda e fatorao de produes. A gramtica sobre a qual o analisador foi construdo obedece a esses requisitos. Caso existisse uma recurso a esquerda, por exemplo, para um analisador sinttico top-down, o mesmo entraria em um loop infinito, uma vez que o mesmo usa anlise do tipo LR (Left-Right), lendo a entrada de texto da esquerda para direita. Alm disso, produes no fatoradas a esquerda deixariam a gramtica ambgua, uma vez que o analisador no saberia para que produo ir quando encontrasse duas derivaes com uma mesma cadeia inicial.

1.3- A estratgia top-down


A estratgia top-down pode ser implementada de trs formas [2]: A) recursivo com retrocesso B) recursivo preditivo C) tabular preditivo A forma recursiva com retrocesso faz a expanso da arvore de derivao a partir da raiz, expandindo sempre o no terminal mais a esquerda. Para no terminais que possuem mais de uma produo, todas as alternativas vo ser tentadas at que se obtenha sucesso. Essa estratgia denotada por muitos ciclos iterativos na busca pela derivao correta. No pior caso, a varredura pode chegar a averiguar todas as possibilidades, o que pode deixar o tempo demasiado grande, dependendo da cadeia a ser analisada e das produes de cada no terminal. O retrocesso desse mtodo existe no principio de que as derivaes podem voltar a determinada produo que j fora definida, por ter encontrado alguma falha posteriormente. O retrocesso faz com que uma

produo antes definida seja trocada, por uma segunda produo, o que torna o mtodo bastante exaustivo. O mtodo recursivo preditivo desenvolvido sem retrocesso. Nesse mtodo, o smbolo sob o cabeote de leitura determina exatamente qual produo deve ser aplicada na expanso de cada no terminal. Esse foi o mtodo implementado para o analisador em questo, aproveitando que ele exige que a gramtica no tenha recursividade esquerda, que a gramtica seja fatorada esquerda e que, para os noterminais com mais de uma regra de produo, os primeiros terminais derivveis sejam capazes de identificar, univocamente,a produo que deve ser aplicada para cada momento da anlise sinttica [2]. Esse tipo de anlise se baseia no princpio de que cada no terminal identificado como uma funo ou procedimento. Funciona como um grafo onde toda regra da gramtica mapeada[3], Toda ocorrncia de um terminal x corresponde ao seu reconhecimento na cadeia de entrada e a leitura do prximo smbolo dessa cadeia. O exemplo de gramtica da figura 1 em correspondncia com seu procedimento implementado na figura 2 descrevem esse princpio.
Figura 1 Exemplo de gramtica

Figura 2 Procedimento de <F>

O cdigo descrito na figura 2 corresponde a implementao do procedimento apresentado na figura 1. Caso o no-terminal <F> seja invocado, esse procedimento executado. Na produo especfica, F deriva a ou b ou o no terminal <E> entre parnteses (<E>). A partir disso ele analisa primeiramente se (. Se sim, o procedimento chama prximo token e passa a responsabilidade para o procedimento <E>, no apresentada aqui sua estrutura de procedimento. Genericamente, caso o contedo depois do ( seja realmente pertencente a produo <E>, ou seja, caso <E> retorne um valor verdadeiro, <F> continuar seu processamento. Assim, ele analisa se o prximo token ). Se sim, ele chama o prximo token, deixando o ponteiro ou cabea do cabeote pronto para prxima anlise, e finaliza o procedimento. Se inicialmente o primeiro token no fosse (, ento o procedimento verificaria se era a ou se era b. Se no fosse nenhum desses, lanado um erro ou um valor falso, indicando que aquela produo no corresponde. Sendo a ou b, o procedimento finalizado, sem ocorrncia de erros. A terceira estratgia top-down a tabular preditiva. Essa forma de anlise chegou ser verificada, mas no foi implementada, devido principalmente a sua complexidade. Esse mtodo utiliza uma tabela que faz relao entre os smbolos no

terminais e os smbolos de entrada. Para construo da tabela, se faz necessrio saber dois conjuntos, denominados Primeiro e Seguinte. O conjunto Primeiro para um simbolo T, por exemplo, o conjunto de smbolos terminais que iniciam as cadeias derivadas de T, onde T qualquer cadeia de smbolos da gramtica[2]. O Seguinte de T o conjunto de terminais a que podem aparecer imediatamente direita de T em alguma forma sentencial[2]. Embora o mtodo tabular no tenha sido implementado, a construo desses dois conjuntos foi necessria no tratamento de erros, descrita no tpico 1.4.

1.4 - Tratamento de erros A Questo do Primeiro e Seguinte


Utilizando a anlise sinttica top-down preditiva recursiva, se faz necessrio saber o que o analisador deve fazer quando encontrar um erro sinttico. Para uma declarao dentro do cdigo, o que fazer quando o analisador espera um identificador aps o token que define um tipo. Declaraes precisam de um tipo e depois um identificador. Erros como esse se classificam como erros sintticos, pois compreendem a quebra de regras de produo, no caso especfico, a regra de declarao. Para esse problema, a soluo foi manter, dentro de cada produo, um conjunto de possveis tokens seguintes, a serem buscados caso o procedimento lance um erro de sintaxe. esse conjunto, o conjunto Seguinte, que ir nortear as possibilidades do analisador em continuar seu processo sinttico. Sem esse tratamento, a anlise terminaria ao encontrar o primeiro erro sinttico, o que contradiz o contexto de anlise sinttica de um compilador. Assim, o conjunto Primeiro e Seguinte precisa ser identificado para cada mtodo o procedimento. Exemplos de levantamentos desses conjuntos para o analisador implementado so descritos no tpico 2.2.

2 - Descrio do analisador desenvolvido


2.1 - A escolha da estratgia
A escolha da estratgia top-down tem motivo exclusivo as condies de andamento do projeto. Uma vez que se mantinha em mos uma gramtica sem recurso esquerda e fatorada esquerda, no caberia utilizar o mtodo bottom-up, pois teria que ser feita toda uma inverso da gramtica, o que levaria tempo. Dentro da anlise top-down, foi escolhida a recursiva preditiva, primeiramente mediante sua simplicidade em relao aos outros dois mtodos aqui citados. Alm disso, como a anlise se baseia na construo de procedimentos, sua aplicao foi til uma vez que o projeto desenvolve-se em grupo e as tarefas puderam ser mais bem divididas entre os membros, na atribuio de mtodos para cada membro.

2.2 - Decises de projeto e estrutura do cdigo


Apenas duas classes foram criadas nessa etapa de desenvolvimento. Uma vez que j existia o projeto de anlise lxica, coube apenas adaptar o projeto para agora realizar tambm a anlise sinttica. As classes construdas foram: ParsingControl.java e ProductionRules.java. A classe ProductionRules.java rene todos os procedimenos, divididos em mtodos, para cada no terminal definido na gramtica EBNF. A classe ParsingControl.java mantm um controle sobre o analisador sinttico e o analisador

lxico, controlando o processo de criao da tabela de tokens, pelo analisador lxico, e a repassando para o analisador sinttico. Os mtodos da classe ProductionRules.java so quase que exclusivamente procedimentos dos smbolos no terminais, como descrito no tpico 1.3. Todavia, ela possui um mtodo start() que faz com que uma tabela de smbolos passada por uma instancia da classe controladora ParsingControl.java, seja transferida numa ordem inversa para uma pilha de tokens, no intuito de pegar os tokens na forma correta. Fazendo isso, os tokens so buscados sempre nessa pilha. Aps isso, o primeiro token retirado da pilha e o primeiro procedimento chamado, o procedimento programa(). A figura 3 mostra o incio da execuo.

Figura 3 Preparao do primeiro token e chamada do primeiro procedimento

O procedimento programa() relaciona a estrutura geral de como deve ser o cdigo. Por deciso de projeto, ele deve comear com zero, uma ou mais declaraes de biblioteca, seguido de zero ou mais declaraes globais e seguido obrigatoriamente por declaraes de variveis e funes, na obrigatoriedade de existir pelo menos o mtodo main() no cdigo do programador. Isso corresponde na prtica que o cdigo do programador deve manter uma estrutura iniciada por <Declarao de Bibliotecas>, depois por <TypeDef> ou <Declarao de Struct> ou <Declarao Const>, depois por <Varveis Globais>* e <Declarao de Funes> e depois por <Principal>, que o mtodo main(). A implementao dos procedimentos se baseou nas seguintes tcnicas, analisando a produo de cada no terminal na gramtica. a) quando houver uma concatenao if's encadeados b) quando houver uma escolha ou else if ... pra cada derivao c) quando houver uma repetio do while. d) quando houver uma repetio do do-while com flag. tem de comparar na seqncia com '|' na derivao, utiliza-se um if tipo { } (0 ou mais) utiliza-se o tipo { }+ (1 ou mais) utiliza-se o

A forma de tratamento de erros se baseou na construo dos conjuntos Primeiro e Seguinte, para cada procedimento, conforme descrito na sesso 1.4. Para o procedimento declaracaoConst() por exemplo, seus seguintes foram levantados e definidos no escopo do prprio mtodo, conforme figura 4.

Figura 4 Exemplo de conjunto Seguinte

Ocorrendo algum erro dentro do procedimento declaracaoConst(), como por exemplo a ausncia do smbolo = depois de um identificador, lanado um erro sinttico com a chamada ao procedimento matchError(smbolo,seguinte). Os parmetros dessa chamada correspondem respectivamente ao smbolo que era esperado e o conjunto de seguintes do procedimento. O mtodo matchError() quando recebe

esses parmetros, adiciona numa lista de erros esse novo erro e faz um loop para saber se o prximo token equivale a algum possvel seguinte a construo declaraConst(). O lanamento de erros com a chamada a matchError() no pode ser feito em qualquer procedimento que falhe; deve ser chamada apenas quando a falha for numa anlise de em um smbolo terminal. Em outros casos, apenas um valor falso retornado, fazendo com que o no terminal que o invocou busque outra produo possvel para ele. A construo do analisador no se deu obedecendo exatamente as construes da gramtica, visto que na mesma ainda pde ser encontrada algumas ambigidades nas produes. Problemas como esse foram resolvidos diretamente dentro do cdigo, ao invs de fazer alteraes na gramtica primeiramente. Um exemplo disso o caso de aceitao de ifs sem elses. Na verdade isso no o problema. O problema que quando aconteciam de se ter dois ifs e um else, estava ambguo, na gramtica, a qual IF o else pertencia. A soluo para isso foi a obrigatoriedade de todo IF acompanhar um else.

3 - Referncias
[1] AHO, Alfred V.; SETHI, Ravi; ULLMAN, Jeffrey D. Compiladores, principios, tecnicas
e ferramentas. Rio de Janeiro: Guanabara Koogan, c1995.

[2] PRICE, Ana Maria de Alencar; TOSCANI, Simao Sirineo. Implementacao de linguagens
de programacao : compiladores. 3. ed. Porto Alegre: Sagra Luzatto, 2005.

[3] Compiladores - Profa. Helena Caseli, disponvel em: http://www2.dc.ufscar.br/~helenacaseli/comp/trabalhos/Trabalho2/Trabalho2.pdf ltimo acesso: 23/12/09

Potrebbero piacerti anche