Преобразование обычного кода синтаксического анализатора attoparsec в код на основе канала/канала

Я написал следующий код синтаксического анализа, используя attoparsec:

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

Это отлично работает для файлов небольшого размера, я выполняю это так:

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

Но когда размер файла превышает 70 МБ, он потребляет тонны памяти. В качестве решения я решил использовать attoparsec-conduit. После изучения их API , я не уверен, как заставить их работать вместе. Мой анализатор имеет тип Parser Test, но sinkParser фактически принимает анализатор типа Parser a b. Меня интересует, как выполнить этот парсер в постоянной памяти? (Решение на основе каналов также приемлемо, но я не привык к Pipes API.)


person Sibi    schedule 05.06.2014    source источник


Ответы (2)


Первый параметр типа для Parser — это просто тип данных ввода (либо Text, либо ByteString). Вы можете указать свою функцию testParser в качестве аргумента для sinkParser, и она будет работать нормально. Вот краткий пример:

{-# 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)
person Michael Snoyman    schedule 05.06.2014

Вот решение pipes (при условии, что вы используете парсер на основе Text):

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)
person Gabriel Gonzalez    schedule 05.06.2014