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