needs refactoring

This commit is contained in:
Jan-Niclas Loosen
2025-11-20 15:33:16 +01:00
parent eb362896fd
commit 346c3c8ffd
4 changed files with 125 additions and 50 deletions

View File

@@ -1,52 +1,127 @@
# (c) Stephan Diehl / Updated for AST display by ChatGPT
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
def display_ast_graph(dotfile="ast.dot"):
"""Render DOT → PNG and display via matplotlib (always)."""
# ------------------------------------------------------------
# Utility: rendering DOT → PNG → matplotlib
# ------------------------------------------------------------
# Read DOT
def render_ast(dotfile: str, outfile: str = "ast.png"):
with open(dotfile) as f:
dot_data = f.read()
# Render using Graphviz
src = Source(dot_data)
src.render("ast", format="png", cleanup=True)
src.render(outfile.replace(".png", ""), format="png", cleanup=True)
# Load rendered PNG
img = mpimg.imread("ast.png")
# Show in matplotlib window
# Show via matplotlib only
img = mpimg.imread(outfile)
plt.imshow(img)
plt.axis('off')
plt.axis("off")
plt.show()
def test_parser(filepath):
# Load program
with open(filepath) as f:
source = f.read()
# ------------------------------------------------------------
# Pretty print AST
# ------------------------------------------------------------
# Parse input
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)
# Print plain-text version of the AST (optional)
print("AST object:")
print(ast)
# Pretty print
if prompt_yesno("\nPretty-print AST?"):
print("")
pretty_print(ast)
# Export DOT file
syntax.export_dot(ast, "ast.dot")
# 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)
# Display AST diagram
print("Rendering AST diagram with matplotlib...")
display_ast_graph("ast.dot")
# Show graph
if prompt_yesno("Display AST diagram?"):
render_ast(dot_name, "ast.png")
print("Rendered AST to ast.png")
if __name__ == '__main__':
test_parser('triplaprograms/or.tripla')
print("\nDone.")

View File

@@ -1,5 +1,3 @@
# (c) Stephan Diehl, University of Trier, Germany, 2025
class EXPRESSION:
pp_count = 0
@@ -69,7 +67,7 @@ class DECL(EXPRESSION):
self.body = body
def __str__(self):
return f"{self.f_name}(" + ",".join(str(p) for p in self.params) + ") { " + str(self.body) + " }"
return f"{self.f_name}({self.params}) {{ {self.body} }}"
class CALL(EXPRESSION):
def __init__(self, f_name, arguments):
@@ -172,19 +170,3 @@ class WHILE(EXPRESSION):
def __str__(self):
return "while (" + str(self.condition) + ") do { " + str(self.body) + " }"
def pretty_print(clas, indent=0):
print(' ' * indent + type(clas).__name__ + ':')
indent += 4
for k, v in clas.__dict__.items():
if isinstance(v, EXPRESSION):
pretty_print(v, indent)
else:
print(' ' * indent + f"{k}: {v}")
def export_dot(ast, filename="ast.dot"):
with open(filename, "w") as f:
f.write("digraph AST {\n")
f.write(" node [shape=box];\n")
ast.to_dot(f)
f.write("}\n")

18
Project-02/test.py Normal file
View File

@@ -0,0 +1,18 @@
# This is a sample Python script for testing your TRIPLA parser.
# In PyCharm press Umschalt+F10 to execute it.
import triplayacc as yacc
import triplalex as lex
def test_parser(name):
source = "\n".join(open(name).readlines())
ast = yacc.parser.parse(source) # ,debug=True)
print("AST:")
print(ast)
# Press the green button in the gutter to run the script.
if __name__ == '__main__':
test_parser('triplaprograms/complex.tripla')
# See PyCharm help at https://www.jetbrains.com/help/pycharm/

View File

@@ -79,11 +79,11 @@ def p_A_multiple(p):
def p_D_single(p):
'D : ID LPAREN V RPAREN LBRACE E RBRACE'
p[0] = ast.DECL(p[1], p[3], p[6])
p[0] = [ast.DECL(p[1], p[3], p[6])]
def p_D_concat(p):
'D : D D'
p[0] = p[1] + [p[2]] if isinstance(p[1], list) else [p[1], p[2]]
p[0] = p[1] + p[2]
# ------------------------------------------------------------
# Rules for V