378 lines
14 KiB
Python
378 lines
14 KiB
Python
from typing import List, Tuple, Union
|
|
import copy
|
|
|
|
from interpreter import tokens as lexerTokens
|
|
from interpreter import movementFunctions as movement
|
|
from interpreter import errors as errors
|
|
from interpreter.dataStructures import direction
|
|
|
|
|
|
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()
|
|
newDirection = copy.deepcopy(inputDirection)
|
|
if len(newStack) < 2:
|
|
return (newDirection, newStack)
|
|
|
|
first = newStack.pop()
|
|
second = newStack.pop()
|
|
if second == 0:
|
|
return ZeroDivisionError("Division by zero {}/{}".format(first, second))
|
|
newStack.append(int(second / first))
|
|
return (newDirection, 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:
|
|
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
|