• Nenhum resultado encontrado

An´alise das Express˜oes Regulares dos Tokens

3.9 Transforma¸c˜oes de M´odulos

4.1.3 An´alise das Express˜oes Regulares dos Tokens

Ap´os o tratamento das macros, a an´alise dos componentes l´exicos da defini¸c˜ao passa a operar sobre as defini¸c˜oes dos tokens da linguagem. Geralmente, quando mais de uma express˜ao regular for capaz de denotar um mesmo string de entrada, os analisadores l´exicos resolvem esse conflito optando pela express˜ao regular capaz de denotar o maior string. Caso mais de uma express˜ao reconhe¸ca strings de mesmo tamanho, o analisa- dor l´exico prioriza a express˜ao regular que aparece antes no arquivo de defini¸c˜oes de express˜oes regulares.

Assim, as defini¸c˜oes l´exicas presentes nos v´arios m´odulos devem ser processadas para definir a ordem das express˜oes no arquivo fonte gerado para o Alex, Lex.x. Para a defini¸c˜ao da ordem, cada express˜ao regular presente em um dos v´arios m´odulos deve ser comparada `as demais da seguinte forma: sejam R1 e R2 express˜oes regulares, e L(R1)

e L(R2), as respectivas linguagens denotadas. Para cada par de express˜oes (R1, R2)

pertencentes `a defini¸c˜ao, verifica-se se L(R1) est´a contida em L(R2). Se isso ocorrer,

a express˜ao R1 deve aparecer antes de R2 na defini¸c˜ao do analisador l´exico gerado.

Na Figura 4.5 as express˜oes regulares que definem as palavras-chave “if”, “then” e “else” est˜ao contidas na express˜ao regular letter( letter | digit )*, que define o

token seqld, e por isso aparecem antes dessa no arquivo Lex.x gerado.

O algoritmo para verificar se L(R1) est´a contida em L(R2) ´e realizado pela opera¸c˜ao

de diferen¸ca dos autˆomatos gerados por R1 e R2. A linguagem L(R1) est´a contida

em L(R2) se e somente se L(R1) − L(R2) = ⊘. Esta opera¸c˜ao ´e feita utilizando-se

autˆomatos finitos determin´ısticos (AFD) que reconhecem L(R1) e L(R2). Na pr´atica a

diferen¸ca entre as linguagens ´e obtida como mostrado no procedimento contained4

,

4

Os pseudo-c´odigos apresentados neste texto utilizam o pacote para latex clrscode [Cormen, 2003], em que o s´ımbolo ⊲ indica coment´ario de linha.

4.1. Compilac¸˜ao da Especificac¸˜ao L´exica 71 onde M1 e M2 s˜ao AFD’s gerados a partir de R1 e R2 [Sipser, 1996]. A opera¸c˜ao

de complemento de linguagens por meio de autˆomatos possui complexidade linear no n´umero de estados do AFD, e a interse¸c˜ao possui complexidade quadr´atica no n´umero de estados dos AFDs envolvidos.

contained(R1, R2)

1 M1 ← toAutomaton(R1);

2 M2 ← toAutomaton(R2);

3 M2 Complement ← complement(M2);

4 res ← intersection(M1, M2 Complement);

5 if empty(res)

6 then ✄L(R1) ⊂ L(R2)

7 return true;

8 else ✄L(R1) 6⊂ L(R2)

9 M1 complement← complement(M1);

10 res2 ← intersection(M1 complement,M2);

11 if ¬ empty(res2)

12 thenERROR(“The token definitions given by regular

13 expressions ”+R1+“ and ” + R2 +“ are ambiguous”);

14 return false;

O compilador de Notus utiliza a biblioteca em Java automaton5

para manipula¸c˜ao de express˜oes regulares e autˆomatos. Os m´etodos toAutomaton, complement, intersection e empty usados no procedimento contained fazem parte desta bibli- oteca.

Com as macros expandidas, o compilador de Notus aplica o procedimento contai- ned. Para evitar que cada express˜ao regular tenha que ser comparada com todas as demais, somente alguns tipos de verifica¸c˜oes s˜ao realizados com base nas propriedades das linguagens regulares. Inicialmente, o conjunto das express˜oes regulares que definem os tokens da linguagem ´e particionado em 3 subconjuntos:

1. QUOTED: express˜oes regulares que representam tokens da linguagem identifi- cados por strings entre aspas

2. INFINITY : express˜oes regulares que denotam linguagens infinitas, detectadas pela presen¸ca dos operadores + e ∗ na express˜ao regular6

;

5

Dispon´ıvel no endere¸co http://www.brics.dk/∼amoeller/automaton/ 6

As linguagens regulares finitas denotadas pelas express˜oes ø∗ e λ∗ n˜ao s˜ao permitidas em Notus, o que permite que este teste seja suficiente.

3. OTHER: demais express˜oes regulares que n˜ao pertencem a nenhum dos grupos anteriores; as linguagens denotadas pelas express˜oes regulares deste conjunto s˜ao, portanto, finitas.

Estes trˆes subconjuntos foram comparados entre si para identifica¸c˜ao de express˜oes regulares que denotam linguagens contidas em outras, como mostra o procedimento analyzeRegExp. No entanto, ´e poss´ıvel observar nos procedimentos que analisam as express˜oes desses conjuntos, que algumas compara¸c˜oes foram evitadas. Esta melhoria ´e importante, pois o procedimento contained, respons´avel por esta verifica¸c˜ao, envolve opera¸c˜oes como complemento e interse¸c˜ao, que requerem autˆomatos determin´ısticos, e a opera¸c˜ao de transformar um autˆomato n˜ao-determin´ıstico em determin´ıstico possui complexidade de tempo exponencial no n´umero de estados [Sudkamp, 1997].

analyzeRegExp(QUOTED,OTHER,INFINITY )

1 for each q ∈ QUOTED

2 doanalyzeQuoted(q,OTHER,INFINITY )

3 for each o ∈ OTHER

4 doanalyzeOther(o,QUOTED,OTHER,INFINITY )

5 for each i ∈ INFINITY

6 doanalyzeInfinity(i,INFINITY )

O procedimento analyzeRegExp ´e importante para determinar a ordem em que as defini¸c˜oes dos tokens devem ser geradas em Alex. Adicionalmente, um analisa- dor l´exico otimizado, com um n´umero menor de estados, ´e gerado utilizando-se as informa¸c˜oes coletadas no procedimento analyzeQuoted.

O procedimento analyzeQuoted verifica a possibilidade de uma linguagem deno- tada por uma express˜ao regular R1 ∈ QUOTED estar contida na linguagem denotada

por R2 ∈ OTHER ∪ INFINITY . O procedimento run pertencente `a biblioteca au-

tomaton ´e respons´avel por verificar se um autˆomato reconhece o string que comp˜oe uma express˜ao regular. Sendo este teste verdadeiro, tem-se que a linguagem denotada por R1 est´a contida em R2, e R1 ´e inserida em uma lista auxiliar, containedRegExps,

associada a R2.

analyzeQuoted(R1,OTHER,INFINITY )

1 for each R2 ∈ OTHER ∪ INFINITY

2 doM2 ← toAutomaton(R2)

3 if run(M2,R1)

4 then ✄L(R1) ⊂ L(R2)

4.1. Compilac¸˜ao da Especificac¸˜ao L´exica 73 Para cada express˜ao regular com uma lista associada composta por elementos s ∈ QUOTED, ´e criado um mapeamento destes elementos em um closure contendo o nome do token definida por s, e a fun¸c˜ao de tratamento de lexema para este token, caso exista. Desta maneira, para o c´odigo da Figura 4.5, a lista associada `a express˜ao regular seqld ∈ INFINITY , que define identificadores, ´e (“if”, “then”, “else”, “while”, “do”). O mapeamento criado para seqld ´e mostrado na Figura 4.8. Note que os closures criados s˜ao compostos apenas pelos nomes dos tokens, j´a que estes n˜ao possuem fun¸c˜oes de lexemas associadas. { “if” 7→ if, “then” 7→ then, “else” 7→ else, “while” 7→ while, “do” 7→ do }

Figura 4.8: Mapeamento criado para a express˜ao regularseqld

Para as express˜oes regulares que possuem mapeamento associado, gera-se uma a¸c˜ao semˆantica que verifica se o string casado est´a associado no mapeamento; se estiver, o resultado ´e o token correspondente, com lexema dado pela aplica¸c˜ao da fun¸c˜ao de tra- tamento ao string casado; se n˜ao estiver, o resultado ´e a pr´opria defini¸c˜ao do token. Para o exemplo da Figura 4.5, o casamento do string if teria como resultado if e o casamento de um identificador qualquer “x ” teria como resultado id. Assim, esta oti- miza¸c˜ao permite que as defini¸c˜oes de tokens referentes `as palavras-chave da linguagem sejam eliminadas, com a conseq¨uente diminui¸c˜ao do tamanho das tabelas de autˆomatos geradas pelo Alex.

Os procedimentos analyzeOther e analyzeInfinity s˜ao utilizados para a cria¸c˜ao de um grafo Gt = (Vt, Et), onde cada v ∈ Vt corresponde a uma express˜ao regular de

defini¸c˜ao de token, e para cada u, v ∈ Vt, tem-se que (u, v) ∈ Et se, considerando que

u representa a express˜ao regular R1 e v, a express˜ao regular R2, L(R1) ⊂ L(R2). A

partir deste grafo, executa-se o algoritmo de ordena¸c˜ao topol´ogica, que informa uma ordem na qual os tokens devem ser gerados.

O procedimento analyzeOther verifica a possibilidade de uma linguagem deno- tada por uma express˜ao regular R1 ∈ OTHER estar contida na linguagem denotada

por R2 ∈ INFINITY ∪ QUOTED ∪ OTHER. Caso isso aconte¸ca, cria-se uma aresta

analyzeOther(R1,QUOTED,OTHER,INFINITY ,Gt)

1 for each R2 ∈ INFINITY ∪ QUOTED ∪ OTHER

2 do if contained(R1, R2)

3 then ✄L(R1) ⊂ L(R2)

4 adjacencieList[Gt,R1] ← adjacencieList[Gt,R1] + R2

O procedimento analyzeInfinity verifica a possibilidade de uma linguagem de- notada por uma express˜ao regular R1 ∈ INFINITY estar contida na linguagem de-

notada por R2 ∈ INFINITY . Caso isso aconte¸ca, cria-se uma aresta de R1 para R2,

adicionando-se R2 na lista de adjacˆencia de R1 (linha 4).

analyzeInfinity(R1, INFINITY , Gt)

1 for each R2 ∈ IN F IN IT Y

2 do if contained(R1, R2)

3 then ✄L(R1) ⊂ L(R2)

4 adjacencieList[Gt,R1] ← adjacencieList[Gt,R1] + R2

Para efeitos de compara¸c˜ao, arquivos Lex.x foram gerados com e sem a otimiza¸c˜ao de elimina¸c˜ao de tokens7

contidos em outros, como exibido na Figura 4.9. Por motivos de simplifica¸c˜ao, n˜ao foi mostrado nesta figura o mapeamento criado para o token seqld mostrado na Figura 4.8. Com a otimiza¸c˜ao, o n´umero de estados do autˆomato finito gerado pelo Alex foi 4, e, sem a otimiza¸c˜ao, 23. O tamanho das tabelas para an´alise l´exica, criadas pelo Alex, tamb´em variou nas duas estrat´egias. O analisador l´exico gerado com a otimiza¸c˜ao manipula tabelas com 417 entradas e sem a otimiza¸c˜ao com 1568 entradas. ´E importante observar que esses n´umeros variam de acordo com a linguagem que est´a sendo definida, principalmente com o n´umero de palavras-chave que a linguagem possui.