3 # Copyright (C) 2009 Chia-I Wu <olv@0xlab.org>
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:
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
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
29 GLAPI
= "../../glapi/gen"
30 sys
.path
.append(GLAPI
)
32 class HeaderParser(object):
33 """Parser for GL header files."""
35 def __init__(self
, verbose
=0):
36 # match #if and #ifdef
37 self
.IFDEF
= re
.compile('#\s*if(n?def\s+(?P<ifdef>\w+)|\s+(?P<if>.+))')
39 self
.ENDIF
= re
.compile('#\s*endif')
40 # match typedef abc def;
41 self
.TYPEDEF
= re
.compile('typedef\s+(?P<from>[\w ]+)\s+(?P<to>\w+);')
42 # match #define XYZ VAL
43 self
.DEFINE
= re
.compile('#\s*define\s+(?P<key>\w+)(?P<value>\s+[\w"]*)?')
45 self
.GLAPI
= re
.compile('^GL_?API(CALL)?\s+(?P<return>[\w\s*]+[\w*])\s+(GL)?_?APIENTRY\s+(?P<name>\w+)\s*\((?P<params>[\w\s(,*\[\])]+)\)\s*;')
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')
52 self
.verbose
= verbose
56 """Reset to initial state."""
57 self
.ifdef_levels
= []
58 self
.need_char
= False
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
!= " "]
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
]
80 pretty
+= sep
+ tmp
[i
]
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)
90 if ctype
.find("char") != -1:
92 elif ctype
.find("short") != -1:
94 elif ctype
.find("int") != -1:
97 if ctype
.find("float") != -1:
102 return (size
, is_float
, is_signed
)
104 def _parse_define(self
, line
):
105 """Parse a #define line for an <enum>."""
106 m
= self
.DEFINE
.search(line
)
108 if self
.verbose
and line
.find("#define") >= 0:
109 print "ignore %s" % (line
)
112 key
= m
.group("key").strip()
113 val
= m
.group("value").strip()
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")):
119 print "ignore enum %s" % (key
)
124 def _parse_typedef(self
, line
):
125 """Parse a typedef line for a <type>."""
126 m
= self
.TYPEDEF
.search(line
)
128 if self
.verbose
and line
.find("typedef") >= 0:
129 print "ignore %s" % (line
)
132 f
= m
.group("from").strip()
133 t
= m
.group("to").strip()
134 if not t
.startswith("GL"):
136 print "ignore type %s" % (t
)
138 attrs
= self
._get
_ctype
_attrs
(f
)
142 def _parse_gl_api(self
, line
):
143 """Parse a GLAPI line for a <function>."""
144 m
= self
.GLAPI
.search(line
)
146 if self
.verbose
and line
.find("APIENTRY") >= 0:
147 print "ignore %s" % (line
)
150 rettype
= m
.group("return")
151 rettype
= self
._format
_ctype
(rettype
)
152 if rettype
== "GLvoid":
155 name
= m
.group("name")
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"):
165 # split type and variable name
177 # convert array to pointer
183 ctype
= self
._format
_ctype
(ctype
)
186 if not self
.need_char
and ctype
.find("GLchar") >= 0:
187 self
.need_char
= True
189 params
.append((ctype
, var
))
191 return (rettype
, name
, params
)
193 def _change_level(self
, line
):
194 """Parse a #ifdef line and change level."""
195 m
= self
.IFDEF
.search(line
)
197 ifdef
= m
.group("ifdef")
199 ifdef
= m
.group("if")
200 self
.ifdef_levels
.append(ifdef
)
202 m
= self
.ENDIF
.search(line
)
204 self
.ifdef_levels
.pop()
208 def _read_header(self
, header
):
209 """Open a header file and read its contents."""
212 fp
= open(header
, "rb")
213 lines
= fp
.readlines()
216 print "failed to read %s: %s" % (header
, e
)
219 def _cmp_enum(self
, enum1
, enum2
):
220 """Compare two enums."""
221 # sort by length of the values as strings
224 ret
= len(val1
) - len(val2
)
230 # in case int cannot hold the result
237 if enum1
[0] < enum2
[0]:
239 elif enum1
[0] > enum2
[0]:
243 def _cmp_type(self
, type1
, type2
):
244 """Compare two types."""
248 ret
= attrs1
[0] - attrs2
[0]
251 ret
= attrs1
[1] - attrs2
[1]
254 ret
= attrs1
[2] - attrs2
[2]
259 def _cmp_function(self
, func1
, func2
):
260 """Compare two functions."""
271 def _postprocess_dict(self
, hdict
):
272 """Post-process a header dict and return an ordered list."""
275 for key
, cat
in hdict
.iteritems():
276 size
= len(cat
["enums"]) + len(cat
["types"]) + len(cat
["functions"])
277 # ignore empty category
281 cat
["enums"].sort(self
._cmp
_enum
)
284 for i
in xrange(1, len(cat
["enums"])):
285 if cat
["enums"][i
] == cat
["enums"][i
- 1]:
288 e
= cat
["enums"].pop(i
)
290 print "remove duplicate enum %s" % e
[0]
292 cat
["types"].sort(self
._cmp
_type
)
293 cat
["functions"].sort(self
._cmp
_function
)
295 # largest category comes first
297 hlist
.insert(0, (key
, cat
))
300 hlist
.append((key
, cat
))
303 def parse(self
, header
):
304 """Parse a header file."""
308 print "Parsing %s" % (header
)
311 lines
= self
._read
_header
(header
)
313 if self
._change
_level
(line
):
316 # skip until the first ifdef (i.e. __gl_h_)
317 if not self
.ifdef_levels
:
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]
327 cat
= hdict
[cat_name
]
334 hdict
[cat_name
] = cat
337 elem
= self
._parse
_define
(line
)
340 elem
= self
._parse
_typedef
(line
)
343 elem
= self
._parse
_gl
_api
(line
)
346 cat
[key
].append(elem
)
350 print "define GLchar"
351 elem
= self
._parse
_typedef
("typedef char GLchar;")
352 cat
["types"].append(elem
)
353 return self
._postprocess
_dict
(hdict
)
355 def spaces(n
, str=""):
356 spaces
= n
- len(str)
361 def output_xml(name
, hlist
):
362 """Output a parsed header in OpenGLAPI XML."""
364 for i
in xrange(len(hlist
)):
365 cat_name
, cat
= hlist
[i
]
367 print '<category name="%s">' % (cat_name
)
370 for enum
in cat
["enums"]:
373 tab
= spaces(41, name
)
374 attrs
= 'name="%s"%svalue="%s"' % (name
, tab
, value
)
375 print '%s<enum %s/>' % (spaces(indent
), attrs
)
377 if cat
["enums"] and cat
["types"]:
380 for type in cat
["types"]:
382 size
, is_float
, is_signed
= type[2]
384 attrs
= 'name="%s"' % (type[1][2:])
385 attrs
+= spaces(16, attrs
) + 'size="%d"' % (size
)
387 attrs
+= ' float="true"'
389 attrs
+= ' unsigned="true"'
391 print '%s<type %s/>' % (spaces(indent
), attrs
)
393 for func
in cat
["functions"]:
399 attrs
= 'name="%s" offset="assign"' % name
400 print '%s<function %s>' % (spaces(indent
), attrs
)
403 attrs
= 'name="%s" type="%s"' % (param
[1], param
[0])
404 print '%s<param %s/>' % (spaces(indent
* 2), attrs
)
406 attrs
= 'type="%s"' % ret
407 print '%s<return %s/>' % (spaces(indent
* 2), attrs
)
409 print '%s</function>' % spaces(indent
)
415 print "Usage: %s [-v] <header> ..." % sys
.argv
[0]
420 args
, headers
= getopt
.getopt(sys
.argv
[1:], "v")
431 need_xml_header
= True
432 parser
= HeaderParser(verbose
)
434 h
= os
.path
.abspath(h
)
435 hlist
= parser
.parse(h
)
438 print '<?xml version="1.0"?>'
439 print '<!DOCTYPE OpenGLAPI SYSTEM "%s/gl_API.dtd">' % GLAPI
440 need_xml_header
= False
443 print '<!-- %s -->' % (h
)
449 if __name__
== '__main__':