Я пытаюсь разобрать многострочный журнал, как этот
[xxx] This is 1
[xxx] This is also 1
[yyy] This is 2
У меня определены следующие типы
{-# LANGUAGE OverloadedStrings #-}
module Parser where
import Prelude hiding(takeWhile)
import Data.Text
import Data.Word
import Data.Attoparsec.Text as T
import Data.Char
import Data.String
data ID = ID String deriving (Eq, Show)
data Entry = Entry ID String deriving (Eq, Show)
data Block = Block ID [String]
data Log = Log [Block]
И определены эти синтаксические анализаторы:
parseID :: Parser ID
parseID = do
char '['
id <- takeTill ( == ']' )
char ']'
return $ ID $ unpack id
parseEntry :: Parser Entry
parseEntry = do
id <- parseID
char ' '
content <- takeTill isEndOfLine
return $ Entry id (unpack content)
Это работает нормально, когда я делаю что-то вроде parseOnly parseEntry entryString
и получаю Entry
.
Проблема в том, что я пытаюсь проанализировать что-то вроде журнала, который я добавил в начале.Я хотел бы получить [Entry]
, но я хотел бы получить [Block]
.
Также я хочу, чтобы, когда 2 или более последовательных строки имели одинаковый идентификатор (например, xxx
), они должны сохраняться в одном и том жеблок, так что для разбора вышеупомянутого журнала я хотел бы получить обратно
[block1, block2]
-- block1 == Block "xxx" ["This is 1", "This is also 1"]
-- block2 == Block "yyy" ["This is 2"]
Как я могу заставить парсер создавать новые блоки или добавлять в последний сгенерированный, в зависимости от того, изменяется ли ID
?
Одно очевидное решение - просто сгенерировать [Entry]
и затем использовать функцию свертывания для преобразования ее в [Block]
с правильной логикой, но я бы делал 2 прохода, 1 над журналом и другой над[Entry]
, который не только не слишком эффективен для больших журналов, но и кажется неправильным способом сделать это (из моего очень ограниченного знания attoparsec)
Любые другие идеи?
РЕДАКТИРОВАТЬ
Боб Далглиш решение по существу работает (большое спасибо !!!), просто нужно несколько настроек, чтобы оно заработало.Это мое окончательное решение:
data ID = ID String deriving (Eq, Show)
data Entry = Entry ID String deriving (Eq, Show)
data Block = Block ID [String] deriving (Eq, Show)
data Log = Log [Block] deriving (Eq, Show)
parseID :: Parser ID
parseID = do
char '['
id <- takeTill ( == ']' )
char ']'
return $ ID $ unpack id
parseEntry :: Parser Entry
parseEntry = do
id <- parseID
char ' '
content <- takeTill isEndOfLine
return $ Entry id (unpack content)
parseEntryFor :: ID -> Parser Entry
parseEntryFor blockId = do
id <- parseID
if blockId == id
then do
char ' '
content <- takeTill isEndOfLine
endOfLine <|> endOfInput
return $ Entry id (unpack content)
else fail "nonmatching id"
parseBlock :: Parser Block
parseBlock = do
(Entry entryId s) <- parseEntry
let newBlock = Block entryId [s]
endOfLine <|> endOfInput
entries <- many' (parseEntryFor entryId)
return $ Block entryId (s : Prelude.map (\(Entry _ s') -> s') entries)