From e12f5405588707d069b9b22c28796127064f029d Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Tue, 11 Dec 2018 01:44:53 -0500 Subject: [PATCH 001/143] Add .gitignore --- .gitignore | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d15acba --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +*.pyc +*.egg-info +build +dist + +__pycache__ +.vscode \ No newline at end of file From 3065ec491c361586c01fe803838d5d9564f26264 Mon Sep 17 00:00:00 2001 From: Dennis Lim Date: Tue, 11 Dec 2018 01:57:32 -0500 Subject: [PATCH 002/143] Fix tests to support python 3 --- CppHeaderParser/CppHeaderParser.py | 2 +- CppHeaderParser/test/test_CppHeaderParser.py | 42 ++++++++++---------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 06a17fc..d24976c 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -943,7 +943,7 @@ def __init__(self, nameStack, **kwargs): if nameStack.count("[") > 1: debug_print("Multi dimensional array") debug_print("arrayStack=%s"%arrayStack) - nums = filter(lambda x: x.isdigit(), arrayStack) + nums = [x for x in arrayStack if x.isdigit()] # Calculate size by multiplying all dimensions p = 1 for n in nums: diff --git a/CppHeaderParser/test/test_CppHeaderParser.py b/CppHeaderParser/test/test_CppHeaderParser.py index f5ba080..fabd7fd 100644 --- a/CppHeaderParser/test/test_CppHeaderParser.py +++ b/CppHeaderParser/test/test_CppHeaderParser.py @@ -1330,7 +1330,7 @@ def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_exists(self): - self.assertEqual(self.cppHeader.classes.has_key("FruitFly"), True) + self.assertEqual("FruitFly" in self.cppHeader.classes, True) # Bug BitBucket #2 class ClassAfterMagicMacro_TestCase(unittest.TestCase): @@ -1339,7 +1339,7 @@ def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_class_exists(self): - self.assertEqual(self.cppHeader.classes.has_key("ClassAfterMagicMacro"), True) + self.assertEqual("ClassAfterMagicMacro" in self.cppHeader.classes, True) # Bug BitBucket #3 class FilterMagicMacro_TestCase(unittest.TestCase): @@ -1377,19 +1377,19 @@ def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_uint_exists(self): - self.assertEqual(self.cppHeader.typedefs.has_key("uint"), True) + self.assertEqual("uint" in self.cppHeader.typedefs, True) def test_string_array_exists(self): - self.assertEqual(self.cppHeader.typedefs.has_key("string_array"), True) + self.assertEqual("string_array" in self.cppHeader.typedefs, True) def test_SmartObjPtr_exists(self): - self.assertEqual(self.cppHeader.typedefs.has_key("SmartObjPtr"), True) + self.assertEqual("SmartObjPtr" in self.cppHeader.typedefs, True) def test_StrStrMap_exists(self): - self.assertEqual(self.cppHeader.typedefs.has_key("StrStrMap"), True) + self.assertEqual("StrStrMap" in self.cppHeader.typedefs, True) def test_AfterTypedefClass_exists(self): - self.assertEqual(self.cppHeader.classes.has_key("AfterTypedefClass"), True) + self.assertEqual("AfterTypedefClass" in self.cppHeader.classes, True) # Bug BitBucket #6 class LineNumAfterDivide_TestCase(unittest.TestCase): @@ -1407,7 +1407,7 @@ def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_HerbCilantro_exists(self): - self.assertEqual(self.cppHeader.classes.has_key("Herb::Cilantro"), True) + self.assertEqual("Herb::Cilantro" in self.cppHeader.classes, True) # Bug BitBucket #7 class print_statement_TestCase(unittest.TestCase): @@ -1449,10 +1449,10 @@ def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_Peach_exists(self): - self.assertEqual(self.cppHeader.classes.has_key("Peach"), True) + self.assertEqual("Peach" in self.cppHeader.classes, True) def test_Plumb_exists(self): - self.assertEqual(self.cppHeader.classes.has_key("Plumb"), True) + self.assertEqual("Plumb" in self.cppHeader.classes, True) def test_function_exists(self): self.assertEqual(self.cppHeader.classes["Plumb"]["methods"]["private"][0]["name"], "doSomethingGreat") @@ -1464,7 +1464,7 @@ def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_Grape_exists(self): - self.assertEqual(self.cppHeader.classes.has_key("Grape"), True) + self.assertEqual("Grape" in self.cppHeader.classes, True) def test_a_exists(self): self.assertEqual(self.cppHeader.classes["Grape"]["properties"]["public"][0]["name"], "a") @@ -1497,7 +1497,7 @@ def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_Avacado_exists(self): - self.assertEqual(self.cppHeader.classes.has_key("Avacado"), True) + self.assertEqual("Avacado" in self.cppHeader.classes, True) def test_foo_return_type(self): self.assertEqual(self.cppHeader.classes["Avacado"]["methods"]["public"][0]["returns"], "uint8_t") @@ -1512,13 +1512,13 @@ def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_anon_struct_1_exists(self): - self.assertEqual(self.cppHeader.classes.has_key(""), True) + self.assertEqual("" in self.cppHeader.classes, True) def test_beta_exists(self): self.assertEqual(self.cppHeader.classes[""]["properties"]["public"][0]["name"], "anon_struct_variable") def test_Raspberry_exists(self): - self.assertEqual(self.cppHeader.classes.has_key("Raspberry"), True) + self.assertEqual("Raspberry" in self.cppHeader.classes, True) def test_a_exists(self): self.assertEqual(self.cppHeader.classes["Raspberry"]["properties"]["public"][0]["name"], "a") @@ -1607,7 +1607,7 @@ def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_name(self): - self.assertEqual(self.cppHeader.typedefs.has_key("TenCharArray[10]"), True) + self.assertEqual("TenCharArray[10]" in self.cppHeader.typedefs, True) def test_value(self): self.assertEqual(self.cppHeader.typedefs["TenCharArray[10]"], "char") @@ -1619,7 +1619,7 @@ def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_name(self): - self.assertEqual(self.cppHeader.typedefs.has_key("MAGIC_FILE"), True) + self.assertEqual("MAGIC_FILE" in self.cppHeader.typedefs, True) def test_value(self): self.assertEqual(self.cppHeader.typedefs["MAGIC_FILE"], "struct SUPER_MAGIC_FILE") @@ -1646,7 +1646,7 @@ def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_Apricot_exists(self): - self.assertEqual(self.cppHeader.classes.has_key("Apricot"), True) + self.assertEqual("Apricot" in self.cppHeader.classes, True) def test_i_exists(self): self.assertEqual(self.cppHeader.classes["Apricot"]["members"][0]["name"], "i") @@ -1715,7 +1715,7 @@ def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_MouseClass_exists(self): - self.assertEqual(self.cppHeader.classes.has_key("MouseClass"), True) + self.assertEqual("MouseClass" in self.cppHeader.classes, True) def test_mouse_typedef_correct_value(self): self.assertEqual(self.cppHeader.classes["MouseClass"]["methods"]["public"][0]["parameters"][0]['raw_type'], @@ -1728,7 +1728,7 @@ def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_Fig_exists(self): - self.assertEqual(self.cppHeader.classes.has_key("Fig"), True) + self.assertEqual("Fig" in self.cppHeader.classes, True) def test_a_exists(self): self.assertEqual(self.cppHeader.classes["Grape"]["properties"]["public"][0]["name"], "a") @@ -1740,7 +1740,7 @@ def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_Olive_exists(self): - self.assertEqual(self.cppHeader.classes.has_key("union olive"), True) + self.assertEqual("union olive" in self.cppHeader.classes, True) def test_union_member_x(self): cmp_values = {'constant': 0, 'name': 'x', 'reference': 0, 'type': 'int', 'static': 0, 'pointer': 0} @@ -1753,7 +1753,7 @@ def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_Beet_exists(self): - self.assertEqual(self.cppHeader.classes.has_key("BeetStruct"), True) + self.assertEqual("BeetStruct" in self.cppHeader.classes, True) def test_BeetEnum_exists(self): self.assertEqual(self.cppHeader.classes["BeetStruct"]["enums"]["public"][0]["name"], "BeetEnum") From 45d6eef1f427c5ca065fe6dc1fb747ca08948ae5 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Tue, 11 Dec 2018 02:03:02 -0500 Subject: [PATCH 003/143] Make tests work on travis --- .travis.yml | 44 ++++++++++++++++++++ CppHeaderParser/test/test_CppHeaderParser.py | 10 ++--- 2 files changed, 48 insertions(+), 6 deletions(-) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..abea3b7 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,44 @@ +language: python +sudo: required +dist: xenial + +python: + - "3.7" + - "3.6" + - "3.5" + - "2.7" + +matrix: + fast_finish: true + +# jobs: +# include: +# - stage: format-check +# python: +# - "3.6" +# install: +# - pip install black +# script: +# - black --check --diff . + +# command to install dependencies +install: + - "pip install -e ." +# command to run tests +script: + - cd CppHeaderParser/test/ && python test_CppHeaderParser.py +deploy: +- provider: pypi + user: $PYPI_USERNAME + password: $PYPI_PASSWORD + distributions: sdist bdist_wheel + on: + tags: true + python: "3.6" +notifications: + webhooks: + urls: + - https://webhooks.gitter.im/e/2fb1c026e64fdd70d27b + on_success: change + on_failure: always + on_start: never diff --git a/CppHeaderParser/test/test_CppHeaderParser.py b/CppHeaderParser/test/test_CppHeaderParser.py index fabd7fd..35638e9 100644 --- a/CppHeaderParser/test/test_CppHeaderParser.py +++ b/CppHeaderParser/test/test_CppHeaderParser.py @@ -1,12 +1,10 @@ # -*- coding: utf-8 -*- import unittest import sys -if sys.version_info[0] == 2: - sys.path = [".."] + sys.path - import CppHeaderParser as CppHeaderParser -else: - sys.path = ["..", "../python3-libs"] + sys.path - import CppHeaderParser3 as CppHeaderParser + +sys.path = [".."] + sys.path +import CppHeaderParser as CppHeaderParser + def filter_pameters(p): "Reduce a list of dictionaries to the desired keys for function parameter testing" From cc83c5d2ae0e41ca2897529254738c02bc94780f Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Tue, 11 Dec 2018 02:34:44 -0500 Subject: [PATCH 004/143] Customize the project --- .gitignore | 2 ++ CppHeaderParser/CppHeaderParser.py | 7 +++- MANIFEST.in | 1 + Makefile | 40 ----------------------- README.md | 49 ++++++++++++++++++++++++++++ setup.py | 51 ++++++++++++++++++++++++------ 6 files changed, 100 insertions(+), 50 deletions(-) delete mode 100644 Makefile create mode 100644 README.md diff --git a/.gitignore b/.gitignore index d15acba..7ad0fca 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,7 @@ build dist +/CppHeaderParser/version.py + __pycache__ .vscode \ No newline at end of file diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index d24976c..0e1dea9 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -59,7 +59,12 @@ def lineno(): """Returns the current line number in our program.""" return inspect.currentframe().f_back.f_lineno -version = __version__ = "2.7.4" +try: + from .version import __version__ +except ImportError: + __version__ = 'devel' + +version = __version__ tokens = [ 'NUMBER', diff --git a/MANIFEST.in b/MANIFEST.in index e608a82..40ab763 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,5 +1,6 @@ include README.txt include README.html +include LICENSE.txt include CppHeaderParser/doc/CppHeaderParser.html include CppHeaderParser/examples/readSampleClass.py include CppHeaderParser/examples/SampleClass.h diff --git a/Makefile b/Makefile deleted file mode 100644 index cc76c13..0000000 --- a/Makefile +++ /dev/null @@ -1,40 +0,0 @@ -all: package - -doc: - @pydoc -w CppHeaderParser/CppHeaderParser.py && mv CppHeaderParser.html CppHeaderParser/doc - @python doc_generator.py - -test: - @echo "" - @echo "" - @echo "Testing Python 2.x" - @(cd CppHeaderParser/test; python test_CppHeaderParser.py) - @echo "" - @echo "" - @echo "Testing Python 3.x" - @if [ ! -e CppHeaderParser/python3-libs ]; \ - then \ - echo "Can't test python3 version without CppHeaderParser/python3-libs containing"; \ - echo " * ply"; \ - echo " * unittest"; \ - exit 1; \ - fi; - @(cd CppHeaderParser/test; python3 test_CppHeaderParser3.py) - -package: doc - @python setup.py sdist --formats=gztar,zip - -install: doc - @python setup.py install - - -upload: doc - @python setup.py sdist upload - - -help: - @echo "doc - Build Documentation" - @echo "test - Run regression tests" - @echo "package - Build a distributable package" - @echo "install - Install the CppHeaderParser package" - @echo "upload - Upload the latest package to pypi" diff --git a/README.md b/README.md new file mode 100644 index 0000000..bbc7a35 --- /dev/null +++ b/README.md @@ -0,0 +1,49 @@ +robotpy-cppheaderparser +======================= + +[![Build Status](https://travis-ci.org/robotpy/robotpy-cppheaderparser.svg?branch=master)](https://travis-ci.org/robotpy/robotpy-cppheaderparser) + +CppHeaderParser is a pure python C++ header parser that parses C++ headers +and creates a data structure that you can use to do many types of things. +We've found it particularly useful for creating programs that generate +python wrappers around existing C++ programs. + +robotpy-cppheaderparser is a fork of the [CppHeaderParser](https://bitbucket.org/senex/cppheaderparser) +library originally created by @senex. CppHeaderParser is an excellent +library and critical to some of the stuff we do in the RobotPy project. +Unfortunately, the maintainer seems to be busy, so robotpy-cppheaderparser +was born. + +We don't currently intend to develop new features, but aim to maintain +compatibility with the existing code and make improvements and bugfixes as +we need them. + +If you find an bug, we encourage you to submit a pull request! New changes +will only be accepted if there are tests to cover the change you made (and +if they don't break existing tests). + +Install +------- + + pip install robotpy-cppheaderparser + +Usage +----- + +[See the examples](CppHeaderParser/examples). + +License +------- + +BSD License + +Authors +------- + +Originally developed by Jashua Cloutier, this fork is maintained by the RobotPy +project. + +Past contributors include: +* Jashua Cloutier +* Chris Love +* HartsAntler diff --git a/setup.py b/setup.py index 9d1df9a..87046f4 100644 --- a/setup.py +++ b/setup.py @@ -1,11 +1,42 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -import sys, glob -try: - from setuptools import setup -except ImportError: - from distutils.core import setup +from __future__ import print_function + +from os.path import dirname, exists, join +import sys, subprocess + +from setuptools import find_packages, setup + +setup_dir = dirname(__file__) +git_dir = join(setup_dir, ".git") +version_file = join(setup_dir, "CppHeaderParser", "version.py") + +# Automatically generate a version.py based on the git version +if exists(git_dir): + p = subprocess.Popen( + ["git", "describe", "--tags", "--long", "--dirty=-dirty"], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + out, err = p.communicate() + # Make sure the git version has at least one tag + if err: + print("Error: You need to create a tag for this repo to use the builder") + sys.exit(1) + + # Convert git version to PEP440 compliant version + # - Older versions of pip choke on local identifiers, so we can't include the git commit + v, commits, local = out.decode("utf-8").rstrip().split("-", 2) + if commits != "0" or "-dirty" in local: + v = "%s.post0.dev%s" % (v, commits) + + # Create the version.py file + with open(version_file, "w") as fp: + fp.write("# Autogenerated by setup.py\n__version__ = '{0}'".format(v)) + +with open(version_file, "r") as fp: + exec(fp.read(), globals()) DESCRIPTION = ( 'Parse C++ header files and generate a data structure ' @@ -29,13 +60,15 @@ ] setup( - name = 'CppHeaderParser', - version = '2.7.4', + name = 'robotpy-cppheaderparser', + version = __version__, author = 'Jashua Cloutier', author_email = 'jashuac@bellsouth.net', - url = 'http://senexcanis.com/open-source/cppheaderparser/', + maintainer = 'RobotPy Development Team', + maintainer_email = 'robotpy@googlegroups.com', + url = 'https://github.com/robotpy/robotpy-cppheaderparser', description = DESCRIPTION, - long_description = open('README.txt').read(), + long_description = open('README.md').read(), license = 'BSD', platforms = 'Platform Independent', packages = ['CppHeaderParser'], From ed3bb6383e816477531d245d193c708054a50df3 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Tue, 11 Dec 2018 02:41:27 -0500 Subject: [PATCH 005/143] Fix the tests --- CppHeaderParser/test/test_CppHeaderParser.py | 1 - 1 file changed, 1 deletion(-) diff --git a/CppHeaderParser/test/test_CppHeaderParser.py b/CppHeaderParser/test/test_CppHeaderParser.py index 35638e9..a8de032 100644 --- a/CppHeaderParser/test/test_CppHeaderParser.py +++ b/CppHeaderParser/test/test_CppHeaderParser.py @@ -2,7 +2,6 @@ import unittest import sys -sys.path = [".."] + sys.path import CppHeaderParser as CppHeaderParser From 5c7aa6b1a94e235a5f8d5223a47a6734f0597545 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Tue, 11 Dec 2018 02:48:52 -0500 Subject: [PATCH 006/143] Fix tests to not interfere with deploy --- .travis.yml | 2 +- run_tests.sh | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100755 run_tests.sh diff --git a/.travis.yml b/.travis.yml index abea3b7..35f8643 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,7 +26,7 @@ install: - "pip install -e ." # command to run tests script: - - cd CppHeaderParser/test/ && python test_CppHeaderParser.py + - ./run_tests.sh deploy: - provider: pypi user: $PYPI_USERNAME diff --git a/run_tests.sh b/run_tests.sh new file mode 100755 index 0000000..ff74e60 --- /dev/null +++ b/run_tests.sh @@ -0,0 +1,4 @@ +#!/bin/sh -e + +cd CppHeaderParser/test/ +python test_CppHeaderParser.py From da3fc78636fe9a326185fbd3c8af2e8baaf0a5c9 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Wed, 5 Dec 2018 23:17:30 -0500 Subject: [PATCH 007/143] Don't recurse infinitely when resolving types - Fixes #45 --- CppHeaderParser/CppHeaderParser.py | 3 +++ CppHeaderParser/test/test_CppHeaderParser.py | 27 ++++++++++++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 0e1dea9..6523f8e 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -1282,6 +1282,9 @@ def resolve_type( self, string, result ): # recursive # result['forward_decl'] = True if alias == '__extension__': result['fundamental_extension'] = True elif alias: + if alias in result['aliases']: + # already resolved + return result['aliases'].append( alias ) if alias in C99_NONSTANDARD: result['type'] = C99_NONSTANDARD[ alias ] diff --git a/CppHeaderParser/test/test_CppHeaderParser.py b/CppHeaderParser/test/test_CppHeaderParser.py index a8de032..356a951 100644 --- a/CppHeaderParser/test/test_CppHeaderParser.py +++ b/CppHeaderParser/test/test_CppHeaderParser.py @@ -1754,8 +1754,31 @@ def test_Beet_exists(self): def test_BeetEnum_exists(self): self.assertEqual(self.cppHeader.classes["BeetStruct"]["enums"]["public"][0]["name"], "BeetEnum") - - + +# BitBucket bug 45 +class HALControlWord_TestCase(unittest.TestCase): + + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader("""\ + struct HAL_ControlWord { + int x : 1; + int y : 1; + }; + typedef struct HAL_ControlWord HAL_ControlWord; + int HAL_GetControlWord(HAL_ControlWord* controlWord); + """, "string") + + def test_functions(self): + self.assertEqual(len(self.cppHeader.functions), 1) + self.assertEqual(self.cppHeader.functions[0]["name"], "HAL_GetControlWord") + + def test_classes(self): + self.assertEqual(len(self.cppHeader.classes), 1) + self.assertEqual(self.cppHeader.classes["HAL_ControlWord"]["name"], "HAL_ControlWord") + + def test_num_typedefs(self): + self.assertEqual(len(self.cppHeader.typedefs), 1) + self.assertEqual(self.cppHeader.typedefs["HAL_ControlWord"], "struct HAL_ControlWord") if __name__ == '__main__': unittest.main() From 714fbd0814e03904e8da1f0b91772e2565e8b42d Mon Sep 17 00:00:00 2001 From: Dennis Lim Date: Sun, 11 Feb 2018 15:10:34 +0800 Subject: [PATCH 008/143] add test case for function pointer in parameter fix for above test case. updated test to use 'in' insetead of has_key for python3 compatibility updated to use list comprehension instead of filter for python3 compatibility add .hgignore to ignore __pycache__ --- CppHeaderParser/CppHeaderParser.py | 16 +++++++--------- CppHeaderParser/test/TestSampleClass.h | 4 +++- CppHeaderParser/test/test_CppHeaderParser.py | 13 +++++++++++++ 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 6523f8e..d8426c5 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -738,14 +738,7 @@ def _params_helper1( self, stack ): stack = stack[ : len(stack)-(_end_+1) ] if '(' not in stack: return stack # safe to return, no defaults that init a class - # transforms ['someclass', '(', '0', '0', '0', ')'] into "someclass(0,0,0)'" - r = []; hit=False - for a in stack: - if a == '(': hit=True - elif a == ')': hit=False - if hit or a == ')': r[-1] = r[-1] + a - else: r.append( a ) - return r + return stack def _params_helper2( self, params ): for p in params: @@ -864,6 +857,7 @@ def __init__(self, nameStack, curClass, methinfo, curTemplate): while (len(paramsStack)): # Find commas that are not nexted in <>'s like template types open_template_count = 0 + open_paren_count = 0 param_separator = 0 i = 0 for elm in paramsStack: @@ -871,7 +865,11 @@ def __init__(self, nameStack, curClass, methinfo, curTemplate): open_template_count += 1 elif '>' in elm: open_template_count -= 1 - elif elm == ',' and open_template_count == 0: + elif '(' in elm : + open_paren_count += 1 + elif ')' in elm: + open_paren_count -= 1 + elif elm == ',' and open_template_count == 0 and open_paren_count==0: param_separator = i break i += 1 diff --git a/CppHeaderParser/test/TestSampleClass.h b/CppHeaderParser/test/TestSampleClass.h index b803203..7473ca2 100644 --- a/CppHeaderParser/test/TestSampleClass.h +++ b/CppHeaderParser/test/TestSampleClass.h @@ -753,4 +753,6 @@ typedef struct FAIL = 0, PASS = 1 }; -} BeetStruct; \ No newline at end of file +} BeetStruct; + +void set_callback(int* b, long (*callback) (struct test_st *, int, const char*, int long, long, long)); \ No newline at end of file diff --git a/CppHeaderParser/test/test_CppHeaderParser.py b/CppHeaderParser/test/test_CppHeaderParser.py index 356a951..9323180 100644 --- a/CppHeaderParser/test/test_CppHeaderParser.py +++ b/CppHeaderParser/test/test_CppHeaderParser.py @@ -1755,6 +1755,18 @@ def test_Beet_exists(self): def test_BeetEnum_exists(self): self.assertEqual(self.cppHeader.classes["BeetStruct"]["enums"]["public"][0]["name"], "BeetEnum") +# BitBucket bug 40 +class set_callback_TestCase(unittest.TestCase): + + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") + + def test_set_callback(self): + self.assertEqual(self.cppHeader.functions[8]["name"], "set_callback") + self.assertEqual(self.cppHeader.functions[8]["parameters"][1]["name"], "callback") + self.assertEqual(self.cppHeader.functions[8]["parameters"][1]["function_pointer"], 1) + self.assertEqual(self.cppHeader.functions[8]["parameters"][1]["type"], "long ( * ) ( struct test_st *, int, const char *, int long, long, long )") + # BitBucket bug 45 class HALControlWord_TestCase(unittest.TestCase): @@ -1780,6 +1792,7 @@ def test_num_typedefs(self): self.assertEqual(len(self.cppHeader.typedefs), 1) self.assertEqual(self.cppHeader.typedefs["HAL_ControlWord"], "struct HAL_ControlWord") + if __name__ == '__main__': unittest.main() From cfa2027bd3c5c9f5fb0700d5749a38c12752da1f Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Fri, 7 Dec 2018 13:09:43 -0500 Subject: [PATCH 009/143] Allow single-line comment at end of file - Fixes #47 --- CppHeaderParser/CppHeaderParser.py | 2 +- CppHeaderParser/test/test_CppHeaderParser.py | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index d8426c5..430d6a8 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -125,7 +125,7 @@ def lineno(): t_PRECOMP_MACRO = r'\#.*' t_PRECOMP_MACRO_CONT = r'.*\\\n' def t_COMMENT_SINGLELINE(t): - r'\/\/.*\n' + r'\/\/.*\n?' global doxygenCommentCache if t.value.startswith("///") or t.value.startswith("//!"): if doxygenCommentCache: diff --git a/CppHeaderParser/test/test_CppHeaderParser.py b/CppHeaderParser/test/test_CppHeaderParser.py index 9323180..b0f3655 100644 --- a/CppHeaderParser/test/test_CppHeaderParser.py +++ b/CppHeaderParser/test/test_CppHeaderParser.py @@ -1791,8 +1791,19 @@ def test_classes(self): def test_num_typedefs(self): self.assertEqual(len(self.cppHeader.typedefs), 1) self.assertEqual(self.cppHeader.typedefs["HAL_ControlWord"], "struct HAL_ControlWord") - - + +# Bitbucket bug 47 +class CommentEOF_TestCase(unittest.TestCase): + + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader(""" +namespace a { +} // namespace a""", "string") + + def test_comment(self): + self.assertTrue('a' in self.cppHeader.namespaces) + + if __name__ == '__main__': unittest.main() From 7769b8a6d48cbf6143da4194a01ee9caea2d0604 Mon Sep 17 00:00:00 2001 From: Joshua Cannon Date: Tue, 30 May 2017 15:43:50 +0000 Subject: [PATCH 010/143] Adding noexcept specifier/operator support --- CppHeaderParser/CppHeaderParser.py | 17 ++++++++++ CppHeaderParser/test/TestSampleClass.h | 14 ++++++++- CppHeaderParser/test/test_CppHeaderParser.py | 33 ++++++++++++++++++++ 3 files changed, 63 insertions(+), 1 deletion(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 430d6a8..fef4c22 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -790,6 +790,23 @@ def __init__(self, nameStack, curClass, methinfo, curTemplate): self["rtnType"] = self["rtnType"].replace(" >",">").replace(">>", "> >").replace(">>", "> >") self["rtnType"] = self["rtnType"].replace(" ,",",") + # deal with "noexcept" specifier/operator + cleaned = [] + hit = False; parentCount = 0 + self['noexcept'] = '' + for a in stack: + if a == 'noexcept': hit = True + if hit: + if a == '(': parentCount += 1 + elif a == ')': parentCount -= 1 + elif parentCount == 0 and a != 'noexcept': hit = False; cleaned.append( a ); continue # noexcept without parenthesis + if a==')' and parentCount == 0: hit = False + self['noexcept'] += a + else: + cleaned.append( a ) + stack = cleaned + self['noexcept'] = self['noexcept'] if self['noexcept'] else None + for spec in ["const", "final", "override"]: self[spec] = False for i in reversed(nameStack): diff --git a/CppHeaderParser/test/TestSampleClass.h b/CppHeaderParser/test/TestSampleClass.h index 7473ca2..ad6caee 100644 --- a/CppHeaderParser/test/TestSampleClass.h +++ b/CppHeaderParser/test/TestSampleClass.h @@ -755,4 +755,16 @@ typedef struct }; } BeetStruct; -void set_callback(int* b, long (*callback) (struct test_st *, int, const char*, int long, long, long)); \ No newline at end of file +void set_callback(int* b, long (*callback) (struct test_st *, int, const char*, int long, long, long)); + +// Bitbucket bug 35 +struct Grackle +{ + void no_noexcept(); + void just_noexcept() noexcept; + void const_noexcept() const noexcept; + void noexcept_bool() noexcept(true); + void const_noexcept_bool() const noexcept(true); + void noexcept_noexceptOperator() noexcept(noexcept(Grackle())); + void const_noexcept_noexceptOperator() const noexcept(noexcept(Grackle())); +}; \ No newline at end of file diff --git a/CppHeaderParser/test/test_CppHeaderParser.py b/CppHeaderParser/test/test_CppHeaderParser.py index b0f3655..d4cfbc2 100644 --- a/CppHeaderParser/test/test_CppHeaderParser.py +++ b/CppHeaderParser/test/test_CppHeaderParser.py @@ -1803,6 +1803,39 @@ def setUp(self): def test_comment(self): self.assertTrue('a' in self.cppHeader.namespaces) +# BitBucket bug 35 +class Grackle_TestCase(unittest.TestCase): + + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") + + def test_Grackle_exists(self): + self.assertEqual(self.cppHeader.classes.has_key("Grackle"), True) + + def test_Grackle_no_noexcept_None(self): + self.assertEqual(self.cppHeader.classes["Grackle"]["methods"]["public"][0]["noexcept"], None) + + def test_Grackle_noexcept(self): + self.assertEqual(self.cppHeader.classes["Grackle"]["methods"]["public"][1]["noexcept"], 'noexcept') + + def test_Grackle_const_noexcept(self): + self.assertEqual(self.cppHeader.classes["Grackle"]["methods"]["public"][2]["const"], True) + self.assertEqual(self.cppHeader.classes["Grackle"]["methods"]["public"][2]["noexcept"], 'noexcept') + + def test_Grackle_noexcept_true(self): + self.assertEqual(self.cppHeader.classes["Grackle"]["methods"]["public"][3]["noexcept"], 'noexcept(true)') + + def test_Grackle_const_noexcept_true(self): + self.assertEqual(self.cppHeader.classes["Grackle"]["methods"]["public"][4]["const"], True) + self.assertEqual(self.cppHeader.classes["Grackle"]["methods"]["public"][4]["noexcept"], 'noexcept(true)') + + def test_Grackle_noexcept_noexcept_operator(self): + self.assertEqual(self.cppHeader.classes["Grackle"]["methods"]["public"][5]["noexcept"], 'noexcept(noexcept(Grackle()))') + + def test_Grackle_const_noexcept_noexcept_operator(self): + self.assertEqual(self.cppHeader.classes["Grackle"]["methods"]["public"][6]["const"], True) + self.assertEqual(self.cppHeader.classes["Grackle"]["methods"]["public"][6]["noexcept"], 'noexcept(noexcept(Grackle()))') + if __name__ == '__main__': unittest.main() From c517316970ca1106b3ff9aab444f1d54fad1b4eb Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Tue, 11 Dec 2018 03:10:47 -0500 Subject: [PATCH 011/143] Fix tests --- CppHeaderParser/CppHeaderParser.py | 4 ++-- CppHeaderParser/test/test_CppHeaderParser.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index fef4c22..55f8e3c 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -794,7 +794,7 @@ def __init__(self, nameStack, curClass, methinfo, curTemplate): cleaned = [] hit = False; parentCount = 0 self['noexcept'] = '' - for a in stack: + for a in nameStack: if a == 'noexcept': hit = True if hit: if a == '(': parentCount += 1 @@ -804,7 +804,7 @@ def __init__(self, nameStack, curClass, methinfo, curTemplate): self['noexcept'] += a else: cleaned.append( a ) - stack = cleaned + nameStack = cleaned self['noexcept'] = self['noexcept'] if self['noexcept'] else None for spec in ["const", "final", "override"]: diff --git a/CppHeaderParser/test/test_CppHeaderParser.py b/CppHeaderParser/test/test_CppHeaderParser.py index d4cfbc2..f5a6bcf 100644 --- a/CppHeaderParser/test/test_CppHeaderParser.py +++ b/CppHeaderParser/test/test_CppHeaderParser.py @@ -1810,7 +1810,7 @@ def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_Grackle_exists(self): - self.assertEqual(self.cppHeader.classes.has_key("Grackle"), True) + self.assertEqual("Grackle" in self.cppHeader.classes, True) def test_Grackle_no_noexcept_None(self): self.assertEqual(self.cppHeader.classes["Grackle"]["methods"]["public"][0]["noexcept"], None) From 505d0a35c815f88a15683674681a1a2f00d25fb5 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 6 Jan 2019 02:42:18 -0500 Subject: [PATCH 012/143] Update manifest for install --- MANIFEST.in | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index 40ab763..cd7befe 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,5 +1,4 @@ -include README.txt -include README.html +include README.md include LICENSE.txt include CppHeaderParser/doc/CppHeaderParser.html include CppHeaderParser/examples/readSampleClass.py From 0e2b53d911d6ee4a7e2b4c6089d06f1913578f3e Mon Sep 17 00:00:00 2001 From: Paul Date: Tue, 8 Jan 2019 21:58:15 +0000 Subject: [PATCH 013/143] Adding flag to identify vararg functions --- CppHeaderParser/CppHeaderParser.py | 16 ++++++++++++++-- CppHeaderParser/test/TestSampleClass.h | 7 ++++++- CppHeaderParser/test/test_CppHeaderParser.py | 13 +++++++++++++ 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 55f8e3c..7d53f05 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -100,9 +100,11 @@ def lineno(): 'STRING_LITERAL', 'NEW_LINE', 'SQUOTE', + 'ELLIPSIS', + 'DOT', ] -t_ignore = " \r.?@\f" +t_ignore = " \r?@\f" t_NUMBER = r'[0-9][0-9XxA-Fa-f]*' t_FLOAT_NUMBER = r'[-+]?[0-9]*\.[0-9]+([eE][-+]?[0-9]+)?' t_TEMPLATE_NAME = r'CppHeaderParser_template_[0-9]+' @@ -143,6 +145,8 @@ def t_COMMENT_SINGLELINE(t): t_EQUALS = r'=' t_CHAR_LITERAL = "'.'" t_SQUOTE = "'" +t_ELLIPSIS = r'\.\.\.' +t_DOT = r'\.' #found at http://wordaligned.org/articles/string-literals-and-regular-expressions #TODO: This does not work with the string "bla \" bla" t_STRING_LITERAL = r'"([^"\\]|\\.)*"' @@ -869,7 +873,9 @@ def __init__(self, nameStack, curClass, methinfo, curTemplate): doxyLine = doxyLine[doxyLine.find(" ") + 1:] doxyVarDesc[lastParamDesc] += " " + doxyLine except: pass - + + # non-vararg by default + self["vararg"] = False #Create the variable now while (len(paramsStack)): # Find commas that are not nexted in <>'s like template types @@ -895,6 +901,9 @@ def __init__(self, nameStack, curClass, methinfo, curTemplate): param = CppVariable(paramsStack[0:param_separator], doxyVarDesc=doxyVarDesc) if len(list(param.keys())): params.append(param) paramsStack = paramsStack[param_separator + 1:] + elif len(paramsStack) and paramsStack[0] == "...": + self["vararg"] = True + paramsStack = paramsStack[1:] else: param = CppVariable(paramsStack, doxyVarDesc=doxyVarDesc) if len(list(param.keys())): params.append(param) @@ -2350,6 +2359,9 @@ def __init__(self, headerFileName, argType="file", **kwargs): self.nameStack.append(tok.value) elif (tok.type == 'STRING_LITERAL'): self.nameStack.append(tok.value) + elif (tok.type == 'ELLIPSIS'): + self.nameStack.append(tok.value) + elif (tok.type == 'DOT'): pass # preserve behaviour and eat individual fullstops elif (tok.type == 'NAME' or tok.type == 'AMPERSTAND' or tok.type == 'ASTERISK' or tok.type == 'CHAR_LITERAL'): if tok.value in ignoreSymbols: debug_print("Ignore symbol %s"%tok.value) diff --git a/CppHeaderParser/test/TestSampleClass.h b/CppHeaderParser/test/TestSampleClass.h index ad6caee..f3bbe63 100644 --- a/CppHeaderParser/test/TestSampleClass.h +++ b/CppHeaderParser/test/TestSampleClass.h @@ -767,4 +767,9 @@ struct Grackle void const_noexcept_bool() const noexcept(true); void noexcept_noexceptOperator() noexcept(noexcept(Grackle())); void const_noexcept_noexceptOperator() const noexcept(noexcept(Grackle())); -}; \ No newline at end of file +}; + +// Two prototypes that are the same apart from the ... +int vararg_func(int foo, const char* fmt, ...); + +int non_vararg_func(int foo, const char* fmt); diff --git a/CppHeaderParser/test/test_CppHeaderParser.py b/CppHeaderParser/test/test_CppHeaderParser.py index f5a6bcf..f734164 100644 --- a/CppHeaderParser/test/test_CppHeaderParser.py +++ b/CppHeaderParser/test/test_CppHeaderParser.py @@ -1837,6 +1837,19 @@ def test_Grackle_const_noexcept_noexcept_operator(self): self.assertEqual(self.cppHeader.classes["Grackle"]["methods"]["public"][6]["noexcept"], 'noexcept(noexcept(Grackle()))') +class VarargFunc_TestCase(unittest.TestCase): + + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") + + def test_vararg_func(self): + vf = next(x for x in self.cppHeader.functions if x['name'] == 'vararg_func') + nvf = next(x for x in self.cppHeader.functions if x['name'] == 'non_vararg_func') + self.assertTrue(vf['vararg']) + self.assertFalse(nvf['vararg']) + self.assertEqual(len(vf['parameters']), len(nvf['parameters'])); + + if __name__ == '__main__': unittest.main() From 6313166ba3805276d194d2b264f1c074cb0be484 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20W=C3=BCthrich?= Date: Fri, 11 Jan 2019 10:31:07 +0100 Subject: [PATCH 014/143] added option to set file encoding --- CppHeaderParser/CppHeaderParser.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 7d53f05..e443386 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -2071,7 +2071,7 @@ class CppHeader( _CppHeader ): def show(self): for className in list(self.classes.keys()):self.classes[className].show() - def __init__(self, headerFileName, argType="file", **kwargs): + def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): """Create the parsed C++ header file parse tree headerFileName - Name of the file to parse OR actual file contents (depends on argType) @@ -2126,7 +2126,7 @@ def __init__(self, headerFileName, argType="file", **kwargs): self.templateRegistry = [] if (len(self.headerFileName)): - fd = open(self.headerFileName) + fd = open(self.headerFileName, encoding=encoding) headerFileStr = "".join(fd.readlines()) fd.close() From c1fb7a4937448c6f44b6e3625c87d304d44c089d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20W=C3=BCthrich?= Date: Mon, 11 Feb 2019 13:01:48 +0100 Subject: [PATCH 015/143] added support for supplying encoding for Python < 3 --- CppHeaderParser/CppHeaderParser.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index e443386..ccb399a 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -2126,10 +2126,18 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self.templateRegistry = [] if (len(self.headerFileName)): - fd = open(self.headerFileName, encoding=encoding) - headerFileStr = "".join(fd.readlines()) - fd.close() - + # Python 3.x + if sys.version_info >= (3, 0): + fd = open(self.headerFileName, encoding=encoding) + headerFileStr = "".join(fd.readlines()) + fd.close() + # Python 2.x + else: + import codecs + fd = codecs.open(self.headerFileName, 'r', encoding=encoding) + headerFileStr = "".join(fd.readlines()) + fd.close() + # Make sure supportedAccessSpecifier are sane for i in range(0, len(supportedAccessSpecifier)): if " " not in supportedAccessSpecifier[i]: continue From da632b0e5db916cc536ddbf0f5b41372d7ca4871 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20W=C3=BCthrich?= Date: Mon, 11 Feb 2019 13:11:32 +0100 Subject: [PATCH 016/143] fixed wrong whitespaces --- CppHeaderParser/CppHeaderParser.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index ccb399a..77ec316 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -2124,19 +2124,19 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self.anon_struct_counter = 0 self.anon_union_counter = [-1, 0] self.templateRegistry = [] - + if (len(self.headerFileName)): - # Python 3.x - if sys.version_info >= (3, 0): - fd = open(self.headerFileName, encoding=encoding) - headerFileStr = "".join(fd.readlines()) - fd.close() - # Python 2.x - else: - import codecs - fd = codecs.open(self.headerFileName, 'r', encoding=encoding) - headerFileStr = "".join(fd.readlines()) - fd.close() + # Python 3.x + if sys.version_info >= (3, 0): + fd = open(self.headerFileName, encoding=encoding) + headerFileStr = "".join(fd.readlines()) + fd.close() + # Python 2.x + else: + import codecs + fd = codecs.open(self.headerFileName, 'r', encoding=encoding) + headerFileStr = "".join(fd.readlines()) + fd.close() # Make sure supportedAccessSpecifier are sane for i in range(0, len(supportedAccessSpecifier)): From 53ee8950029ea88288effd5ed6d37dbdc0b5b811 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20W=C3=BCthrich?= Date: Mon, 11 Feb 2019 15:58:46 +0100 Subject: [PATCH 017/143] use open from module io instead of codecs --- CppHeaderParser/CppHeaderParser.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 77ec316..42369fb 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -2133,8 +2133,8 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): fd.close() # Python 2.x else: - import codecs - fd = codecs.open(self.headerFileName, 'r', encoding=encoding) + import io + fd = io.open(self.headerFileName, 'r', encoding=encoding) headerFileStr = "".join(fd.readlines()) fd.close() From 1615a219a5b01e0814ddfc4b0a734e35b93034d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20W=C3=BCthrich?= Date: Wed, 13 Feb 2019 11:06:08 +0100 Subject: [PATCH 018/143] use io module --- CppHeaderParser/CppHeaderParser.py | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 42369fb..8bce356 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -52,6 +52,7 @@ import os import sys import re +import io import inspect @@ -2126,17 +2127,9 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self.templateRegistry = [] if (len(self.headerFileName)): - # Python 3.x - if sys.version_info >= (3, 0): - fd = open(self.headerFileName, encoding=encoding) - headerFileStr = "".join(fd.readlines()) - fd.close() - # Python 2.x - else: - import io - fd = io.open(self.headerFileName, 'r', encoding=encoding) - headerFileStr = "".join(fd.readlines()) - fd.close() + fd = io.open(self.headerFileName, 'r', encoding=encoding) + headerFileStr = "".join(fd.readlines()) + fd.close() # Make sure supportedAccessSpecifier are sane for i in range(0, len(supportedAccessSpecifier)): From 295d2ed4a6b75703a6bfa6b88a65d8ee00e23ba0 Mon Sep 17 00:00:00 2001 From: FreeYourSoul Date: Mon, 12 Aug 2019 14:55:23 +0200 Subject: [PATCH 019/143] [issue 13] Enhancement : add default flag for default constructor or destructor --- CppHeaderParser/CppHeaderParser.py | 9 ++++- CppHeaderParser/test/TestSampleClass.h | 16 +++++++++ CppHeaderParser/test/test_CppHeaderParser.py | 37 +++++++++++++++++++- 3 files changed, 60 insertions(+), 2 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 8bce356..93c124f 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -1744,12 +1744,13 @@ def parse_method_type( self, stack ): 'namespace':self.cur_namespace(add_double_colon=True), } - for tag in 'defined pure_virtual operator constructor destructor extern template virtual static explicit inline friend returns returns_pointer returns_fundamental returns_class'.split(): info[tag]=False + for tag in 'defined pure_virtual operator constructor destructor extern template virtual static explicit inline friend returns returns_pointer returns_fundamental returns_class default'.split(): info[tag]=False header = stack[ : stack.index('(') ] header = ' '.join( header ) header = header.replace(' : : ', '::' ) header = header.replace(' < ', '<' ) header = header.replace(' > ', '> ' ) + header = header.replace('default ', 'default' ) header = header.strip() if '{' in stack: @@ -1804,9 +1805,15 @@ def parse_method_type( self, stack ): if name.startswith('~'): info['destructor'] = True + if 'default;' in stack: + info['defined'] = True + info['default'] = True name = name[1:] elif not a or (name == self.curClass and len(self.curClass)): info['constructor'] = True + if 'default;' in stack: + info['defined'] = True + info['default'] = True info['name'] = name diff --git a/CppHeaderParser/test/TestSampleClass.h b/CppHeaderParser/test/TestSampleClass.h index f3bbe63..4028d95 100644 --- a/CppHeaderParser/test/TestSampleClass.h +++ b/CppHeaderParser/test/TestSampleClass.h @@ -773,3 +773,19 @@ struct Grackle int vararg_func(int foo, const char* fmt, ...); int non_vararg_func(int foo, const char* fmt); + +// Sample class for testing default constructor destructor +class DefaultConstDest { +public: + DefaultConstDest() =default ; // spacing check + DefaultConstDest() = default ; // spacing check +}; +// default constructor on a class containing "default" as name (edge case check) +class default_class_tricky { +public: + default_class_tricky(); + default_class_tricky(); + + void randomMethod1_default(); + void defaultrandomMethod2(); +}; diff --git a/CppHeaderParser/test/test_CppHeaderParser.py b/CppHeaderParser/test/test_CppHeaderParser.py index f734164..dc55327 100644 --- a/CppHeaderParser/test/test_CppHeaderParser.py +++ b/CppHeaderParser/test/test_CppHeaderParser.py @@ -1836,6 +1836,41 @@ def test_Grackle_const_noexcept_noexcept_operator(self): self.assertEqual(self.cppHeader.classes["Grackle"]["methods"]["public"][6]["const"], True) self.assertEqual(self.cppHeader.classes["Grackle"]["methods"]["public"][6]["noexcept"], 'noexcept(noexcept(Grackle()))') +# Test enhancement 13 (default constructor / destructor) +class DefaultConstDest_TestCase(): + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") + + def test_DefaultConstDest_exists(self): + self.assertEqual("DefaultConstDest" in self.cppHeader.classes, True) + self.assertEqual("default_class_tricky" in self.cppHeader.classes, True) + + def test_DefaultConstDest_constructor_default(self): + self.assertEqual(self.cppHeader.classes["DefaultConstDest"]["methods"]["public"][0]["constructor"], True) + self.assertEqual(self.cppHeader.classes["DefaultConstDest"]["methods"]["public"][0]["default"], True) + self.assertEqual(self.cppHeader.classes["DefaultConstDest"]["methods"]["public"][0]["defined"], True) + + def test_DefaultConstDest_destructor_default(self): + self.assertEqual(self.cppHeader.classes["DefaultConstDest"]["methods"]["public"][1]["destructor"], True) + self.assertEqual(self.cppHeader.classes["DefaultConstDest"]["methods"]["public"][1]["default"], True) + self.assertEqual(self.cppHeader.classes["DefaultConstDest"]["methods"]["public"][1]["defined"], True) + + def test_DefaultConstDest_default_edgeCaseNaming(self): + self.assertEqual(self.cppHeader.classes["default_class_tricky"]["methods"]["public"][0]["constructor"], True) + self.assertEqual(self.cppHeader.classes["default_class_tricky"]["methods"]["public"][0]["default"], False) + self.assertEqual(self.cppHeader.classes["default_class_tricky"]["methods"]["public"][0]["defined"], False) + self.assertEqual(self.cppHeader.classes["default_class_tricky"]["methods"]["public"][1]["destructor"], True) + self.assertEqual(self.cppHeader.classes["default_class_tricky"]["methods"]["public"][1]["default"], False) + self.assertEqual(self.cppHeader.classes["default_class_tricky"]["methods"]["public"][1]["defined"], False) + self.assertEqual(self.cppHeader.classes["default_class_tricky"]["methods"]["public"][2]["name"], "randomMethod1_default") + self.assertEqual(self.cppHeader.classes["default_class_tricky"]["methods"]["public"][2]["destructor"], False) + self.assertEqual(self.cppHeader.classes["default_class_tricky"]["methods"]["public"][2]["default"], False) + self.assertEqual(self.cppHeader.classes["default_class_tricky"]["methods"]["public"][2]["defined"], False) + self.assertEqual(self.cppHeader.classes["default_class_tricky"]["methods"]["public"][3]["name"], "defaultrandomMethod2") + self.assertEqual(self.cppHeader.classes["default_class_tricky"]["methods"]["public"][3]["destructor"], False) + self.assertEqual(self.cppHeader.classes["default_class_tricky"]["methods"]["public"][3]["default"], False) + self.assertEqual(self.cppHeader.classes["default_class_tricky"]["methods"]["public"][3]["defined"], False) + class VarargFunc_TestCase(unittest.TestCase): @@ -1847,7 +1882,7 @@ def test_vararg_func(self): nvf = next(x for x in self.cppHeader.functions if x['name'] == 'non_vararg_func') self.assertTrue(vf['vararg']) self.assertFalse(nvf['vararg']) - self.assertEqual(len(vf['parameters']), len(nvf['parameters'])); + self.assertEqual(len(vf['parameters']), len(nvf['parameters'])) if __name__ == '__main__': From bea0b5aa91456229806d7a2c1d536df865bf1afd Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Tue, 24 Sep 2019 23:18:48 -0400 Subject: [PATCH 020/143] Format code with black - Fixes #8 --- .travis.yml | 18 +- CppHeaderParser/CppHeaderParser.py | 3096 +++++++++++------- CppHeaderParser/__init__.py | 2 +- CppHeaderParser/examples/readSampleClass.py | 22 +- CppHeaderParser/test/gen_test.py | 153 - CppHeaderParser/test/test_CppHeaderParser.py | 2210 +++++++++---- doc_generator.py | 52 - setup.py | 69 +- 8 files changed, 3430 insertions(+), 2192 deletions(-) delete mode 100644 CppHeaderParser/test/gen_test.py delete mode 100644 doc_generator.py diff --git a/.travis.yml b/.travis.yml index 35f8643..d57fc60 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,15 +11,15 @@ python: matrix: fast_finish: true -# jobs: -# include: -# - stage: format-check -# python: -# - "3.6" -# install: -# - pip install black -# script: -# - black --check --diff . +jobs: + include: + - stage: format-check + python: + - "3.6" + install: + - pip install black + script: + - black --check --diff . # command to install dependencies install: diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 93c124f..9fdae7b 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -56,79 +56,83 @@ import inspect + def lineno(): """Returns the current line number in our program.""" return inspect.currentframe().f_back.f_lineno + try: from .version import __version__ except ImportError: - __version__ = 'devel' + __version__ = "devel" version = __version__ tokens = [ - 'NUMBER', - 'FLOAT_NUMBER', - 'TEMPLATE_NAME', - 'NAME', - 'OPEN_PAREN', - 'CLOSE_PAREN', - 'OPEN_BRACE', - 'CLOSE_BRACE', - 'OPEN_SQUARE_BRACKET', - 'CLOSE_SQUARE_BRACKET', - 'COLON', - 'SEMI_COLON', - 'COMMA', - 'TAB', - 'BACKSLASH', - 'PIPE', - 'PERCENT', - 'EXCLAMATION', - 'CARET', - 'COMMENT_SINGLELINE', - 'COMMENT_MULTILINE', - 'PRECOMP_MACRO', - 'PRECOMP_MACRO_CONT', - 'ASTERISK', - 'AMPERSTAND', - 'EQUALS', - 'MINUS', - 'PLUS', - 'DIVIDE', - 'CHAR_LITERAL', - 'STRING_LITERAL', - 'NEW_LINE', - 'SQUOTE', - 'ELLIPSIS', - 'DOT', + "NUMBER", + "FLOAT_NUMBER", + "TEMPLATE_NAME", + "NAME", + "OPEN_PAREN", + "CLOSE_PAREN", + "OPEN_BRACE", + "CLOSE_BRACE", + "OPEN_SQUARE_BRACKET", + "CLOSE_SQUARE_BRACKET", + "COLON", + "SEMI_COLON", + "COMMA", + "TAB", + "BACKSLASH", + "PIPE", + "PERCENT", + "EXCLAMATION", + "CARET", + "COMMENT_SINGLELINE", + "COMMENT_MULTILINE", + "PRECOMP_MACRO", + "PRECOMP_MACRO_CONT", + "ASTERISK", + "AMPERSTAND", + "EQUALS", + "MINUS", + "PLUS", + "DIVIDE", + "CHAR_LITERAL", + "STRING_LITERAL", + "NEW_LINE", + "SQUOTE", + "ELLIPSIS", + "DOT", ] t_ignore = " \r?@\f" -t_NUMBER = r'[0-9][0-9XxA-Fa-f]*' -t_FLOAT_NUMBER = r'[-+]?[0-9]*\.[0-9]+([eE][-+]?[0-9]+)?' -t_TEMPLATE_NAME = r'CppHeaderParser_template_[0-9]+' -t_NAME = r'[<>A-Za-z_~][A-Za-z0-9_]*' -t_OPEN_PAREN = r'\(' -t_CLOSE_PAREN = r'\)' -t_OPEN_BRACE = r'{' -t_CLOSE_BRACE = r'}' -t_OPEN_SQUARE_BRACKET = r'\[' -t_CLOSE_SQUARE_BRACKET = r'\]' -t_SEMI_COLON = r';' -t_COLON = r':' -t_COMMA = r',' -t_TAB = r'\t' -t_BACKSLASH = r'\\' -t_PIPE = r'\|' -t_PERCENT = r'%' -t_CARET = r'\^' -t_EXCLAMATION = r'!' -t_PRECOMP_MACRO = r'\#.*' -t_PRECOMP_MACRO_CONT = r'.*\\\n' +t_NUMBER = r"[0-9][0-9XxA-Fa-f]*" +t_FLOAT_NUMBER = r"[-+]?[0-9]*\.[0-9]+([eE][-+]?[0-9]+)?" +t_TEMPLATE_NAME = r"CppHeaderParser_template_[0-9]+" +t_NAME = r"[<>A-Za-z_~][A-Za-z0-9_]*" +t_OPEN_PAREN = r"\(" +t_CLOSE_PAREN = r"\)" +t_OPEN_BRACE = r"{" +t_CLOSE_BRACE = r"}" +t_OPEN_SQUARE_BRACKET = r"\[" +t_CLOSE_SQUARE_BRACKET = r"\]" +t_SEMI_COLON = r";" +t_COLON = r":" +t_COMMA = r"," +t_TAB = r"\t" +t_BACKSLASH = r"\\" +t_PIPE = r"\|" +t_PERCENT = r"%" +t_CARET = r"\^" +t_EXCLAMATION = r"!" +t_PRECOMP_MACRO = r"\#.*" +t_PRECOMP_MACRO_CONT = r".*\\\n" + + def t_COMMENT_SINGLELINE(t): - r'\/\/.*\n?' + r"\/\/.*\n?" global doxygenCommentCache if t.value.startswith("///") or t.value.startswith("//!"): if doxygenCommentCache: @@ -137,37 +141,43 @@ def t_COMMENT_SINGLELINE(t): doxygenCommentCache += t.value[:-1] else: doxygenCommentCache += t.value - t.lexer.lineno += len([a for a in t.value if a=="\n"]) -t_ASTERISK = r'\*' -t_MINUS = r'\-' -t_PLUS = r'\+' -t_DIVIDE = r'/(?!/)' -t_AMPERSTAND = r'&' -t_EQUALS = r'=' + t.lexer.lineno += len([a for a in t.value if a == "\n"]) + + +t_ASTERISK = r"\*" +t_MINUS = r"\-" +t_PLUS = r"\+" +t_DIVIDE = r"/(?!/)" +t_AMPERSTAND = r"&" +t_EQUALS = r"=" t_CHAR_LITERAL = "'.'" t_SQUOTE = "'" -t_ELLIPSIS = r'\.\.\.' -t_DOT = r'\.' -#found at http://wordaligned.org/articles/string-literals-and-regular-expressions -#TODO: This does not work with the string "bla \" bla" +t_ELLIPSIS = r"\.\.\." +t_DOT = r"\." +# found at http://wordaligned.org/articles/string-literals-and-regular-expressions +# TODO: This does not work with the string "bla \" bla" t_STRING_LITERAL = r'"([^"\\]|\\.)*"' -#Found at http://ostermiller.org/findcomment.html +# Found at http://ostermiller.org/findcomment.html def t_COMMENT_MULTILINE(t): - r'/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/' + r"/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/" global doxygenCommentCache if t.value.startswith("/**") or t.value.startswith("/*!"): - #not sure why, but get double new lines + # not sure why, but get double new lines v = t.value.replace("\n\n", "\n") - #strip prefixing whitespace + # strip prefixing whitespace v = re.sub("\n[\s]+\*", "\n*", v) doxygenCommentCache += v - t.lexer.lineno += len([a for a in t.value if a=="\n"]) + t.lexer.lineno += len([a for a in t.value if a == "\n"]) + + def t_NEWLINE(t): - r'\n+' + r"\n+" t.lexer.lineno += len(t.value) + def t_error(v): - print(( "Lex error: ", v )) + print(("Lex error: ", v)) + lex.lex() # Controls error_print @@ -179,39 +189,43 @@ def t_error(v): # Controls trace_print debug_trace = 0 + def error_print(arg): - if print_errors: print(("[%4d] %s"%(inspect.currentframe().f_back.f_lineno, arg))) + if print_errors: + print(("[%4d] %s" % (inspect.currentframe().f_back.f_lineno, arg))) + def warning_print(arg): - if print_warnings: print(("[%4d] %s"%(inspect.currentframe().f_back.f_lineno, arg))) + if print_warnings: + print(("[%4d] %s" % (inspect.currentframe().f_back.f_lineno, arg))) + def debug_print(arg): global debug - if debug: print(("[%4d] %s"%(inspect.currentframe().f_back.f_lineno, arg))) + if debug: + print(("[%4d] %s" % (inspect.currentframe().f_back.f_lineno, arg))) + def trace_print(*arg): global debug_trace if debug_trace: - sys.stdout.write("[%s] "%(inspect.currentframe().f_back.f_lineno)) - for a in arg: sys.stdout.write("%s "%a) + sys.stdout.write("[%s] " % (inspect.currentframe().f_back.f_lineno)) + for a in arg: + sys.stdout.write("%s " % a) sys.stdout.write("\n") -supportedAccessSpecifier = [ - 'public', - 'protected', - 'private' -] -#Symbols to ignore, usually special macros -ignoreSymbols = [ - 'Q_OBJECT', -] +supportedAccessSpecifier = ["public", "protected", "private"] + +# Symbols to ignore, usually special macros +ignoreSymbols = ["Q_OBJECT"] doxygenCommentCache = "" -#Track what was added in what order and at what depth +# Track what was added in what order and at what depth parseHistory = [] + def is_namespace(nameStack): """Determines if a namespace is being specified""" if len(nameStack) == 0: @@ -220,6 +234,7 @@ def is_namespace(nameStack): return True return False + def is_enum_namestack(nameStack): """Determines if a namestack is an enum namestack""" if len(nameStack) == 0: @@ -230,11 +245,29 @@ def is_enum_namestack(nameStack): return True return False + def is_fundamental(s): for a in s.split(): - if a not in ["size_t", "struct", "union", "unsigned", "signed", "bool", "char", "short", "int", "float", "double", "long", "void", "*"]: return False + if a not in [ + "size_t", + "struct", + "union", + "unsigned", + "signed", + "bool", + "char", + "short", + "int", + "float", + "double", + "long", + "void", + "*", + ]: + return False return True + def is_function_pointer_stack(stack): """Count how many non-nested paranthesis are in the stack. Useful for determining if a stack is a function pointer""" paren_depth = 0 @@ -251,96 +284,125 @@ def is_function_pointer_stack(stack): elif e == "*" and last_e == "(" and paren_count == 0 and paren_depth == 1: star_after_first_paren = True last_e = e - + if star_after_first_paren and paren_count == 2: return True else: return False + def is_method_namestack(stack): r = False - if '(' not in stack: r = False - elif stack[0] == 'typedef': r = False # TODO deal with typedef function prototypes - #elif '=' in stack and stack.index('=') < stack.index('(') and stack[stack.index('=')-1] != 'operator': r = False #disabled July6th - allow all operators - elif 'operator' in stack: r = True # allow all operators - elif '{' in stack and stack.index('{') < stack.index('('): r = False # struct that looks like a method/class - elif '(' in stack and ')' in stack: - if '{' in stack and '}' in stack: r = True - elif stack[-1] == ';': + if "(" not in stack: + r = False + elif stack[0] == "typedef": + r = False # TODO deal with typedef function prototypes + # elif '=' in stack and stack.index('=') < stack.index('(') and stack[stack.index('=')-1] != 'operator': r = False #disabled July6th - allow all operators + elif "operator" in stack: + r = True # allow all operators + elif "{" in stack and stack.index("{") < stack.index("("): + r = False # struct that looks like a method/class + elif "(" in stack and ")" in stack: + if "{" in stack and "}" in stack: + r = True + elif stack[-1] == ";": if is_function_pointer_stack(stack): r = False else: r = True - elif '{' in stack: r = True # ideally we catch both braces... TODO - else: r = False - #Test for case of property set to something with parens such as "static const int CONST_A = (1 << 7) - 1;" - if r and "(" in stack and "=" in stack and 'operator' not in stack: - if stack.index("=") < stack.index("("): r = False + elif "{" in stack: + r = True # ideally we catch both braces... TODO + else: + r = False + # Test for case of property set to something with parens such as "static const int CONST_A = (1 << 7) - 1;" + if r and "(" in stack and "=" in stack and "operator" not in stack: + if stack.index("=") < stack.index("("): + r = False return r + def is_property_namestack(nameStack): r = False - if '(' not in nameStack and ')' not in nameStack: r = True - elif "(" in nameStack and "=" in nameStack and nameStack.index("=") < nameStack.index("("): r = True - #See if we are a function pointer - if not r and is_function_pointer_stack(nameStack): r = True + if "(" not in nameStack and ")" not in nameStack: + r = True + elif ( + "(" in nameStack + and "=" in nameStack + and nameStack.index("=") < nameStack.index("(") + ): + r = True + # See if we are a function pointer + if not r and is_function_pointer_stack(nameStack): + r = True return r + def detect_lineno(s): """Detect the line number for a given token string""" try: rtn = s.lineno() if rtn != -1: return rtn - except: pass + except: + pass global curLine - return curLine + return curLine + def filter_out_attribute_keyword(stack): """Strips __attribute__ and its parenthetical expression from the stack""" - if "__attribute__" not in stack: return stack + if "__attribute__" not in stack: + return stack try: - debug_print("Stripping __attribute__ from %s"% stack) + debug_print("Stripping __attribute__ from %s" % stack) attr_index = stack.index("__attribute__") - attr_end = attr_index + 1 #Assuming not followed by parenthetical expression which wont happen - #Find final paren - if stack[attr_index + 1] == '(': + attr_end = ( + attr_index + 1 + ) # Assuming not followed by parenthetical expression which wont happen + # Find final paren + if stack[attr_index + 1] == "(": paren_count = 1 for i in range(attr_index + 2, len(stack)): elm = stack[i] - if elm == '(': + if elm == "(": paren_count += 1 - elif elm == ')': + elif elm == ")": paren_count -= 1 if paren_count == 0: attr_end = i + 1 break new_stack = stack[0:attr_index] + stack[attr_end:] - debug_print("stripped stack is %s"% new_stack) + debug_print("stripped stack is %s" % new_stack) return new_stack except: return stack - + class TagStr(str): """Wrapper for a string that allows us to store the line number associated with it""" + lineno_reg = {} - def __new__(cls,*args,**kw): - new_obj = str.__new__(cls,*args) + + def __new__(cls, *args, **kw): + new_obj = str.__new__(cls, *args) if "lineno" in kw: TagStr.lineno_reg[id(new_obj)] = kw["lineno"] return new_obj - + def __del__(self): try: del TagStr.lineno_reg[id(self)] - except: pass - + except: + pass + def lineno(self): return TagStr.lineno_reg.get(id(self), -1) -class CppParseError(Exception): pass - + +class CppParseError(Exception): + pass + + class CppClass(dict): """Takes a name stack and turns it into a class @@ -387,54 +449,58 @@ class CppClass(dict): def get_all_methods(self): r = [] - for typ in supportedAccessSpecifier: r += self['methods'][typ] + for typ in supportedAccessSpecifier: + r += self["methods"][typ] return r - def get_all_method_names( self ): + def get_all_method_names(self): r = [] - for typ in supportedAccessSpecifier: r += self.get_method_names(typ) # returns list + for typ in supportedAccessSpecifier: + r += self.get_method_names(typ) # returns list return r - def get_all_pure_virtual_methods( self ): + def get_all_pure_virtual_methods(self): r = {} - for typ in supportedAccessSpecifier: r.update(self.get_pure_virtual_methods(typ)) # returns dict + for typ in supportedAccessSpecifier: + r.update(self.get_pure_virtual_methods(typ)) # returns dict return r + def get_method_names(self, type="public"): + return [meth["name"] for meth in self["methods"][type]] - def get_method_names( self, type='public' ): return [ meth['name'] for meth in self['methods'][ type ] ] - - def get_pure_virtual_methods( self, type='public' ): + def get_pure_virtual_methods(self, type="public"): r = {} - for meth in self['methods'][ type ]: - if meth['pure_virtual']: r[ meth['name'] ] = meth + for meth in self["methods"][type]: + if meth["pure_virtual"]: + r[meth["name"]] = meth return r def __init__(self, nameStack, curTemplate): - self['nested_classes'] = [] - self['parent'] = None - self['abstract'] = False + self["nested_classes"] = [] + self["parent"] = None + self["abstract"] = False self._public_enums = {} self._public_structs = {} self._public_typedefs = {} self._public_forward_declares = [] - self['namespace'] = "" + self["namespace"] = "" - debug_print( "Class: %s"%nameStack ) - debug_print( "Template: %s"%curTemplate) - - if (len(nameStack) < 2): - nameStack.insert(1, "")#anonymous struct + debug_print("Class: %s" % nameStack) + debug_print("Template: %s" % curTemplate) + + if len(nameStack) < 2: + nameStack.insert(1, "") # anonymous struct global doxygenCommentCache if len(doxygenCommentCache): self["doxygen"] = doxygenCommentCache doxygenCommentCache = "" - + if "::" in "".join(nameStack): - #Re-Join class paths (ex ['class', 'Bar', ':', ':', 'Foo'] -> ['class', 'Bar::Foo'] + # Re-Join class paths (ex ['class', 'Bar', ':', ':', 'Foo'] -> ['class', 'Bar::Foo'] try: new_nameStack = [] for name in nameStack: - if len(new_nameStack) == 0: + if len(new_nameStack) == 0: new_nameStack.append(name) elif name == ":" and new_nameStack[-1].endswith(":"): new_nameStack[-1] += name @@ -443,10 +509,13 @@ def __init__(self, nameStack, curTemplate): del new_nameStack[-1] else: new_nameStack.append(name) - trace_print("Convert from namestack\n %s\nto\n%s"%(nameStack, new_nameStack)) + trace_print( + "Convert from namestack\n %s\nto\n%s" % (nameStack, new_nameStack) + ) nameStack = new_nameStack - except: pass - + except: + pass + # Handle final specifier self["final"] = False try: @@ -455,105 +524,119 @@ def __init__(self, nameStack, curTemplate): del nameStack[final_index] self["final"] = True trace_print("final") - except: pass - + except: + pass + self["name"] = nameStack[1] self["line_number"] = detect_lineno(nameStack[0]) - - #Handle template classes + + # Handle template classes if len(nameStack) > 3 and nameStack[2].startswith("<"): open_template_count = 0 param_separator = 0 found_first = False i = 0 for elm in nameStack: - if '<' in elm : + if "<" in elm: open_template_count += 1 found_first = True - elif '>' in elm: + elif ">" in elm: open_template_count -= 1 if found_first and open_template_count == 0: - self["name"] = "".join(nameStack[1:i + 1]) - break; + self["name"] = "".join(nameStack[1 : i + 1]) + break i += 1 elif ":" in nameStack: - self['name'] = nameStack[ nameStack.index(':') - 1 ] + self["name"] = nameStack[nameStack.index(":") - 1] inheritList = [] - if nameStack.count(':') == 1: - nameStack = nameStack[nameStack.index(":") + 1:] + if nameStack.count(":") == 1: + nameStack = nameStack[nameStack.index(":") + 1 :] while len(nameStack): tmpStack = [] - tmpInheritClass = {"access":"private", "virtual": False} + tmpInheritClass = {"access": "private", "virtual": False} if "," in nameStack: - tmpStack = nameStack[:nameStack.index(",")] - nameStack = nameStack[nameStack.index(",") + 1:] + tmpStack = nameStack[: nameStack.index(",")] + nameStack = nameStack[nameStack.index(",") + 1 :] else: tmpStack = nameStack nameStack = [] - + # Convert template classes to one name in the last index for i in range(0, len(tmpStack)): - if '<' in tmpStack[i]: - tmpStack2 = tmpStack[:i-1] - tmpStack2.append("".join(tmpStack[i-1:])) + if "<" in tmpStack[i]: + tmpStack2 = tmpStack[: i - 1] + tmpStack2.append("".join(tmpStack[i - 1 :])) tmpStack = tmpStack2 break if len(tmpStack) == 0: - break; + break elif len(tmpStack) == 1: tmpInheritClass["class"] = tmpStack[0] elif len(tmpStack) == 2: tmpInheritClass["access"] = tmpStack[0] tmpInheritClass["class"] = tmpStack[1] elif len(tmpStack) == 3 and "virtual" in tmpStack: - tmpInheritClass["access"] = tmpStack[1] if tmpStack[1] != "virtual" else tmpStack[0] + tmpInheritClass["access"] = ( + tmpStack[1] if tmpStack[1] != "virtual" else tmpStack[0] + ) tmpInheritClass["class"] = tmpStack[2] tmpInheritClass["virtual"] = True else: - warning_print( "Warning: can not parse inheriting class %s"%(" ".join(tmpStack))) - if '>' in tmpStack: pass # allow skip templates for now - else: raise NotImplemented + warning_print( + "Warning: can not parse inheriting class %s" + % (" ".join(tmpStack)) + ) + if ">" in tmpStack: + pass # allow skip templates for now + else: + raise NotImplemented + + if "class" in tmpInheritClass: + inheritList.append(tmpInheritClass) - if 'class' in tmpInheritClass: inheritList.append(tmpInheritClass) + elif nameStack.count(":") == 2: + self["parent"] = self["name"] + self["name"] = nameStack[-1] - elif nameStack.count(':') == 2: self['parent'] = self['name']; self['name'] = nameStack[-1] + elif nameStack.count(":") > 2 and nameStack[0] in ("class", "struct"): + tmpStack = nameStack[nameStack.index(":") + 1 :] - elif nameStack.count(':') > 2 and nameStack[0] in ("class", "struct"): - tmpStack = nameStack[nameStack.index(":") + 1:] - superTmpStack = [[]] for tok in tmpStack: - if tok == ',': + if tok == ",": superTmpStack.append([]) else: superTmpStack[-1].append(tok) - + for tmpStack in superTmpStack: - tmpInheritClass = {"access":"private"} - + tmpInheritClass = {"access": "private"} + if len(tmpStack) and tmpStack[0] in supportedAccessSpecifier: tmpInheritClass["access"] = tmpStack[0] tmpStack = tmpStack[1:] - + inheritNSStack = [] while len(tmpStack) > 3: - if tmpStack[0] == ':': break; - if tmpStack[1] != ':': break; - if tmpStack[2] != ':': break; + if tmpStack[0] == ":": + break + if tmpStack[1] != ":": + break + if tmpStack[2] != ":": + break inheritNSStack.append(tmpStack[0]) tmpStack = tmpStack[3:] - if len(tmpStack) == 1 and tmpStack[0] != ':': - inheritNSStack.append(tmpStack[0]) + if len(tmpStack) == 1 and tmpStack[0] != ":": + inheritNSStack.append(tmpStack[0]) tmpInheritClass["class"] = "::".join(inheritNSStack) inheritList.append(tmpInheritClass) - self['inherits'] = inheritList + self["inherits"] = inheritList if curTemplate: self["template"] = curTemplate - trace_print("Setting template to '%s'"%self["template"]) + trace_print("Setting template to '%s'" % self["template"]) methodAccessSpecificList = {} propertyAccessSpecificList = {} @@ -561,7 +644,7 @@ def __init__(self, nameStack, curTemplate): structAccessSpecificList = {} typedefAccessSpecificList = {} forwardAccessSpecificList = {} - + for accessSpecifier in supportedAccessSpecifier: methodAccessSpecificList[accessSpecifier] = [] propertyAccessSpecificList[accessSpecifier] = [] @@ -570,94 +653,107 @@ def __init__(self, nameStack, curTemplate): typedefAccessSpecificList[accessSpecifier] = [] forwardAccessSpecificList[accessSpecifier] = [] - self['methods'] = methodAccessSpecificList - self['properties'] = propertyAccessSpecificList - self['enums'] = enumAccessSpecificList - self['structs'] = structAccessSpecificList - self['typedefs'] = typedefAccessSpecificList - self['forward_declares'] = forwardAccessSpecificList + self["methods"] = methodAccessSpecificList + self["properties"] = propertyAccessSpecificList + self["enums"] = enumAccessSpecificList + self["structs"] = structAccessSpecificList + self["typedefs"] = typedefAccessSpecificList + self["forward_declares"] = forwardAccessSpecificList - def show(self): """Convert class to a string""" namespace_prefix = "" - if self["namespace"]: namespace_prefix = self["namespace"] + "::" - rtn = "%s %s"%(self["declaration_method"], namespace_prefix + self["name"]) - if self["final"]: rtn += " final" - if self['abstract']: rtn += ' (abstract)\n' - else: rtn += '\n' + if self["namespace"]: + namespace_prefix = self["namespace"] + "::" + rtn = "%s %s" % (self["declaration_method"], namespace_prefix + self["name"]) + if self["final"]: + rtn += " final" + if self["abstract"]: + rtn += " (abstract)\n" + else: + rtn += "\n" - if 'doxygen' in list(self.keys()): rtn += self["doxygen"] + '\n' - if 'parent' in list(self.keys()) and self['parent']: rtn += 'parent class: ' + self['parent'] + '\n' + if "doxygen" in list(self.keys()): + rtn += self["doxygen"] + "\n" + if "parent" in list(self.keys()) and self["parent"]: + rtn += "parent class: " + self["parent"] + "\n" if "inherits" in list(self.keys()): rtn += " Inherits: " for inheritClass in self["inherits"]: - if inheritClass["virtual"]: rtn += "virtual " - rtn += "%s %s, "%(inheritClass["access"], inheritClass["class"]) + if inheritClass["virtual"]: + rtn += "virtual " + rtn += "%s %s, " % (inheritClass["access"], inheritClass["class"]) rtn += "\n" rtn += " {\n" for accessSpecifier in supportedAccessSpecifier: - rtn += " %s\n"%(accessSpecifier) - #Enums - if (len(self["enums"][accessSpecifier])): + rtn += " %s\n" % (accessSpecifier) + # Enums + if len(self["enums"][accessSpecifier]): rtn += " \n" for enum in self["enums"][accessSpecifier]: - rtn += " %s\n"%(repr(enum)) - #Properties - if (len(self["properties"][accessSpecifier])): + rtn += " %s\n" % (repr(enum)) + # Properties + if len(self["properties"][accessSpecifier]): rtn += " \n" for property in self["properties"][accessSpecifier]: - rtn += " %s\n"%(repr(property)) - #Methods - if (len(self["methods"][accessSpecifier])): + rtn += " %s\n" % (repr(property)) + # Methods + if len(self["methods"][accessSpecifier]): rtn += " \n" for method in self["methods"][accessSpecifier]: - rtn += "\t\t" + method.show() + '\n' + rtn += "\t\t" + method.show() + "\n" rtn += " }\n" print(rtn) - + def __str__(self): """Convert class to a string""" namespace_prefix = "" - if self["namespace"]: namespace_prefix = self["namespace"] + "::" - rtn = "%s %s"%(self["declaration_method"], namespace_prefix + self["name"]) - if self["final"]: rtn += " final" - if self['abstract']: rtn += ' (abstract)\n' - else: rtn += '\n' + if self["namespace"]: + namespace_prefix = self["namespace"] + "::" + rtn = "%s %s" % (self["declaration_method"], namespace_prefix + self["name"]) + if self["final"]: + rtn += " final" + if self["abstract"]: + rtn += " (abstract)\n" + else: + rtn += "\n" - if 'doxygen' in list(self.keys()): rtn += self["doxygen"] + '\n' - if 'parent' in list(self.keys()) and self['parent']: rtn += 'parent class: ' + self['parent'] + '\n' + if "doxygen" in list(self.keys()): + rtn += self["doxygen"] + "\n" + if "parent" in list(self.keys()) and self["parent"]: + rtn += "parent class: " + self["parent"] + "\n" if "inherits" in list(self.keys()) and len(self["inherits"]): rtn += "Inherits: " for inheritClass in self["inherits"]: - if inheritClass.get("virtual", False): rtn += "virtual " - rtn += "%s %s, "%(inheritClass["access"], inheritClass["class"]) + if inheritClass.get("virtual", False): + rtn += "virtual " + rtn += "%s %s, " % (inheritClass["access"], inheritClass["class"]) rtn += "\n" rtn += "{\n" for accessSpecifier in supportedAccessSpecifier: - rtn += "%s\n"%(accessSpecifier) - #Enums - if (len(self["enums"][accessSpecifier])): + rtn += "%s\n" % (accessSpecifier) + # Enums + if len(self["enums"][accessSpecifier]): rtn += " // Enums\n" for enum in self["enums"][accessSpecifier]: - rtn += " %s\n"%(repr(enum)) - #Properties - if (len(self["properties"][accessSpecifier])): + rtn += " %s\n" % (repr(enum)) + # Properties + if len(self["properties"][accessSpecifier]): rtn += " // Properties\n" for property in self["properties"][accessSpecifier]: - rtn += " %s\n"%(repr(property)) - #Methods - if (len(self["methods"][accessSpecifier])): + rtn += " %s\n" % (repr(property)) + # Methods + if len(self["methods"][accessSpecifier]): rtn += " // Methods\n" for method in self["methods"][accessSpecifier]: - rtn += " %s\n"%(repr(method)) + rtn += " %s\n" % (repr(method)) rtn += "}\n" return rtn -class CppUnion( CppClass ): +class CppUnion(CppClass): """Takes a name stack and turns it into a union Contains the following Keys: @@ -672,89 +768,115 @@ class CppUnion( CppClass ): 'members': [] } """ - + def __init__(self, nameStack): CppClass.__init__(self, nameStack, None) self["name"] = "union " + self["name"] self["members"] = self["properties"]["public"] - + def transform_to_union_keys(self): - print("union keys: %s"%list(self.keys())) - for key in ['inherits', 'parent', 'abstract', 'namespace', 'typedefs', 'methods']: - del self[key] - + print("union keys: %s" % list(self.keys())) + for key in [ + "inherits", + "parent", + "abstract", + "namespace", + "typedefs", + "methods", + ]: + del self[key] + def show(self): """Convert class to a string""" print(self) - - + def __str__(self): """Convert class to a string""" namespace_prefix = "" - if self["namespace"]: namespace_prefix = self["namespace"] + "::" - rtn = "%s %s"%(self["declaration_method"], namespace_prefix + self["name"]) - if self['abstract']: rtn += ' (abstract)\n' - else: rtn += '\n' + if self["namespace"]: + namespace_prefix = self["namespace"] + "::" + rtn = "%s %s" % (self["declaration_method"], namespace_prefix + self["name"]) + if self["abstract"]: + rtn += " (abstract)\n" + else: + rtn += "\n" - if 'doxygen' in list(self.keys()): rtn += self["doxygen"] + '\n' - if 'parent' in list(self.keys()) and self['parent']: rtn += 'parent class: ' + self['parent'] + '\n' + if "doxygen" in list(self.keys()): + rtn += self["doxygen"] + "\n" + if "parent" in list(self.keys()) and self["parent"]: + rtn += "parent class: " + self["parent"] + "\n" rtn += "{\n" for member in self["members"]: - rtn += " %s\n"%(repr(member)) + rtn += " %s\n" % (repr(member)) rtn += "}\n" return rtn - -class _CppMethod( dict ): - def _params_helper1( self, stack ): +class _CppMethod(dict): + def _params_helper1(self, stack): # deal with "throw" keyword - if 'throw' in stack: stack = stack[ : stack.index('throw') ] + if "throw" in stack: + stack = stack[: stack.index("throw")] ## remove GCC keyword __attribute__(...) and preserve returns ## cleaned = [] - hit = False; hitOpen = 0; hitClose = 0 + hit = False + hitOpen = 0 + hitClose = 0 for a in stack: - if a == '__attribute__': hit = True + if a == "__attribute__": + hit = True if hit: - if a == '(': hitOpen += 1 - elif a == ')': hitClose += 1 - if a==')' and hitOpen == hitClose: + if a == "(": + hitOpen += 1 + elif a == ")": + hitClose += 1 + if a == ")" and hitOpen == hitClose: hit = False else: - cleaned.append( a ) + cleaned.append(a) stack = cleaned # also deal with attribute((const)) function prefix # # TODO this needs to be better # if len(stack) > 5: - a = ''.join(stack) - if a.startswith('((__const__))'): stack = stack[ 5 : ] - elif a.startswith('__attribute__((__const__))'): stack = stack[ 6 : ] - - stack = stack[stack.index('(') + 1: ] - if not stack: return [] - if len(stack)>=3 and stack[0]==')' and stack[1]==':': # is this always a constructor? - self['constructor'] = True + a = "".join(stack) + if a.startswith("((__const__))"): + stack = stack[5:] + elif a.startswith("__attribute__((__const__))"): + stack = stack[6:] + + stack = stack[stack.index("(") + 1 :] + if not stack: + return [] + if ( + len(stack) >= 3 and stack[0] == ")" and stack[1] == ":" + ): # is this always a constructor? + self["constructor"] = True return [] - stack.reverse(); _end_ = stack.index(')'); stack.reverse() - stack = stack[ : len(stack)-(_end_+1) ] - if '(' not in stack: return stack # safe to return, no defaults that init a class + stack.reverse() + _end_ = stack.index(")") + stack.reverse() + stack = stack[: len(stack) - (_end_ + 1)] + if "(" not in stack: + return stack # safe to return, no defaults that init a class return stack - def _params_helper2( self, params ): + def _params_helper2(self, params): for p in params: - p['method'] = self # save reference in variable to parent method - if '::' in p['type']: - ns = p['type'].split('::')[0] + p["method"] = self # save reference in variable to parent method + if "::" in p["type"]: + ns = p["type"].split("::")[0] if ns not in Resolver.NAMESPACES and ns in Resolver.CLASSES: - p['type'] = self['namespace'] + p['type'] - else: p['namespace'] = self[ 'namespace' ] + p["type"] = self["namespace"] + p["type"] + else: + p["namespace"] = self["namespace"] -class CppMethod( _CppMethod ): + +class CppMethod(_CppMethod): """Takes a name stack and turns it into a method Contains the following Keys: @@ -763,55 +885,75 @@ class CppMethod( _CppMethod ): self['doxygen'] - Doxygen comments associated with the method if they exist self['parameters'] - List of CppVariables """ + def show(self): - r = ['method name: %s (%s)' %(self['name'],self['debug']) ] - if self['returns']: r.append( 'returns: %s'%self['returns'] ) - if self['parameters']: r.append( 'number arguments: %s' %len(self['parameters'])) - if self['pure_virtual']: r.append( 'pure virtual: %s'%self['pure_virtual'] ) - if self['constructor']: r.append( 'constructor' ) - if self['destructor']: r.append( 'destructor' ) - return '\n\t\t '.join( r ) + r = ["method name: %s (%s)" % (self["name"], self["debug"])] + if self["returns"]: + r.append("returns: %s" % self["returns"]) + if self["parameters"]: + r.append("number arguments: %s" % len(self["parameters"])) + if self["pure_virtual"]: + r.append("pure virtual: %s" % self["pure_virtual"]) + if self["constructor"]: + r.append("constructor") + if self["destructor"]: + r.append("destructor") + return "\n\t\t ".join(r) def __init__(self, nameStack, curClass, methinfo, curTemplate): - debug_print( "Method: %s"%nameStack ) - debug_print( "Template: %s"%curTemplate ) + debug_print("Method: %s" % nameStack) + debug_print("Template: %s" % curTemplate) global doxygenCommentCache if len(doxygenCommentCache): self["doxygen"] = doxygenCommentCache doxygenCommentCache = "" if "operator" in nameStack: - self["rtnType"] = " ".join(nameStack[:nameStack.index('operator')]) - self["name"] = "".join(nameStack[nameStack.index('operator'):nameStack.index('(')]) + self["rtnType"] = " ".join(nameStack[: nameStack.index("operator")]) + self["name"] = "".join( + nameStack[nameStack.index("operator") : nameStack.index("(")] + ) else: - self["rtnType"] = " ".join(nameStack[:nameStack.index('(') - 1]) - self["name"] = " ".join(nameStack[nameStack.index('(') - 1:nameStack.index('(')]) + self["rtnType"] = " ".join(nameStack[: nameStack.index("(") - 1]) + self["name"] = " ".join( + nameStack[nameStack.index("(") - 1 : nameStack.index("(")] + ) if self["rtnType"].startswith("virtual"): - self["rtnType"] = self["rtnType"][len("virtual"):].strip() + self["rtnType"] = self["rtnType"][len("virtual") :].strip() if len(self["rtnType"]) == 0 or self["name"] == curClass: self["rtnType"] = "void" - - self["rtnType"] = self["rtnType"].replace(' : : ', '::' ) - self["rtnType"] = self["rtnType"].replace(" <","<") - self["rtnType"] = self["rtnType"].replace(" >",">").replace(">>", "> >").replace(">>", "> >") - self["rtnType"] = self["rtnType"].replace(" ,",",") - + + self["rtnType"] = self["rtnType"].replace(" : : ", "::") + self["rtnType"] = self["rtnType"].replace(" <", "<") + self["rtnType"] = ( + self["rtnType"].replace(" >", ">").replace(">>", "> >").replace(">>", "> >") + ) + self["rtnType"] = self["rtnType"].replace(" ,", ",") + # deal with "noexcept" specifier/operator cleaned = [] - hit = False; parentCount = 0 - self['noexcept'] = '' + hit = False + parentCount = 0 + self["noexcept"] = "" for a in nameStack: - if a == 'noexcept': hit = True + if a == "noexcept": + hit = True if hit: - if a == '(': parentCount += 1 - elif a == ')': parentCount -= 1 - elif parentCount == 0 and a != 'noexcept': hit = False; cleaned.append( a ); continue # noexcept without parenthesis - if a==')' and parentCount == 0: hit = False - self['noexcept'] += a + if a == "(": + parentCount += 1 + elif a == ")": + parentCount -= 1 + elif parentCount == 0 and a != "noexcept": + hit = False + cleaned.append(a) + continue # noexcept without parenthesis + if a == ")" and parentCount == 0: + hit = False + self["noexcept"] += a else: - cleaned.append( a ) + cleaned.append(a) nameStack = cleaned - self['noexcept'] = self['noexcept'] if self['noexcept'] else None - + self["noexcept"] = self["noexcept"] if self["noexcept"] else None + for spec in ["const", "final", "override"]: self[spec] = False for i in reversed(nameStack): @@ -821,10 +963,10 @@ def __init__(self, nameStack, curClass, methinfo, curTemplate): elif i == ")": break - self.update( methinfo ) + self.update(methinfo) self["line_number"] = detect_lineno(nameStack[0]) - #Filter out initializer lists used in constructors + # Filter out initializer lists used in constructors try: paren_depth_counter = 0 for i in range(0, len(nameStack)): @@ -832,36 +974,38 @@ def __init__(self, nameStack, curClass, methinfo, curTemplate): if elm == "(": paren_depth_counter += 1 if elm == ")": - paren_depth_counter -=1 - if paren_depth_counter == 0 and nameStack[i+1] == ':': + paren_depth_counter -= 1 + if paren_depth_counter == 0 and nameStack[i + 1] == ":": debug_print("Stripping out initializer list") - nameStack = nameStack[:i+1] + nameStack = nameStack[: i + 1] break - except: pass - - paramsStack = self._params_helper1( nameStack ) - - debug_print( "curTemplate: %s"%curTemplate) + except: + pass + + paramsStack = self._params_helper1(nameStack) + + debug_print("curTemplate: %s" % curTemplate) if curTemplate: self["template"] = curTemplate - debug_print( "SET self['template'] to `%s`"%self["template"]) + debug_print("SET self['template'] to `%s`" % self["template"]) params = [] - #See if there is a doxygen comment for the variable + # See if there is a doxygen comment for the variable doxyVarDesc = {} - + if "doxygen" in self: doxyLines = self["doxygen"].split("\n") lastParamDesc = "" for doxyLine in doxyLines: if " @param " in doxyLine or " \param " in doxyLine: try: - #Strip out the param - doxyLine = doxyLine[doxyLine.find("param ") + 6:] + # Strip out the param + doxyLine = doxyLine[doxyLine.find("param ") + 6 :] (var, desc) = doxyLine.split(" ", 1) doxyVarDesc[var] = desc.strip() lastParamDesc = var - except: pass + except: + pass elif " @return " in doxyLine or " \return " in doxyLine: lastParamDesc = "" # not handled for now @@ -871,81 +1015,95 @@ def __init__(self, nameStack, curClass, methinfo, curTemplate): if " " not in doxyLine: lastParamDesc = "" continue - doxyLine = doxyLine[doxyLine.find(" ") + 1:] + doxyLine = doxyLine[doxyLine.find(" ") + 1 :] doxyVarDesc[lastParamDesc] += " " + doxyLine - except: pass + except: + pass # non-vararg by default self["vararg"] = False - #Create the variable now - while (len(paramsStack)): + # Create the variable now + while len(paramsStack): # Find commas that are not nexted in <>'s like template types open_template_count = 0 open_paren_count = 0 param_separator = 0 i = 0 for elm in paramsStack: - if '<' in elm : + if "<" in elm: open_template_count += 1 - elif '>' in elm: + elif ">" in elm: open_template_count -= 1 - elif '(' in elm : + elif "(" in elm: open_paren_count += 1 - elif ')' in elm: + elif ")" in elm: open_paren_count -= 1 - elif elm == ',' and open_template_count == 0 and open_paren_count==0: + elif elm == "," and open_template_count == 0 and open_paren_count == 0: param_separator = i break i += 1 - + if param_separator: - param = CppVariable(paramsStack[0:param_separator], doxyVarDesc=doxyVarDesc) - if len(list(param.keys())): params.append(param) - paramsStack = paramsStack[param_separator + 1:] + param = CppVariable( + paramsStack[0:param_separator], doxyVarDesc=doxyVarDesc + ) + if len(list(param.keys())): + params.append(param) + paramsStack = paramsStack[param_separator + 1 :] elif len(paramsStack) and paramsStack[0] == "...": self["vararg"] = True paramsStack = paramsStack[1:] else: - param = CppVariable(paramsStack, doxyVarDesc=doxyVarDesc) - if len(list(param.keys())): params.append(param) + param = CppVariable(paramsStack, doxyVarDesc=doxyVarDesc) + if len(list(param.keys())): + params.append(param) break - self["parameters"] = params - self._params_helper2( params ) # mods params inplace + self._params_helper2(params) # mods params inplace def __str__(self): filter_keys = ("parent", "defined", "operator", "returns_reference") - cpy = dict((k,v) for (k,v) in list(self.items()) if k not in filter_keys) - return "%s"%cpy + cpy = dict((k, v) for (k, v) in list(self.items()) if k not in filter_keys) + return "%s" % cpy class _CppVariable(dict): - def _name_stack_helper( self, stack ): + def _name_stack_helper(self, stack): stack = list(stack) - if '=' not in stack: # TODO refactor me + if "=" not in stack: # TODO refactor me # check for array[n] and deal with funny array syntax: "int myvar:99" array = [] - while stack and stack[-1].isdigit(): array.append( stack.pop() ) - if array: array.reverse(); self['array'] = int(''.join(array)) - if stack and stack[-1].endswith(':'): stack[-1] = stack[-1][:-1] - - while stack and not stack[-1]: stack.pop() # can be empty + while stack and stack[-1].isdigit(): + array.append(stack.pop()) + if array: + array.reverse() + self["array"] = int("".join(array)) + if stack and stack[-1].endswith(":"): + stack[-1] = stack[-1][:-1] + + while stack and not stack[-1]: + stack.pop() # can be empty return stack def init(self): - #assert self['name'] # allow unnamed variables, methods like this: "void func(void);" + # assert self['name'] # allow unnamed variables, methods like this: "void func(void);" a = [] - self['aliases'] = []; self['parent'] = None; self['typedef'] = None - for key in 'constant reference pointer static typedefs class fundamental unresolved'.split(): - self[ key ] = 0 - for b in self['type'].split(): - if b == '__const__': b = 'const' - a.append( b ) - self['type'] = ' '.join( a ) - - -class CppVariable( _CppVariable ): + self["aliases"] = [] + self["parent"] = None + self["typedef"] = None + for ( + key + ) in "constant reference pointer static typedefs class fundamental unresolved".split(): + self[key] = 0 + for b in self["type"].split(): + if b == "__const__": + b = "const" + a.append(b) + self["type"] = " ".join(a) + + +class CppVariable(_CppVariable): """Takes a name stack and turns it into a method Contains the following Keys: @@ -958,94 +1116,122 @@ class CppVariable( _CppVariable ): exist if there is a default value self['extern'] - True if its an extern, false if not """ + Vars = [] - def __init__(self, nameStack, **kwargs): - debug_print("trace %s"%nameStack) + + def __init__(self, nameStack, **kwargs): + debug_print("trace %s" % nameStack) if len(nameStack) and nameStack[0] == "extern": - self['extern'] = True + self["extern"] = True del nameStack[0] else: - self['extern'] = False - + self["extern"] = False + _stack_ = nameStack - if "[" in nameStack: #strip off array informatin - arrayStack = nameStack[nameStack.index("["):] + if "[" in nameStack: # strip off array informatin + arrayStack = nameStack[nameStack.index("[") :] if nameStack.count("[") > 1: debug_print("Multi dimensional array") - debug_print("arrayStack=%s"%arrayStack) + debug_print("arrayStack=%s" % arrayStack) nums = [x for x in arrayStack if x.isdigit()] # Calculate size by multiplying all dimensions p = 1 for n in nums: p *= int(n) - #Multi dimensional array + # Multi dimensional array self["array_size"] = p self["multi_dimensional_array"] = 1 self["multi_dimensional_array_size"] = "x".join(nums) else: debug_print("Array") if len(arrayStack) == 3: - self["array_size"] = arrayStack[1] - nameStack = nameStack[:nameStack.index("[")] + self["array_size"] = arrayStack[1] + nameStack = nameStack[: nameStack.index("[")] self["array"] = 1 else: self["array"] = 0 - nameStack = self._name_stack_helper( nameStack ) + nameStack = self._name_stack_helper(nameStack) global doxygenCommentCache if len(doxygenCommentCache): self["doxygen"] = doxygenCommentCache doxygenCommentCache = "" - debug_print( "Variable: %s"%nameStack ) + debug_print("Variable: %s" % nameStack) self["line_number"] = detect_lineno(nameStack[0]) self["function_pointer"] = 0 - if (len(nameStack) < 2): # +++ - if len(nameStack) == 1: self['type'] = nameStack[0]; self['name'] = '' - else: error_print(_stack_); assert 0 - - elif is_function_pointer_stack(nameStack): #function pointer - self["type"] = " ".join(nameStack[:nameStack.index("(") + 2] + nameStack[nameStack.index(")") :]) - self["name"] = " ".join(nameStack[nameStack.index("(") + 2 : nameStack.index(")")]) + if len(nameStack) < 2: # +++ + if len(nameStack) == 1: + self["type"] = nameStack[0] + self["name"] = "" + else: + error_print(_stack_) + assert 0 + + elif is_function_pointer_stack(nameStack): # function pointer + self["type"] = " ".join( + nameStack[: nameStack.index("(") + 2] + + nameStack[nameStack.index(")") :] + ) + self["name"] = " ".join( + nameStack[nameStack.index("(") + 2 : nameStack.index(")")] + ) self["function_pointer"] = 1 - elif ("=" in nameStack): - self["type"] = " ".join(nameStack[:nameStack.index("=") - 1]) + elif "=" in nameStack: + self["type"] = " ".join(nameStack[: nameStack.index("=") - 1]) self["name"] = nameStack[nameStack.index("=") - 1] - self["defaultValue"] = " ".join(nameStack[nameStack.index("=") + 1:]) # deprecate camelCase in dicts - self['default'] = " ".join(nameStack[nameStack.index("=") + 1:]) + self["defaultValue"] = " ".join( + nameStack[nameStack.index("=") + 1 :] + ) # deprecate camelCase in dicts + self["default"] = " ".join(nameStack[nameStack.index("=") + 1 :]) - elif is_fundamental(nameStack[-1]) or nameStack[-1] in ['>', '<' , ':', '.']: - #Un named parameter + elif is_fundamental(nameStack[-1]) or nameStack[-1] in [">", "<", ":", "."]: + # Un named parameter self["type"] = " ".join(nameStack) self["name"] = "" - else: # common case + else: # common case self["type"] = " ".join(nameStack[:-1]) self["name"] = nameStack[-1] - self["type"] = self["type"].replace(" :",":") - self["type"] = self["type"].replace(": ",":") - self["type"] = self["type"].replace(" <","<") - self["type"] = self["type"].replace(" >",">").replace(">>", "> >").replace(">>", "> >") - self["type"] = self["type"].replace(" ,",",") - #Optional doxygen description + self["type"] = self["type"].replace(" :", ":") + self["type"] = self["type"].replace(": ", ":") + self["type"] = self["type"].replace(" <", "<") + self["type"] = ( + self["type"].replace(" >", ">").replace(">>", "> >").replace(">>", "> >") + ) + self["type"] = self["type"].replace(" ,", ",") + # Optional doxygen description try: self["desc"] = kwargs["doxyVarDesc"][self["name"]] - except: pass + except: + pass self.init() - CppVariable.Vars.append( self ) # save and resolve later - + CppVariable.Vars.append(self) # save and resolve later + def __str__(self): - keys_white_list = ['constant','name','reference','type','static','pointer','desc', 'line_number', 'extern'] - cpy = dict((k,v) for (k,v) in list(self.items()) if k in keys_white_list) - if "array_size" in self: cpy["array_size"] = self["array_size"] - return "%s"%cpy + keys_white_list = [ + "constant", + "name", + "reference", + "type", + "static", + "pointer", + "desc", + "line_number", + "extern", + ] + cpy = dict((k, v) for (k, v) in list(self.items()) if k in keys_white_list) + if "array_size" in self: + cpy["array_size"] = self["array_size"] + return "%s" % cpy + class _CppEnum(dict): - def resolve_enum_values( self, values ): + def resolve_enum_values(self, values): """Evaluates the values list of dictionaries passed in and figures out what the enum value for each enum is editing in place: @@ -1057,42 +1243,49 @@ def resolve_enum_values( self, values ): {'name': 'RED', 'value': 1}, {'name': 'GREEN', 'value': 8}] """ - t = int; i = 0 - names = [ v['name'] for v in values ] + t = int + i = 0 + names = [v["name"] for v in values] for v in values: - if 'value' in v: - a = v['value'].strip() + if "value" in v: + a = v["value"].strip() # Remove single quotes from single quoted chars (unless part of some expression if len(a) == 3 and a[0] == "'" and a[2] == "'": - a = v['value'] = a[1] + a = v["value"] = a[1] if a.lower().startswith("0x"): try: - i = a = int(a , 16) - except:pass + i = a = int(a, 16) + except: + pass elif a.isdigit(): - i = a = int( a ) + i = a = int(a) elif a in names: for other in values: - if other['name'] == a: - v['value'] = other['value'] + if other["name"] == a: + v["value"] = other["value"] break - elif '"' in a or "'" in a: t = str # only if there are quotes it this a string enum + elif '"' in a or "'" in a: + t = str # only if there are quotes it this a string enum else: try: a = i = ord(a) - except: pass - #Allow access of what is in the file pre-convert if converted - if v['value'] != str(a): - v['raw_value'] = v['value'] - v['value'] = a - else: v['value'] = i + except: + pass + # Allow access of what is in the file pre-convert if converted + if v["value"] != str(a): + v["raw_value"] = v["value"] + v["value"] = a + else: + v["value"] = i try: - v['value'] = v['value'].replace(" < < ", " << ").replace(" >> ", " >> ") - except: pass + v["value"] = v["value"].replace(" < < ", " << ").replace(" >> ", " >> ") + except: + pass i += 1 return t + class CppEnum(_CppEnum): """Takes a name stack and turns it into an Enum @@ -1105,6 +1298,7 @@ class CppEnum(_CppEnum): if a value for a given enum value was defined } """ + def __init__(self, nameStack): global doxygenCommentCache if len(doxygenCommentCache): @@ -1113,55 +1307,61 @@ def __init__(self, nameStack): if len(nameStack) == 3 and nameStack[0] == "enum": debug_print("Created enum as just name/value") self["name"] = nameStack[1] - self["instances"]=[nameStack[2]] + self["instances"] = [nameStack[2]] if len(nameStack) < 4 or "{" not in nameStack or "}" not in nameStack: - #Not enough stuff for an enum + # Not enough stuff for an enum debug_print("Bad enum") return valueList = [] self["line_number"] = detect_lineno(nameStack[0]) - #Figure out what values it has - valueStack = nameStack[nameStack.index('{') + 1: nameStack.index('}')] + # Figure out what values it has + valueStack = nameStack[nameStack.index("{") + 1 : nameStack.index("}")] while len(valueStack): tmpStack = [] if "," in valueStack: - tmpStack = valueStack[:valueStack.index(",")] - valueStack = valueStack[valueStack.index(",") + 1:] + tmpStack = valueStack[: valueStack.index(",")] + valueStack = valueStack[valueStack.index(",") + 1 :] else: tmpStack = valueStack valueStack = [] d = {} - if len(tmpStack) == 1: d["name"] = tmpStack[0] + if len(tmpStack) == 1: + d["name"] = tmpStack[0] elif len(tmpStack) >= 3 and tmpStack[1] == "=": - d["name"] = tmpStack[0]; d["value"] = " ".join(tmpStack[2:]) + d["name"] = tmpStack[0] + d["value"] = " ".join(tmpStack[2:]) elif len(tmpStack) == 2 and tmpStack[1] == "=": - debug_print( "WARN-enum: parser missed value for %s"%tmpStack[0] ) + debug_print("WARN-enum: parser missed value for %s" % tmpStack[0]) d["name"] = tmpStack[0] - if d: valueList.append( d ) + if d: + valueList.append(d) if len(valueList): - self['type'] = self.resolve_enum_values( valueList ) # returns int for standard enum + self["type"] = self.resolve_enum_values( + valueList + ) # returns int for standard enum self["values"] = valueList else: - warning_print( 'WARN-enum: empty enum %s'%nameStack ) + warning_print("WARN-enum: empty enum %s" % nameStack) return - #Figure out if it has a name - preBraceStack = nameStack[:nameStack.index("{")] - postBraceStack = nameStack[nameStack.index("}") + 1:] + # Figure out if it has a name + preBraceStack = nameStack[: nameStack.index("{")] + postBraceStack = nameStack[nameStack.index("}") + 1 :] self["typedef"] = False - if (len(preBraceStack) == 4 and ":" in nameStack and "typedef" not in nameStack): + if len(preBraceStack) == 4 and ":" in nameStack and "typedef" not in nameStack: # C++11 specify enum type with "enum : ..." syntax self["name"] = preBraceStack[1] self["type"] = preBraceStack[3] - elif (len(preBraceStack) == 2 and "typedef" not in nameStack): + elif len(preBraceStack) == 2 and "typedef" not in nameStack: # enum "enum ..." syntax - self["name"] = preBraceStack[1] + self["name"] = preBraceStack[1] elif len(postBraceStack) and "typedef" in nameStack: self["name"] = " ".join(postBraceStack) self["typedef"] = True - else: warning_print( 'WARN-enum: nameless enum %s'%nameStack ) - #See if there are instances of this + else: + warning_print("WARN-enum: nameless enum %s" % nameStack) + # See if there are instances of this if "typedef" not in nameStack and len(postBraceStack): self["instances"] = [] for var in postBraceStack: @@ -1173,38 +1373,43 @@ def __init__(self, nameStack): class CppStruct(dict): Structs = [] + def __init__(self, nameStack): - if len(nameStack) >= 2: self['type'] = nameStack[1] - else: self['type'] = None - self['fields'] = [] - self.Structs.append( self ) + if len(nameStack) >= 2: + self["type"] = nameStack[1] + else: + self["type"] = None + self["fields"] = [] + self.Structs.append(self) global curLine self["line_number"] = curLine + C99_NONSTANDARD = { - 'int8' : 'signed char', - 'int16' : 'short int', - 'int32' : 'int', - 'int64' : 'int64_t', # this can be: long int (64bit), or long long int (32bit) - 'uint' : 'unsigned int', - 'uint8' : 'unsigned char', - 'uint16' : 'unsigned short int', - 'uint32' : 'unsigned int', - 'uint64' : 'uint64_t', # depends on host bits + "int8": "signed char", + "int16": "short int", + "int32": "int", + "int64": "int64_t", # this can be: long int (64bit), or long long int (32bit) + "uint": "unsigned int", + "uint8": "unsigned char", + "uint16": "unsigned short int", + "uint32": "unsigned int", + "uint64": "uint64_t", # depends on host bits } -def standardize_fundamental( s ): - if s in C99_NONSTANDARD: return C99_NONSTANDARD[ s ] - else: return s +def standardize_fundamental(s): + if s in C99_NONSTANDARD: + return C99_NONSTANDARD[s] + else: + return s class Resolver(object): - C_FUNDAMENTAL = 'size_t unsigned signed bool char wchar short int float double long void'.split() - C_FUNDAMENTAL += 'struct union enum'.split() + C_FUNDAMENTAL = "size_t unsigned signed bool char wchar short int float double long void".split() + C_FUNDAMENTAL += "struct union enum".split() - - SubTypedefs = {} # TODO deprecate? + SubTypedefs = {} # TODO deprecate? NAMESPACES = [] CLASSES = {} STRUCTS = {} @@ -1215,685 +1420,858 @@ def initextra(self): self.classes_order = [] self.structs = Resolver.STRUCTS self.structs_order = [] - self.namespaces = Resolver.NAMESPACES # save all namespaces + self.namespaces = Resolver.NAMESPACES # save all namespaces self.curStruct = None - self.stack = [] # full name stack, good idea to keep both stacks? (simple stack and full stack) - self._classes_brace_level = {} # class name : level - self._structs_brace_level = {} # struct type : level + self.stack = ( + [] + ) # full name stack, good idea to keep both stacks? (simple stack and full stack) + self._classes_brace_level = {} # class name : level + self._structs_brace_level = {} # struct type : level self._method_body = None self._forward_decls = [] - self._template_typenames = [] # template + self._template_typenames = [] # template - def current_namespace(self): return self.cur_namespace(True) + def current_namespace(self): + return self.cur_namespace(True) def cur_namespace(self, add_double_colon=False): rtn = "" i = 0 while i < len(self.nameSpaces): rtn += self.nameSpaces[i] - if add_double_colon or i < len(self.nameSpaces) - 1: rtn += "::" - i+=1 + if add_double_colon or i < len(self.nameSpaces) - 1: + rtn += "::" + i += 1 return rtn - - def guess_ctypes_type( self, string ): - pointers = string.count('*') - string = string.replace('*','') + def guess_ctypes_type(self, string): + pointers = string.count("*") + string = string.replace("*", "") a = string.split() - if 'unsigned' in a: u = 'u' - else: u = '' - if 'long' in a and 'double' in a: b = 'longdouble' # there is no ctypes.c_ulongdouble (this is a 64bit float?) - elif a.count('long') == 2 and 'int' in a: b = '%sint64' %u - elif a.count('long') == 2: b = '%slonglong' %u - elif 'long' in a: b = '%slong' %u - elif 'double' in a: b = 'double' # no udouble in ctypes - elif 'short' in a: b = '%sshort' %u - elif 'char' in a: b = '%schar' %u - elif 'wchar' in a: b = 'wchar' - elif 'bool' in a: b = 'bool' - elif 'float' in a: b = 'float' - - elif 'int' in a: b = '%sint' %u - elif 'int8' in a: b = 'int8' - elif 'int16' in a: b = 'int16' - elif 'int32' in a: b = 'int32' - elif 'int64' in a: b = 'int64' - - elif 'uint' in a: b = 'uint' - elif 'uint8' in a: b = 'uint8' - elif 'uint16' in a: b = 'uint16' - elif 'uint32' in a: b = 'uint32' - elif 'uint64' in a: b = 'uint64' - - elif 'size_t' in a: b = 'size_t' - elif 'void' in a: b = 'void_p' - - elif string in 'struct union'.split(): b = 'void_p' # what should be done here? don't trust struct, it could be a class, no need to expose via ctypes - else: b = 'void_p' - - if not pointers: return 'ctypes.c_%s' %b + if "unsigned" in a: + u = "u" + else: + u = "" + if "long" in a and "double" in a: + b = ( + "longdouble" + ) # there is no ctypes.c_ulongdouble (this is a 64bit float?) + elif a.count("long") == 2 and "int" in a: + b = "%sint64" % u + elif a.count("long") == 2: + b = "%slonglong" % u + elif "long" in a: + b = "%slong" % u + elif "double" in a: + b = "double" # no udouble in ctypes + elif "short" in a: + b = "%sshort" % u + elif "char" in a: + b = "%schar" % u + elif "wchar" in a: + b = "wchar" + elif "bool" in a: + b = "bool" + elif "float" in a: + b = "float" + + elif "int" in a: + b = "%sint" % u + elif "int8" in a: + b = "int8" + elif "int16" in a: + b = "int16" + elif "int32" in a: + b = "int32" + elif "int64" in a: + b = "int64" + + elif "uint" in a: + b = "uint" + elif "uint8" in a: + b = "uint8" + elif "uint16" in a: + b = "uint16" + elif "uint32" in a: + b = "uint32" + elif "uint64" in a: + b = "uint64" + + elif "size_t" in a: + b = "size_t" + elif "void" in a: + b = "void_p" + + elif string in "struct union".split(): + b = ( + "void_p" + ) # what should be done here? don't trust struct, it could be a class, no need to expose via ctypes + else: + b = "void_p" + + if not pointers: + return "ctypes.c_%s" % b else: - x = '' - for i in range(pointers): x += 'ctypes.POINTER(' - x += 'ctypes.c_%s' %b - x += ')' * pointers + x = "" + for i in range(pointers): + x += "ctypes.POINTER(" + x += "ctypes.c_%s" % b + x += ")" * pointers return x - def resolve_type( self, string, result ): # recursive - ''' + def resolve_type(self, string, result): # recursive + """ keeps track of useful things like: how many pointers, number of typedefs, is fundamental or a class, etc... - ''' + """ ## be careful with templates, what is inside can be a pointer but the overall type is not a pointer ## these come before a template - s = string.split('<')[0] - result[ 'constant' ] += s.split().count('const') - result[ 'static' ] += s.split().count('static') - result[ 'mutable' ] = 'mutable' in s.split() + s = string.split("<")[0] + result["constant"] += s.split().count("const") + result["static"] += s.split().count("static") + result["mutable"] = "mutable" in s.split() ## these come after a template - s = string.split('>')[-1] - result[ 'pointer' ] += s.count('*') - result[ 'reference' ] += s.count('&') - - - x = string; alias = False - for a in '* & const static mutable'.split(): x = x.replace(a,'') + s = string.split(">")[-1] + result["pointer"] += s.count("*") + result["reference"] += s.count("&") + + x = string + alias = False + for a in "* & const static mutable".split(): + x = x.replace(a, "") for y in x.split(): - if y not in self.C_FUNDAMENTAL: alias = y; break + if y not in self.C_FUNDAMENTAL: + alias = y + break - #if alias == 'class': + # if alias == 'class': # result['class'] = result['name'] # forward decl of class # result['forward_decl'] = True - if alias == '__extension__': result['fundamental_extension'] = True + if alias == "__extension__": + result["fundamental_extension"] = True elif alias: - if alias in result['aliases']: + if alias in result["aliases"]: # already resolved return - result['aliases'].append( alias ) + result["aliases"].append(alias) if alias in C99_NONSTANDARD: - result['type'] = C99_NONSTANDARD[ alias ] - result['typedef'] = alias - result['typedefs'] += 1 + result["type"] = C99_NONSTANDARD[alias] + result["typedef"] = alias + result["typedefs"] += 1 elif alias in self.typedefs: - result['typedefs'] += 1 - result['typedef'] = alias - self.resolve_type( self.typedefs[alias], result ) + result["typedefs"] += 1 + result["typedef"] = alias + self.resolve_type(self.typedefs[alias], result) elif alias in self.classes: - klass = self.classes[alias]; result['fundamental'] = False - result['class'] = klass - result['unresolved'] = False - else: result['unresolved'] = True + klass = self.classes[alias] + result["fundamental"] = False + result["class"] = klass + result["unresolved"] = False + else: + result["unresolved"] = True else: - result['fundamental'] = True - result['unresolved'] = False - + result["fundamental"] = True + result["unresolved"] = False def finalize_vars(self): - for s in CppStruct.Structs: # vars within structs can be ignored if they do not resolve - for var in s['fields']: var['parent'] = s['type'] - #for c in self.classes.values(): + for ( + s + ) in ( + CppStruct.Structs + ): # vars within structs can be ignored if they do not resolve + for var in s["fields"]: + var["parent"] = s["type"] + # for c in self.classes.values(): # for var in c.get_all_properties(): var['parent'] = c['name'] ## RESOLVE ## for var in CppVariable.Vars: - self.resolve_type( var['type'], var ) - #if 'method' in var and var['method']['name'] == '_notifyCurrentCamera': print(var); assert 0 + self.resolve_type(var["type"], var) + # if 'method' in var and var['method']['name'] == '_notifyCurrentCamera': print(var); assert 0 # then find concrete type and best guess ctypes type # - for var in CppVariable.Vars: - if not var['aliases']: #var['fundamental']: - var['ctypes_type'] = self.guess_ctypes_type( var['type'] ) + for var in CppVariable.Vars: + if not var["aliases"]: # var['fundamental']: + var["ctypes_type"] = self.guess_ctypes_type(var["type"]) else: - var['unresolved'] = False # below may test to True - if var['class']: - var['ctypes_type'] = 'ctypes.c_void_p' + var["unresolved"] = False # below may test to True + if var["class"]: + var["ctypes_type"] = "ctypes.c_void_p" else: - assert var['aliases'] - tag = var['aliases'][0] + assert var["aliases"] + tag = var["aliases"][0] klass = None nestedEnum = None nestedStruct = None nestedTypedef = None - if 'method' in var and 'parent' in list(var['method'].keys()): - klass = var['method']['parent'] - if tag in var['method']['parent']._public_enums: - nestedEnum = var['method']['parent']._public_enums[ tag ] - elif tag in var['method']['parent']._public_structs: - nestedStruct = var['method']['parent']._public_structs[ tag ] - elif tag in var['method']['parent']._public_typedefs: - nestedTypedef = var['method']['parent']._public_typedefs[ tag ] - - - if '<' in tag: # should also contain '>' - var['template'] = tag # do not resolve templates - var['ctypes_type'] = 'ctypes.c_void_p' - var['unresolved'] = True + if "method" in var and "parent" in list(var["method"].keys()): + klass = var["method"]["parent"] + if tag in var["method"]["parent"]._public_enums: + nestedEnum = var["method"]["parent"]._public_enums[tag] + elif tag in var["method"]["parent"]._public_structs: + nestedStruct = var["method"]["parent"]._public_structs[tag] + elif tag in var["method"]["parent"]._public_typedefs: + nestedTypedef = var["method"]["parent"]._public_typedefs[ + tag + ] + + if "<" in tag: # should also contain '>' + var["template"] = tag # do not resolve templates + var["ctypes_type"] = "ctypes.c_void_p" + var["unresolved"] = True elif nestedEnum: enum = nestedEnum - if enum['type'] is int: - var['ctypes_type'] = 'ctypes.c_int' - var['raw_type'] = 'int' + if enum["type"] is int: + var["ctypes_type"] = "ctypes.c_int" + var["raw_type"] = "int" - elif enum['type'] is str: - var['ctypes_type'] = 'ctypes.c_char_p' - var['raw_type'] = 'char*' + elif enum["type"] is str: + var["ctypes_type"] = "ctypes.c_char_p" + var["raw_type"] = "char*" - var['enum'] = var['method']['path'] + '::' + enum['name'] - var['fundamental'] = True + var["enum"] = var["method"]["path"] + "::" + enum["name"] + var["fundamental"] = True elif nestedStruct: - var['ctypes_type'] = 'ctypes.c_void_p' - var['raw_type'] = var['method']['path'] + '::' + nestedStruct['type'] - var['fundamental'] = False + var["ctypes_type"] = "ctypes.c_void_p" + var["raw_type"] = ( + var["method"]["path"] + "::" + nestedStruct["type"] + ) + var["fundamental"] = False elif nestedTypedef: - var['fundamental'] = is_fundamental( nestedTypedef ) - if not var['fundamental']: - var['raw_type'] = var['method']['path'] + '::' + tag + var["fundamental"] = is_fundamental(nestedTypedef) + if not var["fundamental"]: + var["raw_type"] = var["method"]["path"] + "::" + tag else: _tag = tag - if '::' in tag and tag.split('::')[0] in self.namespaces: tag = tag.split('::')[-1] - con = self.concrete_typedef( _tag ) + if "::" in tag and tag.split("::")[0] in self.namespaces: + tag = tag.split("::")[-1] + con = self.concrete_typedef(_tag) if con: - var['concrete_type'] = con - var['ctypes_type'] = self.guess_ctypes_type( var['concrete_type'] ) + var["concrete_type"] = con + var["ctypes_type"] = self.guess_ctypes_type( + var["concrete_type"] + ) elif tag in self.structs: - trace_print( 'STRUCT', var ) - var['struct'] = tag - var['ctypes_type'] = 'ctypes.c_void_p' - var['raw_type'] = self.structs[tag]['namespace'] + '::' + tag + trace_print("STRUCT", var) + var["struct"] = tag + var["ctypes_type"] = "ctypes.c_void_p" + var["raw_type"] = ( + self.structs[tag]["namespace"] + "::" + tag + ) elif tag in self._forward_decls: - var['forward_declared'] = tag - var['ctypes_type'] = 'ctypes.c_void_p' + var["forward_declared"] = tag + var["ctypes_type"] = "ctypes.c_void_p" elif tag in self.global_enums: - enum = self.global_enums[ tag ] - if enum['type'] is int: - var['ctypes_type'] = 'ctypes.c_int' - var['raw_type'] = 'int' - elif enum['type'] is str: - var['ctypes_type'] = 'ctypes.c_char_p' - var['raw_type'] = 'char*' - var['enum'] = enum['namespace'] + enum['name'] - var['fundamental'] = True - - - elif var['parent']: - warning_print( 'WARN unresolved %s'%_tag) - var['ctypes_type'] = 'ctypes.c_void_p' - var['unresolved'] = True - - - elif tag.count('::')==1: - trace_print( 'trying to find nested something in', tag ) - a = tag.split('::')[0] - b = tag.split('::')[-1] - if a in self.classes: # a::b is most likely something nested in a class - klass = self.classes[ a ] + enum = self.global_enums[tag] + if enum["type"] is int: + var["ctypes_type"] = "ctypes.c_int" + var["raw_type"] = "int" + elif enum["type"] is str: + var["ctypes_type"] = "ctypes.c_char_p" + var["raw_type"] = "char*" + var["enum"] = enum["namespace"] + enum["name"] + var["fundamental"] = True + + elif var["parent"]: + warning_print("WARN unresolved %s" % _tag) + var["ctypes_type"] = "ctypes.c_void_p" + var["unresolved"] = True + + elif tag.count("::") == 1: + trace_print("trying to find nested something in", tag) + a = tag.split("::")[0] + b = tag.split("::")[-1] + if ( + a in self.classes + ): # a::b is most likely something nested in a class + klass = self.classes[a] if b in klass._public_enums: - trace_print( '...found nested enum', b ) - enum = klass._public_enums[ b ] - if enum['type'] is int: - var['ctypes_type'] = 'ctypes.c_int' - var['raw_type'] = 'int' - elif enum['type'] is str: - var['ctypes_type'] = 'ctypes.c_char_p' - var['raw_type'] = 'char*' + trace_print("...found nested enum", b) + enum = klass._public_enums[b] + if enum["type"] is int: + var["ctypes_type"] = "ctypes.c_int" + var["raw_type"] = "int" + elif enum["type"] is str: + var["ctypes_type"] = "ctypes.c_char_p" + var["raw_type"] = "char*" try: - if 'method' in var: var['enum'] = var['method']['path'] + '::' + enum['name'] - else: # class property - var['unresolved'] = True + if "method" in var: + var["enum"] = ( + var["method"]["path"] + + "::" + + enum["name"] + ) + else: # class property + var["unresolved"] = True except: - var['unresolved'] = True - - var['fundamental'] = True + var["unresolved"] = True + + var["fundamental"] = True - else: var['unresolved'] = True # TODO klass._public_xxx + else: + var["unresolved"] = True # TODO klass._public_xxx - elif a in self.namespaces: # a::b can also be a nested namespace + elif ( + a in self.namespaces + ): # a::b can also be a nested namespace if b in self.global_enums: - enum = self.global_enums[ b ] + enum = self.global_enums[b] trace_print(enum) trace_print(var) assert 0 - elif b in self.global_enums: # falling back, this is a big ugly - enum = self.global_enums[ b ] - assert a in enum['namespace'].split('::') - if enum['type'] is int: - var['ctypes_type'] = 'ctypes.c_int' - var['raw_type'] = 'int' - elif enum['type'] is str: - var['ctypes_type'] = 'ctypes.c_char_p' - var['raw_type'] = 'char*' - var['fundamental'] = True - - else: # boost::gets::crazy - trace_print('NAMESPACES', self.namespaces) - trace_print( a, b ) - trace_print( '---- boost gets crazy ----' ) - var['ctypes_type'] = 'ctypes.c_void_p' - var['unresolved'] = True - - - elif 'namespace' in var and self.concrete_typedef(var['namespace']+tag): - #print( 'TRYING WITH NS', var['namespace'] ) - con = self.concrete_typedef( var['namespace']+tag ) + elif ( + b in self.global_enums + ): # falling back, this is a big ugly + enum = self.global_enums[b] + assert a in enum["namespace"].split("::") + if enum["type"] is int: + var["ctypes_type"] = "ctypes.c_int" + var["raw_type"] = "int" + elif enum["type"] is str: + var["ctypes_type"] = "ctypes.c_char_p" + var["raw_type"] = "char*" + var["fundamental"] = True + + else: # boost::gets::crazy + trace_print("NAMESPACES", self.namespaces) + trace_print(a, b) + trace_print("---- boost gets crazy ----") + var["ctypes_type"] = "ctypes.c_void_p" + var["unresolved"] = True + + elif "namespace" in var and self.concrete_typedef( + var["namespace"] + tag + ): + # print( 'TRYING WITH NS', var['namespace'] ) + con = self.concrete_typedef(var["namespace"] + tag) if con: - var['typedef'] = var['namespace']+tag - var['type'] = con - if 'struct' in con.split(): - var['raw_type'] = var['typedef'] - var['ctypes_type'] = 'ctypes.c_void_p' + var["typedef"] = var["namespace"] + tag + var["type"] = con + if "struct" in con.split(): + var["raw_type"] = var["typedef"] + var["ctypes_type"] = "ctypes.c_void_p" else: - self.resolve_type( var['type'], var ) - var['ctypes_type'] = self.guess_ctypes_type( var['type'] ) - - elif '::' in var: - var['ctypes_type'] = 'ctypes.c_void_p' - var['unresolved'] = True - - elif tag in self.SubTypedefs: # TODO remove SubTypedefs - if 'property_of_class' in var or 'property_of_struct' in var: - trace_print( 'class:', self.SubTypedefs[ tag ], 'tag:', tag ) - var['typedef'] = self.SubTypedefs[ tag ] # class name - var['ctypes_type'] = 'ctypes.c_void_p' + self.resolve_type(var["type"], var) + var["ctypes_type"] = self.guess_ctypes_type( + var["type"] + ) + + elif "::" in var: + var["ctypes_type"] = "ctypes.c_void_p" + var["unresolved"] = True + + elif tag in self.SubTypedefs: # TODO remove SubTypedefs + if ( + "property_of_class" in var + or "property_of_struct" in var + ): + trace_print( + "class:", self.SubTypedefs[tag], "tag:", tag + ) + var["typedef"] = self.SubTypedefs[tag] # class name + var["ctypes_type"] = "ctypes.c_void_p" else: - trace_print( "WARN-this should almost never happen!" ) - trace_print( var ); trace_print('-'*80) - var['unresolved'] = True + trace_print("WARN-this should almost never happen!") + trace_print(var) + trace_print("-" * 80) + var["unresolved"] = True elif tag in self._template_typenames: - var['typename'] = tag - var['ctypes_type'] = 'ctypes.c_void_p' - var['unresolved'] = True # TODO, how to deal with templates? - - elif tag.startswith('_'): # assume starting with underscore is not important for wrapping - warning_print( 'WARN unresolved %s'%_tag) - var['ctypes_type'] = 'ctypes.c_void_p' - var['unresolved'] = True + var["typename"] = tag + var["ctypes_type"] = "ctypes.c_void_p" + var[ + "unresolved" + ] = True # TODO, how to deal with templates? + + elif tag.startswith( + "_" + ): # assume starting with underscore is not important for wrapping + warning_print("WARN unresolved %s" % _tag) + var["ctypes_type"] = "ctypes.c_void_p" + var["unresolved"] = True else: - trace_print( 'WARN: unknown type', var ) - assert 'property_of_class' in var or 'property_of_struct' # only allow this case - var['unresolved'] = True - + trace_print("WARN: unknown type", var) + assert ( + "property_of_class" in var or "property_of_struct" + ) # only allow this case + var["unresolved"] = True ## if not resolved and is a method param, not going to wrap these methods ## - if var['unresolved'] and 'method' in var: var['method']['unresolved_parameters'] = True - + if var["unresolved"] and "method" in var: + var["method"]["unresolved_parameters"] = True # create stripped raw_type # - p = '* & const static mutable'.split() # +++ new July7: "mutable" + p = "* & const static mutable".split() # +++ new July7: "mutable" for var in CppVariable.Vars: - if 'raw_type' not in var: + if "raw_type" not in var: raw = [] - for x in var['type'].split(): - if x not in p: raw.append( x ) - var['raw_type'] = ' '.join( raw ) - - #if 'AutoConstantEntry' in var['raw_type']: print(var); assert 0 - if var['class']: - if '::' not in var['raw_type']: - if not var['class']['parent']: - var['raw_type'] = var['class']['namespace'] + '::' + var['raw_type'] - elif var['class']['parent'] in self.classes: - parent = self.classes[ var['class']['parent'] ] - var['raw_type'] = parent['namespace'] + '::' + var['class']['name'] + '::' + var['raw_type'] + for x in var["type"].split(): + if x not in p: + raw.append(x) + var["raw_type"] = " ".join(raw) + + # if 'AutoConstantEntry' in var['raw_type']: print(var); assert 0 + if var["class"]: + if "::" not in var["raw_type"]: + if not var["class"]["parent"]: + var["raw_type"] = ( + var["class"]["namespace"] + "::" + var["raw_type"] + ) + elif var["class"]["parent"] in self.classes: + parent = self.classes[var["class"]["parent"]] + var["raw_type"] = ( + parent["namespace"] + + "::" + + var["class"]["name"] + + "::" + + var["raw_type"] + ) else: - var['unresolved'] = True - - elif '::' in var['raw_type'] and var['raw_type'].split('::')[0] not in self.namespaces: - var['raw_type'] = var['class']['namespace'] + '::' + var['raw_type'] + var["unresolved"] = True + + elif ( + "::" in var["raw_type"] + and var["raw_type"].split("::")[0] not in self.namespaces + ): + var["raw_type"] = ( + var["class"]["namespace"] + "::" + var["raw_type"] + ) else: - var['unresolved'] = True - - elif 'forward_declared' in var and 'namespace' in var: - if '::' not in var['raw_type']: - var['raw_type'] = var['namespace'] + var['raw_type'] - elif '::' in var['raw_type'] and var['raw_type'].split('::')[0] in self.namespaces: + var["unresolved"] = True + + elif "forward_declared" in var and "namespace" in var: + if "::" not in var["raw_type"]: + var["raw_type"] = var["namespace"] + var["raw_type"] + elif ( + "::" in var["raw_type"] + and var["raw_type"].split("::")[0] in self.namespaces + ): pass - else: trace_print('-'*80); trace_print(var); raise NotImplemented - + else: + trace_print("-" * 80) + trace_print(var) + raise NotImplemented ## need full name space for classes in raw type ## - if var['raw_type'].startswith( '::' ): - #print(var) - #print('NAMESPACE', var['class']['namespace']) - #print( 'PARENT NS', var['class']['parent']['namespace'] ) - #assert 0 - var['unresolved'] = True - if 'method' in var: var['method']['unresolved_parameters'] = True - #var['raw_type'] = var['raw_type'][2:] - + if var["raw_type"].startswith("::"): + # print(var) + # print('NAMESPACE', var['class']['namespace']) + # print( 'PARENT NS', var['class']['parent']['namespace'] ) + # assert 0 + var["unresolved"] = True + if "method" in var: + var["method"]["unresolved_parameters"] = True + # var['raw_type'] = var['raw_type'][2:] + # Take care of #defines and #pragmas etc - trace_print("Processing precomp_macro_buf: %s"%self._precomp_macro_buf) + trace_print("Processing precomp_macro_buf: %s" % self._precomp_macro_buf) for m in self._precomp_macro_buf: macro = m.replace("\\n", "\n") try: if macro.lower().startswith("#define"): - trace_print("Adding #define %s"%macro) + trace_print("Adding #define %s" % macro) self.defines.append(re.split("[\t ]+", macro, 1)[1].strip()) elif macro.lower().startswith("#pragma"): - trace_print("Adding #pragma %s"%macro) + trace_print("Adding #pragma %s" % macro) self.pragmas.append(re.split("[\t ]+", macro, 1)[1].strip()) elif macro.lower().startswith("#include"): - trace_print("Adding #include %s"%macro) + trace_print("Adding #include %s" % macro) self.includes.append(re.split("[\t ]+", macro, 1)[1].strip()) else: - debug_print("Cant detect what to do with precomp macro '%s'"%macro) - except: pass + debug_print( + "Cant detect what to do with precomp macro '%s'" % macro + ) + except: + pass self._precomp_macro_buf = None - - def concrete_typedef( self, key ): + def concrete_typedef(self, key): if key not in self.typedefs: - #print( 'FAILED typedef', key ) + # print( 'FAILED typedef', key ) return None while key in self.typedefs: prev = key - key = self.typedefs[ key ] - if '<' in key or '>' in key: return prev # stop at template - if key.startswith('std::'): return key # stop at std lib + key = self.typedefs[key] + if "<" in key or ">" in key: + return prev # stop at template + if key.startswith("std::"): + return key # stop at std lib return key -class _CppHeader( Resolver ): +class _CppHeader(Resolver): def finalize(self): self.finalize_vars() # finalize classes and method returns types for cls in list(self.classes.values()): for meth in cls.get_all_methods(): - if meth['pure_virtual']: cls['abstract'] = True + if meth["pure_virtual"]: + cls["abstract"] = True - if not meth['returns_fundamental'] and meth['returns'] in C99_NONSTANDARD: - meth['returns'] = C99_NONSTANDARD[meth['returns']] - meth['returns_fundamental'] = True + if ( + not meth["returns_fundamental"] + and meth["returns"] in C99_NONSTANDARD + ): + meth["returns"] = C99_NONSTANDARD[meth["returns"]] + meth["returns_fundamental"] = True - elif not meth['returns_fundamental']: # describe the return type + elif not meth["returns_fundamental"]: # describe the return type con = None - if cls['namespace'] and '::' not in meth['returns']: - con = self.concrete_typedef( cls['namespace'] + '::' + meth['returns'] ) - else: con = self.concrete_typedef( meth['returns'] ) - + if cls["namespace"] and "::" not in meth["returns"]: + con = self.concrete_typedef( + cls["namespace"] + "::" + meth["returns"] + ) + else: + con = self.concrete_typedef(meth["returns"]) if con: - meth['returns_concrete'] = con - meth['returns_fundamental'] = is_fundamental( con ) - - elif meth['returns'] in self.classes: - trace_print( 'meth returns class:', meth['returns'] ) - meth['returns_class'] = True - - elif meth['returns'] in self.SubTypedefs: - meth['returns_class'] = True - meth['returns_nested'] = self.SubTypedefs[ meth['returns'] ] - - elif meth['returns'] in cls._public_enums: - enum = cls._public_enums[ meth['returns'] ] - meth['returns_enum'] = enum['type'] - meth['returns_fundamental'] = True - if enum['type'] == int: meth['returns'] = 'int' - else: meth['returns'] = 'char*' - - elif meth['returns'] in self.global_enums: - enum = self.global_enums[ meth['returns'] ] - meth['returns_enum'] = enum['type'] - meth['returns_fundamental'] = True - if enum['type'] == int: meth['returns'] = 'int' - else: meth['returns'] = 'char*' - - elif meth['returns'].count('::')==1: - trace_print( meth ) - a,b = meth['returns'].split('::') + meth["returns_concrete"] = con + meth["returns_fundamental"] = is_fundamental(con) + + elif meth["returns"] in self.classes: + trace_print("meth returns class:", meth["returns"]) + meth["returns_class"] = True + + elif meth["returns"] in self.SubTypedefs: + meth["returns_class"] = True + meth["returns_nested"] = self.SubTypedefs[meth["returns"]] + + elif meth["returns"] in cls._public_enums: + enum = cls._public_enums[meth["returns"]] + meth["returns_enum"] = enum["type"] + meth["returns_fundamental"] = True + if enum["type"] == int: + meth["returns"] = "int" + else: + meth["returns"] = "char*" + + elif meth["returns"] in self.global_enums: + enum = self.global_enums[meth["returns"]] + meth["returns_enum"] = enum["type"] + meth["returns_fundamental"] = True + if enum["type"] == int: + meth["returns"] = "int" + else: + meth["returns"] = "char*" + + elif meth["returns"].count("::") == 1: + trace_print(meth) + a, b = meth["returns"].split("::") if a in self.namespaces: if b in self.classes: - klass = self.classes[ b ] - meth['returns_class'] = a + '::' + b - elif '<' in b and '>' in b: - warning_print( 'WARN-can not return template: %s'%b ) - meth['returns_unknown'] = True + klass = self.classes[b] + meth["returns_class"] = a + "::" + b + elif "<" in b and ">" in b: + warning_print("WARN-can not return template: %s" % b) + meth["returns_unknown"] = True elif b in self.global_enums: - enum = self.global_enums[ b ] - meth['returns_enum'] = enum['type'] - meth['returns_fundamental'] = True - if enum['type'] == int: meth['returns'] = 'int' - else: meth['returns'] = 'char*' + enum = self.global_enums[b] + meth["returns_enum"] = enum["type"] + meth["returns_fundamental"] = True + if enum["type"] == int: + meth["returns"] = "int" + else: + meth["returns"] = "char*" - else: trace_print( a, b); trace_print( meth); meth['returns_unknown'] = True # +++ + else: + trace_print(a, b) + trace_print(meth) + meth["returns_unknown"] = True # +++ elif a in self.classes: - klass = self.classes[ a ] + klass = self.classes[a] if b in klass._public_enums: - trace_print( '...found nested enum', b ) - enum = klass._public_enums[ b ] - meth['returns_enum'] = enum['type'] - meth['returns_fundamental'] = True - if enum['type'] == int: meth['returns'] = 'int' - else: meth['returns'] = 'char*' + trace_print("...found nested enum", b) + enum = klass._public_enums[b] + meth["returns_enum"] = enum["type"] + meth["returns_fundamental"] = True + if enum["type"] == int: + meth["returns"] = "int" + else: + meth["returns"] = "char*" elif b in klass._public_forward_declares: - meth['returns_class'] = True + meth["returns_class"] = True elif b in klass._public_typedefs: - typedef = klass._public_typedefs[ b ] - meth['returns_fundamental'] = is_fundamental( typedef ) + typedef = klass._public_typedefs[b] + meth["returns_fundamental"] = is_fundamental(typedef) else: - trace_print( meth ) # should be a nested class, TODO fix me. - meth['returns_unknown'] = True - - elif '::' in meth['returns']: - trace_print('TODO namespace or extra nested return:', meth) - meth['returns_unknown'] = True + trace_print( + meth + ) # should be a nested class, TODO fix me. + meth["returns_unknown"] = True + + elif "::" in meth["returns"]: + trace_print("TODO namespace or extra nested return:", meth) + meth["returns_unknown"] = True else: - trace_print( 'WARN: UNKNOWN RETURN', meth['name'], meth['returns']) - meth['returns_unknown'] = True - + trace_print( + "WARN: UNKNOWN RETURN", meth["name"], meth["returns"] + ) + meth["returns_unknown"] = True + if meth["returns"].startswith(": : "): meth["returns"] = meth["returns"].replace(": : ", "::") - + for cls in list(self.classes.values()): methnames = cls.get_all_method_names() pvm = cls.get_all_pure_virtual_methods() - for d in cls['inherits']: - c = d['class'] - a = d['access'] # do not depend on this to be 'public' - trace_print( 'PARENT CLASS:', c ) - if c not in self.classes: trace_print('WARN: parent class not found') - if c in self.classes and self.classes[c]['abstract']: - p = self.classes[ c ] - for meth in p.get_all_methods(): #p["methods"]["public"]: - trace_print( '\t\tmeth', meth['name'], 'pure virtual', meth['pure_virtual'] ) - if meth['pure_virtual'] and meth['name'] not in methnames: cls['abstract'] = True; break - - - - + for d in cls["inherits"]: + c = d["class"] + a = d["access"] # do not depend on this to be 'public' + trace_print("PARENT CLASS:", c) + if c not in self.classes: + trace_print("WARN: parent class not found") + if c in self.classes and self.classes[c]["abstract"]: + p = self.classes[c] + for meth in p.get_all_methods(): # p["methods"]["public"]: + trace_print( + "\t\tmeth", + meth["name"], + "pure virtual", + meth["pure_virtual"], + ) + if meth["pure_virtual"] and meth["name"] not in methnames: + cls["abstract"] = True + break def evaluate_struct_stack(self): """Create a Struct out of the name stack (but not its parts)""" - #print( 'eval struct stack', self.nameStack ) - #if self.braceDepth != len(self.nameSpaces): return + # print( 'eval struct stack', self.nameStack ) + # if self.braceDepth != len(self.nameSpaces): return struct = CppStruct(self.nameStack) struct["namespace"] = self.cur_namespace() - self.structs[ struct['type'] ] = struct - self.structs_order.append( struct ) + self.structs[struct["type"]] = struct + self.structs_order.append(struct) if self.curClass: - struct['parent'] = self.curClass - klass = self.classes[ self.curClass ] - klass['structs'][self.curAccessSpecifier].append( struct ) - if self.curAccessSpecifier == 'public': klass._public_structs[ struct['type'] ] = struct + struct["parent"] = self.curClass + klass = self.classes[self.curClass] + klass["structs"][self.curAccessSpecifier].append(struct) + if self.curAccessSpecifier == "public": + klass._public_structs[struct["type"]] = struct self.curStruct = struct - self._structs_brace_level[ struct['type'] ] = self.braceDepth - - - def parse_method_type( self, stack ): - trace_print( 'meth type info', stack ) - if stack[0] in ':;' and stack[1] != ':': stack = stack[1:] - info = { - 'debug': ' '.join(stack).replace(' : : ', '::' ).replace(' < ', '<' ).replace(' > ', '> ' ).replace(" >",">").replace(">>", "> >").replace(">>", "> >"), - 'class':None, - 'namespace':self.cur_namespace(add_double_colon=True), + self._structs_brace_level[struct["type"]] = self.braceDepth + + def parse_method_type(self, stack): + trace_print("meth type info", stack) + if stack[0] in ":;" and stack[1] != ":": + stack = stack[1:] + info = { + "debug": " ".join(stack) + .replace(" : : ", "::") + .replace(" < ", "<") + .replace(" > ", "> ") + .replace(" >", ">") + .replace(">>", "> >") + .replace(">>", "> >"), + "class": None, + "namespace": self.cur_namespace(add_double_colon=True), } - for tag in 'defined pure_virtual operator constructor destructor extern template virtual static explicit inline friend returns returns_pointer returns_fundamental returns_class default'.split(): info[tag]=False - header = stack[ : stack.index('(') ] - header = ' '.join( header ) - header = header.replace(' : : ', '::' ) - header = header.replace(' < ', '<' ) - header = header.replace(' > ', '> ' ) - header = header.replace('default ', 'default' ) + for ( + tag + ) in "defined pure_virtual operator constructor destructor extern template virtual static explicit inline friend returns returns_pointer returns_fundamental returns_class default".split(): + info[tag] = False + header = stack[: stack.index("(")] + header = " ".join(header) + header = header.replace(" : : ", "::") + header = header.replace(" < ", "<") + header = header.replace(" > ", "> ") + header = header.replace("default ", "default") header = header.strip() - if '{' in stack: - info['defined'] = True + if "{" in stack: + info["defined"] = True self._method_body = self.braceDepth + 1 - trace_print( 'NEW METHOD WITH BODY', self.braceDepth ) - elif stack[-1] == ';': - info['defined'] = False - self._method_body = None # not a great idea to be clearing here - else: assert 0 + trace_print("NEW METHOD WITH BODY", self.braceDepth) + elif stack[-1] == ";": + info["defined"] = False + self._method_body = None # not a great idea to be clearing here + else: + assert 0 - if len(stack) > 3 and stack[-1] == ';' and stack[-2] == '0' and stack[-3] == '=': - info['pure_virtual'] = True + if ( + len(stack) > 3 + and stack[-1] == ";" + and stack[-2] == "0" + and stack[-3] == "=" + ): + info["pure_virtual"] = True r = header.split() name = None - if 'operator' in stack: # rare case op overload defined outside of class - op = stack[ stack.index('operator')+1 : stack.index('(') ] - op = ''.join(op) + if "operator" in stack: # rare case op overload defined outside of class + op = stack[stack.index("operator") + 1 : stack.index("(")] + op = "".join(op) if not op: - if " ".join(['operator', '(', ')', '(']) in " ".join(stack): + if " ".join(["operator", "(", ")", "("]) in " ".join(stack): op = "()" else: - trace_print( 'Error parsing operator') + trace_print("Error parsing operator") return None - - info['operator'] = op - name = 'operator' + op - a = stack[ : stack.index('operator') ] + + info["operator"] = op + name = "operator" + op + a = stack[: stack.index("operator")] elif r: name = r[-1] - a = r[ : -1 ] # strip name + a = r[:-1] # strip name - if name is None: return None - #if name.startswith('~'): name = name[1:] + if name is None: + return None + # if name.startswith('~'): name = name[1:] - while a and a[0] == '}': # strip - can have multiple } } + while a and a[0] == "}": # strip - can have multiple } } a = a[1:] - - if '::' in name: - #klass,name = name.split('::') # methods can be defined outside of class - klass = name[ : name.rindex('::') ] - name = name.split('::')[-1] - info['class'] = klass + if "::" in name: + # klass,name = name.split('::') # methods can be defined outside of class + klass = name[: name.rindex("::")] + name = name.split("::")[-1] + info["class"] = klass if klass in self.classes and not self.curClass: - #Class function defined outside the class + # Class function defined outside the class return None # info['name'] = name - #else: info['name'] = name + # else: info['name'] = name - if name.startswith('~'): - info['destructor'] = True - if 'default;' in stack: - info['defined'] = True - info['default'] = True + if name.startswith("~"): + info["destructor"] = True + if "default;" in stack: + info["defined"] = True + info["default"] = True name = name[1:] elif not a or (name == self.curClass and len(self.curClass)): - info['constructor'] = True - if 'default;' in stack: - info['defined'] = True - info['default'] = True - - info['name'] = name - - for tag in 'extern virtual static explicit inline friend'.split(): - if tag in a: info[ tag ] = True; a.remove( tag ) # inplace - if 'template' in a: - a.remove('template') - b = ' '.join( a ) - if '>' in b: - info['template'] = b[ : b.index('>')+1 ] - info['returns'] = b[ b.index('>')+1 : ] # find return type, could be incorrect... TODO - if '' - if typname not in self._template_typenames: self._template_typenames.append( typname ) - else: info['returns'] = ' '.join( a ) - else: info['returns'] = ' '.join( a ) - info['returns'] = info['returns'].replace(' <', '<').strip() + info["constructor"] = True + if "default;" in stack: + info["defined"] = True + info["default"] = True + + info["name"] = name + + for tag in "extern virtual static explicit inline friend".split(): + if tag in a: + info[tag] = True + a.remove(tag) # inplace + if "template" in a: + a.remove("template") + b = " ".join(a) + if ">" in b: + info["template"] = b[: b.index(">") + 1] + info["returns"] = b[ + b.index(">") + 1 : + ] # find return type, could be incorrect... TODO + if "' + if typname not in self._template_typenames: + self._template_typenames.append(typname) + else: + info["returns"] = " ".join(a) + else: + info["returns"] = " ".join(a) + info["returns"] = info["returns"].replace(" <", "<").strip() ## be careful with templates, do not count pointers inside template - info['returns_pointer'] = info['returns'].split('>')[-1].count('*') - if info['returns_pointer']: info['returns'] = info['returns'].replace('*','').strip() + info["returns_pointer"] = info["returns"].split(">")[-1].count("*") + if info["returns_pointer"]: + info["returns"] = info["returns"].replace("*", "").strip() - info['returns_reference'] = '&' in info['returns'] - if info['returns']: info['returns'] = info['returns'].replace('&','').strip() + info["returns_reference"] = "&" in info["returns"] + if info["returns"]: + info["returns"] = info["returns"].replace("&", "").strip() a = [] - for b in info['returns'].split(): - if b == '__const__': info['returns_const'] = True - elif b == 'const': info['returns_const'] = True - else: a.append( b ) - info['returns'] = ' '.join( a ) + for b in info["returns"].split(): + if b == "__const__": + info["returns_const"] = True + elif b == "const": + info["returns_const"] = True + else: + a.append(b) + info["returns"] = " ".join(a) - info['returns_fundamental'] = is_fundamental( info['returns'] ) + info["returns_fundamental"] = is_fundamental(info["returns"]) return info def evaluate_method_stack(self): """Create a method out of the name stack""" if self.curStruct: - trace_print( 'WARN - struct contains methods - skipping' ) - trace_print( self.stack ) + trace_print("WARN - struct contains methods - skipping") + trace_print(self.stack) assert 0 - - info = self.parse_method_type( self.stack ) + + info = self.parse_method_type(self.stack) if info: - if info[ 'class' ] and info['class'] in self.classes: # case where methods are defined outside of class - newMethod = CppMethod(self.nameStack, info['name'], info, self.curTemplate) - klass = self.classes[ info['class'] ] - klass[ 'methods' ][ 'public' ].append( newMethod ) - newMethod['parent'] = klass - if klass['namespace']: newMethod['path'] = klass['namespace'] + '::' + klass['name'] - else: newMethod['path'] = klass['name'] - - elif self.curClass: # normal case - newMethod = CppMethod(self.nameStack, self.curClass, info, self.curTemplate) + if ( + info["class"] and info["class"] in self.classes + ): # case where methods are defined outside of class + newMethod = CppMethod( + self.nameStack, info["name"], info, self.curTemplate + ) + klass = self.classes[info["class"]] + klass["methods"]["public"].append(newMethod) + newMethod["parent"] = klass + if klass["namespace"]: + newMethod["path"] = klass["namespace"] + "::" + klass["name"] + else: + newMethod["path"] = klass["name"] + + elif self.curClass: # normal case + newMethod = CppMethod( + self.nameStack, self.curClass, info, self.curTemplate + ) klass = self.classes[self.curClass] - klass['methods'][self.curAccessSpecifier].append(newMethod) - newMethod['parent'] = klass - if klass['namespace']: newMethod['path'] = klass['namespace'] + '::' + klass['name'] - else: newMethod['path'] = klass['name'] - else: #non class functions + klass["methods"][self.curAccessSpecifier].append(newMethod) + newMethod["parent"] = klass + if klass["namespace"]: + newMethod["path"] = klass["namespace"] + "::" + klass["name"] + else: + newMethod["path"] = klass["name"] + else: # non class functions debug_print("FREE FUNCTION") newMethod = CppMethod(self.nameStack, None, info, self.curTemplate) self.functions.append(newMethod) global parseHistory - parseHistory.append({"braceDepth": self.braceDepth, "item_type": "method", "item": newMethod}) + parseHistory.append( + { + "braceDepth": self.braceDepth, + "item_type": "method", + "item": newMethod, + } + ) else: - trace_print( 'free function?', self.nameStack ) + trace_print("free function?", self.nameStack) self.stack = [] - def _parse_typedef( self, stack, namespace='' ): - if not stack or 'typedef' not in stack: return - stack = list( stack ) # copy just to be safe - if stack[-1] == ';': stack.pop() + def _parse_typedef(self, stack, namespace=""): + if not stack or "typedef" not in stack: + return + stack = list(stack) # copy just to be safe + if stack[-1] == ";": + stack.pop() - while stack and stack[-1].isdigit(): stack.pop() # throw away array size for now + while stack and stack[-1].isdigit(): + stack.pop() # throw away array size for now - idx = stack.index('typedef') + idx = stack.index("typedef") if stack[-1] == "]": try: name = namespace + "".join(stack[-4:]) @@ -1903,181 +2281,215 @@ def _parse_typedef( self, stack, namespace='' ): name = namespace + stack[-1] else: name = namespace + stack[-1] - s = '' - for a in stack[idx+1:-1]: - if a == '{': break - if not s or s[-1] in ':<>' or a in ':<>': s += a # keep compact - else: s += ' ' + a # spacing + s = "" + for a in stack[idx + 1 : -1]: + if a == "{": + break + if not s or s[-1] in ":<>" or a in ":<>": + s += a # keep compact + else: + s += " " + a # spacing - r = {'name':name, 'raw':s, 'type':s} + r = {"name": name, "raw": s, "type": s} if not is_fundamental(s): - if 'struct' in s.split(): pass # TODO is this right? "struct ns::something" - elif '::' not in s: s = namespace + s # only add the current name space if no namespace given - r['type'] = s - if s: return r - + if "struct" in s.split(): + pass # TODO is this right? "struct ns::something" + elif "::" not in s: + s = ( + namespace + s + ) # only add the current name space if no namespace given + r["type"] = s + if s: + return r def evaluate_typedef(self): ns = self.cur_namespace(add_double_colon=True) - res = self._parse_typedef( self.stack, ns ) + res = self._parse_typedef(self.stack, ns) if res: - name = res['name'] - self.typedefs[ name ] = res['type'] - if name not in self.typedefs_order: self.typedefs_order.append( name ) - + name = res["name"] + self.typedefs[name] = res["type"] + if name not in self.typedefs_order: + self.typedefs_order.append(name) def evaluate_property_stack(self): """Create a Property out of the name stack""" global parseHistory - assert self.stack[-1] == ';' - debug_print( "trace" ) - if self.nameStack[0] == 'typedef': + assert self.stack[-1] == ";" + debug_print("trace") + if self.nameStack[0] == "typedef": if self.curClass: - typedef = self._parse_typedef( self.stack ) - name = typedef['name'] - klass = self.classes[ self.curClass ] - klass[ 'typedefs' ][ self.curAccessSpecifier ].append( name ) - if self.curAccessSpecifier == 'public': klass._public_typedefs[ name ] = typedef['type'] - Resolver.SubTypedefs[ name ] = self.curClass - else: assert 0 + typedef = self._parse_typedef(self.stack) + name = typedef["name"] + klass = self.classes[self.curClass] + klass["typedefs"][self.curAccessSpecifier].append(name) + if self.curAccessSpecifier == "public": + klass._public_typedefs[name] = typedef["type"] + Resolver.SubTypedefs[name] = self.curClass + else: + assert 0 elif self.curStruct or self.curClass: if len(self.nameStack) == 1: - #See if we can de anonymize the type - filteredParseHistory = [h for h in parseHistory if h["braceDepth"] == self.braceDepth] - if len(filteredParseHistory) and filteredParseHistory[-1]["item_type"] == "class": + # See if we can de anonymize the type + filteredParseHistory = [ + h for h in parseHistory if h["braceDepth"] == self.braceDepth + ] + if ( + len(filteredParseHistory) + and filteredParseHistory[-1]["item_type"] == "class" + ): self.nameStack.insert(0, filteredParseHistory[-1]["item"]["name"]) - debug_print("DEANONYMOIZING %s to type '%s'"%(self.nameStack[1], self.nameStack[0])) - if "," in self.nameStack: #Maybe we have a variable list - #Figure out what part is the variable separator but remember templates of function pointer - #First find left most comma outside of a > and ) - leftMostComma = 0; + debug_print( + "DEANONYMOIZING %s to type '%s'" + % (self.nameStack[1], self.nameStack[0]) + ) + if "," in self.nameStack: # Maybe we have a variable list + # Figure out what part is the variable separator but remember templates of function pointer + # First find left most comma outside of a > and ) + leftMostComma = 0 for i in range(0, len(self.nameStack)): name = self.nameStack[i] - if name in (">", ")"): leftMostComma = 0 - if leftMostComma == 0 and name == ",": leftMostComma = i + if name in (">", ")"): + leftMostComma = 0 + if leftMostComma == 0 and name == ",": + leftMostComma = i # Is it really a list of variables? if leftMostComma != 0: - trace_print("Multiple variables for namestack in %s. Separating processing"%self.nameStack) + trace_print( + "Multiple variables for namestack in %s. Separating processing" + % self.nameStack + ) orig_nameStack = self.nameStack[:] orig_stack = self.stack[:] - - type_nameStack = orig_nameStack[:leftMostComma-1] - for name in orig_nameStack[leftMostComma - 1::2]: + + type_nameStack = orig_nameStack[: leftMostComma - 1] + for name in orig_nameStack[leftMostComma - 1 :: 2]: self.nameStack = type_nameStack + [name] - self.stack = orig_stack[:] # Not maintained for mucking, but this path it doesnt matter + self.stack = orig_stack[ + : + ] # Not maintained for mucking, but this path it doesnt matter self.evaluate_property_stack() return - + newVar = CppVariable(self.nameStack) - newVar['namespace'] = self.current_namespace() + newVar["namespace"] = self.current_namespace() if self.curStruct: - self.curStruct[ 'fields' ].append( newVar ) - newVar['property_of_struct'] = self.curStruct + self.curStruct["fields"].append(newVar) + newVar["property_of_struct"] = self.curStruct elif self.curClass: klass = self.classes[self.curClass] klass["properties"][self.curAccessSpecifier].append(newVar) - newVar['property_of_class'] = klass['name'] - parseHistory.append({"braceDepth": self.braceDepth, "item_type": "variable", "item": newVar}) + newVar["property_of_class"] = klass["name"] + parseHistory.append( + {"braceDepth": self.braceDepth, "item_type": "variable", "item": newVar} + ) else: - debug_print( "Found Global variable" ) + debug_print("Found Global variable") newVar = CppVariable(self.nameStack) self.variables.append(newVar) - self.stack = [] # CLEAR STACK + self.stack = [] # CLEAR STACK def evaluate_class_stack(self): """Create a Class out of the name stack (but not its parts)""" - #dont support sub classes today - #print( 'eval class stack', self.nameStack ) + # dont support sub classes today + # print( 'eval class stack', self.nameStack ) parent = self.curClass - if self.braceDepth > len( self.nameSpaces) and parent: - trace_print( 'HIT NESTED SUBCLASS' ) + if self.braceDepth > len(self.nameSpaces) and parent: + trace_print("HIT NESTED SUBCLASS") self.accessSpecifierStack.append(self.curAccessSpecifier) elif self.braceDepth != len(self.nameSpaces): - error_print( 'ERROR: WRONG BRACE DEPTH' ) + error_print("ERROR: WRONG BRACE DEPTH") return - + # When dealing with typedefed structs, get rid of typedef keyword to handle later on if self.nameStack[0] == "typedef": del self.nameStack[0] if len(self.nameStack) == 1: self.anon_struct_counter += 1 # We cant handle more than 1 anonymous struct, so name them uniquely - self.nameStack.append(""%self.anon_struct_counter) - + self.nameStack.append("" % self.anon_struct_counter) + if self.nameStack[0] == "class": - self.curAccessSpecifier = 'private' - else:#struct - self.curAccessSpecifier = 'public' - debug_print("curAccessSpecifier changed/defaulted to %s"%self.curAccessSpecifier) + self.curAccessSpecifier = "private" + else: # struct + self.curAccessSpecifier = "public" + debug_print( + "curAccessSpecifier changed/defaulted to %s" % self.curAccessSpecifier + ) if self.nameStack[0] == "union": newClass = CppUnion(self.nameStack) - if newClass['name'] == 'union ': + if newClass["name"] == "union ": self.anon_union_counter = [self.braceDepth, 2] else: self.anon_union_counter = [self.braceDepth, 1] - trace_print( 'NEW UNION', newClass['name'] ) + trace_print("NEW UNION", newClass["name"]) else: newClass = CppClass(self.nameStack, self.curTemplate) - trace_print( 'NEW CLASS', newClass['name'] ) + trace_print("NEW CLASS", newClass["name"]) newClass["declaration_method"] = self.nameStack[0] - self.classes_order.append( newClass ) # good idea to save ordering - self.stack = [] # fixes if class declared with ';' in closing brace + self.classes_order.append(newClass) # good idea to save ordering + self.stack = [] # fixes if class declared with ';' in closing brace if parent: - newClass["namespace"] = self.classes[ parent ]['namespace'] + '::' + parent - newClass['parent'] = parent - self.classes[ parent ]['nested_classes'].append( newClass ) + newClass["namespace"] = self.classes[parent]["namespace"] + "::" + parent + newClass["parent"] = parent + self.classes[parent]["nested_classes"].append(newClass) ## supports nested classes with the same name ## - self.curClass = key = parent+'::'+newClass['name'] - self._classes_brace_level[ key ] = self.braceDepth + self.curClass = key = parent + "::" + newClass["name"] + self._classes_brace_level[key] = self.braceDepth - elif newClass['parent']: # nested class defined outside of parent. A::B {...} - parent = newClass['parent'] - newClass["namespace"] = self.classes[ parent ]['namespace'] + '::' + parent - self.classes[ parent ]['nested_classes'].append( newClass ) + elif newClass["parent"]: # nested class defined outside of parent. A::B {...} + parent = newClass["parent"] + newClass["namespace"] = self.classes[parent]["namespace"] + "::" + parent + self.classes[parent]["nested_classes"].append(newClass) ## supports nested classes with the same name ## - self.curClass = key = parent+'::'+newClass['name'] - self._classes_brace_level[ key ] = self.braceDepth + self.curClass = key = parent + "::" + newClass["name"] + self._classes_brace_level[key] = self.braceDepth else: newClass["namespace"] = self.cur_namespace() - key = newClass['name'] + key = newClass["name"] self.curClass = newClass["name"] - self._classes_brace_level[ newClass['name'] ] = self.braceDepth + self._classes_brace_level[newClass["name"]] = self.braceDepth if not key.endswith("::") and not key.endswith(" ") and len(key) != 0: if key in self.classes: - trace_print( 'ERROR name collision:', key ) + trace_print("ERROR name collision:", key) self.classes[key].show() - trace_print('-'*80) - newClass.show() - assert key not in self.classes # namespace collision - self.classes[ key ] = newClass + trace_print("-" * 80) + newClass.show() + assert key not in self.classes # namespace collision + self.classes[key] = newClass global parseHistory - parseHistory.append({"braceDepth": self.braceDepth, "item_type": "class", "item": newClass}) - + parseHistory.append( + {"braceDepth": self.braceDepth, "item_type": "class", "item": newClass} + ) def evalute_forward_decl(self): - trace_print( 'FORWARD DECL', self.nameStack ) - assert self.nameStack[0] in ('class', 'struct') + trace_print("FORWARD DECL", self.nameStack) + assert self.nameStack[0] in ("class", "struct") name = self.nameStack[-1] if self.curClass: - klass = self.classes[ self.curClass ] - klass['forward_declares'][self.curAccessSpecifier].append( name ) - if self.curAccessSpecifier == 'public': klass._public_forward_declares.append( name ) - else: self._forward_decls.append( name ) + klass = self.classes[self.curClass] + klass["forward_declares"][self.curAccessSpecifier].append(name) + if self.curAccessSpecifier == "public": + klass._public_forward_declares.append(name) + else: + self._forward_decls.append(name) -class CppHeader( _CppHeader ): + +class CppHeader(_CppHeader): """Parsed C++ class header Variables produced: self.classes - Dictionary of classes found in a given header file where the key is the name of the class """ - IGNORE_NAMES = '__extension__'.split() - + + IGNORE_NAMES = "__extension__".split() + def show(self): - for className in list(self.classes.keys()):self.classes[className].show() + for className in list(self.classes.keys()): + self.classes[className].show() def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): """Create the parsed C++ header file parse tree @@ -2092,7 +2504,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): CppVariable.Vars = [] CppStruct.Structs = [] - if (argType == "file"): + if argType == "file": self.headerFileName = os.path.expandvars(headerFileName) self.mainClass = os.path.split(self.headerFileName)[1][:-2] headerFileStr = "" @@ -2103,46 +2515,53 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): else: raise Exception("Arg type must be either file or string") self.curClass = "" - + # nested classes have parent::nested, but no extra namespace, - # this keeps the API compatible, TODO proper namespace for everything. + # this keeps the API compatible, TODO proper namespace for everything. Resolver.CLASSES = {} self.classes = Resolver.CLASSES - #Functions that are not part of a class + # Functions that are not part of a class self.functions = [] - + self.pragmas = [] self.defines = [] self.includes = [] - self._precomp_macro_buf = [] #for internal purposes, will end up filling out pragmras and defines at the end + self._precomp_macro_buf = ( + [] + ) # for internal purposes, will end up filling out pragmras and defines at the end self.enums = [] self.variables = [] self.global_enums = {} self.nameStack = [] self.nameSpaces = [] - self.curAccessSpecifier = 'private' # private is default + self.curAccessSpecifier = "private" # private is default self.curTemplate = None self.accessSpecifierStack = [] self.accessSpecifierScratch = [] - debug_print("curAccessSpecifier changed/defaulted to %s"%self.curAccessSpecifier) + debug_print( + "curAccessSpecifier changed/defaulted to %s" % self.curAccessSpecifier + ) self.initextra() # Old namestacks for a given level self.nameStackHistory = [] - self.anon_struct_counter = 0 + self.anon_struct_counter = 0 self.anon_union_counter = [-1, 0] self.templateRegistry = [] - if (len(self.headerFileName)): - fd = io.open(self.headerFileName, 'r', encoding=encoding) + if len(self.headerFileName): + fd = io.open(self.headerFileName, "r", encoding=encoding) headerFileStr = "".join(fd.readlines()) fd.close() - + # Make sure supportedAccessSpecifier are sane for i in range(0, len(supportedAccessSpecifier)): - if " " not in supportedAccessSpecifier[i]: continue - supportedAccessSpecifier[i] = re.sub("[ ]+", " ", supportedAccessSpecifier[i]).strip() - + if " " not in supportedAccessSpecifier[i]: + continue + supportedAccessSpecifier[i] = re.sub( + "[ ]+", " ", supportedAccessSpecifier[i] + ).strip() + # Strip out template declarations templateSectionsToSliceOut = [] try: @@ -2150,65 +2569,79 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): start = m.start() # Search for the final '>' which may or may not be caught in the case of nexted <>'s for i in range(start, len(headerFileStr)): - if headerFileStr[i] == '<': + if headerFileStr[i] == "<": firstBracket = i break ltgtStackCount = 1 - #Now look for fianl '>' + # Now look for fianl '>' for i in range(firstBracket + 1, len(headerFileStr)): - if headerFileStr[i] == '<': + if headerFileStr[i] == "<": ltgtStackCount += 1 - elif headerFileStr[i] == '>': + elif headerFileStr[i] == ">": ltgtStackCount -= 1 if ltgtStackCount == 0: end = i break templateSectionsToSliceOut.append((start, end)) - + # Now strip out all instances of the template templateSectionsToSliceOut.reverse() for tslice in templateSectionsToSliceOut: # Replace the template symbol with a single symbol - template_symbol="CppHeaderParser_template_%d"%len(self.templateRegistry) - self.templateRegistry.append(headerFileStr[tslice[0]: tslice[1]+1]) - newlines = headerFileStr[tslice[0]: tslice[1]].count("\n") * "\n" #Keep line numbers the same - headerFileStr = headerFileStr[:tslice[0]] + newlines + " " + template_symbol + " " + headerFileStr[tslice[1] + 1:] + template_symbol = "CppHeaderParser_template_%d" % len( + self.templateRegistry + ) + self.templateRegistry.append(headerFileStr[tslice[0] : tslice[1] + 1]) + newlines = ( + headerFileStr[tslice[0] : tslice[1]].count("\n") * "\n" + ) # Keep line numbers the same + headerFileStr = ( + headerFileStr[: tslice[0]] + + newlines + + " " + + template_symbol + + " " + + headerFileStr[tslice[1] + 1 :] + ) except: pass # Change multi line #defines and expressions to single lines maintaining line nubmers # Based from http://stackoverflow.com/questions/2424458/regular-expression-to-match-cs-multiline-preprocessor-statements - matches = re.findall(r'(?m)^(?:.*\\\r?\n)+.*$', headerFileStr) - is_define = re.compile(r'[ \t\v]*#[Dd][Ee][Ff][Ii][Nn][Ee]') + matches = re.findall(r"(?m)^(?:.*\\\r?\n)+.*$", headerFileStr) + is_define = re.compile(r"[ \t\v]*#[Dd][Ee][Ff][Ii][Nn][Ee]") for m in matches: - #Keep the newlines so that linecount doesnt break - num_newlines = len([a for a in m if a=="\n"]) + # Keep the newlines so that linecount doesnt break + num_newlines = len([a for a in m if a == "\n"]) if is_define.match(m): new_m = m.replace("\n", "\\n") else: # Just expression taking up multiple lines, make it take 1 line for easier parsing new_m = m.replace("\\\n", " ") - if (num_newlines > 0): - new_m += "\n"*(num_newlines) + if num_newlines > 0: + new_m += "\n" * (num_newlines) headerFileStr = headerFileStr.replace(m, new_m) - - #Filter out Extern "C" statements. These are order dependent - matches = re.findall(re.compile(r'extern[\t ]+"[Cc]"[\t \n\r]*{', re.DOTALL), headerFileStr) + + # Filter out Extern "C" statements. These are order dependent + matches = re.findall( + re.compile(r'extern[\t ]+"[Cc]"[\t \n\r]*{', re.DOTALL), headerFileStr + ) for m in matches: - #Keep the newlines so that linecount doesnt break - num_newlines = len([a for a in m if a=="\n"]) - headerFileStr = headerFileStr.replace(m, "\n" * num_newlines) + # Keep the newlines so that linecount doesnt break + num_newlines = len([a for a in m if a == "\n"]) + headerFileStr = headerFileStr.replace(m, "\n" * num_newlines) headerFileStr = re.sub(r'extern[ ]+"[Cc]"[ ]*', "", headerFileStr) - - #Filter out any ignore symbols that end with "()" to account for #define magic functions + + # Filter out any ignore symbols that end with "()" to account for #define magic functions for ignore in ignoreSymbols: - if not ignore.endswith("()"): continue + if not ignore.endswith("()"): + continue while True: locStart = headerFileStr.find(ignore[:-1]) if locStart == -1: - break; + break locEnd = None - #Now walk till we find the last paren and account for sub parens + # Now walk till we find the last paren and account for sub parens parenCount = 1 inQuotes = False for i in range(locStart + len(ignore) - 1, len(headerFileStr)): @@ -2222,18 +2655,20 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): inQuotes = True if parenCount == 0: locEnd = i + 1 - break; + break else: - if c == '"' and headerFileStr[i-1] != '\\': + if c == '"' and headerFileStr[i - 1] != "\\": inQuotes = False - + if locEnd: - #Strip it out but keep the linecount the same so line numbers are right + # Strip it out but keep the linecount the same so line numbers are right match_str = headerFileStr[locStart:locEnd] - debug_print("Striping out '%s'"%match_str) - num_newlines = len([a for a in match_str if a=="\n"]) - headerFileStr = headerFileStr.replace(headerFileStr[locStart:locEnd], "\n"*num_newlines) - + debug_print("Striping out '%s'" % match_str) + num_newlines = len([a for a in match_str if a == "\n"]) + headerFileStr = headerFileStr.replace( + headerFileStr[locStart:locEnd], "\n" * num_newlines + ) + self.braceDepth = 0 lex.lex() lex.input(headerFileStr) @@ -2244,68 +2679,85 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): try: while True: tok = lex.token() - if not tok: break - if self.anon_union_counter[0] == self.braceDepth and self.anon_union_counter[1]: + if not tok: + break + if ( + self.anon_union_counter[0] == self.braceDepth + and self.anon_union_counter[1] + ): self.anon_union_counter[1] -= 1 tok.value = TagStr(tok.value, lineno=tok.lineno) - #debug_print("TOK: %s"%tok) - if tok.type == 'NAME' and tok.value in self.IGNORE_NAMES: continue - if tok.type != 'TEMPLATE_NAME': - self.stack.append( tok.value ) + # debug_print("TOK: %s"%tok) + if tok.type == "NAME" and tok.value in self.IGNORE_NAMES: + continue + if tok.type != "TEMPLATE_NAME": + self.stack.append(tok.value) curLine = tok.lineno curChar = tok.lexpos - if (tok.type in ('PRECOMP_MACRO', 'PRECOMP_MACRO_CONT')): - debug_print("PRECOMP: %s"%tok) + if tok.type in ("PRECOMP_MACRO", "PRECOMP_MACRO_CONT"): + debug_print("PRECOMP: %s" % tok) self._precomp_macro_buf.append(tok.value) self.stack = [] self.nameStack = [] continue - if tok.type == 'TEMPLATE_NAME': + if tok.type == "TEMPLATE_NAME": try: - templateId = int(tok.value.replace("CppHeaderParser_template_","")) + templateId = int( + tok.value.replace("CppHeaderParser_template_", "") + ) self.curTemplate = self.templateRegistry[templateId] - except: pass - if (tok.type == 'OPEN_BRACE'): - if len(self.nameStack) >= 2 and is_namespace(self.nameStack): # namespace {} with no name used in boost, this sets default? - if self.nameStack[1] == "__IGNORED_NAMESPACE__CppHeaderParser__":#Used in filtering extern "C" + except: + pass + if tok.type == "OPEN_BRACE": + if len(self.nameStack) >= 2 and is_namespace( + self.nameStack + ): # namespace {} with no name used in boost, this sets default? + if ( + self.nameStack[1] + == "__IGNORED_NAMESPACE__CppHeaderParser__" + ): # Used in filtering extern "C" self.nameStack[1] = "" self.nameSpaces.append(self.nameStack[1]) - ns = self.cur_namespace(); self.stack = [] - if ns not in self.namespaces: self.namespaces.append( ns ) + ns = self.cur_namespace() + self.stack = [] + if ns not in self.namespaces: + self.namespaces.append(ns) # Detect special condition of macro magic before class declaration so we # can filter it out - if 'class' in self.nameStack and self.nameStack[0] != 'class': + if "class" in self.nameStack and self.nameStack[0] != "class": classLocationNS = self.nameStack.index("class") classLocationS = self.stack.index("class") if "(" not in self.nameStack[classLocationNS:]: - debug_print("keyword 'class' found in unexpected location in nameStack, must be following #define magic. Process that before moving on") + debug_print( + "keyword 'class' found in unexpected location in nameStack, must be following #define magic. Process that before moving on" + ) origNameStack = self.nameStack origStack = self.stack - #Process first part of stack which is probably #define macro magic and may cause issues + # Process first part of stack which is probably #define macro magic and may cause issues self.nameStack = self.nameStack[:classLocationNS] self.stack = self.stack[:classLocationS] try: self.evaluate_stack() except: debug_print("Error processing #define magic... Oh well") - #Process rest of stack + # Process rest of stack self.nameStack = origNameStack[classLocationNS:] self.stack = origStack[classLocationS:] - - + if len(self.nameStack) and not is_enum_namestack(self.nameStack): self.evaluate_stack() else: self.nameStack.append(tok.value) - if self.stack and self.stack[0] == 'class': self.stack = [] + if self.stack and self.stack[0] == "class": + self.stack = [] self.braceDepth += 1 - - elif (tok.type == 'CLOSE_BRACE'): + + elif tok.type == "CLOSE_BRACE": if self.braceDepth == 0: continue - if (self.braceDepth == len(self.nameSpaces)): + if self.braceDepth == len(self.nameSpaces): tmp = self.nameSpaces.pop() - self.stack = [] # clear stack when namespace ends? + self.stack = [] # clear stack when namespace ends? if len(self.nameStack) and is_enum_namestack(self.nameStack): self.nameStack.append(tok.value) elif self.braceDepth < 10: @@ -2313,239 +2765,346 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): else: self.nameStack = [] self.braceDepth -= 1 - #self.stack = []; print 'BRACE DEPTH', self.braceDepth, 'NS', len(self.nameSpaces) - if self.curClass: debug_print( 'CURBD %s'%self._classes_brace_level[ self.curClass ] ) - - if (self.braceDepth == 0) or (self.curClass and self._classes_brace_level[self.curClass]==self.braceDepth): - trace_print( 'END OF CLASS DEF' ) + # self.stack = []; print 'BRACE DEPTH', self.braceDepth, 'NS', len(self.nameSpaces) + if self.curClass: + debug_print( + "CURBD %s" % self._classes_brace_level[self.curClass] + ) + + if (self.braceDepth == 0) or ( + self.curClass + and self._classes_brace_level[self.curClass] == self.braceDepth + ): + trace_print("END OF CLASS DEF") if self.accessSpecifierStack: self.curAccessSpecifier = self.accessSpecifierStack[-1] - self.accessSpecifierStack = self.accessSpecifierStack[:-1] - if self.curClass and self.classes[ self.curClass ]['parent']: self.curClass = self.classes[ self.curClass ]['parent'] - else: self.curClass = ""; #self.curStruct = None + self.accessSpecifierStack = self.accessSpecifierStack[:-1] + if self.curClass and self.classes[self.curClass]["parent"]: + self.curClass = self.classes[self.curClass]["parent"] + else: + self.curClass = "" + # self.curStruct = None self.stack = [] - #if self.curStruct: self.curStruct = None - if self.braceDepth == 0 or (self.curStruct and self._structs_brace_level[self.curStruct['type']]==self.braceDepth): - trace_print( 'END OF STRUCT DEF' ) + # if self.curStruct: self.curStruct = None + if self.braceDepth == 0 or ( + self.curStruct + and self._structs_brace_level[self.curStruct["type"]] + == self.braceDepth + ): + trace_print("END OF STRUCT DEF") self.curStruct = None if self._method_body and (self.braceDepth + 1) <= self._method_body: - self._method_body = None; self.stack = []; self.nameStack = []; trace_print( 'FORCE CLEAR METHBODY' ) - - if (tok.type == 'OPEN_PAREN'): + self._method_body = None + self.stack = [] + self.nameStack = [] + trace_print("FORCE CLEAR METHBODY") + + if tok.type == "OPEN_PAREN": self.nameStack.append(tok.value) - elif (tok.type == 'CLOSE_PAREN'): + elif tok.type == "CLOSE_PAREN": self.nameStack.append(tok.value) - elif (tok.type == 'OPEN_SQUARE_BRACKET'): + elif tok.type == "OPEN_SQUARE_BRACKET": self.nameStack.append(tok.value) - elif (tok.type == 'CLOSE_SQUARE_BRACKET'): + elif tok.type == "CLOSE_SQUARE_BRACKET": self.nameStack.append(tok.value) - elif (tok.type == 'TAB'): pass - elif (tok.type == 'EQUALS'): + elif tok.type == "TAB": + pass + elif tok.type == "EQUALS": self.nameStack.append(tok.value) - elif (tok.type == 'COMMA'): + elif tok.type == "COMMA": self.nameStack.append(tok.value) - elif (tok.type == 'BACKSLASH'): + elif tok.type == "BACKSLASH": self.nameStack.append(tok.value) - elif (tok.type == 'DIVIDE'): + elif tok.type == "DIVIDE": self.nameStack.append(tok.value) - elif (tok.type == 'PIPE'): + elif tok.type == "PIPE": self.nameStack.append(tok.value) - elif (tok.type == 'PERCENT'): + elif tok.type == "PERCENT": self.nameStack.append(tok.value) - elif (tok.type == 'CARET'): + elif tok.type == "CARET": self.nameStack.append(tok.value) - elif (tok.type == 'EXCLAMATION'): + elif tok.type == "EXCLAMATION": self.nameStack.append(tok.value) - elif (tok.type == 'SQUOTE'): pass - elif (tok.type == 'NUMBER' or tok.type == 'FLOAT_NUMBER'): + elif tok.type == "SQUOTE": + pass + elif tok.type == "NUMBER" or tok.type == "FLOAT_NUMBER": self.nameStack.append(tok.value) - elif (tok.type == 'MINUS'): + elif tok.type == "MINUS": self.nameStack.append(tok.value) - elif (tok.type == 'PLUS'): + elif tok.type == "PLUS": self.nameStack.append(tok.value) - elif (tok.type == 'STRING_LITERAL'): + elif tok.type == "STRING_LITERAL": self.nameStack.append(tok.value) - elif (tok.type == 'ELLIPSIS'): + elif tok.type == "ELLIPSIS": self.nameStack.append(tok.value) - elif (tok.type == 'DOT'): pass # preserve behaviour and eat individual fullstops - elif (tok.type == 'NAME' or tok.type == 'AMPERSTAND' or tok.type == 'ASTERISK' or tok.type == 'CHAR_LITERAL'): + elif tok.type == "DOT": + pass # preserve behaviour and eat individual fullstops + elif ( + tok.type == "NAME" + or tok.type == "AMPERSTAND" + or tok.type == "ASTERISK" + or tok.type == "CHAR_LITERAL" + ): if tok.value in ignoreSymbols: - debug_print("Ignore symbol %s"%tok.value) - elif (tok.value == 'class'): + debug_print("Ignore symbol %s" % tok.value) + elif tok.value == "class": self.nameStack.append(tok.value) elif tok.value in supportedAccessSpecifier: - if len(self.nameStack) and self.nameStack[0] in ("class", "struct", "union"): + if len(self.nameStack) and self.nameStack[0] in ( + "class", + "struct", + "union", + ): self.nameStack.append(tok.value) - elif self.braceDepth == len(self.nameSpaces) + 1 or self.braceDepth == (len(self.nameSpaces) + len(self.curClass.split("::"))): - self.curAccessSpecifier = tok.value; + elif self.braceDepth == len( + self.nameSpaces + ) + 1 or self.braceDepth == ( + len(self.nameSpaces) + len(self.curClass.split("::")) + ): + self.curAccessSpecifier = tok.value self.accessSpecifierScratch.append(tok.value) - debug_print("curAccessSpecifier updated to %s"%self.curAccessSpecifier) + debug_print( + "curAccessSpecifier updated to %s" + % self.curAccessSpecifier + ) self.stack = [] else: self.nameStack.append(tok.value) if self.anon_union_counter[0] == self.braceDepth: self.anon_union_counter = [-1, 0] - elif (tok.type == 'COLON'): - #Dont want colon to be first in stack + elif tok.type == "COLON": + # Dont want colon to be first in stack if len(self.nameStack) == 0: self.accessSpecifierScratch = [] continue - + # Handle situation where access specifiers can be multi words such as "public slots" jns = " ".join(self.accessSpecifierScratch + self.nameStack) if jns in supportedAccessSpecifier: - self.curAccessSpecifier = jns; - debug_print("curAccessSpecifier updated to %s"%self.curAccessSpecifier) + self.curAccessSpecifier = jns + debug_print( + "curAccessSpecifier updated to %s" % self.curAccessSpecifier + ) self.stack = [] self.nameStack = [] else: self.nameStack.append(tok.value) self.accessSpecifierScratch = [] - elif (tok.type == 'SEMI_COLON'): - if self.anon_union_counter[0] == self.braceDepth and self.anon_union_counter[1]: + elif tok.type == "SEMI_COLON": + if ( + self.anon_union_counter[0] == self.braceDepth + and self.anon_union_counter[1] + ): debug_print("Creating anonymous union") - #Force the processing of an anonymous union - saved_namestack = self.nameStack[:] + # Force the processing of an anonymous union + saved_namestack = self.nameStack[:] saved_stack = self.stack[:] self.nameStack = [""] self.stack = self.nameStack + [";"] self.nameStack = self.nameStack[0:1] debug_print("pre eval anon stack") - self.evaluate_stack( tok.type ) + self.evaluate_stack(tok.type) debug_print("post eval anon stack") self.nameStack = saved_namestack self.stack = saved_stack - self.anon_union_counter = [-1, 0]; - - - if (self.braceDepth < 10): self.evaluate_stack( tok.type ) + self.anon_union_counter = [-1, 0] + + if self.braceDepth < 10: + self.evaluate_stack(tok.type) self.stack = [] self.nameStack = [] except: - if (debug): raise - raise CppParseError("Not able to parse %s on line %d evaluating \"%s\"\nError around: %s" - % (self.headerFileName, tok.lineno, tok.value, " ".join(self.nameStack))) + if debug: + raise + raise CppParseError( + 'Not able to parse %s on line %d evaluating "%s"\nError around: %s' + % (self.headerFileName, tok.lineno, tok.value, " ".join(self.nameStack)) + ) self.finalize() global parseHistory parseHistory = [] # Delete some temporary variables - for key in ["_precomp_macro_buf", "nameStack", "nameSpaces", "curAccessSpecifier", "accessSpecifierStack", - "accessSpecifierScratch", "nameStackHistory", "anon_struct_counter", "anon_union_counter", - "_classes_brace_level", "_forward_decls", "stack", "mainClass", "curStruct", "_template_typenames", - "_method_body", "braceDepth", "_structs_brace_level", "typedefs_order", "curTemplate", "templateRegistry"]: + for key in [ + "_precomp_macro_buf", + "nameStack", + "nameSpaces", + "curAccessSpecifier", + "accessSpecifierStack", + "accessSpecifierScratch", + "nameStackHistory", + "anon_struct_counter", + "anon_union_counter", + "_classes_brace_level", + "_forward_decls", + "stack", + "mainClass", + "curStruct", + "_template_typenames", + "_method_body", + "braceDepth", + "_structs_brace_level", + "typedefs_order", + "curTemplate", + "templateRegistry", + ]: del self.__dict__[key] - def evaluate_stack(self, token=None): """Evaluates the current name stack""" global doxygenCommentCache - + self.nameStack = filter_out_attribute_keyword(self.nameStack) self.stack = filter_out_attribute_keyword(self.stack) nameStackCopy = self.nameStack[:] - - debug_print( "Evaluating stack %s\n BraceDepth: %s (called from %d)" %(self.nameStack,self.braceDepth, inspect.currentframe().f_back.f_lineno)) - - #Handle special case of overloading operator () + + debug_print( + "Evaluating stack %s\n BraceDepth: %s (called from %d)" + % (self.nameStack, self.braceDepth, inspect.currentframe().f_back.f_lineno) + ) + + # Handle special case of overloading operator () if "operator()(" in "".join(self.nameStack): operator_index = self.nameStack.index("operator") self.nameStack.pop(operator_index + 2) self.nameStack.pop(operator_index + 1) self.nameStack[operator_index] = "operator()" - - if (len(self.curClass)): - debug_print( "%s (%s) "%(self.curClass, self.curAccessSpecifier)) + + if len(self.curClass): + debug_print("%s (%s) " % (self.curClass, self.curAccessSpecifier)) else: - debug_print( " (%s) "%self.curAccessSpecifier) + debug_print(" (%s) " % self.curAccessSpecifier) - #Filter special case of array with casting in it + # Filter special case of array with casting in it try: bracePos = self.nameStack.index("[") parenPos = self.nameStack.index("(") if bracePos == parenPos - 1: endParen = self.nameStack.index(")") - self.nameStack = self.nameStack[:bracePos + 1] + self.nameStack[endParen + 1:] - debug_print("Filtered namestack to=%s"%self.nameStack) - except: pass - - #if 'typedef' in self.nameStack: self.evaluate_typedef() # allows nested typedefs, probably a bad idea - if (not self.curClass and 'typedef' in self.nameStack and - (('struct' not in self.nameStack and 'union' not in self.nameStack) or self.stack[-1] == ";") and - not is_enum_namestack(self.nameStack)): - trace_print('STACK', self.stack) + self.nameStack = ( + self.nameStack[: bracePos + 1] + self.nameStack[endParen + 1 :] + ) + debug_print("Filtered namestack to=%s" % self.nameStack) + except: + pass + + # if 'typedef' in self.nameStack: self.evaluate_typedef() # allows nested typedefs, probably a bad idea + if ( + not self.curClass + and "typedef" in self.nameStack + and ( + ("struct" not in self.nameStack and "union" not in self.nameStack) + or self.stack[-1] == ";" + ) + and not is_enum_namestack(self.nameStack) + ): + trace_print("STACK", self.stack) self.evaluate_typedef() return - - elif (len(self.nameStack) == 0): - debug_print( "trace" ) - debug_print( "(Empty Stack)" ) + + elif len(self.nameStack) == 0: + debug_print("trace") + debug_print("(Empty Stack)") return - elif (self.nameStack[0] == "namespace"): - #Taken care of outside of here + elif self.nameStack[0] == "namespace": + # Taken care of outside of here pass - elif len(self.nameStack) == 2 and self.nameStack[0] == "friend":#friend class declaration + elif ( + len(self.nameStack) == 2 and self.nameStack[0] == "friend" + ): # friend class declaration pass - elif len(self.nameStack) >= 2 and self.nameStack[0] == 'using' and self.nameStack[1] == 'namespace': pass # TODO + elif ( + len(self.nameStack) >= 2 + and self.nameStack[0] == "using" + and self.nameStack[1] == "namespace" + ): + pass # TODO elif is_enum_namestack(self.nameStack): - debug_print( "trace" ) + debug_print("trace") self.evaluate_enum_stack() - elif self._method_body and (self.braceDepth + 1) > self._method_body: trace_print( 'INSIDE METHOD DEF' ) - elif is_method_namestack(self.stack) and not self.curStruct and '(' in self.nameStack: - debug_print( "trace" ) + elif self._method_body and (self.braceDepth + 1) > self._method_body: + trace_print("INSIDE METHOD DEF") + elif ( + is_method_namestack(self.stack) + and not self.curStruct + and "(" in self.nameStack + ): + debug_print("trace") if self.braceDepth > 0: - if "{" in self.stack and self.stack[0] != '{' and self.stack[-1] == ';' and self.braceDepth == 1: - #Special case of a method defined outside a class that has a body + if ( + "{" in self.stack + and self.stack[0] != "{" + and self.stack[-1] == ";" + and self.braceDepth == 1 + ): + # Special case of a method defined outside a class that has a body pass - else: + else: self.evaluate_method_stack() else: - #Free function + # Free function self.evaluate_method_stack() - elif (len(self.nameStack) == 1 and len(self.nameStackHistory) > self.braceDepth - and (self.nameStackHistory[self.braceDepth][0][0:2] == ["typedef", "struct"] or - self.nameStackHistory[self.braceDepth][0][0:2] == ["typedef", "union"])): + elif ( + len(self.nameStack) == 1 + and len(self.nameStackHistory) > self.braceDepth + and ( + self.nameStackHistory[self.braceDepth][0][0:2] == ["typedef", "struct"] + or self.nameStackHistory[self.braceDepth][0][0:2] + == ["typedef", "union"] + ) + ): # Look for the name of a typedef struct: struct typedef {...] StructName; or unions to get renamed debug_print("found the naming of a union") type_name_to_rename = self.nameStackHistory[self.braceDepth][1] new_name = self.nameStack[0] type_to_rename = self.classes[type_name_to_rename] type_to_rename["name"] = self.nameStack[0] - #Now re install it in its new location + # Now re install it in its new location self.classes[new_name] = type_to_rename if new_name != type_name_to_rename: - del self.classes[type_name_to_rename] - elif is_property_namestack(self.nameStack) and self.stack[-1] == ';': - debug_print( "trace" ) - if self.nameStack[0] in ('class', 'struct') and len(self.stack) == 3: self.evalute_forward_decl() - elif len(self.nameStack) >= 2 and (self.nameStack[0]=='friend' and self.nameStack[1]=='class'): pass - else: self.evaluate_property_stack() # catches class props and structs in a namespace - - elif self.nameStack[0] in ("class", "struct", "union") or self.nameStack[0] == 'typedef' and self.nameStack[1] in ('struct', 'union'): - #Parsing a union can reuse much of the class parsing - debug_print( "trace" ) + del self.classes[type_name_to_rename] + elif is_property_namestack(self.nameStack) and self.stack[-1] == ";": + debug_print("trace") + if self.nameStack[0] in ("class", "struct") and len(self.stack) == 3: + self.evalute_forward_decl() + elif len(self.nameStack) >= 2 and ( + self.nameStack[0] == "friend" and self.nameStack[1] == "class" + ): + pass + else: + self.evaluate_property_stack() # catches class props and structs in a namespace + + elif ( + self.nameStack[0] in ("class", "struct", "union") + or self.nameStack[0] == "typedef" + and self.nameStack[1] in ("struct", "union") + ): + # Parsing a union can reuse much of the class parsing + debug_print("trace") self.evaluate_class_stack() elif not self.curClass: - debug_print( "trace" ) - if is_enum_namestack(self.nameStack): self.evaluate_enum_stack() - elif self.curStruct and self.stack[-1] == ';': self.evaluate_property_stack() # this catches fields of global structs + debug_print("trace") + if is_enum_namestack(self.nameStack): + self.evaluate_enum_stack() + elif self.curStruct and self.stack[-1] == ";": + self.evaluate_property_stack() # this catches fields of global structs self.nameStack = [] doxygenCommentCache = "" - elif (self.braceDepth < 1): - debug_print( "trace" ) - #Ignore global stuff for now - debug_print( "Global stuff: %s"%self.nameStack ) + elif self.braceDepth < 1: + debug_print("trace") + # Ignore global stuff for now + debug_print("Global stuff: %s" % self.nameStack) self.nameStack = [] doxygenCommentCache = "" - elif (self.braceDepth > len(self.nameSpaces) + 1): - debug_print( "trace" ) + elif self.braceDepth > len(self.nameSpaces) + 1: + debug_print("trace") self.nameStack = [] doxygenCommentCache = "" @@ -2553,33 +3112,36 @@ def evaluate_stack(self, token=None): self.nameStackHistory[self.braceDepth] = (nameStackCopy, self.curClass) except: self.nameStackHistory.append((nameStackCopy, self.curClass)) - self.nameStack = [] # its a little confusing to have some if/else above return and others not, and then clearning the nameStack down here + self.nameStack = ( + [] + ) # its a little confusing to have some if/else above return and others not, and then clearning the nameStack down here doxygenCommentCache = "" self.curTemplate = None - def evaluate_enum_stack(self): """Create an Enum out of the name stack""" - debug_print( "evaluating enum" ) + debug_print("evaluating enum") newEnum = CppEnum(self.nameStack) if len(list(newEnum.keys())): if len(self.curClass): newEnum["namespace"] = self.cur_namespace(False) klass = self.classes[self.curClass] klass["enums"][self.curAccessSpecifier].append(newEnum) - if self.curAccessSpecifier == 'public' and 'name' in newEnum: klass._public_enums[ newEnum['name'] ] = newEnum + if self.curAccessSpecifier == "public" and "name" in newEnum: + klass._public_enums[newEnum["name"]] = newEnum else: newEnum["namespace"] = self.cur_namespace(True) self.enums.append(newEnum) - if 'name' in newEnum and newEnum['name']: self.global_enums[ newEnum['name'] ] = newEnum + if "name" in newEnum and newEnum["name"]: + self.global_enums[newEnum["name"]] = newEnum - #This enum has instances, turn them into properties + # This enum has instances, turn them into properties if "instances" in newEnum: instanceType = "enum" if "name" in newEnum: instanceType = newEnum["name"] for instance in newEnum["instances"]: - self.nameStack = [instanceType, instance] + self.nameStack = [instanceType, instance] self.evaluate_property_stack() del newEnum["instances"] @@ -2588,32 +3150,35 @@ def strip_parent_keys(self): obj_queue = [self] while len(obj_queue): obj = obj_queue.pop() - trace_print("pop %s type %s"%(obj, type(obj))) + trace_print("pop %s type %s" % (obj, type(obj))) try: if "parent" in obj.keys(): del obj["parent"] - trace_print("Stripped parent from %s"%obj.keys()) - except: pass + trace_print("Stripped parent from %s" % obj.keys()) + except: + pass try: if "method" in obj.keys(): del obj["method"] - trace_print("Stripped method from %s"%obj.keys()) - except: pass + trace_print("Stripped method from %s" % obj.keys()) + except: + pass # Figure out what sub types are one of ours try: - if not hasattr(obj, 'keys'): + if not hasattr(obj, "keys"): obj = obj.__dict__ for k in obj.keys(): - trace_print("-Try key %s"%(k)) - trace_print("-type %s"%(type(obj[k]))) - if k in ["nameStackHistory", "parent", "_public_typedefs"]: continue + trace_print("-Try key %s" % (k)) + trace_print("-type %s" % (type(obj[k]))) + if k in ["nameStackHistory", "parent", "_public_typedefs"]: + continue if type(obj[k]) == list: for i in obj[k]: - trace_print("push l %s"%i) + trace_print("push l %s" % i) obj_queue.append(i) - elif type(obj[k]) == dict: + elif type(obj[k]) == dict: if len(obj): - trace_print("push d %s"%obj[k]) + trace_print("push d %s" % obj[k]) obj_queue.append(obj[k]) elif type(obj[k]) == type(type(0)): if type(obj[k]) == int: @@ -2629,36 +3194,37 @@ def strip_parent_keys(self): def toJSON(self, indent=4): """Converts a parsed structure to JSON""" import json + self.strip_parent_keys() try: del self.__dict__["classes_order"] - except: pass + except: + pass return json.dumps(self.__dict__, indent=indent) - def __repr__(self): rtn = { - "classes": self.classes, - "functions": self.functions, - "enums": self.enums, - "variables": self.variables, + "classes": self.classes, + "functions": self.functions, + "enums": self.enums, + "variables": self.variables, } return repr(rtn) def __str__(self): rtn = "" for className in list(self.classes.keys()): - rtn += "%s\n"%self.classes[className] + rtn += "%s\n" % self.classes[className] if self.functions: rtn += "// functions\n" for f in self.functions: - rtn += "%s\n"%f + rtn += "%s\n" % f if self.variables: rtn += "// variables\n" for f in self.variables: - rtn += "%s\n"%f + rtn += "%s\n" % f if self.enums: rtn += "// enums\n" for f in self.enums: - rtn += "%s\n"%f + rtn += "%s\n" % f return rtn diff --git a/CppHeaderParser/__init__.py b/CppHeaderParser/__init__.py index 024da04..dd2fb30 100644 --- a/CppHeaderParser/__init__.py +++ b/CppHeaderParser/__init__.py @@ -3,4 +3,4 @@ from .CppHeaderParser import * -#__all__ = ['CppHeaderParser'] +# __all__ = ['CppHeaderParser'] diff --git a/CppHeaderParser/examples/readSampleClass.py b/CppHeaderParser/examples/readSampleClass.py index 0bab4f7..35d4ac4 100755 --- a/CppHeaderParser/examples/readSampleClass.py +++ b/CppHeaderParser/examples/readSampleClass.py @@ -1,21 +1,25 @@ #!/usr/bin/python import sys + sys.path = ["../"] + sys.path import CppHeaderParser + try: cppHeader = CppHeaderParser.CppHeader("SampleClass.h") except CppHeaderParser.CppParseError as e: print(e) sys.exit(1) -print("CppHeaderParser view of %s"%cppHeader) +print("CppHeaderParser view of %s" % cppHeader) sampleClass = cppHeader.classes["SampleClass"] -print("Number of public methods %d"%(len(sampleClass["methods"]["public"]))) -print("Number of private properties %d"%(len(sampleClass["properties"]["private"]))) -meth3 = [m for m in sampleClass["methods"]["public"] if m["name"] == "meth3"][0] #get meth3 -meth3ParamTypes = [t["type"] for t in meth3["parameters"]] #get meth3s parameters -print("Parameter Types for public method meth3 %s"%(meth3ParamTypes)) +print("Number of public methods %d" % (len(sampleClass["methods"]["public"]))) +print("Number of private properties %d" % (len(sampleClass["properties"]["private"]))) +meth3 = [m for m in sampleClass["methods"]["public"] if m["name"] == "meth3"][ + 0 +] # get meth3 +meth3ParamTypes = [t["type"] for t in meth3["parameters"]] # get meth3s parameters +print("Parameter Types for public method meth3 %s" % (meth3ParamTypes)) print("\nReturn type for meth1:") print(cppHeader.classes["SampleClass"]["methods"]["public"][1]["rtnType"]) @@ -52,12 +56,12 @@ print("\nFree functions are:") for func in cppHeader.functions: - print(" %s"%func["name"]) + print(" %s" % func["name"]) print("\n#includes are:") for incl in cppHeader.includes: - print(" %s"%incl) + print(" %s" % incl) print("\n#defines are:") for define in cppHeader.defines: - print(" %s"%define) + print(" %s" % define) diff --git a/CppHeaderParser/test/gen_test.py b/CppHeaderParser/test/gen_test.py deleted file mode 100644 index 4ba96a9..0000000 --- a/CppHeaderParser/test/gen_test.py +++ /dev/null @@ -1,153 +0,0 @@ -import sys -sys.path = [".."] + sys.path -import CppHeaderParser - -testScript = "" -testCaseClasses = [] - -def main(): - #init testScript with boiler plate code - global testScript - global testCaseClasses - testScript = """\ -import unittest -from test import test_support -import sys -sys.path = [".."] + sys.path -import CppHeaderParser - -""" - cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - for className, classInstance in cppHeader.classes.items(): - gen_test_cases_for_class(className, classInstance) - - testScript += """\ - - -def test_main(): - test_support.run_unittest( - %s) - -if __name__ == '__main__': - test_main() - -"""%",\n ".join(testCaseClasses) - - print testScript - -def gen_test_cases_for_class(className, classInstance): - for methAccessor in classInstance["methods"].keys(): - idx = 0 - for method in classInstance["methods"][methAccessor]: - gen_test_case_for_method(className, classInstance, methAccessor, idx, method); - idx += 1 - - for propAccessor in classInstance["properties"].keys(): - idx = 0 - for property in classInstance["properties"][propAccessor]: - gen_test_case_for_property(className, classInstance, propAccessor, idx, property); - idx += 1 - - for enumAccessor in classInstance["enums"].keys(): - idx = 0 - for enum in classInstance["enums"][enumAccessor]: - gen_test_case_for_enum(className, classInstance, enumAccessor, idx, enum); - idx += 1 - -def gen_test_case_for_method(className, classInstance, methAccessor, methIndex, method): - global testScript - global testCaseClasses - testCaseClassName = "%s_%s_TestCase"%(className, method["name"]) - testCaseClasses.append(testCaseClassName) - testScript += """\ - - -class %s(unittest.TestCase): - - def setUp(self): - self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - -"""%testCaseClassName - methString = """self.cppHeader.classes["%s"]["methods"]["%s"][%d]"""%( - className, methAccessor, methIndex) - for key in ["name", "rtnType", "parameters", "doxygen"]: - if key in method.keys(): - gen_test_equals(key, methString + '["%s"]'%key, method[key]) - else: - gen_test_key_not_exist(key, methString) - - - -def gen_test_case_for_property(className, classInstance, propAccessor, propIndex, property): - global testScript - global testCaseClasses - testCaseClassName = "%s_%s_TestCase"%(className, property["name"]) - testCaseClasses.append(testCaseClassName) - testScript += """\ - - -class %s(unittest.TestCase): - - def setUp(self): - self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - -"""%testCaseClassName - propString = """self.cppHeader.classes["%s"]["properties"]["%s"][%d]"""%( - className, propAccessor, propIndex) - for key in ["name", "type", "doxygen"]: - if key in property.keys(): - gen_test_equals(key, propString + '["%s"]'%key, property[key]) - else: - gen_test_key_not_exist(key, propString) - - - - -def gen_test_case_for_enum(className, classInstance, enumAccessor, enumIndex, enum): - global testScript - global testCaseClasses - testCaseClassName = "%s_%s_TestCase"%(className, enum["name"]) - testCaseClasses.append(testCaseClassName) - testScript += """\ - - -class %s(unittest.TestCase): - - def setUp(self): - self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - -"""%testCaseClassName - enumString = """self.cppHeader.classes["%s"]["enums"]["%s"][%d]"""%( - className, enumAccessor, enumIndex) - for key in ["name", "namespace", "doxygen", "values"]: - if key in enum.keys(): - gen_test_equals(key, enumString + '["%s"]'%key, enum[key]) - else: - gen_test_key_not_exist(key, enumString) - - - -def gen_test_equals(name, v1, v2): - global testScript - testScript += """\ - def test_%s(self): - self.assertEqual( - %s, - %s) - -"""%(name.lower(), v1, repr(v2)) - -def gen_test_key_not_exist(key, testObj): - global testScript - testScript += """\ - def test_%s(self): - self.assertTrue( - "%s" - not in %s.keys()) - -"""%(key.lower(), key, testObj) - -if __name__ == "__main__": - main() - - diff --git a/CppHeaderParser/test/test_CppHeaderParser.py b/CppHeaderParser/test/test_CppHeaderParser.py index dc55327..c877c85 100644 --- a/CppHeaderParser/test/test_CppHeaderParser.py +++ b/CppHeaderParser/test/test_CppHeaderParser.py @@ -9,9 +9,10 @@ def filter_pameters(p): "Reduce a list of dictionaries to the desired keys for function parameter testing" rtn = [] for d in p: - rtn.append({'name': d['name'], 'desc': d['desc'], 'type': d['type']}) + rtn.append({"name": d["name"], "desc": d["desc"], "type": d["type"]}) return rtn + def filter_dict_keys(d, keys): "Filter a dictonary to a specified set of keys" rtn = {} @@ -19,1161 +20,1747 @@ def filter_dict_keys(d, keys): rtn[k] = d.get(k, None) return rtn -class SampleClass_SampleClass_TestCase(unittest.TestCase): +class SampleClass_SampleClass_TestCase(unittest.TestCase): def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_name(self): self.assertEqual( self.cppHeader.classes["SampleClass"]["methods"]["public"][0]["name"], - 'SampleClass') + "SampleClass", + ) def test_rtntype(self): self.assertEqual( self.cppHeader.classes["SampleClass"]["methods"]["public"][0]["rtnType"], - 'void') + "void", + ) def test_parameters(self): self.assertEqual( - filter_pameters(self.cppHeader.classes["SampleClass"]["methods"]["public"][0]["parameters"]), - []) + filter_pameters( + self.cppHeader.classes["SampleClass"]["methods"]["public"][0][ + "parameters" + ] + ), + [], + ) def test_doxygen(self): self.assertTrue( - "doxygen" - not in self.cppHeader.classes["SampleClass"]["methods"]["public"][0].keys()) - + "doxygen" + not in self.cppHeader.classes["SampleClass"]["methods"]["public"][0].keys() + ) class SampleClass_meth1_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_name(self): self.assertEqual( self.cppHeader.classes["SampleClass"]["methods"]["public"][1]["name"], - 'meth1') + "meth1", + ) def test_rtntype(self): self.assertEqual( self.cppHeader.classes["SampleClass"]["methods"]["public"][1]["rtnType"], - 'string') + "string", + ) def test_parameters(self): self.assertEqual( - filter_pameters(self.cppHeader.classes["SampleClass"]["methods"]["public"][1]["parameters"]), - []) + filter_pameters( + self.cppHeader.classes["SampleClass"]["methods"]["public"][1][ + "parameters" + ] + ), + [], + ) def test_doxygen(self): self.assertEqual( self.cppHeader.classes["SampleClass"]["methods"]["public"][1]["doxygen"], - '/*!\n* Method 1\n*/') - + "/*!\n* Method 1\n*/", + ) class SampleClass_meth2_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_name(self): self.assertEqual( self.cppHeader.classes["SampleClass"]["methods"]["public"][2]["name"], - 'meth2') + "meth2", + ) def test_rtntype(self): self.assertEqual( self.cppHeader.classes["SampleClass"]["methods"]["public"][2]["rtnType"], - 'int') + "int", + ) def test_parameters(self): self.assertEqual( - filter_pameters(self.cppHeader.classes["SampleClass"]["methods"]["public"][2]["parameters"]), - [{'type': 'int', 'name': 'v1', 'desc': 'Variable 1'}]) + filter_pameters( + self.cppHeader.classes["SampleClass"]["methods"]["public"][2][ + "parameters" + ] + ), + [{"type": "int", "name": "v1", "desc": "Variable 1"}], + ) def test_doxygen(self): self.assertEqual( self.cppHeader.classes["SampleClass"]["methods"]["public"][2]["doxygen"], - '///\n/// Method 2 description\n///\n/// @param v1 Variable 1\n///') - + "///\n/// Method 2 description\n///\n/// @param v1 Variable 1\n///", + ) class SampleClass_meth3_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_name(self): self.assertEqual( self.cppHeader.classes["SampleClass"]["methods"]["public"][3]["name"], - 'meth3') + "meth3", + ) def test_rtntype(self): self.assertEqual( self.cppHeader.classes["SampleClass"]["methods"]["public"][3]["rtnType"], - 'void') + "void", + ) def test_parameters(self): self.assertEqual( - filter_pameters(self.cppHeader.classes["SampleClass"]["methods"]["public"][3]["parameters"]), - [{'type': 'const string &', 'name': 'v1', 'desc': 'Variable 1 with a really long wrapping description'}, {'type': 'vector &', 'name': 'v2', 'desc': 'Variable 2'}]) + filter_pameters( + self.cppHeader.classes["SampleClass"]["methods"]["public"][3][ + "parameters" + ] + ), + [ + { + "type": "const string &", + "name": "v1", + "desc": "Variable 1 with a really long wrapping description", + }, + {"type": "vector &", "name": "v2", "desc": "Variable 2"}, + ], + ) def test_doxygen(self): self.assertEqual( self.cppHeader.classes["SampleClass"]["methods"]["public"][3]["doxygen"], - '/**\n* Method 3 description\n*\n* \\param v1 Variable 1 with a really long\n* wrapping description\n* \\param v2 Variable 2\n*/') - + "/**\n* Method 3 description\n*\n* \\param v1 Variable 1 with a really long\n* wrapping description\n* \\param v2 Variable 2\n*/", + ) class SampleClass_meth4_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_name(self): self.assertEqual( self.cppHeader.classes["SampleClass"]["methods"]["public"][4]["name"], - 'meth4') + "meth4", + ) def test_rtntype(self): self.assertEqual( self.cppHeader.classes["SampleClass"]["methods"]["public"][4]["rtnType"], - 'unsigned int') + "unsigned int", + ) def test_parameters(self): self.assertEqual( - filter_pameters(self.cppHeader.classes["SampleClass"]["methods"]["public"][4]["parameters"]), - []) + filter_pameters( + self.cppHeader.classes["SampleClass"]["methods"]["public"][4][ + "parameters" + ] + ), + [], + ) def test_doxygen(self): self.assertEqual( self.cppHeader.classes["SampleClass"]["methods"]["public"][4]["doxygen"], - '/**********************************\n* Method 4 description\n*\n* @return Return value\n*********************************/') - + "/**********************************\n* Method 4 description\n*\n* @return Return value\n*********************************/", + ) class SampleClass_meth5_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_name(self): self.assertEqual( self.cppHeader.classes["SampleClass"]["methods"]["private"][0]["name"], - 'meth5') + "meth5", + ) def test_rtntype(self): self.assertEqual( self.cppHeader.classes["SampleClass"]["methods"]["private"][0]["rtnType"], - 'void *') + "void *", + ) def test_parameters(self): self.assertEqual( - filter_pameters(self.cppHeader.classes["SampleClass"]["methods"]["private"][0]["parameters"]), - []) + filter_pameters( + self.cppHeader.classes["SampleClass"]["methods"]["private"][0][ + "parameters" + ] + ), + [], + ) def test_doxygen(self): self.assertTrue( - "doxygen" - not in self.cppHeader.classes["SampleClass"]["methods"]["private"][0].keys()) - + "doxygen" + not in self.cppHeader.classes["SampleClass"]["methods"]["private"][0].keys() + ) class SampleClass_prop1_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_name(self): self.assertEqual( self.cppHeader.classes["SampleClass"]["properties"]["private"][0]["name"], - 'prop1') + "prop1", + ) def test_type(self): self.assertEqual( self.cppHeader.classes["SampleClass"]["properties"]["private"][0]["type"], - 'string') + "string", + ) def test_doxygen(self): self.assertEqual( - self.cppHeader.classes["SampleClass"]["properties"]["private"][0]["doxygen"], - '/// prop1 description') - + self.cppHeader.classes["SampleClass"]["properties"]["private"][0][ + "doxygen" + ], + "/// prop1 description", + ) class SampleClass_prop5_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_name(self): self.assertEqual( self.cppHeader.classes["SampleClass"]["properties"]["private"][1]["name"], - 'prop5') + "prop5", + ) def test_type(self): self.assertEqual( self.cppHeader.classes["SampleClass"]["properties"]["private"][1]["type"], - 'int') + "int", + ) def test_doxygen(self): self.assertEqual( - self.cppHeader.classes["SampleClass"]["properties"]["private"][1]["doxygen"], - '//! prop5 description') - + self.cppHeader.classes["SampleClass"]["properties"]["private"][1][ + "doxygen" + ], + "//! prop5 description", + ) class SampleClass_Elephant_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_name(self): self.assertEqual( self.cppHeader.classes["SampleClass"]["enums"]["public"][0]["name"], - 'Elephant') + "Elephant", + ) def test_namespace(self): self.assertEqual( - self.cppHeader.classes["SampleClass"]["enums"]["public"][0]["namespace"], - '') + self.cppHeader.classes["SampleClass"]["enums"]["public"][0]["namespace"], "" + ) def test_doxygen(self): self.assertTrue( - "doxygen" - not in self.cppHeader.classes["SampleClass"]["enums"]["public"][0].keys()) + "doxygen" + not in self.cppHeader.classes["SampleClass"]["enums"]["public"][0].keys() + ) def test_values(self): self.assertEqual( self.cppHeader.classes["SampleClass"]["enums"]["public"][0]["values"], - [{'name': 'EL_ONE', 'value': 1}, {'name': 'EL_TWO', 'value': 2}, {'name': 'EL_NINE', 'value': 9}, {'name': 'EL_TEN', 'value': 10}]) - + [ + {"name": "EL_ONE", "value": 1}, + {"name": "EL_TWO", "value": 2}, + {"name": "EL_NINE", "value": 9}, + {"name": "EL_TEN", "value": 10}, + ], + ) class AlphaClass_AlphaClass_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_name(self): self.assertEqual( self.cppHeader.classes["AlphaClass"]["methods"]["public"][0]["name"], - 'AlphaClass') + "AlphaClass", + ) def test_rtntype(self): self.assertEqual( self.cppHeader.classes["AlphaClass"]["methods"]["public"][0]["rtnType"], - 'void') + "void", + ) def test_parameters(self): self.assertEqual( - filter_pameters(self.cppHeader.classes["AlphaClass"]["methods"]["public"][0]["parameters"]), - []) + filter_pameters( + self.cppHeader.classes["AlphaClass"]["methods"]["public"][0][ + "parameters" + ] + ), + [], + ) def test_doxygen(self): self.assertTrue( - "doxygen" - not in self.cppHeader.classes["AlphaClass"]["methods"]["public"][0].keys()) - + "doxygen" + not in self.cppHeader.classes["AlphaClass"]["methods"]["public"][0].keys() + ) class AlphaClass_alphaMethod_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_name(self): self.assertEqual( self.cppHeader.classes["AlphaClass"]["methods"]["public"][1]["name"], - 'alphaMethod') + "alphaMethod", + ) def test_rtntype(self): self.assertEqual( self.cppHeader.classes["AlphaClass"]["methods"]["public"][1]["rtnType"], - 'void') + "void", + ) def test_parameters(self): self.assertEqual( - filter_pameters(self.cppHeader.classes["AlphaClass"]["methods"]["public"][1]["parameters"]), - []) + filter_pameters( + self.cppHeader.classes["AlphaClass"]["methods"]["public"][1][ + "parameters" + ] + ), + [], + ) def test_doxygen(self): self.assertTrue( - "doxygen" - not in self.cppHeader.classes["AlphaClass"]["methods"]["public"][1].keys()) - + "doxygen" + not in self.cppHeader.classes["AlphaClass"]["methods"]["public"][1].keys() + ) class AlphaClass_alphaString_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_name(self): self.assertEqual( self.cppHeader.classes["AlphaClass"]["properties"]["public"][0]["name"], - 'alphaString') + "alphaString", + ) def test_type(self): self.assertEqual( self.cppHeader.classes["AlphaClass"]["properties"]["public"][0]["type"], - 'string') + "string", + ) def test_doxygen(self): self.assertTrue( - "doxygen" - not in self.cppHeader.classes["AlphaClass"]["properties"]["public"][0].keys()) - + "doxygen" + not in self.cppHeader.classes["AlphaClass"]["properties"]["public"][ + 0 + ].keys() + ) class AlphaClass_Zebra_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_name(self): self.assertEqual( self.cppHeader.classes["AlphaClass"]["enums"]["protected"][0]["name"], - 'Zebra') + "Zebra", + ) def test_namespace(self): self.assertEqual( self.cppHeader.classes["AlphaClass"]["enums"]["protected"][0]["namespace"], - 'Alpha') + "Alpha", + ) def test_doxygen(self): self.assertTrue( - "doxygen" - not in self.cppHeader.classes["AlphaClass"]["enums"]["protected"][0].keys()) + "doxygen" + not in self.cppHeader.classes["AlphaClass"]["enums"]["protected"][0].keys() + ) def test_values(self): self.assertEqual( self.cppHeader.classes["AlphaClass"]["enums"]["protected"][0]["values"], - [{'name': 'Z_A', 'value': 0}, - {'name': 'Z_B', 'raw_value': '0x2B', 'value': 43}, - {'name': 'Z_C', 'raw_value': 'j', 'value': 106}, - {'name': 'Z_D', 'value': 107}]) - + [ + {"name": "Z_A", "value": 0}, + {"name": "Z_B", "raw_value": "0x2B", "value": 43}, + {"name": "Z_C", "raw_value": "j", "value": 106}, + {"name": "Z_D", "value": 107}, + ], + ) class OmegaClass_OmegaClass_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_name(self): self.assertEqual( self.cppHeader.classes["OmegaClass"]["methods"]["public"][0]["name"], - 'OmegaClass') + "OmegaClass", + ) def test_rtntype(self): self.assertEqual( self.cppHeader.classes["OmegaClass"]["methods"]["public"][0]["rtnType"], - 'void') + "void", + ) def test_parameters(self): self.assertEqual( - filter_pameters(self.cppHeader.classes["OmegaClass"]["methods"]["public"][0]["parameters"]), - []) + filter_pameters( + self.cppHeader.classes["OmegaClass"]["methods"]["public"][0][ + "parameters" + ] + ), + [], + ) def test_doxygen(self): self.assertTrue( - "doxygen" - not in self.cppHeader.classes["OmegaClass"]["methods"]["public"][0].keys()) - + "doxygen" + not in self.cppHeader.classes["OmegaClass"]["methods"]["public"][0].keys() + ) class OmegaClass_omegaString_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_name(self): self.assertEqual( self.cppHeader.classes["OmegaClass"]["properties"]["public"][0]["name"], - 'omegaString') + "omegaString", + ) def test_type(self): self.assertEqual( self.cppHeader.classes["OmegaClass"]["properties"]["public"][0]["type"], - 'string') + "string", + ) def test_doxygen(self): self.assertTrue( - "doxygen" - not in self.cppHeader.classes["OmegaClass"]["properties"]["public"][0].keys()) - + "doxygen" + not in self.cppHeader.classes["OmegaClass"]["properties"]["public"][ + 0 + ].keys() + ) class OmegaClass_Rino_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_name(self): self.assertEqual( self.cppHeader.classes["OmegaClass"]["enums"]["protected"][0]["name"], - 'Rino') + "Rino", + ) def test_namespace(self): self.assertEqual( self.cppHeader.classes["OmegaClass"]["enums"]["protected"][0]["namespace"], - 'Alpha::Omega') + "Alpha::Omega", + ) def test_doxygen(self): self.assertEqual( self.cppHeader.classes["OmegaClass"]["enums"]["protected"][0]["doxygen"], - '///\n/// @brief Rino Numbers, not that that means anything\n///') + "///\n/// @brief Rino Numbers, not that that means anything\n///", + ) def test_values(self): self.assertEqual( self.cppHeader.classes["OmegaClass"]["enums"]["protected"][0]["values"], - [{'name': 'RI_ZERO', 'value': 0}, {'name': 'RI_ONE', 'value': 1}, {'name': 'RI_TWO', 'value': 2}]) + [ + {"name": "RI_ZERO", "value": 0}, + {"name": "RI_ONE", "value": 1}, + {"name": "RI_TWO", "value": 2}, + ], + ) class Bug3488053_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - + def test_public(self): - self.assertEqual(len(self.cppHeader.classes["Bug_3488053::Bug_3488053_Nested"]["properties"]["public"]), 1) - + self.assertEqual( + len( + self.cppHeader.classes["Bug_3488053::Bug_3488053_Nested"]["properties"][ + "public" + ] + ), + 1, + ) + def test_private(self): - self.assertEqual(len(self.cppHeader.classes["Bug_3488053::Bug_3488053_Nested"]["properties"]["private"]), 0) - + self.assertEqual( + len( + self.cppHeader.classes["Bug_3488053::Bug_3488053_Nested"]["properties"][ + "private" + ] + ), + 0, + ) + def test_protected(self): - self.assertEqual(len(self.cppHeader.classes["Bug_3488053::Bug_3488053_Nested"]["properties"]["protected"]), 0) - + self.assertEqual( + len( + self.cppHeader.classes["Bug_3488053::Bug_3488053_Nested"]["properties"][ + "protected" + ] + ), + 0, + ) + class Bug3488360_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - + def test_BloodOrange_inherits(self): self.assertEqual(self.cppHeader.classes["BloodOrange"]["inherits"], []) - + def test_Bananna_inherits(self): - self.assertEqual(self.cppHeader.classes["Bananna"]["inherits"], [{'access': 'public', 'class': 'Citrus::BloodOrange', 'virtual': False}]) - + self.assertEqual( + self.cppHeader.classes["Bananna"]["inherits"], + [{"access": "public", "class": "Citrus::BloodOrange", "virtual": False}], + ) + def test_ExcellentCake_inherits(self): - self.assertEqual(self.cppHeader.classes["ExcellentCake"]["inherits"], - [{'access': 'private', 'class': 'Citrus::BloodOrange', 'virtual': False}, - {'access': 'private', 'class': 'Convoluted::Nested::Mixin', 'virtual': False}]) - + self.assertEqual( + self.cppHeader.classes["ExcellentCake"]["inherits"], + [ + {"access": "private", "class": "Citrus::BloodOrange", "virtual": False}, + { + "access": "private", + "class": "Convoluted::Nested::Mixin", + "virtual": False, + }, + ], + ) + + class Bug3487551_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - + def test_method_rtn_type(self): - self.assertEqual(self.cppHeader.classes["Bug_3487551"]["methods"]["public"][0]["rtnType"], "int") - + self.assertEqual( + self.cppHeader.classes["Bug_3487551"]["methods"]["public"][0]["rtnType"], + "int", + ) -class SampleStruct_meth_TestCase(unittest.TestCase): +class SampleStruct_meth_TestCase(unittest.TestCase): def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_name(self): self.assertEqual( self.cppHeader.classes["SampleStruct"]["methods"]["public"][0]["name"], - 'meth') + "meth", + ) def test_rtntype(self): self.assertEqual( self.cppHeader.classes["SampleStruct"]["methods"]["public"][0]["rtnType"], - 'unsigned int') + "unsigned int", + ) def test_parameters(self): self.assertEqual( - self.cppHeader.classes["SampleStruct"]["methods"]["public"][0]["parameters"], - []) + self.cppHeader.classes["SampleStruct"]["methods"]["public"][0][ + "parameters" + ], + [], + ) def test_doxygen(self): self.assertTrue( - "doxygen" - not in self.cppHeader.classes["SampleStruct"]["methods"]["public"][0].keys()) - + "doxygen" + not in self.cppHeader.classes["SampleStruct"]["methods"]["public"][0].keys() + ) class SampleStruct_prop_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_name(self): self.assertEqual( self.cppHeader.classes["SampleStruct"]["properties"]["private"][0]["name"], - 'prop') + "prop", + ) def test_type(self): self.assertEqual( self.cppHeader.classes["SampleStruct"]["properties"]["private"][0]["type"], - 'int') + "int", + ) def test_doxygen(self): self.assertTrue( - "doxygen" - not in self.cppHeader.classes["SampleStruct"]["properties"]["private"][0].keys()) + "doxygen" + not in self.cppHeader.classes["SampleStruct"]["properties"]["private"][ + 0 + ].keys() + ) class Bird_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_items_array(self): - self.assertEqual(self.cppHeader.classes["Bird"]["properties"]["private"][0]["array"], 1) - + self.assertEqual( + self.cppHeader.classes["Bird"]["properties"]["private"][0]["array"], 1 + ) + def test_otherItems_array(self): - self.assertEqual(self.cppHeader.classes["Bird"]["properties"]["private"][1]["array"], 1) - + self.assertEqual( + self.cppHeader.classes["Bird"]["properties"]["private"][1]["array"], 1 + ) + def test_oneItem_array(self): - self.assertEqual(self.cppHeader.classes["Bird"]["properties"]["private"][2]["array"], 0) - + self.assertEqual( + self.cppHeader.classes["Bird"]["properties"]["private"][2]["array"], 0 + ) + def test_items_array_size(self): - self.assertEqual(self.cppHeader.classes["Bird"]["properties"]["private"][0]["array_size"], "MAX_ITEM") - + self.assertEqual( + self.cppHeader.classes["Bird"]["properties"]["private"][0]["array_size"], + "MAX_ITEM", + ) + def test_otherItems_array_size(self): - self.assertEqual(self.cppHeader.classes["Bird"]["properties"]["private"][1]["array_size"], "7") + self.assertEqual( + self.cppHeader.classes["Bird"]["properties"]["private"][1]["array_size"], + "7", + ) class Monkey_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_num_public_methods(self): self.assertEqual(len(self.cppHeader.classes["Monkey"]["methods"]["public"]), 0) - + def test_num_private_methods(self): self.assertEqual(len(self.cppHeader.classes["Monkey"]["methods"]["private"]), 1) - - def test_num_protected_methods(self): - self.assertEqual(len(self.cppHeader.classes["Monkey"]["methods"]["protected"]), 0) + def test_num_protected_methods(self): + self.assertEqual( + len(self.cppHeader.classes["Monkey"]["methods"]["protected"]), 0 + ) class Chicken_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_num_public_methods(self): self.assertEqual(len(self.cppHeader.classes["Chicken"]["methods"]["public"]), 0) - + def test_num_private_methods(self): - self.assertEqual(len(self.cppHeader.classes["Chicken"]["methods"]["private"]), 1) - + self.assertEqual( + len(self.cppHeader.classes["Chicken"]["methods"]["private"]), 1 + ) + def test_num_protected_methods(self): - self.assertEqual(len(self.cppHeader.classes["Chicken"]["methods"]["protected"]), 0) - - def test_template(self): - self.assertEqual(self.cppHeader.classes["Chicken"]["methods"]["private"][0]['template'], "template ") + self.assertEqual( + len(self.cppHeader.classes["Chicken"]["methods"]["protected"]), 0 + ) + def test_template(self): + self.assertEqual( + self.cppHeader.classes["Chicken"]["methods"]["private"][0]["template"], + "template ", + ) class Lizzard_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - + def test_normal_constructor(self): - cmp_values = {'inline': False, 'name': 'Lizzard', 'parameters': [], 'friend': False, - 'explicit': False, 'constructor': True, 'namespace': '', 'destructor': False, - 'pure_virtual': False, 'returns': '', 'static': False, 'virtual': False, - 'template': False, 'rtnType': 'void', 'extern': False, 'path': 'Lizzard', - 'returns_pointer': 0, 'class': None} - self.assertEqual(filter_dict_keys(self.cppHeader.classes["Lizzard"]["methods"]["private"][0], cmp_values.keys()), - cmp_values) - - def test_explicit_constructor(self): - cmp_values = {'inline': False, 'name': 'Lizzard', 'friend': False, - 'explicit': True, 'constructor': True, 'namespace': '', 'destructor': False, - 'pure_virtual': False, 'returns': '', 'static': False, 'virtual': False, - 'template': False, 'rtnType': 'void', 'extern': False, 'path': 'Lizzard', - 'returns_pointer': 0, 'class': None} - self.assertEqual(filter_dict_keys(self.cppHeader.classes["Lizzard"]["methods"]["private"][1], cmp_values.keys()), - cmp_values) + cmp_values = { + "inline": False, + "name": "Lizzard", + "parameters": [], + "friend": False, + "explicit": False, + "constructor": True, + "namespace": "", + "destructor": False, + "pure_virtual": False, + "returns": "", + "static": False, + "virtual": False, + "template": False, + "rtnType": "void", + "extern": False, + "path": "Lizzard", + "returns_pointer": 0, + "class": None, + } + self.assertEqual( + filter_dict_keys( + self.cppHeader.classes["Lizzard"]["methods"]["private"][0], + cmp_values.keys(), + ), + cmp_values, + ) + def test_explicit_constructor(self): + cmp_values = { + "inline": False, + "name": "Lizzard", + "friend": False, + "explicit": True, + "constructor": True, + "namespace": "", + "destructor": False, + "pure_virtual": False, + "returns": "", + "static": False, + "virtual": False, + "template": False, + "rtnType": "void", + "extern": False, + "path": "Lizzard", + "returns_pointer": 0, + "class": None, + } + self.assertEqual( + filter_dict_keys( + self.cppHeader.classes["Lizzard"]["methods"]["private"][1], + cmp_values.keys(), + ), + cmp_values, + ) class Owl_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_num_public_methods(self): self.assertEqual(len(self.cppHeader.classes["Owl"]["methods"]["public"]), 0) - + def test_num_private_methods(self): self.assertEqual(len(self.cppHeader.classes["Owl"]["methods"]["private"]), 1) - + def test_num_protected_methods(self): self.assertEqual(len(self.cppHeader.classes["Owl"]["methods"]["protected"]), 0) class Grape_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_num_public_properties(self): - self.assertEqual(len(self.cppHeader.classes["GrapeClass"]["properties"]["public"]), 0) - + self.assertEqual( + len(self.cppHeader.classes["GrapeClass"]["properties"]["public"]), 0 + ) + def test_num_private_properties(self): - self.assertEqual(len(self.cppHeader.classes["GrapeClass"]["properties"]["private"]), 1) - + self.assertEqual( + len(self.cppHeader.classes["GrapeClass"]["properties"]["private"]), 1 + ) + def test_num_protected_properties(self): - self.assertEqual(len(self.cppHeader.classes["GrapeClass"]["properties"]["protected"]), 0) + self.assertEqual( + len(self.cppHeader.classes["GrapeClass"]["properties"]["protected"]), 0 + ) def test_num_public_methods(self): - self.assertEqual(len(self.cppHeader.classes["GrapeClass"]["methods"]["public"]), 0) - + self.assertEqual( + len(self.cppHeader.classes["GrapeClass"]["methods"]["public"]), 0 + ) + def test_num_private_methods(self): - self.assertEqual(len(self.cppHeader.classes["GrapeClass"]["methods"]["private"]), 1) - + self.assertEqual( + len(self.cppHeader.classes["GrapeClass"]["methods"]["private"]), 1 + ) + def test_num_protected_methods(self): - self.assertEqual(len(self.cppHeader.classes["GrapeClass"]["methods"]["protected"]), 0) + self.assertEqual( + len(self.cppHeader.classes["GrapeClass"]["methods"]["protected"]), 0 + ) class AnonHolderClass_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_property(self): - cmp_values = {'constant': 0, 'name': 'a', 'reference': 0, 'type': '', 'static': 0, 'pointer': 0} - self.assertEqual(filter_dict_keys(self.cppHeader.classes["AnonHolderClass"]["properties"]["public"][0], cmp_values.keys()), cmp_values) + cmp_values = { + "constant": 0, + "name": "a", + "reference": 0, + "type": "", + "static": 0, + "pointer": 0, + } + self.assertEqual( + filter_dict_keys( + self.cppHeader.classes["AnonHolderClass"]["properties"]["public"][0], + cmp_values.keys(), + ), + cmp_values, + ) class CowClass_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_class_declaration_method(self): - self.assertEqual(self.cppHeader.classes["CowClass"]["declaration_method"], "class") + self.assertEqual( + self.cppHeader.classes["CowClass"]["declaration_method"], "class" + ) def test_struct_declaration_method(self): - self.assertEqual(self.cppHeader.classes["CowStruct"]["declaration_method"], "struct") + self.assertEqual( + self.cppHeader.classes["CowStruct"]["declaration_method"], "struct" + ) class Mango_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - - def test_virtual_inherits(self): - self.assertEqual(self.cppHeader.classes["MangoClass"]["inherits"][0]["virtual"], True) + def test_virtual_inherits(self): + self.assertEqual( + self.cppHeader.classes["MangoClass"]["inherits"][0]["virtual"], True + ) class Eagle_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_property(self): - cmp_values = {'constant': 0, 'name': 'a', 'reference': 0, 'array_size': 'MAX_LEN', 'type': 'int', 'static': 0, 'pointer': 0} - self.assertEqual(filter_dict_keys(self.cppHeader.classes["EagleClass"]["properties"]["private"][0], cmp_values.keys()), cmp_values) - + cmp_values = { + "constant": 0, + "name": "a", + "reference": 0, + "array_size": "MAX_LEN", + "type": "int", + "static": 0, + "pointer": 0, + } + self.assertEqual( + filter_dict_keys( + self.cppHeader.classes["EagleClass"]["properties"]["private"][0], + cmp_values.keys(), + ), + cmp_values, + ) -class Frog_TestCase(unittest.TestCase): +class Frog_TestCase(unittest.TestCase): def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_num_private_properties(self): - self.assertEqual(len(self.cppHeader.classes["FrogClass"]["properties"]["private"]), 3) + self.assertEqual( + len(self.cppHeader.classes["FrogClass"]["properties"]["private"]), 3 + ) - class Cat_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_num_private_properties(self): - self.assertEqual(len(self.cppHeader.classes["CatClass"]["properties"]["private"]), 0) + self.assertEqual( + len(self.cppHeader.classes["CatClass"]["properties"]["private"]), 0 + ) class Fish_TestCase(unittest.TestCase): - def setUp(self): - #Just make sure it doesnt crash + # Just make sure it doesnt crash self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - -class Panda_TestCase(unittest.TestCase): +class Panda_TestCase(unittest.TestCase): def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_property_CONST_A(self): - cmp_values = {'typedef': None, 'unresolved': False, 'constant': 1, 'name': 'CONST_A', - 'parent': None, 'pointer': 0, 'namespace': '', 'raw_type': 'int', 'class': 0, - 'property_of_class': 'PandaClass', 'static': 1, 'fundamental': True, - 'mutable': False, 'typedefs': 0, 'array': 0, 'type': 'static const int', - 'reference': 0, 'aliases': []} - self.assertEqual(filter_dict_keys(self.cppHeader.classes["PandaClass"]["properties"]["private"][0], cmp_values.keys()), cmp_values) + cmp_values = { + "typedef": None, + "unresolved": False, + "constant": 1, + "name": "CONST_A", + "parent": None, + "pointer": 0, + "namespace": "", + "raw_type": "int", + "class": 0, + "property_of_class": "PandaClass", + "static": 1, + "fundamental": True, + "mutable": False, + "typedefs": 0, + "array": 0, + "type": "static const int", + "reference": 0, + "aliases": [], + } + self.assertEqual( + filter_dict_keys( + self.cppHeader.classes["PandaClass"]["properties"]["private"][0], + cmp_values.keys(), + ), + cmp_values, + ) def test_property_CONST_B(self): - cmp_values = {'typedef': None, 'unresolved': False, 'constant': 1, 'name': 'CONST_B', - 'parent': None, 'pointer': 0, 'namespace': '', 'raw_type': 'int', 'class': 0, - 'property_of_class': 'PandaClass', 'static': 1, 'fundamental': True, - 'mutable': False, 'typedefs': 0, 'array': 0, 'type': 'static const int', - 'reference': 0, 'aliases': []} - self.assertEqual(filter_dict_keys(self.cppHeader.classes["PandaClass"]["properties"]["private"][1], cmp_values.keys()), cmp_values) + cmp_values = { + "typedef": None, + "unresolved": False, + "constant": 1, + "name": "CONST_B", + "parent": None, + "pointer": 0, + "namespace": "", + "raw_type": "int", + "class": 0, + "property_of_class": "PandaClass", + "static": 1, + "fundamental": True, + "mutable": False, + "typedefs": 0, + "array": 0, + "type": "static const int", + "reference": 0, + "aliases": [], + } + self.assertEqual( + filter_dict_keys( + self.cppHeader.classes["PandaClass"]["properties"]["private"][1], + cmp_values.keys(), + ), + cmp_values, + ) class Potato_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_num_private_properties_potato(self): - self.assertEqual(len(self.cppHeader.classes["PotatoClass"]["properties"]["private"]), 1) - + self.assertEqual( + len(self.cppHeader.classes["PotatoClass"]["properties"]["private"]), 1 + ) + def test_num_public_properties_potato_fwdstruct(self): - self.assertEqual(len(self.cppHeader.classes["PotatoClass::FwdStruct"]["properties"]["public"]), 1) + self.assertEqual( + len( + self.cppHeader.classes["PotatoClass::FwdStruct"]["properties"]["public"] + ), + 1, + ) class Hog_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_num_private_properties_potato(self): - self.assertEqual(len(self.cppHeader.classes["HogClass"]["properties"]["private"]), 1) - + self.assertEqual( + len(self.cppHeader.classes["HogClass"]["properties"]["private"]), 1 + ) + def test_property(self): - cmp_values = {'constant': 0, 'name': 'u', 'reference': 0, 'type': 'union HogUnion', 'static': 0, 'pointer': 0} - self.assertEqual(filter_dict_keys(self.cppHeader.classes["HogClass"]["properties"]["private"][0], cmp_values.keys()), cmp_values) + cmp_values = { + "constant": 0, + "name": "u", + "reference": 0, + "type": "union HogUnion", + "static": 0, + "pointer": 0, + } + self.assertEqual( + filter_dict_keys( + self.cppHeader.classes["HogClass"]["properties"]["private"][0], + cmp_values.keys(), + ), + cmp_values, + ) def test_union(self): - cmp_values = {"name": "union HogUnion", "parent": "HogClass", "declaration_method": "union"} - self.assertEqual(filter_dict_keys(self.cppHeader.classes["HogClass::union HogUnion"], cmp_values.keys()), cmp_values) - + cmp_values = { + "name": "union HogUnion", + "parent": "HogClass", + "declaration_method": "union", + } + self.assertEqual( + filter_dict_keys( + self.cppHeader.classes["HogClass::union HogUnion"], cmp_values.keys() + ), + cmp_values, + ) + def test_union_member_a(self): - cmp_values = {'constant': 0, 'name': 'a', 'reference': 0, 'type': 'int', 'static': 0, 'pointer': 0} - self.assertEqual(filter_dict_keys(self.cppHeader.classes["HogClass::union HogUnion"]["members"][0], cmp_values.keys()), cmp_values) - + cmp_values = { + "constant": 0, + "name": "a", + "reference": 0, + "type": "int", + "static": 0, + "pointer": 0, + } + self.assertEqual( + filter_dict_keys( + self.cppHeader.classes["HogClass::union HogUnion"]["members"][0], + cmp_values.keys(), + ), + cmp_values, + ) + def test_union_member_b(self): - cmp_values = {'constant': 0, 'name': 'b', 'reference': 0, 'type': 'float', 'static': 0, 'pointer': 0} - self.assertEqual(filter_dict_keys(self.cppHeader.classes["HogClass::union HogUnion"]["members"][1], cmp_values.keys()), cmp_values) + cmp_values = { + "constant": 0, + "name": "b", + "reference": 0, + "type": "float", + "static": 0, + "pointer": 0, + } + self.assertEqual( + filter_dict_keys( + self.cppHeader.classes["HogClass::union HogUnion"]["members"][1], + cmp_values.keys(), + ), + cmp_values, + ) + # Bug 3497158 class CherryClass_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_num_public_properties(self): - self.assertEqual(len(self.cppHeader.classes["CherryClass::NestStruct"]["properties"]["public"]), 1) - + self.assertEqual( + len( + self.cppHeader.classes["CherryClass::NestStruct"]["properties"][ + "public" + ] + ), + 1, + ) + def test_num_public_methods(self): - self.assertEqual(len(self.cppHeader.classes["CherryClass::NestStruct"]["methods"]["public"]), 1) + self.assertEqual( + len(self.cppHeader.classes["CherryClass::NestStruct"]["methods"]["public"]), + 1, + ) + # Bug 3517308 class GarlicClass_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_num_public_properties(self): - self.assertEqual(len(self.cppHeader.classes["GarlicClass"]["properties"]["public"]), 0) - + self.assertEqual( + len(self.cppHeader.classes["GarlicClass"]["properties"]["public"]), 0 + ) + def test_num_public_methods(self): - self.assertEqual(len(self.cppHeader.classes["GarlicClass"]["methods"]["public"]), 3) + self.assertEqual( + len(self.cppHeader.classes["GarlicClass"]["methods"]["public"]), 3 + ) + # Bug 3514728 class CarrotClass_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_num_private_properties(self): - self.assertEqual(len(self.cppHeader.classes["CarrotClass"]["properties"]["private"]), 1) - + self.assertEqual( + len(self.cppHeader.classes["CarrotClass"]["properties"]["private"]), 1 + ) + def test_num_private_methods(self): - self.assertEqual(len(self.cppHeader.classes["CarrotClass"]["methods"]["private"]), 1) - + self.assertEqual( + len(self.cppHeader.classes["CarrotClass"]["methods"]["private"]), 1 + ) + def test_method_params(self): self.assertEqual( - filter_pameters(self.cppHeader.classes["CarrotClass"]["methods"]["private"][0]["parameters"]), - []) - + filter_pameters( + self.cppHeader.classes["CarrotClass"]["methods"]["private"][0][ + "parameters" + ] + ), + [], + ) + def test_class_template(self): - self.assertEqual(self.cppHeader.classes["CarrotClass"]["template"], "template") + self.assertEqual( + self.cppHeader.classes["CarrotClass"]["template"], "template" + ) # Bug 3517289 class CarrotClass_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_num_private_methods(self): - self.assertEqual(len(self.cppHeader.classes["ExternClass"]["methods"]["private"]), 1) + self.assertEqual( + len(self.cppHeader.classes["ExternClass"]["methods"]["private"]), 1 + ) # Bug 3514671 class OliveStruct_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_num_public_properties(self): - self.assertEqual(len(self.cppHeader.classes["OliveStruct"]["properties"]["public"]), 4) - + self.assertEqual( + len(self.cppHeader.classes["OliveStruct"]["properties"]["public"]), 4 + ) + def test_var_a(self): - self.assertEqual(self.cppHeader.classes["OliveStruct"]["properties"]["public"][0]["name"], "a") + self.assertEqual( + self.cppHeader.classes["OliveStruct"]["properties"]["public"][0]["name"], + "a", + ) # Bug 3515330 class Rooster_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - + def test_num_public_properties(self): - self.assertEqual(len(self.cppHeader.classes["RoosterOuterClass"]["properties"]["public"]), 1) - + self.assertEqual( + len(self.cppHeader.classes["RoosterOuterClass"]["properties"]["public"]), 1 + ) + def test_num_private_properties(self): - self.assertEqual(len(self.cppHeader.classes["RoosterOuterClass"]["properties"]["private"]), 1) - + self.assertEqual( + len(self.cppHeader.classes["RoosterOuterClass"]["properties"]["private"]), 1 + ) + def test_num_sub1_public_properties(self): - self.assertEqual(len(self.cppHeader.classes["RoosterOuterClass::RoosterSubClass1"]["properties"]["public"]), 1) - + self.assertEqual( + len( + self.cppHeader.classes["RoosterOuterClass::RoosterSubClass1"][ + "properties" + ]["public"] + ), + 1, + ) + def test_num_sub1_private_properties(self): - self.assertEqual(len(self.cppHeader.classes["RoosterOuterClass::RoosterSubClass1"]["properties"]["private"]), 1) - + self.assertEqual( + len( + self.cppHeader.classes["RoosterOuterClass::RoosterSubClass1"][ + "properties" + ]["private"] + ), + 1, + ) + def test_num_sub2_public_properties(self): - self.assertEqual(len(self.cppHeader.classes["RoosterOuterClass::RoosterSubClass2"]["properties"]["public"]), 1) - + self.assertEqual( + len( + self.cppHeader.classes["RoosterOuterClass::RoosterSubClass2"][ + "properties" + ]["public"] + ), + 1, + ) + def test_num_sub2_private_properties(self): - self.assertEqual(len(self.cppHeader.classes["RoosterOuterClass::RoosterSubClass2"]["properties"]["private"]), 1) + self.assertEqual( + len( + self.cppHeader.classes["RoosterOuterClass::RoosterSubClass2"][ + "properties" + ]["private"] + ), + 1, + ) # Bug 3514672 class OperatorClass_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - + def test_op_0(self): - self.assertEqual(self.cppHeader.classes["OperatorClass"]["methods"]["public"][0]["name"], 'operator=') + self.assertEqual( + self.cppHeader.classes["OperatorClass"]["methods"]["public"][0]["name"], + "operator=", + ) def test_op_1(self): - self.assertEqual(self.cppHeader.classes["OperatorClass"]["methods"]["public"][1]["name"], 'operator-=') + self.assertEqual( + self.cppHeader.classes["OperatorClass"]["methods"]["public"][1]["name"], + "operator-=", + ) def test_op_2(self): - self.assertEqual(self.cppHeader.classes["OperatorClass"]["methods"]["public"][2]["name"], 'operator+=') + self.assertEqual( + self.cppHeader.classes["OperatorClass"]["methods"]["public"][2]["name"], + "operator+=", + ) def test_op_3(self): - self.assertEqual(self.cppHeader.classes["OperatorClass"]["methods"]["public"][3]["name"], 'operator[]') + self.assertEqual( + self.cppHeader.classes["OperatorClass"]["methods"]["public"][3]["name"], + "operator[]", + ) def test_op_4(self): - self.assertEqual(self.cppHeader.classes["OperatorClass"]["methods"]["public"][4]["name"], 'operator==') + self.assertEqual( + self.cppHeader.classes["OperatorClass"]["methods"]["public"][4]["name"], + "operator==", + ) def test_op_5(self): - self.assertEqual(self.cppHeader.classes["OperatorClass"]["methods"]["public"][5]["name"], 'operator+') + self.assertEqual( + self.cppHeader.classes["OperatorClass"]["methods"]["public"][5]["name"], + "operator+", + ) def test_op_6(self): - self.assertEqual(self.cppHeader.classes["OperatorClass"]["methods"]["public"][6]["name"], 'operator-') + self.assertEqual( + self.cppHeader.classes["OperatorClass"]["methods"]["public"][6]["name"], + "operator-", + ) def test_op_7(self): - self.assertEqual(self.cppHeader.classes["OperatorClass"]["methods"]["public"][7]["name"], 'operator*') + self.assertEqual( + self.cppHeader.classes["OperatorClass"]["methods"]["public"][7]["name"], + "operator*", + ) def test_op_8(self): - self.assertEqual(self.cppHeader.classes["OperatorClass"]["methods"]["public"][8]["name"], 'operator\\') + self.assertEqual( + self.cppHeader.classes["OperatorClass"]["methods"]["public"][8]["name"], + "operator\\", + ) def test_op_9(self): - self.assertEqual(self.cppHeader.classes["OperatorClass"]["methods"]["public"][9]["name"], 'operator%') + self.assertEqual( + self.cppHeader.classes["OperatorClass"]["methods"]["public"][9]["name"], + "operator%", + ) def test_op_10(self): - self.assertEqual(self.cppHeader.classes["OperatorClass"]["methods"]["public"][10]["name"], 'operator^') + self.assertEqual( + self.cppHeader.classes["OperatorClass"]["methods"]["public"][10]["name"], + "operator^", + ) def test_op_11(self): - self.assertEqual(self.cppHeader.classes["OperatorClass"]["methods"]["public"][11]["name"], 'operator|') + self.assertEqual( + self.cppHeader.classes["OperatorClass"]["methods"]["public"][11]["name"], + "operator|", + ) def test_op_12(self): - self.assertEqual(self.cppHeader.classes["OperatorClass"]["methods"]["public"][12]["name"], 'operator&') + self.assertEqual( + self.cppHeader.classes["OperatorClass"]["methods"]["public"][12]["name"], + "operator&", + ) def test_op_13(self): - self.assertEqual(self.cppHeader.classes["OperatorClass"]["methods"]["public"][13]["name"], 'operator~') + self.assertEqual( + self.cppHeader.classes["OperatorClass"]["methods"]["public"][13]["name"], + "operator~", + ) def test_op_14(self): - self.assertEqual(self.cppHeader.classes["OperatorClass"]["methods"]["public"][14]["name"], 'operator<<') + self.assertEqual( + self.cppHeader.classes["OperatorClass"]["methods"]["public"][14]["name"], + "operator<<", + ) def test_op_15(self): - self.assertEqual(self.cppHeader.classes["OperatorClass"]["methods"]["public"][15]["name"], 'operator>>') + self.assertEqual( + self.cppHeader.classes["OperatorClass"]["methods"]["public"][15]["name"], + "operator>>", + ) def test_op_16(self): - self.assertEqual(self.cppHeader.classes["OperatorClass"]["methods"]["public"][16]["name"], 'operator!=') + self.assertEqual( + self.cppHeader.classes["OperatorClass"]["methods"]["public"][16]["name"], + "operator!=", + ) def test_op_17(self): - self.assertEqual(self.cppHeader.classes["OperatorClass"]["methods"]["public"][17]["name"], 'operator<') + self.assertEqual( + self.cppHeader.classes["OperatorClass"]["methods"]["public"][17]["name"], + "operator<", + ) def test_op_18(self): - self.assertEqual(self.cppHeader.classes["OperatorClass"]["methods"]["public"][18]["name"], 'operator>') + self.assertEqual( + self.cppHeader.classes["OperatorClass"]["methods"]["public"][18]["name"], + "operator>", + ) def test_op_19(self): - self.assertEqual(self.cppHeader.classes["OperatorClass"]["methods"]["public"][19]["name"], 'operator>=') + self.assertEqual( + self.cppHeader.classes["OperatorClass"]["methods"]["public"][19]["name"], + "operator>=", + ) def test_op_20(self): - self.assertEqual(self.cppHeader.classes["OperatorClass"]["methods"]["public"][20]["name"], 'operator<=') + self.assertEqual( + self.cppHeader.classes["OperatorClass"]["methods"]["public"][20]["name"], + "operator<=", + ) def test_op_21(self): - self.assertEqual(self.cppHeader.classes["OperatorClass"]["methods"]["public"][21]["name"], 'operator!') + self.assertEqual( + self.cppHeader.classes["OperatorClass"]["methods"]["public"][21]["name"], + "operator!", + ) def test_op_22(self): - self.assertEqual(self.cppHeader.classes["OperatorClass"]["methods"]["public"][22]["name"], 'operator&&') + self.assertEqual( + self.cppHeader.classes["OperatorClass"]["methods"]["public"][22]["name"], + "operator&&", + ) def test_op_23(self): - self.assertEqual(self.cppHeader.classes["OperatorClass"]["methods"]["public"][23]["name"], 'operator||') + self.assertEqual( + self.cppHeader.classes["OperatorClass"]["methods"]["public"][23]["name"], + "operator||", + ) def test_op_24(self): - self.assertEqual(self.cppHeader.classes["OperatorClass"]["methods"]["public"][24]["name"], 'operator+=') + self.assertEqual( + self.cppHeader.classes["OperatorClass"]["methods"]["public"][24]["name"], + "operator+=", + ) def test_op_25(self): - self.assertEqual(self.cppHeader.classes["OperatorClass"]["methods"]["public"][25]["name"], 'operator-=') + self.assertEqual( + self.cppHeader.classes["OperatorClass"]["methods"]["public"][25]["name"], + "operator-=", + ) def test_op_26(self): - self.assertEqual(self.cppHeader.classes["OperatorClass"]["methods"]["public"][26]["name"], 'operator*=') + self.assertEqual( + self.cppHeader.classes["OperatorClass"]["methods"]["public"][26]["name"], + "operator*=", + ) def test_op_27(self): - self.assertEqual(self.cppHeader.classes["OperatorClass"]["methods"]["public"][27]["name"], 'operator\\=') + self.assertEqual( + self.cppHeader.classes["OperatorClass"]["methods"]["public"][27]["name"], + "operator\\=", + ) def test_op_28(self): - self.assertEqual(self.cppHeader.classes["OperatorClass"]["methods"]["public"][28]["name"], 'operator%=') + self.assertEqual( + self.cppHeader.classes["OperatorClass"]["methods"]["public"][28]["name"], + "operator%=", + ) def test_op_29(self): - self.assertEqual(self.cppHeader.classes["OperatorClass"]["methods"]["public"][29]["name"], 'operator&=') + self.assertEqual( + self.cppHeader.classes["OperatorClass"]["methods"]["public"][29]["name"], + "operator&=", + ) def test_op_30(self): - self.assertEqual(self.cppHeader.classes["OperatorClass"]["methods"]["public"][30]["name"], 'operator|=') + self.assertEqual( + self.cppHeader.classes["OperatorClass"]["methods"]["public"][30]["name"], + "operator|=", + ) def test_op_31(self): - self.assertEqual(self.cppHeader.classes["OperatorClass"]["methods"]["public"][31]["name"], 'operator^=') + self.assertEqual( + self.cppHeader.classes["OperatorClass"]["methods"]["public"][31]["name"], + "operator^=", + ) def test_op_32(self): - self.assertEqual(self.cppHeader.classes["OperatorClass"]["methods"]["public"][32]["name"], 'operator<<=') + self.assertEqual( + self.cppHeader.classes["OperatorClass"]["methods"]["public"][32]["name"], + "operator<<=", + ) def test_op_33(self): - self.assertEqual(self.cppHeader.classes["OperatorClass"]["methods"]["public"][33]["name"], 'operator>>=') + self.assertEqual( + self.cppHeader.classes["OperatorClass"]["methods"]["public"][33]["name"], + "operator>>=", + ) def test_op_34(self): - self.assertEqual(self.cppHeader.classes["OperatorClass"]["methods"]["public"][34]["name"], 'operator++') + self.assertEqual( + self.cppHeader.classes["OperatorClass"]["methods"]["public"][34]["name"], + "operator++", + ) def test_op_35(self): - self.assertEqual(self.cppHeader.classes["OperatorClass"]["methods"]["public"][35]["name"], 'operator--') + self.assertEqual( + self.cppHeader.classes["OperatorClass"]["methods"]["public"][35]["name"], + "operator--", + ) def test_op_36(self): - self.assertEqual(self.cppHeader.classes["OperatorClass"]["methods"]["public"][36]["name"], 'operator()') + self.assertEqual( + self.cppHeader.classes["OperatorClass"]["methods"]["public"][36]["name"], + "operator()", + ) def test_op_37(self): - self.assertEqual(self.cppHeader.classes["OperatorClass"]["methods"]["public"][37]["name"], 'operator->') + self.assertEqual( + self.cppHeader.classes["OperatorClass"]["methods"]["public"][37]["name"], + "operator->", + ) def test_op_38(self): - self.assertEqual(self.cppHeader.classes["OperatorClass"]["methods"]["public"][38]["name"], 'operator,') + self.assertEqual( + self.cppHeader.classes["OperatorClass"]["methods"]["public"][38]["name"], + "operator,", + ) # Feature Request 3519502 & 3523010 class CrowClass_TestCase(unittest.TestCase): - def setUp(self): self.savedSupportedAccessSpecifier = CppHeaderParser.supportedAccessSpecifier - CppHeaderParser.supportedAccessSpecifier.append("public slots ")#intentionally add expra spaces to make sure they get cleaned up + CppHeaderParser.supportedAccessSpecifier.append( + "public slots " + ) # intentionally add expra spaces to make sure they get cleaned up self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_num_public_methods(self): - self.assertEqual(len(self.cppHeader.classes["CrowClass"]["methods"]["public"]), 1) + self.assertEqual( + len(self.cppHeader.classes["CrowClass"]["methods"]["public"]), 1 + ) def test_rtntype_public_slot_method(self): - self.assertEqual(self.cppHeader.classes["CrowClass"]["methods"]["public slots"][0]["rtnType"], 'void') + self.assertEqual( + self.cppHeader.classes["CrowClass"]["methods"]["public slots"][0][ + "rtnType" + ], + "void", + ) def test_num_public_slot_methods(self): - self.assertEqual(len(self.cppHeader.classes["CrowClass"]["methods"]["public slots"]), 1) - + self.assertEqual( + len(self.cppHeader.classes["CrowClass"]["methods"]["public slots"]), 1 + ) + def tearDown(self): CppHeaderParser.supportedAccessSpecifier = self.savedSupportedAccessSpecifier # Bug 3497170 class DriverFuncs_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - + def test_name_0(self): - self.assertEqual(self.cppHeader.classes["DriverFuncs"]["properties"]["public"][0]["name"], "init") - + self.assertEqual( + self.cppHeader.classes["DriverFuncs"]["properties"]["public"][0]["name"], + "init", + ) + def test_type_0(self): - self.assertEqual(self.cppHeader.classes["DriverFuncs"]["properties"]["public"][0]["type"], "void * ( * ) ( )") - + self.assertEqual( + self.cppHeader.classes["DriverFuncs"]["properties"]["public"][0]["type"], + "void * ( * ) ( )", + ) + def test_function_pointer_field_0(self): - self.assertEqual(self.cppHeader.classes["DriverFuncs"]["properties"]["public"][0]["function_pointer"], 1) - + self.assertEqual( + self.cppHeader.classes["DriverFuncs"]["properties"]["public"][0][ + "function_pointer" + ], + 1, + ) + def test_name_1(self): - self.assertEqual(self.cppHeader.classes["DriverFuncs"]["properties"]["public"][1]["name"], "write") - + self.assertEqual( + self.cppHeader.classes["DriverFuncs"]["properties"]["public"][1]["name"], + "write", + ) + def test_type_1(self): - self.assertEqual(self.cppHeader.classes["DriverFuncs"]["properties"]["public"][1]["type"], "void ( * ) ( void * buf, int buflen )") - + self.assertEqual( + self.cppHeader.classes["DriverFuncs"]["properties"]["public"][1]["type"], + "void ( * ) ( void * buf, int buflen )", + ) + def test_function_pointer_field_1(self): - self.assertEqual(self.cppHeader.classes["DriverFuncs"]["properties"]["public"][1]["function_pointer"], 1) - + self.assertEqual( + self.cppHeader.classes["DriverFuncs"]["properties"]["public"][1][ + "function_pointer" + ], + 1, + ) + # Bug 3519178 class Snail_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - + def test_rtn_type(self): - self.assertEqual(self.cppHeader.classes["Snail2Class"]["methods"]["public"][0]["rtnType"], "SnailNamespace::SnailClass") - + self.assertEqual( + self.cppHeader.classes["Snail2Class"]["methods"]["public"][0]["rtnType"], + "SnailNamespace::SnailClass", + ) + def test_param_name(self): - self.assertEqual(self.cppHeader.classes["Snail2Class"]["methods"]["public"][0]["parameters"][0]["name"], "") - + self.assertEqual( + self.cppHeader.classes["Snail2Class"]["methods"]["public"][0]["parameters"][ + 0 + ]["name"], + "", + ) + def test_param_name(self): - self.assertEqual(self.cppHeader.classes["Snail2Class"]["methods"]["public"][0]["parameters"][0]["type"], "tr1::shared_ptr >") + self.assertEqual( + self.cppHeader.classes["Snail2Class"]["methods"]["public"][0]["parameters"][ + 0 + ]["type"], + "tr1::shared_ptr >", + ) + # Feature Request 3523198 class Quale_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - + def test_rtn_type(self): - self.assertEqual(self.cppHeader.classes["QualeClass"]["methods"]["private"][0]["rtnType"], "void") + self.assertEqual( + self.cppHeader.classes["QualeClass"]["methods"]["private"][0]["rtnType"], + "void", + ) # Feature Request 3523235 class Rock_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - + def test_const_0(self): - self.assertEqual(self.cppHeader.classes["RockClass"]["methods"]["private"][0]["const"], True) - + self.assertEqual( + self.cppHeader.classes["RockClass"]["methods"]["private"][0]["const"], True + ) + def test_const_1(self): - self.assertEqual(self.cppHeader.classes["RockClass"]["methods"]["private"][1]["const"], False) + self.assertEqual( + self.cppHeader.classes["RockClass"]["methods"]["private"][1]["const"], False + ) # Bug 3523196 class Almond_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - + def test_rtn_type(self): - self.assertEqual(self.cppHeader.classes["AlmondClass"]["methods"]["public"][0]["rtnType"], "std::map > >") - + self.assertEqual( + self.cppHeader.classes["AlmondClass"]["methods"]["public"][0]["rtnType"], + "std::map > >", + ) + def test_param_1_name(self): - self.assertEqual(self.cppHeader.classes["AlmondClass"]["methods"]["public"][0]["parameters"][0]["name"], "flag") - + self.assertEqual( + self.cppHeader.classes["AlmondClass"]["methods"]["public"][0]["parameters"][ + 0 + ]["name"], + "flag", + ) + def test_param_1_type(self): - self.assertEqual(self.cppHeader.classes["AlmondClass"]["methods"]["public"][0]["parameters"][0]["type"], "bool") - + self.assertEqual( + self.cppHeader.classes["AlmondClass"]["methods"]["public"][0]["parameters"][ + 0 + ]["type"], + "bool", + ) + def test_param_2_name(self): - self.assertEqual(self.cppHeader.classes["AlmondClass"]["methods"]["public"][0]["parameters"][1]["name"], "bigArg") - + self.assertEqual( + self.cppHeader.classes["AlmondClass"]["methods"]["public"][0]["parameters"][ + 1 + ]["name"], + "bigArg", + ) + def test_param_2_type(self): - self.assertEqual(self.cppHeader.classes["AlmondClass"]["methods"]["public"][0]["parameters"][1]["type"], "std::map > >") + self.assertEqual( + self.cppHeader.classes["AlmondClass"]["methods"]["public"][0]["parameters"][ + 1 + ]["type"], + "std::map > >", + ) # Bug 3524327 class Stone_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - + def test_const_0(self): - self.assertEqual(self.cppHeader.classes["StoneClass"]["methods"]["private"][0]["const"], True) - + self.assertEqual( + self.cppHeader.classes["StoneClass"]["methods"]["private"][0]["const"], True + ) + def test_const_1(self): - self.assertEqual(self.cppHeader.classes["StoneClass"]["methods"]["private"][1]["const"], False) - + self.assertEqual( + self.cppHeader.classes["StoneClass"]["methods"]["private"][1]["const"], + False, + ) + # Bug 3531219 class Kangaroo_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_num_kangaroo_methods(self): - self.assertEqual(len(self.cppHeader.classes["Kangaroo"]["methods"]["public"]), 1) - + self.assertEqual( + len(self.cppHeader.classes["Kangaroo"]["methods"]["public"]), 1 + ) + def test_num_joey_methods(self): - self.assertEqual(len(self.cppHeader.classes["Kangaroo::Joey"]["methods"]["public"]), 1) + self.assertEqual( + len(self.cppHeader.classes["Kangaroo::Joey"]["methods"]["public"]), 1 + ) # Bug 3535465 class Ant_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - + def test_num_constructor_1_params(self): - self.assertEqual(len(self.cppHeader.classes["Ant"]["methods"]["public"][0]["parameters"]), 3) - + self.assertEqual( + len(self.cppHeader.classes["Ant"]["methods"]["public"][0]["parameters"]), 3 + ) + def test_num_constructor_2_params(self): - self.assertEqual(len(self.cppHeader.classes["Ant"]["methods"]["public"][1]["parameters"]), 1) + self.assertEqual( + len(self.cppHeader.classes["Ant"]["methods"]["public"][1]["parameters"]), 1 + ) + # Bug 3536069 class Onion_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_num_public_properties_red(self): - self.assertEqual(len(self.cppHeader.classes["Onion"]["properties"]["public"]), 1) + self.assertEqual( + len(self.cppHeader.classes["Onion"]["properties"]["public"]), 1 + ) def test_num_public_properties_sweet(self): - self.assertEqual(len(self.cppHeader.classes["Onion"]["properties"]["public"]), 1) + self.assertEqual( + len(self.cppHeader.classes["Onion"]["properties"]["public"]), 1 + ) def test_class_template(self): - self.assertEqual(self.cppHeader.classes["Onion"]["template"], "template ") - + self.assertEqual( + self.cppHeader.classes["Onion"]["template"], + "template ", + ) + + # Bug 3536067 class BlueJay_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_num_public_methods(self): self.assertEqual(len(self.cppHeader.classes["BlueJay"]["methods"]["public"]), 1) + # Bug 3536266 class functions_TestCase(unittest.TestCase): - def setUp(self): - self.cppHeader = CppHeaderParser.CppHeader("""\ + self.cppHeader = CppHeaderParser.CppHeader( + """\ void global_funct1(int i); int global_funct2(void); - """, "string") - + """, + "string", + ) + def test_num_functions(self): self.assertEqual(len(self.cppHeader.functions), 2) - + def test_function_name_1(self): self.assertEqual(self.cppHeader.functions[0]["name"], "global_funct1") - + def test_function_name_2(self): self.assertEqual(self.cppHeader.functions[1]["name"], "global_funct2") - + # Bug 3536071 class Pea_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") @@ -1181,68 +1768,82 @@ def test_num_inherits(self): self.assertEqual(len(self.cppHeader.classes["Pea"]["inherits"]), 1) def test_name_inherits(self): - self.assertEqual(self.cppHeader.classes["Pea"]["inherits"][0]["class"], "Vegetable") + self.assertEqual( + self.cppHeader.classes["Pea"]["inherits"][0]["class"], "Vegetable" + ) + # Bug 3540172 class functions2_TestCase(unittest.TestCase): - def setUp(self): - self.cppHeader = CppHeaderParser.CppHeader("""\ + self.cppHeader = CppHeaderParser.CppHeader( + """\ void global_funct1(int i); int global_funct2(void){ // do something } - """, "string") - + """, + "string", + ) + def test_num_functions(self): self.assertEqual(len(self.cppHeader.functions), 2) - + def test_function_name_1(self): self.assertEqual(self.cppHeader.functions[0]["name"], "global_funct1") - + def test_function_name_2(self): self.assertEqual(self.cppHeader.functions[1]["name"], "global_funct2") + # Feature: line numbers class line_num_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("LineNumTest.h") - + def test_lineno_function1(self): return self.assertEqual(self.cppHeader.functions[0]["line_number"], 13) - + def test_lineno_function2(self): return self.assertEqual(self.cppHeader.functions[1]["line_number"], 17) - + def test_lineno_Worm(self): return self.assertEqual(self.cppHeader.classes["Worm"]["line_number"], 20) - + def test_lineno_Worm_Constructor(self): - return self.assertEqual(self.cppHeader.classes["Worm"]["methods"]["public"][0]["line_number"], 23) - + return self.assertEqual( + self.cppHeader.classes["Worm"]["methods"]["public"][0]["line_number"], 23 + ) + def test_lineno_Worm_getName(self): - return self.assertEqual(self.cppHeader.classes["Worm"]["methods"]["public"][1]["line_number"], 24) - + return self.assertEqual( + self.cppHeader.classes["Worm"]["methods"]["public"][1]["line_number"], 24 + ) + def test_lineno_Worm_namep(self): - return self.assertEqual(self.cppHeader.classes["Worm"]["properties"]["private"][0]["line_number"], 29) + return self.assertEqual( + self.cppHeader.classes["Worm"]["properties"]["private"][0]["line_number"], + 29, + ) + # Bug 3567172 class Pear_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_property(self): - self.assertEqual(self.cppHeader.classes["Pear"]["properties"]["private"][0]["name"], "stem_property") - + self.assertEqual( + self.cppHeader.classes["Pear"]["properties"]["private"][0]["name"], + "stem_property", + ) # Bug 3567217 and 3569663 class Macro_TestCase(unittest.TestCase): - def setUp(self): - self.cppHeader = CppHeaderParser.CppHeader(r""" + self.cppHeader = CppHeaderParser.CppHeader( + r""" #include #include "../../debug.h" @@ -1253,98 +1854,103 @@ def setUp(self): #define DEBUG_PRINT(x) \ printf("---------------\n"); \ printf("DEBUG: %d\n", x); \ - printf("---------------\n");""", "string") + printf("---------------\n");""", + "string", + ) def test_includes(self): - self.assertEqual(self.cppHeader.includes, ['', '"../../debug.h"']) - + self.assertEqual(self.cppHeader.includes, ["", '"../../debug.h"']) + def test_pragmas(self): - self.assertEqual(self.cppHeader.pragmas, ['once']) - + self.assertEqual(self.cppHeader.pragmas, ["once"]) + def test_pragmas0(self): - self.assertEqual(self.cppHeader.defines[0], 'ONE 1') - + self.assertEqual(self.cppHeader.defines[0], "ONE 1") + def test_pragmas1(self): self.assertEqual(self.cppHeader.defines[1], 'TWO_NUM_N_NAME "2 (TWO)"') - - def test_pragmas2(self): - self.assertEqual(self.cppHeader.defines[2], 'DEBUG_PRINT(x) \\\n printf("---------------\\n"); \\\n printf("DEBUG: %d\\n", x); \\\n printf("---------------\\n");') + def test_pragmas2(self): + self.assertEqual( + self.cppHeader.defines[2], + 'DEBUG_PRINT(x) \\\n printf("---------------\\n"); \\\n printf("DEBUG: %d\\n", x); \\\n printf("---------------\\n");', + ) # Bug: 3567854 and 3568241 class Beans_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - + def test_anonymous_union_name(self): - return self.assertEqual(self.cppHeader.classes["Beans"]["properties"]["public"][1]["name"], "") - + return self.assertEqual( + self.cppHeader.classes["Beans"]["properties"]["public"][1]["name"], "" + ) + def test_second_anonymous_union_name(self): - return self.assertEqual(self.cppHeader.classes["Beans"]["properties"]["public"][3]["name"], "") + return self.assertEqual( + self.cppHeader.classes["Beans"]["properties"]["public"][3]["name"], "" + ) # Bug: 3567854 and 3568241 class termite_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - + def test_termite_function(self): self.assertEqual(self.cppHeader.functions[5]["name"], "termite") - # Bug: 3569622 class Japyx_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - + def test_japyxFunc(self): self.assertEqual(self.cppHeader.functions[6]["name"], "japyxFunc") # Bug: 3570105 class Author_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - + def test_name(self): self.assertEqual(self.cppHeader.enums[0]["name"], "Author") - + def test_name(self): - self.assertEqual(self.cppHeader.enums[0]["values"], [ - {'name': 'NAME', 'value': "( 'J' << 24 | 'A' << 16 | 'S' << 8 | 'H' )"}]) + self.assertEqual( + self.cppHeader.enums[0]["values"], + [{"name": "NAME", "value": "( 'J' << 24 | 'A' << 16 | 'S' << 8 | 'H' )"}], + ) # Bug: 3577484 class Fly_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - + def test_exists(self): self.assertEqual("FruitFly" in self.cppHeader.classes, True) + # Bug BitBucket #2 class ClassAfterMagicMacro_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - + def test_class_exists(self): self.assertEqual("ClassAfterMagicMacro" in self.cppHeader.classes, True) + # Bug BitBucket #3 class FilterMagicMacro_TestCase(unittest.TestCase): - def setUp(self): savedIgnoreSymbols = CppHeaderParser.ignoreSymbols CppHeaderParser.ignoreSymbols.append("MAGIC_FUNC()") - self.cppHeader = CppHeaderParser.CppHeader(r""" + self.cppHeader = CppHeaderParser.CppHeader( + r""" class FilterMagicMacro { public: @@ -1358,332 +1964,432 @@ class FilterMagicMacro MAGIC_FUNC("1) \" var") void FilterMagicMacroMethod(int); -};""", "string") +};""", + "string", + ) CppHeaderParser.ignoreSymbols = savedIgnoreSymbols - + def test_method_exists(self): - self.assertEqual(self.cppHeader.classes["FilterMagicMacro"]["methods"]["public"][0]["name"], "FilterMagicMacroMethod") - + self.assertEqual( + self.cppHeader.classes["FilterMagicMacro"]["methods"]["public"][0]["name"], + "FilterMagicMacroMethod", + ) + def test_line_num_is_correct(self): - self.assertEqual(self.cppHeader.classes["FilterMagicMacro"]["methods"]["public"][0]["line_number"], 14); + self.assertEqual( + self.cppHeader.classes["FilterMagicMacro"]["methods"]["public"][0][ + "line_number" + ], + 14, + ) + # Bug BitBucket #4 class ClassRegularTypedefs_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - + def test_uint_exists(self): self.assertEqual("uint" in self.cppHeader.typedefs, True) - + def test_string_array_exists(self): self.assertEqual("string_array" in self.cppHeader.typedefs, True) - + def test_SmartObjPtr_exists(self): self.assertEqual("SmartObjPtr" in self.cppHeader.typedefs, True) - + def test_StrStrMap_exists(self): self.assertEqual("StrStrMap" in self.cppHeader.typedefs, True) - + def test_AfterTypedefClass_exists(self): self.assertEqual("AfterTypedefClass" in self.cppHeader.classes, True) + # Bug BitBucket #6 class LineNumAfterDivide_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - + def test_line_num(self): - self.assertEqual(self.cppHeader.classes["LineNumAfterDivide"]["methods"]["private"][1]["line_number"], 583) + self.assertEqual( + self.cppHeader.classes["LineNumAfterDivide"]["methods"]["private"][1][ + "line_number" + ], + 583, + ) + # Bug BitBucket #5 class ClassHerbCilantro_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - + def test_HerbCilantro_exists(self): self.assertEqual("Herb::Cilantro" in self.cppHeader.classes, True) + # Bug BitBucket #7 class print_statement_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - + def test_function_name_type(self): self.assertEqual(self.cppHeader.functions[7]["name"], "print_statement") - + def test_return_type(self): self.assertEqual(self.cppHeader.functions[7]["returns"], "int") + # Bug BitBucket #8 class Garlic_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - + def test_function_exists(self): - self.assertEqual(self.cppHeader.classes["Garlic"]["methods"]["public"][0]["name"], "genNum") + self.assertEqual( + self.cppHeader.classes["Garlic"]["methods"]["public"][0]["name"], "genNum" + ) + # Bug SourceForge #54 class Wheat_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - + def test_name(self): self.assertEqual(self.cppHeader.enums[1]["name"], "Wheat") - + def test_typedef(self): self.assertEqual(self.cppHeader.enums[1]["typedef"], False) + # Bug SourceForge #55 class PeachPlumb_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - + def test_Peach_exists(self): self.assertEqual("Peach" in self.cppHeader.classes, True) - + def test_Plumb_exists(self): self.assertEqual("Plumb" in self.cppHeader.classes, True) - + def test_function_exists(self): - self.assertEqual(self.cppHeader.classes["Plumb"]["methods"]["private"][0]["name"], "doSomethingGreat") + self.assertEqual( + self.cppHeader.classes["Plumb"]["methods"]["private"][0]["name"], + "doSomethingGreat", + ) + # Bug BitBucket #9 class Grape_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - + def test_Grape_exists(self): self.assertEqual("Grape" in self.cppHeader.classes, True) - + def test_a_exists(self): - self.assertEqual(self.cppHeader.classes["Grape"]["properties"]["public"][0]["name"], "a") - + self.assertEqual( + self.cppHeader.classes["Grape"]["properties"]["public"][0]["name"], "a" + ) + def test_a_type(self): - self.assertEqual(self.cppHeader.classes["Grape"]["properties"]["public"][0]["type"], "int") - + self.assertEqual( + self.cppHeader.classes["Grape"]["properties"]["public"][0]["type"], "int" + ) + def test_b_exists(self): - self.assertEqual(self.cppHeader.classes["Grape"]["properties"]["public"][1]["name"], "b") - + self.assertEqual( + self.cppHeader.classes["Grape"]["properties"]["public"][1]["name"], "b" + ) + def test_b_type(self): - self.assertEqual(self.cppHeader.classes["Grape"]["properties"]["public"][1]["type"], "int") - + self.assertEqual( + self.cppHeader.classes["Grape"]["properties"]["public"][1]["type"], "int" + ) + def test_c_exists(self): - self.assertEqual(self.cppHeader.classes["Grape"]["properties"]["public"][2]["name"], "c") - + self.assertEqual( + self.cppHeader.classes["Grape"]["properties"]["public"][2]["name"], "c" + ) + def test_d_exists(self): - self.assertEqual(self.cppHeader.classes["Grape"]["properties"]["public"][3]["name"], "d") - + self.assertEqual( + self.cppHeader.classes["Grape"]["properties"]["public"][3]["name"], "d" + ) + def test_e_exists(self): - self.assertEqual(self.cppHeader.classes["Grape"]["properties"]["public"][4]["name"], "e") - + self.assertEqual( + self.cppHeader.classes["Grape"]["properties"]["public"][4]["name"], "e" + ) + def test_f_exists(self): - self.assertEqual(self.cppHeader.classes["Grape"]["properties"]["public"][5]["name"], "f") + self.assertEqual( + self.cppHeader.classes["Grape"]["properties"]["public"][5]["name"], "f" + ) + # Bug BitBucket #14 class Avacado_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - + def test_Avacado_exists(self): self.assertEqual("Avacado" in self.cppHeader.classes, True) - + def test_foo_return_type(self): - self.assertEqual(self.cppHeader.classes["Avacado"]["methods"]["public"][0]["returns"], "uint8_t") - + self.assertEqual( + self.cppHeader.classes["Avacado"]["methods"]["public"][0]["returns"], + "uint8_t", + ) + def test_bar_return_type(self): - self.assertEqual(self.cppHeader.classes["Avacado"]["methods"]["public"][1]["returns"], "::uint8_t") + self.assertEqual( + self.cppHeader.classes["Avacado"]["methods"]["public"][1]["returns"], + "::uint8_t", + ) + # Bug BitBucket #13 class Raspberry_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - + def test_anon_struct_1_exists(self): self.assertEqual("" in self.cppHeader.classes, True) - + def test_beta_exists(self): - self.assertEqual(self.cppHeader.classes[""]["properties"]["public"][0]["name"], "anon_struct_variable") - + self.assertEqual( + self.cppHeader.classes[""]["properties"]["public"][0][ + "name" + ], + "anon_struct_variable", + ) + def test_Raspberry_exists(self): self.assertEqual("Raspberry" in self.cppHeader.classes, True) - + def test_a_exists(self): - self.assertEqual(self.cppHeader.classes["Raspberry"]["properties"]["public"][0]["name"], "a") + self.assertEqual( + self.cppHeader.classes["Raspberry"]["properties"]["public"][0]["name"], "a" + ) + # Bug BitBucket #15 & 16 class Hen_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - + def test_default_a(self): - self.assertEqual(self.cppHeader.classes["Hen"]["methods"]["public"][0]["parameters"][0]["defaultValue"], "100") - + self.assertEqual( + self.cppHeader.classes["Hen"]["methods"]["public"][0]["parameters"][0][ + "defaultValue" + ], + "100", + ) + def test_default_b(self): - self.assertEqual(self.cppHeader.classes["Hen"]["methods"]["public"][0]["parameters"][1]["defaultValue"], "0xfd") - + self.assertEqual( + self.cppHeader.classes["Hen"]["methods"]["public"][0]["parameters"][1][ + "defaultValue" + ], + "0xfd", + ) + def test_default_c(self): - self.assertEqual(self.cppHeader.classes["Hen"]["methods"]["public"][0]["parameters"][2]["defaultValue"], "1.7e-3") - + self.assertEqual( + self.cppHeader.classes["Hen"]["methods"]["public"][0]["parameters"][2][ + "defaultValue" + ], + "1.7e-3", + ) + def test_default_d(self): - self.assertEqual(self.cppHeader.classes["Hen"]["methods"]["public"][0]["parameters"][3]["defaultValue"], "3.14") - + self.assertEqual( + self.cppHeader.classes["Hen"]["methods"]["public"][0]["parameters"][3][ + "defaultValue" + ], + "3.14", + ) + def test_default_s1(self): - self.assertEqual(self.cppHeader.classes["Hen"]["methods"]["public"][1]["parameters"][0]["defaultValue"], '""') - + self.assertEqual( + self.cppHeader.classes["Hen"]["methods"]["public"][1]["parameters"][0][ + "defaultValue" + ], + '""', + ) + def test_default_s2(self): - self.assertEqual(self.cppHeader.classes["Hen"]["methods"]["public"][1]["parameters"][1]["defaultValue"], '"nothing"') + self.assertEqual( + self.cppHeader.classes["Hen"]["methods"]["public"][1]["parameters"][1][ + "defaultValue" + ], + '"nothing"', + ) # Bug BitBucket #19 class Raddish_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - + def test_Avacado_exists(self): - self.assertEqual(self.cppHeader.classes["Raddish_SetIterator"]["properties"]["protected"][0]["name"], "_beg") - + self.assertEqual( + self.cppHeader.classes["Raddish_SetIterator"]["properties"]["protected"][0][ + "name" + ], + "_beg", + ) + def test_class_template(self): - template_str = \ - "template >" - self.assertEqual(self.cppHeader.classes["Raddish_SetIterator"]["template"], template_str) + template_str = ( + "template >" + ) + self.assertEqual( + self.cppHeader.classes["Raddish_SetIterator"]["template"], template_str + ) # Bug bug 57 class Carambola_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - + def test_name(self): self.assertEqual(self.cppHeader.enums[2]["name"], "Carambola") - + def test_values(self): - self.assertEqual(self.cppHeader.enums[2]["values"], [ - {'name': 'StarFruit', 'value': '( 2 + 2 ) / 2'}]) - + self.assertEqual( + self.cppHeader.enums[2]["values"], + [{"name": "StarFruit", "value": "( 2 + 2 ) / 2"}], + ) + def test_typedef(self): self.assertEqual(self.cppHeader.enums[2]["typedef"], True) - + # globals class Globals_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - + def test_externVar_name(self): self.assertEqual(self.cppHeader.variables[2]["name"], "externVar") - + def test_externVar_extern(self): self.assertEqual(self.cppHeader.variables[2]["extern"], 1) - + def test_globalVar_name(self): self.assertEqual(self.cppHeader.variables[3]["name"], "globalVar") - + def test_globalVar_extern(self): self.assertEqual(self.cppHeader.variables[3]["extern"], 0) + # globals class TypedefArray_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - + def test_name(self): self.assertEqual("TenCharArray[10]" in self.cppHeader.typedefs, True) - + def test_value(self): self.assertEqual(self.cppHeader.typedefs["TenCharArray[10]"], "char") + # typedef structs class TypedefStruct_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - + def test_name(self): self.assertEqual("MAGIC_FILE" in self.cppHeader.typedefs, True) - + def test_value(self): - self.assertEqual(self.cppHeader.typedefs["MAGIC_FILE"], "struct SUPER_MAGIC_FILE") - - + self.assertEqual( + self.cppHeader.typedefs["MAGIC_FILE"], "struct SUPER_MAGIC_FILE" + ) + + # Bug SourceForge #10 class Picture_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - + def test_array_size(self): - self.assertEqual(self.cppHeader.classes["Picture"]["properties"]["public"][1]["array_size"], 16384) - + self.assertEqual( + self.cppHeader.classes["Picture"]["properties"]["public"][1]["array_size"], + 16384, + ) + def test_multi_dimensional_array_size(self): - self.assertEqual(self.cppHeader.classes["Picture"]["properties"]["public"][1]["multi_dimensional_array_size"], "128x128") - + self.assertEqual( + self.cppHeader.classes["Picture"]["properties"]["public"][1][ + "multi_dimensional_array_size" + ], + "128x128", + ) # SourceForge bug 58 class Apricot_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - + def test_Apricot_exists(self): self.assertEqual("Apricot" in self.cppHeader.classes, True) - + def test_i_exists(self): self.assertEqual(self.cppHeader.classes["Apricot"]["members"][0]["name"], "i") - + def test_f_exists(self): self.assertEqual(self.cppHeader.classes["Apricot"]["members"][1]["name"], "f") - + def test_s_exists(self): self.assertEqual(self.cppHeader.classes["Apricot"]["members"][2]["name"], "s") # SourceForge bug 59 class LemonLime_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - + def test_lemon_not_final(self): self.assertEqual(self.cppHeader.classes["Lemon"]["final"], False) - + def test_lime_final(self): self.assertEqual(self.cppHeader.classes["Lime"]["final"], True) - + def test_lemon_foo_is_final(self): - self.assertEqual(self.cppHeader.classes["Lemon"]["methods"]["public"][0]["final"], True) - + self.assertEqual( + self.cppHeader.classes["Lemon"]["methods"]["public"][0]["final"], True + ) + def test_lemon_foo2_is_not_final(self): - self.assertEqual(self.cppHeader.classes["Lemon"]["methods"]["public"][1]["final"], False) - + self.assertEqual( + self.cppHeader.classes["Lemon"]["methods"]["public"][1]["final"], False + ) + def test_lime_abc_is_not_override(self): - self.assertEqual(self.cppHeader.classes["Lime"]["methods"]["public"][0]["override"], False) - + self.assertEqual( + self.cppHeader.classes["Lime"]["methods"]["public"][0]["override"], False + ) + def test_lime_foo2_is_not_override(self): - self.assertEqual(self.cppHeader.classes["Lime"]["methods"]["public"][1]["override"], True) + self.assertEqual( + self.cppHeader.classes["Lime"]["methods"]["public"][1]["override"], True + ) class JSON_TestCase(unittest.TestCase): - def setUp(self): - self.cppHeader = CppHeaderParser.CppHeader(r""" + self.cppHeader = CppHeaderParser.CppHeader( + r""" struct Lemon { virtual void foo() final; @@ -1694,118 +2400,158 @@ def setUp(self): { void abc(); void foo2() override; -};""", "string") +};""", + "string", + ) self.jsonString = self.cppHeader.toJSON() - + def test_hasLemon(self): hasString = ' "Lemon": {' in self.jsonString self.assertEqual(hasString, True) - + def test_can_parse_complex_file(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") j = self.cppHeader.toJSON() + # BitBucket bug 24 class Mouse_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - + def test_MouseClass_exists(self): self.assertEqual("MouseClass" in self.cppHeader.classes, True) - + def test_mouse_typedef_correct_value(self): - self.assertEqual(self.cppHeader.classes["MouseClass"]["methods"]["public"][0]["parameters"][0]['raw_type'], - "MouseNS::MouseClass::mouse_typedef") + self.assertEqual( + self.cppHeader.classes["MouseClass"]["methods"]["public"][0]["parameters"][ + 0 + ]["raw_type"], + "MouseNS::MouseClass::mouse_typedef", + ) + # BitBucket bug 26 class Fig_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - + def test_Fig_exists(self): self.assertEqual("Fig" in self.cppHeader.classes, True) - + def test_a_exists(self): - self.assertEqual(self.cppHeader.classes["Grape"]["properties"]["public"][0]["name"], "a") + self.assertEqual( + self.cppHeader.classes["Grape"]["properties"]["public"][0]["name"], "a" + ) + # BitBucket bug 27 class Olive_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - + def test_Olive_exists(self): self.assertEqual("union olive" in self.cppHeader.classes, True) - + def test_union_member_x(self): - cmp_values = {'constant': 0, 'name': 'x', 'reference': 0, 'type': 'int', 'static': 0, 'pointer': 0} - self.assertEqual(filter_dict_keys(self.cppHeader.classes["union olive"]["members"][0], cmp_values.keys()), cmp_values) + cmp_values = { + "constant": 0, + "name": "x", + "reference": 0, + "type": "int", + "static": 0, + "pointer": 0, + } + self.assertEqual( + filter_dict_keys( + self.cppHeader.classes["union olive"]["members"][0], cmp_values.keys() + ), + cmp_values, + ) + # BitBucket bug 61 class Beet_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - + def test_Beet_exists(self): self.assertEqual("BeetStruct" in self.cppHeader.classes, True) - + def test_BeetEnum_exists(self): - self.assertEqual(self.cppHeader.classes["BeetStruct"]["enums"]["public"][0]["name"], "BeetEnum") + self.assertEqual( + self.cppHeader.classes["BeetStruct"]["enums"]["public"][0]["name"], + "BeetEnum", + ) + # BitBucket bug 40 class set_callback_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - + def test_set_callback(self): self.assertEqual(self.cppHeader.functions[8]["name"], "set_callback") - self.assertEqual(self.cppHeader.functions[8]["parameters"][1]["name"], "callback") - self.assertEqual(self.cppHeader.functions[8]["parameters"][1]["function_pointer"], 1) - self.assertEqual(self.cppHeader.functions[8]["parameters"][1]["type"], "long ( * ) ( struct test_st *, int, const char *, int long, long, long )") + self.assertEqual( + self.cppHeader.functions[8]["parameters"][1]["name"], "callback" + ) + self.assertEqual( + self.cppHeader.functions[8]["parameters"][1]["function_pointer"], 1 + ) + self.assertEqual( + self.cppHeader.functions[8]["parameters"][1]["type"], + "long ( * ) ( struct test_st *, int, const char *, int long, long, long )", + ) + # BitBucket bug 45 class HALControlWord_TestCase(unittest.TestCase): - def setUp(self): - self.cppHeader = CppHeaderParser.CppHeader("""\ + self.cppHeader = CppHeaderParser.CppHeader( + """\ struct HAL_ControlWord { int x : 1; int y : 1; }; typedef struct HAL_ControlWord HAL_ControlWord; int HAL_GetControlWord(HAL_ControlWord* controlWord); - """, "string") - + """, + "string", + ) + def test_functions(self): self.assertEqual(len(self.cppHeader.functions), 1) self.assertEqual(self.cppHeader.functions[0]["name"], "HAL_GetControlWord") - + def test_classes(self): self.assertEqual(len(self.cppHeader.classes), 1) - self.assertEqual(self.cppHeader.classes["HAL_ControlWord"]["name"], "HAL_ControlWord") + self.assertEqual( + self.cppHeader.classes["HAL_ControlWord"]["name"], "HAL_ControlWord" + ) def test_num_typedefs(self): self.assertEqual(len(self.cppHeader.typedefs), 1) - self.assertEqual(self.cppHeader.typedefs["HAL_ControlWord"], "struct HAL_ControlWord") + self.assertEqual( + self.cppHeader.typedefs["HAL_ControlWord"], "struct HAL_ControlWord" + ) + # Bitbucket bug 47 class CommentEOF_TestCase(unittest.TestCase): - def setUp(self): - self.cppHeader = CppHeaderParser.CppHeader(""" + self.cppHeader = CppHeaderParser.CppHeader( + """ namespace a { -} // namespace a""", "string") +} // namespace a""", + "string", + ) def test_comment(self): - self.assertTrue('a' in self.cppHeader.namespaces) + self.assertTrue("a" in self.cppHeader.namespaces) + # BitBucket bug 35 class Grackle_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") @@ -1813,79 +2559,205 @@ def test_Grackle_exists(self): self.assertEqual("Grackle" in self.cppHeader.classes, True) def test_Grackle_no_noexcept_None(self): - self.assertEqual(self.cppHeader.classes["Grackle"]["methods"]["public"][0]["noexcept"], None) + self.assertEqual( + self.cppHeader.classes["Grackle"]["methods"]["public"][0]["noexcept"], None + ) def test_Grackle_noexcept(self): - self.assertEqual(self.cppHeader.classes["Grackle"]["methods"]["public"][1]["noexcept"], 'noexcept') + self.assertEqual( + self.cppHeader.classes["Grackle"]["methods"]["public"][1]["noexcept"], + "noexcept", + ) def test_Grackle_const_noexcept(self): - self.assertEqual(self.cppHeader.classes["Grackle"]["methods"]["public"][2]["const"], True) - self.assertEqual(self.cppHeader.classes["Grackle"]["methods"]["public"][2]["noexcept"], 'noexcept') + self.assertEqual( + self.cppHeader.classes["Grackle"]["methods"]["public"][2]["const"], True + ) + self.assertEqual( + self.cppHeader.classes["Grackle"]["methods"]["public"][2]["noexcept"], + "noexcept", + ) def test_Grackle_noexcept_true(self): - self.assertEqual(self.cppHeader.classes["Grackle"]["methods"]["public"][3]["noexcept"], 'noexcept(true)') + self.assertEqual( + self.cppHeader.classes["Grackle"]["methods"]["public"][3]["noexcept"], + "noexcept(true)", + ) def test_Grackle_const_noexcept_true(self): - self.assertEqual(self.cppHeader.classes["Grackle"]["methods"]["public"][4]["const"], True) - self.assertEqual(self.cppHeader.classes["Grackle"]["methods"]["public"][4]["noexcept"], 'noexcept(true)') + self.assertEqual( + self.cppHeader.classes["Grackle"]["methods"]["public"][4]["const"], True + ) + self.assertEqual( + self.cppHeader.classes["Grackle"]["methods"]["public"][4]["noexcept"], + "noexcept(true)", + ) def test_Grackle_noexcept_noexcept_operator(self): - self.assertEqual(self.cppHeader.classes["Grackle"]["methods"]["public"][5]["noexcept"], 'noexcept(noexcept(Grackle()))') + self.assertEqual( + self.cppHeader.classes["Grackle"]["methods"]["public"][5]["noexcept"], + "noexcept(noexcept(Grackle()))", + ) def test_Grackle_const_noexcept_noexcept_operator(self): - self.assertEqual(self.cppHeader.classes["Grackle"]["methods"]["public"][6]["const"], True) - self.assertEqual(self.cppHeader.classes["Grackle"]["methods"]["public"][6]["noexcept"], 'noexcept(noexcept(Grackle()))') + self.assertEqual( + self.cppHeader.classes["Grackle"]["methods"]["public"][6]["const"], True + ) + self.assertEqual( + self.cppHeader.classes["Grackle"]["methods"]["public"][6]["noexcept"], + "noexcept(noexcept(Grackle()))", + ) + # Test enhancement 13 (default constructor / destructor) -class DefaultConstDest_TestCase(): +class DefaultConstDest_TestCase: def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_DefaultConstDest_exists(self): self.assertEqual("DefaultConstDest" in self.cppHeader.classes, True) self.assertEqual("default_class_tricky" in self.cppHeader.classes, True) - + def test_DefaultConstDest_constructor_default(self): - self.assertEqual(self.cppHeader.classes["DefaultConstDest"]["methods"]["public"][0]["constructor"], True) - self.assertEqual(self.cppHeader.classes["DefaultConstDest"]["methods"]["public"][0]["default"], True) - self.assertEqual(self.cppHeader.classes["DefaultConstDest"]["methods"]["public"][0]["defined"], True) + self.assertEqual( + self.cppHeader.classes["DefaultConstDest"]["methods"]["public"][0][ + "constructor" + ], + True, + ) + self.assertEqual( + self.cppHeader.classes["DefaultConstDest"]["methods"]["public"][0][ + "default" + ], + True, + ) + self.assertEqual( + self.cppHeader.classes["DefaultConstDest"]["methods"]["public"][0][ + "defined" + ], + True, + ) def test_DefaultConstDest_destructor_default(self): - self.assertEqual(self.cppHeader.classes["DefaultConstDest"]["methods"]["public"][1]["destructor"], True) - self.assertEqual(self.cppHeader.classes["DefaultConstDest"]["methods"]["public"][1]["default"], True) - self.assertEqual(self.cppHeader.classes["DefaultConstDest"]["methods"]["public"][1]["defined"], True) + self.assertEqual( + self.cppHeader.classes["DefaultConstDest"]["methods"]["public"][1][ + "destructor" + ], + True, + ) + self.assertEqual( + self.cppHeader.classes["DefaultConstDest"]["methods"]["public"][1][ + "default" + ], + True, + ) + self.assertEqual( + self.cppHeader.classes["DefaultConstDest"]["methods"]["public"][1][ + "defined" + ], + True, + ) def test_DefaultConstDest_default_edgeCaseNaming(self): - self.assertEqual(self.cppHeader.classes["default_class_tricky"]["methods"]["public"][0]["constructor"], True) - self.assertEqual(self.cppHeader.classes["default_class_tricky"]["methods"]["public"][0]["default"], False) - self.assertEqual(self.cppHeader.classes["default_class_tricky"]["methods"]["public"][0]["defined"], False) - self.assertEqual(self.cppHeader.classes["default_class_tricky"]["methods"]["public"][1]["destructor"], True) - self.assertEqual(self.cppHeader.classes["default_class_tricky"]["methods"]["public"][1]["default"], False) - self.assertEqual(self.cppHeader.classes["default_class_tricky"]["methods"]["public"][1]["defined"], False) - self.assertEqual(self.cppHeader.classes["default_class_tricky"]["methods"]["public"][2]["name"], "randomMethod1_default") - self.assertEqual(self.cppHeader.classes["default_class_tricky"]["methods"]["public"][2]["destructor"], False) - self.assertEqual(self.cppHeader.classes["default_class_tricky"]["methods"]["public"][2]["default"], False) - self.assertEqual(self.cppHeader.classes["default_class_tricky"]["methods"]["public"][2]["defined"], False) - self.assertEqual(self.cppHeader.classes["default_class_tricky"]["methods"]["public"][3]["name"], "defaultrandomMethod2") - self.assertEqual(self.cppHeader.classes["default_class_tricky"]["methods"]["public"][3]["destructor"], False) - self.assertEqual(self.cppHeader.classes["default_class_tricky"]["methods"]["public"][3]["default"], False) - self.assertEqual(self.cppHeader.classes["default_class_tricky"]["methods"]["public"][3]["defined"], False) + self.assertEqual( + self.cppHeader.classes["default_class_tricky"]["methods"]["public"][0][ + "constructor" + ], + True, + ) + self.assertEqual( + self.cppHeader.classes["default_class_tricky"]["methods"]["public"][0][ + "default" + ], + False, + ) + self.assertEqual( + self.cppHeader.classes["default_class_tricky"]["methods"]["public"][0][ + "defined" + ], + False, + ) + self.assertEqual( + self.cppHeader.classes["default_class_tricky"]["methods"]["public"][1][ + "destructor" + ], + True, + ) + self.assertEqual( + self.cppHeader.classes["default_class_tricky"]["methods"]["public"][1][ + "default" + ], + False, + ) + self.assertEqual( + self.cppHeader.classes["default_class_tricky"]["methods"]["public"][1][ + "defined" + ], + False, + ) + self.assertEqual( + self.cppHeader.classes["default_class_tricky"]["methods"]["public"][2][ + "name" + ], + "randomMethod1_default", + ) + self.assertEqual( + self.cppHeader.classes["default_class_tricky"]["methods"]["public"][2][ + "destructor" + ], + False, + ) + self.assertEqual( + self.cppHeader.classes["default_class_tricky"]["methods"]["public"][2][ + "default" + ], + False, + ) + self.assertEqual( + self.cppHeader.classes["default_class_tricky"]["methods"]["public"][2][ + "defined" + ], + False, + ) + self.assertEqual( + self.cppHeader.classes["default_class_tricky"]["methods"]["public"][3][ + "name" + ], + "defaultrandomMethod2", + ) + self.assertEqual( + self.cppHeader.classes["default_class_tricky"]["methods"]["public"][3][ + "destructor" + ], + False, + ) + self.assertEqual( + self.cppHeader.classes["default_class_tricky"]["methods"]["public"][3][ + "default" + ], + False, + ) + self.assertEqual( + self.cppHeader.classes["default_class_tricky"]["methods"]["public"][3][ + "defined" + ], + False, + ) class VarargFunc_TestCase(unittest.TestCase): - def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_vararg_func(self): - vf = next(x for x in self.cppHeader.functions if x['name'] == 'vararg_func') - nvf = next(x for x in self.cppHeader.functions if x['name'] == 'non_vararg_func') - self.assertTrue(vf['vararg']) - self.assertFalse(nvf['vararg']) - self.assertEqual(len(vf['parameters']), len(nvf['parameters'])) + vf = next(x for x in self.cppHeader.functions if x["name"] == "vararg_func") + nvf = next( + x for x in self.cppHeader.functions if x["name"] == "non_vararg_func" + ) + self.assertTrue(vf["vararg"]) + self.assertFalse(nvf["vararg"]) + self.assertEqual(len(vf["parameters"]), len(nvf["parameters"])) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() - - diff --git a/doc_generator.py b/doc_generator.py deleted file mode 100644 index 2cfd7d2..0000000 --- a/doc_generator.py +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/python -# Generate documenation -# * README.txt -# * README.html -import sys - -def gen_readme_html(): - """generate README.html""" - import cgi - f = open("templates/README.html").read() - sampleClass = open("CppHeaderParser/examples/SampleClass.h").read() - readSampleClass = open("CppHeaderParser/examples/readSampleClass.py").read() - - f = f.replace("{SAMPLE_CLASS_H}", cgi.escape(sampleClass)) - f = f.replace("{READ_SAMPLE_CLASS_PY}", cgi.escape(readSampleClass)) - f = f.replace("{READ_SAMPLE_CLASS_PY_OUTPUT}", cgi.escape(get_sample_class_output())) - open("README.html", "wa").write(f) - - -def gen_readme_txt(): - """generate README.txt""" - import cgi - f = open("templates/README.txt").read() - sampleClass = open("CppHeaderParser/examples/SampleClass.h").read() - readSampleClass = open("CppHeaderParser/examples/readSampleClass.py").read() - - f = f.replace("{SAMPLE_CLASS_H}", " " + sampleClass.replace("\n", "\n ")) - f = f.replace("{READ_SAMPLE_CLASS_PY}", " " + readSampleClass.replace("\n", "\n ")) - f = f.replace("{READ_SAMPLE_CLASS_PY_OUTPUT}", " " + get_sample_class_output().replace("\n", "\n ")) - open("README.txt", "wa").write(f) - print "wrote README.txt" - - import docutils.core - h = docutils.core.publish_string(source=open("README.txt").read(), writer_name='html') - h = h.replace("", "/*customization*/\npre.literal-block{\ncolor: #6A6A6A;\n}\n\n") - h = h.replace('
', '
')
-    open("README.html", "wa").write(h)
-    print "wrote README.html"
-
-
-def get_sample_class_output():
-    import subprocess
-    return subprocess.Popen(["python", "readSampleClass.py"],
-        stdout=subprocess.PIPE,
-        cwd="CppHeaderParser/examples"
-        ).communicate()[0]
-
-
-
-
-if __name__ == "__main__":
-    gen_readme_txt()
diff --git a/setup.py b/setup.py
index 87046f4..620986e 100644
--- a/setup.py
+++ b/setup.py
@@ -39,42 +39,43 @@
     exec(fp.read(), globals())
 
 DESCRIPTION = (
-    'Parse C++ header files and generate a data structure '
-    'representing the class'
-    )
+    "Parse C++ header files and generate a data structure " "representing the class"
+)
 
 
 CLASSIFIERS = [
-    'Operating System :: OS Independent',
-    'Programming Language :: Python',
-    'Programming Language :: Python :: 2',
-    'Programming Language :: Python :: 3',
-    'Programming Language :: C++',
-    'License :: OSI Approved :: BSD License',
-    'Development Status :: 5 - Production/Stable',
-    'Intended Audience :: Developers',
-    'Topic :: Software Development',
-    'Topic :: Software Development :: Code Generators',
-    'Topic :: Software Development :: Compilers',
-    'Topic :: Software Development :: Disassemblers'
-    ]
+    "Operating System :: OS Independent",
+    "Programming Language :: Python",
+    "Programming Language :: Python :: 2",
+    "Programming Language :: Python :: 3",
+    "Programming Language :: C++",
+    "License :: OSI Approved :: BSD License",
+    "Development Status :: 5 - Production/Stable",
+    "Intended Audience :: Developers",
+    "Topic :: Software Development",
+    "Topic :: Software Development :: Code Generators",
+    "Topic :: Software Development :: Compilers",
+    "Topic :: Software Development :: Disassemblers",
+]
 
 setup(
-    name = 'robotpy-cppheaderparser',
-    version = __version__,
-    author = 'Jashua Cloutier',
-    author_email = 'jashuac@bellsouth.net',
-    maintainer = 'RobotPy Development Team',
-    maintainer_email = 'robotpy@googlegroups.com',
-    url = 'https://github.com/robotpy/robotpy-cppheaderparser',
-    description = DESCRIPTION,
-    long_description = open('README.md').read(),
-    license = 'BSD',
-    platforms = 'Platform Independent',
-    packages = ['CppHeaderParser'],
-    keywords = 'c++ header parser ply',
-    classifiers = CLASSIFIERS,
-    requires = ['ply'],
-    install_requires=['ply'],
-    package_data = { 'CppHeaderParser': ['README', 'README.html', 'doc/*.*', 'examples/*.*'], },
-    )
+    name="robotpy-cppheaderparser",
+    version=__version__,
+    author="Jashua Cloutier",
+    author_email="jashuac@bellsouth.net",
+    maintainer="RobotPy Development Team",
+    maintainer_email="robotpy@googlegroups.com",
+    url="https://github.com/robotpy/robotpy-cppheaderparser",
+    description=DESCRIPTION,
+    long_description=open("README.md").read(),
+    license="BSD",
+    platforms="Platform Independent",
+    packages=["CppHeaderParser"],
+    keywords="c++ header parser ply",
+    classifiers=CLASSIFIERS,
+    requires=["ply"],
+    install_requires=["ply"],
+    package_data={
+        "CppHeaderParser": ["README", "README.html", "doc/*.*", "examples/*.*"]
+    },
+)

From d19d93b77b9316e9512bb47415e16f4507258ee4 Mon Sep 17 00:00:00 2001
From: Dustin Spicuzza 
Date: Wed, 25 Sep 2019 00:37:32 -0400
Subject: [PATCH 021/143] Prefix internal methods with _ to make docs easier to
 grok

---
 CppHeaderParser/CppHeaderParser.py | 50 +++++++++++++++---------------
 1 file changed, 25 insertions(+), 25 deletions(-)

diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py
index 9fdae7b..a05cad3 100644
--- a/CppHeaderParser/CppHeaderParser.py
+++ b/CppHeaderParser/CppHeaderParser.py
@@ -2049,7 +2049,7 @@ def finalize(self):
                             cls["abstract"] = True
                             break
 
-    def evaluate_struct_stack(self):
+    def _evaluate_struct_stack(self):
         """Create a Struct out of the name stack (but not its parts)"""
         # print( 'eval struct stack', self.nameStack )
         # if self.braceDepth != len(self.nameSpaces): return
@@ -2209,7 +2209,7 @@ def parse_method_type(self, stack):
         info["returns_fundamental"] = is_fundamental(info["returns"])
         return info
 
-    def evaluate_method_stack(self):
+    def _evaluate_method_stack(self):
         """Create a method out of the name stack"""
 
         if self.curStruct:
@@ -2302,7 +2302,7 @@ def _parse_typedef(self, stack, namespace=""):
         if s:
             return r
 
-    def evaluate_typedef(self):
+    def _evaluate_typedef(self):
         ns = self.cur_namespace(add_double_colon=True)
         res = self._parse_typedef(self.stack, ns)
         if res:
@@ -2311,7 +2311,7 @@ def evaluate_typedef(self):
             if name not in self.typedefs_order:
                 self.typedefs_order.append(name)
 
-    def evaluate_property_stack(self):
+    def _evaluate_property_stack(self):
         """Create a Property out of the name stack"""
         global parseHistory
         assert self.stack[-1] == ";"
@@ -2367,7 +2367,7 @@ def evaluate_property_stack(self):
                         self.stack = orig_stack[
                             :
                         ]  # Not maintained for mucking, but this path it doesnt matter
-                        self.evaluate_property_stack()
+                        self._evaluate_property_stack()
                     return
 
             newVar = CppVariable(self.nameStack)
@@ -2389,7 +2389,7 @@ def evaluate_property_stack(self):
 
         self.stack = []  # CLEAR STACK
 
-    def evaluate_class_stack(self):
+    def _evaluate_class_stack(self):
         """Create a Class out of the name stack (but not its parts)"""
         # dont support sub classes today
         # print( 'eval class stack', self.nameStack )
@@ -2737,7 +2737,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs):
                             self.nameStack = self.nameStack[:classLocationNS]
                             self.stack = self.stack[:classLocationS]
                             try:
-                                self.evaluate_stack()
+                                self._evaluate_stack()
                             except:
                                 debug_print("Error processing #define magic... Oh well")
                             # Process rest of stack
@@ -2745,7 +2745,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs):
                             self.stack = origStack[classLocationS:]
 
                     if len(self.nameStack) and not is_enum_namestack(self.nameStack):
-                        self.evaluate_stack()
+                        self._evaluate_stack()
                     else:
                         self.nameStack.append(tok.value)
                     if self.stack and self.stack[0] == "class":
@@ -2761,7 +2761,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs):
                     if len(self.nameStack) and is_enum_namestack(self.nameStack):
                         self.nameStack.append(tok.value)
                     elif self.braceDepth < 10:
-                        self.evaluate_stack()
+                        self._evaluate_stack()
                     else:
                         self.nameStack = []
                     self.braceDepth -= 1
@@ -2906,14 +2906,14 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs):
                         self.stack = self.nameStack + [";"]
                         self.nameStack = self.nameStack[0:1]
                         debug_print("pre eval anon stack")
-                        self.evaluate_stack(tok.type)
+                        self._evaluate_stack(tok.type)
                         debug_print("post eval anon stack")
                         self.nameStack = saved_namestack
                         self.stack = saved_stack
                         self.anon_union_counter = [-1, 0]
 
                     if self.braceDepth < 10:
-                        self.evaluate_stack(tok.type)
+                        self._evaluate_stack(tok.type)
                     self.stack = []
                     self.nameStack = []
 
@@ -2954,7 +2954,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs):
         ]:
             del self.__dict__[key]
 
-    def evaluate_stack(self, token=None):
+    def _evaluate_stack(self, token=None):
         """Evaluates the current name stack"""
         global doxygenCommentCache
 
@@ -2992,7 +2992,7 @@ def evaluate_stack(self, token=None):
         except:
             pass
 
-        # if 'typedef' in self.nameStack: self.evaluate_typedef()        # allows nested typedefs, probably a bad idea
+        # if 'typedef' in self.nameStack: self._evaluate_typedef()        # allows nested typedefs, probably a bad idea
         if (
             not self.curClass
             and "typedef" in self.nameStack
@@ -3003,7 +3003,7 @@ def evaluate_stack(self, token=None):
             and not is_enum_namestack(self.nameStack)
         ):
             trace_print("STACK", self.stack)
-            self.evaluate_typedef()
+            self._evaluate_typedef()
             return
 
         elif len(self.nameStack) == 0:
@@ -3026,7 +3026,7 @@ def evaluate_stack(self, token=None):
 
         elif is_enum_namestack(self.nameStack):
             debug_print("trace")
-            self.evaluate_enum_stack()
+            self._evaluate_enum_stack()
 
         elif self._method_body and (self.braceDepth + 1) > self._method_body:
             trace_print("INSIDE METHOD DEF")
@@ -3046,10 +3046,10 @@ def evaluate_stack(self, token=None):
                     # Special case of a method defined outside a class that has a body
                     pass
                 else:
-                    self.evaluate_method_stack()
+                    self._evaluate_method_stack()
             else:
                 # Free function
-                self.evaluate_method_stack()
+                self._evaluate_method_stack()
         elif (
             len(self.nameStack) == 1
             and len(self.nameStackHistory) > self.braceDepth
@@ -3078,7 +3078,7 @@ def evaluate_stack(self, token=None):
             ):
                 pass
             else:
-                self.evaluate_property_stack()  # catches class props and structs in a namespace
+                self._evaluate_property_stack()  # catches class props and structs in a namespace
 
         elif (
             self.nameStack[0] in ("class", "struct", "union")
@@ -3087,14 +3087,14 @@ def evaluate_stack(self, token=None):
         ):
             # Parsing a union can reuse much of the class parsing
             debug_print("trace")
-            self.evaluate_class_stack()
+            self._evaluate_class_stack()
 
         elif not self.curClass:
             debug_print("trace")
             if is_enum_namestack(self.nameStack):
-                self.evaluate_enum_stack()
+                self._evaluate_enum_stack()
             elif self.curStruct and self.stack[-1] == ";":
-                self.evaluate_property_stack()  # this catches fields of global structs
+                self._evaluate_property_stack()  # this catches fields of global structs
             self.nameStack = []
             doxygenCommentCache = ""
         elif self.braceDepth < 1:
@@ -3118,7 +3118,7 @@ def evaluate_stack(self, token=None):
         doxygenCommentCache = ""
         self.curTemplate = None
 
-    def evaluate_enum_stack(self):
+    def _evaluate_enum_stack(self):
         """Create an Enum out of the name stack"""
         debug_print("evaluating enum")
         newEnum = CppEnum(self.nameStack)
@@ -3142,10 +3142,10 @@ def evaluate_enum_stack(self):
                     instanceType = newEnum["name"]
                 for instance in newEnum["instances"]:
                     self.nameStack = [instanceType, instance]
-                    self.evaluate_property_stack()
+                    self._evaluate_property_stack()
                 del newEnum["instances"]
 
-    def strip_parent_keys(self):
+    def _strip_parent_keys(self):
         """Strip all parent (and method) keys to prevent loops"""
         obj_queue = [self]
         while len(obj_queue):
@@ -3195,7 +3195,7 @@ def toJSON(self, indent=4):
         """Converts a parsed structure to JSON"""
         import json
 
-        self.strip_parent_keys()
+        self._strip_parent_keys()
         try:
             del self.__dict__["classes_order"]
         except:

From e5489eedca3c3315f1bd2a36720532b4492cf1b0 Mon Sep 17 00:00:00 2001
From: Dustin Spicuzza 
Date: Wed, 25 Sep 2019 00:43:55 -0400
Subject: [PATCH 022/143] Fix invalid string escapes

---
 CppHeaderParser/CppHeaderParser.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py
index a05cad3..3caff5b 100644
--- a/CppHeaderParser/CppHeaderParser.py
+++ b/CppHeaderParser/CppHeaderParser.py
@@ -165,7 +165,7 @@ def t_COMMENT_MULTILINE(t):
         # not sure why, but get double new lines
         v = t.value.replace("\n\n", "\n")
         # strip prefixing whitespace
-        v = re.sub("\n[\s]+\*", "\n*", v)
+        v = re.sub("\n[\\s]+\\*", "\n*", v)
         doxygenCommentCache += v
     t.lexer.lineno += len([a for a in t.value if a == "\n"])
 
@@ -997,7 +997,7 @@ def __init__(self, nameStack, curClass, methinfo, curTemplate):
             doxyLines = self["doxygen"].split("\n")
             lastParamDesc = ""
             for doxyLine in doxyLines:
-                if " @param " in doxyLine or " \param " in doxyLine:
+                if " @param " in doxyLine or " \\param " in doxyLine:
                     try:
                         # Strip out the param
                         doxyLine = doxyLine[doxyLine.find("param ") + 6 :]

From eeb78f5bdf10232171091e7fcb07860d40aa9ed6 Mon Sep 17 00:00:00 2001
From: Dustin Spicuzza 
Date: Wed, 25 Sep 2019 01:29:23 -0400
Subject: [PATCH 023/143] Add tojson utility

---
 CppHeaderParser/tojson.py | 10 ++++++++++
 1 file changed, 10 insertions(+)
 create mode 100644 CppHeaderParser/tojson.py

diff --git a/CppHeaderParser/tojson.py b/CppHeaderParser/tojson.py
new file mode 100644
index 0000000..e9ea24a
--- /dev/null
+++ b/CppHeaderParser/tojson.py
@@ -0,0 +1,10 @@
+# Utility module
+
+import sys
+import json
+
+from .CppHeaderParser import CppHeader
+
+if __name__ == "__main__":
+    for arg in sys.argv[1:]:
+        print(CppHeader(arg).toJSON())

From 1300fb522ebe154d4b523d391f970252ff3144e8 Mon Sep 17 00:00:00 2001
From: Dustin Spicuzza 
Date: Wed, 25 Sep 2019 01:33:16 -0400
Subject: [PATCH 024/143] Add sphinx documentation

---
 .gitignore                               |    3 +-
 CppHeaderParser/CppHeaderParser.py       |  206 +--
 CppHeaderParser/__init__.py              |    1 +
 CppHeaderParser/doc/CppHeaderParser.html | 1463 ----------------------
 README.html                              |  598 ---------
 README.md                                |   49 -
 README.rst                               |   57 +
 README.txt                               |  267 ----
 docs/Makefile                            |  177 +++
 docs/api.rst                             |   31 +
 docs/conf.py                             |  339 +++++
 docs/index.rst                           |    5 +
 docs/make.bat                            |  242 ++++
 setup.py                                 |    6 +-
 templates/README.txt                     |   27 -
 15 files changed, 968 insertions(+), 2503 deletions(-)
 delete mode 100644 CppHeaderParser/doc/CppHeaderParser.html
 delete mode 100644 README.html
 delete mode 100644 README.md
 create mode 100644 README.rst
 delete mode 100644 README.txt
 create mode 100644 docs/Makefile
 create mode 100644 docs/api.rst
 create mode 100644 docs/conf.py
 create mode 100644 docs/index.rst
 create mode 100644 docs/make.bat
 delete mode 100644 templates/README.txt

diff --git a/.gitignore b/.gitignore
index 7ad0fca..dd90c37 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,8 +2,9 @@
 *.egg-info
 build
 dist
+_build
 
 /CppHeaderParser/version.py
 
 __pycache__
-.vscode
\ No newline at end of file
+.vscode
diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py
index 3caff5b..6bcd961 100644
--- a/CppHeaderParser/CppHeaderParser.py
+++ b/CppHeaderParser/CppHeaderParser.py
@@ -44,9 +44,7 @@
 #
 #   http://www.opensource.org/licenses/bsd-license.php
 #
-"""Parse C++ header files and generate a data structure
-representing the class
-"""
+
 
 import ply.lex as lex
 import os
@@ -176,7 +174,7 @@ def t_NEWLINE(t):
 
 
 def t_error(v):
-    print(("Lex error: ", v))
+    print("Lex error: ", v)
 
 
 lex.lex()
@@ -215,9 +213,10 @@ def trace_print(*arg):
         sys.stdout.write("\n")
 
 
+#: Access specifiers
 supportedAccessSpecifier = ["public", "protected", "private"]
 
-# Symbols to ignore, usually special macros
+#: Symbols to ignore, usually special macros
 ignoreSymbols = ["Q_OBJECT"]
 
 doxygenCommentCache = ""
@@ -404,47 +403,53 @@ class CppParseError(Exception):
 
 
 class CppClass(dict):
-    """Takes a name stack and turns it into a class
-    
-    Contains the following Keys:
-    self['name'] - Name of the class
-    self['doxygen'] - Doxygen comments associated with the class if they exist
-    self['inherits'] - List of Classes that this one inherits where the values
-        are of the form {"access": Anything in supportedAccessSpecifier
-                                  "class": Name of the class
-    self['methods'] - Dictionary where keys are from supportedAccessSpecifier
-        and values are a lists of CppMethod's
-    self['properties'] - Dictionary where keys are from supportedAccessSpecifier
-        and values are lists of CppVariable's 
-    self['enums'] - Dictionary where keys are from supportedAccessSpecifier and
-        values are lists of CppEnum's
-    self['structs'] - Dictionary where keys are from supportedAccessSpecifier and
-        values are lists of nested Struct's
-    
-    An example of how this could look is as follows:
-    #self =
-    {
-        'name': ""
-        'inherits':[]
-        'methods':
-        {
-            'public':[],
-            'protected':[], 
-            'private':[]
-        }, 
-        'properties':
-        {
-            'public':[],
-            'protected':[], 
-            'private':[]
-        },
-        'enums':
-        {
-            'public':[],
-            'protected':[], 
-            'private':[]
-        }
-    }
+    """
+        Dictionary that contains at least the following keys:
+
+        * ``name`` - Name of the class
+        * ``doxygen`` - Doxygen comments associated with the class if they exist
+        * ``inherits`` - List of Classes that this one inherits. Values are
+          dictionaries with the following key/values:
+
+        - ``access`` - Anything in supportedAccessSpecifier
+        - ``class`` - Name of the class
+
+        * ``methods`` - Dictionary where keys are from supportedAccessSpecifier
+          and values are a lists of :class:`.CppMethod`
+        * ``namespace`` - Namespace of class
+        * ``properties`` - Dictionary where keys are from supportedAccessSpecifier
+          and values are lists of :class:`.CppVariable`
+        * ``enums`` - Dictionary where keys are from supportedAccessSpecifier and
+          values are lists of :class:`.CppEnum`
+        * ``structs`` - Dictionary where keys are from supportedAccessSpecifier and
+          values are lists of nested :class:`.CppStruct`
+        * ``final`` - True if final
+        * ``abstract`` - True if abstract
+        
+        An example of how this could look is as follows::
+
+            {
+                'name': ""
+                'inherits':[]
+                'methods':
+                {
+                    'public':[],
+                    'protected':[], 
+                    'private':[]
+                }, 
+                'properties':
+                {
+                    'public':[],
+                    'protected':[], 
+                    'private':[]
+                },
+                'enums':
+                {
+                    'public':[],
+                    'protected':[], 
+                    'private':[]
+                }
+            }
     """
 
     def get_all_methods(self):
@@ -476,6 +481,7 @@ def get_pure_virtual_methods(self, type="public"):
         return r
 
     def __init__(self, nameStack, curTemplate):
+        #: hm
         self["nested_classes"] = []
         self["parent"] = None
         self["abstract"] = False
@@ -754,19 +760,12 @@ def __str__(self):
 
 
 class CppUnion(CppClass):
-    """Takes a name stack and turns it into a union
-    
-    Contains the following Keys:
-    self['name'] - Name of the union
-    self['doxygen'] - Doxygen comments associated with the union if they exist
-    self['members'] - List of members the union has 
-    
-    An example of how this could look is as follows:
-    #self =
-    {
-        'name': ""
-        'members': []
-    }
+    """
+        Dictionary that contains at least the following keys:
+
+        * ``name`` - Name of the union
+        * ``doxygen`` - Doxygen comments associated with the union if they exist
+        * ``members`` - List of members of the union
     """
 
     def __init__(self, nameStack):
@@ -877,13 +876,13 @@ def _params_helper2(self, params):
 
 
 class CppMethod(_CppMethod):
-    """Takes a name stack and turns it into a method
-    
-    Contains the following Keys:
-    self['rtnType'] - Return type of the method (ex. "int")
-    self['name'] - Name of the method (ex. "getSize")
-    self['doxygen'] - Doxygen comments associated with the method if they exist
-    self['parameters'] - List of CppVariables
+    """
+        Dictionary that contains at least the following keys:
+
+        * ``rtnType`` - Return type of the method (ex. "int")
+        * ``name`` - Name of the method
+        * ``doxygen`` - Doxygen comments associated with the method if they exist
+        * ``parameters`` - List of :class:`.CppVariable`
     """
 
     def show(self):
@@ -1104,17 +1103,17 @@ def init(self):
 
 
 class CppVariable(_CppVariable):
-    """Takes a name stack and turns it into a method
+    """
+        Dictionary that contains at least the following keys:
     
-    Contains the following Keys:
-    self['type'] - Type for the variable (ex. "const string &")
-    self['name'] - Name of the variable (ex. "numItems")
-    self['namespace'] - Namespace containing the enum
-    self['desc'] - Description of the variable if part of a method (optional)
-    self['doxygen'] - Doxygen comments associated with the method if they exist
-    self['defaultValue'] - Default value of the variable, this key will only
-        exist if there is a default value
-    self['extern'] - True if its an extern, false if not
+        * ``type`` - Type for the variable (ex. "const string &")
+        * ``name`` - Name of the variable (ex. "numItems")
+        * ``namespace`` - Namespace
+        * ``desc`` - Description of the variable if part of a method (optional)
+        * ``doxygen`` - Doxygen comments associated with the method if they exist
+        * ``defaultValue`` - Default value of the variable, this key will only
+          exist if there is a default value
+        * ``extern`` - True if its an extern, False if not
     """
 
     Vars = []
@@ -1235,7 +1234,7 @@ def resolve_enum_values(self, values):
         """Evaluates the values list of dictionaries passed in and figures out what the enum value
         for each enum is editing in place:
         
-        Example:
+        Example
         From: [{'name': 'ORANGE'},
                {'name': 'RED'},
                {'name': 'GREEN', 'value': '8'}]
@@ -1289,14 +1288,16 @@ def resolve_enum_values(self, values):
 class CppEnum(_CppEnum):
     """Takes a name stack and turns it into an Enum
     
-    Contains the following Keys:
-    self['name'] - Name of the enum (ex. "ItemState")
-    self['namespace'] - Namespace containing the enum
-    self['values'] - List of values where the values are a dictionary of the
-        form {"name": name of the key (ex. "PARSING_HEADER"),
-                  "value": Specified value of the enum, this key will only exist
-                    if a value for a given enum value was defined
-                }
+    Contains the following keys:
+
+    * ``name`` - Name of the enum (ex. "ItemState")
+    * ``namespace`` - Namespace containing the enum
+    * ``values`` - List of values. The values are a dictionary with 
+      the following key/values:
+      
+      - ``name`` - name of the key (ex. "PARSING_HEADER"),
+      - ``value`` - Specified value of the enum, this key will only exist
+        if a value for a given enum value was defined
     """
 
     def __init__(self, nameStack):
@@ -1372,6 +1373,14 @@ def __init__(self, nameStack):
 
 
 class CppStruct(dict):
+    """
+        Dictionary that contains at least the following keys:
+
+        * ``type`` - Name of this struct
+        * ``fields`` - List of :class:`.CppVariable`
+        * ``line_number`` - Line number this struct was found on
+    """
+
     Structs = []
 
     def __init__(self, nameStack):
@@ -2478,12 +2487,7 @@ def evalute_forward_decl(self):
 
 
 class CppHeader(_CppHeader):
-    """Parsed C++ class header
-    
-    Variables produced:
-    self.classes - Dictionary of classes found in a given header file where the
-        key is the name of the class
-    """
+    """Parsed C++ class header"""
 
     IGNORE_NAMES = "__extension__".split()
 
@@ -2519,21 +2523,35 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs):
         # nested classes have parent::nested, but no extra namespace,
         # this keeps the API compatible, TODO proper namespace for everything.
         Resolver.CLASSES = {}
+
+        #: Dictionary of classes found in the header file. The key is the name
+        #: of the class, value is :class:`.CppClass`
         self.classes = Resolver.CLASSES
-        # Functions that are not part of a class
+
+        #: List of free functions as :class:`.CppMethod`
         self.functions = []
 
+        #: List of #pragma directives found as strings
         self.pragmas = []
+
+        #: List of #define directives found
         self.defines = []
+
+        #: List of #include directives found
         self.includes = []
         self._precomp_macro_buf = (
             []
         )  # for internal purposes, will end up filling out pragmras and defines at the end
 
+        #: List of enums in this header as :class:`.CppEnum`
         self.enums = []
+
+        #: List of variables in this header as :class:`.CppVariable`
         self.variables = []
         self.global_enums = {}
         self.nameStack = []
+
+        #: Namespaces in this header
         self.nameSpaces = []
         self.curAccessSpecifier = "private"  # private is default
         self.curTemplate = None
@@ -3191,7 +3209,7 @@ def _strip_parent_keys(self):
             except:
                 trace_print("Exception")
 
-    def toJSON(self, indent=4):
+    def toJSON(self, indent=4, separators=None):
         """Converts a parsed structure to JSON"""
         import json
 
@@ -3200,7 +3218,7 @@ def toJSON(self, indent=4):
             del self.__dict__["classes_order"]
         except:
             pass
-        return json.dumps(self.__dict__, indent=indent)
+        return json.dumps(self.__dict__, indent=indent, separators=separators)
 
     def __repr__(self):
         rtn = {
diff --git a/CppHeaderParser/__init__.py b/CppHeaderParser/__init__.py
index dd2fb30..facdf9b 100644
--- a/CppHeaderParser/__init__.py
+++ b/CppHeaderParser/__init__.py
@@ -2,5 +2,6 @@
 # Author: Jashua Cloutier (contact via sourceforge username:senexcanis)
 
 from .CppHeaderParser import *
+from .CppHeaderParser import __version__
 
 # __all__ = ['CppHeaderParser']
diff --git a/CppHeaderParser/doc/CppHeaderParser.html b/CppHeaderParser/doc/CppHeaderParser.html
deleted file mode 100644
index 8301bdb..0000000
--- a/CppHeaderParser/doc/CppHeaderParser.html
+++ /dev/null
@@ -1,1463 +0,0 @@
-
-
-Codestin Search App
-
-
-
-
-
-
 
- 
CppHeaderParser (version 2.7.4)
index
/home/senex/workspace/cppheaderparser/CppHeaderParser/CppHeaderParser.py
-

Parse C++ header files and generate a data structure
-representing the class

-

- - - - - -
 
-Modules
       
inspect
-ply.lex
-
os
-re
-
sys
-

- - - - - -
 
-Classes
       
-
_CppEnum(__builtin__.dict) -
-
-
CppEnum -
-
-
_CppHeader(Resolver) -
-
-
CppHeader -
-
-
_CppMethod(__builtin__.dict) -
-
-
CppMethod -
-
-
_CppVariable(__builtin__.dict) -
-
-
CppVariable -
-
-
__builtin__.dict(__builtin__.object) -
-
-
CppClass -
-
-
CppUnion -
-
-
CppStruct -
-
-
__builtin__.object -
-
-
Resolver -
-
-
__builtin__.str(__builtin__.basestring) -
-
-
TagStr -
-
-
exceptions.Exception(exceptions.BaseException) -
-
-
CppParseError -
-
-
-

- - - - - - - -
 
-class CppClass(__builtin__.dict)
   Takes a name stack and turns it into a class

-Contains the following Keys:
-self['name'] - Name of the class
-self['doxygen'] - Doxygen comments associated with the class if they exist
-self['inherits'] - List of Classes that this one inherits where the values
-    are of the form {"access": Anything in supportedAccessSpecifier
-                              "class": Name of the class
-self['methods'] - Dictionary where keys are from supportedAccessSpecifier
-    and values are a lists of CppMethod's
-self['properties'] - Dictionary where keys are from supportedAccessSpecifier
-    and values are lists of CppVariable's 
-self['enums'] - Dictionary where keys are from supportedAccessSpecifier and
-    values are lists of CppEnum's
-self['structs'] - Dictionary where keys are from supportedAccessSpecifier and
-    values are lists of nested Struct's

-An example of how this could look is as follows:
-#self =
-{
-    'name': ""
-    'inherits':[]
-    'methods':
-    {
-        'public':[],
-        'protected':[], 
-        'private':[]
-    }, 
-    'properties':
-    {
-        'public':[],
-        'protected':[], 
-        'private':[]
-    },
-    'enums':
-    {
-        'public':[],
-        'protected':[], 
-        'private':[]
-    }
-}
 
 
Method resolution order:
-
CppClass
-
__builtin__.dict
-
__builtin__.object
-
-
-Methods defined here:
-
__init__(self, nameStack, curTemplate)
- -
__str__(self)
Convert class to a string
- -
get_all_method_names(self)
- -
get_all_methods(self)
- -
get_all_pure_virtual_methods(self)
- -
get_method_names(self, type='public')
- -
get_pure_virtual_methods(self, type='public')
- -
show(self)
Convert class to a string
- -
-Data descriptors defined here:
-
__dict__
-
dictionary for instance variables (if defined)
-
-
__weakref__
-
list of weak references to the object (if defined)
-
-
-Methods inherited from __builtin__.dict:
-
__cmp__(...)
x.__cmp__(y) <==> cmp(x,y)
- -
__contains__(...)
D.__contains__(k) -> True if D has a key k, else False
- -
__delitem__(...)
x.__delitem__(y) <==> del x[y]
- -
__eq__(...)
x.__eq__(y) <==> x==y
- -
__ge__(...)
x.__ge__(y) <==> x>=y
- -
__getattribute__(...)
x.__getattribute__('name') <==> x.name
- -
__getitem__(...)
x.__getitem__(y) <==> x[y]
- -
__gt__(...)
x.__gt__(y) <==> x>y
- -
__iter__(...)
x.__iter__() <==> iter(x)
- -
__le__(...)
x.__le__(y) <==> x<=y
- -
__len__(...)
x.__len__() <==> len(x)
- -
__lt__(...)
x.__lt__(y) <==> x<y
- -
__ne__(...)
x.__ne__(y) <==> x!=y
- -
__repr__(...)
x.__repr__() <==> repr(x)
- -
__setitem__(...)
x.__setitem__(i, y) <==> x[i]=y
- -
__sizeof__(...)
D.__sizeof__() -> size of D in memory, in bytes
- -
clear(...)
D.clear() -> None.  Remove all items from D.
- -
copy(...)
D.copy() -> a shallow copy of D
- -
fromkeys(...)
dict.fromkeys(S[,v]) -> New dict with keys from S and values equal to v.
-v defaults to None.
- -
get(...)
D.get(k[,d]) -> D[k] if k in D, else d.  d defaults to None.
- -
has_key(...)
D.has_key(k) -> True if D has a key k, else False
- -
items(...)
D.items() -> list of D's (key, value) pairs, as 2-tuples
- -
iteritems(...)
D.iteritems() -> an iterator over the (key, value) items of D
- -
iterkeys(...)
D.iterkeys() -> an iterator over the keys of D
- -
itervalues(...)
D.itervalues() -> an iterator over the values of D
- -
keys(...)
D.keys() -> list of D's keys
- -
pop(...)
D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
-If key is not found, d is returned if given, otherwise KeyError is raised
- -
popitem(...)
D.popitem() -> (k, v), remove and return some (key, value) pair as a
-2-tuple; but raise KeyError if D is empty.
- -
setdefault(...)
D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D
- -
update(...)
D.update([E, ]**F) -> None.  Update D from dict/iterable E and F.
-If E present and has a .keys() method, does:     for k in E: D[k] = E[k]
-If E present and lacks .keys() method, does:     for (k, v) in E: D[k] = v
-In either case, this is followed by: for k in F: D[k] = F[k]
- -
values(...)
D.values() -> list of D's values
- -
viewitems(...)
D.viewitems() -> a set-like object providing a view on D's items
- -
viewkeys(...)
D.viewkeys() -> a set-like object providing a view on D's keys
- -
viewvalues(...)
D.viewvalues() -> an object providing a view on D's values
- -
-Data and other attributes inherited from __builtin__.dict:
-
__hash__ = None
- -
__new__ = <built-in method __new__ of type object>
T.__new__(S, ...) -> a new object with type S, a subtype of T
- -

- - - - - - - -
 
-class CppEnum(_CppEnum)
   Takes a name stack and turns it into an Enum

-Contains the following Keys:
-self['name'] - Name of the enum (ex. "ItemState")
-self['namespace'] - Namespace containing the enum
-self['values'] - List of values where the values are a dictionary of the
-    form {"name": name of the key (ex. "PARSING_HEADER"),
-              "value": Specified value of the enum, this key will only exist
-                if a value for a given enum value was defined
-            }
 
 
Method resolution order:
-
CppEnum
-
_CppEnum
-
__builtin__.dict
-
__builtin__.object
-
-
-Methods defined here:
-
__init__(self, nameStack)
- -
-Methods inherited from _CppEnum:
-
resolve_enum_values(self, values)
Evaluates the values list of dictionaries passed in and figures out what the enum value
-for each enum is editing in place:

-Example:
-From: [{'name': 'ORANGE'},
-       {'name': 'RED'},
-       {'name': 'GREEN', 'value': '8'}]
-To:   [{'name': 'ORANGE', 'value': 0},
-       {'name': 'RED', 'value': 1},
-       {'name': 'GREEN', 'value': 8}]
- -
-Data descriptors inherited from _CppEnum:
-
__dict__
-
dictionary for instance variables (if defined)
-
-
__weakref__
-
list of weak references to the object (if defined)
-
-
-Methods inherited from __builtin__.dict:
-
__cmp__(...)
x.__cmp__(y) <==> cmp(x,y)
- -
__contains__(...)
D.__contains__(k) -> True if D has a key k, else False
- -
__delitem__(...)
x.__delitem__(y) <==> del x[y]
- -
__eq__(...)
x.__eq__(y) <==> x==y
- -
__ge__(...)
x.__ge__(y) <==> x>=y
- -
__getattribute__(...)
x.__getattribute__('name') <==> x.name
- -
__getitem__(...)
x.__getitem__(y) <==> x[y]
- -
__gt__(...)
x.__gt__(y) <==> x>y
- -
__iter__(...)
x.__iter__() <==> iter(x)
- -
__le__(...)
x.__le__(y) <==> x<=y
- -
__len__(...)
x.__len__() <==> len(x)
- -
__lt__(...)
x.__lt__(y) <==> x<y
- -
__ne__(...)
x.__ne__(y) <==> x!=y
- -
__repr__(...)
x.__repr__() <==> repr(x)
- -
__setitem__(...)
x.__setitem__(i, y) <==> x[i]=y
- -
__sizeof__(...)
D.__sizeof__() -> size of D in memory, in bytes
- -
clear(...)
D.clear() -> None.  Remove all items from D.
- -
copy(...)
D.copy() -> a shallow copy of D
- -
fromkeys(...)
dict.fromkeys(S[,v]) -> New dict with keys from S and values equal to v.
-v defaults to None.
- -
get(...)
D.get(k[,d]) -> D[k] if k in D, else d.  d defaults to None.
- -
has_key(...)
D.has_key(k) -> True if D has a key k, else False
- -
items(...)
D.items() -> list of D's (key, value) pairs, as 2-tuples
- -
iteritems(...)
D.iteritems() -> an iterator over the (key, value) items of D
- -
iterkeys(...)
D.iterkeys() -> an iterator over the keys of D
- -
itervalues(...)
D.itervalues() -> an iterator over the values of D
- -
keys(...)
D.keys() -> list of D's keys
- -
pop(...)
D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
-If key is not found, d is returned if given, otherwise KeyError is raised
- -
popitem(...)
D.popitem() -> (k, v), remove and return some (key, value) pair as a
-2-tuple; but raise KeyError if D is empty.
- -
setdefault(...)
D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D
- -
update(...)
D.update([E, ]**F) -> None.  Update D from dict/iterable E and F.
-If E present and has a .keys() method, does:     for k in E: D[k] = E[k]
-If E present and lacks .keys() method, does:     for (k, v) in E: D[k] = v
-In either case, this is followed by: for k in F: D[k] = F[k]
- -
values(...)
D.values() -> list of D's values
- -
viewitems(...)
D.viewitems() -> a set-like object providing a view on D's items
- -
viewkeys(...)
D.viewkeys() -> a set-like object providing a view on D's keys
- -
viewvalues(...)
D.viewvalues() -> an object providing a view on D's values
- -
-Data and other attributes inherited from __builtin__.dict:
-
__hash__ = None
- -
__new__ = <built-in method __new__ of type object>
T.__new__(S, ...) -> a new object with type S, a subtype of T
- -

- - - - - - - -
 
-class CppHeader(_CppHeader)
   Parsed C++ class header

-Variables produced:
-self.classes - Dictionary of classes found in a given header file where the
-    key is the name of the class
 
 
Method resolution order:
-
CppHeader
-
_CppHeader
-
Resolver
-
__builtin__.object
-
-
-Methods defined here:
-
__init__(self, headerFileName, argType='file', **kwargs)
Create the parsed C++ header file parse tree

-headerFileName - Name of the file to parse OR actual file contents (depends on argType)
-argType - Indicates how to interpret headerFileName as a file string or file name
-kwargs - Supports the following keywords
- -
__repr__(self)
- -
__str__(self)
- -
evaluate_enum_stack(self)
Create an Enum out of the name stack
- -
evaluate_stack(self, token=None)
Evaluates the current name stack
- -
show(self)
- -
strip_parent_keys(self)
Strip all parent (and method) keys to prevent loops
- -
toJSON(self, indent=4)
Converts a parsed structure to JSON
- -
-Data and other attributes defined here:
-
IGNORE_NAMES = ['__extension__']
- -
-Methods inherited from _CppHeader:
-
evaluate_class_stack(self)
Create a Class out of the name stack (but not its parts)
- -
evaluate_method_stack(self)
Create a method out of the name stack
- -
evaluate_property_stack(self)
Create a Property out of the name stack
- -
evaluate_struct_stack(self)
Create a Struct out of the name stack (but not its parts)
- -
evaluate_typedef(self)
- -
evalute_forward_decl(self)
- -
finalize(self)
- -
parse_method_type(self, stack)
- -
-Methods inherited from Resolver:
-
concrete_typedef(self, key)
- -
cur_namespace(self, add_double_colon=False)
- -
current_namespace(self)
- -
finalize_vars(self)
- -
guess_ctypes_type(self, string)
- -
initextra(self)
- -
resolve_type(self, string, result)
keeps track of useful things like: how many pointers, number of typedefs, is fundamental or a class, etc...
- -
-Data descriptors inherited from Resolver:
-
__dict__
-
dictionary for instance variables (if defined)
-
-
__weakref__
-
list of weak references to the object (if defined)
-
-
-Data and other attributes inherited from Resolver:
-
CLASSES = {}
- -
C_FUNDAMENTAL = ['size_t', 'unsigned', 'signed', 'bool', 'char', 'wchar', 'short', 'int', 'float', 'double', 'long', 'void', 'struct', 'union', 'enum']
- -
NAMESPACES = []
- -
STRUCTS = {}
- -
SubTypedefs = {}
- -

- - - - - - - -
 
-class CppMethod(_CppMethod)
   Takes a name stack and turns it into a method

-Contains the following Keys:
-self['rtnType'] - Return type of the method (ex. "int")
-self['name'] - Name of the method (ex. "getSize")
-self['doxygen'] - Doxygen comments associated with the method if they exist
-self['parameters'] - List of CppVariables
 
 
Method resolution order:
-
CppMethod
-
_CppMethod
-
__builtin__.dict
-
__builtin__.object
-
-
-Methods defined here:
-
__init__(self, nameStack, curClass, methinfo, curTemplate)
- -
__str__(self)
- -
show(self)
- -
-Data descriptors inherited from _CppMethod:
-
__dict__
-
dictionary for instance variables (if defined)
-
-
__weakref__
-
list of weak references to the object (if defined)
-
-
-Methods inherited from __builtin__.dict:
-
__cmp__(...)
x.__cmp__(y) <==> cmp(x,y)
- -
__contains__(...)
D.__contains__(k) -> True if D has a key k, else False
- -
__delitem__(...)
x.__delitem__(y) <==> del x[y]
- -
__eq__(...)
x.__eq__(y) <==> x==y
- -
__ge__(...)
x.__ge__(y) <==> x>=y
- -
__getattribute__(...)
x.__getattribute__('name') <==> x.name
- -
__getitem__(...)
x.__getitem__(y) <==> x[y]
- -
__gt__(...)
x.__gt__(y) <==> x>y
- -
__iter__(...)
x.__iter__() <==> iter(x)
- -
__le__(...)
x.__le__(y) <==> x<=y
- -
__len__(...)
x.__len__() <==> len(x)
- -
__lt__(...)
x.__lt__(y) <==> x<y
- -
__ne__(...)
x.__ne__(y) <==> x!=y
- -
__repr__(...)
x.__repr__() <==> repr(x)
- -
__setitem__(...)
x.__setitem__(i, y) <==> x[i]=y
- -
__sizeof__(...)
D.__sizeof__() -> size of D in memory, in bytes
- -
clear(...)
D.clear() -> None.  Remove all items from D.
- -
copy(...)
D.copy() -> a shallow copy of D
- -
fromkeys(...)
dict.fromkeys(S[,v]) -> New dict with keys from S and values equal to v.
-v defaults to None.
- -
get(...)
D.get(k[,d]) -> D[k] if k in D, else d.  d defaults to None.
- -
has_key(...)
D.has_key(k) -> True if D has a key k, else False
- -
items(...)
D.items() -> list of D's (key, value) pairs, as 2-tuples
- -
iteritems(...)
D.iteritems() -> an iterator over the (key, value) items of D
- -
iterkeys(...)
D.iterkeys() -> an iterator over the keys of D
- -
itervalues(...)
D.itervalues() -> an iterator over the values of D
- -
keys(...)
D.keys() -> list of D's keys
- -
pop(...)
D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
-If key is not found, d is returned if given, otherwise KeyError is raised
- -
popitem(...)
D.popitem() -> (k, v), remove and return some (key, value) pair as a
-2-tuple; but raise KeyError if D is empty.
- -
setdefault(...)
D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D
- -
update(...)
D.update([E, ]**F) -> None.  Update D from dict/iterable E and F.
-If E present and has a .keys() method, does:     for k in E: D[k] = E[k]
-If E present and lacks .keys() method, does:     for (k, v) in E: D[k] = v
-In either case, this is followed by: for k in F: D[k] = F[k]
- -
values(...)
D.values() -> list of D's values
- -
viewitems(...)
D.viewitems() -> a set-like object providing a view on D's items
- -
viewkeys(...)
D.viewkeys() -> a set-like object providing a view on D's keys
- -
viewvalues(...)
D.viewvalues() -> an object providing a view on D's values
- -
-Data and other attributes inherited from __builtin__.dict:
-
__hash__ = None
- -
__new__ = <built-in method __new__ of type object>
T.__new__(S, ...) -> a new object with type S, a subtype of T
- -

- - - - - -
 
-class CppParseError(exceptions.Exception)
    
Method resolution order:
-
CppParseError
-
exceptions.Exception
-
exceptions.BaseException
-
__builtin__.object
-
-
-Data descriptors defined here:
-
__weakref__
-
list of weak references to the object (if defined)
-
-
-Methods inherited from exceptions.Exception:
-
__init__(...)
x.__init__(...) initializes x; see help(type(x)) for signature
- -
-Data and other attributes inherited from exceptions.Exception:
-
__new__ = <built-in method __new__ of type object>
T.__new__(S, ...) -> a new object with type S, a subtype of T
- -
-Methods inherited from exceptions.BaseException:
-
__delattr__(...)
x.__delattr__('name') <==> del x.name
- -
__getattribute__(...)
x.__getattribute__('name') <==> x.name
- -
__getitem__(...)
x.__getitem__(y) <==> x[y]
- -
__getslice__(...)
x.__getslice__(i, j) <==> x[i:j]

-Use of negative indices is not supported.
- -
__reduce__(...)
- -
__repr__(...)
x.__repr__() <==> repr(x)
- -
__setattr__(...)
x.__setattr__('name', value) <==> x.name = value
- -
__setstate__(...)
- -
__str__(...)
x.__str__() <==> str(x)
- -
__unicode__(...)
- -
-Data descriptors inherited from exceptions.BaseException:
-
__dict__
-
-
args
-
-
message
-
-

- - - - - -
 
-class CppStruct(__builtin__.dict)
    
Method resolution order:
-
CppStruct
-
__builtin__.dict
-
__builtin__.object
-
-
-Methods defined here:
-
__init__(self, nameStack)
- -
-Data descriptors defined here:
-
__dict__
-
dictionary for instance variables (if defined)
-
-
__weakref__
-
list of weak references to the object (if defined)
-
-
-Data and other attributes defined here:
-
Structs = []
- -
-Methods inherited from __builtin__.dict:
-
__cmp__(...)
x.__cmp__(y) <==> cmp(x,y)
- -
__contains__(...)
D.__contains__(k) -> True if D has a key k, else False
- -
__delitem__(...)
x.__delitem__(y) <==> del x[y]
- -
__eq__(...)
x.__eq__(y) <==> x==y
- -
__ge__(...)
x.__ge__(y) <==> x>=y
- -
__getattribute__(...)
x.__getattribute__('name') <==> x.name
- -
__getitem__(...)
x.__getitem__(y) <==> x[y]
- -
__gt__(...)
x.__gt__(y) <==> x>y
- -
__iter__(...)
x.__iter__() <==> iter(x)
- -
__le__(...)
x.__le__(y) <==> x<=y
- -
__len__(...)
x.__len__() <==> len(x)
- -
__lt__(...)
x.__lt__(y) <==> x<y
- -
__ne__(...)
x.__ne__(y) <==> x!=y
- -
__repr__(...)
x.__repr__() <==> repr(x)
- -
__setitem__(...)
x.__setitem__(i, y) <==> x[i]=y
- -
__sizeof__(...)
D.__sizeof__() -> size of D in memory, in bytes
- -
clear(...)
D.clear() -> None.  Remove all items from D.
- -
copy(...)
D.copy() -> a shallow copy of D
- -
fromkeys(...)
dict.fromkeys(S[,v]) -> New dict with keys from S and values equal to v.
-v defaults to None.
- -
get(...)
D.get(k[,d]) -> D[k] if k in D, else d.  d defaults to None.
- -
has_key(...)
D.has_key(k) -> True if D has a key k, else False
- -
items(...)
D.items() -> list of D's (key, value) pairs, as 2-tuples
- -
iteritems(...)
D.iteritems() -> an iterator over the (key, value) items of D
- -
iterkeys(...)
D.iterkeys() -> an iterator over the keys of D
- -
itervalues(...)
D.itervalues() -> an iterator over the values of D
- -
keys(...)
D.keys() -> list of D's keys
- -
pop(...)
D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
-If key is not found, d is returned if given, otherwise KeyError is raised
- -
popitem(...)
D.popitem() -> (k, v), remove and return some (key, value) pair as a
-2-tuple; but raise KeyError if D is empty.
- -
setdefault(...)
D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D
- -
update(...)
D.update([E, ]**F) -> None.  Update D from dict/iterable E and F.
-If E present and has a .keys() method, does:     for k in E: D[k] = E[k]
-If E present and lacks .keys() method, does:     for (k, v) in E: D[k] = v
-In either case, this is followed by: for k in F: D[k] = F[k]
- -
values(...)
D.values() -> list of D's values
- -
viewitems(...)
D.viewitems() -> a set-like object providing a view on D's items
- -
viewkeys(...)
D.viewkeys() -> a set-like object providing a view on D's keys
- -
viewvalues(...)
D.viewvalues() -> an object providing a view on D's values
- -
-Data and other attributes inherited from __builtin__.dict:
-
__hash__ = None
- -
__new__ = <built-in method __new__ of type object>
T.__new__(S, ...) -> a new object with type S, a subtype of T
- -

- - - - - - - -
 
-class CppUnion(CppClass)
   Takes a name stack and turns it into a union

-Contains the following Keys:
-self['name'] - Name of the union
-self['doxygen'] - Doxygen comments associated with the union if they exist
-self['members'] - List of members the union has 

-An example of how this could look is as follows:
-#self =
-{
-    'name': ""
-    'members': []
-}
 
 
Method resolution order:
-
CppUnion
-
CppClass
-
__builtin__.dict
-
__builtin__.object
-
-
-Methods defined here:
-
__init__(self, nameStack)
- -
__str__(self)
Convert class to a string
- -
show(self)
Convert class to a string
- -
transform_to_union_keys(self)
- -
-Methods inherited from CppClass:
-
get_all_method_names(self)
- -
get_all_methods(self)
- -
get_all_pure_virtual_methods(self)
- -
get_method_names(self, type='public')
- -
get_pure_virtual_methods(self, type='public')
- -
-Data descriptors inherited from CppClass:
-
__dict__
-
dictionary for instance variables (if defined)
-
-
__weakref__
-
list of weak references to the object (if defined)
-
-
-Methods inherited from __builtin__.dict:
-
__cmp__(...)
x.__cmp__(y) <==> cmp(x,y)
- -
__contains__(...)
D.__contains__(k) -> True if D has a key k, else False
- -
__delitem__(...)
x.__delitem__(y) <==> del x[y]
- -
__eq__(...)
x.__eq__(y) <==> x==y
- -
__ge__(...)
x.__ge__(y) <==> x>=y
- -
__getattribute__(...)
x.__getattribute__('name') <==> x.name
- -
__getitem__(...)
x.__getitem__(y) <==> x[y]
- -
__gt__(...)
x.__gt__(y) <==> x>y
- -
__iter__(...)
x.__iter__() <==> iter(x)
- -
__le__(...)
x.__le__(y) <==> x<=y
- -
__len__(...)
x.__len__() <==> len(x)
- -
__lt__(...)
x.__lt__(y) <==> x<y
- -
__ne__(...)
x.__ne__(y) <==> x!=y
- -
__repr__(...)
x.__repr__() <==> repr(x)
- -
__setitem__(...)
x.__setitem__(i, y) <==> x[i]=y
- -
__sizeof__(...)
D.__sizeof__() -> size of D in memory, in bytes
- -
clear(...)
D.clear() -> None.  Remove all items from D.
- -
copy(...)
D.copy() -> a shallow copy of D
- -
fromkeys(...)
dict.fromkeys(S[,v]) -> New dict with keys from S and values equal to v.
-v defaults to None.
- -
get(...)
D.get(k[,d]) -> D[k] if k in D, else d.  d defaults to None.
- -
has_key(...)
D.has_key(k) -> True if D has a key k, else False
- -
items(...)
D.items() -> list of D's (key, value) pairs, as 2-tuples
- -
iteritems(...)
D.iteritems() -> an iterator over the (key, value) items of D
- -
iterkeys(...)
D.iterkeys() -> an iterator over the keys of D
- -
itervalues(...)
D.itervalues() -> an iterator over the values of D
- -
keys(...)
D.keys() -> list of D's keys
- -
pop(...)
D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
-If key is not found, d is returned if given, otherwise KeyError is raised
- -
popitem(...)
D.popitem() -> (k, v), remove and return some (key, value) pair as a
-2-tuple; but raise KeyError if D is empty.
- -
setdefault(...)
D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D
- -
update(...)
D.update([E, ]**F) -> None.  Update D from dict/iterable E and F.
-If E present and has a .keys() method, does:     for k in E: D[k] = E[k]
-If E present and lacks .keys() method, does:     for (k, v) in E: D[k] = v
-In either case, this is followed by: for k in F: D[k] = F[k]
- -
values(...)
D.values() -> list of D's values
- -
viewitems(...)
D.viewitems() -> a set-like object providing a view on D's items
- -
viewkeys(...)
D.viewkeys() -> a set-like object providing a view on D's keys
- -
viewvalues(...)
D.viewvalues() -> an object providing a view on D's values
- -
-Data and other attributes inherited from __builtin__.dict:
-
__hash__ = None
- -
__new__ = <built-in method __new__ of type object>
T.__new__(S, ...) -> a new object with type S, a subtype of T
- -

- - - - - - - -
 
-class CppVariable(_CppVariable)
   Takes a name stack and turns it into a method

-Contains the following Keys:
-self['type'] - Type for the variable (ex. "const string &")
-self['name'] - Name of the variable (ex. "numItems")
-self['namespace'] - Namespace containing the enum
-self['desc'] - Description of the variable if part of a method (optional)
-self['doxygen'] - Doxygen comments associated with the method if they exist
-self['defaultValue'] - Default value of the variable, this key will only
-    exist if there is a default value
-self['extern'] - True if its an extern, false if not
 
 
Method resolution order:
-
CppVariable
-
_CppVariable
-
__builtin__.dict
-
__builtin__.object
-
-
-Methods defined here:
-
__init__(self, nameStack, **kwargs)
- -
__str__(self)
- -
-Data and other attributes defined here:
-
Vars = []
- -
-Methods inherited from _CppVariable:
-
init(self)
- -
-Data descriptors inherited from _CppVariable:
-
__dict__
-
dictionary for instance variables (if defined)
-
-
__weakref__
-
list of weak references to the object (if defined)
-
-
-Methods inherited from __builtin__.dict:
-
__cmp__(...)
x.__cmp__(y) <==> cmp(x,y)
- -
__contains__(...)
D.__contains__(k) -> True if D has a key k, else False
- -
__delitem__(...)
x.__delitem__(y) <==> del x[y]
- -
__eq__(...)
x.__eq__(y) <==> x==y
- -
__ge__(...)
x.__ge__(y) <==> x>=y
- -
__getattribute__(...)
x.__getattribute__('name') <==> x.name
- -
__getitem__(...)
x.__getitem__(y) <==> x[y]
- -
__gt__(...)
x.__gt__(y) <==> x>y
- -
__iter__(...)
x.__iter__() <==> iter(x)
- -
__le__(...)
x.__le__(y) <==> x<=y
- -
__len__(...)
x.__len__() <==> len(x)
- -
__lt__(...)
x.__lt__(y) <==> x<y
- -
__ne__(...)
x.__ne__(y) <==> x!=y
- -
__repr__(...)
x.__repr__() <==> repr(x)
- -
__setitem__(...)
x.__setitem__(i, y) <==> x[i]=y
- -
__sizeof__(...)
D.__sizeof__() -> size of D in memory, in bytes
- -
clear(...)
D.clear() -> None.  Remove all items from D.
- -
copy(...)
D.copy() -> a shallow copy of D
- -
fromkeys(...)
dict.fromkeys(S[,v]) -> New dict with keys from S and values equal to v.
-v defaults to None.
- -
get(...)
D.get(k[,d]) -> D[k] if k in D, else d.  d defaults to None.
- -
has_key(...)
D.has_key(k) -> True if D has a key k, else False
- -
items(...)
D.items() -> list of D's (key, value) pairs, as 2-tuples
- -
iteritems(...)
D.iteritems() -> an iterator over the (key, value) items of D
- -
iterkeys(...)
D.iterkeys() -> an iterator over the keys of D
- -
itervalues(...)
D.itervalues() -> an iterator over the values of D
- -
keys(...)
D.keys() -> list of D's keys
- -
pop(...)
D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
-If key is not found, d is returned if given, otherwise KeyError is raised
- -
popitem(...)
D.popitem() -> (k, v), remove and return some (key, value) pair as a
-2-tuple; but raise KeyError if D is empty.
- -
setdefault(...)
D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D
- -
update(...)
D.update([E, ]**F) -> None.  Update D from dict/iterable E and F.
-If E present and has a .keys() method, does:     for k in E: D[k] = E[k]
-If E present and lacks .keys() method, does:     for (k, v) in E: D[k] = v
-In either case, this is followed by: for k in F: D[k] = F[k]
- -
values(...)
D.values() -> list of D's values
- -
viewitems(...)
D.viewitems() -> a set-like object providing a view on D's items
- -
viewkeys(...)
D.viewkeys() -> a set-like object providing a view on D's keys
- -
viewvalues(...)
D.viewvalues() -> an object providing a view on D's values
- -
-Data and other attributes inherited from __builtin__.dict:
-
__hash__ = None
- -
__new__ = <built-in method __new__ of type object>
T.__new__(S, ...) -> a new object with type S, a subtype of T
- -

- - - - - -
 
-class Resolver(__builtin__.object)
    Methods defined here:
-
concrete_typedef(self, key)
- -
cur_namespace(self, add_double_colon=False)
- -
current_namespace(self)
- -
finalize_vars(self)
- -
guess_ctypes_type(self, string)
- -
initextra(self)
- -
resolve_type(self, string, result)
keeps track of useful things like: how many pointers, number of typedefs, is fundamental or a class, etc...
- -
-Data descriptors defined here:
-
__dict__
-
dictionary for instance variables (if defined)
-
-
__weakref__
-
list of weak references to the object (if defined)
-
-
-Data and other attributes defined here:
-
CLASSES = {}
- -
C_FUNDAMENTAL = ['size_t', 'unsigned', 'signed', 'bool', 'char', 'wchar', 'short', 'int', 'float', 'double', 'long', 'void', 'struct', 'union', 'enum']
- -
NAMESPACES = []
- -
STRUCTS = {}
- -
SubTypedefs = {}
- -

- - - - - - - -
 
-class TagStr(__builtin__.str)
   Wrapper for a string that allows us to store the line number associated with it
 
 
Method resolution order:
-
TagStr
-
__builtin__.str
-
__builtin__.basestring
-
__builtin__.object
-
-
-Methods defined here:
-
__del__(self)
- -
lineno(self)
- -
-Static methods defined here:
-
__new__(cls, *args, **kw)
- -
-Data descriptors defined here:
-
__dict__
-
dictionary for instance variables (if defined)
-
-
-Data and other attributes defined here:
-
lineno_reg = {}
- -
-Methods inherited from __builtin__.str:
-
__add__(...)
x.__add__(y) <==> x+y
- -
__contains__(...)
x.__contains__(y) <==> y in x
- -
__eq__(...)
x.__eq__(y) <==> x==y
- -
__format__(...)
S.__format__(format_spec) -> string

-Return a formatted version of S as described by format_spec.
- -
__ge__(...)
x.__ge__(y) <==> x>=y
- -
__getattribute__(...)
x.__getattribute__('name') <==> x.name
- -
__getitem__(...)
x.__getitem__(y) <==> x[y]
- -
__getnewargs__(...)
- -
__getslice__(...)
x.__getslice__(i, j) <==> x[i:j]

-Use of negative indices is not supported.
- -
__gt__(...)
x.__gt__(y) <==> x>y
- -
__hash__(...)
x.__hash__() <==> hash(x)
- -
__le__(...)
x.__le__(y) <==> x<=y
- -
__len__(...)
x.__len__() <==> len(x)
- -
__lt__(...)
x.__lt__(y) <==> x<y
- -
__mod__(...)
x.__mod__(y) <==> x%y
- -
__mul__(...)
x.__mul__(n) <==> x*n
- -
__ne__(...)
x.__ne__(y) <==> x!=y
- -
__repr__(...)
x.__repr__() <==> repr(x)
- -
__rmod__(...)
x.__rmod__(y) <==> y%x
- -
__rmul__(...)
x.__rmul__(n) <==> n*x
- -
__sizeof__(...)
S.__sizeof__() -> size of S in memory, in bytes
- -
__str__(...)
x.__str__() <==> str(x)
- -
capitalize(...)
S.capitalize() -> string

-Return a copy of the string S with only its first character
-capitalized.
- -
center(...)
S.center(width[, fillchar]) -> string

-Return S centered in a string of length width. Padding is
-done using the specified fill character (default is a space)
- -
count(...)
S.count(sub[, start[, end]]) -> int

-Return the number of non-overlapping occurrences of substring sub in
-string S[start:end].  Optional arguments start and end are interpreted
-as in slice notation.
- -
decode(...)
S.decode([encoding[,errors]]) -> object

-Decodes S using the codec registered for encoding. encoding defaults
-to the default encoding. errors may be given to set a different error
-handling scheme. Default is 'strict' meaning that encoding errors raise
-a UnicodeDecodeError. Other possible values are 'ignore' and 'replace'
-as well as any other name registered with codecs.register_error that is
-able to handle UnicodeDecodeErrors.
- -
encode(...)
S.encode([encoding[,errors]]) -> object

-Encodes S using the codec registered for encoding. encoding defaults
-to the default encoding. errors may be given to set a different error
-handling scheme. Default is 'strict' meaning that encoding errors raise
-a UnicodeEncodeError. Other possible values are 'ignore', 'replace' and
-'xmlcharrefreplace' as well as any other name registered with
-codecs.register_error that is able to handle UnicodeEncodeErrors.
- -
endswith(...)
S.endswith(suffix[, start[, end]]) -> bool

-Return True if S ends with the specified suffix, False otherwise.
-With optional start, test S beginning at that position.
-With optional end, stop comparing S at that position.
-suffix can also be a tuple of strings to try.
- -
expandtabs(...)
S.expandtabs([tabsize]) -> string

-Return a copy of S where all tab characters are expanded using spaces.
-If tabsize is not given, a tab size of 8 characters is assumed.
- -
find(...)
S.find(sub [,start [,end]]) -> int

-Return the lowest index in S where substring sub is found,
-such that sub is contained within S[start:end].  Optional
-arguments start and end are interpreted as in slice notation.

-Return -1 on failure.
- -
format(...)
S.format(*args, **kwargs) -> string

-Return a formatted version of S, using substitutions from args and kwargs.
-The substitutions are identified by braces ('{' and '}').
- -
index(...)
S.index(sub [,start [,end]]) -> int

-Like S.find() but raise ValueError when the substring is not found.
- -
isalnum(...)
S.isalnum() -> bool

-Return True if all characters in S are alphanumeric
-and there is at least one character in S, False otherwise.
- -
isalpha(...)
S.isalpha() -> bool

-Return True if all characters in S are alphabetic
-and there is at least one character in S, False otherwise.
- -
isdigit(...)
S.isdigit() -> bool

-Return True if all characters in S are digits
-and there is at least one character in S, False otherwise.
- -
islower(...)
S.islower() -> bool

-Return True if all cased characters in S are lowercase and there is
-at least one cased character in S, False otherwise.
- -
isspace(...)
S.isspace() -> bool

-Return True if all characters in S are whitespace
-and there is at least one character in S, False otherwise.
- -
istitle(...)
S.istitle() -> bool

-Return True if S is a titlecased string and there is at least one
-character in S, i.e. uppercase characters may only follow uncased
-characters and lowercase characters only cased ones. Return False
-otherwise.
- -
isupper(...)
S.isupper() -> bool

-Return True if all cased characters in S are uppercase and there is
-at least one cased character in S, False otherwise.
- -
join(...)
S.join(iterable) -> string

-Return a string which is the concatenation of the strings in the
-iterable.  The separator between elements is S.
- -
ljust(...)
S.ljust(width[, fillchar]) -> string

-Return S left-justified in a string of length width. Padding is
-done using the specified fill character (default is a space).
- -
lower(...)
S.lower() -> string

-Return a copy of the string S converted to lowercase.
- -
lstrip(...)
S.lstrip([chars]) -> string or unicode

-Return a copy of the string S with leading whitespace removed.
-If chars is given and not None, remove characters in chars instead.
-If chars is unicode, S will be converted to unicode before stripping
- -
partition(...)
S.partition(sep) -> (head, sep, tail)

-Search for the separator sep in S, and return the part before it,
-the separator itself, and the part after it.  If the separator is not
-found, return S and two empty strings.
- -
replace(...)
S.replace(old, new[, count]) -> string

-Return a copy of string S with all occurrences of substring
-old replaced by new.  If the optional argument count is
-given, only the first count occurrences are replaced.
- -
rfind(...)
S.rfind(sub [,start [,end]]) -> int

-Return the highest index in S where substring sub is found,
-such that sub is contained within S[start:end].  Optional
-arguments start and end are interpreted as in slice notation.

-Return -1 on failure.
- -
rindex(...)
S.rindex(sub [,start [,end]]) -> int

-Like S.rfind() but raise ValueError when the substring is not found.
- -
rjust(...)
S.rjust(width[, fillchar]) -> string

-Return S right-justified in a string of length width. Padding is
-done using the specified fill character (default is a space)
- -
rpartition(...)
S.rpartition(sep) -> (head, sep, tail)

-Search for the separator sep in S, starting at the end of S, and return
-the part before it, the separator itself, and the part after it.  If the
-separator is not found, return two empty strings and S.
- -
rsplit(...)
S.rsplit([sep [,maxsplit]]) -> list of strings

-Return a list of the words in the string S, using sep as the
-delimiter string, starting at the end of the string and working
-to the front.  If maxsplit is given, at most maxsplit splits are
-done. If sep is not specified or is None, any whitespace string
-is a separator.
- -
rstrip(...)
S.rstrip([chars]) -> string or unicode

-Return a copy of the string S with trailing whitespace removed.
-If chars is given and not None, remove characters in chars instead.
-If chars is unicode, S will be converted to unicode before stripping
- -
split(...)
S.split([sep [,maxsplit]]) -> list of strings

-Return a list of the words in the string S, using sep as the
-delimiter string.  If maxsplit is given, at most maxsplit
-splits are done. If sep is not specified or is None, any
-whitespace string is a separator and empty strings are removed
-from the result.
- -
splitlines(...)
S.splitlines(keepends=False) -> list of strings

-Return a list of the lines in S, breaking at line boundaries.
-Line breaks are not included in the resulting list unless keepends
-is given and true.
- -
startswith(...)
S.startswith(prefix[, start[, end]]) -> bool

-Return True if S starts with the specified prefix, False otherwise.
-With optional start, test S beginning at that position.
-With optional end, stop comparing S at that position.
-prefix can also be a tuple of strings to try.
- -
strip(...)
S.strip([chars]) -> string or unicode

-Return a copy of the string S with leading and trailing
-whitespace removed.
-If chars is given and not None, remove characters in chars instead.
-If chars is unicode, S will be converted to unicode before stripping
- -
swapcase(...)
S.swapcase() -> string

-Return a copy of the string S with uppercase characters
-converted to lowercase and vice versa.
- -
title(...)
S.title() -> string

-Return a titlecased version of S, i.e. words start with uppercase
-characters, all remaining cased characters have lowercase.
- -
translate(...)
S.translate(table [,deletechars]) -> string

-Return a copy of the string S, where all characters occurring
-in the optional argument deletechars are removed, and the
-remaining characters have been mapped through the given
-translation table, which must be a string of length 256 or None.
-If the table argument is None, no translation is applied and
-the operation simply removes the characters in deletechars.
- -
upper(...)
S.upper() -> string

-Return a copy of the string S converted to uppercase.
- -
zfill(...)
S.zfill(width) -> string

-Pad a numeric string S with zeros on the left, to fill a field
-of the specified width.  The string S is never truncated.
- -

- - - - - -
 
-Functions
       
debug_print(arg)
-
detect_lineno(s)
Detect the line number for a given token string
-
error_print(arg)
-
filter_out_attribute_keyword(stack)
Strips __attribute__ and its parenthetical expression from the stack
-
is_enum_namestack(nameStack)
Determines if a namestack is an enum namestack
-
is_function_pointer_stack(stack)
Count how many non-nested paranthesis are in the stack.  Useful for determining if a stack is a function pointer
-
is_fundamental(s)
-
is_method_namestack(stack)
-
is_namespace(nameStack)
Determines if a namespace is being specified
-
is_property_namestack(nameStack)
-
lineno()
Returns the current line number in our program.
-
standardize_fundamental(s)
-
t_COMMENT_MULTILINE(t)
/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/
-
t_COMMENT_SINGLELINE(t)
\/\/.*\n
-
t_NEWLINE(t)
\n+
-
t_error(v)
-
trace_print(*arg)
-
warning_print(arg)
-

- - - - - -
 
-Data
       C99_NONSTANDARD = {'int16': 'short int', 'int32': 'int', 'int64': 'int64_t', 'int8': 'signed char', 'uint': 'unsigned int', 'uint16': 'unsigned short int', 'uint32': 'unsigned int', 'uint64': 'uint64_t', 'uint8': 'unsigned char'}
-__version__ = '2.7.4'
-debug = 0
-debug_trace = 0
-doxygenCommentCache = ''
-ignoreSymbols = ['Q_OBJECT']
-parseHistory = []
-print_errors = 1
-print_warnings = 1
-supportedAccessSpecifier = ['public', 'protected', 'private']
-t_AMPERSTAND = '&'
-t_ASTERISK = r'\*'
-t_BACKSLASH = r'\\'
-t_CARET = r'\^'
-t_CHAR_LITERAL = "'.'"
-t_CLOSE_BRACE = '}'
-t_CLOSE_PAREN = r'\)'
-t_CLOSE_SQUARE_BRACKET = r'\]'
-t_COLON = ':'
-t_COMMA = ','
-t_DIVIDE = '/(?!/)'
-t_EQUALS = '='
-t_EXCLAMATION = '!'
-t_FLOAT_NUMBER = r'[-+]?[0-9]*\.[0-9]+([eE][-+]?[0-9]+)?'
-t_MINUS = r'\-'
-t_NAME = '[<>A-Za-z_~][A-Za-z0-9_]*'
-t_NUMBER = '[0-9][0-9XxA-Fa-f]*'
-t_OPEN_BRACE = '{'
-t_OPEN_PAREN = r'\('
-t_OPEN_SQUARE_BRACKET = r'\['
-t_PERCENT = '%'
-t_PIPE = r'\|'
-t_PLUS = r'\+'
-t_PRECOMP_MACRO = r'\#.*'
-t_PRECOMP_MACRO_CONT = r'.*\\\n'
-t_SEMI_COLON = ';'
-t_SQUOTE = "'"
-t_STRING_LITERAL = r'"([^"\\]|\\.)*"'
-t_TAB = r'\t'
-t_TEMPLATE_NAME = 'CppHeaderParser_template_[0-9]+'
-t_ignore = ' \r.?@\x0c'
-tokens = ['NUMBER', 'FLOAT_NUMBER', 'TEMPLATE_NAME', 'NAME', 'OPEN_PAREN', 'CLOSE_PAREN', 'OPEN_BRACE', 'CLOSE_BRACE', 'OPEN_SQUARE_BRACKET', 'CLOSE_SQUARE_BRACKET', 'COLON', 'SEMI_COLON', 'COMMA', 'TAB', 'BACKSLASH', 'PIPE', 'PERCENT', 'EXCLAMATION', 'CARET', 'COMMENT_SINGLELINE', ...]
-version = '2.7.4'
- \ No newline at end of file diff --git a/README.html b/README.html deleted file mode 100644 index 8b43596..0000000 --- a/README.html +++ /dev/null @@ -1,598 +0,0 @@ - - - - - - -Codestin Search App - - - -

- - -
-

Python package "CppHeaderParser"

-

Purpose: Parse C++ header files and generate a data structure representing the class

-

Author: Jashua Cloutier

-

Licence: BSD

-

External modules required: PLY

-

Quick start:

-
-#include <vector>
-#include <string>
-
-#define DEF_1 1
-#define OS_NAME "Linux"
-
-using namespace std;
-class SampleClass
-{
-public:
-    SampleClass();
-    /*!
-     * Method 1
-     */
-    string meth1();
-
-    ///
-    /// Method 2 description
-    ///
-    /// @param v1 Variable 1
-    ///
-    int meth2(int v1);
-
-    /**
-     * Method 3 description
-     *
-     * \param v1 Variable 1
-     * \param v2 Variable 2
-     */
-    void meth3(const string & v1, vector<string> & v2);
-
-    /**********************************
-     * Method 4 description
-     *
-     * @return Return value
-     *********************************/
-    unsigned int meth4();
-private:
-    void * meth5(){return NULL};
-
-    /// prop1 description
-    string prop1;
-    //! prop5 description
-    int prop5;
-};
-namespace Alpha
-{
-    class AlphaClass
-    {
-    public:
-        AlphaClass();
-
-        void alphaMethod();
-
-        string alphaString;
-    };
-
-    namespace Omega
-    {
-        class OmegaClass
-        {
-        public:
-            OmegaClass();
-
-            string omegaString;
-        };
-    };
-}
-
-int sampleFreeFunction(int i)
-{
-    return i + 1;
-}
-
-int anotherFreeFunction(void);
-}
-
-

Python code:

-
-#!/usr/bin/python
-import sys
-sys.path = ["../"] + sys.path
-import CppHeaderParser
-try:
-    cppHeader = CppHeaderParser.CppHeader("SampleClass.h")
-except CppHeaderParser.CppParseError as e:
-    print(e)
-    sys.exit(1)
-
-print("CppHeaderParser view of %s"%cppHeader)
-
-sampleClass = cppHeader.classes["SampleClass"]
-print("Number of public methods %d"%(len(sampleClass["methods"]["public"])))
-print("Number of private properties %d"%(len(sampleClass["properties"]["private"])))
-meth3 = [m for m in sampleClass["methods"]["public"] if m["name"] == "meth3"][0] #get meth3
-meth3ParamTypes = [t["type"] for t in meth3["parameters"]] #get meth3s parameters
-print("Parameter Types for public method meth3 %s"%(meth3ParamTypes))
-
-print("\nReturn type for meth1:")
-print(cppHeader.classes["SampleClass"]["methods"]["public"][1]["rtnType"])
-
-print("\nDoxygen for meth2:")
-print(cppHeader.classes["SampleClass"]["methods"]["public"][2]["doxygen"])
-
-print("\nParameters for meth3:")
-print(cppHeader.classes["SampleClass"]["methods"]["public"][3]["parameters"])
-
-print("\nDoxygen for meth4:")
-print(cppHeader.classes["SampleClass"]["methods"]["public"][4]["doxygen"])
-
-print("\nReturn type for meth5:")
-print(cppHeader.classes["SampleClass"]["methods"]["private"][0]["rtnType"])
-
-print("\nDoxygen type for prop1:")
-print(cppHeader.classes["SampleClass"]["properties"]["private"][0]["doxygen"])
-
-print("\nType for prop5:")
-print(cppHeader.classes["SampleClass"]["properties"]["private"][1]["type"])
-
-print("\nNamespace for AlphaClass is:")
-print(cppHeader.classes["AlphaClass"]["namespace"])
-
-print("\nReturn type for alphaMethod is:")
-print(cppHeader.classes["AlphaClass"]["methods"]["public"][0]["rtnType"])
-
-print("\nNamespace for OmegaClass is:")
-print(cppHeader.classes["OmegaClass"]["namespace"])
-
-print("\nType for omegaString is:")
-print(cppHeader.classes["AlphaClass"]["properties"]["public"][0]["type"])
-
-print("\nFree functions are:")
-for func in cppHeader.functions:
-    print(" %s"%func["name"])
-
-print("\n#includes are:")
-for incl in cppHeader.includes:
-    print(" %s"%incl)
-
-print("\n#defines are:")
-for define in cppHeader.defines:
-    print(" %s"%define)
-
-

Output:

-
-CppHeaderParser view of class SampleClass
-{
-public
-    // Methods
-   {'line_number': 11, 'parent': {'inherits': [], 'line_number': 8, 'forward_declares': {'protected': [], 'public': [], 'private': []}, 'name': 'SampleClass', 'parent': None, 'abstract': False, 'namespace': '', 'declaration_method': 'class', 'properties': {'protected': [], 'public': [], 'private': [{'line_number': 42, 'constant': 0, 'reference': 0, 'raw_type': 'string', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': ['string'], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'string', 'property_of_class': 'SampleClass', 'parent': None, 'unresolved': True, 'typedefs': 0, 'extern': False, 'class': 0, 'doxygen': '/// prop1 description', 'name': 'prop1', 'fundamental': 0}, {'line_number': 44, 'constant': 0, 'reference': 0, 'raw_type': 'int', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': [], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'int', 'property_of_class': 'SampleClass', 'parent': None, 'ctypes_type': 'ctypes.c_int', 'unresolved': False, 'typedefs': 0, 'extern': False, 'class': 0, 'doxygen': '//! prop5 description', 'name': 'prop5', 'fundamental': True}]}, 'typedefs': {'protected': [], 'public': [], 'private': []}, 'structs': {'protected': [], 'public': [], 'private': []}, 'enums': {'protected': [], 'public': [], 'private': []}, 'final': False, 'nested_classes': [], 'methods': {'protected': [], 'public': [{...}, {'line_number': 15, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': False, 'rtnType': 'string', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'returns_unknown': True, 'doxygen': '/*!\n* Method 1\n*/', 'const': False, 'name': 'meth1', 'pure_virtual': False, 'debug': 'string meth1 ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'string', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {'line_number': 22, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'int', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [{'line_number': 22, 'constant': 0, 'reference': 0, 'raw_type': 'int', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': [], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'int', 'method': {...}, 'parent': None, 'ctypes_type': 'ctypes.c_int', 'typedefs': 0, 'extern': False, 'class': 0, 'desc': 'Variable 1', 'unresolved': False, 'name': 'v1', 'fundamental': True}], 'class': None, 'returns_reference': False, 'doxygen': '///\n/// Method 2 description\n///\n/// @param v1 Variable 1\n///', 'const': False, 'name': 'meth2', 'pure_virtual': False, 'debug': 'int meth2 ( int v1 ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'int', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {'line_number': 30, 'unresolved_parameters': True, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [{'line_number': 30, 'constant': 1, 'reference': 1, 'raw_type': 'string', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': ['string'], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'const string &', 'method': {...}, 'parent': None, 'typedefs': 0, 'extern': False, 'class': 0, 'desc': 'Variable 1', 'unresolved': True, 'name': 'v1', 'fundamental': 0}, {'line_number': 30, 'constant': 0, 'reference': 1, 'raw_type': 'vector<string>', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': ['vector<string>'], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'template': 'vector<string>', 'mutable': False, 'type': 'vector<string> &', 'method': {...}, 'parent': None, 'ctypes_type': 'ctypes.c_void_p', 'typedefs': 0, 'extern': False, 'class': 0, 'desc': 'Variable 2', 'unresolved': True, 'name': 'v2', 'fundamental': 0}], 'class': None, 'returns_reference': False, 'doxygen': '/**\n* Method 3 description\n*\n* \\param v1 Variable 1\n* \\param v2 Variable 2\n*/', 'const': False, 'name': 'meth3', 'pure_virtual': False, 'debug': 'void meth3 ( const string & v1 , vector <string> & v2 ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'void', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {'line_number': 37, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'unsigned int', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'doxygen': '/**********************************\n* Method 4 description\n*\n* @return Return value\n*********************************/', 'const': False, 'name': 'meth4', 'pure_virtual': False, 'debug': 'unsigned int meth4 ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'unsigned int', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}], 'private': [{'line_number': 39, 'parent': {...}, 'defined': True, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void *', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 1, 'parameters': [], 'class': None, 'returns_reference': False, 'const': False, 'name': 'meth5', 'pure_virtual': False, 'debug': 'void * meth5 ( ) {', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'void', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}]}}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'const': False, 'name': 'SampleClass', 'pure_virtual': False, 'debug': 'SampleClass ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': '', 'template': False, 'constructor': True, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}
-   {'line_number': 15, 'parent': {'inherits': [], 'line_number': 8, 'forward_declares': {'protected': [], 'public': [], 'private': []}, 'name': 'SampleClass', 'parent': None, 'abstract': False, 'namespace': '', 'declaration_method': 'class', 'properties': {'protected': [], 'public': [], 'private': [{'line_number': 42, 'constant': 0, 'reference': 0, 'raw_type': 'string', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': ['string'], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'string', 'property_of_class': 'SampleClass', 'parent': None, 'unresolved': True, 'typedefs': 0, 'extern': False, 'class': 0, 'doxygen': '/// prop1 description', 'name': 'prop1', 'fundamental': 0}, {'line_number': 44, 'constant': 0, 'reference': 0, 'raw_type': 'int', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': [], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'int', 'property_of_class': 'SampleClass', 'parent': None, 'ctypes_type': 'ctypes.c_int', 'unresolved': False, 'typedefs': 0, 'extern': False, 'class': 0, 'doxygen': '//! prop5 description', 'name': 'prop5', 'fundamental': True}]}, 'typedefs': {'protected': [], 'public': [], 'private': []}, 'structs': {'protected': [], 'public': [], 'private': []}, 'enums': {'protected': [], 'public': [], 'private': []}, 'final': False, 'nested_classes': [], 'methods': {'protected': [], 'public': [{'line_number': 11, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'const': False, 'name': 'SampleClass', 'pure_virtual': False, 'debug': 'SampleClass ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': '', 'template': False, 'constructor': True, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {...}, {'line_number': 22, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'int', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [{'line_number': 22, 'constant': 0, 'reference': 0, 'raw_type': 'int', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': [], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'int', 'method': {...}, 'parent': None, 'ctypes_type': 'ctypes.c_int', 'typedefs': 0, 'extern': False, 'class': 0, 'desc': 'Variable 1', 'unresolved': False, 'name': 'v1', 'fundamental': True}], 'class': None, 'returns_reference': False, 'doxygen': '///\n/// Method 2 description\n///\n/// @param v1 Variable 1\n///', 'const': False, 'name': 'meth2', 'pure_virtual': False, 'debug': 'int meth2 ( int v1 ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'int', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {'line_number': 30, 'unresolved_parameters': True, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [{'line_number': 30, 'constant': 1, 'reference': 1, 'raw_type': 'string', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': ['string'], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'const string &', 'method': {...}, 'parent': None, 'typedefs': 0, 'extern': False, 'class': 0, 'desc': 'Variable 1', 'unresolved': True, 'name': 'v1', 'fundamental': 0}, {'line_number': 30, 'constant': 0, 'reference': 1, 'raw_type': 'vector<string>', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': ['vector<string>'], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'template': 'vector<string>', 'mutable': False, 'type': 'vector<string> &', 'method': {...}, 'parent': None, 'ctypes_type': 'ctypes.c_void_p', 'typedefs': 0, 'extern': False, 'class': 0, 'desc': 'Variable 2', 'unresolved': True, 'name': 'v2', 'fundamental': 0}], 'class': None, 'returns_reference': False, 'doxygen': '/**\n* Method 3 description\n*\n* \\param v1 Variable 1\n* \\param v2 Variable 2\n*/', 'const': False, 'name': 'meth3', 'pure_virtual': False, 'debug': 'void meth3 ( const string & v1 , vector <string> & v2 ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'void', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {'line_number': 37, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'unsigned int', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'doxygen': '/**********************************\n* Method 4 description\n*\n* @return Return value\n*********************************/', 'const': False, 'name': 'meth4', 'pure_virtual': False, 'debug': 'unsigned int meth4 ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'unsigned int', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}], 'private': [{'line_number': 39, 'parent': {...}, 'defined': True, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void *', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 1, 'parameters': [], 'class': None, 'returns_reference': False, 'const': False, 'name': 'meth5', 'pure_virtual': False, 'debug': 'void * meth5 ( ) {', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'void', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}]}}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': False, 'rtnType': 'string', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'returns_unknown': True, 'doxygen': '/*!\n* Method 1\n*/', 'const': False, 'name': 'meth1', 'pure_virtual': False, 'debug': 'string meth1 ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'string', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}
-   {'line_number': 22, 'parent': {'inherits': [], 'line_number': 8, 'forward_declares': {'protected': [], 'public': [], 'private': []}, 'name': 'SampleClass', 'parent': None, 'abstract': False, 'namespace': '', 'declaration_method': 'class', 'properties': {'protected': [], 'public': [], 'private': [{'line_number': 42, 'constant': 0, 'reference': 0, 'raw_type': 'string', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': ['string'], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'string', 'property_of_class': 'SampleClass', 'parent': None, 'unresolved': True, 'typedefs': 0, 'extern': False, 'class': 0, 'doxygen': '/// prop1 description', 'name': 'prop1', 'fundamental': 0}, {'line_number': 44, 'constant': 0, 'reference': 0, 'raw_type': 'int', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': [], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'int', 'property_of_class': 'SampleClass', 'parent': None, 'ctypes_type': 'ctypes.c_int', 'unresolved': False, 'typedefs': 0, 'extern': False, 'class': 0, 'doxygen': '//! prop5 description', 'name': 'prop5', 'fundamental': True}]}, 'typedefs': {'protected': [], 'public': [], 'private': []}, 'structs': {'protected': [], 'public': [], 'private': []}, 'enums': {'protected': [], 'public': [], 'private': []}, 'final': False, 'nested_classes': [], 'methods': {'protected': [], 'public': [{'line_number': 11, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'const': False, 'name': 'SampleClass', 'pure_virtual': False, 'debug': 'SampleClass ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': '', 'template': False, 'constructor': True, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {'line_number': 15, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': False, 'rtnType': 'string', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'returns_unknown': True, 'doxygen': '/*!\n* Method 1\n*/', 'const': False, 'name': 'meth1', 'pure_virtual': False, 'debug': 'string meth1 ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'string', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {...}, {'line_number': 30, 'unresolved_parameters': True, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [{'line_number': 30, 'constant': 1, 'reference': 1, 'raw_type': 'string', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': ['string'], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'const string &', 'method': {...}, 'parent': None, 'typedefs': 0, 'extern': False, 'class': 0, 'desc': 'Variable 1', 'unresolved': True, 'name': 'v1', 'fundamental': 0}, {'line_number': 30, 'constant': 0, 'reference': 1, 'raw_type': 'vector<string>', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': ['vector<string>'], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'template': 'vector<string>', 'mutable': False, 'type': 'vector<string> &', 'method': {...}, 'parent': None, 'ctypes_type': 'ctypes.c_void_p', 'typedefs': 0, 'extern': False, 'class': 0, 'desc': 'Variable 2', 'unresolved': True, 'name': 'v2', 'fundamental': 0}], 'class': None, 'returns_reference': False, 'doxygen': '/**\n* Method 3 description\n*\n* \\param v1 Variable 1\n* \\param v2 Variable 2\n*/', 'const': False, 'name': 'meth3', 'pure_virtual': False, 'debug': 'void meth3 ( const string & v1 , vector <string> & v2 ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'void', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {'line_number': 37, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'unsigned int', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'doxygen': '/**********************************\n* Method 4 description\n*\n* @return Return value\n*********************************/', 'const': False, 'name': 'meth4', 'pure_virtual': False, 'debug': 'unsigned int meth4 ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'unsigned int', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}], 'private': [{'line_number': 39, 'parent': {...}, 'defined': True, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void *', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 1, 'parameters': [], 'class': None, 'returns_reference': False, 'const': False, 'name': 'meth5', 'pure_virtual': False, 'debug': 'void * meth5 ( ) {', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'void', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}]}}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'int', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [{'line_number': 22, 'constant': 0, 'reference': 0, 'raw_type': 'int', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': [], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'int', 'method': {...}, 'parent': None, 'ctypes_type': 'ctypes.c_int', 'typedefs': 0, 'extern': False, 'class': 0, 'desc': 'Variable 1', 'unresolved': False, 'name': 'v1', 'fundamental': True}], 'class': None, 'returns_reference': False, 'doxygen': '///\n/// Method 2 description\n///\n/// @param v1 Variable 1\n///', 'const': False, 'name': 'meth2', 'pure_virtual': False, 'debug': 'int meth2 ( int v1 ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'int', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}
-   {'line_number': 30, 'unresolved_parameters': True, 'parent': {'inherits': [], 'line_number': 8, 'forward_declares': {'protected': [], 'public': [], 'private': []}, 'name': 'SampleClass', 'parent': None, 'abstract': False, 'namespace': '', 'declaration_method': 'class', 'properties': {'protected': [], 'public': [], 'private': [{'line_number': 42, 'constant': 0, 'reference': 0, 'raw_type': 'string', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': ['string'], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'string', 'property_of_class': 'SampleClass', 'parent': None, 'unresolved': True, 'typedefs': 0, 'extern': False, 'class': 0, 'doxygen': '/// prop1 description', 'name': 'prop1', 'fundamental': 0}, {'line_number': 44, 'constant': 0, 'reference': 0, 'raw_type': 'int', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': [], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'int', 'property_of_class': 'SampleClass', 'parent': None, 'ctypes_type': 'ctypes.c_int', 'unresolved': False, 'typedefs': 0, 'extern': False, 'class': 0, 'doxygen': '//! prop5 description', 'name': 'prop5', 'fundamental': True}]}, 'typedefs': {'protected': [], 'public': [], 'private': []}, 'structs': {'protected': [], 'public': [], 'private': []}, 'enums': {'protected': [], 'public': [], 'private': []}, 'final': False, 'nested_classes': [], 'methods': {'protected': [], 'public': [{'line_number': 11, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'const': False, 'name': 'SampleClass', 'pure_virtual': False, 'debug': 'SampleClass ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': '', 'template': False, 'constructor': True, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {'line_number': 15, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': False, 'rtnType': 'string', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'returns_unknown': True, 'doxygen': '/*!\n* Method 1\n*/', 'const': False, 'name': 'meth1', 'pure_virtual': False, 'debug': 'string meth1 ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'string', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {'line_number': 22, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'int', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [{'line_number': 22, 'constant': 0, 'reference': 0, 'raw_type': 'int', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': [], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'int', 'method': {...}, 'parent': None, 'ctypes_type': 'ctypes.c_int', 'typedefs': 0, 'extern': False, 'class': 0, 'desc': 'Variable 1', 'unresolved': False, 'name': 'v1', 'fundamental': True}], 'class': None, 'returns_reference': False, 'doxygen': '///\n/// Method 2 description\n///\n/// @param v1 Variable 1\n///', 'const': False, 'name': 'meth2', 'pure_virtual': False, 'debug': 'int meth2 ( int v1 ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'int', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {...}, {'line_number': 37, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'unsigned int', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'doxygen': '/**********************************\n* Method 4 description\n*\n* @return Return value\n*********************************/', 'const': False, 'name': 'meth4', 'pure_virtual': False, 'debug': 'unsigned int meth4 ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'unsigned int', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}], 'private': [{'line_number': 39, 'parent': {...}, 'defined': True, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void *', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 1, 'parameters': [], 'class': None, 'returns_reference': False, 'const': False, 'name': 'meth5', 'pure_virtual': False, 'debug': 'void * meth5 ( ) {', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'void', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}]}}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [{'line_number': 30, 'constant': 1, 'reference': 1, 'raw_type': 'string', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': ['string'], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'const string &', 'method': {...}, 'parent': None, 'typedefs': 0, 'extern': False, 'class': 0, 'desc': 'Variable 1', 'unresolved': True, 'name': 'v1', 'fundamental': 0}, {'line_number': 30, 'constant': 0, 'reference': 1, 'raw_type': 'vector<string>', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': ['vector<string>'], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'template': 'vector<string>', 'mutable': False, 'type': 'vector<string> &', 'method': {...}, 'parent': None, 'ctypes_type': 'ctypes.c_void_p', 'typedefs': 0, 'extern': False, 'class': 0, 'desc': 'Variable 2', 'unresolved': True, 'name': 'v2', 'fundamental': 0}], 'class': None, 'returns_reference': False, 'doxygen': '/**\n* Method 3 description\n*\n* \\param v1 Variable 1\n* \\param v2 Variable 2\n*/', 'const': False, 'name': 'meth3', 'pure_virtual': False, 'debug': 'void meth3 ( const string & v1 , vector <string> & v2 ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'void', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}
-   {'line_number': 37, 'parent': {'inherits': [], 'line_number': 8, 'forward_declares': {'protected': [], 'public': [], 'private': []}, 'name': 'SampleClass', 'parent': None, 'abstract': False, 'namespace': '', 'declaration_method': 'class', 'properties': {'protected': [], 'public': [], 'private': [{'line_number': 42, 'constant': 0, 'reference': 0, 'raw_type': 'string', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': ['string'], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'string', 'property_of_class': 'SampleClass', 'parent': None, 'unresolved': True, 'typedefs': 0, 'extern': False, 'class': 0, 'doxygen': '/// prop1 description', 'name': 'prop1', 'fundamental': 0}, {'line_number': 44, 'constant': 0, 'reference': 0, 'raw_type': 'int', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': [], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'int', 'property_of_class': 'SampleClass', 'parent': None, 'ctypes_type': 'ctypes.c_int', 'unresolved': False, 'typedefs': 0, 'extern': False, 'class': 0, 'doxygen': '//! prop5 description', 'name': 'prop5', 'fundamental': True}]}, 'typedefs': {'protected': [], 'public': [], 'private': []}, 'structs': {'protected': [], 'public': [], 'private': []}, 'enums': {'protected': [], 'public': [], 'private': []}, 'final': False, 'nested_classes': [], 'methods': {'protected': [], 'public': [{'line_number': 11, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'const': False, 'name': 'SampleClass', 'pure_virtual': False, 'debug': 'SampleClass ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': '', 'template': False, 'constructor': True, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {'line_number': 15, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': False, 'rtnType': 'string', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'returns_unknown': True, 'doxygen': '/*!\n* Method 1\n*/', 'const': False, 'name': 'meth1', 'pure_virtual': False, 'debug': 'string meth1 ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'string', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {'line_number': 22, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'int', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [{'line_number': 22, 'constant': 0, 'reference': 0, 'raw_type': 'int', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': [], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'int', 'method': {...}, 'parent': None, 'ctypes_type': 'ctypes.c_int', 'typedefs': 0, 'extern': False, 'class': 0, 'desc': 'Variable 1', 'unresolved': False, 'name': 'v1', 'fundamental': True}], 'class': None, 'returns_reference': False, 'doxygen': '///\n/// Method 2 description\n///\n/// @param v1 Variable 1\n///', 'const': False, 'name': 'meth2', 'pure_virtual': False, 'debug': 'int meth2 ( int v1 ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'int', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {'line_number': 30, 'unresolved_parameters': True, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [{'line_number': 30, 'constant': 1, 'reference': 1, 'raw_type': 'string', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': ['string'], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'const string &', 'method': {...}, 'parent': None, 'typedefs': 0, 'extern': False, 'class': 0, 'desc': 'Variable 1', 'unresolved': True, 'name': 'v1', 'fundamental': 0}, {'line_number': 30, 'constant': 0, 'reference': 1, 'raw_type': 'vector<string>', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': ['vector<string>'], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'template': 'vector<string>', 'mutable': False, 'type': 'vector<string> &', 'method': {...}, 'parent': None, 'ctypes_type': 'ctypes.c_void_p', 'typedefs': 0, 'extern': False, 'class': 0, 'desc': 'Variable 2', 'unresolved': True, 'name': 'v2', 'fundamental': 0}], 'class': None, 'returns_reference': False, 'doxygen': '/**\n* Method 3 description\n*\n* \\param v1 Variable 1\n* \\param v2 Variable 2\n*/', 'const': False, 'name': 'meth3', 'pure_virtual': False, 'debug': 'void meth3 ( const string & v1 , vector <string> & v2 ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'void', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {...}], 'private': [{'line_number': 39, 'parent': {...}, 'defined': True, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void *', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 1, 'parameters': [], 'class': None, 'returns_reference': False, 'const': False, 'name': 'meth5', 'pure_virtual': False, 'debug': 'void * meth5 ( ) {', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'void', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}]}}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'unsigned int', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'doxygen': '/**********************************\n* Method 4 description\n*\n* @return Return value\n*********************************/', 'const': False, 'name': 'meth4', 'pure_virtual': False, 'debug': 'unsigned int meth4 ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'unsigned int', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}
-protected
-private
-    // Properties
-    {'line_number': 42, 'constant': 0, 'reference': 0, 'raw_type': 'string', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': ['string'], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'string', 'property_of_class': 'SampleClass', 'parent': None, 'unresolved': True, 'typedefs': 0, 'extern': False, 'class': 0, 'doxygen': '/// prop1 description', 'name': 'prop1', 'fundamental': 0}
-    {'line_number': 44, 'constant': 0, 'reference': 0, 'raw_type': 'int', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': [], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'int', 'property_of_class': 'SampleClass', 'parent': None, 'ctypes_type': 'ctypes.c_int', 'unresolved': False, 'typedefs': 0, 'extern': False, 'class': 0, 'doxygen': '//! prop5 description', 'name': 'prop5', 'fundamental': True}
-    // Methods
-   {'line_number': 39, 'parent': {'inherits': [], 'line_number': 8, 'forward_declares': {'protected': [], 'public': [], 'private': []}, 'name': 'SampleClass', 'parent': None, 'abstract': False, 'namespace': '', 'declaration_method': 'class', 'properties': {'protected': [], 'public': [], 'private': [{'line_number': 42, 'constant': 0, 'reference': 0, 'raw_type': 'string', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': ['string'], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'string', 'property_of_class': 'SampleClass', 'parent': None, 'unresolved': True, 'typedefs': 0, 'extern': False, 'class': 0, 'doxygen': '/// prop1 description', 'name': 'prop1', 'fundamental': 0}, {'line_number': 44, 'constant': 0, 'reference': 0, 'raw_type': 'int', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': [], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'int', 'property_of_class': 'SampleClass', 'parent': None, 'ctypes_type': 'ctypes.c_int', 'unresolved': False, 'typedefs': 0, 'extern': False, 'class': 0, 'doxygen': '//! prop5 description', 'name': 'prop5', 'fundamental': True}]}, 'typedefs': {'protected': [], 'public': [], 'private': []}, 'structs': {'protected': [], 'public': [], 'private': []}, 'enums': {'protected': [], 'public': [], 'private': []}, 'final': False, 'nested_classes': [], 'methods': {'protected': [], 'public': [{'line_number': 11, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'const': False, 'name': 'SampleClass', 'pure_virtual': False, 'debug': 'SampleClass ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': '', 'template': False, 'constructor': True, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {'line_number': 15, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': False, 'rtnType': 'string', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'returns_unknown': True, 'doxygen': '/*!\n* Method 1\n*/', 'const': False, 'name': 'meth1', 'pure_virtual': False, 'debug': 'string meth1 ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'string', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {'line_number': 22, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'int', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [{'line_number': 22, 'constant': 0, 'reference': 0, 'raw_type': 'int', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': [], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'int', 'method': {...}, 'parent': None, 'ctypes_type': 'ctypes.c_int', 'typedefs': 0, 'extern': False, 'class': 0, 'desc': 'Variable 1', 'unresolved': False, 'name': 'v1', 'fundamental': True}], 'class': None, 'returns_reference': False, 'doxygen': '///\n/// Method 2 description\n///\n/// @param v1 Variable 1\n///', 'const': False, 'name': 'meth2', 'pure_virtual': False, 'debug': 'int meth2 ( int v1 ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'int', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {'line_number': 30, 'unresolved_parameters': True, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [{'line_number': 30, 'constant': 1, 'reference': 1, 'raw_type': 'string', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': ['string'], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'const string &', 'method': {...}, 'parent': None, 'typedefs': 0, 'extern': False, 'class': 0, 'desc': 'Variable 1', 'unresolved': True, 'name': 'v1', 'fundamental': 0}, {'line_number': 30, 'constant': 0, 'reference': 1, 'raw_type': 'vector<string>', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': ['vector<string>'], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'template': 'vector<string>', 'mutable': False, 'type': 'vector<string> &', 'method': {...}, 'parent': None, 'ctypes_type': 'ctypes.c_void_p', 'typedefs': 0, 'extern': False, 'class': 0, 'desc': 'Variable 2', 'unresolved': True, 'name': 'v2', 'fundamental': 0}], 'class': None, 'returns_reference': False, 'doxygen': '/**\n* Method 3 description\n*\n* \\param v1 Variable 1\n* \\param v2 Variable 2\n*/', 'const': False, 'name': 'meth3', 'pure_virtual': False, 'debug': 'void meth3 ( const string & v1 , vector <string> & v2 ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'void', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {'line_number': 37, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'unsigned int', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'doxygen': '/**********************************\n* Method 4 description\n*\n* @return Return value\n*********************************/', 'const': False, 'name': 'meth4', 'pure_virtual': False, 'debug': 'unsigned int meth4 ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'unsigned int', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}], 'private': [{...}]}}, 'defined': True, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void *', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 1, 'parameters': [], 'class': None, 'returns_reference': False, 'const': False, 'name': 'meth5', 'pure_virtual': False, 'debug': 'void * meth5 ( ) {', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'void', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}
-}
-
-class Alpha::AlphaClass
-{
-public
-    // Properties
-    {'raw_type': 'string', 'line_number': 55, 'typedef': None, 'unresolved': True, 'constant': 0, 'name': 'alphaString', 'parent': None, 'pointer': 0, 'namespace': 'Alpha::', 'function_pointer': 0, 'property_of_class': 'AlphaClass', 'static': 0, 'fundamental': 0, 'mutable': False, 'extern': False, 'typedefs': 0, 'array': 0, 'type': 'string', 'class': 0, 'reference': 0, 'aliases': ['string']}
-    // Methods
-   {'line_number': 51, 'parent': {'inherits': [], 'line_number': 48, 'forward_declares': {'protected': [], 'public': [], 'private': []}, 'name': 'AlphaClass', 'parent': None, 'abstract': False, 'namespace': 'Alpha', 'declaration_method': 'class', 'properties': {'protected': [], 'public': [{'raw_type': 'string', 'line_number': 55, 'typedef': None, 'unresolved': True, 'constant': 0, 'name': 'alphaString', 'parent': None, 'pointer': 0, 'namespace': 'Alpha::', 'function_pointer': 0, 'property_of_class': 'AlphaClass', 'static': 0, 'fundamental': 0, 'mutable': False, 'extern': False, 'typedefs': 0, 'array': 0, 'type': 'string', 'class': 0, 'reference': 0, 'aliases': ['string']}], 'private': []}, 'typedefs': {'protected': [], 'public': [], 'private': []}, 'structs': {'protected': [], 'public': [], 'private': []}, 'enums': {'protected': [], 'public': [], 'private': []}, 'final': False, 'nested_classes': [], 'methods': {'protected': [], 'public': [{...}, {'line_number': 53, 'parent': {...}, 'defined': False, 'namespace': 'Alpha::', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void', 'extern': False, 'path': 'Alpha::AlphaClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'const': False, 'name': 'alphaMethod', 'pure_virtual': False, 'debug': 'void alphaMethod ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'void', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}], 'private': []}}, 'defined': False, 'namespace': 'Alpha::', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void', 'extern': False, 'path': 'Alpha::AlphaClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'const': False, 'name': 'AlphaClass', 'pure_virtual': False, 'debug': 'AlphaClass ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': '', 'template': False, 'constructor': True, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}
-   {'line_number': 53, 'parent': {'inherits': [], 'line_number': 48, 'forward_declares': {'protected': [], 'public': [], 'private': []}, 'name': 'AlphaClass', 'parent': None, 'abstract': False, 'namespace': 'Alpha', 'declaration_method': 'class', 'properties': {'protected': [], 'public': [{'raw_type': 'string', 'line_number': 55, 'typedef': None, 'unresolved': True, 'constant': 0, 'name': 'alphaString', 'parent': None, 'pointer': 0, 'namespace': 'Alpha::', 'function_pointer': 0, 'property_of_class': 'AlphaClass', 'static': 0, 'fundamental': 0, 'mutable': False, 'extern': False, 'typedefs': 0, 'array': 0, 'type': 'string', 'class': 0, 'reference': 0, 'aliases': ['string']}], 'private': []}, 'typedefs': {'protected': [], 'public': [], 'private': []}, 'structs': {'protected': [], 'public': [], 'private': []}, 'enums': {'protected': [], 'public': [], 'private': []}, 'final': False, 'nested_classes': [], 'methods': {'protected': [], 'public': [{'line_number': 51, 'parent': {...}, 'defined': False, 'namespace': 'Alpha::', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void', 'extern': False, 'path': 'Alpha::AlphaClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'const': False, 'name': 'AlphaClass', 'pure_virtual': False, 'debug': 'AlphaClass ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': '', 'template': False, 'constructor': True, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {...}], 'private': []}}, 'defined': False, 'namespace': 'Alpha::', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void', 'extern': False, 'path': 'Alpha::AlphaClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'const': False, 'name': 'alphaMethod', 'pure_virtual': False, 'debug': 'void alphaMethod ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'void', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}
-protected
-private
-}
-
-class Alpha::Omega::OmegaClass
-{
-public
-    // Properties
-    {'raw_type': 'string', 'line_number': 65, 'typedef': None, 'unresolved': True, 'constant': 0, 'name': 'omegaString', 'parent': None, 'pointer': 0, 'namespace': 'Alpha::Omega::', 'function_pointer': 0, 'property_of_class': 'OmegaClass', 'static': 0, 'fundamental': 0, 'mutable': False, 'extern': False, 'typedefs': 0, 'array': 0, 'type': 'string', 'class': 0, 'reference': 0, 'aliases': ['string']}
-    // Methods
-   {'line_number': 63, 'parent': {'inherits': [], 'line_number': 60, 'forward_declares': {'protected': [], 'public': [], 'private': []}, 'name': 'OmegaClass', 'parent': None, 'abstract': False, 'namespace': 'Alpha::Omega', 'declaration_method': 'class', 'properties': {'protected': [], 'public': [{'raw_type': 'string', 'line_number': 65, 'typedef': None, 'unresolved': True, 'constant': 0, 'name': 'omegaString', 'parent': None, 'pointer': 0, 'namespace': 'Alpha::Omega::', 'function_pointer': 0, 'property_of_class': 'OmegaClass', 'static': 0, 'fundamental': 0, 'mutable': False, 'extern': False, 'typedefs': 0, 'array': 0, 'type': 'string', 'class': 0, 'reference': 0, 'aliases': ['string']}], 'private': []}, 'typedefs': {'protected': [], 'public': [], 'private': []}, 'structs': {'protected': [], 'public': [], 'private': []}, 'enums': {'protected': [], 'public': [], 'private': []}, 'final': False, 'nested_classes': [], 'methods': {'protected': [], 'public': [{...}], 'private': []}}, 'defined': False, 'namespace': 'Alpha::Omega::', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void', 'extern': False, 'path': 'Alpha::Omega::OmegaClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'const': False, 'name': 'OmegaClass', 'pure_virtual': False, 'debug': 'OmegaClass ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': '', 'template': False, 'constructor': True, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}
-protected
-private
-}
-
-// functions
-{'line_number': 70, 'static': False, 'rtnType': 'int', 'const': False, 'parameters': [{'line_number': 70, 'constant': 0, 'reference': 0, 'raw_type': 'int', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': [], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'int', 'method': {'line_number': 70, 'defined': True, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'int', 'extern': False, 'returns_pointer': 0, 'parameters': [...], 'class': None, 'returns_reference': False, 'const': False, 'name': 'sampleFreeFunction', 'pure_virtual': False, 'debug': 'int sampleFreeFunction ( int i ) {', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'int', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, 'parent': None, 'ctypes_type': 'ctypes.c_int', 'typedefs': 0, 'extern': False, 'class': 0, 'unresolved': False, 'name': 'i', 'fundamental': True}], 'namespace': '', 'virtual': False, 'destructor': False, 'returns': 'int', 'template': False, 'override': False, 'final': False, 'friend': False, 'returns_class': False, 'extern': False, 'returns_pointer': 0, 'class': None, 'name': 'sampleFreeFunction', 'pure_virtual': False, 'explicit': False, 'returns_fundamental': True, 'constructor': False, 'debug': 'int sampleFreeFunction ( int i ) {', 'inline': False}
-{'line_number': 75, 'static': False, 'rtnType': 'int', 'const': False, 'parameters': [{'line_number': 75, 'constant': 0, 'reference': 0, 'raw_type': 'void', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': [], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'void', 'method': {'line_number': 75, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'int', 'extern': False, 'returns_pointer': 0, 'parameters': [...], 'class': None, 'returns_reference': False, 'const': False, 'name': 'anotherFreeFunction', 'pure_virtual': False, 'debug': 'int anotherFreeFunction ( void ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'int', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, 'parent': None, 'ctypes_type': 'ctypes.c_void_p', 'typedefs': 0, 'extern': False, 'class': 0, 'unresolved': False, 'name': '', 'fundamental': True}], 'namespace': '', 'virtual': False, 'destructor': False, 'returns': 'int', 'template': False, 'override': False, 'final': False, 'friend': False, 'returns_class': False, 'extern': False, 'returns_pointer': 0, 'class': None, 'name': 'anotherFreeFunction', 'pure_virtual': False, 'explicit': False, 'returns_fundamental': True, 'constructor': False, 'debug': 'int anotherFreeFunction ( void ) ;', 'inline': False}
-
-Number of public methods 5
-Number of private properties 2
-Parameter Types for public method meth3 ['const string &', 'vector<string> &']
-
-Return type for meth1:
-string
-
-Doxygen for meth2:
-///
-/// Method 2 description
-///
-/// @param v1 Variable 1
-///
-
-Parameters for meth3:
-[{'line_number': 30, 'constant': 1, 'reference': 1, 'raw_type': 'string', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': ['string'], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'const string &', 'method': {'line_number': 30, 'unresolved_parameters': True, 'parent': {'inherits': [], 'line_number': 8, 'forward_declares': {'protected': [], 'public': [], 'private': []}, 'name': 'SampleClass', 'parent': None, 'abstract': False, 'namespace': '', 'declaration_method': 'class', 'properties': {'protected': [], 'public': [], 'private': [{'line_number': 42, 'constant': 0, 'reference': 0, 'raw_type': 'string', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': ['string'], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'string', 'property_of_class': 'SampleClass', 'parent': None, 'unresolved': True, 'typedefs': 0, 'extern': False, 'class': 0, 'doxygen': '/// prop1 description', 'name': 'prop1', 'fundamental': 0}, {'line_number': 44, 'constant': 0, 'reference': 0, 'raw_type': 'int', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': [], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'int', 'property_of_class': 'SampleClass', 'parent': None, 'ctypes_type': 'ctypes.c_int', 'unresolved': False, 'typedefs': 0, 'extern': False, 'class': 0, 'doxygen': '//! prop5 description', 'name': 'prop5', 'fundamental': True}]}, 'typedefs': {'protected': [], 'public': [], 'private': []}, 'structs': {'protected': [], 'public': [], 'private': []}, 'enums': {'protected': [], 'public': [], 'private': []}, 'final': False, 'nested_classes': [], 'methods': {'protected': [], 'public': [{'line_number': 11, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'const': False, 'name': 'SampleClass', 'pure_virtual': False, 'debug': 'SampleClass ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': '', 'template': False, 'constructor': True, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {'line_number': 15, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': False, 'rtnType': 'string', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'returns_unknown': True, 'doxygen': '/*!\n* Method 1\n*/', 'const': False, 'name': 'meth1', 'pure_virtual': False, 'debug': 'string meth1 ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'string', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {'line_number': 22, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'int', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [{'line_number': 22, 'constant': 0, 'reference': 0, 'raw_type': 'int', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': [], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'int', 'method': {...}, 'parent': None, 'ctypes_type': 'ctypes.c_int', 'typedefs': 0, 'extern': False, 'class': 0, 'desc': 'Variable 1', 'unresolved': False, 'name': 'v1', 'fundamental': True}], 'class': None, 'returns_reference': False, 'doxygen': '///\n/// Method 2 description\n///\n/// @param v1 Variable 1\n///', 'const': False, 'name': 'meth2', 'pure_virtual': False, 'debug': 'int meth2 ( int v1 ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'int', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {...}, {'line_number': 37, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'unsigned int', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'doxygen': '/**********************************\n* Method 4 description\n*\n* @return Return value\n*********************************/', 'const': False, 'name': 'meth4', 'pure_virtual': False, 'debug': 'unsigned int meth4 ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'unsigned int', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}], 'private': [{'line_number': 39, 'parent': {...}, 'defined': True, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void *', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 1, 'parameters': [], 'class': None, 'returns_reference': False, 'const': False, 'name': 'meth5', 'pure_virtual': False, 'debug': 'void * meth5 ( ) {', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'void', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}]}}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [...], 'class': None, 'returns_reference': False, 'doxygen': '/**\n* Method 3 description\n*\n* \\param v1 Variable 1\n* \\param v2 Variable 2\n*/', 'const': False, 'name': 'meth3', 'pure_virtual': False, 'debug': 'void meth3 ( const string & v1 , vector <string> & v2 ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'void', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, 'parent': None, 'typedefs': 0, 'extern': False, 'class': 0, 'desc': 'Variable 1', 'unresolved': True, 'name': 'v1', 'fundamental': 0}, {'line_number': 30, 'constant': 0, 'reference': 1, 'raw_type': 'vector<string>', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': ['vector<string>'], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'template': 'vector<string>', 'mutable': False, 'type': 'vector<string> &', 'method': {'line_number': 30, 'unresolved_parameters': True, 'parent': {'inherits': [], 'line_number': 8, 'forward_declares': {'protected': [], 'public': [], 'private': []}, 'name': 'SampleClass', 'parent': None, 'abstract': False, 'namespace': '', 'declaration_method': 'class', 'properties': {'protected': [], 'public': [], 'private': [{'line_number': 42, 'constant': 0, 'reference': 0, 'raw_type': 'string', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': ['string'], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'string', 'property_of_class': 'SampleClass', 'parent': None, 'unresolved': True, 'typedefs': 0, 'extern': False, 'class': 0, 'doxygen': '/// prop1 description', 'name': 'prop1', 'fundamental': 0}, {'line_number': 44, 'constant': 0, 'reference': 0, 'raw_type': 'int', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': [], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'int', 'property_of_class': 'SampleClass', 'parent': None, 'ctypes_type': 'ctypes.c_int', 'unresolved': False, 'typedefs': 0, 'extern': False, 'class': 0, 'doxygen': '//! prop5 description', 'name': 'prop5', 'fundamental': True}]}, 'typedefs': {'protected': [], 'public': [], 'private': []}, 'structs': {'protected': [], 'public': [], 'private': []}, 'enums': {'protected': [], 'public': [], 'private': []}, 'final': False, 'nested_classes': [], 'methods': {'protected': [], 'public': [{'line_number': 11, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'const': False, 'name': 'SampleClass', 'pure_virtual': False, 'debug': 'SampleClass ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': '', 'template': False, 'constructor': True, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {'line_number': 15, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': False, 'rtnType': 'string', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'returns_unknown': True, 'doxygen': '/*!\n* Method 1\n*/', 'const': False, 'name': 'meth1', 'pure_virtual': False, 'debug': 'string meth1 ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'string', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {'line_number': 22, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'int', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [{'line_number': 22, 'constant': 0, 'reference': 0, 'raw_type': 'int', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': [], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'int', 'method': {...}, 'parent': None, 'ctypes_type': 'ctypes.c_int', 'typedefs': 0, 'extern': False, 'class': 0, 'desc': 'Variable 1', 'unresolved': False, 'name': 'v1', 'fundamental': True}], 'class': None, 'returns_reference': False, 'doxygen': '///\n/// Method 2 description\n///\n/// @param v1 Variable 1\n///', 'const': False, 'name': 'meth2', 'pure_virtual': False, 'debug': 'int meth2 ( int v1 ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'int', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {...}, {'line_number': 37, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'unsigned int', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'doxygen': '/**********************************\n* Method 4 description\n*\n* @return Return value\n*********************************/', 'const': False, 'name': 'meth4', 'pure_virtual': False, 'debug': 'unsigned int meth4 ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'unsigned int', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}], 'private': [{'line_number': 39, 'parent': {...}, 'defined': True, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void *', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 1, 'parameters': [], 'class': None, 'returns_reference': False, 'const': False, 'name': 'meth5', 'pure_virtual': False, 'debug': 'void * meth5 ( ) {', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'void', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}]}}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [...], 'class': None, 'returns_reference': False, 'doxygen': '/**\n* Method 3 description\n*\n* \\param v1 Variable 1\n* \\param v2 Variable 2\n*/', 'const': False, 'name': 'meth3', 'pure_virtual': False, 'debug': 'void meth3 ( const string & v1 , vector <string> & v2 ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'void', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, 'parent': None, 'ctypes_type': 'ctypes.c_void_p', 'typedefs': 0, 'extern': False, 'class': 0, 'desc': 'Variable 2', 'unresolved': True, 'name': 'v2', 'fundamental': 0}]
-
-Doxygen for meth4:
-/**********************************
-* Method 4 description
-*
-* @return Return value
-*********************************/
-
-Return type for meth5:
-void *
-
-Doxygen type for prop1:
-/// prop1 description
-
-Type for prop5:
-int
-
-Namespace for AlphaClass is:
-Alpha
-
-Return type for alphaMethod is:
-void
-
-Namespace for OmegaClass is:
-Alpha::Omega
-
-Type for omegaString is:
-string
-
-Free functions are:
- sampleFreeFunction
- anotherFreeFunction
-
-#includes are:
- <vector>
- <string>
-
-#defines are:
- DEF_1 1
- OS_NAME "Linux"
-
-
-
-

Contributors

-
    -
  • Chris Love
  • -
  • HartsAntler
  • -
-
-
- - diff --git a/README.md b/README.md deleted file mode 100644 index bbc7a35..0000000 --- a/README.md +++ /dev/null @@ -1,49 +0,0 @@ -robotpy-cppheaderparser -======================= - -[![Build Status](https://travis-ci.org/robotpy/robotpy-cppheaderparser.svg?branch=master)](https://travis-ci.org/robotpy/robotpy-cppheaderparser) - -CppHeaderParser is a pure python C++ header parser that parses C++ headers -and creates a data structure that you can use to do many types of things. -We've found it particularly useful for creating programs that generate -python wrappers around existing C++ programs. - -robotpy-cppheaderparser is a fork of the [CppHeaderParser](https://bitbucket.org/senex/cppheaderparser) -library originally created by @senex. CppHeaderParser is an excellent -library and critical to some of the stuff we do in the RobotPy project. -Unfortunately, the maintainer seems to be busy, so robotpy-cppheaderparser -was born. - -We don't currently intend to develop new features, but aim to maintain -compatibility with the existing code and make improvements and bugfixes as -we need them. - -If you find an bug, we encourage you to submit a pull request! New changes -will only be accepted if there are tests to cover the change you made (and -if they don't break existing tests). - -Install -------- - - pip install robotpy-cppheaderparser - -Usage ------ - -[See the examples](CppHeaderParser/examples). - -License -------- - -BSD License - -Authors -------- - -Originally developed by Jashua Cloutier, this fork is maintained by the RobotPy -project. - -Past contributors include: -* Jashua Cloutier -* Chris Love -* HartsAntler diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..250c982 --- /dev/null +++ b/README.rst @@ -0,0 +1,57 @@ +robotpy-cppheaderparser +======================= + +|Build Status| + +CppHeaderParser is a pure python C++ header parser that parses C++ +headers and creates a data structure that you can use to do many types +of things. We’ve found it particularly useful for creating programs that +generate python wrappers around existing C++ programs. + +robotpy-cppheaderparser is a fork of the `CppHeaderParser`_ library +originally created by @senex. CppHeaderParser is an excellent library +and critical to some of the stuff we do in the RobotPy project. +Unfortunately, the maintainer seems to be busy, so +robotpy-cppheaderparser was born. + +We don’t currently intend to develop new features, but aim to maintain +compatibility with the existing code and make improvements and bugfixes +as we need them. + +If you find an bug, we encourage you to submit a pull request! New +changes will only be accepted if there are tests to cover the change you +made (and if they don’t break existing tests). + +Documentation +------------- + +Documentation can be found at https://cppheaderparser.readthedocs.io + +Install +------- + +:: + + pip install robotpy-cppheaderparser + +License +------- + +BSD License + +Authors +------- + +Originally developed by Jashua Cloutier, this fork is maintained by the +RobotPy project. + +Past contributors include: + +* Jashua Cloutier +* Chris Love +* HartsAntler + +.. _CppHeaderParser: https://bitbucket.org/senex/cppheaderparser + +.. |Build Status| image:: https://travis-ci.org/robotpy/robotpy-cppheaderparser.svg?branch=master + :target: https://travis-ci.org/robotpy/robotpy-cppheaderparser \ No newline at end of file diff --git a/README.txt b/README.txt deleted file mode 100644 index f7de4dd..0000000 --- a/README.txt +++ /dev/null @@ -1,267 +0,0 @@ -Python package "CppHeaderParser" --------------------------------- -**Purpose:** Parse C++ header files and generate a data structure representing the class - -**Author:** Jashua Cloutier - -**Licence:** BSD - -**External modules required:** PLY - -**Quick start**:: - - #include - #include - - #define DEF_1 1 - #define OS_NAME "Linux" - - using namespace std; - class SampleClass - { - public: - SampleClass(); - /*! - * Method 1 - */ - string meth1(); - - /// - /// Method 2 description - /// - /// @param v1 Variable 1 - /// - int meth2(int v1); - - /** - * Method 3 description - * - * \param v1 Variable 1 - * \param v2 Variable 2 - */ - void meth3(const string & v1, vector & v2); - - /********************************** - * Method 4 description - * - * @return Return value - *********************************/ - unsigned int meth4(); - private: - void * meth5(){return NULL}; - - /// prop1 description - string prop1; - //! prop5 description - int prop5; - }; - namespace Alpha - { - class AlphaClass - { - public: - AlphaClass(); - - void alphaMethod(); - - string alphaString; - }; - - namespace Omega - { - class OmegaClass - { - public: - OmegaClass(); - - string omegaString; - }; - }; - } - - int sampleFreeFunction(int i) - { - return i + 1; - } - - int anotherFreeFunction(void); - } - - -**Python code**:: - - #!/usr/bin/python - import sys - sys.path = ["../"] + sys.path - import CppHeaderParser - try: - cppHeader = CppHeaderParser.CppHeader("SampleClass.h") - except CppHeaderParser.CppParseError as e: - print(e) - sys.exit(1) - - print("CppHeaderParser view of %s"%cppHeader) - - sampleClass = cppHeader.classes["SampleClass"] - print("Number of public methods %d"%(len(sampleClass["methods"]["public"]))) - print("Number of private properties %d"%(len(sampleClass["properties"]["private"]))) - meth3 = [m for m in sampleClass["methods"]["public"] if m["name"] == "meth3"][0] #get meth3 - meth3ParamTypes = [t["type"] for t in meth3["parameters"]] #get meth3s parameters - print("Parameter Types for public method meth3 %s"%(meth3ParamTypes)) - - print("\nReturn type for meth1:") - print(cppHeader.classes["SampleClass"]["methods"]["public"][1]["rtnType"]) - - print("\nDoxygen for meth2:") - print(cppHeader.classes["SampleClass"]["methods"]["public"][2]["doxygen"]) - - print("\nParameters for meth3:") - print(cppHeader.classes["SampleClass"]["methods"]["public"][3]["parameters"]) - - print("\nDoxygen for meth4:") - print(cppHeader.classes["SampleClass"]["methods"]["public"][4]["doxygen"]) - - print("\nReturn type for meth5:") - print(cppHeader.classes["SampleClass"]["methods"]["private"][0]["rtnType"]) - - print("\nDoxygen type for prop1:") - print(cppHeader.classes["SampleClass"]["properties"]["private"][0]["doxygen"]) - - print("\nType for prop5:") - print(cppHeader.classes["SampleClass"]["properties"]["private"][1]["type"]) - - print("\nNamespace for AlphaClass is:") - print(cppHeader.classes["AlphaClass"]["namespace"]) - - print("\nReturn type for alphaMethod is:") - print(cppHeader.classes["AlphaClass"]["methods"]["public"][0]["rtnType"]) - - print("\nNamespace for OmegaClass is:") - print(cppHeader.classes["OmegaClass"]["namespace"]) - - print("\nType for omegaString is:") - print(cppHeader.classes["AlphaClass"]["properties"]["public"][0]["type"]) - - print("\nFree functions are:") - for func in cppHeader.functions: - print(" %s"%func["name"]) - - print("\n#includes are:") - for incl in cppHeader.includes: - print(" %s"%incl) - - print("\n#defines are:") - for define in cppHeader.defines: - print(" %s"%define) - - -**Output**:: - - CppHeaderParser view of class SampleClass - { - public - // Methods - {'line_number': 11, 'parent': {'inherits': [], 'line_number': 8, 'forward_declares': {'protected': [], 'public': [], 'private': []}, 'name': 'SampleClass', 'parent': None, 'abstract': False, 'namespace': '', 'declaration_method': 'class', 'properties': {'protected': [], 'public': [], 'private': [{'line_number': 42, 'constant': 0, 'reference': 0, 'raw_type': 'string', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': ['string'], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'string', 'property_of_class': 'SampleClass', 'parent': None, 'unresolved': True, 'typedefs': 0, 'extern': False, 'class': 0, 'doxygen': '/// prop1 description', 'name': 'prop1', 'fundamental': 0}, {'line_number': 44, 'constant': 0, 'reference': 0, 'raw_type': 'int', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': [], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'int', 'property_of_class': 'SampleClass', 'parent': None, 'ctypes_type': 'ctypes.c_int', 'unresolved': False, 'typedefs': 0, 'extern': False, 'class': 0, 'doxygen': '//! prop5 description', 'name': 'prop5', 'fundamental': True}]}, 'typedefs': {'protected': [], 'public': [], 'private': []}, 'structs': {'protected': [], 'public': [], 'private': []}, 'enums': {'protected': [], 'public': [], 'private': []}, 'final': False, 'nested_classes': [], 'methods': {'protected': [], 'public': [{...}, {'line_number': 15, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': False, 'rtnType': 'string', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'returns_unknown': True, 'doxygen': '/*!\n* Method 1\n*/', 'const': False, 'name': 'meth1', 'pure_virtual': False, 'debug': 'string meth1 ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'string', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {'line_number': 22, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'int', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [{'line_number': 22, 'constant': 0, 'reference': 0, 'raw_type': 'int', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': [], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'int', 'method': {...}, 'parent': None, 'ctypes_type': 'ctypes.c_int', 'typedefs': 0, 'extern': False, 'class': 0, 'desc': 'Variable 1', 'unresolved': False, 'name': 'v1', 'fundamental': True}], 'class': None, 'returns_reference': False, 'doxygen': '///\n/// Method 2 description\n///\n/// @param v1 Variable 1\n///', 'const': False, 'name': 'meth2', 'pure_virtual': False, 'debug': 'int meth2 ( int v1 ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'int', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {'line_number': 30, 'unresolved_parameters': True, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [{'line_number': 30, 'constant': 1, 'reference': 1, 'raw_type': 'string', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': ['string'], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'const string &', 'method': {...}, 'parent': None, 'typedefs': 0, 'extern': False, 'class': 0, 'desc': 'Variable 1', 'unresolved': True, 'name': 'v1', 'fundamental': 0}, {'line_number': 30, 'constant': 0, 'reference': 1, 'raw_type': 'vector', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': ['vector'], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'template': 'vector', 'mutable': False, 'type': 'vector &', 'method': {...}, 'parent': None, 'ctypes_type': 'ctypes.c_void_p', 'typedefs': 0, 'extern': False, 'class': 0, 'desc': 'Variable 2', 'unresolved': True, 'name': 'v2', 'fundamental': 0}], 'class': None, 'returns_reference': False, 'doxygen': '/**\n* Method 3 description\n*\n* \\param v1 Variable 1\n* \\param v2 Variable 2\n*/', 'const': False, 'name': 'meth3', 'pure_virtual': False, 'debug': 'void meth3 ( const string & v1 , vector & v2 ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'void', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {'line_number': 37, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'unsigned int', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'doxygen': '/**********************************\n* Method 4 description\n*\n* @return Return value\n*********************************/', 'const': False, 'name': 'meth4', 'pure_virtual': False, 'debug': 'unsigned int meth4 ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'unsigned int', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}], 'private': [{'line_number': 39, 'parent': {...}, 'defined': True, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void *', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 1, 'parameters': [], 'class': None, 'returns_reference': False, 'const': False, 'name': 'meth5', 'pure_virtual': False, 'debug': 'void * meth5 ( ) {', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'void', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}]}}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'const': False, 'name': 'SampleClass', 'pure_virtual': False, 'debug': 'SampleClass ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': '', 'template': False, 'constructor': True, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False} - {'line_number': 15, 'parent': {'inherits': [], 'line_number': 8, 'forward_declares': {'protected': [], 'public': [], 'private': []}, 'name': 'SampleClass', 'parent': None, 'abstract': False, 'namespace': '', 'declaration_method': 'class', 'properties': {'protected': [], 'public': [], 'private': [{'line_number': 42, 'constant': 0, 'reference': 0, 'raw_type': 'string', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': ['string'], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'string', 'property_of_class': 'SampleClass', 'parent': None, 'unresolved': True, 'typedefs': 0, 'extern': False, 'class': 0, 'doxygen': '/// prop1 description', 'name': 'prop1', 'fundamental': 0}, {'line_number': 44, 'constant': 0, 'reference': 0, 'raw_type': 'int', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': [], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'int', 'property_of_class': 'SampleClass', 'parent': None, 'ctypes_type': 'ctypes.c_int', 'unresolved': False, 'typedefs': 0, 'extern': False, 'class': 0, 'doxygen': '//! prop5 description', 'name': 'prop5', 'fundamental': True}]}, 'typedefs': {'protected': [], 'public': [], 'private': []}, 'structs': {'protected': [], 'public': [], 'private': []}, 'enums': {'protected': [], 'public': [], 'private': []}, 'final': False, 'nested_classes': [], 'methods': {'protected': [], 'public': [{'line_number': 11, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'const': False, 'name': 'SampleClass', 'pure_virtual': False, 'debug': 'SampleClass ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': '', 'template': False, 'constructor': True, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {...}, {'line_number': 22, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'int', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [{'line_number': 22, 'constant': 0, 'reference': 0, 'raw_type': 'int', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': [], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'int', 'method': {...}, 'parent': None, 'ctypes_type': 'ctypes.c_int', 'typedefs': 0, 'extern': False, 'class': 0, 'desc': 'Variable 1', 'unresolved': False, 'name': 'v1', 'fundamental': True}], 'class': None, 'returns_reference': False, 'doxygen': '///\n/// Method 2 description\n///\n/// @param v1 Variable 1\n///', 'const': False, 'name': 'meth2', 'pure_virtual': False, 'debug': 'int meth2 ( int v1 ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'int', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {'line_number': 30, 'unresolved_parameters': True, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [{'line_number': 30, 'constant': 1, 'reference': 1, 'raw_type': 'string', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': ['string'], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'const string &', 'method': {...}, 'parent': None, 'typedefs': 0, 'extern': False, 'class': 0, 'desc': 'Variable 1', 'unresolved': True, 'name': 'v1', 'fundamental': 0}, {'line_number': 30, 'constant': 0, 'reference': 1, 'raw_type': 'vector', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': ['vector'], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'template': 'vector', 'mutable': False, 'type': 'vector &', 'method': {...}, 'parent': None, 'ctypes_type': 'ctypes.c_void_p', 'typedefs': 0, 'extern': False, 'class': 0, 'desc': 'Variable 2', 'unresolved': True, 'name': 'v2', 'fundamental': 0}], 'class': None, 'returns_reference': False, 'doxygen': '/**\n* Method 3 description\n*\n* \\param v1 Variable 1\n* \\param v2 Variable 2\n*/', 'const': False, 'name': 'meth3', 'pure_virtual': False, 'debug': 'void meth3 ( const string & v1 , vector & v2 ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'void', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {'line_number': 37, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'unsigned int', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'doxygen': '/**********************************\n* Method 4 description\n*\n* @return Return value\n*********************************/', 'const': False, 'name': 'meth4', 'pure_virtual': False, 'debug': 'unsigned int meth4 ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'unsigned int', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}], 'private': [{'line_number': 39, 'parent': {...}, 'defined': True, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void *', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 1, 'parameters': [], 'class': None, 'returns_reference': False, 'const': False, 'name': 'meth5', 'pure_virtual': False, 'debug': 'void * meth5 ( ) {', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'void', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}]}}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': False, 'rtnType': 'string', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'returns_unknown': True, 'doxygen': '/*!\n* Method 1\n*/', 'const': False, 'name': 'meth1', 'pure_virtual': False, 'debug': 'string meth1 ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'string', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False} - {'line_number': 22, 'parent': {'inherits': [], 'line_number': 8, 'forward_declares': {'protected': [], 'public': [], 'private': []}, 'name': 'SampleClass', 'parent': None, 'abstract': False, 'namespace': '', 'declaration_method': 'class', 'properties': {'protected': [], 'public': [], 'private': [{'line_number': 42, 'constant': 0, 'reference': 0, 'raw_type': 'string', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': ['string'], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'string', 'property_of_class': 'SampleClass', 'parent': None, 'unresolved': True, 'typedefs': 0, 'extern': False, 'class': 0, 'doxygen': '/// prop1 description', 'name': 'prop1', 'fundamental': 0}, {'line_number': 44, 'constant': 0, 'reference': 0, 'raw_type': 'int', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': [], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'int', 'property_of_class': 'SampleClass', 'parent': None, 'ctypes_type': 'ctypes.c_int', 'unresolved': False, 'typedefs': 0, 'extern': False, 'class': 0, 'doxygen': '//! prop5 description', 'name': 'prop5', 'fundamental': True}]}, 'typedefs': {'protected': [], 'public': [], 'private': []}, 'structs': {'protected': [], 'public': [], 'private': []}, 'enums': {'protected': [], 'public': [], 'private': []}, 'final': False, 'nested_classes': [], 'methods': {'protected': [], 'public': [{'line_number': 11, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'const': False, 'name': 'SampleClass', 'pure_virtual': False, 'debug': 'SampleClass ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': '', 'template': False, 'constructor': True, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {'line_number': 15, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': False, 'rtnType': 'string', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'returns_unknown': True, 'doxygen': '/*!\n* Method 1\n*/', 'const': False, 'name': 'meth1', 'pure_virtual': False, 'debug': 'string meth1 ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'string', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {...}, {'line_number': 30, 'unresolved_parameters': True, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [{'line_number': 30, 'constant': 1, 'reference': 1, 'raw_type': 'string', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': ['string'], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'const string &', 'method': {...}, 'parent': None, 'typedefs': 0, 'extern': False, 'class': 0, 'desc': 'Variable 1', 'unresolved': True, 'name': 'v1', 'fundamental': 0}, {'line_number': 30, 'constant': 0, 'reference': 1, 'raw_type': 'vector', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': ['vector'], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'template': 'vector', 'mutable': False, 'type': 'vector &', 'method': {...}, 'parent': None, 'ctypes_type': 'ctypes.c_void_p', 'typedefs': 0, 'extern': False, 'class': 0, 'desc': 'Variable 2', 'unresolved': True, 'name': 'v2', 'fundamental': 0}], 'class': None, 'returns_reference': False, 'doxygen': '/**\n* Method 3 description\n*\n* \\param v1 Variable 1\n* \\param v2 Variable 2\n*/', 'const': False, 'name': 'meth3', 'pure_virtual': False, 'debug': 'void meth3 ( const string & v1 , vector & v2 ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'void', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {'line_number': 37, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'unsigned int', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'doxygen': '/**********************************\n* Method 4 description\n*\n* @return Return value\n*********************************/', 'const': False, 'name': 'meth4', 'pure_virtual': False, 'debug': 'unsigned int meth4 ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'unsigned int', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}], 'private': [{'line_number': 39, 'parent': {...}, 'defined': True, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void *', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 1, 'parameters': [], 'class': None, 'returns_reference': False, 'const': False, 'name': 'meth5', 'pure_virtual': False, 'debug': 'void * meth5 ( ) {', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'void', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}]}}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'int', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [{'line_number': 22, 'constant': 0, 'reference': 0, 'raw_type': 'int', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': [], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'int', 'method': {...}, 'parent': None, 'ctypes_type': 'ctypes.c_int', 'typedefs': 0, 'extern': False, 'class': 0, 'desc': 'Variable 1', 'unresolved': False, 'name': 'v1', 'fundamental': True}], 'class': None, 'returns_reference': False, 'doxygen': '///\n/// Method 2 description\n///\n/// @param v1 Variable 1\n///', 'const': False, 'name': 'meth2', 'pure_virtual': False, 'debug': 'int meth2 ( int v1 ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'int', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False} - {'line_number': 30, 'unresolved_parameters': True, 'parent': {'inherits': [], 'line_number': 8, 'forward_declares': {'protected': [], 'public': [], 'private': []}, 'name': 'SampleClass', 'parent': None, 'abstract': False, 'namespace': '', 'declaration_method': 'class', 'properties': {'protected': [], 'public': [], 'private': [{'line_number': 42, 'constant': 0, 'reference': 0, 'raw_type': 'string', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': ['string'], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'string', 'property_of_class': 'SampleClass', 'parent': None, 'unresolved': True, 'typedefs': 0, 'extern': False, 'class': 0, 'doxygen': '/// prop1 description', 'name': 'prop1', 'fundamental': 0}, {'line_number': 44, 'constant': 0, 'reference': 0, 'raw_type': 'int', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': [], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'int', 'property_of_class': 'SampleClass', 'parent': None, 'ctypes_type': 'ctypes.c_int', 'unresolved': False, 'typedefs': 0, 'extern': False, 'class': 0, 'doxygen': '//! prop5 description', 'name': 'prop5', 'fundamental': True}]}, 'typedefs': {'protected': [], 'public': [], 'private': []}, 'structs': {'protected': [], 'public': [], 'private': []}, 'enums': {'protected': [], 'public': [], 'private': []}, 'final': False, 'nested_classes': [], 'methods': {'protected': [], 'public': [{'line_number': 11, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'const': False, 'name': 'SampleClass', 'pure_virtual': False, 'debug': 'SampleClass ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': '', 'template': False, 'constructor': True, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {'line_number': 15, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': False, 'rtnType': 'string', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'returns_unknown': True, 'doxygen': '/*!\n* Method 1\n*/', 'const': False, 'name': 'meth1', 'pure_virtual': False, 'debug': 'string meth1 ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'string', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {'line_number': 22, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'int', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [{'line_number': 22, 'constant': 0, 'reference': 0, 'raw_type': 'int', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': [], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'int', 'method': {...}, 'parent': None, 'ctypes_type': 'ctypes.c_int', 'typedefs': 0, 'extern': False, 'class': 0, 'desc': 'Variable 1', 'unresolved': False, 'name': 'v1', 'fundamental': True}], 'class': None, 'returns_reference': False, 'doxygen': '///\n/// Method 2 description\n///\n/// @param v1 Variable 1\n///', 'const': False, 'name': 'meth2', 'pure_virtual': False, 'debug': 'int meth2 ( int v1 ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'int', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {...}, {'line_number': 37, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'unsigned int', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'doxygen': '/**********************************\n* Method 4 description\n*\n* @return Return value\n*********************************/', 'const': False, 'name': 'meth4', 'pure_virtual': False, 'debug': 'unsigned int meth4 ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'unsigned int', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}], 'private': [{'line_number': 39, 'parent': {...}, 'defined': True, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void *', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 1, 'parameters': [], 'class': None, 'returns_reference': False, 'const': False, 'name': 'meth5', 'pure_virtual': False, 'debug': 'void * meth5 ( ) {', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'void', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}]}}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [{'line_number': 30, 'constant': 1, 'reference': 1, 'raw_type': 'string', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': ['string'], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'const string &', 'method': {...}, 'parent': None, 'typedefs': 0, 'extern': False, 'class': 0, 'desc': 'Variable 1', 'unresolved': True, 'name': 'v1', 'fundamental': 0}, {'line_number': 30, 'constant': 0, 'reference': 1, 'raw_type': 'vector', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': ['vector'], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'template': 'vector', 'mutable': False, 'type': 'vector &', 'method': {...}, 'parent': None, 'ctypes_type': 'ctypes.c_void_p', 'typedefs': 0, 'extern': False, 'class': 0, 'desc': 'Variable 2', 'unresolved': True, 'name': 'v2', 'fundamental': 0}], 'class': None, 'returns_reference': False, 'doxygen': '/**\n* Method 3 description\n*\n* \\param v1 Variable 1\n* \\param v2 Variable 2\n*/', 'const': False, 'name': 'meth3', 'pure_virtual': False, 'debug': 'void meth3 ( const string & v1 , vector & v2 ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'void', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False} - {'line_number': 37, 'parent': {'inherits': [], 'line_number': 8, 'forward_declares': {'protected': [], 'public': [], 'private': []}, 'name': 'SampleClass', 'parent': None, 'abstract': False, 'namespace': '', 'declaration_method': 'class', 'properties': {'protected': [], 'public': [], 'private': [{'line_number': 42, 'constant': 0, 'reference': 0, 'raw_type': 'string', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': ['string'], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'string', 'property_of_class': 'SampleClass', 'parent': None, 'unresolved': True, 'typedefs': 0, 'extern': False, 'class': 0, 'doxygen': '/// prop1 description', 'name': 'prop1', 'fundamental': 0}, {'line_number': 44, 'constant': 0, 'reference': 0, 'raw_type': 'int', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': [], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'int', 'property_of_class': 'SampleClass', 'parent': None, 'ctypes_type': 'ctypes.c_int', 'unresolved': False, 'typedefs': 0, 'extern': False, 'class': 0, 'doxygen': '//! prop5 description', 'name': 'prop5', 'fundamental': True}]}, 'typedefs': {'protected': [], 'public': [], 'private': []}, 'structs': {'protected': [], 'public': [], 'private': []}, 'enums': {'protected': [], 'public': [], 'private': []}, 'final': False, 'nested_classes': [], 'methods': {'protected': [], 'public': [{'line_number': 11, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'const': False, 'name': 'SampleClass', 'pure_virtual': False, 'debug': 'SampleClass ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': '', 'template': False, 'constructor': True, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {'line_number': 15, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': False, 'rtnType': 'string', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'returns_unknown': True, 'doxygen': '/*!\n* Method 1\n*/', 'const': False, 'name': 'meth1', 'pure_virtual': False, 'debug': 'string meth1 ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'string', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {'line_number': 22, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'int', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [{'line_number': 22, 'constant': 0, 'reference': 0, 'raw_type': 'int', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': [], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'int', 'method': {...}, 'parent': None, 'ctypes_type': 'ctypes.c_int', 'typedefs': 0, 'extern': False, 'class': 0, 'desc': 'Variable 1', 'unresolved': False, 'name': 'v1', 'fundamental': True}], 'class': None, 'returns_reference': False, 'doxygen': '///\n/// Method 2 description\n///\n/// @param v1 Variable 1\n///', 'const': False, 'name': 'meth2', 'pure_virtual': False, 'debug': 'int meth2 ( int v1 ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'int', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {'line_number': 30, 'unresolved_parameters': True, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [{'line_number': 30, 'constant': 1, 'reference': 1, 'raw_type': 'string', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': ['string'], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'const string &', 'method': {...}, 'parent': None, 'typedefs': 0, 'extern': False, 'class': 0, 'desc': 'Variable 1', 'unresolved': True, 'name': 'v1', 'fundamental': 0}, {'line_number': 30, 'constant': 0, 'reference': 1, 'raw_type': 'vector', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': ['vector'], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'template': 'vector', 'mutable': False, 'type': 'vector &', 'method': {...}, 'parent': None, 'ctypes_type': 'ctypes.c_void_p', 'typedefs': 0, 'extern': False, 'class': 0, 'desc': 'Variable 2', 'unresolved': True, 'name': 'v2', 'fundamental': 0}], 'class': None, 'returns_reference': False, 'doxygen': '/**\n* Method 3 description\n*\n* \\param v1 Variable 1\n* \\param v2 Variable 2\n*/', 'const': False, 'name': 'meth3', 'pure_virtual': False, 'debug': 'void meth3 ( const string & v1 , vector & v2 ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'void', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {...}], 'private': [{'line_number': 39, 'parent': {...}, 'defined': True, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void *', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 1, 'parameters': [], 'class': None, 'returns_reference': False, 'const': False, 'name': 'meth5', 'pure_virtual': False, 'debug': 'void * meth5 ( ) {', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'void', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}]}}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'unsigned int', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'doxygen': '/**********************************\n* Method 4 description\n*\n* @return Return value\n*********************************/', 'const': False, 'name': 'meth4', 'pure_virtual': False, 'debug': 'unsigned int meth4 ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'unsigned int', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False} - protected - private - // Properties - {'line_number': 42, 'constant': 0, 'reference': 0, 'raw_type': 'string', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': ['string'], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'string', 'property_of_class': 'SampleClass', 'parent': None, 'unresolved': True, 'typedefs': 0, 'extern': False, 'class': 0, 'doxygen': '/// prop1 description', 'name': 'prop1', 'fundamental': 0} - {'line_number': 44, 'constant': 0, 'reference': 0, 'raw_type': 'int', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': [], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'int', 'property_of_class': 'SampleClass', 'parent': None, 'ctypes_type': 'ctypes.c_int', 'unresolved': False, 'typedefs': 0, 'extern': False, 'class': 0, 'doxygen': '//! prop5 description', 'name': 'prop5', 'fundamental': True} - // Methods - {'line_number': 39, 'parent': {'inherits': [], 'line_number': 8, 'forward_declares': {'protected': [], 'public': [], 'private': []}, 'name': 'SampleClass', 'parent': None, 'abstract': False, 'namespace': '', 'declaration_method': 'class', 'properties': {'protected': [], 'public': [], 'private': [{'line_number': 42, 'constant': 0, 'reference': 0, 'raw_type': 'string', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': ['string'], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'string', 'property_of_class': 'SampleClass', 'parent': None, 'unresolved': True, 'typedefs': 0, 'extern': False, 'class': 0, 'doxygen': '/// prop1 description', 'name': 'prop1', 'fundamental': 0}, {'line_number': 44, 'constant': 0, 'reference': 0, 'raw_type': 'int', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': [], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'int', 'property_of_class': 'SampleClass', 'parent': None, 'ctypes_type': 'ctypes.c_int', 'unresolved': False, 'typedefs': 0, 'extern': False, 'class': 0, 'doxygen': '//! prop5 description', 'name': 'prop5', 'fundamental': True}]}, 'typedefs': {'protected': [], 'public': [], 'private': []}, 'structs': {'protected': [], 'public': [], 'private': []}, 'enums': {'protected': [], 'public': [], 'private': []}, 'final': False, 'nested_classes': [], 'methods': {'protected': [], 'public': [{'line_number': 11, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'const': False, 'name': 'SampleClass', 'pure_virtual': False, 'debug': 'SampleClass ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': '', 'template': False, 'constructor': True, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {'line_number': 15, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': False, 'rtnType': 'string', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'returns_unknown': True, 'doxygen': '/*!\n* Method 1\n*/', 'const': False, 'name': 'meth1', 'pure_virtual': False, 'debug': 'string meth1 ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'string', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {'line_number': 22, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'int', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [{'line_number': 22, 'constant': 0, 'reference': 0, 'raw_type': 'int', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': [], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'int', 'method': {...}, 'parent': None, 'ctypes_type': 'ctypes.c_int', 'typedefs': 0, 'extern': False, 'class': 0, 'desc': 'Variable 1', 'unresolved': False, 'name': 'v1', 'fundamental': True}], 'class': None, 'returns_reference': False, 'doxygen': '///\n/// Method 2 description\n///\n/// @param v1 Variable 1\n///', 'const': False, 'name': 'meth2', 'pure_virtual': False, 'debug': 'int meth2 ( int v1 ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'int', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {'line_number': 30, 'unresolved_parameters': True, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [{'line_number': 30, 'constant': 1, 'reference': 1, 'raw_type': 'string', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': ['string'], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'const string &', 'method': {...}, 'parent': None, 'typedefs': 0, 'extern': False, 'class': 0, 'desc': 'Variable 1', 'unresolved': True, 'name': 'v1', 'fundamental': 0}, {'line_number': 30, 'constant': 0, 'reference': 1, 'raw_type': 'vector', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': ['vector'], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'template': 'vector', 'mutable': False, 'type': 'vector &', 'method': {...}, 'parent': None, 'ctypes_type': 'ctypes.c_void_p', 'typedefs': 0, 'extern': False, 'class': 0, 'desc': 'Variable 2', 'unresolved': True, 'name': 'v2', 'fundamental': 0}], 'class': None, 'returns_reference': False, 'doxygen': '/**\n* Method 3 description\n*\n* \\param v1 Variable 1\n* \\param v2 Variable 2\n*/', 'const': False, 'name': 'meth3', 'pure_virtual': False, 'debug': 'void meth3 ( const string & v1 , vector & v2 ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'void', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {'line_number': 37, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'unsigned int', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'doxygen': '/**********************************\n* Method 4 description\n*\n* @return Return value\n*********************************/', 'const': False, 'name': 'meth4', 'pure_virtual': False, 'debug': 'unsigned int meth4 ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'unsigned int', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}], 'private': [{...}]}}, 'defined': True, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void *', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 1, 'parameters': [], 'class': None, 'returns_reference': False, 'const': False, 'name': 'meth5', 'pure_virtual': False, 'debug': 'void * meth5 ( ) {', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'void', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False} - } - - class Alpha::AlphaClass - { - public - // Properties - {'raw_type': 'string', 'line_number': 55, 'typedef': None, 'unresolved': True, 'constant': 0, 'name': 'alphaString', 'parent': None, 'pointer': 0, 'namespace': 'Alpha::', 'function_pointer': 0, 'property_of_class': 'AlphaClass', 'static': 0, 'fundamental': 0, 'mutable': False, 'extern': False, 'typedefs': 0, 'array': 0, 'type': 'string', 'class': 0, 'reference': 0, 'aliases': ['string']} - // Methods - {'line_number': 51, 'parent': {'inherits': [], 'line_number': 48, 'forward_declares': {'protected': [], 'public': [], 'private': []}, 'name': 'AlphaClass', 'parent': None, 'abstract': False, 'namespace': 'Alpha', 'declaration_method': 'class', 'properties': {'protected': [], 'public': [{'raw_type': 'string', 'line_number': 55, 'typedef': None, 'unresolved': True, 'constant': 0, 'name': 'alphaString', 'parent': None, 'pointer': 0, 'namespace': 'Alpha::', 'function_pointer': 0, 'property_of_class': 'AlphaClass', 'static': 0, 'fundamental': 0, 'mutable': False, 'extern': False, 'typedefs': 0, 'array': 0, 'type': 'string', 'class': 0, 'reference': 0, 'aliases': ['string']}], 'private': []}, 'typedefs': {'protected': [], 'public': [], 'private': []}, 'structs': {'protected': [], 'public': [], 'private': []}, 'enums': {'protected': [], 'public': [], 'private': []}, 'final': False, 'nested_classes': [], 'methods': {'protected': [], 'public': [{...}, {'line_number': 53, 'parent': {...}, 'defined': False, 'namespace': 'Alpha::', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void', 'extern': False, 'path': 'Alpha::AlphaClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'const': False, 'name': 'alphaMethod', 'pure_virtual': False, 'debug': 'void alphaMethod ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'void', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}], 'private': []}}, 'defined': False, 'namespace': 'Alpha::', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void', 'extern': False, 'path': 'Alpha::AlphaClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'const': False, 'name': 'AlphaClass', 'pure_virtual': False, 'debug': 'AlphaClass ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': '', 'template': False, 'constructor': True, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False} - {'line_number': 53, 'parent': {'inherits': [], 'line_number': 48, 'forward_declares': {'protected': [], 'public': [], 'private': []}, 'name': 'AlphaClass', 'parent': None, 'abstract': False, 'namespace': 'Alpha', 'declaration_method': 'class', 'properties': {'protected': [], 'public': [{'raw_type': 'string', 'line_number': 55, 'typedef': None, 'unresolved': True, 'constant': 0, 'name': 'alphaString', 'parent': None, 'pointer': 0, 'namespace': 'Alpha::', 'function_pointer': 0, 'property_of_class': 'AlphaClass', 'static': 0, 'fundamental': 0, 'mutable': False, 'extern': False, 'typedefs': 0, 'array': 0, 'type': 'string', 'class': 0, 'reference': 0, 'aliases': ['string']}], 'private': []}, 'typedefs': {'protected': [], 'public': [], 'private': []}, 'structs': {'protected': [], 'public': [], 'private': []}, 'enums': {'protected': [], 'public': [], 'private': []}, 'final': False, 'nested_classes': [], 'methods': {'protected': [], 'public': [{'line_number': 51, 'parent': {...}, 'defined': False, 'namespace': 'Alpha::', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void', 'extern': False, 'path': 'Alpha::AlphaClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'const': False, 'name': 'AlphaClass', 'pure_virtual': False, 'debug': 'AlphaClass ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': '', 'template': False, 'constructor': True, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {...}], 'private': []}}, 'defined': False, 'namespace': 'Alpha::', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void', 'extern': False, 'path': 'Alpha::AlphaClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'const': False, 'name': 'alphaMethod', 'pure_virtual': False, 'debug': 'void alphaMethod ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'void', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False} - protected - private - } - - class Alpha::Omega::OmegaClass - { - public - // Properties - {'raw_type': 'string', 'line_number': 65, 'typedef': None, 'unresolved': True, 'constant': 0, 'name': 'omegaString', 'parent': None, 'pointer': 0, 'namespace': 'Alpha::Omega::', 'function_pointer': 0, 'property_of_class': 'OmegaClass', 'static': 0, 'fundamental': 0, 'mutable': False, 'extern': False, 'typedefs': 0, 'array': 0, 'type': 'string', 'class': 0, 'reference': 0, 'aliases': ['string']} - // Methods - {'line_number': 63, 'parent': {'inherits': [], 'line_number': 60, 'forward_declares': {'protected': [], 'public': [], 'private': []}, 'name': 'OmegaClass', 'parent': None, 'abstract': False, 'namespace': 'Alpha::Omega', 'declaration_method': 'class', 'properties': {'protected': [], 'public': [{'raw_type': 'string', 'line_number': 65, 'typedef': None, 'unresolved': True, 'constant': 0, 'name': 'omegaString', 'parent': None, 'pointer': 0, 'namespace': 'Alpha::Omega::', 'function_pointer': 0, 'property_of_class': 'OmegaClass', 'static': 0, 'fundamental': 0, 'mutable': False, 'extern': False, 'typedefs': 0, 'array': 0, 'type': 'string', 'class': 0, 'reference': 0, 'aliases': ['string']}], 'private': []}, 'typedefs': {'protected': [], 'public': [], 'private': []}, 'structs': {'protected': [], 'public': [], 'private': []}, 'enums': {'protected': [], 'public': [], 'private': []}, 'final': False, 'nested_classes': [], 'methods': {'protected': [], 'public': [{...}], 'private': []}}, 'defined': False, 'namespace': 'Alpha::Omega::', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void', 'extern': False, 'path': 'Alpha::Omega::OmegaClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'const': False, 'name': 'OmegaClass', 'pure_virtual': False, 'debug': 'OmegaClass ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': '', 'template': False, 'constructor': True, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False} - protected - private - } - - // functions - {'line_number': 70, 'static': False, 'rtnType': 'int', 'const': False, 'parameters': [{'line_number': 70, 'constant': 0, 'reference': 0, 'raw_type': 'int', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': [], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'int', 'method': {'line_number': 70, 'defined': True, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'int', 'extern': False, 'returns_pointer': 0, 'parameters': [...], 'class': None, 'returns_reference': False, 'const': False, 'name': 'sampleFreeFunction', 'pure_virtual': False, 'debug': 'int sampleFreeFunction ( int i ) {', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'int', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, 'parent': None, 'ctypes_type': 'ctypes.c_int', 'typedefs': 0, 'extern': False, 'class': 0, 'unresolved': False, 'name': 'i', 'fundamental': True}], 'namespace': '', 'virtual': False, 'destructor': False, 'returns': 'int', 'template': False, 'override': False, 'final': False, 'friend': False, 'returns_class': False, 'extern': False, 'returns_pointer': 0, 'class': None, 'name': 'sampleFreeFunction', 'pure_virtual': False, 'explicit': False, 'returns_fundamental': True, 'constructor': False, 'debug': 'int sampleFreeFunction ( int i ) {', 'inline': False} - {'line_number': 75, 'static': False, 'rtnType': 'int', 'const': False, 'parameters': [{'line_number': 75, 'constant': 0, 'reference': 0, 'raw_type': 'void', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': [], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'void', 'method': {'line_number': 75, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'int', 'extern': False, 'returns_pointer': 0, 'parameters': [...], 'class': None, 'returns_reference': False, 'const': False, 'name': 'anotherFreeFunction', 'pure_virtual': False, 'debug': 'int anotherFreeFunction ( void ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'int', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, 'parent': None, 'ctypes_type': 'ctypes.c_void_p', 'typedefs': 0, 'extern': False, 'class': 0, 'unresolved': False, 'name': '', 'fundamental': True}], 'namespace': '', 'virtual': False, 'destructor': False, 'returns': 'int', 'template': False, 'override': False, 'final': False, 'friend': False, 'returns_class': False, 'extern': False, 'returns_pointer': 0, 'class': None, 'name': 'anotherFreeFunction', 'pure_virtual': False, 'explicit': False, 'returns_fundamental': True, 'constructor': False, 'debug': 'int anotherFreeFunction ( void ) ;', 'inline': False} - - Number of public methods 5 - Number of private properties 2 - Parameter Types for public method meth3 ['const string &', 'vector &'] - - Return type for meth1: - string - - Doxygen for meth2: - /// - /// Method 2 description - /// - /// @param v1 Variable 1 - /// - - Parameters for meth3: - [{'line_number': 30, 'constant': 1, 'reference': 1, 'raw_type': 'string', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': ['string'], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'const string &', 'method': {'line_number': 30, 'unresolved_parameters': True, 'parent': {'inherits': [], 'line_number': 8, 'forward_declares': {'protected': [], 'public': [], 'private': []}, 'name': 'SampleClass', 'parent': None, 'abstract': False, 'namespace': '', 'declaration_method': 'class', 'properties': {'protected': [], 'public': [], 'private': [{'line_number': 42, 'constant': 0, 'reference': 0, 'raw_type': 'string', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': ['string'], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'string', 'property_of_class': 'SampleClass', 'parent': None, 'unresolved': True, 'typedefs': 0, 'extern': False, 'class': 0, 'doxygen': '/// prop1 description', 'name': 'prop1', 'fundamental': 0}, {'line_number': 44, 'constant': 0, 'reference': 0, 'raw_type': 'int', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': [], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'int', 'property_of_class': 'SampleClass', 'parent': None, 'ctypes_type': 'ctypes.c_int', 'unresolved': False, 'typedefs': 0, 'extern': False, 'class': 0, 'doxygen': '//! prop5 description', 'name': 'prop5', 'fundamental': True}]}, 'typedefs': {'protected': [], 'public': [], 'private': []}, 'structs': {'protected': [], 'public': [], 'private': []}, 'enums': {'protected': [], 'public': [], 'private': []}, 'final': False, 'nested_classes': [], 'methods': {'protected': [], 'public': [{'line_number': 11, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'const': False, 'name': 'SampleClass', 'pure_virtual': False, 'debug': 'SampleClass ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': '', 'template': False, 'constructor': True, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {'line_number': 15, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': False, 'rtnType': 'string', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'returns_unknown': True, 'doxygen': '/*!\n* Method 1\n*/', 'const': False, 'name': 'meth1', 'pure_virtual': False, 'debug': 'string meth1 ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'string', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {'line_number': 22, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'int', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [{'line_number': 22, 'constant': 0, 'reference': 0, 'raw_type': 'int', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': [], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'int', 'method': {...}, 'parent': None, 'ctypes_type': 'ctypes.c_int', 'typedefs': 0, 'extern': False, 'class': 0, 'desc': 'Variable 1', 'unresolved': False, 'name': 'v1', 'fundamental': True}], 'class': None, 'returns_reference': False, 'doxygen': '///\n/// Method 2 description\n///\n/// @param v1 Variable 1\n///', 'const': False, 'name': 'meth2', 'pure_virtual': False, 'debug': 'int meth2 ( int v1 ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'int', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {...}, {'line_number': 37, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'unsigned int', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'doxygen': '/**********************************\n* Method 4 description\n*\n* @return Return value\n*********************************/', 'const': False, 'name': 'meth4', 'pure_virtual': False, 'debug': 'unsigned int meth4 ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'unsigned int', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}], 'private': [{'line_number': 39, 'parent': {...}, 'defined': True, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void *', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 1, 'parameters': [], 'class': None, 'returns_reference': False, 'const': False, 'name': 'meth5', 'pure_virtual': False, 'debug': 'void * meth5 ( ) {', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'void', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}]}}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [...], 'class': None, 'returns_reference': False, 'doxygen': '/**\n* Method 3 description\n*\n* \\param v1 Variable 1\n* \\param v2 Variable 2\n*/', 'const': False, 'name': 'meth3', 'pure_virtual': False, 'debug': 'void meth3 ( const string & v1 , vector & v2 ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'void', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, 'parent': None, 'typedefs': 0, 'extern': False, 'class': 0, 'desc': 'Variable 1', 'unresolved': True, 'name': 'v1', 'fundamental': 0}, {'line_number': 30, 'constant': 0, 'reference': 1, 'raw_type': 'vector', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': ['vector'], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'template': 'vector', 'mutable': False, 'type': 'vector &', 'method': {'line_number': 30, 'unresolved_parameters': True, 'parent': {'inherits': [], 'line_number': 8, 'forward_declares': {'protected': [], 'public': [], 'private': []}, 'name': 'SampleClass', 'parent': None, 'abstract': False, 'namespace': '', 'declaration_method': 'class', 'properties': {'protected': [], 'public': [], 'private': [{'line_number': 42, 'constant': 0, 'reference': 0, 'raw_type': 'string', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': ['string'], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'string', 'property_of_class': 'SampleClass', 'parent': None, 'unresolved': True, 'typedefs': 0, 'extern': False, 'class': 0, 'doxygen': '/// prop1 description', 'name': 'prop1', 'fundamental': 0}, {'line_number': 44, 'constant': 0, 'reference': 0, 'raw_type': 'int', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': [], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'int', 'property_of_class': 'SampleClass', 'parent': None, 'ctypes_type': 'ctypes.c_int', 'unresolved': False, 'typedefs': 0, 'extern': False, 'class': 0, 'doxygen': '//! prop5 description', 'name': 'prop5', 'fundamental': True}]}, 'typedefs': {'protected': [], 'public': [], 'private': []}, 'structs': {'protected': [], 'public': [], 'private': []}, 'enums': {'protected': [], 'public': [], 'private': []}, 'final': False, 'nested_classes': [], 'methods': {'protected': [], 'public': [{'line_number': 11, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'const': False, 'name': 'SampleClass', 'pure_virtual': False, 'debug': 'SampleClass ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': '', 'template': False, 'constructor': True, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {'line_number': 15, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': False, 'rtnType': 'string', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'returns_unknown': True, 'doxygen': '/*!\n* Method 1\n*/', 'const': False, 'name': 'meth1', 'pure_virtual': False, 'debug': 'string meth1 ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'string', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {'line_number': 22, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'int', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [{'line_number': 22, 'constant': 0, 'reference': 0, 'raw_type': 'int', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': [], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'int', 'method': {...}, 'parent': None, 'ctypes_type': 'ctypes.c_int', 'typedefs': 0, 'extern': False, 'class': 0, 'desc': 'Variable 1', 'unresolved': False, 'name': 'v1', 'fundamental': True}], 'class': None, 'returns_reference': False, 'doxygen': '///\n/// Method 2 description\n///\n/// @param v1 Variable 1\n///', 'const': False, 'name': 'meth2', 'pure_virtual': False, 'debug': 'int meth2 ( int v1 ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'int', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {...}, {'line_number': 37, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'unsigned int', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'doxygen': '/**********************************\n* Method 4 description\n*\n* @return Return value\n*********************************/', 'const': False, 'name': 'meth4', 'pure_virtual': False, 'debug': 'unsigned int meth4 ( ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'unsigned int', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}], 'private': [{'line_number': 39, 'parent': {...}, 'defined': True, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void *', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 1, 'parameters': [], 'class': None, 'returns_reference': False, 'const': False, 'name': 'meth5', 'pure_virtual': False, 'debug': 'void * meth5 ( ) {', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'void', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}]}}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void', 'extern': False, 'path': 'SampleClass', 'returns_pointer': 0, 'parameters': [...], 'class': None, 'returns_reference': False, 'doxygen': '/**\n* Method 3 description\n*\n* \\param v1 Variable 1\n* \\param v2 Variable 2\n*/', 'const': False, 'name': 'meth3', 'pure_virtual': False, 'debug': 'void meth3 ( const string & v1 , vector & v2 ) ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'void', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, 'parent': None, 'ctypes_type': 'ctypes.c_void_p', 'typedefs': 0, 'extern': False, 'class': 0, 'desc': 'Variable 2', 'unresolved': True, 'name': 'v2', 'fundamental': 0}] - - Doxygen for meth4: - /********************************** - * Method 4 description - * - * @return Return value - *********************************/ - - Return type for meth5: - void * - - Doxygen type for prop1: - /// prop1 description - - Type for prop5: - int - - Namespace for AlphaClass is: - Alpha - - Return type for alphaMethod is: - void - - Namespace for OmegaClass is: - Alpha::Omega - - Type for omegaString is: - string - - Free functions are: - sampleFreeFunction - anotherFreeFunction - - #includes are: - - - - #defines are: - DEF_1 1 - OS_NAME "Linux" - - - -Contributors ------------- -* Chris Love -* HartsAntler diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..b8b9c15 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,177 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# User-friendly check for sphinx-build +ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) +$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) +endif + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " xml to make Docutils-native XML files" + @echo " pseudoxml to make pseudoxml-XML files for display purposes" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/sphinx.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/sphinx.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/sphinx" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/sphinx" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +latexpdfja: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through platex and dvipdfmx..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." + +xml: + $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml + @echo + @echo "Build finished. The XML files are in $(BUILDDIR)/xml." + +pseudoxml: + $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml + @echo + @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." diff --git a/docs/api.rst b/docs/api.rst new file mode 100644 index 0000000..c689857 --- /dev/null +++ b/docs/api.rst @@ -0,0 +1,31 @@ +API +=== + +To parse a header file and retrieve the resulting data structure, you'll +want to use the :class:`.CppHeader` object:: + + import CppHeaderParser + + header = CppHeaderParser.CppHeader("path/to/header.h") + +Below is some documentation of the various object types that CppHeaderParser +will generated after parsing a header file. The documentation is not yet +currently comprehensive, so the best way to see what gets generated is +by printing out the JSON representation: + +.. code-block:: sh + + python -m CppHeaderParser.tojson /path/to/header.h + + +.. warning:: CppHeaderParser is not safe to use from multiple threads + +CppHeaderParser +--------------- + +.. automodule:: CppHeaderParser.CppHeaderParser + :members: CppClass, CppEnum, CppHeader, CppMethod, CppParseError, + CppStruct, CppUnion, CppVariable, TagStr, + ignoreSymbols + :undoc-members: + :show-inheritance: diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..696192f --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,339 @@ +import sys +import os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +sys.path.insert(0, os.path.abspath("..")) + + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = ["sphinx.ext.autodoc", "sphinx.ext.viewcode"] + + +# Add any paths that contain templates here, relative to this directory. +templates_path = ["_templates"] + +# The suffix of source filenames. +source_suffix = ".rst" + +# The encoding of source files. +# source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = "index" + +# General information about the project. +project = u"robotpy-cppheaderparser" +copyright = u"2019 RobotPy Development Team" + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# + +import CppHeaderParser + +# The short X.Y version. +version = CppHeaderParser.__version__ +# The full version, including alpha/beta/rc tags. +release = version + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +# today = '' +# Else, today_fmt is used as the format for a strftime call. +# today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ["_build"] + +# The reST default role (used for this markup: `text`) to use for all +# documents. +# default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +# add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +# add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +# show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = "sphinx" + +# A list of ignored prefixes for module index sorting. +# modindex_common_prefix = [] + +# If true, keep warnings as "system message" paragraphs in the built documents. +# keep_warnings = False + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# html_theme = 'default' + +# on_rtd is whether we are on readthedocs.org, this line of code grabbed from docs.readthedocs.org +on_rtd = os.environ.get("READTHEDOCS", None) == "True" + +if not on_rtd: # only import and set the theme if we're building docs locally + import sphinx_rtd_theme + + html_theme = "sphinx_rtd_theme" + html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] +else: + html_theme = "default" + + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +# html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +# html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +# html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +# html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +# html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +# html_static_path = ['_static'] + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +# html_extra_path = [] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +# html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +# html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +# html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +# html_additional_pages = {} + +# If false, no module index is generated. +# html_domain_indices = True + +# If false, no index is generated. +# html_use_index = True + +# If true, the index is split into individual pages for each letter. +# html_split_index = False + +# If true, links to the reST sources are added to the pages. +# html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +# html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +# html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +# html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +# html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = "sphinxdoc" + + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + #'papersize': 'letterpaper', + # The font size ('10pt', '11pt' or '12pt'). + #'pointsize': '10pt', + # Additional stuff for the LaTeX preamble. + #'preamble': '', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + ( + "index", + "sphinx.tex", + u"robotpy-cppheaderparser Documentation", + u"Author", + "manual", + ) +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +# latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +# latex_use_parts = False + +# If true, show page references after internal links. +# latex_show_pagerefs = False + +# If true, show URL addresses after external links. +# latex_show_urls = False + +# Documents to append as an appendix to all manuals. +# latex_appendices = [] + +# If false, no module index is generated. +# latex_domain_indices = True + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ("index", "sphinx", u"robotpy-cppheaderparser Documentation", [u"Author"], 1) +] + +# If true, show URL addresses after external links. +# man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ( + "index", + "sphinx", + u"robotpy-cppheaderparser Documentation", + u"Author", + "sphinx", + "One line description of project.", + "Miscellaneous", + ) +] + +# Documents to append as an appendix to all manuals. +# texinfo_appendices = [] + +# If false, no module index is generated. +# texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +# texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +# texinfo_no_detailmenu = False + + +# -- Options for Epub output ---------------------------------------------- + +# Bibliographic Dublin Core info. +epub_title = u"robotpy-cppheaderparser" +epub_author = u"RobotPy Development Team" +epub_publisher = u"RobotPy Development Team" +epub_copyright = u"2019 RobotPy Development Team" + +# The basename for the epub file. It defaults to the project name. +# epub_basename = u'..' + +# The HTML theme for the epub output. Since the default themes are not optimized +# for small screen space, using the same theme for HTML and epub output is +# usually not wise. This defaults to 'epub', a theme designed to save visual +# space. +# epub_theme = 'epub' + +# The language of the text. It defaults to the language option +# or en if the language is not set. +# epub_language = '' + +# The scheme of the identifier. Typical schemes are ISBN or URL. +# epub_scheme = '' + +# The unique identifier of the text. This can be a ISBN number +# or the project homepage. +# epub_identifier = '' + +# A unique identification for the text. +# epub_uid = '' + +# A tuple containing the cover image and cover page html template filenames. +# epub_cover = () + +# A sequence of (type, uri, title) tuples for the guide element of content.opf. +# epub_guide = () + +# HTML files that should be inserted before the pages created by sphinx. +# The format is a list of tuples containing the path and title. +# epub_pre_files = [] + +# HTML files shat should be inserted after the pages created by sphinx. +# The format is a list of tuples containing the path and title. +# epub_post_files = [] + +# A list of files that should not be packed into the epub file. +epub_exclude_files = ["search.html"] + +# The depth of the table of contents in toc.ncx. +# epub_tocdepth = 3 + +# Allow duplicate toc entries. +# epub_tocdup = True + +# Choose between 'default' and 'includehidden'. +# epub_tocscope = 'default' + +# Fix unsupported image types using the PIL. +# epub_fix_images = False + +# Scale large images. +# epub_max_image_width = 0 + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +# epub_show_urls = 'inline' + +# If false, no index is generated. +# epub_use_index = True diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..d90e920 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,5 @@ +.. include:: ../README.rst + +.. toctree:: + + api diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..4bf38ef --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,242 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=_build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . +set I18NSPHINXOPTS=%SPHINXOPTS% . +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% + set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. texinfo to make Texinfo files + echo. gettext to make PO message catalogs + echo. changes to make an overview over all changed/added/deprecated items + echo. xml to make Docutils-native XML files + echo. pseudoxml to make pseudoxml-XML files for display purposes + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + + +%SPHINXBUILD% 2> nul +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\sphinx.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\sphinx.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdf" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf + cd %BUILDDIR%/.. + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdfja" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf-ja + cd %BUILDDIR%/.. + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "texinfo" ( + %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. + goto end +) + +if "%1" == "gettext" ( + %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The message catalogs are in %BUILDDIR%/locale. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + if errorlevel 1 exit /b 1 + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + if errorlevel 1 exit /b 1 + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + if errorlevel 1 exit /b 1 + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +if "%1" == "xml" ( + %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The XML files are in %BUILDDIR%/xml. + goto end +) + +if "%1" == "pseudoxml" ( + %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. + goto end +) + +:end diff --git a/setup.py b/setup.py index 620986e..7a0f5f0 100644 --- a/setup.py +++ b/setup.py @@ -67,7 +67,7 @@ maintainer_email="robotpy@googlegroups.com", url="https://github.com/robotpy/robotpy-cppheaderparser", description=DESCRIPTION, - long_description=open("README.md").read(), + long_description=open("README.rst").read(), license="BSD", platforms="Platform Independent", packages=["CppHeaderParser"], @@ -75,7 +75,5 @@ classifiers=CLASSIFIERS, requires=["ply"], install_requires=["ply"], - package_data={ - "CppHeaderParser": ["README", "README.html", "doc/*.*", "examples/*.*"] - }, + package_data={"CppHeaderParser": ["README", "README.html", "examples/*.*"]}, ) diff --git a/templates/README.txt b/templates/README.txt deleted file mode 100644 index 2ff1451..0000000 --- a/templates/README.txt +++ /dev/null @@ -1,27 +0,0 @@ -Python package "CppHeaderParser" --------------------------------- -**Purpose:** Parse C++ header files and generate a data structure representing the class - -**Author:** Jashua Cloutier - -**Licence:** BSD - -**External modules required:** PLY - -**Quick start**:: - -{SAMPLE_CLASS_H} - -**Python code**:: - -{READ_SAMPLE_CLASS_PY} - -**Output**:: - -{READ_SAMPLE_CLASS_PY_OUTPUT} - - -Contributors ------------- -* Chris Love -* HartsAntler From 20be87a93cd8f15a886469f8018501d43d9faf01 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Fri, 27 Sep 2019 02:17:10 -0400 Subject: [PATCH 025/143] Add support for 'using' statements that bring in a single definition - Fixes #19 --- CppHeaderParser/CppHeaderParser.py | 84 ++++++++++++++++++-- CppHeaderParser/test/test_CppHeaderParser.py | 60 +++++++++++++- 2 files changed, 136 insertions(+), 8 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 6bcd961..9de8d6a 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -377,6 +377,48 @@ def filter_out_attribute_keyword(stack): return stack +_nhack = re.compile(r"[A-Za-z_][A-Za-z0-9_]*") + + +def _split_namespace(namestack): + """ + Given a list of name elements, find the namespace portion + and return that as a string + + :rtype: Tuple[str, list] + """ + last_colon = None + for i, n in enumerate(namestack): + if n == ":": + last_colon = i + if i and n != ":" and not _nhack.match(n): + break + + if last_colon: + ns, namestack = ( + "".join(namestack[: last_colon + 1]), + namestack[last_colon + 1 :], + ) + else: + ns = "" + + return ns, namestack + + +def _iter_ns_str_reversed(namespace): + """ + Take a namespace string, and yield successively smaller portions + of it (ex foo::bar::baz::, foo::bar::, foo::). The last item yielded + will always be an empty string + """ + if namespace: + parts = namespace.split("::") + for i in range(len(parts) - 1, 0, -1): + yield "::".join(parts[:i]) + "::" + + yield "" + + class TagStr(str): """Wrapper for a string that allows us to store the line number associated with it""" @@ -1577,6 +1619,19 @@ def resolve_type(self, string, result): # recursive result["fundamental"] = False result["class"] = klass result["unresolved"] = False + elif self.using: + # search for type in all enclosing namespaces + for ns in _iter_ns_str_reversed(result.get("namespace", "")): + nsalias = ns + alias + used = self.using.get(nsalias) + if used: + for i in ("type", "namespace", "ctypes_type", "raw_type"): + if i in used: + result[i] = used[i] + result["unresolved"] = False + break + else: + result["unresolved"] = True else: result["unresolved"] = True else: @@ -2567,6 +2622,10 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self.anon_union_counter = [-1, 0] self.templateRegistry = [] + #: Using directives in this header: key is full name for lookup, value + #: is :class:`.CppVariable` + self.using = {} + if len(self.headerFileName): fd = io.open(self.headerFileName, "r", encoding=encoding) headerFileStr = "".join(fd.readlines()) @@ -3035,12 +3094,25 @@ def _evaluate_stack(self, token=None): len(self.nameStack) == 2 and self.nameStack[0] == "friend" ): # friend class declaration pass - elif ( - len(self.nameStack) >= 2 - and self.nameStack[0] == "using" - and self.nameStack[1] == "namespace" - ): - pass # TODO + elif len(self.nameStack) >= 2 and self.nameStack[0] == "using": + if self.nameStack[1] == "namespace": + pass + else: + if len(self.nameStack) > 3 and self.nameStack[2] == "=": + # using foo = ns::bar + alias = self.nameStack[1] + ns, stack = _split_namespace(self.nameStack[3:]) + atype = CppVariable(stack) + else: + # using foo::bar + ns, stack = _split_namespace(self.nameStack[1:]) + atype = CppVariable(stack) + alias = atype["type"] + + atype["namespace"] = ns + atype["raw_type"] = ns + atype["type"] + alias = self.current_namespace() + alias + self.using[alias] = atype elif is_enum_namestack(self.nameStack): debug_print("trace") diff --git a/CppHeaderParser/test/test_CppHeaderParser.py b/CppHeaderParser/test/test_CppHeaderParser.py index c877c85..2769419 100644 --- a/CppHeaderParser/test/test_CppHeaderParser.py +++ b/CppHeaderParser/test/test_CppHeaderParser.py @@ -5,11 +5,14 @@ import CppHeaderParser as CppHeaderParser -def filter_pameters(p): +def filter_pameters(p, extra=[]): "Reduce a list of dictionaries to the desired keys for function parameter testing" rtn = [] for d in p: - rtn.append({"name": d["name"], "desc": d["desc"], "type": d["type"]}) + rd = {} + for k in ["name", "desc", "type"] + extra: + rd[k] = d.get(k) + rtn.append(rd) return rtn @@ -2759,5 +2762,58 @@ def test_vararg_func(self): self.assertEqual(len(vf["parameters"]), len(nvf["parameters"])) +class UsingNamespace_TestCase(unittest.TestCase): + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader( + """ +using std::thing; +namespace a { + using std::string; + using VoidFunction = std::function; + + void fn(string &s, VoidFunction fn, thing * t); +} +""", + "string", + ) + + def test_using(self): + self.assertIn("a::string", self.cppHeader.using) + self.assertIn("a::VoidFunction", self.cppHeader.using) + self.assertIn("thing", self.cppHeader.using) + + def test_fn(self): + self.maxDiff = None + self.assertEqual(len(self.cppHeader.functions), 1) + fn = self.cppHeader.functions[0] + self.assertEqual(fn["name"], "fn") + self.assertEqual( + filter_pameters(fn["parameters"], ["namespace", "raw_type"]), + [ + { + "type": "string", + "name": "s", + "desc": None, + "namespace": "std::", + "raw_type": "std::string", + }, + { + "type": "function", + "name": "fn", + "desc": None, + "namespace": "std::", + "raw_type": "std::function", + }, + { + "type": "thing", + "name": "t", + "desc": None, + "namespace": "std::", + "raw_type": "std::thing", + }, + ], + ) + + if __name__ == "__main__": unittest.main() From e2f678603c231a22c85fc8a1f23e0b215e865f2d Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 29 Sep 2019 00:20:16 -0400 Subject: [PATCH 026/143] Default is preferred, not defaultValue --- CppHeaderParser/CppHeaderParser.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 9de8d6a..ef51107 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -1153,7 +1153,7 @@ class CppVariable(_CppVariable): * ``namespace`` - Namespace * ``desc`` - Description of the variable if part of a method (optional) * ``doxygen`` - Doxygen comments associated with the method if they exist - * ``defaultValue`` - Default value of the variable, this key will only + * ``default`` - Default value of the variable, this key will only exist if there is a default value * ``extern`` - True if its an extern, False if not """ @@ -1223,10 +1223,9 @@ def __init__(self, nameStack, **kwargs): elif "=" in nameStack: self["type"] = " ".join(nameStack[: nameStack.index("=") - 1]) self["name"] = nameStack[nameStack.index("=") - 1] - self["defaultValue"] = " ".join( - nameStack[nameStack.index("=") + 1 :] - ) # deprecate camelCase in dicts self["default"] = " ".join(nameStack[nameStack.index("=") + 1 :]) + # backwards compat; deprecate camelCase in dicts + self["defaultValue"] = self["default"] elif is_fundamental(nameStack[-1]) or nameStack[-1] in [">", "<", ":", "."]: # Un named parameter From 16d26559145b62bc50e3b22674967a7daadfb7d4 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 29 Sep 2019 01:34:55 -0400 Subject: [PATCH 027/143] rtnType for static methods shouldn't include 'static' --- CppHeaderParser/CppHeaderParser.py | 2 ++ CppHeaderParser/test/test_CppHeaderParser.py | 17 +++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index ef51107..3077d58 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -960,6 +960,8 @@ def __init__(self, nameStack, curClass, methinfo, curTemplate): ) if self["rtnType"].startswith("virtual"): self["rtnType"] = self["rtnType"][len("virtual") :].strip() + elif self["rtnType"].startswith("static"): + self["rtnType"] = self["rtnType"][len("static") :].strip() if len(self["rtnType"]) == 0 or self["name"] == curClass: self["rtnType"] = "void" diff --git a/CppHeaderParser/test/test_CppHeaderParser.py b/CppHeaderParser/test/test_CppHeaderParser.py index 2769419..fd6abd2 100644 --- a/CppHeaderParser/test/test_CppHeaderParser.py +++ b/CppHeaderParser/test/test_CppHeaderParser.py @@ -2815,5 +2815,22 @@ def test_fn(self): ) +class StaticFn_TestCase(unittest.TestCase): + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader( + """ +class A { + static int fn(); +}; +""", + "string", + ) + + def test_fn(self): + m = self.cppHeader.classes["A"]["methods"]["private"][0] + self.assertEqual(m["static"], True) + self.assertEqual(m["rtnType"], "int") + + if __name__ == "__main__": unittest.main() From 12d4d0f2d778fb15245c7269c7122e5cb491f801 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Fri, 25 Oct 2019 00:41:37 -0400 Subject: [PATCH 028/143] Add minor constexpr support --- CppHeaderParser/CppHeaderParser.py | 7 ++++--- CppHeaderParser/test/test_CppHeaderParser.py | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 3077d58..c7ff376 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -1137,7 +1137,7 @@ def init(self): self["typedef"] = None for ( key - ) in "constant reference pointer static typedefs class fundamental unresolved".split(): + ) in "constant constexpr reference pointer static typedefs class fundamental unresolved".split(): self[key] = 0 for b in self["type"].split(): if b == "__const__": @@ -1580,6 +1580,7 @@ def resolve_type(self, string, result): # recursive ## these come before a template s = string.split("<")[0] result["constant"] += s.split().count("const") + result["constexpr"] += s.split().count("constexpr") result["static"] += s.split().count("static") result["mutable"] = "mutable" in s.split() @@ -1590,7 +1591,7 @@ def resolve_type(self, string, result): # recursive x = string alias = False - for a in "* & const static mutable".split(): + for a in "* & const constexpr static mutable".split(): x = x.replace(a, "") for y in x.split(): if y not in self.C_FUNDAMENTAL: @@ -1877,7 +1878,7 @@ def finalize_vars(self): var["method"]["unresolved_parameters"] = True # create stripped raw_type # - p = "* & const static mutable".split() # +++ new July7: "mutable" + p = "* & const constexpr static mutable".split() # +++ new July7: "mutable" for var in CppVariable.Vars: if "raw_type" not in var: raw = [] diff --git a/CppHeaderParser/test/test_CppHeaderParser.py b/CppHeaderParser/test/test_CppHeaderParser.py index fd6abd2..63be55c 100644 --- a/CppHeaderParser/test/test_CppHeaderParser.py +++ b/CppHeaderParser/test/test_CppHeaderParser.py @@ -2832,5 +2832,24 @@ def test_fn(self): self.assertEqual(m["rtnType"], "int") +class ConstExpr_TestCase(unittest.TestCase): + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader( + """ +class A { + static constexpr double kThing = 0.02; +}; +""", + "string", + ) + + def test_fn(self): + p = self.cppHeader.classes["A"]["properties"]["private"][0] + self.assertEqual(p["static"], 1) + self.assertEqual(p["constexpr"], 1) + self.assertEqual(p["raw_type"], "double") + self.assertEqual(p["defaultValue"], "0.02") + + if __name__ == "__main__": unittest.main() From 0ff0ff44e7883f05f3f68c3e3aa23789d29e96a1 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Thu, 31 Oct 2019 23:29:22 -0400 Subject: [PATCH 029/143] Add support for nameless enums with default values as part of classes --- CppHeaderParser/CppHeaderParser.py | 44 +++++++++------ CppHeaderParser/test/test_CppHeaderParser.py | 56 ++++++++++++++++++++ 2 files changed, 84 insertions(+), 16 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index c7ff376..7d456c8 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -419,6 +419,17 @@ def _iter_ns_str_reversed(namespace): yield "" +def _split_by_comma(namestack): + while namestack: + if "," not in namestack: + yield namestack + break + idx = namestack.index(",") + ns = namestack[:idx] + yield ns + namestack = namestack[idx + 1 :] + + class TagStr(str): """Wrapper for a string that allows us to store the line number associated with it""" @@ -1351,7 +1362,7 @@ def __init__(self, nameStack): if len(nameStack) == 3 and nameStack[0] == "enum": debug_print("Created enum as just name/value") self["name"] = nameStack[1] - self["instances"] = [nameStack[2]] + self["instances"] = [[nameStack[2]]] if len(nameStack) < 4 or "{" not in nameStack or "}" not in nameStack: # Not enough stuff for an enum debug_print("Bad enum") @@ -1407,11 +1418,8 @@ def __init__(self, nameStack): warning_print("WARN-enum: nameless enum %s" % nameStack) # See if there are instances of this if "typedef" not in nameStack and len(postBraceStack): - self["instances"] = [] - for var in postBraceStack: - if "," in var: - continue - self["instances"].append(var) + self["instances"] = list(_split_by_comma(postBraceStack)) + self["namespace"] = "" @@ -2377,10 +2385,10 @@ def _evaluate_typedef(self): if name not in self.typedefs_order: self.typedefs_order.append(name) - def _evaluate_property_stack(self): + def _evaluate_property_stack(self, clearStack=True, addToVar=None): """Create a Property out of the name stack""" global parseHistory - assert self.stack[-1] == ";" + assert self.stack and self.stack[-1] == ";" debug_print("trace") if self.nameStack[0] == "typedef": if self.curClass: @@ -2425,15 +2433,13 @@ def _evaluate_property_stack(self): % self.nameStack ) orig_nameStack = self.nameStack[:] - orig_stack = self.stack[:] type_nameStack = orig_nameStack[: leftMostComma - 1] for name in orig_nameStack[leftMostComma - 1 :: 2]: self.nameStack = type_nameStack + [name] - self.stack = orig_stack[ - : - ] # Not maintained for mucking, but this path it doesnt matter - self._evaluate_property_stack() + self._evaluate_property_stack( + clearStack=False, addToVar=addToVar + ) return newVar = CppVariable(self.nameStack) @@ -2448,12 +2454,17 @@ def _evaluate_property_stack(self): parseHistory.append( {"braceDepth": self.braceDepth, "item_type": "variable", "item": newVar} ) + if addToVar: + newVar.update(addToVar) else: debug_print("Found Global variable") newVar = CppVariable(self.nameStack) + if addToVar: + newVar.update(addToVar) self.variables.append(newVar) - self.stack = [] # CLEAR STACK + if clearStack: + self.stack = [] # CLEAR STACK def _evaluate_class_stack(self): """Create a Class out of the name stack (but not its parts)""" @@ -3232,9 +3243,10 @@ def _evaluate_enum_stack(self): instanceType = "enum" if "name" in newEnum: instanceType = newEnum["name"] + addToVar = {"enum_type": newEnum} for instance in newEnum["instances"]: - self.nameStack = [instanceType, instance] - self._evaluate_property_stack() + self.nameStack = [instanceType] + instance + self._evaluate_property_stack(clearStack=False, addToVar=addToVar) del newEnum["instances"] def _strip_parent_keys(self): diff --git a/CppHeaderParser/test/test_CppHeaderParser.py b/CppHeaderParser/test/test_CppHeaderParser.py index 63be55c..a9eaea4 100644 --- a/CppHeaderParser/test/test_CppHeaderParser.py +++ b/CppHeaderParser/test/test_CppHeaderParser.py @@ -2851,5 +2851,61 @@ def test_fn(self): self.assertEqual(p["defaultValue"], "0.02") +class DefaultEnum_TestCase(unittest.TestCase): + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader( + """ +class A { + enum { + v1, + v2, + } m_v1 = v1; + + enum { + vv1, + vv2, vv3 + } m_v2 = vv2, m_v3 = vv3; +}; +""", + "string", + ) + + def test_fn(self): + p = self.cppHeader.classes["A"]["properties"]["private"][0] + self.assertEqual("enum", p["type"]) + self.assertEqual("m_v1", p["name"]) + self.assertEqual("v1", p["default"]) + self.assertEqual( + p.get("enum_type", {}).get("values"), + [{"name": "v1", "value": 0}, {"name": "v2", "value": 1}], + ) + + p = self.cppHeader.classes["A"]["properties"]["private"][1] + self.assertEqual("enum", p["type"]) + self.assertEqual("m_v2", p["name"]) + self.assertEqual("vv2", p["default"]) + self.assertEqual( + p.get("enum_type", {}).get("values"), + [ + {"name": "vv1", "value": 0}, + {"name": "vv2", "value": 1}, + {"name": "vv3", "value": 2}, + ], + ) + + p = self.cppHeader.classes["A"]["properties"]["private"][2] + self.assertEqual("enum", p["type"]) + self.assertEqual("m_v3", p["name"]) + self.assertEqual("vv3", p["default"]) + self.assertEqual( + p.get("enum_type", {}).get("values"), + [ + {"name": "vv1", "value": 0}, + {"name": "vv2", "value": 1}, + {"name": "vv3", "value": 2}, + ], + ) + + if __name__ == "__main__": unittest.main() From c14889a7b1155dc4da2db3e0f9acbb1b42941c72 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Thu, 31 Oct 2019 23:39:59 -0400 Subject: [PATCH 030/143] Update black --- CppHeaderParser/CppHeaderParser.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 7d456c8..2763b66 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -1514,9 +1514,7 @@ def guess_ctypes_type(self, string): else: u = "" if "long" in a and "double" in a: - b = ( - "longdouble" - ) # there is no ctypes.c_ulongdouble (this is a 64bit float?) + b = "longdouble" # there is no ctypes.c_ulongdouble (this is a 64bit float?) elif a.count("long") == 2 and "int" in a: b = "%sint64" % u elif a.count("long") == 2: @@ -1564,9 +1562,7 @@ def guess_ctypes_type(self, string): b = "void_p" elif string in "struct union".split(): - b = ( - "void_p" - ) # what should be done here? don't trust struct, it could be a class, no need to expose via ctypes + b = "void_p" # what should be done here? don't trust struct, it could be a class, no need to expose via ctypes else: b = "void_p" From b0f0d9577e79dc726613bb7be77e5d2d5abca99b Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Fri, 1 Nov 2019 08:58:35 -0400 Subject: [PATCH 031/143] Vague optimizations --- CppHeaderParser/CppHeaderParser.py | 84 ++++++++++++++++++------------ 1 file changed, 50 insertions(+), 34 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 2763b66..aa6dec9 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -245,24 +245,29 @@ def is_enum_namestack(nameStack): return False +_fundamentals = set( + [ + "size_t", + "struct", + "union", + "unsigned", + "signed", + "bool", + "char", + "short", + "int", + "float", + "double", + "long", + "void", + "*", + ] +) + + def is_fundamental(s): for a in s.split(): - if a not in [ - "size_t", - "struct", - "union", - "unsigned", - "signed", - "bool", - "char", - "short", - "int", - "float", - "double", - "long", - "void", - "*", - ]: + if a not in _fundamentals: return False return True @@ -1122,6 +1127,12 @@ def __str__(self): return "%s" % cpy +_var_keywords = { + n: 0 + for n in "constant constexpr reference pointer static typedefs class fundamental unresolved".split() +} + + class _CppVariable(dict): def _name_stack_helper(self, stack): stack = list(stack) @@ -1146,10 +1157,7 @@ def init(self): self["aliases"] = [] self["parent"] = None self["typedef"] = None - for ( - key - ) in "constant constexpr reference pointer static typedefs class fundamental unresolved".split(): - self[key] = 0 + self.update(_var_keywords) for b in self["type"].split(): if b == "__const__": b = "const" @@ -1468,6 +1476,12 @@ def standardize_fundamental(s): class Resolver(object): C_FUNDAMENTAL = "size_t unsigned signed bool char wchar short int float double long void".split() C_FUNDAMENTAL += "struct union enum".split() + C_FUNDAMENTAL = set(C_FUNDAMENTAL) + + C_MODIFIERS = "* & const constexpr static mutable".split() + C_MODIFIERS = set(C_MODIFIERS) + + C_KEYWORDS = "extern virtual static explicit inline friend".split() SubTypedefs = {} # TODO deprecate? NAMESPACES = [] @@ -1561,7 +1575,7 @@ def guess_ctypes_type(self, string): elif "void" in a: b = "void_p" - elif string in "struct union".split(): + elif string in ("struct", "union"): b = "void_p" # what should be done here? don't trust struct, it could be a class, no need to expose via ctypes else: b = "void_p" @@ -1582,11 +1596,11 @@ def resolve_type(self, string, result): # recursive """ ## be careful with templates, what is inside can be a pointer but the overall type is not a pointer ## these come before a template - s = string.split("<")[0] - result["constant"] += s.split().count("const") - result["constexpr"] += s.split().count("constexpr") - result["static"] += s.split().count("static") - result["mutable"] = "mutable" in s.split() + s = string.split("<")[0].split() + result["constant"] += s.count("const") + result["constexpr"] += s.count("constexpr") + result["static"] += s.count("static") + result["mutable"] = "mutable" in s ## these come after a template s = string.split(">")[-1] @@ -1595,7 +1609,7 @@ def resolve_type(self, string, result): # recursive x = string alias = False - for a in "* & const constexpr static mutable".split(): + for a in self.C_MODIFIERS: x = x.replace(a, "") for y in x.split(): if y not in self.C_FUNDAMENTAL: @@ -1882,12 +1896,11 @@ def finalize_vars(self): var["method"]["unresolved_parameters"] = True # create stripped raw_type # - p = "* & const constexpr static mutable".split() # +++ new July7: "mutable" for var in CppVariable.Vars: if "raw_type" not in var: raw = [] for x in var["type"].split(): - if x not in p: + if x not in self.C_MODIFIERS: raw.append(x) var["raw_type"] = " ".join(raw) @@ -2136,6 +2149,11 @@ def _evaluate_struct_stack(self): self.curStruct = struct self._structs_brace_level[struct["type"]] = self.braceDepth + _method_type_defaults = { + n: False + for n in "defined pure_virtual operator constructor destructor extern template virtual static explicit inline friend returns returns_pointer returns_fundamental returns_class default".split() + } + def parse_method_type(self, stack): trace_print("meth type info", stack) if stack[0] in ":;" and stack[1] != ":": @@ -2152,10 +2170,8 @@ def parse_method_type(self, stack): "namespace": self.cur_namespace(add_double_colon=True), } - for ( - tag - ) in "defined pure_virtual operator constructor destructor extern template virtual static explicit inline friend returns returns_pointer returns_fundamental returns_class default".split(): - info[tag] = False + info.update(self._method_type_defaults) + header = stack[: stack.index("(")] header = " ".join(header) header = header.replace(" : : ", "::") @@ -2234,7 +2250,7 @@ def parse_method_type(self, stack): info["name"] = name - for tag in "extern virtual static explicit inline friend".split(): + for tag in self.C_KEYWORDS: if tag in a: info[tag] = True a.remove(tag) # inplace From 822ed960aa7241f3061d3f5491f9051c04e8eed5 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 10 Nov 2019 02:35:23 -0500 Subject: [PATCH 032/143] NotImplemented isn't a thing, NotImplementedError is --- CppHeaderParser/CppHeaderParser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index aa6dec9..125e1be 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -655,7 +655,7 @@ def __init__(self, nameStack, curTemplate): if ">" in tmpStack: pass # allow skip templates for now else: - raise NotImplemented + raise NotImplementedError if "class" in tmpInheritClass: inheritList.append(tmpInheritClass) From 5be43414cc00e02e34cc6f6c4c204705322c2f55 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 10 Nov 2019 02:35:51 -0500 Subject: [PATCH 033/143] Vague optimization --- CppHeaderParser/CppHeaderParser.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 125e1be..7a1a090 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -1961,14 +1961,15 @@ def finalize_vars(self): trace_print("Processing precomp_macro_buf: %s" % self._precomp_macro_buf) for m in self._precomp_macro_buf: macro = m.replace("\\n", "\n") + ml = macro.lower() try: - if macro.lower().startswith("#define"): + if ml.startswith("#define"): trace_print("Adding #define %s" % macro) self.defines.append(re.split("[\t ]+", macro, 1)[1].strip()) - elif macro.lower().startswith("#pragma"): + elif ml.startswith("#pragma"): trace_print("Adding #pragma %s" % macro) self.pragmas.append(re.split("[\t ]+", macro, 1)[1].strip()) - elif macro.lower().startswith("#include"): + elif ml.startswith("#include"): trace_print("Adding #include %s" % macro) self.includes.append(re.split("[\t ]+", macro, 1)[1].strip()) else: From 5550557c36a5d86fa96d762cff2bf07e3deac73e Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 10 Nov 2019 02:34:05 -0500 Subject: [PATCH 034/143] Split lexer and doxygen parsing to their own pieces --- CppHeaderParser/CppHeaderParser.py | 249 +++++++---------------------- CppHeaderParser/doxygen.py | 32 ++++ CppHeaderParser/lexer.py | 127 +++++++++++++++ 3 files changed, 214 insertions(+), 194 deletions(-) create mode 100644 CppHeaderParser/doxygen.py create mode 100644 CppHeaderParser/lexer.py diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 7a1a090..6383323 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -46,7 +46,6 @@ # -import ply.lex as lex import os import sys import re @@ -54,11 +53,8 @@ import inspect - -def lineno(): - """Returns the current line number in our program.""" - return inspect.currentframe().f_back.f_lineno - +from .lexer import Lexer +from .doxygen import extract_doxygen_method_params try: from .version import __version__ @@ -67,117 +63,6 @@ def lineno(): version = __version__ -tokens = [ - "NUMBER", - "FLOAT_NUMBER", - "TEMPLATE_NAME", - "NAME", - "OPEN_PAREN", - "CLOSE_PAREN", - "OPEN_BRACE", - "CLOSE_BRACE", - "OPEN_SQUARE_BRACKET", - "CLOSE_SQUARE_BRACKET", - "COLON", - "SEMI_COLON", - "COMMA", - "TAB", - "BACKSLASH", - "PIPE", - "PERCENT", - "EXCLAMATION", - "CARET", - "COMMENT_SINGLELINE", - "COMMENT_MULTILINE", - "PRECOMP_MACRO", - "PRECOMP_MACRO_CONT", - "ASTERISK", - "AMPERSTAND", - "EQUALS", - "MINUS", - "PLUS", - "DIVIDE", - "CHAR_LITERAL", - "STRING_LITERAL", - "NEW_LINE", - "SQUOTE", - "ELLIPSIS", - "DOT", -] - -t_ignore = " \r?@\f" -t_NUMBER = r"[0-9][0-9XxA-Fa-f]*" -t_FLOAT_NUMBER = r"[-+]?[0-9]*\.[0-9]+([eE][-+]?[0-9]+)?" -t_TEMPLATE_NAME = r"CppHeaderParser_template_[0-9]+" -t_NAME = r"[<>A-Za-z_~][A-Za-z0-9_]*" -t_OPEN_PAREN = r"\(" -t_CLOSE_PAREN = r"\)" -t_OPEN_BRACE = r"{" -t_CLOSE_BRACE = r"}" -t_OPEN_SQUARE_BRACKET = r"\[" -t_CLOSE_SQUARE_BRACKET = r"\]" -t_SEMI_COLON = r";" -t_COLON = r":" -t_COMMA = r"," -t_TAB = r"\t" -t_BACKSLASH = r"\\" -t_PIPE = r"\|" -t_PERCENT = r"%" -t_CARET = r"\^" -t_EXCLAMATION = r"!" -t_PRECOMP_MACRO = r"\#.*" -t_PRECOMP_MACRO_CONT = r".*\\\n" - - -def t_COMMENT_SINGLELINE(t): - r"\/\/.*\n?" - global doxygenCommentCache - if t.value.startswith("///") or t.value.startswith("//!"): - if doxygenCommentCache: - doxygenCommentCache += "\n" - if t.value.endswith("\n"): - doxygenCommentCache += t.value[:-1] - else: - doxygenCommentCache += t.value - t.lexer.lineno += len([a for a in t.value if a == "\n"]) - - -t_ASTERISK = r"\*" -t_MINUS = r"\-" -t_PLUS = r"\+" -t_DIVIDE = r"/(?!/)" -t_AMPERSTAND = r"&" -t_EQUALS = r"=" -t_CHAR_LITERAL = "'.'" -t_SQUOTE = "'" -t_ELLIPSIS = r"\.\.\." -t_DOT = r"\." -# found at http://wordaligned.org/articles/string-literals-and-regular-expressions -# TODO: This does not work with the string "bla \" bla" -t_STRING_LITERAL = r'"([^"\\]|\\.)*"' -# Found at http://ostermiller.org/findcomment.html -def t_COMMENT_MULTILINE(t): - r"/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/" - global doxygenCommentCache - if t.value.startswith("/**") or t.value.startswith("/*!"): - # not sure why, but get double new lines - v = t.value.replace("\n\n", "\n") - # strip prefixing whitespace - v = re.sub("\n[\\s]+\\*", "\n*", v) - doxygenCommentCache += v - t.lexer.lineno += len([a for a in t.value if a == "\n"]) - - -def t_NEWLINE(t): - r"\n+" - t.lexer.lineno += len(t.value) - - -def t_error(v): - print("Lex error: ", v) - - -lex.lex() # Controls error_print print_errors = 1 # Controls warning_print @@ -199,13 +84,11 @@ def warning_print(arg): def debug_print(arg): - global debug if debug: print(("[%4d] %s" % (inspect.currentframe().f_back.f_lineno, arg))) def trace_print(*arg): - global debug_trace if debug_trace: sys.stdout.write("[%s] " % (inspect.currentframe().f_back.f_lineno)) for a in arg: @@ -219,7 +102,6 @@ def trace_print(*arg): #: Symbols to ignore, usually special macros ignoreSymbols = ["Q_OBJECT"] -doxygenCommentCache = "" # Track what was added in what order and at what depth parseHistory = [] @@ -538,7 +420,7 @@ def get_pure_virtual_methods(self, type="public"): r[meth["name"]] = meth return r - def __init__(self, nameStack, curTemplate): + def __init__(self, nameStack, curTemplate, doxygen): #: hm self["nested_classes"] = [] self["parent"] = None @@ -554,10 +436,8 @@ def __init__(self, nameStack, curTemplate): if len(nameStack) < 2: nameStack.insert(1, "") # anonymous struct - global doxygenCommentCache - if len(doxygenCommentCache): - self["doxygen"] = doxygenCommentCache - doxygenCommentCache = "" + if doxygen: + self["doxygen"] = doxygen if "::" in "".join(nameStack): # Re-Join class paths (ex ['class', 'Bar', ':', ':', 'Foo'] -> ['class', 'Bar::Foo'] @@ -826,8 +706,8 @@ class CppUnion(CppClass): * ``members`` - List of members of the union """ - def __init__(self, nameStack): - CppClass.__init__(self, nameStack, None) + def __init__(self, nameStack, doxygen): + CppClass.__init__(self, nameStack, None, doxygen) self["name"] = "union " + self["name"] self["members"] = self["properties"]["public"] @@ -957,13 +837,13 @@ def show(self): r.append("destructor") return "\n\t\t ".join(r) - def __init__(self, nameStack, curClass, methinfo, curTemplate): + def __init__(self, nameStack, curClass, methinfo, curTemplate, doxygen): debug_print("Method: %s" % nameStack) debug_print("Template: %s" % curTemplate) - global doxygenCommentCache - if len(doxygenCommentCache): - self["doxygen"] = doxygenCommentCache - doxygenCommentCache = "" + + if doxygen: + self["doxygen"] = doxygen + if "operator" in nameStack: self["rtnType"] = " ".join(nameStack[: nameStack.index("operator")]) self["name"] = "".join( @@ -1050,34 +930,10 @@ def __init__(self, nameStack, curClass, methinfo, curTemplate): params = [] # See if there is a doxygen comment for the variable - doxyVarDesc = {} - if "doxygen" in self: - doxyLines = self["doxygen"].split("\n") - lastParamDesc = "" - for doxyLine in doxyLines: - if " @param " in doxyLine or " \\param " in doxyLine: - try: - # Strip out the param - doxyLine = doxyLine[doxyLine.find("param ") + 6 :] - (var, desc) = doxyLine.split(" ", 1) - doxyVarDesc[var] = desc.strip() - lastParamDesc = var - except: - pass - elif " @return " in doxyLine or " \return " in doxyLine: - lastParamDesc = "" - # not handled for now - elif lastParamDesc: - try: - doxyLine = doxyLine.strip() - if " " not in doxyLine: - lastParamDesc = "" - continue - doxyLine = doxyLine[doxyLine.find(" ") + 1 :] - doxyVarDesc[lastParamDesc] += " " + doxyLine - except: - pass + doxyVarDesc = extract_doxygen_method_params(self["doxygen"]) + else: + doxyVarDesc = {} # non-vararg by default self["vararg"] = False @@ -1104,7 +960,7 @@ def __init__(self, nameStack, curClass, methinfo, curTemplate): if param_separator: param = CppVariable( - paramsStack[0:param_separator], doxyVarDesc=doxyVarDesc + paramsStack[0:param_separator], None, doxyVarDesc=doxyVarDesc ) if len(list(param.keys())): params.append(param) @@ -1113,7 +969,7 @@ def __init__(self, nameStack, curClass, methinfo, curTemplate): self["vararg"] = True paramsStack = paramsStack[1:] else: - param = CppVariable(paramsStack, doxyVarDesc=doxyVarDesc) + param = CppVariable(paramsStack, None, doxyVarDesc=doxyVarDesc) if len(list(param.keys())): params.append(param) break @@ -1172,8 +1028,8 @@ class CppVariable(_CppVariable): * ``type`` - Type for the variable (ex. "const string &") * ``name`` - Name of the variable (ex. "numItems") * ``namespace`` - Namespace - * ``desc`` - Description of the variable if part of a method (optional) - * ``doxygen`` - Doxygen comments associated with the method if they exist + * ``desc`` - If a method/function parameter, doxygen description for this parameter (optional) + * ``doxygen`` - If a normal property/variable, doxygen description for this * ``default`` - Default value of the variable, this key will only exist if there is a default value * ``extern`` - True if its an extern, False if not @@ -1181,7 +1037,7 @@ class CppVariable(_CppVariable): Vars = [] - def __init__(self, nameStack, **kwargs): + def __init__(self, nameStack, doxygen, **kwargs): debug_print("trace %s" % nameStack) if len(nameStack) and nameStack[0] == "extern": self["extern"] = True @@ -1213,10 +1069,9 @@ def __init__(self, nameStack, **kwargs): else: self["array"] = 0 nameStack = self._name_stack_helper(nameStack) - global doxygenCommentCache - if len(doxygenCommentCache): - self["doxygen"] = doxygenCommentCache - doxygenCommentCache = "" + + if doxygen: + self["doxygen"] = doxygen debug_print("Variable: %s" % nameStack) @@ -1362,11 +1217,9 @@ class CppEnum(_CppEnum): if a value for a given enum value was defined """ - def __init__(self, nameStack): - global doxygenCommentCache - if len(doxygenCommentCache): - self["doxygen"] = doxygenCommentCache - doxygenCommentCache = "" + def __init__(self, nameStack, doxygen): + if doxygen: + self["doxygen"] = doxygen if len(nameStack) == 3 and nameStack[0] == "enum": debug_print("Created enum as just name/value") self["name"] = nameStack[1] @@ -2310,7 +2163,11 @@ def _evaluate_method_stack(self): info["class"] and info["class"] in self.classes ): # case where methods are defined outside of class newMethod = CppMethod( - self.nameStack, info["name"], info, self.curTemplate + self.nameStack, + info["name"], + info, + self.curTemplate, + self.lex.get_doxygen(), ) klass = self.classes[info["class"]] klass["methods"]["public"].append(newMethod) @@ -2322,7 +2179,11 @@ def _evaluate_method_stack(self): elif self.curClass: # normal case newMethod = CppMethod( - self.nameStack, self.curClass, info, self.curTemplate + self.nameStack, + self.curClass, + info, + self.curTemplate, + self.lex.get_doxygen(), ) klass = self.classes[self.curClass] klass["methods"][self.curAccessSpecifier].append(newMethod) @@ -2333,7 +2194,9 @@ def _evaluate_method_stack(self): newMethod["path"] = klass["name"] else: # non class functions debug_print("FREE FUNCTION") - newMethod = CppMethod(self.nameStack, None, info, self.curTemplate) + newMethod = CppMethod( + self.nameStack, None, info, self.curTemplate, self.lex.get_doxygen() + ) self.functions.append(newMethod) global parseHistory parseHistory.append( @@ -2455,7 +2318,7 @@ def _evaluate_property_stack(self, clearStack=True, addToVar=None): ) return - newVar = CppVariable(self.nameStack) + newVar = CppVariable(self.nameStack, self.lex.get_doxygen()) newVar["namespace"] = self.current_namespace() if self.curStruct: self.curStruct["fields"].append(newVar) @@ -2471,7 +2334,7 @@ def _evaluate_property_stack(self, clearStack=True, addToVar=None): newVar.update(addToVar) else: debug_print("Found Global variable") - newVar = CppVariable(self.nameStack) + newVar = CppVariable(self.nameStack, self.lex.get_doxygen()) if addToVar: newVar.update(addToVar) self.variables.append(newVar) @@ -2507,14 +2370,16 @@ def _evaluate_class_stack(self): "curAccessSpecifier changed/defaulted to %s" % self.curAccessSpecifier ) if self.nameStack[0] == "union": - newClass = CppUnion(self.nameStack) + newClass = CppUnion(self.nameStack, self.lex.get_doxygen()) if newClass["name"] == "union ": self.anon_union_counter = [self.braceDepth, 2] else: self.anon_union_counter = [self.braceDepth, 1] trace_print("NEW UNION", newClass["name"]) else: - newClass = CppClass(self.nameStack, self.curTemplate) + newClass = CppClass( + self.nameStack, self.curTemplate, self.lex.get_doxygen() + ) trace_print("NEW CLASS", newClass["name"]) newClass["declaration_method"] = self.nameStack[0] self.classes_order.append(newClass) # good idea to save ordering @@ -2584,8 +2449,6 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): kwargs - Supports the following keywords """ ## reset global state ## - global doxygenCommentCache - doxygenCommentCache = "" CppVariable.Vars = [] CppStruct.Structs = [] @@ -2773,12 +2636,14 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): ) self.braceDepth = 0 - lex.lex() + + lex = Lexer() lex.input(headerFileStr) + self.lex = lex + global curLine - global curChar curLine = 0 - curChar = 0 + try: while True: tok = lex.token() @@ -2796,7 +2661,6 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): if tok.type != "TEMPLATE_NAME": self.stack.append(tok.value) curLine = tok.lineno - curChar = tok.lexpos if tok.type in ("PRECOMP_MACRO", "PRECOMP_MACRO_CONT"): debug_print("PRECOMP: %s" % tok) self._precomp_macro_buf.append(tok.value) @@ -3034,6 +2898,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): # Delete some temporary variables for key in [ "_precomp_macro_buf", + "lex", "nameStack", "nameSpaces", "curAccessSpecifier", @@ -3059,7 +2924,6 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): def _evaluate_stack(self, token=None): """Evaluates the current name stack""" - global doxygenCommentCache self.nameStack = filter_out_attribute_keyword(self.nameStack) self.stack = filter_out_attribute_keyword(self.stack) @@ -3128,11 +2992,11 @@ def _evaluate_stack(self, token=None): # using foo = ns::bar alias = self.nameStack[1] ns, stack = _split_namespace(self.nameStack[3:]) - atype = CppVariable(stack) + atype = CppVariable(stack, self.lex.get_doxygen()) else: # using foo::bar ns, stack = _split_namespace(self.nameStack[1:]) - atype = CppVariable(stack) + atype = CppVariable(stack, self.lex.get_doxygen()) alias = atype["type"] atype["namespace"] = ns @@ -3212,17 +3076,14 @@ def _evaluate_stack(self, token=None): elif self.curStruct and self.stack[-1] == ";": self._evaluate_property_stack() # this catches fields of global structs self.nameStack = [] - doxygenCommentCache = "" elif self.braceDepth < 1: debug_print("trace") # Ignore global stuff for now debug_print("Global stuff: %s" % self.nameStack) self.nameStack = [] - doxygenCommentCache = "" elif self.braceDepth > len(self.nameSpaces) + 1: debug_print("trace") self.nameStack = [] - doxygenCommentCache = "" try: self.nameStackHistory[self.braceDepth] = (nameStackCopy, self.curClass) @@ -3231,13 +3092,13 @@ def _evaluate_stack(self, token=None): self.nameStack = ( [] ) # its a little confusing to have some if/else above return and others not, and then clearning the nameStack down here - doxygenCommentCache = "" + self.lex.doxygenCommentCache = "" self.curTemplate = None def _evaluate_enum_stack(self): """Create an Enum out of the name stack""" debug_print("evaluating enum") - newEnum = CppEnum(self.nameStack) + newEnum = CppEnum(self.nameStack, self.lex.get_doxygen()) if len(list(newEnum.keys())): if len(self.curClass): newEnum["namespace"] = self.cur_namespace(False) diff --git a/CppHeaderParser/doxygen.py b/CppHeaderParser/doxygen.py new file mode 100644 index 0000000..67c1584 --- /dev/null +++ b/CppHeaderParser/doxygen.py @@ -0,0 +1,32 @@ +def extract_doxygen_method_params(doxystr): + """ + Given a doxygen string for a method, extract parameter descriptions + """ + doxyVarDesc = {} + doxyLines = doxystr.split("\n") + lastParamDesc = "" + for doxyLine in doxyLines: + if " @param " in doxyLine or " \\param " in doxyLine: + try: + # Strip out the param + doxyLine = doxyLine[doxyLine.find("param ") + 6 :] + (var, desc) = doxyLine.split(" ", 1) + doxyVarDesc[var] = desc.strip() + lastParamDesc = var + except: + pass + elif " @return " in doxyLine or " \return " in doxyLine: + lastParamDesc = "" + # not handled for now + elif lastParamDesc: + try: + doxyLine = doxyLine.strip() + if " " not in doxyLine: + lastParamDesc = "" + continue + doxyLine = doxyLine[doxyLine.find(" ") + 1 :] + doxyVarDesc[lastParamDesc] += " " + doxyLine + except: + pass + + return doxyVarDesc diff --git a/CppHeaderParser/lexer.py b/CppHeaderParser/lexer.py new file mode 100644 index 0000000..c72bad8 --- /dev/null +++ b/CppHeaderParser/lexer.py @@ -0,0 +1,127 @@ +import ply.lex as lex +import re + + +class Lexer(object): + + tokens = [ + "NUMBER", + "FLOAT_NUMBER", + "TEMPLATE_NAME", + "NAME", + "OPEN_PAREN", + "CLOSE_PAREN", + "OPEN_BRACE", + "CLOSE_BRACE", + "OPEN_SQUARE_BRACKET", + "CLOSE_SQUARE_BRACKET", + "COLON", + "SEMI_COLON", + "COMMA", + "TAB", + "BACKSLASH", + "PIPE", + "PERCENT", + "EXCLAMATION", + "CARET", + "COMMENT_SINGLELINE", + "COMMENT_MULTILINE", + "PRECOMP_MACRO", + "PRECOMP_MACRO_CONT", + "ASTERISK", + "AMPERSTAND", + "EQUALS", + "MINUS", + "PLUS", + "DIVIDE", + "CHAR_LITERAL", + "STRING_LITERAL", + "NEW_LINE", + "SQUOTE", + "ELLIPSIS", + "DOT", + ] + + t_ignore = " \r?@\f" + t_NUMBER = r"[0-9][0-9XxA-Fa-f]*" + t_FLOAT_NUMBER = r"[-+]?[0-9]*\.[0-9]+([eE][-+]?[0-9]+)?" + t_TEMPLATE_NAME = r"CppHeaderParser_template_[0-9]+" + t_NAME = r"[<>A-Za-z_~][A-Za-z0-9_]*" + t_OPEN_PAREN = r"\(" + t_CLOSE_PAREN = r"\)" + t_OPEN_BRACE = r"{" + t_CLOSE_BRACE = r"}" + t_OPEN_SQUARE_BRACKET = r"\[" + t_CLOSE_SQUARE_BRACKET = r"\]" + t_SEMI_COLON = r";" + t_COLON = r":" + t_COMMA = r"," + t_TAB = r"\t" + t_BACKSLASH = r"\\" + t_PIPE = r"\|" + t_PERCENT = r"%" + t_CARET = r"\^" + t_EXCLAMATION = r"!" + t_PRECOMP_MACRO = r"\#.*" + t_PRECOMP_MACRO_CONT = r".*\\\n" + + def t_COMMENT_SINGLELINE(self, t): + r"\/\/.*\n?" + if t.value.startswith("///") or t.value.startswith("//!"): + if self.doxygenCommentCache: + self.doxygenCommentCache += "\n" + if t.value.endswith("\n"): + self.doxygenCommentCache += t.value[:-1] + else: + self.doxygenCommentCache += t.value + t.lexer.lineno += t.value.count("\n") + + t_ASTERISK = r"\*" + t_MINUS = r"\-" + t_PLUS = r"\+" + t_DIVIDE = r"/(?!/)" + t_AMPERSTAND = r"&" + t_EQUALS = r"=" + t_CHAR_LITERAL = "'.'" + t_SQUOTE = "'" + t_ELLIPSIS = r"\.\.\." + t_DOT = r"\." + + # found at http://wordaligned.org/articles/string-literals-and-regular-expressions + # TODO: This does not work with the string "bla \" bla" + t_STRING_LITERAL = r'"([^"\\]|\\.)*"' + + # Found at http://ostermiller.org/findcomment.html + def t_COMMENT_MULTILINE(self, t): + r"/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/" + if t.value.startswith("/**") or t.value.startswith("/*!"): + # not sure why, but get double new lines + v = t.value.replace("\n\n", "\n") + # strip prefixing whitespace + v = re.sub("\n[\\s]+\\*", "\n*", v) + self.doxygenCommentCache += v + t.lexer.lineno += len([a for a in t.value if a == "\n"]) + + def t_NEWLINE(self, t): + r"\n+" + t.lexer.lineno += len(t.value) + + def t_error(self, v): + print("Lex error: ", v) + + def __init__(self): + self.lex = lex.lex(module=self) + self.input = self.lex.input + self.token = self.lex.token + + # Doxygen comments + self.doxygenCommentCache = "" + + def get_doxygen(self): + doxygen = self.doxygenCommentCache + self.doxygenCommentCache = "" + return doxygen + + +if __name__ == "__main__": + lex.runmain(lexer=Lexer()) From 4f584bc7e7338fc24e5319eaae254e8ee8a26a3a Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Mon, 11 Nov 2019 22:54:03 -0500 Subject: [PATCH 035/143] More minor optimization --- CppHeaderParser/CppHeaderParser.py | 84 ++++++++++++++---------------- 1 file changed, 40 insertions(+), 44 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 6383323..85292d9 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -2432,6 +2432,43 @@ def evalute_forward_decl(self): self._forward_decls.append(name) +# fmt: off +_namestack_append_tokens = set([ + "OPEN_PAREN", + "CLOSE_PAREN", + "OPEN_SQUARE_BRACKET", + "CLOSE_SQUARE_BRACKET", + "EQUALS", + "COMMA", + "BACKSLASH", + "DIVIDE", + "PIPE", + "PERCENT", + "CARET", + "EXCLAMATION", + "NUMBER", + "FLOAT_NUMBER", + "MINUS", + "PLUS", + "STRING_LITERAL", + "ELLIPSIS", +]) + +_namestack_pass_tokens = set([ + "TAB", + "SQUOTE", + "DOT" # preserve behaviour and eat individual fullstops +]) + +_namestack_str_tokens = set([ + "NAME", + "AMPERSTAND", + "ASTERISK", + "CHAR_LITERAL" +]) +# fmt: on + + class CppHeader(_CppHeader): """Parsed C++ class header""" @@ -2768,52 +2805,11 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self.nameStack = [] trace_print("FORCE CLEAR METHBODY") - if tok.type == "OPEN_PAREN": - self.nameStack.append(tok.value) - elif tok.type == "CLOSE_PAREN": - self.nameStack.append(tok.value) - elif tok.type == "OPEN_SQUARE_BRACKET": - self.nameStack.append(tok.value) - elif tok.type == "CLOSE_SQUARE_BRACKET": - self.nameStack.append(tok.value) - elif tok.type == "TAB": - pass - elif tok.type == "EQUALS": - self.nameStack.append(tok.value) - elif tok.type == "COMMA": - self.nameStack.append(tok.value) - elif tok.type == "BACKSLASH": - self.nameStack.append(tok.value) - elif tok.type == "DIVIDE": + if tok.type in _namestack_append_tokens: self.nameStack.append(tok.value) - elif tok.type == "PIPE": - self.nameStack.append(tok.value) - elif tok.type == "PERCENT": - self.nameStack.append(tok.value) - elif tok.type == "CARET": - self.nameStack.append(tok.value) - elif tok.type == "EXCLAMATION": - self.nameStack.append(tok.value) - elif tok.type == "SQUOTE": + elif tok.type in _namestack_pass_tokens: pass - elif tok.type == "NUMBER" or tok.type == "FLOAT_NUMBER": - self.nameStack.append(tok.value) - elif tok.type == "MINUS": - self.nameStack.append(tok.value) - elif tok.type == "PLUS": - self.nameStack.append(tok.value) - elif tok.type == "STRING_LITERAL": - self.nameStack.append(tok.value) - elif tok.type == "ELLIPSIS": - self.nameStack.append(tok.value) - elif tok.type == "DOT": - pass # preserve behaviour and eat individual fullstops - elif ( - tok.type == "NAME" - or tok.type == "AMPERSTAND" - or tok.type == "ASTERISK" - or tok.type == "CHAR_LITERAL" - ): + elif tok.type in _namestack_str_tokens: if tok.value in ignoreSymbols: debug_print("Ignore symbol %s" % tok.value) elif tok.value == "class": From 677ddd7713970f513341859d254e14fe0da0d97c Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Mon, 11 Nov 2019 22:55:06 -0500 Subject: [PATCH 036/143] Fix spelling mistake --- CppHeaderParser/CppHeaderParser.py | 2 +- CppHeaderParser/lexer.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 85292d9..3abc4ea 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -2462,7 +2462,7 @@ def evalute_forward_decl(self): _namestack_str_tokens = set([ "NAME", - "AMPERSTAND", + "AMPERSAND", "ASTERISK", "CHAR_LITERAL" ]) diff --git a/CppHeaderParser/lexer.py b/CppHeaderParser/lexer.py index c72bad8..e36ecab 100644 --- a/CppHeaderParser/lexer.py +++ b/CppHeaderParser/lexer.py @@ -29,7 +29,7 @@ class Lexer(object): "PRECOMP_MACRO", "PRECOMP_MACRO_CONT", "ASTERISK", - "AMPERSTAND", + "AMPERSAND", "EQUALS", "MINUS", "PLUS", @@ -80,7 +80,7 @@ def t_COMMENT_SINGLELINE(self, t): t_MINUS = r"\-" t_PLUS = r"\+" t_DIVIDE = r"/(?!/)" - t_AMPERSTAND = r"&" + t_AMPERSAND = r"&" t_EQUALS = r"=" t_CHAR_LITERAL = "'.'" t_SQUOTE = "'" From 740484013f28011724025d5a2fe8d549c3311eb0 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Thu, 14 Nov 2019 01:30:24 -0500 Subject: [PATCH 037/143] Use 'raise Foo from bar' where supported --- CppHeaderParser/CppHeaderParser.py | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 3abc4ea..2e211ce 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -72,6 +72,14 @@ # Controls trace_print debug_trace = 0 +if sys.version_info >= (3, 3): + # `raise e from src_e` syntax only supported on python 3.3+ + exec("def raise_exc(e, src_e): raise e from src_e", globals()) +else: + + def raise_exc(e, src_e): + raise e + def error_print(arg): if print_errors: @@ -1797,7 +1805,7 @@ def finalize_vars(self): else: trace_print("-" * 80) trace_print(var) - raise NotImplemented + raise NotImplementedError ## need full name space for classes in raw type ## if var["raw_type"].startswith("::"): @@ -2880,12 +2888,20 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self.stack = [] self.nameStack = [] - except: + except Exception as e: if debug: raise - raise CppParseError( - 'Not able to parse %s on line %d evaluating "%s"\nError around: %s' - % (self.headerFileName, tok.lineno, tok.value, " ".join(self.nameStack)) + raise_exc( + CppParseError( + 'Not able to parse %s on line %d evaluating "%s"\nError around: %s' + % ( + self.headerFileName, + tok.lineno, + tok.value, + " ".join(self.nameStack), + ) + ), + e, ) self.finalize() From 0c151442037ce6474bfbe360e2fa51429f5c659f Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Thu, 14 Nov 2019 01:47:57 -0500 Subject: [PATCH 038/143] Use #line preprocessor directives to set the line number and filename --- CppHeaderParser/CppHeaderParser.py | 161 +++++++++++-------- CppHeaderParser/lexer.py | 22 ++- CppHeaderParser/test/test_CppHeaderParser.py | 48 ++++++ 3 files changed, 163 insertions(+), 68 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 2e211ce..ee2d40a 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -231,16 +231,11 @@ def is_property_namestack(nameStack): return r -def detect_lineno(s): - """Detect the line number for a given token string""" - try: - rtn = s.lineno() - if rtn != -1: - return rtn - except: - pass - global curLine - return curLine +def set_location_info(thing, location): + filename, line_number = location + if filename: + thing["filename"] = filename + thing["line_number"] = line_number def filter_out_attribute_keyword(stack): @@ -328,22 +323,11 @@ def _split_by_comma(namestack): class TagStr(str): """Wrapper for a string that allows us to store the line number associated with it""" - lineno_reg = {} - - def __new__(cls, *args, **kw): - new_obj = str.__new__(cls, *args) - if "lineno" in kw: - TagStr.lineno_reg[id(new_obj)] = kw["lineno"] - return new_obj - - def __del__(self): - try: - del TagStr.lineno_reg[id(self)] - except: - pass - - def lineno(self): - return TagStr.lineno_reg.get(id(self), -1) + def __new__(cls, *args, **kwargs): + location = kwargs.pop("location") + s = str.__new__(cls, *args, **kwargs) + s.location = location + return s class CppParseError(Exception): @@ -428,7 +412,7 @@ def get_pure_virtual_methods(self, type="public"): r[meth["name"]] = meth return r - def __init__(self, nameStack, curTemplate, doxygen): + def __init__(self, nameStack, curTemplate, doxygen, location): #: hm self["nested_classes"] = [] self["parent"] = None @@ -480,7 +464,7 @@ def __init__(self, nameStack, curTemplate, doxygen): pass self["name"] = nameStack[1] - self["line_number"] = detect_lineno(nameStack[0]) + set_location_info(self, location) # Handle template classes if len(nameStack) > 3 and nameStack[2].startswith("<"): @@ -714,8 +698,8 @@ class CppUnion(CppClass): * ``members`` - List of members of the union """ - def __init__(self, nameStack, doxygen): - CppClass.__init__(self, nameStack, None, doxygen) + def __init__(self, nameStack, doxygen, location): + CppClass.__init__(self, nameStack, None, doxygen, location) self["name"] = "union " + self["name"] self["members"] = self["properties"]["public"] @@ -845,7 +829,7 @@ def show(self): r.append("destructor") return "\n\t\t ".join(r) - def __init__(self, nameStack, curClass, methinfo, curTemplate, doxygen): + def __init__(self, nameStack, curClass, methinfo, curTemplate, doxygen, location): debug_print("Method: %s" % nameStack) debug_print("Template: %s" % curTemplate) @@ -911,7 +895,7 @@ def __init__(self, nameStack, curClass, methinfo, curTemplate, doxygen): break self.update(methinfo) - self["line_number"] = detect_lineno(nameStack[0]) + set_location_info(self, location) # Filter out initializer lists used in constructors try: @@ -967,8 +951,12 @@ def __init__(self, nameStack, curClass, methinfo, curTemplate, doxygen): i += 1 if param_separator: + tpstack = paramsStack[0:param_separator] param = CppVariable( - paramsStack[0:param_separator], None, doxyVarDesc=doxyVarDesc + tpstack, + None, + getattr(tpstack[0], "location", location), + doxyVarDesc=doxyVarDesc, ) if len(list(param.keys())): params.append(param) @@ -977,7 +965,12 @@ def __init__(self, nameStack, curClass, methinfo, curTemplate, doxygen): self["vararg"] = True paramsStack = paramsStack[1:] else: - param = CppVariable(paramsStack, None, doxyVarDesc=doxyVarDesc) + param = CppVariable( + paramsStack, + None, + getattr(paramsStack[0], "location", location), + doxyVarDesc=doxyVarDesc, + ) if len(list(param.keys())): params.append(param) break @@ -1045,7 +1038,7 @@ class CppVariable(_CppVariable): Vars = [] - def __init__(self, nameStack, doxygen, **kwargs): + def __init__(self, nameStack, doxygen, location, **kwargs): debug_print("trace %s" % nameStack) if len(nameStack) and nameStack[0] == "extern": self["extern"] = True @@ -1083,7 +1076,7 @@ def __init__(self, nameStack, doxygen, **kwargs): debug_print("Variable: %s" % nameStack) - self["line_number"] = detect_lineno(nameStack[0]) + set_location_info(self, location) self["function_pointer"] = 0 if len(nameStack) < 2: # +++ @@ -1225,7 +1218,7 @@ class CppEnum(_CppEnum): if a value for a given enum value was defined """ - def __init__(self, nameStack, doxygen): + def __init__(self, nameStack, doxygen, location): if doxygen: self["doxygen"] = doxygen if len(nameStack) == 3 and nameStack[0] == "enum": @@ -1237,7 +1230,7 @@ def __init__(self, nameStack, doxygen): debug_print("Bad enum") return valueList = [] - self["line_number"] = detect_lineno(nameStack[0]) + set_location_info(self, location) # Figure out what values it has valueStack = nameStack[nameStack.index("{") + 1 : nameStack.index("}")] while len(valueStack): @@ -1303,15 +1296,14 @@ class CppStruct(dict): Structs = [] - def __init__(self, nameStack): + def __init__(self, nameStack, location): if len(nameStack) >= 2: self["type"] = nameStack[1] else: self["type"] = None self["fields"] = [] + set_location_info(self, location) self.Structs.append(self) - global curLine - self["line_number"] = curLine C99_NONSTANDARD = { @@ -1998,7 +1990,7 @@ def _evaluate_struct_stack(self): """Create a Struct out of the name stack (but not its parts)""" # print( 'eval struct stack', self.nameStack ) # if self.braceDepth != len(self.nameSpaces): return - struct = CppStruct(self.nameStack) + struct = CppStruct(self.nameStack, self._get_location(self.nameStack)) struct["namespace"] = self.cur_namespace() self.structs[struct["type"]] = struct self.structs_order.append(struct) @@ -2176,6 +2168,7 @@ def _evaluate_method_stack(self): info, self.curTemplate, self.lex.get_doxygen(), + self._get_location(self.nameStack), ) klass = self.classes[info["class"]] klass["methods"]["public"].append(newMethod) @@ -2192,6 +2185,7 @@ def _evaluate_method_stack(self): info, self.curTemplate, self.lex.get_doxygen(), + self._get_location(self.nameStack), ) klass = self.classes[self.curClass] klass["methods"][self.curAccessSpecifier].append(newMethod) @@ -2203,7 +2197,12 @@ def _evaluate_method_stack(self): else: # non class functions debug_print("FREE FUNCTION") newMethod = CppMethod( - self.nameStack, None, info, self.curTemplate, self.lex.get_doxygen() + self.nameStack, + None, + info, + self.curTemplate, + self.lex.get_doxygen(), + self._get_location(self.nameStack), ) self.functions.append(newMethod) global parseHistory @@ -2326,7 +2325,11 @@ def _evaluate_property_stack(self, clearStack=True, addToVar=None): ) return - newVar = CppVariable(self.nameStack, self.lex.get_doxygen()) + newVar = CppVariable( + self.nameStack, + self.lex.get_doxygen(), + self._get_location(self.nameStack), + ) newVar["namespace"] = self.current_namespace() if self.curStruct: self.curStruct["fields"].append(newVar) @@ -2342,7 +2345,11 @@ def _evaluate_property_stack(self, clearStack=True, addToVar=None): newVar.update(addToVar) else: debug_print("Found Global variable") - newVar = CppVariable(self.nameStack, self.lex.get_doxygen()) + newVar = CppVariable( + self.nameStack, + self.lex.get_doxygen(), + self._get_location(self.nameStack), + ) if addToVar: newVar.update(addToVar) self.variables.append(newVar) @@ -2378,7 +2385,11 @@ def _evaluate_class_stack(self): "curAccessSpecifier changed/defaulted to %s" % self.curAccessSpecifier ) if self.nameStack[0] == "union": - newClass = CppUnion(self.nameStack, self.lex.get_doxygen()) + newClass = CppUnion( + self.nameStack, + self.lex.get_doxygen(), + self._get_location(self.nameStack), + ) if newClass["name"] == "union ": self.anon_union_counter = [self.braceDepth, 2] else: @@ -2386,7 +2397,10 @@ def _evaluate_class_stack(self): trace_print("NEW UNION", newClass["name"]) else: newClass = CppClass( - self.nameStack, self.curTemplate, self.lex.get_doxygen() + self.nameStack, + self.curTemplate, + self.lex.get_doxygen(), + self._get_location(self.nameStack), ) trace_print("NEW CLASS", newClass["name"]) newClass["declaration_method"] = self.nameStack[0] @@ -2682,13 +2696,11 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self.braceDepth = 0 - lex = Lexer() + lex = Lexer(self.headerFileName) lex.input(headerFileStr) self.lex = lex - global curLine - curLine = 0 - + tok = None try: while True: tok = lex.token() @@ -2699,13 +2711,13 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): and self.anon_union_counter[1] ): self.anon_union_counter[1] -= 1 - tok.value = TagStr(tok.value, lineno=tok.lineno) + tok.value = TagStr(tok.value, location=lex.current_location()) # debug_print("TOK: %s"%tok) if tok.type == "NAME" and tok.value in self.IGNORE_NAMES: continue if tok.type != "TEMPLATE_NAME": self.stack.append(tok.value) - curLine = tok.lineno + if tok.type in ("PRECOMP_MACRO", "PRECOMP_MACRO_CONT"): debug_print("PRECOMP: %s" % tok) self._precomp_macro_buf.append(tok.value) @@ -2891,17 +2903,20 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): except Exception as e: if debug: raise - raise_exc( - CppParseError( + if tok: + filename, lineno = tok.value.location + msg = ( 'Not able to parse %s on line %d evaluating "%s"\nError around: %s' - % ( - self.headerFileName, - tok.lineno, - tok.value, - " ".join(self.nameStack), - ) - ), - e, + % (filename, lineno, tok.value, " ".join(self.nameStack)) + ) + else: + msg = "Error parsing %s\nError around: %s" % ( + self.headerFileName, + " ".join(self.nameStack), + ) + + raise_exc( + CppParseError(msg), e, ) self.finalize() @@ -2934,6 +2949,14 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): ]: del self.__dict__[key] + def _get_location(self, stack): + if stack: + location = getattr(stack[0], "location", None) + if location is not None: + return location + + return self.lex.current_location() + def _evaluate_stack(self, token=None): """Evaluates the current name stack""" @@ -3004,11 +3027,15 @@ def _evaluate_stack(self, token=None): # using foo = ns::bar alias = self.nameStack[1] ns, stack = _split_namespace(self.nameStack[3:]) - atype = CppVariable(stack, self.lex.get_doxygen()) + atype = CppVariable( + stack, self.lex.get_doxygen(), self._get_location(stack) + ) else: # using foo::bar ns, stack = _split_namespace(self.nameStack[1:]) - atype = CppVariable(stack, self.lex.get_doxygen()) + atype = CppVariable( + stack, self.lex.get_doxygen(), self._get_location(stack) + ) alias = atype["type"] atype["namespace"] = ns @@ -3110,7 +3137,9 @@ def _evaluate_stack(self, token=None): def _evaluate_enum_stack(self): """Create an Enum out of the name stack""" debug_print("evaluating enum") - newEnum = CppEnum(self.nameStack, self.lex.get_doxygen()) + newEnum = CppEnum( + self.nameStack, self.lex.get_doxygen(), self._get_location(self.nameStack) + ) if len(list(newEnum.keys())): if len(self.curClass): newEnum["namespace"] = self.cur_namespace(False) diff --git a/CppHeaderParser/lexer.py b/CppHeaderParser/lexer.py index e36ecab..5c7fc1e 100644 --- a/CppHeaderParser/lexer.py +++ b/CppHeaderParser/lexer.py @@ -1,6 +1,8 @@ import ply.lex as lex import re +_line_re = re.compile(r'^#line (\d+) "(.*)"') + class Lexer(object): @@ -62,7 +64,16 @@ class Lexer(object): t_PERCENT = r"%" t_CARET = r"\^" t_EXCLAMATION = r"!" - t_PRECOMP_MACRO = r"\#.*" + + def t_PRECOMP_MACRO(self, t): + r"\#.*" + m = _line_re.match(t.value) + if m: + self.filename = m.group(2) + self.line_offset = 1 + self.lex.lineno - int(m.group(1)) + else: + return t + t_PRECOMP_MACRO_CONT = r".*\\\n" def t_COMMENT_SINGLELINE(self, t): @@ -109,14 +120,21 @@ def t_NEWLINE(self, t): def t_error(self, v): print("Lex error: ", v) - def __init__(self): + def __init__(self, filename): self.lex = lex.lex(module=self) self.input = self.lex.input self.token = self.lex.token + # For tracking current file/line position + self.filename = filename + self.line_offset = 0 + # Doxygen comments self.doxygenCommentCache = "" + def current_location(self): + return self.filename, self.lex.lineno - self.line_offset + def get_doxygen(self): doxygen = self.doxygenCommentCache self.doxygenCommentCache = "" diff --git a/CppHeaderParser/test/test_CppHeaderParser.py b/CppHeaderParser/test/test_CppHeaderParser.py index a9eaea4..9ab0bbf 100644 --- a/CppHeaderParser/test/test_CppHeaderParser.py +++ b/CppHeaderParser/test/test_CppHeaderParser.py @@ -2907,5 +2907,53 @@ def test_fn(self): ) +class MultiFile_TestCase(unittest.TestCase): + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader( + """ +#line 3 "child.h" +#include +#line 3 "base.h" +void functionInBase(void); + +class Base +{ +public: + virtual void baseFunction(); +}; +#line 7 "child.h" +void functionInChild(void); + +class Child : public Base +{ +public: + void childOnlyFunction(); + void baseFunction() override; +}; + +""", + "string", + ) + + def assertLocation(self, thing, fname, lineno): + self.assertEqual(fname, thing["filename"]) + self.assertEqual(lineno, thing["line_number"]) + + def test_fn(self): + baseFn = self.cppHeader.functions[0] + self.assertEqual("functionInBase", baseFn["name"]) + self.assertLocation(baseFn, "base.h", 3) + + base = self.cppHeader.classes["Base"] + self.assertLocation(base, "base.h", 5) + + childFn = self.cppHeader.functions[1] + self.assertEqual("functionInChild", childFn["name"]) + self.assertLocation(childFn, "child.h", 7) + + child = self.cppHeader.classes["Child"] + self.assertLocation(child, "child.h", 9) + + if __name__ == "__main__": unittest.main() From 4ff8ab258b134725a3469926d264252a6c977dfc Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sat, 16 Nov 2019 13:51:47 -0500 Subject: [PATCH 039/143] Add list of filenames encountered when processing #line directives --- CppHeaderParser/CppHeaderParser.py | 5 +++++ CppHeaderParser/lexer.py | 15 ++++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index ee2d40a..2c16043 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -2542,6 +2542,10 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): #: List of #include directives found self.includes = [] + + #: Filenames encountered in #line directives while parsing + self.headerFileNames = [] + self._precomp_macro_buf = ( [] ) # for internal purposes, will end up filling out pragmras and defines at the end @@ -2699,6 +2703,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): lex = Lexer(self.headerFileName) lex.input(headerFileStr) self.lex = lex + self.headerFileNames = lex.filenames tok = None try: diff --git a/CppHeaderParser/lexer.py b/CppHeaderParser/lexer.py index 5c7fc1e..d3c5c40 100644 --- a/CppHeaderParser/lexer.py +++ b/CppHeaderParser/lexer.py @@ -69,8 +69,14 @@ def t_PRECOMP_MACRO(self, t): r"\#.*" m = _line_re.match(t.value) if m: - self.filename = m.group(2) + filename = m.group(2) + if filename not in self._filenames_set: + self.filenames.append(filename) + self._filenames_set.add(filename) + self.filename = filename + self.line_offset = 1 + self.lex.lineno - int(m.group(1)) + else: return t @@ -129,6 +135,13 @@ def __init__(self, filename): self.filename = filename self.line_offset = 0 + self.filenames = [] + self._filenames_set = set() + + if self.filename: + self.filenames.append(filename) + self._filenames_set.add(filename) + # Doxygen comments self.doxygenCommentCache = "" From 0881438c0f0ae5db4d352c4eeef4bfe038795fdf Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Tue, 19 Nov 2019 00:32:21 -0500 Subject: [PATCH 040/143] Rewrite class declaration/base parser - Fixes #27 --- CppHeaderParser/CppHeaderParser.py | 420 ++++++++++++------- CppHeaderParser/test/test_CppHeaderParser.py | 212 +++++++++- docs/api.rst | 4 +- 3 files changed, 485 insertions(+), 151 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index ee2d40a..a3fd087 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -334,6 +334,262 @@ class CppParseError(Exception): pass +class CppTemplateParam(dict): + """ + Dictionary that contains the following: + + - ``decltype`` - If True, this is a decltype + - ``param`` - Parameter value or typename + - ``params`` - Only present if a template specialization. Is a list of + :class:`.CppTemplateParam` + - ``...`` - If True, indicates a parameter pack + """ + + def __init__(self): + self["param"] = "" + self["decltype"] = False + self["..."] = False + + def __str__(self): + s = [] + if self["decltype"]: + s.append("decltype") + + s.append(self["param"]) + + params = self.get("params") + if params is not None: + s.append("<%s>" % ",".join(str(p) for p in params)) + + if self["..."]: + if s: + s[-1] = s[-1] + "..." + else: + s.append("...") + + return "".join(s) + + +class CppBaseDecl(dict): + """ + Dictionary that contains the following + + - ``access`` - Anything in supportedAccessSpecifier + - ``class`` - Name of the type, along with template specializations + - ``decl_name`` - Name of the type only + - ``decl_params`` - Only present if a template specialization (Foo). + Is a list of :class:`.CppTemplateParam`. + - ``decltype`` - True/False indicates a decltype, the contents are in + ``decl_name`` + - ``virtual`` - True/False indicates virtual inheritance + - ``...`` - True/False indicates a parameter pack + + """ + + def __init__(self): + self["access"] = "private" + self["class"] = "" + self["decl_name"] = "" + self["decltype"] = False + self["virtual"] = False + self["..."] = False + + def _fix_classname(self): + # set class to the full decl for legacy reasons + params = self.get("decl_params") + + if self["decltype"]: + s = "decltype" + else: + s = "" + + s += self["decl_name"] + if params: + s += "<%s>" % (",".join(str(p) for p in params)) + + if self["..."]: + s += "..." + + self["class"] = s + + +def _consume_parens(stack): + i = 0 + sl = len(stack) + nested = 1 + while i < sl: + t = stack[i] + i += 1 + if t == ")": + nested -= 1 + if nested == 0: + return i + elif t == "(": + nested += 1 + + raise CppParseError("Unmatched (") + + +def _parse_template_decl(stack): + debug_print("_parse_template_decl: %s" % stack) + params = [] + param = CppTemplateParam() + i = 0 + sl = len(stack) + init = True + require_ending = False + while i < sl: + t = stack[i] + i += 1 + if init: + init = False + if t == "decltype": + param["decltype"] = True + continue + + if t == ",": + params.append(param) + init = True + require_ending = False + param = CppTemplateParam() + + continue + elif t == ">": + params.append(param) + return params, i - 1 + + if require_ending: + raise CppParseError("expected comma, found %s" % t) + + if t == "...": + param["..."] = True + require_ending = True + elif t == "(": + s = stack[i:] + n = _consume_parens(s) + i += n + param["param"] = param["param"] + "".join(s[:n]) + else: + if t and t[0] == "<": + param["params"], n = _parse_template_decl([t[1:]] + stack[i:]) + i += n + else: + param["param"] = param["param"] + t + + raise CppParseError("Unmatched <") + + +def _parse_cppclass_name(c, stack): + # ignore first thing + i = 1 + sl = len(stack) + name = "" + require_ending = False + while i < sl: + t = stack[i] + i += 1 + + if t == ":": + if i >= sl: + raise CppParseError("class decl ended with ':'") + t = stack[i] + if t != ":": + # reached the base declaration + break + + i += 1 + name += "::" + continue + elif t == "final": + c["final"] = True + continue + + if require_ending: + raise CppParseError("Unexpected '%s' in class" % ("".join(stack[i - 1 :]))) + + if t and t[0] == "<": + c["class_params"], n = _parse_template_decl([t[1:]] + stack[i:]) + i += n + require_ending = True + else: + name += t + + c["namespace"] = "" + + # backwards compat + if name.startswith("anon-struct-"): + name = "<" + name + ">" + c["name"] = name + c["bare_name"] = name + + # backwards compat + classParams = c.get("class_params") + if classParams is not None: + c["name"] = c["name"] + "<%s>" % ",".join(str(p) for p in classParams) + + return i + + +def _parse_cpp_base(stack): + debug_print("Parsing base: %s" % stack) + inherits = [] + i = 0 + sl = len(stack) + init = True + base = CppBaseDecl() + require_ending = False + while i < sl: + t = stack[i] + i += 1 + + if init: + if t in supportedAccessSpecifier: + base["access"] = t + continue + elif t == "virtual": + base["virtual"] = True + continue + + init = False + + if t == "decltype": + base["decltype"] = True + continue + + if t == ",": + inherits.append(base) + base = CppBaseDecl() + init = True + require_ending = False + continue + + if require_ending: + raise CppParseError("expected comma, found '%s'" % t) + + if t == "(": + s = stack[i:] + n = _consume_parens(s) + i += n + base["decl_name"] = base["decl_name"] + "".join(s[:n]) + elif t == "...": + base["..."] = True + require_ending = True + else: + if t[0] == "<": + base["decl_params"], n = _parse_template_decl([t[1:]] + stack[i:]) + i += n + require_ending = True + else: + base["decl_name"] = base["decl_name"] + t + + # backwards compat + inherits.append(base) + for base in inherits: + base._fix_classname() + + return inherits + + class CppClass(dict): """ Dictionary that contains at least the following keys: @@ -341,11 +597,7 @@ class CppClass(dict): * ``name`` - Name of the class * ``doxygen`` - Doxygen comments associated with the class if they exist * ``inherits`` - List of Classes that this one inherits. Values are - dictionaries with the following key/values: - - - ``access`` - Anything in supportedAccessSpecifier - - ``class`` - Name of the class - + :class:`.CppBaseDecl` * ``methods`` - Dictionary where keys are from supportedAccessSpecifier and values are a lists of :class:`.CppMethod` * ``namespace`` - Namespace of class @@ -413,10 +665,10 @@ def get_pure_virtual_methods(self, type="public"): return r def __init__(self, nameStack, curTemplate, doxygen, location): - #: hm self["nested_classes"] = [] self["parent"] = None self["abstract"] = False + self["final"] = False self._public_enums = {} self._public_structs = {} self._public_typedefs = {} @@ -431,145 +683,18 @@ def __init__(self, nameStack, curTemplate, doxygen, location): if doxygen: self["doxygen"] = doxygen - if "::" in "".join(nameStack): - # Re-Join class paths (ex ['class', 'Bar', ':', ':', 'Foo'] -> ['class', 'Bar::Foo'] - try: - new_nameStack = [] - for name in nameStack: - if len(new_nameStack) == 0: - new_nameStack.append(name) - elif name == ":" and new_nameStack[-1].endswith(":"): - new_nameStack[-1] += name - elif new_nameStack[-1].endswith("::"): - new_nameStack[-2] += new_nameStack[-1] + name - del new_nameStack[-1] - else: - new_nameStack.append(name) - trace_print( - "Convert from namestack\n %s\nto\n%s" % (nameStack, new_nameStack) - ) - nameStack = new_nameStack - except: - pass + # consume name of class, with any namespaces or templates + n = _parse_cppclass_name(self, nameStack) - # Handle final specifier - self["final"] = False - try: - final_index = nameStack.index("final") - # Dont trip up the rest of the logic - del nameStack[final_index] - self["final"] = True - trace_print("final") - except: - pass + # consume bases + baseStack = nameStack[n:] + if baseStack: + self["inherits"] = _parse_cpp_base(baseStack) + else: + self["inherits"] = [] - self["name"] = nameStack[1] set_location_info(self, location) - # Handle template classes - if len(nameStack) > 3 and nameStack[2].startswith("<"): - open_template_count = 0 - param_separator = 0 - found_first = False - i = 0 - for elm in nameStack: - if "<" in elm: - open_template_count += 1 - found_first = True - elif ">" in elm: - open_template_count -= 1 - if found_first and open_template_count == 0: - self["name"] = "".join(nameStack[1 : i + 1]) - break - i += 1 - elif ":" in nameStack: - self["name"] = nameStack[nameStack.index(":") - 1] - - inheritList = [] - - if nameStack.count(":") == 1: - nameStack = nameStack[nameStack.index(":") + 1 :] - while len(nameStack): - tmpStack = [] - tmpInheritClass = {"access": "private", "virtual": False} - if "," in nameStack: - tmpStack = nameStack[: nameStack.index(",")] - nameStack = nameStack[nameStack.index(",") + 1 :] - else: - tmpStack = nameStack - nameStack = [] - - # Convert template classes to one name in the last index - for i in range(0, len(tmpStack)): - if "<" in tmpStack[i]: - tmpStack2 = tmpStack[: i - 1] - tmpStack2.append("".join(tmpStack[i - 1 :])) - tmpStack = tmpStack2 - break - if len(tmpStack) == 0: - break - elif len(tmpStack) == 1: - tmpInheritClass["class"] = tmpStack[0] - elif len(tmpStack) == 2: - tmpInheritClass["access"] = tmpStack[0] - tmpInheritClass["class"] = tmpStack[1] - elif len(tmpStack) == 3 and "virtual" in tmpStack: - tmpInheritClass["access"] = ( - tmpStack[1] if tmpStack[1] != "virtual" else tmpStack[0] - ) - tmpInheritClass["class"] = tmpStack[2] - tmpInheritClass["virtual"] = True - else: - warning_print( - "Warning: can not parse inheriting class %s" - % (" ".join(tmpStack)) - ) - if ">" in tmpStack: - pass # allow skip templates for now - else: - raise NotImplementedError - - if "class" in tmpInheritClass: - inheritList.append(tmpInheritClass) - - elif nameStack.count(":") == 2: - self["parent"] = self["name"] - self["name"] = nameStack[-1] - - elif nameStack.count(":") > 2 and nameStack[0] in ("class", "struct"): - tmpStack = nameStack[nameStack.index(":") + 1 :] - - superTmpStack = [[]] - for tok in tmpStack: - if tok == ",": - superTmpStack.append([]) - else: - superTmpStack[-1].append(tok) - - for tmpStack in superTmpStack: - tmpInheritClass = {"access": "private"} - - if len(tmpStack) and tmpStack[0] in supportedAccessSpecifier: - tmpInheritClass["access"] = tmpStack[0] - tmpStack = tmpStack[1:] - - inheritNSStack = [] - while len(tmpStack) > 3: - if tmpStack[0] == ":": - break - if tmpStack[1] != ":": - break - if tmpStack[2] != ":": - break - inheritNSStack.append(tmpStack[0]) - tmpStack = tmpStack[3:] - if len(tmpStack) == 1 and tmpStack[0] != ":": - inheritNSStack.append(tmpStack[0]) - tmpInheritClass["class"] = "::".join(inheritNSStack) - inheritList.append(tmpInheritClass) - - self["inherits"] = inheritList - if curTemplate: self["template"] = curTemplate trace_print("Setting template to '%s'" % self["template"]) @@ -2375,7 +2500,7 @@ def _evaluate_class_stack(self): if len(self.nameStack) == 1: self.anon_struct_counter += 1 # We cant handle more than 1 anonymous struct, so name them uniquely - self.nameStack.append("" % self.anon_struct_counter) + self.nameStack.append("anon-struct-%d" % self.anon_struct_counter) if self.nameStack[0] == "class": self.curAccessSpecifier = "private" @@ -2406,12 +2531,14 @@ def _evaluate_class_stack(self): newClass["declaration_method"] = self.nameStack[0] self.classes_order.append(newClass) # good idea to save ordering self.stack = [] # fixes if class declared with ';' in closing brace + classKey = newClass["name"] + if parent: newClass["namespace"] = self.classes[parent]["namespace"] + "::" + parent newClass["parent"] = parent self.classes[parent]["nested_classes"].append(newClass) ## supports nested classes with the same name ## - self.curClass = key = parent + "::" + newClass["name"] + self.curClass = key = parent + "::" + classKey self._classes_brace_level[key] = self.braceDepth elif newClass["parent"]: # nested class defined outside of parent. A::B {...} @@ -2419,14 +2546,13 @@ def _evaluate_class_stack(self): newClass["namespace"] = self.classes[parent]["namespace"] + "::" + parent self.classes[parent]["nested_classes"].append(newClass) ## supports nested classes with the same name ## - self.curClass = key = parent + "::" + newClass["name"] + self.curClass = key = parent + "::" + classKey self._classes_brace_level[key] = self.braceDepth else: newClass["namespace"] = self.cur_namespace() - key = newClass["name"] - self.curClass = newClass["name"] - self._classes_brace_level[newClass["name"]] = self.braceDepth + self.curClass = key = classKey + self._classes_brace_level[classKey] = self.braceDepth if not key.endswith("::") and not key.endswith(" ") and len(key) != 0: if key in self.classes: diff --git a/CppHeaderParser/test/test_CppHeaderParser.py b/CppHeaderParser/test/test_CppHeaderParser.py index 9ab0bbf..b316555 100644 --- a/CppHeaderParser/test/test_CppHeaderParser.py +++ b/CppHeaderParser/test/test_CppHeaderParser.py @@ -573,18 +573,37 @@ def test_BloodOrange_inherits(self): def test_Bananna_inherits(self): self.assertEqual( self.cppHeader.classes["Bananna"]["inherits"], - [{"access": "public", "class": "Citrus::BloodOrange", "virtual": False}], + [ + { + "access": "public", + "class": "Citrus::BloodOrange", + "decl_name": "Citrus::BloodOrange", + "decltype": False, + "virtual": False, + "...": False, + } + ], ) def test_ExcellentCake_inherits(self): self.assertEqual( self.cppHeader.classes["ExcellentCake"]["inherits"], [ - {"access": "private", "class": "Citrus::BloodOrange", "virtual": False}, + { + "access": "private", + "class": "Citrus::BloodOrange", + "decl_name": "Citrus::BloodOrange", + "decltype": False, + "virtual": False, + "...": False, + }, { "access": "private", "class": "Convoluted::Nested::Mixin", + "decl_name": "Convoluted::Nested::Mixin", + "decltype": False, "virtual": False, + "...": False, }, ], ) @@ -2955,5 +2974,194 @@ def test_fn(self): self.assertLocation(child, "child.h", 9) +class TemplateMadness_TestCase(unittest.TestCase): + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader( + """ +template +class XYZ : public MyBaseClass +{ + public: + XYZ(); +}; + +template +class concat_iterator + : public iterator_facade_base, + std::forward_iterator_tag, ValueT> { +}; + +template +struct build_index_impl : build_index_impl {}; + +template +struct build_index_impl<0, I...> : index_sequence {}; + +//template +//struct is_callable, +// void_t()).*std::declval())(std::declval()...))>> +// : std::true_type {}; + +template +struct S : public T... {}; + +""", + "string", + ) + + def testXYZ(self): + c = self.cppHeader.classes["XYZ"] + self.assertEqual("XYZ", c["name"]) + self.assertEqual( + [ + { + "access": "public", + "class": "MyBaseClass", + "decltype": False, + "decl_name": "MyBaseClass", + "decl_params": [ + {"param": "Type", "...": False, "decltype": False}, + {"param": "int", "...": False, "decltype": False}, + ], + "virtual": False, + "...": False, + } + ], + c["inherits"], + ) + + def testConcatIterator(self): + c = self.cppHeader.classes["concat_iterator"] + self.assertEqual("concat_iterator", c["name"]) + self.assertEqual( + [ + { + "access": "public", + "class": "iterator_facade_base,std::forward_iterator_tag,ValueT>", + "decltype": False, + "decl_name": "iterator_facade_base", + "decl_params": [ + { + "decltype": False, + "param": "concat_iterator", + "params": [ + {"param": "ValueT", "...": False, "decltype": False}, + {"param": "IterTs", "...": True, "decltype": False}, + ], + "...": False, + }, + { + "decltype": False, + "param": "std::forward_iterator_tag", + "...": False, + }, + {"decltype": False, "param": "ValueT", "...": False}, + ], + "virtual": False, + "...": False, + } + ], + c["inherits"], + ) + + def testBuildIndexImpl1(self): + c = self.cppHeader.classes["build_index_impl"] + self.assertEqual("build_index_impl", c["name"]) + self.assertEqual( + [ + { + "access": "private", + "class": "build_index_impl", + "decltype": False, + "decl_name": "build_index_impl", + "decl_params": [ + {"param": "N-1", "...": False, "decltype": False}, + {"param": "N-1", "...": False, "decltype": False}, + {"param": "I", "...": True, "decltype": False}, + ], + "virtual": False, + "...": False, + } + ], + c["inherits"], + ) + + def testBuildIndexImpl2(self): + c = self.cppHeader.classes["build_index_impl<0,I...>"] + self.assertEqual("build_index_impl", c["bare_name"]) + self.assertEqual("build_index_impl<0,I...>", c["name"]) + self.assertEqual( + [ + {"decltype": False, "param": "0", "...": False}, + {"decltype": False, "param": "I", "...": True}, + ], + c["class_params"], + ) + self.assertEqual( + [ + { + "access": "private", + "class": "index_sequence", + "decltype": False, + "decl_name": "index_sequence", + "decl_params": [{"decltype": False, "param": "I", "...": True}], + "virtual": False, + "...": False, + } + ], + c["inherits"], + ) + + # def testIsCallable(self): + # c = self.cppHeader.classes["is_callable"] + # self.assertEqual("is_callable", c["name"]) + # self.assertEqual( + # [ + # {"param": "F", "...": False, "decltype": False}, + # {"param": "P", "...": False, "decltype": False}, + # { + # "param": "typelist", + # "...": False, + # "decltype": False, + # "params": [{"param": "T", "...": True, "decltype": False},], + # }, + # { + # "param": "void_t", + # "...": False, + # "decltype": False, + # "params": [ + # { + # "param": "(((*std::declval

()).*std::declval())(std::declval()...))", + # "...": False, + # "decltype": True, + # }, + # ], + # }, + # ], + # c["class_params"], + # ) + # self.assertEqual( + # [{"access": "private", "class": "std::true_type", "virtual": False,}], + # c["inherits"], + # ) + + def testS(self): + c = self.cppHeader.classes["S"] + self.assertEqual("S", c["name"]) + self.assertEqual( + [ + { + "access": "public", + "class": "T...", + "decl_name": "T", + "virtual": False, + "...": True, + "decltype": False, + } + ], + c["inherits"], + ) + + if __name__ == "__main__": unittest.main() diff --git a/docs/api.rst b/docs/api.rst index c689857..50a10b8 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -24,8 +24,8 @@ CppHeaderParser --------------- .. automodule:: CppHeaderParser.CppHeaderParser - :members: CppClass, CppEnum, CppHeader, CppMethod, CppParseError, - CppStruct, CppUnion, CppVariable, TagStr, + :members: CppBaseDecl, CppClass, CppEnum, CppHeader, CppMethod, CppParseError, + CppStruct, CppTemplateParam, CppUnion, CppVariable, TagStr, ignoreSymbols :undoc-members: :show-inheritance: From 3518b32c4544250c65114c2a99e89f89234efc2d Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sat, 21 Dec 2019 22:14:01 -0500 Subject: [PATCH 041/143] lexer: convert single character regex to literals --- CppHeaderParser/CppHeaderParser.py | 43 +++++++++-------- CppHeaderParser/lexer.py | 74 +++++++++++------------------- 2 files changed, 49 insertions(+), 68 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index ee0082b..b041af1 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -2582,36 +2582,35 @@ def evalute_forward_decl(self): # fmt: off _namestack_append_tokens = set([ - "OPEN_PAREN", - "CLOSE_PAREN", - "OPEN_SQUARE_BRACKET", - "CLOSE_SQUARE_BRACKET", - "EQUALS", - "COMMA", - "BACKSLASH", + "(", + ")", + "[", + "]", + "=", + ",", + "\\", "DIVIDE", - "PIPE", - "PERCENT", - "CARET", - "EXCLAMATION", + "|", + "%", + "^", + "!", "NUMBER", "FLOAT_NUMBER", - "MINUS", - "PLUS", + "-", + "+", "STRING_LITERAL", "ELLIPSIS", ]) _namestack_pass_tokens = set([ - "TAB", - "SQUOTE", - "DOT" # preserve behaviour and eat individual fullstops + "'", + "." # preserve behaviour and eat individual fullstops ]) _namestack_str_tokens = set([ "NAME", - "AMPERSAND", - "ASTERISK", + "&", + "*", "CHAR_LITERAL" ]) # fmt: on @@ -2863,7 +2862,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self.curTemplate = self.templateRegistry[templateId] except: pass - if tok.type == "OPEN_BRACE": + if tok.type == "{": if len(self.nameStack) >= 2 and is_namespace( self.nameStack ): # namespace {} with no name used in boost, this sets default? @@ -2907,7 +2906,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self.stack = [] self.braceDepth += 1 - elif tok.type == "CLOSE_BRACE": + elif tok.type == "}": if self.braceDepth == 0: continue if self.braceDepth == len(self.nameSpaces): @@ -2988,7 +2987,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self.nameStack.append(tok.value) if self.anon_union_counter[0] == self.braceDepth: self.anon_union_counter = [-1, 0] - elif tok.type == "COLON": + elif tok.type == ":": # Dont want colon to be first in stack if len(self.nameStack) == 0: self.accessSpecifierScratch = [] @@ -3007,7 +3006,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self.nameStack.append(tok.value) self.accessSpecifierScratch = [] - elif tok.type == "SEMI_COLON": + elif tok.type == ";": if ( self.anon_union_counter[0] == self.braceDepth and self.anon_union_counter[1] diff --git a/CppHeaderParser/lexer.py b/CppHeaderParser/lexer.py index d3c5c40..d7d793a 100644 --- a/CppHeaderParser/lexer.py +++ b/CppHeaderParser/lexer.py @@ -11,59 +11,48 @@ class Lexer(object): "FLOAT_NUMBER", "TEMPLATE_NAME", "NAME", - "OPEN_PAREN", - "CLOSE_PAREN", - "OPEN_BRACE", - "CLOSE_BRACE", - "OPEN_SQUARE_BRACKET", - "CLOSE_SQUARE_BRACKET", - "COLON", - "SEMI_COLON", - "COMMA", - "TAB", - "BACKSLASH", - "PIPE", - "PERCENT", - "EXCLAMATION", - "CARET", "COMMENT_SINGLELINE", "COMMENT_MULTILINE", "PRECOMP_MACRO", "PRECOMP_MACRO_CONT", - "ASTERISK", - "AMPERSAND", - "EQUALS", - "MINUS", - "PLUS", "DIVIDE", "CHAR_LITERAL", "STRING_LITERAL", "NEW_LINE", - "SQUOTE", "ELLIPSIS", - "DOT", ] - t_ignore = " \r?@\f" + literals = [ + "<", + ">", + "(", + ")", + "{", + "}", + "[", + "]", + ";", + ":", + ",", + "\\", + "|", + "%", + "^", + "!", + "*", + "-", + "+", + "&", + "=", + "'", + ".", + ] + + t_ignore = " \t\r?@\f" t_NUMBER = r"[0-9][0-9XxA-Fa-f]*" t_FLOAT_NUMBER = r"[-+]?[0-9]*\.[0-9]+([eE][-+]?[0-9]+)?" t_TEMPLATE_NAME = r"CppHeaderParser_template_[0-9]+" t_NAME = r"[<>A-Za-z_~][A-Za-z0-9_]*" - t_OPEN_PAREN = r"\(" - t_CLOSE_PAREN = r"\)" - t_OPEN_BRACE = r"{" - t_CLOSE_BRACE = r"}" - t_OPEN_SQUARE_BRACKET = r"\[" - t_CLOSE_SQUARE_BRACKET = r"\]" - t_SEMI_COLON = r";" - t_COLON = r":" - t_COMMA = r"," - t_TAB = r"\t" - t_BACKSLASH = r"\\" - t_PIPE = r"\|" - t_PERCENT = r"%" - t_CARET = r"\^" - t_EXCLAMATION = r"!" def t_PRECOMP_MACRO(self, t): r"\#.*" @@ -93,16 +82,9 @@ def t_COMMENT_SINGLELINE(self, t): self.doxygenCommentCache += t.value t.lexer.lineno += t.value.count("\n") - t_ASTERISK = r"\*" - t_MINUS = r"\-" - t_PLUS = r"\+" t_DIVIDE = r"/(?!/)" - t_AMPERSAND = r"&" - t_EQUALS = r"=" t_CHAR_LITERAL = "'.'" - t_SQUOTE = "'" t_ELLIPSIS = r"\.\.\." - t_DOT = r"\." # found at http://wordaligned.org/articles/string-literals-and-regular-expressions # TODO: This does not work with the string "bla \" bla" @@ -155,4 +137,4 @@ def get_doxygen(self): if __name__ == "__main__": - lex.runmain(lexer=Lexer()) + lex.runmain(lexer=Lexer(None)) From 28db98c77064ab1872d412d6300511cbcfac99f9 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sat, 21 Dec 2019 23:10:34 -0500 Subject: [PATCH 042/143] Move linenum test to its own file --- CppHeaderParser/test/TestSampleClass.h | 9 -------- CppHeaderParser/test/test_CppHeaderParser.py | 22 +++++++++++++------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/CppHeaderParser/test/TestSampleClass.h b/CppHeaderParser/test/TestSampleClass.h index 4028d95..c607cd4 100644 --- a/CppHeaderParser/test/TestSampleClass.h +++ b/CppHeaderParser/test/TestSampleClass.h @@ -574,15 +574,6 @@ class AfterTypedefClass public: } -// Bug BitBucket #6 -class LineNumAfterDivide -{ - static int func1(float alpha_num) - { return funcX(alpha_num / - beta_num); } - void func2(); -}; - // Bug BitBucket #5 class Herb { diff --git a/CppHeaderParser/test/test_CppHeaderParser.py b/CppHeaderParser/test/test_CppHeaderParser.py index b316555..4394a6f 100644 --- a/CppHeaderParser/test/test_CppHeaderParser.py +++ b/CppHeaderParser/test/test_CppHeaderParser.py @@ -2030,15 +2030,23 @@ def test_AfterTypedefClass_exists(self): # Bug BitBucket #6 class LineNumAfterDivide_TestCase(unittest.TestCase): def setUp(self): - self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") + self.cppHeader = CppHeaderParser.CppHeader(""" + +// Bug BitBucket #6 +class LineNumAfterDivide +{ + static int func1(float alpha_num) + { return funcX(alpha_num / + beta_num); } + void func2(); +}; + +""", "string") def test_line_num(self): - self.assertEqual( - self.cppHeader.classes["LineNumAfterDivide"]["methods"]["private"][1][ - "line_number" - ], - 583, - ) + m = self.cppHeader.classes["LineNumAfterDivide"]["methods"]["private"][1] + self.assertEqual("func2", m["name"]) + self.assertEqual(9, m["line_number"]) # Bug BitBucket #5 From 0aadd06c6a73457dc9e1f002356212c051ac6232 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sat, 21 Dec 2019 23:40:44 -0500 Subject: [PATCH 043/143] Add [[ and ]] lexing tokens --- CppHeaderParser/lexer.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CppHeaderParser/lexer.py b/CppHeaderParser/lexer.py index d7d793a..25e0d03 100644 --- a/CppHeaderParser/lexer.py +++ b/CppHeaderParser/lexer.py @@ -20,6 +20,8 @@ class Lexer(object): "STRING_LITERAL", "NEW_LINE", "ELLIPSIS", + "DBL_LBRACKET", + "DBL_RBRACKET", ] literals = [ @@ -85,6 +87,8 @@ def t_COMMENT_SINGLELINE(self, t): t_DIVIDE = r"/(?!/)" t_CHAR_LITERAL = "'.'" t_ELLIPSIS = r"\.\.\." + t_DBL_LBRACKET = r"\[\[" + t_DBL_RBRACKET = r"\]\]" # found at http://wordaligned.org/articles/string-literals-and-regular-expressions # TODO: This does not work with the string "bla \" bla" From 71a1bba9d6ffb6855ad5829ab19b65036f477f61 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sat, 21 Dec 2019 23:41:12 -0500 Subject: [PATCH 044/143] Lexer improvements: add lookahead and better doxygen handling --- CppHeaderParser/CppHeaderParser.py | 2 +- CppHeaderParser/lexer.py | 105 ++++++++++++++++--- CppHeaderParser/test/TestSampleClass.h | 8 ++ CppHeaderParser/test/test_CppHeaderParser.py | 81 ++++++-------- 4 files changed, 132 insertions(+), 64 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index b041af1..93a872d 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -2833,7 +2833,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): tok = None try: while True: - tok = lex.token() + tok = lex.token(eof_ok=True) if not tok: break if ( diff --git a/CppHeaderParser/lexer.py b/CppHeaderParser/lexer.py index 25e0d03..3d741be 100644 --- a/CppHeaderParser/lexer.py +++ b/CppHeaderParser/lexer.py @@ -1,3 +1,4 @@ +from collections import deque import ply.lex as lex import re @@ -18,7 +19,7 @@ class Lexer(object): "DIVIDE", "CHAR_LITERAL", "STRING_LITERAL", - "NEW_LINE", + "NEWLINE", "ELLIPSIS", "DBL_LBRACKET", "DBL_RBRACKET", @@ -76,13 +77,9 @@ def t_PRECOMP_MACRO(self, t): def t_COMMENT_SINGLELINE(self, t): r"\/\/.*\n?" if t.value.startswith("///") or t.value.startswith("//!"): - if self.doxygenCommentCache: - self.doxygenCommentCache += "\n" - if t.value.endswith("\n"): - self.doxygenCommentCache += t.value[:-1] - else: - self.doxygenCommentCache += t.value + self.comments.append(t.value.lstrip("\t ").rstrip("\n")) t.lexer.lineno += t.value.count("\n") + return t t_DIVIDE = r"/(?!/)" t_CHAR_LITERAL = "'.'" @@ -96,18 +93,21 @@ def t_COMMENT_SINGLELINE(self, t): # Found at http://ostermiller.org/findcomment.html def t_COMMENT_MULTILINE(self, t): - r"/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/" + r"/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/\n?" if t.value.startswith("/**") or t.value.startswith("/*!"): # not sure why, but get double new lines v = t.value.replace("\n\n", "\n") # strip prefixing whitespace v = re.sub("\n[\\s]+\\*", "\n*", v) - self.doxygenCommentCache += v - t.lexer.lineno += len([a for a in t.value if a == "\n"]) + self.comments = v.splitlines() + t.lexer.lineno += t.value.count("\n") + return t def t_NEWLINE(self, t): r"\n+" t.lexer.lineno += len(t.value) + self.comments.clear() + return t def t_error(self, v): print("Lex error: ", v) @@ -115,7 +115,6 @@ def t_error(self, v): def __init__(self, filename): self.lex = lex.lex(module=self) self.input = self.lex.input - self.token = self.lex.token # For tracking current file/line position self.filename = filename @@ -129,15 +128,91 @@ def __init__(self, filename): self._filenames_set.add(filename) # Doxygen comments - self.doxygenCommentCache = "" + self.comments = [] + + self.lookahead = deque() def current_location(self): return self.filename, self.lex.lineno - self.line_offset def get_doxygen(self): - doxygen = self.doxygenCommentCache - self.doxygenCommentCache = "" - return doxygen + """ + This should be called after the first element of something has + been consumed. + + It will lookahead for comments that come after the item, if prior + comments don't exist. + """ + + # assuption: only time you call this function is after a name + # token is consumed along with all its pieces + + if self.comments: + comments = self.comments + else: + comments = [] + # only look for comments until a newline (including lookahead) + for tok in self.lookahead: + if tok.type == "NEWLINE": + return "" + + while True: + tok = self.lex.token() + comments.extend(self.comments) + + if tok is None: + break + ttype = tok.type + if ttype == "NEWLINE": + self.lookahead.append(tok) + break + + if ttype not in self._discard_types: + self.lookahead.append(tok) + + if ttype == "NAME": + break + + self.comments.clear() + + comments = "\n".join(comments) + self.comments.clear() + return comments + + _discard_types = set(["NEWLINE", "COMMENT_SINGLELINE", "COMMENT_MULTILINE"]) + + def token(self, eof_ok=False): + tok = None + while self.lookahead: + tok = self.lookahead.popleft() + if tok.type not in self._discard_types: + return tok + + while True: + tok = self.lex.token() + if tok is None: + if not eof_ok: + raise EOFError("unexpected end of file") + break + + if tok.type not in self._discard_types: + break + + return tok + + def token_if(self, *types): + tok = self.token(eof_ok=True) + if tok is None: + return None + if tok.type not in types: + # put it back on the left in case it was retrieved + # from the lookahead buffer + self.lookahead.appendleft(tok) + return None + return tok + + def return_token(self, tok): + self.lookahead.appendleft(tok) if __name__ == "__main__": diff --git a/CppHeaderParser/test/TestSampleClass.h b/CppHeaderParser/test/TestSampleClass.h index c607cd4..85a65d5 100644 --- a/CppHeaderParser/test/TestSampleClass.h +++ b/CppHeaderParser/test/TestSampleClass.h @@ -47,6 +47,14 @@ class SampleClass: public BaseSampleClass string prop1; //! prop5 description int prop5; + + bool prop6; /*!< prop6 description */ + + double prop7; //!< prop7 description + //!< with two lines + + /// prop8 description + int prop8; }; namespace Alpha { diff --git a/CppHeaderParser/test/test_CppHeaderParser.py b/CppHeaderParser/test/test_CppHeaderParser.py index 4394a6f..b177311 100644 --- a/CppHeaderParser/test/test_CppHeaderParser.py +++ b/CppHeaderParser/test/test_CppHeaderParser.py @@ -229,54 +229,39 @@ def test_doxygen(self): ) -class SampleClass_prop1_TestCase(unittest.TestCase): - def setUp(self): - self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - - def test_name(self): - self.assertEqual( - self.cppHeader.classes["SampleClass"]["properties"]["private"][0]["name"], - "prop1", - ) - - def test_type(self): - self.assertEqual( - self.cppHeader.classes["SampleClass"]["properties"]["private"][0]["type"], - "string", - ) - - def test_doxygen(self): - self.assertEqual( - self.cppHeader.classes["SampleClass"]["properties"]["private"][0][ - "doxygen" - ], - "/// prop1 description", - ) - - -class SampleClass_prop5_TestCase(unittest.TestCase): - def setUp(self): - self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") - - def test_name(self): - self.assertEqual( - self.cppHeader.classes["SampleClass"]["properties"]["private"][1]["name"], - "prop5", - ) - - def test_type(self): - self.assertEqual( - self.cppHeader.classes["SampleClass"]["properties"]["private"][1]["type"], - "int", - ) - - def test_doxygen(self): - self.assertEqual( - self.cppHeader.classes["SampleClass"]["properties"]["private"][1][ - "doxygen" - ], - "//! prop5 description", - ) +class SampleClass_doxygen_TestCase(unittest.TestCase): + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") + + def test_prop1(self): + m = self.cppHeader.classes["SampleClass"]["properties"]["private"][0] + self.assertEqual(m["name"], "prop1") + self.assertEqual(m["type"], "string") + self.assertEqual(m["doxygen"], "/// prop1 description") + + def test_prop5(self): + m = self.cppHeader.classes["SampleClass"]["properties"]["private"][1] + self.assertEqual(m["name"], "prop5") + self.assertEqual(m["type"], "int") + self.assertEqual(m["doxygen"], "//! prop5 description") + + def test_prop6(self): + m = self.cppHeader.classes["SampleClass"]["properties"]["private"][2] + self.assertEqual(m["name"], "prop6") + self.assertEqual(m["type"], "bool") + self.assertEqual(m["doxygen"], "/*!< prop6 description */") + + def test_prop7(self): + m = self.cppHeader.classes["SampleClass"]["properties"]["private"][3] + self.assertEqual(m["name"], "prop7") + self.assertEqual(m["type"], "double") + self.assertEqual(m["doxygen"], "//!< prop7 description\n//!< with two lines") + + def test_prop8(self): + m = self.cppHeader.classes["SampleClass"]["properties"]["private"][4] + self.assertEqual(m["name"], "prop8") + self.assertEqual(m["type"], "int") + self.assertEqual(m["doxygen"], "/// prop8 description") class SampleClass_Elephant_TestCase(unittest.TestCase): From 20cf71c49dad6990d706b422b868d0aff05032f3 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 22 Dec 2019 00:40:55 -0500 Subject: [PATCH 045/143] Standalone lexer shouldn't fail on EOF --- CppHeaderParser/lexer.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CppHeaderParser/lexer.py b/CppHeaderParser/lexer.py index 3d741be..33a3980 100644 --- a/CppHeaderParser/lexer.py +++ b/CppHeaderParser/lexer.py @@ -216,4 +216,7 @@ def return_token(self, tok): if __name__ == "__main__": - lex.runmain(lexer=Lexer(None)) + try: + lex.runmain(lexer=Lexer(None)) + except EOFError: + pass From 766e032139e291286c658d835f81faa8e94027fa Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 22 Dec 2019 01:43:45 -0500 Subject: [PATCH 046/143] Prevent < from lexing as the start of an identifier --- CppHeaderParser/CppHeaderParser.py | 16 ++++++++-------- CppHeaderParser/lexer.py | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 93a872d..8685c6d 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -979,10 +979,8 @@ def __init__(self, nameStack, curClass, methinfo, curTemplate, doxygen, location self["rtnType"] = "void" self["rtnType"] = self["rtnType"].replace(" : : ", "::") - self["rtnType"] = self["rtnType"].replace(" <", "<") - self["rtnType"] = ( - self["rtnType"].replace(" >", ">").replace(">>", "> >").replace(">>", "> >") - ) + self["rtnType"] = self["rtnType"].replace(" < ", "<") + self["rtnType"] = self["rtnType"].replace(" > ", "> ").replace(">>", "> >") self["rtnType"] = self["rtnType"].replace(" ,", ",") # deal with "noexcept" specifier/operator @@ -1240,11 +1238,11 @@ def __init__(self, nameStack, doxygen, location, **kwargs): self["type"] = self["type"].replace(" :", ":") self["type"] = self["type"].replace(": ", ":") - self["type"] = self["type"].replace(" <", "<") - self["type"] = ( - self["type"].replace(" >", ">").replace(">>", "> >").replace(">>", "> >") - ) + self["type"] = self["type"].replace(" < ", "<") + self["type"] = self["type"].replace(" > ", "> ").replace(">>", "> >") + self["type"] = self["type"].replace(") >", ")>") self["type"] = self["type"].replace(" ,", ",") + # Optional doxygen description try: self["desc"] = kwargs["doxyVarDesc"][self["name"]] @@ -2611,6 +2609,8 @@ def evalute_forward_decl(self): "NAME", "&", "*", + "<", + ">", "CHAR_LITERAL" ]) # fmt: on diff --git a/CppHeaderParser/lexer.py b/CppHeaderParser/lexer.py index 33a3980..d647afe 100644 --- a/CppHeaderParser/lexer.py +++ b/CppHeaderParser/lexer.py @@ -55,7 +55,7 @@ class Lexer(object): t_NUMBER = r"[0-9][0-9XxA-Fa-f]*" t_FLOAT_NUMBER = r"[-+]?[0-9]*\.[0-9]+([eE][-+]?[0-9]+)?" t_TEMPLATE_NAME = r"CppHeaderParser_template_[0-9]+" - t_NAME = r"[<>A-Za-z_~][A-Za-z0-9_]*" + t_NAME = r"[A-Za-z_~][A-Za-z0-9_]*" def t_PRECOMP_MACRO(self, t): r"\#.*" From 81f336dc8d046bc9bb8c26fdcec0bb55a27f7050 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 22 Dec 2019 02:19:18 -0500 Subject: [PATCH 047/143] Sorta parse template tokens --- CppHeaderParser/CppHeaderParser.py | 136 +++++++++++-------- CppHeaderParser/lexer.py | 2 - CppHeaderParser/test/test_CppHeaderParser.py | 21 ++- 3 files changed, 88 insertions(+), 71 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 8685c6d..527bdcb 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -46,6 +46,7 @@ # +from collections import deque import os import sys import re @@ -2697,7 +2698,6 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self.nameStackHistory = [] self.anon_struct_counter = 0 self.anon_union_counter = [-1, 0] - self.templateRegistry = [] #: Using directives in this header: key is full name for lookup, value #: is :class:`.CppVariable` @@ -2716,50 +2716,6 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): "[ ]+", " ", supportedAccessSpecifier[i] ).strip() - # Strip out template declarations - templateSectionsToSliceOut = [] - try: - for m in re.finditer("template[\t ]*<[^>]*>", headerFileStr): - start = m.start() - # Search for the final '>' which may or may not be caught in the case of nexted <>'s - for i in range(start, len(headerFileStr)): - if headerFileStr[i] == "<": - firstBracket = i - break - ltgtStackCount = 1 - # Now look for fianl '>' - for i in range(firstBracket + 1, len(headerFileStr)): - if headerFileStr[i] == "<": - ltgtStackCount += 1 - elif headerFileStr[i] == ">": - ltgtStackCount -= 1 - if ltgtStackCount == 0: - end = i - break - templateSectionsToSliceOut.append((start, end)) - - # Now strip out all instances of the template - templateSectionsToSliceOut.reverse() - for tslice in templateSectionsToSliceOut: - # Replace the template symbol with a single symbol - template_symbol = "CppHeaderParser_template_%d" % len( - self.templateRegistry - ) - self.templateRegistry.append(headerFileStr[tslice[0] : tslice[1] + 1]) - newlines = ( - headerFileStr[tslice[0] : tslice[1]].count("\n") * "\n" - ) # Keep line numbers the same - headerFileStr = ( - headerFileStr[: tslice[0]] - + newlines - + " " - + template_symbol - + " " - + headerFileStr[tslice[1] + 1 :] - ) - except: - pass - # Change multi line #defines and expressions to single lines maintaining line nubmers # Based from http://stackoverflow.com/questions/2424458/regular-expression-to-match-cs-multiline-preprocessor-statements matches = re.findall(r"(?m)^(?:.*\\\r?\n)+.*$", headerFileStr) @@ -2843,10 +2799,14 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self.anon_union_counter[1] -= 1 tok.value = TagStr(tok.value, location=lex.current_location()) # debug_print("TOK: %s"%tok) - if tok.type == "NAME" and tok.value in self.IGNORE_NAMES: - continue - if tok.type != "TEMPLATE_NAME": - self.stack.append(tok.value) + if tok.type == "NAME": + if tok.value in self.IGNORE_NAMES: + continue + elif tok.value == "template": + self._parse_template() + continue + + self.stack.append(tok.value) if tok.type in ("PRECOMP_MACRO", "PRECOMP_MACRO_CONT"): debug_print("PRECOMP: %s" % tok) @@ -2854,14 +2814,6 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self.stack = [] self.nameStack = [] continue - if tok.type == "TEMPLATE_NAME": - try: - templateId = int( - tok.value.replace("CppHeaderParser_template_", "") - ) - self.curTemplate = self.templateRegistry[templateId] - except: - pass if tok.type == "{": if len(self.nameStack) >= 2 and is_namespace( self.nameStack @@ -3075,7 +3027,6 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): "_structs_brace_level", "typedefs_order", "curTemplate", - "templateRegistry", ]: del self.__dict__[key] @@ -3087,6 +3038,60 @@ def _get_location(self, stack): return self.lex.current_location() + def _parse_error(self, tokens, expected): + if not tokens: + # common case after a failed token_if + errtok = self.lex.token() + else: + errtok = tokens[-1] + if expected: + expected = ", expected " + expected + + msg = "unexpected %s%s" % (errtok.value, expected) + + # TODO: better error message + return CppParseError(msg) + + def _next_token_must_be(self, *tokenTypes): + tok = self.lex.token() + if tok.type not in tokenTypes: + raise self._parse_error((tok,), " or ".join(tokenTypes)) + return tok + + _end_balanced_tokens = set([">", "}", "]", ")", "DBL_RBRACKET"]) + _balanced_token_map = { + "<": ">", + "{": "}", + "(": ")", + "[": "]", + "DBL_LBRACKET": "DBL_RBRACKET", + } + + def _consume_balanced_tokens(self, *init_tokens): + + _balanced_token_map = self._balanced_token_map + + consumed = list(init_tokens) + match_stack = deque((_balanced_token_map[tok.type] for tok in consumed)) + get_token = self.lex.token + + while True: + tok = get_token() + consumed.append(tok) + + if tok.type in self._end_balanced_tokens: + expected = match_stack.pop() + if tok.type != expected: + raise self._parse_error(consumed, match_stack[-1]) + if len(match_stack) == 0: + return consumed + + continue + + next_end = _balanced_token_map.get(tok.type) + if next_end: + match_stack.append(next_end) + def _evaluate_stack(self, token=None): """Evaluates the current name stack""" @@ -3264,6 +3269,21 @@ def _evaluate_stack(self, token=None): self.lex.doxygenCommentCache = "" self.curTemplate = None + def _parse_template(self): + tok = self._next_token_must_be("<") + consumed = self._consume_balanced_tokens(tok) + tmpl = " ".join(tok.value for tok in consumed) + tmpl = ( + tmpl.replace(" : : ", "::") + .replace(" <", "<") + .replace("< ", "<") + .replace(" >", ">") + .replace("> ", ">") + .replace(" , ", ", ") + .replace(" = ", "=") + ) + self.curTemplate = "template" + tmpl + def _evaluate_enum_stack(self): """Create an Enum out of the name stack""" debug_print("evaluating enum") diff --git a/CppHeaderParser/lexer.py b/CppHeaderParser/lexer.py index d647afe..a0bfb57 100644 --- a/CppHeaderParser/lexer.py +++ b/CppHeaderParser/lexer.py @@ -10,7 +10,6 @@ class Lexer(object): tokens = [ "NUMBER", "FLOAT_NUMBER", - "TEMPLATE_NAME", "NAME", "COMMENT_SINGLELINE", "COMMENT_MULTILINE", @@ -54,7 +53,6 @@ class Lexer(object): t_ignore = " \t\r?@\f" t_NUMBER = r"[0-9][0-9XxA-Fa-f]*" t_FLOAT_NUMBER = r"[-+]?[0-9]*\.[0-9]+([eE][-+]?[0-9]+)?" - t_TEMPLATE_NAME = r"CppHeaderParser_template_[0-9]+" t_NAME = r"[A-Za-z_~][A-Za-z0-9_]*" def t_PRECOMP_MACRO(self, t): diff --git a/CppHeaderParser/test/test_CppHeaderParser.py b/CppHeaderParser/test/test_CppHeaderParser.py index b177311..d87a921 100644 --- a/CppHeaderParser/test/test_CppHeaderParser.py +++ b/CppHeaderParser/test/test_CppHeaderParser.py @@ -238,7 +238,7 @@ def test_prop1(self): self.assertEqual(m["name"], "prop1") self.assertEqual(m["type"], "string") self.assertEqual(m["doxygen"], "/// prop1 description") - + def test_prop5(self): m = self.cppHeader.classes["SampleClass"]["properties"]["private"][1] self.assertEqual(m["name"], "prop5") @@ -250,7 +250,7 @@ def test_prop6(self): self.assertEqual(m["name"], "prop6") self.assertEqual(m["type"], "bool") self.assertEqual(m["doxygen"], "/*!< prop6 description */") - + def test_prop7(self): m = self.cppHeader.classes["SampleClass"]["properties"]["private"][3] self.assertEqual(m["name"], "prop7") @@ -729,7 +729,7 @@ def test_num_protected_methods(self): def test_template(self): self.assertEqual( self.cppHeader.classes["Chicken"]["methods"]["private"][0]["template"], - "template ", + "template", ) @@ -1732,7 +1732,7 @@ def test_num_public_properties_sweet(self): def test_class_template(self): self.assertEqual( self.cppHeader.classes["Onion"]["template"], - "template ", + "template", ) @@ -2015,7 +2015,8 @@ def test_AfterTypedefClass_exists(self): # Bug BitBucket #6 class LineNumAfterDivide_TestCase(unittest.TestCase): def setUp(self): - self.cppHeader = CppHeaderParser.CppHeader(""" + self.cppHeader = CppHeaderParser.CppHeader( + """ // Bug BitBucket #6 class LineNumAfterDivide @@ -2026,7 +2027,9 @@ class LineNumAfterDivide void func2(); }; -""", "string") +""", + "string", + ) def test_line_num(self): m = self.cppHeader.classes["LineNumAfterDivide"]["methods"]["private"][1] @@ -2259,11 +2262,7 @@ def test_Avacado_exists(self): ) def test_class_template(self): - template_str = ( - "template >" - ) + template_str = "template>" self.assertEqual( self.cppHeader.classes["Raddish_SetIterator"]["template"], template_str ) From 280342682187f610802a8932c42c3fa0ee87c181 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 22 Dec 2019 02:59:32 -0500 Subject: [PATCH 048/143] Add support for ignoring C++11 attributes --- CppHeaderParser/CppHeaderParser.py | 31 ++++++++++++++++++++ CppHeaderParser/test/test_CppHeaderParser.py | 29 ++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 527bdcb..5bb857d 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -2805,6 +2805,12 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): elif tok.value == "template": self._parse_template() continue + elif tok.value == "alignas": + self._parse_attribute_specifier_seq(tok) + continue + elif tok.type == "DBL_LBRACKET": + self._parse_attribute_specifier_seq(tok) + continue self.stack.append(tok.value) @@ -3284,6 +3290,31 @@ def _parse_template(self): ) self.curTemplate = "template" + tmpl + _attribute_specifier_seq_start_types = ("DBL_LBRACKET", "NAME") + + def _parse_attribute_specifier_seq(self, tok): + # TODO: retain the attributes and do something with them + # attrs = [] + + while True: + if tok.type == "DBL_LBRACKET": + tokens = self._consume_balanced_tokens(tok) + # attrs.append(Attribute(tokens)) + elif tok.type == "NAME" and tok.value == "alignas": + next_tok = self._next_token_must_be("(") + tokens = self._consume_balanced_tokens(next_tok) + # attrs.append(AlignasAttribute(tokens)) + else: + self.lex.return_token(tok) + break + + # multiple attributes can be specified + tok = self.lex.token_if(*self._attribute_specifier_seq_start_types) + if tok is None: + break + + # return attrs + def _evaluate_enum_stack(self): """Create an Enum out of the name stack""" debug_print("evaluating enum") diff --git a/CppHeaderParser/test/test_CppHeaderParser.py b/CppHeaderParser/test/test_CppHeaderParser.py index d87a921..16de75a 100644 --- a/CppHeaderParser/test/test_CppHeaderParser.py +++ b/CppHeaderParser/test/test_CppHeaderParser.py @@ -3155,5 +3155,34 @@ def testS(self): ) +class Attributes_TestCase(unittest.TestCase): + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader( + """ + +struct [[deprecated]] S {}; +[[deprecated]] typedef S* PS; + +[[deprecated]] int x; +union U { [[deprecated]] int n; }; +[[deprecated]] void f(); + +enum [[deprecated]] E { A [[deprecated]], B [[deprecated]] = 42 }; + +struct alignas(8) AS {}; + +""", + "string", + ) + + def test_existance(self): + + self.assertIn("S", self.cppHeader.classes) + self.assertIn("PS", self.cppHeader.typedefs) + self.assertEqual("x", self.cppHeader.variables[0]["name"]) + self.assertEqual("f", self.cppHeader.functions[0]["name"]) + self.assertIn("AS", self.cppHeader.classes) + + if __name__ == "__main__": unittest.main() From 70a2a0aaa3d820ff560258a54f4f62704e5c2d33 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 22 Dec 2019 04:54:28 -0500 Subject: [PATCH 049/143] Add another way to ignore __attribute__ --- CppHeaderParser/CppHeaderParser.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 5bb857d..3473ae8 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -2808,6 +2808,9 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): elif tok.value == "alignas": self._parse_attribute_specifier_seq(tok) continue + elif tok.value == "__attribute__": + self._parse_gcc_attribute() + continue elif tok.type == "DBL_LBRACKET": self._parse_attribute_specifier_seq(tok) continue @@ -3290,6 +3293,11 @@ def _parse_template(self): ) self.curTemplate = "template" + tmpl + def _parse_gcc_attribute(self): + tok1 = self._next_token_must_be("(") + tok2 = self._next_token_must_be("(") + self._consume_balanced_tokens(tok1, tok2) + _attribute_specifier_seq_start_types = ("DBL_LBRACKET", "NAME") def _parse_attribute_specifier_seq(self, tok): From 89b3858db3bf0f1e9a1cbe52635a45dc1ad6d3c8 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 22 Dec 2019 04:56:29 -0500 Subject: [PATCH 050/143] Rewrite enum parser - Fixes #12 --- CppHeaderParser/CppHeaderParser.py | 285 +++++++++++-------- CppHeaderParser/test/TestSampleClass.h | 6 +- CppHeaderParser/test/test_CppHeaderParser.py | 6 +- 3 files changed, 172 insertions(+), 125 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 3473ae8..d900a38 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -125,17 +125,6 @@ def is_namespace(nameStack): return False -def is_enum_namestack(nameStack): - """Determines if a namestack is an enum namestack""" - if len(nameStack) == 0: - return False - if nameStack[0] == "enum": - return True - if len(nameStack) > 1 and nameStack[0] == "typedef" and nameStack[1] == "enum": - return True - return False - - _fundamentals = set( [ "size_t", @@ -1324,13 +1313,11 @@ def resolve_enum_values(self, values): except: pass i += 1 - return t + self["type"] = t class CppEnum(_CppEnum): - """Takes a name stack and turns it into an Enum - - Contains the following keys: + """Contains the following keys: * ``name`` - Name of the enum (ex. "ItemState") * ``namespace`` - Namespace containing the enum @@ -1342,71 +1329,15 @@ class CppEnum(_CppEnum): if a value for a given enum value was defined """ - def __init__(self, nameStack, doxygen, location): + def __init__(self, name, doxygen, location): if doxygen: self["doxygen"] = doxygen - if len(nameStack) == 3 and nameStack[0] == "enum": - debug_print("Created enum as just name/value") - self["name"] = nameStack[1] - self["instances"] = [[nameStack[2]]] - if len(nameStack) < 4 or "{" not in nameStack or "}" not in nameStack: - # Not enough stuff for an enum - debug_print("Bad enum") - return - valueList = [] - set_location_info(self, location) - # Figure out what values it has - valueStack = nameStack[nameStack.index("{") + 1 : nameStack.index("}")] - while len(valueStack): - tmpStack = [] - if "," in valueStack: - tmpStack = valueStack[: valueStack.index(",")] - valueStack = valueStack[valueStack.index(",") + 1 :] - else: - tmpStack = valueStack - valueStack = [] - d = {} - if len(tmpStack) == 1: - d["name"] = tmpStack[0] - elif len(tmpStack) >= 3 and tmpStack[1] == "=": - d["name"] = tmpStack[0] - d["value"] = " ".join(tmpStack[2:]) - elif len(tmpStack) == 2 and tmpStack[1] == "=": - debug_print("WARN-enum: parser missed value for %s" % tmpStack[0]) - d["name"] = tmpStack[0] - - if d: - valueList.append(d) - - if len(valueList): - self["type"] = self.resolve_enum_values( - valueList - ) # returns int for standard enum - self["values"] = valueList - else: - warning_print("WARN-enum: empty enum %s" % nameStack) - return - # Figure out if it has a name - preBraceStack = nameStack[: nameStack.index("{")] - postBraceStack = nameStack[nameStack.index("}") + 1 :] - self["typedef"] = False - if len(preBraceStack) == 4 and ":" in nameStack and "typedef" not in nameStack: - # C++11 specify enum type with "enum : ..." syntax - self["name"] = preBraceStack[1] - self["type"] = preBraceStack[3] - elif len(preBraceStack) == 2 and "typedef" not in nameStack: - # enum "enum ..." syntax - self["name"] = preBraceStack[1] - elif len(postBraceStack) and "typedef" in nameStack: - self["name"] = " ".join(postBraceStack) - self["typedef"] = True - else: - warning_print("WARN-enum: nameless enum %s" % nameStack) - # See if there are instances of this - if "typedef" not in nameStack and len(postBraceStack): - self["instances"] = list(_split_by_comma(postBraceStack)) - + if name: + self["name"] = name self["namespace"] = "" + self["typedef"] = False + self["values"] = [] + set_location_info(self, location) class CppStruct(dict): @@ -2395,9 +2326,9 @@ def _evaluate_typedef(self): def _evaluate_property_stack(self, clearStack=True, addToVar=None): """Create a Property out of the name stack""" global parseHistory - assert self.stack and self.stack[-1] == ";" debug_print("trace") if self.nameStack[0] == "typedef": + assert self.stack and self.stack[-1] == ";" if self.curClass: typedef = self._parse_typedef(self.stack) name = typedef["name"] @@ -2808,6 +2739,11 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): elif tok.value == "alignas": self._parse_attribute_specifier_seq(tok) continue + elif tok.value == "enum": + self._parse_enum() + self.stack = [] + self.nameStack = [] + continue elif tok.value == "__attribute__": self._parse_gcc_attribute() continue @@ -2859,10 +2795,8 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self.nameStack = origNameStack[classLocationNS:] self.stack = origStack[classLocationS:] - if len(self.nameStack) and not is_enum_namestack(self.nameStack): + if self.nameStack: self._evaluate_stack() - else: - self.nameStack.append(tok.value) if self.stack and self.stack[0] == "class": self.stack = [] self.braceDepth += 1 @@ -2873,8 +2807,6 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): if self.braceDepth == len(self.nameSpaces): tmp = self.nameSpaces.pop() self.stack = [] # clear stack when namespace ends? - if len(self.nameStack) and is_enum_namestack(self.nameStack): - self.nameStack.append(tok.value) elif self.braceDepth < 10: self._evaluate_stack() else: @@ -3146,7 +3078,6 @@ def _evaluate_stack(self, token=None): ("struct" not in self.nameStack and "union" not in self.nameStack) or self.stack[-1] == ";" ) - and not is_enum_namestack(self.nameStack) ): trace_print("STACK", self.stack) self._evaluate_typedef() @@ -3187,10 +3118,6 @@ def _evaluate_stack(self, token=None): alias = self.current_namespace() + alias self.using[alias] = atype - elif is_enum_namestack(self.nameStack): - debug_print("trace") - self._evaluate_enum_stack() - elif self._method_body and (self.braceDepth + 1) > self._method_body: trace_print("INSIDE METHOD DEF") elif ( @@ -3254,9 +3181,7 @@ def _evaluate_stack(self, token=None): elif not self.curClass: debug_print("trace") - if is_enum_namestack(self.nameStack): - self._evaluate_enum_stack() - elif self.curStruct and self.stack[-1] == ";": + if self.curStruct and self.stack[-1] == ";": self._evaluate_property_stack() # this catches fields of global structs self.nameStack = [] elif self.braceDepth < 1: @@ -3299,6 +3224,7 @@ def _parse_gcc_attribute(self): self._consume_balanced_tokens(tok1, tok2) _attribute_specifier_seq_start_types = ("DBL_LBRACKET", "NAME") + _attribute_specifier_seq_start_values = ("[[", "alignas") def _parse_attribute_specifier_seq(self, tok): # TODO: retain the attributes and do something with them @@ -3323,35 +3249,156 @@ def _parse_attribute_specifier_seq(self, tok): # return attrs - def _evaluate_enum_stack(self): - """Create an Enum out of the name stack""" - debug_print("evaluating enum") - newEnum = CppEnum( - self.nameStack, self.lex.get_doxygen(), self._get_location(self.nameStack) - ) - if len(list(newEnum.keys())): - if len(self.curClass): - newEnum["namespace"] = self.cur_namespace(False) - klass = self.classes[self.curClass] - klass["enums"][self.curAccessSpecifier].append(newEnum) - if self.curAccessSpecifier == "public" and "name" in newEnum: - klass._public_enums[newEnum["name"]] = newEnum + def _parse_enum(self): + """ + opaque_enum_declaration: enum_key [attribute_specifier_seq] IDENTIFIER [enum_base] ";" + + enum_specifier: enum_head "{" [enumerator_list] "}" + | enum_head "{" enumerator_list "," "}" + + enum_head: enum_key [attribute_specifier_seq] [IDENTIFIER] [enum_base] + | enum_key [attribute_specifier_seq] nested_name_specifier IDENTIFIER [enum_base] + + enum_key: "enum" + | "enum" "class" + | "enum" "struct" + + enum_base: ":" type_specifier_seq + """ + + # entry: enum token was just consumed + doxygen = self.lex.get_doxygen() + location = self.lex.current_location() + + nametok = self.lex.token() + if nametok.value in ("class", "struct"): + nametok = self.lex.token() + + if nametok.value == "__attribute__": + self._parse_gcc_attribute() + nametok = self.lex.token() + + if nametok.value in self._attribute_specifier_seq_start_values: + self._parse_attribute_specifier_seq(nametok) + nametok = self.lex.token() + + # TODO: nested_name_specifier + name = "" + if nametok.type == "NAME": + name = nametok.value + tok = self.lex.token() + else: + tok = nametok + + base = [] + if tok.type == ":": + while True: + tok = self.lex.token() + if tok.type in ("{", ";"): + break + base.append(tok.value) + + newEnum = CppEnum(name, doxygen, location) + if self.nameStack: + if self.nameStack[0] == "typedef": + newEnum["typedef"] = True + if base: + newEnum["type"] = "".join(base) + + instancesData = [] + + if tok.type == "{": + self._parse_enumerator_list(newEnum["values"]) + newEnum.resolve_enum_values(newEnum["values"]) + tok = self.lex.token() + + if tok.value == "__attribute__": + self._parse_gcc_attribute() + tok = self.lex.token() + + if tok.type == "NAME": + if newEnum["typedef"]: + newEnum["name"] = tok.value + self._next_token_must_be(";") else: - newEnum["namespace"] = self.cur_namespace(True) - self.enums.append(newEnum) - if "name" in newEnum and newEnum["name"]: - self.global_enums[newEnum["name"]] = newEnum - - # This enum has instances, turn them into properties - if "instances" in newEnum: - instanceType = "enum" - if "name" in newEnum: - instanceType = newEnum["name"] - addToVar = {"enum_type": newEnum} - for instance in newEnum["instances"]: - self.nameStack = [instanceType] + instance - self._evaluate_property_stack(clearStack=False, addToVar=addToVar) - del newEnum["instances"] + # this is an instance of the enum + instancesData.append(tok.value) + while True: + tok = self.lex.token() + if tok.type == ";": + break + instancesData.append(tok.value) + elif tok.type != ";": + raise self._parse_error((tok,), ";") + + self._install_enum(newEnum, instancesData) + + def _install_enum(self, newEnum, instancesData): + if len(self.curClass): + newEnum["namespace"] = self.cur_namespace(False) + klass = self.classes[self.curClass] + klass["enums"][self.curAccessSpecifier].append(newEnum) + if self.curAccessSpecifier == "public" and "name" in newEnum: + klass._public_enums[newEnum["name"]] = newEnum + else: + newEnum["namespace"] = self.cur_namespace(True) + self.enums.append(newEnum) + if "name" in newEnum and newEnum["name"]: + self.global_enums[newEnum["name"]] = newEnum + + # This enum has instances, turn them into properties + if instancesData: + instances = list(_split_by_comma(instancesData)) + instanceType = "enum" + if "name" in newEnum: + instanceType = newEnum["name"] + addToVar = {"enum_type": newEnum} + for instance in instances: + self.nameStack = [instanceType] + instance + self._evaluate_property_stack(clearStack=False, addToVar=addToVar) + + def _parse_enumerator_list(self, values): + """ + enumerator_list: enumerator_definition + | enumerator_list "," enumerator_definition + + enumerator_definition: enumerator + | enumerator "=" constant_expression + + enumerator: IDENTIFIER + """ + while True: + name_tok = self._next_token_must_be("}", "NAME") + if name_tok.value == "}": + return + + value = {"name": name_tok.value} + doxygen = self.lex.get_doxygen() + if doxygen: + value["doxygen"] = doxygen + values.append(value) + + tok = self._next_token_must_be("}", ",", "=", "DBL_LBRACKET") + if tok.type == "DBL_LBRACKET": + self._parse_attribute_specifier_seq(tok) + tok = self._next_token_must_be("}", ",", "=") + + if tok.type == "}": + return + elif tok.type == ",": + continue + elif tok.type == "=": + v = [] + while True: + tok = self.lex.token() + if tok.type == "}": + value["value"] = " ".join(v) + return + elif tok.type == ",": + value["value"] = " ".join(v) + break + else: + v.append(tok.value) def _strip_parent_keys(self): """Strip all parent (and method) keys to prevent loops""" diff --git a/CppHeaderParser/test/TestSampleClass.h b/CppHeaderParser/test/TestSampleClass.h index 85a65d5..c648828 100644 --- a/CppHeaderParser/test/TestSampleClass.h +++ b/CppHeaderParser/test/TestSampleClass.h @@ -90,9 +90,9 @@ namespace Alpha /// typedef enum { - RI_ZERO, - RI_ONE, - RI_TWO + RI_ZERO, /// item zero + RI_ONE, /** item one */ + RI_TWO //!< item two } Rino; }; }; diff --git a/CppHeaderParser/test/test_CppHeaderParser.py b/CppHeaderParser/test/test_CppHeaderParser.py index 16de75a..b100e46 100644 --- a/CppHeaderParser/test/test_CppHeaderParser.py +++ b/CppHeaderParser/test/test_CppHeaderParser.py @@ -506,9 +506,9 @@ def test_values(self): self.assertEqual( self.cppHeader.classes["OmegaClass"]["enums"]["protected"][0]["values"], [ - {"name": "RI_ZERO", "value": 0}, - {"name": "RI_ONE", "value": 1}, - {"name": "RI_TWO", "value": 2}, + {"name": "RI_ZERO", "value": 0, "doxygen": "/// item zero"}, + {"name": "RI_ONE", "value": 1, "doxygen": "/** item one */"}, + {"name": "RI_TWO", "value": 2, "doxygen": "//!< item two"}, ], ) From 0f49fcd03ae868726a0e9686714847c70731479b Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 22 Dec 2019 05:01:30 -0500 Subject: [PATCH 051/143] Python 2.7 compat --- CppHeaderParser/lexer.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CppHeaderParser/lexer.py b/CppHeaderParser/lexer.py index a0bfb57..356006b 100644 --- a/CppHeaderParser/lexer.py +++ b/CppHeaderParser/lexer.py @@ -104,7 +104,7 @@ def t_COMMENT_MULTILINE(self, t): def t_NEWLINE(self, t): r"\n+" t.lexer.lineno += len(t.value) - self.comments.clear() + del self.comments[:] return t def t_error(self, v): @@ -171,10 +171,10 @@ def get_doxygen(self): if ttype == "NAME": break - self.comments.clear() + del self.comments[:] comments = "\n".join(comments) - self.comments.clear() + del self.comments[:] return comments _discard_types = set(["NEWLINE", "COMMENT_SINGLELINE", "COMMENT_MULTILINE"]) From 3709ab404e5251f7801efb155a0775bf8084e017 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 22 Dec 2019 15:16:59 -0500 Subject: [PATCH 052/143] Use set literal syntax --- CppHeaderParser/CppHeaderParser.py | 48 ++++++++++++++---------------- CppHeaderParser/lexer.py | 2 +- 2 files changed, 24 insertions(+), 26 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index d900a38..afb5dd2 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -125,24 +125,22 @@ def is_namespace(nameStack): return False -_fundamentals = set( - [ - "size_t", - "struct", - "union", - "unsigned", - "signed", - "bool", - "char", - "short", - "int", - "float", - "double", - "long", - "void", - "*", - ] -) +_fundamentals = { + "size_t", + "struct", + "union", + "unsigned", + "signed", + "bool", + "char", + "short", + "int", + "float", + "double", + "long", + "void", + "*", +} def is_fundamental(s): @@ -2511,7 +2509,7 @@ def evalute_forward_decl(self): # fmt: off -_namestack_append_tokens = set([ +_namestack_append_tokens = { "(", ")", "[", @@ -2530,21 +2528,21 @@ def evalute_forward_decl(self): "+", "STRING_LITERAL", "ELLIPSIS", -]) +} -_namestack_pass_tokens = set([ +_namestack_pass_tokens = { "'", "." # preserve behaviour and eat individual fullstops -]) +} -_namestack_str_tokens = set([ +_namestack_str_tokens = { "NAME", "&", "*", "<", ">", "CHAR_LITERAL" -]) +} # fmt: on @@ -2999,7 +2997,7 @@ def _next_token_must_be(self, *tokenTypes): raise self._parse_error((tok,), " or ".join(tokenTypes)) return tok - _end_balanced_tokens = set([">", "}", "]", ")", "DBL_RBRACKET"]) + _end_balanced_tokens = {">", "}", "]", ")", "DBL_RBRACKET"} _balanced_token_map = { "<": ">", "{": "}", diff --git a/CppHeaderParser/lexer.py b/CppHeaderParser/lexer.py index 356006b..ee30238 100644 --- a/CppHeaderParser/lexer.py +++ b/CppHeaderParser/lexer.py @@ -177,7 +177,7 @@ def get_doxygen(self): del self.comments[:] return comments - _discard_types = set(["NEWLINE", "COMMENT_SINGLELINE", "COMMENT_MULTILINE"]) + _discard_types = {"NEWLINE", "COMMENT_SINGLELINE", "COMMENT_MULTILINE"} def token(self, eof_ok=False): tok = None From 08e047bb5c9ab0e0ab96295b68707c5171c08a05 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 22 Dec 2019 15:24:21 -0500 Subject: [PATCH 053/143] Add more enum doxygen testcases --- CppHeaderParser/test/TestSampleClass.h | 5 ++++- CppHeaderParser/test/test_CppHeaderParser.py | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CppHeaderParser/test/TestSampleClass.h b/CppHeaderParser/test/TestSampleClass.h index c648828..439635c 100644 --- a/CppHeaderParser/test/TestSampleClass.h +++ b/CppHeaderParser/test/TestSampleClass.h @@ -92,7 +92,10 @@ namespace Alpha { RI_ZERO, /// item zero RI_ONE, /** item one */ - RI_TWO //!< item two + RI_TWO, //!< item two + RI_THREE, + /// item four + RI_FOUR, } Rino; }; }; diff --git a/CppHeaderParser/test/test_CppHeaderParser.py b/CppHeaderParser/test/test_CppHeaderParser.py index b100e46..9019be0 100644 --- a/CppHeaderParser/test/test_CppHeaderParser.py +++ b/CppHeaderParser/test/test_CppHeaderParser.py @@ -509,6 +509,8 @@ def test_values(self): {"name": "RI_ZERO", "value": 0, "doxygen": "/// item zero"}, {"name": "RI_ONE", "value": 1, "doxygen": "/** item one */"}, {"name": "RI_TWO", "value": 2, "doxygen": "//!< item two"}, + {"name": "RI_THREE", "value": 3}, + {"name": "RI_FOUR", "value": 4, "doxygen": "/// item four"}, ], ) From a5633f45c672693ba1a0122883a042cc3d112984 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 22 Dec 2019 20:03:49 -0500 Subject: [PATCH 054/143] Add note that we won't fix preprocessor bugs --- README.rst | 16 +++++++++++++--- docs/api.rst | 10 +++++++++- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/README.rst b/README.rst index 250c982..77e2eb6 100644 --- a/README.rst +++ b/README.rst @@ -14,14 +14,22 @@ and critical to some of the stuff we do in the RobotPy project. Unfortunately, the maintainer seems to be busy, so robotpy-cppheaderparser was born. -We don’t currently intend to develop new features, but aim to maintain -compatibility with the existing code and make improvements and bugfixes -as we need them. +We aim to maintain (some) compatibility with the existing code and make +improvements and bugfixes as we need them -- though some decisions made +early on in this code's development means some compatibility may be broken +as things get fixed. If you find an bug, we encourage you to submit a pull request! New changes will only be accepted if there are tests to cover the change you made (and if they don’t break existing tests). +.. note:: CppHeaderParser only does some very minimal interpretation of + preprocessor directives -- and we're looking at removing some + of that from this library. If you need anything complex, you + should preprocess the code yourself. You can use the excellent + pure python preprocessor `pcpp`_, or the preprocessing facilities + provided by your favorite compiler. + Documentation ------------- @@ -53,5 +61,7 @@ Past contributors include: .. _CppHeaderParser: https://bitbucket.org/senex/cppheaderparser +.. _pcpp: https://github.com/ned14/pcpp + .. |Build Status| image:: https://travis-ci.org/robotpy/robotpy-cppheaderparser.svg?branch=master :target: https://travis-ci.org/robotpy/robotpy-cppheaderparser \ No newline at end of file diff --git a/docs/api.rst b/docs/api.rst index 50a10b8..6da7045 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -17,9 +17,15 @@ by printing out the JSON representation: python -m CppHeaderParser.tojson /path/to/header.h - .. warning:: CppHeaderParser is not safe to use from multiple threads +.. note:: CppHeaderParser only does some very minimal interpretation of + preprocessor directives -- and we're looking at removing some + of that from this library. If you need anything complex, you + should preprocess the code yourself. You can use the excellent + pure python preprocessor `pcpp`_, or the preprocessing facilities + provided by your favorite compiler. + CppHeaderParser --------------- @@ -29,3 +35,5 @@ CppHeaderParser ignoreSymbols :undoc-members: :show-inheritance: + +.. _pcpp: https://github.com/ned14/pcpp \ No newline at end of file From f00994d3391a7a91403e0d60d7b6adcfbb2e73c4 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 22 Dec 2019 16:56:28 -0500 Subject: [PATCH 055/143] Fix additional enum ambiguities --- CppHeaderParser/CppHeaderParser.py | 18 ++++++++++-- CppHeaderParser/lexer.py | 3 ++ CppHeaderParser/test/test_CppHeaderParser.py | 29 ++++++++++++++++++++ 3 files changed, 47 insertions(+), 3 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index afb5dd2..d422c4f 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -2528,6 +2528,7 @@ def evalute_forward_decl(self): "+", "STRING_LITERAL", "ELLIPSIS", + "SHIFT_LEFT", } _namestack_pass_tokens = { @@ -3021,7 +3022,16 @@ def _consume_balanced_tokens(self, *init_tokens): if tok.type in self._end_balanced_tokens: expected = match_stack.pop() if tok.type != expected: - raise self._parse_error(consumed, match_stack[-1]) + # hack: ambiguous right-shift issues here, really + # should be looking at the context + if tok.type == ">": + tok = self.lex.token_if(">") + if tok: + consumed.append(tok) + match_stack.append(expected) + continue + + raise self._parse_error(consumed, expected) if len(match_stack) == 0: return consumed @@ -3390,11 +3400,13 @@ def _parse_enumerator_list(self, values): while True: tok = self.lex.token() if tok.type == "}": - value["value"] = " ".join(v) + value["value"] = (" ".join(v)).replace(": :", "::") return elif tok.type == ",": - value["value"] = " ".join(v) + value["value"] = (" ".join(v)).replace(": :", "::") break + elif tok.type in self._balanced_token_map: + v.extend(t.value for t in self._consume_balanced_tokens(tok)) else: v.append(tok.value) diff --git a/CppHeaderParser/lexer.py b/CppHeaderParser/lexer.py index ee30238..154b95d 100644 --- a/CppHeaderParser/lexer.py +++ b/CppHeaderParser/lexer.py @@ -22,6 +22,7 @@ class Lexer(object): "ELLIPSIS", "DBL_LBRACKET", "DBL_RBRACKET", + "SHIFT_LEFT", ] literals = [ @@ -84,6 +85,8 @@ def t_COMMENT_SINGLELINE(self, t): t_ELLIPSIS = r"\.\.\." t_DBL_LBRACKET = r"\[\[" t_DBL_RBRACKET = r"\]\]" + t_SHIFT_LEFT = r"<<" + # SHIFT_RIGHT introduces ambiguity # found at http://wordaligned.org/articles/string-literals-and-regular-expressions # TODO: This does not work with the string "bla \" bla" diff --git a/CppHeaderParser/test/test_CppHeaderParser.py b/CppHeaderParser/test/test_CppHeaderParser.py index 9019be0..16044c3 100644 --- a/CppHeaderParser/test/test_CppHeaderParser.py +++ b/CppHeaderParser/test/test_CppHeaderParser.py @@ -3186,5 +3186,34 @@ def test_existance(self): self.assertIn("AS", self.cppHeader.classes) +class EnumWithTemplates_TestCase(unittest.TestCase): + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader( + """ +enum { + IsRandomAccess = std::is_base_of::value, + IsBidirectional = std::is_base_of::value, + }; +""", + "string", + ) + + def test_values(self): + e = self.cppHeader.enums[0] + v0 = e["values"][0] + self.assertEqual( + v0["value"], + "std :: is_base_of < std :: random_access_iterator_tag , IteratorCategoryT > :: value", + ) + + v1 = e["values"][1] + self.assertEqual( + v1["value"], + "std :: is_base_of < std :: bidirectional_iterator_tag , IteratorCategoryT > :: value", + ) + + if __name__ == "__main__": unittest.main() From b6b9320aedd441fa54dd14930f7460c19559d619 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 22 Dec 2019 18:26:31 -0500 Subject: [PATCH 056/143] Remove CppStruct, doesn't seem to be used anymore --- CppHeaderParser/CppHeaderParser.py | 94 +----------------------------- docs/api.rst | 2 +- 2 files changed, 4 insertions(+), 92 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index d422c4f..f62575c 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -593,8 +593,7 @@ class CppClass(dict): and values are lists of :class:`.CppVariable` * ``enums`` - Dictionary where keys are from supportedAccessSpecifier and values are lists of :class:`.CppEnum` - * ``structs`` - Dictionary where keys are from supportedAccessSpecifier and - values are lists of nested :class:`.CppStruct` + * ``nested_classes`` - Classes and structs defined within this class * ``final`` - True if final * ``abstract`` - True if abstract @@ -658,7 +657,6 @@ def __init__(self, nameStack, curTemplate, doxygen, location): self["abstract"] = False self["final"] = False self._public_enums = {} - self._public_structs = {} self._public_typedefs = {} self._public_forward_declares = [] self["namespace"] = "" @@ -690,7 +688,6 @@ def __init__(self, nameStack, curTemplate, doxygen, location): methodAccessSpecificList = {} propertyAccessSpecificList = {} enumAccessSpecificList = {} - structAccessSpecificList = {} typedefAccessSpecificList = {} forwardAccessSpecificList = {} @@ -698,14 +695,12 @@ def __init__(self, nameStack, curTemplate, doxygen, location): methodAccessSpecificList[accessSpecifier] = [] propertyAccessSpecificList[accessSpecifier] = [] enumAccessSpecificList[accessSpecifier] = [] - structAccessSpecificList[accessSpecifier] = [] typedefAccessSpecificList[accessSpecifier] = [] forwardAccessSpecificList[accessSpecifier] = [] self["methods"] = methodAccessSpecificList self["properties"] = propertyAccessSpecificList self["enums"] = enumAccessSpecificList - self["structs"] = structAccessSpecificList self["typedefs"] = typedefAccessSpecificList self["forward_declares"] = forwardAccessSpecificList @@ -1338,26 +1333,6 @@ def __init__(self, name, doxygen, location): set_location_info(self, location) -class CppStruct(dict): - """ - Dictionary that contains at least the following keys: - - * ``type`` - Name of this struct - * ``fields`` - List of :class:`.CppVariable` - * ``line_number`` - Line number this struct was found on - """ - - Structs = [] - - def __init__(self, nameStack, location): - if len(nameStack) >= 2: - self["type"] = nameStack[1] - else: - self["type"] = None - self["fields"] = [] - set_location_info(self, location) - self.Structs.append(self) - C99_NONSTANDARD = { "int8": "signed char", @@ -1392,21 +1367,16 @@ class Resolver(object): SubTypedefs = {} # TODO deprecate? NAMESPACES = [] CLASSES = {} - STRUCTS = {} def initextra(self): self.typedefs = {} self.typedefs_order = [] self.classes_order = [] - self.structs = Resolver.STRUCTS - self.structs_order = [] self.namespaces = Resolver.NAMESPACES # save all namespaces - self.curStruct = None self.stack = ( [] ) # full name stack, good idea to keep both stacks? (simple stack and full stack) self._classes_brace_level = {} # class name : level - self._structs_brace_level = {} # struct type : level self._method_body = None self._forward_decls = [] self._template_typenames = [] # template @@ -1565,13 +1535,6 @@ def resolve_type(self, string, result): # recursive result["unresolved"] = False def finalize_vars(self): - for ( - s - ) in ( - CppStruct.Structs - ): # vars within structs can be ignored if they do not resolve - for var in s["fields"]: - var["parent"] = s["type"] # for c in self.classes.values(): # for var in c.get_all_properties(): var['parent'] = c['name'] @@ -1600,8 +1563,6 @@ def finalize_vars(self): klass = var["method"]["parent"] if tag in var["method"]["parent"]._public_enums: nestedEnum = var["method"]["parent"]._public_enums[tag] - elif tag in var["method"]["parent"]._public_structs: - nestedStruct = var["method"]["parent"]._public_structs[tag] elif tag in var["method"]["parent"]._public_typedefs: nestedTypedef = var["method"]["parent"]._public_typedefs[ tag @@ -1648,14 +1609,6 @@ def finalize_vars(self): var["concrete_type"] ) - elif tag in self.structs: - trace_print("STRUCT", var) - var["struct"] = tag - var["ctypes_type"] = "ctypes.c_void_p" - var["raw_type"] = ( - self.structs[tag]["namespace"] + "::" + tag - ) - elif tag in self._forward_decls: var["forward_declared"] = tag var["ctypes_type"] = "ctypes.c_void_p" @@ -2039,23 +1992,6 @@ def finalize(self): cls["abstract"] = True break - def _evaluate_struct_stack(self): - """Create a Struct out of the name stack (but not its parts)""" - # print( 'eval struct stack', self.nameStack ) - # if self.braceDepth != len(self.nameSpaces): return - struct = CppStruct(self.nameStack, self._get_location(self.nameStack)) - struct["namespace"] = self.cur_namespace() - self.structs[struct["type"]] = struct - self.structs_order.append(struct) - if self.curClass: - struct["parent"] = self.curClass - klass = self.classes[self.curClass] - klass["structs"][self.curAccessSpecifier].append(struct) - if self.curAccessSpecifier == "public": - klass._public_structs[struct["type"]] = struct - self.curStruct = struct - self._structs_brace_level[struct["type"]] = self.braceDepth - _method_type_defaults = { n: False for n in "defined pure_virtual operator constructor destructor extern template virtual static explicit inline friend returns returns_pointer returns_fundamental returns_class default".split() @@ -2205,11 +2141,6 @@ def parse_method_type(self, stack): def _evaluate_method_stack(self): """Create a method out of the name stack""" - if self.curStruct: - trace_print("WARN - struct contains methods - skipping") - trace_print(self.stack) - assert 0 - info = self.parse_method_type(self.stack) if info: if ( @@ -2337,7 +2268,7 @@ def _evaluate_property_stack(self, clearStack=True, addToVar=None): Resolver.SubTypedefs[name] = self.curClass else: assert 0 - elif self.curStruct or self.curClass: + elif self.curClass: if len(self.nameStack) == 1: # See if we can de anonymize the type filteredParseHistory = [ @@ -2384,10 +2315,7 @@ def _evaluate_property_stack(self, clearStack=True, addToVar=None): self._get_location(self.nameStack), ) newVar["namespace"] = self.current_namespace() - if self.curStruct: - self.curStruct["fields"].append(newVar) - newVar["property_of_struct"] = self.curStruct - elif self.curClass: + if self.curClass: klass = self.classes[self.curClass] klass["properties"][self.curAccessSpecifier].append(newVar) newVar["property_of_class"] = klass["name"] @@ -2565,7 +2493,6 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): """ ## reset global state ## CppVariable.Vars = [] - CppStruct.Structs = [] if argType == "file": self.headerFileName = os.path.expandvars(headerFileName) @@ -2829,18 +2756,8 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self.curClass = self.classes[self.curClass]["parent"] else: self.curClass = "" - # self.curStruct = None self.stack = [] - # if self.curStruct: self.curStruct = None - if self.braceDepth == 0 or ( - self.curStruct - and self._structs_brace_level[self.curStruct["type"]] - == self.braceDepth - ): - trace_print("END OF STRUCT DEF") - self.curStruct = None - if self._method_body and (self.braceDepth + 1) <= self._method_body: self._method_body = None self.stack = [] @@ -2960,11 +2877,9 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): "_forward_decls", "stack", "mainClass", - "curStruct", "_template_typenames", "_method_body", "braceDepth", - "_structs_brace_level", "typedefs_order", "curTemplate", ]: @@ -3130,7 +3045,6 @@ def _evaluate_stack(self, token=None): trace_print("INSIDE METHOD DEF") elif ( is_method_namestack(self.stack) - and not self.curStruct and "(" in self.nameStack ): debug_print("trace") @@ -3189,8 +3103,6 @@ def _evaluate_stack(self, token=None): elif not self.curClass: debug_print("trace") - if self.curStruct and self.stack[-1] == ";": - self._evaluate_property_stack() # this catches fields of global structs self.nameStack = [] elif self.braceDepth < 1: debug_print("trace") diff --git a/docs/api.rst b/docs/api.rst index 6da7045..f3d83b8 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -31,7 +31,7 @@ CppHeaderParser .. automodule:: CppHeaderParser.CppHeaderParser :members: CppBaseDecl, CppClass, CppEnum, CppHeader, CppMethod, CppParseError, - CppStruct, CppTemplateParam, CppUnion, CppVariable, TagStr, + CppTemplateParam, CppUnion, CppVariable, TagStr, ignoreSymbols :undoc-members: :show-inheritance: From 9e9a515e1c02bf02c05a2c419f19210f476b3753 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 22 Dec 2019 19:14:38 -0500 Subject: [PATCH 057/143] Discard function/method contents in a simpler way --- CppHeaderParser/CppHeaderParser.py | 49 +++++++++----------- CppHeaderParser/test/test_CppHeaderParser.py | 24 ++++++++++ 2 files changed, 47 insertions(+), 26 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index f62575c..025645c 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -1333,7 +1333,6 @@ def __init__(self, name, doxygen, location): set_location_info(self, location) - C99_NONSTANDARD = { "int8": "signed char", "int16": "short int", @@ -1377,7 +1376,6 @@ def initextra(self): [] ) # full name stack, good idea to keep both stacks? (simple stack and full stack) self._classes_brace_level = {} # class name : level - self._method_body = None self._forward_decls = [] self._template_typenames = [] # template @@ -2023,13 +2021,12 @@ def parse_method_type(self, stack): header = header.replace("default ", "default") header = header.strip() - if "{" in stack: + if stack[-1] == "{": info["defined"] = True - self._method_body = self.braceDepth + 1 - trace_print("NEW METHOD WITH BODY", self.braceDepth) + self._discard_function_contents(stack) + self.braceHandled = True elif stack[-1] == ";": info["defined"] = False - self._method_body = None # not a great idea to be clearing here else: assert 0 @@ -2721,11 +2718,13 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self.nameStack = origNameStack[classLocationNS:] self.stack = origStack[classLocationS:] + self.braceHandled = False if self.nameStack: self._evaluate_stack() if self.stack and self.stack[0] == "class": self.stack = [] - self.braceDepth += 1 + if not self.braceHandled: + self.braceDepth += 1 elif tok.type == "}": if self.braceDepth == 0: @@ -2733,10 +2732,8 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): if self.braceDepth == len(self.nameSpaces): tmp = self.nameSpaces.pop() self.stack = [] # clear stack when namespace ends? - elif self.braceDepth < 10: - self._evaluate_stack() else: - self.nameStack = [] + self._evaluate_stack() self.braceDepth -= 1 # self.stack = []; print 'BRACE DEPTH', self.braceDepth, 'NS', len(self.nameSpaces) if self.curClass: @@ -2758,12 +2755,6 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self.curClass = "" self.stack = [] - if self._method_body and (self.braceDepth + 1) <= self._method_body: - self._method_body = None - self.stack = [] - self.nameStack = [] - trace_print("FORCE CLEAR METHBODY") - if tok.type in _namestack_append_tokens: self.nameStack.append(tok.value) elif tok.type in _namestack_pass_tokens: @@ -2834,8 +2825,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self.stack = saved_stack self.anon_union_counter = [-1, 0] - if self.braceDepth < 10: - self._evaluate_stack(tok.type) + self._evaluate_stack(tok.type) self.stack = [] self.nameStack = [] @@ -2878,7 +2868,6 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): "stack", "mainClass", "_template_typenames", - "_method_body", "braceDepth", "typedefs_order", "curTemplate", @@ -2956,6 +2945,20 @@ def _consume_balanced_tokens(self, *init_tokens): if next_end: match_stack.append(next_end) + def _discard_function_contents(self, stack): + # use this instead of consume_balanced_tokens because + # we don't care at all about the internals + level = 1 + get_token = self.lex.token + while True: + tok = get_token() + if tok.type == "{": + level += 1 + elif tok.type == "}": + level -= 1 + if level == 0: + break + def _evaluate_stack(self, token=None): """Evaluates the current name stack""" @@ -3040,13 +3043,7 @@ def _evaluate_stack(self, token=None): atype["raw_type"] = ns + atype["type"] alias = self.current_namespace() + alias self.using[alias] = atype - - elif self._method_body and (self.braceDepth + 1) > self._method_body: - trace_print("INSIDE METHOD DEF") - elif ( - is_method_namestack(self.stack) - and "(" in self.nameStack - ): + elif is_method_namestack(self.stack) and "(" in self.nameStack: debug_print("trace") if self.braceDepth > 0: if ( diff --git a/CppHeaderParser/test/test_CppHeaderParser.py b/CppHeaderParser/test/test_CppHeaderParser.py index 16044c3..82a8a8c 100644 --- a/CppHeaderParser/test/test_CppHeaderParser.py +++ b/CppHeaderParser/test/test_CppHeaderParser.py @@ -3215,5 +3215,29 @@ def test_values(self): ) +class FreeTemplates_TestCase(unittest.TestCase): + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader( + """ + +template +StringRef copy(Allocator &A) const { + // Don't request a length 0 copy from the allocator. + if (empty()) + return StringRef(); + char *S = A.template Allocate(Length); + std::copy(begin(), end(), S); + return StringRef(S, Length); +} + +""", + "string", + ) + + def test_fn(self): + fn = self.cppHeader.functions[0] + self.assertEqual("copy", fn["name"]) + + if __name__ == "__main__": unittest.main() From 7b8870a6fa5db86a4c2de4f36db7ac01cdb026bf Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 22 Dec 2019 22:25:24 -0500 Subject: [PATCH 058/143] Remove __attribute__ scanning since we already do that --- CppHeaderParser/CppHeaderParser.py | 31 ------------------------------ 1 file changed, 31 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 025645c..512e371 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -226,35 +226,6 @@ def set_location_info(thing, location): thing["line_number"] = line_number -def filter_out_attribute_keyword(stack): - """Strips __attribute__ and its parenthetical expression from the stack""" - if "__attribute__" not in stack: - return stack - try: - debug_print("Stripping __attribute__ from %s" % stack) - attr_index = stack.index("__attribute__") - attr_end = ( - attr_index + 1 - ) # Assuming not followed by parenthetical expression which wont happen - # Find final paren - if stack[attr_index + 1] == "(": - paren_count = 1 - for i in range(attr_index + 2, len(stack)): - elm = stack[i] - if elm == "(": - paren_count += 1 - elif elm == ")": - paren_count -= 1 - if paren_count == 0: - attr_end = i + 1 - break - new_stack = stack[0:attr_index] + stack[attr_end:] - debug_print("stripped stack is %s" % new_stack) - return new_stack - except: - return stack - - _nhack = re.compile(r"[A-Za-z_][A-Za-z0-9_]*") @@ -2962,8 +2933,6 @@ def _discard_function_contents(self, stack): def _evaluate_stack(self, token=None): """Evaluates the current name stack""" - self.nameStack = filter_out_attribute_keyword(self.nameStack) - self.stack = filter_out_attribute_keyword(self.stack) nameStackCopy = self.nameStack[:] debug_print( From 465560e3dea5b050aec3092fa2375601c1c1a6da Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 22 Dec 2019 23:21:53 -0500 Subject: [PATCH 059/143] Fix current location to use lookahead buffer --- CppHeaderParser/lexer.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CppHeaderParser/lexer.py b/CppHeaderParser/lexer.py index 154b95d..2c6fd76 100644 --- a/CppHeaderParser/lexer.py +++ b/CppHeaderParser/lexer.py @@ -134,6 +134,8 @@ def __init__(self, filename): self.lookahead = deque() def current_location(self): + if self.lookahead: + return self.lookahead[0].location return self.filename, self.lex.lineno - self.line_offset def get_doxygen(self): @@ -163,6 +165,8 @@ def get_doxygen(self): if tok is None: break + + tok.location = (self.filename, tok.lineno - self.line_offset) ttype = tok.type if ttype == "NEWLINE": self.lookahead.append(tok) @@ -197,6 +201,7 @@ def token(self, eof_ok=False): break if tok.type not in self._discard_types: + tok.location = (self.filename, tok.lineno - self.line_offset) break return tok From 27d9dca5cff4b9fa37f251226d557dd69ea1eab9 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Mon, 23 Dec 2019 21:54:39 -0500 Subject: [PATCH 060/143] Add parser notes --- CppHeaderParser/CppHeaderParser.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 512e371..aa1b442 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -2611,6 +2611,18 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self.lex = lex self.headerFileNames = lex.filenames + # + # A note on parsing methodology + # + # The idea here is to consume as many tokens as needed to determine + # what the thing is that we're parsing. While some items can be identified + # early, typically the code below consumes until a '{', '}', or ; and + # then looks at the accumulated tokens to figure out what it is. + # + # Unfortunately, the code isn't always particularly consistent (but + # it's slowly getting there!), so take this with a grain of salt. + # + tok = None try: while True: @@ -2622,7 +2634,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): and self.anon_union_counter[1] ): self.anon_union_counter[1] -= 1 - tok.value = TagStr(tok.value, location=lex.current_location()) + tok.value = TagStr(tok.value, location=tok.location) # debug_print("TOK: %s"%tok) if tok.type == "NAME": if tok.value in self.IGNORE_NAMES: From 4602c99a697850c7a6d4624d8a7a0194f7b0e3dc Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Mon, 23 Dec 2019 22:28:22 -0500 Subject: [PATCH 061/143] Improve error messages --- CppHeaderParser/CppHeaderParser.py | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index aa1b442..4844581 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -290,7 +290,9 @@ def __new__(cls, *args, **kwargs): class CppParseError(Exception): - pass + def __init__(self, msg, tok=None): + Exception.__init__(self, msg) + self.tok = tok class CppTemplateParam(dict): @@ -2815,15 +2817,21 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): except Exception as e: if debug: raise + context = "" + if isinstance(e, CppParseError): + context = ": " + str(e) + if e.tok: + tok = e.tok + if tok: - filename, lineno = tok.value.location + filename, lineno = tok.location msg = ( - 'Not able to parse %s on line %d evaluating "%s"\nError around: %s' - % (filename, lineno, tok.value, " ".join(self.nameStack)) + "Not able to parse %s on line %d evaluating '%s'%s\nError around: %s" + % (filename, lineno, tok.value, context, " ".join(self.nameStack)) ) else: - msg = "Error parsing %s\nError around: %s" % ( - self.headerFileName, + msg = "Error parsing %s%s\nError around: %s" % ( + self.headerFileName, context, " ".join(self.nameStack), ) @@ -2872,12 +2880,12 @@ def _parse_error(self, tokens, expected): else: errtok = tokens[-1] if expected: - expected = ", expected " + expected + expected = ", expected '" + expected + "'" - msg = "unexpected %s%s" % (errtok.value, expected) + msg = "unexpected '%s'%s" % (errtok.value, expected) # TODO: better error message - return CppParseError(msg) + return CppParseError(msg, errtok) def _next_token_must_be(self, *tokenTypes): tok = self.lex.token() From d9092098387db8671d6fa2637c8190ed1ed7460e Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Mon, 23 Dec 2019 23:22:15 -0500 Subject: [PATCH 062/143] Make debug logging suck mildly less --- CppHeaderParser/CppHeaderParser.py | 98 ++++++++++++++++-------------- 1 file changed, 54 insertions(+), 44 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 4844581..66417ff 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -45,6 +45,8 @@ # http://www.opensource.org/licenses/bsd-license.php # +from __future__ import print_function + from collections import deque import os @@ -69,7 +71,7 @@ # Controls warning_print print_warnings = 1 # Controls debug_print -debug = 0 +debug = 1 if os.environ.get("CPPHEADERPARSER_DEBUG") == "1" else 0 # Controls trace_print debug_trace = 0 @@ -82,25 +84,31 @@ def raise_exc(e, src_e): raise e -def error_print(arg): +def error_print(fmt, *args): if print_errors: - print(("[%4d] %s" % (inspect.currentframe().f_back.f_lineno, arg))) + fmt = "[%4d] " + fmt + args = (inspect.currentframe().f_back.f_lineno,) + args + print(fmt % args) -def warning_print(arg): +def warning_print(fmt, *args): if print_warnings: - print(("[%4d] %s" % (inspect.currentframe().f_back.f_lineno, arg))) + fmt = "[%4d] " + fmt + args = (inspect.currentframe().f_back.f_lineno,) + args + print(fmt % args) -def debug_print(arg): +def debug_print(fmt, *args): if debug: - print(("[%4d] %s" % (inspect.currentframe().f_back.f_lineno, arg))) + fmt = "[%4d] " + fmt + args = (inspect.currentframe().f_back.f_lineno,) + args + print(fmt % args) -def trace_print(*arg): +def trace_print(*args): if debug_trace: sys.stdout.write("[%s] " % (inspect.currentframe().f_back.f_lineno)) - for a in arg: + for a in args: sys.stdout.write("%s " % a) sys.stdout.write("\n") @@ -392,7 +400,7 @@ def _consume_parens(stack): def _parse_template_decl(stack): - debug_print("_parse_template_decl: %s" % stack) + debug_print("_parse_template_decl: %s", stack) params = [] param = CppTemplateParam() i = 0 @@ -492,7 +500,7 @@ def _parse_cppclass_name(c, stack): def _parse_cpp_base(stack): - debug_print("Parsing base: %s" % stack) + debug_print("Parsing base: %s", stack) inherits = [] i = 0 sl = len(stack) @@ -634,8 +642,8 @@ def __init__(self, nameStack, curTemplate, doxygen, location): self._public_forward_declares = [] self["namespace"] = "" - debug_print("Class: %s" % nameStack) - debug_print("Template: %s" % curTemplate) + debug_print("Class: %s", nameStack) + debug_print("Template: %s", curTemplate) if len(nameStack) < 2: nameStack.insert(1, "") # anonymous struct @@ -911,8 +919,8 @@ def show(self): return "\n\t\t ".join(r) def __init__(self, nameStack, curClass, methinfo, curTemplate, doxygen, location): - debug_print("Method: %s" % nameStack) - debug_print("Template: %s" % curTemplate) + debug_print("Method: %s", nameStack) + debug_print("Template: %s", curTemplate) if doxygen: self["doxygen"] = doxygen @@ -994,10 +1002,10 @@ def __init__(self, nameStack, curClass, methinfo, curTemplate, doxygen, location paramsStack = self._params_helper1(nameStack) - debug_print("curTemplate: %s" % curTemplate) + debug_print("curTemplate: %s", curTemplate) if curTemplate: self["template"] = curTemplate - debug_print("SET self['template'] to `%s`" % self["template"]) + debug_print("SET self['template'] to `%s`", self["template"]) params = [] # See if there is a doxygen comment for the variable @@ -1118,7 +1126,7 @@ class CppVariable(_CppVariable): Vars = [] def __init__(self, nameStack, doxygen, location, **kwargs): - debug_print("trace %s" % nameStack) + debug_print("trace %s", nameStack) if len(nameStack) and nameStack[0] == "extern": self["extern"] = True del nameStack[0] @@ -1130,7 +1138,7 @@ def __init__(self, nameStack, doxygen, location, **kwargs): arrayStack = nameStack[nameStack.index("[") :] if nameStack.count("[") > 1: debug_print("Multi dimensional array") - debug_print("arrayStack=%s" % arrayStack) + debug_print("arrayStack=%s", arrayStack) nums = [x for x in arrayStack if x.isdigit()] # Calculate size by multiplying all dimensions p = 1 @@ -1153,7 +1161,7 @@ def __init__(self, nameStack, doxygen, location, **kwargs): if doxygen: self["doxygen"] = doxygen - debug_print("Variable: %s" % nameStack) + debug_print("Variable: %s", nameStack) set_location_info(self, location) self["function_pointer"] = 0 @@ -1163,7 +1171,7 @@ def __init__(self, nameStack, doxygen, location, **kwargs): self["type"] = nameStack[0] self["name"] = "" else: - error_print(_stack_) + error_print("%s", _stack_) assert 0 elif is_function_pointer_stack(nameStack): # function pointer @@ -1596,7 +1604,7 @@ def finalize_vars(self): var["fundamental"] = True elif var["parent"]: - warning_print("WARN unresolved %s" % _tag) + warning_print("WARN unresolved %s", _tag) var["ctypes_type"] = "ctypes.c_void_p" var["unresolved"] = True @@ -1710,7 +1718,7 @@ def finalize_vars(self): elif tag.startswith( "_" ): # assume starting with underscore is not important for wrapping - warning_print("WARN unresolved %s" % _tag) + warning_print("WARN unresolved %s", _tag) var["ctypes_type"] = "ctypes.c_void_p" var["unresolved"] = True @@ -1803,9 +1811,7 @@ def finalize_vars(self): trace_print("Adding #include %s" % macro) self.includes.append(re.split("[\t ]+", macro, 1)[1].strip()) else: - debug_print( - "Cant detect what to do with precomp macro '%s'" % macro - ) + debug_print("Cant detect what to do with precomp macro '%s'", macro) except: pass self._precomp_macro_buf = None @@ -1887,7 +1893,7 @@ def finalize(self): klass = self.classes[b] meth["returns_class"] = a + "::" + b elif "<" in b and ">" in b: - warning_print("WARN-can not return template: %s" % b) + warning_print("WARN-can not return template: %s", b) meth["returns_unknown"] = True elif b in self.global_enums: enum = self.global_enums[b] @@ -2250,8 +2256,9 @@ def _evaluate_property_stack(self, clearStack=True, addToVar=None): ): self.nameStack.insert(0, filteredParseHistory[-1]["item"]["name"]) debug_print( - "DEANONYMOIZING %s to type '%s'" - % (self.nameStack[1], self.nameStack[0]) + "DEANONYMOIZING %s to type '%s'", + self.nameStack[1], + self.nameStack[0], ) if "," in self.nameStack: # Maybe we have a variable list # Figure out what part is the variable separator but remember templates of function pointer @@ -2333,7 +2340,7 @@ def _evaluate_class_stack(self): else: # struct self.curAccessSpecifier = "public" debug_print( - "curAccessSpecifier changed/defaulted to %s" % self.curAccessSpecifier + "curAccessSpecifier changed/defaulted to %s", self.curAccessSpecifier ) if self.nameStack[0] == "union": newClass = CppUnion( @@ -2518,7 +2525,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self.accessSpecifierStack = [] self.accessSpecifierScratch = [] debug_print( - "curAccessSpecifier changed/defaulted to %s" % self.curAccessSpecifier + "curAccessSpecifier changed/defaulted to %s", self.curAccessSpecifier ) self.initextra() # Old namestacks for a given level @@ -2600,7 +2607,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): if locEnd: # Strip it out but keep the linecount the same so line numbers are right match_str = headerFileStr[locStart:locEnd] - debug_print("Striping out '%s'" % match_str) + debug_print("Striping out '%s'", match_str) num_newlines = len([a for a in match_str if a == "\n"]) headerFileStr = headerFileStr.replace( headerFileStr[locStart:locEnd], "\n" * num_newlines @@ -2637,7 +2644,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): ): self.anon_union_counter[1] -= 1 tok.value = TagStr(tok.value, location=tok.location) - # debug_print("TOK: %s"%tok) + # debug_print("TOK: %s", tok) if tok.type == "NAME": if tok.value in self.IGNORE_NAMES: continue @@ -2662,7 +2669,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self.stack.append(tok.value) if tok.type in ("PRECOMP_MACRO", "PRECOMP_MACRO_CONT"): - debug_print("PRECOMP: %s" % tok) + debug_print("PRECOMP: %s", tok) self._precomp_macro_buf.append(tok.value) self.stack = [] self.nameStack = [] @@ -2723,7 +2730,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): # self.stack = []; print 'BRACE DEPTH', self.braceDepth, 'NS', len(self.nameSpaces) if self.curClass: debug_print( - "CURBD %s" % self._classes_brace_level[self.curClass] + "CURBD %s", self._classes_brace_level[self.curClass] ) if (self.braceDepth == 0) or ( @@ -2746,7 +2753,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): pass elif tok.type in _namestack_str_tokens: if tok.value in ignoreSymbols: - debug_print("Ignore symbol %s" % tok.value) + debug_print("Ignore symbol %s", tok.value) elif tok.value == "class": self.nameStack.append(tok.value) elif tok.value in supportedAccessSpecifier: @@ -2764,8 +2771,8 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self.curAccessSpecifier = tok.value self.accessSpecifierScratch.append(tok.value) debug_print( - "curAccessSpecifier updated to %s" - % self.curAccessSpecifier + "curAccessSpecifier updated to %s", + self.curAccessSpecifier, ) self.stack = [] else: @@ -2831,7 +2838,8 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): ) else: msg = "Error parsing %s%s\nError around: %s" % ( - self.headerFileName, context, + self.headerFileName, + context, " ".join(self.nameStack), ) @@ -2956,8 +2964,10 @@ def _evaluate_stack(self, token=None): nameStackCopy = self.nameStack[:] debug_print( - "Evaluating stack %s\n BraceDepth: %s (called from %d)" - % (self.nameStack, self.braceDepth, inspect.currentframe().f_back.f_lineno) + "Evaluating stack %s\n BraceDepth: %s (called from %d)", + self.nameStack, + self.braceDepth, + inspect.currentframe().f_back.f_lineno, ) # Handle special case of overloading operator () @@ -2968,9 +2978,9 @@ def _evaluate_stack(self, token=None): self.nameStack[operator_index] = "operator()" if len(self.curClass): - debug_print("%s (%s) " % (self.curClass, self.curAccessSpecifier)) + debug_print("%s (%s) ", self.curClass, self.curAccessSpecifier) else: - debug_print(" (%s) " % self.curAccessSpecifier) + debug_print(" (%s) ", self.curAccessSpecifier) # Filter special case of array with casting in it try: @@ -2981,7 +2991,7 @@ def _evaluate_stack(self, token=None): self.nameStack = ( self.nameStack[: bracePos + 1] + self.nameStack[endParen + 1 :] ) - debug_print("Filtered namestack to=%s" % self.nameStack) + debug_print("Filtered namestack to=%s", self.nameStack) except: pass From d2dd07520128705382478ef44ff876e9d438fce9 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Mon, 23 Dec 2019 23:04:29 -0500 Subject: [PATCH 063/143] Simplify access specifier parsing --- CppHeaderParser/CppHeaderParser.py | 43 +++++++----------------------- 1 file changed, 9 insertions(+), 34 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 66417ff..cf81d08 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -2523,7 +2523,6 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self.curAccessSpecifier = "private" # private is default self.curTemplate = None self.accessSpecifierStack = [] - self.accessSpecifierScratch = [] debug_print( "curAccessSpecifier changed/defaulted to %s", self.curAccessSpecifier ) @@ -2747,7 +2746,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self.curClass = "" self.stack = [] - if tok.type in _namestack_append_tokens: + elif tok.type in _namestack_append_tokens: self.nameStack.append(tok.value) elif tok.type in _namestack_pass_tokens: pass @@ -2756,47 +2755,24 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): debug_print("Ignore symbol %s", tok.value) elif tok.value == "class": self.nameStack.append(tok.value) - elif tok.value in supportedAccessSpecifier: - if len(self.nameStack) and self.nameStack[0] in ( - "class", - "struct", - "union", - ): - self.nameStack.append(tok.value) - elif self.braceDepth == len( - self.nameSpaces - ) + 1 or self.braceDepth == ( - len(self.nameSpaces) + len(self.curClass.split("::")) - ): - self.curAccessSpecifier = tok.value - self.accessSpecifierScratch.append(tok.value) - debug_print( - "curAccessSpecifier updated to %s", - self.curAccessSpecifier, - ) - self.stack = [] else: self.nameStack.append(tok.value) if self.anon_union_counter[0] == self.braceDepth: self.anon_union_counter = [-1, 0] elif tok.type == ":": - # Dont want colon to be first in stack - if len(self.nameStack) == 0: - self.accessSpecifierScratch = [] - continue - - # Handle situation where access specifiers can be multi words such as "public slots" - jns = " ".join(self.accessSpecifierScratch + self.nameStack) - if jns in supportedAccessSpecifier: - self.curAccessSpecifier = jns + if self.nameStack and self.nameStack[0] in supportedAccessSpecifier: + specifier = " ".join(self.nameStack) + if specifier in supportedAccessSpecifier: + self.curAccessSpecifier = specifier + else: + self.curAccessSpecifier = self.nameStack[0] debug_print( - "curAccessSpecifier updated to %s" % self.curAccessSpecifier + "curAccessSpecifier updated to %s", self.curAccessSpecifier ) - self.stack = [] self.nameStack = [] + self.stack = [] else: self.nameStack.append(tok.value) - self.accessSpecifierScratch = [] elif tok.type == ";": if ( @@ -2858,7 +2834,6 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): "nameSpaces", "curAccessSpecifier", "accessSpecifierStack", - "accessSpecifierScratch", "nameStackHistory", "anon_struct_counter", "anon_union_counter", From 4260f06fec8eade5c66603a4a83c68cae97eead6 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Tue, 24 Dec 2019 01:41:27 -0500 Subject: [PATCH 064/143] Handle doxygen comments for elements separated with a newline --- CppHeaderParser/CppHeaderParser.py | 38 ++++++++++---- CppHeaderParser/lexer.py | 4 +- CppHeaderParser/test/test_CppHeaderParser.py | 55 ++++++++++++++++++++ 3 files changed, 85 insertions(+), 12 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index cf81d08..f05683b 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -2127,7 +2127,7 @@ def _evaluate_method_stack(self): info["name"], info, self.curTemplate, - self.lex.get_doxygen(), + self._get_stmt_doxygen(), self._get_location(self.nameStack), ) klass = self.classes[info["class"]] @@ -2144,7 +2144,7 @@ def _evaluate_method_stack(self): self.curClass, info, self.curTemplate, - self.lex.get_doxygen(), + self._get_stmt_doxygen(), self._get_location(self.nameStack), ) klass = self.classes[self.curClass] @@ -2161,7 +2161,7 @@ def _evaluate_method_stack(self): None, info, self.curTemplate, - self.lex.get_doxygen(), + self._get_stmt_doxygen(), self._get_location(self.nameStack), ) self.functions.append(newMethod) @@ -2288,7 +2288,7 @@ def _evaluate_property_stack(self, clearStack=True, addToVar=None): newVar = CppVariable( self.nameStack, - self.lex.get_doxygen(), + self._get_stmt_doxygen(), self._get_location(self.nameStack), ) newVar["namespace"] = self.current_namespace() @@ -2305,7 +2305,7 @@ def _evaluate_property_stack(self, clearStack=True, addToVar=None): debug_print("Found Global variable") newVar = CppVariable( self.nameStack, - self.lex.get_doxygen(), + self._get_stmt_doxygen(), self._get_location(self.nameStack), ) if addToVar: @@ -2345,7 +2345,7 @@ def _evaluate_class_stack(self): if self.nameStack[0] == "union": newClass = CppUnion( self.nameStack, - self.lex.get_doxygen(), + self._get_stmt_doxygen(), self._get_location(self.nameStack), ) if newClass["name"] == "union ": @@ -2357,7 +2357,7 @@ def _evaluate_class_stack(self): newClass = CppClass( self.nameStack, self.curTemplate, - self.lex.get_doxygen(), + self._get_stmt_doxygen(), self._get_location(self.nameStack), ) trace_print("NEW CLASS", newClass["name"]) @@ -2631,6 +2631,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): # it's slowly getting there!), so take this with a grain of salt. # + self._doxygen_cache = None tok = None try: while True: @@ -2648,6 +2649,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): if tok.value in self.IGNORE_NAMES: continue elif tok.value == "template": + self._doxygen_cache = self.lex.get_doxygen() self._parse_template() continue elif tok.value == "alignas": @@ -2666,6 +2668,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): continue self.stack.append(tok.value) + nslen = len(self.nameStack) if tok.type in ("PRECOMP_MACRO", "PRECOMP_MACRO_CONT"): debug_print("PRECOMP: %s", tok) @@ -2748,6 +2751,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): elif tok.type in _namestack_append_tokens: self.nameStack.append(tok.value) + nameStackAppended = True elif tok.type in _namestack_pass_tokens: pass elif tok.type in _namestack_str_tokens: @@ -2797,6 +2801,11 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self.stack = [] self.nameStack = [] + newNsLen = len(self.nameStack) + if nslen != newNsLen and newNsLen == 1: + if not self.curTemplate: + self._doxygen_cache = self.lex.get_doxygen() + except Exception as e: if debug: raise @@ -2829,6 +2838,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): # Delete some temporary variables for key in [ "_precomp_macro_buf", + "_doxygen_cache", "lex", "nameStack", "nameSpaces", @@ -2856,6 +2866,14 @@ def _get_location(self, stack): return self.lex.current_location() + def _get_stmt_doxygen(self): + # retrieves the doxygen comment associated with an accumulated + # statement (since doxygen comments have to be retrieved immediately) + doxygen, self._doxygen_cache = self._doxygen_cache, "" + if not doxygen: + doxygen = self.lex.get_doxygen() + return doxygen + def _parse_error(self, tokens, expected): if not tokens: # common case after a failed token_if @@ -3003,13 +3021,13 @@ def _evaluate_stack(self, token=None): alias = self.nameStack[1] ns, stack = _split_namespace(self.nameStack[3:]) atype = CppVariable( - stack, self.lex.get_doxygen(), self._get_location(stack) + stack, self._get_stmt_doxygen(), self._get_location(stack) ) else: # using foo::bar ns, stack = _split_namespace(self.nameStack[1:]) atype = CppVariable( - stack, self.lex.get_doxygen(), self._get_location(stack) + stack, self._get_stmt_doxygen(), self._get_location(stack) ) alias = atype["type"] @@ -3158,7 +3176,7 @@ def _parse_enum(self): """ # entry: enum token was just consumed - doxygen = self.lex.get_doxygen() + doxygen = self._get_stmt_doxygen() location = self.lex.current_location() nametok = self.lex.token() diff --git a/CppHeaderParser/lexer.py b/CppHeaderParser/lexer.py index 2c6fd76..cb3f76a 100644 --- a/CppHeaderParser/lexer.py +++ b/CppHeaderParser/lexer.py @@ -147,8 +147,8 @@ def get_doxygen(self): comments don't exist. """ - # assuption: only time you call this function is after a name - # token is consumed along with all its pieces + # Assumption: This function is either called at the beginning of a + # statement or at the end of a statement if self.comments: comments = self.comments diff --git a/CppHeaderParser/test/test_CppHeaderParser.py b/CppHeaderParser/test/test_CppHeaderParser.py index 82a8a8c..c631adb 100644 --- a/CppHeaderParser/test/test_CppHeaderParser.py +++ b/CppHeaderParser/test/test_CppHeaderParser.py @@ -3239,5 +3239,60 @@ def test_fn(self): self.assertEqual("copy", fn["name"]) +class MessedUpDoxygen_TestCase(unittest.TestCase): + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader( + """ + +/// fn comment +void +fn(); + +/// var comment +int +v1 = 0; + +int +v2 = 0; /// var2 comment + +/// cls comment +class +C {}; + +/// template comment +template +class +C2 {}; + +""", + "string", + ) + + def test_fn(self): + fn = self.cppHeader.functions[0] + self.assertEqual("fn", fn["name"]) + self.assertEqual("/// fn comment", fn["doxygen"]) + + def test_var1(self): + v = self.cppHeader.variables[0] + self.assertEqual("v1", v["name"]) + self.assertEqual("/// var comment", v["doxygen"]) + + def test_var2(self): + v = self.cppHeader.variables[1] + self.assertEqual("v2", v["name"]) + self.assertEqual("/// var2 comment", v["doxygen"]) + + def test_cls(self): + c = self.cppHeader.classes["C"] + self.assertEqual("C", c["name"]) + self.assertEqual("/// cls comment", c["doxygen"]) + + def test_cls2(self): + c = self.cppHeader.classes["C2"] + self.assertEqual("C2", c["name"]) + self.assertEqual("/// template comment", c["doxygen"]) + + if __name__ == "__main__": unittest.main() From 0030ea4a56d3d849261c39df337d00e4c6bc0872 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Tue, 24 Dec 2019 02:12:00 -0500 Subject: [PATCH 065/143] foo(void) should be recorded as having zero parameters --- CppHeaderParser/CppHeaderParser.py | 4 ++++ CppHeaderParser/test/test_CppHeaderParser.py | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index f05683b..b453083 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -1062,6 +1062,10 @@ def __init__(self, nameStack, curClass, methinfo, curTemplate, doxygen, location params.append(param) break + # foo(void) should be zero parameters + if len(params) == 1 and params[0]["type"] == "void": + params = [] + self["parameters"] = params self._params_helper2(params) # mods params inplace diff --git a/CppHeaderParser/test/test_CppHeaderParser.py b/CppHeaderParser/test/test_CppHeaderParser.py index c631adb..ec9f556 100644 --- a/CppHeaderParser/test/test_CppHeaderParser.py +++ b/CppHeaderParser/test/test_CppHeaderParser.py @@ -1908,7 +1908,9 @@ def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_termite_function(self): - self.assertEqual(self.cppHeader.functions[5]["name"], "termite") + f = self.cppHeader.functions[5] + self.assertEqual(f["name"], "termite") + self.assertEqual(len(f["parameters"]), 0) # Bug: 3569622 From e32ecadd4a2537cd661ec445f51b8b7149f04d0f Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Tue, 24 Dec 2019 02:23:35 -0500 Subject: [PATCH 066/143] Fix enum parsing with methods that have return values or parameters --- CppHeaderParser/CppHeaderParser.py | 71 ++++++++++++++++---- CppHeaderParser/lexer.py | 3 + CppHeaderParser/test/test_CppHeaderParser.py | 55 +++++++++++++++ 3 files changed, 116 insertions(+), 13 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index b453083..05c075e 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -227,6 +227,17 @@ def is_property_namestack(nameStack): return r +def is_enum_namestack(nameStack): + """Determines if a namestack is an enum namestack""" + if not nameStack: + return False + if nameStack[0] == "enum": + return True + if len(nameStack) > 1 and nameStack[0] == "typedef" and nameStack[1] == "enum": + return True + return False + + def set_location_info(thing, location): filename, line_number = location if filename: @@ -1598,10 +1609,11 @@ def finalize_vars(self): elif tag in self.global_enums: enum = self.global_enums[tag] - if enum["type"] is int: + enum_type = enum.get("type") + if enum_type is int: var["ctypes_type"] = "ctypes.c_int" var["raw_type"] = "int" - elif enum["type"] is str: + elif enum_type is str: var["ctypes_type"] = "ctypes.c_char_p" var["raw_type"] = "char*" var["enum"] = enum["namespace"] + enum["name"] @@ -2181,6 +2193,7 @@ def _evaluate_method_stack(self): trace_print("free function?", self.nameStack) self.stack = [] + self.stmtTokens = [] def _parse_typedef(self, stack, namespace=""): if not stack or "typedef" not in stack: @@ -2318,6 +2331,7 @@ def _evaluate_property_stack(self, clearStack=True, addToVar=None): if clearStack: self.stack = [] # CLEAR STACK + self.stmtTokens = [] def _evaluate_class_stack(self): """Create a Class out of the name stack (but not its parts)""" @@ -2368,6 +2382,7 @@ def _evaluate_class_stack(self): newClass["declaration_method"] = self.nameStack[0] self.classes_order.append(newClass) # good idea to save ordering self.stack = [] # fixes if class declared with ';' in closing brace + self.stmtTokens = [] classKey = newClass["name"] if parent: @@ -2637,6 +2652,8 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self._doxygen_cache = None tok = None + self.stmtTokens = [] + try: while True: tok = lex.token(eof_ok=True) @@ -2659,11 +2676,6 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): elif tok.value == "alignas": self._parse_attribute_specifier_seq(tok) continue - elif tok.value == "enum": - self._parse_enum() - self.stack = [] - self.nameStack = [] - continue elif tok.value == "__attribute__": self._parse_gcc_attribute() continue @@ -2671,15 +2683,20 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self._parse_attribute_specifier_seq(tok) continue + # TODO: get rid of stack, move to stmtTokens self.stack.append(tok.value) + self.stmtTokens.append(tok) + nslen = len(self.nameStack) if tok.type in ("PRECOMP_MACRO", "PRECOMP_MACRO_CONT"): debug_print("PRECOMP: %s", tok) self._precomp_macro_buf.append(tok.value) self.stack = [] + self.stmtTokens = [] self.nameStack = [] continue + if tok.type == "{": if len(self.nameStack) >= 2 and is_namespace( self.nameStack @@ -2692,6 +2709,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self.nameSpaces.append(self.nameStack[1]) ns = self.cur_namespace() self.stack = [] + self.stmtTokens = [] if ns not in self.namespaces: self.namespaces.append(ns) # Detect special condition of macro magic before class declaration so we @@ -2716,11 +2734,14 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self.nameStack = origNameStack[classLocationNS:] self.stack = origStack[classLocationS:] + # If set to True, indicates that the callee consumed + # all of the tokens between { and } self.braceHandled = False if self.nameStack: self._evaluate_stack() if self.stack and self.stack[0] == "class": self.stack = [] + self.stmtTokens = [] if not self.braceHandled: self.braceDepth += 1 @@ -2730,6 +2751,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): if self.braceDepth == len(self.nameSpaces): tmp = self.nameSpaces.pop() self.stack = [] # clear stack when namespace ends? + self.stmtTokens = [] else: self._evaluate_stack() self.braceDepth -= 1 @@ -2752,6 +2774,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): else: self.curClass = "" self.stack = [] + self.stmtTokens = [] elif tok.type in _namestack_append_tokens: self.nameStack.append(tok.value) @@ -2779,6 +2802,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): ) self.nameStack = [] self.stack = [] + self.stmtTokens = [] else: self.nameStack.append(tok.value) @@ -2804,6 +2828,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self._evaluate_stack(tok.type) self.stack = [] self.nameStack = [] + self.stmtTokens = [] newNsLen = len(self.nameStack) if nslen != newNsLen and newNsLen == 1: @@ -2997,7 +3022,11 @@ def _evaluate_stack(self, token=None): not self.curClass and "typedef" in self.nameStack and ( - ("struct" not in self.nameStack and "union" not in self.nameStack) + ( + "struct" not in self.nameStack + and "union" not in self.nameStack + and "enum" not in self.nameStack + ) or self.stack[-1] == ";" ) ): @@ -3055,6 +3084,12 @@ def _evaluate_stack(self, token=None): else: # Free function self._evaluate_method_stack() + elif is_enum_namestack(self.nameStack): + debug_print("trace") + self._parse_enum() + self.nameStack = [] + self.stack = [] + self.stmtTokens = [] elif ( len(self.nameStack) == 1 and len(self.nameStackHistory) > self.braceDepth @@ -3178,10 +3213,20 @@ def _parse_enum(self): enum_base: ":" type_specifier_seq """ + is_typedef = False + self.lex.return_tokens(self.stmtTokens) - # entry: enum token was just consumed doxygen = self._get_stmt_doxygen() - location = self.lex.current_location() + + tok = self.lex.token() + if tok.value == "typedef": + is_typedef = True + tok = self.lex.token() + + if tok.value != "enum": + raise self._parse_error((tok,), "enum") + + location = tok.location nametok = self.lex.token() if nametok.value in ("class", "struct"): @@ -3212,15 +3257,15 @@ def _parse_enum(self): base.append(tok.value) newEnum = CppEnum(name, doxygen, location) - if self.nameStack: - if self.nameStack[0] == "typedef": - newEnum["typedef"] = True + if is_typedef: + newEnum["typedef"] = True if base: newEnum["type"] = "".join(base) instancesData = [] if tok.type == "{": + self.braceHandled = True self._parse_enumerator_list(newEnum["values"]) newEnum.resolve_enum_values(newEnum["values"]) tok = self.lex.token() diff --git a/CppHeaderParser/lexer.py b/CppHeaderParser/lexer.py index cb3f76a..1cd411e 100644 --- a/CppHeaderParser/lexer.py +++ b/CppHeaderParser/lexer.py @@ -220,6 +220,9 @@ def token_if(self, *types): def return_token(self, tok): self.lookahead.appendleft(tok) + def return_tokens(self, toks): + self.lookahead.extendleft(reversed(toks)) + if __name__ == "__main__": try: diff --git a/CppHeaderParser/test/test_CppHeaderParser.py b/CppHeaderParser/test/test_CppHeaderParser.py index ec9f556..49d58d4 100644 --- a/CppHeaderParser/test/test_CppHeaderParser.py +++ b/CppHeaderParser/test/test_CppHeaderParser.py @@ -3296,5 +3296,60 @@ def test_cls2(self): self.assertEqual("/// template comment", c["doxygen"]) +class EnumParameter_TestCase(unittest.TestCase): + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader( + """ +enum E { + VALUE, +}; + +void fn_with_enum_param1(const enum E e); + +void fn_with_enum_param2(const enum E e) { + // code here +} + +enum E fn_with_enum_retval1(void); + +enum E fn_with_enum_retval2(void) { + // code here +} + +""", + "string", + ) + + def test_enum_param(self): + fn = self.cppHeader.functions[0] + self.assertEqual("fn_with_enum_param1", fn["name"]) + self.assertEqual(1, len(fn["parameters"])) + + p1 = fn["parameters"][0] + self.assertEqual("e", p1["name"]) + self.assertEqual("const enum E", p1["type"]) + self.assertEqual("int", p1["raw_type"]) + + fn = self.cppHeader.functions[1] + self.assertEqual("fn_with_enum_param2", fn["name"]) + self.assertEqual(1, len(fn["parameters"])) + + p1 = fn["parameters"][0] + self.assertEqual("e", p1["name"]) + self.assertEqual("const enum E", p1["type"]) + self.assertEqual("int", p1["raw_type"]) + + def test_enum_retval(self): + fn = self.cppHeader.functions[2] + self.assertEqual("fn_with_enum_retval1", fn["name"]) + self.assertEqual(0, len(fn["parameters"])) + self.assertEqual("enum E", fn["rtnType"]) + + fn = self.cppHeader.functions[3] + self.assertEqual("fn_with_enum_retval2", fn["name"]) + self.assertEqual(0, len(fn["parameters"])) + self.assertEqual("enum E", fn["rtnType"]) + + if __name__ == "__main__": unittest.main() From 27682a154b7d1dbb224add0f9fc560193dd86e51 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Tue, 24 Dec 2019 02:24:31 -0500 Subject: [PATCH 067/143] Add additional debug_prints --- CppHeaderParser/CppHeaderParser.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 05c075e..77e5336 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -501,6 +501,7 @@ def _parse_cppclass_name(c, stack): name = "<" + name + ">" c["name"] = name c["bare_name"] = name + debug_print("Found class '%s'", name) # backwards compat classParams = c.get("class_params") @@ -1909,7 +1910,6 @@ def finalize(self): klass = self.classes[b] meth["returns_class"] = a + "::" + b elif "<" in b and ">" in b: - warning_print("WARN-can not return template: %s", b) meth["returns_unknown"] = True elif b in self.global_enums: enum = self.global_enums[b] @@ -2665,6 +2665,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): ): self.anon_union_counter[1] -= 1 tok.value = TagStr(tok.value, location=tok.location) + # debug_print("TOK: %s", tok) if tok.type == "NAME": if tok.value in self.IGNORE_NAMES: @@ -2755,7 +2756,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): else: self._evaluate_stack() self.braceDepth -= 1 - # self.stack = []; print 'BRACE DEPTH', self.braceDepth, 'NS', len(self.nameSpaces) + if self.curClass: debug_print( "CURBD %s", self._classes_brace_level[self.curClass] @@ -3030,13 +3031,13 @@ def _evaluate_stack(self, token=None): or self.stack[-1] == ";" ) ): - trace_print("STACK", self.stack) + debug_print("trace") + trace_print("typedef %s", self.stack) self._evaluate_typedef() return elif len(self.nameStack) == 0: - debug_print("trace") - debug_print("(Empty Stack)") + debug_print("trace (Empty Stack)") return elif self.nameStack[0] == "namespace": # Taken care of outside of here @@ -3140,6 +3141,8 @@ def _evaluate_stack(self, token=None): elif self.braceDepth > len(self.nameSpaces) + 1: debug_print("trace") self.nameStack = [] + else: + debug_print("Discarded statement %s" % (self.nameStack,)) try: self.nameStackHistory[self.braceDepth] = (nameStackCopy, self.curClass) @@ -3213,6 +3216,8 @@ def _parse_enum(self): enum_base: ":" type_specifier_seq """ + debug_print("parsing enum") + is_typedef = False self.lex.return_tokens(self.stmtTokens) @@ -3244,8 +3249,10 @@ def _parse_enum(self): name = "" if nametok.type == "NAME": name = nametok.value + debug_print("enum name is '%s'", name) tok = self.lex.token() else: + debug_print("anonymous enum") tok = nametok base = [] @@ -3336,6 +3343,8 @@ def _parse_enumerator_list(self, values): value["doxygen"] = doxygen values.append(value) + debug_print("enumerator value '%s'", value["name"]) + tok = self._next_token_must_be("}", ",", "=", "DBL_LBRACKET") if tok.type == "DBL_LBRACKET": self._parse_attribute_specifier_seq(tok) From 4a882ef73418328cd495df5e03216decc127c1b0 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Tue, 24 Dec 2019 02:40:14 -0500 Subject: [PATCH 068/143] Discard static_assert statements --- CppHeaderParser/CppHeaderParser.py | 13 +++++++++---- CppHeaderParser/test/test_CppHeaderParser.py | 15 +++++++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 77e5336..0b830b7 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -2018,7 +2018,7 @@ def parse_method_type(self, stack): if stack[-1] == "{": info["defined"] = True - self._discard_function_contents(stack) + self._discard_contents("{", "}") self.braceHandled = True elif stack[-1] == ";": info["defined"] = False @@ -2680,6 +2680,11 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): elif tok.value == "__attribute__": self._parse_gcc_attribute() continue + elif not self.stack and tok.value == "static_assert": + self._next_token_must_be("(") + self._discard_contents("(", ")") + continue + elif tok.type == "DBL_LBRACKET": self._parse_attribute_specifier_seq(tok) continue @@ -2967,16 +2972,16 @@ def _consume_balanced_tokens(self, *init_tokens): if next_end: match_stack.append(next_end) - def _discard_function_contents(self, stack): + def _discard_contents(self, start_type, end_type): # use this instead of consume_balanced_tokens because # we don't care at all about the internals level = 1 get_token = self.lex.token while True: tok = get_token() - if tok.type == "{": + if tok.type == start_type: level += 1 - elif tok.type == "}": + elif tok.type == end_type: level -= 1 if level == 0: break diff --git a/CppHeaderParser/test/test_CppHeaderParser.py b/CppHeaderParser/test/test_CppHeaderParser.py index 49d58d4..6cfc5a9 100644 --- a/CppHeaderParser/test/test_CppHeaderParser.py +++ b/CppHeaderParser/test/test_CppHeaderParser.py @@ -3351,5 +3351,20 @@ def test_enum_retval(self): self.assertEqual("enum E", fn["rtnType"]) +class StaticAssert_TestCase(unittest.TestCase): + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader( + """ +static_assert(sizeof(int) == 4, + "integer size is wrong" + "for some reason"); +""", + "string", + ) + + def test_nothing(self): + self.assertEqual(self.cppHeader.functions, []) + + if __name__ == "__main__": unittest.main() From 212ae62dd14c0e2f9ba94b19070ffe5e03901784 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Tue, 24 Dec 2019 22:17:20 -0500 Subject: [PATCH 069/143] Introduce :: lexical token, should make parsing easier --- CppHeaderParser/CppHeaderParser.py | 31 +++++++++++++++--------------- CppHeaderParser/lexer.py | 2 ++ 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 0b830b7..89f195a 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -255,11 +255,13 @@ def _split_namespace(namestack): :rtype: Tuple[str, list] """ + # TODO: this should be using tokens instead of nhack + last_colon = None for i, n in enumerate(namestack): - if n == ":": + if n == "::": last_colon = i - if i and n != ":" and not _nhack.match(n): + if i and n != "::" and not _nhack.match(n): break if last_colon: @@ -472,12 +474,8 @@ def _parse_cppclass_name(c, stack): if t == ":": if i >= sl: raise CppParseError("class decl ended with ':'") - t = stack[i] - if t != ":": - # reached the base declaration - break - - i += 1 + break + elif t == "::": name += "::" continue elif t == "final": @@ -954,7 +952,7 @@ def __init__(self, nameStack, curClass, methinfo, curTemplate, doxygen, location if len(self["rtnType"]) == 0 or self["name"] == curClass: self["rtnType"] = "void" - self["rtnType"] = self["rtnType"].replace(" : : ", "::") + self["rtnType"] = self["rtnType"].replace(" :: ", "::") self["rtnType"] = self["rtnType"].replace(" < ", "<") self["rtnType"] = self["rtnType"].replace(" > ", "> ").replace(">>", "> >") self["rtnType"] = self["rtnType"].replace(" ,", ",") @@ -1959,8 +1957,8 @@ def finalize(self): ) meth["returns_unknown"] = True - if meth["returns"].startswith(": : "): - meth["returns"] = meth["returns"].replace(": : ", "::") + if meth["returns"].startswith(":: "): + meth["returns"] = meth["returns"].replace(":: ", "::") for cls in list(self.classes.values()): methnames = cls.get_all_method_names() @@ -1996,7 +1994,7 @@ def parse_method_type(self, stack): stack = stack[1:] info = { "debug": " ".join(stack) - .replace(" : : ", "::") + .replace(" :: ", "::") .replace(" < ", "<") .replace(" > ", "> ") .replace(" >", ">") @@ -2010,7 +2008,7 @@ def parse_method_type(self, stack): header = stack[: stack.index("(")] header = " ".join(header) - header = header.replace(" : : ", "::") + header = header.replace(" :: ", "::") header = header.replace(" < ", "<") header = header.replace(" > ", "> ") header = header.replace("default ", "default") @@ -2452,6 +2450,7 @@ def evalute_forward_decl(self): "+", "STRING_LITERAL", "ELLIPSIS", + "DBL_COLON", "SHIFT_LEFT", } @@ -3164,7 +3163,7 @@ def _parse_template(self): consumed = self._consume_balanced_tokens(tok) tmpl = " ".join(tok.value for tok in consumed) tmpl = ( - tmpl.replace(" : : ", "::") + tmpl.replace(" :: ", "::") .replace(" <", "<") .replace("< ", "<") .replace(" >", ">") @@ -3364,10 +3363,10 @@ def _parse_enumerator_list(self, values): while True: tok = self.lex.token() if tok.type == "}": - value["value"] = (" ".join(v)).replace(": :", "::") + value["value"] = " ".join(v) return elif tok.type == ",": - value["value"] = (" ".join(v)).replace(": :", "::") + value["value"] = " ".join(v) break elif tok.type in self._balanced_token_map: v.extend(t.value for t in self._consume_balanced_tokens(tok)) diff --git a/CppHeaderParser/lexer.py b/CppHeaderParser/lexer.py index 1cd411e..4531ab2 100644 --- a/CppHeaderParser/lexer.py +++ b/CppHeaderParser/lexer.py @@ -22,6 +22,7 @@ class Lexer(object): "ELLIPSIS", "DBL_LBRACKET", "DBL_RBRACKET", + "DBL_COLON", "SHIFT_LEFT", ] @@ -85,6 +86,7 @@ def t_COMMENT_SINGLELINE(self, t): t_ELLIPSIS = r"\.\.\." t_DBL_LBRACKET = r"\[\[" t_DBL_RBRACKET = r"\]\]" + t_DBL_COLON = r"::" t_SHIFT_LEFT = r"<<" # SHIFT_RIGHT introduces ambiguity From 9607663d3015001fd06df4c1fd0b0e7a008079fe Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Tue, 24 Dec 2019 22:35:07 -0500 Subject: [PATCH 070/143] Remove function parsing special case that's no longer needed --- CppHeaderParser/CppHeaderParser.py | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 89f195a..295c388 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -3075,20 +3075,7 @@ def _evaluate_stack(self, token=None): self.using[alias] = atype elif is_method_namestack(self.stack) and "(" in self.nameStack: debug_print("trace") - if self.braceDepth > 0: - if ( - "{" in self.stack - and self.stack[0] != "{" - and self.stack[-1] == ";" - and self.braceDepth == 1 - ): - # Special case of a method defined outside a class that has a body - pass - else: - self._evaluate_method_stack() - else: - # Free function - self._evaluate_method_stack() + self._evaluate_method_stack() elif is_enum_namestack(self.nameStack): debug_print("trace") self._parse_enum() From 33b7958576e916b939daef80ed634d6fa32f9404 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Tue, 24 Dec 2019 22:44:50 -0500 Subject: [PATCH 071/143] Remove more mess --- CppHeaderParser/CppHeaderParser.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 295c388..95b192b 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -3079,7 +3079,6 @@ def _evaluate_stack(self, token=None): elif is_enum_namestack(self.nameStack): debug_print("trace") self._parse_enum() - self.nameStack = [] self.stack = [] self.stmtTokens = [] elif ( @@ -3123,15 +3122,12 @@ def _evaluate_stack(self, token=None): elif not self.curClass: debug_print("trace") - self.nameStack = [] elif self.braceDepth < 1: debug_print("trace") # Ignore global stuff for now debug_print("Global stuff: %s" % self.nameStack) - self.nameStack = [] elif self.braceDepth > len(self.nameSpaces) + 1: debug_print("trace") - self.nameStack = [] else: debug_print("Discarded statement %s" % (self.nameStack,)) @@ -3139,9 +3135,9 @@ def _evaluate_stack(self, token=None): self.nameStackHistory[self.braceDepth] = (nameStackCopy, self.curClass) except: self.nameStackHistory.append((nameStackCopy, self.curClass)) - self.nameStack = ( - [] - ) # its a little confusing to have some if/else above return and others not, and then clearning the nameStack down here + + # its a little confusing to have some if/else above return and others not, and then clearning the nameStack down here + self.nameStack = [] self.lex.doxygenCommentCache = "" self.curTemplate = None From 9b354bdcff58623e2196e6851408a026317ff0aa Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Tue, 24 Dec 2019 22:51:09 -0500 Subject: [PATCH 072/143] Remove unneeded method parsing special case --- CppHeaderParser/CppHeaderParser.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 95b192b..53c589e 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -1990,8 +1990,6 @@ def finalize(self): def parse_method_type(self, stack): trace_print("meth type info", stack) - if stack[0] in ":;" and stack[1] != ":": - stack = stack[1:] info = { "debug": " ".join(stack) .replace(" :: ", "::") From 67a5956f4251141be30ce52d4639aed3fac0fadf Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Tue, 24 Dec 2019 23:46:40 -0500 Subject: [PATCH 073/143] More robust ctor initializer discard code --- CppHeaderParser/CppHeaderParser.py | 87 +++++++++++++++----- CppHeaderParser/test/test_CppHeaderParser.py | 53 ++++++++++++ 2 files changed, 121 insertions(+), 19 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 53c589e..e940dd2 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -193,7 +193,9 @@ def is_method_namestack(stack): elif "{" in stack and stack.index("{") < stack.index("("): r = False # struct that looks like a method/class elif "(" in stack and ")" in stack: - if "{" in stack and "}" in stack: + if stack[-1] == ":": + r = True + elif "{" in stack and "}" in stack: r = True elif stack[-1] == ";": if is_function_pointer_stack(stack): @@ -994,22 +996,6 @@ def __init__(self, nameStack, curClass, methinfo, curTemplate, doxygen, location self.update(methinfo) set_location_info(self, location) - # Filter out initializer lists used in constructors - try: - paren_depth_counter = 0 - for i in range(0, len(nameStack)): - elm = nameStack[i] - if elm == "(": - paren_depth_counter += 1 - if elm == ")": - paren_depth_counter -= 1 - if paren_depth_counter == 0 and nameStack[i + 1] == ":": - debug_print("Stripping out initializer list") - nameStack = nameStack[: i + 1] - break - except: - pass - paramsStack = self._params_helper1(nameStack) debug_print("curTemplate: %s", curTemplate) @@ -2018,6 +2004,10 @@ def parse_method_type(self, stack): self.braceHandled = True elif stack[-1] == ";": info["defined"] = False + elif stack[-1] == ":": + info["defined"] = True + self._discard_ctor_initializer() + self.braceHandled = True else: assert 0 @@ -2806,6 +2796,12 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self.nameStack = [] self.stack = [] self.stmtTokens = [] + elif is_method_namestack(self.stack): + debug_print("trace") + self._evaluate_method_stack() + self.nameStack = [] + self.stack = [] + self.stmtTokens = [] else: self.nameStack.append(tok.value) @@ -2923,7 +2919,7 @@ def _parse_error(self, tokens, expected): def _next_token_must_be(self, *tokenTypes): tok = self.lex.token() if tok.type not in tokenTypes: - raise self._parse_error((tok,), " or ".join(tokenTypes)) + raise self._parse_error((tok,), "' or '".join(tokenTypes)) return tok _end_balanced_tokens = {">", "}", "]", ")", "DBL_RBRACKET"} @@ -2983,6 +2979,59 @@ def _discard_contents(self, start_type, end_type): if level == 0: break + def _discard_ctor_initializer(self): + """ + ctor_initializer: ":" mem_initializer_list + + mem_initializer_list: mem_initializer ["..."] + | mem_initializer "," mem_initializer_list ["..."] + + mem_initializer: mem_initializer_id "(" [expression_list] ")" + | mem_initializer_id braced_init_list + + mem_initializer_id: class_or_decltype + | IDENTIFIER + """ + debug_print("discarding ctor intializer") + # all of this is discarded.. the challenge is to determine + # when the initializer ends and the function starts + while True: + tok = self.lex.token() + if tok.type == "DBL_COLON": + tok = self.lex.token() + + if tok.type == "decltype": + tok = self._next_token_must_be("(") + self._consume_balanced_tokens(tok) + tok = self.lex.token() + + # each initializer is either foo() or foo{}, so look for that + while True: + if tok.type not in ("{", "("): + tok = self.lex.token() + continue + + if tok.type == "{": + self._discard_contents("{", "}") + elif tok.type == "(": + self._discard_contents("(", ")") + + tok = self.lex.token() + break + + # at the end + if tok.type == "ELLIPSIS": + tok = self.lex.token() + + if tok.type == ",": + continue + elif tok.type == "{": + # reached the function + self._discard_contents("{", "}") + return + else: + raise self._parse_error((tok,), ",' or '{") + def _evaluate_stack(self, token=None): """Evaluates the current name stack""" @@ -3133,7 +3182,7 @@ def _evaluate_stack(self, token=None): self.nameStackHistory[self.braceDepth] = (nameStackCopy, self.curClass) except: self.nameStackHistory.append((nameStackCopy, self.curClass)) - + # its a little confusing to have some if/else above return and others not, and then clearning the nameStack down here self.nameStack = [] self.lex.doxygenCommentCache = "" diff --git a/CppHeaderParser/test/test_CppHeaderParser.py b/CppHeaderParser/test/test_CppHeaderParser.py index 6cfc5a9..ec52861 100644 --- a/CppHeaderParser/test/test_CppHeaderParser.py +++ b/CppHeaderParser/test/test_CppHeaderParser.py @@ -3366,5 +3366,58 @@ def test_nothing(self): self.assertEqual(self.cppHeader.functions, []) +class InitializerWithInitializerList_TestCase(unittest.TestCase): + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader( + """ +struct ComplexInit : SomeBase { + ComplexInit(int i) : + m_stuff{i,2} + { + auto i = something(); + } + + void fn(); + + std::vector m_stuff; +}; + +template +class future final { +public: + template + future(future&& oth) noexcept + : future(oth.then([](R&& val) -> T { return val; })) {} +}; + + +""", + "string", + ) + + def test_cls_props(self): + c = self.cppHeader.classes["ComplexInit"] + self.assertEqual(2, len(c["methods"]["public"])) + self.assertEqual(0, len(c["methods"]["private"])) + self.assertEqual(0, len(c["methods"]["private"])) + self.assertEqual(1, len(c["properties"]["public"])) + self.assertEqual(0, len(c["properties"]["private"])) + self.assertEqual(0, len(c["properties"]["protected"])) + + self.assertEqual(c["methods"]["public"][0]["name"], "ComplexInit") + self.assertEqual(c["methods"]["public"][1]["name"], "fn") + + self.assertEqual(c["properties"]["public"][0]["name"], "m_stuff") + + def test_future(self): + c = self.cppHeader.classes["future"] + self.assertEqual(1, len(c["methods"]["public"])) + self.assertEqual(0, len(c["methods"]["private"])) + self.assertEqual(0, len(c["methods"]["private"])) + self.assertEqual(0, len(c["properties"]["public"])) + self.assertEqual(0, len(c["properties"]["private"])) + self.assertEqual(0, len(c["properties"]["protected"])) + self.assertEqual(c["methods"]["public"][0]["name"], "future") + if __name__ == "__main__": unittest.main() From 6863871e8ca87bd856adb7fbda239e4466f1a0ae Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Wed, 25 Dec 2019 00:23:30 -0500 Subject: [PATCH 074/143] Remove temporary variable --- CppHeaderParser/CppHeaderParser.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index e940dd2..e97a175 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -2638,6 +2638,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): # self._doxygen_cache = None + self.braceHandled = False tok = None self.stmtTokens = [] @@ -2867,6 +2868,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): for key in [ "_precomp_macro_buf", "_doxygen_cache", + "braceHandled", "lex", "nameStack", "nameSpaces", From f6109d2fe9a647bbbb36b1463fd3b74ebbb78fd8 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Wed, 25 Dec 2019 02:06:15 -0500 Subject: [PATCH 075/143] Fix formatting --- CppHeaderParser/test/test_CppHeaderParser.py | 1 + 1 file changed, 1 insertion(+) diff --git a/CppHeaderParser/test/test_CppHeaderParser.py b/CppHeaderParser/test/test_CppHeaderParser.py index ec52861..2a048ce 100644 --- a/CppHeaderParser/test/test_CppHeaderParser.py +++ b/CppHeaderParser/test/test_CppHeaderParser.py @@ -3419,5 +3419,6 @@ def test_future(self): self.assertEqual(0, len(c["properties"]["protected"])) self.assertEqual(c["methods"]["public"][0]["name"], "future") + if __name__ == "__main__": unittest.main() From 485261120307d4669926c3d398b6fab4bc9ce65e Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Fri, 27 Dec 2019 13:00:02 -0500 Subject: [PATCH 076/143] Move tests, examples --- .../examples => examples}/SampleClass.h | 0 .../examples => examples}/readSampleClass.py | 14 ++++++++------ run_tests.sh | 2 +- {CppHeaderParser/test => test}/LineNumTest.h | 0 {CppHeaderParser/test => test}/TestSampleClass.h | 0 .../test => test}/test_CppHeaderParser.py | 0 6 files changed, 9 insertions(+), 7 deletions(-) rename {CppHeaderParser/examples => examples}/SampleClass.h (100%) rename {CppHeaderParser/examples => examples}/readSampleClass.py (85%) rename {CppHeaderParser/test => test}/LineNumTest.h (100%) rename {CppHeaderParser/test => test}/TestSampleClass.h (100%) rename {CppHeaderParser/test => test}/test_CppHeaderParser.py (100%) diff --git a/CppHeaderParser/examples/SampleClass.h b/examples/SampleClass.h similarity index 100% rename from CppHeaderParser/examples/SampleClass.h rename to examples/SampleClass.h diff --git a/CppHeaderParser/examples/readSampleClass.py b/examples/readSampleClass.py similarity index 85% rename from CppHeaderParser/examples/readSampleClass.py rename to examples/readSampleClass.py index 35d4ac4..1b9e9be 100755 --- a/CppHeaderParser/examples/readSampleClass.py +++ b/examples/readSampleClass.py @@ -1,12 +1,13 @@ -#!/usr/bin/python +#!/usr/bin/env python +import pprint import sys sys.path = ["../"] + sys.path -import CppHeaderParser +from CppHeaderParser import CppHeader, CppParseError try: - cppHeader = CppHeaderParser.CppHeader("SampleClass.h") -except CppHeaderParser.CppParseError as e: + cppHeader = CppHeader("SampleClass.h") +except CppParseError as e: print(e) sys.exit(1) @@ -19,7 +20,8 @@ 0 ] # get meth3 meth3ParamTypes = [t["type"] for t in meth3["parameters"]] # get meth3s parameters -print("Parameter Types for public method meth3 %s" % (meth3ParamTypes)) +print("Parameter Types for public method meth3") +pprint.pprint(meth3ParamTypes) print("\nReturn type for meth1:") print(cppHeader.classes["SampleClass"]["methods"]["public"][1]["rtnType"]) @@ -28,7 +30,7 @@ print(cppHeader.classes["SampleClass"]["methods"]["public"][2]["doxygen"]) print("\nParameters for meth3:") -print(cppHeader.classes["SampleClass"]["methods"]["public"][3]["parameters"]) +pprint.pprint(cppHeader.classes["SampleClass"]["methods"]["public"][3]["parameters"]) print("\nDoxygen for meth4:") print(cppHeader.classes["SampleClass"]["methods"]["public"][4]["doxygen"]) diff --git a/run_tests.sh b/run_tests.sh index ff74e60..83b59b6 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -1,4 +1,4 @@ #!/bin/sh -e -cd CppHeaderParser/test/ +cd test/ python test_CppHeaderParser.py diff --git a/CppHeaderParser/test/LineNumTest.h b/test/LineNumTest.h similarity index 100% rename from CppHeaderParser/test/LineNumTest.h rename to test/LineNumTest.h diff --git a/CppHeaderParser/test/TestSampleClass.h b/test/TestSampleClass.h similarity index 100% rename from CppHeaderParser/test/TestSampleClass.h rename to test/TestSampleClass.h diff --git a/CppHeaderParser/test/test_CppHeaderParser.py b/test/test_CppHeaderParser.py similarity index 100% rename from CppHeaderParser/test/test_CppHeaderParser.py rename to test/test_CppHeaderParser.py From 5623e93d5085450947050a5770f8b097ada06857 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Fri, 27 Dec 2019 13:03:10 -0500 Subject: [PATCH 077/143] Update stuffs --- CppHeaderParser/Makefile | 4 ---- MANIFEST.in | 7 +++---- setup.py | 1 - 3 files changed, 3 insertions(+), 9 deletions(-) delete mode 100644 CppHeaderParser/Makefile diff --git a/CppHeaderParser/Makefile b/CppHeaderParser/Makefile deleted file mode 100644 index e3fce3e..0000000 --- a/CppHeaderParser/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -all: package - -package: - @zip CppHeaderParser-`grep __version__ CppHeaderParser.py | sed 's/.*"\([^"]*\)"/\1/'`.zip CppHeaderParser.html CppHeaderParser.py \ No newline at end of file diff --git a/MANIFEST.in b/MANIFEST.in index cd7befe..1021a91 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,5 +1,4 @@ -include README.md +include README.rst include LICENSE.txt -include CppHeaderParser/doc/CppHeaderParser.html -include CppHeaderParser/examples/readSampleClass.py -include CppHeaderParser/examples/SampleClass.h +include examples/readSampleClass.py +include examples/SampleClass.h diff --git a/setup.py b/setup.py index 7a0f5f0..5de215c 100644 --- a/setup.py +++ b/setup.py @@ -75,5 +75,4 @@ classifiers=CLASSIFIERS, requires=["ply"], install_requires=["ply"], - package_data={"CppHeaderParser": ["README", "README.html", "examples/*.*"]}, ) From 08950fc52ccfe260fe12de0e909956443efc78bc Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sat, 28 Dec 2019 19:46:12 -0500 Subject: [PATCH 078/143] Prevent enums from being parsed as a class --- CppHeaderParser/CppHeaderParser.py | 5 ++++- test/test_CppHeaderParser.py | 23 +++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index e97a175..4978d47 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -2711,7 +2711,10 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): if "class" in self.nameStack and self.nameStack[0] != "class": classLocationNS = self.nameStack.index("class") classLocationS = self.stack.index("class") - if "(" not in self.nameStack[classLocationNS:]: + if ( + "(" not in self.nameStack[classLocationNS:] + and self.nameStack[classLocationNS - 1] != "enum" + ): debug_print( "keyword 'class' found in unexpected location in nameStack, must be following #define magic. Process that before moving on" ) diff --git a/test/test_CppHeaderParser.py b/test/test_CppHeaderParser.py index 2a048ce..2b78eff 100644 --- a/test/test_CppHeaderParser.py +++ b/test/test_CppHeaderParser.py @@ -3420,5 +3420,28 @@ def test_future(self): self.assertEqual(c["methods"]["public"][0]["name"], "future") +class EnumClass_TestCase(unittest.TestCase): + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader( + """ +enum class MyEnum { + V = 1 +}; + +""", + "string", + ) + + def test_enum(self): + self.assertEqual(self.cppHeader.classes, {}) + self.assertEqual(len(self.cppHeader.enums), 1) + e = self.cppHeader.enums[0] + + self.assertEqual(e["name"], "MyEnum") + self.assertEqual( + e["values"], [{"name": "V", "value": 1}], + ) + + if __name__ == "__main__": unittest.main() From ac1f3dc7e9ab5b8e599b4f2a51ab88c1caaeca2e Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 29 Dec 2019 17:02:13 -0500 Subject: [PATCH 079/143] Add 'parent' attributes to vars/methods/classes - The parent attribute already exists, but sometimes it was a string and sometimes it was a thing. Now it's always a thing --- CppHeaderParser/CppHeaderParser.py | 50 +++++++++++++++++------------- test/test_CppHeaderParser.py | 6 ++-- 2 files changed, 32 insertions(+), 24 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 4978d47..8b1f275 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -589,6 +589,7 @@ class CppClass(dict): * ``nested_classes`` - Classes and structs defined within this class * ``final`` - True if final * ``abstract`` - True if abstract + * ``parent`` - If not None, the class that this class is nested in An example of how this could look is as follows:: @@ -713,7 +714,7 @@ def show(self): if "doxygen" in list(self.keys()): rtn += self["doxygen"] + "\n" if "parent" in list(self.keys()) and self["parent"]: - rtn += "parent class: " + self["parent"] + "\n" + rtn += "parent class: " + self["parent"]["name"] + "\n" if "inherits" in list(self.keys()): rtn += " Inherits: " @@ -759,7 +760,7 @@ def __str__(self): if "doxygen" in list(self.keys()): rtn += self["doxygen"] + "\n" if "parent" in list(self.keys()) and self["parent"]: - rtn += "parent class: " + self["parent"] + "\n" + rtn += "parent class: " + self["parent"]["name"] + "\n" if "inherits" in list(self.keys()) and len(self["inherits"]): rtn += "Inherits: " @@ -834,7 +835,7 @@ def __str__(self): if "doxygen" in list(self.keys()): rtn += self["doxygen"] + "\n" if "parent" in list(self.keys()) and self["parent"]: - rtn += "parent class: " + self["parent"] + "\n" + rtn += "parent class: " + self["parent"]["name"] + "\n" rtn += "{\n" for member in self["members"]: @@ -898,6 +899,7 @@ def _params_helper1(self, stack): def _params_helper2(self, params): for p in params: p["method"] = self # save reference in variable to parent method + p["parent"] = self if "::" in p["type"]: ns = p["type"].split("::")[0] if ns not in Resolver.NAMESPACES and ns in Resolver.CLASSES: @@ -914,6 +916,7 @@ class CppMethod(_CppMethod): * ``name`` - Name of the method * ``doxygen`` - Doxygen comments associated with the method if they exist * ``parameters`` - List of :class:`.CppVariable` + * ``parent`` - If not None, the class this method belongs to """ def show(self): @@ -1538,14 +1541,16 @@ def finalize_vars(self): nestedEnum = None nestedStruct = None nestedTypedef = None - if "method" in var and "parent" in list(var["method"].keys()): - klass = var["method"]["parent"] - if tag in var["method"]["parent"]._public_enums: - nestedEnum = var["method"]["parent"]._public_enums[tag] - elif tag in var["method"]["parent"]._public_typedefs: - nestedTypedef = var["method"]["parent"]._public_typedefs[ - tag - ] + + parent = var["parent"] + while parent: + nestedEnum = getattr(parent, "_public_enums", {}).get(tag) + if nestedEnum: + break + nestedTypedef = getattr(parent, "_public_typedefs", {}).get(tag) + if nestedTypedef: + break + parent = parent["parent"] if "<" in tag: # should also contain '>' var["template"] = tag # do not resolve templates @@ -1604,7 +1609,7 @@ def finalize_vars(self): var["enum"] = enum["namespace"] + enum["name"] var["fundamental"] = True - elif var["parent"]: + elif var["parent"] and var["unresolved"]: warning_print("WARN unresolved %s", _tag) var["ctypes_type"] = "ctypes.c_void_p" var["unresolved"] = True @@ -1750,8 +1755,8 @@ def finalize_vars(self): var["raw_type"] = ( var["class"]["namespace"] + "::" + var["raw_type"] ) - elif var["class"]["parent"] in self.classes: - parent = self.classes[var["class"]["parent"]] + else: + parent = var["class"]["parent"] var["raw_type"] = ( parent["namespace"] + "::" @@ -1759,8 +1764,6 @@ def finalize_vars(self): + "::" + var["raw_type"] ) - else: - var["unresolved"] = True elif ( "::" in var["raw_type"] @@ -2166,6 +2169,7 @@ def _evaluate_method_stack(self): self._get_stmt_doxygen(), self._get_location(self.nameStack), ) + newMethod["parent"] = None self.functions.append(newMethod) global parseHistory parseHistory.append( @@ -2299,6 +2303,7 @@ def _evaluate_property_stack(self, clearStack=True, addToVar=None): klass = self.classes[self.curClass] klass["properties"][self.curAccessSpecifier].append(newVar) newVar["property_of_class"] = klass["name"] + newVar["parent"] = klass parseHistory.append( {"braceDepth": self.braceDepth, "item_type": "variable", "item": newVar} ) @@ -2373,16 +2378,17 @@ def _evaluate_class_stack(self): if parent: newClass["namespace"] = self.classes[parent]["namespace"] + "::" + parent - newClass["parent"] = parent + newClass["parent"] = self.classes[parent] self.classes[parent]["nested_classes"].append(newClass) ## supports nested classes with the same name ## self.curClass = key = parent + "::" + classKey self._classes_brace_level[key] = self.braceDepth elif newClass["parent"]: # nested class defined outside of parent. A::B {...} - parent = newClass["parent"] - newClass["namespace"] = self.classes[parent]["namespace"] + "::" + parent - self.classes[parent]["nested_classes"].append(newClass) + pcls = newClass["parent"] + parent = pcls["name"] + newClass["namespace"] = pcls["namespace"] + "::" + parent + pcls["nested_classes"].append(newClass) ## supports nested classes with the same name ## self.curClass = key = parent + "::" + classKey self._classes_brace_level[key] = self.braceDepth @@ -2767,7 +2773,9 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self.curAccessSpecifier = self.accessSpecifierStack[-1] self.accessSpecifierStack = self.accessSpecifierStack[:-1] if self.curClass and self.classes[self.curClass]["parent"]: - self.curClass = self.classes[self.curClass]["parent"] + self.curClass = self.classes[self.curClass]["parent"][ + "name" + ] else: self.curClass = "" self.stack = [] diff --git a/test/test_CppHeaderParser.py b/test/test_CppHeaderParser.py index 2b78eff..2729656 100644 --- a/test/test_CppHeaderParser.py +++ b/test/test_CppHeaderParser.py @@ -952,7 +952,7 @@ def test_property_CONST_A(self): "unresolved": False, "constant": 1, "name": "CONST_A", - "parent": None, + "parent": self.cppHeader.classes["PandaClass"], "pointer": 0, "namespace": "", "raw_type": "int", @@ -981,7 +981,7 @@ def test_property_CONST_B(self): "unresolved": False, "constant": 1, "name": "CONST_B", - "parent": None, + "parent": self.cppHeader.classes["PandaClass"], "pointer": 0, "namespace": "", "raw_type": "int", @@ -1052,7 +1052,7 @@ def test_property(self): def test_union(self): cmp_values = { "name": "union HogUnion", - "parent": "HogClass", + "parent": self.cppHeader.classes["HogClass"], "declaration_method": "union", } self.assertEqual( From a1f5eae8f1d9668521200657cfcf2ade7eafd08c Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 29 Dec 2019 17:09:20 -0500 Subject: [PATCH 080/143] using directives in a class are slightly different from global using --- CppHeaderParser/CppHeaderParser.py | 74 ++++++++++++++++++++++-------- test/test_CppHeaderParser.py | 47 ++++++++++++++++++- 2 files changed, 100 insertions(+), 21 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 8b1f275..1a6b0ca 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -589,6 +589,8 @@ class CppClass(dict): * ``nested_classes`` - Classes and structs defined within this class * ``final`` - True if final * ``abstract`` - True if abstract + * ``using`` - Using directives in this class scope: key is name for lookup, + value is :class:`.CppVariable` * ``parent`` - If not None, the class that this class is nested in An example of how this could look is as follows:: @@ -654,6 +656,7 @@ def __init__(self, nameStack, curTemplate, doxygen, location): self._public_typedefs = {} self._public_forward_declares = [] self["namespace"] = "" + self["using"] = {} debug_print("Class: %s", nameStack) debug_print("Template: %s", curTemplate) @@ -1124,12 +1127,14 @@ class CppVariable(_CppVariable): * ``default`` - Default value of the variable, this key will only exist if there is a default value * ``extern`` - True if its an extern, False if not + * ``parent`` - If not None, either the class this is a property of, or the + method this variable is a parameter in """ Vars = [] def __init__(self, nameStack, doxygen, location, **kwargs): - debug_print("trace %s", nameStack) + debug_print("var trace %s", nameStack) if len(nameStack) and nameStack[0] == "extern": self["extern"] = True del nameStack[0] @@ -1497,21 +1502,33 @@ def resolve_type(self, string, result): # recursive result["fundamental"] = False result["class"] = klass result["unresolved"] = False - elif self.using: - # search for type in all enclosing namespaces - for ns in _iter_ns_str_reversed(result.get("namespace", "")): - nsalias = ns + alias - used = self.using.get(nsalias) - if used: - for i in ("type", "namespace", "ctypes_type", "raw_type"): - if i in used: - result[i] = used[i] - result["unresolved"] = False - break - else: - result["unresolved"] = True else: - result["unresolved"] = True + used = None + + # Search for using directives in parents + parent = result["parent"] + while parent: + p_using = parent.get("using") + if p_using: + used = p_using.get(alias) + if used: + break + parent = parent["parent"] + + if not used and self.using: + # search for type in all enclosing namespaces + # TODO: would be nice if namespaces were an object? + for ns in _iter_ns_str_reversed(result.get("namespace", "")): + nsalias = ns + alias + used = self.using.get(nsalias) + if used: + break + + if used: + for i in ("type", "namespace", "ctypes_type", "raw_type"): + if i in used: + result[i] = used[i] + result["unresolved"] = False else: result["fundamental"] = True result["unresolved"] = False @@ -2544,8 +2561,8 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self.anon_struct_counter = 0 self.anon_union_counter = [-1, 0] - #: Using directives in this header: key is full name for lookup, value - #: is :class:`.CppVariable` + #: Using directives in this header outside of class scope: key is + #: full name for lookup, value is :class:`.CppVariable` self.using = {} if len(self.headerFileName): @@ -3116,23 +3133,40 @@ def _evaluate_stack(self, token=None): else: if len(self.nameStack) > 3 and self.nameStack[2] == "=": # using foo = ns::bar + # -> type alias: same behavior in all scopes alias = self.nameStack[1] ns, stack = _split_namespace(self.nameStack[3:]) atype = CppVariable( stack, self._get_stmt_doxygen(), self._get_location(stack) ) + + # namespace refers to the embedded type + atype["namespace"] = ns else: # using foo::bar + # -> in global scope this is bringing in something + # from a different namespace + # -> in class scope this is bringing in a member + # from a base class ns, stack = _split_namespace(self.nameStack[1:]) atype = CppVariable( stack, self._get_stmt_doxygen(), self._get_location(stack) ) alias = atype["type"] + if self.curClass: + atype["baseclass"] = ns + else: + atype["namespace"] = ns - atype["namespace"] = ns atype["raw_type"] = ns + atype["type"] - alias = self.current_namespace() + alias - self.using[alias] = atype + + if self.curClass: + klass = self.classes[self.curClass] + klass["using"][alias] = atype + else: + # lookup is done + alias = self.current_namespace() + alias + self.using[alias] = atype elif is_method_namestack(self.stack) and "(" in self.nameStack: debug_print("trace") self._evaluate_method_stack() diff --git a/test/test_CppHeaderParser.py b/test/test_CppHeaderParser.py index 2729656..1f35355 100644 --- a/test/test_CppHeaderParser.py +++ b/test/test_CppHeaderParser.py @@ -2787,18 +2787,26 @@ def setUp(self): using VoidFunction = std::function; void fn(string &s, VoidFunction fn, thing * t); + + class A : public B { + public: + using B::B; + using IntFunction = std::function; + + void a(string &s, IntFunction fn, thing * t); + }; } """, "string", ) def test_using(self): + self.assertEqual(len(self.cppHeader.using), 3) self.assertIn("a::string", self.cppHeader.using) self.assertIn("a::VoidFunction", self.cppHeader.using) self.assertIn("thing", self.cppHeader.using) def test_fn(self): - self.maxDiff = None self.assertEqual(len(self.cppHeader.functions), 1) fn = self.cppHeader.functions[0] self.assertEqual(fn["name"], "fn") @@ -2829,6 +2837,43 @@ def test_fn(self): ], ) + def test_class(self): + c = self.cppHeader.classes["A"] + + self.assertEqual(len(c["using"]), 2) + self.assertIn("B", c["using"]) + self.assertIn("IntFunction", c["using"]) + + self.assertEqual(len(c["methods"]["public"]), 1) + fn = c["methods"]["public"][0] + self.assertEqual(fn["name"], "a") + self.assertEqual( + filter_pameters(fn["parameters"], ["namespace", "raw_type"]), + [ + { + "type": "string", + "name": "s", + "desc": None, + "namespace": "std::", + "raw_type": "std::string", + }, + { + "type": "function", + "name": "fn", + "desc": None, + "namespace": "std::", + "raw_type": "std::function", + }, + { + "type": "thing", + "name": "t", + "desc": None, + "namespace": "std::", + "raw_type": "std::thing", + }, + ], + ) + class StaticFn_TestCase(unittest.TestCase): def setUp(self): From 7fcb015dec5cf0f7a336f8379ab33540bec1b3f0 Mon Sep 17 00:00:00 2001 From: David Vo Date: Mon, 30 Dec 2019 15:55:55 +1100 Subject: [PATCH 081/143] Ensure license file is included in wheels Since we upload wheels to PyPI, they should include the license. --- setup.cfg | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 setup.cfg diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..9853d1a --- /dev/null +++ b/setup.cfg @@ -0,0 +1,3 @@ +[metadata] +# Include the license file in wheels. +license_file = LICENSE.txt From d140ad93825da1edf3cfaaa87ff8bf784ef9dbc2 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Tue, 31 Dec 2019 04:19:57 -0500 Subject: [PATCH 082/143] Fix nested lookups for parameters and return types --- CppHeaderParser/CppHeaderParser.py | 61 +++++++++++++++++++++++++----- test/test_CppHeaderParser.py | 34 +++++++++++++++++ 2 files changed, 85 insertions(+), 10 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 1a6b0ca..0df933c 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -647,6 +647,21 @@ def get_pure_virtual_methods(self, type="public"): r[meth["name"]] = meth return r + def _lookup_type(self, name): + # TODO: should have indexes for these lookups... and they + # should be more unified + for access in supportedAccessSpecifier: + for e in self["enums"][access]: + if e.get("name") == name: + return { + "enum": self["name"] + "::" + e["name"], + "type": e["name"], + "namespace": e["namespace"], + } + for n in self["nested_classes"]: + if n["name"] == name: + return {"raw_type": self["name"] + "::" + n["name"], "type": n["name"]} + def __init__(self, nameStack, curTemplate, doxygen, location): self["nested_classes"] = [] self["parent"] = None @@ -1505,15 +1520,21 @@ def resolve_type(self, string, result): # recursive else: used = None - # Search for using directives in parents - parent = result["parent"] - while parent: - p_using = parent.get("using") - if p_using: - used = p_using.get(alias) - if used: - break - parent = parent["parent"] + # Search for in parents + if not used: + parent = result["parent"] + while parent: + p_using = parent.get("using") + if p_using: + used = p_using.get(alias) + if used: + break + lookup = getattr(parent, "_lookup_type", None) + if lookup: + used = lookup(alias) + if used: + break + parent = parent["parent"] if not used and self.using: # search for type in all enclosing namespaces @@ -1525,7 +1546,7 @@ def resolve_type(self, string, result): # recursive break if used: - for i in ("type", "namespace", "ctypes_type", "raw_type"): + for i in ("enum", "type", "namespace", "ctypes_type", "raw_type"): if i in used: result[i] = used[i] result["unresolved"] = False @@ -1860,6 +1881,26 @@ def finalize(self): if meth["pure_virtual"]: cls["abstract"] = True + # hack + rtnType = { + "aliases": [], + "parent": cls, + "unresolved": True, + "constant": 0, + "constexpr": 0, + "static": 0, + "pointer": 0, + "reference": 0, + } + self.resolve_type(meth["rtnType"], rtnType) + if not rtnType["unresolved"]: + if "enum" in rtnType: + meth["rtnType"] = rtnType["enum"] + elif "raw_type" in rtnType: + meth["rtnType"] = rtnType["raw_type"] + + # TODO: all of this needs to die and be replaced by CppVariable + if ( not meth["returns_fundamental"] and meth["returns"] in C99_NONSTANDARD diff --git a/test/test_CppHeaderParser.py b/test/test_CppHeaderParser.py index 1f35355..a699f36 100644 --- a/test/test_CppHeaderParser.py +++ b/test/test_CppHeaderParser.py @@ -3488,5 +3488,39 @@ def test_enum(self): ) +class NestedResolving_TestCase(unittest.TestCase): + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader( + """ +struct A { + + enum { ANON }; + + struct B {}; + enum C { X }; + + B fnested(B b); + C fenum(C c); +}; + +""", + "string", + ) + + def test_nothing(self): + c = self.cppHeader.classes["A"] + fn = c["methods"]["public"][0] + self.assertEqual(fn["name"], "fnested") + self.assertEqual(fn["rtnType"], "A::B") + self.assertEqual(len(fn["parameters"]), 1) + self.assertEqual(fn["parameters"][0]["raw_type"], "A::B") + + fn = c["methods"]["public"][1] + self.assertEqual(fn["name"], "fenum") + self.assertEqual(fn["rtnType"], "A::C") + self.assertEqual(len(fn["parameters"]), 1) + self.assertEqual(fn["parameters"][0]["enum"], "A::C") + + if __name__ == "__main__": unittest.main() From 207bb7e9e8ea037a8be7ec03c8c1e8abc8ba0587 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Thu, 2 Jan 2020 04:36:20 -0500 Subject: [PATCH 083/143] Fix enum crash --- CppHeaderParser/CppHeaderParser.py | 36 +++++++++++++++++------------- test/test_CppHeaderParser.py | 23 +++++++++++++++++++ 2 files changed, 44 insertions(+), 15 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 0df933c..2713f68 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -1597,15 +1597,19 @@ def finalize_vars(self): elif nestedEnum: enum = nestedEnum - if enum["type"] is int: + etype = enum.get("type") + if etype is int: var["ctypes_type"] = "ctypes.c_int" var["raw_type"] = "int" - elif enum["type"] is str: + elif etype is str: var["ctypes_type"] = "ctypes.c_char_p" var["raw_type"] = "char*" - var["enum"] = var["method"]["path"] + "::" + enum["name"] + if "method" in var: + var["enum"] = var["method"]["path"] + "::" + enum["name"] + else: + var["enum"] = enum["name"] var["fundamental"] = True elif nestedStruct: @@ -1663,10 +1667,11 @@ def finalize_vars(self): if b in klass._public_enums: trace_print("...found nested enum", b) enum = klass._public_enums[b] - if enum["type"] is int: + etype = enum.get("type") + if etype is int: var["ctypes_type"] = "ctypes.c_int" var["raw_type"] = "int" - elif enum["type"] is str: + elif etype is str: var["ctypes_type"] = "ctypes.c_char_p" var["raw_type"] = "char*" try: @@ -1700,10 +1705,11 @@ def finalize_vars(self): ): # falling back, this is a big ugly enum = self.global_enums[b] assert a in enum["namespace"].split("::") - if enum["type"] is int: + etype = enum.get("type") + if etype is int: var["ctypes_type"] = "ctypes.c_int" var["raw_type"] = "int" - elif enum["type"] is str: + elif etype is str: var["ctypes_type"] = "ctypes.c_char_p" var["raw_type"] = "char*" var["fundamental"] = True @@ -1931,18 +1937,18 @@ def finalize(self): elif meth["returns"] in cls._public_enums: enum = cls._public_enums[meth["returns"]] - meth["returns_enum"] = enum["type"] + meth["returns_enum"] = enum.get("type") meth["returns_fundamental"] = True - if enum["type"] == int: + if enum.get("type") == int: meth["returns"] = "int" else: meth["returns"] = "char*" elif meth["returns"] in self.global_enums: enum = self.global_enums[meth["returns"]] - meth["returns_enum"] = enum["type"] + meth["returns_enum"] = enum.get("type") meth["returns_fundamental"] = True - if enum["type"] == int: + if enum.get("type") == int: meth["returns"] = "int" else: meth["returns"] = "char*" @@ -1958,9 +1964,9 @@ def finalize(self): meth["returns_unknown"] = True elif b in self.global_enums: enum = self.global_enums[b] - meth["returns_enum"] = enum["type"] + meth["returns_enum"] = enum.get("type") meth["returns_fundamental"] = True - if enum["type"] == int: + if enum.get("type") == int: meth["returns"] = "int" else: meth["returns"] = "char*" @@ -1975,9 +1981,9 @@ def finalize(self): if b in klass._public_enums: trace_print("...found nested enum", b) enum = klass._public_enums[b] - meth["returns_enum"] = enum["type"] + meth["returns_enum"] = enum.get("type") meth["returns_fundamental"] = True - if enum["type"] == int: + if enum.get("type") == int: meth["returns"] = "int" else: meth["returns"] = "char*" diff --git a/test/test_CppHeaderParser.py b/test/test_CppHeaderParser.py index a699f36..9560e59 100644 --- a/test/test_CppHeaderParser.py +++ b/test/test_CppHeaderParser.py @@ -3522,5 +3522,28 @@ def test_nothing(self): self.assertEqual(fn["parameters"][0]["enum"], "A::C") +class EnumCrash_TestCase(unittest.TestCase): + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader( + """ +enum HAL_Type { + HAL_UNASSIGNED = 0, +}; + +struct HAL_Value { + union { + HAL_Bool v_boolean; + } data; + enum HAL_Type type; +}; + +""", + "string", + ) + + def test_nothing(self): + pass + + if __name__ == "__main__": unittest.main() From 43c65dcc663b4f4b4c604c630acbbc606d42cb55 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Fri, 3 Jan 2020 14:20:23 -0500 Subject: [PATCH 084/143] Properly handle 'extern "C"' namespaces --- CppHeaderParser/CppHeaderParser.py | 4 ++++ test/test_CppHeaderParser.py | 34 ++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 2713f68..a7cd719 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -3170,6 +3170,10 @@ def _evaluate_stack(self, token=None): elif self.nameStack[0] == "namespace": # Taken care of outside of here pass + elif len(self.nameStack) == 2 and self.nameStack[0] == "extern": + debug_print("trace extern") + self.stack = [] + self.stmtTokens = [] elif ( len(self.nameStack) == 2 and self.nameStack[0] == "friend" ): # friend class declaration diff --git a/test/test_CppHeaderParser.py b/test/test_CppHeaderParser.py index 9560e59..d543f13 100644 --- a/test/test_CppHeaderParser.py +++ b/test/test_CppHeaderParser.py @@ -3545,5 +3545,39 @@ def test_nothing(self): pass +class ExternInline_TestCase(unittest.TestCase): + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader( + """ +extern "C++" { +inline HAL_Value HAL_GetSimValue(HAL_SimValueHandle handle) { + HAL_Value v; + return v; +} +} // extern "C++" + +""", + "string", + ) + + def test_fn(self): + self.assertEqual(self.cppHeader.variables, []) + self.assertEqual(len(self.cppHeader.functions), 1) + fn = self.cppHeader.functions[0] + self.assertEqual(fn["name"], "HAL_GetSimValue") + self.assertEqual( + filter_pameters(fn["parameters"], ["namespace", "raw_type"]), + [ + { + "type": "HAL_SimValueHandle", + "name": "handle", + "desc": None, + "namespace": "", + "raw_type": "HAL_SimValueHandle", + }, + ], + ) + + if __name__ == "__main__": unittest.main() From 795c22385e33e8a4c88716f86c0012f63e829e45 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Fri, 3 Jan 2020 16:22:23 -0500 Subject: [PATCH 085/143] Modify using to indicate which type it is --- CppHeaderParser/CppHeaderParser.py | 3 ++ test/test_CppHeaderParser.py | 76 ++++++++++++++++++++++++++++-- 2 files changed, 75 insertions(+), 4 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index a7cd719..a57de8a 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -3193,6 +3193,8 @@ def _evaluate_stack(self, token=None): # namespace refers to the embedded type atype["namespace"] = ns + atype["using_type"] = "typealias" + atype["typealias"] = alias else: # using foo::bar # -> in global scope this is bringing in something @@ -3204,6 +3206,7 @@ def _evaluate_stack(self, token=None): stack, self._get_stmt_doxygen(), self._get_location(stack) ) alias = atype["type"] + atype["using_type"] = "declaration" if self.curClass: atype["baseclass"] = ns else: diff --git a/test/test_CppHeaderParser.py b/test/test_CppHeaderParser.py index d543f13..c8524d9 100644 --- a/test/test_CppHeaderParser.py +++ b/test/test_CppHeaderParser.py @@ -2782,6 +2782,7 @@ def setUp(self): self.cppHeader = CppHeaderParser.CppHeader( """ using std::thing; +using MyThing = SomeThing; namespace a { using std::string; using VoidFunction = std::function; @@ -2801,10 +2802,77 @@ class A : public B { ) def test_using(self): - self.assertEqual(len(self.cppHeader.using), 3) - self.assertIn("a::string", self.cppHeader.using) - self.assertIn("a::VoidFunction", self.cppHeader.using) - self.assertIn("thing", self.cppHeader.using) + self.assertEqual(len(self.cppHeader.using), 4) + + self.assertEqual( + filter_pameters( + [self.cppHeader.using["a::string"]], + extra=["using_type", "raw_type", "namespace"], + ), + [ + { + "desc": None, + "name": "", + "namespace": "std::", + "raw_type": "std::string", + "type": "string", + "using_type": "declaration", + } + ], + ) + + self.assertEqual( + filter_pameters( + [self.cppHeader.using["a::VoidFunction"]], + extra=["using_type", "raw_type", "namespace", "typealias"], + ), + [ + { + "desc": None, + "name": "", + "namespace": "std::", + "raw_type": "std::function", + "type": "function", + "typealias": "VoidFunction", + "using_type": "typealias", + } + ], + ) + + self.assertEqual( + filter_pameters( + [self.cppHeader.using["thing"]], + extra=["using_type", "raw_type", "namespace"], + ), + [ + { + "desc": None, + "name": "", + "namespace": "std::", + "raw_type": "std::thing", + "type": "thing", + "using_type": "declaration", + } + ], + ) + + self.assertEqual( + filter_pameters( + [self.cppHeader.using["MyThing"]], + extra=["using_type", "raw_type", "namespace", "typealias"], + ), + [ + { + "desc": None, + "name": "", + "namespace": "", + "raw_type": "SomeThing", + "type": "SomeThing", + "typealias": "MyThing", + "using_type": "typealias", + } + ], + ) def test_fn(self): self.assertEqual(len(self.cppHeader.functions), 1) From 56400a4af9f48b3a46dae91bb4933dba1f2e44b9 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Fri, 3 Jan 2020 17:13:08 -0500 Subject: [PATCH 086/143] Strip 'inline' from return type --- CppHeaderParser/CppHeaderParser.py | 2 ++ test/test_CppHeaderParser.py | 1 + 2 files changed, 3 insertions(+) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index a57de8a..e9dca2a 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -972,6 +972,8 @@ def __init__(self, nameStack, curClass, methinfo, curTemplate, doxygen, location self["rtnType"] = self["rtnType"][len("virtual") :].strip() elif self["rtnType"].startswith("static"): self["rtnType"] = self["rtnType"][len("static") :].strip() + elif self["rtnType"].startswith("inline"): + self["rtnType"] = self["rtnType"][len("inline") :].strip() if len(self["rtnType"]) == 0 or self["name"] == curClass: self["rtnType"] = "void" diff --git a/test/test_CppHeaderParser.py b/test/test_CppHeaderParser.py index c8524d9..6d804e9 100644 --- a/test/test_CppHeaderParser.py +++ b/test/test_CppHeaderParser.py @@ -3645,6 +3645,7 @@ def test_fn(self): }, ], ) + self.assertEqual(fn["rtnType"], "HAL_Value") if __name__ == "__main__": From 9ff5a7ab45296d7f2c75b74384dce6c5ade25511 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Fri, 3 Jan 2020 17:47:31 -0500 Subject: [PATCH 087/143] Don't strip pointer from inside of templates --- CppHeaderParser/CppHeaderParser.py | 23 ++++++++++++++++++----- test/test_CppHeaderParser.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 5 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index e9dca2a..6c0a834 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -1470,6 +1470,23 @@ def guess_ctypes_type(self, string): x += ")" * pointers return x + def _remove_modifiers(self, vtype): + return " ".join(x for x in vtype.split() if x not in self.C_MODIFIERS) + + def _create_raw_type(self, vtype): + lt = vtype.find("<") + if lt != -1: + gt = vtype.rfind(">") + vtype = ( + self._remove_modifiers(vtype[:lt]) + + vtype[lt : gt + 1] + + self._remove_modifiers(vtype[gt + 1 :]) + ) + else: + vtype = self._remove_modifiers(vtype) + + return vtype + def resolve_type(self, string, result): # recursive """ keeps track of useful things like: how many pointers, number of typedefs, is fundamental or a class, etc... @@ -1788,11 +1805,7 @@ def finalize_vars(self): # create stripped raw_type # for var in CppVariable.Vars: if "raw_type" not in var: - raw = [] - for x in var["type"].split(): - if x not in self.C_MODIFIERS: - raw.append(x) - var["raw_type"] = " ".join(raw) + var["raw_type"] = self._create_raw_type(var["type"]) # if 'AutoConstantEntry' in var['raw_type']: print(var); assert 0 if var["class"]: diff --git a/test/test_CppHeaderParser.py b/test/test_CppHeaderParser.py index 6d804e9..5ee3dc5 100644 --- a/test/test_CppHeaderParser.py +++ b/test/test_CppHeaderParser.py @@ -3648,5 +3648,35 @@ def test_fn(self): self.assertEqual(fn["rtnType"], "HAL_Value") +class PointerTemplate_TestCase(unittest.TestCase): + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader( + """ + +std::vector * fn(std::vector * ps); + +""", + "string", + ) + + def test_fn(self): + self.assertEqual(len(self.cppHeader.functions), 1) + fn = self.cppHeader.functions[0] + self.assertEqual(fn["name"], "fn") + self.assertEqual( + filter_pameters(fn["parameters"], ["namespace", "raw_type"]), + [ + { + "type": "std::vector *", + "name": "ps", + "desc": None, + "namespace": None, + "raw_type": "std::vector", + }, + ], + ) + self.assertEqual(fn["rtnType"], "std::vector *") + + if __name__ == "__main__": unittest.main() From bcaa2db1c10ea46ca10703673edf7bc80852d5fb Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 5 Jan 2020 13:21:56 -0500 Subject: [PATCH 088/143] Only do noexcept processing when needed --- CppHeaderParser/CppHeaderParser.py | 47 ++++++++++++++++-------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 6c0a834..ec7d6c3 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -983,29 +983,32 @@ def __init__(self, nameStack, curClass, methinfo, curTemplate, doxygen, location self["rtnType"] = self["rtnType"].replace(" ,", ",") # deal with "noexcept" specifier/operator - cleaned = [] - hit = False - parentCount = 0 - self["noexcept"] = "" - for a in nameStack: - if a == "noexcept": - hit = True - if hit: - if a == "(": - parentCount += 1 - elif a == ")": - parentCount -= 1 - elif parentCount == 0 and a != "noexcept": - hit = False + self["noexcept"] = None + if "noexcept" in nameStack: + noexcept_idx = nameStack.index("noexcept") + hit = True + cleaned = nameStack[:noexcept_idx] + parentCount = 0 + noexcept = "noexcept" + for a in nameStack[noexcept_idx + 1 :]: + if a == "noexcept": + hit = True + if hit: + if a == "(": + parentCount += 1 + elif a == ")": + parentCount -= 1 + elif parentCount == 0 and a != "noexcept": + hit = False + cleaned.append(a) + continue # noexcept without parenthesis + if a == ")" and parentCount == 0: + hit = False + noexcept += a + else: cleaned.append(a) - continue # noexcept without parenthesis - if a == ")" and parentCount == 0: - hit = False - self["noexcept"] += a - else: - cleaned.append(a) - nameStack = cleaned - self["noexcept"] = self["noexcept"] if self["noexcept"] else None + self["noexcept"] = noexcept + nameStack = cleaned for spec in ["const", "final", "override"]: self[spec] = False From 1702f270af5ac5285b9a733efec99a43aa89deee Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 5 Jan 2020 13:42:58 -0500 Subject: [PATCH 089/143] Remove stmtTokens from returned stuff --- CppHeaderParser/CppHeaderParser.py | 1 + 1 file changed, 1 insertion(+) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index ec7d6c3..5108c07 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -2976,6 +2976,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): "mainClass", "_template_typenames", "braceDepth", + "stmtTokens", "typedefs_order", "curTemplate", ]: From 5e65a22267a31a9fe30ca5391b9d208f1b8a2d8b Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 5 Jan 2020 16:29:00 -0500 Subject: [PATCH 090/143] Properly process parameters with initializer lists --- CppHeaderParser/CppHeaderParser.py | 74 ++++++++++++++++++++---------- test/test_CppHeaderParser.py | 30 ++++++++++++ 2 files changed, 80 insertions(+), 24 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 5108c07..aca0fac 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -1043,20 +1043,31 @@ def __init__(self, nameStack, curClass, methinfo, curTemplate, doxygen, location # Find commas that are not nexted in <>'s like template types open_template_count = 0 open_paren_count = 0 + open_brace_count = 0 param_separator = 0 i = 0 for elm in paramsStack: - if "<" in elm: - open_template_count += 1 - elif ">" in elm: - open_template_count -= 1 - elif "(" in elm: - open_paren_count += 1 - elif ")" in elm: - open_paren_count -= 1 - elif elm == "," and open_template_count == 0 and open_paren_count == 0: - param_separator = i - break + if elm in "<>(){},": + if elm == ",": + if ( + open_template_count == 0 + and open_paren_count == 0 + and open_brace_count == 0 + ): + param_separator = i + break + elif "<" == elm: + open_template_count += 1 + elif ">" == elm: + open_template_count -= 1 + elif "(" == elm: + open_paren_count += 1 + elif ")" == elm: + open_paren_count -= 1 + elif "{" == elm: + open_brace_count += 1 + elif "}" == elm: + open_brace_count -= 1 i += 1 if param_separator: @@ -1215,9 +1226,11 @@ def __init__(self, nameStack, doxygen, location, **kwargs): elif "=" in nameStack: self["type"] = " ".join(nameStack[: nameStack.index("=") - 1]) self["name"] = nameStack[nameStack.index("=") - 1] - self["default"] = " ".join(nameStack[nameStack.index("=") + 1 :]) + default = " ".join(nameStack[nameStack.index("=") + 1 :]) + default = self._filter_name(default) + self["default"] = default # backwards compat; deprecate camelCase in dicts - self["defaultValue"] = self["default"] + self["defaultValue"] = default elif is_fundamental(nameStack[-1]) or nameStack[-1] in [">", "<", ":", "."]: # Un named parameter @@ -1228,12 +1241,7 @@ def __init__(self, nameStack, doxygen, location, **kwargs): self["type"] = " ".join(nameStack[:-1]) self["name"] = nameStack[-1] - self["type"] = self["type"].replace(" :", ":") - self["type"] = self["type"].replace(": ", ":") - self["type"] = self["type"].replace(" < ", "<") - self["type"] = self["type"].replace(" > ", "> ").replace(">>", "> >") - self["type"] = self["type"].replace(") >", ")>") - self["type"] = self["type"].replace(" ,", ",") + self["type"] = self._filter_name(self["type"]) # Optional doxygen description try: @@ -1244,6 +1252,15 @@ def __init__(self, nameStack, doxygen, location, **kwargs): self.init() CppVariable.Vars.append(self) # save and resolve later + def _filter_name(self, name): + name = name.replace(" :", ":").replace(": ", ":") + name = name.replace(" < ", "<") + name = name.replace(" > ", "> ").replace(">>", "> >") + name = name.replace(") >", ")>") + name = name.replace(" {", "{").replace(" }", "}") + name = name.replace(" ,", ",") + return name + def __str__(self): keys_white_list = [ "constant", @@ -2508,8 +2525,8 @@ def evalute_forward_decl(self): # fmt: off _namestack_append_tokens = { - "(", - ")", + "{", + "}", "[", "]", "=", @@ -2729,6 +2746,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self.braceHandled = False tok = None self.stmtTokens = [] + parenDepth = 0 try: while True: @@ -2779,7 +2797,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self.nameStack = [] continue - if tok.type == "{": + if parenDepth == 0 and tok.type == "{": if len(self.nameStack) >= 2 and is_namespace( self.nameStack ): # namespace {} with no name used in boost, this sets default? @@ -2830,7 +2848,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): if not self.braceHandled: self.braceDepth += 1 - elif tok.type == "}": + elif parenDepth == 0 and tok.type == "}": if self.braceDepth == 0: continue if self.braceDepth == len(self.nameSpaces): @@ -2862,7 +2880,6 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self.curClass = "" self.stack = [] self.stmtTokens = [] - elif tok.type in _namestack_append_tokens: self.nameStack.append(tok.value) nameStackAppended = True @@ -2922,6 +2939,15 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self.stack = [] self.nameStack = [] self.stmtTokens = [] + elif tok.type == "(": + parenDepth += 1 + self.nameStack.append(tok.value) + nameStackAppended = True + elif tok.type == ")": + self.nameStack.append(tok.value) + nameStackAppended = True + if parenDepth != 0: + parenDepth -= 1 newNsLen = len(self.nameStack) if nslen != newNsLen and newNsLen == 1: diff --git a/test/test_CppHeaderParser.py b/test/test_CppHeaderParser.py index 5ee3dc5..c78e810 100644 --- a/test/test_CppHeaderParser.py +++ b/test/test_CppHeaderParser.py @@ -3678,5 +3678,35 @@ def test_fn(self): self.assertEqual(fn["rtnType"], "std::vector *") +class ParamWithInitializer_TestCase(unittest.TestCase): + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader( + """ +template +void fn(something s = something{1,2,3}); +""", + "string", + ) + + def test_fn(self): + self.assertEqual(len(self.cppHeader.functions), 1) + fn = self.cppHeader.functions[0] + self.assertEqual(fn["name"], "fn") + self.assertEqual( + filter_pameters(fn["parameters"], ["namespace", "raw_type", "default"]), + [ + { + "type": "something", + "name": "s", + "desc": None, + "namespace": "", + "raw_type": "something", + "default": "something{ 1, 2, 3}", + }, + ], + ) + self.assertEqual(fn["rtnType"], "void") + + if __name__ == "__main__": unittest.main() From d444b580d972c7cc455af861a911d522ff21c340 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Mon, 6 Jan 2020 23:11:24 -0500 Subject: [PATCH 091/143] Add access specifier for nested classes --- CppHeaderParser/CppHeaderParser.py | 8 +++---- test/test_CppHeaderParser.py | 36 ++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index aca0fac..dc2201a 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -2428,12 +2428,9 @@ def _evaluate_class_stack(self): # dont support sub classes today # print( 'eval class stack', self.nameStack ) parent = self.curClass - if self.braceDepth > len(self.nameSpaces) and parent: - trace_print("HIT NESTED SUBCLASS") + if parent: + debug_print("found nested subclass") self.accessSpecifierStack.append(self.curAccessSpecifier) - elif self.braceDepth != len(self.nameSpaces): - error_print("ERROR: WRONG BRACE DEPTH") - return # When dealing with typedefed structs, get rid of typedef keyword to handle later on if self.nameStack[0] == "typedef": @@ -2478,6 +2475,7 @@ def _evaluate_class_stack(self): if parent: newClass["namespace"] = self.classes[parent]["namespace"] + "::" + parent newClass["parent"] = self.classes[parent] + newClass["access_in_parent"] = self.accessSpecifierStack[-1] self.classes[parent]["nested_classes"].append(newClass) ## supports nested classes with the same name ## self.curClass = key = parent + "::" + classKey diff --git a/test/test_CppHeaderParser.py b/test/test_CppHeaderParser.py index c78e810..4de05de 100644 --- a/test/test_CppHeaderParser.py +++ b/test/test_CppHeaderParser.py @@ -3708,5 +3708,41 @@ def test_fn(self): self.assertEqual(fn["rtnType"], "void") +class NestedClassAccess_TestCase(unittest.TestCase): + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader( + """ +class Outer { + struct Inner { + void fn(); + }; + + void ofn(); +}; +""", + "string", + ) + + def test_fn(self): + self.assertEqual(len(self.cppHeader.functions), 0) + + outer = self.cppHeader.classes["Outer"] + self.assertEqual(outer["parent"], None) + + self.assertEqual(0, len(outer["methods"]["public"])) + self.assertEqual(0, len(outer["methods"]["protected"])) + self.assertEqual(1, len(outer["methods"]["private"])) + self.assertEqual("ofn", outer["methods"]["private"][0]["name"]) + + inner = self.cppHeader.classes["Outer::Inner"] + self.assertIs(inner["parent"], outer) + self.assertEqual(inner["access_in_parent"], "private") + + self.assertEqual(1, len(inner["methods"]["public"])) + self.assertEqual(0, len(inner["methods"]["protected"])) + self.assertEqual(0, len(inner["methods"]["private"])) + self.assertEqual("fn", inner["methods"]["public"][0]["name"]) + + if __name__ == "__main__": unittest.main() From 46bb3d6d425468f1dbe94b615b0ac6e6a0d1df46 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Thu, 16 Jan 2020 01:49:59 -0500 Subject: [PATCH 092/143] Fix nested classes and unions --- CppHeaderParser/CppHeaderParser.py | 5 ++-- test/test_CppHeaderParser.py | 41 ++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index dc2201a..a13ec55 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -2871,8 +2871,9 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self.curAccessSpecifier = self.accessSpecifierStack[-1] self.accessSpecifierStack = self.accessSpecifierStack[:-1] if self.curClass and self.classes[self.curClass]["parent"]: - self.curClass = self.classes[self.curClass]["parent"][ - "name" + thisClass = self.classes[self.curClass] + self.curClass = self.curClass[ + : -(len(thisClass["name"]) + 2) ] else: self.curClass = "" diff --git a/test/test_CppHeaderParser.py b/test/test_CppHeaderParser.py index 4de05de..e62e5ce 100644 --- a/test/test_CppHeaderParser.py +++ b/test/test_CppHeaderParser.py @@ -3744,5 +3744,46 @@ def test_fn(self): self.assertEqual("fn", inner["methods"]["public"][0]["name"]) +class AnonUnion_TestCase(unittest.TestCase): + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader( + """ +struct Outer { + union { + int x; + int y; + }; + int z; +}; +""", + "string", + ) + + def test_fn(self): + self.assertEqual(len(self.cppHeader.functions), 0) + + outer = self.cppHeader.classes["Outer"] + self.assertEqual(outer["parent"], None) + + inner = self.cppHeader.classes["Outer::union "] + self.assertIs(inner["parent"], outer) + + self.assertEqual(2, len(outer["properties"]["public"])) + self.assertEqual(0, len(outer["properties"]["protected"])) + self.assertEqual(0, len(outer["properties"]["private"])) + + props = outer["properties"]["public"] + self.assertEqual(props[0]["name"], "") + self.assertEqual(props[1]["name"], "z") + + self.assertEqual(2, len(outer["properties"]["public"])) + self.assertEqual(0, len(outer["properties"]["protected"])) + self.assertEqual(0, len(outer["properties"]["private"])) + + props = inner["properties"]["public"] + self.assertEqual(props[0]["name"], "x") + self.assertEqual(props[1]["name"], "y") + + if __name__ == "__main__": unittest.main() From e6be5ec0de8b2e7424e3f4165f7fa6e5cf5423a9 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Thu, 16 Jan 2020 01:54:25 -0500 Subject: [PATCH 093/143] Make union naming more sensible - Breaks backwards compat --- CppHeaderParser/CppHeaderParser.py | 77 ++++++++++++++++-------------- test/test_CppHeaderParser.py | 30 +++++++----- 2 files changed, 59 insertions(+), 48 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index a13ec55..3d365b2 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -497,8 +497,13 @@ def _parse_cppclass_name(c, stack): c["namespace"] = "" # backwards compat - if name.startswith("anon-struct-"): - name = "<" + name + ">" + if name.startswith("anon-"): + if ( + name.startswith("anon-class-") + or name.startswith("anon-struct-") + or name.startswith("anon-union-") + ): + name = "<" + name + ">" c["name"] = name c["bare_name"] = name debug_print("Found class '%s'", name) @@ -820,7 +825,6 @@ class CppUnion(CppClass): def __init__(self, nameStack, doxygen, location): CppClass.__init__(self, nameStack, None, doxygen, location) - self["name"] = "union " + self["name"] self["members"] = self["properties"]["public"] def transform_to_union_keys(self): @@ -2435,14 +2439,24 @@ def _evaluate_class_stack(self): # When dealing with typedefed structs, get rid of typedef keyword to handle later on if self.nameStack[0] == "typedef": del self.nameStack[0] - if len(self.nameStack) == 1: + + if len(self.nameStack) == 1: + if self.nameStack[0] == "struct": self.anon_struct_counter += 1 # We cant handle more than 1 anonymous struct, so name them uniquely self.nameStack.append("anon-struct-%d" % self.anon_struct_counter) + elif self.nameStack[0] == "union": + self.anon_union_counter += 1 + # We cant handle more than 1 anonymous union, so name them uniquely + self.nameStack.append("anon-union-%d" % self.anon_union_counter) + elif self.nameStack[0] == "class": + self.anon_class_counter += 1 + # We cant handle more than 1 anonymous class, so name them uniquely + self.nameStack.append("anon-class-%d" % self.anon_class_counter) if self.nameStack[0] == "class": self.curAccessSpecifier = "private" - else: # struct + else: # struct/union self.curAccessSpecifier = "public" debug_print( "curAccessSpecifier changed/defaulted to %s", self.curAccessSpecifier @@ -2453,11 +2467,6 @@ def _evaluate_class_stack(self): self._get_stmt_doxygen(), self._get_location(self.nameStack), ) - if newClass["name"] == "union ": - self.anon_union_counter = [self.braceDepth, 2] - else: - self.anon_union_counter = [self.braceDepth, 1] - trace_print("NEW UNION", newClass["name"]) else: newClass = CppClass( self.nameStack, @@ -2465,7 +2474,6 @@ def _evaluate_class_stack(self): self._get_stmt_doxygen(), self._get_location(self.nameStack), ) - trace_print("NEW CLASS", newClass["name"]) newClass["declaration_method"] = self.nameStack[0] self.classes_order.append(newClass) # good idea to save ordering self.stack = [] # fixes if class declared with ';' in closing brace @@ -2639,7 +2647,8 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): # Old namestacks for a given level self.nameStackHistory = [] self.anon_struct_counter = 0 - self.anon_union_counter = [-1, 0] + self.anon_union_counter = 0 + self.anon_class_counter = 0 #: Using directives in this header outside of class scope: key is #: full name for lookup, value is :class:`.CppVariable` @@ -2751,11 +2760,6 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): tok = lex.token(eof_ok=True) if not tok: break - if ( - self.anon_union_counter[0] == self.braceDepth - and self.anon_union_counter[1] - ): - self.anon_union_counter[1] -= 1 tok.value = TagStr(tok.value, location=tok.location) # debug_print("TOK: %s", tok) @@ -2875,6 +2879,24 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self.curClass = self.curClass[ : -(len(thisClass["name"]) + 2) ] + + # Detect anonymous union members + if ( + self.curClass + and thisClass["declaration_method"] == "union" + and thisClass["name"].startswith("<") + and self.lex.token_if(";") + ): + debug_print("Creating anonymous union") + # Force the processing of an anonymous union + self.nameStack = [""] + self.stack = self.nameStack + [";"] + debug_print("pre eval anon stack") + self._evaluate_stack(";") + debug_print("post eval anon stack") + self.stack = [] + self.nameStack = [] + self.stmtTokens = [] else: self.curClass = "" self.stack = [] @@ -2891,8 +2913,6 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self.nameStack.append(tok.value) else: self.nameStack.append(tok.value) - if self.anon_union_counter[0] == self.braceDepth: - self.anon_union_counter = [-1, 0] elif tok.type == ":": if self.nameStack and self.nameStack[0] in supportedAccessSpecifier: specifier = " ".join(self.nameStack) @@ -2916,24 +2936,6 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self.nameStack.append(tok.value) elif tok.type == ";": - if ( - self.anon_union_counter[0] == self.braceDepth - and self.anon_union_counter[1] - ): - debug_print("Creating anonymous union") - # Force the processing of an anonymous union - saved_namestack = self.nameStack[:] - saved_stack = self.stack[:] - self.nameStack = [""] - self.stack = self.nameStack + [";"] - self.nameStack = self.nameStack[0:1] - debug_print("pre eval anon stack") - self._evaluate_stack(tok.type) - debug_print("post eval anon stack") - self.nameStack = saved_namestack - self.stack = saved_stack - self.anon_union_counter = [-1, 0] - self._evaluate_stack(tok.type) self.stack = [] self.nameStack = [] @@ -2995,6 +2997,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): "nameStackHistory", "anon_struct_counter", "anon_union_counter", + "anon_class_counter", "_classes_brace_level", "_forward_decls", "stack", diff --git a/test/test_CppHeaderParser.py b/test/test_CppHeaderParser.py index e62e5ce..fce3db7 100644 --- a/test/test_CppHeaderParser.py +++ b/test/test_CppHeaderParser.py @@ -855,7 +855,7 @@ def test_property(self): "constant": 0, "name": "a", "reference": 0, - "type": "", + "type": "", "static": 0, "pointer": 0, } @@ -1037,7 +1037,7 @@ def test_property(self): "constant": 0, "name": "u", "reference": 0, - "type": "union HogUnion", + "type": "HogUnion", "static": 0, "pointer": 0, } @@ -1051,13 +1051,13 @@ def test_property(self): def test_union(self): cmp_values = { - "name": "union HogUnion", + "name": "HogUnion", "parent": self.cppHeader.classes["HogClass"], "declaration_method": "union", } self.assertEqual( filter_dict_keys( - self.cppHeader.classes["HogClass::union HogUnion"], cmp_values.keys() + self.cppHeader.classes["HogClass::HogUnion"], cmp_values.keys() ), cmp_values, ) @@ -1073,7 +1073,7 @@ def test_union_member_a(self): } self.assertEqual( filter_dict_keys( - self.cppHeader.classes["HogClass::union HogUnion"]["members"][0], + self.cppHeader.classes["HogClass::HogUnion"]["members"][0], cmp_values.keys(), ), cmp_values, @@ -1090,7 +1090,7 @@ def test_union_member_b(self): } self.assertEqual( filter_dict_keys( - self.cppHeader.classes["HogClass::union HogUnion"]["members"][1], + self.cppHeader.classes["HogClass::HogUnion"]["members"][1], cmp_values.keys(), ), cmp_values, @@ -1891,6 +1891,14 @@ class Beans_TestCase(unittest.TestCase): def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") + def test_public_props(self): + self.assertEqual( + len(self.cppHeader.classes["Beans"]["properties"]["public"]), 4 + ) + self.assertEqual( + self.cppHeader.classes["Beans"]["properties"]["public"][2]["name"], "data" + ) + def test_anonymous_union_name(self): return self.assertEqual( self.cppHeader.classes["Beans"]["properties"]["public"][1]["name"], "" @@ -2179,11 +2187,11 @@ def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_anon_struct_1_exists(self): - self.assertEqual("" in self.cppHeader.classes, True) + self.assertEqual("" in self.cppHeader.classes, True) def test_beta_exists(self): self.assertEqual( - self.cppHeader.classes[""]["properties"]["public"][0][ + self.cppHeader.classes[""]["properties"]["public"][0][ "name" ], "anon_struct_variable", @@ -2469,7 +2477,7 @@ def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") def test_Olive_exists(self): - self.assertEqual("union olive" in self.cppHeader.classes, True) + self.assertEqual("olive" in self.cppHeader.classes, True) def test_union_member_x(self): cmp_values = { @@ -2482,7 +2490,7 @@ def test_union_member_x(self): } self.assertEqual( filter_dict_keys( - self.cppHeader.classes["union olive"]["members"][0], cmp_values.keys() + self.cppHeader.classes["olive"]["members"][0], cmp_values.keys() ), cmp_values, ) @@ -3765,7 +3773,7 @@ def test_fn(self): outer = self.cppHeader.classes["Outer"] self.assertEqual(outer["parent"], None) - inner = self.cppHeader.classes["Outer::union "] + inner = self.cppHeader.classes["Outer::"] self.assertIs(inner["parent"], outer) self.assertEqual(2, len(outer["properties"]["public"])) From 57770dfc0a41ca94e873ff4fc193290711c8eb05 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Thu, 16 Jan 2020 01:55:43 -0500 Subject: [PATCH 094/143] Update debugging --- CppHeaderParser/CppHeaderParser.py | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 3d365b2..0eb1241 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -98,21 +98,35 @@ def warning_print(fmt, *args): print(fmt % args) -def debug_print(fmt, *args): - if debug: +if debug: + + def debug_print(fmt, *args): fmt = "[%4d] " + fmt args = (inspect.currentframe().f_back.f_lineno,) + args print(fmt % args) -def trace_print(*args): - if debug_trace: +else: + + def debug_print(fmt, *args): + pass + + +if debug_trace: + + def trace_print(*args): sys.stdout.write("[%s] " % (inspect.currentframe().f_back.f_lineno)) for a in args: sys.stdout.write("%s " % a) sys.stdout.write("\n") +else: + + def trace_print(*args): + pass + + #: Access specifiers supportedAccessSpecifier = ["public", "protected", "private"] @@ -2861,10 +2875,10 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self._evaluate_stack() self.braceDepth -= 1 - if self.curClass: - debug_print( - "CURBD %s", self._classes_brace_level[self.curClass] - ) + # if self.curClass: + # debug_print( + # "CURBD %s", self._classes_brace_level[self.curClass] + # ) if (self.braceDepth == 0) or ( self.curClass From 69017114baa2cf01e26544b487b703f8da525228 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Thu, 23 Jan 2020 04:13:59 -0500 Subject: [PATCH 095/143] Fix default access for structs/union base classes --- CppHeaderParser/CppHeaderParser.py | 19 ++++++++++--------- test/test_CppHeaderParser.py | 4 ++-- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 0eb1241..536e7a7 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -384,8 +384,8 @@ class CppBaseDecl(dict): """ - def __init__(self): - self["access"] = "private" + def __init__(self, default_access): + self["access"] = default_access self["class"] = "" self["decl_name"] = "" self["decltype"] = False @@ -530,13 +530,13 @@ def _parse_cppclass_name(c, stack): return i -def _parse_cpp_base(stack): - debug_print("Parsing base: %s", stack) +def _parse_cpp_base(stack, default_access): + debug_print("Parsing base: %s (access %s)", stack, default_access) inherits = [] i = 0 sl = len(stack) init = True - base = CppBaseDecl() + base = CppBaseDecl(default_access) require_ending = False while i < sl: t = stack[i] @@ -558,7 +558,7 @@ def _parse_cpp_base(stack): if t == ",": inherits.append(base) - base = CppBaseDecl() + base = CppBaseDecl(default_access) init = True require_ending = False continue @@ -681,7 +681,7 @@ def _lookup_type(self, name): if n["name"] == name: return {"raw_type": self["name"] + "::" + n["name"], "type": n["name"]} - def __init__(self, nameStack, curTemplate, doxygen, location): + def __init__(self, nameStack, curTemplate, doxygen, location, defaultAccess): self["nested_classes"] = [] self["parent"] = None self["abstract"] = False @@ -706,7 +706,7 @@ def __init__(self, nameStack, curTemplate, doxygen, location): # consume bases baseStack = nameStack[n:] if baseStack: - self["inherits"] = _parse_cpp_base(baseStack) + self["inherits"] = _parse_cpp_base(baseStack, defaultAccess) else: self["inherits"] = [] @@ -838,7 +838,7 @@ class CppUnion(CppClass): """ def __init__(self, nameStack, doxygen, location): - CppClass.__init__(self, nameStack, None, doxygen, location) + CppClass.__init__(self, nameStack, None, doxygen, location, "public") self["members"] = self["properties"]["public"] def transform_to_union_keys(self): @@ -2487,6 +2487,7 @@ def _evaluate_class_stack(self): self.curTemplate, self._get_stmt_doxygen(), self._get_location(self.nameStack), + self.curAccessSpecifier, ) newClass["declaration_method"] = self.nameStack[0] self.classes_order.append(newClass) # good idea to save ordering diff --git a/test/test_CppHeaderParser.py b/test/test_CppHeaderParser.py index fce3db7..8e1a775 100644 --- a/test/test_CppHeaderParser.py +++ b/test/test_CppHeaderParser.py @@ -3187,7 +3187,7 @@ def testBuildIndexImpl1(self): self.assertEqual( [ { - "access": "private", + "access": "public", "class": "build_index_impl", "decltype": False, "decl_name": "build_index_impl", @@ -3217,7 +3217,7 @@ def testBuildIndexImpl2(self): self.assertEqual( [ { - "access": "private", + "access": "public", "class": "index_sequence", "decltype": False, "decl_name": "index_sequence", From 561449c9a2f5dc0b41ae4e4128bde9db27c83989 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 2 Feb 2020 00:33:10 -0500 Subject: [PATCH 096/143] Detect deleted functions --- CppHeaderParser/CppHeaderParser.py | 14 ++++++-------- test/test_CppHeaderParser.py | 16 ++++++++++++++++ 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 536e7a7..9686d4e 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -2091,7 +2091,7 @@ def finalize(self): _method_type_defaults = { n: False - for n in "defined pure_virtual operator constructor destructor extern template virtual static explicit inline friend returns returns_pointer returns_fundamental returns_class default".split() + for n in "defined deleted pure_virtual operator constructor destructor extern template virtual static explicit inline friend returns returns_pointer returns_fundamental returns_class default".split() } def parse_method_type(self, stack): @@ -2131,13 +2131,11 @@ def parse_method_type(self, stack): else: assert 0 - if ( - len(stack) > 3 - and stack[-1] == ";" - and stack[-2] == "0" - and stack[-3] == "=" - ): - info["pure_virtual"] = True + if len(stack) > 3 and stack[-1] == ";" and stack[-3] == "=": + if stack[-2] == "0": + info["pure_virtual"] = True + elif stack[-2] == "delete": + info["deleted"] = True r = header.split() name = None diff --git a/test/test_CppHeaderParser.py b/test/test_CppHeaderParser.py index 8e1a775..f61acc2 100644 --- a/test/test_CppHeaderParser.py +++ b/test/test_CppHeaderParser.py @@ -3792,6 +3792,22 @@ def test_fn(self): self.assertEqual(props[0]["name"], "x") self.assertEqual(props[1]["name"], "y") +class Deleted_TestCase(unittest.TestCase): + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader( + """ +class A { +public: + A() = delete; +}; +""", + "string", + ) + + def test_fn(self): + m = self.cppHeader.classes["A"]["methods"]["public"][0] + self.assertEqual(m["constructor"], True) + self.assertEqual(m["deleted"], True) if __name__ == "__main__": unittest.main() From 32fb1bb34898bba5f12be1dd0a76b4703f4e17c9 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Tue, 3 Mar 2020 12:02:03 -0500 Subject: [PATCH 097/143] Fix base classes with namespaces - Fixes #39 --- CppHeaderParser/CppHeaderParser.py | 13 ++++++++++--- test/test_CppHeaderParser.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 9686d4e..56739b0 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -408,7 +408,7 @@ def _fix_classname(self): if self["..."]: s += "..." - self["class"] = s + return s def _consume_parens(stack): @@ -564,7 +564,14 @@ def _parse_cpp_base(stack, default_access): continue if require_ending: - raise CppParseError("expected comma, found '%s'" % t) + if t == "::": + if "decl_params" in base: + base["decl_name"] = base._fix_classname() + del base["decl_params"] + base["..."] + require_ending = False + else: + raise CppParseError("expected comma, found '%s'" % t) if t == "(": s = stack[i:] @@ -585,7 +592,7 @@ def _parse_cpp_base(stack, default_access): # backwards compat inherits.append(base) for base in inherits: - base._fix_classname() + base["class"] = base._fix_classname() return inherits diff --git a/test/test_CppHeaderParser.py b/test/test_CppHeaderParser.py index f61acc2..d23cb23 100644 --- a/test/test_CppHeaderParser.py +++ b/test/test_CppHeaderParser.py @@ -3792,6 +3792,7 @@ def test_fn(self): self.assertEqual(props[0]["name"], "x") self.assertEqual(props[1]["name"], "y") + class Deleted_TestCase(unittest.TestCase): def setUp(self): self.cppHeader = CppHeaderParser.CppHeader( @@ -3809,5 +3810,33 @@ def test_fn(self): self.assertEqual(m["constructor"], True) self.assertEqual(m["deleted"], True) + +class BaseTemplateNs_TestCase(unittest.TestCase): + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader( + """ +class A : public B::C {}; +""", + "string", + ) + + def test_fn(self): + c = self.cppHeader.classes["A"] + self.assertEqual("A", c["name"]) + self.assertEqual( + [ + { + "access": "public", + "class": "B::C", + "decl_name": "B::C", + "virtual": False, + "...": False, + "decltype": False, + } + ], + c["inherits"], + ) + + if __name__ == "__main__": unittest.main() From f19ca9310796de3d621ddf3049fd4a4416a200e7 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Wed, 4 Mar 2020 02:01:22 -0500 Subject: [PATCH 098/143] Fix typedef thing - Fixes #41 --- CppHeaderParser/CppHeaderParser.py | 2 +- test/test_CppHeaderParser.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 56739b0..7b07900 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -1685,7 +1685,7 @@ def finalize_vars(self): elif nestedTypedef: var["fundamental"] = is_fundamental(nestedTypedef) - if not var["fundamental"]: + if not var["fundamental"] and "method" in var: var["raw_type"] = var["method"]["path"] + "::" + tag else: diff --git a/test/test_CppHeaderParser.py b/test/test_CppHeaderParser.py index d23cb23..a876d87 100644 --- a/test/test_CppHeaderParser.py +++ b/test/test_CppHeaderParser.py @@ -3838,5 +3838,35 @@ def test_fn(self): ) +class NestedTypedef(unittest.TestCase): + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader( + """ +template class A { + public: + typedef B C; + + A(); + + protected: + C aCInstance; +}; +""", + "string", + ) + + def test_fn(self): + c = self.cppHeader.classes["A"] + self.assertEqual("A", c["name"]) + + self.assertEqual(0, len(c["properties"]["public"])) + self.assertEqual(1, len(c["properties"]["protected"])) + self.assertEqual(0, len(c["properties"]["private"])) + + c = c["properties"]["protected"][0] + self.assertEqual(c["name"], "aCInstance") + self.assertEqual(c["type"], "C") + + if __name__ == "__main__": unittest.main() From 98288433e6286445a6214a75587638ca7c51e827 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Wed, 4 Mar 2020 10:43:32 -0500 Subject: [PATCH 099/143] Fix #43 --- CppHeaderParser/CppHeaderParser.py | 1 + test/test_CppHeaderParser.py | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 7b07900..cb8fda2 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -1957,6 +1957,7 @@ def finalize(self): "static": 0, "pointer": 0, "reference": 0, + "typedefs": 0, } self.resolve_type(meth["rtnType"], rtnType) if not rtnType["unresolved"]: diff --git a/test/test_CppHeaderParser.py b/test/test_CppHeaderParser.py index a876d87..d3f54e7 100644 --- a/test/test_CppHeaderParser.py +++ b/test/test_CppHeaderParser.py @@ -3868,5 +3868,30 @@ def test_fn(self): self.assertEqual(c["type"], "C") +class MoreTypedef(unittest.TestCase): + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader( + """ +typedef C A; + +class B { +public: + A aMethod(); +}; +""", + "string", + ) + + def test_fn(self): + c = self.cppHeader.classes["B"] + self.assertEqual("B", c["name"]) + + m = c["methods"]["public"][0] + self.assertEqual(m["name"], "aMethod") + self.assertEqual(m["rtnType"], "A") + + self.assertEqual(self.cppHeader.typedefs["A"], "C") + + if __name__ == "__main__": unittest.main() From da87e4fb93f20b0e563419aa5910b45124c2bf49 Mon Sep 17 00:00:00 2001 From: Garden Tools Date: Tue, 24 Mar 2020 00:48:04 +0000 Subject: [PATCH 100/143] Retaining line number information for #define #pragma and #include --- CppHeaderParser/CppHeaderParser.py | 56 +++++++++++++++++++++++++++--- test/test_CppHeaderParser.py | 12 +++++++ 2 files changed, 63 insertions(+), 5 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index cb8fda2..db2cce5 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -1384,6 +1384,37 @@ def __init__(self, name, doxygen, location): set_location_info(self, location) +class _CppPreprocessorLiteral(dict): + """ Implementation for #pragmas, #defines and #includes, contains the + following keys: + + * ``value`` the value literal of the preprocessor item + * ``line_number`` line number at which the item was found + """ + + def __init__(self, macro, location): + self["value"] = re.split("[\t ]+", macro, 1)[1].strip() + set_location_info(self, location) + + def __str__(self): + return self["value"] + + +# Implementation is shared between CppPragma, CppDefine, CppInclude but they are +# distinct so that they may diverge if required without interface-breaking +# changes +class CppPragma(_CppPreprocessorLiteral): + pass + + +class CppDefine(_CppPreprocessorLiteral): + pass + + +class CppInclude(_CppPreprocessorLiteral): + pass + + C99_NONSTANDARD = { "int8": "signed char", "int16": "short int", @@ -1905,19 +1936,25 @@ def finalize_vars(self): # Take care of #defines and #pragmas etc trace_print("Processing precomp_macro_buf: %s" % self._precomp_macro_buf) - for m in self._precomp_macro_buf: + for m, location in self._precomp_macro_buf: macro = m.replace("\\n", "\n") ml = macro.lower() try: if ml.startswith("#define"): trace_print("Adding #define %s" % macro) - self.defines.append(re.split("[\t ]+", macro, 1)[1].strip()) + define = CppDefine(macro, location) + self.defines.append(define["value"]) + self.defines_detail.append(define) elif ml.startswith("#pragma"): trace_print("Adding #pragma %s" % macro) - self.pragmas.append(re.split("[\t ]+", macro, 1)[1].strip()) + pragma = CppPragma(macro, location) + self.pragmas_detail.append(pragma) + self.pragmas.append(pragma["value"]) elif ml.startswith("#include"): trace_print("Adding #include %s" % macro) - self.includes.append(re.split("[\t ]+", macro, 1)[1].strip()) + include = CppInclude(macro, location) + self.includes.append(include["value"]) + self.includes_detail.append(include) else: debug_print("Cant detect what to do with precomp macro '%s'", macro) except: @@ -2635,12 +2672,21 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): #: List of #pragma directives found as strings self.pragmas = [] + #: List of pragmas with location information + self.pragmas_detail = [] + #: List of #define directives found self.defines = [] + #: List of #define directives found, with location information + self.defines_detail = [] + #: List of #include directives found self.includes = [] + #: List of #include directives found with location information + self.includes_detail = [] + #: Filenames encountered in #line directives while parsing self.headerFileNames = [] @@ -2814,7 +2860,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): if tok.type in ("PRECOMP_MACRO", "PRECOMP_MACRO_CONT"): debug_print("PRECOMP: %s", tok) - self._precomp_macro_buf.append(tok.value) + self._precomp_macro_buf.append((tok.value, tok.location)) self.stack = [] self.stmtTokens = [] self.nameStack = [] diff --git a/test/test_CppHeaderParser.py b/test/test_CppHeaderParser.py index d3f54e7..99dc8cf 100644 --- a/test/test_CppHeaderParser.py +++ b/test/test_CppHeaderParser.py @@ -1869,15 +1869,27 @@ def setUp(self): def test_includes(self): self.assertEqual(self.cppHeader.includes, ["", '"../../debug.h"']) + self.assertEqual(self.cppHeader.includes_detail[0]["value"], "") + self.assertEqual(self.cppHeader.includes_detail[0]["line_number"], 2) + self.assertEqual(self.cppHeader.includes_detail[1]["value"], '"../../debug.h"') + self.assertEqual(self.cppHeader.includes_detail[1]["line_number"], 3) def test_pragmas(self): self.assertEqual(self.cppHeader.pragmas, ["once"]) + self.assertEqual(self.cppHeader.pragmas_detail[0]["value"], "once") + self.assertEqual(self.cppHeader.pragmas_detail[0]["line_number"], 7) def test_pragmas0(self): self.assertEqual(self.cppHeader.defines[0], "ONE 1") + self.assertEqual(self.cppHeader.defines_detail[0]["value"], "ONE 1") + self.assertEqual(self.cppHeader.defines_detail[0]["line_number"], 5) def test_pragmas1(self): self.assertEqual(self.cppHeader.defines[1], 'TWO_NUM_N_NAME "2 (TWO)"') + self.assertEqual( + self.cppHeader.defines_detail[1]["value"], 'TWO_NUM_N_NAME "2 (TWO)"' + ) + self.assertEqual(self.cppHeader.defines_detail[1]["line_number"], 6) def test_pragmas2(self): self.assertEqual( From 1fece2e559c48c9c99e9a8ef47c6b1ba362d6e0f Mon Sep 17 00:00:00 2001 From: Garden Tools Date: Tue, 24 Mar 2020 00:55:02 +0000 Subject: [PATCH 101/143] The travis docs indicate that "sudo" is deprecated and that you shouldn't have both "matrix" and "jobs" --- .travis.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index d57fc60..f31c480 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,4 @@ language: python -sudo: required dist: xenial python: @@ -8,18 +7,16 @@ python: - "3.5" - "2.7" -matrix: - fast_finish: true jobs: include: - stage: format-check - python: - - "3.6" + python: "3.6" install: - pip install black script: - black --check --diff . + fast_finish: true # command to install dependencies install: From c23af0bb7655ec1d740281cd0bedb51be3f0b2f1 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Wed, 16 Sep 2020 19:06:32 -0400 Subject: [PATCH 102/143] Update black formatting --- CppHeaderParser/CppHeaderParser.py | 249 +++++++++++++++-------------- CppHeaderParser/doxygen.py | 2 +- CppHeaderParser/lexer.py | 8 +- test/test_CppHeaderParser.py | 3 +- 4 files changed, 132 insertions(+), 130 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index db2cce5..f0951ad 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -266,10 +266,10 @@ def set_location_info(thing, location): def _split_namespace(namestack): """ - Given a list of name elements, find the namespace portion - and return that as a string + Given a list of name elements, find the namespace portion + and return that as a string - :rtype: Tuple[str, list] + :rtype: Tuple[str, list] """ # TODO: this should be using tokens instead of nhack @@ -293,9 +293,9 @@ def _split_namespace(namestack): def _iter_ns_str_reversed(namespace): """ - Take a namespace string, and yield successively smaller portions - of it (ex foo::bar::baz::, foo::bar::, foo::). The last item yielded - will always be an empty string + Take a namespace string, and yield successively smaller portions + of it (ex foo::bar::baz::, foo::bar::, foo::). The last item yielded + will always be an empty string """ if namespace: parts = namespace.split("::") @@ -334,13 +334,13 @@ def __init__(self, msg, tok=None): class CppTemplateParam(dict): """ - Dictionary that contains the following: + Dictionary that contains the following: - - ``decltype`` - If True, this is a decltype - - ``param`` - Parameter value or typename - - ``params`` - Only present if a template specialization. Is a list of - :class:`.CppTemplateParam` - - ``...`` - If True, indicates a parameter pack + - ``decltype`` - If True, this is a decltype + - ``param`` - Parameter value or typename + - ``params`` - Only present if a template specialization. Is a list of + :class:`.CppTemplateParam` + - ``...`` - If True, indicates a parameter pack """ def __init__(self): @@ -370,17 +370,17 @@ def __str__(self): class CppBaseDecl(dict): """ - Dictionary that contains the following - - - ``access`` - Anything in supportedAccessSpecifier - - ``class`` - Name of the type, along with template specializations - - ``decl_name`` - Name of the type only - - ``decl_params`` - Only present if a template specialization (Foo). - Is a list of :class:`.CppTemplateParam`. - - ``decltype`` - True/False indicates a decltype, the contents are in - ``decl_name`` - - ``virtual`` - True/False indicates virtual inheritance - - ``...`` - True/False indicates a parameter pack + Dictionary that contains the following + + - ``access`` - Anything in supportedAccessSpecifier + - ``class`` - Name of the type, along with template specializations + - ``decl_name`` - Name of the type only + - ``decl_params`` - Only present if a template specialization (Foo). + Is a list of :class:`.CppTemplateParam`. + - ``decltype`` - True/False indicates a decltype, the contents are in + ``decl_name`` + - ``virtual`` - True/False indicates virtual inheritance + - ``...`` - True/False indicates a parameter pack """ @@ -599,50 +599,50 @@ def _parse_cpp_base(stack, default_access): class CppClass(dict): """ - Dictionary that contains at least the following keys: - - * ``name`` - Name of the class - * ``doxygen`` - Doxygen comments associated with the class if they exist - * ``inherits`` - List of Classes that this one inherits. Values are - :class:`.CppBaseDecl` - * ``methods`` - Dictionary where keys are from supportedAccessSpecifier - and values are a lists of :class:`.CppMethod` - * ``namespace`` - Namespace of class - * ``properties`` - Dictionary where keys are from supportedAccessSpecifier - and values are lists of :class:`.CppVariable` - * ``enums`` - Dictionary where keys are from supportedAccessSpecifier and - values are lists of :class:`.CppEnum` - * ``nested_classes`` - Classes and structs defined within this class - * ``final`` - True if final - * ``abstract`` - True if abstract - * ``using`` - Using directives in this class scope: key is name for lookup, - value is :class:`.CppVariable` - * ``parent`` - If not None, the class that this class is nested in - - An example of how this could look is as follows:: - + Dictionary that contains at least the following keys: + + * ``name`` - Name of the class + * ``doxygen`` - Doxygen comments associated with the class if they exist + * ``inherits`` - List of Classes that this one inherits. Values are + :class:`.CppBaseDecl` + * ``methods`` - Dictionary where keys are from supportedAccessSpecifier + and values are a lists of :class:`.CppMethod` + * ``namespace`` - Namespace of class + * ``properties`` - Dictionary where keys are from supportedAccessSpecifier + and values are lists of :class:`.CppVariable` + * ``enums`` - Dictionary where keys are from supportedAccessSpecifier and + values are lists of :class:`.CppEnum` + * ``nested_classes`` - Classes and structs defined within this class + * ``final`` - True if final + * ``abstract`` - True if abstract + * ``using`` - Using directives in this class scope: key is name for lookup, + value is :class:`.CppVariable` + * ``parent`` - If not None, the class that this class is nested in + + An example of how this could look is as follows:: + + { + 'name': "" + 'inherits':[] + 'methods': { - 'name': "" - 'inherits':[] - 'methods': - { - 'public':[], - 'protected':[], - 'private':[] - }, - 'properties': - { - 'public':[], - 'protected':[], - 'private':[] - }, - 'enums': - { - 'public':[], - 'protected':[], - 'private':[] - } + 'public':[], + 'protected':[], + 'private':[] + }, + 'properties': + { + 'public':[], + 'protected':[], + 'private':[] + }, + 'enums': + { + 'public':[], + 'protected':[], + 'private':[] } + } """ def get_all_methods(self): @@ -837,11 +837,11 @@ def __str__(self): class CppUnion(CppClass): """ - Dictionary that contains at least the following keys: + Dictionary that contains at least the following keys: - * ``name`` - Name of the union - * ``doxygen`` - Doxygen comments associated with the union if they exist - * ``members`` - List of members of the union + * ``name`` - Name of the union + * ``doxygen`` - Doxygen comments associated with the union if they exist + * ``members`` - List of members of the union """ def __init__(self, nameStack, doxygen, location): @@ -953,13 +953,13 @@ def _params_helper2(self, params): class CppMethod(_CppMethod): """ - Dictionary that contains at least the following keys: + Dictionary that contains at least the following keys: - * ``rtnType`` - Return type of the method (ex. "int") - * ``name`` - Name of the method - * ``doxygen`` - Doxygen comments associated with the method if they exist - * ``parameters`` - List of :class:`.CppVariable` - * ``parent`` - If not None, the class this method belongs to + * ``rtnType`` - Return type of the method (ex. "int") + * ``name`` - Name of the method + * ``doxygen`` - Doxygen comments associated with the method if they exist + * ``parameters`` - List of :class:`.CppVariable` + * ``parent`` - If not None, the class this method belongs to """ def show(self): @@ -1173,18 +1173,18 @@ def init(self): class CppVariable(_CppVariable): """ - Dictionary that contains at least the following keys: - - * ``type`` - Type for the variable (ex. "const string &") - * ``name`` - Name of the variable (ex. "numItems") - * ``namespace`` - Namespace - * ``desc`` - If a method/function parameter, doxygen description for this parameter (optional) - * ``doxygen`` - If a normal property/variable, doxygen description for this - * ``default`` - Default value of the variable, this key will only - exist if there is a default value - * ``extern`` - True if its an extern, False if not - * ``parent`` - If not None, either the class this is a property of, or the - method this variable is a parameter in + Dictionary that contains at least the following keys: + + * ``type`` - Type for the variable (ex. "const string &") + * ``name`` - Name of the variable (ex. "numItems") + * ``namespace`` - Namespace + * ``desc`` - If a method/function parameter, doxygen description for this parameter (optional) + * ``doxygen`` - If a normal property/variable, doxygen description for this + * ``default`` - Default value of the variable, this key will only + exist if there is a default value + * ``extern`` - True if its an extern, False if not + * ``parent`` - If not None, either the class this is a property of, or the + method this variable is a parameter in """ Vars = [] @@ -1308,7 +1308,7 @@ class _CppEnum(dict): def resolve_enum_values(self, values): """Evaluates the values list of dictionaries passed in and figures out what the enum value for each enum is editing in place: - + Example From: [{'name': 'ORANGE'}, {'name': 'RED'}, @@ -1365,9 +1365,9 @@ class CppEnum(_CppEnum): * ``name`` - Name of the enum (ex. "ItemState") * ``namespace`` - Namespace containing the enum - * ``values`` - List of values. The values are a dictionary with + * ``values`` - List of values. The values are a dictionary with the following key/values: - + - ``name`` - name of the key (ex. "PARSING_HEADER"), - ``value`` - Specified value of the enum, this key will only exist if a value for a given enum value was defined @@ -1385,11 +1385,11 @@ def __init__(self, name, doxygen, location): class _CppPreprocessorLiteral(dict): - """ Implementation for #pragmas, #defines and #includes, contains the - following keys: + """Implementation for #pragmas, #defines and #includes, contains the + following keys: - * ``value`` the value literal of the preprocessor item - * ``line_number`` line number at which the item was found + * ``value`` the value literal of the preprocessor item + * ``line_number`` line number at which the item was found """ def __init__(self, macro, location): @@ -2638,7 +2638,7 @@ def show(self): def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): """Create the parsed C++ header file parse tree - + headerFileName - Name of the file to parse OR actual file contents (depends on argType) argType - Indicates how to interpret headerFileName as a file string or file name kwargs - Supports the following keywords @@ -3045,7 +3045,8 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): ) raise_exc( - CppParseError(msg), e, + CppParseError(msg), + e, ) self.finalize() @@ -3172,16 +3173,16 @@ def _discard_contents(self, start_type, end_type): def _discard_ctor_initializer(self): """ - ctor_initializer: ":" mem_initializer_list + ctor_initializer: ":" mem_initializer_list + + mem_initializer_list: mem_initializer ["..."] + | mem_initializer "," mem_initializer_list ["..."] - mem_initializer_list: mem_initializer ["..."] - | mem_initializer "," mem_initializer_list ["..."] + mem_initializer: mem_initializer_id "(" [expression_list] ")" + | mem_initializer_id braced_init_list - mem_initializer: mem_initializer_id "(" [expression_list] ")" - | mem_initializer_id braced_init_list - - mem_initializer_id: class_or_decltype - | IDENTIFIER + mem_initializer_id: class_or_decltype + | IDENTIFIER """ debug_print("discarding ctor intializer") # all of this is discarded.. the challenge is to determine @@ -3451,19 +3452,19 @@ def _parse_attribute_specifier_seq(self, tok): def _parse_enum(self): """ - opaque_enum_declaration: enum_key [attribute_specifier_seq] IDENTIFIER [enum_base] ";" - - enum_specifier: enum_head "{" [enumerator_list] "}" - | enum_head "{" enumerator_list "," "}" - - enum_head: enum_key [attribute_specifier_seq] [IDENTIFIER] [enum_base] - | enum_key [attribute_specifier_seq] nested_name_specifier IDENTIFIER [enum_base] - - enum_key: "enum" - | "enum" "class" - | "enum" "struct" - - enum_base: ":" type_specifier_seq + opaque_enum_declaration: enum_key [attribute_specifier_seq] IDENTIFIER [enum_base] ";" + + enum_specifier: enum_head "{" [enumerator_list] "}" + | enum_head "{" enumerator_list "," "}" + + enum_head: enum_key [attribute_specifier_seq] [IDENTIFIER] [enum_base] + | enum_key [attribute_specifier_seq] nested_name_specifier IDENTIFIER [enum_base] + + enum_key: "enum" + | "enum" "class" + | "enum" "struct" + + enum_base: ":" type_specifier_seq """ debug_print("parsing enum") @@ -3573,13 +3574,13 @@ def _install_enum(self, newEnum, instancesData): def _parse_enumerator_list(self, values): """ - enumerator_list: enumerator_definition - | enumerator_list "," enumerator_definition - - enumerator_definition: enumerator - | enumerator "=" constant_expression - - enumerator: IDENTIFIER + enumerator_list: enumerator_definition + | enumerator_list "," enumerator_definition + + enumerator_definition: enumerator + | enumerator "=" constant_expression + + enumerator: IDENTIFIER """ while True: name_tok = self._next_token_must_be("}", "NAME") diff --git a/CppHeaderParser/doxygen.py b/CppHeaderParser/doxygen.py index 67c1584..cac2b35 100644 --- a/CppHeaderParser/doxygen.py +++ b/CppHeaderParser/doxygen.py @@ -1,6 +1,6 @@ def extract_doxygen_method_params(doxystr): """ - Given a doxygen string for a method, extract parameter descriptions + Given a doxygen string for a method, extract parameter descriptions """ doxyVarDesc = {} doxyLines = doxystr.split("\n") diff --git a/CppHeaderParser/lexer.py b/CppHeaderParser/lexer.py index 4531ab2..fa62a1a 100644 --- a/CppHeaderParser/lexer.py +++ b/CppHeaderParser/lexer.py @@ -142,11 +142,11 @@ def current_location(self): def get_doxygen(self): """ - This should be called after the first element of something has - been consumed. + This should be called after the first element of something has + been consumed. - It will lookahead for comments that come after the item, if prior - comments don't exist. + It will lookahead for comments that come after the item, if prior + comments don't exist. """ # Assumption: This function is either called at the beginning of a diff --git a/test/test_CppHeaderParser.py b/test/test_CppHeaderParser.py index 99dc8cf..400816a 100644 --- a/test/test_CppHeaderParser.py +++ b/test/test_CppHeaderParser.py @@ -3572,7 +3572,8 @@ def test_enum(self): self.assertEqual(e["name"], "MyEnum") self.assertEqual( - e["values"], [{"name": "V", "value": 1}], + e["values"], + [{"name": "V", "value": 1}], ) From 2a8a096d1d4d345e70ec4fa5063ab643272a89ac Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Wed, 16 Sep 2020 19:09:18 -0400 Subject: [PATCH 103/143] Fix removal of keywords from method return types --- CppHeaderParser/CppHeaderParser.py | 14 ++++++++------ test/test_CppHeaderParser.py | 22 ++++++++++++++++++++++ 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index f0951ad..f8c5291 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -983,6 +983,12 @@ def __init__(self, nameStack, curClass, methinfo, curTemplate, doxygen, location if doxygen: self["doxygen"] = doxygen + # Remove leading keywords + for i, word in enumerate(nameStack): + if word not in Resolver.C_KEYWORDS: + nameStack = nameStack[i:] + break + if "operator" in nameStack: self["rtnType"] = " ".join(nameStack[: nameStack.index("operator")]) self["name"] = "".join( @@ -993,12 +999,7 @@ def __init__(self, nameStack, curClass, methinfo, curTemplate, doxygen, location self["name"] = " ".join( nameStack[nameStack.index("(") - 1 : nameStack.index("(")] ) - if self["rtnType"].startswith("virtual"): - self["rtnType"] = self["rtnType"][len("virtual") :].strip() - elif self["rtnType"].startswith("static"): - self["rtnType"] = self["rtnType"][len("static") :].strip() - elif self["rtnType"].startswith("inline"): - self["rtnType"] = self["rtnType"][len("inline") :].strip() + if len(self["rtnType"]) == 0 or self["name"] == curClass: self["rtnType"] = "void" @@ -1444,6 +1445,7 @@ class Resolver(object): C_MODIFIERS = set(C_MODIFIERS) C_KEYWORDS = "extern virtual static explicit inline friend".split() + C_KEYWORDS = set(C_KEYWORDS) SubTypedefs = {} # TODO deprecate? NAMESPACES = [] diff --git a/test/test_CppHeaderParser.py b/test/test_CppHeaderParser.py index 400816a..7a966b8 100644 --- a/test/test_CppHeaderParser.py +++ b/test/test_CppHeaderParser.py @@ -3906,5 +3906,27 @@ def test_fn(self): self.assertEqual(self.cppHeader.typedefs["A"], "C") +class InlineVirtual(unittest.TestCase): + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader( + """ +class B { +public: + virtual inline int aMethod(); +}; +""", + "string", + ) + + def test_fn(self): + c = self.cppHeader.classes["B"] + self.assertEqual("B", c["name"]) + + m = c["methods"]["public"][0] + self.assertEqual(m["name"], "aMethod") + self.assertEqual(m["rtnType"], "int") + self.assertEqual(m["returns"], "int") + + if __name__ == "__main__": unittest.main() From 671c58203e87640ccd7c109580ae90c15e6fcb7c Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Wed, 16 Sep 2020 19:13:14 -0400 Subject: [PATCH 104/143] Optimize rtnType storage --- CppHeaderParser/CppHeaderParser.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index f8c5291..68e374f 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -990,23 +990,26 @@ def __init__(self, nameStack, curClass, methinfo, curTemplate, doxygen, location break if "operator" in nameStack: - self["rtnType"] = " ".join(nameStack[: nameStack.index("operator")]) + rtnType = " ".join(nameStack[: nameStack.index("operator")]) self["name"] = "".join( nameStack[nameStack.index("operator") : nameStack.index("(")] ) else: - self["rtnType"] = " ".join(nameStack[: nameStack.index("(") - 1]) + rtnType = " ".join(nameStack[: nameStack.index("(") - 1]) self["name"] = " ".join( nameStack[nameStack.index("(") - 1 : nameStack.index("(")] ) - if len(self["rtnType"]) == 0 or self["name"] == curClass: - self["rtnType"] = "void" + if len(rtnType) == 0 or self["name"] == curClass: + rtnType = "void" - self["rtnType"] = self["rtnType"].replace(" :: ", "::") - self["rtnType"] = self["rtnType"].replace(" < ", "<") - self["rtnType"] = self["rtnType"].replace(" > ", "> ").replace(">>", "> >") - self["rtnType"] = self["rtnType"].replace(" ,", ",") + self["rtnType"] = ( + rtnType.replace(" :: ", "::") + .replace(" < ", "<") + .replace(" > ", "> ") + .replace(">>", "> >") + .replace(" ,", ",") + ) # deal with "noexcept" specifier/operator self["noexcept"] = None From 7c7d9477ca385a87ad99adb8b9c6cf55dcf0d517 Mon Sep 17 00:00:00 2001 From: Luke Kucalaba Date: Sat, 19 Sep 2020 13:10:40 -0400 Subject: [PATCH 105/143] Added 'isclass' boolean key to CppEnum to indicate enum is an 'enum class' --- CppHeaderParser/CppHeaderParser.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 68e374f..a4c8dbf 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -1384,6 +1384,7 @@ def __init__(self, name, doxygen, location): self["name"] = name self["namespace"] = "" self["typedef"] = False + self["isclass"] = False self["values"] = [] set_location_info(self, location) @@ -3488,8 +3489,10 @@ def _parse_enum(self): location = tok.location + is_class = False nametok = self.lex.token() if nametok.value in ("class", "struct"): + is_class = True nametok = self.lex.token() if nametok.value == "__attribute__": @@ -3521,6 +3524,8 @@ def _parse_enum(self): newEnum = CppEnum(name, doxygen, location) if is_typedef: newEnum["typedef"] = True + if is_class: + newEnum["isclass"] = True if base: newEnum["type"] = "".join(base) From 71ee61e20a5fced3966078b204044f609c8da088 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sat, 19 Sep 2020 14:40:26 -0400 Subject: [PATCH 106/143] Add docs/tests for enum change --- CppHeaderParser/CppHeaderParser.py | 1 + test/test_CppHeaderParser.py | 1 + 2 files changed, 2 insertions(+) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index a4c8dbf..af29b45 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -1369,6 +1369,7 @@ class CppEnum(_CppEnum): * ``name`` - Name of the enum (ex. "ItemState") * ``namespace`` - Namespace containing the enum + * ``isclass`` - True if created via 'enum class' or 'enum struct' * ``values`` - List of values. The values are a dictionary with the following key/values: diff --git a/test/test_CppHeaderParser.py b/test/test_CppHeaderParser.py index 7a966b8..1be0344 100644 --- a/test/test_CppHeaderParser.py +++ b/test/test_CppHeaderParser.py @@ -3575,6 +3575,7 @@ def test_enum(self): e["values"], [{"name": "V", "value": 1}], ) + self.assertTrue(e["isclass"]) class NestedResolving_TestCase(unittest.TestCase): From 6d2eb78c54f815349add4a4873632c8ca58e364e Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sat, 19 Sep 2020 14:53:44 -0400 Subject: [PATCH 107/143] Migrate to github actions --- .github/workflows/dist.yml | 91 ++++++++++++++++++++++++++++++++++++++ .travis.yml | 41 ----------------- docs/requirements.txt | 2 + 3 files changed, 93 insertions(+), 41 deletions(-) create mode 100644 .github/workflows/dist.yml delete mode 100644 .travis.yml create mode 100644 docs/requirements.txt diff --git a/.github/workflows/dist.yml b/.github/workflows/dist.yml new file mode 100644 index 0000000..d6801dd --- /dev/null +++ b/.github/workflows/dist.yml @@ -0,0 +1,91 @@ +--- +name: dist + +on: [push, pull_request] + +jobs: + check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: psf/black@stable + + check-doc: + runs-on: ubuntu-18.04 + + steps: + - uses: actions/checkout@v2 + with: + submodules: recursive + fetch-depth: 0 + + - uses: actions/setup-python@v2 + with: + python-version: 3.8 + - name: Sphinx + run: | + pip --disable-pip-version-check install -e . + pip --disable-pip-version-check install -r docs/requirements.txt + cd docs && make clean html SPHINXOPTS="-W --keep-going" + + test: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [windows-latest, macos-latest, ubuntu-18.04] + python_version: [2.7, 3.5, 3.6, 3.7, 3.8] + architecture: [x86, x64] + exclude: + - os: macos-latest + architecture: x86 + - os: ubuntu-18.04 + architecture: x86 + + steps: + - uses: actions/checkout@v2 + with: + submodules: recursive + fetch-depth: 0 + + - uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python_version }} + architecture: ${{ matrix.architecture }} + + - name: Install build dependencies + run: python -m pip --disable-pip-version-check install wheel + + - name: Build wheel + run: python setup.py bdist_wheel + + - name: Test wheel + shell: bash + run: | + cd dist + python -m pip --disable-pip-version-check install *.whl + cd ../test + python test_CppHeaderParser.py + + publish: + runs-on: ubuntu-latest + needs: [check, check-doc, test] + if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') + + steps: + - uses: actions/checkout@v2 + with: + submodules: recursive + fetch-depth: 0 + + - uses: actions/setup-python@v2 + with: + python-version: 3.8 + - run: pip --disable-pip-version-check install wheel + + - name: Build packages + run: python setup.py sdist bdist_wheel + - name: Publish to PyPI + uses: pypa/gh-action-pypi-publish@master + with: + user: __token__ + password: ${{ secrets.PYPI_PASSWORD }} diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index f31c480..0000000 --- a/.travis.yml +++ /dev/null @@ -1,41 +0,0 @@ -language: python -dist: xenial - -python: - - "3.7" - - "3.6" - - "3.5" - - "2.7" - - -jobs: - include: - - stage: format-check - python: "3.6" - install: - - pip install black - script: - - black --check --diff . - fast_finish: true - -# command to install dependencies -install: - - "pip install -e ." -# command to run tests -script: - - ./run_tests.sh -deploy: -- provider: pypi - user: $PYPI_USERNAME - password: $PYPI_PASSWORD - distributions: sdist bdist_wheel - on: - tags: true - python: "3.6" -notifications: - webhooks: - urls: - - https://webhooks.gitter.im/e/2fb1c026e64fdd70d27b - on_success: change - on_failure: always - on_start: never diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 0000000..07084c4 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,2 @@ +sphinx +sphinx-rtd-theme \ No newline at end of file From e7789a63b1c78cee4517ad76df9c702fddbc34ed Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 15 Nov 2020 21:17:32 -0500 Subject: [PATCH 108/143] Defer inspect.currentframe only used for debugging --- CppHeaderParser/CppHeaderParser.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index af29b45..6e1c487 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -100,6 +100,12 @@ def warning_print(fmt, *args): if debug: + class _debug_caller_lineno: + def __str__(self): + return str(inspect.currentframe().f_back.f_back.f_back.f_lineno) + + debug_caller_lineno = _debug_caller_lineno() + def debug_print(fmt, *args): fmt = "[%4d] " + fmt args = (inspect.currentframe().f_back.f_lineno,) + args @@ -108,6 +114,8 @@ def debug_print(fmt, *args): else: + debug_caller_lineno = None + def debug_print(fmt, *args): pass @@ -3237,10 +3245,10 @@ def _evaluate_stack(self, token=None): nameStackCopy = self.nameStack[:] debug_print( - "Evaluating stack %s\n BraceDepth: %s (called from %d)", + "Evaluating stack %s\n BraceDepth: %s (called from %s)", self.nameStack, self.braceDepth, - inspect.currentframe().f_back.f_lineno, + debug_caller_lineno, ) # Handle special case of overloading operator () From b60b0f2073f57d6301948a315581123b9d6accba Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 15 Nov 2020 21:24:11 -0500 Subject: [PATCH 109/143] Defer formatting for various debug messages --- CppHeaderParser/CppHeaderParser.py | 39 +++++++++++++++--------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 6e1c487..2825efb 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -123,9 +123,9 @@ def debug_print(fmt, *args): if debug_trace: def trace_print(*args): - sys.stdout.write("[%s] " % (inspect.currentframe().f_back.f_lineno)) + sys.stdout.write("[%s]" % (inspect.currentframe().f_back.f_lineno)) for a in args: - sys.stdout.write("%s " % a) + sys.stdout.write(" %s" % a) sys.stdout.write("\n") @@ -729,7 +729,7 @@ def __init__(self, nameStack, curTemplate, doxygen, location, defaultAccess): if curTemplate: self["template"] = curTemplate - trace_print("Setting template to '%s'" % self["template"]) + trace_print("Setting template to", self["template"]) methodAccessSpecificList = {} propertyAccessSpecificList = {} @@ -1865,7 +1865,6 @@ def finalize_vars(self): else: trace_print("WARN-this should almost never happen!") trace_print(var) - trace_print("-" * 80) var["unresolved"] = True elif tag in self._template_typenames: @@ -1950,23 +1949,23 @@ def finalize_vars(self): # var['raw_type'] = var['raw_type'][2:] # Take care of #defines and #pragmas etc - trace_print("Processing precomp_macro_buf: %s" % self._precomp_macro_buf) + trace_print("Processing precomp_macro_buf:", self._precomp_macro_buf) for m, location in self._precomp_macro_buf: macro = m.replace("\\n", "\n") ml = macro.lower() try: if ml.startswith("#define"): - trace_print("Adding #define %s" % macro) + trace_print("Adding #define", macro) define = CppDefine(macro, location) self.defines.append(define["value"]) self.defines_detail.append(define) elif ml.startswith("#pragma"): - trace_print("Adding #pragma %s" % macro) + trace_print("Adding #pragma", macro) pragma = CppPragma(macro, location) self.pragmas_detail.append(pragma) self.pragmas.append(pragma["value"]) elif ml.startswith("#include"): - trace_print("Adding #include %s" % macro) + trace_print("Adding #include", macro) include = CppInclude(macro, location) self.includes.append(include["value"]) self.includes_detail.append(include) @@ -2455,8 +2454,9 @@ def _evaluate_property_stack(self, clearStack=True, addToVar=None): # Is it really a list of variables? if leftMostComma != 0: trace_print( - "Multiple variables for namestack in %s. Separating processing" - % self.nameStack + "Multiple variables for namestack in", + self.nameStack, + ". Separating processing", ) orig_nameStack = self.nameStack[:] @@ -2580,7 +2580,6 @@ def _evaluate_class_stack(self): if key in self.classes: trace_print("ERROR name collision:", key) self.classes[key].show() - trace_print("-" * 80) newClass.show() assert key not in self.classes # namespace collision self.classes[key] = newClass @@ -3290,7 +3289,7 @@ def _evaluate_stack(self, token=None): ) ): debug_print("trace") - trace_print("typedef %s", self.stack) + trace_print("typedef", self.stack) self._evaluate_typedef() return @@ -3407,7 +3406,7 @@ def _evaluate_stack(self, token=None): elif self.braceDepth > len(self.nameSpaces) + 1: debug_print("trace") else: - debug_print("Discarded statement %s" % (self.nameStack,)) + debug_print("Discarded statement %s", self.nameStack) try: self.nameStackHistory[self.braceDepth] = (nameStackCopy, self.curClass) @@ -3643,17 +3642,17 @@ def _strip_parent_keys(self): obj_queue = [self] while len(obj_queue): obj = obj_queue.pop() - trace_print("pop %s type %s" % (obj, type(obj))) + trace_print("pop", obj, "type", type(obj)) try: if "parent" in obj.keys(): del obj["parent"] - trace_print("Stripped parent from %s" % obj.keys()) + trace_print("Stripped parent from", obj.keys()) except: pass try: if "method" in obj.keys(): del obj["method"] - trace_print("Stripped method from %s" % obj.keys()) + trace_print("Stripped method from", obj.keys()) except: pass # Figure out what sub types are one of ours @@ -3661,17 +3660,17 @@ def _strip_parent_keys(self): if not hasattr(obj, "keys"): obj = obj.__dict__ for k in obj.keys(): - trace_print("-Try key %s" % (k)) - trace_print("-type %s" % (type(obj[k]))) + trace_print("-Try key", k) + trace_print("-type", type(obj[k])) if k in ["nameStackHistory", "parent", "_public_typedefs"]: continue if type(obj[k]) == list: for i in obj[k]: - trace_print("push l %s" % i) + trace_print("push l", i) obj_queue.append(i) elif type(obj[k]) == dict: if len(obj): - trace_print("push d %s" % obj[k]) + trace_print("push d", obj[k]) obj_queue.append(obj[k]) elif type(obj[k]) == type(type(0)): if type(obj[k]) == int: From 688d3e5f9a25ae69242eab4179283d29124044c7 Mon Sep 17 00:00:00 2001 From: Grand Joldes Date: Mon, 14 Dec 2020 12:02:16 +0800 Subject: [PATCH 110/143] Add namespace to global variables. --- CppHeaderParser/CppHeaderParser.py | 1 + 1 file changed, 1 insertion(+) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 2825efb..c4f9123 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -2491,6 +2491,7 @@ def _evaluate_property_stack(self, clearStack=True, addToVar=None): self._get_stmt_doxygen(), self._get_location(self.nameStack), ) + newVar["namespace"] = self.current_namespace() if addToVar: newVar.update(addToVar) self.variables.append(newVar) From eba85201d5531bdcefba2280f7962d804e518991 Mon Sep 17 00:00:00 2001 From: Grand Joldes Date: Mon, 14 Dec 2020 12:07:18 +0800 Subject: [PATCH 111/143] Remove trailing :: from global variabe namespace. --- CppHeaderParser/CppHeaderParser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index c4f9123..1180898 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -2491,7 +2491,7 @@ def _evaluate_property_stack(self, clearStack=True, addToVar=None): self._get_stmt_doxygen(), self._get_location(self.nameStack), ) - newVar["namespace"] = self.current_namespace() + newVar["namespace"] = self.cur_namespace(False) if addToVar: newVar.update(addToVar) self.variables.append(newVar) From b87ee6d23236da419d72df24f0df3f8348d6484e Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 10 Jan 2021 01:43:40 -0500 Subject: [PATCH 112/143] Resolve forward declared variables correctly --- CppHeaderParser/CppHeaderParser.py | 5 +++-- test/test_CppHeaderParser.py | 32 ++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 1180898..9d1b0a0 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -1473,7 +1473,7 @@ def initextra(self): [] ) # full name stack, good idea to keep both stacks? (simple stack and full stack) self._classes_brace_level = {} # class name : level - self._forward_decls = [] + self._forward_decls = {} # name: namespace self._template_typenames = [] # template def current_namespace(self): @@ -1747,6 +1747,7 @@ def finalize_vars(self): elif tag in self._forward_decls: var["forward_declared"] = tag + var["namespace"] = self._forward_decls[tag] var["ctypes_type"] = "ctypes.c_void_p" elif tag in self.global_enums: @@ -2599,7 +2600,7 @@ def evalute_forward_decl(self): if self.curAccessSpecifier == "public": klass._public_forward_declares.append(name) else: - self._forward_decls.append(name) + self._forward_decls[name] = self.current_namespace() # fmt: off diff --git a/test/test_CppHeaderParser.py b/test/test_CppHeaderParser.py index 1be0344..88bd5d4 100644 --- a/test/test_CppHeaderParser.py +++ b/test/test_CppHeaderParser.py @@ -3929,5 +3929,37 @@ def test_fn(self): self.assertEqual(m["returns"], "int") +class ForwardDeclResolve(unittest.TestCase): + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader( + """ +namespace n1 { + +class A; + +namespace n2 { + +class B { +public: + explicit B(const A &a); +}; + +} +""", + "string", + ) + + def test_fn(self): + c = self.cppHeader.classes["B"] + self.assertEqual("B", c["name"]) + + m = c["methods"]["public"][0] + self.assertEqual(m["name"], "B") + self.assertEqual(m["parameters"][0]["forward_declared"], "A") + self.assertEqual(m["parameters"][0]["namespace"], "n1::") + self.assertEqual(m["parameters"][0]["name"], "a") + self.assertEqual(m["parameters"][0]["raw_type"], "n1::A") + + if __name__ == "__main__": unittest.main() From 5df193381f8d87b8bbc8057ec3735d7981a06410 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 10 Jan 2021 02:03:39 -0500 Subject: [PATCH 113/143] Support compound namespace declarations --- CppHeaderParser/CppHeaderParser.py | 2 +- test/test_CppHeaderParser.py | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 9d1b0a0..04073fa 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -2891,7 +2891,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): == "__IGNORED_NAMESPACE__CppHeaderParser__" ): # Used in filtering extern "C" self.nameStack[1] = "" - self.nameSpaces.append(self.nameStack[1]) + self.nameSpaces.append("".join(self.nameStack[1:])) ns = self.cur_namespace() self.stack = [] self.stmtTokens = [] diff --git a/test/test_CppHeaderParser.py b/test/test_CppHeaderParser.py index 88bd5d4..a4b545b 100644 --- a/test/test_CppHeaderParser.py +++ b/test/test_CppHeaderParser.py @@ -3961,5 +3961,22 @@ def test_fn(self): self.assertEqual(m["parameters"][0]["raw_type"], "n1::A") +class CompoundNS(unittest.TestCase): + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader( + """ +namespace N1::N2 { +class B {}; +}; +""", + "string", + ) + + def test_fn(self): + c = self.cppHeader.classes["B"] + self.assertEqual("B", c["name"]) + self.assertEqual("N1::N2", c["namespace"]) + + if __name__ == "__main__": unittest.main() From b41f83162062caf914ccc9a643df08c80322adea Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Tue, 12 Jan 2021 00:21:24 -0500 Subject: [PATCH 114/143] Clear templates after using them --- CppHeaderParser/CppHeaderParser.py | 5 +++++ test/test_CppHeaderParser.py | 32 ++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 04073fa..007d78a 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -2346,6 +2346,10 @@ def _evaluate_method_stack(self): ) newMethod["parent"] = None self.functions.append(newMethod) + + # Reset template once it has been used + self.curTemplate = None + global parseHistory parseHistory.append( { @@ -2549,6 +2553,7 @@ def _evaluate_class_stack(self): self._get_location(self.nameStack), self.curAccessSpecifier, ) + self.curTemplate = None newClass["declaration_method"] = self.nameStack[0] self.classes_order.append(newClass) # good idea to save ordering self.stack = [] # fixes if class declared with ';' in closing brace diff --git a/test/test_CppHeaderParser.py b/test/test_CppHeaderParser.py index a4b545b..9656437 100644 --- a/test/test_CppHeaderParser.py +++ b/test/test_CppHeaderParser.py @@ -3978,5 +3978,37 @@ def test_fn(self): self.assertEqual("N1::N2", c["namespace"]) +class TemplateMustBeCleared(unittest.TestCase): + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader( + """ +template class LinearPlantInversionFeedforward { +public: + template + LinearPlantInversionFeedforward( + const LinearSystem &plant, units::second_t dt) + : LinearPlantInversionFeedforward(plant.A(), plant.B(), dt) {} + + LinearPlantInversionFeedforward( + const Eigen::Matrix &A, + const Eigen::Matrix &B, units::second_t dt) + : m_dt(dt) { + DiscretizeAB(A, B, dt, &m_A, &m_B); + + m_r.setZero(); + Reset(m_r); + } +}; +""", + "string", + ) + + def test_fn(self): + c = self.cppHeader.classes["LinearPlantInversionFeedforward"] + self.assertEqual("LinearPlantInversionFeedforward", c["name"]) + self.assertEqual("template", c["methods"]["public"][0]["template"]) + self.assertEqual(False, c["methods"]["public"][1]["template"]) + + if __name__ == "__main__": unittest.main() From 63afe03e5aaa8208afa34b369089ef5922986522 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 24 Jan 2021 01:11:13 -0500 Subject: [PATCH 115/143] Emit correct types when 'typename' is present --- CppHeaderParser/CppHeaderParser.py | 12 +++++++++++- test/test_CppHeaderParser.py | 20 ++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 007d78a..1204846 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -280,6 +280,10 @@ def _split_namespace(namestack): :rtype: Tuple[str, list] """ # TODO: this should be using tokens instead of nhack + typename = None + if namestack and namestack[0] == "typename": + typename = namestack[0] + namestack = namestack[1:] last_colon = None for i, n in enumerate(namestack): @@ -296,6 +300,9 @@ def _split_namespace(namestack): else: ns = "" + if typename: + namestack = [typename] + namestack + return ns, namestack @@ -3348,7 +3355,10 @@ def _evaluate_stack(self, token=None): else: atype["namespace"] = ns - atype["raw_type"] = ns + atype["type"] + if atype["type"].startswith("typename "): + atype["raw_type"] = "typename " + ns + atype["type"][9:] + else: + atype["raw_type"] = ns + atype["type"] if self.curClass: klass = self.classes[self.curClass] diff --git a/test/test_CppHeaderParser.py b/test/test_CppHeaderParser.py index 9656437..91f9d74 100644 --- a/test/test_CppHeaderParser.py +++ b/test/test_CppHeaderParser.py @@ -4010,5 +4010,25 @@ def test_fn(self): self.assertEqual(False, c["methods"]["public"][1]["template"]) +class UsingTypename(unittest.TestCase): + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader( + """ +template class P { +public: + using State = typename f::TP::S; +}; +""", + "string", + ) + + def test_fn(self): + c = self.cppHeader.classes["P"] + self.assertEqual("P", c["name"]) + state = c["using"]["State"] + self.assertEqual(state["raw_type"], "typename f::TP::") + self.assertEqual(state["type"], "typename TP::") + + if __name__ == "__main__": unittest.main() From 0bac48227d208cab04105893c9594c7463bf67a5 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 24 Jan 2021 02:06:52 -0500 Subject: [PATCH 116/143] Fix 'using' typealiases that end with ::N --- CppHeaderParser/CppHeaderParser.py | 6 +++++- test/test_CppHeaderParser.py | 12 ++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 1204846..a369308 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -1276,7 +1276,11 @@ def __init__(self, nameStack, doxygen, location, **kwargs): # backwards compat; deprecate camelCase in dicts self["defaultValue"] = default - elif is_fundamental(nameStack[-1]) or nameStack[-1] in [">", "<", ":", "."]: + elif ( + is_fundamental(nameStack[-1]) + or nameStack[-1] in [">", "<", ":", "."] + or (len(nameStack) > 2 and nameStack[-2] == "::") + ): # Un named parameter self["type"] = " ".join(nameStack) self["name"] = "" diff --git a/test/test_CppHeaderParser.py b/test/test_CppHeaderParser.py index 91f9d74..fedacc0 100644 --- a/test/test_CppHeaderParser.py +++ b/test/test_CppHeaderParser.py @@ -4017,6 +4017,7 @@ def setUp(self): template class P { public: using State = typename f::TP::S; + P(State st); }; """, "string", @@ -4026,8 +4027,15 @@ def test_fn(self): c = self.cppHeader.classes["P"] self.assertEqual("P", c["name"]) state = c["using"]["State"] - self.assertEqual(state["raw_type"], "typename f::TP::") - self.assertEqual(state["type"], "typename TP::") + self.assertEqual(state["raw_type"], "typename f::TP::S") + self.assertEqual(state["type"], "typename TP::S") + + m = c["methods"]["public"][0] + self.assertEqual(m["name"], "P") + self.assertEqual(m["parameters"][0]["namespace"], "f::") + self.assertEqual(m["parameters"][0]["name"], "st") + self.assertEqual(m["parameters"][0]["raw_type"], "typename f::TP::S") + self.assertEqual(m["parameters"][0]["type"], "typename TP::S") if __name__ == "__main__": From e025e94bebf01631970b16cc826cf533bdceb57f Mon Sep 17 00:00:00 2001 From: William Date: Fri, 12 Feb 2021 10:09:39 -0600 Subject: [PATCH 117/143] Fixing type reference to self --- CppHeaderParser/CppHeaderParser.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index a369308..9737d75 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -3707,13 +3707,25 @@ def _strip_parent_keys(self): def toJSON(self, indent=4, separators=None): """Converts a parsed structure to JSON""" import json - self._strip_parent_keys() + def clean_dict(markers, keys = []): + if (id(markers) in keys): + return None + elif isinstance(markers, dict): + keys_ = keys + [id(markers)] + return {key: clean_dict(markers[key], keys_) for + key, value in markers.items()} + elif type(markers) in [list, set, tuple]: + return type(markers)(clean_dict(m, keys) for m in markers) + return markers try: del self.__dict__["classes_order"] except: pass - return json.dumps(self.__dict__, indent=indent, separators=separators) + + d = self.__dict__ + d["classes"] = clean_dict(d["classes"]) + return json.dumps(d, indent=indent, separators=separators, default = "") def __repr__(self): rtn = { From f3b91084318fcb09c98a3d0d7d927e66decdd69a Mon Sep 17 00:00:00 2001 From: William Date: Fri, 12 Feb 2021 10:28:15 -0600 Subject: [PATCH 118/143] Reformatting for consistency --- CppHeaderParser/CppHeaderParser.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 9737d75..70cffda 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -3707,17 +3707,22 @@ def _strip_parent_keys(self): def toJSON(self, indent=4, separators=None): """Converts a parsed structure to JSON""" import json + self._strip_parent_keys() - def clean_dict(markers, keys = []): - if (id(markers) in keys): + + def clean_dict(markers, keys=[]): + if id(markers) in keys: return None elif isinstance(markers, dict): keys_ = keys + [id(markers)] - return {key: clean_dict(markers[key], keys_) for - key, value in markers.items()} + return { + key: clean_dict(markers[key], keys_) + for key, value in markers.items() + } elif type(markers) in [list, set, tuple]: return type(markers)(clean_dict(m, keys) for m in markers) return markers + try: del self.__dict__["classes_order"] except: @@ -3725,7 +3730,7 @@ def clean_dict(markers, keys = []): d = self.__dict__ d["classes"] = clean_dict(d["classes"]) - return json.dumps(d, indent=indent, separators=separators, default = "") + return json.dumps(d, indent=indent, separators=separators, default="") def __repr__(self): rtn = { From d8ff031413f30aa8fa1a1116c7e3e85f3a7268a5 Mon Sep 17 00:00:00 2001 From: Erignik Date: Fri, 9 Apr 2021 21:52:48 +0800 Subject: [PATCH 119/143] Add function pointer parse. #66 --- CppHeaderParser/CppHeaderParser.py | 18 ++++++++++++++++++ test/test_CppHeaderParser.py | 18 ++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 70cffda..9796cea 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -2375,6 +2375,20 @@ def _evaluate_method_stack(self): self.stack = [] self.stmtTokens = [] + _function_point_typedef_format = re.compile(r".*?\(.*?\*(.*?)\).*?\(.*?\).*?") + + def _function_point_typedef_parse(self, stack): + idx = stack.index("typedef") + expression = "".join(stack[idx + 1 :]) + m = self._function_point_typedef_format.match(expression) + if m is None: + return {} + + name = m.group(1) + s = " ".join([i for i in stack if i != name]) + r = {"name": name, "raw": s, "type": s} + return r + def _parse_typedef(self, stack, namespace=""): if not stack or "typedef" not in stack: return @@ -2382,6 +2396,10 @@ def _parse_typedef(self, stack, namespace=""): if stack[-1] == ";": stack.pop() + r = self._function_point_typedef_parse(stack) + if len(r) == 3: + return r + while stack and stack[-1].isdigit(): stack.pop() # throw away array size for now diff --git a/test/test_CppHeaderParser.py b/test/test_CppHeaderParser.py index fedacc0..514cc48 100644 --- a/test/test_CppHeaderParser.py +++ b/test/test_CppHeaderParser.py @@ -4038,5 +4038,23 @@ def test_fn(self): self.assertEqual(m["parameters"][0]["type"], "typename TP::S") +class FunctionPointerParse(unittest.TestCase): + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader( + """ +typedef int U32; +typedef unsigned int( * p )(int, int); +typedef int( * mmmmp )(int, int) ; +""", + "string", + ) + + def test_fn(self): + c = self.cppHeader + self.assertEqual(c.typedefs["U32"], "int") + self.assertEqual(c.typedefs["p"], "typedef unsigned int ( * ) ( int , int )") + self.assertEqual(c.typedefs["mmmmp"], "typedef int ( * ) ( int , int )") + + if __name__ == "__main__": unittest.main() From da346a5e566feb3af0dd9f254d0144bec317956d Mon Sep 17 00:00:00 2001 From: David Caron Date: Wed, 4 Aug 2021 15:13:55 -0400 Subject: [PATCH 120/143] add access property to 'using' CppVariable --- CppHeaderParser/CppHeaderParser.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 9796cea..f142278 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -1204,6 +1204,7 @@ class CppVariable(_CppVariable): * ``extern`` - True if its an extern, False if not * ``parent`` - If not None, either the class this is a property of, or the method this variable is a parameter in + * ``access`` - Anything in supportedAccessSpecifier """ Vars = [] @@ -1320,6 +1321,7 @@ def __str__(self): "desc", "line_number", "extern", + "access", ] cpy = dict((k, v) for (k, v) in list(self.items()) if k in keys_white_list) if "array_size" in self: @@ -3382,6 +3384,7 @@ def _evaluate_stack(self, token=None): else: atype["raw_type"] = ns + atype["type"] + atype["access"] = self.curAccessSpecifier if self.curClass: klass = self.classes[self.curClass] klass["using"][alias] = atype From 2dc5df5c8fd2234f47d462de6bd1e8e5034febca Mon Sep 17 00:00:00 2001 From: David Caron Date: Thu, 5 Aug 2021 10:00:23 -0400 Subject: [PATCH 121/143] add test for access property on 'using' --- test/test_CppHeaderParser.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/test_CppHeaderParser.py b/test/test_CppHeaderParser.py index 514cc48..c454a32 100644 --- a/test/test_CppHeaderParser.py +++ b/test/test_CppHeaderParser.py @@ -4015,6 +4015,7 @@ def setUp(self): self.cppHeader = CppHeaderParser.CppHeader( """ template class P { +using A = typename f::TP::A; public: using State = typename f::TP::S; P(State st); @@ -4026,9 +4027,15 @@ def setUp(self): def test_fn(self): c = self.cppHeader.classes["P"] self.assertEqual("P", c["name"]) + self.assertEqual(len(c["using"]), 2) state = c["using"]["State"] self.assertEqual(state["raw_type"], "typename f::TP::S") self.assertEqual(state["type"], "typename TP::S") + self.assertEqual(state["access"], "public") + private = c["using"]["A"] + self.assertEqual(private["raw_type"], "typename f::TP::A") + self.assertEqual(private["type"], "typename TP::A") + self.assertEqual(private["access"], "private") m = c["methods"]["public"][0] self.assertEqual(m["name"], "P") From e97f7c91280c69226e06cda72729779054386d47 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sat, 8 Jan 2022 03:25:25 -0500 Subject: [PATCH 122/143] Update black format --- CppHeaderParser/CppHeaderParser.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index f142278..159cdb3 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -111,7 +111,6 @@ def debug_print(fmt, *args): args = (inspect.currentframe().f_back.f_lineno,) + args print(fmt % args) - else: debug_caller_lineno = None @@ -128,7 +127,6 @@ def trace_print(*args): sys.stdout.write(" %s" % a) sys.stdout.write("\n") - else: def trace_print(*args): From 0cb3e932961afd1cc1148d2a49c3231d344a0701 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sat, 8 Jan 2022 03:25:48 -0500 Subject: [PATCH 123/143] Detect constexpr functions --- CppHeaderParser/CppHeaderParser.py | 2 +- test/test_CppHeaderParser.py | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 159cdb3..0d6b76c 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -1468,7 +1468,7 @@ class Resolver(object): C_MODIFIERS = "* & const constexpr static mutable".split() C_MODIFIERS = set(C_MODIFIERS) - C_KEYWORDS = "extern virtual static explicit inline friend".split() + C_KEYWORDS = "extern virtual static explicit inline friend constexpr".split() C_KEYWORDS = set(C_KEYWORDS) SubTypedefs = {} # TODO deprecate? diff --git a/test/test_CppHeaderParser.py b/test/test_CppHeaderParser.py index c454a32..374658c 100644 --- a/test/test_CppHeaderParser.py +++ b/test/test_CppHeaderParser.py @@ -2999,6 +2999,21 @@ def test_fn(self): self.assertEqual(p["defaultValue"], "0.02") +class ConstExprFn_TestCase(unittest.TestCase): + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader( + """ +constexpr int overloaded_constexpr(int a, int b, int c) { return a + b + c; } +""", + "string", + ) + + def test_fn(self): + m = self.cppHeader.functions[0] + self.assertEqual(m["constexpr"], True) + self.assertEqual(m["rtnType"], "int") + + class DefaultEnum_TestCase(unittest.TestCase): def setUp(self): self.cppHeader = CppHeaderParser.CppHeader( From 763456603410470eea7933e49fb9fb2d3164950c Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Tue, 27 Sep 2022 23:37:12 -0400 Subject: [PATCH 124/143] Support extern template declarations --- CppHeaderParser/CppHeaderParser.py | 38 ++++++++++++++++++++++++++++-- test/test_CppHeaderParser.py | 32 +++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 0d6b76c..eb5c5ac 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -1425,6 +1425,10 @@ def __str__(self): return self["value"] +class CppExternTemplate(dict): + pass + + # Implementation is shared between CppPragma, CppDefine, CppInclude but they are # distinct so that they may diverge if required without interface-breaking # changes @@ -2747,6 +2751,8 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): #: List of enums in this header as :class:`.CppEnum` self.enums = [] + self.extern_templates = [] + #: List of variables in this header as :class:`.CppVariable` self.variables = [] self.global_enums = {} @@ -2879,7 +2885,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): break tok.value = TagStr(tok.value, location=tok.location) - # debug_print("TOK: %s", tok) + debug_print("TOK: %s", tok) if tok.type == "NAME": if tok.value in self.IGNORE_NAMES: continue @@ -3459,6 +3465,21 @@ def _evaluate_stack(self, token=None): self.curTemplate = None def _parse_template(self): + # check for 'extern template' + extern_template = False + if len(self.stmtTokens) == 1 and self.stmtTokens[0].value == "extern": + extern_template = True + tok = self._next_token_must_be("NAME") + if not tok.value == "class": + raise self._parse_error((tok,), "class") + + tok = self._next_token_must_be("NAME") + if tok.value == "__attribute__": + self._parse_gcc_attribute() + tok = self._next_token_must_be("NAME") + + extern_template_name = tok.value + tok = self._next_token_must_be("<") consumed = self._consume_balanced_tokens(tok) tmpl = " ".join(tok.value for tok in consumed) @@ -3471,7 +3492,20 @@ def _parse_template(self): .replace(" , ", ", ") .replace(" = ", "=") ) - self.curTemplate = "template" + tmpl + + if extern_template: + self.extern_templates.append( + CppExternTemplate( + name=extern_template_name, + params=tmpl, + namespace=self.cur_namespace(), + ) + ) + self.stack = [] + self.nameStack = [] + self.stmtTokens = [] + else: + self.curTemplate = "template" + tmpl def _parse_gcc_attribute(self): tok1 = self._next_token_must_be("(") diff --git a/test/test_CppHeaderParser.py b/test/test_CppHeaderParser.py index 374658c..f5c5d10 100644 --- a/test/test_CppHeaderParser.py +++ b/test/test_CppHeaderParser.py @@ -4078,5 +4078,37 @@ def test_fn(self): self.assertEqual(c.typedefs["mmmmp"], "typedef int ( * ) ( int , int )") +class ExternTemplateTest(unittest.TestCase): + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader( + """ +extern template class MyClass<1,2>; +extern template class __attribute__(("something")) MyClass<3,4>; + +namespace foo { +extern template class MyClass<5,6>; +}; + +""", + "string", + ) + + def test_fn(self): + c = self.cppHeader + et0, et1, et2 = c.extern_templates + + self.assertEqual(et0["name"], "MyClass") + self.assertEqual(et0["namespace"], "") + self.assertEqual(et0["params"], "<1, 2>") + + self.assertEqual(et1["name"], "MyClass") + self.assertEqual(et1["namespace"], "") + self.assertEqual(et1["params"], "<3, 4>") + + self.assertEqual(et2["name"], "MyClass") + self.assertEqual(et2["namespace"], "foo") + self.assertEqual(et2["params"], "<5, 6>") + + if __name__ == "__main__": unittest.main() From cc0d4a0610a73488ec301bda9332415c37a4e3e9 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Tue, 27 Sep 2022 23:57:37 -0400 Subject: [PATCH 125/143] Correctly detect templates for 'using' statements --- CppHeaderParser/CppHeaderParser.py | 18 ++++++++++++++---- test/test_CppHeaderParser.py | 25 +++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index eb5c5ac..87dae7d 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -1207,7 +1207,7 @@ class CppVariable(_CppVariable): Vars = [] - def __init__(self, nameStack, doxygen, location, **kwargs): + def __init__(self, nameStack, doxygen, location, is_var=True, **kwargs): debug_print("var trace %s", nameStack) if len(nameStack) and nameStack[0] == "extern": self["extern"] = True @@ -1297,7 +1297,8 @@ def __init__(self, nameStack, doxygen, location, **kwargs): pass self.init() - CppVariable.Vars.append(self) # save and resolve later + if is_var: + CppVariable.Vars.append(self) # save and resolve later def _filter_name(self, name): name = name.replace(" :", ":").replace(": ", ":") @@ -3359,7 +3360,10 @@ def _evaluate_stack(self, token=None): alias = self.nameStack[1] ns, stack = _split_namespace(self.nameStack[3:]) atype = CppVariable( - stack, self._get_stmt_doxygen(), self._get_location(stack) + stack, + self._get_stmt_doxygen(), + self._get_location(stack), + is_var=False, ) # namespace refers to the embedded type @@ -3374,7 +3378,10 @@ def _evaluate_stack(self, token=None): # from a base class ns, stack = _split_namespace(self.nameStack[1:]) atype = CppVariable( - stack, self._get_stmt_doxygen(), self._get_location(stack) + stack, + self._get_stmt_doxygen(), + self._get_location(stack), + is_var=False, ) alias = atype["type"] atype["using_type"] = "declaration" @@ -3383,6 +3390,9 @@ def _evaluate_stack(self, token=None): else: atype["namespace"] = ns + atype["template"] = self.curTemplate + self.curTemplate = None + if atype["type"].startswith("typename "): atype["raw_type"] = "typename " + ns + atype["type"][9:] else: diff --git a/test/test_CppHeaderParser.py b/test/test_CppHeaderParser.py index f5c5d10..8337858 100644 --- a/test/test_CppHeaderParser.py +++ b/test/test_CppHeaderParser.py @@ -4110,5 +4110,30 @@ def test_fn(self): self.assertEqual(et2["params"], "<5, 6>") +class UsingTemplateTest(unittest.TestCase): + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader( + """ +class X { + template + using TT = U; +}; + +""", + "string", + ) + + def test_fn(self): + u = self.cppHeader.classes["X"]["using"] + self.assertEqual(len(u), 1) + tt = u["TT"] + + self.assertEqual(tt["access"], "private") + self.assertEqual(tt["raw_type"], "U") + self.assertEqual(tt["type"], "U") + self.assertEqual(tt["typealias"], "TT") + self.assertEqual(tt["template"], "template") + + if __name__ == "__main__": unittest.main() From f98128ce340fb4419800d01751e191ad26fa5177 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Tue, 27 Sep 2022 23:59:04 -0400 Subject: [PATCH 126/143] Update black formatting --- docs/conf.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 696192f..cc167b2 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -31,8 +31,8 @@ master_doc = "index" # General information about the project. -project = u"robotpy-cppheaderparser" -copyright = u"2019 RobotPy Development Team" +project = "robotpy-cppheaderparser" +copyright = "2019 RobotPy Development Team" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -200,8 +200,8 @@ ( "index", "sphinx.tex", - u"robotpy-cppheaderparser Documentation", - u"Author", + "robotpy-cppheaderparser Documentation", + "Author", "manual", ) ] @@ -232,7 +232,7 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ("index", "sphinx", u"robotpy-cppheaderparser Documentation", [u"Author"], 1) + ("index", "sphinx", "robotpy-cppheaderparser Documentation", ["Author"], 1) ] # If true, show URL addresses after external links. @@ -248,8 +248,8 @@ ( "index", "sphinx", - u"robotpy-cppheaderparser Documentation", - u"Author", + "robotpy-cppheaderparser Documentation", + "Author", "sphinx", "One line description of project.", "Miscellaneous", @@ -272,10 +272,10 @@ # -- Options for Epub output ---------------------------------------------- # Bibliographic Dublin Core info. -epub_title = u"robotpy-cppheaderparser" -epub_author = u"RobotPy Development Team" -epub_publisher = u"RobotPy Development Team" -epub_copyright = u"2019 RobotPy Development Team" +epub_title = "robotpy-cppheaderparser" +epub_author = "RobotPy Development Team" +epub_publisher = "RobotPy Development Team" +epub_copyright = "2019 RobotPy Development Team" # The basename for the epub file. It defaults to the project name. # epub_basename = u'..' From 7dfe47347c63a99bfc3d9b99b7c29391b4037afe Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Thu, 20 Oct 2022 01:56:52 -0400 Subject: [PATCH 127/143] Doxygen comments were being discarded when an attribute was encountered --- CppHeaderParser/CppHeaderParser.py | 7 +++++-- test/test_CppHeaderParser.py | 9 +++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 87dae7d..a13ad2e 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -2895,9 +2895,11 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): self._parse_template() continue elif tok.value == "alignas": + self._doxygen_cache = self.lex.get_doxygen() self._parse_attribute_specifier_seq(tok) continue elif tok.value == "__attribute__": + self._doxygen_cache = self.lex.get_doxygen() self._parse_gcc_attribute() continue elif not self.stack and tok.value == "static_assert": @@ -2906,6 +2908,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): continue elif tok.type == "DBL_LBRACKET": + self._doxygen_cache = self.lex.get_doxygen() self._parse_attribute_specifier_seq(tok) continue @@ -3076,7 +3079,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): newNsLen = len(self.nameStack) if nslen != newNsLen and newNsLen == 1: - if not self.curTemplate: + if not self._doxygen_cache: self._doxygen_cache = self.lex.get_doxygen() except Exception as e: @@ -3471,7 +3474,7 @@ def _evaluate_stack(self, token=None): # its a little confusing to have some if/else above return and others not, and then clearning the nameStack down here self.nameStack = [] - self.lex.doxygenCommentCache = "" + self._doxygen_cache = None self.curTemplate = None def _parse_template(self): diff --git a/test/test_CppHeaderParser.py b/test/test_CppHeaderParser.py index 8337858..46eaaeb 100644 --- a/test/test_CppHeaderParser.py +++ b/test/test_CppHeaderParser.py @@ -3414,6 +3414,10 @@ def setUp(self): class C2 {}; +/// hasattr comment +[[nodiscard]] +int hasattr(); + """, "string", ) @@ -3443,6 +3447,11 @@ def test_cls2(self): self.assertEqual("C2", c["name"]) self.assertEqual("/// template comment", c["doxygen"]) + def test_hasattr(self): + fn = self.cppHeader.functions[1] + self.assertEqual("hasattr", fn["name"]) + self.assertEqual("/// hasattr comment", fn["doxygen"]) + class EnumParameter_TestCase(unittest.TestCase): def setUp(self): From d20f739fe8e9570b14ac7dbc9996d64478cd1c83 Mon Sep 17 00:00:00 2001 From: Stephen Hansen Date: Wed, 26 Oct 2022 21:42:31 -0400 Subject: [PATCH 128/143] Fix enum parsing bug with decimals as char values --- CppHeaderParser/CppHeaderParser.py | 3 ++- test/TestSampleClass.h | 1 + test/test_CppHeaderParser.py | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index a13ad2e..a07ad6e 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -1350,7 +1350,8 @@ def resolve_enum_values(self, values): # Remove single quotes from single quoted chars (unless part of some expression if len(a) == 3 and a[0] == "'" and a[2] == "'": a = v["value"] = a[1] - if a.lower().startswith("0x"): + a = i = ord(a) + elif a.lower().startswith("0x"): try: i = a = int(a, 16) except: diff --git a/test/TestSampleClass.h b/test/TestSampleClass.h index 439635c..aebae3c 100644 --- a/test/TestSampleClass.h +++ b/test/TestSampleClass.h @@ -73,6 +73,7 @@ namespace Alpha Z_B = 0x2B, Z_C = 'j', Z_D, + Z_E = '9', } Zebra; }; diff --git a/test/test_CppHeaderParser.py b/test/test_CppHeaderParser.py index 46eaaeb..ac93501 100644 --- a/test/test_CppHeaderParser.py +++ b/test/test_CppHeaderParser.py @@ -418,6 +418,7 @@ def test_values(self): {"name": "Z_B", "raw_value": "0x2B", "value": 43}, {"name": "Z_C", "raw_value": "j", "value": 106}, {"name": "Z_D", "value": 107}, + {"name": "Z_E", "raw_value": "9", "value": 57}, ], ) From fd528bb8a4f77e20c8813f6d6e284b73e7995f98 Mon Sep 17 00:00:00 2001 From: Stephen Hansen Date: Wed, 26 Oct 2022 21:53:00 -0400 Subject: [PATCH 129/143] adding an integer to the enum test --- test/TestSampleClass.h | 1 + test/test_CppHeaderParser.py | 1 + 2 files changed, 2 insertions(+) diff --git a/test/TestSampleClass.h b/test/TestSampleClass.h index aebae3c..45efef7 100644 --- a/test/TestSampleClass.h +++ b/test/TestSampleClass.h @@ -74,6 +74,7 @@ namespace Alpha Z_C = 'j', Z_D, Z_E = '9', + Z_F = 9, } Zebra; }; diff --git a/test/test_CppHeaderParser.py b/test/test_CppHeaderParser.py index ac93501..d91edca 100644 --- a/test/test_CppHeaderParser.py +++ b/test/test_CppHeaderParser.py @@ -419,6 +419,7 @@ def test_values(self): {"name": "Z_C", "raw_value": "j", "value": 106}, {"name": "Z_D", "value": 107}, {"name": "Z_E", "raw_value": "9", "value": 57}, + {"name": "Z_F", "value": 9}, ], ) From 953c40854e7f84b51fdea30d7fd54a30bf36a531 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Thu, 3 Nov 2022 22:22:17 -0400 Subject: [PATCH 130/143] Add support for parsing ref-qualifiers --- CppHeaderParser/CppHeaderParser.py | 20 ++++++++++++---- test/test_CppHeaderParser.py | 37 ++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index a07ad6e..b8f744b 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -2208,11 +2208,21 @@ def parse_method_type(self, stack): else: assert 0 - if len(stack) > 3 and stack[-1] == ";" and stack[-3] == "=": - if stack[-2] == "0": - info["pure_virtual"] = True - elif stack[-2] == "delete": - info["deleted"] = True + refqual = "" + if len(stack) > 3: + if stack[-1] == ";" and stack[-3] == "=": + if stack[-2] == "0": + info["pure_virtual"] = True + elif stack[-2] == "delete": + info["deleted"] = True + + for e in reversed(stack): + if e == ")": + break + elif e == "&": + refqual += "&" + + info["ref_qualifiers"] = refqual r = header.split() name = None diff --git a/test/test_CppHeaderParser.py b/test/test_CppHeaderParser.py index d91edca..e3472f5 100644 --- a/test/test_CppHeaderParser.py +++ b/test/test_CppHeaderParser.py @@ -4146,5 +4146,42 @@ def test_fn(self): self.assertEqual(tt["template"], "template") +class RefQualifierTest(unittest.TestCase): + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader( + """ +struct X { + void fn0(); + void fn1() &; + void fn2() &&; + void fn3() && = 0; +}; + +""", + "string", + ) + + def test_fn0(self): + fn = self.cppHeader.classes["X"]["methods"]["public"][0] + self.assertEqual(fn["name"], "fn0") + self.assertEqual(fn["ref_qualifiers"], "") + + def test_fn1(self): + fn = self.cppHeader.classes["X"]["methods"]["public"][1] + self.assertEqual(fn["name"], "fn1") + self.assertEqual(fn["ref_qualifiers"], "&") + + def test_fn1(self): + fn = self.cppHeader.classes["X"]["methods"]["public"][2] + self.assertEqual(fn["name"], "fn2") + self.assertEqual(fn["ref_qualifiers"], "&&") + + def test_fn3(self): + fn = self.cppHeader.classes["X"]["methods"]["public"][3] + self.assertEqual(fn["name"], "fn3") + self.assertEqual(fn["ref_qualifiers"], "&&") + self.assertEqual(fn["pure_virtual"], True) + + if __name__ == "__main__": unittest.main() From 4d307a886ed1efd9e26d768a4bb3491d4fde9443 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 4 Dec 2022 10:16:52 -0500 Subject: [PATCH 131/143] Add option to remove preprocessor macro detection --- CppHeaderParser/CppHeaderParser.py | 42 ++++++++++++++++++------------ 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index b8f744b..5897f6b 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -2702,7 +2702,14 @@ def show(self): for className in list(self.classes.keys()): self.classes[className].show() - def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): + def __init__( + self, + headerFileName, + argType="file", + encoding=None, + preprocessed=False, + **kwargs + ): """Create the parsed C++ header file parse tree headerFileName - Name of the file to parse OR actual file contents (depends on argType) @@ -2802,21 +2809,24 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs): "[ ]+", " ", supportedAccessSpecifier[i] ).strip() - # Change multi line #defines and expressions to single lines maintaining line nubmers - # Based from http://stackoverflow.com/questions/2424458/regular-expression-to-match-cs-multiline-preprocessor-statements - matches = re.findall(r"(?m)^(?:.*\\\r?\n)+.*$", headerFileStr) - is_define = re.compile(r"[ \t\v]*#[Dd][Ee][Ff][Ii][Nn][Ee]") - for m in matches: - # Keep the newlines so that linecount doesnt break - num_newlines = len([a for a in m if a == "\n"]) - if is_define.match(m): - new_m = m.replace("\n", "\\n") - else: - # Just expression taking up multiple lines, make it take 1 line for easier parsing - new_m = m.replace("\\\n", " ") - if num_newlines > 0: - new_m += "\n" * (num_newlines) - headerFileStr = headerFileStr.replace(m, new_m) + if not preprocessed: + # Change multi line #defines and expressions to single lines maintaining line nubmers + # Based from http://stackoverflow.com/questions/2424458/regular-expression-to-match-cs-multiline-preprocessor-statements + matches = re.findall(r"(?m)^(?:.*\\\r?\n)+.*$", headerFileStr) + is_define = re.compile(r"[ \t\v]*#[Dd][Ee][Ff][Ii][Nn][Ee]") + for m in matches: + # Keep the newlines so that linecount doesnt break + num_newlines = len([a for a in m if a == "\n"]) + if is_define.match(m): + new_m = m.replace( + "\n", "\\n" + ) + else: + # Just expression taking up multiple lines, make it take 1 line for easier parsing + new_m = m.replace("\\\n", " ") + if num_newlines > 0: + new_m += "\n" * (num_newlines) + headerFileStr = headerFileStr.replace(m, new_m) # Filter out Extern "C" statements. These are order dependent matches = re.findall( From 8d4abd7a949b7289170fd685c704b847f475ac82 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 4 Dec 2022 21:56:07 -0500 Subject: [PATCH 132/143] Fix extern 'C' parsing - Linkage wasn't being recorded previously - Namespaces were being destroyed --- CppHeaderParser/CppHeaderParser.py | 48 +++++++++++++++++++----------- test/test_CppHeaderParser.py | 46 ++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 18 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 5897f6b..05588e9 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -139,6 +139,9 @@ def trace_print(*args): #: Symbols to ignore, usually special macros ignoreSymbols = ["Q_OBJECT"] +_BRACE_REASON_OTHER = 0 +_BRACE_REASON_NS = 1 +_BRACE_REASON_EXTERN = 2 # Track what was added in what order and at what depth parseHistory = [] @@ -1506,6 +1509,11 @@ def cur_namespace(self, add_double_colon=False): i += 1 return rtn + def cur_linkage(self): + if len(self.linkage_stack): + return self.linkage_stack[-1] + return "" + def guess_ctypes_type(self, string): pointers = string.count("*") string = string.replace("*", "") @@ -2372,6 +2380,7 @@ def _evaluate_method_stack(self): self._get_location(self.nameStack), ) newMethod["parent"] = None + newMethod["linkage"] = self.cur_linkage() self.functions.append(newMethod) # Reset template once it has been used @@ -2524,6 +2533,7 @@ def _evaluate_property_stack(self, clearStack=True, addToVar=None): self._get_location(self.nameStack), ) newVar["namespace"] = self.current_namespace() + newVar["linkage"] = self.cur_linkage() if self.curClass: klass = self.classes[self.curClass] klass["properties"][self.curAccessSpecifier].append(newVar) @@ -2542,6 +2552,7 @@ def _evaluate_property_stack(self, clearStack=True, addToVar=None): self._get_location(self.nameStack), ) newVar["namespace"] = self.cur_namespace(False) + newVar["linkage"] = self.cur_linkage() if addToVar: newVar.update(addToVar) self.variables.append(newVar) @@ -2600,6 +2611,7 @@ def _evaluate_class_stack(self): ) self.curTemplate = None newClass["declaration_method"] = self.nameStack[0] + newClass["linkage"] = self.cur_linkage() self.classes_order.append(newClass) # good idea to save ordering self.stack = [] # fixes if class declared with ';' in closing brace self.stmtTokens = [] @@ -2782,6 +2794,7 @@ def __init__( self.curAccessSpecifier = "private" # private is default self.curTemplate = None self.accessSpecifierStack = [] + self.linkage_stack = [] debug_print( "curAccessSpecifier changed/defaulted to %s", self.curAccessSpecifier ) @@ -2828,16 +2841,6 @@ def __init__( new_m += "\n" * (num_newlines) headerFileStr = headerFileStr.replace(m, new_m) - # Filter out Extern "C" statements. These are order dependent - matches = re.findall( - re.compile(r'extern[\t ]+"[Cc]"[\t \n\r]*{', re.DOTALL), headerFileStr - ) - for m in matches: - # Keep the newlines so that linecount doesnt break - num_newlines = len([a for a in m if a == "\n"]) - headerFileStr = headerFileStr.replace(m, "\n" * num_newlines) - headerFileStr = re.sub(r'extern[ ]+"[Cc]"[ ]*', "", headerFileStr) - # Filter out any ignore symbols that end with "()" to account for #define magic functions for ignore in ignoreSymbols: if not ignore.endswith("()"): @@ -2876,6 +2879,8 @@ def __init__( ) self.braceDepth = 0 + self.braceReason = [] + self.lastBraceReason = _BRACE_REASON_OTHER lex = Lexer(self.headerFileName) lex.input(headerFileStr) @@ -2948,23 +2953,20 @@ def __init__( continue if parenDepth == 0 and tok.type == "{": + self.lastBraceReason = _BRACE_REASON_OTHER if len(self.nameStack) >= 2 and is_namespace( self.nameStack ): # namespace {} with no name used in boost, this sets default? - if ( - self.nameStack[1] - == "__IGNORED_NAMESPACE__CppHeaderParser__" - ): # Used in filtering extern "C" - self.nameStack[1] = "" self.nameSpaces.append("".join(self.nameStack[1:])) ns = self.cur_namespace() self.stack = [] self.stmtTokens = [] if ns not in self.namespaces: self.namespaces.append(ns) + self.lastBraceReason = _BRACE_REASON_NS # Detect special condition of macro magic before class declaration so we # can filter it out - if "class" in self.nameStack and self.nameStack[0] != "class": + elif "class" in self.nameStack and self.nameStack[0] != "class": classLocationNS = self.nameStack.index("class") classLocationS = self.stack.index("class") if ( @@ -2997,14 +2999,20 @@ def __init__( self.stmtTokens = [] if not self.braceHandled: self.braceDepth += 1 + self.braceReason.append(self.lastBraceReason) elif parenDepth == 0 and tok.type == "}": if self.braceDepth == 0: continue - if self.braceDepth == len(self.nameSpaces): - tmp = self.nameSpaces.pop() + reason = self.braceReason.pop() + if reason == _BRACE_REASON_NS: + self.nameSpaces.pop() self.stack = [] # clear stack when namespace ends? self.stmtTokens = [] + elif reason == _BRACE_REASON_EXTERN: + self.linkage_stack.pop() + self.stack = [] # clear stack when linkage ends? + self.stmtTokens = [] else: self._evaluate_stack() self.braceDepth -= 1 @@ -3368,8 +3376,10 @@ def _evaluate_stack(self, token=None): pass elif len(self.nameStack) == 2 and self.nameStack[0] == "extern": debug_print("trace extern") + self.linkage_stack.append(self.nameStack[1].strip('"')) self.stack = [] self.stmtTokens = [] + self.lastBraceReason = _BRACE_REASON_EXTERN elif ( len(self.nameStack) == 2 and self.nameStack[0] == "friend" ): # friend class declaration @@ -3677,12 +3687,14 @@ def _parse_enum(self): def _install_enum(self, newEnum, instancesData): if len(self.curClass): newEnum["namespace"] = self.cur_namespace(False) + newEnum["linkage"] = self.cur_linkage() klass = self.classes[self.curClass] klass["enums"][self.curAccessSpecifier].append(newEnum) if self.curAccessSpecifier == "public" and "name" in newEnum: klass._public_enums[newEnum["name"]] = newEnum else: newEnum["namespace"] = self.cur_namespace(True) + newEnum["linkage"] = self.cur_linkage() self.enums.append(newEnum) if "name" in newEnum and newEnum["name"]: self.global_enums[newEnum["name"]] = newEnum diff --git a/test/test_CppHeaderParser.py b/test/test_CppHeaderParser.py index e3472f5..c8aef1b 100644 --- a/test/test_CppHeaderParser.py +++ b/test/test_CppHeaderParser.py @@ -4183,5 +4183,51 @@ def test_fn3(self): self.assertEqual(fn["pure_virtual"], True) +class ExternCQuirk(unittest.TestCase): + # bug where extern "C" reset the namespace + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader( + """ +namespace cs { +extern "C" { + struct InCSAndExtern {}; + void FnInCSAndExtern(InCSAndExtern *n); +} + +class InCS {}; + +} + +void FnNotInCSOrExtern(); + +""", + "string", + ) + + def test_fn(self): + + # NotCS should be in namespace cs, extern C + c = self.cppHeader.classes["InCSAndExtern"] + self.assertEqual(c["namespace"], "cs") + self.assertEqual(c["linkage"], "C") + + # FnNotCS should be in namespace cs, extern C + fn = self.cppHeader.functions[0] + self.assertEqual(fn["name"], "FnInCSAndExtern") + self.assertEqual(fn["namespace"], "cs::") + self.assertEqual(fn["linkage"], "C") + + # InCS should be in namespace cs + c = self.cppHeader.classes["InCS"] + self.assertEqual(c["namespace"], "cs") + self.assertEqual(c["linkage"], "") + + # FnNotCS should not in namespace cs nor extern C + fn = self.cppHeader.functions[1] + self.assertEqual(fn["name"], "FnNotInCSOrExtern") + self.assertEqual(fn["namespace"], "") + self.assertEqual(fn["linkage"], "") + + if __name__ == "__main__": unittest.main() From da1a074314e753a970bc00e7eccc4a62a8c616d7 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Sun, 4 Dec 2022 22:18:21 -0500 Subject: [PATCH 133/143] Remove SubTypedefs thing - Doesn't seem useful --- CppHeaderParser/CppHeaderParser.py | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 05588e9..4482287 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -1480,7 +1480,6 @@ class Resolver(object): C_KEYWORDS = "extern virtual static explicit inline friend constexpr".split() C_KEYWORDS = set(C_KEYWORDS) - SubTypedefs = {} # TODO deprecate? NAMESPACES = [] CLASSES = {} @@ -1878,21 +1877,6 @@ def finalize_vars(self): var["ctypes_type"] = "ctypes.c_void_p" var["unresolved"] = True - elif tag in self.SubTypedefs: # TODO remove SubTypedefs - if ( - "property_of_class" in var - or "property_of_struct" in var - ): - trace_print( - "class:", self.SubTypedefs[tag], "tag:", tag - ) - var["typedef"] = self.SubTypedefs[tag] # class name - var["ctypes_type"] = "ctypes.c_void_p" - else: - trace_print("WARN-this should almost never happen!") - trace_print(var) - var["unresolved"] = True - elif tag in self._template_typenames: var["typename"] = tag var["ctypes_type"] = "ctypes.c_void_p" @@ -2069,10 +2053,6 @@ def finalize(self): trace_print("meth returns class:", meth["returns"]) meth["returns_class"] = True - elif meth["returns"] in self.SubTypedefs: - meth["returns_class"] = True - meth["returns_nested"] = self.SubTypedefs[meth["returns"]] - elif meth["returns"] in cls._public_enums: enum = cls._public_enums[meth["returns"]] meth["returns_enum"] = enum.get("type") @@ -2481,7 +2461,6 @@ def _evaluate_property_stack(self, clearStack=True, addToVar=None): klass["typedefs"][self.curAccessSpecifier].append(name) if self.curAccessSpecifier == "public": klass._public_typedefs[name] = typedef["type"] - Resolver.SubTypedefs[name] = self.curClass else: assert 0 elif self.curClass: From cc3967e189261b4e7b70c3272c1ae68178b3d20c Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Thu, 5 Oct 2023 03:26:58 -0400 Subject: [PATCH 134/143] deprecate robotpy-cppheaderparser --- .github/ISSUE_TEMPLATE/bug-report.yml | 25 +++++++++++++++++++++++++ README.rst | 15 +++++++++++---- 2 files changed, 36 insertions(+), 4 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/bug-report.yml diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml new file mode 100644 index 0000000..618c600 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -0,0 +1,25 @@ +name: Bug Report +description: | + Please be aware that we are no longer making fixes ourselves + to robotpy-cppheaderparser, but we will accept pull requests with appropriately + tested fixes. We recommend all users migrate to our new parser which can be + found at https://github.com/robotpy/cxxheaderparser +title: "[BUG]: " +body: + - type: textarea + id: description + attributes: + label: Problem description + placeholder: >- + Provide a short description, state the expected behavior and what + actually happens. + validations: + required: true + + - type: textarea + id: code + attributes: + label: Reproducible example code + placeholder: >- + Minimal code to reproduce this issue + render: text \ No newline at end of file diff --git a/README.rst b/README.rst index 77e2eb6..627f849 100644 --- a/README.rst +++ b/README.rst @@ -1,7 +1,17 @@ robotpy-cppheaderparser ======================= -|Build Status| +**robotpy-cppheaderparser is DEPRECATED, and we are no longer updating it**. +We will accept pull requests that have fixes and appropriate tests, but we +will no longer be making any fixes ourselves. + +We highly recommend all current and future users to migrate to cxxheaderparser, +which supports all the syntax that CppHeaderParser does and much much more. The +parser output is very different, but it is strictly typed and hopefully easier +to work with. You can find it at https://github.com/robotpy/cxxheaderparser, +or try the live interactive demo at https://robotpy.github.io/cxxheaderparser/ + +--------- CppHeaderParser is a pure python C++ header parser that parses C++ headers and creates a data structure that you can use to do many types @@ -62,6 +72,3 @@ Past contributors include: .. _CppHeaderParser: https://bitbucket.org/senex/cppheaderparser .. _pcpp: https://github.com/ned14/pcpp - -.. |Build Status| image:: https://travis-ci.org/robotpy/robotpy-cppheaderparser.svg?branch=master - :target: https://travis-ci.org/robotpy/robotpy-cppheaderparser \ No newline at end of file From 212ef369389c3aea3b21c7493d5e1553840a6306 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Thu, 5 Oct 2023 03:34:42 -0400 Subject: [PATCH 135/143] Update the updates --- .github/ISSUE_TEMPLATE/bug-report.yml | 6 +----- .github/workflows/dist.yml | 4 ++-- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index 618c600..a69c2a8 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -1,9 +1,5 @@ name: Bug Report -description: | - Please be aware that we are no longer making fixes ourselves - to robotpy-cppheaderparser, but we will accept pull requests with appropriately - tested fixes. We recommend all users migrate to our new parser which can be - found at https://github.com/robotpy/cxxheaderparser +description: We are no longer making fixes ourselves to robotpy-cppheaderparser, but we will accept pull requests with appropriately tested fixes. We recommend all users migrate to cxxheaderparser. title: "[BUG]: " body: - type: textarea diff --git a/.github/workflows/dist.yml b/.github/workflows/dist.yml index d6801dd..abaadec 100644 --- a/.github/workflows/dist.yml +++ b/.github/workflows/dist.yml @@ -32,8 +32,8 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [windows-latest, macos-latest, ubuntu-18.04] - python_version: [2.7, 3.5, 3.6, 3.7, 3.8] + os: [windows-latest, macos-latest, ubuntu-20.04] + python_version: [3.5, 3.6, 3.7, 3.8] architecture: [x86, x64] exclude: - os: macos-latest From c20d7d50c50e0b6f916f2d3925f7f983e8507d02 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Thu, 5 Oct 2023 03:57:25 -0400 Subject: [PATCH 136/143] Update black --- CppHeaderParser/CppHeaderParser.py | 2 -- CppHeaderParser/lexer.py | 1 - test/test_CppHeaderParser.py | 2 -- 3 files changed, 5 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index 4482287..ad78c1e 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -112,7 +112,6 @@ def debug_print(fmt, *args): print(fmt % args) else: - debug_caller_lineno = None def debug_print(fmt, *args): @@ -3192,7 +3191,6 @@ def _next_token_must_be(self, *tokenTypes): } def _consume_balanced_tokens(self, *init_tokens): - _balanced_token_map = self._balanced_token_map consumed = list(init_tokens) diff --git a/CppHeaderParser/lexer.py b/CppHeaderParser/lexer.py index fa62a1a..b63501f 100644 --- a/CppHeaderParser/lexer.py +++ b/CppHeaderParser/lexer.py @@ -6,7 +6,6 @@ class Lexer(object): - tokens = [ "NUMBER", "FLOAT_NUMBER", diff --git a/test/test_CppHeaderParser.py b/test/test_CppHeaderParser.py index c8aef1b..a7810a7 100644 --- a/test/test_CppHeaderParser.py +++ b/test/test_CppHeaderParser.py @@ -3330,7 +3330,6 @@ def setUp(self): ) def test_existance(self): - self.assertIn("S", self.cppHeader.classes) self.assertIn("PS", self.cppHeader.typedefs) self.assertEqual("x", self.cppHeader.variables[0]["name"]) @@ -4205,7 +4204,6 @@ class InCS {}; ) def test_fn(self): - # NotCS should be in namespace cs, extern C c = self.cppHeader.classes["InCSAndExtern"] self.assertEqual(c["namespace"], "cs") From 36357a08e832c6a4aa58b33e7a6ece4ac1c517ac Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Thu, 5 Oct 2023 03:58:52 -0400 Subject: [PATCH 137/143] Update gh actions --- .github/workflows/dist.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dist.yml b/.github/workflows/dist.yml index abaadec..0ec4934 100644 --- a/.github/workflows/dist.yml +++ b/.github/workflows/dist.yml @@ -11,7 +11,7 @@ jobs: - uses: psf/black@stable check-doc: - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 @@ -38,7 +38,7 @@ jobs: exclude: - os: macos-latest architecture: x86 - - os: ubuntu-18.04 + - os: ubuntu-20.04 architecture: x86 steps: From 2c7891a3bd52799b29e9f5b09828049b12d3fb10 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Thu, 16 May 2024 19:48:04 -0400 Subject: [PATCH 138/143] Update black --- CppHeaderParser/CppHeaderParser.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index ad78c1e..a7c2ce5 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -1879,9 +1879,9 @@ def finalize_vars(self): elif tag in self._template_typenames: var["typename"] = tag var["ctypes_type"] = "ctypes.c_void_p" - var[ - "unresolved" - ] = True # TODO, how to deal with templates? + var["unresolved"] = ( + True # TODO, how to deal with templates? + ) elif tag.startswith( "_" From d1ec3bf78d996b097f569261b4b96e72a0fde30b Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Thu, 16 May 2024 19:49:20 -0400 Subject: [PATCH 139/143] Update github actions --- .github/workflows/dist.yml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/dist.yml b/.github/workflows/dist.yml index 0ec4934..ce3ac9a 100644 --- a/.github/workflows/dist.yml +++ b/.github/workflows/dist.yml @@ -7,19 +7,19 @@ jobs: check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - uses: psf/black@stable check-doc: runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: submodules: recursive fetch-depth: 0 - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v4 with: python-version: 3.8 - name: Sphinx @@ -32,22 +32,22 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [windows-latest, macos-latest, ubuntu-20.04] - python_version: [3.5, 3.6, 3.7, 3.8] - architecture: [x86, x64] + os: [windows-latest, macos-13, ubuntu-20.04] + python_version: [3.8] + architecture: [x64] exclude: - - os: macos-latest + - os: macos-13 architecture: x86 - os: ubuntu-20.04 architecture: x86 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: submodules: recursive fetch-depth: 0 - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v4 with: python-version: ${{ matrix.python_version }} architecture: ${{ matrix.architecture }} @@ -72,12 +72,12 @@ jobs: if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: submodules: recursive fetch-depth: 0 - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v4 with: python-version: 3.8 - run: pip --disable-pip-version-check install wheel From 60238ebce89822ea3a57d62f9923734a2a2c7bd1 Mon Sep 17 00:00:00 2001 From: LE GARREC Vincent Date: Tue, 21 May 2024 07:38:13 +0200 Subject: [PATCH 140/143] Fix duplicated testcase name And fix CarrotClass_TestCase --- test/test_CppHeaderParser.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/test/test_CppHeaderParser.py b/test/test_CppHeaderParser.py index a7810a7..5714e6c 100644 --- a/test/test_CppHeaderParser.py +++ b/test/test_CppHeaderParser.py @@ -1162,14 +1162,9 @@ def test_method_params(self): [], ) - def test_class_template(self): - self.assertEqual( - self.cppHeader.classes["CarrotClass"]["template"], "template" - ) - # Bug 3517289 -class CarrotClass_TestCase(unittest.TestCase): +class ExternClass_TestCase(unittest.TestCase): def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") From 8a2caee9d82c00b819bdd2bb572911f3f7b9a8d9 Mon Sep 17 00:00:00 2001 From: LE GARREC Vincent Date: Wed, 22 May 2024 23:12:24 +0200 Subject: [PATCH 141/143] Add support to variable with [] inside type (#85) * Add support to variable with [] inside type std::unique_ptr variable; * Type of "array_size" should always be the same * Add test for std::unique_ptr * Add support for initialization with {} int double {0}; int double {(1.2+3.2)}; * Fix format --- CppHeaderParser/CppHeaderParser.py | 88 +++++++++++++++++++++--------- test/test_CppHeaderParser.py | 85 ++++++++++++++++++++++++++++- 2 files changed, 145 insertions(+), 28 deletions(-) diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index a7c2ce5..c6061e6 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -141,6 +141,7 @@ def trace_print(*args): _BRACE_REASON_OTHER = 0 _BRACE_REASON_NS = 1 _BRACE_REASON_EXTERN = 2 +_BRACE_REASON_VARIABLE = 3 # Track what was added in what order and at what depth parseHistory = [] @@ -239,10 +240,13 @@ def is_property_namestack(nameStack): r = False if "(" not in nameStack and ")" not in nameStack: r = True - elif ( - "(" in nameStack - and "=" in nameStack - and nameStack.index("=") < nameStack.index("(") + elif "(" in nameStack and ( + ( # = initialization + "=" in nameStack and nameStack.index("=") < nameStack.index("(") + ) + or ( # {} initialization + "{" in nameStack and nameStack.index("{") < nameStack.index("(") + ) ): r = True # See if we are a function pointer @@ -1217,29 +1221,48 @@ def __init__(self, nameStack, doxygen, location, is_var=True, **kwargs): else: self["extern"] = False + if "=" in nameStack: + self["type"] = " ".join(nameStack[: nameStack.index("=") - 1]) + self["name"] = nameStack[nameStack.index("=") - 1] + default = " ".join(nameStack[nameStack.index("=") + 1 :]) + nameStack = nameStack[: nameStack.index("=")] + default = self._filter_name(default) + self["default"] = default + # backwards compat; deprecate camelCase in dicts + self["defaultValue"] = default + elif "{" in nameStack and "}" in nameStack: + posBracket = nameStack.index("{") + self["type"] = " ".join(nameStack[: posBracket - 1]) + self["name"] = nameStack[posBracket - 1] + default = " ".join(nameStack[posBracket + 1 : -1]) + nameStack = nameStack[:posBracket] + default = self._filter_name(default) + self["default"] = default + # backwards compat; deprecate camelCase in dicts + self["defaultValue"] = default + _stack_ = nameStack - if "[" in nameStack: # strip off array informatin - arrayStack = nameStack[nameStack.index("[") :] - if nameStack.count("[") > 1: + self["array"] = 0 + while "]" in nameStack[-1]: # strip off array information + arrayPos = len(nameStack) - 1 - nameStack[::-1].index("[") + arrayStack = nameStack[arrayPos:] + if self["array"] == 1: debug_print("Multi dimensional array") debug_print("arrayStack=%s", arrayStack) - nums = [x for x in arrayStack if x.isdigit()] - # Calculate size by multiplying all dimensions - p = 1 - for n in nums: - p *= int(n) + if len(arrayStack) == 3: + n = arrayStack[1] # Multi dimensional array - self["array_size"] = p + if not "multi_dimensional_array_size" in self: + self["multi_dimensional_array_size"] = self["array_size"] + self["multi_dimensional_array_size"] += "x" + n + self["array_size"] = str(int(self["array_size"]) * int(n)) self["multi_dimensional_array"] = 1 - self["multi_dimensional_array_size"] = "x".join(nums) else: debug_print("Array") if len(arrayStack) == 3: self["array_size"] = arrayStack[1] - nameStack = nameStack[: nameStack.index("[")] + nameStack = nameStack[:arrayPos] self["array"] = 1 - else: - self["array"] = 0 nameStack = self._name_stack_helper(nameStack) if doxygen: @@ -1268,15 +1291,6 @@ def __init__(self, nameStack, doxygen, location, is_var=True, **kwargs): ) self["function_pointer"] = 1 - elif "=" in nameStack: - self["type"] = " ".join(nameStack[: nameStack.index("=") - 1]) - self["name"] = nameStack[nameStack.index("=") - 1] - default = " ".join(nameStack[nameStack.index("=") + 1 :]) - default = self._filter_name(default) - self["default"] = default - # backwards compat; deprecate camelCase in dicts - self["defaultValue"] = default - elif ( is_fundamental(nameStack[-1]) or nameStack[-1] in [">", "<", ":", "."] @@ -2931,7 +2945,23 @@ def __init__( continue if parenDepth == 0 and tok.type == "{": - self.lastBraceReason = _BRACE_REASON_OTHER + if self.nameStack[0] in ( + "class", + "struct", + "union", + "namespace", + "enum", + "extern", + "typedef", + ) or (is_method_namestack(self.stack) or (not self.curClass)): + self.lastBraceReason = _BRACE_REASON_OTHER + else: + # Case : type variable {init}; + self.lastBraceReason = _BRACE_REASON_VARIABLE + self.braceDepth += 1 + self.braceReason.append(self.lastBraceReason) + self.nameStack.append(tok.value) + continue if len(self.nameStack) >= 2 and is_namespace( self.nameStack ): # namespace {} with no name used in boost, this sets default? @@ -2991,6 +3021,10 @@ def __init__( self.linkage_stack.pop() self.stack = [] # clear stack when linkage ends? self.stmtTokens = [] + # Case : type variable {init}; + elif reason == _BRACE_REASON_VARIABLE: + self.nameStack.append(tok.value) + continue else: self._evaluate_stack() self.braceDepth -= 1 diff --git a/test/test_CppHeaderParser.py b/test/test_CppHeaderParser.py index 5714e6c..4f949f5 100644 --- a/test/test_CppHeaderParser.py +++ b/test/test_CppHeaderParser.py @@ -2359,7 +2359,7 @@ def setUp(self): def test_array_size(self): self.assertEqual( self.cppHeader.classes["Picture"]["properties"]["public"][1]["array_size"], - 16384, + "16384", ) def test_multi_dimensional_array_size(self): @@ -4222,5 +4222,88 @@ def test_fn(self): self.assertEqual(fn["linkage"], "") +# Github PR 85 +class ContainerOfArray_TestCase(unittest.TestCase): + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader( + """ +class ContainerOfArray { +public: + std::unique_ptr variable; + std::unique_ptr function(std::unique_ptr param1); +}; +""", + "string", + ) + + def test_rtntype(self): + self.assertEqual( + self.cppHeader.classes["ContainerOfArray"]["methods"]["public"][0][ + "rtnType" + ], + "std::unique_ptr", + ) + + def test_parameters(self): + self.assertEqual( + filter_pameters( + self.cppHeader.classes["ContainerOfArray"]["methods"]["public"][0][ + "parameters" + ] + ), + [{"name": "param1", "desc": None, "type": "std::unique_ptr"}], + ) + + def test_member(self): + self.assertEqual( + self.cppHeader.classes["ContainerOfArray"]["properties"]["public"][0][ + "name" + ], + "variable", + ) + self.assertEqual( + self.cppHeader.classes["ContainerOfArray"]["properties"]["public"][0][ + "type" + ], + "std::unique_ptr", + ) + + +class InitBracket_TestCase(unittest.TestCase): + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader( + """ +class InitBracket { +public: + int variable{10}; + std::shared_ptr variable2 {std::make_shared(150)}; +}; +""", + "string", + ) + + def test_member(self): + self.assertEqual( + self.cppHeader.classes["InitBracket"]["properties"]["public"][0]["name"], + "variable", + ) + self.assertEqual( + self.cppHeader.classes["InitBracket"]["properties"]["public"][0][ + "defaultValue" + ], + "10", + ) + self.assertEqual( + self.cppHeader.classes["InitBracket"]["properties"]["public"][1]["name"], + "variable2", + ) + self.assertEqual( + self.cppHeader.classes["InitBracket"]["properties"]["public"][1][ + "defaultValue" + ], + "std::make_shared ( 150 )", + ) + + if __name__ == "__main__": unittest.main() From 75a599ae13d46a940cdf9e93761078ed39faef27 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Wed, 22 May 2024 17:18:41 -0400 Subject: [PATCH 142/143] Update github actions publish action --- .github/workflows/dist.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dist.yml b/.github/workflows/dist.yml index ce3ac9a..198cb11 100644 --- a/.github/workflows/dist.yml +++ b/.github/workflows/dist.yml @@ -85,7 +85,7 @@ jobs: - name: Build packages run: python setup.py sdist bdist_wheel - name: Publish to PyPI - uses: pypa/gh-action-pypi-publish@master + uses: pypa/gh-action-pypi-publish@release/v1 with: user: __token__ password: ${{ secrets.PYPI_PASSWORD }} From 0feb2a8d76a459130f98666be4191ab41fd29400 Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Mon, 27 May 2024 23:06:12 -0400 Subject: [PATCH 143/143] Use trusted publishing --- .github/workflows/dist.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/dist.yml b/.github/workflows/dist.yml index 198cb11..95df148 100644 --- a/.github/workflows/dist.yml +++ b/.github/workflows/dist.yml @@ -70,6 +70,8 @@ jobs: runs-on: ubuntu-latest needs: [check, check-doc, test] if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') + permissions: + id-token: write steps: - uses: actions/checkout@v4 @@ -86,6 +88,3 @@ jobs: run: python setup.py sdist bdist_wheel - name: Publish to PyPI uses: pypa/gh-action-pypi-publish@release/v1 - with: - user: __token__ - password: ${{ secrets.PYPI_PASSWORD }}