needs refactoring
This commit is contained in:
@@ -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.")
|
||||
|
||||
@@ -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
18
Project-02/test.py
Normal 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/
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user