e4de4cacdd681aa1b6fcd06f33a23d1d61ef9d12
[mesa.git] / src / mesa / glapi / gl_XML.py
1 #!/usr/bin/env python
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 import libxml2
29 import re, sys, string
30 import typeexpr
31
32
33 def parse_GL_API( file_name, factory = None ):
34 doc = libxml2.readFile( file_name, None, libxml2.XML_PARSE_XINCLUDE + libxml2.XML_PARSE_NOBLANKS + libxml2.XML_PARSE_DTDVALID + libxml2.XML_PARSE_DTDATTR + libxml2.XML_PARSE_DTDLOAD + libxml2.XML_PARSE_NOENT )
35 ret = doc.xincludeProcess()
36
37 if not factory:
38 factory = gl_item_factory()
39
40 api = factory.create_item( "api", None, None )
41 api.process_element( doc )
42
43 doc.freeDoc()
44
45 return api
46
47
48 def is_attr_true( element, name ):
49 """Read a name value from an element's attributes.
50
51 The value read from the attribute list must be either 'true' or
52 'false'. If the value is 'false', zero will be returned. If the
53 value is 'true', non-zero will be returned. An exception will be
54 raised for any other value."""
55
56 value = element.nsProp( name, None )
57 if value == "true":
58 return 1
59 elif value == "false":
60 return 0
61 else:
62 raise RuntimeError('Invalid value "%s" for boolean "%s".' % (value, name))
63
64
65 class gl_print_base:
66 """Base class of all API pretty-printers.
67
68 In the model-view-controller pattern, this is the view. Any derived
69 class will want to over-ride the printBody, printRealHader, and
70 printRealFooter methods. Some derived classes may want to over-ride
71 printHeader and printFooter, or even Print (though this is unlikely).
72 """
73
74 def __init__(self):
75 # Name of the script that is generating the output file.
76 # Every derived class should set this to the name of its
77 # source file.
78
79 self.name = "a"
80
81
82 # License on the *generated* source file. This may differ
83 # from the license on the script that is generating the file.
84 # Every derived class should set this to some reasonable
85 # value.
86 #
87 # See license.py for an example of a reasonable value.
88
89 self.license = "The license for this file is unspecified."
90
91
92 # The header_tag is the name of the C preprocessor define
93 # used to prevent multiple inclusion. Typically only
94 # generated C header files need this to be set. Setting it
95 # causes code to be generated automatically in printHeader
96 # and printFooter.
97
98 self.header_tag = None
99
100
101 # List of file-private defines that must be undefined at the
102 # end of the file. This can be used in header files to define
103 # names for use in the file, then undefine them at the end of
104 # the header file.
105
106 self.undef_list = []
107 return
108
109
110 def Print(self, api):
111 self.printHeader()
112 self.printBody(api)
113 self.printFooter()
114 return
115
116
117 def printHeader(self):
118 """Print the header associated with all files and call the printRealHeader method."""
119
120 print '/* DO NOT EDIT - This file generated automatically by %s script */' \
121 % (self.name)
122 print ''
123 print '/*'
124 print ' * ' + self.license.replace('\n', '\n * ')
125 print ' */'
126 print ''
127 if self.header_tag:
128 print '#if !defined( %s )' % (self.header_tag)
129 print '# define %s' % (self.header_tag)
130 print ''
131 self.printRealHeader();
132 return
133
134
135 def printFooter(self):
136 """Print the header associated with all files and call the printRealFooter method."""
137
138 self.printRealFooter()
139
140 if self.undef_list:
141 print ''
142 for u in self.undef_list:
143 print "# undef %s" % (u)
144
145 if self.header_tag:
146 print ''
147 print '#endif /* !defined( %s ) */' % (self.header_tag)
148
149
150 def printRealHeader(self):
151 """Print the "real" header for the created file.
152
153 In the base class, this function is empty. All derived
154 classes should over-ride this function."""
155 return
156
157
158 def printRealFooter(self):
159 """Print the "real" footer for the created file.
160
161 In the base class, this function is empty. All derived
162 classes should over-ride this function."""
163 return
164
165
166 def printPure(self):
167 """Conditionally define `PURE' function attribute.
168
169 Conditionally defines a preprocessor macro `PURE' that wraps
170 GCC's `pure' function attribute. The conditional code can be
171 easilly adapted to other compilers that support a similar
172 feature.
173
174 The name is also added to the file's undef_list.
175 """
176 self.undef_list.append("PURE")
177 print """# if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96)
178 # define PURE __attribute__((pure))
179 # else
180 # define PURE
181 # endif"""
182 return
183
184
185 def printFastcall(self):
186 """Conditionally define `FASTCALL' function attribute.
187
188 Conditionally defines a preprocessor macro `FASTCALL' that
189 wraps GCC's `fastcall' function attribute. The conditional
190 code can be easilly adapted to other compilers that support a
191 similar feature.
192
193 The name is also added to the file's undef_list.
194 """
195
196 self.undef_list.append("FASTCALL")
197 print """# if defined(__i386__) && defined(__GNUC__)
198 # define FASTCALL __attribute__((fastcall))
199 # else
200 # define FASTCALL
201 # endif"""
202 return
203
204
205 def printVisibility(self, S, s):
206 """Conditionally define visibility function attribute.
207
208 Conditionally defines a preprocessor macro name S that wraps
209 GCC's visibility function attribute. The visibility used is
210 the parameter s. The conditional code can be easilly adapted
211 to other compilers that support a similar feature.
212
213 The name is also added to the file's undef_list.
214 """
215
216 self.undef_list.append(S)
217 print """# if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)
218 # define %s __attribute__((visibility("%s")))
219 # else
220 # define %s
221 # endif""" % (S, s, S)
222 return
223
224
225 def printNoinline(self):
226 """Conditionally define `NOINLINE' function attribute.
227
228 Conditionally defines a preprocessor macro `NOINLINE' that
229 wraps GCC's `noinline' function attribute. The conditional
230 code can be easilly adapted to other compilers that support a
231 similar feature.
232
233 The name is also added to the file's undef_list.
234 """
235
236 self.undef_list.append("NOINLINE")
237 print """# if defined(__GNUC__)
238 # define NOINLINE __attribute__((noinline))
239 # else
240 # define NOINLINE
241 # endif"""
242 return
243
244
245 def printHaveAlias(self):
246 """Conditionally define `HAVE_ALIAS'.
247
248 Conditionally defines a preprocessor macro `HAVE_ALIAS'. The
249 existance of this macro can be used to determine whether or
250 not GCC's alias function attribute can be used.
251
252 The name is also added to the file's undef_list.
253 """
254
255 self.undef_list.append("HAVE_ALIAS")
256 print """# if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
257 # define HAVE_ALIAS
258 # endif"""
259 return
260
261
262 def real_function_name(element):
263 name = element.nsProp( "name", None )
264 alias = element.nsProp( "alias", None )
265
266 if alias:
267 return alias
268 else:
269 return name
270
271
272 class gl_item:
273 def __init__(self, element, context):
274 self.context = context
275
276 self.name = element.nsProp( "name", None )
277
278 c = element.parent.nsProp( "name", None )
279 if re.compile("[1-9][0-9]*[.][0-9]+").match(c):
280 self.category = "GL_VERSION_" + c.replace(".", "_")
281 else:
282 self.category = c
283
284 return
285
286
287 class gl_type( gl_item ):
288 def __init__(self, element, context):
289 gl_item.__init__(self, element, context)
290 self.size = int( element.nsProp( "size", None ), 0 )
291
292 te = typeexpr.type_expression( None )
293 tn = typeexpr.type_node()
294 tn.size = int( element.nsProp( "size", None ), 0 )
295 tn.integer = not is_attr_true( element, "float" )
296 tn.unsigned = is_attr_true( element, "unsigned" )
297 tn.name = "GL" + self.name
298 te.set_base_type_node( tn )
299
300 self.type_expr = te
301 return
302
303
304 def get_type_expression(self):
305 return self.type_expr
306
307
308 class gl_enum( gl_item ):
309 def __init__(self, element, context):
310 gl_item.__init__(self, element, context)
311 self.value = int( element.nsProp( "value", None ), 0 )
312
313 temp = element.nsProp( "count", None )
314 if not temp or temp == "?":
315 self.default_count = -1
316 else:
317 try:
318 c = int(temp)
319 except Exception,e:
320 raise RuntimeError('Invalid count value "%s" for enum "%s" in function "%s" when an integer was expected.' % (temp, self.name, n))
321
322 self.default_count = c
323
324 return
325
326
327 def priority(self):
328 """Calculate a 'priority' for this enum name.
329
330 When an enum is looked up by number, there may be many
331 possible names, but only one is the 'prefered' name. The
332 priority is used to select which name is the 'best'.
333
334 Highest precedence is given to core GL name. ARB extension
335 names have the next highest, followed by EXT extension names.
336 Vendor extension names are the lowest.
337 """
338
339 if self.name.endswith( "_BIT" ):
340 bias = 1
341 else:
342 bias = 0
343
344 if self.category.startswith( "GL_VERSION_" ):
345 priority = 0
346 elif self.category.startswith( "GL_ARB_" ):
347 priority = 2
348 elif self.category.startswith( "GL_EXT_" ):
349 priority = 4
350 else:
351 priority = 6
352
353 return priority + bias
354
355
356
357 class gl_parameter:
358 def __init__(self, element, context):
359 self.name = element.nsProp( "name", None )
360
361 ts = element.nsProp( "type", None )
362 self.type_expr = typeexpr.type_expression( ts, context )
363
364 temp = element.nsProp( "variable_param", None )
365 if temp:
366 self.count_parameter_list = temp.split( ' ' )
367 else:
368 self.count_parameter_list = []
369
370 # The count tag can be either a numeric string or the name of
371 # a variable. If it is the name of a variable, the int(c)
372 # statement will throw an exception, and the except block will
373 # take over.
374
375 c = element.nsProp( "count", None )
376 try:
377 count = int(c)
378 self.count = count
379 self.counter = None
380 except Exception,e:
381 count = 1
382 self.count = 0
383 self.counter = c
384
385 self.count_scale = int(element.nsProp( "count_scale", None ))
386
387 elements = (count * self.count_scale)
388 if elements == 1:
389 elements = 0
390
391 #if ts == "GLdouble":
392 # print '/* stack size -> %s = %u (before)*/' % (self.name, self.type_expr.get_stack_size())
393 # print '/* # elements = %u */' % (elements)
394 self.type_expr.set_elements( elements )
395 #if ts == "GLdouble":
396 # print '/* stack size -> %s = %u (after) */' % (self.name, self.type_expr.get_stack_size())
397
398 self.is_counter = is_attr_true( element, 'counter' )
399 self.is_output = is_attr_true( element, 'output' )
400
401
402 # Pixel data has special parameters.
403
404 self.width = element.nsProp('img_width', None)
405 self.height = element.nsProp('img_height', None)
406 self.depth = element.nsProp('img_depth', None)
407 self.extent = element.nsProp('img_extent', None)
408
409 self.img_xoff = element.nsProp('img_xoff', None)
410 self.img_yoff = element.nsProp('img_yoff', None)
411 self.img_zoff = element.nsProp('img_zoff', None)
412 self.img_woff = element.nsProp('img_woff', None)
413
414 self.img_format = element.nsProp('img_format', None)
415 self.img_type = element.nsProp('img_type', None)
416 self.img_target = element.nsProp('img_target', None)
417
418 self.img_pad_dimensions = is_attr_true( element, 'img_pad_dimensions' )
419 self.img_null_flag = is_attr_true( element, 'img_null_flag' )
420 self.img_send_null = is_attr_true( element, 'img_send_null' )
421
422 return
423
424
425 def compatible(self, other):
426 return 1
427
428
429 def is_array(self):
430 return self.is_pointer()
431
432
433 def is_pointer(self):
434 return self.type_expr.is_pointer()
435
436
437 def is_image(self):
438 if self.width:
439 return 1
440 else:
441 return 0
442
443
444 def is_variable_length(self):
445 return len(self.count_parameter_list) or self.counter
446
447
448 def is_64_bit(self):
449 count = self.type_expr.get_element_count()
450 if count:
451 if (self.size() / count) == 8:
452 return 1
453 else:
454 if self.size() == 8:
455 return 1
456
457 return 0
458
459
460 def string(self):
461 return self.type_expr.original_string + " " + self.name
462
463
464 def type_string(self):
465 return self.type_expr.original_string
466
467
468 def get_base_type_string(self):
469 return self.type_expr.get_base_name()
470
471
472 def get_dimensions(self):
473 if not self.width:
474 return [ 0, "0", "0", "0", "0" ]
475
476 dim = 1
477 w = self.width
478 h = "1"
479 d = "1"
480 e = "1"
481
482 if self.height:
483 dim = 2
484 h = self.height
485
486 if self.depth:
487 dim = 3
488 d = self.depth
489
490 if self.extent:
491 dim = 4
492 e = self.extent
493
494 return [ dim, w, h, d, e ]
495
496
497 def get_stack_size(self):
498 return self.type_expr.get_stack_size()
499
500
501 def size(self):
502 if self.is_image():
503 return 0
504 else:
505 return self.type_expr.get_element_size()
506
507
508 def get_element_count(self):
509 c = self.type_expr.get_element_count()
510 if c == 0:
511 return 1
512
513 return c
514
515
516 def size_string(self, use_parens = 1):
517 s = self.size()
518 if self.counter or self.count_parameter_list:
519 list = [ "compsize" ]
520
521 if self.counter and self.count_parameter_list:
522 list.append( self.counter )
523 elif self.counter:
524 list = [ self.counter ]
525
526 if s > 1:
527 list.append( str(s) )
528
529 if len(list) > 1 and use_parens :
530 return "(%s)" % (string.join(list, " * "))
531 else:
532 return string.join(list, " * ")
533
534 elif self.is_image():
535 return "compsize"
536 else:
537 return str(s)
538
539
540 def format_string(self):
541 if self.type_expr.original_string == "GLenum":
542 return "0x%x"
543 else:
544 return self.type_expr.format_string()
545
546
547
548 class gl_function( gl_item ):
549 def __init__(self, element, context):
550 self.context = context
551 self.name = None
552
553 self.entry_points = []
554 self.return_type = "void"
555 self.parameters = []
556 self.offset = -1
557 self.uninitialized = 1
558 self.images = []
559
560 self.process_element( element )
561
562 return
563
564
565 def process_element(self, element):
566 name = element.nsProp( "name", None )
567 alias = element.nsProp( "alias", None )
568
569 self.entry_points.append( name )
570 if alias:
571 true_name = alias
572 else:
573 true_name = name
574
575 # Only try to set the offset when a non-alias
576 # entry-point is being processes.
577
578 offset = element.nsProp( "offset", None )
579 if offset:
580 try:
581 o = int( offset )
582 self.offset = o
583 except Exception, e:
584 self.offset = -1
585
586
587 if not self.name:
588 self.name = true_name
589 elif self.name != true_name:
590 raise RuntimeError("Function true name redefined. Was %s, now %s." % (self.name, true_name))
591
592
593 # There are two possible cases. The first time an entry-point
594 # with data is seen, self.uninitialzied will be 1. On that
595 # pass, we just fill in the data. The next time an
596 # entry-point with data is seen, self.uninitialized will be 0.
597 # On that pass we have to make that the new values match the
598 # valuse from the previous entry-point.
599
600 child = element.children
601 if self.uninitialized:
602 while child:
603 if child.type == "element":
604 if child.name == "return":
605 self.return_type = child.nsProp( "type", None )
606 elif child.name == "param":
607 param = self.context.factory.create_item( "parameter", child, self.context)
608 self.parameters.append( param )
609
610 child = child.next
611 else:
612 parameters = []
613 while child:
614 if child.type == "element":
615 if child.name == "return":
616 return_type = child.nsProp( "type", None )
617 if self.return_type != return_type:
618 raise RuntimeError( "Return type changed in %s. Was %s, now %s." % (name, self.return_type, return_type))
619 elif child.name == "param":
620 param = self.context.factory.create_item( "parameter", child, self.context)
621 parameters.append( param )
622
623 child = child.next
624
625 if len(parameters) != len(self.parameters):
626 raise RuntimeError( "Parameter count mismatch in %s. Was %d, now %d." % (name, len(self.parameters), len(parameters)))
627
628 for j in range(0, len(parameters)):
629 p1 = parameters[j]
630 p2 = self.parameters[j]
631 if not p1.compatible( p2 ):
632 raise RuntimeError( 'Parameter type mismatch in %s. "%s" was "%s", now "%s".' % (name, p2.name, p2.type_expr.original_string, p1.type_expr.original_string))
633
634
635 # This is done becuase we may hit an alias before we
636 # hit the "real" entry. The aliases may not have all
637 # of the parameter information (e.g., counter,
638 # variable_param, etc. fields) required to generate
639 # GLX code.
640
641 if true_name == name:
642 self.parameters = parameters
643
644 for param in self.parameters:
645 if param.is_image():
646 self.images.append( param )
647
648 if true_name == name:
649 for param in self.parameters:
650 if param.is_image():
651 self.images.append( param )
652
653 if element.children:
654 self.uninitialized = 0
655
656 return
657
658
659 def get_images(self):
660 """Return potentially empty list of input images."""
661 return self.images
662
663
664 def parameterIterator(self):
665 return self.parameters.__iter__();
666
667
668 def get_parameter_string(self):
669 list = []
670 for p in self.parameters:
671 list.append( p.string() )
672
673 if len(list) == 0:
674 return "void"
675 else:
676 return string.join(list, ", ")
677
678
679 class gl_item_factory:
680 """Factory to create objects derived from gl_item."""
681
682 def create_item(self, item_name, element, context):
683 if item_name == "function":
684 return gl_function(element, context)
685 if item_name == "type":
686 return gl_type(element, context)
687 elif item_name == "enum":
688 return gl_enum(element, context)
689 elif item_name == "parameter":
690 return gl_parameter(element, context)
691 elif item_name == "api":
692 return gl_api(self)
693 else:
694 return None
695
696
697 class gl_api:
698 def __init__(self, factory):
699 self.functions_by_name = {}
700 self.enums_by_name = {}
701 self.types_by_name = {}
702 self.category_dict = {}
703
704 self.factory = factory
705
706 typeexpr.create_initial_types()
707 return
708
709
710 def process_element(self, doc):
711 element = doc.children
712 while element.type != "element" or element.name != "OpenGLAPI":
713 element = element.next
714
715 if element:
716 self.process_OpenGLAPI(element)
717 return
718
719
720 def process_OpenGLAPI(self, element):
721 child = element.children
722 while child:
723 if child.type == "element":
724 if child.name == "category":
725 self.process_category( child )
726 elif child.name == "OpenGLAPI":
727 self.process_OpenGLAPI( child )
728
729 child = child.next
730
731 return
732
733
734 def process_category(self, cat):
735 cat_name = cat.nsProp( "name", None )
736 cat_number = cat.nsProp( "number", None )
737
738 child = cat.children
739 while child:
740 if child.type == "element":
741 if child.name == "function":
742 func_name = real_function_name( child )
743
744 if self.functions_by_name.has_key( func_name ):
745 func = self.functions_by_name[ func_name ]
746 func.process_element( child )
747 else:
748 func = self.factory.create_item( "function", child, self )
749 self.functions_by_name[ func_name ] = func
750
751 if func_name == child.nsProp("name", None):
752 self.category_dict[ func.name ] = [cat_name, cat_number]
753
754 elif child.name == "enum":
755 enum = self.factory.create_item( "enum", child, self )
756 self.enums_by_name[ enum.name ] = enum
757 elif child.name == "type":
758 t = self.factory.create_item( "type", child, self )
759 self.types_by_name[ "GL" + t.name ] = t
760
761
762 child = child.next
763
764 return
765
766
767 def functionIterateByOffset(self):
768 max_offset = -1
769 for func in self.functions_by_name.itervalues():
770 if func.offset > max_offset:
771 max_offset = func.offset
772
773
774 temp = [None for i in range(0, max_offset + 1)]
775 for func in self.functions_by_name.itervalues():
776 if func.offset != -1:
777 temp[ func.offset ] = func
778
779
780 list = []
781 for i in range(0, max_offset + 1):
782 if temp[i]:
783 list.append(temp[i])
784
785 return list.__iter__();
786
787
788 def functionIterateAll(self):
789 return self.functions_by_name.itervalues()
790
791
792 def enumIterateByName(self):
793 keys = self.enums_by_name.keys()
794 keys.sort()
795
796 list = []
797 for enum in keys:
798 list.append( self.enums_by_name[ enum ] )
799
800 return list.__iter__()
801
802
803 def get_category_for_name( self, name ):
804 if self.category_dict.has_key(name):
805 return self.category_dict[name]
806 else:
807 return ["<unknown category>", None]
808
809
810 def typeIterate(self):
811 return self.types_by_name.itervalues()
812
813
814 def find_type( self, type_name ):
815 if type_name in self.types_by_name:
816 return self.types_by_name[ type_name ].type_expr
817 else:
818 print "Unable to find base type matching \"%s\"." % (type_name)
819 return None