Haskell Parsec解析字符串块以映射值

时间:2017-09-06 14:21:55

标签: parsing haskell parsec

我正在尝试解析一个字符串,比如

AA{A}{END}}

给定地图:fromList [("{",43),("}",44),("{END}",255),("A",65)]

以便所需的输出为:[65,65,43,65,44,255,44]

看起来在直接Haskell中搜索地图中最长的前缀,但是如何用Parsec解析它?它类似于this question,但在这里我应该返回Word8值而不是'choice'解析的字符串。

1 个答案:

答案 0 :(得分:3)

首先需要编写一个解析器,将(Word8, Int)元组作为输入,并返回Parser Word8值。

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

上面的解析器对try解析器使用string因为Parsec有一个讨厌的习惯,即使在失败时也会消耗匹配的字符。如果try (string s)部分成功,则会从元组返回Word8值。

现在,您可以根据keyValParser映射输入列表,以构建您正在寻找的解析器:

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

在GHCi中使用parseTest运行此解析器会产生:

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

但是等等!那不太对劲。现在的问题是choice在第一个匹配的解析器处停止,字符串{END}首先匹配{,因此返回43。您可以先使用lookup

按最长文本排序sortBy (flip $ comparing (length . fst))值来解决此问题
parser :: [(String, Word8)] -> Parser [Word8]
parser = many . choice . map keyValParser . sortBy (flip $ comparing (length . fst))

现在你得到了正确的结果:

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