blob: 79da5abab9b61d08394c209217e12e5864a817e4 [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 Romanick4f0a75e2004-12-01 00:29:48 +0000196 return (self.p_count_parameters != None) or (self.counter != None)
Ian Romanick73f59b02004-05-18 18:33:40 +0000197
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():
Ian Romanick4f0a75e2004-12-01 00:29:48 +0000214 if self.p_count_parameters != None:
Ian Romanick73f59b02004-05-18 18:33:40 +0000215 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):
Ian Romanick4f0a75e2004-12-01 00:29:48 +0000225 if self.p_count_parameters != None or self.counter != None or self.is_output:
Ian Romanick73f59b02004-05-18 18:33:40 +0000226 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 Romanick4f0a75e2004-12-01 00:29:48 +0000232 def size_string(self):
233 s = self.size()
234 if s == 0:
235 a_prod = "compsize"
236 b_prod = self.p_type.size
237
238 if self.p_count_parameters == None and self.counter != None:
239 a_prod = self.counter
240 elif self.p_count_parameters != None and self.counter == None:
241 pass
242 elif self.p_count_parameters != None and self.counter != None:
243 b_prod = self.counter
244 else:
245 raise RuntimeError("Parameter '%s' to function '%s' has size 0." % (self.name, self.context.name))
246
247 return "(%s * %s)" % (a_prod, b_prod)
248 else:
249 return str(s)
250
Ian Romanicka9d033c2004-05-19 23:33:08 +0000251
Ian Romanick73f59b02004-05-18 18:33:40 +0000252class glParameterIterator:
Ian Romanicka9d033c2004-05-19 23:33:08 +0000253 """Class to iterate over a list of glParameters.
254
255 Objects of this class are returned by the __iter__ method of the
256 glFunction class. They are used to iterate over the list of
257 parameters to the function."""
258
Ian Romanick73f59b02004-05-18 18:33:40 +0000259 def __init__(self, data):
260 self.data = data
261 self.index = 0
262
263 def next(self):
264 if self.index == len( self.data ):
265 raise StopIteration
266 i = self.index
267 self.index += 1
268 return self.data[i]
269
Ian Romanicka9d033c2004-05-19 23:33:08 +0000270
Ian Romanick73f59b02004-05-18 18:33:40 +0000271class glFunction( glItem ):
272 real_name = ""
273 fn_alias = None
274 fn_offset = -1
275 fn_return_type = "void"
276 fn_parameters = []
277
278 def __init__(self, context, name, attrs):
279 self.fn_alias = attrs.get('alias', None)
280 self.fn_parameters = []
281
282 temp = attrs.get('offset', None)
283 if temp == None or temp == "?":
284 self.fn_offset = -1
285 else:
286 self.fn_offset = int(temp)
287
288 fn_name = attrs.get('name', None)
289 if self.fn_alias != None:
290 self.real_name = self.fn_alias
291 else:
292 self.real_name = fn_name
293
294 glItem.__init__(self, name, fn_name, context)
295 return
296
297
298 def __iter__(self):
299 return glParameterIterator(self.fn_parameters)
300
301
302 def startElement(self, name, attrs):
303 if name == "param":
Ian Romanick73f59b02004-05-18 18:33:40 +0000304 try:
Ian Romanick4f0a75e2004-12-01 00:29:48 +0000305 self.context.factory.create(self, name, attrs)
Ian Romanick73f59b02004-05-18 18:33:40 +0000306 except RuntimeError:
307 print "Error with parameter '%s' in function '%s'." \
Ian Romanicka9d033c2004-05-19 23:33:08 +0000308 % (attrs.get('name','(unknown)'), self.name)
Ian Romanick73f59b02004-05-18 18:33:40 +0000309 raise
Ian Romanick73f59b02004-05-18 18:33:40 +0000310 elif name == "return":
311 self.set_return_type(attrs.get('type', None))
312
313
Ian Romanicka9d033c2004-05-19 23:33:08 +0000314 def append(self, tag_name, p):
315 if tag_name != "param":
316 raise RuntimeError("Trying to append '%s' to parameter list of function '%s'." % (tag_name, self.name))
317
Ian Romanick73f59b02004-05-18 18:33:40 +0000318 self.fn_parameters.append(p)
319
Ian Romanicka9d033c2004-05-19 23:33:08 +0000320
Ian Romanick73f59b02004-05-18 18:33:40 +0000321 def set_return_type(self, t):
322 self.fn_return_type = t
323
Ian Romanicka9d033c2004-05-19 23:33:08 +0000324
Ian Romanick73f59b02004-05-18 18:33:40 +0000325 def get_parameter_string(self):
326 arg_string = ""
327 comma = ""
328 for p in self:
Ian Romanicka9d033c2004-05-19 23:33:08 +0000329 arg_string = arg_string + comma + p.p_type_string + " " + p.name
Ian Romanick73f59b02004-05-18 18:33:40 +0000330 comma = ", "
331
332 if arg_string == "":
333 arg_string = "void"
334
335 return arg_string
336
337
338class glItemFactory:
339 """Factory to create objects derived from glItem."""
340
341 def create(self, context, name, attrs):
342 if name == "function":
343 return glFunction(context, name, attrs)
344 elif name == "type":
345 return glType(context, name, attrs)
346 elif name == "enum":
347 return glEnum(context, name, attrs)
Ian Romanick4f0a75e2004-12-01 00:29:48 +0000348 elif name == "param":
349 return glParameter(context, name, attrs)
Ian Romanick73f59b02004-05-18 18:33:40 +0000350 else:
351 return None
352
353
354class FilterGLAPISpecBase(saxutils.XMLFilterBase):
355 name = "a"
356 license = "The license for this file is unspecified."
357 functions = {}
358 next_alias = -2
359 types = {}
360 xref = {}
361 current_object = None
362 factory = None
363 current_category = ""
364
365 def __init__(self):
366 saxutils.XMLFilterBase.__init__(self)
367 self.functions = {}
368 self.types = {}
369 self.xref = {}
370 self.factory = glItemFactory()
371
Ian Romanicka9d033c2004-05-19 23:33:08 +0000372
Ian Romanick73f59b02004-05-18 18:33:40 +0000373 def find_type(self,type_name):
374 for t in self.types:
375 if re.compile(t).search(type_name):
376 return self.types[t]
377 print "Unable to find base type matching \"%s\"." % (type_name)
378 return None
379
Ian Romanicka9d033c2004-05-19 23:33:08 +0000380
Ian Romanick73f59b02004-05-18 18:33:40 +0000381 def find_function(self,function_name):
382 index = self.xref[function_name]
383 return self.functions[index]
384
Ian Romanicka9d033c2004-05-19 23:33:08 +0000385
Ian Romanick73f59b02004-05-18 18:33:40 +0000386 def printFunctions(self):
387 keys = self.functions.keys()
388 keys.sort()
389 prevk = -1
390 for k in keys:
391 if k < 0: continue
392
393 if self.functions[k].fn_alias == None:
394 if k != prevk + 1:
395 #print 'Missing offset %d' % (prevk)
396 pass
397 prevk = int(k)
398 self.printFunction(self.functions[k])
399
400 keys.reverse()
401 for k in keys:
402 if self.functions[k].fn_alias != None:
403 self.printFunction(self.functions[k])
404
405 return
406
Ian Romanicka9d033c2004-05-19 23:33:08 +0000407
Ian Romanick73f59b02004-05-18 18:33:40 +0000408 def printHeader(self):
Ian Romanicka9d033c2004-05-19 23:33:08 +0000409 """Print the header associated with all files and call the printRealHeader method."""
410
Ian Romanick73f59b02004-05-18 18:33:40 +0000411 print '/* DO NOT EDIT - This file generated automatically by %s script */' \
412 % (self.name)
413 print ''
414 print '/*'
415 print ' * ' + self.license.replace('\n', '\n * ')
416 print ' */'
417 print ''
418 self.printRealHeader();
419 return
420
Ian Romanicka9d033c2004-05-19 23:33:08 +0000421
Ian Romanick73f59b02004-05-18 18:33:40 +0000422 def printFooter(self):
Ian Romanicka9d033c2004-05-19 23:33:08 +0000423 """Print the header associated with all files and call the printRealFooter method."""
424
Ian Romanick73f59b02004-05-18 18:33:40 +0000425 self.printFunctions()
426 self.printRealFooter()
427
428
429 def get_category_define(self):
Ian Romanicka9d033c2004-05-19 23:33:08 +0000430 """Convert the category name to the #define that would be found in glext.h"""
431
Ian Romanick73f59b02004-05-18 18:33:40 +0000432 if re.compile("[1-9][0-9]*[.][0-9]+").match(self.current_category):
433 s = self.current_category
434 return "GL_VERSION_" + s.replace(".", "_")
435 else:
436 return self.current_category
437
438
439 def append(self, object_type, obj):
440 if object_type == "function":
441 # If the function is not an alias and has a negative
442 # offset, then we do not need to track it. These are
443 # functions that don't have an assigned offset
444
445 if obj.fn_offset >= 0 or obj.fn_alias != None:
446 if obj.fn_offset >= 0:
447 index = obj.fn_offset
448 else:
449 index = self.next_alias
450 self.next_alias -= 1
451
452 self.functions[index] = obj
453 self.xref[obj.name] = index
454 elif object_type == "type":
455 self.types[obj.name] = obj
456
457 return
458
459
460 def startElement(self, name, attrs):
Ian Romanicka9d033c2004-05-19 23:33:08 +0000461 """Start a new element in the XML stream.
462
463 Starts a new element. There are three types of elements that
464 are specially handled by this function. When a "category"
465 element is encountered, the name of the category is saved.
466 If an element is encountered and no API object is
467 in-progress, a new object is created using the API factory.
468 Any future elements, until that API object is closed, are
469 passed to the current objects startElement method.
470
471 This paradigm was chosen becuase it allows subclasses of the
472 basic API types (i.e., glFunction, glEnum, etc.) to handle
473 additional XML data, GLX protocol information, that the base
474 classes do not know about."""
475
Ian Romanick73f59b02004-05-18 18:33:40 +0000476 if self.current_object != None:
477 self.current_object.startElement(name, attrs)
478 elif name == "category":
479 self.current_category = attrs.get('name', "")
480 else:
481 self.current_object = self.factory.create(self, name, attrs)
482 return
483
Ian Romanicka9d033c2004-05-19 23:33:08 +0000484
Ian Romanick73f59b02004-05-18 18:33:40 +0000485 def endElement(self, name):
486 if self.current_object != None:
487 if self.current_object.endElement(name):
488 self.current_object = None
489 return
490
Ian Romanicka9d033c2004-05-19 23:33:08 +0000491
Ian Romanick73f59b02004-05-18 18:33:40 +0000492 def printFunction(self,offset):
Ian Romanicka9d033c2004-05-19 23:33:08 +0000493 """Print a single function.
494
495 In the base class, this function is empty. All derived
496 classes should over-ride this function."""
Ian Romanick73f59b02004-05-18 18:33:40 +0000497 return
498
Ian Romanicka9d033c2004-05-19 23:33:08 +0000499
Ian Romanick73f59b02004-05-18 18:33:40 +0000500 def printRealHeader(self):
Ian Romanicka9d033c2004-05-19 23:33:08 +0000501 """Print the "real" header for the created file.
502
503 In the base class, this function is empty. All derived
504 classes should over-ride this function."""
Ian Romanick73f59b02004-05-18 18:33:40 +0000505 return
506
Ian Romanicka9d033c2004-05-19 23:33:08 +0000507
Ian Romanick73f59b02004-05-18 18:33:40 +0000508 def printRealFooter(self):
Ian Romanicka9d033c2004-05-19 23:33:08 +0000509 """Print the "real" footer for the created file.
510
511 In the base class, this function is empty. All derived
512 classes should over-ride this function."""
Ian Romanick73f59b02004-05-18 18:33:40 +0000513 return