2015-06-02 33 views
2

我正在學習與Write yourself a scheme haskell。Parsec <|>解析器的選擇,錯誤拋出但不去下一個解析器

我目前正試圖在計劃中實施char識別。字符是#\<character>#\<character-name>,如#\a#\#\space

所以我寫了下面的代碼:

-- .. some code .. 
data LispVal = Atom String 
      | List [LispVal] 
      | DottedList [LispVal] LispVal 
      | String String 
      | Number Integer 
      | Bool Bool 
      | Char Char deriving Show 
-- .... More code ... 
parseChar :: Parser LispVal 
parseChar = liftM Char (parseSingleChar <|> parseSpecialCharNotation) 

parseSingleChar :: Parser Char 
parseSingleChar = do string "#\\" 
        x <- letter 
        return x 

parseSpecialCharNotation :: Parser Char 
parseSpecialCharNotation = do string "#\\" 
           x <- (parseSpace <|> parseNewline) 
           return x 

parseSpace :: Parser Char 
parseSpace = do char 's' 
       char 'p' 
       char 'a' 
       char 'c' 
       char 'e' 
       return ' ' 

parseNewline :: Parser Char 
parseNewline = do char 'n' 
        char 'e' 
        char 'w' 
        char 'l' 
        char 'i' 
        char 'n' 
        char 'e' 
        return '\n' 

-- .. some more code... 

readExpr :: String -> String 
readExpr input = case parse parseExpr "lisp" input of 
       Left err -> "Parse Error: " ++ show err 
       Right val -> "Found value: " ++ show val 

在這一刻,我不知道在Parsecstring解析器。

問題是我認識到,#\a#\space被視爲s

*Main> readExpr "#\\space" 
"Found value: Char 's'" 

要解決此問題,我改變parseChar

parseChar :: Parser LispVal 
parseChar = liftM Char (parseSpecialCharNotation <|> parseSingleChar) 

,但早期的問題是解決了,但現在它給我的錯誤與正常字符 -

*Main> readExpr "#\\s" 
"Parse Error: \"lisp\" (line 1, column 4):\nunexpected end of input\nexpecting \"p\"" 

這是爲什麼發生?難道它不應該由於parseSpecialCharNotation失敗而轉移到parseSingleChar

的完整代碼在:Gist

回答

5

documentation<|>

解析器被稱爲預測,因爲當分析器p去不消耗任何輸入q爲只嘗試(即前瞻。是1)。

在你的情況下,兩個解析在失敗之前消耗"#\\",所以不能評估其他選擇。您可以使用try確保回溯按預期工作:

解析器try p行爲就像解析器p,除了它假裝當錯誤發生時它不消耗任何輸入。

喜歡的東西了下:

try parseSpecialCharNotation <|> parseSingleChar 

邊注:是它更好地提取"#\\"出來的解析器,否則你在做同樣的工作兩次。喜歡的東西了下:

do 
    string "#\\" 
    try parseSpecialCharNotation <|> parseSingleChar 

此外,您還可以使用string組合子,而不是一系列char解析器。