packages feed

OpenGLRaw-2.0.0.0: RegistryProcessor/src/Registry.hs

-- 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 (\(a,b) -> Commands a b
         ,\(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]
                          c <- return [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 (\(a,b) -> InterfaceElement ty a b
                 ,\(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