diff options
author | Alon Zakai <alonzakai@gmail.com> | 2011-07-06 20:50:04 -0700 |
---|---|---|
committer | Alon Zakai <alonzakai@gmail.com> | 2011-07-06 20:50:04 -0700 |
commit | 8e4dc2caa67d22b47f0a209ed1828c2a291aea85 (patch) | |
tree | 93c4237214da30e71b058c429f62490f024d876f /third_party | |
parent | c2d3b71f9959f90f38b360e9861c931a53b085f5 (diff) |
improved bindings generator
Diffstat (limited to 'third_party')
5 files changed, 1319 insertions, 823 deletions
diff --git a/third_party/CppHeaderParser/CppHeaderParser/CppHeaderParser.py b/third_party/CppHeaderParser/CppHeaderParser/CppHeaderParser.py index cc3755f7..675b74d8 100644 --- a/third_party/CppHeaderParser/CppHeaderParser/CppHeaderParser.py +++ b/third_party/CppHeaderParser/CppHeaderParser/CppHeaderParser.py @@ -41,8 +41,56 @@ # # http://www.opensource.org/licenses/bsd-license.php # -"""Parse C++ header files and generate a data structure -representing the class +""" +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() + + """ import ply.lex as lex @@ -57,8 +105,8 @@ def lineno(): return inspect.currentframe().f_back.f_lineno -__version__ = "1.9" -version = "1.9" +version = __version__ = "1.9.9" + tokens = [ 'NUMBER', 'NAME', @@ -85,7 +133,7 @@ tokens = [ 'NEW_LINE', ] -t_ignore = " \t\r[].|!?%@" +t_ignore = " \t\r[].|!?%@'^\\" # harts hack (litteral backslash is a bad idea?) - old version: " \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'/=' @@ -133,7 +181,7 @@ def t_NEWLINE(t): t.lexer.lineno += len(t.value) def t_error(v): - print "Lex error: ", v + print( "Lex error: ", v ) lex.lex() debug = 0 @@ -181,6 +229,8 @@ class CppClass(dict): and values are lists of CppVariable's self['enums'] - Dictionary where keys are from supportedAccessSpecifier and values are lists of CppEnum's + self['structs'] - Dictionary where keys are from supportedAccessSpecifier and + values are lists of nested Struct's An example of how this could look is as follows: #self = @@ -207,10 +257,45 @@ class CppClass(dict): } } """ + def __repr__( self ): return self['name'] + + def get_all_methods(self): + r = [] + for typ in 'public protected private'.split(): r += self['methods'][typ] + return r + + def get_all_method_names( self ): + r = [] + 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 + return r + + + def get_method_names( self, type='public' ): return [ meth['name'] for meth in self['methods'][ type ] ] + + def get_pure_virtual_methods( self, type='public' ): + r = {} + for meth in self['methods'][ type ]: + if meth['pure_virtual']: r[ meth['name'] ] = meth + return r + def __init__(self, nameStack): - if (debug): print "Class: ", nameStack + self['nested_classes'] = [] + self['parent'] = None + self['abstract'] = False + self._public_enums = {} + self._public_structs = {} + self._public_typedefs = {} + self._public_forward_declares = [] + self['namespace'] = "" + + if (debug): print( "Class: ", nameStack ) if (len(nameStack) < 2): - print "Error detecting class" + print( "Error detecting class" ) return global doxygenCommentCache if len(doxygenCommentCache): @@ -218,11 +303,15 @@ class CppClass(dict): 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"} + tmpInheritClass = {"access":"private"} # shouldn't public be default? if "," in nameStack: tmpStack = nameStack[:nameStack.index(",")] nameStack = nameStack[nameStack.index(",") + 1:] @@ -237,58 +326,89 @@ class CppClass(dict): tmpInheritClass["access"] = tmpStack[0] tmpInheritClass["class"] = tmpStack[1] else: - print "Warning: Cant figure out class inheriting %s\n"%(" ".join(tmpStack)) - continue - inheritList.append(tmpInheritClass) + 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 = {} enumAccessSpecificList = {} + structAccessSpecificList = {} + typedefAccessSpecificList = {} + forwardAccessSpecificList = {} for accessSpecifier in supportedAccessSpecifier: methodAccessSpecificList[accessSpecifier] = [] propertyAccessSpecificList[accessSpecifier] = [] enumAccessSpecificList[accessSpecifier] = [] - self['inherits'] = inheritList + structAccessSpecificList[accessSpecifier] = [] + typedefAccessSpecificList[accessSpecifier] = [] + forwardAccessSpecificList[accessSpecifier] = [] + self['methods'] = methodAccessSpecificList self['properties'] = propertyAccessSpecificList self['enums'] = enumAccessSpecificList - self['namespace'] = "" + self['structs'] = structAccessSpecificList + self['typedefs'] = typedefAccessSpecificList + self['forward_declares'] = forwardAccessSpecificList - def __repr__(self): + + def show(self): """Convert class to a string""" namespace_prefix = "" if self["namespace"]: namespace_prefix = self["namespace"] + "::" - rtn = "class %s\n"%(namespace_prefix + self["name"]) - try: - print self["doxygen"], - except: pass + rtn = "class %s"%(namespace_prefix + self["name"]) + if self['abstract']: rtn += ' (abstract)\n' + else: rtn += '\n' + + if 'doxygen' in self.keys(): rtn += self["doxygen"] + '\n' + if 'parent' in self.keys() and self['parent']: rtn += 'parent class:' + self['parent'] + '\n' + if "inherits" in self.keys(): - rtn += "Inherits: " + rtn += " Inherits: " for inheritClass in self["inherits"]: rtn += "%s %s, "%(inheritClass["access"], inheritClass["class"]) rtn += "\n" - 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 += " // Method\n" + rtn += " <Methods>\n" for method in self["methods"][accessSpecifier]: - rtn += " %s\n"%(repr(method)) - rtn += "}\n" - return rtn + 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' ] -class CppMethod(dict): +class CppMethod( _CppMethod ): """Takes a name stack and turns it into a method Contains the following Keys: @@ -297,8 +417,17 @@ class CppMethod(dict): self['doxygen'] - Doxygen comments associated with the method if they exist self['parameters'] - List of CppVariables """ - def __init__(self, nameStack, curClass): - if (debug): print "Method: ", nameStack + def show(self): + r = ['method name: %s (%s)' %(self['name'],self['debug']) ] + if self['returns']: r.append( 'returns: %s'%self['returns'] ) + if self['parameters']: r.append( 'number arguments: %s' %len(self['parameters'])) + if self['pure_virtual']: r.append( 'pure virtual: %s'%self['pure_virtual'] ) + if self['constructor']: r.append( 'constructor' ) + if self['destructor']: r.append( 'destructor' ) + return '\n\t\t '.join( r ) + + def __init__(self, nameStack, curClass, methinfo): + if (debug): print( "Method: ", nameStack ) global doxygenCommentCache if len(doxygenCommentCache): self["doxygen"] = doxygenCommentCache @@ -311,11 +440,16 @@ class CppMethod(dict): 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 - while (paramsStack[-1] != ")"): - paramsStack.pop() - paramsStack.pop() + #if paramsStack: # harts hacks - this is a bug caused by change to line 88? + # while paramsStack[-1] != ")": paramsStack.pop() + # paramsStack.pop() + params = [] #See if there is a doxygen comment for the variable doxyVarDesc = {} @@ -348,17 +482,45 @@ class CppMethod(dict): #Create the variable now while (len(paramsStack)): if (',' in paramsStack): - params.append(CppVariable(paramsStack[0:paramsStack.index(',')], doxyVarDesc=doxyVarDesc)) + param = CppVariable(paramsStack[0:paramsStack.index(',')], doxyVarDesc=doxyVarDesc) + if len(param.keys()): params.append(param) paramsStack = paramsStack[paramsStack.index(',') + 1:] else: param = CppVariable(paramsStack, doxyVarDesc=doxyVarDesc) - if len(param.keys()): - params.append(param) + if len(param.keys()): params.append(param) break + + self["parameters"] = params + self._params_helper( params ) + + +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 ) -class CppVariable(dict): + +class CppVariable( _CppVariable ): """Takes a name stack and turns it into a method Contains the following Keys: @@ -370,10 +532,12 @@ class CppVariable(dict): self['defaltValue'] - Default value of the variable, this key will only exist if there is a default value """ + Vars = [] def __init__(self, nameStack, **kwargs): - if (debug): print "Variable: ", nameStack - if (len(nameStack) < 2): - return + nameStack = self._name_stack_helper( nameStack ) + if (debug): print( "Variable: ", nameStack ) + if (len(nameStack) < 2): return + global doxygenCommentCache if len(doxygenCommentCache): self["doxygen"] = doxygenCommentCache @@ -381,10 +545,17 @@ class CppVariable(dict): if ("=" in nameStack): self["type"] = " ".join(nameStack[:nameStack.index("=") - 1]) self["name"] = nameStack[nameStack.index("=") - 1] - self["defaltValue"] = " ".join(nameStack[nameStack.index("=") + 1:]) - else: + self["defaltValue"] = " ".join(nameStack[nameStack.index("=") + 1:]) # deprecate camelCase in dicts + self['default'] = " ".join(nameStack[nameStack.index("=") + 1:]) + + elif nameStack[-1] == '*': # rare case - function param is an unnamed pointer: "void somemethod( SomeObject* )" + self['type'] = ' '.join(nameStack) + self['name'] = '' + + else: # common case self["type"] = " ".join(nameStack[:-1]) self["name"] = nameStack[-1] + self["type"] = self["type"].replace(" :",":") self["type"] = self["type"].replace(": ",":") self["type"] = self["type"].replace(" <","<") @@ -394,7 +565,30 @@ class CppVariable(dict): self["desc"] = kwargs["doxyVarDesc"][self["name"]] except: pass -class CppEnum(dict): + self.init() + 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 + +class CppEnum(_CppEnum): """Takes a name stack and turns it into an Enum Contains the following Keys: @@ -425,17 +619,21 @@ class CppEnum(dict): else: tmpStack = valueStack valueStack = [] - if len(tmpStack) == 1: - valueList.append({"name": tmpStack[0]}) + d = {} + if len(tmpStack) == 1: d["name"] = tmpStack[0] elif len(tmpStack) >= 3 and tmpStack[1] == "=": - valueList.append({"name": tmpStack[0], "value": " ".join(tmpStack[2:])}) + d["name"] = tmpStack[0]; d["value"] = " ".join(tmpStack[2:]) elif len(tmpStack) == 2 and tmpStack[1] == "=": - if (debug): print "Missed value for %s"%tmpStack[0] - valueList.append({"name": tmpStack[0]}) + if (debug): print( "WARN-enum: parser missed value for %s"%tmpStack[0] ) + d["name"] = tmpStack[0] + + if d: valueList.append( d ) + if len(valueList): + self['type'] = self.resolve_enum_values( valueList ) # returns int for standard enum self["values"] = valueList else: - #An enum without any values is useless, dont bother existing + print( 'WARN-enum: empty enum', nameStack ) return #Figure out if it has a name preBraceStack = nameStack[:nameStack.index("{")] @@ -444,6 +642,7 @@ class CppEnum(dict): self["name"] = preBraceStack[1] elif len(postBraceStack) and "typedef" in nameStack: self["name"] = " ".join(postBraceStack) + else: print( 'WARN-enum: nameless enum', nameStack ) #See if there are instances of this if "typedef" not in nameStack and len(postBraceStack): self["instances"] = [] @@ -453,20 +652,873 @@ class CppEnum(dict): self["instances"].append(var) self["namespace"] = "" -class CppHeader: +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 + +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 + + +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 ) + +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 +} + + +def standardize_fundamental( 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 + + + 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 + + # 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 + + 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 + + #if var['raw_type'] == 'Animation': print(var); assert 0 + + ## need full name space for classes in raw type ## + #if var['class'] and '::' not in var['raw_type'] and var['class']['namespace']: + + + + 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 + + +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' + |