blob: a58f28515563ff118c89627f705438a4078e3f27 [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
Ian Romanick1d270842004-12-21 21:26:36 +0000255 Objects of this class are returned by the parameterIterator method of
256 the glFunction class. They are used to iterate over the list of
Ian Romanicka9d033c2004-05-19 23:33:08 +0000257 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
Ian Romanick1d270842004-12-21 21:26:36 +0000262
263 def __iter__(self):
264 return self
265
Ian Romanick73f59b02004-05-18 18:33:40 +0000266 def next(self):
267 if self.index == len( self.data ):
268 raise StopIteration
269 i = self.index
270 self.index += 1
271 return self.data[i]
272
Ian Romanicka9d033c2004-05-19 23:33:08 +0000273
Ian Romanick73f59b02004-05-18 18:33:40 +0000274class glFunction( glItem ):
275 real_name = ""
276 fn_alias = None
277 fn_offset = -1
278 fn_return_type = "void"
279 fn_parameters = []
280
281 def __init__(self, context, name, attrs):
282 self.fn_alias = attrs.get('alias', None)
283 self.fn_parameters = []
284
285 temp = attrs.get('offset', None)
286 if temp == None or temp == "?":
287 self.fn_offset = -1
288 else:
289 self.fn_offset = int(temp)
290
291 fn_name = attrs.get('name', None)
292 if self.fn_alias != None:
293 self.real_name = self.fn_alias
294 else:
295 self.real_name = fn_name
296
297 glItem.__init__(self, name, fn_name, context)
298 return
299
300
Ian Romanick1d270842004-12-21 21:26:36 +0000301 def parameterIterator(self):
Ian Romanick73f59b02004-05-18 18:33:40 +0000302 return glParameterIterator(self.fn_parameters)
303
304
305 def startElement(self, name, attrs):
306 if name == "param":
Ian Romanick73f59b02004-05-18 18:33:40 +0000307 try:
Ian Romanick4f0a75e2004-12-01 00:29:48 +0000308 self.context.factory.create(self, name, attrs)
Ian Romanick73f59b02004-05-18 18:33:40 +0000309 except RuntimeError:
310 print "Error with parameter '%s' in function '%s'." \
Ian Romanicka9d033c2004-05-19 23:33:08 +0000311 % (attrs.get('name','(unknown)'), self.name)
Ian Romanick73f59b02004-05-18 18:33:40 +0000312 raise
Ian Romanick73f59b02004-05-18 18:33:40 +0000313 elif name == "return":
314 self.set_return_type(attrs.get('type', None))
315
316
Ian Romanicka9d033c2004-05-19 23:33:08 +0000317 def append(self, tag_name, p):
318 if tag_name != "param":
319 raise RuntimeError("Trying to append '%s' to parameter list of function '%s'." % (tag_name, self.name))
320
Ian Romanick73f59b02004-05-18 18:33:40 +0000321 self.fn_parameters.append(p)
322
Ian Romanicka9d033c2004-05-19 23:33:08 +0000323
Ian Romanick73f59b02004-05-18 18:33:40 +0000324 def set_return_type(self, t):
325 self.fn_return_type = t
326
Ian Romanicka9d033c2004-05-19 23:33:08 +0000327
Ian Romanick73f59b02004-05-18 18:33:40 +0000328 def get_parameter_string(self):
329 arg_string = ""
330 comma = ""
Ian Romanick1d270842004-12-21 21:26:36 +0000331 for p in glFunction.parameterIterator(self):
Ian Romanicka9d033c2004-05-19 23:33:08 +0000332 arg_string = arg_string + comma + p.p_type_string + " " + p.name
Ian Romanick73f59b02004-05-18 18:33:40 +0000333 comma = ", "
334
335 if arg_string == "":
336 arg_string = "void"
337
338 return arg_string
339
340
341class glItemFactory:
342 """Factory to create objects derived from glItem."""
343
344 def create(self, context, name, attrs):
345 if name == "function":
346 return glFunction(context, name, attrs)
347 elif name == "type":
348 return glType(context, name, attrs)
349 elif name == "enum":
350 return glEnum(context, name, attrs)
Ian Romanick4f0a75e2004-12-01 00:29:48 +0000351 elif name == "param":
352 return glParameter(context, name, attrs)
Ian Romanick73f59b02004-05-18 18:33:40 +0000353 else:
354 return None
355
356
357class FilterGLAPISpecBase(saxutils.XMLFilterBase):
358 name = "a"
359 license = "The license for this file is unspecified."
360 functions = {}
361 next_alias = -2
362 types = {}
363 xref = {}
364 current_object = None
365 factory = None
366 current_category = ""
367
368 def __init__(self):
369 saxutils.XMLFilterBase.__init__(self)
370 self.functions = {}
371 self.types = {}
372 self.xref = {}
373 self.factory = glItemFactory()
374
Ian Romanicka9d033c2004-05-19 23:33:08 +0000375
Ian Romanick73f59b02004-05-18 18:33:40 +0000376 def find_type(self,type_name):
377 for t in self.types:
378 if re.compile(t).search(type_name):
379 return self.types[t]
380 print "Unable to find base type matching \"%s\"." % (type_name)
381 return None
382
Ian Romanicka9d033c2004-05-19 23:33:08 +0000383
Ian Romanick73f59b02004-05-18 18:33:40 +0000384 def find_function(self,function_name):
385 index = self.xref[function_name]
386 return self.functions[index]
387
Ian Romanicka9d033c2004-05-19 23:33:08 +0000388
Ian Romanick73f59b02004-05-18 18:33:40 +0000389 def printFunctions(self):
390 keys = self.functions.keys()
391 keys.sort()
392 prevk = -1
393 for k in keys:
394 if k < 0: continue
395
396 if self.functions[k].fn_alias == None:
397 if k != prevk + 1:
398 #print 'Missing offset %d' % (prevk)
399 pass
400 prevk = int(k)
401 self.printFunction(self.functions[k])
402
403 keys.reverse()
404 for k in keys:
405 if self.functions[k].fn_alias != None:
406 self.printFunction(self.functions[k])
407
408 return
409
Ian Romanicka9d033c2004-05-19 23:33:08 +0000410
Ian Romanick73f59b02004-05-18 18:33:40 +0000411 def printHeader(self):
Ian Romanicka9d033c2004-05-19 23:33:08 +0000412 """Print the header associated with all files and call the printRealHeader method."""
413
Ian Romanick73f59b02004-05-18 18:33:40 +0000414 print '/* DO NOT EDIT - This file generated automatically by %s script */' \
415 % (self.name)
416 print ''
417 print '/*'
418 print ' * ' + self.license.replace('\n', '\n * ')
419 print ' */'
420 print ''
421 self.printRealHeader();
422 return
423
Ian Romanicka9d033c2004-05-19 23:33:08 +0000424
Ian Romanick73f59b02004-05-18 18:33:40 +0000425 def printFooter(self):
Ian Romanicka9d033c2004-05-19 23:33:08 +0000426 """Print the header associated with all files and call the printRealFooter method."""
427
Ian Romanick73f59b02004-05-18 18:33:40 +0000428 self.printFunctions()
429 self.printRealFooter()
430
431
432 def get_category_define(self):
Ian Romanicka9d033c2004-05-19 23:33:08 +0000433 """Convert the category name to the #define that would be found in glext.h"""
434
Ian Romanick73f59b02004-05-18 18:33:40 +0000435 if re.compile("[1-9][0-9]*[.][0-9]+").match(self.current_category):
436 s = self.current_category
437 return "GL_VERSION_" + s.replace(".", "_")
438 else:
439 return self.current_category
440
441
442 def append(self, object_type, obj):
443 if object_type == "function":
444 # If the function is not an alias and has a negative
445 # offset, then we do not need to track it. These are
446 # functions that don't have an assigned offset
447
448 if obj.fn_offset >= 0 or obj.fn_alias != None:
449 if obj.fn_offset >= 0:
450 index = obj.fn_offset
451 else:
452 index = self.next_alias
453 self.next_alias -= 1
454
455 self.functions[index] = obj
456 self.xref[obj.name] = index
457 elif object_type == "type":
458 self.types[obj.name] = obj
459
460 return
461
462
463 def startElement(self, name, attrs):
Ian Romanicka9d033c2004-05-19 23:33:08 +0000464 """Start a new element in the XML stream.
465
466 Starts a new element. There are three types of elements that
467 are specially handled by this function. When a "category"
468 element is encountered, the name of the category is saved.
469 If an element is encountered and no API object is
470 in-progress, a new object is created using the API factory.
471 Any future elements, until that API object is closed, are
472 passed to the current objects startElement method.
473
474 This paradigm was chosen becuase it allows subclasses of the
475 basic API types (i.e., glFunction, glEnum, etc.) to handle
476 additional XML data, GLX protocol information, that the base
477 classes do not know about."""
478
Ian Romanick73f59b02004-05-18 18:33:40 +0000479 if self.current_object != None:
480 self.current_object.startElement(name, attrs)
481 elif name == "category":
482 self.current_category = attrs.get('name', "")
483 else:
484 self.current_object = self.factory.create(self, name, attrs)
485 return
486
Ian Romanicka9d033c2004-05-19 23:33:08 +0000487
Ian Romanick73f59b02004-05-18 18:33:40 +0000488 def endElement(self, name):
489 if self.current_object != None:
490 if self.current_object.endElement(name):
491 self.current_object = None
492 return
493
Ian Romanicka9d033c2004-05-19 23:33:08 +0000494
Ian Romanick73f59b02004-05-18 18:33:40 +0000495 def printFunction(self,offset):
Ian Romanicka9d033c2004-05-19 23:33:08 +0000496 """Print a single function.
497
498 In the base class, this function is empty. All derived
499 classes should over-ride this function."""
Ian Romanick73f59b02004-05-18 18:33:40 +0000500 return
501
Ian Romanicka9d033c2004-05-19 23:33:08 +0000502
Ian Romanick73f59b02004-05-18 18:33:40 +0000503 def printRealHeader(self):
Ian Romanicka9d033c2004-05-19 23:33:08 +0000504 """Print the "real" header for the created file.
505
506 In the base class, this function is empty. All derived
507 classes should over-ride this function."""
Ian Romanick73f59b02004-05-18 18:33:40 +0000508 return
509
Ian Romanicka9d033c2004-05-19 23:33:08 +0000510
Ian Romanick73f59b02004-05-18 18:33:40 +0000511 def printRealFooter(self):
Ian Romanicka9d033c2004-05-19 23:33:08 +0000512 """Print the "real" footer for the created file.
513
514 In the base class, this function is empty. All derived
515 classes should over-ride this function."""
Ian Romanick73f59b02004-05-18 18:33:40 +0000516 return