jak-project/scripts/gsrc/lint-gsrc-file.py
Tyler Wilding 8b12553d15
d/jak2: first pass at decompiling the core vehicle/traffic code (#2058)
Effects the following files:
- [x] vehicle-rider
- [x] vehicle-control
- [x] vehicle-effects
- [x] vehicle
~~- [ ] vehicle-util~~
- [x] vehicle-physics
- [x] vehicle-states
~~- [ ] vehicle-guard~~
~~- [ ] traffic-engine~~
~~- [ ] traffic-manager~~

With the exception of traffic-engine, most of these files are either
done or have 1-3 stubborn functions remaining. Draft while I try to
resolve as many as possible / cleanup names and such.

Co-authored-by: water <awaterford111445@gmail.com>
2023-01-14 12:01:33 -05:00

160 lines
5.2 KiB
Python

import re
import argparse
from utils import get_gsrc_path_from_filename
from colorama import just_fix_windows_console, Fore, Back, Style
just_fix_windows_console()
parser = argparse.ArgumentParser("lint-gsrc-file")
parser.add_argument("--game", help="The name of the game", type=str)
parser.add_argument("--file", help="The name of the file", type=str)
args = parser.parse_args()
class LintMatch:
def __init__(self, src_path, offending_lineno, context):
self.src_path = src_path
self.offending_lineno = offending_lineno
self.context = context
def __str__(self):
output = (
Style.BRIGHT
+ Fore.MAGENTA
+ "@ {}:{}\n".format(self.src_path, self.offending_lineno)
+ Fore.RESET
+ Style.RESET_ALL
)
for line in self.context:
# skip lines that are just brackets
if line.strip() == ")" or line.strip() == "(":
continue
output += "\t{}\n".format(line)
return output
class LinterRule:
def __init__(self, level, rule_name, regex_pattern, context_size):
self.level = level
self.rule_name = rule_name
self.regex_pattern = regex_pattern
self.context_size = context_size
self.matches = []
def __str__(self):
level_color = Fore.LIGHTBLUE_EX
if self.level == "WARN":
level_color = Fore.YELLOW
elif self.level == "ERROR":
level_color = Fore.RED
return (
level_color
+ "[{}]{} - {} - {}/{}/g".format(
self.level,
Fore.RESET,
level_color + self.rule_name + Fore.RESET,
Fore.CYAN,
self.regex_pattern.pattern,
)
+ Fore.RESET
+ ":"
)
# Construct all rules
linter_rules = []
# Infos
# Warnings
linter_rules.append(
LinterRule("WARN", "method_splits", re.compile("method-of-(?:type|object)"), 3)
)
linter_rules.append(
LinterRule("WARN", "func_splits", re.compile("\(t9-\d+(?:\s+[^\s]+\s*)?\)"), 3)
)
linter_rules.append(
LinterRule("WARN", "missing_arg", re.compile("local-vars.*[at].*\s+none\)"), 1)
)
# Errors
linter_rules.append(LinterRule("ERROR", "missing_res_tag", re.compile(".pcpyud"), 1))
linter_rules.append(LinterRule("ERROR", "decomp_error", re.compile(";; ERROR"), 1))
linter_rules.append(
LinterRule(
"ERROR", "casting_stack_var", re.compile("the-as\s+[^\s]*\s+.*\(new 'stack"), 2
)
)
src_path = get_gsrc_path_from_filename(args.game, args.file)
# Iterate through the file line by line, check against each rule
# if the rule is violated (it matches) then we append the match with useful details
print("Linting GOAL_SRC File...")
def get_context(lines, match_span, idx, amount_inclusive):
lines_grabbed = []
# Strip left pad, while maintaining indent
last_line_indent_width = -1
last_line_indent = -1
while len(lines_grabbed) < amount_inclusive and len(lines) > idx + len(
lines_grabbed
):
# TODO - first line, colorize the match
# if len(lines_grabbed) == 0:
# line = lines[idx + len(lines_grabbed)]
# line = line[:match_span[0]] + Back.RED + line[:match_span[1]] + Back.RESET + line[match_span[1]:]
# line = line.rstrip()
line = lines[idx + len(lines_grabbed)].rstrip()
indent_width = len(line) - len(line.lstrip())
if last_line_indent_width == -1:
lines_grabbed.append(line.lstrip())
elif last_line_indent == -1:
# calculate the difference
indent_diff = indent_width - last_line_indent_width
last_line_indent = indent_diff
stripped_line = line.lstrip()
lines_grabbed.append(stripped_line.rjust(indent_diff + len(stripped_line)))
else:
stripped_line = line.lstrip()
lines_grabbed.append(
stripped_line.rjust(last_line_indent + len(stripped_line))
)
last_line_indent_width = indent_width
return lines_grabbed
with open(src_path) as f:
src_lines = f.readlines()
for lineno, line in enumerate(src_lines):
adjusted_lineno = lineno + 1
for rule in linter_rules:
match = rule.regex_pattern.search(line)
if match:
rule.matches.append(
LintMatch(
src_path,
adjusted_lineno,
get_context(src_lines, match.span(), lineno, rule.context_size),
)
)
# Iterate through all our linter rules, printing nicely in groups with the
# context surrounding the match
#
# If we find any violations at warning or above, we will ultimately return exit(1)
throw_error = False
for rule in linter_rules:
# Iterate through violations
if len(rule.matches) > 0:
print(rule)
for match in rule.matches:
if rule.level == "ERROR" or rule.level == "WARN":
throw_error = True
print(match)
if throw_error:
print(Fore.RED + "Found potential problems, exiting with code 1!" + Fore.RESET)
exit(1)
else:
print(Fore.GREEN + "Looks good!" + Fore.RESET)