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) # 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]) # 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 def __flatten_macro(self) -> None: def expand(prog: Dict[int, str], stack: List[str]) -> List[Tuple[str, List[str]]]: out: List[Tuple[str, List[str]]] = [] for _, micro in sorted(prog.items()): name, args = self.decode(micro) if name in self.macros: out.extend(expand(self.macros[name]["prog"], stack + [name])) 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)} # Add macros to structure @override def structure(self) -> Dict[int, Dict[str, Any]]: return { i: {"micro": micro, "macros": self._macro_trace.get(i, []), "p_prog": self.p_prog} for i, micro in sorted(self.prog.items()) }