Haskell Parsec组合子'many'应用于接受空字符串的解析器

MCH*_*MCH 5 parsing haskell runtime-error parsec

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
Run Code Online (Sandbox Code Playgroud)

当我运行parseTest items "a|b|c"上面的代码时,我收到以下错误:

*** Exception: Text.ParserCombinators.Parsec.Prim.many: 
combinator 'many' is applied to a parser that accepts an empty string.
Run Code Online (Sandbox Code Playgroud)

我相信这是与eofmany item,如果我删除eof,然后我可以得到,只要它的工作作为行不结束eof,这使得它那种无用的.

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

kos*_*kus 6

像这样的解析器many确实不能应用于接受空字符串的解析器,因为这会使语法不明确:你解析空字符串的频率是多少?选择不同的数字可能导致不同的解析结果......

你认为这many item是有问题的组合是正确的.An item是用来定义的manyTill.(游览:顺便说一下,你可以简化manyTill

item :: Parser String
item = manyTill anyChar (eol <|> delimiter <|> eof)
Run Code Online (Sandbox Code Playgroud)

不需要do或者return,也不需要try,因为三个解析器中的每一个都期望不同的第一个令牌.)解析器manyTill因此解析任意数量的字符,然后是a eol,a delimiter或an eof.现在,eoldelimiter实际消耗至少一个字符,当他们成功,但eof没有.解析器eof在输入结束时成功,但可以多次应用.例如,

ghci> parseTest (do { eof; eof }) ""
()
Run Code Online (Sandbox Code Playgroud)

它不消耗任何输入,因此可以item在空字符串上(在输入结束时)成功,从而导致模糊.

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