diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/Construction-of-Compilers.iml b/.idea/Construction-of-Compilers.iml new file mode 100644 index 0000000..24ef2ea --- /dev/null +++ b/.idea/Construction-of-Compilers.iml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/dictionaries/project.xml b/.idea/dictionaries/project.xml new file mode 100644 index 0000000..d335fa7 --- /dev/null +++ b/.idea/dictionaries/project.xml @@ -0,0 +1,7 @@ + + + + ldsp + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..a794ecf --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,30 @@ + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..f7274d5 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..dfd6964 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..8306744 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/Uebung-01/MaMa.py b/Uebung-01/MaMa.py new file mode 100644 index 0000000..c2ea282 --- /dev/null +++ b/Uebung-01/MaMa.py @@ -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 diff --git a/Uebung-01/MaMaGUI.py b/Uebung-01/MaMaGUI.py new file mode 100644 index 0000000..0f787e8 --- /dev/null +++ b/Uebung-01/MaMaGUI.py @@ -0,0 +1,145 @@ +import tkinter as tk +from tkinter import ttk +from typing import Dict, List +from MaMa import MaMa + +class MaMaGUI: + def __init__(self, mama: MaMa, delay: int = 400) -> None: + self.machine = mama + self.delay = delay + self.index = 0 + self.journal: List[Dict[str, object]] = [] + + # Tracks automatic running state + self.running = False + + # Run machine once and include initial configuration + self.journal = self.machine.run() + if not self.journal: + raise ValueError("Journal is empty. Make sure MaMa executed successfully.") + print(self.journal) + + self.root = tk.Tk() + self.root.title("MaMa GUI") + + # Prepare rows and columns + container = ttk.Frame(self.root, padding=10) + container.grid(sticky="nsew") + container.columnconfigure(0, weight=1) + container.columnconfigure(1, weight=1) + container.rowconfigure(0, weight=1) + + # Program display + prog_frame = ttk.LabelFrame(container, text="Program", padding=5) + prog_frame.grid(row=0, column=0, sticky="nsew", padx=(0, 10)) + self.prog_list = tk.Listbox(prog_frame, width=30, height=22, font=("Consolas", 11)) + self.prog_list.pack(fill="both", expand=True) + + # Stack and configuration display + stack_frame = ttk.LabelFrame(container, text="Stack", padding=5) + stack_frame.grid(row=0, column=1, sticky="nsew") + stack_frame.rowconfigure(0, weight=1) + stack_frame.columnconfigure(0, weight=1) + self.stack_list = tk.Listbox(stack_frame, width=32, height=20, font=("Consolas", 11)) + self.stack_list.grid(row=0, column=0, sticky="nsew") + + # Configuration label + self.conf_label = ttk.Label(stack_frame, text="(s, p, S) = (-, -, [])", font=("Consolas", 11), anchor="e") + self.conf_label.grid(row=1, column=0, sticky="ew", pady=(4, 0)) + + # Init and bind controls + control = ttk.Frame(self.root, padding=5) + control.grid(row=1, column=0, columnspan=2, sticky="ew") + + ttk.Button(control, text="▶ Start", command=self.start).pack(side="left", padx=5) + ttk.Button(control, text="⏸ Stop", command=self.stop).pack(side="left", padx=5) + ttk.Button(control, text="⏭ Next", command=self.next_step).pack(side="left", padx=5) + ttk.Button(control, text="↩ Prev", command=self.prev_step).pack(side="left", padx=5) + ttk.Button(control, text="⟲ Reset", command=self.reset).pack(side="left", padx=5) + + ttk.Label(control, text="Speed:").pack(side="left", padx=5) + self.speed_scale = ttk.Scale(control, from_=2000, to=100, orient="horizontal", command=self.__update_speed) + self.speed_scale.set(self.delay) + self.speed_scale.pack(side="left", padx=5) + + # Status bar + self.status = ttk.Label(self.root, text="Ready", padding=5, anchor="w") + self.status.grid(row=2, column=0, columnspan=2, sticky="ew") + + # Initial update + self.__update() + + def __update(self) -> None: + state = self.journal[self.index] + prog = self.machine.prog + p_prog = state["p_prog"] + p_stack = state["p_stack"] + stack = state["stack"] + step = state["step"] + call = state["call"] + + # Program display + self.prog_list.delete(0, tk.END) + for addr, instr in prog.items(): + prefix = ">> " if addr == p_prog else " " + self.prog_list.insert(tk.END, f"{prefix}{addr:>3}: {instr}") + if addr == p_prog: + self.prog_list.itemconfig(tk.END, {"bg": "#ffd966"}) + + # Stack display + self.stack_list.delete(0, tk.END) + if stack: + for i in range(p_stack, -1, -1): + val = stack.get(i, 0) + mark = "<-- top" if i == p_stack else "" + self.stack_list.insert(tk.END, f"[{i:>2}] {val:>6} {mark}") + if i == p_stack: + self.stack_list.itemconfig(tk.END, {"bg": "#cfe2f3"}) + else: + self.stack_list.insert(tk.END, "(empty)") + + # Configuration line + conf_str = f"({p_stack}, {p_prog}, {list(stack.values())})" + self.conf_label.config(text=conf_str) + + # Status update + status = f"Step {step}/{len(self.journal)-1} | PC={p_prog} | SP={p_stack} | Last call: {call}" + self.status.config(text=status) + + def __update_speed(self, value: str) -> None: + self.delay = int(float(value)) + + def start(self) -> None: + if not self.running: + self.running = True + self.__auto_step() + + def stop(self) -> None: + self.running = False + + def __auto_step(self) -> None: + if self.running and self.index < len(self.journal) - 1: + self.index += 1 + self.__update() + self.root.after(self.delay, self.__auto_step) + else: + self.running = False + + def next_step(self) -> None: + if self.index < len(self.journal) - 1: + self.index += 1 + self.__update() + + def prev_step(self) -> None: + if self.index > 0: + self.index -= 1 + self.__update() + + def reset(self) -> None: + self.machine.reload() + self.journal = self.machine.run() + self.index = 0 + self.__update() + + def display(self) -> None: + self.root.mainloop() diff --git a/Uebung-01/ggt.py b/Uebung-01/ggt.py new file mode 100644 index 0000000..99e0b1d --- /dev/null +++ b/Uebung-01/ggt.py @@ -0,0 +1,34 @@ +from MaMaGUI import MaMaGUI +from MaMa import MaMa + +if __name__ == "__main__": + prog = { + 0: 'ldo(-1)', + 1: 'push(0)', + 2: 'equal(17)', + 3: 'push(0)', + 4: 'ldo(-1)', + 5: 'equal(19)', + 6: 'ldo(-1)', + 7: 'ldo(-1)', + 8: 'leq(14)', + 9: 'ldo(-1)', + 10: 'ldo(-1)', + 11: 'sub', + 12: 'sto(-2)', + 13: 'ujp(6)', + 14: 'ldo(-1)', + 15: 'sub', + 16: 'ujp(3)', + 17: 'sto(-1)', + 18: 'stop', + 19: 'pop', + 20: 'stop' + } + + # Create and execute MaMa instance + machine = MaMa(prog, [4, 6]) + + # Visualize finished execution using journal + gui = MaMaGUI(machine) + gui.display()