MaMa support macros
This commit is contained in:
@@ -24,6 +24,16 @@ class MaMa:
|
|||||||
# Machine halted flag
|
# Machine halted flag
|
||||||
self.halted = False
|
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
|
# Restore initial machine state
|
||||||
def reload(self, stack: Dict[int, int] | List[int] | None = None) -> None:
|
def reload(self, stack: Dict[int, int] | List[int] | None = None) -> None:
|
||||||
@@ -34,15 +44,16 @@ class MaMa:
|
|||||||
self.stack.update({i: v for i, v in enumerate(stack)})
|
self.stack.update({i: v for i, v in enumerate(stack)})
|
||||||
else:
|
else:
|
||||||
self.stack.update(stack)
|
self.stack.update(stack)
|
||||||
|
|
||||||
self.p_prog = 0
|
self.p_prog = 0
|
||||||
self.p_stack = max(self.stack.keys(), default=-1)
|
self.p_stack = max(self.stack.keys(), default=-1)
|
||||||
self.halted = False
|
self.halted = False
|
||||||
self.steps = 0
|
self.steps = 0
|
||||||
|
self.call_stack = []
|
||||||
|
self.in_macro_stack = []
|
||||||
|
|
||||||
def run(self, max_steps: int = 1000) -> List[Dict[str, int|str|dict]]:
|
def run(self, max_steps: int = 1000) -> List[Dict[str, int | str | dict | list]]:
|
||||||
steps = 0
|
steps = 0
|
||||||
journal = [self.config('init')]
|
journal = [self.config("init")]
|
||||||
while not self.halted and steps < max_steps:
|
while not self.halted and steps < max_steps:
|
||||||
journal.append(self.step())
|
journal.append(self.step())
|
||||||
steps += 1
|
steps += 1
|
||||||
@@ -51,35 +62,82 @@ class MaMa:
|
|||||||
def is_halted(self) -> bool:
|
def is_halted(self) -> bool:
|
||||||
return self.halted
|
return self.halted
|
||||||
|
|
||||||
def step(self) -> Dict[str, int|str|dict]:
|
def step(self) -> Dict[str, int | str | dict | list]:
|
||||||
if self.halted or self.p_prog not in self.prog:
|
if self.halted or self.p_prog not in self.prog:
|
||||||
self.halted = True
|
# if macro ended, return to caller
|
||||||
return self.config("halted")
|
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]
|
call = self.prog[self.p_prog]
|
||||||
name, arg = self.decode(call)
|
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)
|
method = getattr(self, f"_{name}", None)
|
||||||
if method is None:
|
if method is None:
|
||||||
raise ValueError(f"Unknown instruction: {call}")
|
raise ValueError(f"Unknown instruction: {call}")
|
||||||
|
|
||||||
method(arg)
|
method(args)
|
||||||
self.steps += 1
|
self.steps += 1
|
||||||
return self.config(call)
|
return self.config(call)
|
||||||
|
|
||||||
def config(self, call: str) -> Dict[str, int|str|dict]:
|
# Return current configuration including macro nesting
|
||||||
|
def config(self, call: str) -> Dict[str, int | str | dict | list]:
|
||||||
return {
|
return {
|
||||||
"step": self.steps,
|
"step": self.steps,
|
||||||
"call": call,
|
"call": call,
|
||||||
"p_prog": self.p_prog,
|
"p_prog": self.p_prog,
|
||||||
"p_stack": self.p_stack,
|
"p_stack": self.p_stack,
|
||||||
"stack": dict(self.stack),
|
"stack": dict(self.stack),
|
||||||
|
"in_macro": list(self.in_macro_stack), # first = outermost, last = innermost
|
||||||
}
|
}
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def decode(call: str) -> Tuple[str, Optional[int]]:
|
def decode(call: str) -> Tuple[str, Optional[List[int]]]:
|
||||||
if "(" in call:
|
if "(" in call:
|
||||||
name, arg = call.split("(")
|
name, rest = call.split("(", 1)
|
||||||
return name, int(arg[:-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
|
return call, None
|
||||||
|
|
||||||
# Stop execution
|
# Stop execution
|
||||||
@@ -98,10 +156,11 @@ class MaMa:
|
|||||||
self.p_prog += 1
|
self.p_prog += 1
|
||||||
|
|
||||||
# Push constant n onto stack
|
# Push constant n onto stack
|
||||||
def _push(self, n: Optional[int]) -> None:
|
def _push(self, n: Optional[List[int]]) -> None:
|
||||||
assert n is not None
|
assert n is not None and len(n) == 1
|
||||||
|
val = n[0]
|
||||||
self.p_stack += 1
|
self.p_stack += 1
|
||||||
self.stack[self.p_stack] = n
|
self.stack[self.p_stack] = val
|
||||||
self.p_prog += 1
|
self.p_prog += 1
|
||||||
|
|
||||||
# Add top two stack elements
|
# Add top two stack elements
|
||||||
@@ -129,41 +188,45 @@ class MaMa:
|
|||||||
self.p_prog += 1
|
self.p_prog += 1
|
||||||
|
|
||||||
# Load value from relative address n in stack
|
# Load value from relative address n in stack
|
||||||
def _ldo(self, n: Optional[int]) -> None:
|
def _ldo(self, n: Optional[List[int]]) -> None:
|
||||||
assert n is not None
|
assert n is not None and len(n) == 1
|
||||||
|
offset = n[0]
|
||||||
old_sp = self.p_stack
|
old_sp = self.p_stack
|
||||||
self.p_stack += 1
|
self.p_stack += 1
|
||||||
self.stack[self.p_stack] = self.stack[old_sp + n]
|
self.stack[self.p_stack] = self.stack[old_sp + offset]
|
||||||
self.p_prog += 1
|
self.p_prog += 1
|
||||||
|
|
||||||
# Store top value to relative address n in stack
|
# Store top value to relative address n in stack
|
||||||
def _sto(self, n: Optional[int]) -> None:
|
def _sto(self, n: Optional[List[int]]) -> None:
|
||||||
assert n is not None
|
assert n is not None and len(n) == 1
|
||||||
self.stack[self.p_stack + n] = self.stack[self.p_stack]
|
offset = n[0]
|
||||||
|
self.stack[self.p_stack + offset] = self.stack[self.p_stack]
|
||||||
self.p_stack -= 1
|
self.p_stack -= 1
|
||||||
self.p_prog += 1
|
self.p_prog += 1
|
||||||
|
|
||||||
# Unconditional jump to address a
|
# Unconditional jump to address a
|
||||||
def _ujp(self, a: Optional[int]) -> None:
|
def _ujp(self, a: Optional[List[int]]) -> None:
|
||||||
assert a is not None
|
assert a is not None and len(a) == 1
|
||||||
self.p_prog = a
|
self.p_prog = a[0]
|
||||||
|
|
||||||
# Conditional jump if top two elements are equal
|
# Conditional jump if top two elements are equal
|
||||||
def _equal(self, a: Optional[int]) -> None:
|
def _equal(self, a: Optional[List[int]]) -> None:
|
||||||
assert a is not 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]:
|
if self.stack[self.p_stack - 1] == self.stack[self.p_stack]:
|
||||||
self.p_stack -= 2
|
self.p_stack -= 2
|
||||||
self.p_prog = a
|
self.p_prog = addr
|
||||||
else:
|
else:
|
||||||
self.p_stack -= 2
|
self.p_stack -= 2
|
||||||
self.p_prog += 1
|
self.p_prog += 1
|
||||||
|
|
||||||
# Conditional jump if second-top ≤ top element
|
# Conditional jump if second-top ≤ top element
|
||||||
def _leq(self, a: Optional[int]) -> None:
|
def _leq(self, a: Optional[List[int]]) -> None:
|
||||||
assert a is not 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]:
|
if self.stack[self.p_stack - 1] <= self.stack[self.p_stack]:
|
||||||
self.p_stack -= 2
|
self.p_stack -= 2
|
||||||
self.p_prog = a
|
self.p_prog = addr
|
||||||
else:
|
else:
|
||||||
self.p_stack -= 2
|
self.p_stack -= 2
|
||||||
self.p_prog += 1
|
self.p_prog += 1
|
||||||
|
|||||||
31
Uebung-01/test.py
Normal file
31
Uebung-01/test.py
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
from MaMaGUI import MaMaGUI
|
||||||
|
from MaMa import MaMa
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
prog = {
|
||||||
|
0: 'outer',
|
||||||
|
1: 'stop'
|
||||||
|
}
|
||||||
|
|
||||||
|
machine = MaMa(prog, [4, 6])
|
||||||
|
|
||||||
|
inner_prog = [
|
||||||
|
'push(1)',
|
||||||
|
'push(2)',
|
||||||
|
'add',
|
||||||
|
'stop'
|
||||||
|
]
|
||||||
|
|
||||||
|
outer_prog = [
|
||||||
|
'push(10)',
|
||||||
|
'inner',
|
||||||
|
'mult',
|
||||||
|
'stop'
|
||||||
|
]
|
||||||
|
|
||||||
|
machine.add_macro("inner", inner_prog, [])
|
||||||
|
machine.add_macro("outer", outer_prog, [])
|
||||||
|
|
||||||
|
# Run machine once for GUI visualization
|
||||||
|
gui = MaMaGUI(machine)
|
||||||
|
gui.display()
|
||||||
Reference in New Issue
Block a user