Skip to content

Instantly share code, notes, and snippets.

@SpaghettDev
Last active August 31, 2024 21:04
Show Gist options
  • Save SpaghettDev/3744581418c69560713cfb36101836b0 to your computer and use it in GitHub Desktop.
Save SpaghettDev/3744581418c69560713cfb36101836b0 to your computer and use it in GitHub Desktop.
C++ function arguments parser in python (no regex real). From BromaIDA.
CPP_TYPE_SPECIFIERS = ("unsigned", "signed")
CPP_TYPE_QUALIFIERS = ("const", "volatile")
def parse_str_args(args_str: str) -> list[tuple[str]]:
"""Mini parser that converts a string of arguments
into a list of tuples in the form of ("type", "name").
Supports templates, named and unnamed arguments,
as well as stuff like long long, const short int...
Args:
args_str (str)
Returns:
list[tuple[str]]: list of tuples in the form ("type", "name").
If the argument is unnamed, the "name" is an empty string.
"""
args_list: list[str] = []
argstype_list: list[tuple[str]] = []
cur_arg = ""
nested_template_count = 0
for i, c in enumerate(args_str):
if c == "<":
nested_template_count += 1
elif c == ">":
nested_template_count -= 1
if nested_template_count == 0 and (c == "," or i == len(args_str) - 1):
if i == len(args_str) - 1:
cur_arg += c
# replace east pointer or reference to west pointer or reference
# (~because east sucks~, I mean because then it'll be a part
# of the name and not the type)
arg = cur_arg.lstrip().replace(" &", "& ").replace(" *", "* ")
parts = arg.split()
# parts with no specifiers nor qualifiers
# this isn't actually used to build the type,
# its only to avoid stuff like "long long"
# being interpreted as type: long, name: long
parts_no_sq = arg.split()
for x in CPP_TYPE_SPECIFIERS + CPP_TYPE_QUALIFIERS:
if x in parts_no_sq:
parts_no_sq.remove(x)
if "long" in parts_no_sq:
try:
if parts_no_sq[parts_no_sq.index("long") + 1] == "long":
parts_no_sq.pop(parts_no_sq.index("long") + 1)
if parts_no_sq[parts_no_sq.index("long") + 1] == "int":
parts_no_sq.pop(parts_no_sq.index("long") + 1)
except: pass
if "short" in parts_no_sq:
try:
if parts_no_sq[parts_no_sq.index("short") + 1] == "int":
parts_no_sq.pop(parts_no_sq.index("short") + 1)
except: pass
arg_type: str
arg_name: str
if len(parts_no_sq) > 1 and \
not parts_no_sq[-1].endswith("*") and \
not parts_no_sq[-1].endswith("&"):
arg_type = " ".join(parts[:-1])
arg_name = parts[-1]
else:
arg_type = " ".join(parts)
arg_name = ""
argstype_list.append((arg_type, arg_name))
cur_arg = ""
continue
cur_arg += c
return argstype_list
parse_str_args("std::string const&, char const*, std::map<std::string, std::string>&")
# [('std::string const&', ''), ('char const*', ''), ('std::map<std::string, std::string>&', '')]
parse_str_args("std::string a1, const char, long long int* a3")
# [('std::string', 'a1'), ('const char', ''), ('long long int*', 'a3')]
parse_str_args("const std::vector<int> &a1, volatile bool*, char *a3")
# [('const std::vector<int>&', 'a1'), ('volatile bool*', ''), ('char*', 'a3')]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment