Improve code maintainability

This commit is contained in:
Jan-Niclas Loosen
2025-12-07 23:12:35 +01:00
parent 6e9f0331ed
commit 88b8de363d
44 changed files with 45 additions and 58 deletions

View File

@@ -0,0 +1,26 @@
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-{len(options)}.")
def prompt_confirmation(question: str, default="y"):
s = input(f"{question} (y/n) [{default}]: ").strip().lower()
if not s:
s = default
return s.startswith("y")
def search_programs():
base = Path(__file__).parent / "triplaprograms"
if not base.exists():
return []
return sorted([f for f in base.glob("*.tripla")])

View File

@@ -4,14 +4,17 @@ import syntax
from pathlib import Path 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 lib.console as cnsl
import os import os
import matplotlib import matplotlib
matplotlib.use("TkAgg") matplotlib.use("TkAgg")
def render_ast_from_string(dot_string: str): # Renders a diagram of the AST
def render_diagram(dot_string: str):
# Set DPI for PNG # Set DPI for PNG
os.environ["GV_FILE_DPI"] = "300" os.environ["GV_FILE_DPI"] = "300"
@@ -27,6 +30,7 @@ def render_ast_from_string(dot_string: str):
plt.axis("off") plt.axis("off")
plt.show() plt.show()
# Pretty prints the 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__}:")
@@ -47,6 +51,7 @@ def pretty_print(node, indent=0):
else: else:
print(f"{prefix} {key}: {value}") print(f"{prefix} {key}: {value}")
# Exports the AST as a DOT file
def export_dot_file(dot_string: str, filename: str): def export_dot_file(dot_string: str, filename: str):
try: try:
Path(filename).write_text(dot_string, encoding="utf-8") Path(filename).write_text(dot_string, encoding="utf-8")
@@ -54,69 +59,42 @@ def export_dot_file(dot_string: str, filename: str):
except Exception as e: except Exception as e:
print(f"Could not save DOT file: {e}") print(f"Could not save DOT file: {e}")
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-{len(options)}.")
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")
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__": if __name__ == "__main__":
print("\nTRIPLA Parser Tool") print("\nTRIPLA Parser Tool")
while True: while True:
choice = __prompt_choice("\nSelect action:", ["Parse .tripla", "Exit"]) choice = cnsl.prompt_choice("\nSelect action:", ["Parse .tripla", "Exit"])
if choice == 1: if choice == 1:
print("\nBye Bye.") print("\nBye Bye.")
break break
programs = __search_programs() programs = cnsl.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 = cnsl.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 cnsl.prompt_confirmation("\nPretty-print AST?"):
print("") print("")
pretty_print(ast) pretty_print(ast)
# Export DOT # Export DOT
dot_str = ast.to_dot() dot_str = ast.to_dot()
if __prompt_yesno("Export AST as .dot file?"): if cnsl.prompt_confirmation("Export AST as .dot file?"):
default = f"{path.stem}.dot" default = f"{path.stem}.dot"
out = input(f"Filename [{default}]: ").strip() cnsl = input(f"Filename [{default}]: ").strip()
if not out: if not cnsl:
out = default cnsl = default
export_dot_file(dot_str, out) export_dot_file(dot_str, cnsl)
# Display AST diagram # Display AST diagram
if __prompt_yesno("Display AST diagram?"): if cnsl.prompt_confirmation("Display AST diagram?"):
render_ast_from_string(dot_str) render_diagram(dot_str)
print("Rendered AST diagram.") print("Rendered AST diagram.")

View File

@@ -9,8 +9,8 @@ class EXPRESSION:
def copy(): def copy():
return EXPRESSION() return EXPRESSION()
# Returns a list of tuples (edge_name, child_expression)
def children(self): def children(self):
"""Return a list of (name, childNode)."""
out = [] out = []
for key, value in self.__dict__.items(): for key, value in self.__dict__.items():
if key == "pp": if key == "pp":
@@ -23,6 +23,7 @@ class EXPRESSION:
out.append((f"{key}{i}", elem)) out.append((f"{key}{i}", elem))
return out return out
# Export AST to dot format
def to_dot(self, visited=None, root=True): def to_dot(self, visited=None, root=True):
if visited is None: if visited is None:
visited = set() visited = set()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 252 KiB

View File

@@ -1,18 +0,0 @@
# 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/