Haskell Parsec parsing chunks of string to map values

512 Views Asked by At

I'm trying to parse a string, like

AA{A}{END}}

with given map: fromList [("{",43),("}",44),("{END}",255),("A",65)],

so that desired output is: [65,65,43,65,44,255,44]

It looks like searching for longest prefix in map in straight Haskell, but how do I parse it with Parsec? It is similar to this question, but here I should return Word8 value instead of string parsed by 'choice'.

1

There are 1 best solutions below

1
On BEST ANSWER

You first need to write a parser that takes as input a (Word8, Int) tuple and returns a Parser Word8 value.

keyValParser :: (String, Word8) -> Parser Word8
keyValParser (s,v) = try (string s) >> return v

The above parser uses try against the string parser because Parsec has the nasty habit of consuming matching characters even on fail. If the try (string s) portion is successful, it returns the Word8 value from the tuple.

Now you can map your input list against that keyValParser to build the parser you're looking for:

parser :: [(String, Word8)] -> Parser [Word8]
parser = many . choice . map keyValParser

Running this parser using parseTest in GHCi yields:

> let lookup = [("{",43),("}",44),("{END}",255),("A",65)]
> parseTest (parser lookup) "AA{A}{END}}"
[65,65,43,65,44,43]

But wait! That's not quite right. The problem now is that choice stops at the first matching parser, and the string {END} first matches { and thus returns 43. You can fix this by ordering the lookup values by longest text first using sortBy (flip $ comparing (length . fst)):

parser :: [(String, Word8)] -> Parser [Word8]
parser = many . choice . map keyValParser . sortBy (flip $ comparing (length . fst))

Now you get the right results:

> let lookup = [("{",43),("}",44),("{END}",255),("A",65)]
> parseTest (parser lookup) "AA{A}{END}}"
[65,65,43,65,44,255,44]