Implement MaMa machine
This commit is contained in:
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
@@ -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
|
||||||
10
.idea/Construction-of-Compilers.iml
generated
Normal file
10
.idea/Construction-of-Compilers.iml
generated
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="PYTHON_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="jdk" jdkName="Python 3.13 (Construction-of-Compilers)" jdkType="Python SDK" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
7
.idea/dictionaries/project.xml
generated
Normal file
7
.idea/dictionaries/project.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<component name="ProjectDictionaryState">
|
||||||
|
<dictionary name="project">
|
||||||
|
<words>
|
||||||
|
<w>ldsp</w>
|
||||||
|
</words>
|
||||||
|
</dictionary>
|
||||||
|
</component>
|
||||||
30
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
30
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<profile version="1.0">
|
||||||
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="PyPep8Inspection" enabled="true" level="INFORMATION" enabled_by_default="true">
|
||||||
|
<option name="ignoredErrors">
|
||||||
|
<list>
|
||||||
|
<option value="E302" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PyPep8NamingInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||||
|
<option name="ignoredErrors">
|
||||||
|
<list>
|
||||||
|
<option value="N802" />
|
||||||
|
<option value="N806" />
|
||||||
|
<option value="N803" />
|
||||||
|
<option value="N801" />
|
||||||
|
<option value="N812" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PyUnresolvedReferencesInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="ignoredIdentifiers">
|
||||||
|
<list>
|
||||||
|
<option value="int.*" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</inspection_tool>
|
||||||
|
</profile>
|
||||||
|
</component>
|
||||||
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<settings>
|
||||||
|
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||||
|
<version value="1.0" />
|
||||||
|
</settings>
|
||||||
|
</component>
|
||||||
7
.idea/misc.xml
generated
Normal file
7
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Black">
|
||||||
|
<option name="sdkName" value="Python 3.13 (python_clients)" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.13 (Construction-of-Compilers)" project-jdk-type="Python SDK" />
|
||||||
|
</project>
|
||||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/Construction-of-Compilers.iml" filepath="$PROJECT_DIR$/.idea/Construction-of-Compilers.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
7
.idea/vcs.xml
generated
Normal file
7
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="Git" />
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
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
|
||||||
145
Uebung-01/MaMaGUI.py
Normal file
145
Uebung-01/MaMaGUI.py
Normal file
@@ -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()
|
||||||
34
Uebung-01/ggt.py
Normal file
34
Uebung-01/ggt.py
Normal file
@@ -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()
|
||||||
Reference in New Issue
Block a user