Files
Construction-of-Compilers/Project-02-03-04/main.py
2026-01-23 13:01:50 +01:00

172 lines
5.2 KiB
Python

import os
import tkinter as tk
from pathlib import Path
import matplotlib
import matplotlib.image as mpimg
import matplotlib.pyplot as plt
from graphviz import Source
import lib.console as cnsl
import syntax
import triplayacc as yacc
from cfg.CFG import CFG
from vistram.tram import *
from vistram.vistram import MachineUI
matplotlib.use("TkAgg")
# Assembles the AST into TRAM code
def assemble(ast):
code = ast.code({}, 0)
return code + [halt()]
def make_cfg(ast):
return CFG(ast)
# Renders a diagram of the AST
def render_diagram(dot_string: str):
# Set DPI for PNG
os.environ["GV_FILE_DPI"] = "300"
src = Source(dot_string, format="png", engine="dot")
png_path = src.render(cleanup=True)
img = mpimg.imread(png_path)
fig = plt.figure(figsize=(12, 12))
fig.canvas.manager.window.wm_title("TRIPLA AST Viewer")
plt.imshow(img)
plt.axis("off")
plt.show()
# Pretty prints the AST
def pretty_print(node, indent=0):
prefix = " " * indent
print(f"{prefix}{type(node).__name__}:")
for key, value in node.__dict__.items():
if isinstance(value, syntax.EXPRESSION):
pretty_print(value, indent + 4)
elif isinstance(value, list):
print(f"{prefix} {key}: [")
for element in value:
if isinstance(element, syntax.EXPRESSION):
pretty_print(element, indent + 4)
else:
print(" " * (indent + 4) + str(element))
print(f"{prefix} ]")
else:
print(f"{prefix} {key}: {value}")
if __name__ == "__main__":
print("\nTRIPLA Parser and TRIPLA to TRAM Compiler")
while True:
mode = cnsl.prompt_choice("\nSelect action:", ["Parse .tripla", "Compile .tripla", "CFG for .tripla", "Exit"])
if mode == 3:
print("\nBye Bye.")
break
base = Path(__file__).parent / "triplaprograms"
programs = sorted([f for f in base.glob("*.tripla")])
if not programs:
print("\nNo .tripla files found.")
continue
idx = cnsl.prompt_choice("\nSelect TRIPLA program:", [p.name for p in programs])
path = programs[idx]
source = path.read_text()
ast = yacc.parser.parse(source)
if mode == 0:
# Pretty print
if cnsl.prompt_confirmation("\nPretty-print AST?"):
print("")
pretty_print(ast)
# Export DOT
dot_str = ast.to_dot()
if cnsl.prompt_confirmation("Export AST as .dot file?"):
default = f"{path.stem}.dot"
filename = input(f"Filename [{default}]: ").strip()
if not filename:
filename = default
try:
base_dir = Path(__file__).parent
out_path = base_dir / filename
with open(out_path, "w") as f:
f.write(dot_str)
print(f"Saved DOT file as: {out_path}")
except Exception as e:
print(f"Could not save DOT file: {e}")
# Display AST diagram
if cnsl.prompt_confirmation("Display AST diagram?"):
render_diagram(dot_str)
print("Rendered AST diagram.")
elif mode == 1:
tram_code = assemble(ast)
# Print TRAM code
if cnsl.prompt_confirmation("\nPrint TRAM code to console?"):
print("\nGenerated TRAM code:\n")
for instr in tram_code:
print(instr.toString())
# Save TRAM code
if cnsl.prompt_confirmation("Save TRAM code as .tram file?"):
base_dir = Path(__file__).parent / "tramcodes"
base_dir.mkdir(exist_ok=True)
default = f"{path.stem}.tram"
filename = input(f"Filename [{default}]: ").strip()
if not filename:
filename = default
out_path = base_dir / filename
with open(out_path, "w") as f:
for instr in tram_code:
f.write(instr.toString() + "\n")
print(f"Saved TRAM code to: {out_path}")
# Display TRAM code in Visual TRAM UI
if cnsl.prompt_confirmation("Display TRAM code in Visual TRAM UI?"):
root = tk.Tk()
ui = MachineUI(root)
ui.machine.initial_program = tram_code
ui.machine.reset()
root.mainloop()
elif mode == 2:
cfg = make_cfg(ast)
dot_str = cfg.to_dot()
if cnsl.prompt_confirmation("\nExport CFG as .dot file?"):
default = f"{path.stem}_cfg.dot"
filename = input(f"Filename [{default}]: ").strip()
if not filename:
filename = default
out_path = Path(__file__).parent / 'cfgdots' / filename
with open(out_path, "w") as f:
f.write(dot_str)
print(f"Saved CFG DOT file as: {out_path}")
if cnsl.prompt_confirmation("Display CFG diagram?"):
render_diagram(dot_str)
print("Rendered CFG diagram.")