2014-06-05 14 views
5

Napisałem następujący kod parsowania przy użyciu attoparsec:Konwersja normalny kod attoparsec parsera do przewodu/rury oparty

data Test = Test { 
    a :: Int, 
    b :: Int 
    } deriving (Show) 

testParser :: Parser Test 
testParser = do 
    a <- decimal 
    tab 
    b <- decimal 
    return $ Test a b 

tParser :: Parser [Test] 
tParser = many' $ testParser <* endOfLine 

Działa to dobrze dla małych plików wielkości, mogę wykonać to tak:

main :: IO() 
main = do 
    text <- TL.readFile "./testFile" 
    let (Right a) = parseOnly (manyTill anyChar endOfLine *> tParser) text 
    print a 

Ale gdy rozmiar pliku jest większy niż 70 MB, zużywa mnóstwo pamięci. Jako rozwiązanie, pomyślałem, że użyłbym attoparsec-conduit. Po przejściu przez ich API, nie jestem pewien, jak sprawić by ze sobą współpracowały. Mój analizator składni ma typ Parser Test, ale jest to sinkParser, który akceptuje parser typu Parser a b. Interesuje mnie, jak wykonać ten parser w stałej pamięci? (Rozwiązanie oparte rury A jest również do przyjęcia, ale nie będę wykorzystane do rur API).

Odpowiedz

5

Pierwszy parametr typem Parser tylko typ danych wejściowych (albo Text lub ByteString). Możesz podać swoją testParser funkcję jako argument do sinkParser i będzie działać dobrze. Oto krótki przykład:

{-# LANGUAGE OverloadedStrings #-} 
import   Conduit     (liftIO, mapM_C, runResourceT, 
              sourceFile, ($$), (=$)) 
import   Data.Attoparsec.Text (Parser, decimal, endOfLine, space) 
import   Data.Conduit.Attoparsec (conduitParser) 

data Test = Test { 
    a :: Int, 
    b :: Int 
    } deriving (Show) 

testParser :: Parser Test 
testParser = do 
    a <- decimal 
    space 
    b <- decimal 
    endOfLine 
    return $ Test a b 

main :: IO() 
main = runResourceT 
    $ sourceFile "foo.txt" 
    $$ conduitParser testParser 
    =$ mapM_C (liftIO . print) 
5

Oto rozwiązanie pipes (zakładając że używasz Text opartych parser):

import Pipes 
import Pipes.Text.IO (fromHandle) 
import Pipes.Attoparsec (parsed) 
import qualified System.IO as IO 

main = IO.withFile "./testfile" IO.ReadMode $ \handle -> runEffect $ 
    for (parsed (testParser <* endOfLine) (fromHandle handle)) (lift . print)