8e383b9e3b
Added strict delimiter handling and empty-parameter filtering to catch unbalanced expressions and discard blank entries
116 lines
3.8 KiB
Python
116 lines
3.8 KiB
Python
import os
|
|
import re
|
|
import sys
|
|
|
|
|
|
def split_params(param_string: str):
|
|
"""Split parameters on commas while ignoring nested structures."""
|
|
params = []
|
|
current: list[str] = []
|
|
stack: list[str] = []
|
|
pairs = {')': '(', ']': '[', '}': '{'}
|
|
in_quote = None
|
|
escape = False
|
|
for char in param_string:
|
|
if escape:
|
|
current.append(char)
|
|
escape = False
|
|
continue
|
|
|
|
if char == "\\":
|
|
current.append(char)
|
|
escape = True
|
|
continue
|
|
|
|
if in_quote:
|
|
current.append(char)
|
|
if char == in_quote:
|
|
in_quote = None
|
|
continue
|
|
|
|
if char in "'\"":
|
|
current.append(char)
|
|
in_quote = char
|
|
continue
|
|
|
|
if char in '([{':
|
|
stack.append(char)
|
|
elif char in ')]}':
|
|
if stack and stack[-1] == pairs.get(char):
|
|
stack.pop()
|
|
else:
|
|
raise ValueError("unbalance in delimiter")
|
|
|
|
if char == ',' and not stack:
|
|
params.append(''.join(current).strip())
|
|
current = []
|
|
else:
|
|
current.append(char)
|
|
|
|
if stack:
|
|
raise ValueError("unbalance in delimiter")
|
|
if in_quote:
|
|
raise ValueError(f"Unclosed quote: {in_quote}")
|
|
|
|
params.append(''.join(current).strip())
|
|
params = [p for p in params if p]
|
|
return params
|
|
|
|
EXCLUDED_DIRS = {"addons", ".git", ".github"}
|
|
|
|
single_char_vars = []
|
|
|
|
# Patterns reused from check_gd_nomenclature.py
|
|
VAR_PATTERN = re.compile(r"(?:@\w+\s+)*var\s+([A-Za-z0-9_]+)")
|
|
FUNC_PATTERN = re.compile(r"func\s+[A-Za-z0-9_]+\s*(\([^)]*\))?")
|
|
FOR_PATTERN = re.compile(r"for\s+([A-Za-z0-9_]+)(?:\s*:\s*[^\s]+)?\s+in\b")
|
|
|
|
for root, dirs, files in os.walk('.', topdown=True):
|
|
rel_root = os.path.relpath(root, '.')
|
|
if any(rel_root == ex or rel_root.startswith(f"{ex}{os.sep}") for ex in EXCLUDED_DIRS):
|
|
dirs[:] = []
|
|
continue
|
|
for fname in files:
|
|
if fname.endswith('.gd'):
|
|
path = os.path.join(root, fname)
|
|
try:
|
|
with open(path, 'r', encoding='utf-8') as f:
|
|
lines = f.readlines()
|
|
except Exception as e:
|
|
continue
|
|
for idx, line in enumerate(lines, 1):
|
|
stripped = line.strip()
|
|
if stripped.startswith('#') or not stripped:
|
|
continue
|
|
|
|
match_var = VAR_PATTERN.match(stripped)
|
|
if match_var:
|
|
name = match_var.group(1)
|
|
if len(name) == 1:
|
|
single_char_vars.append((path, idx, name))
|
|
|
|
match_func = FUNC_PATTERN.match(stripped)
|
|
if match_func:
|
|
params = match_func.group(1)
|
|
if params:
|
|
params = params.strip('()')
|
|
for param in split_params(params):
|
|
param_name = param.split(':')[0].split('=')[0].strip()
|
|
if param_name and len(param_name) == 1:
|
|
single_char_vars.append((path, idx, param_name))
|
|
|
|
match_for = FOR_PATTERN.match(stripped)
|
|
if match_for:
|
|
name = match_for.group(1)
|
|
if len(name) == 1:
|
|
single_char_vars.append((path, idx, name))
|
|
|
|
if single_char_vars:
|
|
print("### \u274c Single Character Variable Check Failed\n")
|
|
print(f"Variables and functions must not have a name of a single character, they must be more explicit.\nFound {len(single_char_vars)} single-character name{'s' if len(single_char_vars) > 1 else ''}:\n")
|
|
for path, idx, name in single_char_vars:
|
|
print(f"- `{path}:{idx}` variable `{name}`")
|
|
sys.exit(1)
|
|
else:
|
|
print("\u2705 No single-character variable names found.")
|