• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1
2# (C) Copyright IBM Corporation 2004, 2005
3# All Rights Reserved.
4#
5# Permission is hereby granted, free of charge, to any person obtaining a
6# copy of this software and associated documentation files (the "Software"),
7# to deal in the Software without restriction, including without limitation
8# on the rights to use, copy, modify, merge, publish, distribute, sub
9# license, and/or sell copies of the Software, and to permit persons to whom
10# the Software is furnished to do so, subject to the following conditions:
11#
12# The above copyright notice and this permission notice (including the next
13# paragraph) shall be included in all copies or substantial portions of the
14# Software.
15#
16# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
19# IBM AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22# IN THE SOFTWARE.
23#
24# Authors:
25#    Ian Romanick <idr@us.ibm.com>
26
27from collections import OrderedDict
28from decimal import Decimal
29import xml.etree.ElementTree as ET
30import re, sys
31import os.path
32import typeexpr
33import static_data
34
35
36def parse_GL_API(file_name, factory=None, pointer_size=0):
37
38    if not factory:
39        factory = gl_item_factory()
40
41    api = factory.create_api(pointer_size)
42    api.parse_file(file_name)
43    return api
44
45
46def is_attr_true( element, name, default = "false" ):
47    """Read a name value from an element's attributes.
48
49    The value read from the attribute list must be either 'true' or
50    'false'.  If the value is 'false', zero will be returned.  If the
51    value is 'true', non-zero will be returned.  An exception will be
52    raised for any other value."""
53
54    value = element.get( name, default )
55    if value == "true":
56        return 1
57    elif value == "false":
58        return 0
59    else:
60        raise RuntimeError('Invalid value "%s" for boolean "%s".' % (value, name))
61
62
63class gl_print_base(object):
64    """Base class of all API pretty-printers.
65
66    In the model-view-controller pattern, this is the view.  Any derived
67    class will want to over-ride the printBody, printRealHader, and
68    printRealFooter methods.  Some derived classes may want to over-ride
69    printHeader and printFooter, or even Print (though this is unlikely).
70    """
71
72    def __init__(self):
73        # Name of the script that is generating the output file.
74        # Every derived class should set this to the name of its
75        # source file.
76
77        self.name = "a"
78
79
80        # License on the *generated* source file.  This may differ
81        # from the license on the script that is generating the file.
82        # Every derived class should set this to some reasonable
83        # value.
84        #
85        # See license.py for an example of a reasonable value.
86
87        self.license = "The license for this file is unspecified."
88
89
90        # The header_tag is the name of the C preprocessor define
91        # used to prevent multiple inclusion.  Typically only
92        # generated C header files need this to be set.  Setting it
93        # causes code to be generated automatically in printHeader
94        # and printFooter.
95
96        self.header_tag = None
97
98
99        # List of file-private defines that must be undefined at the
100        # end of the file.  This can be used in header files to define
101        # names for use in the file, then undefine them at the end of
102        # the header file.
103
104        self.undef_list = []
105        return
106
107
108    def Print(self, api):
109        self.printHeader()
110        self.printBody(api)
111        self.printFooter()
112        return
113
114
115    def printHeader(self):
116        """Print the header associated with all files and call the printRealHeader method."""
117
118        print('/* DO NOT EDIT - This file generated automatically by %s script */' \
119                % (self.name))
120        print('')
121        print('/*')
122        print((' * ' + self.license.replace('\n', '\n * ')).replace(' \n', '\n'))
123        print(' */')
124        print('')
125        if self.header_tag:
126            print('#if !defined( %s )' % (self.header_tag))
127            print('#  define %s' % (self.header_tag))
128            print('')
129        self.printRealHeader();
130        return
131
132
133    def printFooter(self):
134        """Print the header associated with all files and call the printRealFooter method."""
135
136        self.printRealFooter()
137
138        if self.undef_list:
139            print('')
140            for u in self.undef_list:
141                print("#  undef %s" % (u))
142
143        if self.header_tag:
144            print('')
145            print('#endif /* !defined( %s ) */' % (self.header_tag))
146
147
148    def printRealHeader(self):
149        """Print the "real" header for the created file.
150
151        In the base class, this function is empty.  All derived
152        classes should over-ride this function."""
153        return
154
155
156    def printRealFooter(self):
157        """Print the "real" footer for the created file.
158
159        In the base class, this function is empty.  All derived
160        classes should over-ride this function."""
161        return
162
163
164    def printPure(self):
165        """Conditionally define `PURE' function attribute.
166
167        Conditionally defines a preprocessor macro `PURE' that wraps
168        GCC's `pure' function attribute.  The conditional code can be
169        easilly adapted to other compilers that support a similar
170        feature.
171
172        The name is also added to the file's undef_list.
173        """
174        self.undef_list.append("PURE")
175        print("""#  if defined(__GNUC__) || (defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590))
176#    define PURE __attribute__((pure))
177#  else
178#    define PURE
179#  endif""")
180        return
181
182
183    def printFastcall(self):
184        """Conditionally define `FASTCALL' function attribute.
185
186        Conditionally defines a preprocessor macro `FASTCALL' that
187        wraps GCC's `fastcall' function attribute.  The conditional
188        code can be easilly adapted to other compilers that support a
189        similar feature.
190
191        The name is also added to the file's undef_list.
192        """
193
194        self.undef_list.append("FASTCALL")
195        print("""#  if defined(__i386__) && defined(__GNUC__) && !defined(__CYGWIN__) && !defined(__MINGW32__)
196#    define FASTCALL __attribute__((fastcall))
197#  else
198#    define FASTCALL
199#  endif""")
200        return
201
202
203    def printVisibility(self, S, s):
204        """Conditionally define visibility function attribute.
205
206        Conditionally defines a preprocessor macro name S that wraps
207        GCC's visibility function attribute.  The visibility used is
208        the parameter s.  The conditional code can be easilly adapted
209        to other compilers that support a similar feature.
210
211        The name is also added to the file's undef_list.
212        """
213
214        self.undef_list.append(S)
215        print("""#  if defined(__GNUC__) && !defined(__CYGWIN__) && !defined(__MINGW32__)
216#    define %s  __attribute__((visibility("%s")))
217#  else
218#    define %s
219#  endif""" % (S, s, S))
220        return
221
222
223    def printNoinline(self):
224        """Conditionally define `NOINLINE' function attribute.
225
226        Conditionally defines a preprocessor macro `NOINLINE' that
227        wraps GCC's `noinline' function attribute.  The conditional
228        code can be easilly adapted to other compilers that support a
229        similar feature.
230
231        The name is also added to the file's undef_list.
232        """
233
234        self.undef_list.append("NOINLINE")
235        print("""#  if defined(__GNUC__)
236#    define NOINLINE __attribute__((noinline))
237#  else
238#    define NOINLINE
239#  endif""")
240        return
241
242
243def real_function_name(element):
244    name = element.get( "name" )
245    alias = element.get( "alias" )
246
247    if alias:
248        return alias
249    else:
250        return name
251
252
253def real_category_name(c):
254    if re.compile("[1-9][0-9]*[.][0-9]+").match(c):
255        return "GL_VERSION_" + c.replace(".", "_")
256    else:
257        return c
258
259
260def classify_category(name, number):
261    """Based on the category name and number, select a numerical class for it.
262
263    Categories are divided into four classes numbered 0 through 3.  The
264    classes are:
265
266            0. Core GL versions, sorted by version number.
267            1. ARB extensions, sorted by extension number.
268            2. Non-ARB extensions, sorted by extension number.
269            3. Un-numbered extensions, sorted by extension name.
270    """
271
272    try:
273        core_version = float(name)
274    except Exception:
275        core_version = 0.0
276
277    if core_version > 0.0:
278        cat_type = 0
279        key = name
280    elif name.startswith("GL_ARB_") or name.startswith("GLX_ARB_") or name.startswith("WGL_ARB_"):
281        cat_type = 1
282        key = int(number)
283    else:
284        if number != None:
285            cat_type = 2
286            key = int(number)
287        else:
288            cat_type = 3
289            key = name
290
291
292    return [cat_type, key]
293
294
295def create_parameter_string(parameters, include_names):
296    """Create a parameter string from a list of gl_parameters."""
297
298    list = []
299    for p in parameters:
300        if p.is_padding:
301            continue
302
303        if include_names:
304            list.append( p.string() )
305        else:
306            list.append( p.type_string() )
307
308    if len(list) == 0: list = ["void"]
309
310    return ", ".join(list)
311
312
313class gl_item(object):
314    def __init__(self, element, context, category):
315        self.context = context
316        self.name = element.get( "name" )
317        self.category = real_category_name( category )
318
319        return
320
321
322class gl_type( gl_item ):
323    def __init__(self, element, context, category):
324        gl_item.__init__(self, element, context, category)
325        self.size = int( element.get( "size" ), 0 )
326
327        te = typeexpr.type_expression( None )
328        tn = typeexpr.type_node()
329        tn.size = int( element.get( "size" ), 0 )
330        tn.integer = not is_attr_true( element, "float" )
331        tn.unsigned = is_attr_true( element, "unsigned" )
332        tn.pointer = is_attr_true( element, "pointer" )
333        tn.name = "GL" + self.name
334        te.set_base_type_node( tn )
335
336        self.type_expr = te
337        return
338
339
340    def get_type_expression(self):
341        return self.type_expr
342
343
344class gl_enum( gl_item ):
345    def __init__(self, element, context, category):
346        gl_item.__init__(self, element, context, category)
347        self.value = int( element.get( "value" ), 0 )
348
349        temp = element.get( "count" )
350        if not temp or temp == "?":
351            self.default_count = -1
352        else:
353            try:
354                c = int(temp)
355            except Exception:
356                raise RuntimeError('Invalid count value "%s" for enum "%s" in function "%s" when an integer was expected.' % (temp, self.name, n))
357
358            self.default_count = c
359
360        return
361
362
363    def priority(self):
364        """Calculate a 'priority' for this enum name.
365
366        When an enum is looked up by number, there may be many
367        possible names, but only one is the 'prefered' name.  The
368        priority is used to select which name is the 'best'.
369
370        Highest precedence is given to core GL name.  ARB extension
371        names have the next highest, followed by EXT extension names.
372        Vendor extension names are the lowest.
373        """
374
375        if self.name.endswith( "_BIT" ):
376            bias = 1
377        else:
378            bias = 0
379
380        if self.category.startswith( "GL_VERSION_" ):
381            priority = 0
382        elif self.category.startswith( "GL_ARB_" ):
383            priority = 2
384        elif self.category.startswith( "GL_EXT_" ):
385            priority = 4
386        else:
387            priority = 6
388
389        return priority + bias
390
391
392
393class gl_parameter(object):
394    def __init__(self, element, context):
395        self.name = element.get( "name" )
396
397        ts = element.get( "type" )
398        self.type_expr = typeexpr.type_expression( ts, context )
399
400        temp = element.get( "variable_param" )
401        if temp:
402            self.count_parameter_list = temp.split( ' ' )
403        else:
404            self.count_parameter_list = []
405
406        # The count tag can be either a numeric string or the name of
407        # a variable.  If it is the name of a variable, the int(c)
408        # statement will throw an exception, and the except block will
409        # take over.
410
411        c = element.get( "count" )
412        try:
413            count = int(c)
414            self.count = count
415            self.counter = None
416        except Exception:
417            count = 1
418            self.count = 0
419            self.counter = c
420
421        self.marshal_count = element.get("marshal_count")
422        self.marshal_large_count = element.get("marshal_large_count")
423        self.count_scale = int(element.get( "count_scale", "1" ))
424
425        elements = (count * self.count_scale)
426        if elements == 1:
427            elements = 0
428
429        #if ts == "GLdouble":
430        #	print '/* stack size -> %s = %u (before)*/' % (self.name, self.type_expr.get_stack_size())
431        #	print '/* # elements = %u */' % (elements)
432        self.type_expr.set_elements( elements )
433        #if ts == "GLdouble":
434        #	print '/* stack size -> %s = %u (after) */' % (self.name, self.type_expr.get_stack_size())
435
436        self.is_client_only = is_attr_true( element, 'client_only' )
437        self.is_counter     = is_attr_true( element, 'counter' )
438        self.is_output      = is_attr_true( element, 'output' )
439
440
441        # Pixel data has special parameters.
442
443        self.width      = element.get('img_width')
444        self.height     = element.get('img_height')
445        self.depth      = element.get('img_depth')
446        self.extent     = element.get('img_extent')
447
448        self.img_xoff   = element.get('img_xoff')
449        self.img_yoff   = element.get('img_yoff')
450        self.img_zoff   = element.get('img_zoff')
451        self.img_woff   = element.get('img_woff')
452
453        self.img_format = element.get('img_format')
454        self.img_type   = element.get('img_type')
455        self.img_target = element.get('img_target')
456
457        self.img_pad_dimensions = is_attr_true( element, 'img_pad_dimensions' )
458        self.img_null_flag      = is_attr_true( element, 'img_null_flag' )
459        self.img_send_null      = is_attr_true( element, 'img_send_null' )
460
461        self.is_padding = is_attr_true( element, 'padding' )
462        return
463
464
465    def compatible(self, other):
466        return 1
467
468
469    def is_array(self):
470        return self.is_pointer()
471
472
473    def is_pointer(self):
474        return self.type_expr.is_pointer()
475
476
477    def is_image(self):
478        if self.width:
479            return 1
480        else:
481            return 0
482
483
484    def is_variable_length(self):
485        return (len(self.count_parameter_list) or self.counter or
486                self.marshal_count or self.marshal_large_count)
487
488
489    def is_64_bit(self):
490        count = self.type_expr.get_element_count()
491        if count:
492            if (self.size() / count) == 8:
493                return 1
494        else:
495            if self.size() == 8:
496                return 1
497
498        return 0
499
500
501    def string(self):
502        if self.type_expr.original_string[-1] == '*':
503            return self.type_expr.original_string + self.name
504        else:
505            return self.type_expr.original_string + " " + self.name
506
507
508    def type_string(self):
509        return self.type_expr.original_string
510
511
512    def get_base_type_string(self):
513        return self.type_expr.get_base_name()
514
515
516    def get_dimensions(self):
517        if not self.width:
518            return [ 0, "0", "0", "0", "0" ]
519
520        dim = 1
521        w = self.width
522        h = "1"
523        d = "1"
524        e = "1"
525
526        if self.height:
527            dim = 2
528            h = self.height
529
530        if self.depth:
531            dim = 3
532            d = self.depth
533
534        if self.extent:
535            dim = 4
536            e = self.extent
537
538        return [ dim, w, h, d, e ]
539
540
541    def get_stack_size(self):
542        return self.type_expr.get_stack_size()
543
544
545    def size(self):
546        if self.is_image():
547            return 0
548        else:
549            return self.type_expr.get_element_size()
550
551
552    def get_element_count(self):
553        c = self.type_expr.get_element_count()
554        if c == 0:
555            return 1
556
557        return c
558
559
560    def size_string(self, use_parens = 1, marshal = 0):
561        base_size_str = ""
562
563        count = self.get_element_count()
564        if count:
565            base_size_str = "%d * " % count
566
567        base_size_str += "sizeof(%s)" % ( self.get_base_type_string() )
568
569        if (self.counter or self.count_parameter_list or
570            (marshal and (self.marshal_count or self.marshal_large_count))):
571            list = [ "compsize" ]
572
573            if marshal and self.marshal_count:
574                list = [ self.marshal_count ]
575            elif marshal and self.marshal_large_count:
576                list = [ self.marshal_large_count ]
577            elif self.counter and self.count_parameter_list:
578                list.append( self.counter )
579            elif self.counter:
580                list = [ self.counter ]
581
582            if self.size() > 1:
583                list.append( base_size_str )
584
585            # Don't use safe_mul if marshal_count is used, which indicates
586            # a small size.
587            if len(list) > 1 and use_parens and not self.marshal_count:
588                return "safe_mul(%s)" % ", ".join(list)
589            else:
590                return " * ".join(list)
591
592        elif self.is_image():
593            return "compsize"
594        else:
595            return base_size_str
596
597
598    def format_string(self):
599        if self.type_expr.original_string == "GLenum":
600            return "0x%x"
601        else:
602            return self.type_expr.format_string()
603
604
605class gl_function( gl_item ):
606    def __init__(self, element, context):
607        self.context = context
608        self.name = None
609
610        self.entry_points = []
611        self.return_type = "void"
612        self.parameters = []
613        self.offset = -1
614        self.initialized = 0
615        self.images = []
616        self.exec_flavor = 'mesa'
617        self.has_hw_select_variant = False
618        self.desktop = True
619        self.deprecated = None
620        self.has_no_error_variant = False
621
622        # self.api_map[api] is a decimal value indicating the earliest
623        # version of the given API in which ANY alias for the function
624        # exists.  The map only lists APIs which contain the function
625        # in at least one version.  For example, for the ClipPlanex
626        # function, self.api_map == { 'es1':
627        # Decimal('1.1') }.
628        self.api_map = {}
629
630        self.static_entry_points = []
631
632        # Track the parameter string (for the function prototype)
633        # for each entry-point.  This is done because some functions
634        # change their prototype slightly when promoted from extension
635        # to ARB extension to core.  glTexImage3DEXT and glTexImage3D
636        # are good examples of this.  Scripts that need to generate
637        # code for these differing aliases need to real prototype
638        # for each entry-point.  Otherwise, they may generate code
639        # that won't compile.
640
641        self.entry_point_parameters = {}
642
643        self.process_element( element )
644
645        return
646
647
648    def process_element(self, element):
649        name = element.get( "name" )
650        alias = element.get( "alias" )
651
652        # marshal isn't allowed with alias
653        assert not alias or not element.get('marshal')
654        assert not alias or not element.get('marshal_count')
655        assert not alias or not element.get('marshal_large_count')
656        assert not alias or not element.get('marshal_sync')
657        assert not alias or not element.get('marshal_call_before')
658        assert not alias or not element.get('marshal_call_after')
659        assert not alias or not element.get('deprecated')
660
661        if name in static_data.functions:
662            self.static_entry_points.append(name)
663
664        self.entry_points.append( name )
665
666        for api in ('es1', 'es2'):
667            version_str = element.get(api, 'none')
668            assert version_str is not None
669            if version_str != 'none':
670                version_decimal = Decimal(version_str)
671                if api not in self.api_map or \
672                        version_decimal < self.api_map[api]:
673                    self.api_map[api] = version_decimal
674
675        exec_flavor = element.get('exec')
676        if exec_flavor:
677            self.exec_flavor = exec_flavor
678
679        deprecated = element.get('deprecated', 'none')
680        if deprecated != 'none':
681            self.deprecated = Decimal(deprecated)
682
683        if not is_attr_true(element, 'desktop', 'true'):
684            self.desktop = False
685
686        if self.has_no_error_variant or is_attr_true(element, 'no_error'):
687            self.has_no_error_variant = True
688        else:
689            self.has_no_error_variant = False
690
691        if alias:
692            true_name = alias
693        else:
694            true_name = name
695
696            self.has_hw_select_variant = exec_flavor == 'beginend' and name[0:6] == 'Vertex'
697
698            # Only try to set the offset when a non-alias entry-point
699            # is being processed.
700
701            if name in static_data.offsets:
702                self.offset = static_data.offsets[name]
703            else:
704                if self.exec_flavor != "skip":
705                    raise RuntimeError("Entry-point %s is missing offset in static_data.py. Add one at the bottom of the list." % (name))
706
707        if not self.name:
708            self.name = true_name
709        elif self.name != true_name:
710            raise RuntimeError("Function true name redefined.  Was %s, now %s." % (self.name, true_name))
711
712
713        # There are two possible cases.  The first time an entry-point
714        # with data is seen, self.initialized will be 0.  On that
715        # pass, we just fill in the data.  The next time an
716        # entry-point with data is seen, self.initialized will be 1.
717        # On that pass we have to make that the new values match the
718        # valuse from the previous entry-point.
719
720        parameters = []
721        return_type = "void"
722        for child in element:
723            if child.tag == "return":
724                return_type = child.get( "type", "void" )
725            elif child.tag == "param":
726                param = self.context.factory.create_parameter(child, self.context)
727                parameters.append( param )
728
729
730        if self.initialized:
731            if self.return_type != return_type:
732                raise RuntimeError( "Return type changed in %s.  Was %s, now %s." % (name, self.return_type, return_type))
733
734            if len(parameters) != len(self.parameters):
735                raise RuntimeError( "Parameter count mismatch in %s.  Was %d, now %d." % (name, len(self.parameters), len(parameters)))
736
737            for j in range(0, len(parameters)):
738                p1 = parameters[j]
739                p2 = self.parameters[j]
740                if not p1.compatible( p2 ):
741                    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))
742
743
744        if true_name == name or not self.initialized:
745            self.return_type = return_type
746            self.parameters = parameters
747
748            for param in self.parameters:
749                if param.is_image():
750                    self.images.append( param )
751
752        if list(element):
753            self.initialized = 1
754            self.entry_point_parameters[name] = parameters
755        else:
756            self.entry_point_parameters[name] = []
757
758        return
759
760    def filter_entry_points(self, entry_point_list):
761        """Filter out entry points not in entry_point_list."""
762        if not self.initialized:
763            raise RuntimeError('%s is not initialized yet' % self.name)
764
765        entry_points = []
766        for ent in self.entry_points:
767            if ent not in entry_point_list:
768                if ent in self.static_entry_points:
769                    self.static_entry_points.remove(ent)
770                self.entry_point_parameters.pop(ent)
771            else:
772                entry_points.append(ent)
773
774        if not entry_points:
775            raise RuntimeError('%s has no entry point after filtering' % self.name)
776
777        self.entry_points = entry_points
778        if self.name not in entry_points:
779            # use the first remaining entry point
780            self.name = entry_points[0]
781            self.parameters = self.entry_point_parameters[entry_points[0]]
782
783    def get_images(self):
784        """Return potentially empty list of input images."""
785        return self.images
786
787
788    def parameterIterator(self, name = None):
789        if name is not None:
790            return iter(self.entry_point_parameters[name]);
791        else:
792            return iter(self.parameters);
793
794
795    def get_parameter_string(self, entrypoint = None):
796        if entrypoint:
797            params = self.entry_point_parameters[ entrypoint ]
798        else:
799            params = self.parameters
800
801        return create_parameter_string( params, 1 )
802
803    def get_called_parameter_string(self):
804        p_string = ""
805        comma = ""
806
807        for p in self.parameterIterator():
808            if p.is_padding:
809                continue
810            p_string = p_string + comma + p.name
811            comma = ", "
812
813        return p_string
814
815    def is_static_entry_point(self, name):
816        return name in self.static_entry_points
817
818    def dispatch_name(self):
819        if self.name in self.static_entry_points:
820            return self.name
821        else:
822            return "_dispatch_stub_%u" % (self.offset)
823
824    def static_name(self, name):
825        if name in self.static_entry_points:
826            return name
827        else:
828            return "_dispatch_stub_%u" % (self.offset)
829
830class gl_item_factory(object):
831    """Factory to create objects derived from gl_item."""
832
833    def create_function(self, element, context):
834        return gl_function(element, context)
835
836    def create_type(self, element, context, category):
837        return gl_type(element, context, category)
838
839    def create_enum(self, element, context, category):
840        return gl_enum(element, context, category)
841
842    def create_parameter(self, element, context):
843        return gl_parameter(element, context)
844
845    def create_api(self, pointer_size):
846        return gl_api(self, pointer_size)
847
848
849class gl_api(object):
850    def __init__(self, factory, pointer_size):
851        self.functions_by_name = OrderedDict()
852        self.enums_by_name = {}
853        self.types_by_name = {}
854
855        self.category_dict = {}
856        self.categories = [{}, {}, {}, {}]
857
858        self.factory = factory
859
860        self.next_offset = 0
861        self.pointer_size = pointer_size
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