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
:
79 def get_category_define(self
):
83 class glEnum( glItem
):
84 """Subclass of glItem for representing GL enumerants.
86 This class is not complete, and is not really used yet."""
88 def __init__(self
, context
, name
, attrs
):
89 self
.value
= int(attrs
.get('value', "0x0000"), 0)
92 enum_name
= "GL_" + attrs
.get('name', None)
93 glItem
.__init
__(self
, name
, enum_name
, context
)
95 def startElement(self
, name
, attrs
):
97 name
= attrs
.get('name', None)
98 count
= int(attrs
.get('count', "0"), 0)
99 self
.functions
[name
] = count
104 class glType( glItem
):
105 """Subclass of glItem for representing GL types."""
107 def __init__(self
, context
, name
, attrs
):
108 self
.size
= int(attrs
.get('size', "0"))
110 type_name
= "GL" + attrs
.get('name', None)
111 glItem
.__init
__(self
, name
, type_name
, context
)
114 class glParameter( glItem
):
115 """Parameter of a glFunction."""
119 p_count_parameters
= None
125 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)
130 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
))
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
140 c
= attrs
.get('count', "0")
142 self
.p_count
= int(c
)
148 if attrs
.get('counter', "false") == "true":
153 if attrs
.get('output', "false") == "true":
158 if self
.p_count
> 0 or self
.counter
!= None or self
.p_count_parameters
!= None :
164 # If there is a * anywhere in the parameter's type, then it
167 if re
.compile("[*]").search(self
.p_type_string
):
168 # We could do some other validation here. For
169 # example, an output parameter should not be const,
170 # but every non-output parameter should.
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.
178 if has_count
or self
.is_output
:
179 raise RuntimeError("Non-pointer type has count or is output.")
182 glItem
.__init
__(self
, name
, p_name
, context
)
186 def is_variable_length_array(self
):
187 """Determine if a parameter is a variable length array.
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
197 return self
.p_count_parameters
!= None
201 return self
.is_pointer
204 def count_string(self
):
205 """Return a string representing the number of items
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."""
215 if self
.is_variable_length_array():
217 elif self
.counter
!= None:
220 return str(self
.p_count
)
226 if self
.is_variable_length_array():
228 elif self
.p_count
== 0:
229 return self
.p_type
.size
231 return self
.p_type
.size
* self
.p_count
234 class glParameterIterator
:
235 """Class to iterate over a list of glParameters.
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."""
241 def __init__(self
, data
):
246 if self
.index
== len( self
.data
):
253 class glFunction( glItem
):
257 fn_return_type
= "void"
260 def __init__(self
, context
, name
, attrs
):
261 self
.fn_alias
= attrs
.get('alias', None)
262 self
.fn_parameters
= []
264 temp
= attrs
.get('offset', None)
265 if temp
== None or temp
== "?":
268 self
.fn_offset
= int(temp
)
270 fn_name
= attrs
.get('name', None)
271 if self
.fn_alias
!= None:
272 self
.real_name
= self
.fn_alias
274 self
.real_name
= fn_name
276 glItem
.__init
__(self
, name
, fn_name
, context
)
281 return glParameterIterator(self
.fn_parameters
)
284 def startElement(self
, name
, attrs
):
287 glParameter(self
, name
, attrs
)
289 print "Error with parameter '%s' in function '%s'." \
290 % (attrs
.get('name','(unknown)'), self
.name
)
292 elif name
== "return":
293 self
.set_return_type(attrs
.get('type', None))
296 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
))
300 self
.fn_parameters
.append(p
)
303 def set_return_type(self
, t
):
304 self
.fn_return_type
= t
307 def get_parameter_string(self
):
311 arg_string
= arg_string
+ comma
+ p
.p_type_string
+ " " + p
.name
321 """Factory to create objects derived from glItem."""
323 def create(self
, context
, name
, attrs
):
324 if name
== "function":
325 return glFunction(context
, name
, attrs
)
327 return glType(context
, name
, attrs
)
329 return glEnum(context
, name
, attrs
)
334 class FilterGLAPISpecBase(saxutils
.XMLFilterBase
):
336 license
= "The license for this file is unspecified."
341 current_object
= None
343 current_category
= ""
346 saxutils
.XMLFilterBase
.__init
__(self
)
350 self
.factory
= glItemFactory()
353 def find_type(self
,type_name
):
355 if re
.compile(t
).search(type_name
):
357 print "Unable to find base type matching \"%s\"." % (type_name
)
361 def find_function(self
,function_name
):
362 index
= self
.xref
[function_name
]
363 return self
.functions
[index
]
366 def printFunctions(self
):
367 keys
= self
.functions
.keys()
373 if self
.functions
[k
].fn_alias
== None:
375 #print 'Missing offset %d' % (prevk)
378 self
.printFunction(self
.functions
[k
])
382 if self
.functions
[k
].fn_alias
!= None:
383 self
.printFunction(self
.functions
[k
])
388 def printHeader(self
):
389 """Print the header associated with all files and call the printRealHeader method."""
391 print '/* DO NOT EDIT - This file generated automatically by %s script */' \
395 print ' * ' + self
.license
.replace('\n', '\n * ')
398 self
.printRealHeader();
402 def printFooter(self
):
403 """Print the header associated with all files and call the printRealFooter method."""
405 self
.printFunctions()
406 self
.printRealFooter()
409 def get_category_define(self
):
410 """Convert the category name to the #define that would be found in glext.h"""
412 if re
.compile("[1-9][0-9]*[.][0-9]+").match(self
.current_category
):
413 s
= self
.current_category
414 return "GL_VERSION_" + s
.replace(".", "_")
416 return self
.current_category
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
425 if obj
.fn_offset
>= 0 or obj
.fn_alias
!= None:
426 if obj
.fn_offset
>= 0:
427 index
= obj
.fn_offset
429 index
= self
.next_alias
432 self
.functions
[index
] = obj
433 self
.xref
[obj
.name
] = index
434 elif object_type
== "type":
435 self
.types
[obj
.name
] = obj
440 def startElement(self
, name
, attrs
):
441 """Start a new element in the XML stream.
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.
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."""
456 if self
.current_object
!= None:
457 self
.current_object
.startElement(name
, attrs
)
458 elif name
== "category":
459 self
.current_category
= attrs
.get('name', "")
461 self
.current_object
= self
.factory
.create(self
, name
, attrs
)
465 def endElement(self
, name
):
466 if self
.current_object
!= None:
467 if self
.current_object
.endElement(name
):
468 self
.current_object
= None
472 def printFunction(self
,offset
):
473 """Print a single function.
475 In the base class, this function is empty. All derived
476 classes should over-ride this function."""
480 def printRealHeader(self
):
481 """Print the "real" header for the created file.
483 In the base class, this function is empty. All derived
484 classes should over-ride this function."""
488 def printRealFooter(self
):
489 """Print the "real" footer for the created file.
491 In the base class, this function is empty. All derived
492 classes should over-ride this function."""