2017-09-12 94 views
4

給出以下類型和功能,這意味着到CSV字段的字段解析爲一個字符串:生成解析器加入結果

type Parser resultType = ParsecT String() Identity resultType 
cell :: Parser String 

我有實現以下功能:

customCell :: String -> Parser res -> Parser res 
customCell typeName subparser = 
    cell 
    >>= either (const $ unexpected typeName) 
       return . parse (subparser <* eof) "" 

雖然我不能停止思考,我不使用單子概念不亞於期望,並最終有一個更好的方式向內側的結果合併與外解析器,特別是關於它的失敗是什麼。

有沒有人知道我該怎麼做,或者這個代碼是做什麼的?

PS - 我現在意識到我的類型簡化可能不合適,也許我想要的是用Monad替換底層的Identity Monad ....不幸的是,我對Monad變換器還不夠熟悉。

PS2 - 無論如何,底層monad究竟有什麼好處?

回答

1

很遺憾,我知道Haskell沒有解析器庫或解析器生成器,它支持像這樣的垂直解析器組合。就像你寫的東西一樣好。黨!

+1

我不確定垂直分析器組成的含義。你介意擴大一點嗎? – gallais

+1

@gallais我的意思是你要求的操作:一種採用「Parser Foo [Bar]」和「Parser Bar Baz」並將其變成「Parser Foo Baz」的方法。 –

+1

哦,我明白了。用這些簡單的類型,我的目標比OP的問題更清晰。感謝您的澄清! – gallais

4

詳細闡述@Daniel Wagner的答案......方式解析器通常使用Parsec構建,您可以從解析特定字符(例如,加號或數字)的低級解析器開始,然後在頂部構建解析器他們使用組合器(如many1組合器,該組合器將讀取單個數字的解析器轉換爲讀取一個或多個數字的解析器,或解析器「一個或多個數字」,然後跟隨「加號」,接着是「一個或多個數字「)。

但是,每個解析器,無論是低級數字解析器還是更高級別的「添加表達式」解析器,都旨在直接應用於相同的輸入流。

你做什麼不通常要做的就是寫狼吞虎嚥的輸入流的數據塊產生,也就是說,一個String和另一解析器解析解析器String(而不是原來的輸入流)和嘗試將它們結合起來。這是Parsec不直接支持的那種「垂直組合」,看起來不自然和非單調。

正如在評論中指出,有一些情況下豎構圖是最乾淨的總體方法(當你有一個語言嵌入式組件或其他語言的表達式中一樣),但它沒有采取通常的做法由一個Parsec解析器。

應用程序的底線是cell解析器只生成String太專業化,無法使用。爲CSV文件更加有用秒差距的框架應該是:

import Text.Parsec 
import Text.Parsec.String 

-- | `csv cell` parses a CSV file each of whose elements is parsed by `cell` 
csv :: Parser a -> Parser [[a]] 
csv cell = many (row cell) 

-- | `row cell` parses a newline-terminated row of comma separated 
-- `cell`-expressions 
row :: Parser a -> Parser [a] 
row cell = sepBy cell (char ',') <* char '\n' 

現在,您可以編寫解析正整數自定義單元格解析器:

customCell :: Parser Int 
customCell = read <$> many1 digit 

並解析CSV文件:

> parse (csv customCell) "" "1,2,3\n4,5,6\n" 
Right [[1,2,3],[4,5,6]] 
> 

這裏,「細胞」是一個隱含的上下文,而不是將子分析器明確地將逗號分隔的單元格解析爲要提供給不同解析器的字符串,其中suppl ied cell parser被調用來在適當的位置解析底層輸入流,其中在輸入流中間的行中間會出現逗號分隔的單元格。