2014-11-09 5 views
1

У меня есть раковина и вы хотите провести синтаксический анализ с помощью attoparsec. Бывает, что я получаю результат Partial. Поэтому я подумал, что могу просто использовать leftover, чтобы поместить недостаточное содержимое обратно вверх, чтобы оно вернулось с последующим добавлением. Но новый контент не добавляется, как я надеялся. Я был бы очень признателен за любые предложения о том, как решить эту проблему. Благодаря!раковина кабелепровода с остаточным

{-# LANGUAGE OverloadedStrings #-} 

import Control.Monad.IO.Class (liftIO) 
import Data.Conduit 
import qualified Data.Conduit.List as CL 
import qualified Data.ByteString.Char8 as BS 
import Data.Attoparsec.Char8 


main = (CL.sourceList [BS.pack "foo", BS.pack "bar"]) $$ sink -- endless loop 

-- this works: 
-- main = (CL.sourceList [BS.pack "foobar"]) $$ sink 

sink :: Sink BS.ByteString IO() 
sink = awaitForever $ \str -> do 
        liftIO $ putStrLn $ BS.unpack str -- debug, will print foo forever. 
        case (parse (string "foobar") str) of 
         Fail _ _ _ -> do 
            liftIO $ putStr $ "f: " ++ BS.unpack str 
            sink 
         Partial _ -> do 
            leftover str 
            sink 
         Done rest final -> do 
              liftIO $ putStr $ "d: " ++ show final ++ " // " ++ show rest 
              sink 
+1

См. Также ['Data.Conduit.Attoparsec'] (https://hackage.haskell.org/package/conduit-extra-1.1.4.1/docs/Data-Conduit-Attoparsec.html). –

+0

Спасибо! Это может пригодиться! Но можете ли вы объяснить, почему мое решение не работает? Я не возражаю дважды. Я думал, что лефуры могут работать так. – Schoon

+0

Не могли бы вы привести свой код в [автономный] (http://sscce.org/) пример с тестовым примером, показывающим проблему? –

ответ

0

Имейте в виду, что Conduit не имеет понятия о конкатенации выхода. Так что происходит:

  • Трубопровод получает частичный вход.
  • Недостаточно синтаксического анализа.
  • Вы возвращаете его в качестве остатка.
  • Трубопровод снова считывает то же, что вы положили обратно.
  • И это продолжается навсегда.

Если вы действительно хотите преследовать направление повторного анализа парсера, вам необходимо убедиться, что каждый раз, когда вы возвращаете оставшееся значение, оно больше, чем в предыдущее время. Таким образом, вы сделали бы что-то вроде этого: если парсер не закончит, прочитайте дополнительный ввод, соедините его с уже введенным вами вводом, отодвиньте его назад и повторите попытку.

Обратите внимание, что описанная выше процедура имеет сложность O (n^2), что будет особенно проблематично, если ваш парсер удастся после использования большого блока данных. Если вы будете получать один символ за раз (что может случиться), и парсеру нужно потреблять 1000 символов, вы получите что-то вроде 500000 шагов обработки. Поэтому я настоятельно рекомендую использовать либо предоставленное связывание между Conduit и Attoparsec, либо, если вы хотите сделать это самостоятельно, правильно используйте продолжение, предоставляемое Partial.

+0

Спасибо, много! – Schoon

2

Идея «Частичная» заключается в том, что она возвращает вам функцию продолжения; то есть, как только у вас будет больше ввода, вы вызываете продолжение с этим вводом. Попытка выталкивать оставшиеся строки обратно во входной поток в лучшем случае расточительна, потому что вы неоднократно разбираете первый бит ввода.

Вам необходимо написать свою функцию, чтобы принять функцию анализатора в качестве параметра. Тогда ваш частный случай следует читать

Partial c -> sink c 

Это приведет к «слив», чтобы ждать больше ввода, а затем передать его в функцию «с», который продолжит разбор нового входа, с которого она была прервана.