aboutsummaryrefslogtreecommitdiff
path: root/third_party
diff options
context:
space:
mode:
authorAlon Zakai <alonzakai@gmail.com>2011-08-05 20:47:30 -0700
committerAlon Zakai <alonzakai@gmail.com>2011-08-05 20:47:30 -0700
commitb37f0ce62244d16cb216b1f125d7dc76c90c066e (patch)
tree349a1dda98ac19d800db5d63120ecb514505125e /third_party
parentb63082e4b35a2001bb3029c1fab3e403445feaec (diff)
new CppHeaderParser
Diffstat (limited to 'third_party')
-rw-r--r--third_party/CppHeaderParser/CppHeaderParser/CppHeaderParser.py2740
1 files changed, 1677 insertions, 1063 deletions
diff --git a/third_party/CppHeaderParser/CppHeaderParser/CppHeaderParser.py b/third_party/CppHeaderParser/CppHeaderParser/CppHeaderParser.py
index 675b74d8..a16d32be 100644
--- a/third_party/CppHeaderParser/CppHeaderParser/CppHeaderParser.py
+++ b/third_party/CppHeaderParser/CppHeaderParser/CppHeaderParser.py
@@ -42,54 +42,47 @@
# http://www.opensource.org/licenses/bsd-license.php
#
"""
-CppHeaderParser2.0: April 2011 - July 2011
- by HartsAntler
- bhartsho@yahoo.com
- http://pyppet.blogspot.com
-
- Quick Start - User API:
- h = CppHeaderParser.CppHeader("someheader.h")
- for name in h.classes:
- c = h.classes[name]
- for method in c['methods']['public']:
- print method['name']
- print dir(method) # view the rest of the API here.
-
- ... TODO document more ...
-
-
-
- New Features by Hart:
- should be able to parse all c++ files, not just headers
- parsing global typedefs with resolution
- parsing global structs
- fixes nested struct in class changes accessor type
- parsing if class is abstract
- parsing more info about variables
- save ordering of classes, structs, and typedefs
- handle forward decl of class in a class
- handle mutable, static, and other variable types
- handle 1D arrays
-
-
- Internal Developer Notes:
-
- 1. in method['rtnType'] is not recommended, instead use: 'returns', 'returns_pointer', 'returns_reference', 'returns_fundamental'
- (camelCase may become deprecated in the future for the dict lookup keys)
-
- 2. double name stacks:
- . the main stack is self.nameStack, this stack is simpler and easy to get hints from
- . the secondary stack is self.stack is the full name stack, required for parsing somethings
- . each stack maybe cleared at different points, since they are used to detect different things
- . it looks ugly but it works :)
-
- 3. Tabs vs Spaces:
- This file now contains tabs and spaces, the tabb'ed code is the new stuff,
- in the future this should be made consistent, one way or the other.
-
- 4. Had to make the __repr__ methods simple because some of these dicts are interlinked.
- For nice printing, call something.show()
-
+CppHeaderParser2.0: April 2011 - August 2011
+ by HartsAntler
+ http://pyppet.blogspot.com
+
+ Quick Start - User API:
+ h = CppHeaderParser.CppHeader("someheader.h")
+ for name in h.classes:
+ c = h.classes[name]
+ for method in c['methods']['public']:
+ print method['name']
+ print dir(method) # view the rest of the API here.
+
+ ... TODO document more ...
+
+
+
+ New Features by Hart:
+ should be able to parse all c++ files, not just headers
+ parsing global typedefs with resolution
+ parsing global structs
+ fixes nested struct in class changes accessor type
+ parsing if class is abstract
+ parsing more info about variables
+ save ordering of classes, structs, and typedefs
+ handle forward decl of class in a class
+ handle mutable, static, and other variable types
+ handle 1D arrays
+ handle throw keyword and function prefix __attribute__((__const__))
+ handle nameless parameters "void method(void);"
+ handle simple templates, and global functions.
+
+ Internal Developer Notes:
+
+ 1. double name stacks:
+ . the main stack is self.nameStack, this stack is simpler and easy to get hints from
+ . the secondary stack is self.stack is the full name stack, required for parsing somethings
+ . each stack maybe cleared at different points, since they are used to detect different things
+ . it looks ugly but it works :)
+
+ 2. Had to make the __repr__ methods simple because some of these dicts are interlinked.
+ For nice printing, call something.show()
"""
@@ -104,8 +97,7 @@ def lineno():
"""Returns the current line number in our program."""
return inspect.currentframe().f_back.f_lineno
-
-version = __version__ = "1.9.9"
+version = __version__ = "1.9.9o"
tokens = [
'NUMBER',
@@ -131,9 +123,19 @@ tokens = [
'STRING_LITERAL',
'OPERATOR_DIVIDE_OVERLOAD',
'NEW_LINE',
+
+ 'OPEN_BRACKET',
+ 'CLOSE_BRACKET',
+
]
-t_ignore = " \t\r[].|!?%@'^\\" # harts hack (litteral backslash is a bad idea?) - old version: " \t\r[].|!?%@"
+t_OPEN_BRACKET = r'\['
+t_CLOSE_BRACKET = r'\]'
+
+
+#t_ignore = " \t\r[].|!?%@" # (cppheaderparser 1.9x)
+#t_ignore = " \t\r[].|!?%@'^\\"
+t_ignore = " \t\r.|!?%@'^\\"
t_NUMBER = r'[0-9][0-9XxA-Fa-f]*'
t_NAME = r'[<>A-Za-z_~][A-Za-z0-9_]*'
t_OPERATOR_DIVIDE_OVERLOAD = r'/='
@@ -159,7 +161,7 @@ def t_COMMENT_SINGLELINE(t):
t_ASTERISK = r'\*'
t_MINUS = r'\-'
t_PLUS = r'\+'
-t_DIVIDE = r'/[^/]'
+t_DIVIDE = r'/[^/]' # fails to catch "/(" - method operator that overloads divide
t_AMPERSTAND = r'&'
t_EQUALS = r'='
t_CHAR_LITERAL = "'.'"
@@ -185,6 +187,10 @@ def t_error(v):
lex.lex()
debug = 0
+debug_trace = 0
+def trace_print(*arg):
+ global debug_trace
+ if debug_trace: print(arg)
supportedAccessSpecifier = [
'public',
@@ -192,6 +198,7 @@ supportedAccessSpecifier = [
'private'
]
+enumMaintianValueFormat = False
doxygenCommentCache = ""
def is_namespace(nameStack):
@@ -214,7 +221,105 @@ def is_enum_namestack(nameStack):
class CppParseError(Exception): pass
-class CppClass(dict):
+
+class _CppClass(dict):
+ def _parser_helper( self, stack ):
+ prev = None
+ prev2 = None
+ print('stack IN', ' '.join(stack))
+ for i, tok in enumerate(stack): # can not trust the first single ":" or last
+ if prev and prev2 and tok == ':' and prev != ':' and prev2 != ':':
+ break
+ prev = tok
+ prev2 = prev
+
+
+ a = stack[ : i+1 ]
+ b = stack[ i+1 : ]
+ while a[-1] == ':': a.pop()
+
+ print( 'HEAD', a )
+ print('______________')
+ print( 'TAIL', b )
+
+ if ''.join(stack).replace('::','_').count(':') >= 2:
+ if stack.count('class') == 1:
+ self['name'] = stack[ stack.index('class') + 1 ]
+ elif stack.count('struct') == 1:
+ self['name'] = stack[ stack.index('struct') + 1 ]
+ else:
+ self['unsafe_template'] = True
+ b = []
+
+ elif a[0] == 'template' and ('class' in a or 'struct' in a):
+ if '>' not in a:
+ self['name'] = a[ a.index('class') + 1 ]
+ self['unsafe_template'] = True
+
+ else:
+ copy = list( a )
+ last = len(a) - 1 - a[::-1].index('>')
+ self['template_typename'] = a[ a.index('>')-1 ]
+ a = a[ last+1 : ]
+ if not a:
+ a = copy[ copy.index('class')+1 : ]
+ x = ''.join( a )
+ assert '<' in x and '>' in x
+ self['name'] = x
+ self['special_template'] = True
+
+ elif 'class' in a:
+ self['name'] = ''.join( a[1:] )
+
+ elif 'struct' in a:
+ self['name'] = ''.join( a[1:] )
+ self['struct'] = True
+ self['struct_template'] = self['template_typename']
+ elif 'class' in b:
+ self['name'] = b[ b.index('class') + 1 ]
+ b = []
+ elif 'struct' in b:
+ self['name'] = b[ b.index('struct') + 1 ]
+ b = []
+ else:
+ self['unsafe_template'] = True
+ assert 0
+
+ elif a[0] == 'template' and b[-2] in ('class','struct'):
+ self['name'] = b[-1]
+ b = [] # b is invalid
+ elif a[0] == 'class':
+ self['name'] = ''.join( a[1:] )
+ elif 'class' in b:
+ self['name'] = b[ b.index('class') + 1 ]
+ b = []
+ elif 'struct' in b:
+ self['name'] = b[ b.index('struct') + 1 ]
+ self['struct'] = True
+ b = []
+ else:
+ assert 0
+
+
+ if b:
+ p = [ {'access':'public', 'class':''} ]
+ for x in b:
+ if x in 'public protected private'.split():
+ p[-1]['access'] = x
+ elif x == 'virtual':
+ p[-1]['virtual'] = True
+ elif x == ',':
+ p.append( {'access':'public', 'class':''} )
+ else:
+ p[-1]['class'] += x
+ self['inherits'] = p
+ else:
+ self['inherits'] = []
+
+ return True
+
+
+class CppClass( _CppClass ):
"""Takes a name stack and turns it into a class
Contains the following Keys:
@@ -266,12 +371,12 @@ class CppClass(dict):
def get_all_method_names( self ):
r = []
- for typ in 'public protected private'.split(): r += self.get_method_names(typ) # returns list
+ for typ in 'public protected private'.split(): r += self.get_method_names(typ) # returns list
return r
def get_all_pure_virtual_methods( self ):
r = {}
- for typ in 'public protected private'.split(): r.update(self.get_pure_virtual_methods(typ)) # returns dict
+ for typ in 'public protected private'.split(): r.update(self.get_pure_virtual_methods(typ)) # returns dict
return r
@@ -287,11 +392,11 @@ class CppClass(dict):
self['nested_classes'] = []
self['parent'] = None
self['abstract'] = False
+ self['namespace'] = ""
self._public_enums = {}
self._public_structs = {}
self._public_typedefs = {}
self._public_forward_declares = []
- self['namespace'] = ""
if (debug): print( "Class: ", nameStack )
if (len(nameStack) < 2):
@@ -301,42 +406,6 @@ class CppClass(dict):
if len(doxygenCommentCache):
self["doxygen"] = doxygenCommentCache
doxygenCommentCache = ""
- self["name"] = nameStack[1]
- inheritList = []
-
- if ":" in nameStack:
- self['name'] = nameStack[ nameStack.index(':') - 1 ] # harts hack - fixes: class __something__ classname
-
- if nameStack.count(':') == 1:
- nameStack = nameStack[nameStack.index(":") + 1:]
- while len(nameStack):
- tmpStack = []
- tmpInheritClass = {"access":"private"} # shouldn't public be default?
- if "," in nameStack:
- tmpStack = nameStack[:nameStack.index(",")]
- nameStack = nameStack[nameStack.index(",") + 1:]
- else:
- tmpStack = nameStack
- nameStack = []
- if len(tmpStack) == 0:
- break;
- elif len(tmpStack) == 1:
- tmpInheritClass["class"] = tmpStack[0]
- elif len(tmpStack) == 2:
- tmpInheritClass["access"] = tmpStack[0]
- tmpInheritClass["class"] = tmpStack[1]
- else:
- print( "Warning: can not parse inheriting class %s"%(" ".join(tmpStack)))
- if '>' in tmpStack: pass # allow skip templates for now
- else: raise NotImplemented
-
- if 'class' in tmpInheritClass: inheritList.append(tmpInheritClass)
-
- elif nameStack.count(':') == 2: self['parent'] = self['name']; self['name'] = nameStack[-1]
-
- elif nameStack.count(':') > 2: print('ERROR can not parse', nameStack) # TODO
-
- self['inherits'] = inheritList
methodAccessSpecificList = {}
propertyAccessSpecificList = {}
@@ -360,13 +429,19 @@ class CppClass(dict):
self['typedefs'] = typedefAccessSpecificList
self['forward_declares'] = forwardAccessSpecificList
+ ok = self._parser_helper( nameStack )
+ if not ok: self['invalid'] = True
+
+ def show_all(self):
+ self.show()
+ for key in self.keys(): print( ' %s : %s' %(key,self[key]) )
def show(self):
"""Convert class to a string"""
namespace_prefix = ""
if self["namespace"]: namespace_prefix = self["namespace"] + "::"
rtn = "class %s"%(namespace_prefix + self["name"])
- if self['abstract']: rtn += ' (abstract)\n'
+ if self['abstract']: rtn += ' (abstract)\n'
else: rtn += '\n'
if 'doxygen' in self.keys(): rtn += self["doxygen"] + '\n'
@@ -379,40 +454,93 @@ class CppClass(dict):
rtn += "\n"
rtn += " {\n"
for accessSpecifier in supportedAccessSpecifier:
- rtn += " %s\n"%(accessSpecifier)
+ rtn += " %s\n"%(accessSpecifier)
#Enums
if (len(self["enums"][accessSpecifier])):
- rtn += " <Enums>\n"
+ rtn += " <Enums>\n"
for enum in self["enums"][accessSpecifier]:
- rtn += " %s\n"%(repr(enum))
+ rtn += " %s\n"%(repr(enum))
#Properties
if (len(self["properties"][accessSpecifier])):
- rtn += " <Properties>\n"
+ rtn += " <Properties>\n"
for property in self["properties"][accessSpecifier]:
- rtn += " %s\n"%(repr(property))
+ rtn += " %s\n"%(repr(property))
#Methods
if (len(self["methods"][accessSpecifier])):
- rtn += " <Methods>\n"
+ rtn += " <Methods>\n"
for method in self["methods"][accessSpecifier]:
rtn += "\t\t" + method.show() + '\n'
rtn += " }\n"
print( rtn )
class _CppMethod( dict ):
- def _params_helper( self, params ):
- for p in params:
- p['method'] = self # save reference in variable to parent method
- if '::' in p['type']:
- ns = p['type'].split('::')[0]
- if ns not in Resolver.NAMESPACES and ns in Resolver.CLASSES:
- p['type'] = self['namespace'] + p['type']
- else: p['namespace'] = self[ 'namespace' ]
+ def _params_helper1( self, stack ):
+ # new July 7th, deal with defaults that init: vec3(0,0,0)
+ # so that comma split still works later on parsing the parameters.
+
+ # also deal with "throw" keyword
+ if 'throw' in stack: stack = stack[ : stack.index('throw') ]
+
+ ## remove GCC keyword __attribute__(...) and preserve returns ##
+ cleaned = []
+ hit = False; hitOpen = 0; hitClose = 0
+ for a in stack:
+ if a == '__attribute__': hit = True
+ if hit:
+ if a == '(': hitOpen += 1
+ elif a == ')': hitClose += 1
+ if a==')' and hitOpen == hitClose:
+ hit = False
+ else:
+ cleaned.append( a )
+ stack = cleaned
+
+ # also deal with attribute((const)) function prefix #
+ # TODO this needs to be better #
+ if len(stack) > 5:
+ a = ''.join(stack)
+ if a.startswith('((__const__))'): stack = stack[ 5 : ]
+ elif a.startswith('__attribute__((__const__))'): stack = stack[ 6 : ]
+
+ stack = stack[stack.index('(') + 1: ]
+ if not stack: return []
+ if len(stack)>=3 and stack[0]==')' and stack[1]==':': # is this always a constructor?
+ self['constructor'] = True
+ return []
+
+ stack.reverse(); _end_ = stack.index(')'); stack.reverse()
+ stack = stack[ : len(stack)-(_end_+1) ]
+
+ if '(' not in stack: return stack # safe to return, no defaults that init a class
+ elif stack.index('(') > stack.index(')'): # deals with: "constructor(int x) : func(x) {}"
+ return stack[ : stack.index(')') ] # fixed july20
+
+ # transforms ['someclass', '(', '0', '0', '0', ')'] into "someclass(0,0,0)'"
+ r = []; hit=False
+ for a in stack:
+ if a == '(': hit=True
+ elif a == ')': hit=False
+ if hit or a == ')': r[-1] = r[-1] + a
+ else: r.append( a )
+ return r
+
+ def _params_helper2( self, params ):
+ for p in params:
+ # if param becomes unresolved - function/parent is marked with 'unresolved_parameters'
+ if 'function' in self: p['function'] = self
+ else: p['method'] = self
+ # force full namespace for nested items, or take method name space as our own (bad idea?)
+ if '::' in p['type']:
+ ns = p['type'].split('::')[0]
+ if ns not in Resolver.NAMESPACES and ns in Resolver.CLASSES:
+ p['type'] = self['namespace'] + p['type']
+ else: p['namespace'] = self[ 'namespace' ]
class CppMethod( _CppMethod ):
"""Takes a name stack and turns it into a method
Contains the following Keys:
- self['rtnType'] - Return type of the method (ex. "int")
+ self['returns'] - Return type of the method (ex. "int")
self['name'] - Name of the method (ex. "getSize")
self['doxygen'] - Doxygen comments associated with the method if they exist
self['parameters'] - List of CppVariables
@@ -426,30 +554,22 @@ class CppMethod( _CppMethod ):
if self['destructor']: r.append( 'destructor' )
return '\n\t\t '.join( r )
- def __init__(self, nameStack, curClass, methinfo):
+ def __init__(self, nameStack, curClass=None, methinfo={} ):
if (debug): print( "Method: ", nameStack )
global doxygenCommentCache
+
+ if not curClass: self['function'] = True
+
if len(doxygenCommentCache):
self["doxygen"] = doxygenCommentCache
doxygenCommentCache = ""
if "operator" in nameStack:
- self["rtnType"] = " ".join(nameStack[:nameStack.index('operator')])
self["name"] = "".join(nameStack[nameStack.index('operator'):nameStack.index('(')])
else:
- self["rtnType"] = " ".join(nameStack[:nameStack.index('(') - 1])
self["name"] = " ".join(nameStack[nameStack.index('(') - 1:nameStack.index('(')])
- if len(self["rtnType"]) == 0 or self["name"] == curClass:
- self["rtnType"] = "void"
-
- self.update( methinfo ) # harts hack
-
- paramsStack = nameStack[nameStack.index('(') + 1: ]
- if paramsStack: paramsStack = paramsStack[ : paramsStack.index(')') ] # fixed april 7th
- #Remove things from the stack till we hit the last paren, this helps handle abstract and normal methods
- #if paramsStack: # harts hacks - this is a bug caused by change to line 88?
- # while paramsStack[-1] != ")": paramsStack.pop()
- # paramsStack.pop()
+ self.update( methinfo ) # harts hack
+ paramsStack = self._params_helper1( nameStack )
params = []
#See if there is a doxygen comment for the variable
doxyVarDesc = {}
@@ -490,34 +610,48 @@ class CppMethod( _CppMethod ):
if len(param.keys()): params.append(param)
break
-
self["parameters"] = params
- self._params_helper( params )
+ self._params_helper2( params ) # mods params inplace
class _CppVariable(dict):
- def _name_stack_helper( self, stack ):
- stack = list(stack)
- if '=' not in stack: # TODO refactor me
- # check for array[n] and deal with funny array syntax: "int myvar:99"
- array = []
- while stack and stack[-1].isdigit(): array.append( stack.pop() )
- if array: array.reverse(); self['array'] = int(''.join(array))
- if stack and stack[-1].endswith(':'): stack[-1] = stack[-1][:-1] # fixed June 23, BulletPhysics
-
- while stack and not stack[-1]: stack.pop() # can be empty
- return stack
-
- def init(self):
- #assert self['name'] # allow unnamed variables, methods like this: "void func(void);"
- a = []
- self['aliases'] = []; self['parent'] = None; self['typedef'] = None
- for key in 'constant reference pointer static typedefs class fundamental unresolved'.split():
- self[ key ] = 0
- for b in self['type'].split():
- if b == '__const__': b = 'const'
- a.append( b )
- self['type'] = ' '.join( a )
+ def _name_stack_helper( self, stack, fullStack ):
+ print('V'*80); print( stack ); print(fullStack); print('_'*80)
+ stack = list(stack)
+ if stack[-1].isdigit() and '=' not in stack: # TODO refactor me - was: '=' not in stack or
+ # check for array[n] and deal with funny array syntax: "int myvar:99"
+ bits = []
+ while stack and stack[-1].isdigit(): bits.append( stack.pop() )
+ if bits:
+ bits.reverse()
+ self['bitfield'] = int(''.join(bits))
+ assert stack[-1] == ':'
+ stack.pop()
+
+ ## find and strip array def ##
+ if '[' in stack:
+ assert stack.count('[') == stack.count(']')
+ a = ['']; hit = 0; _stack = []
+ for s in stack:
+ if s == '[': hit += 1
+ elif s == ']': hit -= 1; a.append( '' )
+ elif hit: a[-1] += s
+ elif not hit: _stack.append( s )
+ stack = _stack
+
+ b = []
+ for s in a:
+ if s.isdigit(): b.append( int( s ) )
+ elif s != '': self['invalid'] = True
+ if not b: self['pointer'] += 1
+ else:
+ self['array'] = b[0]
+ self['array_dimensions'] = b
+ if len(b)>1: self['multidimensional'] = True
+
+ while stack and not stack[-1]: stack.pop() # can be empty?
+ return stack
+
class CppVariable( _CppVariable ):
@@ -525,34 +659,46 @@ class CppVariable( _CppVariable ):
Contains the following Keys:
self['type'] - Type for the variable (ex. "const string &")
+ self['raw_type'] - Type of variable without pointers or other markup (ex. "string")
self['name'] - Name of the variable (ex. "numItems")
self['namespace'] - Namespace containing the enum
self['desc'] - Description of the variable if part of a method (optional)
self['doxygen'] - Doxygen comments associated with the method if they exist
- self['defaltValue'] - Default value of the variable, this key will only
- exist if there is a default value
+ self['defalt'] - Default value of the variable, this key will only exist if there is a default value
"""
Vars = []
- def __init__(self, nameStack, **kwargs):
- nameStack = self._name_stack_helper( nameStack )
- if (debug): print( "Variable: ", nameStack )
- if (len(nameStack) < 2): return
+ def __init__(self, nameStack, fullStack=None, doxyVarDesc=None): # CppMethod will not pass fullStack for params
+ self['aliases'] = []; self['parent'] = None; self['typedef'] = None
+ for key in 'constant reference pointer static typedefs class fundamental unresolved mutable'.split():
+ self[ key ] = 0
+
+
+ _stack_ = nameStack
+ nameStack = self._name_stack_helper( nameStack, fullStack )
global doxygenCommentCache
if len(doxygenCommentCache):
self["doxygen"] = doxygenCommentCache
doxygenCommentCache = ""
- if ("=" in nameStack):
+
+ if (debug): print( "Variable: ", nameStack )
+
+ if (len(nameStack) < 2):
+ if len(nameStack) == 1: self['type'] = nameStack[0]; self['name'] = ''
+ else: print(_stack_); assert 0
+
+ elif ("=" in nameStack):
self["type"] = " ".join(nameStack[:nameStack.index("=") - 1])
self["name"] = nameStack[nameStack.index("=") - 1]
- self["defaltValue"] = " ".join(nameStack[nameStack.index("=") + 1:]) # deprecate camelCase in dicts
self['default'] = " ".join(nameStack[nameStack.index("=") + 1:])
+ self['default'] = self['default'].replace(' <', '<' )
+ self['default'] = self['default'].replace(' >', '>' )
- elif nameStack[-1] == '*': # rare case - function param is an unnamed pointer: "void somemethod( SomeObject* )"
+ elif nameStack[-1] in '*&': # rare cases - function param is an unnamed pointer: "void somemethod( SomeObject* )"
self['type'] = ' '.join(nameStack)
self['name'] = ''
- else: # common case
+ else: # common case
self["type"] = " ".join(nameStack[:-1])
self["name"] = nameStack[-1]
@@ -561,32 +707,71 @@ class CppVariable( _CppVariable ):
self["type"] = self["type"].replace(" <","<")
self["type"] = self["type"].replace(" >",">")
#Optional doxygen description
- try:
- self["desc"] = kwargs["doxyVarDesc"][self["name"]]
- except: pass
+ if doxyVarDesc and self['name'] in doxyVarDesc:
+ self['description'] = doxyVarDesc[ self['name'] ]
- self.init()
- CppVariable.Vars.append( self ) # save and resolve later
+ self['type'] = self['type'].strip()
+ a = []
+ for b in self['type'].split():
+ if b == '__const__': b = 'const'
+ a.append( b )
+
+ if not a:
+ self['invalid'] = True # void someinvalidfunction( int x, y=INVALID );
+ print('WARN - bad variable', self )
+
+ else:
+ if a[0] == 'class':
+ self['explicit_class'] = a[1]
+ a = a[1:]
+ elif a[0] == 'struct':
+ self['explicit_struct'] = a[1]
+ a = a[1:]
+ self['type'] = ' '.join( a )
+
+ if self['name'].count('<') != self['name'].count('>'): self['invalid'] = True
+
+ CppVariable.Vars.append( self ) # save and resolve later
class _CppEnum(dict):
- def resolve_enum_values( self, values ):
- t = int; i = 0
- names = [ v['name'] for v in values ]
- for v in values:
- if 'value' in v:
- a = v['value'].strip()
- if a.isdigit(): i = a = int( a )
- elif a in names:
- for other in values:
- if other['name'] == a:
- v['value'] = other['value']
- break
-
- elif '"' in a or "'" in a: t = str # only if there are quotes it this a string enum
-
- else: v['value'] = i
- i += 1
- return t
+ def resolve_enum_values( self, values ):
+ """Evaluates the values list of dictionaries passed in and figures out what the enum value
+ for each enum is editing in place:
+
+ Example:
+ From: [{'name': 'ORANGE'},
+ {'name': 'RED'},
+ {'name': 'GREEN', 'value': '8'}]
+ To: [{'name': 'ORANGE', 'value': 0},
+ {'name': 'RED', 'value': 1},
+ {'name': 'GREEN', 'value': 8}]
+ """
+ t = 'int'; i = 0
+ names = [ v['name'] for v in values ]
+ for v in values:
+ if 'value' in v:
+ a = v['value'].strip()
+ if a.lower().startswith("0x"):
+ try:
+ i = a = int(a , 16)
+ except:pass
+ elif a.isdigit():
+ i = a = int( a )
+ elif a in names:
+ for other in values:
+ if other['name'] == a:
+ v['value'] = other['value']
+ break
+
+ elif '"' in a or "'" in a: t = 'char*' # only if there are quotes it this a string enum
+ else:
+ try:
+ a = i = ord(a)
+ except: pass
+ if not enumMaintianValueFormat: v['value'] = a
+ else: v['value'] = i
+ i += 1
+ return t
class CppEnum(_CppEnum):
"""Takes a name stack and turns it into an Enum
@@ -630,7 +815,7 @@ class CppEnum(_CppEnum):
if d: valueList.append( d )
if len(valueList):
- self['type'] = self.resolve_enum_values( valueList ) # returns int for standard enum
+ self['type'] = self.resolve_enum_values( valueList ) # returns int for standard enum
self["values"] = valueList
else:
print( 'WARN-enum: empty enum', nameStack )
@@ -653,848 +838,1209 @@ class CppEnum(_CppEnum):
self["namespace"] = ""
def is_fundamental(s):
- for a in s.split():
- if a not in 'size_t struct union unsigned signed bool char short int float double long void *': return False
- return True
+ for a in s.split():
+ if a not in 'size_t wchar_t struct union unsigned signed bool char short int float double long void *': return False
+ return True
+
+def prune_templates( stack ):
+ x = []; hit = 0
+ for a in stack:
+ if a == '<' or a.startswith('<'): hit += 1
+ elif a == '>' or a.endswith('>'): hit -= 1
+ elif not hit and a != 'template': x.append( a )
+ return x
+
+def prune_arrays( stack ):
+ x = []; hit = 0
+ for a in stack:
+ if a == '[' or a.startswith('['): hit += 1
+ elif a == ']' or a.endswith(']'): hit -= 1
+ elif not hit: x.append( a )
+ return x
+
def is_method_namestack(stack):
- r = False
- if '(' not in stack: r = False
- #elif '=' in stack and stack.index('=') < stack.index('(') and stack[stack.index('=')-1] != 'operator': r = False #disabled July6th - allow all operators
- elif 'operator' in stack: r = True # allow all operators
- elif '{' in stack and stack.index('{') < stack.index('('): r = False # struct that looks like a method/class
- elif '(' in stack and ')' in stack:
- if '{' in stack and '}' in stack: r = True
- elif stack[-1] == ';': r = True
- elif '{' in stack: r = True # ideally we catch both braces... TODO
- else: r = False
- return r
+ clean = prune_templates( stack ); print('CLEAN TEMPLATES',clean)
+ clean = prune_arrays( clean ); print('CLEAN ARRAYS',clean)
+
+ r = False
+ if 'operator' in stack: r = True # allow all operators
+ elif not ('(' in stack or '/(' in stack): r = False
+ elif stack[0]=='mutable': r = False
+ elif clean and clean[0] in ('class', 'struct'): r = False
+ elif not ('(' in clean or '/(' in clean): r = False
+ #elif '__attribute__' in stack: r = False
+ #elif stack[0] == '__attribute': r = False
+ elif stack[0] == 'typedef': r = False # TODO deal with typedef function prototypes
+ elif stack[0] in 'return if else case switch throw +'.split(): print( stack ); assert 0; r = False
+ elif stack[0] == '}' and stack[1] in 'return if else case switch'.split(): print( stack ); assert 0; r = False
+ elif '=' in stack:# and stack.index('=') < stack.index('('):
+ #if 'template' not in stack: r = False
+ if '=' in clean and clean.index('=') < clean.index('('): r = False
+ else: r = True
+
+ elif '{' in stack and stack.index('{') < stack.index('('): r = False # struct that looks like a method/class
+ elif '(' in stack and ')' in stack:
+ if '{' in stack and '}' in stack: r = True
+ #elif '/' in stack: r = False
+ #elif '/ ' in stack: r = False
+ elif stack[-1] == ';': r = True
+ elif '{' in stack: r = True
+ x = ''.join(stack)
+ if x.endswith('(0,0,0);'): r = False
+ elif x.endswith('(0);'): r = False
+ elif x.endswith(',0);'): r = False
+ elif x.endswith(',1);'): r = False
+ elif x.endswith(',true);'): r = False
+ elif x.endswith(',false);'): r = False
+ elif x.endswith(',0xFF);'): r = False
+ else: r = False
+ print( 'is method namestack', r, stack ); print('_'*80)
+ return r
class CppStruct(dict):
- Structs = []
- def __init__(self, nameStack):
- if len(nameStack) >= 2: self['type'] = nameStack[1]
- else: self['type'] = None
- self['fields'] = []
- self.Structs.append( self )
+ Structs = []
+ def __init__(self, nameStack):
+ if nameStack[0] == 'template': self['template'] = True
+ if nameStack.index('struct')+1 < len(nameStack):
+ self['type'] = nameStack[ nameStack.index('struct') + 1 ]
+ else: self['type'] = None
+ self['fields'] = []
+ self['methods'] = []
+ self['parent'] = None
+ self.Structs.append( self )
C99_NONSTANDARD = {
- 'int8' : 'signed char',
- 'int16' : 'short int',
- 'int32' : 'int',
- 'int64' : 'int64_t', # this can be: long int (64bit), or long long int (32bit)
- 'uint' : 'unsigned int',
- 'uint8' : 'unsigned char',
- 'uint16' : 'unsigned short int',
- 'uint32' : 'unsigned int',
- 'uint64' : 'uint64_t', # depends on host bits
+ 'int8' : 'signed char',
+ 'int16' : 'short int',
+ 'int32' : 'int',
+ 'int64' : 'int64_t', # this can be: long int (64bit), or long long int (32bit)
+ 'uint' : 'unsigned int',
+ 'uint8' : 'unsigned char',
+ 'uint16' : 'unsigned short int',
+ 'uint32' : 'unsigned int',
+ 'uint64' : 'uint64_t', # depends on host bits
}
def standardize_fundamental( s ):
- if s in C99_NONSTANDARD: return C99_NONSTANDARD[ s ]
- else: return s
+ if s in C99_NONSTANDARD: return C99_NONSTANDARD[ s ]
+ else: return s
class Resolver(object):
- C_FUNDAMENTAL = 'size_t unsigned signed bool char wchar short int float double long void'.split()
- C_FUNDAMENTAL += 'struct union enum'.split()
-
-
- SubTypedefs = {} # TODO deprecate?
- NAMESPACES = []
- CLASSES = {}
- STRUCTS = {}
-
- def initextra(self):
- self.typedefs = {}
- self.typedefs_order = []
- self.classes_order = []
- self.structs = Resolver.STRUCTS
- self.structs_order = []
- self.namespaces = Resolver.NAMESPACES # save all namespaces
- self.curStruct = None
- self.stack = [] # full name stack, good idea to keep both stacks? (simple stack and full stack)
- self._classes_brace_level = {} # class name : level
- self._structs_brace_level = {} # struct type : level
- self._method_body = None
- self._forward_decls = []
- self._template_typenames = [] # template<typename XXX>
-
- def current_namespace(self): return self.cur_namespace(True)
-
- def cur_namespace(self, add_double_colon=False):
- rtn = ""
- i = 0
- while i < len(self.nameSpaces):
- rtn += self.nameSpaces[i]
- if add_double_colon or i < len(self.nameSpaces) - 1: rtn += "::"
- i+=1
- return rtn
-
-
- def guess_ctypes_type( self, string ):
- pointers = string.count('*')
- string = string.replace('*','')
-
- a = string.split()
- if 'unsigned' in a: u = 'u'
- else: u = ''
- if 'long' in a and 'double' in a: b = 'longdouble' # there is no ctypes.c_ulongdouble (this is a 64bit float?)
- elif a.count('long') == 2 and 'int' in a: b = '%sint64' %u
- elif a.count('long') == 2: b = '%slonglong' %u
- elif 'long' in a: b = '%slong' %u
- elif 'double' in a: b = 'double' # no udouble in ctypes
- elif 'short' in a: b = '%sshort' %u
- elif 'char' in a: b = '%schar' %u
- elif 'wchar' in a: b = 'wchar'
- elif 'bool' in a: b = 'bool'
- elif 'float' in a: b = 'float'
-
- elif 'int' in a: b = '%sint' %u
- elif 'int8' in a: b = 'int8'
- elif 'int16' in a: b = 'int16'
- elif 'int32' in a: b = 'int32'
- elif 'int64' in a: b = 'int64'
-
- elif 'uint' in a: b = 'uint'
- elif 'uint8' in a: b = 'uint8'
- elif 'uint16' in a: b = 'uint16'
- elif 'uint32' in a: b = 'uint32'
- elif 'uint64' in a: b = 'uint64'
-
- elif 'size_t' in a: b = 'size_t'
- elif 'void' in a: b = 'void_p'
-
- elif string in 'struct union'.split(): b = 'void_p' # what should be done here? don't trust struct, it could be a class, no need to expose via ctypes
- else: b = 'void_p'
-
- if not pointers: return 'ctypes.c_%s' %b
- else:
- x = ''
- for i in range(pointers): x += 'ctypes.POINTER('
- x += 'ctypes.c_%s' %b
- x += ')' * pointers
- return x
-
- def resolve_type( self, string, result ): # recursive
- '''
- keeps track of useful things like: how many pointers, number of typedefs, is fundamental or a class, etc...
- '''
- ## be careful with templates, what is inside <something*> can be a pointer but the overall type is not a pointer
- ## these come before a template
- s = string.split('<')[0]
- result[ 'constant' ] += s.split().count('const')
- result[ 'static' ] += s.split().count('static')
- result[ 'mutable' ] = 'mutable' in s.split()
-
- ## these come after a template
- s = string.split('>')[-1]
- result[ 'pointer' ] += s.count('*')
- result[ 'reference' ] += s.count('&')
-
-
- x = string; alias = False
- for a in '* & const static mutable'.split(): x = x.replace(a,'')
- for y in x.split():
- if y not in self.C_FUNDAMENTAL: alias = y; break
-
- #if alias == 'class':
- # result['class'] = result['name'] # forward decl of class
- # result['forward_decl'] = True
- if alias == '__extension__': result['fundamental_extension'] = True
- elif alias:
- result['aliases'].append( alias )
- if alias in C99_NONSTANDARD:
- result['type'] = C99_NONSTANDARD[ alias ]
- result['typedef'] = alias
- result['typedefs'] += 1
- elif alias in self.typedefs:
- result['typedefs'] += 1
- result['typedef'] = alias
- self.resolve_type( self.typedefs[alias], result )
- elif alias in self.classes:
- klass = self.classes[alias]; result['fundamental'] = False
- result['class'] = klass
- result['unresolved'] = False
- else: result['unresolved'] = True
- else:
-