「ログイン・ログアウトの解析」のパーサ部分

30分プログラム、その535。http://gauc.no-ip.org/awk-users-jp/blis.cgi/DoukakuAWK_022をやってみよう。
が、残念ながら30分では終わりそうにないので、今日はパーサ部分だけにチャレンジ。リスト操作とパターンマッチを駆使すればできそうな気がするけど、あえてParsecで。
でも、tryが必要なのに気がつかず、30分をオーバーしてしまった。

使い方

*Main> run dataset "1 2\n3\n1 2 3 0\n4\n1 2 3\n"
"Dataset {pc = 1, student = 2, recoards = [Logout 3 2 1], queries = [Query 3 1 2]}

ソースコード

import Text.ParserCombinators.Parsec
import Text.ParserCombinators.Parsec.Char

-- Node
data Dataset =
    Dataset { pc       :: Int ,
              student  :: Int ,
              recoards :: [Recoard],
              queries  :: [Query]
            } deriving Show

data Recoard =
    Login Student PC Time |
    Logout Student PC Time deriving Show

data Query =
    Query Student Time Time deriving Show

type Student = Int
type Time = Int
type PC = Int

-- Parser
int :: Parser Int
int = do n <- many1 digit
         many $ char ' '
         return $ read n

recoard :: Parser Recoard
recoard = do t       <- int
             pc      <- int
             student <- int
             s       <- int
             if s == 0
               then return $ Logout student pc t
               else return $ Login  student pc t

query :: Parser Query
query = do from    <- int
           to      <- int
           student <- int
           return $ Query student from to

recoardFields :: Parser [Recoard]
recoardFields = do int
                   char '\n'
                   (try recoard) `endBy` (char '\n')

queryFields :: Parser [Query]
queryFields = do int
                 char '\n'
                 query `endBy` (char '\n')

--dataset :: Parser DataSet
dataset = do pc <- int
             student <- int
             char '\n'
             recoards <- recoardFields
             queries  <- queryFields
             return $ Dataset {
                          pc = pc,
                          student = student,
                          recoards= recoards,
                          queries = queries
                        }

file = do datasets <- many1 dataset
          string "0 0"
          eof
          return datasets

-- driver
run :: Show a => Parser a -> String -> String
run p input
        = case (parse p "" input) of
            Left err -> show err
            Right x  -> show x
main = interact $ run dataset