content: {- Author:     Jeff Newbern
     Maintainer: Jeff Newbern <jnewbern@nomaware.com>
     Time-stamp: <Wed Jul  2 16:49:28 2003>
     License:    GPL
  -}
  
  {- DESCRIPTION
  
  Example 4 - Using the foldM function - part 2
  
  Usage: Compile the code and execute the resulting program
         with names of definition files as arguments.
         The definition files have the form of a series
         of assignments "key = value", one per line.
         The assignments from each file are accumulated
         and then the final dictionary is printed
         as an association list.
  
  -}
  
  import Monad
  import IO
  import System
  import Data.FiniteMap
  import Char(isSpace)
  
  -- an Entry is a key and a value, both Strings
  data Entry = Entry {key::String, value::String}
  
  -- show an entry as "key = value"
  instance Show Entry where
    show e = show (key e) ++ " = " ++ (show (value e))
  
  -- we parse "key = value" strings into Entry values
  instance Read Entry where
    readsPrec _ s = readsEntry s
  
  readsEntry :: ReadS Entry
  readsEntry s = [(Entry (trim key) (trim val), s'') | (key, s')    <- [break (=='=') s],
                                                       (x:val, s'') <- [break (=='\n') s'] ]
  
  -- remove leading and trailing whitespace
  trim :: String -> String
  trim s = dropWhile isSpace (reverse (dropWhile isSpace (reverse s)))
  
  -- convenience function
  openForReading :: FilePath -> IO Handle
  openForReading f = openFile f ReadMode
  
  -- a Dict is just a finite map from strings to strings
  type Dict = FiniteMap String String
  
  -- this an auxilliary function used with foldl
  addEntry :: Dict -> Entry -> Dict
  addEntry d e = addToFM d (key e) (value e)
  
  -- this is an auxiliiary function used with foldM inside the IO monad
  addDataFromFile :: Dict -> Handle -> IO Dict
  addDataFromFile dict hdl = do contents <- hGetContents hdl
                                entries  <- return (map read (lines contents))
  			      return (foldl (addEntry) dict entries)
  
  -- this program builds a dictionary from the entries in all files named on the
  -- command line and then prints it out as an association list
  main :: IO ()
  main = do files   <- getArgs
            handles <- mapM openForReading files
  	  dict    <- foldM addDataFromFile emptyFM handles
  	  print (fmToList dict)
  
  -- END OF FILE