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.
我认为它与eof
和many item
有关,如果我删除eof
,那么只要该行不以{{{}结尾,我就可以使其工作1}},这使它变得毫无用处。
我意识到我可以使用eof
,但我感兴趣的是为什么这段代码不起作用以及如何使它工作。
答案 0 :(得分:6)
像many
这样的解析器确实不能应用于接受空字符串的解析器,因为这会使语法不明确:您解析空字符串的频率是多少?选择不同的数字会导致不同的解析结果......
你认为many item
是有问题的组合是正确的。 item
的定义是manyTill
。 (游览:顺便说一下,你可以简化manyTill
到
item :: Parser String
item = manyTill anyChar (eol <|> delimiter <|> eof)
不需要do
或return
,也不需要try
,因为三个解析器中的每一个都是
期望不同的第一个令牌。)解析器manyTill
因此解析任意数量的字符,然后是eol
,delimiter
或eof
。现在,eol
和delimiter
在成功时实际上至少消耗了一个字符,但eof
却没有。解析器eof
在输入结束时成功,但可以多次应用。例如,
ghci> parseTest (do { eof; eof }) ""
()
它不消耗任何输入,从而使item
能够成功处理空字符串(在输入结束时),从而导致歧义。
要解决此问题,您确实可以重写语法并转到sepBy
之类的内容,或者您可以尝试区分正常item
s(其中eof
不允许作为结束-marker)来自最终item
(允许eof
)。
答案 1 :(得分:0)
那是因为有无数种方法可以将空字符串解析为many emptyString