glapi: add KHR_no_error support to dispatch table generation
[mesa.git] / src / mapi / glapi / gen / gl_XML.py
1
2 # (C) Copyright IBM Corporation 2004, 2005
3 # All Rights Reserved.
4 #
5 # Permission is hereby granted, free of charge, to any person obtaining a
6 # copy of this software and associated documentation files (the "Software"),
7 # to deal in the Software without restriction, including without limitation
8 # on the rights to use, copy, modify, merge, publish, distribute, sub
9 # license, and/or sell copies of the Software, and to permit persons to whom
10 # the Software is furnished to do so, subject to the following conditions:
11 #
12 # The above copyright notice and this permission notice (including the next
13 # paragraph) shall be included in all copies or substantial portions of the
14 # Software.
15 #
16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 # FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
19 # IBM AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 # IN THE SOFTWARE.
23 #
24 # Authors:
25 # Ian Romanick <idr@us.ibm.com>
26
27 from decimal import Decimal
28 import xml.etree.ElementTree as ET
29 import re, sys, string
30 import os.path
31 import typeexpr
32 import static_data
33
34
35 def parse_GL_API( file_name, factory = None ):
36
37 if not factory:
38 factory = gl_item_factory()
39
40 api = factory.create_api()
41 api.parse_file( file_name )
42
43 # After the XML has been processed, we need to go back and assign
44 # dispatch offsets to the functions that request that their offsets
45 # be assigned by the scripts. Typically this means all functions
46 # that are not part of the ABI.
47
48 for func in api.functionIterateByCategory():
49 if func.assign_offset:
50 func.offset = api.next_offset;
51 api.next_offset += 1
52
53 return api
54
55
56 def is_attr_true( element, name, default = "false" ):
57 """Read a name value from an element's attributes.
58
59 The value read from the attribute list must be either 'true' or
60 'false'. If the value is 'false', zero will be returned. If the
61 value is 'true', non-zero will be returned. An exception will be
62 raised for any other value."""
63
64 value = element.get( name, default )
65 if value == "true":
66 return 1
67 elif value == "false":
68 return 0
69 else:
70 raise RuntimeError('Invalid value "%s" for boolean "%s".' % (value, name))
71
72
73 class gl_print_base(object):
74 """Base class of all API pretty-printers.
75
76 In the model-view-controller pattern, this is the view. Any derived
77 class will want to over-ride the printBody, printRealHader, and
78 printRealFooter methods. Some derived classes may want to over-ride
79 printHeader and printFooter, or even Print (though this is unlikely).
80 """
81
82 def __init__(self):
83 # Name of the script that is generating the output file.
84 # Every derived class should set this to the name of its
85 # source file.
86
87 self.name = "a"
88
89
90 # License on the *generated* source file. This may differ
91 # from the license on the script that is generating the file.
92 # Every derived class should set this to some reasonable
93 # value.
94 #
95 # See license.py for an example of a reasonable value.
96
97 self.license = "The license for this file is unspecified."
98
99
100 # The header_tag is the name of the C preprocessor define
101 # used to prevent multiple inclusion. Typically only
102 # generated C header files need this to be set. Setting it
103 # causes code to be generated automatically in printHeader
104 # and printFooter.
105
106 self.header_tag = None
107
108
109 # List of file-private defines that must be undefined at the
110 # end of the file. This can be used in header files to define
111 # names for use in the file, then undefine them at the end of
112 # the header file.
113
114 self.undef_list = []
115 return
116
117
118 def Print(self, api):
119 self.printHeader()
120 self.printBody(api)
121 self.printFooter()
122 return
123
124
125 def printHeader(self):
126 """Print the header associated with all files and call the printRealHeader method."""
127
128 print '/* DO NOT EDIT - This file generated automatically by %s script */' \
129 % (self.name)
130 print ''
131 print '/*'
132 print (' * ' + self.license.replace('\n', '\n * ')).replace(' \n', '\n')
133 print ' */'
134 print ''
135 if self.header_tag:
136 print '#if !defined( %s )' % (self.header_tag)
137 print '# define %s' % (self.header_tag)
138 print ''
139 self.printRealHeader();
140 return
141
142
143 def printFooter(self):
144 """Print the header associated with all files and call the printRealFooter method."""
145
146 self.printRealFooter()
147
148 if self.undef_list:
149 print ''
150 for u in self.undef_list:
151 print "# undef %s" % (u)
152
153 if self.header_tag:
154 print ''
155 print '#endif /* !defined( %s ) */' % (self.header_tag)
156
157
158 def printRealHeader(self):
159 """Print the "real" header 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 printRealFooter(self):
167 """Print the "real" footer for the created file.
168
169 In the base class, this function is empty. All derived
170 classes should over-ride this function."""
171 return
172
173
174 def printPure(self):
175 """Conditionally define `PURE' function attribute.
176
177 Conditionally defines a preprocessor macro `PURE' that wraps
178 GCC's `pure' function attribute. The conditional code can be
179 easilly adapted to other compilers that support a similar
180 feature.
181
182 The name is also added to the file's undef_list.
183 """
184 self.undef_list.append("PURE")
185 print """# if defined(__GNUC__) || (defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590))
186 # define PURE __attribute__((pure))
187 # else
188 # define PURE
189 # endif"""
190 return
191
192
193 def printFastcall(self):
194 """Conditionally define `FASTCALL' function attribute.
195
196 Conditionally defines a preprocessor macro `FASTCALL' that
197 wraps GCC's `fastcall' function attribute. The conditional
198 code can be easilly adapted to other compilers that support a
199 similar feature.
200
201 The name is also added to the file's undef_list.
202 """
203
204 self.undef_list.append("FASTCALL")
205 print """# if defined(__i386__) && defined(__GNUC__) && !defined(__CYGWIN__) && !defined(__MINGW32__)
206 # define FASTCALL __attribute__((fastcall))
207 # else
208 # define FASTCALL
209 # endif"""
210 return
211
212
213 def printVisibility(self, S, s):
214 """Conditionally define visibility function attribute.
215
216 Conditionally defines a preprocessor macro name S that wraps
217 GCC's visibility function attribute. The visibility used is
218 the parameter s. The conditional code can be easilly adapted
219 to other compilers that support a similar feature.
220
221 The name is also added to the file's undef_list.
222 """
223
224 self.undef_list.append(S)
225 print """# if defined(__GNUC__) && !defined(__CYGWIN__) && !defined(__MINGW32__)
226 # define %s __attribute__((visibility("%s")))
227 # else
228 # define %s
229 # endif""" % (S, s, S)
230 return
231
232
233 def printNoinline(self):
234 """Conditionally define `NOINLINE' function attribute.
235
236 Conditionally defines a preprocessor macro `NOINLINE' that
237 wraps GCC's `noinline' function attribute. The conditional
238 code can be easilly adapted to other compilers that support a
239 similar feature.
240
241 The name is also added to the file's undef_list.
242 """
243
244 self.undef_list.append("NOINLINE")
245 print """# if defined(__GNUC__)
246 # define NOINLINE __attribute__((noinline))
247 # else
248 # define NOINLINE
249 # endif"""
250 return
251
252
253 def real_function_name(element):
254 name = element.get( "name" )
255 alias = element.get( "alias" )
256
257 if alias:
258 return alias
259 else:
260 return name
261
262
263 def real_category_name(c):
264 if re.compile("[1-9][0-9]*[.][0-9]+").match(c):
265 return "GL_VERSION_" + c.replace(".", "_")
266 else:
267 return c
268
269
270 def classify_category(name, number):
271 """Based on the category name and number, select a numerical class for it.
272
273 Categories are divided into four classes numbered 0 through 3. The
274 classes are:
275
276 0. Core GL versions, sorted by version number.
277 1. ARB extensions, sorted by extension number.
278 2. Non-ARB extensions, sorted by extension number.
279 3. Un-numbered extensions, sorted by extension name.
280 """
281
282 try:
283 core_version = float(name)
284 except Exception,e:
285 core_version = 0.0
286
287 if core_version > 0.0:
288 cat_type = 0
289 key = name
290 elif name.startswith("GL_ARB_") or name.startswith("GLX_ARB_") or name.startswith("WGL_ARB_"):
291 cat_type = 1
292 key = int(number)
293 else:
294 if number != None:
295 cat_type = 2
296 key = int(number)
297 else:
298 cat_type = 3
299 key = name
300
301
302 return [cat_type, key]
303
304
305 def create_parameter_string(parameters, include_names):
306 """Create a parameter string from a list of gl_parameters."""
307
308 list = []
309 for p in parameters:
310 if p.is_padding:
311 continue
312
313 if include_names:
314 list.append( p.string() )
315 else:
316 list.append( p.type_string() )
317
318 if len(list) == 0: list = ["void"]
319
320 return string.join(list, ", ")
321
322
323 class gl_item(object):
324 def __init__(self, element, context, category):
325 self.context = context
326 self.name = element.get( "name" )
327 self.category = real_category_name( category )
328
329 return
330
331
332 class gl_type( gl_item ):
333 def __init__(self, element, context, category):
334 gl_item.__init__(self, element, context, category)
335 self.size = int( element.get( "size" ), 0 )
336
337 te = typeexpr.type_expression( None )
338 tn = typeexpr.type_node()
339 tn.size = int( element.get( "size" ), 0 )
340 tn.integer = not is_attr_true( element, "float" )
341 tn.unsigned = is_attr_true( element, "unsigned" )
342 tn.pointer = is_attr_true( element, "pointer" )
343 tn.name = "GL" + self.name
344 te.set_base_type_node( tn )
345
346 self.type_expr = te
347 return
348
349
350 def get_type_expression(self):
351 return self.type_expr
352
353
354 class gl_enum( gl_item ):
355 def __init__(self, element, context, category):
356 gl_item.__init__(self, element, context, category)
357 self.value = int( element.get( "value" ), 0 )
358
359 temp = element.get( "count" )
360 if not temp or temp == "?":
361 self.default_count = -1
362 else:
363 try:
364 c = int(temp)
365 except Exception,e:
366 raise RuntimeError('Invalid count value "%s" for enum "%s" in function "%s" when an integer was expected.' % (temp, self.name, n))
367
368 self.default_count = c
369
370 return
371
372
373 def priority(self):
374 """Calculate a 'priority' for this enum name.
375
376 When an enum is looked up by number, there may be many
377 possible names, but only one is the 'prefered' name. The
378 priority is used to select which name is the 'best'.
379
380 Highest precedence is given to core GL name. ARB extension
381 names have the next highest, followed by EXT extension names.
382 Vendor extension names are the lowest.
383 """
384
385 if self.name.endswith( "_BIT" ):
386 bias = 1
387 else:
388 bias = 0
389
390 if self.category.startswith( "GL_VERSION_" ):
391 priority = 0
392 elif self.category.startswith( "GL_ARB_" ):
393 priority = 2
394 elif self.category.startswith( "GL_EXT_" ):
395 priority = 4
396 else:
397 priority = 6
398
399 return priority + bias
400
401
402
403 class gl_parameter(object):
404 def __init__(self, element, context):
405 self.name = element.get( "name" )
406
407 ts = element.get( "type" )
408 self.type_expr = typeexpr.type_expression( ts, context )
409
410 temp = element.get( "variable_param" )
411 if temp:
412 self.count_parameter_list = temp.split( ' ' )
413 else:
414 self.count_parameter_list = []
415
416 # The count tag can be either a numeric string or the name of
417 # a variable. If it is the name of a variable, the int(c)
418 # statement will throw an exception, and the except block will
419 # take over.
420
421 c = element.get( "count" )
422 try:
423 count = int(c)
424 self.count = count
425 self.counter = None
426 except Exception,e:
427 count = 1
428 self.count = 0
429 self.counter = c
430
431 self.count_scale = int(element.get( "count_scale", "1" ))
432
433 elements = (count * self.count_scale)
434 if elements == 1:
435 elements = 0
436
437 #if ts == "GLdouble":
438 # print '/* stack size -> %s = %u (before)*/' % (self.name, self.type_expr.get_stack_size())
439 # print '/* # elements = %u */' % (elements)
440 self.type_expr.set_elements( elements )
441 #if ts == "GLdouble":
442 # print '/* stack size -> %s = %u (after) */' % (self.name, self.type_expr.get_stack_size())
443
444 self.is_client_only = is_attr_true( element, 'client_only' )
445 self.is_counter = is_attr_true( element, 'counter' )
446 self.is_output = is_attr_true( element, 'output' )
447
448
449 # Pixel data has special parameters.
450
451 self.width = element.get('img_width')
452 self.height = element.get('img_height')
453 self.depth = element.get('img_depth')
454 self.extent = element.get('img_extent')
455
456 self.img_xoff = element.get('img_xoff')
457 self.img_yoff = element.get('img_yoff')
458 self.img_zoff = element.get('img_zoff')
459 self.img_woff = element.get('img_woff')
460
461 self.img_format = element.get('img_format')
462 self.img_type = element.get('img_type')
463 self.img_target = element.get('img_target')
464
465 self.img_pad_dimensions = is_attr_true( element, 'img_pad_dimensions' )
466 self.img_null_flag = is_attr_true( element, 'img_null_flag' )
467 self.img_send_null = is_attr_true( element, 'img_send_null' )
468
469 self.is_padding = is_attr_true( element, 'padding' )
470 return
471
472
473 def compatible(self, other):
474 return 1
475
476
477 def is_array(self):
478 return self.is_pointer()
479
480
481 def is_pointer(self):
482 return self.type_expr.is_pointer()
483
484
485 def is_image(self):
486 if self.width:
487 return 1
488 else:
489 return 0
490
491
492 def is_variable_length(self):
493 return len(self.count_parameter_list) or self.counter
494
495
496 def is_64_bit(self):
497 count = self.type_expr.get_element_count()
498 if count:
499 if (self.size() / count) == 8:
500 return 1
501 else:
502 if self.size() == 8:
503 return 1
504
505 return 0
506
507
508 def string(self):
509 return self.type_expr.original_string + " " + self.name
510
511
512 def type_string(self):
513 return self.type_expr.original_string
514
515
516 def get_base_type_string(self):
517 return self.type_expr.get_base_name()
518
519
520 def get_dimensions(self):
521 if not self.width:
522 return [ 0, "0", "0", "0", "0" ]
523
524 dim = 1
525 w = self.width
526 h = "1"
527 d = "1"
528 e = "1"
529
530 if self.height:
531 dim = 2
532 h = self.height
533
534 if self.depth:
535 dim = 3
536 d = self.depth
537
538 if self.extent:
539 dim = 4
540 e = self.extent
541
542 return [ dim, w, h, d, e ]
543
544
545 def get_stack_size(self):
546 return self.type_expr.get_stack_size()
547
548
549 def size(self):
550 if self.is_image():
551 return 0
552 else:
553 return self.type_expr.get_element_size()
554
555
556 def get_element_count(self):
557 c = self.type_expr.get_element_count()
558 if c == 0:
559 return 1
560
561 return c
562
563
564 def size_string(self, use_parens = 1):
565 s = self.size()
566 if self.counter or self.count_parameter_list:
567 list = [ "compsize" ]
568
569 if self.counter and self.count_parameter_list:
570 list.append( self.counter )
571 elif self.counter:
572 list = [ self.counter ]
573
574 if s > 1:
575 list.append( str(s) )
576
577 if len(list) > 1 and use_parens :
578 return "safe_mul(%s)" % (string.join(list, ", "))
579 else:
580 return string.join(list, " * ")
581
582 elif self.is_image():
583 return "compsize"
584 else:
585 return str(s)
586
587
588 def format_string(self):
589 if self.type_expr.original_string == "GLenum":
590 return "0x%x"
591 else:
592 return self.type_expr.format_string()
593
594
595 class gl_function( gl_item ):
596 def __init__(self, element, context):
597 self.context = context
598 self.name = None
599
600 self.entry_points = []
601 self.return_type = "void"
602 self.parameters = []
603 self.offset = -1
604 self.initialized = 0
605 self.images = []
606 self.exec_flavor = 'mesa'
607 self.desktop = True
608 self.deprecated = None
609 self.has_no_error_variant = False
610
611 # self.entry_point_api_map[name][api] is a decimal value
612 # indicating the earliest version of the given API in which
613 # each entry point exists. Every entry point is included in
614 # the first level of the map; the second level of the map only
615 # lists APIs which contain the entry point in at least one
616 # version. For example,
617 # self.entry_point_api_map['ClipPlanex'] == { 'es1':
618 # Decimal('1.1') }.
619 self.entry_point_api_map = {}
620
621 # self.api_map[api] is a decimal value indicating the earliest
622 # version of the given API in which ANY alias for the function
623 # exists. The map only lists APIs which contain the function
624 # in at least one version. For example, for the ClipPlanex
625 # function, self.entry_point_api_map == { 'es1':
626 # Decimal('1.1') }.
627 self.api_map = {}
628
629 self.assign_offset = False
630
631 self.static_entry_points = []
632
633 # Track the parameter string (for the function prototype)
634 # for each entry-point. This is done because some functions
635 # change their prototype slightly when promoted from extension
636 # to ARB extension to core. glTexImage3DEXT and glTexImage3D
637 # are good examples of this. Scripts that need to generate
638 # code for these differing aliases need to real prototype
639 # for each entry-point. Otherwise, they may generate code
640 # that won't compile.
641
642 self.entry_point_parameters = {}
643
644 self.process_element( element )
645
646 return
647
648
649 def process_element(self, element):
650 name = element.get( "name" )
651 alias = element.get( "alias" )
652
653 if name in static_data.functions:
654 self.static_entry_points.append(name)
655
656 self.entry_points.append( name )
657
658 self.entry_point_api_map[name] = {}
659 for api in ('es1', 'es2'):
660 version_str = element.get(api, 'none')
661 assert version_str is not None
662 if version_str != 'none':
663 version_decimal = Decimal(version_str)
664 self.entry_point_api_map[name][api] = version_decimal
665 if api not in self.api_map or \
666 version_decimal < self.api_map[api]:
667 self.api_map[api] = version_decimal
668
669 exec_flavor = element.get('exec')
670 if exec_flavor:
671 self.exec_flavor = exec_flavor
672
673 deprecated = element.get('deprecated', 'none')
674 if deprecated != 'none':
675 self.deprecated = Decimal(deprecated)
676
677 if not is_attr_true(element, 'desktop', 'true'):
678 self.desktop = False
679
680 if self.has_no_error_variant or is_attr_true(element, 'no_error'):
681 self.has_no_error_variant = True
682 else:
683 self.has_no_error_variant = False
684
685 if alias:
686 true_name = alias
687 else:
688 true_name = name
689
690 # Only try to set the offset when a non-alias entry-point
691 # is being processed.
692
693 if name in static_data.offsets:
694 self.offset = static_data.offsets[name]
695 else:
696 self.offset = -1
697 self.assign_offset = self.exec_flavor != "skip" or name in static_data.unused_functions
698
699 if not self.name:
700 self.name = true_name
701 elif self.name != true_name:
702 raise RuntimeError("Function true name redefined. Was %s, now %s." % (self.name, true_name))
703
704
705 # There are two possible cases. The first time an entry-point
706 # with data is seen, self.initialized will be 0. On that
707 # pass, we just fill in the data. The next time an
708 # entry-point with data is seen, self.initialized will be 1.
709 # On that pass we have to make that the new values match the
710 # valuse from the previous entry-point.
711
712 parameters = []
713 return_type = "void"
714 for child in element.getchildren():
715 if child.tag == "return":
716 return_type = child.get( "type", "void" )
717 elif child.tag == "param":
718 param = self.context.factory.create_parameter(child, self.context)
719 parameters.append( param )
720
721
722 if self.initialized:
723 if self.return_type != return_type:
724 raise RuntimeError( "Return type changed in %s. Was %s, now %s." % (name, self.return_type, return_type))
725
726 if len(parameters) != len(self.parameters):
727 raise RuntimeError( "Parameter count mismatch in %s. Was %d, now %d." % (name, len(self.parameters), len(parameters)))
728
729 for j in range(0, len(parameters)):
730 p1 = parameters[j]
731 p2 = self.parameters[j]
732 if not p1.compatible( p2 ):
733 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))
734
735
736 if true_name == name or not self.initialized:
737 self.return_type = return_type
738 self.parameters = parameters
739
740 for param in self.parameters:
741 if param.is_image():
742 self.images.append( param )
743
744 if element.getchildren():
745 self.initialized = 1
746 self.entry_point_parameters[name] = parameters
747 else:
748 self.entry_point_parameters[name] = []
749
750 return
751
752 def filter_entry_points(self, entry_point_list):
753 """Filter out entry points not in entry_point_list."""
754 if not self.initialized:
755 raise RuntimeError('%s is not initialized yet' % self.name)
756
757 entry_points = []
758 for ent in self.entry_points:
759 if ent not in entry_point_list:
760 if ent in self.static_entry_points:
761 self.static_entry_points.remove(ent)
762 self.entry_point_parameters.pop(ent)
763 else:
764 entry_points.append(ent)
765
766 if not entry_points:
767 raise RuntimeError('%s has no entry point after filtering' % self.name)
768
769 self.entry_points = entry_points
770 if self.name not in entry_points:
771 # use the first remaining entry point
772 self.name = entry_points[0]
773 self.parameters = self.entry_point_parameters[entry_points[0]]
774
775 def get_images(self):
776 """Return potentially empty list of input images."""
777 return self.images
778
779
780 def parameterIterator(self, name = None):
781 if name is not None:
782 return self.entry_point_parameters[name].__iter__();
783 else:
784 return self.parameters.__iter__();
785
786
787 def get_parameter_string(self, entrypoint = None):
788 if entrypoint:
789 params = self.entry_point_parameters[ entrypoint ]
790 else:
791 params = self.parameters
792
793 return create_parameter_string( params, 1 )
794
795 def get_called_parameter_string(self):
796 p_string = ""
797 comma = ""
798
799 for p in self.parameterIterator():
800 if p.is_padding:
801 continue
802 p_string = p_string + comma + p.name
803 comma = ", "
804
805 return p_string
806
807
808 def is_abi(self):
809 return (self.offset >= 0 and not self.assign_offset)
810
811 def is_static_entry_point(self, name):
812 return name in self.static_entry_points
813
814 def dispatch_name(self):
815 if self.name in self.static_entry_points:
816 return self.name
817 else:
818 return "_dispatch_stub_%u" % (self.offset)
819
820 def static_name(self, name):
821 if name in self.static_entry_points:
822 return name
823 else:
824 return "_dispatch_stub_%u" % (self.offset)
825
826 def entry_points_for_api_version(self, api, version = None):
827 """Return a list of the entry point names for this function
828 which are supported in the given API (and optionally, version).
829
830 Use the decimal.Decimal type to precisely express non-integer
831 versions.
832 """
833 result = []
834 for entry_point, api_to_ver in self.entry_point_api_map.iteritems():
835 if api not in api_to_ver:
836 continue
837 if version is not None and version < api_to_ver[api]:
838 continue
839 result.append(entry_point)
840 return result
841
842
843 class gl_item_factory(object):
844 """Factory to create objects derived from gl_item."""
845
846 def create_function(self, element, context):
847 return gl_function(element, context)
848
849 def create_type(self, element, context, category):
850 return gl_type(element, context, category)
851
852 def create_enum(self, element, context, category):
853 return gl_enum(element, context, category)
854
855 def create_parameter(self, element, context):
856 return gl_parameter(element, context)
857
858 def create_api(self):
859 return gl_api(self)
860
861
862 class gl_api(object):
863 def __init__(self, factory):
864 self.functions_by_name = {}
865 self.enums_by_name = {}
866 self.types_by_name = {}
867
868 self.category_dict = {}
869 self.categories = [{}, {}, {}, {}]
870
871 self.factory = factory
872
873 self.next_offset = 0
874
875 typeexpr.create_initial_types()
876 return
877
878 def filter_functions(self, entry_point_list):
879 """Filter out entry points not in entry_point_list."""
880 functions_by_name = {}
881 for func in self.functions_by_name.itervalues():
882 entry_points = [ent for ent in func.entry_points if ent in entry_point_list]
883 if entry_points:
884 func.filter_entry_points(entry_points)
885 functions_by_name[func.name] = func
886
887 self.functions_by_name = functions_by_name
888
889 def filter_functions_by_api(self, api, version = None):
890 """Filter out entry points not in the given API (or
891 optionally, not in the given version of the given API).
892 """
893 functions_by_name = {}
894 for func in self.functions_by_name.itervalues():
895 entry_points = func.entry_points_for_api_version(api, version)
896 if entry_points:
897 func.filter_entry_points(entry_points)
898 functions_by_name[func.name] = func
899
900 self.functions_by_name = functions_by_name
901
902
903 def parse_file(self, file_name):
904 doc = ET.parse( file_name )
905 self.process_element(file_name, doc)
906
907
908 def process_element(self, file_name, doc):
909 element = doc.getroot()
910 if element.tag == "OpenGLAPI":
911 self.process_OpenGLAPI(file_name, element)
912 return
913
914
915 def process_OpenGLAPI(self, file_name, element):
916 for child in element.getchildren():
917 if child.tag == "category":
918 self.process_category( child )
919 elif child.tag == "OpenGLAPI":
920 self.process_OpenGLAPI( file_name, child )
921 elif child.tag == '{http://www.w3.org/2001/XInclude}include':
922 href = child.get('href')
923 href = os.path.join(os.path.dirname(file_name), href)
924 self.parse_file(href)
925
926 return
927
928
929 def process_category(self, cat):
930 cat_name = cat.get( "name" )
931 cat_number = cat.get( "number" )
932
933 [cat_type, key] = classify_category(cat_name, cat_number)
934 self.categories[cat_type][key] = [cat_name, cat_number]
935
936 for child in cat.getchildren():
937 if child.tag == "function":
938 func_name = real_function_name( child )
939
940 temp_name = child.get( "name" )
941 self.category_dict[ temp_name ] = [cat_name, cat_number]
942
943 if self.functions_by_name.has_key( func_name ):
944 func = self.functions_by_name[ func_name ]
945 func.process_element( child )
946 else:
947 func = self.factory.create_function( child, self )
948 self.functions_by_name[ func_name ] = func
949
950 if func.offset >= self.next_offset:
951 self.next_offset = func.offset + 1
952
953
954 elif child.tag == "enum":
955 enum = self.factory.create_enum( child, self, cat_name )
956 self.enums_by_name[ enum.name ] = enum
957 elif child.tag == "type":
958 t = self.factory.create_type( child, self, cat_name )
959 self.types_by_name[ "GL" + t.name ] = t
960
961 return
962
963
964 def functionIterateByCategory(self, cat = None):
965 """Iterate over functions by category.
966
967 If cat is None, all known functions are iterated in category
968 order. See classify_category for details of the ordering.
969 Within a category, functions are sorted by name. If cat is
970 not None, then only functions in that category are iterated.
971 """
972 lists = [{}, {}, {}, {}]
973
974 for func in self.functionIterateAll():
975 [cat_name, cat_number] = self.category_dict[func.name]
976
977 if (cat == None) or (cat == cat_name):
978 [func_cat_type, key] = classify_category(cat_name, cat_number)
979
980 if not lists[func_cat_type].has_key(key):
981 lists[func_cat_type][key] = {}
982
983 lists[func_cat_type][key][func.name] = func
984
985
986 functions = []
987 for func_cat_type in range(0,4):
988 keys = lists[func_cat_type].keys()
989 keys.sort()
990
991 for key in keys:
992 names = lists[func_cat_type][key].keys()
993 names.sort()
994
995 for name in names:
996 functions.append(lists[func_cat_type][key][name])
997
998 return functions.__iter__()
999
1000
1001 def functionIterateByOffset(self):
1002 max_offset = -1
1003 for func in self.functions_by_name.itervalues():
1004 if func.offset > max_offset:
1005 max_offset = func.offset
1006
1007
1008 temp = [None for i in range(0, max_offset + 1)]
1009 for func in self.functions_by_name.itervalues():
1010 if func.offset != -1:
1011 temp[ func.offset ] = func
1012
1013
1014 list = []
1015 for i in range(0, max_offset + 1):
1016 if temp[i]:
1017 list.append(temp[i])
1018
1019 return list.__iter__();
1020
1021
1022 def functionIterateAll(self):
1023 return self.functions_by_name.itervalues()
1024
1025
1026 def enumIterateByName(self):
1027 keys = self.enums_by_name.keys()
1028 keys.sort()
1029
1030 list = []
1031 for enum in keys:
1032 list.append( self.enums_by_name[ enum ] )
1033
1034 return list.__iter__()
1035
1036
1037 def categoryIterate(self):
1038 """Iterate over categories.
1039
1040 Iterate over all known categories in the order specified by
1041 classify_category. Each iterated value is a tuple of the
1042 name and number (which may be None) of the category.
1043 """
1044
1045 list = []
1046 for cat_type in range(0,4):
1047 keys = self.categories[cat_type].keys()
1048 keys.sort()
1049
1050 for key in keys:
1051 list.append(self.categories[cat_type][key])
1052
1053 return list.__iter__()
1054
1055
1056 def get_category_for_name( self, name ):
1057 if self.category_dict.has_key(name):
1058 return self.category_dict[name]
1059 else:
1060 return ["<unknown category>", None]
1061
1062
1063 def typeIterate(self):
1064 return self.types_by_name.itervalues()
1065
1066
1067 def find_type( self, type_name ):
1068 if type_name in self.types_by_name:
1069 return self.types_by_name[ type_name ].type_expr
1070 else:
1071 print "Unable to find base type matching \"%s\"." % (type_name)
1072 return None