parsers - decom-ufop | início · parsers ... = mkdata d m a where d = 10*(digittoint d1) +...
TRANSCRIPT
1/74
. . . . . .
Programação Funcional
Aula 10
Parsers
José Romildo Malaquias
Departamento de ComputaçãoUniversidade Federal de Ouro Preto
2011.2
2/74
. . . . . .
1 Parsers
2 Definindo um tipo para parsers
3 Parsers básicos
4 Combinadores de parsers
5 Mônadas
6 Mais combinadores de parsers
7 Calculadora
3/74
. . . . . .
Layout
1 Parsers
2 Definindo um tipo para parsers
3 Parsers básicos
4 Combinadores de parsers
5 Mônadas
6 Mais combinadores de parsers
7 Calculadora
4/74
. . . . . .
Análise sintática ou parsing
▶ Análise sintática (também conhecida pelo termo em inglês parsing) é oprocesso de analisar um texto para determinar sua estrutura lógica.
▶ O texto pode descrever, por exemplo, uma expressão aritmética, umprograma de computador, ou um banco de dados.
▶ A análise sintática transforma o texto em uma estrutura de dados (em geraluma árvore) que
captura a hierarquia implícita no texto, e
é conveniente para processamento posterior.
▶ Por exemplo, a análise sintática de uma expressão aritmética pode produziruma árvore com constantes nas folhas, e operadores nos nós internos.
...1+2*3.
...+
.
.
1 .
.
×
.
.
2 .
.
3
. parsing
5/74
. . . . . .
Parsers
▶ Analisador sintático ou parser é um objeto que faz análise sintática de umtexto para determinar a sua estrutura lógica.
▶ Um parser transforma uma string em um valor de um determinado tipo.
...texto .. parser .. estrutura de dados
6/74
. . . . . .
Layout
1 Parsers
2 Definindo um tipo para parsers
3 Parsers básicos
4 Combinadores de parsers
5 Mônadas
6 Mais combinadores de parsers
7 Calculadora
7/74
. . . . . .
Como representar um parser em Haskell?
▶ Definir o tipo polimórfico
Parser a
ou seja, o tipo dos parsers que transformam uma string em um valor do tipoa.
▶ Definir uma função para aplicar um parser a uma string:
parse :: Parser a -> String -> a
▶ Definir parsers básicos.
▶ Definir combinadores de parsers, que permitem construir novos parsersusando parsers mais simples.
8/74
. . . . . .
Um parser é uma função
▶ Um parser pode ser representado por uma função que recebe uma string eretorna um valor que é construído a partir da string.
type Parser a = String -> a
▶ Como um parser é uma função, para aplicá-lo basta aplicar a funçãodiretamente à string de entrada:
parse :: Parser a -> String -> aparse p s = p s
9/74
. . . . . .
Um parser é uma função (cont.)
▶ Exemplo:parser para datas no formato: DD/MM/AAAAdata Data = MkData Int Int Int
deriving (Show)
pData :: Parser DatapData :: Parser DatapData [d1,d2,’/’,m1,m2,’/’,a1,a2,a3,a4]
| all isDigit [d1,d2,m1,m2,a1,a2,a3,a4] = MkData d m awhered = 10*(digitToInt d1) + (digitToInt d2)m = 10*(digitToInt m1) + (digitToInt m2)a = 1000*(digitToInt a1) + 100*(digitToInt a2) +
10*(digitToInt a3) + (digitToInt a4){- isDigit e digitToInt estão definidos
no módulo Data.Char -}
parse pData ”18/11/2011” ⇝ MkData 18 11 2011parse pData ”18/11/2011 13:30” ⇝ ERROparse pData ”8/1/2012” ⇝ ERROparse pData ”d5/m9/2012” ⇝ ERRO
10/74
. . . . . .
O tipo dos parsers deve ser um novo tipo
▶ A definição de tipo dada
type Parser a = String -> a
é inadequada porque queremos um novo tipo, distinto de todos os outrostipos, porém isomorfo ao tipo das funções de String em a.
▶ Definindo o tipo dos parsers com newtype:newtype Parser a = MkP (String -> a)
▶ A função que aplica um parser a uma string pode ser definida como:
parse :: Parser a -> String -> aparse (MkP fun) text = fun s
11/74
. . . . . .
O tipo dos parsers deve ser um novo tipo (cont.)
▶ Exemplo:parser para datas no formato: DD/MM/AAAApData :: Parser DatapData = MkP f
wheref [d1,d2,’/’,m1,m2,’/’,a1,a2,a3,a4]
| all isDigit [d1,d2,m1,m2,a1,a2,a3,a4] = MkData d m awhere
d = 10*(digitToInt d1) + (digitToInt d2)m = 10*(digitToInt m1) + (digitToInt m2)a = 1000*(digitToInt a1) + 100*(digitToInt a2) +
10*(digitToInt a3) + (digitToInt a4)
parse pData ”18/11/2011” ⇝ MkData 18 11 2011parse pData ”18/11/2011 13:30” ⇝ ERROparse pData ”8/1/2012” ⇝ ERROparse pData ”d5/m9/2012” ⇝ ERRO
12/74
. . . . . .
Um parser pode não usar toda a entrada
▶ A última definição dada para o tipo dos parsers
newtype Parser a = MkP (String -> a)
ainda não é adequada, pois ela não prevê a possibilidade desequenciamento de parsers.
▶ Um parser pode usar apenas parte (um prefixo) da string dada, devendoentão retornar:
o valor construído a partir do prefixo, e
o sufixo da string que não foi consumido.
Assim o resultado pode ser representado por um par.
newtype Parser a = MkP (String -> (a,String))
▶ Isto permite a outro parser continuar a análise usando o restante da string(sufixo) que não foi utilizado.
▶ Adaptando o tipo da função que aplica um parser a uma string:
parse :: Parser a -> String -> (a,String)parse (MkP fun) text = fun s
13/74
. . . . . .
Um parser pode não usar toda a entrada (cont.)
▶ Exemplo:parser para datas no formato: DD/MM/AAAApData :: Parser DatapData = MkP f
wheref (d1:d2:’/’:m1:m2:’/’:a1:a2:a3:a4:resto)
| all isDigit [d1,d2,m1,m2,a1,a2,a3,a4]= (MkData d m a,resto)
whered = 10*(digitToInt d1) + (digitToInt d2)m = 10*(digitToInt m1) + (digitToInt m2)a = 1000*(digitToInt a1) + 100*(digitToInt a2) +
10*(digitToInt a3) + (digitToInt a4)
parse pData ”18/11/2011” ⇝ (MkData 18 11 2011,””)parse pData ”18/11/2011 13:30” ⇝ (MkData 18 11 2011,” 13:30”)parse pData ”8/1/2012” ⇝ ERROparse pData ”d5/m9/2012” ⇝ ERRO
14/74
. . . . . .
Um parser pode suceder ou falhar
▶ Um parser pode suceder ou falhar ao ser aplicado a uma string, uma vezque pode não ser possível construir um valor do tipo desejado a partir dastring usando o parser.
▶ Assim é desejável que o resultado do parser indique a falha ou o sucesso,juntamente com o valor resultante quando houver sucesso.
▶ Para indicar sucesso ou falha podemos usar o construtor de tipo Maybe doprelúdio:
data Maybe a = Nothing | Just a
▶ Portanto o tipo dos parsers deve refletir esta possibilidade:
newtype Parser a = MkP (String -> Maybe (a,String))
▶ A função que aplica um parser a uma string deve ter o seu tipo adaptado aesta nova definição:
parse :: Parser a -> String -> Maybe (a,String)parse (MkP fun) text = fun text
ou de forma mais concisa:parse (MkP fun) = fun
15/74
. . . . . .
Um parser pode suceder ou falhar (cont.)
▶ Exemplo:parser para datas no formato: DD/MM/AAAApData :: Parser DatapData = MkP f
wheref (d1:d2:’/’:m1:m2:’/’:a1:a2:a3:a4:resto)
| all isDigit [d1,d2,m1,m2,a1,a2,a3,a4]= Just (MkData d m a,resto)
whered = 10*(digitToInt d1) + (digitToInt d2)m = 10*(digitToInt m1) + (digitToInt m2)a = 1000*(digitToInt a1) + 100*(digitToInt a2) +
10*(digitToInt a3) + (digitToInt a4)f _ = Nothing
parse pData ”18/11/2011” ⇝ Just (MkData 18 11 2011,””)parse pData ”18/11/2011 13:30” ⇝ Just (MkData 18 11 2011,” 13:30”)parse pData ”8/1/2012” ⇝ Nothingparse pData ”d5/m9/2012” ⇝ Nothing
16/74
. . . . . .
Layout
1 Parsers
2 Definindo um tipo para parsers
3 Parsers básicos
4 Combinadores de parsers
5 Mônadas
6 Mais combinadores de parsers
7 Calculadora
17/74
. . . . . .
Parser básico: failure
Um parser muito simples é aquele que sempre falha, independentemente daentrada.
failure :: Parser afailure = MkP ( \_ -> Nothing )
18/74
. . . . . .
Parser básico: failure (cont.)
Exemplos:
parse failure ”6123” ⇝ Nothingparse failure ”bom dia” ⇝ Nothingparse failure ”-89” ⇝ Nothingparse failure ”” ⇝ Nothing
19/74
. . . . . .
Parser básico: preturn
Outro parser muito simples é aquele que sempre sucede, resultando em um valorespecificado, independente da entrada.
preturn :: a -> Parser apreturn x = MkP ( \s -> Just (x,s) )
20/74
. . . . . .
Parser básico: preturn (cont.)
Exemplos:
parse (preturn False) ”6123” ⇝ Just (False,”6123”)parse (preturn ’#’) ”bom dia” ⇝ Justs (’#’,”bom dia”)parse (preturn ()) ”-89” ⇝ Just ((),”-89”)parse (preturn 18) ”” ⇝ Just (18,””)
21/74
. . . . . .
Parser básico: preturn (cont.)
O método sobrecarregado return da classe Monad (a ser definido em breve)será utilizado em vez da função preturn.
22/74
. . . . . .
Parser básico: item
Outro parser muito simples é aquele que obtém o primeiro caracter da string deentrada.
item :: Parser Charitem = MkP ( \s -> case s of
x:xs -> Just (x,xs)”” -> Nothing
)
23/74
. . . . . .
Parser básico: item (cont.)
Exemplos:
parse item ”6123” ⇝ Just (’6’,”123”)parse item ”bom dia” ⇝ Just (’b’, ”om dia”)parse item ”-89” ⇝ Just (’-’, ”89”)parse item ”” ⇝ Nothing
24/74
. . . . . .
Layout
1 Parsers
2 Definindo um tipo para parsers
3 Parsers básicos
4 Combinadores de parsers
5 Mônadas
6 Mais combinadores de parsers
7 Calculadora
25/74
. . . . . .
Combinando de parsers
▶ Parsers mais simples podem ser combinados de diferentes maneiras paraformar parsers mais complexos.
▶ Combinadores de parsers são funções que recebem parsers comoargumentos e produzem novos parsers a partir deles.
26/74
. . . . . .
Seqüenciamento de parsers
▶ Uma das formas mais simples de combinar parsers é o seqüenciamento.
▶ A função pseq recebe dois parsers e resulta em um novo parser que,quando aplicado:
aplica o primeiro parser na string inicial, e
se houver sucesso, aplica o segundo parser no resto da string, e
se houver sucesso, retorna o par dos valores retornados por cada um dosparsers
27/74
. . . . . .
Seqüenciamento de parsers (cont.)
pseq :: Parser a -> Parser b -> Parser (a,b)pseq p q =MkP (\s -> case parse p s of
Nothing -> NothingJust (x,s’) -> case parse q s’ of
Nothing -> NothingJust (y,s”) -> Just ((x,y),s”)
)
28/74
. . . . . .
Seqüenciamento de parsers (cont.)
Exemplos:
parse (pseq item item) ”6123”⇝ Just ((’6’,’1’),”23”)
parse (pseq item (preturn 5)) ”bom dia”⇝ Just ((’b’,5), ”om dia”)
parse (pseq item failure) ”-89”⇝ Nothing
parse (pseq item item) ””⇝ Nothing
29/74
. . . . . .
Seqüenciamento de parsers (cont.)
▶ A função pbind recebe um parser e uma função, e resulta em um novoparser que, quando aplicado a uma string:
aplica o parser dado (primeiro argumento) à string inicial, e
se houver sucesso, aplica a função dada (segundo argumento) ao resultadodo primeiro parser, obtendo um segundo parser, e
aplica este segundo parser ao resto da string
▶ Observe que:
O primeiro argumento é um parser.
O segundo argumento é uma função cujo resultado é um parser.
Esta função é aplicada ao valor retornado pelo primeiro parser.
Assim o valor produzido na aplicação do primeiro parser pode ser utilizado dameneira que for mais conveniente, através desta função.
O segundo parser é o resultado da aplicação da função dada ao valorproduzido pelo primeiro parser.
O resultado final é o resultado do segundo parser.
30/74
. . . . . .
Seqüenciamento de parsers (cont.)
pbind :: Parser a -> (a -> Parser b) -> Parser bpbind p f =
MkP (\s -> case parse p s ofNothing -> NothingJust (x,s’) -> parse (f x) s’
)
31/74
. . . . . .
Seqüenciamento de parsers (cont.)
Exemplos:
parse (pbind item (\k -> return (toUpper k))) ”h := 34;”⇝ Just (’H’,” := 34;”)
parse (pbind item (\_ -> item)) ”-89078”⇝ Just (’8’,”9078”)
parse (pbind item (\x -> preturn ”FIM”) ””⇝ Nothing
let myparser =pbind item
(\m -> pbind item (\n -> preturn [m,n]))in map (parse myparser) [”:= 34;”, ”;”]⇝ [Just (”:=”, ” 34;”), Nothing]
32/74
. . . . . .
Seqüenciamento de parsers (cont.)
▶ O método sobrecarregado (>>=) da classe Monad (a ser definido embreve) será utilizado em vez da função pbind aqui definida.
33/74
. . . . . .
Layout
1 Parsers
2 Definindo um tipo para parsers
3 Parsers básicos
4 Combinadores de parsers
5 Mônadas
6 Mais combinadores de parsers
7 Calculadora
34/74
. . . . . .
Mônadas
▶ Mônadas são estruturas que representam computações que podem serrealizadas em seqüência.
▶ O prelúdio tem uma classe para as mônadas:
class Monad m wherereturn :: a -> m a(>>=) :: m a -> (a -> m b) -> m b(>>) :: m a -> m b -> m bfail :: String -> m a
p >> q = p >>= \_ -> q
fail msg = error msg
35/74
. . . . . .
Mônadas (cont.)
▶ return xé uma computação que não faz nada e retorna o valor x
▶ p >>= fé uma computação que executa a computação p e, em seguida, aplica afunção f ao resultado de p retornando uma segunda computação, que éentão executada (permitindo assim o sequenciamento das duascomputações)
▶ p >> qé uma computação que executa p e q em seqüência; o resultado de p éignorado
▶ fail msgé uma computação que falha com a mensagem de erro msg
36/74
. . . . . .
Mônadas (cont.)
▶ O construtor de tipo IO, apresentado no capítulo sobre entrada e saída, éuma mônada.
▶ A expressão do estudada anteriormente pode ser utilizada com qualquermônada para execução de computações seqüenciais.
▶ Se necessário, reveja a seção sobre a expressão do que foi apresentada nocapítulo sobre entrada e saída.
37/74
. . . . . .
Parsers são mônadas
▶ O construtor de tipo Parser é uma mônada e representa uma computaçãoque constrói um valor a partir de uma dada string.
▶ return x é um parser que não usa a string de entrada e resulta no valorx.
▶ A função (>>=) permite o seqüenciamento de dois parsers:
p >>= f é um parser que, quando aplicado a uma string, primeiramenteaplica o parser p a esta string. Caso esta aplicação suceda resultando emum valor x, a função f é aplicada a x para obter o segundo parser daseqüência, que é então aplicado à string que restou da aplicação do primeirparser.
38/74
. . . . . .
Parsers são mônadas (cont.)
instance Monad Parser wherereturn x = MkP ( \s -> Just (x,s) )
p >>= f = MkP ( \s -> case parse p s ofNothing -> NothingJust (x,s’) -> parse (f x) s’
)
▶ Observe que os métodos return e (>>=) correspondem às funçõespreturn e pbind apresentados anteriormente.
39/74
. . . . . .
Parsers são mônadas (cont.)
▶ Exemplos:
parse (return 8) ”6123”⇝ Just (8,”6123”)
parse (return ”alpha”) ”x := 2 + 3”⇝ Just (”alpha”,”x := 2 + 3”)
parse (return ()) ”abc”⇝ Just ((),”abc”)
parse (return (0,1)) ””⇝ Just ((0,1),””)
40/74
. . . . . .
Parsers são mônadas (cont.)
parse (item >>= \c -> return (ord c - ord ’0’)) ”7123”⇝ Just (7,”123”)
let p = item >>= \x ->return [x,x]
in parse p ”2+3*4”⇝ Just (”22”,”+3*4”)
let p = item >>= \x ->item >>= \y ->return (x,y)
in parse p ”abcde”⇝ Just ((’a’, ’b’),”cde”)
let p = item >>= \x ->item >>= \y ->return [x,y]
in parse p ”a”⇝ Nothing
41/74
. . . . . .
Parsers são mônadas (cont.)
parse (item >> return 0) ”abcd”⇝ Just (0,”bcd”)
parse (item >> item) ”abcd”⇝ Just (’b’,”cd”)
parse (item >> item >> return ()) ”abcd”⇝ Just ((),”cd”)
parse (fail ”unexpected character”) ”abcd”⇝ ERROR: unexpected character
42/74
. . . . . .
Parsers são mônadas (cont.)
{- usando a expressão do -}
let p = do x <- itemitemy <- itemreturn (x,y)
in parse p ”2+3*4”⇝ Just ((’2’,’3’),”*4”)
Esta expressão é expandida pelo compilador de Haskell para:
let p = item >>= \x ->item >>item >>= \y ->return (x,y)
in parse p ”2+3*4”
43/74
. . . . . .
Parsers são mônadas (cont.)
Exercício 1
Defina o combinador de parser pseq apresentado anteriormente usando (>>=)e return.
44/74
. . . . . .
Parsers são mônadas (cont.)
Exercício 2
Defina a função psat :: (a -> Bool) -> Parser a -> Parser a querecebe uma função f e um parser p e retorna um parser que, ao ser executado,executa o parser p e, havendo sucesso, aplica f ao seu resultado e verifica ovalor obtido: se verdadeiro, o resultado de p é o resultado final; se falso, ocorreuma falha.Por exemplo:
parse (psat isAlpha item) ”Java” ⇝ Just (’J’,”ava”)parse (psat isDigit item) ”Java” ⇝ Nothingparse (psat isDigit item) ”56+3” ⇝ Just (’5’,”6+3”)parse (psat (==’#’) item) ”#java” ⇝ Just (’X’,”java”)parse (psat (==’_’) item) ”#java” ⇝ Nothing
Use as funções (>>=) e return, e a expressão condicional if.
45/74
. . . . . .
Parsers são mônadas (cont.)
Exercício 3
Defina o parser lower :: Parser Char que procura por uma letra minúsculano começo da string de entrada.Por exemplo:
parse lower ”alpha” ⇝ Just (’a’,”lpha”)parse lower ”pi-12.3” ⇝ Just (’p’,”i-12.3”)parse lower ”Pi-12.3” ⇝ Nothingparse lower ”12.3” ⇝ Nothingparse lower ”” ⇝ Nothing
Use as funções psat e Data.Char.isLower.
46/74
. . . . . .
Parsers são mônadas (cont.)
Exercício 4
Defina o parser upper :: Parser Char que procura por uma letra minúsculano começo da string de entrada.Por exemplo:
parse upper ”Alpha” ⇝ Just (’A’,”lpha”)parse upper ”Pi-12.3” ⇝ Just (’P’,”i-12.3”)parse upper ”pi-12.3” ⇝ Nothingparse upper ”12.3” ⇝ Nothingparse upper ”” ⇝ Nothing
Use as funções psat e Data.Char.isUpper.
47/74
. . . . . .
Parsers são mônadas (cont.)
Exercício 5
Defina um parser upperLower :: Parser (Char,Char) que reconhece umaletra maiúscula seguida de uma letra minúscula.Por exemplo:
parse upperLower ”Alpha” ⇝ Just ((’A’,’l’),”pha”)parse upperLower ”Xy” ⇝ Just ((’X’,’y’),””)parse upperLower ”xYz” ⇝ Nothingparse upperLower ”H” ⇝ Nothing
48/74
. . . . . .
Parsers são mônadas (cont.)
Exercício 6
Defina o parser digit :: Parser Int que procura por um dígito no começoda string de entrada, retornando o valor numérico associado em caso desucesso.Por exemplo:
parse digit ”6483” ⇝ Just (6,”483”)parse digit ”9+12” ⇝ Just (9,”+12”)parse digit ”pi/3” ⇝ Nothingparse digit ”” ⇝ Nothing
Use as funções psat, return e Data.Char.ord.
49/74
. . . . . .
Parsers são mônadas (cont.)
Exercício 7
Defina a função char :: Char -> Parser Char que recebe um caractere x everifica se x é o próximo item da string de entrada.Por exemplo:
parse (char ’J’) ”Java” ⇝ Just (’J’,”ava”)parse (char ’J’) ”Haskell” ⇝ Nothingparse (char ’*’) ”*pointer” ⇝ Just (’*’,”pointer”)parse (char ’*’) ”alfa+1” ⇝ Nothingparse (char ’&’) ”” ⇝ Nothing
Use a função psat e uma seção do operador (==).
50/74
. . . . . .
Parsers são mônadas (cont.)
Exercício 8
Defina a função string :: String -> Parser String que recebe umastring str e procura por str no começo da string de entrada.Por exemplo:
parse (string ”while”) ”while a>0” ⇝ Just (”while”,” a>0”)parse (string ”while”) ”if a>0” ⇝ Nothingparse (string ”do”) ”do c while a>0” ⇝ Just (”do”,” c while a>0”)parse (string ”do”) ”” ⇝ Nothingparse (string ””) ”x := 1” ⇝ Just (””,”x := 1”)
Use a função char e recursividade.
51/74
. . . . . .
Layout
1 Parsers
2 Definindo um tipo para parsers
3 Parsers básicos
4 Combinadores de parsers
5 Mônadas
6 Mais combinadores de parsers
7 Calculadora
52/74
. . . . . .
Alternativas
▶ A fim de podermos construir parsers mais sofisticados, precisamos de umoperador para combinar parsers alternativos.
▶ O parser p <|> q aplica o parser p à string de entrada e, caso ele falhe,aplica o parser q à mesma string de entrada.
infixl 5 <|>(<|>) :: Parser a -> Parser a -> Parser ap <|> q =
MkP ( \s -> case parse p s ofNothing -> parse q sJust x -> Just x
)
53/74
. . . . . .
Alternativas (cont.)
▶ Exemplos:
parse (char ’a’ <|> char ’b’) ”a=3”⇝ Just (’a’,”=3”)
parse (char ’a’ <|> char ’b’) ”b=4”⇝ Just (’b’,”=4”)
parse (char ’a’ <|> char ’b’) ”c=5”⇝ Nothing
parse (psat isAlpha item <|> psat isDigit) ”b=4”⇝ Just (’b’,”=4”)
parse (psat isAlpha item <|> psat isDigit) ”9”⇝ Just (’9’,””)
parse (psat isAlpha item <|> psat isDigit item) ”++”⇝ Nothing
54/74
. . . . . .
Alternativas (cont.)
Exercício 9
Defina o parser alphaNum :: Parser Char que reconhece uma letra ou umdígito.Por exemplo:
parse alphaNum ”Java” ⇝ Just (’J’,”ava”)parse alphaNum ”pi” ⇝ Just (’p’,”i”)parse alphaNum ”9*3” ⇝ Just (’9’,”*3”)parse alphaNum ”-78” ⇝ Nothingparse alphaNum ”” ⇝ Nothing
Use a função (<|>).
55/74
. . . . . .
Repetições
▶ Podemos definir um combinador de parser que repete um parser zero oumais vezes.
▶ O resultado é a lista dos resultados de cada aplicação do parser dado.
many :: Parser a -> Parser [a]many p =
do x <- p {- aplica parser pelo menos 1 vez -}xs <- many preturn (x:xs)
<|> {- ou -}return [] {- não aplica o parser -}
56/74
. . . . . .
Repetições (cont.)
▶ Exemplos:
parse (many (char ’*’)) ”***p” ⇝ Just (”***”,”p”)parse (many lower) ”pi/3” ⇝ Just (”pi”,”3”)parse (many digit) ”8712+4” ⇝ Just ([8,7,1,2],”+4”)parse (many digit) ”pi/3” ⇝ Just ([],”pi/3”)parse (many digit) ”” ⇝ Just ([],””)
57/74
. . . . . .
Repetições (cont.)
▶ Podemos definir um combinador de parser que repete um parser uma oumais vezes.
▶ O resultado é a lista dos resultados de cada aplicação do parser dado.
many1 :: Parser a -> Parser [a]many1 p = do x <- p
xs <- many preturn (x:xs)
58/74
. . . . . .
Repetições (cont.)
▶ Exemplos:
parse (many1 (char ’*’)) ”***p” ⇝ Just (”***”,”p”)parse (many1 lower) ”pi/3” ⇝ Just (”pi”,”/3”)parse (many1 digit) ”8712+4” ⇝ Just ([8,7,1,2],”+4”)parse (many1 digit) ”pi/3” ⇝ Nothingparse (many1 digit) ”IF a > 0” ⇝ Nothingparse (many1 digit) ”” ⇝ Nothing
59/74
. . . . . .
Repetições (cont.)
Exercício 10
Defina o parser natural :: Parser Integer que reconhece um númeronatural.Por exemplo:
parse natural ”0” ⇝ Just (0,””)parse natural ”192+56” ⇝ Just (192,”+56”)parse natural ”pi*4” ⇝ Nothingparse natural ”-981” ⇝ Nothing
Use a função many1.
60/74
. . . . . .
Repetições (cont.)
Exercício 11
Defina o parser identifier :: Parser String que reconhece umidentificador. Considere que um identificador é formado por uma seqüência deletras, dígitos decimais e sublinhados começando com uma letra.Por exemplo:
parse identifier ”pi+3” ⇝ Just (”pi”,”+3”)parse identifier ”alpha*(pi+3)” ⇝ Just (”alpha”,”*(pi+3)”)parse identifier ”xy_23-k” ⇝ Just (”xy_23”,”-k”)parse identifier ”J45+7” ⇝ Just (”J45”,”+7”)parse identifier ”k” ⇝ Just (”k”,””)parse identifier ”5xy” ⇝ Nothingparse identifier ”_abc*3” ⇝ Nothingparse identifier ”” ⇝ Nothing
Use as funções many e many1.
61/74
. . . . . .
Repetições (cont.)
Exercício 12
Defina o combinador de parser many usando a função many1.
62/74
. . . . . .
Ignorando brancos
▶ Em muitas aplicações, espaços em branco (seqüências de espaços,mudanças de linha e tabuladores) pode aparecer entre tokens (símboloscomo identificadores, números, palavras-chave, etc.) para tornar a leiturado texto mais fácil.
▶ O parser space reconhece espaços em branco.
space :: Parser ()space =
many (satisfy isSpace item) >> return ()
A função isSpace está definida no módulo Data.Char.
63/74
. . . . . .
Ignorando brancos (cont.)
▶ O combinador de parser token ignora espaços em branco antes e depoisdo parser dado.
token :: Parser a -> Parser atoken p =
do spacex <- pspacereturn x
64/74
. . . . . .
Ignorando brancos (cont.)
▶ Exemplos:
parse space ” xy ++” ⇝ Just ((),”xy++”)parse (token identifier) ” xy ++” ⇝ Just (”xy”,”++”)parse (token natural) ” 9876*78” ⇝ Just (9876,”*78”)parse (token (char ”&”)) ” & xy” ⇝ Just (’&’,”xy”)
65/74
. . . . . .
Layout
1 Parsers
2 Definindo um tipo para parsers
3 Parsers básicos
4 Combinadores de parsers
5 Mônadas
6 Mais combinadores de parsers
7 Calculadora
66/74
. . . . . .
Calculadora
▶ Vamos implementar uma calculadora onde o usuário digita uma expressãoartimética e em seguida o valor da expressão é exibido.
▶ Uma expressão aritmética pode ser
uma constante numérica,
uma variável,
uma operação binária (adição, subtração, multiplicação ou divisão) entre duasexpressões aritméticas, ou
uma atribuição envolvendo uma variável e uma expressão aritmética.
▶ Podemos representar as variáveis por strings.
type Variavel = String
▶ O tipo Operator representa um operador aritmético binário.
data Operator = Sum | Sub | Mul | Divderiving (Show)
67/74
. . . . . .
Calculadora (cont.)
▶ O tipo Exp representa expressões aritméticas:
data Exp = Con Double| Var Variable| Op Operator Exp Exp| Assign Variable Expderiving (Show)
▶ A memória pode ser representa por uma lista de associações que associauma variável com o seu valortype Memory = [(Variavel,Double)]
68/74
. . . . . .
Calculadora (cont.)
Exercício 13
Defina a função eval :: Memory -> Exp -> (Double,Memory) que recebeuma memória e uma expressão aritmética, e retorna um par formado pelo valorda expressão aritmética, e pela nova memória após a avaliação da expressão.Observe que:
▶ A memória é necessária para se obter o valor das variáveis que aparecemna expressão aritmética.
▶ Caso uma variável não esteja na memória, considere que o seu valor é zero.
▶ O valor de uma expressão aritmética que é uma atribuição é o valoratribuído à variável. Por exemplo, o valor da expressão x := 2 + 3 é 5.
▶ O resulado é formado por um par contendo uma memória porque quando aexpressão aritmética sendo avaliada contém atribuições, uma novamemória é formada para refletir o efeito da atribuição.
69/74
. . . . . .
Calculadora (cont.)
Exercício 14
Defina o parser pfactor :: Parser Exp que reconhece as formas primáriasde expressões aritméticas:
▶ constantes numéricas
▶ variáveis
▶ aplicação do menos unário (operador prefixo que muda o sinal de umaexpressão)
▶ expressões parentetizadas
70/74
. . . . . .
Calculadora (cont.)
Exercício 15
Defina o parser pterm :: Parser Exp que reconhece as expressõesaritméticas que podem ser termos de uma adição ou subtração.Um termo é um fator seguido pelo resto do termo, sendo que o resto do termopode ser:
▶ um operador multiplicativo (multiplicação ou divisão) seguido de outro fatore de outro resto de termo, ou
▶ vazio
71/74
. . . . . .
Calculadora (cont.)
Exercício 16
Defina o parser pbasic :: Parser Exp que reconhece as expressõesaritméticas básicas que podem ser usadas em uma atribuiçãoEstas expressões são formadas por um termo seguido pelo resto da expressãobásica, sendo que o resto da expressão básica pode ser:
▶ um operador aditivo (adição ou subtração) seguido de outro termo e deoutro resto de expressão básica, ou
▶ vazio
72/74
. . . . . .
Calculadora (cont.)
Exercício 17
Defina o parser pexp :: Parser Exp que reconhece as expressõesaritméticas mais gerais, incluindo atribuições.Uma expressão aritmética é uma atribuição ou uma expressão básica.
73/74
. . . . . .
Calculadora (cont.)
Exercício 18
Defina a operação main :: IO () que lê as expressões fornecidas pelousuário, calcula e exibe os seus valores.
74/74
. . . . . .
Fim