From 7f3cd757aeeba75afaf3c3083326842c7210b46e Mon Sep 17 00:00:00 2001 From: Jan-Niclas Loosen Date: Sat, 18 Oct 2025 17:13:08 +0200 Subject: [PATCH] Finish ggt task --- Uebung-01/MaMa.py | 4 -- Uebung-01/MaMaGUI.py | 112 +++++++++++++++++-------------------------- Uebung-01/MaMaMa.py | 99 -------------------------------------- Uebung-01/ggt.py | 92 +++++++++++++++++++---------------- 4 files changed, 96 insertions(+), 211 deletions(-) delete mode 100644 Uebung-01/MaMaMa.py diff --git a/Uebung-01/MaMa.py b/Uebung-01/MaMa.py index f558f14..2bc8caf 100644 --- a/Uebung-01/MaMa.py +++ b/Uebung-01/MaMa.py @@ -35,10 +35,6 @@ class MaMa: steps += 1 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 @staticmethod def decode(micro: str) -> Tuple[str, Optional[List[Any]]]: diff --git a/Uebung-01/MaMaGUI.py b/Uebung-01/MaMaGUI.py index 34a1cce..014c567 100644 --- a/Uebung-01/MaMaGUI.py +++ b/Uebung-01/MaMaGUI.py @@ -5,112 +5,92 @@ from MaMa import MaMa class MaMaGUI: def __init__(self, mama: MaMa, delay: int = 400) -> None: - # Autoplay speed self.delay = delay - - # Rather autoplay is activated self.do_autoplay = False - - # Run MaMa and create journal self.machine = mama - self.journal: List[Dict[str, object]] = self.machine.run() + self.journal = self.machine.run() if not self.journal: raise ValueError("Journal is empty.") self.journal_index = 0 - # Bootstrap UI + # Root window self.root = tk.Tk() 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.grid(sticky="nsew") - container.columnconfigure((0, 1), weight=1) + container.grid(row=0, column=0, sticky="nsew") + container.columnconfigure(0, weight=3) + container.columnconfigure(1, weight=2) container.rowconfigure(0, weight=1) # Program display prog_frame = ttk.LabelFrame(container, text="Program", padding=5) 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)) - self.prog_list.pack(fill="both", expand=True) + prog_frame.rowconfigure(0, weight=1) + 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_frame = ttk.LabelFrame(container, text="Stack", padding=5) stack_frame.grid(row=0, column=1, sticky="nsew") 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") - - # Configuration display - self.conf_label = ttk.Label(stack_frame, text="(s, p, S) = (-, -, [])", 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)) - # Create and bind controls + # Control buttons control = ttk.Frame(self.root, padding=5) - control.grid(row=1, column=0, columnspan=2, sticky="ew") - btns = [ + control.grid(row=1, column=0, sticky="ew") + control.columnconfigure(0, weight=1) + for text, cmd in [ ("▶ Start", self.__start), ("⏸ Stop", self.__stop), ("⏭ Next", self.__next_step), ("↩ Prev", self.__prev_step), - ("⟲ Reset", self.__reset) - ] - - for text, cmd in btns: + ("⟲ Reset", self.__reset), + ]: ttk.Button(control, text=text, command=cmd).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.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.grid(row=2, column=0, columnspan=2, sticky="ew") + self.status.grid(row=2, column=0, sticky="ew") self.__update() + # --- UI update --- def __update(self) -> None: - # Load current journal entry + # Current journal state state = self.journal[self.journal_index] stack = dict(state["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 - structure = self.machine.structure() + # --- Program display --- self.prog_list.delete(0, tk.END) - opened_macros: List[str] = [] - 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 + for addr, instr in sorted(self.machine.prog.items()): 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) - opened_macros = macros - - # Highlight current program line if addr == p_prog: self.prog_list.itemconfig(tk.END, {"bg": "#ffd966"}) - # Visualize current stack + # --- Stack display --- self.stack_list.delete(0, tk.END) if stack: for i in range(p_stack, -1, -1): @@ -122,25 +102,25 @@ class MaMaGUI: else: 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.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: self.delay = int(float(value)) - # Start autoplay + # --- Autoplay controls --- def __start(self) -> None: if not self.do_autoplay: self.do_autoplay = True self.__auto_step() - # Stop autoplay def __stop(self) -> None: self.do_autoplay = False - # Play an autoplay step def __auto_step(self) -> None: if self.do_autoplay and self.journal_index < len(self.journal) - 1: self.journal_index += 1 @@ -149,23 +129,21 @@ class MaMaGUI: else: self.do_autoplay = False - # Play next step + # --- Step navigation --- def __next_step(self) -> None: if self.journal_index < len(self.journal) - 1: self.journal_index += 1 self.__update() - # Play previous step def __prev_step(self) -> None: if self.journal_index > 0: self.journal_index -= 1 self.__update() - # Reset machine def __reset(self) -> None: self.journal_index = 0 self.__update() - # Run main loop + # --- Run GUI --- def display(self) -> None: self.root.mainloop() diff --git a/Uebung-01/MaMaMa.py b/Uebung-01/MaMaMa.py deleted file mode 100644 index b5236fe..0000000 --- a/Uebung-01/MaMaMa.py +++ /dev/null @@ -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 diff --git a/Uebung-01/ggt.py b/Uebung-01/ggt.py index 286743b..04ac46d 100644 --- a/Uebung-01/ggt.py +++ b/Uebung-01/ggt.py @@ -1,57 +1,67 @@ from MaMaGUI import MaMaGUI -from MaMaMa import MaMaMa +from MaMa import MaMa if __name__ == "__main__": + # Algor prog = [ - 'cequal(26)', - 'cleqR(11)', - 'csub(-1,0,-1)', + 'ldo(-1)', + 'ldo(-1)', + 'equal(25)', + 'ldo(0)', + 'ldo(-2)', + 'leq(11)', + 'ldo(0)', + 'ldo(-2)', + 'sub', + 'sto(-1)', 'ujp(15)', - 'csub(0,-1,0)', - 'nzero(-1,3)', - 'swp', + 'ldo(-1)', + 'ldo(-1)', + 'sub', + 'sto(-2)', + 'ldo(-1)', + 'push(0)', + 'equal(19)', + 'ujp(3)', + 'ldo(-1)', + 'ldo(-1)', + 'sto(-3)', + 'sto(-1)', 'pop', 'stop', 'pop', 'stop' ] - # Create and execute MaMa instance - machine = MaMaMa(prog, [1, 1, 1, 4, 6]) + # Test cases + 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', [ - 'ldo(n)', - 'ldo(0)', - 'sub', - 'push(0)', - 'equal(p)' - ], ['n', 'p']) + # Print restults into console + passed = 0 + for a,b,expected in tests: + m = MaMa(prog, [a,b]) + m.run() + result = m.stack.get(0) + 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', [ - 'ldo(-1)', - '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)', - ]) + # Load machine + machine = MaMa(prog, [4,6]) # Visualize finished execution using journal gui = MaMaGUI(machine)