MaMa support macros
This commit is contained in:
		| @@ -24,6 +24,16 @@ class MaMa: | |||||||
|         # Machine halted flag |         # Machine halted flag | ||||||
|         self.halted = False |         self.halted = False | ||||||
|  |  | ||||||
|  |         # Macro system | ||||||
|  |         self.macros: Dict[str, Dict[str, object]] = {} | ||||||
|  |         self.call_stack: List[Tuple[Dict[int, str], int, Optional[str]]] = []  # (prog, return_pc, macro_name) | ||||||
|  |         self.in_macro_stack: List[str] = []  # first = outermost, last = innermost | ||||||
|  |  | ||||||
|  |     # Add new macro with parameters | ||||||
|  |     def add_macro(self, name: str, prog: List[str] | Dict[int, str], args: List[str]) -> None: | ||||||
|  |         if isinstance(prog, list): | ||||||
|  |             prog = {i: instr for i, instr in enumerate(prog)} | ||||||
|  |         self.macros[name] = {"prog": prog, "args": args} | ||||||
|  |  | ||||||
|     # Restore initial machine state |     # Restore initial machine state | ||||||
|     def reload(self, stack: Dict[int, int] | List[int] | None = None) -> None: |     def reload(self, stack: Dict[int, int] | List[int] | None = None) -> None: | ||||||
| @@ -34,15 +44,16 @@ class MaMa: | |||||||
|                 self.stack.update({i: v for i, v in enumerate(stack)}) |                 self.stack.update({i: v for i, v in enumerate(stack)}) | ||||||
|             else: |             else: | ||||||
|                 self.stack.update(stack) |                 self.stack.update(stack) | ||||||
|  |  | ||||||
|         self.p_prog = 0 |         self.p_prog = 0 | ||||||
|         self.p_stack = max(self.stack.keys(), default=-1) |         self.p_stack = max(self.stack.keys(), default=-1) | ||||||
|         self.halted = False |         self.halted = False | ||||||
|         self.steps = 0 |         self.steps = 0 | ||||||
|  |         self.call_stack = [] | ||||||
|  |         self.in_macro_stack = [] | ||||||
|  |  | ||||||
|     def run(self, max_steps: int = 1000) -> List[Dict[str, int|str|dict]]: |     def run(self, max_steps: int = 1000) -> List[Dict[str, int | str | dict | list]]: | ||||||
|         steps = 0 |         steps = 0 | ||||||
|         journal = [self.config('init')] |         journal = [self.config("init")] | ||||||
|         while not self.halted and steps < max_steps: |         while not self.halted and steps < max_steps: | ||||||
|             journal.append(self.step()) |             journal.append(self.step()) | ||||||
|             steps += 1 |             steps += 1 | ||||||
| @@ -51,35 +62,82 @@ class MaMa: | |||||||
|     def is_halted(self) -> bool: |     def is_halted(self) -> bool: | ||||||
|         return self.halted |         return self.halted | ||||||
|  |  | ||||||
|     def step(self) -> Dict[str, int|str|dict]: |     def step(self) -> Dict[str, int | str | dict | list]: | ||||||
|         if self.halted or self.p_prog not in self.prog: |         if self.halted or self.p_prog not in self.prog: | ||||||
|  |             # if macro ended, return to caller | ||||||
|  |             if self.call_stack: | ||||||
|  |                 self.prog, self.p_prog, macro_name = self.call_stack.pop() | ||||||
|  |                 if self.in_macro_stack: | ||||||
|  |                     self.in_macro_stack.pop() | ||||||
|  |                 return self.step()  # skip macro return entry | ||||||
|  |             else: | ||||||
|                 self.halted = True |                 self.halted = True | ||||||
|                 return self.config("halted") |                 return self.config("halted") | ||||||
|  |  | ||||||
|         call = self.prog[self.p_prog] |         call = self.prog[self.p_prog] | ||||||
|         name, arg = self.decode(call) |         name, args = self.decode(call) | ||||||
|  |  | ||||||
|  |         # Handle macro invocation with parameter substitution | ||||||
|  |         if name in self.macros: | ||||||
|  |             macro = self.macros[name] | ||||||
|  |             formal_args = macro["args"] | ||||||
|  |             actual_args = args if isinstance(args, list) else [] | ||||||
|  |             mapping = dict(zip(formal_args, actual_args)) | ||||||
|  |  | ||||||
|  |             expanded_prog = {} | ||||||
|  |             for i, instr in macro["prog"].items(): | ||||||
|  |                 # split into opcode and argument part for safe substitution | ||||||
|  |                 if "(" in instr: | ||||||
|  |                     instr_name, rest = instr.split("(", 1) | ||||||
|  |                     arg_text = rest[:-1] | ||||||
|  |                     if arg_text.strip() != "": | ||||||
|  |                         parts = [a.strip() for a in arg_text.split(",")] | ||||||
|  |                         resolved = [] | ||||||
|  |                         for a in parts: | ||||||
|  |                             if a in mapping: | ||||||
|  |                                 resolved.append(str(mapping[a])) | ||||||
|  |                             else: | ||||||
|  |                                 resolved.append(a) | ||||||
|  |                         instr = f"{instr_name}({','.join(resolved)})" | ||||||
|  |                     else: | ||||||
|  |                         instr = f"{instr_name}()" | ||||||
|  |                 expanded_prog[i] = instr | ||||||
|  |  | ||||||
|  |             # push current program context | ||||||
|  |             self.call_stack.append((self.prog, self.p_prog + 1, name)) | ||||||
|  |             self.in_macro_stack.append(name) | ||||||
|  |             self.prog = expanded_prog | ||||||
|  |             self.p_prog = 0 | ||||||
|  |             return self.step()  # directly continue without adding step | ||||||
|  |  | ||||||
|         method = getattr(self, f"_{name}", None) |         method = getattr(self, f"_{name}", None) | ||||||
|         if method is None: |         if method is None: | ||||||
|             raise ValueError(f"Unknown instruction: {call}") |             raise ValueError(f"Unknown instruction: {call}") | ||||||
|  |  | ||||||
|         method(arg) |         method(args) | ||||||
|         self.steps += 1 |         self.steps += 1 | ||||||
|         return self.config(call) |         return self.config(call) | ||||||
|  |  | ||||||
|     def config(self, call: str) -> Dict[str, int|str|dict]: |     # Return current configuration including macro nesting | ||||||
|  |     def config(self, call: str) -> Dict[str, int | str | dict | list]: | ||||||
|         return { |         return { | ||||||
|             "step": self.steps, |             "step": self.steps, | ||||||
|             "call": call, |             "call": call, | ||||||
|             "p_prog": self.p_prog, |             "p_prog": self.p_prog, | ||||||
|             "p_stack": self.p_stack, |             "p_stack": self.p_stack, | ||||||
|             "stack": dict(self.stack), |             "stack": dict(self.stack), | ||||||
|  |             "in_macro": list(self.in_macro_stack),  # first = outermost, last = innermost | ||||||
|         } |         } | ||||||
|  |  | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     def decode(call: str) -> Tuple[str, Optional[int]]: |     def decode(call: str) -> Tuple[str, Optional[List[int]]]: | ||||||
|         if "(" in call: |         if "(" in call: | ||||||
|             name, arg = call.split("(") |             name, rest = call.split("(", 1) | ||||||
|             return name, int(arg[:-1]) |             args_str = rest[:-1] | ||||||
|  |             if args_str.strip() == "": | ||||||
|  |                 return name, [] | ||||||
|  |             args = [int(a.strip()) for a in args_str.split(",")] | ||||||
|  |             return name, args | ||||||
|         return call, None |         return call, None | ||||||
|  |  | ||||||
|     # Stop execution |     # Stop execution | ||||||
| @@ -98,10 +156,11 @@ class MaMa: | |||||||
|         self.p_prog += 1 |         self.p_prog += 1 | ||||||
|  |  | ||||||
|     # Push constant n onto stack |     # Push constant n onto stack | ||||||
|     def _push(self, n: Optional[int]) -> None: |     def _push(self, n: Optional[List[int]]) -> None: | ||||||
|         assert n is not None |         assert n is not None and len(n) == 1 | ||||||
|  |         val = n[0] | ||||||
|         self.p_stack += 1 |         self.p_stack += 1 | ||||||
|         self.stack[self.p_stack] = n |         self.stack[self.p_stack] = val | ||||||
|         self.p_prog += 1 |         self.p_prog += 1 | ||||||
|  |  | ||||||
|     # Add top two stack elements |     # Add top two stack elements | ||||||
| @@ -129,41 +188,45 @@ class MaMa: | |||||||
|         self.p_prog += 1 |         self.p_prog += 1 | ||||||
|  |  | ||||||
|     # Load value from relative address n in stack |     # Load value from relative address n in stack | ||||||
|     def _ldo(self, n: Optional[int]) -> None: |     def _ldo(self, n: Optional[List[int]]) -> None: | ||||||
|         assert n is not None |         assert n is not None and len(n) == 1 | ||||||
|  |         offset = n[0] | ||||||
|         old_sp = self.p_stack |         old_sp = self.p_stack | ||||||
|         self.p_stack += 1 |         self.p_stack += 1 | ||||||
|         self.stack[self.p_stack] = self.stack[old_sp + n] |         self.stack[self.p_stack] = self.stack[old_sp + offset] | ||||||
|         self.p_prog += 1 |         self.p_prog += 1 | ||||||
|  |  | ||||||
|     # Store top value to relative address n in stack |     # Store top value to relative address n in stack | ||||||
|     def _sto(self, n: Optional[int]) -> None: |     def _sto(self, n: Optional[List[int]]) -> None: | ||||||
|         assert n is not None |         assert n is not None and len(n) == 1 | ||||||
|         self.stack[self.p_stack + n] = self.stack[self.p_stack] |         offset = n[0] | ||||||
|  |         self.stack[self.p_stack + offset] = self.stack[self.p_stack] | ||||||
|         self.p_stack -= 1 |         self.p_stack -= 1 | ||||||
|         self.p_prog += 1 |         self.p_prog += 1 | ||||||
|  |  | ||||||
|     # Unconditional jump to address a |     # Unconditional jump to address a | ||||||
|     def _ujp(self, a: Optional[int]) -> None: |     def _ujp(self, a: Optional[List[int]]) -> None: | ||||||
|         assert a is not None |         assert a is not None and len(a) == 1 | ||||||
|         self.p_prog = a |         self.p_prog = a[0] | ||||||
|  |  | ||||||
|     # Conditional jump if top two elements are equal |     # Conditional jump if top two elements are equal | ||||||
|     def _equal(self, a: Optional[int]) -> None: |     def _equal(self, a: Optional[List[int]]) -> None: | ||||||
|         assert a is not None |         assert a is not None and len(a) == 1 | ||||||
|  |         addr = a[0] | ||||||
|         if self.stack[self.p_stack - 1] == self.stack[self.p_stack]: |         if self.stack[self.p_stack - 1] == self.stack[self.p_stack]: | ||||||
|             self.p_stack -= 2 |             self.p_stack -= 2 | ||||||
|             self.p_prog = a |             self.p_prog = addr | ||||||
|         else: |         else: | ||||||
|             self.p_stack -= 2 |             self.p_stack -= 2 | ||||||
|             self.p_prog += 1 |             self.p_prog += 1 | ||||||
|  |  | ||||||
|     # Conditional jump if second-top ≤ top element |     # Conditional jump if second-top ≤ top element | ||||||
|     def _leq(self, a: Optional[int]) -> None: |     def _leq(self, a: Optional[List[int]]) -> None: | ||||||
|         assert a is not None |         assert a is not None and len(a) == 1 | ||||||
|  |         addr = a[0] | ||||||
|         if self.stack[self.p_stack - 1] <= self.stack[self.p_stack]: |         if self.stack[self.p_stack - 1] <= self.stack[self.p_stack]: | ||||||
|             self.p_stack -= 2 |             self.p_stack -= 2 | ||||||
|             self.p_prog = a |             self.p_prog = addr | ||||||
|         else: |         else: | ||||||
|             self.p_stack -= 2 |             self.p_stack -= 2 | ||||||
|             self.p_prog += 1 |             self.p_prog += 1 | ||||||
|   | |||||||
							
								
								
									
										31
									
								
								Uebung-01/test.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								Uebung-01/test.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | |||||||
|  | from MaMaGUI import MaMaGUI | ||||||
|  | from MaMa import MaMa | ||||||
|  |  | ||||||
|  | if __name__ == "__main__": | ||||||
|  |     prog = { | ||||||
|  |         0: 'outer', | ||||||
|  |         1: 'stop' | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     machine = MaMa(prog, [4, 6]) | ||||||
|  |  | ||||||
|  |     inner_prog = [ | ||||||
|  |         'push(1)', | ||||||
|  |         'push(2)', | ||||||
|  |         'add', | ||||||
|  |         'stop' | ||||||
|  |     ] | ||||||
|  |  | ||||||
|  |     outer_prog = [ | ||||||
|  |         'push(10)', | ||||||
|  |         'inner', | ||||||
|  |         'mult', | ||||||
|  |         'stop' | ||||||
|  |     ] | ||||||
|  |  | ||||||
|  |     machine.add_macro("inner", inner_prog, []) | ||||||
|  |     machine.add_macro("outer", outer_prog, []) | ||||||
|  |  | ||||||
|  |     # Run machine once for GUI visualization | ||||||
|  |     gui = MaMaGUI(machine) | ||||||
|  |     gui.display() | ||||||
		Reference in New Issue
	
	Block a user