Interpreter

This commit is contained in:
Jens Bouman 2020-04-29 13:42:12 +02:00
commit 1d0858c545
17 changed files with 2115 additions and 0 deletions

View 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)

View 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
View 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
View File

View 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) -&gt; (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
View 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) -&gt; (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
View 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
View 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
View File

67
interpreter/colors.py Normal file
View 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
View 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
View 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))

View 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
View 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
View 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))

View 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
View 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)