How to store the result of parsec in a list in Haskell?

450 views Asked by At

This question might sound trivial but i am a Haskell beginner and i've read some tutorials for parsec but can't figure out how to store the result from a parser in a list (or in my case a list of list of string).

Here is the code of the parser:

-- Adapted from http://book.realworldhaskell.org/read/using-parsec.html -> ch16/csv9.hs and ch16/csv6.hs
import Text.ParserCombinators.Parsec

pgmFile = endBy line eol
line = sepBy cell (char ' ')
cell = many (noneOf " \n")

eol =   try (string "\n\r")
    <|> try (string "\r\n")
    <|> string "\n"
    <|> string "\r"
    <?> "end of line"

parsePGM :: String -> Either ParseError [[String]]
parsePGM input = parse pgmFile "(unknown)" input

main =
    do c <- getContents
       case parse pgmFile "(stdin)" c of
            Left e -> do putStrLn "Error parsing input:"
                         print e
            Right r -> mapM_ print r
{-- /snippet all --}


returnString = 
    do c <- getContents
       case parse pgmFile "(stdin)" c of
            Left e -> do putStrLn "Error parsing input:"
                         print e
            Right r -> r

The returnString function does not work but the main function accepts a ascii pgm file and parses it in a list of list of strings where every line is a list and the words are the content of the list. I want to store the resulting list in a variable to later work with it. So how can i do this?

I am really grateful for every help i can get!


Edit: The error message for the function returnString is:

pgmCH9.hs:32:24:
    Couldn't match type β€˜[]’ with β€˜IO’
    Expected type: IO ()
      Actual type: [[[Char]]]
    In the expression: r
    In a case alternative: Right r -> r

I guess this is exactly what Sarah wrote in her comment. The type in the Left and Right case must be identical. The output of the main Right case looked like the list which i want to store so that is why i tried to save it with the help of a return value.

Here is the type of parse pgmFile "(stdin)":

ghci> :type parse pgmFile "(stdin)"
parse pgmFile "(stdin)" :: [Char] -> Either ParseError [[[Char]]]

Edit2: So here is the revised code with the added content based on the comments:

-- Adapted from http://book.realworldhaskell.org/read/using-parsec.html -> ch16/csv9.hs and ch16/csv6.hs
import Text.ParserCombinators.Parsec
import System.IO 

-- 

{- A PGM file contains a header witht the type, a comment, the width and height of the picture
   and the maximum value for all pixels. The picture presists of width*height pixels, each of
   which is seperated by a space or the end-of-line character (eol). -}
--pgmFile :: Text.Parsec.Prim.ParsecT [Char] u Data.Functor.Identity.Identity [[[Char]]]
pgmFile = endBy line eol

-- Each line contains 1 or more pixels, separated by a space
--line :: Text.Parsec.Prim.ParsecT [Char] u Data.Functor.Identity.Identity [[Char]]
line = sepBy pixel (char ' ')

-- Each pixel contains of characters and is limited by space or a newline
--pixel :: Text.Parsec.Prim.ParsecT [Char] u Data.Functor.Identity.Identity [Char]
pixel = many (noneOf " \n")


--eol :: Text.Parsec.Prim.ParsecT [Char] u Data.Functor.Identity.Identity String
eol =   try (string "\n\r")
    <|> try (string "\r\n")
    <|> string "\n"
    <|> string "\r"
    <?> "end of line"

parsePGM :: String -> Either ParseError [[String]]
parsePGM input = parse pgmFile "(unknown)" input

main :: IO ()
main =
    do c <- getContents
       case parse pgmFile "(stdin)" c of
            Left e -> do putStrLn "Error parsing input:"
                         print e
            Right r -> mapM_ print r

returnString :: IO ()
returnString = 
    do c <- readFile "test_ascii.pgm"
       case parse pgmFile "(stdin)" c of
            Left e -> do putStrLn "Error parsing input:"
                         print e
            Right r -> print r
0

There are 0 answers