·

Engenharia de Computação ·

Compiladores

Send your question to AI and receive an answer instantly

Ask Question

Preview text

Flex Um Tutorial 1 of 6 Flex Um Tutorial Compiladores O que é o Flex Como o Flex funciona Estrutura do arquivo de descrição Um exemplo básico Gerando um scanner Um outro exemplo Mais um exemplo Definição de Padrões O que é o Flex Flex é uma ferramenta para geração automática de analisadores léxicos scanners isto é programas que reconhecem padrões léxicos num texto O Flex é uma evolução da ferramenta Lex sendo mais rápido Fast Lex Lex foi desenvolvido por M E Lesk e E Shmidt Bell Laboratories ATT enquanto que o Flex é um produto da Free Software Foundation Inc Como são comumente distribuídos em sistemas Unix sua documentação se encontra na forma de manual pages para as entradas lex flex e flexdoc Ao invês do programador escrever manualmente um programa que realize a identificação de padrões numa entrada o uso do FlexLex permite que sejam apenas especificados os padrões desejados e as ações necessárias para processálos Para que FlexLex reconheçam padrões no texto tais padrões devem ser descritos através de expressões regulares Como o Flex funciona Flex lê os arquivos de entrada especificados ou a entrada padrão se nenhum arquivo for especificado obtendo assim uma descrição do scanner a ser gerado Este arquivo de entrada é o que chamamos arquivo de definição ou arquivo de descrição A descrição é realizada na forma de pares de expressões regulares e código C Tais pares são denominados regras As regras definem simultaneamente quais padrões devem ser procurados e quais as ações que devem ser executada quando da identificação deste padrão ou seja para cada padrão desejado pode ser associado um conjunto de ações escritas sob a forma de código C Flex gera como saída um arquivo fonte em linguagem C cujo nome é lexyyc no qual é definida a função yylex e as variáveis global yytext e yyleng A função yylex é na verdade o analisador léxico gerado pelo arquivo de definição através do Flex A variável global yytext contêm o texto do padrão reconhecido uma string no momento enquanto yyleng contêm o número de caracteres de tal string podendo ambas serem usadas no trechos de código C que definem as ações associadas a cada padrão A seguir temos uma ilustração que indica os arquivos e etapas necessárias para produção de um scanner através da ferramenta Flex O arquivo lexyyc pode ser compilado para produzir um executável ou pode ser combinado com outros arquivo ou ainda modificado para se integrar com outros sistemas Quando executado o programa gerado é capaz analisar uma cadeia de caracteres recebida como sua entrada Material adaptado Prof Peter Jandl Jr Prof Me Stéfano Borges Flex Um Tutorial 2 of 6 buscando ocorrências dos padrões especificados no arquivo de descrição Quando um dos padrões é encontrados a varíavel yytext passa a apontar para a string do padrão encontrado e o comprimento da string é armazenado em yyleng Após isto o programa gerado passa a executar as ações especificadas pelo código C associado a cada padrão Se nenhum padrão é encontrado então é tomada a ação default que é copiar o caractere para a saída A entrada é varrida caractere a caractere até seu final quando a função yylex retorna zero Se necessário a função yylex pode ser acionada outras vezes mas tal ação só será efetiva se uma chamada a função yyrestartFILE f for executada informando um ponteiro válido para um arquivo de entrada Estrutura do arquivo de descrição Todo arquivo de descrição do Flex possui três seções separadas por uma linha com apenas os caracteres colocados em seu início como esquematicamente ilustrado a seguir D E F I N I Ç Õ E S REGRAS C Ó D I G O Seção DEFINIÇÕES Como esperado a seção D E F I N I Ç Õ E S possui definições léxicas É possível no entanto que esta seção permaneça vazia isto é sem definções Toda definição léxica tem a forma nome definição Como nome devemos usar uma palavra iniciada por letra ou underscore seguida de uma ou mais letras dígitos underscore ou traços A definição segue o nome e se inicia no primeiro caractere não branco continuando até o final da linha Usualmente as definições são conjuntos de caracteres ou expressões regulares contidas entre colchetes A seção D E F I N I Ç Õ E S pode ainda conter a declaração e inicialização de variáveis globais que poderão ser utilizadas nas ações e no código fornecido pelo programador Seção REGRAS A seção REGRAS possui por sua vez as regras do analisador léxico a ser construído Esta seção sempre possui regras pois sem estas o analisador gerado apenas copia sua entrada para a saída Uma regra tem sempre a forma padrão ação O padrão deve começar na primeira coluna do texto e são definidos como expressões regulares extendidas A ação deve sempre ser iniciada na mesma linha e usualmente corresponde a um trecho de código C que será executado toda vez que o padrão for reconhecido na entrada Tanto na seção de D E F I N I Ç Õ E S como de REGRAS qualquer texto identado ou entre e é copiado literalmente para a saída sem os caracteres e Para o reconhecimento dos caracteres e estes devem aparecer não identados como padrões em separado Em adição apenas na seção de D E F I N I Ç Õ E S qualquer texto não identado é copiado literalmente para a saída Sob certos aspectos os caracteres indicam o início e o fim da seção REGRAS Seção CÓDIGO A última seção C Ó D I G O contêm todo o código C definido e fornecido pelo programador Nesta seção usualmente temos declarada uma função main que define o início do programa que pode efetuar uma chamada a função yylex o scanner gerado por Flex Outras funções utilizadas nas ações definidas pelas regras podem ser colocadas aqui Esta seção também pode permanecer vazia ou seja sem código definido pelo programador Recomendase ainda a declaração de uma função yywrap como abaixo Flex Um Tutorial 3 of 6 int yywrap return 1 Um exemplo básico Um arquivo de descrição simples poderia ser como descrito a seguir separe a primeira definicao por um tab int numeroDeLinhas0 numeroDeCaracteres0 incrementa numero de linhas numeroDeLinhas incrementa numero de caracteres numeroDeCaracteres incrementa numero de caracteres numeroDeCaracteres recomendavel declarar sempre funcao yywrap int yywrap programa principal main yylex scanner gerado por Flex printfNumero de Linhas d numeroDeLinhas printfNumero de Caracteres d numeroDeCaracteres int yywrap return 1 Este scanner conta o número de caracteres e o número de linhas da entrada fornecida produzindo como saída apenas duas mensagens informando o valor dos contadores de caracteres e linhas Neste exemplo a seção DEFINIÇÕES contêm apenas a declaração de duas variáveis globais numeroDeLinhas e numeroDeCaracteres ambas acessíveis para a função yylex que será construída pelo Flex e para main declarado na seção CÓDIGO Na seção REGRAS temos a declaração de duas regras Uma para o caractere que incrementa tanto o número de linhas como o número de caracteres lidos outra para os demais caracteres indicada pelo caractere que incrementa o número de caracteres lidos Note que o caractere tem o comportamente de uma cláusula default de uma diretiva switch da linguagem C Gerando um scanner Para gerarmos um analisador léxico scanner devemos seguir os passos seguintes num ambiente Unix Executar o Flex para o arquivo de definição supondo que seu nome seja lex01l flex lex01l Isto produzirá um arquivo fonte em linguagem C que equivale ao scanner desejado O arquivo C produzido tem sempre o nome lexyyc 1 Compilar o código C gerado pelo Flex gcc lexyyc o lex01 lfl O parâmetro o lex01 indica o nome do arquivo executável desejado Sua omissão faz que o executável 2 Flex Um Tutorial 4 of 6 seja produzido com o nome default aout Executar o scanner gerado lex01 3 Ao executarmos o programa este fica aguardando uma entrada manual Podemos assim digitar qualquer texto em uma ou mais linhas O scanner não produz qualquer saída intermediária isto é não produz qualquer espécie de mensagem enquanto esta recebendo sua entrada Podemos finalizar a entrada teclando CtrlD o que produz uma mensagem indicando o numero de linhas e caracteres fornecidos Um outro exemplo Observemos o arquivo de definição a seguir int subs0 username printfs getlogin subs int yywrap main yylex if subs0 printfd substituicoes realizadas subs int yywrap return 1 Nele temos a definição de uma variável global subs que será destinada a contabilizar a quantidade de substituições realizadas pelo scanner seção DEFINIÇÕES Na seção REGRAS temos a declaração de uma única regra a qual reconhece a palavra username cujas ocorrências serão contabilizadas e substituídas pelo nome do usuário obtido através da chamada de uma função da API do Unix Como não existem outras regras nem mesmo uma para todas as demais ocorrências então toda a entrada que não corresponder a palavra username será transcrita verbatim para saída isto é exatamente como figurou na entrada Na seção CÓDIGO temos a declaração de uma função main que aciona o scanner gerado por Flex e indica ao finao o número de substituições realizadas é indicado ao final da execução do programa Temos portanto um scanner capaz de substituir uma determinada ocorrência por outra convertendo uma certa entrada para outra saída especificada Isto poderia ser útil na substitução de sequências de caracteres por outros caracteres correspondendo a programas que realizam a filtragem da entrada Supondo que o arquivo de definição seja denominado lex02l podemos gerar o scanner repetindo os passos indicados anteriormente vide Gerando um scanner flex lex02l gcc lexyyc o lex02 Possuindo um arquivo teste o qual desejamos efetuar a substituição de username pelo nome do usuário corrente poderíamos escrever more teste lex02l Ou lex02 teste A saída do programa poderia igualmente ser redirecionada para um arquivo de nome resultado lex02 teste resultado Flex Um Tutorial 5 of 6 Mais um exemplo Agora veremos um exemplo onde utilizamos expressões regulares para a definição de padrões que deverão ser reconhecidos pelo analisador léxico D I G I T 0 9 I D a z a z 0 9 W H I T E S P A C E t n r D I G I T p r i n t f I n t e i r o s n y y t e x t D I G I T D I G I T p r i n t f R e a l s n y y t e x t I D p r i n t f I d e n t i f i c a d o r s n y y t e x t W H I T E S P A C E E l i m i n a e s p a ç o s e m b r a n c o C a r a c t e r e n a o r e c o n h e c i d o p r i n t f C a r a c t e r e n a o r e c o n h e c i d o s n y y t e x t int yywrap main yylex int yywrap return 1 Neste arquivo de definição temos a declaração de definições léxicas ou invés de variáveis C D I G I T 0 9 I D a z a z 0 9 W H I T E S P A C E t n r Foram criadas três definições léxicas DIGIT que corresponde ao conjunto de caracteres de 0 até 9 que serão aceitos como dígitos ID que corresponde a sequências iniciadas por letras seguidas de zero ou mais letras ou dígitos WHITESPACE que corresponde aos caracteres de tabulação nova linha e retorno de carro Tais definições léxicas são usadas na seção REGRAS através de referências nome Imaginando que o arquivo de definição seja denominado lex03l podemos gerar o scanner repetindo os passos indicados anteriormente vide Gerando um scanner flex lex03l gcc lexyyc o lex03 lfl Sua execução exibe uma mensagem indicando se a entrada é reconhecida como um valor inteiro um valor real ou um identificador sendo que os demais caracteres são indicados como não reconhecidos Definição de Padrões Os padrões de entrada utilizados nas seções DEFINIÇÕES e REGRAS são escritos através de uma notação ampliada das expressões regulares que são da maior para menor precedência c caractere c Flex Um Tutorial 6 of 6 caractere exceto nova linha abc um caractere da classe de caracteres no caso um a ou b ou c abjoZ caractere da classe de caracteres no caso um a ou b ou q u a l q u e r c a r a c t e r e e n t r e j e o ou Z AZ um caractere que não esteja na classe das maiúsculas de A a Z AD um caractere que não esteja na classe das maiúsculas de A a T ou tabulação e zero ou mais e onde e é uma expressão regular e um ou mais e onde e é uma expressão regular e zero ou um e um e opcional onde e é uma expressão regular r25 dois até cinco e onde e é uma expressão regular r2 dois até mais e onde e é uma expressão regular r4 exatamente quatro e onde e é uma expressão regular name uma ocorrência da definição name xyoi literal xyoi x caractere especial se x é um a b f n r t ou v 0 caractere nul código ASCII zero 123 caractere cujo código octal é 123 x2a caractere cujo código hexadecimal é 2a r expressão regular r sendo que os parêntesis são usados para modificar precedência rs concatenação das expressões regulares r e s r s expressão regular r ou s rs expressão regular r apenas se seguido por uma expressão regular s A expressão s não faz parte do texto reconhecido sendo denominada trailing context r expressão regular r apenas no início de uma nova linha equivale a r r expressão regular r apenas no final de uma nova linha equivale a r sr expressão regular r apenas se precedida pela expressão s s1s2r expressão regular r apenas se precedida pela expressão s1 ou s2 r expressão regular r em qualquer condição de início EOF um endoffile fim de arquivo Considerações Adicionais Note que a noção de uma nova linha new line é exatamente o que o compilador C utilizado para compilar os arquivos gerados pelo Flex interpreta como um ou seja pode ser significativamente diferente entre sistemas particulares Sistemas DOS ou Windows entendem uma nova linha como a sequência indicando que o caractere r deve ser implicitamente filtrado da entrada ou explicitamente indicado pelas definições e regras Por exemplo Num sistema Unix r ou r indicam a expressão r no final de uma linha Num sistema DOS a expressão equivalente seria r Dentro de classes de caracteres conjunto de caracteres delimintados por colchetes todos os operadores de expressões regulares perdem seu sentido exceto o caractere de escape o caractere traço e apenas no início da definição da classe o caractere de negação