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.marshal_count = element.get("marshal_count")
435        self.count_scale = int(element.get( "count_scale", "1" ))
436
437        elements = (count * self.count_scale)
438        if elements == 1:
439            elements = 0
440
441        #if ts == "GLdouble":
442        #	print '/* stack size -> %s = %u (before)*/' % (self.name, self.type_expr.get_stack_size())
443        #	print '/* # elements = %u */' % (elements)
444        self.type_expr.set_elements( elements )
445        #if ts == "GLdouble":
446        #	print '/* stack size -> %s = %u (after) */' % (self.name, self.type_expr.get_stack_size())
447
448        self.is_client_only = is_attr_true( element, 'client_only' )
449        self.is_counter     = is_attr_true( element, 'counter' )
450        self.is_output      = is_attr_true( element, 'output' )
451
452
453        # Pixel data has special parameters.
454
455        self.width      = element.get('img_width')
456        self.height     = element.get('img_height')
457        self.depth      = element.get('img_depth')
458        self.extent     = element.get('img_extent')
459
460        self.img_xoff   = element.get('img_xoff')
461        self.img_yoff   = element.get('img_yoff')
462        self.img_zoff   = element.get('img_zoff')
463        self.img_woff   = element.get('img_woff')
464
465        self.img_format = element.get('img_format')
466        self.img_type   = element.get('img_type')
467        self.img_target = element.get('img_target')
468
469        self.img_pad_dimensions = is_attr_true( element, 'img_pad_dimensions' )
470        self.img_null_flag      = is_attr_true( element, 'img_null_flag' )
471        self.img_send_null      = is_attr_true( element, 'img_send_null' )
472
473        self.is_padding = is_attr_true( element, 'padding' )
474        return
475
476
477    def compatible(self, other):
478        return 1
479
480
481    def is_array(self):
482        return self.is_pointer()
483
484
485    def is_pointer(self):
486        return self.type_expr.is_pointer()
487
488
489    def is_image(self):
490        if self.width:
491            return 1
492        else:
493            return 0
494
495
496    def is_variable_length(self):
497        return len(self.count_parameter_list) or self.counter or self.marshal_count
498
499
500    def is_64_bit(self):
501        count = self.type_expr.get_element_count()
502        if count:
503            if (self.size() / count) == 8:
504                return 1
505        else:
506            if self.size() == 8:
507                return 1
508
509        return 0
510
511
512    def string(self):
513        return self.type_expr.original_string + " " + self.name
514
515
516    def type_string(self):
517        return self.type_expr.original_string
518
519
520    def get_base_type_string(self):
521        return self.type_expr.get_base_name()
522
523
524    def get_dimensions(self):
525        if not self.width:
526            return [ 0, "0", "0", "0", "0" ]
527
528        dim = 1
529        w = self.width
530        h = "1"
531        d = "1"
532        e = "1"
533
534        if self.height:
535            dim = 2
536            h = self.height
537
538        if self.depth:
539            dim = 3
540            d = self.depth
541
542        if self.extent:
543            dim = 4
544            e = self.extent
545
546        return [ dim, w, h, d, e ]
547
548
549    def get_stack_size(self):
550        return self.type_expr.get_stack_size()
551
552
553    def size(self):
554        if self.is_image():
555            return 0
556        else:
557            return self.type_expr.get_element_size()
558
559
560    def get_element_count(self):
561        c = self.type_expr.get_element_count()
562        if c == 0:
563            return 1
564
565        return c
566
567
568    def size_string(self, use_parens = 1, marshal = 0):
569        base_size_str = ""
570
571        count = self.get_element_count()
572        if count:
573            base_size_str = "%d * " % count
574
575        base_size_str += "sizeof(%s)" % ( self.get_base_type_string() )
576
577        if self.counter or self.count_parameter_list or (self.marshal_count and marshal):
578            list = [ "compsize" ]
579
580            if self.marshal_count and marshal:
581                list = [ self.marshal_count ]
582            elif self.counter and self.count_parameter_list:
583                list.append( self.counter )
584            elif self.counter:
585                list = [ self.counter ]
586
587            if self.size() > 1:
588                list.append( base_size_str )
589
590            if len(list) > 1 and use_parens :
591                return "safe_mul(%s)" % ", ".join(list)
592            else:
593                return " * ".join(list)
594
595        elif self.is_image():
596            return "compsize"
597        else:
598            return base_size_str
599
600
601    def format_string(self):
602        if self.type_expr.original_string == "GLenum":
603            return "0x%x"
604        else:
605            return self.type_expr.format_string()
606
607
608class gl_function( gl_item ):
609    def __init__(self, element, context):
610        self.context = context
611        self.name = None
612
613        self.entry_points = []
614        self.return_type = "void"
615        self.parameters = []
616        self.offset = -1
617        self.initialized = 0
618        self.images = []
619        self.exec_flavor = 'mesa'
620        self.desktop = True
621        self.deprecated = None
622        self.has_no_error_variant = False
623
624        # self.api_map[api] is a decimal value indicating the earliest
625        # version of the given API in which ANY alias for the function
626        # exists.  The map only lists APIs which contain the function
627        # in at least one version.  For example, for the ClipPlanex
628        # function, self.api_map == { 'es1':
629        # Decimal('1.1') }.
630        self.api_map = {}
631
632        self.assign_offset = False
633
634        self.static_entry_points = []
635
636        # Track the parameter string (for the function prototype)
637        # for each entry-point.  This is done because some functions
638        # change their prototype slightly when promoted from extension
639        # to ARB extension to core.  glTexImage3DEXT and glTexImage3D
640        # are good examples of this.  Scripts that need to generate
641        # code for these differing aliases need to real prototype
642        # for each entry-point.  Otherwise, they may generate code
643        # that won't compile.
644
645        self.entry_point_parameters = {}
646
647        self.process_element( element )
648
649        return
650
651
652    def process_element(self, element):
653        name = element.get( "name" )
654        alias = element.get( "alias" )
655
656        if name in static_data.functions:
657            self.static_entry_points.append(name)
658
659        self.entry_points.append( name )
660
661        for api in ('es1', 'es2'):
662            version_str = element.get(api, 'none')
663            assert version_str is not None
664            if version_str != 'none':
665                version_decimal = Decimal(version_str)
666                if api not in self.api_map or \
667                        version_decimal < self.api_map[api]:
668                    self.api_map[api] = version_decimal
669
670        exec_flavor = element.get('exec')
671        if exec_flavor:
672            self.exec_flavor = exec_flavor
673
674        deprecated = element.get('deprecated', 'none')
675        if deprecated != 'none':
676            self.deprecated = Decimal(deprecated)
677
678        if not is_attr_true(element, 'desktop', 'true'):
679            self.desktop = False
680
681        if self.has_no_error_variant or is_attr_true(element, 'no_error'):
682            self.has_no_error_variant = True
683        else:
684            self.has_no_error_variant = False
685
686        if alias:
687            true_name = alias
688        else:
689            true_name = name
690
691            # Only try to set the offset when a non-alias entry-point
692            # is being processed.
693
694            if name in static_data.offsets and static_data.offsets[name] <= static_data.MAX_OFFSETS:
695                self.offset = static_data.offsets[name]
696            elif name in static_data.offsets and static_data.offsets[name] > static_data.MAX_OFFSETS:
697                self.offset = static_data.offsets[name]
698                self.assign_offset = True
699            else:
700                if self.exec_flavor != "skip":
701                    raise RuntimeError("Entry-point %s is missing offset in static_data.py. Add one at the bottom of the list." % (name))
702                self.assign_offset = self.exec_flavor != "skip" or name in static_data.unused_functions
703
704        if not self.name:
705            self.name = true_name
706        elif self.name != true_name:
707            raise RuntimeError("Function true name redefined.  Was %s, now %s." % (self.name, true_name))
708
709
710        # There are two possible cases.  The first time an entry-point
711        # with data is seen, self.initialized will be 0.  On that
712        # pass, we just fill in the data.  The next time an
713        # entry-point with data is seen, self.initialized will be 1.
714        # On that pass we have to make that the new values match the
715        # valuse from the previous entry-point.
716
717        parameters = []
718        return_type = "void"
719        for child in element:
720            if child.tag == "return":
721                return_type = child.get( "type", "void" )
722            elif child.tag == "param":
723                param = self.context.factory.create_parameter(child, self.context)
724                parameters.append( param )
725
726
727        if self.initialized:
728            if self.return_type != return_type:
729                raise RuntimeError( "Return type changed in %s.  Was %s, now %s." % (name, self.return_type, return_type))
730
731            if len(parameters) != len(self.parameters):
732                raise RuntimeError( "Parameter count mismatch in %s.  Was %d, now %d." % (name, len(self.parameters), len(parameters)))
733
734            for j in range(0, len(parameters)):
735                p1 = parameters[j]
736                p2 = self.parameters[j]
737                if not p1.compatible( p2 ):
738                    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))
739
740
741        if true_name == name or not self.initialized:
742            self.return_type = return_type
743            self.parameters = parameters
744
745            for param in self.parameters:
746                if param.is_image():
747                    self.images.append( param )
748
749        if list(element):
750            self.initialized = 1
751            self.entry_point_parameters[name] = parameters
752        else:
753            self.entry_point_parameters[name] = []
754
755        return
756
757    def filter_entry_points(self, entry_point_list):
758        """Filter out entry points not in entry_point_list."""
759        if not self.initialized:
760            raise RuntimeError('%s is not initialized yet' % self.name)
761
762        entry_points = []
763        for ent in self.entry_points:
764            if ent not in entry_point_list:
765                if ent in self.static_entry_points:
766                    self.static_entry_points.remove(ent)
767                self.entry_point_parameters.pop(ent)
768            else:
769                entry_points.append(ent)
770
771        if not entry_points:
772            raise RuntimeError('%s has no entry point after filtering' % self.name)
773
774        self.entry_points = entry_points
775        if self.name not in entry_points:
776            # use the first remaining entry point
777            self.name = entry_points[0]
778            self.parameters = self.entry_point_parameters[entry_points[0]]
779
780    def get_images(self):
781        """Return potentially empty list of input images."""
782        return self.images
783
784
785    def parameterIterator(self, name = None):
786        if name is not None:
787            return iter(self.entry_point_parameters[name]);
788        else:
789            return iter(self.parameters);
790
791
792    def get_parameter_string(self, entrypoint = None):
793        if entrypoint:
794            params = self.entry_point_parameters[ entrypoint ]
795        else:
796            params = self.parameters
797
798        return create_parameter_string( params, 1 )
799
800    def get_called_parameter_string(self):
801        p_string = ""
802        comma = ""
803
804        for p in self.parameterIterator():
805            if p.is_padding:
806                continue
807            p_string = p_string + comma + p.name
808            comma = ", "
809
810        return p_string
811
812
813    def is_abi(self):
814        return (self.offset >= 0 and not self.assign_offset)
815
816    def is_static_entry_point(self, name):
817        return name in self.static_entry_points
818
819    def dispatch_name(self):
820        if self.name in self.static_entry_points:
821            return self.name
822        else:
823            return "_dispatch_stub_%u" % (self.offset)
824
825    def static_name(self, name):
826        if name in self.static_entry_points:
827            return name
828        else:
829            return "_dispatch_stub_%u" % (self.offset)
830
831class gl_item_factory(object):
832    """Factory to create objects derived from gl_item."""
833
834    def create_function(self, element, context):
835        return gl_function(element, context)
836
837    def create_type(self, element, context, category):
838        return gl_type(element, context, category)
839
840    def create_enum(self, element, context, category):
841        return gl_enum(element, context, category)
842
843    def create_parameter(self, element, context):
844        return gl_parameter(element, context)
845
846    def create_api(self):
847        return gl_api(self)
848
849
850class gl_api(object):
851    def __init__(self, factory):
852        self.functions_by_name = OrderedDict()
853        self.enums_by_name = {}
854        self.types_by_name = {}
855
856        self.category_dict = {}
857        self.categories = [{}, {}, {}, {}]
858
859        self.factory = factory
860
861        self.next_offset = 0
862
863        typeexpr.create_initial_types()
864        return
865
866    def parse_file(self, file_name):
867        doc = ET.parse( file_name )
868        self.process_element(file_name, doc)
869
870
871    def process_element(self, file_name, doc):
872        element = doc.getroot()
873        if element.tag == "OpenGLAPI":
874            self.process_OpenGLAPI(file_name, element)
875        return
876
877
878    def process_OpenGLAPI(self, file_name, element):
879        for child in element:
880            if child.tag == "category":
881                self.process_category( child )
882            elif child.tag == "OpenGLAPI":
883                self.process_OpenGLAPI( file_name, child )
884            elif child.tag == '{http://www.w3.org/2001/XInclude}include':
885                href = child.get('href')
886                href = os.path.join(os.path.dirname(file_name), href)
887                self.parse_file(href)
888
889        return
890
891
892    def process_category(self, cat):
893        cat_name = cat.get( "name" )
894        cat_number = cat.get( "number" )
895
896        [cat_type, key] = classify_category(cat_name, cat_number)
897        self.categories[cat_type][key] = [cat_name, cat_number]
898
899        for child in cat:
900            if child.tag == "function":
901                func_name = real_function_name( child )
902
903                temp_name = child.get( "name" )
904                self.category_dict[ temp_name ] = [cat_name, cat_number]
905
906                if func_name in self.functions_by_name:
907                    func = self.functions_by_name[ func_name ]
908                    func.process_element( child )
909                else:
910                    func = self.factory.create_function( child, self )
911                    self.functions_by_name[ func_name ] = func
912
913                if func.offset >= self.next_offset:
914                    self.next_offset = func.offset + 1
915
916
917            elif child.tag == "enum":
918                enum = self.factory.create_enum( child, self, cat_name )
919                self.enums_by_name[ enum.name ] = enum
920            elif child.tag == "type":
921                t = self.factory.create_type( child, self, cat_name )
922                self.types_by_name[ "GL" + t.name ] = t
923
924        return
925
926
927    def functionIterateByCategory(self, cat = None):
928        """Iterate over functions by category.
929
930        If cat is None, all known functions are iterated in category
931        order.  See classify_category for details of the ordering.
932        Within a category, functions are sorted by name.  If cat is
933        not None, then only functions in that category are iterated.
934        """
935        lists = [{}, {}, {}, {}]
936
937        for func in self.functionIterateAll():
938            [cat_name, cat_number] = self.category_dict[func.name]
939
940            if (cat == None) or (cat == cat_name):
941                [func_cat_type, key] = classify_category(cat_name, cat_number)
942
943                if key not in lists[func_cat_type]:
944                    lists[func_cat_type][key] = {}
945
946                lists[func_cat_type][key][func.name] = func
947
948
949        functions = []
950        for func_cat_type in range(0,4):
951            keys = sorted(lists[func_cat_type].keys())
952
953            for key in keys:
954                names = sorted(lists[func_cat_type][key].keys())
955
956                for name in names:
957                    functions.append(lists[func_cat_type][key][name])
958
959        return iter(functions)
960
961
962    def functionIterateByOffset(self):
963        max_offset = -1
964        for func in self.functions_by_name.values():
965            if func.offset > max_offset:
966                max_offset = func.offset
967
968
969        temp = [None for i in range(0, max_offset + 1)]
970        for func in self.functions_by_name.values():
971            if func.offset != -1:
972                temp[ func.offset ] = func
973
974
975        list = []
976        for i in range(0, max_offset + 1):
977            if temp[i]:
978                list.append(temp[i])
979
980        return iter(list);
981
982
983    def functionIterateAll(self):
984        return self.functions_by_name.values()
985
986
987    def enumIterateByName(self):
988        keys = sorted(self.enums_by_name.keys())
989
990        list = []
991        for enum in keys:
992            list.append( self.enums_by_name[ enum ] )
993
994        return iter(list)
995
996
997    def categoryIterate(self):
998        """Iterate over categories.
999
1000        Iterate over all known categories in the order specified by
1001        classify_category.  Each iterated value is a tuple of the
1002        name and number (which may be None) of the category.
1003        """
1004
1005        list = []
1006        for cat_type in range(0,4):
1007            keys = sorted(self.categories[cat_type].keys())
1008
1009            for key in keys:
1010                list.append(self.categories[cat_type][key])
1011
1012        return iter(list)
1013
1014
1015    def get_category_for_name( self, name ):
1016        if name in self.category_dict:
1017            return self.category_dict[name]
1018        else:
1019            return ["<unknown category>", None]
1020
1021
1022    def typeIterate(self):
1023        return self.types_by_name.values()
1024
1025
1026    def find_type( self, type_name ):
1027        if type_name in self.types_by_name:
1028            return self.types_by_name[ type_name ].type_expr
1029        else:
1030            print("Unable to find base type matching \"%s\"." % (type_name))
1031            return None
1032