added function isolation patch

This commit is contained in:
Erik Fabrizzi
2025-10-27 13:38:58 +01:00
parent cd09ee8742
commit a8f1bae625
2 changed files with 73 additions and 15 deletions

View File

@@ -7,6 +7,7 @@ import subprocess
import shlex
import tomllib
import re
import difflib
from unidecode import unidecode
SUCCESS_BOX = "[\033[0;92mSUCCESS\033[0m]"
@@ -65,6 +66,29 @@ def _weaken_file(filename, functions):
f.write(new_code)
def _add_static_attribute(match):
prefix = match.group(1)
func_name = match.group(2)
suffix = match.group(3)
if "static" in prefix:
return match.group(0)
return f"{prefix} static {func_name}{suffix}"
def _staticize_file(filename, functions):
pattern = re.compile(
r"^(\s*(?:(?:\w+[\s\*]+)+))"
r"(" + "|".join(functions) + r")"
r"(\s*\([^)]*\)\s*\{)",
re.MULTILINE
)
with open(filename, "r") as f:
code = f.read()
new_code = pattern.sub(_add_static_attribute, code)
with open(filename, "w") as f:
f.write(new_code)
def _remove_static_attribute(match):
prefix = match.group(1)
func_name = match.group(2)
@@ -87,17 +111,43 @@ def _unstaticize_file(filename, functions):
f.write(new_code)
def _extract_prototypes_to_header(filename, functions, header_filename):
func_pattern = re.compile(
r"^(\s*(?:(?:\w+[\s\*]+)+))"
r"(" + "|".join(functions) + r")"
r"(\s*\([^)]*\)\s*\{)",
re.MULTILINE
)
with open(filename, "r") as f:
code = f.read()
matches = func_pattern.findall(code)
if not matches:
print(f"No functions found in {filename}")
return
prototypes = []
for prefix, func_name, args in matches:
proto = f"{prefix.strip()} static {func_name}{args.strip()[:-1]};"
prototypes.append(proto)
prototypes = list(dict.fromkeys(prototypes)) # preserve order
header_exists = os.path.exists(header_filename)
with open(header_filename, "w" if header_exists else "w") as header:
header.write('#include "solver.h"'+"\n")
for proto in prototypes:
header.write(proto + "\n")
include_line = f'#include "{os.path.basename(header_filename)}"\n'
if include_line not in code:
new_code = include_line + code
with open(filename, "w") as f:
f.write(new_code)
def is_submission_dir(dir_path):
return os.path.isdir(dir_path) and "_assignsubmission_file" in dir_path
# def _make_names(dir_path):
# student_names = [unidecode(name[:name.find('_')].lower().replace(
# ' ', '_')) for name in os.listdir(
# dir_path) if is_submission_dir(os.path.join(dir_path, name))]
# return student_names
def _make_names_pairs(dir_path):
student_pairs = [(unidecode(name[:name.find('_')].lower().replace(
' ', '_')), os.path.join(dir_path, name)) for name in os.listdir(
@@ -356,8 +406,6 @@ def compile(number, path, command, verbose):
@click.argument('number', required=True, type=click.INT)
@click.option('-s', '--solution-path', default='./solutions', type=click.Path(exists=True, file_okay=False, dir_okay=True, resolve_path=True))
@click.option('-p', '--path', default='./roots', type=click.Path(exists=True, file_okay=False, dir_okay=True, resolve_path=True))
# @click.option('-c', '--command', default='make', type=click.STRING)
# @click.option('-v', '--verbose', is_flag=True, default=False)
def dissect(number, solution_path, path):
sol_as_path = os.path.join(solution_path, f"assignment_{number}")
config_path = os.path.join(sol_as_path, "test_config.toml")
@@ -384,6 +432,11 @@ def dissect(number, solution_path, path):
f"{ERROR_BOX}: No function dissect config found for assignment_{number}.", err=True)
return
all_functions = config['dissect']['all_functions']
if not 'build_cmd' in config["dissect"]:
click.echo(
f"{ERROR_BOX}: No build config found for assignment_{number}.", err=True)
return
static_functions = config['dissect']['static_functions']
if not 'build_cmd' in config["dissect"]:
click.echo(
f"{ERROR_BOX}: No build config found for assignment_{number}.", err=True)
@@ -428,11 +481,17 @@ def dissect(number, solution_path, path):
click.echo(
f"\t[{os.path.basename(root)}]:{ERROR_BOX}: Missing source {os.path.basename(src)}")
shutil.copyfile(src, dest)
_extract_prototypes_to_header(
dest, static_functions, os.path.join(sol_as_path, 'src', 'aid.h'))
_unstaticize_file(dest, all_functions)
_weaken_file(dest, all_functions)
# BUILD TEST
_staticize_file(dest, static_functions)
result = 0
os.chdir(sol_as_path)
for function, flag in zip(test_functions, flags):
command = build_cmd + f" CFLAGS+={flag}"
result = subprocess.run(
@@ -446,13 +505,9 @@ def dissect(number, solution_path, path):
else:
click.echo(
f"\t[{os.path.basename(root)}]:[BUILD]:[{function}]:{ERROR_BOX}")
click.echo(f"stderr: {result.stderr[:69]:69s}...")
click.echo(f"\t\tstderr: {result.stderr[:61]:61s}...")
os.chdir(current_wd)
# for file in solution_files:
# _unstaticize_file(file, test_functions)
# _weaken_file(file, all_functions)
cli.add_command(init)
cli.add_command(archives)