Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 146 additions & 0 deletions interpreter/core/computer/terminal/languages/java.py
Original file line number Diff line number Diff line change
@@ -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, preserving newlines
with open(file_name, "w", newline='\n') 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
2 changes: 2 additions & 0 deletions interpreter/core/computer/terminal/terminal.py
Original file line number Diff line number Diff line change
Expand Up @@ -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?

Expand All @@ -27,6 +28,7 @@ def __init__(self, computer):
R,
PowerShell,
React,
Java,
]
self._active_languages = {}

Expand Down