Haskell attoparsec: Не удалось прочитать: удовлетворяютWith

Я хочу разобрать текст, например "John","Kate","Ruddiger", в список строк.

Я попытался начать с разбора "John", в Name (псевдоним для String), но это уже не удается с Fail "\"," [","] "Failed reading: satisfyWith".

Вопрос A: Почему возникает эта ошибка и как ее исправить? (Я не нашел вызова для удовлетворенияWith в исходном коде attoparsec)

Вопрос Б: Как сделать так, чтобы парсер не требовал запятую после фамилии?

{-# LANGUAGE OverloadedStrings #-}

import Data.Attoparsec.Char8 as P
import qualified Data.ByteString.Char8 as BS
import Control.Applicative(many)

data Name = Name String deriving Show

readName =  P.takeWhile (/='"')

entryParser :: Parser Name
entryParser = do
    P.char '"'
    name <- readName
    P.char ','
    return $ Name (BS.unpack name)

someEntry :: IO BS.ByteString
someEntry = do
    return $ BS.pack "\"John\","

main :: IO()
main = do
    someEntry >>= print . parse entryParser  

Я использую GHC 7.6.3 и attoparsec-0.11.3.4.


person A123321    schedule 20.05.2014    source источник


Ответы (1)


Вопрос A: Почему возникает эта ошибка и как ее исправить? (Я не нашел вызова для удовлетворенияWith в исходном коде attoparsec)

readName =  P.takeWhile (/='"')

takeWhile потребляет, пока предикат истинен. Таким образом, после того, как вы прочитали название, " не было израсходовано. Это легко увидеть, если мы удалим P.char ',' из entryParser:

entryParser = P.char '"' >> fmap (Name . BS.unpack) readName
$ runhaskell SO.hs
Done "\"," Name "John"

Вам нужно потреблять ":

entryParser :: Parser Name
entryParser = do
    P.char '"'
    name <- readName
    P.char '"' -- <<<<<<<<<<<<<<<<<<<<<<
    P.char ','
    return $ Name (BS.unpack name)

Вопрос Б: Как сделать так, чтобы парсер не требовал запятую после фамилии?

Используйте sepBy.


Теперь ваши вопросы прояснились, давайте немного упростим ситуацию. Не используйте , вообще в entryParser, вместо этого используйте только имя:

entryParser = P.char '"' *> fmap ( Name . BS.unpack ) readName <* P.char '"'

Если вы не знаете (*>) и (<*), они оба из Control.Applicative, и они в основном означают «отбросить все, что находится на стороне звездочек».

Теперь, чтобы проанализировать все записи, разделенные запятыми, мы используем sepBy entryParser (P.char ','). Однако это приведет к тому, что attoparsec вернет Partial:

$ runhaskell SO.hs
Partial _

На самом деле это функция attoparsec вы должны иметь в виду:

Attoparsec поддерживает инкрементный ввод, что означает, что вы можете передать ему строку байтов, которая представляет собой только часть ожидаемого общего объема данных для анализа. Если ваш синтаксический анализатор достигает конца фрагмента ввода и может потреблять больше ввода, он приостанавливает синтаксический анализ и возвращает продолжение Partial.

Если вы хотите использовать инкрементный ввод, используйте parse и feed. В противном случае используйте parseOnly. Полный код для вашего примера будет примерно таким

{-# LANGUAGE OverloadedStrings #-}

import Data.Attoparsec.Char8 as P
import qualified Data.ByteString.Char8 as BS
import Control.Applicative(many, (*>), (<*))

data Name = Name String deriving Show

readName =  P.takeWhile (/='"')

entryParser :: Parser Name
entryParser = P.char '"' *> fmap ( Name . BS.unpack ) readName <* P.char '"'

allEntriesParser = sepBy entryParser (P.char ',')

testString = "\"John\",\"Martha\",\"test\""

main = print . parseOnly allEntriesParser $ testString  
$ runhaskell SO.hs
Right [Name "John",Name "Martha",Name "test"]
person Zeta    schedule 20.05.2014