Piet_interpreter/interpreter/tokenFunctions.py
2020-05-06 22:42:04 +02:00

381 lines
14 KiB
Python

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