-- This module contains a 1:1 translation of registry.rnc with an (un-)parser. -- No simplifications or GL/GLX/EGL/WGL-specific things are done here. module Registry ( parseRegistry , unparseRegistry , Registry(..) , RegistryElement(..) , Types(..) , Type(..) , Groups(..) , Group(..) , Enums(..) , Enum'(..) , Unused(..) , Commands(..) , Command(..) , Proto(..) , Param(..) , GLX(..) , Feature(..) , Modification(..) , ModificationKind(..) , Extensions(..) , Extension(..) , ConditionalModification(..) , InterfaceElement(..) , InterfaceElementKind(..) , Integer'(..) , TypeName(..) , TypeSuffix(..) , StringGroup(..) , ProfileName(..) , Vendor(..) , Comment(..) , Name(..) ) where import Data.Maybe (maybeToList) import Text.XML.HXT.Core parseRegistry :: String -> Either String Registry parseRegistry = head . runLA (xreadDoc >>> neg isXmlPi >>> removeAllWhiteSpace >>> canonicalizeAllNodes >>> -- needed to e.g. process CDATA, remove XML comments, etc. arr (unpickleDoc' xpRegistry)) unparseRegistry :: Registry -> String unparseRegistry = concat . (pickleDoc xpRegistry >>> runLA (writeDocumentToString [withIndent yes, withOutputEncoding utf8, withXmlPi yes])) -- Note: We do this slightly different from the schema. newtype Registry = Registry { unRegistry :: [RegistryElement] } deriving (Eq, Ord, Show) xpRegistry :: PU Registry xpRegistry = xpWrap (Registry, unRegistry) $ xpElem "registry" $ xpList xpRegistryElement data RegistryElement = CommentElement { unCommentElement :: Comment } | TypesElement { unTypesElement :: Types } | GroupsElement { unGroupsElement :: Groups } | EnumsElement { unEnumsElement :: Enums } | CommandsElement { unCommandsElement :: Commands } | FeatureElement { unFeatureElement :: Feature } | ExtensionsElement { unExtensionsElement :: Extensions } deriving (Eq, Ord, Show) xpRegistryElement :: PU RegistryElement xpRegistryElement = xpAlt tag pus where tag (CommentElement _) = 0 tag (TypesElement _) = 1 tag (GroupsElement _) = 2 tag (EnumsElement _) = 3 tag (CommandsElement _) = 4 tag (FeatureElement _) = 5 tag (ExtensionsElement _) = 6 pus = [ xpWrap (CommentElement, unCommentElement) $ xpElem "comment" xpComment , xpWrap (TypesElement, unTypesElement) xpTypes , xpWrap (GroupsElement, unGroupsElement) xpGroups , xpWrap (EnumsElement, unEnumsElement) xpEnums , xpWrap (CommandsElement, unCommandsElement) xpCommands , xpWrap (FeatureElement, unFeatureElement) xpFeature , xpWrap (ExtensionsElement, unExtensionsElement) xpExtensions ] newtype Types = Types { unTypes :: [Type] } deriving (Eq, Ord, Show) xpTypes :: PU Types xpTypes = xpWrap (Types, unTypes) $ xpElem "types" $ xpList xpType data Type = Type { typeAPI :: Maybe String , typeRequires :: Maybe String , typeName1 :: Maybe TypeName , typeType :: Maybe String , typeComment :: Maybe Comment , typeText1 :: String , typeAPIEntry :: Maybe String , typeText2 :: String , typeName2 :: Maybe TypeName , typeText3 :: String } deriving (Eq, Ord, Show) xpType :: PU Type xpType = xpWrap ( \(a, b, c, d, e, f, g, h, i, j) -> Type a b c d e f g h i j , \(Type a b c d e f g h i j) -> (a, b, c, d, e, f, g, h, i, j)) $ xpElem "type" $ xp10Tuple (xpAttrImplied "api" xpText) (xpAttrImplied "requires" xpText) (xpAttrImplied "name" xpTypeName) (xpAttrImplied "type" xpText) (xpOption xpCommentAttr) xpText0 (xpOption $ xpElem "apientry" xpText0) xpText0 (xpOption $ xpElem "name" xpTypeName) xpText0 newtype Groups = Groups { unGroups :: [Group] } deriving (Eq, Ord, Show) xpGroups :: PU Groups xpGroups = xpWrap (Groups, unGroups) $ xpElem "groups" $ xpList xpGroup data Group = Group { groupName :: Name , groupComment :: Maybe Comment , groupEnums :: [Name] } deriving (Eq, Ord, Show) xpGroup :: PU Group xpGroup = xpWrap (\(a, b, c) -> Group a b c, \(Group a b c) -> (a, b, c)) $ xpElem "group" $ xpTriple xpName (xpOption xpCommentAttr) (xpList $ xpElem "enum" xpName) data Enums = Enums { enumsNamespace :: Maybe String , enumsGroup :: Maybe String , enumsType :: Maybe String -- enumsStart is conceptually a 'Maybe Integer', but egl.xml cheats a bit and -- uses e.g. 'start="0x3060-0x306F"' as a shorthand for -- 'start="0x3060" end="0x306F"'. , enumsStart :: Maybe Integer' , enumsEnd :: Maybe Integer' , enumsVendor :: Maybe Vendor , enumsComment :: Maybe Comment , enumsEnumOrUnuseds :: [Either Enum' Unused] } deriving (Eq, Ord, Show) xpEnums :: PU Enums xpEnums = xpWrap ( \(a, b, c, d, e, f, g, h) -> Enums a b c d e f g h , \(Enums a b c d e f g h) -> (a, b, c, d, e, f, g, h)) $ xpElem "enums" $ xp8Tuple (xpAttrImplied "namespace" xpText) (xpAttrImplied "group" xpText) (xpAttrImplied "type" xpText) (xpAttrImplied "start" xpInteger) (xpAttrImplied "end" xpInteger) (xpOption xpVendor) (xpOption xpCommentAttr) (xpList $ xpEither xpEnum xpUnused) xpEither :: PU a -> PU b -> PU (Either a b) xpEither pl pr = xpAlt tag pus where tag (Left _) = 0 tag (Right _) = 1 pus = [xpWrap (Left, \(Left l) -> l) pl, xpWrap (Right, \(Right r) -> r) pr] data Enum' = Enum { enumValue :: String , enumAPI :: Maybe String , enumType :: Maybe TypeSuffix , enumName :: String , enumAlias :: Maybe String , enumComment :: Maybe Comment } deriving (Eq, Ord, Show) -- NOTE: The spec uses the interleave pattern, which is not needed: Attributes -- are by definition unordered. xpEnum :: PU Enum' xpEnum = xpWrap ( \(a, b, c, d, e, f) -> Enum a b c d e f , \(Enum a b c d e f) -> (a, b, c, d, e, f)) $ xpElem "enum" $ xp6Tuple (xpAttr "value" xpText) (xpAttrImplied "api" xpText) (xpAttrImplied "type" xpTypeSuffix) (xpAttr "name" xpText) (xpAttrImplied "alias" xpText) (xpOption xpCommentAttr) data Unused = Unused { unusedStart :: Integer' , unusedEnd :: Maybe Integer' , unusedVendor :: Maybe Vendor , unusedComment :: Maybe Comment } deriving (Eq, Ord, Show) xpUnused :: PU Unused xpUnused = xpWrap (\(a, b, c, d) -> Unused a b c d, \(Unused a b c d) -> (a, b, c, d)) $ xpElem "unused" $ xp4Tuple (xpAttr "start" xpInteger) (xpAttrImplied "end" xpInteger) (xpOption xpVendor) (xpOption xpCommentAttr) data Commands = Commands { commandsNamespace :: Maybe String , commandsCommands :: [Command] } deriving (Eq, Ord, Show) xpCommands :: PU Commands xpCommands = xpWrap (uncurry Commands, \(Commands a b) -> (a, b)) $ xpElem "commands" $ xpPair (xpAttrImplied "namespace" xpText) (xpList xpCommand) data Command = Command { commandComment :: Maybe Comment , commandProto :: Proto , commandParams :: [Param] , commandAlias :: Maybe Name , commandVecEquiv :: Maybe Name , commandGLXs :: [GLX] } deriving (Eq, Ord, Show) xpCommand :: PU Command xpCommand = xpWrap ( \(a, b, c, (d, e, f)) -> Command a b c d e f , \(Command a b c d e f) -> (a, b, c, (d, e, f))) $ xpElem "command" $ xp4Tuple (xpOption xpCommentAttr) xpProto (xpList xpParam) xpCommandTail -- The spec uses the interleave pattern here, which is not supported in hxt. -- As a workaround, we use a list of disjoint types. xpCommandTail :: PU (Maybe Name, Maybe Name, [GLX]) xpCommandTail = xpWrapEither ( \xs -> do a <- check "alias" [x | AliasElement x <- xs] b <- check "vecequiv" [x | VecEquivElement x <- xs] let c = [x | GLXElement x <- xs] return (a, b, c) , \(a, b, c) -> map AliasElement (maybeToList a) ++ map VecEquivElement (maybeToList b) ++ map GLXElement c) $ xpList $ xpAlt tag pus where tag (AliasElement _) = 0 tag (VecEquivElement _) = 1 tag (GLXElement _) = 2 pus = [ xpWrap (AliasElement, unAliasElement) $ xpElem "alias" xpName , xpWrap (VecEquivElement, unVecEquivElement) $ xpElem "vecequiv" xpName , xpWrap (GLXElement, unGLXElement) xpGLX ] check n xs = case xs of [] -> Right Nothing [x] -> Right $ Just x _ -> Left $ "expected at most one '" ++ n ++ "' element" data CommandTail = AliasElement { unAliasElement :: Name } | VecEquivElement { unVecEquivElement :: Name } | GLXElement { unGLXElement :: GLX } deriving (Eq, Ord, Show) data Proto = Proto { protoGroup :: Maybe String , protoText1 :: String , protoPtype :: Maybe TypeName , protoText2 :: String , protoName :: String , protoText3 :: String } deriving (Eq, Ord, Show) xpProto :: PU Proto xpProto = xpWrap ( \(a, b, c, d, e, f) -> Proto a b c d e f , \(Proto a b c d e f) -> (a, b, c, d, e, f)) $ xpElem "proto" $ xp6Tuple (xpAttrImplied "group" xpText) xpText0 (xpOption $ xpElem "ptype" xpTypeName) xpText0 (xpElem "name" xpText) xpText0 data Param = Param { paramLen :: Maybe String , paramProto :: Proto } deriving (Eq, Ord, Show) xpParam :: PU Param xpParam = xpWrap ( \(b, a, c, d, e, f, g) -> Param a (Proto b c d e f g) , \(Param a (Proto b c d e f g)) -> (b, a, c, d, e, f, g)) $ xpElem "param" $ xp7Tuple (xpAttrImplied "group" xpText) (xpAttrImplied "len" xpText) xpText0 (xpOption $ xpElem "ptype" xpTypeName) xpText0 (xpElem "name" xpText) xpText0 data GLX = GLX { glxType :: String , glxOpcode :: Int , glxName :: Maybe Name , glxComment :: Maybe Comment } deriving (Eq, Ord, Show) xpGLX :: PU GLX xpGLX = xpWrap (\(a, b, c, d) -> GLX a b c d, \(GLX a b c d) -> (a, b, c, d)) $ xpElem "glx" $ xp4Tuple (xpAttr "type" xpText) (xpAttr "opcode" xpInt) (xpOption xpName) (xpOption xpCommentAttr) data Feature = Feature { featureAPI :: String , featureName :: Name , featureNumber :: String -- actually xsd:decimal, but used as a string , featureProtect :: Maybe String , featureComment :: Maybe Comment , featureModifications :: [Modification] } deriving (Eq, Ord, Show) xpFeature :: PU Feature xpFeature = xpWrap ( \(a, b, c, d, e, f) -> Feature a b c d e f , \(Feature a b c d e f) -> (a, b, c, d, e, f)) $ xpElem "feature" $ xp6Tuple (xpAttr "api" xpText) xpName (xpAttr "number" xpText) (xpAttrImplied "protect" xpText) (xpOption xpCommentAttr) (xpList xpModification) data Modification = Modification { modificationModificationKind :: ModificationKind , modificationProfileName :: Maybe ProfileName , modificationComment :: Maybe Comment , modificationInterfaceElements :: [InterfaceElement] } deriving (Eq, Ord, Show) data ModificationKind = Require | Remove -- TODO: Better name deriving (Eq, Ord, Show, Enum) xpModification :: PU Modification xpModification = xpAlt (fromEnum . modificationModificationKind) pus where pus = [xpMod "require" Require, xpMod "remove" Remove] xpMod el kind = xpWrap ( \(a, b, c) -> Modification kind a b c , \(Modification _ a b c) -> (a, b, c)) $ xpElem el $ xpTriple (xpOption xpProfileName) (xpOption xpCommentAttr) (xpList xpInterfaceElement) newtype Extensions = Extensions { unExtensions :: [Extension] } deriving (Eq, Ord, Show) xpExtensions :: PU Extensions xpExtensions = xpWrap (Extensions, unExtensions) $ xpElem "extensions" $ xpList xpExtension data Extension = Extension { extensionName :: Name , extensionProtect :: Maybe String , extensionSupported :: Maybe StringGroup , extensionComment :: Maybe Comment , extensionsRequireRemove :: [ConditionalModification] } deriving (Eq, Ord, Show) xpExtension :: PU Extension xpExtension = xpWrap ( \(a, b, c, d, e) -> Extension a b c d e , \(Extension a b c d e) -> (a, b, c, d, e)) $ xpElem "extension" $ xp5Tuple xpName (xpAttrImplied "protect" xpText) (xpAttrImplied "supported" xpStringGroup) (xpOption xpCommentAttr) (xpList xpConditionalModification) data ConditionalModification = ConditionalModification { conditionalModificationAPI :: Maybe String , conditionalModificationModification :: Modification } deriving (Eq, Ord, Show) xpConditionalModification :: PU ConditionalModification xpConditionalModification = xpAlt (fromEnum . modificationModificationKind . conditionalModificationModification) pus where pus = [xpMod "require" Require, xpMod "remove" Remove] xpMod el kind = xpWrap ( \(a, b, c, d) -> ConditionalModification a (Modification kind b c d) , \(ConditionalModification a (Modification _ b c d)) -> (a, b, c, d)) $ xpElem el $ xp4Tuple (xpAttrImplied "api" xpText) (xpOption xpProfileName) (xpOption xpCommentAttr) (xpList xpInterfaceElement) data InterfaceElement = InterfaceElement { interfaceElementKind :: InterfaceElementKind , interfaceElementName :: Name , interfaceElementComment :: Maybe Comment } deriving (Eq, Ord, Show) data InterfaceElementKind = InterfaceElementType | InterfaceElementEnum | InterfaceElementCommand deriving (Eq, Ord, Show, Enum) xpInterfaceElement :: PU InterfaceElement xpInterfaceElement = xpAlt (fromEnum . interfaceElementKind) pus where pus = [ xpIE InterfaceElementType "type" , xpIE InterfaceElementEnum "enum" , xpIE InterfaceElementCommand "command" ] xpIE ty el = xpWrap (uncurry (InterfaceElement ty), \(InterfaceElement _ a b) -> (a, b)) $ xpElem el $ xpPair xpName (xpOption xpCommentAttr) newtype Integer' = Integer { unInteger :: String } deriving (Eq, Ord, Show) xpInteger :: PU Integer' xpInteger = xpWrap (Integer, unInteger) xpText newtype TypeName = TypeName { unTypeName :: String } deriving (Eq, Ord, Show) xpTypeName :: PU TypeName xpTypeName = xpWrap (TypeName, unTypeName) xpText newtype TypeSuffix = TypeSuffix { unTypeSuffix :: String } deriving (Eq, Ord, Show) xpTypeSuffix :: PU TypeSuffix xpTypeSuffix = xpWrap (TypeSuffix, unTypeSuffix) xpText newtype StringGroup = StringGroup { unStringGroup :: String } deriving (Eq, Ord, Show) xpStringGroup :: PU StringGroup xpStringGroup = xpWrap (StringGroup, unStringGroup) xpText newtype ProfileName = ProfileName { unProfileName :: String } deriving (Eq, Ord, Show) xpProfileName :: PU ProfileName xpProfileName = xpAttr "profile" $ xpWrap (ProfileName, unProfileName) xpText newtype Vendor = Vendor { unVendor :: String } deriving (Eq, Ord, Show) xpVendor :: PU Vendor xpVendor = xpAttr "vendor" $ xpWrap (Vendor, unVendor) xpText newtype Comment = Comment { unComment :: String } deriving (Eq, Ord, Show) xpComment :: PU Comment xpComment = xpWrap (Comment, unComment) xpText xpCommentAttr :: PU Comment xpCommentAttr = xpAttr "comment" xpComment newtype Name = Name { unName :: String } deriving (Eq, Ord, Show) xpName :: PU Name xpName = xpAttr "name" $ xpWrap (Name, unName) xpText