shorter error messages when calling a GL function without a bound context (FDO bug...
[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
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.is_variable_length_array():
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.is_variable_length_array():
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
233 class glParameterIterator:
234 """Class to iterate over a list of glParameters.
235
236 Objects of this class are returned by the __iter__ method of the
237 glFunction class. They are used to iterate over the list of
238 parameters to the function."""
239
240 def __init__(self, data):
241 self.data = data
242 self.index = 0
243
244 def next(self):
245 if self.index == len( self.data ):
246 raise StopIteration
247 i = self.index
248 self.index += 1
249 return self.data[i]
250
251
252 class glFunction( glItem ):
253 real_name = ""
254 fn_alias = None
255 fn_offset = -1
256 fn_return_type = "void"
257 fn_parameters = []
258
259 def __init__(self, context, name, attrs):
260 self.fn_alias = attrs.get('alias', None)
261 self.fn_parameters = []
262
263 temp = attrs.get('offset', None)
264 if temp == None or temp == "?":
265 self.fn_offset = -1
266 else:
267 self.fn_offset = int(temp)
268
269 fn_name = attrs.get('name', None)
270 if self.fn_alias != None:
271 self.real_name = self.fn_alias
272 else:
273 self.real_name = fn_name
274
275 glItem.__init__(self, name, fn_name, context)
276 return
277
278
279 def __iter__(self):
280 return glParameterIterator(self.fn_parameters)
281
282
283 def startElement(self, name, attrs):
284 if name == "param":
285 try:
286 glParameter(self, name, attrs)
287 except RuntimeError:
288 print "Error with parameter '%s' in function '%s'." \
289 % (attrs.get('name','(unknown)'), self.name)
290 raise
291 elif name == "return":
292 self.set_return_type(attrs.get('type', None))
293
294
295 def append(self, tag_name, p):
296 if tag_name != "param":
297 raise RuntimeError("Trying to append '%s' to parameter list of function '%s'." % (tag_name, self.name))
298
299 self.fn_parameters.append(p)
300
301
302 def set_return_type(self, t):
303 self.fn_return_type = t
304
305
306 def get_parameter_string(self):
307 arg_string = ""
308 comma = ""
309 for p in self:
310 arg_string = arg_string + comma + p.p_type_string + " " + p.name
311 comma = ", "
312
313 if arg_string == "":
314 arg_string = "void"
315
316 return arg_string
317
318
319 class glItemFactory:
320 """Factory to create objects derived from glItem."""
321
322 def create(self, context, name, attrs):
323 if name == "function":
324 return glFunction(context, name, attrs)
325 elif name == "type":
326 return glType(context, name, attrs)
327 elif name == "enum":
328 return glEnum(context, name, attrs)
329 else:
330 return None
331
332
333 class FilterGLAPISpecBase(saxutils.XMLFilterBase):
334 name = "a"
335 license = "The license for this file is unspecified."
336 functions = {}
337 next_alias = -2
338 types = {}
339 xref = {}
340 current_object = None
341 factory = None
342 current_category = ""
343
344 def __init__(self):
345 saxutils.XMLFilterBase.__init__(self)
346 self.functions = {}
347 self.types = {}
348 self.xref = {}
349 self.factory = glItemFactory()
350
351
352 def find_type(self,type_name):
353 for t in self.types:
354 if re.compile(t).search(type_name):
355 return self.types[t]
356 print "Unable to find base type matching \"%s\"." % (type_name)
357 return None
358
359
360 def find_function(self,function_name):
361 index = self.xref[function_name]
362 return self.functions[index]
363
364
365 def printFunctions(self):
366 keys = self.functions.keys()
367 keys.sort()
368 prevk = -1
369 for k in keys:
370 if k < 0: continue
371
372 if self.functions[k].fn_alias == None:
373 if k != prevk + 1:
374 #print 'Missing offset %d' % (prevk)
375 pass
376 prevk = int(k)
377 self.printFunction(self.functions[k])
378
379 keys.reverse()
380 for k in keys:
381 if self.functions[k].fn_alias != None:
382 self.printFunction(self.functions[k])
383
384 return
385
386
387 def printHeader(self):
388 """Print the header associated with all files and call the printRealHeader method."""
389
390 print '/* DO NOT EDIT - This file generated automatically by %s script */' \
391 % (self.name)
392 print ''
393 print '/*'
394 print ' * ' + self.license.replace('\n', '\n * ')
395 print ' */'
396 print ''
397 self.printRealHeader();
398 return
399
400
401 def printFooter(self):
402 """Print the header associated with all files and call the printRealFooter method."""
403
404 self.printFunctions()
405 self.printRealFooter()
406
407
408 def get_category_define(self):
409 """Convert the category name to the #define that would be found in glext.h"""
410
411 if re.compile("[1-9][0-9]*[.][0-9]+").match(self.current_category):
412 s = self.current_category
413 return "GL_VERSION_" + s.replace(".", "_")
414 else:
415 return self.current_category
416
417
418 def append(self, object_type, obj):
419 if object_type == "function":
420 # If the function is not an alias and has a negative
421 # offset, then we do not need to track it. These are
422 # functions that don't have an assigned offset
423
424 if obj.fn_offset >= 0 or obj.fn_alias != None:
425 if obj.fn_offset >= 0:
426 index = obj.fn_offset
427 else:
428 index = self.next_alias
429 self.next_alias -= 1
430
431 self.functions[index] = obj
432 self.xref[obj.name] = index
433 elif object_type == "type":
434 self.types[obj.name] = obj
435
436 return
437
438
439 def startElement(self, name, attrs):
440 """Start a new element in the XML stream.
441
442 Starts a new element. There are three types of elements that
443 are specially handled by this function. When a "category"
444 element is encountered, the name of the category is saved.
445 If an element is encountered and no API object is
446 in-progress, a new object is created using the API factory.
447 Any future elements, until that API object is closed, are
448 passed to the current objects startElement method.
449
450 This paradigm was chosen becuase it allows subclasses of the
451 basic API types (i.e., glFunction, glEnum, etc.) to handle
452 additional XML data, GLX protocol information, that the base
453 classes do not know about."""
454
455 if self.current_object != None:
456 self.current_object.startElement(name, attrs)
457 elif name == "category":
458 self.current_category = attrs.get('name', "")
459 else:
460 self.current_object = self.factory.create(self, name, attrs)
461 return
462
463
464 def endElement(self, name):
465 if self.current_object != None:
466 if self.current_object.endElement(name):
467 self.current_object = None
468 return
469
470
471 def printFunction(self,offset):
472 """Print a single function.
473
474 In the base class, this function is empty. All derived
475 classes should over-ride this function."""
476 return
477
478
479 def printRealHeader(self):
480 """Print the "real" header for the created file.
481
482 In the base class, this function is empty. All derived
483 classes should over-ride this function."""
484 return
485
486
487 def printRealFooter(self):
488 """Print the "real" footer for the created file.
489
490 In the base class, this function is empty. All derived
491 classes should over-ride this function."""
492 return