2013-10-22 56 views
1

一個noob Haskell問題。使用Writer與Parsec的最佳方式是什麼?

我很開心用Parsec和AttoParsec編寫了幾個解析器。我現在想在解析過程中收集信息(基本上建立一個符號表),使用WriterT monad變換器似乎是一個不錯的選擇。

我現在這個工作在這個簡單的例子:

module NewParse where 

import qualified Text.ParserCombinators.Parsec as A 
import Control.Monad.Trans (lift) 
import Control.Monad.Writer (WriterT, tell, runWriterT) 
type WParser a = WriterT [String] A.Parser a 

data StoryToken = StoryToken Char deriving (Show) 

getToken :: WParser StoryToken 
getToken = do 
    tell ["hello from Writer T"] 
    c <- lift A.anyChar 
    return $ StoryToken c 

test = A.parse (runWriterT getToken) "story" "#" 

它的偉大工程。在調用ghci中試驗給了我這樣的:

*NewParse> test 
Right (StoryToken '#',["hello"]) 

什麼我申請這個時候我的解析器代碼爲解除解析器單子對每一種害怕每次調用它。我會打電話告訴比較少,但解析器功能很多。代碼將會變得更加醜陋。

是否有方法可以輕鬆地從Parsec暴露某些功能,所以我不必在代碼中使用lift?

得到成功的解決方案,我是創造我自己的函數

anyChar = lift A.anyChar 

這是很酷,但需要爲每一個我從秒差距使用函數創建類似陰影的功能。

我不介意這樣做,但只是想知道是否有一個更好的,不太鍋爐的方式來實現同樣的事情。

任何幫助感激地接受:)

+0

一般來說,當用Haskell解析時,你不會打擾創建符號表。使用Haskell編碼AST很容易,因爲代數類型對應於樹,從符號映射到除單形類型之外的任何東西都很困難。 在後期處理中,通常有一個存儲綁定的環境(類似於一個符號表),但是環境通常可以將標識符映射到單一型 - 例如,當實現功能語言時,環境將映射到表達式的標識符。 總之,我建議你嘗試用AST做更多的事情,並避免使用單獨的符號表。 –

回答

2

秒差距允許用戶通過解析器攜帶狀態只是用

addSym :: String -> Parsec String [String]() 
addSym = void . updateState . (:) -- append a string to the symbol list 

type MyParser = Parsec String [String] 

這將正確處理回溯。

+3

警告! 'ParsecT String()(Writer W)'和'WriterT W Parser'不一樣。在前者中,如果使用回溯('try')並從分支內寫入結束回溯的東西,那麼這個東西仍然會出現在最終輸出中 – luqui

+2

@luqui修復了下一個建議 – jozefg

+0

您並沒有真正「修復」它。 「翻轉它」是一個錯誤的和有害的建議(因爲@luqui指出);我認爲你應該刪除它。另見http://ro-che.info/articles/2012-01-02-composing-monads.html#even-more-interesting-example-parsec-state –

相關問題