JD2022-TU1/main/extern/UbiServices/custom/doc/cs_comments.py

204 lines
8.3 KiB
Python

import os
import shutil
import re
import stat
from tempfile import mkstemp
friend_regex = re.compile('friend', re.I)
template_regex = re.compile('^(\s*template)(.*)', re.I)
class_regex = re.compile('class', re.I)
struct_regex = re.compile('struct', re.I)
comment_regex = re.compile('^(\s*\/\/)(.*)')
right_side_comments_regex = re.compile('(\/\/.*)')
include_regex = re.compile('^((?!(^\#include\s*<)).)*$')
cpp_keywords_regex = re.compile('.*(std::unique_ptr<.*>|static_cast<.*>|const_cast<.*>|reinterpret_cast<.*>).*$')
angular_content_pattern = '([^<>]*)'
angular_content_regex = re.compile('(?<=<)' + angular_content_pattern + '(?=>)')
angular_pattern = '<' + angular_content_pattern + '>'
variable_pattern = '([a-zA-Z_][a-zA-Z0-9]+)'
variable_template_pattern = variable_pattern + '\s*' + angular_pattern
variable_template_regex = re.compile(variable_template_pattern)
ubiservices_root = os.path.join(os.getcwd(), '..')
source_path = os.path.join(ubiservices_root, 'client-sdk/public/ubiservices')
target_path = os.path.join(ubiservices_root, 'doc/temp_headers/ubiservices')
global_transformed_templates_list = []
def force_remove(action, file_path, exception):
remove_readonly(file_path)
def remove_readonly(file_path):
os.chmod(file_path, stat.S_IWRITE)
if (os.path.isdir(file_path)):
os.rmdir(file_path)
else:
os.remove(file_path)
def copy_headers():
if os.path.exists(target_path):
shutil.rmtree(target_path, onerror=force_remove)
shutil.copytree(source_path, target_path)
def make_name(*strings):
#remove the pointers/references from the types
strings = [re.sub('[\^\&\*]','',w) for w in strings]
result = '_'.join(map(str,strings))
result = re.sub('::', '_', result)
if(result[0] == '_'):
return ''
return result
def get_transformed_template(s):
name = ''
while re.search(variable_template_pattern, s):
name = compose_transformed_template(s)
s = replace_template(s)
return name
# this method handles patterns such as:
# input: Map<UserId, ProfileInfo>
# output: sdk_Map_UserId_ProfileInfo
def replace_template(s):
result = variable_template_regex.sub(compose_transformed_template(s), s, 1)
result = re.sub('[\^\&\*]', '', result)
return result
def compose_transformed_template(s):
components = get_transformed_template_components(s)
return make_name(components[0], components[1])
# this method is used to obtain the template components found in the input
# input: Map<String, int>
# output: (Map, String_int)
def get_transformed_template_components(s):
result = ''
match_string = variable_template_regex.search(s).group()
var1 = re.sub(' ', '', re.search('[^<]*', match_string).group())
tp = angular_content_regex.search(match_string).group()
tp = re.sub('\s', '', re.sub(',', '_', tp))
return var1, tp
def get_transformed_template_base_class(s):
base = ''
while re.search(variable_template_pattern, s):
base = get_transformed_template_components(s)[0]
s = replace_template(s)
return base
def detected_class_or_struct_declaration(line):
return class_regex.match(line) or struct_regex.match(line)
# this method is used to eliminate some edge-cases in our pattern detections
def could_be_template_specialization(line):
return not friend_regex.match(line) and \
not template_regex.match(line) and \
not cpp_keywords_regex.match(line) and \
re.compile('^((?!(^\/\/)).)*$').match(line) and \
include_regex.match(line)
def extract_class_documentation(content):
comments = ''
# used to check if the detected class is a template class
template_class_definition = ''
lines = content.split('\n')
for line in reversed(lines):
if comment_regex.match(line) :
comments = line + '\n' + comments
else:
if template_regex.match(line) and comments == '':
template_class_definition = line
else:
if re.compile('^((?!(^\/\/)).).*$').match(line):
break;
# if we have a template class then add it to the final result
# in order to keep the same structure as before
if(template_class_definition != ''):
return comments + template_class_definition + '\n'
else:
return comments
# this method is used to get the resulting string from the generated list
# it also registers every local detected template specialization in the global list
def local_list_to_string(list):
final_string = ''
if(len(list) > 0):
for item in list:
if(item not in global_transformed_templates_list):
global_transformed_templates_list.append(item)
final_string += item
return final_string + '\n'
def generate_class(name, base_name):
return '\n/// @note Specialization for ' + base_name + '\n/// @addindex ' + name +'\nclass ' + name + ': public ' + base_name + '<> {};\n'
def replace_template_in_line(line):
while re.search(variable_template_pattern, line):
line = replace_template(line)
return line
# this method is used to insert the generated C#-like classes above the class in which they have been first detected
def insert_generated_classes(content_before, content_after, generated_class_list):
classDocumentation = extract_class_documentation(content_before)
content_before = content_before.replace(classDocumentation, '');
return content_before + local_list_to_string(generated_class_list) + classDocumentation + content_after;
def get_right_side_comments(line):
comments = ''
comments_res = (right_side_comments_regex.search(line))
if comments_res:
comments = comments_res.group()
return comments
def process_file(input_file):
# used to keep a list with the C#-like generated classes
local_transformed_templates_list = []
# the content variables are used to store content before and after reaching a class, a struct or a template class definition
# whenever we detect a template specialization we generate the C# equivalent and if it requires a class generation
# we use these two variables to add the generated classes in the right place
content_before = ''
content_after = ''
finalContent = ''
lines = input_file.read().split('\n')
for line in lines:
stripped_line = line.lstrip()
if(detected_class_or_struct_declaration(stripped_line)):
# if this is the first detection then switch the contents
if(content_before == ''):
content_before = content_after
content_after = ''
content_before = insert_generated_classes(content_before, content_after, local_transformed_templates_list)
content_after = ''
# clear the local list
local_transformed_templates_list = []
else:
if could_be_template_specialization(stripped_line):
comments = get_right_side_comments(line)
try:
line = re.sub(comments, '', line)
except:
print("\nLast line read was:\n" + line + "\n")
raise
transformed_template_name = get_transformed_template(line)
if ((not transformed_template_name in local_transformed_templates_list) and (transformed_template_name != '')):
transformed_template_base_class = get_transformed_template_base_class(line)
generated_class = generate_class(transformed_template_name, transformed_template_base_class)
local_transformed_templates_list.append(generated_class)
line = replace_template_in_line(line) + comments
content_after = content_after + line + '\n'
return insert_generated_classes(content_before, content_after, local_transformed_templates_list)
def main():
copy_headers()
for root, dirs, files in os.walk(target_path, topdown=False):
for file in files:
input_path = os.path.join(root, file)
file_handle, absolute_path = mkstemp()
with open(input_path, 'r') as input_file, os.fdopen(file_handle, 'w') as output_file:
output_file.write(process_file(input_file))
remove_readonly(input_path)
shutil.move(absolute_path, input_path)
if __name__ == '__main__':
main()