diff options
author | Alon Zakai <alonzakai@gmail.com> | 2011-08-05 20:47:30 -0700 |
---|---|---|
committer | Alon Zakai <alonzakai@gmail.com> | 2011-08-05 20:47:30 -0700 |
commit | b37f0ce62244d16cb216b1f125d7dc76c90c066e (patch) | |
tree | 349a1dda98ac19d800db5d63120ecb514505125e /third_party | |
parent | b63082e4b35a2001bb3029c1fab3e403445feaec (diff) |
new CppHeaderParser
Diffstat (limited to 'third_party')
-rw-r--r-- | third_party/CppHeaderParser/CppHeaderParser/CppHeaderParser.py | 2740 |
1 files changed, 1677 insertions, 1063 deletions
diff --git a/third_party/CppHeaderParser/CppHeaderParser/CppHeaderParser.py b/third_party/CppHeaderParser/CppHeaderParser/CppHeaderParser.py index 675b74d8..a16d32be 100644 --- a/third_party/CppHeaderParser/CppHeaderParser/CppHeaderParser.py +++ b/third_party/CppHeaderParser/CppHeaderParser/CppHeaderParser.py @@ -42,54 +42,47 @@ # http://www.opensource.org/licenses/bsd-license.php # """ -CppHeaderParser2.0: April 2011 - July 2011 - by HartsAntler - bhartsho@yahoo.com - http://pyppet.blogspot.com - - Quick Start - User API: - h = CppHeaderParser.CppHeader("someheader.h") - for name in h.classes: - c = h.classes[name] - for method in c['methods']['public']: - print method['name'] - print dir(method) # view the rest of the API here. - - ... TODO document more ... - - - - New Features by Hart: - should be able to parse all c++ files, not just headers - parsing global typedefs with resolution - parsing global structs - fixes nested struct in class changes accessor type - parsing if class is abstract - parsing more info about variables - save ordering of classes, structs, and typedefs - handle forward decl of class in a class - handle mutable, static, and other variable types - handle 1D arrays - - - Internal Developer Notes: - - 1. in method['rtnType'] is not recommended, instead use: 'returns', 'returns_pointer', 'returns_reference', 'returns_fundamental' - (camelCase may become deprecated in the future for the dict lookup keys) - - 2. double name stacks: - . the main stack is self.nameStack, this stack is simpler and easy to get hints from - . the secondary stack is self.stack is the full name stack, required for parsing somethings - . each stack maybe cleared at different points, since they are used to detect different things - . it looks ugly but it works :) - - 3. Tabs vs Spaces: - This file now contains tabs and spaces, the tabb'ed code is the new stuff, - in the future this should be made consistent, one way or the other. - - 4. Had to make the __repr__ methods simple because some of these dicts are interlinked. - For nice printing, call something.show() - +CppHeaderParser2.0: April 2011 - August 2011 + by HartsAntler + http://pyppet.blogspot.com + + Quick Start - User API: + h = CppHeaderParser.CppHeader("someheader.h") + for name in h.classes: + c = h.classes[name] + for method in c['methods']['public']: + print method['name'] + print dir(method) # view the rest of the API here. + + ... TODO document more ... + + + + New Features by Hart: + should be able to parse all c++ files, not just headers + parsing global typedefs with resolution + parsing global structs + fixes nested struct in class changes accessor type + parsing if class is abstract + parsing more info about variables + save ordering of classes, structs, and typedefs + handle forward decl of class in a class + handle mutable, static, and other variable types + handle 1D arrays + handle throw keyword and function prefix __attribute__((__const__)) + handle nameless parameters "void method(void);" + handle simple templates, and global functions. + + Internal Developer Notes: + + 1. double name stacks: + . the main stack is self.nameStack, this stack is simpler and easy to get hints from + . the secondary stack is self.stack is the full name stack, required for parsing somethings + . each stack maybe cleared at different points, since they are used to detect different things + . it looks ugly but it works :) + + 2. Had to make the __repr__ methods simple because some of these dicts are interlinked. + For nice printing, call something.show() """ @@ -104,8 +97,7 @@ def lineno(): """Returns the current line number in our program.""" return inspect.currentframe().f_back.f_lineno - -version = __version__ = "1.9.9" +version = __version__ = "1.9.9o" tokens = [ 'NUMBER', @@ -131,9 +123,19 @@ tokens = [ 'STRING_LITERAL', 'OPERATOR_DIVIDE_OVERLOAD', 'NEW_LINE', + + 'OPEN_BRACKET', + 'CLOSE_BRACKET', + ] -t_ignore = " \t\r[].|!?%@'^\\" # harts hack (litteral backslash is a bad idea?) - old version: " \t\r[].|!?%@" +t_OPEN_BRACKET = r'\[' +t_CLOSE_BRACKET = r'\]' + + +#t_ignore = " \t\r[].|!?%@" # (cppheaderparser 1.9x) +#t_ignore = " \t\r[].|!?%@'^\\" +t_ignore = " \t\r.|!?%@'^\\" t_NUMBER = r'[0-9][0-9XxA-Fa-f]*' t_NAME = r'[<>A-Za-z_~][A-Za-z0-9_]*' t_OPERATOR_DIVIDE_OVERLOAD = r'/=' @@ -159,7 +161,7 @@ def t_COMMENT_SINGLELINE(t): t_ASTERISK = r'\*' t_MINUS = r'\-' t_PLUS = r'\+' -t_DIVIDE = r'/[^/]' +t_DIVIDE = r'/[^/]' # fails to catch "/(" - method operator that overloads divide t_AMPERSTAND = r'&' t_EQUALS = r'=' t_CHAR_LITERAL = "'.'" @@ -185,6 +187,10 @@ def t_error(v): lex.lex() debug = 0 +debug_trace = 0 +def trace_print(*arg): + global debug_trace + if debug_trace: print(arg) supportedAccessSpecifier = [ 'public', @@ -192,6 +198,7 @@ supportedAccessSpecifier = [ 'private' ] +enumMaintianValueFormat = False doxygenCommentCache = "" def is_namespace(nameStack): @@ -214,7 +221,105 @@ def is_enum_namestack(nameStack): class CppParseError(Exception): pass -class CppClass(dict): + +class _CppClass(dict): + def _parser_helper( self, stack ): + prev = None + prev2 = None + print('stack IN', ' '.join(stack)) + for i, tok in enumerate(stack): # can not trust the first single ":" or last + if prev and prev2 and tok == ':' and prev != ':' and prev2 != ':': + break + prev = tok + prev2 = prev + + + a = stack[ : i+1 ] + b = stack[ i+1 : ] + while a[-1] == ':': a.pop() + + print( 'HEAD', a ) + print('______________') + print( 'TAIL', b ) + + if ''.join(stack).replace('::','_').count(':') >= 2: + if stack.count('class') == 1: + self['name'] = stack[ stack.index('class') + 1 ] + elif stack.count('struct') == 1: + self['name'] = stack[ stack.index('struct') + 1 ] + else: + self['unsafe_template'] = True + b = [] + + elif a[0] == 'template' and ('class' in a or 'struct' in a): + if '>' not in a: + self['name'] = a[ a.index('class') + 1 ] + self['unsafe_template'] = True + + else: + copy = list( a ) + last = len(a) - 1 - a[::-1].index('>') + self['template_typename'] = a[ a.index('>')-1 ] + a = a[ last+1 : ] + if not a: + a = copy[ copy.index('class')+1 : ] + x = ''.join( a ) + assert '<' in x and '>' in x + self['name'] = x + self['special_template'] = True + + elif 'class' in a: + self['name'] = ''.join( a[1:] ) + + elif 'struct' in a: + self['name'] = ''.join( a[1:] ) + self['struct'] = True + self['struct_template'] = self['template_typename'] + elif 'class' in b: + self['name'] = b[ b.index('class') + 1 ] + b = [] + elif 'struct' in b: + self['name'] = b[ b.index('struct') + 1 ] + b = [] + else: + self['unsafe_template'] = True + assert 0 + + elif a[0] == 'template' and b[-2] in ('class','struct'): + self['name'] = b[-1] + b = [] # b is invalid + elif a[0] == 'class': + self['name'] = ''.join( a[1:] ) + elif 'class' in b: + self['name'] = b[ b.index('class') + 1 ] + b = [] + elif 'struct' in b: + self['name'] = b[ b.index('struct') + 1 ] + self['struct'] = True + b = [] + else: + assert 0 + + + if b: + p = [ {'access':'public', 'class':''} ] + for x in b: + if x in 'public protected private'.split(): + p[-1]['access'] = x + elif x == 'virtual': + p[-1]['virtual'] = True + elif x == ',': + p.append( {'access':'public', 'class':''} ) + else: + p[-1]['class'] += x + self['inherits'] = p + else: + self['inherits'] = [] + + return True + + +class CppClass( _CppClass ): """Takes a name stack and turns it into a class Contains the following Keys: @@ -266,12 +371,12 @@ class CppClass(dict): def get_all_method_names( self ): r = [] - for typ in 'public protected private'.split(): r += self.get_method_names(typ) # returns list + for typ in 'public protected private'.split(): r += self.get_method_names(typ) # returns list return r def get_all_pure_virtual_methods( self ): r = {} - for typ in 'public protected private'.split(): r.update(self.get_pure_virtual_methods(typ)) # returns dict + for typ in 'public protected private'.split(): r.update(self.get_pure_virtual_methods(typ)) # returns dict return r @@ -287,11 +392,11 @@ class CppClass(dict): self['nested_classes'] = [] self['parent'] = None self['abstract'] = False + self['namespace'] = "" self._public_enums = {} self._public_structs = {} self._public_typedefs = {} self._public_forward_declares = [] - self['namespace'] = "" if (debug): print( "Class: ", nameStack ) if (len(nameStack) < 2): @@ -301,42 +406,6 @@ class CppClass(dict): if len(doxygenCommentCache): self["doxygen"] = doxygenCommentCache doxygenCommentCache = "" - self["name"] = nameStack[1] - inheritList = [] - - if ":" in nameStack: - self['name'] = nameStack[ nameStack.index(':') - 1 ] # harts hack - fixes: class __something__ classname - - if nameStack.count(':') == 1: - nameStack = nameStack[nameStack.index(":") + 1:] - while len(nameStack): - tmpStack = [] - tmpInheritClass = {"access":"private"} # shouldn't public be default? - if "," in nameStack: - tmpStack = nameStack[:nameStack.index(",")] - nameStack = nameStack[nameStack.index(",") + 1:] - else: - tmpStack = nameStack - nameStack = [] - if len(tmpStack) == 0: - break; - elif len(tmpStack) == 1: - tmpInheritClass["class"] = tmpStack[0] - elif len(tmpStack) == 2: - tmpInheritClass["access"] = tmpStack[0] - tmpInheritClass["class"] = tmpStack[1] - else: - print( "Warning: can not parse inheriting class %s"%(" ".join(tmpStack))) - if '>' in tmpStack: pass # allow skip templates for now - else: raise NotImplemented - - if 'class' in tmpInheritClass: inheritList.append(tmpInheritClass) - - elif nameStack.count(':') == 2: self['parent'] = self['name']; self['name'] = nameStack[-1] - - elif nameStack.count(':') > 2: print('ERROR can not parse', nameStack) # TODO - - self['inherits'] = inheritList methodAccessSpecificList = {} propertyAccessSpecificList = {} @@ -360,13 +429,19 @@ class CppClass(dict): self['typedefs'] = typedefAccessSpecificList self['forward_declares'] = forwardAccessSpecificList + ok = self._parser_helper( nameStack ) + if not ok: self['invalid'] = True + + def show_all(self): + self.show() + for key in self.keys(): print( ' %s : %s' %(key,self[key]) ) def show(self): """Convert class to a string""" namespace_prefix = "" if self["namespace"]: namespace_prefix = self["namespace"] + "::" rtn = "class %s"%(namespace_prefix + self["name"]) - if self['abstract']: rtn += ' (abstract)\n' + if self['abstract']: rtn += ' (abstract)\n' else: rtn += '\n' if 'doxygen' in self.keys(): rtn += self["doxygen"] + '\n' @@ -379,40 +454,93 @@ class CppClass(dict): rtn += "\n" rtn += " {\n" for accessSpecifier in supportedAccessSpecifier: - rtn += " %s\n"%(accessSpecifier) + rtn += " %s\n"%(accessSpecifier) #Enums if (len(self["enums"][accessSpecifier])): - rtn += " <Enums>\n" + rtn += " <Enums>\n" for enum in self["enums"][accessSpecifier]: - rtn += " %s\n"%(repr(enum)) + rtn += " %s\n"%(repr(enum)) #Properties if (len(self["properties"][accessSpecifier])): - rtn += " <Properties>\n" + rtn += " <Properties>\n" for property in self["properties"][accessSpecifier]: - rtn += " %s\n"%(repr(property)) + rtn += " %s\n"%(repr(property)) #Methods if (len(self["methods"][accessSpecifier])): - rtn += " <Methods>\n" + rtn += " <Methods>\n" for method in self["methods"][accessSpecifier]: rtn += "\t\t" + method.show() + '\n' rtn += " }\n" print( rtn ) class _CppMethod( dict ): - def _params_helper( self, params ): - for p in params: - p['method'] = self # save reference in variable to parent method - if '::' in p['type']: - ns = p['type'].split('::')[0] - if ns not in Resolver.NAMESPACES and ns in Resolver.CLASSES: - p['type'] = self['namespace'] + p['type'] - else: p['namespace'] = self[ 'namespace' ] + def _params_helper1( self, stack ): + # new July 7th, deal with defaults that init: vec3(0,0,0) + # so that comma split still works later on parsing the parameters. + + # also deal with "throw" keyword + if 'throw' in stack: stack = stack[ : stack.index('throw') ] + + ## remove GCC keyword __attribute__(...) and preserve returns ## + cleaned = [] + hit = False; hitOpen = 0; hitClose = 0 + for a in stack: + if a == '__attribute__': hit = True + if hit: + if a == '(': hitOpen += 1 + elif a == ')': hitClose += 1 + if a==')' and hitOpen == hitClose: + hit = False + else: + cleaned.append( a ) + stack = cleaned + + # also deal with attribute((const)) function prefix # + # TODO this needs to be better # + if len(stack) > 5: + a = ''.join(stack) + if a.startswith('((__const__))'): stack = stack[ 5 : ] + elif a.startswith('__attribute__((__const__))'): stack = stack[ 6 : ] + + stack = stack[stack.index('(') + 1: ] + if not stack: return [] + if len(stack)>=3 and stack[0]==')' and stack[1]==':': # is this always a constructor? + self['constructor'] = True + return [] + + stack.reverse(); _end_ = stack.index(')'); stack.reverse() + stack = stack[ : len(stack)-(_end_+1) ] + + if '(' not in stack: return stack # safe to return, no defaults that init a class + elif stack.index('(') > stack.index(')'): # deals with: "constructor(int x) : func(x) {}" + return stack[ : stack.index(')') ] # fixed july20 + + # transforms ['someclass', '(', '0', '0', '0', ')'] into "someclass(0,0,0)'" + r = []; hit=False + for a in stack: + if a == '(': hit=True + elif a == ')': hit=False + if hit or a == ')': r[-1] = r[-1] + a + else: r.append( a ) + return r + + def _params_helper2( self, params ): + for p in params: + # if param becomes unresolved - function/parent is marked with 'unresolved_parameters' + if 'function' in self: p['function'] = self + else: p['method'] = self + # force full namespace for nested items, or take method name space as our own (bad idea?) + if '::' in p['type']: + ns = p['type'].split('::')[0] + if ns not in Resolver.NAMESPACES and ns in Resolver.CLASSES: + p['type'] = self['namespace'] + p['type'] + else: p['namespace'] = self[ 'namespace' ] class CppMethod( _CppMethod ): """Takes a name stack and turns it into a method Contains the following Keys: - self['rtnType'] - Return type of the method (ex. "int") + self['returns'] - Return type of the method (ex. "int") self['name'] - Name of the method (ex. "getSize") self['doxygen'] - Doxygen comments associated with the method if they exist self['parameters'] - List of CppVariables @@ -426,30 +554,22 @@ class CppMethod( _CppMethod ): if self['destructor']: r.append( 'destructor' ) return '\n\t\t '.join( r ) - def __init__(self, nameStack, curClass, methinfo): + def __init__(self, nameStack, curClass=None, methinfo={} ): if (debug): print( "Method: ", nameStack ) global doxygenCommentCache + + if not curClass: self['function'] = True + if len(doxygenCommentCache): self["doxygen"] = doxygenCommentCache doxygenCommentCache = "" if "operator" in nameStack: - self["rtnType"] = " ".join(nameStack[:nameStack.index('operator')]) self["name"] = "".join(nameStack[nameStack.index('operator'):nameStack.index('(')]) else: - self["rtnType"] = " ".join(nameStack[:nameStack.index('(') - 1]) self["name"] = " ".join(nameStack[nameStack.index('(') - 1:nameStack.index('(')]) - if len(self["rtnType"]) == 0 or self["name"] == curClass: - self["rtnType"] = "void" - - self.update( methinfo ) # harts hack - - paramsStack = nameStack[nameStack.index('(') + 1: ] - if paramsStack: paramsStack = paramsStack[ : paramsStack.index(')') ] # fixed april 7th - #Remove things from the stack till we hit the last paren, this helps handle abstract and normal methods - #if paramsStack: # harts hacks - this is a bug caused by change to line 88? - # while paramsStack[-1] != ")": paramsStack.pop() - # paramsStack.pop() + self.update( methinfo ) # harts hack + paramsStack = self._params_helper1( nameStack ) params = [] #See if there is a doxygen comment for the variable doxyVarDesc = {} @@ -490,34 +610,48 @@ class CppMethod( _CppMethod ): if len(param.keys()): params.append(param) break - self["parameters"] = params - self._params_helper( params ) + self._params_helper2( params ) # mods params inplace class _CppVariable(dict): - def _name_stack_helper( self, stack ): - stack = list(stack) - if '=' not in stack: # TODO refactor me - # check for array[n] and deal with funny array syntax: "int myvar:99" - array = [] - while stack and stack[-1].isdigit(): array.append( stack.pop() ) - if array: array.reverse(); self['array'] = int(''.join(array)) - if stack and stack[-1].endswith(':'): stack[-1] = stack[-1][:-1] # fixed June 23, BulletPhysics - - while stack and not stack[-1]: stack.pop() # can be empty - return stack - - def init(self): - #assert self['name'] # allow unnamed variables, methods like this: "void func(void);" - a = [] - self['aliases'] = []; self['parent'] = None; self['typedef'] = None - for key in 'constant reference pointer static typedefs class fundamental unresolved'.split(): - self[ key ] = 0 - for b in self['type'].split(): - if b == '__const__': b = 'const' - a.append( b ) - self['type'] = ' '.join( a ) + def _name_stack_helper( self, stack, fullStack ): + print('V'*80); print( stack ); print(fullStack); print('_'*80) + stack = list(stack) + if stack[-1].isdigit() and '=' not in stack: # TODO refactor me - was: '=' not in stack or + # check for array[n] and deal with funny array syntax: "int myvar:99" + bits = [] + while stack and stack[-1].isdigit(): bits.append( stack.pop() ) + if bits: + bits.reverse() + self['bitfield'] = int(''.join(bits)) + assert stack[-1] == ':' + stack.pop() + + ## find and strip array def ## + if '[' in stack: + assert stack.count('[') == stack.count(']') + a = ['']; hit = 0; _stack = [] + for s in stack: + if s == '[': hit += 1 + elif s == ']': hit -= 1; a.append( '' ) + elif hit: a[-1] += s + elif not hit: _stack.append( s ) + stack = _stack + + b = [] + for s in a: + if s.isdigit(): b.append( int( s ) ) + elif s != '': self['invalid'] = True + if not b: self['pointer'] += 1 + else: + self['array'] = b[0] + self['array_dimensions'] = b + if len(b)>1: self['multidimensional'] = True + + while stack and not stack[-1]: stack.pop() # can be empty? + return stack + class CppVariable( _CppVariable ): @@ -525,34 +659,46 @@ class CppVariable( _CppVariable ): Contains the following Keys: self['type'] - Type for the variable (ex. "const string &") + self['raw_type'] - Type of variable without pointers or other markup (ex. "string") self['name'] - Name of the variable (ex. "numItems") self['namespace'] - Namespace containing the enum self['desc'] - Description of the variable if part of a method (optional) self['doxygen'] - Doxygen comments associated with the method if they exist - self['defaltValue'] - Default value of the variable, this key will only - exist if there is a default value + self['defalt'] - Default value of the variable, this key will only exist if there is a default value """ Vars = [] - def __init__(self, nameStack, **kwargs): - nameStack = self._name_stack_helper( nameStack ) - if (debug): print( "Variable: ", nameStack ) - if (len(nameStack) < 2): return + def __init__(self, nameStack, fullStack=None, doxyVarDesc=None): # CppMethod will not pass fullStack for params + self['aliases'] = []; self['parent'] = None; self['typedef'] = None + for key in 'constant reference pointer static typedefs class fundamental unresolved mutable'.split(): + self[ key ] = 0 + + + _stack_ = nameStack + nameStack = self._name_stack_helper( nameStack, fullStack ) global doxygenCommentCache if len(doxygenCommentCache): self["doxygen"] = doxygenCommentCache doxygenCommentCache = "" - if ("=" in nameStack): + + if (debug): print( "Variable: ", nameStack ) + + if (len(nameStack) < 2): + if len(nameStack) == 1: self['type'] = nameStack[0]; self['name'] = '' + else: print(_stack_); assert 0 + + elif ("=" in nameStack): self["type"] = " ".join(nameStack[:nameStack.index("=") - 1]) self["name"] = nameStack[nameStack.index("=") - 1] - self["defaltValue"] = " ".join(nameStack[nameStack.index("=") + 1:]) # deprecate camelCase in dicts self['default'] = " ".join(nameStack[nameStack.index("=") + 1:]) + self['default'] = self['default'].replace(' <', '<' ) + self['default'] = self['default'].replace(' >', '>' ) - elif nameStack[-1] == '*': # rare case - function param is an unnamed pointer: "void somemethod( SomeObject* )" + elif nameStack[-1] in '*&': # rare cases - function param is an unnamed pointer: "void somemethod( SomeObject* )" self['type'] = ' '.join(nameStack) self['name'] = '' - else: # common case + else: # common case self["type"] = " ".join(nameStack[:-1]) self["name"] = nameStack[-1] @@ -561,32 +707,71 @@ class CppVariable( _CppVariable ): self["type"] = self["type"].replace(" <","<") self["type"] = self["type"].replace(" >",">") #Optional doxygen description - try: - self["desc"] = kwargs["doxyVarDesc"][self["name"]] - except: pass + if doxyVarDesc and self['name'] in doxyVarDesc: + self['description'] = doxyVarDesc[ self['name'] ] - self.init() - CppVariable.Vars.append( self ) # save and resolve later + self['type'] = self['type'].strip() + a = [] + for b in self['type'].split(): + if b == '__const__': b = 'const' + a.append( b ) + + if not a: + self['invalid'] = True # void someinvalidfunction( int x, y=INVALID ); + print('WARN - bad variable', self ) + + else: + if a[0] == 'class': + self['explicit_class'] = a[1] + a = a[1:] + elif a[0] == 'struct': + self['explicit_struct'] = a[1] + a = a[1:] + self['type'] = ' '.join( a ) + + if self['name'].count('<') != self['name'].count('>'): self['invalid'] = True + + CppVariable.Vars.append( self ) # save and resolve later class _CppEnum(dict): - def resolve_enum_values( self, values ): - t = int; i = 0 - names = [ v['name'] for v in values ] - for v in values: - if 'value' in v: - a = v['value'].strip() - if a.isdigit(): i = a = int( a ) - elif a in names: - for other in values: - if other['name'] == a: - v['value'] = other['value'] - break - - elif '"' in a or "'" in a: t = str # only if there are quotes it this a string enum - - else: v['value'] = i - i += 1 - return t + def resolve_enum_values( self, values ): + """Evaluates the values list of dictionaries passed in and figures out what the enum value + for each enum is editing in place: + + Example: + From: [{'name': 'ORANGE'}, + {'name': 'RED'}, + {'name': 'GREEN', 'value': '8'}] + To: [{'name': 'ORANGE', 'value': 0}, + {'name': 'RED', 'value': 1}, + {'name': 'GREEN', 'value': 8}] + """ + t = 'int'; i = 0 + names = [ v['name'] for v in values ] + for v in values: + if 'value' in v: + a = v['value'].strip() + if a.lower().startswith("0x"): + try: + i = a = int(a , 16) + except:pass + elif a.isdigit(): + i = a = int( a ) + elif a in names: + for other in values: + if other['name'] == a: + v['value'] = other['value'] + break + + elif '"' in a or "'" in a: t = 'char*' # only if there are quotes it this a string enum + else: + try: + a = i = ord(a) + except: pass + if not enumMaintianValueFormat: v['value'] = a + else: v['value'] = i + i += 1 + return t class CppEnum(_CppEnum): """Takes a name stack and turns it into an Enum @@ -630,7 +815,7 @@ class CppEnum(_CppEnum): if d: valueList.append( d ) if len(valueList): - self['type'] = self.resolve_enum_values( valueList ) # returns int for standard enum + self['type'] = self.resolve_enum_values( valueList ) # returns int for standard enum self["values"] = valueList else: print( 'WARN-enum: empty enum', nameStack ) @@ -653,848 +838,1209 @@ class CppEnum(_CppEnum): self["namespace"] = "" def is_fundamental(s): - for a in s.split(): - if a not in 'size_t struct union unsigned signed bool char short int float double long void *': return False - return True + for a in s.split(): + if a not in 'size_t wchar_t struct union unsigned signed bool char short int float double long void *': return False + return True + +def prune_templates( stack ): + x = []; hit = 0 + for a in stack: + if a == '<' or a.startswith('<'): hit += 1 + elif a == '>' or a.endswith('>'): hit -= 1 + elif not hit and a != 'template': x.append( a ) + return x + +def prune_arrays( stack ): + x = []; hit = 0 + for a in stack: + if a == '[' or a.startswith('['): hit += 1 + elif a == ']' or a.endswith(']'): hit -= 1 + elif not hit: x.append( a ) + return x + def is_method_namestack(stack): - r = False - if '(' not in stack: r = False - #elif '=' in stack and stack.index('=') < stack.index('(') and stack[stack.index('=')-1] != 'operator': r = False #disabled July6th - allow all operators - elif 'operator' in stack: r = True # allow all operators - elif '{' in stack and stack.index('{') < stack.index('('): r = False # struct that looks like a method/class - elif '(' in stack and ')' in stack: - if '{' in stack and '}' in stack: r = True - elif stack[-1] == ';': r = True - elif '{' in stack: r = True # ideally we catch both braces... TODO - else: r = False - return r + clean = prune_templates( stack ); print('CLEAN TEMPLATES',clean) + clean = prune_arrays( clean ); print('CLEAN ARRAYS',clean) + + r = False + if 'operator' in stack: r = True # allow all operators + elif not ('(' in stack or '/(' in stack): r = False + elif stack[0]=='mutable': r = False + elif clean and clean[0] in ('class', 'struct'): r = False + elif not ('(' in clean or '/(' in clean): r = False + #elif '__attribute__' in stack: r = False + #elif stack[0] == '__attribute': r = False + elif stack[0] == 'typedef': r = False # TODO deal with typedef function prototypes + elif stack[0] in 'return if else case switch throw +'.split(): print( stack ); assert 0; r = False + elif stack[0] == '}' and stack[1] in 'return if else case switch'.split(): print( stack ); assert 0; r = False + elif '=' in stack:# and stack.index('=') < stack.index('('): + #if 'template' not in stack: r = False + if '=' in clean and clean.index('=') < clean.index('('): r = False + else: r = True + + elif '{' in stack and stack.index('{') < stack.index('('): r = False # struct that looks like a method/class + elif '(' in stack and ')' in stack: + if '{' in stack and '}' in stack: r = True + #elif '/' in stack: r = False + #elif '/ ' in stack: r = False + elif stack[-1] == ';': r = True + elif '{' in stack: r = True + x = ''.join(stack) + if x.endswith('(0,0,0);'): r = False + elif x.endswith('(0);'): r = False + elif x.endswith(',0);'): r = False + elif x.endswith(',1);'): r = False + elif x.endswith(',true);'): r = False + elif x.endswith(',false);'): r = False + elif x.endswith(',0xFF);'): r = False + else: r = False + print( 'is method namestack', r, stack ); print('_'*80) + return r class CppStruct(dict): - Structs = [] - def __init__(self, nameStack): - if len(nameStack) >= 2: self['type'] = nameStack[1] - else: self['type'] = None - self['fields'] = [] - self.Structs.append( self ) + Structs = [] + def __init__(self, nameStack): + if nameStack[0] == 'template': self['template'] = True + if nameStack.index('struct')+1 < len(nameStack): + self['type'] = nameStack[ nameStack.index('struct') + 1 ] + else: self['type'] = None + self['fields'] = [] + self['methods'] = [] + self['parent'] = None + self.Structs.append( self ) C99_NONSTANDARD = { - 'int8' : 'signed char', - 'int16' : 'short int', - 'int32' : 'int', - 'int64' : 'int64_t', # this can be: long int (64bit), or long long int (32bit) - 'uint' : 'unsigned int', - 'uint8' : 'unsigned char', - 'uint16' : 'unsigned short int', - 'uint32' : 'unsigned int', - 'uint64' : 'uint64_t', # depends on host bits + 'int8' : 'signed char', + 'int16' : 'short int', + 'int32' : 'int', + 'int64' : 'int64_t', # this can be: long int (64bit), or long long int (32bit) + 'uint' : 'unsigned int', + 'uint8' : 'unsigned char', + 'uint16' : 'unsigned short int', + 'uint32' : 'unsigned int', + 'uint64' : 'uint64_t', # depends on host bits } def standardize_fundamental( s ): - if s in C99_NONSTANDARD: return C99_NONSTANDARD[ s ] - else: return s + if s in C99_NONSTANDARD: return C99_NONSTANDARD[ s ] + else: return s class Resolver(object): - C_FUNDAMENTAL = 'size_t unsigned signed bool char wchar short int float double long void'.split() - C_FUNDAMENTAL += 'struct union enum'.split() - - - SubTypedefs = {} # TODO deprecate? - NAMESPACES = [] - CLASSES = {} - STRUCTS = {} - - def initextra(self): - self.typedefs = {} - self.typedefs_order = [] - self.classes_order = [] - self.structs = Resolver.STRUCTS - self.structs_order = [] - self.namespaces = Resolver.NAMESPACES # save all namespaces - self.curStruct = None - self.stack = [] # full name stack, good idea to keep both stacks? (simple stack and full stack) - self._classes_brace_level = {} # class name : level - self._structs_brace_level = {} # struct type : level - self._method_body = None - self._forward_decls = [] - self._template_typenames = [] # template<typename XXX> - - def current_namespace(self): return self.cur_namespace(True) - - def cur_namespace(self, add_double_colon=False): - rtn = "" - i = 0 - while i < len(self.nameSpaces): - rtn += self.nameSpaces[i] - if add_double_colon or i < len(self.nameSpaces) - 1: rtn += "::" - i+=1 - return rtn - - - def guess_ctypes_type( self, string ): - pointers = string.count('*') - string = string.replace('*','') - - a = string.split() - if 'unsigned' in a: u = 'u' - else: u = '' - if 'long' in a and 'double' in a: b = 'longdouble' # there is no ctypes.c_ulongdouble (this is a 64bit float?) - elif a.count('long') == 2 and 'int' in a: b = '%sint64' %u - elif a.count('long') == 2: b = '%slonglong' %u - elif 'long' in a: b = '%slong' %u - elif 'double' in a: b = 'double' # no udouble in ctypes - elif 'short' in a: b = '%sshort' %u - elif 'char' in a: b = '%schar' %u - elif 'wchar' in a: b = 'wchar' - elif 'bool' in a: b = 'bool' - elif 'float' in a: b = 'float' - - elif 'int' in a: b = '%sint' %u - elif 'int8' in a: b = 'int8' - elif 'int16' in a: b = 'int16' - elif 'int32' in a: b = 'int32' - elif 'int64' in a: b = 'int64' - - elif 'uint' in a: b = 'uint' - elif 'uint8' in a: b = 'uint8' - elif 'uint16' in a: b = 'uint16' - elif 'uint32' in a: b = 'uint32' - elif 'uint64' in a: b = 'uint64' - - elif 'size_t' in a: b = 'size_t' - elif 'void' in a: b = 'void_p' - - 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 - else: b = 'void_p' - - if not pointers: return 'ctypes.c_%s' %b - else: - x = '' - for i in range(pointers): x += 'ctypes.POINTER(' - x += 'ctypes.c_%s' %b - x += ')' * pointers - return x - - def resolve_type( self, string, result ): # recursive - ''' - keeps track of useful things like: how many pointers, number of typedefs, is fundamental or a class, etc... - ''' - ## be careful with templates, what is inside <something*> can be a pointer but the overall type is not a pointer - ## these come before a template - s = string.split('<')[0] - result[ 'constant' ] += s.split().count('const') - result[ 'static' ] += s.split().count('static') - result[ 'mutable' ] = 'mutable' in s.split() - - ## these come after a template - s = string.split('>')[-1] - result[ 'pointer' ] += s.count('*') - result[ 'reference' ] += s.count('&') - - - x = string; alias = False - for a in '* & const static mutable'.split(): x = x.replace(a,'') - for y in x.split(): - if y not in self.C_FUNDAMENTAL: alias = y; break - - #if alias == 'class': - # result['class'] = result['name'] # forward decl of class - # result['forward_decl'] = True - if alias == '__extension__': result['fundamental_extension'] = True - elif alias: - result['aliases'].append( alias ) - if alias in C99_NONSTANDARD: - result['type'] = C99_NONSTANDARD[ alias ] - result['typedef'] = alias - result['typedefs'] += 1 - elif alias in self.typedefs: - result['typedefs'] += 1 - result['typedef'] = alias - self.resolve_type( self.typedefs[alias], result ) - elif alias in self.classes: - klass = self.classes[alias]; result['fundamental'] = False - result['class'] = klass - result['unresolved'] = False - else: result['unresolved'] = True - else: - result['fundamental'] = True - result['unresolved'] = False - + C_FUNDAMENTAL = 'size_t unsigned signed bool char wchar short int float double long void'.split() + C_FUNDAMENTAL += 'struct union enum'.split() + + + SubTypedefs = {} # TODO deprecate? + NAMESPACES = [] + CLASSES = {} + STRUCTS = {} + + def initextra(self): + self.typedefs = {} + self.typedefs_info = {} + self.typedefs_order = [] + self.classes_order = [] + self.template_classes = {} + self.template_typedefs = {} + self.structs = Resolver.STRUCTS + self.structs_order = [] + self.namespaces = Resolver.NAMESPACES # save all namespaces + self.curStruct = None + self.stack = [] # full name stack, good idea to keep both stacks? (simple stack and full stack) + self._classes_brace_level = {} # class name : level + self._structs_brace_level = {} # struct type : level + self._method_body = None + self._forward_decls = [] + self._template_typenames = [] # template<typename XXX> + self.functions = [] # free functions + + def current_namespace(self): return self.cur_namespace(True) + + def cur_namespace(self, add_double_colon=False): + rtn = "" + i = 0 + while i < len(self.nameSpaces): + rtn += self.nameSpaces[i] + if add_double_colon or i < len(self.nameSpaces) - 1: rtn += "::" + i+=1 + return rtn + + + def guess_ctypes_type( self, string ): + pointers = string.count('*') + string = string.replace('*','') + + a = string.split() + if 'unsigned' in a: u = 'u' + else: u = '' + if 'long' in a and 'double' in a: b = 'longdouble' # there is no ctypes.c_ulongdouble (this is a 64bit float?) + elif a.count('long') == 2 and 'int' in a: b = '%sint64' %u + elif a.count('long') == 2: b = '%slonglong' %u + elif 'long' in a: b = '%slong' %u + elif 'double' in a: b = 'double' # no udouble in ctypes + elif 'short' in a: b = '%sshort' %u + elif 'char' in a: + if u: b = 'ubyte' # no ctypes.c_uchar + else: b = 'char' + elif 'wchar' in a: b = 'wchar' + elif 'bool' in a: b = 'bool' + elif 'float' in a: b = 'float' + + elif 'int' in a: b = '%sint' %u + elif 'int8' in a: b = 'int8' + elif 'int16' in a: b = 'int16' + elif 'int32' in a: b = 'int32' + elif 'int64' in a: b = 'int64' + + elif 'uint' in a: b = 'uint' + elif 'uint8' in a: b = 'uint8' + elif 'uint16' in a: b = 'uint16' + elif 'uint32' in a: b = 'uint32' + elif 'uint64' in a: b = 'uint64' + + elif 'size_t' in a: b = 'size_t' + elif 'void' in a: b = 'void_p' + + 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 + else: b = 'void_p' + + if not pointers: return 'ctypes.c_%s' %b + else: + x = '' + for i in range(pointers): x += 'ctypes.POINTER(' + x += 'ctypes.c_%s' %b + x += ')' * pointers + return x + + def resolve_type( self, string, result ): # recursive + ''' + keeps track of useful things like: how many pointers, number of typedefs, is fundamental or a class, etc... + ''' + ## be careful with templates, what is inside <something*> can be a pointer but the overall type is not a pointer + ## these come before a template + s = string.split('<')[0] + if not result['constant']: result[ 'constant' ] = 'const' in s.split() + if not result['static']: result[ 'static' ] = 'static' in s.split() + if not result['mutable']: result[ 'mutable' ] = 'mutable' in s.split() + + ## these come after a template + s = string.split('>')[-1] + result[ 'pointer' ] += s.count('*') + result[ 'reference' ] += s.count('&') + + + x = string; alias = False + for a in '* & const static mutable'.split(): x = x.replace(a,'') + for y in x.split(): + if y not in self.C_FUNDAMENTAL: alias = y; break + + #if alias == 'class': + # result['class'] = result['name'] # forward decl of class + # result['forward_decl'] = True + if alias == '__extension__': result['fundamental_extension'] = True + elif alias:# and alias not in result['aliases']: + if alias in result['aliases']: return #print( result ); assert 0 # G3D::SkyParameters + result['aliases'].append( alias ) + if alias in C99_NONSTANDARD: + result['type'] = C99_NONSTANDARD[ alias ] + result['typedef'] = alias + result['typedefs'] += 1 + elif alias in self.typedefs: + result['typedefs'] += 1 + result['typedef'] = alias + self.resolve_type( self.typedefs[alias], result ) + + elif 'namespace' in result and result['namespace']+alias in self.typedefs and '::' not in alias: # is this always safe? + alias = result['namespace']+alias + result['typedefs'] += 1 + result['typedef'] = alias + self.resolve_type( self.typedefs[alias], result ) + + elif alias in self.classes: + klass = self.classes[alias]; result['fundamental'] = False + result['class'] = klass + result['unresolved'] = False + #else: result['unresolved'] = True + else: + result['fundamental'] = True + #result['unresolved'] = False - def finalize_vars(self): - for s in CppStruct.Structs: # vars within structs can be ignored if they do not resolve - for var in s['fields']: var['parent'] = s['type'] - #for c in self.classes.values(): - # for var in c.get_all_properties(): var['parent'] = c['name'] - ## RESOLVE ## - for var in CppVariable.Vars: - self.resolve_type( var['type'], var ) - #if 'method' in var and var['method']['name'] == '_notifyCurrentCamera': print(var); assert 0 + def finalize_vars(self): + for s in CppStruct.Structs: # vars within structs can be ignored if they do not resolve + for var in s['fields']: var['parent'] = s['type'] + #for c in self.classes.values(): + # for var in c.get_all_properties(): var['parent'] = c['name'] - # then find concrete type and best guess ctypes type # - for var in CppVariable.Vars: - if not var['aliases']: #var['fundamental']: - var['ctypes_type'] = self.guess_ctypes_type( var['type'] ) - else: - var['unresolved'] = False # below may test to True - if var['class']: - var['ctypes_type'] = 'ctypes.c_void_p' - else: - assert var['aliases'] - tag = var['aliases'][0] - - klass = None - nestedEnum = None - nestedStruct = None - nestedTypedef = None - if 'method' in var: - klass = var['method']['parent'] - if tag in var['method']['parent']._public_enums: - nestedEnum = var['method']['parent']._public_enums[ tag ] - elif tag in var['method']['parent']._public_structs: - nestedStruct = var['method']['parent']._public_structs[ tag ] - elif tag in var['method']['parent']._public_typedefs: - nestedTypedef = var['method']['parent']._public_typedefs[ tag ] - - - if '<' in tag: # should also contain '>' - var['template'] = tag # do not resolve templates - var['ctypes_type'] = 'ctypes.c_void_p' - var['unresolved'] = True - - elif nestedEnum: - enum = nestedEnum - if enum['type'] is int: - var['ctypes_type'] = 'ctypes.c_int' - var['raw_type'] = 'int' - - elif enum['type'] is str: - var['ctypes_type'] = 'ctypes.c_char_p' - var['raw_type'] = 'char*' - - var['enum'] = var['method']['path'] + '::' + enum['name'] - var['fundamental'] = True - - elif nestedStruct: - var['ctypes_type'] = 'ctypes.c_void_p' - var['raw_type'] = var['method']['path'] + '::' + nestedStruct['type'] - var['fundamental'] = False - - elif nestedTypedef: - var['fundamental'] = is_fundamental( nestedTypedef ) - if not var['fundamental']: - var['raw_type'] = var['method']['path'] + '::' + tag - - else: - _tag = tag - if '::' in tag and tag.split('::')[0] in self.namespaces: tag = tag.split('::')[-1] - con = self.concrete_typedef( _tag ) - if con: - var['concrete_type'] = con - var['ctypes_type'] = self.guess_ctypes_type( var['concrete_type'] ) - - elif tag in self.structs: - print( 'STRUCT', var ) - var['struct'] = tag - var['ctypes_type'] = 'ctypes.c_void_p' - var['raw_type'] = self.structs[tag]['namespace'] + '::' + tag - - elif tag in self._forward_decls: - var['forward_declared'] = tag - var['ctypes_type'] = 'ctypes.c_void_p' - - elif tag in self.global_enums: - enum = self.global_enums[ tag ] - if enum['type'] is int: - var['ctypes_type'] = 'ctypes.c_int' - var['raw_type'] = 'int' - elif enum['type'] is str: - var['ctypes_type'] = 'ctypes.c_char_p' - var['raw_type'] = 'char*' - var['enum'] = enum['namespace'] + enum['name'] - var['fundamental'] = True - - - elif var['parent']: - print( 'WARN unresolved', _tag) - var['ctypes_type'] = 'ctypes.c_void_p' - var['unresolved'] = True - - - elif tag.count('::')==1: - print( 'trying to find nested something in', tag ) - a = tag.split('::')[0] - b = tag.split('::')[-1] - if a in self.classes: # a::b is most likely something nested in a class - klass = self.classes[ a ] - if b in klass._public_enums: - print( '...found nested enum', b ) - enum = klass._public_enums[ b ] - if enum['type'] is int: - var['ctypes_type'] = 'ctypes.c_int' - var['raw_type'] = 'int' - elif enum['type'] is str: - var['ctypes_type'] = 'ctypes.c_char_p' - var['raw_type'] = 'char*' - if 'method' in var: var['enum'] = var['method']['path'] + '::' + enum['name'] - else: # class property - var['unresolved'] = True - var['fundamental'] = True - - else: var['unresolved'] = True # TODO klass._public_xxx - - elif a in self.namespaces: # a::b can also be a nested namespace - if b in self.global_enums: - enum = self.global_enums[ b ] - print(enum) - print(var) - assert 0 - - elif b in self.global_enums: # falling back, this is a big ugly - enum = self.global_enums[ b ] - assert a in enum['namespace'].split('::') - if enum['type'] is int: - var['ctypes_type'] = 'ctypes.c_int' - var['raw_type'] = 'int' - elif enum['type'] is str: - var['ctypes_type'] = 'ctypes.c_char_p' - var['raw_type'] = 'char*' - var['fundamental'] = True - - else: # boost::gets::crazy - print('NAMESPACES', self.namespaces) - print( a, b ) - print( '---- boost gets crazy ----' ) - var['ctypes_type'] = 'ctypes.c_void_p' - var['unresolved'] = True - - - elif 'namespace' in var and self.concrete_typedef(var['namespace']+tag): - #print( 'TRYING WITH NS', var['namespace'] ) - con = self.concrete_typedef( var['namespace']+tag ) - if con: - var['typedef'] = var['namespace']+tag - var['type'] = con - if 'struct' in con.split(): - var['raw_type'] = var['typedef'] - var['ctypes_type'] = 'ctypes.c_void_p' - else: - self.resolve_type( var['type'], var ) - var['ctypes_type'] = self.guess_ctypes_type( var['type'] ) - - elif '::' in var: - var['ctypes_type'] = 'ctypes.c_void_p' - var['unresolved'] = True - - elif tag in self.SubTypedefs: # TODO remove SubTypedefs - if 'property_of_class' in var or 'property_of_struct' in var: - print( 'class:', self.SubTypedefs[ tag ], 'tag:', tag ) - var['typedef'] = self.SubTypedefs[ tag ] # class name - var['ctypes_type'] = 'ctypes.c_void_p' - else: - print( "WARN-this should almost never happen!" ) - print( var ); print('-'*80) - var['unresolved'] = True - - elif tag in self._template_typenames: - var['typename'] = tag - var['ctypes_type'] = 'ctypes.c_void_p' - var['unresolved'] = True # TODO, how to deal with templates? - - elif tag.startswith('_'): # assume starting with underscore is not important for wrapping - print( 'WARN unresolved', _tag) - var['ctypes_type'] = 'ctypes.c_void_p' - var['unresolved'] = True - - else: - print( 'WARN: unknown type', var ) - assert 'property_of_class' in var or 'property_of_struct' # only allow this case - var['unresolved'] = True - - - ## if not resolved and is a method param, not going to wrap these methods ## - if var['unresolved'] and 'method' in var: var['method']['unresolved_parameters'] = True - - - # create stripped raw_type # - p = '* & const static'.split() - for var in CppVariable.Vars: - if 'raw_type' in var: continue - - raw = [] - for x in var['type'].split(): - if x not in p: raw.append( x ) - var['raw_type'] = ' '.join( raw ) - - #if 'AutoConstantEntry' in var['raw_type']: print(var); assert 0 - if var['class']: - if '::' not in var['raw_type']: - if not var['class']['parent']: - var['raw_type'] = var['class']['namespace'] + '::' + var['raw_type'] - elif var['class']['parent'] in self.classes: - parent = self.classes[ var['class']['parent'] ] - var['raw_type'] = parent['namespace'] + '::' + var['class']['name'] + '::' + var['raw_type'] - else: - var['unresolved'] = True - - elif '::' in var['raw_type'] and var['raw_type'].split('::')[0] not in self.namespaces: - var['raw_type'] = var['class']['namespace'] + '::' + var['raw_type'] - else: - var['unresolved'] = True + ## RESOLVE ## + for var in CppVariable.Vars: + if 'invalid' in var and var['invalid']: # rare cases where parser fails + if 'method' in var: var['method']['unresolved_parameters'] = True + if 'function' in var: var['function']['unresolved_parameters'] = True - elif 'forward_declared' in var and 'namespace' in var: - if '::' not in var['raw_type']: - var['raw_type'] = var['namespace'] + var['raw_type'] - elif '::' in var['raw_type'] and var['raw_type'].split('::')[0] in self.namespaces: - pass - else: print('-'*80); print(var); raise NotImplemented + var['unresolved'] = False # force False + self.resolve_type( var['type'], var ) - #if var['raw_type'] == 'Animation': print(var); assert 0 + # then find concrete type and best guess ctypes type # + for var in CppVariable.Vars: + if not var['aliases']: #var['fundamental']: + var['ctypes_type'] = self.guess_ctypes_type( var['type'] ) - ## need full name space for classes in raw type ## - #if var['class'] and '::' not in var['raw_type'] and var['class']['namespace']: + else: + var['unresolved'] = False # below may set to True + if var['class']: + var['ctypes_type'] = 'ctypes.c_void_p' + else: + assert var['aliases'] + tag = var['aliases'][-1] # get the last alias to resolve + + klass = None + nestedEnum = None + nestedStruct = None + nestedTypedef = None + template = None + if 'method' in var and 'parent' in var['method']: # if no 'parent' method of struct + klass = var['method']['parent'] + if tag in klass._public_enums: + nestedEnum = klass._public_enums[ tag ] + elif tag in klass._public_structs: + nestedStruct = klass._public_structs[ tag ] + elif tag in klass._public_typedefs: + nestedTypedef = klass._public_typedefs[ tag ] + elif 'template_typename' in klass and klass['template_typename'] == tag: + template = tag + + ############################ + if template: + var['raw_type'] = '(template)' + var['template'] = template + + elif tag in ('std::string', 'std::basic_string', 'std::basic_string<char>'): + var['std'] = 'string' + var['constant'] = True + var['raw_type'] = 'char' + var['pointer'] = 1 + var['type'] = 'const char*' + var['ctypes_type'] = 'ctypes.c_char_p' + var['reference'] = False # force False + var['fundamental'] = True + + elif '<' in tag: # should also contain '>' + #print(var) + T = tag.split('<')[0]#; print( self.typedefs_info.keys() ) + typename = tag.split('<')[-1].split('>')[0] + Tclass = self.find_template_class( T ) + if not Tclass: + var['unresolved'] = True; var['unresolved_template_class']=True; continue + + var['raw_type'] = Tclass['namespace'] + '::' + tag.split('::')[-1] + var['template'] = Tclass['template_typename']; var['template_class'] = T + var['ctypes_type'] = 'ctypes.c_void_p' + + Tdef = None + h = Tclass['namespace'] + '::' + Tclass['name'] + if h in self.template_typedefs: Tdef = self.template_typedefs[ h ] + + if Tclass['template_typename'] != typename: + if Tdef and typename in Tdef: + var['raw_type'] = Tdef[ typename ]['name'] # the typedef'ed name + else: var['unresolved'] = True + else: + var['template_requires_typename'] = True + + var['typename_fundamental'] = is_fundamental( typename ) + if not var['typename_fundamental']: + con = self.concrete_typedef( typename ) + if con: var['template'] = con + else: var['template_nonfundamental'] = True + + + elif nestedEnum: + enum = nestedEnum + if enum['type'] == 'int': + var['ctypes_type'] = 'ctypes.c_int' + var['raw_type'] = 'int' + + elif enum['type'] == 'char*': + var['ctypes_type'] = 'ctypes.c_char_p' + var['raw_type'] = 'char*' + + var['enum'] = var['method']['path'] + '::' + enum['name'] + var['fundamental'] = True + + elif nestedStruct: + var['ctypes_type'] = 'ctypes.c_void_p' + var['raw_type'] = var['method']['path'] + '::' + nestedStruct['type'] + var['fundamental'] = False + var['struct'] = nestedStruct['type'] + var['nested_struct'] = True # july17th + + elif nestedTypedef: + var['nested_typedef'] = nestedTypedef + var['fundamental'] = is_fundamental( nestedTypedef ) + if not var['fundamental']: + var['raw_type'] = var['method']['path'] + '::' + tag + var['ctypes_type'] = 'ctypes.c_void_p' + else: + var['raw_type'] = nestedTypedef + var['ctypes_type'] = self.guess_ctypes_type( nestedTypedef ) + else: - def concrete_typedef( self, key ): - if key not in self.typedefs: - #print( 'FAILED typedef', key ) - return None - while key in self.typedefs: - prev = key - key = self.typedefs[ key ] - if '<' in key or '>' in key: return prev # stop at template - if key.startswith('std::'): return key # stop at std lib - return key + _tag = tag + if '::' in tag and tag.split('::')[0] in self.namespaces: tag = '::'.join(tag.split('::')[1:]) #tag.split('::')[-1] + con = self.concrete_typedef( _tag ) + + if not con: + for ns in self.namespaces: + con = self.concrete_typedef( ns + '::' + _tag ) + if con: break + + if con: + var['concrete_type'] = _tag + var['type'] = con + self.resolve_type( var['type'], var ) + var['ctypes_type'] = self.guess_ctypes_type( var['type'] ) + + elif tag in self.structs: + trace_print( 'STRUCT', var ) + var['struct'] = tag + var['ctypes_type'] = 'ctypes.c_void_p' + var['raw_type'] = self.structs[tag]['namespace'] + '::' + tag + + elif tag in self._forward_decls: + var['forward_declared'] = tag + var['ctypes_type'] = 'ctypes.c_void_p' + + elif tag in self.global_enums: + enum = self.global_enums[ tag ] + if enum['type'] == 'int': + var['ctypes_type'] = 'ctypes.c_int' + var['raw_type'] = 'int' + elif enum['type'] == 'char*': + var['ctypes_type'] = 'ctypes.c_char_p' + var['raw_type'] = 'char*' + var['enum'] = enum['namespace'] + enum['name'] + var['fundamental'] = True + + + elif var['parent']: + print( 'WARN unresolved', _tag) + var['ctypes_type'] = 'ctypes.c_void_p' + var['unresolved'] = True + + + elif tag.count('::')==1: + trace_print( 'trying to find nested something in', tag ) + a = tag.split('::')[0] + b = tag.split('::')[-1] + if a in self.classes: # a::b is most likely something nested in a class + klass = self.classes[ a ] + if b in klass._public_enums: + trace_print( '...found nested enum', b ) + enum = klass._public_enums[ b ] + if enum['type'] == 'int': + var['ctypes_type'] = 'ctypes.c_int' + var['raw_type'] = 'int' + elif enum['type'] == 'char*': + var['ctypes_type'] = 'ctypes.c_char_p' + var['raw_type'] = 'char*' + + if klass['namespace']: var['enum'] = '::'.join( [klass['namespace'], klass['name'], enum['name']] ) + else: var['enum'] = '::'.join( [klass['name'], enum['name']] ) + + var['fundamental'] = True + + else: var['unresolved'] = True # TODO klass._public_xxx + + elif a in self.namespaces: # a::b can also be a nested namespace + if b in self.global_enums: + enum = self.global_enums[ b ] + trace_print(enum) + trace_print(var) + assert 0 + + elif b in self.global_enums: # falling back, this is a big ugly + enum = self.global_enums[ b ] + assert a in enum['namespace'].split('::') + if enum['type'] == 'int': + var['ctypes_type'] = 'ctypes.c_int' + var['raw_type'] = 'int' + elif enum['type'] == 'char*': + var['ctypes_type'] = 'ctypes.c_char_p' + var['raw_type'] = 'char*' + var['enum'] = enum['namespace'] + enum['name'] + var['fundamental'] = True + if '::' in var['enum']: + ns = var['enum'].split('::')[0] + assert ns in self.namespaces + if self.get_parent_namespace( ns ): + var['enum'] = self.get_parent_namespace( ns ) + '::' + var['enum'] + + else: # boost::gets::crazy + trace_print('NAMESPACES', self.namespaces) + trace_print( a, b ) + trace_print( '---- boost gets crazy ----' ) + var['ctypes_type'] = 'ctypes.c_void_p' + var['unresolved'] = True + + + elif 'namespace' in var and self.concrete_typedef(var['namespace']+tag): + #print( 'TRYING WITH NS', var['namespace'] ) + con = self.concrete_typedef( var['namespace']+tag ) + if con: + var['typedef'] = var['namespace']+tag + var['type'] = con + if 'struct' in con.split(): + var['raw_type'] = var['typedef'] + var['ctypes_type'] = 'ctypes.c_void_p' + else: + self.resolve_type( var['type'], var ) + var['ctypes_type'] = self.guess_ctypes_type( var['type'] ) + + elif '::' in var: + var['ctypes_type'] = 'ctypes.c_void_p' + var['unresolved'] = True + + elif tag in self.SubTypedefs: # TODO remove SubTypedefs + if 'property_of_class' in var or 'property_of_struct' in var: + trace_print( 'class:', self.SubTypedefs[ tag ], 'tag:', tag ) + var['typedef'] = self.SubTypedefs[ tag ] # class name + var['ctypes_type'] = 'ctypes.c_void_p' + else: + trace_print( "WARN-this should almost never happen!" ) + trace_print( var ); trace_print('-'*80) + var['unresolved'] = True + + elif tag in self._template_typenames: + var['typename'] = tag + var['ctypes_type'] = 'ctypes.c_void_p' + var['unresolved'] = True # TODO, how to deal with templates? + + elif tag.startswith('_'): # assume starting with underscore is not important for wrapping + print( 'WARN unresolved', _tag) + var['ctypes_type'] = 'ctypes.c_void_p' + var['unresolved'] = True + + else: + trace_print( 'WARN: unknown type', var ) + if not 'property_of_class' in var or not 'property_of_struct' in var: print('TODO - fixme', var['name'], var['type']) + var['unresolved'] = True + + + ## if not resolved and is a method param, not going to wrap these methods ## + if var['unresolved'] and 'method' in var: var['method']['unresolved_parameters'] = True + if var['unresolved'] and 'function' in var: var['function']['unresolved_parameters'] = True + if 'template' in var and 'method' in var: var['method']['template'] = True + + + # create stripped raw_type # + p = '* & const static mutable'.split() + for var in CppVariable.Vars: + if 'raw_type' not in var: + raw = [] + for x in var['type'].split(): + if x not in p: raw.append( x ) + var['raw_type'] = ' '.join( raw ) + + if var['class']: + if '::' not in var['raw_type']: + if not var['class']['parent']: + var['raw_type'] = var['class']['namespace'] + '::' + var['raw_type'] + elif var['class']['parent'] in self.classes: + parent = self.classes[ var['class']['parent'] ] + var['raw_type'] = parent['namespace'] + '::' + var['class']['name'] + '::' + var['raw_type'] + else: + var['unresolved'] = True + + elif '::' in var['raw_type'] and var['raw_type'].split('::')[0] not in self.namespaces: + var['raw_type'] = var['class']['namespace'] + '::' + var['raw_type'] + else: + var['unresolved'] = True + + elif 'forward_declared' in var and 'namespace' in var: + if '::' not in var['raw_type']: + var['raw_type'] = var['namespace'] + var['raw_type'] + elif '::' in var['raw_type'] and var['raw_type'].split('::')[0] in self.namespaces: + pass + else: trace_print('-'*80); trace_print(var); raise NotImplemented + + ## need full name space for classes in raw type ## + if var['raw_type'].startswith( '::' ): + var['unresolved'] = True + + if 'method' in var and var['unresolved']: var['method']['unresolved_parameters'] = True + if 'function' in var and var['unresolved']: var['function']['unresolved_parameters'] = True + + def concrete_typedef( self, key ): + if key not in self.typedefs: + #print( 'FAILED typedef', key ) + return None + checked = [] + while key in self.typedefs and key not in checked: + prev = key + key = self.typedefs[ key ] + if '<' in key or '>' in key: return prev # stop at template + elif key.startswith('std::'): return key # stop at std lib + checked.append( key ) + return key + + def get_parent_namespace(self, ns): + for p in self.namespaces: + if ns != p and ns in p.split('::') and p.split('::')[0] != ns: + return p.split('::')[0] + + def find_template_class( self, T ): + if T in self.template_classes: return self.template_classes[ T ] + else: + for name in self.template_classes: + if T in name: return self.template_classes[ name ] class _CppHeader( Resolver ): - def finalize(self): - self.finalize_vars() - # finalize classes and method returns types - for cls in self.classes.values(): - for meth in cls.get_all_methods(): - if meth['pure_virtual']: cls['abstract'] = True - - if not meth['returns_fundamental'] and meth['returns'] in C99_NONSTANDARD: - meth['returns'] = C99_NONSTANDARD[meth['returns']] - meth['returns_fundamental'] = True - - elif not meth['returns_fundamental']: # describe the return type - con = None - if cls['namespace'] and '::' not in meth['returns']: - con = self.concrete_typedef( cls['namespace'] + '::' + meth['returns'] ) - else: con = self.concrete_typedef( meth['returns'] ) - - - if con: - meth['returns_concrete'] = con - meth['returns_fundamental'] = is_fundamental( con ) - - elif meth['returns'] in self.classes: - print( 'meth returns class:', meth['returns'] ) - meth['returns_class'] = True - - elif meth['returns'] in self.SubTypedefs: - meth['returns_class'] = True - meth['returns_nested'] = self.SubTypedefs[ meth['returns'] ] - - elif meth['returns'] in cls._public_enums: - enum = cls._public_enums[ meth['returns'] ] - meth['returns_enum'] = enum['type'] - meth['returns_fundamental'] = True - if enum['type'] == int: meth['returns'] = 'int' - else: meth['returns'] = 'char*' - - elif meth['returns'] in self.global_enums: - enum = self.global_enums[ meth['returns'] ] - meth['returns_enum'] = enum['type'] - meth['returns_fundamental'] = True - if enum['type'] == int: meth['returns'] = 'int' - else: meth['returns'] = 'char*' - - elif meth['returns'].count('::')==1: - print( meth ) - a,b = meth['returns'].split('::') - if a in self.namespaces: - if b in self.classes: - klass = self.classes[ b ] - meth['returns_class'] = a + '::' + b - elif '<' in b and '>' in b: - print( 'WARN-can not return template:', b ) - meth['returns_unknown'] = True - elif b in self.global_enums: - enum = self.global_enums[ b ] - meth['returns_enum'] = enum['type'] - meth['returns_fundamental'] = True - if enum['type'] == int: meth['returns'] = 'int' - else: meth['returns'] = 'char*' - - else: print( a, b); print( meth); assert 0 - - elif a in self.classes: - klass = self.classes[ a ] - if b in klass._public_enums: - print( '...found nested enum', b ) - enum = klass._public_enums[ b ] - meth['returns_enum'] = enum['type'] - meth['returns_fundamental'] = True - if enum['type'] == int: meth['returns'] = 'int' - else: meth['returns'] = 'char*' - - elif b in klass._public_forward_declares: - meth['returns_class'] = True - - elif b in klass._public_typedefs: - typedef = klass._public_typedefs[ b ] - meth['returns_fundamental'] = is_fundamental( typedef ) - - else: print( meth ); raise NotImplemented - - elif '::' in meth['returns']: - print('TODO namespace or extra nested return:', meth) - meth['returns_unknown'] = True - else: - print( 'WARN: UNKNOWN RETURN', meth['name'], meth['returns']) - meth['returns_unknown'] = True - - for cls in self.classes.values(): - methnames = cls.get_all_method_names() - pvm = cls.get_all_pure_virtual_methods() - - for d in cls['inherits']: - c = d['class'] - a = d['access'] # do not depend on this to be 'public' - print( 'PARENT CLASS:', c ) - if c not in self.classes: print('WARN: parent class not found') - if c in self.classes and self.classes[c]['abstract']: - p = self.classes[ c ] - for meth in p.get_all_methods(): #p["methods"]["public"]: - print( '\t\tmeth', meth['name'], 'pure virtual', meth['pure_virtual'] ) - if meth['pure_virtual'] and meth['name'] not in methnames: cls['abstract'] = True; break - - - - - - def evaluate_struct_stack(self): - """Create a Struct out of the name stack (but not its parts)""" - #print( 'eval struct stack', self.nameStack ) - #if self.braceDepth != len(self.nameSpaces): return - struct = CppStruct(self.nameStack) - struct["namespace"] = self.cur_namespace() - self.structs[ struct['type'] ] = struct - self.structs_order.append( struct ) - if self.curClass: - struct['parent'] = self.curClass - klass = self.classes[ self.curClass ] - klass['structs'][self.curAccessSpecifier].append( struct ) - if self.curAccessSpecifier == 'public': klass._public_structs[ struct['type'] ] = struct - self.curStruct = struct - self._structs_brace_level[ struct['type'] ] = self.braceDepth - - ## python style ## - PYTHON_OPERATOR_MAP = { - '()' : '__call__', - '[]' : '__getitem__', - '<' : '__lt__', - '<=' : '__le__', - '==' : '__eq__', - '!=' : '__ne__', - '>' : '__gt__', - '>=' : '__ge__', - '+' : '__add__', - '-' : '__sub__', - '*' : '__mul__', - '%' : '__divmod__', - '**' : '__pow__', - '>>' : '__lshift__', - '<<' : '__rshift__', - '&' : '__and__', - '^' : '__xor__', - '|' : '__or__', - '+=' : '__iadd__', - '-=' : '__isub__', - '*=' : '__imult__', - '/=' : '__idiv__', - '%=' : '__imod__', - '**=' : '__ipow__', - '<<=' : '__ilshift__', - '>>=' : '__irshift__', - '&=' : '__iand__', - '^=' : '__ixor__', - '|=' : '__ior__', - #__neg__ __pos__ __abs__; what are these in c++? - '~' : '__invert__', - '.' : '__getattr__', - } - OPERATOR_MAP = { # do not use double under-scores so no conflicts with python # - '=' : '_assignment_', - '->' : '_select_member_', - '++' : '_increment_', - '--' : '_deincrement_', - 'new' : '_new_', - 'delete' : '_delete_', - } - OPERATOR_MAP.update( PYTHON_OPERATOR_MAP ) - - def parse_method_type( self, stack ): - #print( 'meth type info', stack ) - if stack[0] in ':;': stack = stack[1:] - info = { - 'debug': ' '.join(stack), - 'class':None, - 'namespace':self.cur_namespace(add_double_colon=True), - } - - 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 - header = stack[ : stack.index('(') ] - header = ' '.join( header ) - header = header.replace(' : : ', '::' ) - header = header.replace(' < ', '<' ) - header = header.replace(' > ', '> ' ) - header = header.strip() - - if '{' in stack: - info['defined'] = True - self._method_body = self.braceDepth - print( 'NEW METHOD WITH BODY', self.braceDepth ) - elif stack[-1] == ';': - info['defined'] = False - self._method_body = None # not a great idea to be clearing here - else: assert 0 - - if len(stack) > 3 and stack[-1] == ';' and stack[-2] == '0' and stack[-3] == '=': - info['pure_virtual'] = True - - r = header.split() - name = None - if 'operator' in stack: # rare case op overload defined outside of class - op = stack[ stack.index('operator')+1 : stack.index('(') ] - op = ''.join(op) - if not op: - print( 'TODO - parse [] and () operators' ) - return None - else: - info['operator'] = op - if op in self.OPERATOR_MAP: - name = '__operator__' + self.OPERATOR_MAP[ op ] - a = stack[ : stack.index('operator') ] - else: - print('ERROR - not a C++ operator', op) - return None - - elif r: # June 23 2011 - name = r[-1] - a = r[ : -1 ] # strip name - - if name is None: return None - #if name.startswith('~'): name = name[1:] - - while a and a[0] == '}': # strip - can have multiple } } - a = a[1:] # july3rd - - - if '::' in name: - #klass,name = name.split('::') # methods can be defined outside of class - klass = name[ : name.rindex('::') ] - name = name.split('::')[-1] - info['class'] = klass - # info['name'] = name - #else: info['name'] = name - - if name.startswith('~'): - info['destructor'] = True - name = name[1:] - elif not a: - info['constructor'] = True - - info['name'] = name - - for tag in 'extern virtual static explicit inline friend'.split(): - if tag in a: info[ tag ] = True; a.remove( tag ) # inplace - if 'template' in a: - a.remove('template') - b = ' '.join( a ) - if '>' in b: - info['template'] = b[ : b.index('>')+1 ] - info['returns'] = b[ b.index('>')+1 : ] # find return type, could be incorrect... TODO - if '<typename' in info['template'].split(): - typname = info['template'].split()[-1] - typname = typname[ : -1 ] # strip '>' - if typname not in self._template_typenames: self._template_typenames.append( typname ) - else: info['returns'] = ' '.join( a ) - else: info['returns'] = ' '.join( a ) - info['returns'] = info['returns'].replace(' <', '<').strip() - - ## be careful with templates, do not count pointers inside template - info['returns_pointer'] = info['returns'].split('>')[-1].count('*') - if info['returns_pointer']: info['returns'] = info['returns'].replace('*','').strip() - - info['returns_reference'] = '&' in info['returns'] - if info['returns']: info['returns'] = info['returns'].replace('&','').strip() - - a = [] - for b in info['returns'].split(): - if b == '__const__': info['returns_const'] = True - elif b == 'const': info['returns_const'] = True - else: a.append( b ) - info['returns'] = ' '.join( a ) - - info['returns_fundamental'] = is_fundamental( info['returns'] ) - return info - - def evaluate_method_stack(self): - """Create a method out of the name stack""" - - if self.curStruct: - print( 'WARN - struct contains methods - skipping' ) - print( self.stack ) - assert 0 + def finalize_return( self, meth, cls=None ): + if meth['returns'] in 'return if else + for inline case break'.split(): + meth['returns_invalid'] = True + return # TODO more irrlicht tests + + + if not meth['returns_fundamental'] and meth['returns'] in C99_NONSTANDARD: + meth['returns'] = C99_NONSTANDARD[meth['returns']] + meth['returns_fundamental'] = True + + elif not meth['returns_fundamental']: # describe the return type + + con = self.concrete_typedef( meth['returns'] ) + if not con and cls: + if cls['parent'] and '::' not in meth['returns']: + parent = self.classes[ cls['parent'] ] + con = self.concrete_typedef( parent['namespace'] + '::' + meth['returns'] ) + elif cls['namespace'] and '::' not in meth['returns']: + con = self.concrete_typedef( cls['namespace'] + '::' + meth['returns'] ) + + if not con: + for ns in self.namespaces: + con = self.concrete_typedef( ns + '::' + meth['returns'] ) + if con: break + + if con: + if con == 'std::size_t': con = 'size_t' + meth['returns_typedef'] = meth['returns'] + meth['returns'] = con + meth['returns_fundamental'] = is_fundamental( con ) + if not meth['returns_fundamental']: + if 'typename' in con.split(): meth['returns_invalid'] = True # TODO + elif con in self.classes: meth['returns_class'] = True + else: meth['returns_unknown'] = True + + elif meth['returns'] in self.classes: + trace_print( 'meth returns class:', meth['returns'] ) + meth['returns_class'] = True + + elif meth['returns'] in self.SubTypedefs: + clsname = self.SubTypedefs[ meth['returns'] ] + meth['returns_nested'] = clsname + '::' + meth['returns'] + klass = self.classes[ clsname ] + if meth['returns'] in klass._public_typedefs: + meth['returns'] = klass._public_typedefs[ meth['returns'] ] + if meth['returns'] in C99_NONSTANDARD: meth['returns'] = C99_NONSTANDARD[ meth['returns'] ] + meth['returns_fundamental'] = is_fundamental( meth['returns'] ) + if not meth['returns_fundamental']: meth['returns_class'] = True + else: + meth['returns_unsafe'] = True # protected or private + + elif cls and meth['returns'] in cls._public_enums: + enum = cls._public_enums[ meth['returns'] ] + meth['returns_enum'] = enum['type'] + meth['returns_fundamental'] = True + if enum['type'] == 'int': meth['returns'] = 'int' + else: meth['returns'] = 'char*' + + elif meth['returns'] in self.global_enums: + enum = self.global_enums[ meth['returns'] ] + meth['returns_enum'] = enum['type'] + meth['returns_fundamental'] = True + if enum['type'] == 'int': meth['returns'] = 'int' + else: meth['returns'] = 'char*' + + elif '::' in meth['returns'] and meth['returns'].split('::')[-1] in self.global_enums: + a = meth['returns'].split('::')[-1] + enum = self.global_enums[ a ] + meth['returns_enum'] = enum['type'] + meth['returns_fundamental'] = True + if enum['type'] == 'int': meth['returns'] = 'int' + else: meth['returns'] = 'char*' + + + elif meth['returns'].count('::')==1: + trace_print( meth ) + a,b = meth['returns'].split('::') + if a in self.namespaces: + if b in self.classes: + klass = self.classes[ b ] + meth['returns_class'] = a + '::' + b + + elif '<' in b and '>' in b: + meth['returns_unknown'] = True + meth['returns_template'] = True + #meth['returns_class'] = b # template returns are always classes? + + elif b in self.global_enums: + enum = self.global_enums[ b ] + meth['returns_enum'] = enum['type'] + meth['returns_fundamental'] = True + if enum['type'] == 'int': meth['returns'] = 'int' + else: meth['returns'] = 'char*' + + else: trace_print( a, b); trace_print( meth); meth['returns_unknown'] = True + + elif a in self.classes: + klass = self.classes[ a ] + if b in klass._public_enums: + trace_print( '...found nested enum', b ) + enum = klass._public_enums[ b ] + meth['returns_enum'] = enum['type'] + meth['returns_fundamental'] = True + if enum['type'] == 'int': meth['returns'] = 'int' + else: meth['returns'] = 'char*' + + elif b in klass._public_forward_declares: + meth['returns_class'] = True + + elif b in klass._public_typedefs: + meth['returns_nested_explicit'] = True + meth['returns_nested'] = meth['returns'] + meth['returns'] = klass._public_typedefs[ b ] + meth['returns_fundamental'] = is_fundamental( meth['returns'] ) + if not meth['returns_fundamental']: meth['returns_class'] = True - info = self.parse_method_type( self.stack ) - if info: - if info[ 'class' ] and info['class'] in self.classes: # case where methods are defined outside of class - newMethod = CppMethod(self.nameStack, info['name'], info) - klass = self.classes[ info['class'] ] - klass[ 'methods' ][ 'public' ].append( newMethod ) - newMethod['parent'] = klass - if klass['namespace']: newMethod['path'] = klass['namespace'] + '::' + klass['name'] - else: newMethod['path'] = klass['name'] - - elif self.curClass: # normal case - newMethod = CppMethod(self.nameStack, self.curClass, info) - klass = self.classes[self.curClass] - klass['methods'][self.curAccessSpecifier].append(newMethod) - newMethod['parent'] = klass - if klass['namespace']: newMethod['path'] = klass['namespace'] + '::' + klass['name'] - else: newMethod['path'] = klass['name'] + else: + trace_print( meth ) # should be a nested class, TODO fix me. + meth['returns_unknown'] = True - else: - print( 'free function?', self.nameStack ) - - self.stack = [] - - def _parse_typedef( self, stack, namespace='' ): - if not stack or 'typedef' not in stack: return - stack = list( stack ) # copy just to be safe - if stack[-1] == ';': stack.pop() - - while stack and stack[-1].isdigit(): stack.pop() # throw away array size for now - - idx = stack.index('typedef') - name = namespace + stack[-1] - s = '' - for a in stack[idx+1:-1]: - if a == '{': break - if not s or s[-1] in ':<>' or a in ':<>': s += a # keep compact - else: s += ' ' + a # spacing - - r = {'name':name, 'raw':s, 'type':s} - if not is_fundamental(s): - if 'struct' in s.split(): pass # TODO is this right? "struct ns::something" - elif '::' not in s: s = namespace + s # only add the current name space if no namespace given - #elif '::' in s: - # ns = s.split('::')[0] - # if ns not in self.namespaces: - r['type'] = s - if s: return r - - - def evaluate_typedef(self): - ns = self.cur_namespace(add_double_colon=True) - res = self._parse_typedef( self.stack, ns ) - if res: - name = res['name'] - self.typedefs[ name ] = res['type'] - if name not in self.typedefs_order: self.typedefs_order.append( name ) - - - def evaluate_property_stack(self): - """Create a Property out of the name stack""" - assert self.stack[-1] == ';' - if self.nameStack[0] == 'typedef': - if self.curClass: - typedef = self._parse_typedef( self.stack ) - name = typedef['name'] - klass = self.classes[ self.curClass ] - klass[ 'typedefs' ][ self.curAccessSpecifier ].append( name ) - if self.curAccessSpecifier == 'public': klass._public_typedefs[ name ] = typedef['type'] - Resolver.SubTypedefs[ name ] = self.curClass - else: assert 0 - elif self.curStruct or self.curClass: - newVar = CppVariable(self.nameStack) - newVar['namespace'] = self.current_namespace() - if self.curStruct: - self.curStruct[ 'fields' ].append( newVar ) - newVar['property_of_struct'] = self.curStruct - elif self.curClass: - klass = self.classes[self.curClass] - klass["properties"][self.curAccessSpecifier].append(newVar) - newVar['property_of_class'] = klass['name'] - - self.stack = [] # CLEAR STACK - - def evaluate_class_stack(self): - """Create a Class out of the name stack (but not its parts)""" - #dont support sub classes today - #print( 'eval class stack', self.nameStack ) - parent = self.curClass - if self.braceDepth > len( self.nameSpaces) and parent: - print( 'HIT NESTED SUBCLASS' ) - elif self.braceDepth != len(self.nameSpaces): - print( 'ERROR: WRONG BRACE DEPTH' ) - return - - self.curAccessSpecifier = 'private' # private is default - newClass = CppClass(self.nameStack) - print( 'NEW CLASS', newClass['name'] ) - self.classes_order.append( newClass ) # good idea to save ordering - self.stack = [] # fixes if class declared with ';' in closing brace - if parent: - newClass["namespace"] = self.classes[ parent ]['namespace'] + '::' + parent - newClass['parent'] = parent - self.classes[ parent ]['nested_classes'].append( newClass ) - ## supports nested classes with the same name ## - self.curClass = key = parent+'::'+newClass['name'] - self._classes_brace_level[ key ] = self.braceDepth - - elif newClass['parent']: # nested class defined outside of parent. A::B {...} - parent = newClass['parent'] - newClass["namespace"] = self.classes[ parent ]['namespace'] + '::' + parent - self.classes[ parent ]['nested_classes'].append( newClass ) - ## supports nested classes with the same name ## - self.curClass = key = parent+'::'+newClass['name'] - self._classes_brace_level[ key ] = self.braceDepth + elif '::' in meth['returns']: + trace_print('TODO namespace or extra nested return:', meth) + meth['returns_unknown'] = True - else: - newClass["namespace"] = self.cur_namespace() - key = newClass['name'] - self.curClass = newClass["name"] - self._classes_brace_level[ newClass['name'] ] = self.braceDepth - - if key in self.classes: - print( 'ERROR name collision:', key ) - self.classes[key].show() - print('-'*80) - newClass.show() - - assert key not in self.classes # namespace collision - self.classes[ key ] = newClass - - def evalute_forward_decl(self): - print( 'FORWARD DECL', self.nameStack ) - assert self.nameStack[0] == 'class' - name = self.nameStack[-1] - if self.curClass: - klass = self.classes[ self.curClass ] - klass['forward_declares'][self.curAccessSpecifier].append( name ) - if self.curAccessSpecifier == 'public': klass._public_forward_declares.append( name ) - else: self._forward_decls.append( name ) + elif self.concrete_typedef( meth['namespace'].split('::')[0] + '::' + meth['returns'] ): + con = self.concrete_typedef( meth['namespace'].split('::')[0] + '::' + meth['returns'] ) + if con == 'std::size_t': con = 'size_t' + meth['returns_typedef'] = meth['returns'] + meth['returns'] = con + meth['returns_fundamental'] = is_fundamental( con ) + if not meth['returns_fundamental']: + if 'typename' in con.split(): meth['returns_invalid'] = True # TODO + elif con in self.classes: meth['returns_class'] = True + else: meth['returns_unknown'] = True + + + else: + trace_print( 'WARN: UNKNOWN RETURN', meth['name'], meth['returns']) + #print( self.global_enums.keys() ) + meth['returns_unknown'] = True + + if meth['returns_fundamental']: meth['returns_ctypes'] = self.guess_ctypes_type( meth['returns'] ) + + def finalize(self): + ## finalize templates ## + for typedef in self.typedefs_info: + tdef = self.typedefs_info[ typedef ] + if 'template' in tdef: + T = self.template_typedefs[ tdef['template'] ] + if tdef['template'] not in self.template_classes: continue # TODO fix me + + cls = self.template_classes[ tdef['template'] ] + for info in T.values(): + if ',' in info['typename']: continue # TODO - complex templates + + info['fundamental'] = is_fundamental( info['typename'] ) + + if not info['fundamental']: + tn = info['typename'] + con = self.concrete_typedef( tn ) + if not con and cls: + if cls['parent'] and '::' not in tn: + parent = self.classes[ cls['parent'] ] + con = self.concrete_typedef( parent['namespace'] + '::' + tn ) + elif cls['namespace'] and '::' not in tn: + con = self.concrete_typedef( cls['namespace'] + '::' + tn ) + + if not con: + for ns in self.namespaces: + #print('trying', ns + '::' + tn) + con = self.concrete_typedef( ns + '::' + tn ) + if con: break + if con: + if con == 'std::size_t': con = 'size_t' + info['type'] = info['type'].replace('<%s>'%tn, '<%s>'%con) + info['fundamental'] = is_fundamental( con ) + info['alias'] = tn + info['typename'] = con + info['ctypes_type'] = self.guess_ctypes_type( con ) + cls['template_info'] = T # do not resolve structs now + + elif tn in self.structs: + info['fundamental'] = False + info['struct'] = True + info['ctypes_type'] = 'ctypes.c_void_p' + elif '::' in tn and tn.split('::')[-1] in self.structs: + info['fundamental'] = False + info['struct'] = True + info['ctypes_type'] = 'ctypes.c_void_p' + + else: print('TODO template', tn) + + + ## finalize variables ## + self.finalize_vars() + + # finalize method returns types + for cls in self.classes.values(): + for meth in cls.get_all_methods(): + if meth['pure_virtual']: cls['abstract'] = True + self.finalize_return( meth, cls ) + + ## finalize free function returns ## + for func in self.functions: self.finalize_return( func ) + + + ## find pure virtuals and mark abstract classes ## + for cls in self.classes.values(): + methnames = cls.get_all_method_names() + pvm = cls.get_all_pure_virtual_methods() + + for d in cls['inherits']: + c = d['class'] + a = d['access'] # do not depend on this to be 'public' + trace_print( 'PARENT CLASS:', c ) + if c not in self.classes: trace_print('WARN: parent class not found') + if c in self.classes and self.classes[c]['abstract']: + p = self.classes[ c ] + for meth in p.get_all_methods(): #p["methods"]["public"]: + trace_print( '\t\tmeth', meth['name'], 'pure virtual', meth['pure_virtual'] ) + if meth['pure_virtual'] and meth['name'] not in methnames: cls['abstract'] = True; break + + print self.typedefs.keys() + print '-------------' + for k in self.template_typedefs.keys(): print k + #assert 'irr::core::dimension2d<u32>' in self.typedefs + #assert 0 + + + + + def evaluate_struct_stack(self): + """Create a Struct out of the name stack (but not its parts)""" + #print( 'eval struct stack', self.nameStack ) + #if self.braceDepth != len(self.nameSpaces): return + struct = CppStruct(self.nameStack) + struct["namespace"] = self.cur_namespace() + self.structs_order.append( struct ) + parentStruct = self.curStruct + self.curStruct = struct + if not struct['type']: return # struct {} name; + + if self.curClass: + struct['parent'] = self.curClass + klass = self.classes[ self.curClass ] + klass['structs'][self.curAccessSpecifier].append( struct ) + if self.curAccessSpecifier == 'public': klass._public_structs[ struct['type'] ] = struct + self.structs[ self.curClass + '::' + struct['type'] ] = struct + + elif parentStruct: + print( 'TODO - struct in struct' ) + struct['nested'] = True + self.structs[ struct['type'] ] = struct + + else: + self.structs[ struct['type'] ] = struct + + self._structs_brace_level[ struct['type'] ] = self.braceDepth + + ## python style ## + PYTHON_OPERATOR_MAP = { + '()' : '__call__', + '[]' : '__getitem__', + '<' : '__lt__', + '<=' : '__le__', + '==' : '__eq__', + '!=' : '__ne__', + '>' : '__gt__', + '>=' : '__ge__', + '+' : '__add__', + '-' : '__sub__', + '*' : '__mul__', + '%' : '__divmod__', + '**' : '__pow__', + '>>' : '__lshift__', + '<<' : '__rshift__', + '&' : '__and__', + '^' : '__xor__', + '|' : '__or__', + '+=' : '__iadd__', + '-=' : '__isub__', + '*=' : '__imult__', + '/=' : '__idiv__', + '%=' : '__imod__', + '**=' : '__ipow__', + '<<=' : '__ilshift__', + '>>=' : '__irshift__', + '&=' : '__iand__', + '^=' : '__ixor__', + '|=' : '__ior__', + #__neg__ __pos__ __abs__; what are these in c++? + '~' : '__invert__', + '.' : '__getattr__', + } + OPERATOR_MAP = { # do not use double under-scores so no conflicts with python # + '=' : '_assignment_', + '->' : '_select_member_', + '++' : '_increment_', + '--' : '_deincrement_', + 'new' : '_new_', + 'delete' : '_delete_', + } + OPERATOR_MAP.update( PYTHON_OPERATOR_MAP ) + + def parse_method_type( self, stack ): + #print( 'meth type info', stack ) + if stack[0] in ':;': stack = stack[1:] + info = { + 'debug': ' '.join(stack), + 'class':None, + 'namespace':self.current_namespace(), + } + if stack[0] == 'extern': + info['extern'] = True + if stack[1] == '"C"': stack = stack[ 2 : ]; info['C'] = True + elif stack[1] == '"C++"': stack = stack[ 2 : ] + else: stack = stack[ 1 : ] + + 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 + + for a in ('__attribute__', '__attribute', 'throw'): + if a in stack: + x = []; hit=None; skip=0 + for i,b in enumerate(stack): + if b==a: hit = i + elif hit is not None and b=='(' and i-1 == hit: + skip = 1 + elif hit and b=='(': + skip += 1 + elif skip and b==')': + skip -= 1 + if skip == 0: hit = None + elif not hit and not skip: + x.append( b ) + if x: stack = x + while stack and stack[0]==')': stack.pop(0) + # is_method can be confused by properties like: "__pthread_unwind_buf_t __attribute__((__aligned__));" + # and ends up passing to here thinking its a method. + if '(' not in stack: return None + #print('AFTER------',stack) + + assert stack.count('(') == stack.count(')') + + header = stack[ : stack.index('(') ] + header = ' '.join( header ) + header = header.replace(' : : ', '::' ) + header = header.replace(' < ', '<' ) + header = header.replace(' > ', '> ' ) + header = header.strip() + + if '{' in stack: + info['defined'] = True + self._method_body = self.braceDepth + print( '----------NEW METHOD WITH BODY---------', self.braceDepth ) + elif stack[-1] == ';': + info['defined'] = False + self._method_body = None # this is force cleared in several other places + else: assert 0 + + if len(stack) > 3 and stack[-1] == ';' and stack[-2] == '0' and stack[-3] == '=': + info['pure_virtual'] = True + elif len(stack) > 2 and stack[-1] == ';' and stack[-2] == '=0': + info['pure_virtual'] = True + elif stack[-1] == '=0;': + info['pure_virtual'] = True + + r = header.split() + name = None + if 'operator' in stack: # rare case op overload defined outside of class + op = stack[ stack.index('operator')+1 : stack.index('(') ] + op = ''.join(op) + if not op: + trace_print( 'TODO - parse () operator' ) + return None + else: + info['operator'] = op + if op in self.OPERATOR_MAP: + name = '__operator__' + self.OPERATOR_MAP[ op ] + a = stack[ : stack.index('operator') ] + else: + trace_print('ERROR - not a C++ operator', op) + return None + + elif r: # June 23 2011 + name = r[-1] + a = r[ : -1 ] # strip name + + if name is None: print('HEAD', header); assert 0 #return None + + while a and a[0] == '}': # strip - can have multiple } } + a = a[1:] # july3rd + + print(name) + if '::' in name: + klass = name[ : name.rindex('::') ] + name = name.split('::')[-1] + info['class'] = klass + + if name.startswith('~'): + info['destructor'] = True + name = name[1:] + elif not a: + info['constructor'] = True + + info['name'] = name + + for tag in 'virtual static friend explicit inline __explicit__ __inline__'.split(): + if tag in a: info[ tag.replace('_','') ] = True; a.remove( tag ) # inplace + + + if 'template' in a: + a.remove('template') + b = ' '.join( a ) + if '>' in b: + info['template'] = b[ : b.index('>')+1 ] + info['returns'] = b[ b.index('>')+1 : ] # find return type, could be incorrect... TODO + if '<typename' in info['template'].split(): + typname = info['template'].split()[-1] + typname = typname[ : -1 ] # strip '>' + if typname not in self._template_typenames: self._template_typenames.append( typname ) + else: info['returns'] = ' '.join( a ) + else: info['returns'] = ' '.join( a ) + + info['returns'] = info['returns'].replace(" <","<") + info['returns'] = info['returns'].replace(" >",">") + info['returns'] = info['returns'].strip() + + ## be careful with templates, do not count pointers inside template + info['returns_pointer'] = info['returns'].split('>')[-1].count('*') + if info['returns_pointer']: info['returns'] = info['returns'].replace('*','').strip() + + info['returns_reference'] = '&' in info['returns'] + if info['returns']: info['returns'] = info['returns'].replace('&','').strip() + + a = [] + for b in info['returns'].split(): + if b == '__const__': info['returns_const'] = True + elif b == 'const': info['returns_const'] = True + else: a.append( b ) + info['returns'] = ' '.join( a ) + + info['returns_fundamental'] = is_fundamental( info['returns'] ) + return info + + def evaluate_method_stack(self): + """Create a method out of the name stack""" + + stack = [] ## fixes "/(" + for a in self.stack: + if a == '/(': stack.append( '/' ); stack.append('(') + else: stack.append( a ) + info = self.parse_method_type( stack ) + + if info: + if info[ 'class' ] and info['class'] in self.classes: # case where methods are defined outside of class + klass = self.classes[ info['class'] ] + if not info['name'] in klass.get_all_method_names(): # do not overwrite header defined method + print('External Method:', info['name']) + newMethod = CppMethod(self.nameStack, info['name'], info) + klass[ 'methods' ][ 'public' ].append( newMethod ) # it could be private, how do we know this. + newMethod['parent'] = klass + if klass['namespace']: newMethod['path'] = klass['namespace'] + '::' + klass['name'] + else: newMethod['path'] = klass['name'] + elif info['class']: pass + elif '::' in info['name']: print( info ); assert 0 + + elif self.curStruct: # struct is a class in c++ + print( 'Struct Method:', info ) + newMethod = CppMethod(self.nameStack, self.curStruct, info) + self.curStruct['methods'].append( newMethod ) + + elif self.curClass: # normal case + trace_print( 'Internal Method:', info ) + newMethod = CppMethod(self.nameStack, self.curClass, info) + klass = self.classes[self.curClass] + klass['methods'][self.curAccessSpecifier].append(newMethod) + newMethod['parent'] = klass + if klass['namespace']: newMethod['path'] = klass['namespace'] + '::' + klass['name'] + else: newMethod['path'] = klass['name'] + + + else: + print( 'free function:', self.nameStack ) + if 'operator' in info and info['operator']: + trace_print( 'skipping external method def with operator', info ) + else: + func = CppMethod(self.nameStack, methinfo=info) + self.functions.append( func ) + if func['name'] == 'setNull': print(info); assert 0 + + self.stack = [] + + def _parse_typedef( self, stack, namespace='' ): + if not stack or 'typedef' not in stack: return + stack = list( stack ) # copy just to be safe + if stack[-1] == ';': stack.pop() + + while stack and stack[-1].isdigit(): stack.pop() # throw away array size for now + + idx = stack.index('typedef') + name = namespace + stack[-1] + s = '' + for a in stack[idx+1:-1]: + if a == '{': break + if not s or s[-1] in ':<>' or a in ':<>': s += a # keep compact + else: s += ' ' + a # spacing + s = s.replace(' <', '<') + r = {'name':name, 'raw':s, 'type':s} + if not is_fundamental(s): + if 'struct' in s.split(): pass # TODO is this right? "struct ns::something" + elif '::' not in s: s = namespace + s # only add the current name space if no namespace given + #elif '::' in s: + # ns = s.split('::')[0] + # if ns not in self.namespaces: + r['type'] = s + if '<' in s: + r['template'] = s.split('<')[0] + r['typename'] = s.split('<')[-1].split('>')[0] + if s: return r + + + def evaluate_typedef(self): + ns = self.cur_namespace(add_double_colon=True) + res = self._parse_typedef( self.stack, ns ) + if res: + name = res['name'] + self.typedefs[ name ] = res['type'] + self.typedefs_info[ name ] = res + if name not in self.typedefs_order: self.typedefs_order.append( name ) + if 'template' in res: + T = res['template'] + if T not in self.template_typedefs: self.template_typedefs[ T ] = {} + self.template_typedefs[ T ][ res['typename'] ] = res + + def evaluate_property_stack(self): + """Create a Property out of the name stack""" + assert self.stack[-1] == ';' + if self.nameStack[0] == 'typedef': + if self.curClass: + typedef = self._parse_typedef( self.stack ) + name = typedef['name'] + klass = self.classes[ self.curClass ] + klass[ 'typedefs' ][ self.curAccessSpecifier ].append( name ) + if self.curAccessSpecifier == 'public': klass._public_typedefs[ name ] = typedef['type'] + Resolver.SubTypedefs[ name ] = self.curClass + else: assert 0 + elif self.curStruct or self.curClass: + newVar = CppVariable(self.nameStack, self.stack) + newVar['namespace'] = self.current_namespace() + if self.curStruct: + self.curStruct[ 'fields' ].append( newVar ) + newVar['property_of_struct'] = self.curStruct + elif self.curClass: + klass = self.classes[self.curClass] + klass["properties"][self.curAccessSpecifier].append(newVar) + newVar['property_of_class'] = klass['name'] + + self.stack = [] # CLEAR STACK + + def evaluate_class_stack(self): + """Create a Class out of the name stack (but not its parts)""" + parent = self.curClass + print('NEWCLASS', ' '.join(self.nameStack)) + + if self.braceDepth > len( self.nameSpaces) and (parent or self.curStruct): + print( 'HIT NESTED SUBCLASS' ) + elif self.braceDepth-1 != len(self.nameSpaces): + print( 'ERROR: WRONG BRACE DEPTH', self.braceDepth, self.nameSpaces ) + assert 0 + + self._method_body = None # force clear + + prevAccess = None + if parent: prevAccess = self.curAccessSpecifier + self.curAccessSpecifier = 'private' # private is default + self._current_access.append( self.curAccessSpecifier ) + + newClass = CppClass(self.nameStack) + if 'name' not in newClass or not newClass['name']: assert 0 + print( 'CLASS OK', newClass['name'] ) + if newClass['name'] in 'RenderQueueInvocationIterator RenderQueueInvocationList'.split(): assert 0 + + if '>' in newClass['name']: print('WARN: strange template class', newClass['name']) + + self.classes_order.append( newClass ) # good idea to save ordering + self.stack = [] # fixes if class declared with ';' in closing brace + + if parent: + newClass["namespace"] = self.classes[ parent ]['namespace'] + '::' + parent + newClass['parent'] = parent + self.classes[ parent ]['nested_classes'].append( newClass ) + ## supports nested classes with the same name ## + self.curClass = key = parent+'::'+newClass['name'] + self._classes_brace_level[ key ] = self.braceDepth + ## nested classes that are protected or private can not be exposed in a C wrapper ## + if prevAccess and prevAccess in ('protected', 'private'): newClass['internal'] = True + elif 'internal' in self.classes[parent]: newClass['internal'] = True + elif 'template_typename' in self.classes[parent]: newClass['internal'] = True; newClass['nested_in_template'] = True + + elif newClass['parent']: # nested class defined outside of parent. A::B {...} + parent = newClass['parent'] + newClass["namespace"] = self.classes[ parent ]['namespace'] + '::' + parent + self.classes[ parent ]['nested_classes'].append( newClass ) + ## supports nested classes with the same name ## + self.curClass = key = parent+'::'+newClass['name'] + self._classes_brace_level[ key ] = self.braceDepth + if 'internal' in self.classes[parent]: newClass['internal'] = True + elif 'template_typename' in self.classes[parent]: newClass['internal'] = True; newClass['nested_in_template'] = True + + else: + newClass["namespace"] = self.cur_namespace() + key = newClass['name'] + self.curClass = newClass["name"] + self._classes_brace_level[ newClass['name'] ] = self.braceDepth + + if newClass['namespace'] in ('std', 'boost'): + self.curClass = key = newClass['namespace'] + '::' + key + self._classes_brace_level[ key ] = self.braceDepth + + if key in self.classes: + print('WARN - name collision', key) + + self.classes[ key ] = newClass + if 'template_typename' in newClass: + self.template_classes[ self.current_namespace() + key ] = newClass + if newClass['name'] not in self.template_classes: + self.template_classes[ newClass['name'] ] = newClass + + + def evaluate_forward_decl(self): + trace_print( 'FORWARD DECL', self.nameStack ) + #assert self.nameStack[0] == 'class' + name = self.nameStack[-1] + if self.curClass: + klass = self.classes[ self.curClass ] + klass['forward_declares'][self.curAccessSpecifier].append( name ) + if self.curAccessSpecifier == 'public': klass._public_forward_declares.append( name ) + else: self._forward_decls.append( name ) class CppHeader( _CppHeader ): """Parsed C++ class header @@ -1508,7 +2054,42 @@ class CppHeader( _CppHeader ): def show(self): for className in self.classes.keys(): self.classes[className].show() - def __init__(self, headerFileName, argType="file"): + + def evaluate_enum_stack(self): + """Create an Enum out of the name stack""" + newEnum = CppEnum(self.nameStack) + if len(newEnum.keys()): + if len(self.curClass): + newEnum["namespace"] = self.cur_namespace(False) + klass = self.classes[self.curClass] + klass["enums"][self.curAccessSpecifier].append(newEnum) + if self.curAccessSpecifier == 'public': + if 'name' in newEnum and newEnum['name']: + klass._public_enums[ newEnum['name'] ] = newEnum + else: + newEnum["namespace"] = self.cur_namespace(True) + self.enums.append(newEnum) + if 'name' in newEnum and newEnum['name']: self.global_enums[ newEnum['name'] ] = newEnum + + #This enum has instances, turn them into properties + if newEnum.has_key("instances"): + instanceType = "enum" + if newEnum.has_key("name"): + instanceType = newEnum["name"] + for instance in newEnum["instances"]: + self.nameStack = [instanceType, instance] + self.evaluate_property_stack() + del newEnum["instances"] + + + def __init__(self, headerFileName, argType="file", **kwargs): + """Create the parsed C++ header file parse tree + + headerFileName - Name of the file to parse OR actual file contents (depends on argType) + argType - Indicates how to interpret headerFileName as a file string or file name + kwargs - Supports the following keywords + "enumMaintianValueFormat" - Set to true for enum values to maintain the original format ('j' will not convert to 106) + """ ## reset global state ## global doxygenCommentCache doxygenCommentCache = "" @@ -1526,6 +2107,12 @@ class CppHeader( _CppHeader ): else: raise Exception("Arg type must be either file or string") self.curClass = "" + + global enumMaintianValueFormat + if kwargs.has_key("enumMaintianValueFormat"): + enumMaintianValueFormat = kwargs["enumMaintianValueFormat"] + else: + enumMaintianValueFormat = False # nested classes have parent::nested, but no extra namespace, # this keeps the API compatible, TODO proper namespace for everything. @@ -1536,8 +2123,9 @@ class CppHeader( _CppHeader ): self.global_enums = {} self.nameStack = [] self.nameSpaces = [] - self.curAccessSpecifier = 'private' # private is default - self.initextra() # harts hack + self.curAccessSpecifier = 'private' # private is default + self._current_access = [] + self.initextra() # harts hack if (len(self.headerFileName)): headerFileStr = "\n".join(open(self.headerFileName).readlines()) @@ -1545,7 +2133,7 @@ class CppHeader( _CppHeader ): lex.input(headerFileStr) curLine = 0 curChar = 0 - if 1: #try: + if 1: #try: while True: tok = lex.token() if not tok: break @@ -1553,8 +2141,19 @@ class CppHeader( _CppHeader ): if tok.type not in ('PRECOMP_MACRO', 'PRECOMP_MACRO_CONT'): self.stack.append( tok.value ) curLine = tok.lineno curChar = tok.lexpos - if (tok.type == 'OPEN_BRACE'): - if len(self.nameStack) >= 2 and is_namespace(self.nameStack): # namespace {} with no name used in boost, this sets default? + + if tok.type in ('OPEN_BRACKET', 'CLOSE_BRACKET'): self.nameStack.append( tok.value ) + + elif (tok.type == 'OPEN_BRACE'): + _brace = True + if len(self.nameStack)>=2 and self.nameStack[0]=='extern' and self.nameStack[1]=='"C"': + _brace = False; print( 'extern C') + elif len(self.nameStack)>=2 and self.nameStack[0]=='extern' and self.nameStack[1]=='"C++"': + _brace = False; print( 'extern C++' ) + if _brace: self.braceDepth += 1 + + + if len(self.nameStack) >= 2 and is_namespace(self.nameStack): # namespace {} with no name used in boost, this sets default? self.nameSpaces.append(self.nameStack[1]) ns = self.cur_namespace(); self.stack = [] if ns not in self.namespaces: self.namespaces.append( ns ) @@ -1563,13 +2162,16 @@ class CppHeader( _CppHeader ): else: self.nameStack.append(tok.value) if self.stack and self.stack[0] == 'class': self.stack = [] - self.braceDepth += 1 + + #if _brace: self.braceDepth += 1 + elif (tok.type == 'CLOSE_BRACE'): + if self.braceDepth == 0: continue if (self.braceDepth == len(self.nameSpaces)): tmp = self.nameSpaces.pop() - self.stack = [] # clear stack when namespace ends? + self.stack = [] # clear stack when namespace ends? if len(self.nameStack) and is_enum_namestack(self.nameStack): self.nameStack.append(tok.value) elif self.braceDepth < 10: @@ -1577,18 +2179,29 @@ class CppHeader( _CppHeader ): else: self.nameStack = [] self.braceDepth -= 1 - #self.stack = []; print 'BRACE DEPTH', self.braceDepth, 'NS', len(self.nameSpaces) # June29 2011 + + if self.braceDepth < 0: + print('---------- END OF EXTERN -----------') + self.braceDepth = 0 + if self.curClass and debug: print( 'CURBD', self._classes_brace_level[ self.curClass ] ) - if (self.braceDepth == 0) or (self.curClass and self._classes_brace_level[self.curClass]==self.braceDepth): - print( 'END OF CLASS DEF' ) - if self.curClass and self.classes[ self.curClass ]['parent']: self.curClass = self.classes[ self.curClass ]['parent'] - else: self.curClass = ""; #self.curStruct = None + if (self.braceDepth == 0) or (self.curClass and self._classes_brace_level[self.curClass] > self.braceDepth): + if self.curClass: print( '------------END OF CLASS DEF-------------', 'braceDepth:', self.braceDepth ) + + if self._current_access: self._current_access.pop() + + if self.curClass and self.classes[ self.curClass ]['parent']: + self.curClass = self.classes[ self.curClass ]['parent'] + if self._current_access: self.curAccessSpecifier = self._current_access[-1] + else: + self.curClass = "" self.stack = [] #if self.curStruct: self.curStruct = None - if self.braceDepth == 0 or (self.curStruct and self._structs_brace_level[self.curStruct['type']]==self.braceDepth): - print( 'END OF STRUCT DEF' ) + if self.braceDepth==0 or (self.curStruct and not self.curStruct['type']) or (self.curStruct and self._structs_brace_level[self.curStruct['type']] > self.braceDepth): + if self.curStruct: print( '---------END OF STRUCT DEF-------------' ) + if self.curStruct and not self.curStruct['type']: self._struct_needs_name = self.curStruct self.curStruct = None if self._method_body and self.braceDepth < self._method_body: @@ -1596,39 +2209,52 @@ class CppHeader( _CppHeader ): if (tok.type == 'OPEN_PAREN'): self.nameStack.append(tok.value) + elif (tok.type == 'CLOSE_PAREN'): self.nameStack.append(tok.value) + elif (tok.type == 'EQUALS'): self.nameStack.append(tok.value) + elif (tok.type == 'COMMA'): self.nameStack.append(tok.value) + elif (tok.type == 'NUMBER'): self.nameStack.append(tok.value) + elif (tok.type == 'MINUS'): self.nameStack.append(tok.value) elif (tok.type == 'PLUS'): self.nameStack.append(tok.value) + elif (tok.type == 'STRING_LITERAL'): self.nameStack.append(tok.value) + elif (tok.type == 'NAME' or tok.type == 'AMPERSTAND' or tok.type == 'ASTERISK'): - if (tok.value == 'class'): - self.nameStack.append(tok.value) - elif tok.value in supportedAccessSpecifier: - if self.braceDepth == len(self.nameSpaces) + 1: self.curAccessSpecifier = tok.value; - self.stack = [] - else: - self.nameStack.append(tok.value) + self.nameStack.append(tok.value) + elif (tok.type == 'COLON'): #Dont want colon to be first in stack if len(self.nameStack) == 0: continue - self.nameStack.append(tok.value) + if self.nameStack and self.nameStack[-1] in supportedAccessSpecifier: + if self.curClass or self.curStruct: + cas = self.nameStack[-1] + self.curAccessSpecifier = cas; print('CURACCESS-set', cas) + if self.curClass: + if self._current_access: self._current_access[-1] = cas + else: self._current_access.append( cas ) + else: print('warning - "public ::namespace"', ' '.join(self.nameStack)) + + self.stack = []; self.nameStack = [] # need to clear nameStack to so that nested structs can be found + else: + self.nameStack.append(tok.value) elif (tok.type == 'SEMI_COLON'): if (self.braceDepth < 10): self.evaluate_stack( tok.type ) if not self.stack: continue - if self.stack[0]=='typedef' and ( '{' not in self.stack or '}' in self.stack ): self.stack = []; print( "REAL CLEAR") - elif self.stack[0] != 'typedef': self.stack = []; print('CLEAR STACK') + if self.stack[0]=='typedef' and ( '{' not in self.stack or '}' in self.stack ): self.stack = []; trace_print( "REAL CLEAR") + elif self.stack[0] != 'typedef': self.stack = []; trace_print('CLEAR STACK') #except: # raise CppParseError("Not able to parse %s on line %d evaluating \"%s\"\nError around: %s" @@ -1639,13 +2265,14 @@ class CppHeader( _CppHeader ): def evaluate_stack(self, token=None): """Evaluates the current name stack""" global doxygenCommentCache - if (debug): print( "Evaluating stack %s\nBraceDepth: %s" %(self.nameStack,self.braceDepth)) + print( "Evaluating stack %s\nBraceDepth: %s" %(self.nameStack,self.braceDepth)) + print( "Evaluating stack %s\nBraceDepth: %s" %(self.stack,self.braceDepth)) if (len(self.curClass)): if (debug): print( "%s (%s) "%(self.curClass, self.curAccessSpecifier)) - #if 'typedef' in self.nameStack: self.evaluate_typedef() # allows nested typedefs, probably a bad idea + #if 'typedef' in self.nameStack: self.evaluate_typedef() # allows nested typedefs, probably a bad idea if not self.curClass and 'typedef' in self.nameStack: - print('STACK', self.stack) + print('HIT TYPEDEF', self.stack) if token == 'SEMI_COLON' and ('{' not in self.stack or '}' in self.stack): self.evaluate_typedef() else: return @@ -1656,35 +2283,46 @@ class CppHeader( _CppHeader ): elif (self.nameStack[0] == "namespace"): #Taken care of outside of here pass - elif len(self.nameStack) >= 2 and self.nameStack[0] == 'using' and self.nameStack[1] == 'namespace': pass # TODO + elif len(self.nameStack) >= 2 and self.nameStack[0] == 'using' and self.nameStack[1] == 'namespace': pass # TODO elif is_enum_namestack(self.nameStack): if (debug): print( "line ",lineno() ) self.evaluate_enum_stack() - elif self._method_body and self.braceDepth > self._method_body: print( 'INSIDE METHOD DEF' ) - elif is_method_namestack(self.stack) and not self.curStruct and '(' in self.nameStack: # updated by hart - if (debug): print( "line ",lineno() ) + elif self._method_body and self.braceDepth >= self._method_body: + #print( 'INSIDE METHOD DEF', self.nameStack ) + self.stack = [] + + #elif is_method_namestack(self.stack) and '(' in self.nameStack: # this fails on "operator /(..." + elif ')' in self.nameStack and is_method_namestack(self.stack): + #print( 'eval method', self.nameStack ) self.evaluate_method_stack() - elif '(' not in self.nameStack and ')' not in self.nameStack and self.stack[-1] == ';': - if (debug): print( "line ",lineno() ) - if self.nameStack[0]=='class': self.evalute_forward_decl() - elif len(self.nameStack) >= 2 and (self.nameStack[0]=='friend' and self.nameStack[1]=='class'): pass - else: self.evaluate_property_stack() # catches class props and structs in a namespace + self.stack = [] - elif (self.nameStack[0] == "class"): - if (debug): print( "line ",lineno() ) + elif len(self.nameStack) >= 2 and (self.nameStack[0]=='friend' and self.nameStack[1]=='class'): pass + + elif ('class' in self.nameStack or 'struct' in self.nameStack) and self.stack[-1] == ';': self.evaluate_forward_decl() + + elif (self.nameStack[0] == "class") or (self.nameStack[0]=='template' and 'class' in self.nameStack): + #print('^^^^^^^^^^^^^^^^^^^^') self.evaluate_class_stack() - elif (self.nameStack[0] == "struct"): - if (debug): print( "line ",lineno() ) - ##this causes a bug when structs are nested in protected or private##self.curAccessSpecifier = "public" - self.evaluate_struct_stack() # hart's hack - do structs properly + elif (self.nameStack[0] == "struct") or (len(self.nameStack)>3 and self.stack[-1]=='{' and self.nameStack[-3]=='struct'): + print( '------------new struct-----------' ) + self.evaluate_struct_stack() + self.stack = [] + elif self.nameStack[0]=='template' and self.stack[-1]=='{' and 'struct' in self.nameStack: + print( '------------new struct - unsafe?' ) + self.evaluate_struct_stack() + self.stack = [] + + elif '(' not in self.nameStack and ')' not in self.nameStack and self.stack[-1] == ';': # catching all props? + self.evaluate_property_stack() elif not self.curClass: if (debug): print( "line ",lineno() ) if is_enum_namestack(self.nameStack): self.evaluate_enum_stack() - elif self.curStruct and self.stack[-1] == ';': self.evaluate_property_stack() # this catches fields of global structs + elif self.curStruct and self.stack[-1] == ';': self.evaluate_property_stack() # this catches fields of global structs self.nameStack = [] doxygenCommentCache = "" return @@ -1693,6 +2331,7 @@ class CppHeader( _CppHeader ): #Ignore global stuff for now if (debug): print( "Global stuff: ", self.nameStack ) self.nameStack = [] + self._method_body = None doxygenCommentCache = "" return elif (self.braceDepth > len(self.nameSpaces) + 1): @@ -1701,33 +2340,8 @@ class CppHeader( _CppHeader ): doxygenCommentCache = "" return - self.nameStack = [] # its a little confusing to have some if/else above return and others not, and then clearning the nameStack down here + self.nameStack = [] # some if/else above return and others not, so this may or may not be reset doxygenCommentCache = "" - - - def evaluate_enum_stack(self): - """Create an Enum out of the name stack""" - newEnum = CppEnum(self.nameStack) - if len(newEnum.keys()): - if len(self.curClass): - newEnum["namespace"] = self.cur_namespace(True) - klass = self.classes[self.curClass] - klass["enums"][self.curAccessSpecifier].append(newEnum) - if self.curAccessSpecifier == 'public' and 'name' in newEnum: klass._public_enums[ newEnum['name'] ] = newEnum - else: - newEnum["namespace"] = self.cur_namespace(True) - self.enums.append(newEnum) - if 'name' in newEnum and newEnum['name']: self.global_enums[ newEnum['name'] ] = newEnum - - #This enum has instances, turn them into properties - if newEnum.has_key("instances"): - instanceType = "enum" - if newEnum.has_key("name"): - instanceType = newEnum["name"] - for instance in newEnum["instances"]: - self.nameStack = [instanceType, instance] - self.evaluate_property_stack() - del newEnum["instances"] |