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 |
||
38 | # match #endif |
||
39 | self.ENDIF = re.compile('#\s*endif') |
||
40 | # match typedef abc def; |
||
41 | self.TYPEDEF = re.compile('typedef\s+(?P |
||
42 | # match #define XYZ VAL |
||
43 | self.DEFINE = re.compile('#\s*define\s+(?P |
||
44 | # match GLAPI |
||
45 | self.GLAPI = re.compile('^GL_?API(CALL)?\s+(?P |
||
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 ' |
||
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 |
||
376 | |||
377 | if cat["enums"] and cat["types"]: |
||
378 | |||
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 |
||
392 | |||
393 | for func in cat["functions"]: |
||
394 | |||
395 | ret = func[0] |
||
396 | name = func[1][2:] |
||
397 | params = func[2] |
||
398 | |||
399 | attrs = 'name="%s" offset="assign"' % name |
||
400 | print '%s |
||
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 |
||
408 | |||
409 | print '%s' % spaces(indent) |
||
410 | |||
411 | print '' |
||
412 | |||
413 | |||
414 | def show_usage(): |
||
415 | print "Usage: %s [-v] |
||
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 | |||
443 | print '' % (h) |
||
444 | print ' |
||
445 | |||
446 | output_xml(h, hlist) |
||
447 | print '' |
||
448 | |||
449 | if __name__ == '__main__': |
||
450 | main()!-->!DOCTYPE>?xml>>>>>> |