-- | Pretty-print the internal Haskell model of XSD datatypes to a real -- Haskell module containing type declarations, and instances for parsing -- (and printing - though not yet implemented) values of those datatypes -- from(/to) XML. module Text.XML.HaXml.Schema.PrettyHaskell ( ppComment , ppModule , ppHighLevelDecl , ppHighLevelDecls , ppModuleWithInstances , ppHighLevelInstances , ppvList ) where import Text.XML.HaXml.Types (QName(..),Namespace(..)) import Text.XML.HaXml.Schema.HaskellTypeModel import Text.XML.HaXml.Schema.XSDTypeModel (Occurs(..)) import Text.XML.HaXml.Schema.NameConversion import Text.PrettyPrint.HughesPJ as PP import Data.List (intersperse,notElem,inits) import Data.Maybe (isJust,fromJust,catMaybes) -- | Vertically pretty-print a list of things, with open and close brackets, -- and separators. ppvList :: String -> String -> String -> (a->Doc) -> [a] -> Doc ppvList open sep close pp [] = text open <> text close ppvList open sep close pp (x:xs) = text open <+> pp x $$ vcat (map (\y-> text sep <+> pp y) xs) $$ text close data CommentPosition = Before | After -- | Generate aligned haddock-style documentation. -- (but without escapes in comment text yet) ppComment :: CommentPosition -> Comment -> Doc ppComment _ Nothing = empty ppComment pos (Just s) = text "--" <+> text (case pos of Before -> "|"; After -> "^") <+> text c $$ vcat (map (\x-> text "-- " <+> text x) cs) where (c:cs) = lines (paragraph 60 s) -- | Pretty-print a Haskell-style name. ppHName :: HName -> Doc ppHName (HName x) = text x -- | Pretty-print an XML-style name. ppXName :: XName -> Doc ppXName (XName (N x)) = text x ppXName (XName (QN ns x)) = text (nsPrefix ns) <> text ":" <> text x -- | Some different ways of using a Haskell identifier. ppModId, ppConId, ppVarId, ppUnqConId, ppUnqVarId, ppFwdConId :: NameConverter -> XName -> Doc ppModId nx = ppHName . modid nx ppConId nx = ppHName . conid nx ppVarId nx = ppHName . varid nx ppUnqConId nx = ppHName . unqconid nx ppUnqVarId nx = ppHName . unqvarid nx ppFwdConId nx = ppHName . fwdconid nx ppJoinConId, ppFieldId :: NameConverter -> XName -> XName -> Doc ppJoinConId nx p q = ppHName (conid nx p) <> text "_" <> ppHName (conid nx q) ppFieldId nx = \t-> ppHName . fieldid nx t -- | Convert a whole document from HaskellTypeModel to Haskell source text. ppModule :: NameConverter -> Module -> Doc ppModule nx m = text "{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies," $$ text " ExistentialQuantification, FlexibleContexts #-}" $$ text "{-# OPTIONS_GHC -fno-warn-duplicate-exports #-}" $$ text "module" <+> ppModId nx (module_name m) $$ nest 2 (text "( module" <+> ppModId nx (module_name m) $$ vcat (map (\(XSDInclude ex com)-> ppComment Before com $$ text ", module" <+> ppModId nx ex) (module_re_exports m)) $$ text ") where") $$ text " " $$ text "import Text.XML.HaXml.Schema.Schema (SchemaType(..),SimpleType(..),Extension(..),Restricts(..))" $$ text "import Text.XML.HaXml.Schema.Schema as Schema" $$ (case module_xsd_ns m of Nothing -> text "import Text.XML.HaXml.Schema.PrimitiveTypes as Xsd" Just ns -> text "import qualified Text.XML.HaXml.Schema.PrimitiveTypes as"<+>ppConId nx ns) $$ vcat (map (ppHighLevelDecl nx) (module_re_exports m ++ module_import_only m)) $$ text " " $$ ppHighLevelDecls nx (module_decls m) -- | Generate a supplementary module to contain instances, especially for -- types that needed to be forward-declared. ppModuleWithInstances :: NameConverter -> Module -> Doc ppModuleWithInstances nx m = text "{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies," $$ text " ExistentialQuantification, FlexibleContexts #-}" $$ text "{-# OPTIONS_GHC -fno-warn-orphan-instances #-}" $$ text "module" <+> ppModId nx (module_name m) <> text "Instances" $$ nest 2 (text "( module" <+> ppModId nx (module_name m) <> text "Instances" $$ text ") where") $$ text " " $$ text "import Text.XML.HaXml.Schema.Schema (SchemaType(..),SimpleType(..),Extension(..),Restricts(..))" $$ text "import Text.XML.HaXml.Schema.Schema as Schema" $$ (case module_xsd_ns m of Nothing -> text "import Text.XML.HaXml.Schema.PrimitiveTypes as Xsd" Just ns -> text "import qualified Text.XML.HaXml.Schema.PrimitiveTypes as"<+>ppConId nx ns) $$ vcat (map (ppHighLevelDecl nx) (module_re_exports m ++ module_import_only m)) $$ text " " $$ text "import" <+> ppModId nx (module_name m) $$ text "-- More imports are required, extracted from FwdDecls" $$ vcat (map ppFwdDecl $ concatMap imports $ module_decls m) $$ text " " $$ vcat (intersperse (text " ") (map (ppHighLevelInstances nx) (filter hasInstances (module_decls m)))) where hasInstances ElementsAttrsAbstract{} = True hasInstances ElementAbstractOfType{} = True hasInstances ExtendComplexType{} = True hasInstances ExtendComplexTypeAbstract{} = True hasInstances _ = False imports (ElementsAttrsAbstract _ insts _) = insts imports (ExtendComplexTypeAbstract _ _ insts _ _ _) = insts imports _ = [] ppFwdDecl (_, Nothing) = empty ppFwdDecl (name,Just mod) = text "import" <+> ppModId nx mod <+> text "-- for" <+> ppConId nx name -- | Generate a fragmentary parser for an attribute. ppAttr :: Attribute -> Int -> Doc ppAttr a n = (text "a"<>text (show n)) <+> text "<- getAttribute \"" <> ppXName (attr_name a) <> text "\" e pos" -- | Generate a fragmentary parser for an element. ppElem :: NameConverter -> Element -> Doc ppElem nx e@Element{} | elem_byRef e = ppElemModifier (elem_modifier e) (text "element" <> ppUnqConId nx (elem_name e)) | otherwise = ppElemModifier (elem_modifier e) (text "parseSchemaType \"" <> ppXName (elem_name e) <> text "\"") ppElem nx e@AnyElem{} = ppElemModifier (elem_modifier e) (text "parseAnyElement") ppElem nx e@Text{} = text "parseText" ppElem nx e@OneOf{} = ppElemModifier (elem_modifier e) (text "oneOf" <+> ppvList "[" "," "]" (ppOneOf n) (zip (elem_oneOf e) [1..n])) where n = length (elem_oneOf e) ppOneOf n (e,i) = text "fmap" <+> text (ordinal i ++"Of"++show n) <+> parens (ppSeqElem e) ordinal i | i <= 20 = ordinals!!i | otherwise = "Choice" ++ show i ordinals = ["Zero","One","Two","Three","Four","Five","Six","Seven","Eight" ,"Nine","Ten","Eleven","Twelve","Thirteen","Fourteen","Fifteen" ,"Sixteen","Seventeen","Eighteen","Nineteen","Twenty"] ppSeqElem [] = PP.empty ppSeqElem [e] = ppElem nx e ppSeqElem es = text ("return ("++replicate (length es-1) ','++")") <+> vcat (map (\e-> text "`apply`" <+> ppElem nx e) es) -- | Convert multiple HaskellTypeModel Decls to Haskell source text. ppHighLevelDecls :: NameConverter -> [Decl] -> Doc ppHighLevelDecls nx hs = vcat (intersperse (text " ") (map (ppHighLevelDecl nx) hs)) -- | Convert a single Haskell Decl into Haskell source text. ppHighLevelDecl :: NameConverter -> Decl -> Doc ppHighLevelDecl nx (NamedSimpleType t s comm) = ppComment Before comm $$ text "type" <+> ppUnqConId nx t <+> text "=" <+> ppConId nx s $$ text "-- No instances required: synonym is isomorphic to the original." ppHighLevelDecl nx (RestrictSimpleType t s r comm) = ppComment Before comm $$ text "newtype" <+> ppUnqConId nx t <+> text "=" <+> ppUnqConId nx t <+> ppConId nx s <+> text "deriving (Eq,Show)" $$ text "instance Restricts" <+> ppUnqConId nx t <+> ppConId nx s <+> text "where" $$ nest 4 (text "restricts (" <> ppUnqConId nx t <+> text "x) = x") $$ text "instance SchemaType" <+> ppUnqConId nx t <+> text "where" $$ nest 4 (text "parseSchemaType s = do" $$ nest 4 (text "e <- element [s]" $$ text "commit $ interior e $ parseSimpleType") ) $$ text "instance SimpleType" <+> ppUnqConId nx t <+> text "where" $$ nest 4 (text "acceptingParser = fmap" <+> ppUnqConId nx t <+> text "acceptingParser" -- XXX should enforce the restrictions somehow. (?) $$ text "-- XXX should enforce the restrictions somehow?" $$ text "-- The restrictions are:" $$ vcat (map ((text "-- " <+>) . ppRestrict) r)) where ppRestrict (RangeR occ comm) = text "(RangeR" <+> ppOccurs occ <> text ")" ppRestrict (Pattern regexp comm) = text ("(Pattern "++regexp++")") ppRestrict (Enumeration items) = text "(Enumeration" <+> hsep (map (text . fst) items) <> text ")" ppRestrict (StrLength occ comm) = text "(StrLength" <+> ppOccurs occ <> text ")" ppOccurs = parens . text . show ppHighLevelDecl nx (ExtendSimpleType t s as comm) = ppComment Before comm $$ text "data" <+> ppUnqConId nx t <+> text "=" <+> ppUnqConId nx t <+> ppConId nx s <+> ppConId nx t_attrs $$ text "data" <+> ppConId nx t_attrs <+> text "=" <+> ppConId nx t_attrs $$ nest 4 (ppFields nx t_attrs [] as) $$ text "instance SchemaType" <+> ppUnqConId nx t <+> text "where" $$ nest 4 (text "parseSchemaType s = do" $$ nest 4 (text "(pos,e) <- posnElement [s]" $$ text "commit $ do" $$ nest 2 (vcat (zipWith ppAttr as [0..]) $$ text "reparse [CElem e pos]" $$ text "v <- parseSchemaType s" $$ text "return $" <+> ppUnqConId nx t <+> text "v" <+> attrsValue as) ) ) $$ text "instance Extension" <+> ppUnqConId nx t <+> ppConId nx s <+> text "where" $$ nest 4 (text "supertype (" <> ppUnqConId nx t <> text " s _) = s") where t_attrs = let (XName (N t_base)) = t in XName (N (t_base++"Attributes")) attrsValue [] = ppConId nx t_attrs attrsValue as = parens (ppConId nx t_attrs <+> hsep [text ("a"++show n) | n <- [0..length as-1]]) -- do element [s] -- blah <- attribute foo -- interior e $ do -- simple <- parseText acceptingParser -- return (T simple blah) ppHighLevelDecl nx (UnionSimpleTypes t sts comm) = ppComment Before comm $$ text "data" <+> ppUnqConId nx t <+> text "=" <+> ppUnqConId nx t $$ text "-- Placeholder for a Union type, not yet implemented." ppHighLevelDecl nx (EnumSimpleType t [] comm) = ppComment Before comm $$ text "data" <+> ppUnqConId nx t ppHighLevelDecl nx (EnumSimpleType t is comm) = ppComment Before comm $$ text "data" <+> ppUnqConId nx t $$ nest 4 ( ppvList "=" "|" "deriving (Eq,Show,Enum)" item is ) $$ text "instance SchemaType" <+> ppUnqConId nx t <+> text "where" $$ nest 4 (text "parseSchemaType s = do" $$ nest 4 (text "e <- element [s]" $$ text "commit $ interior e $ parseSimpleType") ) $$ text "instance SimpleType" <+> ppUnqConId nx t <+> text "where" $$ nest 4 (text "acceptingParser =" <+> ppvList "" "`onFail`" "" parseItem is) where item (i,c) = (ppUnqConId nx t <> text "_" <> ppConId nx i) $$ ppComment After c parseItem (i,_) = text "do isWord \"" <> ppXName i <> text "\"; return" <+> (ppUnqConId nx t <> text "_" <> ppConId nx i) ppHighLevelDecl nx (ElementsAttrs t es as comm) = ppComment Before comm $$ text "data" <+> ppUnqConId nx t <+> text "=" <+> ppUnqConId nx t $$ nest 8 (ppFields nx t (uniqueify es) as) $$ text "instance SchemaType" <+> ppUnqConId nx t <+> text "where" $$ nest 4 (text "parseSchemaType s = do" $$ nest 4 (text "(pos,e) <- posnElement [s]" $$ text "commit $ do" $$ nest 2 (vcat (zipWith ppAttr as [0..]) $$ text "interior e $ return" <+> returnValue as $$ nest 4 (vcat (map ppApplyElem es)) ) ) ) where returnValue [] = ppUnqConId nx t returnValue as = parens (ppUnqConId nx t <+> hsep [text ("a"++show n) | n <- [0..length as-1]]) ppApplyElem e = text "`apply`" <+> ppElem nx e ppHighLevelDecl nx (ElementsAttrsAbstract t insts comm) = ppComment Before comm $$ text "data" <+> ppUnqConId nx t $$ nest 8 (ppvList "=" "|" "" ppAbstrCons insts) $$ text "-- instance SchemaType" <+> ppUnqConId nx t <+> text "(declared in Instance module)" -- $$ text "instance SchemaType" <+> ppUnqConId nx t <+> text "where" -- $$ nest 4 (text "parseSchemaType s = do" -- $$ nest 4 (vcat (intersperse (text "`onFail`") -- (map ppParse insts) -- ++ [text "`onFail` fail" <+> errmsg]))) $$ text "" $$ vcat (map ppFwdDecl $ filter (isJust . snd) insts) where ppAbstrCons (name,Nothing) = con name <+> ppConId nx name ppAbstrCons (name,Just mod) = text "forall q . (FwdDecl" <+> fwd name <+> text "q," <+> text "SchemaType q) =>" <+> con name <+> text "("<>fwd name<>text"->q)" <+> fwd name -- ppParse (name,Nothing) = text "(fmap" <+> con name <+> -- text "$ parseSchemaType s)" -- ppParse (name,Just _) = text "(return" <+> con name <+> -- text "`apply` (fmap const $ parseSchemaType s)" <+> -- text "`apply` return" <+> fwd name <> text ")" ppFwdDecl (name,Just mod) = text "-- | Proxy:" <+> ppConId nx name <+> text "declared later in" <+> ppModId nx mod $$ text "data" <+> fwd name <+> text "=" <+> fwd name -- errmsg = text "\"Parse failed when expecting an extension type of" -- <+> ppXName t <> text ",\\n\\\n\\ namely one of:\\n\\\n\\" -- <> hcat (intersperse (text ",") -- (map (ppXName . fst) insts)) -- <> text "\"" fwd name = ppFwdConId nx name con name = ppJoinConId nx t name ppHighLevelDecl nx (ElementOfType e@Element{}) = ppComment Before (elem_comment e) $$ (text "element" <> ppUnqConId nx (elem_name e)) <+> text "::" <+> text "XMLParser" <+> ppConId nx (elem_type e) $$ (text "element" <> ppUnqConId nx (elem_name e)) <+> text "=" <+> (text "parseSchemaType \"" <> ppXName (elem_name e) <> text "\"") ppHighLevelDecl nx e@(ElementAbstractOfType n t substgrp comm) | any notInScope substgrp = (text "-- element" <> ppUnqConId nx n) <+> text "::" <+> text "XMLParser" <+> ppConId nx t $$ text "-- declared in Instances module" | otherwise = ppElementAbstractOfType nx e where notInScope (_,Just _) = True notInScope (_,Nothing) = False ppHighLevelDecl nx (Choice t es comm) = ppComment Before comm $$ text "data" <+> ppUnqConId nx t <+> nest 4 ( ppvList "=" "|" "" choices (zip es [1..]) ) where choices (e,n) = (ppUnqConId nx t <> text (show n)) <+> ppConId nx (elem_type e) -- Comment out the Group for now. Groups get inlined into the ComplexType -- where they are used, so it may not be sensible to declare them separately -- as well. ppHighLevelDecl nx (Group t es comm) = PP.empty -- ppComment Before comm -- $$ text "data" <+> ppConId nx t <+> text "=" -- <+> ppConId nx t <+> hsep (map (ppConId nx . elem_type) es) -- Possibly we want to declare a really more restrictive type, e.g. -- to remove optionality, (Maybe Foo) -> (Foo), [Foo] -> Foo -- consequently the "restricts" method should do a proper translation, -- not merely an unwrapping. ppHighLevelDecl nx (RestrictComplexType t s comm) = ppComment Before comm $$ text "newtype" <+> ppUnqConId nx t <+> text "=" <+> ppUnqConId nx t <+> ppConId nx s $$ text "-- plus different (more restrictive) parser" $$ text "instance Restricts" <+> ppUnqConId nx t <+> ppConId nx s <+> text "where" $$ nest 4 (text "restricts (" <> ppUnqConId nx t <+> text "x) = x") $$ text "instance SchemaType" <+> ppUnqConId nx t <+> text "where" $$ nest 4 (text "parseSchemaType = fmap " <+> ppUnqConId nx t <+> text ". parseSchemaType") -- XXX should enforce the restriction. {- ppHighLevelDecl nx (ExtendComplexType t s es as _ comm) | length es + length as = 1 = ppComment Before comm $$ text "data" <+> ppConId nx t <+> text "=" <+> ppConId nx t <+> ppConId nx s <+> ppFields nx t es as $$ text "instance Extension" <+> ppConId nx t <+> ppConId nx s <+> ppAuxConId nx t <+> text "where" $$ nest 4 (text "supertype (" <> ppConId nx t <> text " s e) = s" $$ text "extension (" <> ppConId nx t <> text " s e) = e") -} ppHighLevelDecl nx (ExtendComplexType t s oes oas es as fwdReqd absSup grandsuper comm) = ppHighLevelDecl nx (ElementsAttrs t (oes++es) (oas++as) comm) $$ ppExtension nx t s fwdReqd absSup oes oas es as $$ (if not (null grandsuper) -- && isJust fwdReqd then ppSuperExtension nx s grandsuper (t,Nothing) else empty) ppHighLevelDecl nx (ExtendComplexTypeAbstract t s insts fwdReqd grandsuper comm) = ppHighLevelDecl nx (ElementsAttrsAbstract t insts comm) $$ ppExtension nx t s fwdReqd True [] [] [] [] $$ if not (null grandsuper) then vcat (map (ppSuperExtension nx t grandsuper) insts) -- FIXME some instances are missing! else empty ppHighLevelDecl nx (XSDInclude m comm) = ppComment After comm $$ text "import" <+> ppModId nx m ppHighLevelDecl nx (XSDImport m ma comm) = ppComment After comm $$ text "import" <+> ppModId nx m <+> maybe empty (\a->text "as"<+>ppConId nx a) ma ppHighLevelDecl nx (XSDComment comm) = ppComment Before comm -------------------------------------------------------------------------------- -- | Instances that depend on FwdDecl'd types, need to be declared in a -- different module. So they have been separated out from ppHighLevelDecl. ppHighLevelInstances :: NameConverter -> Decl -> Doc ppHighLevelInstances nx (ElementsAttrsAbstract t insts comm) = text "instance SchemaType" <+> ppUnqConId nx t <+> text "where" $$ nest 4 (text "parseSchemaType s = do" $$ nest 4 (vcat (intersperse (text "`onFail`") (map ppParse insts) ++ [text "`onFail` fail" <+> errmsg]))) where ppParse (name,Nothing) = text "(fmap" <+> con name <+> text "$ parseSchemaType s)" ppParse (name,Just _) = text "(return" <+> con name <+> text "`apply` (fmap const $ parseSchemaType s)" <+> text "`apply` return" <+> fwd name <> text ")" errmsg = text "\"Parse failed when expecting an extension type of" <+> ppXName t <> text ",\\n\\\n\\ namely one of:\\n\\\n\\" <> hcat (intersperse (text ",") (map (ppXName . fst) insts)) <> text "\"" fwd name = ppFwdConId nx name con name = ppJoinConId nx t name ppHighLevelInstances nx e@(ElementAbstractOfType n t substgrp comm) | any notInScope substgrp = ppElementAbstractOfType nx e | otherwise = empty where notInScope (_,Just _) = True notInScope (_,Nothing) = False ppHighLevelInstances nx (ExtendComplexType t s oes oas es as fwdReqd absSup grandsuper comm) = empty -- ppExtension nx t s fwdReqd absSup oes oas es as -- $$ (if not (null grandsuper) && isJust fwdReqd -- then ppSuperExtension nx s grandsuper (t,Nothing) -- else empty) ppHighLevelInstances nx (ExtendComplexTypeAbstract t s insts fwdReqd grandsuper comm) = ppHighLevelInstances nx (ElementsAttrsAbstract t insts comm) -- $$ ppExtension nx t s fwdReqd True [] [] [] [] -- $$ if not (null grandsuper) -- then vcat (map (ppSuperExtension nx t grandsuper) insts) -- -- FIXME some instances are missing! -- else empty ppElementAbstractOfType nx (ElementAbstractOfType n t substgrp comm) = ppComment Before comm $$ (text "element" <> ppUnqConId nx n) <+> text "::" <+> text "XMLParser" <+> ppConId nx t $$ (text "element" <> ppUnqConId nx n) <+> text "=" <+> vcat (intersperse (text "`onFail`") (map ppOne substgrp) ++ [text "`onFail` fail" <+> errmsg]) where ppOne (c,Nothing) = text "fmap" <+> text "supertype" -- ppJoinConId nx t c <+> (text "element" <> ppConId nx c) ppOne (c,Just _) = text "fmap" <+> text "supertype" -- ppJoinConId nx t c <+> (text "element" <> ppConId nx c) <+> text "-- FIXME: element is forward-declared" errmsg = text "\"Parse failed when expecting an element in the substitution group for\\n\\\n\\ <" <> ppXName n <> text ">,\\n\\\n\\ namely one of:\\n\\\n\\<" <> hcat (intersperse (text ">, <") (map (ppXName . fst) substgrp)) <> text ">\"" -------------------------------------------------------------------------------- -- | Generate an instance of the Extension class for a subtype/supertype pair. ppExtension :: NameConverter -> XName -> XName -> Maybe XName -> Bool -> [Element] -> [Attribute] -> [Element] -> [Attribute] -> Doc ppExtension nx t s fwdReqd abstractSuper oes oas es as = text "instance Extension" <+> ppUnqConId nx t <+> ppConId nx s <+> text "where" $$ (if abstractSuper then nest 4 (text "supertype v" <+> text "=" <+> ppJoinConId nx s t <+> (if isJust fwdReqd then text "(\\_-> v)" <+> ppFwdConId nx t else text "v")) else nest 4 (text "supertype (" <> ppType t (oes++es) (oas++as) <> text ") =" $$ nest 11 (ppType s oes oas) )) $$ (if isJust fwdReqd then -- text "data" <+> fwd t <+> text "=" <+> fwd t $$ -- already defined text "" $$ text "-- | Proxy" <+> fwd t <+> text "was declared earlier in" <+> ppModId nx (fromJust fwdReqd) $$ text "instance FwdDecl" <+> fwd t <+> ppConId nx t else empty) where fwd name = ppFwdConId nx name ppType t es as = ppUnqConId nx t <+> hsep (take (length as) [text ('a':show n) | n<-[0..]]) <+> hsep (take (length es) [text ('e':show n) | n<-[0..]]) -- | Generate an instance of the Extension class for a type and its -- "grand"-supertype, that is, the supertype of its supertype. ppSuperExtension :: NameConverter -> XName -> [XName] -> (XName,Maybe XName) -> Doc {- ppSuperExtension nx super (grandSuper:_) (t,Nothing) = text "instance Extension" <+> ppUnqConId nx t <+> ppConId nx grandSuper <+> text "where" $$ nest 4 (text "supertype = (supertype ::" <+> ppUnqConId nx super <+> text "->" <+> ppConId nx grandSuper <> text ")" $$ nest 12 (text ". (supertype ::" <+> ppUnqConId nx t <+> text "->" <+> ppConId nx super <> text ")")) -} ppSuperExtension nx super (grandSuper:_) (t,Just mod) = -- fwddecl -- FIXME: generate comment for all of the grandSupers. text "-- instance Extension" <+> ppUnqConId nx t <+> ppConId nx grandSuper $$ text "-- will be declared in module" <+> ppModId nx mod ppSuperExtension nx super grandSupers (t,Nothing) = vcat (map (ppSuper t) (map reverse . drop 2 . inits $ super: grandSupers)) where ppSuper :: XName -> [XName] -> Doc ppSuper t gss@(gs:_) = text "instance Extension" <+> ppUnqConId nx t <+> ppConId nx gs <+> text "where" $$ nest 4 (text "supertype" <+> (ppvList "=" "." "" coerce (zip (tail gss++[t]) gss))) coerce (a,b) = text "(supertype ::" <+> ppUnqConId nx a <+> text "->" <+> ppConId nx b <> text ")" -- | Generate named fields from elements and attributes. ppFields :: NameConverter -> XName -> [Element] -> [Attribute] -> Doc ppFields nx t es as | null es && null as = empty ppFields nx t es as = ppvList "{" "," "}" id fields where fields = map (ppFieldAttribute nx t) as ++ zipWith (ppFieldElement nx t) es [0..] -- | Generate a single named field from an element. ppFieldElement :: NameConverter -> XName -> Element -> Int -> Doc ppFieldElement nx t e@Element{} _ = ppFieldId nx t (elem_name e) <+> text "::" <+> ppElemTypeName nx id e $$ ppComment After (elem_comment e) ppFieldElement nx t e@OneOf{} i = ppFieldId nx t (XName $ N $"choice"++show i) <+> text "::" <+> ppElemTypeName nx id e $$ ppComment After (elem_comment e) ppFieldElement nx t e@AnyElem{} i = ppFieldId nx t (XName $ N $"any"++show i) <+> text "::" <+> ppElemTypeName nx id e $$ ppComment After (elem_comment e) ppFieldElement nx t e@Text{} i = ppFieldId nx t (XName $ N $"text"++show i) <+> text "::" <+> ppElemTypeName nx id e -- | What is the name of the type for an Element (or choice of Elements)? ppElemTypeName :: NameConverter -> (Doc->Doc) -> Element -> Doc ppElemTypeName nx brack e@Element{} = ppTypeModifier (elem_modifier e) brack $ ppConId nx (elem_type e) ppElemTypeName nx brack e@OneOf{} = brack $ ppTypeModifier (elem_modifier e) parens $ text "OneOf" <> text (show (length (elem_oneOf e))) <+> hsep (map ppSeq (elem_oneOf e)) where ppSeq [] = text "()" ppSeq [e] = ppElemTypeName nx parens e ppSeq es = text "(" <> hcat (intersperse (text ",") (map (ppElemTypeName nx parens) es)) <> text ")" ppElemTypeName nx brack e@AnyElem{} = brack $ ppTypeModifier (elem_modifier e) id $ text "AnyElement" ppElemTypeName nx brack e@Text{} = text "String" -- | Generate a single named field from an attribute. ppFieldAttribute :: NameConverter -> XName -> Attribute -> Doc ppFieldAttribute nx t a = ppFieldId nx t (attr_name a) <+> text "::" <+> ppConId nx (attr_type a) $$ ppComment After (attr_comment a) -- | Generate a list or maybe type name (possibly parenthesised). ppTypeModifier :: Modifier -> (Doc->Doc) -> Doc -> Doc ppTypeModifier Single _ d = d ppTypeModifier Optional k d = k $ text "Maybe" <+> k d ppTypeModifier (Range (Occurs Nothing Nothing)) _ d = d ppTypeModifier (Range (Occurs (Just 0) Nothing)) k d = k $ text "Maybe" <+> k d ppTypeModifier (Range (Occurs _ _)) _ d = text "[" <> d <> text "]" -- | Generate a parser for a list or Maybe value. ppElemModifier Single doc = doc ppElemModifier Optional doc = text "optional" <+> parens doc ppElemModifier (Range (Occurs Nothing Nothing)) doc = doc ppElemModifier (Range (Occurs (Just 0) Nothing)) doc = text "optional" <+> parens doc ppElemModifier (Range o) doc = text "between" <+> (parens (text (show o)) $$ parens doc) -- | Split long lines of comment text into a paragraph with a maximum width. paragraph :: Int -> String -> String paragraph n s = go n (words s) where go i [] = [] go i (x:xs) | len [Element] uniqueify = go [] where go seen [] = [] go seen (e@Element{}:es) | show (elem_name e) `elem` seen = let fresh = new (`elem`seen) (elem_name e) in e{elem_name=fresh} : go (show fresh:seen) es | otherwise = e: go (show (elem_name e): seen) es go seen (e:es) = e : go seen es new pred (XName (N n)) = XName $ N $ head $ dropWhile pred [(n++show i) | i <- [2..]] new pred (XName (QN ns n)) = XName $ QN ns $ head $ dropWhile pred [(n++show i) | i <- [2..]]