This commit is contained in:
Jens Bouman 2020-05-06 22:42:04 +02:00
parent 0576145b10
commit afa67b21ac
27 changed files with 9278 additions and 884 deletions

BIN
GUI/assets/play_button.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

BIN
GUI/assets/play_button.xcf Normal file

Binary file not shown.

View File

@ -51,7 +51,7 @@ class canvasManager():
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
for position in codel.codel:
x = position.coords[0] * self.scaleSize
y = position.coords[1] * self.scaleSize
self.canvas.create_rectangle(x,y, x+self.scaleSize - 1, y+self.scaleSize - 1, fill=fill, outline=outline)

View File

@ -1,7 +1,8 @@
import interpreter.imageWrapper as imageWrapper
import interpreter.colors as colors
import interpreter.lexerTokens as lexerTokens
import interpreter.tokens as lexerTokens
import interpreter.movement as movement
from interpreter.dataStructures import direction
class infoManager():
def __init__(self, builder, generalInfoFrame, programStateInfoFrame):
@ -19,7 +20,7 @@ class infoManager():
def updateProgramStateInfo(self, programState):
self.updateStackInfo(programState.dataStack)
self.updatePointersInfo(programState.position, programState.pointers)
self.updatePointersInfo(programState.position, programState.direction)
def updateCodelInfo(self, image, newPosition):
infoMessage = self.builder.get_object('positionInfoMessage', self.generalInfo)
@ -29,13 +30,13 @@ class infoManager():
baseString = "Selected codel contains:\n"
codel = imageWrapper.getCodel(image, newPosition)
for position in codel:
for position in codel.codel:
baseString += "{}\n".format(position)
infoMessage.configure(text=baseString.strip('\n'))
def updateEdgesInfo(self, image, graph, programState):
def updateEdgesInfo(self, image, inputGraph, programState):
edgesInfo = self.builder.get_object('codelEdgesMessage', self.generalInfo)
if colors.isBlack(imageWrapper.getPixel(image, programState.position)):
@ -44,22 +45,25 @@ class infoManager():
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)
graphNode = inputGraph.graph[codel]
edge = graphNode.graphNode[programState.direction]
baseString += self.getEdgeDescription(edge, programState.direction)
baseString += "\nCodel edges are as follows:\n"
#Generate pointers
edgePointers = list(map(lambda i: (i%4, int(i/4)), iter(range(8))))
edgePointers = list(map(lambda i: direction((i%4, int(i/4))), iter(range(8))))
for edgePointer in edgePointers:
edge = graph[hash(frozenset(codel))][hash(edgePointer)]
edge = graphNode.graphNode[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)
if isinstance(edge[0], lexerTokens.toColorToken) and edge[0].tokenType == "push":
return "{}/{},{} -> {}({})\n".format(edge[1], movement.getDP(pointer.pointers[0]), movement.getCC(pointer.pointers[1]), edge[0].tokenType, edge[0].codelSize)
else:
return "{}/{},{} -> {}\n".format(edge[1], movement.getDP(pointer[0]), movement.getCC(pointer[1]), edge[0].type)
return "{}/{},{} -> {}\n".format(edge[1], movement.getDP(pointer.pointers[0]), movement.getCC(pointer.pointers[1]), edge[0].tokenType)
def updateStackInfo(self, stack):
baseString = ""
@ -70,10 +74,10 @@ class infoManager():
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]))
def updatePointersInfo(self, position, direction):
# print("Update pointers: {} -> Arrow: {}".format(direction, movement.getArrow(direction)))
baseString = "Pos: ({},{})\n".format(position.coords[0], position.coords[1])
baseString += u"DP: {} ({},{})".format(movement.getArrow(direction), movement.getDP(direction.pointers[0]), movement.getCC(direction.pointers[1]))
pointersInfoMessage = self.builder.get_object("pointerMessage", self.programStateInfoFrame)
pointersInfoMessage.configure(text=baseString)

View File

@ -1,68 +0,0 @@
<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

View File

@ -1,74 +0,0 @@
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()

View File

@ -1,20 +1,20 @@
# helloworld.py
from time import sleep
import threading
import tkinter as tk
import pygubu
import sys
import os
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
sys.path.insert(0, "GUI/TKinter/.")
from interpreter import imageWrapper as imageWrapper
from interpreter import lexer as lexer
from interpreter import tokens as lexerTokens
from interpreter import colors as colors
from interpreter import movement as movement
from interpreter import executionFunctions as main
from interpreter.dataStructures import programState, direction, position
import threading
import infoManager
import canvasManager
from GUI import infoManager
from GUI import canvasManager
class GUI:
@ -41,7 +41,7 @@ class GUI:
self.builder = builder = pygubu.Builder()
#2: Load an ui file
builder.add_from_file('../assets/tkinterLayout.ui')
builder.add_from_file("{}/tkinterLayout.ui".format(os.path.abspath(os.path.dirname(__file__))))
#3: Create the mainwindow
self.mainwindow = builder.get_object('rootWindow')
@ -63,7 +63,8 @@ class GUI:
'takeStep': self.takeStep,
'setExecutionSpeed': self.setExecutionSpeed,
'setBreakpoint': self.setBreakpoint,
'runProgram': self.runProgram
'runProgram': self.runProgram,
'updateHighlight': self.toggleHighlight
})
self.canvas.bind("<Button-1>", self.canvasPressed)
@ -104,11 +105,19 @@ class GUI:
def setBreakpoint(self):
print("BREAKPOINT")
def setFileText(self, filePath):
print("Filepath: {}".format(filePath))
self.builder.get_object("fileNameEntry", self.optionBar).delete(0, len(self.builder.get_object("fileNameEntry", self.optionBar).get()))
self.builder.get_object("fileNameEntry", self.optionBar).insert(0, filePath)
print("Get filepath: {}".format(self.builder.get_object("fileNameEntry", self.optionBar).get()))
def setExecutionSpeed(self, pos):
if 0 < float(pos) < 100:
self.executionSpeed = float(pos)
def toggleHighlight(self):
print(self.builder.get_object("highlightEdgeCheck", self.actionBar).instate(['selected']))
def getWaitTime(self):
return self.executionSpeed/100*self.maxWait
@ -135,9 +144,12 @@ class GUI:
def loadFile(self):
fileName = self.builder.get_object('fileNameEntry', self.optionBar).get()
if len(fileName) < 1:
return None
self.image = imageWrapper.getImage(fileName)
self.graph = lexer.graphImage(self.image)
self.programState = programState.programState(self.graph, (0,0), (0,0))
self.graph = lexer.graphImage(self.image)[0]
self.programState = programState(self.graph, position((0,0)), direction((0,0)))
self.update()
print("LOAD FILE!")

View File

@ -42,7 +42,8 @@
<child>
<object id="fileNameEntry" class="ttk.Entry">
<property name="exportselection">false</property>
<property name="text" translatable="yes">../../Add.png</property>
<property name="state">normal</property>
<property name="text" translatable="yes">Add.png</property>
<property name="width">44</property>
<layout>
<property name="column">1</property>
@ -146,6 +147,18 @@
</layout>
</object>
</child>
<child>
<object id="highlightEdgeCheck" class="ttk.Checkbutton">
<property name="command">updateHighlight</property>
<property name="text" translatable="yes">Highlight edges</property>
<property name="variable">int:0</property>
<layout>
<property name="column">4</property>
<property name="propagate">True</property>
<property name="row">0</property>
</layout>
</object>
</child>
</object>
</child>
<child>

BIN
Info/poster.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 520 KiB

BIN
Info/poster.xcf Normal file

Binary file not shown.

BIN
interpreter/__init__.pyc Normal file

Binary file not shown.

View File

@ -1,7 +1,8 @@
from typing import Dict
from typing import Dict, Union
import numpy as np
import interpreter.errors as errors
class possiblePixels:
def __init__(self):
@ -29,16 +30,38 @@ class possiblePixels:
self.black = [0, 0, 0]
def getPixelChange(colorStart: np.ndarray, colorEnd: np.ndarray) -> Dict[str, int]:
pixelsColors = possiblePixels()
def getPixelChange(colorStart: np.ndarray, colorEnd: np.ndarray) -> Union[Dict[str, int], BaseException]:
"""
Gets the Hue change and the light change from two different colors
:param colorStart: Starting color
:param colorEnd: Final color
:return: Either a dictionary {'hueChange': int, 'lightChange': int}, or an Exception
"""
if type(colorStart) is not np.ndarray:
return TypeError("Start color is not of type np.ndarray, but {}".format(type(colorStart)))
if type(colorEnd) is not np.ndarray:
return TypeError("End color is not of type np.ndarray, but {}".format(type(colorStart)))
if len(colorStart) < 3:
return ValueError("Start color does contain at least 3 values, but {}".format(colorStart))
if len(colorEnd) < 3:
return ValueError("Start color does contain at least 3 values, but {}".format(colorEnd))
# If either the starting or leaving color is white, there is no change (It is considered a noop)
if isWhite(colorStart) or isWhite(colorEnd):
return {"hueChange": 0, "lightChange": 0}
pixelsColors = possiblePixels()
# Converting np arrays to common lists
colorStart = list(colorStart)[:3]
colorEnd = list(colorEnd)[:3]
if colorStart not in pixelsColors.colors:
return errors.UnknownColorError("Color {} is not recognized as a correct color".format(colorStart))
if colorEnd not in pixelsColors.colors:
return errors.UnknownColorError("Color {} is not recognized as a correct color".format(colorEnd))
indexStart = pixelsColors.colors.index(colorStart)
indexEnd = pixelsColors.colors.index(colorEnd)
@ -50,18 +73,33 @@ def getPixelChange(colorStart: np.ndarray, colorEnd: np.ndarray) -> Dict[str, in
def isWhite(testColor: np.ndarray) -> bool:
"""
Compares the color to white
:param testColor: Input color
:return: Boolean whether the input color is white (255, 255, 255)
"""
colors = possiblePixels()
testColor = list(testColor)[:3]
return testColor == colors.white
def isBlack(testColor: np.ndarray) -> bool:
"""
Compares the color to black
:param testColor: Input color
:return: Boolean whether the input color is black (0, 0, 0)
"""
colors = possiblePixels()
testColor = list(testColor)[:3]
return testColor == colors.black
def isColor(testColor: np.ndarray) -> bool:
"""
Compares the color to the 18 pre-defined Piet colors
:param testColor: Input color
:return: Boolean whether the input color is a Piet-color
"""
colors = possiblePixels()
testColor = list(testColor)[:3]
return testColor in colors.colors

View File

@ -0,0 +1,145 @@
from typing import Set, Tuple, Dict, List
import copy
import interpreter.tokens as tokens
class position():
"""
A coords is a tuple of x and y coordinates
"""
def __init__(self, newPosition: Tuple[int, int]):
self.coords = newPosition
def __str__(self):
return "{}".format(self.coords)
def __repr__(self):
return str(self)
def __deepcopy__(self, memodict):
return position(copy.deepcopy(self.coords))
# Functions to allow this datatype to behave in sets
def __hash__(self):
return hash(self.coords)
def __eq__(self, other):
return other.coords == self.coords
def __ne__(self, other):
return not self == other
class direction():
"""
A direction is made up of a Direction Pointer (DP) at .pointers[0] and a Codel Chooser (CC) at .pointers[1].
"""
def __init__(self, newPointers: Tuple[int, int]):
self.pointers = newPointers
def __str__(self):
return "{}".format(self.pointers)
def __repr__(self):
return "{}".format(self.pointers)
def __deepcopy__(self, memodict):
return direction(copy.deepcopy(self.pointers))
# Functions to allow this datatype to behave in sets
def __eq__(self, other):
return self.pointers == other.pointers
def __ne__(self, other):
return not self == other
def __hash__(self):
return hash(self.pointers)
class codel():
"""
A codel is a set of positions adjacent to each other and with the same color as each other
"""
def __init__(self, newCodel: Set[position]):
self.codel = newCodel
def __str__(self):
return "{}".format(self.codel)
def __repr__(self):
return str(self)
def __copy__(self):
return codel(copy.copy(self.codel))
# Functions to allow this datatype to behave in sets
def __hash__(self):
return hash(frozenset(self.codel))
def __eq__(self, other):
return other.codel == self.codel
def __ne__(self, other):
return not self == other
class edge():
"""
The edge contains a position and direction (DP and CC)
"""
def __init__(self, newEdge: Tuple[position, direction]):
self.edge = newEdge
def __str__(self):
return "{}".format(self.edge)
def __repr__(self):
return str(self)
class graphNode():
"""
The key to the token and coords is a direction
"""
def __init__(self, newNode: Dict[direction, Tuple[tokens.baseLexerToken, position]]):
self.graphNode = newNode
def __str__(self):
return "{}".format(self.graphNode)
def __repr__(self):
return str(self)
class graph():
"""
Each codel has a node of directions and tokens associated with those directions (and where the edge will start)
"""
def __init__(self, newGraph: Dict[codel, graphNode]):
self.graph = newGraph
def __str__(self):
return "{}".format(self.graph)
def __repr__(self):
return str(self)
class programState():
def __init__(self, newGraph: graph, newPosition: position, newDirection: direction, dataStack: List[int] = None):
if dataStack is None:
dataStack = []
self.graph = newGraph
self.position = newPosition
self.direction = newDirection
self.dataStack = dataStack
def __str__(self):
return "{pos} / {pointers}. Stack: {stack}".format(pos=self.position, pointers=self.direction, stack=self.dataStack)
def __repr__(self):
return str(self)
def __deepcopy__(self, memodict):
# Don't copy the graph, because it is not intended to be edited, and it is a slow process
return programState(self.graph, copy.deepcopy(self.position), copy.deepcopy(self.direction), copy.deepcopy(self.dataStack))

13
interpreter/errors.py Normal file
View File

@ -0,0 +1,13 @@
class UnknownColorError(BaseException):
"""Raise this when a color is found that is not one of the 18 allowed colors"""
class CommandNotFoundError(BaseException):
"""Raise this when a command of an token is unknown"""
class UnknownTokenError(BaseException):
"""Raise this when a token is unknown"""
class inBlackPixelError(BaseException):
"""Raise this when a programstate begins inside a black pixel"""

View File

@ -0,0 +1,110 @@
import copy
from typing import Union, List, Callable
import sys
import numpy as np
# sys.path.insert(0, "../")
from interpreter import imageWrapper as imageWrapper
from interpreter import lexer as lexer
from interpreter import tokens as tokens
from interpreter import movement as movement
from interpreter import colors as colors
from interpreter import tokenFunctions as runner
from interpreter import errors as errors
from interpreter.dataStructures import programState, position, direction
def interpret(image: np.ndarray) -> Union[programState, List[BaseException]]:
"""
Interprets and executes a Piet image
:param image: Input image
:return: Either the final state of the program, or a list of exceptions
"""
graph = lexer.graphImage(image)
if len(graph[1]) > 0:
print("The following exceptions occured while making the graph:\n{}".format("".join(list(map(lambda x: "\t{}\n".format(x), graph[1])))))
return graph[1]
startPosition = position((0, 0))
pointers = direction((0, 0))
PS = programState(graph[0], startPosition, pointers)
result = runProgram(image, PS)
if isinstance(result, BaseException):
print("The following exceptions occured while executing the next step:\n{}".format(result))
return [result]
return result
def runProgram(image: np.ndarray, PS: programState) -> Union[programState, BaseException]:
"""
Executes all steps from the image
:param image: input image
:param PS: current program state with which to make the next step
:return: Either the last program state, or a runtime exception
"""
newState = copy.deepcopy(PS)
if colors.isBlack(imageWrapper.getPixel(image, newState.position)):
return errors.inBlackPixelError("Programstate starts in black pixel at {}".format(newState.position))
currentCodel = imageWrapper.getCodel(image, newState.position)
newGraph = newState.graph.graph
graphNode = newGraph[currentCodel]
newToken = graphNode.graphNode[newState.direction][0]
if isinstance(newToken, tokens.terminateToken):
return newState
newState = takeStep(image, newState)
if isinstance(newState, BaseException):
return newState
return runProgram(image, newState)
def countSteps(f: Callable):
def inner(image: np.ndarray, PS: programState):
inner.counter += 1
return f(image, PS)
inner.counter = 0
return inner
@countSteps
def takeStep(image: np.ndarray, PS: programState) -> Union[programState, BaseException]:
"""
Takes a single step from the programstate
:param image: input image
:param PS: input programstate
:return: Returns either the resulting programstate, or an exception that occurred
"""
newState = copy.deepcopy(PS)
currentCodel = imageWrapper.getCodel(image, newState.position)
newGraph = newState.graph.graph
graphNode = newGraph[currentCodel]
newToken = graphNode.graphNode[newState.direction][0]
edgePosition = graphNode.graphNode[newState.direction][1]
result = runner.executeToken(newToken, newState.direction, newState.dataStack)
if isinstance(result, BaseException):
return result
# If the next token is either white or color, just move along. If the token was black (or terminate), the direction
# is already changed
if isinstance(newToken, (tokens.toWhiteToken, tokens.toColorToken)):
newState.position = movement.getNextPosition(edgePosition, newState.direction.pointers[0])
newState.direction = result[0]
newState.dataStack = result[1]
return newState
if __name__ == "__main__":
sys.setrecursionlimit(1000000)
im = imageWrapper.getImage("../Piet_hello.png")
interpret(im)

View File

@ -0,0 +1,44 @@
from typing import Union, List, Any
import numpy as np
import interpreter.imageWrapper as imageWrapper
import interpreter.colors as colors
import interpreter.movement as movement
import interpreter.tokens as tokens
import interpreter.errors as errors
from interpreter.dataStructures import edge
def edgeToToken(image: np.ndarray, inputEdge: edge) -> Union[tokens.baseLexerToken, BaseException]:
"""
This function creates a token based on the given edge
:param image: input image
:param inputEdge: an edge containing (coords, direction)
:return: Either a newly created token, or an exception
"""
if not imageWrapper.boundsChecker(image, inputEdge.edge[0]):
return IndexError("Edge position {} is not in image".format(inputEdge.edge[0]))
nextPosition = movement.getNextPosition(inputEdge.edge[0], inputEdge.edge[1].pointers[0])
if not imageWrapper.boundsChecker(image, nextPosition):
return tokens.toBlackToken("edge")
pixel = imageWrapper.getPixel(image, nextPosition)
if colors.isBlack(pixel):
return tokens.toBlackToken("toBlack")
if colors.isWhite(pixel):
return tokens.toWhiteToken()
if not colors.isColor(pixel):
return tokens.toBlackToken("Unknown color")
colorChange = colors.getPixelChange(imageWrapper.getPixel(image, inputEdge.edge[0]), imageWrapper.getPixel(image, nextPosition))
if isinstance(colorChange, BaseException):
# Modify existing error message with location
newText = "{} at position {}".format(colorChange.args[0], nextPosition)
return errors.UnknownColorError(newText)
tokenType = tokens.getTokenType(colorChange['hueChange'], colorChange['lightChange'])
return tokens.toColorToken(tokenType, len(imageWrapper.getCodel(image, inputEdge.edge[0]).codel))

View File

@ -1,29 +1,27 @@
from typing import Tuple, Union, Set, List
from typing import Union
from PIL import Image
import numpy as np
import interpreter.movement as movement
import interpreter.colors as colors
from interpreter.dataStructures import position, codel
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 boundsChecker(image: np.ndarray, inputPosition: position) -> bool:
# Position 0 = x-axis, while matrix[0] = y-axis. This is why we compare coords[0] with matrix[1]
return 0 <= inputPosition.coords[0] < image.shape[1] and \
0 <= inputPosition.coords[1] < image.shape[0]
def getPixel(image: np.ndarray, position: Tuple[int, int]) -> Union[np.ndarray, bool]:
def getPixel(image: np.ndarray, inputPosition: position) -> Union[np.ndarray, bool]:
"""
This function the pixel at a specific location
:param image: np.ndarray of image
:param position: wanted position
:param coords: wanted coords
: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
if boundsChecker(image, inputPosition):
return image[inputPosition.coords[1]][inputPosition.coords[0]]
return False
def getImage(fileName: str) -> np.ndarray:
@ -38,89 +36,51 @@ def getImage(fileName: str) -> np.ndarray:
return np.array(image)
def getCodel(image: np.ndarray, position: Tuple[int, int], foundPixels: Set[Tuple[int, int]] = None) -> Set[Tuple[int, int]]:
def getCodel(image: np.ndarray, inputPosition: position, foundPixels: codel = None) -> codel:
"""
This function finds all adjacent pixels with the same color as the pixel on the given position
This function finds all adjacent pixels with the same color as the pixel on the given coords
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 coords: Starting coords
: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()
foundPixels = codel(set())
# If this position is already in the set, it has already been traversed
if position in foundPixels:
# If this coords is already in the set, it has already been traversed
if inputPosition in foundPixels.codel:
return foundPixels
if colors.isWhite(getPixel(image, position)):
foundPixels.add(position)
if colors.isWhite(getPixel(image, inputPosition)):
foundPixels.codel.add(inputPosition)
return foundPixels
x = position[0]
y = position[1]
x = inputPosition.coords[0]
y = inputPosition.coords[1]
foundPixels.add(position)
foundPixels.codel.add(inputPosition)
# 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))
if boundsChecker(image, position((x + 1, y))) and np.all(image[y][x + 1] == image[y][x]):
newPosition = position((inputPosition.coords[0] + 1, inputPosition.coords[1]))
foundPixels = codel(foundPixels.codel.union(getCodel(image, newPosition, foundPixels).codel))
# 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))
if boundsChecker(image, position((x, y - 1))) and np.all(image[y - 1][x] == image[y][x]):
newPosition = position((inputPosition.coords[0], inputPosition.coords[1] - 1))
foundPixels = codel(foundPixels.codel.union(getCodel(image, newPosition, foundPixels).codel))
# 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))
if boundsChecker(image, position((x - 1, y))) and np.all(image[y][x - 1] == image[y][x]):
newPosition = position((inputPosition.coords[0] - 1, inputPosition.coords[1]))
foundPixels = codel(foundPixels.codel.union(getCodel(image, newPosition, foundPixels).codel))
# 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))
if boundsChecker(image, position((x, y + 1))) and np.all(image[y + 1][x] == image[y][x]):
newPosition = position((inputPosition.coords[0], inputPosition.coords[1] + 1))
foundPixels = codel(foundPixels.codel.union(getCodel(image, newPosition, foundPixels).codel))
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))

View File

@ -1,138 +1,148 @@
from typing import List, Tuple, Set, Dict, Union
from typing import List, Union, Tuple
import copy
import numpy as np
import interpreter.colors as colors
import interpreter.imageWrapper as imageWrapper
import interpreter.lexerTokens as lexerTokens
import interpreter.tokens as tokens
import interpreter.helperFunctions as helperFunctions
import interpreter.movement as movement
from interpreter.dataStructures import position, codel, edge, graphNode, graph, direction
def cyclePosition(image: np.ndarray, startPosition: Tuple[int, int]) -> Union[Tuple[int, int], bool]:
def cyclePosition(image: np.ndarray, startPosition: position) -> Union[position, 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
:return: newPosition (x,y), or false if new coords 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])
if startPosition.coords[0] == image.shape[1] - 1:
if startPosition.coords[1] < image.shape[0] - 1:
return position((0, startPosition.coords[1] + 1))
return False
return position((startPosition.coords[0] + 1, startPosition.coords[1]))
def getCodelsEfficient(image: np.ndarray, positionList: List[Tuple[int, int]]) -> List[Set[Tuple[int, int]]]:
def getCodels(image: np.ndarray, positionList: List[position]) -> List[codel]:
"""
Makes a list of codels from an image and a lits of positions to check
:param image: an np.ndarray representing the image
:param positionList: A list of positions, for which to find adjacent pixels of the same color
:return: A list of codels found in the given image
"""
if len(positionList) == 0:
return []
copiedList = positionList.copy()
newPosition = copiedList.pop(0)
if colors.isBlack(imageWrapper.getPixel(image, newPosition)):
return getCodelsEfficient(image, copiedList)
return getCodels(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)
# Remove found positions from coords list
copiedList = list(set(copiedList) - newCodel.codel)
codelList = getCodels(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]]]:
def edgesToGraphNode(image: np.ndarray, edges: List[edge]) -> Tuple[graphNode, List[BaseException]]:
"""
Constructs a dictionary with each pointer possibility as key and (token, position) as value
Constructs a dictionary with each pointer possibility as key and (token, coords) as value
:param image: Image required to find calculate tokens
:param edges: List[Tuple[position, pointers]]
:return:
:param edges: List[Tuple[coords, pointers]]
:return: A graphNode containing tokens for each edge given, and a list of exceptions occurred during creation
"""
return dict(map(lambda x, lambdaImage=image: (hash(x[1]), (lexerTokens.edgeToToken(lambdaImage, x), x[0])), edges))
node = graphNode(dict(map(lambda x, lambdaImage=image: (x.edge[1], (helperFunctions.edgeToToken(lambdaImage, x), x.edge[0])), edges)))
# Extract the exceptions from each edge
exceptions = list(map(lambda x: x[1][0], filter(lambda graphNodeItem: isinstance(graphNodeItem[1][0], BaseException), node.graphNode.items())))
return (node, exceptions)
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 isGraphNodeTerminate(inputNode: graphNode) -> bool:
"""
Gets the token from the graphNode, and compares it against the toBlackToken from tokens.
:param inputNode: A graph node
:return: True if all tokens in graph node are toBlackTokens, False otherwise.
"""
return all(map(lambda x: isinstance(x[1][0], tokens.toBlackToken), inputNode.graphNode.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 graphNodeToTerminate(inputNode: graphNode) -> graphNode:
"""
Replaces all tokens in the graphNode to terminate tokens
:param inputNode: A graph node
:return: A new graph node with only terminateTokens
"""
return graphNode(dict(map(lambda x: (x[0], (tokens.terminateToken(), x[1][1])), inputNode.graphNode.items())))
def codelToCodelDict(image: np.ndarray, codel: Set[Tuple[int, int]], edgePointers: List[Tuple[int, int]]) -> Dict[int, Tuple[lexerTokens.baseLexerToken, Tuple[int, int]]]:
def codelToGraphNode(image: np.ndarray, inputCodel: codel, edgePointers: List[direction]) -> Tuple[graphNode, List[BaseException]]:
"""
:param image: image
:param codel: set of positions within the same color
:param inputCodel: 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
:return: A dictionary with each pointer possibility as key and (token, coords) as value, and a list of exceptions
"""
# make codel immutable
copiedCodel = frozenset(codel)
copiedCodel = copy.copy(inputCodel)
# 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)
edges = list(map(lambda pointers, lambdaCodel=copiedCodel: edge((movement.findEdge(lambdaCodel, pointers), pointers)), edgePointers))
newGraphNode = edgesToGraphNode(image, edges)
if isCodeldictTerminate(codelDict):
codelDict = codelDictToTerminate(codelDict)
# If there were exceptions in the graph node, there is no need to terminate them
if len(newGraphNode[1]) > 0:
return newGraphNode
return codelDict
# Check if all tokens go either towards black pixels, or towards the edge. If thats the case, this is a terminate-node
if isGraphNodeTerminate(newGraphNode[0]):
return (graphNodeToTerminate(newGraphNode[0]), newGraphNode[1])
return newGraphNode
def codelsToGraph(image: np.ndarray, codels: List[codel]) -> Tuple[graph, List[BaseException]]:
"""
Converts a list of codels into a graph
:param image: Input image
:param codels: Input list of codels
:return: A tuple of a graph and a list of exceptions
"""
codels = codels.copy()
# Get an iterator of all possible directions (0,0), (0,1), (1,0) etc...
edgePointers = list(map(lambda i: direction((i % 4, int(i / 4))), iter(range(8))))
# If no more codels are to be graphed, return
if len(codels) == 0:
newGraph = graph(dict())
return (newGraph, [])
newNode = codelToGraphNode(image, codels[0], edgePointers)
newGraph = codelsToGraph(image, codels[1:])
newGraph[0].graph[codels[0]] = newNode[0]
errorList = newNode[1]
errorList.extend(newGraph[1])
return (newGraph[0], errorList)
def graphImage(image: np.ndarray, position: Tuple[int, int] = (0, 0)) -> Dict[int, Dict[int, Tuple[lexerTokens.baseLexerToken, Tuple[int, int]]]]:
def graphImage(image: np.ndarray) -> Tuple[graph, List[BaseException]]:
"""
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))
coords = np.ndindex(image.shape[1], image.shape[0])
# Converts tuples of coordinates into position objects
positions = map(position, coords)
# Makes a list of non-black pixel positions
nonBlackPositions = list(filter(lambda pos: not colors.isBlack(imageWrapper.getPixel(image, pos)), positions))
# Gets all codels from all non-black pixel positions
allCodels = getCodels(image, nonBlackPositions)
# Makes a graph with the codel as key, and the node as value
return codelsToGraph(image, allCodels)

View File

@ -1,77 +0,0 @@
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])))

View File

@ -1,96 +0,0 @@
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()

View File

@ -1,48 +1,68 @@
from typing import Tuple, Set, Union
from typing import Union
from interpreter.dataStructures import direction, position, codel
def getDP(directionPointer: int) -> str:
"""
Finds the correct direction pointer string
:param directionPointer: Input direction pointer
:return: direction pointer string
"""
if directionPointer == 0:
return 'r'
elif directionPointer == 1:
if directionPointer == 1:
return 'd'
elif directionPointer == 2:
if directionPointer == 2:
return 'l'
else:
return 'u'
return 'u'
def getCC(codelChooser: int) -> str:
"""
finds the correct codel chooser direction string
:param codelChooser: input codel chooser
:return: codel chooser direction string
"""
if codelChooser == 0:
return 'l'
else:
return 'r'
return 'r'
def getArrow(pointers: Tuple[int, int]) -> str:
if pointers[0] == 0:
if pointers[1] == 0:
def getArrow(direction: direction) -> str:
"""
Returns the Unicode arrow from the direction
:param direction: Input direction
:return: Unicode arrow string
"""
if direction.pointers[0] == 0:
if direction.pointers[1] == 0:
return "\u2197"
elif pointers[1] == 1:
if direction.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 ""
if direction.pointers[0] == 1:
if direction.pointers[1] == 0:
return "\u2198"
if direction.pointers[1] == 1:
return "\u2199"
return ""
if direction.pointers[0] == 2:
if direction.pointers[1] == 0:
return "\u2199"
if direction.pointers[1] == 1:
return "\u2196"
return ""
if direction.pointers[0] == 3:
if direction.pointers[1] == 0:
return "\u2196"
if direction.pointers[1] == 1:
return "\u2197"
return ""
return ""
def flipCC(codelChooser: int) -> int:
"""
@ -63,8 +83,15 @@ def flipDP(directionPointer: int) -> int:
return directionPointer + 1
return 0
def flipDPInvert(directionPointer: int, count = 0) -> int:
if count >= 0:
return directionPointer
else:
if directionPointer != 0:
return flipDPInvert(directionPointer - 1, count + 1)
return flipDPInvert(3, count + 1)
def flip(pointers: Tuple[int, int]) -> Tuple[int, int]:
def flip(inputDirection: direction) -> direction:
"""
Chooses what part of the general pointer to flip, by DP%2 == CC rule, providing the following flow:
(0,0) -> (0,1)
@ -75,93 +102,101 @@ def flip(pointers: Tuple[int, int]) -> Tuple[int, int]:
(2,1) -> (3,1)
(3,1) -> (3,0)
(3,0) -> (0,0)
:param pointers: Original state of the pointers
:param inputDirection: 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])
if inputDirection.pointers[0] % 2 == inputDirection.pointers[1]:
return direction((inputDirection.pointers[0], flipCC(inputDirection.pointers[1])))
return direction((flipDP(inputDirection.pointers[0]), inputDirection.pointers[1]))
# TODO FIX KEYERROR
def getNextPosition(startPosition: Tuple[int, int], directionPointer: int) -> Union[Tuple[int, int], KeyError]:
def getNextPosition(startPosition: position, directionPointer: int) -> Union[position, KeyError]:
"""
Finds next position along the direction pointer
:param startPosition: start position
:param directionPointer: direction pointer
:return: next position
"""
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))
return position((startPosition.coords[0] + 1, startPosition.coords[1]))
if directionPointer == 1:
return position((startPosition.coords[0], startPosition.coords[1] + 1))
if directionPointer == 2:
return position((startPosition.coords[0] - 1, startPosition.coords[1]))
if directionPointer == 3:
return position((startPosition.coords[0], startPosition.coords[1] - 1))
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]:
def getPreviousPosition(startPosition: position, directionPointer: int) -> position:
"""
Inverts the directionPointer, and finds the next position
:param startPosition: Input position
:param directionPointer: Input directionpointer
:return: Previous position
"""
if directionPointer == 0:
return getNextPosition(startPosition, 2)
elif directionPointer == 1:
if directionPointer == 1:
return getNextPosition(startPosition, 3)
elif directionPointer == 2:
if 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]:
def findEdge(inputCodel: codel, inputDirection: direction) -> Union[position, 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 inputCodel: 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]
dp = inputDirection.pointers[0]
cc = inputDirection.pointers[1]
if dp == 0:
edgePosition = max(codel, key=lambda lambdaPos: lambdaPos[0])
for pos in codel:
if pos[0] == edgePosition[0]:
edgePosition = max(inputCodel.codel, key=lambda lambdaPos: lambdaPos.coords[0])
for pos in inputCodel.codel:
if pos.coords[0] == edgePosition.coords[0]:
# -> ^ Right and up
if cc == 0 and pos[1] < edgePosition[1]:
if cc == 0 and pos.coords[1] < edgePosition.coords[1]:
edgePosition = pos
# -> V Right and down
elif cc == 1 and pos[1] > edgePosition[1]:
if cc == 1 and pos.coords[1] > edgePosition.coords[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]:
edgePosition = max(inputCodel.codel, key=lambda lambdaPos: lambdaPos.coords[1])
for pos in inputCodel.codel:
if pos.coords[1] == edgePosition.coords[1]:
# V -> Down and right
if cc == 0 and pos[0] > edgePosition[0]:
if cc == 0 and pos.coords[0] > edgePosition.coords[0]:
edgePosition = pos
# V <- Down and left
elif cc == 1 and pos[0] < edgePosition[0]:
elif cc == 1 and pos.coords[0] < edgePosition.coords[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]:
edgePosition = min(inputCodel.codel, key=lambda lambdaPos: lambdaPos.coords[0])
for pos in inputCodel.codel:
if pos.coords[0] == edgePosition.coords[0]:
# <- V Left and down
if cc == 0 and pos[1] > edgePosition[1]:
if cc == 0 and pos.coords[1] > edgePosition.coords[1]:
edgePosition = pos
# <- ^ left and up
elif cc == 1 and pos[1] < edgePosition[1]:
elif cc == 1 and pos.coords[1] < edgePosition.coords[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]:
edgePosition = min(inputCodel.codel, key=lambda lambdaPos: lambdaPos.coords[1])
for pos in inputCodel.codel:
if pos.coords[1] == edgePosition.coords[1]:
# ^ <- Up and left
if cc == 0 and pos[0] < edgePosition[0]:
if cc == 0 and pos.coords[0] < edgePosition.coords[0]:
edgePosition = pos
# ^ -> Up and right
elif cc == 1 and pos[0] > edgePosition[0]:
elif cc == 1 and pos.coords[0] > edgePosition.coords[0]:
edgePosition = pos
return edgePosition
else:

8177
interpreter/output2.txt Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,24 +0,0 @@
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))

View File

@ -1,277 +0,0 @@
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)

View File

@ -0,0 +1,380 @@
from typing import List, Tuple, Union
import copy
import interpreter.tokens as lexerTokens
import interpreter.movement as movement
import interpreter.errors as errors
from interpreter.dataStructures import direction
# TODO Nettere afhandeling errors (Union[Tuple[List[int], Tuple[int, int]], bool])
# TODO Test cases maken per token
def executeToken(token: lexerTokens.baseLexerToken, inputDirection: direction, dataStack: List[int]) -> Union[Tuple[direction, List[int]], BaseException]:
"""
Executes the function associated with tokens
:param token: Input token
:param inputDirection: Input direction
:param dataStack: Input stack
:return: Either a combination of a new stack and direction, or a runtime Exception
"""
if isinstance(token, lexerTokens.toBlackToken):
newPointers = movement.flip(inputDirection)
return (newPointers, dataStack)
if isinstance(token, lexerTokens.toWhiteToken):
return (inputDirection, dataStack)
if isinstance(token, lexerTokens.toColorToken):
return executeColorToken(token, inputDirection, dataStack)
if isinstance(token, lexerTokens.terminateToken):
return (inputDirection, dataStack)
return errors.UnknownTokenError("Token of type {} is unknown")
def executeColorToken(token: lexerTokens.toColorToken, inputDirection: direction, dataStack: List[int]) -> Union[Tuple[direction, List[int]], BaseException]:
"""
Executes the to color operations
:param token: input token
:param inputDirection: Input direction
:param dataStack: Input data stack
:return: either a combination of a new direction and data stack, or a runtime Exception
"""
if token.tokenType == "noop":
return noopOperator(inputDirection, dataStack)
elif token.tokenType == "push":
# Needs the codelsize to push
return pushOperator(token, inputDirection, dataStack)
elif token.tokenType == "pop":
return popOperator(inputDirection, dataStack)
elif token.tokenType == "add":
return addOperator(inputDirection, dataStack)
elif token.tokenType == "subtract":
return subtractOperator(inputDirection, dataStack)
elif token.tokenType == "multiply":
return multiplyOperator(inputDirection, dataStack)
elif token.tokenType == "divide":
return divideOperator(inputDirection, dataStack)
elif token.tokenType == "mod":
return modOperator(inputDirection, dataStack)
elif token.tokenType == "not":
return notOperator(inputDirection, dataStack)
elif token.tokenType == "greater":
return greaterOperator(inputDirection, dataStack)
elif token.tokenType == "pointer":
return pointerOperator(inputDirection, dataStack)
elif token.tokenType == "switch":
return switchOperator(inputDirection, dataStack)
elif token.tokenType == "duplicate":
return duplicateOperator(inputDirection, dataStack)
elif token.tokenType == "roll":
return rollOperator(inputDirection, dataStack)
elif token.tokenType == "inN":
return inNOperator(inputDirection, dataStack)
elif token.tokenType == "inC":
return inCOperator(inputDirection, dataStack)
elif token.tokenType == "outN":
return outNOperator(inputDirection, dataStack)
elif token.tokenType == "outC":
return outCOperator(inputDirection, dataStack)
else:
return errors.UnknownTokenError("Token {} not found".format(token.tokenType))
def noopOperator(inputDirection: direction, dataStack: List[int]) -> Tuple[direction, 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 (copy.deepcopy(inputDirection), dataStack.copy())
def addOperator(inputDirection: direction, dataStack: List[int]) -> Tuple[direction, 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 = dataStack.copy()
inputDirection = copy.deepcopy(inputDirection)
if len(newStack) < 2:
return (inputDirection, newStack)
newStack.append(newStack.pop() + newStack.pop())
return (inputDirection, newStack)
def subtractOperator(inputDirection: direction, dataStack: List[int]) -> Tuple[direction, List[int]]:
"""
Subtracts the second value from the first value of the stack
"""
newStack = dataStack.copy()
inputDirection = copy.deepcopy(inputDirection)
if len(newStack) < 2:
return (inputDirection, newStack)
first = newStack.pop()
second = newStack.pop()
newStack.append(second - first)
return (inputDirection, newStack)
def multiplyOperator(inputDirection: direction, dataStack: List[int]) -> Tuple[direction, List[int]]:
"""
Pops the first 2 values from the stack, and pushes the product of them
"""
newStack = list(dataStack)
inputDirection = copy.deepcopy(inputDirection)
if len(newStack) < 2:
return (inputDirection, newStack)
newStack.append(newStack.pop() * newStack.pop())
return (inputDirection, newStack)
def divideOperator(inputDirection: direction, dataStack: List[int]) -> Union[Tuple[direction, List[int]], BaseException]:
"""
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 = dataStack.copy()
inputDirection = copy.deepcopy(inputDirection)
if len(newStack) < 2:
return (inputDirection, newStack)
first = newStack.pop()
second = newStack.pop()
if second == 0:
return ZeroDivisionError("Division by zero {}/{}".format(first, second))
newStack.append(int(second / first))
return (inputDirection, newStack)
def modOperator(inputDirection: direction, dataStack: List[int]) -> Union[Tuple[direction, List[int]], BaseException]:
"""
Pops the first two values of the stack, mods the second value by the first value and pushes the result back to the stack
:param inputDirection:
:param dataStack:
:return: Tuple of direction and new data stack
"""
newStack = dataStack.copy()
inputDirection = copy.deepcopy(inputDirection)
if len(newStack) < 2:
return (inputDirection, newStack)
valA = newStack.pop()
valB = newStack.pop()
if valB == 0:
# TODO ERROR
return ZeroDivisionError("Second value is 0: {}%{}".format(valA, valB))
newStack.append(valB % valA)
return (inputDirection, newStack)
def greaterOperator(inputDirection: direction, dataStack: List[int]) -> Tuple[direction, 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 = dataStack.copy()
inputDirection = copy.deepcopy(inputDirection)
if len(newStack) < 2:
return (inputDirection, newStack)
valA = newStack.pop()
valB = newStack.pop()
newStack.append(int(valB > valA))
return (inputDirection, newStack)
def notOperator(inputDirection: direction, dataStack: List[int]) -> Tuple[direction, 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 = dataStack.copy()
inputDirection = copy.deepcopy(inputDirection)
if len(newStack) < 1:
return (inputDirection, newStack)
result = 1 if newStack.pop() == 0 else 0
newStack.append(result)
return (inputDirection, newStack)
def pointerOperator(inputDirection: direction, dataStack: List[int]) -> Tuple[direction, List[int]]:
"""
Pop the top value of the stack, and turn the direction pointer that many times. (counter clockwise if negative)
:param inputDirection:
:param dataStack:
:return:
"""
newStack = dataStack.copy()
inputDirection = copy.deepcopy(inputDirection)
if len(newStack) < 1:
return (inputDirection, newStack)
dp = inputDirection.pointers[0]
dpTurnCount = newStack.pop()
# Python module makes negative modulo's positive, so we need to manually flip the DP the required amount of times
if dpTurnCount < 0:
dp = movement.flipDPInvert(dp, dpTurnCount)
return (direction((dp, inputDirection.pointers[1])), newStack)
else:
# Cycle the DP forward by using the module operator
newDP = (inputDirection.pointers[0] + (dpTurnCount % 4)) % 4
return (direction((newDP, inputDirection.pointers[1])), newStack)
def switchOperator(inputDirection: direction, dataStack: List[int]) -> Tuple[direction, List[int]]:
"""
Pop the first value of the stack, and turn the codel chooser that many times.
:param pointers:
:param dataStack:
:return:
"""
newStack = dataStack.copy()
inputDirection = copy.deepcopy(inputDirection)
if len(newStack) < 1:
return (inputDirection, newStack)
ccTurnCount = abs(newStack.pop()) % 2
newCC = (inputDirection.pointers[1] + ccTurnCount) % 2
return (direction((inputDirection.pointers[0], newCC)), newStack)
def inNOperator(inputDirection: direction, dataStack: List[int]) -> Tuple[direction, List[int]]:
"""
Add a number from the input. If it isn't a number, nothing is added instead
"""
newStack = dataStack.copy()
inputDirection = copy.deepcopy(inputDirection)
newVal = input("Input number: ")
if newVal.isdigit():
newStack.append(newVal)
return (inputDirection, newStack)
def inCOperator(inputDirection: direction, dataStack: List[int]) -> Tuple[direction, List[int]]:
"""
Add a numeric representation of a character to the stack.
"""
newStack = dataStack.copy()
inputDirection = copy.deepcopy(inputDirection)
newVal = input("Input character")
if len(newVal) < 1:
return (inputDirection, newStack)
newStack.append(ord(newVal[0]))
return (inputDirection, newStack)
def outNOperator(inputDirection: direction, dataStack: List[int]) -> Tuple[direction, List[int]]:
"""
Pops the top number from the stack and outputs it as a number
"""
newStack = dataStack.copy()
inputDirection = copy.deepcopy(inputDirection)
if len(newStack) < 1:
return (inputDirection, newStack)
print(newStack.pop(), end="")
return (inputDirection, newStack)
def outCOperator(inputDirection: direction, dataStack: List[int]) -> Tuple[direction, List[int]]:
"""
Pops the top number from the stack and outputs it as a number. Does nothing if top value is negative
"""
newStack = dataStack.copy()
inputDirection = copy.deepcopy(inputDirection)
if len(newStack) < 1:
return (inputDirection, newStack)
valA = newStack.pop()
if valA < 0:
newStack.append(valA)
return (inputDirection, newStack)
print(chr(valA), end="")
return (inputDirection, newStack)
def pushOperator(token: lexerTokens.toColorToken, inputDirection: direction, dataStack: List[int]) -> Tuple[direction, List[int]]:
"""
Pushes the codelsize of the token to the stack
"""
newStack = dataStack.copy()
inputDirection = copy.deepcopy(inputDirection)
newStack.append(token.codelSize)
return (inputDirection, newStack)
def popOperator(inputDirection: direction, dataStack: List[int]) -> Tuple[direction, List[int]]:
"""
Pops and discards the top number of the stack
"""
newStack = dataStack.copy()
inputDirection = copy.deepcopy(inputDirection)
if len(newStack) < 1:
return (inputDirection, newStack)
newStack.pop()
return (inputDirection, newStack)
def duplicateOperator(inputDirection: direction, dataStack: List[int]) -> Tuple[direction, List[int]]:
"""
Duplicates the top value of the stack
"""
newStack = dataStack.copy()
inputDirection = copy.deepcopy(inputDirection)
if len(newStack) < 1:
return (inputDirection, newStack)
val = newStack.pop()
newStack.append(val)
newStack.append(val)
return (inputDirection, newStack)
def rollOperator(inputDirection: direction, dataStack: List[int]) -> Tuple[direction, List[int]]:
"""
Rolls the stack x times, to a depth of y, where x is equal to the top value of the stack, and y is equal to the second value of the stack
"""
newStack = dataStack.copy()
inputDirection = copy.deepcopy(inputDirection)
if len(newStack) < 2:
return (inputDirection, newStack)
rolls = newStack.pop()
depth = newStack.pop()
insertIndex = len(newStack) - depth
newStack = rollStack(newStack, rolls, insertIndex)
return (inputDirection, newStack)
def rollStack(dataStack: List[int], numberOfRolls: int, insertIndex: int) -> List[int]:
"""
Rolls the stack recursively, and inverted when negative number of rolls
:param dataStack: Input stack
:param numberOfRolls: Number of rolls
:param insertIndex: At which index to either insert new values, or to get values from
:return: Rolled data stack
"""
newStack = dataStack.copy()
if numberOfRolls > 0:
newStack.insert(insertIndex, newStack.pop())
return rollStack(newStack, numberOfRolls - 1, insertIndex)
elif numberOfRolls < 0:
newStack.append(newStack.pop(insertIndex))
return rollStack(newStack, numberOfRolls + 1, insertIndex)
else:
return newStack

45
interpreter/tokens.py Normal file
View File

@ -0,0 +1,45 @@
class baseLexerToken():
def __init__(self, tokenType: str):
self.tokenType = tokenType
def __str__(self):
return "Token type = {}".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 getTokenType(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]

24
main.py Normal file
View File

@ -0,0 +1,24 @@
import argparse
from interpreter import executionFunctions as executionFunctions
from interpreter import imageWrapper as imageWrapper
from GUI import main as GUIMain
parser = argparse.ArgumentParser(description='Interprets a piet image')
parser.add_argument("-f", "--file", required=True, type=str, help="complete filepath to a .png or .gif image")
parser.add_argument("-v", "--verbose", action="store_true", help="Outputs number of steps to STDOUT")
parser.add_argument("-g", "--graphical", action="store_true", help="Opens GUI with the file loaded")
args = parser.parse_args()
if not args.graphical:
executionFunctions.interpret(imageWrapper.getImage(args.file))
if args.verbose:
print("\nTotal steps: {}".format(executionFunctions.takeStep.counter))
else:
print("GUI TIME!")
app = GUIMain.GUI()
app.setFileText(args.file)
app.loadFile()
app.run()