示例#1
0
class Interpret:
    string_reg = re.compile(r'^([^\\#\s]*(\\\d{3})*)*$')
    int_reg = re.compile(r'^([+\-])*(\d)+')

    def __init__(self, program_list):
        # seznam instrukci vstupniho programu
        self.program_list = program_list
        # pocet vsech vykonanych instrukci
        # (muze byt vyssi nez pocet instrukci ve vstupnim programu)
        self.ins_cntr = 0 
        self.prog_cntr = 0 # poradi prave prochazene instrukce od 0

        # seznam slovniku promennych v ramci GF, 
        # docasne tam budou vsechny promenne (TF i LF)
        # slovnik promennych - jmeno:hodnota
        self.frames = Frames(self.prog_cntr)
        self.label_dict = {} # jmeno : poradi_instrukce
        self.data_stack = [] # stack pro POPS a PUSHS
        self.call_stack = []

        # promenna pro skoky
        # - interpret projde znovu program od mista, kam se skoci
        self.again = True

    # hlavni metoda interpretu
    def interpret(self):
        # Projde program a ulozi labely
        for self.prog_cntr, val in enumerate(self.program_list):
            self.opcode = self.program_list[self.prog_cntr][0]
            if self.opcode  == 'LABEL':
                name = self.get_value_prog(1)
                # kdyz neni label ve slovniku labelu, pridej ho tam
                if name not in self.label_dict:
                    self.label_dict[name] = self.prog_cntr
                else:
                    print("{}. instrukce, {}, pokus o redefinici navesti '{}'."
                        .format(self.prog_cntr+1, self.opcode, name), file=sys.stderr)
                    sys.exit(SEM_ERR)

        # Hlavni cyklus vykovanani instrukci       
        self.prog_cntr = 0        
        start = 0 # misto kam se ma skocit
        self.again = True        
        while self.again:
            self.prog_cntr = 0
            self.again = False

            for self.prog_cntr, val in enumerate(self.program_list[start:], start = start):
                #print(self.prog_cntr, val) #DEBUG         
                #print(program_list[self.prog_cntr][0]) #DEBUG 
                self.opcode = self.program_list[self.prog_cntr][0]
                self.ins_cntr += 1
                self.frames.update_prog_cntr(self.prog_cntr)
                if self.opcode == 'LABEL':
                    pass
                ###############################################################
                elif self.opcode == 'DEFVAR':
                    var = self.get_var_prog()
                    self.frames.get_frame(var) # zkontroluje jestli frame existuje
                    # deklarace promenne, ktera jeste neni deklarovana
                    if not self.is_var_declared(var):
                        self.declare_var(var)
                ###############################################################
                elif self.opcode == 'MOVE':
                    symb_type = self.get_symb_type(2, True)
                    value = self.get_symb_value(2)                    
                    self.update_or_def_val(1, symb_type, value)
                ###############################################################
                elif self.opcode == 'JUMP':
                    start = self.jump()
                    if self.again:
                        break
                ###############################################################
                elif self.opcode == 'JUMPIFEQ':
                    start = self.jump_equality(operator.eq)
                    if self.again:
                        break
                ###############################################################
                elif self.opcode == 'JUMPIFNEQ':
                    start = self.jump_equality(operator.ne)
                    if self.again:
                        break
                ###############################################################
                elif self.opcode == 'WRITE':
                    symb_type = self.get_symb_type(1, True)
                    value = self.get_symb_value(1)                    
                    value = self.bool_to_str(symb_type, value)
                    print(value, file=sys.stdout)
                elif self.opcode == 'DPRINT':
                    pass
                ###############################################################
                elif self.opcode == 'CONCAT':
                    self.operator_3_args(operator.add,'string')
                elif self.opcode == 'ADD':
                    self.operator_3_args(operator.add,'int')
                elif self.opcode == 'SUB':
                    self.operator_3_args(operator.sub,'int')
                elif self.opcode == 'MUL':
                    self.operator_3_args(operator.mul,'int')
                elif self.opcode == 'IDIV':
                    self.operator_3_args(operator.floordiv,'int')
                elif self.opcode == 'LT':
                    self.operator_3_args(operator.lt,'any')
                elif self.opcode == 'GT':
                    self.operator_3_args(operator.gt,'any')
                elif self.opcode == 'EQ':
                    self.operator_3_args(operator.eq,'any')
                elif self.opcode == 'AND':
                    self.operator_3_args(operator.and_,'bool')
                elif self.opcode == 'OR':
                    self.operator_3_args(operator.or_,'bool')                
                ###############################################################
                elif self.opcode == 'NOT':
                    self.op_not()
                elif self.opcode == 'PUSHS':
                    symb_type = self.get_symb_type(1, True)
                    value = self.get_symb_value(1)
                    var_list = []
                    var_list.append(symb_type)
                    var_list.append(value)
                    self.data_stack.append(var_list)
                ###############################################################
                elif self.opcode == 'POPS':
                    stack_len = len(self.data_stack)
                    if stack_len > 0:
                        value_list = self.data_stack.pop()
                        var_type = value_list[0]
                        value = value_list[1]
                        self.update_or_def_val(1, var_type, value)
                    else:
                        print("{}. instrukce, {}, datovy zasobnik je prazdny"
                            .format(self.prog_cntr+1, self.opcode), file=sys.stderr)
                        sys.exit(VALUE_MISSING)
                ###############################################################  
                elif self.opcode == 'TYPE':
                    arg_type = self.get_type_prog(2)
                    name = self.get_value_prog(2)
                    if arg_type == 'var':
                        if not self.is_var_declared(name):
                            self.not_declared_err(name)

                    # promenna nemusi byt definovana -> False
                    value = self.get_symb_type(2, False)
                    self.update_or_def_val(1, 'string', value)
                ###############################################################
                elif self.opcode == 'READ':
                    exp_type = self.check_type_get_value(2, 'type')
                    try:
                        value = input()
                    except EOFError as error:
                        value = ''

                    if exp_type == 'int':
                        if re.match(self.int_reg, value):
                            value = int(value)
                        else:
                            value = 0
                    elif exp_type == 'bool':
                        val_lower = value.lower()
                        if val_lower in {'true','false'}:
                            value = self.str_to_bool('bool',val_lower)
                        else:
                            value = False

                    self.update_or_def_val(1, exp_type, value)
                ###############################################################
                elif self.opcode == 'STRLEN':
                    value = self.check_type_get_value(2, 'string')
                    length = len(value)
                    self.update_or_def_val(1, 'int', length)
                ###############################################################
                elif self.opcode == 'STRI2INT':
                    string = self.check_type_get_value(2, 'string')
                    index = self.check_type_get_value(3, 'int')
                    self.check_str_bounds(index, string)
                    char = string[index]
                    try:
                        ord_val = ord(char)
                    except TypeError as error:
                        print("{}. instrukce, {}, znak {} se nepodaril prevest na ordinalni hodnotu"
                            .format(self.prog_cntr+1, self.opcode, char), file=sys.stderr)
                        sys.exit(STR_ERR)
                    self.update_or_def_val(1, 'int', ord_val)
                ###############################################################
                elif self.opcode == 'INT2CHAR':
                    value = self.check_type_get_value(2, 'int')
                    try:
                        char = chr(value)
                    except ValueError as error:
                        print("{}. instrukce, {}, hodnota {} neni validni ordinalni hodnota unicode"
                            .format(self.prog_cntr+1, self.opcode, value), file=sys.stderr)
                        sys.exit(STR_ERR)
                    self.update_or_def_val(1, 'string', char)
                ###############################################################
                elif self.opcode == 'GETCHAR':
                    symb1_type = self.get_symb_type(2, True)
                    if symb1_type != 'string':
                        self.symb_type_err(2, 'string')
                    
                    symb2_type = self.get_symb_type(3, True)
                    if symb2_type != 'int':
                        self.symb_type_err(3, 'int')

                    string = self.get_symb_value(2)
                    index = self.get_symb_value(3)
                    self.check_str_bounds(index, string)
                                           
                    char = string[index]
                    self.update_or_def_val(1, 'string', char)
                ###############################################################
                elif self.opcode == 'SETCHAR':
                    to_change = self.check_type_get_value(1, 'string')

                    symb1_type = self.get_symb_type(2, True)
                    if symb1_type != 'int':
                        self.symb_type_err(2, 'int')

                    string = self.check_type_get_value(3, 'string')
                    if len(string) > 0:
                        char = string[0]
                    else:
                        print("{}.instrukce, {}, retezec ve 3. argumentu je prazdny"
                            .format(self.prog_cntr+1, self.opcode), file=sys.stderr)
                        sys.exit(STR_ERR)

                    index = self.get_symb_value(2)                    
                    self.check_str_bounds(index, to_change)
                    changed_str = to_change[:index] + char + to_change[index+1:]
                    self.update_or_def_val(1, 'string', changed_str)
                ###############################################################

                elif self.opcode == 'BREAK':
                    print("Pozice v kodu: {}. instrukce, {}, pocet vykonanych instrukci: {}."
                        .format(self.prog_cntr+1, self.opcode, self.ins_cntr), file=sys.stderr)
                    print("Definovana navesti:\n{}"
                        .format(self.label_dict), file=sys.stderr)
                    print("\nGlobalni ramec:\n{}"
                        .format(self.frames.return_frame('GF')), file = sys.stderr)
                    print("\nLokalni ramec:\n{}"
                        .format(self.frames.return_frame('LF')), file = sys.stderr)
                    print("\nDocasny ramec:\n{}"
                        .format(self.frames.return_frame('TF')), file = sys.stderr)
                    print("\nDeklarovane promenne:\n{}"
                        .format(self.var_name_list), file = sys.stderr)
                    print("\nDatovy zasobnik:\n{}"
                        .format(self.data_stack), file = sys.stderr)

                ###############################################################
                elif self.opcode == 'CREATEFRAME':
                    self.frames.create_frame()
                elif self.opcode == 'PUSHFRAME':
                    self.frames.push_frame()
                elif self.opcode == 'POPFRAME':
                    self.frames.pop_frame()
                ###############################################################
                elif self.opcode == 'CALL':
                    label = self.check_type_get_value(1, 'label')
                    self.call_stack.append(self.prog_cntr)

                    start = self.jump()
                    if self.again:
                        break

                elif self.opcode == 'RETURN':
                    # skoci az za CALL, jinak by se zacyklil
                    if len(self.call_stack) > 0:
                        start = self.call_stack.pop() + 1
                        self.again = True
                        break
                    else:
                        print("{}. instrukce, {}, zasobnik volani je prazdny"
                            .format(self.prog_cntr+1, self.opcode), file=sys.stderr)
                        sys.exit(VALUE_MISSING)
                else:
                    print("{}. instrukce, neznama instrukce {}"
                        .format(self.prog_cntr+1, self.opcode), file=sys.stderr)
                    sys.exit(LEX_SYN_ERR)

    ########################################################################
    ########################################################################
    # vykona instrukce se tremi argumenty, ktere vyzaduji pouziti nejakeho operatoru
    # operator: definuje operaci mezi symboly
    # arg_type: podporovany typ vsech argumentu 
    # (tam kde jsou libovolne je tato hodnota 'any')
    def operator_3_args(self, operator, arg_type):
        symb1_type = self.get_symb_type(2, True)
        symb2_type = self.get_symb_type(3, True)
        symb1_val = self.get_symb_value(2)
        symb2_val = self.get_symb_value(3)

        defined = False
        if arg_type != 'any':
            if symb1_type != arg_type or symb2_type != arg_type:
                self.types_err(arg_type)
        else:
            if symb1_type != symb2_type:
                self.same_types_err()
            arg_type = 'bool'

        try:
            value = operator(symb1_val, symb2_val)
        except ZeroDivisionError as error:
            print("{}.instrukce, {}, deleni nulou."
                .format(self.prog_cntr+1, self.opcode))
            sys.exit(DIV_BY_ZERO_ERR)
        # je potreba, protoze v ostatnich pripadech se hodnota ziskava ve forme retezce
        self.update_or_def_val(1, arg_type, value) 

    # vykona instrukci NOT
    def op_not(self):
        symb_type = self.get_symb_type(2, True)
        if symb_type != 'bool':
            self.types_err('bool')
        symb_val = self.get_symb_value(2)
        value = not symb_val
        self.update_or_def_val(1, 'bool', value) 

    # skoci na label
    def jump(self):
        start = 0
        label = self.get_value_prog(1)
        if label not in self.label_dict:
            print("{}. instrukce, {}, skok na neexistujici navesti: {}."
                .format(self.prog_cntr+1, self.opcode, label), file=sys.stderr)
            sys.exit(SEM_ERR)
        else:
             # skoci az za label, aby nenastala chyba dvojite deklarace labelu
            start = self.label_dict[label]+1
            self.again = True
        return start

    # skoci na label v pripade, kdy vysledna hodnota vyrazu je vyhodnocena jako true
    # operator - operace mezi temito dvema symboly
    def jump_equality(self, operator):
        start = 0
        symb1_type = self.get_symb_type(2, True)
        symb2_type = self.get_symb_type(3, True)
        symb1_val = self.get_symb_value(2)
        symb2_val = self.get_symb_value(3)

        if symb1_type == symb2_type:
            if operator(symb1_val, symb2_val):
                start = self.jump()                
        else:
            print("{}.instrukce, {}, nelze porovnat typ {} s {}."
                .format(self.prog_cntr+1, self.opcode, symb1_type, symb2_type), file=sys.stderr)
            sys.exit(OPER_TYPE_ERR)
        return start
    ########################################################################
    # ze vstupniho seznamu instrukci vraci nazev promenne (tedy prvni argument) dane instrukce
    # napr z ['MOVE', {'var': 'GF@counter'}, {'string': None}] vrati GF@counter
    def get_var_prog(self):
        # var_dict - 'var' : jmeno
        var_dict = self.program_list[self.prog_cntr][1]
        var = var_dict['var']
        return var

    # Ze vstupniho seznamu instrukci vraci typ argumentu
    def get_type_prog(self, arg_order):
        type_dict = self.program_list[self.prog_cntr][arg_order]
        if 'var' in type_dict:
            var_type = 'var'
        elif 'int' in type_dict:
            var_type = 'int'
        elif 'string' in type_dict:
            var_type = 'string'
        elif 'bool' in type_dict:
            var_type = 'bool'
        elif 'label' in type_dict:
            var_type = 'label'
        elif 'type' in type_dict:
            var_type = 'type'
        else:
            print("Neexistujici typ.",file=sys.stderr)
            sys.exit(LEX_SYN_ERR)
        return var_type

    # Ze vstupniho seznamu instrukci vraci hodnotu argumentu
    # arg_order - poradi argumentu prave vyhodnocovane instrukce
    def get_value_prog(self,arg_order):
        var_type = self.get_type_prog(arg_order)
        value_dict = self.program_list[self.prog_cntr][arg_order]
        value = value_dict[var_type]
        value = self.str_to_int(var_type, value)             
        value = self.str_to_bool(var_type, value)

        if var_type == 'string':
            if value is None:
                value = ''
            else:
                value = self.convert_esc_seq(value)
        return value
    ########################################################################

    # vraci typ argumentu ze slovniku promenne (ze seznamu ramce)
    # var_dict - slovnik promenne: {jmeno : typ}
    def var_type_dict(self, var_dict):
        if 'int' in var_dict:
            var_type = 'int'
        elif 'string' in var_dict:
            var_type = 'string'
        elif 'bool' in var_dict:
            var_type = 'bool'
        elif 'type' in var_dict:
            var_type = 'type'
        else:
            print("Neexistujici typ promenne.",file=sys.stderr)
            sys.exit(LEX_SYN_ERR)
        return var_type

    # vraci hodnotu promenne ze slovniku promenne
    # var_dict - slovnik promenne: {jmeno : typ}
    def get_var_value(self, var_dict):
        var_type = self.var_type_dict(var_dict)        
        value = var_dict[var_type]            
        return value

    # konvertuje escape sekvenci na ASCII znak
    def convert_esc_seq(self, value):
        new_str = value
        esc_reg = re.compile(r'\\\d{3}')
        all_esc = re.findall(esc_reg, value)
        for esc in all_esc:
            try:
                string = chr(int(esc.lstrip('\\')))
                new_str = new_str.replace(esc, string)
            except TypeError as error:
                print("{}.instrukce, {}, escape sekvence {} se nepodarila prevest na znak unicode."
                    .format(self.prog_cntr+1, self.opcode, esc), file=sys.stderr)
                sys.exit(LEX_SYN_ERR)            
        return new_str

    ########################################################################
    
    # V programu na indexu instrukce self.prog_cntr najde argument v poradi arg_order
    # a vrati jeho hodnotu.
    # V pripade promenne vraci hodnotu promenne z daneho framu (listu).
    # arg_order - poradi argumentu prave vyhodnocovane instrukce
    def get_value(self, arg_order):
        var_type = self.get_type_prog(arg_order)
        value = self.get_value_prog(arg_order)
        
        if var_type == 'var':
            frame = self.frames.get_frame(value)
            value = self.without_frame(value)
            defined = False
            for var_list in frame:
                if value in var_list:
                    if len(var_list) != 2:
                        self.not_defined_err(value)
                    var_dict = var_list[1]
                    value = self.get_var_value(var_dict)
                    defined = True
                    break
            if not defined:
                self.not_defined_err(value)
        return value

    # v danem ramci aktualizuje hodnotu a typ promenne
    # arg_order - poradi argumentu prave vyhodnocovane instrukce
    # var_dict - slovnik promenne: {jmeno : typ}
    def update_value(self, arg_order, var_dict):
        symb_type = self.get_type_prog(arg_order)
        if symb_type != 'var':
            return False
        
        name = self.get_value_prog(arg_order)
        
        frame = self.frames.get_frame(name)
        name = self.without_frame(name)

        defined = False
        for var_list in frame:
            if var_list[0] == name:
                if len(var_list) == 2:
                    var_list.pop()                
                var_list.append(var_dict)
                defined = True
        
        if not defined:
            self.not_defined_err(name)

    # vraci typ promenne z jejiho ramce
    # v pripade instrukce TYPE je pri nenalezeni typu vracen prazdny retezec
    # arg_order - poradi argumentu prave vyhodnocovane instrukce
    def get_var_type(self, arg_order, exit_error):
        #              var       symb_type  value      
        #frame = [['GF@retezec', {'string': ahoj}]]
        #           |              |--------------|| var_dict
        #           |------------------------------| var_list
        symb_type = self.get_type_prog(arg_order)
        if symb_type != 'var':
            return False
        
        name = self.get_value_prog(arg_order)
        frame = self.frames.get_frame(name)
        name = self.without_frame(name)

        for var_list in frame:
            if var_list[0] == name:
                if len(var_list) == 2:
                    var_dict = var_list[1]
                    ret_type = self.var_type_dict(var_dict)
                else:
                    self.not_defined_err(name)
                return ret_type

        if exit_error:
            self.not_defined_err(name)
        else:
            return '' # pro instrukci TYPE

    # vrati hodnotu symbolu (promenne i konstanty)
    # arg_order - poradi argumentu prave vyhodnocovane instrukce
    def get_symb_value(self, arg_order):
        symb_type = self.get_type_prog(arg_order)
        if symb_type == 'var':
            symb_val = self.get_value(arg_order)
        else:
            symb_val = self.get_value_prog(arg_order)
        return symb_val

    # vrati typ symbolu
    # arg_order - poradi argumentu prave vyhodnocovane instrukce
    def get_symb_type(self,arg_order, exit_error):
        symb_type = self.get_type_prog(arg_order)
        if symb_type == 'var':
            symb_type = self.get_var_type(arg_order, exit_error)
        return symb_type

    # zjisti typ symbolu a zkontroluje, jestli je spravny
    # v pripade uspechu vraci hodnotu tohoto symbolu
    # arg_order - poradi argumentu prave vyhodnocovane instrukce
    # exp_type - spravny (ocekavany) typ symbolu
    def check_type_get_value(self, arg_order, exp_type):
        symb_type = self.get_symb_type(arg_order, True)
        if symb_type != exp_type:
            self.symb_type_err(arg_order, exp_type)
        else:
            return self.get_symb_value(arg_order)    

    # v pripade definovane promenne ji aktualizuje, jinak definuje
    # arg_order - poradi argumentu prave vyhodnocovane instrukce
    def update_or_def_val(self, arg_order, exp_type, value):
        defined = False
        if self.is_var_defined(arg_order):
            var_dict = {exp_type : value}
            self.update_value(arg_order, var_dict)
        else:
            self.define_undecl_var(arg_order, exp_type, value)

    # vraci True pokud je promenna definovana, jinak False
    # arg_order - poradi argumentu prave vyhodnocovane instrukce
    def is_var_defined(self, arg_order):
        name = self.get_value_prog(arg_order)
        frame = self.frames.get_frame(name)
        name = self.without_frame(name)        

        for var_list in frame:
            if var_list[0] == name and len(var_list) == 2:
                return True
        return False

    # z promenne odstrani prvni tri znaky (tedy GF@)
    # pozor na nazvy promennych zacinajici na G|T|LF@
    #  - tato hodnota by byla take odstranena
    # var - nazev promenne
    def without_frame(self, var):
        if var.startswith('GF@') or var.startswith('LF@') or var.startswith('TF@'):
            return var[3:]

    # najde index promenne v danem ramci
    # v pripade neuspechu vraci -1
    # var - nazev promenne
    # frame - ramec (seznam) 
    def find_var_index(self, var, frame):
        for index, var_list in enumerate(frame):
            if var_list[0] == var:
                return index
        return -1


    # definuje promennou
    # var - jmeno promenne
    # symb_type - typ promenne
    # value - prirazena hodnota
    def define_var(self, var, symb_type, value):
        frame = self.frames.get_frame(var)
        var = self.without_frame(var)

        index = self.find_var_index(var, frame)
        var_dict = {symb_type : value}
        frame[index].append(var_dict)

    # definuje deklarovanou promennou    
    # arg_order - poradi argumentu prave vyhodnocovane instrukce
    # var_type - typ promenne
    # value - hodnota
    def define_undecl_var(self, arg_order, var_type, value):
        var = self.get_var_prog()
        if self.is_var_declared(var):
            self.define_var(var, var_type, value)
        else:
            self.not_declared_err(var)
    
    ########################################################################

    # ladici vystup pro vypis vstupniho programu
    def print_program(self):
        for index, val in enumerate(self.program_list):
            print(index, val)
        print("--------------------------------------------------------")

    # v pripade, ze je promenna var deklarovana, vraci True, jinak False
    # var - nazev promenne i s ramcem
    def is_var_declared(self, var):        
        frame = self.frames.get_frame(var)
        var = self.without_frame(var)

        index = self.find_var_index(var, frame)
        if index == -1:
            return False
        else:
            return True

    # deklaruje promennou
    # var - nazev promenne i s ramcem
    def declare_var(self,var):
        frame = self.frames.get_frame(var)
        var = self.without_frame(var)

        var_list = []
        var_list.append(var)
        frame.append(var_list)

    # konvertuje bool hodnotu IPPcode18 na bool hodnoty pythonu
    # arg_type - typ symbolu
    # value - hodnota 
    def str_to_bool(self, arg_type, value):
        if arg_type == 'bool':            
            if value == 'true':
                value = True
            else:
                value = False
        return value

    # konvertuje bool hodnotu pythonu na hodnotu IPPcode18
    # arg_type - typ symbolu
    # value - hodnota 
    def bool_to_str(self, arg_type, value):
        if arg_type == 'bool':
            if value == True:
                value = 'true'
            else:
                value = 'false'
        return value

    # prevede cislo ulozene v retezci na cele cislo srozumitelne pythonem
    # arg_type - typ symbolu
    # value - hodnota  
    def str_to_int(self, arg_type, value):
        if arg_type == 'int':
            value = int(value)
        return value

    # zkontroluje, jestli se na indexu v retezci naleza nejaka hodnota
    # index - poradi znaku v retezci (od 0)
    # string - retezec
    def check_str_bounds(self, index, string):
        length = len(string)
        if index >= length or index < 0:
            print("{}.instrukce, {}, index {} je mimo velikost retezce {}"
                .format(self.prog_cntr+1, self.opcode, index, string), file=sys.stderr)
            sys.exit(STR_ERR)

    ########################################################################################
    # vypise chybove hlaseni spatneho typu symbolu a skonci s chybou
    # arg_order - poradi argumentu v instrukci
    # symb_type - typ symbolu
    def symb_type_err(self, arg_order, symb_type):
        print("{}.instrukce, {}, {}. argument musi byt typu {}."
            .format(self.prog_cntr+1, self.opcode, arg_order, symb_type), file=sys.stderr)
        sys.exit(OPER_TYPE_ERR)

    # vypise chybove hlaseni nedefinovane promenne a skonci s chybou
    # var - jmeno promenne
    def not_defined_err(self, var):
        print("{}. instrukce, {}, promenna {} neni definovana."
            .format(self.prog_cntr+1, self.opcode, var), file=sys.stderr)
        sys.exit(VALUE_MISSING)

    # vypise chybove hlaseni nedeklarovane promenne a skonci s chybou
    # var - jmeno promenne
    def not_declared_err(self,var):
        print("{}. instrukce, {}, promenna {} neni deklarovana."
                    .format(self.prog_cntr+1, self.opcode, var), file=sys.stderr)
        sys.exit(VAR_NOT_EXISTS_ERR)

    # vypise chybove hlaseni nekompatibility typu s instrukci
    # symb_type - typ symbolu
    def types_err(self, symb_type):
        print("{}.instrukce, {} podporuje pouze typ {}."
            .format(self.prog_cntr+1, self.opcode, symb_type), file=sys.stderr)
        sys.exit(OPER_TYPE_ERR)

    # vypise chybove hlaseni v pripade odlisnych typu instrukce vyzadujici stejne typy symbolu (napr. ADD)
    def same_types_err(self):
        print("{}.instrukce, symboly {} musi byt stejneho typu."
            .format(self.prog_cntr+1, self.opcode), file=sys.stderr)
        sys.exit(OPER_TYPE_ERR)
示例#2
0
class InterpretFactory:
    def __init__(self):
        """
        Set all variables to its default values
        """
        self.total_inst = 0
        self.stat_vars = 0
        self.instructions = []
        self.labels = []
        self.calls = []
        self.names_pattern = re.compile("[^A-ZÁ-Ža-zá-ž0-9\-\*\$%_&]")
        self.frames = Frames()
        self.variables_factory = VariablesFactory(self.frames)

    def add_instruction(self, opcode, args, position):
        """
        Parses instruction from XML
        :param opcode: Instruction code
        :param args: Instruction arguments
        :param position: Instruction position
        """
        if opcode not in INSTRUCTIONS:
            raise IPPcodeParseError("unknown instrustion %s" % opcode)

        try:
            if opcode == "MOVE":
                self.count_args(len(args), 2, opcode)
                self.var(args[0])
                self.symb(args[1])
            elif opcode == "CREATEFRAME":
                self.count_args(len(args), 0, opcode)
            elif opcode == "PUSHFRAME":
                self.count_args(len(args), 0, opcode)
            elif opcode == "POPFRAME":
                self.count_args(len(args), 0, opcode)
            elif opcode == "DEFVAR":
                self.count_args(len(args), 1, opcode)
                self.var(args[0])
            elif opcode == "CALL":
                self.count_args(len(args), 1, opcode)
                self.label(args[0])
            elif opcode == "RETURN":
                self.count_args(len(args), 0, opcode)
            elif opcode == "PUSHS":
                self.count_args(len(args), 1, opcode)
                self.symb(args[0])
            elif opcode == "POPS":
                self.count_args(len(args), 1, opcode)
                self.var(args[0])
            elif opcode == "ADD":
                self.count_args(len(args), 3, opcode)
                self.var(args[0])
                self.symb(args[1])
                self.symb(args[2])
            elif opcode == "SUB":
                self.count_args(len(args), 3, opcode)
                self.var(args[0])
                self.symb(args[1])
                self.symb(args[2])
            elif opcode == "MUL":
                self.count_args(len(args), 3, opcode)
                self.var(args[0])
                self.symb(args[1])
                self.symb(args[2])
            elif opcode == "IDIV":
                self.count_args(len(args), 3, opcode)
                self.var(args[0])
                self.symb(args[1])
                self.symb(args[2])
            elif opcode == "DIV":
                self.count_args(len(args), 3, opcode)
                self.var(args[0])
                self.symb(args[1])
                self.symb(args[2])
            elif opcode == "LT":
                self.count_args(len(args), 3, opcode)
                self.var(args[0])
                self.symb(args[1])
                self.symb(args[2])
            elif opcode == "GT":
                self.count_args(len(args), 3, opcode)
                self.var(args[0])
                self.symb(args[1])
                self.symb(args[2])
            elif opcode == "EQ":
                self.count_args(len(args), 3, opcode)
                self.var(args[0])
                self.symb(args[1])
                self.symb(args[2])
            elif opcode == "AND":
                self.count_args(len(args), 3, opcode)
                self.var(args[0])
                self.symb(args[1])
                self.symb(args[2])
            elif opcode == "OR":
                self.count_args(len(args), 3, opcode)
                self.var(args[0])
                self.symb(args[1])
                self.symb(args[2])
            elif opcode == "NOT":
                self.count_args(len(args), 2, opcode)
                self.var(args[0])
                self.symb(args[1])
            elif opcode == "INT2CHAR":
                self.count_args(len(args), 2, opcode)
                self.var(args[0])
                self.symb(args[1])
            elif opcode == "STRI2INT":
                self.count_args(len(args), 3, opcode)
                self.var(args[0])
                self.symb(args[1])
                self.symb(args[2])
            elif opcode == "INT2FLOAT":
                self.count_args(len(args), 2, opcode)
                self.var(args[0])
                self.symb(args[1])
            elif opcode == "FLOAT2INT":
                self.count_args(len(args), 2, opcode)
                self.var(args[0])
                self.symb(args[1])
            elif opcode == "READ":
                self.count_args(len(args), 2, opcode)
                self.var(args[0])
                self.type(args[1])
            elif opcode == "WRITE":
                self.count_args(len(args), 1, opcode)
                self.symb(args[0])
            elif opcode == "CONCAT":
                self.count_args(len(args), 3, opcode)
                self.var(args[0])
                self.symb(args[1])
                self.symb(args[2])
            elif opcode == "STRLEN":
                self.count_args(len(args), 2, opcode)
                self.var(args[0])
                self.symb(args[1])
            elif opcode == "GETCHAR":
                self.count_args(len(args), 3, opcode)
                self.var(args[0])
                self.symb(args[1])
                self.symb(args[2])
            elif opcode == "SETCHAR":
                self.count_args(len(args), 3, opcode)
                self.var(args[0])
                self.symb(args[1])
                self.symb(args[2])
            elif opcode == "TYPE":
                self.count_args(len(args), 2, opcode)
                self.var(args[0])
                self.symb(args[1])
            elif opcode == "LABEL":
                self.count_args(len(args), 1, opcode)
                self.label(args[0])
                self.add_label(args[0], position)
            elif opcode == "JUMP":
                self.count_args(len(args), 1, opcode)
                self.label(args[0])
            elif opcode == "JUMPIFEQ":
                self.count_args(len(args), 3, opcode)
                self.label(args[0])
                self.symb(args[1])
                self.symb(args[2])
            elif opcode == "JUMPIFNEQ":
                self.count_args(len(args), 3, opcode)
                self.label(args[0])
                self.symb(args[1])
                self.symb(args[2])
            elif opcode == "DPRINT":
                self.count_args(len(args), 1, opcode)
                self.symb(args[0])
            elif opcode == "BREAK":
                self.count_args(len(args), 0, opcode)
            elif opcode == "CLEARS":
                self.count_args(len(args), 0, opcode)
            elif opcode == "ADDS":
                self.count_args(len(args), 0, opcode)
            elif opcode == "SUBS":
                self.count_args(len(args), 0, opcode)
            elif opcode == "MULS":
                self.count_args(len(args), 0, opcode)
            elif opcode == "IDIVS":
                self.count_args(len(args), 0, opcode)
            elif opcode == "LTS":
                self.count_args(len(args), 0, opcode)
            elif opcode == "GTS":
                self.count_args(len(args), 0, opcode)
            elif opcode == "EQS":
                self.count_args(len(args), 0, opcode)
            elif opcode == "ANDS":
                self.count_args(len(args), 0, opcode)
            elif opcode == "ORS":
                self.count_args(len(args), 0, opcode)
            elif opcode == "NOTS":
                self.count_args(len(args), 0, opcode)
            elif opcode == "INT2CHARS":
                self.count_args(len(args), 0, opcode)
            elif opcode == "STRI2INTS":
                self.count_args(len(args), 0, opcode)
            elif opcode == "JUMPIFEQS":
                self.count_args(len(args), 1, opcode)
                self.label(args[0])
            elif opcode == "JUMPIFNEQS":
                self.count_args(len(args), 1, opcode)
                self.label(args[0])

            self.instructions.append({"opcode": opcode, "args": args})
        except IndexError:
            raise IPPcodeParseError("missing arguments for %s instruction" %
                                    opcode)

    def run(self):
        """
        Runs program and interprets all the instructions
        """
        inst_len = len(self.instructions)
        current_inst = 0

        while current_inst < inst_len:
            if self.instructions[current_inst]["opcode"] == "DEFVAR":
                self.variables_factory.def_var(
                    self.instructions[current_inst]["args"][0])
            elif self.instructions[current_inst]["opcode"] == "MOVE":
                self.variables_factory.move_to_var(
                    self.instructions[current_inst]["args"][0],
                    self.instructions[current_inst]["args"][1])
            elif self.instructions[current_inst]["opcode"] == "CREATEFRAME":
                self.frames.temporary_frame = []
            elif self.instructions[current_inst]["opcode"] == "PUSHFRAME":
                self.frames.push_frame()
            elif self.instructions[current_inst]["opcode"] == "POPFRAME":
                self.frames.pop_frame()
            elif self.instructions[current_inst]["opcode"] == "CALL":
                self.calls.append(current_inst)
                current_inst = self.jump_to_label(
                    self.instructions[current_inst]["args"][0]) - 1
            elif self.instructions[current_inst]["opcode"] == "RETURN":
                if len(self.calls) == 0:
                    sys.stderr.write("ERROR: CALL stack is empty!\n")
                    exit(56)
                current_inst = self.calls.pop()
            elif self.instructions[current_inst]["opcode"] == "PUSHS":
                self.variables_factory.push_stack(
                    self.instructions[current_inst]["args"][0])
            elif self.instructions[current_inst]["opcode"] == "CLEARS":
                self.variables_factory.clear_stack()
            elif self.instructions[current_inst]["opcode"] == "POPS":
                self.variables_factory.pop_stack(
                    self.instructions[current_inst]["args"][0])
            elif self.instructions[current_inst]["opcode"] == "WRITE":
                self.variables_factory.print_var(
                    self.instructions[current_inst]["args"][0])
            elif self.instructions[current_inst]["opcode"] == "DPRINT":
                self.variables_factory.print_var(
                    self.instructions[current_inst]["args"][0], True)
            elif self.instructions[current_inst]["opcode"] == "ADD":
                self.variables_factory.aritmetic_operation(
                    self.instructions[current_inst]["args"][0],
                    self.instructions[current_inst]["args"][1],
                    self.instructions[current_inst]["args"][2], "add")
            elif self.instructions[current_inst]["opcode"] == "SUB":
                self.variables_factory.aritmetic_operation(
                    self.instructions[current_inst]["args"][0],
                    self.instructions[current_inst]["args"][1],
                    self.instructions[current_inst]["args"][2], "sub")
            elif self.instructions[current_inst]["opcode"] == "MUL":
                self.variables_factory.aritmetic_operation(
                    self.instructions[current_inst]["args"][0],
                    self.instructions[current_inst]["args"][1],
                    self.instructions[current_inst]["args"][2], "mul")
            elif self.instructions[current_inst]["opcode"] == "IDIV":
                self.variables_factory.aritmetic_operation(
                    self.instructions[current_inst]["args"][0],
                    self.instructions[current_inst]["args"][1],
                    self.instructions[current_inst]["args"][2], "idiv")
            elif self.instructions[current_inst]["opcode"] == "DIV":
                self.variables_factory.aritmetic_operation(
                    self.instructions[current_inst]["args"][0],
                    self.instructions[current_inst]["args"][1],
                    self.instructions[current_inst]["args"][2], "div")
            elif self.instructions[current_inst]["opcode"] == "LT":
                self.variables_factory.relation_operator(
                    self.instructions[current_inst]["args"][0],
                    self.instructions[current_inst]["args"][1],
                    self.instructions[current_inst]["args"][2], "lt")
            elif self.instructions[current_inst]["opcode"] == "GT":
                self.variables_factory.relation_operator(
                    self.instructions[current_inst]["args"][0],
                    self.instructions[current_inst]["args"][1],
                    self.instructions[current_inst]["args"][2], "gt")
            elif self.instructions[current_inst]["opcode"] == "EQ":
                self.variables_factory.relation_operator(
                    self.instructions[current_inst]["args"][0],
                    self.instructions[current_inst]["args"][1],
                    self.instructions[current_inst]["args"][2], "eq")
            elif self.instructions[current_inst]["opcode"] == "AND":
                self.variables_factory.bool_operator(
                    self.instructions[current_inst]["args"][0],
                    self.instructions[current_inst]["args"][1],
                    self.instructions[current_inst]["args"][2], "and")
            elif self.instructions[current_inst]["opcode"] == "OR":
                self.variables_factory.bool_operator(
                    self.instructions[current_inst]["args"][0],
                    self.instructions[current_inst]["args"][1],
                    self.instructions[current_inst]["args"][2], "or")
            elif self.instructions[current_inst]["opcode"] == "NOT":
                self.variables_factory.bool_operator(
                    self.instructions[current_inst]["args"][0],
                    self.instructions[current_inst]["args"][1],
                    self.instructions[current_inst]["args"][1], "not")
            elif self.instructions[current_inst]["opcode"] == "INT2CHAR":
                self.variables_factory.int_to_char(
                    self.instructions[current_inst]["args"][0],
                    self.instructions[current_inst]["args"][1])
            elif self.instructions[current_inst]["opcode"] == "STRI2INT":
                self.variables_factory.stri_to_int(
                    self.instructions[current_inst]["args"][0],
                    self.instructions[current_inst]["args"][1],
                    self.instructions[current_inst]["args"][2])
            elif self.instructions[current_inst]["opcode"] == "INT2FLOAT":
                self.variables_factory.int_to_float(
                    self.instructions[current_inst]["args"][0],
                    self.instructions[current_inst]["args"][1])
            elif self.instructions[current_inst]["opcode"] == "FLOAT2INT":
                self.variables_factory.float_to_int(
                    self.instructions[current_inst]["args"][0],
                    self.instructions[current_inst]["args"][1])
            elif self.instructions[current_inst]["opcode"] == "TYPE":
                self.variables_factory.get_type(
                    self.instructions[current_inst]["args"][0],
                    self.instructions[current_inst]["args"][1])
            elif self.instructions[current_inst]["opcode"] == "JUMP":
                current_inst = self.jump_to_label(
                    self.instructions[current_inst]["args"][0]) - 1
            elif self.instructions[current_inst]["opcode"] == "JUMPIFEQ":
                if (self.variables_factory.is_equal(
                        self.instructions[current_inst]["args"][1],
                        self.instructions[current_inst]["args"][2])):
                    current_inst = self.jump_to_label(
                        self.instructions[current_inst]["args"][0]) - 1
            elif self.instructions[current_inst]["opcode"] == "JUMPIFNEQ":
                if (self.variables_factory.is_not_equal(
                        self.instructions[current_inst]["args"][1],
                        self.instructions[current_inst]["args"][2])):
                    current_inst = self.jump_to_label(
                        self.instructions[current_inst]["args"][0]) - 1
            elif self.instructions[current_inst]["opcode"] == "CONCAT":
                self.variables_factory.concat_strings(
                    self.instructions[current_inst]["args"][0],
                    self.instructions[current_inst]["args"][1],
                    self.instructions[current_inst]["args"][2])
            elif self.instructions[current_inst]["opcode"] == "STRLEN":
                self.variables_factory.len_string(
                    self.instructions[current_inst]["args"][0],
                    self.instructions[current_inst]["args"][1])
            elif self.instructions[current_inst]["opcode"] == "GETCHAR":
                self.variables_factory.get_char(
                    self.instructions[current_inst]["args"][0],
                    self.instructions[current_inst]["args"][1],
                    self.instructions[current_inst]["args"][2])
            elif self.instructions[current_inst]["opcode"] == "SETCHAR":
                self.variables_factory.set_char(
                    self.instructions[current_inst]["args"][0],
                    self.instructions[current_inst]["args"][1],
                    self.instructions[current_inst]["args"][2])
            elif self.instructions[current_inst]["opcode"] == "READ":
                self.variables_factory.read_var(
                    self.instructions[current_inst]["args"][0],
                    self.instructions[current_inst]["args"][1])
            elif self.instructions[current_inst]["opcode"] == "BREAK":
                sys.stderr.write("--------- DEBUG INFO START ---------\n")
                sys.stderr.write("Instrictions interpreted: %d\n" %
                                 self.total_inst)
                sys.stderr.write("Current instruction number: %d\n" %
                                 int(current_inst + 1))
                sys.stderr.write("-- Global frame:\n")
                sys.stderr.write("Total: %d\n" % len(self.frames.global_frame))
                for var in self.frames.global_frame:
                    sys.stderr.write("%s (%d): %s\n" %
                                     (var.name, var.variable_type, var.value))
                sys.stderr.write("-- Local frame:\n")
                if self.frames.local_frame is not None:
                    sys.stderr.write("Total: %d\n" %
                                     len(self.frames.local_frame))
                    for var in self.frames.local_frame:
                        sys.stderr.write(
                            "%s (%d): %s\n" %
                            (var.name, var.variable_type, var.value))
                else:
                    sys.stderr.write("Not initialized\n")
                if self.frames.temporary_frame is not None:
                    sys.stderr.write("-- Temporary frame:\n")
                    sys.stderr.write("Total: %d\n" %
                                     len(self.frames.temporary_frame))
                    for var in self.frames.temporary_frame:
                        sys.stderr.write(
                            "%s (%d): %s\n" %
                            (var.name, var.variable_type, var.value))
                else:
                    sys.stderr.write("Not initialized\n")
                sys.stderr.write("---------- DEBUG INFO END ----------\n")
            elif self.instructions[current_inst]["opcode"] == "ADDS":
                self.variables_factory.aritmetic_operation(
                    None, None, None, "add")
            elif self.instructions[current_inst]["opcode"] == "SUBS":
                self.variables_factory.aritmetic_operation(
                    None, None, None, "sub")
            elif self.instructions[current_inst]["opcode"] == "MULS":
                self.variables_factory.aritmetic_operation(
                    None, None, None, "mul")
            elif self.instructions[current_inst]["opcode"] == "IDIVS":
                self.variables_factory.aritmetic_operation(
                    None, None, None, "idiv")
            elif self.instructions[current_inst]["opcode"] == "LTS":
                self.variables_factory.relation_operator(
                    None, None, None, "lt")
            elif self.instructions[current_inst]["opcode"] == "GTS":
                self.variables_factory.relation_operator(
                    None, None, None, "gt")
            elif self.instructions[current_inst]["opcode"] == "EQS":
                self.variables_factory.relation_operator(
                    None, None, None, "eq")
            elif self.instructions[current_inst]["opcode"] == "ANDS":
                self.variables_factory.bool_operator(None, None, None, "and")
            elif self.instructions[current_inst]["opcode"] == "NOTS":
                self.variables_factory.bool_operator(None, None, None, "not")
            elif self.instructions[current_inst]["opcode"] == "ORS":
                self.variables_factory.bool_operator(None, None, None, "or")
            elif self.instructions[current_inst]["opcode"] == "INT2CHARS":
                self.variables_factory.int_to_char(None, None)
            elif self.instructions[current_inst]["opcode"] == "STRI2INTS":
                self.variables_factory.stri_to_int(None, None, None)
            elif self.instructions[current_inst]["opcode"] == "JUMPIFEQS":
                if self.variables_factory.is_equal(None, None):
                    current_inst = self.jump_to_label(
                        self.instructions[current_inst]["args"][0]) - 1
            elif self.instructions[current_inst]["opcode"] == "JUMPIFNEQS":
                if self.variables_factory.is_not_equal(None, None):
                    current_inst = self.jump_to_label(
                        self.instructions[current_inst]["args"][0]) - 1

            current_inst += 1
            self.total_inst += 1

        self.stat_vars = self.frames.stat_vars

    @staticmethod
    def count_args(actual, needed, opcode):
        """
        Counts if instruction has valid arguments number
        :param actual: Actual instruction's number of arguments
        :param needed: Needed instruction's number of arguments
        :param opcode: Instruction code
        """
        if actual != needed:
            raise ET.ParseError(
                "too many or missing arguments for %s instruction" % opcode)

    def var(self, arg):
        """
        Parse and validate variable
        :param arg: Variable argument
        """
        if arg[0] != "var":
            raise IPPcodeParseError("expected variable")

        try:
            frame, name = arg[1].split("@")
        except ValueError:
            raise IPPcodeParseError("variable has wrong format")

        if self.names_pattern.match(name) or name[0].isdigit():
            raise IPPcodeParseError("variable name has wrong format")

        if frame not in FRAMES:
            raise IPPcodeParseError("unknown frame")

    def symb(self, arg):
        """
        Parse and validate symbols
        :param arg: Symbol argument
        """
        if arg[0] == "var":
            self.var(arg)
        elif arg[0] == "bool":
            if arg[1] != "true" and arg[1] != "false":
                raise IPPcodeParseError("unknown bool value")
        elif arg[0] == "int":
            try:
                arg[1] = int(arg[1])
            except (ValueError, TypeError):
                raise IPPcodeParseError("wrong int literal")
        elif arg[0] == "float":
            try:
                arg[1] = float.fromhex(arg[1])
            except (ValueError, TypeError):
                raise IPPcodeParseError("wrong float literal")
        elif arg[0] == "string":
            if arg[1] is None:
                arg[1] = ""

            if " " in arg[1]:
                raise IPPcodeParseError("found whitespace in string literal")
            elif "#" in arg[1]:
                raise IPPcodeParseError("found # char in string literal")

            arg[1] = unescape(arg[1])

            i = 0
            for char in arg[1]:
                if char == "\\":
                    try:
                        xyz = int(arg[1][i + 1] + arg[1][i + 2] +
                                  arg[1][i + 3])
                        arg[1] = arg[1][:i] + chr(xyz) + arg[1][i + 4:]
                        i -= 3
                    except (ValueError, IndexError):
                        raise IPPcodeParseError("wrong string escape sequence")
                i += 1
        else:
            raise ET.ParseError(
                "symbol can only be var, int, float, bool or string")

    def label(self, arg):
        """
        Parses and valids Labels
        :param arg: Label argument
        """
        if arg[0] != "label":
            raise IPPcodeParseError("expected label")

        if self.names_pattern.match(arg[1]) or arg[1][0].isdigit():
            raise IPPcodeParseError("label name has wrong format")

    @staticmethod
    def type(arg):
        """
        Parses and valids types
        :param arg: Type argument
        """
        if arg[0] != "type":
            raise IPPcodeParseError("expected type")

        if arg[1] not in TYPES:
            raise IPPcodeParseError("unexpected type")

    def find_label(self, name):
        """
        Finds label by name and returns it
        :param name: label name
        :return: label or None if not found
        """
        for label in self.labels:
            if label[0] == name:
                return label

        return None

    def add_label(self, arg, position):
        """
        Adds new label
        :param arg: label name
        :param position: label position
        """
        label = arg[1]

        if not self.find_label(label):
            self.labels.append([label, position])
        else:
            sys.stderr.write("ERROR: Label %s already exists!\n" % label)
            exit(52)

    def jump_to_label(self, arg):
        """
        Find label and get its position for jump
        :param arg: Label
        :return: Label position
        """
        self.total_inst += 1
        label = self.find_label(arg[1])

        if not label:
            sys.stderr.write("ERROR: Label %s not found!\n" % arg[1])
            exit(52)
        else:
            return label[1] - 1