128 lines
3.8 KiB
Python
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.")
|