def __init__(self, tree): """ Parser invoker. :param tree: tree of the XML structure. """ self.handler = ErrorHandler() root = self.__checkRootNode(tree.documentElement) self.__checkNodes(root.childNodes)
def __init__(self, frame, name, value, varType): """ Initializes a variable. :param frame: Variable frame :param name: Variable name :param value: Variable value :param varType: Variable type """ self.handler = ErrorHandler() self.frame = frame self.name = name self.value = value self.type = varType
class Labels: def __init__(self): self.handler = ErrorHandler() self.registry = dict() def register(self, name, order): if self.has(name): self.handler.terminateProgram(52, 'Label already exists.') self.registry[name] = order def has(self, name): for label in self.registry: if label == name: return True return False def getOrder(self, name): if not self.has(name): self.handler.terminateProgram(52, 'Label does not exist.') return self.registry[name] def getAll(self): return self.registry
class Instruction: handler = ErrorHandler() def __init__(self): """ Initialize Instruction """ self.order = None self.opcode = None self.args = list() def setOrder(self, order: str): """ Sets the order of instruction. :param order: Order of instruction """ self.order = int(order) def setOpcode(self, opcode: str): """ Sets the opcode of instruction. :param opcode: Opcode of instruction """ self.opcode = opcode def setArg(self, argType: str, value: str): """ Sets the argument of instruction. :param argType: Argument type :param value: Argument value """ self.args.append(Argument(argType, value)) def getArg(self, index: int): """ Gets the argument of instruction. :param index: Argument index """ return self.args[index] def isLabel(self): """ Checks whether instruction is a LABEL. """ return True if self.opcode == 'LABEL' else False
class Argument(ArgumentInterface): handler = ErrorHandler() def __init__(self, argType: str, value: str): """ Initializes the argument :param argType: Argument type :param value: Argument value """ self.type = argType if len(value) > 2 and value[2] == '@': self.frame = value[:2] self.value = value[3:] else: self.frame = None self.value = value
class Storage: handler = ErrorHandler() def __init__(self): """ Initializes a storage. """ self.frames = Frames() self.stack = Stack() self.labels = Labels() self.calls = Stack() def statement(self): """ Returns the statement of Storage. :return: Statement of storage in string. """ string = "Global Frame:\n" + self.frames.get('global').statement() + "\n" \ "Temp Frame:\n" + self.frames.get('temp', True).statement() + "\n" \ "Local Frame:\n" + self.frames.get('local', True).statement() + "\n" \ "Stack:\n" + self.stack.statement() + "\n" \ "Call Stack:\n" + self.calls.statement(True) + "" return string
class StackInterface: handler = ErrorHandler() def __init__(self): self.registry = list() def push(self, item): """ Push item into a stack. :param item: Item that is pushed into a stack """ self.registry.append(item) def pop(self): """ Pops item from a stack. :return: Item popped from a stack. """ if len(self.registry) > 0: return self.registry.pop(-1) else: self.handler.terminateProgram(56, 'Can not return - stack is empty.')
class Interpret: handler = ErrorHandler() def __init__(self, sourceFile, inputFile): """ Initializes the interpret :param sourceFile: XML source file of IPPcode21 :param inputFile: Input file with defined inputs """ # Get Nodes tree = self.__getNodes(sourceFile) # Run parser Parser(tree) # Initialize storage self.storage = Storage() # Initialize instructions self.instructions, self.ordersList = self.__collectInstructions(tree) # Initialize inputs self.inputs = self.__getInputs(inputFile) self.inputsFlag = True if self.inputs is not None else False # Program counter self.counter = 0 # End if no instructions provided. if len(self.instructions) == 0: self.handler.terminateProgram( 0, 'Interpretation done (no instructions set).') # Execute the code self.order = self.ordersList[0] self.execute(self.__getInstructionAt(self.order)) def execute(self, instruction: Instruction): """ Executes an instruction. :param instruction: Instruction to be executed. """ # Increment counter self.counter += 1 # Set instruction to error handler self.handler.instruction = instruction if instruction.opcode == 'MOVE': # MOVE <var> <symb> var = self.__checkVariable(instruction.getArg(0), False) symb = self.__checkVariable(instruction.getArg(1)) if symb.value is None: self.handler.terminateInterpret( 56, 'Uninitialized variable <symb>.') self.storage.frames.updateVar(var, symb.value, symb.type) elif instruction.opcode == 'CREATEFRAME': # CREATEFRAME self.storage.frames.create('temp') elif instruction.opcode == 'PUSHFRAME': # PUSHFRAME self.storage.frames.create('local') elif instruction.opcode == 'POPFRAME': # POPFRAME self.storage.frames.pop() elif instruction.opcode == 'DEFVAR': # DEFVAR <var> var = instruction.getArg(0) self.storage.frames.registerVar(var) elif instruction.opcode == 'CALL': # LABEL <label> label = instruction.getArg(0) self.storage.calls.push(instruction) self.order = self.storage.labels.getOrder(label.value) self.execute(self.__getInstructionAt(self.order)) elif instruction.opcode == 'RETURN': # RETURN order = self.storage.calls.pop().order self.execute(self.__getInstructionAt(order, True)) elif instruction.opcode == 'PUSHS': # PUSHS <symb> symb = self.__checkVariable(instruction.getArg(0)) self.storage.stack.push({'value': symb.value, 'type': symb.type}) elif instruction.opcode == 'POPS': # POPS <var> var = self.__checkVariable(instruction.getArg(0), False) item = self.storage.stack.pop() self.storage.frames.updateVar(var, item.get('value'), item.get('type')) elif instruction.opcode == 'ADD': # ADD <var> <symb1> <symb2> var, symb1, symb2 = self.__initializeArithmeticOperation( instruction) self.storage.frames.updateVar(var, int(symb1.value) + int(symb2.value), 'int') elif instruction.opcode == 'SUB': # SUB <var> <symb1> <symb2> var, symb1, symb2 = self.__initializeArithmeticOperation( instruction) self.storage.frames.updateVar(var, int(symb1.value) - int(symb2.value), 'int') elif instruction.opcode == 'MUL': # MUL <var> <symb1> <symb2> var, symb1, symb2 = self.__initializeArithmeticOperation( instruction) self.storage.frames.updateVar(var, int(symb1.value) * int(symb2.value), 'int') elif instruction.opcode == 'IDIV': # IDIV <var> <symb1> <symb2> var, symb1, symb2 = self.__initializeArithmeticOperation( instruction) if int(symb2.value) == 0: self.handler.terminateProgram(57, 'Division by zero.') self.storage.frames.updateVar(var, int(symb1.value) / int(symb2.value), 'int') elif instruction.opcode == 'LT': # LT <var> <symb1> <symb2> var, symb1, symb2 = self.__initializeRelationOperation(instruction) value = 'true' if symb1.value < symb2.value else 'false' self.storage.frames.updateVar(var, value, 'bool') elif instruction.opcode == 'GT': # GT <var> <symb1> <symb2> var, symb1, symb2 = self.__initializeRelationOperation(instruction) value = 'true' if symb1.value > symb2.value else 'false' self.storage.frames.updateVar(var, value, 'bool') elif instruction.opcode == 'EQ': # EQ <var> <symb1> <symb2> var, symb1, symb2 = self.__initializeRelationOperation(instruction) value = 'true' if symb1.value == symb2.value else 'false' self.storage.frames.updateVar(var, value, 'bool') elif instruction.opcode == 'AND': # AND <var> <symb1> <symb2> var, symb1, symb2 = self.__initializeBooleanOperation(instruction) value = 'true' if symb1 & symb2 else 'false' self.storage.frames.updateVar(var, value, 'bool') elif instruction.opcode == 'OR': # OR <var> <symb1> <symb2> var, symb1, symb2 = self.__initializeBooleanOperation(instruction) value = 'true' if symb1 | symb2 else 'false' self.storage.frames.updateVar(var, value, 'bool') elif instruction.opcode == 'NOT': # NOT <var> <symb> var, symb = self.__initializeBooleanOperation(instruction) value = 'false' if symb else 'true' self.storage.frames.updateVar(var, value, 'bool') elif instruction.opcode == 'INT2CHAR': # INT2CHAR <var> <symb> var = self.__checkVariable(instruction.getArg(0), False) symb = self.__checkVariable(instruction.getArg(1)) if not symb.isInt(): self.handler.terminateInterpret( 53, 'Int is expected as second parameter.') try: self.storage.frames.updateVar(var, chr(int(symb.value)), 'string') except ValueError: self.handler.terminateInterpret( 58, 'Value of second parameter is out of range.') elif instruction.opcode == 'STRI2INT': # STRI2INT <var> <symb1> <symb2> var = self.__checkVariable(instruction.getArg(0), False) symb1 = self.__checkVariable(instruction.getArg(1)) symb2 = self.__checkVariable(instruction.getArg(2)) if not symb1.isString() or not symb2.isInt(): self.handler.terminateInterpret( 53, 'Params error (<got:expected>) ' '<' + symb1.type + ':string> <' + symb2.type + ':int>') index = int(symb2.value) if index >= len(symb1.value) or index < 0: self.handler.terminateInterpret(58, 'Index is out of range.') self.storage.frames.updateVar(var, int(ord(symb1.value[index])), 'int') elif instruction.opcode == 'READ': # READ <var> <type> var = self.__checkVariable(instruction.getArg(0), False) readType = instruction.getArg(1).value if self.inputsFlag: if len(self.inputs) > 0: read = self.inputs.pop(0) else: read = '' else: read = input() if len(read) == 0: self.storage.frames.updateVar(var, 'nil', 'nil') elif readType == 'int': read = read.lstrip().rstrip() if read.lstrip('-').isdigit(): self.storage.frames.updateVar(var, int(read), 'int') else: self.storage.frames.updateVar(var, 'nil', 'nil') elif readType == 'string': read = read.lstrip().rstrip() self.storage.frames.updateVar(var, str(read), 'string') elif readType == 'bool': read = read.lstrip().rstrip() if read.lower() == 'true': self.storage.frames.updateVar(var, 'true', 'bool') else: self.storage.frames.updateVar(var, 'false', 'bool') elif instruction.opcode == 'WRITE': # WRITE <symb> symb = self.__checkVariable(instruction.getArg(0)) if symb.isNil(): print() elif symb.isBool(): print(symb.value) elif symb.isInt(): print(int(symb.value), end='') else: symb.value = str(symb.value) escapes = re.findall(r'\\[0-9]{3}', symb.value) for escape in escapes: symb.value = symb.value.replace( escape, chr(int(escape.lstrip('\\').rstrip()))) print(symb.value, end='') elif instruction.opcode == 'CONCAT': # CONCAT <var> <symb1> <symb2> var = self.__checkVariable(instruction.getArg(0), False) symb1 = self.__checkVariable(instruction.getArg(1)) symb2 = self.__checkVariable(instruction.getArg(2)) if not symb1.isString() or not symb2.isString(): self.handler.terminateInterpret( 53, 'Can concatenate only strings.') self.storage.frames.updateVar(var, str(symb1.value) + str(symb2.value), 'string') elif instruction.opcode == 'STRLEN': # STRLEN <var> <symb> var = self.__checkVariable(instruction.getArg(0), False) symb = self.__checkVariable(instruction.getArg(1)) if not symb.isString(): self.handler.terminateInterpret( 53, 'Second parameter is not a string.') self.storage.frames.updateVar(var, len(symb.value), 'int') elif instruction.opcode == 'GETCHAR': # GETCHAR <var> <symb1> <symb2> var = self.__checkVariable(instruction.getArg(0), False) symb1 = self.__checkVariable(instruction.getArg(1)) symb2 = self.__checkVariable(instruction.getArg(2)) if not symb1.isString() or not symb2.isInt(): self.handler.terminateInterpret( 53, 'Params error (<got:expected>) ' '<' + symb1.type + ':string> <' + symb2.type + ':int>') index = int(symb2.value) if index >= len(symb1.value) or index < 0: self.handler.terminateInterpret(58, 'Index is out of range.') self.storage.frames.updateVar(var, symb1.value[index], 'string') elif instruction.opcode == 'SETCHAR': # SETCHAR <var> <symb1> <symb2> var = self.__checkVariable(instruction.getArg(0)) symb1 = self.__checkVariable(instruction.getArg(1)) symb2 = self.__checkVariable(instruction.getArg(2)) if not var.isString() or not symb1.isInt() or not symb2.isString(): self.handler.terminateInterpret( 53, 'Params error (<got:expected>) ' '<' + var.type + ':string> ' '<' + symb1.type + ':string> <' + symb2.type + ':int>') index = int(symb1.value) if index >= len(var.value) or index < 0 or not len(symb2.value): self.handler.terminateInterpret( 58, 'Index is out of range or third parameter is empty.') var.value = var.value[0:index] + symb2.value[0] + var.value[index + 1:] self.storage.frames.updateVar(var, var.value, 'string') elif instruction.opcode == 'TYPE': # TYPE <var> <symb> var = self.__checkVariable(instruction.getArg(0), False) symb = self.__checkVariable(instruction.getArg(1), False) if symb.isInitialized(): self.storage.frames.updateVar(var, symb.type, 'string') else: self.storage.frames.updateVar(var, '', 'string') elif instruction.opcode == 'LABEL': # LABEL <label> pass elif instruction.opcode == 'JUMP': # JUMP <label> label = instruction.getArg(0) self.order = self.storage.labels.getOrder(label.value) self.execute(self.__getInstructionAt(self.order)) elif (instruction.opcode == 'JUMPIFEQ' or instruction.opcode == 'JUMPIFNEQ'): # JUMPIF(N)EQ <label> <symb1> <symb2> label = instruction.getArg(0) symb1 = self.__checkVariable(instruction.getArg(1)) symb2 = self.__checkVariable(instruction.getArg(2)) if not self.storage.labels.has(label.value): self.handler.terminateInterpret(52, 'Label does not exists.') if not symb1.isNil() and not symb2.isNil( ) and symb1.type != symb2.type: self.handler.terminateInterpret( 53, "Types does not match or symbols are not 'nil'.") if ((instruction.opcode == 'JUMPIFEQ' and symb1.value == symb2.value) or (instruction.opcode == 'JUMPIFNEQ' and symb1.value != symb2.value)): order = self.storage.labels.getOrder(label.value) self.execute(self.__getInstructionAt(order, True)) elif instruction.opcode == 'EXIT': # EXIT <symb> symb = self.__checkVariable(instruction.getArg(0)) if not symb.isInt(): self.handler.terminateInterpret(53, 'Excepted int.') symb.value = int(symb.value) if not (0 <= symb.value <= 49): self.handler.terminateInterpret( 57, 'Invalid exit code value (excepted range: 0-49).') self.handler.terminateProgram(symb.value, 'Terminated by EXIT instruction.') elif instruction.opcode == 'DPRINT': # DPRINT <symb> symb = self.__checkVariable(instruction.getArg(0)) print(symb.value, file=sys.stderr) elif instruction.opcode == 'BREAK': # BREAK stats = "Executions: " + str(self.counter) + '\n' \ "Current order: " + str(self.order + 1) + '\n' \ "========== Storage ==========\n" + str(self.storage.statement()) + '' \ "=============================" print(stats, file=sys.stderr) # Execute next instruction self.execute(self.__getInstructionAt(self.order, True)) def __getNodes(self, source): """ Gets nodes from XML source file. :param source: XML source file :return: XML Object that refers to the root of XML file. """ try: tree = minidom.parse(source) return tree except Exception as exception: self.handler.terminateProgram(31, 'XML Error: ' + str(exception)) @staticmethod def __getInputs(source) -> list or None: """ Collect inputs into a list. :param source: Source file of inputs :return: List of input data or None if non provided. """ inputs = None if not source: return inputs inputs = list() if isinstance(source, io.TextIOWrapper): for line in source: inputs.append(line) return inputs file = open(source) for line in file: inputs.append(line) return inputs def __collectInstructions(self, tree) -> tuple: """ Initializes instructions into a collection. :param tree: XML tree from source file :return: List of instructions sorted by order. """ collection = list() instructions = tree.getElementsByTagName('instruction') for instruction in instructions: arguments = instruction.childNodes collection.append( self.__registerInstruction(instruction, arguments)) # Sort by order collection.sort(key=lambda x: x.order) # Check for duplicated orders and create orders list ordersList = list() for instruction in collection: if instruction.order in ordersList: self.handler.terminateProgram(32, 'Order duplication.') ordersList.append(instruction.order) return collection, ordersList def __registerInstruction(self, instructionNode, argNodes) -> Instruction: """ Register an instruction. :param instructionNode: <instruction> node :param argNodes: <argX> node :return: Instruction instance """ instruction = Instruction() instruction.setOrder(instructionNode.getAttribute('order')) instruction.setOpcode(instructionNode.getAttribute('opcode').upper()) # Sort arguments argNodes = [x for x in argNodes if x.nodeType == 1] argNodes.sort(key=lambda x: x.tagName) # Set arguments for argument in argNodes: if argument.nodeType == 1: # Get only elements instruction.setArg( argument.getAttribute('type'), argument.childNodes[0].nodeValue if argument.hasChildNodes() else '') # Register labels if instruction.isLabel(): if self.storage.labels.has(instruction): self.handler.terminateProgram(52, 'This label is already set.') self.storage.labels.register( instruction.getArg(0).value, instruction.order) return instruction def __getInstructionAt(self, order, nextInstruction=False) -> Instruction: """ Gets an instruction at orderList's index. :param order: The order of instruction :return: Instance of instruction """ index = None for i in range(len(self.ordersList)): if order == self.ordersList[i]: index = i break # Instruction not found if index is None: self.handler.terminateProgram( 99, 'Instruction at order ' + str(index) + ' not found.') # If returned find next order if nextInstruction: index += 1 # If no next index that means we are done. if index >= len(self.instructions): self.handler.terminateProgram(0, 'Interpret done.') self.order = self.instructions[index].order return self.instructions[index] def __checkVariable(self, var: Argument, initRequired=True) -> Argument or Variable: """ Checks a variable. :param var: Variable that is checked :return: If it is a variable return variable from storage otherwise return it back. """ if var.isVar() and not self.storage.frames.has(var.frame): self.handler.terminateInterpret( 55, 'Frame ' + var.frame + ' is not set') if var.isVar() and not self.storage.frames.hasVar(var): self.handler.terminateInterpret( 54, 'Undefined variable <' + var.value + ':' + var.type + '>') if var.isVar(): variable = self.storage.frames.getVar(var) if initRequired and not variable.isInitialized(): self.handler.terminateInterpret( 56, "Variable '" + var.value + "' is not initialized") return variable if var.isInt(): var.value = int(var.value) return var if var.isString(): # If string parse regex var.value = self.__stringEscapesCheck(var.value) return var @staticmethod def __stringEscapesCheck(string): """ Checks string for escape characters. :param string: The checked string :return: String with removed escape sequences. """ string = str(string) escapes = re.findall(r'\\[0-9]{3}', string) for escape in escapes: string = string.replace(escape, chr(int(escape.lstrip('\\')))) return string def __initializeArithmeticOperation(self, instruction: Instruction) -> tuple: """ Initialize arguments for arithmetic operation. :param instruction: Arithmetic instruction :return: Initialized operands for arithmetic operations. """ var = self.__checkVariable(instruction.getArg(0), False) symb1 = self.__checkVariable(instruction.getArg(1)) symb2 = self.__checkVariable(instruction.getArg(2)) if not symb1.isInt() or not symb2.isInt(): self.handler.terminateProgram(53, 'Int expected') return var, symb1, symb2 def __initializeRelationOperation(self, instruction: Instruction) -> tuple: """ Initialize arguments for relation operation. :param instruction: Relation instruction :return: Initialized operands for relation operations. """ var = self.__checkVariable(instruction.getArg(0), False) symb1 = self.__checkVariable(instruction.getArg(1)) symb2 = self.__checkVariable(instruction.getArg(2)) if symb1.type != symb2.type or (symb1.isNil() or symb2.isNil()): self.handler.terminateInterpret( 53, 'Types of operands do not match ' + symb1.type + ' != ' + symb2.type) if not symb1.isRelationValid() or not symb2.isRelationValid(): self.handler.terminateInterpret( 53, 'Not valid types <' + symb1.type + ':symb1> <' + symb2.type + ':symb2>') return var, symb1, symb2 def __initializeBooleanOperation(self, instruction: Instruction) -> tuple: """ Initialize arguments for boolean operation. :param instruction: Boolean instruction :return: Initialized operands for boolean operations. """ var = self.__checkVariable(instruction.getArg(0), False) symb1 = self.__checkVariable(instruction.getArg(1)) symb2 = self.__checkVariable( instruction.getArg(2)) if len(instruction.args) > 2 else None if not symb1.isBool() or (symb2 and not symb2.isBool()): self.handler.terminateProgram(53, 'Expected Boolean.') if symb2: symb1 = True if symb1.value == 'true' else False symb2 = True if symb2.value == 'true' else False return var, symb1, symb2 else: symb1 = True if symb1.value == 'true' else False return var, symb1
class App: handler = ErrorHandler() def __init__(self): """ Initialize error handler and argument handler. """ self.programName = "" self.Argument = Argument() def registerArguments(self, arguments: list): """ Adds arguments into registry. :param arguments: Arguments passed into program """ for argument in arguments: self.Argument.register(argument) def listen(self, arguments: list): """ Listen for arguments. :param arguments: The entered arguments """ self.programName = arguments.pop(0) self.parseArguments(arguments) def runInterpret(self): """ Runs the interpret """ if self.Argument.isSet('source'): sourceFile = self.Argument.getPath('source') if not self.Argument.isValidPath(sourceFile): self.handler.terminateProgram(11, 'File ' + sourceFile + ' is invalid.') else: sourceFile = sys.stdin if self.Argument.isSet('input'): inputFile = self.Argument.getPath('input') if not self.Argument.isValidPath(inputFile): self.handler.terminateProgram(11, 'File ' + inputFile + ' is invalid.') else: inputFile = sys.stdin Interpret(sourceFile, inputFile) def parseArguments(self, arguments: list): """ Parse entered arguments. :param arguments: Arguments to parse """ # Check if at least one argument is set if len(arguments) == 0: self.handler.terminateProgram(10, "At least one argument is required.") # Loop through arguments for argument in arguments: # Check if its valid argument if not self.Argument.isValid(argument): self.handler.terminateProgram(10, "Argument: " + argument + " is invalid.") # Check if argument is --help if self.Argument.isHelp(argument): if len(arguments) > 1: self.handler.terminateProgram(10, "Can not use more arguments with --help argument.") self.printHelp() self.Argument.add(argument) def printHelp(self): """ Prints help """ print("Interpret for XML representation of IPPcode21") print("Usage: py " + self.programName + " [--help] OPTIONS") print("Arguments:") print("\t--help\tDisplay this help and exit.") print("OPTIONS:") print("\t--source=file\tInput file with XML representation of IPPcode21.") print("\t--input=file\tFile with inputs for the interpretation of the entered source code.") self.handler.terminateProgram(0) def terminate(self): """ Terminate application """ self.handler.terminateProgram(0, 'Interpretation is done.')
class Argument: handler = ErrorHandler() def __init__(self): """ Initialize allowed arguments and arguments passed by program. """ self.allowedArguments = ["--help"] self.arguments = [] def register(self, argument: str): """ Add argument into registry of allowed arguments. :param argument: The entered argument """ self.allowedArguments.append(argument) def add(self, argument: str): """ Add argument into registry. :param argument: The entered argument """ self.arguments.append(argument) def isValid(self, argument: str) -> bool: """ Checks if argument is valid. :param argument: The entered argument :return: True if it is a valid argument otherwise false. """ for allowedArgument in self.allowedArguments: if self.getReal(argument) == self.getReal(allowedArgument): return True return False def isHelp(self, argument: str) -> bool: """ Checks if argument is --help argument. :param argument: The entered argument :return: True if argument is help otherwise false. """ if self.getReal(argument) == "help": return True return False def isSet(self, name: str) -> bool: """ Checks if argument is set. :param name: Name of argument :return: True if set otherwise false. """ for argument in self.arguments: if self.getReal(argument) == name: return True return False @staticmethod def getReal(argument: str): """ Checks if argument is --help argument. :param argument: The entered argument """ # Remove dashes argument = argument[2:] # Find equal sign eqPos = argument.find('=') if eqPos != -1: return argument[:eqPos] else: return argument def getPath(self, name: str) -> str: """ Gets path from argument :param name: Name of argument :return: Path of argument or Internal Error if fail """ # Found argument found = name # Find equal sign for argument in self.arguments: if self.getReal(argument) == name: found = argument eqPos = found.find('=') if eqPos == -1: self.handler.terminateProgram(99, 'This argument does not have path.') return found[(eqPos + 1):] @staticmethod def isValidPath(file: str) -> bool: """ Checks path if exists :param file: The checked path :return: True if path exists otherwise false. """ if path.exists(file): return True return False
class Variables: handler = ErrorHandler() def __init__(self): """ Initializes a variable registry. """ self.registry = list() def register(self, arg: Argument) -> Variable: """ Register a variable into registry. :param arg: Argument that is registered. :return: Variable object. """ if self.has(arg.value): self.handler.terminateProgram( 52, "Variable '" + arg.value + "' already exists.") variable = Variable(arg.frame, arg.value, None, None) self.registry.append(variable) return variable def has(self, name: str) -> bool: """ Check if variables has a variable. :param name: Name of variable. :return: True if variable found otherwise false. """ for item in self.registry: if name == item.name: return True return False def get(self, name: str) -> Variable: """ Gets a variable from variables. :param name: Name of variable. :return: Found variable object. """ for item in self.registry: if item.name == name: return item self.handler.terminateProgram(52, "Variable '" + name + "' is not set") def getAll(self) -> list: """ Registry of variables. :return: Registry of variables. """ return self.registry def update(self, var: Variable, value: str, varType: str): """ Updates a variable. :param var: Variable that is updated :param value: New value of variable :param varType: New type of variable """ for index in range(len(self.registry)): if self.registry[index].name == var.name: self.registry[index].setValue(value) self.registry[index].setType(varType) return self.handler.terminateProgram(52, "Variable '" + var.name + "' is not set") def clone(self, variables): """ Clones a variable into this registry. :param variables: Variables that are cloned """ self.registry = variables.registry def statement(self) -> str: """ Prints a statement of variables. :return: Statement of variables as string. """ string = "" for item in self.registry: string += '<' + str(item.type) + '>' + str(item.name) + '=' + str( item.value) + '\n' return string
def __init__(self): self.handler = ErrorHandler() self.registry = dict()
class Frames: handler = ErrorHandler() def __init__(self): """ Initializes interpret frames. """ self.__global = Variables() self.__temp = None self.__locals = list() self.__nesting = -1 def create(self, frameType: str): """ Creates a new frame. :param frameType: Frame type """ if frameType == 'local': if self.__temp is None: self.handler.terminateProgram( 55, 'Accessing to non-defined temporary frame.') variables = Variables() variables.clone(self.__temp) self.__locals.append(variables) self.__nesting += 1 for variable in self.get('local').getAll(): variable.frame = 'LF' self.__temp = None if frameType == 'temp': self.__temp = Variables() def get(self, frameType: str, statement=False) -> Variables or None: """ Gets requested frame. :param frameType: Frame type :param statement: If a statement is called create new instance for local/temp frame if None. :return: Requested frame. """ if frameType == 'global' or frameType == 'GF': return self.__global if frameType == 'temp' or frameType == 'TF': if statement and self.__temp is None: return Variables() return self.__temp if frameType == 'local' or frameType == 'LF': if statement: return Variables() if self.__nesting >= 0: return self.__locals[self.__nesting] return None def pop(self): """ Pops local frame into a temporary frame. """ if self.__nesting != -1 and self.__nesting < len(self.__locals): self.__temp = self.__locals[self.__nesting] self.__nesting -= 1 else: self.handler.terminateProgram( 55, 'Accessing to non-existing local frame.') def has(self, frameType): """ Checks whether frame exists. :param frameType: Type of frame :return: """ if frameType == 'GF' or frameType == 'global': return True frame = self.get(frameType) if isinstance(frame, Variables): return True if isinstance(frame, list): return True return True def registerVar(self, var: Argument) -> Variable: """ Registers a variable into a frame. :param var: Variable that holds required parameters :return: Registered Variable. """ self.__checkFrame(var.frame) return self.get(var.frame).register(var) def getVar(self, var: Argument or Variable): """ Gets a variable from a frame. :param var: Argument of Variable instance that is searched. :return: Variable that is found in storage. """ self.__checkFrame(var.frame) if isinstance(var, Argument): return self.get(var.frame).get(var.value) if isinstance(var, Variable): return self.get(var.frame).get(var.name) def updateVar(self, var: Variable, value: str, varType: str): """ Updates a variable. :param var: Variable that is updated. :param value: New value for variable. :param varType: New type for variable. :return: """ self.__checkFrame(var.frame) return self.get(var.frame).update(var, value, varType) def hasVar(self, var: Argument): """ Check if frames contains variable. :param var: Searched variable. :return: True if one of frames has variable otherwise false. """ self.__checkFrame(var.frame) return self.get(var.frame).has(var.value) def __checkFrame(self, frameType): """ Checks if frame exists. :param frameType: Frame type """ if self.get(frameType) is None: frame = 'Temporary' if frameType == 'TF' else 'Local' self.handler.terminateInterpret(55, frame + ' frame is not set.')
import sys from src.Interpret.App import App from src.Support.ErrorHandler import ErrorHandler handler = ErrorHandler() # Create Application instance app = App() # Register arguments app.registerArguments(["--source=file", "--input=file"]) # Listen for arguments app.listen(sys.argv) # Run interpret app.runInterpret() # Terminate app app.terminate()
class Parser: def __init__(self, tree): """ Parser invoker. :param tree: tree of the XML structure. """ self.handler = ErrorHandler() root = self.__checkRootNode(tree.documentElement) self.__checkNodes(root.childNodes) def __checkRootNode(self, root): """ Checks root node element. :param root: The root node """ allowedAttributes = ['name', 'description', 'language'] if root.tagName != 'program': self.handler.terminateProgram( 32, 'Unknown root element: ' + root.tagName) if not self.__hasValidAttributes(root, allowedAttributes): self.handler.terminateProgram(32, '<program> has illegal attribute') return root def __checkNodes(self, nodes): """ Checks if nodes contains only instructions. :param nodes: The checked nodes """ for node in nodes: if node.nodeType == 1: self.__checkInstructionNode(node) else: if node.nodeType == 3 and node.data.lstrip().rstrip( ) == "": # TEXT Node continue self.handler.terminateProgram(32, 'Illegal nodeType.') def __checkInstructionNode(self, node): """ Checks if node is an instruction. :param node: The checked node """ allowedAttributes = ['order', 'opcode'] if node.tagName != 'instruction': self.handler.terminateProgram(32, 'Illegal tag name: ' + node.tagName) if not self.__hasValidAttributes(node, allowedAttributes): self.handler.terminateProgram( 32, '<instruction> has illegal attributes.') # Check if instruction exists opcode = node.getAttribute('opcode').upper() if opcode not in instructions: self.handler.terminateProgram( 32, 'Unknown instruction: ' + node.getAttribute('opcode')) # Check if order > 0 order = node.getAttribute('order') if not order.lstrip('-').isdigit() or int(order) < 1: self.handler.terminateProgram( 32, 'Order attribute has incorrect value.') # Check child (arg) nodes if node.hasChildNodes(): self.__checkArgNodes(opcode, node.childNodes) def __checkArgNodes(self, instruction, nodes): """ Checks arg nodes of instruction. :param instruction: Instruction that has these args :param nodes: The checked nodes """ index = 0 argIndexes = [0] * len(instructions.get(instruction)) for node in nodes: if node.nodeType == 1: # Check for index if index >= len(instructions.get(instruction)): self.handler.terminateProgram( 32, instruction + ' has invalid number of operands.') # Check argument node argIndex = self.__checkArgNode(node, instruction) argIndexes[index] = argIndex index += 1 else: if node.nodeType == 3 and node.data.lstrip().rstrip( ) == "": # TEXT Node newline continue self.handler.terminateProgram(32, 'Illegal nodeType.') if 0 in argIndexes: self.handler.terminateProgram( 32, instruction + ' has invalid number of operands.') def __checkArgNode(self, node, instruction): """ Checks current arg node and its value. :param node: Current arg node :param instruction: Instruction that is related to current argument :return Index of argument. """ allowedAttributes = ['type'] if not re.fullmatch('(arg[1-3])', node.tagName): self.handler.terminateProgram( 32, instruction + ' has invalid name of arg.') argIndex = node.tagName[3] if not self.__hasValidAttributes(node, allowedAttributes): self.handler.terminateProgram( 32, '<arg' + argIndex + '> has illegal attributes') # Check child (text) nodes if node.hasChildNodes(): for arg in node.childNodes: if arg.nodeType == 3: # TEXT Nodes index = int(argIndex) - 1 if index >= len(instructions.get(instruction)): self.handler.terminateProgram( 32, instruction + " has invalid argument.") operandType = instructions.get(instruction)[index] if not self.__isValueValid( arg.data, node.getAttribute('type'), operandType): self.handler.terminateProgram( 32, instruction + " expected " + operandType + " but type " + node.getAttribute('type') + " given.") else: self.handler.terminateProgram(32, 'Illegal nodeType.') return int(argIndex) @staticmethod def __hasValidAttributes(node, attributes: list) -> bool: """ Checks if attribute is valid. :param node: The checked node :param attributes: List of allowed attributes :return: False if attributes are not in allowed attributes otherwise True. """ for i in range(node.attributes.length): if node.attributes.item(i).name not in attributes: return False return True @staticmethod def __isValueValid(expression: str, argType: str, operandType: str) -> bool: """ Check if arg has valid value :param expression: The checked expression :param argType: Argument type (actual) :param operandType: Operand type (from instruction) :return: """ frames = ['GF', 'LF', 'TF'] if operandType == 'var' or (operandType == 'symb' and argType == 'var'): if argType != 'var': return False if expression[0:2] not in frames: return False if expression[2] != '@': return False if not re.match('[a-zA-Z?!*%$&_-]', expression[3]): return False if not re.fullmatch('^([a-zA-Z0-9?!*%$&_-])*$', expression[3:]): return False elif operandType == 'symb': args = ['int', 'bool', 'nil', 'string'] if argType not in args: return False if argType == 'int' and not expression.lstrip('-').isdigit(): return False if argType == 'bool' and (expression != 'true' and expression != 'false'): return False if argType == 'nil' and expression != 'nil': return False if argType == 'string' and not isinstance(expression, str): return False elif operandType == 'label': if argType != 'label': return False if not re.match('[a-zA-Z?!*%$&_-]', expression[0]): return False if not re.fullmatch('^([a-zA-Z0-9?!*%$&_-])*$', expression): return False elif operandType == 'type': if argType != 'type': return False if expression != 'int' and expression != 'string' and expression != 'bool': return False return True