Initial commit
This commit is contained in:
parent
ce030c0827
commit
464e434626
744
gen_definition.py
Executable file
744
gen_definition.py
Executable file
@ -0,0 +1,744 @@
|
||||
#!/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')
|
Loading…
Reference in New Issue
Block a user