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