Added the ability to get the size of a parameter as a string. Changed the
[mesa.git] / src / mesa / glapi / gl_XML.py
1 #!/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
28 from xml.sax import saxutils
29 from xml.sax import make_parser
30 from xml.sax.handler import feature_namespaces
31
32 import re
33
34 class glItem:
35 """Generic class on which all other API entity types are based."""
36
37 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):
47 """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."""
54 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
67 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."""
72
73 if name == self.tag_name:
74 return 1
75 else:
76 return 0
77
78 def get_category_define(self):
79 return self.category
80
81
82 class glEnum( glItem ):
83 """Subclass of glItem for representing GL enumerants.
84
85 This class is not complete, and is not really used yet."""
86
87 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
103 class glType( glItem ):
104 """Subclass of glItem for representing GL types."""
105
106 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
113 class glParameter( glItem ):
114 """Parameter of a glFunction."""
115 p_type = None
116 p_type_string = ""
117 p_count = 0
118 p_count_parameters = None
119 counter = None
120 is_output = 0
121 is_counter = 0
122 is_pointer = 0
123
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)
128
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))
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")
140 try:
141 self.p_count = int(c)
142 self.counter = None
143 except Exception,e:
144 self.p_count = 0
145 self.counter = c
146
147 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":
153 self.is_output = 1
154 else:
155 self.is_output = 0
156
157 if self.p_count > 0 or self.counter != None or self.p_count_parameters != None :
158 has_count = 1
159 else:
160 has_count = 0
161
162
163 # If there is a * anywhere in the parameter's type, then it
164 # is a pointer.
165
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.
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
181 glItem.__init__(self, name, p_name, context)
182 return
183
184
185 def is_variable_length_array(self):
186 """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
196 return (self.p_count_parameters != None) or (self.counter != None)
197
198
199 def is_array(self):
200 return self.is_pointer
201
202
203 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():
214 if self.p_count_parameters != None:
215 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
223
224 def size(self):
225 if self.p_count_parameters != None or self.counter != None or self.is_output:
226 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
232 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
251
252 class glParameterIterator:
253 """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
259 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
270
271 class 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":
304 try:
305 self.context.factory.create(self, name, attrs)
306 except RuntimeError:
307 print "Error with parameter '%s' in function '%s'." \
308 % (attrs.get('name','(unknown)'), self.name)
309 raise
310 elif name == "return":
311 self.set_return_type(attrs.get('type', None))
312
313
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))
317
318 self.fn_parameters.append(p)
319
320
321 def set_return_type(self, t):
322 self.fn_return_type = t
323
324
325 def get_parameter_string(self):
326 arg_string = ""
327 comma = ""
328 for p in self:
329 arg_string = arg_string + comma + p.p_type_string + " " + p.name
330 comma = ", "
331
332 if arg_string == "":
333 arg_string = "void"
334
335 return arg_string
336
337
338 class 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)
348 elif name == "param":
349 return glParameter(context, name, attrs)
350 else:
351 return None
352
353
354 class 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
372
373 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
380
381 def find_function(self,function_name):
382 index = self.xref[function_name]
383 return self.functions[index]
384
385
386 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
407
408 def printHeader(self):
409 """Print the header associated with all files and call the printRealHeader method."""
410
411 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
421
422 def printFooter(self):
423 """Print the header associated with all files and call the printRealFooter method."""
424
425 self.printFunctions()
426 self.printRealFooter()
427
428
429 def get_category_define(self):
430 """Convert the category name to the #define that would be found in glext.h"""
431
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(".", "_")
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):
461 """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
476 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
484
485 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
491
492 def printFunction(self,offset):
493 """Print a single function.
494
495 In the base class, this function is empty. All derived
496 classes should over-ride this function."""
497 return
498
499
500 def printRealHeader(self):
501 """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."""
505 return
506
507
508 def printRealFooter(self):
509 """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."""
513 return