http://www.dcc.ufrj.br/~fabiom/comp Especificao x Implementao Usamos expresses regulares para dar a especificao lxica da linguagem Mas como podemos fazer a implementao do analisador lxico a partir dessa especificao? Especificao x Implementao Usamos expresses regulares para dar a especificao lxica da linguagem Mas como podemos fazer a implementao do analisador lxico a partir dessa especificao? Autmatos finitos! Algoritmos para converter expresses regulares so conhecidos e podem ser reaproveitados, e autmatos levam a um analisador lxico bastante eficiente Autmatos Finitos Um autmato finito formado por: Um alfabeto de entrada Um conjunto de estados Um estado inicial Um conjunto de estados finais rotulados Um conjunto de transies entre estados Transies Uma transio s1 - a -> s2 quer dizer que se autmato est no estado s1 e o prximo smbolo da entrada a ento ele vai para o estado s2 Se no h mais caracteres na entrada e estamos em um estado final ento o autmato aceitou a entrada Se em algum ponto no foi possvel tomar nenhuma transio, ou a entrada acabou e no estamos em um estado final, o autmato rejeitou a entrada Graficamente Graficamente estado Graficamente estado estado inicial Graficamente estado estado inicial estado final Graficamente estado estado inicial estado final transio Graficamente estado estado inicial estado final transio a b c Graficamente (0|1)*00 Transies Uma transio uma transio que pode ser tomada espontaneamente pelo autmato, sem ler nenhum smbolo da entrada Podemos tambm construir um autmato que pode tomar mais de uma transio dado um estado e um smbolo Autmatos com transies e mltiplas transies saindo de um mesmo estado para um mesmo caractere so no-determinsticos DFA vs NFA Um DFA um autmato determinstico, um NFA no-determinstico Um DFA, dada uma entrada, toma apenas um caminho atravs dos seus estados Um NFA toma todos os caminhos possveis para aquela entrada, e aceita entrada se pelo menos um caminho termina em um estado final Funcionamento de um NFA Entrada: Estados: Funcionamento de um NFA Entrada: 1 Estados: { 0 } Funcionamento de um NFA Entrada: 1 0 Estados: { 0 } { 0, 1 } Funcionamento de um NFA Entrada: 1 0 0 Estados: { 0 } { 0, 1 } { 0, 1, 2 } Funcionamento de um NFA Entrada: 1 0 0 Estados: { 0 } { 0, 1 } { 0, 1, 2 } Aceita! Funcionamento de um NFA Entrada: 0 Estados: { 0, 1 } Funcionamento de um NFA Entrada: 0 1 Estados: { 0, 1 } { 0 } No aceita! Autmatos e linguagens DFAs, NFAs e expresses regulares todos expressam a mesma classe de conjunto de smbolos Linguagens regulares Isso quer dizer que podemos converter de um para outro DFAs so mais rpidos para executar NFAs tm representao mais compacta Expresses regulares so mais fceis de entender qual conjunto est sendo expresso Autmatos e linguagens DFAs, NFAs e expresses regulares todos expressam a mesma classe de conjunto de smbolos Linguagens regulares Isso quer dizer que podemos converter de um para outro DFAs so mais rpidos para executar NFAs tm representao mais compacta Expresses regulares so mais fceis de entender qual conjunto est sendo expresso Por isso usamos expresses regulares para a especificao, e DFAs (ou NFAs) para implementao! DFA de anlise lxica Um DFA de anlise lxica tem os estados finais rotulados com tipos de token A ideia executar o autmato at chegar no final da entrada, ou dar erro por no conseguir fazer uma transio, mantendo uma pilha de estados visitados e o token que est sendo lido Ento voltamos atrs, botando smbolos de volta na entrada, at chegar em um estado final, que vai dar o tipo do token Analisador lxico de tabela // reconhecer palavras estado = s 0 token = pilha.limpa() while (!eof && estado erro) do char = leChar() token = token + char push (estado) estado = trans(estado,char) end; // limpar estado final while (estado S A and !pilha.vazia()) do estado pilha.pop() token = token.truncaUltimo() voltaChar() end; if (estado S A ) // rtulo do estado tipo do token then return <estado, token> else return erro Uma otimizao Se visitamos um estado final ento podemos limpar a pilha, j que vamos parar nele na volta // reconhecer palavras estado = s 0 token = pilha.limpa() pilha.push(erro) while (estado erro) do char = leChar() token = token + char if estado S A then pilha.limpa() push (estado) estado = trans(estado,char) end; // limpar estado final while (estado S A and estado erro) do estado pilha.pop() token = token.truncaUltimo() voltaChar() end; if (estado S A ) // rtulo do estado tipo do token then return <estado, token> else return erro Construindo o DFA de anlise lxica Passo 1: construir um NFA para cada regra, o estado final desse NFA rotulado com o tipo do token Construo de Thompson Passo 2: combinar os NFAs em um NFA com um estado inicial que leva aos estados iniciais do NFA de cada regra via uma transio Passo 3: transformar esse NFA em um DFA, estados finais ficam com o rtulo da regra que aparece primeiro Algoritmo de construo de subconjuntos DFA da linguagem de comandos simples