Upload
others
View
8
Download
0
Embed Size (px)
Citation preview
UNIVERSIDADE REGIONAL DE BLUMENAU
CENTRO DE CIÊNCIAS EXATAS E NATURAIS
CURSO DE CIÊNCIAS DA COMPUTAÇÃO
(Bacharelado)
EDITOR DE AUTÔMATOS FINITOS
TRABALHO DE CONCLUSÃO DE CURSO SUBMETIDO À UNIVERSIDADE REGIONAL DE BLUMENAU PARA A OBTENÇÃO DOS CRÉDITOS NA
DISCIPLINA COM NOME EQUIVALENTE NO CURSO DE CIÊNCIAS DA COMPUTAÇÃO — BACHARELADO
JOSIANE PATRÍCIA MORASTONI
BLUMENAU, NOVEMBRO/2002.
2002/2-40
ii
EDITOR DE AUTÔMATOS FINITOS
JOSIANE PATRÍCIA MORASTONI
ESTE TRABALHO DE CONCLUSÃO DE CURSO FOI JULGADO ADEQUADO PARA OBTENÇÃO DOS CRÉDITOS NA DISCIPLINA DE TRABALHO DE
CONCLUSÃO DE CURSO OBRIGATÓRIA PARA OBTENÇÃO DO TÍTULO DE:
BACHAREL EM CIÊNCIAS DA COMPUTAÇÃO
Profa. Joyce Martins - Orientadora na FURB
Prof. José Roque Voltolini da Silva - Coordenador do TCC
BANCA EXAMINADORA
Prof. Joyce Martins
Prof. Dalton Solano dos Reis
Prof. José Roque Voltolini da Silva
iii
AGRADECIMENTOS
Agradeço primeiramente a Deus pela oportunidade de realizar este trabalho, dando-me
esperança e força para seguir em frente nos momentos difíceis e também alegrias ao superar
os desafios. Um agradecimento especial àqueles que estiveram junto comigo e me apoiaram
em todos os momentos: meus pais Sérgio e Lúcia e meu namorado Fabiano.
Agradecimentos merecidos à minha orientadora, professora Joyce Martins, cujo
conhecimento na área e atenção para comigo, foram de grande importância para o
desenvolvimento desse trabalho.
iv
SUMÁRIO
LISTA DE FIGURAS ..............................................................................................................VI
LISTA DE QUADROS .......................................................................................................... VII
LISTA DE TABELAS ........................................................................................................... VII
LISTA DE ABREVIATURAS E SIGLAS ........................................................................... VIII
RESUMO .................................................................................................................................IX
ABSTRACT ..............................................................................................................................X
1 INTRODUÇÃO.....................................................................................................................1
1.1 CONTEXTUALIZAÇÃO / JUSTIFICATIVA...................................................................1
1.2 OBJETIVOS........................................................................................................................3
1.3 ORGANIZAÇÃO DO TRABALHO ..................................................................................3
2 FUNDAMENTAÇÃO TEÓRICA.........................................................................................4
2.1 ALFABETO, PALAVRA, LINGUAGEM E GRAMÁTICA ............................................4
2.2 CLASSIFICAÇÃO DAS LINGUAGENS..........................................................................6
2.2.1 PROPRIEDADES DAS LINGUAGENS REGULARES..............................................8
2.3 EXPRESSÃO REGULAR ..................................................................................................8
2.4 AUTÔMATOS FINITOS....................................................................................................9
2.4.1 AUTÔMATO FINITO COM MOVIMENTO VAZIO................................................11
2.4.2 AUTÔMATO FINITO NÃO-DETERMINÍSTICO ....................................................12
2.4.3 AUTÔMATO FINITO DETERMINÍSTICO ..............................................................13
2.4.4 EQUIVALÊNCIA ENTRE AFND E AFD..................................................................14
2.4.5 MINIMIZAÇÃO DE UM AUTÔMATO FINITO ......................................................16
2.5 IMPLEMENTAÇÕES DE AUTÔMATOS FINITOS......................................................17
3 DESENVOLVIMENTO DO EDITOR DE AUTÔMATOS FINITOS...............................20
3.1 ESPECIFICAÇÃO ............................................................................................................20
3.2 IMPLEMENTAÇÃO ........................................................................................................27
3.3 OPERACIONALIDADE DA IMPLEMENTAÇÃO........................................................29
4 CONSIDERAÇÕES FINAIS ..............................................................................................34
4.1 CONCLUSÕES.................................................................................................................34
4.2 DIFICULDADES ENCONTRADAS ...............................................................................34
4.3 EXTENSÕES ....................................................................................................................35
v
REFERÊNCIAS BIBLIOGRÁFICAS .....................................................................................36
APÊNDICE 1 ...........................................................................................................................37
vi
LISTA DE FIGURAS
FIGURA 2.1 – HIERARQUIA DE CHOMSKY.......................................................................6
FIGURA 2.2 – REPRESENTAÇÃO DO AUTÔMATO FINITO...........................................10
FIGURA 2.3 – DIAGRAMA DE TRANSIÇÃO DE UM AUTÔMATO FINITO .................11
FIGURA 2.4 – AUTÔMATO FINITO COM MOVIMENTO VAZIO...................................12
FIGURA 2.5 – AUTÔMATO FINITO NÃO DETERMINÍSTICO ........................................13
FIGURA 2.6 – AUTÔMATO FINITO DETERMINÍSTICO..................................................13
FIGURA 2.7 – AFD CONTRUÍDO A PARTIR DE UM AFND............................................16
FIGURA 2.8 – AUTÔMATO FINITO PARA 01(0+1)*.........................................................17
FIGURA 3.1 – TE-001: INTERFACE DO EDITOR DE AUTÔMATOS FINITOS.............21
FIGURA 3.2 – DIAGRAMA DE CLASSES...........................................................................24
FIGURA 3.3 – DIAGRAMA DE SEQÜÊNCIA DA VERIFICAÇÃO DAS
PROPRIEDADES ..........................................................................................25
FIGURA 3.4 – DIAGRAMA DE SEQÜÊNCIA DO RECONHECIMENTO DE PALAVRAS
........................................................................................................................26
FIGURA 3.5 – VALIDAÇÃO DE UM AUTÔMATO FINITO NÃO DETERMINÍSTICO .30
FIGURA 3.6 – VALIDAÇÃO DE UM AUTÔMATO FINITO DETERMINÍSTICO ...........31
FIGURA 3.7 – RECONHECIMENTO DIRETO DE UMA PALAVRA................................32
FIGURA 3.8 – RECONHECIMENTO DA PALAVRA 011 PASSO A PASSO....................33
vii
LISTA DE QUADROS
QUADRO 2.1 – PASSO A DA TRANSFORMAÇÃO DO AFND PARA AFD....................14
QUADRO 2.2 – PASSO B DA TRANSFORMAÇÃO DO AFND PARA AFD....................15
QUADRO 2.3 – PASSO C DA TRANSFORMAÇÃO DO AFND PARA AFD....................15
QUADRO 2.4 – ALGORITMO ESPECÍFICO........................................................................18
QUADRO 2.5 – ALGORITMO GENÉRICO..........................................................................19
QUADRO 3.1 – CASO DE USO: DESENHAR AUTÔMATO..............................................20
QUADRO 3.2 – CASO DE USO: VERIFICAR PROPRIEDADES.......................................22
QUADRO 3.3 – CASO DE USO: RECONHECER PALAVRA ............................................22
QUADRO 6.1 – CÓDIGOS FONTES DA VERIFICAÇÃO DAS PROPRIEDADES...........37
QUADRO 6.2 – CÓDIGOS FONTES DO RECONHECIMENTO DE PALAVRA..............37
LISTA DE TABELAS
TABELA 2.1 – EXEMPLOS DE EXPRESSÕES REGULARES.............................................9
TABELA 2.2 – TABELA DE TRANSIÇÃO DE UM AUTÔMATO FINITO.......................11
TABELA 2.3 – TABELA DO ALGORITMO DE IMPLEMENTAÇÃO GENÉRICA..........19
viii
LISTA DE ABREVIATURAS E SIGLAS
AFD - Autômato Finito Determinístico
AFND - Autômato Finito não-determinístico
AFε - Autômato Finito com Movimento vazio
ASCII - American Standard Code for Information Interchange
ER - Expressão Regular
MDI - Multiple Document Interface
RAD - Desenvolvimento Rápido de Aplicações
UML - Unified Modeling Language
ix
RESUMO
Este trabalho apresenta o desenvolvimento de um editor de autômatos finitos a ser
utilizado pelos acadêmicos da disciplina de Linguagens Formais com o objetivo de facilitar a
compreensão da teoria estudada. O acadêmico poderá construir autômatos finitos
determinísticos ou não-determinísticos, sendo capaz de validá-los e efetuar o reconhecimento
de palavras. Na validação de um autômato, o editor retorna as propriedades identificadas. No
reconhecimento de palavras, tem-se duas opções: o reconhecimento direto, que indica
somente se a palavra foi aceita ou não, e o reconhecimento passo a passo, que permite que o
acadêmico acompanhe a análise de uma palavra a cada símbolo reconhecido.
x
ABSTRACT
This work presents the development of an editor of finite automatons to be used by
Formal Language academics in which the objective is to facilitate the comprehension of the
studied theory. The academic in this editor will construct deterministic or nondeterministic
finite automatons, and will be able to validate and recognize words through it. In
the automaton validation the editor returns the automaton properties. In the recognition of the
word there are two options; (1) the direct identification, only returning the word is accepted or
not; (2) and the step by step identification, which permits the student to follow the analysis of
a word in each recognized symbol.
1
1 INTRODUÇÃO
1.1 CONTEXTUALIZAÇÃO / JUSTIFICATIVA
Na década de 1950 originou-se a Teoria das Linguagens Formais com o objetivo de
desenvolver teorias relacionadas com as linguagens naturais. Foi verificado que esta teoria era
importante para o estudo de linguagens artificiais e, em especial, para as linguagens
originárias na Ciência da Computação, mais especificamente as linguagens de programação.
Uma linguagem de programação é uma das ferramentas usadas para o
desenvolvimento de determinada aplicação. A escolha da mesma deve ser feita objetivando a
construção de programas legíveis, confiáveis e com baixo custo de compilação.
Estudar os conceitos de linguagens, conforme Sebesta (2000), visa permitir que:
a) o acadêmico aumente sua capacidade de expressar idéias visto que, conhecendo
mais recursos e linguagens, adquire aos poucos experiência para estruturar e
desenvolver melhor um programa;
b) o acadêmico adquira maior conhecimento para escolher a linguagem mais
apropriada a um domínio de problemas;
c) o acadêmico tenha maior facilidade no aprendizado de novas linguagens;
d) o acadêmico entenda melhor a importância da implementação;
e) o acadêmico possa projetar novas linguagens ou estender linguagens existentes,
implementando compiladores eficientes.
As disciplinas na área de compiladores são, de uma forma geral, o ponto de partida
para o entendimento das linguagens de programação. No currículo do curso de Ciências da
Computação da FURB, há disciplinas como Teoria da Computação, Linguagens Formais e
Compiladores, onde são estudados os conceitos e as ferramentas da Teoria da Computação
que podem ser usados para especificar linguagens de programação e construir compiladores.
Um compilador é composto por vários módulos, sendo o primeiro o analisador léxico.
Na implementação do analisador léxico são utilizados autômatos finitos, ou seja,
reconhecedores de linguagens. Conforme Aho (1995), um reconhecedor para uma linguagem
é um programa que toma como entrada uma seqüência x de símbolos e responde sim, se x for
uma palavra da linguagem e não, em caso contrário. No caso de autômatos finitos, o
reconhecimento é feito por uma máquina de estados capaz de observar apenas um símbolo de
2
cada vez. A palavra finito é incluída no nome para ressaltar que um autômato só pode conter
uma quantidade limitada de informações a qualquer momento. Tais informações são
representadas por um conjunto finito de estados e de transições entre esses.
Um autômato finito pode ser representado diagramaticamente por um grafo dirigido e
rotulado, chamado de diagrama de transições, no qual os nós são os estados e os lados
rotulados com os símbolos do alfabeto de entrada, determinam o próximo estado em função
do estado atual e do símbolo de entrada. Há um estado inicial, chamado de estado de partida,
e um ou mais estados, denominados estados finais, que indicam onde o autômato deve parar
no processo de reconhecimento para que uma seqüência de símbolos de entrada seja aceita.
Essa diagramação pode ser usada para representar tanto autômatos finitos não-determinísticos
(AFND) quanto autômatos finitos determinísticos (AFD).
Em um AFND mais de uma transição de estado pode ser possível para o mesmo
símbolo de entrada a partir de um único estado. Já em um AFD existe no máximo uma
transição de estado definida para cada símbolo a partir de um único estado. Os dois são
capazes de reconhecer precisamente linguagens simples classificadas como regulares, mas
existem diferenças. Por exemplo, os autômatos finitos determinísticos podem especificar
reconhecedores mais rápidos do que os não-determinísticos. No entanto, podem possuir mais
estados e transições, sendo mais complexos do que autômatos finitos não-determinísticos
equivalentes.
Autômatos finitos são os mais simples reconhecedores de linguagens. No entanto,
durante estes anos cursando Ciências da Computação, constatou-se que os acadêmicos
apresentam dificuldade no aprendizado dessa teoria. Assim, este trabalho apresenta o
desenvolvimento de um Editor de Autômatos Finitos em função de não existir uma ferramenta
adequada que auxilie a prática do conteúdo lecionado sobre esse assunto nas disciplinas de
Linguagens Formais e de Compiladores. Com a ferramenta proposta, os acadêmicos serão
capazes de construir e testar autômatos finitos determinísticos e não-determinísticos,
possibilitando a validação dos mesmos. O desenvolvimento de uma ferramenta desse tipo visa
não só motivar a compreensão dos conceitos envolvidos, como permitir que os acadêmicos
possam relacionar teoria e prática.
3
1.2 OBJETIVOS
O objetivo deste trabalho é desenvolver um editor gráfico de autômatos finitos para
uso como ferramenta de apoio nas disciplinas de Linguagem Formais e de Compiladores do
curso de Ciências da Computação da FURB.
Os objetivos específicos do trabalho são:
a) especificar autômatos finitos para linguagens regulares através de diagramas de
transições;
b) tratar autômatos finitos determinístico (AFD) e autômatos finitos não-
determinísticos (AFND);
c) permitir o reconhecimento de palavras passo a passo;
d) verificar as propriedades dos autômatos especificados, tais como estados mortos,
estados inalcançáveis, entre outras.
1.3 ORGANIZAÇÃO DO TRABALHO
O trabalho está organizado em quatro capítulos. O capítulo seguinte apresenta os
conceitos básicos da teoria de linguagens formais desde alfabetos, palavras, linguagens e
gramáticas, seguindo um caminho mais específico até chegar na definição de autômatos
finitos. Os autômatos finitos são classificados em tipos, sendo explicadas as diferenças e as
formas de tratamento e de implementação.
No capítulo três é apresentado o desenvolvimento do editor de autômatos finitos sendo
que a especificação, a implementação e o funcionamento da ferramenta desenvolvida são
relatados. Por fim, no capítulo quatro encontram-se as considerações finais onde são
apresentadas as conclusões provenientes da execução desse trabalho e as dificuldades
encontradas, bem como as possíveis extensões que podem ser desenvolvidas.
4
2 FUNDAMENTAÇÃO TEÓRICA
2.1 ALFABETO, PALAVRA, LINGUAGEM E GRAMÁTICA
Na teoria de linguagens formais, alfabeto é um conjunto não vazio onde os elementos
são chamados de símbolos. Segundo Rangel (2001), “os conceitos de símbolo e alfabeto são
inseridos de forma interdependente, ou seja, um alfabeto é um conjunto de símbolos e um
símbolo é um elemento qualquer de um alfabeto”.
O alfabeto deve ser escolhido dependendo do contexto que se é trabalhado. Assim,
para cada aplicação específica, o usuário escolhe o alfabeto que pretende utilizar. Por
exemplo, para ensinar a linguagem Pascal, segundo Rangel (2001), o alfabeto escolhido
deverá conter símbolos como program, begin, end, if, then, else. Já para implementar esta
linguagem, o alfabeto terá novos símbolos do conjunto ASCII, tais como letras, dígitos, +, *,
entre outros, sendo que esses elementos serão os componentes básicos dos programas fontes.
Os símbolos de um alfabeto são utilizados para compor palavras. Palavra, cadeia ou
sentença pode ser definida como uma seqüência finita de símbolos justapostos ou,
formalmente, palavra é uma função que determina uma seqüência s de comprimento1 n no
alfabeto ∑∑∑∑, isto é, s:[n]→ ∑ tem domínio [n] e contra-domínio ∑. Por exemplo, se o alfabeto
for considerado ∑ = {0,1}, a seqüência s = 0101 de comprimento 4 pode ser vista como
função s: [4] → ∑, definida por s(1) = 0, s(2) = 1, s(3) = 0, s(4) = 1.
Segundo Menezes (1998), palavras podem ser concatenadas para formar novas
palavras. A concatenação é uma operação binária que associa a cada par de palavras, uma
palavra formada pela justaposição da primeira com a segunda. Formalmente, a concatenação
de duas seqüências x:[m] → ∑ e y: [n] → ∑, de comprimentos m e n, no mesmo alfabeto ∑, é
definida como a seqüência xoy: [m+n] → ∑, de comprimento m+n. Assim, se ∑ = {0,1}, x =
0110 e y = 011, a concatenação das palavras x e y é representada por xoy e é igual a 0110011.
Uma operação definida sobre a concatenação é o elemento neutro à esquerda e à
direita, ou seqüência vazia. É o caso quando n = 0, que é representado por ε. A seqüência
vazia é o elemento neutro (identidade) da concatenação, por exemplo, qualquer que seja a
1 Comprimento de uma palavra é o número de símbolos que compõe a palavra e pode ser representado por |s|.
5
seqüência x, será: x o ε = ε o x = x, ou seja, para ∑={0,1} e x = 01, tem-se que 01 o ε = ε o 01
= 01.
A concatenação sucessiva é representada na forma de expoente, onde o expoente
indica o número de concatenações sucessivas. Por exemplo, 15 = 11111, onde o símbolo é
repetido conforme o expoente.
O conjunto de todas as seqüências que podem ser formadas com os símbolos de um
alfabeto ∑ é uma linguagem, incluindo também a seqüência vazia ε. Portanto, uma linguagem
L em ∑ é um subconjunto de ∑*, ou seja, L ⊆ ∑*. Assim, {0101, 0100, 01, ...} é a linguagem
em {0,1}* composta por todas as palavras de comprimento maior ou igual a dois que iniciam
com 01.
Visto que linguagens são conjuntos, então as operações de conjuntos podem ser usadas
com as linguagens. Logo, se L1 e L2 são linguagens em ∑*, então:
a) a união de L1 com L2 é representada por L1 ∪ L2 e é igual ao conjunto {x | x ∈ L1
ou x ∈ L2}
b) a interseção de L1 com L2 é representada por L1 ∩ L2 e é igual ao conjunto {x | x ∈
L1 e x ∈ L2}
c) a diferença entre L1 e L2 é representada por L1 – L2 e é igual ao conjunto {x | x ∈
L1 e x ∉ L2}
d) o complemento de L1 é representado por ∑* - L1 e é igual ao conjunto {x | x ∈ ∑*
e x ∉ L1}
De acordo com Menezes (1998), gramática é um formalismo que permite gerar todas
as palavras de uma linguagem. Uma gramática é composta por regras de produção, sendo
possível obter todos elementos da linguagem a partir de um símbolo inicial. Formalmente,
uma gramática é uma quádrupla ordenada G=(V, T, P, S), onde:
a) V é um conjunto finito de símbolos não-terminais;
b) T é um conjunto finito de símbolos terminais disjunto de V;
c) P é um conjunto finito de pares (α, β), denominados regras de produção, tal que o
primeiro componente é uma palavra em (V ∪ T)+ e o segundo componente é
uma palavra em (V ∪ T)*;
d) S é o elemento de V denominado símbolo inicial.
6
Uma regra de produção (α, β) é representada por α → β. Ela define as condições de
geração das palavras da linguagem. Uma seqüência de regras pode ser representada por α →
β1 | β2 | ... | βn .
Logo, para a gramática G= (V, T, P, S) = ({S,X}, {0,1}, {(S,01X), (X,ε), (X,0X),
(X,1X)}, S) tem-se que V = {S,X} é o conjunto de não-terminais, T = {0,1} é o conjunto de
terminais, e P = {S→01X, X→ε | 0X |1X} é o conjunto de regras de produção para gerar as
palavras de comprimento maior ou igual a dois que iniciam com 01.
A aplicação de uma regra de produção é denominada derivação da palavra. Derivação
é uma substituição de uma subpalavra de acordo com uma regra de produção. Abaixo segue a
definição dos passos de derivação:
a) ⇒* fecho transitivo e reflexivo da relação, ou seja, zero ou mais passos de
derivações sucessivas;
b) ⇒+ fecho transitivo da relação, isto é, um ou mais passos de derivações sucessivas;
c) ⇒i exatos i passos de derivações sucessivas, onde i é um número natural.
2.2 CLASSIFICAÇÃO DAS LINGUAGENS
As linguagens podem ser estudadas conforme a hierarquia de Chomsky, ilustrada na
fig. 2.1, que definiu estas classes como modelos para linguagens naturais.
FIGURA 2.1 – HIERARQUIA DE CHOMSKY
Fonte: Menezes (1998)
Linguagens regulares ou tipo 3 surgiram de estudos biológicos de redes de neurônios e
circuitos de chaveamento. Podem ser geradas pelas gramáticas regulares ou tipo 3 e são
7
reconhecidas pelos autômatos finitos. Conforme Price (2001), fornecendo uma palavra a um
autômato, este é capaz de responder se a mesma pertence à linguagem que ele representa. Os
algoritmos geradores e os reconhecedores são de grande eficiência e fácil implementação.
Segundo Lewis (2000), têm como principal característica o tempo de reconhecimento de uma
palavra, que é proporcional ao comprimento da entrada. São usados para desenvolver
analisadores léxicos, parte de um compilador que identifica e codifica as unidades básicas de
uma linguagem, como por exemplo identificadores e números. Outras aplicações são: editores
de texto, sistemas de pesquisa e atualização de arquivos, interface de sistemas operacionais,
protocolos de comunicação, entre outros.
Já as linguagens livres de contexto ou tipo 2 compreendem um universo mais amplo
de linguagens, se comparado com as regulares, tratando adequadamente construções bloco-
estruturadas, entre outras, típicas de linguagens de programação como Pascal e C. São geradas
pelas gramáticas livres de contexto ou tipo 2 e reconhecidas por autômatos com pilha, usados
para o desenvolvimento de analisadores sintáticos, tradutores de linguagens e processadores
de textos em geral. Esses algoritmos reconhecedores e geradores são relativamente simples e
possuem uma boa eficiência. Observa-se que o tempo de reconhecimento de uma palavra é
proporcional ao dobro do tamanho da entrada, conforme Lewis (2000).
As linguagens sensíveis ao contexto e as enumeráveis recursivamente (irrestritas)
permitem explorar os limites da capacidade de desenvolvimento de reconhecedores ou
geradores de linguagens. Estudam a solução do problema da existência de algum
reconhecedor ou gerador para determinada linguagem.
As linguagens sensíveis ao contexto podem ser geradas pelas gramáticas sensíveis ao
contexto que são assim definidas pelo fato de que o lado esquerdo das produções da gramática
pode conter símbolos não-terminais ou terminais definindo o contexto de derivação. São
reconhecidas pela máquina de Turing com fita limitada, ou seja, o tamanho da fita é finito.
As linguagens enumeráveis recursivamente (irrestritas) representam o conjunto de
todas as linguagens que podem ser reconhecidas ou compiladas mecanicamente e em um
tempo finito. São reconhecidas por uma máquina de Turing que, segundo Menezes (1998), é o
mais geral dispositivo de computação. Usa um gerador na forma de gramática, que é
denominado gramática irrestrita, ou seja, que não possui restrição sobre a forma das
produções.
8
2.2.1 PROPRIEDADES DAS LINGUAGENS REGULARES
Uma das características das linguagens regulares é a facilidade para representá-las.
Essa facilidade já demonstra que é uma classe restrita e limitada. Para representar uma
linguagem regular são usados formalismos como gramática regular, expressão regular e
autômato finito.
Para determinar se uma linguagem é regular ou não, pode ser usado o lema do
bombeamento para as linguagens regulares, esclarecendo as propriedades desta linguagem. As
idéias do lema do bombeamento são as seguintes:
a) se uma linguagem é regular, então ela é aceita por um autômato finito
determinístico, que tem um número finito e predefinido de n estados;
b) se o autômato reconhece uma palavra de entrada w, de comprimento maior ou igual
a n, o autômato assume algum estado q por mais de uma vez, criando um ciclo na
função programa que passa por q;
c) assim w pode se dividir em três subpalavras, w = uvz, tal que |uv| ≤ n, |v| ≥ 1, onde
v é a parte de w que é reconhecida pelo ciclo. Portanto, uviz para i ≥ 0, é sempre
aceito pelo autômato.
Após ter analisado se a linguagem é regular, pode-se investigar se é uma linguagem
regular vazia, finita ou infinita através de um algoritmo. Se L é uma linguagem regular aceita
por um autômato finito M = (Σ, Q, δ, qo, F) com n estados, então L é:
a) vazia, se M não aceita qualquer palavra w tal que |w| < n ;
b) finita, se M não aceita uma palavra w tal que n ≤ |w| < 2n;
c) infinita, se M aceita uma palavra w tal que n ≤ |w| < 2n.
2.3 EXPRESSÃO REGULAR
Toda linguagem regular pode ser descrita por uma expressão simples, denominada
expressão regular. Uma expressão regular é definida a partir de conjuntos (linguagens básicas)
e operações de concatenação e união. É utilizada para facilitar a comunicação entre homens e
homem X máquina.
Uma expressão regular (ER) no alfabeto ∑ é definida pelas seguintes regras:
a) ∅ denota a linguagem vazia;
9
b) ε denota a linguagem contendo a palavra vazia {ε};
c) qualquer símbolo x pertencente a ∑ denota a linguagem contendo a palavra
unitária {x};
d) se x e y são ER então:
d.1) (x + y) denota a linguagem X ∪ Y;
d.2) (xy) denota a linguagem XY = {uv | u ∈ X e v ∈ Y};
d.3) (x*) denota a linguagem X*.
Uma linguagem gerada por uma ER r é representada por L(r) ou GERA(r). A tabela
abaixo mostra algumas expressões regulares e as linguagens denotadas.
TABELA 2.1 – EXEMPLOS DE EXPRESSÕES REGULARES EXPRESSÃO REGULAR LINGUAGEM REPRESENTADA
00 Somente a palavra 00
10* Todas as palavras que iniciam com 1, seguido por zero ou mais 0s
(0+1)*00(0+1)* Todas as palavras contendo 00 como subpalavra
(0+ε)(1+10)* Todas as palavras que não possuam dois 0s consecutivos
(01)(0+1)* Todas as palavras que iniciam com 01
2.4 AUTÔMATOS FINITOS
O autômato finito ou máquina de estados finitos recebe como entrada uma palavra,
através de uma fita de entrada, e não produz nenhuma saída, exceto se a entrada foi aceita ou
não. É um dispositivo de reconhecimento de linguagens regulares.
Permite projetar vários tipos comuns de máquinas, algoritmos e programas. Por
exemplo, a fase de análise léxica de um compilador, onde unidades de programas como begin
e + são identificadas, é freqüentemente baseada na simulação de um autômato finito. Um
autômato finito pode ser dividido em três partes: fita de entrada, cabeça de leitura e unidade
de controle, como mostra a fig. 2.2 abaixo.
a) fita de entrada: divide-se em quadros, com um símbolo inscrito em cada quadro.
Contém a informação a ser processada;
b) unidade de controle: é a parte principal da máquina, que define o estado da
máquina em função do símbolo escrito em qualquer posição na fita de entrada;
c) cabeça de leitura: é o meio por onde a unidade de controle lê qualquer posição na
fita de entrada.
10
FIGURA 2.2 – REPRESENTAÇÃO DO AUTÔMATO FINITO
Fonte: Lewis (2000)
Inicialmente, a cabeça de leitura está posicionada no quadro mais à esquerda da fita e a
unidade de controle tem configurado um estado inicial. O autômato lê um símbolo da fita de
entrada e entra em um novo estado a partir do estado atual e do símbolo que acabou de ser
lido. Após a leitura de um símbolo de entrada, a cabeça de leitura movimenta um quadro para
a direita na fita de entrada de modo que no próximo movimento, ela lerá o símbolo no
próximo quadro da fita. Esse processo é realizado até que a cabeça de leitura chegue no final
da palavra de entrada ou até ocorrer uma condição de parada.
O autômato indica sua aprovação ou não quanto ao que leu, baseado no estado em que
se encontra no fim. Se o resultado é um estado do conjunto de estados finais, a palavra de
entrada foi aceita. A linguagem aceita pela máquina é o conjunto de palavras que ela
reconhece.
A representação de um autômato finito pode ser feita por grafos dirigidos e também
através de tabelas de transição. Na representação por grafos, os estados são os nós e as
transições são as arestas rotuladas com os símbolos do alfabeto de entrada. O estado inicial
tem uma seta que antecede o mesmo e os estados finais são representados por nós com uma
borda dupla. Na fig. 2.3, tem-se um autômato finito representado por diagrama de transição.
11
FIGURA 2.3 – DIAGRAMA DE TRANSIÇÃO DE UM AUTÔMATO FINITO
A tabela de transição é uma matriz onde as linhas representam os estados do autômato
e as colunas os símbolos do alfabeto. Na primeira coluna são descritos todos os estados
pertencentes ao autômato finito. Já nas demais colunas, baseado na primeira linha onde estão
descriminados os símbolos, ter-se-á o próximo estado quando o símbolo lido for o da
respectiva coluna, se houver transição. Abaixo segue um exemplo da tabela de transição
correspondente ao autômato da fig. 2.3. Nota-se que na tabela de transição o estado inicial é
indicado por uma seta e os estados finais pelo símbolo *.
TABELA 2.2 – TABELA DE TRANSIÇÃO DE UM AUTÔMATO FINITO δ 0 1
→ q0 q1 -
q1 - qf1
* qf1 qf1 qf1
Os autômatos finitos podem ser classificados em três tipos: autômatos finitos com
movimento vazio (AFε), autômatos finitos não-determinísticos (AFND) e autômatos finitos
determinísticos (AFD).
2.4.1 AUTÔMATO FINITO COM MOVIMENTO VAZIO
Um movimento vazio é uma transição sem leitura de símbolo na fita. Pode ser
representado como não-determinismo interno, ou seja, executa-se por uma eventual alteração
de estados. Conforme Menezes (1998), uma das vantagens dos autômatos finitos com
movimento vazio é o fato de facilitar as construções e demonstrações relacionadas com os
autômatos.
A facilidade de movimentos vazios não aumenta o poder de reconhecimento de
linguagens pelos autômatos finitos. Um autômato finito não-determinístico e com movimento
vazio ou um autômato finito com movimento vazio M é uma quíntupla M= (Σ, Q, δ, q0, F),
onde:
12
a) Σ é o alfabeto de símbolos de entrada;
b) Q é o conjunto finito de estados possíveis do autômato;
c) δ é a função programa ou função de transição, tal que δ pode ser uma função
parcial com domínio Q x (Σ ∪ {ε}) e contra-domínio 2Q;
d) q0 é o estado inicial, tal que q0 é elemento de Q;
e) F é o conjunto de estados finais, tal que F está contido em Q.
Um movimento vazio é representado pela aplicação da função programa em um estado
q ao símbolo especial ε, obtendo-se δ(q, ε).
O processamento de um AFε é similar ao de um AFND, sendo que tem a mais é uma
transição para uma entrada vazia que é não-determinística. Na fig. 2.4 é apresentado um AFε
que reconhece a linguagem especificada nas seções anteriores.
FIGURA 2.4 – AUTÔMATO FINITO COM MOVIMENTO VAZIO
2.4.2 AUTÔMATO FINITO NÃO-DETERMINÍSTICO
O AFND ao processar um símbolo da entrada a partir do estado corrente, pode ter
como resultado um conjunto de novos estados. Nesse caso, o AFND assume o conjunto de
caminhos alternativos como se tivesse uma multiplicação da unidade de controle, uma para
cada alternativa, processando os vários caminhos individualmente sem compartilhar recursos.
O processamento de um caminho não influi em um estado, símbolo lido e posição da cabeça
de leitura dos demais caminhos alternativos.
Um AFND M é uma quíntupla M = (∑, Q, δ, q0, F), onde:
a) ∑ é o alfabeto de símbolos de entrada;
b) Q é o conjunto finito de estados possíveis do autômato;
c) δ é a função programa ou função transição, tal que δ pode ser uma função parcial
com domínio Q x ∑ e contra-domínio 2Q;
13
d) q0 é o estado inicial, tal que q0 é elemento de Q;
e) F é o conjunto de estados finais, tal que F está contido em Q.
O autômato finito da fig. 2.5 é um AFND equivalente ao AFε da fig. 2.4.
FIGURA 2.5 – AUTÔMATO FINITO NÃO DETERMINÍSTICO
2.4.3 AUTÔMATO FINITO DETERMINÍSTICO
Ao processar um símbolo da entrada a partir do estado corrente, o AFD pode assumir
um único estado, ou seja, só existe um caminho para processar a palavra de entrada.
Um AFD é uma quíntupla M = (Σ, Q, δ, q0 , F ), onde:
a) Σ é o alfabeto de símbolos de entrada;
b) Q é o conjunto finito de estados possíveis do autômato;
c) δ é a função programa ou função de transição, tal que δ pode ser uma função
parcial com domínio Q x Σ e contra-domínio Q;
d) q0 é o estado inicial, tal que q0 é elemento de Q;
e) F é o conjunto de estados finais, tal que F está contido em Q.
Tem-se na fig. 2.6 um autômato finito determinístico que representa a linguagem
citada nas seções anteriores.
FIGURA 2.6 – AUTÔMATO FINITO DETERMINÍSTICO
14
2.4.4 EQUIVALÊNCIA ENTRE AFND E AFD
A classe dos AFD é equivalente a classe dos AFND, ou seja, pode-se converter um
AFND em um AFD equivalente. Será apresentado um algoritmo para fazer essa conversão. A
idéia central deste algoritmo é a construção de um AFD M´ com estados que simulem as
diversas combinações de estados alternativos de um AFND M, ou seja, M’ simula M.
Seja M = (Σ, Q, δ, q0, F) um AFND qualquer, M’ = (Σ, Q’, δ’, <q0>, F’) é um AFD
construído a partir do mesmo M como segue:
a) Q’ é o conjunto de todas as combinações, sem repetições de estados de Q denotadas
por <q1, q2, ..., qn>, sendo que a ordem dos elementos não distingue mais
combinações;
b) em δ’, um estado de M’ representa uma imagem dos estados de todos os caminhos
alternativos de M;
c) <q0> é o estado inicial;
d) F’ é o conjunto de todos os estados pertencentes a Q’, tal que qi pertence a F, para i
em {1,2, ..., n}.
O algoritmo abaixo apresenta em passos a transformação do AFND representado na
fig. 2.5, em um AFD equivalente. Após ter construído a tabela de transição do autômato finito
não-determinístico, deve-se:
a) identificar a primeira linha, ou a que contiver o estado inicial, da tabela de
transições do AFND e atribuir à tabela de transições do AFD, como adverte as
tabelas apresentadas no quadro 2.1;
QUADRO 2.1 – PASSO A DA TRANSFORMAÇÃO DO AFND PARA AFD
AFND AFD δ 0 1
→→→→ q0 q1 , q2 -
q1 - qf1
q2 - q2 ,qf1
* qf1 qf1 qf1
δ 0 1
→→→→ <q0> <q1 q2> -
15
b) verificar para cada estado do AFD se todas transições foram criadas. Em caso
negativo, atribuir na primeira coluna os estados, possivelmente conjuntos, que
reconhecem os símbolos indicados. Repetir esse passo até que todas as transições
tenham sido criadas. O quadro a seguir apresenta o passo b do processo de
transformação do AFND da fig. 2.5 em AFD;
QUADRO 2.2 – PASSO B DA TRANSFORMAÇÃO DO AFND PARA AFD
AFND AFD
c) determinar a primeira linha da tabela de transição criada como sendo o estado
inicial do AFD e as linhas que contiverem qf no conjunto de estados como sendo
estados finais, como indicado no quadro 2.3.
QUADRO 2.3 – PASSO C DA TRANSFORMAÇÃO DO AFND PARA AFD
AFD δ 0 1
→→→→ <q0> <q1 q2> -
<q1 q2> - <q2 qf1>
* <q2 qf1> <qf1> <q2 qf1>
* <qf1> <qf1> <qf1>
A fig.2.7 abaixo mostra o autômato finito determinístico equivalente ao autômato
finito não determinístico da fig. 2.5 resultante da aplicação do algoritmo de transformação
descrito.
δ 0 1
→→→→ q0 q1 , q2 -
q1 - qf1
q2 - q2 ,qf1
* qf1 qf1 qf1
δ 0 1
→→→→ <q0> <q1 q2> -
<q1 q2> - <q2 qf1>
<q2 qf1> <qf1> <q2 qf1>
<qf1> <qf1> <qf1>
16
FIGURA 2.7 – AFD CONTRUÍDO A PARTIR DE UM AFND
2.4.5 MINIMIZAÇÃO DE UM AUTÔMATO FINITO
O objetivo de minimizar um autômato finito é gerar outro equivalente com o menor
número possível de estados. Em algumas aplicações, a minimização do menor número de
estados não implica necessariamente no menor custo de implementação. O autômato finito
mínimo é único. Assim, quando dois autômatos distintos aceitam a mesma linguagem, ao
serem minimizados geram o mesmo autômato mínimo, diferenciando-se na identificação dos
estados.
O autômato finito para ser minimizado deve atender os seguintes pré-requisitos:
a) deve ser determinístico;
b) não pode ter estados inacessíveis, ou seja, estados que não podem ser alcançados a
partir do estado inicial;
c) a função programa deve ser total, isto é, a partir de qualquer estado devem ser
previstas transições para todos os símbolos do alfabeto.
O algoritmo para minimização de autômatos finitos determinísticos segue os seguintes
passos:
a) elimina os estados inacessíveis e suas correspondentes transições;
b) altera a função programa em total, introduzindo um estado não final Φ e incluindo
Φ como estado destino das transições não previstas;
c) unifica os estados equivalentes, sendo que dois estados são ditos equivalentes se e
somente se para qualquer seqüência de símbolos resultam simultaneamente em
estados finais ou estados não-finais também equivalentes entre si;
d) elimina os estados mortos, ou seja, estados que não são finais e a partir deles não é
possível atingir um estado final.
17
A ferramenta desenvolvida não gera um autômato finito mínimo a partir de outro
especificado, apenas identifica estados inacessíveis e estados mortos. Assim, o algoritmo para
minimização de autômatos finitos não será apresentado, mas encontra-se em Menezes (1998).
2.5 IMPLEMENTAÇÕES DE AUTÔMATOS FINITOS
Uma das aplicações dos autômatos finitos é o desenvolvimento de analisadores
léxicos. O analisador léxico é responsável pelo reconhecimento dos tokens, ou seja, faz a
leitura dos caracteres da entrada, agrupando em palavras, que são classificadas em categorias,
tais como identificadores, símbolos especiais, constantes, comentários, entre outras.
Segundo Martins (2002), para construir o analisador é preciso primeiramente
descrever precisamente a regra de formação dos tokens, usando expressões regulares. Depois
deve-se converter as expressões regulares em autômatos finitos determinísticos mínimos
correspondentes e, por fim, implementar os autômatos em uma linguagem qualquer. Um
autômato finito pode ser implementado de maneira específica ou genérica.
Assim, a construção de um reconhecedor para a linguagem em {0,1}* composta por
todas as palavras de comprimento maior ou igual a dois constituída por 01 e seguidos por
nenhum ou vários 0s ou 1s, envolve:
a) a especificação de uma ER: 01(0+1)*;
b) a definição do AFD mínimo correspondente à expressão regular definida mostrado
na fig. 2.8;
FIGURA 2.8 – AUTÔMATO FINITO PARA 01(0+1)*
c) a implementação, de maneira específica exemplificada no quadro 2.4, ou genérica,
apresentada no quadro 2.5.
18
QUADRO 2.4 – ALGORITMO ESPECÍFICO ALGORITMO especifico VARIÁVEIS
STRING : palavra INTEIRO: posição
INÍCIO LEIA (palavra) posição ← 0 q0:SE posição < tamanho (palavra) ENTÃO INC(posição) SE palavra[posição] = '0' ENTÃO GOTO q1 SENÃO GOTO erro FIMSE SENÃO GOTO erro FIMSE q1:SE posição < tamanho (palavra) ENTÃO INC(posição) SE palavra[posição] = '1' ENTÃO GOTO q f SENÃO GOTO erro FIMSE SENÃO GOTO erro FIMSE qf :SE posição < tamanho (palavra) ENTÃO INC(posição) SE palavra[posição] = '0' OU palavra[posição] = '1' ENTÃO GOTO q f
FIMSE SENÃO ESCREVA ('palavra reconhecida') GOTO fim FIMSE erro: ESCREVA ('palavra não reconhecida') fim:
FIM
A implementação específica pode ser usada quando o autômato finito a ser
implementado, é sempre o mesmo. Neste trabalho, o acadêmico irá construir os autômatos
finitos a serem analisados. Neste caso, considera-se mais adequado implementar os autômatos
finitos especificados de maneira genérica utilizando, para o reconhecimento de palavras, o
algoritmo apresentado no quadro 2.5.
19
QUADRO 2.5 – ALGORITMO GENÉRICO ALGORITMO genérico(palavra, estado: STRING; posição : INTEIRO): LÓGICO; VARIÁVEIS CARACTER: símbolo INTEIRO : índice BOOLEAN : achou TABELA : transição {Esta tabela contém os campos: estado atual, símbolo lido, próximo estado} INÍCIO SE (COPIA(estado,1,2) = 'qf') E (tamanho(palavra ) < posição) ENTÃO achou ← verdadeiro SENÃO achou ← falso símbolo ← COPIA(palavra,posição,1) PARA índice DE 0 ATÉ transição.tamanhotabela F AÇA SE (transição.estadoAtual = estado) E (trans ição.símboloLido = símbolo) E (NÃO achou) ENTÃO achou ← genérico(palavra, transição.próximoEstado, posição +1) FIMSE FIMPARA FIMSE genérico ← achou FIM
O algoritmo apresentado é recursivo e retorna verdadeiro, caso a palavra tenha sido
reconhecida, e falso, em caso contrário. Faz uso de uma tabela, onde cada linha representa
uma transição de estado constituída por estado atual, símbolo lido e próximo estado. A sua
estrutura é mostrada na tabela 2.3 sendo que os dados são do autômato finito da fig. 2.8. Essa
mesma tabela pode ser utilizada para validação do autômato finito.
TABELA 2.3 – TABELA DO ALGORITMO DE IMPLEMENTAÇÃO GENÉRICA
ESTADO ATUAL
SÍMBOLO LIDO
PRÓXIMO ESTADO
q0 0 q1
q1 1 qf1
qf1 0 qf1
qf1 1 qf1
20
3 DESENVOLVIMENTO DO EDITOR DE AUTÔMATOS FINITOS
A partir dos assuntos abordados no capítulo anterior, pôde-se obter o conhecimento
necessário para desenvolver o editor de autômatos finitos. Procurou-se desenvolver a
ferramenta proposta utilizando como modelo o desenvolvimento rápido de aplicações
descrito em Thiry (2001). Resumidamente, esse modelo engloba:
a) analisar os requisitos, para determinar o foco do problema que será resolvido;
b) desenvolver um projeto inicial;
���葞۽葠�����.���萏�萑l옕�촁�葞�葠l����.���萏�萑�옕�鴁،葞�葠�����epetir os seguintes passos até que a aplicação esteja pronta: implementar uma
versão da aplicação; entregar a implementação para o cliente testar; receber um
feedback do cliente; planejar uma nova versão para responder ao feedback, ou seja,
se houver problemas na versão atual, uma nova solução deve ser proposta.
3.1 ESPECIFICAÇÃO
Na especificação da aplicação foi utilizada a linguagem de modelagem UML (Furlan,
1998). A seguir, são apresentados os casos de uso, as principais classes modeladas e os
diagramas de seqüência desenvolvidos com auxílio da ferramenta Rational Rose (Quatrani,
2001).
No quadro 3.1 tem-se o primeiro caso de uso2.
QUADRO 3.1 – CASO DE USO: DESENHAR AUTÔMATO
CASO DE USO: Desenhar autômato
BREVE DESCRIÇÃO: Em uma janela do editor, o acadêmico irá desenhar o diagrama de transição de um autômato finito, utilizando para isso a paleta de desenhos.
ATOR(ES): Acadêmico
PRÉ-CONDIÇÕES:
2 A notação para caso de uso utilizada é uma adaptação daquela apresentada por Thiry (2001).
Desenhar autômato Acadêmico
21
FLUXO PRINCIPAL: 1. O acadêmico irá abrir uma nova janela no editor.
2. Para construir um autômato, o acadêmico deverá obrigatoriamente criar primeiro o estado inicial.
3. O acadêmico poderá adicionar quantos estados desejar.
4. Para obter um estado final, é necessário pressionar duplo clique no estado selecionado.
5. Para criar uma transição, é necessário selecionar dois estados, o estado origem e o destino. Se for um auto-relacionamento, basta um único estado estar selecionado. Deve ser informado o símbolo que será reconhecido quando a transição for disparada.
6. A cada nova transição criada, o editor identifica os símbolos de entrada, apresentando dinamicamente o alfabeto digitado.
7. Estados e transições podem ser excluídos a qualquer momento, sendo que o estado inicial tem que ser o último a ser apagado.
PÓS-CONDIÇÕES:
DETALHES DE INTERFACE: TE-001
Na fig. 3.1 é apresentada a tela TE-001 que contém uma janela com título Autômato
Finito, onde o diagrama de transição de um autômato deve ser desenhado pelo acadêmico. À
esquerda tem-se a paleta de desenhos e na barra de ferramentas encontra-se um botão cuja
função é criar uma nova janela para que o acadêmico possa construir o autômato desejado. A
janela de desenho possui duas áreas, sendo uma para o acadêmico desenhar um autômato
finito e outra para as mensagens emitidas pelo editor, e um campo para o acadêmico digitar
palavras que serão verificadas pelo autômato especificado. O alfabeto de símbolos que é
reconhecido a cada nova transição criada, é apresentado na parte superior da janela.
FIGURA 3.1 – TE-001: INTERFACE DO EDITOR DE AUTÔMATOS FINITOS
22
No quadro 3.2 é apresentado o segundo caso de uso.
QUADRO 3.2 – CASO DE USO: VERIFICAR PROPRIEDADES
CASO DE USO: Validar autômato
BREVE DESCRIÇÃO: O editor validará o autômato finito desenhado pelo acadêmico, retornando na área de mensagens as características do mesmo.
ATOR(ES): Acadêmico
PRÉ-CONDIÇÕES: Um autômato finito deve ter sido desenhado em uma janela do editor.
FLUXO PRINCIPAL: 1. O acadêmico irá selecionar no menu Autômato a opção que permite fazer a validação do autômato especificado.
2. O editor irá determinar o tipo do autômato finito, os possíveis estados mortos e os possíveis estados inalcançáveis.
3. O editor apresentará mensagens com as propriedades identificadas.
PÓS-CONDIÇÕES:
DETALHES DE INTERFACE: TE-001
No quadro 3.3 é apresentado o terceiro caso de uso.
QUADRO 3.3 – CASO DE USO: RECONHECER PALAVRA
CASO DE USO: Reconhecer palavra
BREVE DESCRIÇÃO: O editor analisará palavras digitadas pelo acadêmico percorrendo o autômato finito construído, podendo aceitá-las ou rejeitá-las dependendo da análise feita.
ATOR(ES): Acadêmico
PRÉ-CONDIÇÕES: Um autômato finito deve ter sido especificado em uma janela do editor.
FLUXO PRINCIPAL: 1. O acadêmico irá digitar uma palavra no campo Digite a palavra.
2. O acadêmico irá selecionar no menu Autômato a opção que permite fazer o reconhecimento da palavra, podendo ser direto ou passo a passo.
2.1 Se optar por reconhecer a palavra de forma direta, o editor
Validar autômato Acadêmico
Reconhecer palavra Acadêmico
23
irá retornar uma mensagem indicando se a palavra foi aceita ou rejeitada.
2.2 Se optar por reconhecer a palavra passo a passo, o editor além da mensagem, pintará o caminho percorrido pelo editor durante o reconhecimento.
PÓS-CONDIÇÕES:
DETALHES DE INTERFACE: TE-001
As classes da aplicação encontram-se representadas no diagrama de classe da fig.3.2,
onde a classe principal do editor é a TAutomatoFinito . As demais classes são
TConjuntos e TTransicao .
Na classe TAutomatoFinito , tem-se os atributos alfabeto, estados, estado inicial, estados finais e transições, ou seja, os elementos de um autômato. O atributo estado inicial é do tipo TDesenha , que é a classe do componente usado para desenhar os estados, visto que neste trabalho será permitido que o AFND, tenha somente um estado inicial. Os demais atributos desta classe são do tipo TConjuntos , de acordo com a definição de autômatos finitos apresentada no capítulo 2.
A classe TConjuntos é parte da classe TAutomatoFinito . Esta classe tem somente um atributo, elementos. Os elementos de um conjunto são armazenados em uma lista, pois o atributo elementos é do tipo TList .
A classe responsável pelo armazenamento da tabela de transição é a TTransicao .
Na classe TAutomatoFinito , no atributo transições, é guardado somente o endereço de
cada transição. Na classe TTransicao , tem-se os seguintes atributos: estado atual, símbolo
lido e próximo estado, todos do tipo string. Quando o autômato construído é modificado, esta
tabela é reconstruída. É através desta tabela, que serão executadas a validação e
reconhecimento de palavras do autômato.
24
FIGURA 3.2 – DIAGRAMA DE CLASSES
1
TAutomatoFinitoAlfabeto : TConjuntosEstados : TConjuntosEstadoInicial : TDesenhaEstadosFinais : TConjuntosTransicoes : TConjuntos
Create( )Destroy( )Mostra_Alfabeto( )Eh_deterministico( )Eh_Estado_Morto( )Mostra_Estados_Mortos( )Eh_Estado_Inalcancavel( )Mostra_Estados_Inalcancaveis( )Verifica_Palavra( )Reconstroi_Transicoes( )
1..*
1TTransicao
EstadoAtual : StringSimboloLido : StringProximoEstado : String
1
TConjuntosElementos : TList
Create( )Destroy( )
1
1..*
1
1
Serão apresentados dois diagramas de seqüência: da validação do autômato e do reconhecimento da palavra.
A validação de um autômato é executada sempre que o acadêmico, ao terminar de
desenhar o autômato, solicitar a validação. É instanciado um objeto da classe
TAutomatoFinito . Nesta classe são acionados cinco métodos. O primeiro método,
Eh_Deterministico , verifica qual o tipo do autômato desenhado, ou seja, se um estado
qualquer possui duas ou mais transições definidas com o mesmo símbolo, o autômato é
classificado como AFND, caso contrário, como AFD. O segundo método,
Mostra_Estados_Mortos , varre os estados acionando o terceiro método
Eh_Estado_Morto que verifica se cada estado do autômato, exceto os estados finais, têm
no mínimo uma transição que permita alcançar um estado final direta ou indiretamente. Todos
os estados que não atendam a essa condição são classificados como estados mortos. O quarto
método, Mostra_Estados_Inalcancaveis , lista os estados acionando o quinto
método Eh_Estado_Inalcançavel que verifica se cada estado final e não final do
autômato pode ser alcançado a partir do estado inicial. Os estados que não atendam a essa
condição são classificados como inalcançáveis. Na fig. 3.3 tem-se a representação do
diagrama de seqüência da validação de autômatos finitos.
25
FIGURA 3.3 – DIAGRAMA DE SEQÜÊNCIA DA VERIFICAÇÃO DAS PROPRIEDADES
: Acadêmico Editor de Autômatos Finitos : TAutomatoFinito
Solicita Validação
Valida (autômato)
Eh_Deterministico ( )
Mostra_Estados_Mortos ( )
Mostra_Estados_Inalcancaveis ( )
Apresenta propriedades identificadas
Eh_Estado_Inalcancavel ( )
Eh_Estado_Morto ( )
O reconhecimento de uma palavra é executado sempre que o acadêmico, após ter
desenhado um autômato no editor, digitar a palavra a ser reconhecida. É instanciado um
objeto da classe TAutomatoFinito . Nesta classe, será acionado o método
Reconhece_Palavra com o parâmetro trace indicando o tipo de reconhecimento. Quando
26
trace for igual a falso, o método simplesmente percorre a tabela de transição e verifica se
existe um caminho a partir do estado inicial até um estado final capaz de reconhecer a palavra
digitada pelo usuário. Quando trace for igual a verdadeiro, além de fazer o processo anterior,
o método desenha o estado atual com outra cor para mostrar para o usuário o reconhecimento
da palavra passo a passo. Em ambos os casos, o método indica se a palavra foi aceita ou
rejeitada pelo autômato em questão. Na fig. 3.4, tem-se o diagrama de seqüência do
reconhecimento de uma palavra.
FIGURA 3.4 – DIAGRAMA DE SEQÜÊNCIA DO RECONHECIMENTO DE PALAVRAS
: Acadêmico :Editor de Autômatos Finitos : TAutomatoFinito
Confirma palavra digitada
Analisa(palavra, trace)
Verifica_Palavra (trace = falso)
Verifica_Palavra (trace = verdadeiro)
Retorna aceita / rejeita
27
3.2 IMPLEMENTAÇÃO
Para o desenvolvimento deste protótipo utilizou-se a linguagem Object Pascal no
ambiente Borland Delphi 6 (Cantù, 2001). A implementação foi dividida em quatro passos:
a) primeiro passo: desenvolvimento de um ambiente gráfico onde o acadêmico possa
desenhar o autômato finito;
b) segundo passo: validação do autômato construído no editor durante o primeiro
passo;
c) terceiro passo: reconhecimento da palavra digitada pelo acadêmico após ter
desenhado o autômato finito;
d) quarto passo: reconhecimento da palavra, mas mostrando passo a passo o caminho
percorrido no autômato durante o processo, e, quando for um autômato finito não
determinístico, mostrando todos os caminhos alternativos até que a palavra seja
reconhecida ou até que todos os caminhos tenham sido percorridos.
A interface do ambiente gráfico foi construída em uma aplicação MDI, ou seja,
interface de múltiplos documentos. Assim, trabalhou-se com o conceito de form pai e form
filhos para ser possível abrir e executar várias janelas (forms) de desenhos no mesmo
momento.
Na construção do diagrama de transição de um autômato finito, o usuário poderá
adicionar tantos estados e transições quantos desejar. A única restrição é que o estado inicial
deve ser o primeiro a ser adicionado. Cada vez que um estado ou uma transição é criado, esse
elemento é inserido em um dos atributos de um objeto da classe TAutomatoFinito . Um
estado pode ser inicial, ou um estado qualquer, ou um estado final, ou seja, isto determina o
atributo onde o elemento será armazenado. Já uma transição é inserida no atributo
Transicoes , referenciando-se à tabela de transição, sendo que o símbolo lido é guardado
no conjunto do atributo Alfabeto. Os mesmos passos acontecem quando os estados são
apagados ou alterados. Por exemplo, se um estado é transformado em um estado final,
acionando o mouse com dois cliques em cima do estado, o mesmo é decrementado do
conjunto de estados e incrementado no conjunto de estados finais, o contrário também é
verdadeiro. Quando um estado é apagado, se contiver transições, todas as transições que têm
esse estado como origem ou como destino serão excluídas do atributo Transicoes .
Qualquer alteração no diagrama de transições é controlada pelo componente TDesenha .
28
Observa-se que a tabela de transições será recriada pelo método
Reconstroi_Transicoes sempre que o autômato desenhado for alterado.
Os componentes TDesenha e TDesenhaLinha não foram desenvolvidos neste
trabalho, mas foram usados especialmente para atender as necessidades do editor de
autômatos finitos. As propriedades do TDesenha usadas neste trabalho foram caption
para mostrar o rótulo do estado, automove para permitir que o desenho seja arrastado,
estado para indicar o tipo do desenho do estado, isto é, se é inicial, transitório ou final,
brush neste caso usado para mudar cor, shape para definir o tipo do desenho, pen que
define no sub-grupo a cor da borda do desenho com color e style que especifica o tipo do
traço da borda. Tem-se também os eventos onclick para desenhar e ondblclick para
alterar os estados para final ou não final e vice-versa.
Já do TDesenhaLinha usou-se as propriedades caption que mostra o(s)
símbolo(s) da transição, origem e destino que guardam os ponteiros do objeto de origem
e destino da linha para que ela seja traçada, pen que nas sub-propriedades style e color ,
definem o tipo e a cor da linha. Dos eventos tem-se o onclick para selecionar a linha e
ondblclick para abrir a caixa de diálogo para alterar o(s) símbolo(s).
A validação de autômatos finitos, como já foi citado anteriormente, é implementada
pelos métodos Eh_Deterministico , Mostra_Estados_Mortos ,
Eh_Estado_Morto , Mostra_Estados_Inalcancaveis e
Eh_Estado_Inalcançavel que verificam as propriedades do autômato em questão. A
primeira propriedade a ser averiguada é o tipo do autômato finito. Para tanto, a aplicação
varre a tabela de transição procurando se o estado atual se repete em outra linha da tabela e
verificando se o símbolo lido é o mesmo. Nesse caso, tem-se um AFND, caso contrário, tem-
se um AFD.
Logo após, os estados mortos são identificados. A análise para determinar estados
mortos é feita da seguinte maneira: inicialmente, é verificado se o estado não é um estado
inalcançável ou algum estado final. Se não for, todos os estados do autômato serão analisados
percorrendo a lista de estados e verificando para cada estado se existem transições de estado
definidas e se uma dessas transições contém um estado com rótulo igual a qf. Se estas
restrições forem atendidas, retorna da função não encontrando nenhum estado morto, caso
contrário, chama a função recursiva que ficará procurando até encontrar um rótulo qf, ou até
29
chegar em um estado que não seja morto e não tenha um próximo estado, assim listará o
mesmo como estado morto. O que pretende-se mostrar é a situação onde um estado tenha uma
seqüência de transições ligadas com o estado inicial, mas que não alcancem um estado final,
caracterizando um estado morto.
E, por fim, Mostra_Estados_Inalcancaveis tem como objetivo listar os
estados que não possam ser alcançados a partir do estado inicial. Para implementar a
validação desta propriedade, foi necessário varrer a tabela de estados, chamando a função que
verifica se o estado analisado é inicial. Se for, interrompe o looping, senão varre a lista de
estados testando se o estado tem um estado origem e se essa origem é igual ao ponteiro do
estado inicial. Enquanto o teste anterior não for aceito mas houver estados origem, a função
recursiva continua a fazer o teste até encontrar o estado inicial ou terminar a lista de estados.
Para o reconhecimento de palavras foi implementado o algoritmo genérico descrito na seção 2.5.
No apêndice 1 encontra-se cada um dos métodos explicados nessa seção.
3.3 OPERACIONALIDADE DA IMPLEMENTAÇÃO
Como foi dito anteriormente, com a ferramenta desenvolvida, os acadêmicos da
disciplina de Linguagens Formais serão capazes de construir e testar autômatos finitos
determinísticos (AFD) e não-determinísticos (AFND), podendo validar os mesmos.
Assim, para construir um autômato finito, o acadêmico deverá selecionar a opção
Criar do menu Autômato. Em uma janela do editor, o acadêmico irá desenhar um autômato
finito utilizando para isso a paleta de desenhos da tela principal. Na paleta de desenhos
encontram-se os botões para desenhar os estados e as transições. Há três botões: estado
inicial, estados e transição. Quando cada botão é acionado, o modo de desenho é ativado,
porém pode-se desenhar somente se houver uma janela de desenhos aberta. Quando é
acionado o botão do estado inicial, é desenhado no editor um estado com o rótulo q0, que
identificará este estado como sendo inicial. Se o mesmo for alterado para um estado final,
então será apresentado qf0, que determina ser o estado tanto inicial quanto final. Qualquer
estado do autômato pode ser transformado em final, bastando para isso clicar duas vezes com
o botão esquerdo do mouse em cima do estado desejado. Da mesma forma, um estado final
também pode ser transformado em não final.
30
Para criar uma transição, deve-se selecionar no máximo dois estados, um será o de
origem e outro de destino. Se houver somente um estado selecionado, este terá um auto-
relacionamento, ou seja, a transição terá este estado tanto como origem quanto como destino.
Para apagar um estado ou transição, deve-se selecionar o elemento desejado e
pressionar na tecla delete.
A validação de um autômato ocorre quando o acadêmico está com o autômato
desenhado no editor e, através do menu Autômato, selecionar a opção Validar. A partir do
momento em que a solicitação for feita, o autômato desenhado será classificado como
determinístico ou não determinístico e as suas propriedades serão identificadas, quais sejam
estados mortos e estados inalcançáveis. O resultado da validação será impresso na área de
mensagens. Nas figuras 3.5 e 3.6 estão ilustradas a validação de um autômato finito não
determinístico e de um autômato finito determinístico, respectivamente.
FIGURA 3.5 – VALIDAÇÃO DE UM AUTÔMATO FINITO NÃO DETERMINÍSTICO
31
FIGURA 3.6 – VALIDAÇÃO DE UM AUTÔMATO FINITO DETERMINÍSTICO
O reconhecimento de uma palavra ocorre quando o acadêmico está com o autômato
desenhado no editor, o campo "Digite a palavra" contém uma palavra para análise e a opção
"Reconhecer palavra" do menu Autômato for selecionada. A palavra pode ser reconhecida
diretamente ou passo a passo. Em ambos os casos, o editor irá retornar se a palavra foi aceita
ou rejeitada. A diferença das duas funções de reconhecimento de palavra que é a segunda, a
cada símbolo analisado, pinta o estado atual do processo de reconhecimento. O resultado do
reconhecimento será impresso na área de mensagens. Observa-se nas figuras 3.7 e 3.8 o
reconhecimento de palavras diretamente e passo a passo, respectivamente.
32
FIGURA 3.7 – RECONHECIMENTO DIRETO DE UMA PALAVRA
33
FIGURA 3.8 – RECONHECIMENTO DA PALAVRA 011 PASSO A PASSO
Na Fig. 3.8, tem-se ilustrada a execução da rotina que reconhece as palavras, logo:
a) do estado q0 lendo o símbolo 0 atinge o estado q1;
b) do estado q1 lendo o símbolo 1, atinge os estados q2 e q3;
c) do estado q2 lendo o símbolo 1, não há nenhuma transição. Então, partindo do
estado q3 atinge o estado q4 e qf1;
d) o estado q4 é morto, ou seja, não é final e não atinge nenhum estado final. Já o qf1,
é um estado final, então a palavra foi aceita.
34
4 CONSIDERAÇÕES FINAIS
4.1 CONCLUSÕES
Com o desenvolvimento deste trabalho, foram atingidos os objetivos propostos, tendo
como resultado final a implementação do editor de autômatos finitos, que permite efetuar a
validação de autômatos finitos desenhados e o reconhecimento de palavras. O editor de
autômatos finitos foi desenvolvido para auxiliar professores e acadêmicos da disciplina de
Linguagens Formais.
Para este trabalho ser realizado, foram estudados conceitos de autômatos finitos para
implementar a validação de um autômato e para desenvolver o algoritmo genérico,
apresentado na seção 2.5, utilizado no reconhecimento de palavras.
Durante o desenvolvimento do editor foram utilizadas três ferramentas: Rational Rose
para fazer a especificação; Borland Delphi 6 para implementar o editor de autômatos finitos e
PhotoShop 6.0 para construir as imagens de botões do editor. Na implementação da parte
gráfica do editor, levantou-se a questão, através de pesquisa na internet, de que o
desenvolvimento poderia ser facilitado se fosse desenvolvido na linguagem Java, pois haviam
várias aplicações prontas de desenhos que poderiam ser usados para representar estados e
transições, não sendo necessário construir componentes para usar neste trabalho. Porém, além
do conhecimento não ser de nível considerado, o tempo era curto para efetuar a troca de
linguagem de programação.
Cabe ressaltar que o trabalho usou vários componentes, entre eles o TDesenha , que
desenha os estados, e o TDesenhaLinha , que desenha as transições, que não foram
desenvolvidos nesse trabalho. O código fonte destes componentes encontra-se disponível
junto à implementação, para as possíveis extensões sugeridas na próxima seção.
4.2 DIFICULDADES ENCONTRADAS
As principais dificuldades encontradas no desenvolvimento deste trabalho estão
relacionadas à interação do usuário e aos componentes utilizados para desenhar autômatos
finitos.
35
Na interação do usuário com o editor, as dificuldades dizem respeito à elaboração de
todos os tratamentos relativos aos possíveis passos dos usuários, com informações indicando a
maneira correta para manusear a ferramenta, principalmente na criação dos diagramas de
transição dos autômatos .
Quanto aos componentes de desenho, pode-se afirmar que são relativamente
complexos, e o tempo necessário para conhece-los foi restrito pelo cronograma de
desenvolvimento. Observa-se que os mesmos ainda estavam em fase de teste e não continham
todos os elementos necessários para desenhar autômatos finitos.
4.3 EXTENSÕES
Como possíveis extensões desse trabalho enumera-se:
a) melhorar a interface gráfica do componente TDesenhaLinha , aplicando bezier
para desenhar as transições do autômato;
b) mostrar na área de mensagens o caminho percorrido quando for acionada a opção
reconhecer palavra;
c) aplicar equivalência em autômatos com movimentos vazios;
d) aplicar a transformação de um AFND em um AFD;
e) mostrar os estados que geram o não-determinismo, por exemplo, pintando-os com
outra cor, na validação do autômato;
f) aplicar algoritmos de minimização em AFDs.
36
REFERÊNCIAS BIBLIOGRÁFICAS
AHO, Alfred V.; SETHI, Ravi; ULLMAN, Jeffrey D. Compiladores: princípios, técnicas
e ferramentas. Tradução Daniel de Ariosto Pinto. Rio de Janeiro: LTC, 1995.
CANTÙ, Marco. Dominando o Delphi 6: a bíblia. Tradução João Eduardo Nóbrega Tortello.
São Paulo: Makron Books, 2001.
FURLAN, José D. Modelagem de objetos através da UML: the unified modeling
language. São Paulo: Makron Books, 1998.
LEWIS, Harry R.; PAPADIMITRIOU, Christos H. Elementos de teoria da computação.
Tradução Edson Furmankiewicz. Porto Alegre: Bookman, 2000.
MARTINS, Joyce. Linguagens formais e compiladores. [2002]. 43 f. Notas de Aula (Curso
de Ciências da Computação) – Universidade Regional de Blumenau, Blumenau, SC.
Disponível em: <http://www.inf.furb.br/~joyce>. Acesso em: jun. 2002.
MENEZES, Paulo B. Linguagens formais e autômatos. Porto Alegre: Sagra-Luzzatto, 1998.
PRICE, Ana M.A.; TOSCANI, Simão S. Implementação de linguagens de programação:
compiladores. Porto Alegre: Sagra-Luzzatto, 2001.
QUATRANI, Terry. Modelagem visual com Rational Rose 2000 e UML. Tradução
Savannah Hartmann. Rio de Janeiro: Ciência Moderna, 2001.
RANGEL NETTO, José L.M. Linguagens formais e autômatos. [2001].
Disponível em: <http://www-di.inf.puc-rio.br/~rangel/lf.html>. Acesso em: ago.2002.
SEBESTA, R.W. Conceitos de linguagens de programação. Tradução José Carlos Barbosa
dos Santos. Porto Alegre: Bookman, 2000.
THIRY, Marcello; SALM JR., José. Processo de desenvolvimento de software com UML.
[2001]. Disponível em: < http://www.eps.ufsc.br/disc/procuml/>. Acesso em: nov. 2002.
37
APÊNDICE 1
Neste anexo encontra-se a implementação dos algoritmos dos principais métodos deste
trabalho. Nos quadros 6.1 e 6.2, seguem os códigos fontes da validação do autômato finito e
do reconhecimento de palavras.
QUADRO 6.1 – CÓDIGOS FONTES DA VERIFICAÇÃO DAS PROPRIEDADES
function TAutomatosFinitos.Eh_Deterministico: boole an; var ind, ind1, Contador : longInt; begin Contador:=0; For ind:=0 to Transicoes.Elementos.Count-1 Do Begin Contador:=0; For ind1:=0 to Transicoes.Elementos.Count-1 Do Begin //Verifica se o estado atual é igual If (TTransicao(Transicoes.Elementos. Items[ind1]).EstadoAtual = TTransicao(Transicoes.Elementos. Items[ind]).EstadoAtual) //Verifica se o símbolo lido é igual and (Contem(TTransicao(Transicoes.Ele mentos.Items[ind1]).Simbolo_lido, TTransicao(Transicoes.Ele mentos.Items[ind]).Simbolo_lido)) Then inc(Contador); If Contador > 1 Then Break; End; If Contador > 1 Then Break; End; Result := not (Contador > 1); end; function TAutomatosFinitos.Eh_Estado_Inalcancavel(E stado: TDesenha): boolean; var ind : integer; Begin Result := True; If Estado = Estado_Inicial Then Result := False Else For ind := 0 to Estado.Linhas.Count - 1 Do Begin If (Estado <> TDesenhaLinha(Estado.Lin has.Items[ind]).Origem) And (TDesenhaLinha(Estado.Linhas.Items[ ind]).Origem = Estado_Inicial) Then Begin Result := False; Break; End Else If (Estado <> TDesenhaLinha(Estado. Linhas.Items[ind]).Origem) Then Result :=Eh_Estado_Inalcançável (TDesenhaLinha(Estado. Linhas.Items[ind]).Origem); If Not Result Then Break; End; end;
function TAutomatosFinitos.Mostra_Estados_Inalcanca veis: String; var ind : longInt; begin Result := ''; //Varre lista de estados For ind := 0 to Estados.Elementos.Count-1 Do If Eh_Estado_Inalcancavel(TDesenha(Estados.El ementos.Items[ind])) Then Result := Result + TDesenha(Estados.Elemen tos.Items[ind]).Caption + ','; //Varre lista de estados finais For ind := 0 to Estados_Finais.Elementos.Count-1 Do If Eh_Estado_Inalcancavel(TDesenha(Estados_Fi nais.Elementos.Items[ind])) Then Result := Result + TDesenha(Estados_Finais .Elementos.Items[ind]).Caption + ','; Result := Copy(Result,1,length(Result)-1);
38
end;
function TAutomatosFinitos.Eh_Estado_Morto(Estado: TDesenha) : boolean; var ind : integer; Begin Result := True; if Eh_Estado_Inalcancavel(Estado) Or (Copy(Estado.Caption,1,2) = 'qf') Then Result := False Else For ind := 0 to Estado.Linhas.Count - 1 Do // Varre as linhas Begin If Not Eh_Estado_Inalcancavel(Estado) T hen If (TDesenhaLinha(Estado.Linhas.Ite ms[ind]).Destino <> nil) And (Estado <> TDesenhaLinha(Estado. Linhas.Items[ind]).Destino)
And (Copy(TDesenhaLinha(Estado.Linhas.Items [ind]).Destino.Caption,1,2) = 'qf') Then Begin Result := False;
Break; End Else If (TDesenhaLinha(Estado.Linhas .Items[ind]).Destino <> nil) And (Estado <> TDesenhaLinha(Est ado.Linhas.Items[ind]).Destino) Then Result := Eh_Estado_Morto(Td esenhaLinha (Estado.Linhas.Ite ms[ind]).Destino); If Not Result Then Break;
End; End;
function TAutomatosFinitos.Mostra_Estados_Mortos: S tring; var ind : longInt; begin Result := ''; If Eh_Estado_Morto(Estado_Inicial) Then Result := Result + Estado_Inicial.Caption + ', '; For ind := 0 to Estados.Elementos.Count-1 Do If Eh_Estado_Morto(TDesenha(Estados.Elementos .Items[ind])) Then Result := Result + TDesenha(Estados.Elemen tos.Items[ind]).Caption + ','; Result := Copy(Result,1,length(Result)-1); end;
QUADRO 6.2 – CÓDIGO FONTE DO RECONHECIMENTO DE PALAVRA
function TAutomatosFinitos.Verifica_palavra(palavra , estado:string; posicao: byte; passoapasso: boolean): boolean; var Transicao : TTransicao; Simbolo: string; Ind : Integer; begin If (copy(estado,1,2) = 'qf') And (Length(palavra) < posicao) Then Begin Result:= true; If passoapasso Then Begin If TDesenhaLinha(Alfabeto.Elementos.It ems[ind]).Destino <> nil Then Begin TDesenhaLinha(Alfabeto.Elementos. Items[ind]).Destino.Brush.Color:=clGreen; TDesenhaLinha(Alfabeto.Elementos. Items[ind]).Destino.Repaint; Sleep(1700); TDesenhaLinha(Alfabeto.Elementos. Items[ind]).Destino.Brush.Color:= clInfoBk; TDesenhaLinha(Alfabeto.Elementos. Items[ind]).Destino.Repaint; End Else Begin TDesenhaLinha(Alfabeto.Elementos. Items[ind]).Origem.Brush.Color:= clGreen; TDesenhaLinha(Alfabeto.Elementos. Items[ind]).Origem.Repaint; Sleep(1700); TDesenhaLinha(Alfabeto.Elementos. Items[ind]).Origem.Brush.Color:= clInfoBk; TDesenhaLinha(Alfabeto.Elementos. Items[ind]).Origem.Repaint; End;
39
End; End Else Begin Result:= False; Simbolo:= copy(palavra,posicao,1); For ind:=0 to Transicoes.Elementos.Count-1 D o Begin Transicao := Transicoes.Elementos.Item s[ind]; If (Transicao.EstadoAtual = Estado) And (Contem(simbolo,Transicao.Simbolo_ lido)) And Not Result Then Begin If passoapasso Then Begin TDesenhaLinha(Alfabeto.Elem entos.Items[ind]).Origem.Brush.Color:= clGreen; TDesenhaLinha(Alfabeto.Elem entos.Items[ind]).Origem.Repaint; Sleep(1700); TDesenhaLinha(Alfabeto.Elem entos.Items[ind]).Origem.Brush.Color:= clInfoBk; TDesenhaLinha(Alfabeto.Elem entos.Items[ind]).Origem.Repaint; Sleep(1100); End; Result := verifica_palavra(palav ra, Transicao.ProximoEstado, posicao+1, passoapasso); End End; End; end;