2

我正在使用解析库 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

如何正确实现这一目标?

4

3 回答 3

6

如果您将many(或many1)应用于接受(以及其他)空字符串的解析器,则您的语法不明确。空字符串可以被任意识别,导致不同的解析树。

在这种情况下,line接受空字符串,并many1根据 来实现many,因此会触发异常。在您的情况下,解决方案可能是确保line始终使用至少一个字符。

于 2013-09-18T16:30:35.093 回答
6

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

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

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

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

line = many $ noneOf "\n"

如果文件以换行符结尾,这将在末尾产生一个空行。

于 2013-09-19T07:30:06.823 回答
2

为此使用解析器有点过头了,因为您对行的内容没有限制。有一个库函数 ,lines可以轻松地实现您的要求。

例子:

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

如果这些行具有某种结构,则应首先对其进行编码,而不是尝试将字符串向上切 - 自底向上是编写递归下降解析器的最佳方式。

于 2013-09-18T21:58:48.053 回答