From 035aa8d69801b9528e11609d2b273b6dcb569d07 Mon Sep 17 00:00:00 2001 From: CyanideByte Date: Tue, 25 Jun 2024 14:27:27 -0700 Subject: [PATCH 1/2] Java language support --- .../core/computer/terminal/languages/java.py | 146 ++++++++++++++++++ .../core/computer/terminal/terminal.py | 2 + 2 files changed, 148 insertions(+) create mode 100644 interpreter/core/computer/terminal/languages/java.py diff --git a/interpreter/core/computer/terminal/languages/java.py b/interpreter/core/computer/terminal/languages/java.py new file mode 100644 index 0000000000..5eb46b8cf3 --- /dev/null +++ b/interpreter/core/computer/terminal/languages/java.py @@ -0,0 +1,146 @@ +import os +import queue +import re +import subprocess +import threading +import time +import traceback +from .subprocess_language import SubprocessLanguage + +class Java(SubprocessLanguage): + file_extension = "java" + name = "Java" + + def __init__(self): + super().__init__() + self.start_cmd = None # We will handle the start command in the run method + + def preprocess_code(self, code): + return preprocess_java(code) + + def line_postprocessor(self, line): + # Clean up output from javac and java + return line.strip() + + def detect_active_line(self, line): + if "##active_line" in line: + return int(line.split("##active_line")[1].split("##")[0]) + return None + + def detect_end_of_execution(self, line): + return "##end_of_execution##" in line + + def run(self, code): + try: + # Extract the class name from the code + match = re.search(r'class\s+(\w+)', code) + if not match: + yield { + "type": "console", + "format": "output", + "content": "Error: No class definition found in the provided code." + } + return + + class_name = match.group(1) + file_name = f"{class_name}.java" + + # Write the Java code to a file + with open(file_name, "w") as file: + file.write(code) + + # Compile the Java code + compile_process = subprocess.Popen( + ["javac", file_name], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True + ) + + stdout, stderr = compile_process.communicate() + + if compile_process.returncode != 0: + yield { + "type": "console", + "format": "output", + "content": f"Compilation Error:\n{stderr}" + } + return + + # Run the compiled Java code + run_process = subprocess.Popen( + ["java", class_name], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True + ) + + stdout_thread = threading.Thread( + target=self.handle_stream_output, + args=(run_process.stdout, False), + daemon=True, + ) + stderr_thread = threading.Thread( + target=self.handle_stream_output, + args=(run_process.stderr, True), + daemon=True, + ) + + stdout_thread.start() + stderr_thread.start() + + stdout_thread.join() + stderr_thread.join() + + run_process.wait() + self.done.set() + + while True: + if not self.output_queue.empty(): + yield self.output_queue.get() + else: + time.sleep(0.1) + try: + output = self.output_queue.get(timeout=0.3) + yield output + except queue.Empty: + if self.done.is_set(): + for _ in range(3): + if not self.output_queue.empty(): + yield self.output_queue.get() + time.sleep(0.2) + break + + except Exception as e: + yield { + "type": "console", + "format": "output", + "content": f"{traceback.format_exc()}" + } + finally: + # Clean up the generated Java files + if os.path.exists(file_name): + os.remove(file_name) + class_file = file_name.replace(".java", ".class") + if os.path.exists(class_file): + os.remove(class_file) + +def preprocess_java(code): + """ + Add active line markers + Add end of execution marker + """ + lines = code.split("\n") + processed_lines = [] + + for i, line in enumerate(lines, 1): + # Add active line print + processed_lines.append(f'System.out.println("##active_line{i}##");') + processed_lines.append(line) + + # Join lines to form the processed code + code = "\n".join(processed_lines) + + # Add end of execution marker + code += '\nSystem.out.println("##end_of_execution##");' + return code diff --git a/interpreter/core/computer/terminal/terminal.py b/interpreter/core/computer/terminal/terminal.py index fa34a3f3af..7fc870698c 100644 --- a/interpreter/core/computer/terminal/terminal.py +++ b/interpreter/core/computer/terminal/terminal.py @@ -10,6 +10,7 @@ from .languages.react import React from .languages.ruby import Ruby from .languages.shell import Shell +from .languages.java import Java # Should this be renamed to OS or System? @@ -27,6 +28,7 @@ def __init__(self, computer): R, PowerShell, React, + Java, ] self._active_languages = {} From 663ef3399be540e1386dc75b6b2ab33e00bc015e Mon Sep 17 00:00:00 2001 From: CyanideByte Date: Tue, 25 Jun 2024 14:40:23 -0700 Subject: [PATCH 2/2] Fixed removing newlines --- interpreter/core/computer/terminal/languages/java.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interpreter/core/computer/terminal/languages/java.py b/interpreter/core/computer/terminal/languages/java.py index 5eb46b8cf3..c3ac31f39e 100644 --- a/interpreter/core/computer/terminal/languages/java.py +++ b/interpreter/core/computer/terminal/languages/java.py @@ -45,8 +45,8 @@ def run(self, code): class_name = match.group(1) file_name = f"{class_name}.java" - # Write the Java code to a file - with open(file_name, "w") as file: + # Write the Java code to a file, preserving newlines + with open(file_name, "w", newline='\n') as file: file.write(code) # Compile the Java code