2015-06-02 18 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解析器。