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