Improve code maintainability
This commit is contained in:
26
Project-02-03/lib/console.py
Normal file
26
Project-02-03/lib/console.py
Normal 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")])
|
||||||
@@ -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.")
|
||||||
@@ -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 |
@@ -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/
|
|
||||||
Reference in New Issue
Block a user