651bd7369b34581f777fb062fb44523be06252c6
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__)
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)
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 printHaveAlias(self
):
246 """Conditionally define `HAVE_ALIAS'.
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.
252 The name is also added to the file's undef_list.
255 self
.undef_list
.append("HAVE_ALIAS")
256 print """# if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
262 def real_function_name(element
):
263 name
= element
.nsProp( "name", None )
264 alias
= element
.nsProp( "alias", None )
272 def real_category_name(c
):
273 if re
.compile("[1-9][0-9]*[.][0-9]+").match(c
):
274 return "GL_VERSION_" + c
.replace(".", "_")
279 def create_parameter_string(parameters
, include_names
):
280 """Create a parameter string from a list of gl_parameters."""
285 list.append( p
.string() )
287 list.append( p
.type_string() )
289 if len(list) == 0: list = ["void"]
291 return string
.join(list, ", ")
295 def __init__(self
, element
, context
):
296 self
.context
= context
297 self
.name
= element
.nsProp( "name", None )
298 self
.category
= real_category_name( element
.parent
.nsProp( "name", None ) )
302 class gl_type( gl_item
):
303 def __init__(self
, element
, context
):
304 gl_item
.__init
__(self
, element
, context
)
305 self
.size
= int( element
.nsProp( "size", None ), 0 )
307 te
= typeexpr
.type_expression( None )
308 tn
= typeexpr
.type_node()
309 tn
.size
= int( element
.nsProp( "size", None ), 0 )
310 tn
.integer
= not is_attr_true( element
, "float" )
311 tn
.unsigned
= is_attr_true( element
, "unsigned" )
312 tn
.name
= "GL" + self
.name
313 te
.set_base_type_node( tn
)
319 def get_type_expression(self
):
320 return self
.type_expr
323 class gl_enum( gl_item
):
324 def __init__(self
, element
, context
):
325 gl_item
.__init
__(self
, element
, context
)
326 self
.value
= int( element
.nsProp( "value", None ), 0 )
328 temp
= element
.nsProp( "count", None )
329 if not temp
or temp
== "?":
330 self
.default_count
= -1
335 raise RuntimeError('Invalid count value "%s" for enum "%s" in function "%s" when an integer was expected.' % (temp
, self
.name
, n
))
337 self
.default_count
= c
343 """Calculate a 'priority' for this enum name.
345 When an enum is looked up by number, there may be many
346 possible names, but only one is the 'prefered' name. The
347 priority is used to select which name is the 'best'.
349 Highest precedence is given to core GL name. ARB extension
350 names have the next highest, followed by EXT extension names.
351 Vendor extension names are the lowest.
354 if self
.name
.endswith( "_BIT" ):
359 if self
.category
.startswith( "GL_VERSION_" ):
361 elif self
.category
.startswith( "GL_ARB_" ):
363 elif self
.category
.startswith( "GL_EXT_" ):
368 return priority
+ bias
373 def __init__(self
, element
, context
):
374 self
.name
= element
.nsProp( "name", None )
376 ts
= element
.nsProp( "type", None )
377 self
.type_expr
= typeexpr
.type_expression( ts
, context
)
379 temp
= element
.nsProp( "variable_param", None )
381 self
.count_parameter_list
= temp
.split( ' ' )
383 self
.count_parameter_list
= []
385 # The count tag can be either a numeric string or the name of
386 # a variable. If it is the name of a variable, the int(c)
387 # statement will throw an exception, and the except block will
390 c
= element
.nsProp( "count", None )
400 self
.count_scale
= int(element
.nsProp( "count_scale", None ))
402 elements
= (count
* self
.count_scale
)
406 #if ts == "GLdouble":
407 # print '/* stack size -> %s = %u (before)*/' % (self.name, self.type_expr.get_stack_size())
408 # print '/* # elements = %u */' % (elements)
409 self
.type_expr
.set_elements( elements
)
410 #if ts == "GLdouble":
411 # print '/* stack size -> %s = %u (after) */' % (self.name, self.type_expr.get_stack_size())
413 self
.is_counter
= is_attr_true( element
, 'counter' )
414 self
.is_output
= is_attr_true( element
, 'output' )
417 # Pixel data has special parameters.
419 self
.width
= element
.nsProp('img_width', None)
420 self
.height
= element
.nsProp('img_height', None)
421 self
.depth
= element
.nsProp('img_depth', None)
422 self
.extent
= element
.nsProp('img_extent', None)
424 self
.img_xoff
= element
.nsProp('img_xoff', None)
425 self
.img_yoff
= element
.nsProp('img_yoff', None)
426 self
.img_zoff
= element
.nsProp('img_zoff', None)
427 self
.img_woff
= element
.nsProp('img_woff', None)
429 self
.img_format
= element
.nsProp('img_format', None)
430 self
.img_type
= element
.nsProp('img_type', None)
431 self
.img_target
= element
.nsProp('img_target', None)
433 self
.img_pad_dimensions
= is_attr_true( element
, 'img_pad_dimensions' )
434 self
.img_null_flag
= is_attr_true( element
, 'img_null_flag' )
435 self
.img_send_null
= is_attr_true( element
, 'img_send_null' )
440 def compatible(self
, other
):
445 return self
.is_pointer()
448 def is_pointer(self
):
449 return self
.type_expr
.is_pointer()
459 def is_variable_length(self
):
460 return len(self
.count_parameter_list
) or self
.counter
464 count
= self
.type_expr
.get_element_count()
466 if (self
.size() / count
) == 8:
476 return self
.type_expr
.original_string
+ " " + self
.name
479 def type_string(self
):
480 return self
.type_expr
.original_string
483 def get_base_type_string(self
):
484 return self
.type_expr
.get_base_name()
487 def get_dimensions(self
):
489 return [ 0, "0", "0", "0", "0" ]
509 return [ dim
, w
, h
, d
, e
]
512 def get_stack_size(self
):
513 return self
.type_expr
.get_stack_size()
520 return self
.type_expr
.get_element_size()
523 def get_element_count(self
):
524 c
= self
.type_expr
.get_element_count()
531 def size_string(self
, use_parens
= 1):
533 if self
.counter
or self
.count_parameter_list
:
534 list = [ "compsize" ]
536 if self
.counter
and self
.count_parameter_list
:
537 list.append( self
.counter
)
539 list = [ self
.counter
]
542 list.append( str(s
) )
544 if len(list) > 1 and use_parens
:
545 return "(%s)" % (string
.join(list, " * "))
547 return string
.join(list, " * ")
549 elif self
.is_image():
555 def format_string(self
):
556 if self
.type_expr
.original_string
== "GLenum":
559 return self
.type_expr
.format_string()
563 class gl_function( gl_item
):
564 def __init__(self
, element
, context
):
565 self
.context
= context
568 self
.entry_points
= []
569 self
.return_type
= "void"
575 # Track the parameter string (for the function prototype)
576 # for each entry-point. This is done because some functions
577 # change their prototype slightly when promoted from extension
578 # to ARB extension to core. glTexImage3DEXT and glTexImage3D
579 # are good examples of this. Scripts that need to generate
580 # code for these differing aliases need to real prototype
581 # for each entry-point. Otherwise, they may generate code
582 # that won't compile.
584 self
.parameter_strings
= {}
586 self
.process_element( element
)
591 def process_element(self
, element
):
592 name
= element
.nsProp( "name", None )
593 alias
= element
.nsProp( "alias", None )
595 self
.entry_points
.append( name
)
601 # Only try to set the offset when a non-alias
602 # entry-point is being processes.
604 offset
= element
.nsProp( "offset", None )
614 self
.name
= true_name
615 elif self
.name
!= true_name
:
616 raise RuntimeError("Function true name redefined. Was %s, now %s." % (self
.name
, true_name
))
619 # There are two possible cases. The first time an entry-point
620 # with data is seen, self.initialized will be 0. On that
621 # pass, we just fill in the data. The next time an
622 # entry-point with data is seen, self.initialized will be 1.
623 # On that pass we have to make that the new values match the
624 # valuse from the previous entry-point.
628 child
= element
.children
630 if child
.type == "element":
631 if child
.name
== "return":
632 return_type
= child
.nsProp( "type", None )
633 elif child
.name
== "param":
634 param
= self
.context
.factory
.create_item( "parameter", child
, self
.context
)
635 parameters
.append( param
)
641 if self
.return_type
!= return_type
:
642 raise RuntimeError( "Return type changed in %s. Was %s, now %s." % (name
, self
.return_type
, return_type
))
644 if len(parameters
) != len(self
.parameters
):
645 raise RuntimeError( "Parameter count mismatch in %s. Was %d, now %d." % (name
, len(self
.parameters
), len(parameters
)))
647 for j
in range(0, len(parameters
)):
649 p2
= self
.parameters
[j
]
650 if not p1
.compatible( p2
):
651 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
))
654 if true_name
== name
or not self
.initialized
:
655 self
.return_type
= return_type
656 self
.parameters
= parameters
658 for param
in self
.parameters
:
660 self
.images
.append( param
)
664 self
.parameter_strings
[name
] = create_parameter_string(parameters
, 1)
666 self
.parameter_strings
[name
] = None
671 def get_images(self
):
672 """Return potentially empty list of input images."""
676 def parameterIterator(self
):
677 return self
.parameters
.__iter
__();
680 def get_parameter_string(self
, entrypoint
= None):
682 s
= self
.parameter_strings
[ entrypoint
]
686 return create_parameter_string( self
.parameters
, 1 )
689 class gl_item_factory
:
690 """Factory to create objects derived from gl_item."""
692 def create_item(self
, item_name
, element
, context
):
693 if item_name
== "function":
694 return gl_function(element
, context
)
695 if item_name
== "type":
696 return gl_type(element
, context
)
697 elif item_name
== "enum":
698 return gl_enum(element
, context
)
699 elif item_name
== "parameter":
700 return gl_parameter(element
, context
)
701 elif item_name
== "api":
708 def __init__(self
, factory
):
709 self
.functions_by_name
= {}
710 self
.enums_by_name
= {}
711 self
.types_by_name
= {}
712 self
.category_dict
= {}
714 self
.factory
= factory
716 typeexpr
.create_initial_types()
720 def process_element(self
, doc
):
721 element
= doc
.children
722 while element
.type != "element" or element
.name
!= "OpenGLAPI":
723 element
= element
.next
726 self
.process_OpenGLAPI(element
)
730 def process_OpenGLAPI(self
, element
):
731 child
= element
.children
733 if child
.type == "element":
734 if child
.name
== "category":
735 self
.process_category( child
)
736 elif child
.name
== "OpenGLAPI":
737 self
.process_OpenGLAPI( child
)
744 def process_category(self
, cat
):
745 cat_name
= cat
.nsProp( "name", None )
746 cat_number
= cat
.nsProp( "number", None )
750 if child
.type == "element":
751 if child
.name
== "function":
752 func_name
= real_function_name( child
)
754 temp_name
= child
.nsProp( "name", None )
755 self
.category_dict
[ temp_name
] = [cat_name
, cat_number
]
757 if self
.functions_by_name
.has_key( func_name
):
758 func
= self
.functions_by_name
[ func_name
]
759 func
.process_element( child
)
761 func
= self
.factory
.create_item( "function", child
, self
)
762 self
.functions_by_name
[ func_name
] = func
765 elif child
.name
== "enum":
766 enum
= self
.factory
.create_item( "enum", child
, self
)
767 self
.enums_by_name
[ enum
.name
] = enum
768 elif child
.name
== "type":
769 t
= self
.factory
.create_item( "type", child
, self
)
770 self
.types_by_name
[ "GL" + t
.name
] = t
778 def functionIterateByOffset(self
):
780 for func
in self
.functions_by_name
.itervalues():
781 if func
.offset
> max_offset
:
782 max_offset
= func
.offset
785 temp
= [None for i
in range(0, max_offset
+ 1)]
786 for func
in self
.functions_by_name
.itervalues():
787 if func
.offset
!= -1:
788 temp
[ func
.offset
] = func
792 for i
in range(0, max_offset
+ 1):
796 return list.__iter
__();
799 def functionIterateAll(self
):
800 return self
.functions_by_name
.itervalues()
803 def enumIterateByName(self
):
804 keys
= self
.enums_by_name
.keys()
809 list.append( self
.enums_by_name
[ enum
] )
811 return list.__iter
__()
814 def get_category_for_name( self
, name
):
815 if self
.category_dict
.has_key(name
):
816 return self
.category_dict
[name
]
818 return ["<unknown category>", None]
821 def typeIterate(self
):
822 return self
.types_by_name
.itervalues()
825 def find_type( self
, type_name
):
826 if type_name
in self
.types_by_name
:
827 return self
.types_by_name
[ type_name
].type_expr
829 print "Unable to find base type matching \"%s\"." % (type_name
)