Вопрос 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