• Nenhum resultado encontrado

Trabalho CT200

N/A
N/A
Protected

Academic year: 2021

Share "Trabalho CT200"

Copied!
31
0
0

Texto

(1)

Instituto Tecnológico de Aeronáutica

Divisão de Ciência da Computação - IEC

Programa de Pós-Graduação em Engenharia Eletrônica e Computação Área de Informática – EEC-I

CT200 – Fundamentos de Automata e Linguagens Formais Prof. Paulo M. Tasinaffo

Desenvolvimento de um analisador léxico para álgebra

lógica

(2)

2

Instituto Tecnológico de Aeronáutica

Divisão de Ciência da Computação - IEC

Programa de Pós-Graduação em Engenharia Eletrônica e Computação Área de Informática – EEC-I

CT200 – Fundamentos de Automata e Linguagens Formais Prof. Paulo M. Tasinaffo

Desenvolvimento de um analisador léxico para álgebra

lógica

Caio Henrique Coutinho Ciro Fernandes Matrigrani

Eliezer Rodrigues Segeti Marco Antonio Leite

(3)

3

Índice

Introdução ... 5

1. Análise Léxica ... 6

2.1. Erros e Falhas ... 10

2. Relação entre Análise Léxica e Autômatos ... 12

3. Apresentação do estudo de caso ... 20

4. Apresentação do programa desenvolvido ... 24

Conclusão ... 27

Apêndice ... 28

Como usar o JFlex ... 28

(4)

4

Lista de Figuras

Figura 1 – Esquema do analisador léxico e analisador sintático ... 6

Figura 2- Exemplo simples de gramática regular ... 8

Figura 3 - Exemplo de expressões regulares ... 8

Figura 4 - Tokens de uma expressão regular ... 9

Figura 5 - Memória do analisador léxico ... 9

Figura 6 - Tabela de símbolos ... 10

Figura 7 - Exemplo de erro na análise léxica 1 ... 11

Figura 8 - Exemplo de erro na análise léxica 2 ... 11

Figura 9 - Autômato finito - Identificadores ... 13

Figura 10 - Procedimento - Identificadores... 13

Figura 11 - Autômato finito - Números ... 13

Figura 12 - Procedimento - Números ... 14

Figura 13 - Autômato finito - Operadores ... 14

Figura 14 – Procedimento Operador ... 15

Figura 15 - Autômato finito - Analisador Léxico ... 16

Figura 17 - Gramática formal da algebra lógica ... 21

Figura 18 - Tabela verdade para conectivos lógicos ... 22

Figura 19 - Exemplo de sentença ... 22

Figura 20 - Definição de uma sentença complexa ... 23

Figura 21 - Declaração dos símbolos ... 23

Figura 22 - Definição das proposições lógicas ... 23

Figura 23 – Tela geral do programa ... 24

Figura 24 - Tela com erro na definição dos símbolos ... 25

Figura 25 - Tela com definição das proposições lógicas ... 25

Figura 26 - Tela com a conclusão da sentença ... 26

Figura 27 – Exemplo do Código ... 28

(5)

5

Introdução

O objetivo deste trabalho é mostrar algo sobre analise léxica voltada para álgebra lógica, que nada mais é um dos processos utilizados na construção de tradutores, responsável pela análise e categorização das entradas, que facilita a análise sintática. Também será explicado sobre a relação entre a análise léxica e autômatos. E a partir desta percepção intelectual construir um analisador léxico para a álgebra lógica, que é basicamente um sistema formal, que combina proposições atômicas usando conectivos lógicos, sendo que com essas combinações é possível inferir resultados.

Assim, a partir deste trabalho teremos uma visão abrangente sobre um projeto de compilador e a ideia de um analisador léxico e sua relação com autômatos, com um pouco do funcionamento do mesmo.

(6)

6

1. Análise Léxica

A análise léxica é realizada através de um analisador léxico (scanner em algumas literaturas). O analisador léxico é a primeira etapa de um tradutor, e incumbido de separar os símbolos que tenham algum significado para a linguagem, e de assinalar os que não fazem parte da linguagem quando são encontrados. Esses símbolos são chamados de tokens (ou simplesmente de símbolos), que por sua vez constituem classes de símbolos instanciados por lexemas (átomos em algumas literaturas) como, por exemplo, palavras reservadas, delimitadores, identificadores, números, operadores, parentisadores, etc. Existem também classes de símbolos que são separados e eliminados durante o processo, como por exemplo espaços em branco, quebras de linha, comentários, tabulações, etc. Todos os lexemas (unidades significativas) lidos são incluídos na tabela de símbolos junto com os seus respectivos atributos, como o tipo de token, número da linha, valor do lexema, etc. A tabela de símbolos representa a saída do analisador léxico.

Em um projeto de compilador, o analisador léxico é seguido pelo analisador sintático, que utiliza a tabela de símbolos como entrada. O analisador léxico interage diretamente com o analisador sintático de duas possíveis formas, como apresentado na figura 1.

Figura 1 – Esquema do analisador léxico e analisador sintático

A segunda forma apresentada é, em geral, uma melhor opção, pois eliminamos a necessidade de carregar, ler, analisar e montar a tabela de símbolos para todo o programa de uma só vez, o que implica um maior tempo de resposta e na necessidade de uma área de armazenamento para toda essa informação que normalmente é utilizada a memória RAM.

(7)

7

A divisão da análise léxica da análise sintática facilita consideravelmente o projeto do tradutor e o torna mais eficiente e portável.

Os tipos de tokens reconhecidos pelo analisador léxico se enquadram em uma das seguintes classes:

Classe Descrição

Palavra-chaves ou Palavras reservadas

São cadeias utilizadas pela própria linguagem e já possuem um significado próprio

Números Representa os números inteiros, reais, complexos, etc Identificadores São os lexemas expressados pelo usuário. Em linguagens

de programação possuem o significado de variáveis, parâmetros, funções, classes, etc.

Operadores Podem ser aritméticos, relacionais, lógicos, transformadores, etc.

Literal Lexemas que contém uma cadeia de símbolos informada entre aspas.

Parentisadores Exemplo: abre/fecha aspas, abre/fecha parênteses, abre/fecha colchetes, abre/fecha chaves, begin/end, etc. Sinais de pontuação Lexemas que contém ponto, vírgula, ponto-e-vírgula, dois

pontos, etc.

Os tipos de tokens demonstrados acima são exemplos comuns mais utilizados, mas não é uma regra. Os analisadores léxicos dependem de uma gramática regular previamente definida, criando as bases de conhecimento necessárias para aceitar ou não às cadeias analisadas.

Uma forma de mostrar o funcionamento de um analisador léxico desde a concepção é definindo um alfabeto e uma gramática simples. Assim definimos um alfabeto composto pelos seguintes símbolos { [a-z], [A-Z], [0-9], =, +, -, (, ), ; }. Na figura 2 poderemos determinar uma gramática aritmética somente com os operadores +(mais) e -(menos). A produção <SEP> indica a divisão entre instruções (proposições) reconhecidas pela linguagem.

(8)

8

<IDENT> -> L | L<RESTO>

<RESTO> -> L | D | L<RESTO> | D<RESTO> L -> [a-z] | [A-Z]

D -> [0-9]

<NUM> -> D | D<NUMERO> <OP> -> = | + | -

<EXPR> -> <EXPR><OP><EXPR> | <IDENT> | <NUM> | (<EXPR>)

<CMD> -> <EXPR><SEP> <SEP> -> ;

Figura 2- Exemplo simples de gramática regular

Utilizando a gramática demonstrada na figura 2 podemos construir expressões regulares. Alguns exemplos são mostrados na figura 3.

A = B + 2

VAR02 = VAR05 – 10 + C

BRUTO – DESCONTOS = LIQUIDO

COUNT = COUNT + 1

Figura 3 - Exemplo de expressões regulares

Consideremos um analisador léxico baseado na gramática apresentada na figura 2 e uma das expressões regulares apresentadas na figura 3, com esse conjunto, é possível simular uma possível execução e interpretação do analisador léxico e verificar como ficará sua memória e estado.

(9)

9 Figura 4 - Tokens de uma expressão regular

A figura 4 é um exemplo de “tokenização”, ou seja, a extração de tokens a partir de uma expressão regular. Observe que os símbolos não reconhecidos pela gramática, como espaços e comentários, foram excluídos da interpretação feita pelo analisador léxico. Após essa etapa teremos na memória de nosso analisador léxico os seguintes tokens:

ID(var02)

OP(=)

ID(var05)

OP(-)

NUM(10)

OP(+)

ID(C)

Figura 5 - Memória do analisador léxico

Concluindo, após definirmos a gramática, construir uma expressão regular e submete-la ao analisador léxico, o mesmo interpreta e atribui na memória todos os tokens encontrados, assim finalmente torna-se possível construir a tabela de símbolos. Veja um exemplo na figura 6.

(10)

10

TOKEN LEXEMA DESCRIÇÃO INFORMAL DO PADRÃO ATRIBUTOS IDENT VAR02 Identificador – Letras seguidas por letras

e/ou números.

Expressão K. Linha de código N.

OP = Operador de atribuição =(igual) Expressão K. Linha de código N.

IDENT VAR05 Identificador – Letras seguidas por letras e/ou números.

Expressão K. Linha de código N.

OP - Operador aritmético –(menos) Expressão K. Linha de código N.

NUM 10 Número – Inteiro Expressão K. Linha

de código N.

OP + Operador aritmético +(mais) Expressão K. Linha de código N.

IDENT C Identificador – Letras seguidas por letras e/ou números.

Expressão K. Linha de código N.

Figura 6 - Tabela de símbolos

2.1. Erros e Falhas

É incomum ocorrer falhas ou erros durante o processo de análise léxica, uma vez que sua tarefa é simplesmente percorrer o código e montar a tabela de símbolos, adiando a verificação dos relacionamentos entre tokens para a análise sintática. Mas dois possíveis erros são passíveis de ocorrer durante a varredura do analisador léxico:

a) quando um símbolo que não pertence ao alfabeto é informado no código fonte;

b) quando uma cadeia é formatada erroneamente não sendo reconhecida. Os exemplos desses erros são apresentados na figura 7 e na figura 8.

(11)

11

VAR02 = VAR05 ? 10 + C // Comentário

<IDENT> <OP> <IDENT>

Símbolo não pertence ao alfabeto

<NUM> <OP> <IDENT>

Figura 7 - Exemplo de erro na análise léxica 1

VAR02 = 9COD - 10 + C // Comentário

<IDENT> <OP>

Cadeia não reconhecida

<OP> <NUM> <OP> <IDENT>

Figura 8 - Exemplo de erro na análise léxica 2

Alguns compiladores implementam um algoritmo chamado modalidade de pânico, que atua no momento em que o analisador léxico entra em um laço infinito em decorrência de um conflito recursivo entre a gramática definida com as cadeias inseridas, dessa forma finalizando com erro o analisador.

(12)

12

2. Relação entre Análise Léxica e Autômatos

Conhecendo a teoria de linguagens regulares e de autômatos finitos têm-se a base para os algoritmos de analisadores léxicos. Esse conhecimento é útil para a construção de geradores de analisadores léxicos, e em escala menor, para o uso desses geradores.

Outra forma de descrever os símbolos de uma linguagem seria com o uso de autômatos de estados finitos. Essa prática tem a vantagem de permitir uma tradução quase direta para código de programação. Como exemplo, tokens podem ser reconhecidos através de autômatos finitos onde o estado final gera o reconhecimento de um token específico e/ou um procedimento específico (como por exemplo, inserir na tabela de símbolo), normalmente cria-se um diagrama de transição para representar o reconhecimento de tokens.

A melhor forma de representar a relação entre análise léxica e autômato é estudar casos isolados com pequenos exemplos. Continuando os exemplos utilizados no capítulo 2, podemos elaborar autômatos finitos para reconhecer expressões regulares geradas a partir da gramática definida na figura 2, e posteriormente, veremos como codificá-los. Todos os autômatos de reconhecimento de tokens começam pelo estado “S”, este estado representa a decisão do tipo de token.

É mostrado na figura 9, um autômato utilizado para reconhecer identificadores. Importante citar que o procedimento de um analisador léxico após aceitar um token decorrente das regras de identificador, acessa a tabela de palavras chaves (ou palavras reservadas) e procura por uma ocorrência, para decidir se é um token do tipo palavra-chave ou se realmente é um token do tipo identificador. Como estamos trabalhando com uma gramática simples, essa verificação será omitida.

(13)

13

S

LETRA

LETRA | DIGITO

IDENT

Figura 9 - Autômato finito - Identificadores

Uma vez que o autômato foi desenhado, podemos então programar o procedimento pertencente ao analisador léxico para reconhecer identificadores.

procedimento pega_identificador {

se ler_proximo_simbolo() = LETRA então

{

enquanto (ler_proximo_simbolo() = LETRA) ou (ler_proximo_simbolo() = DIGITO) faça

{

cadeia = cadeia || pega_proximo_simbolo() // o símbolo || é o mesmo que concatenar }

retorna cadeia; // A variável cadeia significa um token do tipo identificador }

}

Figura 10 - Procedimento - Identificadores

Visando um melhor entendimento da lógica utilizada no procedimento mostrado na figura 10, a sub-rotina “ler_proximo_simbolo()” somente lê o próximo símbolo encontrado no buffer de entrada, já a sub-rotina “pega_proximo_simbolo()” executa a mesma função e remove o símbolo lido. Essas sub-rotinas serão utilizadas em outros procedimentos.

Na figura 11 é mostrado um autômato para reconhecer números.

S DIGITO

DIGITO

NUM

(14)

14

procedimento pega_numero {

se ler_proximo_simbolo() = DIGITO então

{

enquanto ler_proximo_simbolo() = DIGITO faça

{

cadeia = cadeia || pega_proximo_simbolo() }

se ler_proximo_simbolo() = LETRA então

{

lança erro “Token não aceito”.

} senão { retorna cadeia; } } }

Figura 12 - Procedimento - Números

A seguir, na figura 13, um autômato para reconhecer operadores. O procedimento gerado por este autômato utiliza uma lógica simples somente com o “se” e laços não são necessários, podendo ser utilizado o recurso de “se” encadeado ou ainda a palavra-chave switch/case de algumas linguagens.

) ( S = + -OP = OP + OP -( )

(15)

15 procedimento pega_operador { se (ler_proximo_simbolo() = “=”) ou (ler_proximo_simbolo() = “(”) ou (ler_proximo_simbolo() = “+”) ou (ler_proximo_simbolo() = “)”) ou (ler_proximo_simbolo() = “-”) então {

cadeia = cadeia || pega_proximo_simbolo()

retorna cadeia; }

}

Figura 14 – Procedimento Operador

Após estudar cada autômato isoladamente e entender os procedimentos gerados, podemos uni-los e visualizar um protótipo do analisador léxico baseado na gramática definida na figura 2. Para facilitar o projeto do autômato, utilizamos as transições épsilon. Veja a figura 15.

(16)

16 ) ( S LETRA LETRA | DIGITO DIGITO DIGITO = + -CMD SEP ; IDENT NUM OP = OP + OP -( ) E E E E E E E

Figura 15 - Autômato finito - Analisador Léxico

Na figura 16 é apresentado um código didático do protótipo do analisador léxico.

procedimento pega_identificador {

se ler_proximo_simbolo() = LETRA então

{

(17)

17

(ler_proximo_simbolo() = DIGITO) faça

{

cadeia = cadeia || pega_proximo_simbolo() } retorna cadeia; } } procedimento pega_numero {

se ler_proximo_simbolo() = DIGITO então

{

enquanto ler_proximo_simbolo() = DIGITO faça

{

cadeia = cadeia || pega_proximo_simbolo() }

se ler_proximo_simbolo() = LETRA então

{

lança erro “Token não aceito. Número inválido.”

} caso contrário { retorna cadeia; } } } procedimento pega_operador { se (ler_proximo_simbolo() = “=”) ou (ler_proximo_simbolo() = “(”) ou (ler_proximo_simbolo() = “+”) ou (ler_proximo_simbolo() = “)”) ou (ler_proximo_simbolo() = “-”) então {

cadeia = cadeia || pega_proximo_simbolo() retorna cadeia;

} }

procedimento pega_separador {

se ler_proximo_simbolo() = SEP então

{

cadeia = pega_proximo_simbolo() retorna cadeia;

} }

(18)

18

procedimento pega_token {

proximo_simbolo = ler_proximo_simbolo()

se proximo_simbolo = LETRA então

{

cadeia = pega_identificador() }

ouse proximo_simbolo = DIGITO então

{

cadeia = pega_numero() }

ouse proximo_simbolo = IGUAL ou MAIS ou MENOS ou LPARENT ou RPARENT então

{

cadeia = pega_operador() }

ouse ler_proximo_simbolo() = SEP então

{

cadeia = pega_separador() }

caso contrário

{

lança erro “Símbolo não pertence ao alfabeto.”

} retorna cadeia } procedimento executa_analisador_lexico { zerar_tabela_simbolos() carregar_buffer_entrada() instrução = 0 cadeia = pega_token()

enquantocadeia não for vazia faça

{

insere_token(cadeia, instrução) // Insere o token (+ atributos) na tabela de símbolos

se cadeia = SEP então

{

instrução = instrução + 1 }

cadeia = pega_token() }

(19)

19

se ocorreu erro então

{ retorna ERRO } casocontrário { retorna SUCESSO } }

Figura 16 - Protótipo - Analisador Léxico

Observe que na figura 16, que o procedimento “pega_token()” exerce a função do estado “S” existente nos autômatos, isto se deve porque é nesse momento que o analisador léxico reconhece qual é o tipo do próximo token que será lido. Da mesma forma, o código “expressão = expressão + 1” que está dentro do procedimento “executa_analisador_léxico()” exerce a função do estado final “CMD” presente no autômato listado na figura 15, apesar de simples é um procedimento muito importante, porque é nesse momento que o analisador léxico sinaliza através do campo atributos na tabela de símbolos a mudança de instrução, informação imprescindível para a próxima etapa do tradutor, a análise sintática. E por fim, o procedimento “executa_analisador_lexico()” é responsável por ler e carregar o buffer de entrada, contar as instruções, inserir os tokens mais os respectivos atributos na tabela de símbolos e executar até encontrar o final do código fonte.

(20)

20

3. Apresentação do estudo de caso

O estudo de caso é o desenvolvimento de um analisador léxico para álgebra lógica, foi desenvolvido um sistema de inferência em bases de conhecimento com estrutura de álgebra lógica.

A sintaxe da álgebra lógica define as sentenças permitidas. As sentenças atômicas consistem em um único símbolo algébrico lógico. Cada símbolo algébrico lógico representa uma proposição que pode ser verdadeira ou falsa. As sentenças complexas são construídas a partir de sentenças mais simples com a utilização de conectivos lógicos, que são cinco, porém no programa desenvolvido são usados os quatro seguintes:

Conectivos Descrição

~ (não). Uma sentença como ~W, é chamada negação de W. Um literal é uma sentença atômica (um literal positivo) ou uma sentença atômica negada (um literal negativo

^ (e) Uma sentença cujo principal conectivo é ^, como W ^ P, é chamado de conjunção, suas partes são os elementos da conjunção

V (ou) Uma sentença que utiliza v, como (W ^ P) v O, é uma disjunção dos disjuntos (W ^ P) e O.

 (implica) Uma sentença como (W ^ P) > ~W é chamada de implicação (ou condicional). Sua premissa ou antecedente é (W ^ P), e sua conclusão é conseqüente é ~W. As implicações também são conhecidas como regras ou declarações se-então.

Agora deve ser definida a Gramática formal da álgebra lógica, poderão ser usados os símbolos “(“ e “)” para indicar a precedência das proposições.

(21)

21

Segue a definição formal da Gramática:

G = (N, T, P, S) onde, N é o alfabeto;

T é o conjunto de símbolos não terminais; P é o conjunto de regras de produção; e S é o símbolo não terminal inicial.

N = a,..., z, A,..., Z, >, ^, ~, (, ); T = S, SA, SC, SI, LP, RP, OP, N; P = { S -> SA | SC SA -> Verdadeiro | Falso | SI SI -> a |...| u | w | x | y | z | A |...| Z SC -> N S | S OP S | LP S RP LP -> ( RP -> ) OP -> v | ^ | > N -> ~ }

Figura 17 - Gramática formal da algebra lógica

Após especificar a sintaxe da algébrica lógica é necessário especificar sua semântica. A semântica define as regras para determinar a verdade de uma sentença com respeito a um modelo específico. Em algébrica lógica, um modelo simplesmente fixa o valor verdade (verdadeiro ou falso) para todo símbolo da algébrica lógica.

A semântica da algébrica lógica deve especificar como calcular o valor verdade de qualquer sentença, dado um modelo. Isso é feito de forma recursiva. Todas as sentenças são construídas a partir de sentenças atômicas e dos cinco conectivos; assim precisamos especificar como calcular a verdade de

(22)

22

sentenças atômicas e como calcular a verdade de sentenças formadas com cada um dos cinco conectivos. As regras para cada conectivo podem ser resumidas em uma tabela-verdade que especifica o valor verdade de uma sentença complexa para cada atribuição possível de valores-verdade e aos seus componentes. As tabelas-verdade para os conectivos lógicos são dadas na figura abaixo:

P Q ~P P ^Q P v Q P > Q

Falso Falso Verdadeiro Falso Falso Verdadeiro

Falso Verdadeiro Verdadeiro Falso Verdadeiro Verdadeiro

Verdadeiro Falso Falso Falso Verdadeiro Falso

Verdadeiro Verdadeiro Falso Verdadeiro Verdadeiro Verdadeiro Figura 18 - Tabela verdade para conectivos lógicos

Usando-se essas tabelas, o valor de qualquer sentença “s” pode ser calculado com relação a qualquer modelo “m” por um processo simples de avaliação recursiva.

Após definir a semântica para a álgebra lógica, podemos construir uma base de conhecimento. Seguem alguns exemplos de sentenças:

a: Jogo de futebol b: Peder o jogo c: Vitorioso d: Último jogo e: Campeão

Figura 19 - Exemplo de sentença

Neste ponto foram definidos alguns símbolos ou sentenças atômicas para que no próximo passo sejam definidas as sentenças complexas, usando estes símbolos e posteriormente sejam executadas as inferências.

(23)

23

Segue a definição de uma sentença complexa:

a ^ c ^ d > e

Figura 20 - Definição de uma sentença complexa

Onde se lê: Se jogo futebol, e é o último jogo do campeonato, e sou vitorioso, então sou campeão

Perceba que neste ponto as sentenças atômicas não possuem definidos os seus valores de verdadeiro ou falso, o programa que foi desenvolvido solicita esta informação ao usuário e posteriormente faz o processamento até chegar a alguma definição.

O programa aceita bases de conhecimento com as seguintes estruturas:

a : Vou ao Supermercado b : Tenho Dinheiro

c : Fico em Casa

d : Tenho Cartão de Crédito e : Efetuo Compras

Figura 21 - Declaração dos símbolos

a > ~c

a ^ (b v d) > e

Figura 22 - Definição das proposições lógicas

São definidos os símbolos que serão usados e depois as proposições lógicas, que devem estar com estrutura pós-fixa. Perceba que em estruturas mais complexas a precedência entre as sentenças é feita através dos parênteses.

(24)

24

O programa faz a análise léxica para verificar se as definições dos símbolos e das sentenças são entradas válidas, antes de iniciar o processamento da base de conhecimento.

4. Apresentação do programa desenvolvido

O programa desenvolvido permite ao usuário digitar diretamente as informações de símbolos e proposições, e através de um clique no botão, calcular todas as possíveis inferências que se pode tirar.

Segue uma imagem da tela do programa:

Figura 23 – Tela geral do programa

Na área de símbolos, o usuário pode digitar as variáveis relevantes no formato de "letra : descrição". Na área de proposições, o usuário pode digitar implicações lógicas no formato infixo, podendo usar os operadores ^ ("e"), v ("ou"), ~ ("negação") e > ("implica"), além dos parêntesis.

Após preencher os campos, o usuário clica em Verificar Definições, o que dará início ao processo de verificação léxica e sintática de tudo que foi digitado. Em

(25)

25

caso de que algum erro tenha acontecido durante as análises, uma mensagem notifica o usuário e todos os erros gerados são apresentados na área de Erros, do lado esquerdo.

Figura 24 - Tela com erro na definição dos símbolos

Caso não tenham acontecidos erros, o usuário é requisitado a inserir as informações mínimas referentes ao valor das variáveis para que possam ser realizadas inferências com relação às demais variáveis

(26)

26

As proposições são consideradas em conjunto e não separadamente, sendo que o resultado obtido através de uma das proposições é utilizado para calcular o valor de outras.

Caso alguma conclusão possa ser tirada das proposições e dos valores inseridos, as conclusões serão dispostas no campo Conclusões, mais abaixo na tela do programa.

(27)

27

Conclusão

Com este trabalho procuramos demonstrar o funcionamento de um analisador léxico no processo de construção de tradutores, reconhecendo os tokens válidos e sua interação com o analisador sintático. Assim, no capitulo 2, definimos um alfabeto e uma gramática simples (figura 2) para a construção das expressões regulares que foram apresentadas na figura 3. As figuras 4 e 5 permitem um entendimento pela simulação de uma possível execução e interpretação do analisador léxico e a verificação de como ficou sua memória e estado, e construir a tabela de símbolos apresentada na figura 6. Para representar a relação entre análise léxica e autômato, elaboramos AFN´s para reconhecer expressões regulares a partir da gramática definida na figura 2, os quais estão apresentados nas figuras 9, 11 e 13. Após o entendimento dos procedimentos gerados pelos autômatos isoladamente, pudemos uni-los e visualizar um protótipo do analisador léxico baseado na gramática da figura 2, e usando as transições épsilon para facilitar o projeto representado na figura 15, e sua codificação na figura 16. Ao final apresentamos um estudo de caso com um analisador léxico para álgebra lógica, através de um sistema de inferência em bases de conhecimento com estrutura da álgebra lógica.

(28)

28

Apêndice

Como usar o JFlex

O JFlex é uma ferramenta de geração automática de analisadores léxicos em Java. Ela lê um arquivo.flex com a definição dos tokens da sua gramática e produz uma classe .java que pode reconhecer estes tokens em um texto.

Para obter o JFlex, consulte http://jflex.de/download.html, a documentação apesar de não ser muito extensa, é suficiente para trabalhar com os casos mais simples.

Para escrever o arquivo.flex, o tutorial do site dá o exemplo de uma simplificação da própria linguagem .java, que por analogia, pode ser adaptado para outras linguagens. O código do analisador léxico construído se encontra abaixo: package br.ita.flex; import java_cup.runtime.Symbol; %class Lexer %public %unicode %cup %line %column %{

private Symbol symbol(int type) {

return new Symbol(type, yyline, yycolumn); }

private Symbol symbol(int type, Object value) { return new Symbol(type, yyline, yycolumn, value); } } IDENTIFIER = [:jletter:][:jletterdigit:]* LP = "(" RP = ")" OPERATION = "v" | "^" | ">" NOT = "~" WHITESPACE = [ \t\f] /* keywords */ <YYINITIAL>{

{IDENTIFIER} { return symbol(Sym.IDENTIFIER); } {LP} { return symbol(Sym.LP); }

{RP} { return symbol(Sym.RP); }

{OPERATION} { return symbol(Sym.OPERATION); } {NOT} { return symbol(Sym.NOT); }

{WHITESPACE} {} }

{ throw new Exception("Caractere inválido na definição do símbolo."); } Figura 27 – Exemplo do Código

(29)

29 Figura 28 – Apresentação do código no JFlex

Pode ser visto no arquivo a definição dos tokens que serão necessários à linguagem. É possível usar qualquer expressão regular para a definição dos tokens, o que facilita a verificação de strings no programa.

Utilizando as expressões regulares corretamente, é possível inclusive, utilizar o analisador para verificar algumas regras sintáticas mais simples. No entanto, o JFlex não gera o analisador sintático automaticamente, o que exige um esforço adicional do programador.

Depois de terminado o arquivo.flex, basta executar o comando:

"jflex arquivo.flex" na pasta do diretório do arquivo para que seja gerada uma classe .java representando o analisador léxico da linguagem definida.

(30)

30

(31)

31

5. Referências

Hopcroft, John E. Introdução à teoria dos autômatos, linguagens e computação / John E. Hopcroft, Rajeev Motwani, Jeffrey D. Ullman; tradução da 2ed. Original de Vandenberg D. de Souza. – Rio de Janeiro : Elsevier, 2002.

Lewis, Harry R. Elementos de teoria da computação / Harry R. Lewis e Christos H. Papadimitriou; tradução Edson Furmankiewicz. 2ed. Porto alegre : Bookman, 2000.

[RUS04] RUSSEL, Stuart J. & NORVIG, Peter. Inteligência Artificial, 2ª edição, Campus, 2004.

Referências

Documentos relacionados

ABSTRACT: The toxicological effects of crude ethanolic extracts (CEE) of the seed and bark of Persea americana have been analyzed on larvae and pupae of

Penso ter demonstrado que as doutrinas da eleição incondicional, expiação limitada, graça irresistível e perseverança incondicional dos santos, apresentadas pelo acrônimo

Varr edura TCP Window ( cont inuação) ACK- win manipulado Não Responde ACK- win manipulado ICMP Tipo 3 Firewall Negando Firewall Rejeitando Scanner de Porta... Var r edur a FI N/

Microsoft foi lançado no ano de 1995 e trouxe, pela primeira vez, o Menu Iniciar e a Barra de ferramentas.. O Windows 95 também inaugurou o conceito de “plug

O Reitor do Instituto Federal de Santa Catarina (IFSC) torna público pelo presente Edital, de acordo com as disposições legais em vigor, o início do período de manifestação de

A comunicação desenvolve o tema de aplicação do conceito gestão do risco precisamente ao risco de gestão dos recursos hídricos, focando os processos de decisão e de

Diferentemente do prazo fixado para pagamento dos precató- rios, as RPVs são depositadas pelos Tribunais Regionais Federais nos bancos ofi- ciais, mensalmente, obedecido o prazo de

15.6 - Os pedidos de esclarecimentos referentes a este processo licitatório deverão ser enviados ao pregoeiro, até três dias úteis anteriores à data fixada para abertura da