blob: ff9bc8f4a4d41ec17a44ff0fda94b449b41caf2b [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
32import sys, re
33
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
77 return
78
Ian Romanicka9d033c2004-05-19 23:33:08 +000079 def get_category_define(self):
80 return self.category
81
Ian Romanick73f59b02004-05-18 18:33:40 +000082
83class glEnum( glItem ):
Ian Romanicka9d033c2004-05-19 23:33:08 +000084 """Subclass of glItem for representing GL enumerants.
85
86 This class is not complete, and is not really used yet."""
87
Ian Romanick73f59b02004-05-18 18:33:40 +000088 def __init__(self, context, name, attrs):
89 self.value = int(attrs.get('value', "0x0000"), 0)
90 self.functions = {}
91
92 enum_name = "GL_" + attrs.get('name', None)
93 glItem.__init__(self, name, enum_name, context)
94
95 def startElement(self, name, attrs):
96 if name == "size":
97 name = attrs.get('name', None)
98 count = int(attrs.get('count', "0"), 0)
99 self.functions[name] = count
100
101 return
102
103
104class glType( glItem ):
Ian Romanicka9d033c2004-05-19 23:33:08 +0000105 """Subclass of glItem for representing GL types."""
106
Ian Romanick73f59b02004-05-18 18:33:40 +0000107 def __init__(self, context, name, attrs):
108 self.size = int(attrs.get('size', "0"))
109
110 type_name = "GL" + attrs.get('name', None)
111 glItem.__init__(self, name, type_name, context)
112
113
Ian Romanicka9d033c2004-05-19 23:33:08 +0000114class glParameter( glItem ):
115 """Parameter of a glFunction."""
Ian Romanick73f59b02004-05-18 18:33:40 +0000116 p_type = None
117 p_type_string = ""
Ian Romanick73f59b02004-05-18 18:33:40 +0000118 p_count = 0
119 p_count_parameters = None
120 counter = None
121 is_output = 0
122 is_counter = 0
123 is_pointer = 0
124
Ian Romanicka9d033c2004-05-19 23:33:08 +0000125 def __init__(self, context, name, attrs):
126 p_name = attrs.get('name', None)
127 self.p_type_string = attrs.get('type', None)
128 self.p_count_parameters = attrs.get('variable_param', None)
Ian Romanick73f59b02004-05-18 18:33:40 +0000129
Ian Romanicka9d033c2004-05-19 23:33:08 +0000130 self.p_type = context.context.find_type(self.p_type_string)
131 if self.p_type == None:
132 raise RuntimeError("Unknown type '%s' in function '%s'." % (self.p_type_string, context.name))
133
134
135 # The count tag can be either a numeric string or the name of
136 # a variable. If it is the name of a variable, the int(c)
137 # statement will throw an exception, and the except block will
138 # take over.
139
140 c = attrs.get('count', "0")
Ian Romanick73f59b02004-05-18 18:33:40 +0000141 try:
142 self.p_count = int(c)
Ian Romanicka9d033c2004-05-19 23:33:08 +0000143 self.counter = None
Ian Romanick73f59b02004-05-18 18:33:40 +0000144 except Exception,e:
145 self.p_count = 0
146 self.counter = c
147
Ian Romanicka9d033c2004-05-19 23:33:08 +0000148 if attrs.get('counter', "false") == "true":
149 self.is_counter = 1
150 else:
151 self.is_counter = 0
152
153 if attrs.get('output', "false") == "true":
Ian Romanick73f59b02004-05-18 18:33:40 +0000154 self.is_output = 1
155 else:
156 self.is_output = 0
157
Ian Romanicka9d033c2004-05-19 23:33:08 +0000158 if self.p_count > 0 or self.counter != None or self.p_count_parameters != None :
Ian Romanick73f59b02004-05-18 18:33:40 +0000159 has_count = 1
160 else:
161 has_count = 0
162
Ian Romanicka9d033c2004-05-19 23:33:08 +0000163
Ian Romanick73f59b02004-05-18 18:33:40 +0000164 # If there is a * anywhere in the parameter's type, then it
165 # is a pointer.
166
Ian Romanicka9d033c2004-05-19 23:33:08 +0000167 if re.compile("[*]").search(self.p_type_string):
Ian Romanick73f59b02004-05-18 18:33:40 +0000168 # We could do some other validation here. For
169 # example, an output parameter should not be const,
170 # but every non-output parameter should.
171
172 self.is_pointer = 1;
173 else:
174 # If a parameter is not a pointer, then there cannot
175 # be an associated count (either fixed size or
176 # variable) and the parameter cannot be an output.
177
178 if has_count or self.is_output:
179 raise RuntimeError("Non-pointer type has count or is output.")
180 self.is_pointer = 0;
181
Ian Romanicka9d033c2004-05-19 23:33:08 +0000182 glItem.__init__(self, name, p_name, context)
183 return
184
185
Ian Romanick73f59b02004-05-18 18:33:40 +0000186 def is_variable_length_array(self):
Ian Romanicka9d033c2004-05-19 23:33:08 +0000187 """Determine if a parameter is a variable length array.
188
189 A parameter is considered to be a variable length array if
190 its size depends on the value of another parameter that is
191 an enumerant. The params parameter to glTexEnviv is an
192 example of a variable length array parameter. Arrays whose
193 size depends on a count variable, such as the lists parameter
194 to glCallLists, are not variable length arrays in this
195 sense."""
196
Ian Romanick73f59b02004-05-18 18:33:40 +0000197 return self.p_count_parameters != None
198
Ian Romanicka9d033c2004-05-19 23:33:08 +0000199
Ian Romanick73f59b02004-05-18 18:33:40 +0000200 def is_array(self):
201 return self.is_pointer
202
Ian Romanicka9d033c2004-05-19 23:33:08 +0000203
Ian Romanick73f59b02004-05-18 18:33:40 +0000204 def count_string(self):
205 """Return a string representing the number of items
206
207 Returns a string representing the number of items in a
208 parameter. For scalar types this will always be "1". For
209 vector types, it will depend on whether or not it is a
210 fixed length vector (like the parameter of glVertex3fv),
211 a counted length (like the vector parameter of
212 glDeleteTextures), or a general variable length vector."""
213
214 if self.is_array():
215 if self.is_variable_length_array():
216 return "compsize"
217 elif self.counter != None:
218 return self.counter
219 else:
220 return str(self.p_count)
221 else:
222 return "1"
223
Ian Romanicka9d033c2004-05-19 23:33:08 +0000224
Ian Romanick73f59b02004-05-18 18:33:40 +0000225 def size(self):
226 if self.is_variable_length_array():
227 return 0
228 elif self.p_count == 0:
229 return self.p_type.size
230 else:
231 return self.p_type.size * self.p_count
232
Ian Romanicka9d033c2004-05-19 23:33:08 +0000233
Ian Romanick73f59b02004-05-18 18:33:40 +0000234class glParameterIterator:
Ian Romanicka9d033c2004-05-19 23:33:08 +0000235 """Class to iterate over a list of glParameters.
236
237 Objects of this class are returned by the __iter__ method of the
238 glFunction class. They are used to iterate over the list of
239 parameters to the function."""
240
Ian Romanick73f59b02004-05-18 18:33:40 +0000241 def __init__(self, data):
242 self.data = data
243 self.index = 0
244
245 def next(self):
246 if self.index == len( self.data ):
247 raise StopIteration
248 i = self.index
249 self.index += 1
250 return self.data[i]
251
Ian Romanicka9d033c2004-05-19 23:33:08 +0000252
Ian Romanick73f59b02004-05-18 18:33:40 +0000253class glFunction( glItem ):
254 real_name = ""
255 fn_alias = None
256 fn_offset = -1
257 fn_return_type = "void"
258 fn_parameters = []
259
260 def __init__(self, context, name, attrs):
261 self.fn_alias = attrs.get('alias', None)
262 self.fn_parameters = []
263
264 temp = attrs.get('offset', None)
265 if temp == None or temp == "?":
266 self.fn_offset = -1
267 else:
268 self.fn_offset = int(temp)
269
270 fn_name = attrs.get('name', None)
271 if self.fn_alias != None:
272 self.real_name = self.fn_alias
273 else:
274 self.real_name = fn_name
275
276 glItem.__init__(self, name, fn_name, context)
277 return
278
279
280 def __iter__(self):
281 return glParameterIterator(self.fn_parameters)
282
283
284 def startElement(self, name, attrs):
285 if name == "param":
Ian Romanick73f59b02004-05-18 18:33:40 +0000286 try:
Ian Romanicka9d033c2004-05-19 23:33:08 +0000287 glParameter(self, name, attrs)
Ian Romanick73f59b02004-05-18 18:33:40 +0000288 except RuntimeError:
289 print "Error with parameter '%s' in function '%s'." \
Ian Romanicka9d033c2004-05-19 23:33:08 +0000290 % (attrs.get('name','(unknown)'), self.name)
Ian Romanick73f59b02004-05-18 18:33:40 +0000291 raise
Ian Romanick73f59b02004-05-18 18:33:40 +0000292 elif name == "return":
293 self.set_return_type(attrs.get('type', None))
294
295
Ian Romanicka9d033c2004-05-19 23:33:08 +0000296 def append(self, tag_name, p):
297 if tag_name != "param":
298 raise RuntimeError("Trying to append '%s' to parameter list of function '%s'." % (tag_name, self.name))
299
Ian Romanick73f59b02004-05-18 18:33:40 +0000300 self.fn_parameters.append(p)
301
Ian Romanicka9d033c2004-05-19 23:33:08 +0000302
Ian Romanick73f59b02004-05-18 18:33:40 +0000303 def set_return_type(self, t):
304 self.fn_return_type = t
305
Ian Romanicka9d033c2004-05-19 23:33:08 +0000306
Ian Romanick73f59b02004-05-18 18:33:40 +0000307 def get_parameter_string(self):
308 arg_string = ""
309 comma = ""
310 for p in self:
Ian Romanicka9d033c2004-05-19 23:33:08 +0000311 arg_string = arg_string + comma + p.p_type_string + " " + p.name
Ian Romanick73f59b02004-05-18 18:33:40 +0000312 comma = ", "
313
314 if arg_string == "":
315 arg_string = "void"
316
317 return arg_string
318
319
320class glItemFactory:
321 """Factory to create objects derived from glItem."""
322
323 def create(self, context, name, attrs):
324 if name == "function":
325 return glFunction(context, name, attrs)
326 elif name == "type":
327 return glType(context, name, attrs)
328 elif name == "enum":
329 return glEnum(context, name, attrs)
330 else:
331 return None
332
333
334class FilterGLAPISpecBase(saxutils.XMLFilterBase):
335 name = "a"
336 license = "The license for this file is unspecified."
337 functions = {}
338 next_alias = -2
339 types = {}
340 xref = {}
341 current_object = None
342 factory = None
343 current_category = ""
344
345 def __init__(self):
346 saxutils.XMLFilterBase.__init__(self)
347 self.functions = {}
348 self.types = {}
349 self.xref = {}
350 self.factory = glItemFactory()
351
Ian Romanicka9d033c2004-05-19 23:33:08 +0000352
Ian Romanick73f59b02004-05-18 18:33:40 +0000353 def find_type(self,type_name):
354 for t in self.types:
355 if re.compile(t).search(type_name):
356 return self.types[t]
357 print "Unable to find base type matching \"%s\"." % (type_name)
358 return None
359
Ian Romanicka9d033c2004-05-19 23:33:08 +0000360
Ian Romanick73f59b02004-05-18 18:33:40 +0000361 def find_function(self,function_name):
362 index = self.xref[function_name]
363 return self.functions[index]
364
Ian Romanicka9d033c2004-05-19 23:33:08 +0000365
Ian Romanick73f59b02004-05-18 18:33:40 +0000366 def printFunctions(self):
367 keys = self.functions.keys()
368 keys.sort()
369 prevk = -1
370 for k in keys:
371 if k < 0: continue
372
373 if self.functions[k].fn_alias == None:
374 if k != prevk + 1:
375 #print 'Missing offset %d' % (prevk)
376 pass
377 prevk = int(k)
378 self.printFunction(self.functions[k])
379
380 keys.reverse()
381 for k in keys:
382 if self.functions[k].fn_alias != None:
383 self.printFunction(self.functions[k])
384
385 return
386
Ian Romanicka9d033c2004-05-19 23:33:08 +0000387
Ian Romanick73f59b02004-05-18 18:33:40 +0000388 def printHeader(self):
Ian Romanicka9d033c2004-05-19 23:33:08 +0000389 """Print the header associated with all files and call the printRealHeader method."""
390
Ian Romanick73f59b02004-05-18 18:33:40 +0000391 print '/* DO NOT EDIT - This file generated automatically by %s script */' \
392 % (self.name)
393 print ''
394 print '/*'
395 print ' * ' + self.license.replace('\n', '\n * ')
396 print ' */'
397 print ''
398 self.printRealHeader();
399 return
400
Ian Romanicka9d033c2004-05-19 23:33:08 +0000401
Ian Romanick73f59b02004-05-18 18:33:40 +0000402 def printFooter(self):
Ian Romanicka9d033c2004-05-19 23:33:08 +0000403 """Print the header associated with all files and call the printRealFooter method."""
404
Ian Romanick73f59b02004-05-18 18:33:40 +0000405 self.printFunctions()
406 self.printRealFooter()
407
408
409 def get_category_define(self):
Ian Romanicka9d033c2004-05-19 23:33:08 +0000410 """Convert the category name to the #define that would be found in glext.h"""
411
Ian Romanick73f59b02004-05-18 18:33:40 +0000412 if re.compile("[1-9][0-9]*[.][0-9]+").match(self.current_category):
413 s = self.current_category
414 return "GL_VERSION_" + s.replace(".", "_")
415 else:
416 return self.current_category
417
418
419 def append(self, object_type, obj):
420 if object_type == "function":
421 # If the function is not an alias and has a negative
422 # offset, then we do not need to track it. These are
423 # functions that don't have an assigned offset
424
425 if obj.fn_offset >= 0 or obj.fn_alias != None:
426 if obj.fn_offset >= 0:
427 index = obj.fn_offset
428 else:
429 index = self.next_alias
430 self.next_alias -= 1
431
432 self.functions[index] = obj
433 self.xref[obj.name] = index
434 elif object_type == "type":
435 self.types[obj.name] = obj
436
437 return
438
439
440 def startElement(self, name, attrs):
Ian Romanicka9d033c2004-05-19 23:33:08 +0000441 """Start a new element in the XML stream.
442
443 Starts a new element. There are three types of elements that
444 are specially handled by this function. When a "category"
445 element is encountered, the name of the category is saved.
446 If an element is encountered and no API object is
447 in-progress, a new object is created using the API factory.
448 Any future elements, until that API object is closed, are
449 passed to the current objects startElement method.
450
451 This paradigm was chosen becuase it allows subclasses of the
452 basic API types (i.e., glFunction, glEnum, etc.) to handle
453 additional XML data, GLX protocol information, that the base
454 classes do not know about."""
455
Ian Romanick73f59b02004-05-18 18:33:40 +0000456 if self.current_object != None:
457 self.current_object.startElement(name, attrs)
458 elif name == "category":
459 self.current_category = attrs.get('name', "")
460 else:
461 self.current_object = self.factory.create(self, name, attrs)
462 return
463
Ian Romanicka9d033c2004-05-19 23:33:08 +0000464
Ian Romanick73f59b02004-05-18 18:33:40 +0000465 def endElement(self, name):
466 if self.current_object != None:
467 if self.current_object.endElement(name):
468 self.current_object = None
469 return
470
Ian Romanicka9d033c2004-05-19 23:33:08 +0000471
Ian Romanick73f59b02004-05-18 18:33:40 +0000472 def printFunction(self,offset):
Ian Romanicka9d033c2004-05-19 23:33:08 +0000473 """Print a single function.
474
475 In the base class, this function is empty. All derived
476 classes should over-ride this function."""
Ian Romanick73f59b02004-05-18 18:33:40 +0000477 return
478
Ian Romanicka9d033c2004-05-19 23:33:08 +0000479
Ian Romanick73f59b02004-05-18 18:33:40 +0000480 def printRealHeader(self):
Ian Romanicka9d033c2004-05-19 23:33:08 +0000481 """Print the "real" header for the created file.
482
483 In the base class, this function is empty. All derived
484 classes should over-ride this function."""
Ian Romanick73f59b02004-05-18 18:33:40 +0000485 return
486
Ian Romanicka9d033c2004-05-19 23:33:08 +0000487
Ian Romanick73f59b02004-05-18 18:33:40 +0000488 def printRealFooter(self):
Ian Romanicka9d033c2004-05-19 23:33:08 +0000489 """Print the "real" footer for the created file.
490
491 In the base class, this function is empty. All derived
492 classes should over-ride this function."""
Ian Romanick73f59b02004-05-18 18:33:40 +0000493 return