Implement MaMa machine
This commit is contained in:
169
Uebung-01/MaMa.py
Normal file
169
Uebung-01/MaMa.py
Normal file
@@ -0,0 +1,169 @@
|
||||
from typing import Dict, Optional, Tuple, List
|
||||
|
||||
class MaMa:
|
||||
def __init__(self, prog: Dict[int, str] | List[str], stack: Dict[int, int] | List[int] | None = None) -> None:
|
||||
# Execution state
|
||||
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.p_prog = 0
|
||||
|
||||
# Stack memory and pointer
|
||||
self.stack: Dict[int, int] = {}
|
||||
self.p_stack = -1
|
||||
self.initial_stack: Dict[int, int] = {}
|
||||
if stack is not None:
|
||||
self.reload(stack)
|
||||
|
||||
# Machine halted flag
|
||||
self.halted = False
|
||||
|
||||
|
||||
# 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)})
|
||||
else:
|
||||
self.stack.update(stack)
|
||||
|
||||
self.p_prog = 0
|
||||
self.p_stack = max(self.stack.keys(), default=-1)
|
||||
self.halted = False
|
||||
self.steps = 0
|
||||
|
||||
def run(self, max_steps: int = 1000) -> List[Dict[str, int|str|dict]]:
|
||||
steps = 0
|
||||
journal = [self.config('init')]
|
||||
while not self.halted and steps < max_steps:
|
||||
journal.append(self.step())
|
||||
steps += 1
|
||||
return journal
|
||||
|
||||
def is_halted(self) -> bool:
|
||||
return self.halted
|
||||
|
||||
def step(self) -> Dict[str, int|str|dict]:
|
||||
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, arg = self.decode(call)
|
||||
method = getattr(self, f"_{name}", None)
|
||||
if method is None:
|
||||
raise ValueError(f"Unknown instruction: {call}")
|
||||
|
||||
method(arg)
|
||||
self.steps += 1
|
||||
return self.config(call)
|
||||
|
||||
def config(self, call: str) -> Dict[str, int|str|dict]:
|
||||
return {
|
||||
"step": self.steps,
|
||||
"call": call,
|
||||
"p_prog": self.p_prog,
|
||||
"p_stack": self.p_stack,
|
||||
"stack": dict(self.stack),
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def decode(call: str) -> Tuple[str, Optional[int]]:
|
||||
if "(" in call:
|
||||
name, arg = call.split("(")
|
||||
return name, int(arg[:-1])
|
||||
return call, None
|
||||
|
||||
# Stop execution
|
||||
def _stop(self, _: Optional[int]) -> None:
|
||||
self.halted = True
|
||||
|
||||
# Remove top element from stack
|
||||
def _pop(self, _: Optional[int]) -> None:
|
||||
self.p_stack -= 1
|
||||
self.p_prog += 1
|
||||
|
||||
# Load stack pointer value onto stack
|
||||
def _ldsp(self, _: Optional[int]) -> None:
|
||||
self.p_stack += 1
|
||||
self.stack[self.p_stack] = self.p_stack
|
||||
self.p_prog += 1
|
||||
|
||||
# Push constant n onto stack
|
||||
def _push(self, n: Optional[int]) -> None:
|
||||
assert n is not None
|
||||
self.p_stack += 1
|
||||
self.stack[self.p_stack] = n
|
||||
self.p_prog += 1
|
||||
|
||||
# Add top two stack elements
|
||||
def _add(self, _: Optional[int]) -> None:
|
||||
self.stack[self.p_stack - 1] += self.stack[self.p_stack]
|
||||
self.p_stack -= 1
|
||||
self.p_prog += 1
|
||||
|
||||
# Subtract top element from second-top element
|
||||
def _sub(self, _: Optional[int]) -> None:
|
||||
self.stack[self.p_stack - 1] -= self.stack[self.p_stack]
|
||||
self.p_stack -= 1
|
||||
self.p_prog += 1
|
||||
|
||||
# Multiply top two stack elements
|
||||
def _mult(self, _: Optional[int]) -> None:
|
||||
self.stack[self.p_stack - 1] *= self.stack[self.p_stack]
|
||||
self.p_stack -= 1
|
||||
self.p_prog += 1
|
||||
|
||||
# Integer division of second-top by top stack element
|
||||
def _div(self, _: Optional[int]) -> None:
|
||||
self.stack[self.p_stack - 1] //= self.stack[self.p_stack]
|
||||
self.p_stack -= 1
|
||||
self.p_prog += 1
|
||||
|
||||
# Load value from relative address n in stack
|
||||
def _ldo(self, n: Optional[int]) -> None:
|
||||
assert n is not None
|
||||
old_sp = self.p_stack
|
||||
self.p_stack += 1
|
||||
self.stack[self.p_stack] = self.stack[old_sp + n]
|
||||
self.p_prog += 1
|
||||
|
||||
# Store top value to relative address n in stack
|
||||
def _sto(self, n: Optional[int]) -> None:
|
||||
assert n is not None
|
||||
self.stack[self.p_stack + n] = self.stack[self.p_stack]
|
||||
self.p_stack -= 1
|
||||
self.p_prog += 1
|
||||
|
||||
# Unconditional jump to address a
|
||||
def _ujp(self, a: Optional[int]) -> None:
|
||||
assert a is not None
|
||||
self.p_prog = a
|
||||
|
||||
# Conditional jump if top two elements are equal
|
||||
def _equal(self, a: Optional[int]) -> None:
|
||||
assert a is not None
|
||||
if self.stack[self.p_stack - 1] == self.stack[self.p_stack]:
|
||||
self.p_stack -= 2
|
||||
self.p_prog = a
|
||||
else:
|
||||
self.p_stack -= 2
|
||||
self.p_prog += 1
|
||||
|
||||
# Conditional jump if second-top ≤ top element
|
||||
def _leq(self, a: Optional[int]) -> None:
|
||||
assert a is not None
|
||||
if self.stack[self.p_stack - 1] <= self.stack[self.p_stack]:
|
||||
self.p_stack -= 2
|
||||
self.p_prog = a
|
||||
else:
|
||||
self.p_stack -= 2
|
||||
self.p_prog += 1
|
||||
Reference in New Issue
Block a user