blob: 55bbddafa5b8f592e53dda22e6fbbdff9ceb8d6b [file] [log] [blame]
Ian Romanick73f59b02004-05-18 18:33:40 +00001#!/usr/bin/python2
2
3# (C) Copyright IBM Corporation 2004
4# All Rights Reserved.
5#
6# Permission is hereby granted, free of charge, to any person obtaining a
7# copy of this software and associated documentation files (the "Software"),
8# to deal in the Software without restriction, including without limitation
9# on the rights to use, copy, modify, merge, publish, distribute, sub
10# license, and/or sell copies of the Software, and to permit persons to whom
11# the Software is furnished to do so, subject to the following conditions:
12#
13# The above copyright notice and this permission notice (including the next
14# paragraph) shall be included in all copies or substantial portions of the
15# Software.
16#
17# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
20# IBM AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23# IN THE SOFTWARE.
24#
25# Authors:
26# Ian Romanick <idr@us.ibm.com>
27
28from xml.sax import saxutils
29from xml.sax import make_parser
30from xml.sax.handler import feature_namespaces
31
Brian Paul98fa2bf2004-10-28 21:11:02 +000032import re
Ian Romanick73f59b02004-05-18 18:33:40 +000033
34class glItem:
35 """Generic class on which all other API entity types are based."""
36
Ian Romanick73f59b02004-05-18 18:33:40 +000037 def __init__(self, tag_name, name, context):
38 self.name = name
39 self.category = context.get_category_define()
40 self.context = context
41 self.tag_name = tag_name
42
43 context.append(tag_name, self)
44 return
45
46 def startElement(self, name, attrs):
Ian Romanicka9d033c2004-05-19 23:33:08 +000047 """Generic startElement handler.
48
49 The startElement handler is called for all elements except
50 the one that starts the object. For a foo element, the
51 XML "<foo><bar/></foo>" would cause the startElement handler
52 to be called once, but the endElement handler would be called
53 twice."""
Ian Romanick73f59b02004-05-18 18:33:40 +000054 return
55
56 def endElement(self, name):
57 """Generic endElement handler.
58
59 Generic endElement handler. Returns 1 if the tag containing
60 the object is complete. Otherwise 0 is returned. All
61 derived class endElement handlers should call this method. If
62 the name of the ending tag is the same as the tag that
63 started this object, the object is assumed to be complete.
64
65 This fails if a tag can contain another tag with the same
66 name. The XML "<foo><foo/><bar/></foo>" would fail. The
Ian Romanicka9d033c2004-05-19 23:33:08 +000067 object would end before the bar tag was processed.
68
69 The endElement handler is called for every end element
70 associated with an object, even the element that started the
71 object. See the description of startElement an example."""
Ian Romanick73f59b02004-05-18 18:33:40 +000072
73 if name == self.tag_name:
74 return 1
75 else:
76 return 0
Ian Romanick73f59b02004-05-18 18:33:40 +000077
Ian Romanicka9d033c2004-05-19 23:33:08 +000078 def get_category_define(self):
79 return self.category
80
Ian Romanick73f59b02004-05-18 18:33:40 +000081
82class glEnum( glItem ):
Ian Romanicka9d033c2004-05-19 23:33:08 +000083 """Subclass of glItem for representing GL enumerants.
84
85 This class is not complete, and is not really used yet."""
86
Ian Romanick73f59b02004-05-18 18:33:40 +000087 def __init__(self, context, name, attrs):
88 self.value = int(attrs.get('value', "0x0000"), 0)
89 self.functions = {}
90
91 enum_name = "GL_" + attrs.get('name', None)
92 glItem.__init__(self, name, enum_name, context)
93
94 def startElement(self, name, attrs):
95 if name == "size":
96 name = attrs.get('name', None)
97 count = int(attrs.get('count', "0"), 0)
98 self.functions[name] = count
99
100 return
101
102
103class glType( glItem ):
Ian Romanicka9d033c2004-05-19 23:33:08 +0000104 """Subclass of glItem for representing GL types."""
105
Ian Romanick73f59b02004-05-18 18:33:40 +0000106 def __init__(self, context, name, attrs):
107 self.size = int(attrs.get('size', "0"))
108
109 type_name = "GL" + attrs.get('name', None)
110 glItem.__init__(self, name, type_name, context)
111
112
Ian Romanicka9d033c2004-05-19 23:33:08 +0000113class glParameter( glItem ):
114 """Parameter of a glFunction."""
Ian Romanick73f59b02004-05-18 18:33:40 +0000115 p_type = None
116 p_type_string = ""
Ian Romanick73f59b02004-05-18 18:33:40 +0000117 p_count = 0
118 p_count_parameters = None
119 counter = None
120 is_output = 0
121 is_counter = 0
122 is_pointer = 0
123
Ian Romanicka9d033c2004-05-19 23:33:08 +0000124 def __init__(self, context, name, attrs):
125 p_name = attrs.get('name', None)
126 self.p_type_string = attrs.get('type', None)
127 self.p_count_parameters = attrs.get('variable_param', None)
Ian Romanick73f59b02004-05-18 18:33:40 +0000128
Ian Romanicka9d033c2004-05-19 23:33:08 +0000129 self.p_type = context.context.find_type(self.p_type_string)
130 if self.p_type == None:
131 raise RuntimeError("Unknown type '%s' in function '%s'." % (self.p_type_string, context.name))
132
133
134 # The count tag can be either a numeric string or the name of
135 # a variable. If it is the name of a variable, the int(c)
136 # statement will throw an exception, and the except block will
137 # take over.
138
139 c = attrs.get('count', "0")
Ian Romanick73f59b02004-05-18 18:33:40 +0000140 try:
141 self.p_count = int(c)
Ian Romanicka9d033c2004-05-19 23:33:08 +0000142 self.counter = None
Ian Romanick73f59b02004-05-18 18:33:40 +0000143 except Exception,e:
144 self.p_count = 0
145 self.counter = c
146
Ian Romanicka9d033c2004-05-19 23:33:08 +0000147 if attrs.get('counter', "false") == "true":
148 self.is_counter = 1
149 else:
150 self.is_counter = 0
151
152 if attrs.get('output', "false") == "true":
Ian Romanick73f59b02004-05-18 18:33:40 +0000153 self.is_output = 1
154 else:
155 self.is_output = 0
156
Ian Romanicka9d033c2004-05-19 23:33:08 +0000157 if self.p_count > 0 or self.counter != None or self.p_count_parameters != None :
Ian Romanick73f59b02004-05-18 18:33:40 +0000158 has_count = 1
159 else:
160 has_count = 0
161
Ian Romanicka9d033c2004-05-19 23:33:08 +0000162
Ian Romanick73f59b02004-05-18 18:33:40 +0000163 # If there is a * anywhere in the parameter's type, then it
164 # is a pointer.
165
Ian Romanicka9d033c2004-05-19 23:33:08 +0000166 if re.compile("[*]").search(self.p_type_string):
Ian Romanick73f59b02004-05-18 18:33:40 +0000167 # We could do some other validation here. For
168 # example, an output parameter should not be const,
169 # but every non-output parameter should.
170
171 self.is_pointer = 1;
172 else:
173 # If a parameter is not a pointer, then there cannot
174 # be an associated count (either fixed size or
175 # variable) and the parameter cannot be an output.
176
177 if has_count or self.is_output:
178 raise RuntimeError("Non-pointer type has count or is output.")
179 self.is_pointer = 0;
180
Ian Romanicka9d033c2004-05-19 23:33:08 +0000181 glItem.__init__(self, name, p_name, context)
182 return
183
184
Ian Romanick73f59b02004-05-18 18:33:40 +0000185 def is_variable_length_array(self):
Ian Romanicka9d033c2004-05-19 23:33:08 +0000186 """Determine if a parameter is a variable length array.
187
188 A parameter is considered to be a variable length array if
189 its size depends on the value of another parameter that is
190 an enumerant. The params parameter to glTexEnviv is an
191 example of a variable length array parameter. Arrays whose
192 size depends on a count variable, such as the lists parameter
193 to glCallLists, are not variable length arrays in this
194 sense."""
195
Ian Romanick73f59b02004-05-18 18:33:40 +0000196 return self.p_count_parameters != None
197
Ian Romanicka9d033c2004-05-19 23:33:08 +0000198
Ian Romanick73f59b02004-05-18 18:33:40 +0000199 def is_array(self):
200 return self.is_pointer
201
Ian Romanicka9d033c2004-05-19 23:33:08 +0000202
Ian Romanick73f59b02004-05-18 18:33:40 +0000203 def count_string(self):
204 """Return a string representing the number of items
205
206 Returns a string representing the number of items in a
207 parameter. For scalar types this will always be "1". For
208 vector types, it will depend on whether or not it is a
209 fixed length vector (like the parameter of glVertex3fv),
210 a counted length (like the vector parameter of
211 glDeleteTextures), or a general variable length vector."""
212
213 if self.is_array():
214 if self.is_variable_length_array():
215 return "compsize"
216 elif self.counter != None:
217 return self.counter
218 else:
219 return str(self.p_count)
220 else:
221 return "1"
222
Ian Romanicka9d033c2004-05-19 23:33:08 +0000223
Ian Romanick73f59b02004-05-18 18:33:40 +0000224 def size(self):
225 if self.is_variable_length_array():
226 return 0
227 elif self.p_count == 0:
228 return self.p_type.size
229 else:
230 return self.p_type.size * self.p_count
231
Ian Romanicka9d033c2004-05-19 23:33:08 +0000232
Ian Romanick73f59b02004-05-18 18:33:40 +0000233class glParameterIterator:
Ian Romanicka9d033c2004-05-19 23:33:08 +0000234 """Class to iterate over a list of glParameters.
235
236 Objects of this class are returned by the __iter__ method of the
237 glFunction class. They are used to iterate over the list of
238 parameters to the function."""
239
Ian Romanick73f59b02004-05-18 18:33:40 +0000240 def __init__(self, data):
241 self.data = data
242 self.index = 0
243
244 def next(self):
245 if self.index == len( self.data ):
246 raise StopIteration
247 i = self.index
248 self.index += 1
249 return self.data[i]
250
Ian Romanicka9d033c2004-05-19 23:33:08 +0000251
Ian Romanick73f59b02004-05-18 18:33:40 +0000252class glFunction( glItem ):
253 real_name = ""
254 fn_alias = None
255 fn_offset = -1
256 fn_return_type = "void"
257 fn_parameters = []
258
259 def __init__(self, context, name, attrs):
260 self.fn_alias = attrs.get('alias', None)
261 self.fn_parameters = []
262
263 temp = attrs.get('offset', None)
264 if temp == None or temp == "?":
265 self.fn_offset = -1
266 else:
267 self.fn_offset = int(temp)
268
269 fn_name = attrs.get('name', None)
270 if self.fn_alias != None:
271 self.real_name = self.fn_alias
272 else:
273 self.real_name = fn_name
274
275 glItem.__init__(self, name, fn_name, context)
276 return
277
278
279 def __iter__(self):
280 return glParameterIterator(self.fn_parameters)
281
282
283 def startElement(self, name, attrs):
284 if name == "param":
Ian Romanick73f59b02004-05-18 18:33:40 +0000285 try:
Ian Romanicka9d033c2004-05-19 23:33:08 +0000286 glParameter(self, name, attrs)
Ian Romanick73f59b02004-05-18 18:33:40 +0000287 except RuntimeError:
288 print "Error with parameter '%s' in function '%s'." \
Ian Romanicka9d033c2004-05-19 23:33:08 +0000289 % (attrs.get('name','(unknown)'), self.name)
Ian Romanick73f59b02004-05-18 18:33:40 +0000290 raise
Ian Romanick73f59b02004-05-18 18:33:40 +0000291 elif name == "return":
292 self.set_return_type(attrs.get('type', None))
293
294
Ian Romanicka9d033c2004-05-19 23:33:08 +0000295 def append(self, tag_name, p):
296 if tag_name != "param":
297 raise RuntimeError("Trying to append '%s' to parameter list of function '%s'." % (tag_name, self.name))
298
Ian Romanick73f59b02004-05-18 18:33:40 +0000299 self.fn_parameters.append(p)
300
Ian Romanicka9d033c2004-05-19 23:33:08 +0000301
Ian Romanick73f59b02004-05-18 18:33:40 +0000302 def set_return_type(self, t):
303 self.fn_return_type = t
304
Ian Romanicka9d033c2004-05-19 23:33:08 +0000305
Ian Romanick73f59b02004-05-18 18:33:40 +0000306 def get_parameter_string(self):
307 arg_string = ""
308 comma = ""
309 for p in self:
Ian Romanicka9d033c2004-05-19 23:33:08 +0000310 arg_string = arg_string + comma + p.p_type_string + " " + p.name
Ian Romanick73f59b02004-05-18 18:33:40 +0000311 comma = ", "
312
313 if arg_string == "":
314 arg_string = "void"
315
316 return arg_string
317
318
319class glItemFactory:
320 """Factory to create objects derived from glItem."""
321
322 def create(self, context, name, attrs):
323 if name == "function":
324 return glFunction(context, name, attrs)
325 elif name == "type":
326 return glType(context, name, attrs)
327 elif name == "enum":
328 return glEnum(context, name, attrs)
329 else:
330 return None
331
332
333class FilterGLAPISpecBase(saxutils.XMLFilterBase):
334 name = "a"
335 license = "The license for this file is unspecified."
336 functions = {}
337 next_alias = -2
338 types = {}
339 xref = {}
340 current_object = None
341 factory = None
342 current_category = ""
343
344 def __init__(self):
345 saxutils.XMLFilterBase.__init__(self)
346 self.functions = {}
347 self.types = {}
348 self.xref = {}
349 self.factory = glItemFactory()
350
Ian Romanicka9d033c2004-05-19 23:33:08 +0000351
Ian Romanick73f59b02004-05-18 18:33:40 +0000352 def find_type(self,type_name):
353 for t in self.types:
354 if re.compile(t).search(type_name):
355 return self.types[t]
356 print "Unable to find base type matching \"%s\"." % (type_name)
357 return None
358
Ian Romanicka9d033c2004-05-19 23:33:08 +0000359
Ian Romanick73f59b02004-05-18 18:33:40 +0000360 def find_function(self,function_name):
361 index = self.xref[function_name]
362 return self.functions[index]
363
Ian Romanicka9d033c2004-05-19 23:33:08 +0000364
Ian Romanick73f59b02004-05-18 18:33:40 +0000365 def printFunctions(self):
366 keys = self.functions.keys()
367 keys.sort()
368 prevk = -1
369 for k in keys:
370 if k < 0: continue
371
372 if self.functions[k].fn_alias == None:
373 if k != prevk + 1:
374 #print 'Missing offset %d' % (prevk)
375 pass
376 prevk = int(k)
377 self.printFunction(self.functions[k])
378
379 keys.reverse()
380 for k in keys:
381 if self.functions[k].fn_alias != None:
382 self.printFunction(self.functions[k])
383
384 return
385
Ian Romanicka9d033c2004-05-19 23:33:08 +0000386
Ian Romanick73f59b02004-05-18 18:33:40 +0000387 def printHeader(self):
Ian Romanicka9d033c2004-05-19 23:33:08 +0000388 """Print the header associated with all files and call the printRealHeader method."""
389
Ian Romanick73f59b02004-05-18 18:33:40 +0000390 print '/* DO NOT EDIT - This file generated automatically by %s script */' \
391 % (self.name)
392 print ''
393 print '/*'
394 print ' * ' + self.license.replace('\n', '\n * ')
395 print ' */'
396 print ''
397 self.printRealHeader();
398 return
399
Ian Romanicka9d033c2004-05-19 23:33:08 +0000400
Ian Romanick73f59b02004-05-18 18:33:40 +0000401 def printFooter(self):
Ian Romanicka9d033c2004-05-19 23:33:08 +0000402 """Print the header associated with all files and call the printRealFooter method."""
403
Ian Romanick73f59b02004-05-18 18:33:40 +0000404 self.printFunctions()
405 self.printRealFooter()
406
407
408 def get_category_define(self):
Ian Romanicka9d033c2004-05-19 23:33:08 +0000409 """Convert the category name to the #define that would be found in glext.h"""
410
Ian Romanick73f59b02004-05-18 18:33:40 +0000411 if re.compile("[1-9][0-9]*[.][0-9]+").match(self.current_category):
412 s = self.current_category
413 return "GL_VERSION_" + s.replace(".", "_")
414 else:
415 return self.current_category
416
417
418 def append(self, object_type, obj):
419 if object_type == "function":
420 # If the function is not an alias and has a negative
421 # offset, then we do not need to track it. These are
422 # functions that don't have an assigned offset
423
424 if obj.fn_offset >= 0 or obj.fn_alias != None:
425 if obj.fn_offset >= 0:
426 index = obj.fn_offset
427 else:
428 index = self.next_alias
429 self.next_alias -= 1
430
431 self.functions[index] = obj
432 self.xref[obj.name] = index
433 elif object_type == "type":
434 self.types[obj.name] = obj
435
436 return
437
438
439 def startElement(self, name, attrs):
Ian Romanicka9d033c2004-05-19 23:33:08 +0000440 """Start a new element in the XML stream.
441
442 Starts a new element. There are three types of elements that
443 are specially handled by this function. When a "category"
444 element is encountered, the name of the category is saved.
445 If an element is encountered and no API object is
446 in-progress, a new object is created using the API factory.
447 Any future elements, until that API object is closed, are
448 passed to the current objects startElement method.
449
450 This paradigm was chosen becuase it allows subclasses of the
451 basic API types (i.e., glFunction, glEnum, etc.) to handle
452 additional XML data, GLX protocol information, that the base
453 classes do not know about."""
454
Ian Romanick73f59b02004-05-18 18:33:40 +0000455 if self.current_object != None:
456 self.current_object.startElement(name, attrs)
457 elif name == "category":
458 self.current_category = attrs.get('name', "")
459 else:
460 self.current_object = self.factory.create(self, name, attrs)
461 return
462
Ian Romanicka9d033c2004-05-19 23:33:08 +0000463
Ian Romanick73f59b02004-05-18 18:33:40 +0000464 def endElement(self, name):
465 if self.current_object != None:
466 if self.current_object.endElement(name):
467 self.current_object = None
468 return
469
Ian Romanicka9d033c2004-05-19 23:33:08 +0000470
Ian Romanick73f59b02004-05-18 18:33:40 +0000471 def printFunction(self,offset):
Ian Romanicka9d033c2004-05-19 23:33:08 +0000472 """Print a single function.
473
474 In the base class, this function is empty. All derived
475 classes should over-ride this function."""
Ian Romanick73f59b02004-05-18 18:33:40 +0000476 return
477
Ian Romanicka9d033c2004-05-19 23:33:08 +0000478
Ian Romanick73f59b02004-05-18 18:33:40 +0000479 def printRealHeader(self):
Ian Romanicka9d033c2004-05-19 23:33:08 +0000480 """Print the "real" header for the created file.
481
482 In the base class, this function is empty. All derived
483 classes should over-ride this function."""
Ian Romanick73f59b02004-05-18 18:33:40 +0000484 return
485
Ian Romanicka9d033c2004-05-19 23:33:08 +0000486
Ian Romanick73f59b02004-05-18 18:33:40 +0000487 def printRealFooter(self):
Ian Romanicka9d033c2004-05-19 23:33:08 +0000488 """Print the "real" footer for the created file.
489
490 In the base class, this function is empty. All derived
491 classes should over-ride this function."""
Ian Romanick73f59b02004-05-18 18:33:40 +0000492 return