62 lines
		
	
	
		
			2.4 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			62 lines
		
	
	
		
			2.4 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| 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], 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
 | |
|                 if args:
 | |
|                     args = [env.get(str(a), a) 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)}
 | |
| 
 | |
|     # 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())
 | |
|         }
 |