Reputation: 1780
I would like to solve the following task with parsec, although splitOn "\n\n"
is probably the simpler answer.
I have an inputstring like
testInput = unlines ["ab", "cd", "", "e"] -- "ab\ncd\n\ne"
The parser shall stop when encountering an empty line.
I tried this
import Text.ParserCombinators.Parsec
inputFileP :: GenParser Char st String
inputFileP = many (lower <|> delimP)
delimP :: GenParser Char st Char
delimP = do
x <- char '\n'
notFollowedBy (char '\n')
return x
This fails with unexpected '\n'
.
Why?
I was under the impression that many x
parses x
until it fails and then stops.
Upvotes: 0
Views: 262
Reputation: 370112
I was under the impression that many x parses x until it fails and then stops.
This is only the case if x
fails without consuming any input. If x
fails after consuming input, the whole parse will fail unless there's a try
somewhere (this isn't just specific to many
: x <|> y
would also fail in that case even if y
would succeed). In your case delimP
fails on the notFollowedBy (char '\n')
after already consuming the first \n
, so the whole parse fails.
To change this behaviour, you need to explicitly enable backtracking using try
like this:
delimP = try $ do
x <- char '\n'
notFollowedBy (char '\n')
return x
Alternatively, you can make it so that delimP
fails without consuming any input (and thus no need for try) by making it look ahead by two characters before matching the \n
:
delimP = do
notFollowedBy (string "\n\n")
char '\n'
Upvotes: 4