diff --git a/nac3/find_ndarray_anomalies.py b/nac3/find_ndarray_anomalies.py new file mode 100644 index 0000000..e18c6de --- /dev/null +++ b/nac3/find_ndarray_anomalies.py @@ -0,0 +1,109 @@ +#!/usr/bin/env python + +# # PLACE ME IN nac3standalone/demo/find_ndarray_anomalies.py AND DO `chmod +x` ON ME!! + +from __future__ import annotations +from pathlib import Path +from typing import * +import argparse +import re +import subprocess + +class SourceTemplate(NamedTuple): + source: str + + def _list_all_functions(self) -> list[str]: + # Find all `def ???()` functions + pattern = re.compile( + r"^def\s+(?P.+?)\s*\(.*?\)\s*:\s*$", + re.MULTILINE + ) + + results = pattern.findall(self.source) + if not results: + raise RuntimeError("Something is wrong with the regex") + + return results + + def list_all_test_functions(self) -> Iterator[str]: + # Find all `def test_*()` functions + return [ n for n in self._list_all_functions() if n.startswith("test_") ] + + def subst_function(self, func_name: str) -> str: + # Append a `def run()` at the end of `source` + return self.source + f""" + +def run() -> int32: + {func_name}() + return 0 +""" + + @classmethod + def load(cls) -> SourceTemplate: + # What this function does: + # 1. Read src/ndarray.py + # 2. Chop off the `run()` function, leaving behind everything else. + # - This is done by finding the `def run() -> int32:` line and + # remove that line and everything below it. + original_source = Path("src/ndarray.py").read_text() + + # Read the function's comment + pattern = re.compile( + r"(?P.+?)^\s+^def\s+run\(\)\s*->\s*int32\s*:\s*$.*", + re.MULTILINE | re.DOTALL + ) + + regex_result = pattern.fullmatch(original_source) + if regex_result is None: + raise RuntimeError("Regex cannot find `def run() -> int32:`!") + + # source is now original_source but without the + # `def run() -> int32:` line and everything below it. + source = regex_result.group("source") + return cls(source=source) + +def proc_nac3( + py_path: Path, + size_t: int, + lli: bool, + nac3args: Optional[list[str]] = None +) -> subprocess.CompletedProcess: + # Prepare args + args = ["./run_demo.sh"] + if lli: + args.append("--lli") + + args.extend(["--out", "/dev/stdout"]) + + if nac3args is not None: + args.extend(nac3args) + + args.extend(["-s", str(size_t)]) + args.append(str(py_path)) + + # Run + return subprocess.run(args, capture_output=True) + +def main(): + template = SourceTemplate.load() + + tmp_py_path = Path("_ndarray_tmp.py") + + for test_func in template.list_all_test_functions(): + source = template.subst_function(test_func) + tmp_py_path.write_text(source) + + # We only want to find LLVM codegen defects, + # therefore we don't need to run them, and `--lli` doesn't matter. + nac3result = proc_nac3(tmp_py_path, 32, lli=False, nac3args=["--debug"]) + if nac3result.returncode != 0: + print(f"\n\n\n>>>>>>>>>>>>>> Error in {test_func}") + # utf8 decode content + # There should be no raw bytes output from nac3core when compiling + error = nac3result.stderr.decode() + print(error) + + print("\n\n\nDone") + +if __name__ == "__main__": + main() \ No newline at end of file