Attoparsec Iteratee

Я хотел, чтобы немного узнать об Iteratees, заново реализовать созданный мной простой парсер, используя Data.Iteratee и Data.Attoparsec.Iteratee. Хотя я в значительной степени озадачен. Ниже у меня есть простой пример, который может анализировать одну строку из файла. Мой синтаксический анализатор читает по одной строке за раз, поэтому мне нужен способ передачи строк итерируемому до тех пор, пока он не будет выполнен. Я прочитал все, что нашел в гугле, но многие материалы по итерациям/перечислителям довольно продвинуты. Это часть кода, которая имеет значение:

-- There are more imports above.
import Data.Attoparsec.Iteratee
import Data.Iteratee (joinI, run)
import Data.Iteratee.IO (defaultBufSize, enumFile)

line :: Parser ByteString -- left the implementation out (it doesn't check for 
                             new line)

iter = parserToIteratee line

main = do
    p <- liftM head getArgs
    i <- enumFile defaultBufSize p $ iter
    i' <- run i
    print i'

В этом примере будет проанализирована и напечатана одна строка из файла с несколькими строками. Исходный сценарий сопоставил анализатор списку ByteStrings. Поэтому я хотел бы сделать то же самое здесь. Я нашел enumLines в Iteratee, но я не могу понять, как его использовать. Может я неправильно понимаю его назначение?


person Johanna Larsson    schedule 15.06.2011    source источник


Ответы (1)


Поскольку ваш синтаксический анализатор работает построчно, вам даже не нужно использовать 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
comment
Удивительный ответ, хотел бы я дважды проголосовать за него! Могу я попросить пример того, как написать этого потребителя? Допустим, все, что я хочу сделать, это напечатать успешные результаты синтаксического анализа, если это простой пример. - person Johanna Larsson; 16.06.2011
comment
@shintoist: я добавил это сейчас. - person John L; 16.06.2011