Finish ggt task
This commit is contained in:
@@ -35,10 +35,6 @@ class MaMa:
|
|||||||
steps += 1
|
steps += 1
|
||||||
return journal
|
return journal
|
||||||
|
|
||||||
# Returns the full program
|
|
||||||
def structure(self) -> Dict[int, Dict[str, Any]]:
|
|
||||||
return {i: {"micro": micro, "macros": []} for i, micro in sorted(self.prog.items())}
|
|
||||||
|
|
||||||
# Decodes string MaMa instructions to function callables
|
# Decodes string MaMa instructions to function callables
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def decode(micro: str) -> Tuple[str, Optional[List[Any]]]:
|
def decode(micro: str) -> Tuple[str, Optional[List[Any]]]:
|
||||||
|
|||||||
@@ -5,112 +5,92 @@ from MaMa import MaMa
|
|||||||
|
|
||||||
class MaMaGUI:
|
class MaMaGUI:
|
||||||
def __init__(self, mama: MaMa, delay: int = 400) -> None:
|
def __init__(self, mama: MaMa, delay: int = 400) -> None:
|
||||||
# Autoplay speed
|
|
||||||
self.delay = delay
|
self.delay = delay
|
||||||
|
|
||||||
# Rather autoplay is activated
|
|
||||||
self.do_autoplay = False
|
self.do_autoplay = False
|
||||||
|
|
||||||
# Run MaMa and create journal
|
|
||||||
self.machine = mama
|
self.machine = mama
|
||||||
self.journal: List[Dict[str, object]] = self.machine.run()
|
self.journal = self.machine.run()
|
||||||
if not self.journal:
|
if not self.journal:
|
||||||
raise ValueError("Journal is empty.")
|
raise ValueError("Journal is empty.")
|
||||||
self.journal_index = 0
|
self.journal_index = 0
|
||||||
|
|
||||||
# Bootstrap UI
|
# Root window
|
||||||
self.root = tk.Tk()
|
self.root = tk.Tk()
|
||||||
self.root.title("MaMa GUI")
|
self.root.title("MaMa GUI")
|
||||||
|
self.root.geometry("1000x600")
|
||||||
|
self.root.columnconfigure(0, weight=1)
|
||||||
|
self.root.rowconfigure(0, weight=1)
|
||||||
|
self.root.rowconfigure(1, weight=0)
|
||||||
|
self.root.rowconfigure(2, weight=0)
|
||||||
|
|
||||||
|
# Main flex container
|
||||||
container = ttk.Frame(self.root, padding=10)
|
container = ttk.Frame(self.root, padding=10)
|
||||||
container.grid(sticky="nsew")
|
container.grid(row=0, column=0, sticky="nsew")
|
||||||
container.columnconfigure((0, 1), weight=1)
|
container.columnconfigure(0, weight=3)
|
||||||
|
container.columnconfigure(1, weight=2)
|
||||||
container.rowconfigure(0, weight=1)
|
container.rowconfigure(0, weight=1)
|
||||||
|
|
||||||
# Program display
|
# Program display
|
||||||
prog_frame = ttk.LabelFrame(container, text="Program", padding=5)
|
prog_frame = ttk.LabelFrame(container, text="Program", padding=5)
|
||||||
prog_frame.grid(row=0, column=0, sticky="nsew", padx=(0, 10))
|
prog_frame.grid(row=0, column=0, sticky="nsew", padx=(0, 10))
|
||||||
self.prog_list = tk.Listbox(prog_frame, width=70, height=24, font=("Consolas", 11))
|
prog_frame.rowconfigure(0, weight=1)
|
||||||
self.prog_list.pack(fill="both", expand=True)
|
prog_frame.columnconfigure(0, weight=1)
|
||||||
|
self.prog_list = tk.Listbox(prog_frame, font=("Consolas", 11))
|
||||||
|
self.prog_list.grid(row=0, column=0, sticky="nsew")
|
||||||
|
|
||||||
# Stack display
|
# Stack display
|
||||||
stack_frame = ttk.LabelFrame(container, text="Stack", padding=5)
|
stack_frame = ttk.LabelFrame(container, text="Stack", padding=5)
|
||||||
stack_frame.grid(row=0, column=1, sticky="nsew")
|
stack_frame.grid(row=0, column=1, sticky="nsew")
|
||||||
stack_frame.rowconfigure(0, weight=1)
|
stack_frame.rowconfigure(0, weight=1)
|
||||||
self.stack_list = tk.Listbox(stack_frame, width=36, height=22, font=("Consolas", 11))
|
stack_frame.columnconfigure(0, weight=1)
|
||||||
|
self.stack_list = tk.Listbox(stack_frame, font=("Consolas", 11))
|
||||||
self.stack_list.grid(row=0, column=0, sticky="nsew")
|
self.stack_list.grid(row=0, column=0, sticky="nsew")
|
||||||
|
self.conf_label = ttk.Label(stack_frame, text="(s, p, S) = (-, -, [])",
|
||||||
# Configuration display
|
font=("Consolas", 11), anchor="e")
|
||||||
self.conf_label = ttk.Label(stack_frame, text="(s, p, S) = (-, -, [])", font=("Consolas", 11), anchor="e")
|
|
||||||
self.conf_label.grid(row=1, column=0, sticky="ew", pady=(4, 0))
|
self.conf_label.grid(row=1, column=0, sticky="ew", pady=(4, 0))
|
||||||
|
|
||||||
# Create and bind controls
|
# Control buttons
|
||||||
control = ttk.Frame(self.root, padding=5)
|
control = ttk.Frame(self.root, padding=5)
|
||||||
control.grid(row=1, column=0, columnspan=2, sticky="ew")
|
control.grid(row=1, column=0, sticky="ew")
|
||||||
btns = [
|
control.columnconfigure(0, weight=1)
|
||||||
|
for text, cmd in [
|
||||||
("▶ Start", self.__start),
|
("▶ Start", self.__start),
|
||||||
("⏸ Stop", self.__stop),
|
("⏸ Stop", self.__stop),
|
||||||
("⏭ Next", self.__next_step),
|
("⏭ Next", self.__next_step),
|
||||||
("↩ Prev", self.__prev_step),
|
("↩ Prev", self.__prev_step),
|
||||||
("⟲ Reset", self.__reset)
|
("⟲ Reset", self.__reset),
|
||||||
]
|
]:
|
||||||
|
|
||||||
for text, cmd in btns:
|
|
||||||
ttk.Button(control, text=text, command=cmd).pack(side="left", padx=5)
|
ttk.Button(control, text=text, command=cmd).pack(side="left", padx=5)
|
||||||
|
|
||||||
ttk.Label(control, text="Speed:").pack(side="left", padx=5)
|
ttk.Label(control, text="Speed:").pack(side="left", padx=5)
|
||||||
self.speed_scale = ttk.Scale(control, from_=2000, to=100, orient="horizontal", command=self.__update_speed)
|
self.speed_scale = ttk.Scale(control, from_=2000, to=100,
|
||||||
|
orient="horizontal", command=self.__update_speed)
|
||||||
self.speed_scale.set(self.delay)
|
self.speed_scale.set(self.delay)
|
||||||
self.speed_scale.pack(side="left", padx=5)
|
self.speed_scale.pack(side="left", padx=5, fill="x", expand=True)
|
||||||
|
|
||||||
# Global status bar
|
# Status bar
|
||||||
self.status = ttk.Label(self.root, text="Ready", padding=5, anchor="w")
|
self.status = ttk.Label(self.root, text="Ready", padding=5, anchor="w")
|
||||||
self.status.grid(row=2, column=0, columnspan=2, sticky="ew")
|
self.status.grid(row=2, column=0, sticky="ew")
|
||||||
|
|
||||||
self.__update()
|
self.__update()
|
||||||
|
|
||||||
|
# --- UI update ---
|
||||||
def __update(self) -> None:
|
def __update(self) -> None:
|
||||||
# Load current journal entry
|
# Current journal state
|
||||||
state = self.journal[self.journal_index]
|
state = self.journal[self.journal_index]
|
||||||
stack = dict(state["stack"])
|
stack = dict(state["stack"])
|
||||||
p_prog, p_stack = int(state["p_prog"]), int(state["p_stack"])
|
p_prog, p_stack = int(state["p_prog"]), int(state["p_stack"])
|
||||||
step, micro = int(state["step"]), str(state["micro"])
|
step, micro = int(state["step"]), str(state["micro"])
|
||||||
|
|
||||||
# Load code structure
|
# --- Program display ---
|
||||||
structure = self.machine.structure()
|
|
||||||
self.prog_list.delete(0, tk.END)
|
self.prog_list.delete(0, tk.END)
|
||||||
opened_macros: List[str] = []
|
for addr, instr in sorted(self.machine.prog.items()):
|
||||||
macro_offset = 4
|
|
||||||
|
|
||||||
# Display code lines and unpack macros
|
|
||||||
for addr, info in structure.items():
|
|
||||||
macros = info.get("macros", [])
|
|
||||||
depth = len(macros)
|
|
||||||
|
|
||||||
# Align macro headers at parent's instruction column
|
|
||||||
if len(macros) > len(opened_macros):
|
|
||||||
parent_depth = len(macros) - 1
|
|
||||||
header_indent = " " * parent_depth + " " * macro_offset
|
|
||||||
macro = macros[-1]
|
|
||||||
if isinstance(macro, dict):
|
|
||||||
params = ", ".join(f"{k}={v}" for k, v in macro.get("params", {}).items())
|
|
||||||
header = f"{macro['name']}({params})" if params else macro["name"]
|
|
||||||
else:
|
|
||||||
header = str(macro)
|
|
||||||
self.prog_list.insert(tk.END, f"{header_indent}[{header}]")
|
|
||||||
self.prog_list.itemconfig(tk.END, {"bg": "#e6e6e6"})
|
|
||||||
|
|
||||||
instr_indent = " " * depth
|
|
||||||
prefix = ">> " if addr == p_prog else " "
|
prefix = ">> " if addr == p_prog else " "
|
||||||
line = f"{prefix}{instr_indent}{addr:>3}: {info['micro']}"
|
line = f"{prefix}{addr:>3}: {instr}"
|
||||||
self.prog_list.insert(tk.END, line)
|
self.prog_list.insert(tk.END, line)
|
||||||
opened_macros = macros
|
|
||||||
|
|
||||||
# Highlight current program line
|
|
||||||
if addr == p_prog:
|
if addr == p_prog:
|
||||||
self.prog_list.itemconfig(tk.END, {"bg": "#ffd966"})
|
self.prog_list.itemconfig(tk.END, {"bg": "#ffd966"})
|
||||||
|
|
||||||
# Visualize current stack
|
# --- Stack display ---
|
||||||
self.stack_list.delete(0, tk.END)
|
self.stack_list.delete(0, tk.END)
|
||||||
if stack:
|
if stack:
|
||||||
for i in range(p_stack, -1, -1):
|
for i in range(p_stack, -1, -1):
|
||||||
@@ -122,25 +102,25 @@ class MaMaGUI:
|
|||||||
else:
|
else:
|
||||||
self.stack_list.insert(tk.END, "Empty")
|
self.stack_list.insert(tk.END, "Empty")
|
||||||
|
|
||||||
# Update configuration and global stack
|
# --- Configuration & status ---
|
||||||
self.conf_label.config(text=f"({p_stack}, {p_prog}, {list(stack.values())})")
|
self.conf_label.config(text=f"({p_stack}, {p_prog}, {list(stack.values())})")
|
||||||
self.status.config(text=f"Step {step}/{len(self.journal)-1} | PC={p_prog} | SP={p_stack} | Micro: {micro}")
|
self.status.config(
|
||||||
|
text=f"Step {step}/{len(self.journal)-1} | PC={p_prog} | SP={p_stack} | Micro: {micro}"
|
||||||
|
)
|
||||||
|
|
||||||
# Configure auto step speed
|
# --- Speed control ---
|
||||||
def __update_speed(self, value: str) -> None:
|
def __update_speed(self, value: str) -> None:
|
||||||
self.delay = int(float(value))
|
self.delay = int(float(value))
|
||||||
|
|
||||||
# Start autoplay
|
# --- Autoplay controls ---
|
||||||
def __start(self) -> None:
|
def __start(self) -> None:
|
||||||
if not self.do_autoplay:
|
if not self.do_autoplay:
|
||||||
self.do_autoplay = True
|
self.do_autoplay = True
|
||||||
self.__auto_step()
|
self.__auto_step()
|
||||||
|
|
||||||
# Stop autoplay
|
|
||||||
def __stop(self) -> None:
|
def __stop(self) -> None:
|
||||||
self.do_autoplay = False
|
self.do_autoplay = False
|
||||||
|
|
||||||
# Play an autoplay step
|
|
||||||
def __auto_step(self) -> None:
|
def __auto_step(self) -> None:
|
||||||
if self.do_autoplay and self.journal_index < len(self.journal) - 1:
|
if self.do_autoplay and self.journal_index < len(self.journal) - 1:
|
||||||
self.journal_index += 1
|
self.journal_index += 1
|
||||||
@@ -149,23 +129,21 @@ class MaMaGUI:
|
|||||||
else:
|
else:
|
||||||
self.do_autoplay = False
|
self.do_autoplay = False
|
||||||
|
|
||||||
# Play next step
|
# --- Step navigation ---
|
||||||
def __next_step(self) -> None:
|
def __next_step(self) -> None:
|
||||||
if self.journal_index < len(self.journal) - 1:
|
if self.journal_index < len(self.journal) - 1:
|
||||||
self.journal_index += 1
|
self.journal_index += 1
|
||||||
self.__update()
|
self.__update()
|
||||||
|
|
||||||
# Play previous step
|
|
||||||
def __prev_step(self) -> None:
|
def __prev_step(self) -> None:
|
||||||
if self.journal_index > 0:
|
if self.journal_index > 0:
|
||||||
self.journal_index -= 1
|
self.journal_index -= 1
|
||||||
self.__update()
|
self.__update()
|
||||||
|
|
||||||
# Reset machine
|
|
||||||
def __reset(self) -> None:
|
def __reset(self) -> None:
|
||||||
self.journal_index = 0
|
self.journal_index = 0
|
||||||
self.__update()
|
self.__update()
|
||||||
|
|
||||||
# Run main loop
|
# --- Run GUI ---
|
||||||
def display(self) -> None:
|
def display(self) -> None:
|
||||||
self.root.mainloop()
|
self.root.mainloop()
|
||||||
|
|||||||
@@ -1,99 +0,0 @@
|
|||||||
from typing import Dict, Optional, List, Any, Tuple, override
|
|
||||||
from MaMa import MaMa
|
|
||||||
|
|
||||||
class MaMaMa(MaMa):
|
|
||||||
def __init__(self, prog, stack=None) -> None:
|
|
||||||
# Store macros
|
|
||||||
self.macros: Dict[str, Dict[str, Any]] = {}
|
|
||||||
self.initial_macros: Dict[str, Dict[str, Any]] = {}
|
|
||||||
|
|
||||||
# Trace nested macros
|
|
||||||
self._macro_trace: Dict[int, List[str]] = {}
|
|
||||||
super().__init__(prog, stack)
|
|
||||||
self.initial_macros["__top__"] = dict(self.prog)
|
|
||||||
|
|
||||||
# Register a new macro, which is a name and a sequence of micros
|
|
||||||
def add_macro(self, name: str, prog: List[str] | Dict[int, str], args: List[str] = None) -> None:
|
|
||||||
if isinstance(prog, list):
|
|
||||||
prog = {i: instr for i, instr in enumerate(prog)}
|
|
||||||
self.macros[name] = {"prog": prog, "args": args}
|
|
||||||
self.initial_macros[name] = dict(self.macros[name])
|
|
||||||
|
|
||||||
# Safely evaluate simple arithmetic expressions
|
|
||||||
def _eval_expr(self, expr: Any, env: Dict[str, Any]) -> Any:
|
|
||||||
if isinstance(expr, (int, float)):
|
|
||||||
return expr
|
|
||||||
if isinstance(expr, str):
|
|
||||||
# Replace variables in the expression with env values
|
|
||||||
safe_expr = expr
|
|
||||||
for k, v in env.items():
|
|
||||||
safe_expr = safe_expr.replace(str(k), str(v))
|
|
||||||
try:
|
|
||||||
return eval(safe_expr, {"__builtins__": {}})
|
|
||||||
except Exception:
|
|
||||||
return expr
|
|
||||||
return expr
|
|
||||||
|
|
||||||
# Automatically flatten macros and then execute
|
|
||||||
@override
|
|
||||||
def run(self, max_steps: int = 1000):
|
|
||||||
self.__flatten_macro()
|
|
||||||
return super().run(max_steps)
|
|
||||||
|
|
||||||
# Flatten macros recursively with expression support
|
|
||||||
def __flatten_macro(self) -> None:
|
|
||||||
def expand(prog: Dict[int, str], stack: List[str], env: Dict[str, Any]) -> List[Tuple[str, List[str]]]:
|
|
||||||
out: List[Tuple[str, List[str]]] = []
|
|
||||||
for _, micro in sorted(prog.items()):
|
|
||||||
name, args = self.decode(micro)
|
|
||||||
|
|
||||||
# substitute arguments if defined in env and evaluate expressions
|
|
||||||
if args:
|
|
||||||
args = [self._eval_expr(env.get(str(a), a), env) for a in args]
|
|
||||||
micro = f"{name}({','.join(map(str, args))})"
|
|
||||||
|
|
||||||
if name in self.macros:
|
|
||||||
macro = self.macros[name]
|
|
||||||
params = macro.get("args") or []
|
|
||||||
new_env = env.copy()
|
|
||||||
if args and params:
|
|
||||||
for p, v in zip(params, args):
|
|
||||||
new_env[p] = v
|
|
||||||
out.extend(expand(macro["prog"], stack + [name], new_env))
|
|
||||||
else:
|
|
||||||
out.append((micro, list(stack)))
|
|
||||||
return out
|
|
||||||
|
|
||||||
expanded = expand(self.prog, [], {})
|
|
||||||
self.prog = {i: call for i, (call, _) in enumerate(expanded)}
|
|
||||||
self._macro_trace = {i: macros for i, (_, macros) in enumerate(expanded)}
|
|
||||||
|
|
||||||
# Build program structure with parameter info (no change to _macro_trace)
|
|
||||||
@override
|
|
||||||
def structure(self) -> Dict[int, Dict[str, Any]]:
|
|
||||||
struct: Dict[int, Dict[str, Any]] = {}
|
|
||||||
|
|
||||||
# extract macro calls with their argument values from original top-level prog
|
|
||||||
top_calls: Dict[str, Dict[str, Any]] = {}
|
|
||||||
for _, micro in sorted(self.initial_macros.get("__top__", self.prog).items()):
|
|
||||||
name, args = self.decode(micro)
|
|
||||||
if name in self.macros:
|
|
||||||
macro = self.macros[name]
|
|
||||||
params = macro.get("args") or []
|
|
||||||
if args and params:
|
|
||||||
top_calls[name] = dict(zip(params, args))
|
|
||||||
|
|
||||||
# build structure
|
|
||||||
for i, micro in sorted(self.prog.items()):
|
|
||||||
entry = {"micro": micro, "macros": []}
|
|
||||||
for macro_name in self._macro_trace.get(i, []):
|
|
||||||
macro_info = {"name": macro_name, "params": {}}
|
|
||||||
if macro_name in self.macros:
|
|
||||||
param_names = self.macros[macro_name].get("args") or []
|
|
||||||
macro_info["params"] = {
|
|
||||||
p: top_calls.get(macro_name, {}).get(p, None)
|
|
||||||
for p in param_names
|
|
||||||
}
|
|
||||||
entry["macros"].append(macro_info)
|
|
||||||
struct[i] = entry
|
|
||||||
return struct
|
|
||||||
@@ -1,57 +1,67 @@
|
|||||||
from MaMaGUI import MaMaGUI
|
from MaMaGUI import MaMaGUI
|
||||||
from MaMaMa import MaMaMa
|
from MaMa import MaMa
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
# Algor
|
||||||
prog = [
|
prog = [
|
||||||
'cequal(26)',
|
'ldo(-1)',
|
||||||
'cleqR(11)',
|
'ldo(-1)',
|
||||||
'csub(-1,0,-1)',
|
'equal(25)',
|
||||||
|
'ldo(0)',
|
||||||
|
'ldo(-2)',
|
||||||
|
'leq(11)',
|
||||||
|
'ldo(0)',
|
||||||
|
'ldo(-2)',
|
||||||
|
'sub',
|
||||||
|
'sto(-1)',
|
||||||
'ujp(15)',
|
'ujp(15)',
|
||||||
'csub(0,-1,0)',
|
'ldo(-1)',
|
||||||
'nzero(-1,3)',
|
'ldo(-1)',
|
||||||
'swp',
|
'sub',
|
||||||
|
'sto(-2)',
|
||||||
|
'ldo(-1)',
|
||||||
|
'push(0)',
|
||||||
|
'equal(19)',
|
||||||
|
'ujp(3)',
|
||||||
|
'ldo(-1)',
|
||||||
|
'ldo(-1)',
|
||||||
|
'sto(-3)',
|
||||||
|
'sto(-1)',
|
||||||
'pop',
|
'pop',
|
||||||
'stop',
|
'stop',
|
||||||
'pop',
|
'pop',
|
||||||
'stop'
|
'stop'
|
||||||
]
|
]
|
||||||
|
|
||||||
# Create and execute MaMa instance
|
# Test cases
|
||||||
machine = MaMaMa(prog, [1, 1, 1, 4, 6])
|
tests = [
|
||||||
|
(4,6,2),
|
||||||
|
(10,5,5),
|
||||||
|
(9,3,3),
|
||||||
|
(21,14,7),
|
||||||
|
(7,13,1),
|
||||||
|
(12,18,6),
|
||||||
|
(25,5,5),
|
||||||
|
(42,56,14),
|
||||||
|
(100,80,20),
|
||||||
|
(81,27,27)
|
||||||
|
]
|
||||||
|
|
||||||
machine.add_macro('nzero', [
|
# Print restults into console
|
||||||
'ldo(n)',
|
passed = 0
|
||||||
'ldo(0)',
|
for a,b,expected in tests:
|
||||||
'sub',
|
m = MaMa(prog, [a,b])
|
||||||
'push(0)',
|
m.run()
|
||||||
'equal(p)'
|
result = m.stack.get(0)
|
||||||
], ['n', 'p'])
|
if result == expected:
|
||||||
|
print(f"GGT({a},{b}) = {result} ✓")
|
||||||
|
passed += 1
|
||||||
|
else:
|
||||||
|
print(f"GGT({a},{b}) = {result} ✗ expected {expected}")
|
||||||
|
print(f"{passed}/{len(tests)} tests passed.")
|
||||||
|
|
||||||
machine.add_macro('cequal', [
|
# Load machine
|
||||||
'ldo(-1)',
|
machine = MaMa(prog, [4,6])
|
||||||
'ldo(-1)',
|
|
||||||
'equal(p)'
|
|
||||||
], ["p"])
|
|
||||||
|
|
||||||
machine.add_macro('cleqR', [
|
|
||||||
'ldo(0)',
|
|
||||||
'ldo(-2)',
|
|
||||||
'leq(p)'
|
|
||||||
], ['p'])
|
|
||||||
|
|
||||||
machine.add_macro('csub', [
|
|
||||||
'ldo(n)',
|
|
||||||
'ldo(m-1)',
|
|
||||||
'sub',
|
|
||||||
'sto(s-1)'
|
|
||||||
], ['n', 'm', 's'])
|
|
||||||
|
|
||||||
machine.add_macro('swp', [
|
|
||||||
'ldo(-1)',
|
|
||||||
'ldo(-1)',
|
|
||||||
'sto(-2)',
|
|
||||||
'sto(-1)',
|
|
||||||
])
|
|
||||||
|
|
||||||
# Visualize finished execution using journal
|
# Visualize finished execution using journal
|
||||||
gui = MaMaGUI(machine)
|
gui = MaMaGUI(machine)
|
||||||
|
|||||||
Reference in New Issue
Block a user