Files
Construction-of-Compilers/Project-02/main.py
Jan-Niclas Loosen 346c3c8ffd needs refactoring
2025-11-20 15:33:16 +01:00

128 lines
3.8 KiB
Python

import triplayacc as yacc
import triplalex as lex
import syntax
from pathlib import Path
from graphviz import Source
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
# ------------------------------------------------------------
# Utility: rendering DOT → PNG → matplotlib
# ------------------------------------------------------------
def render_ast(dotfile: str, outfile: str = "ast.png"):
with open(dotfile) as f:
dot_data = f.read()
src = Source(dot_data)
src.render(outfile.replace(".png", ""), format="png", cleanup=True)
# Show via matplotlib only
img = mpimg.imread(outfile)
plt.imshow(img)
plt.axis("off")
plt.show()
# ------------------------------------------------------------
# Pretty print AST
# ------------------------------------------------------------
def pretty_print(node, indent=0):
prefix = " " * indent
print(f"{prefix}{type(node).__name__}")
for key, value in node.__dict__.items():
if key == "pp":
continue
if isinstance(value, list):
print(f"{prefix} {key}: [")
for v in value:
if isinstance(v, syntax.EXPRESSION):
pretty_print(v, indent + 4)
else:
print(" " * (indent + 4) + str(v))
print(f"{prefix} ]")
elif isinstance(value, syntax.EXPRESSION):
print(f"{prefix} {key}:")
pretty_print(value, indent + 4)
else:
print(f"{prefix} {key}: {value}")
# Ask for a choice input
def prompt_choice(prompt: str, options: list) -> int:
print(prompt)
for i, opt in enumerate(options, 1):
print(f" {i}. {opt}")
while True:
try:
s = input(f"Enter choice (1-{len(options)}): ").strip()
idx = int(s) - 1
if 0 <= idx < len(options):
return idx
except Exception:
pass
print(f"Invalid. Enter a number between 1 and {len(options)}.")
# Ask for a yes/no input
def prompt_yesno(question: str, default="y"):
s = input(f"{question} (y/n) [{default}]: ").strip().lower()
if not s:
s = default
return s.startswith("y")
# Ask for a value input
def prompt_value(prompt: str, default: str):
s = input(f"{prompt} [{default}]: ").strip()
return s if s else default
# Search for known .tripla files
def search_programs():
base = Path(__file__).parent / "triplaprograms"
if not base.exists():
return []
return sorted([f for f in base.glob("*.tripla")])
if __name__ == "__main__":
print("\nTRIPLA Parser Tool")
while True:
choice = prompt_choice("\nSelect action:",["Parse TRIPLA program", "Exit"])
if choice == 1:
print("\nBye.")
break
else:
programs = search_programs()
if not programs:
print("\nNo .tripla files found.")
continue
idx = prompt_choice("\nSelect program to parse:", [p.name for p in programs])
path = programs[idx]
source = path.read_text()
ast = yacc.parser.parse(source)
# Pretty print
if prompt_yesno("\nPretty-print AST?"):
print("")
pretty_print(ast)
# Export DOT
dot_name = prompt_value("\nSave DOT as", "ast.dot")
if not dot_name.endswith(".dot"):
dot_name += ".dot"
ast.to_dot(ast, dot_name)
# Show graph
if prompt_yesno("Display AST diagram?"):
render_ast(dot_name, "ast.png")
print("Rendered AST to ast.png")
print("\nDone.")