From 2d439de8212cad4c0abd56a53b62bbcc934673c3 Mon Sep 17 00:00:00 2001 From: Timo Lotterbach Date: Wed, 27 Mar 2013 12:46:33 +0100 Subject: [PATCH] Check-style scripts: fixes -check license can now handle different license -solved a lot of limitations for check indentation and single definitions on line -solved some small bugs (did not introduce any new positives or negatives) Signed-off-by: Timo Lotterbach --- scripts/check_all_styles.py | 9 +- scripts/check_class_name.py | 15 +- scripts/check_consistent_code_style.py | 98 +++++++--- scripts/check_curly_braces_alone_on_line.py | 85 ++++---- scripts/check_empty_lines.py | 15 +- scripts/check_header_guards.py | 49 +++-- scripts/check_indentation.py | 287 +++++++++++++++++++++------- scripts/check_license.py | 99 ++++++---- scripts/check_long_lines.py | 16 +- scripts/check_single_definition_on_line.py | 47 ++++- scripts/check_single_statement_on_line.py | 16 +- scripts/check_tabbing_and_spacing.py | 25 ++- scripts/common_modules/common.py | 50 ++--- scripts/common_modules/config.py | 54 +++++- 14 files changed, 610 insertions(+), 255 deletions(-) diff --git a/scripts/check_all_styles.py b/scripts/check_all_styles.py index c8cb972..0806265 100755 --- a/scripts/check_all_styles.py +++ b/scripts/check_all_styles.py @@ -19,7 +19,8 @@ ########################################################################### """ - Runs all enabled style checking +Runs all enabled style checking + """ import sys, re, string @@ -57,7 +58,7 @@ CHECK_CLASS_NAME = "ON" if __name__ == "__main__": targets = sys.argv[1:] targets = get_all_files(targets) - + if len(targets) == 0: print """ \tTakes a list of files and directories as input and runs all enabled style-checking on all files. @@ -65,11 +66,11 @@ if __name__ == "__main__": \tTo enable/disable style-checking you can toggle the "ON"/"OFF" value of the configuration variables inside the file. """ exit(0) - + for t in targets: if t[-2:] == ".h" or t[-4:] == ".cpp" or t[-2:] == ".c": file_contents, clean_file_contents, file_lines, clean_file_lines = read_file(t) - + if CHECK_LICENSE == "ON": check_license_in_file(t, file_contents) if CHECK_HEADER_GUARDS == "ON" and t[-2:] == ".h": diff --git a/scripts/check_class_name.py b/scripts/check_class_name.py index ef08ca6..0acf25f 100755 --- a/scripts/check_class_name.py +++ b/scripts/check_class_name.py @@ -23,8 +23,13 @@ from common_modules.common import * def check_class_name(filename, clean_file_contents): - #a class is followed by classname, and possible an inherticance + """ + Check that the file contains (maximum) one class that has a name identical to file name + + """ + #a class keyword is followed by classname, (and possibly an inherticance) and a left curly brace class_def_re = re.compile(r'(?= ) is not included in the match class_name_re = re.compile(r'(?<=((?=', '<=', '&&', r'\|\|', r'\|', '<<', '>>', r'\+=', '-=', r'\*=', '/=', '%=', '&=', r'\|=', r'\^=', '<<=', '>>=', ':', r'\?'] + binary_ops = ['=', r'\+', '%', r'\^', '==', '!=', '>=', '<=', '&&', r'\|\|', r'\|', '<<', '>>', r'\+=', '-=', r'\*=', '/=', '%=', '&=', r'\|=', r'\^=', '<<=', '>>=', ':', r'\?', '/'] #operators that are not handled - #r'\*', '&', '>', '<', '/', '-' + #r'\*', '&', '>', '<', '-' #access binary operators must have no space between operator and operands binary_ops_re_text = '(' + "|".join(binary_ops) + ')' - + + #initially: clean file contents from confusing content: + #remove all preprocessor directives + preprocessor_re = re.compile(r'#\s*\w+(\s+)(((?!\\).)+)((\\\n(((?!\\).)+))*)') + clean_file_contents = clean_string_from_regex(clean_file_contents, preprocessor_re, '') + clean_file_lines = clean_file_contents.split('\n') + #re for NO space between operator and operands nospace_re = re.compile(r"\w{0}\w".format(binary_ops_re_text)) #re for ONE space between operator and operands @@ -38,69 +48,82 @@ def check_binary_operators(filename, file_contents, file_lines, clean_file_conte #unbalanced is a match for binary op (\w\s*{0}\s*\w) that is neither no space (?!(\w{0}\w)) nor single space (?!(\w\s{0}\s\w)) #this works because (?!...) does not consume strings unbalanced_spaces_re = re.compile(r"(?!(\w{0}\w))(?!(\w\s{0}\s\w))(\w\s*{0}\s*\w)".format(binary_ops_re_text)) - - #access_ops = ['.', '->', '::'] + access_ops_re_text = r'(\.|->|::)' bad_access_ops_re = re.compile(r"(\w\s+{0}\s*\w)|(\w\s*{0}\s+\w)".format(access_ops_re_text)) - + #check if there is general inconsistance in the file nospace_match = re.search(nospace_re, clean_file_contents) space_match = re.search(space_re, clean_file_contents) - + + #a single consistency in the file is enough to issue a warning if nospace_match != None and space_match != None: line1 = clean_file_contents[:nospace_match.start()].count("\n") + 1 line2 = clean_file_contents[:space_match.start()].count("\n") + 1 - + log_warning(filename, line1, "inconsistent spacing between operands and binary operator. All operands in a file must have same space (0 or 1 spaces) [Reported once per file]", "lines :{0} and {1}".format(line1, line2)) - + for i in range(len(file_lines)): line = clean_file_lines[i] - + if re.search(unbalanced_spaces_re, line) != None: log_warning(filename, i + 1, "incorrect or unbalanced spaces between operands and binary operator", file_lines[i].strip(" ")) - + if re.search(bad_access_ops_re, line) != None: log_warning(filename, i + 1, "unneeded space(s) around access/resolution operator", file_lines[i].strip(" ")) def check_semicolor(filename, file_contents, file_lines, clean_file_contents, clean_file_lines): + """ + Checks that there are no spaces before semicolons, and that there is a space after it in case there is other content + on the same line after it + + """ #check if there is extra space before semicolon, or missing space after semicolon (if there is content after it like in for loop) space_before_re = re.compile(r"\s;"); nospace_after_re = re.compile(r";((?! ).)"); for i in range(len(file_lines)): line = clean_file_lines[i] - + if re.search(space_before_re, line) != None: log_warning(filename, i + 1, "unneeded space(s) before semicolon", file_lines[i].strip(" ")) - + if re.search(nospace_after_re, line) != None: log_warning(filename, i + 1, "missing space after semicolon", file_lines[i].strip(" ")) def check_braces_spaces(filename, file_contents, file_lines, clean_file_contents, clean_file_lines): + """ + Check that there are no unnecessary spaces around parentheses + + """ #check if there are spaces between between right/left brace and contents bad_left_brace_re = re.compile(r"\(\s") bad_right_brace_re = re.compile(r"\s\)") - + for i in range(len(file_lines)): line = clean_file_lines[i] - + if re.search(bad_left_brace_re, line) != None: log_warning(filename, i + 1, "unneeded space(s) after left brace", file_lines[i].strip(" ")) - + if re.search(bad_right_brace_re, line) != None: log_warning(filename, i + 1, "unneeded space(s) before right brace", file_lines[i].strip(" ")) def check_commas(filename, file_contents, file_lines, clean_file_contents, clean_file_lines): + """ + Check that there is no space before commas, and there is exactly one space after coma + + """ nospace_after_re = re.compile(r",((?!\s).)") too_many_spaces_after_re = re.compile(r",\s(\s+)") space_before_re = re.compile(r"\S(\s+),") - + for i in range(len(file_lines)): line = clean_file_lines[i] - + if re.search(nospace_after_re, line) != None: log_warning(filename, i + 1, "missing space after comma", file_lines[i].strip(" ")) - + if re.search(too_many_spaces_after_re, line) != None: log_warning(filename, i + 1, "unneeded space(s) after comma", file_lines[i].strip(" ")) @@ -108,6 +131,10 @@ def check_commas(filename, file_contents, file_lines, clean_file_contents, clean log_warning(filename, i + 1, "unneeded space(s) before comma", file_lines[i].strip(" ")) def check_function_call_or_definition(filename, file_contents, file_lines, clean_file_contents, clean_file_lines): + """ + Check that there is no space between function call/definition and left parenthese + + """ #in bad function definition or call there exists a space (or more) between function name and left brace (the last \s+ in the expression) #the function name must not be a if, for, switch or while reserved_words = ['if', 'for', 'while', 'switch', 'return', 'delete'] @@ -118,7 +145,7 @@ def check_function_call_or_definition(filename, file_contents, file_lines, clean #the \s+ is the fauled pattern that should be caught, because it means a space exists between function name and left parenthese bad_func_re_text = r"(\w+){0}(\s+)(\()".format(reserved_words_re_text) bad_func_re = re.compile(bad_func_re_text) - + #function pointer return_type_re_text = r'\w+\s*(&|\*)*\s*' func_ptr_re_text = r'\s*{0}\((\w|\*|:|\s)+\)(\s*)\('.format(return_type_re_text) @@ -126,41 +153,50 @@ def check_function_call_or_definition(filename, file_contents, file_lines, clean func_ptr_re = re.compile(func_ptr_re_text) macro_re = re.compile(r"(\s*)#define") - + for i in range(len(file_lines)): line = clean_file_lines[i] + #check all occurances in a line (a line can contain several function calls) for bad_func_match in bad_func_re.finditer(line): #print line[bad_func_match.start():], re.match(func_ptr_re, line[bad_func_match.start():]) != None if re.search(func_ptr_re, line) == None and re.match(macro_re, line) == None: log_warning(filename, i + 1, "unneeded space(s) between function name and left brace", file_lines[i].strip(" ")) -def check_space_before_braces(filename, file_contents, file_lines, clean_file_contents, clean_file_lines): +def check_keyword_brace_space(filename, file_contents, file_lines, clean_file_contents, clean_file_lines): + """ + Check that all occurances of (if, for,...etc) in file have a space between keyword and left brace or + that all occurances do not have a space between keyword and left brace + + """ + #the keywords to be checked reserved_words = ['if', 'for', 'while', 'switch', 'return'] + #the \W makes sure the keyword is actually a keyword and not a part of a function name, e.g, if() would match but foo_if() would be filtered out reserved_words_re_text = r'\W(' + "|".join(reserved_words) + ')' - + nospace_re = re.compile(r"{0}\(".format(reserved_words_re_text)) space_re = re.compile(r"{0}\s\(".format(reserved_words_re_text)) too_many_space_re = re.compile(r"{0}\s(\s+)\(".format(reserved_words_re_text)) - + + #check if there is a general inconsistency (a signle occurance is in the file is enough to give a warning) nospace_match = re.search(nospace_re, clean_file_contents) space_match = re.search(space_re, clean_file_contents) if nospace_match != None and space_match != None: line1 = clean_file_contents[:nospace_match.start()].count("\n") + 1 line2 = clean_file_contents[:space_match.start()].count("\n") + 1 - + log_warning(filename, line1, "inconsistent spacing between keyword and left brace. All braces in a file must have same space (0 or 1 spaces) [Reported once per file]", "lines :{0} and {1}".format(line1, line2)) - + for i in range(len(file_lines)): line = clean_file_lines[i] - + if re.search(too_many_space_re, line) != None: log_warning(filename, i + 1, "unneeded space(s) between keyword and left brace", file_lines[i].strip(" ")) def check_consistent_code_style(filename, file_contents, clean_file_contents, file_lines, clean_file_lines): check_function_call_or_definition(filename, file_contents, file_lines, clean_file_contents, clean_file_lines) check_semicolor(filename, file_contents, file_lines, clean_file_contents, clean_file_lines) - check_space_before_braces(filename, file_contents, file_lines, clean_file_contents, clean_file_lines) + check_keyword_brace_space(filename, file_contents, file_lines, clean_file_contents, clean_file_lines) check_braces_spaces(filename, file_contents, file_lines, clean_file_contents, clean_file_lines) check_commas(filename, file_contents, file_lines, clean_file_contents, clean_file_lines) check_binary_operators(filename, file_contents, file_lines, clean_file_contents, clean_file_lines) @@ -169,16 +205,16 @@ def check_consistent_code_style(filename, file_contents, clean_file_contents, fi if __name__ == "__main__": targets = sys.argv[1:] targets = get_all_files(targets) - + if len(targets) == 0: print """ -\t**** No input provided *** +\t**** No input provided **** \tTakes a list of files/directories as input and performs specific style checking on all files/directories \tGives warnings if there is inconsistency in spacing of commas, semicolons, operands, braces, function calls or definitions. """ exit(0) - + for t in targets: if t[-2:] == ".h" or t[-4:] == ".cpp" or t[-2] == ".c": file_contents, clean_file_contents, file_lines, clean_file_lines = read_file(t) diff --git a/scripts/check_curly_braces_alone_on_line.py b/scripts/check_curly_braces_alone_on_line.py index 3e57ccc..aedbb11 100755 --- a/scripts/check_curly_braces_alone_on_line.py +++ b/scripts/check_curly_braces_alone_on_line.py @@ -22,92 +22,105 @@ import sys, re, string from common_modules.common import * def remove_tollerable_braces(clean_file_contents): - #remove array assignments - assignment_re = re.compile(r'=(\s*)(\{)((?!;).)*(\})(\s*);', re.DOTALL) - - no_assig_contents = clean_file_contents - found_match = re.search(assignment_re, no_assig_contents) - - while found_match != None: - matched_string = no_assig_contents[found_match.start(): found_match.end()] - no_assig_contents = no_assig_contents[:found_match.start()] + "\n" * matched_string.count("\n") + no_assig_contents[found_match.end():] + """ + Cleans file contents from code blocks that should not be checked: + 1- Array initialization blocks + 2- Empty functions - found_match = re.search(assignment_re, no_assig_contents) + """ + #array assignment begins with a left curly brace ({) followed by any number of characters that is not a semicolon + #it ends by a right curly brace (}) and a semicolon (;) + assignment_re = re.compile(r'=(\s*)(\{)((?!;).)*(\})(\s*);', re.DOTALL) + #remove array assignments from file contents + no_assig_contents = clean_string_from_regex(clean_file_contents, assignment_re, '') #remove braces that are used for implementing empty functions empty_func_re = re.compile(r'\{\s*\}') - - no_empty_func_contents = no_assig_contents - found_match = re.search(empty_func_re, no_empty_func_contents) - - while found_match != None: - matched_string = no_empty_func_contents[found_match.start(): found_match.end()] - no_empty_func_contents = no_empty_func_contents[:found_match.start()] + "\n" * matched_string.count("\n") + no_empty_func_contents[found_match.end():] - - found_match = re.search(empty_func_re, no_empty_func_contents) + no_empty_func_contents = clean_string_from_regex(no_assig_contents, empty_func_re, '') return no_empty_func_contents def check_curly_braces_alone_on_line(filename, file_contents, clean_file_contents, file_lines, clean_file_lines): + """ + Checks that there is no unnecessary code on same line with curly braces. + + """ clean_file_contents = remove_tollerable_braces(clean_file_contents) clean_file_lines = clean_file_contents.split('\n') - + + #keep a stack booleans to indicate where type declarations are made typedecl_stack = [] - #a type declaration contains the words struct, class , union or enum + + #a type declaration contains the words struct, class, union or enum + #it can also have the typdef keyword in the beginning typedecl_re = re.compile(r"(?!<\w)(typedef)?(\s*)(struct|class|enum|union)(?!\w)") + #some statements are allowed to have curly brace on same + #(do..while) blocks, namespaces and extern blocks permissible_re = re.compile(r'''(\s*) ( - do| # do keyword - (namespace((?!\{).)*)| # namespace keyword - (extern((?!\{).)*) # extern keyword + do| # do keyword + (namespace((?!\{).)*)| # namespace keyword + (extern((?!\{).)*) # extern keyword )? - (\s*)\{(\s*) - (\\?)(\s*) + (\s*)\{(\s*) # open curly brace + (\\?)(\s*) # backslash indicates a multiline macro or multiline string $''', re.VERBOSE) - for i in range(len(file_lines)): line = clean_file_lines[i] - + def get_former_line(): + """ + Gets the last non-empty line + + """ if i > 0: for j in range(i - 1, 0 , -1): if len(clean_file_lines[j].strip(" \t\n\f\r")): return clean_file_lines[j] - return None - + return "" + former_line = get_former_line() - + if line.count("{"): + #check if the line contains a type declaration typedecl = re.search(typedecl_re, former_line) typedecl_stack.append(typedecl) - + + #make a warning if line does not follow the permissible set of expressions if re.match(permissible_re, line) == None : log_warning(filename, i + 1, "other code on same line with curly brace", file_lines[i].strip(" ")) - + if line.count("}"): typedecl = typedecl_stack.pop() + if typedecl: + #if the right curly brace marks the end of type declaration: + #it is possible to have code AFTER the curly brace in case of type declarations + #like instantiating a structure at end of structure definition if re.match(r"(\s*)\}", line) == None: log_warning(filename, i + 1, "other code on same line with curly brace", file_lines[i].strip(" ")) else: + #if the right curly brace does not mark the end of type declaration: + #the line must begin with the right curly brace, can possibly have a while expression and ends by semicolon + #this has to be the end of line ($) to avoid having a second statement on the same line if re.match(r"(\s*)\}(\s*)((while((?!;).)*)?)(;?)(\s*)(\\?)(\s*)$", line) == None: log_warning(filename, i + 1, "other code on same line with curly brace", file_lines[i].strip(" ")) if __name__ == "__main__": targets = sys.argv[1:] targets = get_all_files(targets) - + if len(targets) == 0: print """ -\t**** No input provided *** +\t**** No input provided **** \tTakes a list of files/directories as input and performs specific style checking on all files/directories \tGives warnings if unnecessary code exists on the same line with curly braces. """ exit(0) - + for t in targets: if t[-2:] == ".h" or t[-4:] == ".cpp" or t[-2] == ".c": file_contents, clean_file_contents, file_lines, clean_file_lines = read_file(t) diff --git a/scripts/check_empty_lines.py b/scripts/check_empty_lines.py index e5f51f2..7bff68d 100755 --- a/scripts/check_empty_lines.py +++ b/scripts/check_empty_lines.py @@ -20,10 +20,13 @@ import sys, re, string from common_modules.common import * -from common_modules.config import G_MAX_INDENTATION_THRESHOLD def check_empty_lines(filename, file_contents, file_lines): + """ + Check that there are no non-necessary empty lines + + """ #keep track of the current indentation #need only to know if level of indent is zero or not line_indent_zero = True @@ -38,6 +41,11 @@ def check_empty_lines(filename, file_contents, file_lines): next_line = file_lines[i + 1] if i < len(file_lines) - 1 else "" def is_next_indent_zero(): + """ + Checks the indentation of the next non-empty line + return True if it is at zero-level indentation + + """ for j in range(i + 1, len(file_lines)): if not is_line_empty(file_lines[j]): return re.match(r'(\s+)(\S+)', file_lines[j]) == None @@ -57,12 +65,13 @@ def check_empty_lines(filename, file_contents, file_lines): #if the line after it is a block end elif re.match(r'(\s*)\}', next_line): log_warning(filename, i + 1, "unneeded empty line (block ends with empty line)") - #if non-empty line else: + #if non-empty line: check if line is at zero indentation #if the line contains one or more white-space characters followed by #one or more non white-space characters line_indent_zero = re.match(r'(\s+)(\S+)', line) == None + #if file does not end with an empty line: give warning if re.search(r'\n$', file_contents) == None: log_warning(filename, len(file_lines), "file does not end with new line") @@ -73,7 +82,7 @@ if __name__ == "__main__": if len(targets) == 0: print """ -\t**** No input provided *** +\t**** No input provided **** \tTakes a list of files/directories as input and performs specific style checking on all files/directories \tGives warnings if there are unneeded empty lines diff --git a/scripts/check_header_guards.py b/scripts/check_header_guards.py index 0dbb7d5..bc8e857 100755 --- a/scripts/check_header_guards.py +++ b/scripts/check_header_guards.py @@ -22,6 +22,13 @@ import sys, re, string from common_modules.common import * def check_header_guards(filename, file_contents): + """ + Check if a header files contains valid header guards: + 1- #ifndef GUARDNAME directly after license that has a guard name identical to file name in uppercase letters, underscores at beginnings and end are tollerable + 2- directly followed by #define that matches the guard name of #ifndef exactly + 3- the file has to end with an #endif + + """ #remove strings string_re = re.compile(r'"((?!((? 0: good_name = good_name[good_name.rfind("/") + 1:] - + #remove .h extension good_name = good_name[:good_name.find(".h")] - + #good name is all uppercase good_name = good_name.upper() - + #a good name can start with underscores, and can end with underscores, can optionally have a capital H character good_name_re = re.compile("(_*){0}(_+H?_*)?".format(good_name)) - + return re.match(good_name_re, name) != None - + #find the ifndef guard ifndef_re = re.compile(r"(\s*)#ifndef .*", re.MULTILINE) ifndef_match = re.match(ifndef_re, file_contents) if ifndef_match == None: log_warning(filename, 1, "header file does not contain a valid #ifndef guard at the beginning") return None - + + #get guard name ifndef_match_text = file_contents[:ifndef_match.end()] ifndef_match_text = ifndef_match_text.strip(" \n\r\f\t") splitted_match_text = ifndef_match_text.split(" ") ifndef_guard_name = splitted_match_text[1] if len(splitted_match_text) > 1 else "" - + if not check_good_name(ifndef_guard_name): log_warning(filename, 1, "#ifndef {0} guard name does not follow convention, name has to contain file name in uppercase".format(ifndef_guard_name)) return None - + file_contents = file_contents[ifndef_match.end():] - + #find the define guard define_re = re.compile(r"(\s*)#define .*") define_match = re.match(define_re, file_contents) if define_match == None: log_warning(filename, 1, "header file does not contain a #define guard at the beginning that matches #ifndef {0}".format(ifndef_guard_name)) return None - + + #get guard name define_match_text = file_contents[:define_match.end()] define_match_text = define_match_text.strip(" \n\r\f\t") splitted_match_text = define_match_text.split(" ") define_guard_name = splitted_match_text[1] if len(splitted_match_text) > 1 else "" - + + #check if #ifndef and #define have identical guard names if define_guard_name != ifndef_guard_name: log_warning(filename, 1, "header guard names do not match :(#ifndef {0}) and (#define {1})".format(ifndef_guard_name, define_guard_name)) - + #find the endif guard endif_re = re.compile(r"#endif.*(\s*)$") endif_match = re.search(endif_re, file_contents) @@ -96,21 +106,20 @@ def check_header_guards(filename, file_contents): log_warning(filename, 1, "header file does not end with #endif at last line") return None - if __name__ == "__main__": targets = sys.argv[1:] targets = get_all_files(targets) - + if len(targets) == 0: print """ -\t**** No input provided *** +\t**** No input provided **** \tTakes a list of files/directories as input and performs specific style checking on all files/directories \tGives warnings if header guards does not exist in header files. Header files must have a #ifndef and #define guards directly after the license. \tThe names of the header guards must match together and match with the name of the file in uppercase letters. """ exit(0) - + for t in targets: if t[-2:] == ".h": file_contents, _, _, _ = read_file(t) diff --git a/scripts/check_indentation.py b/scripts/check_indentation.py index 4ae6c83..3ec53ba 100755 --- a/scripts/check_indentation.py +++ b/scripts/check_indentation.py @@ -21,45 +21,170 @@ import sys, re, string from common_modules.common import * from common_modules.config import G_MAX_INDENTATION_THRESHOLD +from common_modules.config import G_TAB_SEQUENCE -def check_indentation(filename, file_contents, clean_file_contents, file_lines, clean_file_lines): +def get_correct_indentation_list(clean_file_lines): #It is important to keep track of the changes made to the indent level because #curly braces of namespaces do not affect indentation, while curly braces of #other statements like if, for and while increase/decease indentation indent_levels_stack = [0] + + #current indent level indent_level = 0 - parentheses_count = 0 - - def is_incomplete_statement(line, former_line): - incomplete_statement = False + + #indent level for every line + indentation_levels = [0] * len(clean_file_lines) + + #get correct indentation for every line + for i in range(len(clean_file_lines)): + line = clean_file_lines[i] + + def get_former_line(): + """ + Gets the last previous non-empty line + + """ + if i > 0: + for j in range(i - 1, 0 , -1): + if len(clean_file_lines[j].strip(" \t\n\f\r")): + return clean_file_lines[j] + return "" + + #previous non-empty line + former_line = get_former_line() + + # the next part of the code decreases the indent level + # + # the level of indent decrease when right curly braces are found + # in the line. + # + # if the line begins with a right curly brace then the line ITSELF + # gets a decreased indent level + # + # if the line has some content on the same line before the right + # curly brace (which is a bad practice, but nevertheless) the line + # ITSELF does not get a decreased level of indentation, but the + # following lines get a decreased level of indentation + + #check if the line begins with a right curly brace + line_begins_with_right_brace = re.match(r"\s*\}", line) != None + + #if the line DOES NOT begin with right curly brace then it has the same level + #of indentation as the previous line(s) + if not line_begins_with_right_brace: + indentation_levels[i] = indent_level + + #this is to decrease the level of indentation in case there is a right curly brace "}" + #the level of indentation is decreased by popping elements from the indentation level stack + #a loop is to account for cases where there are several braces on same line + for _ in range(0, line.count("}") - line.count("{")): + indent_level = indent_levels_stack.pop() + + #if the line begins with right curly brace then it gets a decreased + #level of indentation + if line_begins_with_right_brace: + indentation_levels[i] = indent_level + + # the next part of the code increases the indent level + # + # indent level increases when a left curly brace is found + # + # an exception to this if the curly brace is the beginning + # of a namespace or an extern block, in this case the indent + # level does not increase + + # A flag is used in the very specific case of having several left + # curly braces after a namespace/extern block (which is a bad practice) + # + # Initially the flag is False,soo that the first curly brace would not + # increase indentation, after that the flag is raised (set to True) + # so that every new left curly brace would incraese indentation + flag = False + + #increase the level of indentation of there are open left curly braces "{" + #a loop is to account for cases where there are several braces on same line + for _ in range(0, line.count("{") - line.count("}")): + #push the current level of indentation to the stack + indent_levels_stack.append(indent_level) + + #if there is no namesapce or extern declaration before curly brace: increase indentation + #that is because namespaces do not increase the level of indentation + namespace_or_extern_re_text = r'\s*(namespace|(extern((\s*\S*\s*)?)(\s*)($|\{)))' + namespace_or_extern_re = re.compile(namespace_or_extern_re_text) + + #if the current line contains a namespace/extern declation + if re.match(namespace_or_extern_re, line) != None: + #increase the level of indentation only if the flag is raised + #(if it is NOT the first curly brace on line) + if flag: + indent_level += 1 + else: + #raise the flag (so that next curly braces would increase indentation) + flag = True + + #otherwise: if the previous line contains namesapce/extern declaration + elif re.match(namespace_or_extern_re, former_line) != None: + #the indent level does not increase only if the previous line does not contain + #a left curly brace, and if this is the first curly brace on the current line (flag is still False) + #if either of the conditions is false it is safe to increase the indent level + if re.search(r'(namespace|extern)((?!\{).)*\{', former_line) == None and not flag: + flag = True + else: + indent_level += 1 + + #otherwise: just increase indent level + else: + indent_level += 1 + + return indentation_levels + +def check_indentation(filename, file_contents, clean_file_contents, file_lines, clean_file_lines): + """ + Checks if all lines in a file (except multi-line comments, strings and macros) have correct indentation + + """ + def is_statement_supplement(line, former_line, parentheses_count): + """ + Checks if the statement is the completion of a previous statement, like arethmatic expressions broken on several lines, or for loop + parts on separate lines. It also includes checks that the previous statement is well-closed (ends by semicolon or curly brace) + + IMPORTANT: the first line of a for loop (or an arethmatic expression...etc) broken on several lines would still not be considered + a statement supplement + + """ + statement_supplement = False #include statements not preceded by a semicolon in previous line - incomplete_statement |= former_line != None and re.search(re.compile(r";(\s*)$"), former_line) == None + statement_supplement |= former_line != None and re.search(re.compile(r";(\s*)$"), former_line) == None #exclude lines containing block beginnings and ends - incomplete_statement &= re.search(re.compile(r"(\{|\})(\s*)$"), line) == None + statement_supplement &= re.search(re.compile(r"(\{|\})(\s*)$"), line) == None #exclude statements preceded by block beginnings and ends - incomplete_statement &= former_line != None and re.search(re.compile(r"(\{|\})(\s*)$"), former_line) == None + statement_supplement &= former_line != None and re.search(re.compile(r"(\{|\})(\s*)$"), former_line) == None #exclude preprocessor directives (they dunt have to end with a ; for example) - incomplete_statement &= re.match(re.compile(r"(\s*)#"), line) == None + statement_supplement &= not is_preprocessor_directive(line) #exclude statements preceded by preprocessor directives that do not end with a slash \ - incomplete_statement &= former_line != None and re.match(re.compile(r"(\s*)#((?!\/).)*\/$"), former_line) == None - + statement_supplement &= former_line != None and re.match(re.compile(r"(\s*)#((?!\/).)*\/$"), former_line) == None #filter out the case where a line is preceeded by a label statement - fl_label_statement = False - if former_line != None: - #case statements -> case id : expr that does not begin with a : (to avoid matching with :: operator) - fl_label_statement = re.match(r'(\s*)case(\s*)((?!:).)*(\s*):(?!:)(\s*)$', former_line) != None - #label statements -> label : expr that does not begin with a : (to avoid matching with :: operator) - fl_label_statement |= re.match(r'(\s*)(\w+)(\s*):(?!:)(\s*)$', former_line) != None - - incomplete_statement &= not fl_label_statement + fl_label_statement = is_label_statement(former_line) + statement_supplement &= not fl_label_statement #include statements that are contained inside parentheses - incomplete_statement |= parentheses_count > 0 - - return incomplete_statement - + statement_supplement |= parentheses_count > 0 + + return statement_supplement + + def is_macro_body(former_line): + """ + Checks if the line belongs to a macro body by checking that the line before it ends with a slash + + """ + return former_line != None and re.search(r"\\(\s*)$", former_line) != None + def is_label_statement(line): + """ + Checks if line starts with a label/case definition + + """ label_statement = False if line != None: #case statements -> case id : expr that does not begin with a : (to avoid matching with :: operator) @@ -67,97 +192,111 @@ def check_indentation(filename, file_contents, clean_file_contents, file_lines, #label statements -> label : expr that does not begin with a : (to avoid matching with :: operator) label_statement |= re.match(r'(\s*)(\w+)(\s*):(?!:)', line) != None return label_statement - + def is_preprocessor_directive(line): + """ + Checks if a line is a preprocessor directive + + """ + #preproccessor directives start with a # return re.match(r"(\s*)#", line) != None - def is_macro_body(former_line): - return former_line != None and re.search(r"\\$", former_line) != None - + #keeps count of open (non-closed) left parentheses + parentheses_count = 0 + + #configured tab character + tab_character = G_TAB_SEQUENCE[0] + tab_character_count = G_TAB_SEQUENCE[1] + + #get list of correct levels of indentation for every line + correct_indent_levels = get_correct_indentation_list(clean_file_lines) + + #indent level of current line and previous line + correct_il = 0 + previous_il = 0 + + #check every line for correct indentation for i in range(len(clean_file_lines)): line = clean_file_lines[i] - line_indent = 0 - - #a loop is to account for cases where there are several braces on same line - for _ in range(0, line.count("}") - line.count("{")): - indent_level = indent_levels_stack.pop() - - #check correct line indentation - found_match = re.search("(?! )", line) - if found_match: - line_indent = found_match.start() / 4.0 - + #correct indent level of current line + correct_il = correct_indent_levels[i] + + #actual indent level of the line + actual_il = 0 + + #get the actual line indentation + #this regex searches for the first character in line that is not a space + #if no match found this means a zero level of indentation + indent_match = re.search("(?!{0})".format(tab_character), line) + if indent_match: + actual_il = 1.0 * indent_match.start() / tab_character_count + def get_former_line(): + """ + Gets the last previous non-empty line + + """ if i > 0: for j in range(i - 1, 0 , -1): if len(clean_file_lines[j].strip(" \t\n\f\r")): return clean_file_lines[j] return "" - + + #previous non-empty line former_line = get_former_line() - - incomplete_statement = is_incomplete_statement(line, former_line) + + #make several checks about the line + statement_supplement = is_statement_supplement(line, former_line, parentheses_count) label_statement = is_label_statement(line) fl_label_statement = is_label_statement(former_line) preprocessor_directive = is_preprocessor_directive(line) macro_body = is_macro_body(former_line) - + + #if at non-empty line: check if indentation is correct if len(line.strip(" \t\n\r")) > 0: if macro_body: #do not check anything, just ignore line None elif preprocessor_directive: - #warning if not at 0 level indentation - if line_indent != 0: + #preproccessor directives must have 0 level indentation + if actual_il != 0: log_warning(filename, i + 1, "incorrect indentation", file_lines[i].strip(" ")) elif label_statement: - #warning if label statement has one level less indentation than the normal indentation level - if line_indent != indent_level - 1: + #label statements must have one level less indentation than the normal indentation level + if actual_il != correct_il - 1: log_warning(filename, i + 1, "incorrect indentation", file_lines[i].strip(" ")) - elif (not incomplete_statement) and line_indent != indent_level: - #warning if a complete statement (NON incomplete statement) is not at the normal level of indentation + elif (not statement_supplement) and actual_il != correct_il: + #a complete statement (NON supplement statement) must be at the normal level of indentation + log_warning(filename, i + 1, "incorrect indentation", file_lines[i].strip(" ")) + elif statement_supplement and actual_il < correct_il: + #statement supplements must have a level of indentation at least equal to the normal level of indentation log_warning(filename, i + 1, "incorrect indentation", file_lines[i].strip(" ")) - elif incomplete_statement: - if fl_label_statement and line_indent != indent_level: - #warning if preceeded by a label statement but not exactly at normal indentation - log_warning(filename, i + 1, "incorrect indentation", file_lines[i].strip(" ")) - elif (not fl_label_statement ) and line_indent < indent_level: - #warning if incomplete statement and indentation is less than normal indentation - log_warning(filename, i + 1, "incorrect indentation", file_lines[i].strip(" ")) - - #a loop is to account for cases where there are several braces on same line - for _ in range(0, line.count("{") - line.count("}")): - indent_levels_stack.append(indent_level) - #if there is no namesapce or extern declaration before curly brace: increase indentation - #if re.match(r"(\s*)((namespace(\s+\w+)?(\s*)\{)|(extern(((?!\{).)*)\{))", line) == None and re.match(r"(\s*)(namespace|extern)", former_line) == None: - #if re.match(r"(\s*)(namespace)", line) == None and re.match(r"(\s*)namespace", former_line) == None: - namespace_or_extern_re_text = r'\s*(namespace|(extern((\s*\S*\s*)?)(\s*)($|\{)))' - namespace_or_extern_re = re.compile(namespace_or_extern_re_text) - if re.match(namespace_or_extern_re, line) == None and re.match(namespace_or_extern_re, former_line) == None: - indent_level += 1 - - #warning if indentation level is too much - if indent_level == G_MAX_INDENTATION_THRESHOLD + 1: - log_warning(filename, i + 1, "code block has too much indentation [maximum level of indentation is {0}]".format(G_MAX_INDENTATION_THRESHOLD),) - + #warning if indentation level is too much + if correct_il == G_MAX_INDENTATION_THRESHOLD + 1 and previous_il < correct_il: + log_warning(filename, i + 1, "code block has too much indentation [maximum level of indentation is {0}]".format(G_MAX_INDENTATION_THRESHOLD),) + + #update open parentheses count parentheses_count += line.count("(") - line.count(")") + #update previous indent level + previous_il = correct_il + if __name__ == "__main__": targets = sys.argv[1:] targets = get_all_files(targets) - + if len(targets) == 0: print """ -\t**** No input provided *** +\t**** No input provided **** \tTakes a list of files/directories as input and performs specific style checking on all files/directories \tGives warnings if lines (except multi-line comments and macros) do not have correct indentation. \tIt also checks if code blocks have too much indentation (too deep). Code should have a maximum of {0} indentation levels. """.format(G_MAX_INDENTATION_THRESHOLD) exit(0) - + for t in targets: if t[-2:] == ".h" or t[-4:] == ".cpp" or t[-2] == ".c": file_contents, clean_file_contents, file_lines, clean_file_lines = read_file(t) diff --git a/scripts/check_license.py b/scripts/check_license.py index 340c31b..7ec9ed5 100755 --- a/scripts/check_license.py +++ b/scripts/check_license.py @@ -20,28 +20,13 @@ import sys, re, string from common_modules.common import * - -BMW_LICENSE_TEMPLATE = '''/*************************************************************************** -* -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -****************************************************************************/''' +from common_modules.config import G_LICENSE_TEMPLATES def clean_comment_chars(s): """ - Removes comment characters from a string + Removes comment characters from a string + """ s = string.replace(s, "/", "") s = string.replace(s, "*", "") @@ -50,79 +35,119 @@ def clean_comment_chars(s): def make_license_re(license_text): """ - Makes a regular expression for every line in the license, this would match the license - text tolerating extra spaces + Makes a regular expression for every line in the license, this would match the license + text tolerating extra spaces + """ license_lines = license_text.split("\n") license_lines_re = {} for i in range(len(license_lines)): license_line = license_lines[i] re_text = clean_comment_chars(license_line) + #remove white space paddings re_text = re_text.strip(" \n\t\r\f") + #replace special characters re_text = string.replace(re_text, "(", "\(") re_text = string.replace(re_text, ")", "\)") re_text = string.replace(re_text, " ", "(\s+)") re_text = string.replace(re_text, "\n", "(\s*)") re_text = string.replace(re_text, "\t", "(\s+)") re_text = string.replace(re_text, ".", "\.") - + + #this replaces the text [YYYY] with a regex that mathces years in one of the following forms: + #2013 or 2000-2013 or 2000 or 2000, 2001, 2002, 2013 re_text = string.replace(re_text, "[YYYY]", r"(\d{4})(((-(\d{4}))|(((\s*),(\s*)\d{4})+))?)") - + if len(re_text) > 0: re_text = "(\s*)" + re_text + "(\s*)" current_text = "" + #remove unneeded space matches while current_text != re_text: current_text = re_text re_text = string.replace(re_text, "(\s*)(\s*)", "(\s*)") re_text = string.replace(re_text, "(\s+)(\s+)", "(\s+)") re_text = string.replace(re_text, "(\s*)(\s+)", "(\s+)") - license_lines_re[i] = re_text return license_lines_re +def check_specific_license_in_file(filename, clean_file_contents, license_text): + """ + Checks if the file contains a valid license according to the license template provided -def check_license_in_file(filename, file_contents): - clean_file_contents = clean_comment_chars(file_contents) - license_text = BMW_LICENSE_TEMPLATE - + """ license_lines = license_text.split("\n") - + license_re = make_license_re(license_text) - + #search for the first line of the license in the target file line_re = re.compile(license_re.values()[0]) found_match = line_re.search(clean_file_contents) - + if found_match: clean_file_contents = clean_file_contents[found_match.start():] - - #check license as it is + + #check that license exists without any added or removed words for (line_num, line_re_text) in license_re.items(): line_re = re.compile(line_re_text) found_match = line_re.match(clean_file_contents) - + if found_match: clean_file_contents = clean_file_contents[found_match.end():] else: - log_warning(filename, 1, "license does not match at", license_lines[line_num]) + #log_warning(filename, 1, "license does not match at", license_lines[line_num]) + return (line_num, license_lines[line_num]) + + return None # success + +def check_license_in_file(filename, file_contents): + """ + Checks if the file contains a valid license. + It tries to find a match inside the file with any of the licenses configured + + """ + clean_file_contents = clean_comment_chars(file_contents) + + #license that had the best match with the file + best_match = (-1, None) + + #try to match with every license + for license in G_LICENSE_TEMPLATES: + call_result = check_specific_license_in_file(filename, clean_file_contents, license) + + #if match is found just return + if call_result == None: return None + #if no match found check if this license was a good candidate for the match + else: + best_match = call_result if call_result[0] > best_match[0] else best_match + + #(this else clause is executed if the for loop exists naturally) + #if loop ended without return, this means no license matched + else: + #if no license matched at all + if best_match[1] == None: + log_warning(filename, 1, "no license found") + + #get the license with the best match + else: + log_warning(filename, 1, "license does not match at", best_match[1]) if __name__ == "__main__": targets = sys.argv[1:] targets = get_all_files(targets) - + if len(targets) == 0: print """ -\t**** No input provided *** +\t**** No input provided **** \tTakes a list of files/directories as input and performs specific style checking on all files/directories. \tGives warnings if the file does not contain a valid license text. It does not check if Copyright statements are included. """ exit(0) - + for t in targets: file_contents, _, _, _ = read_file(t) check_license_in_file(t, file_contents) diff --git a/scripts/check_long_lines.py b/scripts/check_long_lines.py index 63da64d..636f798 100755 --- a/scripts/check_long_lines.py +++ b/scripts/check_long_lines.py @@ -24,11 +24,17 @@ from common_modules.config import G_MAX_LINE_LENGTH_THRESHOLD def check_long_lines(filename, file_lines): + """ + Checks if a line is too long by comparing it to a (configurable) threshold. If a line contains + string literals it is not checked + + """ + #regex for finding strings string_re = re.compile(r'"((?!((? G_MAX_LINE_LENGTH_THRESHOLD and re.search(string_re, line) == None: log_warning(filename, i + 1, "line too long, {0} characters [maximum is {1}]".format(len(line), G_MAX_LINE_LENGTH_THRESHOLD)) @@ -37,16 +43,16 @@ def check_long_lines(filename, file_lines): if __name__ == "__main__": targets = sys.argv[1:] targets = get_all_files(targets) - + if len(targets) == 0: print """ -\t**** No input provided *** +\t**** No input provided **** \tTakes a list of files/directories as input and performs specific style checking on all files/directories. \tGives warnings if there are too long lines for readability. A line can have a maximum of {0} characters. """.format(G_MAX_LINE_LENGTH_THRESHOLD) exit(0) - + for t in targets: _, _, file_lines, _ = read_file(t) check_long_lines(t, file_lines) diff --git a/scripts/check_single_definition_on_line.py b/scripts/check_single_definition_on_line.py index 13cdacf..02b709b 100755 --- a/scripts/check_single_definition_on_line.py +++ b/scripts/check_single_definition_on_line.py @@ -22,29 +22,60 @@ import sys, re, string from common_modules.common import * def check_single_definition_on_line(filename, file_contents, clean_file_contents, file_lines, clean_file_lines): + """ + Checks if a line contains several variable definitions + + """ + #remove arrays + #an array regex starts with "{", ends with "}" and contains anything in between + #an array of arrays is removed recursively inside out + array_re = re.compile(r'\{[^ {};]*\}') + clean_file_contents = clean_string_from_regex(clean_file_contents, array_re, '') + + #remove brackets + array_brackets_re = re.compile(r'\[((?!\[|\]|;).)*\]') + clean_file_contents = clean_string_from_regex(clean_file_contents, array_brackets_re, '') + + #remove angle brackets + template_re = re.compile(r'<((?![<>;]).)*>') + clean_file_contents = clean_string_from_regex(clean_file_contents, template_re, '') + #variable name can be preceeded by * for pointers or & for references, can be followed by an assignment - var_decl_re_text = r"(((\**)|(&?))(\s*)(\w+)((=((?![,{]).)*)?))" + var_decl_re_text = r"""( + ((&?)(\*)*) # can start by & (for reference defs) and/or any number of * (for pointer defs) + (\s*)(\w+)(\s*) # followed by identifier + ((=((?![,;]).)*)?) # can be followed by an assignment + )""" + #types can be in namespaces or nested classes - no_template_type_re_text = r"\w+(::\w+)*" - simple_several_defs_re = re.compile(r"^(\s*){0}(\s+){1}((\s*),(\s*){1})+;".format(no_template_type_re_text, var_decl_re_text), re.MULTILINE) - - for match in re.finditer(simple_several_defs_re, clean_file_contents): + type_re_text = r"\w+(::\w+)*" + + #it is enough to have + several_defs_re_text = r""" + ^(\s*){0} + (\s+){1} + ((\s*),(\s*){1})+; + """.format(type_re_text, var_decl_re_text) + + several_defs_re = re.compile(several_defs_re_text, re.MULTILINE | re.VERBOSE) + + for match in re.finditer(several_defs_re, clean_file_contents): line_number = clean_file_contents.count("\n", 0, match.end()) log_warning(filename, line_number + 1, "several definitions on same line", file_lines[line_number].strip(" \t\r\n")) if __name__ == "__main__": targets = sys.argv[1:] targets = get_all_files(targets) - + if len(targets) == 0: print """ -\t**** No input provided *** +\t**** No input provided **** \tTakes a list of files/directories as input and performs specific style checking on all files/directories. \tGives warnings if a line contains several variable definitions (This does not include lines that contain template or array declarations). """ exit(0) - + for t in targets: if t[-2:] == ".h" or t[-4:] == ".cpp" or t[-2] == ".c": file_contents, clean_file_contents, file_lines, clean_file_lines = read_file(t) diff --git a/scripts/check_single_statement_on_line.py b/scripts/check_single_statement_on_line.py index 7096e13..313c140 100755 --- a/scripts/check_single_statement_on_line.py +++ b/scripts/check_single_statement_on_line.py @@ -22,13 +22,17 @@ import sys, re, string from common_modules.common import * def check_single_statement_on_line(filename, file_contents, clean_file_contents, file_lines, clean_file_lines): + """ + Checks if there are several statements on the same line + + """ open_parantheses_count = 0 - + for i in range(len(file_lines)): line = clean_file_lines[i] - + open_parantheses_count += line.count("(") - line.count(")") - + #check if there are several semicolons on the line and that is not in the middle of a for statement if line.count(";") > 1 and open_parantheses_count == 0: #check that there is NO right parentheses after semicolon @@ -38,16 +42,16 @@ def check_single_statement_on_line(filename, file_contents, clean_file_contents, if __name__ == "__main__": targets = sys.argv[1:] targets = get_all_files(targets) - + if len(targets) == 0: print """ -\t**** No input provided *** +\t**** No input provided **** \tTakes a list of files/directories as input and performs specific style checking on all files/directories. \tGives warnings if a line contains several statements. """ exit(0) - + for t in targets: if t[-2:] == ".h" or t[-4:] == ".cpp" or t[-2] == ".c": file_contents, clean_file_contents, file_lines, clean_file_lines = read_file(t) diff --git a/scripts/check_tabbing_and_spacing.py b/scripts/check_tabbing_and_spacing.py index eb04529..a8d72b5 100755 --- a/scripts/check_tabbing_and_spacing.py +++ b/scripts/check_tabbing_and_spacing.py @@ -22,19 +22,30 @@ import sys, re, string from common_modules.common import * def check_tabs_no_spaces(filename, file_lines): + """ + Checks if any tab character "\t" exists in the file + + """ for i in range(len(file_lines)): if file_lines[i].count("\t") > 0: log_warning(filename, i + 1, "tab character (\\t) found, use 4 spaces instead", file_lines[i].strip(" ")) def check_correct_space_count(filename, file_lines): + """ + Checks that the number of spaces at the beginning of every line is divislbe by four + + """ in_multiline_comment = False for i in range(len(file_lines)): if in_multiline_comment: + #if end of muli-line comment if file_lines[i].count("*/") > 0: + #just make sure there is no other new multi-line comment starting on the same line (after the current mult-line comment is closed) in_multiline_comment = file_lines[i].count("/*") > 0 and file_lines[i].index("/*") > file_lines[i].index("*/") else: in_multiline_comment = file_lines[i].count("/*") > 0 - + + #regex searches for the first character that is not a space character found_match = re.search("(?! )", file_lines[i]) if found_match: space_count = found_match.start() @@ -43,11 +54,19 @@ def check_correct_space_count(filename, file_lines): def check_no_spacing_line_end(filename, file_lines): + """ + Checks that lines do not end with unnecessary white space characters + + """ for i in range(len(file_lines)): if re.search(" $", file_lines[i]): log_warning(filename, i + 1, "unneeded space(s) at end of line") def check_tabbing_and_spacing(filename, file_lines): + """ + Calls other functions that check general issues about tabbing and spacing + + """ check_tabs_no_spaces(filename, file_lines) check_correct_space_count(filename, file_lines) check_no_spacing_line_end(filename, file_lines) @@ -59,14 +78,14 @@ if __name__ == "__main__": if len(targets) == 0: print """ -\t**** No input provided *** +\t**** No input provided **** \tTakes a list of files/directories as input and performs specific style checking on all files/directories. \tGives warnings if a line contains unneeded spaces at end of line, contains tab characters (\\t) \tor if spaces at line beginning are not divisible by 4. """ exit(0) - + for t in targets: if t[-2:] == ".h" or t[-4:] == ".cpp" or t[-2] == ".c": _, _, file_lines, _ = read_file(t) diff --git a/scripts/common_modules/common.py b/scripts/common_modules/common.py index 70d42b8..4b0520b 100755 --- a/scripts/common_modules/common.py +++ b/scripts/common_modules/common.py @@ -19,7 +19,7 @@ ########################################################################### """ - Contains utility functions used by other modules +Contains utility functions used by other modules """ import sys, re, string, os @@ -27,10 +27,10 @@ import config def clean_string_from_regex(s, regex, marker): """ - Removes all occurances of the passed regex from a string s, and puts instances of marker at the beginnings and ends - of remved substring - """ + Removes all occurances of the passed regex from a string s, and puts instances of marker at the beginnings and ends + of remved substring + """ result = s found_match = re.search(regex, result) @@ -45,18 +45,19 @@ def clean_string_from_regex(s, regex, marker): def get_clean_file_contents(file_contents): """ - Removes strings, C style and C++ comments style comments and macros from file_contents + Removes strings, C style and C++ comments style comments and macros from file_contents + """ - + clean_file_contents = file_contents - + #remove all strings #a string is something that starts with a (") and ends with a ("), can have any character inside it except a (") that is not preceded by a slash (\) that is not preceded by another slash #this means a string can contains a \\ escape sequence #a string can contain a \" escape sequence #a string can end with \\", but it does not end with \" string_re = re.compile(r'"((?!((?