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

时间:2013-11-11 06:10:53

标签: 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

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

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

我认为它与eofmany item有关,如果我删除eof,那么只要该行不以{{{}结尾,我就可以使其工作1}},这使它变得毫无用处。

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

2 个答案:

答案 0 :(得分:6)

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

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

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

不需要doreturn,也不需要try,因为三个解析器中的每一个都是 期望不同的第一个令牌。)解析器manyTill因此解析任意数量的字符,然后是eoldelimitereof。现在,eoldelimiter在成功时实际上至少消耗了一个字符,但eof却没有。解析器eof在输入结束时成功,但可以多次应用。例如,

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

它不消耗任何输入,从而使item能够成功处理空字符串(在输入结束时),从而导致歧义。

要解决此问题,您确实可以重写语法并转到sepBy之类的内容,或者您​​可以尝试区分正常item s(其中eof不允许作为结束-marker)来自最终item(允许eof)。

答案 1 :(得分:0)

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