Files
whetstone_DSL/tools/mcp/evaluate_generated_code_gates.py

120 lines
3.6 KiB
Python
Executable File

#!/usr/bin/env python3
"""
Evaluate production gates for generated code.
"""
from __future__ import annotations
import argparse
import json
import os
import re
import subprocess
import tempfile
from pathlib import Path
from typing import Dict, List
PLACEHOLDER_PATTERNS = [
r"\bTODO\b",
r"\bFIXME\b",
r"\bplaceholder\b",
r"auto\s*/\*\s*TODO",
r"/\*\s*missing",
]
def detect_placeholders(code: str) -> Dict[str, object]:
findings: List[Dict[str, object]] = []
for pat in PLACEHOLDER_PATTERNS:
for m in re.finditer(pat, code, re.IGNORECASE):
findings.append({"pattern": pat, "start": m.start(), "end": m.end()})
return {"passed": len(findings) == 0, "count": len(findings), "findings": findings}
def compile_check(code: str, language: str) -> Dict[str, object]:
lang = language.lower()
with tempfile.TemporaryDirectory(prefix="whetstone_gate_") as td:
td_path = Path(td)
if lang in ("cpp", "c++"):
src = td_path / "generated.cpp"
src.write_text(code)
cmd = ["g++", "-std=c++20", "-fsyntax-only", str(src)]
elif lang in ("python", "py"):
src = td_path / "generated.py"
src.write_text(code)
cmd = ["python3", "-m", "py_compile", str(src)]
else:
return {"passed": False, "skipped": True, "reason": f"compile gate not implemented for {language}"}
try:
p = subprocess.run(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
timeout=20,
check=False,
)
except FileNotFoundError:
return {"passed": False, "skipped": True, "reason": f"compiler missing for {language}"}
except subprocess.TimeoutExpired:
return {"passed": False, "skipped": False, "reason": "compile timeout"}
return {
"passed": p.returncode == 0,
"skipped": False,
"return_code": p.returncode,
"stdout": p.stdout[-4000:],
"stderr": p.stderr[-4000:],
}
def test_check(code: str, language: str) -> Dict[str, object]:
text = code.lower()
if "priorityqueue" in text:
required = ["enqueue", "dequeue", "peek", "size", "empty"]
missing = [x for x in required if x not in text]
return {"passed": not missing, "missing": missing}
return {"passed": True, "missing": []}
def main() -> None:
ap = argparse.ArgumentParser()
ap.add_argument("--code-file", help="Path to generated code file", default="")
ap.add_argument("--code", help="Inline generated code", default="")
ap.add_argument("--language", required=True)
ap.add_argument("--out", default="")
args = ap.parse_args()
code = args.code
if args.code_file:
code = Path(args.code_file).read_text()
placeholders = detect_placeholders(code)
compile_res = compile_check(code, args.language)
test_res = test_check(code, args.language)
overall = bool(placeholders["passed"] and compile_res.get("passed", False) and test_res.get("passed", False))
payload = {
"language": args.language,
"gates": {
"compile": compile_res,
"tests": test_res,
"placeholder": placeholders,
"overall_ready": overall,
},
}
text = json.dumps(payload, indent=2, sort_keys=True)
if args.out:
out = Path(args.out)
out.parent.mkdir(parents=True, exist_ok=True)
out.write_text(text + "\n")
print(text)
if __name__ == "__main__":
main()