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 and func.offset < 0:
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.api_map[api] is a decimal value indicating the earliest
615        # version of the given API in which ANY alias for the function
616        # exists.  The map only lists APIs which contain the function
617        # in at least one version.  For example, for the ClipPlanex
618        # function, self.api_map == { 'es1':
619        # Decimal('1.1') }.
620        self.api_map = {}
621
622        self.assign_offset = False
623
624        self.static_entry_points = []
625
626        # Track the parameter string (for the function prototype)
627        # for each entry-point.  This is done because some functions
628        # change their prototype slightly when promoted from extension
629        # to ARB extension to core.  glTexImage3DEXT and glTexImage3D
630        # are good examples of this.  Scripts that need to generate
631        # code for these differing aliases need to real prototype
632        # for each entry-point.  Otherwise, they may generate code
633        # that won't compile.
634
635        self.entry_point_parameters = {}
636
637        self.process_element( element )
638
639        return
640
641
642    def process_element(self, element):
643        name = element.get( "name" )
644        alias = element.get( "alias" )
645
646        if name in static_data.functions:
647            self.static_entry_points.append(name)
648
649        self.entry_points.append( name )
650
651        for api in ('es1', 'es2'):
652            version_str = element.get(api, 'none')
653            assert version_str is not None
654            if version_str != 'none':
655                version_decimal = Decimal(version_str)
656                if api not in self.api_map or \
657                        version_decimal < self.api_map[api]:
658                    self.api_map[api] = version_decimal
659
660        exec_flavor = element.get('exec')
661        if exec_flavor:
662            self.exec_flavor = exec_flavor
663
664        deprecated = element.get('deprecated', 'none')
665        if deprecated != 'none':
666            self.deprecated = Decimal(deprecated)
667
668        if not is_attr_true(element, 'desktop', 'true'):
669            self.desktop = False
670
671        if self.has_no_error_variant or is_attr_true(element, 'no_error'):
672            self.has_no_error_variant = True
673        else:
674            self.has_no_error_variant = False
675
676        if alias:
677            true_name = alias
678        else:
679            true_name = name
680
681            # Only try to set the offset when a non-alias entry-point
682            # is being processed.
683
684            if name in static_data.offsets and static_data.offsets[name] <= static_data.MAX_OFFSETS:
685                self.offset = static_data.offsets[name]
686            elif name in static_data.offsets and static_data.offsets[name] > static_data.MAX_OFFSETS:
687                self.offset = static_data.offsets[name]
688                self.assign_offset = True
689            else:
690                if self.exec_flavor != "skip":
691                    raise RuntimeError("Entry-point %s is missing offset in static_data.py. Add one at the bottom of the list." % (name))
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 iter(self.entry_point_parameters[name]);
778        else:
779            return iter(self.parameters);
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
821class gl_item_factory(object):
822    """Factory to create objects derived from gl_item."""
823
824    def create_function(self, element, context):
825        return gl_function(element, context)
826
827    def create_type(self, element, context, category):
828        return gl_type(element, context, category)
829
830    def create_enum(self, element, context, category):
831        return gl_enum(element, context, category)
832
833    def create_parameter(self, element, context):
834        return gl_parameter(element, context)
835
836    def create_api(self):
837        return gl_api(self)
838
839
840class gl_api(object):
841    def __init__(self, factory):
842        self.functions_by_name = OrderedDict()
843        self.enums_by_name = {}
844        self.types_by_name = {}
845
846        self.category_dict = {}
847        self.categories = [{}, {}, {}, {}]
848
849        self.factory = factory
850
851        self.next_offset = 0
852
853        typeexpr.create_initial_types()
854        return
855
856    def parse_file(self, file_name):
857        doc = ET.parse( file_name )
858        self.process_element(file_name, doc)
859
860
861    def process_element(self, file_name, doc):
862        element = doc.getroot()
863        if element.tag == "OpenGLAPI":
864            self.process_OpenGLAPI(file_name, element)
865        return
866
867
868    def process_OpenGLAPI(self, file_name, element):
869        for child in element.getchildren():
870            if child.tag == "category":
871                self.process_category( child )
872            elif child.tag == "OpenGLAPI":
873                self.process_OpenGLAPI( file_name, child )
874            elif child.tag == '{http://www.w3.org/2001/XInclude}include':
875                href = child.get('href')
876                href = os.path.join(os.path.dirname(file_name), href)
877                self.parse_file(href)
878
879        return
880
881
882    def process_category(self, cat):
883        cat_name = cat.get( "name" )
884        cat_number = cat.get( "number" )
885
886        [cat_type, key] = classify_category(cat_name, cat_number)
887        self.categories[cat_type][key] = [cat_name, cat_number]
888
889        for child in cat.getchildren():
890            if child.tag == "function":
891                func_name = real_function_name( child )
892
893                temp_name = child.get( "name" )
894                self.category_dict[ temp_name ] = [cat_name, cat_number]
895
896                if func_name in self.functions_by_name:
897                    func = self.functions_by_name[ func_name ]
898                    func.process_element( child )
899                else:
900                    func = self.factory.create_function( child, self )
901                    self.functions_by_name[ func_name ] = func
902
903                if func.offset >= self.next_offset:
904                    self.next_offset = func.offset + 1
905
906
907            elif child.tag == "enum":
908                enum = self.factory.create_enum( child, self, cat_name )
909                self.enums_by_name[ enum.name ] = enum
910            elif child.tag == "type":
911                t = self.factory.create_type( child, self, cat_name )
912                self.types_by_name[ "GL" + t.name ] = t
913
914        return
915
916
917    def functionIterateByCategory(self, cat = None):
918        """Iterate over functions by category.
919
920        If cat is None, all known functions are iterated in category
921        order.  See classify_category for details of the ordering.
922        Within a category, functions are sorted by name.  If cat is
923        not None, then only functions in that category are iterated.
924        """
925        lists = [{}, {}, {}, {}]
926
927        for func in self.functionIterateAll():
928            [cat_name, cat_number] = self.category_dict[func.name]
929
930            if (cat == None) or (cat == cat_name):
931                [func_cat_type, key] = classify_category(cat_name, cat_number)
932
933                if key not in lists[func_cat_type]:
934                    lists[func_cat_type][key] = {}
935
936                lists[func_cat_type][key][func.name] = func
937
938
939        functions = []
940        for func_cat_type in range(0,4):
941            keys = sorted(lists[func_cat_type].keys())
942
943            for key in keys:
944                names = sorted(lists[func_cat_type][key].keys())
945
946                for name in names:
947                    functions.append(lists[func_cat_type][key][name])
948
949        return iter(functions)
950
951
952    def functionIterateByOffset(self):
953        max_offset = -1
954        for func in self.functions_by_name.values():
955            if func.offset > max_offset:
956                max_offset = func.offset
957
958
959        temp = [None for i in range(0, max_offset + 1)]
960        for func in self.functions_by_name.values():
961            if func.offset != -1:
962                temp[ func.offset ] = func
963
964
965        list = []
966        for i in range(0, max_offset + 1):
967            if temp[i]:
968                list.append(temp[i])
969
970        return iter(list);
971
972
973    def functionIterateAll(self):
974        return self.functions_by_name.values()
975
976
977    def enumIterateByName(self):
978        keys = sorted(self.enums_by_name.keys())
979
980        list = []
981        for enum in keys:
982            list.append( self.enums_by_name[ enum ] )
983
984        return iter(list)
985
986
987    def categoryIterate(self):
988        """Iterate over categories.
989
990        Iterate over all known categories in the order specified by
991        classify_category.  Each iterated value is a tuple of the
992        name and number (which may be None) of the category.
993        """
994
995        list = []
996        for cat_type in range(0,4):
997            keys = sorted(self.categories[cat_type].keys())
998
999            for key in keys:
1000                list.append(self.categories[cat_type][key])
1001
1002        return iter(list)
1003
1004
1005    def get_category_for_name( self, name ):
1006        if name in self.category_dict:
1007            return self.category_dict[name]
1008        else:
1009            return ["<unknown category>", None]
1010
1011
1012    def typeIterate(self):
1013        return self.types_by_name.values()
1014
1015
1016    def find_type( self, type_name ):
1017        if type_name in self.types_by_name:
1018            return self.types_by_name[ type_name ].type_expr
1019        else:
1020            print("Unable to find base type matching \"%s\"." % (type_name))
1021            return None
1022