如何使用Parsec解析一行?

时间:2013-09-18 14:32:27

标签: haskell parsec

我正在使用解析库Parsec来解析一些文本。 我只需要解析行,这些行是任意字符的字符串,当它位于字符串的末尾时以'\ n'或eof结尾。 致电parseHS'时,我收到的投诉是Exception: Text.ParserCombinators.Parsec.Prim.many: combinator 'many' is applied to a parser that accepts an empty string.

parseHS' :: String -> Either ParseError [String]
parseHS' input = parse hsFile' "(unknown)" input

hsFile' :: GenParser Char st [String]
hsFile' = do
    many1 line

line :: GenParser Char st String
line = do
    result <- many (noneOf "\n")
    optional newline
    return result

如何才能正确实现?

3 个答案:

答案 0 :(得分:6)

如果将many(或many1)应用于接受(以及其他)空字符串的解析器,则会出现含糊不清的语法。可以经常任意识别空字符串,从而产生不同的解析树。

在这种情况下,line接受空字符串,many1many表示,因此会触发异常。在您的情况下,解决方案可能是确保line始终消耗至少一个字符。

答案 1 :(得分:6)

当然,如果您只需要按行拆分输入,则可以使用lines

Parsec中的

sepEndBy执行您想要的操作 - 将输入拆分为由给定分隔符分隔的已解析实体列表,可选地以它或eof结尾。

line的语法允许解析器为任何输入生成永无止境的行流。这可以通过在外部对换行做出关于换行的决定来解决:

hsFile' = do
        x <- line
        xs <- many $ do
                newline
                line
        eof
        return (x:xs)

line = many $ noneOf "\n"

如果文件以换行符结尾,则此行将在末尾生成一个空行。

答案 2 :(得分:1)

使用解析器是不合适的,因为你对行的内容没有限制。有一个库函数lines,可以大大减少你所要求的内容。

示例:

lines "Hello there\neveryone,\nhere are some lines,"
> ["Hello there", "everyone,", "here are some lines,"]

如果这些行有一些结构,你应该首先编写代码,而不是试图将字符串剪掉 - 自下而上是编写递归下降解析器的最佳方法。