3 # (C) Copyright IBM Corporation 2004, 2005
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:
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
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
26 # Ian Romanick <idr@us.ibm.com>
29 import re
, sys
, string
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()
38 factory
= gl_item_factory()
40 api
= factory
.create_item( "api", None, None )
41 api
.process_element( doc
)
48 def is_attr_true( element
, name
):
49 """Read a name value from an element's attributes.
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."""
56 value
= element
.nsProp( name
, None )
59 elif value
== "false":
62 raise RuntimeError('Invalid value "%s" for boolean "%s".' % (value
, name
))
66 """Base class of all API pretty-printers.
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).
75 # Name of the script that is generating the output file.
76 # Every derived class should set this to the name of its
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
87 # See license.py for an example of a reasonable value.
89 self
.license
= "The license for this file is unspecified."
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
98 self
.header_tag
= None
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
110 def Print(self
, api
):
117 def printHeader(self
):
118 """Print the header associated with all files and call the printRealHeader method."""
120 print '/* DO NOT EDIT - This file generated automatically by %s script */' \
124 print ' * ' + self
.license
.replace('\n', '\n * ')
128 print '#if !defined( %s )' % (self
.header_tag
)
129 print '# define %s' % (self
.header_tag
)
131 self
.printRealHeader();
135 def printFooter(self
):
136 """Print the header associated with all files and call the printRealFooter method."""
138 self
.printRealFooter()
142 for u
in self
.undef_list
:
143 print "# undef %s" % (u
)
147 print '#endif /* !defined( %s ) */' % (self
.header_tag
)
150 def printRealHeader(self
):
151 """Print the "real" header for the created file.
153 In the base class, this function is empty. All derived
154 classes should over-ride this function."""
158 def printRealFooter(self
):
159 """Print the "real" footer for the created file.
161 In the base class, this function is empty. All derived
162 classes should over-ride this function."""
167 """Conditionally define `PURE' function attribute.
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
174 The name is also added to the file's undef_list.
176 self
.undef_list
.append("PURE")
177 print """# if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96)
178 # define PURE __attribute__((pure))
185 def printFastcall(self
):
186 """Conditionally define `FASTCALL' function attribute.
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
193 The name is also added to the file's undef_list.
196 self
.undef_list
.append("FASTCALL")
197 print """# if defined(__i386__) && defined(__GNUC__) && !defined(__CYGWIN__) && !defined(__MINGW32__)
198 # define FASTCALL __attribute__((fastcall))
205 def printVisibility(self
, S
, s
):
206 """Conditionally define visibility function attribute.
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.
213 The name is also added to the file's undef_list.
216 self
.undef_list
.append(S
)
217 print """# if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)) && defined(__ELF__)
218 # define %s __attribute__((visibility("%s")))
221 # endif""" % (S
, s
, S
)
225 def printNoinline(self
):
226 """Conditionally define `NOINLINE' function attribute.
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
233 The name is also added to the file's undef_list.
236 self
.undef_list
.append("NOINLINE")
237 print """# if defined(__GNUC__)
238 # define NOINLINE __attribute__((noinline))
245 def real_function_name(element
):
246 name
= element
.nsProp( "name", None )
247 alias
= element
.nsProp( "alias", None )
255 def real_category_name(c
):
256 if re
.compile("[1-9][0-9]*[.][0-9]+").match(c
):
257 return "GL_VERSION_" + c
.replace(".", "_")
262 def create_parameter_string(parameters
, include_names
):
263 """Create a parameter string from a list of gl_parameters."""
268 list.append( p
.string() )
270 list.append( p
.type_string() )
272 if len(list) == 0: list = ["void"]
274 return string
.join(list, ", ")
278 def __init__(self
, element
, context
):
279 self
.context
= context
280 self
.name
= element
.nsProp( "name", None )
281 self
.category
= real_category_name( element
.parent
.nsProp( "name", None ) )
285 class gl_type( gl_item
):
286 def __init__(self
, element
, context
):
287 gl_item
.__init
__(self
, element
, context
)
288 self
.size
= int( element
.nsProp( "size", None ), 0 )
290 te
= typeexpr
.type_expression( None )
291 tn
= typeexpr
.type_node()
292 tn
.size
= int( element
.nsProp( "size", None ), 0 )
293 tn
.integer
= not is_attr_true( element
, "float" )
294 tn
.unsigned
= is_attr_true( element
, "unsigned" )
295 tn
.name
= "GL" + self
.name
296 te
.set_base_type_node( tn
)
302 def get_type_expression(self
):
303 return self
.type_expr
306 class gl_enum( gl_item
):
307 def __init__(self
, element
, context
):
308 gl_item
.__init
__(self
, element
, context
)
309 self
.value
= int( element
.nsProp( "value", None ), 0 )
311 temp
= element
.nsProp( "count", None )
312 if not temp
or temp
== "?":
313 self
.default_count
= -1
318 raise RuntimeError('Invalid count value "%s" for enum "%s" in function "%s" when an integer was expected.' % (temp
, self
.name
, n
))
320 self
.default_count
= c
326 """Calculate a 'priority' for this enum name.
328 When an enum is looked up by number, there may be many
329 possible names, but only one is the 'prefered' name. The
330 priority is used to select which name is the 'best'.
332 Highest precedence is given to core GL name. ARB extension
333 names have the next highest, followed by EXT extension names.
334 Vendor extension names are the lowest.
337 if self
.name
.endswith( "_BIT" ):
342 if self
.category
.startswith( "GL_VERSION_" ):
344 elif self
.category
.startswith( "GL_ARB_" ):
346 elif self
.category
.startswith( "GL_EXT_" ):
351 return priority
+ bias
356 def __init__(self
, element
, context
):
357 self
.name
= element
.nsProp( "name", None )
359 ts
= element
.nsProp( "type", None )
360 self
.type_expr
= typeexpr
.type_expression( ts
, context
)
362 temp
= element
.nsProp( "variable_param", None )
364 self
.count_parameter_list
= temp
.split( ' ' )
366 self
.count_parameter_list
= []
368 # The count tag can be either a numeric string or the name of
369 # a variable. If it is the name of a variable, the int(c)
370 # statement will throw an exception, and the except block will
373 c
= element
.nsProp( "count", None )
383 self
.count_scale
= int(element
.nsProp( "count_scale", None ))
385 elements
= (count
* self
.count_scale
)
389 #if ts == "GLdouble":
390 # print '/* stack size -> %s = %u (before)*/' % (self.name, self.type_expr.get_stack_size())
391 # print '/* # elements = %u */' % (elements)
392 self
.type_expr
.set_elements( elements
)
393 #if ts == "GLdouble":
394 # print '/* stack size -> %s = %u (after) */' % (self.name, self.type_expr.get_stack_size())
396 self
.is_counter
= is_attr_true( element
, 'counter' )
397 self
.is_output
= is_attr_true( element
, 'output' )
400 # Pixel data has special parameters.
402 self
.width
= element
.nsProp('img_width', None)
403 self
.height
= element
.nsProp('img_height', None)
404 self
.depth
= element
.nsProp('img_depth', None)
405 self
.extent
= element
.nsProp('img_extent', None)
407 self
.img_xoff
= element
.nsProp('img_xoff', None)
408 self
.img_yoff
= element
.nsProp('img_yoff', None)
409 self
.img_zoff
= element
.nsProp('img_zoff', None)
410 self
.img_woff
= element
.nsProp('img_woff', None)
412 self
.img_format
= element
.nsProp('img_format', None)
413 self
.img_type
= element
.nsProp('img_type', None)
414 self
.img_target
= element
.nsProp('img_target', None)
416 self
.img_pad_dimensions
= is_attr_true( element
, 'img_pad_dimensions' )
417 self
.img_null_flag
= is_attr_true( element
, 'img_null_flag' )
418 self
.img_send_null
= is_attr_true( element
, 'img_send_null' )
423 def compatible(self
, other
):
428 return self
.is_pointer()
431 def is_pointer(self
):
432 return self
.type_expr
.is_pointer()
442 def is_variable_length(self
):
443 return len(self
.count_parameter_list
) or self
.counter
447 count
= self
.type_expr
.get_element_count()
449 if (self
.size() / count
) == 8:
459 return self
.type_expr
.original_string
+ " " + self
.name
462 def type_string(self
):
463 return self
.type_expr
.original_string
466 def get_base_type_string(self
):
467 return self
.type_expr
.get_base_name()
470 def get_dimensions(self
):
472 return [ 0, "0", "0", "0", "0" ]
492 return [ dim
, w
, h
, d
, e
]
495 def get_stack_size(self
):
496 return self
.type_expr
.get_stack_size()
503 return self
.type_expr
.get_element_size()
506 def get_element_count(self
):
507 c
= self
.type_expr
.get_element_count()
514 def size_string(self
, use_parens
= 1):
516 if self
.counter
or self
.count_parameter_list
:
517 list = [ "compsize" ]
519 if self
.counter
and self
.count_parameter_list
:
520 list.append( self
.counter
)
522 list = [ self
.counter
]
525 list.append( str(s
) )
527 if len(list) > 1 and use_parens
:
528 return "(%s)" % (string
.join(list, " * "))
530 return string
.join(list, " * ")
532 elif self
.is_image():
538 def format_string(self
):
539 if self
.type_expr
.original_string
== "GLenum":
542 return self
.type_expr
.format_string()
546 class gl_function( gl_item
):
547 def __init__(self
, element
, context
):
548 self
.context
= context
551 self
.entry_points
= []
552 self
.return_type
= "void"
558 # Track the parameter string (for the function prototype)
559 # for each entry-point. This is done because some functions
560 # change their prototype slightly when promoted from extension
561 # to ARB extension to core. glTexImage3DEXT and glTexImage3D
562 # are good examples of this. Scripts that need to generate
563 # code for these differing aliases need to real prototype
564 # for each entry-point. Otherwise, they may generate code
565 # that won't compile.
567 self
.parameter_strings
= {}
569 self
.process_element( element
)
574 def process_element(self
, element
):
575 name
= element
.nsProp( "name", None )
576 alias
= element
.nsProp( "alias", None )
578 self
.entry_points
.append( name
)
584 # Only try to set the offset when a non-alias
585 # entry-point is being processes.
587 offset
= element
.nsProp( "offset", None )
597 self
.name
= true_name
598 elif self
.name
!= true_name
:
599 raise RuntimeError("Function true name redefined. Was %s, now %s." % (self
.name
, true_name
))
602 # There are two possible cases. The first time an entry-point
603 # with data is seen, self.initialized will be 0. On that
604 # pass, we just fill in the data. The next time an
605 # entry-point with data is seen, self.initialized will be 1.
606 # On that pass we have to make that the new values match the
607 # valuse from the previous entry-point.
611 child
= element
.children
613 if child
.type == "element":
614 if child
.name
== "return":
615 return_type
= child
.nsProp( "type", None )
616 elif child
.name
== "param":
617 param
= self
.context
.factory
.create_item( "parameter", child
, self
.context
)
618 parameters
.append( param
)
624 if self
.return_type
!= return_type
:
625 raise RuntimeError( "Return type changed in %s. Was %s, now %s." % (name
, self
.return_type
, return_type
))
627 if len(parameters
) != len(self
.parameters
):
628 raise RuntimeError( "Parameter count mismatch in %s. Was %d, now %d." % (name
, len(self
.parameters
), len(parameters
)))
630 for j
in range(0, len(parameters
)):
632 p2
= self
.parameters
[j
]
633 if not p1
.compatible( p2
):
634 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
))
637 if true_name
== name
or not self
.initialized
:
638 self
.return_type
= return_type
639 self
.parameters
= parameters
641 for param
in self
.parameters
:
643 self
.images
.append( param
)
647 self
.parameter_strings
[name
] = create_parameter_string(parameters
, 1)
649 self
.parameter_strings
[name
] = None
654 def get_images(self
):
655 """Return potentially empty list of input images."""
659 def parameterIterator(self
):
660 return self
.parameters
.__iter
__();
663 def get_parameter_string(self
, entrypoint
= None):
665 s
= self
.parameter_strings
[ entrypoint
]
669 return create_parameter_string( self
.parameters
, 1 )
672 class gl_item_factory
:
673 """Factory to create objects derived from gl_item."""
675 def create_item(self
, item_name
, element
, context
):
676 if item_name
== "function":
677 return gl_function(element
, context
)
678 if item_name
== "type":
679 return gl_type(element
, context
)
680 elif item_name
== "enum":
681 return gl_enum(element
, context
)
682 elif item_name
== "parameter":
683 return gl_parameter(element
, context
)
684 elif item_name
== "api":
691 def __init__(self
, factory
):
692 self
.functions_by_name
= {}
693 self
.enums_by_name
= {}
694 self
.types_by_name
= {}
695 self
.category_dict
= {}
697 self
.factory
= factory
699 typeexpr
.create_initial_types()
703 def process_element(self
, doc
):
704 element
= doc
.children
705 while element
.type != "element" or element
.name
!= "OpenGLAPI":
706 element
= element
.next
709 self
.process_OpenGLAPI(element
)
713 def process_OpenGLAPI(self
, element
):
714 child
= element
.children
716 if child
.type == "element":
717 if child
.name
== "category":
718 self
.process_category( child
)
719 elif child
.name
== "OpenGLAPI":
720 self
.process_OpenGLAPI( child
)
727 def process_category(self
, cat
):
728 cat_name
= cat
.nsProp( "name", None )
729 cat_number
= cat
.nsProp( "number", None )
733 if child
.type == "element":
734 if child
.name
== "function":
735 func_name
= real_function_name( child
)
737 temp_name
= child
.nsProp( "name", None )
738 self
.category_dict
[ temp_name
] = [cat_name
, cat_number
]
740 if self
.functions_by_name
.has_key( func_name
):
741 func
= self
.functions_by_name
[ func_name
]
742 func
.process_element( child
)
744 func
= self
.factory
.create_item( "function", child
, self
)
745 self
.functions_by_name
[ func_name
] = func
748 elif child
.name
== "enum":
749 enum
= self
.factory
.create_item( "enum", child
, self
)
750 self
.enums_by_name
[ enum
.name
] = enum
751 elif child
.name
== "type":
752 t
= self
.factory
.create_item( "type", child
, self
)
753 self
.types_by_name
[ "GL" + t
.name
] = t
761 def functionIterateByOffset(self
):
763 for func
in self
.functions_by_name
.itervalues():
764 if func
.offset
> max_offset
:
765 max_offset
= func
.offset
768 temp
= [None for i
in range(0, max_offset
+ 1)]
769 for func
in self
.functions_by_name
.itervalues():
770 if func
.offset
!= -1:
771 temp
[ func
.offset
] = func
775 for i
in range(0, max_offset
+ 1):
779 return list.__iter
__();
782 def functionIterateAll(self
):
783 return self
.functions_by_name
.itervalues()
786 def enumIterateByName(self
):
787 keys
= self
.enums_by_name
.keys()
792 list.append( self
.enums_by_name
[ enum
] )
794 return list.__iter
__()
797 def get_category_for_name( self
, name
):
798 if self
.category_dict
.has_key(name
):
799 return self
.category_dict
[name
]
801 return ["<unknown category>", None]
804 def typeIterate(self
):
805 return self
.types_by_name
.itervalues()
808 def find_type( self
, type_name
):
809 if type_name
in self
.types_by_name
:
810 return self
.types_by_name
[ type_name
].type_expr
812 print "Unable to find base type matching \"%s\"." % (type_name
)