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