-- | Defines an internal representation of Haskell data\/newtype definitions -- that correspond to the XML DTD types, and provides pretty-printers to -- convert these types into the 'Doc' type of "Text.PrettyPrint.HughesPJ". module Text.XML.HaXml.DtdToHaskell.TypeDef ( -- * Internal representation of types TypeDef(..) , Constructors , AttrFields , StructType(..) -- * Pretty-print a TypeDef , ppTypeDef , ppHName , ppXName , ppAName -- * Name mangling , Name(..) , name, name_, name_a, name_ac, name_f, mangle, manglef ) where import Char (isLower, isUpper, toLower, toUpper, isDigit) import List (intersperse) import Text.PrettyPrint.HughesPJ ---- Internal representation for typedefs ---- -- | Need to keep both the XML and Haskell versions of a name. data Name = Name { xName :: String -- ^ original XML name , hName :: String -- ^ mangled Haskell name } deriving Eq data TypeDef = DataDef Bool Name AttrFields Constructors -- ^ Bool for main\/aux. | EnumDef Name [Name] deriving Eq type Constructors = [(Name,[StructType])] type AttrFields = [(Name, StructType)] data StructType = Maybe StructType | Defaultable StructType String -- ^ String holds default value. | List StructType | List1 StructType -- ^ Non-empty lists. | Tuple [StructType] | OneOf [StructType] | Any -- ^ XML's contentspec allows ANY | String | Defined Name deriving Eq -- used for converting StructType (roughly) back to an XML content model instance Show StructType where showsPrec p (Maybe s) = showsPrec (p+1) s . showChar '?' showsPrec p (Defaultable s _) = shows s showsPrec p (List s) = showsPrec (p+1) s . showChar '*' showsPrec p (List1 s) = showsPrec (p+1) s . showChar '+' showsPrec p (Tuple ss) = showChar '(' . foldr1 (.) (intersperse (showChar ',') (map shows ss)) . showChar ')' showsPrec p (OneOf ss) = showChar '(' . foldr1 (.) (intersperse (showChar '|') (map shows ss)) . showChar ')' showsPrec p (Any) = showString "ANY" showsPrec p (String) = showString "#PCDATA" showsPrec p (Defined (Name n _)) = showString n ---- Pretty-printing typedefs ---- ppTypeDef :: TypeDef -> Doc -- no attrs, no constructors ppTypeDef (DataDef _ n [] []) = let name = ppHName n in text "data" <+> name <+> text "=" <+> name <+> text "\t\t" <> derives -- no attrs, single constructor ppTypeDef (DataDef _ n [] [c@(_,[_])]) = text "newtype" <+> ppHName n <+> text "=" <+> ppC c <+> text "\t\t" <> derives -- no attrs, multiple constrs ppTypeDef (DataDef _ n [] cs) = text "data" <+> ppHName n <+> ( text "=" <+> ppC (head cs) $$ vcat (map (\c-> text "|" <+> ppC c) (tail cs)) $$ derives ) -- nonzero attrs, no constructors ppTypeDef (DataDef _ n fs []) = let name = ppHName n in text "data" <+> name <+> text "=" <+> name $$ nest 4 ( text "{" <+> ppF (head fs) $$ vcat (map (\f-> text "," <+> ppF f) (tail fs)) $$ text "}" <+> derives ) -- nonzero attrs, one or more constrs ppTypeDef (DataDef _ n fs cs) = let attr = ppAName n in text "data" <+> ppHName n <+> ( text "=" <+> ppAC attr (head cs) $$ vcat (map (\c-> text "|" <+> ppAC attr c) (tail cs)) $$ derives ) $$ text "data" <+> attr <+> text "=" <+> attr $$ nest 4 ( text "{" <+> ppF (head fs) $$ vcat (map (\f-> text "," <+> ppF f) (tail fs)) $$ text "}" <+> derives ) -- enumerations (of attribute values) ppTypeDef (EnumDef n es) = text "data" <+> ppHName n <+> ( text "=" <+> fsep (intersperse (text " | ") (map ppHName es)) $$ derives ) ppST :: StructType -> Doc ppST (Defaultable st _) = parens (text "Defaultable" <+> ppST st) ppST (Maybe st) = parens (text "Maybe" <+> ppST st) ppST (List st) = text "[" <> ppST st <> text "]" ppST (List1 st) = parens (text "List1" <+> ppST st) ppST (Tuple sts) = parens (commaList (map ppST sts)) ppST (OneOf sts) = parens (text "OneOf" <> text (show (length sts)) <+> hsep (map ppST sts)) ppST String = text "String" ppST Any = text "ANYContent" ppST (Defined n) = ppHName n -- constructor and components ppC :: (Name,[StructType]) -> Doc ppC (n,sts) = ppHName n <+> fsep (map ppST sts) -- attribute (fieldname and type) ppF :: (Name,StructType) -> Doc ppF (n,st) = ppHName n <+> text "::" <+> ppST st -- constructor and components with initial attr-type ppAC :: Doc -> (Name,[StructType]) -> Doc ppAC atype (n,sts) = ppHName n <+> fsep (atype: map ppST sts) -- | Pretty print Haskell name. ppHName :: Name -> Doc ppHName (Name _ s) = text s -- | Pretty print XML name. ppXName :: Name -> Doc ppXName (Name s _) = text s -- | Pretty print Haskell attributes name. ppAName :: Name -> Doc ppAName (Name _ s) = text s <> text "_Attrs" derives = text "deriving" <+> parens (commaList (map text ["Eq","Show"])) ---- Some operations on Names ---- -- | Make a name valid in both XML and Haskell. name :: String -> Name name n = Name n (mangle n) -- | Append an underscore to the Haskell version of the name. name_ :: String -> Name name_ n = Name n (mangle n ++ "_") -- | Prefix an attribute enumeration type name with its containing element -- name. name_a :: String -> String -> Name name_a e n = Name n (mangle e ++ "_" ++ map decolonify n) -- | Prefix an attribute enumeration constructor with its element-tag name, -- and its enumeration type name. name_ac :: String -> String -> String -> Name name_ac e t n = Name n (mangle e ++ "_" ++ map decolonify t ++ "_" ++ map decolonify n) -- | Prefix a field name with its enclosing element name. name_f :: String -> String -> Name name_f e n = Name n (manglef e ++ mangle n) ---- obsolete -- elementname_at :: String -> Name -- elementname_at n = Name n (mangle n ++ "_Attrs") -- | Convert an XML name to a Haskell conid. mangle :: String -> String mangle (n:ns) | isLower n = toUpper n: map decolonify ns | isDigit n = 'I': n: map decolonify ns | otherwise = n: map decolonify ns -- | Convert an XML name to a Haskell varid. manglef :: String -> String manglef (n:ns) | isUpper n = toLower n: map decolonify ns | isDigit n = '_': n: map decolonify ns | otherwise = n: map decolonify ns -- | Convert colon to prime, hyphen to underscore. decolonify :: Char -> Char decolonify ':' = '\'' -- TODO: turn namespaces into qualified identifiers decolonify '-' = '_' decolonify c = c commaList = hcat . intersperse comma