module Text.XML.HaXml.TypeMapping ( -- * A class to get an explicit type representation for any value HTypeable(..) -- sole method, toHType -- * Explicit representation of Haskell datatype information , HType(..) -- instance of Eq, Show , Constr(..) -- instance of Eq, Show -- * Helper functions to extract type info as strings , showHType -- :: HType -> ShowS , showConstr -- :: Int -> HType -> String -- * Conversion from Haskell datatype to DTD , toDTD ) where import Text.XML.HaXml.Types import Data.List (partition, intersperse) import Text.PrettyPrint.HughesPJ (render) import qualified Text.XML.HaXml.Pretty as PP ------------------------------------------------------------------------ -- idea: in DrIFT, -- named field == primitive type, becomes an attribute -- named field == single-constructor type, renames the tag -- named field == multi-constructor type, as normal -- if prefix of all named fields is roughly typename, delete it -- | @HTypeable@ promises that we can create an explicit representation of -- of the type of any value. class HTypeable a where toHType :: a -> HType -- | A concrete representation of any Haskell type. data HType = Maybe HType | List HType | Tuple [HType] | Prim String String -- ^ separate Haskell name and XML name | String | Defined String [HType] [Constr] -- ^ A user-defined type has a name, a sequence of type variables, -- and a set of constructors. (The variables might already be -- instantiated to actual types.) deriving (Show) instance Eq HType where (Maybe x) == (Maybe y) = x==y (List x) == (List y) = x==y (Tuple xs) == (Tuple ys) = xs==ys (Prim x _) == (Prim y _) = x==y String == String = True (Defined n _xs _) == (Defined m _ys _) = n==m -- && xs==ys _ == _ = False -- | A concrete representation of any user-defined Haskell constructor. -- The constructor has a name, and a sequence of component types. The -- first sequence of types represents the minimum set of free type -- variables occurring in the (second) list of real component types. -- If there are fieldnames, they are contained in the final list, and -- correspond one-to-one with the component types. data Constr = Constr String [HType] [HType] -- (Maybe [String]) deriving (Eq,Show) -- | Project the n'th constructor from an HType and convert it to a string -- suitable for an XML tagname. showConstr :: Int -> HType -> String showConstr n (Defined _ _ cs) = flatConstr (cs!!n) "" showConstr _ _ = error "no constructors for builtin types" ------------------------------------------------------------------------ -- Some instances instance HTypeable Bool where toHType _ = Prim "Bool" "bool" instance HTypeable Int where toHType _ = Prim "Int" "int" instance HTypeable Integer where toHType _ = Prim "Integer" "integer" instance HTypeable Float where toHType _ = Prim "Float" "float" instance HTypeable Double where toHType _ = Prim "Double" "double" instance HTypeable Char where toHType _ = Prim "Char" "char" instance HTypeable () where toHType _ = Prim "unit" "unit" instance (HTypeable a, HTypeable b) => HTypeable (a,b) where toHType p = Tuple [toHType a, toHType b] where (a,b) = p instance (HTypeable a, HTypeable b, HTypeable c) => HTypeable (a,b,c) where toHType p = Tuple [toHType a, toHType b, toHType c] where (a,b,c) = p instance (HTypeable a, HTypeable b, HTypeable c, HTypeable d) => HTypeable (a,b,c,d) where toHType p = Tuple [toHType a, toHType b, toHType c, toHType d] where (a,b,c,d) = p instance (HTypeable a, HTypeable b, HTypeable c, HTypeable d, HTypeable e) => HTypeable (a,b,c,d,e) where toHType p = Tuple [ toHType a, toHType b, toHType c, toHType d , toHType e ] where (a,b,c,d,e) = p instance ( HTypeable a, HTypeable b, HTypeable c, HTypeable d, HTypeable e , HTypeable f) => HTypeable (a,b,c,d,e,f) where toHType p = Tuple [ toHType a, toHType b, toHType c, toHType d , toHType e, toHType f ] where (a,b,c,d,e,f) = p instance ( HTypeable a, HTypeable b, HTypeable c, HTypeable d, HTypeable e , HTypeable f, HTypeable g) => HTypeable (a,b,c,d,e,f,g) where toHType p = Tuple [ toHType a, toHType b, toHType c, toHType d , toHType e, toHType f, toHType g ] where (a,b,c,d,e,f,g) = p instance ( HTypeable a, HTypeable b, HTypeable c, HTypeable d, HTypeable e , HTypeable f, HTypeable g, HTypeable h) => HTypeable (a,b,c,d,e,f,g,h) where toHType p = Tuple [ toHType a, toHType b, toHType c, toHType d , toHType e, toHType f, toHType g, toHType h ] where (a,b,c,d,e,f,g,h) = p instance ( HTypeable a, HTypeable b, HTypeable c, HTypeable d, HTypeable e , HTypeable f, HTypeable g, HTypeable h, HTypeable i) => HTypeable (a,b,c,d,e,f,g,h,i) where toHType p = Tuple [ toHType a, toHType b, toHType c, toHType d , toHType e, toHType f, toHType g, toHType h , toHType i ] where (a,b,c,d,e,f,g,h,i) = p instance ( HTypeable a, HTypeable b, HTypeable c, HTypeable d, HTypeable e , HTypeable f, HTypeable g, HTypeable h, HTypeable i, HTypeable j) => HTypeable (a,b,c,d,e,f,g,h,i,j) where toHType p = Tuple [ toHType a, toHType b, toHType c, toHType d , toHType e, toHType f, toHType g, toHType h , toHType i, toHType j ] where (a,b,c,d,e,f,g,h,i,j) = p instance ( HTypeable a, HTypeable b, HTypeable c, HTypeable d, HTypeable e , HTypeable f, HTypeable g, HTypeable h, HTypeable i, HTypeable j , HTypeable k) => HTypeable (a,b,c,d,e,f,g,h,i,j,k) where toHType p = Tuple [ toHType a, toHType b, toHType c, toHType d , toHType e, toHType f, toHType g, toHType h , toHType i, toHType j, toHType k ] where (a,b,c,d,e,f,g,h,i,j,k) = p instance ( HTypeable a, HTypeable b, HTypeable c, HTypeable d, HTypeable e , HTypeable f, HTypeable g, HTypeable h, HTypeable i, HTypeable j , HTypeable k, HTypeable l) => HTypeable (a,b,c,d,e,f,g,h,i,j,k,l) where toHType p = Tuple [ toHType a, toHType b, toHType c, toHType d , toHType e, toHType f, toHType g, toHType h , toHType i, toHType j, toHType k, toHType l ] where (a,b,c,d,e,f,g,h,i,j,k,l) = p instance ( HTypeable a, HTypeable b, HTypeable c, HTypeable d, HTypeable e , HTypeable f, HTypeable g, HTypeable h, HTypeable i, HTypeable j , HTypeable k, HTypeable l, HTypeable m) => HTypeable (a,b,c,d,e,f,g,h,i,j,k,l,m) where toHType p = Tuple [ toHType a, toHType b, toHType c, toHType d , toHType e, toHType f, toHType g, toHType h , toHType i, toHType j, toHType k, toHType l , toHType m ] where (a,b,c,d,e,f,g,h,i,j,k,l,m) = p instance ( HTypeable a, HTypeable b, HTypeable c, HTypeable d, HTypeable e , HTypeable f, HTypeable g, HTypeable h, HTypeable i, HTypeable j , HTypeable k, HTypeable l, HTypeable m, HTypeable n) => HTypeable (a,b,c,d,e,f,g,h,i,j,k,l,m,n) where toHType p = Tuple [ toHType a, toHType b, toHType c, toHType d , toHType e, toHType f, toHType g, toHType h , toHType i, toHType j, toHType k, toHType l , toHType m, toHType n ] where (a,b,c,d,e,f,g,h,i,j,k,l,m,n) = p instance ( HTypeable a, HTypeable b, HTypeable c, HTypeable d, HTypeable e , HTypeable f, HTypeable g, HTypeable h, HTypeable i, HTypeable j , HTypeable k, HTypeable l, HTypeable m, HTypeable n, HTypeable o) => HTypeable (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o) where toHType p = Tuple [ toHType a, toHType b, toHType c, toHType d , toHType e, toHType f, toHType g, toHType h , toHType i, toHType j, toHType k, toHType l , toHType m, toHType n, toHType o ] where (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o) = p instance (HTypeable a) => HTypeable (Maybe a) where toHType m = Maybe (toHType x) where (Just x) = m instance (HTypeable a, HTypeable b) => HTypeable (Either a b) where toHType m = Defined "Either" [hx, hy] [ Constr "Left" [hx] [hx] {-Nothing-} , Constr "Right" [hy] [hy] {-Nothing-}] where (Left x) = m (Right y) = m hx = toHType x hy = toHType y instance HTypeable a => HTypeable [a] where toHType xs = case toHType x of (Prim "Char" _) -> String _ -> List (toHType x) where (x:_) = xs ------------------------------------------------------------------------ -- | 'toDTD' converts a concrete representation of the Haskell type of -- a value (obtained by the method 'toHType') into a real DocTypeDecl. -- It ensures that PERefs are defined before they are used, and that no -- element or attribute-list is declared more than once. toDTD :: HType -> DocTypeDecl toDTD ht = DTD (toplevel ht) Nothing (macrosFirst (reverse (h2d True [] [] [ht]))) where macrosFirst :: [MarkupDecl] -> [MarkupDecl] macrosFirst decls = concat [p, p'] where (p, p') = partition f decls f (Entity _) = True f _ = False toplevel ht@(Defined _ _ _) = N $ showHType ht "-XML" toplevel ht@_ = N $ showHType ht "" c0 = False h2d :: Bool -> [HType] -> [Constr] -> [HType] -> [MarkupDecl] -- toplevel? history history remainingwork result h2d _c _history _chist [] = [] h2d c history chist (ht:hts) = if ht `elem` history then h2d c0 history chist hts else case ht of Maybe ht0 -> declelem ht: h2d c0 (ht:history) chist (ht0:hts) List ht0 -> declelem ht: h2d c0 (ht:history) chist (ht0:hts) Tuple hts0 -> (c ? (declelem ht:)) (h2d c0 history chist (hts0++hts)) Prim _ _ -> declprim ht ++ h2d c0 (ht:history) chist hts String -> declstring: h2d c0 (ht:history) chist hts Defined _ _ cs -> let hts0 = concatMap grab cs in (c ? (decltopelem ht:)) (declmacro ht chist) ++ h2d c0 (ht:history) (cs++chist) (hts0++hts) declelem ht = Element (ElementDecl (N $ showHType ht "") (ContentSpec (outerHtExpr ht))) decltopelem ht = -- hack to avoid peref at toplevel Element (ElementDecl (N $ showHType ht "-XML") (ContentSpec (innerHtExpr ht None))) declmacro ht@(Defined _ _ cs) chist = Entity (EntityPEDecl (PEDecl (showHType ht "") (PEDefEntityValue ev))): concatMap (declConstr chist) cs where ev = EntityValue [EVString (render (PP.cp (outerHtExpr ht)))] declConstr chist c@(Constr s fv hts) | c `notElem` chist = [Element (ElementDecl (N $ flatConstr c "") (ContentSpec (constrHtExpr c)))] | otherwise = [] declprim (Prim _ t) = [ Element (ElementDecl (N t) EMPTY) , AttList (AttListDecl (N t) [AttDef (N "value") StringType REQUIRED])] declstring = Element (ElementDecl (N "string") (Mixed PCDATA)) grab (Constr _ _ hts) = hts (?) :: Bool -> (a->a) -> (a->a) b ? f | b = f | not b = id -- Flatten an HType to a String suitable for an XML tagname. showHType :: HType -> ShowS showHType (Maybe ht) = showString "maybe-" . showHType ht showHType (List ht) = showString "list-" . showHType ht showHType (Tuple hts) = showString "tuple" . shows (length hts) . showChar '-' . foldr1 (.) (intersperse (showChar '-') (map showHType hts)) showHType (Prim _ t) = showString t showHType String = showString "string" showHType (Defined s fv _) = showString s . ((length fv > 0) ? (showChar '-')) . foldr (.) id (intersperse (showChar '-') (map showHType fv)) flatConstr :: Constr -> ShowS flatConstr (Constr s fv _) = showString s . ((length fv > 0) ? (showChar '-')) . foldr (.) id (intersperse (showChar '-') (map showHType fv)) outerHtExpr :: HType -> CP outerHtExpr (Maybe ht) = innerHtExpr ht Query outerHtExpr (List ht) = innerHtExpr ht Star outerHtExpr (Defined _s _fv cs) = Choice (map (\c->TagName (N $ flatConstr c "") None) cs) None outerHtExpr ht = innerHtExpr ht None innerHtExpr :: HType -> Modifier -> CP innerHtExpr (Prim _ t) m = TagName (N t) m innerHtExpr (Tuple hts) m = Seq (map (\c-> innerHtExpr c None) hts) m innerHtExpr ht@(Defined _ _ _) m = -- CPPE (showHType ht "") (outerHtExpr ht) TagName (N ('%': showHType ht ";")) m -- ***HACK!!!*** innerHtExpr ht m = TagName (N $ showHType ht "") m constrHtExpr :: Constr -> CP constrHtExpr (Constr _s _fv []) = TagName (N "EMPTY") None -- ***HACK!!!*** constrHtExpr (Constr _s _fv hts) = innerHtExpr (Tuple hts) None ------------------------------------------------------------------------