Files
compilers/python-src/compiler/__main__.py
T

129 lines
4.1 KiB
Python

from base64 import b64encode
import json
import re
import sys
from socketserver import ForkingTCPServer, StreamRequestHandler
from traceback import format_exception
from typing import Any
import subprocess
def call_compiler(source_code: str) -> bytes:
# *** TODO ***
# Call your compiler here and return the compiled executable.
# Raise an exception on compilation error.
# *** TODO ***
print("Compiling:", "./compilers_compiler", source_code)
import uuid
# Give the compiler a file to write the result to
filename = "compile-result-" + str(uuid.uuid4())
response = subprocess.run(["./compilers_compiler", source_code, filename], check=True, capture_output=True).stdout
print(response)
try:
# Read the updated file and fetch compiler result
with open(filename,"rb") as file_content:
content = file_content.read()
print(content)
except FileNotFoundError as e:
print("File not found", filename, e)
content = b""
return content
def main() -> int:
# === Option parsing ===
command: str | None = None
input_file: str | None = None
output_file: str | None = None
host = "127.0.0.1"
port = 3000
for arg in sys.argv[1:]:
if (m := re.fullmatch(r'--output=(.+)', arg)) is not None:
output_file = m[1]
elif (m := re.fullmatch(r'--host=(.+)', arg)) is not None:
host = m[1]
elif (m := re.fullmatch(r'--port=(.+)', arg)) is not None:
port = int(m[1])
elif arg.startswith('-'):
raise Exception(f"Unknown argument: {arg}")
elif command is None:
command = arg
elif input_file is None:
input_file = arg
else:
raise Exception("Multiple input files not supported")
valid_commands = ['compile', 'serve']
if command is None:
print(f"Error: command argument missing. Valid commands: {', '.join(valid_commands)}", file=sys.stderr)
return 1
if command not in valid_commands:
print(f"Error: unknown command: {command}", file=sys.stderr)
return 1
def read_source_code() -> str:
if input_file is not None:
with open(input_file) as f:
return f.read()
else:
return sys.stdin.read()
# === Command implementations ===
if command == 'compile':
source_code = read_source_code()
if output_file is None:
raise Exception("Output file flag --output=... required")
executable = call_compiler(source_code)
with open(output_file, 'wb') as f:
f.write(executable)
elif command == 'serve':
try:
run_server(host, port)
except KeyboardInterrupt:
pass
return 0
def run_server(host: str, port: int) -> None:
class Server(ForkingTCPServer):
allow_reuse_address = True
request_queue_size = 32
class Handler(StreamRequestHandler):
def handle(self) -> None:
print("Got a request?")
result: dict[str, Any] = {}
try:
input_str = self.rfile.read().decode()
print("INPUTSTR:", input_str)
input = json.loads(input_str)
print("JSONINPUT", input)
if input["command"] == "compile":
source_code = input["code"]
executable = call_compiler(source_code)
result["program"] = b64encode(executable).decode()
elif input["command"] == "ping":
pass
else:
result["error"] = "Unknown command: " + input['command']
except Exception as e:
print("ERROR!!", e)
result["error"] = "".join(format_exception(e))
result_str = json.dumps(result)
print(result_str)
self.request.sendall(str.encode(result_str))
print(f"Starting TCP server at {host}:{port}")
with Server((host, port), Handler) as server:
server.serve_forever()
if __name__ == '__main__':
sys.exit(main())