diff --git a/m5r_interpreter.py b/m5r_interpreter.py index 167af17..fdc6459 100644 --- a/m5r_interpreter.py +++ b/m5r_interpreter.py @@ -2,72 +2,124 @@ import re import subprocess import tempfile import os +import shutil + +def safe_run(cmd, timeout=8, **sub_kwargs): + try: + result = subprocess.run( + cmd, + capture_output=True, + text=True, + timeout=timeout, + **sub_kwargs + ) + out = result.stdout + err = result.stderr + rc = result.returncode + except subprocess.TimeoutExpired: + return "", "[ERROR: timed out]", 1 + except FileNotFoundError: + return "", f"[ERROR: Not installed: {cmd[0]}]", 1 + except Exception as e: + return "", f"[ERROR: {e}]", 1 + else: + return out, err, rc + +def lang_header(name): + s = f"\n====== [{name.upper()} BLOCK] ======\n" + return s def interpret(source): outputs = [] - # Python segments + # Python for segment in re.findall(r'<\?py(.*?)\?>', source, re.S): code = segment.strip() with tempfile.NamedTemporaryFile('w', delete=False, suffix='.py') as f: f.write(code) path = f.name - res = subprocess.run(['python', path], capture_output=True, text=True) - outputs.append(res.stdout) + out, err, rc = safe_run(['python', path]) + outputs.append(lang_header("python") + out) + if err or rc: + outputs.append(f"[PYTHON ERROR]\n{err}") os.unlink(path) - # JavaScript segments + # JavaScript for segment in re.findall(r'<\?js(.*?)\?>', source, re.S): code = segment.strip() with tempfile.NamedTemporaryFile('w', delete=False, suffix='.js') as f: f.write(code) path = f.name - res = subprocess.run(['node', path], capture_output=True, text=True) - outputs.append(res.stdout) + out, err, rc = safe_run(['node', path]) + outputs.append(lang_header("js") + out) + if err or rc: + outputs.append(f"[JS ERROR]\n{err}") os.unlink(path) - # PHP segments + # PHP for segment in re.findall(r'<\?php(.*?)\?>', source, re.S): code = segment.strip() with tempfile.NamedTemporaryFile('w', delete=False, suffix='.php') as f: f.write("") path = f.name - res = subprocess.run(['php', path], capture_output=True, text=True) - outputs.append(res.stdout) + out, err, rc = safe_run(['php', path]) + outputs.append(lang_header("php") + out) + if err or rc: + outputs.append(f"[PHP ERROR]\n{err}") os.unlink(path) - # CSS segments (print as message, not executed) + # CSS (just pretty print) for segment in re.findall(r'<\?css(.*?)\?>', source, re.S): css = segment.strip() - outputs.append(f"[CSS Styling Loaded]\n{css}\n") + outputs.append(lang_header("css") + "[CSS Styling Loaded]\n" + css + "\n") - # C# segments (assumes csc installed) + # Bash/Shell + for segment in re.findall(r'<\?sh(.*?)\?>', source, re.S): + code = segment.strip() + with tempfile.NamedTemporaryFile('w', delete=False, suffix='.sh') as f: + f.write(code) + path = f.name + os.chmod(path, 0o700) + out, err, rc = safe_run(['bash', path]) + outputs.append(lang_header("shell") + out) + if err or rc: + outputs.append(f"[BASH ERROR]\n{err}") + os.unlink(path) + + # C# for segment in re.findall(r'<\?cs(.*?)\?>', source, re.S): code = segment.strip() with tempfile.NamedTemporaryFile('w', delete=False, suffix='.cs') as f: f.write(f"using System; class Program {{ static void Main() {{ {code} }} }}") path = f.name exe_path = path.replace('.cs', '.exe') - compile_res = subprocess.run(['csc', '/nologo', '/out:' + exe_path, path], capture_output=True, text=True) - if os.path.exists(exe_path): - run_res = subprocess.run([exe_path], capture_output=True, text=True) - outputs.append(run_res.stdout) + compile_out, compile_err, compile_rc = safe_run(['csc', '/nologo', '/out:' + exe_path, path]) + if not os.path.exists(exe_path) or compile_rc: + outputs.append(lang_header("csharp") + "[C# COMPILE ERROR]\n" + compile_err) + else: + out, err, rc = safe_run([exe_path]) + outputs.append(lang_header("csharp") + out) + if err or rc: + outputs.append(f"[C# ERROR]\n{err}") os.unlink(exe_path) os.unlink(path) - # C++ segments (assumes g++ installed) + # C++ (requires g++) for segment in re.findall(r'<\?cpp(.*?)\?>', source, re.S): code = segment.strip() with tempfile.NamedTemporaryFile('w', delete=False, suffix='.cpp') as f: f.write(f"#include \nusing namespace std;\nint main() {{ {code} return 0; }}") path = f.name exe_path = path.replace('.cpp', '') - compile_res = subprocess.run(['g++', path, '-o', exe_path], capture_output=True, text=True) - if os.path.exists(exe_path): - run_res = subprocess.run([exe_path], capture_output=True, text=True) - outputs.append(run_res.stdout) + compile_out, compile_err, compile_rc = safe_run(['g++', path, '-o', exe_path]) + if not os.path.exists(exe_path) or compile_rc: + outputs.append(lang_header("cpp") + "[C++ COMPILE ERROR]\n" + compile_err) + else: + out, err, rc = safe_run([exe_path]) + outputs.append(lang_header("cpp") + out) + if err or rc: + outputs.append(f"[C++ ERROR]\n{err}") os.unlink(exe_path) os.unlink(path) - # Print combined output print(''.join(outputs))