5
import Text.ParserCombinators.Parsec

delimiter :: Parser ()
delimiter = do char '|'
               return ()
          <?> "delimiter"


eol :: Parser ()
eol = do oneOf "\n\r"
         return ()
    <?> "end of line"

item :: Parser String
item = do entry <- manyTill anyChar (try eol <|> try delimiter <|> eof)
          return entry

items :: Parser [String]
items = do result <- many item
           return result

当我parseTest items "a|b|c"使用上面的代码运行时,出现以下错误:

*** Exception: Text.ParserCombinators.Parsec.Prim.many: 
combinator 'many' is applied to a parser that accepts an empty string.

我相信它与eofand有关系many item,如果我删除eof,那么只要该行不以 结尾,我就可以让它工作eof,这使得它有点没用。

我意识到我可以使用sepBy,但我感兴趣的是为什么这段代码不起作用以及如何使它起作用。

4

2 回答 2

6

像这样的解析器many确实不能应用于接受空字符串的解析器,因为这会使语法模棱两可:你多久解析一次空字符串?选择不同的数字会导致不同的解析结果......

您认为这many item是有问题的组合是正确的。Anitem是根据 定义的manyTill。(游览:顺便说一句,您可以简化manyTill

item :: Parser String
item = manyTill anyChar (eol <|> delimiter <|> eof)

不需要 thedoreturn,也不需要try,因为三个解析器中的每一个都期望不同的第一个标记。)因此解析器manyTill解析任意数量的字符,后跟 an eol、 adelimiter或 an eof。现在,当他们成功时eoldelimiter实际上至少消耗了一个角色,但eof没有。解析器eof在输入结束时成功,但可以多次应用。例如,

ghci> parseTest (do { eof; eof }) ""
()

它不消耗任何输入,因此可以item在空字符串上成功(在输入的末尾),从而导致歧义。

要解决这个问题,您确实可以重写您的语法并移动到类似的东西sepBy,或者您可以尝试区分正常item的 s (eof不允许作为结束标记的地方)和最终的itemeof允许的地方)。

于 2013-11-11T07:05:33.040 回答
0

那是因为有无数种方法可以将空字符串解析为many emptyString

于 2013-11-11T06:25:24.183 回答