Finish MaMaGUI and create MaMaMa
This commit is contained in:
@@ -1,133 +1,37 @@
|
||||
from typing import Dict, Optional, Tuple, List
|
||||
from typing import Dict, Optional, Tuple, List, Any
|
||||
|
||||
class MaMa:
|
||||
def __init__(self, prog: Dict[int, str] | List[str], stack: Dict[int, int] | List[int] | None = None) -> None:
|
||||
# Execution state
|
||||
def __init__(self, prog: Dict[int, str] | List[str],
|
||||
stack: Dict[int, int] | List[int] | None = None) -> None:
|
||||
self.steps = 0
|
||||
|
||||
# Allow either dict or list for program input
|
||||
if isinstance(prog, list):
|
||||
self.prog = {i: instr for i, instr in enumerate(prog)}
|
||||
else:
|
||||
self.prog = prog
|
||||
|
||||
# Program counter
|
||||
self.prog = {i: instr for i, instr in enumerate(prog)} if isinstance(prog, list) else prog
|
||||
self.p_prog = 0
|
||||
|
||||
# Stack memory and pointer
|
||||
self.stack: Dict[int, int] = {}
|
||||
self.p_stack = -1
|
||||
self.initial_stack: Dict[int, int] = {}
|
||||
self.halted = False
|
||||
if stack is not None:
|
||||
self.reload(stack)
|
||||
|
||||
# Machine halted flag
|
||||
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
|
||||
def reload(self, stack: Dict[int, int] | List[int] | None = None) -> None:
|
||||
if stack is None:
|
||||
self.stack = dict(self.initial_stack)
|
||||
else:
|
||||
if isinstance(stack, list):
|
||||
self.stack.update({i: v for i, v in enumerate(stack)})
|
||||
self.stack = {i: v for i, v in enumerate(stack)}
|
||||
else:
|
||||
self.stack.update(stack)
|
||||
self.p_prog = 0
|
||||
self.p_stack = max(self.stack.keys(), default=-1)
|
||||
self.halted = False
|
||||
self.steps = 0
|
||||
self.call_stack = []
|
||||
self.in_macro_stack = []
|
||||
self.stack = dict(stack)
|
||||
self.p_stack = max(self.stack.keys(), default=-1)
|
||||
self.initial_stack = dict(self.stack)
|
||||
|
||||
# -------------------------------------------------------------
|
||||
|
||||
def run(self, max_steps: int = 1000) -> List[Dict[str, int | str | dict | list]]:
|
||||
steps = 0
|
||||
# always insert init configuration as step 0
|
||||
journal = [self.config("init")]
|
||||
while not self.halted and steps < max_steps:
|
||||
journal.append(self.step())
|
||||
journal.append(self.__step())
|
||||
steps += 1
|
||||
return journal
|
||||
|
||||
def is_halted(self) -> bool:
|
||||
return self.halted
|
||||
# -------------------------------------------------------------
|
||||
|
||||
def step(self) -> Dict[str, int | str | dict | list]:
|
||||
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
|
||||
return self.config("halted")
|
||||
|
||||
call = self.prog[self.p_prog]
|
||||
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)
|
||||
if method is None:
|
||||
raise ValueError(f"Unknown instruction: {call}")
|
||||
|
||||
method(args)
|
||||
self.steps += 1
|
||||
return self.config(call)
|
||||
|
||||
# Return current configuration including macro nesting
|
||||
def config(self, call: str) -> Dict[str, int | str | dict | list]:
|
||||
return {
|
||||
"step": self.steps,
|
||||
"call": call,
|
||||
"p_prog": self.p_prog,
|
||||
"p_stack": self.p_stack,
|
||||
"stack": dict(self.stack),
|
||||
"in_macro": list(self.in_macro_stack), # first = outermost, last = innermost
|
||||
}
|
||||
def structure(self) -> Dict[int, Dict[str, Any]]:
|
||||
return {i: {"call": call, "macros": []} for i, call in sorted(self.prog.items())}
|
||||
|
||||
@staticmethod
|
||||
def decode(call: str) -> Tuple[str, Optional[List[int]]]:
|
||||
@@ -140,6 +44,33 @@ class MaMa:
|
||||
return name, args
|
||||
return call, None
|
||||
|
||||
# -------------------------------------------------------------
|
||||
|
||||
def config(self, call: str) -> Dict[str, int | str | dict | list]:
|
||||
return {
|
||||
"step": self.steps,
|
||||
"call": call,
|
||||
"p_prog": self.p_prog,
|
||||
"p_stack": self.p_stack,
|
||||
"stack": dict(self.stack),
|
||||
}
|
||||
|
||||
# -------------------------------------------------------------
|
||||
|
||||
def __step(self) -> Dict[str, int | str | dict | list]:
|
||||
if self.halted or self.p_prog not in self.prog:
|
||||
self.halted = True
|
||||
return self.config("halted")
|
||||
|
||||
call = self.prog[self.p_prog]
|
||||
name, args = MaMa.decode(call)
|
||||
method = getattr(self, f"_{name}", None)
|
||||
if method is None:
|
||||
raise ValueError(f"Unknown instruction: {call}")
|
||||
method(args)
|
||||
self.steps += 1
|
||||
return self.config(call)
|
||||
|
||||
# Stop execution
|
||||
def _stop(self, _: Optional[int]) -> None:
|
||||
self.halted = True
|
||||
|
||||
Reference in New Issue
Block a user