├── .gitignore ├── README.md ├── checkall.py ├── clang ├── __init__.py ├── check.py ├── format.py ├── tidy.py └── utils.py ├── p1 ├── 19SU │ ├── README.md │ └── codestyle.py ├── README.md └── codestyle.py ├── p2 ├── 20SU │ ├── codestyle.py │ └── driver │ │ ├── constants.h │ │ ├── rand.cpp │ │ └── rand.h ├── README.md └── codestyle.py ├── p3 ├── README.md ├── clang-deprecated │ ├── __init__.py │ ├── check.py │ ├── format.py │ ├── tidy.py │ └── utils.py ├── codestyle.py └── driver │ ├── card.cpp │ ├── card.h │ ├── deck.h │ ├── hand.h │ ├── player.h │ ├── rand.cpp │ └── rand.h ├── p4 ├── README.md ├── codestyle.py └── driver │ ├── binaryTree.h │ ├── huffmanTree.cpp │ └── huffmanTree.h ├── p5 ├── README.md ├── clang │ ├── __init__.py │ ├── check.py │ ├── format.py │ ├── tidy.py │ └── utils.py ├── codestyle.py └── driver │ ├── Dlist.h │ └── Instr.h ├── preprocess └── uncompress.py └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | test 2 | *.cpp 3 | *.h 4 | !*/driver/*.cpp 5 | !*/driver/*.h 6 | !p*/*/driver/*.cpp 7 | !p*/*/driver/*.h 8 | *_records 9 | *.tar.* 10 | *.zip 11 | .idea 12 | *.csv 13 | 14 | # Byte-compiled / optimized / DLL files 15 | __pycache__/ 16 | *.py[cod] 17 | *$py.class 18 | 19 | # C extensions 20 | *.so 21 | 22 | # Distribution / packaging 23 | .Python 24 | build/ 25 | develop-eggs/ 26 | dist/ 27 | downloads/ 28 | eggs/ 29 | .eggs/ 30 | lib/ 31 | lib64/ 32 | parts/ 33 | sdist/ 34 | var/ 35 | wheels/ 36 | pip-wheel-metadata/ 37 | share/python-wheels/ 38 | *.egg-info/ 39 | .installed.cfg 40 | *.egg 41 | MANIFEST 42 | 43 | # PyInstaller 44 | # Usually these files are written by a python script from a template 45 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 46 | *.manifest 47 | *.spec 48 | 49 | # Installer logs 50 | pip-log.txt 51 | pip-delete-this-directory.txt 52 | 53 | # Unit test / coverage reports 54 | htmlcov/ 55 | .tox/ 56 | .nox/ 57 | .coverage 58 | .coverage.* 59 | .cache 60 | nosetests.xml 61 | coverage.xml 62 | *.cover 63 | .hypothesis/ 64 | .pytest_cache/ 65 | 66 | # Translations 67 | *.mo 68 | *.pot 69 | 70 | # Django stuff: 71 | *.log 72 | local_settings.py 73 | db.sqlite3 74 | db.sqlite3-journal 75 | 76 | # Flask stuff: 77 | instance/ 78 | .webassets-cache 79 | 80 | # Scrapy stuff: 81 | .scrapy 82 | 83 | # Sphinx documentation 84 | docs/_build/ 85 | 86 | # PyBuilder 87 | target/ 88 | 89 | # Jupyter Notebook 90 | .ipynb_checkpoints 91 | 92 | # IPython 93 | profile_default/ 94 | ipython_config.py 95 | 96 | # pyenv 97 | .python-version 98 | 99 | # pipenv 100 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 101 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 102 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 103 | # install all needed dependencies. 104 | #Pipfile.lock 105 | 106 | # celery beat schedule file 107 | celerybeat-schedule 108 | 109 | # SageMath parsed files 110 | *.sage.py 111 | 112 | # Environments 113 | .env 114 | .venv 115 | env/ 116 | venv/ 117 | ENV/ 118 | env.bak/ 119 | venv.bak/ 120 | 121 | # Spyder project settings 122 | .spyderproject 123 | .spyproject 124 | 125 | # Rope project settings 126 | .ropeproject 127 | 128 | # mkdocs documentation 129 | /site 130 | 131 | # mypy 132 | .mypy_cache/ 133 | .dmypy.json 134 | dmypy.json 135 | 136 | # Pyre type checker 137 | .pyre/ 138 | 139 | # Mac OS 140 | .DS_Store 141 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Code Check 2 | 3 | This repository includes all static code analysis of projects in ve280. 4 | `clang` and its tools are used to perform the various code checks. 5 | We are planing to embed these checks into JOJ (in the future). 6 | 7 | 8 | ## Prerequisite 9 | 10 | We use `Python>=3.6` and `Clang>=6` to develop the tests. 11 | 12 | On Ubuntu, install them with 13 | 14 | ```bash 15 | sudo apt install python3 python3-pip 16 | sudo apt install clang clang-tools clang-format clang-tidy 17 | pip3 install -r requirements.txt 18 | ``` 19 | 20 | ## Usage 21 | 22 | ### For Students 23 | 24 | Directly run the code check by (p1 as example): 25 | 26 | ```bash 27 | PYTHONPATH=. python3 p1/codestyle.py 28 | ``` 29 | 30 | ### For TAs 31 | 32 | First, download the zip of all submission of the project from JOJ, and rename them into `_records.zip`, for example, `p1_records.zip`. 33 | **Make sure that there is no inner directory within the `_records` folder, i.e., students' submission should be under `_records/` directory instead of `_records//`.** 34 | 35 | Second, uncompress the file with `preprocess/uncompress.py`. 36 | 37 | ```bash 38 | python3 preprocess/uncompress.py p1_records.zip 39 | ``` 40 | 41 | At last, use the `checkall.py` to generate the result in a csv file (`p1_code_check.csv`): 42 | 43 | ```bash 44 | python3 checkall.py p1 45 | ``` 46 | 47 | ## Clang Tidy Arguments 48 | 49 | You can directly test `clang-tidy` warnings by 50 | ```bash 51 | clang-tidy -config='{"Checks": "*,-android-*,-abseil-string-find-str-contains,-altera-*,-bugprone-bool-pointer-implicit-conversion,-bugprone-exception-escape,-bugprone-implicit-widening-of-multiplication-result,-bugprone-easily-swappable-parameters,-cert-*,-cppcoreguidelines-*,-fuchsia-*,-google-*,google-default-arguments,google-explicit-constructor,google-runtime-operator,-hicpp-*,-llvm-*,-llvmlibc-*,-objc-*,-readability-else-after-return,-readability-implicit-bool-conversion,-readability-container-data-pointer,-readability-magic-numbers,-readability-named-parameter,-readability-simplify-boolean-expr,-readability-braces-around-statements,-readability-identifier-naming,-readability-identifier-length,-readability-function-size,-readability-function-cognitive-complexity,-readability-redundant-member-init,-readability-isolate-declaration,-readability-redundant-control-flow,-readability-use-anyofallof,-misc-bool-pointer-implicit-conversion,-misc-definitions-in-headers,-misc-unused-alias-decls,-misc-unused-parameters,-misc-unused-using-decls,-modernize-*,-clang-diagnostic-*,-clang-analyzer-*,-zircon-*,-readability-avoid-const-params-in-decls,-readability-make-member-function-const", "-misc-no-recursion", "CheckOptions": [{"key": "misc-throw-by-value-catch-by-reference.CheckThrowTemporaries", "value": "0"}]}' *.cpp -- 52 | ``` 53 | 54 | 55 | ## General Styles 56 | 57 | 1) Appropriate use of indenting and white space 58 | 2) Program appropriately split into subroutines 59 | 3) Variable and function names that reflect their use 60 | 4) Informative comments at the head of each function. 61 | 62 | 63 | -------------------------------------------------------------------------------- /checkall.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import csv 3 | import os 4 | import shutil 5 | import subprocess 6 | import multiprocessing 7 | 8 | 9 | def check_one(project, checkers, uid, project_dir): 10 | result = [uid] 11 | for checker in checkers: 12 | checker_file = os.path.join(project, checker) 13 | p = subprocess.run("PYTHONPATH=. python3 %s %s --silent" % (checker_file, project_dir), 14 | shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 15 | result += p.stdout.decode().strip().split(',') 16 | print(result) 17 | return result 18 | 19 | 20 | def inject_driver(project_dir, driver_dir): 21 | if os.path.isdir(project_dir) and os.path.isdir(driver_dir): 22 | for file in os.listdir(driver_dir): 23 | shutil.copy2(os.path.join(driver_dir, file), os.path.join(project_dir, file)) 24 | 25 | 26 | def main(project, jobs): 27 | records_dir = project + '_records' 28 | driver_dir = os.path.join(project, 'driver') 29 | # results_dir = project + '_results' 30 | result_file = project + '_code_check.csv' 31 | results = [] 32 | pool = multiprocessing.Pool(processes=jobs) 33 | for uid in os.listdir(records_dir): 34 | project_dir = os.path.join(records_dir, uid) 35 | if os.path.isdir(project_dir): 36 | inject_driver(project_dir, driver_dir) 37 | results.append(pool.apply_async(check_one, (project, ['codestyle.py'], uid, project_dir,))) 38 | pool.close() 39 | pool.join() 40 | 41 | with open(result_file, 'w', encoding='utf-8') as csvfile: 42 | writer = csv.writer(csvfile) 43 | for result in results: 44 | writer.writerow(result.get()) 45 | 46 | 47 | parser = argparse.ArgumentParser(description='Code Check with Multiprocessing.') 48 | parser.add_argument('-j', '--jobs', type=int, default=multiprocessing.cpu_count()) 49 | parser.add_argument('project', type=str, nargs=1) 50 | args = parser.parse_args() 51 | main(args.project[0], jobs=args.jobs) 52 | -------------------------------------------------------------------------------- /clang/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ve280/code-check/8805623f25f625b231f01642746f7b45e3243a5f/clang/__init__.py -------------------------------------------------------------------------------- /clang/check.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | import shlex 4 | import subprocess 5 | 6 | from clang.utils import read_file, split_sources_headers, build_full_paths 7 | 8 | 9 | class FunctionDeclaration: 10 | function_declares = [] 11 | 12 | def __init__(self, line, file=''): 13 | self.file = file 14 | self.error = False 15 | 16 | # use regexp to parse the start and end line number 17 | function_range = re.findall(r"<(.*?)>", line) 18 | if not function_range: 19 | self.error = True 20 | return 21 | 22 | function_range = re.findall(r"[^l0-9]:(\d+)", function_range[0]) 23 | if len(function_range) < 1: 24 | self.error = True 25 | return 26 | self.start = int(function_range[0]) 27 | 28 | # maybe a one-line function (declaration) 29 | if len(function_range) == 1: 30 | self.end = self.start 31 | else: 32 | self.end = int(function_range[1]) 33 | 34 | # simply assume one line definition / declaration 35 | self.len = self.end - self.start 36 | 37 | # use a trick to split the line 38 | splitter = shlex.shlex(line, posix=True) 39 | splitter.whitespace += ',' 40 | splitter.whitespace_split = True 41 | args = list(splitter) 42 | if len(args) < 2: 43 | self.error = True 44 | return 45 | if args[-1] == 'static': 46 | self.static = True 47 | if len(args) < 3: 48 | self.error = True 49 | return 50 | self.name = args[-3] 51 | splitter = shlex.shlex(args[-2], posix=True) 52 | else: 53 | self.static = False 54 | self.name = args[-2] 55 | splitter = shlex.shlex(args[-1], posix=True) 56 | 57 | if self.name == 'main': 58 | self.name += '__' + file 59 | 60 | # use another trick to split the args 61 | splitter.whitespace = ',()' 62 | splitter.whitespace_split = True 63 | args = list(splitter) 64 | if len(args) < 1: 65 | self.error = True 66 | return 67 | self.ret_type = args[0].strip() 68 | self.args_type = list(map(lambda x: x.strip(), args[1:])) 69 | self.id = len(FunctionDeclaration.function_declares) 70 | self.body = [] 71 | FunctionDeclaration.function_declares.append(self) 72 | 73 | def calculate_length(self, lines): 74 | self.len = 0 75 | block_comment = False 76 | for line in lines: 77 | line = line.strip() 78 | if len(line) == 0: 79 | continue 80 | left_block = len(re.findall(r'/\*', line)) 81 | right_block = len(re.findall(r'\*/', line)) 82 | if left_block > right_block: 83 | if not line.startswith('/*'): 84 | self.len += 1 85 | block_comment = True 86 | elif left_block < right_block: 87 | if not line.endswith('*/'): 88 | self.len += 1 89 | block_comment = False 90 | elif block_comment or line.startswith('//'): 91 | continue 92 | else: 93 | self.len += 1 94 | self.len = max(0, self.len - 1) 95 | 96 | def set_body(self, lines): 97 | self.body = lines 98 | 99 | def __str__(self): 100 | prefix = self.static and 'static ' or '' 101 | return '%s%s %s(%s)' % (prefix, self.ret_type, self.name, ', '.join(self.args_type)) 102 | 103 | @staticmethod 104 | def get_by_id(_id): 105 | return FunctionDeclaration.function_declares[_id] 106 | 107 | 108 | class Function: 109 | def __init__(self, func_decl): 110 | self.func_declarations = [func_decl] 111 | self.prototype_comments = 0 112 | self.body_comments = 0 113 | self.name = func_decl.name 114 | self.prototype = str(func_decl) 115 | self.len = 0 116 | 117 | def add_declaration(self, func_decl): 118 | self.func_declarations.append(func_decl) 119 | 120 | def calculate_length(self): 121 | self.len = 0 122 | for func_decl in self.func_declarations: 123 | self.len += func_decl.len 124 | 125 | def analyze_comments(self): 126 | self.prototype_comments = 0 127 | self.body_comments = 0 128 | 129 | def add_comment(line, is_prototype): 130 | if not block_comment: 131 | line = ''.join(re.findall(r'//(.+)', line, re.DOTALL)) 132 | line = re.sub(r'[/*\s]', '', line) 133 | if len(line) > 5: 134 | if is_prototype: 135 | self.prototype_comments += 1 136 | else: 137 | self.body_comments += 1 138 | 139 | for func_decl in self.func_declarations: 140 | is_prototype = func_decl.start == func_decl.end 141 | block_comment = False 142 | for i, line in func_decl.body: 143 | line = line.strip() 144 | if len(line) == 0: 145 | continue 146 | left_block = len(re.findall(r'/\*', line)) 147 | right_block = len(re.findall(r'\*/', line)) 148 | if left_block > right_block: 149 | block_comment = True 150 | add_comment(line, is_prototype) 151 | elif left_block < right_block: 152 | add_comment(line, is_prototype) 153 | block_comment = False 154 | elif block_comment: 155 | add_comment(line, is_prototype) 156 | elif left_block == right_block and left_block > 0: 157 | block_comment = True 158 | add_comment(line, is_prototype) 159 | block_comment = False 160 | elif '//' in line: 161 | line = line[line.find('//'):] 162 | add_comment(line, is_prototype) 163 | 164 | def __str__(self): 165 | return self.prototype 166 | 167 | 168 | def parse_functions_new(project_dir, files, silent=False, functions=None): 169 | if not functions: 170 | functions = dict() 171 | 172 | split_sources_headers(files) 173 | sources, headers, _ = split_sources_headers(files) 174 | files = sources + headers 175 | sources_path = build_full_paths(project_dir, sources) 176 | files_path = build_full_paths(project_dir, files) 177 | p = subprocess.run("clang-check -ast-dump %s --extra-arg='-fno-color-diagnostics' --extra-arg='-std=c++17' --" 178 | % ' '.join(sources_path), shell=True, stdout=subprocess.PIPE, 179 | stderr=silent and subprocess.PIPE or None) 180 | 181 | current_file = None 182 | 183 | if not silent: 184 | print('\nparsing function declarations:') 185 | 186 | func_decl_lines = [] 187 | 188 | lines = p.stdout.decode().split('\n') 189 | for line in lines: 190 | line = line.strip() 191 | result = re.findall(r'<(?!(line|col))(.*?), (line|col):.*?>', line) 192 | if result: 193 | file_name = result[0][1] 194 | flag = False 195 | for i, file_path in enumerate(files_path): 196 | if file_path in file_name: 197 | current_file = files[i] 198 | flag = True 199 | break 200 | if not flag: 201 | current_file = None 202 | 203 | decl = 'FunctionDecl' in line \ 204 | or 'CXXConstructorDecl' in line \ 205 | or 'CXXDestructorDecl' in line \ 206 | or 'CXXMethodDecl' in line 207 | if current_file and ' default ' not in line and decl: 208 | line = line.strip() 209 | func_decl_lines.append((line, current_file)) 210 | 211 | file_func_decls = {x: [] for x in files} 212 | 213 | for line, file in func_decl_lines: 214 | func_decl = FunctionDeclaration(line, file) 215 | if not func_decl.error: 216 | func_prototype = str(func_decl) 217 | if not silent: 218 | print('[%s:%d-%d] %s' % (func_decl.file, func_decl.start, func_decl.end, func_decl)) 219 | if func_prototype not in functions: 220 | functions[func_prototype] = Function(func_decl) 221 | else: 222 | functions[func_prototype].add_declaration(func_decl) 223 | file_func_decls[file].append(func_decl) 224 | elif not silent: 225 | print('error occurred in %s' % line) 226 | 227 | if not silent: 228 | print('\nparsing cpp files:') 229 | 230 | for i, file in enumerate(files): 231 | try: 232 | file_contents = read_file(files_path[i], silent=silent) 233 | func_decls = sorted(file_func_decls[file], key=lambda x: x.start) 234 | for j, func_decl in enumerate(func_decls): 235 | if func_decl.end <= len(file_contents): 236 | """ 237 | if j > 0: 238 | start = max(func_decls[j - 1].end, func_decl.start - 20) 239 | else: 240 | start = max(0, func_decl.start - 20) 241 | end = min(func_decl.end, len(file_contents) - 1) 242 | """ 243 | start = func_decl.start - 2 244 | end = func_decl.end 245 | while file_contents[start].startswith('/') or "*" in file_contents[start]: 246 | if start == 0: 247 | break 248 | start -= 1 249 | while file_contents[end].startswith('/') or "*" in file_contents[start]: 250 | if end == len(file_contents) - 1: 251 | break 252 | end += 1 253 | func_decl.calculate_length(file_contents[func_decl.start:end + 1]) 254 | func_decl.set_body([(x, file_contents[x]) for x in range(start, end + 1)]) 255 | except Exception as e: 256 | if not silent: 257 | print(e.args) 258 | 259 | for function in functions.values(): 260 | function.calculate_length() 261 | 262 | return functions 263 | 264 | 265 | def parse_functions(main_cpp_name, main_cpp_path, silent=False): 266 | p = subprocess.Popen("clang-check -ast-dump %s --extra-arg='-fno-color-diagnostics' --" 267 | % main_cpp_path, shell=True, stdout=subprocess.PIPE) 268 | main_cpp_found = False 269 | functions = {} 270 | 271 | if not silent: 272 | print('\nparsing function declarations:') 273 | while p.poll() is None: 274 | line = p.stdout.readline().decode('utf-8') 275 | if main_cpp_name in line: 276 | main_cpp_found = True 277 | if main_cpp_found and 'FunctionDecl' in line and 'line' in line: 278 | line = line.strip() 279 | func_decl = FunctionDeclaration(line) 280 | if not func_decl.error: 281 | func_prototype = str(func_decl) 282 | if not silent: 283 | print('[line %d-%d] %s' % (func_decl.start, func_decl.end, func_decl)) 284 | if func_prototype not in functions: 285 | functions[func_prototype] = Function(func_decl) 286 | else: 287 | functions[func_prototype].add_declaration(func_decl) 288 | elif not silent: 289 | print('error occurred in %s', line) 290 | 291 | if not silent: 292 | print('\nparsing cpp file:') 293 | 294 | main_cpp_contents = read_file(main_cpp_path, silent=silent) 295 | 296 | for i, func_decl in enumerate(FunctionDeclaration.function_declares): 297 | if func_decl.end <= len(main_cpp_contents): 298 | if i > 1: 299 | start = max(FunctionDeclaration.get_by_id(i - 1).end, func_decl.start - 20) 300 | else: 301 | start = max(0, func_decl.start - 20) 302 | end = func_decl.end 303 | func_decl.set_body([(x, main_cpp_contents[x]) for x in range(start, end)]) 304 | 305 | return functions 306 | 307 | 308 | def parse_comments(functions, silent=False): 309 | if not silent: 310 | print('\nparsing function comments:') 311 | for func_prototype, func in functions.items(): 312 | if func.name == "inline": 313 | continue 314 | func.analyze_comments() 315 | if not silent: 316 | print(func) 317 | print('declarations: %d, body length: %d, prototype comments: %d, body comments: %d' 318 | % (len(func.func_declarations), func.len, func.prototype_comments, func.body_comments)) 319 | -------------------------------------------------------------------------------- /clang/format.py: -------------------------------------------------------------------------------- 1 | import os 2 | import subprocess 3 | import time 4 | 5 | 6 | def generate_formatted_files(project_dir, target_dir, files, silent=False): 7 | if not silent: 8 | print('reformatting files:') 9 | 10 | os.makedirs(target_dir, exist_ok=True) 11 | for file in files: 12 | input_file = os.path.join(project_dir, file) 13 | output_file = os.path.join(target_dir, file) 14 | if not silent: 15 | print('%s => %s' % (input_file, output_file)) 16 | with open(output_file, 'wb') as f: 17 | p = subprocess.run("clang-format -style=WebKit %s" 18 | % input_file, shell=True, stdout=f, 19 | stderr=silent and subprocess.PIPE or None) 20 | # while p.poll() is None: 21 | # time.sleep(0.001) 22 | # Add a newline at the end of file 23 | with open(output_file, 'a') as f: 24 | f.write('\n') 25 | 26 | if not silent: 27 | print('') 28 | 29 | # generate_formatted_files('/home/liu/SJTU/code-check/solution', '/home/liu/SJTU/code-check/solution/formatted', 30 | # ['world_type.h', 'simulation.cpp', 'p3.cpp', 'simulation.h']) 31 | -------------------------------------------------------------------------------- /clang/tidy.py: -------------------------------------------------------------------------------- 1 | import re 2 | import subprocess 3 | import json 4 | 5 | from clang.utils import split_sources_headers, build_full_paths 6 | 7 | # ported from CLion, remove modernize-*, cert-*, hicpp-*, cppcoreguidelines-* 8 | clang_tidy_checks = {'Checks': ','.join([ 9 | "*", 10 | "-android-*", 11 | "-abseil-string-find-str-contains", 12 | "-altera-*", 13 | "-bugprone-bool-pointer-implicit-conversion", 14 | "-bugprone-exception-escape", 15 | "-bugprone-easily-swappable-parameters", 16 | "-bugprone-implicit-widening-of-multiplication-result", 17 | "-cert-*", 18 | "-cppcoreguidelines-*", 19 | "-fuchsia-*", 20 | "-google-*", 21 | "google-default-arguments", 22 | "google-explicit-constructor", 23 | "google-runtime-operator", 24 | "-hicpp-*", 25 | "-llvm-*", 26 | "-llvmlibc-*", 27 | "-objc-*", 28 | "-readability-avoid-const-params-in-decls", 29 | "-readability-make-member-function-const", 30 | "-readability-else-after-return", 31 | "-readability-container-data-pointer", 32 | "-readability-implicit-bool-conversion", 33 | "-readability-magic-numbers", 34 | "-readability-named-parameter", 35 | "-readability-simplify-boolean-expr", 36 | "-readability-braces-around-statements", 37 | "-readability-identifier-naming", 38 | "-readability-identifier-length", 39 | "-readability-function-size", 40 | "-readability-function-cognitive-complexity", 41 | "-readability-redundant-member-init", 42 | "-readability-isolate-declaration", 43 | "-readability-redundant-control-flow", 44 | "-readability-use-anyofallof", 45 | "-misc-bool-pointer-implicit-conversion", 46 | "-misc-definitions-in-headers", 47 | "-misc-unused-alias-decls", 48 | "-misc-unused-parameters", 49 | "-misc-unused-using-decls", 50 | "-modernize-*", 51 | "-clang-diagnostic-*", 52 | "-clang-analyzer-*", 53 | "-zircon-*", 54 | "-misc-no-recursion" 55 | ]), 'CheckOptions': [{ 56 | 'key': 'misc-throw-by-value-catch-by-reference.CheckThrowTemporaries', 57 | 'value': '0' 58 | }]} 59 | 60 | 61 | # print(json.dumps(clang_tidy_checks)) 62 | 63 | 64 | def parse_warnings_new(project_dir, files, silent=False): 65 | split_sources_headers(files) 66 | sources, headers, _ = split_sources_headers(files) 67 | sources_path = build_full_paths(project_dir, sources) 68 | p = subprocess.run("clang-tidy %s -config='%s' --extra-arg='-fno-color-diagnostics' --extra-arg='-std=c++17' --" 69 | % (' '.join(sources_path), json.dumps(clang_tidy_checks)), 70 | shell=True, stdout=subprocess.PIPE, stderr=silent and subprocess.PIPE or None) 71 | 72 | warnings = {} 73 | warnings_count = 0 74 | 75 | if not silent: 76 | print('\nparsing clang-tidy results:') 77 | print('Ignore the following warnings, if they are suspended.') 78 | lines = p.stdout.decode().split('\n') 79 | for line in lines: 80 | line = line.strip() 81 | res = re.findall(r'warning:.*?\[(.*?)\]', line) 82 | if res: 83 | if res[0] in warnings: 84 | warnings[res[0]] += 1 85 | else: 86 | warnings[res[0]] = 1 87 | warnings_count += 1 88 | 89 | return warnings, warnings_count 90 | 91 | 92 | def parse_warnings(main_cpp_path, silent=False): 93 | p = subprocess.Popen("clang-tidy %s -checks=%s --extra-arg='-fno-color-diagnostics' --" 94 | % (main_cpp_path, clang_tidy_checks), 95 | shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 96 | 97 | warnings = {} 98 | warnings_count = 0 99 | 100 | if not silent: 101 | print('\nparsing clang-tidy results:') 102 | while p.poll() is None: 103 | line = p.stdout.readline().decode('utf-8').strip() 104 | res = re.findall(r'warning:.*?\[(.*?)\]', line) 105 | if res: 106 | if res[0] in warnings: 107 | warnings[res[0]] += 1 108 | else: 109 | warnings[res[0]] = 1 110 | warnings_count += 1 111 | 112 | return warnings, warnings_count 113 | -------------------------------------------------------------------------------- /clang/utils.py: -------------------------------------------------------------------------------- 1 | import chardet 2 | import re 3 | import os 4 | import shutil 5 | 6 | 7 | def read_file(file_path, silent=False): 8 | with open(file_path, 'rb') as file: 9 | bytes_str = file.read() 10 | charset = chardet.detect(bytes_str)['encoding'] or 'utf-8' 11 | if not silent: 12 | print('%s: encoding: %s' % (file_path, charset)) 13 | return bytes_str.decode(charset).split('\n') 14 | 15 | 16 | def split_sources_headers(files): 17 | headers = [] 18 | sources = [] 19 | other = [] 20 | for file in files: 21 | if re.findall(r'\.(h|hpp|hh|H|hxx|h\+\+)$', file): 22 | headers.append(file) 23 | elif re.findall(r'\.(c|cpp|cc|C|cxx|c\+\+)$', file): 24 | sources.append(file) 25 | else: 26 | other.append(file) 27 | return sources, headers, other 28 | 29 | 30 | def build_full_paths(project_dir, files): 31 | return list(map(lambda x: os.path.join(project_dir, x), files)) 32 | 33 | 34 | def inject_driver(project_dir, driver_dir): 35 | if os.path.isdir(project_dir) and os.path.isdir(driver_dir): 36 | for file in os.listdir(driver_dir): 37 | shutil.copy2(os.path.join(driver_dir, file), os.path.join(project_dir, file)) 38 | 39 | 40 | def count_not_allowed_headers(project_dir, files, allowed_headers, silent=False): 41 | not_allowed_usage_count = 0 42 | for file in files: 43 | absolute_path = os.path.join(project_dir, file) 44 | with open(absolute_path, 'r', encoding='unicode_escape') as source_file: 45 | lines = source_file.readlines() 46 | for line in lines: 47 | for usage in re.findall(r'\s*#\s*include\s+<\s*[a-z]+\s*>\s*', line): 48 | remove_right = re.split(r'\s*>\s*', usage)[0] 49 | header_name = re.sub(r'\s*#\s*include\s+<\s*', '', remove_right) 50 | if header_name not in allowed_headers: 51 | not_allowed_usage_count += 1 52 | if not silent: 53 | print('Found not allowed header file at:\n\n{}\nin {}'.format(usage, file)) 54 | return not_allowed_usage_count 55 | -------------------------------------------------------------------------------- /p1/19SU/README.md: -------------------------------------------------------------------------------- 1 | # Code Check 2 | 3 | This repository includes all static code analysis of projects in ve280. 4 | `clang` and its tools are used to perform the various code checks. 5 | We are planing to embed these checks into JOJ (in the future). 6 | 7 | 8 | ## Prerequisite 9 | 10 | We use `Python>=3.6` and `Clang>=6` to develop the tests. 11 | 12 | On Ubuntu, install them with 13 | 14 | ```bash 15 | sudo apt install python3 python3-pip 16 | sudo apt install clang clang-tools clang-format clang-tidy 17 | pip3 install -r requirements.txt 18 | ``` 19 | 20 | ## Usage 21 | 22 | ### For Students 23 | 24 | Directly run the code check by (p1 as example): 25 | 26 | ```bash 27 | PYTHONPATH=. python3 p1/codestyle.py 28 | ``` 29 | 30 | ### For TAs 31 | 32 | First, download the zip of all submission of the project from JOJ, for example, `p1_records.zip`. 33 | 34 | Second, uncompress the file with `preprocess/uncompress.py`. 35 | 36 | ```bash 37 | python3 preprocess/uncompress.py p1_records.zip 38 | ``` 39 | 40 | At last, use the `checkall.py` to generate the result in a csv file (`p1_code_check.csv`): 41 | 42 | ```bash 43 | python3 checkall.py p1 44 | ``` 45 | 46 | ## Clang Tidy Arguments 47 | 48 | You can directly test `clang-tidy` warnings by 49 | ```bash 50 | clang-tidy -config='{"Checks": "*,-android-*,-bugprone-bool-pointer-implicit-conversion,-bugprone-exception-escape,-cert-*,-cppcoreguidelines-*,-fuchsia-*,-google-*,google-default-arguments,google-explicit-constructor,google-runtime-operator,-hicpp-*,-llvm-*,-objc-*,-readability-else-after-return,-readability-implicit-bool-conversion,-readability-magic-numbers,-readability-named-parameter,-readability-simplify-boolean-expr,-readability-braces-around-statements,-readability-identifier-naming,-readability-function-size,-readability-redundant-member-init,-readability-isolate-declaration,-readability-redundant-control-flow,-misc-bool-pointer-implicit-conversion,-misc-definitions-in-headers,-misc-unused-alias-decls,-misc-unused-parameters,-misc-unused-using-decls,-modernize-*,-clang-diagnostic-*,-clang-analyzer-*,-zircon-*", "CheckOptions": [{"key": "misc-throw-by-value-catch-by-reference.CheckThrowTemporaries", "value": "0"}]}' *.cpp -- 51 | ``` 52 | 53 | 54 | ## General Styles 55 | 56 | 1) Appropriate use of indenting and white space 57 | 2) Program appropriately split into subroutines 58 | 3) Variable and function names that reflect their use 59 | 4) Informative comments at the head of each function. 60 | 61 | 62 | -------------------------------------------------------------------------------- /p1/19SU/codestyle.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import os 4 | import argparse 5 | from pprint import pprint 6 | 7 | import clang.check 8 | import clang.tidy 9 | 10 | 11 | def main(project_dir, silent=False): 12 | main_cpp_name = 'p1.cpp' 13 | main_cpp_path = os.path.join(project_dir, main_cpp_name) 14 | clang_check_score = 0 15 | 16 | functions = clang.check.parse_functions(main_cpp_name, main_cpp_path, silent=silent) 17 | clang.check.parse_comments(functions, silent=silent) 18 | 19 | subroutine_count = 0 20 | one_line_comment_count = 0 21 | five_line_comment_count = 0 22 | 23 | for func_prototype, func in functions.items(): 24 | comment_count = func.prototype_comments + func.body_comments 25 | 26 | if func.name != 'main': 27 | subroutine_count += 1 28 | if comment_count >= 1: 29 | one_line_comment_count += 1 30 | if comment_count >= 5: 31 | five_line_comment_count += 1 32 | 33 | if subroutine_count <= 1: 34 | if five_line_comment_count > 0: 35 | clang_check_score += 1 36 | clang_check_score += min(2, one_line_comment_count) 37 | else: 38 | clang_check_score += 1 39 | if subroutine_count >= 4: 40 | clang_check_score += 1 41 | clang_check_score += min(4, one_line_comment_count) 42 | 43 | if not silent: 44 | print('\nclang-check score: %d' % clang_check_score) 45 | 46 | clang_tidy_warnings, clang_tidy_warnings_count = clang.tidy.parse_warnings(main_cpp_path, silent=silent) 47 | clang_tidy_score = 0 48 | 49 | if clang_tidy_warnings_count <= 5: 50 | clang_tidy_score += 2 51 | elif clang_tidy_warnings_count <= 11: 52 | clang_tidy_score += 1 53 | if len(clang_tidy_warnings) <= 2: 54 | clang_tidy_score += 2 55 | elif len(clang_tidy_warnings) <= 5: 56 | clang_tidy_score += 1 57 | if not silent: 58 | pprint(clang_tidy_warnings) 59 | print('\nclang-tidy score: %d' % clang_tidy_score) 60 | 61 | if silent: 62 | print('%d,%d' % (clang_check_score, clang_tidy_score)) 63 | 64 | 65 | parser = argparse.ArgumentParser(description='Project 1 Code Checker.') 66 | parser.add_argument('--silent', action='store_true') 67 | parser.add_argument('project_dir', type=str, nargs=1) 68 | args = parser.parse_args() 69 | main(args.project_dir[0], silent=args.silent) 70 | -------------------------------------------------------------------------------- /p1/README.md: -------------------------------------------------------------------------------- 1 | # Project 1 Grading Criteria 2 | 3 | 4 | ## Composition 5 | 1. Correctness: 90% 6 | 2. Codestyle: 10% 7 | 8 | 9 | ## Correctness [90 points] 10 | The correctness score depends on how many test cases you pass on JOJ. 11 | 12 | #### JOJ Test Cases 13 | * Pretest: `case1` ~ `case5` 14 | * Triangle: `case6` ~ `case17` 15 | * Palindrome: `case18` ~ `case27` 16 | * Power: `case28` ~ `case39` 17 | * Abundant: `case40` ~ `case53` 18 | * Invalid: `case54` ~ `case64` 19 | 20 | 21 | ## Codestyle [10 points] 22 | 23 | #### clang-check [6 points] 24 | Number of subroutines: 25 | 1. \>=4 subroutines [2 points] 26 | 2. 2-3 subroutines [1 point] 27 | 3. 0-1 subroutine [0 point] 28 | 29 | Number of comments: 30 | 1. \>=2 subroutines: 1 point for each function containing >=1 comment, at most [4 points] 31 | 2. 0-1 subroutines: exist a function with >=5 comments [2 points] 32 | 33 | #### clang-tidy [4 points] 34 | Number of warning types: 35 | 1. 0-2 types [2 points] 36 | 2. 3-5 types [1 point] 37 | 3. \>=6 types [0 point] 38 | 39 | Number of warnings: 40 | 1. 0-5 warnings [2 points] 41 | 2. 6-11 warnings [1 point] 42 | 3. \>=12 warnings [0 point] 43 | -------------------------------------------------------------------------------- /p1/codestyle.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import argparse 4 | import os 5 | import clang.check 6 | import clang.tidy 7 | 8 | 9 | def main(project_dir,silent=False): 10 | # get functions 11 | main_cpp_name="p1.cpp" 12 | main_cpp_path=os.path.join(project_dir,main_cpp_name) 13 | functions=clang.check.parse_functions(main_cpp_name,main_cpp_path,silent=silent) 14 | clang.check.parse_comments(functions,silent=silent) 15 | 16 | # initialize clang check 17 | clang_check_score=0 18 | subroutine_cnt=0 19 | light_commented_funtion_cnt=0 20 | heavy_commented_function_cnt=0 21 | 22 | for fn_prototype, fn in functions.items(): 23 | # count subroutine 24 | if fn.name != 'main__': 25 | subroutine_cnt+=1 26 | # count comment 27 | comment_cnt=fn.prototype_comments+fn.body_comments 28 | if comment_cnt>=1: 29 | light_commented_funtion_cnt+=1 30 | if comment_cnt>=5: 31 | heavy_commented_function_cnt+=1 32 | 33 | # subroutine score 34 | if subroutine_cnt <=1: 35 | clang_check_score+=0 36 | elif subroutine_cnt<=3: 37 | clang_check_score+=1 38 | else: 39 | clang_check_score+=2 40 | 41 | # comment score 42 | if subroutine_cnt<=1: 43 | if heavy_commented_function_cnt>=1: 44 | clang_check_score+=2 45 | else: 46 | clang_check_score+=min(4,light_commented_funtion_cnt) 47 | 48 | # initialize clang tidy 49 | clang_tidy_score=0 50 | warning_types, warning_cnt = clang.tidy.parse_warnings(main_cpp_path,silent=silent) 51 | 52 | # warning type score 53 | if len(warning_types)<=2: 54 | clang_tidy_score+=2 55 | elif len(warning_cnt)<=5: 56 | clang_tidy_score+=1 57 | 58 | # warning cnt score 59 | if warning_cnt<=5: 60 | clang_tidy_score+=2 61 | elif warning_cnt<=11: 62 | clang_tidy_score+=1 63 | 64 | if not silent: 65 | print("\n") 66 | print("clang-check score: %d" %clang_check_score) 67 | print("clang-tidy score: %d" %clang_tidy_score) 68 | 69 | if silent: 70 | print('%d,%d' % (clang_check_score, clang_tidy_score)) 71 | 72 | 73 | # parse program args and call main 74 | parser = argparse.ArgumentParser(description='Project 1 Code Checker.') 75 | parser.add_argument('--silent', action='store_true') 76 | parser.add_argument('project_dir', type=str, nargs=1) 77 | args = parser.parse_args() 78 | main(args.project_dir[0], silent=args.silent) 79 | -------------------------------------------------------------------------------- /p2/20SU/codestyle.py: -------------------------------------------------------------------------------- 1 | import os 2 | import argparse 3 | from pprint import pprint 4 | import re 5 | 6 | import clang.check 7 | import clang.tidy 8 | import clang.format 9 | import clang.utils 10 | 11 | 12 | def main(project_dir, silent=False): 13 | files = ['bot.cpp', 'main.cpp', 'bot.h'] 14 | format_dir = os.path.join(project_dir, 'formatted') 15 | 16 | clang.format.generate_formatted_files(project_dir, format_dir, files, silent=silent) 17 | 18 | driver_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'driver') 19 | clang.utils.inject_driver(project_dir, driver_dir) 20 | clang.utils.inject_driver(format_dir, driver_dir) 21 | clang_check_score = 0 22 | 23 | functions = clang.check.parse_functions_new(format_dir, files, silent=silent) 24 | clang.check.parse_comments(functions, silent=silent) 25 | 26 | subroutine_count = 0 27 | long_function_count = 0 28 | 29 | for func_prototype, func in functions.items(): 30 | if func.name != 'main' and func.len >= 1: 31 | subroutine_count += 1 32 | if func.len >= 100: 33 | long_function_count += 1 34 | 35 | clang_check_score += min(3, subroutine_count // 4) 36 | clang_check_score += max(0, 3 - long_function_count) 37 | 38 | if not silent: 39 | print('\nsubroutines: %d, long functions: %d' % (subroutine_count, long_function_count)) 40 | print('clang-check score: %d' % clang_check_score) 41 | 42 | clang_tidy_warnings, clang_tidy_warnings_count = clang.tidy.parse_warnings_new(project_dir, files, silent=silent) 43 | clang_tidy_score = 0 44 | 45 | if clang_tidy_warnings_count <= 10: 46 | clang_tidy_score += 2 47 | elif clang_tidy_warnings_count <= 25: 48 | clang_tidy_score += 1 49 | if len(clang_tidy_warnings) <= 3: 50 | clang_tidy_score += 2 51 | elif len(clang_tidy_warnings) <= 6: 52 | clang_tidy_score += 1 53 | 54 | if not silent: 55 | pprint(clang_tidy_warnings) 56 | print('\nclang-tidy score: %d' % clang_tidy_score) 57 | 58 | # header usage check 59 | allowed_header_names = ['iostream', 'fstream', 'sstream', 'iomanip', 'string', 'cstdlib', 'cassert'] 60 | not_allowed_usage_count = clang.utils.count_not_allowed_headers(project_dir, files, allowed_header_names, silent) 61 | header_usage_score = max(0, 5 - not_allowed_usage_count) 62 | 63 | total_score = clang_check_score + clang_tidy_score + header_usage_score 64 | 65 | if silent: 66 | print('%d,%d,%d,%d' % (total_score, clang_check_score, clang_tidy_score, header_usage_score)) 67 | 68 | 69 | parser = argparse.ArgumentParser(description='Project 2 Code Checker.') 70 | parser.add_argument('--silent', action='store_true') 71 | parser.add_argument('project_dir', type=str, nargs=1) 72 | args = parser.parse_args() 73 | main(args.project_dir[0], silent=args.silent) 74 | -------------------------------------------------------------------------------- /p2/20SU/driver/constants.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by 吴佳遥 on 2021/5/25. 3 | // 4 | 5 | #ifndef PROJECT2_CONSTANTS_H 6 | #define PROJECT2_CONSTANTS_H 7 | 8 | /* 9 | * Command Line Arguments Error 10 | */ 11 | #define INVALID_ARGUMENT_MESSAGE "Invalid argument for random settings. Only number is accepted. The program exits" 12 | #define OUT_OF_RANGE_MESSAGE "Random seed exceeds the range of integer. The program exits" 13 | #define MISSING_ARGUMENT_MESSAGE "There are missing arguments. The program exits" 14 | #define CANNOT_OPEN_FILE_PREFIX "Cannot open the file: " 15 | 16 | /* 17 | * Bot Response 18 | */ 19 | #define EXIT_PROMPT "Good night. I am going to sleep" 20 | #define STOP_BOT_FAIL "You are not qualified to stop me" 21 | #define COURSE_NOT_FOUND "I don't know this course" 22 | #define FACULTY_NOT_FOUND "I don't know this instructor" 23 | #define MISSING_KEYWORD "Oh, input the search keyword first..." 24 | 25 | /* 26 | * Help Text 27 | */ 28 | #define HELP_TEXT \ 29 | "Cheat Sheet for Repeater Bot:\n"\ 30 | "Notice: Commands start with #\n"\ 31 | " course [keyword]: find all the courses that contain the keyword\n"\ 32 | " instructor [keyword]: find all the instructors that contain the keyword\n" \ 33 | " help: show help message\n"\ 34 | " time: show the time when the message was sent\n"\ 35 | " stop: (For bot admins only) stop the bot" 36 | 37 | /* 38 | * Values that define bot's response to no-command message 39 | * To repeat or admire, that is a question 40 | */ 41 | #define REPEAT_ROLL 20 42 | #define ADMIRE_ROLL 30 43 | #endif //PROJECT2_CONSTANTS_H 44 | -------------------------------------------------------------------------------- /p2/20SU/driver/rand.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | A C-program for MT19937, with initialization improved 2002/1/26. 3 | Coded by Takuji Nishimura and Makoto Matsumoto. 4 | 5 | Before using, initialize the state by using init_genrand(seed) 6 | or init_by_array(init_key, key_length). 7 | 8 | Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, 9 | All rights reserved. 10 | 11 | Redistribution and use in source and binary forms, with or without 12 | modification, are permitted provided that the following conditions 13 | are met: 14 | 15 | 1. Redistributions of source code must retain the above copyright 16 | notice, this list of conditions and the following disclaimer. 17 | 18 | 2. Redistributions in binary form must reproduce the above copyright 19 | notice, this list of conditions and the following disclaimer in the 20 | documentation and/or other materials provided with the distribution. 21 | 22 | 3. The names of its contributors may not be used to endorse or promote 23 | products derived from this software without specific prior written 24 | permission. 25 | 26 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 27 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 28 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 29 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 30 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 31 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 32 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 33 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 34 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 35 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 36 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 37 | 38 | 39 | Any feedback is very welcome. 40 | http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html 41 | email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space) 42 | */ 43 | 44 | /* 45 | * modified slightly by Brian Noble for use with EECS 280 project 4 Fall 2005. 46 | */ 47 | 48 | #include 49 | #include 50 | 51 | #include "rand.h" 52 | 53 | static int initialized = 0; 54 | 55 | /* Period parameters */ 56 | #define N 624 57 | #define M 397 58 | #define MATRIX_A 0x9908b0dfUL /* constant vector a */ 59 | #define UPPER_MASK 0x80000000UL /* most significant w-r bits */ 60 | #define LOWER_MASK 0x7fffffffUL /* least significant r bits */ 61 | 62 | static unsigned long mt[N]; /* the array for the state vector */ 63 | static int mti = N + 1; /* mti==N+1 means mt[N] is not initialized */ 64 | 65 | /* initializes mt[N] with a seed */ 66 | void init_genrand(unsigned long s) { 67 | mt[0] = s & 0xffffffffUL; 68 | for (mti = 1; mti < N; mti++) { 69 | mt[mti] = 70 | (1812433253UL * (mt[mti - 1] ^ (mt[mti - 1] >> 30)) + mti); 71 | /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */ 72 | /* In the previous versions, MSBs of the seed affect */ 73 | /* only MSBs of the array mt[]. */ 74 | /* 2002/01/09 modified by Makoto Matsumoto */ 75 | mt[mti] &= 0xffffffffUL; 76 | /* for >32 bit machines */ 77 | } 78 | } 79 | 80 | /* initialize by an array with array-length */ 81 | /* init_key is the array for initializing keys */ 82 | /* key_length is its length */ 83 | /* slight change for C++, 2004/2/26 */ 84 | void init_by_array(unsigned long init_key[], int key_length) { 85 | int i, j, k; 86 | init_genrand(19650218UL); 87 | i = 1; 88 | j = 0; 89 | k = (N > key_length ? N : key_length); 90 | for (; k; k--) { 91 | mt[i] = (mt[i] ^ ((mt[i - 1] ^ (mt[i - 1] >> 30)) * 1664525UL)) 92 | + init_key[j] + j; /* non linear */ 93 | mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */ 94 | i++; 95 | j++; 96 | if (i >= N) { 97 | mt[0] = mt[N - 1]; 98 | i = 1; 99 | } 100 | if (j >= key_length) j = 0; 101 | } 102 | for (k = N - 1; k; k--) { 103 | mt[i] = (mt[i] ^ ((mt[i - 1] ^ (mt[i - 1] >> 30)) * 1566083941UL)) 104 | - i; /* non linear */ 105 | mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */ 106 | i++; 107 | if (i >= N) { 108 | mt[0] = mt[N - 1]; 109 | i = 1; 110 | } 111 | } 112 | 113 | mt[0] = 0x80000000UL; /* MSB is 1; assuring non-zero initial array */ 114 | } 115 | 116 | /* generates a random number on [0,0xffffffff]-interval */ 117 | unsigned long genrand_int32(void) { 118 | unsigned long y; 119 | static unsigned long mag01[2] = {0x0UL, MATRIX_A}; 120 | /* mag01[x] = x * MATRIX_A for x=0,1 */ 121 | 122 | if (mti >= N) { /* generate N words at one time */ 123 | int kk; 124 | 125 | if (mti == N + 1) /* if init_genrand() has not been called, */ 126 | init_genrand(5489UL); /* a default initial seed is used */ 127 | 128 | for (kk = 0; kk < N - M; kk++) { 129 | y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK); 130 | mt[kk] = mt[kk + M] ^ (y >> 1) ^ mag01[y & 0x1UL]; 131 | } 132 | for (; kk < N - 1; kk++) { 133 | y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK); 134 | mt[kk] = mt[kk + (M - N)] ^ (y >> 1) ^ mag01[y & 0x1UL]; 135 | } 136 | y = (mt[N - 1] & UPPER_MASK) | (mt[0] & LOWER_MASK); 137 | mt[N - 1] = mt[M - 1] ^ (y >> 1) ^ mag01[y & 0x1UL]; 138 | 139 | mti = 0; 140 | } 141 | 142 | y = mt[mti++]; 143 | 144 | /* Tempering */ 145 | y ^= (y >> 11); 146 | y ^= (y << 7) & 0x9d2c5680UL; 147 | y ^= (y << 15) & 0xefc60000UL; 148 | y ^= (y >> 18); 149 | 150 | return y; 151 | } 152 | 153 | /* generates a random number on [0,0x7fffffff]-interval */ 154 | long genrand_int31(void) { 155 | return (long) (genrand_int32() >> 1); 156 | } 157 | 158 | /* generates a random number on [0,1]-real-interval */ 159 | double genrand_real1(void) { 160 | return genrand_int32() * (1.0 / 4294967295.0); 161 | /* divided by 2^32-1 */ 162 | } 163 | 164 | /* generates a random number on [0,1)-real-interval */ 165 | double genrand_real2(void) { 166 | return genrand_int32() * (1.0 / 4294967296.0); 167 | /* divided by 2^32 */ 168 | } 169 | 170 | /* generates a random number on (0,1)-real-interval */ 171 | double genrand_real3(void) { 172 | return (((double) genrand_int32()) + 0.5) * (1.0 / 4294967296.0); 173 | /* divided by 2^32 */ 174 | } 175 | 176 | /* generates a random number on [0,1) with 53-bit resolution*/ 177 | double genrand_res53(void) { 178 | unsigned long a = genrand_int32() >> 5, b = genrand_int32() >> 6; 179 | return (a * 67108864.0 + b) * (1.0 / 9007199254740992.0); 180 | } 181 | 182 | /* These real versions are due to Isaku Wada, 2002/01/09 added */ 183 | 184 | void initializeWithSeed(int seed) { 185 | if (!initialized) {} 186 | init_genrand(long(seed)); 187 | initialized = 1; 188 | } 189 | 190 | static int randomInt(const int &min, const int &max) { 191 | if (!initialized) { 192 | init_genrand(0); 193 | initialized = 1; 194 | } 195 | const int spread = max - min + 1; 196 | 197 | unsigned long r = genrand_int32(); 198 | int offset = r % spread; 199 | return min + offset; 200 | } 201 | 202 | 203 | int flipCoin() { 204 | int ret = randomInt(1, 10000); 205 | return ret <= 5000; 206 | } 207 | 208 | RespChoice randomResponse() { 209 | const int max = 10000; 210 | int ret = randomInt(1, max); 211 | if (ret <= REPEAT_ROLL * max / 100) { 212 | return REPEAT; 213 | } else if (ret <= (REPEAT_ROLL + ADMIRE_ROLL) * max / 100) { 214 | return ADMIRE; 215 | } else { 216 | return NONE; 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /p2/20SU/driver/rand.h: -------------------------------------------------------------------------------- 1 | #ifndef __RAND_H__ 2 | #define __RAND_H__ 3 | #include "constants.h" 4 | 5 | void initializeWithSeed(int seed); 6 | 7 | // EFFECTS: randomly returns 0 or 1 8 | int flipCoin(); 9 | 10 | enum RespChoice { 11 | REPEAT = 1, 12 | ADMIRE = 2, 13 | NONE = 0, 14 | }; 15 | 16 | /* 17 | * Depending on repeat and admire constant, decides the response 18 | */ 19 | RespChoice randomResponse(); 20 | 21 | #endif /* __RAND_H__ */ 22 | -------------------------------------------------------------------------------- /p2/README.md: -------------------------------------------------------------------------------- 1 | # Project 2 Grading Criteria 2 | 3 | 4 | ## Composition 5 | 1. Correctness: 85% 6 | 2. Coding style: 15% 7 | 8 | 9 | ## Correctness [85 points] 10 | The correctness score depends on how many test cases you pass on JOJ. 11 | 12 | ## Coding style [15 points] 13 | ### clang-check [6 points] 14 | 15 | - **Number of non-main functions** [3 points] 16 | - Your program should be split into at least 12 non-main functions. 17 | 1. 12 or more subroutines [3 points] 18 | 2. 8-11 subroutines [2 points] 19 | 3. 4-7 subroutine [1 point] 20 | 4. 0-3 subroutine [0 point] 21 | 22 | - **Length of functions** [3 points] 23 | - Your main functions should be no longer than 100 lines and non-main functions should be no more than 150 lines. 24 | - 1 point deduction for each function that exceeds 100 lines 25 | 26 | ### clang-tidy [4 points] 27 | 28 | `clang-tidy` is used to check general style. A report made up of intolerable warnings 29 | will be generated and we will count the numbers and types of the warnings and give a deduction. 30 | 31 | - **Warning types (2 pts)** 32 | - For the types of warnings generated, 33 | + 0-3 types of warnings generated [2 points] 34 | + 4-6 types of warnings generated [1 mark] 35 | + 7+ types of warnings generated [0 mark] 36 | - **Warning counts (2 pts)** 37 | - For the number of warnings generated, 38 | + 0-10 warnings generated [2 points] 39 | + 11-25 warnings generated [1 mark] 40 | + 26+ warnings generated [0 mark] 41 | 42 | See clang-tidy flags in https://github.com/ve280/code-check/blob/master/clang/tidy.py 43 | 44 | ### header file usage check [5 points] 45 | Deduction is applied for usage of some header files that are not allowed. 46 | - one point deduction for each appearance of a header file that is not allowed -------------------------------------------------------------------------------- /p2/codestyle.py: -------------------------------------------------------------------------------- 1 | import os 2 | import argparse 3 | from pprint import pprint 4 | import re 5 | 6 | import clang.check 7 | import clang.tidy 8 | import clang.format 9 | import clang.utils 10 | 11 | 12 | def main(project_dir, silent=False): 13 | files = ['mip.cpp','constants.h','image.h'] 14 | format_dir = os.path.join(project_dir, 'formatted') 15 | 16 | clang.format.generate_formatted_files(project_dir, format_dir, files, silent=silent) 17 | 18 | # driver_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'driver') 19 | # clang.utils.inject_driver(project_dir, driver_dir) 20 | # clang.utils.inject_driver(format_dir, driver_dir) 21 | clang_check_score = 0 22 | 23 | functions = clang.check.parse_functions_new(format_dir, files, silent=silent) 24 | clang.check.parse_comments(functions, silent=silent) 25 | 26 | subroutine_count = 0 27 | long_function_count = 0 28 | 29 | for func_prototype, func in functions.items(): 30 | if func.name != 'main__mip.cpp' and func.len >= 1: 31 | subroutine_count += 1 32 | if func.len >= 100: 33 | long_function_count += 1 34 | 35 | clang_check_score += min(3, subroutine_count // 4) 36 | clang_check_score += max(0, 3 - long_function_count) 37 | 38 | if not silent: 39 | print('\nsubroutines: %d, long functions: %d' % (subroutine_count, long_function_count)) 40 | print('clang-check score: %d' % clang_check_score) 41 | 42 | clang_tidy_warnings, clang_tidy_warnings_count = clang.tidy.parse_warnings_new(project_dir, files, silent=silent) 43 | clang_tidy_score = 0 44 | 45 | if clang_tidy_warnings_count <= 10: 46 | clang_tidy_score += 2 47 | elif clang_tidy_warnings_count <= 25: 48 | clang_tidy_score += 1 49 | if len(clang_tidy_warnings) <= 3: 50 | clang_tidy_score += 2 51 | elif len(clang_tidy_warnings) <= 6: 52 | clang_tidy_score += 1 53 | 54 | if not silent: 55 | pprint(clang_tidy_warnings) 56 | print('\nclang-tidy score: %d' % clang_tidy_score) 57 | 58 | # header usage check 59 | allowed_header_names = ['iostream', 'fstream', 'sstream', 'cstring', 'string', 'cstdlib', 'vector', 'algorithm'] 60 | not_allowed_usage_count = clang.utils.count_not_allowed_headers(project_dir, files, allowed_header_names, silent) 61 | header_usage_score = max(0, 5 - not_allowed_usage_count) 62 | 63 | total_score = clang_check_score + clang_tidy_score + header_usage_score 64 | 65 | if silent: 66 | print('%d,%d,%d,%d' % (total_score, clang_check_score, clang_tidy_score, header_usage_score)) 67 | 68 | 69 | parser = argparse.ArgumentParser(description='Project 2 Code Checker.') 70 | parser.add_argument('--silent', action='store_true') 71 | parser.add_argument('project_dir', type=str, nargs=1) 72 | args = parser.parse_args() 73 | main(args.project_dir[0], silent=args.silent) 74 | -------------------------------------------------------------------------------- /p3/README.md: -------------------------------------------------------------------------------- 1 | # Project 3 Grading Criteria 2 | This grading policy is adapted and revised from project 2. 3 | 4 | ## Running `codestyle.py` 5 | This script is for checking 4 files: `deck.cpp`, `hand.cpp`, `player.cpp` and `blackjack.cpp`. Do not worry about errors like "card.h not found". This is just cerr output of clang-check which will not lead to any deduction (since we will not check the already implemented files). If you want to mute those errors, run with 6 | ```bash 7 | python3 p3/codestyle.py 2>/dev/null 8 | ``` 9 | 10 | ## Composition 11 | 1. Correctness: 85% 12 | 2. Coding style: 15% 13 | 14 | 15 | ## Correctness [85 points] 16 | The correctness score depends on how many test cases you pass on JOJ. 17 | 18 | #### JOJ Test Cases 19 | There are in total 6 pretest cases and some hidden cases. 20 | 21 | 22 | ## Coding style [15 points] 23 | 24 | #### clang-check [5 points] 25 | * **Length of functions** [2 point] 26 | 27 | Your main functions should be no longer than 100 lines and non-main functions should be no more than 150 lines. 28 | 29 | 1. No long functions [2 point] 30 | 2. 1 long functions [1 points] 31 | 3. 2 or more long functions [0 points] 32 | 33 | * **Specification comments (REQUIRES, MODIFIES, EFFECTS)** [1.5 points] 34 | 35 | Your functions declarations should always contain specification comments. 36 | 37 | 1. All declarations are well specified [1.5 points] 38 | 2. 1 declaration are poorly specified [1 point] 39 | 3. 2 declarations are poorly specified [0.5 points] 40 | 4. 3 or more declarations are poorly specified [0 points] 41 | 42 | * **Body comments** [1.5 points] 43 | 44 | You should have your functions well commented. The length of function // the number of comments < 50. 45 | 46 | 1. All function are well commented [1.5 points] 47 | 2. 1 function are poorly commented [1 point] 48 | 3. 2 function are poorly commented [0.5 points] 49 | 4. 3 or more function are poorly commented [0 points] 50 | 51 | The total clang-check score will round **down** to the nearest integer, *i.e.* 3.5 will round down to 3. 52 | 53 | #### clang-tidy [5 points] 54 | * **Number of warning types** [3 points] 55 | 1. 0-5 types [3 points] 56 | 2. 5-10 types [2 points] 57 | 3. 10-25 types [1 point] 58 | 4. More than 25 types [0 points] 59 | 60 | * **Number of warnings** [2 points] 61 | 1. 0-2 warnings [2 points] 62 | 2. 2-5 warnings [1 point] 63 | 3. More than 5 warnings [0 points] 64 | 65 | #### Header file usage check [5 points] 66 | 67 | Deduction is applied for usage of some header files that are not allowed. 68 | 69 | - One point deduction for each appearance of a header file that is not allowed 70 | - Allowed header file: 71 | 72 | See clang-tidy flags in https://github.com/ve280/code-check/blob/master/clang/tidy.py 73 | -------------------------------------------------------------------------------- /p3/clang-deprecated/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ve280/code-check/8805623f25f625b231f01642746f7b45e3243a5f/p3/clang-deprecated/__init__.py -------------------------------------------------------------------------------- /p3/clang-deprecated/check.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | import shlex 4 | import subprocess 5 | 6 | from clang.utils import read_file, split_sources_headers, build_full_paths 7 | 8 | 9 | class FunctionDeclaration: 10 | function_declares = [] 11 | 12 | def __init__(self, line, file=''): 13 | self.file = file 14 | self.error = False 15 | 16 | # use regexp to parse the start and end line number 17 | function_range = re.findall(r"<(.*?)>", line) 18 | if not function_range: 19 | self.error = True 20 | return 21 | 22 | function_range = re.findall(r"[^l0-9]:(\d+)", function_range[0]) 23 | if len(function_range) < 1: 24 | self.error = True 25 | return 26 | self.start = int(function_range[0]) 27 | 28 | # maybe a one-line function (declaration) 29 | if len(function_range) == 1: 30 | self.end = self.start 31 | else: 32 | self.end = int(function_range[1]) 33 | 34 | # simply assume one line definition / declaration 35 | self.len = self.end - self.start 36 | 37 | # use a trick to split the line 38 | splitter = shlex.shlex(line, posix=True) 39 | splitter.whitespace += ',' 40 | splitter.whitespace_split = True 41 | args = list(splitter) 42 | if len(args) < 2: 43 | self.error = True 44 | return 45 | if args[-1] == 'static': 46 | self.static = True 47 | if len(args) < 3: 48 | self.error = True 49 | return 50 | self.name = args[-3] 51 | splitter = shlex.shlex(args[-2], posix=True) 52 | else: 53 | self.static = False 54 | self.name = args[-2] 55 | splitter = shlex.shlex(args[-1], posix=True) 56 | 57 | if self.name == 'main': 58 | self.name += '__' + file 59 | 60 | # use another trick to split the args 61 | splitter.whitespace = ',()' 62 | splitter.whitespace_split = True 63 | args = list(splitter) 64 | if len(args) < 1: 65 | self.error = True 66 | return 67 | self.ret_type = args[0].strip() 68 | self.args_type = list(map(lambda x: x.strip(), args[1:])) 69 | self.id = len(FunctionDeclaration.function_declares) 70 | self.body = [] 71 | FunctionDeclaration.function_declares.append(self) 72 | 73 | def calculate_length(self, lines): 74 | self.len = 0 75 | block_comment = False 76 | for line in lines: 77 | line = line.strip() 78 | if len(line) == 0: 79 | continue 80 | left_block = len(re.findall(r'/\*', line)) 81 | right_block = len(re.findall(r'\*/', line)) 82 | if left_block > right_block: 83 | if not line.startswith('/*'): 84 | self.len += 1 85 | block_comment = True 86 | elif left_block < right_block: 87 | if not line.endswith('*/'): 88 | self.len += 1 89 | block_comment = False 90 | elif block_comment or line.startswith('//'): 91 | continue 92 | else: 93 | self.len += 1 94 | self.len = max(0, self.len - 1) 95 | 96 | def set_body(self, lines): 97 | self.body = lines 98 | 99 | def __str__(self): 100 | prefix = self.static and 'static ' or '' 101 | return '%s%s %s(%s)' % (prefix, self.ret_type, self.name, ', '.join(self.args_type)) 102 | 103 | @staticmethod 104 | def get_by_id(_id): 105 | return FunctionDeclaration.function_declares[_id] 106 | 107 | 108 | class Function: 109 | def __init__(self, func_decl): 110 | self.func_declarations = [func_decl] 111 | self.prototype_comments = 0 112 | self.body_comments = 0 113 | self.name = func_decl.name 114 | self.prototype = str(func_decl) 115 | self.len = 0 116 | 117 | def add_declaration(self, func_decl): 118 | self.func_declarations.append(func_decl) 119 | 120 | def calculate_length(self): 121 | self.len = 0 122 | for func_decl in self.func_declarations: 123 | self.len += func_decl.len 124 | 125 | def analyze_comments(self): 126 | self.prototype_comments = 0 127 | self.body_comments = 0 128 | 129 | def add_comment(line, is_prototype): 130 | if not block_comment: 131 | line = ''.join(re.findall(r'//(.+)', line, re.DOTALL)) 132 | line = re.sub(r'[/*\s]', '', line) 133 | if len(line) > 5: 134 | if is_prototype: 135 | self.prototype_comments += 1 136 | else: 137 | self.body_comments += 1 138 | 139 | for func_decl in self.func_declarations: 140 | is_prototype = func_decl.start == func_decl.end 141 | block_comment = False 142 | for i, line in func_decl.body: 143 | line = line.strip() 144 | if len(line) == 0: 145 | continue 146 | left_block = len(re.findall(r'/\*', line)) 147 | right_block = len(re.findall(r'\*/', line)) 148 | if left_block > right_block: 149 | block_comment = True 150 | add_comment(line, is_prototype) 151 | elif left_block < right_block: 152 | add_comment(line, is_prototype) 153 | block_comment = False 154 | elif block_comment: 155 | add_comment(line, is_prototype) 156 | elif left_block == right_block and left_block > 0: 157 | block_comment = True 158 | add_comment(line, is_prototype) 159 | block_comment = False 160 | elif '//' in line: 161 | line = line[line.find('//'):] 162 | add_comment(line, is_prototype) 163 | 164 | def __str__(self): 165 | return self.prototype 166 | 167 | 168 | def parse_functions_new(project_dir, files, silent=False, functions=None): 169 | if not functions: 170 | functions = dict() 171 | 172 | split_sources_headers(files) 173 | sources, headers, _ = split_sources_headers(files) 174 | files = sources + headers 175 | sources_path = build_full_paths(project_dir, sources) 176 | files_path = build_full_paths(project_dir, files) 177 | p = subprocess.Popen("clang-check -ast-dump %s --extra-arg='-fno-color-diagnostics' --" 178 | % ' '.join(sources_path), shell=True, stdout=subprocess.PIPE, 179 | stderr=silent and subprocess.PIPE or None) 180 | 181 | current_file = None 182 | 183 | if not silent: 184 | print('\nparsing function declarations:') 185 | 186 | func_decl_lines = [] 187 | 188 | while p.poll() is None: 189 | line = p.stdout.readline().decode('utf-8').strip() 190 | result = re.findall(r'<(?!(line|col))(.*?), (line|col):.*?>', line) 191 | if result: 192 | file_name = result[0][1] 193 | flag = False 194 | for i, file_path in enumerate(files_path): 195 | if file_path in file_name: 196 | current_file = files[i] 197 | flag = True 198 | break 199 | if not flag: 200 | current_file = None 201 | 202 | decl = 'FunctionDecl' in line \ 203 | or 'CXXConstructorDecl' in line \ 204 | or 'CXXDestructorDecl' in line \ 205 | or 'CXXMethodDecl' in line 206 | if current_file and ' default ' not in line and decl: 207 | line = line.strip() 208 | func_decl_lines.append((line, current_file)) 209 | 210 | file_func_decls = {x: [] for x in files} 211 | 212 | for line, file in func_decl_lines: 213 | func_decl = FunctionDeclaration(line, file) 214 | if not func_decl.error: 215 | func_prototype = str(func_decl) 216 | if not silent: 217 | print('[%s:%d-%d] %s' % (func_decl.file, func_decl.start, func_decl.end, func_decl)) 218 | if func_prototype not in functions: 219 | functions[func_prototype] = Function(func_decl) 220 | else: 221 | functions[func_prototype].add_declaration(func_decl) 222 | file_func_decls[file].append(func_decl) 223 | elif not silent: 224 | print('error occurred in %s' % line) 225 | 226 | if not silent: 227 | print('\nparsing cpp files:') 228 | 229 | for i, file in enumerate(files): 230 | try: 231 | file_contents = read_file(files_path[i], silent=silent) 232 | func_decls = sorted(file_func_decls[file], key=lambda x: x.start) 233 | for j, func_decl in enumerate(func_decls): 234 | if func_decl.end <= len(file_contents): 235 | """ 236 | if j > 0: 237 | start = max(func_decls[j - 1].end, func_decl.start - 20) 238 | else: 239 | start = max(0, func_decl.start - 20) 240 | end = min(func_decl.end, len(file_contents) - 1) 241 | """ 242 | start = func_decl.start - 2 243 | end = func_decl.end 244 | while file_contents[start].startswith('/') or "*" in file_contents[start]: 245 | if start == 0: 246 | break 247 | start -= 1 248 | while file_contents[end].startswith('/') or "*" in file_contents[start]: 249 | if end == len(file_contents) - 1: 250 | break 251 | end += 1 252 | func_decl.calculate_length(file_contents[func_decl.start:end + 1]) 253 | func_decl.set_body([(x, file_contents[x]) for x in range(start, end + 1)]) 254 | except Exception as e: 255 | if not silent: 256 | print(e.args) 257 | 258 | for function in functions.values(): 259 | function.calculate_length() 260 | 261 | return functions 262 | 263 | 264 | def parse_functions(main_cpp_name, main_cpp_path, silent=False): 265 | p = subprocess.Popen("clang-check -ast-dump %s --extra-arg='-fno-color-diagnostics' --" 266 | % main_cpp_path, shell=True, stdout=subprocess.PIPE) 267 | main_cpp_found = False 268 | functions = {} 269 | 270 | if not silent: 271 | print('\nparsing function declarations:') 272 | while p.poll() is None: 273 | line = p.stdout.readline().decode('utf-8') 274 | if main_cpp_name in line: 275 | main_cpp_found = True 276 | if main_cpp_found and 'FunctionDecl' in line and 'line' in line: 277 | line = line.strip() 278 | func_decl = FunctionDeclaration(line) 279 | if not func_decl.error: 280 | func_prototype = str(func_decl) 281 | if not silent: 282 | print('[line %d-%d] %s' % (func_decl.start, func_decl.end, func_decl)) 283 | if func_prototype not in functions: 284 | functions[func_prototype] = Function(func_decl) 285 | else: 286 | functions[func_prototype].add_declaration(func_decl) 287 | elif not silent: 288 | print('error occurred in %s', line) 289 | 290 | if not silent: 291 | print('\nparsing cpp file:') 292 | 293 | main_cpp_contents = read_file(main_cpp_path, silent=silent) 294 | 295 | for i, func_decl in enumerate(FunctionDeclaration.function_declares): 296 | if func_decl.end <= len(main_cpp_contents): 297 | if i > 1: 298 | start = max(FunctionDeclaration.get_by_id(i - 1).end, func_decl.start - 20) 299 | else: 300 | start = max(0, func_decl.start - 20) 301 | end = func_decl.end 302 | func_decl.set_body([(x, main_cpp_contents[x]) for x in range(start, end)]) 303 | 304 | return functions 305 | 306 | 307 | def parse_comments(functions, silent=False): 308 | if not silent: 309 | print('\nparsing function comments:') 310 | for func_prototype, func in functions.items(): 311 | if func.name == "inline": 312 | continue 313 | func.analyze_comments() 314 | if not silent: 315 | print(func) 316 | print('declarations: %d, body length: %d, prototype comments: %d, body comments: %d' 317 | % (len(func.func_declarations), func.len, func.prototype_comments, func.body_comments)) 318 | -------------------------------------------------------------------------------- /p3/clang-deprecated/format.py: -------------------------------------------------------------------------------- 1 | import os 2 | import subprocess 3 | import time 4 | 5 | 6 | def generate_formatted_files(project_dir, target_dir, files, silent=False): 7 | if not silent: 8 | print('reformatting files:') 9 | 10 | os.makedirs(target_dir, exist_ok=True) 11 | for file in files: 12 | input_file = os.path.join(project_dir, file) 13 | output_file = os.path.join(target_dir, file) 14 | if not silent: 15 | print('%s => %s' % (input_file, output_file)) 16 | with open(output_file, 'wb') as f: 17 | p = subprocess.run("clang-format -style=WebKit %s" 18 | % input_file, shell=True, stdout=f, 19 | stderr=silent and subprocess.PIPE or None) 20 | # while p.poll() is None: 21 | # time.sleep(0.001) 22 | 23 | if not silent: 24 | print('') 25 | 26 | # generate_formatted_files('/home/liu/SJTU/code-check/solution', '/home/liu/SJTU/code-check/solution/formatted', 27 | # ['world_type.h', 'simulation.cpp', 'p3.cpp', 'simulation.h']) 28 | -------------------------------------------------------------------------------- /p3/clang-deprecated/tidy.py: -------------------------------------------------------------------------------- 1 | import re 2 | import subprocess 3 | import json 4 | 5 | from clang.utils import split_sources_headers, build_full_paths 6 | 7 | # ported from CLion, remove modernize-*, cert-*, hicpp-*, cppcoreguidelines-* 8 | clang_tidy_checks = {'Checks': ','.join([ 9 | "*", 10 | "-android-*", 11 | "-bugprone-bool-pointer-implicit-conversion", 12 | "-bugprone-exception-escape", 13 | "-cert-*", 14 | "-cppcoreguidelines-*", 15 | "-fuchsia-*", 16 | "-google-*", 17 | "google-default-arguments", 18 | "google-explicit-constructor", 19 | "google-runtime-operator", 20 | "-hicpp-*", 21 | "-llvm-*", 22 | "-objc-*", 23 | "-readability-else-after-return", 24 | "-readability-implicit-bool-conversion", 25 | "-readability-magic-numbers", 26 | "-readability-named-parameter", 27 | "-readability-simplify-boolean-expr", 28 | "-readability-braces-around-statements", 29 | "-readability-identifier-naming", 30 | "-readability-function-size", 31 | "-readability-redundant-member-init", 32 | "-readability-isolate-declaration", 33 | "-readability-redundant-control-flow," 34 | "-misc-bool-pointer-implicit-conversion", 35 | "-misc-definitions-in-headers", 36 | "-misc-unused-alias-decls", 37 | "-misc-unused-parameters", 38 | "-misc-unused-using-decls", 39 | "-modernize-*", 40 | "-clang-diagnostic-*", 41 | "-clang-analyzer-*", 42 | "-zircon-*", 43 | ]), 'CheckOptions': [{ 44 | 'key': 'misc-throw-by-value-catch-by-reference.CheckThrowTemporaries', 45 | 'value': '0' 46 | }]} 47 | 48 | # print(json.dumps(clang_tidy_checks)) 49 | 50 | 51 | def parse_warnings_new(project_dir, files, silent=False): 52 | split_sources_headers(files) 53 | sources, headers, _ = split_sources_headers(files) 54 | sources_path = build_full_paths(project_dir, sources) 55 | p = subprocess.Popen("clang-tidy %s -config='%s' --extra-arg='-fno-color-diagnostics' --" 56 | % (' '.join(sources_path), json.dumps(clang_tidy_checks)), 57 | shell=True, stdout=subprocess.PIPE, stderr=silent and subprocess.PIPE or None) 58 | 59 | warnings = {} 60 | warnings_count = 0 61 | 62 | if not silent: 63 | print('\nparsing clang-tidy results:') 64 | print('Ignore the following warnings, if they are suspended.') 65 | while p.poll() is None: 66 | line = p.stdout.readline().decode('utf-8').strip() 67 | res = re.findall(r'warning:.*?\[(.*?)\]', line) 68 | if res: 69 | if res[0] in warnings: 70 | warnings[res[0]] += 1 71 | else: 72 | warnings[res[0]] = 1 73 | warnings_count += 1 74 | 75 | return warnings, warnings_count 76 | 77 | 78 | def parse_warnings(main_cpp_path, silent=False): 79 | p = subprocess.Popen("clang-tidy %s -checks=%s --extra-arg='-fno-color-diagnostics' --" 80 | % (main_cpp_path, clang_tidy_checks), 81 | shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 82 | 83 | warnings = {} 84 | warnings_count = 0 85 | 86 | if not silent: 87 | print('\nparsing clang-tidy results:') 88 | while p.poll() is None: 89 | line = p.stdout.readline().decode('utf-8').strip() 90 | res = re.findall(r'warning:.*?\[(.*?)\]', line) 91 | if res: 92 | if res[0] in warnings: 93 | warnings[res[0]] += 1 94 | else: 95 | warnings[res[0]] = 1 96 | warnings_count += 1 97 | 98 | return warnings, warnings_count 99 | -------------------------------------------------------------------------------- /p3/clang-deprecated/utils.py: -------------------------------------------------------------------------------- 1 | import chardet 2 | import re 3 | import os 4 | import shutil 5 | 6 | 7 | def read_file(file_path, silent=False): 8 | with open(file_path, 'rb') as file: 9 | bytes_str = file.read() 10 | charset = chardet.detect(bytes_str)['encoding'] or 'utf-8' 11 | if not silent: 12 | print('%s: encoding: %s' % (file_path, charset)) 13 | return bytes_str.decode(charset).split('\n') 14 | 15 | 16 | def split_sources_headers(files): 17 | headers = [] 18 | sources = [] 19 | other = [] 20 | for file in files: 21 | if re.findall(r'\.(h|hpp|hh|H|hxx|h\+\+)$', file): 22 | headers.append(file) 23 | elif re.findall(r'\.(c|cpp|cc|C|cxx|c\+\+)$', file): 24 | sources.append(file) 25 | else: 26 | other.append(file) 27 | return sources, headers, other 28 | 29 | 30 | def build_full_paths(project_dir, files): 31 | return list(map(lambda x: os.path.join(project_dir, x), files)) 32 | 33 | 34 | def inject_driver(project_dir, driver_dir): 35 | if os.path.isdir(project_dir) and os.path.isdir(driver_dir): 36 | for file in os.listdir(driver_dir): 37 | shutil.copy2(os.path.join(driver_dir, file), os.path.join(project_dir, file)) 38 | 39 | 40 | def count_not_allowed_headers(project_dir, files, allowed_headers, silent=False): 41 | not_allowed_usage_count = 0 42 | for file in files: 43 | absolute_path = os.path.join(project_dir, file) 44 | with open(absolute_path, 'r', encoding='unicode_escape') as source_file: 45 | lines = source_file.readlines() 46 | for line in lines: 47 | for usage in re.findall(r'\s*#\s*include\s+<\s*[a-z]+\s*>\s*', line): 48 | remove_right = re.split(r'\s*>\s*', usage)[0] 49 | header_name = re.sub(r'\s*#\s*include\s+<\s*', '', remove_right) 50 | if header_name not in allowed_headers: 51 | not_allowed_usage_count += 1 52 | if not silent: 53 | print('Found not allowed header file at:\n\n{}\nin {}'.format(usage, file)) 54 | return not_allowed_usage_count 55 | -------------------------------------------------------------------------------- /p3/codestyle.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | 4 | import os 5 | import argparse 6 | from pprint import pprint 7 | import re 8 | import clang.check 9 | import clang.tidy 10 | import clang.format 11 | import clang.utils 12 | 13 | def main(project_dir, silent=False): 14 | # Formatting initialization 15 | files = ['deck.cpp', 'hand.cpp', 'player.cpp', 'blackjack.cpp'] 16 | #files = ['deck.h', 'deck.cpp', 'hand.h', 'hand.cpp', 'player.h', 'player.cpp', 'blackjack.cpp','rand.h','card.h'] 17 | format_dir = os.path.join(project_dir, 'formatted') 18 | main_cpp_name = 'blackjack.cpp' 19 | main_cpp_path = os.path.join(project_dir, main_cpp_name) 20 | 21 | # Clang checkings 22 | clang.format.generate_formatted_files(project_dir, format_dir, files, silent=silent) 23 | clang_check_score = 0 24 | subroutine_count = 0 25 | long_function_count = 0 26 | uncomment_prototype_cnt = 0 27 | poorly_commented_cnt = 0 28 | 29 | # Checkpoint 1: Main function length 30 | # Requirement: Main function should be no longer than 100 physical lines. 31 | main_function = clang.check.parse_functions(main_cpp_name, main_cpp_path, silent=silent) 32 | for func_prototype, func in main_function.items(): 33 | if func.func_declarations[0].end - func.func_declarations[0].start >= 100: 34 | long_function_count += 1 35 | 36 | functions = clang.check.parse_functions_new(format_dir, files, silent=silent) 37 | clang.check.parse_comments(functions, silent=silent) 38 | for func_prototype, func in functions.items(): 39 | # Checkpoint 2: Non-main function amount 40 | # Requirement: Program should be split into at least 6 non-main functions. 41 | if func.name != 'main__' + main_cpp_name and func.len >= 1: 42 | subroutine_count += 1 43 | 44 | # Checkpoint 3: Non-main function length and amount 45 | # Requirement: Non-main functions should be no longer than 150 physical lines. 46 | if func.name != 'main' + main_cpp_name and func.len >= 150: 47 | long_function_count += 1 48 | 49 | # Checkpoint 4: Function declaration comments (REQUIRES, MODIFIES, EFFECTS) 50 | # Requirement: All functions should have RME in their declaration. 51 | if func.name != 'main' + main_cpp_name and func.prototype_comments == 0: 52 | tolerance = ['Exception_t', 'bool', 'operator', 'static', 'inline'] 53 | flag = len(func.func_declarations)>1 54 | for entity in tolerance: 55 | if func.name == entity: 56 | flag = False 57 | break 58 | if flag and (not silent): 59 | print("{} is not commented under declaration.".format(func.name)) 60 | uncomment_prototype_cnt += 1 61 | 62 | # Checkpoint 5: Function body comments 63 | # Requirement: the length of function // the number of comments < 50. 64 | if func.body_comments == 0: 65 | if func.len >= 50: 66 | poorly_commented_cnt += 1 67 | elif func.len // func.body_comments >= 50: 68 | poorly_commented_cnt += 1 69 | 70 | # clang_check_score += min(2, subroutine_count // 3) 71 | clang_check_score += max(0, 4 - long_function_count * 2) 72 | clang_check_score += max(0, 3 - uncomment_prototype_cnt) 73 | clang_check_score += max(0, 3 - poorly_commented_cnt) 74 | clang_check_score //= 2 75 | 76 | if not silent: 77 | print('\nsubroutines: %d, \nlong functions: %d, \nuncomment declarations: %d, \npoorly commented functions: %d' 78 | % (subroutine_count, long_function_count, uncomment_prototype_cnt, poorly_commented_cnt)) 79 | print('clang-check score: %d' % clang_check_score) 80 | 81 | clang_tidy_warnings, clang_tidy_warnings_count = clang.tidy.parse_warnings_new(project_dir, files, silent=silent) 82 | clang_tidy_score = 0 83 | 84 | # Checkpoint 6: Clang tidies 85 | # Requirement: Your program should be free of clang tidies. 86 | if clang_tidy_warnings_count <= 5: 87 | clang_tidy_score += 3 88 | elif clang_tidy_warnings_count <= 10: 89 | clang_tidy_score += 2 90 | elif clang_tidy_warnings_count <= 25: 91 | clang_tidy_score += 1 92 | 93 | if len(clang_tidy_warnings) <= 2: 94 | clang_tidy_score += 2 95 | elif len(clang_tidy_warnings) <= 5: 96 | clang_tidy_score += 1 97 | 98 | 99 | # header usage check 100 | allowed_header_names = ['iostream', 'iomanip', 'string', 'cstdlib', 'cassert'] 101 | not_allowed_usage_count = clang.utils.count_not_allowed_headers(project_dir, files, allowed_header_names, silent) 102 | header_usage_score = max(0, 5 - not_allowed_usage_count) 103 | 104 | 105 | if not silent: 106 | pprint(clang_tidy_warnings) 107 | print('\nclang-tidy score: %d' % clang_tidy_score) 108 | print('\nheader usage score: %d' % header_usage_score) 109 | print('\nTotal style score: %d' % (clang_tidy_score + clang_check_score + header_usage_score)) 110 | 111 | if silent: 112 | print('%d,%d,%d' % (clang_check_score, clang_tidy_score, header_usage_score)) 113 | 114 | 115 | parser = argparse.ArgumentParser(description='Project 3 Code Checker.') 116 | parser.add_argument('--silent', action='store_true') 117 | parser.add_argument('project_dir', type=str, nargs=1) 118 | args = parser.parse_args() 119 | main(args.project_dir[0], silent=args.silent) 120 | -------------------------------------------------------------------------------- /p3/driver/card.cpp: -------------------------------------------------------------------------------- 1 | #include "card.h" 2 | #include 3 | using namespace std; 4 | 5 | const string SuitNames[] = {"Spades", "Hearts", "Clubs", "Diamonds"}; 6 | 7 | const string SpotNames[] = {"Two", "Three", "Four", "Five", "Six", 8 | "Seven", "Eight", "Nine", "Ten", "Jack", 9 | "Queen", "King", "Ace"}; 10 | const string SOS_Name[5] = { "Suzumiya Haruhi","Nagato Yuki","Asahina Mikuru","Kyon","Koizumi Itzuki" }; 11 | const string SC_Name[5] = { "Joseph Joestar","Kujo Jotaro","Jean Pierre Polnareff","Kakyoin Noriaki","Mohammed Avdol" }; 12 | /* 13 | void displayCard(Card card) 14 | { 15 | cout << SpotNames[card.spot] << " of " << SuitNames[card.suit] 16 | << flush; 17 | } 18 | */ 19 | -------------------------------------------------------------------------------- /p3/driver/card.h: -------------------------------------------------------------------------------- 1 | #ifndef __CARD_H__ 2 | #define __CARD_H__ 3 | #include 4 | using namespace std; 5 | enum Suit { 6 | SPADES, HEARTS, CLUBS, DIAMONDS 7 | }; 8 | 9 | enum Spot { 10 | TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, 11 | JACK, QUEEN, KING, ACE 12 | }; 13 | 14 | enum Team { 15 | SOSBrigade, StardustCrusaders 16 | }; 17 | 18 | extern const string SuitNames[DIAMONDS + 1]; 19 | extern const string SpotNames[ACE+1]; 20 | extern const string SOS_Name[5]; // The full name of each member in SOS Brigade 21 | extern const string SC_Name[5]; // The full name of each member in Stardust 22 | struct Card { 23 | Spot spot; 24 | Suit suit; 25 | }; 26 | 27 | #endif /* __CARD_H__ */ 28 | -------------------------------------------------------------------------------- /p3/driver/deck.h: -------------------------------------------------------------------------------- 1 | #ifndef __DECK_H__ 2 | #define __DECK_H__ 3 | 4 | #include "card.h" 5 | 6 | class DeckEmpty { // An exception type 7 | }; 8 | 9 | const int DeckSize = 52; 10 | 11 | class Deck { 12 | // A standard deck of 52 playing cards---no jokers 13 | Card deck[DeckSize]; // The deck of cards 14 | int next; // The next card to deal 15 | 16 | public: 17 | 18 | Deck(); 19 | // EFFECTS: constructs a "newly opened" deck of cards. first the 20 | // spades from 2-A, then the hearts, then the clubs, then the 21 | // diamonds. The first card dealt should be the 2 of Spades. 22 | 23 | void reset(); 24 | // EFFECTS: resets the deck to the state of a "newly opened" deck 25 | // of cards: 26 | 27 | void shuffle(int n); 28 | // REQUIRES: n is between 0 and 52, inclusive. 29 | 30 | // MODIFIES: this 31 | 32 | // EFFECTS: cut the deck into two segments: the first n cards, 33 | // called the "left", and the rest called the "right". Note that 34 | // either right or left might be empty. Then, rearrange the deck 35 | // to be the first card of the right, then the first card of the 36 | // left, the 2nd of right, the 2nd of left, and so on. Once one 37 | // side is exhausted, fill in the remainder of the deck with the 38 | // cards remaining in the other side. Finally, make the first 39 | // card in this shuffled deck the next card to deal. For example, 40 | // shuffle(26) on a newly-reset() deck results in: 2-clubs, 41 | // 2-spades, 3-clubs, 3-spades ... A-diamonds, A-hearts. 42 | // 43 | // Note: if shuffle is called on a deck that has already had some 44 | // cards dealt, those cards should first be restored to the deck 45 | // in the order in which they were dealt, preserving the most 46 | // recent post-shuffled/post-reset state. After shuffling, the 47 | // next card to deal is the first one in the deck. 48 | 49 | Card deal(); 50 | // MODIFIES: this 51 | 52 | // EFFECTS: returns the next card to be dealt. If no cards 53 | // remain, throws an instance of DeckEmpty. 54 | 55 | int cardsLeft(); 56 | // EFFECTS: returns the number of cards in the deck that have not 57 | // been dealt since the last reset/shuffle. 58 | 59 | }; 60 | 61 | #endif /* __DECK_H__ */ 62 | -------------------------------------------------------------------------------- /p3/driver/hand.h: -------------------------------------------------------------------------------- 1 | #ifndef __HAND_H__ 2 | #define __HAND_H__ 3 | 4 | #include "card.h" 5 | 6 | struct HandValue { 7 | int count; // Value of hand 8 | bool soft; // true if hand value is a soft count 9 | }; 10 | 11 | class Hand { 12 | // OVERVIEW: A blackjack hand of zero or more cards 13 | // Note: this really is the only private state you need! 14 | HandValue curValue; 15 | public: 16 | Hand(); 17 | // EFFECTS: establishes an empty blackjack hand. 18 | 19 | void discardAll(); 20 | // MODIFIES: this 21 | // EFFECTS: discards any cards presently held, restoring the state 22 | // of the hand to that of an empty blackjack hand. 23 | 24 | void addCard(Card c); 25 | // MODIFIES: this 26 | // EFFECTS: adds the card "c" to those presently held. 27 | 28 | HandValue handValue() const; 29 | // EFFECTS: returns the present value of the blackjack hand. The 30 | // count field is the highest blackjack total possible without 31 | // going over 21. The soft field should be true if and only if at 32 | // least one ACE is present, and its value is counted as 11 rather 33 | // than 1. If the hand is over 21, just output the handvalue as what it is. 34 | // 35 | // Note: the const qualifier at the end of handValue means that 36 | // you are not allowed to change any member variables inside 37 | // handValue. It is required because Players only get const Hands 38 | // passed to them, and therefore can only call methods gauranteed 39 | // not to change the hand. 40 | 41 | // void displayHand() const; 42 | // MODIFIES: cout 43 | // EFFECTS: show curValue. 44 | }; 45 | 46 | #endif /* __HAND_H__ */ 47 | -------------------------------------------------------------------------------- /p3/driver/player.h: -------------------------------------------------------------------------------- 1 | #ifndef __PLAYER_H_ 2 | #define __PLAYER_H_ 3 | 4 | #include "hand.h" 5 | 6 | 7 | class Player { 8 | // A virtual base class, providing the player interface 9 | protected: 10 | Team team; // The team of each member, either SOS Brigade or Stardust Crusaders 11 | int ID; // The ID of each member. 12 | string name; // The full name of each member. 13 | 14 | public: 15 | virtual int bet(unsigned int bankroll, 16 | unsigned int minimum) = 0; 17 | // REQUIRES: bankroll >= minimum 18 | // EFFECTS: returns the player's bet, between minimum and bankroll 19 | // inclusive 20 | 21 | virtual bool draw(Card dealer, // Dealer's "up card" 22 | const Hand& player) = 0; // Player's current hand 23 | // EFFECTS: returns true if the player wishes to be dealt another 24 | // card, false otherwise. 25 | 26 | virtual void expose(Card c) = 0; 27 | // EFFECTS: allows the player to "see" the newly-exposed card. 28 | // For example, each card that is dealt "face up" is exposed, i.e. expose(). 29 | // Likewise, if the dealer must show his "hole card", it is also exposed, i.e. 30 | // expose(). Note: not all cards dealt are exposed, i.e. expose()---if the 31 | // player goes over 21 or is dealt a natural 21, the dealer need 32 | // not expose his hole card. 33 | 34 | virtual void shuffled() = 0; 35 | // EFFECTS: tells the player that the deck has been re-shuffled. 36 | 37 | virtual string getName(); 38 | // EFFECTS: get the name of the player. 39 | 40 | virtual int getID(); 41 | // EFFECTS: get the ID of the player. 42 | 43 | virtual Team getTeam(); 44 | // EFFECTS: get the team of the player. 45 | 46 | virtual void setPlayer(Team tm, int id); 47 | // EFFECTS: set a member's name, ID and team 48 | // MODIFIES: name, ID, team 49 | 50 | virtual ~Player() { } 51 | // Note: this is here only to suppress a compiler warning. 52 | // Destructors are not needed for this project. 53 | }; 54 | 55 | extern Player* get_Player(string& dealerSide, string& playerType, int& ID); 56 | // EFFECTS: get a pointer to a player. 57 | // "dealerSide" describes whether the dealer is from  58 | // SOS Brigade or Stardust Crusade. 59 | // This depends on the last program argument: [sos|sc]. 60 | // sc means the dealer team is Stardust Crusaders, 61 | // sos means the dealer team is SOS Brigade. 62 | // "playerType" describes whether Koizumi Itzuki and  63 | // Mohammed Avdol are simple player or count player. 64 | // This depends on the penultimate program argument: [simple|counting]. 65 | // If this argument is "simple", then Itzuki and Avdol are simple players. 66 | // If this argument is "counting", then Itzuki and Avdol are countingplayers. 67 | // "ID" is the player's ID. 68 | 69 | #endif /* __PLAYER_H__ */ 70 | -------------------------------------------------------------------------------- /p3/driver/rand.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | A C-program for MT19937, with initialization improved 2002/1/26. 3 | Coded by Takuji Nishimura and Makoto Matsumoto. 4 | 5 | Before using, initialize the state by using init_genrand(seed) 6 | or init_by_array(init_key, key_length). 7 | 8 | Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, 9 | All rights reserved. 10 | 11 | Redistribution and use in source and binary forms, with or without 12 | modification, are permitted provided that the following conditions 13 | are met: 14 | 15 | 1. Redistributions of source code must retain the above copyright 16 | notice, this list of conditions and the following disclaimer. 17 | 18 | 2. Redistributions in binary form must reproduce the above copyright 19 | notice, this list of conditions and the following disclaimer in the 20 | documentation and/or other materials provided with the distribution. 21 | 22 | 3. The names of its contributors may not be used to endorse or promote 23 | products derived from this software without specific prior written 24 | permission. 25 | 26 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 27 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 28 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 29 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 30 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 31 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 32 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 33 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 34 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 35 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 36 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 37 | 38 | 39 | Any feedback is very welcome. 40 | http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html 41 | email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space) 42 | */ 43 | 44 | /* 45 | * modified slightly by Brian Noble for use with EECS 280 project 4 Fall 2005. 46 | */ 47 | 48 | #include 49 | #include 50 | 51 | #include "rand.h" 52 | 53 | static int initialized = 0; 54 | 55 | /* Period parameters */ 56 | #define N 624 57 | #define M 397 58 | #define MATRIX_A 0x9908b0dfUL /* constant vector a */ 59 | #define UPPER_MASK 0x80000000UL /* most significant w-r bits */ 60 | #define LOWER_MASK 0x7fffffffUL /* least significant r bits */ 61 | 62 | static unsigned long mt[N]; /* the array for the state vector */ 63 | static int mti=N+1; /* mti==N+1 means mt[N] is not initialized */ 64 | 65 | /* initializes mt[N] with a seed */ 66 | void init_genrand(unsigned long s) 67 | { 68 | mt[0]= s & 0xffffffffUL; 69 | for (mti=1; mti> 30)) + mti); 72 | /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */ 73 | /* In the previous versions, MSBs of the seed affect */ 74 | /* only MSBs of the array mt[]. */ 75 | /* 2002/01/09 modified by Makoto Matsumoto */ 76 | mt[mti] &= 0xffffffffUL; 77 | /* for >32 bit machines */ 78 | } 79 | } 80 | 81 | /* initialize by an array with array-length */ 82 | /* init_key is the array for initializing keys */ 83 | /* key_length is its length */ 84 | /* slight change for C++, 2004/2/26 */ 85 | void init_by_array(unsigned long init_key[], int key_length) 86 | { 87 | int i, j, k; 88 | init_genrand(19650218UL); 89 | i=1; j=0; 90 | k = (N>key_length ? N : key_length); 91 | for (; k; k--) { 92 | mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1664525UL)) 93 | + init_key[j] + j; /* non linear */ 94 | mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */ 95 | i++; j++; 96 | if (i>=N) { mt[0] = mt[N-1]; i=1; } 97 | if (j>=key_length) j=0; 98 | } 99 | for (k=N-1; k; k--) { 100 | mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1566083941UL)) 101 | - i; /* non linear */ 102 | mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */ 103 | i++; 104 | if (i>=N) { mt[0] = mt[N-1]; i=1; } 105 | } 106 | 107 | mt[0] = 0x80000000UL; /* MSB is 1; assuring non-zero initial array */ 108 | } 109 | 110 | /* generates a random number on [0,0xffffffff]-interval */ 111 | unsigned long genrand_int32(void) 112 | { 113 | unsigned long y; 114 | static unsigned long mag01[2]={0x0UL, MATRIX_A}; 115 | /* mag01[x] = x * MATRIX_A for x=0,1 */ 116 | 117 | if (mti >= N) { /* generate N words at one time */ 118 | int kk; 119 | 120 | if (mti == N+1) /* if init_genrand() has not been called, */ 121 | init_genrand(5489UL); /* a default initial seed is used */ 122 | 123 | for (kk=0;kk> 1) ^ mag01[y & 0x1UL]; 126 | } 127 | for (;kk> 1) ^ mag01[y & 0x1UL]; 130 | } 131 | y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK); 132 | mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1UL]; 133 | 134 | mti = 0; 135 | } 136 | 137 | y = mt[mti++]; 138 | 139 | /* Tempering */ 140 | y ^= (y >> 11); 141 | y ^= (y << 7) & 0x9d2c5680UL; 142 | y ^= (y << 15) & 0xefc60000UL; 143 | y ^= (y >> 18); 144 | 145 | return y; 146 | } 147 | 148 | /* generates a random number on [0,0x7fffffff]-interval */ 149 | long genrand_int31(void) 150 | { 151 | return (long)(genrand_int32()>>1); 152 | } 153 | 154 | /* generates a random number on [0,1]-real-interval */ 155 | double genrand_real1(void) 156 | { 157 | return genrand_int32()*(1.0/4294967295.0); 158 | /* divided by 2^32-1 */ 159 | } 160 | 161 | /* generates a random number on [0,1)-real-interval */ 162 | double genrand_real2(void) 163 | { 164 | return genrand_int32()*(1.0/4294967296.0); 165 | /* divided by 2^32 */ 166 | } 167 | 168 | /* generates a random number on (0,1)-real-interval */ 169 | double genrand_real3(void) 170 | { 171 | return (((double)genrand_int32()) + 0.5)*(1.0/4294967296.0); 172 | /* divided by 2^32 */ 173 | } 174 | 175 | /* generates a random number on [0,1) with 53-bit resolution*/ 176 | double genrand_res53(void) 177 | { 178 | unsigned long a=genrand_int32()>>5, b=genrand_int32()>>6; 179 | return(a*67108864.0+b)*(1.0/9007199254740992.0); 180 | } 181 | /* These real versions are due to Isaku Wada, 2002/01/09 added */ 182 | 183 | int get_cut(void) 184 | { 185 | if (!initialized) { 186 | init_genrand(0); 187 | initialized = 1; 188 | } 189 | 190 | const int max = 39; 191 | const int min = 13; 192 | const int spread = max - min + 1; 193 | 194 | unsigned long r = genrand_int32(); 195 | int offset = r % spread; 196 | return min + offset; 197 | } 198 | -------------------------------------------------------------------------------- /p3/driver/rand.h: -------------------------------------------------------------------------------- 1 | #ifndef __RAND_H__ 2 | #define __RAND_H__ 3 | 4 | int get_cut(); 5 | // EFFECTS: returns a random number between 13 and 39 6 | 7 | #endif /* __RAND_H__ */ 8 | -------------------------------------------------------------------------------- /p4/README.md: -------------------------------------------------------------------------------- 1 | # Project 4 Grading Criteria 2 | 3 | 4 | ## Composition 5 | 1. Correctness: 80% 6 | 2. General coding style: 10% 7 | 3. `BinaryTree.cpp` check (number of lines in each function definition): 10% 8 | 9 | 10 | ## Correctness [80 points] 11 | The correctness score depends on how many test cases you pass on JOJ. 12 | 13 | 14 | ## Coding style [10 points] 15 | 16 | #### clang-check [5 points] 17 | * **Number of non-main functions** [1 point] 18 | 19 | Your program should be split into at least 3 non-main functions for *compress.cpp* and *decompress.cpp*. 20 | 21 | 1. 2 or more subroutines [1 point] 22 | 2. 1 subroutines [0.5 points] 23 | 3. 0 subroutine [0 points] 24 | 25 | * **Length of functions** [1 point] 26 | 27 | Your main functions should be no longer than 50 lines and non-main functions should be no more than 150 lines. 28 | 29 | 1. No long functions [1 point] 30 | 2. 1 long functions [0.5 points] 31 | 3. 2 or more long functions [0 points] 32 | 33 | * **Specification comments (REQUIRES, MODIFIES, EFFECTS)** [1.5 points] 34 | 35 | Your functions declarations should always contain specification comments. 36 | 37 | 1. All declarations are well specified [1.5 points] 38 | 2. 1 declaration are poorly specified [1 point] 39 | 3. 2 declarations are poorly specified [0.5 points] 40 | 4. 3 or more declarations are poorly specified [0 points] 41 | 42 | * **Body comments** [1.5 points] 43 | 44 | You should have your functions well commented. The length of function // the number of comments < 50. 45 | 46 | 1. All function are well commented [1.5 points] 47 | 2. 1 function are poorly commented [1 point] 48 | 3. 2 function are poorly commented [0.5 points] 49 | 4. 3 or more function are poorly commented [0 points] 50 | 51 | The total clang-check score will round **down** to the nearest integer, *i.e.* 3.5 will round down to 3. 52 | 53 | #### clang-tidy [5 points] 54 | * **Number of warning types** [3 points] 55 | 1. 0-5 types [3 points] 56 | 2. 5-10 types [2 points] 57 | 3. 10-25 types [1 point] 58 | 4. More than 25 types [0 points] 59 | 60 | * **Number of warnings** [2 points] 61 | 1. 0-2 warnings [2 points] 62 | 2. 2-5 warnings [1 point] 63 | 3. More than 5 warnings [0 points] 64 | 65 | See clang-tidy flags in https://github.com/ve280/code-check/blob/master/clang/tidy.py 66 | -------------------------------------------------------------------------------- /p4/codestyle.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import os 4 | import argparse 5 | from pprint import pprint 6 | 7 | import clang.check 8 | import clang.tidy 9 | import clang.format 10 | 11 | 12 | def main(project_dir, silent=False): 13 | # Formatting initialization 14 | main_cpp_files = ['compress.cpp', 'decompress.cpp'] 15 | limited_line_files = ['binaryTree.cpp'] 16 | files = ['binaryTree.cpp', 'compress.cpp', 'decompress.cpp'] 17 | format_dir = os.path.join(project_dir, 'formatted') 18 | 19 | # Clang check 20 | clang.format.generate_formatted_files(project_dir, format_dir, files, silent=silent) 21 | 22 | driver_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'driver') 23 | clang.utils.inject_driver(project_dir, driver_dir) 24 | clang.utils.inject_driver(format_dir, driver_dir) 25 | 26 | clang_check_score = 0 27 | subroutine_count = 0 28 | long_function_count = 0 29 | uncomment_prototype_cnt = 0 30 | poorly_commented_cnt = 0 31 | 32 | functions = clang.check.parse_functions_new(format_dir, main_cpp_files, silent=silent) 33 | clang.check.parse_comments(functions, silent=silent) 34 | for func_prototype, func in functions.items(): 35 | is_main_function = (func.name in ['main_{}'.format(_file) for _file in main_cpp_files]) 36 | # Checkpoint 1: Main function length 37 | # Requirement: Main function should be no longer than 50 physical lines. 38 | if is_main_function: 39 | if func.func_declarations[0].end - func.func_declarations[0].start >= 50: 40 | long_function_count += 1 41 | 42 | # Checkpoint 2: Non-main function amount 43 | # Requirement: Program should be split into at least 2 non-main functions. 44 | if not is_main_function and func.len >= 1: 45 | subroutine_count += 1 46 | 47 | # Checkpoint 3: Non-main function length and amount 48 | # Requirement: Non-main functions should be no longer than 100 physical lines. 49 | if not is_main_function and func.len >= 100: 50 | long_function_count += 1 51 | 52 | # Checkpoint 4: Function declaration comments (REQUIRES, MODIFIES, EFFECTS) 53 | # Requirement: All functions should have RME in their declaration. 54 | if not is_main_function and func.prototype_comments == 0: 55 | tolerance = ['Exception_t', 'bool', 'operator', 'static', 'inline'] 56 | flag = len(func.func_declarations) > 1 57 | for entity in tolerance: 58 | if func.name == entity: 59 | flag = False 60 | break 61 | if flag: 62 | print("{} is not commented under declaration.".format(func.name)) 63 | uncomment_prototype_cnt += 1 64 | 65 | # Checkpoint 5: Function body comments 66 | # Requirement: the length of function // the number of comments < 50. 67 | if func.body_comments == 0: 68 | if func.len >= 50: 69 | poorly_commented_cnt += 1 70 | elif func.len // func.body_comments >= 50: 71 | poorly_commented_cnt += 1 72 | 73 | clang_check_score += min(2, subroutine_count) 74 | clang_check_score += max(0, 2 - long_function_count) 75 | clang_check_score += max(0, 3 - uncomment_prototype_cnt) 76 | clang_check_score += max(0, 3 - poorly_commented_cnt) 77 | 78 | clang_check_score = clang_check_score // 2 79 | 80 | if not silent: 81 | print('\nsubroutines: %d, \nlong functions: %d, \nuncomment declarations: %d, \npoorly commented functions: %d' 82 | % (subroutine_count, long_function_count, uncomment_prototype_cnt, poorly_commented_cnt)) 83 | print('clang-check score: %d' % clang_check_score) 84 | 85 | clang_tidy_warnings, clang_tidy_warnings_count = clang.tidy.parse_warnings_new(project_dir, files, silent=silent) 86 | clang_tidy_score = 0 87 | 88 | # Checkpoint 6: Clang tidy 89 | # Requirement: Your program should be free of clang tidy problems. 90 | if clang_tidy_warnings_count <= 5: 91 | clang_tidy_score += 3 92 | elif clang_tidy_warnings_count <= 10: 93 | clang_tidy_score += 2 94 | elif clang_tidy_warnings_count <= 25: 95 | clang_tidy_score += 1 96 | 97 | if len(clang_tidy_warnings) <= 2: 98 | clang_tidy_score += 2 99 | elif len(clang_tidy_warnings) <= 5: 100 | clang_tidy_score += 1 101 | 102 | if not silent: 103 | pprint(clang_tidy_warnings) 104 | print('\nclang-tidy score: %d' % clang_tidy_score) 105 | print('\nTotal style score: %d' % (clang_tidy_score + clang_check_score)) 106 | 107 | # Line Check in BinaryTree methods 108 | line_check_count = 0 109 | 110 | line_check_functions = clang.check.parse_functions_new(format_dir, limited_line_files, silent=silent) 111 | for func_prototype, func in line_check_functions.items(): 112 | if not silent: 113 | print('{}: length {}'.format(func.name, func.len)) 114 | if func.len > 10: 115 | line_check_count += 1 116 | # weight of binary tree methods is 2 117 | line_check_score = max(0, 10 - 2 * line_check_count) 118 | if not silent: 119 | print('Line Check score: {}'.format(line_check_score)) 120 | if silent: 121 | print('{},{},{},{}'.format(clang_check_score + clang_tidy_score + line_check_score, 122 | clang_check_score, 123 | clang_tidy_score, 124 | line_check_score)) 125 | 126 | 127 | parser = argparse.ArgumentParser(description='Project 4 Code Checker.') 128 | parser.add_argument('--silent', action='store_true') 129 | parser.add_argument('project_dir', type=str, nargs=1) 130 | args = parser.parse_args() 131 | main(args.project_dir[0], silent=args.silent) 132 | -------------------------------------------------------------------------------- /p4/driver/binaryTree.h: -------------------------------------------------------------------------------- 1 | #ifndef P4_BINARYTREE_H 2 | #define P4_BINARYTREE_H 3 | 4 | #include 5 | 6 | class Node { 7 | // A node in a binary tree 8 | 9 | std::string str; 10 | int num; 11 | Node *left; // A pointer to the left child of this node. 12 | // If it does not exist, left should be nullptr 13 | Node *right; // A pointer to the right child of this node. 14 | // If it does not exist, right should be nullptr 15 | 16 | public: 17 | Node(const std::string &str, int num, Node *left = nullptr, Node *right = nullptr); 18 | // REQUIRES: The input left and right point to a dynamically allocated node object, 19 | // if not being NULL. 20 | // MODIFIES: this 21 | // EFFECTS: Construct a node with given input values. 22 | 23 | Node *leftSubtree() const; 24 | // EFFECTS: Return the pointer to the left child of the node. 25 | 26 | void setleft(Node *n); 27 | // MODIFIES: this 28 | // EFFECTS: set the left child of the node to be n. 29 | 30 | Node *rightSubtree() const; 31 | // EFFECTS: Return the pointer to the right child of the node. 32 | 33 | void setright(Node *n); 34 | // MODIFIES: this 35 | // EFFECTS: set the right child of the node to be n. 36 | 37 | std::string getstr() const; 38 | // EFFECTS: Return the “str” component of the node. 39 | 40 | int getnum() const; 41 | // EFFECTS: Return the “num” component of the node. 42 | 43 | void incnum(); 44 | // MODIFIES: this 45 | // EFFECTS: increment num by 1 46 | 47 | static Node *mergeNodes(Node *leftNode, Node *rightNode); 48 | // REQUIRES: leftNode and rightNode points to dynamically allocated node objects. 49 | // EFFECTS: Returns a pointer to a node with "str" being the result of appending 50 | // leftNode->str and rightNode->str, and "num" being leftNode->num + 51 | // rightNode->num. Also, Please allocate memory for this returned node 52 | // object. 53 | // 54 | // For example, if leftNode->str = "a", and rightNode->str = "b", then 55 | // the "str" of the merged node is "ab". 56 | }; 57 | 58 | class BinaryTree { 59 | // A binary tree object 60 | 61 | public: 62 | Node *root; // Root node of the binary tree 63 | 64 | BinaryTree(Node *rootNode = nullptr); 65 | // REQUIRES: The input rootNode points to a dynamically allocated node object, 66 | // if not being NULL. 67 | // MODIFIES: this 68 | // EFFECTS: Construct a binary tree with a root node. 69 | 70 | ~BinaryTree(); 71 | // MODIFIES: this 72 | // EFFECTS: Free all the memory allocated for this binary tree. 73 | 74 | std::string findPath(const std::string &s) const; 75 | // EFFECTS: Return the path from the root node to the node with the string s. 76 | // The path is encoded by a string only containing '0' and '1'. Each 77 | // character, from left to right, shows whether going left (encoded 78 | // by ‘0’) or right (encoded by ‘1’) from a node can lead to the 79 | // target node. 80 | // 81 | // For example, we want to find "g" in the following 82 | // tree (only the "str" components are shown): 83 | // 84 | // "a" 85 | // / \ 86 | // / \ 87 | // "b" "c" 88 | // / \ / \ 89 | // "d" "e" 90 | // / \ / \ 91 | // "f" "g" 92 | // / \ / \ 93 | // 94 | // The returned string should be "011". (Go left from "a", then go right from 95 | // "b", and finally go right from "d" can lead us to "g".) 96 | // 97 | // If s is in root node, then return an empty string. 98 | // If s is not in the tree, then return "-1". 99 | // You can assume that the "str" components of all the nodes are unique in a 100 | // tree. 101 | 102 | int sum() const; 103 | // EFFECTS: Returns the sum of "num" values of all nodes in the tree. If the tree is 104 | // empty, return 0. 105 | 106 | int depth() const; 107 | // EFFECTS: Returns the depth of the tree, which equals the number of layers of nodes 108 | // in the tree. Returns zero if the tree is empty. 109 | // 110 | // For example, the tree 111 | // 112 | // a 113 | // / \ 114 | // / \ 115 | // b c 116 | // / \ / \ 117 | // d e 118 | // / \ / \ 119 | // f g 120 | // / \ / \ 121 | // 122 | // has depth 4. 123 | // The node a is on the first layer. 124 | // The nodes b and c are on the second layer. 125 | // The nodes d and e are on the third layer. 126 | // The nodes f and g are on the fourth layer. 127 | 128 | void preorder_num() const; 129 | // EFFECTS: Prints the "num" component of each node using a pre-order traversal. 130 | // Seperate each "num" with a space. A pre-order traversal prints the 131 | // current node first, then recursively visit its left subtree, and then 132 | // recursively visit its right subtree and so on, until the right-most 133 | // element is printed. 134 | // 135 | // For any node, all the elements of its left subtree 136 | // are considered as on the left of that node and 137 | // all the elements of its right subtree are considered as 138 | // on the right of that node. 139 | // 140 | // For example, the tree: 141 | // 142 | // 4 143 | // / \ 144 | // / \ 145 | // 2 5 146 | // / \ 147 | // 7 3 148 | // / \ 149 | // 8 9 150 | // 151 | // would print 4 2 7 3 8 9 5 152 | // 153 | // An empty tree would print nothing. 154 | 155 | void inorder_str() const; 156 | // EFFECTS: Prints the "str" component of each node using an in-order traversal. 157 | // Separate each "str" with a space. An in-order traversal prints the 158 | // "left most" element first, then the second-left-most, and so on, 159 | // until the right-most element is printed. 160 | // 161 | // For any node, all the elements of its left subtree 162 | // are considered as on the left of that node and 163 | // all the elements of its right subtree are considered as 164 | // on the right of that node. 165 | // 166 | // For example, the tree: 167 | // 168 | // a 169 | // / \ 170 | // / \ 171 | // bb ddd 172 | // / \ 173 | // e c 174 | // 175 | // would print e bb c a ddd 176 | // 177 | // An empty tree would print nothing. 178 | 179 | void postorder_num() const; 180 | // EFFECTS: Prints the "num" component of each node using a post-order traversal. 181 | // Seperate each "num" with a space. A post-order traversal recursively 182 | // visit its left subtree, and then recursively visit its right subtree 183 | // and then print the current node. 184 | // 185 | // For any node, all the elements of its left subtree 186 | // are considered as on the left of that node and 187 | // all the elements of its right subtree are considered as 188 | // on the right of that node. 189 | // 190 | // For example, the tree: 191 | // 192 | // 4 193 | // / \ 194 | // / \ 195 | // 2 5 196 | // / \ 197 | // 7 3 198 | // / \ 199 | // 8 9 200 | // 201 | // would print 7 8 9 3 2 5 4 202 | // 203 | // An empty tree would print nothing. 204 | 205 | bool allPathSumGreater(int sum) const; 206 | // REQUIRES: The tree is not empty 207 | // 208 | // EFFECTS: Returns true if and only if for each root-to-leaf path of the tree, 209 | // the sum of "num" of all nodes along the path is greater than "sum". 210 | // 211 | // A root-to-leaf path is a sequence of nodes in a tree starting with 212 | // the root element and proceeding downward to a leaf (an element with 213 | // no children). 214 | // 215 | // For example, the tree (only the "num" components are shown): 216 | // 217 | // 4 218 | // / \ 219 | // / \ 220 | // 1 5 221 | // / \ / \ 222 | // 3 6 223 | // / \ / \ 224 | // 225 | // has three root-to-leaf paths: 4->1->3, 4->1->6 and 4->5. 226 | // Given the input sum = 9, the path 4->5 has the sum 9, so the function 227 | // should return false. If the input sum = 7, since all paths have the sums 228 | // greater than 7, the function should return true. 229 | 230 | bool covered_by(const BinaryTree &tree) const; 231 | // EFFECTS: Returns true if this tree is covered by the input binary tree "tree". 232 | // (only consider the "num" component) 233 | 234 | bool contained_by(const BinaryTree &tree) const; 235 | // EFFECTS: Returns true if this tree is contained by the input binary tree "tree". 236 | // (only consider the "num" component) 237 | 238 | BinaryTree copy() const; 239 | // EFFECTS: Returns a copy of this tree. Hint: use deep copy. 240 | }; 241 | 242 | 243 | #endif 244 | -------------------------------------------------------------------------------- /p4/driver/huffmanTree.cpp: -------------------------------------------------------------------------------- 1 | #include "huffmanTree.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace std; 8 | 9 | 10 | HuffmanTree::HuffmanTree(Node *rootNode) { 11 | this->root = rootNode; 12 | } 13 | 14 | 15 | static Node *filehelper(int pos, int row, vector > &vec) { 16 | string line; 17 | if (vec[row][pos][0] >= '0' && vec[row][pos][0] <= '9') { 18 | // will have child 19 | Node *left = filehelper(2 * pos, row + 1, vec); 20 | Node *right = filehelper(2 * pos + 1, row + 1, vec); 21 | return new Node("", stoi(vec[row][pos]), left, right); 22 | } else { 23 | if (vec[row][pos].size() == 2) return new Node("\n", 0); 24 | return new Node(vec[row][pos], 0); 25 | } 26 | } 27 | 28 | HuffmanTree::HuffmanTree(const string &treefile) { 29 | // Construct tree from file 30 | string line; 31 | vector > vec; 32 | ifstream ifile; 33 | ifile.open(treefile); 34 | while (getline(ifile, line)) { 35 | stringstream ss(line); 36 | vector v; 37 | while (getline(ss, line, ',')) { 38 | v.push_back(line); 39 | } 40 | vec.push_back(v); 41 | } 42 | ifile.close(); 43 | if (!vec.empty()) { 44 | root = filehelper(0, 0, vec); 45 | } 46 | } 47 | 48 | static void printTree_helper(Node *n, int depth, int targetDepth) { 49 | // Iterative deepening printer 50 | if (targetDepth > depth) { 51 | return; 52 | } else if (targetDepth == depth) { 53 | if (n->getstr().length() != 1) { 54 | cout << n->getnum() << ","; 55 | } else { 56 | if (n->getstr()=="\n") cout << "\\n,"; 57 | else cout << n->getstr() << ","; 58 | } 59 | return; 60 | } else { 61 | printTree_helper(n->leftSubtree(), depth - 1, targetDepth); 62 | printTree_helper(n->rightSubtree(), depth - 1, targetDepth); 63 | } 64 | } 65 | 66 | static void fill_nodes(Node *n, int depth) { 67 | // Fill the tree to be a full binary tree 68 | if (depth == 0) return; 69 | if (n->leftSubtree() == nullptr) 70 | n->setleft(new Node("-", 0, nullptr, nullptr)); 71 | if (n->rightSubtree() == nullptr) 72 | n->setright(new Node("-", 0, nullptr, nullptr)); 73 | fill_nodes(n->leftSubtree(), depth - 1); 74 | fill_nodes(n->rightSubtree(), depth - 1); 75 | } 76 | 77 | static Node *copy_node(Node *node) { 78 | if (!node) 79 | return nullptr; 80 | Node *copy = new Node(node->getstr(), node->getnum(), copy_node(node->leftSubtree()), 81 | copy_node(node->rightSubtree())); 82 | 83 | return copy; 84 | } 85 | 86 | void HuffmanTree::printTree() { 87 | Node *cpRoot = copy_node(this->root); 88 | HuffmanTree cp(cpRoot); 89 | int depth = this->depth(); 90 | fill_nodes(cp.root, depth - 1); 91 | 92 | // print 93 | for (int i = depth - 1; i >= 0; i--) { 94 | printTree_helper(cp.root, depth - 1, i); 95 | cout << endl; 96 | } 97 | } -------------------------------------------------------------------------------- /p4/driver/huffmanTree.h: -------------------------------------------------------------------------------- 1 | #ifndef P4_HUFFMANTREE_H 2 | #define P4_HUFFMANTREE_H 3 | 4 | #include "binaryTree.h" 5 | 6 | class HuffmanTree : public BinaryTree { 7 | // Huffman tree 8 | 9 | public: 10 | 11 | HuffmanTree(Node *rootNode = nullptr); 12 | // REQUIRES: The input rootNode points to a dynamically allocated 13 | // node object, if not, it is NULL. 14 | // MODIFIES: this 15 | // EFFECTS: Construct a huffman tree with a root node. 16 | 17 | HuffmanTree(const std::string &treefile); 18 | // MODIFIES: this 19 | // EFFECTS: Constructs a huffman tree from the treefile. (treefile 20 | // corresponds to the file name.) The treefile saves all the 21 | // node information needed for a huffman tree. You do not 22 | // need to understand the details of the implementation of 23 | // this function. 24 | // 25 | // In a huffman tree, all the leaf nodes contains information of 26 | // a character. You can find the encoding for each character just 27 | // by the path from root node to it. So only the "str" components 28 | // of leaf nodes need to be save in the treefile (we do not care 29 | // about its "num" when doing compression or decompression). For 30 | // nodes other than leaf nodes, they contain information about 31 | // frequencies. So we only care about the "num" components of 32 | // those nodes. Thus a huffman tree can be saved in a file like 33 | // the following example: 34 | // 35 | // 8, 36 | // a,4, 37 | // -,-,b,c, 38 | // 39 | // which represents the tree 40 | // 41 | // 8 42 | // / \ 43 | // a 4 44 | // / \ / \ 45 | // b c 46 | // / \ / \ 47 | // 48 | // Each '-' in the file means there is no node present in that position. 49 | // 50 | 51 | void printTree(); 52 | // EFFECTS: Prints the huffman tree following the format explained above. 53 | // You do not need to understand the details of the implementation 54 | // of this function. 55 | 56 | }; 57 | 58 | #endif -------------------------------------------------------------------------------- /p5/README.md: -------------------------------------------------------------------------------- 1 | # Project 5 Grading Criteria 2 | 3 | ## Composition 4 | 1. Correctness: 90% 5 | 2. Coding style: 10% 6 | 7 | 8 | ## Correctness [90 points] 9 | The correctness score depends on how many test cases you pass on JOJ. 10 | 11 | #### JOJ Test Cases 12 | There are in total 14 pretest cases and some hidden cases. 13 | 14 | 15 | ## Coding style [10 points] 16 | 17 | #### clang-check [3 points] 18 | * **Length of functions** [3 point] 19 | 20 | Your main functions should be no longer than 100 lines and non-main functions should be no more than 150 lines. 21 | 22 | 1. No long functions [3 point] 23 | 2. 1 long functions [2 points] 24 | 3. 2 long functions [1 points] 25 | 4. 3 or more long functions [0 points] 26 | 27 | #### clang-tidy [4 points] 28 | * **Number of warning types** [2 points] 29 | 1. 0-10 types [2 points] 30 | 2. 10-25 types [1 point] 31 | 3. More than 25 types [0 points] 32 | 33 | * **Number of warnings** [2 points] 34 | 1. 0-2 warnings [2 points] 35 | 2. 2-5 warnings [1 point] 36 | 3. More than 5 warnings [0 points] 37 | 38 | #### Header file usage check [3 points] 39 | 40 | Deduction is applied for usage of some header files that are not allowed. 41 | 42 | - One point deduction for each appearance of a header file that is not allowed 43 | - Allowed header file: 44 | - \ 45 | - \ 46 | - \ 47 | - \ 48 | - \ 49 | - \ 50 | 51 | 52 | See clang-tidy flags in https://github.com/ve280/code-check/blob/master/clang/tidy.py 53 | -------------------------------------------------------------------------------- /p5/clang/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ve280/code-check/8805623f25f625b231f01642746f7b45e3243a5f/p5/clang/__init__.py -------------------------------------------------------------------------------- /p5/clang/check.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | import shlex 4 | import subprocess 5 | 6 | from clang.utils import read_file, split_sources_headers, build_full_paths 7 | 8 | 9 | class FunctionDeclaration: 10 | function_declares = [] 11 | 12 | def __init__(self, line, file=''): 13 | self.file = file 14 | self.error = False 15 | 16 | # use regexp to parse the start and end line number 17 | function_range = re.findall(r"<(.*?)>", line) 18 | if not function_range: 19 | self.error = True 20 | return 21 | 22 | function_range = re.findall(r"[^l0-9]:(\d+)", function_range[0]) 23 | if len(function_range) < 1: 24 | self.error = True 25 | return 26 | self.start = int(function_range[0]) 27 | 28 | # maybe a one-line function (declaration) 29 | if len(function_range) == 1: 30 | self.end = self.start 31 | else: 32 | self.end = int(function_range[1]) 33 | 34 | # simply assume one line definition / declaration 35 | self.len = self.end - self.start 36 | 37 | # use a trick to split the line 38 | splitter = shlex.shlex(line, posix=True) 39 | splitter.whitespace += ',' 40 | splitter.whitespace_split = True 41 | args = list(splitter) 42 | if len(args) < 2: 43 | self.error = True 44 | return 45 | if args[-1] == 'static': 46 | self.static = True 47 | if len(args) < 3: 48 | self.error = True 49 | return 50 | self.name = args[-3] 51 | splitter = shlex.shlex(args[-2], posix=True) 52 | else: 53 | self.static = False 54 | self.name = args[-2] 55 | splitter = shlex.shlex(args[-1], posix=True) 56 | 57 | if self.name == 'main': 58 | self.name += '__' + file 59 | 60 | # use another trick to split the args 61 | splitter.whitespace = ',()' 62 | splitter.whitespace_split = True 63 | args = list(splitter) 64 | if len(args) < 1: 65 | self.error = True 66 | return 67 | self.ret_type = args[0].strip() 68 | self.args_type = list(map(lambda x: x.strip(), args[1:])) 69 | self.id = len(FunctionDeclaration.function_declares) 70 | self.body = [] 71 | FunctionDeclaration.function_declares.append(self) 72 | 73 | def calculate_length(self, lines): 74 | self.len = 0 75 | block_comment = False 76 | for line in lines: 77 | line = line.strip() 78 | if len(line) == 0: 79 | continue 80 | left_block = len(re.findall(r'/\*', line)) 81 | right_block = len(re.findall(r'\*/', line)) 82 | if left_block > right_block: 83 | if not line.startswith('/*'): 84 | self.len += 1 85 | block_comment = True 86 | elif left_block < right_block: 87 | if not line.endswith('*/'): 88 | self.len += 1 89 | block_comment = False 90 | elif block_comment or line.startswith('//'): 91 | continue 92 | else: 93 | self.len += 1 94 | self.len = max(0, self.len - 1) 95 | 96 | def set_body(self, lines): 97 | self.body = lines 98 | 99 | def __str__(self): 100 | prefix = self.static and 'static ' or '' 101 | return '%s%s %s(%s)' % (prefix, self.ret_type, self.name, ', '.join(self.args_type)) 102 | 103 | @staticmethod 104 | def get_by_id(_id): 105 | return FunctionDeclaration.function_declares[_id] 106 | 107 | 108 | class Function: 109 | def __init__(self, func_decl): 110 | self.func_declarations = [func_decl] 111 | self.prototype_comments = 0 112 | self.body_comments = 0 113 | self.name = func_decl.name 114 | self.prototype = str(func_decl) 115 | self.len = 0 116 | 117 | def add_declaration(self, func_decl): 118 | self.func_declarations.append(func_decl) 119 | 120 | def calculate_length(self): 121 | self.len = 0 122 | for func_decl in self.func_declarations: 123 | self.len += func_decl.len 124 | 125 | def analyze_comments(self): 126 | self.prototype_comments = 0 127 | self.body_comments = 0 128 | 129 | def add_comment(line, is_prototype): 130 | if not block_comment: 131 | line = ''.join(re.findall(r'//(.+)', line, re.DOTALL)) 132 | line = re.sub(r'[/*\s]', '', line) 133 | if len(line) > 5: 134 | if is_prototype: 135 | self.prototype_comments += 1 136 | else: 137 | self.body_comments += 1 138 | 139 | for func_decl in self.func_declarations: 140 | is_prototype = func_decl.start == func_decl.end 141 | block_comment = False 142 | for i, line in func_decl.body: 143 | line = line.strip() 144 | if len(line) == 0: 145 | continue 146 | left_block = len(re.findall(r'/\*', line)) 147 | right_block = len(re.findall(r'\*/', line)) 148 | if left_block > right_block: 149 | block_comment = True 150 | add_comment(line, is_prototype) 151 | elif left_block < right_block: 152 | add_comment(line, is_prototype) 153 | block_comment = False 154 | elif block_comment: 155 | add_comment(line, is_prototype) 156 | elif left_block == right_block and left_block > 0: 157 | block_comment = True 158 | add_comment(line, is_prototype) 159 | block_comment = False 160 | elif '//' in line: 161 | line = line[line.find('//'):] 162 | add_comment(line, is_prototype) 163 | 164 | def __str__(self): 165 | return self.prototype 166 | 167 | 168 | def parse_functions_new(project_dir, files, silent=False, functions=None): 169 | if not functions: 170 | functions = dict() 171 | 172 | split_sources_headers(files) 173 | sources, headers, _ = split_sources_headers(files) 174 | files = sources + headers 175 | sources_path = build_full_paths(project_dir, sources) 176 | files_path = build_full_paths(project_dir, files) 177 | p = subprocess.Popen("clang-check -ast-dump %s --extra-arg='-fno-color-diagnostics' --" 178 | % ' '.join(sources_path), shell=True, stdout=subprocess.PIPE, 179 | stderr=silent and subprocess.PIPE or None) 180 | 181 | current_file = None 182 | 183 | if not silent: 184 | print('\nparsing function declarations:') 185 | 186 | func_decl_lines = [] 187 | 188 | while p.poll() is None: 189 | line = p.stdout.readline().decode('utf-8').strip() 190 | result = re.findall(r'<(?!(line|col))(.*?), (line|col):.*?>', line) 191 | if result: 192 | file_name = result[0][1] 193 | flag = False 194 | for i, file_path in enumerate(files_path): 195 | if file_path in file_name: 196 | current_file = files[i] 197 | flag = True 198 | break 199 | if not flag: 200 | current_file = None 201 | 202 | decl = 'FunctionDecl' in line \ 203 | or 'CXXConstructorDecl' in line \ 204 | or 'CXXDestructorDecl' in line \ 205 | or 'CXXMethodDecl' in line 206 | if current_file and ' default ' not in line and decl: 207 | line = line.strip() 208 | func_decl_lines.append((line, current_file)) 209 | 210 | file_func_decls = {x: [] for x in files} 211 | 212 | for line, file in func_decl_lines: 213 | func_decl = FunctionDeclaration(line, file) 214 | if not func_decl.error: 215 | func_prototype = str(func_decl) 216 | if not silent: 217 | print('[%s:%d-%d] %s' % (func_decl.file, func_decl.start, func_decl.end, func_decl)) 218 | if func_prototype not in functions: 219 | functions[func_prototype] = Function(func_decl) 220 | else: 221 | functions[func_prototype].add_declaration(func_decl) 222 | file_func_decls[file].append(func_decl) 223 | elif not silent: 224 | print('error occurred in %s' % line) 225 | 226 | if not silent: 227 | print('\nparsing cpp files:') 228 | 229 | for i, file in enumerate(files): 230 | try: 231 | file_contents = read_file(files_path[i], silent=silent) 232 | func_decls = sorted(file_func_decls[file], key=lambda x: x.start) 233 | for j, func_decl in enumerate(func_decls): 234 | if func_decl.end <= len(file_contents): 235 | """ 236 | if j > 0: 237 | start = max(func_decls[j - 1].end, func_decl.start - 20) 238 | else: 239 | start = max(0, func_decl.start - 20) 240 | end = min(func_decl.end, len(file_contents) - 1) 241 | """ 242 | start = func_decl.start - 2 243 | end = func_decl.end 244 | while file_contents[start].startswith('/') or "*" in file_contents[start]: 245 | if start == 0: 246 | break 247 | start -= 1 248 | while file_contents[end].startswith('/') or "*" in file_contents[start]: 249 | if end == len(file_contents) - 1: 250 | break 251 | end += 1 252 | func_decl.calculate_length(file_contents[func_decl.start:end + 1]) 253 | func_decl.set_body([(x, file_contents[x]) for x in range(start, end + 1)]) 254 | except Exception as e: 255 | if not silent: 256 | print(e.args) 257 | 258 | for function in functions.values(): 259 | function.calculate_length() 260 | 261 | return functions 262 | 263 | 264 | def parse_functions(main_cpp_name, main_cpp_path, silent=False): 265 | p = subprocess.Popen("clang-check -ast-dump %s --extra-arg='-fno-color-diagnostics' --" 266 | % main_cpp_path, shell=True, stdout=subprocess.PIPE) 267 | main_cpp_found = False 268 | functions = {} 269 | 270 | if not silent: 271 | print('\nparsing function declarations:') 272 | while p.poll() is None: 273 | line = p.stdout.readline().decode('utf-8') 274 | if main_cpp_name in line: 275 | main_cpp_found = True 276 | if main_cpp_found and 'FunctionDecl' in line and 'line' in line: 277 | line = line.strip() 278 | func_decl = FunctionDeclaration(line) 279 | if not func_decl.error: 280 | func_prototype = str(func_decl) 281 | if not silent: 282 | print('[line %d-%d] %s' % (func_decl.start, func_decl.end, func_decl)) 283 | if func_prototype not in functions: 284 | functions[func_prototype] = Function(func_decl) 285 | else: 286 | functions[func_prototype].add_declaration(func_decl) 287 | elif not silent: 288 | print('error occurred in %s', line) 289 | 290 | if not silent: 291 | print('\nparsing cpp file:') 292 | 293 | main_cpp_contents = read_file(main_cpp_path, silent=silent) 294 | 295 | for i, func_decl in enumerate(FunctionDeclaration.function_declares): 296 | if func_decl.end <= len(main_cpp_contents): 297 | if i > 1: 298 | start = max(FunctionDeclaration.get_by_id(i - 1).end, func_decl.start - 20) 299 | else: 300 | start = max(0, func_decl.start - 20) 301 | end = func_decl.end 302 | func_decl.set_body([(x, main_cpp_contents[x]) for x in range(start, end)]) 303 | 304 | return functions 305 | 306 | 307 | def parse_comments(functions, silent=False): 308 | if not silent: 309 | print('\nparsing function comments:') 310 | for func_prototype, func in functions.items(): 311 | if func.name == "inline": 312 | continue 313 | func.analyze_comments() 314 | if not silent: 315 | print(func) 316 | print('declarations: %d, body length: %d, prototype comments: %d, body comments: %d' 317 | % (len(func.func_declarations), func.len, func.prototype_comments, func.body_comments)) 318 | -------------------------------------------------------------------------------- /p5/clang/format.py: -------------------------------------------------------------------------------- 1 | import os 2 | import subprocess 3 | import time 4 | 5 | 6 | def generate_formatted_files(project_dir, target_dir, files, silent=False): 7 | if not silent: 8 | print('reformatting files:') 9 | 10 | os.makedirs(target_dir, exist_ok=True) 11 | for file in files: 12 | input_file = os.path.join(project_dir, file) 13 | output_file = os.path.join(target_dir, file) 14 | if not silent: 15 | print('%s => %s' % (input_file, output_file)) 16 | with open(output_file, 'wb') as f: 17 | p = subprocess.run("clang-format -style=WebKit %s" 18 | % input_file, shell=True, stdout=f, 19 | stderr=silent and subprocess.PIPE or None) 20 | # while p.poll() is None: 21 | # time.sleep(0.001) 22 | 23 | if not silent: 24 | print('') 25 | 26 | # generate_formatted_files('/home/liu/SJTU/code-check/solution', '/home/liu/SJTU/code-check/solution/formatted', 27 | # ['world_type.h', 'simulation.cpp', 'p3.cpp', 'simulation.h']) 28 | -------------------------------------------------------------------------------- /p5/clang/tidy.py: -------------------------------------------------------------------------------- 1 | import re 2 | import subprocess 3 | import json 4 | 5 | from clang.utils import split_sources_headers, build_full_paths 6 | 7 | # ported from CLion, remove modernize-*, cert-*, hicpp-*, cppcoreguidelines-* 8 | clang_tidy_checks = {'Checks': ','.join([ 9 | "*", 10 | "-android-*", 11 | "-bugprone-bool-pointer-implicit-conversion", 12 | "-bugprone-exception-escape", 13 | "-cert-*", 14 | "-cppcoreguidelines-*", 15 | "-fuchsia-*", 16 | "-google-*", 17 | "google-default-arguments", 18 | "google-explicit-constructor", 19 | "google-runtime-operator", 20 | "-hicpp-*", 21 | "-llvm-*", 22 | "-objc-*", 23 | "-readability-else-after-return", 24 | "-readability-implicit-bool-conversion", 25 | "-readability-magic-numbers", 26 | "-readability-named-parameter", 27 | "-readability-simplify-boolean-expr", 28 | "-readability-braces-around-statements", 29 | "-readability-identifier-naming", 30 | "-readability-function-size", 31 | "-readability-redundant-member-init", 32 | "-readability-isolate-declaration", 33 | "-readability-redundant-control-flow," 34 | "-misc-bool-pointer-implicit-conversion", 35 | "-misc-definitions-in-headers", 36 | "-misc-unused-alias-decls", 37 | "-misc-unused-parameters", 38 | "-misc-unused-using-decls", 39 | "-modernize-*", 40 | "-clang-diagnostic-*", 41 | "-clang-analyzer-*", 42 | "-zircon-*", 43 | ]), 'CheckOptions': [{ 44 | 'key': 'misc-throw-by-value-catch-by-reference.CheckThrowTemporaries', 45 | 'value': '0' 46 | }]} 47 | 48 | # print(json.dumps(clang_tidy_checks)) 49 | 50 | 51 | def parse_warnings_new(project_dir, files, silent=False): 52 | split_sources_headers(files) 53 | sources, headers, _ = split_sources_headers(files) 54 | sources_path = build_full_paths(project_dir, sources) 55 | p = subprocess.Popen("clang-tidy %s -config='%s' --extra-arg='-fno-color-diagnostics' --" 56 | % (' '.join(sources_path), json.dumps(clang_tidy_checks)), 57 | shell=True, stdout=subprocess.PIPE, stderr=silent and subprocess.PIPE or None) 58 | 59 | warnings = {} 60 | warnings_count = 0 61 | 62 | if not silent: 63 | print('\nparsing clang-tidy results:') 64 | print('Ignore the following warnings, if they are suspended.') 65 | while p.poll() is None: 66 | line = p.stdout.readline().decode('utf-8').strip() 67 | res = re.findall(r'warning:.*?\[(.*?)\]', line) 68 | if res: 69 | if res[0] in warnings: 70 | warnings[res[0]] += 1 71 | else: 72 | warnings[res[0]] = 1 73 | warnings_count += 1 74 | 75 | return warnings, warnings_count 76 | 77 | 78 | def parse_warnings(main_cpp_path, silent=False): 79 | p = subprocess.Popen("clang-tidy %s -checks=%s --extra-arg='-fno-color-diagnostics' --" 80 | % (main_cpp_path, clang_tidy_checks), 81 | shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 82 | 83 | warnings = {} 84 | warnings_count = 0 85 | 86 | if not silent: 87 | print('\nparsing clang-tidy results:') 88 | while p.poll() is None: 89 | line = p.stdout.readline().decode('utf-8').strip() 90 | res = re.findall(r'warning:.*?\[(.*?)\]', line) 91 | if res: 92 | if res[0] in warnings: 93 | warnings[res[0]] += 1 94 | else: 95 | warnings[res[0]] = 1 96 | warnings_count += 1 97 | 98 | return warnings, warnings_count 99 | -------------------------------------------------------------------------------- /p5/clang/utils.py: -------------------------------------------------------------------------------- 1 | import chardet 2 | import re 3 | import os 4 | import shutil 5 | 6 | 7 | def read_file(file_path, silent=False): 8 | with open(file_path, 'rb') as file: 9 | bytes_str = file.read() 10 | charset = chardet.detect(bytes_str)['encoding'] or 'utf-8' 11 | if not silent: 12 | print('%s: encoding: %s' % (file_path, charset)) 13 | return bytes_str.decode(charset).split('\n') 14 | 15 | 16 | def split_sources_headers(files): 17 | headers = [] 18 | sources = [] 19 | other = [] 20 | for file in files: 21 | if re.findall(r'\.(h|hpp|hh|H|hxx|h\+\+)$', file): 22 | headers.append(file) 23 | elif re.findall(r'\.(c|cpp|cc|C|cxx|c\+\+)$', file): 24 | sources.append(file) 25 | else: 26 | other.append(file) 27 | return sources, headers, other 28 | 29 | 30 | def build_full_paths(project_dir, files): 31 | return list(map(lambda x: os.path.join(project_dir, x), files)) 32 | 33 | 34 | def inject_driver(project_dir, driver_dir): 35 | if os.path.isdir(project_dir) and os.path.isdir(driver_dir): 36 | for file in os.listdir(driver_dir): 37 | shutil.copy2(os.path.join(driver_dir, file), os.path.join(project_dir, file)) 38 | 39 | 40 | def count_not_allowed_headers(project_dir, files, allowed_headers, silent=False): 41 | not_allowed_usage_count = 0 42 | for file in files: 43 | absolute_path = os.path.join(project_dir, file) 44 | with open(absolute_path, 'r', encoding='unicode_escape') as source_file: 45 | lines = source_file.readlines() 46 | for line in lines: 47 | for usage in re.findall(r'\s*#\s*include\s+<\s*[a-z]+\s*>\s*', line): 48 | remove_right = re.split(r'\s*>\s*', usage)[0] 49 | header_name = re.sub(r'\s*#\s*include\s+<\s*', '', remove_right) 50 | if header_name not in allowed_headers: 51 | not_allowed_usage_count += 1 52 | if not silent: 53 | print('Found not allowed header file at:\n\n{}\nin {}'.format(usage, file)) 54 | return not_allowed_usage_count 55 | -------------------------------------------------------------------------------- /p5/codestyle.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | 4 | import os 5 | import argparse 6 | from pprint import pprint 7 | import re 8 | import clang.check 9 | import clang.tidy 10 | import clang.format 11 | import clang.utils 12 | 13 | def main(project_dir, silent=False): 14 | # Formatting initialization 15 | files = ['DlistImpl.h', 'sam.cpp'] 16 | #files = ['deck.h', 'deck.cpp', 'hand.h', 'hand.cpp', 'player.h', 'player.cpp', 'blackjack.cpp'] 17 | format_dir = os.path.join(project_dir, 'formatted') 18 | main_cpp_name = 'sam.cpp' 19 | main_cpp_path = os.path.join(project_dir, main_cpp_name) 20 | 21 | # Clang checkings 22 | clang.format.generate_formatted_files(project_dir, format_dir, files, silent=silent) 23 | clang_check_score = 0 24 | subroutine_count = 0 25 | long_function_count = 0 26 | uncomment_prototype_cnt = 0 27 | poorly_commented_cnt = 0 28 | 29 | # Checkpoint 1: Main function length 30 | # Requirement: Main function should be no longer than 100 physical lines. 31 | main_function = clang.check.parse_functions(main_cpp_name, main_cpp_path, silent=silent) 32 | for func_prototype, func in main_function.items(): 33 | if func.func_declarations[0].end - func.func_declarations[0].start >= 100: 34 | long_function_count += 1 35 | 36 | functions = clang.check.parse_functions_new(format_dir, files, silent=silent) 37 | clang.check.parse_comments(functions, silent=silent) 38 | for func_prototype, func in functions.items(): 39 | # Checkpoint 2: Non-main function amount 40 | # Requirement: Program should be split into at least 6 non-main functions. 41 | if func.name != 'main' + main_cpp_name and func.len >= 1: 42 | subroutine_count += 1 43 | 44 | # Checkpoint 3: Non-main function length and amount 45 | # Requirement: Non-main functions should be no longer than 150 physical lines. 46 | if func.name != 'main' + main_cpp_name and func.len >= 150: 47 | long_function_count += 1 48 | 49 | # # Checkpoint 4: Function declaration comments (REQUIRES, MODIFIES, EFFECTS) 50 | # # Requirement: All functions should have RME in their declaration. 51 | # if func.name != 'main' + main_cpp_name and func.prototype_comments == 0: 52 | # tolerance = ['Exception_t', 'bool', 'operator', 'static', 'inline'] 53 | # flag = len(func.func_declarations)>1 54 | # for entity in tolerance: 55 | # if func.name == entity: 56 | # flag = False 57 | # break 58 | # if flag and (not silent): 59 | # print("{} is not commented under declaration.".format(func.name)) 60 | # uncomment_prototype_cnt += 1 61 | 62 | # # Checkpoint 5: Function body comments 63 | # # Requirement: the length of function // the number of comments < 50. 64 | # if func.body_comments == 0: 65 | # if func.len >= 50: 66 | # poorly_commented_cnt += 1 67 | # elif func.len // func.body_comments >= 50: 68 | # poorly_commented_cnt += 1 69 | 70 | # clang_check_score += min(2, subroutine_count // 3) 71 | clang_check_score += max(0, 6 - long_function_count * 2) 72 | # clang_check_score += max(0, 3 - uncomment_prototype_cnt) 73 | # clang_check_score += max(0, 3 - poorly_commented_cnt) 74 | clang_check_score //= 2 75 | 76 | if not silent: 77 | print('\nsubroutines: %d, \nlong functions: %d, \nuncomment declarations: %d, \npoorly commented functions: %d' 78 | % (subroutine_count, long_function_count, uncomment_prototype_cnt, poorly_commented_cnt)) 79 | print('clang-check score: %d' % clang_check_score) 80 | 81 | clang_tidy_warnings, clang_tidy_warnings_count = clang.tidy.parse_warnings_new(project_dir, files, silent=silent) 82 | clang_tidy_score = 0 83 | 84 | # Checkpoint 6: Clang tidies 85 | # Requirement: Your program should be free of clang tidies. 86 | # if clang_tidy_warnings_count <= 5: 87 | # clang_tidy_score += 3 88 | if clang_tidy_warnings_count <= 10: 89 | clang_tidy_score += 2 90 | elif clang_tidy_warnings_count <= 25: 91 | clang_tidy_score += 1 92 | 93 | if len(clang_tidy_warnings) <= 2: 94 | clang_tidy_score += 2 95 | elif len(clang_tidy_warnings) <= 5: 96 | clang_tidy_score += 1 97 | 98 | 99 | # header usage check 100 | allowed_header_names = ['iostream', 'string', 'sstream', 'cstdlib', 'cassert', 'algorithm'] 101 | not_allowed_usage_count = clang.utils.count_not_allowed_headers(project_dir, files, allowed_header_names, silent) 102 | header_usage_score = max(0, 3 - not_allowed_usage_count) 103 | 104 | 105 | if not silent: 106 | pprint(clang_tidy_warnings) 107 | print('\nclang-tidy score: %d' % clang_tidy_score) 108 | print('\nheader usage score: %d' % header_usage_score) 109 | print('\nTotal style score: %d' % (clang_tidy_score + clang_check_score + header_usage_score)) 110 | 111 | if silent: 112 | print('%d,%d,%d' % (clang_check_score, clang_tidy_score, header_usage_score)) 113 | 114 | 115 | parser = argparse.ArgumentParser(description='Project 5 Code Checker.') 116 | parser.add_argument('--silent', action='store_true') 117 | parser.add_argument('project_dir', type=str, nargs=1) 118 | args = parser.parse_args() 119 | main(args.project_dir[0], silent=args.silent) 120 | -------------------------------------------------------------------------------- /p5/driver/Dlist.h: -------------------------------------------------------------------------------- 1 | #ifndef __DLIST_H__ 2 | #define __DLIST_H__ 3 | 4 | class EmptyList { 5 | // OVERVIEW: an exception class 6 | }; 7 | 8 | template 9 | class Dlist { 10 | // OVERVIEW: contains a double-ended list of Objects 11 | 12 | public: 13 | // Operational methods 14 | 15 | bool isEmpty() const; 16 | // EFFECTS: returns true if list is empy, false otherwise 17 | 18 | void insertFront(T *op); 19 | // MODIFIES this 20 | // EFFECTS inserts o at the front of the list 21 | 22 | void insertBack(T *op); 23 | // MODIFIES this 24 | // EFFECTS inserts o at the back of the list 25 | 26 | T *removeFront(); 27 | // MODIFIES this 28 | // EFFECTS removes and returns first object from non-empty list 29 | // throws an instance of EmptyList if empty 30 | 31 | T *removeBack(); 32 | // MODIFIES this 33 | // EFFECTS removes and returns last object from non-empty list 34 | // throws an instance of EmptyList if empty 35 | 36 | // This method is provided for part 2. Just skip it. 37 | friend std::ostream& operator<< (std::ostream& os, const Dlist& dl) { 38 | for(auto i = dl.first; i != NULL ; i = i->next) { 39 | os << (*(i->op)) << ' '; 40 | } 41 | return os; 42 | } 43 | 44 | // Maintenance methods 45 | Dlist(); // constructor 46 | Dlist(const Dlist &l); // copy constructor 47 | Dlist &operator=(const Dlist &l); // assignment operator 48 | ~Dlist(); // destructor 49 | 50 | private: 51 | // A private type 52 | struct node { 53 | node *next; // (NULL if none) 54 | node *prev; // (NULL if none) 55 | T *op; 56 | }; 57 | 58 | node *first; // The pointer to the first node (NULL if none) 59 | node *last; // The pointer to the last node (NULL if none) 60 | 61 | // Utility methods 62 | 63 | void removeAll(); 64 | // EFFECT: called by destructor/operator= to remove and destroy 65 | // all list elements 66 | 67 | void copyAll(const Dlist &l); 68 | // EFFECT: called by copy constructor/operator= to copy elements 69 | // from a source instance l to this instance 70 | }; 71 | 72 | /* 73 | Note: as we have shown in the lecture, for template, we also need 74 | to include the method implementation in the .h file. For this 75 | purpose, we include dlist_impl.h below. Please provide the method 76 | implementation in this file. 77 | */ 78 | 79 | 80 | #include "DlistImpl.h" 81 | 82 | #endif /* __DLIST_H__ */ 83 | -------------------------------------------------------------------------------- /p5/driver/Instr.h: -------------------------------------------------------------------------------- 1 | #ifndef INSTR_H 2 | #define INSTR_H 3 | 4 | #include 5 | #include 6 | 7 | enum class InstrName {ADD, NOR, IFZ, HALT, LOAD, STORE, POP, PUSHI, NOOP}; 8 | 9 | const std::string instrPrint[] = {"ADD", "NOR", "IFZ", "HALT", "LOAD", "STORE", "POP", "PUSHI", "NOOP"}; 10 | 11 | struct Instr { 12 | InstrName name; 13 | int parameter; 14 | friend std::ostream& operator<< (std::ostream& os, const Instr& it) { 15 | os << instrPrint[static_cast(it.name)]; 16 | if (it.name == InstrName::PUSHI || it.name == InstrName::IFZ) { 17 | os << " " << it.parameter; 18 | } 19 | return os; 20 | } 21 | }; 22 | 23 | std::istream& operator>>(std::istream& is, Instr& it) 24 | { 25 | std::string s; 26 | is >> s; 27 | auto ptr = std::find(instrPrint, instrPrint + 9, s); 28 | it.name = static_cast(ptr - instrPrint); 29 | if (it.name == InstrName::PUSHI || it.name == InstrName::IFZ) { 30 | is >> it.parameter; 31 | } 32 | return is; 33 | } 34 | 35 | #endif -------------------------------------------------------------------------------- /preprocess/uncompress.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import tarfile 4 | import zipfile 5 | import rarfile 6 | import os 7 | import tempfile 8 | import sys 9 | 10 | 11 | def unzip(file, dest): 12 | print('unzip %s in %s' % (file, dest)) 13 | if zipfile.is_zipfile(file): 14 | zf = zipfile.ZipFile(file, 'r') 15 | zf.extractall(dest) 16 | chmod(dest, 0o644) 17 | return 0 18 | else: 19 | print('failed', file=sys.stderr) 20 | return 1 21 | 22 | 23 | def untar(file, dest): 24 | print('untar %s in %s' % (file, dest)) 25 | if tarfile.is_tarfile(file): 26 | tf = tarfile.open(file, 'r') 27 | tf.extractall(dest) 28 | chmod(dest, 0o644) 29 | return 0 30 | else: 31 | print('failed', file=sys.stderr) 32 | return 1 33 | 34 | 35 | def unrar(file, dest): 36 | print('unrar %s in %s' % (file, dest)) 37 | if rarfile.is_rarfile(file): 38 | rf = rarfile.RarFile(file, 'r') 39 | rf.extractall(dest) 40 | chmod(dest, 0o644) 41 | return 0 42 | else: 43 | print('failed', file=sys.stderr) 44 | return 1 45 | 46 | 47 | def chmod(directory, mode): 48 | for root, dirs, files in os.walk(directory, topdown=False): 49 | for name in files: 50 | os.chmod(os.path.join(root, name), mode=mode) 51 | for name in dirs: 52 | os.chmod(os.path.join(root, name), mode=mode) 53 | 54 | 55 | def main(file): 56 | filename, _ = os.path.splitext(file) 57 | with tempfile.TemporaryDirectory() as tempdir: 58 | if unzip(file, tempdir) == 0: 59 | error = 0 60 | success = 0 61 | for file in os.listdir(tempdir): 62 | record_file = os.path.join(tempdir, file) 63 | record_filename, record_ext = os.path.splitext(file) 64 | record_dest = os.path.join(filename, record_filename) 65 | if record_ext == '.zip': 66 | if unzip(record_file, record_dest): 67 | error += 1 68 | else: 69 | success += 1 70 | elif record_ext == '.tar': 71 | if untar(record_file, record_dest): 72 | error += 1 73 | else: 74 | success += 1 75 | elif record_ext == '.rar': 76 | if unrar(record_file, record_dest): 77 | error += 1 78 | else: 79 | success += 1 80 | else: 81 | print('extension not supported', file=sys.stderr) 82 | print('finished, with %d success(es) and %d error(s)' % (success, error)) 83 | 84 | 85 | if len(sys.argv) > 1: 86 | main(sys.argv[1]) 87 | else: 88 | print('usage: uncompress.py [filename]', file=sys.stderr) 89 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | chardet 2 | rarfile 3 | networkx 4 | --------------------------------------------------------------------------------