packages feed

ideas-0.5.8: src/Text/OpenMath/ContentDictionary.hs

-----------------------------------------------------------------------------
-- Copyright 2009, Open Universiteit Nederland. This file is distributed 
-- under the terms of the GNU General Public License. For more information, 
-- see the file "LICENSE.txt", which is included in the distribution.
-----------------------------------------------------------------------------
-- |
-- Maintainer  :  bastiaan.heeren@ou.nl
-- Stability   :  provisional
-- Portability :  portable (depends on ghc)
--
-----------------------------------------------------------------------------
module Text.OpenMath.ContentDictionary 
   ( -- data types
     ContentDictionary(..), VersionNumber, Date
   , ContentDictionaryStatus(..), Definition(..)
     -- parsing and reading
   , readContentDictionary, main, findOCDs
   ) where

import Text.OpenMath.Object (OMOBJ, xml2omobj)
import Text.XML
import Data.Char
import Data.List
import Data.Maybe
import Control.Monad
import System.Directory

main :: IO ()
main = do
   let base = "lib/Dictionaries"
       f x  = base ++ "/" ++ x
   xs  <- findOCDs base
   cds <- mapM (readContentDictionary . f) xs
   let defs = concatMap definitions cds 
   putStrLn $ show (length cds) ++ " valid dictionaries, with " ++ show (length defs) ++ " definitions"

   -- print [ p | d <- defs, p <- formalProperties d ]

findOCDs :: String -> IO [FilePath]
findOCDs filepath = do
   xs <- getDirectoryContents filepath
   return $ filter (".ocd" `isSuffixOf`) xs

readContentDictionary :: String -> IO ContentDictionary
readContentDictionary filename = do
   putStrLn $ "reading " ++ show filename ++ "..."
   input <- readFile filename
   case parseXML input of
      Left s  -> err s
      Right xml -> do 
         guard (name xml == "CD") 
         case buildContentDictionary xml of 
            Left s -> err s
            Right cd -> do
               putStrLn $ "  found " ++ show (length $ definitions cd) ++ " definition(s)"
               return cd
 `catch` (err . show)
 where
   err s = fail $ "Content dictionary not found: " ++ s

buildContentDictionary :: XML -> Either String ContentDictionary
buildContentDictionary xml = do
   cdname    <- extractText "CDName"       xml
   descr     <- extractText "Description"  xml
   revision  <- extractDate "CDDate"       xml
   review    <- extractDate "CDReviewDate" xml
   major     <- extractInt "CDVersion"     xml
   minor     <- extractInt "CDRevision"    xml
   theStatus <- extractStatus              xml
   let theBase = extractText "CDBase"      xml
   theURL    <- extractText "CDURL"        xml
   defs      <- mapM buildDefinition [ d | d <- children xml, name d == "CDDefinition" ]
   return CD
      { dictionaryName = cdname
      , description    = descr
      , revisionDate   = revision
      , reviewDate     = review
      , versionNumber  = (major, minor)
      , status         = theStatus
      , base           = theBase
      , url            = theURL
      , definitions    = defs
  }

buildDefinition :: XML -> Either String Definition
buildDefinition xml = do
   theName    <- extractText "Name"        xml
   descr      <- extractText "Description" xml
   let theRole = extractText "Role"        xml
       cmps    = [ getData a   | a <- children xml, name a == "CMP" ]
       fmps    = [ head xs   | a <- children xml, name a == "FMP", let xs=children a, length xs==1 ]
       exs     = [ children a | a <- children xml, name a == "Example" ]
   return Definition
      { symbolName          = theName
      , symbolDescription   = descr
      , role                = theRole
      , commentedProperties = cmps
      , formalProperties    = map (either error id . xml2omobj) fmps
      , examples            = exs
      }

extractDate :: String -> XML -> Either String Date
extractDate s xml = do
   txt <- extractText s xml
   case txt of 
      [y1,y2,y3,y4,'-',m1,m2,'-',d1,d2] | all isDigit [y1,y2,y3,y4,m1,m2,d1,d2] -> 
         return (read [y1,y2,y3,y4], read [m1,m2], read [d1,d2])
      _ -> fail ("invalid date (YYYY-MM-DD): " ++ txt)

extractInt :: String -> XML -> Either String Int
extractInt s xml = do
   txt <- extractText s xml
   case reads txt of 
      [(n, xs)] | all isSpace xs -> 
         return n
      _ -> fail ("invalid number" ++ txt)

extractStatus :: XML -> Either String ContentDictionaryStatus
extractStatus xml = do
   txt <- extractText "CDStatus" xml
   let (hd, tl) = splitAt 1 txt
   case reads (map toUpper hd ++ map toLower tl) of
      [(st, xs)] | all isSpace xs -> 
         return st
      _ -> fail ("invalid status: " ++ txt)

extractText :: MonadPlus m => String -> XML -> m String
extractText s xml = do
   a <- findChild s xml
   guard (null $ children a)
   return (getData a)

data ContentDictionary = CD 
   { dictionaryName :: String
   , description    :: String
   , revisionDate   :: Date
   , reviewDate     :: Date
   , versionNumber  :: VersionNumber
   , status         :: ContentDictionaryStatus
   , base           :: Maybe String
   , url            :: String
   , definitions    :: [Definition]   
   } deriving Show

type VersionNumber = (Int, Int) -- major and minor part
type Date = (Int, Int, Int) -- YYYY-MM-DD

data ContentDictionaryStatus = Official | Experimental | Private | Obsolete deriving (Read,Show)

data Definition = Definition 
   { symbolName          :: String
   , symbolDescription   :: String
   , role                :: Maybe String
   , commentedProperties :: [String]
   , formalProperties    :: [OMOBJ]
   , examples            :: [[XML]]
   } deriving Show