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