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