From ee212e83716bc053060d26a7734d0977e789f0b2 Mon Sep 17 00:00:00 2001 From: desire04 Date: Thu, 21 Nov 2024 23:50:09 -0500 Subject: [PATCH 1/3] Testing python parser. --- src/constants.ts | 6 +- src/context/language/javascript-parser.ts | 48 +++++++------ src/context/language/python-parser.ts | 84 +++++++++++++++++++++-- src/context/language/python_parser.py | 0 4 files changed, 108 insertions(+), 30 deletions(-) create mode 100644 src/context/language/python_parser.py diff --git a/src/constants.ts b/src/constants.ts index 14c7de1..4dd35cf 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,6 +1,7 @@ import { Node } from "@babel/traverse"; import { JavascriptParser } from "./context/language/javascript-parser"; import { ChatCompletionMessageParam } from "groq-sdk/resources/chat/completions"; +import { PythonParser } from "./context/language/python-parser"; export interface PRFile { sha: string; @@ -98,8 +99,8 @@ export interface AbstractParser { file: string, lineStart: number, lineEnd: number - ): EnclosingContext; - dryRun(file: string): { valid: boolean; error: string }; + ): Promise; + dryRun(file: string): Promise<{ valid: boolean; error: string }>; } const EXTENSIONS_TO_PARSERS: Map = new Map([ @@ -107,6 +108,7 @@ const EXTENSIONS_TO_PARSERS: Map = new Map([ ["tsx", new JavascriptParser()], ["js", new JavascriptParser()], ["jsx", new JavascriptParser()], + ["py", new PythonParser()] ]); export const getParserForExtension = (filename: string) => { diff --git a/src/context/language/javascript-parser.ts b/src/context/language/javascript-parser.ts index 41cd52a..1a34752 100644 --- a/src/context/language/javascript-parser.ts +++ b/src/context/language/javascript-parser.ts @@ -21,17 +21,19 @@ const processNode = ( }; export class JavascriptParser implements AbstractParser { - findEnclosingContext( + async findEnclosingContext( file: string, lineStart: number, lineEnd: number - ): EnclosingContext { - const ast = parser.parse(file, { - sourceType: "module", - plugins: ["jsx", "typescript"], // To allow JSX and TypeScript - }); - let largestEnclosingContext: Node = null; - let largestSize = 0; + ): Promise { + return new Promise ((resolve) => { + const ast = parser.parse(file, { + sourceType: "module", + plugins: ["jsx", "typescript"], // To allow JSX and TypeScript + }); + + let largestEnclosingContext: Node = null; + let largestSize = 0; traverse(ast, { Function(path) { ({ largestSize, largestEnclosingContext } = processNode( @@ -52,26 +54,30 @@ export class JavascriptParser implements AbstractParser { )); }, }); - return { + resolve({ enclosingContext: largestEnclosingContext, - } as EnclosingContext; + } as EnclosingContext); + }); } + - dryRun(file: string): { valid: boolean; error: string } { - try { - const ast = parser.parse(file, { - sourceType: "module", - plugins: ["jsx", "typescript"], // To allow JSX and TypeScript - }); - return { + async dryRun(file: string): Promise<{ valid: boolean; error: string }> { + return new Promise((resolve) => { + try { + const ast = parser.parse(file, { + sourceType: "module", + plugins: ["jsx", "typescript"], // To allow JSX and TypeScript + }); + resolve({ valid: true, error: "", - }; + }); } catch (exc) { - return { + resolve({ valid: false, error: exc, - }; + }); } - } + }); +} } diff --git a/src/context/language/python-parser.ts b/src/context/language/python-parser.ts index 845e90b..92d6e02 100644 --- a/src/context/language/python-parser.ts +++ b/src/context/language/python-parser.ts @@ -1,15 +1,85 @@ import { AbstractParser, EnclosingContext } from "../../constants"; +import { spawn } from 'child_process'; +import * as path from 'path'; + export class PythonParser implements AbstractParser { - findEnclosingContext( + private pythonScriptPath: string; + + constructor() { + this.pythonScriptPath = path.join(__dirname, 'python_parser.py'); + } + + async findEnclosingContext( file: string, lineStart: number, lineEnd: number - ): EnclosingContext { - // TODO: Implement this method for Python - return null; + ): Promise { + return new Promise((resolve, reject) => { + const pythonProcess = spawn('python', [ + this.pythonScriptPath, + file, + lineStart.toString(), + lineEnd.toString() + ]); + + let output = ''; + let errorOutput = ''; + + pythonProcess.stdout.on('data', (data) => { + output += data.toString(); + }); + + pythonProcess.stderr.on('data', (data) => { + errorOutput += data.toString(); + }); + + pythonProcess.on('close', (code) => { + if (code === 0) { + try { + const context = JSON.parse(output); + resolve({ enclosingContext: context } as EnclosingContext); + } catch (error) { + reject(new Error(`Failed to parse Python output: ${error}`)); + } + } else { + reject(new Error(`Python script exited with code ${code}: ${errorOutput}`)); + } + }); + }); } - dryRun(file: string): { valid: boolean; error: string } { - // TODO: Implement this method for Python - return { valid: false, error: "Not implemented yet" }; + async dryRun(file: string): Promise<{ valid: boolean; error: string }> { + + return new Promise((resolve, reject) => { + const pythonProcess = spawn('python', ['-c', ` + import ast + + try: + with open('${file})', 'r') as f: + ast.parse(f.read()) + print('{"valid": true, "error": ""}') + except Exception as e: + print('{"valid": false, "error": "' + str(e).replace('"', '\\"') + + '"}')` + ]); + + let output = ''; + + pythonProcess.stdout.on('data', (data) => { + output += data.toString(); + }); + + pythonProcess.on('close', (code) => { + if (code === 0) { + try { + const result = JSON.parse(output); + resolve(result); + } catch (error) { + reject(new Error(`Failed to parse Python output: ${error}`)); + } + } else { + reject(new Error(`Python script exited with code ${code}`)); + } + }); + }); } } diff --git a/src/context/language/python_parser.py b/src/context/language/python_parser.py new file mode 100644 index 0000000..e69de29 From 554c987057902600697c804f65690ef483930dbc Mon Sep 17 00:00:00 2001 From: desire04 Date: Fri, 22 Nov 2024 00:00:34 -0500 Subject: [PATCH 2/3] Testing python parser. --- src/randomStuff.py | 65 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 src/randomStuff.py diff --git a/src/randomStuff.py b/src/randomStuff.py new file mode 100644 index 0000000..a018fc5 --- /dev/null +++ b/src/randomStuff.py @@ -0,0 +1,65 @@ +import random +import string +import json +import math + +def generate_random_number(min_value=1, max_value=100): + """Generate a random number between min_value and max_value.""" + return random.randint(min_value, max_value) + +def generate_random_string(length=10): + """Generate a random string of a specified length.""" + letters = string.ascii_letters # Uppercase and lowercase letters + return ''.join(random.choice(letters) for i in range(length)) + +def create_random_list(size=5, min_value=1, max_value=50): + """Create a list of random integers.""" + return [generate_random_number(min_value, max_value) for _ in range(size)] + +def calculate_factorial(n): + """Calculate the factorial of a number.""" + if n < 0: + return None + return math.factorial(n) + +def write_to_json_file(data, filename='data.json'): + """Write a dictionary to a JSON file.""" + with open(filename, 'w') as file: + json.dump(data, file, indent=4) + +def read_from_json_file(filename='data.json'): + """Read data from a JSON file.""" + with open(filename, 'r') as file: + return json.load(file) + +def main(): + # Generate some random numbers and strings + random_number = generate_random_number() + random_string = generate_random_string() + random_list = create_random_list() + + print(f"Random Number: {random_number}") + print(f"Random String: {random_string}") + print(f"Random List: {random_list}") + + # Calculate factorial for a random number + factorial_of = random.choice(random_list) + factorial_value = calculate_factorial(factorial_of) + print(f"Factorial of {factorial_of}: {factorial_value}") + + # Write some data to a JSON file + data = { + "random_number": random_number, + "random_string": random_string, + "random_list": random_list, + "factorial_value": factorial_value + } + write_to_json_file(data) + + # Read the data back from the JSON file + loaded_data = read_from_json_file() + print("Data loaded from JSON file:") + print(json.dumps(loaded_data, indent=4)) + +if __name__ == "__main__": + main() \ No newline at end of file From 99a81d2c24706a49fa1c1c303ba8aad2b901cdf7 Mon Sep 17 00:00:00 2001 From: desire04 Date: Fri, 22 Nov 2024 20:55:34 -0500 Subject: [PATCH 3/3] Testing python parser. --- src/context/language/python_parser.py | 55 +++++++++++++++++++++++++++ src/context/review.ts | 6 +-- 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/src/context/language/python_parser.py b/src/context/language/python_parser.py index e69de29..7d87c5f 100644 --- a/src/context/language/python_parser.py +++ b/src/context/language/python_parser.py @@ -0,0 +1,55 @@ +import ast +import sys +import json + +def find_enclosing_context(file_content, line_start, line_end): + tree = ast.parse(file_content) + + class ContextFinder(ast.NodeVisitor): + def __init__(self, line_start, line_end): + self.line_start = line_start + self.line_end = line_end + self.largest_context = None + self.largest_size = 0 + + def visit_FunctionDef(self, node): + self.check_node(node) + self.generic_visit(node) + + def visit_ClassDef(self, node): + self.check_node(node) + self.generic_visit(node) + + def check_node(self, node): + if node.lineno <= self.line_start and self.line_end <= node.end_lineno: + size = node.end_lineno - node.lineno + if size > self.largest_size: + self.largest_size = size + self.largest_context = node + + finder = ContextFinder(line_start, line_end) + finder.visit(tree) + + if finder.largest_context: + return { + "type": finder.largest_context.__class__.__name__, + "name": finder.largest_context.name, + "start_line": finder.largest_context.lineno, + "end_line": finder.largest_context.end_lineno + } + + return None + +def main(): + file_path = sys.argv[1] + line_start = int(sys.argv[2]) + line_end = int(sys.argv[3]) + + with open(file_path, 'r') as file: + file_content = file.read() + + context = find_enclosing_context(file_content, line_start, line_end) + print(json.dumps(context)) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/src/context/review.ts b/src/context/review.ts index 7d44aa1..67c717f 100644 --- a/src/context/review.ts +++ b/src/context/review.ts @@ -205,7 +205,7 @@ const diffContextPerHunk = (file: PRFile, parser: AbstractParser) => { }); }); - hunks.forEach((hunk, idx) => { + hunks.forEach(async (hunk, idx) => { try { const trimmedHunk = trimHunk(hunk); const insertions = hunk.lines.filter((line) => @@ -213,11 +213,11 @@ const diffContextPerHunk = (file: PRFile, parser: AbstractParser) => { ).length; const lineStart = trimmedHunk.newStart; const lineEnd = lineStart + insertions; - const largestEnclosingFunction = parser.findEnclosingContext( + const largestEnclosingFunction = (await parser.findEnclosingContext( updatedFile, lineStart, lineEnd - ).enclosingContext; + )).enclosingContext; if (largestEnclosingFunction) { const enclosingRangeKey = `${largestEnclosingFunction.loc.start.line} -> ${largestEnclosingFunction.loc.end.line}`;