Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1901 serge 1
#!/usr/bin/python
2
#
3
# Copyright (C) 2009 Chia-I Wu 
4
#
5
# Permission is hereby granted, free of charge, to any person obtaining a
6
# copy of this software and associated documentation files (the "Software"),
7
# to deal in the Software without restriction, including without limitation
8
# on the rights to use, copy, modify, merge, publish, distribute, sub
9
# license, and/or sell copies of the Software, and to permit persons to whom
10
# the Software is furnished to do so, subject to the following conditions:
11
#
12
# The above copyright notice and this permission notice (including the next
13
# paragraph) shall be included in all copies or substantial portions of the
14
# Software.
15
#
16
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
19
# IBM AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22
# IN THE SOFTWARE.
23
 
24
import sys
25
import os.path
26
import getopt
27
import re
28
 
29
GLAPI = "../../glapi/gen"
30
sys.path.append(GLAPI)
31
 
32
class HeaderParser(object):
33
    """Parser for GL header files."""
34
 
35
    def __init__(self, verbose=0):
36
        # match #if and #ifdef
37
        self.IFDEF = re.compile('#\s*if(n?def\s+(?P\w+)|\s+(?P.+))')
38
        # match #endif
39
        self.ENDIF = re.compile('#\s*endif')
40
        # match typedef abc def;
41
        self.TYPEDEF = re.compile('typedef\s+(?P[\w ]+)\s+(?P\w+);')
42
        # match #define XYZ VAL
43
        self.DEFINE = re.compile('#\s*define\s+(?P\w+)(?P\s+[\w"]*)?')
44
        # match GLAPI
45
        self.GLAPI = re.compile('^GL_?API(CALL)?\s+(?P[\w\s*]+[\w*])\s+(GL)?_?APIENTRY\s+(?P\w+)\s*\((?P[\w\s(,*\[\])]+)\)\s*;')
46
 
47
        self.split_params = re.compile('\s*,\s*')
48
        self.split_ctype = re.compile('(\W)')
49
        # ignore GL_VERSION_X_Y
50
        self.ignore_enum = re.compile('GL(_ES)?_VERSION(_ES_C[ML])?_\d_\d')
51
 
52
        self.verbose = verbose
53
        self._reset()
54
 
55
    def _reset(self):
56
        """Reset to initial state."""
57
        self.ifdef_levels = []
58
        self.need_char = False
59
 
60
    # use typeexpr?
61
    def _format_ctype(self, ctype, fix=True):
62
        """Format a ctype string, optionally fix it."""
63
        # split the type string
64
        tmp = self.split_ctype.split(ctype)
65
        tmp = [s for s in tmp if s and s != " "]
66
 
67
        pretty = ""
68
        for i in xrange(len(tmp)):
69
            # add missing GL prefix
70
            if (fix and tmp[i] != "const" and tmp[i] != "*" and
71
                not tmp[i].startswith("GL")):
72
                tmp[i] = "GL" + tmp[i]
73
 
74
            if i == 0:
75
                pretty = tmp[i]
76
            else:
77
                sep = " "
78
                if tmp[i - 1] == "*":
79
                    sep = ""
80
                pretty += sep + tmp[i]
81
        return pretty
82
 
83
    # use typeexpr?
84
    def _get_ctype_attrs(self, ctype):
85
        """Get the attributes of a ctype."""
86
        is_float = (ctype.find("float") != -1 or ctype.find("double") != -1)
87
        is_signed = not (ctype.find("unsigned")  != -1)
88
 
89
        size = 0
90
        if ctype.find("char") != -1:
91
            size = 1
92
        elif ctype.find("short") != -1:
93
            size = 2
94
        elif ctype.find("int") != -1:
95
            size = 4
96
        elif is_float:
97
            if ctype.find("float") != -1:
98
                size = 4
99
            else:
100
                size = 8
101
 
102
        return (size, is_float, is_signed)
103
 
104
    def _parse_define(self, line):
105
        """Parse a #define line for an ."""
106
        m = self.DEFINE.search(line)
107
        if not m:
108
            if self.verbose and line.find("#define") >= 0:
109
                print "ignore %s" % (line)
110
            return None
111
 
112
        key = m.group("key").strip()
113
        val = m.group("value").strip()
114
 
115
        # enum must begin with GL_ and be all uppercase
116
        if ((not (key.startswith("GL_") and key.isupper())) or
117
            (self.ignore_enum.match(key) and val == "1")):
118
            if self.verbose:
119
                print "ignore enum %s" % (key)
120
            return None
121
 
122
        return (key, val)
123
 
124
    def _parse_typedef(self, line):
125
        """Parse a typedef line for a ."""
126
        m = self.TYPEDEF.search(line)
127
        if not m:
128
            if self.verbose and line.find("typedef") >= 0:
129
                print "ignore %s" % (line)
130
            return None
131
 
132
        f = m.group("from").strip()
133
        t = m.group("to").strip()
134
        if not t.startswith("GL"):
135
            if self.verbose:
136
                print "ignore type %s" % (t)
137
            return None
138
        attrs = self._get_ctype_attrs(f)
139
 
140
        return (f, t, attrs)
141
 
142
    def _parse_gl_api(self, line):
143
        """Parse a GLAPI line for a ."""
144
        m = self.GLAPI.search(line)
145
        if not m:
146
            if self.verbose and line.find("APIENTRY") >= 0:
147
                print "ignore %s" % (line)
148
            return None
149
 
150
        rettype = m.group("return")
151
        rettype = self._format_ctype(rettype)
152
        if rettype == "GLvoid":
153
            rettype = ""
154
 
155
        name = m.group("name")
156
 
157
        param_str = m.group("params")
158
        chunks = self.split_params.split(param_str)
159
        chunks = [s.strip() for s in chunks]
160
        if len(chunks) == 1 and (chunks[0] == "void" or chunks[0] == "GLvoid"):
161
            chunks = []
162
 
163
        params = []
164
        for c in chunks:
165
            # split type and variable name
166
            idx = c.rfind("*")
167
            if idx < 0:
168
                idx = c.rfind(" ")
169
            if idx >= 0:
170
                idx += 1
171
                ctype = c[:idx]
172
                var = c[idx:]
173
            else:
174
                ctype = c
175
                var = "unnamed"
176
 
177
            # convert array to pointer
178
            idx = var.find("[")
179
            if idx >= 0:
180
                var = var[:idx]
181
                ctype += "*"
182
 
183
            ctype = self._format_ctype(ctype)
184
            var = var.strip()
185
 
186
            if not self.need_char and ctype.find("GLchar") >= 0:
187
                self.need_char = True
188
 
189
            params.append((ctype, var))
190
 
191
        return (rettype, name, params)
192
 
193
    def _change_level(self, line):
194
        """Parse a #ifdef line and change level."""
195
        m = self.IFDEF.search(line)
196
        if m:
197
            ifdef = m.group("ifdef")
198
            if not ifdef:
199
                ifdef = m.group("if")
200
            self.ifdef_levels.append(ifdef)
201
            return True
202
        m = self.ENDIF.search(line)
203
        if m:
204
            self.ifdef_levels.pop()
205
            return True
206
        return False
207
 
208
    def _read_header(self, header):
209
        """Open a header file and read its contents."""
210
        lines = []
211
        try:
212
            fp = open(header, "rb")
213
            lines = fp.readlines()
214
            fp.close()
215
        except IOError, e:
216
            print "failed to read %s: %s" % (header, e)
217
        return lines
218
 
219
    def _cmp_enum(self, enum1, enum2):
220
        """Compare two enums."""
221
        # sort by length of the values as strings
222
        val1 = enum1[1]
223
        val2 = enum2[1]
224
        ret = len(val1) - len(val2)
225
        # sort by the values
226
        if not ret:
227
            val1 = int(val1, 16)
228
            val2 = int(val2, 16)
229
            ret = val1 - val2
230
            # in case int cannot hold the result
231
            if ret > 0:
232
                ret = 1
233
            elif ret < 0:
234
                ret = -1
235
        # sort by the names
236
        if not ret:
237
            if enum1[0] < enum2[0]:
238
                ret = -1
239
            elif enum1[0] > enum2[0]:
240
                ret = 1
241
        return ret
242
 
243
    def _cmp_type(self, type1, type2):
244
        """Compare two types."""
245
        attrs1 = type1[2]
246
        attrs2 = type2[2]
247
        # sort by type size
248
        ret = attrs1[0] - attrs2[0]
249
        # float is larger
250
        if not ret:
251
            ret = attrs1[1] - attrs2[1]
252
        # signed is larger
253
        if not ret:
254
            ret = attrs1[2] - attrs2[2]
255
        # reverse
256
        ret = -ret
257
        return ret
258
 
259
    def _cmp_function(self, func1, func2):
260
        """Compare two functions."""
261
        name1 = func1[1]
262
        name2 = func2[1]
263
        ret = 0
264
        # sort by the names
265
        if name1 < name2:
266
            ret = -1
267
        elif name1 > name2:
268
            ret = 1
269
        return ret
270
 
271
    def _postprocess_dict(self, hdict):
272
        """Post-process a header dict and return an ordered list."""
273
        hlist = []
274
        largest = 0
275
        for key, cat in hdict.iteritems():
276
            size = len(cat["enums"]) + len(cat["types"]) + len(cat["functions"])
277
            # ignore empty category
278
            if not size:
279
                continue
280
 
281
            cat["enums"].sort(self._cmp_enum)
282
            # remove duplicates
283
            dup = []
284
            for i in xrange(1, len(cat["enums"])):
285
                if cat["enums"][i] == cat["enums"][i - 1]:
286
                    dup.insert(0, i)
287
            for i in dup:
288
                e = cat["enums"].pop(i)
289
                if self.verbose:
290
                    print "remove duplicate enum %s" % e[0]
291
 
292
            cat["types"].sort(self._cmp_type)
293
            cat["functions"].sort(self._cmp_function)
294
 
295
            # largest category comes first
296
            if size > largest:
297
                hlist.insert(0, (key, cat))
298
                largest = size
299
            else:
300
                hlist.append((key, cat))
301
        return hlist
302
 
303
    def parse(self, header):
304
        """Parse a header file."""
305
        self._reset()
306
 
307
        if self.verbose:
308
            print "Parsing %s" % (header)
309
 
310
        hdict = {}
311
        lines = self._read_header(header)
312
        for line in lines:
313
            if self._change_level(line):
314
                continue
315
 
316
            # skip until the first ifdef (i.e. __gl_h_)
317
            if not self.ifdef_levels:
318
                continue
319
 
320
            cat_name = os.path.basename(header)
321
            # check if we are in an extension
322
            if (len(self.ifdef_levels) > 1 and
323
                self.ifdef_levels[-1].startswith("GL_")):
324
                cat_name = self.ifdef_levels[-1]
325
 
326
            try:
327
                cat = hdict[cat_name]
328
            except KeyError:
329
                cat = {
330
                        "enums": [],
331
                        "types": [],
332
                        "functions": []
333
                }
334
                hdict[cat_name] = cat
335
 
336
            key = "enums"
337
            elem = self._parse_define(line)
338
            if not elem:
339
                key = "types"
340
                elem = self._parse_typedef(line)
341
            if not elem:
342
                key = "functions"
343
                elem = self._parse_gl_api(line)
344
 
345
            if elem:
346
                cat[key].append(elem)
347
 
348
        if self.need_char:
349
            if self.verbose:
350
                print "define GLchar"
351
            elem = self._parse_typedef("typedef char GLchar;")
352
            cat["types"].append(elem)
353
        return self._postprocess_dict(hdict)
354
 
355
def spaces(n, str=""):
356
    spaces = n - len(str)
357
    if spaces < 1:
358
        spaces = 1
359
    return " " * spaces
360
 
361
def output_xml(name, hlist):
362
    """Output a parsed header in OpenGLAPI XML."""
363
 
364
    for i in xrange(len(hlist)):
365
        cat_name, cat = hlist[i]
366
 
367
        print '' % (cat_name)
368
        indent = 4
369
 
370
        for enum in cat["enums"]:
371
            name = enum[0][3:]
372
            value = enum[1]
373
            tab = spaces(41, name)
374
            attrs = 'name="%s"%svalue="%s"' % (name, tab, value)
375
            print '%s' % (spaces(indent), attrs)
376
 
377
        if cat["enums"] and cat["types"]:
378
            print
379
 
380
        for type in cat["types"]:
381
            ctype = type[0]
382
            size, is_float, is_signed = type[2]
383
 
384
            attrs = 'name="%s"' % (type[1][2:])
385
            attrs += spaces(16, attrs) + 'size="%d"' % (size)
386
            if is_float:
387
                attrs += ' float="true"'
388
            elif not is_signed:
389
                attrs += ' unsigned="true"'
390
 
391
            print '%s' % (spaces(indent), attrs)
392
 
393
        for func in cat["functions"]:
394
            print
395
            ret = func[0]
396
            name = func[1][2:]
397
            params = func[2]
398
 
399
            attrs = 'name="%s" offset="assign"' % name
400
            print '%s' % (spaces(indent), attrs)
401
 
402
            for param in params:
403
                attrs = 'name="%s" type="%s"' % (param[1], param[0])
404
                print '%s' % (spaces(indent * 2), attrs)
405
            if ret:
406
                attrs = 'type="%s"' % ret
407
                print '%s' % (spaces(indent * 2), attrs)
408
 
409
            print '%s' % spaces(indent)
410
 
411
        print ''
412
        print
413
 
414
def show_usage():
415
    print "Usage: %s [-v] 
..." % sys.argv[0]
416
    sys.exit(1)
417
 
418
def main():
419
    try:
420
        args, headers = getopt.getopt(sys.argv[1:], "v")
421
    except Exception, e:
422
        show_usage()
423
    if not headers:
424
        show_usage()
425
 
426
    verbose = 0
427
    for arg in args:
428
        if arg[0] == "-v":
429
            verbose += 1
430
 
431
    need_xml_header = True
432
    parser = HeaderParser(verbose)
433
    for h in headers:
434
        h = os.path.abspath(h)
435
        hlist = parser.parse(h)
436
 
437
        if need_xml_header:
438
            print ''
439
            print '' % GLAPI
440
            need_xml_header = False
441
 
442
        print
443
        print '' % (h)
444
        print ''
445
        print
446
        output_xml(h, hlist)
447
        print ''
448
 
449
if __name__ == '__main__':
450
    main()