metaclass: (Default)
metaclass ([personal profile] metaclass) wrote2011-11-01 12:48 pm
Entry tags:

attoparsec-enumerator, забавное

Три парсера :

--построчный парсер
iterParseLine :: Monad m => E.Iteratee BS.ByteString m BS.ByteString
iterParseLine = iterParser parseLine

--парсер всех строк сразу
iterParseLines :: Monad m => E.Iteratee BS.ByteString m [BS.ByteString]
iterParseLines = iterParser $ many parseLine

--парсер из Enumeratee
enumParseLines :: Monad m => E.Enumeratee BS.ByteString BS.ByteString m b
enumParseLines = E.sequence $ iterParser $ parseLine



и их использование:

parseHandleByLine :: IO.Handle -> IO ()
parseHandleByLine h = loop
      where loop = do s<-E.run_ $ enumHandle1 h E.$$ iterParseLine
                      BS.putStrLn s
                      loop


testIterParseLines :: IO.Handle -> IO ()
testIterParseLines = do
   answers <- E.run_ $ enumHandle1 h E.$$ iterParseLines
   mapM BS.putStrLn answers


testEnumParseLines :: IO.Handle -> IO ()
testEnumParseLines h = E.run_ $ enumHandle1 h E.$= enumParseLines E.$$ printLnI



Второй и третий работают. Первый глючит - пропускает куски потока. Потому как после разбора не сохраняет остаток буфера, прочитанного енумератором и не отдает его при продолжении, а начинает разбор уже со свежепрочитанного буфера.
А нужен именно первый, для того, чтобы посылать по одной команды на девайс и читать и парсить ответы. Т.е. один цикл работы связки enumerator $$ iteratee на каждый ответ от девайса.
Либо использовать не enumerator, а более сложную реализацию - пакет iteratee, который умеет из Iteratee посылать команды обратно в Enumerator, используя исключения и их перехват.
Либо забить и сделать в лоб, т.к. тут на самом деле Iteratees нужны только для сушения мозга и удобства комбинирования парсеров и прочих фильтров потоков.

[identity profile] gds.livejournal.com 2011-11-01 10:17 am (UTC)(link)
чтобы по одной команде -- перед собственно чтением сделать lift io-действия в итерат, и натравить итерат на входной поток. Перед чтением оно выполнит io-действие.

[identity profile] metaclass.livejournal.com 2011-11-01 10:21 am (UTC)(link)
О, я как раз сейчас это делаю :)

[identity profile] metaclass.livejournal.com 2011-11-01 11:11 am (UTC)(link)
Заработало таки. Там кроме того что IO для посылки команды нужно было в Iteratee поднять, я еще и парсер неправильно написал - он по завершению команды продолжал ждать данных и валился на пришедшей по таймауту пустой строке (в serialport с обработкой ошибок и таймаутов вообще все печально, надо переделывать).

[identity profile] gds.livejournal.com 2011-11-01 11:15 am (UTC)(link)
интересны результаты.

кстати, примерно понятно, как за'loop'ить всё это мероприятие, или ещё надо думать?

[identity profile] metaclass.livejournal.com 2011-11-01 11:39 am (UTC)(link)
Тут непонятно, как корректно из цикла выйти. Потому как enumSerial работает как бесконечный енумератор без EOF.
Впрочем, наверно и iteratee может завершить работу, по идее.

[identity profile] metaclass.livejournal.com 2011-11-01 11:42 am (UTC)(link)
А, цикл там выглядит так:
iterDevice send = loop
   where loop = do liftIO $ do setCursorPosition 0 0   
                               send "PWR"                
                   s <- iterParseLine                   
                   liftIO $ BS.putStrLn s 
                   loop

[identity profile] gds.livejournal.com 2011-11-01 11:52 am (UTC)(link)
правильный enumerator должен завершить работу, если iteratee закончил (с результатом или с ошибкой).
То есть, в зависимости от условий, нужно будет выбирать либо loop в конце, либо что-то другое. return () какой-нибудь вполне ок.