解析器中的 Parsec <|> 选择,错误抛出但不转到下一个解析器
Parsec <|> choice in parser, Error throws but does not go to next parser
我正在学习 haskell Write yourself a scheme
。
我目前正在尝试在方案中实施 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
此刻,我还不知道 Parsec
中的 string
解析器。
问题是我认识到,#\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
来自 documentation for <|>
:
The parser is called predictive since q is only tried when parser p didn't consume any input (i.e.. the look ahead is 1).
在您的情况下,两个解析都在失败之前消耗 "#\"
,因此无法评估另一个替代方案。您可以使用 try
来确保回溯按预期工作:
The parser try p
behaves like parser p
, except that it pretends that it hasn't consumed any input when an error occurs.
类似于下一个:
try parseSpecialCharNotation <|> parseSingleChar
旁注:从解析器中提取 "#\"
是否更好,否则您将做同样的工作两次。类似下一个:
do
string "#\"
try parseSpecialCharNotation <|> parseSingleChar
此外,您可以使用 string
组合器代替一系列 char
解析器。
我正在学习 haskell Write yourself a scheme
。
我目前正在尝试在方案中实施 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
此刻,我还不知道 Parsec
中的 string
解析器。
问题是我认识到,#\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
来自 documentation for <|>
:
The parser is called predictive since q is only tried when parser p didn't consume any input (i.e.. the look ahead is 1).
在您的情况下,两个解析都在失败之前消耗 "#\"
,因此无法评估另一个替代方案。您可以使用 try
来确保回溯按预期工作:
The parser
try p
behaves like parserp
, except that it pretends that it hasn't consumed any input when an error occurs.
类似于下一个:
try parseSpecialCharNotation <|> parseSingleChar
旁注:从解析器中提取 "#\"
是否更好,否则您将做同样的工作两次。类似下一个:
do
string "#\"
try parseSpecialCharNotation <|> parseSingleChar
此外,您可以使用 string
组合器代替一系列 char
解析器。