2014-11-23 71 views
4

是否可以使用其中一個解析庫(例如Parsec)來解析與String不同的內容?我該怎麼做?如何用Haskell解析器解析任意列表?

爲了簡單起見,我們假設輸入是一個整數的列表[Int]。該任務可能是

  • 降前導零
  • 解析其餘入圖案(S+L+)*,其中S是小於10的數,並且L是一個數大於或等於10。
  • 返回記錄(Int,Int),其中fstSsnd的產品的清單是L整數

的產品這將是巨大的,如果有人可以顯示如何寫這樣的解析器(或東西類似)。

回答

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

3

很有可能有一種方法可以讓Parsec使用[a]作爲流類型,但解析器組合器背後的想法其實非常簡單,並且它不是很難推出自己的庫。

我推薦的非常方便的資源是由Graham Hutton和Erik Meijer提供的Monadic Parsing in Haskell。確實,現在Erik Meijer在edx.org (link)上講授了一個介紹Haskell /函數式編程課程,講座7講述了所有關於函數式解析器的內容。正如他在介紹了講座規定:

」 ......沒有一個可以遵循努力掌握函數式編程,而無需編寫自己的解析器組合庫的路徑首先,我們要解釋解析器是什麼,以及他們如何能自然被視爲副作用的功能。接下來,我們定義了一些基本的解析器和高階函數的結合爲解析器。......」