1 / 43

CES-41 COMPILADORES Aulas Práticas - 2014

CES-41 COMPILADORES Aulas Práticas - 2014. Capítulo I A Ferramenta Flex. Flex é um gerador de analisadores léxicos: Tem como entrada expressões regulares e implementa um autômato finito reconhecedor e classificador dos átomos dos programas a serem compilados

tamah
Download Presentation

CES-41 COMPILADORES Aulas Práticas - 2014

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. CES-41 COMPILADORESAulas Práticas - 2014 Capítulo I A Ferramenta Flex

  2. Flex é um gerador de analisadores léxicos: • Tem como entrada expressões regulares e implementa um autômato finito reconhecedor e classificador dos átomos dos programas a serem compilados • Flexé uma versão, para o sistema DOS, do mais conhecido gerador de analisadores léxicos: o Lex do sistema Unix • O analisador gerado é um programa escrito em C • Flex, Yacc, compilador Gcc e outros softwares estão reunidos numa pasta denominada MinGW(Minimalist GNU for Windows), a ser usada nas aulas práticas de CES-41 • Extrair a pasta MinGW para e:\alunos\ces-41

  3. Programa 1.1: Saída de dados • Num editor de texto (Bloco de Notas, CodeBlocks, ou outros), criar o seguinte arquivo extensão .l (saida.l, por exemplo): %% %% main () { printf ("hello friends!"); } • Cuidado para salvá-lo como “All Files” • Guardar esse arquivo na pasta bin de mingw

  4. No prompt do DOS, entrar em e:\alunos\ces-41\mingw\bin • Executar os seguintes comandos: flex saida.l gcclex.yy.c -lfl a • Executar: a > ttt • Abrir o arquivottt (No DOS:more ttt)

  5. Por curiosidade, abrir o arquivo lex.yy.c e procurar no final a função main que aparece no arquivo saida.l • Flexgera uma função fundamental chamada yylex, mas não é chamada pela main neste programa

  6. Programa 1.2: Entrada de dados • Criar na pasta bin de mingw o arquivo entra.l com o seguinte programa: %% %% main () { int i, n; printf ("Digite o numero de repeticoes: "); scanf ("%d", &n); for (i = 1; i <= n; i++) printf ("\nhello friends!"); }

  7. Executar os seguintes comandos: flex entra.l gcclex.yy.c -lfl a • Criar um arquivo entra.dat, colocando nele o número 10 • Executar os comandos: a < entra.dat a < entra.dat > ttt • Abrir o arquivo ttt(No DOS: more ttt)

  8. Procurar novamente a função main no arquivo lex.yy.c • Novamente main não chama yylex

  9. Esquema de produção de um programa executável usando Flex:

  10. Programa 1.3: Reconhecimento de while • Criar um arquivo (while.l) com o seguinte programa: %{ #define WHILE 1 %} %% while {return WHILE;} %% main () { inti; while (i = yylex ()) printf ("\nstring: %6s; tipo: %d; \n", yytext, i); } yytext é uma variável string, global, do arquivo lex.yy.c Agora main chama yylex Criar um arquivo de dados (while.dat) com o seguinte conteúdo: fabio 111 while else whil whiles if BHwhile22 Executar: flex while.l gcclex.yy.c –lfl a < while.dat

  11. Arquivo de dados: fabio 111 while else whil whiles if BHwhile22 %{ #define WHILE 1 %} %% while {return WHILE;} %% main () { int i; while (i = yylex ()) printf ("\nstring: %6s; tipo: %d; \n", yytext, i); } Resultados: a < while.dat fabio 111 string: while; tipo: 1; elsewhil string: while; tipo: 1; s if BH string: while; tipo: 1; 22 Funcionamento do yylex: yylex lê caractere por caractere da entrada e o coloca em yytext Quando não reconhece uma sequência guardada em yytext, ele escreve seu conteúdo e a esvazia Escreve tudo o que não é reconhecido, inclusive espaços em branco

  12. Arquivo de dados: fabio 111 while else whil whiles if BHwhile22 %{ #define WHILE 1 %} %% while {return WHILE;} %% main () { int i; while (i = yylex ()) printf ("\nstring: %6s; tipo: %d; \n", yytext, i); } Resultados: a < while.dat fabio 111 string: while; tipo: 1; elsewhil string: while; tipo: 1; s if BH string: while; tipo: 1; 22 Funcionamento do yylex: Continua percorrendo a entrada, tentando reconhecer algo Neste programa, só while é reconhecido Ao reconhecer algo, executa a ação em frente {return WHILE;}

  13. Arquivo de dados: fabio 111 while else whil whiles if BHwhile22 %{ #define WHILE 1 %} %% while {return WHILE;} %% main () { int i; while (i = yylex ()) printf ("\nstring: %6s; tipo: %d; \n", yytext, i); } Resultados: a < while.dat fabio 111 string: while; tipo: 1; elsewhil string: while; tipo: 1; s if BH string: while; tipo: 1; 22 Funcionamento do yylex: Retorna zero ao encontrar fim de arquivo Tenta reconhecer a maior string possível Sempre esvazia yytext no início de sua execução

  14. Estrutura de um programa em Flex: • Um programa em Flex é dividido em três partes: Declarações %% Regras de tradução %% Rotinas auxiliares • As strings “%%” são os separadores dessas partes • Seu uso é obrigatório, mesmo que o programa não tenha alguma(s) dessa(s) parte(s)

  15. Regras de tradução: • Constituem-se na parte principal de um programa em Flex • São comandos da forma: p1 {ação1} p2 {ação2}  pn {açãon} • Cada pi é uma expressão regular e cada açãoi é um fragmento de programaem C • Caso um conjunto máximo de caracteres da entrada se enquadre em uma expressão regular pi, a açãoi é executada

  16. Declarações: Nelas estão inclusas: • Declarações de variáveis, tipos e diretivas de pré-processamento (define’s, include’s, etc), tudo escrito em C, delimitado por %{ e %} • Definições regulares componentes das expressões regulares que aparecem nas regras de tradução • Essas definições ficam fora dos %{ e %} • Os arquivos incluídos devem conter somente declarações

  17. Rotinas auxiliares: • São as definições das funções em C, referenciadas nas ações das regras de tradução • Podem trazer inclusão de arquivos com extensão .c • A função main pode aí aparecer

  18. Executar Resultados: a < reservadas.dat fabio 111 string: while; tipo: 1; string: else; tipo: 5; wh whiwhil string: while; tipo: 1; s then string: if; tipo: 2; string: for; tipo: 4; BH string: if; tipo: 2; string: else; tipo: 5; 22 string: if; tipo: 2; 1 string: if11; tipo: 3; Programa 1.4: Reconhecimento de várias palavras Criar um arquivo com o seguinte programa: %{ #define WHILE 1 #define IF 2 #define IF11 3 #define FOR 4 #define ELSE 5 %} %% while {return WHILE;} if {return IF;} if11 {return IF11;} for {return FOR;} else {return ELSE;} %% main () { int i; while (i = yylex ()) printf ("\nstring: %6s; tipo: %d; \n", yytext, i); } Arquivo de dados: fabio 111 while else whwhiwhil whiles then if for BHifelse22 if1 if11

  19. Resultados: a < reservadas.dat string: if; tipo: 2; string: if; tipo: 2; 1 string: if11; tipo: 3; string: if; tipo: 2; 12 Programa 1.4: Reconhecimento de várias palavras %{ #define WHILE 1 #define IF 2 #define IF11 3 #define FOR 4 #define ELSE 5 %} %% while {return WHILE;} if {return IF;} if11 {return IF11;} for {return FOR;} else {return ELSE;} %% main () { int i; while (i = yylex ()) printf ("\nstring: %6s; tipo: %d; \n", yytext, i); } Para if1 e if12, yylexlê1{branco} e 12 Como if1{branco} e if12 não são reconhecidos, ele devolve 1{branco} e 12 para o buffer de entrada Reconhece o if e retorna Arquivo de dados: if if1 if11 if12

  20. Experimentar com o seguintearquivo de dados: if if1 if11 if12 ififwhileif Programa 1.4: Reconhecimento de várias palavras %{ #define WHILE 1 #define IF 2 #define IF11 3 #define FOR 4 #define ELSE 5 %} %% while {return WHILE;} if {return IF;} if11 {return IF11;} for {return FOR;} else {return ELSE;} %% main () { int i; while (i = yylex ()) printf ("\nstring: %6s; tipo: %d; \n", yytext, i); }

  21. Expressão regular reconhecedora de espaço de um ou mais brancos, tabulações, new-lines ou carriage-returns Programa 1.5: Tratamento de espaços em branco %{ #define WHILE 1 #define IF 2 #define IF11 3 #define FOR 4 #define ELSE 5 %} %% [ \t\n\r]+ {printf ("\n");} while {return WHILE;} if {return IF;} if11 {return IF11;} for {return FOR;} else {return ELSE;} %% main () { inti; while (i = yylex ()) printf ("\nstring: %6s; tipo: %d; \n", yytext, i); } [abc] significa: um caractere que pode ser a, b ou c [abc]+ significa: um ou mais caracteres a, b ou c [abc]* significa: zero ou mais caracteres a, b ou c Arquivo de dados: fabio 111 while else whwhiwhil whiles then if for BHifelse22 if1 if11

  22. fabio 111 string: while; tipo: 1; string: else; tipo: 5; wh whi whil string: while; tipo: 1; s then string: if; tipo: 2; string: for; tipo: 4; BH string: if; tipo: 2; string: else; tipo: 5; 22 string: if; tipo: 2; 1 string: if11; tipo: 3; Arquivo de dados: fabio 111 while else whwhiwhil whiles then if for BHifelse22 if1 if11 Resultados: Reconhecimento da regra [ \t\n\r]+ {printf ("\n");} Imprime new-line e não retorna Esvazia yytext ao iniciar novo processo de reconhecimento

  23. O conteúdo do que está entre os colchetes [ e ] representa um só caractere Programa 1.6: Identificadores, números e operadores %{ #define DOLAR 0 #define LT 1 #define LE 2 #define EQ 3 #define NE 4 #define GT 5 #define GE 6 #define IF 7 #define THEN 8 #define ELSE 9 #define ID 10 #define NUM 11 %} delim [ \t\n\r] ws {delim}+ digito [0-9] letra [A-Za-z] num {digito}+ id {letra}({letra}|{digito})* %% Entre as chaves { e } coloca-se o nome de uma definição regular Entre os parêntesis ( e ) coloca-se uma sub-expressão regular Definições regulares

  24. {ws} { ;} if {return IF;} then {return THEN;} else {return ELSE;} {id} {return ID;} {num} {return NUM;} "<" {return LT;} "<=" {return LE;} "=" {return EQ;} "<>" {return NE;} ">" {return GT;} ">=" {return GE;} "$" {return DOLAR;} %% main () { inti; while (i = yylex ()) printf ("\nstring: %6s; tipo: %d;", yytext, i); } É preciso usar chaves: {id}, {num} e {ws} Por que? A função main é igual à dos programas anteriores, exceto por um ‘\n’

  25. Executar com o seguinte arquivo de entrada: thenif xxx 123 < <> <= >= > = else $ {ws} { ;} if {return IF;} then {return THEN;} else {return ELSE;} {id} {return ID;} {num} {return NUM;} "<" {return LT;} "<=" {return LE;} "=" {return EQ;} "<>" {return NE;} ">" {return GT;} ">=" {return GE;} "$" {return DOLAR;} %% main () { inti; while (i = yylex ()) printf ("\nstring: %6s; tipo: %d;", yytext, i); } Resultados: string: then; tipo: 8; string: if; tipo: 7; string: xxx; tipo: 10; string: 123; tipo: 11; string: <; tipo: 1; string: <>; tipo: 4; string: <=; tipo: 2; string: >=; tipo: 6; string: >; tipo: 5; string: =; tipo: 3; string: else; tipo: 9; Por curiosidade, colocar a regra do {id} antes da regra do if Caso uma string seja reconhecida por 2 regras, a regra escolhida é a que aparece primeiro na lista de regras

  26. then if xxx 123 < <> <= >= > = else $ Resultados: string: then; tipo: 8; string: if; tipo: 7; string: xxx; tipo: 10; string: 123; tipo: 11; string: <; tipo: 1; string: <>; tipo: 4; string: <=; tipo: 2; string: >=; tipo: 6; string: >; tipo: 5; string: =; tipo: 3; string: else; tipo: 9; {ws} { ;} if {return IF;} then {return THEN;} else {return ELSE;} {id} {return ID;} {num} {return NUM;} "<" {return LT;} "<=" {return LE;} "=" {return EQ;} "<>" {return NE;} ">" {return GT;} ">=" {return GE;} "$" {return DOLAR;} %% main () { inti; while (i = yylex ()) printf ("\nstring: %6s; tipo: %d;", yytext, i); } Este é um exemplo de formação de átomos de uma mini-linguagem yylex retorna o tipo do átomo

  27. Reconhecimento de strings de 0’s e 1’s contendo um número ímpar de 1’s Programa 1.7: Autômato %{ #define ACEITA 1 #define OUTRA 2 %} delim [ \t\n\r] ws {delim}+ aceita 0*1(0*10*1)*0* string [^ \t\n\r]+ %% {ws} { ;} {aceita} {return ACEITA;} {string} {return OUTRA;} %% main () { inti; while (i = yylex ()) switch (i) { case ACEITA: printf ("%-20s: Aceita\n", yytext); break; case OUTRA: printf ("%-20s: Rejeitada\n", yytext); break; } } [^abc] : um caractere diferente de a, b e c Executar com o seguinte arquivo de entrada: 111 001 00101 11 00 100100 100100001 21 000001 00110001 01110001100

  28. Exercício 1.1: Escrever um programa em Flex reconhecedor de strings sobre o alfabeto {0, 1} que possuam pelo menos dois dígitos 0’s seguidos • Exercício 1.2: Escrever um programa em Flex reconhecedor de strings sobre o alfabeto {0, 1, 2}, nas quais cada dígito 2 é imediatamente seguido por dois 0’s e cada dígito 1 é imediatamente seguido por um dígito 0 ou pelo par de dígitos 20 • Exercício 1.3: Escrever um programa em Flex reconhecedor de strings sobre o alfabeto {0, 1}, nas quais o número de dígitos 0 é par ou o número de dígitos 1 é ímpar

  29. Exercício 1.4: Escrever um programa em Flex reconhecedor de strings sobre o alfabeto {0, 1}, nas quais a string 101 não é uma sub-string • Exercício 1.5: Escrever um programa em Flex reconhecedor de strings sobre o alfabeto {0, 1}, nas quais o número de dígitos 0 é par e o número de dígitos 1 é ímpar

  30. Programa 1.8: Atributos para os átomos além do tipo Sejam os seguintesdefine’spara tipos de átomos: %{ #define ELSE 1 #define IF 2 #define WHILE 3 #define ID 4 #define CTINT 5 #define OPREL 6 %} O tipo dos átomos ELSE, IF e WHILE já os define completamente Os átomos de tipos ID, CTINT e OPREL necessitam de mais informações para ficarem completamente definidos: ID: sua string CTINT: seu valor numérico OPREL: qual o operador relacional Solução: atributos para os átomos

  31. Atributos: ID: string CTINT: valor numérico OPREL: operador %{ #include <string.h> #define ELSE 1 #define IF 2 #define WHILE 3 #define ID 4 #define CTINT 5 #define OPREL 6 #define LT 1 #define LE 2 #define GT 3 #define GE 4 #define EQ 5 #define NE 6 union { char string[50]; intatr, valor; charcarac; } yylval; %} Define’s para os atributos dos átomos de tipo OPREL yylval: variável global com vários campos: Um campo para cada tipo de átomo

  32. delim [ \t\n\r] ws {delim}+ digito [0-9] letra [A-Za-z] ctint {digito}+ id {letra}({letra}|{digito})* %% {ws} { ;} else {return ELSE;} if {return IF;} while {return WHILE;} {id} {strcpy (yylval.string, yytext); return ID;} {ctint} {yylval.valor = atoi(yytext); return CTINT;} "<" {yylval.atr = LT; return OPREL;} "<=" {yylval.atr = LE; return OPREL;} ">" {yylval.atr = GT; return OPREL;} ">=" {yylval.atr = GE; return OPREL;} "==" {yylval.atr = EQ; return OPREL;} "!=" {yylval.atr = NE; return OPREL;} %% Alguns átomos formados por yylex são compostos pelo valor retornado e pelo valor de algum campo de yylval

  33. main () { int i; printf ("\n texto | tipo | atributo \n"); printf ("--------------------------------\n"); while (i = yylex ()) { printf ("%10s|%10d|", yytext, i); switch (i) { case ID: printf ("%10s", yylval.string); break; case CTINT: printf ("%10d", yylval.valor); break; case OPREL: printf ("%10d", yylval.atr); break; } printf ("\n"); } } Executar com o seguinte arquivo de entrada: while if else xxx 123 < <= > >= == != Acrescentar pelo meio da entrada: (&%

  34. Programa 1.9: Tratamento de caracteres estranhos • Acrescentar a seguinte regra no final das regras de tradução: . {yylval.carac = yytext[0]; return INVAL;} • Acrescentar no meio dos define’s a declaração: #define INVAL 7 • Acrescentar na função main ( ): case INVAL: printf ("%10c", yylval.carac); break; Alterações no Programa 1.8 O ponto ‘.’ é um meta-símbolo que significa qualquer caractere, exceto o new-line

  35. Exercício 1.6: Acrescentar ao programa anterior regras para reconhecimento de constantes reais, caracteres e strings • Constante real: um ou mais dígitos seguidos de um ponto decimal, seguido de zero ou mais dígitos • A constante pode ainda estar na notação exponencial: • Acrescenta-se opcionalmente o seguinte: a letra E maiúscula ou minúscula, seguida opcionalmente de + ou -, seguidos de um ou mais dígitos • Exemplos: 12. 3.57 0.23 3.2E19 7.5e-45

  36. Exercício 1.6: Acrescentar ao programa anterior regras para reconhecimento de constantes reais, caracteres e strings • Constante real: um ou mais dígitos seguidos de um ponto decimal, seguido de zero ou mais dígitos • Definição regular: ctreal {digito}+\.{digito}* • Opcionalmente a letra E maiúscula ou minúscula, seguida opcionalmente de + ou -, seguidos de um ou mais dígitos: ctreal {digito}+\.{digito}*([Ee][+-]?{digito}+)? • <ExprRegular>? significa: <ExprRegular> aparece zero ou uma vez, ou seja, é opcional

  37. Exercício 1.6: Acrescentar ao programa anterior regras para reconhecimento de constantes reais, caracteres e strings • Caractere: qualquer caractere entre apóstrofos; cuidado com os caracteres iniciados pela barra ‘\’; cuidado quando o caractere for o apóstrofo • Exemplos: ‘w’ ‘\n’ ‘\‘’ • String: conjunto de caracteres entre aspas; mesmos cuidados; cuidado quando o caractere for aspas • Exemplo: “w\n123\g\“” Executar o programa com o seguinte arquivo: while ifelse xxx 123 12.53 13. 31.5E-12 1.5e11 19.E+27 ';' '\'' '\n' 's' "ab \\ \" \n'z" '"' "'"

  38. Dicas: • Definir novos tipos de átomos: • Constante real, constante caractere e constante string • Novo campo para yylval: union { char string[50]; intatr, valor; floatvalreal; charcarac; } yylval; • Caractere e string podem usar o campo string do yylval • Em programas mais avançados, caractere usará o campo carac

  39. Qualquer caractere precedido pela ‘\’ ou qualquer caractere que não seja ‘\’ ou apóstrofo isolados • Criar definições regulares para constante real, constante caractere e constante string • Constante caractere: carac1 \\.|[^\\'] ctcarac '{carac1}' • Constante string: carac2 \\.|[^\\\"] string \"{carac2}*\" • Criar novas regras de tradução para constante real, ctcarace string • Aumentar o switch da função main Qualquer caractere precedido pela ‘\’ ou qualquer caractere que não seja ‘\’ ou aspas isolados Solução: no arquivo RealCharString.l Página do Professor

  40. Exercício 1.7: Acrescentar ao programa anterior regras para reconhecimento e descarte de comentários • Comentários: tudo entre /* e */; • Não é para criar um átomo do tipo comentário • Eles devem ser lidos e descartados, antes do retorno da função yylex

  41. Exercício 1.7: Acrescentar ao programa anterior regras para reconhecimento e descarte de comentários • Acrescentar uma linha com o protótipo voidcomentario (void); entre os delimitadores %{ e %} • Acrescentar a seguinte regra de tradução: "/*" {comentario ();} • Acrescentar e programar a seguinte rotina auxiliar: void comentario () { ----- }

  42. Idéia: usar o seguinte autômato depois de detectado o par /*: Executar o programa com o seguinte arquivo: while if /* else xxx */ 123 /*12.53 13. 31.5E-12 */1.5e11/* 19.E+27*/ ';' '\'' '\n' 's' "ab \\ \" \n'z"

  43. void comentario () { char c; int estado; estado = 1; while (estado != 3) { switch (estado) { case 1: c = input (); if (c == EOF) estado = 3; else if (c == '*') estado = 2; break; case 2: - - - - - } } } scanf (“%c”, &c), dentro do yylex, não retira o caractere do arquivo de entrada Por isso usa-se c = input

More Related