def __init__(self, instructions: List, data_input: IO, stats: Stats): # Datovy vstup pro instrukci read. self.input = data_input # Aktualni pozice v programu. self.instruction_pointer = 0 self.GF: Dict[str, Symbol] = dict() # Globalni ramec self.TF: Dict[str, Symbol] = None # Docasny ramec. self.LF_Stack: List[Dict[str, Symbol]] = list() # Lokalni ramec. self.data_stack: List[Symbol] = list() # Datovy zasobnik self.call_stack: List[int] = list() # Zasobnik volani self.exit_code = 0 # Navratovy kod self.stats = stats # Statistiky # <label, instructionPointerPosition> self.labels: Dict[str, int] = dict() self.instructions: List[instrs.InstructionBase] = list() # Detekce navesti for instruction in instructions: if type(instruction) is instrs.Label: if instruction.name.name in self.labels: exit_app(exitCodes.SEMANTIC_ERROR, 'Detected label redefinition.', True) self.labels.update( {instruction.name.name: len(self.instructions)}) else: self.instructions.append(instruction)
def validate_scope(scope: str): """ Kontrola platnosti ramce. """ if scope != Frames.GLOBAL.value and scope != Frames.LOCAL.value and \ scope != Frames.TEMPORARY.value: exit_app(exitCodes.INVALID_XML_STRUCT, 'Invalid scope. ({})'.format(scope), True)
def execute(self, program: Program): label: LabelModel = self.args[0] if label.name not in program.labels: exit_app(exitCodes.SEMANTIC_ERROR, 'Undefined label to jump. ({})'.format(label.name), True) program.instruction_pointer = program.labels[label.name]
def execute(self, program: Program): arg = self.args[0] if program.var_exists(arg): exit_app(exitCodes.SEMANTIC_ERROR, 'DEFVAR\nVariable {} now exists. Cannot redefine.', True) program.var_set('DEFVAR', arg, None, True)
def __init__(self, args: List[InstructionArgument], opcode: str): if len(self.expected_args) != len(args): exit_app(exitCodes.INVALID_XML_STRUCT, 'Invalid count of arguments at opcode {}'.format(opcode), True) self.opcode = opcode self.args = args
def validate_variable_name(name: str, is_label: bool = False): """ Kontrola spravnosti nazvu promenne, nebo navesti. """ if re.compile(r"^[_\-$&%*!?a-zA-Z][_\-$&%*!?a-zA-Z0-9]*$").match(name)\ is None: exit_app( exitCodes.INVALID_XML_STRUCT, 'Invalid {} name. ({})'.format( 'label' if is_label else 'variable', name), True)
def execute(self, program: Program): if len(program.data_stack) == 0: exit_app( exitCodes.UNDEFINED_VALUE, 'POPS\nInstruction {}. Data Stack is empty.'.format( self.opcode), True) program.var_set('POPS', self.args[0], program.pop_stack(1)[0])
def execute(self, program: Program): if program.TF is None: exit_app( exitCodes.INVALID_FRAME, 'PUSHFRAME\nInvalid access to undefined temporary frame.', True) program.LF_Stack.append(program.TF) program.TF = None
def execute(self, program: Program): string = program.get_symb('STRLEN', self.args[1]) if not string.is_string(): exit_app(exitCodes.INVALID_DATA_TYPE, 'STRLEN\nExpected string', True) string_length = Symbol(DataTypes.INT, len(string.value)) program.var_set('STRLEN', self.args[0], string_length)
def execute(self, program: Program): symb = program.pop_stack(1)[0] if not symb.is_float(): exit_app( exitCodes.INVALID_DATA_TYPE, 'INT2CHAR\nInvalid data type' + ' Expected FLOAT in second parameter.') symbol = Symbol(DataTypes.INT, int(symb.value)) program.data_stack.append(symbol)
def execute(self, program: Program): symb = program.get_symb('FLOAT2INT', self.args[1]) if not symb.is_float(): exit_app( exitCodes.INVALID_DATA_TYPE, 'INT2CHAR\nInvalid data type' + ' Expected FLOAT in second parameter.') symbol = Symbol(DataTypes.INT, int(symb.value)) program.var_set('FLOAT2INT', self.args[0], symbol)
def execute(self, program: Program): symb1 = program.get_symb('JUMPNIFEQ', self.args[1]) symb2 = program.get_symb('JUMPNIFEQ', self.args[2]) if symb2.equal_type(symb1.data_type) or symb1.is_nil() or\ symb2.is_nil(): if not symb2.equals_value(symb1): Jump.execute(self, program) else: exit_app(exitCodes.INVALID_DATA_TYPE, 'JUMPIFEQ\nOperands must have same type.', True)
def execute(self, program: Program): symb = program.get_symb('NOT', self.args[1]) if not symb.is_bool(): exit_app( exitCodes.INVALID_DATA_TYPE, 'NOT\nInvalid data type. Expected: bool. Have: ({})'.format( symb.data_type.value), True) result = Symbol(DataTypes.BOOL, not symb.value) program.var_set('NOT', self.args[0], result)
def execute(self, program: Program): symb = program.pop_stack(1)[0] if not symb.is_bool(): exit_app( exitCodes.INVALID_DATA_TYPE, 'NOT\nInvalid data type. Expected: bool. Have: ({})'.format( symb.data_type.value), True) result = Symbol(DataTypes.BOOL, not symb.value) program.data_stack.append(result)
def execute(self, program: Program): symb = program.get_symb('EXIT', self.args[0], True) if not symb.is_int(): exit_app(exitCodes.INVALID_DATA_TYPE, 'EXIT\nInvalid exit code', True) elif symb.value < 0 or symb.value > 49: exit_app(exitCodes.INVALID_OPERAND_VALUE, 'EXIT\nInvalid exit code. Allowed range is <0; 49>.', True) else: program.exit(symb.value)
def get_symb(self, opcode: str, symb: Symbol or Variable, required_value: bool = True): result = symb if type(symb) is Variable: result = self.var_get(opcode, symb) if required_value and result is None: exit_app(exitCodes.UNDEFINED_VALUE, '{}\nSymbol or variable is undefined.'.format(opcode)) return result
def execute(self, program: Program): symbols = program.pop_stack(2) symb2 = symbols[0] symb1 = symbols[1] if symb2.equal_type(symb1.data_type) or symb1.is_nil() or\ symb2.is_nil(): if not symb2.equals_value(symb1): Jump.execute(self, program) else: exit_app(exitCodes.INVALID_DATA_TYPE, 'JUMPIFEQS\nOperands must have same type.', True)
def execute(self, program: Program): variable = program.var_get('SETCHAR', self.args[0]) index = program.get_symb('SETCHAR', self.args[1]) toModify = program.get_symb('SETCHAR', self.args[2]) if variable is None: exit_app(exitCodes.UNDEFINED_VALUE, 'SETCHAR\nUndefined variable.', True) if not index.is_int() or not variable.is_string() or\ not toModify.is_string(): exit_app(exitCodes.INVALID_DATA_TYPE, 'SETCHAR\nExpected: string variable, int, string', True) if len(toModify.value) == 0 or index.value >= len(variable.value): exit_app(exitCodes.INVALID_STRING_OPERATION, 'SETCHAR\nZero length of to modify characters.', True) try: result = "{}{}{}".format(variable.value[:index.value], toModify.value[0], variable.value[index.value + 1:]) program.var_set('SETCHAR', self.args[0], Symbol(DataTypes.STRING, result)) except IndexError: exit_app(exitCodes.INVALID_STRING_OPERATION, 'SETCHAR\nIndex is out of range.', True)
def execute(self, program: Program): string = program.get_symb('GETCHAR', self.args[1]) index = program.get_symb('GETCHAR', self.args[2]) if not string.is_string() or not index.is_int(): exit_app(exitCodes.INVALID_DATA_TYPE, 'GETCHAR\nExpected string and int', True) try: result = Symbol(DataTypes.STRING, string.value[index.value]) program.var_set('GETCHAR', self.args[0], result) except IndexError: exit_app(exitCodes.INVALID_STRING_OPERATION, 'GETCHAR\nIndex out of range.', True)
def var_get(self, opcode: str, var: Variable) -> Symbol: """ Ziskani hodnoty promenne. """ if not self.var_exists(var): exit_app(exitCodes.UNDEFINED_VARIABLE, '{}\nVariable {} not exists'.format(opcode, var.value), True) if var.frame == Frames.TEMPORARY: return self.TF[var.value] elif var.frame == Frames.GLOBAL: return self.GF[var.value] elif var.frame == Frames.LOCAL: return self.LF_Stack[-1][var.value]
def execute(self, program: Program): symb1 = program.get_symb('CONCAT', self.args[1]) if not symb1.is_string(): exit_app(exitCodes.INVALID_DATA_TYPE, 'CONCAT\nInvalid type at second operand.', True) symb2 = program.get_symb('CONCAT', self.args[2]) if not symb2.is_string(): exit_app(exitCodes.INVALID_DATA_TYPE, 'CONCAT\nInvalid type at third operand.', True) result = Symbol(DataTypes.STRING, symb1.value + symb2.value) program.var_set('CONCAT', self.args[0], result)
def pop_stack(self, required_count: int): """ Ziskani N hodnot ze zasovniku. """ if len(self.data_stack) < required_count: exit_app( exitCodes.UNDEFINED_VALUE, 'Invalid count of required arguments in stack at' + ' instruction {}. Count of values in data_stack: {}'.format( self.instructions[self.instruction_pointer].opcode, len(self.data_stack))) stack_data = list() for i in range(required_count): stack_data.append(self.data_stack.pop()) return stack_data
def execute(self, program: Program): symb = program.pop_stack(1)[0] if not symb.is_int(): exit_app(exitCodes.INVALID_DATA_TYPE, 'INT2CHARS\nInvalid data type. Expected: int', True) try: char = chr(symb.value) except Exception: exit_app( exitCodes.INVALID_STRING_OPERATION, 'INT2CHARS\nInvalid int to char conversion value. {}'.format( symb.value)) else: program.data_stack.append(Symbol(DataTypes.STRING, char))
def var_exists(self, var: Variable): """ Kontrola na existenci promenne. """ if var.frame == Frames.GLOBAL: return var.value in self.GF elif var.frame == Frames.TEMPORARY: if self.TF is None: exit_app(exitCodes.INVALID_FRAME, 'Temporary frame is unitialized', True) return var.value in self.TF elif var.frame == Frames.LOCAL: if len(self.LF_Stack) == 0: exit_app(exitCodes.INVALID_FRAME, 'Local frame stack is empty.', True) return var.value in self.LF_Stack[-1]
def execute(self, program: Program): var = self.args[0] symb = program.get_symb('INT2CHAR', self.args[1]) if not symb.is_int(): exit_app(exitCodes.INVALID_DATA_TYPE, 'INT2CHARS\nInvalid data type. Expected: int', True) try: char = chr(symb.value) program.var_set('INT2CHAR', var, Symbol(DataTypes.STRING, char)) except Exception: exit_app( exitCodes.INVALID_STRING_OPERATION, 'INT2CHAR\nInvalid int to char conversion value. {}'.format( symb.value))
def parse_file(file: IO) -> Dict[int, instructions.InstructionBase]: """ Nacteni XML dat z datoveho proudu a zpracovani. Perameters ---------- file: IO Vstupni datovy proud. Returns ------- Dict[int, instructions.InstructionBase] Serazeny slovnik, kde klicem bude obsah XML atributu order. """ try: xml_data = parse_xml(file).getroot() return InstructionsParser.parse(xml_data) except ParseError: exit_app(exitCodes.INVALID_XML_FORMAT, 'Invalid XML format.', True)
def var_set(self, opcode: str, var: Variable, value: Symbol, create: bool = False): """ Nastaveni hodnoty promenne. Pripadne vytvořeni. """ if not create and not self.var_exists(var): exit_app(exitCodes.UNDEFINED_VARIABLE, '{}\nVariable {} not exists'.format(opcode, var.value), True) if var.frame == Frames.GLOBAL: self.GF[var.value] = value elif var.frame == Frames.LOCAL: self.LF_Stack[-1][var.value] = value elif var.frame == Frames.TEMPORARY: self.TF[var.value] = value
def parse_arguments(element: Element) -> List[InstructionArgument]: """ Nacteni parametru instrukce. Parameters ---------- element: Element XML element obsahujici data instrukce. Returns ------- List[InstructionArgument] Pole instanci parametru instrukce. """ arg1 = element.findall('arg1') arg2 = element.findall('arg2') arg3 = element.findall('arg3') if len(arg1) > 1: exit_app(exitCodes.INVALID_XML_STRUCT, 'Multiple elements named arg1', True) elif len(arg2) > 1: exit_app(exitCodes.INVALID_XML_STRUCT, 'Multiple elements named arg2', True) elif len(arg3) > 1: exit_app(exitCodes.INVALID_XML_STRUCT, 'Multiple elements named arg3', True) if len(arg3) > 0 and (len(arg1) == 0 or len(arg2) == 0): exit_app(exitCodes.INVALID_XML_STRUCT, 'Third argument was set, but first or second missing.', True) if len(arg2) > 0 and len(arg1) == 0: exit_app(exitCodes.INVALID_XML_STRUCT, 'Second argument was set, but first missing.', True) args = list() if len(arg1) > 0: args.append(InstructionsParser.parse_argument(arg1[0])) if len(arg2) > 0: args.append(InstructionsParser.parse_argument(arg2[0])) if len(arg3) > 0: args.append(InstructionsParser.parse_argument(arg3[0])) return args
def execute(self, program: Program): symbols = program.pop_stack(2) index = symbols[0] string = symbols[1] if not string.is_string() or not index.is_int(): exit_app( exitCodes.INVALID_DATA_TYPE, 'STRI2INTS\nInvalid data type. Expected: string and int.' + ' Have: {} and {}'.format(string.data_type.value, index.data_type.value), True) try: ordinary = ord(string.value[index.value]) except IndexError: exit_app(exitCodes.INVALID_STRING_OPERATION, 'String is out of range.', True) else: program.data_stack.append(Symbol(DataTypes.INT, ordinary))
def execute(self, program: Program): string = program.get_symb('STRI2INT', self.args[1]) index = program.get_symb('STRI2INT', self.args[2]) if string is None or not string.is_string( ) or index is None or not index.is_int(): exit_app( exitCodes.INVALID_DATA_TYPE, 'STRI2INT\nInvalid data type. Expected: string and int.' + ' Have: {} and {}'.format(string.data_type.value, index.data_type.value), True) try: ordinary = ord(string.value[index.value]) program.var_set('STRI2INT', self.args[0], Symbol(DataTypes.INT, ordinary)) except IndexError: exit_app(exitCodes.INVALID_STRING_OPERATION, 'String is out of range.', True)