File: Type.hs

package info (click to toggle)
haskell-haskell-gi 0.26.12-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 800 kB
  • sloc: haskell: 8,617; ansic: 74; makefile: 4
file content (198 lines) | stat: -rw-r--r-- 7,795 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
{-# LANGUAGE CPP #-}
{-# LANGUAGE RecordWildCards, PatternGuards #-}
-- | Parsing type information from GIR files.
module Data.GI.GIR.Type
    ( parseType
    , queryCType
    , parseCType
    , queryElementCType
    , parseOptionalType
    ) where

#include "HsBaseConfig.h"

import Data.Maybe (catMaybes)
#if !MIN_VERSION_base(4,11,0)
import Data.Monoid ((<>))
#endif
import Data.Text (Text)
import qualified Data.Text as T

import Data.GI.GIR.BasicTypes (Type(..), BasicType(..))
import Data.GI.GIR.Parser

-- | Map the given type name to a `BasicType` (defined in
-- Data.GI.GIR.BasicTypes), if possible.
nameToBasicType :: Text -> Maybe BasicType
nameToBasicType "gpointer" = Just TPtr
nameToBasicType "gboolean" = Just TBoolean
nameToBasicType "gchar"    = Just TInt8
nameToBasicType "gint"     = Just TInt
nameToBasicType "guint"    = Just TUInt
nameToBasicType "glong"    = Just TLong
nameToBasicType "gulong"   = Just TULong
nameToBasicType "gint8"    = Just TInt8
nameToBasicType "guint8"   = Just TUInt8
nameToBasicType "gint16"   = Just TInt16
nameToBasicType "guint16"  = Just TUInt16
nameToBasicType "gint32"   = Just TInt32
nameToBasicType "guint32"  = Just TUInt32
nameToBasicType "gint64"   = Just TInt64
nameToBasicType "guint64"  = Just TUInt64
nameToBasicType "gfloat"   = Just TFloat
nameToBasicType "gdouble"  = Just TDouble
nameToBasicType "gunichar" = Just TUniChar
nameToBasicType "GType"    = Just TGType
nameToBasicType "utf8"     = Just TUTF8
nameToBasicType "filename" = Just TFileName
nameToBasicType "gintptr"  = Just TIntPtr
nameToBasicType "guintptr" = Just TUIntPtr
nameToBasicType "gshort"   = Just TShort
nameToBasicType "gushort"  = Just TUShort
nameToBasicType "gssize"   = Just TSSize
nameToBasicType "gsize"    = Just TSize
nameToBasicType "time_t"   = Just Ttime_t
nameToBasicType "off_t"    = Just Toff_t
nameToBasicType "dev_t"    = Just Tdev_t
nameToBasicType "gid_t"    = Just Tgid_t
nameToBasicType "pid_t"    = Just Tpid_t
nameToBasicType "socklen_t" = Just Tsocklen_t
nameToBasicType "uid_t"    = Just Tuid_t
nameToBasicType _          = Nothing

-- | The different array types.
parseArrayInfo :: Parser Type
parseArrayInfo = queryAttr "name" >>= \case
      Just "GLib.Array" -> TGArray <$> parseType
      Just "GLib.PtrArray" -> TPtrArray <$> parseType
      Just "GLib.ByteArray" -> return TByteArray
      Just other -> parseError $ "Unsupported array type: \"" <> other <> "\""
      Nothing -> parseCArrayType

-- | A C array
parseCArrayType :: Parser Type
parseCArrayType = do
  zeroTerminated <- queryAttr "zero-terminated" >>= \case
                    Just b -> parseBool b
                    Nothing -> return True
  length <- queryAttr "length" >>= \case
            Just l -> parseIntegral l
            Nothing -> return (-1)
  fixedSize <- queryAttr "fixed-size" >>= \case
               Just s -> parseIntegral s
               Nothing -> return (-1)
  elementType <- parseType
  return $ TCArray zeroTerminated fixedSize length elementType

-- | A hash table.
parseHashTable :: Parser Type
parseHashTable = parseTypeElements >>= \case
                 [] -> return $ TGHash (TBasicType TPtr) (TBasicType TPtr)
                 [Just key, Just value] -> return $ TGHash key value
                 other -> parseError $ "Unsupported hash type: "
                                       <> T.pack (show other)

-- | Parse a `GClosure` declaration.
parseClosure :: Parser Type
parseClosure = queryAttr "closure-type" >>= \case
                Just t -> (TGClosure . Just) <$> parseTypeName t
                Nothing -> return $ TGClosure Nothing

-- | For GLists and GSLists there is sometimes no information about
-- the type of the elements. In these cases we report them as
-- pointers.
parseListType :: Parser Type
parseListType = queryType >>= \case
                Just t -> return t
                Nothing -> return (TBasicType TPtr)

-- | A type which is not a BasicType or array.
parseFundamentalType :: Text -> Text -> Parser Type
parseFundamentalType "GLib" "List" = TGList <$> parseListType
parseFundamentalType "GLib" "SList" = TGSList <$> parseListType
parseFundamentalType "GLib" "HashTable" = parseHashTable
parseFundamentalType "GLib" "Error" = return TError
parseFundamentalType "GLib" "Variant" = return TVariant
parseFundamentalType "GObject" "ParamSpec" = return TParamSpec
parseFundamentalType "GObject" "Value" = return TGValue
parseFundamentalType "GObject" "Closure" = parseClosure
-- A TInterface type (basically, everything that is not of a known type).
parseFundamentalType ns n = resolveQualifiedTypeName (Name ns n)

-- | Parse a type given as a string.
parseTypeName :: Text -> Parser Type
parseTypeName typeName = case nameToBasicType typeName of
    Just b -> return (TBasicType b)
    Nothing -> case T.split ('.' ==) typeName of
                 [ns, n] -> parseFundamentalType ns n
                 [n] -> do
                   ns <- currentNamespace
                   parseFundamentalType ns n
                 _ -> parseError $ "Unsupported type form: \""
                                   <> typeName <> "\""

-- | Parse information on a "type" element. Returns either a `Type`,
-- or `Nothing` indicating that the name of the type in the
-- introspection data was "none" (associated with @void@ in C).
parseTypeInfo :: Parser (Maybe Type)
parseTypeInfo = do
  typeName <- getAttr "name"
  if typeName == "none"
  then return Nothing
  else Just <$> parseTypeName typeName

-- | Find the children giving the type of the given element.
parseTypeElements :: Parser [Maybe Type]
parseTypeElements = do
  types <- parseChildrenWithLocalName "type" parseTypeInfo
  arrays <- parseChildrenWithLocalName "array" parseArrayInfo
  return (types ++ map Just arrays)

-- | Find the C name for the current element.
queryCType :: Parser (Maybe Text)
queryCType = queryAttrWithNamespace CGIRNS "type"

-- | Parse the C type for the current node.
parseCType :: Parser Text
parseCType = getAttrWithNamespace CGIRNS "type"

-- | Find the children giving the C type for the element.
parseCTypeNameElements :: Parser [Text]
parseCTypeNameElements = do
  types <- parseChildrenWithLocalName "type" queryCType
  arrays <- parseChildrenWithLocalName "array" queryCType
  return (catMaybes (types ++ arrays))

-- | Try to find a type node, but do not error out if it is not
-- found. This _does_ give an error if more than one type node is
-- found, or if the type name is "none".
queryType :: Parser (Maybe Type)
queryType = parseTypeElements >>= \case
            [Just e] -> return (Just e)
            [] -> return Nothing
            [Nothing] -> parseError $ "Unexpected \"none\" type."
            _ -> parseError $ "Found more than one type for the element."

-- | Parse the type of a node (which will be described by a child node
-- named "type" or "array").
parseType :: Parser Type
parseType = parseTypeElements >>= \case
            [Just e] -> return e
            [] -> parseError $ "Did not find a type for the element."
            [Nothing] -> parseError $ "Unexpected \"none\" type."
            _ -> parseError $ "Found more than one type for the element."

-- | Like `parseType`, but allow for @none@, returned as `Nothing`.
parseOptionalType :: Parser (Maybe Type)
parseOptionalType =
    parseTypeElements >>= \case
           [e] -> return e
           [] -> parseError $ "Did not find a type for the element."
           _ -> parseError $ "Found more than one type for the element."

-- | Parse the C-type associated to the element, if found.
queryElementCType :: Parser (Maybe Text)
queryElementCType = parseCTypeNameElements >>= \case
             [ctype] -> return (Just ctype)
             [] -> return Nothing
             _ -> parseError $ "Found more than one type for the element."