From 7fa1a39adfc715fe7022e03ea09dd7260195d0bd Mon Sep 17 00:00:00 2001 From: 7000pctAUTO Date: Fri, 30 Jan 2026 18:58:59 +0000 Subject: [PATCH] Add CLI and services modules --- src/codexchange/utils/syntax_check.py | 177 ++++++++++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 src/codexchange/utils/syntax_check.py diff --git a/src/codexchange/utils/syntax_check.py b/src/codexchange/utils/syntax_check.py new file mode 100644 index 0000000..c1fef3d --- /dev/null +++ b/src/codexchange/utils/syntax_check.py @@ -0,0 +1,177 @@ +"""Syntax verification utilities for CodeXchange CLI.""" + +import ast +import re +import subprocess +from typing import Tuple + +from codexchange.models import Language + + +def check_python_syntax(code: str) -> Tuple[bool, list[str]]: + """Check Python syntax using ast.parse. + + Args: + code: Python code to check. + + Returns: + Tuple of (is_valid, error_message). + """ + try: + ast.parse(code) + return True, [] + except SyntaxError as e: + return False, [f"Syntax error at line {e.lineno}: {e.msg}"] + + +def check_typescript_syntax(code: str) -> Tuple[bool, list[str]]: + """Check TypeScript syntax using tsc --noEmit. + + Args: + code: TypeScript code to check. + + Returns: + Tuple of (is_valid, list of warnings/errors). + """ + import tempfile + import os + + warnings = [] + + with tempfile.NamedTemporaryFile(mode="w", suffix=".ts", delete=False) as f: + f.write(code) + temp_file = f.name + + try: + result = subprocess.run( + ["npx", "tsc", "--noEmit", "--skipLibCheck", temp_file], + capture_output=True, + text=True, + timeout=30 + ) + + if result.returncode != 0: + for line in result.stderr.split("\n"): + line = line.strip() + if line and "error" in line.lower(): + warnings.append(line) + + if not warnings: + warnings = result.stderr.strip().split("\n") + warnings = [w for w in warnings if w.strip()] + + except FileNotFoundError: + warnings.append("TypeScript compiler (tsc) not found. Install Node.js and TypeScript.") + except subprocess.TimeoutExpired: + warnings.append("TypeScript compilation timed out.") + except Exception as e: + warnings.append(f"TypeScript check failed: {str(e)}") + finally: + os.unlink(temp_file) + + return len(warnings) == 0, warnings + + +def check_javascript_syntax(code: str) -> Tuple[bool, list[str]]: + """Check JavaScript syntax using ESLint or basic parsing. + + Args: + code: JavaScript code to check. + + Returns: + Tuple of (is_valid, list of warnings/errors). + """ + import tempfile + import os + + warnings = [] + + with tempfile.NamedTemporaryFile(mode="w", suffix=".js", delete=False) as f: + f.write(code) + temp_file = f.name + + try: + result = subprocess.run( + ["npx", "eslint", "--format", "compact", temp_file], + capture_output=True, + text=True, + timeout=30 + ) + + if result.returncode != 0: + for line in result.stderr.split("\n"): + line = line.strip() + if line: + warnings.append(line) + + output = result.stdout.strip() + if output: + warnings.extend(output.split("\n")) + + except FileNotFoundError: + warnings.append("ESLint not found. Install Node.js and ESLint for JavaScript validation.") + except subprocess.TimeoutExpired: + warnings.append("ESLint check timed out.") + except Exception as e: + warnings.append(f"JavaScript check failed: {str(e)}") + finally: + os.unlink(temp_file) + + return len(warnings) == 0, warnings + + +def check_java_syntax(code: str) -> Tuple[bool, list[str]]: + """Check Java syntax (basic validation). + + Args: + code: Java code to check. + + Returns: + Tuple of (is_valid, list of warnings/errors). + """ + warnings = [] + + if not code.strip(): + return False, ["Empty Java file"] + + class_name_pattern = r"public\s+class\s+(\w+)" + matches = re.findall(class_name_pattern, code) + + if not matches: + warnings.append("No public class found in Java file") + + brace_count = code.count("{") - code.count("}") + if brace_count != 0: + warnings.append("Mismatched braces in Java file") + + if code.count("(") != code.count(")"): + warnings.append("Mismatched parentheses in Java file") + + return len(warnings) == 0, warnings + + +def verify_syntax(code: str, language: Language) -> Tuple[bool, list[str]]: + """Verify syntax for the given language. + + Args: + code: Source code to verify. + language: Programming language of the code. + + Returns: + Tuple of (is_valid, list of warnings/errors). + """ + if not code or not code.strip(): + return False, ["Empty code input"] + + checkers = { + Language.PYTHON: check_python_syntax, + Language.TYPESCRIPT: check_typescript_syntax, + Language.JAVASCRIPT: check_javascript_syntax, + Language.JAVA: check_java_syntax, + } + + checker = checkers.get(language) + if not checker: + return True, [] + + return checker(code)