Skip to content

Commit da9a33c

Browse files
authored
Merge pull request robotpy#32 from robotpy/yup
Yup
2 parents 5432b45 + f6109d2 commit da9a33c

File tree

3 files changed

+144
-57
lines changed

3 files changed

+144
-57
lines changed

CppHeaderParser/CppHeaderParser.py

Lines changed: 88 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,9 @@ def is_method_namestack(stack):
193193
elif "{" in stack and stack.index("{") < stack.index("("):
194194
r = False # struct that looks like a method/class
195195
elif "(" in stack and ")" in stack:
196-
if "{" in stack and "}" in stack:
196+
if stack[-1] == ":":
197+
r = True
198+
elif "{" in stack and "}" in stack:
197199
r = True
198200
elif stack[-1] == ";":
199201
if is_function_pointer_stack(stack):
@@ -255,11 +257,13 @@ def _split_namespace(namestack):
255257
256258
:rtype: Tuple[str, list]
257259
"""
260+
# TODO: this should be using tokens instead of nhack
261+
258262
last_colon = None
259263
for i, n in enumerate(namestack):
260-
if n == ":":
264+
if n == "::":
261265
last_colon = i
262-
if i and n != ":" and not _nhack.match(n):
266+
if i and n != "::" and not _nhack.match(n):
263267
break
264268

265269
if last_colon:
@@ -472,12 +476,8 @@ def _parse_cppclass_name(c, stack):
472476
if t == ":":
473477
if i >= sl:
474478
raise CppParseError("class decl ended with ':'")
475-
t = stack[i]
476-
if t != ":":
477-
# reached the base declaration
478-
break
479-
480-
i += 1
479+
break
480+
elif t == "::":
481481
name += "::"
482482
continue
483483
elif t == "final":
@@ -954,7 +954,7 @@ def __init__(self, nameStack, curClass, methinfo, curTemplate, doxygen, location
954954
if len(self["rtnType"]) == 0 or self["name"] == curClass:
955955
self["rtnType"] = "void"
956956

957-
self["rtnType"] = self["rtnType"].replace(" : : ", "::")
957+
self["rtnType"] = self["rtnType"].replace(" :: ", "::")
958958
self["rtnType"] = self["rtnType"].replace(" < ", "<")
959959
self["rtnType"] = self["rtnType"].replace(" > ", "> ").replace(">>", "> >")
960960
self["rtnType"] = self["rtnType"].replace(" ,", ",")
@@ -996,22 +996,6 @@ def __init__(self, nameStack, curClass, methinfo, curTemplate, doxygen, location
996996
self.update(methinfo)
997997
set_location_info(self, location)
998998

999-
# Filter out initializer lists used in constructors
1000-
try:
1001-
paren_depth_counter = 0
1002-
for i in range(0, len(nameStack)):
1003-
elm = nameStack[i]
1004-
if elm == "(":
1005-
paren_depth_counter += 1
1006-
if elm == ")":
1007-
paren_depth_counter -= 1
1008-
if paren_depth_counter == 0 and nameStack[i + 1] == ":":
1009-
debug_print("Stripping out initializer list")
1010-
nameStack = nameStack[: i + 1]
1011-
break
1012-
except:
1013-
pass
1014-
1015999
paramsStack = self._params_helper1(nameStack)
10161000

10171001
debug_print("curTemplate: %s", curTemplate)
@@ -1959,8 +1943,8 @@ def finalize(self):
19591943
)
19601944
meth["returns_unknown"] = True
19611945

1962-
if meth["returns"].startswith(": : "):
1963-
meth["returns"] = meth["returns"].replace(": : ", "::")
1946+
if meth["returns"].startswith(":: "):
1947+
meth["returns"] = meth["returns"].replace(":: ", "::")
19641948

19651949
for cls in list(self.classes.values()):
19661950
methnames = cls.get_all_method_names()
@@ -1992,11 +1976,9 @@ def finalize(self):
19921976

19931977
def parse_method_type(self, stack):
19941978
trace_print("meth type info", stack)
1995-
if stack[0] in ":;" and stack[1] != ":":
1996-
stack = stack[1:]
19971979
info = {
19981980
"debug": " ".join(stack)
1999-
.replace(" : : ", "::")
1981+
.replace(" :: ", "::")
20001982
.replace(" < ", "<")
20011983
.replace(" > ", "> ")
20021984
.replace(" >", ">")
@@ -2010,7 +1992,7 @@ def parse_method_type(self, stack):
20101992

20111993
header = stack[: stack.index("(")]
20121994
header = " ".join(header)
2013-
header = header.replace(" : : ", "::")
1995+
header = header.replace(" :: ", "::")
20141996
header = header.replace(" < ", "<")
20151997
header = header.replace(" > ", "> ")
20161998
header = header.replace("default ", "default")
@@ -2022,6 +2004,10 @@ def parse_method_type(self, stack):
20222004
self.braceHandled = True
20232005
elif stack[-1] == ";":
20242006
info["defined"] = False
2007+
elif stack[-1] == ":":
2008+
info["defined"] = True
2009+
self._discard_ctor_initializer()
2010+
self.braceHandled = True
20252011
else:
20262012
assert 0
20272013

@@ -2452,6 +2438,7 @@ def evalute_forward_decl(self):
24522438
"+",
24532439
"STRING_LITERAL",
24542440
"ELLIPSIS",
2441+
"DBL_COLON",
24552442
"SHIFT_LEFT",
24562443
}
24572444

@@ -2651,6 +2638,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs):
26512638
#
26522639

26532640
self._doxygen_cache = None
2641+
self.braceHandled = False
26542642
tok = None
26552643
self.stmtTokens = []
26562644

@@ -2809,6 +2797,12 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs):
28092797
self.nameStack = []
28102798
self.stack = []
28112799
self.stmtTokens = []
2800+
elif is_method_namestack(self.stack):
2801+
debug_print("trace")
2802+
self._evaluate_method_stack()
2803+
self.nameStack = []
2804+
self.stack = []
2805+
self.stmtTokens = []
28122806
else:
28132807
self.nameStack.append(tok.value)
28142808

@@ -2874,6 +2868,7 @@ def __init__(self, headerFileName, argType="file", encoding=None, **kwargs):
28742868
for key in [
28752869
"_precomp_macro_buf",
28762870
"_doxygen_cache",
2871+
"braceHandled",
28772872
"lex",
28782873
"nameStack",
28792874
"nameSpaces",
@@ -2926,7 +2921,7 @@ def _parse_error(self, tokens, expected):
29262921
def _next_token_must_be(self, *tokenTypes):
29272922
tok = self.lex.token()
29282923
if tok.type not in tokenTypes:
2929-
raise self._parse_error((tok,), " or ".join(tokenTypes))
2924+
raise self._parse_error((tok,), "' or '".join(tokenTypes))
29302925
return tok
29312926

29322927
_end_balanced_tokens = {">", "}", "]", ")", "DBL_RBRACKET"}
@@ -2986,6 +2981,59 @@ def _discard_contents(self, start_type, end_type):
29862981
if level == 0:
29872982
break
29882983

2984+
def _discard_ctor_initializer(self):
2985+
"""
2986+
ctor_initializer: ":" mem_initializer_list
2987+
2988+
mem_initializer_list: mem_initializer ["..."]
2989+
| mem_initializer "," mem_initializer_list ["..."]
2990+
2991+
mem_initializer: mem_initializer_id "(" [expression_list] ")"
2992+
| mem_initializer_id braced_init_list
2993+
2994+
mem_initializer_id: class_or_decltype
2995+
| IDENTIFIER
2996+
"""
2997+
debug_print("discarding ctor intializer")
2998+
# all of this is discarded.. the challenge is to determine
2999+
# when the initializer ends and the function starts
3000+
while True:
3001+
tok = self.lex.token()
3002+
if tok.type == "DBL_COLON":
3003+
tok = self.lex.token()
3004+
3005+
if tok.type == "decltype":
3006+
tok = self._next_token_must_be("(")
3007+
self._consume_balanced_tokens(tok)
3008+
tok = self.lex.token()
3009+
3010+
# each initializer is either foo() or foo{}, so look for that
3011+
while True:
3012+
if tok.type not in ("{", "("):
3013+
tok = self.lex.token()
3014+
continue
3015+
3016+
if tok.type == "{":
3017+
self._discard_contents("{", "}")
3018+
elif tok.type == "(":
3019+
self._discard_contents("(", ")")
3020+
3021+
tok = self.lex.token()
3022+
break
3023+
3024+
# at the end
3025+
if tok.type == "ELLIPSIS":
3026+
tok = self.lex.token()
3027+
3028+
if tok.type == ",":
3029+
continue
3030+
elif tok.type == "{":
3031+
# reached the function
3032+
self._discard_contents("{", "}")
3033+
return
3034+
else:
3035+
raise self._parse_error((tok,), ",' or '{")
3036+
29893037
def _evaluate_stack(self, token=None):
29903038
"""Evaluates the current name stack"""
29913039

@@ -3076,24 +3124,10 @@ def _evaluate_stack(self, token=None):
30763124
self.using[alias] = atype
30773125
elif is_method_namestack(self.stack) and "(" in self.nameStack:
30783126
debug_print("trace")
3079-
if self.braceDepth > 0:
3080-
if (
3081-
"{" in self.stack
3082-
and self.stack[0] != "{"
3083-
and self.stack[-1] == ";"
3084-
and self.braceDepth == 1
3085-
):
3086-
# Special case of a method defined outside a class that has a body
3087-
pass
3088-
else:
3089-
self._evaluate_method_stack()
3090-
else:
3091-
# Free function
3092-
self._evaluate_method_stack()
3127+
self._evaluate_method_stack()
30933128
elif is_enum_namestack(self.nameStack):
30943129
debug_print("trace")
30953130
self._parse_enum()
3096-
self.nameStack = []
30973131
self.stack = []
30983132
self.stmtTokens = []
30993133
elif (
@@ -3137,25 +3171,22 @@ def _evaluate_stack(self, token=None):
31373171

31383172
elif not self.curClass:
31393173
debug_print("trace")
3140-
self.nameStack = []
31413174
elif self.braceDepth < 1:
31423175
debug_print("trace")
31433176
# Ignore global stuff for now
31443177
debug_print("Global stuff: %s" % self.nameStack)
3145-
self.nameStack = []
31463178
elif self.braceDepth > len(self.nameSpaces) + 1:
31473179
debug_print("trace")
3148-
self.nameStack = []
31493180
else:
31503181
debug_print("Discarded statement %s" % (self.nameStack,))
31513182

31523183
try:
31533184
self.nameStackHistory[self.braceDepth] = (nameStackCopy, self.curClass)
31543185
except:
31553186
self.nameStackHistory.append((nameStackCopy, self.curClass))
3156-
self.nameStack = (
3157-
[]
3158-
) # its a little confusing to have some if/else above return and others not, and then clearning the nameStack down here
3187+
3188+
# its a little confusing to have some if/else above return and others not, and then clearning the nameStack down here
3189+
self.nameStack = []
31593190
self.lex.doxygenCommentCache = ""
31603191
self.curTemplate = None
31613192

@@ -3164,7 +3195,7 @@ def _parse_template(self):
31643195
consumed = self._consume_balanced_tokens(tok)
31653196
tmpl = " ".join(tok.value for tok in consumed)
31663197
tmpl = (
3167-
tmpl.replace(" : : ", "::")
3198+
tmpl.replace(" :: ", "::")
31683199
.replace(" <", "<")
31693200
.replace("< ", "<")
31703201
.replace(" >", ">")
@@ -3364,10 +3395,10 @@ def _parse_enumerator_list(self, values):
33643395
while True:
33653396
tok = self.lex.token()
33663397
if tok.type == "}":
3367-
value["value"] = (" ".join(v)).replace(": :", "::")
3398+
value["value"] = " ".join(v)
33683399
return
33693400
elif tok.type == ",":
3370-
value["value"] = (" ".join(v)).replace(": :", "::")
3401+
value["value"] = " ".join(v)
33713402
break
33723403
elif tok.type in self._balanced_token_map:
33733404
v.extend(t.value for t in self._consume_balanced_tokens(tok))

CppHeaderParser/lexer.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ class Lexer(object):
2222
"ELLIPSIS",
2323
"DBL_LBRACKET",
2424
"DBL_RBRACKET",
25+
"DBL_COLON",
2526
"SHIFT_LEFT",
2627
]
2728

@@ -85,6 +86,7 @@ def t_COMMENT_SINGLELINE(self, t):
8586
t_ELLIPSIS = r"\.\.\."
8687
t_DBL_LBRACKET = r"\[\["
8788
t_DBL_RBRACKET = r"\]\]"
89+
t_DBL_COLON = r"::"
8890
t_SHIFT_LEFT = r"<<"
8991
# SHIFT_RIGHT introduces ambiguity
9092

CppHeaderParser/test/test_CppHeaderParser.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3366,5 +3366,59 @@ def test_nothing(self):
33663366
self.assertEqual(self.cppHeader.functions, [])
33673367

33683368

3369+
class InitializerWithInitializerList_TestCase(unittest.TestCase):
3370+
def setUp(self):
3371+
self.cppHeader = CppHeaderParser.CppHeader(
3372+
"""
3373+
struct ComplexInit : SomeBase {
3374+
ComplexInit(int i) :
3375+
m_stuff{i,2}
3376+
{
3377+
auto i = something();
3378+
}
3379+
3380+
void fn();
3381+
3382+
std::vector<int> m_stuff;
3383+
};
3384+
3385+
template <typename T>
3386+
class future final {
3387+
public:
3388+
template <typename R>
3389+
future(future<R>&& oth) noexcept
3390+
: future(oth.then([](R&& val) -> T { return val; })) {}
3391+
};
3392+
3393+
3394+
""",
3395+
"string",
3396+
)
3397+
3398+
def test_cls_props(self):
3399+
c = self.cppHeader.classes["ComplexInit"]
3400+
self.assertEqual(2, len(c["methods"]["public"]))
3401+
self.assertEqual(0, len(c["methods"]["private"]))
3402+
self.assertEqual(0, len(c["methods"]["private"]))
3403+
self.assertEqual(1, len(c["properties"]["public"]))
3404+
self.assertEqual(0, len(c["properties"]["private"]))
3405+
self.assertEqual(0, len(c["properties"]["protected"]))
3406+
3407+
self.assertEqual(c["methods"]["public"][0]["name"], "ComplexInit")
3408+
self.assertEqual(c["methods"]["public"][1]["name"], "fn")
3409+
3410+
self.assertEqual(c["properties"]["public"][0]["name"], "m_stuff")
3411+
3412+
def test_future(self):
3413+
c = self.cppHeader.classes["future"]
3414+
self.assertEqual(1, len(c["methods"]["public"]))
3415+
self.assertEqual(0, len(c["methods"]["private"]))
3416+
self.assertEqual(0, len(c["methods"]["private"]))
3417+
self.assertEqual(0, len(c["properties"]["public"]))
3418+
self.assertEqual(0, len(c["properties"]["private"]))
3419+
self.assertEqual(0, len(c["properties"]["protected"]))
3420+
self.assertEqual(c["methods"]["public"][0]["name"], "future")
3421+
3422+
33693423
if __name__ == "__main__":
33703424
unittest.main()

0 commit comments

Comments
 (0)
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