3 # (C) Copyright IBM Corporation 2004
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:
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
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
26 # Ian Romanick <idr@us.ibm.com>
28 from xml.sax import saxutils
29 from xml.sax import make_parser
30 from xml.sax.handler import feature_namespaces
35 """Generic class on which all other API entity types are based."""
37 def __init__(self, tag_name, name, context):
39 self.category = context.get_category_define()
40 self.context = context
41 self.tag_name = tag_name
43 context.append(tag_name, self)
46 def startElement(self, name, attrs):
47 """Generic startElement handler.
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
56 def endElement(self, name):
57 """Generic endElement handler.
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.
65 This fails if a tag can contain another tag with the same
66 name. The XML "<foo><foo/><bar/></foo>" would fail. The
67 object would end before the bar tag was processed.
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."""
73 if name == self.tag_name:
78 def get_category_define(self):
82 class glEnum( glItem ):
83 """Subclass of glItem for representing GL enumerants.
85 This class is not complete, and is not really used yet."""
87 def __init__(self, context, name, attrs):
88 self.value = int(attrs.get('value', "0x0000"), 0)
91 enum_name = "GL_" + attrs.get('name', None)
92 glItem.__init__(self, name, enum_name, context)
94 def startElement(self, name, attrs):
96 name = attrs.get('name', None)
97 count = int(attrs.get('count', "0"), 0)
98 self.functions[name] = count
103 class glType( glItem ):
104 """Subclass of glItem for representing GL types."""
106 def __init__(self, context, name, attrs):
107 self.size = int(attrs.get('size', "0"))
109 type_name = "GL" + attrs.get('name', None)
110 glItem.__init__(self, name, type_name, context)
113 class glParameter( glItem ):
114 """Parameter of a glFunction."""
118 p_count_parameters = None
124 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)
129 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))
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
139 c = attrs.get('count', "0")
141 self.p_count = int(c)
147 if attrs.get('counter', "false") == "true":
152 if attrs.get('output', "false") == "true":
157 if self.p_count > 0 or self.counter != None or self.p_count_parameters != None :
163 # If there is a * anywhere in the parameter's type, then it
166 if re.compile("[*]").search(self.p_type_string):
167 # We could do some other validation here. For
168 # example, an output parameter should not be const,
169 # but every non-output parameter should.
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.
177 if has_count or self.is_output:
178 raise RuntimeError("Non-pointer type has count or is output.")
181 glItem.__init__(self, name, p_name, context)
185 def is_variable_length_array(self):
186 """Determine if a parameter is a variable length array.
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
196 return self.p_count_parameters != None
200 return self.is_pointer
203 def count_string(self):
204 """Return a string representing the number of items
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."""
214 if self.is_variable_length_array():
216 elif self.counter != None:
219 return str(self.p_count)
225 if self.is_variable_length_array():
227 elif self.p_count == 0:
228 return self.p_type.size
230 return self.p_type.size * self.p_count
233 class glParameterIterator:
234 """Class to iterate over a list of glParameters.
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."""
240 def __init__(self, data):
245 if self.index == len( self.data ):
252 class glFunction( glItem ):
256 fn_return_type = "void"
259 def __init__(self, context, name, attrs):
260 self.fn_alias = attrs.get('alias', None)
261 self.fn_parameters = []
263 temp = attrs.get('offset', None)
264 if temp == None or temp == "?":
267 self.fn_offset = int(temp)
269 fn_name = attrs.get('name', None)
270 if self.fn_alias != None:
271 self.real_name = self.fn_alias
273 self.real_name = fn_name
275 glItem.__init__(self, name, fn_name, context)
280 return glParameterIterator(self.fn_parameters)
283 def startElement(self, name, attrs):
286 glParameter(self, name, attrs)
288 print "Error with parameter '%s' in function '%s'." \
289 % (attrs.get('name','(unknown)'), self.name)
291 elif name == "return":
292 self.set_return_type(attrs.get('type', None))
295 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))
299 self.fn_parameters.append(p)
302 def set_return_type(self, t):
303 self.fn_return_type = t
306 def get_parameter_string(self):
310 arg_string = arg_string + comma + p.p_type_string + " " + p.name
320 """Factory to create objects derived from glItem."""
322 def create(self, context, name, attrs):
323 if name == "function":
324 return glFunction(context, name, attrs)
326 return glType(context, name, attrs)
328 return glEnum(context, name, attrs)
333 class FilterGLAPISpecBase(saxutils.XMLFilterBase):
335 license = "The license for this file is unspecified."
340 current_object = None
342 current_category = ""
345 saxutils.XMLFilterBase.__init__(self)
349 self.factory = glItemFactory()
352 def find_type(self,type_name):
354 if re.compile(t).search(type_name):
356 print "Unable to find base type matching \"%s\"." % (type_name)
360 def find_function(self,function_name):
361 index = self.xref[function_name]
362 return self.functions[index]
365 def printFunctions(self):
366 keys = self.functions.keys()
372 if self.functions[k].fn_alias == None:
374 #print 'Missing offset %d' % (prevk)
377 self.printFunction(self.functions[k])
381 if self.functions[k].fn_alias != None:
382 self.printFunction(self.functions[k])
387 def printHeader(self):
388 """Print the header associated with all files and call the printRealHeader method."""
390 print '/* DO NOT EDIT - This file generated automatically by %s script */' \
394 print ' * ' + self.license.replace('\n', '\n * ')
397 self.printRealHeader();
401 def printFooter(self):
402 """Print the header associated with all files and call the printRealFooter method."""
404 self.printFunctions()
405 self.printRealFooter()
408 def get_category_define(self):
409 """Convert the category name to the #define that would be found in glext.h"""
411 if re.compile("[1-9][0-9]*[.][0-9]+").match(self.current_category):
412 s = self.current_category
413 return "GL_VERSION_" + s.replace(".", "_")
415 return self.current_category
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
424 if obj.fn_offset >= 0 or obj.fn_alias != None:
425 if obj.fn_offset >= 0:
426 index = obj.fn_offset
428 index = self.next_alias
431 self.functions[index] = obj
432 self.xref[obj.name] = index
433 elif object_type == "type":
434 self.types[obj.name] = obj
439 def startElement(self, name, attrs):
440 """Start a new element in the XML stream.
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.
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."""
455 if self.current_object != None:
456 self.current_object.startElement(name, attrs)
457 elif name == "category":
458 self.current_category = attrs.get('name', "")
460 self.current_object = self.factory.create(self, name, attrs)
464 def endElement(self, name):
465 if self.current_object != None:
466 if self.current_object.endElement(name):
467 self.current_object = None
471 def printFunction(self,offset):
472 """Print a single function.
474 In the base class, this function is empty. All derived
475 classes should over-ride this function."""
479 def printRealHeader(self):
480 """Print the "real" header for the created file.
482 In the base class, this function is empty. All derived
483 classes should over-ride this function."""
487 def printRealFooter(self):
488 """Print the "real" footer for the created file.
490 In the base class, this function is empty. All derived
491 classes should over-ride this function."""