CppHeaderParser3.py 96 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281
  1. #!/usr/bin/python
  2. #
  3. # Author: Jashua R. Cloutier (contact via https://bitbucket.org/senex)
  4. # Project: http://senexcanis.com/open-source/cppheaderparser/
  5. #
  6. # Copyright (C) 2011, Jashua R. Cloutier
  7. # All rights reserved.
  8. #
  9. # Redistribution and use in source and binary forms, with or without
  10. # modification, are permitted provided that the following conditions
  11. # are met:
  12. #
  13. # * Redistributions of source code must retain the above copyright
  14. # notice, this list of conditions and the following disclaimer.
  15. #
  16. # * Redistributions in binary form must reproduce the above copyright
  17. # notice, this list of conditions and the following disclaimer in
  18. # the documentation and/or other materials provided with the
  19. # distribution.
  20. #
  21. # * Neither the name of Jashua R. Cloutier nor the names of its
  22. # contributors may be used to endorse or promote products derived from
  23. # this software without specific prior written permission. Stories,
  24. # blog entries etc making reference to this project may mention the
  25. # name Jashua R. Cloutier in terms of project originator/creator etc.
  26. #
  27. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  28. # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  29. # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  30. # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  31. # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  32. # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  33. # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  34. # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  35. # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  36. # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  37. # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  38. # POSSIBILITY OF SUCH DAMAGE.
  39. #
  40. #
  41. # The CppHeaderParser.py script is written in Python 2.4 and released to
  42. # the open source community for continuous improvements under the BSD
  43. # 2.0 new license, which can be found at:
  44. #
  45. # http://www.opensource.org/licenses/bsd-license.php
  46. #
  47. """Parse C++ header files and generate a data structure
  48. representing the class
  49. """
  50. import ply.lex as lex
  51. import os
  52. import sys
  53. import re
  54. import inspect
  55. def lineno():
  56. """Returns the current line number in our program."""
  57. return inspect.currentframe().f_back.f_lineno
  58. version = __version__ = "2.3"
  59. tokens = [
  60. 'NUMBER',
  61. 'NAME',
  62. 'OPEN_PAREN',
  63. 'CLOSE_PAREN',
  64. 'OPEN_BRACE',
  65. 'CLOSE_BRACE',
  66. 'OPEN_SQUARE_BRACKET',
  67. 'CLOSE_SQUARE_BRACKET',
  68. 'COLON',
  69. 'SEMI_COLON',
  70. 'COMMA',
  71. 'TAB',
  72. 'BACKSLASH',
  73. 'PIPE',
  74. 'PERCENT',
  75. 'EXCLAMATION',
  76. 'CARET',
  77. 'COMMENT_SINGLELINE',
  78. 'COMMENT_MULTILINE',
  79. 'PRECOMP_MACRO',
  80. 'PRECOMP_MACRO_CONT',
  81. 'ASTERISK',
  82. 'AMPERSTAND',
  83. 'EQUALS',
  84. 'MINUS',
  85. 'PLUS',
  86. 'DIVIDE',
  87. 'CHAR_LITERAL',
  88. 'STRING_LITERAL',
  89. 'NEW_LINE',
  90. 'SQUOTE',
  91. ]
  92. t_ignore = " \r.?@\f"
  93. t_NUMBER = r'[0-9][0-9XxA-Fa-f]*'
  94. t_NAME = r'[<>A-Za-z_~][A-Za-z0-9_]*'
  95. t_OPEN_PAREN = r'\('
  96. t_CLOSE_PAREN = r'\)'
  97. t_OPEN_BRACE = r'{'
  98. t_CLOSE_BRACE = r'}'
  99. t_OPEN_SQUARE_BRACKET = r'\['
  100. t_CLOSE_SQUARE_BRACKET = r'\]'
  101. t_SEMI_COLON = r';'
  102. t_COLON = r':'
  103. t_COMMA = r','
  104. t_TAB = r'\t'
  105. t_BACKSLASH = r'\\'
  106. t_PIPE = r'\|'
  107. t_PERCENT = r'%'
  108. t_CARET = r'\^'
  109. t_EXCLAMATION = r'!'
  110. t_PRECOMP_MACRO = r'\#.*'
  111. t_PRECOMP_MACRO_CONT = r'.*\\\n'
  112. def t_COMMENT_SINGLELINE(t):
  113. r'\/\/.*\n'
  114. global doxygenCommentCache
  115. if t.value.startswith("///") or t.value.startswith("//!"):
  116. if doxygenCommentCache:
  117. doxygenCommentCache += "\n"
  118. if t.value.endswith("\n"):
  119. doxygenCommentCache += t.value[:-1]
  120. else:
  121. doxygenCommentCache += t.value
  122. t.lexer.lineno += len([a for a in t.value if a=="\n"])
  123. t_ASTERISK = r'\*'
  124. t_MINUS = r'\-'
  125. t_PLUS = r'\+'
  126. t_DIVIDE = r'/[^/]'
  127. t_AMPERSTAND = r'&'
  128. t_EQUALS = r'='
  129. t_CHAR_LITERAL = "'.'"
  130. t_SQUOTE = "'"
  131. #found at http://wordaligned.org/articles/string-literals-and-regular-expressions
  132. #TODO: This does not work with the string "bla \" bla"
  133. t_STRING_LITERAL = r'"([^"\\]|\\.)*"'
  134. #Found at http://ostermiller.org/findcomment.html
  135. def t_COMMENT_MULTILINE(t):
  136. r'/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/'
  137. global doxygenCommentCache
  138. if t.value.startswith("/**") or t.value.startswith("/*!"):
  139. #not sure why, but get double new lines
  140. v = t.value.replace("\n\n", "\n")
  141. #strip prefixing whitespace
  142. v = re.sub("\n[\s]+\*", "\n*", v)
  143. doxygenCommentCache += v
  144. t.lexer.lineno += len([a for a in t.value if a=="\n"])
  145. def t_NEWLINE(t):
  146. r'\n+'
  147. t.lexer.lineno += len(t.value)
  148. def t_error(v):
  149. print(( "Lex error: ", v ))
  150. lex.lex()
  151. # Controls error_print
  152. print_errors = 1
  153. # Controls warning_print
  154. print_warnings = 1
  155. # Controls debug_print
  156. debug = 0
  157. # Controls trace_print
  158. debug_trace = 0
  159. def error_print(arg):
  160. if print_errors: print(("[%4d] %s"%(inspect.currentframe().f_back.f_lineno, arg)))
  161. def warning_print(arg):
  162. if print_warnings: print(("[%4d] %s"%(inspect.currentframe().f_back.f_lineno, arg)))
  163. def debug_print(arg):
  164. global debug
  165. if debug: print(("[%4d] %s"%(inspect.currentframe().f_back.f_lineno, arg)))
  166. def trace_print(*arg):
  167. global debug_trace
  168. if debug_trace:
  169. sys.stdout.write("[%s] "%(inspect.currentframe().f_back.f_lineno))
  170. for a in arg: sys.stdout.write("%s "%a)
  171. sys.stdout.write("\n")
  172. supportedAccessSpecifier = [
  173. 'public',
  174. 'protected',
  175. 'private'
  176. ]
  177. #Symbols to ignore, usually special macros
  178. ignoreSymbols = [
  179. 'Q_OBJECT',
  180. ]
  181. doxygenCommentCache = ""
  182. #Track what was added in what order and at what depth
  183. parseHistory = []
  184. def is_namespace(nameStack):
  185. """Determines if a namespace is being specified"""
  186. if len(nameStack) == 0:
  187. return False
  188. if nameStack[0] == "namespace":
  189. return True
  190. return False
  191. def is_enum_namestack(nameStack):
  192. """Determines if a namestack is an enum namestack"""
  193. if len(nameStack) == 0:
  194. return False
  195. if nameStack[0] == "enum":
  196. return True
  197. if len(nameStack) > 1 and nameStack[0] == "typedef" and nameStack[1] == "enum":
  198. return True
  199. return False
  200. def is_fundamental(s):
  201. for a in s.split():
  202. if a not in ["size_t", "struct", "union", "unsigned", "signed", "bool", "char", "short", "int", "float", "double", "long", "void", "*"]: return False
  203. return True
  204. def is_function_pointer_stack(stack):
  205. """Count how many non-nested paranthesis are in the stack. Useful for determining if a stack is a function pointer"""
  206. paren_depth = 0
  207. paren_count = 0
  208. star_after_first_paren = False
  209. last_e = None
  210. for e in stack:
  211. if e == "(":
  212. paren_depth += 1
  213. elif e == ")" and paren_depth > 0:
  214. paren_depth -= 1
  215. if paren_depth == 0:
  216. paren_count += 1
  217. elif e == "*" and last_e == "(" and paren_count == 0 and paren_depth == 1:
  218. star_after_first_paren = True
  219. last_e = e
  220. if star_after_first_paren and paren_count == 2:
  221. return True
  222. else:
  223. return False
  224. def is_method_namestack(stack):
  225. r = False
  226. if '(' not in stack: r = False
  227. elif stack[0] == 'typedef': r = False # TODO deal with typedef function prototypes
  228. #elif '=' in stack and stack.index('=') < stack.index('(') and stack[stack.index('=')-1] != 'operator': r = False #disabled July6th - allow all operators
  229. elif 'operator' in stack: r = True # allow all operators
  230. elif '{' in stack and stack.index('{') < stack.index('('): r = False # struct that looks like a method/class
  231. elif '(' in stack and ')' in stack:
  232. if '{' in stack and '}' in stack: r = True
  233. elif stack[-1] == ';':
  234. if is_function_pointer_stack(stack):
  235. r = False
  236. else:
  237. r = True
  238. elif '{' in stack: r = True # ideally we catch both braces... TODO
  239. else: r = False
  240. #Test for case of property set to something with parens such as "static const int CONST_A = (1 << 7) - 1;"
  241. if r and "(" in stack and "=" in stack and 'operator' not in stack:
  242. if stack.index("=") < stack.index("("): r = False
  243. return r
  244. def is_property_namestack(nameStack):
  245. r = False
  246. if '(' not in nameStack and ')' not in nameStack: r = True
  247. elif "(" in nameStack and "=" in nameStack and nameStack.index("=") < nameStack.index("("): r = True
  248. #See if we are a function pointer
  249. if not r and is_function_pointer_stack(nameStack): r = True
  250. return r
  251. def detect_lineno(s):
  252. """Detect the line number for a given token string"""
  253. try:
  254. rtn = s.lineno()
  255. if rtn != -1:
  256. return rtn
  257. except: pass
  258. global curLine
  259. return curLine
  260. class TagStr(str):
  261. """Wrapper for a string that allows us to store the line number associated with it"""
  262. lineno_reg = {}
  263. def __new__(cls,*args,**kw):
  264. new_obj = str.__new__(cls,*args)
  265. if "lineno" in kw:
  266. TagStr.lineno_reg[id(new_obj)] = kw["lineno"]
  267. return new_obj
  268. def __del__(self):
  269. try:
  270. del TagStr.lineno_reg[id(self)]
  271. except: pass
  272. def lineno(self):
  273. return TagStr.lineno_reg.get(id(self), -1)
  274. class CppParseError(Exception): pass
  275. class CppClass(dict):
  276. """Takes a name stack and turns it into a class
  277. Contains the following Keys:
  278. self['name'] - Name of the class
  279. self['doxygen'] - Doxygen comments associated with the class if they exist
  280. self['inherits'] - List of Classes that this one inherits where the values
  281. are of the form {"access": Anything in supportedAccessSpecifier
  282. "class": Name of the class
  283. self['methods'] - Dictionary where keys are from supportedAccessSpecifier
  284. and values are a lists of CppMethod's
  285. self['properties'] - Dictionary where keys are from supportedAccessSpecifier
  286. and values are lists of CppVariable's
  287. self['enums'] - Dictionary where keys are from supportedAccessSpecifier and
  288. values are lists of CppEnum's
  289. self['structs'] - Dictionary where keys are from supportedAccessSpecifier and
  290. values are lists of nested Struct's
  291. An example of how this could look is as follows:
  292. #self =
  293. {
  294. 'name': ""
  295. 'inherits':[]
  296. 'methods':
  297. {
  298. 'public':[],
  299. 'protected':[],
  300. 'private':[]
  301. },
  302. 'properties':
  303. {
  304. 'public':[],
  305. 'protected':[],
  306. 'private':[]
  307. },
  308. 'enums':
  309. {
  310. 'public':[],
  311. 'protected':[],
  312. 'private':[]
  313. }
  314. }
  315. """
  316. def get_all_methods(self):
  317. r = []
  318. for typ in supportedAccessSpecifier: r += self['methods'][typ]
  319. return r
  320. def get_all_method_names( self ):
  321. r = []
  322. for typ in supportedAccessSpecifier: r += self.get_method_names(typ) # returns list
  323. return r
  324. def get_all_pure_virtual_methods( self ):
  325. r = {}
  326. for typ in supportedAccessSpecifier: r.update(self.get_pure_virtual_methods(typ)) # returns dict
  327. return r
  328. def get_method_names( self, type='public' ): return [ meth['name'] for meth in self['methods'][ type ] ]
  329. def get_pure_virtual_methods( self, type='public' ):
  330. r = {}
  331. for meth in self['methods'][ type ]:
  332. if meth['pure_virtual']: r[ meth['name'] ] = meth
  333. return r
  334. def __init__(self, nameStack):
  335. self['nested_classes'] = []
  336. self['parent'] = None
  337. self['abstract'] = False
  338. self._public_enums = {}
  339. self._public_structs = {}
  340. self._public_typedefs = {}
  341. self._public_forward_declares = []
  342. self['namespace'] = ""
  343. debug_print( "Class: %s"%nameStack )
  344. if (len(nameStack) < 2):
  345. nameStack.insert(1, "")#anonymous struct
  346. global doxygenCommentCache
  347. if len(doxygenCommentCache):
  348. self["doxygen"] = doxygenCommentCache
  349. doxygenCommentCache = ""
  350. self["name"] = nameStack[1]
  351. self["line_number"] = detect_lineno(nameStack[0])
  352. #Handle template classes
  353. if len(nameStack) > 3 and nameStack[2].startswith("<"):
  354. open_template_count = 0
  355. param_separator = 0
  356. found_first = False
  357. i = 0
  358. for elm in nameStack:
  359. if '<' in elm :
  360. open_template_count += 1
  361. found_first = True
  362. elif '>' in elm:
  363. open_template_count -= 1
  364. if found_first and open_template_count == 0:
  365. self["name"] = "".join(nameStack[1:i + 1])
  366. break;
  367. i += 1
  368. elif ":" in nameStack:
  369. self['name'] = nameStack[ nameStack.index(':') - 1 ]
  370. inheritList = []
  371. if nameStack.count(':') == 1:
  372. nameStack = nameStack[nameStack.index(":") + 1:]
  373. while len(nameStack):
  374. tmpStack = []
  375. tmpInheritClass = {"access":"private", "virtual": False}
  376. if "," in nameStack:
  377. tmpStack = nameStack[:nameStack.index(",")]
  378. nameStack = nameStack[nameStack.index(",") + 1:]
  379. else:
  380. tmpStack = nameStack
  381. nameStack = []
  382. # Convert template classes to one name in the last index
  383. for i in range(0, len(tmpStack)):
  384. if '<' in tmpStack[i]:
  385. tmpStack2 = tmpStack[:i-1]
  386. tmpStack2.append("".join(tmpStack[i-1:]))
  387. tmpStack = tmpStack2
  388. break
  389. if len(tmpStack) == 0:
  390. break;
  391. elif len(tmpStack) == 1:
  392. tmpInheritClass["class"] = tmpStack[0]
  393. elif len(tmpStack) == 2:
  394. tmpInheritClass["access"] = tmpStack[0]
  395. tmpInheritClass["class"] = tmpStack[1]
  396. elif len(tmpStack) == 3 and "virtual" in tmpStack:
  397. tmpInheritClass["access"] = tmpStack[1] if tmpStack[1] != "virtual" else tmpStack[0]
  398. tmpInheritClass["class"] = tmpStack[2]
  399. tmpInheritClass["virtual"] = True
  400. else:
  401. warning_print( "Warning: can not parse inheriting class %s"%(" ".join(tmpStack)))
  402. if '>' in tmpStack: pass # allow skip templates for now
  403. else: raise NotImplemented
  404. if 'class' in tmpInheritClass: inheritList.append(tmpInheritClass)
  405. elif nameStack.count(':') == 2: self['parent'] = self['name']; self['name'] = nameStack[-1]
  406. elif nameStack.count(':') > 2 and nameStack[0] in ("class", "struct"):
  407. tmpStack = nameStack[nameStack.index(":") + 1:]
  408. superTmpStack = [[]]
  409. for tok in tmpStack:
  410. if tok == ',':
  411. superTmpStack.append([])
  412. else:
  413. superTmpStack[-1].append(tok)
  414. for tmpStack in superTmpStack:
  415. tmpInheritClass = {"access":"private"}
  416. if len(tmpStack) and tmpStack[0] in supportedAccessSpecifier:
  417. tmpInheritClass["access"] = tmpStack[0]
  418. tmpStack = tmpStack[1:]
  419. inheritNSStack = []
  420. while len(tmpStack) > 3:
  421. if tmpStack[0] == ':': break;
  422. if tmpStack[1] != ':': break;
  423. if tmpStack[2] != ':': break;
  424. inheritNSStack.append(tmpStack[0])
  425. tmpStack = tmpStack[3:]
  426. if len(tmpStack) == 1 and tmpStack[0] != ':':
  427. inheritNSStack.append(tmpStack[0])
  428. tmpInheritClass["class"] = "::".join(inheritNSStack)
  429. inheritList.append(tmpInheritClass)
  430. self['inherits'] = inheritList
  431. methodAccessSpecificList = {}
  432. propertyAccessSpecificList = {}
  433. enumAccessSpecificList = {}
  434. structAccessSpecificList = {}
  435. typedefAccessSpecificList = {}
  436. forwardAccessSpecificList = {}
  437. for accessSpecifier in supportedAccessSpecifier:
  438. methodAccessSpecificList[accessSpecifier] = []
  439. propertyAccessSpecificList[accessSpecifier] = []
  440. enumAccessSpecificList[accessSpecifier] = []
  441. structAccessSpecificList[accessSpecifier] = []
  442. typedefAccessSpecificList[accessSpecifier] = []
  443. forwardAccessSpecificList[accessSpecifier] = []
  444. self['methods'] = methodAccessSpecificList
  445. self['properties'] = propertyAccessSpecificList
  446. self['enums'] = enumAccessSpecificList
  447. self['structs'] = structAccessSpecificList
  448. self['typedefs'] = typedefAccessSpecificList
  449. self['forward_declares'] = forwardAccessSpecificList
  450. def show(self):
  451. """Convert class to a string"""
  452. namespace_prefix = ""
  453. if self["namespace"]: namespace_prefix = self["namespace"] + "::"
  454. rtn = "%s %s"%(self["declaration_method"], namespace_prefix + self["name"])
  455. if self['abstract']: rtn += ' (abstract)\n'
  456. else: rtn += '\n'
  457. if 'doxygen' in list(self.keys()): rtn += self["doxygen"] + '\n'
  458. if 'parent' in list(self.keys()) and self['parent']: rtn += 'parent class: ' + self['parent'] + '\n'
  459. if "inherits" in list(self.keys()):
  460. rtn += " Inherits: "
  461. for inheritClass in self["inherits"]:
  462. if inheritClass["virtual"]: rtn += "virtual "
  463. rtn += "%s %s, "%(inheritClass["access"], inheritClass["class"])
  464. rtn += "\n"
  465. rtn += " {\n"
  466. for accessSpecifier in supportedAccessSpecifier:
  467. rtn += " %s\n"%(accessSpecifier)
  468. #Enums
  469. if (len(self["enums"][accessSpecifier])):
  470. rtn += " <Enums>\n"
  471. for enum in self["enums"][accessSpecifier]:
  472. rtn += " %s\n"%(repr(enum))
  473. #Properties
  474. if (len(self["properties"][accessSpecifier])):
  475. rtn += " <Properties>\n"
  476. for property in self["properties"][accessSpecifier]:
  477. rtn += " %s\n"%(repr(property))
  478. #Methods
  479. if (len(self["methods"][accessSpecifier])):
  480. rtn += " <Methods>\n"
  481. for method in self["methods"][accessSpecifier]:
  482. rtn += "\t\t" + method.show() + '\n'
  483. rtn += " }\n"
  484. print(rtn)
  485. def __repr__(self):
  486. """Convert class to a string"""
  487. namespace_prefix = ""
  488. if self["namespace"]: namespace_prefix = self["namespace"] + "::"
  489. rtn = "%s %s"%(self["declaration_method"], namespace_prefix + self["name"])
  490. if self['abstract']: rtn += ' (abstract)\n'
  491. else: rtn += '\n'
  492. if 'doxygen' in list(self.keys()): rtn += self["doxygen"] + '\n'
  493. if 'parent' in list(self.keys()) and self['parent']: rtn += 'parent class: ' + self['parent'] + '\n'
  494. if "inherits" in list(self.keys()) and len(self["inherits"]):
  495. rtn += "Inherits: "
  496. for inheritClass in self["inherits"]:
  497. if inheritClass.get("virtual", False): rtn += "virtual "
  498. rtn += "%s %s, "%(inheritClass["access"], inheritClass["class"])
  499. rtn += "\n"
  500. rtn += "{\n"
  501. for accessSpecifier in supportedAccessSpecifier:
  502. rtn += "%s\n"%(accessSpecifier)
  503. #Enums
  504. if (len(self["enums"][accessSpecifier])):
  505. rtn += " // Enums\n"
  506. for enum in self["enums"][accessSpecifier]:
  507. rtn += " %s\n"%(repr(enum))
  508. #Properties
  509. if (len(self["properties"][accessSpecifier])):
  510. rtn += " // Properties\n"
  511. for property in self["properties"][accessSpecifier]:
  512. rtn += " %s\n"%(repr(property))
  513. #Methods
  514. if (len(self["methods"][accessSpecifier])):
  515. rtn += " // Methods\n"
  516. for method in self["methods"][accessSpecifier]:
  517. rtn += " %s\n"%(repr(method))
  518. rtn += "}\n"
  519. return rtn
  520. class CppUnion( CppClass ):
  521. """Takes a name stack and turns it into a union
  522. Contains the following Keys:
  523. self['name'] - Name of the union
  524. self['doxygen'] - Doxygen comments associated with the union if they exist
  525. self['members'] - List of members the union has
  526. An example of how this could look is as follows:
  527. #self =
  528. {
  529. 'name': ""
  530. 'members': []
  531. }
  532. """
  533. def __init__(self, nameStack):
  534. CppClass.__init__(self, nameStack)
  535. self["name"] = "union " + self["name"]
  536. self["members"] = self["properties"]["public"]
  537. def transform_to_union_keys(self):
  538. print("union keys: %s"%list(self.keys()))
  539. for key in ['inherits', 'parent', 'abstract', 'namespace', 'typedefs', 'methods']:
  540. del self[key]
  541. def show(self):
  542. """Convert class to a string"""
  543. print(self)
  544. def __repr__(self):
  545. """Convert class to a string"""
  546. namespace_prefix = ""
  547. if self["namespace"]: namespace_prefix = self["namespace"] + "::"
  548. rtn = "%s %s"%(self["declaration_method"], namespace_prefix + self["name"])
  549. if self['abstract']: rtn += ' (abstract)\n'
  550. else: rtn += '\n'
  551. if 'doxygen' in list(self.keys()): rtn += self["doxygen"] + '\n'
  552. if 'parent' in list(self.keys()) and self['parent']: rtn += 'parent class: ' + self['parent'] + '\n'
  553. rtn += "{\n"
  554. for member in self["members"]:
  555. rtn += " %s\n"%(repr(member))
  556. rtn += "}\n"
  557. return rtn
  558. class _CppMethod( dict ):
  559. def _params_helper1( self, stack ):
  560. # deal with "throw" keyword
  561. if 'throw' in stack: stack = stack[ : stack.index('throw') ]
  562. ## remove GCC keyword __attribute__(...) and preserve returns ##
  563. cleaned = []
  564. hit = False; hitOpen = 0; hitClose = 0
  565. for a in stack:
  566. if a == '__attribute__': hit = True
  567. if hit:
  568. if a == '(': hitOpen += 1
  569. elif a == ')': hitClose += 1
  570. if a==')' and hitOpen == hitClose:
  571. hit = False
  572. else:
  573. cleaned.append( a )
  574. stack = cleaned
  575. # also deal with attribute((const)) function prefix #
  576. # TODO this needs to be better #
  577. if len(stack) > 5:
  578. a = ''.join(stack)
  579. if a.startswith('((__const__))'): stack = stack[ 5 : ]
  580. elif a.startswith('__attribute__((__const__))'): stack = stack[ 6 : ]
  581. stack = stack[stack.index('(') + 1: ]
  582. if not stack: return []
  583. if len(stack)>=3 and stack[0]==')' and stack[1]==':': # is this always a constructor?
  584. self['constructor'] = True
  585. return []
  586. stack.reverse(); _end_ = stack.index(')'); stack.reverse()
  587. stack = stack[ : len(stack)-(_end_+1) ]
  588. if '(' not in stack: return stack # safe to return, no defaults that init a class
  589. # transforms ['someclass', '(', '0', '0', '0', ')'] into "someclass(0,0,0)'"
  590. r = []; hit=False
  591. for a in stack:
  592. if a == '(': hit=True
  593. elif a == ')': hit=False
  594. if hit or a == ')': r[-1] = r[-1] + a
  595. else: r.append( a )
  596. return r
  597. def _params_helper2( self, params ):
  598. for p in params:
  599. p['method'] = self # save reference in variable to parent method
  600. if '::' in p['type']:
  601. ns = p['type'].split('::')[0]
  602. if ns not in Resolver.NAMESPACES and ns in Resolver.CLASSES:
  603. p['type'] = self['namespace'] + p['type']
  604. else: p['namespace'] = self[ 'namespace' ]
  605. class CppMethod( _CppMethod ):
  606. """Takes a name stack and turns it into a method
  607. Contains the following Keys:
  608. self['rtnType'] - Return type of the method (ex. "int")
  609. self['name'] - Name of the method (ex. "getSize")
  610. self['doxygen'] - Doxygen comments associated with the method if they exist
  611. self['parameters'] - List of CppVariables
  612. """
  613. def show(self):
  614. r = ['method name: %s (%s)' %(self['name'],self['debug']) ]
  615. if self['returns']: r.append( 'returns: %s'%self['returns'] )
  616. if self['parameters']: r.append( 'number arguments: %s' %len(self['parameters']))
  617. if self['pure_virtual']: r.append( 'pure virtual: %s'%self['pure_virtual'] )
  618. if self['constructor']: r.append( 'constructor' )
  619. if self['destructor']: r.append( 'destructor' )
  620. return '\n\t\t '.join( r )
  621. def __init__(self, nameStack, curClass, methinfo):
  622. debug_print( "Method: %s"%nameStack )
  623. global doxygenCommentCache
  624. if len(doxygenCommentCache):
  625. self["doxygen"] = doxygenCommentCache
  626. doxygenCommentCache = ""
  627. if "operator" in nameStack:
  628. self["rtnType"] = " ".join(nameStack[:nameStack.index('operator')])
  629. self["name"] = "".join(nameStack[nameStack.index('operator'):nameStack.index('(')])
  630. else:
  631. self["rtnType"] = " ".join(nameStack[:nameStack.index('(') - 1])
  632. self["name"] = " ".join(nameStack[nameStack.index('(') - 1:nameStack.index('(')])
  633. if self["rtnType"].startswith("virtual"):
  634. self["rtnType"] = self["rtnType"][len("virtual"):].strip()
  635. if len(self["rtnType"]) == 0 or self["name"] == curClass:
  636. self["rtnType"] = "void"
  637. self["rtnType"] = self["rtnType"].replace(' : : ', '::' )
  638. self["rtnType"] = self["rtnType"].replace(" <","<")
  639. self["rtnType"] = self["rtnType"].replace(" >",">").replace(">>", "> >").replace(">>", "> >")
  640. self["rtnType"] = self["rtnType"].replace(" ,",",")
  641. self["const"] = False
  642. for i in reversed(nameStack):
  643. if i == "const":
  644. self["const"] = True
  645. break
  646. elif i == ")":
  647. break
  648. self.update( methinfo )
  649. self["line_number"] = detect_lineno(nameStack[0])
  650. #Filter out initializer lists used in constructors
  651. try:
  652. paren_depth_counter = 0
  653. for i in range(0, len(nameStack)):
  654. elm = nameStack[i]
  655. if elm == "(":
  656. paren_depth_counter += 1
  657. if elm == ")":
  658. paren_depth_counter -=1
  659. if paren_depth_counter == 0 and nameStack[i+1] == ':':
  660. debug_print("Stripping out initializer list")
  661. nameStack = nameStack[:i+1]
  662. break
  663. except: pass
  664. paramsStack = self._params_helper1( nameStack )
  665. params = []
  666. #See if there is a doxygen comment for the variable
  667. doxyVarDesc = {}
  668. if "doxygen" in self:
  669. doxyLines = self["doxygen"].split("\n")
  670. lastParamDesc = ""
  671. for doxyLine in doxyLines:
  672. if " @param " in doxyLine or " \param " in doxyLine:
  673. try:
  674. #Strip out the param
  675. doxyLine = doxyLine[doxyLine.find("param ") + 6:]
  676. (var, desc) = doxyLine.split(" ", 1)
  677. doxyVarDesc[var] = desc.strip()
  678. lastParamDesc = var
  679. except: pass
  680. elif " @return " in doxyLine or " \return " in doxyLine:
  681. lastParamDesc = ""
  682. # not handled for now
  683. elif lastParamDesc:
  684. try:
  685. doxyLine = doxyLine.strip()
  686. if " " not in doxyLine:
  687. lastParamDesc = ""
  688. continue
  689. doxyLine = doxyLine[doxyLine.find(" ") + 1:]
  690. doxyVarDesc[lastParamDesc] += " " + doxyLine
  691. except: pass
  692. #Create the variable now
  693. while (len(paramsStack)):
  694. # Find commas that are not nexted in <>'s like template types
  695. open_template_count = 0
  696. param_separator = 0
  697. i = 0
  698. for elm in paramsStack:
  699. if '<' in elm :
  700. open_template_count += 1
  701. elif '>' in elm:
  702. open_template_count -= 1
  703. elif elm == ',' and open_template_count == 0:
  704. param_separator = i
  705. break
  706. i += 1
  707. if param_separator:
  708. param = CppVariable(paramsStack[0:param_separator], doxyVarDesc=doxyVarDesc)
  709. if len(list(param.keys())): params.append(param)
  710. paramsStack = paramsStack[param_separator + 1:]
  711. else:
  712. param = CppVariable(paramsStack, doxyVarDesc=doxyVarDesc)
  713. if len(list(param.keys())): params.append(param)
  714. break
  715. self["parameters"] = params
  716. self._params_helper2( params ) # mods params inplace
  717. def __repr__(self):
  718. filter_keys = ("parent", "defined", "operator", "returns_reference")
  719. cpy = dict((k,v) for (k,v) in list(self.items()) if k not in filter_keys)
  720. return "%s"%cpy
  721. class _CppVariable(dict):
  722. def _name_stack_helper( self, stack ):
  723. stack = list(stack)
  724. if '=' not in stack: # TODO refactor me
  725. # check for array[n] and deal with funny array syntax: "int myvar:99"
  726. array = []
  727. while stack and stack[-1].isdigit(): array.append( stack.pop() )
  728. if array: array.reverse(); self['array'] = int(''.join(array))
  729. if stack and stack[-1].endswith(':'): stack[-1] = stack[-1][:-1]
  730. while stack and not stack[-1]: stack.pop() # can be empty
  731. return stack
  732. def init(self):
  733. #assert self['name'] # allow unnamed variables, methods like this: "void func(void);"
  734. a = []
  735. self['aliases'] = []; self['parent'] = None; self['typedef'] = None
  736. for key in 'constant reference pointer static typedefs class fundamental unresolved'.split():
  737. self[ key ] = 0
  738. for b in self['type'].split():
  739. if b == '__const__': b = 'const'
  740. a.append( b )
  741. self['type'] = ' '.join( a )
  742. class CppVariable( _CppVariable ):
  743. """Takes a name stack and turns it into a method
  744. Contains the following Keys:
  745. self['type'] - Type for the variable (ex. "const string &")
  746. self['name'] - Name of the variable (ex. "numItems")
  747. self['namespace'] - Namespace containing the enum
  748. self['desc'] - Description of the variable if part of a method (optional)
  749. self['doxygen'] - Doxygen comments associated with the method if they exist
  750. self['defaltValue'] - Default value of the variable, this key will only
  751. exist if there is a default value
  752. """
  753. Vars = []
  754. def __init__(self, nameStack, **kwargs):
  755. _stack_ = nameStack
  756. if "[" in nameStack: #strip off array informatin
  757. arrayStack = nameStack[nameStack.index("["):]
  758. if len(arrayStack) == 3:
  759. self["array_size"] = arrayStack[1]
  760. nameStack = nameStack[:nameStack.index("[")]
  761. self["array"] = 1
  762. else:
  763. self["array"] = 0
  764. nameStack = self._name_stack_helper( nameStack )
  765. global doxygenCommentCache
  766. if len(doxygenCommentCache):
  767. self["doxygen"] = doxygenCommentCache
  768. doxygenCommentCache = ""
  769. debug_print( "Variable: %s"%nameStack )
  770. self["line_number"] = detect_lineno(nameStack[0])
  771. self["function_pointer"] = 0
  772. if (len(nameStack) < 2): # +++
  773. if len(nameStack) == 1: self['type'] = nameStack[0]; self['name'] = ''
  774. else: error_print(_stack_); assert 0
  775. elif is_function_pointer_stack(nameStack): #function pointer
  776. self["type"] = " ".join(nameStack[:nameStack.index("(") + 2] + nameStack[nameStack.index(")") :])
  777. self["name"] = " ".join(nameStack[nameStack.index("(") + 2 : nameStack.index(")")])
  778. self["function_pointer"] = 1
  779. elif ("=" in nameStack):
  780. self["type"] = " ".join(nameStack[:nameStack.index("=") - 1])
  781. self["name"] = nameStack[nameStack.index("=") - 1]
  782. self["defaltValue"] = " ".join(nameStack[nameStack.index("=") + 1:]) # deprecate camelCase in dicts
  783. self['default'] = " ".join(nameStack[nameStack.index("=") + 1:])
  784. elif is_fundamental(nameStack[-1]) or nameStack[-1] in ['>', '<' , ':', '.']:
  785. #Un named parameter
  786. self["type"] = " ".join(nameStack)
  787. self["name"] = ""
  788. else: # common case
  789. self["type"] = " ".join(nameStack[:-1])
  790. self["name"] = nameStack[-1]
  791. self["type"] = self["type"].replace(" :",":")
  792. self["type"] = self["type"].replace(": ",":")
  793. self["type"] = self["type"].replace(" <","<")
  794. self["type"] = self["type"].replace(" >",">").replace(">>", "> >").replace(">>", "> >")
  795. self["type"] = self["type"].replace(" ,",",")
  796. #Optional doxygen description
  797. try:
  798. self["desc"] = kwargs["doxyVarDesc"][self["name"]]
  799. except: pass
  800. self.init()
  801. CppVariable.Vars.append( self ) # save and resolve later
  802. def __repr__(self):
  803. keys_white_list = ['constant','name','reference','type','static','pointer','desc', 'line_number']
  804. cpy = dict((k,v) for (k,v) in list(self.items()) if k in keys_white_list)
  805. if "array_size" in self: cpy["array_size"] = self["array_size"]
  806. return "%s"%cpy
  807. class _CppEnum(dict):
  808. def resolve_enum_values( self, values ):
  809. """Evaluates the values list of dictionaries passed in and figures out what the enum value
  810. for each enum is editing in place:
  811. Example:
  812. From: [{'name': 'ORANGE'},
  813. {'name': 'RED'},
  814. {'name': 'GREEN', 'value': '8'}]
  815. To: [{'name': 'ORANGE', 'value': 0},
  816. {'name': 'RED', 'value': 1},
  817. {'name': 'GREEN', 'value': 8}]
  818. """
  819. t = int; i = 0
  820. names = [ v['name'] for v in values ]
  821. for v in values:
  822. if 'value' in v:
  823. a = v['value'].strip()
  824. # Remove single quotes from single quoted chars (unless part of some expression
  825. if len(a) == 3 and a[0] == "'" and a[2] == "'":
  826. a = v['value'] = a[1]
  827. if a.lower().startswith("0x"):
  828. try:
  829. i = a = int(a , 16)
  830. except:pass
  831. elif a.isdigit():
  832. i = a = int( a )
  833. elif a in names:
  834. for other in values:
  835. if other['name'] == a:
  836. v['value'] = other['value']
  837. break
  838. elif '"' in a or "'" in a: t = str # only if there are quotes it this a string enum
  839. else:
  840. try:
  841. a = i = ord(a)
  842. except: pass
  843. #Allow access of what is in the file pre-convert if converted
  844. if v['value'] != str(a):
  845. v['raw_value'] = v['value']
  846. v['value'] = a
  847. else: v['value'] = i
  848. try:
  849. v['value'] = v['value'].replace(" < < ", " << ").replace(" >> ", " >> ")
  850. except: pass
  851. i += 1
  852. return t
  853. class CppEnum(_CppEnum):
  854. """Takes a name stack and turns it into an Enum
  855. Contains the following Keys:
  856. self['name'] - Name of the enum (ex. "ItemState")
  857. self['namespace'] - Namespace containing the enum
  858. self['values'] - List of values where the values are a dictionary of the
  859. form {"name": name of the key (ex. "PARSING_HEADER"),
  860. "value": Specified value of the enum, this key will only exist
  861. if a value for a given enum value was defined
  862. }
  863. """
  864. def __init__(self, nameStack):
  865. global doxygenCommentCache
  866. if len(doxygenCommentCache):
  867. self["doxygen"] = doxygenCommentCache
  868. doxygenCommentCache = ""
  869. if len(nameStack) == 3 and nameStack[0] == "enum":
  870. debug_print("Created enum as just name/value")
  871. self["name"] = nameStack[1]
  872. self["instances"]=[nameStack[2]]
  873. if len(nameStack) < 4 or "{" not in nameStack or "}" not in nameStack:
  874. #Not enough stuff for an enum
  875. debug_print("Bad enum")
  876. return
  877. valueList = []
  878. self["line_number"] = detect_lineno(nameStack[0])
  879. #Figure out what values it has
  880. valueStack = nameStack[nameStack.index('{') + 1: nameStack.index('}')]
  881. while len(valueStack):
  882. tmpStack = []
  883. if "," in valueStack:
  884. tmpStack = valueStack[:valueStack.index(",")]
  885. valueStack = valueStack[valueStack.index(",") + 1:]
  886. else:
  887. tmpStack = valueStack
  888. valueStack = []
  889. d = {}
  890. if len(tmpStack) == 1: d["name"] = tmpStack[0]
  891. elif len(tmpStack) >= 3 and tmpStack[1] == "=":
  892. d["name"] = tmpStack[0]; d["value"] = " ".join(tmpStack[2:])
  893. elif len(tmpStack) == 2 and tmpStack[1] == "=":
  894. debug_print( "WARN-enum: parser missed value for %s"%tmpStack[0] )
  895. d["name"] = tmpStack[0]
  896. if d: valueList.append( d )
  897. if len(valueList):
  898. self['type'] = self.resolve_enum_values( valueList ) # returns int for standard enum
  899. self["values"] = valueList
  900. else:
  901. warning_print( 'WARN-enum: empty enum %s'%nameStack )
  902. return
  903. #Figure out if it has a name
  904. preBraceStack = nameStack[:nameStack.index("{")]
  905. postBraceStack = nameStack[nameStack.index("}") + 1:]
  906. if (len(preBraceStack) == 2 and "typedef" not in nameStack):
  907. self["name"] = preBraceStack[1]
  908. elif len(postBraceStack) and "typedef" in nameStack:
  909. self["name"] = " ".join(postBraceStack)
  910. else: warning_print( 'WARN-enum: nameless enum %s'%nameStack )
  911. #See if there are instances of this
  912. if "typedef" not in nameStack and len(postBraceStack):
  913. self["instances"] = []
  914. for var in postBraceStack:
  915. if "," in var:
  916. continue
  917. self["instances"].append(var)
  918. self["namespace"] = ""
  919. class CppStruct(dict):
  920. Structs = []
  921. def __init__(self, nameStack):
  922. if len(nameStack) >= 2: self['type'] = nameStack[1]
  923. else: self['type'] = None
  924. self['fields'] = []
  925. self.Structs.append( self )
  926. global curLine
  927. self["line_number"] = curLine
  928. C99_NONSTANDARD = {
  929. 'int8' : 'signed char',
  930. 'int16' : 'short int',
  931. 'int32' : 'int',
  932. 'int64' : 'int64_t', # this can be: long int (64bit), or long long int (32bit)
  933. 'uint' : 'unsigned int',
  934. 'uint8' : 'unsigned char',
  935. 'uint16' : 'unsigned short int',
  936. 'uint32' : 'unsigned int',
  937. 'uint64' : 'uint64_t', # depends on host bits
  938. }
  939. def standardize_fundamental( s ):
  940. if s in C99_NONSTANDARD: return C99_NONSTANDARD[ s ]
  941. else: return s
  942. class Resolver(object):
  943. C_FUNDAMENTAL = 'size_t unsigned signed bool char wchar short int float double long void'.split()
  944. C_FUNDAMENTAL += 'struct union enum'.split()
  945. SubTypedefs = {} # TODO deprecate?
  946. NAMESPACES = []
  947. CLASSES = {}
  948. STRUCTS = {}
  949. def initextra(self):
  950. self.typedefs = {}
  951. self.typedefs_order = []
  952. self.classes_order = []
  953. self.structs = Resolver.STRUCTS
  954. self.structs_order = []
  955. self.namespaces = Resolver.NAMESPACES # save all namespaces
  956. self.curStruct = None
  957. self.stack = [] # full name stack, good idea to keep both stacks? (simple stack and full stack)
  958. self._classes_brace_level = {} # class name : level
  959. self._structs_brace_level = {} # struct type : level
  960. self._method_body = None
  961. self._forward_decls = []
  962. self._template_typenames = [] # template<typename XXX>
  963. def current_namespace(self): return self.cur_namespace(True)
  964. def cur_namespace(self, add_double_colon=False):
  965. rtn = ""
  966. i = 0
  967. while i < len(self.nameSpaces):
  968. rtn += self.nameSpaces[i]
  969. if add_double_colon or i < len(self.nameSpaces) - 1: rtn += "::"
  970. i+=1
  971. return rtn
  972. def guess_ctypes_type( self, string ):
  973. pointers = string.count('*')
  974. string = string.replace('*','')
  975. a = string.split()
  976. if 'unsigned' in a: u = 'u'
  977. else: u = ''
  978. if 'long' in a and 'double' in a: b = 'longdouble' # there is no ctypes.c_ulongdouble (this is a 64bit float?)
  979. elif a.count('long') == 2 and 'int' in a: b = '%sint64' %u
  980. elif a.count('long') == 2: b = '%slonglong' %u
  981. elif 'long' in a: b = '%slong' %u
  982. elif 'double' in a: b = 'double' # no udouble in ctypes
  983. elif 'short' in a: b = '%sshort' %u
  984. elif 'char' in a: b = '%schar' %u
  985. elif 'wchar' in a: b = 'wchar'
  986. elif 'bool' in a: b = 'bool'
  987. elif 'float' in a: b = 'float'
  988. elif 'int' in a: b = '%sint' %u
  989. elif 'int8' in a: b = 'int8'
  990. elif 'int16' in a: b = 'int16'
  991. elif 'int32' in a: b = 'int32'
  992. elif 'int64' in a: b = 'int64'
  993. elif 'uint' in a: b = 'uint'
  994. elif 'uint8' in a: b = 'uint8'
  995. elif 'uint16' in a: b = 'uint16'
  996. elif 'uint32' in a: b = 'uint32'
  997. elif 'uint64' in a: b = 'uint64'
  998. elif 'size_t' in a: b = 'size_t'
  999. elif 'void' in a: b = 'void_p'
  1000. 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
  1001. else: b = 'void_p'
  1002. if not pointers: return 'ctypes.c_%s' %b
  1003. else:
  1004. x = ''
  1005. for i in range(pointers): x += 'ctypes.POINTER('
  1006. x += 'ctypes.c_%s' %b
  1007. x += ')' * pointers
  1008. return x
  1009. def resolve_type( self, string, result ): # recursive
  1010. '''
  1011. keeps track of useful things like: how many pointers, number of typedefs, is fundamental or a class, etc...
  1012. '''
  1013. ## be careful with templates, what is inside <something*> can be a pointer but the overall type is not a pointer
  1014. ## these come before a template
  1015. s = string.split('<')[0]
  1016. result[ 'constant' ] += s.split().count('const')
  1017. result[ 'static' ] += s.split().count('static')
  1018. result[ 'mutable' ] = 'mutable' in s.split()
  1019. ## these come after a template
  1020. s = string.split('>')[-1]
  1021. result[ 'pointer' ] += s.count('*')
  1022. result[ 'reference' ] += s.count('&')
  1023. x = string; alias = False
  1024. for a in '* & const static mutable'.split(): x = x.replace(a,'')
  1025. for y in x.split():
  1026. if y not in self.C_FUNDAMENTAL: alias = y; break
  1027. #if alias == 'class':
  1028. # result['class'] = result['name'] # forward decl of class
  1029. # result['forward_decl'] = True
  1030. if alias == '__extension__': result['fundamental_extension'] = True
  1031. elif alias:
  1032. result['aliases'].append( alias )
  1033. if alias in C99_NONSTANDARD:
  1034. result['type'] = C99_NONSTANDARD[ alias ]
  1035. result['typedef'] = alias
  1036. result['typedefs'] += 1
  1037. elif alias in self.typedefs:
  1038. result['typedefs'] += 1
  1039. result['typedef'] = alias
  1040. self.resolve_type( self.typedefs[alias], result )
  1041. elif alias in self.classes:
  1042. klass = self.classes[alias]; result['fundamental'] = False
  1043. result['class'] = klass
  1044. result['unresolved'] = False
  1045. else: result['unresolved'] = True
  1046. else:
  1047. result['fundamental'] = True
  1048. result['unresolved'] = False
  1049. def finalize_vars(self):
  1050. for s in CppStruct.Structs: # vars within structs can be ignored if they do not resolve
  1051. for var in s['fields']: var['parent'] = s['type']
  1052. #for c in self.classes.values():
  1053. # for var in c.get_all_properties(): var['parent'] = c['name']
  1054. ## RESOLVE ##
  1055. for var in CppVariable.Vars:
  1056. self.resolve_type( var['type'], var )
  1057. #if 'method' in var and var['method']['name'] == '_notifyCurrentCamera': print(var); assert 0
  1058. # then find concrete type and best guess ctypes type #
  1059. for var in CppVariable.Vars:
  1060. if not var['aliases']: #var['fundamental']:
  1061. var['ctypes_type'] = self.guess_ctypes_type( var['type'] )
  1062. else:
  1063. var['unresolved'] = False # below may test to True
  1064. if var['class']:
  1065. var['ctypes_type'] = 'ctypes.c_void_p'
  1066. else:
  1067. assert var['aliases']
  1068. tag = var['aliases'][0]
  1069. klass = None
  1070. nestedEnum = None
  1071. nestedStruct = None
  1072. nestedTypedef = None
  1073. if 'method' in var and 'parent' in list(var['method'].keys()):
  1074. klass = var['method']['parent']
  1075. if tag in var['method']['parent']._public_enums:
  1076. nestedEnum = var['method']['parent']._public_enums[ tag ]
  1077. elif tag in var['method']['parent']._public_structs:
  1078. nestedStruct = var['method']['parent']._public_structs[ tag ]
  1079. elif tag in var['method']['parent']._public_typedefs:
  1080. nestedTypedef = var['method']['parent']._public_typedefs[ tag ]
  1081. if '<' in tag: # should also contain '>'
  1082. var['template'] = tag # do not resolve templates
  1083. var['ctypes_type'] = 'ctypes.c_void_p'
  1084. var['unresolved'] = True
  1085. elif nestedEnum:
  1086. enum = nestedEnum
  1087. if enum['type'] is int:
  1088. var['ctypes_type'] = 'ctypes.c_int'
  1089. var['raw_type'] = 'int'
  1090. elif enum['type'] is str:
  1091. var['ctypes_type'] = 'ctypes.c_char_p'
  1092. var['raw_type'] = 'char*'
  1093. var['enum'] = var['method']['path'] + '::' + enum['name']
  1094. var['fundamental'] = True
  1095. elif nestedStruct:
  1096. var['ctypes_type'] = 'ctypes.c_void_p'
  1097. var['raw_type'] = var['method']['path'] + '::' + nestedStruct['type']
  1098. var['fundamental'] = False
  1099. elif nestedTypedef:
  1100. var['fundamental'] = is_fundamental( nestedTypedef )
  1101. if not var['fundamental']:
  1102. var['raw_type'] = var['method']['path'] + '::' + tag
  1103. else:
  1104. _tag = tag
  1105. if '::' in tag and tag.split('::')[0] in self.namespaces: tag = tag.split('::')[-1]
  1106. con = self.concrete_typedef( _tag )
  1107. if con:
  1108. var['concrete_type'] = con
  1109. var['ctypes_type'] = self.guess_ctypes_type( var['concrete_type'] )
  1110. elif tag in self.structs:
  1111. trace_print( 'STRUCT', var )
  1112. var['struct'] = tag
  1113. var['ctypes_type'] = 'ctypes.c_void_p'
  1114. var['raw_type'] = self.structs[tag]['namespace'] + '::' + tag
  1115. elif tag in self._forward_decls:
  1116. var['forward_declared'] = tag
  1117. var['ctypes_type'] = 'ctypes.c_void_p'
  1118. elif tag in self.global_enums:
  1119. enum = self.global_enums[ tag ]
  1120. if enum['type'] is int:
  1121. var['ctypes_type'] = 'ctypes.c_int'
  1122. var['raw_type'] = 'int'
  1123. elif enum['type'] is str:
  1124. var['ctypes_type'] = 'ctypes.c_char_p'
  1125. var['raw_type'] = 'char*'
  1126. var['enum'] = enum['namespace'] + enum['name']
  1127. var['fundamental'] = True
  1128. elif var['parent']:
  1129. warning_print( 'WARN unresolved %s'%_tag)
  1130. var['ctypes_type'] = 'ctypes.c_void_p'
  1131. var['unresolved'] = True
  1132. elif tag.count('::')==1:
  1133. trace_print( 'trying to find nested something in', tag )
  1134. a = tag.split('::')[0]
  1135. b = tag.split('::')[-1]
  1136. if a in self.classes: # a::b is most likely something nested in a class
  1137. klass = self.classes[ a ]
  1138. if b in klass._public_enums:
  1139. trace_print( '...found nested enum', b )
  1140. enum = klass._public_enums[ b ]
  1141. if enum['type'] is int:
  1142. var['ctypes_type'] = 'ctypes.c_int'
  1143. var['raw_type'] = 'int'
  1144. elif enum['type'] is str:
  1145. var['ctypes_type'] = 'ctypes.c_char_p'
  1146. var['raw_type'] = 'char*'
  1147. try:
  1148. if 'method' in var: var['enum'] = var['method']['path'] + '::' + enum['name']
  1149. else: # class property
  1150. var['unresolved'] = True
  1151. except:
  1152. var['unresolved'] = True
  1153. var['fundamental'] = True
  1154. else: var['unresolved'] = True # TODO klass._public_xxx
  1155. elif a in self.namespaces: # a::b can also be a nested namespace
  1156. if b in self.global_enums:
  1157. enum = self.global_enums[ b ]
  1158. trace_print(enum)
  1159. trace_print(var)
  1160. assert 0
  1161. elif b in self.global_enums: # falling back, this is a big ugly
  1162. enum = self.global_enums[ b ]
  1163. assert a in enum['namespace'].split('::')
  1164. if enum['type'] is int:
  1165. var['ctypes_type'] = 'ctypes.c_int'
  1166. var['raw_type'] = 'int'
  1167. elif enum['type'] is str:
  1168. var['ctypes_type'] = 'ctypes.c_char_p'
  1169. var['raw_type'] = 'char*'
  1170. var['fundamental'] = True
  1171. else: # boost::gets::crazy
  1172. trace_print('NAMESPACES', self.namespaces)
  1173. trace_print( a, b )
  1174. trace_print( '---- boost gets crazy ----' )
  1175. var['ctypes_type'] = 'ctypes.c_void_p'
  1176. var['unresolved'] = True
  1177. elif 'namespace' in var and self.concrete_typedef(var['namespace']+tag):
  1178. #print( 'TRYING WITH NS', var['namespace'] )
  1179. con = self.concrete_typedef( var['namespace']+tag )
  1180. if con:
  1181. var['typedef'] = var['namespace']+tag
  1182. var['type'] = con
  1183. if 'struct' in con.split():
  1184. var['raw_type'] = var['typedef']
  1185. var['ctypes_type'] = 'ctypes.c_void_p'
  1186. else:
  1187. self.resolve_type( var['type'], var )
  1188. var['ctypes_type'] = self.guess_ctypes_type( var['type'] )
  1189. elif '::' in var:
  1190. var['ctypes_type'] = 'ctypes.c_void_p'
  1191. var['unresolved'] = True
  1192. elif tag in self.SubTypedefs: # TODO remove SubTypedefs
  1193. if 'property_of_class' in var or 'property_of_struct' in var:
  1194. trace_print( 'class:', self.SubTypedefs[ tag ], 'tag:', tag )
  1195. var['typedef'] = self.SubTypedefs[ tag ] # class name
  1196. var['ctypes_type'] = 'ctypes.c_void_p'
  1197. else:
  1198. trace_print( "WARN-this should almost never happen!" )
  1199. trace_print( var ); trace_print('-'*80)
  1200. var['unresolved'] = True
  1201. elif tag in self._template_typenames:
  1202. var['typename'] = tag
  1203. var['ctypes_type'] = 'ctypes.c_void_p'
  1204. var['unresolved'] = True # TODO, how to deal with templates?
  1205. elif tag.startswith('_'): # assume starting with underscore is not important for wrapping
  1206. warning_print( 'WARN unresolved %s'%_tag)
  1207. var['ctypes_type'] = 'ctypes.c_void_p'
  1208. var['unresolved'] = True
  1209. else:
  1210. trace_print( 'WARN: unknown type', var )
  1211. assert 'property_of_class' in var or 'property_of_struct' # only allow this case
  1212. var['unresolved'] = True
  1213. ## if not resolved and is a method param, not going to wrap these methods ##
  1214. if var['unresolved'] and 'method' in var: var['method']['unresolved_parameters'] = True
  1215. # create stripped raw_type #
  1216. p = '* & const static mutable'.split() # +++ new July7: "mutable"
  1217. for var in CppVariable.Vars:
  1218. if 'raw_type' not in var:
  1219. raw = []
  1220. for x in var['type'].split():
  1221. if x not in p: raw.append( x )
  1222. var['raw_type'] = ' '.join( raw )
  1223. #if 'AutoConstantEntry' in var['raw_type']: print(var); assert 0
  1224. if var['class']:
  1225. if '::' not in var['raw_type']:
  1226. if not var['class']['parent']:
  1227. var['raw_type'] = var['class']['namespace'] + '::' + var['raw_type']
  1228. elif var['class']['parent'] in self.classes:
  1229. parent = self.classes[ var['class']['parent'] ]
  1230. var['raw_type'] = parent['namespace'] + '::' + var['class']['name'] + '::' + var['raw_type']
  1231. else:
  1232. var['unresolved'] = True
  1233. elif '::' in var['raw_type'] and var['raw_type'].split('::')[0] not in self.namespaces:
  1234. var['raw_type'] = var['class']['namespace'] + '::' + var['raw_type']
  1235. else:
  1236. var['unresolved'] = True
  1237. elif 'forward_declared' in var and 'namespace' in var:
  1238. if '::' not in var['raw_type']:
  1239. var['raw_type'] = var['namespace'] + var['raw_type']
  1240. elif '::' in var['raw_type'] and var['raw_type'].split('::')[0] in self.namespaces:
  1241. pass
  1242. else: trace_print('-'*80); trace_print(var); raise NotImplemented
  1243. ## need full name space for classes in raw type ##
  1244. if var['raw_type'].startswith( '::' ):
  1245. #print(var)
  1246. #print('NAMESPACE', var['class']['namespace'])
  1247. #print( 'PARENT NS', var['class']['parent']['namespace'] )
  1248. #assert 0
  1249. var['unresolved'] = True
  1250. if 'method' in var: var['method']['unresolved_parameters'] = True
  1251. #var['raw_type'] = var['raw_type'][2:]
  1252. # Take care of #defines and #pragmas etc
  1253. trace_print("Processing precomp_macro_buf: %s"%self._precomp_macro_buf)
  1254. for m in self._precomp_macro_buf:
  1255. macro = m.replace("<CppHeaderParser_newline_temp_replacement>\\n", "\n")
  1256. try:
  1257. if macro.lower().startswith("#define"):
  1258. trace_print("Adding #define %s"%macro)
  1259. self.defines.append(macro.split(" ", 1)[1].strip())
  1260. elif macro.lower().startswith("#pragma"):
  1261. trace_print("Adding #pragma %s"%macro)
  1262. self.pragmas.append(macro.split(" ", 1)[1].strip())
  1263. elif macro.lower().startswith("#include"):
  1264. trace_print("Adding #include %s"%macro)
  1265. self.includes.append(macro.split(" ", 1)[1].strip())
  1266. else:
  1267. debug_print("Cant detect what to do with precomp macro '%s'"%macro)
  1268. except: pass
  1269. self._precomp_macro_buf = None
  1270. def concrete_typedef( self, key ):
  1271. if key not in self.typedefs:
  1272. #print( 'FAILED typedef', key )
  1273. return None
  1274. while key in self.typedefs:
  1275. prev = key
  1276. key = self.typedefs[ key ]
  1277. if '<' in key or '>' in key: return prev # stop at template
  1278. if key.startswith('std::'): return key # stop at std lib
  1279. return key
  1280. class _CppHeader( Resolver ):
  1281. def finalize(self):
  1282. self.finalize_vars()
  1283. # finalize classes and method returns types
  1284. for cls in list(self.classes.values()):
  1285. for meth in cls.get_all_methods():
  1286. if meth['pure_virtual']: cls['abstract'] = True
  1287. if not meth['returns_fundamental'] and meth['returns'] in C99_NONSTANDARD:
  1288. meth['returns'] = C99_NONSTANDARD[meth['returns']]
  1289. meth['returns_fundamental'] = True
  1290. elif not meth['returns_fundamental']: # describe the return type
  1291. con = None
  1292. if cls['namespace'] and '::' not in meth['returns']:
  1293. con = self.concrete_typedef( cls['namespace'] + '::' + meth['returns'] )
  1294. else: con = self.concrete_typedef( meth['returns'] )
  1295. if con:
  1296. meth['returns_concrete'] = con
  1297. meth['returns_fundamental'] = is_fundamental( con )
  1298. elif meth['returns'] in self.classes:
  1299. trace_print( 'meth returns class:', meth['returns'] )
  1300. meth['returns_class'] = True
  1301. elif meth['returns'] in self.SubTypedefs:
  1302. meth['returns_class'] = True
  1303. meth['returns_nested'] = self.SubTypedefs[ meth['returns'] ]
  1304. elif meth['returns'] in cls._public_enums:
  1305. enum = cls._public_enums[ meth['returns'] ]
  1306. meth['returns_enum'] = enum['type']
  1307. meth['returns_fundamental'] = True
  1308. if enum['type'] == int: meth['returns'] = 'int'
  1309. else: meth['returns'] = 'char*'
  1310. elif meth['returns'] in self.global_enums:
  1311. enum = self.global_enums[ meth['returns'] ]
  1312. meth['returns_enum'] = enum['type']
  1313. meth['returns_fundamental'] = True
  1314. if enum['type'] == int: meth['returns'] = 'int'
  1315. else: meth['returns'] = 'char*'
  1316. elif meth['returns'].count('::')==1:
  1317. trace_print( meth )
  1318. a,b = meth['returns'].split('::')
  1319. if a in self.namespaces:
  1320. if b in self.classes:
  1321. klass = self.classes[ b ]
  1322. meth['returns_class'] = a + '::' + b
  1323. elif '<' in b and '>' in b:
  1324. warning_print( 'WARN-can not return template: %s'%b )
  1325. meth['returns_unknown'] = True
  1326. elif b in self.global_enums:
  1327. enum = self.global_enums[ b ]
  1328. meth['returns_enum'] = enum['type']
  1329. meth['returns_fundamental'] = True
  1330. if enum['type'] == int: meth['returns'] = 'int'
  1331. else: meth['returns'] = 'char*'
  1332. else: trace_print( a, b); trace_print( meth); meth['returns_unknown'] = True # +++
  1333. elif a in self.classes:
  1334. klass = self.classes[ a ]
  1335. if b in klass._public_enums:
  1336. trace_print( '...found nested enum', b )
  1337. enum = klass._public_enums[ b ]
  1338. meth['returns_enum'] = enum['type']
  1339. meth['returns_fundamental'] = True
  1340. if enum['type'] == int: meth['returns'] = 'int'
  1341. else: meth['returns'] = 'char*'
  1342. elif b in klass._public_forward_declares:
  1343. meth['returns_class'] = True
  1344. elif b in klass._public_typedefs:
  1345. typedef = klass._public_typedefs[ b ]
  1346. meth['returns_fundamental'] = is_fundamental( typedef )
  1347. else:
  1348. trace_print( meth ) # should be a nested class, TODO fix me.
  1349. meth['returns_unknown'] = True
  1350. elif '::' in meth['returns']:
  1351. trace_print('TODO namespace or extra nested return:', meth)
  1352. meth['returns_unknown'] = True
  1353. else:
  1354. trace_print( 'WARN: UNKNOWN RETURN', meth['name'], meth['returns'])
  1355. meth['returns_unknown'] = True
  1356. for cls in list(self.classes.values()):
  1357. methnames = cls.get_all_method_names()
  1358. pvm = cls.get_all_pure_virtual_methods()
  1359. for d in cls['inherits']:
  1360. c = d['class']
  1361. a = d['access'] # do not depend on this to be 'public'
  1362. trace_print( 'PARENT CLASS:', c )
  1363. if c not in self.classes: trace_print('WARN: parent class not found')
  1364. if c in self.classes and self.classes[c]['abstract']:
  1365. p = self.classes[ c ]
  1366. for meth in p.get_all_methods(): #p["methods"]["public"]:
  1367. trace_print( '\t\tmeth', meth['name'], 'pure virtual', meth['pure_virtual'] )
  1368. if meth['pure_virtual'] and meth['name'] not in methnames: cls['abstract'] = True; break
  1369. def evaluate_struct_stack(self):
  1370. """Create a Struct out of the name stack (but not its parts)"""
  1371. #print( 'eval struct stack', self.nameStack )
  1372. #if self.braceDepth != len(self.nameSpaces): return
  1373. struct = CppStruct(self.nameStack)
  1374. struct["namespace"] = self.cur_namespace()
  1375. self.structs[ struct['type'] ] = struct
  1376. self.structs_order.append( struct )
  1377. if self.curClass:
  1378. struct['parent'] = self.curClass
  1379. klass = self.classes[ self.curClass ]
  1380. klass['structs'][self.curAccessSpecifier].append( struct )
  1381. if self.curAccessSpecifier == 'public': klass._public_structs[ struct['type'] ] = struct
  1382. self.curStruct = struct
  1383. self._structs_brace_level[ struct['type'] ] = self.braceDepth
  1384. def parse_method_type( self, stack ):
  1385. trace_print( 'meth type info', stack )
  1386. if stack[0] in ':;': stack = stack[1:]
  1387. info = {
  1388. 'debug': ' '.join(stack).replace(' : : ', '::' ).replace(' < ', '<' ).replace(' > ', '> ' ).replace(" >",">").replace(">>", "> >").replace(">>", "> >"),
  1389. 'class':None,
  1390. 'namespace':self.cur_namespace(add_double_colon=True),
  1391. }
  1392. 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
  1393. header = stack[ : stack.index('(') ]
  1394. header = ' '.join( header )
  1395. header = header.replace(' : : ', '::' )
  1396. header = header.replace(' < ', '<' )
  1397. header = header.replace(' > ', '> ' )
  1398. header = header.strip()
  1399. if '{' in stack:
  1400. info['defined'] = True
  1401. self._method_body = self.braceDepth + 1
  1402. trace_print( 'NEW METHOD WITH BODY', self.braceDepth )
  1403. elif stack[-1] == ';':
  1404. info['defined'] = False
  1405. self._method_body = None # not a great idea to be clearing here
  1406. else: assert 0
  1407. if len(stack) > 3 and stack[-1] == ';' and stack[-2] == '0' and stack[-3] == '=':
  1408. info['pure_virtual'] = True
  1409. r = header.split()
  1410. name = None
  1411. if 'operator' in stack: # rare case op overload defined outside of class
  1412. op = stack[ stack.index('operator')+1 : stack.index('(') ]
  1413. op = ''.join(op)
  1414. if not op:
  1415. if " ".join(['operator', '(', ')', '(']) in " ".join(stack):
  1416. op = "()"
  1417. else:
  1418. trace_print( 'Error parsing operator')
  1419. return None
  1420. info['operator'] = op
  1421. name = 'operator' + op
  1422. a = stack[ : stack.index('operator') ]
  1423. elif r:
  1424. name = r[-1]
  1425. a = r[ : -1 ] # strip name
  1426. if name is None: return None
  1427. #if name.startswith('~'): name = name[1:]
  1428. while a and a[0] == '}': # strip - can have multiple } }
  1429. a = a[1:]
  1430. if '::' in name:
  1431. #klass,name = name.split('::') # methods can be defined outside of class
  1432. klass = name[ : name.rindex('::') ]
  1433. name = name.split('::')[-1]
  1434. info['class'] = klass
  1435. if klass in self.classes and not self.curClass:
  1436. #Class function defined outside the class
  1437. return None
  1438. # info['name'] = name
  1439. #else: info['name'] = name
  1440. if name.startswith('~'):
  1441. info['destructor'] = True
  1442. name = name[1:]
  1443. elif not a or (name == self.curClass and len(self.curClass)):
  1444. info['constructor'] = True
  1445. info['name'] = name
  1446. for tag in 'extern virtual static explicit inline friend'.split():
  1447. if tag in a: info[ tag ] = True; a.remove( tag ) # inplace
  1448. if 'template' in a:
  1449. a.remove('template')
  1450. b = ' '.join( a )
  1451. if '>' in b:
  1452. info['template'] = b[ : b.index('>')+1 ]
  1453. info['returns'] = b[ b.index('>')+1 : ] # find return type, could be incorrect... TODO
  1454. if '<typename' in info['template'].split():
  1455. typname = info['template'].split()[-1]
  1456. typname = typname[ : -1 ] # strip '>'
  1457. if typname not in self._template_typenames: self._template_typenames.append( typname )
  1458. else: info['returns'] = ' '.join( a )
  1459. else: info['returns'] = ' '.join( a )
  1460. info['returns'] = info['returns'].replace(' <', '<').strip()
  1461. ## be careful with templates, do not count pointers inside template
  1462. info['returns_pointer'] = info['returns'].split('>')[-1].count('*')
  1463. if info['returns_pointer']: info['returns'] = info['returns'].replace('*','').strip()
  1464. info['returns_reference'] = '&' in info['returns']
  1465. if info['returns']: info['returns'] = info['returns'].replace('&','').strip()
  1466. a = []
  1467. for b in info['returns'].split():
  1468. if b == '__const__': info['returns_const'] = True
  1469. elif b == 'const': info['returns_const'] = True
  1470. else: a.append( b )
  1471. info['returns'] = ' '.join( a )
  1472. info['returns_fundamental'] = is_fundamental( info['returns'] )
  1473. return info
  1474. def evaluate_method_stack(self):
  1475. """Create a method out of the name stack"""
  1476. if self.curStruct:
  1477. trace_print( 'WARN - struct contains methods - skipping' )
  1478. trace_print( self.stack )
  1479. assert 0
  1480. info = self.parse_method_type( self.stack )
  1481. if info:
  1482. if info[ 'class' ] and info['class'] in self.classes: # case where methods are defined outside of class
  1483. newMethod = CppMethod(self.nameStack, info['name'], info)
  1484. klass = self.classes[ info['class'] ]
  1485. klass[ 'methods' ][ 'public' ].append( newMethod )
  1486. newMethod['parent'] = klass
  1487. if klass['namespace']: newMethod['path'] = klass['namespace'] + '::' + klass['name']
  1488. else: newMethod['path'] = klass['name']
  1489. elif self.curClass: # normal case
  1490. newMethod = CppMethod(self.nameStack, self.curClass, info)
  1491. klass = self.classes[self.curClass]
  1492. klass['methods'][self.curAccessSpecifier].append(newMethod)
  1493. newMethod['parent'] = klass
  1494. if klass['namespace']: newMethod['path'] = klass['namespace'] + '::' + klass['name']
  1495. else: newMethod['path'] = klass['name']
  1496. else: #non class functions
  1497. debug_print("FREE FUNCTION")
  1498. newMethod = CppMethod(self.nameStack, None, info)
  1499. self.functions.append(newMethod)
  1500. global parseHistory
  1501. parseHistory.append({"braceDepth": self.braceDepth, "item_type": "method", "item": newMethod})
  1502. else:
  1503. trace_print( 'free function?', self.nameStack )
  1504. self.stack = []
  1505. def _parse_typedef( self, stack, namespace='' ):
  1506. if not stack or 'typedef' not in stack: return
  1507. stack = list( stack ) # copy just to be safe
  1508. if stack[-1] == ';': stack.pop()
  1509. while stack and stack[-1].isdigit(): stack.pop() # throw away array size for now
  1510. idx = stack.index('typedef')
  1511. name = namespace + stack[-1]
  1512. s = ''
  1513. for a in stack[idx+1:-1]:
  1514. if a == '{': break
  1515. if not s or s[-1] in ':<>' or a in ':<>': s += a # keep compact
  1516. else: s += ' ' + a # spacing
  1517. r = {'name':name, 'raw':s, 'type':s}
  1518. if not is_fundamental(s):
  1519. if 'struct' in s.split(): pass # TODO is this right? "struct ns::something"
  1520. elif '::' not in s: s = namespace + s # only add the current name space if no namespace given
  1521. r['type'] = s
  1522. if s: return r
  1523. def evaluate_typedef(self):
  1524. ns = self.cur_namespace(add_double_colon=True)
  1525. res = self._parse_typedef( self.stack, ns )
  1526. if res:
  1527. name = res['name']
  1528. self.typedefs[ name ] = res['type']
  1529. if name not in self.typedefs_order: self.typedefs_order.append( name )
  1530. def evaluate_property_stack(self):
  1531. """Create a Property out of the name stack"""
  1532. global parseHistory
  1533. assert self.stack[-1] == ';'
  1534. if self.nameStack[0] == 'typedef':
  1535. if self.curClass:
  1536. typedef = self._parse_typedef( self.stack )
  1537. name = typedef['name']
  1538. klass = self.classes[ self.curClass ]
  1539. klass[ 'typedefs' ][ self.curAccessSpecifier ].append( name )
  1540. if self.curAccessSpecifier == 'public': klass._public_typedefs[ name ] = typedef['type']
  1541. Resolver.SubTypedefs[ name ] = self.curClass
  1542. else: assert 0
  1543. elif self.curStruct or self.curClass:
  1544. if len(self.nameStack) == 1:
  1545. #See if we can de anonymize the type
  1546. filteredParseHistory = [h for h in parseHistory if h["braceDepth"] == self.braceDepth]
  1547. if len(filteredParseHistory) and filteredParseHistory[-1]["item_type"] == "class":
  1548. self.nameStack.insert(0, filteredParseHistory[-1]["item"]["name"])
  1549. debug_print("DEANONYMOIZING %s to type '%s'"%(self.nameStack[1], self.nameStack[0]))
  1550. newVar = CppVariable(self.nameStack)
  1551. newVar['namespace'] = self.current_namespace()
  1552. if self.curStruct:
  1553. self.curStruct[ 'fields' ].append( newVar )
  1554. newVar['property_of_struct'] = self.curStruct
  1555. elif self.curClass:
  1556. klass = self.classes[self.curClass]
  1557. klass["properties"][self.curAccessSpecifier].append(newVar)
  1558. newVar['property_of_class'] = klass['name']
  1559. parseHistory.append({"braceDepth": self.braceDepth, "item_type": "variable", "item": newVar})
  1560. self.stack = [] # CLEAR STACK
  1561. def evaluate_class_stack(self):
  1562. """Create a Class out of the name stack (but not its parts)"""
  1563. #dont support sub classes today
  1564. #print( 'eval class stack', self.nameStack )
  1565. parent = self.curClass
  1566. if self.braceDepth > len( self.nameSpaces) and parent:
  1567. trace_print( 'HIT NESTED SUBCLASS' )
  1568. self.accessSpecifierStack.append(self.curAccessSpecifier)
  1569. elif self.braceDepth != len(self.nameSpaces):
  1570. error_print( 'ERROR: WRONG BRACE DEPTH' )
  1571. return
  1572. if self.nameStack[0] == "class":
  1573. self.curAccessSpecifier = 'private'
  1574. else:#struct
  1575. self.curAccessSpecifier = 'public'
  1576. debug_print("curAccessSpecifier changed/defaulted to %s"%self.curAccessSpecifier)
  1577. if self.nameStack[0] == "union":
  1578. newClass = CppUnion(self.nameStack)
  1579. self.anon_union_counter = [self.braceDepth, 2]
  1580. trace_print( 'NEW UNION', newClass['name'] )
  1581. else:
  1582. newClass = CppClass(self.nameStack)
  1583. trace_print( 'NEW CLASS', newClass['name'] )
  1584. newClass["declaration_method"] = self.nameStack[0]
  1585. self.classes_order.append( newClass ) # good idea to save ordering
  1586. self.stack = [] # fixes if class declared with ';' in closing brace
  1587. if parent:
  1588. newClass["namespace"] = self.classes[ parent ]['namespace'] + '::' + parent
  1589. newClass['parent'] = parent
  1590. self.classes[ parent ]['nested_classes'].append( newClass )
  1591. ## supports nested classes with the same name ##
  1592. self.curClass = key = parent+'::'+newClass['name']
  1593. self._classes_brace_level[ key ] = self.braceDepth
  1594. elif newClass['parent']: # nested class defined outside of parent. A::B {...}
  1595. parent = newClass['parent']
  1596. newClass["namespace"] = self.classes[ parent ]['namespace'] + '::' + parent
  1597. self.classes[ parent ]['nested_classes'].append( newClass )
  1598. ## supports nested classes with the same name ##
  1599. self.curClass = key = parent+'::'+newClass['name']
  1600. self._classes_brace_level[ key ] = self.braceDepth
  1601. else:
  1602. newClass["namespace"] = self.cur_namespace()
  1603. key = newClass['name']
  1604. self.curClass = newClass["name"]
  1605. self._classes_brace_level[ newClass['name'] ] = self.braceDepth
  1606. if not key.endswith("::") and not key.endswith(" ") and len(key) != 0:
  1607. if key in self.classes:
  1608. trace_print( 'ERROR name collision:', key )
  1609. self.classes[key].show()
  1610. trace_print('-'*80)
  1611. newClass.show()
  1612. assert key not in self.classes # namespace collision
  1613. self.classes[ key ] = newClass
  1614. global parseHistory
  1615. parseHistory.append({"braceDepth": self.braceDepth, "item_type": "class", "item": newClass})
  1616. def evalute_forward_decl(self):
  1617. trace_print( 'FORWARD DECL', self.nameStack )
  1618. assert self.nameStack[0] in ('class', 'struct')
  1619. name = self.nameStack[-1]
  1620. if self.curClass:
  1621. klass = self.classes[ self.curClass ]
  1622. klass['forward_declares'][self.curAccessSpecifier].append( name )
  1623. if self.curAccessSpecifier == 'public': klass._public_forward_declares.append( name )
  1624. else: self._forward_decls.append( name )
  1625. class CppHeader( _CppHeader ):
  1626. """Parsed C++ class header
  1627. Variables produced:
  1628. self.classes - Dictionary of classes found in a given header file where the
  1629. key is the name of the class
  1630. """
  1631. IGNORE_NAMES = '__extension__'.split()
  1632. def show(self):
  1633. for className in list(self.classes.keys()):self.classes[className].show()
  1634. def __init__(self, headerFileName, argType="file", **kwargs):
  1635. """Create the parsed C++ header file parse tree
  1636. headerFileName - Name of the file to parse OR actual file contents (depends on argType)
  1637. argType - Indicates how to interpret headerFileName as a file string or file name
  1638. kwargs - Supports the following keywords
  1639. """
  1640. ## reset global state ##
  1641. global doxygenCommentCache
  1642. doxygenCommentCache = ""
  1643. CppVariable.Vars = []
  1644. CppStruct.Structs = []
  1645. if (argType == "file"):
  1646. self.headerFileName = os.path.expandvars(headerFileName)
  1647. self.mainClass = os.path.split(self.headerFileName)[1][:-2]
  1648. headerFileStr = ""
  1649. elif argType == "string":
  1650. self.headerFileName = ""
  1651. self.mainClass = "???"
  1652. headerFileStr = headerFileName
  1653. else:
  1654. raise Exception("Arg type must be either file or string")
  1655. self.curClass = ""
  1656. # nested classes have parent::nested, but no extra namespace,
  1657. # this keeps the API compatible, TODO proper namespace for everything.
  1658. Resolver.CLASSES = {}
  1659. self.classes = Resolver.CLASSES
  1660. #Functions that are not part of a class
  1661. self.functions = []
  1662. self.pragmas = []
  1663. self.defines = []
  1664. self.includes = []
  1665. self._precomp_macro_buf = [] #for internal purposes, will end up filling out pragmras and defines at the end
  1666. self.enums = []
  1667. self.global_enums = {}
  1668. self.nameStack = []
  1669. self.nameSpaces = []
  1670. self.curAccessSpecifier = 'private' # private is default
  1671. self.accessSpecifierStack = []
  1672. self.accessSpecifierScratch = []
  1673. debug_print("curAccessSpecifier changed/defaulted to %s"%self.curAccessSpecifier)
  1674. self.initextra()
  1675. self.anon_union_counter = [-1, 0]
  1676. if (len(self.headerFileName)):
  1677. fd = open(self.headerFileName)
  1678. headerFileStr = "".join(fd.readlines())
  1679. fd.close()
  1680. # Make sure supportedAccessSpecifier are sane
  1681. for i in range(0, len(supportedAccessSpecifier)):
  1682. if " " not in supportedAccessSpecifier[i]: continue
  1683. supportedAccessSpecifier[i] = re.sub("[ ]+", " ", supportedAccessSpecifier[i]).strip()
  1684. # Strip out template declarations
  1685. headerFileStr = re.sub("template[\t ]*<[^>]*>", "", headerFileStr)
  1686. # Change multi line #defines and expressions to single lines maintaining line nubmers
  1687. # Based from http://stackoverflow.com/questions/2424458/regular-expression-to-match-cs-multiline-preprocessor-statements
  1688. matches = re.findall(r'(?m)^(?:.*\\\r?\n)+.*$', headerFileStr)
  1689. is_define = re.compile(r'[ \t\v]*#[Dd][Ee][Ff][Ii][Nn][Ee]')
  1690. for m in matches:
  1691. #Keep the newlines so that linecount doesnt break
  1692. num_newlines = len([a for a in m if a=="\n"])
  1693. if is_define.match(m):
  1694. new_m = m.replace("\n", "<CppHeaderParser_newline_temp_replacement>\\n")
  1695. else:
  1696. # Just expression taking up multiple lines, make it take 1 line for easier parsing
  1697. new_m = m.replace("\\\n", " ")
  1698. if (num_newlines > 1):
  1699. new_m += "\n"*(num_newlines)
  1700. headerFileStr = headerFileStr.replace(m, new_m)
  1701. #Filter out Extern "C" statements. These are order dependent
  1702. matches = re.findall(re.compile(r'extern[\t ]+"[Cc]"[\t \n\r]*{', re.DOTALL), headerFileStr)
  1703. for m in matches:
  1704. #Keep the newlines so that linecount doesnt break
  1705. num_newlines = len([a for a in m if a=="\n"])
  1706. headerFileStr = headerFileStr.replace(m, "\n" * num_newlines)
  1707. headerFileStr = re.sub(r'extern[ ]+"[Cc]"[ ]*', "", headerFileStr)
  1708. self.braceDepth = 0
  1709. lex.lex()
  1710. lex.input(headerFileStr)
  1711. global curLine
  1712. global curChar
  1713. curLine = 0
  1714. curChar = 0
  1715. try:
  1716. while True:
  1717. tok = lex.token()
  1718. if not tok: break
  1719. if self.anon_union_counter[0] == self.braceDepth and self.anon_union_counter[1]:
  1720. self.anon_union_counter[1] -= 1
  1721. tok.value = TagStr(tok.value, lineno=tok.lineno)
  1722. #debug_print("TOK: %s"%tok)
  1723. if tok.type == 'NAME' and tok.value in self.IGNORE_NAMES: continue
  1724. self.stack.append( tok.value )
  1725. curLine = tok.lineno
  1726. curChar = tok.lexpos
  1727. if (tok.type in ('PRECOMP_MACRO', 'PRECOMP_MACRO_CONT')):
  1728. debug_print("PRECOMP: %s"%tok)
  1729. self._precomp_macro_buf.append(tok.value)
  1730. continue
  1731. if (tok.type == 'OPEN_BRACE'):
  1732. if len(self.nameStack) >= 2 and is_namespace(self.nameStack): # namespace {} with no name used in boost, this sets default?
  1733. if self.nameStack[1] == "__IGNORED_NAMESPACE__CppHeaderParser__":#Used in filtering extern "C"
  1734. self.nameStack[1] = ""
  1735. self.nameSpaces.append(self.nameStack[1])
  1736. ns = self.cur_namespace(); self.stack = []
  1737. if ns not in self.namespaces: self.namespaces.append( ns )
  1738. if len(self.nameStack) and not is_enum_namestack(self.nameStack):
  1739. self.evaluate_stack()
  1740. else:
  1741. self.nameStack.append(tok.value)
  1742. if self.stack and self.stack[0] == 'class': self.stack = []
  1743. self.braceDepth += 1
  1744. elif (tok.type == 'CLOSE_BRACE'):
  1745. if self.braceDepth == 0:
  1746. continue
  1747. if (self.braceDepth == len(self.nameSpaces)):
  1748. tmp = self.nameSpaces.pop()
  1749. self.stack = [] # clear stack when namespace ends?
  1750. if len(self.nameStack) and is_enum_namestack(self.nameStack):
  1751. self.nameStack.append(tok.value)
  1752. elif self.braceDepth < 10:
  1753. self.evaluate_stack()
  1754. else:
  1755. self.nameStack = []
  1756. self.braceDepth -= 1
  1757. #self.stack = []; print 'BRACE DEPTH', self.braceDepth, 'NS', len(self.nameSpaces)
  1758. if self.curClass: debug_print( 'CURBD %s'%self._classes_brace_level[ self.curClass ] )
  1759. if (self.braceDepth == 0) or (self.curClass and self._classes_brace_level[self.curClass]==self.braceDepth):
  1760. trace_print( 'END OF CLASS DEF' )
  1761. if self.accessSpecifierStack:
  1762. self.curAccessSpecifier = self.accessSpecifierStack[-1]
  1763. self.accessSpecifierStack = self.accessSpecifierStack[:-1]
  1764. if self.curClass and self.classes[ self.curClass ]['parent']: self.curClass = self.classes[ self.curClass ]['parent']
  1765. else: self.curClass = ""; #self.curStruct = None
  1766. self.stack = []
  1767. #if self.curStruct: self.curStruct = None
  1768. if self.braceDepth == 0 or (self.curStruct and self._structs_brace_level[self.curStruct['type']]==self.braceDepth):
  1769. trace_print( 'END OF STRUCT DEF' )
  1770. self.curStruct = None
  1771. if self._method_body and (self.braceDepth + 1) <= self._method_body:
  1772. self._method_body = None; self.stack = []; self.nameStack = []; trace_print( 'FORCE CLEAR METHBODY' )
  1773. if (tok.type == 'OPEN_PAREN'):
  1774. self.nameStack.append(tok.value)
  1775. elif (tok.type == 'CLOSE_PAREN'):
  1776. self.nameStack.append(tok.value)
  1777. elif (tok.type == 'OPEN_SQUARE_BRACKET'):
  1778. self.nameStack.append(tok.value)
  1779. elif (tok.type == 'CLOSE_SQUARE_BRACKET'):
  1780. self.nameStack.append(tok.value)
  1781. elif (tok.type == 'TAB'): pass
  1782. elif (tok.type == 'EQUALS'):
  1783. self.nameStack.append(tok.value)
  1784. elif (tok.type == 'COMMA'):
  1785. self.nameStack.append(tok.value)
  1786. elif (tok.type == 'BACKSLASH'):
  1787. self.nameStack.append(tok.value)
  1788. elif (tok.type == 'PIPE'):
  1789. self.nameStack.append(tok.value)
  1790. elif (tok.type == 'PERCENT'):
  1791. self.nameStack.append(tok.value)
  1792. elif (tok.type == 'CARET'):
  1793. self.nameStack.append(tok.value)
  1794. elif (tok.type == 'EXCLAMATION'):
  1795. self.nameStack.append(tok.value)
  1796. elif (tok.type == 'SQUOTE'): pass
  1797. elif (tok.type == 'NUMBER'):
  1798. self.nameStack.append(tok.value)
  1799. elif (tok.type == 'MINUS'):
  1800. self.nameStack.append(tok.value)
  1801. elif (tok.type == 'PLUS'):
  1802. self.nameStack.append(tok.value)
  1803. elif (tok.type == 'STRING_LITERAL'):
  1804. self.nameStack.append(tok.value)
  1805. elif (tok.type == 'NAME' or tok.type == 'AMPERSTAND' or tok.type == 'ASTERISK' or tok.type == 'CHAR_LITERAL'):
  1806. if tok.value in ignoreSymbols:
  1807. debug_print("Ignore symbol %s"%tok.value)
  1808. elif (tok.value == 'class'):
  1809. self.nameStack.append(tok.value)
  1810. elif tok.value in supportedAccessSpecifier:
  1811. if len(self.nameStack) and self.nameStack[0] in ("class", "struct", "union"):
  1812. self.nameStack.append(tok.value)
  1813. elif self.braceDepth == len(self.nameSpaces) + 1 or self.braceDepth == (len(self.nameSpaces) + len(self.curClass.split("::"))):
  1814. self.curAccessSpecifier = tok.value;
  1815. self.accessSpecifierScratch.append(tok.value)
  1816. debug_print("curAccessSpecifier updated to %s"%self.curAccessSpecifier)
  1817. self.stack = []
  1818. else:
  1819. self.nameStack.append(tok.value)
  1820. if self.anon_union_counter[0] == self.braceDepth:
  1821. self.anon_union_counter = [-1, 0]
  1822. elif (tok.type == 'COLON'):
  1823. #Dont want colon to be first in stack
  1824. if len(self.nameStack) == 0:
  1825. self.accessSpecifierScratch = []
  1826. continue
  1827. # Handle situation where access specifiers can be multi words such as "public slots"
  1828. jns = " ".join(self.accessSpecifierScratch + self.nameStack)
  1829. if jns in supportedAccessSpecifier:
  1830. self.curAccessSpecifier = jns;
  1831. debug_print("curAccessSpecifier updated to %s"%self.curAccessSpecifier)
  1832. self.stack = []
  1833. self.nameStack = []
  1834. else:
  1835. self.nameStack.append(tok.value)
  1836. self.accessSpecifierScratch = []
  1837. elif (tok.type == 'SEMI_COLON'):
  1838. if self.anon_union_counter[0] == self.braceDepth and self.anon_union_counter[1]:
  1839. debug_print("Creating anonymous union")
  1840. #Force the processing of an anonymous union
  1841. saved_namestack = self.nameStack[:]
  1842. saved_stack = self.stack[:]
  1843. self.nameStack = [""]
  1844. self.stack = self.nameStack + [";"]
  1845. self.nameStack = self.nameStack[0:1]
  1846. debug_print("pre eval anon stack")
  1847. self.evaluate_stack( tok.type )
  1848. debug_print("post eval anon stack")
  1849. self.nameStack = saved_namestack
  1850. self.stack = saved_stack
  1851. self.anon_union_counter = [-1, 0];
  1852. if (self.braceDepth < 10): self.evaluate_stack( tok.type )
  1853. if not self.stack: continue
  1854. if self.stack[0]=='typedef' and ( '{' not in self.stack or '}' in self.stack ): self.stack = []; trace_print( "REAL CLEAR")
  1855. elif self.stack[0] != 'typedef': self.stack = []; trace_print('CLEAR STACK')
  1856. except:
  1857. if (debug): raise
  1858. raise CppParseError("Not able to parse %s on line %d evaluating \"%s\"\nError around: %s"
  1859. % (self.headerFileName, tok.lineno, tok.value, " ".join(self.nameStack)))
  1860. self.finalize()
  1861. global parseHistory
  1862. parseHistory = []
  1863. def evaluate_stack(self, token=None):
  1864. """Evaluates the current name stack"""
  1865. global doxygenCommentCache
  1866. debug_print( "Evaluating stack %s\n BraceDepth: %s (called from %d)" %(self.nameStack,self.braceDepth, inspect.currentframe().f_back.f_lineno))
  1867. #Handle special case of overloading operator ()
  1868. if "operator()(" in "".join(self.nameStack):
  1869. operator_index = self.nameStack.index("operator")
  1870. self.nameStack.pop(operator_index + 2)
  1871. self.nameStack.pop(operator_index + 1)
  1872. self.nameStack[operator_index] = "operator()"
  1873. if (len(self.curClass)):
  1874. debug_print( "%s (%s) "%(self.curClass, self.curAccessSpecifier))
  1875. #Filter special case of array with casting in it
  1876. try:
  1877. bracePos = self.nameStack.index("[")
  1878. parenPos = self.nameStack.index("(")
  1879. if bracePos == parenPos - 1:
  1880. endParen = self.nameStack.index(")")
  1881. self.nameStack = self.nameStack[:bracePos + 1] + self.nameStack[endParen + 1:]
  1882. debug_print("Filtered namestack to=%s"%self.nameStack)
  1883. except: pass
  1884. #if 'typedef' in self.nameStack: self.evaluate_typedef() # allows nested typedefs, probably a bad idea
  1885. if not self.curClass and 'typedef' in self.nameStack:
  1886. trace_print('STACK', self.stack)
  1887. if token == 'SEMI_COLON' and ('{' not in self.stack or '}' in self.stack): self.evaluate_typedef()
  1888. else: return
  1889. elif (len(self.nameStack) == 0):
  1890. debug_print( "trace" )
  1891. debug_print( "(Empty Stack)" )
  1892. return
  1893. elif (self.nameStack[0] == "namespace"):
  1894. #Taken care of outside of here
  1895. pass
  1896. elif self.nameStack[0] == "friend":
  1897. pass
  1898. elif len(self.nameStack) >= 2 and self.nameStack[0] == 'using' and self.nameStack[1] == 'namespace': pass # TODO
  1899. elif is_enum_namestack(self.nameStack):
  1900. debug_print( "trace" )
  1901. self.evaluate_enum_stack()
  1902. elif self._method_body and (self.braceDepth + 1) > self._method_body: trace_print( 'INSIDE METHOD DEF' )
  1903. elif is_method_namestack(self.stack) and not self.curStruct and '(' in self.nameStack:
  1904. debug_print( "trace" )
  1905. if self.braceDepth > 0:
  1906. if "{" in self.stack and self.stack[0] != '{' and self.stack[-1] == ';' and self.braceDepth == 1:
  1907. #Special case of a method defined outside a class that has a body
  1908. pass
  1909. else:
  1910. self.evaluate_method_stack()
  1911. else:
  1912. #Free function
  1913. self.evaluate_method_stack()
  1914. elif is_property_namestack(self.nameStack) and self.stack[-1] == ';':
  1915. debug_print( "trace" )
  1916. if self.nameStack[0] in ('class', 'struct') and len(self.stack) == 3: self.evalute_forward_decl()
  1917. elif len(self.nameStack) >= 2 and (self.nameStack[0]=='friend' and self.nameStack[1]=='class'): pass
  1918. else: self.evaluate_property_stack() # catches class props and structs in a namespace
  1919. elif self.nameStack[0] in ("class", "struct", "union"):
  1920. #Parsing a union can reuse much of the class parsing
  1921. debug_print( "trace" )
  1922. self.evaluate_class_stack()
  1923. #elif (self.nameStack[0] == "struct"):
  1924. # debug_print( "trace" )
  1925. ##this causes a bug when structs are nested in protected or private##self.curAccessSpecifier = "public"
  1926. # self.evaluate_struct_stack()
  1927. elif not self.curClass:
  1928. debug_print( "trace" )
  1929. if is_enum_namestack(self.nameStack): self.evaluate_enum_stack()
  1930. elif self.curStruct and self.stack[-1] == ';': self.evaluate_property_stack() # this catches fields of global structs
  1931. self.nameStack = []
  1932. doxygenCommentCache = ""
  1933. return
  1934. elif (self.braceDepth < 1):
  1935. debug_print( "trace" )
  1936. #Ignore global stuff for now
  1937. debug_print( "Global stuff: %s"%self.nameStack )
  1938. self.nameStack = []
  1939. doxygenCommentCache = ""
  1940. return
  1941. elif (self.braceDepth > len(self.nameSpaces) + 1):
  1942. debug_print( "trace" )
  1943. self.nameStack = []
  1944. doxygenCommentCache = ""
  1945. return
  1946. self.nameStack = [] # its a little confusing to have some if/else above return and others not, and then clearning the nameStack down here
  1947. doxygenCommentCache = ""
  1948. def evaluate_enum_stack(self):
  1949. """Create an Enum out of the name stack"""
  1950. debug_print( "evaluating enum" )
  1951. newEnum = CppEnum(self.nameStack)
  1952. if len(list(newEnum.keys())):
  1953. if len(self.curClass):
  1954. newEnum["namespace"] = self.cur_namespace(False)
  1955. klass = self.classes[self.curClass]
  1956. klass["enums"][self.curAccessSpecifier].append(newEnum)
  1957. if self.curAccessSpecifier == 'public' and 'name' in newEnum: klass._public_enums[ newEnum['name'] ] = newEnum
  1958. else:
  1959. newEnum["namespace"] = self.cur_namespace(True)
  1960. self.enums.append(newEnum)
  1961. if 'name' in newEnum and newEnum['name']: self.global_enums[ newEnum['name'] ] = newEnum
  1962. #This enum has instances, turn them into properties
  1963. if "instances" in newEnum:
  1964. instanceType = "enum"
  1965. if "name" in newEnum:
  1966. instanceType = newEnum["name"]
  1967. for instance in newEnum["instances"]:
  1968. self.nameStack = [instanceType, instance]
  1969. self.evaluate_property_stack()
  1970. del newEnum["instances"]
  1971. def __repr__(self):
  1972. rtn = ""
  1973. for className in list(self.classes.keys()):
  1974. rtn += "%s\n"%self.classes[className]
  1975. if self.functions:
  1976. rtn += "// functions\n"
  1977. for f in self.functions:
  1978. rtn += "%s\n"%f
  1979. if self.enums:
  1980. rtn += "// enums\n"
  1981. for f in self.enums:
  1982. rtn += "%s\n"%f
  1983. return rtn