2016-03-20 135 views
2

如果我有這樣的代碼:當我測試testparser與像輸入秒差距產生奇怪的錯誤

import Text.Parsec 

ispositive a = if (a<0) then Nothing else (Just a) 

f a b = a+b 

parserfrommaybe :: String -> (Maybe c) -> Parsec a b c 
parserfrommaybe msg Nothing = fail msg 
parserfrommaybe _ (Just res) = return res  

(<!>) :: Parsec a b (Maybe c) -> String -> Parsec a b c 
(<!>) p1 msg = p1 >>= (parserfrommaybe msg)  

integermaybeparser = (ispositive <$> integer) <!> "negative numbers are not allowed" 
testparser = f <$> (integermaybeparser <* whiteSpace) <*> integermaybeparser 

這個「-1 3」它給:

Left (line 1, column 4): 
unexpected "3" 
negative numbers are not allowed 

我期望它給列1上的錯誤,並給出沒有「意外的3」句子的錯誤消息,但它似乎parsec繼續解析。

爲什麼會發生這種情況?以及如何使parsec給出我期望的錯誤信息?

回答

1

我找到了解決方案,其原因是第一個解析器即使在失敗時也會運行並消耗輸入。

的解決方案是使用lookAhead這樣的:

(<!>) :: (Monad m,Stream a m t) => ParsecT a b m (Maybe c) -> String -> ParsecT a b m c 
(<!>) p1 msg = ((lookAhead p1) >>= (parserfrommaybe msg)) *> (p1 >>= (parserfrommaybe msg)) 

如果lookAhead p1回報Nothing那麼*>第一個參數會失敗,而無需耗費因爲lookAhead輸入,現在如果lookAhead p1回報Just res那麼就無需再次成功消耗的輸入,結果將從*>的第二個參數中獲得。

ofcourse我不得不將parserfrommaybe類型註釋改爲(Monad m) => String -> (Maybe c) -> ParsecT a b m c以滿足ghc。

+0

只是要指出,這將是非常低效的 - 'p1'幾乎總是被解析兩次。問題更基本 - 如果負整數無效,那爲什麼要解析負號?如果你想專門報告負數整數錯誤(所以你必須解析負號),你應該使用'parserFail'(而不是'ispositive <$> ..' - 'Nothing'並不代表你錯誤的方式想想這裏)當你遇到一個負整數 - 這將導致一個實際的解析器失敗,正好在負整數之後。 ''不能導致解析器失敗。 – user2407038