如何使用Haskell解析器解析任意列表?

时间:2014-11-23 10:13:22

标签: parsing haskell parsec

是否可以使用其中一个解析库(例如Parsec)来解析与String不同的内容?我该怎么做?

为简单起见,我们假设输入是一个整数列表[Int]。任务可能是

  • drop leading zeros
  • 将其余部分解析为模式(S+L+)*,其中S是一个小于10的数字,L是一个大于或等于十的数字。
  • 返回元组列表(Int,Int),其中fstS的乘积,sndL整数的乘积

如果有人能够展示如何编写这样的解析器(或类似的东西),那将会很棒。

2 个答案:

答案 0 :(得分:6)

是的,正如user5402指出的那样,Parsec可以解析Stream的任何实例,包括任意列表。由于没有预定义的令牌解析器(就像文本一样),您必须使用例如自己的(myToken)自行滚动。 tokenPrim

我唯一觉得有点尴尬的是处理“源位置”。 SourcePos是一个抽象类型(而不是类型类),并迫使我使用其“文件名/行/列”格式,这在这里感觉有点不自然。

无论如何,这里是代码(为简洁而没有跳过前导零)

import Text.Parsec

myToken ::  (Show a) => (a -> Bool) -> Parsec [a] () a
myToken test = tokenPrim show incPos $ justIf test where
  incPos pos _ _ = incSourceColumn pos 1
  justIf test x = if (test x) then Just x else Nothing

small = myToken  (< 10)
large = myToken  (>= 10)

smallLargePattern = do
  smallints <- many1 small
  largeints <- many1 large
  let prod = foldl1 (*)
  return (prod smallints, prod largeints)

myIntListParser :: Parsec [Int] () [(Int,Int)]
myIntListParser = many smallLargePattern

testMe :: [Int] -> [(Int, Int)]
testMe xs = case parse myIntListParser "your list" xs of
  Left err -> error $ show err
  Right result -> result

全力以赴:

*Main> testMe [1,2,55,33,3,5,99]
[(2,1815),(15,99)]
*Main> testMe [1,2,55,33,3,5,99,1]
*** Exception: "your list" (line 1, column 9):
unexpected end of input

请注意错误消息中的笨拙的行/列格式

当然可以写一个函数sanitiseSourcePos :: SourcePos -> MyListPosition

答案 1 :(得分:3)

很有可能让Parsec使用[a]作为流类型,但解析器组合器背后的想法实际上非常简单,并且滚动自己的库并不是很困难。

我建议的一个非常容易获得的资源是Graham Hutton和Erik Meijer的Monadic Parsing in Haskell

事实上,现在Erik Meijer正在edx.org (link)教授介绍Haskell /函数式编程课程,而讲座7则是关于函数解析器的。正如他在讲座的介绍中所述:

  

“......没有人可以在没有编写自己的解析器组合库的情况下遵循掌握函数式编程的道路。我们首先解释解析器是什么以及它们如何被自然地视为副作用函数。接下来我们定义一个数字用于组合解析器的基本解析器和高阶函数....“