Parsec< |>解析器中的选择,错误会引发但不会转到下一个解析器 [英] Parsec <|> choice in parser, Error throws but does not go to next parser

查看:74
本文介绍了Parsec< |>解析器中的选择,错误会引发但不会转到下一个解析器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



我正在尝试实现一个 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 parser p, 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&lt; |&gt;解析器中的选择,错误会引发但不会转到下一个解析器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆