-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
774 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
#!/usr/bin/env python | ||
|
||
import fnmatch | ||
import os | ||
import re | ||
import ntpath | ||
import sys | ||
import argparse | ||
|
||
if sys.version_info.major == 2: | ||
import codecs | ||
open = codecs.open | ||
|
||
def check_config_style(filepath): | ||
bad_count_file = 0 | ||
def pushClosing(t): | ||
closingStack.append(closing.expr) | ||
closing << Literal( closingFor[t[0]] ) | ||
|
||
def popClosing(): | ||
closing << closingStack.pop() | ||
|
||
with open(filepath, 'r', encoding='utf-8', errors='ignore') as file: | ||
content = file.read() | ||
|
||
# Store all brackets we find in this file, so we can validate everything on the end | ||
brackets_list = [] | ||
|
||
# To check if we are in a comment block | ||
isInCommentBlock = False | ||
checkIfInComment = False | ||
# Used in case we are in a line comment (//) | ||
ignoreTillEndOfLine = False | ||
# Used in case we are in a comment block (/* */). This is true if we detect a * inside a comment block. | ||
# If the next character is a /, it means we end our comment block. | ||
checkIfNextIsClosingBlock = False | ||
|
||
# We ignore everything inside a string | ||
isInString = False | ||
# Used to store the starting type of a string, so we can match that to the end of a string | ||
inStringType = ''; | ||
|
||
lastIsCurlyBrace = False | ||
checkForSemiColumn = False | ||
|
||
# Extra information so we know what line we find errors at | ||
lineNumber = 1 | ||
|
||
indexOfCharacter = 0 | ||
# Parse all characters in the content of this file to search for potential errors | ||
for c in content: | ||
if (lastIsCurlyBrace): | ||
lastIsCurlyBrace = False | ||
if c == '\n': # Keeping track of our line numbers | ||
lineNumber += 1 # so we can print accurate line number information when we detect a possible error | ||
if (isInString): # while we are in a string, we can ignore everything else, except the end of the string | ||
if (c == inStringType): | ||
isInString = False | ||
# if we are not in a comment block, we will check if we are at the start of one or count the () {} and [] | ||
elif (isInCommentBlock == False): | ||
|
||
# This means we have encountered a /, so we are now checking if this is an inline comment or a comment block | ||
if (checkIfInComment): | ||
checkIfInComment = False | ||
if c == '*': # if the next character after / is a *, we are at the start of a comment block | ||
isInCommentBlock = True | ||
elif (c == '/'): # Otherwise, will check if we are in an line comment | ||
ignoreTillEndOfLine = True # and an line comment is a / followed by another / (//) We won't care about anything that comes after it | ||
|
||
if (isInCommentBlock == False): | ||
if (ignoreTillEndOfLine): # we are in a line comment, just continue going through the characters until we find an end of line | ||
if (c == '\n'): | ||
ignoreTillEndOfLine = False | ||
else: # validate brackets | ||
if (c == '"' or c == "'"): | ||
isInString = True | ||
inStringType = c | ||
elif (c == '/'): | ||
checkIfInComment = True | ||
elif (c == '('): | ||
brackets_list.append('(') | ||
elif (c == ')'): | ||
if (len(brackets_list) > 0 and brackets_list[-1] in ['{', '[']): | ||
print("ERROR: Possible missing round bracket ')' detected at {0} Line number: {1}".format(filepath,lineNumber)) | ||
bad_count_file += 1 | ||
brackets_list.append(')') | ||
elif (c == '['): | ||
brackets_list.append('[') | ||
elif (c == ']'): | ||
if (len(brackets_list) > 0 and brackets_list[-1] in ['{', '(']): | ||
print("ERROR: Possible missing square bracket ']' detected at {0} Line number: {1}".format(filepath,lineNumber)) | ||
bad_count_file += 1 | ||
brackets_list.append(']') | ||
elif (c == '{'): | ||
brackets_list.append('{') | ||
elif (c == '}'): | ||
lastIsCurlyBrace = True | ||
if (len(brackets_list) > 0 and brackets_list[-1] in ['(', '[']): | ||
print("ERROR: Possible missing curly brace '}}' detected at {0} Line number: {1}".format(filepath,lineNumber)) | ||
bad_count_file += 1 | ||
brackets_list.append('}') | ||
elif (c== '\t'): | ||
print("ERROR: Tab detected at {0} Line number: {1}".format(filepath,lineNumber)) | ||
bad_count_file += 1 | ||
|
||
else: # Look for the end of our comment block | ||
if (c == '*'): | ||
checkIfNextIsClosingBlock = True; | ||
elif (checkIfNextIsClosingBlock): | ||
if (c == '/'): | ||
isInCommentBlock = False | ||
elif (c != '*'): | ||
checkIfNextIsClosingBlock = False | ||
indexOfCharacter += 1 | ||
|
||
if brackets_list.count('[') != brackets_list.count(']'): | ||
print("ERROR: A possible missing square bracket [ or ] in file {0} [ = {1} ] = {2}".format(filepath,brackets_list.count('['),brackets_list.count(']'))) | ||
bad_count_file += 1 | ||
if brackets_list.count('(') != brackets_list.count(')'): | ||
print("ERROR: A possible missing round bracket ( or ) in file {0} ( = {1} ) = {2}".format(filepath,brackets_list.count('('),brackets_list.count(')'))) | ||
bad_count_file += 1 | ||
if brackets_list.count('{') != brackets_list.count('}'): | ||
print("ERROR: A possible missing curly brace {{ or }} in file {0} {{ = {1} }} = {2}".format(filepath,brackets_list.count('{'),brackets_list.count('}'))) | ||
bad_count_file += 1 | ||
return bad_count_file | ||
|
||
def main(): | ||
|
||
print("Validating Config Style") | ||
|
||
sqf_list = [] | ||
bad_count = 0 | ||
|
||
parser = argparse.ArgumentParser() | ||
parser.add_argument('-m','--module', help='only search specified module addon folder', required=False, default="") | ||
args = parser.parse_args() | ||
|
||
# Allow running from root directory as well as from inside the tools directory | ||
rootDir = "../addons" | ||
if (os.path.exists("addons")): | ||
rootDir = "addons" | ||
|
||
for root, dirnames, filenames in os.walk(rootDir + '/' + args.module): | ||
for filename in fnmatch.filter(filenames, '*.cpp'): | ||
sqf_list.append(os.path.join(root, filename)) | ||
for filename in fnmatch.filter(filenames, '*.hpp'): | ||
sqf_list.append(os.path.join(root, filename)) | ||
|
||
for filename in sqf_list: | ||
bad_count = bad_count + check_config_style(filename) | ||
|
||
print("------\nChecked {0} files\nErrors detected: {1}".format(len(sqf_list), bad_count)) | ||
if (bad_count == 0): | ||
print("Config validation PASSED") | ||
else: | ||
print("Config validation FAILED") | ||
|
||
return bad_count | ||
|
||
if __name__ == "__main__": | ||
sys.exit(main()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
#!/usr/bin/env python3 | ||
|
||
import fnmatch | ||
import os | ||
import re | ||
import sys | ||
|
||
|
||
def get_files(): | ||
# Allow running from root directory and tools directory | ||
root_dir = ".." | ||
if os.path.exists("addons"): | ||
root_dir = "." | ||
|
||
sqf_files = [] | ||
|
||
for root, _, files in os.walk(root_dir): | ||
for file in fnmatch.filter(files, "*.sqf"): | ||
sqf_files.append(os.path.join(root, file)) | ||
|
||
sqf_files.sort() | ||
|
||
return sqf_files | ||
|
||
|
||
def filter_files(filepaths): | ||
filtered_files = [] | ||
|
||
# Return only files that have a docblock | ||
for filepath in filepaths: | ||
with open(filepath, 'r') as file_contents: | ||
for line in file_contents: | ||
contents = line.strip() | ||
|
||
# A possible docblock starts | ||
if contents.startswith('/*'): | ||
# Find the `* Return Value:` comment | ||
lines = list(map( | ||
# Remove \n from all the lines | ||
(lambda s: s.strip()), file_contents.readlines() | ||
)) | ||
|
||
return_value_comment_index = lines.index('* Return Value:') | ||
return_value_index = return_value_comment_index + 1 | ||
|
||
# Drop the first two characters (e.g. `* `) so it returns the return type | ||
return_value = lines[return_value_index][2:] | ||
|
||
filtered_files.append([filepath, return_value]) | ||
|
||
break | ||
|
||
return filtered_files | ||
|
||
|
||
def get_last_line(filepath): | ||
with open(filepath, 'r') as file_contents: | ||
lines = file_contents.readlines() | ||
last_line = lines[-1].strip() | ||
|
||
# Handle multiple blank lines at the end of the file | ||
if last_line == "": | ||
i = -2 | ||
|
||
while lines[i].strip() == "": | ||
i -= 1 | ||
|
||
return lines[i].strip() | ||
return last_line | ||
|
||
|
||
def check_last_character(filepath, return_value): | ||
last_line = get_last_line(filepath) | ||
last_line_character = last_line[-1] | ||
|
||
# If return type is None and the last line has a semicolon OR the last thing is just the nil keyword OR last thing is a closing bracket | ||
if return_value == 'None' and (last_line_character == ';' or last_line == 'nil' or last_line == '};'): | ||
return True | ||
elif return_value != 'None' and (last_line_character != ';' or last_line == '};'): | ||
return True | ||
else: | ||
return False | ||
|
||
|
||
def get_expected_last_line(last_line, return_value): | ||
last_line_character = last_line[-1] | ||
|
||
if return_value == 'None': | ||
# If last character is a letter or a number | ||
if re.search(r'[A-Za-z0-9]', last_line_character): | ||
return '{};'.format(last_line) | ||
else: | ||
return 'nil' | ||
else: | ||
if last_line_character == ';': | ||
return last_line[:-1] | ||
|
||
return 'Unknown' | ||
|
||
|
||
def main(): | ||
print('Validating Return Types') | ||
print('-----------------------') | ||
|
||
bad_files = [] | ||
|
||
files = get_files() | ||
filtered_files = filter_files(files) | ||
|
||
for file_details in filtered_files: | ||
filepath, return_value = file_details | ||
|
||
status = check_last_character(filepath, return_value) | ||
|
||
if not status: | ||
bad_files.append( | ||
[filepath, return_value, get_last_line(filepath)]) | ||
|
||
error_count = len(bad_files) | ||
print('Found {} error(s)'.format(error_count)) | ||
|
||
for bad_file in bad_files: | ||
filepath, return_value, last_line = bad_file | ||
|
||
expected_last_line = get_expected_last_line(last_line, return_value) | ||
|
||
print('\nERROR: In file {}'.format(filepath)) | ||
print('Incorrect return type, expected `{}`'.format(return_value)) | ||
print('Found line `{}`'.format(last_line)) | ||
print('Expected line `{}`'.format(expected_last_line)) | ||
|
||
if error_count: | ||
print('\nReturn Validation FAILED') | ||
else: | ||
print('\nReturn Validation PASSED') | ||
|
||
return error_count | ||
|
||
|
||
if __name__ == "__main__": | ||
sys.exit(main()) |
Oops, something went wrong.