From 7ac8889a1db568f536be6a9f8c2a2bbdf2aaf35f Mon Sep 17 00:00:00 2001 From: Jan-Niclas Loosen Date: Thu, 20 Nov 2025 19:19:52 +0100 Subject: [PATCH] Finish refactoring --- Project-02/Source.gv.png | Bin 0 -> 19444 bytes Project-02/main.py | 108 ++++++++++++++++----------------------- Project-02/syntax.py | 85 ++++++++++++++++-------------- 3 files changed, 92 insertions(+), 101 deletions(-) create mode 100644 Project-02/Source.gv.png diff --git a/Project-02/Source.gv.png b/Project-02/Source.gv.png new file mode 100644 index 0000000000000000000000000000000000000000..a3eb0b27be0c3c4160e897a59561488da0b053f2 GIT binary patch literal 19444 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 U2;aPnukj+ODQPPfD_Gq8KaskohyVZp literal 0 HcmV?d00001 diff --git a/Project-02/main.py b/Project-02/main.py index dbd4633..66c30d2 100644 --- a/Project-02/main.py +++ b/Project-02/main.py @@ -6,52 +6,47 @@ from pathlib import Path from graphviz import Source import matplotlib.pyplot as plt import matplotlib.image as mpimg +import os +import matplotlib +matplotlib.use("TkAgg") -# ------------------------------------------------------------ -# Utility: rendering DOT → PNG → matplotlib -# ------------------------------------------------------------ +def render_ast_from_string(dot_string: str): + # Set DPI for PNG + os.environ["GV_FILE_DPI"] = "300" -def render_ast(dotfile: str, outfile: str = "ast.png"): - with open(dotfile) as f: - dot_data = f.read() + src = Source(dot_string, format="png", engine="dot") + png_path = src.render(cleanup=True) - src = Source(dot_data) - src.render(outfile.replace(".png", ""), format="png", cleanup=True) + img = mpimg.imread(png_path) + + 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.axis("off") plt.show() - -# ------------------------------------------------------------ -# Pretty print AST -# ------------------------------------------------------------ - def pretty_print(node, indent=0): prefix = " " * indent - print(f"{prefix}{type(node).__name__}") + 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}:") + if isinstance(value, syntax.EXPRESSION): 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: 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): @@ -65,21 +60,14 @@ def prompt_choice(prompt: str, options: list) -> int: return idx except Exception: 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"): 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(): @@ -90,38 +78,30 @@ if __name__ == "__main__": print("\nTRIPLA Parser Tool") while True: - choice = prompt_choice("\nSelect action:",["Parse TRIPLA program", "Exit"]) + choice = prompt_choice("\nSelect action:", ["Parse .tripla", "Exit"]) if choice == 1: - print("\nBye.") + print("\nBye Bye.") break - else: - programs = search_programs() - if not programs: - print("\nNo .tripla files found.") - continue + 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] + 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) + source = path.read_text() + ast = yacc.parser.parse(source) - # Pretty print - if prompt_yesno("\nPretty-print AST?"): - print("") - pretty_print(ast) + # 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.") + # Show AST graph + if prompt_yesno("Display AST diagram?"): + dot_str = ast.to_dot() + render_ast_from_string(dot_str) + print("Rendered AST diagram.") \ No newline at end of file diff --git a/Project-02/syntax.py b/Project-02/syntax.py index 652e026..9ca58e6 100644 --- a/Project-02/syntax.py +++ b/Project-02/syntax.py @@ -9,17 +9,6 @@ class EXPRESSION: def copy(): 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): """Return a list of (name, childNode).""" out = [] @@ -31,33 +20,55 @@ class EXPRESSION: elif isinstance(value, list): for i, elem in enumerate(value): if isinstance(elem, EXPRESSION): - out.append((f"{key}[{i}]", elem)) + out.append((f"{key}{i}", elem)) return out - def to_dot(self, out): - # node label is class name or class name + value + def to_dot(self, visited=None, root=True): + 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__ - if hasattr(self, "operator"): # AOP/EQOP/COMP/LOP + if hasattr(self, "operator"): label += f"({self.operator})" - if hasattr(self, "name"): # ID + if hasattr(self, "name"): label += f"({self.name})" - if hasattr(self, "value"): # CONST + if hasattr(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): - def __init__(self, declarations, body): + def __init__(self, decls, body): super().__init__() - self.declarations = declarations + self.decl = decls self.body = body 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): def __init__(self, f_name, params, body): @@ -70,13 +81,13 @@ class DECL(EXPRESSION): return f"{self.f_name}({self.params}) {{ {self.body} }}" class CALL(EXPRESSION): - def __init__(self, f_name, arguments): + def __init__(self, f_name, args): super().__init__() self.f_name = f_name - self.arguments = arguments + self.arg = args 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): def __init__(self, name): @@ -135,13 +146,13 @@ class LOP(EXPRESSION): return "(" + str(self.arg1) + " " + str(self.operator) + " " + str(self.arg2) + ")" class ASSIGN(EXPRESSION): - def __init__(self, variable, expression): + def __init__(self, var, expr): super().__init__() - self.variable = variable - self.expression = expression + self.var = var + self.expr = expr def __str__(self): - return self.variable.name + " = " + str(self.expression) + return self.var.name + " = " + str(self.expr) class SEQ(EXPRESSION): def __init__(self, exp1, exp2): @@ -153,20 +164,20 @@ class SEQ(EXPRESSION): return str(self.exp1) + "; " + str(self.exp2) class IF(EXPRESSION): - def __init__(self, condition, exp1, exp2): + def __init__(self, cond, exp1, exp2): super().__init__() - self.condition = condition + self.cond = cond self.exp1 = exp1 self.exp2 = exp2 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): - def __init__(self, condition, body): + def __init__(self, cond, body): super().__init__() - self.condition = condition + self.cond = cond self.body = body def __str__(self): - return "while (" + str(self.condition) + ") do { " + str(self.body) + " }" \ No newline at end of file + return "while (" + str(self.cond) + ") do { " + str(self.body) + " }" \ No newline at end of file