Você está na página 1de 10

11

1. programar em assembly, controlando diretamente a leitura do


programa fonte;

2. programar em linguagem de programação de alto nível, utilizando


as facilidades de I/O da linguagem para a leitura do programa
fonte;

3. utilizar uma ferramenta para geração de analisador léxico como o


lex, que provê rotinas para ler e bufferizar a entrada.

Especificação de tokens

ê feita através de expressões regulares, que descrevem formalmente


os padrões dos tokens.

Definições Regulares

ê notação usada para atribuir nomes a expressões regulares e


reutilizá-las através desses nomes.

ê uma definição regular é uma seqüência de definições na forma:

d1  r1
d2  r2
...
dn  rn

onde cada di é um nome distinto e cada ri é uma expressão regular


sobre os símbolos de (  {d1,d2,...,dn}).

ê Ex.: identificadores da linguagem Pascal

letra  A | B | ... | Z | a | ... | z


digito  0 | 1 | ... | 9
id  letra (letra | digito)*

ê Ex.: constantes numéricas da linguagem Pascal

digito  0 | 1 | ... | 9
digitos  digito digito*
fração_opc  . digitos | 
expoente_opc  (E ( +| - | ) digitos) | 
12

num  digitos fração_opc expoente_opc

Extensões

1. '+' : uma ou mais instâncias (ocorrências)


r+ corresponde a r.r* e denota (L(r))+

Ex.: digitos  digito+

2. '?' : zero ou uma instância (ocorrência)


r? corresponde a (r | ) e denota L(r)  {}

Ex.: fração_opc  (. digitos)?

3. caracteres entre '['e ']' : classes de caracteres


[abc] corresponde a a | b | c
[a-z] forma abreviada, corresponde a a | b | ... | z

Ex.: digito  [0-9]

Reconhecimento de tokens

Exemplo

atrib  id op_atrib expr

expr  expr op_ma expr


| expr op_maig expr
| expr op_ig expr
| termo

termo  termo op_vezes termo


| termo div termo
| num

1. identificação de tokens

identificadores, constantes numéricas inteira e real, símbolos especiais,


delimitadores, palavras reservadas etc.

2. especificação de tokens através de definições regulares


13

letra  [a-zA-Z]
digito  [0-9]
id  letra.(letra| digito)*
num  digito+(.digito+)?(E(+|-)?digito+)?
op_atrib  := op_ma  >
op_maig  >= op_ig  =
op_vezes  * div  div
delim  [ \t\n] espac  delim+

3. projeto de diagramas de transição

 um diagrama é um grafo composto por vários subgrafos em que


em cada qual possui caminhos para reconhecer exatamente um
token.

 transições entre estados: rótulos são símbolos de entrada e


pertencem a .

 ações associadas a estados.

 autômatos determinísticos

Ex.: op_ma  > e op_maig  >=

=
2
início >
0 1
outro

 estado 2: reconhecimento do token op_maig


 estado 3: reconhecimento do token op_ma
devolver símbolo "outro"

Ex.: id  letra.(letra| digito)*


14

letra ou dígito

início letra outro


5 6
7

 estado 7: reconhecimento do token id


ações: testar se é palavra reservada
devolver atributo de id

Implementação de Analisadores Léxicos

A implementação de um analisador léxico requer uma descrição do autômato


que reconhece as sentenças da gramática ou expressão regular de
interesse. Com essa descrição, é possível oferecer os seguintes
procedimentos auxiliares para o analisador léxico:

 ESTADO-INICIAL, que recebe como argumento a referência para o


autômato e retorna o seu estado inicial;
 ESTADO-FINAL, que recebe como argumentos a referência para o
autômato e a referência para o estado corrente. O procedimento
retorna true se o estado especificado é elemento do conjunto de
estados finais do autômato, ou false caso contrário; e
 PRÓXIMO-ESTADO, que recebe como argumento a referência para o
autômato, para o estado corrente e para o símbolo sendo analisado. O
procedimento consulta a tabela de transições e retorna o próximo
estado do autômato, ou o valor nulo se não houver transição possível.

A sentença a ser reconhecida é estruturada como uma lista de símbolos, que


é passada como argumento para o analisador léxico juntamente com a
referência para o autômato.

O analisador léxico inicia sua operação, definindo o estado inicial como o


estado corrente. Obtém então o próximo símbolo (que inicialmente é o
primeiro símbolo) da sentença . Se não houver próximo símbolo, é preciso
verificar se o estado corrente é um estado final. Se for, o procedimento
retorna true, indicando que a sentença foi reconhecida pelo autômato. Se o
estado corrente não for um estado final e não houver mais símbolos na
sentença, então não houve reconhecimento e o procedimento retorna false.

Se houver símbolo a ser analisado, então o procedimento deve continuar o


processo de reconhecimento. Para tanto, obtém o próximo estado
correspondente à transição do estado atual pelo símbolo sob análise. Se não
15

houver transição possível, então a sentença não foi reconhecida e o


procedimento deve encerrar, retornando false.

Esse algoritmo é apresentado a seguir que determina se a string  pertence


à linguagem reconhecida pelo autômato M.

Lex

ê lex é um gerador de programas projetado para o processamento


léxico de seqüências de caracteres.

ê lex aceita como entrada uma especificação baseada em padrões


(expressões regulares) e produz um programa em linguagem de alto
nível (C) para o reconhecimento de conjuntos regulares.

ê o código gerado identifica subcadeias baseadas nos padrões


especificados, particionando a seqüência de entrada em unidades
básicas.

ê uma especificação lex permite a associação de ações aos padrões


especificados, de forma a permitir a sua execução quando houver
casamento de um padrão na seqüência de entrada.

A ferramenta lex

ê arquivo com extensão ".l"


16

contém um programa lex, que consiste de um conjunto de expressões


regulares associado a um conjunto de ações expressas na linguagem
C.

ê arquivo lexyy.c (ou lex.yy.c)

contém uma representação tabular de um diagrama de transições


construído a partir das expressões regulares e um programa para
simular o reconhecimento de uma seqüência de entrada, consultando o
autômato e executando as ações associadas a cada padrão.

 função (int) yylex( )

programa lex lexyy.c


lex
(arq.l)

lexyy.c compilador anal. léxico


C

sequência de anal. sequência de


léxico
entrada tokens

ê lex e yacc
regras
padrões
gramaticais

lex yacc

input yylex yyparse programa


analisado

ê no contexto do processo de compilação, lex é um gerador de


analisador léxico (função yylex()) e yacc é um gerador de analisador
sintático (função yyparse()).

lex arq.l (gera arquivo lexyy.c)

yacc -d arq.y (gera arquivos ytab.c e ytab.h)


17

ê yyparse() ativa yylex() sempre que necessita de um token.

ê yylex() retorna um token para yyparse() e, caso deseja retorna


algum atributo, utiliza a variável global yylval.

Programas lex

ê um programa lex consiste de três partes:

declarações
%%
regras
%%
funções auxiliares

ê a parte I (declarações) consiste em declarações de variáveis,


constantes e definições regulares. (Opcional).

 os nomes de definições regulares usados do lado direito devem


aparecer entre '{' e '}'.

Ex.:
letra [a-z]
ident {letra}+

ê a parte II (regras) consiste em sentenças na forma:

p1 { ação 1}
p2 { ação 2}
... ...
pn { ação n}

onde: cada pi é uma expressão regular (padrão) e


cada ai é uma ação ou conjunto de ações em C.

 dois ou mais padrões consecutivos podem compartilhar a mesma


ação.

Ex.:
p1 |
p2 { ação }
18

 lex adiciona um padrão+ação default aqueles especificados pelo


programador (todo caractere de entrada não reconhecido é
copiado na saída padrão).

 um comando de 'return' explícito deve ser colocado nas ações


para que o controle seja retornado a quem ativou yylex().

ê a parte III (funções auxiliares) consiste em funções necessárias à


execução das ações da parte II. (Opcional).

ê variáveis

 yytext: contém a cadeia de entrada (lexema) que casou com um


determinado padrão.

 yyleng: contém o valor de strlen(yytext).

 yylineno: contém o número da linha de entrada corrente.

Exemplo

%{
/* conta palavras */

int nchar, nword, nline;


%}

%%

\n ++nchar, ++nline;

[^ \t\n]+ ++nword, nchar += yyleng;


. ++nchar;

%%

main()
{
yylex();
printf("%d\t%d\t%d\n", nchar, nword, nline);
}
19

Padrões lex

 letras, dígitos e alguns caracteres especiais representam a si próprios.

 justaposição é concatenação.

 '|' é união.

 '.' (ponto) representa qualquer caractere, exceto '\n'.

 classe de caracteres

[abc] qualquer caractere da seqüência (a | b | c)


[^abc] qualquer caractere diferente de a, b ou c
[a-c] qualquer caractere no intervalo (fechado)

 '*' (asterisco) representa 0 ou mais ocorrências da expressão à


esquerda.

 '?' (interrogação) representa 0 ou 1 ocorrência.

ab?c cadeia "ac" ou cadeia "abc"

 iteração limitada: intervalo numérico entre '{' e '}'

[a-z][a-z0-9]{0,7}

 '\' (barra invertida) é um caractere de escape para caracteres especiais


(meta-símbolos de lex).

\\ símbolo barra invertida


\* símbolo asterisco
\" símbolo aspas
\n \t \b símbolos de newline, tab e backspace

 caractere entre '"' (aspas): outra forma de expressar caracteres


especiais.

"\" símbolo barra invertida


"*" símbolo asterisco
"\"" símbolo aspas
20

Obs.: caracteres especiais não precisam \ ou " dentro de classes de


caracteres.

 caracteres especiais (meta-símbolos):

" \ [ ] ^ - ? . * +
| $ ( ) / { } % < >

Exemplos

x letra "x"
abc cadeia "abc"
"*" símbolo asterisco
xyz"+" cadeia "xyz+"
xyz+ cadeias que começam por xy seguidas de 1 ou mais z's
[+-0-9] símbolo + ou símbolo - ou qualquer dígito

Padrões Sensíveis ao Contexto

 '^' (circunflexo) no início de um padrão representa o início de uma linha


de entrada.

^.*\n uma linha de entrada, incluindo \n.

 '$' (dólar) ao final de um padrão representa o final de uma linha de


entrada (mas não inclui \n)

^.*$ uma linha de entrada, excluindo \n.

 '/' (barra) é um operador de contexto à direita.

ab/cd casa com a cadeia "ab", mas só se seguida por "cd".

ab/\n é o mesmo que ab$

Regras para retirar ambigüidade

1. lex sempre escolhe o padrão que representa a cadeia de maior


comprimento na seqüência de entrada.

Você também pode gostar