It works
This commit is contained in:
parent
0576145b10
commit
afa67b21ac
BIN
GUI/assets/play_button.png
Normal file
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
BIN
GUI/assets/play_button.xcf
Normal file
Binary file not shown.
@ -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)
|
@ -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)
|
@ -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
|
@ -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()
|
@ -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!")
|
@ -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
BIN
Info/poster.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 520 KiB |
BIN
Info/poster.xcf
Normal file
BIN
Info/poster.xcf
Normal file
Binary file not shown.
BIN
interpreter/__init__.pyc
Normal file
BIN
interpreter/__init__.pyc
Normal file
Binary file not shown.
@ -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
|
||||
|
145
interpreter/dataStructures.py
Normal file
145
interpreter/dataStructures.py
Normal 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
13
interpreter/errors.py
Normal 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"""
|
110
interpreter/executionFunctions.py
Normal file
110
interpreter/executionFunctions.py
Normal 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)
|
44
interpreter/helperFunctions.py
Normal file
44
interpreter/helperFunctions.py
Normal 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))
|
@ -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))
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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])))
|
@ -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()
|
@ -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
8177
interpreter/output2.txt
Normal file
File diff suppressed because it is too large
Load Diff
@ -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))
|
@ -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)
|
380
interpreter/tokenFunctions.py
Normal file
380
interpreter/tokenFunctions.py
Normal 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
45
interpreter/tokens.py
Normal 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
24
main.py
Normal 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()
|
Loading…
x
Reference in New Issue
Block a user