diff --git a/cppParser/Class.py b/cppParser/Class.py index c485358..e3fed09 100644 --- a/cppParser/Class.py +++ b/cppParser/Class.py @@ -14,10 +14,13 @@ def concatenate_template(list): class Class(Node.Node): def __init__(self, stack=[], file="", lineNumber=0): + # check input : if len(stack) < 2: debug.error("Can not parse class : " + str(stack)) return Node.Node.__init__(self, 'class', stack[1], file, lineNumber) + self.subList = [] + self.access = "private" # heritage list : self.inherit = [] if len(stack) == 2: @@ -30,7 +33,7 @@ class Class(Node.Node): debug.error("error in parsing class : " + str(stack) + " missing ':' at the 3rd position ...") list = concatenate_template(stack[3:]) - debug.info("inherit : " + str(list)) + debug.verbose("inherit : " + str(list)) access = "private" for element in list: if element in ['private', 'protected', 'public']: @@ -40,7 +43,7 @@ class Class(Node.Node): else: self.inherit.append({'access' : access, 'class' : element}) - debug.info("class : " + self.to_str()) + debug.verbose("class : " + self.to_str()) def to_str(self) : ret = "class " + self.name diff --git a/cppParser/Enum.py b/cppParser/Enum.py index c31c5b3..082ed3b 100644 --- a/cppParser/Enum.py +++ b/cppParser/Enum.py @@ -4,12 +4,39 @@ import Node class Enum(Node.Node): def __init__(self, stack=[], file="", lineNumber=0): - name = "" - Node.Node.__init__(self, 'enum', name, file, lineNumber) - # CPP section: + self.baseValue = 0; + # check input : + if len(stack) < 2: + debug.error("Can not parse class : " + str(stack)) + return + self.typedef = False + if stack[0] == 'typedef': + self.typedef = True + stack[1:] + + Node.Node.__init__(self, 'enum', stack[1], file, lineNumber) + self.listElement = [] def to_str(self) : return "enum " + self.name + " { ... };" + + def enum_append(self, stack): + subList = [] + tmp = [] + for element in stack: + if element == ',': + subList.append(tmp) + tmp = [] + else: + tmp.append(element) + if len(tmp) != 0: + subList.append(tmp) - + debug.verbose(" TODO : Need to append enum : " + str(subList)) + + # TODO : Set the value at the enum ... + for element in subList: + self.listElement.append(element[0]) + + debug.info("enum list : " + str(self.listElement)) \ No newline at end of file diff --git a/cppParser/Methode.py b/cppParser/Methode.py index c41214b..a463148 100644 --- a/cppParser/Methode.py +++ b/cppParser/Methode.py @@ -2,20 +2,83 @@ import lutinDebug as debug import Node import Type +import Variable class Methode(Node.Node): def __init__(self, stack=[], file="", lineNumber=0): name = "" - Node.Node.__init__(self, 'methode', name, file, lineNumber) - self.name = "" - self.returnType = Type.TypeVoid() - self.virtual = False # only for C++ + type = 'methode' + self.virtual = False + self.virtualPure = False self.static = False self.inline = False self.const = False # the end of line cont methode is sont for the class ... - self.doc = None - self.variable = None - self.visibility = "private" # only for C++ : "public" "protected" "private" + + # remove constructer inside declaration ... + if ':' in stack: + res = [] + for element in stack: + if element != ':': + res.append(element) + else: + break + stack = res + + if stack[len(stack)-2] == '=' \ + and stack[len(stack)-1] == '0': + stack = stack[:len(stack)-2] + self.virtualPure = True + + if stack[0] == 'virtual': + self.virtual = True + stack = stack[1:] + if stack[0] == 'static': + self.static = True + stack = stack[1:] + if stack[0] == 'inline': + self.inline = True + stack = stack[1:] + if stack[len(stack)-1] == 'const': + self.const = True + stack = stack[:len(stack)-1] + + namePos = -1 + + debug.verbose("methode parse : " + str(stack)) + for iii in range(0, len(stack)-2): + if stack[iii+1] == '(': + name = stack[iii] + namePos = iii + break; + + if namePos == 0: + debug.verbose("start with '" + str(name[0]) + "'") + if name[0] == '~': + type = 'destructor' + else: + type = 'constructor' + debug.verbose("methode name : " + name) + Node.Node.__init__(self, type, name, file, lineNumber) + + self.returnType = Type.TypeNone() + self.variable = [] + + # create the return Type (Can be Empty) + retTypeStack = stack[:namePos] + debug.verbose("return : " + str(retTypeStack)) + self.returnType = Type.Type(retTypeStack) + + parameterStack = stack[namePos+2:len(stack)-1] + debug.verbose("parameter : " + str(parameterStack)) + paramTmp = [] + for element in parameterStack: + if element == ',': + self.variable.append(Variable.Variable(paramTmp)) + paramTmp = [] + else: + paramTmp.append(element) + if len(paramTmp) != 0: + self.variable.append(Variable.Variable(paramTmp)) def to_str(self): ret = "" @@ -31,7 +94,9 @@ class Methode(Node.Node): ret += "(" # ... ret += ")" + if self.virtualPure == True: + ret += " = 0" if self.const == True: ret += " const" return ret - + \ No newline at end of file diff --git a/cppParser/Namespace.py b/cppParser/Namespace.py index 7b9d08f..fa3817c 100644 --- a/cppParser/Namespace.py +++ b/cppParser/Namespace.py @@ -7,8 +7,10 @@ class Namespace(Node.Node): if len(stack) != 2: debug.error("Can not parse namespace : " + str(stack)) Node.Node.__init__(self, 'namespace', stack[1], file, lineNumber) + # enable sub list + self.subList = [] - debug.verbose("find class : " + self.to_str()) + debug.verbose("find namespace : " + self.to_str()) def to_str(self) : return "namespace " + self.name + " { ... };" diff --git a/cppParser/Node.py b/cppParser/Node.py index 796c4a1..3a21348 100644 --- a/cppParser/Node.py +++ b/cppParser/Node.py @@ -1,6 +1,14 @@ #!/usr/bin/python import lutinDebug as debug +accessList = ['private', 'protected', 'public'] + +def debug_space(level): + ret = "" + for iii in range(0,level): + ret += " " + return ret + class Node(): def __init__(self, type, name="", file="", lineNumber=0): self.nodeType = type @@ -8,13 +16,83 @@ class Node(): self.doc = None self.fileName = file self.lineNumber = lineNumber - self.subList = [] + self.subList = None + self.access = None def to_str(self): return "" - + + def str(self): + return self.to_str() + + def get_node_type(self): + return self.nodeType + + def get_name(self): + return self.name + + def debug_display(self, level=0, access = None): + if access == 'private': + debug.info(debug_space(level) + "- " + self.nodeType + " => " + self.name) + elif access == 'protected': + debug.info(debug_space(level) + "# " + self.nodeType + " => " + self.name) + elif access == 'public': + debug.info(debug_space(level) + "+ " + self.nodeType + " => " + self.name) + else: + debug.info(debug_space(level) + self.nodeType + " => " + self.name) + if self.subList!= None: + for element in self.subList: + if 'access' in element.keys(): + element['node'].debug_display(level+1, element['access']) + else: + element['node'].debug_display(level+1) + + def set_access(self, access): + if access not in accessList: + debug.warning("This is not a valid access : '" + access + "' : availlable : " + str(accessList)) + return + if self.access == None: + debug.error("This Node does not support acces configuration...") + return + self.access = access + + def get_access(self): + return self.access + def append(self, newSubElement): # just add it in a sub List : - self.subList.append(newSubElement) + if self.subList == None: + debug.error("can not add a '" + newSubElement.nodeType + "' at this '" + self.nodeType + "'") + return + if newSubElement.get_node_type() != 'namespace': + if self.access == None: + self.subList.append({'node' : newSubElement}) + else: + self.subList.append({'access' : self.access, 'node' : newSubElement}) + return + + # check if the element already exist + for element in self.subList: + if element['node'].get_node_type() == 'namespace': + if element['node'].get_name() == newSubElement.get_name(): + debug.verbose("fusionate with previous declaration") + element['node'].fusion(newSubElement) + return + # normal case adding : + if self.access == None: + self.subList.append({'node' : newSubElement}) + else: + self.subList.append({'access' : self.access, 'node' : newSubElement}) + + ## @ brief only for namespace : + def fusion(self, addedElement): + for element in addedElement.subList: + self.append(element['node']) + + +class MainNode(Node): + def __init__(self, type="library", name=""): + Node.__init__(self, type, name) + self.subList = [] \ No newline at end of file diff --git a/cppParser/Parse.py b/cppParser/Parse.py index 2d95073..f9aa8ac 100644 --- a/cppParser/Parse.py +++ b/cppParser/Parse.py @@ -15,6 +15,7 @@ import Union import Methode import Enum import Variable +import Node tokens = [ 'NUMBER', @@ -114,11 +115,6 @@ lex.lex() doxygenCommentCache = "" -supportedAccessSpecifier = [ - 'public', - 'protected', - 'private' -] ## ## @brief Join the class name element : ['class', 'Bar', ':', ':', 'Foo'] -> ['class', 'Bar::Foo'] @@ -168,10 +164,14 @@ class parse_file(): ret += " " return ret + def fusion(self, baseNode): + baseNode.fusion(self.mainNode) + return baseNode + def __init__(self, fileName): - self.m_classes = [] + self.mainNode = Node.MainNode("main-node", "tmp") self.m_elementParseStack = [] - debug.info("Parse File tod document : '" + fileName + "'") + debug.debug("Parse file : '" + fileName + "'") self.headerFileName = fileName @@ -179,11 +179,6 @@ class parse_file(): # load all the file data : headerFileStr = lutinTools.FileReadData(fileName) - # Make sure supportedAccessSpecifier are sane - for i in range(0, len(supportedAccessSpecifier)): - if " " not in supportedAccessSpecifier[i]: continue - supportedAccessSpecifier[i] = re.sub("[ ]+", " ", supportedAccessSpecifier[i]).strip() - # Strip out template declarations # TODO : What is the real need ??? headerFileStr = re.sub("template[\t ]*<[^>]*>", "", headerFileStr) @@ -193,8 +188,12 @@ class parse_file(): headerFileStr = re.sub("\r", "", headerFileStr) # TODO : Can generate some error ... headerFileStr = re.sub("\#if 0(.*?)(\#endif|\#else)", "", headerFileStr, flags=re.DOTALL) + headerFileafter = re.sub("\@interface(.*?)\@end", "", headerFileStr, flags=re.DOTALL) + if headerFileStr != headerFileafter : + debug.debug(" Objective C interface ... ==> not supported") + return - debug.debug(headerFileStr) + debug.verbose(headerFileStr) # Change multi line #defines and expressions to single lines maintaining line nubmers matches = re.findall(r'(?m)^(?:.*\\\n)+.*$', headerFileStr) @@ -278,7 +277,7 @@ class parse_file(): elif tok.type == 'CLOSE_BRACE': if len(self.nameStack) != 0: if self.previous_is('enum') == True: - debug.info(self.gen_debug_space() + "enum list... : " + str(self.nameStack)); + self.brace_type_append('enum list', self.nameStack); else: debug.warning(self.gen_debug_space() + "end brace DROP : " + str(self.nameStack)); self.stack = [] @@ -325,7 +324,7 @@ class parse_file(): or tok.type == 'CHAR_LITERAL': self.nameStack.append(tok.value) elif tok.type == 'COLON': - if self.nameStack[0] in ['private', 'protected', 'public']: + if self.nameStack[0] in Node.accessList: debug.debug(self.gen_debug_space() + "change visibility : " + self.nameStack[0]); self.brace_type_change_access(self.nameStack[0]) self.nameStack = [] @@ -336,7 +335,7 @@ class parse_file(): if len(self.nameStack) != 0: self.nameStack = create_compleate_class_name(self.nameStack) if is_a_function(self.nameStack): - debug.info(self.gen_debug_space() + "function : " + str(self.nameStack)); + self.brace_type_append('function', self.nameStack); elif 'namespace' in self.nameStack: debug.debug(self.gen_debug_space() + "find a namespace DECLARATION : " + str(self.nameStack)); elif 'class' in self.nameStack: @@ -351,17 +350,23 @@ class parse_file(): debug.debug(self.gen_debug_space() + "find a union DECLARATION : " + str(self.nameStack)); else: if self.previous_is('enum') == True: - debug.info(self.gen_debug_space() + "enum list : " + str(self.nameStack)); + self.brace_type_append('enum list', self.nameStack); else: # TODO : Check if it is true in all case : self.brace_type_append('variable', self.nameStack); #debug.warning(self.gen_debug_space() + "semicolumn : " + str(self.nameStack)); self.stack = [] self.nameStack = [] + #self.debug_display(); + + def debug_display(self): + debug.info("Debug display :") + self.mainNode.debug_display(1) def create_element(self, type, stack): ret = None - if type == 'empty': + if type == 'empty' \ + or type == 'enum list': pass elif type == 'namespace': ret = Namespace.Namespace(stack, self.headerFileName, self.curLine) @@ -386,46 +391,75 @@ class parse_file(): return ret def brace_type_push(self, type, stack): - debug.info(self.gen_debug_space() + "find a <<" + type + ">> : " + str(stack)); + debug.debug(self.gen_debug_space() + "find a <<" + type + ">> : " + str(stack)); myClassElement = self.create_element(type, stack) element = { 'type' : type, 'stack' : stack, - 'access' : None, 'node' : myClassElement } - if type == 'class': - element['access'] = "private" - elif type == 'struct': - element['access'] = "public" self.braceDepthType.append(element) #debug.info ("append : " + str(element)) + def brace_type_append_current(self, element, id = -50): + if id == -50: + id = len(self.braceDepthType)-1 + if id >= 0: + while self.braceDepthType[id]['node'] == None: + # special case for empty brace, just add it to the upper + id -=1 + if id < 0: + break; + if id < 0: + self.mainNode.append(element) + else: + self.braceDepthType[id]['node'].append(element) + def brace_type_append(self, type, stack): - debug.info(self.gen_debug_space() + " append a <<" + type + ">> : " + str(stack)); + debug.debug(self.gen_debug_space() + " append a <<" + type + ">> : " + str(stack)); lastType = self.get_last_type() newType = self.create_element(type, stack) - if newType == None: - debug.info("TODO : Parse the special type") + if newType != None: + self.brace_type_append_current(newType) return - if len(self.braceDepthType) == 0: - debug.info("TODO : Append in glocal directly ...") + # enum sub list: + if lastType == 'enum' \ + and type == 'enum list': + id = len(self.braceDepthType)-1 + self.braceDepthType[id]['node'].enum_append(stack) return - self.braceDepthType[len(self.braceDepthType)-1]['node'].append(newType) + debug.info("TODO : Parse the special type") def brace_type_pop(self): + id = len(self.braceDepthType)-1 + if id < 0: + debug.warning("Try to pop the stack with No more element ...") + return + if self.braceDepthType[id]['node'] == None: + # nothing to add at the upper ... + pass + else: + # add it on the previous + self.brace_type_append_current(self.braceDepthType[id]['node'], id-1) self.braceDepthType.pop() def brace_type_change_access(self, newOne): - if len(self.braceDepthType) == 0: - debug.error("set access in nothing ... ") - return - if newOne not in supportedAccessSpecifier: + if newOne not in Node.accessList: debug.error("unknow access type : " + newOne) return - if self.braceDepthType[len(self.braceDepthType)-1]['access'] == None: - debug.error("Can not set access in other as : 'class' or 'struct' :" + str(self.braceDepthType[len(self.braceDepthType)-1])) - return - self.braceDepthType[len(self.braceDepthType)-1]['access'] = newOne + id = len(self.braceDepthType)-1 + if id >= 0: + while self.braceDepthType[id]['node'] == None: + # special case for empty brace, just add it to the upper + id -=1 + if id < 0: + break; + if id < 0: + debug.warning("can not change the main access on the library") + else: + if self.braceDepthType[id]['node'].get_access() == None: + debug.error("Can not set access in other as : 'class' or 'struct' :" + str(self.braceDepthType[id])) + return + self.braceDepthType[id]['node'].set_access(newOne) def previous_is(self, type): if self.get_last_type() == type: @@ -441,6 +475,17 @@ def is_a_function(stack) : # in a function we need to have functionName + ( + ) if len(stack) < 3: return False + if ':' in stack: + res = [] + for element in stack: + if element != ':': + res.append(element) + else: + break + stack = res + if stack[len(stack)-2] == '=' \ + and stack[len(stack)-1] == '0': + stack = stack[:len(stack)-2] #can end with 2 possibilities : ')', 'const' or ')' if stack[len(stack)-1] == ')' \ or ( stack[len(stack)-2] == ')' \ diff --git a/cppParser/Resolver.py b/cppParser/Resolver.py deleted file mode 100644 index e69de29..0000000 diff --git a/cppParser/Struct.py b/cppParser/Struct.py index f4e0dff..dde1a1f 100644 --- a/cppParser/Struct.py +++ b/cppParser/Struct.py @@ -6,15 +6,8 @@ class Struct(Node.Node): def __init__(self, stack=[], file="", lineNumber=0): name = "" Node.Node.__init__(self, 'struct', name, file, lineNumber) - # CPP section: - self.namespaces = [] - self.classes = [] - # C section: - self.structs = [] - self.variables = [] - self.methodes = [] - self.unions = [] - self.types = [] + self.access = "public" + def to_str(self) : return "struct " + self.name + " { ... };" diff --git a/cppParser/Type.py b/cppParser/Type.py index f197bc2..d159f03 100644 --- a/cppParser/Type.py +++ b/cppParser/Type.py @@ -5,12 +5,14 @@ import Node class Type(): def __init__(self, stack=[]): - if len(stack) == 0: - debug.error("Can not parse Type : " + str(stack)) self.name = "" self.const = False # the const xxxxx self.reference = False self.constVar = False # the char* const VarName + + if len(stack) == 0: + # None type + return if len(stack) == 1: self.name = stack[0] return; @@ -46,3 +48,7 @@ class TypeVoid(Type): def __init__(self): Type.__init__(self, ['void']) +class TypeNone(Type): + def __init__(self): + Type.__init__(self) + diff --git a/cppParser/Variable.py b/cppParser/Variable.py index ce47959..73afa62 100644 --- a/cppParser/Variable.py +++ b/cppParser/Variable.py @@ -5,8 +5,24 @@ import Node class Variable(Node.Node): def __init__(self, stack=[], file="", lineNumber=0): + name = "" + # TODO : better manageement for xxx[**][**] element: + res = [] + for element in stack: + if element == '[': + break + else: + res.append(element) + stack = res + if len(stack) < 2: - debug.error("Can not parse variable : " + str(stack)) + if stack[0] == 'void': + pass + else: + debug.error("Can not parse variable : " + str(stack)) + else: + name = stack[len(stack)-1] + Node.Node.__init__(self, 'variable', stack[len(stack)-1], file, lineNumber) # force the sublist error generation ... self.subList = None @@ -15,6 +31,9 @@ class Variable(Node.Node): self.static = False self.external = False self.volatile = False + #empty name ... ==> this is really bad ... + if name == "": + return if 'static' in stack: self.static = True diff --git a/cppParser/parserCpp.py b/cppParser/parserCpp.py deleted file mode 100644 index 3e0c4da..0000000 --- a/cppParser/parserCpp.py +++ /dev/null @@ -1,2411 +0,0 @@ -#!/usr/bin/python -# -# Author: Jashua R. Cloutier (contact via https://bitbucket.org/senex) -# Project: http://senexcanis.com/open-source/cppheaderparser/ -# -# Copyright (C) 2011, 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. Stories, -# blog entries etc making reference to this project may mention the -# name Jashua R. Cloutier in terms of project originator/creator etc. -# -# 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 -""" -try : - # normal install module - import ply.lex as lex -except ImportError : - # local module - import 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 = __version__ = "2.4" - -tokens = [ - 'NUMBER', - 'NAME', - 'OPEN_PAREN', - 'CLOSE_PAREN', - 'OPEN_BRACE', - 'CLOSE_BRACE', - 'OPEN_SQUARE_BRACKET', - 'CLOSE_SQUARE_BRACKET', - 'COLON', - 'SEMI_COLON', - 'COMMA', - 'TAB', - 'BACKSLASH', - 'PIPE', - 'PERCENT', - 'EXCLAMATION', - 'CARET', - 'COMMENT_SINGLELINE', - 'COMMENT_MULTILINE', - 'PRECOMP_MACRO', - 'PRECOMP_MACRO_CONT', - 'ASTERISK', - 'AMPERSTAND', - 'EQUALS', - 'MINUS', - 'PLUS', - 'DIVIDE', - 'CHAR_LITERAL', - 'STRING_LITERAL', - 'NEW_LINE', - 'SQUOTE', -] - -t_ignore = " \r.?@\f" -t_NUMBER = r'[0-9][0-9XxA-Fa-f]*' -t_NAME = r'[<>A-Za-z_~][A-Za-z0-9_]*' -t_OPEN_PAREN = r'\(' -t_CLOSE_PAREN = r'\)' -t_OPEN_BRACE = r'{' -t_CLOSE_BRACE = r'}' -t_OPEN_SQUARE_BRACKET = r'\[' -t_CLOSE_SQUARE_BRACKET = r'\]' -t_SEMI_COLON = r';' -t_COLON = r':' -t_COMMA = r',' -t_TAB = r'\t' -t_BACKSLASH = r'\\' -t_PIPE = r'\|' -t_PERCENT = r'%' -t_CARET = r'\^' -t_EXCLAMATION = 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.lexer.lineno += len(filter(lambda a: a=="\n", t.value)) -t_ASTERISK = r'\*' -t_MINUS = r'\-' -t_PLUS = r'\+' -t_DIVIDE = r'/(?!/)' -t_AMPERSTAND = r'&' -t_EQUALS = r'=' -t_CHAR_LITERAL = "'.'" -t_SQUOTE = "'" -#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 - t.lexer.lineno += len(filter(lambda a: a=="\n", t.value)) -def t_NEWLINE(t): - r'\n+' - t.lexer.lineno += len(t.value) - -def t_error(v): - print( "Lex error: ", v ) - -lex.lex() -# Controls error_print -print_errors = 1 -# Controls warning_print -print_warnings = 1 -# Controls debug_print -debug = 0 -# Controls trace_print -debug_trace = 0 - -def error_print(arg): - if print_errors: print("[%4d] %s"%(inspect.currentframe().f_back.f_lineno, arg)) - -def warning_print(arg): - if print_warnings: print("[%4d] %s"%(inspect.currentframe().f_back.f_lineno, arg)) - -def debug_print(arg): - global debug - if debug: print("[%4d] %s"%(inspect.currentframe().f_back.f_lineno, arg)) - -def trace_print(*arg): - global debug_trace - if debug_trace: - sys.stdout.write("[%s] "%(inspect.currentframe().f_back.f_lineno)) - for a in arg: sys.stdout.write("%s "%a) - sys.stdout.write("\n") - -supportedAccessSpecifier = [ - 'public', - 'protected', - 'private' -] - -#Symbols to ignore, usually special macros -ignoreSymbols = [ - 'Q_OBJECT', -] - -doxygenCommentCache = "" - -#Track what was added in what order and at what depth -parseHistory = [] - -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 - -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_function_pointer_stack(stack): - """Count how many non-nested paranthesis are in the stack. Useful for determining if a stack is a function pointer""" - paren_depth = 0 - paren_count = 0 - star_after_first_paren = False - last_e = None - for e in stack: - if e == "(": - paren_depth += 1 - elif e == ")" and paren_depth > 0: - paren_depth -= 1 - if paren_depth == 0: - paren_count += 1 - elif e == "*" and last_e == "(" and paren_count == 0 and paren_depth == 1: - star_after_first_paren = True - last_e = e - - if star_after_first_paren and paren_count == 2: - return True - else: - return False - -def is_method_namestack(stack): - r = False - if '(' not in stack: r = False - elif stack[0] == 'typedef': r = False # TODO deal with typedef function prototypes - #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] == ';': - if is_function_pointer_stack(stack): - r = False - else: - r = True - elif '{' in stack: r = True # ideally we catch both braces... TODO - else: r = False - #Test for case of property set to something with parens such as "static const int CONST_A = (1 << 7) - 1;" - if r and "(" in stack and "=" in stack and 'operator' not in stack: - if stack.index("=") < stack.index("("): r = False - return r - -def is_property_namestack(nameStack): - r = False - if '(' not in nameStack and ')' not in nameStack: r = True - elif "(" in nameStack and "=" in nameStack and nameStack.index("=") < nameStack.index("("): r = True - #See if we are a function pointer - if not r and is_function_pointer_stack(nameStack): r = True - return r - -def detect_lineno(s): - """Detect the line number for a given token string""" - try: - rtn = s.lineno() - if rtn != -1: - return rtn - except: pass - global curLine - return curLine - -def filter_out_attribute_keyword(stack): - """Strips __attribute__ and its parenthetical expression from the stack""" - if "__attribute__" not in stack: return stack - try: - debug_print("Stripping __attribute__ from %s"% stack) - attr_index = stack.index("__attribute__") - attr_end = attr_index + 1 #Assuming not followed by parenthetical expression which wont happen - #Find final paren - if stack[attr_index + 1] == '(': - paren_count = 1 - for i in xrange(attr_index + 2, len(stack)): - elm = stack[i] - if elm == '(': - paren_count += 1 - elif elm == ')': - paren_count -= 1 - if paren_count == 0: - attr_end = i + 1 - break - new_stack = stack[0:attr_index] + stack[attr_end:] - debug_print("stripped stack is %s"% new_stack) - return new_stack - except: - return stack - - -class TagStr(str): - """Wrapper for a string that allows us to store the line number associated with it""" - lineno_reg = {} - def __new__(cls,*args,**kw): - new_obj = str.__new__(cls,*args) - if "lineno" in kw: - TagStr.lineno_reg[id(new_obj)] = kw["lineno"] - return new_obj - - def __del__(self): - try: - del TagStr.lineno_reg[id(self)] - except: pass - - def lineno(self): - return TagStr.lineno_reg.get(id(self), -1) - -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 - 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 = - { - 'name': "" - 'inherits':[] - 'methods': - { - 'public':[], - 'protected':[], - 'private':[] - }, - 'properties': - { - 'public':[], - 'protected':[], - 'private':[] - }, - 'enums': - { - 'public':[], - 'protected':[], - 'private':[] - } - } - """ - - def get_all_methods(self): - r = [] - for typ in supportedAccessSpecifier: r += self['methods'][typ] - return r - - def get_all_method_names( self ): - r = [] - for typ in supportedAccessSpecifier: r += self.get_method_names(typ) # returns list - return r - - def get_all_pure_virtual_methods( self ): - r = {} - for typ in supportedAccessSpecifier: 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): - self['nested_classes'] = [] - self['parent'] = None - self['abstract'] = False - self._public_enums = {} - self._public_structs = {} - self._public_typedefs = {} - self._public_forward_declares = [] - self['namespace'] = "" - - debug_print( "Class: %s"%nameStack ) - if (len(nameStack) < 2): - nameStack.insert(1, "")#anonymous struct - global doxygenCommentCache - if len(doxygenCommentCache): - self["doxygen"] = doxygenCommentCache - doxygenCommentCache = "" - - if "::" in "".join(nameStack): - #Re-Join class paths (ex ['class', 'Bar', ':', ':', 'Foo'] -> ['class', 'Bar::Foo'] - try: - new_nameStack = [] - for name in nameStack: - if len(new_nameStack) == 0: - new_nameStack.append(name) - elif name == ":" and new_nameStack[-1].endswith(":"): - new_nameStack[-1] += name - elif new_nameStack[-1].endswith("::"): - new_nameStack[-2] += new_nameStack[-1] + name - del new_nameStack[-1] - else: - new_nameStack.append(name) - trace_print("Convert from namestack\n %s\nto\n%s"%(nameStack, new_nameStack)) - nameStack = new_nameStack - except: pass - - self["name"] = nameStack[1] - self["line_number"] = detect_lineno(nameStack[0]) - - #Handle template classes - if len(nameStack) > 3 and nameStack[2].startswith("<"): - open_template_count = 0 - param_separator = 0 - found_first = False - i = 0 - for elm in nameStack: - if '<' in elm : - open_template_count += 1 - found_first = True - elif '>' in elm: - open_template_count -= 1 - if found_first and open_template_count == 0: - self["name"] = "".join(nameStack[1:i + 1]) - break; - i += 1 - elif ":" in nameStack: - self['name'] = nameStack[ nameStack.index(':') - 1 ] - - inheritList = [] - - if nameStack.count(':') == 1: - nameStack = nameStack[nameStack.index(":") + 1:] - while len(nameStack): - tmpStack = [] - tmpInheritClass = {"access":"private", "virtual": False} - if "," in nameStack: - tmpStack = nameStack[:nameStack.index(",")] - nameStack = nameStack[nameStack.index(",") + 1:] - else: - tmpStack = nameStack - nameStack = [] - - # Convert template classes to one name in the last index - for i in range(0, len(tmpStack)): - if '<' in tmpStack[i]: - tmpStack2 = tmpStack[:i-1] - tmpStack2.append("".join(tmpStack[i-1:])) - tmpStack = tmpStack2 - break - 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] - elif len(tmpStack) == 3 and "virtual" in tmpStack: - tmpInheritClass["access"] = tmpStack[1] if tmpStack[1] != "virtual" else tmpStack[0] - tmpInheritClass["class"] = tmpStack[2] - tmpInheritClass["virtual"] = True - else: - warning_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 and nameStack[0] in ("class", "struct"): - tmpStack = nameStack[nameStack.index(":") + 1:] - - superTmpStack = [[]] - for tok in tmpStack: - if tok == ',': - superTmpStack.append([]) - else: - superTmpStack[-1].append(tok) - - for tmpStack in superTmpStack: - tmpInheritClass = {"access":"private"} - - if len(tmpStack) and tmpStack[0] in supportedAccessSpecifier: - tmpInheritClass["access"] = tmpStack[0] - tmpStack = tmpStack[1:] - - inheritNSStack = [] - while len(tmpStack) > 3: - if tmpStack[0] == ':': break; - if tmpStack[1] != ':': break; - if tmpStack[2] != ':': break; - inheritNSStack.append(tmpStack[0]) - tmpStack = tmpStack[3:] - if len(tmpStack) == 1 and tmpStack[0] != ':': - inheritNSStack.append(tmpStack[0]) - tmpInheritClass["class"] = "::".join(inheritNSStack) - inheritList.append(tmpInheritClass) - - self['inherits'] = inheritList - - methodAccessSpecificList = {} - propertyAccessSpecificList = {} - enumAccessSpecificList = {} - structAccessSpecificList = {} - typedefAccessSpecificList = {} - forwardAccessSpecificList = {} - - for accessSpecifier in supportedAccessSpecifier: - methodAccessSpecificList[accessSpecifier] = [] - propertyAccessSpecificList[accessSpecifier] = [] - enumAccessSpecificList[accessSpecifier] = [] - structAccessSpecificList[accessSpecifier] = [] - typedefAccessSpecificList[accessSpecifier] = [] - forwardAccessSpecificList[accessSpecifier] = [] - - self['methods'] = methodAccessSpecificList - self['properties'] = propertyAccessSpecificList - self['enums'] = enumAccessSpecificList - self['structs'] = structAccessSpecificList - self['typedefs'] = typedefAccessSpecificList - self['forward_declares'] = forwardAccessSpecificList - - - def show(self): - """Convert class to a string""" - namespace_prefix = "" - if self["namespace"]: namespace_prefix = self["namespace"] + "::" - rtn = "%s %s"%(self["declaration_method"], 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: " - for inheritClass in self["inherits"]: - if inheritClass["virtual"]: rtn += "virtual " - 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 += " \n" - for enum in self["enums"][accessSpecifier]: - rtn += " %s\n"%(repr(enum)) - #Properties - if (len(self["properties"][accessSpecifier])): - rtn += " \n" - for property in self["properties"][accessSpecifier]: - rtn += " %s\n"%(repr(property)) - #Methods - if (len(self["methods"][accessSpecifier])): - rtn += " \n" - for method in self["methods"][accessSpecifier]: - rtn += "\t\t" + method.show() + '\n' - rtn += " }\n" - print rtn - - def __repr__(self): - """Convert class to a string""" - namespace_prefix = "" - if self["namespace"]: namespace_prefix = self["namespace"] + "::" - rtn = "%s %s"%(self["declaration_method"], 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() and len(self["inherits"]): - rtn += "Inherits: " - for inheritClass in self["inherits"]: - if inheritClass.get("virtual", False): rtn += "virtual " - 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 += " // Methods\n" - for method in self["methods"][accessSpecifier]: - rtn += " %s\n"%(repr(method)) - rtn += "}\n" - return rtn - - -class CppUnion( CppClass ): - """Takes a name stack and turns it into a union - - Contains the following Keys: - self['name'] - Name of the union - self['doxygen'] - Doxygen comments associated with the union if they exist - self['members'] - List of members the union has - - An example of how this could look is as follows: - #self = - { - 'name': "" - 'members': [] - } - """ - - def __init__(self, nameStack): - CppClass.__init__(self, nameStack) - self["name"] = "union " + self["name"] - self["members"] = self["properties"]["public"] - - def transform_to_union_keys(self): - print "union keys: %s"%self.keys() - for key in ['inherits', 'parent', 'abstract', 'namespace', 'typedefs', 'methods']: - del self[key] - - def show(self): - """Convert class to a string""" - print self - - - def __repr__(self): - """Convert class to a string""" - namespace_prefix = "" - if self["namespace"]: namespace_prefix = self["namespace"] + "::" - rtn = "%s %s"%(self["declaration_method"], 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' - - rtn += "{\n" - for member in self["members"]: - rtn += " %s\n"%(repr(member)) - rtn += "}\n" - return rtn - - - -class _CppMethod( dict ): - def _params_helper1( self, stack ): - # 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 - - # 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: - 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( _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['name'] - Name of the method (ex. "getSize") - self['doxygen'] - Doxygen comments associated with the method if they exist - self['parameters'] - List of CppVariables - """ - 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): - debug_print( "Method: %s"%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 self["rtnType"].startswith("virtual"): - self["rtnType"] = self["rtnType"][len("virtual"):].strip() - if len(self["rtnType"]) == 0 or self["name"] == curClass: - self["rtnType"] = "void" - - self["rtnType"] = self["rtnType"].replace(' : : ', '::' ) - self["rtnType"] = self["rtnType"].replace(" <","<") - self["rtnType"] = self["rtnType"].replace(" >",">").replace(">>", "> >").replace(">>", "> >") - self["rtnType"] = self["rtnType"].replace(" ,",",") - - self["const"] = False - for i in reversed(nameStack): - if i == "const": - self["const"] = True - break - elif i == ")": - break - - self.update( methinfo ) - self["line_number"] = detect_lineno(nameStack[0]) - - #Filter out initializer lists used in constructors - try: - paren_depth_counter = 0 - for i in range(0, len(nameStack)): - elm = nameStack[i] - if elm == "(": - paren_depth_counter += 1 - if elm == ")": - paren_depth_counter -=1 - if paren_depth_counter == 0 and nameStack[i+1] == ':': - debug_print("Stripping out initializer list") - nameStack = nameStack[:i+1] - break - except: pass - - paramsStack = self._params_helper1( nameStack ) - - - params = [] - #See if there is a doxygen comment for the variable - doxyVarDesc = {} - - if self.has_key("doxygen"): - 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)): - # Find commas that are not nexted in <>'s like template types - open_template_count = 0 - param_separator = 0 - i = 0 - for elm in paramsStack: - if '<' in elm : - open_template_count += 1 - elif '>' in elm: - open_template_count -= 1 - elif elm == ',' and open_template_count == 0: - param_separator = i - break - i += 1 - - if param_separator: - param = CppVariable(paramsStack[0:param_separator], doxyVarDesc=doxyVarDesc) - if len(param.keys()): params.append(param) - paramsStack = paramsStack[param_separator + 1:] - else: - param = CppVariable(paramsStack, doxyVarDesc=doxyVarDesc) - if len(param.keys()): params.append(param) - break - - - self["parameters"] = params - self._params_helper2( params ) # mods params inplace - - def __repr__(self): - filter_keys = ("parent", "defined", "operator", "returns_reference") - cpy = dict((k,v) for (k,v) in self.items() if k not in filter_keys) - return "%s"%cpy - - -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] - - 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( _CppVariable ): - """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 - """ - Vars = [] - def __init__(self, nameStack, **kwargs): - _stack_ = nameStack - if "[" in nameStack: #strip off array informatin - arrayStack = nameStack[nameStack.index("["):] - if len(arrayStack) == 3: - self["array_size"] = arrayStack[1] - nameStack = nameStack[:nameStack.index("[")] - self["array"] = 1 - else: - self["array"] = 0 - nameStack = self._name_stack_helper( nameStack ) - global doxygenCommentCache - if len(doxygenCommentCache): - self["doxygen"] = doxygenCommentCache - doxygenCommentCache = "" - - debug_print( "Variable: %s"%nameStack ) - - self["line_number"] = detect_lineno(nameStack[0]) - self["function_pointer"] = 0 - - if (len(nameStack) < 2): # +++ - if len(nameStack) == 1: self['type'] = nameStack[0]; self['name'] = '' - else: error_print(_stack_); assert 0 - - elif is_function_pointer_stack(nameStack): #function pointer - self["type"] = " ".join(nameStack[:nameStack.index("(") + 2] + nameStack[nameStack.index(")") :]) - self["name"] = " ".join(nameStack[nameStack.index("(") + 2 : nameStack.index(")")]) - self["function_pointer"] = 1 - - 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:]) - - elif is_fundamental(nameStack[-1]) or nameStack[-1] in ['>', '<' , ':', '.']: - #Un named parameter - 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(" <","<") - self["type"] = self["type"].replace(" >",">").replace(">>", "> >").replace(">>", "> >") - self["type"] = self["type"].replace(" ,",",") - #Optional doxygen description - try: - self["desc"] = kwargs["doxyVarDesc"][self["name"]] - except: pass - - self.init() - CppVariable.Vars.append( self ) # save and resolve later - - def __repr__(self): - keys_white_list = ['constant','name','reference','type','static','pointer','desc', 'line_number'] - cpy = dict((k,v) for (k,v) in self.items() if k in keys_white_list) - if self.has_key("array_size"): cpy["array_size"] = self["array_size"] - return "%s"%cpy - -class _CppEnum(dict): - 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() - # Remove single quotes from single quoted chars (unless part of some expression - if len(a) == 3 and a[0] == "'" and a[2] == "'": - a = v['value'] = a[1] - 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 = str # only if there are quotes it this a string enum - else: - try: - a = i = ord(a) - except: pass - #Allow access of what is in the file pre-convert if converted - if v['value'] != str(a): - v['raw_value'] = v['value'] - v['value'] = a - else: v['value'] = i - try: - v['value'] = v['value'].replace(" < < ", " << ").replace(" >> ", " >> ") - except: pass - i += 1 - return t - -class CppEnum(_CppEnum): - """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): - global doxygenCommentCache - if len(doxygenCommentCache): - self["doxygen"] = doxygenCommentCache - doxygenCommentCache = "" - if len(nameStack) == 3 and nameStack[0] == "enum": - debug_print("Created enum as just name/value") - self["name"] = nameStack[1] - self["instances"]=[nameStack[2]] - if len(nameStack) < 4 or "{" not in nameStack or "}" not in nameStack: - #Not enough stuff for an enum - debug_print("Bad enum") - return - valueList = [] - self["line_number"] = detect_lineno(nameStack[0]) - #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 = [] - d = {} - if len(tmpStack) == 1: d["name"] = tmpStack[0] - elif len(tmpStack) >= 3 and tmpStack[1] == "=": - d["name"] = tmpStack[0]; d["value"] = " ".join(tmpStack[2:]) - elif len(tmpStack) == 2 and tmpStack[1] == "=": - 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: - warning_print( 'WARN-enum: empty enum %s'%nameStack ) - 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) - else: warning_print( 'WARN-enum: nameless enum %s'%nameStack ) - #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 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 ) - global curLine - self["line_number"] = curLine - -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 - - 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 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 and 'parent' in var['method'].keys(): - 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: - trace_print( 'STRUCT', var ) - var['struct'] = tag - var['ctypes_type'] = 'ctypes.c_void_p' - var['raw_type'] = self.structs[tag]['namespace'] + '::' + tag - - elif tag in self._forward_decls: - var['forward_declared'] = tag - var['ctypes_type'] = 'ctypes.c_void_p' - - elif tag in self.global_enums: - enum = self.global_enums[ tag ] - if enum['type'] 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']: - warning_print( 'WARN unresolved %s'%_tag) - var['ctypes_type'] = 'ctypes.c_void_p' - var['unresolved'] = True - - - elif tag.count('::')==1: - trace_print( 'trying to find nested something in', tag ) - a = tag.split('::')[0] - b = tag.split('::')[-1] - if a in self.classes: # a::b is most likely something nested in a class - klass = self.classes[ a ] - if b in klass._public_enums: - trace_print( '...found nested enum', b ) - enum = klass._public_enums[ b ] - if enum['type'] 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*' - try: - if 'method' in var: var['enum'] = var['method']['path'] + '::' + enum['name'] - else: # class property - var['unresolved'] = True - except: - 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 ] - trace_print(enum) - trace_print(var) - assert 0 - - elif b in self.global_enums: # falling back, this is a big ugly - enum = self.global_enums[ b ] - assert a in enum['namespace'].split('::') - if enum['type'] 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 - trace_print('NAMESPACES', self.namespaces) - trace_print( a, b ) - trace_print( '---- boost gets crazy ----' ) - var['ctypes_type'] = 'ctypes.c_void_p' - var['unresolved'] = True - - - elif 'namespace' in var and self.concrete_typedef(var['namespace']+tag): - #print( 'TRYING WITH NS', var['namespace'] ) - con = self.concrete_typedef( var['namespace']+tag ) - if con: - var['typedef'] = var['namespace']+tag - var['type'] = con - if 'struct' in con.split(): - var['raw_type'] = var['typedef'] - var['ctypes_type'] = 'ctypes.c_void_p' - else: - self.resolve_type( var['type'], var ) - var['ctypes_type'] = self.guess_ctypes_type( var['type'] ) - - elif '::' in var: - var['ctypes_type'] = 'ctypes.c_void_p' - var['unresolved'] = True - - elif tag in self.SubTypedefs: # TODO remove SubTypedefs - if 'property_of_class' in var or 'property_of_struct' in var: - trace_print( 'class:', self.SubTypedefs[ tag ], 'tag:', tag ) - var['typedef'] = self.SubTypedefs[ tag ] # class name - var['ctypes_type'] = 'ctypes.c_void_p' - else: - trace_print( "WARN-this should almost never happen!" ) - trace_print( var ); trace_print('-'*80) - var['unresolved'] = True - - elif tag in self._template_typenames: - var['typename'] = tag - var['ctypes_type'] = 'ctypes.c_void_p' - var['unresolved'] = True # TODO, how to deal with templates? - - elif tag.startswith('_'): # assume starting with underscore is not important for wrapping - warning_print( 'WARN unresolved %s'%_tag) - var['ctypes_type'] = 'ctypes.c_void_p' - var['unresolved'] = True - - else: - trace_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 mutable'.split() # +++ new July7: "mutable" - for var in CppVariable.Vars: - if 'raw_type' not in var: - raw = [] - for x in var['type'].split(): - if x not in p: raw.append( x ) - var['raw_type'] = ' '.join( raw ) - - #if '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: trace_print('-'*80); trace_print(var); raise NotImplemented - - - ## need full name space for classes in raw type ## - if var['raw_type'].startswith( '::' ): - #print(var) - #print('NAMESPACE', var['class']['namespace']) - #print( 'PARENT NS', var['class']['parent']['namespace'] ) - #assert 0 - var['unresolved'] = True - if 'method' in var: var['method']['unresolved_parameters'] = True - #var['raw_type'] = var['raw_type'][2:] - - # Take care of #defines and #pragmas etc - trace_print("Processing precomp_macro_buf: %s"%self._precomp_macro_buf) - for m in self._precomp_macro_buf: - macro = m.replace("\\n", "\n") - try: - if macro.lower().startswith("#define"): - trace_print("Adding #define %s"%macro) - self.defines.append(macro.split(" ", 1)[1].strip()) - elif macro.lower().startswith("#pragma"): - trace_print("Adding #pragma %s"%macro) - self.pragmas.append(macro.split(" ", 1)[1].strip()) - elif macro.lower().startswith("#include"): - trace_print("Adding #include %s"%macro) - self.includes.append(macro.split(" ", 1)[1].strip()) - else: - debug_print("Cant detect what to do with precomp macro '%s'"%macro) - except: pass - self._precomp_macro_buf = None - - - 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: - trace_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: - trace_print( meth ) - a,b = meth['returns'].split('::') - if a in self.namespaces: - if b in self.classes: - klass = self.classes[ b ] - meth['returns_class'] = a + '::' + b - elif '<' in b and '>' in b: - warning_print( 'WARN-can not return template: %s'%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: trace_print( a, b); trace_print( meth); meth['returns_unknown'] = True # +++ - - elif a in self.classes: - klass = self.classes[ a ] - if b in klass._public_enums: - trace_print( '...found nested enum', b ) - enum = klass._public_enums[ b ] - meth['returns_enum'] = enum['type'] - meth['returns_fundamental'] = True - if enum['type'] == int: meth['returns'] = 'int' - else: meth['returns'] = 'char*' - - elif b in klass._public_forward_declares: - meth['returns_class'] = True - - elif b in klass._public_typedefs: - typedef = klass._public_typedefs[ b ] - meth['returns_fundamental'] = is_fundamental( typedef ) - - else: - trace_print( meth ) # should be a nested class, TODO fix me. - meth['returns_unknown'] = True - - elif '::' in meth['returns']: - trace_print('TODO namespace or extra nested return:', meth) - meth['returns_unknown'] = True - else: - trace_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' - trace_print( 'PARENT CLASS:', c ) - if c not in self.classes: trace_print('WARN: parent class not found') - if c in self.classes and self.classes[c]['abstract']: - p = self.classes[ c ] - for meth in p.get_all_methods(): #p["methods"]["public"]: - trace_print( '\t\tmeth', meth['name'], 'pure virtual', meth['pure_virtual'] ) - if meth['pure_virtual'] and meth['name'] not in methnames: cls['abstract'] = True; break - - - - - - 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 - - - def parse_method_type( self, stack ): - trace_print( 'meth type info', stack ) - if stack[0] in ':;': stack = stack[1:] - info = { - 'debug': ' '.join(stack).replace(' : : ', '::' ).replace(' < ', '<' ).replace(' > ', '> ' ).replace(" >",">").replace(">>", "> >").replace(">>", "> >"), - '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 + 1 - trace_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: - if " ".join(['operator', '(', ')', '(']) in " ".join(stack): - op = "()" - else: - trace_print( 'Error parsing operator') - return None - - info['operator'] = op - name = 'operator' + op - a = stack[ : stack.index('operator') ] - - elif r: - 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:] - - - 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 - if self.classes.has_key(klass) and not self.curClass: - #Class function defined outside the class - return None - # info['name'] = name - #else: info['name'] = name - - if name.startswith('~'): - info['destructor'] = True - name = name[1:] - elif not a or (name == self.curClass and len(self.curClass)): - 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 '' - 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: - trace_print( 'WARN - struct contains methods - skipping' ) - trace_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: #non class functions - debug_print("FREE FUNCTION") - newMethod = CppMethod(self.nameStack, None, info) - self.functions.append(newMethod) - global parseHistory - parseHistory.append({"braceDepth": self.braceDepth, "item_type": "method", "item": newMethod}) - else: - trace_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 - 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""" - global parseHistory - 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: - if len(self.nameStack) == 1: - #See if we can de anonymize the type - filteredParseHistory = [h for h in parseHistory if h["braceDepth"] == self.braceDepth] - if len(filteredParseHistory) and filteredParseHistory[-1]["item_type"] == "class": - self.nameStack.insert(0, filteredParseHistory[-1]["item"]["name"]) - debug_print("DEANONYMOIZING %s to type '%s'"%(self.nameStack[1], self.nameStack[0])) - if "," in self.nameStack: #Maybe we have a variable list - #Figure out what part is the variable separator but remember templates of function pointer - #First find left most comma outside of a > and ) - leftMostComma = 0; - for i in xrange(0, len(self.nameStack)): - name = self.nameStack[i] - if name in (">", ")"): leftMostComma = 0 - if leftMostComma == 0 and name == ",": leftMostComma = i - # Is it really a list of variables? - if leftMostComma != 0: - trace_print("Multiple variables for namestack in %s. Separating processing"%self.nameStack) - orig_nameStack = self.nameStack[:] - orig_stack = self.stack[:] - - type_nameStack = orig_nameStack[:leftMostComma-1] - for name in orig_nameStack[leftMostComma - 1::2]: - self.nameStack = type_nameStack + [name] - self.stack = orig_stack[:] # Not maintained for mucking, but this path it doesnt matter - self.evaluate_property_stack() - return - - 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'] - parseHistory.append({"braceDepth": self.braceDepth, "item_type": "variable", "item": newVar}) - - 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: - trace_print( 'HIT NESTED SUBCLASS' ) - self.accessSpecifierStack.append(self.curAccessSpecifier) - elif self.braceDepth != len(self.nameSpaces): - error_print( 'ERROR: WRONG BRACE DEPTH' ) - return - - if self.nameStack[0] == "class": - self.curAccessSpecifier = 'private' - else:#struct - self.curAccessSpecifier = 'public' - debug_print("curAccessSpecifier changed/defaulted to %s"%self.curAccessSpecifier) - if self.nameStack[0] == "union": - newClass = CppUnion(self.nameStack) - self.anon_union_counter = [self.braceDepth, 2] - trace_print( 'NEW UNION', newClass['name'] ) - else: - newClass = CppClass(self.nameStack) - trace_print( 'NEW CLASS', newClass['name'] ) - newClass["declaration_method"] = self.nameStack[0] - 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 not key.endswith("::") and not key.endswith(" ") and len(key) != 0: - if key in self.classes: - trace_print( 'ERROR name collision:', key ) - self.classes[key].show() - trace_print('-'*80) - newClass.show() - assert key not in self.classes # namespace collision - self.classes[ key ] = newClass - global parseHistory - parseHistory.append({"braceDepth": self.braceDepth, "item_type": "class", "item": newClass}) - - - def evalute_forward_decl(self): - trace_print( 'FORWARD DECL', self.nameStack ) - assert self.nameStack[0] in ('class', 'struct') - 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 - """ - IGNORE_NAMES = '__extension__'.split() - - def show(self): - for className in self.classes.keys():self.classes[className].show() - - def __init__(self, headerFileName, argType="file", **kwargs): - """Create the parsed C++ header file parse tree - - headerFileName - Name of the file to parse OR actual file contents (depends on argType) - argType - Indicates how to interpret headerFileName as a file string or file name - kwargs - Supports the following keywords - """ - ## 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 = "" - elif argType == "string": - self.headerFileName = "" - self.mainClass = "???" - headerFileStr = headerFileName - else: - raise Exception("Arg type must be either file or string") - self.curClass = "" - - # 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 - #Functions that are not part of a class - self.functions = [] - - self.pragmas = [] - self.defines = [] - self.includes = [] - self._precomp_macro_buf = [] #for internal purposes, will end up filling out pragmras and defines at the end - - self.enums = [] - self.global_enums = {} - self.nameStack = [] - self.nameSpaces = [] - self.curAccessSpecifier = 'private' # private is default - self.accessSpecifierStack = [] - self.accessSpecifierScratch = [] - debug_print("curAccessSpecifier changed/defaulted to %s"%self.curAccessSpecifier) - self.initextra() - - self.anon_union_counter = [-1, 0] - - if (len(self.headerFileName)): - fd = open(self.headerFileName) - headerFileStr = "".join(fd.readlines()) - fd.close() - - # Make sure supportedAccessSpecifier are sane - for i in range(0, len(supportedAccessSpecifier)): - if " " not in supportedAccessSpecifier[i]: continue - supportedAccessSpecifier[i] = re.sub("[ ]+", " ", supportedAccessSpecifier[i]).strip() - - # Strip out template declarations - headerFileStr = re.sub("template[\t ]*<[^>]*>", "", headerFileStr) - - # Change multi line #defines and expressions to single lines maintaining line nubmers - # Based from http://stackoverflow.com/questions/2424458/regular-expression-to-match-cs-multiline-preprocessor-statements - matches = re.findall(r'(?m)^(?:.*\\\r?\n)+.*$', headerFileStr) - is_define = re.compile(r'[ \t\v]*#[Dd][Ee][Ff][Ii][Nn][Ee]') - for m in matches: - #Keep the newlines so that linecount doesnt break - num_newlines = len(filter(lambda a: a=="\n", m)) - if is_define.match(m): - new_m = m.replace("\n", "\\n") - else: - # Just expression taking up multiple lines, make it take 1 line for easier parsing - new_m = m.replace("\\\n", " ") - if (num_newlines > 0): - new_m += "\n"*(num_newlines) - headerFileStr = headerFileStr.replace(m, new_m) - - #Filter out Extern "C" statements. These are order dependent - matches = re.findall(re.compile(r'extern[\t ]+"[Cc]"[\t \n\r]*{', re.DOTALL), headerFileStr) - for m in matches: - #Keep the newlines so that linecount doesnt break - num_newlines = len(filter(lambda a: a=="\n", m)) - headerFileStr = headerFileStr.replace(m, "\n" * num_newlines) - headerFileStr = re.sub(r'extern[ ]+"[Cc]"[ ]*', "", headerFileStr) - - #Filter out any ignore symbols that end with "()" to account for #define magic functions - for ignore in ignoreSymbols: - if not ignore.endswith("()"): continue - while True: - locStart = headerFileStr.find(ignore[:-1]) - if locStart == -1: - break; - locEnd = None - #Now walk till we find the last paren and account for sub parens - parenCount = 1 - inQuotes = False - for i in xrange(locStart + len(ignore) - 1, len(headerFileStr)): - c = headerFileStr[i] - if not inQuotes: - if c == "(": - parenCount += 1 - elif c == ")": - parenCount -= 1 - elif c == '"': - inQuotes = True - if parenCount == 0: - locEnd = i + 1 - break; - else: - if c == '"' and headerFileStr[i-1] != '\\': - inQuotes = False - - if locEnd: - #Strip it out but keep the linecount the same so line numbers are right - match_str = headerFileStr[locStart:locEnd] - debug_print("Striping out '%s'"%match_str) - num_newlines = len(filter(lambda a: a=="\n", match_str)) - headerFileStr = headerFileStr.replace(headerFileStr[locStart:locEnd], "\n"*num_newlines) - - self.braceDepth = 0 - lex.lex() - lex.input(headerFileStr) - global curLine - global curChar - curLine = 0 - curChar = 0 - try: - while True: - tok = lex.token() - if not tok: break - if self.anon_union_counter[0] == self.braceDepth and self.anon_union_counter[1]: - self.anon_union_counter[1] -= 1 - tok.value = TagStr(tok.value, lineno=tok.lineno) - #debug_print("TOK: %s"%tok) - if tok.type == 'NAME' and tok.value in self.IGNORE_NAMES: continue - self.stack.append( tok.value ) - curLine = tok.lineno - curChar = tok.lexpos - if (tok.type in ('PRECOMP_MACRO', 'PRECOMP_MACRO_CONT')): - debug_print("PRECOMP: %s"%tok) - self._precomp_macro_buf.append(tok.value) - self.stack = [] - self.nameStack = [] - continue - if (tok.type == 'OPEN_BRACE'): - if len(self.nameStack) >= 2 and is_namespace(self.nameStack): # namespace {} with no name used in boost, this sets default? - if self.nameStack[1] == "__IGNORED_NAMESPACE__CppHeaderParser__":#Used in filtering extern "C" - self.nameStack[1] = "" - self.nameSpaces.append(self.nameStack[1]) - ns = self.cur_namespace(); self.stack = [] - if ns not in self.namespaces: self.namespaces.append( ns ) - # Detect special condition of macro magic before class declaration so we - # can filter it out - if 'class' in self.nameStack and self.nameStack[0] != 'class': - classLocationNS = self.nameStack.index("class") - classLocationS = self.stack.index("class") - if "(" not in self.nameStack[classLocationNS:]: - debug_print("keyword 'class' found in unexpected location in nameStack, must be following #define magic. Process that before moving on") - origNameStack = self.nameStack - origStack = self.stack - #Process first part of stack which is probably #define macro magic and may cause issues - self.nameStack = self.nameStack[:classLocationNS] - self.stack = self.stack[:classLocationS] - try: - self.evaluate_stack() - except: - debug_print("Error processing #define magic... Oh well") - #Process rest of stack - self.nameStack = origNameStack[classLocationNS:] - self.stack = origStack[classLocationS:] - - - 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: - self.evaluate_stack() - else: - self.nameStack = [] - self.braceDepth -= 1 - #self.stack = []; print 'BRACE DEPTH', self.braceDepth, 'NS', len(self.nameSpaces) - if self.curClass: debug_print( 'CURBD %s'%self._classes_brace_level[ self.curClass ] ) - - if (self.braceDepth == 0) or (self.curClass and self._classes_brace_level[self.curClass]==self.braceDepth): - trace_print( 'END OF CLASS DEF' ) - if self.accessSpecifierStack: - self.curAccessSpecifier = self.accessSpecifierStack[-1] - self.accessSpecifierStack = self.accessSpecifierStack[:-1] - 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): - trace_print( 'END OF STRUCT DEF' ) - self.curStruct = None - - if self._method_body and (self.braceDepth + 1) <= self._method_body: - self._method_body = None; self.stack = []; self.nameStack = []; trace_print( 'FORCE CLEAR METHBODY' ) - - if (tok.type == 'OPEN_PAREN'): - self.nameStack.append(tok.value) - elif (tok.type == 'CLOSE_PAREN'): - self.nameStack.append(tok.value) - elif (tok.type == 'OPEN_SQUARE_BRACKET'): - self.nameStack.append(tok.value) - elif (tok.type == 'CLOSE_SQUARE_BRACKET'): - self.nameStack.append(tok.value) - elif (tok.type == 'TAB'): pass - elif (tok.type == 'EQUALS'): - self.nameStack.append(tok.value) - elif (tok.type == 'COMMA'): - self.nameStack.append(tok.value) - elif (tok.type == 'BACKSLASH'): - self.nameStack.append(tok.value) - elif (tok.type == 'PIPE'): - self.nameStack.append(tok.value) - elif (tok.type == 'PERCENT'): - self.nameStack.append(tok.value) - elif (tok.type == 'CARET'): - self.nameStack.append(tok.value) - elif (tok.type == 'EXCLAMATION'): - self.nameStack.append(tok.value) - elif (tok.type == 'SQUOTE'): pass - 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' or tok.type == 'CHAR_LITERAL'): - if tok.value in ignoreSymbols: - debug_print("Ignore symbol %s"%tok.value) - elif (tok.value == 'class'): - self.nameStack.append(tok.value) - elif tok.value in supportedAccessSpecifier: - if len(self.nameStack) and self.nameStack[0] in ("class", "struct", "union"): - self.nameStack.append(tok.value) - elif self.braceDepth == len(self.nameSpaces) + 1 or self.braceDepth == (len(self.nameSpaces) + len(self.curClass.split("::"))): - self.curAccessSpecifier = tok.value; - self.accessSpecifierScratch.append(tok.value) - debug_print("curAccessSpecifier updated to %s"%self.curAccessSpecifier) - self.stack = [] - else: - self.nameStack.append(tok.value) - if self.anon_union_counter[0] == self.braceDepth: - self.anon_union_counter = [-1, 0] - elif (tok.type == 'COLON'): - #Dont want colon to be first in stack - if len(self.nameStack) == 0: - self.accessSpecifierScratch = [] - continue - - # Handle situation where access specifiers can be multi words such as "public slots" - jns = " ".join(self.accessSpecifierScratch + self.nameStack) - if jns in supportedAccessSpecifier: - self.curAccessSpecifier = jns; - debug_print("curAccessSpecifier updated to %s"%self.curAccessSpecifier) - self.stack = [] - self.nameStack = [] - else: - self.nameStack.append(tok.value) - self.accessSpecifierScratch = [] - - elif (tok.type == 'SEMI_COLON'): - if self.anon_union_counter[0] == self.braceDepth and self.anon_union_counter[1]: - debug_print("Creating anonymous union") - #Force the processing of an anonymous union - saved_namestack = self.nameStack[:] - saved_stack = self.stack[:] - self.nameStack = [""] - self.stack = self.nameStack + [";"] - self.nameStack = self.nameStack[0:1] - debug_print("pre eval anon stack") - self.evaluate_stack( tok.type ) - debug_print("post eval anon stack") - self.nameStack = saved_namestack - self.stack = saved_stack - self.anon_union_counter = [-1, 0]; - - - if (self.braceDepth < 10): self.evaluate_stack( tok.type ) - self.stack = [] - self.nameStack = [] - - except: - if (debug): raise - 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() - global parseHistory - parseHistory = [] - - def evaluate_stack(self, token=None): - """Evaluates the current name stack""" - global doxygenCommentCache - - self.nameStack = filter_out_attribute_keyword(self.nameStack) - self.stack = filter_out_attribute_keyword(self.stack) - - debug_print( "Evaluating stack %s\n BraceDepth: %s (called from %d)" %(self.nameStack,self.braceDepth, inspect.currentframe().f_back.f_lineno)) - - #Handle special case of overloading operator () - if "operator()(" in "".join(self.nameStack): - operator_index = self.nameStack.index("operator") - self.nameStack.pop(operator_index + 2) - self.nameStack.pop(operator_index + 1) - self.nameStack[operator_index] = "operator()" - - if (len(self.curClass)): - debug_print( "%s (%s) "%(self.curClass, self.curAccessSpecifier)) - - #Filter special case of array with casting in it - try: - bracePos = self.nameStack.index("[") - parenPos = self.nameStack.index("(") - if bracePos == parenPos - 1: - endParen = self.nameStack.index(")") - self.nameStack = self.nameStack[:bracePos + 1] + self.nameStack[endParen + 1:] - debug_print("Filtered namestack to=%s"%self.nameStack) - except: pass - - #if 'typedef' in self.nameStack: self.evaluate_typedef() # allows nested typedefs, probably a bad idea - if not self.curClass and 'typedef' in self.nameStack: - trace_print('STACK', self.stack) - self.evaluate_typedef() - return - - elif (len(self.nameStack) == 0): - debug_print( "trace" ) - 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] == "friend":#friend class declaration - pass - elif len(self.nameStack) >= 2 and self.nameStack[0] == 'using' and self.nameStack[1] == 'namespace': pass # TODO - - elif is_enum_namestack(self.nameStack): - debug_print( "trace" ) - self.evaluate_enum_stack() - - elif self._method_body and (self.braceDepth + 1) > self._method_body: trace_print( 'INSIDE METHOD DEF' ) - elif is_method_namestack(self.stack) and not self.curStruct and '(' in self.nameStack: - debug_print( "trace" ) - if self.braceDepth > 0: - if "{" in self.stack and self.stack[0] != '{' and self.stack[-1] == ';' and self.braceDepth == 1: - #Special case of a method defined outside a class that has a body - pass - else: - self.evaluate_method_stack() - else: - #Free function - self.evaluate_method_stack() - elif is_property_namestack(self.nameStack) and self.stack[-1] == ';': - debug_print( "trace" ) - if self.nameStack[0] in ('class', 'struct') and len(self.stack) == 3: 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] in ("class", "struct", "union"): - #Parsing a union can reuse much of the class parsing - debug_print( "trace" ) - self.evaluate_class_stack() - #elif (self.nameStack[0] == "struct"): - # debug_print( "trace" ) - ##this causes a bug when structs are nested in protected or private##self.curAccessSpecifier = "public" - # self.evaluate_struct_stack() - - - elif not self.curClass: - debug_print( "trace" ) - 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): - debug_print( "trace" ) - #Ignore global stuff for now - debug_print( "Global stuff: %s"%self.nameStack ) - self.nameStack = [] - doxygenCommentCache = "" - return - elif (self.braceDepth > len(self.nameSpaces) + 1): - debug_print( "trace" ) - self.nameStack = [] - doxygenCommentCache = "" - return - - self.nameStack = [] # its a little confusing to have some if/else above return and others not, and then clearning the nameStack down here - doxygenCommentCache = "" - - - def evaluate_enum_stack(self): - """Create an Enum out of the name stack""" - debug_print( "evaluating enum" ) - newEnum = CppEnum(self.nameStack) - if len(newEnum.keys()): - if len(self.curClass): - newEnum["namespace"] = self.cur_namespace(False) - klass = self.classes[self.curClass] - klass["enums"][self.curAccessSpecifier].append(newEnum) - if self.curAccessSpecifier == 'public' and 'name' in newEnum: klass._public_enums[ newEnum['name'] ] = newEnum - else: - newEnum["namespace"] = self.cur_namespace(True) - self.enums.append(newEnum) - if 'name' in newEnum and newEnum['name']: self.global_enums[ newEnum['name'] ] = newEnum - - #This enum has instances, turn them into properties - if newEnum.has_key("instances"): - instanceType = "enum" - if newEnum.has_key("name"): - instanceType = newEnum["name"] - for instance in newEnum["instances"]: - self.nameStack = [instanceType, instance] - self.evaluate_property_stack() - del newEnum["instances"] - - - def __repr__(self): - rtn = "" - for className in self.classes.keys(): - rtn += "%s\n"%self.classes[className] - if self.functions: - rtn += "// functions\n" - for f in self.functions: - rtn += "%s\n"%f - if self.enums: - rtn += "// enums\n" - for f in self.enums: - rtn += "%s\n"%f - return rtn diff --git a/lutinDoc.py b/lutinDoc.py index b3e4d21..89965f0 100644 --- a/lutinDoc.py +++ b/lutinDoc.py @@ -8,6 +8,7 @@ sys.path.append(lutinTools.GetCurrentPath(__file__) + "/cppParser/") sys.path.append(lutinTools.GetCurrentPath(__file__) + "/codeBB/") sys.path.append(lutinTools.GetCurrentPath(__file__) + "/codeHL/") import Parse +import Node import lutinDocHtml import lutinDocMd import os @@ -21,6 +22,7 @@ class doc: def __init__(self, moduleName): self.moduleName = moduleName self.listDocFile = [] + self.structureLib = Node.MainNode("library", moduleName) self.listTutorialFile = [] self.listClass = dict() self.listEnum = dict() @@ -90,6 +92,8 @@ class doc: fileCompleteName = os.path.join(root, filename) debug.debug(" Find a file : '" + fileCompleteName + "'") self.add_file(fileCompleteName) + self.structureLib.debug_display() + debug.error("ended") if self.pathGlobalDoc != "": for root, dirnames, filenames in os.walk(self.pathGlobalDoc): tmpList = fnmatch.filter(filenames, "*.bb") @@ -131,6 +135,15 @@ class doc: ## def add_file(self, filename): debug.debug("adding file in documantation : '" + filename + "'"); + + #plop = Parse.parse_file('ewol/sources/ewol/context/MacOs/OpenglView.h') + #debug.error("parse done"); + + parsedFile = Parse.parse_file(filename) + self.structureLib = parsedFile.fusion(self.structureLib) + + + def deprecate(self): plop = Parse.parse_file('Widget.h') debug.error("parse done");