745 lines
26 KiB
Python
Executable File
745 lines
26 KiB
Python
Executable File
#!/bin/python3
|
|
"""
|
|
A python script to generate empty definitions from a header file having declarations
|
|
Copyright © 2022 Matthias Quintern.
|
|
This software comes with no warranty.
|
|
This software is licensed under the GPL3
|
|
"""
|
|
|
|
from typing import Callable
|
|
from os import path, listdir, chdir
|
|
from sys import argv, exit
|
|
import re
|
|
|
|
print_debug = False
|
|
|
|
# search in header files for enums
|
|
header_filetypes = [".hpp"]
|
|
# definition are written to source file having the same name as the header
|
|
source_filetype = ".cpp"
|
|
|
|
def error(s):
|
|
print(s)
|
|
exit(1)
|
|
|
|
constexpr = "constexpr"
|
|
inline = "inline"
|
|
static = "static"
|
|
|
|
ACC_PUBLIC = 0
|
|
ACC_PRIVATE = 1
|
|
ACC_PROTECTED = 2
|
|
acc_to_uml = [ '+', '-', '#' ]
|
|
acc_to_str = [ 'public', 'private', 'protected' ]
|
|
uml_style_autoresize = True
|
|
|
|
def template_str2list(s:str):
|
|
if s:
|
|
templates = s.removeprefix("template<").removesuffix(">").split(", ")
|
|
templates = [ t.split(" ") for t in templates ]
|
|
else:
|
|
templates = []
|
|
return templates
|
|
|
|
def template_list2str(t:list[tuple[str, str]], req:str=""):
|
|
if len(t) == 0:
|
|
return ""
|
|
s = "template<"
|
|
for temp in t:
|
|
s += temp[0] + " " + temp[1] + ", "
|
|
s = s.removesuffix(", ") + ">\n"
|
|
if req:
|
|
s += "\t" + "requires " + req
|
|
return s
|
|
|
|
def append_comma_separated(s: str, l: list[str]):
|
|
for e in l:
|
|
s += e + ", "
|
|
return s.removesuffix(", ")
|
|
def apply_and_append_comma_separated(s: str, l: list[str], f: Callable[[str], str]):
|
|
for e in l:
|
|
s += f(e) + ", "
|
|
return s.removesuffix(", ")
|
|
|
|
def transformString(s: str, depth: int):
|
|
"""Add tabs after all but the last line break and before the first char.
|
|
Do not add tabs to lines that start with a label"""
|
|
new_s = ""
|
|
for line in s.split("\n"):
|
|
if re.fullmatch(r"\w+:[^:].*", line) is None:
|
|
new_s += depth * "\t";
|
|
new_s += line + "\n"
|
|
new_s = new_s.removesuffix("\n").removesuffix(depth*"\t")
|
|
# return depth * tab + s.replace("\n", "\n" + depth * tab, s.count("\n") - 1)
|
|
return new_s
|
|
|
|
def firstLetterUppercase(s: str) -> str:
|
|
return s[0].capitalize() + s[1:]
|
|
|
|
type_to_uml_alias = {
|
|
"unsigned int": "uint",
|
|
"glm::vec2": "vec2",
|
|
"glm::vec3": "vec3",
|
|
}
|
|
re_templated_type = r"([\w:]+)<(.*)>"
|
|
|
|
def type_to_uml(t: str) -> str:
|
|
"""
|
|
std::vector<T> -> T[]
|
|
std::array<T, N> -> T[N]
|
|
std::map<K, T> -> { K : T }
|
|
std::reference_wrapper<T> -> T&
|
|
"""
|
|
if t in type_to_uml_alias.keys():
|
|
return type_to_uml_alias[t]
|
|
# if templated
|
|
match = re.search(re_templated_type, t)
|
|
if match:
|
|
# remove namespaces
|
|
# name of outer template
|
|
t = match.groups()[0].split(":")[-1]
|
|
# the template arguments
|
|
temps = [ "" ]
|
|
scope = 0
|
|
for c in match.groups()[1]:
|
|
if c == "<": scope += 1
|
|
elif c == ">": scope -= 1
|
|
elif c == ",":
|
|
if scope == 0:
|
|
temps.append("")
|
|
continue
|
|
temps[-1] += c
|
|
if print_debug: print("type_to_uml", t, temps)
|
|
|
|
if len(temps) == 0:
|
|
pass
|
|
elif len(temps) == 1:
|
|
if t in [ "vector" ]:
|
|
return type_to_uml(temps[0]) + "[]"
|
|
elif t in [ "reference_wrapper" ]:
|
|
return type_to_uml(temps[0]) + "&"
|
|
elif len(temps) == 2:
|
|
if "map" in t:
|
|
return "{ " + type_to_uml(temps[0]) + " : " + type_to_uml(temps[1]) + " }"
|
|
elif "pair" in t:
|
|
return "{ " + type_to_uml(temps[0]) + ", " + type_to_uml(temps[1]) + " }"
|
|
elif "array" in t:
|
|
return f"{type_to_uml(temps[0])}[{type_to_uml(temps[1])}]"
|
|
else:
|
|
if t in "tuple":
|
|
return "{ " + apply_and_append_comma_separated(t, temps, type_to_uml) + " }"
|
|
|
|
return apply_and_append_comma_separated(t + "<", temps, type_to_uml) + ">"
|
|
return t.strip(" ")
|
|
|
|
|
|
class Member:
|
|
def __init__(self, name:str, m_temp:list=[], prefixes:list=[], ret_type:str="", args:str="", attributes="", init:str="", requires:str="", suffixes="", defined=False, is_value=False, namespace:str="", accessibility=ACC_PUBLIC):
|
|
"""
|
|
:param name: member name
|
|
:param m_temp: list of templates of the member
|
|
:param prefixes: things like constexpr, inline...
|
|
:param ret_type: return type of the member
|
|
:param args: arguments of the member
|
|
:param init: initializion of the member
|
|
:param attributes: c++11 attributes [[ ... ]]
|
|
:param requires: constraints on template types
|
|
:param suffixes: const noexcept
|
|
:param defined: method defined
|
|
:param is_value: wether the member is a value or a method
|
|
:param accessibility: public, private or protected
|
|
|
|
template<c_temp>
|
|
class class {
|
|
comment
|
|
template<m_temp>
|
|
prefixes ret_type name(args) const : init {
|
|
body
|
|
}
|
|
}
|
|
"""
|
|
self.name = name
|
|
self.prefixes = prefixes
|
|
self.m_temp = m_temp
|
|
self.ret_type = ""
|
|
if ret_type: self.ret_type = ret_type + " "
|
|
self.args = args
|
|
self.attributes = attributes
|
|
self.init = ""
|
|
if init: self.init = f"\n\t : {init} "
|
|
self.requires = requires
|
|
self.suffixes = suffixes # have a ' ' in front of them
|
|
self.defined = defined
|
|
self.comment = ""
|
|
self.accessibility = accessibility
|
|
# if comment:
|
|
# if type(comment) == str:
|
|
# self.comment = "/// " + comment + "\n"
|
|
# elif type(comment) == dict:
|
|
# self.comment = comment_from_dict(comment)
|
|
self.is_value = is_value
|
|
self.namespace = namespace
|
|
|
|
def getArgsAsList(self):
|
|
args = []
|
|
const = False
|
|
arglist = self.args.split(", ")
|
|
for i in range(len(arglist)):
|
|
if "const" in arglist[i]:
|
|
arglist[i] = arglist[i].replace("const ", "")
|
|
const = True
|
|
args.append(arglist[i].split(" "))
|
|
if const:
|
|
args[-1][0] = "const " + args[-1][0]
|
|
return args
|
|
|
|
def get_source_name(self, c_temp=[], class_=""):
|
|
s = ""
|
|
if class_:
|
|
if len(c_temp) > 0:
|
|
class_ = class_ + "<"
|
|
for t in c_temp:
|
|
class_ += f"{t[1]}, "
|
|
class_ = class_.removesuffix(", ") + ">"
|
|
class_ += "::"
|
|
|
|
s += template_list2str(self.m_temp, self.requires)
|
|
for p in self.prefixes:
|
|
s += p + " "
|
|
|
|
if self.is_value:
|
|
if static in self.prefixes and not constexpr in self.prefixes:
|
|
s += f"{self.ret_type}{class_}{self.name};\n"
|
|
return s
|
|
else:
|
|
s += f"{self.ret_type}{class_}{self.name};\n"
|
|
return s
|
|
s += f"{self.ret_type}{class_}{self.name}({self.args}){self.suffixes}"
|
|
return s
|
|
|
|
def source(self, c_temp=[], class_="", depth:int=0):
|
|
"""
|
|
Return the empty defition + declarations for a source file
|
|
"""
|
|
s = ""
|
|
if class_:
|
|
s += template_list2str(c_temp)
|
|
if len(c_temp) > 0:
|
|
class_ = class_ + "<"
|
|
for t in c_temp:
|
|
class_ += f"{t[1]}, "
|
|
class_ = class_.removesuffix(", ") + ">"
|
|
class_ += "::"
|
|
|
|
s += template_list2str(self.m_temp, self.requires)
|
|
for p in self.prefixes:
|
|
if p not in [ "static" ]:
|
|
s += p + " "
|
|
|
|
if self.is_value:
|
|
if static in self.prefixes and not constexpr in self.prefixes:
|
|
s += f"{self.ret_type}{class_}{self.name};\n"
|
|
return s
|
|
else:
|
|
return ""
|
|
|
|
s += f"{self.ret_type}{class_}{self.name}({self.args}){self.suffixes.replace(' override', '')}{self.init}" + " {\n\t\n}\n"
|
|
# s += " {\n"
|
|
# body = ""
|
|
# for l in self.body: body += l + "\n"
|
|
# s += transformString(body, depth)
|
|
# s += "}\n"
|
|
# else:
|
|
# s += "{}"
|
|
return transformString(s, depth)
|
|
|
|
def uml(self):
|
|
"""
|
|
Return uml representation of the member
|
|
accessibility
|
|
"""
|
|
if not self.ret_type: return "" # if constructor, ret_type is empty
|
|
s = acc_to_uml[self.accessibility] + " "
|
|
s += self.name
|
|
# if function
|
|
if not self.is_value:
|
|
s += "("
|
|
for arg in self.args.split(','):
|
|
s += type_to_uml(arg.replace("const", "").strip(' ').split(' ')[0]) + ", "
|
|
s = s.strip(", ") + ")"
|
|
s += ": " + type_to_uml(self.ret_type)
|
|
return s + "\n"
|
|
|
|
def getter(self, depth:int=0):
|
|
"""
|
|
Return a getter
|
|
"""
|
|
return transformString(f"inline const {self.ret_type.strip(' ')}& get{firstLetterUppercase(self.name)}() const " + "{ " + f"return {self.name};" + " }\n", depth)
|
|
|
|
def lv_setter(self, depth:int=0):
|
|
"""
|
|
Return a lvalue setter
|
|
"""
|
|
return transformString(f"inline void set{firstLetterUppercase(self.name)}(const {self.ret_type.strip(' ')}& v) " + "{ " + f"{self.name} = v;" + " }\n", depth)
|
|
|
|
def rv_setter(self, depth:int=0):
|
|
"""
|
|
Return a rvalue setter
|
|
"""
|
|
return transformString(f"inline void set{firstLetterUppercase(self.name)}({self.ret_type.strip(' ')}&& v) " + "{ " + f"{self.name} = std::move(v);" + " }\n", depth)
|
|
|
|
def __eq__(self, other) -> bool:
|
|
return self.name == other.name and self.args == other.args and self.ret_type == other.ret_type
|
|
|
|
def __repr__(self):
|
|
return self.get_source_name([], "")
|
|
|
|
|
|
class Class:
|
|
def __init__(self, name: str, c_temp: list[tuple[str, str]]):
|
|
self.member_functions: list[Member] = []
|
|
self.member_variables: list[Member] = []
|
|
self.name = name
|
|
self.c_temp: list[tuple[str, str]] = c_temp
|
|
|
|
def __repr__(self) -> str:
|
|
return f"<{self.name}: {self.member_variables}, {self.member_functions}>"
|
|
|
|
def uml(self):
|
|
"""
|
|
Beginning of uml class
|
|
"""
|
|
s = ""
|
|
if self.c_temp:
|
|
s += "template="
|
|
for temp, t in self.c_temp:
|
|
if temp == "typename":
|
|
s += t + ", "
|
|
else:
|
|
s += t + ": " + temp + ", "
|
|
s = s.strip(", ") + "\n"
|
|
s += self.name + "\n--\n"
|
|
for member in self.member_variables:
|
|
s += member.uml()
|
|
if self.member_variables: s += "--\n"
|
|
for member in self.member_functions:
|
|
s += member.uml()
|
|
if uml_style_autoresize: s += "style=autoresize"
|
|
return s + "\n"
|
|
|
|
|
|
#
|
|
# regex
|
|
#
|
|
# s must be a string where all indentation was removed
|
|
# COMMENTS
|
|
def starts_with_normal_comment(s: str) -> bool:
|
|
if re.search(r"^(//|/?\*)[^*/]", s): return True
|
|
else: return False
|
|
|
|
def starts_with_doxygen_comment(s: str) -> bool:
|
|
if re.search(r"^(///|/\*\*)", s): return True
|
|
else: return False
|
|
|
|
def starts_with_comment(s: str) -> bool:
|
|
return starts_with_normal_comment(s) or starts_with_doxygen_comment(s);
|
|
|
|
def starts_with_comment_end(s: str) -> bool:
|
|
if re.search(r"^(\*/)", s): return True
|
|
else: return False
|
|
|
|
# NAMESPACE
|
|
def namespace_open(s: str) -> tuple[bool, str]:
|
|
match = re.search(r"^namespace (\w+) *{", s)
|
|
if match:
|
|
return True, match.groups()[0]
|
|
return False, ""
|
|
|
|
|
|
# TEMPLATE
|
|
def template_declaration(s: str) -> bool:
|
|
if re.search(r"^template<(.+)>", s):
|
|
return True
|
|
return False
|
|
|
|
# CLASS DECLARATION
|
|
def class_declaration(s: str) -> tuple[bool, str]:
|
|
match = re.search(r"^(?:(?:class)|(?:struct)) (\w+)", s)
|
|
if match:
|
|
return True, match.groups()[0]
|
|
return False, ""
|
|
|
|
# VARIABLE/MEMBER DECLARATION
|
|
def variable_declaration(s: str, template_str:str = "", namespace:str="") -> tuple[bool, Member]:
|
|
match = re.search(r"^((?:[\w<>:,]+ )+)(\w+)( *= *.+)? *;", s)
|
|
# match = re.search(r"^((?:\w+ )+)(\w+) *(const)? *;", s) # declration
|
|
if match:
|
|
g = match.groups()
|
|
prefixes = []
|
|
ret_type = ""
|
|
if g[2]: defined = True
|
|
else: defined = False
|
|
# get ret_type and prefixes
|
|
for prefix in g[0].strip(" ").split(" "):
|
|
if prefix in ["const", "constexpr", "inline", "static"]:
|
|
prefixes.append(prefix)
|
|
else:
|
|
ret_type += prefix + " "
|
|
return True, Member(g[1], m_temp=template_str2list(template_str), prefixes=prefixes, ret_type=ret_type, is_value=True, defined=defined, namespace=namespace)
|
|
return False, Member("")
|
|
|
|
re_spaces = r"[ \t]*" # no or more spaces
|
|
re_spaces1 = r"[ \t]+" # at least one space
|
|
# each of these regexes captures something in one group
|
|
re_ret_type = r"((?:[\w<>:,&*()]+ )+)" # return type
|
|
re_attributes = r"((?:\[\[.+\]\])?)" # eg [[ nodiscard ]]
|
|
re_function_name = r"(\w+(?:(?:\[\])|(?:\(\))|[+\-/%=*&]{0,2})?)" # function name: eg operator[], operator%=, do_smth
|
|
re_function_args = r"\(((?:[\w<>:,&*= ]+,? )*(?:[\w<>:,&*= ]+))?\)" # function parameters (capture without ()), eg (std::map<std::string, int> map, const Args&&... args)
|
|
re_suffixes = r"(" # eg const noexcept
|
|
for suffix in [ "const", "noexcept", "override" ]:
|
|
re_suffixes += f"(?:{re_spaces1}{suffix})?"
|
|
re_suffixes += ")"
|
|
|
|
re_constructor = "^" + re_function_name + re_spaces + re_function_args + re_spaces + re_suffixes
|
|
re_member_f = "^" + re_attributes + re_spaces + re_ret_type + re_spaces + re_function_name + re_spaces + re_function_args + re_suffixes
|
|
re_member_f_declaration = re_member_f + re_spaces + ";"
|
|
re_member_f_dec_with_def = re_member_f + re_spaces + r"\{"
|
|
|
|
if print_debug: print("re_member_f_declaration:", re_member_f_declaration)
|
|
if print_debug: print("re_member_f_dec_with_def:", re_member_f_dec_with_def)
|
|
if print_debug: print("re_constructor:", re_constructor)
|
|
|
|
# FUNCTION DECLARATION
|
|
def function_declaration(s: str, template_str:str = "", namespace: str="") -> tuple[bool, Member]:
|
|
# match = re.search(r"^((?:[\w<>:,&*]+ )+)(\w+)\(((?:[\w<>:,&*]+,? )*(?:[\w<>:,&*=]+))?\) *((const)?) *;", s) # declration
|
|
# groups are: attributes, ret_type, name, args, suffixes
|
|
# if print_debug: print("function_declaration:", s.strip())
|
|
match = re.search(re_member_f_declaration, s) # declaration
|
|
if match is None:
|
|
# match = re.search(r"^((?:[\w<>:,&*]+ )+)(\w+)\(((?:[\w<>:,&*]+,? )*(?:[\w<>:,&*=]+))?\) *((?:const)?) * *{", s) # declaration with definition
|
|
match = re.search(re_member_f_dec_with_def, s) # declaration with definition
|
|
defined = True
|
|
else:
|
|
defined = False
|
|
if match:
|
|
g = match.groups()
|
|
prefixes = []
|
|
ret_type = ""
|
|
# get ret_type and prefixes
|
|
for prefix in g[1].strip(" ").split(" "):
|
|
if prefix in ["const", "constexpr", "inline", "static"]:
|
|
prefixes.append(prefix)
|
|
else:
|
|
ret_type += prefix + " "
|
|
ret_type = ret_type.strip(" ")
|
|
args = ""
|
|
if g[3]:
|
|
for arg in g[3].split(","):
|
|
if "=" in arg:
|
|
args += arg[:arg.find("=")].strip(" ") + ", "
|
|
else:
|
|
args += arg.strip(" ") + ", "
|
|
args = args[:-2]
|
|
else: args = ""
|
|
if print_debug: print("function_declaration:", match.groups())
|
|
|
|
return True, Member(g[2], template_str2list(template_str), prefixes=prefixes, ret_type=ret_type, args=args, attributes=g[0], defined=defined, suffixes=g[4], namespace=namespace)
|
|
return False, Member("")
|
|
|
|
# constructor
|
|
def constructor_declaration(s: str, template_str:str = "", namespace: str="") -> tuple[bool, Member]:
|
|
# match = re.search(r"^((?:[\w<>:,&*]+ )+)(\w+)\(((?:[\w<>:,&*]+,? )*(?:[\w<>:,&*=]+))?\) *((const)?) *;", s) # declration
|
|
# groups are: name, args, suffixes
|
|
match = re.search(re_constructor, s) # declaration
|
|
if match:
|
|
g = match.groups()
|
|
args = ""
|
|
if g[1]:
|
|
for arg in g[1].split(","):
|
|
if "=" in arg:
|
|
args += arg[:arg.find("=")].strip(" ") + ", "
|
|
else:
|
|
args += arg.strip(" ") + ", "
|
|
args = args[:-2]
|
|
else: args = ""
|
|
if print_debug: print("constructor_declaration:", match.groups())
|
|
|
|
return True, Member(g[0], template_str2list(template_str), prefixes=[], ret_type="", args=args, defined=False, suffixes=g[2], namespace=namespace)
|
|
return False, Member("")
|
|
|
|
|
|
#
|
|
# INDENTATION
|
|
#
|
|
def get_indentation(s_indentation: str) -> int:
|
|
"""
|
|
returns number of spaces
|
|
"""
|
|
return s_indentation.replace("\t", " ").count(" ")
|
|
|
|
def get_string_and_indent(s: str) -> tuple[str, int]:
|
|
"""
|
|
returns the string without indentation and the number of spaces in the indentation
|
|
"""
|
|
match = re.search("^([ \t]*)", s)
|
|
if match:
|
|
return s[len(match.groups()[0]):], get_indentation(match.groups()[0])
|
|
return s, 0
|
|
|
|
|
|
# SCOPE END
|
|
def scope_end(s: str) -> bool:
|
|
if re.search(r"^}", s):
|
|
return True
|
|
return False
|
|
|
|
def parse_header(header_file:str):
|
|
with open(header_file, "r") as file:
|
|
header = file.readlines()
|
|
|
|
declarations: list[Class | Member] = []
|
|
|
|
template_str = ""
|
|
scopes : list[tuple[str, str, int, int]] = [] # type (eg class, namespace...), name, indentation, line
|
|
in_class = ""
|
|
namespace = ""
|
|
accessibility = ACC_PUBLIC
|
|
|
|
for i in range(len(header)):
|
|
line, indent = get_string_and_indent(header[i])
|
|
if starts_with_normal_comment(line): continue
|
|
if starts_with_doxygen_comment(line):
|
|
pass
|
|
if starts_with_comment_end(line):
|
|
pass
|
|
|
|
if template_declaration(line):
|
|
template_str = line.strip("\n")
|
|
|
|
# check begin namespace
|
|
match, name = namespace_open(line)
|
|
if match:
|
|
scopes.append(("namespace", name, indent, i))
|
|
namespace += "::" + name
|
|
namespace = namespace.strip("::")
|
|
|
|
# check accessibility
|
|
if in_class:
|
|
for i in range(len(acc_to_str)):
|
|
if acc_to_str[i] in line:
|
|
accessibility = i
|
|
else:
|
|
accessibility = ACC_PUBLIC
|
|
|
|
# check class begin
|
|
match, name = class_declaration(line)
|
|
if match:
|
|
# print("class in", i, "-", name)
|
|
scopes.append(("class", name, indent, i))
|
|
declarations.append(Class(name, c_temp=template_str2list(template_str)))
|
|
in_class = name
|
|
# declarations[-1].members.append(member_from_str(line, template_str))
|
|
template_str = ""
|
|
# check constructor
|
|
match, member = constructor_declaration(line, template_str, namespace)
|
|
if match:
|
|
# print("function in", i, "-", member.get_source_name([], ""))
|
|
member.accessibility = accessibility
|
|
if in_class:
|
|
declarations[-1].member_functions.append(member)
|
|
else:
|
|
declarations.append(member)
|
|
template_str = ""
|
|
# check function dec
|
|
match, member = function_declaration(line, template_str, namespace)
|
|
if match:
|
|
# print("function in", i, "-", member.get_source_name([], ""))
|
|
member.accessibility = accessibility
|
|
if in_class:
|
|
declarations[-1].member_functions.append(member)
|
|
else:
|
|
declarations.append(member)
|
|
template_str = ""
|
|
# check variable dec
|
|
match, member = variable_declaration(line, template_str, namespace)
|
|
if match:
|
|
# print("variable in", i, "-", member.get_source_name([], ""))
|
|
member.accessibility = accessibility
|
|
if in_class:
|
|
declarations[-1].member_variables.append(member)
|
|
else:
|
|
declarations.append(member)
|
|
template_str = ""
|
|
|
|
# check scope end
|
|
if scope_end(line):
|
|
if len(scopes) > 0 and scopes[-1][2] == indent:
|
|
if (scopes[-1][0] == "namespace"):
|
|
for i in range(len(namespace)):
|
|
if namespace[-i-1] == ":":
|
|
namespace = namespace[:-i-2]
|
|
break;
|
|
scopes.pop()
|
|
else:
|
|
# print("WARNING: Found scope end but no scope was opened at this indentaiton. line:", i)
|
|
pass
|
|
return declarations
|
|
|
|
|
|
def get_definition(declarations: list[Class | Member], target: (str | Member)):
|
|
for dec in declarations:
|
|
if type(dec) == Class:
|
|
for mem in dec.member_functions + dec.member_variables:
|
|
# print("mem", mem)
|
|
if (type(target) == str and target in mem.name) or (type(target) == Member and target == mem):
|
|
return mem.source(dec.c_temp, dec.name, len(mem.namespace.split("::")))
|
|
elif type(dec) == Member:
|
|
if (type(target) == str and target in dec.name) or (type(target) == Member and target == dec):
|
|
return dec.source([], "", len(dec.namespace.split("::")))
|
|
|
|
return ""
|
|
|
|
def apply_on_target_member(f: Callable[[Member, int], str], declarations: list[Class | Member], target_name: (str | Member)):
|
|
for dec in declarations:
|
|
if type(dec) == Class:
|
|
for mem in dec.member_variables + dec.member_functions:
|
|
# print("mem", mem)
|
|
if (type(target_name) == str and target_name in mem.name) or (type(target_name) == Member and target_name == mem):
|
|
return f(mem, len(mem.namespace.split("::"))+2)
|
|
|
|
return ""
|
|
|
|
def apply_on_every_class_member(f: Callable[[Member], str], declarations: list[Class | Member], class_name: str):
|
|
s = ""
|
|
for dec in declarations:
|
|
if type(dec) == Class and dec.name == class_name:
|
|
for mem in dec.member_functions + dec.member_variables:
|
|
s += f(mem)
|
|
# print("mem", mem)
|
|
|
|
return s
|
|
|
|
def apply_on_class(f: Callable[[Class], str], declarations: list[Class | Member], class_name: str):
|
|
for dec in declarations:
|
|
if type(dec) == Class and dec.name == class_name:
|
|
return f(dec)
|
|
return ""
|
|
|
|
def apply_on_all_classes(f: Callable[[Class], str], declarations: list[Class | Member]):
|
|
s = ""
|
|
for dec in declarations:
|
|
if type(dec) == Class:
|
|
s += f(dec)
|
|
return s
|
|
|
|
def print_help():
|
|
print("""
|
|
Synposis: gen_definitions.py <Options>... --file <file> --name <name>
|
|
General:
|
|
-h --help help
|
|
--no-docs turn off docstring generation
|
|
--file <file>
|
|
Target:
|
|
--name <name>
|
|
--line <number>
|
|
Output:
|
|
--getter
|
|
--lv_setter
|
|
--rv_setter
|
|
--setters
|
|
--getter-and-setter
|
|
--def
|
|
--uml
|
|
""")
|
|
|
|
# def missing_arg(arg):
|
|
# print("Missing argument for", arg)
|
|
# exit(1)
|
|
|
|
if __name__ == "__main__":
|
|
docs = True
|
|
getter = False
|
|
rv_setter = False
|
|
lv_setter = False
|
|
definition = False
|
|
uml = False
|
|
file = ""
|
|
target_name = ""
|
|
target_line_nr = -1
|
|
i = 1
|
|
while i in range(1, len(argv)):
|
|
if argv[i] == "--help" or argv[i] == "-h":
|
|
print_help()
|
|
exit(0)
|
|
elif argv[i] == "--no-docs":
|
|
docs = False
|
|
elif argv[i-1] == "--file":
|
|
file = argv[i]
|
|
elif argv[i] == "--getter-and-setter":
|
|
getter = True
|
|
lv_setter = True
|
|
rv_setter = True
|
|
elif argv[i] == "--lv_setter":
|
|
lv_setter = True
|
|
elif argv[i] == "--rv_setter":
|
|
rv_setter = True
|
|
elif argv[i] == "--setters":
|
|
lv_setter = True
|
|
rv_setter = True
|
|
elif argv[i] == "--getter":
|
|
getter = True
|
|
elif argv[i] == "--def":
|
|
definition = True
|
|
interactive = True
|
|
elif argv[i] == "--uml":
|
|
uml = True
|
|
elif argv[i-1] == "--name":
|
|
target_name = argv[i]
|
|
elif argv[i-1] == "--line":
|
|
target_line_nr = int(argv[i])
|
|
i += 1
|
|
|
|
ret = ""
|
|
declarations = []
|
|
if file:
|
|
if path.isfile(file):
|
|
declarations = parse_header(file)
|
|
if print_debug: print("found declartions:", declarations)
|
|
else:
|
|
print("Not a file:", file)
|
|
exit(1)
|
|
if target_line_nr > 0:
|
|
with open(file) as file_:
|
|
line, indent = get_string_and_indent(file_.readlines()[target_line_nr-1])
|
|
if print_debug: print("line", target_line_nr, line)
|
|
match, member = constructor_declaration(line, "", "")
|
|
if match:
|
|
target_name = member
|
|
match, member = function_declaration(line, "", "")
|
|
if match:
|
|
target_name = member
|
|
match, member = variable_declaration(line, "", "")
|
|
if match:
|
|
target_name = member
|
|
if not target_name:
|
|
error(f"Could not find target in line {target_line_nr}")
|
|
|
|
s = ""
|
|
if print_debug: print("target:", target_name)
|
|
if getter:
|
|
s += apply_on_target_member(Member.getter, declarations, target_name)
|
|
if lv_setter:
|
|
s += apply_on_target_member(Member.lv_setter, declarations, target_name)
|
|
if rv_setter:
|
|
s += apply_on_target_member(Member.rv_setter, declarations, target_name)
|
|
if definition:
|
|
s = get_definition(declarations, target_name).strip("\n")
|
|
if uml:
|
|
if target_name:
|
|
s += apply_on_class(Class.uml, declarations, target_name)
|
|
else:
|
|
s += apply_on_all_classes(Class.uml, declarations)
|
|
# s += apply_on_every_class_member(Member.uml, declarations, target_name)
|
|
|
|
print(s, end='\n')
|