Add a glFunctionIterator class to iterate over the functions stored in a
[mesa.git] / src / mesa / glapi / gl_XML.py
1 #!/usr/bin/python2
2
3 # (C) Copyright IBM Corporation 2004, 2005
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
90 enum_name = "GL_" + attrs.get('name', None)
91 glItem.__init__(self, name, enum_name, context)
92
93 temp = attrs.get('count', None)
94 if temp == None:
95 self.default_count = 0
96 else:
97 try:
98 c = int(temp)
99 except Exception,e:
100 raise RuntimeError('Invalid count value "%s" for enum "%s" in function "%s" when an integer was expected.' % (temp, self.name, n))
101
102 self.default_count = c
103 return
104
105
106 def process_attributes(self, attrs):
107 name = attrs.get('name', None)
108
109 temp = attrs.get('count', None)
110 if temp == None:
111 c = self.default_count
112 else:
113 try:
114 c = int(temp)
115 except Exception,e:
116 raise RuntimeError('Invalid count value "%s" for enum "%s" in function "%s" when an integer was expected.' % (temp, self.name, n))
117
118 mode_str = attrs.get('mode', "set")
119 if mode_str == "set":
120 mode = 1
121 elif mode_str == "get":
122 mode = 0
123 else:
124 raise RuntimeError("Invalid mode '%s' for function '%s' in enum '%s'." % (mode_str, self.context.name, self.name))
125
126 return [name, c, mode]
127
128
129 class glType( glItem ):
130 """Subclass of glItem for representing GL types."""
131
132 def __init__(self, context, name, attrs):
133 self.size = int(attrs.get('size', "0"))
134 self.glx_name = attrs.get('glx_name', "")
135
136 type_name = "GL" + attrs.get('name', None)
137 glItem.__init__(self, name, type_name, context)
138
139
140 class glParameter( glItem ):
141 """Parameter of a glFunction."""
142 p_type = None
143 p_type_string = ""
144 p_count = 0
145 p_count_parameters = None
146 counter = None
147 is_output = 0
148 is_counter = 0
149 is_pointer = 0
150
151 def __init__(self, context, name, attrs):
152 p_name = attrs.get('name', None)
153 self.p_type_string = attrs.get('type', None)
154 self.p_count_parameters = attrs.get('variable_param', None)
155
156 self.p_type = context.context.find_type(self.p_type_string)
157 if self.p_type == None:
158 raise RuntimeError("Unknown type '%s' in function '%s'." % (self.p_type_string, context.name))
159
160
161 # The count tag can be either a numeric string or the name of
162 # a variable. If it is the name of a variable, the int(c)
163 # statement will throw an exception, and the except block will
164 # take over.
165
166 c = attrs.get('count', "0")
167 try:
168 self.p_count = int(c)
169 self.counter = None
170 except Exception,e:
171 self.p_count = 0
172 self.counter = c
173
174 if attrs.get('counter', "false") == "true":
175 self.is_counter = 1
176 else:
177 self.is_counter = 0
178
179 if attrs.get('output', "false") == "true":
180 self.is_output = 1
181 else:
182 self.is_output = 0
183
184
185 # Pixel data has special parameters.
186
187 self.width = attrs.get('img_width', None)
188 self.height = attrs.get('img_height', None)
189 self.depth = attrs.get('img_depth', None)
190 self.extent = attrs.get('img_extent', None)
191
192 self.img_xoff = attrs.get('img_xoff', None)
193 self.img_yoff = attrs.get('img_yoff', None)
194 self.img_zoff = attrs.get('img_zoff', None)
195 self.img_woff = attrs.get('img_woff', None)
196
197 self.img_format = attrs.get('img_format', None)
198 self.img_type = attrs.get('img_type', None)
199 self.img_target = attrs.get('img_target', None)
200
201 pad = attrs.get('img_pad_dimensions', "false")
202 if pad == "true":
203 self.img_pad_dimensions = 1
204 else:
205 self.img_pad_dimensions = 0
206
207
208 null_flag = attrs.get('img_null_flag', "false")
209 if null_flag == "true":
210 self.img_null_flag = 1
211 else:
212 self.img_null_flag = 0
213
214 send_null = attrs.get('img_send_null', "false")
215 if send_null == "true":
216 self.img_send_null = 1
217 else:
218 self.img_send_null = 0
219
220
221
222 if self.p_count > 0 or self.counter or self.p_count_parameters:
223 has_count = 1
224 else:
225 has_count = 0
226
227
228 # If there is a * anywhere in the parameter's type, then it
229 # is a pointer.
230
231 if re.compile("[*]").search(self.p_type_string):
232 # We could do some other validation here. For
233 # example, an output parameter should not be const,
234 # but every non-output parameter should.
235
236 self.is_pointer = 1;
237 else:
238 # If a parameter is not a pointer, then there cannot
239 # be an associated count (either fixed size or
240 # variable) and the parameter cannot be an output.
241
242 if has_count or self.is_output:
243 raise RuntimeError("Non-pointer type has count or is output.")
244 self.is_pointer = 0;
245
246 glItem.__init__(self, name, p_name, context)
247 return
248
249
250 def is_variable_length_array(self):
251 """Determine if a parameter is a variable length array.
252
253 A parameter is considered to be a variable length array if
254 its size depends on the value of another parameter that is
255 an enumerant. The params parameter to glTexEnviv is an
256 example of a variable length array parameter. Arrays whose
257 size depends on a count variable, such as the lists parameter
258 to glCallLists, are not variable length arrays in this
259 sense."""
260
261 return self.p_count_parameters or self.counter or self.width
262
263
264 def is_array(self):
265 return self.is_pointer
266
267
268 def count_string(self):
269 """Return a string representing the number of items
270
271 Returns a string representing the number of items in a
272 parameter. For scalar types this will always be "1". For
273 vector types, it will depend on whether or not it is a
274 fixed length vector (like the parameter of glVertex3fv),
275 a counted length (like the vector parameter of
276 glDeleteTextures), or a general variable length vector."""
277
278 if self.is_array():
279 if self.p_count_parameters != None:
280 return "compsize"
281 elif self.counter != None:
282 return self.counter
283 else:
284 return str(self.p_count)
285 else:
286 return "1"
287
288
289 def size(self):
290 if self.p_count_parameters or self.counter or self.width or self.is_output:
291 return 0
292 elif self.p_count == 0:
293 return self.p_type.size
294 else:
295 return self.p_type.size * self.p_count
296
297 def size_string(self):
298 s = self.size()
299 if s == 0:
300 a_prod = "compsize"
301 b_prod = self.p_type.size
302
303 if self.p_count_parameters == None and self.counter != None:
304 a_prod = self.counter
305 elif self.p_count_parameters != None and self.counter == None:
306 pass
307 elif self.p_count_parameters != None and self.counter != None:
308 b_prod = self.counter
309 elif self.width:
310 return "compsize"
311 else:
312 raise RuntimeError("Parameter '%s' to function '%s' has size 0." % (self.name, self.context.name))
313
314 return "(%s * %s)" % (a_prod, b_prod)
315 else:
316 return str(s)
317
318
319 class glParameterIterator:
320 """Class to iterate over a list of glParameters.
321
322 Objects of this class are returned by the parameterIterator method of
323 the glFunction class. They are used to iterate over the list of
324 parameters to the function."""
325
326 def __init__(self, data):
327 self.data = data
328 self.index = 0
329
330 def __iter__(self):
331 return self
332
333 def next(self):
334 if self.index == len( self.data ):
335 raise StopIteration
336 i = self.index
337 self.index += 1
338 return self.data[i]
339
340
341 class glFunction( glItem ):
342 real_name = ""
343 fn_alias = None
344 fn_offset = -1
345 fn_return_type = "void"
346 fn_parameters = []
347
348 def __init__(self, context, name, attrs):
349 self.fn_alias = attrs.get('alias', None)
350 self.fn_parameters = []
351 self.image = None
352
353 temp = attrs.get('offset', None)
354 if temp == None or temp == "?":
355 self.fn_offset = -1
356 else:
357 self.fn_offset = int(temp)
358
359 fn_name = attrs.get('name', None)
360 if self.fn_alias != None:
361 self.real_name = self.fn_alias
362 else:
363 self.real_name = fn_name
364
365 glItem.__init__(self, name, fn_name, context)
366 return
367
368
369 def parameterIterator(self):
370 return glParameterIterator(self.fn_parameters)
371
372
373 def startElement(self, name, attrs):
374 if name == "param":
375 try:
376 self.context.factory.create(self, name, attrs)
377 except RuntimeError:
378 print "Error with parameter '%s' in function '%s'." \
379 % (attrs.get('name','(unknown)'), self.name)
380 raise
381 elif name == "return":
382 self.set_return_type(attrs.get('type', None))
383
384
385 def append(self, tag_name, p):
386 if tag_name != "param":
387 raise RuntimeError("Trying to append '%s' to parameter list of function '%s'." % (tag_name, self.name))
388
389 if p.width:
390 self.image = p
391
392 self.fn_parameters.append(p)
393
394
395 def set_return_type(self, t):
396 self.fn_return_type = t
397
398
399 def get_parameter_string(self):
400 arg_string = ""
401 comma = ""
402 for p in glFunction.parameterIterator(self):
403 arg_string = arg_string + comma + p.p_type_string + " " + p.name
404 comma = ", "
405
406 if arg_string == "":
407 arg_string = "void"
408
409 return arg_string
410
411
412 class glItemFactory:
413 """Factory to create objects derived from glItem."""
414
415 def create(self, context, name, attrs):
416 if name == "function":
417 return glFunction(context, name, attrs)
418 elif name == "type":
419 return glType(context, name, attrs)
420 elif name == "enum":
421 return glEnum(context, name, attrs)
422 elif name == "param":
423 return glParameter(context, name, attrs)
424 else:
425 return None
426
427
428 class glFunctionIterator:
429 """Class to iterate over a list of glFunctions
430
431 Objects of this classare returned by
432 FilterGLAPISpecBase::functionIterator. This default version
433 iterates over the functions in order of dispatch table offset. All
434 of the "true" functions are iterated first, followed by the alias
435 functions."""
436
437 def __init__(self, context):
438 self.context = context
439 self.keys = context.functions.keys()
440 self.keys.sort()
441
442 self.prevk = -1
443 self.direction = 1
444
445 for self.index in range(0, len(self.keys)):
446 if self.keys[ self.index ] >= 0: break
447
448 if self.index == len(self.keys):
449 self.direction = -1
450 self.index -= 1
451
452 self.split = self.index - 1
453 return
454
455
456 def __iter__(self):
457 return self
458
459
460 def next(self):
461 if self.index < 0:
462 raise StopIteration
463
464 k = self.keys[ self.index ]
465
466 #if self.context.functions[k].fn_alias == None:
467 # if k != self.prevk + 1:
468 # print 'Missing offset %d' % (prevk)
469 # self.prevk = int(k)
470
471 self.index += self.direction
472
473 if self.index == len(self.keys):
474 self.index = self.split
475 self.direction = -1
476
477 return self.context.functions[k]
478
479
480 class FilterGLAPISpecBase(saxutils.XMLFilterBase):
481 name = "a"
482 license = "The license for this file is unspecified."
483 functions = {}
484 next_alias = -2
485 types = {}
486 xref = {}
487 current_object = None
488 factory = None
489 current_category = ""
490
491 def __init__(self):
492 saxutils.XMLFilterBase.__init__(self)
493 self.functions = {}
494 self.types = {}
495 self.xref = {}
496 self.factory = glItemFactory()
497
498
499 def find_type(self,type_name):
500 for t in self.types:
501 if re.compile(t).search(type_name):
502 return self.types[t]
503 print "Unable to find base type matching \"%s\"." % (type_name)
504 return None
505
506
507 def find_function(self,function_name):
508 index = self.xref[function_name]
509 return self.functions[index]
510
511
512 def functionIterator(self):
513 return glFunctionIterator(self)
514
515
516 def printFunctions(self):
517 for f in self.functionIterator():
518 self.printFunction(f)
519 return
520
521
522 def printHeader(self):
523 """Print the header associated with all files and call the printRealHeader method."""
524
525 print '/* DO NOT EDIT - This file generated automatically by %s script */' \
526 % (self.name)
527 print ''
528 print '/*'
529 print ' * ' + self.license.replace('\n', '\n * ')
530 print ' */'
531 print ''
532 self.printRealHeader();
533 return
534
535
536 def printFooter(self):
537 """Print the header associated with all files and call the printRealFooter method."""
538
539 self.printFunctions()
540 self.printRealFooter()
541
542
543 def get_category_define(self):
544 """Convert the category name to the #define that would be found in glext.h"""
545
546 if re.compile("[1-9][0-9]*[.][0-9]+").match(self.current_category):
547 s = self.current_category
548 return "GL_VERSION_" + s.replace(".", "_")
549 else:
550 return self.current_category
551
552
553 def append(self, object_type, obj):
554 if object_type == "function":
555 # If the function is not an alias and has a negative
556 # offset, then we do not need to track it. These are
557 # functions that don't have an assigned offset
558
559 if obj.fn_offset >= 0 or obj.fn_alias != None:
560 if obj.fn_offset >= 0:
561 index = obj.fn_offset
562 else:
563 index = self.next_alias
564 self.next_alias -= 1
565
566 self.functions[index] = obj
567 self.xref[obj.name] = index
568 elif object_type == "type":
569 self.types[obj.name] = obj
570
571 return
572
573
574 def startElement(self, name, attrs):
575 """Start a new element in the XML stream.
576
577 Starts a new element. There are three types of elements that
578 are specially handled by this function. When a "category"
579 element is encountered, the name of the category is saved.
580 If an element is encountered and no API object is
581 in-progress, a new object is created using the API factory.
582 Any future elements, until that API object is closed, are
583 passed to the current objects startElement method.
584
585 This paradigm was chosen becuase it allows subclasses of the
586 basic API types (i.e., glFunction, glEnum, etc.) to handle
587 additional XML data, GLX protocol information, that the base
588 classes do not know about."""
589
590 if self.current_object != None:
591 self.current_object.startElement(name, attrs)
592 elif name == "category":
593 self.current_category = attrs.get('name', "")
594 else:
595 self.current_object = self.factory.create(self, name, attrs)
596 return
597
598
599 def endElement(self, name):
600 if self.current_object != None:
601 if self.current_object.endElement(name):
602 self.current_object = None
603 return
604
605
606 def printFunction(self,offset):
607 """Print a single function.
608
609 In the base class, this function is empty. All derived
610 classes should over-ride this function."""
611 return
612
613
614 def printRealHeader(self):
615 """Print the "real" header for the created file.
616
617 In the base class, this function is empty. All derived
618 classes should over-ride this function."""
619 return
620
621
622 def printRealFooter(self):
623 """Print the "real" footer for the created file.
624
625 In the base class, this function is empty. All derived
626 classes should over-ride this function."""
627 return