2013-11-10 49 views
1

假設有一些解析器:嘗試從下一個特殊符號occurence解析,如果解析失敗

valid :: Parser String 
valid = string "valid" <* skipWhile (/= '\n') 

它可以被用於從多行文字越來越"valid"字符串:

> parseOnly (many $ valid <* optional endOfLine) "valid\nvalid\nvalid" 
Right ["valid","valid","valid"] 

如果有是一個行,其中valid解析器失敗的進一步文本將不會被解析:

> parseOnly (many $ valid <* optional endOfLine) "valid\ninvalid\nvalid" 
Right ["valid"] 

如何獲得Rigth["valid", "valid"]?我認爲try在這裏可能會有所幫助,但不知道如何繼續從下一行解析。

回答

3

使用秒差距:

-- parser for the rest of the line 
rest = manyTill anyChar (eof <|> char '\n' *> return()) <* optional (char '\n') 

-- change this to accept lines, but Just the valid ones 
valid :: Parser (Maybe String) 
valid = (Just <$> string "valid" <|> const Nothing <$> anyChar) <* rest 

-- filter out Nothing 
valids = catMaybes <$> many valid 

-- Run 
*Foo> runParser valids() "input" "valid1\ninvvalid2\nvalid3" 
Right ["valid","valid"] 
*Foo> runParser valids() "input" "valid1\nvalid2\nvalid3" 
Right ["valid","valid","valid"] 

在這裏,我不得不做出一個有錯誤的黑客:const Nothing <$> anyChar所以valid消耗總是至少東西,否則我可以不給many。然而,使用Maybe你可以根據需要重新編寫你的解析器(例如,強制換行符)

非常類似的方法適用於attoparsec,很抱歉破壞了你自己的喜悅。

{-# LANGUAGE OverloadedStrings #-} 
import Data.Attoparsec.Text 
import Control.Applicative 
import Data.Maybe 
import Data.Text 

-- parser for the rest of the line 
rest = skipWhile (/= '\n') <* optional endOfLine 

-- change this to accept lines, but Just the valid ones 
valid :: Parser (Maybe Text) 
valid = (Just <$> string "valid" <|> const Nothing <$> anyChar) <* rest 

-- filter out Nothing 
valids = catMaybes <$> many valid 
*Main> parseOnly valids "valid1\nvalid2\nvalid3" 
Right ["valid","valid","valid"] 
*Main> parseOnly valids "valid1\ninvalid2\nvalid3" 
Right ["valid","valid"] 
+0

'<*可選的(字符 '\ n')'部分是在'rest'冗餘。不錯的parsec解決方案,但問題是關於attoparsec。 –

+0

attoparsec非常相似。一些原語解析器命名不同,但總體結構相同。 – phadej

+0

這'可能'的方法似乎是合理的,謝謝。另外,如果你不介意的話,我刪除了所有與文本相關的包。 –