Finish refactoring
This commit is contained in:
BIN
Project-02/Source.gv.png
Normal file
BIN
Project-02/Source.gv.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
@@ -6,52 +6,47 @@ from pathlib import Path
|
|||||||
from graphviz import Source
|
from graphviz import Source
|
||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
import matplotlib.image as mpimg
|
import matplotlib.image as mpimg
|
||||||
|
import os
|
||||||
|
|
||||||
|
import matplotlib
|
||||||
|
matplotlib.use("TkAgg")
|
||||||
|
|
||||||
# ------------------------------------------------------------
|
def render_ast_from_string(dot_string: str):
|
||||||
# Utility: rendering DOT → PNG → matplotlib
|
# Set DPI for PNG
|
||||||
# ------------------------------------------------------------
|
os.environ["GV_FILE_DPI"] = "300"
|
||||||
|
|
||||||
def render_ast(dotfile: str, outfile: str = "ast.png"):
|
src = Source(dot_string, format="png", engine="dot")
|
||||||
with open(dotfile) as f:
|
png_path = src.render(cleanup=True)
|
||||||
dot_data = f.read()
|
|
||||||
|
|
||||||
src = Source(dot_data)
|
img = mpimg.imread(png_path)
|
||||||
src.render(outfile.replace(".png", ""), format="png", cleanup=True)
|
|
||||||
|
fig = plt.figure(figsize=(12, 12))
|
||||||
|
fig.canvas.manager.window.wm_title("TRIPLA AST Viewer")
|
||||||
|
|
||||||
# Show via matplotlib only
|
|
||||||
img = mpimg.imread(outfile)
|
|
||||||
plt.imshow(img)
|
plt.imshow(img)
|
||||||
plt.axis("off")
|
plt.axis("off")
|
||||||
plt.show()
|
plt.show()
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------
|
|
||||||
# Pretty print AST
|
|
||||||
# ------------------------------------------------------------
|
|
||||||
|
|
||||||
def pretty_print(node, indent=0):
|
def pretty_print(node, indent=0):
|
||||||
prefix = " " * indent
|
prefix = " " * indent
|
||||||
print(f"{prefix}{type(node).__name__}")
|
print(f"{prefix}{type(node).__name__}:")
|
||||||
|
|
||||||
for key, value in node.__dict__.items():
|
for key, value in node.__dict__.items():
|
||||||
if key == "pp":
|
if isinstance(value, syntax.EXPRESSION):
|
||||||
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)
|
pretty_print(value, indent + 4)
|
||||||
|
|
||||||
|
elif isinstance(value, list):
|
||||||
|
print(f"{prefix} {key}: [")
|
||||||
|
for element in value:
|
||||||
|
if isinstance(element, syntax.EXPRESSION):
|
||||||
|
pretty_print(element, indent + 4)
|
||||||
|
else:
|
||||||
|
print(" " * (indent + 4) + str(element))
|
||||||
|
print(f"{prefix} ]")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
print(f"{prefix} {key}: {value}")
|
print(f"{prefix} {key}: {value}")
|
||||||
|
|
||||||
# Ask for a choice input
|
|
||||||
def prompt_choice(prompt: str, options: list) -> int:
|
def prompt_choice(prompt: str, options: list) -> int:
|
||||||
print(prompt)
|
print(prompt)
|
||||||
for i, opt in enumerate(options, 1):
|
for i, opt in enumerate(options, 1):
|
||||||
@@ -65,21 +60,14 @@ def prompt_choice(prompt: str, options: list) -> int:
|
|||||||
return idx
|
return idx
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
print(f"Invalid. Enter a number between 1 and {len(options)}.")
|
print(f"Invalid. Enter a number between 1-{len(options)}.")
|
||||||
|
|
||||||
# Ask for a yes/no input
|
|
||||||
def prompt_yesno(question: str, default="y"):
|
def prompt_yesno(question: str, default="y"):
|
||||||
s = input(f"{question} (y/n) [{default}]: ").strip().lower()
|
s = input(f"{question} (y/n) [{default}]: ").strip().lower()
|
||||||
if not s:
|
if not s:
|
||||||
s = default
|
s = default
|
||||||
return s.startswith("y")
|
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():
|
def search_programs():
|
||||||
base = Path(__file__).parent / "triplaprograms"
|
base = Path(__file__).parent / "triplaprograms"
|
||||||
if not base.exists():
|
if not base.exists():
|
||||||
@@ -90,38 +78,30 @@ if __name__ == "__main__":
|
|||||||
print("\nTRIPLA Parser Tool")
|
print("\nTRIPLA Parser Tool")
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
choice = prompt_choice("\nSelect action:",["Parse TRIPLA program", "Exit"])
|
choice = prompt_choice("\nSelect action:", ["Parse .tripla", "Exit"])
|
||||||
|
|
||||||
if choice == 1:
|
if choice == 1:
|
||||||
print("\nBye.")
|
print("\nBye Bye.")
|
||||||
break
|
break
|
||||||
|
|
||||||
else:
|
programs = search_programs()
|
||||||
programs = search_programs()
|
if not programs:
|
||||||
if not programs:
|
print("\nNo .tripla files found.")
|
||||||
print("\nNo .tripla files found.")
|
continue
|
||||||
continue
|
|
||||||
|
|
||||||
idx = prompt_choice("\nSelect program to parse:", [p.name for p in programs])
|
idx = prompt_choice("\nSelect program to parse:", [p.name for p in programs])
|
||||||
path = programs[idx]
|
path = programs[idx]
|
||||||
|
|
||||||
source = path.read_text()
|
source = path.read_text()
|
||||||
ast = yacc.parser.parse(source)
|
ast = yacc.parser.parse(source)
|
||||||
|
|
||||||
# Pretty print
|
# Pretty print
|
||||||
if prompt_yesno("\nPretty-print AST?"):
|
if prompt_yesno("\nPretty-print AST?"):
|
||||||
print("")
|
print("")
|
||||||
pretty_print(ast)
|
pretty_print(ast)
|
||||||
|
|
||||||
# Export DOT
|
# Show AST graph
|
||||||
dot_name = prompt_value("\nSave DOT as", "ast.dot")
|
if prompt_yesno("Display AST diagram?"):
|
||||||
if not dot_name.endswith(".dot"):
|
dot_str = ast.to_dot()
|
||||||
dot_name += ".dot"
|
render_ast_from_string(dot_str)
|
||||||
ast.to_dot(ast, dot_name)
|
print("Rendered AST diagram.")
|
||||||
|
|
||||||
# Show graph
|
|
||||||
if prompt_yesno("Display AST diagram?"):
|
|
||||||
render_ast(dot_name, "ast.png")
|
|
||||||
print("Rendered AST to ast.png")
|
|
||||||
|
|
||||||
print("\nDone.")
|
|
||||||
@@ -9,17 +9,6 @@ class EXPRESSION:
|
|||||||
def copy():
|
def copy():
|
||||||
return EXPRESSION()
|
return EXPRESSION()
|
||||||
|
|
||||||
def allNodes(self):
|
|
||||||
ret = [self]
|
|
||||||
for node in (self.__getattribute__(a) for a in self.__dict__.keys()):
|
|
||||||
if isinstance(node, EXPRESSION):
|
|
||||||
ret += node.allNodes()
|
|
||||||
if isinstance(node, list):
|
|
||||||
for n in node:
|
|
||||||
if isinstance(n, EXPRESSION):
|
|
||||||
ret += n.allNodes()
|
|
||||||
return ret
|
|
||||||
|
|
||||||
def children(self):
|
def children(self):
|
||||||
"""Return a list of (name, childNode)."""
|
"""Return a list of (name, childNode)."""
|
||||||
out = []
|
out = []
|
||||||
@@ -31,33 +20,55 @@ class EXPRESSION:
|
|||||||
elif isinstance(value, list):
|
elif isinstance(value, list):
|
||||||
for i, elem in enumerate(value):
|
for i, elem in enumerate(value):
|
||||||
if isinstance(elem, EXPRESSION):
|
if isinstance(elem, EXPRESSION):
|
||||||
out.append((f"{key}[{i}]", elem))
|
out.append((f"{key}{i}", elem))
|
||||||
return out
|
return out
|
||||||
|
|
||||||
def to_dot(self, out):
|
def to_dot(self, visited=None, root=True):
|
||||||
# node label is class name or class name + value
|
if visited is None:
|
||||||
|
visited = set()
|
||||||
|
|
||||||
|
# Prevent infinite recursion
|
||||||
|
if id(self) in visited:
|
||||||
|
return ""
|
||||||
|
visited.add(id(self))
|
||||||
|
|
||||||
|
parts = []
|
||||||
|
|
||||||
|
# Add a header at the root node
|
||||||
|
if root:
|
||||||
|
parts.append("digraph AST {\n")
|
||||||
|
|
||||||
|
# Append to label
|
||||||
label = type(self).__name__
|
label = type(self).__name__
|
||||||
if hasattr(self, "operator"): # AOP/EQOP/COMP/LOP
|
if hasattr(self, "operator"):
|
||||||
label += f"({self.operator})"
|
label += f"({self.operator})"
|
||||||
if hasattr(self, "name"): # ID
|
if hasattr(self, "name"):
|
||||||
label += f"({self.name})"
|
label += f"({self.name})"
|
||||||
if hasattr(self, "value"): # CONST
|
if hasattr(self, "value"):
|
||||||
label += f"({self.value})"
|
label += f"({self.value})"
|
||||||
|
|
||||||
out.write(f' node{self.pp} [label="{label}"];\n')
|
parts.append(f' node{self.pp} [label="{label}"];\n')
|
||||||
|
|
||||||
|
# Draw edges
|
||||||
|
for edge_name, child in self.children():
|
||||||
|
parts.append(f' node{self.pp} -> node{child.pp} [label="{edge_name}"];\n')
|
||||||
|
parts.append(child.to_dot(visited, root=False))
|
||||||
|
|
||||||
|
# Add footer at the root node
|
||||||
|
if root:
|
||||||
|
parts.append("}\n")
|
||||||
|
|
||||||
|
return "".join(parts)
|
||||||
|
|
||||||
for (edge_name, child) in self.children():
|
|
||||||
out.write(f' node{self.pp} -> node{child.pp} [label="{edge_name}"];\n')
|
|
||||||
child.to_dot(out)
|
|
||||||
|
|
||||||
class LET(EXPRESSION):
|
class LET(EXPRESSION):
|
||||||
def __init__(self, declarations, body):
|
def __init__(self, decls, body):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.declarations = declarations
|
self.decl = decls
|
||||||
self.body = body
|
self.body = body
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "let " + ", ".join(str(d) for d in self.declarations) + " in " + str(self.body)
|
return "let " + ", ".join(str(d) for d in self.decl) + " in " + str(self.body)
|
||||||
|
|
||||||
class DECL(EXPRESSION):
|
class DECL(EXPRESSION):
|
||||||
def __init__(self, f_name, params, body):
|
def __init__(self, f_name, params, body):
|
||||||
@@ -70,13 +81,13 @@ class DECL(EXPRESSION):
|
|||||||
return f"{self.f_name}({self.params}) {{ {self.body} }}"
|
return f"{self.f_name}({self.params}) {{ {self.body} }}"
|
||||||
|
|
||||||
class CALL(EXPRESSION):
|
class CALL(EXPRESSION):
|
||||||
def __init__(self, f_name, arguments):
|
def __init__(self, f_name, args):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.f_name = f_name
|
self.f_name = f_name
|
||||||
self.arguments = arguments
|
self.arg = args
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.f_name + "(" + ",".join(str(a) for a in self.arguments) + ")"
|
return self.f_name + "(" + ",".join(str(a) for a in self.arg) + ")"
|
||||||
|
|
||||||
class ID(EXPRESSION):
|
class ID(EXPRESSION):
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
@@ -135,13 +146,13 @@ class LOP(EXPRESSION):
|
|||||||
return "(" + str(self.arg1) + " " + str(self.operator) + " " + str(self.arg2) + ")"
|
return "(" + str(self.arg1) + " " + str(self.operator) + " " + str(self.arg2) + ")"
|
||||||
|
|
||||||
class ASSIGN(EXPRESSION):
|
class ASSIGN(EXPRESSION):
|
||||||
def __init__(self, variable, expression):
|
def __init__(self, var, expr):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.variable = variable
|
self.var = var
|
||||||
self.expression = expression
|
self.expr = expr
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.variable.name + " = " + str(self.expression)
|
return self.var.name + " = " + str(self.expr)
|
||||||
|
|
||||||
class SEQ(EXPRESSION):
|
class SEQ(EXPRESSION):
|
||||||
def __init__(self, exp1, exp2):
|
def __init__(self, exp1, exp2):
|
||||||
@@ -153,20 +164,20 @@ class SEQ(EXPRESSION):
|
|||||||
return str(self.exp1) + "; " + str(self.exp2)
|
return str(self.exp1) + "; " + str(self.exp2)
|
||||||
|
|
||||||
class IF(EXPRESSION):
|
class IF(EXPRESSION):
|
||||||
def __init__(self, condition, exp1, exp2):
|
def __init__(self, cond, exp1, exp2):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.condition = condition
|
self.cond = cond
|
||||||
self.exp1 = exp1
|
self.exp1 = exp1
|
||||||
self.exp2 = exp2
|
self.exp2 = exp2
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "if (" + str(self.condition) + ") then { " + str(self.exp1) + " } else { " + str(self.exp2) + " }"
|
return "if (" + str(self.cond) + ") then { " + str(self.exp1) + " } else { " + str(self.exp2) + " }"
|
||||||
|
|
||||||
class WHILE(EXPRESSION):
|
class WHILE(EXPRESSION):
|
||||||
def __init__(self, condition, body):
|
def __init__(self, cond, body):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.condition = condition
|
self.cond = cond
|
||||||
self.body = body
|
self.body = body
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "while (" + str(self.condition) + ") do { " + str(self.body) + " }"
|
return "while (" + str(self.cond) + ") do { " + str(self.body) + " }"
|
||||||
Reference in New Issue
Block a user