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 @@
-
-
-Python: module CppHeaderParser
-
-
-
-
-
-
 
- 
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 @@ - - - - - - - - - - -

- - -
-

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 }} pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy