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