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) or (self
.counter
!= 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
.p_count_parameters
!= None:
216 elif self
.counter
!= None:
219 return str(self
.p_count
)
225 if self
.p_count_parameters
!= None or self
.counter
!= None or self
.is_output
:
227 elif self
.p_count
== 0:
228 return self
.p_type
.size
230 return self
.p_type
.size
* self
.p_count
232 def size_string(self
):
236 b_prod
= self
.p_type
.size
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:
242 elif self
.p_count_parameters
!= None and self
.counter
!= None:
243 b_prod
= self
.counter
245 raise RuntimeError("Parameter '%s' to function '%s' has size 0." % (self
.name
, self
.context
.name
))
247 return "(%s * %s)" % (a_prod
, b_prod
)
252 class glParameterIterator
:
253 """Class to iterate over a list of glParameters.
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."""
259 def __init__(self
, data
):
264 if self
.index
== len( self
.data
):
271 class glFunction( glItem
):
275 fn_return_type
= "void"
278 def __init__(self
, context
, name
, attrs
):
279 self
.fn_alias
= attrs
.get('alias', None)
280 self
.fn_parameters
= []
282 temp
= attrs
.get('offset', None)
283 if temp
== None or temp
== "?":
286 self
.fn_offset
= int(temp
)
288 fn_name
= attrs
.get('name', None)
289 if self
.fn_alias
!= None:
290 self
.real_name
= self
.fn_alias
292 self
.real_name
= fn_name
294 glItem
.__init
__(self
, name
, fn_name
, context
)
299 return glParameterIterator(self
.fn_parameters
)
302 def startElement(self
, name
, attrs
):
305 self
.context
.factory
.create(self
, name
, attrs
)
307 print "Error with parameter '%s' in function '%s'." \
308 % (attrs
.get('name','(unknown)'), self
.name
)
310 elif name
== "return":
311 self
.set_return_type(attrs
.get('type', None))
314 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
))
318 self
.fn_parameters
.append(p
)
321 def set_return_type(self
, t
):
322 self
.fn_return_type
= t
325 def get_parameter_string(self
):
329 arg_string
= arg_string
+ comma
+ p
.p_type_string
+ " " + p
.name
339 """Factory to create objects derived from glItem."""
341 def create(self
, context
, name
, attrs
):
342 if name
== "function":
343 return glFunction(context
, name
, attrs
)
345 return glType(context
, name
, attrs
)
347 return glEnum(context
, name
, attrs
)
348 elif name
== "param":
349 return glParameter(context
, name
, attrs
)
354 class FilterGLAPISpecBase(saxutils
.XMLFilterBase
):
356 license
= "The license for this file is unspecified."
361 current_object
= None
363 current_category
= ""
366 saxutils
.XMLFilterBase
.__init
__(self
)
370 self
.factory
= glItemFactory()
373 def find_type(self
,type_name
):
375 if re
.compile(t
).search(type_name
):
377 print "Unable to find base type matching \"%s\"." % (type_name
)
381 def find_function(self
,function_name
):
382 index
= self
.xref
[function_name
]
383 return self
.functions
[index
]
386 def printFunctions(self
):
387 keys
= self
.functions
.keys()
393 if self
.functions
[k
].fn_alias
== None:
395 #print 'Missing offset %d' % (prevk)
398 self
.printFunction(self
.functions
[k
])
402 if self
.functions
[k
].fn_alias
!= None:
403 self
.printFunction(self
.functions
[k
])
408 def printHeader(self
):
409 """Print the header associated with all files and call the printRealHeader method."""
411 print '/* DO NOT EDIT - This file generated automatically by %s script */' \
415 print ' * ' + self
.license
.replace('\n', '\n * ')
418 self
.printRealHeader();
422 def printFooter(self
):
423 """Print the header associated with all files and call the printRealFooter method."""
425 self
.printFunctions()
426 self
.printRealFooter()
429 def get_category_define(self
):
430 """Convert the category name to the #define that would be found in glext.h"""
432 if re
.compile("[1-9][0-9]*[.][0-9]+").match(self
.current_category
):
433 s
= self
.current_category
434 return "GL_VERSION_" + s
.replace(".", "_")
436 return self
.current_category
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
445 if obj
.fn_offset
>= 0 or obj
.fn_alias
!= None:
446 if obj
.fn_offset
>= 0:
447 index
= obj
.fn_offset
449 index
= self
.next_alias
452 self
.functions
[index
] = obj
453 self
.xref
[obj
.name
] = index
454 elif object_type
== "type":
455 self
.types
[obj
.name
] = obj
460 def startElement(self
, name
, attrs
):
461 """Start a new element in the XML stream.
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.
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."""
476 if self
.current_object
!= None:
477 self
.current_object
.startElement(name
, attrs
)
478 elif name
== "category":
479 self
.current_category
= attrs
.get('name', "")
481 self
.current_object
= self
.factory
.create(self
, name
, attrs
)
485 def endElement(self
, name
):
486 if self
.current_object
!= None:
487 if self
.current_object
.endElement(name
):
488 self
.current_object
= None
492 def printFunction(self
,offset
):
493 """Print a single function.
495 In the base class, this function is empty. All derived
496 classes should over-ride this function."""
500 def printRealHeader(self
):
501 """Print the "real" header for the created file.
503 In the base class, this function is empty. All derived
504 classes should over-ride this function."""
508 def printRealFooter(self
):
509 """Print the "real" footer for the created file.
511 In the base class, this function is empty. All derived
512 classes should over-ride this function."""