content: 
  module TestDataParser (
     parse_testdata, -- Read a => FilePath -> IO (TestData a)
     write_testdata  -- Show a => TestData -> IO ()
  ) where
  
  import TestData
  
  -- Parser for parsing TestData. The following is a sample of a test data: 
  --
  --
  -- Name: sample
  -- Threads: 4
  -- Modes: ["CAS","IO","BACK"]
  -- Repeat: 6
  -- Initial-List: 
  -- [4,5,6,7,8,4,5,754,345,23432,6547,4]
  -- Tasks: 
  -- [Find 3,Delete 4,Insert 34,Find 45]
  -- [Delete 34,Insert 43,Delete 3,Delete 45,Find 87]
  -- [Insert 3 , Find 6]
  -- [Find 3,Find 67,  Insert 3]
  --
  --
  -- Some assumptions:
  --   - Number of tasks must correspond to number of threads.
  --   - Fields must come in the exact sequence specified above.
  
  -- Auxiliary Functions
  
  partitionAt :: Eq a => (a -> Bool) -> [a] -> [[a]]
  partitionAt f as = 
    filter (/=[]) (partitionAt' f as)
    where
      partitionAt' _ [] = []
      partitionAt' f as = let (v,rest) = span f as
                          in v:(partitionAt' f (drop 1 rest))
                
  is_delimit :: Char -> Bool
  is_delimit ' '  = True
  is_delimit '\n' = True
  is_delimit _    = False
                
  not_delimit :: Char -> Bool
  not_delimit x = not (is_delimit x)
  
  -- Parsing Functions
  
  parse_token :: String -> (String,String)
  parse_token str = let str' = dropWhile is_delimit str 
                    in span not_delimit str'
  
  parse_list :: String -> ([String],String)
  parse_list str = 
     let str' = dropWhile is_delimit str
     in parse_list (tail str')
     where
       parse_list str = let comma x = x == ','
                            close x = x == ']'
                            commaOrClose x = or [comma x,close x]
                            (a,rest)   = span (\x -> not $ commaOrClose x) str
                        in case head rest of
                             ',' -> let (as,rest') = parse_list (tail rest)
                                    in (a:as,rest')
                             ']' -> ([a],tail rest)                         
  
  parse_list_many :: Int -> String -> ([[String]],String)
  parse_list_many 0 str = ([],str)
  parse_list_many n str = let (a,rest)   = parse_list str
                              (as,rest') = parse_list_many (n-1) rest
                          in (a:as,rest')
                      
  drop_token :: String -> String
  drop_token str = let (_,rest) = parse_token str
                   in rest              
        
  -- Main function for parseing testcases from file              
  parse_testdata :: Read a => FilePath -> IO (TestData a)
  parse_testdata fname = do
    { input <- readFile fname
    ; let (name,input1) = parse_token (drop_token input)
          (t,input2)    = parse_token (drop_token input1)
          (list,input3) = parse_list (drop_token input2)
          (ops,_)       = parse_list_many (read t) (drop_token input3)
    ; return $ TestData { t_name    = name
                        , t_threads = read t
                        , t_init_list = map read list
                        , t_tasks   = map readops ops } }
    where
      readops (s:ss) = let (oper,rest) = parse_token s
                           (arg,_)     = parse_token rest
                           x  = read arg
                           op = case oper of
                                 "Find"   -> Find x
                                 "Delete" -> Delete x
                                 "Insert" -> Insert x
                                 _        -> error ("parse_testdata: " ++ oper ++ "\n" ++ rest ++ "\n" ++ arg)
                       in op:(readops ss) 
      readops [] = []
      
  -- Main function for writing test data to file
  write_testdata :: Show a => TestData a -> IO ()
  write_testdata tc = writeFile (t_name tc) (show tc) 
  