Проблема с неполным вводом при использовании attoParsec
-
24-10-2019 - |
Вопрос
Я преобразую какой -нибудь функционирующий код Haskell, который использует ParSec, чтобы вместо этого использовать AttoParsec в надежде получить лучшую производительность. Я внес изменения и все компилизируется, но мой анализатор работает неправильно.
Я анализирую файл, который состоит из различных типов записей, по одному на строку. Каждая из моих отдельных функций для анализа записи или комментария работает правильно, но когда я пытаюсь написать функцию, чтобы скомпилировать последовательность записей, анализатор всегда возвращает частичный результат, потому что он ожидает большего ввода.
Это два основных вариации, которые я пробовал. Оба имеют одинаковую проблему.
items :: Parser [Item]
items = sepBy (comment <|> recordType1 <|> recordType2) endOfLine
Для этого второго я изменил анализаторы записи/комментариев, чтобы потреблять символов конца линии.
items :: Parser [Item]
items = manyTill (comment <|> recordType1 <|> recordType2) endOfInput
Есть ли что -нибудь не так с моим подходом? Есть ли другой способ достичь того, что я пытаюсь?
Решение
Я сталкивался с этой проблемой раньше, и я понимаю, что это вызвано тем, как <|>
работает в определении sepBy
:
sepBy1 :: Alternative f => f a -> f s -> f [a]
sepBy1 p s = scan
where scan = liftA2 (:) p ((s *> scan) <|> pure [])
Это только перейдет к pure []
однажды (s *> scan)
не удалось, что не произойдет только потому, что вы находитесь в конце ввода.
Мое решение было просто позвонить feed
с empty
Отсутствие на Result
возвращен parse
. Анкет Это может быть своего рода взлом, но, похоже, это также, как attoparsec-iteratee
имеет дело с проблемой:
f k (EOF Nothing) = finalChunk $ feed (k S.empty) S.empty
Насколько я могу судить, это единственная причина, по которой attoparsec-iteratee
Работает здесь и простым старым parse
не.
Другие советы
Если вы пишете анализатор AttoParsec, который потребляет как можно больше ввода перед сбоем, вы должны сообщить о продолжении частичного результата, когда вы достигнете конца вашего ввода.
Вы даете довольно мало информации, поэтому я думаю, что трудно оказать вам хорошую помощь. Однако есть несколько комментариев, которые я хотел бы дать:
- Возможно, анализатор не понимает, что ввод сделан, и он зависит от получения EOL или получения другой записи. Следовательно, это запрашивает частичный результат. Попробуйте кормить его эквивалентом EOL в надежде, что он его насильно.
- Я не могу вспомнить код, но использование альтернативного экземпляра может нанести ущерб производительности. Если это так, вы можете захотеть принять участие в комментариях и записи.
- Я использую хлопья для большого бинарного анализа, и это также очень быстро. AttoParsec кажется лучше, как текстовый, хотя. Вы обязательно должны рассмотреть этот вариант.
- Другим вариантом является использование IO на основе ITERATE в более длительном заезде. Джон Лато сделал отличную статью о Iterates в последнем читателе Monad (я считаю, что выпуск № 16). Условием конец линии является итерационным сигналом. Остерегайтесь, хотя типы итерации довольно пугающие и требуют некоторого времени, к которым привыкают.