Added the ability to get the size of a parameter as a string. Changed the
[profile/ivi/mesa.git] / src / mesa / glapi / gl_XML.py
1 #!/usr/bin/python2
2
3 # (C) Copyright IBM Corporation 2004
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
28 from xml.sax import saxutils
29 from xml.sax import make_parser
30 from xml.sax.handler import feature_namespaces
31
32 import re
33
34 class glItem:
35         """Generic class on which all other API entity types are based."""
36
37         def __init__(self, tag_name, name, context):
38                 self.name = name
39                 self.category = context.get_category_define()
40                 self.context = context
41                 self.tag_name = tag_name
42                 
43                 context.append(tag_name, self)
44                 return
45         
46         def startElement(self, name, attrs):
47                 """Generic startElement handler.
48                 
49                 The startElement handler is called for all elements except
50                 the one that starts the object.  For a foo element, the
51                 XML "<foo><bar/></foo>" would cause the startElement handler
52                 to be called once, but the endElement handler would be called
53                 twice."""
54                 return
55
56         def endElement(self, name):
57                 """Generic endElement handler.
58
59                 Generic endElement handler.    Returns 1 if the tag containing
60                 the object is complete.  Otherwise 0 is returned.  All
61                 derived class endElement handlers should call this method.  If
62                 the name of the ending tag is the same as the tag that
63                 started this object, the object is assumed to be complete.
64                 
65                 This fails if a tag can contain another tag with the same
66                 name.  The XML "<foo><foo/><bar/></foo>" would fail.  The
67                 object would end before the bar tag was processed.
68                 
69                 The endElement handler is called for every end element
70                 associated with an object, even the element that started the
71                 object.  See the description of startElement an example."""
72
73                 if name == self.tag_name:
74                         return 1
75                 else:
76                         return 0
77
78         def get_category_define(self):
79                 return self.category
80
81
82 class glEnum( glItem ):
83         """Subclass of glItem for representing GL enumerants.
84         
85         This class is not complete, and is not really used yet."""
86
87         def __init__(self, context, name, attrs):
88                 self.value = int(attrs.get('value', "0x0000"), 0)
89                 self.functions = {}
90
91                 enum_name = "GL_" + attrs.get('name', None)
92                 glItem.__init__(self, name, enum_name, context)
93
94         def startElement(self, name, attrs):
95                 if name == "size":
96                         name = attrs.get('name', None)
97                         count = int(attrs.get('count', "0"), 0)
98                         self.functions[name] = count
99
100                 return
101
102
103 class glType( glItem ):
104         """Subclass of glItem for representing GL types."""
105
106         def __init__(self, context, name, attrs):
107                 self.size = int(attrs.get('size', "0"))
108
109                 type_name = "GL" + attrs.get('name', None)
110                 glItem.__init__(self, name, type_name, context)
111
112
113 class glParameter( glItem ):
114         """Parameter of a glFunction."""
115         p_type = None
116         p_type_string = ""
117         p_count = 0
118         p_count_parameters = None
119         counter = None
120         is_output = 0
121         is_counter = 0
122         is_pointer = 0
123
124         def __init__(self, context, name, attrs):
125                 p_name = attrs.get('name', None)
126                 self.p_type_string = attrs.get('type', None)
127                 self.p_count_parameters = attrs.get('variable_param', None)
128
129                 self.p_type = context.context.find_type(self.p_type_string)
130                 if self.p_type == None:
131                         raise RuntimeError("Unknown type '%s' in function '%s'." % (self.p_type_string, context.name))
132
133
134                 # The count tag can be either a numeric string or the name of
135                 # a variable.  If it is the name of a variable, the int(c)
136                 # statement will throw an exception, and the except block will
137                 # take over.
138
139                 c = attrs.get('count', "0")
140                 try: 
141                         self.p_count = int(c)
142                         self.counter = None
143                 except Exception,e:
144                         self.p_count = 0
145                         self.counter = c
146
147                 if attrs.get('counter', "false") == "true":
148                         self.is_counter = 1
149                 else:
150                         self.is_counter = 0
151
152                 if attrs.get('output', "false") == "true":
153                         self.is_output = 1
154                 else:
155                         self.is_output = 0
156
157                 if self.p_count > 0 or self.counter != None or self.p_count_parameters != None :
158                         has_count = 1
159                 else:
160                         has_count = 0
161
162
163                 # If there is a * anywhere in the parameter's type, then it
164                 # is a pointer.
165
166                 if re.compile("[*]").search(self.p_type_string):
167                         # We could do some other validation here.  For
168                         # example, an output parameter should not be const,
169                         # but every non-output parameter should.
170
171                         self.is_pointer = 1;
172                 else:
173                         # If a parameter is not a pointer, then there cannot
174                         # be an associated count (either fixed size or
175                         # variable) and the parameter cannot be an output.
176
177                         if has_count or self.is_output:
178                                 raise RuntimeError("Non-pointer type has count or is output.")
179                         self.is_pointer = 0;
180
181                 glItem.__init__(self, name, p_name, context)
182                 return
183
184
185         def is_variable_length_array(self):
186                 """Determine if a parameter is a variable length array.
187                 
188                 A parameter is considered to be a variable length array if
189                 its size depends on the value of another parameter that is
190                 an enumerant.  The params parameter to glTexEnviv is an
191                 example of a variable length array parameter.  Arrays whose
192                 size depends on a count variable, such as the lists parameter
193                 to glCallLists, are not variable length arrays in this
194                 sense."""
195
196                 return (self.p_count_parameters != None) or (self.counter != None)
197
198
199         def is_array(self):
200                 return self.is_pointer
201
202
203         def count_string(self):
204                 """Return a string representing the number of items
205                 
206                 Returns a string representing the number of items in a
207                 parameter.  For scalar types this will always be "1".  For
208                 vector types, it will depend on whether or not it is a
209                 fixed length vector (like the parameter of glVertex3fv),
210                 a counted length (like the vector parameter of
211                 glDeleteTextures), or a general variable length vector."""
212
213                 if self.is_array():
214                         if self.p_count_parameters != None:
215                                 return "compsize"
216                         elif self.counter != None:
217                                 return self.counter
218                         else:
219                                 return str(self.p_count)
220                 else:
221                         return "1"
222
223
224         def size(self):
225                 if self.p_count_parameters != None or self.counter != None or self.is_output:
226                         return 0
227                 elif self.p_count == 0:
228                         return self.p_type.size
229                 else:
230                         return self.p_type.size * self.p_count
231
232         def size_string(self):
233                 s = self.size()
234                 if s == 0:
235                         a_prod = "compsize"
236                         b_prod = self.p_type.size
237
238                         if self.p_count_parameters == None and self.counter != None:
239                                 a_prod = self.counter
240                         elif self.p_count_parameters != None and self.counter == None:
241                                 pass
242                         elif self.p_count_parameters != None and self.counter != None:
243                                 b_prod = self.counter
244                         else:
245                                 raise RuntimeError("Parameter '%s' to function '%s' has size 0." % (self.name, self.context.name))
246
247                         return "(%s * %s)" % (a_prod, b_prod)
248                 else:
249                         return str(s)
250
251
252 class glParameterIterator:
253         """Class to iterate over a list of glParameters.
254         
255         Objects of this class are returned by the __iter__ method of the
256         glFunction class.  They are used to iterate over the list of
257         parameters to the function."""
258
259         def __init__(self, data):
260                 self.data = data
261                 self.index = 0
262                 
263         def next(self):
264                 if self.index == len( self.data ):
265                         raise StopIteration
266                 i = self.index
267                 self.index += 1
268                 return self.data[i]
269
270
271 class glFunction( glItem ):
272         real_name = ""
273         fn_alias = None
274         fn_offset = -1
275         fn_return_type = "void"
276         fn_parameters = []
277
278         def __init__(self, context, name, attrs):
279                 self.fn_alias = attrs.get('alias', None)
280                 self.fn_parameters = []
281
282                 temp = attrs.get('offset', None)
283                 if temp == None or temp == "?":
284                         self.fn_offset = -1
285                 else:
286                         self.fn_offset = int(temp)
287
288                 fn_name = attrs.get('name', None)
289                 if self.fn_alias != None:
290                         self.real_name = self.fn_alias
291                 else:
292                         self.real_name = fn_name
293
294                 glItem.__init__(self, name, fn_name, context)
295                 return
296
297
298         def __iter__(self):
299                 return glParameterIterator(self.fn_parameters)
300
301
302         def startElement(self, name, attrs):
303                 if name == "param":
304                         try:
305                                 self.context.factory.create(self, name, attrs)
306                         except RuntimeError:
307                                 print "Error with parameter '%s' in function '%s'." \
308                                         % (attrs.get('name','(unknown)'), self.name)
309                                 raise
310                 elif name == "return":
311                         self.set_return_type(attrs.get('type', None))
312
313
314         def append(self, tag_name, p):
315                 if tag_name != "param":
316                         raise RuntimeError("Trying to append '%s' to parameter list of function '%s'." % (tag_name, self.name))
317
318                 self.fn_parameters.append(p)
319
320
321         def set_return_type(self, t):
322                 self.fn_return_type = t
323
324
325         def get_parameter_string(self):
326                 arg_string = ""
327                 comma = ""
328                 for p in self:
329                         arg_string = arg_string + comma + p.p_type_string + " " + p.name
330                         comma = ", "
331
332                 if arg_string == "":
333                         arg_string = "void"
334
335                 return arg_string
336
337
338 class glItemFactory:
339         """Factory to create objects derived from glItem."""
340     
341         def create(self, context, name, attrs):
342                 if name == "function":
343                         return glFunction(context, name, attrs)
344                 elif name == "type":
345                         return glType(context, name, attrs)
346                 elif name == "enum":
347                         return glEnum(context, name, attrs)
348                 elif name == "param":
349                         return glParameter(context, name, attrs)
350                 else:
351                         return None
352
353
354 class FilterGLAPISpecBase(saxutils.XMLFilterBase):
355         name = "a"
356         license = "The license for this file is unspecified."
357         functions = {}
358         next_alias = -2
359         types = {}
360         xref = {}
361         current_object = None
362         factory = None
363         current_category = ""
364
365         def __init__(self):
366                 saxutils.XMLFilterBase.__init__(self)
367                 self.functions = {}
368                 self.types = {}
369                 self.xref = {}
370                 self.factory = glItemFactory()
371
372
373         def find_type(self,type_name):
374                 for t in self.types:
375                         if re.compile(t).search(type_name):
376                                 return self.types[t]
377                 print "Unable to find base type matching \"%s\"." % (type_name)
378                 return None
379
380
381         def find_function(self,function_name):
382                 index = self.xref[function_name]
383                 return self.functions[index]
384
385
386         def printFunctions(self):
387                 keys = self.functions.keys()
388                 keys.sort()
389                 prevk = -1
390                 for k in keys:
391                         if k < 0: continue
392
393                         if self.functions[k].fn_alias == None:
394                                 if k != prevk + 1:
395                                         #print 'Missing offset %d' % (prevk)
396                                         pass
397                         prevk = int(k)
398                         self.printFunction(self.functions[k])
399
400                 keys.reverse()
401                 for k in keys:
402                         if self.functions[k].fn_alias != None:
403                                 self.printFunction(self.functions[k])
404
405                 return
406
407
408         def printHeader(self):
409                 """Print the header associated with all files and call the printRealHeader method."""
410
411                 print '/* DO NOT EDIT - This file generated automatically by %s script */' \
412                         % (self.name)
413                 print ''
414                 print '/*'
415                 print ' * ' + self.license.replace('\n', '\n * ')
416                 print ' */'
417                 print ''
418                 self.printRealHeader();
419                 return
420
421
422         def printFooter(self):
423                 """Print the header associated with all files and call the printRealFooter method."""
424
425                 self.printFunctions()
426                 self.printRealFooter()
427
428
429         def get_category_define(self):
430                 """Convert the category name to the #define that would be found in glext.h"""
431
432                 if re.compile("[1-9][0-9]*[.][0-9]+").match(self.current_category):
433                         s = self.current_category
434                         return "GL_VERSION_" + s.replace(".", "_")
435                 else:
436                         return self.current_category
437
438
439         def append(self, object_type, obj):
440                 if object_type == "function":
441                         # If the function is not an alias and has a negative
442                         # offset, then we do not need to track it.  These are
443                         # functions that don't have an assigned offset
444
445                         if obj.fn_offset >= 0 or obj.fn_alias != None:
446                                 if obj.fn_offset >= 0:
447                                         index = obj.fn_offset
448                                 else:
449                                         index = self.next_alias
450                                         self.next_alias -= 1
451
452                                 self.functions[index] = obj
453                                 self.xref[obj.name] = index
454                 elif object_type == "type":
455                         self.types[obj.name] = obj
456
457                 return
458
459
460         def startElement(self, name, attrs):
461                 """Start a new element in the XML stream.
462                 
463                 Starts a new element.  There are three types of elements that
464                 are specially handled by this function.  When a "category"
465                 element is encountered, the name of the category is saved.
466                 If an element is encountered and no API object is
467                 in-progress, a new object is created using the API factory.
468                 Any future elements, until that API object is closed, are
469                 passed to the current objects startElement method.
470         
471                 This paradigm was chosen becuase it allows subclasses of the
472                 basic API types (i.e., glFunction, glEnum, etc.) to handle
473                 additional XML data, GLX protocol information,  that the base
474                 classes do not know about."""
475
476                 if self.current_object != None:
477                         self.current_object.startElement(name, attrs)
478                 elif name == "category":
479                         self.current_category = attrs.get('name', "")
480                 else:
481                         self.current_object = self.factory.create(self, name, attrs)
482                 return
483
484
485         def endElement(self, name):
486                 if self.current_object != None:
487                         if self.current_object.endElement(name):
488                                 self.current_object = None
489                 return
490
491
492         def printFunction(self,offset):
493                 """Print a single function.
494
495                 In the base class, this function is empty.  All derived
496                 classes should over-ride this function."""
497                 return
498     
499
500         def printRealHeader(self):
501                 """Print the "real" header for the created file.
502
503                 In the base class, this function is empty.  All derived
504                 classes should over-ride this function."""
505                 return
506
507
508         def printRealFooter(self):
509                 """Print the "real" footer for the created file.
510
511                 In the base class, this function is empty.  All derived
512                 classes should over-ride this function."""
513                 return