Поскольку ваш синтаксический анализатор работает построчно, вам даже не нужно использовать 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", который является просто итерируемым термином для преобразователя потока. Он берет потоковый процессор (итеративный) одного типа потока и преобразует его для работы с другим потоком. И 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
.
Тем не менее, остается еще один важный элемент. Эта функция просто возвращает результаты синтаксического анализа в виде списка. Как правило, вы хотели бы сделать что-то еще, например, объединить результаты со сгибом.
редактировать: 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 или обрабатывать их другими способами.
person
John L
schedule
15.06.2011