CES41 COMPILADORES Aulas Prticas 2018 Captulo I A

  • Slides: 44
Download presentation
CES-41 COMPILADORES Aulas Práticas - 2018 Capítulo I A Ferramenta Flex

CES-41 COMPILADORES Aulas Práticas - 2018 Capítulo I A Ferramenta Flex

Flex é um gerador de analisadores léxicos: n Tem como entrada expressões regulares e

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

Programa 1. 1: Saída de dados n Num editor de texto (Bloco de Notas,

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

n No prompt do DOS, entrar na pasta mingwbin n Executar os seguintes comandos:

n No prompt do DOS, entrar na pasta mingwbin n Executar os seguintes comandos: flex saida. l gcc lex. yy. c -lfl a n Executar: n Abrir o arquivo ttt (No DOS: more ttt) a > ttt

n Por curiosidade, abrir o arquivo lex. yy. c e procurar no final a

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

Programa 1. 2: Entrada de dados n Criar na pasta bin de mingw o

Programa 1. 2: Entrada de dados n 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!"); }

n Executar os seguintes comandos: flex entra. l gcc lex. yy. c -lfl a

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

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

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

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

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

Programa 1. 3: Reconhecimento de while n Criar um arquivo (while. l) com o

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

%{ Arquivo de dados: #define WHILE 1 %} fabio 111 while else %% while

%{ Arquivo de dados: #define WHILE 1 %} fabio 111 while else %% while {return WHILE; } whiles if BHwhile 22 %% main () { int i; while (i = yylex ()) printf ("nstring: %6 s; tipo: %d; n", yytext, i); } Funcionamento do yylex: Resultados: a < while. dat fabio 111 string: while; else whil string: while; s if BH string: while; 22 yylex lê caractere por caractere da entrada e o coloca em yytext tipo: 1; 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 e newline’s.

%{ Arquivo de dados: #define WHILE 1 %} fabio 111 while else %% while

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

%{ Arquivo de dados: #define WHILE 1 %} fabio 111 while else %% while

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

Estrutura de um programa em Flex: n Um programa em Flex é dividido em

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

Regras de tradução: n Constituem-se na parte principal de um programa em Flex n

Regras de tradução: n Constituem-se na parte principal de um programa em Flex n São comandos da forma: p 1 {ação 1} p 2 {ação 2} pn {açãon} n Cada pi é uma expressão regular e cada açãoi é um fragmento de programa em C n Caso um conjunto máximo de caracteres da entrada se enquadre em uma expressão regular pi, a açãoi é

Declarações: Nelas estão inclusas: n Declarações de variáveis, tipos e diretivas de préprocessamento (define’s,

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

Rotinas auxiliares: n São as definições das funções em C, referenciadas nas ações das

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

Programa 1. 4: Reconhecimento de várias palavras Resultados: Executar a < reservadas. dat fabio

Programa 1. 4: Reconhecimento de várias palavras Resultados: Executar a < reservadas. dat fabio 111 string: while; tipo: 1; Criar um arquivo com o seguinte programa: %{ string: else; tipo: 5; #define WHILE 1 wh whil #define IF 2 string: while; tipo: 1; s then #define IF 11 3 string: if; tipo: 2; #define FOR 4 #define ELSE 5 string: for; tipo: 4; %} BH %% string: if; tipo: 2; while {return WHILE; } if {return IF; } string: else; tipo: 5; 22 if 11 {return IF 11; } Aqui 2; não string: if; tipo: for {return FOR; } 1 else {return ELSE; } tem ‘n’ string: if 11; tipo: 3; %% main () { int i; while (i = yylex ()) printf ("nstring: %6 s; while tipo: %d; i); Arquivo de dados: fabio 111 elsen", wh yytext, whil } whiles then if for BHifelse 22 if 11

Programa 1. 4: Reconhecimento de várias palavras Resultados: a < reservadas. dat string: if;

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

Programa 1. 4: Reconhecimento de várias palavras Experimentar com o seguinte arquivo de dados:

Programa 1. 4: Reconhecimento de várias palavras Experimentar com o seguinte arquivo de dados: %{ if if 11 if 12 #define WHILE 1 ifif whileif #define IF 2 #define IF 11 3 #define FOR 4 #define ELSE 5 %} %% while {return WHILE; } if {return IF; } if 11 {return IF 11; } for {return FOR; } else {return ELSE; } %% main () { int i; while (i = yylex ()) printf ("nstring: %6 s; tipo: %d; n", yytext, i); }

Programa 1. 5: Tratamento de espaços em branco Expressão regular reconhecedora de espaço de

Programa 1. 5: Tratamento de espaços em branco Expressão regular reconhecedora de espaço de um ou mais brancos, tabulações, new-lines ou carriagereturns %{ #define WHILE 1 #define IF 2 #define IF 11 3 #define FOR 4 [abc] significa: um caractere que #define ELSE 5 pode ser a, b ou c %} %% [abc]+ significa: um ou mais [ tnr]+ {printf ("n"); } while {return WHILE; } caracteres a, b ou c if {return IF; } if 11 {return IF 11; } [abc]* significa: zero ou mais for {return FOR; } caracteres a, b ou c else {return ELSE; } %% main () { int i; while (i = yylex ()) printf %d; else n", yytext, Arquivo de ("nstring: dados: fabio%6 s; 111 tipo: while wh whi i); } whiles then if for BHifelse 22 if 11

Resultados: fabio 111 Arquivo de dados: fabio 111 while else wh whiles then if

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

Programa 1. 6: Identificadores, números e operadores %{ #define #define #define %} delim ws

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

{ws} { ; } if {return IF; } then {return THEN; } else {return

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

Executar com o seguinte arquivo de entrada: {ws} { ; } then if xxx

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

then if xxx 123 <= >= > = else < $ != {ws} {

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

Programa 1. 7: Autômato Reconhecimento de strings %{ #define ACEITA 1 de 0’s e

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

n Exercício 1. 1: Escrever um programa em Flex reconhecedor de strings sobre o

n 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 n 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 n 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

n Exercício 1. 4: Escrever um programa em Flex reconhecedor de strings sobre o

n 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 n 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

Programa 1. 8: Atributos para os átomos além do tipo Sejam os seguintes define’s

Programa 1. 8: Atributos para os átomos além do tipo Sejam os seguintes define’s para tipos de átomos: %{ #define #define %} ELSE IF WHILE ID CTINT OPREL 1 2 3 4 5 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

%{ #include <string. h> #define ELSE #define IF #define WHILE #define ID #define CTINT

%{ #include <string. h> #define ELSE #define IF #define WHILE #define ID #define CTINT #define OPREL #define LT #define LE #define GT #define GE #define EQ #define NE union { char string[50]; int atr, valor; char carac; } yylval; %} Atributos: 1 2 3 4 5 6 ID: string CTINT: valor numérico OPREL: operador 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

delim ws digito letra ctint id %% {ws} else if while {id} {ctint} "<"

delim ws digito letra ctint id %% {ws} else if while {id} {ctint} "<" "<=" ">=" "==" "!=" %% [ tnr] {delim}+ [0 -9] [A-Za-z] {digito}+ {letra}({letra}|{digito})* { ; } {return ELSE; } {return IF; } {return WHILE; } {strcpy (yylval. string, yytext); return ID; } {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

main () { int i; printf ("n texto | tipo | atributo n"); printf

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

Programa 1. 9: Tratamento de caracteres estranhos n Acrescentar a seguinte regra no final

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

Exercício 1. 6: Acrescentar ao programa anterior regras para reconhecimento de constantes reais, caracteres

Exercício 1. 6: Acrescentar ao programa anterior regras para reconhecimento de constantes reais, caracteres e strings n Constante real: um ou mais dígitos seguidos de um ponto decimal, seguido de zero ou mais dígitos n A constante pode ainda estar na notação exponencial: - n 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. 2 E 19 7. 5 e-45

Exercício 1. 6: Acrescentar ao programa anterior regras para reconhecimento de constantes reais, caracteres

Exercício 1. 6: Acrescentar ao programa anterior regras para reconhecimento de constantes reais, caracteres e strings n Constante real: um ou mais dígitos seguidos de um ponto decimal, seguido de zero ou mais dígitos n Definição regular: ctreal n {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}+)?

Exercício 1. 6: Acrescentar ao programa anterior regras para reconhecimento de constantes reais, caracteres

Exercício 1. 6: Acrescentar ao programa anterior regras para reconhecimento de constantes reais, caracteres e strings n Caractere: qualquer caractere entre apóstrofos; cuidado com os caracteres iniciados pela barra ‘’; cuidado quando o caractere for o apóstrofo - n Exemplos: ‘w’ ‘n’ ‘‘’ String: conjunto de caracteres entre aspas; mesmos cuidados; cuidado quando o caractere for aspas - Exemplo: “wn 123g“” Executar o programa com o seguinte arquivo: while if else xxx 123 12. 53 13. 31. 5 E-12 1. 5 e 11 19. E+27 '; ' ''' 'n' 's' "ab \ " n'z" '"' "'"

Dicas: n Definir novos tipos de átomos: - n Constante real, constante caractere e

Dicas: n Definir novos tipos de átomos: - n Constante real, constante caractere e constante string Novo campo para yylval: union { char string[50]; int atr, valor; float valreal; char carac; } yylval; n Caractere e string podem usar o campo string do yylval n Em programas mais avançados, caractere usará o

n Criar definições regulares para constante real, constante caractere e constante string n Constante

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

Execução: para o arquivo de entrada sugerido:

Execução: para o arquivo de entrada sugerido:

Exercício 1. 7: Acrescentar ao programa anterior regras para reconhecimento e descarte de comentários

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

Exercício 1. 7: Acrescentar ao programa anterior regras para reconhecimento e descarte de comentários

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

Idéia: usar o seguinte autômato depois de detectado o par /*: Executar o programa

Idéia: usar o seguinte autômato depois de detectado o par /*: Executar o programa com o seguinte arquivo: while if */1. 5 e 11/* /* else 19. E+27*/ xxx */ 123 /*12. 53 13. 31. 5 E-12 '; ' ''' 'n' 's' "ab \ " n'z"

void comentario () { char c; int estado; estado = 1; while (estado !=

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 - - yylex, não retira o } caractere do arquivo de } entrada } do Por isso usa-se c = input ( )