e4de4cacdd681aa1b6fcd06f33a23d1d61ef9d12
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 )
273 def __init__(self
, element
, context
):
274 self
.context
= context
276 self
.name
= element
.nsProp( "name", None )
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(".", "_")
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 )
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
)
304 def get_type_expression(self
):
305 return self
.type_expr
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 )
313 temp
= element
.nsProp( "count", None )
314 if not temp
or temp
== "?":
315 self
.default_count
= -1
320 raise RuntimeError('Invalid count value "%s" for enum "%s" in function "%s" when an integer was expected.' % (temp
, self
.name
, n
))
322 self
.default_count
= c
328 """Calculate a 'priority' for this enum name.
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'.
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.
339 if self
.name
.endswith( "_BIT" ):
344 if self
.category
.startswith( "GL_VERSION_" ):
346 elif self
.category
.startswith( "GL_ARB_" ):
348 elif self
.category
.startswith( "GL_EXT_" ):
353 return priority
+ bias
358 def __init__(self
, element
, context
):
359 self
.name
= element
.nsProp( "name", None )
361 ts
= element
.nsProp( "type", None )
362 self
.type_expr
= typeexpr
.type_expression( ts
, context
)
364 temp
= element
.nsProp( "variable_param", None )
366 self
.count_parameter_list
= temp
.split( ' ' )
368 self
.count_parameter_list
= []
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
375 c
= element
.nsProp( "count", None )
385 self
.count_scale
= int(element
.nsProp( "count_scale", None ))
387 elements
= (count
* self
.count_scale
)
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())
398 self
.is_counter
= is_attr_true( element
, 'counter' )
399 self
.is_output
= is_attr_true( element
, 'output' )
402 # Pixel data has special parameters.
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)
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)
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)
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' )
425 def compatible(self
, other
):
430 return self
.is_pointer()
433 def is_pointer(self
):
434 return self
.type_expr
.is_pointer()
444 def is_variable_length(self
):
445 return len(self
.count_parameter_list
) or self
.counter
449 count
= self
.type_expr
.get_element_count()
451 if (self
.size() / count
) == 8:
461 return self
.type_expr
.original_string
+ " " + self
.name
464 def type_string(self
):
465 return self
.type_expr
.original_string
468 def get_base_type_string(self
):
469 return self
.type_expr
.get_base_name()
472 def get_dimensions(self
):
474 return [ 0, "0", "0", "0", "0" ]
494 return [ dim
, w
, h
, d
, e
]
497 def get_stack_size(self
):
498 return self
.type_expr
.get_stack_size()
505 return self
.type_expr
.get_element_size()
508 def get_element_count(self
):
509 c
= self
.type_expr
.get_element_count()
516 def size_string(self
, use_parens
= 1):
518 if self
.counter
or self
.count_parameter_list
:
519 list = [ "compsize" ]
521 if self
.counter
and self
.count_parameter_list
:
522 list.append( self
.counter
)
524 list = [ self
.counter
]
527 list.append( str(s
) )
529 if len(list) > 1 and use_parens
:
530 return "(%s)" % (string
.join(list, " * "))
532 return string
.join(list, " * ")
534 elif self
.is_image():
540 def format_string(self
):
541 if self
.type_expr
.original_string
== "GLenum":
544 return self
.type_expr
.format_string()
548 class gl_function( gl_item
):
549 def __init__(self
, element
, context
):
550 self
.context
= context
553 self
.entry_points
= []
554 self
.return_type
= "void"
557 self
.uninitialized
= 1
560 self
.process_element( element
)
565 def process_element(self
, element
):
566 name
= element
.nsProp( "name", None )
567 alias
= element
.nsProp( "alias", None )
569 self
.entry_points
.append( name
)
575 # Only try to set the offset when a non-alias
576 # entry-point is being processes.
578 offset
= element
.nsProp( "offset", None )
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
))
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.
600 child
= element
.children
601 if self
.uninitialized
:
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
)
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
)
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
)))
628 for j
in range(0, len(parameters
)):
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
))
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
641 if true_name
== name
:
642 self
.parameters
= parameters
644 for param
in self
.parameters
:
646 self
.images
.append( param
)
648 if true_name
== name
:
649 for param
in self
.parameters
:
651 self
.images
.append( param
)
654 self
.uninitialized
= 0
659 def get_images(self
):
660 """Return potentially empty list of input images."""
664 def parameterIterator(self
):
665 return self
.parameters
.__iter
__();
668 def get_parameter_string(self
):
670 for p
in self
.parameters
:
671 list.append( p
.string() )
676 return string
.join(list, ", ")
679 class gl_item_factory
:
680 """Factory to create objects derived from gl_item."""
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":
698 def __init__(self
, factory
):
699 self
.functions_by_name
= {}
700 self
.enums_by_name
= {}
701 self
.types_by_name
= {}
702 self
.category_dict
= {}
704 self
.factory
= factory
706 typeexpr
.create_initial_types()
710 def process_element(self
, doc
):
711 element
= doc
.children
712 while element
.type != "element" or element
.name
!= "OpenGLAPI":
713 element
= element
.next
716 self
.process_OpenGLAPI(element
)
720 def process_OpenGLAPI(self
, element
):
721 child
= element
.children
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
)
734 def process_category(self
, cat
):
735 cat_name
= cat
.nsProp( "name", None )
736 cat_number
= cat
.nsProp( "number", None )
740 if child
.type == "element":
741 if child
.name
== "function":
742 func_name
= real_function_name( child
)
744 if self
.functions_by_name
.has_key( func_name
):
745 func
= self
.functions_by_name
[ func_name
]
746 func
.process_element( child
)
748 func
= self
.factory
.create_item( "function", child
, self
)
749 self
.functions_by_name
[ func_name
] = func
751 if func_name
== child
.nsProp("name", None):
752 self
.category_dict
[ func
.name
] = [cat_name
, cat_number
]
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
767 def functionIterateByOffset(self
):
769 for func
in self
.functions_by_name
.itervalues():
770 if func
.offset
> max_offset
:
771 max_offset
= func
.offset
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
781 for i
in range(0, max_offset
+ 1):
785 return list.__iter
__();
788 def functionIterateAll(self
):
789 return self
.functions_by_name
.itervalues()
792 def enumIterateByName(self
):
793 keys
= self
.enums_by_name
.keys()
798 list.append( self
.enums_by_name
[ enum
] )
800 return list.__iter
__()
803 def get_category_for_name( self
, name
):
804 if self
.category_dict
.has_key(name
):
805 return self
.category_dict
[name
]
807 return ["<unknown category>", None]
810 def typeIterate(self
):
811 return self
.types_by_name
.itervalues()
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
818 print "Unable to find base type matching \"%s\"." % (type_name
)