解析器中的Parsec <|>选项,错误抛出但不转到下一个解析器

Ash*_*egi 2 parsing haskell parsec

我正在学习哈斯克尔Write yourself a scheme.

我目前正在尝试char在计划中实施认可.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
Run Code Online (Sandbox Code Playgroud)

此刻,我不知道string解析器Parsec.

问题是我承认,#\a#\space被视为一个s.

*Main> readExpr "#\\space"
"Found value: Char 's'"
Run Code Online (Sandbox Code Playgroud)

要解决这个问题,我改变parseChar

parseChar :: Parser LispVal
parseChar = liftM Char (parseSpecialCharNotation <|> parseSingleChar)
Run Code Online (Sandbox Code Playgroud)

但是早期的问题已经解决了,但是现在它给了我正常字符的错误 -

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

为什么会这样?它不应该parseSingleChar变成parseSpecialCharNotation失败吗?

完整代码:Gist

Yur*_*ras 6

文档<|>:

解析器被称为预测,因为仅在解析器p不消耗任何输入时才尝试q(即,前瞻为1).

在您的情况下,两个解析都会"#\\"在失败之前消耗,因此无法评估另一个替代方案.您可以使用try以确保回溯按预期工作:

解析器的try p行为类似于解析器p,除了它假装它在发生错误时没有消耗任何输入.

像下一个:

try parseSpecialCharNotation <|> parseSingleChar
Run Code Online (Sandbox Code Playgroud)

旁注:最好"#\\"从解析器中提取出来,否则你要做两次相同的工作.像下一个:

do
  string "#\\"
  try parseSpecialCharNotation <|> parseSingleChar
Run Code Online (Sandbox Code Playgroud)

此外,您可以使用string组合器而不是一系列char解析器.