• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 decimal import Decimal
28import xml.etree.ElementTree as ET
29import re, sys, string
30import os.path
31import typeexpr
32import static_data
33
34
35def 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
56def 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
73class 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
253def 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
263def 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
270def 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
305def 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
323class 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
332class 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
354class 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
403class 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
595class 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
843class 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
862class 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
1073