Refactor the code to emit multiple-inclusion protection to
[mesa.git] / src / mesa / glapi / gl_XML.py
1 #!/usr/bin/python2
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
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
90 enum_name = "GL_" + attrs.get('name', None)
91 glItem.__init__(self, name, enum_name, context)
92
93 temp = attrs.get('count', None)
94 if temp == None:
95 self.default_count = 0
96 else:
97 try:
98 c = int(temp)
99 except Exception,e:
100 raise RuntimeError('Invalid count value "%s" for enum "%s" in function "%s" when an integer was expected.' % (temp, self.name, n))
101
102 self.default_count = c
103 return
104
105
106 def process_attributes(self, attrs):
107 name = attrs.get('name', None)
108
109 temp = attrs.get('count', None)
110 if temp == None:
111 c = self.default_count
112 else:
113 try:
114 c = int(temp)
115 except Exception,e:
116 raise RuntimeError('Invalid count value "%s" for enum "%s" in function "%s" when an integer was expected.' % (temp, self.name, n))
117
118 mode_str = attrs.get('mode', "set")
119 if mode_str == "set":
120 mode = 1
121 elif mode_str == "get":
122 mode = 0
123 else:
124 raise RuntimeError("Invalid mode '%s' for function '%s' in enum '%s'." % (mode_str, self.context.name, self.name))
125
126 return [name, c, mode]
127
128
129 class glType( glItem ):
130 """Subclass of glItem for representing GL types."""
131
132 def __init__(self, context, name, attrs):
133 self.size = int(attrs.get('size', "0"))
134 self.glx_name = attrs.get('glx_name', "")
135
136 type_name = "GL" + attrs.get('name', None)
137 glItem.__init__(self, name, type_name, context)
138
139
140 class glParameter( glItem ):
141 """Parameter of a glFunction."""
142 p_type = None
143 p_type_string = ""
144 p_count = 0
145 p_count_parameters = None
146 counter = None
147 is_output = 0
148 is_counter = 0
149 is_pointer = 0
150
151 def __init__(self, context, name, attrs):
152 p_name = attrs.get('name', None)
153 self.p_type_string = attrs.get('type', None)
154 self.p_count_parameters = attrs.get('variable_param', None)
155
156 self.p_type = context.context.find_type(self.p_type_string)
157 if self.p_type == None:
158 raise RuntimeError("Unknown type '%s' in function '%s'." % (self.p_type_string, context.name))
159
160
161 # The count tag can be either a numeric string or the name of
162 # a variable. If it is the name of a variable, the int(c)
163 # statement will throw an exception, and the except block will
164 # take over.
165
166 c = attrs.get('count', "0")
167 try:
168 self.p_count = int(c)
169 self.counter = None
170 except Exception,e:
171 self.p_count = 0
172 self.counter = c
173
174 if attrs.get('counter', "false") == "true":
175 self.is_counter = 1
176 else:
177 self.is_counter = 0
178
179 if attrs.get('output', "false") == "true":
180 self.is_output = 1
181 else:
182 self.is_output = 0
183
184
185 # Pixel data has special parameters.
186
187 self.width = attrs.get('img_width', None)
188 self.height = attrs.get('img_height', None)
189 self.depth = attrs.get('img_depth', None)
190 self.extent = attrs.get('img_extent', None)
191
192 self.img_xoff = attrs.get('img_xoff', None)
193 self.img_yoff = attrs.get('img_yoff', None)
194 self.img_zoff = attrs.get('img_zoff', None)
195 self.img_woff = attrs.get('img_woff', None)
196
197 self.img_format = attrs.get('img_format', None)
198 self.img_type = attrs.get('img_type', None)
199 self.img_target = attrs.get('img_target', None)
200
201 pad = attrs.get('img_pad_dimensions', "false")
202 if pad == "true":
203 self.img_pad_dimensions = 1
204 else:
205 self.img_pad_dimensions = 0
206
207
208 null_flag = attrs.get('img_null_flag', "false")
209 if null_flag == "true":
210 self.img_null_flag = 1
211 else:
212 self.img_null_flag = 0
213
214 send_null = attrs.get('img_send_null', "false")
215 if send_null == "true":
216 self.img_send_null = 1
217 else:
218 self.img_send_null = 0
219
220
221
222 if self.p_count > 0 or self.counter or self.p_count_parameters:
223 has_count = 1
224 else:
225 has_count = 0
226
227
228 # If there is a * anywhere in the parameter's type, then it
229 # is a pointer.
230
231 if re.compile("[*]").search(self.p_type_string):
232 # We could do some other validation here. For
233 # example, an output parameter should not be const,
234 # but every non-output parameter should.
235
236 self.is_pointer = 1;
237 else:
238 # If a parameter is not a pointer, then there cannot
239 # be an associated count (either fixed size or
240 # variable) and the parameter cannot be an output.
241
242 if has_count or self.is_output:
243 raise RuntimeError("Non-pointer type has count or is output.")
244 self.is_pointer = 0;
245
246 glItem.__init__(self, name, p_name, context)
247 return
248
249
250 def is_variable_length_array(self):
251 """Determine if a parameter is a variable length array.
252
253 A parameter is considered to be a variable length array if
254 its size depends on the value of another parameter that is
255 an enumerant. The params parameter to glTexEnviv is an
256 example of a variable length array parameter. Arrays whose
257 size depends on a count variable, such as the lists parameter
258 to glCallLists, are not variable length arrays in this
259 sense."""
260
261 return self.p_count_parameters or self.counter or self.width
262
263
264 def is_array(self):
265 return self.is_pointer
266
267
268 def count_string(self):
269 """Return a string representing the number of items
270
271 Returns a string representing the number of items in a
272 parameter. For scalar types this will always be "1". For
273 vector types, it will depend on whether or not it is a
274 fixed length vector (like the parameter of glVertex3fv),
275 a counted length (like the vector parameter of
276 glDeleteTextures), or a general variable length vector."""
277
278 if self.is_array():
279 if self.p_count_parameters != None:
280 return "compsize"
281 elif self.counter != None:
282 return self.counter
283 else:
284 return str(self.p_count)
285 else:
286 return "1"
287
288
289 def size(self):
290 if self.p_count_parameters or self.counter or self.width or self.is_output:
291 return 0
292 elif self.p_count == 0:
293 return self.p_type.size
294 else:
295 return self.p_type.size * self.p_count
296
297 def size_string(self):
298 s = self.size()
299 if s == 0:
300 a_prod = "compsize"
301 b_prod = self.p_type.size
302
303 if self.p_count_parameters == None and self.counter != None:
304 a_prod = self.counter
305 elif self.p_count_parameters != None and self.counter == None:
306 pass
307 elif self.p_count_parameters != None and self.counter != None:
308 b_prod = self.counter
309 elif self.width:
310 return "compsize"
311 else:
312 raise RuntimeError("Parameter '%s' to function '%s' has size 0." % (self.name, self.context.name))
313
314 return "(%s * %s)" % (a_prod, b_prod)
315 else:
316 return str(s)
317
318
319 class glParameterIterator:
320 """Class to iterate over a list of glParameters.
321
322 Objects of this class are returned by the parameterIterator method of
323 the glFunction class. They are used to iterate over the list of
324 parameters to the function."""
325
326 def __init__(self, data):
327 self.data = data
328 self.index = 0
329
330 def __iter__(self):
331 return self
332
333 def next(self):
334 if self.index == len( self.data ):
335 raise StopIteration
336 i = self.index
337 self.index += 1
338 return self.data[i]
339
340
341 class glFunction( glItem ):
342 real_name = ""
343 fn_alias = None
344 fn_offset = -1
345 fn_return_type = "void"
346 fn_parameters = []
347
348 def __init__(self, context, name, attrs):
349 self.fn_alias = attrs.get('alias', None)
350 self.fn_parameters = []
351 self.image = None
352
353 temp = attrs.get('offset', None)
354 if temp == None or temp == "?":
355 self.fn_offset = -1
356 else:
357 self.fn_offset = int(temp)
358
359 fn_name = attrs.get('name', None)
360 if self.fn_alias != None:
361 self.real_name = self.fn_alias
362 else:
363 self.real_name = fn_name
364
365 glItem.__init__(self, name, fn_name, context)
366 return
367
368
369 def parameterIterator(self):
370 return glParameterIterator(self.fn_parameters)
371
372
373 def startElement(self, name, attrs):
374 if name == "param":
375 try:
376 self.context.factory.create(self, name, attrs)
377 except RuntimeError:
378 print "Error with parameter '%s' in function '%s'." \
379 % (attrs.get('name','(unknown)'), self.name)
380 raise
381 elif name == "return":
382 self.set_return_type(attrs.get('type', None))
383
384
385 def append(self, tag_name, p):
386 if tag_name != "param":
387 raise RuntimeError("Trying to append '%s' to parameter list of function '%s'." % (tag_name, self.name))
388
389 if p.width:
390 self.image = p
391
392 self.fn_parameters.append(p)
393
394
395 def set_return_type(self, t):
396 self.fn_return_type = t
397
398
399 def get_parameter_string(self):
400 arg_string = ""
401 comma = ""
402 for p in glFunction.parameterIterator(self):
403 arg_string = arg_string + comma + p.p_type_string + " " + p.name
404 comma = ", "
405
406 if arg_string == "":
407 arg_string = "void"
408
409 return arg_string
410
411
412 class glItemFactory:
413 """Factory to create objects derived from glItem."""
414
415 def create(self, context, name, attrs):
416 if name == "function":
417 return glFunction(context, name, attrs)
418 elif name == "type":
419 return glType(context, name, attrs)
420 elif name == "enum":
421 return glEnum(context, name, attrs)
422 elif name == "param":
423 return glParameter(context, name, attrs)
424 else:
425 return None
426
427
428 class glFunctionIterator:
429 """Class to iterate over a list of glFunctions
430
431 Objects of this classare returned by
432 FilterGLAPISpecBase::functionIterator. This default version
433 iterates over the functions in order of dispatch table offset. All
434 of the "true" functions are iterated first, followed by the alias
435 functions."""
436
437 def __init__(self, context):
438 self.context = context
439 self.keys = context.functions.keys()
440 self.keys.sort()
441
442 self.prevk = -1
443 self.direction = 1
444
445 for self.index in range(0, len(self.keys)):
446 if self.keys[ self.index ] >= 0: break
447
448 if self.index == len(self.keys):
449 self.direction = -1
450 self.index -= 1
451
452 self.split = self.index - 1
453 return
454
455
456 def __iter__(self):
457 return self
458
459
460 def next(self):
461 if self.index < 0:
462 raise StopIteration
463
464 k = self.keys[ self.index ]
465
466 #if self.context.functions[k].fn_alias == None:
467 # if k != self.prevk + 1:
468 # print 'Missing offset %d' % (prevk)
469 # self.prevk = int(k)
470
471 self.index += self.direction
472
473 if self.index == len(self.keys):
474 self.index = self.split
475 self.direction = -1
476
477 return self.context.functions[k]
478
479
480 class FilterGLAPISpecBase(saxutils.XMLFilterBase):
481 name = "a"
482 license = "The license for this file is unspecified."
483 functions = {}
484 next_alias = -2
485 types = {}
486 xref = {}
487 current_object = None
488 factory = None
489 current_category = ""
490
491 def __init__(self):
492 saxutils.XMLFilterBase.__init__(self)
493 self.functions = {}
494 self.types = {}
495 self.xref = {}
496 self.factory = glItemFactory()
497 self.header_tag = None
498
499
500 def find_type(self,type_name):
501 for t in self.types:
502 if re.compile(t).search(type_name):
503 return self.types[t]
504 print "Unable to find base type matching \"%s\"." % (type_name)
505 return None
506
507
508 def find_function(self,function_name):
509 index = self.xref[function_name]
510 return self.functions[index]
511
512
513 def functionIterator(self):
514 return glFunctionIterator(self)
515
516
517 def printFunctions(self):
518 for f in self.functionIterator():
519 self.printFunction(f)
520 return
521
522
523 def printHeader(self):
524 """Print the header associated with all files and call the printRealHeader method."""
525
526 print '/* DO NOT EDIT - This file generated automatically by %s script */' \
527 % (self.name)
528 print ''
529 print '/*'
530 print ' * ' + self.license.replace('\n', '\n * ')
531 print ' */'
532 print ''
533 if self.header_tag:
534 print '#if !defined( %s )' % (self.header_tag)
535 print '# define %s' % (self.header_tag)
536 print ''
537 self.printRealHeader();
538 return
539
540
541 def printFooter(self):
542 """Print the header associated with all files and call the printRealFooter method."""
543
544 self.printFunctions()
545 self.printRealFooter()
546 if self.header_tag:
547 print ''
548 print '#endif /* !defined( %s ) */' % (self.header_tag)
549
550
551 def get_category_define(self):
552 """Convert the category name to the #define that would be found in glext.h"""
553
554 if re.compile("[1-9][0-9]*[.][0-9]+").match(self.current_category):
555 s = self.current_category
556 return "GL_VERSION_" + s.replace(".", "_")
557 else:
558 return self.current_category
559
560
561 def append(self, object_type, obj):
562 if object_type == "function":
563 # If the function is not an alias and has a negative
564 # offset, then we do not need to track it. These are
565 # functions that don't have an assigned offset
566
567 if obj.fn_offset >= 0 or obj.fn_alias != None:
568 if obj.fn_offset >= 0:
569 index = obj.fn_offset
570 else:
571 index = self.next_alias
572 self.next_alias -= 1
573
574 self.functions[index] = obj
575 self.xref[obj.name] = index
576 elif object_type == "type":
577 self.types[obj.name] = obj
578
579 return
580
581
582 def startElement(self, name, attrs):
583 """Start a new element in the XML stream.
584
585 Starts a new element. There are three types of elements that
586 are specially handled by this function. When a "category"
587 element is encountered, the name of the category is saved.
588 If an element is encountered and no API object is
589 in-progress, a new object is created using the API factory.
590 Any future elements, until that API object is closed, are
591 passed to the current objects startElement method.
592
593 This paradigm was chosen becuase it allows subclasses of the
594 basic API types (i.e., glFunction, glEnum, etc.) to handle
595 additional XML data, GLX protocol information, that the base
596 classes do not know about."""
597
598 if self.current_object != None:
599 self.current_object.startElement(name, attrs)
600 elif name == "category":
601 self.current_category = attrs.get('name', "")
602 else:
603 self.current_object = self.factory.create(self, name, attrs)
604 return
605
606
607 def endElement(self, name):
608 if self.current_object != None:
609 if self.current_object.endElement(name):
610 self.current_object = None
611 return
612
613
614 def printFunction(self,offset):
615 """Print a single function.
616
617 In the base class, this function is empty. All derived
618 classes should over-ride this function."""
619 return
620
621
622 def printRealHeader(self):
623 """Print the "real" header for the created file.
624
625 In the base class, this function is empty. All derived
626 classes should over-ride this function."""
627 return
628
629
630 def printRealFooter(self):
631 """Print the "real" footer for the created file.
632
633 In the base class, this function is empty. All derived
634 classes should over-ride this function."""
635 return