Skip to content

Commit 3fab198

Browse files
authored
Merge pull request #1326 from CyanideByte/java-language
Java language exeution support
2 parents da91a72 + 663ef33 commit 3fab198

File tree

2 files changed

+148
-0
lines changed

2 files changed

+148
-0
lines changed
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
import os
2+
import queue
3+
import re
4+
import subprocess
5+
import threading
6+
import time
7+
import traceback
8+
from .subprocess_language import SubprocessLanguage
9+
10+
class Java(SubprocessLanguage):
11+
file_extension = "java"
12+
name = "Java"
13+
14+
def __init__(self):
15+
super().__init__()
16+
self.start_cmd = None # We will handle the start command in the run method
17+
18+
def preprocess_code(self, code):
19+
return preprocess_java(code)
20+
21+
def line_postprocessor(self, line):
22+
# Clean up output from javac and java
23+
return line.strip()
24+
25+
def detect_active_line(self, line):
26+
if "##active_line" in line:
27+
return int(line.split("##active_line")[1].split("##")[0])
28+
return None
29+
30+
def detect_end_of_execution(self, line):
31+
return "##end_of_execution##" in line
32+
33+
def run(self, code):
34+
try:
35+
# Extract the class name from the code
36+
match = re.search(r'class\s+(\w+)', code)
37+
if not match:
38+
yield {
39+
"type": "console",
40+
"format": "output",
41+
"content": "Error: No class definition found in the provided code."
42+
}
43+
return
44+
45+
class_name = match.group(1)
46+
file_name = f"{class_name}.java"
47+
48+
# Write the Java code to a file, preserving newlines
49+
with open(file_name, "w", newline='\n') as file:
50+
file.write(code)
51+
52+
# Compile the Java code
53+
compile_process = subprocess.Popen(
54+
["javac", file_name],
55+
stdout=subprocess.PIPE,
56+
stderr=subprocess.PIPE,
57+
text=True
58+
)
59+
60+
stdout, stderr = compile_process.communicate()
61+
62+
if compile_process.returncode != 0:
63+
yield {
64+
"type": "console",
65+
"format": "output",
66+
"content": f"Compilation Error:\n{stderr}"
67+
}
68+
return
69+
70+
# Run the compiled Java code
71+
run_process = subprocess.Popen(
72+
["java", class_name],
73+
stdout=subprocess.PIPE,
74+
stderr=subprocess.PIPE,
75+
text=True
76+
)
77+
78+
stdout_thread = threading.Thread(
79+
target=self.handle_stream_output,
80+
args=(run_process.stdout, False),
81+
daemon=True,
82+
)
83+
stderr_thread = threading.Thread(
84+
target=self.handle_stream_output,
85+
args=(run_process.stderr, True),
86+
daemon=True,
87+
)
88+
89+
stdout_thread.start()
90+
stderr_thread.start()
91+
92+
stdout_thread.join()
93+
stderr_thread.join()
94+
95+
run_process.wait()
96+
self.done.set()
97+
98+
while True:
99+
if not self.output_queue.empty():
100+
yield self.output_queue.get()
101+
else:
102+
time.sleep(0.1)
103+
try:
104+
output = self.output_queue.get(timeout=0.3)
105+
yield output
106+
except queue.Empty:
107+
if self.done.is_set():
108+
for _ in range(3):
109+
if not self.output_queue.empty():
110+
yield self.output_queue.get()
111+
time.sleep(0.2)
112+
break
113+
114+
except Exception as e:
115+
yield {
116+
"type": "console",
117+
"format": "output",
118+
"content": f"{traceback.format_exc()}"
119+
}
120+
finally:
121+
# Clean up the generated Java files
122+
if os.path.exists(file_name):
123+
os.remove(file_name)
124+
class_file = file_name.replace(".java", ".class")
125+
if os.path.exists(class_file):
126+
os.remove(class_file)
127+
128+
def preprocess_java(code):
129+
"""
130+
Add active line markers
131+
Add end of execution marker
132+
"""
133+
lines = code.split("\n")
134+
processed_lines = []
135+
136+
for i, line in enumerate(lines, 1):
137+
# Add active line print
138+
processed_lines.append(f'System.out.println("##active_line{i}##");')
139+
processed_lines.append(line)
140+
141+
# Join lines to form the processed code
142+
code = "\n".join(processed_lines)
143+
144+
# Add end of execution marker
145+
code += '\nSystem.out.println("##end_of_execution##");'
146+
return code

interpreter/core/computer/terminal/terminal.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from .languages.react import React
1212
from .languages.ruby import Ruby
1313
from .languages.shell import Shell
14+
from .languages.java import Java
1415

1516
# Should this be renamed to OS or System?
1617

@@ -28,6 +29,7 @@ def __init__(self, computer):
2829
R,
2930
PowerShell,
3031
React,
32+
Java,
3133
]
3234
self._active_languages = {}
3335

0 commit comments

Comments
 (0)