Поскольку ваш синтаксический анализатор работает со строкой за раз, вам даже не нужно использовать attoparsec-iteratee.Я бы написал это так:
import Data.Iteratee as I
import Data.Iteratee.Char
import Data.Attoparsec as A
parser :: Parser ParseOutput
type POut = Either String ParseOutput
processLines :: Iteratee ByteString IO [POut]
processLines = joinI $ (enumLinesBS ><> I.mapStream (A.parseOnly parser)) stream2list
Ключом к пониманию этого является "enumeratee", который является просто термином итерируемого для преобразователя потока.Он принимает потоковый процессор (iteratee) одного типа потока и преобразует его для работы с другим потоком.И enumLinesBS
, и mapStream
являются перечислителями.
Чтобы отобразить ваш синтаксический анализатор на несколько строк, достаточно mapStream
:
i1 :: Iteratee [ByteString] IO (Iteratee [POut] IO [POut]
i1 = mapStream (A.parseOnly parser) stream2list
Вложенные итерации просто означают, что это преобразует поток[ByteString]
для потока [POut]
, и когда запускается последний итератор (stream2list), он возвращает этот поток как [POut]
.Так что теперь вам просто нужен итеративный эквивалент lines
для создания этого потока [ByteString]
, что и делает enumLinesBS
:
i2 :: Iteratee ByteString IO (Iteratee [ByteString] IO (Iteratee [POut] m [POut])))
i2 = enumLinesBS $ mapStream f stream2list
Но эту функцию довольно громоздко использовать из-за всехвложенности.Что нам действительно нужно, так это способ передачи выходных данных напрямую между потоковыми конвертерами, и в конце концов все упростится до одного итератора.Для этого мы используем joinI
, (><>)
и (><>)
:
e1 :: Iteratee [POut] IO a -> Iteratee ByteString IO (Iteratee [POut] IO a)
e1 = enumLinesBS ><> mapStream (A.parseOnly parser)
i' :: Iteratee ByteString IO [POut]
i' = joinI $ e1 stream2list
, что эквивалентно тому, как я написал это выше, с e1
встроенным.
Естьвсе еще важный элемент, оставшийся все же.Эта функция просто возвращает результаты разбора в виде списка.Обычно вы хотите сделать что-то еще, например, объединить результаты со сгибом.
edit: Data.Iteratee.ListLike.mapM_
часто полезно для создания потребителей.В этот момент каждый элемент потока является результатом разбора, поэтому, если вы хотите распечатать их, вы можете использовать
consumeParse :: Iteratee [POut] IO ()
consumeParse = I.mapM_ (either (\e -> return ()) print)
processLines2 :: Iteratee ByteString IO ()
processLines2 = joinI $ (enumLinesBS ><> I.mapStream (A.parseOnly parser)) consumeParse
. Это будет печатать только успешные разборы.Вы можете легко сообщать об ошибках в STDERR или обрабатывать их и другими способами.