1#!/usr/bin/env python
2
3# (C) Copyright IBM Corporation 2004, 2005
4# All Rights Reserved.
5#
6# Permission is hereby granted, free of charge, to any person obtaining a
7# copy of this software and associated documentation files (the "Software"),
8# to deal in the Software without restriction, including without limitation
9# on the rights to use, copy, modify, merge, publish, distribute, sub
10# license, and/or sell copies of the Software, and to permit persons to whom
11# the Software is furnished to do so, subject to the following conditions:
12#
13# The above copyright notice and this permission notice (including the next
14# paragraph) shall be included in all copies or substantial portions of the
15# Software.
16#
17# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
20# IBM AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23# IN THE SOFTWARE.
24#
25# Authors:
26#    Ian Romanick <idr@us.ibm.com>
27
28import libxml2
29import re, sys, string
30import typeexpr
31
32
33def parse_GL_API( file_name, factory = None ):
34	doc = libxml2.readFile( file_name, None, libxml2.XML_PARSE_XINCLUDE + libxml2.XML_PARSE_NOBLANKS + libxml2.XML_PARSE_DTDVALID + libxml2.XML_PARSE_DTDATTR + libxml2.XML_PARSE_DTDLOAD + libxml2.XML_PARSE_NOENT )
35	ret = doc.xincludeProcess()
36
37	if not factory:
38		factory = gl_item_factory()
39
40	api = factory.create_item( "api", None, None )
41	api.process_element( doc )
42
43	# After the XML has been processed, we need to go back and assign
44	# dispatch offsets to the functions that request that their offsets
45	# be assigned by the scripts.  Typically this means all functions
46	# that are not part of the ABI.
47
48	for func in api.functionIterateByCategory():
49		if func.assign_offset:
50			func.offset = api.next_offset;
51			api.next_offset += 1
52
53	doc.freeDoc()
54
55	return api
56
57
58def is_attr_true( element, name ):
59	"""Read a name value from an element's attributes.
60
61	The value read from the attribute list must be either 'true' or
62	'false'.  If the value is 'false', zero will be returned.  If the
63	value is 'true', non-zero will be returned.  An exception will be
64	raised for any other value."""
65
66	value = element.nsProp( name, None )
67	if value == "true":
68		return 1
69	elif value == "false":
70		return 0
71	else:
72		raise RuntimeError('Invalid value "%s" for boolean "%s".' % (value, name))
73
74
75class gl_print_base:
76	"""Base class of all API pretty-printers.
77
78	In the model-view-controller pattern, this is the view.  Any derived
79	class will want to over-ride the printBody, printRealHader, and
80	printRealFooter methods.  Some derived classes may want to over-ride
81	printHeader and printFooter, or even Print (though this is unlikely).
82	"""
83
84	def __init__(self):
85		# Name of the script that is generating the output file.
86		# Every derived class should set this to the name of its
87		# source file.
88
89		self.name = "a"
90
91
92		# License on the *generated* source file.  This may differ
93		# from the license on the script that is generating the file.
94		# Every derived class should set this to some reasonable
95		# value.
96		#
97		# See license.py for an example of a reasonable value.
98
99		self.license = "The license for this file is unspecified."
100
101
102		# The header_tag is the name of the C preprocessor define
103		# used to prevent multiple inclusion.  Typically only
104		# generated C header files need this to be set.  Setting it
105		# causes code to be generated automatically in printHeader
106		# and printFooter.
107
108		self.header_tag = None
109
110
111		# List of file-private defines that must be undefined at the
112		# end of the file.  This can be used in header files to define
113		# names for use in the file, then undefine them at the end of
114		# the header file.
115
116		self.undef_list = []
117		return
118
119
120	def Print(self, api):
121		self.printHeader()
122		self.printBody(api)
123		self.printFooter()
124		return
125
126
127	def printHeader(self):
128		"""Print the header associated with all files and call the printRealHeader method."""
129
130		print '/* DO NOT EDIT - This file generated automatically by %s script */' \
131			% (self.name)
132		print ''
133		print '/*'
134		print ' * ' + self.license.replace('\n', '\n * ')
135		print ' */'
136		print ''
137		if self.header_tag:
138		    print '#if !defined( %s )' % (self.header_tag)
139		    print '#  define %s' % (self.header_tag)
140		    print ''
141		self.printRealHeader();
142		return
143
144
145	def printFooter(self):
146		"""Print the header associated with all files and call the printRealFooter method."""
147
148		self.printRealFooter()
149
150		if self.undef_list:
151			print ''
152			for u in self.undef_list:
153				print "#  undef %s" % (u)
154
155		if self.header_tag:
156			print ''
157			print '#endif /* !defined( %s ) */' % (self.header_tag)
158
159
160	def printRealHeader(self):
161		"""Print the "real" header for the created file.
162
163		In the base class, this function is empty.  All derived
164		classes should over-ride this function."""
165		return
166
167
168	def printRealFooter(self):
169		"""Print the "real" footer for the created file.
170
171		In the base class, this function is empty.  All derived
172		classes should over-ride this function."""
173		return
174
175
176	def printPure(self):
177		"""Conditionally define `PURE' function attribute.
178
179		Conditionally defines a preprocessor macro `PURE' that wraps
180		GCC's `pure' function attribute.  The conditional code can be
181		easilly adapted to other compilers that support a similar
182		feature.
183
184		The name is also added to the file's undef_list.
185		"""
186		self.undef_list.append("PURE")
187		print """#  if defined(__GNUC__) || (defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590))
188#    define PURE __attribute__((pure))
189#  else
190#    define PURE
191#  endif"""
192		return
193
194
195	def printFastcall(self):
196		"""Conditionally define `FASTCALL' function attribute.
197
198		Conditionally defines a preprocessor macro `FASTCALL' that
199		wraps GCC's `fastcall' function attribute.  The conditional
200		code can be easilly adapted to other compilers that support a
201		similar feature.
202
203		The name is also added to the file's undef_list.
204		"""
205
206		self.undef_list.append("FASTCALL")
207		print """#  if defined(__i386__) && defined(__GNUC__) && !defined(__CYGWIN__) && !defined(__MINGW32__)
208#    define FASTCALL __attribute__((fastcall))
209#  else
210#    define FASTCALL
211#  endif"""
212		return
213
214
215	def printVisibility(self, S, s):
216		"""Conditionally define visibility function attribute.
217
218		Conditionally defines a preprocessor macro name S that wraps
219		GCC's visibility function attribute.  The visibility used is
220		the parameter s.  The conditional code can be easilly adapted
221		to other compilers that support a similar feature.
222
223		The name is also added to the file's undef_list.
224		"""
225
226		self.undef_list.append(S)
227		print """#  if (defined(__GNUC__) && !defined(__CYGWIN__) && !defined(__MINGW32__)) || (defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590) && defined(__ELF__))
228#    define %s  __attribute__((visibility("%s")))
229#  else
230#    define %s
231#  endif""" % (S, s, S)
232		return
233
234
235	def printNoinline(self):
236		"""Conditionally define `NOINLINE' function attribute.
237
238		Conditionally defines a preprocessor macro `NOINLINE' that
239		wraps GCC's `noinline' function attribute.  The conditional
240		code can be easilly adapted to other compilers that support a
241		similar feature.
242
243		The name is also added to the file's undef_list.
244		"""
245
246		self.undef_list.append("NOINLINE")
247		print """#  if defined(__GNUC__) || (defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590))
248#    define NOINLINE __attribute__((noinline))
249#  else
250#    define NOINLINE
251#  endif"""
252		return
253
254
255def real_function_name(element):
256	name = element.nsProp( "name", None )
257	alias = element.nsProp( "alias", None )
258
259	if alias:
260		return alias
261	else:
262		return name
263
264
265def real_category_name(c):
266	if re.compile("[1-9][0-9]*[.][0-9]+").match(c):
267		return "GL_VERSION_" + c.replace(".", "_")
268	else:
269		return c
270
271
272def classify_category(name, number):
273	"""Based on the category name and number, select a numerical class for it.
274
275	Categories are divided into four classes numbered 0 through 3.  The
276	classes are:
277
278		0. Core GL versions, sorted by version number.
279		1. ARB extensions, sorted by extension number.
280		2. Non-ARB extensions, sorted by extension number.
281		3. Un-numbered extensions, sorted by extension name.
282	"""
283
284	try:
285		core_version = float(name)
286	except Exception,e:
287		core_version = 0.0
288
289	if core_version > 0.0:
290		cat_type = 0
291		key = name
292	elif name.startswith("GL_ARB_") or name.startswith("GLX_ARB_") or name.startswith("WGL_ARB_"):
293		cat_type = 1
294		key = int(number)
295	else:
296		if number != None:
297			cat_type = 2
298			key = int(number)
299		else:
300			cat_type = 3
301			key = name
302
303
304	return [cat_type, key]
305
306
307def create_parameter_string(parameters, include_names):
308	"""Create a parameter string from a list of gl_parameters."""
309
310	list = []
311	for p in parameters:
312		if p.is_padding:
313			continue
314
315		if include_names:
316			list.append( p.string() )
317		else:
318			list.append( p.type_string() )
319
320	if len(list) == 0: list = ["void"]
321
322	return string.join(list, ", ")
323
324
325class gl_item:
326	def __init__(self, element, context):
327		self.context = context
328		self.name = element.nsProp( "name", None )
329		self.category = real_category_name( element.parent.nsProp( "name", None ) )
330		return
331
332
333class gl_type( gl_item ):
334	def __init__(self, element, context):
335		gl_item.__init__(self, element, context)
336		self.size = int( element.nsProp( "size", None ), 0 )
337
338		te = typeexpr.type_expression( None )
339		tn = typeexpr.type_node()
340		tn.size = int( element.nsProp( "size", None ), 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):
357		gl_item.__init__(self, element, context)
358		self.value = int( element.nsProp( "value", None ), 0 )
359
360		temp = element.nsProp( "count", None )
361		if not temp or temp == "?":
362			self.default_count = -1
363		else:
364			try:
365				c = int(temp)
366			except Exception,e:
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:
405	def __init__(self, element, context):
406		self.name = element.nsProp( "name", None )
407
408		ts = element.nsProp( "type", None )
409		self.type_expr = typeexpr.type_expression( ts, context )
410
411		temp = element.nsProp( "variable_param", None )
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.nsProp( "count", None )
423		try:
424			count = int(c)
425			self.count = count
426			self.counter = None
427		except Exception,e:
428			count = 1
429			self.count = 0
430			self.counter = c
431
432		self.count_scale = int(element.nsProp( "count_scale", None ))
433
434		elements = (count * self.count_scale)
435		if elements == 1:
436			elements = 0
437
438		#if ts == "GLdouble":
439		#	print '/* stack size -> %s = %u (before)*/' % (self.name, self.type_expr.get_stack_size())
440		#	print '/* # elements = %u */' % (elements)
441		self.type_expr.set_elements( elements )
442		#if ts == "GLdouble":
443		#	print '/* stack size -> %s = %u (after) */' % (self.name, self.type_expr.get_stack_size())
444
445		self.is_client_only = is_attr_true( element, 'client_only' )
446		self.is_counter     = is_attr_true( element, 'counter' )
447		self.is_output      = is_attr_true( element, 'output' )
448
449
450		# Pixel data has special parameters.
451
452		self.width      = element.nsProp('img_width',  None)
453		self.height     = element.nsProp('img_height', None)
454		self.depth      = element.nsProp('img_depth',  None)
455		self.extent     = element.nsProp('img_extent', None)
456
457		self.img_xoff   = element.nsProp('img_xoff',   None)
458		self.img_yoff   = element.nsProp('img_yoff',   None)
459		self.img_zoff   = element.nsProp('img_zoff',   None)
460		self.img_woff   = element.nsProp('img_woff',   None)
461
462		self.img_format = element.nsProp('img_format', None)
463		self.img_type   = element.nsProp('img_type',   None)
464		self.img_target = element.nsProp('img_target', None)
465
466		self.img_pad_dimensions = is_attr_true( element, 'img_pad_dimensions' )
467		self.img_null_flag      = is_attr_true( element, 'img_null_flag' )
468		self.img_send_null      = is_attr_true( element, 'img_send_null' )
469
470		self.is_padding = is_attr_true( element, 'padding' )
471		return
472
473
474	def compatible(self, other):
475		return 1
476
477
478	def is_array(self):
479		return self.is_pointer()
480
481
482	def is_pointer(self):
483		return self.type_expr.is_pointer()
484
485
486	def is_image(self):
487		if self.width:
488			return 1
489		else:
490			return 0
491
492
493	def is_variable_length(self):
494		return len(self.count_parameter_list) or self.counter
495
496
497	def is_64_bit(self):
498		count = self.type_expr.get_element_count()
499		if count:
500			if (self.size() / count) == 8:
501				return 1
502		else:
503			if self.size() == 8:
504				return 1
505
506		return 0
507
508
509	def string(self):
510		return self.type_expr.original_string + " " + self.name
511
512
513	def type_string(self):
514		return self.type_expr.original_string
515
516
517	def get_base_type_string(self):
518		return self.type_expr.get_base_name()
519
520
521	def get_dimensions(self):
522		if not self.width:
523			return [ 0, "0", "0", "0", "0" ]
524
525		dim = 1
526		w = self.width
527		h = "1"
528		d = "1"
529		e = "1"
530
531		if self.height:
532			dim = 2
533			h = self.height
534
535		if self.depth:
536			dim = 3
537			d = self.depth
538
539		if self.extent:
540			dim = 4
541			e = self.extent
542
543		return [ dim, w, h, d, e ]
544
545
546	def get_stack_size(self):
547		return self.type_expr.get_stack_size()
548
549
550	def size(self):
551		if self.is_image():
552			return 0
553		else:
554			return self.type_expr.get_element_size()
555
556
557	def get_element_count(self):
558		c = self.type_expr.get_element_count()
559		if c == 0:
560			return 1
561
562		return c
563
564
565	def size_string(self, use_parens = 1):
566		s = self.size()
567		if self.counter or self.count_parameter_list:
568			list = [ "compsize" ]
569
570			if self.counter and self.count_parameter_list:
571				list.append( self.counter )
572			elif self.counter:
573				list = [ self.counter ]
574
575			if s > 1:
576				list.append( str(s) )
577
578			if len(list) > 1 and use_parens :
579				return "(%s)" % (string.join(list, " * "))
580			else:
581				return string.join(list, " * ")
582
583		elif self.is_image():
584			return "compsize"
585		else:
586			return str(s)
587
588
589	def format_string(self):
590		if self.type_expr.original_string == "GLenum":
591			return "0x%x"
592		else:
593			return self.type_expr.format_string()
594
595
596
597class gl_function( gl_item ):
598	def __init__(self, element, context):
599		self.context = context
600		self.name = None
601
602		self.entry_points = []
603		self.return_type = "void"
604		self.parameters = []
605		self.offset = -1
606		self.initialized = 0
607		self.images = []
608
609		self.assign_offset = 0
610
611		self.static_entry_points = []
612
613		# Track the parameter string (for the function prototype)
614		# for each entry-point.  This is done because some functions
615		# change their prototype slightly when promoted from extension
616		# to ARB extension to core.  glTexImage3DEXT and glTexImage3D
617		# are good examples of this.  Scripts that need to generate
618		# code for these differing aliases need to real prototype
619		# for each entry-point.  Otherwise, they may generate code
620		# that won't compile.
621
622		self.entry_point_parameters = {}
623
624		self.process_element( element )
625
626		return
627
628
629	def process_element(self, element):
630		name = element.nsProp( "name", None )
631		alias = element.nsProp( "alias", None )
632
633		if is_attr_true(element, "static_dispatch"):
634			self.static_entry_points.append(name)
635
636		self.entry_points.append( name )
637		if alias:
638			true_name = alias
639		else:
640			true_name = name
641
642			# Only try to set the offset when a non-alias
643			# entry-point is being processes.
644
645			offset = element.nsProp( "offset", None )
646			if offset:
647				try:
648					o = int( offset )
649					self.offset = o
650				except Exception, e:
651					self.offset = -1
652					if offset == "assign":
653						self.assign_offset = 1
654
655
656		if not self.name:
657			self.name = true_name
658		elif self.name != true_name:
659			raise RuntimeError("Function true name redefined.  Was %s, now %s." % (self.name, true_name))
660
661
662		# There are two possible cases.  The first time an entry-point
663		# with data is seen, self.initialized will be 0.  On that
664		# pass, we just fill in the data.  The next time an
665		# entry-point with data is seen, self.initialized will be 1.
666		# On that pass we have to make that the new values match the
667		# valuse from the previous entry-point.
668
669		parameters = []
670		return_type = "void"
671		child = element.children
672		while child:
673			if child.type == "element":
674				if child.name == "return":
675					return_type = child.nsProp( "type", None )
676				elif child.name == "param":
677					param = self.context.factory.create_item( "parameter", child, self.context)
678					parameters.append( param )
679
680			child = child.next
681
682
683		if self.initialized:
684			if self.return_type != return_type:
685				raise RuntimeError( "Return type changed in %s.  Was %s, now %s." % (name, self.return_type, return_type))
686
687			if len(parameters) != len(self.parameters):
688				raise RuntimeError( "Parameter count mismatch in %s.  Was %d, now %d." % (name, len(self.parameters), len(parameters)))
689
690			for j in range(0, len(parameters)):
691				p1 = parameters[j]
692				p2 = self.parameters[j]
693				if not p1.compatible( p2 ):
694					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))
695
696
697		if true_name == name or not self.initialized:
698			self.return_type = return_type
699			self.parameters = parameters
700
701			for param in self.parameters:
702				if param.is_image():
703					self.images.append( param )
704
705		if element.children:
706			self.initialized = 1
707			self.entry_point_parameters[name] = parameters
708		else:
709			self.entry_point_parameters[name] = []
710
711		return
712
713	def filter_entry_points(self, entry_point_list):
714		"""Filter out entry points not in entry_point_list."""
715		if not self.initialized:
716			raise RuntimeError('%s is not initialized yet' % self.name)
717
718		entry_points = []
719		for ent in self.entry_points:
720			if ent not in entry_point_list:
721				if ent in self.static_entry_points:
722					self.static_entry_points.remove(ent)
723				self.entry_point_parameters.pop(ent)
724			else:
725				entry_points.append(ent)
726
727		if not entry_points:
728			raise RuntimeError('%s has no entry point after filtering' % self.name)
729
730		self.entry_points = entry_points
731		if self.name not in entry_points:
732			# use the first remaining entry point
733			self.name = entry_points[0]
734			self.parameters = self.entry_point_parameters[entry_points[0]]
735
736	def get_images(self):
737		"""Return potentially empty list of input images."""
738		return self.images
739
740
741	def parameterIterator(self):
742		return self.parameters.__iter__();
743
744
745	def get_parameter_string(self, entrypoint = None):
746		if entrypoint:
747			params = self.entry_point_parameters[ entrypoint ]
748		else:
749			params = self.parameters
750
751		return create_parameter_string( params, 1 )
752
753	def get_called_parameter_string(self):
754		p_string = ""
755		comma = ""
756
757		for p in self.parameterIterator():
758			p_string = p_string + comma + p.name
759			comma = ", "
760
761		return p_string
762
763
764	def is_abi(self):
765		return (self.offset >= 0 and not self.assign_offset)
766
767	def is_static_entry_point(self, name):
768		return name in self.static_entry_points
769
770	def dispatch_name(self):
771		if self.name in self.static_entry_points:
772			return self.name
773		else:
774			return "_dispatch_stub_%u" % (self.offset)
775
776	def static_name(self, name):
777		if name in self.static_entry_points:
778			return name
779		else:
780			return "_dispatch_stub_%u" % (self.offset)
781
782
783class gl_item_factory:
784	"""Factory to create objects derived from gl_item."""
785
786	def create_item(self, item_name, element, context):
787		if item_name == "function":
788			return gl_function(element, context)
789		if item_name == "type":
790			return gl_type(element, context)
791		elif item_name == "enum":
792			return gl_enum(element, context)
793		elif item_name == "parameter":
794			return gl_parameter(element, context)
795		elif item_name == "api":
796			return gl_api(self)
797		else:
798			return None
799
800
801class gl_api:
802	def __init__(self, factory):
803		self.functions_by_name = {}
804		self.enums_by_name = {}
805		self.types_by_name = {}
806
807		self.category_dict = {}
808		self.categories = [{}, {}, {}, {}]
809
810		self.factory = factory
811
812		self.next_offset = 0
813
814		typeexpr.create_initial_types()
815		return
816
817	def filter_functions(self, entry_point_list):
818		"""Filter out entry points not in entry_point_list."""
819		functions_by_name = {}
820		for func in self.functions_by_name.itervalues():
821			entry_points = [ent for ent in func.entry_points if ent in entry_point_list]
822			if entry_points:
823				func.filter_entry_points(entry_points)
824				functions_by_name[func.name] = func
825
826		self.functions_by_name = functions_by_name
827
828	def process_element(self, doc):
829		element = doc.children
830		while element.type != "element" or element.name != "OpenGLAPI":
831			element = element.next
832
833		if element:
834			self.process_OpenGLAPI(element)
835		return
836
837
838	def process_OpenGLAPI(self, element):
839		child = element.children
840		while child:
841			if child.type == "element":
842				if child.name == "category":
843					self.process_category( child )
844				elif child.name == "OpenGLAPI":
845					self.process_OpenGLAPI( child )
846
847			child = child.next
848
849		return
850
851
852	def process_category(self, cat):
853		cat_name = cat.nsProp( "name", None )
854		cat_number = cat.nsProp( "number", None )
855
856		[cat_type, key] = classify_category(cat_name, cat_number)
857		self.categories[cat_type][key] = [cat_name, cat_number]
858
859		child = cat.children
860		while child:
861			if child.type == "element":
862				if child.name == "function":
863					func_name = real_function_name( child )
864
865					temp_name = child.nsProp( "name", None )
866					self.category_dict[ temp_name ] = [cat_name, cat_number]
867
868					if self.functions_by_name.has_key( func_name ):
869						func = self.functions_by_name[ func_name ]
870						func.process_element( child )
871					else:
872						func = self.factory.create_item( "function", child, self )
873						self.functions_by_name[ func_name ] = func
874
875					if func.offset >= self.next_offset:
876						self.next_offset = func.offset + 1
877
878
879				elif child.name == "enum":
880					enum = self.factory.create_item( "enum", child, self )
881					self.enums_by_name[ enum.name ] = enum
882				elif child.name == "type":
883					t = self.factory.create_item( "type", child, self )
884					self.types_by_name[ "GL" + t.name ] = t
885
886
887			child = child.next
888
889		return
890
891
892	def functionIterateByCategory(self, cat = None):
893		"""Iterate over functions by category.
894
895		If cat is None, all known functions are iterated in category
896		order.  See classify_category for details of the ordering.
897		Within a category, functions are sorted by name.  If cat is
898		not None, then only functions in that category are iterated.
899		"""
900		lists = [{}, {}, {}, {}]
901
902		for func in self.functionIterateAll():
903			[cat_name, cat_number] = self.category_dict[func.name]
904
905			if (cat == None) or (cat == cat_name):
906				[func_cat_type, key] = classify_category(cat_name, cat_number)
907
908				if not lists[func_cat_type].has_key(key):
909					lists[func_cat_type][key] = {}
910
911				lists[func_cat_type][key][func.name] = func
912
913
914		functions = []
915		for func_cat_type in range(0,4):
916			keys = lists[func_cat_type].keys()
917			keys.sort()
918
919			for key in keys:
920				names = lists[func_cat_type][key].keys()
921				names.sort()
922
923				for name in names:
924					functions.append(lists[func_cat_type][key][name])
925
926		return functions.__iter__()
927
928
929	def functionIterateByOffset(self):
930		max_offset = -1
931		for func in self.functions_by_name.itervalues():
932			if func.offset > max_offset:
933				max_offset = func.offset
934
935
936		temp = [None for i in range(0, max_offset + 1)]
937		for func in self.functions_by_name.itervalues():
938			if func.offset != -1:
939				temp[ func.offset ] = func
940
941
942		list = []
943		for i in range(0, max_offset + 1):
944			if temp[i]:
945				list.append(temp[i])
946
947		return list.__iter__();
948
949
950	def functionIterateAll(self):
951		return self.functions_by_name.itervalues()
952
953
954	def enumIterateByName(self):
955		keys = self.enums_by_name.keys()
956		keys.sort()
957
958		list = []
959		for enum in keys:
960			list.append( self.enums_by_name[ enum ] )
961
962		return list.__iter__()
963
964
965	def categoryIterate(self):
966		"""Iterate over categories.
967
968		Iterate over all known categories in the order specified by
969		classify_category.  Each iterated value is a tuple of the
970		name and number (which may be None) of the category.
971		"""
972
973		list = []
974		for cat_type in range(0,4):
975			keys = self.categories[cat_type].keys()
976			keys.sort()
977
978			for key in keys:
979				list.append(self.categories[cat_type][key])
980
981		return list.__iter__()
982
983
984	def get_category_for_name( self, name ):
985		if self.category_dict.has_key(name):
986			return self.category_dict[name]
987		else:
988			return ["<unknown category>", None]
989
990
991	def typeIterate(self):
992		return self.types_by_name.itervalues()
993
994
995	def find_type( self, type_name ):
996		if type_name in self.types_by_name:
997			return self.types_by_name[ type_name ].type_expr
998		else:
999			print "Unable to find base type matching \"%s\"." % (type_name)
1000			return None
1001