【Cコード解析】コード整形まとめ(備忘録)

## 今まで書いた記事を1つのスクリプトにまとめる。
下記の記事で実践したことをまとめる。

https://qiita.com/name_ko/items/207693c45bc5bf282b36

https://qiita.com/name_ko/items/a2514c8ff16d5c8be18c

https://qiita.com/name_ko/items/b336f1b5fc495f1ab377

https://qiita.com/name_ko/items/c014fd67bd6d146a73cd

https://qiita.com/name_ko/items/8e92f24576a75d39a8eb

## スクリプト
``` python:main.py
import os
import shutil

import service.config as config
import service.anti_multiple_variable_declaration as AMVD
import service.anti_unused_variable as AUV
import service.anti_nothing_elseblock as ANE
import service.anti_blank_default_case as ABDC
import service.anti_nothing_default_case as ANDC

def main():
	target_dir, target_files = config.load_cfg("cfg.xml")

	# バックアップファイルの作成.
	backup_dir = target_dir + "_backup"
	if os.path.exists(backup_dir):
		shutil.rmtree(backup_dir)
	shutil.copytree(target_dir, backup_dir)

	for target_file in target_files:
		target_path = os.path.join(target_dir, target_file)

		AMVD.multiple_to_variable_declaration(target_path)
		AUV.delete_unused_variable(target_path)
		ANE.add_elseblock(target_path)
		ABDC.add_comment_default_case(target_path)
		ANDC.add_default_case(target_path)

if __name__ == "__main__":
	main()
 ```

```python:config.py
import xml.etree.ElementTree as ET

def load_cfg(filename:str)->tuple[str, list]:
	# XMLファイルを読み込む
	tree = ET.parse(filename)
	root = tree.getroot()

	target_dir        = root.find('target_dir').text
	target_file_items = root.find('target_file')
	target_files      = [item.text for item in target_file_items]

	return target_dir, target_files
```

```python:anti_unused_variable.py
import regex

def delete_unused_variable(filepath:str):
	pattern = r"(?:(?:auto|register|static|extern)\s+)?(?:(?:const|volatile|restrict|signed|unsigned|short|long)\s+)*\w+\s+\w+\s*\([^()]*\)\s*(\{([^{}]|(?1))*\})"

	pattern_val = r"[\t ]*(?:(?:auto|register|static|extern)\s+)?(?:(?:const|volatile|restrict|signed|unsigned|short|long)\s+)*\w+[\s*]+\w+\s*(?:=[^;]+)?;[^\n]*\n"

	with open(filepath, "r", encoding="utf-8", errors="ignore") as file_obj:
		codetext = file_obj.read()
		functions = regex.finditer(pattern, codetext, regex.DOTALL)
		for func in functions:
			functext = func.group()
			functext_edit = functext
			values = regex.finditer(pattern_val, functext, regex.DOTALL)
			for val in values:
				valtext = val.group()

				if "=" in valtext:
					# 代入演算子がある場合は=の左にある文字列が変数名.
					valname = regex.search(r"\w+(?=\s*=)", valtext).group()
				else:
					# 代入演算子がない場合は;の左にある文字列が変数名.
					valname = regex.search(r"\w+(?=\s*;)", valtext).group()


				valmatchs = regex.finditer(rf"\b{valname}\b", functext, regex.DOTALL)
				usedcount = 0
				for vallll in valmatchs:
					usedcount += 1

				if usedcount == 1:
					# 一度しか記述されていない場合は未使用変数と判断して削除.
					functext_edit = functext_edit.replace(valtext, "")
					pass
			
			codetext = codetext.replace(functext, functext_edit)
				
	with open(filepath, "w", encoding="utf-8", errors="ignore") as file_obj:
		file_obj.write(codetext)
```
```python:anti_nothing_elseblock.py
import regex

def __elseblock_repl(match):
	elseblock = "\nelse\n{\n\t/* 処理なし */\n}"
	matchstr = match.group()

	# if文のインデントを取得する
	indent = regex.search(r"[\t ]*(?=\bif\b|\belse\b\s*\bif\b)", matchstr, regex.DOTALL).group()
	return matchstr + elseblock.replace("\n", "\n" + indent)


def add_elseblock(filepath:str):
	with open(filepath, "r", encoding="utf-8", errors="ignore") as file_obj:
		codetext = file_obj.read()
		edited_code = regex.sub(r"(?<=\n)\s*(?:\bif\b|\belse\b\s*\bif\b)\s*\([^()]*\)\s*(\{(?:[^{}]|(?1))*\})(?!\s*\belse\b\s*\bif\b|\s*\belse\b)", __elseblock_repl, codetext, regex.DOTALL)

	with open(filepath, "w", encoding="utf-8", errors="ignore") as file_obj:
		file_obj.write(edited_code)
```
```python:anti_nothing_default_case.py
import regex

def __repl_add_default(match):
	matchstr = match.group()

	# 既にdefaultがある場合は、そのままの文字をreturn
	if not regex.search("\s*default\s*:", matchstr, regex.DOTALL):
		index = matchstr.rfind("}")

		indent = regex.search(r"[\t ]*(?=\bswitch\b)", matchstr, regex.DOTALL).group()
		matchstr = matchstr[:index] + "default: /* 処理なし */ break;\n"+ indent + matchstr[index:]

	return matchstr

def add_default_case(filepath:str):
	with open(filepath, "r", encoding="utf-8") as file_obj:
		codetext = file_obj.read()

		edited_code = regex.sub(r"(?<=\n)\s*switch\s*\([^()]*\)\s*(\{(?:[^{}]|(?1))*\})", __repl_add_default, codetext, flags=regex.DOTALL)

	with open(filepath, "w", encoding="utf-8", errors="ignore") as file_obj:
		file_obj.write(edited_code)
```
```python:anti_multiple_variable_declaration.py
import regex

def __repldef(match:regex.Match)->str:
	matchtext:str = match.group()
	
	pattern = r"[\t ]*(?:(?:auto|register|static|extern)\s+)?(?:(?:const|volatile|restrict|signed|unsigned|short|long)\s+)*\w+\*?"
	datatype = regex.search(pattern, matchtext, regex.DOTALL).group()

	ret = matchtext.replace(",", f";\n{datatype}")
	return ret

def multiple_to_variable_declaration(file_path:str):
	pattern = r"(?<=[\n;])[\t ]*(?:(?:auto|register|static|extern)\s+)?(?:(?:const|volatile|restrict|signed|unsigned|short|long)\s+)*\w+[\s*]+\w+(?:\s*=[^,;]+)?\s*(,[\s*]*\w+(?:\s*=[^,;]+)?)+;"

	with open(file_path, "r", encoding="utf-8", errors="ignore") as file_obj:
		codetext = file_obj.read()
		edited_code = regex.sub(pattern, __repldef, codetext, flags=regex.DOTALL)

	with open(file_path, "w", encoding="utf-8", errors="ignore") as file_obj:
		file_obj.write(edited_code)
```
```python:anti_blank_default_case.py
import regex

def add_comment_default_case(filepath:str):
	with open(filepath, "r", encoding="utf-8") as file_obj:
		codetext = file_obj.read()

		edited_code = regex.sub(r"(?<=default\s*:\s*);", "/* 処理なし */", codetext, flags=regex.DOTALL)

	with open(filepath, "w", encoding="utf-8", errors="ignore") as file_obj:
		file_obj.write(edited_code)
```

## configファイル
こんな感じ
```xml:cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<config_data>
	<target_dir>C:\Users\XXXX\code</target_dir>
	<target_file>
		<item>main.c</item>
		<item>test.c</item>
		<item>func.c</item>
	</target_file>
</config_data>
```

## ファイル構成
    main.py
    cfg.xml
    service
    ┣ anti_blank_default_case.py
    ┣ anti_multiple_variable_declaration.py
    ┣ anti_nothing_default_case.py
    ┣ anti_nothing_elseblock.py
    ┣ anti_unused_variable.py
    ┣ config.py

以上