First implementation of the compiler
This commit is contained in:
170
Project-02-03/compiler.py
Normal file
170
Project-02-03/compiler.py
Normal file
@@ -0,0 +1,170 @@
|
||||
import syntax
|
||||
from vistram.tram import *
|
||||
|
||||
label_counter = 0
|
||||
|
||||
def new_label():
|
||||
global label_counter
|
||||
label_counter += 1
|
||||
return Label(text=f"L{label_counter}")
|
||||
|
||||
def elab_def(decls, rho, nl):
|
||||
r = dict(rho)
|
||||
for d in decls:
|
||||
r = d.elab(r, nl)
|
||||
return r
|
||||
|
||||
class CONST(syntax.CONST):
|
||||
def code(self, rho, nl):
|
||||
return [const(self.value)]
|
||||
|
||||
class ID(syntax.ID):
|
||||
def code(self, rho, nl):
|
||||
k, nlp = rho[self.name]
|
||||
return [load(k, nl - nlp)]
|
||||
|
||||
class AOP(syntax.AOP):
|
||||
def code(self, rho, nl):
|
||||
c1 = self.arg1.code(rho, nl)
|
||||
c2 = self.arg2.code(rho, nl)
|
||||
op = self.operator
|
||||
if op == "+":
|
||||
return c1 + c2 + [add()]
|
||||
if op == "-":
|
||||
return c1 + c2 + [sub()]
|
||||
if op == "*":
|
||||
return c1 + c2 + [mul()]
|
||||
return c1 + c2 + [div()]
|
||||
|
||||
class COMP(syntax.COMP):
|
||||
def code(self, rho, nl):
|
||||
c1 = self.arg1.code(rho, nl)
|
||||
c2 = self.arg2.code(rho, nl)
|
||||
if self.operator == "<":
|
||||
return c1 + c2 + [lt()]
|
||||
if self.operator == ">":
|
||||
return c1 + c2 + [gt()]
|
||||
if self.operator == "<=":
|
||||
return c1 + c2 + [gt(), nop()] # NOT IMPLEMENTED IN TRAM
|
||||
if self.operator == ">=":
|
||||
return c1 + c2 + [lt(), nop()] # NOT IMPLEMENTED IN TRAM
|
||||
return c1 + c2
|
||||
|
||||
class EQOP(syntax.EQOP):
|
||||
def code(self, rho, nl):
|
||||
c1 = self.arg1.code(rho, nl)
|
||||
c2 = self.arg2.code(rho, nl)
|
||||
if self.operator == "==":
|
||||
return c1 + c2 + [eq()]
|
||||
return c1 + c2 + [neq()]
|
||||
|
||||
class LOP(syntax.LOP):
|
||||
def code(self, rho, nl):
|
||||
# TRIPLA: logical operators must be done by short-circuiting (not strict)
|
||||
# but your TRAM has no boolean ops, so fallback to == and pop
|
||||
c1 = self.arg1.code(rho, nl)
|
||||
c2 = self.arg2.code(rho, nl)
|
||||
if self.operator == "&&":
|
||||
l = new_label()
|
||||
return (
|
||||
c1 +
|
||||
[ifzero(l)] +
|
||||
c2 +
|
||||
[ifzero(l)] +
|
||||
[const(1)] +
|
||||
[nop(assigned_label=l)]
|
||||
)
|
||||
if self.operator == "||":
|
||||
l = new_label()
|
||||
return (
|
||||
c1 +
|
||||
[ifzero(l)] +
|
||||
[const(1)] +
|
||||
[goto(l)] +
|
||||
[nop(assigned_label=l)] +
|
||||
c2
|
||||
)
|
||||
return c1 + c2
|
||||
|
||||
class ASSIGN(syntax.ASSIGN):
|
||||
def code(self, rho, nl):
|
||||
c = self.expr.code(rho, nl)
|
||||
k, nlp = rho[self.var.name]
|
||||
d = nl - nlp
|
||||
return c + [store(k, d), load(k, d)]
|
||||
|
||||
class SEQ(syntax.SEQ):
|
||||
def code(self, rho, nl):
|
||||
c1 = self.exp1.code(rho, nl)
|
||||
c2 = self.exp2.code(rho, nl)
|
||||
return c1 + [pop()] + c2
|
||||
|
||||
class IF(syntax.IF):
|
||||
def code(self, rho, nl):
|
||||
l1 = new_label()
|
||||
l2 = new_label()
|
||||
c1 = self.cond.code(rho, nl)
|
||||
c2 = self.exp1.code(rho, nl)
|
||||
c3 = self.exp2.code(rho, nl)
|
||||
return (
|
||||
c1 +
|
||||
[ifzero(l1)] +
|
||||
c2 +
|
||||
[goto(l2)] +
|
||||
[nop(assigned_label=l1)] +
|
||||
c3 +
|
||||
[nop(assigned_label=l2)]
|
||||
)
|
||||
|
||||
class WHILE(syntax.WHILE):
|
||||
def code(self, rho, nl):
|
||||
l1 = new_label() # loop header
|
||||
l2 = new_label() # exit
|
||||
l3 = new_label() # repeat
|
||||
l4 = new_label() # evaluate body
|
||||
return (
|
||||
self.cond.code(rho, nl)
|
||||
+ [ifzero(l2), goto(l4)]
|
||||
+ [nop(assigned_label=l1)]
|
||||
+ self.cond.code(rho, nl)
|
||||
+ [ifzero(l3), pop()]
|
||||
+ [nop(assigned_label=l4)]
|
||||
+ self.body.code(rho, nl)
|
||||
+ [goto(l1)]
|
||||
+ [nop(assigned_label=l2), const(None), nop(assigned_label=l3)]
|
||||
)
|
||||
|
||||
class LET(syntax.LET):
|
||||
def code(self, rho, nl):
|
||||
l = new_label()
|
||||
rho2 = elab_def(self.decl, rho, nl)
|
||||
cd = []
|
||||
for d in self.decl:
|
||||
cd += d.code(rho2, nl)
|
||||
ce = self.body.code(rho2, nl)
|
||||
return [goto(l)] + cd + [nop(assigned_label=l)] + ce
|
||||
|
||||
class DECL(syntax.DECL):
|
||||
def elab(self, rho, nl):
|
||||
r = dict(rho)
|
||||
r[self.f_name] = (new_label(), nl)
|
||||
return r
|
||||
|
||||
def code(self, rho, nl):
|
||||
l, _ = rho[self.f_name]
|
||||
rho2 = dict(rho)
|
||||
for i, p in enumerate(self.params):
|
||||
rho2[p] = (i, nl + 1)
|
||||
return (
|
||||
[nop(assigned_label=l)]
|
||||
+ self.body.code(rho2, nl + 1)
|
||||
+ [ireturn()]
|
||||
)
|
||||
|
||||
class CALL(syntax.CALL):
|
||||
def code(self, rho, nl):
|
||||
c = []
|
||||
for a in self.arg:
|
||||
c += a.code(rho, nl)
|
||||
l, nlp = rho[self.f_name]
|
||||
return c + [invoke(len(self.arg), l, nl - nlp)]
|
||||
0
Project-02-03/helpers/__init__.py
Normal file
0
Project-02-03/helpers/__init__.py
Normal file
@@ -1,3 +1,5 @@
|
||||
from pathlib import Path
|
||||
|
||||
def prompt_choice(prompt: str, options: list) -> int:
|
||||
print(prompt)
|
||||
for i, opt in enumerate(options, 1):
|
||||
@@ -18,9 +20,3 @@ def prompt_confirmation(question: str, default="y"):
|
||||
if not s:
|
||||
s = default
|
||||
return s.startswith("y")
|
||||
|
||||
def search_programs():
|
||||
base = Path(__file__).parent / "triplaprograms"
|
||||
if not base.exists():
|
||||
return []
|
||||
return sorted([f for f in base.glob("*.tripla")])
|
||||
@@ -1,18 +1,26 @@
|
||||
import triplayacc as yacc
|
||||
import triplalex as lex
|
||||
import syntax
|
||||
|
||||
from pathlib import Path
|
||||
from graphviz import Source
|
||||
from vistram.tram import *
|
||||
|
||||
import tkinter as tk
|
||||
from vistram.vistram import MachineUI
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.image as mpimg
|
||||
import lib.console as cnsl
|
||||
import helpers.console as cnsl
|
||||
import os
|
||||
|
||||
import matplotlib
|
||||
matplotlib.use("TkAgg")
|
||||
|
||||
# Assembles the AST into TRAM code
|
||||
def assemble(ast):
|
||||
code = ast.code({}, 0)
|
||||
return code + [halt()]
|
||||
|
||||
# Renders a diagram of the AST
|
||||
def render_diagram(dot_string: str):
|
||||
# Set DPI for PNG
|
||||
@@ -60,26 +68,28 @@ def export_dot_file(dot_string: str, filename: str):
|
||||
print(f"Could not save DOT file: {e}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("\nTRIPLA Parser Tool")
|
||||
print("\nTRIPLA Parser and TRIPLA to TRAM Compiler")
|
||||
|
||||
while True:
|
||||
choice = cnsl.prompt_choice("\nSelect action:", ["Parse .tripla", "Exit"])
|
||||
mode = cnsl.prompt_choice("\nSelect action:", ["Parse .tripla", "Compile .tripla", "Exit"])
|
||||
|
||||
if choice == 1:
|
||||
if mode == 2:
|
||||
print("\nBye Bye.")
|
||||
break
|
||||
|
||||
programs = cnsl.search_programs()
|
||||
base = Path(__file__).parent / "triplaprograms"
|
||||
programs = sorted([f for f in base.glob("*.tripla")])
|
||||
if not programs:
|
||||
print("\nNo .tripla files found.")
|
||||
continue
|
||||
|
||||
idx = cnsl.prompt_choice("\nSelect program to parse:", [p.name for p in programs])
|
||||
idx = cnsl.prompt_choice("\nSelect TRIPLA program:", [p.name for p in programs])
|
||||
path = programs[idx]
|
||||
|
||||
source = path.read_text()
|
||||
ast = yacc.parser.parse(source)
|
||||
|
||||
if mode == 0:
|
||||
# Pretty print
|
||||
if cnsl.prompt_confirmation("\nPretty-print AST?"):
|
||||
print("")
|
||||
@@ -98,3 +108,37 @@ if __name__ == "__main__":
|
||||
if cnsl.prompt_confirmation("Display AST diagram?"):
|
||||
render_diagram(dot_str)
|
||||
print("Rendered AST diagram.")
|
||||
|
||||
elif mode == 1:
|
||||
tram_code = assemble(ast)
|
||||
|
||||
if cnsl.prompt_confirmation("\nPrint TRAM code to console?"):
|
||||
print("\nGenerated TRAM code:\n")
|
||||
for instr in tram_code:
|
||||
print(instr.toString())
|
||||
|
||||
if cnsl.prompt_confirmation("Save TRAM code as .tram file?"):
|
||||
base_dir = Path(__file__).parent / "tramcodes"
|
||||
base_dir.mkdir(exist_ok=True)
|
||||
|
||||
default = f"{path.stem}.tram"
|
||||
filename = input(f"Filename [{default}]: ").strip()
|
||||
if not filename:
|
||||
filename = default
|
||||
|
||||
out_path = base_dir / filename
|
||||
|
||||
with open(out_path, "w") as f:
|
||||
for instr in tram_code:
|
||||
f.write(instr.toString() + "\n")
|
||||
|
||||
print(f"Saved TRAM code to: {out_path}")
|
||||
|
||||
if cnsl.prompt_confirmation("Display TRAM code in Visual TRAM UI?"):
|
||||
root = tk.Tk()
|
||||
ui = MachineUI(root)
|
||||
ui.machine.initial_program = tram_code
|
||||
ui.machine.reset()
|
||||
root.mainloop()
|
||||
|
||||
|
||||
|
||||
@@ -61,7 +61,6 @@ class EXPRESSION:
|
||||
|
||||
return "".join(parts)
|
||||
|
||||
|
||||
class LET(EXPRESSION):
|
||||
def __init__(self, decls, body):
|
||||
super().__init__()
|
||||
|
||||
10
Project-02-03/tramcodes/argsParamsExample.tram
Normal file
10
Project-02-03/tramcodes/argsParamsExample.tram
Normal file
@@ -0,0 +1,10 @@
|
||||
GOTO L1
|
||||
L2: NOP
|
||||
LOAD 0 0
|
||||
RETURN
|
||||
L1: NOP
|
||||
CONST 1
|
||||
CONST 2
|
||||
CONST 3
|
||||
INVOKE 3 L2 0
|
||||
HALT
|
||||
@@ -3,7 +3,7 @@
|
||||
# ------------------------------------------------------------
|
||||
|
||||
import ply.yacc as yacc
|
||||
import syntax as ast
|
||||
import compiler as ast
|
||||
from triplalex import tokens
|
||||
|
||||
# Operator precedence
|
||||
|
||||
0
Project-02-03/vistram/__init__.py
Normal file
0
Project-02-03/vistram/__init__.py
Normal file
76
Project-02-03/vistram/assembler.py
Normal file
76
Project-02-03/vistram/assembler.py
Normal file
@@ -0,0 +1,76 @@
|
||||
# (c) Stephan Diehl, University of Trier, Germany, 2025
|
||||
from . import tram
|
||||
|
||||
class Assembler:
|
||||
@staticmethod
|
||||
def read_tram_code(filename):
|
||||
code = []
|
||||
lines = []
|
||||
labelmap = {}
|
||||
linemap = {}
|
||||
|
||||
try:
|
||||
with open(filename, 'r', encoding='utf-8') as f:
|
||||
line_number = 0
|
||||
for raw_line in f:
|
||||
line = raw_line.strip()
|
||||
if line.startswith("//") or line.startswith("#") or not line:
|
||||
continue
|
||||
if ":" in line:
|
||||
labels_part = line[:line.index(':')]
|
||||
labels = [label.strip() for label in labels_part.split(',')]
|
||||
linemap[line_number] = labels
|
||||
for label in labels:
|
||||
labelmap[label] = tram.Label(line_number,label)
|
||||
line = line[line.index(':') + 1:].strip()
|
||||
lines.append(line)
|
||||
line_number += 1
|
||||
except IOError as e:
|
||||
print(f"Fehler beim Lesen der Datei: {e}")
|
||||
|
||||
for i, line in enumerate(lines):
|
||||
labels = [labelmap.get(label,None) for label in linemap.get(i,[])]
|
||||
instr = Assembler.convert_to_instruction(line, labelmap, labels)
|
||||
code.append(instr)
|
||||
|
||||
return code
|
||||
|
||||
@staticmethod
|
||||
def convert_to_instruction(line, labelmap={}, labels=None):
|
||||
parts = line.split()
|
||||
instr =parts[0].upper()
|
||||
arg1 = Assembler.arg_to_number(parts, 1, labelmap)
|
||||
arg2 = Assembler.arg_to_number(parts, 2, labelmap)
|
||||
arg3 = Assembler.arg_to_number(parts, 3, labelmap)
|
||||
if instr == "CONST": code=tram.const(arg1,assigned_label=labels)
|
||||
elif instr == "LOAD": code=tram.load(arg1, arg2,assigned_label=labels)
|
||||
elif instr == "STORE": code=tram.store(arg1,arg2,assigned_label=labels)
|
||||
elif instr == "ADD": code=tram.add(assigned_label=labels)
|
||||
elif instr == "SUB": code=tram.sub(assigned_label=labels)
|
||||
elif instr == "MUL": code=tram.mul(assigned_label=labels)
|
||||
elif instr == "DIV": code=tram.div(assigned_label=labels)
|
||||
elif instr == "LT": code=tram.lt(assigned_label=labels)
|
||||
elif instr == "GT": code=tram.gt(assigned_label=labels)
|
||||
elif instr == "EQ": code=tram.eq(assigned_label=labels)
|
||||
elif instr == "NEQ": code=tram.neq(assigned_label=labels)
|
||||
elif instr == "IFZERO": code=tram.ifzero(arg1,assigned_label=labels)
|
||||
elif instr == "GOTO": code=tram.goto(arg1,assigned_label=labels)
|
||||
elif instr == "HALT": code=tram.halt(assigned_label=labels)
|
||||
elif instr == "NOP": code=tram.nop(assigned_label=labels)
|
||||
elif instr == "INVOKE": code=tram.invoke(arg1,arg2,arg3,assigned_label=labels)
|
||||
elif instr == "RETURN": code=tram.ireturn(assigned_label=labels)
|
||||
elif instr == "POP": code=tram.pop(assigned_label=labels)
|
||||
else:
|
||||
print(f"Keine gültige Instruktion: {line}")
|
||||
code = None
|
||||
return code
|
||||
|
||||
@staticmethod
|
||||
def arg_to_number(parts, index, labelmap):
|
||||
if index >= len(parts):
|
||||
return 0
|
||||
arg = parts[index]
|
||||
try:
|
||||
return int(arg)
|
||||
except ValueError:
|
||||
return labelmap.get(arg, 0)
|
||||
339
Project-02-03/vistram/tram.py
Normal file
339
Project-02-03/vistram/tram.py
Normal file
@@ -0,0 +1,339 @@
|
||||
# (c) Stephan Diehl, University of Trier, Germany, 2025
|
||||
|
||||
class TRAM:
|
||||
def __init__(self, prog):
|
||||
self.set_label_addresses(prog)
|
||||
self.program=prog+[halt()]
|
||||
self.print_prog(self.program)
|
||||
self.pc = 0
|
||||
self.stack = []
|
||||
self.top = -1
|
||||
self.pp = 0 #-1
|
||||
self.fp = 0 #-1
|
||||
|
||||
def start(self):
|
||||
while self.pc>=0:
|
||||
self.print_instruction(self.program[self.pc])
|
||||
self.program[self.pc].execute(self)
|
||||
print(self.stack)
|
||||
|
||||
def set_label_addresses(self,code):
|
||||
pos = 0
|
||||
for instr in code:
|
||||
for label in instr.assigned_labels:
|
||||
label.address = pos
|
||||
pos = pos + 1
|
||||
|
||||
def pop(self):
|
||||
del self.stack[self.top]
|
||||
self.top = self.top - 1
|
||||
|
||||
def push(self,v):
|
||||
self.stack.append(v)
|
||||
self.top = self.top + 1
|
||||
|
||||
def spp(self,d,pp,fp):
|
||||
if (d==0):
|
||||
return pp
|
||||
else:
|
||||
return self.spp(d-1,self.stack[self.fp+3],self.stack[self.fp+4])
|
||||
|
||||
def sfp(self, d, pp, fp):
|
||||
if (d==0):
|
||||
return fp
|
||||
else:
|
||||
return self.sfp(d-1, self.stack[self.fp+3], self.stack[self.fp+4])
|
||||
|
||||
def print_prog(self,prog):
|
||||
pos=0
|
||||
for instr in prog:
|
||||
print(str(pos)+": ",end="")
|
||||
pos=pos+1
|
||||
self.print_instruction(instr)
|
||||
|
||||
def print_instruction(self, instr):
|
||||
print(type(instr).__name__, end = "")
|
||||
for attr in vars(instr).keys():
|
||||
value=getattr(instr,attr)
|
||||
if isinstance(value,list): continue
|
||||
if isinstance(value,Label):
|
||||
value="#"+str(value.address)
|
||||
else:
|
||||
value=str(value)
|
||||
print(" "+value,end="")
|
||||
print()
|
||||
|
||||
#################
|
||||
|
||||
class Label:
|
||||
count=0
|
||||
address=-1
|
||||
|
||||
def __init__(self,address=-1,text=""):
|
||||
Label.count+=1
|
||||
self.id=Label.count
|
||||
self.address=address
|
||||
if text=="":
|
||||
self.text=f"L{Label.count}"
|
||||
else:
|
||||
self.text=text
|
||||
|
||||
def toString(self): return self.text
|
||||
|
||||
|
||||
##################
|
||||
|
||||
class Instruction:
|
||||
|
||||
def __init__(self,assigned_label=None):
|
||||
self.assigned_labels = []
|
||||
if not assigned_label is None:
|
||||
if isinstance(assigned_label,Label):
|
||||
self.assigned_labels+=[assigned_label]
|
||||
else:
|
||||
self.assigned_labels+=assigned_label
|
||||
|
||||
def toString(self):
|
||||
s=self.toStringx()
|
||||
#print(s)
|
||||
return(s)
|
||||
|
||||
def toStringx(self):
|
||||
if (len(self.assigned_labels)==0):
|
||||
return " "
|
||||
else:
|
||||
return ','.join( [ label.toString()
|
||||
for label in self.assigned_labels] )+": "
|
||||
|
||||
class halt(Instruction):
|
||||
def __init__(self,assigned_label=None):
|
||||
super().__init__(assigned_label=assigned_label)
|
||||
|
||||
def execute(self,tram):
|
||||
tram.pc=-1
|
||||
|
||||
def toString(self): return super().toString()+"HALT"
|
||||
|
||||
class nop(Instruction):
|
||||
def __init__(self,assigned_label=None):
|
||||
super().__init__(assigned_label=assigned_label)
|
||||
|
||||
def execute(self,tram):
|
||||
tram.pc=tram.pc+1
|
||||
|
||||
def toString(self): return super().toString()+"NOP"
|
||||
|
||||
class pop(Instruction):
|
||||
def __init__(self,assigned_label=None):
|
||||
super().__init__(assigned_label=assigned_label)
|
||||
|
||||
def execute(self,tram):
|
||||
tram.pop()
|
||||
tram.pc=tram.pc+1
|
||||
|
||||
def toString(self): return super().toString()+"POP"
|
||||
|
||||
class const(Instruction):
|
||||
def __init__(self, k, assigned_label=None):
|
||||
super().__init__(assigned_label=assigned_label)
|
||||
self.k=k
|
||||
|
||||
def execute(self,tram):
|
||||
tram.push(self.k)
|
||||
tram.pc=tram.pc+1
|
||||
|
||||
def toString(self): return super().toString()+"CONST "+str(self.k)
|
||||
|
||||
class store(Instruction):
|
||||
def __init__(self, k, assigned_label=None):
|
||||
super().__init__(assigned_label=assigned_label)
|
||||
self.k=k
|
||||
self.d=d
|
||||
|
||||
def execute(self,tram):
|
||||
tram.stack[tram.spp(self.d,tram.pp,tram.fp)+self.k]=tram.stack[tram.top]
|
||||
tram.pop()
|
||||
tram.pc=tram.pc+1
|
||||
|
||||
def toString(self):
|
||||
return super().toString()+"STORE "+str(self.k)+" "+str(self.d)
|
||||
|
||||
class load(Instruction):
|
||||
def __init__(self, k, d, assigned_label=None):
|
||||
super().__init__(assigned_label=assigned_label)
|
||||
self.k=k
|
||||
self.d=d
|
||||
|
||||
def execute(self,tram):
|
||||
tram.push(tram.stack[tram.spp(self.d,tram.pp,tram.fp)+self.k])
|
||||
tram.pc=tram.pc+1
|
||||
|
||||
def toString(self):
|
||||
return super().toString()+"LOAD "+str(self.k)+" "+str(self.d)
|
||||
|
||||
class add(Instruction):
|
||||
def __init__(self, assigned_label=None):
|
||||
super().__init__(assigned_label=assigned_label)
|
||||
|
||||
def execute(self,tram):
|
||||
tram.stack[tram.top-1]=tram.stack[tram.top-1]+tram.stack[tram.top]
|
||||
tram.pop()
|
||||
tram.pc=tram.pc+1
|
||||
|
||||
def toString(self): return super().toString()+"ADD"
|
||||
|
||||
|
||||
|
||||
class sub(Instruction):
|
||||
def __init__(self, assigned_label=None):
|
||||
super().__init__(assigned_label=assigned_label)
|
||||
|
||||
def execute(self,tram):
|
||||
tram.stack[tram.top-1]=tram.stack[tram.top-1]-tram.stack[tram.top]
|
||||
tram.pop()
|
||||
tram.pc=tram.pc+1
|
||||
|
||||
def toString(self): return super().toString() + "SUB"
|
||||
|
||||
class mul(Instruction):
|
||||
def __init__(self, assigned_label=None):
|
||||
super().__init__(assigned_label=assigned_label)
|
||||
|
||||
def execute(self,tram):
|
||||
tram.stack[tram.top-1]=tram.stack[tram.top-1]*tram.stack[tram.top]
|
||||
tram.pop()
|
||||
tram.pc=tram.pc+1
|
||||
|
||||
def toString(self): return super().toString() + "MUL"
|
||||
|
||||
class div(Instruction):
|
||||
def __init__(self, assigned_label=None):
|
||||
super().__init__(assigned_label=assigned_label)
|
||||
|
||||
def execute(self,tram):
|
||||
tram.stack[tram.top-1]=tram.stack[tram.top-1]/tram.stack[tram.top]
|
||||
tram.pop()
|
||||
tram.pc=tram.pc+1
|
||||
|
||||
def toString(self): return super().toString() + "DIV"
|
||||
|
||||
class lt(Instruction):
|
||||
def __init__(self, assigned_label=None):
|
||||
super().__init__(assigned_label=assigned_label)
|
||||
|
||||
def execute(self,tram):
|
||||
if tram.stack[tram.top-1]<tram.stack[tram.top]:
|
||||
tram.stack[tram.top-1]=1
|
||||
else:
|
||||
tram.stack[tram.top-1]=0
|
||||
tram.pop()
|
||||
tram.pc=tram.pc+1
|
||||
|
||||
def toString(self): return super().toString() + "LT"
|
||||
|
||||
class gt(Instruction):
|
||||
def __init__(self, assigned_label=None):
|
||||
super().__init__(assigned_label=assigned_label)
|
||||
|
||||
def execute(self,tram):
|
||||
if tram.stack[tram.top-1]>tram.stack[tram.top]:
|
||||
tram.stack[tram.top-1]=1
|
||||
else:
|
||||
tram.stack[tram.top-1]=0
|
||||
tram.pop()
|
||||
tram.pc=tram.pc+1
|
||||
|
||||
def toString(self): return super().toString() + "GT"
|
||||
|
||||
class eq(Instruction):
|
||||
def __init__(self, assigned_label=None):
|
||||
super().__init__(assigned_label=assigned_label)
|
||||
|
||||
def execute(self,tram):
|
||||
if tram.stack[tram.top-1]==tram.stack[tram.top]:
|
||||
tram.stack[tram.top-1]=1
|
||||
else:
|
||||
tram.stack[tram.top-1]=0
|
||||
tram.pop()
|
||||
tram.pc=tram.pc+1
|
||||
|
||||
def toString(self): return super().toString() + "EQ"
|
||||
|
||||
class neq(Instruction):
|
||||
def __init__(self, assigned_label=None):
|
||||
super().__init__(assigned_label=assigned_label)
|
||||
|
||||
def execute(self,tram):
|
||||
if tram.stack[tram.top-1]!=tram.stack[tram.top]:
|
||||
tram.stack[tram.top-1]=1
|
||||
else:
|
||||
tram.stack[tram.top-1]=0
|
||||
tram.pop()
|
||||
tram.pc=tram.pc+1
|
||||
|
||||
def toString(self): return super().toString() + "NEQ"
|
||||
|
||||
class goto(Instruction):
|
||||
def __init__(self, label, assigned_label=None):
|
||||
super().__init__(assigned_label=assigned_label)
|
||||
self.label=label
|
||||
|
||||
def execute(self,tram):
|
||||
tram.pc=self.label.address
|
||||
|
||||
def toString(self):
|
||||
return super().toString() + "GOTO "+self.label.toString()
|
||||
|
||||
class ifzero(Instruction):
|
||||
def __init__(self, label, assigned_label=None):
|
||||
super().__init__(assigned_label=assigned_label)
|
||||
self.label=label
|
||||
|
||||
def execute(self,tram):
|
||||
if tram.stack[tram.top]==0:
|
||||
tram.pc=self.label.address
|
||||
else:
|
||||
tram.pc=tram.pc+1
|
||||
tram.pop()
|
||||
|
||||
def toString(self):
|
||||
return super().toString() + "IFZERO "+self.label.toString()
|
||||
|
||||
class invoke(Instruction):
|
||||
def __init__(self,n,label,d, assigned_label=None):
|
||||
super().__init__(assigned_label=assigned_label)
|
||||
self.n=n
|
||||
self.label=label
|
||||
self.d=d
|
||||
|
||||
def execute(self,tram):
|
||||
tmp_top=tram.top
|
||||
tram.push(tram.pc+1)
|
||||
tram.push(tram.pp)
|
||||
tram.push(tram.fp)
|
||||
tram.push(tram.spp(self.d,tram.pp,tram.fp))
|
||||
tram.push(tram.sfp(self.d,tram.pp,tram.fp))
|
||||
tram.pp=tmp_top-self.n+1
|
||||
tram.fp=tmp_top+1
|
||||
tram.pc=self.label.address
|
||||
|
||||
def toString(self):
|
||||
return super().toString() \
|
||||
+ "INVOKE "+str(self.n)+" "+self.label.toString()+" "+str(self.d)
|
||||
|
||||
class ireturn(Instruction):
|
||||
def __init__(self, assigned_label=None):
|
||||
super().__init__(assigned_label=assigned_label)
|
||||
|
||||
def execute(self,tram):
|
||||
res=tram.stack[tram.top]
|
||||
tram.top=tram.pp
|
||||
tram.pc=tram.stack[tram.fp]
|
||||
tram.pp=tram.stack[tram.fp+1]
|
||||
tram.fp=tram.stack[tram.fp+2]
|
||||
del tram.stack[tram.top:]
|
||||
tram.top=tram.top-1
|
||||
tram.push(res)
|
||||
|
||||
def toString(self): return super().toString() + "RETURN"
|
||||
|
||||
220
Project-02-03/vistram/vistram.py
Normal file
220
Project-02-03/vistram/vistram.py
Normal file
@@ -0,0 +1,220 @@
|
||||
# (c) Stephan Diehl, University of Trier, Germany, 2025
|
||||
|
||||
import tkinter as tk
|
||||
from copy import deepcopy
|
||||
from tkinter import ttk
|
||||
from tkinter import filedialog
|
||||
import os
|
||||
|
||||
from .assembler import *
|
||||
from .tram import *
|
||||
|
||||
import inspect
|
||||
import re
|
||||
|
||||
"""
|
||||
test_prog1=[const(1), const(2), const(5), store(1, 0), load(1, 0), add(), halt()]
|
||||
|
||||
L4=Label(4)
|
||||
L7=Label(7)
|
||||
L15=Label(15)
|
||||
test_prog2=[const(4),
|
||||
const(10),
|
||||
invoke(2,L4,0),
|
||||
halt(),
|
||||
load(0,0,assigned_label=L4),
|
||||
invoke(1,L7,0),
|
||||
ireturn(),
|
||||
load(0,0,assigned_label=L7),
|
||||
load(0,0),
|
||||
mul(),
|
||||
load(1,1),
|
||||
gt(),
|
||||
ifzero(L15),
|
||||
load(0,0),
|
||||
ireturn(),
|
||||
load(0,0,assigned_label=L15),
|
||||
load(0,0),
|
||||
mul(),
|
||||
ireturn()]
|
||||
"""
|
||||
|
||||
class AbstractMachine(TRAM):
|
||||
def __init__(self):
|
||||
self.initial_program = [] #Assembler.read_tram_code("examples/tramprograms/test.tram")
|
||||
#self.initial_program = test_prog2
|
||||
print(self.initial_program)
|
||||
self.reset()
|
||||
|
||||
def reset(self):
|
||||
super().__init__(self.initial_program)
|
||||
self.program_text = [f"{instr.toString()}" for i, instr in enumerate(self.program)]
|
||||
self.running = False
|
||||
|
||||
def step(self):
|
||||
if self.pc >= len(self.program):
|
||||
self.running = False
|
||||
return ("Error: End of program store reached!", None)
|
||||
|
||||
if self.pc == -1:
|
||||
self.running = False
|
||||
return ("The program terminated normally!", None)
|
||||
|
||||
instr=self.program[self.pc]
|
||||
self.print_instruction(instr)
|
||||
instr.execute(self)
|
||||
|
||||
return (f"Ausgeführt: {instr.toString()}", instr)
|
||||
|
||||
class MachineUI:
|
||||
def __init__(self, root):
|
||||
self.root = root
|
||||
self.machine = AbstractMachine()
|
||||
self.previous_state = None
|
||||
self.minimal_vis = False
|
||||
self.current_instruction = None
|
||||
|
||||
root.title("Visual TRAM")
|
||||
|
||||
# Frames
|
||||
control_frame = ttk.Frame(root)
|
||||
control_frame.pack(pady=10)
|
||||
|
||||
display_frame = ttk.Frame(root)
|
||||
display_frame.pack(padx=10, pady=5, fill="both", expand=True)
|
||||
|
||||
# Buttons
|
||||
ttk.Button(control_frame, text="Start", command=self.start).grid(row=0, column=0, padx=5)
|
||||
ttk.Button(control_frame, text="Stop", command=self.stop).grid(row=0, column=1, padx=5)
|
||||
ttk.Button(control_frame, text="Step", command=self.step).grid(row=0, column=2, padx=5)
|
||||
ttk.Button(control_frame, text="Reset", command=self.reset).grid(row=0, column=3, padx=5)
|
||||
button = ttk.Button(control_frame, text="Open File", command=self.open_file).grid(row=0, column=4, padx=5)
|
||||
|
||||
# Add a label to show the selected file
|
||||
self.label = ttk.Label(root, text="No file selected.", wraplength=800, justify="center")
|
||||
self.label.pack(pady=20)
|
||||
|
||||
# Add a button to open the file browser
|
||||
#button = ttk.Button(root, text="Open File", command=self.open_file)
|
||||
#button.pack(pady=10)
|
||||
|
||||
# Textfelder
|
||||
self.code_text = tk.Text(display_frame, width=35, height=32, wrap="none", bg="#f8f8f8")
|
||||
self.code_text.pack(side="left", padx=10, pady=10, fill="both", expand=True)
|
||||
self.code_text.tag_configure('highlight', background='#AAFFAA')
|
||||
|
||||
self.prev_state_text = tk.Text(display_frame, width=35, height=32, wrap="none", bg="#f0f8ff")
|
||||
self.prev_state_text.pack(side="left", padx=10, pady=10, fill="both", expand=True)
|
||||
|
||||
self.state_text = tk.Text(display_frame, width=35, height=32, wrap="none", bg="#f0f8ff")
|
||||
self.state_text.pack(side="left", padx=10, pady=10, fill="both", expand=True)
|
||||
|
||||
self.instr_text = tk.Text(display_frame, width=70, height=32, wrap="none", bg="#f0f8ff")
|
||||
self.instr_text.pack(side="right", padx=10, pady=10, fill="both", expand=True)
|
||||
|
||||
self.state_text.tag_configure('blue', background='#AAAAFF')
|
||||
self.prev_state_text.tag_configure('blue', background='#AAAAFF')
|
||||
|
||||
self.update_display()
|
||||
|
||||
def update_display(self):
|
||||
# Programmkode anzeigen
|
||||
self.code_text.delete("1.0", tk.END)
|
||||
for i, line in enumerate(self.machine.program_text):
|
||||
if i == self.machine.pc:
|
||||
prefix = "-> "
|
||||
self.code_text.insert(tk.END, f"{prefix}{i:02d}: {line}\n",'highlight')
|
||||
else:
|
||||
self.code_text.insert(tk.END, f" {i:02d}: {line}\n")
|
||||
|
||||
# Maschinenzustand anzeigen
|
||||
self.update_state_display(self.machine, self.state_text)
|
||||
if self.previous_state!=None:
|
||||
self.update_state_display(self.previous_state, self.prev_state_text)
|
||||
else:
|
||||
self.prev_state_text.delete("1.0", tk.END)
|
||||
self.previous_state = deepcopy(self.machine)
|
||||
|
||||
# Aktuell ausgeführte Instruktion anzeigen
|
||||
if self.current_instruction != None:
|
||||
(msg,instr) = self.current_instruction
|
||||
self.instr_text.delete("1.0", tk.END)
|
||||
self.instr_text.insert(tk.END, f"\n{msg}\n\n")
|
||||
if instr!=None:
|
||||
source_text= inspect.getsource(instr.execute)
|
||||
source_text=source_text.replace("tram.","")
|
||||
rest = source_text.split('\n', 1)[1] if '\n' in source_text else source_text
|
||||
source_text = '\n'.join(line.lstrip() for line in rest.splitlines())
|
||||
self.instr_text.insert(tk.END, source_text)
|
||||
else:
|
||||
self.instr_text.delete("1.0", tk.END)
|
||||
|
||||
|
||||
def update_state_display(self, machine, tk_text):
|
||||
tk_text.delete("1.0", tk.END)
|
||||
tk_text.insert(tk.END, f"PC : {machine.pc}\n")
|
||||
tk_text.insert(tk.END, f"FP : {machine.fp}\n")
|
||||
tk_text.insert(tk.END, f"PP : {machine.pp}\n")
|
||||
tk_text.insert(tk.END, f"TOP: {machine.top}\n\n")
|
||||
if (self.minimal_vis == True):
|
||||
tk_text.insert(tk.END, f"Stack: {machine.stack}\n")
|
||||
else:
|
||||
for i, value in enumerate(machine.stack):
|
||||
suffix = " "
|
||||
if i == machine.top: suffix += "<-TOP "
|
||||
if i == machine.pp: suffix += "<-PP "
|
||||
if i == machine.fp: suffix += "<-FP "
|
||||
if i == machine.fp + 4: suffix += "<-end of frame "
|
||||
if i >= machine.pp and i < machine.fp + 5:
|
||||
tk_text.insert(tk.END, f"{i:02d}: {value}{suffix}\n", 'blue')
|
||||
else:
|
||||
tk_text.insert(tk.END, f"{i:02d}: {value}{suffix}\n")
|
||||
def start(self):
|
||||
self.machine.running = True
|
||||
self.run_next()
|
||||
|
||||
def stop(self):
|
||||
self.machine.running = False
|
||||
|
||||
def step(self):
|
||||
self.current_instruction = self.machine.step()
|
||||
self.update_display()
|
||||
|
||||
|
||||
def run_next(self):
|
||||
if self.machine.running:
|
||||
self.step()
|
||||
#msg = self.machine.step()
|
||||
#self.update_display()
|
||||
#if msg:
|
||||
# self.state_text.insert(tk.END, f"\n{msg}\n")
|
||||
if self.machine.running:
|
||||
self.root.after(500, self.run_next) # Automatische Ausführung
|
||||
|
||||
def reset(self):
|
||||
self.machine.reset()
|
||||
self.previous_state=None
|
||||
self.current_instruction=None
|
||||
self.update_display()
|
||||
|
||||
def open_file(self):
|
||||
# Define the starting directory
|
||||
start_dir = os.path.expanduser("examples/tramprograms")
|
||||
|
||||
# Open the file browser starting in start_dir
|
||||
filename = filedialog.askopenfilename(
|
||||
initialdir=start_dir,
|
||||
title="Select a File",
|
||||
filetypes=(("TRAM files", "*.tram"), ("All files", "*.*"))
|
||||
)
|
||||
|
||||
# Display the selected file path in the label
|
||||
if filename:
|
||||
self.label.config(text=f"Selected file:\n{filename}")
|
||||
self.machine.initial_program = Assembler.read_tram_code(filename) #examples/tramprograms/test.tram")
|
||||
self.reset()
|
||||
|
||||
if __name__ == "__main__":
|
||||
root = tk.Tk()
|
||||
app = MachineUI(root)
|
||||
root.mainloop()
|
||||
Reference in New Issue
Block a user