• 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 ):
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_before')
659        assert not alias or not element.get('marshal_call_after')
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            # Only try to set the offset when a non-alias entry-point
697            # is being processed.
698
699            if name in static_data.offsets and static_data.offsets[name] <= static_data.MAX_OFFSETS:
700                self.offset = static_data.offsets[name]
701            elif name in static_data.offsets and static_data.offsets[name] > static_data.MAX_OFFSETS:
702                self.offset = static_data.offsets[name]
703                self.assign_offset = True
704            else:
705                if self.exec_flavor != "skip":
706                    raise RuntimeError("Entry-point %s is missing offset in static_data.py. Add one at the bottom of the list." % (name))
707                self.assign_offset = self.exec_flavor != "skip" or name in static_data.unused_functions
708
709        if not self.name:
710            self.name = true_name
711        elif self.name != true_name:
712            raise RuntimeError("Function true name redefined.  Was %s, now %s." % (self.name, true_name))
713
714
715        # There are two possible cases.  The first time an entry-point
716        # with data is seen, self.initialized will be 0.  On that
717        # pass, we just fill in the data.  The next time an
718        # entry-point with data is seen, self.initialized will be 1.
719        # On that pass we have to make that the new values match the
720        # valuse from the previous entry-point.
721
722        parameters = []
723        return_type = "void"
724        for child in element:
725            if child.tag == "return":
726                return_type = child.get( "type", "void" )
727            elif child.tag == "param":
728                param = self.context.factory.create_parameter(child, self.context)
729                parameters.append( param )
730
731
732        if self.initialized:
733            if self.return_type != return_type:
734                raise RuntimeError( "Return type changed in %s.  Was %s, now %s." % (name, self.return_type, return_type))
735
736            if len(parameters) != len(self.parameters):
737                raise RuntimeError( "Parameter count mismatch in %s.  Was %d, now %d." % (name, len(self.parameters), len(parameters)))
738
739            for j in range(0, len(parameters)):
740                p1 = parameters[j]
741                p2 = self.parameters[j]
742                if not p1.compatible( p2 ):
743                    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))
744
745
746        if true_name == name or not self.initialized:
747            self.return_type = return_type
748            self.parameters = parameters
749
750            for param in self.parameters:
751                if param.is_image():
752                    self.images.append( param )
753
754        if list(element):
755            self.initialized = 1
756            self.entry_point_parameters[name] = parameters
757        else:
758            self.entry_point_parameters[name] = []
759
760        return
761
762    def filter_entry_points(self, entry_point_list):
763        """Filter out entry points not in entry_point_list."""
764        if not self.initialized:
765            raise RuntimeError('%s is not initialized yet' % self.name)
766
767        entry_points = []
768        for ent in self.entry_points:
769            if ent not in entry_point_list:
770                if ent in self.static_entry_points:
771                    self.static_entry_points.remove(ent)
772                self.entry_point_parameters.pop(ent)
773            else:
774                entry_points.append(ent)
775
776        if not entry_points:
777            raise RuntimeError('%s has no entry point after filtering' % self.name)
778
779        self.entry_points = entry_points
780        if self.name not in entry_points:
781            # use the first remaining entry point
782            self.name = entry_points[0]
783            self.parameters = self.entry_point_parameters[entry_points[0]]
784
785    def get_images(self):
786        """Return potentially empty list of input images."""
787        return self.images
788
789
790    def parameterIterator(self, name = None):
791        if name is not None:
792            return iter(self.entry_point_parameters[name]);
793        else:
794            return iter(self.parameters);
795
796
797    def get_parameter_string(self, entrypoint = None):
798        if entrypoint:
799            params = self.entry_point_parameters[ entrypoint ]
800        else:
801            params = self.parameters
802
803        return create_parameter_string( params, 1 )
804
805    def get_called_parameter_string(self):
806        p_string = ""
807        comma = ""
808
809        for p in self.parameterIterator():
810            if p.is_padding:
811                continue
812            p_string = p_string + comma + p.name
813            comma = ", "
814
815        return p_string
816
817
818    def is_abi(self):
819        return (self.offset >= 0 and not self.assign_offset)
820
821    def is_static_entry_point(self, name):
822        return name in self.static_entry_points
823
824    def dispatch_name(self):
825        if self.name in self.static_entry_points:
826            return self.name
827        else:
828            return "_dispatch_stub_%u" % (self.offset)
829
830    def static_name(self, name):
831        if name in self.static_entry_points:
832            return name
833        else:
834            return "_dispatch_stub_%u" % (self.offset)
835
836class gl_item_factory(object):
837    """Factory to create objects derived from gl_item."""
838
839    def create_function(self, element, context):
840        return gl_function(element, context)
841
842    def create_type(self, element, context, category):
843        return gl_type(element, context, category)
844
845    def create_enum(self, element, context, category):
846        return gl_enum(element, context, category)
847
848    def create_parameter(self, element, context):
849        return gl_parameter(element, context)
850
851    def create_api(self):
852        return gl_api(self)
853
854
855class gl_api(object):
856    def __init__(self, factory):
857        self.functions_by_name = OrderedDict()
858        self.enums_by_name = {}
859        self.types_by_name = {}
860
861        self.category_dict = {}
862        self.categories = [{}, {}, {}, {}]
863
864        self.factory = factory
865
866        self.next_offset = 0
867
868        typeexpr.create_initial_types()
869        return
870
871    def parse_file(self, file_name):
872        doc = ET.parse( file_name )
873        self.process_element(file_name, doc)
874
875
876    def process_element(self, file_name, doc):
877        element = doc.getroot()
878        if element.tag == "OpenGLAPI":
879            self.process_OpenGLAPI(file_name, element)
880        return
881
882
883    def process_OpenGLAPI(self, file_name, element):
884        for child in element:
885            if child.tag == "category":
886                self.process_category( child )
887            elif child.tag == "OpenGLAPI":
888                self.process_OpenGLAPI( file_name, child )
889            elif child.tag == '{http://www.w3.org/2001/XInclude}include':
890                href = child.get('href')
891                href = os.path.join(os.path.dirname(file_name), href)
892                self.parse_file(href)
893
894        return
895
896
897    def process_category(self, cat):
898        cat_name = cat.get( "name" )
899        cat_number = cat.get( "number" )
900
901        [cat_type, key] = classify_category(cat_name, cat_number)
902        self.categories[cat_type][key] = [cat_name, cat_number]
903
904        for child in cat:
905            if child.tag == "function":
906                func_name = real_function_name( child )
907
908                temp_name = child.get( "name" )
909                self.category_dict[ temp_name ] = [cat_name, cat_number]
910
911                if func_name in self.functions_by_name:
912                    func = self.functions_by_name[ func_name ]
913                    func.process_element( child )
914                else:
915                    func = self.factory.create_function( child, self )
916                    self.functions_by_name[ func_name ] = func
917
918                if func.offset >= self.next_offset:
919                    self.next_offset = func.offset + 1
920
921
922            elif child.tag == "enum":
923                enum = self.factory.create_enum( child, self, cat_name )
924                self.enums_by_name[ enum.name ] = enum
925            elif child.tag == "type":
926                t = self.factory.create_type( child, self, cat_name )
927                self.types_by_name[ "GL" + t.name ] = t
928
929        return
930
931
932    def functionIterateByCategory(self, cat = None):
933        """Iterate over functions by category.
934
935        If cat is None, all known functions are iterated in category
936        order.  See classify_category for details of the ordering.
937        Within a category, functions are sorted by name.  If cat is
938        not None, then only functions in that category are iterated.
939        """
940        lists = [{}, {}, {}, {}]
941
942        for func in self.functionIterateAll():
943            [cat_name, cat_number] = self.category_dict[func.name]
944
945            if (cat == None) or (cat == cat_name):
946                [func_cat_type, key] = classify_category(cat_name, cat_number)
947
948                if key not in lists[func_cat_type]:
949                    lists[func_cat_type][key] = {}
950
951                lists[func_cat_type][key][func.name] = func
952
953
954        functions = []
955        for func_cat_type in range(0,4):
956            keys = sorted(lists[func_cat_type].keys())
957
958            for key in keys:
959                names = sorted(lists[func_cat_type][key].keys())
960
961                for name in names:
962                    functions.append(lists[func_cat_type][key][name])
963
964        return iter(functions)
965
966
967    def functionIterateByOffset(self):
968        max_offset = -1
969        for func in self.functions_by_name.values():
970            if func.offset > max_offset:
971                max_offset = func.offset
972
973
974        temp = [None for i in range(0, max_offset + 1)]
975        for func in self.functions_by_name.values():
976            if func.offset != -1:
977                temp[ func.offset ] = func
978
979
980        list = []
981        for i in range(0, max_offset + 1):
982            if temp[i]:
983                list.append(temp[i])
984
985        return iter(list);
986
987
988    def functionIterateAll(self):
989        return self.functions_by_name.values()
990
991
992    def enumIterateByName(self):
993        keys = sorted(self.enums_by_name.keys())
994
995        list = []
996        for enum in keys:
997            list.append( self.enums_by_name[ enum ] )
998
999        return iter(list)
1000
1001
1002    def categoryIterate(self):
1003        """Iterate over categories.
1004
1005        Iterate over all known categories in the order specified by
1006        classify_category.  Each iterated value is a tuple of the
1007        name and number (which may be None) of the category.
1008        """
1009
1010        list = []
1011        for cat_type in range(0,4):
1012            keys = sorted(self.categories[cat_type].keys())
1013
1014            for key in keys:
1015                list.append(self.categories[cat_type][key])
1016
1017        return iter(list)
1018
1019
1020    def get_category_for_name( self, name ):
1021        if name in self.category_dict:
1022            return self.category_dict[name]
1023        else:
1024            return ["<unknown category>", None]
1025
1026
1027    def typeIterate(self):
1028        return self.types_by_name.values()
1029
1030
1031    def find_type( self, type_name ):
1032        if type_name in self.types_by_name:
1033            return self.types_by_name[ type_name ].type_expr
1034        else:
1035            print("Unable to find base type matching \"%s\"." % (type_name))
1036            return None
1037