1#!/usr/local/bin/python3.8 2# 3# Author: Jashua R. Cloutier (contact via https://bitbucket.org/senex) 4# Project: http://senexcanis.com/open-source/cppheaderparser/ 5# 6# Copyright (C) 2011, Jashua R. Cloutier 7# All rights reserved. 8# 9# Redistribution and use in source and binary forms, with or without 10# modification, are permitted provided that the following conditions 11# are met: 12# 13# * Redistributions of source code must retain the above copyright 14# notice, this list of conditions and the following disclaimer. 15# 16# * Redistributions in binary form must reproduce the above copyright 17# notice, this list of conditions and the following disclaimer in 18# the documentation and/or other materials provided with the 19# distribution. 20# 21# * Neither the name of Jashua R. Cloutier nor the names of its 22# contributors may be used to endorse or promote products derived from 23# this software without specific prior written permission. Stories, 24# blog entries etc making reference to this project may mention the 25# name Jashua R. Cloutier in terms of project originator/creator etc. 26# 27# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 28# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 29# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 30# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 31# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 32# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 33# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 34# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 35# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 36# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 37# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 38# POSSIBILITY OF SUCH DAMAGE. 39# 40# 41# The CppHeaderParser.py script is written in Python 2.4 and released to 42# the open source community for continuous improvements under the BSD 43# 2.0 new license, which can be found at: 44# 45# http://www.opensource.org/licenses/bsd-license.php 46# 47"""Parse C++ header files and generate a data structure 48representing the class 49""" 50 51import ply.lex as lex 52import os 53import sys 54import re 55 56import inspect 57 58def lineno(): 59 """Returns the current line number in our program.""" 60 return inspect.currentframe().f_back.f_lineno 61 62version = __version__ = "2.7.4" 63 64tokens = [ 65 'NUMBER', 66 'FLOAT_NUMBER', 67 'TEMPLATE_NAME', 68 'NAME', 69 'OPEN_PAREN', 70 'CLOSE_PAREN', 71 'OPEN_BRACE', 72 'CLOSE_BRACE', 73 'OPEN_SQUARE_BRACKET', 74 'CLOSE_SQUARE_BRACKET', 75 'COLON', 76 'SEMI_COLON', 77 'COMMA', 78 'TAB', 79 'BACKSLASH', 80 'PIPE', 81 'PERCENT', 82 'EXCLAMATION', 83 'CARET', 84 'COMMENT_SINGLELINE', 85 'COMMENT_MULTILINE', 86 'PRECOMP_MACRO', 87 'PRECOMP_MACRO_CONT', 88 'ASTERISK', 89 'AMPERSTAND', 90 'EQUALS', 91 'MINUS', 92 'PLUS', 93 'DIVIDE', 94 'CHAR_LITERAL', 95 'STRING_LITERAL', 96 'NEW_LINE', 97 'SQUOTE', 98] 99 100t_ignore = " \r.?@\f" 101t_NUMBER = r'[0-9][0-9XxA-Fa-f]*' 102t_FLOAT_NUMBER = r'[-+]?[0-9]*\.[0-9]+([eE][-+]?[0-9]+)?' 103t_TEMPLATE_NAME = r'CppHeaderParser_template_[0-9]+' 104t_NAME = r'[<>A-Za-z_~][A-Za-z0-9_]*' 105t_OPEN_PAREN = r'\(' 106t_CLOSE_PAREN = r'\)' 107t_OPEN_BRACE = r'{' 108t_CLOSE_BRACE = r'}' 109t_OPEN_SQUARE_BRACKET = r'\[' 110t_CLOSE_SQUARE_BRACKET = r'\]' 111t_SEMI_COLON = r';' 112t_COLON = r':' 113t_COMMA = r',' 114t_TAB = r'\t' 115t_BACKSLASH = r'\\' 116t_PIPE = r'\|' 117t_PERCENT = r'%' 118t_CARET = r'\^' 119t_EXCLAMATION = r'!' 120t_PRECOMP_MACRO = r'\#.*' 121t_PRECOMP_MACRO_CONT = r'.*\\\n' 122def t_COMMENT_SINGLELINE(t): 123 r'\/\/.*\n' 124 global doxygenCommentCache 125 if t.value.startswith("///") or t.value.startswith("//!"): 126 if doxygenCommentCache: 127 doxygenCommentCache += "\n" 128 if t.value.endswith("\n"): 129 doxygenCommentCache += t.value[:-1] 130 else: 131 doxygenCommentCache += t.value 132 t.lexer.lineno += len([a for a in t.value if a=="\n"]) 133t_ASTERISK = r'\*' 134t_MINUS = r'\-' 135t_PLUS = r'\+' 136t_DIVIDE = r'/(?!/)' 137t_AMPERSTAND = r'&' 138t_EQUALS = r'=' 139t_CHAR_LITERAL = "'.'" 140t_SQUOTE = "'" 141#found at http://wordaligned.org/articles/string-literals-and-regular-expressions 142#TODO: This does not work with the string "bla \" bla" 143t_STRING_LITERAL = r'"([^"\\]|\\.)*"' 144#Found at http://ostermiller.org/findcomment.html 145def t_COMMENT_MULTILINE(t): 146 r'/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/' 147 global doxygenCommentCache 148 if t.value.startswith("/**") or t.value.startswith("/*!"): 149 #not sure why, but get double new lines 150 v = t.value.replace("\n\n", "\n") 151 #strip prefixing whitespace 152 v = re.sub("\n[\s]+\*", "\n*", v) 153 doxygenCommentCache += v 154 t.lexer.lineno += len([a for a in t.value if a=="\n"]) 155def t_NEWLINE(t): 156 r'\n+' 157 t.lexer.lineno += len(t.value) 158 159def t_error(v): 160 print(( "Lex error: ", v )) 161 162lex.lex() 163# Controls error_print 164print_errors = 1 165# Controls warning_print 166print_warnings = 1 167# Controls debug_print 168debug = 0 169# Controls trace_print 170debug_trace = 0 171 172def error_print(arg): 173 if print_errors: print(("[%4d] %s"%(inspect.currentframe().f_back.f_lineno, arg))) 174 175def warning_print(arg): 176 if print_warnings: print(("[%4d] %s"%(inspect.currentframe().f_back.f_lineno, arg))) 177 178def debug_print(arg): 179 global debug 180 if debug: print(("[%4d] %s"%(inspect.currentframe().f_back.f_lineno, arg))) 181 182def trace_print(*arg): 183 global debug_trace 184 if debug_trace: 185 sys.stdout.write("[%s] "%(inspect.currentframe().f_back.f_lineno)) 186 for a in arg: sys.stdout.write("%s "%a) 187 sys.stdout.write("\n") 188 189supportedAccessSpecifier = [ 190 'public', 191 'protected', 192 'private' 193] 194 195#Symbols to ignore, usually special macros 196ignoreSymbols = [ 197 'Q_OBJECT', 198] 199 200doxygenCommentCache = "" 201 202#Track what was added in what order and at what depth 203parseHistory = [] 204 205def is_namespace(nameStack): 206 """Determines if a namespace is being specified""" 207 if len(nameStack) == 0: 208 return False 209 if nameStack[0] == "namespace": 210 return True 211 return False 212 213def is_enum_namestack(nameStack): 214 """Determines if a namestack is an enum namestack""" 215 if len(nameStack) == 0: 216 return False 217 if nameStack[0] == "enum": 218 return True 219 if len(nameStack) > 1 and nameStack[0] == "typedef" and nameStack[1] == "enum": 220 return True 221 return False 222 223def is_fundamental(s): 224 for a in s.split(): 225 if a not in ["size_t", "struct", "union", "unsigned", "signed", "bool", "char", "short", "int", "float", "double", "long", "void", "*"]: return False 226 return True 227 228def is_function_pointer_stack(stack): 229 """Count how many non-nested paranthesis are in the stack. Useful for determining if a stack is a function pointer""" 230 paren_depth = 0 231 paren_count = 0 232 star_after_first_paren = False 233 last_e = None 234 for e in stack: 235 if e == "(": 236 paren_depth += 1 237 elif e == ")" and paren_depth > 0: 238 paren_depth -= 1 239 if paren_depth == 0: 240 paren_count += 1 241 elif e == "*" and last_e == "(" and paren_count == 0 and paren_depth == 1: 242 star_after_first_paren = True 243 last_e = e 244 245 if star_after_first_paren and paren_count == 2: 246 return True 247 else: 248 return False 249 250def is_method_namestack(stack): 251 r = False 252 if '(' not in stack: r = False 253 elif stack[0] == 'typedef': r = False # TODO deal with typedef function prototypes 254 #elif '=' in stack and stack.index('=') < stack.index('(') and stack[stack.index('=')-1] != 'operator': r = False #disabled July6th - allow all operators 255 elif 'operator' in stack: r = True # allow all operators 256 elif '{' in stack and stack.index('{') < stack.index('('): r = False # struct that looks like a method/class 257 elif '(' in stack and ')' in stack: 258 if '{' in stack and '}' in stack: r = True 259 elif stack[-1] == ';': 260 if is_function_pointer_stack(stack): 261 r = False 262 else: 263 r = True 264 elif '{' in stack: r = True # ideally we catch both braces... TODO 265 else: r = False 266 #Test for case of property set to something with parens such as "static const int CONST_A = (1 << 7) - 1;" 267 if r and "(" in stack and "=" in stack and 'operator' not in stack: 268 if stack.index("=") < stack.index("("): r = False 269 return r 270 271def is_property_namestack(nameStack): 272 r = False 273 if '(' not in nameStack and ')' not in nameStack: r = True 274 elif "(" in nameStack and "=" in nameStack and nameStack.index("=") < nameStack.index("("): r = True 275 #See if we are a function pointer 276 if not r and is_function_pointer_stack(nameStack): r = True 277 return r 278 279def detect_lineno(s): 280 """Detect the line number for a given token string""" 281 try: 282 rtn = s.lineno() 283 if rtn != -1: 284 return rtn 285 except: pass 286 global curLine 287 return curLine 288 289def filter_out_attribute_keyword(stack): 290 """Strips __attribute__ and its parenthetical expression from the stack""" 291 if "__attribute__" not in stack: return stack 292 try: 293 debug_print("Stripping __attribute__ from %s"% stack) 294 attr_index = stack.index("__attribute__") 295 attr_end = attr_index + 1 #Assuming not followed by parenthetical expression which wont happen 296 #Find final paren 297 if stack[attr_index + 1] == '(': 298 paren_count = 1 299 for i in range(attr_index + 2, len(stack)): 300 elm = stack[i] 301 if elm == '(': 302 paren_count += 1 303 elif elm == ')': 304 paren_count -= 1 305 if paren_count == 0: 306 attr_end = i + 1 307 break 308 new_stack = stack[0:attr_index] + stack[attr_end:] 309 debug_print("stripped stack is %s"% new_stack) 310 return new_stack 311 except: 312 return stack 313 314 315class TagStr(str): 316 """Wrapper for a string that allows us to store the line number associated with it""" 317 lineno_reg = {} 318 def __new__(cls,*args,**kw): 319 new_obj = str.__new__(cls,*args) 320 if "lineno" in kw: 321 TagStr.lineno_reg[id(new_obj)] = kw["lineno"] 322 return new_obj 323 324 def __del__(self): 325 try: 326 del TagStr.lineno_reg[id(self)] 327 except: pass 328 329 def lineno(self): 330 return TagStr.lineno_reg.get(id(self), -1) 331 332class CppParseError(Exception): pass 333 334class CppClass(dict): 335 """Takes a name stack and turns it into a class 336 337 Contains the following Keys: 338 self['name'] - Name of the class 339 self['doxygen'] - Doxygen comments associated with the class if they exist 340 self['inherits'] - List of Classes that this one inherits where the values 341 are of the form {"access": Anything in supportedAccessSpecifier 342 "class": Name of the class 343 self['methods'] - Dictionary where keys are from supportedAccessSpecifier 344 and values are a lists of CppMethod's 345 self['properties'] - Dictionary where keys are from supportedAccessSpecifier 346 and values are lists of CppVariable's 347 self['enums'] - Dictionary where keys are from supportedAccessSpecifier and 348 values are lists of CppEnum's 349 self['structs'] - Dictionary where keys are from supportedAccessSpecifier and 350 values are lists of nested Struct's 351 352 An example of how this could look is as follows: 353 #self = 354 { 355 'name': "" 356 'inherits':[] 357 'methods': 358 { 359 'public':[], 360 'protected':[], 361 'private':[] 362 }, 363 'properties': 364 { 365 'public':[], 366 'protected':[], 367 'private':[] 368 }, 369 'enums': 370 { 371 'public':[], 372 'protected':[], 373 'private':[] 374 } 375 } 376 """ 377 378 def get_all_methods(self): 379 r = [] 380 for typ in supportedAccessSpecifier: r += self['methods'][typ] 381 return r 382 383 def get_all_method_names( self ): 384 r = [] 385 for typ in supportedAccessSpecifier: r += self.get_method_names(typ) # returns list 386 return r 387 388 def get_all_pure_virtual_methods( self ): 389 r = {} 390 for typ in supportedAccessSpecifier: r.update(self.get_pure_virtual_methods(typ)) # returns dict 391 return r 392 393 394 def get_method_names( self, type='public' ): return [ meth['name'] for meth in self['methods'][ type ] ] 395 396 def get_pure_virtual_methods( self, type='public' ): 397 r = {} 398 for meth in self['methods'][ type ]: 399 if meth['pure_virtual']: r[ meth['name'] ] = meth 400 return r 401 402 def __init__(self, nameStack, curTemplate): 403 self['nested_classes'] = [] 404 self['parent'] = None 405 self['abstract'] = False 406 self._public_enums = {} 407 self._public_structs = {} 408 self._public_typedefs = {} 409 self._public_forward_declares = [] 410 self['namespace'] = "" 411 412 debug_print( "Class: %s"%nameStack ) 413 debug_print( "Template: %s"%curTemplate) 414 415 if (len(nameStack) < 2): 416 nameStack.insert(1, "")#anonymous struct 417 global doxygenCommentCache 418 if len(doxygenCommentCache): 419 self["doxygen"] = doxygenCommentCache 420 doxygenCommentCache = "" 421 422 if "::" in "".join(nameStack): 423 #Re-Join class paths (ex ['class', 'Bar', ':', ':', 'Foo'] -> ['class', 'Bar::Foo'] 424 try: 425 new_nameStack = [] 426 for name in nameStack: 427 if len(new_nameStack) == 0: 428 new_nameStack.append(name) 429 elif name == ":" and new_nameStack[-1].endswith(":"): 430 new_nameStack[-1] += name 431 elif new_nameStack[-1].endswith("::"): 432 new_nameStack[-2] += new_nameStack[-1] + name 433 del new_nameStack[-1] 434 else: 435 new_nameStack.append(name) 436 trace_print("Convert from namestack\n %s\nto\n%s"%(nameStack, new_nameStack)) 437 nameStack = new_nameStack 438 except: pass 439 440 # Handle final specifier 441 self["final"] = False 442 try: 443 final_index = nameStack.index("final") 444 # Dont trip up the rest of the logic 445 del nameStack[final_index] 446 self["final"] = True 447 trace_print("final") 448 except: pass 449 450 self["name"] = nameStack[1] 451 self["line_number"] = detect_lineno(nameStack[0]) 452 453 #Handle template classes 454 if len(nameStack) > 3 and nameStack[2].startswith("<"): 455 open_template_count = 0 456 param_separator = 0 457 found_first = False 458 i = 0 459 for elm in nameStack: 460 if '<' in elm : 461 open_template_count += 1 462 found_first = True 463 elif '>' in elm: 464 open_template_count -= 1 465 if found_first and open_template_count == 0: 466 self["name"] = "".join(nameStack[1:i + 1]) 467 break; 468 i += 1 469 elif ":" in nameStack: 470 self['name'] = nameStack[ nameStack.index(':') - 1 ] 471 472 inheritList = [] 473 474 if nameStack.count(':') == 1: 475 nameStack = nameStack[nameStack.index(":") + 1:] 476 while len(nameStack): 477 tmpStack = [] 478 tmpInheritClass = {"access":"private", "virtual": False} 479 if "," in nameStack: 480 tmpStack = nameStack[:nameStack.index(",")] 481 nameStack = nameStack[nameStack.index(",") + 1:] 482 else: 483 tmpStack = nameStack 484 nameStack = [] 485 486 # Convert template classes to one name in the last index 487 for i in range(0, len(tmpStack)): 488 if '<' in tmpStack[i]: 489 tmpStack2 = tmpStack[:i-1] 490 tmpStack2.append("".join(tmpStack[i-1:])) 491 tmpStack = tmpStack2 492 break 493 if len(tmpStack) == 0: 494 break; 495 elif len(tmpStack) == 1: 496 tmpInheritClass["class"] = tmpStack[0] 497 elif len(tmpStack) == 2: 498 tmpInheritClass["access"] = tmpStack[0] 499 tmpInheritClass["class"] = tmpStack[1] 500 elif len(tmpStack) == 3 and "virtual" in tmpStack: 501 tmpInheritClass["access"] = tmpStack[1] if tmpStack[1] != "virtual" else tmpStack[0] 502 tmpInheritClass["class"] = tmpStack[2] 503 tmpInheritClass["virtual"] = True 504 else: 505 warning_print( "Warning: can not parse inheriting class %s"%(" ".join(tmpStack))) 506 if '>' in tmpStack: pass # allow skip templates for now 507 else: raise NotImplemented 508 509 if 'class' in tmpInheritClass: inheritList.append(tmpInheritClass) 510 511 elif nameStack.count(':') == 2: self['parent'] = self['name']; self['name'] = nameStack[-1] 512 513 elif nameStack.count(':') > 2 and nameStack[0] in ("class", "struct"): 514 tmpStack = nameStack[nameStack.index(":") + 1:] 515 516 superTmpStack = [[]] 517 for tok in tmpStack: 518 if tok == ',': 519 superTmpStack.append([]) 520 else: 521 superTmpStack[-1].append(tok) 522 523 for tmpStack in superTmpStack: 524 tmpInheritClass = {"access":"private"} 525 526 if len(tmpStack) and tmpStack[0] in supportedAccessSpecifier: 527 tmpInheritClass["access"] = tmpStack[0] 528 tmpStack = tmpStack[1:] 529 530 inheritNSStack = [] 531 while len(tmpStack) > 3: 532 if tmpStack[0] == ':': break; 533 if tmpStack[1] != ':': break; 534 if tmpStack[2] != ':': break; 535 inheritNSStack.append(tmpStack[0]) 536 tmpStack = tmpStack[3:] 537 if len(tmpStack) == 1 and tmpStack[0] != ':': 538 inheritNSStack.append(tmpStack[0]) 539 tmpInheritClass["class"] = "::".join(inheritNSStack) 540 inheritList.append(tmpInheritClass) 541 542 self['inherits'] = inheritList 543 544 if curTemplate: 545 self["template"] = curTemplate 546 trace_print("Setting template to '%s'"%self["template"]) 547 548 methodAccessSpecificList = {} 549 propertyAccessSpecificList = {} 550 enumAccessSpecificList = {} 551 structAccessSpecificList = {} 552 typedefAccessSpecificList = {} 553 forwardAccessSpecificList = {} 554 555 for accessSpecifier in supportedAccessSpecifier: 556 methodAccessSpecificList[accessSpecifier] = [] 557 propertyAccessSpecificList[accessSpecifier] = [] 558 enumAccessSpecificList[accessSpecifier] = [] 559 structAccessSpecificList[accessSpecifier] = [] 560 typedefAccessSpecificList[accessSpecifier] = [] 561 forwardAccessSpecificList[accessSpecifier] = [] 562 563 self['methods'] = methodAccessSpecificList 564 self['properties'] = propertyAccessSpecificList 565 self['enums'] = enumAccessSpecificList 566 self['structs'] = structAccessSpecificList 567 self['typedefs'] = typedefAccessSpecificList 568 self['forward_declares'] = forwardAccessSpecificList 569 570 571 def show(self): 572 """Convert class to a string""" 573 namespace_prefix = "" 574 if self["namespace"]: namespace_prefix = self["namespace"] + "::" 575 rtn = "%s %s"%(self["declaration_method"], namespace_prefix + self["name"]) 576 if self["final"]: rtn += " final" 577 if self['abstract']: rtn += ' (abstract)\n' 578 else: rtn += '\n' 579 580 if 'doxygen' in list(self.keys()): rtn += self["doxygen"] + '\n' 581 if 'parent' in list(self.keys()) and self['parent']: rtn += 'parent class: ' + self['parent'] + '\n' 582 583 if "inherits" in list(self.keys()): 584 rtn += " Inherits: " 585 for inheritClass in self["inherits"]: 586 if inheritClass["virtual"]: rtn += "virtual " 587 rtn += "%s %s, "%(inheritClass["access"], inheritClass["class"]) 588 rtn += "\n" 589 rtn += " {\n" 590 for accessSpecifier in supportedAccessSpecifier: 591 rtn += " %s\n"%(accessSpecifier) 592 #Enums 593 if (len(self["enums"][accessSpecifier])): 594 rtn += " <Enums>\n" 595 for enum in self["enums"][accessSpecifier]: 596 rtn += " %s\n"%(repr(enum)) 597 #Properties 598 if (len(self["properties"][accessSpecifier])): 599 rtn += " <Properties>\n" 600 for property in self["properties"][accessSpecifier]: 601 rtn += " %s\n"%(repr(property)) 602 #Methods 603 if (len(self["methods"][accessSpecifier])): 604 rtn += " <Methods>\n" 605 for method in self["methods"][accessSpecifier]: 606 rtn += "\t\t" + method.show() + '\n' 607 rtn += " }\n" 608 print(rtn) 609 610 def __str__(self): 611 """Convert class to a string""" 612 namespace_prefix = "" 613 if self["namespace"]: namespace_prefix = self["namespace"] + "::" 614 rtn = "%s %s"%(self["declaration_method"], namespace_prefix + self["name"]) 615 if self["final"]: rtn += " final" 616 if self['abstract']: rtn += ' (abstract)\n' 617 else: rtn += '\n' 618 619 if 'doxygen' in list(self.keys()): rtn += self["doxygen"] + '\n' 620 if 'parent' in list(self.keys()) and self['parent']: rtn += 'parent class: ' + self['parent'] + '\n' 621 622 if "inherits" in list(self.keys()) and len(self["inherits"]): 623 rtn += "Inherits: " 624 for inheritClass in self["inherits"]: 625 if inheritClass.get("virtual", False): rtn += "virtual " 626 rtn += "%s %s, "%(inheritClass["access"], inheritClass["class"]) 627 rtn += "\n" 628 rtn += "{\n" 629 for accessSpecifier in supportedAccessSpecifier: 630 rtn += "%s\n"%(accessSpecifier) 631 #Enums 632 if (len(self["enums"][accessSpecifier])): 633 rtn += " // Enums\n" 634 for enum in self["enums"][accessSpecifier]: 635 rtn += " %s\n"%(repr(enum)) 636 #Properties 637 if (len(self["properties"][accessSpecifier])): 638 rtn += " // Properties\n" 639 for property in self["properties"][accessSpecifier]: 640 rtn += " %s\n"%(repr(property)) 641 #Methods 642 if (len(self["methods"][accessSpecifier])): 643 rtn += " // Methods\n" 644 for method in self["methods"][accessSpecifier]: 645 rtn += " %s\n"%(repr(method)) 646 rtn += "}\n" 647 return rtn 648 649 650class CppUnion( CppClass ): 651 """Takes a name stack and turns it into a union 652 653 Contains the following Keys: 654 self['name'] - Name of the union 655 self['doxygen'] - Doxygen comments associated with the union if they exist 656 self['members'] - List of members the union has 657 658 An example of how this could look is as follows: 659 #self = 660 { 661 'name': "" 662 'members': [] 663 } 664 """ 665 666 def __init__(self, nameStack): 667 CppClass.__init__(self, nameStack, None) 668 self["name"] = "union " + self["name"] 669 self["members"] = self["properties"]["public"] 670 671 def transform_to_union_keys(self): 672 print("union keys: %s"%list(self.keys())) 673 for key in ['inherits', 'parent', 'abstract', 'namespace', 'typedefs', 'methods']: 674 del self[key] 675 676 def show(self): 677 """Convert class to a string""" 678 print(self) 679 680 681 def __str__(self): 682 """Convert class to a string""" 683 namespace_prefix = "" 684 if self["namespace"]: namespace_prefix = self["namespace"] + "::" 685 rtn = "%s %s"%(self["declaration_method"], namespace_prefix + self["name"]) 686 if self['abstract']: rtn += ' (abstract)\n' 687 else: rtn += '\n' 688 689 if 'doxygen' in list(self.keys()): rtn += self["doxygen"] + '\n' 690 if 'parent' in list(self.keys()) and self['parent']: rtn += 'parent class: ' + self['parent'] + '\n' 691 692 rtn += "{\n" 693 for member in self["members"]: 694 rtn += " %s\n"%(repr(member)) 695 rtn += "}\n" 696 return rtn 697 698 699 700class _CppMethod( dict ): 701 def _params_helper1( self, stack ): 702 # deal with "throw" keyword 703 if 'throw' in stack: stack = stack[ : stack.index('throw') ] 704 705 ## remove GCC keyword __attribute__(...) and preserve returns ## 706 cleaned = [] 707 hit = False; hitOpen = 0; hitClose = 0 708 for a in stack: 709 if a == '__attribute__': hit = True 710 if hit: 711 if a == '(': hitOpen += 1 712 elif a == ')': hitClose += 1 713 if a==')' and hitOpen == hitClose: 714 hit = False 715 else: 716 cleaned.append( a ) 717 stack = cleaned 718 719 # also deal with attribute((const)) function prefix # 720 # TODO this needs to be better # 721 if len(stack) > 5: 722 a = ''.join(stack) 723 if a.startswith('((__const__))'): stack = stack[ 5 : ] 724 elif a.startswith('__attribute__((__const__))'): stack = stack[ 6 : ] 725 726 stack = stack[stack.index('(') + 1: ] 727 if not stack: return [] 728 if len(stack)>=3 and stack[0]==')' and stack[1]==':': # is this always a constructor? 729 self['constructor'] = True 730 return [] 731 732 stack.reverse(); _end_ = stack.index(')'); stack.reverse() 733 stack = stack[ : len(stack)-(_end_+1) ] 734 if '(' not in stack: return stack # safe to return, no defaults that init a class 735 736 # transforms ['someclass', '(', '0', '0', '0', ')'] into "someclass(0,0,0)'" 737 r = []; hit=False 738 for a in stack: 739 if a == '(': hit=True 740 elif a == ')': hit=False 741 if hit or a == ')': r[-1] = r[-1] + a 742 else: r.append( a ) 743 return r 744 745 def _params_helper2( self, params ): 746 for p in params: 747 p['method'] = self # save reference in variable to parent method 748 if '::' in p['type']: 749 ns = p['type'].split('::')[0] 750 if ns not in Resolver.NAMESPACES and ns in Resolver.CLASSES: 751 p['type'] = self['namespace'] + p['type'] 752 else: p['namespace'] = self[ 'namespace' ] 753 754class CppMethod( _CppMethod ): 755 """Takes a name stack and turns it into a method 756 757 Contains the following Keys: 758 self['rtnType'] - Return type of the method (ex. "int") 759 self['name'] - Name of the method (ex. "getSize") 760 self['doxygen'] - Doxygen comments associated with the method if they exist 761 self['parameters'] - List of CppVariables 762 """ 763 def show(self): 764 r = ['method name: %s (%s)' %(self['name'],self['debug']) ] 765 if self['returns']: r.append( 'returns: %s'%self['returns'] ) 766 if self['parameters']: r.append( 'number arguments: %s' %len(self['parameters'])) 767 if self['pure_virtual']: r.append( 'pure virtual: %s'%self['pure_virtual'] ) 768 if self['constructor']: r.append( 'constructor' ) 769 if self['destructor']: r.append( 'destructor' ) 770 return '\n\t\t '.join( r ) 771 772 def __init__(self, nameStack, curClass, methinfo, curTemplate): 773 debug_print( "Method: %s"%nameStack ) 774 debug_print( "Template: %s"%curTemplate ) 775 global doxygenCommentCache 776 if len(doxygenCommentCache): 777 self["doxygen"] = doxygenCommentCache 778 doxygenCommentCache = "" 779 if "operator" in nameStack: 780 self["rtnType"] = " ".join(nameStack[:nameStack.index('operator')]) 781 self["name"] = "".join(nameStack[nameStack.index('operator'):nameStack.index('(')]) 782 else: 783 self["rtnType"] = " ".join(nameStack[:nameStack.index('(') - 1]) 784 self["name"] = " ".join(nameStack[nameStack.index('(') - 1:nameStack.index('(')]) 785 if self["rtnType"].startswith("virtual"): 786 self["rtnType"] = self["rtnType"][len("virtual"):].strip() 787 if len(self["rtnType"]) == 0 or self["name"] == curClass: 788 self["rtnType"] = "void" 789 790 self["rtnType"] = self["rtnType"].replace(' : : ', '::' ) 791 self["rtnType"] = self["rtnType"].replace(" <","<") 792 self["rtnType"] = self["rtnType"].replace(" >",">").replace(">>", "> >").replace(">>", "> >") 793 self["rtnType"] = self["rtnType"].replace(" ,",",") 794 795 for spec in ["const", "final", "override"]: 796 self[spec] = False 797 for i in reversed(nameStack): 798 if i == spec: 799 self[spec] = True 800 break 801 elif i == ")": 802 break 803 804 self.update( methinfo ) 805 self["line_number"] = detect_lineno(nameStack[0]) 806 807 #Filter out initializer lists used in constructors 808 try: 809 paren_depth_counter = 0 810 for i in range(0, len(nameStack)): 811 elm = nameStack[i] 812 if elm == "(": 813 paren_depth_counter += 1 814 if elm == ")": 815 paren_depth_counter -=1 816 if paren_depth_counter == 0 and nameStack[i+1] == ':': 817 debug_print("Stripping out initializer list") 818 nameStack = nameStack[:i+1] 819 break 820 except: pass 821 822 paramsStack = self._params_helper1( nameStack ) 823 824 debug_print( "curTemplate: %s"%curTemplate) 825 if curTemplate: 826 self["template"] = curTemplate 827 debug_print( "SET self['template'] to `%s`"%self["template"]) 828 829 params = [] 830 #See if there is a doxygen comment for the variable 831 doxyVarDesc = {} 832 833 if "doxygen" in self: 834 doxyLines = self["doxygen"].split("\n") 835 lastParamDesc = "" 836 for doxyLine in doxyLines: 837 if " @param " in doxyLine or " \param " in doxyLine: 838 try: 839 #Strip out the param 840 doxyLine = doxyLine[doxyLine.find("param ") + 6:] 841 (var, desc) = doxyLine.split(" ", 1) 842 doxyVarDesc[var] = desc.strip() 843 lastParamDesc = var 844 except: pass 845 elif " @return " in doxyLine or " \return " in doxyLine: 846 lastParamDesc = "" 847 # not handled for now 848 elif lastParamDesc: 849 try: 850 doxyLine = doxyLine.strip() 851 if " " not in doxyLine: 852 lastParamDesc = "" 853 continue 854 doxyLine = doxyLine[doxyLine.find(" ") + 1:] 855 doxyVarDesc[lastParamDesc] += " " + doxyLine 856 except: pass 857 858 #Create the variable now 859 while (len(paramsStack)): 860 # Find commas that are not nexted in <>'s like template types 861 open_template_count = 0 862 param_separator = 0 863 i = 0 864 for elm in paramsStack: 865 if '<' in elm : 866 open_template_count += 1 867 elif '>' in elm: 868 open_template_count -= 1 869 elif elm == ',' and open_template_count == 0: 870 param_separator = i 871 break 872 i += 1 873 874 if param_separator: 875 param = CppVariable(paramsStack[0:param_separator], doxyVarDesc=doxyVarDesc) 876 if len(list(param.keys())): params.append(param) 877 paramsStack = paramsStack[param_separator + 1:] 878 else: 879 param = CppVariable(paramsStack, doxyVarDesc=doxyVarDesc) 880 if len(list(param.keys())): params.append(param) 881 break 882 883 884 self["parameters"] = params 885 self._params_helper2( params ) # mods params inplace 886 887 def __str__(self): 888 filter_keys = ("parent", "defined", "operator", "returns_reference") 889 cpy = dict((k,v) for (k,v) in list(self.items()) if k not in filter_keys) 890 return "%s"%cpy 891 892 893class _CppVariable(dict): 894 def _name_stack_helper( self, stack ): 895 stack = list(stack) 896 if '=' not in stack: # TODO refactor me 897 # check for array[n] and deal with funny array syntax: "int myvar:99" 898 array = [] 899 while stack and stack[-1].isdigit(): array.append( stack.pop() ) 900 if array: array.reverse(); self['array'] = int(''.join(array)) 901 if stack and stack[-1].endswith(':'): stack[-1] = stack[-1][:-1] 902 903 while stack and not stack[-1]: stack.pop() # can be empty 904 return stack 905 906 def init(self): 907 #assert self['name'] # allow unnamed variables, methods like this: "void func(void);" 908 a = [] 909 self['aliases'] = []; self['parent'] = None; self['typedef'] = None 910 for key in 'constant reference pointer static typedefs class fundamental unresolved'.split(): 911 self[ key ] = 0 912 for b in self['type'].split(): 913 if b == '__const__': b = 'const' 914 a.append( b ) 915 self['type'] = ' '.join( a ) 916 917 918class CppVariable( _CppVariable ): 919 """Takes a name stack and turns it into a method 920 921 Contains the following Keys: 922 self['type'] - Type for the variable (ex. "const string &") 923 self['name'] - Name of the variable (ex. "numItems") 924 self['namespace'] - Namespace containing the enum 925 self['desc'] - Description of the variable if part of a method (optional) 926 self['doxygen'] - Doxygen comments associated with the method if they exist 927 self['defaultValue'] - Default value of the variable, this key will only 928 exist if there is a default value 929 self['extern'] - True if its an extern, false if not 930 """ 931 Vars = [] 932 def __init__(self, nameStack, **kwargs): 933 debug_print("trace %s"%nameStack) 934 if len(nameStack) and nameStack[0] == "extern": 935 self['extern'] = True 936 del nameStack[0] 937 else: 938 self['extern'] = False 939 940 _stack_ = nameStack 941 if "[" in nameStack: #strip off array informatin 942 arrayStack = nameStack[nameStack.index("["):] 943 if nameStack.count("[") > 1: 944 debug_print("Multi dimensional array") 945 debug_print("arrayStack=%s"%arrayStack) 946 nums = filter(lambda x: x.isdigit(), arrayStack) 947 # Calculate size by multiplying all dimensions 948 p = 1 949 for n in nums: 950 p *= int(n) 951 #Multi dimensional array 952 self["array_size"] = p 953 self["multi_dimensional_array"] = 1 954 self["multi_dimensional_array_size"] = "x".join(nums) 955 else: 956 debug_print("Array") 957 if len(arrayStack) == 3: 958 self["array_size"] = arrayStack[1] 959 nameStack = nameStack[:nameStack.index("[")] 960 self["array"] = 1 961 else: 962 self["array"] = 0 963 nameStack = self._name_stack_helper( nameStack ) 964 global doxygenCommentCache 965 if len(doxygenCommentCache): 966 self["doxygen"] = doxygenCommentCache 967 doxygenCommentCache = "" 968 969 debug_print( "Variable: %s"%nameStack ) 970 971 self["line_number"] = detect_lineno(nameStack[0]) 972 self["function_pointer"] = 0 973 974 if (len(nameStack) < 2): # +++ 975 if len(nameStack) == 1: self['type'] = nameStack[0]; self['name'] = '' 976 else: error_print(_stack_); assert 0 977 978 elif is_function_pointer_stack(nameStack): #function pointer 979 self["type"] = " ".join(nameStack[:nameStack.index("(") + 2] + nameStack[nameStack.index(")") :]) 980 self["name"] = " ".join(nameStack[nameStack.index("(") + 2 : nameStack.index(")")]) 981 self["function_pointer"] = 1 982 983 elif ("=" in nameStack): 984 self["type"] = " ".join(nameStack[:nameStack.index("=") - 1]) 985 self["name"] = nameStack[nameStack.index("=") - 1] 986 self["defaultValue"] = " ".join(nameStack[nameStack.index("=") + 1:]) # deprecate camelCase in dicts 987 self['default'] = " ".join(nameStack[nameStack.index("=") + 1:]) 988 989 elif is_fundamental(nameStack[-1]) or nameStack[-1] in ['>', '<' , ':', '.']: 990 #Un named parameter 991 self["type"] = " ".join(nameStack) 992 self["name"] = "" 993 994 else: # common case 995 self["type"] = " ".join(nameStack[:-1]) 996 self["name"] = nameStack[-1] 997 998 self["type"] = self["type"].replace(" :",":") 999 self["type"] = self["type"].replace(": ",":") 1000 self["type"] = self["type"].replace(" <","<") 1001 self["type"] = self["type"].replace(" >",">").replace(">>", "> >").replace(">>", "> >") 1002 self["type"] = self["type"].replace(" ,",",") 1003 #Optional doxygen description 1004 try: 1005 self["desc"] = kwargs["doxyVarDesc"][self["name"]] 1006 except: pass 1007 1008 self.init() 1009 CppVariable.Vars.append( self ) # save and resolve later 1010 1011 def __str__(self): 1012 keys_white_list = ['constant','name','reference','type','static','pointer','desc', 'line_number', 'extern'] 1013 cpy = dict((k,v) for (k,v) in list(self.items()) if k in keys_white_list) 1014 if "array_size" in self: cpy["array_size"] = self["array_size"] 1015 return "%s"%cpy 1016 1017class _CppEnum(dict): 1018 def resolve_enum_values( self, values ): 1019 """Evaluates the values list of dictionaries passed in and figures out what the enum value 1020 for each enum is editing in place: 1021 1022 Example: 1023 From: [{'name': 'ORANGE'}, 1024 {'name': 'RED'}, 1025 {'name': 'GREEN', 'value': '8'}] 1026 To: [{'name': 'ORANGE', 'value': 0}, 1027 {'name': 'RED', 'value': 1}, 1028 {'name': 'GREEN', 'value': 8}] 1029 """ 1030 t = int; i = 0 1031 names = [ v['name'] for v in values ] 1032 for v in values: 1033 if 'value' in v: 1034 a = v['value'].strip() 1035 # Remove single quotes from single quoted chars (unless part of some expression 1036 if len(a) == 3 and a[0] == "'" and a[2] == "'": 1037 a = v['value'] = a[1] 1038 if a.lower().startswith("0x"): 1039 try: 1040 i = a = int(a , 16) 1041 except:pass 1042 elif a.isdigit(): 1043 i = a = int( a ) 1044 elif a in names: 1045 for other in values: 1046 if other['name'] == a: 1047 v['value'] = other['value'] 1048 break 1049 1050 elif '"' in a or "'" in a: t = str # only if there are quotes it this a string enum 1051 else: 1052 try: 1053 a = i = ord(a) 1054 except: pass 1055 #Allow access of what is in the file pre-convert if converted 1056 if v['value'] != str(a): 1057 v['raw_value'] = v['value'] 1058 v['value'] = a 1059 else: v['value'] = i 1060 try: 1061 v['value'] = v['value'].replace(" < < ", " << ").replace(" >> ", " >> ") 1062 except: pass 1063 i += 1 1064 return t 1065 1066class CppEnum(_CppEnum): 1067 """Takes a name stack and turns it into an Enum 1068 1069 Contains the following Keys: 1070 self['name'] - Name of the enum (ex. "ItemState") 1071 self['namespace'] - Namespace containing the enum 1072 self['values'] - List of values where the values are a dictionary of the 1073 form {"name": name of the key (ex. "PARSING_HEADER"), 1074 "value": Specified value of the enum, this key will only exist 1075 if a value for a given enum value was defined 1076 } 1077 """ 1078 def __init__(self, nameStack): 1079 global doxygenCommentCache 1080 if len(doxygenCommentCache): 1081 self["doxygen"] = doxygenCommentCache 1082 doxygenCommentCache = "" 1083 if len(nameStack) == 3 and nameStack[0] == "enum": 1084 debug_print("Created enum as just name/value") 1085 self["name"] = nameStack[1] 1086 self["instances"]=[nameStack[2]] 1087 if len(nameStack) < 4 or "{" not in nameStack or "}" not in nameStack: 1088 #Not enough stuff for an enum 1089 debug_print("Bad enum") 1090 return 1091 valueList = [] 1092 self["line_number"] = detect_lineno(nameStack[0]) 1093 #Figure out what values it has 1094 valueStack = nameStack[nameStack.index('{') + 1: nameStack.index('}')] 1095 while len(valueStack): 1096 tmpStack = [] 1097 if "," in valueStack: 1098 tmpStack = valueStack[:valueStack.index(",")] 1099 valueStack = valueStack[valueStack.index(",") + 1:] 1100 else: 1101 tmpStack = valueStack 1102 valueStack = [] 1103 d = {} 1104 if len(tmpStack) == 1: d["name"] = tmpStack[0] 1105 elif len(tmpStack) >= 3 and tmpStack[1] == "=": 1106 d["name"] = tmpStack[0]; d["value"] = " ".join(tmpStack[2:]) 1107 elif len(tmpStack) == 2 and tmpStack[1] == "=": 1108 debug_print( "WARN-enum: parser missed value for %s"%tmpStack[0] ) 1109 d["name"] = tmpStack[0] 1110 1111 if d: valueList.append( d ) 1112 1113 if len(valueList): 1114 self['type'] = self.resolve_enum_values( valueList ) # returns int for standard enum 1115 self["values"] = valueList 1116 else: 1117 warning_print( 'WARN-enum: empty enum %s'%nameStack ) 1118 return 1119 #Figure out if it has a name 1120 preBraceStack = nameStack[:nameStack.index("{")] 1121 postBraceStack = nameStack[nameStack.index("}") + 1:] 1122 self["typedef"] = False 1123 if (len(preBraceStack) == 4 and ":" in nameStack and "typedef" not in nameStack): 1124 # C++11 specify enum type with "enum <enum_name> : <type> ..." syntax 1125 self["name"] = preBraceStack[1] 1126 self["type"] = preBraceStack[3] 1127 elif (len(preBraceStack) == 2 and "typedef" not in nameStack): 1128 # enum "enum <enum_name> ..." syntax 1129 self["name"] = preBraceStack[1] 1130 elif len(postBraceStack) and "typedef" in nameStack: 1131 self["name"] = " ".join(postBraceStack) 1132 self["typedef"] = True 1133 else: warning_print( 'WARN-enum: nameless enum %s'%nameStack ) 1134 #See if there are instances of this 1135 if "typedef" not in nameStack and len(postBraceStack): 1136 self["instances"] = [] 1137 for var in postBraceStack: 1138 if "," in var: 1139 continue 1140 self["instances"].append(var) 1141 self["namespace"] = "" 1142 1143 1144class CppStruct(dict): 1145 Structs = [] 1146 def __init__(self, nameStack): 1147 if len(nameStack) >= 2: self['type'] = nameStack[1] 1148 else: self['type'] = None 1149 self['fields'] = [] 1150 self.Structs.append( self ) 1151 global curLine 1152 self["line_number"] = curLine 1153 1154C99_NONSTANDARD = { 1155 'int8' : 'signed char', 1156 'int16' : 'short int', 1157 'int32' : 'int', 1158 'int64' : 'int64_t', # this can be: long int (64bit), or long long int (32bit) 1159 'uint' : 'unsigned int', 1160 'uint8' : 'unsigned char', 1161 'uint16' : 'unsigned short int', 1162 'uint32' : 'unsigned int', 1163 'uint64' : 'uint64_t', # depends on host bits 1164} 1165 1166 1167def standardize_fundamental( s ): 1168 if s in C99_NONSTANDARD: return C99_NONSTANDARD[ s ] 1169 else: return s 1170 1171 1172class Resolver(object): 1173 C_FUNDAMENTAL = 'size_t unsigned signed bool char wchar short int float double long void'.split() 1174 C_FUNDAMENTAL += 'struct union enum'.split() 1175 1176 1177 SubTypedefs = {} # TODO deprecate? 1178 NAMESPACES = [] 1179 CLASSES = {} 1180 STRUCTS = {} 1181 1182 def initextra(self): 1183 self.typedefs = {} 1184 self.typedefs_order = [] 1185 self.classes_order = [] 1186 self.structs = Resolver.STRUCTS 1187 self.structs_order = [] 1188 self.namespaces = Resolver.NAMESPACES # save all namespaces 1189 self.curStruct = None 1190 self.stack = [] # full name stack, good idea to keep both stacks? (simple stack and full stack) 1191 self._classes_brace_level = {} # class name : level 1192 self._structs_brace_level = {} # struct type : level 1193 self._method_body = None 1194 self._forward_decls = [] 1195 self._template_typenames = [] # template<typename XXX> 1196 1197 def current_namespace(self): return self.cur_namespace(True) 1198 1199 def cur_namespace(self, add_double_colon=False): 1200 rtn = "" 1201 i = 0 1202 while i < len(self.nameSpaces): 1203 rtn += self.nameSpaces[i] 1204 if add_double_colon or i < len(self.nameSpaces) - 1: rtn += "::" 1205 i+=1 1206 return rtn 1207 1208 1209 def guess_ctypes_type( self, string ): 1210 pointers = string.count('*') 1211 string = string.replace('*','') 1212 1213 a = string.split() 1214 if 'unsigned' in a: u = 'u' 1215 else: u = '' 1216 if 'long' in a and 'double' in a: b = 'longdouble' # there is no ctypes.c_ulongdouble (this is a 64bit float?) 1217 elif a.count('long') == 2 and 'int' in a: b = '%sint64' %u 1218 elif a.count('long') == 2: b = '%slonglong' %u 1219 elif 'long' in a: b = '%slong' %u 1220 elif 'double' in a: b = 'double' # no udouble in ctypes 1221 elif 'short' in a: b = '%sshort' %u 1222 elif 'char' in a: b = '%schar' %u 1223 elif 'wchar' in a: b = 'wchar' 1224 elif 'bool' in a: b = 'bool' 1225 elif 'float' in a: b = 'float' 1226 1227 elif 'int' in a: b = '%sint' %u 1228 elif 'int8' in a: b = 'int8' 1229 elif 'int16' in a: b = 'int16' 1230 elif 'int32' in a: b = 'int32' 1231 elif 'int64' in a: b = 'int64' 1232 1233 elif 'uint' in a: b = 'uint' 1234 elif 'uint8' in a: b = 'uint8' 1235 elif 'uint16' in a: b = 'uint16' 1236 elif 'uint32' in a: b = 'uint32' 1237 elif 'uint64' in a: b = 'uint64' 1238 1239 elif 'size_t' in a: b = 'size_t' 1240 elif 'void' in a: b = 'void_p' 1241 1242 elif string in 'struct union'.split(): b = 'void_p' # what should be done here? don't trust struct, it could be a class, no need to expose via ctypes 1243 else: b = 'void_p' 1244 1245 if not pointers: return 'ctypes.c_%s' %b 1246 else: 1247 x = '' 1248 for i in range(pointers): x += 'ctypes.POINTER(' 1249 x += 'ctypes.c_%s' %b 1250 x += ')' * pointers 1251 return x 1252 1253 def resolve_type( self, string, result ): # recursive 1254 ''' 1255 keeps track of useful things like: how many pointers, number of typedefs, is fundamental or a class, etc... 1256 ''' 1257 ## be careful with templates, what is inside <something*> can be a pointer but the overall type is not a pointer 1258 ## these come before a template 1259 s = string.split('<')[0] 1260 result[ 'constant' ] += s.split().count('const') 1261 result[ 'static' ] += s.split().count('static') 1262 result[ 'mutable' ] = 'mutable' in s.split() 1263 1264 ## these come after a template 1265 s = string.split('>')[-1] 1266 result[ 'pointer' ] += s.count('*') 1267 result[ 'reference' ] += s.count('&') 1268 1269 1270 x = string; alias = False 1271 for a in '* & const static mutable'.split(): x = x.replace(a,'') 1272 for y in x.split(): 1273 if y not in self.C_FUNDAMENTAL: alias = y; break 1274 1275 #if alias == 'class': 1276 # result['class'] = result['name'] # forward decl of class 1277 # result['forward_decl'] = True 1278 if alias == '__extension__': result['fundamental_extension'] = True 1279 elif alias: 1280 result['aliases'].append( alias ) 1281 if alias in C99_NONSTANDARD: 1282 result['type'] = C99_NONSTANDARD[ alias ] 1283 result['typedef'] = alias 1284 result['typedefs'] += 1 1285 elif alias in self.typedefs: 1286 result['typedefs'] += 1 1287 result['typedef'] = alias 1288 self.resolve_type( self.typedefs[alias], result ) 1289 elif alias in self.classes: 1290 klass = self.classes[alias]; result['fundamental'] = False 1291 result['class'] = klass 1292 result['unresolved'] = False 1293 else: result['unresolved'] = True 1294 else: 1295 result['fundamental'] = True 1296 result['unresolved'] = False 1297 1298 1299 def finalize_vars(self): 1300 for s in CppStruct.Structs: # vars within structs can be ignored if they do not resolve 1301 for var in s['fields']: var['parent'] = s['type'] 1302 #for c in self.classes.values(): 1303 # for var in c.get_all_properties(): var['parent'] = c['name'] 1304 1305 ## RESOLVE ## 1306 for var in CppVariable.Vars: 1307 self.resolve_type( var['type'], var ) 1308 #if 'method' in var and var['method']['name'] == '_notifyCurrentCamera': print(var); assert 0 1309 1310 # then find concrete type and best guess ctypes type # 1311 for var in CppVariable.Vars: 1312 if not var['aliases']: #var['fundamental']: 1313 var['ctypes_type'] = self.guess_ctypes_type( var['type'] ) 1314 else: 1315 var['unresolved'] = False # below may test to True 1316 if var['class']: 1317 var['ctypes_type'] = 'ctypes.c_void_p' 1318 else: 1319 assert var['aliases'] 1320 tag = var['aliases'][0] 1321 1322 klass = None 1323 nestedEnum = None 1324 nestedStruct = None 1325 nestedTypedef = None 1326 if 'method' in var and 'parent' in list(var['method'].keys()): 1327 klass = var['method']['parent'] 1328 if tag in var['method']['parent']._public_enums: 1329 nestedEnum = var['method']['parent']._public_enums[ tag ] 1330 elif tag in var['method']['parent']._public_structs: 1331 nestedStruct = var['method']['parent']._public_structs[ tag ] 1332 elif tag in var['method']['parent']._public_typedefs: 1333 nestedTypedef = var['method']['parent']._public_typedefs[ tag ] 1334 1335 1336 if '<' in tag: # should also contain '>' 1337 var['template'] = tag # do not resolve templates 1338 var['ctypes_type'] = 'ctypes.c_void_p' 1339 var['unresolved'] = True 1340 1341 elif nestedEnum: 1342 enum = nestedEnum 1343 if enum['type'] is int: 1344 var['ctypes_type'] = 'ctypes.c_int' 1345 var['raw_type'] = 'int' 1346 1347 elif enum['type'] is str: 1348 var['ctypes_type'] = 'ctypes.c_char_p' 1349 var['raw_type'] = 'char*' 1350 1351 var['enum'] = var['method']['path'] + '::' + enum['name'] 1352 var['fundamental'] = True 1353 1354 elif nestedStruct: 1355 var['ctypes_type'] = 'ctypes.c_void_p' 1356 var['raw_type'] = var['method']['path'] + '::' + nestedStruct['type'] 1357 var['fundamental'] = False 1358 1359 elif nestedTypedef: 1360 var['fundamental'] = is_fundamental( nestedTypedef ) 1361 if not var['fundamental']: 1362 var['raw_type'] = var['method']['path'] + '::' + tag 1363 1364 else: 1365 _tag = tag 1366 if '::' in tag and tag.split('::')[0] in self.namespaces: tag = tag.split('::')[-1] 1367 con = self.concrete_typedef( _tag ) 1368 if con: 1369 var['concrete_type'] = con 1370 var['ctypes_type'] = self.guess_ctypes_type( var['concrete_type'] ) 1371 1372 elif tag in self.structs: 1373 trace_print( 'STRUCT', var ) 1374 var['struct'] = tag 1375 var['ctypes_type'] = 'ctypes.c_void_p' 1376 var['raw_type'] = self.structs[tag]['namespace'] + '::' + tag 1377 1378 elif tag in self._forward_decls: 1379 var['forward_declared'] = tag 1380 var['ctypes_type'] = 'ctypes.c_void_p' 1381 1382 elif tag in self.global_enums: 1383 enum = self.global_enums[ tag ] 1384 if enum['type'] is int: 1385 var['ctypes_type'] = 'ctypes.c_int' 1386 var['raw_type'] = 'int' 1387 elif enum['type'] is str: 1388 var['ctypes_type'] = 'ctypes.c_char_p' 1389 var['raw_type'] = 'char*' 1390 var['enum'] = enum['namespace'] + enum['name'] 1391 var['fundamental'] = True 1392 1393 1394 elif var['parent']: 1395 warning_print( 'WARN unresolved %s'%_tag) 1396 var['ctypes_type'] = 'ctypes.c_void_p' 1397 var['unresolved'] = True 1398 1399 1400 elif tag.count('::')==1: 1401 trace_print( 'trying to find nested something in', tag ) 1402 a = tag.split('::')[0] 1403 b = tag.split('::')[-1] 1404 if a in self.classes: # a::b is most likely something nested in a class 1405 klass = self.classes[ a ] 1406 if b in klass._public_enums: 1407 trace_print( '...found nested enum', b ) 1408 enum = klass._public_enums[ b ] 1409 if enum['type'] is int: 1410 var['ctypes_type'] = 'ctypes.c_int' 1411 var['raw_type'] = 'int' 1412 elif enum['type'] is str: 1413 var['ctypes_type'] = 'ctypes.c_char_p' 1414 var['raw_type'] = 'char*' 1415 try: 1416 if 'method' in var: var['enum'] = var['method']['path'] + '::' + enum['name'] 1417 else: # class property 1418 var['unresolved'] = True 1419 except: 1420 var['unresolved'] = True 1421 1422 var['fundamental'] = True 1423 1424 else: var['unresolved'] = True # TODO klass._public_xxx 1425 1426 elif a in self.namespaces: # a::b can also be a nested namespace 1427 if b in self.global_enums: 1428 enum = self.global_enums[ b ] 1429 trace_print(enum) 1430 trace_print(var) 1431 assert 0 1432 1433 elif b in self.global_enums: # falling back, this is a big ugly 1434 enum = self.global_enums[ b ] 1435 assert a in enum['namespace'].split('::') 1436 if enum['type'] is int: 1437 var['ctypes_type'] = 'ctypes.c_int' 1438 var['raw_type'] = 'int' 1439 elif enum['type'] is str: 1440 var['ctypes_type'] = 'ctypes.c_char_p' 1441 var['raw_type'] = 'char*' 1442 var['fundamental'] = True 1443 1444 else: # boost::gets::crazy 1445 trace_print('NAMESPACES', self.namespaces) 1446 trace_print( a, b ) 1447 trace_print( '---- boost gets crazy ----' ) 1448 var['ctypes_type'] = 'ctypes.c_void_p' 1449 var['unresolved'] = True 1450 1451 1452 elif 'namespace' in var and self.concrete_typedef(var['namespace']+tag): 1453 #print( 'TRYING WITH NS', var['namespace'] ) 1454 con = self.concrete_typedef( var['namespace']+tag ) 1455 if con: 1456 var['typedef'] = var['namespace']+tag 1457 var['type'] = con 1458 if 'struct' in con.split(): 1459 var['raw_type'] = var['typedef'] 1460 var['ctypes_type'] = 'ctypes.c_void_p' 1461 else: 1462 self.resolve_type( var['type'], var ) 1463 var['ctypes_type'] = self.guess_ctypes_type( var['type'] ) 1464 1465 elif '::' in var: 1466 var['ctypes_type'] = 'ctypes.c_void_p' 1467 var['unresolved'] = True 1468 1469 elif tag in self.SubTypedefs: # TODO remove SubTypedefs 1470 if 'property_of_class' in var or 'property_of_struct' in var: 1471 trace_print( 'class:', self.SubTypedefs[ tag ], 'tag:', tag ) 1472 var['typedef'] = self.SubTypedefs[ tag ] # class name 1473 var['ctypes_type'] = 'ctypes.c_void_p' 1474 else: 1475 trace_print( "WARN-this should almost never happen!" ) 1476 trace_print( var ); trace_print('-'*80) 1477 var['unresolved'] = True 1478 1479 elif tag in self._template_typenames: 1480 var['typename'] = tag 1481 var['ctypes_type'] = 'ctypes.c_void_p' 1482 var['unresolved'] = True # TODO, how to deal with templates? 1483 1484 elif tag.startswith('_'): # assume starting with underscore is not important for wrapping 1485 warning_print( 'WARN unresolved %s'%_tag) 1486 var['ctypes_type'] = 'ctypes.c_void_p' 1487 var['unresolved'] = True 1488 1489 else: 1490 trace_print( 'WARN: unknown type', var ) 1491 assert 'property_of_class' in var or 'property_of_struct' # only allow this case 1492 var['unresolved'] = True 1493 1494 1495 ## if not resolved and is a method param, not going to wrap these methods ## 1496 if var['unresolved'] and 'method' in var: var['method']['unresolved_parameters'] = True 1497 1498 1499 # create stripped raw_type # 1500 p = '* & const static mutable'.split() # +++ new July7: "mutable" 1501 for var in CppVariable.Vars: 1502 if 'raw_type' not in var: 1503 raw = [] 1504 for x in var['type'].split(): 1505 if x not in p: raw.append( x ) 1506 var['raw_type'] = ' '.join( raw ) 1507 1508 #if 'AutoConstantEntry' in var['raw_type']: print(var); assert 0 1509 if var['class']: 1510 if '::' not in var['raw_type']: 1511 if not var['class']['parent']: 1512 var['raw_type'] = var['class']['namespace'] + '::' + var['raw_type'] 1513 elif var['class']['parent'] in self.classes: 1514 parent = self.classes[ var['class']['parent'] ] 1515 var['raw_type'] = parent['namespace'] + '::' + var['class']['name'] + '::' + var['raw_type'] 1516 else: 1517 var['unresolved'] = True 1518 1519 elif '::' in var['raw_type'] and var['raw_type'].split('::')[0] not in self.namespaces: 1520 var['raw_type'] = var['class']['namespace'] + '::' + var['raw_type'] 1521 else: 1522 var['unresolved'] = True 1523 1524 elif 'forward_declared' in var and 'namespace' in var: 1525 if '::' not in var['raw_type']: 1526 var['raw_type'] = var['namespace'] + var['raw_type'] 1527 elif '::' in var['raw_type'] and var['raw_type'].split('::')[0] in self.namespaces: 1528 pass 1529 else: trace_print('-'*80); trace_print(var); raise NotImplemented 1530 1531 1532 ## need full name space for classes in raw type ## 1533 if var['raw_type'].startswith( '::' ): 1534 #print(var) 1535 #print('NAMESPACE', var['class']['namespace']) 1536 #print( 'PARENT NS', var['class']['parent']['namespace'] ) 1537 #assert 0 1538 var['unresolved'] = True 1539 if 'method' in var: var['method']['unresolved_parameters'] = True 1540 #var['raw_type'] = var['raw_type'][2:] 1541 1542 # Take care of #defines and #pragmas etc 1543 trace_print("Processing precomp_macro_buf: %s"%self._precomp_macro_buf) 1544 for m in self._precomp_macro_buf: 1545 macro = m.replace("<CppHeaderParser_newline_temp_replacement>\\n", "\n") 1546 try: 1547 if macro.lower().startswith("#define"): 1548 trace_print("Adding #define %s"%macro) 1549 self.defines.append(re.split("[\t ]+", macro, 1)[1].strip()) 1550 elif macro.lower().startswith("#pragma"): 1551 trace_print("Adding #pragma %s"%macro) 1552 self.pragmas.append(re.split("[\t ]+", macro, 1)[1].strip()) 1553 elif macro.lower().startswith("#include"): 1554 trace_print("Adding #include %s"%macro) 1555 self.includes.append(re.split("[\t ]+", macro, 1)[1].strip()) 1556 else: 1557 debug_print("Cant detect what to do with precomp macro '%s'"%macro) 1558 except: pass 1559 self._precomp_macro_buf = None 1560 1561 1562 def concrete_typedef( self, key ): 1563 if key not in self.typedefs: 1564 #print( 'FAILED typedef', key ) 1565 return None 1566 while key in self.typedefs: 1567 prev = key 1568 key = self.typedefs[ key ] 1569 if '<' in key or '>' in key: return prev # stop at template 1570 if key.startswith('std::'): return key # stop at std lib 1571 return key 1572 1573 1574class _CppHeader( Resolver ): 1575 def finalize(self): 1576 self.finalize_vars() 1577 # finalize classes and method returns types 1578 for cls in list(self.classes.values()): 1579 for meth in cls.get_all_methods(): 1580 if meth['pure_virtual']: cls['abstract'] = True 1581 1582 if not meth['returns_fundamental'] and meth['returns'] in C99_NONSTANDARD: 1583 meth['returns'] = C99_NONSTANDARD[meth['returns']] 1584 meth['returns_fundamental'] = True 1585 1586 elif not meth['returns_fundamental']: # describe the return type 1587 con = None 1588 if cls['namespace'] and '::' not in meth['returns']: 1589 con = self.concrete_typedef( cls['namespace'] + '::' + meth['returns'] ) 1590 else: con = self.concrete_typedef( meth['returns'] ) 1591 1592 1593 if con: 1594 meth['returns_concrete'] = con 1595 meth['returns_fundamental'] = is_fundamental( con ) 1596 1597 elif meth['returns'] in self.classes: 1598 trace_print( 'meth returns class:', meth['returns'] ) 1599 meth['returns_class'] = True 1600 1601 elif meth['returns'] in self.SubTypedefs: 1602 meth['returns_class'] = True 1603 meth['returns_nested'] = self.SubTypedefs[ meth['returns'] ] 1604 1605 elif meth['returns'] in cls._public_enums: 1606 enum = cls._public_enums[ meth['returns'] ] 1607 meth['returns_enum'] = enum['type'] 1608 meth['returns_fundamental'] = True 1609 if enum['type'] == int: meth['returns'] = 'int' 1610 else: meth['returns'] = 'char*' 1611 1612 elif meth['returns'] in self.global_enums: 1613 enum = self.global_enums[ meth['returns'] ] 1614 meth['returns_enum'] = enum['type'] 1615 meth['returns_fundamental'] = True 1616 if enum['type'] == int: meth['returns'] = 'int' 1617 else: meth['returns'] = 'char*' 1618 1619 elif meth['returns'].count('::')==1: 1620 trace_print( meth ) 1621 a,b = meth['returns'].split('::') 1622 if a in self.namespaces: 1623 if b in self.classes: 1624 klass = self.classes[ b ] 1625 meth['returns_class'] = a + '::' + b 1626 elif '<' in b and '>' in b: 1627 warning_print( 'WARN-can not return template: %s'%b ) 1628 meth['returns_unknown'] = True 1629 elif b in self.global_enums: 1630 enum = self.global_enums[ b ] 1631 meth['returns_enum'] = enum['type'] 1632 meth['returns_fundamental'] = True 1633 if enum['type'] == int: meth['returns'] = 'int' 1634 else: meth['returns'] = 'char*' 1635 1636 else: trace_print( a, b); trace_print( meth); meth['returns_unknown'] = True # +++ 1637 1638 elif a in self.classes: 1639 klass = self.classes[ a ] 1640 if b in klass._public_enums: 1641 trace_print( '...found nested enum', b ) 1642 enum = klass._public_enums[ b ] 1643 meth['returns_enum'] = enum['type'] 1644 meth['returns_fundamental'] = True 1645 if enum['type'] == int: meth['returns'] = 'int' 1646 else: meth['returns'] = 'char*' 1647 1648 elif b in klass._public_forward_declares: 1649 meth['returns_class'] = True 1650 1651 elif b in klass._public_typedefs: 1652 typedef = klass._public_typedefs[ b ] 1653 meth['returns_fundamental'] = is_fundamental( typedef ) 1654 1655 else: 1656 trace_print( meth ) # should be a nested class, TODO fix me. 1657 meth['returns_unknown'] = True 1658 1659 elif '::' in meth['returns']: 1660 trace_print('TODO namespace or extra nested return:', meth) 1661 meth['returns_unknown'] = True 1662 else: 1663 trace_print( 'WARN: UNKNOWN RETURN', meth['name'], meth['returns']) 1664 meth['returns_unknown'] = True 1665 1666 if meth["returns"].startswith(": : "): 1667 meth["returns"] = meth["returns"].replace(": : ", "::") 1668 1669 for cls in list(self.classes.values()): 1670 methnames = cls.get_all_method_names() 1671 pvm = cls.get_all_pure_virtual_methods() 1672 1673 for d in cls['inherits']: 1674 c = d['class'] 1675 a = d['access'] # do not depend on this to be 'public' 1676 trace_print( 'PARENT CLASS:', c ) 1677 if c not in self.classes: trace_print('WARN: parent class not found') 1678 if c in self.classes and self.classes[c]['abstract']: 1679 p = self.classes[ c ] 1680 for meth in p.get_all_methods(): #p["methods"]["public"]: 1681 trace_print( '\t\tmeth', meth['name'], 'pure virtual', meth['pure_virtual'] ) 1682 if meth['pure_virtual'] and meth['name'] not in methnames: cls['abstract'] = True; break 1683 1684 1685 1686 1687 1688 def evaluate_struct_stack(self): 1689 """Create a Struct out of the name stack (but not its parts)""" 1690 #print( 'eval struct stack', self.nameStack ) 1691 #if self.braceDepth != len(self.nameSpaces): return 1692 struct = CppStruct(self.nameStack) 1693 struct["namespace"] = self.cur_namespace() 1694 self.structs[ struct['type'] ] = struct 1695 self.structs_order.append( struct ) 1696 if self.curClass: 1697 struct['parent'] = self.curClass 1698 klass = self.classes[ self.curClass ] 1699 klass['structs'][self.curAccessSpecifier].append( struct ) 1700 if self.curAccessSpecifier == 'public': klass._public_structs[ struct['type'] ] = struct 1701 self.curStruct = struct 1702 self._structs_brace_level[ struct['type'] ] = self.braceDepth 1703 1704 1705 def parse_method_type( self, stack ): 1706 trace_print( 'meth type info', stack ) 1707 if stack[0] in ':;' and stack[1] != ':': stack = stack[1:] 1708 info = { 1709 'debug': ' '.join(stack).replace(' : : ', '::' ).replace(' < ', '<' ).replace(' > ', '> ' ).replace(" >",">").replace(">>", "> >").replace(">>", "> >"), 1710 'class':None, 1711 'namespace':self.cur_namespace(add_double_colon=True), 1712 } 1713 1714 for tag in 'defined pure_virtual operator constructor destructor extern template virtual static explicit inline friend returns returns_pointer returns_fundamental returns_class'.split(): info[tag]=False 1715 header = stack[ : stack.index('(') ] 1716 header = ' '.join( header ) 1717 header = header.replace(' : : ', '::' ) 1718 header = header.replace(' < ', '<' ) 1719 header = header.replace(' > ', '> ' ) 1720 header = header.strip() 1721 1722 if '{' in stack: 1723 info['defined'] = True 1724 self._method_body = self.braceDepth + 1 1725 trace_print( 'NEW METHOD WITH BODY', self.braceDepth ) 1726 elif stack[-1] == ';': 1727 info['defined'] = False 1728 self._method_body = None # not a great idea to be clearing here 1729 else: assert 0 1730 1731 if len(stack) > 3 and stack[-1] == ';' and stack[-2] == '0' and stack[-3] == '=': 1732 info['pure_virtual'] = True 1733 1734 r = header.split() 1735 name = None 1736 if 'operator' in stack: # rare case op overload defined outside of class 1737 op = stack[ stack.index('operator')+1 : stack.index('(') ] 1738 op = ''.join(op) 1739 if not op: 1740 if " ".join(['operator', '(', ')', '(']) in " ".join(stack): 1741 op = "()" 1742 else: 1743 trace_print( 'Error parsing operator') 1744 return None 1745 1746 info['operator'] = op 1747 name = 'operator' + op 1748 a = stack[ : stack.index('operator') ] 1749 1750 elif r: 1751 name = r[-1] 1752 a = r[ : -1 ] # strip name 1753 1754 if name is None: return None 1755 #if name.startswith('~'): name = name[1:] 1756 1757 while a and a[0] == '}': # strip - can have multiple } } 1758 a = a[1:] 1759 1760 1761 if '::' in name: 1762 #klass,name = name.split('::') # methods can be defined outside of class 1763 klass = name[ : name.rindex('::') ] 1764 name = name.split('::')[-1] 1765 info['class'] = klass 1766 if klass in self.classes and not self.curClass: 1767 #Class function defined outside the class 1768 return None 1769 # info['name'] = name 1770 #else: info['name'] = name 1771 1772 if name.startswith('~'): 1773 info['destructor'] = True 1774 name = name[1:] 1775 elif not a or (name == self.curClass and len(self.curClass)): 1776 info['constructor'] = True 1777 1778 info['name'] = name 1779 1780 for tag in 'extern virtual static explicit inline friend'.split(): 1781 if tag in a: info[ tag ] = True; a.remove( tag ) # inplace 1782 if 'template' in a: 1783 a.remove('template') 1784 b = ' '.join( a ) 1785 if '>' in b: 1786 info['template'] = b[ : b.index('>')+1 ] 1787 info['returns'] = b[ b.index('>')+1 : ] # find return type, could be incorrect... TODO 1788 if '<typename' in info['template'].split(): 1789 typname = info['template'].split()[-1] 1790 typname = typname[ : -1 ] # strip '>' 1791 if typname not in self._template_typenames: self._template_typenames.append( typname ) 1792 else: info['returns'] = ' '.join( a ) 1793 else: info['returns'] = ' '.join( a ) 1794 info['returns'] = info['returns'].replace(' <', '<').strip() 1795 1796 ## be careful with templates, do not count pointers inside template 1797 info['returns_pointer'] = info['returns'].split('>')[-1].count('*') 1798 if info['returns_pointer']: info['returns'] = info['returns'].replace('*','').strip() 1799 1800 info['returns_reference'] = '&' in info['returns'] 1801 if info['returns']: info['returns'] = info['returns'].replace('&','').strip() 1802 1803 a = [] 1804 for b in info['returns'].split(): 1805 if b == '__const__': info['returns_const'] = True 1806 elif b == 'const': info['returns_const'] = True 1807 else: a.append( b ) 1808 info['returns'] = ' '.join( a ) 1809 1810 info['returns_fundamental'] = is_fundamental( info['returns'] ) 1811 return info 1812 1813 def evaluate_method_stack(self): 1814 """Create a method out of the name stack""" 1815 1816 if self.curStruct: 1817 trace_print( 'WARN - struct contains methods - skipping' ) 1818 trace_print( self.stack ) 1819 assert 0 1820 1821 info = self.parse_method_type( self.stack ) 1822 if info: 1823 if info[ 'class' ] and info['class'] in self.classes: # case where methods are defined outside of class 1824 newMethod = CppMethod(self.nameStack, info['name'], info, self.curTemplate) 1825 klass = self.classes[ info['class'] ] 1826 klass[ 'methods' ][ 'public' ].append( newMethod ) 1827 newMethod['parent'] = klass 1828 if klass['namespace']: newMethod['path'] = klass['namespace'] + '::' + klass['name'] 1829 else: newMethod['path'] = klass['name'] 1830 1831 elif self.curClass: # normal case 1832 newMethod = CppMethod(self.nameStack, self.curClass, info, self.curTemplate) 1833 klass = self.classes[self.curClass] 1834 klass['methods'][self.curAccessSpecifier].append(newMethod) 1835 newMethod['parent'] = klass 1836 if klass['namespace']: newMethod['path'] = klass['namespace'] + '::' + klass['name'] 1837 else: newMethod['path'] = klass['name'] 1838 else: #non class functions 1839 debug_print("FREE FUNCTION") 1840 newMethod = CppMethod(self.nameStack, None, info, self.curTemplate) 1841 self.functions.append(newMethod) 1842 global parseHistory 1843 parseHistory.append({"braceDepth": self.braceDepth, "item_type": "method", "item": newMethod}) 1844 else: 1845 trace_print( 'free function?', self.nameStack ) 1846 1847 self.stack = [] 1848 1849 def _parse_typedef( self, stack, namespace='' ): 1850 if not stack or 'typedef' not in stack: return 1851 stack = list( stack ) # copy just to be safe 1852 if stack[-1] == ';': stack.pop() 1853 1854 while stack and stack[-1].isdigit(): stack.pop() # throw away array size for now 1855 1856 idx = stack.index('typedef') 1857 if stack[-1] == "]": 1858 try: 1859 name = namespace + "".join(stack[-4:]) 1860 # Strip off the array part so the rest of the parsing is better 1861 stack = stack[:-3] 1862 except: 1863 name = namespace + stack[-1] 1864 else: 1865 name = namespace + stack[-1] 1866 s = '' 1867 for a in stack[idx+1:-1]: 1868 if a == '{': break 1869 if not s or s[-1] in ':<>' or a in ':<>': s += a # keep compact 1870 else: s += ' ' + a # spacing 1871 1872 r = {'name':name, 'raw':s, 'type':s} 1873 if not is_fundamental(s): 1874 if 'struct' in s.split(): pass # TODO is this right? "struct ns::something" 1875 elif '::' not in s: s = namespace + s # only add the current name space if no namespace given 1876 r['type'] = s 1877 if s: return r 1878 1879 1880 def evaluate_typedef(self): 1881 ns = self.cur_namespace(add_double_colon=True) 1882 res = self._parse_typedef( self.stack, ns ) 1883 if res: 1884 name = res['name'] 1885 self.typedefs[ name ] = res['type'] 1886 if name not in self.typedefs_order: self.typedefs_order.append( name ) 1887 1888 1889 def evaluate_property_stack(self): 1890 """Create a Property out of the name stack""" 1891 global parseHistory 1892 assert self.stack[-1] == ';' 1893 debug_print( "trace" ) 1894 if self.nameStack[0] == 'typedef': 1895 if self.curClass: 1896 typedef = self._parse_typedef( self.stack ) 1897 name = typedef['name'] 1898 klass = self.classes[ self.curClass ] 1899 klass[ 'typedefs' ][ self.curAccessSpecifier ].append( name ) 1900 if self.curAccessSpecifier == 'public': klass._public_typedefs[ name ] = typedef['type'] 1901 Resolver.SubTypedefs[ name ] = self.curClass 1902 else: assert 0 1903 elif self.curStruct or self.curClass: 1904 if len(self.nameStack) == 1: 1905 #See if we can de anonymize the type 1906 filteredParseHistory = [h for h in parseHistory if h["braceDepth"] == self.braceDepth] 1907 if len(filteredParseHistory) and filteredParseHistory[-1]["item_type"] == "class": 1908 self.nameStack.insert(0, filteredParseHistory[-1]["item"]["name"]) 1909 debug_print("DEANONYMOIZING %s to type '%s'"%(self.nameStack[1], self.nameStack[0])) 1910 if "," in self.nameStack: #Maybe we have a variable list 1911 #Figure out what part is the variable separator but remember templates of function pointer 1912 #First find left most comma outside of a > and ) 1913 leftMostComma = 0; 1914 for i in range(0, len(self.nameStack)): 1915 name = self.nameStack[i] 1916 if name in (">", ")"): leftMostComma = 0 1917 if leftMostComma == 0 and name == ",": leftMostComma = i 1918 # Is it really a list of variables? 1919 if leftMostComma != 0: 1920 trace_print("Multiple variables for namestack in %s. Separating processing"%self.nameStack) 1921 orig_nameStack = self.nameStack[:] 1922 orig_stack = self.stack[:] 1923 1924 type_nameStack = orig_nameStack[:leftMostComma-1] 1925 for name in orig_nameStack[leftMostComma - 1::2]: 1926 self.nameStack = type_nameStack + [name] 1927 self.stack = orig_stack[:] # Not maintained for mucking, but this path it doesnt matter 1928 self.evaluate_property_stack() 1929 return 1930 1931 newVar = CppVariable(self.nameStack) 1932 newVar['namespace'] = self.current_namespace() 1933 if self.curStruct: 1934 self.curStruct[ 'fields' ].append( newVar ) 1935 newVar['property_of_struct'] = self.curStruct 1936 elif self.curClass: 1937 klass = self.classes[self.curClass] 1938 klass["properties"][self.curAccessSpecifier].append(newVar) 1939 newVar['property_of_class'] = klass['name'] 1940 parseHistory.append({"braceDepth": self.braceDepth, "item_type": "variable", "item": newVar}) 1941 else: 1942 debug_print( "Found Global variable" ) 1943 newVar = CppVariable(self.nameStack) 1944 self.variables.append(newVar) 1945 1946 self.stack = [] # CLEAR STACK 1947 1948 def evaluate_class_stack(self): 1949 """Create a Class out of the name stack (but not its parts)""" 1950 #dont support sub classes today 1951 #print( 'eval class stack', self.nameStack ) 1952 parent = self.curClass 1953 if self.braceDepth > len( self.nameSpaces) and parent: 1954 trace_print( 'HIT NESTED SUBCLASS' ) 1955 self.accessSpecifierStack.append(self.curAccessSpecifier) 1956 elif self.braceDepth != len(self.nameSpaces): 1957 error_print( 'ERROR: WRONG BRACE DEPTH' ) 1958 return 1959 1960 # When dealing with typedefed structs, get rid of typedef keyword to handle later on 1961 if self.nameStack[0] == "typedef": 1962 del self.nameStack[0] 1963 if len(self.nameStack) == 1: 1964 self.anon_struct_counter += 1 1965 # We cant handle more than 1 anonymous struct, so name them uniquely 1966 self.nameStack.append("<anon-struct-%d>"%self.anon_struct_counter) 1967 1968 if self.nameStack[0] == "class": 1969 self.curAccessSpecifier = 'private' 1970 else:#struct 1971 self.curAccessSpecifier = 'public' 1972 debug_print("curAccessSpecifier changed/defaulted to %s"%self.curAccessSpecifier) 1973 if self.nameStack[0] == "union": 1974 newClass = CppUnion(self.nameStack) 1975 if newClass['name'] == 'union ': 1976 self.anon_union_counter = [self.braceDepth, 2] 1977 else: 1978 self.anon_union_counter = [self.braceDepth, 1] 1979 trace_print( 'NEW UNION', newClass['name'] ) 1980 else: 1981 newClass = CppClass(self.nameStack, self.curTemplate) 1982 trace_print( 'NEW CLASS', newClass['name'] ) 1983 newClass["declaration_method"] = self.nameStack[0] 1984 self.classes_order.append( newClass ) # good idea to save ordering 1985 self.stack = [] # fixes if class declared with ';' in closing brace 1986 if parent: 1987 newClass["namespace"] = self.classes[ parent ]['namespace'] + '::' + parent 1988 newClass['parent'] = parent 1989 self.classes[ parent ]['nested_classes'].append( newClass ) 1990 ## supports nested classes with the same name ## 1991 self.curClass = key = parent+'::'+newClass['name'] 1992 self._classes_brace_level[ key ] = self.braceDepth 1993 1994 elif newClass['parent']: # nested class defined outside of parent. A::B {...} 1995 parent = newClass['parent'] 1996 newClass["namespace"] = self.classes[ parent ]['namespace'] + '::' + parent 1997 self.classes[ parent ]['nested_classes'].append( newClass ) 1998 ## supports nested classes with the same name ## 1999 self.curClass = key = parent+'::'+newClass['name'] 2000 self._classes_brace_level[ key ] = self.braceDepth 2001 2002 else: 2003 newClass["namespace"] = self.cur_namespace() 2004 key = newClass['name'] 2005 self.curClass = newClass["name"] 2006 self._classes_brace_level[ newClass['name'] ] = self.braceDepth 2007 2008 if not key.endswith("::") and not key.endswith(" ") and len(key) != 0: 2009 if key in self.classes: 2010 trace_print( 'ERROR name collision:', key ) 2011 self.classes[key].show() 2012 trace_print('-'*80) 2013 newClass.show() 2014 assert key not in self.classes # namespace collision 2015 self.classes[ key ] = newClass 2016 global parseHistory 2017 parseHistory.append({"braceDepth": self.braceDepth, "item_type": "class", "item": newClass}) 2018 2019 2020 def evalute_forward_decl(self): 2021 trace_print( 'FORWARD DECL', self.nameStack ) 2022 assert self.nameStack[0] in ('class', 'struct') 2023 name = self.nameStack[-1] 2024 if self.curClass: 2025 klass = self.classes[ self.curClass ] 2026 klass['forward_declares'][self.curAccessSpecifier].append( name ) 2027 if self.curAccessSpecifier == 'public': klass._public_forward_declares.append( name ) 2028 else: self._forward_decls.append( name ) 2029 2030class CppHeader( _CppHeader ): 2031 """Parsed C++ class header 2032 2033 Variables produced: 2034 self.classes - Dictionary of classes found in a given header file where the 2035 key is the name of the class 2036 """ 2037 IGNORE_NAMES = '__extension__'.split() 2038 2039 def show(self): 2040 for className in list(self.classes.keys()):self.classes[className].show() 2041 2042 def __init__(self, headerFileName, argType="file", **kwargs): 2043 """Create the parsed C++ header file parse tree 2044 2045 headerFileName - Name of the file to parse OR actual file contents (depends on argType) 2046 argType - Indicates how to interpret headerFileName as a file string or file name 2047 kwargs - Supports the following keywords 2048 """ 2049 ## reset global state ## 2050 global doxygenCommentCache 2051 doxygenCommentCache = "" 2052 CppVariable.Vars = [] 2053 CppStruct.Structs = [] 2054 2055 if (argType == "file"): 2056 self.headerFileName = os.path.expandvars(headerFileName) 2057 self.mainClass = os.path.split(self.headerFileName)[1][:-2] 2058 headerFileStr = "" 2059 elif argType == "string": 2060 self.headerFileName = "" 2061 self.mainClass = "???" 2062 headerFileStr = headerFileName 2063 else: 2064 raise Exception("Arg type must be either file or string") 2065 self.curClass = "" 2066 2067 # nested classes have parent::nested, but no extra namespace, 2068 # this keeps the API compatible, TODO proper namespace for everything. 2069 Resolver.CLASSES = {} 2070 self.classes = Resolver.CLASSES 2071 #Functions that are not part of a class 2072 self.functions = [] 2073 2074 self.pragmas = [] 2075 self.defines = [] 2076 self.includes = [] 2077 self._precomp_macro_buf = [] #for internal purposes, will end up filling out pragmras and defines at the end 2078 2079 self.enums = [] 2080 self.variables = [] 2081 self.global_enums = {} 2082 self.nameStack = [] 2083 self.nameSpaces = [] 2084 self.curAccessSpecifier = 'private' # private is default 2085 self.curTemplate = None 2086 self.accessSpecifierStack = [] 2087 self.accessSpecifierScratch = [] 2088 debug_print("curAccessSpecifier changed/defaulted to %s"%self.curAccessSpecifier) 2089 self.initextra() 2090 # Old namestacks for a given level 2091 self.nameStackHistory = [] 2092 self.anon_struct_counter = 0 2093 self.anon_union_counter = [-1, 0] 2094 self.templateRegistry = [] 2095 2096 if (len(self.headerFileName)): 2097 fd = open(self.headerFileName) 2098 headerFileStr = "".join(fd.readlines()) 2099 fd.close() 2100 2101 # Make sure supportedAccessSpecifier are sane 2102 for i in range(0, len(supportedAccessSpecifier)): 2103 if " " not in supportedAccessSpecifier[i]: continue 2104 supportedAccessSpecifier[i] = re.sub("[ ]+", " ", supportedAccessSpecifier[i]).strip() 2105 2106 # Strip out template declarations 2107 templateSectionsToSliceOut = [] 2108 try: 2109 for m in re.finditer("template[\t ]*<[^>]*>", headerFileStr): 2110 start = m.start() 2111 # Search for the final '>' which may or may not be caught in the case of nexted <>'s 2112 for i in range(start, len(headerFileStr)): 2113 if headerFileStr[i] == '<': 2114 firstBracket = i 2115 break 2116 ltgtStackCount = 1 2117 #Now look for fianl '>' 2118 for i in range(firstBracket + 1, len(headerFileStr)): 2119 if headerFileStr[i] == '<': 2120 ltgtStackCount += 1 2121 elif headerFileStr[i] == '>': 2122 ltgtStackCount -= 1 2123 if ltgtStackCount == 0: 2124 end = i 2125 break 2126 templateSectionsToSliceOut.append((start, end)) 2127 2128 # Now strip out all instances of the template 2129 templateSectionsToSliceOut.reverse() 2130 for tslice in templateSectionsToSliceOut: 2131 # Replace the template symbol with a single symbol 2132 template_symbol="CppHeaderParser_template_%d"%len(self.templateRegistry) 2133 self.templateRegistry.append(headerFileStr[tslice[0]: tslice[1]+1]) 2134 newlines = headerFileStr[tslice[0]: tslice[1]].count("\n") * "\n" #Keep line numbers the same 2135 headerFileStr = headerFileStr[:tslice[0]] + newlines + " " + template_symbol + " " + headerFileStr[tslice[1] + 1:] 2136 except: 2137 pass 2138 2139 # Change multi line #defines and expressions to single lines maintaining line nubmers 2140 # Based from http://stackoverflow.com/questions/2424458/regular-expression-to-match-cs-multiline-preprocessor-statements 2141 matches = re.findall(r'(?m)^(?:.*\\\r?\n)+.*$', headerFileStr) 2142 is_define = re.compile(r'[ \t\v]*#[Dd][Ee][Ff][Ii][Nn][Ee]') 2143 for m in matches: 2144 #Keep the newlines so that linecount doesnt break 2145 num_newlines = len([a for a in m if a=="\n"]) 2146 if is_define.match(m): 2147 new_m = m.replace("\n", "<CppHeaderParser_newline_temp_replacement>\\n") 2148 else: 2149 # Just expression taking up multiple lines, make it take 1 line for easier parsing 2150 new_m = m.replace("\\\n", " ") 2151 if (num_newlines > 0): 2152 new_m += "\n"*(num_newlines) 2153 headerFileStr = headerFileStr.replace(m, new_m) 2154 2155 #Filter out Extern "C" statements. These are order dependent 2156 matches = re.findall(re.compile(r'extern[\t ]+"[Cc]"[\t \n\r]*{', re.DOTALL), headerFileStr) 2157 for m in matches: 2158 #Keep the newlines so that linecount doesnt break 2159 num_newlines = len([a for a in m if a=="\n"]) 2160 headerFileStr = headerFileStr.replace(m, "\n" * num_newlines) 2161 headerFileStr = re.sub(r'extern[ ]+"[Cc]"[ ]*', "", headerFileStr) 2162 2163 #Filter out any ignore symbols that end with "()" to account for #define magic functions 2164 for ignore in ignoreSymbols: 2165 if not ignore.endswith("()"): continue 2166 while True: 2167 locStart = headerFileStr.find(ignore[:-1]) 2168 if locStart == -1: 2169 break; 2170 locEnd = None 2171 #Now walk till we find the last paren and account for sub parens 2172 parenCount = 1 2173 inQuotes = False 2174 for i in range(locStart + len(ignore) - 1, len(headerFileStr)): 2175 c = headerFileStr[i] 2176 if not inQuotes: 2177 if c == "(": 2178 parenCount += 1 2179 elif c == ")": 2180 parenCount -= 1 2181 elif c == '"': 2182 inQuotes = True 2183 if parenCount == 0: 2184 locEnd = i + 1 2185 break; 2186 else: 2187 if c == '"' and headerFileStr[i-1] != '\\': 2188 inQuotes = False 2189 2190 if locEnd: 2191 #Strip it out but keep the linecount the same so line numbers are right 2192 match_str = headerFileStr[locStart:locEnd] 2193 debug_print("Striping out '%s'"%match_str) 2194 num_newlines = len([a for a in match_str if a=="\n"]) 2195 headerFileStr = headerFileStr.replace(headerFileStr[locStart:locEnd], "\n"*num_newlines) 2196 2197 self.braceDepth = 0 2198 lex.lex() 2199 lex.input(headerFileStr) 2200 global curLine 2201 global curChar 2202 curLine = 0 2203 curChar = 0 2204 try: 2205 while True: 2206 tok = lex.token() 2207 if not tok: break 2208 if self.anon_union_counter[0] == self.braceDepth and self.anon_union_counter[1]: 2209 self.anon_union_counter[1] -= 1 2210 tok.value = TagStr(tok.value, lineno=tok.lineno) 2211 #debug_print("TOK: %s"%tok) 2212 if tok.type == 'NAME' and tok.value in self.IGNORE_NAMES: continue 2213 if tok.type != 'TEMPLATE_NAME': 2214 self.stack.append( tok.value ) 2215 curLine = tok.lineno 2216 curChar = tok.lexpos 2217 if (tok.type in ('PRECOMP_MACRO', 'PRECOMP_MACRO_CONT')): 2218 debug_print("PRECOMP: %s"%tok) 2219 self._precomp_macro_buf.append(tok.value) 2220 self.stack = [] 2221 self.nameStack = [] 2222 continue 2223 if tok.type == 'TEMPLATE_NAME': 2224 try: 2225 templateId = int(tok.value.replace("CppHeaderParser_template_","")) 2226 self.curTemplate = self.templateRegistry[templateId] 2227 except: pass 2228 if (tok.type == 'OPEN_BRACE'): 2229 if len(self.nameStack) >= 2 and is_namespace(self.nameStack): # namespace {} with no name used in boost, this sets default? 2230 if self.nameStack[1] == "__IGNORED_NAMESPACE__CppHeaderParser__":#Used in filtering extern "C" 2231 self.nameStack[1] = "" 2232 self.nameSpaces.append(self.nameStack[1]) 2233 ns = self.cur_namespace(); self.stack = [] 2234 if ns not in self.namespaces: self.namespaces.append( ns ) 2235 # Detect special condition of macro magic before class declaration so we 2236 # can filter it out 2237 if 'class' in self.nameStack and self.nameStack[0] != 'class': 2238 classLocationNS = self.nameStack.index("class") 2239 classLocationS = self.stack.index("class") 2240 if "(" not in self.nameStack[classLocationNS:]: 2241 debug_print("keyword 'class' found in unexpected location in nameStack, must be following #define magic. Process that before moving on") 2242 origNameStack = self.nameStack 2243 origStack = self.stack 2244 #Process first part of stack which is probably #define macro magic and may cause issues 2245 self.nameStack = self.nameStack[:classLocationNS] 2246 self.stack = self.stack[:classLocationS] 2247 try: 2248 self.evaluate_stack() 2249 except: 2250 debug_print("Error processing #define magic... Oh well") 2251 #Process rest of stack 2252 self.nameStack = origNameStack[classLocationNS:] 2253 self.stack = origStack[classLocationS:] 2254 2255 2256 if len(self.nameStack) and not is_enum_namestack(self.nameStack): 2257 self.evaluate_stack() 2258 else: 2259 self.nameStack.append(tok.value) 2260 if self.stack and self.stack[0] == 'class': self.stack = [] 2261 self.braceDepth += 1 2262 2263 elif (tok.type == 'CLOSE_BRACE'): 2264 if self.braceDepth == 0: 2265 continue 2266 if (self.braceDepth == len(self.nameSpaces)): 2267 tmp = self.nameSpaces.pop() 2268 self.stack = [] # clear stack when namespace ends? 2269 if len(self.nameStack) and is_enum_namestack(self.nameStack): 2270 self.nameStack.append(tok.value) 2271 elif self.braceDepth < 10: 2272 self.evaluate_stack() 2273 else: 2274 self.nameStack = [] 2275 self.braceDepth -= 1 2276 #self.stack = []; print 'BRACE DEPTH', self.braceDepth, 'NS', len(self.nameSpaces) 2277 if self.curClass: debug_print( 'CURBD %s'%self._classes_brace_level[ self.curClass ] ) 2278 2279 if (self.braceDepth == 0) or (self.curClass and self._classes_brace_level[self.curClass]==self.braceDepth): 2280 trace_print( 'END OF CLASS DEF' ) 2281 if self.accessSpecifierStack: 2282 self.curAccessSpecifier = self.accessSpecifierStack[-1] 2283 self.accessSpecifierStack = self.accessSpecifierStack[:-1] 2284 if self.curClass and self.classes[ self.curClass ]['parent']: self.curClass = self.classes[ self.curClass ]['parent'] 2285 else: self.curClass = ""; #self.curStruct = None 2286 self.stack = [] 2287 2288 #if self.curStruct: self.curStruct = None 2289 if self.braceDepth == 0 or (self.curStruct and self._structs_brace_level[self.curStruct['type']]==self.braceDepth): 2290 trace_print( 'END OF STRUCT DEF' ) 2291 self.curStruct = None 2292 2293 if self._method_body and (self.braceDepth + 1) <= self._method_body: 2294 self._method_body = None; self.stack = []; self.nameStack = []; trace_print( 'FORCE CLEAR METHBODY' ) 2295 2296 if (tok.type == 'OPEN_PAREN'): 2297 self.nameStack.append(tok.value) 2298 elif (tok.type == 'CLOSE_PAREN'): 2299 self.nameStack.append(tok.value) 2300 elif (tok.type == 'OPEN_SQUARE_BRACKET'): 2301 self.nameStack.append(tok.value) 2302 elif (tok.type == 'CLOSE_SQUARE_BRACKET'): 2303 self.nameStack.append(tok.value) 2304 elif (tok.type == 'TAB'): pass 2305 elif (tok.type == 'EQUALS'): 2306 self.nameStack.append(tok.value) 2307 elif (tok.type == 'COMMA'): 2308 self.nameStack.append(tok.value) 2309 elif (tok.type == 'BACKSLASH'): 2310 self.nameStack.append(tok.value) 2311 elif (tok.type == 'DIVIDE'): 2312 self.nameStack.append(tok.value) 2313 elif (tok.type == 'PIPE'): 2314 self.nameStack.append(tok.value) 2315 elif (tok.type == 'PERCENT'): 2316 self.nameStack.append(tok.value) 2317 elif (tok.type == 'CARET'): 2318 self.nameStack.append(tok.value) 2319 elif (tok.type == 'EXCLAMATION'): 2320 self.nameStack.append(tok.value) 2321 elif (tok.type == 'SQUOTE'): pass 2322 elif (tok.type == 'NUMBER' or tok.type == 'FLOAT_NUMBER'): 2323 self.nameStack.append(tok.value) 2324 elif (tok.type == 'MINUS'): 2325 self.nameStack.append(tok.value) 2326 elif (tok.type == 'PLUS'): 2327 self.nameStack.append(tok.value) 2328 elif (tok.type == 'STRING_LITERAL'): 2329 self.nameStack.append(tok.value) 2330 elif (tok.type == 'NAME' or tok.type == 'AMPERSTAND' or tok.type == 'ASTERISK' or tok.type == 'CHAR_LITERAL'): 2331 if tok.value in ignoreSymbols: 2332 debug_print("Ignore symbol %s"%tok.value) 2333 elif (tok.value == 'class'): 2334 self.nameStack.append(tok.value) 2335 elif tok.value in supportedAccessSpecifier: 2336 if len(self.nameStack) and self.nameStack[0] in ("class", "struct", "union"): 2337 self.nameStack.append(tok.value) 2338 elif self.braceDepth == len(self.nameSpaces) + 1 or self.braceDepth == (len(self.nameSpaces) + len(self.curClass.split("::"))): 2339 self.curAccessSpecifier = tok.value; 2340 self.accessSpecifierScratch.append(tok.value) 2341 debug_print("curAccessSpecifier updated to %s"%self.curAccessSpecifier) 2342 self.stack = [] 2343 else: 2344 self.nameStack.append(tok.value) 2345 if self.anon_union_counter[0] == self.braceDepth: 2346 self.anon_union_counter = [-1, 0] 2347 elif (tok.type == 'COLON'): 2348 #Dont want colon to be first in stack 2349 if len(self.nameStack) == 0: 2350 self.accessSpecifierScratch = [] 2351 continue 2352 2353 # Handle situation where access specifiers can be multi words such as "public slots" 2354 jns = " ".join(self.accessSpecifierScratch + self.nameStack) 2355 if jns in supportedAccessSpecifier: 2356 self.curAccessSpecifier = jns; 2357 debug_print("curAccessSpecifier updated to %s"%self.curAccessSpecifier) 2358 self.stack = [] 2359 self.nameStack = [] 2360 else: 2361 self.nameStack.append(tok.value) 2362 self.accessSpecifierScratch = [] 2363 2364 elif (tok.type == 'SEMI_COLON'): 2365 if self.anon_union_counter[0] == self.braceDepth and self.anon_union_counter[1]: 2366 debug_print("Creating anonymous union") 2367 #Force the processing of an anonymous union 2368 saved_namestack = self.nameStack[:] 2369 saved_stack = self.stack[:] 2370 self.nameStack = [""] 2371 self.stack = self.nameStack + [";"] 2372 self.nameStack = self.nameStack[0:1] 2373 debug_print("pre eval anon stack") 2374 self.evaluate_stack( tok.type ) 2375 debug_print("post eval anon stack") 2376 self.nameStack = saved_namestack 2377 self.stack = saved_stack 2378 self.anon_union_counter = [-1, 0]; 2379 2380 2381 if (self.braceDepth < 10): self.evaluate_stack( tok.type ) 2382 self.stack = [] 2383 self.nameStack = [] 2384 2385 except: 2386 if (debug): raise 2387 raise CppParseError("Not able to parse %s on line %d evaluating \"%s\"\nError around: %s" 2388 % (self.headerFileName, tok.lineno, tok.value, " ".join(self.nameStack))) 2389 2390 self.finalize() 2391 global parseHistory 2392 parseHistory = [] 2393 # Delete some temporary variables 2394 for key in ["_precomp_macro_buf", "nameStack", "nameSpaces", "curAccessSpecifier", "accessSpecifierStack", 2395 "accessSpecifierScratch", "nameStackHistory", "anon_struct_counter", "anon_union_counter", 2396 "_classes_brace_level", "_forward_decls", "stack", "mainClass", "curStruct", "_template_typenames", 2397 "_method_body", "braceDepth", "_structs_brace_level", "typedefs_order", "curTemplate", "templateRegistry"]: 2398 del self.__dict__[key] 2399 2400 2401 def evaluate_stack(self, token=None): 2402 """Evaluates the current name stack""" 2403 global doxygenCommentCache 2404 2405 self.nameStack = filter_out_attribute_keyword(self.nameStack) 2406 self.stack = filter_out_attribute_keyword(self.stack) 2407 nameStackCopy = self.nameStack[:] 2408 2409 debug_print( "Evaluating stack %s\n BraceDepth: %s (called from %d)" %(self.nameStack,self.braceDepth, inspect.currentframe().f_back.f_lineno)) 2410 2411 #Handle special case of overloading operator () 2412 if "operator()(" in "".join(self.nameStack): 2413 operator_index = self.nameStack.index("operator") 2414 self.nameStack.pop(operator_index + 2) 2415 self.nameStack.pop(operator_index + 1) 2416 self.nameStack[operator_index] = "operator()" 2417 2418 if (len(self.curClass)): 2419 debug_print( "%s (%s) "%(self.curClass, self.curAccessSpecifier)) 2420 else: 2421 debug_print( "<anonymous> (%s) "%self.curAccessSpecifier) 2422 2423 #Filter special case of array with casting in it 2424 try: 2425 bracePos = self.nameStack.index("[") 2426 parenPos = self.nameStack.index("(") 2427 if bracePos == parenPos - 1: 2428 endParen = self.nameStack.index(")") 2429 self.nameStack = self.nameStack[:bracePos + 1] + self.nameStack[endParen + 1:] 2430 debug_print("Filtered namestack to=%s"%self.nameStack) 2431 except: pass 2432 2433 #if 'typedef' in self.nameStack: self.evaluate_typedef() # allows nested typedefs, probably a bad idea 2434 if (not self.curClass and 'typedef' in self.nameStack and 2435 (('struct' not in self.nameStack and 'union' not in self.nameStack) or self.stack[-1] == ";") and 2436 not is_enum_namestack(self.nameStack)): 2437 trace_print('STACK', self.stack) 2438 self.evaluate_typedef() 2439 return 2440 2441 elif (len(self.nameStack) == 0): 2442 debug_print( "trace" ) 2443 debug_print( "(Empty Stack)" ) 2444 return 2445 elif (self.nameStack[0] == "namespace"): 2446 #Taken care of outside of here 2447 pass 2448 elif len(self.nameStack) == 2 and self.nameStack[0] == "friend":#friend class declaration 2449 pass 2450 elif len(self.nameStack) >= 2 and self.nameStack[0] == 'using' and self.nameStack[1] == 'namespace': pass # TODO 2451 2452 elif is_enum_namestack(self.nameStack): 2453 debug_print( "trace" ) 2454 self.evaluate_enum_stack() 2455 2456 elif self._method_body and (self.braceDepth + 1) > self._method_body: trace_print( 'INSIDE METHOD DEF' ) 2457 elif is_method_namestack(self.stack) and not self.curStruct and '(' in self.nameStack: 2458 debug_print( "trace" ) 2459 if self.braceDepth > 0: 2460 if "{" in self.stack and self.stack[0] != '{' and self.stack[-1] == ';' and self.braceDepth == 1: 2461 #Special case of a method defined outside a class that has a body 2462 pass 2463 else: 2464 self.evaluate_method_stack() 2465 else: 2466 #Free function 2467 self.evaluate_method_stack() 2468 elif (len(self.nameStack) == 1 and len(self.nameStackHistory) > self.braceDepth 2469 and (self.nameStackHistory[self.braceDepth][0][0:2] == ["typedef", "struct"] or 2470 self.nameStackHistory[self.braceDepth][0][0:2] == ["typedef", "union"])): 2471 # Look for the name of a typedef struct: struct typedef {...] StructName; or unions to get renamed 2472 debug_print("found the naming of a union") 2473 type_name_to_rename = self.nameStackHistory[self.braceDepth][1] 2474 new_name = self.nameStack[0] 2475 type_to_rename = self.classes[type_name_to_rename] 2476 type_to_rename["name"] = self.nameStack[0] 2477 #Now re install it in its new location 2478 self.classes[new_name] = type_to_rename 2479 if new_name != type_name_to_rename: 2480 del self.classes[type_name_to_rename] 2481 elif is_property_namestack(self.nameStack) and self.stack[-1] == ';': 2482 debug_print( "trace" ) 2483 if self.nameStack[0] in ('class', 'struct') and len(self.stack) == 3: self.evalute_forward_decl() 2484 elif len(self.nameStack) >= 2 and (self.nameStack[0]=='friend' and self.nameStack[1]=='class'): pass 2485 else: self.evaluate_property_stack() # catches class props and structs in a namespace 2486 2487 elif self.nameStack[0] in ("class", "struct", "union") or self.nameStack[0] == 'typedef' and self.nameStack[1] in ('struct', 'union'): 2488 #Parsing a union can reuse much of the class parsing 2489 debug_print( "trace" ) 2490 self.evaluate_class_stack() 2491 2492 elif not self.curClass: 2493 debug_print( "trace" ) 2494 if is_enum_namestack(self.nameStack): self.evaluate_enum_stack() 2495 elif self.curStruct and self.stack[-1] == ';': self.evaluate_property_stack() # this catches fields of global structs 2496 self.nameStack = [] 2497 doxygenCommentCache = "" 2498 elif (self.braceDepth < 1): 2499 debug_print( "trace" ) 2500 #Ignore global stuff for now 2501 debug_print( "Global stuff: %s"%self.nameStack ) 2502 self.nameStack = [] 2503 doxygenCommentCache = "" 2504 elif (self.braceDepth > len(self.nameSpaces) + 1): 2505 debug_print( "trace" ) 2506 self.nameStack = [] 2507 doxygenCommentCache = "" 2508 2509 try: 2510 self.nameStackHistory[self.braceDepth] = (nameStackCopy, self.curClass) 2511 except: 2512 self.nameStackHistory.append((nameStackCopy, self.curClass)) 2513 self.nameStack = [] # its a little confusing to have some if/else above return and others not, and then clearning the nameStack down here 2514 doxygenCommentCache = "" 2515 self.curTemplate = None 2516 2517 2518 def evaluate_enum_stack(self): 2519 """Create an Enum out of the name stack""" 2520 debug_print( "evaluating enum" ) 2521 newEnum = CppEnum(self.nameStack) 2522 if len(list(newEnum.keys())): 2523 if len(self.curClass): 2524 newEnum["namespace"] = self.cur_namespace(False) 2525 klass = self.classes[self.curClass] 2526 klass["enums"][self.curAccessSpecifier].append(newEnum) 2527 if self.curAccessSpecifier == 'public' and 'name' in newEnum: klass._public_enums[ newEnum['name'] ] = newEnum 2528 else: 2529 newEnum["namespace"] = self.cur_namespace(True) 2530 self.enums.append(newEnum) 2531 if 'name' in newEnum and newEnum['name']: self.global_enums[ newEnum['name'] ] = newEnum 2532 2533 #This enum has instances, turn them into properties 2534 if "instances" in newEnum: 2535 instanceType = "enum" 2536 if "name" in newEnum: 2537 instanceType = newEnum["name"] 2538 for instance in newEnum["instances"]: 2539 self.nameStack = [instanceType, instance] 2540 self.evaluate_property_stack() 2541 del newEnum["instances"] 2542 2543 def strip_parent_keys(self): 2544 """Strip all parent (and method) keys to prevent loops""" 2545 obj_queue = [self] 2546 while len(obj_queue): 2547 obj = obj_queue.pop() 2548 trace_print("pop %s type %s"%(obj, type(obj))) 2549 try: 2550 if "parent" in obj.keys(): 2551 del obj["parent"] 2552 trace_print("Stripped parent from %s"%obj.keys()) 2553 except: pass 2554 try: 2555 if "method" in obj.keys(): 2556 del obj["method"] 2557 trace_print("Stripped method from %s"%obj.keys()) 2558 except: pass 2559 # Figure out what sub types are one of ours 2560 try: 2561 if not hasattr(obj, 'keys'): 2562 obj = obj.__dict__ 2563 for k in obj.keys(): 2564 trace_print("-Try key %s"%(k)) 2565 trace_print("-type %s"%(type(obj[k]))) 2566 if k in ["nameStackHistory", "parent", "_public_typedefs"]: continue 2567 if type(obj[k]) == list: 2568 for i in obj[k]: 2569 trace_print("push l %s"%i) 2570 obj_queue.append(i) 2571 elif type(obj[k]) == dict: 2572 if len(obj): 2573 trace_print("push d %s"%obj[k]) 2574 obj_queue.append(obj[k]) 2575 elif type(obj[k]) == type(type(0)): 2576 if type(obj[k]) == int: 2577 obj[k] = "int" 2578 elif type(obj[k]) == str: 2579 obj[k] = "string" 2580 else: 2581 obj[k] = "???" 2582 trace_print("next key\n") 2583 except: 2584 trace_print("Exception") 2585 2586 def toJSON(self, indent=4): 2587 """Converts a parsed structure to JSON""" 2588 import json 2589 self.strip_parent_keys() 2590 try: 2591 del self.__dict__["classes_order"] 2592 except: pass 2593 return json.dumps(self.__dict__, indent=indent) 2594 2595 2596 def __repr__(self): 2597 rtn = { 2598 "classes": self.classes, 2599 "functions": self.functions, 2600 "enums": self.enums, 2601 "variables": self.variables, 2602 } 2603 return repr(rtn) 2604 2605 def __str__(self): 2606 rtn = "" 2607 for className in list(self.classes.keys()): 2608 rtn += "%s\n"%self.classes[className] 2609 if self.functions: 2610 rtn += "// functions\n" 2611 for f in self.functions: 2612 rtn += "%s\n"%f 2613 if self.variables: 2614 rtn += "// variables\n" 2615 for f in self.variables: 2616 rtn += "%s\n"%f 2617 if self.enums: 2618 rtn += "// enums\n" 2619 for f in self.enums: 2620 rtn += "%s\n"%f 2621 return rtn 2622