Você está na página 1de 11

Programao de Autmatos Finitos Determinsticos

Centro Federal de Educacao Tecnologica de Minas Gerais

Alain Andr Tomaz Amaral1 Thiago Mendes Vieira2

RESUMO Este trabalho prope um material de apoio para estudo de implementao de autmatos na programao. Tem como objetivo mostrar mtodos, tcnicas e exemplos de implementao de autmatos finitos determinsticos na programao, bem como apresentar a ferramenta Lex, uma ferramenta de apoio que permite gerar cdigo verificador de linguagens regulares atravs de definies de suas expresses regulares. ABSTRACT This paper proposes a support material for the study of automata implementation on programming. Aims to show the methods, techniques and examples of implementation of deterministic finite automata programming, as well as presenting the tool Lex, a support tool to generate code checker regular languages via settings of your regular expressions.

Palavras-chaves: autmato, programao, expresso regular, Lex

Graduando em Engenharia de Computao (CEFET-MG). Email: alainandreamaral@gmail.com 2 Graduando em Engenharia de Computao (CEFET-MG). Email: thiagomv.0301@gmail.com

1 INTRODUO Os conceitos de autmatos constituem um modelo til para muitos elementos importantes de hardware e software. Alguns dos elementos mais importantes onde tais conceitos so empregados so: 1. Software que verifica o comportamento de circuitos digitais; 2. O analisador lxico de um compilador tpico; 3. Software que encontra ocorrncias de palavras, frases ou padres em textos; 4. Software para verificar sistemas que tm um nmero finito de estados distintos, como protocolos. (HOPCROFT et al., 2002) O reconhecimento de linguagens uma das principais aplicaes das teorias de autmatos. No estudo aqui apresentado sero mostradas tcnicas e ferramentas utilizadas para implementao de softwares reconhecedores de linguagens. As linguagens que sero abordadas no estudo sero as Linguagens Regulares.

2 SOFTWARE DE RECONHECIMENTO DE LINGUAGENS REGULARES 2.1 Estudo de caso: Reconhecimento de dadas vlidas 2.1.1 O diagrama de transio Vamos iniciar nossos estudos de autmatos na programao apresentando um diagrama de transio que reconhece uma data vlida no formato aaaa/mm/dd. Para simplificao do problema vamos desconsiderar os casos de anos bissextos.

Figura 1 - Autmato reconhecedor de data vlida

Como se pode ver este autmato possui trs estados finais, que identificam a quantidade de dias do ms, alm de verificar a validade da data.

Existem vrias formas de usar uma coleo de diagramas de transio para construir um algoritmo verificador de linguagem. Podemos imaginar uma varivel state contendo o nmero do estado corrente para o diagrama de transio. Um comando switch baseado no valor de state que nos leva ao cdigo para cada um dos possveis estados, onde encontramos a ao desse estado. Normalmente o cdigo para um estado ele mesmo, um comando switch ou um desvio de mltiplos caminhos que determina o prximo estado, lendo e examinando o prximo caractere da entrada. 2.1.2 O algoritmo Abaixo o algoritmo verificador de data escrito na linguagem c++:
#include <iostream> using namespace std; #define #define #define #define #define STATE_ERROR 1000 D28 1 D30 2 D31 3 R_ERROR -1

int main(){ int state = 0; //guarda o estado atual no automato char str[1000];//buffer para armazenar a palavra a ser verificada cin.getline(str,1000); int contadorAno = 0; //conta os dgitos do ano int index = -1; //percorre cada posio no buffer char now; //caracter atual que esta sendo analizado bool running = true; int result; //resultado encontrado na verificao da palavra while(running){ index++;//prxima posio no buffer now=str[index];//smbolo atual na palavra switch(state){ case 0: if(isdigit(now)){ contadorAno++; if(contadorAno==4) state = 1; }else{ state = STATE_ERROR; } break; case 1: if(now=='/') state = 2; else state = STATE_ERROR;

case 2:

break; if(now=='0') state = 3; else if(now=='1') state = 4; else state = STATE_ERROR; break; if(now=='1' || now=='3' || now=='5' || now=='7' || now=='8') state = 5; else if(now=='4' || now=='6' || now=='9') state = 6; else if(now=='2') state = 7; else state = STATE_ERROR; break; if(now=='0' || now=='2') state = 8; else if(now=='1') state = 9; else state = STATE_ERROR; break; if(now=='/') state = 10; else state = STATE_ERROR; break; if(now=='/') state = 11; else state = STATE_ERROR; break; if(now=='/') state = 12; else state = STATE_ERROR; break; if(now=='/') state = 10; else state = STATE_ERROR; break;

case 3:

case 4:

case 5:

case 6:

case 7:

case 8:

case 9:

if(now=='/') state = 11; else state = STATE_ERROR; break; case 10:

case

case

case

case

case

case

case

if (now == '0') state = 13; else if (now == '1'||now == '2') state = 14; else if (now == '3') state = 15; else state = STATE_ERROR; break; 11: if (now == '3') state = 16; else if (now == '1'||now == '2') state = 17; else if (now == '0') state = 18; else state = STATE_ERROR; break; 12: if (now == '2') state = 19; else if (now == '0') state = 20; else if (now == '1') state = 21; else state = STATE_ERROR; break; 13: if (isdigit(now) && now != '0') state = 22; else state = STATE_ERROR; break; 14: if (isdigit(now)) state = 22; else state = STATE_ERROR; break; 15: if ( now == '0' || now == '1') state = 22; else state = STATE_ERROR; break; 16: if (now == '0') state = 23; else state = STATE_ERROR; break; 17: if (isdigit(now)) state = 23; else state = STATE_ERROR;

break; case 18: if (isdigit(now) && now != '0') state = 23; else state = STATE_ERROR; break; case 19: if (isdigit(now) && now != '9') state = 24; else state = STATE_ERROR; break; case 20: if (isdigit(now) && now != '0') state = 24; else state = STATE_ERROR; break; case 21: if (isdigit(now)) state = 24; else state = STATE_ERROR; break; case 22: result=D31; if(index+1==strlen(str)) state = STATE_ERROR; else running = false; break; case 23: result=D30; if(index+1==strlen(str)) state = STATE_ERROR; else running = false; break; case 24: result=D28; if(index+1==strlen(str)) state = STATE_ERROR; else running = false; break; case STATE_ERROR: result=R_ERROR; running = false; break; }

//Analiza o resultado encontrado switch(result){ case R_ERROR: cout<<"ERRO\n"; break;

case D28: cout<<"Mes com 28 dias\n"; break; case D30: cout<<"Mes com 30 dias\n"; break; case D31: cout<<"Mes com 31 dias\n"; break; } }

O algoritmo acima baseado no diagrama de transio do autmato verificador de datas vlidas (figura 1). Ele faz a leitura de uma palavra e em seguida verifica se esta uma data vlida. Na verificao retornado um resultado que identifica a quantidade de dias do ms da data, ou um erro, caso a palavra no for uma data vlida. 2.2 A ferramenta Lex Em cincia da computao (linguagens de programao), lex um programa que gera analisadores lxicos. Ele geralmente usado com o yacc, um gerador de analisador sinttico. Escrito originalmente por Eric Schmidt e Mike Lesk, ele o gerador de analisador lxico padro em diversos sistemas Unix. O lex l um fluxo de entrada especificando um analisador que mapeia expresses regulares em blocos de cdigo, e retorna um cdigo fonte implementando o analisador. Apesar do gerador ser genrico e poder se adequar a diferentes linguagens de programao, atualmente, somente a gerao de cdigo C suportada. Apesar de ser software proprietrio, verses do lex baseadas no cdigo original da AT&T esto disponveis em cdigo aberto, como parte de sistemas como OpenSolaris e Plan 9. Outra verso popular e livre do lex o flex. A estrutura de um arquivo lex intencionalmente similar ao de um arquivo yacc. Os arquivos so divididos em trs sees, separadas por linhas que contm somente dois smbolos de porcentagem, como a seguir: definies %% regras %% subrotinas

Na seo de definies so definidas as macros e so importadas as bibliotecas escritas em C. tambm possvel escrever cdigo C na mesma seo. J a seo de regras associa padres com instrues C, padres escritos na forma de expresses regulares. Quando o analisador lxico identifica algum texto da entrada casando com um padro, ele executa o cdigo C associado. A tentativa do casamento sempre gananciosa, isto , no caso de dois padres distintos casando a mesma entrada, o maior deles ser usado. O maior deles o que consome mais caracteres da entrada. Caso os padres ambguos consumam a mesma quantidade de caracteres, o padro definido antes escolhido. Por fim, a seo de subrotinas contm blocos de cdigo C que sero apenas copiados ao arquivo final. Assume-se que tal cdigo ser invocado a partir das regras da seo de regras. Em programas maiores, mais conveniente separar esse cdigo final noutro arquivo. 2.2.1 Implementando o autmato verificador de datas na ferramenta Lex Para implementar o autmato devemos primeiro criar a expresso regular. Usaremos o seguinte padro para implementar nosso autmato na ferramenta Lex:
%{ #include <iostream> %} %% <expresso regular> printf(<mensagem>); <expresso regular> printf(<mensagem>); <expresso regular> printf(<mensagem>); <...> %%

Resumo das expresses regulares: . => qualquer caractere em sua posio

[acz] => qualquer um dos caracteres, no caso: a, c ou z [a-d] => qualquer caracter do intervalo, no caso: a,b,c ou d [157] => 1,5 ou 7 [0-7] => 0,1,2,3,4,5,6 ou 7

[^Z] => qualquer caractere, exceto Z a* => "a" zero ou mais vezes a+ => "a" uma ou mais vezes a? => "a" zero ou uma vez a{5} => "a" cinco vezes a{5,} => "a" de cinco a infinito vezes a{,5} => "a" de zero a cinco vezes ^a => "a" no incio da linha a$ => "a" no final da linha .* => qualquer caractere Observaes:

As expresses regulares podem ser agrupadas formando outras expresses regulares mais complexas No esquea que o curinga conhecido ( * ), em expresses regulares, no significa qualquer caracter e sim o caracter anterior zero ou mais vezes. Em expresses regulares qualquer caracter representado pela expresso .*

A expresso regular pode ser dividida em vrias partes, pois a unio de vrias linguagens regulares uma linguagem regular. O cdigo final para o Lex ser:
%{ #include <stdio.h> %} %% [0-9][0-9][0-9][0-9][/][0][2][/]([0][1-9]|[1][0-9]|[2][0-8]) printf("Mes com 28 dias\n"); [0-9][0-9][0-9][0-9][/]([0][13578]|[1][02])[/]([0][1-9]|[1-2][0-9]|[3][0-1]) printf("Mes com 31 dias\n"); [0-9][0-9][0-9][0-9][/]([0][469]|[1][1])[/]([0][1-9]|[1-2][0-9]|[3][0]) printf("Mes com 30 dias\n"); .* printf("Data Invalida\n"); %%

3 REFERNCIAS AHO, A. V.; SETHI, R.; ULLMAN, J. D. Compiladores: princpios, tcnicas e ferramentas. So Paulo: Pearson Addison-Wesley,2008. 634p. ISBN 9788588639249 DIAS NETO, Samuel. Expresses Regulares. Disponvel em: <http://homepages.dcc.ufmg.br/~joaoreis/Site%20de%20tutoriais/tools/er.html>. Acesso em: 10 dez. 2011. HOPCROFT, John E.; ULLMAN, Jeffrey D.; MOTWANI, Rajeev. Introduo teoria de autmatos, linguagens e computao. Rio de Janeiro: Elsevier,2002. 560 p. ISBN 8535210725 HUBERT, Bert. Lex e YACC primer. Disponvel em: <http://lexyacc.codigolivre.org.br>. Acesso em: 10 dez. 2011.

Você também pode gostar