我正在使用解析库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
如何才能正确实现?
答案 0 :(得分:6)
如果将many
(或many1
)应用于接受(以及其他)空字符串的解析器,则会出现含糊不清的语法。可以经常任意识别空字符串,从而产生不同的解析树。
在这种情况下,line
接受空字符串,many1
以many
表示,因此会触发异常。在您的情况下,解决方案可能是确保line
始终消耗至少一个字符。
答案 1 :(得分:6)
当然,如果您只需要按行拆分输入,则可以使用lines
。
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,"]
如果这些行有一些结构,你应该首先编写代码,而不是试图将字符串剪掉 - 自下而上是编写递归下降解析器的最佳方法。