aboutsummaryrefslogtreecommitdiff
path: root/third_party
diff options
context:
space:
mode:
authorAlon Zakai <alonzakai@gmail.com>2011-07-06 20:50:04 -0700
committerAlon Zakai <alonzakai@gmail.com>2011-07-06 20:50:04 -0700
commit8e4dc2caa67d22b47f0a209ed1828c2a291aea85 (patch)
tree93c4237214da30e71b058c429f62490f024d876f /third_party
parentc2d3b71f9959f90f38b360e9861c931a53b085f5 (diff)
improved bindings generator
Diffstat (limited to 'third_party')
-rw-r--r--third_party/CppHeaderParser/CppHeaderParser/CppHeaderParser.py1356
-rw-r--r--third_party/CppHeaderParser/CppHeaderParser/CppHeaderParser3.py669
-rw-r--r--third_party/CppHeaderParser/CppHeaderParser/__init__.py7
-rw-r--r--third_party/CppHeaderParser/CppHeaderParser/examples/SampleClass.h85
-rwxr-xr-xthird_party/CppHeaderParser/CppHeaderParser/examples/readSampleClass.py25
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'
+ 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
+
+ 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:
+ 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
+
+ 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 )
+
+class CppHeader( _CppHeader ):
"""Parsed C++ class header
Variables produced:
self.classes - Dictionary of classes found in a given header file where the
key is the name of the class
"""
- def __init__(self, headerFileName, argType = "file"):
+ IGNORE_NAMES = '__extension__'.split()
+
+ def show(self):
+ for className in self.classes.keys(): self.classes[className].show()
+
+ def __init__(self, headerFileName, argType="file"):
+ ## reset global state ##
+ global doxygenCommentCache
+ doxygenCommentCache = ""
+ CppVariable.Vars = []
+ CppStruct.Structs = []
+
if (argType == "file"):
self.headerFileName = os.path.expandvars(headerFileName)
self.mainClass = os.path.split(self.headerFileName)[1][:-2]
headerFileStr = ""
-# if headerFileName[-2:] != ".h":
-# raise Exception("file must be a header file and end with .h")
elif argType == "string":
self.headerFileName = ""
self.mainClass = "???"
@@ -474,11 +1526,18 @@ class CppHeader:
else:
raise Exception("Arg type must be either file or string")
self.curClass = ""
- self.classes = {}
+
+ # nested classes have parent::nested, but no extra namespace,
+ # this keeps the API compatible, TODO proper namespace for everything.
+ Resolver.CLASSES = {}
+ self.classes = Resolver.CLASSES
+
self.enums = []
+ self.global_enums = {}
self.nameStack = []
self.nameSpaces = []
- self.curAccessSpecifier = 'private'
+ self.curAccessSpecifier = 'private' # private is default
+ self.initextra() # harts hack
if (len(self.headerFileName)):
headerFileStr = "\n".join(open(self.headerFileName).readlines())
@@ -486,28 +1545,31 @@ class CppHeader:
lex.input(headerFileStr)
curLine = 0
curChar = 0
- try:
+ if 1: #try:
while True:
tok = lex.token()
- # Example: LexToken(COLON,';',1,373)
- # where (tok.name, tok.value, ?, ?)
- if not tok:
- break
+ if not tok: break
+ if tok.type == 'NAME' and tok.value in self.IGNORE_NAMES: continue
+ 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) and is_namespace(self.nameStack):
+ 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 )
if len(self.nameStack) and not is_enum_namestack(self.nameStack):
self.evaluate_stack()
else:
self.nameStack.append(tok.value)
+ if self.stack and self.stack[0] == 'class': self.stack = []
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?
if len(self.nameStack) and is_enum_namestack(self.nameStack):
self.nameStack.append(tok.value)
elif self.braceDepth < 10:
@@ -515,8 +1577,22 @@ class CppHeader:
else:
self.nameStack = []
self.braceDepth -= 1
- if (self.braceDepth == 0):
- self.curClass = ""
+ #self.stack = []; print 'BRACE DEPTH', self.braceDepth, 'NS', len(self.nameSpaces) # June29 2011
+ 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
+ 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' )
+ self.curStruct = None
+
+ if self._method_body and self.braceDepth < self._method_body:
+ self._method_body = None; self.stack = []; self.nameStack = []; print( 'FORCE CLEAR METHBODY' )
if (tok.type == 'OPEN_PAREN'):
self.nameStack.append(tok.value)
@@ -537,8 +1613,9 @@ class CppHeader:
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 and self.braceDepth == len(self.nameSpaces) + 1):
- self.curAccessSpecifier = 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)
elif (tok.type == 'COLON'):
@@ -546,101 +1623,102 @@ class CppHeader:
if len(self.nameStack) == 0:
continue
self.nameStack.append(tok.value)
+
elif (tok.type == 'SEMI_COLON'):
- if (self.braceDepth < 10):
- self.evaluate_stack()
- except:
- raise CppParseError("Not able to parse %s on line %d evaluating \"%s\"\nError around: %s"
- % (self.headerFileName, tok.lineno, tok.value, " ".join(self.nameStack)))
-
- def evaluate_stack(self):
+ 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')
+
+ #except:
+ # raise CppParseError("Not able to parse %s on line %d evaluating \"%s\"\nError around: %s"
+ # % (self.headerFileName, tok.lineno, tok.value, " ".join(self.nameStack)))
+
+ self.finalize()
+
+ def evaluate_stack(self, token=None):
"""Evaluates the current name stack"""
global doxygenCommentCache
- if (debug): print "Evaluating stack %s at..."%self.nameStack
+ if (debug): print( "Evaluating stack %s\nBraceDepth: %s" %(self.nameStack,self.braceDepth))
if (len(self.curClass)):
- if (debug): print "%s (%s) "%(self.curClass, self.curAccessSpecifier),
- if (len(self.nameStack) == 0):
- if (debug): print "line ",lineno()
- if (debug): print "(Empty Stack)"
+ 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 not self.curClass and 'typedef' in self.nameStack:
+ print('STACK', self.stack)
+ if token == 'SEMI_COLON' and ('{' not in self.stack or '}' in self.stack): self.evaluate_typedef()
+ else: return
+
+ elif (len(self.nameStack) == 0):
+ if (debug): print( "line ",lineno() )
+ if (debug): print( "(Empty Stack)" )
return
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 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() )
+ 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
+
elif (self.nameStack[0] == "class"):
- if (debug): print "line ",lineno()
+ if (debug): print( "line ",lineno() )
self.evaluate_class_stack()
elif (self.nameStack[0] == "struct"):
- if (debug): print "line ",lineno()
- self.curAccessSpecifier = "public"
- self.evaluate_class_stack()
- elif (len(self.curClass) == 0):
- if (debug): print "line ",lineno()
- if is_enum_namestack(self.nameStack):
- self.evaluate_enum_stack()
+ 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 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
self.nameStack = []
doxygenCommentCache = ""
return
elif (self.braceDepth < 1):
- if (debug): print "line ",lineno()
+ if (debug): print( "line ",lineno() )
#Ignore global stuff for now
- if (debug): print "Global stuff: ", self.nameStack
+ if (debug): print( "Global stuff: ", self.nameStack )
self.nameStack = []
doxygenCommentCache = ""
return
elif (self.braceDepth > len(self.nameSpaces) + 1):
- if (debug): print "line ",lineno()
+ if (debug): print( "line ",lineno() )
self.nameStack = []
doxygenCommentCache = ""
return
- elif is_enum_namestack(self.nameStack):
- if (debug): print "line ",lineno()
- #elif self.nameStack[0] == "enum":
- self.evaluate_enum_stack()
- elif ('(' in self.nameStack):
- if (debug): print "line ",lineno()
- self.evaluate_method_stack()
- else:
- if (debug): print "line ",lineno()
- self.evaluate_property_stack()
- self.nameStack = []
+
+ self.nameStack = [] # its a little confusing to have some if/else above return and others not, and then clearning the nameStack down here
doxygenCommentCache = ""
- def evaluate_class_stack(self):
- """Create a Class out of the name stack (but not its parts)"""
- #dont support sub classes today
- if self.braceDepth != len(self.nameSpaces):
- return
- newClass = CppClass(self.nameStack)
- if len(newClass.keys()):
- self.curClass = newClass["name"]
- self.classes[self.curClass] = newClass
- else:
- self.curClass = ""
- newClass["namespace"] = self.cur_namespace()
-
- def evaluate_method_stack(self):
- """Create a method out of the name stack"""
- newMethod = CppMethod(self.nameStack, self.curClass)
- if len(newMethod.keys()):
- self.classes[self.curClass]["methods"][self.curAccessSpecifier].append(newMethod)
-
- def evaluate_property_stack(self):
- """Create a Property out of the name stack"""
- newVar = CppVariable(self.nameStack)
- if len(newVar.keys()):
- self.classes[self.curClass]["properties"][self.curAccessSpecifier].append(newVar)
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()
- self.classes[self.curClass]["enums"][self.curAccessSpecifier].append(newEnum)
+ 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()
-# print "Adding global enum"
+ 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"
@@ -651,19 +1729,5 @@ class CppHeader:
self.evaluate_property_stack()
del newEnum["instances"]
- 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 __repr__(self):
- rtn = ""
- for className in self.classes.keys():
- rtn += repr(self.classes[className])
- return rtn
+
+
diff --git a/third_party/CppHeaderParser/CppHeaderParser/CppHeaderParser3.py b/third_party/CppHeaderParser/CppHeaderParser/CppHeaderParser3.py
deleted file mode 100644
index 4f54e2fa..00000000
--- a/third_party/CppHeaderParser/CppHeaderParser/CppHeaderParser3.py
+++ /dev/null
@@ -1,669 +0,0 @@
-#!/usr/bin/python
-#
-# Author: Jashua R. Cloutier (contact via sourceforge username:senexcanis)
-#
-# Copyright (C) 2010, Jashua R. Cloutier
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#
-# * Redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in
-# the documentation and/or other materials provided with the
-# distribution.
-#
-# * Neither the name of Jashua R. Cloutier nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
-# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-# POSSIBILITY OF SUCH DAMAGE.
-#
-#
-# The CppHeaderParser.py script is written in Python 2.4 and released to
-# the open source community for continuous improvements under the BSD
-# 2.0 new license, which can be found at:
-#
-# http://www.opensource.org/licenses/bsd-license.php
-#
-"""Parse C++ header files and generate a data structure
-representing the class
-"""
-
-import ply.lex as lex
-import os
-import sys
-import re
-
-import inspect
-
-def lineno():
- """Returns the current line number in our program."""
- return inspect.currentframe().f_back.f_lineno
-
-
-__version__ = "1.9"
-version = "1.9"
-tokens = [
- 'NUMBER',
- 'NAME',
- 'OPEN_PAREN',
- 'CLOSE_PAREN',
- 'OPEN_BRACE',
- 'CLOSE_BRACE',
- 'COLON',
- 'SEMI_COLON',
- 'COMMA',
- 'COMMENT_SINGLELINE',
- 'COMMENT_MULTILINE',
- 'PRECOMP_MACRO',
- 'PRECOMP_MACRO_CONT',
- 'ASTERISK',
- 'AMPERSTAND',
- 'EQUALS',
- 'MINUS',
- 'PLUS',
- 'DIVIDE',
- 'CHAR_LITERAL',
- 'STRING_LITERAL',
- 'OPERATOR_DIVIDE_OVERLOAD',
- 'NEW_LINE',
-]
-
-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'/='
-t_OPEN_PAREN = r'\('
-t_CLOSE_PAREN = r'\)'
-t_OPEN_BRACE = r'{'
-t_CLOSE_BRACE = r'}'
-t_SEMI_COLON = r';'
-t_COLON = r':'
-t_COMMA = r','
-t_PRECOMP_MACRO = r'\#.*'
-t_PRECOMP_MACRO_CONT = r'.*\\\n'
-def t_COMMENT_SINGLELINE(t):
- r'\/\/.*\n'
- global doxygenCommentCache
- if t.value.startswith("///") or t.value.startswith("//!"):
- if doxygenCommentCache:
- doxygenCommentCache += "\n"
- if t.value.endswith("\n"):
- doxygenCommentCache += t.value[:-1]
- else:
- doxygenCommentCache += t.value
-t_ASTERISK = r'\*'
-t_MINUS = r'\-'
-t_PLUS = r'\+'
-t_DIVIDE = r'/[^/]'
-t_AMPERSTAND = r'&'
-t_EQUALS = r'='
-t_CHAR_LITERAL = "'.'"
-#found at http://wordaligned.org/articles/string-literals-and-regular-expressions
-#TODO: This does not work with the string "bla \" bla"
-t_STRING_LITERAL = r'"([^"\\]|\\.)*"'
-#Found at http://ostermiller.org/findcomment.html
-def t_COMMENT_MULTILINE(t):
- r'/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/'
- global doxygenCommentCache
- if t.value.startswith("/**") or t.value.startswith("/*!"):
- #not sure why, but get double new lines
- v = t.value.replace("\n\n", "\n")
- #strip prefixing whitespace
- v = re.sub("\n[\s]+\*", "\n*", v)
- doxygenCommentCache += v
-def t_NEWLINE(t):
- r'\n+'
- t.lexer.lineno += len(t.value)
-
-def t_error(v):
- print("Lex error: ", v)
-
-lex.lex()
-debug = 0
-
-supportedAccessSpecifier = [
- 'public',
- 'protected',
- 'private'
-]
-
-doxygenCommentCache = ""
-
-def is_namespace(nameStack):
- """Determines if a namespace is being specified"""
- if len(nameStack) == 0:
- return False
- if nameStack[0] == "namespace":
- return True
- return False
-
-def is_enum_namestack(nameStack):
- """Determines if a namestack is an enum namestack"""
- if len(nameStack) == 0:
- return False
- if nameStack[0] == "enum":
- return True
- if len(nameStack) > 1 and nameStack[0] == "typedef" and nameStack[1] == "enum":
- return True
- return False
-
-class CppParseError(Exception): pass
-
-class CppClass(dict):
- """Takes a name stack and turns it into a class
-
- Contains the following Keys:
- self['name'] - Name of the class
- self['doxygen'] - Doxygen comments associated with the class if they exist
- self['inherits'] - List of Classes that this one inherits where the values
- are of the form {"access": Anything in supportedAccessSpecifier
- "class": Name of the class
- self['methods'] - Dictionary where keys are from supportedAccessSpecifier
- and values are a lists of CppMethod's
- self['properties'] - Dictionary where keys are from supportedAccessSpecifier
- and values are lists of CppVariable's
- self['enums'] - Dictionary where keys are from supportedAccessSpecifier and
- values are lists of CppEnum's
-
- An example of how this could look is as follows:
- #self =
- {
- 'name': ""
- 'inherits':[]
- 'methods':
- {
- 'public':[],
- 'protected':[],
- 'private':[]
- },
- 'properties':
- {
- 'public':[],
- 'protected':[],
- 'private':[]
- },
- 'enums':
- {
- 'public':[],
- 'protected':[],
- 'private':[]
- }
- }
- """
- def __init__(self, nameStack):
- if (debug): print("Class: ", nameStack)
- if (len(nameStack) < 2):
- print("Error detecting class")
- return
- global doxygenCommentCache
- if len(doxygenCommentCache):
- self["doxygen"] = doxygenCommentCache
- doxygenCommentCache = ""
- self["name"] = nameStack[1]
- inheritList = []
- if ":" in nameStack:
- nameStack = nameStack[nameStack.index(":") + 1:]
- while len(nameStack):
- tmpStack = []
- tmpInheritClass = {"access":"private"}
- 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: Cant figure out class inheriting %s\n"%(" ".join(tmpStack)))
- continue
- inheritList.append(tmpInheritClass)
- methodAccessSpecificList = {}
- propertyAccessSpecificList = {}
- enumAccessSpecificList = {}
-
- for accessSpecifier in supportedAccessSpecifier:
- methodAccessSpecificList[accessSpecifier] = []
- propertyAccessSpecificList[accessSpecifier] = []
- enumAccessSpecificList[accessSpecifier] = []
- self['inherits'] = inheritList
- self['methods'] = methodAccessSpecificList
- self['properties'] = propertyAccessSpecificList
- self['enums'] = enumAccessSpecificList
- self['namespace'] = ""
-
- def __repr__(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"], end=' ')
- except: pass
- if "inherits" in list(self.keys()):
- rtn += "Inherits: "
- for inheritClass in self["inherits"]:
- rtn += "%s %s, "%(inheritClass["access"], inheritClass["class"])
- rtn += "\n"
- rtn += "{\n"
- for accessSpecifier in supportedAccessSpecifier:
- rtn += "%s\n"%(accessSpecifier)
- #Enums
- if (len(self["enums"][accessSpecifier])):
- rtn += " // Enums\n"
- for enum in self["enums"][accessSpecifier]:
- rtn += " %s\n"%(repr(enum))
- #Properties
- if (len(self["properties"][accessSpecifier])):
- rtn += " // Properties\n"
- for property in self["properties"][accessSpecifier]:
- rtn += " %s\n"%(repr(property))
- #Methods
- if (len(self["methods"][accessSpecifier])):
- rtn += " // Method\n"
- for method in self["methods"][accessSpecifier]:
- rtn += " %s\n"%(repr(method))
- rtn += "}\n"
- return rtn
-
-class CppMethod(dict):
- """Takes a name stack and turns it into a method
-
- Contains the following Keys:
- self['rtnType'] - 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
- """
- def __init__(self, nameStack, curClass):
- if (debug): print("Method: ", nameStack)
- global doxygenCommentCache
- 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"
- paramsStack = nameStack[nameStack.index('(') + 1: ]
- #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()
- params = []
- #See if there is a doxygen comment for the variable
- doxyVarDesc = {}
- #TODO: Put this into a class
- if "doxygen" in self:
- doxyLines = self["doxygen"].split("\n")
- lastParamDesc = ""
- for doxyLine in doxyLines:
- if " @param " in doxyLine or " \param " in doxyLine:
- try:
- #Strip out the param
- doxyLine = doxyLine[doxyLine.find("param ") + 6:]
- (var, desc) = doxyLine.split(" ", 1)
- doxyVarDesc[var] = desc.strip()
- lastParamDesc = var
- except: pass
- elif " @return " in doxyLine or " \return " in doxyLine:
- lastParamDesc = ""
- # not handled for now
- elif lastParamDesc:
- try:
- doxyLine = doxyLine.strip()
- if " " not in doxyLine:
- lastParamDesc = ""
- continue
- doxyLine = doxyLine[doxyLine.find(" ") + 1:]
- doxyVarDesc[lastParamDesc] += " " + doxyLine
- except: pass
-
- #Create the variable now
- while (len(paramsStack)):
- if (',' in paramsStack):
- params.append(CppVariable(paramsStack[0:paramsStack.index(',')], doxyVarDesc=doxyVarDesc))
- paramsStack = paramsStack[paramsStack.index(',') + 1:]
- else:
- param = CppVariable(paramsStack, doxyVarDesc=doxyVarDesc)
- if len(list(param.keys())):
- params.append(param)
- break
- self["parameters"] = params
-
-
-class CppVariable(dict):
- """Takes a name stack and turns it into a method
-
- Contains the following Keys:
- self['type'] - Type for the variable (ex. "const 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
- """
- def __init__(self, nameStack, **kwargs):
- if (debug): print("Variable: ", nameStack)
- if (len(nameStack) < 2):
- return
- global doxygenCommentCache
- if len(doxygenCommentCache):
- self["doxygen"] = doxygenCommentCache
- doxygenCommentCache = ""
- 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["type"] = " ".join(nameStack[:-1])
- self["name"] = nameStack[-1]
- self["type"] = self["type"].replace(" :",":")
- self["type"] = self["type"].replace(": ",":")
- self["type"] = self["type"].replace(" <","<")
- self["type"] = self["type"].replace(" >",">")
- #Optional doxygen description
- try:
- self["desc"] = kwargs["doxyVarDesc"][self["name"]]
- except: pass
-
-class CppEnum(dict):
- """Takes a name stack and turns it into an Enum
-
- Contains the following Keys:
- self['name'] - Name of the enum (ex. "ItemState")
- self['namespace'] - Namespace containing the enum
- self['values'] - List of values where the values are a dictionary of the
- form {"name": name of the key (ex. "PARSING_HEADER"),
- "value": Specified value of the enum, this key will only exist
- if a value for a given enum value was defined
- }
- """
- def __init__(self, nameStack):
- if len(nameStack) < 4 or "{" not in nameStack or "}" not in nameStack:
- #Not enough stuff for an enum
- return
- global doxygenCommentCache
- if len(doxygenCommentCache):
- self["doxygen"] = doxygenCommentCache
- doxygenCommentCache = ""
- valueList = []
- #Figure out what values it has
- valueStack = nameStack[nameStack.index('{') + 1: nameStack.index('}')]
- while len(valueStack):
- tmpStack = []
- if "," in valueStack:
- tmpStack = valueStack[:valueStack.index(",")]
- valueStack = valueStack[valueStack.index(",") + 1:]
- else:
- tmpStack = valueStack
- valueStack = []
- if len(tmpStack) == 1:
- valueList.append({"name": tmpStack[0]})
- elif len(tmpStack) >= 3 and tmpStack[1] == "=":
- valueList.append({"name": tmpStack[0], "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 len(valueList):
- self["values"] = valueList
- else:
- #An enum without any values is useless, dont bother existing
- return
- #Figure out if it has a name
- preBraceStack = nameStack[:nameStack.index("{")]
- postBraceStack = nameStack[nameStack.index("}") + 1:]
- if (len(preBraceStack) == 2 and "typedef" not in nameStack):
- self["name"] = preBraceStack[1]
- elif len(postBraceStack) and "typedef" in nameStack:
- self["name"] = " ".join(postBraceStack)
- #See if there are instances of this
- if "typedef" not in nameStack and len(postBraceStack):
- self["instances"] = []
- for var in postBraceStack:
- if "," in var:
- continue
- self["instances"].append(var)
- self["namespace"] = ""
-
-class CppHeader:
- """Parsed C++ class header
-
- Variables produced:
- self.classes - Dictionary of classes found in a given header file where the
- key is the name of the class
- """
- def __init__(self, headerFileName, argType = "file"):
- if (argType == "file"):
- self.headerFileName = os.path.expandvars(headerFileName)
- self.mainClass = os.path.split(self.headerFileName)[1][:-2]
- headerFileStr = ""
-# if headerFileName[-2:] != ".h":
-# raise Exception("file must be a header file and end with .h")
- elif argType == "string":
- self.headerFileName = ""
- self.mainClass = "???"
- headerFileStr = headerFileName
- else:
- raise Exception("Arg type must be either file or string")
- self.curClass = ""
- self.classes = {}
- self.enums = []
- self.nameStack = []
- self.nameSpaces = []
- self.curAccessSpecifier = 'private'
-
- if (len(self.headerFileName)):
- headerFileStr = "\n".join(open(self.headerFileName).readlines())
- self.braceDepth = 0
- lex.input(headerFileStr)
- curLine = 0
- curChar = 0
- try:
- while True:
- tok = lex.token()
- # Example: LexToken(COLON,';',1,373)
- # where (tok.name, tok.value, ?, ?)
- if not tok:
- break
- curLine = tok.lineno
- curChar = tok.lexpos
- if (tok.type == 'OPEN_BRACE'):
- if len(self.nameStack) and is_namespace(self.nameStack):
- self.nameSpaces.append(self.nameStack[1])
- if len(self.nameStack) and not is_enum_namestack(self.nameStack):
- self.evaluate_stack()
- else:
- self.nameStack.append(tok.value)
- self.braceDepth += 1
- elif (tok.type == 'CLOSE_BRACE'):
- if self.braceDepth == 0:
- continue
- if (self.braceDepth == len(self.nameSpaces)):
- tmp = self.nameSpaces.pop()
- if len(self.nameStack) and is_enum_namestack(self.nameStack):
- self.nameStack.append(tok.value)
- elif self.braceDepth < 10:
- self.evaluate_stack()
- else:
- self.nameStack = []
- self.braceDepth -= 1
- if (self.braceDepth == 0):
- self.curClass = ""
-
- 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 and self.braceDepth == len(self.nameSpaces) + 1):
- self.curAccessSpecifier = tok.value
- else:
- 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)
- elif (tok.type == 'SEMI_COLON'):
- if (self.braceDepth < 10):
- self.evaluate_stack()
- except:
- raise CppParseError("Not able to parse %s on line %d evaluating \"%s\"\nError around: %s"
- % (self.headerFileName, tok.lineno, tok.value, " ".join(self.nameStack)))
-
- def evaluate_stack(self):
- """Evaluates the current name stack"""
- global doxygenCommentCache
- if (debug): print("Evaluating stack %s at..."%self.nameStack)
- if (len(self.curClass)):
- if (debug): print("%s (%s) "%(self.curClass, self.curAccessSpecifier), end=' ')
- if (len(self.nameStack) == 0):
- if (debug): print("line ",lineno())
- if (debug): print("(Empty Stack)")
- return
- elif (self.nameStack[0] == "namespace"):
- #Taken care of outside of here
- pass
- elif (self.nameStack[0] == "class"):
- if (debug): print("line ",lineno())
- self.evaluate_class_stack()
- elif (self.nameStack[0] == "struct"):
- if (debug): print("line ",lineno())
- self.curAccessSpecifier = "public"
- self.evaluate_class_stack()
- elif (len(self.curClass) == 0):
- if (debug): print("line ",lineno())
- if is_enum_namestack(self.nameStack):
- self.evaluate_enum_stack()
- self.nameStack = []
- doxygenCommentCache = ""
- return
- elif (self.braceDepth < 1):
- if (debug): print("line ",lineno())
- #Ignore global stuff for now
- if (debug): print("Global stuff: ", self.nameStack)
- self.nameStack = []
- doxygenCommentCache = ""
- return
- elif (self.braceDepth > len(self.nameSpaces) + 1):
- if (debug): print("line ",lineno())
- self.nameStack = []
- doxygenCommentCache = ""
- return
- elif is_enum_namestack(self.nameStack):
- if (debug): print("line ",lineno())
- #elif self.nameStack[0] == "enum":
- self.evaluate_enum_stack()
- elif ('(' in self.nameStack):
- if (debug): print("line ",lineno())
- self.evaluate_method_stack()
- else:
- if (debug): print("line ",lineno())
- self.evaluate_property_stack()
- self.nameStack = []
- doxygenCommentCache = ""
-
- def evaluate_class_stack(self):
- """Create a Class out of the name stack (but not its parts)"""
- #dont support sub classes today
- if self.braceDepth != len(self.nameSpaces):
- return
- newClass = CppClass(self.nameStack)
- if len(list(newClass.keys())):
- self.curClass = newClass["name"]
- self.classes[self.curClass] = newClass
- else:
- self.curClass = ""
- newClass["namespace"] = self.cur_namespace()
-
- def evaluate_method_stack(self):
- """Create a method out of the name stack"""
- newMethod = CppMethod(self.nameStack, self.curClass)
- if len(list(newMethod.keys())):
- self.classes[self.curClass]["methods"][self.curAccessSpecifier].append(newMethod)
-
- def evaluate_property_stack(self):
- """Create a Property out of the name stack"""
- newVar = CppVariable(self.nameStack)
- if len(list(newVar.keys())):
- self.classes[self.curClass]["properties"][self.curAccessSpecifier].append(newVar)
-
- def evaluate_enum_stack(self):
- """Create an Enum out of the name stack"""
- newEnum = CppEnum(self.nameStack)
- if len(list(newEnum.keys())):
- if len(self.curClass):
- newEnum["namespace"] = self.cur_namespace()
- self.classes[self.curClass]["enums"][self.curAccessSpecifier].append(newEnum)
- else:
- newEnum["namespace"] = self.cur_namespace()
-# print "Adding global enum"
- self.enums.append(newEnum)
- #This enum has instances, turn them into properties
- if "instances" in newEnum:
- instanceType = "enum"
- if "name" in newEnum:
- instanceType = newEnum["name"]
- for instance in newEnum["instances"]:
- self.nameStack = [instanceType, instance]
- self.evaluate_property_stack()
- del newEnum["instances"]
-
- 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 __repr__(self):
- rtn = ""
- for className in list(self.classes.keys()):
- rtn += repr(self.classes[className])
- return rtn
diff --git a/third_party/CppHeaderParser/CppHeaderParser/__init__.py b/third_party/CppHeaderParser/CppHeaderParser/__init__.py
index 5af3d180..b9b23cbb 100644
--- a/third_party/CppHeaderParser/CppHeaderParser/__init__.py
+++ b/third_party/CppHeaderParser/CppHeaderParser/__init__.py
@@ -1,9 +1,4 @@
# CppHeaderParser package
# Author: Jashua Cloutier (contact via sourceforge username:senexcanis)
-import sys
-if sys.version_info[0] == 2:
- from CppHeaderParser import *
-else:
- from CppHeaderParser3 import *
+from CppHeaderParser import *
-#__all__ = ['CppHeaderParser']
diff --git a/third_party/CppHeaderParser/CppHeaderParser/examples/SampleClass.h b/third_party/CppHeaderParser/CppHeaderParser/examples/SampleClass.h
index e6315a29..628e50f8 100644
--- a/third_party/CppHeaderParser/CppHeaderParser/examples/SampleClass.h
+++ b/third_party/CppHeaderParser/CppHeaderParser/examples/SampleClass.h
@@ -1,5 +1,8 @@
#include <vector>
#include <string>
+
+typedef char* string;
+
using namespace std;
class SampleClass
{
@@ -32,7 +35,7 @@ public:
*********************************/
unsigned int meth4();
private:
- void * meth5(){return NULL};
+ void * meth5(){return NULL}; // invalid c++, fixing parser anyways.
/// prop1 description
string prop1;
@@ -62,3 +65,83 @@ namespace Alpha
};
};
}
+
+// tests by hart //
+namespace Gamma {
+ typedef std::string mystring;
+}
+typedef std::string _StringBase;
+typedef _StringBase String;
+
+namespace Theta {
+ class ThetaClass {
+ public:
+ ThetaClass();
+ ~ThetaClass();
+ protected:
+ struct mystruct {
+ bool xxx;
+ };
+ void this_method_protected();
+ }
+
+ struct ThetaStruct {
+ bool test1;
+ };
+
+ class ThetaClass2 {
+ public:
+ ThetaClass2();
+ static inline void static_inlined_method();
+ inline friend void myfriendmethod();
+ static std::vector<float> returns_std_vector();
+ float operator + ();
+ template<typename AAA> std::vector<float> template_method( const AAA & aaa );
+
+ }
+
+ inline int ThetaClass::method_defined_outside() {
+ return 1;
+ }
+ inline ThetaClass::operator ThetaClass() const
+ {
+ return ThetaClass();
+ }
+
+
+}
+
+struct FreeStruct {
+ bool freestructs_boolvar;
+ float freestructs_floatvar;
+};
+
+namespace A {
+
+ class FixMe {
+ virtual ~__forced_unwind() throw();
+ virtual void purevirt() = 0;
+ int realMethod() {
+ return 1
+ }
+ }; // legal to end class with }; ?
+
+ class SubClassFixMe : public FixMe {
+ public:
+ void purevirt();
+ }
+
+ namespace B {
+
+ }
+ class StillAbstract : public FixMe {
+ public:
+ void somemethod();
+ pointer operator->() const { return &(operator*()); } // example of operator used in method def
+
+ }
+
+}
+namespace C __attribute__ ((__visibility__ ("default"))) {
+}
+
diff --git a/third_party/CppHeaderParser/CppHeaderParser/examples/readSampleClass.py b/third_party/CppHeaderParser/CppHeaderParser/examples/readSampleClass.py
index e6b2e296..c7c0ec52 100755
--- a/third_party/CppHeaderParser/CppHeaderParser/examples/readSampleClass.py
+++ b/third_party/CppHeaderParser/CppHeaderParser/examples/readSampleClass.py
@@ -48,4 +48,27 @@ print "\nNamespace for OmegaClass is:"
print cppHeader.classes["OmegaClass"]["namespace"]
print "\nType for omegaString is:"
-print cppHeader.classes["AlphaClass"]["properties"]["public"][0]["type"] \ No newline at end of file
+print cppHeader.classes["AlphaClass"]["properties"]["public"][0]["type"]
+
+# by hart
+print 'global typedefs:'
+print cppHeader.typedefs
+
+print 'order of classes:'
+for cls in cppHeader.classes_order:
+ print '\t', cls['name']
+
+print 'order of typedefs:'
+for name in cppHeader.typedefs_order:
+ print '\t', name
+
+print cppHeader.classes['ThetaClass']
+print '_'*80
+print cppHeader.classes['ThetaClass2']
+
+print cppHeader.structs
+
+print cppHeader.namespaces
+
+print cppHeader.classes['FixMe']
+