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.")