203 lines
5.9 KiB
Python
203 lines
5.9 KiB
Python
"""
|
|
C++ Language Utility
|
|
* some methods may not be designed to be universal.
|
|
"""
|
|
|
|
import re
|
|
|
|
import util.string as StrUtil
|
|
|
|
|
|
def is_header_file(path: str):
|
|
return path.endswith('.h') or path.endswith('.hpp')
|
|
|
|
|
|
def find_class_definition(line: str) -> str | None:
|
|
# KEYWORD A (no quotation mark)
|
|
# KEYWORD A :
|
|
# KEYWORD A {
|
|
# KEYWORD A { ... }; (in single line)
|
|
|
|
keyword_pos, keyword = StrUtil.find_mb(line, 'enum class ', 'class ', 'struct ', 'union ')
|
|
if keyword_pos == -1 or keyword == 'enum class ':
|
|
return None
|
|
|
|
keyword_size = len(keyword) - 1 # not class defs
|
|
|
|
left_brace_pos = line.find('{')
|
|
semicolon_pos = line.find(';')
|
|
if semicolon_pos != -1 and (left_brace_pos == -1 or semicolon_pos < left_brace_pos):
|
|
return None # is forward decl
|
|
|
|
end_pos = len(line)
|
|
colon_pos = line.find(':')
|
|
l_angle_bracket_pos = line.find('<')
|
|
if left_brace_pos != -1:
|
|
end_pos = min(end_pos, left_brace_pos)
|
|
if colon_pos != -1:
|
|
end_pos = min(end_pos, colon_pos)
|
|
if l_angle_bracket_pos != -1 and l_angle_bracket_pos < colon_pos:
|
|
return None # template specialization (is not supported)
|
|
|
|
result = line[keyword_pos + keyword_size : end_pos].strip()
|
|
if '>' in result:
|
|
return None
|
|
|
|
return result
|
|
|
|
|
|
class ForwardDeclaration:
|
|
namespace_decl = str()
|
|
class_decl = str()
|
|
|
|
def __init__(self, namespace_decl: str, class_decl: str):
|
|
self.namespace_decl = namespace_decl
|
|
self.class_decl = class_decl
|
|
|
|
|
|
def find_class_forward_declaration(line: str) -> ForwardDeclaration | None:
|
|
# class A;
|
|
# namespace B { class A; }
|
|
|
|
namespace_decl = ''
|
|
class_decl = ''
|
|
|
|
namespace_pos = line.find('namespace')
|
|
left_brace_pos = line.find('{')
|
|
if namespace_pos != -1 and left_brace_pos != -1:
|
|
namespace_decl = line[namespace_pos + len('namespace') : left_brace_pos].strip()
|
|
|
|
keyword_pos, keyword = StrUtil.find_mb(line, 'class ', 'struct ', 'union ')
|
|
if keyword_pos == -1:
|
|
return None
|
|
|
|
keyword_size = len(keyword) - 1 # not class defs
|
|
|
|
semicolon_pos = line.find(';')
|
|
if semicolon_pos == -1:
|
|
return None
|
|
|
|
class_decl = line[keyword_pos + keyword_size : semicolon_pos].strip()
|
|
return ForwardDeclaration(namespace_decl, class_decl)
|
|
|
|
|
|
def find_namespace_declaration(line: str) -> str | None:
|
|
namespace_pos = line.find('namespace')
|
|
left_brace_pos = line.find('{')
|
|
if namespace_pos == -1 or left_brace_pos == -1:
|
|
return None
|
|
|
|
return line[namespace_pos + len('namespace') : left_brace_pos].strip()
|
|
|
|
|
|
def find_template_name(full: str, what: str, disable_regex_word_bound: bool = False):
|
|
def _impl(endpos: int):
|
|
while True:
|
|
r_angle_bracket_pos = full.rfind('>', 0, endpos)
|
|
l_angle_bracket_pos = full.rfind('<', 0, endpos)
|
|
if l_angle_bracket_pos == -1:
|
|
return None
|
|
if r_angle_bracket_pos > l_angle_bracket_pos:
|
|
endpos = l_angle_bracket_pos
|
|
continue
|
|
ret = full[:l_angle_bracket_pos]
|
|
matched_non_name = list(re.finditer(r'[^a-zA-Z_]', ret))
|
|
if len(matched_non_name) > 0:
|
|
ret = ret[matched_non_name[-1].start() + 1 :]
|
|
assert len(ret) > 0
|
|
return ret
|
|
|
|
if not disable_regex_word_bound:
|
|
for matched in re.finditer(rf'\b{re.escape(what)}\b', full):
|
|
endpos = matched.start()
|
|
result = _impl(endpos)
|
|
if result:
|
|
return result
|
|
else:
|
|
endpos = full.find(what)
|
|
if endpos == -1:
|
|
return None
|
|
return _impl(endpos)
|
|
|
|
|
|
def is_full_type_required(namespace_decl: str, class_decl: str, type_decl: str):
|
|
"""
|
|
Determine whether `class_decl` requires a full type for `type_decl`.
|
|
|
|
TODO: namespace_decl (currently UNUSED).
|
|
"""
|
|
|
|
# Y: T
|
|
# Y: std::optional<T>
|
|
# Y: std::variant<T>
|
|
# Y: std::array<T, _>
|
|
# Y: std::pair<T, T>
|
|
# Y: std::unordered_set<T>
|
|
# Y: std::unordered_map<T, _>
|
|
# Y: std::deque<T> // under msstl only
|
|
# Y: std::queue<T> // under msstl only
|
|
# N: T&
|
|
# N: T*
|
|
# N: std::map<T, T>
|
|
# N: std::shared_ptr<T>
|
|
# N: std::unique_ptr<T>
|
|
# N: std::weak_ptr<T>
|
|
# N: std::vector<T>
|
|
# N: std::set<T>
|
|
# N: std::unordered_map<_, T>
|
|
# N: std::function<T(T)>
|
|
|
|
def is_subtk_ends_with(full: str, tk: str, whats: list):
|
|
founded = False
|
|
for matched in re.finditer(rf'\b{re.escape(tk)}\b', full):
|
|
founded = True
|
|
if len(full) > matched.end():
|
|
for what in whats:
|
|
if full[matched.end() : matched.end() + len(what)] == what:
|
|
return founded, True
|
|
return founded, False
|
|
|
|
# is reference or pointer type?
|
|
founded, is_endswith = is_subtk_ends_with(
|
|
type_decl, class_decl, ['&', '*', ' const&', ' const*']
|
|
)
|
|
|
|
if not founded or is_endswith:
|
|
return False
|
|
|
|
# is template params?
|
|
template_name = find_template_name(type_decl, class_decl)
|
|
if not template_name:
|
|
return True # moreover, is not a template parameter
|
|
|
|
if template_name in [ # forward declarations are allowed.
|
|
'map',
|
|
'shared_ptr',
|
|
'unique_ptr',
|
|
'weak_ptr',
|
|
# 'vector',
|
|
'queue',
|
|
'set',
|
|
'function',
|
|
]:
|
|
return False
|
|
elif template_name in [ # full type is required.
|
|
'optional',
|
|
'variant',
|
|
'array',
|
|
'pair',
|
|
'unordered_set',
|
|
'deque',
|
|
'queue',
|
|
]:
|
|
return True
|
|
else:
|
|
return True # by default, we assume that a full type is required.
|
|
|
|
|
|
def is_full_type_required_for_typeset(namespace_decl: str, class_decl: str, typeset: list):
|
|
for type_decl in typeset:
|
|
if is_full_type_required(namespace_decl, class_decl, type_decl):
|
|
return True
|
|
return False
|