Parsec: No newline after last line and 'many'

Till Doerges
Sun, 7 Apr 2002 19:44:42 +0200

Hi folks,

when trying to write a parser for unix-style textfiles, I stumbled
across the problem having to distinguish between lines w/ a newline
character at the end and lines w/o a newline at the end (i.e. the last
line in a file doesn't have to have it).

Eventually I want to be able to use my parser 'linep' with 'many' on
no matter which text-files.

The first version I tried, was

--- snip ---
skip1 :: Parser()
skip1 = do

linep :: Parser(String)
linep = do { cs <- many (noneOf "\n")
           ; skip1
           ; return cs
--- snap ---

Well, this only works, if the last line in the file is also terminated
by '\n'. So, I tried to be smarter using:

--- snip ---
lineWith :: Parser(String)
lineWith = try (do { cs <- many (noneOf "\n")
		   ; skip1
		   ; return cs

lineWithOut :: Parser(String)
lineWithOut = many anyChar

linep :: Parser(String)
linep =     lineWith
	<|> lineWithOut
	<?> "line"
--- snap ---

But this doesn't do the trick either, instead it'll overflow my stack
if used in conjunction w/ 'many':

--- snip ---
parse (do {many linep}) "stdin" (unlines ["hallo","ballo"])
ReadOrig> parse (do {many linep}) "stdin" ("hallo\nballo")
--- snap ---

Avoiding many OTOH works fine:
--- snip ---
parse (do {linep;linep}) "stdin" (unlines ["hallo","ballo"])
Right "ballo"
ReadOrig> parse (do {linep;linep}) "stdin" ("hallo\nballo")
Right "ballo"
--- snap ---

Besides these two attempts explicitly spelled out I've made quite a
few others, but none of them did the trick.

The documentation for 'try' says that it'll only consume input upon
success, but why doesn't it work w/ 'many'?

I know about the Prelude-functions unlines, lines etc., but they don't
really help, since linep should be used within another parser of mine.

I am using Parsec as of Jan 25, 2001 (the version included w/ ghc 5.02).

Any hints/solutions/... would be greatly appreciated!

Bye -- Till