Finish ggt task
This commit is contained in:
		| @@ -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]]]: | ||||
|   | ||||
| @@ -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() | ||||
|   | ||||
| @@ -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 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) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user