Interpreter
This commit is contained in:
commit
1d0858c545
57
GUI/TKinter/canvasManager.py
Normal file
57
GUI/TKinter/canvasManager.py
Normal file
@ -0,0 +1,57 @@
|
||||
import interpreter.imageWrapper as imageWrapper
|
||||
|
||||
|
||||
class canvasManager():
|
||||
def __init__(self, canvas, image, programState, scaleSize):
|
||||
self.canvas = canvas
|
||||
self.image = image
|
||||
self.programState = programState
|
||||
self.scaleSize = scaleSize
|
||||
|
||||
def updateImage(self, newImage):
|
||||
self.image = newImage
|
||||
|
||||
def updateScaleSize(self, scaleSize):
|
||||
self.scaleSize = scaleSize
|
||||
|
||||
def updateProgramState(self, newProgramState):
|
||||
self.programState = newProgramState
|
||||
|
||||
def pixelToHexString(self, pixel) -> str:
|
||||
return '#%02x%02x%02x' %(pixel[0], pixel[1], pixel[2])
|
||||
|
||||
def updateCanvas(self):
|
||||
if self.image is None or self.canvas is None or self.programState is None or self.scaleSize is None:
|
||||
return False
|
||||
self.drawImage()
|
||||
self.highlightCodel()
|
||||
# Draw breakpoint
|
||||
return True
|
||||
|
||||
def drawImage(self):
|
||||
self.clearCanvas()
|
||||
for raw_y, row in enumerate(self.image):
|
||||
for raw_x, pixel in enumerate(row):
|
||||
x = raw_x * self.scaleSize
|
||||
y = raw_y * self.scaleSize
|
||||
color = self.pixelToHexString(pixel)
|
||||
self.canvas.create_rectangle(x,y, x+self.scaleSize, y+self.scaleSize, fill=color, outline=color)
|
||||
|
||||
|
||||
def clearCanvas(self):
|
||||
width = self.canvas.winfo_width()
|
||||
height = self.canvas.winfo_height()
|
||||
self.canvas.create_rectangle(0,0, width, height, fill="#FFFFFF")
|
||||
|
||||
|
||||
def highlightCodel(self):
|
||||
codel = imageWrapper.getCodel(self.image, self.programState.position)
|
||||
pixel = imageWrapper.getPixel(self.image, self.programState.position)
|
||||
color = self.pixelToHexString(pixel)
|
||||
self.colorCodel(codel, color, "#000000")
|
||||
|
||||
def colorCodel(self, codel, fill, outline):
|
||||
for position in codel:
|
||||
x = position[0] * self.scaleSize
|
||||
y = position[1] * self.scaleSize
|
||||
self.canvas.create_rectangle(x,y, x+self.scaleSize - 1, y+self.scaleSize - 1, fill=fill, outline=outline)
|
79
GUI/TKinter/infoManager.py
Normal file
79
GUI/TKinter/infoManager.py
Normal file
@ -0,0 +1,79 @@
|
||||
import interpreter.imageWrapper as imageWrapper
|
||||
import interpreter.colors as colors
|
||||
import interpreter.lexerTokens as lexerTokens
|
||||
import interpreter.movement as movement
|
||||
|
||||
class infoManager():
|
||||
def __init__(self, builder, generalInfoFrame, programStateInfoFrame):
|
||||
self.builder = builder
|
||||
self.generalInfo = generalInfoFrame
|
||||
self.programStateInfoFrame = programStateInfoFrame
|
||||
|
||||
def updateInfo(self, image, graph, programState):
|
||||
self.updateGeneralinfo(image, graph, programState)
|
||||
self.updateProgramStateInfo(programState)
|
||||
|
||||
def updateGeneralinfo(self, image, graph, programState):
|
||||
self.updateCodelInfo(image, programState.position)
|
||||
self.updateEdgesInfo(image, graph, programState)
|
||||
|
||||
def updateProgramStateInfo(self, programState):
|
||||
self.updateStackInfo(programState.dataStack)
|
||||
self.updatePointersInfo(programState.position, programState.pointers)
|
||||
|
||||
def updateCodelInfo(self, image, newPosition):
|
||||
infoMessage = self.builder.get_object('positionInfoMessage', self.generalInfo)
|
||||
if colors.isBlack(imageWrapper.getPixel(image, newPosition)):
|
||||
infoMessage.configure(text="Black pixels are no codel, and have no edges")
|
||||
return None
|
||||
|
||||
baseString = "Selected codel contains:\n"
|
||||
codel = imageWrapper.getCodel(image, newPosition)
|
||||
for position in codel:
|
||||
baseString += "{}\n".format(position)
|
||||
|
||||
infoMessage.configure(text=baseString.strip('\n'))
|
||||
|
||||
|
||||
def updateEdgesInfo(self, image, graph, programState):
|
||||
edgesInfo = self.builder.get_object('codelEdgesMessage', self.generalInfo)
|
||||
|
||||
if colors.isBlack(imageWrapper.getPixel(image, programState.position)):
|
||||
edgesInfo.configure(text = "Black pixels are no codel, and have no edges")
|
||||
return None
|
||||
|
||||
codel = imageWrapper.getCodel(image, programState.position)
|
||||
baseString = "Next step will be:\n"
|
||||
edge = graph[hash(frozenset(codel))][hash(programState.pointers)]
|
||||
baseString += self.getEdgeDescription(edge, programState.pointers)
|
||||
|
||||
baseString += "\nCodel edges are as follows:\n"
|
||||
#Generate pointers
|
||||
edgePointers = list(map(lambda i: (i%4, int(i/4)), iter(range(8))))
|
||||
for edgePointer in edgePointers:
|
||||
edge = graph[hash(frozenset(codel))][hash(edgePointer)]
|
||||
baseString += self.getEdgeDescription(edge, edgePointer)
|
||||
edgesInfo.configure(text = baseString)
|
||||
|
||||
def getEdgeDescription(self, edge, pointer):
|
||||
if isinstance(edge[0], lexerTokens.toColorToken) and edge[0].type == "push":
|
||||
return "{}/{},{} -> {}({})\n".format(edge[1], movement.getDP(pointer[0]), movement.getCC(pointer[1]), edge[0].type, edge[0].codelSize)
|
||||
else:
|
||||
return "{}/{},{} -> {}\n".format(edge[1], movement.getDP(pointer[0]), movement.getCC(pointer[1]), edge[0].type)
|
||||
|
||||
def updateStackInfo(self, stack):
|
||||
baseString = ""
|
||||
for item in reversed(stack):
|
||||
baseString += "{}\n".format(item)
|
||||
baseString.strip("\n")
|
||||
|
||||
stackInfoMessage = self.builder.get_object("stackContents", self.programStateInfoFrame)
|
||||
stackInfoMessage.configure(text=baseString)
|
||||
|
||||
def updatePointersInfo(self, position, pointers):
|
||||
print("Update pointers: {} -> Arrow: {}".format(pointers, movement.getArrow(pointers)))
|
||||
baseString = "Pos: ({},{})\n".format(position[0], position[1])
|
||||
baseString += u"DP: {} ({},{})".format(movement.getArrow(pointers), movement.getDP(pointers[0]), movement.getCC(pointers[1]))
|
||||
|
||||
pointersInfoMessage = self.builder.get_object("pointerMessage", self.programStateInfoFrame)
|
||||
pointersInfoMessage.configure(text=baseString)
|
156
GUI/TKinter/main.py
Normal file
156
GUI/TKinter/main.py
Normal file
@ -0,0 +1,156 @@
|
||||
# helloworld.py
|
||||
from time import sleep
|
||||
import threading
|
||||
import tkinter as tk
|
||||
import pygubu
|
||||
|
||||
import interpreter.imageWrapper as imageWrapper
|
||||
import interpreter.lexer as lexer
|
||||
import interpreter.lexerTokens as lexerTokens
|
||||
import interpreter.colors as colors
|
||||
import interpreter.movement as movement
|
||||
import interpreter.programState as programState
|
||||
import interpreter.main as main
|
||||
import threading
|
||||
|
||||
import infoManager
|
||||
import canvasManager
|
||||
|
||||
|
||||
class GUI:
|
||||
def __init__(self):
|
||||
# In pixelWidth/height per pixel. scaleSize = 25 means that every pixel will show as a 25x25 square
|
||||
self.scaleSize = 25
|
||||
# In percentage
|
||||
self.executionSpeed = 15
|
||||
|
||||
# In seconds
|
||||
self.maxWait = 5
|
||||
|
||||
self.image = None
|
||||
self.graph = None
|
||||
self.programState = None
|
||||
self.selectedPosition = None
|
||||
|
||||
self.optionBar = None
|
||||
self.actionBar = None
|
||||
self.content = None
|
||||
self.canvas = None
|
||||
|
||||
#1: Create a builder
|
||||
self.builder = builder = pygubu.Builder()
|
||||
|
||||
#2: Load an ui file
|
||||
builder.add_from_file('../assets/tkinterLayout.ui')
|
||||
|
||||
#3: Create the mainwindow
|
||||
self.mainwindow = builder.get_object('rootWindow')
|
||||
|
||||
self.initializeFrames()
|
||||
self.initializeCallbacks()
|
||||
self.infoManager = infoManager.infoManager(self.builder, self.generalInfoFrame, self.programStateInfoFrame)
|
||||
self.canvasManager = canvasManager.canvasManager(self.canvas, self.image, self.programState, self.scaleSize)
|
||||
|
||||
|
||||
def run(self):
|
||||
self.mainwindow.mainloop()
|
||||
|
||||
|
||||
def initializeCallbacks(self):
|
||||
self.builder.connect_callbacks({
|
||||
'loadFile': self.loadFile,
|
||||
'setScale': self.setScale,
|
||||
'takeStep': self.takeStep,
|
||||
'setExecutionSpeed': self.setExecutionSpeed,
|
||||
'setBreakpoint': self.setBreakpoint,
|
||||
'runProgram': self.runProgram
|
||||
})
|
||||
|
||||
self.canvas.bind("<Button-1>", self.canvasPressed)
|
||||
|
||||
def initializeFrames(self):
|
||||
self.optionBar = self.builder.get_object('optionBar', self.mainwindow)
|
||||
self.content = self.builder.get_object('content', self.mainwindow)
|
||||
self.actionBar = self.builder.get_object('actionBar', self.mainwindow)
|
||||
self.generalInfoFrame = self.builder.get_object("generalInfoFrame", self.content)
|
||||
self.programStateInfoFrame = self.builder.get_object("programStateInfoFrame", self.content)
|
||||
canvasFrame = self.builder.get_object('canvasFrame', self.content)
|
||||
self.canvas = self.builder.get_object('canvas', canvasFrame)
|
||||
|
||||
|
||||
def update(self):
|
||||
self.infoManager.updateInfo(self.image, self.graph, self.programState)
|
||||
self.canvasManager.updateScaleSize(self.scaleSize)
|
||||
self.canvasManager.updateImage(self.image)
|
||||
self.canvasManager.updateProgramState(self.programState)
|
||||
self.canvasManager.updateCanvas()
|
||||
|
||||
|
||||
def takeStep(self):
|
||||
if self.image is None or self.programState is None or self.graph is None:
|
||||
return None
|
||||
|
||||
newProgramState = main.takeStep(self.image, self.programState)
|
||||
if isinstance(newProgramState, bool):
|
||||
return False
|
||||
|
||||
self.programState = newProgramState
|
||||
self.selectedPosition = self.programState.position
|
||||
self.update()
|
||||
print("Take step!")
|
||||
return True
|
||||
|
||||
|
||||
def setBreakpoint(self):
|
||||
print("BREAKPOINT")
|
||||
|
||||
|
||||
def setExecutionSpeed(self, pos):
|
||||
if 0 < float(pos) < 100:
|
||||
self.executionSpeed = float(pos)
|
||||
|
||||
def getWaitTime(self):
|
||||
return self.executionSpeed/100*self.maxWait
|
||||
|
||||
def runProgram(self):
|
||||
if self.graph is None or self.image is None:
|
||||
return None
|
||||
|
||||
step = self.takeStep()
|
||||
if step:
|
||||
timer = threading.Timer(self.getWaitTime(), self.runProgram)
|
||||
timer.start()
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def setScale(self):
|
||||
scaleValue = int(self.builder.get_object('scaleEntry', self.optionBar).get())
|
||||
if 0 < scaleValue < 100:
|
||||
self.scaleSize = int(scaleValue)
|
||||
self.update()
|
||||
print("SCALE")
|
||||
|
||||
|
||||
def loadFile(self):
|
||||
fileName = self.builder.get_object('fileNameEntry', self.optionBar).get()
|
||||
self.image = imageWrapper.getImage(fileName)
|
||||
self.graph = lexer.graphImage(self.image)
|
||||
self.programState = programState.programState(self.graph, (0,0), (0,0))
|
||||
|
||||
self.update()
|
||||
print("LOAD FILE!")
|
||||
|
||||
|
||||
def canvasPressed(self, event):
|
||||
unscaled_x = int(event.x / self.scaleSize)
|
||||
unscaled_y = int(event.y / self.scaleSize)
|
||||
|
||||
self.selectedPosition = (unscaled_x, unscaled_y)
|
||||
self.update()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = GUI()
|
||||
app.run()
|
0
GUI/__init__.py
Normal file
0
GUI/__init__.py
Normal file
337
GUI/assets/tkinterLayout - kopie.ui
Normal file
337
GUI/assets/tkinterLayout - kopie.ui
Normal file
@ -0,0 +1,337 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<interface>
|
||||
<object id="rootWindow" class="ttk.Frame">
|
||||
<property name="height">200</property>
|
||||
<property name="width">200</property>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">0</property>
|
||||
</layout>
|
||||
<child>
|
||||
<object id="optionBar" class="ttk.Frame">
|
||||
<property name="borderwidth">2</property>
|
||||
<property name="height">200</property>
|
||||
<property name="width">200</property>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">0</property>
|
||||
<rows>
|
||||
<row id="0">
|
||||
<property name="weight">0</property>
|
||||
</row>
|
||||
</rows>
|
||||
</layout>
|
||||
<child>
|
||||
<object id="loadFileButton" class="ttk.Button">
|
||||
<property name="command">loadFile</property>
|
||||
<property name="text" translatable="yes">Open File</property>
|
||||
<property name="width">7.5</property>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
<property name="ipadx">5</property>
|
||||
<property name="ipady">2</property>
|
||||
<property name="padx">10</property>
|
||||
<property name="pady">2</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">0</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object id="fileNameEntry" class="ttk.Entry">
|
||||
<property name="exportselection">false</property>
|
||||
<property name="text" translatable="yes">../../Add.png</property>
|
||||
<property name="width">44</property>
|
||||
<layout>
|
||||
<property name="column">1</property>
|
||||
<property name="ipady">2</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">0</property>
|
||||
<property name="sticky">w</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object id="setScale" class="ttk.Button">
|
||||
<property name="command">setScale</property>
|
||||
<property name="text" translatable="yes">Set scale</property>
|
||||
<property name="textvariable">int:scaleSize</property>
|
||||
<layout>
|
||||
<property name="column">2</property>
|
||||
<property name="ipadx">5</property>
|
||||
<property name="ipady">2</property>
|
||||
<property name="padx">10</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">0</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object id="scaleEntry" class="ttk.Entry">
|
||||
<property name="text" translatable="yes">25</property>
|
||||
<layout>
|
||||
<property name="column">3</property>
|
||||
<property name="ipady">2</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">0</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object id="actionBar" class="ttk.Frame">
|
||||
<property name="height">200</property>
|
||||
<property name="width">200</property>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
<property name="padx">7</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">1</property>
|
||||
<rows>
|
||||
<row id="0">
|
||||
<property name="pad">5</property>
|
||||
</row>
|
||||
</rows>
|
||||
<columns>
|
||||
<column id="0">
|
||||
<property name="pad">15</property>
|
||||
</column>
|
||||
<column id="1">
|
||||
<property name="pad">10</property>
|
||||
</column>
|
||||
<column id="2">
|
||||
<property name="pad">15</property>
|
||||
</column>
|
||||
</columns>
|
||||
</layout>
|
||||
<child>
|
||||
<object id="runProgram" class="ttk.Button">
|
||||
<property name="command">runProgram</property>
|
||||
<property name="text" translatable="yes">Run program</property>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">0</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object id="executionSpeed" class="ttk.Scale">
|
||||
<property name="command">setExecutionSpeed</property>
|
||||
<property name="from_">0</property>
|
||||
<property name="orient">horizontal</property>
|
||||
<property name="to">100</property>
|
||||
<property name="value">15</property>
|
||||
<property name="variable">int:pos</property>
|
||||
<layout>
|
||||
<property name="column">1</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">0</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object id="takeStep" class="ttk.Button">
|
||||
<property name="command">takeStep</property>
|
||||
<property name="text" translatable="yes">takeStep</property>
|
||||
<layout>
|
||||
<property name="column">2</property>
|
||||
<property name="padx">15</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">0</property>
|
||||
<property name="sticky">e</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object id="content" class="ttk.Frame">
|
||||
<property name="height">200</property>
|
||||
<property name="width">200</property>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">2</property>
|
||||
<rows>
|
||||
<row id="0">
|
||||
<property name="minsize">0</property>
|
||||
</row>
|
||||
<row id="1">
|
||||
<property name="minsize">0</property>
|
||||
</row>
|
||||
</rows>
|
||||
</layout>
|
||||
<child>
|
||||
<object id="generalInfoFrame" class="ttk.Frame">
|
||||
<property name="height">200</property>
|
||||
<property name="width">200</property>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">0</property>
|
||||
<property name="sticky">n</property>
|
||||
<rows>
|
||||
<row id="0">
|
||||
<property name="pad">5</property>
|
||||
</row>
|
||||
<row id="1">
|
||||
<property name="minsize">0</property>
|
||||
</row>
|
||||
<row id="2">
|
||||
<property name="pad">0</property>
|
||||
</row>
|
||||
<row id="3">
|
||||
<property name="pad">15</property>
|
||||
</row>
|
||||
</rows>
|
||||
</layout>
|
||||
<child>
|
||||
<object id="codelInfoLabel" class="ttk.Label">
|
||||
<property name="text" translatable="yes">Codel info:</property>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">0</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object id="positionInfoMessage" class="tk.Message">
|
||||
<property name="background">#ffffff</property>
|
||||
<property name="highlightcolor">#a7a7a7</property>
|
||||
<property name="highlightthickness">1</property>
|
||||
<property name="text" translatable="yes">Selected codel contains the following positions:
|
||||
- (0,0)
|
||||
- (0,1)</property>
|
||||
<property name="width">150</property>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
<property name="padx">5</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">1</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object id="codelEdgesMessage" class="tk.Message">
|
||||
<property name="background">#FFFFFF</property>
|
||||
<property name="highlightcolor">#a7a7a7</property>
|
||||
<property name="highlightthickness">1</property>
|
||||
<property name="text" translatable="yes">Codel edges are as follows:
|
||||
|
||||
(0,1) -> (1,0) push 0</property>
|
||||
<property name="width">150</property>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">2</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object id="setBreakPoint" class="ttk.Button">
|
||||
<property name="command">setBreakpoint</property>
|
||||
<property name="text" translatable="yes">Set breakpoint</property>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">3</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object id="stackInfoFrame" class="ttk.Frame">
|
||||
<property name="height">200</property>
|
||||
<property name="width">200</property>
|
||||
<layout>
|
||||
<property name="column">1</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">0</property>
|
||||
<property name="sticky">n</property>
|
||||
</layout>
|
||||
<child>
|
||||
<object id="stackInfo" class="ttk.Label">
|
||||
<property name="anchor">center</property>
|
||||
<property name="text" translatable="yes">Stack</property>
|
||||
<property name="width">15</property>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">0</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object id="Message_4" class="tk.Message">
|
||||
<property name="background">#ffffff</property>
|
||||
<property name="highlightcolor">#a7a7a7</property>
|
||||
<property name="highlightthickness">1</property>
|
||||
<property name="text" translatable="yes">2
|
||||
15
|
||||
4
|
||||
0</property>
|
||||
<property name="width">150</property>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">1</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object id="canvasFrame" class="ttk.Frame">
|
||||
<property name="height">200</property>
|
||||
<property name="width">200</property>
|
||||
<layout>
|
||||
<property name="column">2</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">0</property>
|
||||
</layout>
|
||||
<child>
|
||||
<object id="canvas" class="tk.Canvas">
|
||||
<property name="background">#ffffff</property>
|
||||
<property name="height">400</property>
|
||||
<property name="highlightbackground">#8a8a8a</property>
|
||||
<property name="highlightthickness">1</property>
|
||||
<property name="width">700</property>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">0</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object id="Scrollbar_1" class="ttk.Scrollbar">
|
||||
<property name="orient">vertical</property>
|
||||
<layout>
|
||||
<property name="column">1</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">0</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object id="Scrollbar_2" class="ttk.Scrollbar">
|
||||
<property name="orient">horizontal</property>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">1</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
371
GUI/assets/tkinterLayout.ui
Normal file
371
GUI/assets/tkinterLayout.ui
Normal file
@ -0,0 +1,371 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<interface>
|
||||
<object id="rootWindow" class="ttk.Frame">
|
||||
<property name="height">200</property>
|
||||
<property name="width">200</property>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">0</property>
|
||||
</layout>
|
||||
<child>
|
||||
<object id="optionBar" class="ttk.Frame">
|
||||
<property name="borderwidth">2</property>
|
||||
<property name="height">200</property>
|
||||
<property name="width">200</property>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">0</property>
|
||||
<rows>
|
||||
<row id="0">
|
||||
<property name="weight">0</property>
|
||||
</row>
|
||||
</rows>
|
||||
</layout>
|
||||
<child>
|
||||
<object id="loadFileButton" class="ttk.Button">
|
||||
<property name="command">loadFile</property>
|
||||
<property name="text" translatable="yes">Open File</property>
|
||||
<property name="width">7.5</property>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
<property name="ipadx">5</property>
|
||||
<property name="ipady">2</property>
|
||||
<property name="padx">10</property>
|
||||
<property name="pady">2</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">0</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object id="fileNameEntry" class="ttk.Entry">
|
||||
<property name="exportselection">false</property>
|
||||
<property name="text" translatable="yes">../../Add.png</property>
|
||||
<property name="width">44</property>
|
||||
<layout>
|
||||
<property name="column">1</property>
|
||||
<property name="ipady">2</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">0</property>
|
||||
<property name="sticky">w</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object id="setScale" class="ttk.Button">
|
||||
<property name="command">setScale</property>
|
||||
<property name="text" translatable="yes">Set scale</property>
|
||||
<property name="textvariable">int:scaleSize</property>
|
||||
<layout>
|
||||
<property name="column">2</property>
|
||||
<property name="ipadx">5</property>
|
||||
<property name="ipady">2</property>
|
||||
<property name="padx">10</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">0</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object id="scaleEntry" class="ttk.Entry">
|
||||
<property name="text" translatable="yes">75</property>
|
||||
<layout>
|
||||
<property name="column">3</property>
|
||||
<property name="ipady">2</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">0</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object id="actionBar" class="ttk.Frame">
|
||||
<property name="height">200</property>
|
||||
<property name="width">200</property>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
<property name="padx">7</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">1</property>
|
||||
<rows>
|
||||
<row id="0">
|
||||
<property name="pad">5</property>
|
||||
</row>
|
||||
</rows>
|
||||
<columns>
|
||||
<column id="0">
|
||||
<property name="pad">15</property>
|
||||
</column>
|
||||
<column id="1">
|
||||
<property name="pad">10</property>
|
||||
</column>
|
||||
<column id="2">
|
||||
<property name="pad">15</property>
|
||||
</column>
|
||||
</columns>
|
||||
</layout>
|
||||
<child>
|
||||
<object id="runProgram" class="ttk.Button">
|
||||
<property name="command">runProgram</property>
|
||||
<property name="text" translatable="yes">Run program</property>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">0</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object id="executionSpeed" class="ttk.Scale">
|
||||
<property name="command">setExecutionSpeed</property>
|
||||
<property name="from_">1</property>
|
||||
<property name="orient">horizontal</property>
|
||||
<property name="to">100</property>
|
||||
<property name="value">15</property>
|
||||
<property name="variable">int:pos</property>
|
||||
<layout>
|
||||
<property name="column">1</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">0</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object id="takeStep" class="ttk.Button">
|
||||
<property name="command">takeStep</property>
|
||||
<property name="text" translatable="yes">takeStep</property>
|
||||
<layout>
|
||||
<property name="column">2</property>
|
||||
<property name="padx">15</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">0</property>
|
||||
<property name="sticky">e</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object id="content" class="ttk.Frame">
|
||||
<property name="height">200</property>
|
||||
<property name="width">200</property>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">2</property>
|
||||
<rows>
|
||||
<row id="0">
|
||||
<property name="minsize">0</property>
|
||||
</row>
|
||||
<row id="1">
|
||||
<property name="minsize">0</property>
|
||||
</row>
|
||||
</rows>
|
||||
</layout>
|
||||
<child>
|
||||
<object id="generalInfoFrame" class="ttk.Frame">
|
||||
<property name="height">200</property>
|
||||
<property name="width">250</property>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
<property name="padx">5</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">0</property>
|
||||
<property name="sticky">n</property>
|
||||
<rows>
|
||||
<row id="0">
|
||||
<property name="pad">5</property>
|
||||
</row>
|
||||
<row id="1">
|
||||
<property name="minsize">0</property>
|
||||
</row>
|
||||
<row id="2">
|
||||
<property name="pad">0</property>
|
||||
</row>
|
||||
<row id="3">
|
||||
<property name="pad">15</property>
|
||||
</row>
|
||||
</rows>
|
||||
</layout>
|
||||
<child>
|
||||
<object id="codelInfoLabel" class="ttk.Label">
|
||||
<property name="text" translatable="yes">Codel info:</property>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">0</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object id="positionInfoMessage" class="tk.Message">
|
||||
<property name="background">#ffffff</property>
|
||||
<property name="highlightcolor">#a7a7a7</property>
|
||||
<property name="highlightthickness">1</property>
|
||||
<property name="text" translatable="yes">Selected codel contains the following positions:
|
||||
- (0,0)
|
||||
- (0,1)</property>
|
||||
<property name="width">200</property>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
<property name="padx">5</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">1</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object id="codelEdgesMessage" class="tk.Message">
|
||||
<property name="background">#FFFFFF</property>
|
||||
<property name="highlightcolor">#a7a7a7</property>
|
||||
<property name="highlightthickness">1</property>
|
||||
<property name="text" translatable="yes">Codel edges are as follows:
|
||||
|
||||
(0,1) -> (1,0) push 0</property>
|
||||
<property name="width">200</property>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">2</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object id="setBreakPoint" class="ttk.Button">
|
||||
<property name="command">setBreakpoint</property>
|
||||
<property name="text" translatable="yes">Set breakpoint</property>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">3</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object id="programStateInfoFrame" class="ttk.Frame">
|
||||
<property name="height">200</property>
|
||||
<property name="width">200</property>
|
||||
<layout>
|
||||
<property name="column">1</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">0</property>
|
||||
<property name="sticky">n</property>
|
||||
</layout>
|
||||
<child>
|
||||
<object id="stackInfo" class="ttk.Label">
|
||||
<property name="anchor">center</property>
|
||||
<property name="background">#ffffff</property>
|
||||
<property name="text" translatable="yes">Stack</property>
|
||||
<property name="width">15</property>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">1</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object id="stackContents" class="tk.Message">
|
||||
<property name="anchor">center</property>
|
||||
<property name="background">#ffffff</property>
|
||||
<property name="highlightcolor">#a7a7a7</property>
|
||||
<property name="highlightthickness">1</property>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">2</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object id="programStateLabel" class="ttk.Label">
|
||||
<property name="text" translatable="yes">Program state:</property>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
<property name="columnspan">2</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">0</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object id="currentPointer" class="ttk.Label">
|
||||
<property name="background">#ffffff</property>
|
||||
<property name="text" translatable="yes">Current direction</property>
|
||||
<layout>
|
||||
<property name="column">1</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">1</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object id="pointerMessage" class="tk.Message">
|
||||
<property name="background">#ffffff</property>
|
||||
<layout>
|
||||
<property name="column">1</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">2</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object id="canvasFrame" class="ttk.Frame">
|
||||
<property name="height">200</property>
|
||||
<property name="width">200</property>
|
||||
<layout>
|
||||
<property name="column">2</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">0</property>
|
||||
</layout>
|
||||
<child>
|
||||
<object id="canvas" class="tk.Canvas">
|
||||
<property name="background">#ffffff</property>
|
||||
<property name="height">400</property>
|
||||
<property name="highlightbackground">#8a8a8a</property>
|
||||
<property name="highlightthickness">1</property>
|
||||
<property name="width">700</property>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
<property name="ipadx">5</property>
|
||||
<property name="ipady">5</property>
|
||||
<property name="padx">5</property>
|
||||
<property name="pady">5</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">0</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object id="Scrollbar_1" class="ttk.Scrollbar">
|
||||
<property name="orient">vertical</property>
|
||||
<layout>
|
||||
<property name="column">1</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">0</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object id="Scrollbar_2" class="ttk.Scrollbar">
|
||||
<property name="orient">horizontal</property>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
<property name="propagate">True</property>
|
||||
<property name="row">1</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
68
GUI/kivy/Debugger.kv
Normal file
68
GUI/kivy/Debugger.kv
Normal file
@ -0,0 +1,68 @@
|
||||
<GeneralLayout>:
|
||||
rows : 3
|
||||
row_default_height: 25
|
||||
canvas:
|
||||
Color:
|
||||
rgb: [1, 1, 1, 1]
|
||||
Rectangle:
|
||||
pos:self.pos
|
||||
size:self.size
|
||||
OptionBar
|
||||
ToolBar
|
||||
ContentLayout
|
||||
|
||||
|
||||
<OptionBar>:
|
||||
spacing:5
|
||||
padding: (3,1)
|
||||
BoxLayout:
|
||||
size: root.width*0.70, root.height
|
||||
size_hint: None, None
|
||||
Button:
|
||||
id: myFileButton
|
||||
text: "Open file:"
|
||||
on_press: root.setFile(filePath.text)
|
||||
TextInput:
|
||||
id: filePath
|
||||
hint_text: "File path"
|
||||
cursor_color: (0,1,0,1)
|
||||
multiline: False
|
||||
|
||||
BoxLayout:
|
||||
Button:
|
||||
id: myOptionsButton
|
||||
text: "Set pixel scale"
|
||||
on_press: root.setScale(scaleSize.text)
|
||||
TextInput:
|
||||
id: scaleSize
|
||||
input_type: "number"
|
||||
hint_text: "10"
|
||||
|
||||
<ToolBar>:
|
||||
Button:
|
||||
id: myMiddleButton
|
||||
text: "Next step"
|
||||
|
||||
|
||||
<ContentLayout>:
|
||||
rows: 1
|
||||
cols: 3
|
||||
size_hint_y : 100
|
||||
Button:
|
||||
size: root.width*0.15, root.height
|
||||
size_hint: None, None
|
||||
id: myLeftBottomButton
|
||||
text: "Left Bottom"
|
||||
|
||||
ImageCanvas
|
||||
|
||||
|
||||
Button:
|
||||
size: root.width*0.15, root.height
|
||||
size_hint: None, None
|
||||
id: myRightBottmButton
|
||||
text: "Right Bottom"
|
||||
|
||||
|
||||
<ImageCanvas>:
|
||||
id: imageCanvas
|
74
GUI/kivy/kivyWindow.py
Normal file
74
GUI/kivy/kivyWindow.py
Normal file
@ -0,0 +1,74 @@
|
||||
from kivy.app import App
|
||||
from kivy.uix.button import Button
|
||||
from kivy.uix.gridlayout import GridLayout
|
||||
from kivy.uix.boxlayout import BoxLayout
|
||||
from kivy.graphics.vertex_instructions import Line
|
||||
from kivy.graphics.context_instructions import Color
|
||||
from kivy.properties import ObjectProperty
|
||||
|
||||
import interpreter.lexer as lexer
|
||||
import interpreter.imageWrapper as imageWrapper
|
||||
|
||||
|
||||
class GeneralLayout(GridLayout):
|
||||
pass
|
||||
|
||||
class ContentLayout(GridLayout):
|
||||
pass
|
||||
|
||||
class ImageCanvas(BoxLayout):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
image = None
|
||||
|
||||
|
||||
def drawImage(self, image, scale):
|
||||
app = App.get_running_app()
|
||||
self.image = app.loadImage(image)
|
||||
max_height = self.size
|
||||
print(max_height)
|
||||
with self.canvas:
|
||||
for y_axis, row in enumerate(image):
|
||||
for x_axis, pixel in enumerate(row):
|
||||
x = x_axis*scale
|
||||
y = y_axis*scale
|
||||
|
||||
# with self.:
|
||||
# Color([x/255 for x in pixel])
|
||||
# Line(points = [x_axis, x, y_axis, y])
|
||||
|
||||
|
||||
class OptionBar(BoxLayout):
|
||||
def setFile(self, value):
|
||||
app = App.get_running_app()
|
||||
app.loadImage(value)
|
||||
self.ids["filePath"].hint_text = value
|
||||
self.ids['filePath'].text = ""
|
||||
|
||||
def setScale(self, value):
|
||||
app = App.get_running_app()
|
||||
self.ids['scaleSize'].hint_text = value
|
||||
self.ids['scaleSize'].text = ""
|
||||
app.pixelScale = value
|
||||
|
||||
class ToolBar(BoxLayout):
|
||||
pass
|
||||
|
||||
|
||||
class DebuggerApp(App):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.pixelScale = 10
|
||||
self.canvas = ObjectProperty()
|
||||
|
||||
|
||||
def loadImage(self, filepath):
|
||||
image = imageWrapper.getImage(filepath)
|
||||
return image
|
||||
|
||||
def build(self):
|
||||
return GeneralLayout()
|
||||
|
||||
if __name__ == '__main__':
|
||||
GUI = DebuggerApp()
|
||||
GUI.run()
|
0
interpreter/__init__.py
Normal file
0
interpreter/__init__.py
Normal file
67
interpreter/colors.py
Normal file
67
interpreter/colors.py
Normal file
@ -0,0 +1,67 @@
|
||||
from typing import Dict
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
||||
class possiblePixels:
|
||||
def __init__(self):
|
||||
self.colors = [
|
||||
[255, 192, 192], # Light red
|
||||
[255, 0, 0], # Red
|
||||
[192, 0, 0], # Dark red
|
||||
[255, 255, 192], # Light yellow
|
||||
[255, 255, 0], # Yellow
|
||||
[192, 192, 0], # Dark yellow
|
||||
[192, 255, 192], # Light green
|
||||
[0, 255, 0], # Green
|
||||
[0, 192, 0], # Dark green
|
||||
[192, 255, 255], # Light cyan
|
||||
[0, 255, 255], # Cyan
|
||||
[0, 192, 192], # Dark cyan
|
||||
[192, 192, 255], # Light blue
|
||||
[0, 0, 255], # Blue
|
||||
[0, 0, 192], # Dark blue
|
||||
[255, 192, 255], # Light magenta
|
||||
[255, 0, 255], # Magenta
|
||||
[192, 0, 192] # Dark magenta
|
||||
]
|
||||
self.white = [255, 255, 255]
|
||||
self.black = [0, 0, 0]
|
||||
|
||||
|
||||
def getPixelChange(colorStart: np.ndarray, colorEnd: np.ndarray) -> Dict[str, int]:
|
||||
pixelsColors = possiblePixels()
|
||||
|
||||
|
||||
if isWhite(colorStart) or isWhite(colorEnd):
|
||||
return {"hueChange": 0, "lightChange": 0}
|
||||
|
||||
# Converting np arrays to common lists
|
||||
colorStart = list(colorStart)[:3]
|
||||
colorEnd = list(colorEnd)[:3]
|
||||
indexStart = pixelsColors.colors.index(colorStart)
|
||||
indexEnd = pixelsColors.colors.index(colorEnd)
|
||||
|
||||
# Calculating hue and lightness changes
|
||||
hueChange = (int(indexEnd / 3) - int(indexStart / 3)) % 6
|
||||
lightChange = (indexEnd - indexStart) % 3
|
||||
|
||||
return {"hueChange": hueChange, "lightChange": lightChange}
|
||||
|
||||
|
||||
def isWhite(testColor: np.ndarray) -> bool:
|
||||
colors = possiblePixels()
|
||||
testColor = list(testColor)[:3]
|
||||
return testColor == colors.white
|
||||
|
||||
|
||||
def isBlack(testColor: np.ndarray) -> bool:
|
||||
colors = possiblePixels()
|
||||
testColor = list(testColor)[:3]
|
||||
return testColor == colors.black
|
||||
|
||||
|
||||
def isColor(testColor: np.ndarray) -> bool:
|
||||
colors = possiblePixels()
|
||||
testColor = list(testColor)[:3]
|
||||
return testColor in colors.colors
|
126
interpreter/imageWrapper.py
Normal file
126
interpreter/imageWrapper.py
Normal file
@ -0,0 +1,126 @@
|
||||
from typing import Tuple, Union, Set, List
|
||||
|
||||
from PIL import Image
|
||||
import numpy as np
|
||||
|
||||
import interpreter.movement as movement
|
||||
import interpreter.colors as colors
|
||||
|
||||
|
||||
def boundsChecker(image: np.ndarray, position: Tuple[int, int]) -> bool:
|
||||
# Position 0 = x-axis, while matrix[0] = y-axis. This is why we compare position[0] with matrix[1]
|
||||
return 0 <= position[0] < image.shape[1] and \
|
||||
0 <= position[1] < image.shape[0]
|
||||
|
||||
|
||||
def getPixel(image: np.ndarray, position: Tuple[int, int]) -> Union[np.ndarray, bool]:
|
||||
"""
|
||||
This function the pixel at a specific location
|
||||
:param image: np.ndarray of image
|
||||
:param position: wanted position
|
||||
:return: either a cell or False, if the cell is not inside the image
|
||||
"""
|
||||
if boundsChecker(image, position):
|
||||
return image[position[1]][position[0]]
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def getImage(fileName: str) -> np.ndarray:
|
||||
"""
|
||||
Returns an np.ndarray of the image found at the given file location
|
||||
:param fileName: Complete filename (including extension)
|
||||
:return: np.ndarray of the image
|
||||
"""
|
||||
image = Image.open(fileName)
|
||||
if fileName.split('.')[-1] == "gif":
|
||||
image = image.convert("RGB")
|
||||
return np.array(image)
|
||||
|
||||
|
||||
def getCodel(image: np.ndarray, position: Tuple[int, int], foundPixels: Set[Tuple[int, int]] = None) -> Set[Tuple[int, int]]:
|
||||
"""
|
||||
This function finds all adjacent pixels with the same color as the pixel on the given position
|
||||
|
||||
If you pass a white pixel, this will return a set with only the white pixel in it.
|
||||
|
||||
:param image: The image with all pixel values
|
||||
:param position: Starting position
|
||||
:param foundPixels: currently found pixels
|
||||
:return: A Set with all positions of same-colored pixels (Also known as a codel)
|
||||
"""
|
||||
if foundPixels is None:
|
||||
foundPixels = set()
|
||||
|
||||
# If this position is already in the set, it has already been traversed
|
||||
if position in foundPixels:
|
||||
return foundPixels
|
||||
|
||||
if colors.isWhite(getPixel(image, position)):
|
||||
foundPixels.add(position)
|
||||
return foundPixels
|
||||
|
||||
x = position[0]
|
||||
y = position[1]
|
||||
|
||||
foundPixels.add(position)
|
||||
|
||||
# right
|
||||
if boundsChecker(image, (x + 1, y)) and np.all(image[y][x + 1] == image[y][x]):
|
||||
newPosition = (position[0] + 1, position[1])
|
||||
foundPixels = foundPixels.union(getCodel(image, newPosition, foundPixels))
|
||||
|
||||
# below
|
||||
if boundsChecker(image, (x, y - 1)) and np.all(image[y - 1][x] == image[y][x]):
|
||||
newPosition = (position[0], position[1] - 1)
|
||||
foundPixels = foundPixels.union(getCodel(image, newPosition, foundPixels))
|
||||
|
||||
# left
|
||||
if boundsChecker(image, (x - 1, y)) and np.all(image[y][x - 1] == image[y][x]):
|
||||
newPosition = (position[0] - 1, position[1])
|
||||
foundPixels = foundPixels.union(getCodel(image, newPosition, foundPixels))
|
||||
|
||||
# above
|
||||
if boundsChecker(image, (x, y + 1)) and np.all(image[y + 1][x] == image[y][x]):
|
||||
newPosition = (position[0], position[1] + 1)
|
||||
foundPixels = foundPixels.union(getCodel(image, newPosition, foundPixels))
|
||||
|
||||
return foundPixels
|
||||
|
||||
|
||||
def getWhiteLine(image: np.ndarray, startPosition: Tuple[int, int], directionPointer: int, foundPixels: List[Tuple[int, int]] = None) -> List[Tuple[int, int]]:
|
||||
"""
|
||||
Finds all adjacent white pixels in the same direction
|
||||
:param image: base image
|
||||
:param startPosition: Starting position from which the white line starts
|
||||
:param directionPointer: Direction in which the line goes
|
||||
:param foundPixels: already found pixels
|
||||
:return: A list of white pixels found
|
||||
"""
|
||||
|
||||
# Can't give mutable values as default parameter
|
||||
if foundPixels is None:
|
||||
foundPixels = []
|
||||
|
||||
# If it is already found, skip
|
||||
if startPosition in foundPixels:
|
||||
return foundPixels
|
||||
|
||||
foundPixels.append(startPosition)
|
||||
|
||||
# Get the new position, and check if the colors match
|
||||
newPos = movement.getNextPosition(startPosition, directionPointer)
|
||||
if boundsChecker(image, newPos) and colors.isWhite(image[newPos[1]][newPos[0]]):
|
||||
return getWhiteLine(image, newPos, directionPointer, foundPixels)
|
||||
else:
|
||||
return foundPixels
|
||||
|
||||
|
||||
def getNewWhiteDirection(image: np.ndarray, startPosition: Tuple[int, int], directionPointer: int) -> int:
|
||||
newPosition = movement.getNextPosition(startPosition, directionPointer)
|
||||
|
||||
if boundsChecker(image, newPosition) and (not colors.isBlack(getPixel(image, newPosition))):
|
||||
return directionPointer
|
||||
else:
|
||||
return getNewWhiteDirection(image, startPosition, movement.flipDP(directionPointer))
|
||||
|
138
interpreter/lexer.py
Normal file
138
interpreter/lexer.py
Normal file
@ -0,0 +1,138 @@
|
||||
from typing import List, Tuple, Set, Dict, Union
|
||||
|
||||
import numpy as np
|
||||
|
||||
import interpreter.colors as colors
|
||||
import interpreter.imageWrapper as imageWrapper
|
||||
import interpreter.lexerTokens as lexerTokens
|
||||
import interpreter.movement as movement
|
||||
|
||||
|
||||
def cyclePosition(image: np.ndarray, startPosition: Tuple[int, int]) -> Union[Tuple[int, int], bool]:
|
||||
"""
|
||||
:param image: numpy image array
|
||||
:param startPosition: from where to go to Tuple (x,y)
|
||||
:return: newPosition (x,y), or false if new position would fall out of bounds
|
||||
"""
|
||||
if not imageWrapper.boundsChecker(image, startPosition):
|
||||
return False
|
||||
|
||||
if startPosition[0] == image.shape[1] - 1:
|
||||
if startPosition[1] < image.shape[0] - 1:
|
||||
return (0, startPosition[1] + 1)
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
return (startPosition[0] + 1, startPosition[1])
|
||||
|
||||
|
||||
|
||||
def getCodelsEfficient(image: np.ndarray, positionList: List[Tuple[int, int]]) -> List[Set[Tuple[int, int]]]:
|
||||
if len(positionList) == 0:
|
||||
return []
|
||||
copiedList = positionList.copy()
|
||||
newPosition = copiedList.pop(0)
|
||||
|
||||
|
||||
if colors.isBlack(imageWrapper.getPixel(image, newPosition)):
|
||||
return getCodelsEfficient(image, copiedList)
|
||||
|
||||
newCodel = imageWrapper.getCodel(image, newPosition)
|
||||
|
||||
# print("Original positionList: {}".format(positionList))
|
||||
# print("Codel found: {}".format(newCodel))
|
||||
# Remove found positions from position list
|
||||
copiedList = list(set(copiedList) - newCodel)
|
||||
# print("New positionList: {}".format(copiedList))
|
||||
codelList = getCodelsEfficient(image, copiedList)
|
||||
|
||||
codelList.append(newCodel)
|
||||
return codelList
|
||||
|
||||
|
||||
|
||||
|
||||
def getAllCodels(image: np.ndarray, position: Tuple[int, int] = (0, 0),
|
||||
foundCodels: List[Set[Tuple[int, int]]] = None) -> List[Set[Tuple[int, int]]]:
|
||||
if foundCodels is None:
|
||||
foundCodels = []
|
||||
|
||||
# Checks if the current position is already in a found codel, and also if the current pixel is white or black
|
||||
if (True in map(lambda codelSet, lambdaPosition=position: lambdaPosition in codelSet, foundCodels)) or colors.isBlack(imageWrapper.getPixel(image, position)):
|
||||
nextPosition = cyclePosition(image, position)
|
||||
if type(nextPosition) == bool and not nextPosition:
|
||||
return foundCodels
|
||||
return getAllCodels(image, nextPosition, foundCodels)
|
||||
|
||||
newCodel = imageWrapper.getCodel(image, position)
|
||||
foundCodels.append(newCodel)
|
||||
|
||||
nextPosition = cyclePosition(image, position)
|
||||
if type(nextPosition) == bool and nextPosition is False:
|
||||
return foundCodels
|
||||
else:
|
||||
return getAllCodels(image, nextPosition, foundCodels)
|
||||
|
||||
|
||||
def edgesToCodeldict(image: np.ndarray, edges: List[Tuple[Tuple[int, int], Tuple[int, int]]]) -> Dict[int, Tuple[lexerTokens.baseLexerToken, Tuple[int, int]]]:
|
||||
"""
|
||||
Constructs a dictionary with each pointer possibility as key and (token, position) as value
|
||||
:param image: Image required to find calculate tokens
|
||||
:param edges: List[Tuple[position, pointers]]
|
||||
:return:
|
||||
"""
|
||||
return dict(map(lambda x, lambdaImage=image: (hash(x[1]), (lexerTokens.edgeToToken(lambdaImage, x), x[0])), edges))
|
||||
|
||||
|
||||
def isCodeldictTerminate(codelDict: Dict[int, Tuple[lexerTokens.baseLexerToken, Tuple[int, int]]]) -> bool:
|
||||
return all(map(lambda x: isinstance(x[1][0], lexerTokens.toBlackToken), codelDict.items()))
|
||||
|
||||
|
||||
def codelDictToTerminate(codelDict: Dict[int, Tuple[lexerTokens.baseLexerToken, Tuple[int, int]]]) -> Dict[int, Tuple[lexerTokens.terminateToken, Tuple[int, int]]]:
|
||||
return dict(map(lambda x: (x[0], (lexerTokens.terminateToken(), x[1][1])), codelDict.items()))
|
||||
|
||||
|
||||
def codelToCodelDict(image: np.ndarray, codel: Set[Tuple[int, int]], edgePointers: List[Tuple[int, int]]) -> Dict[int, Tuple[lexerTokens.baseLexerToken, Tuple[int, int]]]:
|
||||
"""
|
||||
:param image: image
|
||||
:param codel: set of positions within the same color
|
||||
:param edgePointers: list of pointers to find tokens for
|
||||
:return: A dictionary with each pointer possibility as key and (token, position) as value
|
||||
"""
|
||||
# make codel immutable
|
||||
copiedCodel = frozenset(codel)
|
||||
# Find all edges along the codel and edgepointers
|
||||
edges = list(map(lambda pointers, lambdaCodel=copiedCodel: (movement.findEdge(lambdaCodel, pointers), pointers), edgePointers))
|
||||
codelDict = edgesToCodeldict(image, edges)
|
||||
|
||||
if isCodeldictTerminate(codelDict):
|
||||
codelDict = codelDictToTerminate(codelDict)
|
||||
|
||||
return codelDict
|
||||
|
||||
|
||||
def graphImage(image: np.ndarray, position: Tuple[int, int] = (0, 0)) -> Dict[int, Dict[int, Tuple[lexerTokens.baseLexerToken, Tuple[int, int]]]]:
|
||||
"""
|
||||
Returns a dict with hashes of each codel as keys, and a codelDict as value. That codelDict contains hashed pointers (Tuple[int, int]) as keys to tokens as values.
|
||||
:param image:
|
||||
:param position:
|
||||
:return:
|
||||
"""
|
||||
# allCodels = getAllCodels(image, position)
|
||||
allPositions = []
|
||||
whiteCodels = []
|
||||
print(image)
|
||||
for y, row in enumerate(image):
|
||||
for x, pixel in enumerate(row):
|
||||
if not colors.isBlack(pixel):
|
||||
if colors.isWhite(pixel):
|
||||
whiteCodels.append((x,y))
|
||||
else:
|
||||
allPositions.append((x,y))
|
||||
print(len(allPositions))
|
||||
|
||||
|
||||
allCodels = getCodelsEfficient(image, allPositions)
|
||||
# Get an iterator of all possible pointers
|
||||
edgePointers = list(map(lambda i: (i % 4, int(i / 4)), iter(range(8))))
|
||||
return dict(map(lambda x: (hash(frozenset(x)), codelToCodelDict(image, x, edgePointers)), allCodels))
|
77
interpreter/lexerTokens.py
Normal file
77
interpreter/lexerTokens.py
Normal file
@ -0,0 +1,77 @@
|
||||
from typing import Tuple, Union
|
||||
import numpy as np
|
||||
|
||||
import interpreter.imageWrapper as imageWrapper
|
||||
import interpreter.movement as movement
|
||||
import interpreter.colors as colors
|
||||
|
||||
|
||||
class baseLexerToken():
|
||||
def __init__(self, tokenType: str):
|
||||
self.tokenType = tokenType
|
||||
|
||||
def __str__(self):
|
||||
return "Token tokenType = {}".format(self.tokenType)
|
||||
|
||||
def __repr__(self):
|
||||
return str(self)
|
||||
|
||||
|
||||
class toBlackToken(baseLexerToken):
|
||||
def __init__(self, tokenType: str = "toBlack"):
|
||||
super().__init__(tokenType)
|
||||
|
||||
|
||||
class toWhiteToken(baseLexerToken):
|
||||
def __init__(self):
|
||||
super().__init__("toWhite")
|
||||
|
||||
|
||||
class terminateToken(baseLexerToken):
|
||||
def __init__(self):
|
||||
super().__init__("exit")
|
||||
|
||||
|
||||
class toColorToken(baseLexerToken):
|
||||
def __init__(self, tokenType: str, codelSize: int):
|
||||
super().__init__(tokenType)
|
||||
self.codelSize = codelSize
|
||||
|
||||
def __str__(self):
|
||||
return "{}, codelSize = {}".format(super().__str__(), self.codelSize)
|
||||
|
||||
|
||||
def getToken(hueChange: int, lightChange: int) -> str:
|
||||
tokens = [
|
||||
["noop", "push", "pop"],
|
||||
["add", "subtract", "multiply"],
|
||||
["divide", "mod", "not"],
|
||||
["greater", "pointer", "switch"],
|
||||
["duplicate", "roll", "inN"],
|
||||
["inC", "outN", "outC"],
|
||||
]
|
||||
return tokens[hueChange][lightChange]
|
||||
|
||||
|
||||
def edgeToToken(image: np.ndarray, edge: Tuple[Tuple[int, int], Tuple[int, int]]) -> Union[baseLexerToken, bool]:
|
||||
"""
|
||||
:param image:
|
||||
:param edge: (position, direction)
|
||||
:return:
|
||||
"""
|
||||
if not imageWrapper.boundsChecker(image, edge[0]):
|
||||
return False
|
||||
|
||||
nextPosition = movement.getNextPosition(edge[0], edge[1][0])
|
||||
if not imageWrapper.boundsChecker(image, nextPosition):
|
||||
return toBlackToken("edge")
|
||||
|
||||
elif colors.isBlack(imageWrapper.getPixel(image, nextPosition)):
|
||||
return toBlackToken("toBlack")
|
||||
|
||||
if colors.isWhite(imageWrapper.getPixel(image, nextPosition)):
|
||||
return toWhiteToken()
|
||||
|
||||
colorChange = colors.getPixelChange(imageWrapper.getPixel(image, edge[0]), imageWrapper.getPixel(image, nextPosition))
|
||||
tokenType = getToken(colorChange['hueChange'], colorChange['lightChange'])
|
||||
return toColorToken(tokenType, len(imageWrapper.getCodel(image, edge[0])))
|
96
interpreter/main.py
Normal file
96
interpreter/main.py
Normal file
@ -0,0 +1,96 @@
|
||||
import copy
|
||||
from typing import Union
|
||||
import threading
|
||||
import time
|
||||
import sys
|
||||
|
||||
import numpy as np
|
||||
|
||||
import interpreter.imageWrapper as imageWrapper
|
||||
import interpreter.lexer as lexer
|
||||
import interpreter.lexerTokens as lexerTokens
|
||||
import interpreter.movement as movement
|
||||
import interpreter.programState as programState
|
||||
import interpreter.runner as runner
|
||||
|
||||
|
||||
def interpret(image: np.ndarray):
|
||||
graph = lexer.graphImage(im)
|
||||
position = (0, 0)
|
||||
pointers = (0, 0)
|
||||
PS = programState.programState(graph, position, pointers)
|
||||
|
||||
runProgram(image, PS)
|
||||
|
||||
|
||||
def runProgram(image: np.ndarray, PS: programState) -> programState:
|
||||
newState = PS
|
||||
currentCodel = imageWrapper.getCodel(image, newState.position)
|
||||
|
||||
frozencodel = frozenset(currentCodel)
|
||||
newToken = newState.graph[hash(frozencodel)][hash(newState.pointers)][0]
|
||||
|
||||
if isinstance(newToken, lexerTokens.terminateToken):
|
||||
print("")
|
||||
print("TERMINATE!")
|
||||
return newState
|
||||
|
||||
newState = takeStep(image, newState)
|
||||
|
||||
return runProgram(image, newState)
|
||||
|
||||
|
||||
def takeStep(image: np.ndarray, PS: programState.programState) -> Union[programState.programState, bool]:
|
||||
newState = copy.deepcopy(PS)
|
||||
currentCodel = imageWrapper.getCodel(image, newState.position)
|
||||
|
||||
frozencodel = frozenset(currentCodel)
|
||||
newToken = newState.graph[hash(frozencodel)][hash(newState.pointers)][0]
|
||||
edgePosition = newState.graph[hash(frozencodel)][hash(newState.pointers)][1]
|
||||
result = runner.executeToken(newToken, newState.pointers, newState.dataStack)
|
||||
|
||||
if result is None:
|
||||
print("TERMINATE")
|
||||
return False
|
||||
|
||||
if isinstance(newToken, lexerTokens.toWhiteToken) or isinstance(newToken, lexerTokens.toColorToken):
|
||||
newState.position = movement.getNextPosition(edgePosition, newState.pointers[0])
|
||||
|
||||
newState.pointers = result[0]
|
||||
newState.dataStack = result[1]
|
||||
|
||||
return newState
|
||||
|
||||
|
||||
class run:
|
||||
def __init__(self, image: np.ndarray):
|
||||
self.image = image
|
||||
|
||||
|
||||
def __call__(self):
|
||||
self.run_program(self.image, programState.programState(lexer.graphImage(self.image), (0,0), (0,0)) )
|
||||
|
||||
|
||||
def run_program(self,image: np.ndarray, PS: programState) -> programState:
|
||||
currentCodel = imageWrapper.getCodel(image, PS.position)
|
||||
|
||||
frozencodel = frozenset(currentCodel)
|
||||
newToken = PS.graph[hash(frozencodel)][hash(PS.pointers)][0]
|
||||
|
||||
if isinstance(newToken, lexerTokens.terminateToken):
|
||||
print("")
|
||||
print("TERMINATE!")
|
||||
return PS
|
||||
return self.run_program(image, takeStep(image, PS))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
im = imageWrapper.getImage("../brainfuck_interpreter_black.png")
|
||||
interpret(im)
|
||||
|
||||
start_time = time.time()
|
||||
sys.setrecursionlimit(0x100000)
|
||||
threading.stack_size(256000000) #set stack to 256mb
|
||||
t = threading.Thread(target=run(im))
|
||||
t.start()
|
||||
t.join()
|
168
interpreter/movement.py
Normal file
168
interpreter/movement.py
Normal file
@ -0,0 +1,168 @@
|
||||
from typing import Tuple, Set, Union
|
||||
|
||||
|
||||
def getDP(directionPointer: int) -> str:
|
||||
if directionPointer == 0:
|
||||
return 'r'
|
||||
elif directionPointer == 1:
|
||||
return 'd'
|
||||
elif directionPointer == 2:
|
||||
return 'l'
|
||||
else:
|
||||
return 'u'
|
||||
|
||||
|
||||
def getCC(codelChooser: int) -> str:
|
||||
if codelChooser == 0:
|
||||
return 'l'
|
||||
else:
|
||||
return 'r'
|
||||
|
||||
|
||||
def getArrow(pointers: Tuple[int, int]) -> str:
|
||||
if pointers[0] == 0:
|
||||
if pointers[1] == 0:
|
||||
return "\u2197"
|
||||
elif pointers[1] == 1:
|
||||
return "\u2198"
|
||||
elif pointers[0] == 1:
|
||||
if pointers[1] == 0:
|
||||
return "\u2198"
|
||||
elif pointers[1] == 1:
|
||||
return "\u2199"
|
||||
elif pointers[0] == 2:
|
||||
if pointers[1] == 0:
|
||||
return "\u2199"
|
||||
elif pointers[1] == 1:
|
||||
return "\u2196"
|
||||
elif pointers[0] == 3:
|
||||
if pointers[1] == 0:
|
||||
return "\u2196"
|
||||
elif pointers[1] == 1:
|
||||
return "\u2197"
|
||||
else:
|
||||
return ""
|
||||
|
||||
|
||||
def flipCC(codelChooser: int) -> int:
|
||||
"""
|
||||
Flips the codelChooser 0 -> 1, 1 -> 0
|
||||
:param codelChooser: unflipped codelChooser
|
||||
:return: flipped codelChooser
|
||||
"""
|
||||
return int(not codelChooser)
|
||||
|
||||
|
||||
def flipDP(directionPointer: int) -> int:
|
||||
"""
|
||||
Cycles the directionpointer 0 -> 1, 1 -> 2, 2 -> 3, 3 -> 0
|
||||
:param directionPointer: unflipped directionPointer
|
||||
:return: new DirectionPointer
|
||||
"""
|
||||
if directionPointer != 3:
|
||||
return directionPointer + 1
|
||||
return 0
|
||||
|
||||
|
||||
def flip(pointers: Tuple[int, int]) -> Tuple[int, int]:
|
||||
"""
|
||||
Chooses what part of the general pointer to flip, by DP%2 == CC rule, providing the following flow:
|
||||
(0,0) -> (0,1)
|
||||
(0,1) -> (1,1)
|
||||
(1,1) -> (1,0)
|
||||
(1,0) -> (2,0)
|
||||
(2,0) -> (2,1)
|
||||
(2,1) -> (3,1)
|
||||
(3,1) -> (3,0)
|
||||
(3,0) -> (0,0)
|
||||
:param pointers: Original state of the pointers
|
||||
:return: Tuple of ints containing new pointers
|
||||
"""
|
||||
if pointers[0] % 2 == pointers[1]:
|
||||
return (pointers[0], flipCC(pointers[1]))
|
||||
else:
|
||||
return (flipDP(pointers[0]), pointers[1])
|
||||
|
||||
|
||||
# TODO FIX KEYERROR
|
||||
def getNextPosition(startPosition: Tuple[int, int], directionPointer: int) -> Union[Tuple[int, int], KeyError]:
|
||||
if directionPointer == 0:
|
||||
return (startPosition[0] + 1, startPosition[1])
|
||||
elif directionPointer == 1:
|
||||
return (startPosition[0], startPosition[1] + 1)
|
||||
elif directionPointer == 2:
|
||||
return (startPosition[0] - 1, startPosition[1])
|
||||
elif directionPointer == 3:
|
||||
return (startPosition[0], startPosition[1] - 1)
|
||||
else:
|
||||
return KeyError("Given key {} is no valid Direction Pointer (0, 1, 2, or 3)".format(directionPointer))
|
||||
|
||||
|
||||
def getPreviousPosition(startPosition: Tuple[int, int], directionPointer: int) -> Tuple[int, int]:
|
||||
if directionPointer == 0:
|
||||
return getNextPosition(startPosition, 2)
|
||||
elif directionPointer == 1:
|
||||
return getNextPosition(startPosition, 3)
|
||||
elif directionPointer == 2:
|
||||
return getNextPosition(startPosition, 0)
|
||||
return getNextPosition(startPosition, 1)
|
||||
# TODO: make the else return an error, and elif return 'd' position
|
||||
|
||||
|
||||
# TODO Error handling
|
||||
def findEdge(codel: Set[Tuple[int, int]], pointers: Tuple[int, int]) -> Union[Tuple[int, int], bool]:
|
||||
"""
|
||||
Finds the edge of the codel according to the direction pointer and the codel chooser
|
||||
:param codel: Set of adjacent positions with the same color
|
||||
:param pointers: Tuple where pointers[0] = DP and pointers[1] = CC
|
||||
:return: Position within the codel that is adjacent to the next pixel to go to
|
||||
"""
|
||||
dp = pointers[0]
|
||||
cc = pointers[1]
|
||||
|
||||
if dp == 0:
|
||||
edgePosition = max(codel, key=lambda lambdaPos: lambdaPos[0])
|
||||
for pos in codel:
|
||||
if pos[0] == edgePosition[0]:
|
||||
# -> ^ Right and up
|
||||
if cc == 0 and pos[1] < edgePosition[1]:
|
||||
edgePosition = pos
|
||||
# -> V Right and down
|
||||
elif cc == 1 and pos[1] > edgePosition[1]:
|
||||
edgePosition = pos
|
||||
return edgePosition
|
||||
elif dp == 1:
|
||||
edgePosition = max(codel, key=lambda lambdaPos: lambdaPos[1])
|
||||
for pos in codel:
|
||||
if pos[1] == edgePosition[1]:
|
||||
# V -> Down and right
|
||||
if cc == 0 and pos[0] > edgePosition[0]:
|
||||
edgePosition = pos
|
||||
# V <- Down and left
|
||||
elif cc == 1 and pos[0] < edgePosition[0]:
|
||||
edgePosition = pos
|
||||
return edgePosition
|
||||
elif dp == 2:
|
||||
edgePosition = min(codel, key=lambda lambdaPos: lambdaPos[0])
|
||||
for pos in codel:
|
||||
if pos[0] == edgePosition[0]:
|
||||
# <- V Left and down
|
||||
if cc == 0 and pos[1] > edgePosition[1]:
|
||||
edgePosition = pos
|
||||
# <- ^ left and up
|
||||
elif cc == 1 and pos[1] < edgePosition[1]:
|
||||
edgePosition = pos
|
||||
return edgePosition
|
||||
elif dp == 3:
|
||||
edgePosition = min(codel, key=lambda lambdaPos: lambdaPos[1])
|
||||
for pos in codel:
|
||||
if pos[1] == edgePosition[1]:
|
||||
# ^ <- Up and left
|
||||
if cc == 0 and pos[0] < edgePosition[0]:
|
||||
edgePosition = pos
|
||||
# ^ -> Up and right
|
||||
elif cc == 1 and pos[0] > edgePosition[0]:
|
||||
edgePosition = pos
|
||||
return edgePosition
|
||||
else:
|
||||
raise SyntaxError("DirectionPointer '{}' is unknown".format(dp))
|
24
interpreter/programState.py
Normal file
24
interpreter/programState.py
Normal file
@ -0,0 +1,24 @@
|
||||
from typing import Dict, List, Tuple
|
||||
from copy import deepcopy
|
||||
|
||||
import interpreter.lexerTokens as lexerTokens
|
||||
|
||||
|
||||
class programState():
|
||||
def __init__(self, graph: Dict[int, Dict[int, Tuple[lexerTokens.baseLexerToken, Tuple[int, int]]]], position: Tuple[int, int], pointers: Tuple[int, int], dataStack: List[int] = None):
|
||||
if dataStack is None:
|
||||
dataStack = []
|
||||
|
||||
self.graph = graph
|
||||
self.pointers = pointers
|
||||
self.position = position
|
||||
self.dataStack = dataStack
|
||||
|
||||
def __str__(self):
|
||||
return "{pos} / {pointers}. Stack: {stack}".format(pos=self.position, pointers=self.pointers, stack=self.dataStack)
|
||||
|
||||
def __repr__(self):
|
||||
return str(self)
|
||||
|
||||
def __deepcopy__(self, memodict):
|
||||
return programState(self.graph, deepcopy(self.position), deepcopy(self.pointers), deepcopy(self.dataStack))
|
277
interpreter/runner.py
Normal file
277
interpreter/runner.py
Normal file
@ -0,0 +1,277 @@
|
||||
from typing import List, Tuple
|
||||
|
||||
import interpreter.lexerTokens as lexerTokens
|
||||
import interpreter.movement as movement
|
||||
|
||||
|
||||
# TODO Nettere afhandeling errors (Union[Tuple[List[int], Tuple[int, int]], bool])
|
||||
# TODO Test cases maken per token
|
||||
def executeToken(token: lexerTokens.baseLexerToken, pointers: Tuple[int, int], dataStack: List[int]) -> Tuple[Tuple[int, int], List[int]]:
|
||||
if isinstance(token, lexerTokens.toBlackToken):
|
||||
newPointers = movement.flip(pointers)
|
||||
return (newPointers, dataStack)
|
||||
elif isinstance(token, lexerTokens.toWhiteToken):
|
||||
return (pointers, dataStack)
|
||||
elif isinstance(token, lexerTokens.toColorToken):
|
||||
result = executeColorToken(token, pointers, dataStack)
|
||||
return (result[0], result[1])
|
||||
|
||||
|
||||
def executeColorToken(token: lexerTokens.toColorToken, pointers: Tuple[int, int], dataStack: List[int]) -> Tuple[Tuple[int, int], List[int]]:
|
||||
if token.tokenType == "noop":
|
||||
return noopOperator(pointers, dataStack)
|
||||
elif token.tokenType == "push":
|
||||
# Needs the codelsize to push
|
||||
return pushOperator(token, pointers, dataStack)
|
||||
elif token.tokenType == "pop":
|
||||
return popOperator(pointers, dataStack)
|
||||
|
||||
elif token.tokenType == "add":
|
||||
return addOperator(pointers, dataStack)
|
||||
elif token.tokenType == "subtract":
|
||||
return subtractOperator(pointers, dataStack)
|
||||
elif token.tokenType == "multiply":
|
||||
return multiplyOperator(pointers, dataStack)
|
||||
|
||||
elif token.tokenType == "divide":
|
||||
return divideOperator(pointers, dataStack)
|
||||
elif token.tokenType == "mod":
|
||||
return modOperator(pointers, dataStack)
|
||||
elif token.tokenType == "not":
|
||||
return notOperator(pointers, dataStack)
|
||||
|
||||
elif token.tokenType == "greater":
|
||||
return greaterOperator(pointers, dataStack)
|
||||
elif token.tokenType == "pointer":
|
||||
return pointerOperator(pointers, dataStack)
|
||||
elif token.tokenType == "switch":
|
||||
return switchOperator(pointers, dataStack)
|
||||
|
||||
elif token.tokenType == "duplicate":
|
||||
return duplicateOperator(pointers, dataStack)
|
||||
elif token.tokenType == "roll":
|
||||
return rollOperator(pointers, dataStack)
|
||||
elif token.tokenType == "inN":
|
||||
return inNOperator(pointers, dataStack)
|
||||
|
||||
elif token.tokenType == "inC":
|
||||
return inCOperator(pointers, dataStack)
|
||||
elif token.tokenType == "outN":
|
||||
return outNOperator(pointers, dataStack)
|
||||
elif token.tokenType == "outC":
|
||||
return outCOperator(pointers, dataStack)
|
||||
else:
|
||||
# TODO Elegantere manier van afhandelen
|
||||
print("Type niet gevonden, noop uitgevoerd")
|
||||
return noopOperator(pointers, dataStack)
|
||||
|
||||
|
||||
def noopOperator(pointers: Tuple[int, int], dataStack: List[int]) -> Tuple[Tuple[int, int], List[int]]:
|
||||
"""
|
||||
Does nothing
|
||||
:param pointers: The tuple with the direction pointer and codel chooser
|
||||
:param dataStack: input dataStack
|
||||
:return: Tuple of a copy of the dataStack and the endpointers of the token
|
||||
"""
|
||||
return (pointers, list(dataStack))
|
||||
|
||||
|
||||
def addOperator(pointers: Tuple[int, int], dataStack: List[int]) -> Tuple[Tuple[int, int], List[int]]:
|
||||
"""
|
||||
Pops the two values from the stack and add them together, then pushes the result
|
||||
:param pointers: The tuple with the direction pointer and codel chooser
|
||||
:param dataStack: input datastack
|
||||
:return:
|
||||
"""
|
||||
newStack = list(dataStack)
|
||||
if len(newStack) < 2:
|
||||
return (pointers, newStack)
|
||||
newStack.append(newStack.pop() + newStack.pop())
|
||||
return (pointers, newStack)
|
||||
|
||||
|
||||
def subtractOperator(pointers: Tuple[int, int], dataStack: List[int]) -> Tuple[Tuple[int, int], List[int]]:
|
||||
newStack = list(dataStack)
|
||||
if len(newStack) < 2:
|
||||
return (pointers, newStack)
|
||||
|
||||
first = newStack.pop()
|
||||
second = newStack.pop()
|
||||
newStack.append(second - first)
|
||||
return (pointers, newStack)
|
||||
|
||||
|
||||
def multiplyOperator(pointers: Tuple[int, int], dataStack: List[int]) -> Tuple[Tuple[int, int], List[int]]:
|
||||
newStack = list(dataStack)
|
||||
if len(newStack) < 2:
|
||||
return (pointers, newStack)
|
||||
newStack.append(newStack.pop() * newStack.pop())
|
||||
return (pointers, newStack)
|
||||
|
||||
|
||||
def divideOperator(pointers: Tuple[int, int], dataStack: List[int]) -> Tuple[Tuple[int, int], List[int]]:
|
||||
"""
|
||||
Provides integer division (//)
|
||||
:param pointers: The tuple with the direction pointer and codel chooser
|
||||
:param dataStack: A list of ints as stack. last entry is the top
|
||||
:return: Tuple with the new data stack and new pointers
|
||||
"""
|
||||
newStack = list(dataStack)
|
||||
if len(newStack) < 2:
|
||||
return (pointers, newStack)
|
||||
|
||||
first = newStack.pop()
|
||||
second = newStack.pop()
|
||||
if second == 0:
|
||||
raise ZeroDivisionError("{} / {} ".format(first, second))
|
||||
newStack.append(newStack.pop() // newStack.pop())
|
||||
return (pointers, newStack)
|
||||
|
||||
|
||||
def modOperator(pointers: Tuple[int, int], dataStack: List[int]) -> Tuple[Tuple[int, int], List[int]]:
|
||||
newStack = list(dataStack)
|
||||
if len(newStack) < 2:
|
||||
return (pointers, newStack)
|
||||
valA = newStack.pop()
|
||||
valB = newStack.pop()
|
||||
if valB == 0:
|
||||
return (pointers, newStack)
|
||||
newStack.append(valA % valB)
|
||||
return (pointers, newStack)
|
||||
|
||||
|
||||
def greaterOperator(pointers: Tuple[int, int], dataStack: List[int]) -> Tuple[Tuple[int, int], List[int]]:
|
||||
"""
|
||||
Compares the second value of the stack with the first value of the stack. If the stack is empty, this gets ignored
|
||||
:param pointers: The tuple with the direction pointer and codel chooser
|
||||
:param dataStack: The list of values as the stack, last entry is the top of the stack
|
||||
:return: A tuple of pointers and new data stack
|
||||
"""
|
||||
newStack = list(dataStack)
|
||||
if len(newStack) < 2:
|
||||
return (pointers, newStack)
|
||||
|
||||
newStack.append(int(newStack.pop() < newStack.pop()))
|
||||
return (pointers, newStack)
|
||||
|
||||
|
||||
def notOperator(pointers: Tuple[int, int], dataStack: List[int]) -> Tuple[Tuple[int, int], List[int]]:
|
||||
"""
|
||||
Compares the second value of the stack with the first value of the stack
|
||||
:param pointers: The tuple with the direction pointer and codel chooser
|
||||
:param dataStack: The input list of ints as stcak. Last entry is the top of the stack
|
||||
:return: A tuple of pointers and new data stack
|
||||
"""
|
||||
newStack = list(dataStack)
|
||||
if len(newStack) < 1:
|
||||
return (pointers, newStack)
|
||||
|
||||
result = 1 if newStack.pop() == 0 else 0
|
||||
newStack.append(result)
|
||||
return (pointers, newStack)
|
||||
|
||||
|
||||
def pointerOperator(pointers: Tuple[int, int], dataStack: List[int]) -> Tuple[Tuple[int, int], List[int]]:
|
||||
newStack = list(dataStack)
|
||||
if len(newStack) < 1:
|
||||
return (pointers, newStack)
|
||||
|
||||
dpTurnCount = newStack.pop() % 4
|
||||
newDp = (pointers[0] + dpTurnCount) % 4
|
||||
return ((newDp, pointers[1]), newStack)
|
||||
|
||||
|
||||
def switchOperator(pointers: Tuple[int, int], dataStack: List[int]) -> Tuple[Tuple[int, int], List[int]]:
|
||||
newStack = list(dataStack)
|
||||
if len(newStack) < 1:
|
||||
return (pointers, newStack)
|
||||
|
||||
ccTurnCount = newStack.pop() % 2
|
||||
newCC = (pointers[1] + ccTurnCount) % 2
|
||||
return ((pointers[0], newCC), newStack)
|
||||
|
||||
|
||||
# TODO BETERE IO
|
||||
def inNOperator(pointers: Tuple[int, int], dataStack: List[int]) -> Tuple[Tuple[int, int], List[int]]:
|
||||
newStack = list(dataStack)
|
||||
newVal = int(input("Input number: "))
|
||||
newStack.append(newVal)
|
||||
return (pointers, newStack)
|
||||
|
||||
|
||||
def inCOperator(pointers: Tuple[int, int], dataStack: List[int]) -> Tuple[Tuple[int, int], List[int]]:
|
||||
newStack = list(dataStack)
|
||||
newVal = input("Input character")
|
||||
if len(newVal) < 1:
|
||||
return (pointers, newStack)
|
||||
|
||||
appendedStack = pushCharacters(newStack, newVal)
|
||||
return (pointers, appendedStack)
|
||||
|
||||
|
||||
def pushCharacters(dataStack: List[int], characters: str) -> List[int]:
|
||||
newStack = list(dataStack)
|
||||
if len(characters) < 1:
|
||||
return newStack
|
||||
else:
|
||||
newStack.append(ord(characters[0]))
|
||||
return pushCharacters(newStack, characters[1:])
|
||||
|
||||
|
||||
def outNOperator(pointers: Tuple[int, int], dataStack: List[int]) -> Tuple[Tuple[int, int], List[int]]:
|
||||
newStack = list(dataStack)
|
||||
print(newStack.pop(), end="")
|
||||
return (pointers, newStack)
|
||||
|
||||
|
||||
def outCOperator(pointers: Tuple[int, int], dataStack: List[int]) -> Tuple[Tuple[int, int], List[int]]:
|
||||
newStack = list(dataStack)
|
||||
print(chr(newStack.pop()), end="")
|
||||
return (pointers, newStack)
|
||||
|
||||
|
||||
def pushOperator(token: lexerTokens.toColorToken, pointers: Tuple[int, int], dataStack: List[int]) -> Tuple[Tuple[int, int], List[int]]:
|
||||
newStack = list(dataStack)
|
||||
newStack.append(token.codelSize)
|
||||
return (pointers, newStack)
|
||||
|
||||
|
||||
def popOperator(pointers: Tuple[int, int], dataStack: List[int]) -> Tuple[Tuple[int, int], List[int]]:
|
||||
newStack = list(dataStack)
|
||||
if len(newStack) < 1:
|
||||
return (pointers, newStack)
|
||||
newStack.pop()
|
||||
return (pointers, newStack)
|
||||
|
||||
|
||||
def duplicateOperator(pointers: Tuple[int, int], dataStack: List[int]) -> Tuple[Tuple[int, int], List[int]]:
|
||||
newStack = list(dataStack)
|
||||
if len(newStack) < 1:
|
||||
return (pointers, newStack)
|
||||
|
||||
val = newStack.pop()
|
||||
newStack.append(val)
|
||||
newStack.append(val)
|
||||
return (pointers, newStack)
|
||||
|
||||
|
||||
def rollOperator(pointers: Tuple[int, int], dataStack: List[int]) -> Tuple[Tuple[int, int], List[int]]:
|
||||
newStack = list(dataStack)
|
||||
if len(newStack) < 3:
|
||||
return (pointers, newStack)
|
||||
rolls = newStack.pop()
|
||||
depth = newStack.pop()
|
||||
insertIndex = len(newStack) - depth
|
||||
|
||||
if depth <= 0 or insertIndex < 0 or insertIndex >= len(newStack) or rolls == 0 or depth == rolls:
|
||||
return (pointers, newStack)
|
||||
|
||||
# TODO could also do rolls % depth times, instead of rolls times
|
||||
if rolls < 0:
|
||||
for i in range(abs(rolls)):
|
||||
newStack.append(newStack.pop(insertIndex))
|
||||
else:
|
||||
for i in range(rolls):
|
||||
newStack.insert(insertIndex, newStack.pop())
|
||||
|
||||
return (pointers, newStack)
|
Loading…
x
Reference in New Issue
Block a user