Parsec< |>解析器中的选择,错误会引发但不会转到下一个解析器 [英] Parsec <|> choice in parser, Error throws but does not go to next parser
问题描述
我正在尝试实现一个 char
方案中的识别。字符是#\< character>
或#\< character-name>
like #\ a
或#\
或#\ space
。
所以我写了下面的代码:
- ..一些代码..
data LispVal = Atom String
|列表[LispVal]
| DottedList [LispVal] LispVal
|字符串字符串
|数字整数
| Bool Bool
| Char Char派生Show
- ....更多代码...
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 =做char's'
char'p'
char'a'$ b $ 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'\\\
'
- ..更多代码...
readExpr :: String - >字符串
readExpr输入= case解析parseExprlisp
的输入Left err - > 解析错误:++显示错误
右值 - > Found value:++ show val
此时,我不知道 string
解析器位于 Parsec
中。
问题是我认识到,#\ a
但是#\ space 被视为
s
。
*主> readExpr#\\space
Found value:Char's'
为了解决这个问题,我将 parseChar
更改为
parseChar ::解析器LispVal
parseChar = liftM Char(parseSpecialCharNotation< |> parseSingleChar)
问题解决了,但现在它给了我正常字符的错误 -
* Main> readExpr#\\\ s
解析错误:\lisp \(第1行,第4列):\ \\ n \\ n \\ n \\结尾输入\\ \\ \\\p \
为什么会发生这种情况?它不应该被转移到 parseSingleChar
作为 parseSpecialCharNotation
失败吗?
Full code at: Gist
解析器被称为预测性的,因为只有当解析器p没有消耗任何输入时才会尝试q(即,前瞻为1)。
在你的情况下,两个解析在失败之前消耗#\\
,所以不能评估其他选择。您可以使用 try
确保回溯按预期工作:
解析器
try p
的行为类似于解析器p
,除非它假装发生错误时没有消耗任何输入。 p>
类似于下一个:
尝试parseSpecialCharNotation< |> parseSingleChar
注意:提取#\\
出分析器,否则你做两次相同的工作。类似于下一个:
do
字符串#\\
try parseSpecialCharNotation< |> parseSingleChar
另外,您可以使用 string
combinator而不是一系列 char
解析器。
I am learning haskell with Write yourself a scheme
.
I am currently trying to implement a char
recognition in scheme. A char is #\<character>
or #\<character-name>
like #\a
or #\
or #\space
.
So i wrote the following code :
-- .. 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
At this moment, i did not know about the string
parser in Parsec
.
The problem is that i recognizes, #\a
but #\space
is treated as a s
.
*Main> readExpr "#\\space"
"Found value: Char 's'"
To resolve this problem, i changed parseChar
as
parseChar :: Parser LispVal
parseChar = liftM Char (parseSpecialCharNotation <|> parseSingleChar)
but earlier problem is solved, but now it gives me errors with normal characters as -
*Main> readExpr "#\\s"
"Parse Error: \"lisp\" (line 1, column 4):\nunexpected end of input\nexpecting \"p\""
Why is that happening ? Should not it had moved to parseSingleChar
as parseSpecialCharNotation
failed ?
Full code at: Gist
From the 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).
In your case both the parses consume "#\\"
before failing, so the other alternative can't be evaluated. You can use try
to ensure backtracking works as expected:
The parser
try p
behaves like parserp
, except that it pretends that it hasn't consumed any input when an error occurs.
Something like the next:
try parseSpecialCharNotation <|> parseSingleChar
Side note: is it better to extract "#\\"
out of the parsers because otherwise you are doing the same work twice. Something like the next:
do
string "#\\"
try parseSpecialCharNotation <|> parseSingleChar
Also, you can use string
combinator instead of a series of char
parsers.
这篇关于Parsec< |>解析器中的选择,错误会引发但不会转到下一个解析器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!