Reputation: 107
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'.
Upvotes: 1
Views: 525
Reputation: 36375
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]
Upvotes: 3