Files
Construction-of-Compilers/Uebung-01/MaMa.py
2025-10-18 10:59:29 +02:00

164 lines
5.5 KiB
Python

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:
self.steps = 0
self.prog = {i: instr for i, instr in enumerate(prog)} if isinstance(prog, list) else prog
self.p_prog = 0
self.stack: Dict[int, int] = {}
self.p_stack = -1
self.halted = False
if stack is not None:
if isinstance(stack, list):
self.stack = {i: v for i, v in enumerate(stack)}
else:
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())
steps += 1
return journal
# -------------------------------------------------------------
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]]]:
if "(" in call:
name, rest = call.split("(", 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
# -------------------------------------------------------------
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
# 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[List[int]]) -> None:
assert n is not None and len(n) == 1
val = n[0]
self.p_stack += 1
self.stack[self.p_stack] = val
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[List[int]]) -> None:
assert n is not None and len(n) == 1
offset = n[0]
old_sp = self.p_stack
self.p_stack += 1
self.stack[self.p_stack] = self.stack[old_sp + offset]
self.p_prog += 1
# Store top value to relative address n in stack
def _sto(self, n: Optional[List[int]]) -> None:
assert n is not None and len(n) == 1
offset = n[0]
self.stack[self.p_stack + offset] = self.stack[self.p_stack]
self.p_stack -= 1
self.p_prog += 1
# Unconditional jump to address a
def _ujp(self, a: Optional[List[int]]) -> None:
assert a is not None and len(a) == 1
self.p_prog = a[0]
# Conditional jump if top two elements are equal
def _equal(self, a: Optional[List[int]]) -> 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]:
self.p_stack -= 2
self.p_prog = addr
else:
self.p_stack -= 2
self.p_prog += 1
# Conditional jump if second-top ≤ top element
def _leq(self, a: Optional[List[int]]) -> 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]:
self.p_stack -= 2
self.p_prog = addr
else:
self.p_stack -= 2
self.p_prog += 1