Haskell 秒差距解析字符串块以映射值

Haskell Parsec parsing chunks of string to map values

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

AA{A}{END}}

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

所以期望的输出是:[65,65,43,65,44,255,44]

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

解析的字符串

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

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

上面的解析器使用 try 而不是 string 解析器,因为 Parsec 有即使失败也会消耗匹配字符的坏习惯。如果 try (string s) 部分成功,它 returns 来自元组的 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} 首先匹配 {,因此 returns 43。您可以通过首先使用 sortBy (flip $ comparing (length . fst)):

按最长文本排序 lookup 值来解决此问题
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]