113 lines
4.1 KiB
Python
113 lines
4.1 KiB
Python
import copy
|
|
from typing import Union, List, Callable
|
|
|
|
import numpy as np
|
|
|
|
from interpreter import imageFunctions as imageWrapper
|
|
from interpreter import lexer as lexer
|
|
from interpreter import tokens as tokens
|
|
from interpreter import movementFunctions 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]
|
|
|
|
# This is the default programState.
|
|
startPosition = position((0, 0))
|
|
pointers = direction((0, 0))
|
|
PS = programState(graph[0], startPosition, pointers)
|
|
|
|
result = runProgram(image, PS)
|
|
# Check if executed step had an error
|
|
if isinstance(result, BaseException):
|
|
print("The following exception 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[[np.ndarray, programState], programState]) -> Callable[[np.ndarray, programState], programState]:
|
|
"""
|
|
A decorator function to count the steps taken in the program
|
|
:param f: original function to call
|
|
:return: A decorated function
|
|
"""
|
|
def inner(image: np.ndarray, PS: programState) -> 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)
|
|
|
|
# Add additional information to the error message (Position and direction)
|
|
if isinstance(result, BaseException):
|
|
return type(result)("{}, at position {}, direction {}".format(result.args[0], edgePosition,newState.direction))
|
|
# 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, but the position shouldn't move
|
|
if isinstance(newToken, (tokens.toWhiteToken, tokens.toColorToken)):
|
|
newState.position = movement.getNextPosition(edgePosition, newState.direction.pointers[0])
|
|
|
|
# Use the new direction and stack for the next step
|
|
newState.direction = result[0]
|
|
newState.dataStack = result[1]
|
|
|
|
return newState
|