Escolar Documentos
Profissional Documentos
Cultura Documentos
Linguagens e Programao
Ana Madureira
Engenharia Informtica Ano Lectivo: 2011/2012
Anlise Top-Down
Neste tipo de anlise sintctica, a rvore de parse construda partindo da raiz, at se chegar sequncia de tokens do texto da entrada:
Gera-se sempre uma derivao mais esquerda Os ns da rvore de parse construda so visitados em pr-ordem (descida recursiva) ou prximo;
Estes tipos de anlise sintctica requerem geralmente uma classe de gramticas livres de contexto, denominadas LL(k) (na prtica LL(1))
O mtodo da descida recursiva, sendo geralmente escrito " mo", pode incorporar tcnicas ad-hoc e portanto relaxar um pouco as exigncias das gramticas LL(k).
26-03-2012
Exp Exp Op Exp (Exp) Op Exp (Exp Op Exp) Op Exp (num Op Exp) Op Exp (num - Exp) Op Exp (num - num) Op Exp (num - num) * Exp (num - num) * num
Analisadores Sintcticos Descendentes Constri a derivao mais esquerda da sentena de entrada a partir do smbolo inicial da gramtica. Podem ser implementados:
com retrocesso (analisadores nodeterminsticos)
Fora bruta Maior conjunto de gramticas (ambguas) Maior tempo para anlise Dificuldade para recuperao de erros Dificuldade para anlise semntica e gerao de cdigo
26-03-2012
26-03-2012
3.
1. Ordenar os no-terminais numa ordem arbitrria: A1, A2, , Ai, , An 2. for i de 1 a n do for j de 1 a i-1 do substituir todas as produes da forma Ai Aj pelas produes Ai 1 | 2 | | k , onde Aj 1 | 2 | | k so todas as produes decorrentes de Aj eliminar, se for caso disso, a recursividade esquerda imediata de Ai
26-03-2012
Quais so os smblos terminais seguidores (Followers(A)): ou seja, se A *A, que terminais podem aparecer como primeiro smbolo de
Starters(X) = { t | X * t }
Followers(X) - conjunto de terminais que podem aparecer a seguir a um X em qualquer forma sentencial - Followers(X) = { t | S * Xt }
26-03-2012
Descida recursiva
Consiste na construo de um conjunto de procedimentos (normalmente recursivos), um para cada smbolo no terminal da gramtica em questo. A cada no-terminal est associada uma rotina, sendo a sua anlise efectuada pela chamada a essa rotina. Considere-se a seguinte gramtica para uma linguagem muito simples: S if E then S else S S begin S L S print E L end L;SL E num = num
A esta gramtica deve ser acrescentado um novo smbolo inicial P e a seguinte regra: PS$ ($ designa o fim do texto EOF) Ser necessrio adicionar uma rotina relativa a esta nova produo
Descida recursiva
enum token {EOF, IF, THEN, ELSE, BEGIN, END,PRINT, SEMI, NUM, EQ}; enum token getToken(void); enum token tok; void advance(void) { tok = getToken(); } void eat(enum token t) { if (tok == t) advance(); else error(); } void P(void) { S(); if (tok == EOF) accept(); else error(); } void S(void) { switch (tok) { case IF: eat(IF); E(); eat(THEN); S(); eat(ELSE); S(); break; case BEGIN: eat(BEGIN); S(); L(); break; case PRINT: eat(PRINT); E(); break; default: error(); }}
void L(void) { switch(tok) { case END: eat(END); break; case SEMI: eat(SEMI); S(); L(); break; default: error(); }} void E(void) { eat(NUM); eat(EQ); eat(NUM); } void main(void) { advance(); P(); }
26-03-2012
Descida recursiva
Desvantagens
no geral, ou seja, os procedimentos so especficos para cada gramtica tempo de anlise maior necessidade de uma linguagem que permita recursividade para sua implementao Dificuldade de validao
Vantagens
simplicidade de implementao facilidade para inserir as diferentes funes do processo de compilao nomeadamente a possibilidade de incluso de rotinas de anlise semntica se existem diferentes alternativas para reescrever o mesmo no terminal, eficincia no ser necessrio tentar diferentes rotinas para o reconhecimento de uma frase (backtraking)
As expanses para E e T tornam-se evidentes: void E(void) { T(); while (tok==PLUS || tok==MINUS) { advance(); T(); } } void T(void) { F(); while (tok==MUL || tok==DIV) { advance(); F(); } }
No entanto existem mtodos para eliminar a recursividade esquerda de uma gramtica e para distinguir entre regras, quando um no-terminal possui vrias produes que comeam por no-terminais distintos, ou smbolos iguais. So eles: Recursividade esquerda: (A + A) Fazer uma eliminao da recursividade esquerda, transformando a gramtica Produes que comeam com o mesmo smbolo: ( A X | X) Fazer uma factorizao esquerda Produes que comeam por no-terminais distintos: (A B | C) Dispor do conjunto de terminais que pode comear qualquer derivao de B e C starters(B) e starters(C) Produes vazias: (A ) Dispor do conjunto de terminais que pode legalmente aparecer a seguir a A em qualquer forma sentencial - followers(A)
26-03-2012
Utilizao dos starters e dos followers para implementao de um analisador de descida recursiva (exemplo)
Verificou-se anteriormente que a gramtica para expresses no era adequada para a descida recursiva (era recursiva esquerda). Eliminando a recursividade esquerda daquela gramtica, obtm-se: Exemplo: Sejam as regras para expresso do exemplo anterior: EE+T EE-T ET Podem ser transformadas no conjunto: E T E' E' + T E' E' - T E' E'
S E $ ET E' T F T' E' + T E' T' * F T' F id E' - T E' T' / F T' F num E' T' F ( E ) que j seria adequada para implementar uma descida recursiva.
Utilizao dos starters e dos followers para implementao de um analisador de descida recursiva (exemplo)
Verificou-se anteriormente que a gramtica para expresses no era adequada para a descida recursiva (era recursiva esquerda). Eliminando a recursividade esquerda daquela gramtica, obtm-se:
F id F num F ( E )
que j seria adequada para implementar uma descida recursiva. No entanto se pretendermos validar as produes vazias de E' e T', necessitamos dos seus followers: followers(E') = { ), $ } followers(T') = { ), +, -, $ }
26-03-2012
26-03-2012
Tabela de Parse
Em cada entrada da tabela M existe uma nica produo viabilizando a anlise determinstica da sentena de entrada. Para isso necessrio que a gramtica:
No possua recursividade esquerda Estar factorizada ( determinstica) Para todo A | A * e, First(A) Follow(A) =
Tabela de parsing
As GLC que satisfazem estas condies so denominadas GLC LL(K)
podem ser analisadas deterministicamente da esquerda para a direita (Left-to-right) o analisador construir uma derivao mais esquerda (Leftmost derivation) sendo necessrio a cada passo o conhecimento de K smbolos de lookahead (smbolos de entrada que podem ser vistos para que uma aco seja determinada).
Somente GLC LL(K) podem ser analisadas pelos analisadores preditivos (os analisadores preditivos so tambm denominados analisadores LL(K)
na prtica usa-se K = 1, obtendo-se desta forma analisadores LL(1) para GLC LL(1).
10
26-03-2012
11
26-03-2012
push($); push(S); advance(); while (top() != $ && tok != $) do if ((a = top()) == terminal && tok == a) then pop(); advance(); else if ((X = top()) == non-terminal && M[X, tok] == X Y1 ... Yn) then pop(); for (k = n; k > 0; k--) push(Yk); output(" X Y1 ... Yn"); else error(); if (top() == $ && tok == $) then accept(); else error();
Exemplo
Considere-se a gramtica ET E' E' + TE' E' T F T' T' * FT' T' Anlise da entrada id + id * id $ e regras aplicadas para a aceitao Desta: F id F ( E )
12
26-03-2012
Exemplo
Considere-se a seguinte gramtica para instrues:
S If-st | other If-st if ( Exp ) S El El else S | Exp 0 | 1
Contruir a tabela de parse para esta gramtica. Comeando por determinar nullable, starters e followers chegaramos aos seguintes resultados:
Repare-se na dupla entrada em M[El, else] que faz com que esta gramtica no seja LL(1). Deve-se conhecida ambiguidade da instruo if com else opcional. No entanto usando a habitual regra de elimio de ambiguidade (emparelhar cada else com o ltimo if) a produo El pode ser eliminada dessa entrada, tornando a tabela perfeitamente definida e correcta para aquela regra.
Condio para a gramtica ser LL(1): Para cada entrada M[A, a] s pode haver um valor; caso contrrio, diz-se que existe um conflito e a gramtica no LL(1)
13
26-03-2012
14
26-03-2012
Tratamento de erros
O tratamento de erros durante a anlise sintctica deve obedecer a certos princpios gerais: O erro deve ser declarado o mais cedo possvel, de modo a que possa ser localizado com preciso na sequncia de tokens da entrada Depois de detectado um erro o parser deve continuar, num ponto mais frente, de modo a detectar o maior nmero possvel de erros numa nica passagem O parser deve tentar evitar o problema da "cascata de erros", em que um nico erro gera uma srie de mensagens O parser deve evitar um ciclo infinito, reatando a anlise sem consumir nenhum token, e gerando sucessivamente as mesmas mensagens de erros A forma usual de tratamento de erros em parsers top-down tentar sincronizar a anlise a partir de um prximo token, descartando alguns se for necessrio, quando se detecta um erro sintctico Estes tokens de sincronizao, devero ser criteriosamente escolhidos para cada classe gramatical e so geralmente tokens dos conjuntos followersers( ) e starters( ) das classes gramaticais mais significativas da linguagem
15
26-03-2012
Escolhe-se esta alternativa nos casos no contemplados na alternativa anterior Vo-se descartando tokens da entrada at se encontrar um pertencente a Starters(A) followers(A)
Colocar (push) um no-terminal na stack
Se em virtude da primeira aco a stack ficar vazia, normalmente faz-se o push do smbolo inicial da gramtica Estas aces so codificadas na tabela de parse
Cada regra representada por uma lista ligada em que os elementos so do tipo TABGRAFO As listas ligadas referentes a regras alternativas do mesmo no-terminal esto interligadas atravs do apontador ALT
16
26-03-2012
2 3
b S M
4
8 10
5 7
d e
Quando se efectuam chamadas a outros smbolos no-terminais, necessrio usar uma stack para suporte da anlise: depois da chamada necessrio voltar ao ponto de onde foi efectuada a chamada. Exemplo: Frase aabc S aSc a ab c 3 Stack
S M
d a b c d e f
S ab | aSc | dM | e M { fS }*
17